feat(k8s/application): add placement constraints validation (#4214)

* feat(k8s/application): add constraints validation

* feat(k8s/application): minor UI update

Co-authored-by: Anthony Lapenna <lapenna.anthony@gmail.com>
pull/4217/head
xAt0mZ 2020-08-16 00:11:56 +02:00 committed by GitHub
parent 36bf9c24b9
commit 8d6f6e306a
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
3 changed files with 47 additions and 36 deletions

View File

@ -150,3 +150,10 @@ export class KubernetesApplicationAutoScalerFormValue {
Object.assign(this, JSON.parse(JSON.stringify(_KubernetesApplicationAutoScalerFormValue))); Object.assign(this, JSON.parse(JSON.stringify(_KubernetesApplicationAutoScalerFormValue)));
} }
} }
export function KubernetesApplicationFormValidationDuplicate() {
return {
refs: {},
hasDuplicates: false,
};
}

View File

@ -944,17 +944,17 @@
<div class="col-sm-12 small text-muted" ng-if="ctrl.formValues.Placements.length > 0" style="margin-top: 10px;"> <div class="col-sm-12 small text-muted" ng-if="ctrl.formValues.Placements.length > 0" style="margin-top: 10px;">
<i class="fa fa-info-circle blue-icon" aria-hidden="true" style="margin-right: 2px;"></i> <i class="fa fa-info-circle blue-icon" aria-hidden="true" style="margin-right: 2px;"></i>
Deploy this application on nodes that respect <b>ALL</b> of the following placement rules. Deploy this application on nodes that respect <b>ALL</b> of the following placement rules. Placement rules are based on node labels.
</div> </div>
<div class="col-sm-12 form-inline" style="margin-top: 10px;"> <div class="col-sm-12 form-inline" style="margin-top: 10px;">
<div ng-repeat="placement in ctrl.formValues.Placements" style="margin-top: 2px;"> <div ng-repeat-start="placement in ctrl.formValues.Placements" style="margin-top: 2px;">
<div class="col-sm-5 input-group" ng-class="{ striked: placement.NeedsDeletion }"> <div class="col-sm-5 input-group" ng-class="{ striked: placement.NeedsDeletion }">
<select <select
class="form-control" class="form-control"
ng-model="placement.Label" ng-model="placement.Label"
ng-options="label as (label.Key | kubernetesNodeLabelHumanReadbleText) for label in ctrl.nodesLabels" ng-options="label as (label.Key | kubernetesNodeLabelHumanReadbleText) for label in ctrl.nodesLabels"
ng-change="ctrl.onPlacementLabelChange($index)" ng-change="ctrl.onChangePlacementLabel($index)"
ng-disabled="ctrl.isEditAndNotNewPlacement($index)" ng-disabled="ctrl.isEditAndNotNewPlacement($index)"
> >
</select> </select>
@ -978,6 +978,15 @@
</button> </button>
</div> </div>
</div> </div>
<div ng-repeat-end ng-show="ctrl.state.duplicates.placements.refs[$index] !== undefined">
<div class="col-sm-5 input-group">
<div class="small text-warning" style="margin-top: 5px;" ng-if="ctrl.state.duplicates.placements.refs[$index] !== undefined">
<p ng-if="ctrl.state.duplicates.placements.refs[$index] !== undefined">
<i class="fa fa-exclamation-triangle" aria-hidden="true"></i> This label is already defined.
</p>
</div>
</div>
</div>
</div> </div>
</div> </div>
<div ng-if="ctrl.showPlacementPolicySection()"> <div ng-if="ctrl.showPlacementPolicySection()">

View File

@ -20,6 +20,7 @@ import {
KubernetesApplicationPersistedFolderFormValue, KubernetesApplicationPersistedFolderFormValue,
KubernetesApplicationPublishedPortFormValue, KubernetesApplicationPublishedPortFormValue,
KubernetesApplicationPlacementFormValue, KubernetesApplicationPlacementFormValue,
KubernetesApplicationFormValidationDuplicate,
} from 'Kubernetes/models/application/formValues'; } from 'Kubernetes/models/application/formValues';
import KubernetesFormValidationHelper from 'Kubernetes/helpers/formValidationHelper'; import KubernetesFormValidationHelper from 'Kubernetes/helpers/formValidationHelper';
import KubernetesApplicationConverter from 'Kubernetes/converters/application'; import KubernetesApplicationConverter from 'Kubernetes/converters/application';
@ -259,10 +260,12 @@ class KubernetesCreateApplicationController {
placement.Label = label; placement.Label = label;
placement.Value = label.Values[0]; placement.Value = label.Values[0];
this.formValues.Placements.push(placement); this.formValues.Placements.push(placement);
this.onChangePlacement();
} }
restorePlacement(index) { restorePlacement(index) {
this.formValues.Placements[index].NeedsDeletion = false; this.formValues.Placements[index].NeedsDeletion = false;
this.onChangePlacement();
} }
removePlacement(index) { removePlacement(index) {
@ -271,10 +274,25 @@ class KubernetesCreateApplicationController {
} else { } else {
this.formValues.Placements.splice(index, 1); this.formValues.Placements.splice(index, 1);
} }
this.onChangePlacement();
} }
onPlacementLabelChange(index) { // call all validation functions when a placement is added/removed/restored
onChangePlacement() {
this.onChangePlacementLabelValidate();
}
onChangePlacementLabel(index) {
this.formValues.Placements[index].Value = this.formValues.Placements[index].Label.Values[0]; this.formValues.Placements[index].Value = this.formValues.Placements[index].Label.Values[0];
this.onChangePlacementLabelValidate();
}
onChangePlacementLabelValidate() {
const state = this.state.duplicates.placements;
const source = _.map(this.formValues.Placements, (p) => (p.NeedsDeletion ? undefined : p.Label.Key));
const duplicates = KubernetesFormValidationHelper.getDuplicates(source);
state.refs = duplicates;
state.hasDuplicates = Object.keys(duplicates).length > 0;
} }
/* #endregion */ /* #endregion */
@ -816,40 +834,17 @@ class KubernetesCreateApplicationController {
availableSizeUnits: ['MB', 'GB', 'TB'], availableSizeUnits: ['MB', 'GB', 'TB'],
alreadyExists: false, alreadyExists: false,
duplicates: { duplicates: {
environmentVariables: { environmentVariables: new KubernetesApplicationFormValidationDuplicate(),
refs: {}, persistedFolders: new KubernetesApplicationFormValidationDuplicate(),
hasDuplicates: false, configurationPaths: new KubernetesApplicationFormValidationDuplicate(),
}, existingVolumes: new KubernetesApplicationFormValidationDuplicate(),
persistedFolders: {
refs: {},
hasDuplicates: false,
},
configurationPaths: {
refs: {},
hasDuplicates: false,
},
existingVolumes: {
refs: {},
hasDuplicates: false,
},
publishedPorts: { publishedPorts: {
containerPorts: { containerPorts: new KubernetesApplicationFormValidationDuplicate(),
refs: {}, nodePorts: new KubernetesApplicationFormValidationDuplicate(),
hasDuplicates: false, ingressRoutes: new KubernetesApplicationFormValidationDuplicate(),
}, loadBalancerPorts: new KubernetesApplicationFormValidationDuplicate(),
nodePorts: {
refs: {},
hasDuplicates: false,
},
ingressRoutes: {
refs: {},
hasDuplicates: false,
},
loadBalancerPorts: {
refs: {},
hasDuplicates: false,
},
}, },
placements: new KubernetesApplicationFormValidationDuplicate(),
}, },
isEdit: false, isEdit: false,
params: { params: {