diff --git a/app/kubernetes/react/components/index.ts b/app/kubernetes/react/components/index.ts index f0ad11e94..eab580a93 100644 --- a/app/kubernetes/react/components/index.ts +++ b/app/kubernetes/react/components/index.ts @@ -30,6 +30,10 @@ import { configurationsValidationSchema } from '@/react/kubernetes/applications/ import { ConfigMapsFormSection } from '@/react/kubernetes/applications/components/ConfigurationsFormSection/ConfigMapsFormSection'; import { PersistedFoldersFormSection } from '@/react/kubernetes/applications/components/PersistedFoldersFormSection'; import { persistedFoldersValidation } from '@/react/kubernetes/applications/components/PersistedFoldersFormSection/persistedFoldersValidation'; +import { + ResourceReservationFormSection, + resourceReservationValidation, +} from '@/react/kubernetes/applications/components/ResourceReservationFormSection'; import { EnvironmentVariablesFieldset } from '@@/form-components/EnvironmentVariablesFieldset'; @@ -221,3 +225,16 @@ withFormValidation( ], persistedFoldersValidation ); + +withFormValidation( + ngModule, + withUIRouter(withCurrentUser(withReactQuery(ResourceReservationFormSection))), + 'resourceReservationFormSection', + [ + 'namespaceHasQuota', + 'resourceQuotaCapacityExceeded', + 'maxMemoryLimit', + 'maxCpuLimit', + ], + resourceReservationValidation +); diff --git a/app/kubernetes/views/applications/create/createApplication.html b/app/kubernetes/views/applications/create/createApplication.html index f559bb292..a4af13daa 100644 --- a/app/kubernetes/views/applications/create/createApplication.html +++ b/app/kubernetes/views/applications/create/createApplication.html @@ -432,102 +432,15 @@ -
Resource reservations
- -
-
- - Resource reservations are applied per instance of the application. -
-
- -
-
- - A resource quota is set on this namespace, you must specify resource reservations. Resource reservations are applied per instance of the application. Maximums - are inherited from the namespace quota. -
-
- -
-
- - This namespace has exhausted its resource capacity and you will not be able to deploy the application. Contact your administrator to expand the capacity of - the namespace. -
-
- - -
- -
- -
-
- -
-
-
-
-
-
-

Value must be between {{ ctrl.state.sliders.memory.min }} and - {{ ctrl.state.sliders.memory.max }} -

