mirror of https://github.com/portainer/portainer
fix(app): remove duplicate validation messages [EE-5933] (#10967)
parent
93593e1379
commit
a58b4f479b
|
@ -24,7 +24,7 @@ func (kcl *KubeClient) GetNodesLimits() (portainer.K8sNodesLimits, error) {
|
||||||
|
|
||||||
for _, item := range nodes.Items {
|
for _, item := range nodes.Items {
|
||||||
cpu := item.Status.Allocatable.Cpu().MilliValue()
|
cpu := item.Status.Allocatable.Cpu().MilliValue()
|
||||||
memory := item.Status.Allocatable.Memory().Value()
|
memory := item.Status.Allocatable.Memory().Value() // bytes
|
||||||
|
|
||||||
nodesLimits[item.ObjectMeta.Name] = &portainer.K8sNodeLimits{
|
nodesLimits[item.ObjectMeta.Name] = &portainer.K8sNodeLimits{
|
||||||
CPU: cpu,
|
CPU: cpu,
|
||||||
|
@ -57,7 +57,7 @@ func (client *KubeClient) GetMaxResourceLimits(skipNamespace string, overCommitE
|
||||||
memory := int64(0)
|
memory := int64(0)
|
||||||
for _, node := range nodes.Items {
|
for _, node := range nodes.Items {
|
||||||
limits.CPU += node.Status.Allocatable.Cpu().MilliValue()
|
limits.CPU += node.Status.Allocatable.Cpu().MilliValue()
|
||||||
memory += node.Status.Allocatable.Memory().Value()
|
memory += node.Status.Allocatable.Memory().Value() // bytes
|
||||||
}
|
}
|
||||||
limits.Memory = memory / 1000000 // B to MB
|
limits.Memory = memory / 1000000 // B to MB
|
||||||
|
|
||||||
|
|
|
@ -285,7 +285,7 @@
|
||||||
namespace-has-quota="ctrl.state.resourcePoolHasQuota"
|
namespace-has-quota="ctrl.state.resourcePoolHasQuota"
|
||||||
max-memory-limit="ctrl.state.sliders.memory.max"
|
max-memory-limit="ctrl.state.sliders.memory.max"
|
||||||
max-cpu-limit="ctrl.state.sliders.cpu.max"
|
max-cpu-limit="ctrl.state.sliders.cpu.max"
|
||||||
validation-data="{maxMemoryLimit: ctrl.state.sliders.memory.max, maxCpuLimit: ctrl.state.sliders.cpu.max, isEnvironmentAdmin: ctrl.isAdmin}"
|
validation-data="{maxMemoryLimit: ctrl.state.sliders.memory.max, maxCpuLimit: ctrl.state.sliders.cpu.max, isEnvironmentAdmin: ctrl.isAdmin, nodeLimits: ctrl.nodesLimits.nodesLimits}"
|
||||||
resource-quota-capacity-exceeded="ctrl.resourceQuotaCapacityExceeded()"
|
resource-quota-capacity-exceeded="ctrl.resourceQuotaCapacityExceeded()"
|
||||||
></resource-reservation-form-section>
|
></resource-reservation-form-section>
|
||||||
|
|
||||||
|
|
|
@ -14,6 +14,12 @@ export function deploymentTypeValidation(
|
||||||
.test(
|
.test(
|
||||||
'exhaused',
|
'exhaused',
|
||||||
`This application would exceed available resources. Please review resource reservations or the instance count.`,
|
`This application would exceed available resources. Please review resource reservations or the instance count.`,
|
||||||
() => !validationData?.isQuotaExceeded
|
(value) => {
|
||||||
|
// ignore this validation if the user has selected Replicated, in this case, the isQuotaExceeded will below the instance count input
|
||||||
|
if (value === 'Replicated') {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
return !validationData?.isQuotaExceeded;
|
||||||
|
}
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
|
@ -24,7 +24,13 @@ export function replicationValidation(
|
||||||
.test(
|
.test(
|
||||||
'overflow',
|
'overflow',
|
||||||
'This application would exceed available resources. Please review resource reservations or the instance count.',
|
'This application would exceed available resources. Please review resource reservations or the instance count.',
|
||||||
() => !resourceReservationsOverflow // must not have resource reservations overflow
|
(value) => {
|
||||||
|
// the user can't fix the error here with 1 replica. There are validation errors in the resource reservations section that are helpful in a case of resourceReservationsOverflow.
|
||||||
|
if (value === 1) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
return !resourceReservationsOverflow;
|
||||||
|
}
|
||||||
)
|
)
|
||||||
.test(
|
.test(
|
||||||
'quota',
|
'quota',
|
||||||
|
|
|
@ -1,11 +1,21 @@
|
||||||
import { SchemaOf, number, object } from 'yup';
|
import { SchemaOf, TestContext, number, object } from 'yup';
|
||||||
|
|
||||||
|
import KubernetesResourceReservationHelper from '@/kubernetes/helpers/resourceReservationHelper';
|
||||||
|
|
||||||
import { ResourceQuotaFormValues } from './types';
|
import { ResourceQuotaFormValues } from './types';
|
||||||
|
|
||||||
|
type NodeLimit = {
|
||||||
|
CPU: number;
|
||||||
|
Memory: number;
|
||||||
|
};
|
||||||
|
|
||||||
|
type NodesLimits = Record<string, NodeLimit>;
|
||||||
|
|
||||||
type ValidationData = {
|
type ValidationData = {
|
||||||
maxMemoryLimit: number;
|
maxMemoryLimit: number;
|
||||||
maxCpuLimit: number;
|
maxCpuLimit: number;
|
||||||
isEnvironmentAdmin: boolean;
|
isEnvironmentAdmin: boolean;
|
||||||
|
nodeLimits: NodesLimits;
|
||||||
};
|
};
|
||||||
|
|
||||||
export function resourceReservationValidation(
|
export function resourceReservationValidation(
|
||||||
|
@ -28,6 +38,23 @@ export function resourceReservationValidation(
|
||||||
({ value }) =>
|
({ value }) =>
|
||||||
`Value must be between 0 and ${validationData?.maxMemoryLimit}MB now - the previous value of ${value} exceeds this`
|
`Value must be between 0 and ${validationData?.maxMemoryLimit}MB now - the previous value of ${value} exceeds this`
|
||||||
)
|
)
|
||||||
|
.test(
|
||||||
|
'hasSuitableNode',
|
||||||
|
`These reservations would exceed the resources currently available in the cluster.`,
|
||||||
|
// eslint-disable-next-line prefer-arrow-callback, func-names
|
||||||
|
function (value: number | undefined, context: TestContext) {
|
||||||
|
if (!validationData || value === undefined) {
|
||||||
|
// explicitely check for undefined, since 0 is a valid value
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
const { memoryLimit, cpuLimit } = context.parent;
|
||||||
|
return hasSuitableNode(
|
||||||
|
memoryLimit,
|
||||||
|
cpuLimit,
|
||||||
|
validationData.nodeLimits
|
||||||
|
);
|
||||||
|
}
|
||||||
|
)
|
||||||
.required(),
|
.required(),
|
||||||
cpuLimit: number()
|
cpuLimit: number()
|
||||||
.min(0)
|
.min(0)
|
||||||
|
@ -45,6 +72,41 @@ export function resourceReservationValidation(
|
||||||
({ value }) =>
|
({ value }) =>
|
||||||
`Value must be between 0 and ${validationData?.maxCpuLimit} now - the previous value of ${value} exceeds this`
|
`Value must be between 0 and ${validationData?.maxCpuLimit} now - the previous value of ${value} exceeds this`
|
||||||
)
|
)
|
||||||
|
.test(
|
||||||
|
'hasSuitableNode',
|
||||||
|
`These reservations would exceed the resources currently available in the cluster.`,
|
||||||
|
// eslint-disable-next-line prefer-arrow-callback, func-names
|
||||||
|
function (value: number | undefined, context: TestContext) {
|
||||||
|
if (!validationData || value === undefined) {
|
||||||
|
// explicitely check for undefined, since 0 is a valid value
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
const { memoryLimit, cpuLimit } = context.parent;
|
||||||
|
return hasSuitableNode(
|
||||||
|
memoryLimit,
|
||||||
|
cpuLimit,
|
||||||
|
validationData.nodeLimits
|
||||||
|
);
|
||||||
|
}
|
||||||
|
)
|
||||||
.required(),
|
.required(),
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function hasSuitableNode(
|
||||||
|
memoryLimit: number,
|
||||||
|
cpuLimit: number,
|
||||||
|
nodeLimits: NodesLimits
|
||||||
|
) {
|
||||||
|
// transform the nodelimits from bytes to MB
|
||||||
|
const limits = Object.values(nodeLimits).map((nodeLimit) => ({
|
||||||
|
...nodeLimit,
|
||||||
|
Memory: KubernetesResourceReservationHelper.megaBytesValue(
|
||||||
|
nodeLimit.Memory
|
||||||
|
),
|
||||||
|
}));
|
||||||
|
// make sure there's a node available with enough memory and cpu
|
||||||
|
return limits.some(
|
||||||
|
(nodeLimit) => nodeLimit.Memory >= memoryLimit && nodeLimit.CPU >= cpuLimit
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
Loading…
Reference in New Issue