mirror of https://github.com/rancher/ui.git
801 lines
24 KiB
JavaScript
801 lines
24 KiB
JavaScript
import { alias } from '@ember/object/computed';
|
|
import {
|
|
get, set, computed, observer, setProperties
|
|
} from '@ember/object';
|
|
import Component from '@ember/component';
|
|
import NodeDriver from 'shared/mixins/node-driver';
|
|
import layout from './template';
|
|
import { inject as service } from '@ember/service';
|
|
import { throttledObserver } from 'ui/utils/debounce';
|
|
import { hash } from 'rsvp';
|
|
|
|
const DRIVER = 'harvester';
|
|
const CONFIG = 'harvesterConfig';
|
|
|
|
const SYSTEM_NAMESPACES = [
|
|
'cattle-dashboards',
|
|
'cattle-global-data',
|
|
'cattle-system',
|
|
'gatekeeper-system',
|
|
'ingress-nginx',
|
|
'kube-node-lease',
|
|
'kube-public',
|
|
'kube-system',
|
|
'linkerd',
|
|
'rio-system',
|
|
'security-scan',
|
|
'tekton-pipelines',
|
|
];
|
|
|
|
const TYPE = {
|
|
AFFINITY: 'affinity',
|
|
ANTI_AFFINITY: 'antiAffinity'
|
|
};
|
|
|
|
const PRIORITY = {
|
|
REQUIRED: 'required',
|
|
PREFERRED: 'preferred'
|
|
};
|
|
|
|
const STORAGE_NETWORK = 'storage-network.settings.harvesterhci.io'
|
|
|
|
export default Component.extend(NodeDriver, {
|
|
growl: service(),
|
|
settings: service(),
|
|
intl: service(),
|
|
|
|
layout,
|
|
driverName: DRIVER,
|
|
model: {},
|
|
|
|
currentCluster: null,
|
|
clusters: [],
|
|
clusterContent: [],
|
|
imageContent: [],
|
|
networkContent: [],
|
|
namespaceContent: [],
|
|
nodes: [],
|
|
namespaces: [],
|
|
nodeSchedulings: [],
|
|
podSchedulings: [],
|
|
networkDataContent: [],
|
|
storageClassContent: [],
|
|
defaultStorageClass: '',
|
|
userDataContent: [],
|
|
controller: null,
|
|
signal: '',
|
|
isImportMode: true,
|
|
loading: false,
|
|
disks: [],
|
|
interfaces: [],
|
|
|
|
config: alias(`model.${ CONFIG }`),
|
|
|
|
init() {
|
|
this._super(...arguments);
|
|
const controller = new AbortController();
|
|
|
|
set(this, 'controller', controller);
|
|
|
|
this.fetchResource();
|
|
|
|
if (!!get(this, 'config.vmAffinity')) {
|
|
this.initSchedulings();
|
|
}
|
|
|
|
this.initDisks()
|
|
|
|
this.initInterfaces()
|
|
},
|
|
|
|
actions: {
|
|
async finishAndSelectCloudCredential(credential) {
|
|
await this.globalStore.findAll('cloudcredential', { forceReload: true });
|
|
set(this, 'model.cloudCredentialId', get(credential, 'id'));
|
|
},
|
|
|
|
updateYaml(type, value) {
|
|
set(this, `config.${ type }`, value);
|
|
},
|
|
|
|
addNodeScheduling() {
|
|
const neu = {
|
|
priority: PRIORITY.REQUIRED,
|
|
nodeSelectorTerms: { matchExpressions: [] },
|
|
};
|
|
|
|
this.get('nodeSchedulings').pushObject(neu);
|
|
},
|
|
|
|
addVolume(type) {
|
|
let neu = {}
|
|
|
|
if (type === 'volume') {
|
|
neu = {
|
|
storageClassName: get(this, 'defaultStorageClass'),
|
|
size: 10,
|
|
bootOrder: 0
|
|
};
|
|
} else if (type === 'image') {
|
|
neu = {
|
|
imageName: '',
|
|
size: 40,
|
|
bootOrder: 0
|
|
};
|
|
}
|
|
|
|
this.get('disks').pushObject(neu);
|
|
},
|
|
|
|
addNetwork() {
|
|
const neu = {
|
|
networkName: '',
|
|
macAddress: ''
|
|
}
|
|
|
|
this.get('interfaces').pushObject(neu);
|
|
},
|
|
|
|
removeNodeScheduling(scheduling) {
|
|
this.get('nodeSchedulings').removeObject(scheduling);
|
|
},
|
|
|
|
removeDisk(disk) {
|
|
this.get('disks').removeObject(disk);
|
|
},
|
|
|
|
removeNetwork(network) {
|
|
this.get('interfaces').removeObject(network);
|
|
},
|
|
|
|
updateNodeScheduling() {
|
|
this.parseNodeScheduling();
|
|
},
|
|
|
|
addPodScheduling() {
|
|
const neu = {
|
|
type: TYPE.AFFINITY,
|
|
priority: PRIORITY.REQUIRED,
|
|
labelSelector: { matchExpressions: [] },
|
|
topologyKey: ''
|
|
};
|
|
|
|
this.get('podSchedulings').pushObject(neu);
|
|
},
|
|
|
|
removePodScheduling(scheduling) {
|
|
this.get('podSchedulings').removeObject(scheduling);
|
|
},
|
|
|
|
updatePodScheduling() {
|
|
this.parsePodScheduling();
|
|
},
|
|
},
|
|
|
|
clearData: observer('currentCredential.id', function() {
|
|
set(this, 'config.imageName', '');
|
|
set(this, 'config.networkName', '');
|
|
set(this, 'config.vmNamespace', '');
|
|
set(this, 'nodeSchedulings', []);
|
|
set(this, 'podSchedulings', []);
|
|
set(this, 'vmAffinity', {});
|
|
set(this, 'config.vmAffinity', '');
|
|
set(this, 'config.diskInfo', '');
|
|
set(this, 'config.networkInfo', '');
|
|
|
|
this.initDisks()
|
|
|
|
this.initInterfaces()
|
|
}),
|
|
|
|
setDiskInfo: observer('disks.@each.{imageName,bootOrder,storageClassName,size}', function() {
|
|
const diskInfo = {
|
|
disks: get(this, 'disks').map((disk) => {
|
|
return {
|
|
...disk,
|
|
size: Number(disk.size),
|
|
};
|
|
})
|
|
};
|
|
|
|
set(this, 'config.diskInfo', JSON.stringify(diskInfo));
|
|
}),
|
|
|
|
setNetworkInfo: observer('interfaces.@each.{networkName,macAddress}', function() {
|
|
const networkInfo = { interfaces: get(this, 'interfaces') };
|
|
|
|
set(this, 'config.networkInfo', JSON.stringify(networkInfo))
|
|
}),
|
|
|
|
nodeSchedulingsChanged: observer('nodeSchedulings.[]', function() {
|
|
this.parseNodeScheduling();
|
|
}),
|
|
|
|
podSchedulingsChanged: observer('podSchedulings.[]', function() {
|
|
this.parsePodScheduling();
|
|
}),
|
|
|
|
fetchResource: throttledObserver('currentCredential.id', 'currentCredential.harvestercredentialConfig.clusterId', async function() {
|
|
const clusterId = get(this, 'currentCredential') && get(this, 'currentCredential').harvestercredentialConfig && get(this, 'currentCredential').harvestercredentialConfig.clusterId;
|
|
|
|
const url = clusterId === 'local' ? '' : `/k8s/clusters/${ clusterId }`;
|
|
|
|
if (!clusterId) {
|
|
return;
|
|
}
|
|
|
|
let controller = get(this, 'controller');
|
|
let signal = get(this, 'signal');
|
|
|
|
signal = controller.signal;
|
|
set(this, 'signal', signal);
|
|
|
|
set(this, 'loading', true);
|
|
|
|
hash({ nodes: get(this, 'globalStore').rawRequest({ url: `${ url }/v1/node` }) }).then((resp) => {
|
|
set(this, 'nodes', resp.nodes.body.data || []);
|
|
}).catch((err) => {
|
|
const message = err.statusText || err.message;
|
|
|
|
set(this, 'nodes', []);
|
|
get(this, 'growl').fromError('Error request Node API', message);
|
|
})
|
|
|
|
hash({
|
|
images: get(this, 'globalStore').rawRequest({ url: `${ url }/v1/harvesterhci.io.virtualmachineimages` }),
|
|
networks: get(this, 'globalStore').rawRequest({ url: `${ url }/v1/k8s.cni.cncf.io.networkattachmentdefinition` }),
|
|
namespaces: get(this, 'globalStore').rawRequest({ url: `${ url }/v1/namespace` }),
|
|
configmaps: get(this, 'globalStore').rawRequest({ url: `${ url }/v1/configmap` }),
|
|
storageClass: get(this, 'globalStore').rawRequest({ url: `${ url }/v1/storage.k8s.io.storageclasses` }),
|
|
systemNamespace: get(this, 'globalStore').rawRequest({ url: `${ url }/v1/management.cattle.io.settings/system-namespaces` }),
|
|
}).then((resp) => {
|
|
const images = resp.images.body.data || [];
|
|
const imageContent = images.filter((O) => {
|
|
return !O.spec.url.endsWith('.iso') && this.isReady.call(O);
|
|
}).map((O) => {
|
|
const value = O.id;
|
|
const label = `${ O.spec.displayName } (${ value })`;
|
|
|
|
return {
|
|
label,
|
|
value
|
|
}
|
|
});
|
|
|
|
const networks = resp.networks.body.data || [];
|
|
const networkContent = networks.filter((O) => {
|
|
return O.metadata?.annotations?.[STORAGE_NETWORK] !== 'true'
|
|
}).map((O) => {
|
|
let id = '';
|
|
|
|
try {
|
|
const config = JSON.parse(O.spec.config);
|
|
|
|
id = config.vlan;
|
|
} catch (err) {
|
|
get(this, 'growl').fromError('Error parse network config', err);
|
|
}
|
|
|
|
const value = O.id;
|
|
const label = `${ value } (vlanId=${ id })`;
|
|
|
|
return {
|
|
label,
|
|
value
|
|
}
|
|
});
|
|
|
|
const systemNamespaceValue = resp.systemNamespace.body.value || '';
|
|
const systemNamespaces = systemNamespaceValue.split(',');
|
|
|
|
const namespaces = resp.namespaces.body.data || [];
|
|
const namespaceContent = namespaces
|
|
.filter((O) => {
|
|
return !this.isSystemNamespace(O) && O.links.update && !systemNamespaces.includes(O.metadata.name);
|
|
})
|
|
.map((O) => {
|
|
const value = O.id;
|
|
const label = O.id;
|
|
|
|
return {
|
|
label,
|
|
value
|
|
}
|
|
});
|
|
|
|
const configmaps = resp.configmaps.body.data || [];
|
|
const networkDataContent = [];
|
|
const userDataContent = [];
|
|
|
|
configmaps.map((O) => {
|
|
const cloudTemplate = O.metadata && O.metadata.labels && O.metadata.labels['harvesterhci.io/cloud-init-template'];
|
|
const value = O.data && O.data.cloudInit;
|
|
const label = O.metadata.name;
|
|
|
|
if (cloudTemplate === 'user') {
|
|
userDataContent.push({
|
|
label,
|
|
value
|
|
})
|
|
} else if (cloudTemplate === 'network') {
|
|
networkDataContent.push({
|
|
label,
|
|
value
|
|
})
|
|
}
|
|
})
|
|
|
|
const storageClass = resp.storageClass.body.data || [];
|
|
let defaultStorageClass = '';
|
|
const storageClassContent = storageClass.filter((s) => !s.parameters?.backingImage).map((s) => {
|
|
const isDefault = s.metadata?.annotations?.['storageclass.kubernetes.io/is-default-class'] === 'true';
|
|
const label = isDefault ? `${ s.metadata.name } (${ this.intl.t('generic.default') })` : s.metadata.name;
|
|
|
|
if (isDefault) {
|
|
defaultStorageClass = s.metadata.name;
|
|
}
|
|
|
|
return {
|
|
label,
|
|
value: s.metadata.name,
|
|
};
|
|
}) || [];
|
|
|
|
setProperties(this, {
|
|
imageContent,
|
|
networkContent,
|
|
namespaceContent,
|
|
userDataContent,
|
|
networkDataContent,
|
|
storageClassContent,
|
|
defaultStorageClass
|
|
});
|
|
}).catch((err) => {
|
|
setProperties(this, {
|
|
imageContent: [],
|
|
networkContent: [],
|
|
namespaceContent: [],
|
|
userDataContent: [],
|
|
networkDataContent: [],
|
|
namespaces: [],
|
|
vmAffinity: [],
|
|
nodeSchedulings: [],
|
|
podSchedulings: [],
|
|
storageClassContent: [],
|
|
})
|
|
|
|
const message = err.statusText || err.message;
|
|
|
|
get(this, 'growl').fromError('Error request Image API', message);
|
|
}).finally(() => {
|
|
set(this, 'loading', false);
|
|
})
|
|
|
|
controller.abort()
|
|
}),
|
|
|
|
harvestercredentialConfig: computed('cloudCredentials.@each.harvestercredentialConfig', function() {
|
|
return (get(this, 'cloudCredentials') || []).mapBy('harvestercredentialConfig')
|
|
}),
|
|
|
|
currentCredential: computed('cloudCredentials', 'harvestercredentialConfig.[]', 'model.cloudCredentialId', function() {
|
|
return (get(this, 'cloudCredentials') || []).find((C) => C.id === get(this, 'model.cloudCredentialId'));
|
|
}),
|
|
|
|
isSystemNamespace(namespace) {
|
|
if ( namespace.metadata && namespace.metadata.annotations && namespace.metadata.annotations['management.cattle.io/system-namespace'] === 'true' ) {
|
|
return true;
|
|
}
|
|
|
|
if (namespace.metadata.labels['fleet.cattle.io/managed'] === 'true') {
|
|
return true;
|
|
}
|
|
|
|
if ( SYSTEM_NAMESPACES.includes(namespace.metadata.name) ) {
|
|
return true;
|
|
}
|
|
|
|
if ( namespace.metadata && namespace.metadata.name && namespace.metadata.name.endsWith('-system') ) {
|
|
return true;
|
|
}
|
|
|
|
return false;
|
|
},
|
|
|
|
bootstrap() {
|
|
let config = get(this, 'globalStore').createRecord({
|
|
type: CONFIG,
|
|
cpuCount: 2,
|
|
memorySize: 4,
|
|
diskSize: 40,
|
|
diskBus: 'virtio',
|
|
imageName: '',
|
|
sshUser: '',
|
|
networkName: '',
|
|
networkData: '',
|
|
vmNamespace: '',
|
|
userData: '',
|
|
vmAffinity: '',
|
|
diskInfo: '',
|
|
networkInfo: ''
|
|
});
|
|
|
|
set(this, `model.${ CONFIG }`, config);
|
|
},
|
|
|
|
validate() {
|
|
this._super();
|
|
let errors = get(this, 'errors') || [];
|
|
|
|
if (!this.validateCloudCredentials()) {
|
|
errors.push(this.intl.t('nodeDriver.cloudCredentialError'))
|
|
}
|
|
|
|
if (!get(this, 'config.vmNamespace')) {
|
|
errors.push(this.intl.t('generic.required', { key: this.intl.t('nodeDriver.harvester.namespace.label') }));
|
|
}
|
|
|
|
if (!get(this, 'config.sshUser')) {
|
|
errors.push(this.intl.t('generic.required', { key: this.intl.t('nodeDriver.harvester.sshUser.label') }));
|
|
}
|
|
|
|
this.validateScheduling(errors);
|
|
|
|
this.validateDiskAndNetwork(errors);
|
|
|
|
// Set the array of errors for display,
|
|
// and return true if saving should continue.
|
|
|
|
if (errors.length) {
|
|
set(this, 'errors', errors.uniq());
|
|
|
|
return false;
|
|
}
|
|
|
|
return true;
|
|
},
|
|
|
|
isReady() {
|
|
function getStatusConditionOfType(type, defaultValue = []) {
|
|
const conditions = Array.isArray(get(this, 'status.conditions')) ? this.status.conditions : defaultValue;
|
|
|
|
return conditions.find( (cond) => cond.type === type);
|
|
}
|
|
|
|
const initialized = getStatusConditionOfType.call(this, 'Initialized');
|
|
const imported = getStatusConditionOfType.call(this, 'Imported');
|
|
const isCompleted = this.status?.progress === 100;
|
|
|
|
if ([initialized?.status, imported?.status].includes('False')) {
|
|
return false;
|
|
} else {
|
|
return isCompleted && true;
|
|
}
|
|
},
|
|
|
|
isEmptyObject(obj) {
|
|
return obj
|
|
&& Object.keys(obj).length === 0
|
|
&& Object.getPrototypeOf(obj) === Object.prototype;
|
|
},
|
|
|
|
isImageVolume(volume) {
|
|
return Object.prototype.hasOwnProperty.call(volume, 'imageName');
|
|
},
|
|
|
|
initSchedulings() {
|
|
const nodeSchedulings = [];
|
|
const podSchedulings = [];
|
|
const parsedObj = JSON.parse(AWS.util.base64.decode(get(this, 'config.vmAffinity')).toString());
|
|
const nodeAffinityRequired = get(parsedObj, 'nodeAffinity.requiredDuringSchedulingIgnoredDuringExecution');
|
|
const nodeAffinityPreferred = get(parsedObj, 'nodeAffinity.preferredDuringSchedulingIgnoredDuringExecution');
|
|
const podAffinityRequired = get(parsedObj, 'podAffinity.requiredDuringSchedulingIgnoredDuringExecution');
|
|
const podAffinityPreferred = get(parsedObj, 'podAffinity.preferredDuringSchedulingIgnoredDuringExecution');
|
|
const podAntiAffinityRequired = get(parsedObj, 'podAntiAffinity.requiredDuringSchedulingIgnoredDuringExecution');
|
|
const podAntiAffinityPreferred = get(parsedObj, 'podAntiAffinity.preferredDuringSchedulingIgnoredDuringExecution');
|
|
|
|
if (nodeAffinityRequired) {
|
|
nodeAffinityRequired.nodeSelectorTerms.forEach((S) => {
|
|
nodeSchedulings.push({
|
|
priority: PRIORITY.REQUIRED,
|
|
nodeSelectorTerms: { matchExpressions: S.matchExpressions },
|
|
})
|
|
});
|
|
}
|
|
|
|
if (nodeAffinityPreferred) {
|
|
nodeAffinityPreferred.forEach((S) => {
|
|
nodeSchedulings.push({
|
|
priority: PRIORITY.PREFERRED,
|
|
nodeSelectorTerms: { matchExpressions: S.preference.matchExpressions },
|
|
})
|
|
});
|
|
}
|
|
|
|
if (podAffinityRequired) {
|
|
podAffinityRequired.forEach((S) => {
|
|
podSchedulings.push({
|
|
type: TYPE.AFFINITY,
|
|
priority: PRIORITY.REQUIRED,
|
|
labelSelector: { matchExpressions: S.labelSelector.matchExpressions },
|
|
topologyKey: S.topologyKey,
|
|
namespaces: S.namespaces || [],
|
|
weight: S.weight || ''
|
|
})
|
|
});
|
|
}
|
|
|
|
if (podAffinityPreferred) {
|
|
podAffinityPreferred.forEach((S) => {
|
|
podSchedulings.push({
|
|
type: TYPE.AFFINITY,
|
|
priority: PRIORITY.PREFERRED,
|
|
labelSelector: { matchExpressions: S.podAffinityTerm.labelSelector.matchExpressions },
|
|
topologyKey: S.podAffinityTerm.topologyKey,
|
|
namespaces: get(S, 'podAffinityTerm.namespaces') || [],
|
|
weight: get(S, 'podAffinityTerm.weight') || ''
|
|
})
|
|
});
|
|
}
|
|
|
|
if (podAntiAffinityRequired) {
|
|
podAntiAffinityRequired.forEach((S) => {
|
|
podSchedulings.push({
|
|
type: TYPE.ANTI_AFFINITY,
|
|
priority: PRIORITY.REQUIRED,
|
|
labelSelector: { matchExpressions: S.labelSelector.matchExpressions },
|
|
topologyKey: S.topologyKey,
|
|
namespaces: S.namespaces || [],
|
|
weight: S.weight || ''
|
|
})
|
|
});
|
|
}
|
|
|
|
if (podAntiAffinityPreferred) {
|
|
podAntiAffinityPreferred.forEach((S) => {
|
|
podSchedulings.push({
|
|
type: TYPE.ANTI_AFFINITY,
|
|
priority: PRIORITY.PREFERRED,
|
|
labelSelector: { matchExpressions: S.podAffinityTerm.labelSelector.matchExpressions },
|
|
topologyKey: S.podAffinityTerm.topologyKey,
|
|
namespaces: get(S, 'podAffinityTerm.namespaces') || [],
|
|
weight: get(S, 'podAffinityTerm.weight') || ''
|
|
})
|
|
});
|
|
}
|
|
|
|
set(this, 'nodeSchedulings', nodeSchedulings);
|
|
set(this, 'podSchedulings', podSchedulings);
|
|
},
|
|
|
|
initDisks() {
|
|
let disks = [];
|
|
|
|
if (!get(this, 'config.diskInfo')) {
|
|
const imageName = get(this, 'config.imageName') || '';
|
|
|
|
disks = [{
|
|
imageName,
|
|
bootOrder: 1,
|
|
size: 40,
|
|
}];
|
|
|
|
if (get(this, 'config.diskBus')) {
|
|
disks[0].bus = get(this, 'config.diskBus');
|
|
}
|
|
|
|
const diskInfo = JSON.stringify({ disks });
|
|
|
|
set(this, 'config.diskInfo', diskInfo);
|
|
} else {
|
|
const diskInfo = get(this, 'config.diskInfo');
|
|
|
|
disks = JSON.parse(diskInfo).disks || [];
|
|
}
|
|
set(this, 'disks', disks);
|
|
},
|
|
|
|
initInterfaces() {
|
|
let _interfaces = [];
|
|
|
|
if (!get(this, 'config.networkInfo')) {
|
|
const networkName = get(this, 'config.networkName') || '';
|
|
|
|
_interfaces = [{
|
|
networkName,
|
|
macAddress: '',
|
|
}];
|
|
|
|
if (get(this, 'config.networkModel')) {
|
|
_interfaces[0].model = get(this, 'config.networkModel');
|
|
}
|
|
|
|
const networkInfo = JSON.stringify({ interfaces: _interfaces });
|
|
|
|
set(this, 'config.networkInfo', networkInfo);
|
|
} else {
|
|
const networkInfo = get(this, 'config.networkInfo');
|
|
|
|
_interfaces = JSON.parse(networkInfo).interfaces || [];
|
|
}
|
|
set(this, 'interfaces', _interfaces);
|
|
},
|
|
|
|
parseNodeScheduling() {
|
|
const arr = this.nodeSchedulings;
|
|
const out = {};
|
|
|
|
if (arr.find((S) => S.priority === PRIORITY.REQUIRED)) {
|
|
out.requiredDuringSchedulingIgnoredDuringExecution = { nodeSelectorTerms: [] }
|
|
}
|
|
|
|
if (arr.find((S) => S.priority === PRIORITY.PREFERRED)) {
|
|
out.preferredDuringSchedulingIgnoredDuringExecution = [];
|
|
}
|
|
|
|
arr.forEach((S) => {
|
|
if (S.priority === PRIORITY.REQUIRED) {
|
|
out.requiredDuringSchedulingIgnoredDuringExecution.nodeSelectorTerms.push({ matchExpressions: S.nodeSelectorTerms.matchExpressions })
|
|
}
|
|
|
|
if (S.priority === PRIORITY.PREFERRED) {
|
|
out.preferredDuringSchedulingIgnoredDuringExecution.push({ preference: { matchExpressions: S.nodeSelectorTerms.matchExpressions } })
|
|
}
|
|
})
|
|
|
|
const parseObj = { ...get(this, 'vmAffinity') };
|
|
|
|
if (!this.isEmptyObject(out)) {
|
|
set(parseObj, 'nodeAffinity', out);
|
|
} else {
|
|
delete parseObj.nodeAffinity;
|
|
}
|
|
|
|
set(this, 'config.vmAffinity', this.isEmptyObject(parseObj) ? '' : AWS.util.base64.encode(JSON.stringify(parseObj)));
|
|
set(this, 'vmAffinity', parseObj);
|
|
},
|
|
|
|
parsePodScheduling() {
|
|
const arr = this.podSchedulings;
|
|
const out = {};
|
|
|
|
if (arr.find((S) => S.type === TYPE.AFFINITY)) {
|
|
out.podAffinity = {};
|
|
}
|
|
|
|
if (arr.find((S) => S.type === TYPE.ANTI_AFFINITY)) {
|
|
out.podAntiAffinity = {};
|
|
}
|
|
|
|
if (arr.find((S) => S.type === TYPE.AFFINITY && S.priority === PRIORITY.REQUIRED)) {
|
|
out.podAffinity.requiredDuringSchedulingIgnoredDuringExecution = [];
|
|
}
|
|
|
|
if (arr.find((S) => S.type === TYPE.AFFINITY && S.priority === PRIORITY.PREFERRED)) {
|
|
out.podAffinity.preferredDuringSchedulingIgnoredDuringExecution = [];
|
|
}
|
|
|
|
if (arr.find((S) => S.type === TYPE.ANTI_AFFINITY && S.priority === PRIORITY.REQUIRED)) {
|
|
out.podAntiAffinity.requiredDuringSchedulingIgnoredDuringExecution = [];
|
|
}
|
|
|
|
if (arr.find((S) => S.type === TYPE.ANTI_AFFINITY && S.priority === PRIORITY.PREFERRED)) {
|
|
out.podAntiAffinity.preferredDuringSchedulingIgnoredDuringExecution = [];
|
|
}
|
|
|
|
|
|
arr.forEach((S) => {
|
|
const requiredObj = {
|
|
labelSelector: S.labelSelector,
|
|
topologyKey: S.topologyKey,
|
|
};
|
|
|
|
const preferredObj = {
|
|
podAffinityTerm: {
|
|
labelSelector: S.labelSelector,
|
|
topologyKey: S.topologyKey
|
|
}
|
|
}
|
|
|
|
if (S.namespaces) {
|
|
requiredObj.namespaces = S.namespaces;
|
|
preferredObj.podAffinityTerm.namespaces = S.namespaces;
|
|
}
|
|
|
|
if (S.weight) {
|
|
requiredObj.weight = S.weight;
|
|
preferredObj.weight = S.weight;
|
|
}
|
|
|
|
if (S.type === TYPE.AFFINITY && S.priority === PRIORITY.REQUIRED) {
|
|
out.podAffinity.requiredDuringSchedulingIgnoredDuringExecution.push(requiredObj);
|
|
}
|
|
|
|
if (S.type === TYPE.AFFINITY && S.priority === PRIORITY.PREFERRED) {
|
|
out.podAffinity.preferredDuringSchedulingIgnoredDuringExecution.push(preferredObj);
|
|
}
|
|
|
|
if (S.type === TYPE.ANTI_AFFINITY && S.priority === PRIORITY.REQUIRED) {
|
|
out.podAntiAffinity.requiredDuringSchedulingIgnoredDuringExecution.push(requiredObj)
|
|
}
|
|
|
|
if (S.type === TYPE.ANTI_AFFINITY && S.priority === PRIORITY.PREFERRED) {
|
|
out.podAntiAffinity.preferredDuringSchedulingIgnoredDuringExecution.push(preferredObj);
|
|
}
|
|
});
|
|
|
|
const parseObj = { ...get(this, 'vmAffinity') }
|
|
|
|
if (!this.isEmptyObject(get(out, 'podAffinity') || {})) {
|
|
set(parseObj, 'podAffinity', get(out, 'podAffinity'));
|
|
} else {
|
|
delete parseObj.podAffinity;
|
|
}
|
|
|
|
if (!this.isEmptyObject(get(out, 'podAntiAffinity') || {})) {
|
|
set(parseObj, 'podAntiAffinity', get(out, 'podAntiAffinity'));
|
|
} else {
|
|
delete parseObj.podAntiAffinity;
|
|
}
|
|
|
|
set(this, 'config.vmAffinity', this.isEmptyObject(parseObj) ? '' : AWS.util.base64.encode(JSON.stringify(parseObj)));
|
|
set(this, 'vmAffinity', parseObj);
|
|
},
|
|
|
|
validateScheduling(errors) {
|
|
if (get(this, 'podSchedulings').find((S) => !S.topologyKey)) {
|
|
errors.push(this.intl.t('generic.required', { key: this.intl.t('nodeDriver.harvester.scheduling.input.topology.label') }));
|
|
}
|
|
|
|
const nodeHasMissingKey = get(this, 'nodeSchedulings').find((S) => {
|
|
return get(S, 'nodeSelectorTerms.matchExpressions').find((M) => !get(M, 'key'));
|
|
});
|
|
|
|
const podHasMissingKey = get(this, 'podSchedulings').find((S) => {
|
|
return get(S, 'labelSelector.matchExpressions').find((M) => !get(M, 'key'));
|
|
});
|
|
|
|
if (nodeHasMissingKey || podHasMissingKey) {
|
|
errors.push(this.intl.t('generic.required', { key: this.intl.t('formNodeRequirement.key.label') }));
|
|
}
|
|
},
|
|
|
|
isValidMac(value) {
|
|
return /^[A-Fa-f0-9]{2}(-[A-Fa-f0-9]{2}){5}$|^[A-Fa-f0-9]{2}(:[A-Fa-f0-9]{2}){5}$/.test(value);
|
|
},
|
|
|
|
validateDiskAndNetwork(errors) {
|
|
const disks = get(this, 'disks');
|
|
|
|
disks.forEach((disk) => {
|
|
if (Object.prototype.hasOwnProperty.call(disk, 'imageName') && !disk.imageName) {
|
|
errors.push(this.intl.t('generic.required', { key: this.intl.t('nodeDriver.harvester.imageName.label') }));
|
|
}
|
|
|
|
if (Object.prototype.hasOwnProperty.call(disk, 'storageClassName') && !disk.storageClassName) {
|
|
errors.push(this.intl.t('generic.required', { key: this.intl.t('nodeDriver.harvester.storageClass.label') }));
|
|
}
|
|
|
|
if (!disk.size) {
|
|
errors.push(this.intl.t('generic.required', { key: this.intl.t('nodeDriver.harvester.diskSize.label') }));
|
|
}
|
|
|
|
if (!disk.size) {
|
|
errors.push(this.intl.t('generic.required', { key: this.intl.t('nodeDriver.harvester.diskSize.label') }));
|
|
}
|
|
});
|
|
|
|
const interfaces = get(this, 'interfaces');
|
|
|
|
interfaces.forEach((_interface) => {
|
|
if (!_interface.networkName) {
|
|
errors.push(this.intl.t('generic.required', { key: this.intl.t('nodeDriver.harvester.networkName.label') }));
|
|
}
|
|
|
|
if (_interface.macAddress && !this.isValidMac(_interface.macAddress)) {
|
|
errors.push(this.intl.t('generic.required', { key: this.intl.t('nodeDriver.harvester.network.macFormat') }));
|
|
}
|
|
});
|
|
}
|
|
});
|