mirror of
https://github.com/portainer/portainer.git
synced 2025-08-02 20:35:25 +02:00
refactor(containers): migrate resources tab to react [EE-5214] (#10355)
This commit is contained in:
parent
ec091efe3b
commit
ffac83864d
28 changed files with 1114 additions and 537 deletions
|
@ -25,6 +25,11 @@ import {
|
|||
NetworkTab,
|
||||
type NetworkTabValues,
|
||||
} from '@/react/docker/containers/CreateView/NetworkTab';
|
||||
import {
|
||||
ResourcesTab,
|
||||
resourcesTabUtils,
|
||||
type ResourcesTabValues,
|
||||
} from '@/react/docker/containers/CreateView/ResourcesTab';
|
||||
|
||||
const ngModule = angular
|
||||
.module('portainer.docker.react.components.containers', [])
|
||||
|
@ -70,3 +75,19 @@ withFormValidation<ComponentProps<typeof NetworkTab>, NetworkTabValues>(
|
|||
[],
|
||||
networkTabUtils.validation
|
||||
);
|
||||
|
||||
withFormValidation<ComponentProps<typeof ResourcesTab>, ResourcesTabValues>(
|
||||
ngModule,
|
||||
withUIRouter(withReactQuery(ResourcesTab)),
|
||||
'dockerCreateContainerResourcesTab',
|
||||
[
|
||||
'allowPrivilegedMode',
|
||||
'isDevicesFieldVisible',
|
||||
'isInitFieldVisible',
|
||||
'isSysctlFieldVisible',
|
||||
'isDuplicate',
|
||||
'isImageInvalid',
|
||||
'redeploy',
|
||||
],
|
||||
resourcesTabUtils.validation
|
||||
);
|
||||
|
|
|
@ -6,7 +6,6 @@ import { StackContainersDatatable } from '@/react/common/stacks/ItemView/StackCo
|
|||
import { ContainerQuickActions } from '@/react/docker/containers/components/ContainerQuickActions';
|
||||
import { TemplateListDropdownAngular } from '@/react/docker/app-templates/TemplateListDropdown';
|
||||
import { TemplateListSortAngular } from '@/react/docker/app-templates/TemplateListSort';
|
||||
import { Gpu } from '@/react/docker/containers/CreateView/Gpu';
|
||||
import { withCurrentUser } from '@/react-tools/withCurrentUser';
|
||||
import { withReactQuery } from '@/react-tools/withReactQuery';
|
||||
import { withUIRouter } from '@/react-tools/withUIRouter';
|
||||
|
@ -57,17 +56,6 @@ const ngModule = angular
|
|||
['environment', 'stackName']
|
||||
)
|
||||
)
|
||||
.component(
|
||||
'gpu',
|
||||
r2a(Gpu, [
|
||||
'values',
|
||||
'onChange',
|
||||
'gpus',
|
||||
'usedGpus',
|
||||
'usedAllGpus',
|
||||
'enableGpuManagement',
|
||||
])
|
||||
)
|
||||
.component(
|
||||
'gpusList',
|
||||
r2a(withControlledInput(GpusList), ['value', 'onChange'])
|
||||
|
|
|
@ -16,6 +16,7 @@ import { ContainerDetailsViewModel } from '@/docker/models/container';
|
|||
import './createcontainer.css';
|
||||
import { envVarsTabUtils } from '@/react/docker/containers/CreateView/EnvVarsTab';
|
||||
import { getContainers } from '@/react/docker/containers/queries/containers';
|
||||
import { resourcesTabUtils } from '@/react/docker/containers/CreateView/ResourcesTab';
|
||||
|
||||
angular.module('portainer.docker').controller('CreateContainerController', [
|
||||
'$q',
|
||||
|
@ -65,7 +66,6 @@ angular.module('portainer.docker').controller('CreateContainerController', [
|
|||
endpoint
|
||||
) {
|
||||
$scope.create = create;
|
||||
$scope.update = update;
|
||||
$scope.endpoint = endpoint;
|
||||
$scope.containerWebhookFeature = FeatureId.CONTAINER_WEBHOOK;
|
||||
$scope.formValues = {
|
||||
|
@ -84,18 +84,14 @@ angular.module('portainer.docker').controller('CreateContainerController', [
|
|||
DnsPrimary: '',
|
||||
DnsSecondary: '',
|
||||
AccessControlData: new AccessControlFormData(),
|
||||
CpuLimit: 0,
|
||||
MemoryLimit: 0,
|
||||
MemoryReservation: 0,
|
||||
ShmSize: 64,
|
||||
NodeName: null,
|
||||
capabilities: [],
|
||||
Sysctls: [],
|
||||
RegistryModel: new PorImageRegistryModel(),
|
||||
commands: commandsTabUtils.getDefaultViewModel(),
|
||||
envVars: envVarsTabUtils.getDefaultViewModel(),
|
||||
volumes: volumesTabUtils.getDefaultViewModel(),
|
||||
network: networkTabUtils.getDefaultViewModel(),
|
||||
resources: resourcesTabUtils.getDefaultViewModel(),
|
||||
};
|
||||
|
||||
$scope.state = {
|
||||
|
@ -138,6 +134,12 @@ angular.module('portainer.docker').controller('CreateContainerController', [
|
|||
});
|
||||
};
|
||||
|
||||
$scope.onResourcesChange = function (resources) {
|
||||
return $scope.$evalAsync(() => {
|
||||
$scope.formValues.resources = resources;
|
||||
});
|
||||
};
|
||||
|
||||
function onAlwaysPullChange(checked) {
|
||||
return $scope.$evalAsync(() => {
|
||||
$scope.formValues.alwaysPull = checked;
|
||||
|
@ -299,57 +301,6 @@ angular.module('portainer.docker').controller('CreateContainerController', [
|
|||
config.Labels = labels;
|
||||
}
|
||||
|
||||
function prepareDevices(config) {
|
||||
var path = [];
|
||||
config.HostConfig.Devices.forEach(function (p) {
|
||||
if (p.pathOnHost) {
|
||||
if (p.pathInContainer === '') {
|
||||
p.pathInContainer = p.pathOnHost;
|
||||
}
|
||||
path.push({ PathOnHost: p.pathOnHost, PathInContainer: p.pathInContainer, CgroupPermissions: 'rwm' });
|
||||
}
|
||||
});
|
||||
config.HostConfig.Devices = path;
|
||||
}
|
||||
|
||||
function prepareSysctls(config) {
|
||||
var sysctls = {};
|
||||
$scope.formValues.Sysctls.forEach(function (sysctl) {
|
||||
if (sysctl.name && sysctl.value) {
|
||||
sysctls[sysctl.name] = sysctl.value;
|
||||
}
|
||||
});
|
||||
config.HostConfig.Sysctls = sysctls;
|
||||
}
|
||||
|
||||
function prepareResources(config) {
|
||||
// Shared Memory Size - Round to 0.125
|
||||
if ($scope.formValues.ShmSize >= 0) {
|
||||
var shmSize = (Math.round($scope.formValues.ShmSize * 8) / 8).toFixed(3);
|
||||
shmSize *= 1024 * 1024;
|
||||
config.HostConfig.ShmSize = shmSize;
|
||||
}
|
||||
|
||||
// Memory Limit - Round to 0.125
|
||||
if ($scope.formValues.MemoryLimit >= 0) {
|
||||
var memoryLimit = (Math.round($scope.formValues.MemoryLimit * 8) / 8).toFixed(3);
|
||||
memoryLimit *= 1024 * 1024;
|
||||
config.HostConfig.Memory = memoryLimit;
|
||||
}
|
||||
|
||||
// Memory Resevation - Round to 0.125
|
||||
if ($scope.formValues.MemoryReservation >= 0) {
|
||||
var memoryReservation = (Math.round($scope.formValues.MemoryReservation * 8) / 8).toFixed(3);
|
||||
memoryReservation *= 1024 * 1024;
|
||||
config.HostConfig.MemoryReservation = memoryReservation;
|
||||
}
|
||||
|
||||
// CPU Limit
|
||||
if ($scope.formValues.CpuLimit >= 0) {
|
||||
config.HostConfig.NanoCpus = $scope.formValues.CpuLimit * 1000000000;
|
||||
}
|
||||
}
|
||||
|
||||
function prepareCapabilities(config) {
|
||||
var allowed = $scope.formValues.capabilities.filter(function (item) {
|
||||
return item.allowed === true;
|
||||
|
@ -365,51 +316,18 @@ angular.module('portainer.docker').controller('CreateContainerController', [
|
|||
config.HostConfig.CapDrop = notAllowed.map(getCapName);
|
||||
}
|
||||
|
||||
function prepareGPUOptions(config) {
|
||||
const driver = 'nvidia';
|
||||
const gpuOptions = $scope.formValues.GPU;
|
||||
const existingDeviceRequest = _.find($scope.config.HostConfig.DeviceRequests, { Driver: driver });
|
||||
if (existingDeviceRequest) {
|
||||
_.pullAllBy(config.HostConfig.DeviceRequests, [existingDeviceRequest], 'Driver');
|
||||
}
|
||||
if (!gpuOptions.enabled) {
|
||||
return;
|
||||
}
|
||||
const deviceRequest = {
|
||||
Driver: driver,
|
||||
Count: -1,
|
||||
DeviceIDs: [], // must be empty if Count != 0 https://github.com/moby/moby/blob/master/daemon/nvidia_linux.go#L50
|
||||
Capabilities: [], // array of ORed arrays of ANDed capabilites = [ [c1 AND c2] OR [c1 AND c3] ] : https://github.com/moby/moby/blob/master/api/types/container/host_config.go#L272
|
||||
// Options: { property1: "string", property2: "string" }, // seems to never be evaluated/used in docker API ?
|
||||
};
|
||||
if (gpuOptions.useSpecific) {
|
||||
deviceRequest.DeviceIDs = gpuOptions.selectedGPUs;
|
||||
deviceRequest.Count = 0;
|
||||
}
|
||||
deviceRequest.Capabilities = [gpuOptions.capabilities];
|
||||
|
||||
if (config.HostConfig.DeviceRequests) {
|
||||
config.HostConfig.DeviceRequests.push(deviceRequest);
|
||||
} else {
|
||||
config.HostConfig.DeviceRequests = [deviceRequest];
|
||||
}
|
||||
}
|
||||
|
||||
function prepareConfiguration() {
|
||||
var config = angular.copy($scope.config);
|
||||
config = commandsTabUtils.toRequest(config, $scope.formValues.commands);
|
||||
config = envVarsTabUtils.toRequest(config, $scope.formValues.envVars);
|
||||
config = volumesTabUtils.toRequest(config, $scope.formValues.volumes);
|
||||
config = networkTabUtils.toRequest(config, $scope.formValues.network, $scope.fromContainer.Id);
|
||||
config = resourcesTabUtils.toRequest(config, $scope.formValues.resources);
|
||||
|
||||
prepareImageConfig(config);
|
||||
preparePortBindings(config);
|
||||
prepareLabels(config);
|
||||
prepareDevices(config);
|
||||
prepareResources(config);
|
||||
prepareCapabilities(config);
|
||||
prepareSysctls(config);
|
||||
prepareGPUOptions(config);
|
||||
return config;
|
||||
}
|
||||
|
||||
|
@ -426,45 +344,6 @@ angular.module('portainer.docker').controller('CreateContainerController', [
|
|||
}
|
||||
}
|
||||
|
||||
function loadFromContainerDevices() {
|
||||
var path = [];
|
||||
for (var dev in $scope.config.HostConfig.Devices) {
|
||||
if ({}.hasOwnProperty.call($scope.config.HostConfig.Devices, dev)) {
|
||||
var device = $scope.config.HostConfig.Devices[dev];
|
||||
path.push({ pathOnHost: device.PathOnHost, pathInContainer: device.PathInContainer });
|
||||
}
|
||||
}
|
||||
$scope.config.HostConfig.Devices = path;
|
||||
}
|
||||
|
||||
function loadFromContainerDeviceRequests() {
|
||||
const deviceRequest = _.find($scope.config.HostConfig.DeviceRequests, function (o) {
|
||||
return o.Driver === 'nvidia' || o.Capabilities[0][0] === 'gpu';
|
||||
});
|
||||
if (deviceRequest) {
|
||||
$scope.formValues.GPU.enabled = true;
|
||||
$scope.formValues.GPU.useSpecific = deviceRequest.Count !== -1;
|
||||
$scope.formValues.GPU.selectedGPUs = deviceRequest.DeviceIDs || [];
|
||||
if ($scope.formValues.GPU.useSpecific) {
|
||||
$scope.formValues.GPU.selectedGPUs = deviceRequest.DeviceIDs;
|
||||
} else {
|
||||
$scope.formValues.GPU.selectedGPUs = ['all'];
|
||||
}
|
||||
// we only support a single set of capabilities for now
|
||||
// UI needs to be reworked in order to support OR combinations of AND capabilities
|
||||
$scope.formValues.GPU.capabilities = deviceRequest.Capabilities[0];
|
||||
$scope.formValues.GPU = { ...$scope.formValues.GPU };
|
||||
}
|
||||
}
|
||||
|
||||
function loadFromContainerSysctls() {
|
||||
for (var s in $scope.config.HostConfig.Sysctls) {
|
||||
if ({}.hasOwnProperty.call($scope.config.HostConfig.Sysctls, s)) {
|
||||
$scope.formValues.Sysctls.push({ name: s, value: $scope.config.HostConfig.Sysctls[s] });
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function loadFromContainerImageConfig() {
|
||||
RegistryService.retrievePorRegistryModelFromRepository($scope.config.Image, endpoint.Id)
|
||||
.then((model) => {
|
||||
|
@ -475,21 +354,6 @@ angular.module('portainer.docker').controller('CreateContainerController', [
|
|||
});
|
||||
}
|
||||
|
||||
function loadFromContainerResources(d) {
|
||||
if (d.HostConfig.NanoCpus) {
|
||||
$scope.formValues.CpuLimit = d.HostConfig.NanoCpus / 1000000000;
|
||||
}
|
||||
if (d.HostConfig.Memory) {
|
||||
$scope.formValues.MemoryLimit = d.HostConfig.Memory / 1024 / 1024;
|
||||
}
|
||||
if (d.HostConfig.MemoryReservation) {
|
||||
$scope.formValues.MemoryReservation = d.HostConfig.MemoryReservation / 1024 / 1024;
|
||||
}
|
||||
if (d.HostConfig.ShmSize) {
|
||||
$scope.formValues.ShmSize = d.HostConfig.ShmSize / 1024 / 1024;
|
||||
}
|
||||
}
|
||||
|
||||
function loadFromContainerCapabilities(d) {
|
||||
if (d.HostConfig.CapAdd) {
|
||||
d.HostConfig.CapAdd.forEach(function (cap) {
|
||||
|
@ -543,15 +407,13 @@ angular.module('portainer.docker').controller('CreateContainerController', [
|
|||
$scope.formValues.envVars = envVarsTabUtils.toViewModel(d);
|
||||
$scope.formValues.volumes = volumesTabUtils.toViewModel(d);
|
||||
$scope.formValues.network = networkTabUtils.toViewModel(d, $scope.availableNetworks, $scope.runningContainers);
|
||||
$scope.formValues.resources = resourcesTabUtils.toViewModel(d);
|
||||
|
||||
loadFromContainerPortBindings(d);
|
||||
loadFromContainerLabels(d);
|
||||
loadFromContainerDevices(d);
|
||||
loadFromContainerDeviceRequests(d);
|
||||
loadFromContainerImageConfig(d);
|
||||
loadFromContainerResources(d);
|
||||
|
||||
loadFromContainerCapabilities(d);
|
||||
loadFromContainerSysctls(d);
|
||||
})
|
||||
.then(() => {
|
||||
$scope.state.containerIsLoaded = true;
|
||||
|
@ -568,7 +430,7 @@ angular.module('portainer.docker').controller('CreateContainerController', [
|
|||
|
||||
$scope.isAdmin = Authentication.isAdmin();
|
||||
$scope.showDeviceMapping = await shouldShowDevices();
|
||||
$scope.showSysctls = await shouldShowSysctls();
|
||||
$scope.allowSysctl = await shouldShowSysctls();
|
||||
$scope.areContainerCapabilitiesEnabled = await checkIfContainerCapabilitiesEnabled();
|
||||
$scope.isAdminOrEndpointAdmin = Authentication.isAdmin();
|
||||
|
||||
|
@ -647,27 +509,12 @@ angular.module('portainer.docker').controller('CreateContainerController', [
|
|||
}
|
||||
}
|
||||
|
||||
async function updateLimits(config) {
|
||||
try {
|
||||
if ($scope.state.settingUnlimitedResources) {
|
||||
create();
|
||||
} else {
|
||||
await ContainerService.updateLimits($transition$.params().from, config);
|
||||
$scope.config = config;
|
||||
Notifications.success('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;
|
||||
}
|
||||
$scope.redeployUnlimitedResources = function (resources) {
|
||||
return $async(async () => {
|
||||
$scope.formValues.resources = resources;
|
||||
return create();
|
||||
});
|
||||
};
|
||||
|
||||
function create() {
|
||||
var oldContainer = null;
|
||||
|
|
|
@ -283,233 +283,21 @@
|
|||
<!-- !tab-restart-policy -->
|
||||
<!-- tab-runtime-resources -->
|
||||
<div class="tab-pane" id="runtime-resources">
|
||||
<form class="form-horizontal" style="margin-top: 15px">
|
||||
<div class="col-sm-12 form-section-title"> Runtime </div>
|
||||
<!-- privileged-mode -->
|
||||
<div class="form-group" ng-if="isAdmin || allowPrivilegedMode">
|
||||
<div class="col-sm-12">
|
||||
<por-switch-field
|
||||
label-class="'col-sm-2'"
|
||||
checked="config.HostConfig.Privileged"
|
||||
label="'Privileged mode'"
|
||||
on-change="(handlePrivilegedChange)"
|
||||
></por-switch-field>
|
||||
</div>
|
||||
</div>
|
||||
<!-- !privileged-mode -->
|
||||
<!-- init -->
|
||||
<div class="form-group" ng-if="applicationState.endpoint.apiVersion >= 1.37">
|
||||
<div class="col-sm-12">
|
||||
<por-switch-field label-class="'col-sm-2'" checked="config.HostConfig.Init" label="'Init'" on-change="(handleInitChange)"></por-switch-field>
|
||||
</div>
|
||||
</div>
|
||||
<!-- !init -->
|
||||
<!-- runtimes -->
|
||||
<div class="form-group">
|
||||
<label for="container_runtime" class="col-sm-1 control-label text-left">Runtime</label>
|
||||
<div class="col-sm-11">
|
||||
<select class="form-control" ng-model="config.HostConfig.Runtime" id="container_runtime" ng-options="runtime for runtime in availableRuntimes">
|
||||
<option selected value="">Default</option>
|
||||
</select>
|
||||
</div>
|
||||
</div>
|
||||
<!-- !runtimes -->
|
||||
</form>
|
||||
<form class="form-horizontal" style="margin-top: 15px" name="resourceForm">
|
||||
<!-- devices -->
|
||||
<div ng-if="showDeviceMapping" class="form-group">
|
||||
<div class="col-sm-12" style="margin-top: 5px">
|
||||
<label class="control-label text-left">Devices</label>
|
||||
<span class="label label-default interactive" style="margin-left: 10px" ng-click="addDevice()">
|
||||
<pr-icon icon="'plus'" mode="'alt'"></pr-icon> add device
|
||||
</span>
|
||||
</div>
|
||||
<!-- devices-input-list -->
|
||||
<div class="col-sm-12 form-inline" style="margin-top: 10px">
|
||||
<div ng-repeat="device in config.HostConfig.Devices" style="margin-top: 2px">
|
||||
<div class="input-group col-sm-5 input-group-sm">
|
||||
<span class="input-group-addon">host</span>
|
||||
<input type="text" class="form-control" ng-model="device.pathOnHost" placeholder="e.g. /dev/tty0" />
|
||||
</div>
|
||||
<div class="input-group col-sm-5 input-group-sm">
|
||||
<span class="input-group-addon">container</span>
|
||||
<input type="text" class="form-control" ng-model="device.pathInContainer" placeholder="e.g. /dev/tty0" />
|
||||
</div>
|
||||
<button class="btn btn-sm btn-light" type="button" ng-click="removeDevice($index)">
|
||||
<pr-icon icon="'trash-2'" class-name="'icon-secondary icon-md'"></pr-icon>
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
<!-- !devices-input-list -->
|
||||
</div>
|
||||
<!-- !devices-->
|
||||
<!-- sysctls -->
|
||||
<div ng-if="showSysctls" class="form-group">
|
||||
<div class="col-sm-12" style="margin-top: 5px">
|
||||
<label class="control-label text-left">Sysctls</label>
|
||||
<span class="label label-default interactive" style="margin-left: 10px" ng-click="addSysctl()"> <pr-icon icon="'plus'"></pr-icon> add sysctl </span>
|
||||
</div>
|
||||
<!-- sysctls-input-list -->
|
||||
<div class="col-sm-12 form-inline" style="margin-top: 10px">
|
||||
<div ng-repeat="sysctl in formValues.Sysctls" style="margin-top: 2px">
|
||||
<div class="input-group col-sm-5 input-group-sm">
|
||||
<span class="input-group-addon">name</span>
|
||||
<input type="text" class="form-control" ng-model="sysctl.name" placeholder="e.g. FOO" />
|
||||
</div>
|
||||
<div class="input-group col-sm-5 input-group-sm">
|
||||
<span class="input-group-addon">value</span>
|
||||
<input type="text" class="form-control" ng-model="sysctl.value" placeholder="e.g. bar" />
|
||||
</div>
|
||||
<button class="btn btn-sm btn-light" type="button" ng-click="removeSysctl($index)">
|
||||
<pr-icon icon="'trash-2'" class-name="'icon-secondary icon-md'"></pr-icon>
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
<!-- !sysctls-input-list -->
|
||||
</div>
|
||||
<!-- !sysctls -->
|
||||
<!-- shm-size-input -->
|
||||
<div class="form-group">
|
||||
<label for="shm-size" class="col-sm-2 control-label text-left"> Shared memory size </label>
|
||||
<div class="col-sm-2">
|
||||
<input type="number" min="1" class="form-control" ng-model="formValues.ShmSize" id="shm-size" />
|
||||
</div>
|
||||
<div class="col-sm-2">
|
||||
<p class="small text-muted mt-2"> Size of /dev/shm (<b>MB</b>) </p>
|
||||
</div>
|
||||
</div>
|
||||
<!-- !shm-size-input -->
|
||||
<!-- #region GPU -->
|
||||
<div ng-if="applicationState.endpoint.mode.provider === 'DOCKER_STANDALONE'">
|
||||
<div class="col-sm-12 form-section-title"> GPU </div>
|
||||
<gpu
|
||||
ng-if="applicationState.endpoint.apiVersion >= 1.4"
|
||||
values="formValues.GPU"
|
||||
on-change="(onGpuChange)"
|
||||
gpus="endpoint.Gpus"
|
||||
used-gpus="gpuUseList"
|
||||
used-all-gpus="gpuUseAll"
|
||||
enable-gpu-management="endpoint.EnableGPUManagement"
|
||||
>
|
||||
</gpu>
|
||||
</div>
|
||||
<!-- #endregion GPU -->
|
||||
<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 flex">
|
||||
<label for="memory-reservation" class="col-sm-3 col-lg-2 control-label vertical-center text-left"> Memory reservation (MB) </label>
|
||||
<div class="col-sm-6">
|
||||
<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 vertical-center">
|
||||
<input
|
||||
name="memory_reservation"
|
||||
type="number"
|
||||
min="0"
|
||||
max="{{ state.sliderMaxMemory }}"
|
||||
class="form-control"
|
||||
ng-model="formValues.MemoryReservation"
|
||||
id="memory-reservation"
|
||||
required
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
<div class="form-group" ng-show="resourceForm.memory_reservation.$invalid">
|
||||
<div class="col-sm-3 col-lg-2"></div>
|
||||
<div class="col-sm-8 small text-muted">
|
||||
<div ng-messages="resourceForm.memory-reservation.$error">
|
||||
<p class="vertical-center text-warning">
|
||||
<pr-icon icon="'alert-triangle'" mode="'warning'"></pr-icon> Value must be between 0 and {{ state.sliderMaxMemory }}.
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<!-- !memory-reservation-input -->
|
||||
<!-- memory-limit-input -->
|
||||
<div class="form-group flex">
|
||||
<label for="memory-limit" class="col-sm-3 col-lg-2 control-label vertical-center text-left"> Memory limit (MB) </label>
|
||||
<div class="col-sm-6">
|
||||
<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 vertical-center">
|
||||
<input
|
||||
name="memory_Limit"
|
||||
type="number"
|
||||
min="0"
|
||||
max="{{ state.sliderMaxMemory }}"
|
||||
class="form-control"
|
||||
ng-model="formValues.MemoryLimit"
|
||||
id="memory-limit"
|
||||
required
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
<div class="form-group" ng-show="resourceForm.memory_Limit.$invalid">
|
||||
<div class="col-sm-3 col-lg-2"></div>
|
||||
<div class="col-sm-8 small text-muted">
|
||||
<div ng-messages="resourceForm.memory-limit.$error">
|
||||
<p class="vertical-center text-warning">
|
||||
<pr-icon icon="'alert-triangle'" mode="'warning'"></pr-icon> Value must be between 0 and {{ state.sliderMaxMemory }}.
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<!-- !memory-limit-input -->
|
||||
<!-- cpu-limit-input -->
|
||||
<div class="form-group flex">
|
||||
<label for="cpu-limit" class="col-sm-3 col-lg-2 control-label vertical-center text-left"> Maximum CPU usage </label>
|
||||
<div class="col-sm-8">
|
||||
<slider
|
||||
on-change="(handleResourceChange)"
|
||||
model="formValues.CpuLimit"
|
||||
floor="0"
|
||||
ceil="state.sliderMaxCpu"
|
||||
step="0.1"
|
||||
precision="2"
|
||||
ng-if="state.sliderMaxCpu"
|
||||
></slider>
|
||||
</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">
|
||||
<pr-icon icon="'alert-triangle'" mode="'warning'" class-name="'mt-10'"></pr-icon>
|
||||
Updating any resource value to ‘unlimited' will redeploy this container.
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
<!-- !update-limit-btn -->
|
||||
</div>
|
||||
</form>
|
||||
<docker-create-container-resources-tab
|
||||
values="formValues.resources"
|
||||
on-change="(onResourcesChange)"
|
||||
allow-privileged-mode="allowPrivilegedMode"
|
||||
is-devices-field-visible="showDeviceMapping"
|
||||
is-sysctl-field-visible="allowSysctl"
|
||||
is-init-field-visible="applicationState.endpoint.apiVersion >= 1.37"
|
||||
is-image-invalid="!formValues.RegistryModel.Image || (!formValues.RegistryModel.Registry && fromContainer)"
|
||||
redeploy="(redeployUnlimitedResources)"
|
||||
is-duplicate="state.mode == 'duplicate'"
|
||||
validation-data="{
|
||||
maxMemory: state.sliderMaxMemory,
|
||||
maxCpu: state.sliderMaxCpu,
|
||||
}"
|
||||
></docker-create-container-resources-tab>
|
||||
</div>
|
||||
<!-- !tab-runtime-resources -->
|
||||
<!-- tab-container-capabilities -->
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue