portainer/app/react/docker/containers/CreateView/ResourcesTab/EditResourceForm.tsx

124 lines
3.5 KiB
TypeScript

import { Formik } from 'formik';
import { useMutation } from 'react-query';
import { useCurrentStateAndParams } from '@uirouter/react';
import { notifySuccess } from '@/portainer/services/notifications';
import { mutationOptions, withError } from '@/react-tools/react-query';
import { useSystemLimits } from '@/react/docker/proxy/queries/useInfo';
import { useEnvironmentId } from '@/react/hooks/useEnvironmentId';
import { EnvironmentId } from '@/react/portainer/environments/types';
import { LoadingButton } from '@@/buttons';
import { TextTip } from '@@/Tip/TextTip';
import { updateContainer } from '../../queries/useUpdateContainer';
import {
ResourceFieldset,
resourcesValidation,
Values,
} from './ResourcesFieldset';
import { toConfigCpu, toConfigMemory } from './memory-utils';
export function EditResourcesForm({
redeploy,
initialValues,
isImageInvalid,
}: {
initialValues: Values;
redeploy: (values: Values) => Promise<void>;
isImageInvalid: boolean;
}) {
const {
params: { from: containerId },
} = useCurrentStateAndParams();
if (!containerId || typeof containerId !== 'string') {
throw new Error('missing parameter "from"');
}
const updateMutation = useMutation(
updateLimitsOrCreate,
mutationOptions(withError('Failed to update limits'))
);
const environmentId = useEnvironmentId();
const systemLimits = useSystemLimits(environmentId);
return (
<Formik
initialValues={initialValues}
onSubmit={handleSubmit}
validationSchema={() => resourcesValidation(systemLimits)}
>
{({ values, errors, setValues, dirty, submitForm }) => (
<div className="edit-resources p-5">
<ResourceFieldset
values={values}
onChange={setValues}
errors={errors}
/>
<div className="form-group">
<div className="col-sm-12 flex items-center gap-4">
<LoadingButton
isLoading={updateMutation.isLoading}
disabled={isImageInvalid || !dirty}
loadingText="Update in progress..."
type="button"
onClick={submitForm}
>
Update Limits
</LoadingButton>
{settingUnlimitedResources(values) && (
<TextTip>
Updating any resource value to &apos;unlimited&apos; will
redeploy this container.
</TextTip>
)}
</div>
</div>
</div>
)}
</Formik>
);
function handleSubmit(values: Values) {
updateMutation.mutate(values, {
onSuccess: () => {
notifySuccess('Success', 'Limits updated');
},
});
}
function settingUnlimitedResources(values: Values) {
return (
(initialValues.limit > 0 && values.limit === 0) ||
(initialValues.reservation > 0 && values.reservation === 0) ||
(initialValues.cpu > 0 && values.cpu === 0)
);
}
async function updateLimitsOrCreate(values: Values) {
if (settingUnlimitedResources(values)) {
return redeploy(values);
}
return updateLimits(environmentId, containerId, values);
}
}
async function updateLimits(
environmentId: EnvironmentId,
containerId: string,
values: Values
) {
return updateContainer(environmentId, containerId, {
// MemorySwap: must be set
// -1: non limits, 0: treated as unset(cause update error).
MemorySwap: -1,
MemoryReservation: toConfigMemory(values.reservation),
Memory: toConfigMemory(values.limit),
NanoCpus: toConfigCpu(values.cpu),
});
}