mirror of
https://github.com/portainer/portainer.git
synced 2025-07-24 15:59:41 +02:00
Enable the ability to cordon/uncordon/drain nodes (#4723)
* feat(node): Enable the ability to cordon/uncordon/drain nodes * feat(cluster): check if there is a drain operation somewhere * feat(kubernetes): allow to cordon, uncordon, drain nodes * refacto(kubernetes): set a constant for drain label name * fix(node): Relocate the warning message next to the dropdown and change the information message
This commit is contained in:
parent
660bc2dadf
commit
32a9a2e46b
10 changed files with 182 additions and 13 deletions
|
@ -1,6 +1,6 @@
|
|||
import _ from 'lodash-es';
|
||||
|
||||
import { KubernetesNode, KubernetesNodeDetails, KubernetesNodeTaint } from 'Kubernetes/node/models';
|
||||
import { KubernetesNode, KubernetesNodeDetails, KubernetesNodeTaint, KubernetesNodeAvailabilities, KubernetesPortainerNodeDrainLabel } from 'Kubernetes/node/models';
|
||||
import KubernetesResourceReservationHelper from 'Kubernetes/helpers/resourceReservationHelper';
|
||||
import { KubernetesNodeFormValues, KubernetesNodeTaintFormValues, KubernetesNodeLabelFormValues } from 'Kubernetes/node/formValues';
|
||||
import { KubernetesNodeCreatePayload, KubernetesNodeTaintPayload } from 'Kubernetes/node/payload';
|
||||
|
@ -30,6 +30,11 @@ class KubernetesNodeConverter {
|
|||
NetworkUnavailable: networkUnavailable && networkUnavailable.status === 'True',
|
||||
};
|
||||
|
||||
res.Availability = KubernetesNodeAvailabilities.ACTIVE;
|
||||
if (data.spec.unschedulable === true) {
|
||||
res.Availability = _.has(data.metadata.labels, KubernetesPortainerNodeDrainLabel) ? KubernetesNodeAvailabilities.DRAIN : KubernetesNodeAvailabilities.PAUSE;
|
||||
}
|
||||
|
||||
if (ready.status === 'False') {
|
||||
res.Status = 'Unhealthy';
|
||||
} else if (ready.status === 'Unknown' || res.Conditions.MemoryPressure || res.Conditions.PIDPressure || res.Conditions.DiskPressure || res.Conditions.NetworkUnavailable) {
|
||||
|
@ -67,6 +72,8 @@ class KubernetesNodeConverter {
|
|||
static nodeToFormValues(node) {
|
||||
const res = new KubernetesNodeFormValues();
|
||||
|
||||
res.Availability = node.Availability;
|
||||
|
||||
res.Taints = _.map(node.Taints, (taint) => {
|
||||
const res = new KubernetesNodeTaintFormValues();
|
||||
res.Key = taint.Key;
|
||||
|
@ -92,6 +99,8 @@ class KubernetesNodeConverter {
|
|||
static formValuesToNode(node, formValues) {
|
||||
const res = angular.copy(node);
|
||||
|
||||
res.Availability = formValues.Availability;
|
||||
|
||||
const filteredTaints = _.filter(formValues.Taints, (taint) => !taint.NeedsDeletion);
|
||||
res.Taints = _.map(filteredTaints, (item) => {
|
||||
const taint = new KubernetesNodeTaint();
|
||||
|
@ -130,6 +139,15 @@ class KubernetesNodeConverter {
|
|||
|
||||
payload.metadata.labels = node.Labels;
|
||||
|
||||
if (node.Availability !== KubernetesNodeAvailabilities.ACTIVE) {
|
||||
payload.spec.unschedulable = true;
|
||||
if (node.Availability === KubernetesNodeAvailabilities.DRAIN) {
|
||||
payload.metadata.labels[KubernetesPortainerNodeDrainLabel] = '';
|
||||
} else {
|
||||
delete payload.metadata.labels[KubernetesPortainerNodeDrainLabel];
|
||||
}
|
||||
}
|
||||
|
||||
return payload;
|
||||
}
|
||||
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
const _KubernetesNodeFormValues = Object.freeze({
|
||||
Taints: [],
|
||||
Labels: [],
|
||||
Availability: '',
|
||||
});
|
||||
|
||||
export class KubernetesNodeFormValues {
|
||||
|
|
|
@ -1,3 +1,5 @@
|
|||
export const KubernetesPortainerNodeDrainLabel = 'io.portainer/node-status-drain';
|
||||
|
||||
/**
|
||||
* KubernetesNode Model
|
||||
*/
|
||||
|
@ -14,6 +16,7 @@ const _KubernetesNode = Object.freeze({
|
|||
Api: false,
|
||||
Taints: [],
|
||||
Port: 0,
|
||||
Availability: '',
|
||||
});
|
||||
|
||||
export class KubernetesNode {
|
||||
|
@ -58,6 +61,12 @@ export class KubernetesNodeTaint {
|
|||
}
|
||||
}
|
||||
|
||||
export const KubernetesNodeAvailabilities = Object.freeze({
|
||||
ACTIVE: 'Active',
|
||||
PAUSE: 'Pause',
|
||||
DRAIN: 'Drain',
|
||||
});
|
||||
|
||||
export const KubernetesNodeTaintEffects = Object.freeze({
|
||||
NOSCHEDULE: 'NoSchedule',
|
||||
PREFERNOSCHEDULE: 'PreferNoSchedule',
|
||||
|
|
|
@ -57,7 +57,8 @@ class KubernetesNodeService {
|
|||
const newNode = KubernetesNodeConverter.formValuesToNode(node, nodeFormValues);
|
||||
const payload = KubernetesNodeConverter.patchPayload(node, newNode);
|
||||
const data = await this.KubernetesNodes().patch(params, payload).$promise;
|
||||
return data;
|
||||
const patchedNode = KubernetesNodeConverter.apiToNodeDetails(data);
|
||||
return patchedNode;
|
||||
} catch (err) {
|
||||
throw { msg: 'Unable to patch node', err: err };
|
||||
}
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue