mirror of
https://github.com/portainer/portainer.git
synced 2025-07-23 15:29:42 +02:00
feat(service-details): add ability to edit service details (#453)
This commit is contained in:
parent
ab91ffe12c
commit
a8f70d7f59
17 changed files with 1057 additions and 296 deletions
|
@ -1,6 +1,6 @@
|
|||
angular.module('service', [])
|
||||
.controller('ServiceController', ['$scope', '$stateParams', '$state', 'Service', 'ServiceHelper', 'Task', 'Node', 'Messages', 'Pagination',
|
||||
function ($scope, $stateParams, $state, Service, ServiceHelper, Task, Node, Messages, Pagination) {
|
||||
.controller('ServiceController', ['$scope', '$stateParams', '$state', '$location', '$anchorScroll', 'Service', 'ServiceHelper', 'Task', 'Node', 'Messages', 'Pagination',
|
||||
function ($scope, $stateParams, $state, $location, $anchorScroll, Service, ServiceHelper, Task, Node, Messages, Pagination) {
|
||||
|
||||
$scope.state = {};
|
||||
$scope.state.pagination_count = Pagination.getPaginationCount('service_tasks');
|
||||
|
@ -10,7 +10,10 @@ function ($scope, $stateParams, $state, Service, ServiceHelper, Task, Node, Mess
|
|||
$scope.sortType = 'Status';
|
||||
$scope.sortReverse = false;
|
||||
|
||||
var previousServiceValues = {};
|
||||
$scope.lastVersion = 0;
|
||||
|
||||
var originalService = {};
|
||||
var previousServiceValues = [];
|
||||
|
||||
$scope.order = function (sortType) {
|
||||
$scope.sortReverse = ($scope.sortType === sortType) ? !$scope.sortReverse : false;
|
||||
|
@ -35,85 +38,175 @@ function ($scope, $stateParams, $state, Service, ServiceHelper, Task, Node, Mess
|
|||
service.EditReplicas = false;
|
||||
};
|
||||
|
||||
$scope.goToItem = function(hash) {
|
||||
$anchorScroll(hash);
|
||||
};
|
||||
|
||||
$scope.addEnvironmentVariable = function addEnvironmentVariable(service) {
|
||||
service.EnvironmentVariables.push({ key: '', value: '', originalValue: '' });
|
||||
service.hasChanges = true;
|
||||
updateServiceArray(service, 'EnvironmentVariables', service.EnvironmentVariables);
|
||||
};
|
||||
$scope.removeEnvironmentVariable = function removeEnvironmentVariable(service, index) {
|
||||
var removedElement = service.EnvironmentVariables.splice(index, 1);
|
||||
service.hasChanges = service.hasChanges || removedElement !== null;
|
||||
if (removedElement !== null) {
|
||||
updateServiceArray(service, 'EnvironmentVariables', service.EnvironmentVariables);
|
||||
}
|
||||
};
|
||||
$scope.updateEnvironmentVariable = function updateEnvironmentVariable(service, variable) {
|
||||
service.hasChanges = service.hasChanges || variable.value !== variable.originalValue;
|
||||
if (variable.value !== variable.originalValue || variable.key !== variable.originalKey) {
|
||||
updateServiceArray(service, 'EnvironmentVariables', service.EnvironmentVariables);
|
||||
}
|
||||
};
|
||||
$scope.addLabel = function addLabel(service) {
|
||||
service.hasChanges = true;
|
||||
service.ServiceLabels.push({ key: '', value: '', originalValue: '' });
|
||||
updateServiceArray(service, 'ServiceLabels', service.ServiceLabels);
|
||||
};
|
||||
$scope.removeLabel = function removeLabel(service, index) {
|
||||
var removedElement = service.ServiceLabels.splice(index, 1);
|
||||
service.hasChanges = service.hasChanges || removedElement !== null;
|
||||
if (removedElement !== null) {
|
||||
updateServiceArray(service, 'ServiceLabels', service.ServiceLabels);
|
||||
}
|
||||
};
|
||||
$scope.updateLabel = function updateLabel(service, label) {
|
||||
service.hasChanges = service.hasChanges || label.value !== label.originalValue;
|
||||
if (label.value !== label.originalValue || label.key !== label.originalKey) {
|
||||
updateServiceArray(service, 'ServiceLabels', service.ServiceLabels);
|
||||
}
|
||||
};
|
||||
$scope.addContainerLabel = function addContainerLabel(service) {
|
||||
service.hasChanges = true;
|
||||
service.ServiceContainerLabels.push({ key: '', value: '', originalValue: '' });
|
||||
updateServiceArray(service, 'ServiceContainerLabels', service.ServiceContainerLabels);
|
||||
};
|
||||
$scope.removeContainerLabel = function removeContainerLabel(service, index) {
|
||||
$scope.removeContainerLabel = function removeLabel(service, index) {
|
||||
var removedElement = service.ServiceContainerLabels.splice(index, 1);
|
||||
service.hasChanges = service.hasChanges || removedElement !== null;
|
||||
if (removedElement !== null) {
|
||||
updateServiceArray(service, 'ServiceContainerLabels', service.ServiceContainerLabels);
|
||||
}
|
||||
};
|
||||
$scope.updateContainerLabel = function updateLabel(service, label) {
|
||||
if (label.value !== label.originalValue || label.key !== label.originalKey) {
|
||||
updateServiceArray(service, 'ServiceContainerLabels', service.ServiceContainerLabels);
|
||||
}
|
||||
};
|
||||
$scope.addMount = function addMount(service) {
|
||||
service.ServiceMounts.push({Type: 'volume', Source: '', Target: '', ReadOnly: false });
|
||||
updateServiceArray(service, 'ServiceMounts', service.ServiceMounts);
|
||||
};
|
||||
$scope.removeMount = function removeMount(service, index) {
|
||||
var removedElement = service.ServiceMounts.splice(index, 1);
|
||||
if (removedElement !== null) {
|
||||
updateServiceArray(service, 'ServiceMounts', service.ServiceMounts);
|
||||
}
|
||||
};
|
||||
$scope.updateMount = function updateMount(service, mount) {
|
||||
updateServiceArray(service, 'ServiceMounts', service.ServiceMounts);
|
||||
};
|
||||
$scope.addPlacementConstraint = function addPlacementConstraint(service) {
|
||||
service.ServiceConstraints.push({ key: '', operator: '==', value: '' });
|
||||
updateServiceArray(service, 'ServiceConstraints', service.ServiceConstraints);
|
||||
};
|
||||
$scope.removePlacementConstraint = function removePlacementConstraint(service, index) {
|
||||
var removedElement = service.ServiceConstraints.splice(index, 1);
|
||||
if (removedElement !== null) {
|
||||
updateServiceArray(service, 'ServiceConstraints', service.ServiceConstraints);
|
||||
}
|
||||
};
|
||||
$scope.updatePlacementConstraint = function updatePlacementConstraint(service, constraint) {
|
||||
updateServiceArray(service, 'ServiceConstraints', service.ServiceConstraints);
|
||||
};
|
||||
|
||||
$scope.changeParallelism = function changeParallelism(service) {
|
||||
updateServiceAttribute(service, 'UpdateParallelism', service.newServiceUpdateParallelism);
|
||||
service.EditParallelism = false;
|
||||
$scope.addPublishedPort = function addPublishedPort(service) {
|
||||
if (!service.Ports) {
|
||||
service.Ports = [];
|
||||
}
|
||||
service.Ports.push({ PublishedPort: '', TargetPort: '', Protocol: 'tcp' });
|
||||
};
|
||||
$scope.changeUpdateDelay = function changeUpdateDelay(service) {
|
||||
updateServiceAttribute(service, 'UpdateDelay', service.newServiceUpdateDelay);
|
||||
service.EditDelay = false;
|
||||
$scope.updatePublishedPort = function updatePublishedPort(service, portMapping) {
|
||||
updateServiceArray(service, 'Ports', service.Ports);
|
||||
};
|
||||
$scope.changeUpdateFailureAction = function changeUpdateFailureAction(service) {
|
||||
updateServiceAttribute(service, 'UpdateFailureAction', service.newServiceUpdateFailureAction);
|
||||
$scope.removePortPublishedBinding = function removePortPublishedBinding(service, index) {
|
||||
var removedElement = service.Ports.splice(index, 1);
|
||||
if (removedElement !== null) {
|
||||
updateServiceArray(service, 'Ports', service.Ports);
|
||||
}
|
||||
};
|
||||
|
||||
$scope.cancelChanges = function changeServiceImage(service) {
|
||||
Object.keys(previousServiceValues).forEach(function(attribute) {
|
||||
service[attribute] = previousServiceValues[attribute]; // reset service values
|
||||
service['newService' + attribute] = previousServiceValues[attribute]; // reset edit fields
|
||||
$scope.cancelChanges = function cancelChanges(service, keys) {
|
||||
if (keys) { // clean out the keys only from the list of modified keys
|
||||
keys.forEach(function(key) {
|
||||
var index = previousServiceValues.indexOf(key);
|
||||
if (index >= 0) {
|
||||
previousServiceValues.splice(index, 1);
|
||||
}
|
||||
});
|
||||
} else { // clean out all changes
|
||||
keys = Object.keys(service);
|
||||
previousServiceValues = [];
|
||||
}
|
||||
keys.forEach(function(attribute) {
|
||||
service[attribute] = originalService[attribute]; // reset service values
|
||||
});
|
||||
previousServiceValues = {}; // clear out all changes
|
||||
// clear out environment variable changes
|
||||
service.EnvironmentVariables = translateEnvironmentVariables(service.Env);
|
||||
service.ServiceLabels = translateLabelsToServiceLabels(service.Labels);
|
||||
service.ServiceContainerLabels = translateLabelsToServiceLabels(service.ContainerLabels);
|
||||
|
||||
service.hasChanges = false;
|
||||
};
|
||||
|
||||
$scope.hasChanges = function(service, elements) {
|
||||
var hasChanges = false;
|
||||
elements.forEach(function(key) {
|
||||
hasChanges = hasChanges || (previousServiceValues.indexOf(key) >= 0);
|
||||
});
|
||||
return hasChanges;
|
||||
};
|
||||
|
||||
$scope.updateService = function updateService(service) {
|
||||
$('#loadServicesSpinner').show();
|
||||
var config = ServiceHelper.serviceToConfig(service.Model);
|
||||
config.Name = service.newServiceName;
|
||||
config.Name = service.Name;
|
||||
config.Labels = translateServiceLabelsToLabels(service.ServiceLabels);
|
||||
config.TaskTemplate.ContainerSpec.Env = translateEnvironmentVariablesToEnv(service.EnvironmentVariables);
|
||||
config.TaskTemplate.ContainerSpec.Labels = translateServiceLabelsToLabels(service.ServiceContainerLabels);
|
||||
config.TaskTemplate.ContainerSpec.Image = service.newServiceImage;
|
||||
config.TaskTemplate.ContainerSpec.Image = service.Image;
|
||||
config.TaskTemplate.ContainerSpec.Secrets = service.ServiceSecrets;
|
||||
|
||||
if (service.Mode === 'replicated') {
|
||||
config.Mode.Replicated.Replicas = service.Replicas;
|
||||
}
|
||||
config.TaskTemplate.ContainerSpec.Mounts = service.ServiceMounts;
|
||||
if (typeof config.TaskTemplate.Placement === 'undefined') {
|
||||
config.TaskTemplate.Placement = {};
|
||||
}
|
||||
config.TaskTemplate.Placement.Constraints = translateKeyValueToConstraints(service.ServiceConstraints);
|
||||
|
||||
config.TaskTemplate.Resources = {
|
||||
Limits: {
|
||||
NanoCPUs: service.LimitNanoCPUs,
|
||||
MemoryBytes: service.LimitMemoryBytes
|
||||
},
|
||||
Reservations: {
|
||||
NanoCPUs: service.ReservationNanoCPUs,
|
||||
MemoryBytes: service.ReservationMemoryBytes
|
||||
}
|
||||
};
|
||||
|
||||
config.UpdateConfig = {
|
||||
Parallelism: service.newServiceUpdateParallelism,
|
||||
Delay: service.newServiceUpdateDelay,
|
||||
FailureAction: service.newServiceUpdateFailureAction
|
||||
Parallelism: service.UpdateParallelism,
|
||||
Delay: service.UpdateDelay,
|
||||
FailureAction: service.UpdateFailureAction
|
||||
};
|
||||
config.TaskTemplate.RestartPolicy = {
|
||||
Condition: service.RestartCondition,
|
||||
Delay: service.RestartDelay,
|
||||
MaxAttempts: service.RestartMaxAttempts,
|
||||
Window: service.RestartWindow
|
||||
};
|
||||
config.EndpointSpec = {
|
||||
Mode: config.EndpointSpec.Mode || 'vip',
|
||||
Ports: service.Ports
|
||||
};
|
||||
|
||||
Service.update({ id: service.Id, version: service.Version }, config, function (data) {
|
||||
$('#loadServicesSpinner').hide();
|
||||
Messages.send("Service successfully updated", "Service updated");
|
||||
$state.go('service', {id: service.Id}, {reload: true});
|
||||
$scope.cancelChanges({});
|
||||
fetchServiceDetails();
|
||||
}, function (e) {
|
||||
$('#loadServicesSpinner').hide();
|
||||
Messages.error("Failure", e, "Unable to update service");
|
||||
|
@ -138,22 +231,28 @@ function ($scope, $stateParams, $state, Service, ServiceHelper, Task, Node, Mess
|
|||
});
|
||||
};
|
||||
|
||||
function translateServiceArrays(service) {
|
||||
service.ServiceSecrets = service.Secrets;
|
||||
service.EnvironmentVariables = translateEnvironmentVariables(service.Env);
|
||||
service.ServiceLabels = translateLabelsToServiceLabels(service.Labels);
|
||||
service.ServiceContainerLabels = translateLabelsToServiceLabels(service.ContainerLabels);
|
||||
service.ServiceMounts = angular.copy(service.Mounts);
|
||||
service.ServiceConstraints = translateConstraintsToKeyValue(service.Constraints);
|
||||
}
|
||||
|
||||
function fetchServiceDetails() {
|
||||
$('#loadingViewSpinner').show();
|
||||
Service.get({id: $stateParams.id}, function (d) {
|
||||
var service = new ServiceViewModel(d);
|
||||
service.newServiceName = service.Name;
|
||||
service.newServiceImage = service.Image;
|
||||
service.newServiceReplicas = service.Replicas;
|
||||
service.newServiceUpdateParallelism = service.UpdateParallelism;
|
||||
service.newServiceUpdateDelay = service.UpdateDelay;
|
||||
service.newServiceUpdateFailureAction = service.UpdateFailureAction;
|
||||
|
||||
service.EnvironmentVariables = translateEnvironmentVariables(service.Env);
|
||||
service.ServiceLabels = translateLabelsToServiceLabels(service.Labels);
|
||||
service.ServiceContainerLabels = translateLabelsToServiceLabels(service.ContainerLabels);
|
||||
$scope.isUpdating = $scope.lastVersion >= service.Version;
|
||||
if (!$scope.isUpdating) {
|
||||
$scope.lastVersion = service.Version;
|
||||
}
|
||||
|
||||
translateServiceArrays(service);
|
||||
$scope.service = service;
|
||||
originalService = angular.copy(service);
|
||||
|
||||
Task.query({filters: {service: [service.Name]}}, function (tasks) {
|
||||
Node.query({}, function (nodes) {
|
||||
$scope.displayNode = true;
|
||||
|
@ -178,13 +277,15 @@ function ($scope, $stateParams, $state, Service, ServiceHelper, Task, Node, Mess
|
|||
});
|
||||
}
|
||||
|
||||
function updateServiceAttribute(service, name, newValue) {
|
||||
// ensure we only capture the original previous value, in case we update the attribute multiple times
|
||||
if (!previousServiceValues[name]) {
|
||||
previousServiceValues[name] = service[name];
|
||||
$scope.updateServiceAttribute = function updateServiceAttribute(service, name) {
|
||||
if (service[name] !== originalService[name] || !(name in originalService)) {
|
||||
service.hasChanges = true;
|
||||
}
|
||||
// update the attribute
|
||||
service[name] = newValue;
|
||||
previousServiceValues.push(name);
|
||||
};
|
||||
|
||||
function updateServiceArray(service, name) {
|
||||
previousServiceValues.push(name);
|
||||
service.hasChanges = true;
|
||||
}
|
||||
|
||||
|
@ -195,7 +296,7 @@ function ($scope, $stateParams, $state, Service, ServiceHelper, Task, Node, Mess
|
|||
var idx = variable.indexOf('=');
|
||||
var keyValue = [variable.slice(0,idx), variable.slice(idx+1)];
|
||||
var originalValue = (keyValue.length > 1) ? keyValue[1] : '';
|
||||
variables.push({ key: keyValue[0], value: originalValue, originalValue: originalValue, added: true});
|
||||
variables.push({ key: keyValue[0], value: originalValue, originalKey: keyValue[0], originalValue: originalValue, added: true});
|
||||
});
|
||||
return variables;
|
||||
}
|
||||
|
@ -218,7 +319,7 @@ function ($scope, $stateParams, $state, Service, ServiceHelper, Task, Node, Mess
|
|||
var labels = [];
|
||||
if (Labels) {
|
||||
Object.keys(Labels).forEach(function(key) {
|
||||
labels.push({ key: key, value: Labels[key], originalValue: Labels[key], added: true});
|
||||
labels.push({ key: key, value: Labels[key], originalKey: key, originalValue: Labels[key], added: true});
|
||||
});
|
||||
}
|
||||
return labels;
|
||||
|
@ -233,5 +334,48 @@ function ($scope, $stateParams, $state, Service, ServiceHelper, Task, Node, Mess
|
|||
return Labels;
|
||||
}
|
||||
|
||||
function translateConstraintsToKeyValue(constraints) {
|
||||
function getOperator(constraint) {
|
||||
var indexEquals = constraint.indexOf('==');
|
||||
if (indexEquals >= 0) {
|
||||
return [indexEquals, '=='];
|
||||
}
|
||||
return [constraint.indexOf('!='), '!='];
|
||||
}
|
||||
if (constraints) {
|
||||
var keyValueConstraints = [];
|
||||
constraints.forEach(function(constraint) {
|
||||
var operatorIndices = getOperator(constraint);
|
||||
|
||||
var key = constraint.slice(0, operatorIndices[0]);
|
||||
var operator = operatorIndices[1];
|
||||
var value = constraint.slice(operatorIndices[0] + 2);
|
||||
|
||||
keyValueConstraints.push({
|
||||
key: key,
|
||||
value: value,
|
||||
operator: operator,
|
||||
originalKey: key,
|
||||
originalValue: value
|
||||
});
|
||||
});
|
||||
return keyValueConstraints;
|
||||
}
|
||||
return [];
|
||||
}
|
||||
|
||||
function translateKeyValueToConstraints(keyValueConstraints) {
|
||||
if (keyValueConstraints) {
|
||||
var constraints = [];
|
||||
keyValueConstraints.forEach(function(keyValueConstraint) {
|
||||
if (keyValueConstraint.key && keyValueConstraint.key !== '' && keyValueConstraint.value && keyValueConstraint.value !== '') {
|
||||
constraints.push(keyValueConstraint.key + keyValueConstraint.operator + keyValueConstraint.value);
|
||||
}
|
||||
});
|
||||
return constraints;
|
||||
}
|
||||
return [];
|
||||
}
|
||||
|
||||
fetchServiceDetails();
|
||||
}]);
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue