mirror of https://github.com/rancher/ui.git
405 lines
13 KiB
JavaScript
405 lines
13 KiB
JavaScript
import Component from '@ember/component';
|
|
import {
|
|
computed, get, observer, set, setProperties
|
|
} from '@ember/object';
|
|
import { equal } from '@ember/object/computed';
|
|
import { on } from '@ember/object/evented';
|
|
import { next } from '@ember/runloop';
|
|
import { inject as service } from '@ember/service';
|
|
import { isEmpty } from '@ember/utils';
|
|
import { Promise } from 'rsvp';
|
|
import { coerce, minor } from 'semver';
|
|
import { INSTANCE_TYPES } from 'shared/utils/amazon';
|
|
import { DEFAULT_NODE_GROUP_CONFIG } from 'ui/models/cluster';
|
|
import layout from './template';
|
|
|
|
|
|
export default Component.extend({
|
|
globalStore: service(),
|
|
layout,
|
|
classNames: ['row', 'mb-20'],
|
|
|
|
instanceTypes: INSTANCE_TYPES,
|
|
|
|
clusterConfig: null,
|
|
keyPairs: null,
|
|
mode: null,
|
|
model: null,
|
|
nodeGroupsVersionCollection: null,
|
|
originalCluster: null,
|
|
versions: null,
|
|
launchTemplates: null,
|
|
allSelectedTemplateVersions: null,
|
|
|
|
clusterSaving: false,
|
|
clusterSaved: false,
|
|
nameIsEditable: true,
|
|
showNodeUpgradePreventionReason: false,
|
|
upgradeVersion: false,
|
|
refreshResourceInstanceTags: true, // simply used for reinit'ing the resource instance tags key value component rather than add weird logic to recomput to the component
|
|
|
|
|
|
editing: equal('mode', 'edit'),
|
|
|
|
init() {
|
|
this._super(...arguments);
|
|
if (!this.launchTemplates) {
|
|
set(this, 'launchTemplates', []);
|
|
}
|
|
|
|
if (this.editing) {
|
|
if (!isEmpty(this.model.nodegroupName)) {
|
|
set(this, 'nameIsEditable', false);
|
|
}
|
|
}
|
|
},
|
|
|
|
actions: {
|
|
setTags(section) {
|
|
if (this.isDestroyed || this.isDestroying) {
|
|
return;
|
|
}
|
|
|
|
set(this, 'model.tags', section);
|
|
},
|
|
|
|
setLabels(section) {
|
|
if (this.isDestroyed || this.isDestroying) {
|
|
return;
|
|
}
|
|
set(this, 'model.labels', section);
|
|
},
|
|
|
|
setResourceTags(section) {
|
|
if (this.isDestroyed || this.isDestroying) {
|
|
return;
|
|
}
|
|
|
|
set(this, 'model.resourceTags', section);
|
|
},
|
|
},
|
|
|
|
loadTemplateVersionInfo: observer('model.launchTemplate.{id,version}', async function() {
|
|
if (!this.clusterSaving && !this.clusterSaved) {
|
|
const { launchTemplate = {} } = this.model;
|
|
let defaults = { ...DEFAULT_NODE_GROUP_CONFIG };
|
|
|
|
// in this case we dont want the defaults for nodegroup items
|
|
delete defaults.nodegroupName;
|
|
delete defaults.maxSize;
|
|
delete defaults.minSize;
|
|
delete defaults.desiredSize;
|
|
|
|
|
|
if (isEmpty(launchTemplate)) {
|
|
set(this, 'refreshResourceInstanceTags', false);
|
|
|
|
next(() => {
|
|
setProperties(this.model, defaults);
|
|
set(this, 'refreshResourceInstanceTags', true)
|
|
});
|
|
} else if ( !isEmpty(get(launchTemplate, 'id')) ) {
|
|
try {
|
|
const versions = await this.listTemplateVersions();
|
|
const match = versions.findBy('VersionNumber', parseInt(launchTemplate.version, 10)); // newselect doesn't handle numbers as values very well
|
|
const { LaunchTemplateData: launchTemplateData } = match;
|
|
|
|
const overrides = {
|
|
imageId: get(launchTemplateData, 'ImageId') ?? null,
|
|
instanceType: get(launchTemplateData, 'InstanceType') ?? 't3.medium',
|
|
volumeSize: get(launchTemplateData, 'BlockDeviceMappings.Ebs.VolumeSize') ?? 20,
|
|
ec2SshKey: get(launchTemplateData, 'KeyName') ?? null,
|
|
userData: isEmpty(get(launchTemplateData, 'UserData')) ? null : atob(get(launchTemplateData, 'UserData')),
|
|
};
|
|
|
|
|
|
defaults = Object.assign({}, defaults, overrides);
|
|
|
|
if (get(launchTemplateData, 'InstanceMarketOptions.MarketType') && get(launchTemplateData, 'InstanceMarketOptions.MarketType') === 'spot') {
|
|
set(defaults, 'requestSpotInstances', true);
|
|
}
|
|
|
|
if ( !isEmpty(get(launchTemplateData, 'TagSpecifications')) ) {
|
|
const resourceInstanceTags = get(launchTemplateData, 'TagSpecifications').findBy('ResourceType', 'instance');
|
|
|
|
if (!isEmpty(resourceInstanceTags) && !isEmpty(get(resourceInstanceTags, 'Tags'))) {
|
|
set(defaults, 'resourceTags', {});
|
|
|
|
resourceInstanceTags.Tags.forEach((tag) => set(defaults.resourceTags, get(tag, 'Key'), get(tag, 'Value')));
|
|
}
|
|
}
|
|
|
|
// if ( !isEmpty(get(launchTemplateData, 'NetworkInterfaces')) ) {
|
|
// const subnets = [];
|
|
// const interfaces = get(launchTemplateData, 'NetworkInterfaces');
|
|
|
|
// interfaces.forEach((enterface) => subnets.push(get(enterface, 'SubnetId')))
|
|
// }
|
|
|
|
set(this, 'refreshResourceInstanceTags', false);
|
|
|
|
next(() => {
|
|
setProperties(this.model, defaults);
|
|
|
|
set(this, 'allSelectedTemplateVersions', versions);
|
|
set(this, 'refreshResourceInstanceTags', true)
|
|
} );
|
|
} catch (err) { }
|
|
}
|
|
}
|
|
}),
|
|
|
|
selectedLaunchTemplateVersion: computed('model.launchTemplate.version', 'model.launchTemplate.id', {
|
|
get() {
|
|
return get(this, 'model.launchTemplate.version') ? get(this, 'model.launchTemplate.version').toString() : null;
|
|
},
|
|
set(_key, value) {
|
|
set(this, 'model.launchTemplate.version', parseInt(value, 10));
|
|
|
|
return value;
|
|
},
|
|
}),
|
|
|
|
isRancherLaunchTemplate: computed('model.{launchTemplate,nodegroupName}', 'originalCluster.eksStatus.managedLaunchTemplateID', function() {
|
|
const { originalCluster, model } = this;
|
|
const { launchTemplate } = model;
|
|
const eksStatus = get((originalCluster ?? {}), 'eksStatus') || {};
|
|
const { managedLaunchTemplateID = null, managedLaunchTemplateVersions = {} } = eksStatus;
|
|
const matchedManagedVersion = get(managedLaunchTemplateVersions, this.model.nodegroupName);
|
|
|
|
if (isEmpty(launchTemplate) && !isEmpty(managedLaunchTemplateID) && !isEmpty(matchedManagedVersion)) {
|
|
return true;
|
|
}
|
|
|
|
return false;
|
|
}),
|
|
|
|
isUserLaunchTemplate: computed('model.launchTemplate', 'originalCluster.eksStatus.managedLaunchTemplateID', function() {
|
|
const { model } = this;
|
|
const { launchTemplate } = model;
|
|
|
|
if (!isEmpty(launchTemplate)) {
|
|
return true;
|
|
}
|
|
|
|
return false;
|
|
}),
|
|
|
|
isNoLaunchTemplate: computed('isRancherLaunchTemplate', 'isUserLaunchTemplate', function() {
|
|
return !this.isRancherLaunchTemplate && !this.isUserLaunchTemplate;
|
|
}),
|
|
|
|
filteredLaunchTemplates: computed('launchTemplates.[]', function() {
|
|
const { launchTemplates } = this;
|
|
|
|
if (isEmpty(launchTemplates)) {
|
|
return [];
|
|
}
|
|
|
|
return launchTemplates.filter(({ LaunchTemplateName }) => !LaunchTemplateName.includes('rancher-managed-lt') ).map(({
|
|
LaunchTemplateName, LaunchTemplateId, DefaultVersionNumber
|
|
}) => {
|
|
return {
|
|
label: LaunchTemplateName,
|
|
value: {
|
|
id: LaunchTemplateId,
|
|
name: LaunchTemplateName,
|
|
defaultVersion: DefaultVersionNumber,
|
|
},
|
|
};
|
|
}).sortBy('label');
|
|
}),
|
|
|
|
selectedTemplateVersion: computed('model.launchTemplate.{id,version}', 'allSelectedTemplateVersions.[]', function() {
|
|
const { model, allSelectedTemplateVersions } = this;
|
|
const version = get(model, 'launchTemplate.version');
|
|
|
|
if (isEmpty(model.launchTemplate) || isEmpty(version)) {
|
|
return null;
|
|
}
|
|
|
|
const match = (allSelectedTemplateVersions || []).findBy('VersionNumber', parseInt(version, 10));
|
|
|
|
if (match) {
|
|
return match;
|
|
}
|
|
|
|
return null;
|
|
}),
|
|
|
|
selectedLaunchTemplateVersions: computed('model.launchTemplate.{id,name,version}', 'launchTemplates', function() {
|
|
const { model, launchTemplates } = this;
|
|
const { launchTemplate } = model;
|
|
|
|
if (isEmpty(launchTemplate) || isEmpty(get(launchTemplate, 'id'))) {
|
|
return [];
|
|
}
|
|
|
|
const match = launchTemplates.findBy('LaunchTemplateId', launchTemplate.id);
|
|
|
|
if (match) {
|
|
// this lets us create a range of values 1...XX because the launch template only gives us the 1st and latest numbers but we want all for the version select
|
|
// ++ver -> zero based array so we need to +1 that value to match a non-zero based version number system
|
|
return Array.from(Array(match.LatestVersionNumber).keys()).map((ver) => ({ label: `${ ++ver }` }));
|
|
}
|
|
|
|
return [];
|
|
}),
|
|
|
|
selectedLaunchTemplate: computed('model.launchTemplate', 'filteredLaunchTemplates.[]', {
|
|
get() {
|
|
const launchTemplate = get(this, 'model.launchTemplate') ?? false;
|
|
|
|
if (launchTemplate) {
|
|
const out = this.filteredLaunchTemplates.findBy('value.id', launchTemplate.id);
|
|
|
|
return isEmpty(out) ? null : get(out, 'value');
|
|
}
|
|
|
|
return null;
|
|
},
|
|
|
|
set(key, value) {
|
|
const {
|
|
id, name, defaultVersion: version
|
|
} = value ?? {};
|
|
|
|
if (isEmpty(value)) {
|
|
set(this, 'model.launchTemplate', null);
|
|
} else {
|
|
set(this, 'model.launchTemplate', {
|
|
id,
|
|
name,
|
|
version
|
|
});
|
|
}
|
|
|
|
return value;
|
|
}
|
|
}),
|
|
|
|
creating: computed('mode', function() {
|
|
const {
|
|
mode, originalCluster, model: { nodegroupName }
|
|
} = this;
|
|
|
|
if (mode === 'new') {
|
|
return true;
|
|
}
|
|
|
|
const upstreamSpec = get(originalCluster, 'eksStatus.upstreamSpec');
|
|
const nodeGroups = upstreamSpec ? get(upstreamSpec, 'nodeGroups') : [];
|
|
|
|
if (nodegroupName && nodeGroups.length >= 1) {
|
|
if (nodeGroups.findBy('nodegroupName', nodegroupName)) {
|
|
return false;
|
|
}
|
|
}
|
|
|
|
return true;
|
|
}),
|
|
|
|
originalClusterVersion: computed('originalCluster.eksConfig.kubernetesVersion', 'originalCluster.eksStatus.upstreamSpec.kubernetesVersion', function() {
|
|
if (!isEmpty(get(this, 'originalCluster.eksConfig.kubernetesVersion'))) {
|
|
return get(this, 'originalCluster.eksConfig.kubernetesVersion');
|
|
}
|
|
|
|
if (!isEmpty(get(this, 'originalCluster.eksStatus.upstreamSpec.kubernetesVersion'))) {
|
|
return get(this, 'originalCluster.eksStatus.upstreamSpec.kubernetesVersion');
|
|
}
|
|
|
|
return '';
|
|
}),
|
|
|
|
upgradeAvailable: computed('clusterConfig.kubernetesVersion', 'mode', 'model.version', 'originalClusterVersion', 'showNodeUpgradePreventionReason', function() {
|
|
const originalClusterVersion = get(this, 'originalClusterVersion');
|
|
const clusterVersion = get(this, 'clusterConfig.kubernetesVersion');
|
|
const nodeVersion = get(this, 'model.version');
|
|
const mode = get(this, 'mode');
|
|
|
|
const initalClusterMinorVersion = parseInt(minor(coerce(clusterVersion)), 10);
|
|
const initalNodeMinorVersion = parseInt(minor(coerce(nodeVersion)), 10);
|
|
const diff = initalClusterMinorVersion - initalNodeMinorVersion;
|
|
|
|
if (mode === 'edit') {
|
|
// we must upgrade the cluster first
|
|
if (originalClusterVersion !== clusterVersion) {
|
|
set(this, 'showNodeUpgradePreventionReason', true);
|
|
|
|
return false;
|
|
}
|
|
}
|
|
|
|
if (diff === 0 && get(this, 'showNodeUpgradePreventionReason')) {
|
|
set(this, 'showNodeUpgradePreventionReason', false);
|
|
}
|
|
|
|
return diff === 1;
|
|
}),
|
|
|
|
showGPUWarning: computed('model.launchTemplate.{id,version}', 'selectedTemplateVersion', function() {
|
|
const { model, selectedTemplateVersion } = this;
|
|
const ltId = get(model, 'launchTemplate.id');
|
|
const ltVersion = get(model, 'launchTemplate.version');
|
|
const imageId = selectedTemplateVersion ? get(selectedTemplateVersion, 'LaunchTemplateData.ImageId') : undefined;
|
|
|
|
return ltId && ltVersion && selectedTemplateVersion && imageId;
|
|
}),
|
|
|
|
requestedSpotInstances: on('init', observer('model.requestSpotInstances', function() {
|
|
const { model } = this;
|
|
|
|
if (get(model, 'requestSpotInstances')) {
|
|
set(this, 'model.instanceType', null);
|
|
} else if (!get(model, 'requestSpotInstances') && get(model, 'instanceType') === null) {
|
|
set(this, 'model.instanceType', 't3.medium');
|
|
}
|
|
})),
|
|
|
|
clusterVersionDidChange: on('init', observer('clusterConfig.kubernetesVersion', function() {
|
|
const { clusterConfig, editing } = this;
|
|
|
|
if (get(clusterConfig, 'kubernetesVersion') && !editing) {
|
|
set(this, 'model.version', clusterConfig.kubernetesVersion);
|
|
}
|
|
})),
|
|
|
|
shouldUpgradeVersion: on('init', observer('upgradeVersion', function() {
|
|
const { upgradeVersion } = this;
|
|
const clusterVersion = get(this, 'clusterConfig.kubernetesVersion');
|
|
const nodeVersion = get(this, 'model.version');
|
|
|
|
if (upgradeVersion && clusterVersion !== nodeVersion) {
|
|
set(this, 'model.version', clusterVersion);
|
|
}
|
|
})),
|
|
|
|
listTemplateVersions() {
|
|
const { launchTemplate } = this.model;
|
|
const match = this.launchTemplates.findBy('LaunchTemplateId', launchTemplate.id);
|
|
|
|
return new Promise((resolve, reject) => {
|
|
const ec2 = new AWS.EC2(this.authCreds());
|
|
|
|
const maxVersion = match.LatestVersionNumber;
|
|
|
|
ec2.describeLaunchTemplateVersions({
|
|
LaunchTemplateId: launchTemplate.id,
|
|
MaxVersion: maxVersion.toString(),
|
|
MinVersion: '1',
|
|
}, (err, data) => {
|
|
if (err) {
|
|
reject(err);
|
|
}
|
|
|
|
resolve(data.LaunchTemplateVersions);
|
|
});
|
|
})
|
|
},
|
|
|
|
removeNodeGroup() {
|
|
throw new Error('remove node group action is required!');
|
|
},
|
|
|
|
});
|