From 1e8006118615c6740f90ce0fdd5684a1d2542e4a Mon Sep 17 00:00:00 2001 From: Prabhat Khera <91852476+prabhat-org@users.noreply.github.com> Date: Tue, 30 Nov 2021 11:01:09 +1300 Subject: [PATCH] feat(docker): allow docker container resource settings without restart EE-1942 (#6065) Co-authored-by: sam Co-authored-by: sam@gemibook Co-authored-by: Prabhat Khera --- app/docker/services/containerService.js | 15 ++ .../create/createContainerController.js | 38 +++++ .../containers/create/createcontainer.css | 20 +++ .../containers/create/createcontainer.html | 152 ++++++++++++------ app/portainer/components/slider/slider.js | 2 +- 5 files changed, 173 insertions(+), 54 deletions(-) create mode 100644 app/docker/views/containers/create/createcontainer.css diff --git a/app/docker/services/containerService.js b/app/docker/services/containerService.js index c7a92f17a..78fc42a68 100644 --- a/app/docker/services/containerService.js +++ b/app/docker/services/containerService.js @@ -90,11 +90,26 @@ angular.module('portainer.docker').factory('ContainerService', [ }; service.updateRestartPolicy = updateRestartPolicy; + service.updateLimits = updateLimits; function updateRestartPolicy(id, restartPolicy, maximumRetryCounts) { return Container.update({ id: id }, { RestartPolicy: { Name: restartPolicy, MaximumRetryCount: maximumRetryCounts } }).$promise; } + function updateLimits(id, config) { + return Container.update( + { id: id }, + { + // MemorySwap: must be set + // -1: non limits, 0: treated as unset(cause update error). + MemoryReservation: config.HostConfig.MemoryReservation, + Memory: config.HostConfig.Memory, + MemorySwap: -1, + NanoCpus: config.HostConfig.NanoCpus, + } + ).$promise; + } + service.createContainer = function (configuration) { var deferred = $q.defer(); Container.create(configuration) diff --git a/app/docker/views/containers/create/createContainerController.js b/app/docker/views/containers/create/createContainerController.js index c626691ec..850282d62 100644 --- a/app/docker/views/containers/create/createContainerController.js +++ b/app/docker/views/containers/create/createContainerController.js @@ -7,6 +7,8 @@ import { ContainerCapabilities, ContainerCapability } from '../../../models/cont import { AccessControlFormData } from '../../../../portainer/components/accessControlForm/porAccessControlFormModel'; import { ContainerDetailsViewModel } from '../../../models/container'; +import './createcontainer.css'; + angular.module('portainer.docker').controller('CreateContainerController', [ '$q', '$scope', @@ -61,6 +63,7 @@ angular.module('portainer.docker').controller('CreateContainerController', [ endpoint ) { $scope.create = create; + $scope.update = update; $scope.endpoint = endpoint; $scope.formValues = { @@ -97,6 +100,7 @@ angular.module('portainer.docker').controller('CreateContainerController', [ actionInProgress: false, mode: '', pullImageValidity: true, + settingUnlimitedResources: false, }; $scope.handleEnvVarChange = handleEnvVarChange; @@ -758,6 +762,40 @@ angular.module('portainer.docker').controller('CreateContainerController', [ return true; } + $scope.handleResourceChange = handleResourceChange; + function handleResourceChange() { + $scope.state.settingUnlimitedResources = false; + if ( + ($scope.config.HostConfig.Memory > 0 && $scope.formValues.MemoryLimit === 0) || + ($scope.config.HostConfig.MemoryReservation > 0 && $scope.formValues.MemoryReservation === 0) || + ($scope.config.HostConfig.NanoCpus > 0 && $scope.formValues.CpuLimit === 0) + ) { + $scope.state.settingUnlimitedResources = true; + } + } + + async function updateLimits(config) { + try { + if ($scope.state.settingUnlimitedResources) { + create(); + } else { + await ContainerService.updateLimits($transition$.params().from, config); + $scope.config = config; + Notifications.success('Limits updated'); + } + } catch (err) { + Notifications.error('Failure', err, 'Update Limits fail'); + } + } + + async function update() { + $scope.state.actionInProgress = true; + var config = angular.copy($scope.config); + prepareResources(config); + await updateLimits(config); + $scope.state.actionInProgress = false; + } + function create() { var oldContainer = null; HttpRequestHelper.setPortainerAgentTargetHeader($scope.formValues.NodeName); diff --git a/app/docker/views/containers/create/createcontainer.css b/app/docker/views/containers/create/createcontainer.css new file mode 100644 index 000000000..9dd901d60 --- /dev/null +++ b/app/docker/views/containers/create/createcontainer.css @@ -0,0 +1,20 @@ +.edit-resources { + padding: 20px; + border: 1px solid var(--border-widget-color); +} + +.widget .edit-resources button { + margin-left: 0; +} + +.mt-20 { + margin-top: 20px; +} + +.mt-7 { + margin-top: 7px; +} + +.mt-10 { + margin-top: 10px; +} diff --git a/app/docker/views/containers/create/createcontainer.html b/app/docker/views/containers/create/createcontainer.html index 0ce6d50b1..93e09632c 100644 --- a/app/docker/views/containers/create/createcontainer.html +++ b/app/docker/views/containers/create/createcontainer.html @@ -708,60 +708,106 @@ -
- Resources +
+
+ Resources +
+ +
+ +
+ +
+
+ +
+
+

+ Memory soft limit (MB) +

+
+
+ + +
+ +
+ +
+
+ +
+
+

+ Memory limit (MB) +

+
+
+ + +
+ +
+ +
+
+

+ Maximum CPU usage +

+
+
+ + + +
+
+ +
+
+

+ Updating any resource value to ‘unlimited' will redeploy this container. +

+
+
+
- -
- -
- -
-
- -
-
-

- Memory soft limit (MB) -

-
-
- - -
- -
- -
-
- -
-
-

- Memory limit (MB) -

-
-
- - -
- -
- -
-
-

- Maximum CPU usage -

-
-
-
diff --git a/app/portainer/components/slider/slider.js b/app/portainer/components/slider/slider.js index dba7d27e1..c57f04000 100644 --- a/app/portainer/components/slider/slider.js +++ b/app/portainer/components/slider/slider.js @@ -3,7 +3,7 @@ angular.module('portainer.app').component('slider', { controller: 'SliderController', bindings: { model: '=', - onChange: '&', + onChange: '<', floor: '<', ceil: '<', step: '<',