diff --git a/app/components/service/includes/constraints.html b/app/components/service/includes/constraints.html new file mode 100644 index 000000000..58be6fd88 --- /dev/null +++ b/app/components/service/includes/constraints.html @@ -0,0 +1,66 @@ +
+ + + + + +

There are no placement constraints for this service.

+
+ + + + + + + + + + + + + + + + +
NameOperatorValue
+
+ +
+
+
+ +
+
+
+ + + + +
+
+
+ + + +
+
diff --git a/app/components/service/includes/container-specs.html b/app/components/service/includes/container-specs.html new file mode 100644 index 000000000..d8b9ad97f --- /dev/null +++ b/app/components/service/includes/container-specs.html @@ -0,0 +1,56 @@ +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
CMD{{ service.Command|command }} +

+ Command to execute. +

+
Args{{ service.Arguments }} +

+ Arguments passed to command in container. +

+
User{{ service.User }} +

+ Username or UID. +

+
Working directory{{ service.Dir }} +

+ Working directory inside the container. +

+
Stop grace period{{ service.StopGracePeriod }} +

+ Time to wait before force killing a container (default none). +

+
+
+
+
diff --git a/app/components/service/includes/containerlabels.html b/app/components/service/includes/containerlabels.html new file mode 100644 index 000000000..40011fe2a --- /dev/null +++ b/app/components/service/includes/containerlabels.html @@ -0,0 +1,59 @@ +
+ + + + + +

There are no container labels for this service.

+
+ + + + + + + + + + + + + + +
LabelValue
+
+ name + +
+
+
+ value + + + + +
+
+
+ + + +
+
diff --git a/app/components/service/includes/environmentvariables.html b/app/components/service/includes/environmentvariables.html new file mode 100644 index 000000000..f915245b7 --- /dev/null +++ b/app/components/service/includes/environmentvariables.html @@ -0,0 +1,59 @@ +
+ + + + + +

There are no environment variables for this service.

+
+ + + + + + + + + + + + + + +
NameValue
+
+ name + +
+
+
+ value + + + + +
+
+
+ + + +
+
diff --git a/app/components/service/includes/mounts.html b/app/components/service/includes/mounts.html new file mode 100644 index 000000000..d273072c2 --- /dev/null +++ b/app/components/service/includes/mounts.html @@ -0,0 +1,67 @@ +
+ + + + + +

There are no mounts for this service.

+
+ + + + + + + + + + + + + + + + + + + + +
TypeSourceTargetRead onlyActions
+ + + + + + + + + + + +
+
+ + + +
+
diff --git a/app/components/service/includes/networks.html b/app/components/service/includes/networks.html new file mode 100644 index 000000000..f3c551ac4 --- /dev/null +++ b/app/components/service/includes/networks.html @@ -0,0 +1,26 @@ +
+ + + +

This service is not connected to any networks.

+
+ + + + + + + + + + + + + + +
IDIP address
+ {{ network.NetworkID }} + {{ network.Addr }}
+
+
+
diff --git a/app/components/service/includes/ports.html b/app/components/service/includes/ports.html new file mode 100644 index 000000000..2938489bf --- /dev/null +++ b/app/components/service/includes/ports.html @@ -0,0 +1,71 @@ +
+ + + + + +

This service has no ports published.

+
+ + + + + + + + + + + + + + + + + + +
Host portContainer portProtocolActions
+
+ host + +
+
+
+ container + +
+
+
+ +
+
+ + + +
+
+ + +
+ + + diff --git a/app/components/service/includes/resources.html b/app/components/service/includes/resources.html new file mode 100644 index 000000000..f96b8dac4 --- /dev/null +++ b/app/components/service/includes/resources.html @@ -0,0 +1,36 @@ +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + +
CPU limits + {{ service.LimitNanoCPUs / 1000000000 }} + None
Memory limits{{service.LimitMemoryBytes|humansize}}None
CPU reservation + {{service.ReservationNanoCPUs / 1000000000}} + None
Memory reservation{{service.ReservationMemoryBytes|humansize}}None
+
+
+
diff --git a/app/components/service/includes/restart.html b/app/components/service/includes/restart.html new file mode 100644 index 000000000..22fee2774 --- /dev/null +++ b/app/components/service/includes/restart.html @@ -0,0 +1,76 @@ +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + +
Restart condition +
+ +
+
+

+ Condition for restart. +

+
Restart delay + + +

+ Delay between restart attempts. Time in seconds. +

+
Restart max attempts + + +

+ Maximum attempts to restart a given container before giving up (default value is 0, which is ignored). +

+
Restart window + + +

+ The time window used to evaluate the restart policy (default value is 0, which is unbounded). +

+
+
+ + + +
+
diff --git a/app/components/service/includes/servicelabels.html b/app/components/service/includes/servicelabels.html new file mode 100644 index 000000000..cb0ff9992 --- /dev/null +++ b/app/components/service/includes/servicelabels.html @@ -0,0 +1,63 @@ +
+ + + + + +

There are no labels for this service.

+
+ + + + + + + + + + + + + + +
+ Label + + Value +
+
+ name + +
+
+
+ value + + + + +
+
+
+ + + +
+
diff --git a/app/components/service/includes/tasks.html b/app/components/service/includes/tasks.html new file mode 100644 index 000000000..48dc1b6f9 --- /dev/null +++ b/app/components/service/includes/tasks.html @@ -0,0 +1,65 @@ +
+ + +
+ Items per page: + +
+
+ + + + + + + + + + + + + + + + + + + + +
Id + + Status + + + + + + Slot + + + + + + Node + + + + + + Last update + + + +
{{ task.Id }}{{ task.Status }}{{ task.Slot }}{{ task.Node }}{{ task.Updated|getisodate }}
+
+ +
+
+
+
diff --git a/app/components/service/includes/updateconfig.html b/app/components/service/includes/updateconfig.html new file mode 100644 index 000000000..2fe2d89d8 --- /dev/null +++ b/app/components/service/includes/updateconfig.html @@ -0,0 +1,68 @@ +
+ + + + + + + + + + + + + + + + + + + + + + +
Update Parallelism + + +

+ Maximum number of tasks to be updated simultaneously (0 to update all at once). +

+
Update Delay + + +

+ Amount of time between updates. +

+
Update Failure Action +
+ + +
+
+

+ Action taken on failure to start after update. +

+
+
+ + + +
+
diff --git a/app/components/service/service.html b/app/components/service/service.html index ff6b66d2c..a00ff7ced 100644 --- a/app/components/service/service.html +++ b/app/components/service/service.html @@ -11,7 +11,16 @@
-
+
+ +
+
+ +
+
@@ -19,23 +28,32 @@ Name - - {{ service.Name }} - + + - - - - + + {{ service.Name }} ID {{ service.Id }} - + + + Created at + {{ service.CreatedAt|getisodate}} + + + Last updated at + {{ service.UpdatedAt|getisodate }} + + + Version + {{ service.Version }} + Scheduling mode {{ service.Mode }} @@ -43,251 +61,90 @@ Replicas - - {{ service.Replicas }} - Scale - - - - - + + Image - - {{ service.Image }} - - - - - - - - - - - Published ports -
- {{ mapping.TargetPort }} {{ mapping.PublishedPort }} -
- - - - Environment variables - -
-
- - environment variable - -
- -
-
-
- name - -
-
- value - - - - -
-
-
- -
- - - - Labels - -
-
- - label - -
- -
-
-
- name - -
-
- value - - - - -
-
-
- -
- - - - Container labels - -
-
- - container label - -
- -
-
-
- name - -
-
- value - - - - -
-
-
- -
- - - - Update Parallelism - - - {{ service.UpdateParallelism }} - Change - - - - - - - - - - Update Delay - - - {{ service.UpdateDelay }} - Change - - - - - - - - - - Update Failure Action - -
- - -
+
- -
- - + +

+ Do you need help? View the Docker Service documentation here. +

+
-
-
-
+
- -
- Items per page: - -
-
+ - - - - - - - - - - - - - - - - - - - -
Id - - Status - - - - - - Slot - - - - - - Node - - - - - - Last update - - - -
{{ task.Id }}{{ task.Status }}{{ task.Slot }}{{ task.Node }}{{ task.Updated|getisodate }}
-
- -
+
+ +
+
+
+

Container specification

+
+
+
+
+
+
+ +
+
+
+

Networks & ports

+
+
+
+
+ +
+
+
+

Service specification

