ui/lib/global-admin/addon/components/cru-cloud-credential/component.js

497 lines
14 KiB
JavaScript

import { inject as service } from '@ember/service';
import Component from '@ember/component';
import ViewNewEdit from 'shared/mixins/view-new-edit';
import layout from './template';
import {
get, set, computed, setProperties, observer
} from '@ember/object';
import { next } from '@ember/runloop';
import { REGIONS } from 'shared/utils/amazon';
import { OCI_REGIONS } from 'shared/utils/oci';
import { Promise } from 'rsvp';
const CRED_CONFIG_CHOICES = [
{
name: 'amazon',
displayName: 'Amazon',
driver: 'amazonec2',
configField: 'amazonec2credentialConfig',
kontainerDriverId: 'amazonelasticcontainerservice'
},
{
name: 'azure',
displayName: 'Azure',
driver: 'azure',
configField: 'azurecredentialConfig',
kontainerDriverId: 'azurekubernetesservice'
},
{
name: 'digitalOcean',
displayName: 'Digital Ocean',
driver: 'digitalocean',
configField: 'digitaloceancredentialConfig',
},
{
name: 'google',
displayName: 'Google',
driver: 'google',
configField: 'googlecredentialConfig',
kontainerDriverId: 'googlekubernetesengine'
},
{
name: 'harvester',
displayName: 'Harvester',
driver: 'harvester',
configField: 'harvestercredentialConfig',
},
{
name: 'linode',
displayName: 'Linode',
driver: 'linode',
configField: 'linodecredentialConfig',
kontainerDriverId: 'linodekubernetesengine'
},
{
name: 'oci',
displayName: 'OCI',
driver: 'oci',
configField: 'ocicredentialConfig',
},
{
name: 'pnap',
displayName: 'phoenixNAP',
driver: 'pnap',
configField: 'pnapcredentialConfig',
},
{
name: 'vmware',
displayName: 'VMware vSphere',
driver: 'vmwarevsphere',
configField: 'vmwarevspherecredentialConfig',
},
]
export default Component.extend(ViewNewEdit, {
globalStore: service(),
digitalOcean: service(),
linode: service(),
oci: service(),
intl: service(),
google: service(),
router: service(),
layout,
nodeConfigTemplateType: null,
cloudCredentialType: null,
model: null,
cancelAdd: null,
doneSavingCloudCredential: null,
disableHeader: false,
validatingKeys: false,
region: null,
sinlgeCloudKeyChoice: null,
regionChoices: REGIONS,
ociRegionChoices: OCI_REGIONS,
mode: 'new',
urlInvalid: false,
urlWarning: null,
urlError: null,
gkeProjectId: null,
clusters: null,
allNodeDrivers: null,
allKontainerDrivers: null,
init() {
this._super(...arguments);
let cloudCredentialType = '';
let model = null;
const driverName = get(this, 'driverName') === 'aks' ? 'azure' : get(this, 'driverName');
if (driverName) {
let match = CRED_CONFIG_CHOICES.findBy('driver', driverName);
cloudCredentialType = get(match, 'name');
model = this.globalStore.createRecord({ type: 'cloudCredential' });
} else {
if (get(this, 'originalModel')) {
let configField = Object.keys(this.originalModel).find((key) => key.toLowerCase().includes('config'));
let configChoice = CRED_CONFIG_CHOICES.findBy('configField', configField);
cloudCredentialType = get(configChoice, 'name');
model = this.originalModel.clone();
} else {
model = this.globalStore.createRecord({ type: 'cloudCredential' });
}
}
this.feetchNodeDriver();
this.feetchKontainerDriver();
if (driverName === 'harvester' || cloudCredentialType === 'harvester') {
this.fetchCluster();
}
setProperties(this, {
cloudCredentialType,
model,
});
if (!get(this, 'originalModel')) {
this.initCloudCredentialConfig();
}
},
actions: {
selectConfig(configType) {
this.cleanupPreviousConfig();
set(this, 'cloudCredentialType', configType);
this.initCloudCredentialConfig();
},
updateKubeconfigYaml(value) {
set(this, 'config.kubeconfigContent', value);
},
},
cloudCredentialTypeChange: observer('cloudCredentialType', function() {
if (get(this, 'cloudCredentialType') === 'harvester') {
this.fetchCluster();
}
}),
changeClusterId: observer('config.clusterId', function() {
const clusterId = get(this, 'config.clusterId');
if (!clusterId) {
return;
}
const currentCluster = (get(this, 'clusters') || []).find( (C) => {
return C.id === get(this, 'config.clusterId')
});
currentCluster.doAction('generateKubeconfig')
.then((obj) => {
set(this, 'config.kubeconfigContent', get(obj, 'config'));
})
.catch((err) => {
this.get('growl').fromError('Error getting kubeconfig file', err);
})
}),
clusterContent: computed('clusters', function() {
const clusterContent = (get(this, 'clusters') || []).map((O) => {
const value = O.id;
const label = O.name;
return {
label,
value
}
})
return clusterContent;
}),
config: computed('cloudCredentialType', 'model.{amazonec2credentialConfig,azurecredentialConfig,digitaloceancredentialConfig,googlecredentialConfig,harvestercredentialConfig,linodecredentialConfig,ocicredentialConfig,pnapcredentialConfig,vmwarevspherecredentialConfig}', function() {
const { model } = this;
const configField = this.getConfigField();
return get(model, configField);
}),
configChoices: computed('driverName', 'allNodeDrivers', 'allKontainerDrivers', function() {
if (get(this, 'driverName')) {
// const { driverName } = this;
const driverName = get(this, 'driverName') === 'aks' ? 'azure' : get(this, 'driverName');
let match = CRED_CONFIG_CHOICES.findBy('driver', driverName);
next(() => {
setProperties(this, {
cloudCredentialType: get(match, 'name'),
singleCloudKeyChoice: get(match, 'displayName'),
});
this.initCloudCredentialConfig();
})
return [match];
} else {
if (get(this, 'allNodeDrivers') && get(this, 'allKontainerDrivers')) {
return (CRED_CONFIG_CHOICES.filter((N) => {
const isActive = (get(this, 'allNodeDrivers') || []).find((O) => O.id === N.driver && O.active === true) || (get(this, 'allKontainerDrivers') || []).find((O) => O.id === N.kontainerDriverId && O.active === true);
return isActive ? true : false;
})).sortBy('displayName');
} else {
return CRED_CONFIG_CHOICES.sortBy('displayName');
}
}
}),
savingLabel: computed('validatingKeys', 'cloudCredentialType', function() {
if (this.validatingKeys) {
switch (this.cloudCredentialType) {
case 'amazon':
case 'digitalOcean':
case 'linode':
case 'azure':
case 'google':
return 'modalAddCloudKey.saving.validating';
case 'oci':
case 'pnap':
case 'vmware':
case 'harvester':
default:
return 'saveCancel.saving';
}
}
return 'saveCancel.saving';
}),
validate() {
if ((this.errors || []).length <= 0) {
set(this, 'errors', []);
}
var ok = this._super(...arguments);
let errors = [];
const { cloudCredentialType } = this;
if (cloudCredentialType === 'amazon') {
if (!get(this, 'region')) {
ok = false;
errors.pushObject(this.intl.t('modalAddCloudKey.errors.region'));
}
}
this.parseAndCollectErrors(errors, true);
return ok;
},
willSave() {
let ok = this._super(...arguments);
if (!ok) {
return ok;
}
const { cloudCredentialType } = this;
const keysThatWeCanValidate = ['amazon', 'digitalOcean', 'linode', 'oci', 'google'];
const auth = {
type: 'validate',
token: null,
};
if (keysThatWeCanValidate.includes(cloudCredentialType)) {
set(this, 'validatingKeys', true);
if (cloudCredentialType === 'linode') {
set(auth, 'token', get(this, 'config.token'));
return this.linode.request(auth, 'profile').then(() => {
set(this, 'validatingKeys', false);
return true;
}).catch((err) => {
return this.setError(`${ err.status } ${ err.statusText }`);
});
}
if (cloudCredentialType === 'digitalOcean') {
set(auth, 'token', get(this, 'config.accessToken'));
return this.digitalOcean.request(auth, 'regions').then(() => {
set(this, 'validatingKeys', false);
return true;
}).catch((err) => {
return this.setError(`${ err.status } ${ err.statusText }`);
});
}
if (cloudCredentialType === 'amazon') {
let authConfig = {
accessKeyId: this.config.accessKey,
secretAccessKey: this.config.secretKey,
region: this.region,
};
let ec2 = new AWS.EC2(authConfig);
return new Promise((resolve, reject) => {
ec2.describeAccountAttributes({}, (err) => {
if ( err ) {
reject(err);
}
return resolve();
})
}).then(() => {
set(this, 'validatingKeys', false);
return true;
}).catch((err) => {
return this.setError(`${ err.statusCode } ${ err.code }`);
});
}
if (cloudCredentialType === 'oci') {
let authConfig = {
region: this.config.region,
tenancyOCID: this.config.tenancyId,
userOCID: this.config.userId,
fingerprint: this.config.fingerprint,
privateKey: this.config.privateKeyContents,
privateKeyPassphrase: this.config.privateKeyPassphrase,
token: get(this, 'config.token'),
};
return this.oci.request(authConfig, 'availabilityDomains').then(() => {
set(this, 'validatingKeys', false);
return true;
}).catch((err) => {
return this.setError(`${ err.message }`);
});
}
if (cloudCredentialType === 'google') {
return this.fetchZones().then(() => {
const auth = JSON.parse(get(this, 'config.authEncodedJson'));
const projectId = auth?.project_id;
set(this, 'gkeProjectId', projectId);
set(this, 'validatingKeys', false);
return true;
}).catch((err) => {
return this.setError(`${ err.message }`);
});
}
}
set(this, 'validatingKeys', false);
return ok;
},
setError(message = '') {
const translation = this.intl.t('modalAddCloudKey.errors.validation', { status: message });
set(this, 'validatingKeys', false);
this.parseAndCollectErrors(translation, true);
return false;
},
initCloudCredentialConfig() {
const { model } = this;
const configField = this.getConfigField();
if (configField) {
set(model, configField, this.globalStore.createRecord({ type: configField.toLowerCase() }));
}
},
doneSaving(neu) {
const driverName = get(this, 'driverName');
const projectId = get(this, 'gkeProjectId');
if (driverName === 'google' && projectId) {
set(neu, this.getConfigField(), { projectId });
set(this, 'gkeProjectId', null)
} else {
// API sends back empty object which doesn't overrite the keys when the response is merged.
// Just need to ensure that when the user loads this model again the acceess key/secret/pw is not present.
set(neu, this.getConfigField(), {});
}
this.model.replaceWith(neu);
this.doneSavingCloudCredential(neu);
},
cleanupPreviousConfig() {
const { model } = this;
const configField = this.getConfigField();
if (configField) {
delete model[configField];
}
},
getConfigField() {
const { cloudCredentialType, configChoices } = this;
if (cloudCredentialType) {
const matchType = configChoices.findBy('name', cloudCredentialType);
return get(matchType, 'configField');
}
return;
},
parseNodeTemplateConfigType(nodeTemplate) {
return Object.keys(nodeTemplate).find((f) => f.toLowerCase().indexOf('config') > -1);
},
parseAndCollectErrors() {
throw new Error('parseAndCollectErrors action is required!');
},
fetchCluster() {
get(this, 'globalStore').findAll('cluster').then( (data) => {
const harvesterCluster = (data || []).filter((C) => {
return C.provider === 'harvester';
})
set(this, 'clusters', harvesterCluster)
});
},
feetchNodeDriver() {
get(this, 'globalStore').findAll('nodeDriver').then((allNodeDrivers) => {
set(this, 'allNodeDrivers', allNodeDrivers);
});
},
feetchKontainerDriver() {
get(this, 'globalStore').findAll('kontainerDriver').then((allKontainerDrivers) => {
set(this, 'allKontainerDrivers', allKontainerDrivers);
});
},
fetchZones() {
let credentials = null;
let config = null;
let projectId = null;
try {
credentials = get(this, 'config.authEncodedJson');
config = JSON.parse(credentials || '{}');
projectId = get(config, 'project_id');
} catch (error) {
return Promise.reject({ message: 'Invalid JSON' });
}
return get(this, 'globalStore').rawRequest({
url: '/meta/gkeZones',
method: 'POST',
data: {
credentials,
projectId,
}
}).then(() => {
return Promise.resolve();
}).catch((xhr) => {
return Promise.reject({ message: xhr?.body?.error });
});
},
});