mirror of https://github.com/portainer/portainer
fix(container): runtime and resources issues EE-6306 (#10611)
parent
1bcbfb8213
commit
dc574af734
|
@ -138,14 +138,14 @@ function CreateForm() {
|
||||||
});
|
});
|
||||||
|
|
||||||
if (!confirmed) {
|
if (!confirmed) {
|
||||||
return;
|
return false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
const registry = getRegistry(values.image, registriesQuery.data || []);
|
const registry = getRegistry(values.image, registriesQuery.data || []);
|
||||||
const config = toRequest(values, registry, hideCapabilities);
|
const config = toRequest(values, registry, hideCapabilities);
|
||||||
|
|
||||||
mutation.mutate(
|
return mutation.mutate(
|
||||||
{ config, environment, values, registry, oldContainer, extraNetworks },
|
{ config, environment, values, registry, oldContainer, extraNetworks },
|
||||||
{
|
{
|
||||||
onSuccess() {
|
onSuccess() {
|
||||||
|
|
|
@ -186,13 +186,13 @@ export function InnerForm({
|
||||||
? (values) => (
|
? (values) => (
|
||||||
<EditResourcesForm
|
<EditResourcesForm
|
||||||
initialValues={values}
|
initialValues={values}
|
||||||
redeploy={(values) => {
|
onChange={(values) => {
|
||||||
setFieldValue(
|
setFieldValue(
|
||||||
'resources.resources',
|
'resources.resources',
|
||||||
values
|
values
|
||||||
);
|
);
|
||||||
return submitForm();
|
|
||||||
}}
|
}}
|
||||||
|
redeploy={submitForm}
|
||||||
isImageInvalid={!!errors?.image}
|
isImageInvalid={!!errors?.image}
|
||||||
/>
|
/>
|
||||||
)
|
)
|
||||||
|
|
|
@ -1,7 +1,10 @@
|
||||||
import { Formik } from 'formik';
|
import { Formik } from 'formik';
|
||||||
import { useMutation } from 'react-query';
|
import { useMutation } from 'react-query';
|
||||||
import { useCurrentStateAndParams } from '@uirouter/react';
|
import { useCurrentStateAndParams } from '@uirouter/react';
|
||||||
|
import { useState } from 'react';
|
||||||
|
import { FormikHelpers } from 'formik/dist/types';
|
||||||
|
|
||||||
|
import { invalidateContainer } from '@/react/docker/containers/queries/container';
|
||||||
import { notifySuccess } from '@/portainer/services/notifications';
|
import { notifySuccess } from '@/portainer/services/notifications';
|
||||||
import { mutationOptions, withError } from '@/react-tools/react-query';
|
import { mutationOptions, withError } from '@/react-tools/react-query';
|
||||||
import { useSystemLimits } from '@/react/docker/proxy/queries/useInfo';
|
import { useSystemLimits } from '@/react/docker/proxy/queries/useInfo';
|
||||||
|
@ -21,17 +24,22 @@ import {
|
||||||
import { toConfigCpu, toConfigMemory } from './memory-utils';
|
import { toConfigCpu, toConfigMemory } from './memory-utils';
|
||||||
|
|
||||||
export function EditResourcesForm({
|
export function EditResourcesForm({
|
||||||
|
onChange,
|
||||||
redeploy,
|
redeploy,
|
||||||
initialValues,
|
initialValues,
|
||||||
isImageInvalid,
|
isImageInvalid,
|
||||||
}: {
|
}: {
|
||||||
initialValues: Values;
|
initialValues: Values;
|
||||||
redeploy: (values: Values) => Promise<void>;
|
onChange: (values: Values) => void;
|
||||||
|
redeploy: () => Promise<boolean>;
|
||||||
isImageInvalid: boolean;
|
isImageInvalid: boolean;
|
||||||
}) {
|
}) {
|
||||||
const {
|
const {
|
||||||
params: { from: containerId },
|
params: { from: containerId },
|
||||||
} = useCurrentStateAndParams();
|
} = useCurrentStateAndParams();
|
||||||
|
|
||||||
|
const [savedInitValues, setSavedInitValues] = useState(initialValues);
|
||||||
|
|
||||||
if (!containerId || typeof containerId !== 'string') {
|
if (!containerId || typeof containerId !== 'string') {
|
||||||
throw new Error('missing parameter "from"');
|
throw new Error('missing parameter "from"');
|
||||||
}
|
}
|
||||||
|
@ -54,7 +62,10 @@ export function EditResourcesForm({
|
||||||
<div className="edit-resources p-5">
|
<div className="edit-resources p-5">
|
||||||
<ResourceFieldset
|
<ResourceFieldset
|
||||||
values={values}
|
values={values}
|
||||||
onChange={setValues}
|
onChange={(values) => {
|
||||||
|
onChange(values);
|
||||||
|
setValues(values);
|
||||||
|
}}
|
||||||
errors={errors}
|
errors={errors}
|
||||||
/>
|
/>
|
||||||
|
|
||||||
|
@ -82,28 +93,44 @@ export function EditResourcesForm({
|
||||||
</Formik>
|
</Formik>
|
||||||
);
|
);
|
||||||
|
|
||||||
function handleSubmit(values: Values) {
|
function handleSubmit(values: Values, helper: FormikHelpers<Values>) {
|
||||||
updateMutation.mutate(values, {
|
updateMutation.mutate(values, {
|
||||||
onSuccess: () => {
|
onSuccess: (data) => {
|
||||||
notifySuccess('Success', 'Limits updated');
|
if (data) {
|
||||||
|
notifySuccess('Success', 'Limits updated');
|
||||||
|
helper.resetForm({ values: initialValues });
|
||||||
|
invalidateContainer(environmentId, containerId);
|
||||||
|
}
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
function settingUnlimitedResources(values: Values) {
|
function settingUnlimitedResources(values: Values) {
|
||||||
return (
|
return (
|
||||||
(initialValues.limit > 0 && values.limit === 0) ||
|
(savedInitValues.limit > 0 && values.limit === 0) ||
|
||||||
(initialValues.reservation > 0 && values.reservation === 0) ||
|
(savedInitValues.reservation > 0 && values.reservation === 0) ||
|
||||||
(initialValues.cpu > 0 && values.cpu === 0)
|
(savedInitValues.cpu > 0 && values.cpu === 0)
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// return true only if limits are updated
|
||||||
|
// return false otherwise(container recreated, user canceled container recreation, etc.)
|
||||||
async function updateLimitsOrCreate(values: Values) {
|
async function updateLimitsOrCreate(values: Values) {
|
||||||
if (settingUnlimitedResources(values)) {
|
if (settingUnlimitedResources(values)) {
|
||||||
return redeploy(values);
|
const ret = await redeploy();
|
||||||
|
|
||||||
|
if (ret === false) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
setSavedInitValues(values);
|
||||||
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
return updateLimits(environmentId, containerId, values);
|
setSavedInitValues(values);
|
||||||
|
await updateLimits(environmentId, containerId, values);
|
||||||
|
|
||||||
|
return true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -135,7 +135,7 @@ export function GpuFieldset({
|
||||||
checked={values.enabled && !!enableGpuManagement}
|
checked={values.enabled && !!enableGpuManagement}
|
||||||
onChange={toggleEnableGpu}
|
onChange={toggleEnableGpu}
|
||||||
className="ml-2"
|
className="ml-2"
|
||||||
disabled={enableGpuManagement === false}
|
disabled={!enableGpuManagement}
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
{enableGpuManagement && values.enabled && (
|
{enableGpuManagement && values.enabled && (
|
||||||
|
|
|
@ -35,6 +35,7 @@ export function ResourceFieldset({
|
||||||
<FormSection title="Resources">
|
<FormSection title="Resources">
|
||||||
<FormControl label="Memory reservation (MB)" errors={errors?.reservation}>
|
<FormControl label="Memory reservation (MB)" errors={errors?.reservation}>
|
||||||
<SliderWithInput
|
<SliderWithInput
|
||||||
|
visibleTooltip
|
||||||
value={values.reservation}
|
value={values.reservation}
|
||||||
onChange={(value) => onChange({ ...values, reservation: value })}
|
onChange={(value) => onChange({ ...values, reservation: value })}
|
||||||
max={maxMemory}
|
max={maxMemory}
|
||||||
|
@ -45,6 +46,7 @@ export function ResourceFieldset({
|
||||||
|
|
||||||
<FormControl label="Memory limit (MB)" errors={errors?.limit}>
|
<FormControl label="Memory limit (MB)" errors={errors?.limit}>
|
||||||
<SliderWithInput
|
<SliderWithInput
|
||||||
|
visibleTooltip
|
||||||
value={values.limit}
|
value={values.limit}
|
||||||
onChange={(value) => onChange({ ...values, limit: value })}
|
onChange={(value) => onChange({ ...values, limit: value })}
|
||||||
max={maxMemory}
|
max={maxMemory}
|
||||||
|
@ -55,6 +57,7 @@ export function ResourceFieldset({
|
||||||
|
|
||||||
<FormControl label="Maximum CPU usage" errors={errors?.cpu}>
|
<FormControl label="Maximum CPU usage" errors={errors?.cpu}>
|
||||||
<Slider
|
<Slider
|
||||||
|
visibleTooltip
|
||||||
value={values.cpu}
|
value={values.cpu}
|
||||||
onChange={(value) =>
|
onChange={(value) =>
|
||||||
onChange({
|
onChange({
|
||||||
|
|
|
@ -12,6 +12,7 @@ import { PortainerResponse } from '@/react/docker/types';
|
||||||
import axios, { parseAxiosError } from '@/portainer/services/axios';
|
import axios, { parseAxiosError } from '@/portainer/services/axios';
|
||||||
import { ContainerId } from '@/react/docker/containers/types';
|
import { ContainerId } from '@/react/docker/containers/types';
|
||||||
import { EnvironmentId } from '@/react/portainer/environments/types';
|
import { EnvironmentId } from '@/react/portainer/environments/types';
|
||||||
|
import { queryClient } from '@/react-tools/react-query';
|
||||||
|
|
||||||
import { urlBuilder } from '../containers.service';
|
import { urlBuilder } from '../containers.service';
|
||||||
|
|
||||||
|
@ -89,6 +90,15 @@ export function useContainer(
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export function invalidateContainer(
|
||||||
|
environmentId: EnvironmentId,
|
||||||
|
containerId?: ContainerId
|
||||||
|
) {
|
||||||
|
return queryClient.invalidateQueries(
|
||||||
|
containerId ? queryKeys.container(environmentId, containerId) : []
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
export type ContainerResponse = PortainerResponse<ContainerJSON>;
|
export type ContainerResponse = PortainerResponse<ContainerJSON>;
|
||||||
|
|
||||||
async function getContainer(
|
async function getContainer(
|
||||||
|
|
Loading…
Reference in New Issue