mirror of https://github.com/portainer/portainer
feat(docker): allow docker container resource settings without restart EE-1942 (#6065)
Co-authored-by: sam <sam@allofword> Co-authored-by: sam@gemibook <huapox@126.com> Co-authored-by: Prabhat Khera <prabhat.khera@gmail.com>pull/6079/head
parent
c267355759
commit
1e80061186
|
@ -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)
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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;
|
||||
}
|
|
@ -708,60 +708,106 @@
|
|||
<!-- !sysctls-input-list -->
|
||||
</div>
|
||||
<!-- !sysctls -->
|
||||
<div class="col-sm-12 form-section-title">
|
||||
Resources
|
||||
<div ng-class="{ 'edit-resources': state.mode == 'duplicate' }">
|
||||
<div class="col-sm-12 form-section-title">
|
||||
Resources
|
||||
</div>
|
||||
<!-- memory-reservation-input -->
|
||||
<div class="form-group">
|
||||
<label for="memory-reservation" class="col-sm-3 col-lg-2 control-label text-left mt-20">
|
||||
Memory reservation
|
||||
</label>
|
||||
<div class="col-sm-3">
|
||||
<slider
|
||||
on-change="(handleResourceChange)"
|
||||
model="formValues.MemoryReservation"
|
||||
floor="0"
|
||||
ceil="state.sliderMaxMemory"
|
||||
step="256"
|
||||
ng-if="state.sliderMaxMemory"
|
||||
></slider>
|
||||
</div>
|
||||
<div class="col-sm-2">
|
||||
<input type="number" min="0" class="form-control" ng-model="formValues.MemoryReservation" id="memory-reservation" />
|
||||
</div>
|
||||
<div class="col-sm-4">
|
||||
<p class="small text-muted mt-7">
|
||||
Memory soft limit (<b>MB</b>)
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
<!-- !memory-reservation-input -->
|
||||
<!-- memory-limit-input -->
|
||||
<div class="form-group">
|
||||
<label for="memory-limit" class="col-sm-3 col-lg-2 control-label text-left mt-20">
|
||||
Memory limit
|
||||
</label>
|
||||
<div class="col-sm-3">
|
||||
<slider
|
||||
on-change="(handleResourceChange)"
|
||||
model="formValues.MemoryLimit"
|
||||
floor="0"
|
||||
ceil="state.sliderMaxMemory"
|
||||
step="256"
|
||||
ng-if="state.sliderMaxMemory"
|
||||
></slider>
|
||||
</div>
|
||||
<div class="col-sm-2">
|
||||
<input type="number" min="0" class="form-control" ng-model="formValues.MemoryLimit" id="memory-limit" />
|
||||
</div>
|
||||
<div class="col-sm-4">
|
||||
<p class="small text-muted mt-7">
|
||||
Memory limit (<b>MB</b>)
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
<!-- !memory-limit-input -->
|
||||
<!-- cpu-limit-input -->
|
||||
<div class="form-group">
|
||||
<label for="cpu-limit" class="col-sm-3 col-lg-2 control-label text-left mt-20">
|
||||
CPU limit
|
||||
</label>
|
||||
<div class="col-sm-5">
|
||||
<slider
|
||||
on-change="(handleResourceChange)"
|
||||
model="formValues.CpuLimit"
|
||||
floor="0"
|
||||
ceil="state.sliderMaxCpu"
|
||||
step="0.25"
|
||||
precision="2"
|
||||
ng-if="state.sliderMaxCpu"
|
||||
></slider>
|
||||
</div>
|
||||
<div class="col-sm-4 mt-20">
|
||||
<p class="small text-muted">
|
||||
Maximum CPU usage
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
<!-- !cpu-limit-input -->
|
||||
|
||||
<!-- update-limit-btn -->
|
||||
<div class="form-group" ng-if="state.mode == 'duplicate'">
|
||||
<div class="col-sm-12">
|
||||
<button
|
||||
type="button"
|
||||
class="btn btn-primary btn-sm"
|
||||
ng-disabled="state.actionInProgress || !formValues.RegistryModel.Image || (!formValues.RegistryModel.Registry && fromContainer)"
|
||||
ng-click="update()"
|
||||
button-spinner="state.actionInProgress"
|
||||
>
|
||||
<span ng-hide="state.actionInProgress">Update Limits</span>
|
||||
<span ng-show="state.actionInProgress">Update in progress...</span>
|
||||
</button>
|
||||
</div>
|
||||
<div class="col-sm-12" ng-if="state.settingUnlimitedResources">
|
||||
<p class="text-muted mr-4">
|
||||
<i class="fa fa-exclamation-circle text-warning mt-10" aria-hidden="true"></i> Updating any resource value to ‘unlimited' will redeploy this container.
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
<!-- !update-limit-btn -->
|
||||
</div>
|
||||
<!-- memory-reservation-input -->
|
||||
<div class="form-group">
|
||||
<label for="memory-reservation" class="col-sm-3 col-lg-2 control-label text-left" style="margin-top: 20px;">
|
||||
Memory reservation
|
||||
</label>
|
||||
<div class="col-sm-3">
|
||||
<slider model="formValues.MemoryReservation" floor="0" ceil="state.sliderMaxMemory" step="256" ng-if="state.sliderMaxMemory"></slider>
|
||||
</div>
|
||||
<div class="col-sm-2">
|
||||
<input type="number" min="0" class="form-control" ng-model="formValues.MemoryReservation" id="memory-reservation" />
|
||||
</div>
|
||||
<div class="col-sm-4">
|
||||
<p class="small text-muted" style="margin-top: 7px;">
|
||||
Memory soft limit (<b>MB</b>)
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
<!-- !memory-reservation-input -->
|
||||
<!-- memory-limit-input -->
|
||||
<div class="form-group">
|
||||
<label for="memory-limit" class="col-sm-3 col-lg-2 control-label text-left" style="margin-top: 20px;">
|
||||
Memory limit
|
||||
</label>
|
||||
<div class="col-sm-3">
|
||||
<slider model="formValues.MemoryLimit" floor="0" ceil="state.sliderMaxMemory" step="256" ng-if="state.sliderMaxMemory"></slider>
|
||||
</div>
|
||||
<div class="col-sm-2">
|
||||
<input type="number" min="0" class="form-control" ng-model="formValues.MemoryLimit" id="memory-limit" />
|
||||
</div>
|
||||
<div class="col-sm-4">
|
||||
<p class="small text-muted" style="margin-top: 7px;">
|
||||
Memory limit (<b>MB</b>)
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
<!-- !memory-limit-input -->
|
||||
<!-- cpu-limit-input -->
|
||||
<div class="form-group">
|
||||
<label for="cpu-limit" class="col-sm-3 col-lg-2 control-label text-left" style="margin-top: 20px;">
|
||||
CPU limit
|
||||
</label>
|
||||
<div class="col-sm-5">
|
||||
<slider model="formValues.CpuLimit" floor="0" ceil="state.sliderMaxCpu" step="0.25" precision="2" ng-if="state.sliderMaxCpu"></slider>
|
||||
</div>
|
||||
<div class="col-sm-4" style="margin-top: 20px;">
|
||||
<p class="small text-muted">
|
||||
Maximum CPU usage
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
<!-- !cpu-limit-input -->
|
||||
</form>
|
||||
</div>
|
||||
<!-- !tab-runtime-resources -->
|
||||
|
|
|
@ -3,7 +3,7 @@ angular.module('portainer.app').component('slider', {
|
|||
controller: 'SliderController',
|
||||
bindings: {
|
||||
model: '=',
|
||||
onChange: '&',
|
||||
onChange: '<',
|
||||
floor: '<',
|
||||
ceil: '<',
|
||||
step: '<',
|
||||
|
|
Loading…
Reference in New Issue