+
+
+
+
+
+
+
+
diff --git a/app/components/service/serviceController.js b/app/components/service/serviceController.js index 35b086847..438038087 100644 --- a/app/components/service/serviceController.js +++ b/app/components/service/serviceController.js @@ -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(); }]); diff --git a/app/components/services/services.html b/app/components/services/services.html index 02925a678..f68133fe0 100644 --- a/app/components/services/services.html +++ b/app/components/services/services.html @@ -58,6 +58,13 @@ + + + Updated at + + + + Ownership @@ -85,6 +92,9 @@ + + {{ service.UpdatedAt|getisodate }} + diff --git a/app/models/service.js b/app/models/service.js index 156faefef..c60c3faab 100644 --- a/app/models/service.js +++ b/app/models/service.js @@ -2,6 +2,8 @@ function ServiceViewModel(data, runningTasks, nodes) { this.Model = data; this.Id = data.ID; this.Name = data.Spec.Name; + this.CreatedAt = data.CreatedAt; + this.UpdatedAt = data.UpdatedAt; this.Image = data.Spec.TaskTemplate.ContainerSpec.Image; this.Version = data.Version.Index; if (data.Spec.Mode.Replicated) { @@ -16,20 +18,52 @@ function ServiceViewModel(data, runningTasks, nodes) { if (runningTasks) { this.Running = runningTasks.length; } + if (data.Spec.TaskTemplate.Resources) { + if (data.Spec.TaskTemplate.Resources.Limits) { + this.LimitNanoCPUs = data.Spec.TaskTemplate.Resources.Limits.NanoCPUs; + this.LimitMemoryBytes = data.Spec.TaskTemplate.Resources.Limits.MemoryBytes; + } + if (data.Spec.TaskTemplate.Resources.Reservations) { + this.ReservationNanoCPUs = data.Spec.TaskTemplate.Resources.Reservations.NanoCPUs; + this.ReservationMemoryBytes = data.Spec.TaskTemplate.Resources.Reservations.MemoryBytes; + } + } + + if (data.Spec.TaskTemplate.RestartPolicy) { + this.RestartCondition = data.Spec.TaskTemplate.RestartPolicy.Condition; + this.RestartDelay = data.Spec.TaskTemplate.RestartPolicy.Delay; + this.RestartMaxAttempts = data.Spec.TaskTemplate.RestartPolicy.MaxAttempts; + this.RestartWindow = data.Spec.TaskTemplate.RestartPolicy.Window; + } else { + this.RestartCondition = 'none'; + this.RestartDelay = 0; + this.RestartMaxAttempts = 0; + this.RestartWindow = 0; + } + this.Constraints = data.Spec.TaskTemplate.Placement ? data.Spec.TaskTemplate.Placement.Constraints || [] : []; this.Labels = data.Spec.Labels; - if (data.Spec.TaskTemplate.ContainerSpec) { - this.ContainerLabels = data.Spec.TaskTemplate.ContainerSpec.Labels; + + var containerSpec = data.Spec.TaskTemplate.ContainerSpec; + if (containerSpec) { + this.ContainerLabels = containerSpec.Labels; + this.Env = containerSpec.Env; + this.Mounts = containerSpec.Mounts || []; + this.User = containerSpec.User; + this.Dir = containerSpec.Dir; + this.Command = containerSpec.Command; + this.Secrets = containerSpec.Secrets; } - if (data.Spec.TaskTemplate.ContainerSpec.Env) { - this.Env = data.Spec.TaskTemplate.ContainerSpec.Env; + if (data.Spec.EndpointSpec) { + this.Ports = data.Spec.EndpointSpec.Ports; } + this.Mounts = []; if (data.Spec.TaskTemplate.ContainerSpec.Mounts) { this.Mounts = data.Spec.TaskTemplate.ContainerSpec.Mounts; } - if (data.Endpoint.Ports) { - this.Ports = data.Endpoint.Ports; - } + + this.VirtualIPs = data.Endpoint ? data.Endpoint.VirtualIPs : []; + if (data.Spec.UpdateConfig) { this.UpdateParallelism = (typeof data.Spec.UpdateConfig.Parallelism !== undefined) ? data.Spec.UpdateConfig.Parallelism || 0 : 1; this.UpdateDelay = data.Spec.UpdateConfig.Delay || 0; diff --git a/assets/css/app.css b/assets/css/app.css index 9ea1b028c..4371ab229 100644 --- a/assets/css/app.css +++ b/assets/css/app.css @@ -125,6 +125,10 @@ a[ng-click]{ padding: 0 !important; } +.padding-top { + padding-top: 15px !important; +} + .terminal-container { width: 100%; padding: 10px 5px;