ui/lib/shared/addon/components/node-driver/driver-exoscale/component.js

279 lines
8.7 KiB
JavaScript

import { alias, equal } from '@ember/object/computed';
import { get, set } from '@ember/object';
import Component from '@ember/component';
import NodeDriver from 'shared/mixins/node-driver';
import { ajaxPromise } from 'ember-api-store/utils/ajax-promise';
import layout from './template';
let RANCHER_TEMPLATE = 'Linux Ubuntu 14.04 LTS 64-bit';
let RANCHER_GROUP = 'rancher-machine';
let RANCHER_INGRESS_RULES = [
{
startport: 22,
endport: 22,
cidrlist: '0.0.0.0/0',
protocol: 'TCP'
},
{
icmptype: 8,
icmpcode: 0,
cidrlist: '0.0.0.0/0',
protocol: 'ICMP'
}
];
export default Component.extend(NodeDriver, {
layout,
driverName: 'exoscale',
model: null,
config: alias('model.exoscaleConfig'),
step: 1,
allDiskSizes : null,
allInstanceProfiles : null,
allSecurityGroups : null,
selectedSecurityGroup : null,
defaultSecurityGroup : null,
defaultSecurityGroupName : RANCHER_GROUP,
whichSecurityGroup : 'default',
isCustomSecurityGroup : equal('whichSecurityGroup','custom'),
exoscaleApi : 'api.exoscale.ch/compute',
bootstrap() {
const config = get(this,'globalStore').createRecord({
type: 'exoscaleConfig',
exoscaleApiKey: '',
exoscaleApiSecretKey: '',
diskSize: 50,
instanceProfile: 'small',
securityGroup: 'rancher-machine'
});
const model = get(this, 'model');
set(model, 'exoscaleConfig', config);
},
init() {
this._super();
const cur = get(this,'config.securityGroup');
if (cur === RANCHER_GROUP) {
this.setProperties({
whichSecurityGroup: 'default',
selectedSecurityGroup: null
});
} else {
this.setProperties({
whichSecurityGroup: 'custom',
selectedSecurityGroup: cur
});
}
},
actions: {
/* Login step */
exoscaleLogin() {
set(this,'errors', null);
set(this,'step', 2);
set(this,'config.exoscaleApiKey', (get(this,'config.exoscaleApiKey')||'').trim());
set(this,'config.exoscaleApiSecretKey', (get(this,'config.exoscaleApiSecretKey')||'').trim());
this.apiRequest('listSecurityGroups').then((res) => {
let groups = [];
let defaultGroup = null;
/* Retrieve the list of security groups. */
(res.listsecuritygroupsresponse.securitygroup || [])
.forEach((group) => {
let obj = {
id : group.id,
name : group.name,
description : group.description,
isDefault : group.name === get(this,'defaultSecurityGroupName')
};
groups.push(obj);
if (obj.isDefault && !defaultGroup) {
defaultGroup = obj;
}
});
/* Move to next step */
set(this,'step', 3);
set(this,'allSecurityGroups', groups);
set(this,'defaultSecurityGroup', defaultGroup);
set(this,'selectedSecurityGroup', get(this,'config.securityGroup') || get(this,'allSecurityGroups.firstObject.id'));
}, (err) => {
let errors = get(this,'errors')||[];
errors.pushObject(this.apiErrorMessage(err,
'listsecuritygroupsresponse',
'While requesting security groups',
'Authentication failure: please check the provided access credentials'
));
set(this,'errors', errors);
set(this,'step', 1);
});
},
/* Security group selection */
selectSecurityGroup() {
set(this,'errors',null);
/* When selecting a custom security group, we don't have to do anything more */
if (get(this,'isCustomSecurityGroup')) {
set(this,'config.securityGroup', get(this,'selectedSecurityGroup'));
this.fetchInstanceSettings();
return;
}
/* Otherwise, do we need to create the default security group? */
set(this,'config.securityGroup', get(this,'defaultSecurityGroupName'));
let group = get(this,'defaultSecurityGroup');
if (group) {
/* Already exists, we assume that it contains the appropriate rules */
set(this,'config.securityGroup', group.name);
this.fetchInstanceSettings();
return;
}
/* We need to create the security group */
set(this,'step', 4);
this.apiRequest('createSecurityGroup', {
name : get(this,'defaultSecurityGroupName'),
description : get(this,'settings.appName') + ' default security group'
}).then((res) => {
return async.eachSeries(RANCHER_INGRESS_RULES, (item, cb) => {
item.securitygroupid = res.createsecuritygroupresponse.securitygroup.id;
this.apiRequest('authorizeSecurityGroupIngress', item)
.then(() => {
return cb();
}, (err) => {
return cb(err);
});
}, (err) => {
if (err) {
let errors = get(this,'errors')||[];
errors.pushObject(this.apiErrorMessage(err,
'authorizesecuritygroupingressresponse',
'While setting default security group',
'Unable to configure the default security group'
));
set(this,'errors', errors);
set(this,'step', 3);
} else {
this.fetchInstanceSettings();
}
});
}, (err) => {
let errors = get(this,'errors')||[];
errors.pushObject(this.apiErrorMessage(err,
'createsecuritygroupresponse',
'While creating default security group',
'Unable to create the default security group'
));
set(this,'errors', errors);
set(this,'step', 3);
});
}
},
fetchInstanceSettings() {
set(this,'step', 5);
/* First, get a list of templates to get available disk sizes */
this.apiRequest('listTemplates', {
templatefilter : 'featured',
name : RANCHER_TEMPLATE
}).then((res) => {
set(this,'allDiskSizes',
res.listtemplatesresponse.template
.map((item) => Math.round(item.size / 1024 / 1024 / 1024))
.sort((a, b) => (a - b)).uniq()
);
/* Also get the instance types */
return this.apiRequest('listServiceOfferings', {
issystem: 'false'
}).then((res) => {
set(this,'allInstanceProfiles',
res.listserviceofferingsresponse.serviceoffering.sort((a, b) => {
if (a.memory < b.memory) {
return -1;
}
if (b.memory < a.memory) {
return 1;
}
return 0;
}).map((item) => {
return { name: item.name, displaytext: item.displaytext }
}));
set(this,'step', 6);
}).catch((err) => {
let errors = get(this,'errors')||[];
errors.pushObject(this.apiErrorMessage(err,
'listserviceofferingsresponse',
'While getting list of instance types',
'Unable to get list of instance types'
));
set(this,'errors', errors);
set(this,'step', 3);
});
}).catch((err) => {
let errors = get(this,'errors')||[];
errors.pushObject(this.apiErrorMessage(err,
'listtemplatesresponse',
'While getting list of available images',
'Unable to get list of available images'
));
set(this,'errors', errors);
set(this,'step', 3);
});
},
apiErrorMessage(err, kind, prefix, def) {
let answer = (err.xhr || {}).responseJSON || {};
let text = (answer[kind] || {}).errortext;
if (text) {
return prefix + ": " + text;
} else {
return def;
}
},
apiRequest(command, params) {
let url = get(this,'app.proxyEndpoint') + '/' + this.exoscaleApi;
params = params || {};
params.command = command;
params.apiKey = get(this,'config.exoscaleApiKey');
params.response = 'json';
return ajaxPromise({url: url,
method: 'POST',
dataType: 'json',
data: params,
headers: {
'Accept': 'application/json',
'X-API-Headers-Restrict': 'Content-Length'
},
beforeSend: (xhr, settings) => {
// Append 'rancher:' to Content-Type
xhr.setRequestHeader('Content-Type',
'rancher:' + settings.contentType);
// Compute the signature
let qs = settings.data.split('&')
.map((q) => q.replace(/\+/g, '%20'))
.map(Function.prototype.call, String.prototype.toLowerCase)
.sort()
.join('&');
settings.data += '&signature=' + encodeURIComponent(AWS.util.crypto.hmac(
get(this,'config.exoscaleApiSecretKey'), qs, 'base64', 'sha1'));
return true;
},
}, true);
},
});