mirror of https://github.com/portainer/portainer
fix(stack): validate original containers names [EE-4520] (#7978)
parent
ff10588383
commit
2868da296a
|
@ -23,28 +23,45 @@ angular.module('portainer.app').factory('StackHelper', [
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
helper.validateYAML = function (yaml, containerNames) {
|
helper.validateYAML = validateYAML;
|
||||||
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, ', ') +
|
|
||||||
'.'
|
|
||||||
);
|
|
||||||
};
|
|
||||||
|
|
||||||
return helper;
|
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'));
|
||||||
|
}
|
||||||
|
|
|
@ -141,6 +141,9 @@
|
||||||
<span class="col-sm-12 text-muted small">
|
<span class="col-sm-12 text-muted small">
|
||||||
You can get more information about Compose file format in the <a href="https://docs.docker.com/compose/compose-file/" target="_blank">official documentation</a>.
|
You can get more information about Compose file format in the <a href="https://docs.docker.com/compose/compose-file/" target="_blank">official documentation</a>.
|
||||||
</span>
|
</span>
|
||||||
|
<div class="col-sm-12" ng-if="state.yamlError">
|
||||||
|
<span class="text-danger small">{{ state.yamlError }}</span>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="form-group">
|
<div class="form-group">
|
||||||
<div class="col-sm-12">
|
<div class="col-sm-12">
|
||||||
|
|
|
@ -3,6 +3,7 @@ import { AccessControlFormData } from 'Portainer/components/accessControlForm/po
|
||||||
import { FeatureId } from 'Portainer/feature-flags/enums';
|
import { FeatureId } from 'Portainer/feature-flags/enums';
|
||||||
import { getEnvironments } from '@/react/portainer/environments/environment.service';
|
import { getEnvironments } from '@/react/portainer/environments/environment.service';
|
||||||
import { StackStatus, StackType } from '@/react/docker/stacks/types';
|
import { StackStatus, StackType } from '@/react/docker/stacks/types';
|
||||||
|
import { extractContainerNames } from '@/portainer/helpers/stackHelper';
|
||||||
|
|
||||||
angular.module('portainer.app').controller('StackController', [
|
angular.module('portainer.app').controller('StackController', [
|
||||||
'$async',
|
'$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, '')) {
|
if ($scope.stackFileContent.replace(/(\r\n|\n|\r)/gm, '') !== cm.getValue().replace(/(\r\n|\n|\r)/gm, '')) {
|
||||||
$scope.state.isEditorDirty = true;
|
$scope.state.isEditorDirty = true;
|
||||||
$scope.stackFileContent = cm.getValue();
|
$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) {
|
if (isSwarm && $scope.stack.Status === StackStatus.Active) {
|
||||||
assignSwarmStackResources(data.resources, agentProxy);
|
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) {
|
.catch(function error(err) {
|
||||||
Notifications.error('Failure', err, 'Unable to retrieve stack details');
|
Notifications.error('Failure', err, 'Unable to retrieve stack details');
|
||||||
|
|
Loading…
Reference in New Issue