diff --git a/app/portainer/helpers/stackHelper.js b/app/portainer/helpers/stackHelper.js index bfaf24b9f..63e959e84 100644 --- a/app/portainer/helpers/stackHelper.js +++ b/app/portainer/helpers/stackHelper.js @@ -23,28 +23,45 @@ angular.module('portainer.app').factory('StackHelper', [ ); } - helper.validateYAML = function (yaml, containerNames) { - let yamlObject; - - try { - yamlObject = YAML.parse(yaml); - } catch (err) { - return 'There is an error in the yaml syntax: ' + err; - } - - const names = _.uniq(GenericHelper.findDeepAll(yamlObject, 'container_name')); - const duplicateContainers = _.intersection(containerNames, names); - - if (duplicateContainers.length === 0) return; - - return ( - (duplicateContainers.length === 1 ? 'This container name is' : 'These container names are') + - ' already used by another container running in this environment: ' + - _.join(duplicateContainers, ', ') + - '.' - ); - }; + helper.validateYAML = validateYAML; return helper; }, ]); + +function validateYAML(yaml, containerNames, originalContainersNames = []) { + let yamlObject; + + try { + yamlObject = YAML.parse(yaml); + } catch (err) { + return 'There is an error in the yaml syntax: ' + err; + } + + const names = _.uniq(GenericHelper.findDeepAll(yamlObject, 'container_name')); + + const duplicateContainers = _.intersection(_.difference(containerNames, originalContainersNames), names); + + if (duplicateContainers.length === 0) { + return ''; + } + + return ( + (duplicateContainers.length === 1 ? 'This container name is' : 'These container names are') + + ' already used by another container running in this environment: ' + + _.join(duplicateContainers, ', ') + + '.' + ); +} + +export function extractContainerNames(yaml = '') { + let yamlObject; + + try { + yamlObject = YAML.parse(yaml); + } catch (err) { + return []; + } + + return _.uniq(GenericHelper.findDeepAll(yamlObject, 'container_name')); +} diff --git a/app/portainer/views/stacks/edit/stack.html b/app/portainer/views/stacks/edit/stack.html index 5af957241..adfeb9d02 100644 --- a/app/portainer/views/stacks/edit/stack.html +++ b/app/portainer/views/stacks/edit/stack.html @@ -141,6 +141,9 @@ You can get more information about Compose file format in the official documentation. +
+ {{ state.yamlError }} +
diff --git a/app/portainer/views/stacks/edit/stackController.js b/app/portainer/views/stacks/edit/stackController.js index 6dc2117f2..cb9dffac6 100644 --- a/app/portainer/views/stacks/edit/stackController.js +++ b/app/portainer/views/stacks/edit/stackController.js @@ -3,6 +3,7 @@ import { AccessControlFormData } from 'Portainer/components/accessControlForm/po import { FeatureId } from 'Portainer/feature-flags/enums'; import { getEnvironments } from '@/react/portainer/environments/environment.service'; import { StackStatus, StackType } from '@/react/docker/stacks/types'; +import { extractContainerNames } from '@/portainer/helpers/stackHelper'; angular.module('portainer.app').controller('StackController', [ '$async', @@ -275,7 +276,7 @@ angular.module('portainer.app').controller('StackController', [ if ($scope.stackFileContent.replace(/(\r\n|\n|\r)/gm, '') !== cm.getValue().replace(/(\r\n|\n|\r)/gm, '')) { $scope.state.isEditorDirty = true; $scope.stackFileContent = cm.getValue(); - $scope.state.yamlError = StackHelper.validateYAML($scope.stackFileContent, $scope.containerNames); + $scope.state.yamlError = StackHelper.validateYAML($scope.stackFileContent, $scope.containerNames, $scope.state.originalContainerNames); } }; @@ -365,8 +366,9 @@ angular.module('portainer.app').controller('StackController', [ if (isSwarm && $scope.stack.Status === StackStatus.Active) { assignSwarmStackResources(data.resources, agentProxy); } + $scope.state.originalContainerNames = extractContainerNames($scope.stackFileContent); - $scope.state.yamlError = StackHelper.validateYAML($scope.stackFileContent, $scope.containerNames); + $scope.state.yamlError = StackHelper.validateYAML($scope.stackFileContent, $scope.containerNames, $scope.state.originalContainerNames); }) .catch(function error(err) { Notifications.error('Failure', err, 'Unable to retrieve stack details');