-
-
-
- - -
- -
- -
-
- -
-
-
- - These reservations would exceed the resources currently available in the cluster. -
-
- - +
Deployment
diff --git a/app/kubernetes/views/applications/create/createApplicationController.js b/app/kubernetes/views/applications/create/createApplicationController.js index 415c6acf1..2d0dabe44 100644 --- a/app/kubernetes/views/applications/create/createApplicationController.js +++ b/app/kubernetes/views/applications/create/createApplicationController.js @@ -154,6 +154,7 @@ class KubernetesCreateApplicationController { this.onConfigMapsChange = this.onConfigMapsChange.bind(this); this.onSecretsChange = this.onSecretsChange.bind(this); this.onChangePersistedFolder = this.onChangePersistedFolder.bind(this); + this.onChangeResourceReservation = this.onChangeResourceReservation.bind(this); } /* #endregion */ @@ -539,6 +540,13 @@ class KubernetesCreateApplicationController { } } + onChangeResourceReservation(values) { + return this.$async(async () => { + this.formValues.MemoryLimit = values.memoryLimit; + this.formValues.CpuLimit = values.cpuLimit; + }); + } + resourceQuotaCapacityExceeded() { return !this.state.sliders.memory.max || !this.state.sliders.cpu.max; } diff --git a/app/react/kubernetes/applications/components/ResourceReservationFormSection/ResourceReservationFormSection.tsx b/app/react/kubernetes/applications/components/ResourceReservationFormSection/ResourceReservationFormSection.tsx new file mode 100644 index 000000000..802417f97 --- /dev/null +++ b/app/react/kubernetes/applications/components/ResourceReservationFormSection/ResourceReservationFormSection.tsx @@ -0,0 +1,99 @@ +import { FormikErrors } from 'formik'; + +import { FormSection } from '@@/form-components/FormSection'; +import { TextTip } from '@@/Tip/TextTip'; +import { SliderWithInput } from '@@/form-components/Slider/SliderWithInput'; +import { FormControl } from '@@/form-components/FormControl'; +import { FormError } from '@@/form-components/FormError'; +import { Slider } from '@@/form-components/Slider'; + +import { ResourceQuotaFormValues } from './types'; + +type Props = { + values: ResourceQuotaFormValues; + onChange: (values: ResourceQuotaFormValues) => void; + errors: FormikErrors; + namespaceHasQuota: boolean; + resourceQuotaCapacityExceeded: boolean; + maxMemoryLimit: number; + maxCpuLimit: number; +}; + +export function ResourceReservationFormSection({ + values, + onChange, + errors, + namespaceHasQuota, + resourceQuotaCapacityExceeded, + maxMemoryLimit, + maxCpuLimit, +}: Props) { + return ( + + {!namespaceHasQuota && ( + + Resource reservations are applied per instance of the application. + + )} + {namespaceHasQuota && !resourceQuotaCapacityExceeded && ( + + A resource quota is set on this namespace, you must specify resource + reservations. Resource reservations are applied per instance of the + application. Maximums are inherited from the namespace quota. + + )} + {namespaceHasQuota && resourceQuotaCapacityExceeded && ( + + This namespace has exhausted its resource capacity and you will not be + able to deploy the application. Contact your administrator to expand + the capacity of the namespace. + + )} + +
+ onChange({ ...values, memoryLimit: value })} + max={maxMemoryLimit} + step={128} + dataCy="k8sAppCreate-memoryLimit" + visibleTooltip + /> + {errors?.memoryLimit && ( + {errors.memoryLimit} + )} +
+
+ +
+ + onChange( + typeof value === 'number' + ? { ...values, cpuLimit: value } + : { ...values, cpuLimit: value[0] ?? 0 } + ) + } + value={values.cpuLimit} + min={0} + max={maxCpuLimit} + step={0.01} + dataCy="k8sAppCreate-cpuLimitSlider" + visibleTooltip + /> + {errors?.cpuLimit && ( + {errors.cpuLimit} + )} +
+
+
+ ); +} diff --git a/app/react/kubernetes/applications/components/ResourceReservationFormSection/index.ts b/app/react/kubernetes/applications/components/ResourceReservationFormSection/index.ts new file mode 100644 index 000000000..7aa4471a5 --- /dev/null +++ b/app/react/kubernetes/applications/components/ResourceReservationFormSection/index.ts @@ -0,0 +1,2 @@ +export { ResourceReservationFormSection } from './ResourceReservationFormSection'; +export { resourceReservationValidation } from './resourceReservationValidation'; diff --git a/app/react/kubernetes/applications/components/ResourceReservationFormSection/resourceReservationValidation.ts b/app/react/kubernetes/applications/components/ResourceReservationFormSection/resourceReservationValidation.ts new file mode 100644 index 000000000..11b5d708d --- /dev/null +++ b/app/react/kubernetes/applications/components/ResourceReservationFormSection/resourceReservationValidation.ts @@ -0,0 +1,29 @@ +import { SchemaOf, number, object } from 'yup'; + +import { ResourceQuotaFormValues } from './types'; + +type ValidationData = { + maxMemoryLimit: number; + maxCpuLimit: number; +}; + +export function resourceReservationValidation( + validationData?: ValidationData +): SchemaOf { + return object().shape({ + memoryLimit: number() + .min(0) + .max( + validationData?.maxMemoryLimit || 0, + `Value must be between 0 and ${validationData?.maxMemoryLimit}` + ) + .required(), + cpuLimit: number() + .min(0) + .max( + validationData?.maxCpuLimit || 0, + `Value must be between 0 and ${validationData?.maxCpuLimit}` + ) + .required(), + }); +} diff --git a/app/react/kubernetes/applications/components/ResourceReservationFormSection/types.ts b/app/react/kubernetes/applications/components/ResourceReservationFormSection/types.ts new file mode 100644 index 000000000..21a414d93 --- /dev/null +++ b/app/react/kubernetes/applications/components/ResourceReservationFormSection/types.ts @@ -0,0 +1,4 @@ +export type ResourceQuotaFormValues = { + memoryLimit: number; + cpuLimit: number; +};