Merge pull request #2640 from westlywright/cloud-keys

cloud credentials
This commit is contained in:
Westly Wright 2019-02-12 09:08:48 -07:00 committed by GitHub
commit 542b348f75
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
28 changed files with 1143 additions and 302 deletions

View File

@ -1,12 +1,20 @@
import { get } from '@ember/object';
export function initialize(application) { export function initialize(application) {
// Monkey patch AWS SDK to go through our proxy // Monkey patch AWS SDK to go through our proxy
var orig = AWS.XHRClient.prototype.handleRequest; var orig = AWS.XHRClient.prototype.handleRequest;
AWS.XHRClient.prototype.handleRequest = function handleRequest(httpRequest, httpOptions, callback, errCallback) { AWS.XHRClient.prototype.handleRequest = function handleRequest(httpRequest, httpOptions, callback, errCallback) {
httpRequest.headers['X-Api-Headers-Restrict'] = 'Content-Length';
if (get(httpOptions, 'cloudCredentialId')) {
httpRequest.headers['X-Api-CattleAuth-Header'] = `awsv4 credID=${ get(httpOptions, 'cloudCredentialId') }`;
} else {
httpRequest.endpoint.protocol = 'http:'; httpRequest.endpoint.protocol = 'http:';
httpRequest.endpoint.port = 80; httpRequest.endpoint.port = 80;
httpRequest.headers['X-Api-Headers-Restrict'] = 'Content-Length';
httpRequest.headers['X-Api-Auth-Header'] = httpRequest.headers['Authorization']; httpRequest.headers['X-Api-Auth-Header'] = httpRequest.headers['Authorization'];
}
delete httpRequest.headers['Authorization']; delete httpRequest.headers['Authorization'];
httpRequest.headers['Content-Type'] = `rancher:${ httpRequest.headers['Content-Type'] }`; httpRequest.headers['Content-Type'] = `rancher:${ httpRequest.headers['Content-Type'] }`;

View File

@ -353,7 +353,7 @@ const rootNav = [
{ {
id: 'global-security-roles', id: 'global-security-roles',
localizedLabel: 'nav.admin.security.roles', localizedLabel: 'nav.admin.security.roles',
icon: 'icon icon-key', icon: 'icon icon-users',
route: 'global-admin.security.roles', route: 'global-admin.security.roles',
resource: ['roletemplate'], resource: ['roletemplate'],
resourceScope: 'global', resourceScope: 'global',
@ -366,6 +366,14 @@ const rootNav = [
resource: ['podsecuritypolicytemplate'], resource: ['podsecuritypolicytemplate'],
resourceScope: 'global', resourceScope: 'global',
}, },
{
id: 'global-security-cloud-keys',
localizedLabel: 'nav.admin.security.cloudKeys',
icon: 'icon icon-secrets',
route: 'global-admin.security.cloud-keys',
resource: ['cloudcredential'],
resourceScope: 'global',
},
{ {
id: 'global-security-authentication', id: 'global-security-authentication',
localizedLabel: 'nav.admin.security.authentication', localizedLabel: 'nav.admin.security.authentication',

View File

@ -0,0 +1,38 @@
import Resource from '@rancher/ember-api-store/models/resource';
import { computed } from '@ember/object';
import { notEmpty } from '@ember/object/computed';
const cloudCredential = Resource.extend({
type: 'cloudCredential',
canClone: false,
canEdit: false,
isAmazon: notEmpty('amazonec2credentialConfig'),
isAzure: notEmpty('azurecredentialConfig'),
isDo: notEmpty('digitaloceancredentialConfig'),
isVMware: notEmpty('vmwarevspherecredentialConfig'),
displayType: computed('amazonec2credentialConfig', 'azurecredentialConfig', 'digitaloceancredentialConfig', 'vmwarevspherecredentialConfig', function() {
const {
isAmazon,
isAzure,
isDo,
isVMware
} = this;
if (isAmazon) {
return 'Amazon';
} else if (isAzure) {
return 'Azure';
} else if (isDo) {
return 'Digital Ocean';
} else if (isVMware) {
return 'VMware vSphere';
}
}),
});
export default cloudCredential;

View File

@ -0,0 +1,130 @@
import { inject as service } from '@ember/service';
import Component from '@ember/component';
import NewOrEdit from 'shared/mixins/new-or-edit';
import layout from './template';
import { get, set, computed } from '@ember/object';
import { next } from '@ember/runloop';
const CRED_CONFIG_CHOICES = [
{
name: 'amazon',
displayName: 'Amazon',
driver: 'amazonec2',
configField: 'amazonec2credentialConfig',
},
{
name: 'azure',
displayName: 'Azure',
driver: 'azure',
configField: 'azurecredentialConfig',
},
{
name: 'digitalOcean',
displayName: 'Digital Ocean',
driver: 'digitalocean',
configField: 'digitaloceancredentialConfig',
},
{
name: 'vmware',
displayName: 'VMware vSphere',
driver: 'vmwarevsphere',
configField: 'vmwarevspherecredentialConfig',
},
]
export default Component.extend(NewOrEdit, {
globalStore: service(),
layout,
nodeConfigTemplateType: null,
cloudKeyType: null,
model: null,
cancelAdd: null,
doneSavingCloudCredential: null,
disableHeader: false,
didReceiveAttrs() {
set(this, 'model', this.globalStore.createRecord({ type: 'cloudCredential' }));
},
actions: {
selectConfig(configType) {
this.cleanupPreviousConfig();
set(this, 'cloudKeyType', configType);
this.initCloudCredentialConfig();
},
},
config: computed('cloudKeyType', {
get() {
const { model } = this;
const type = this.getConfigField();
return get(model, type);
}
}),
configChoices: computed('driverName', function() {
if (get(this, 'driverName')) {
const { driverName } = this;
let match = CRED_CONFIG_CHOICES.findBy('driver', driverName);
next(() => {
set(this, 'cloudKeyType', get(match, 'name'));
this.initCloudCredentialConfig();
})
return [match];
} else {
return CRED_CONFIG_CHOICES.sortBy('displayName');
}
}),
saveDisabled: computed('config.{amazonec2credentialConfig,azurecredentialConfig,digitaloceancredentialConfig,vmwarevspherecredentialConfig}', 'cloudKeyType', function() {
if (this.getConfigField()) {
return false;
}
return true;
}),
initCloudCredentialConfig() {
const { model } = this;
const type = this.getConfigField();
set(model, type, this.globalStore.createRecord({ type: type.toLowerCase() }));
},
doneSaving(neu) {
this.doneSavingCloudCredential(neu);
},
cleanupPreviousConfig() {
const { model } = this;
const configField = this.getConfigField();
if (configField) {
delete model[configField];
}
},
getConfigField() {
const { cloudKeyType, configChoices } = this;
if (cloudKeyType) {
const matchType = configChoices.findBy('name', cloudKeyType);
return get(matchType, 'configField');
}
return;
},
parseNodeTemplateConfigType(nodeTemplate) {
return Object.keys(nodeTemplate).find((f) => f.toLowerCase().indexOf('config') > -1);
},
});

View File

@ -0,0 +1,191 @@
<section class="horizontal-form container-fluid">
{{#unless disableHeader}}
<h2>
{{t "modalAddCloudKey.header"}}
</h2>
{{/unless}}
<hr/>
{{form-name-description
model=primaryResource
namePlaceholder="newSecret.name.placeholder"
descriptionPlaceholder="newSecret.description.placeholder"
}}
<div class="row">
<div class="col span-6">
<label class="acc-label">
{{t "modalAddCloudKey.type"}}
</label>
<select
class="form-control"
onchange={{action "selectConfig" value="target.value"}}
>
{{#if (not-eq cloudKeyType value)}}
<option value="" selected=true >
{{t "modalAddCloudKey.typeSelect.prompt"}}
</option>
{{/if}}
{{#each configChoices as |choice|}}
<option
value="{{choice.name}}"
selected={{eq choice.name cloudKeyType}}
>
{{choice.displayName}}
</option>
{{/each}}
</select>
</div>
{{#if (eq cloudKeyType "amazon")}}
<div class="row">
<div class="col span-6">
<label class="acc-label" for="amazonec2-accessKey">
{{t "modalAddCloudKey.amazonec2.accessKey.label"}}{{field-required}}
</label>
{{input
type="text"
name="username"
classNames="form-control"
placeholder=(t "modalAddCloudKey.amazonec2.accessKey.placeholder")
value=config.accessKey
id="amazonec2-accessKey"
}}
</div>
<div class="col span-6">
<label class="acc-label" for="amazonec2-secretKey">
{{t "modalAddCloudKey.amazonec2.secretKey.label"}}{{field-required}}
</label>
{{input
type="password"
name="password"
classNames="form-control"
placeholder=(t "modalAddCloudKey.amazonec2.secretKey.placeholder")
value=config.secretKey
id="amazonec2-secretKey"
}}
</div>
</div>
{{else if (eq cloudKeyType "azure")}}
<div class="row">
<div class="col span-6">
<label class="acc-label">
{{t "nodeDriver.azure.subscriptionId.label"}}{{field-required}}
</label>
{{input
type="text"
value=config.subscriptionId
classNames="form-control"
placeholder=(t "nodeDriver.azure.subscriptionId.placeholder")
}}
</div>
</div>
<div class="row">
<div class="col span-6">
<label class="acc-label" for="azure-clientId">
{{t "modalAddCloudKey.azure.clientId.label"}}{{field-required}}
</label>
{{input
type="text"
value=config.clientId
classNames="form-control"
id="azure-clientId"
placeholder=(t "modalAddCloudKey.azure.clientId.placeholder")
}}
</div>
<div class="col span-6">
<label class="acc-label" for="azure-clientSecret">
{{t "modalAddCloudKey.azure.clientSecret.label"}}{{field-required}}
</label>
{{input
type="password"
value=config.clientSecret
classNames="form-control"
id="azure-clientSecret"
placeholder=(t "modalAddCloudKey.azure.clientSecret.placeholder")
}}
</div>
</div>
{{else if (eq cloudKeyType "digitalOcean")}}
<div class="row">
<div class="col span-6">
<label class="acc-label" for="digitalocean-accessToken">
{{t "modalAddCloudKey.digitalocean.accessToken.label"}}
</label>
{{input
type="password"
value=config.accessToken
classNames="form-control"
placeholder=(t "modalAddCloudKey.digitalocean.accessToken.placeholder")
id="digitalocean-accessToken"
}}
<p class="text-info">
{{t "modalAddCloudKey.digitalocean.accessToken.help" htmlSafe=true}}
</p>
</div>
</div>
{{else if (eq cloudKeyType "vmware")}}
<div class="row">
<div class="col span-6">
<label class="acc-label">
{{t "nodeDriver.vmwarevsphere.vcenter.label"}}{{field-required}}
</label>
{{input
type="text"
class="form-control"
value=config.vcenter
placeholder=(t "nodeDriver.vmwarevsphere.vcenter.placeholder")
}}
</div>
<div class="col span-6">
<label class="acc-label">
{{t "nodeDriver.vmwarevsphere.vcenterPort.label"}}{{field-required}}
</label>
{{input-integer
min=1
max=65535
class="form-control"
value=config.vcenterPort
}}
</div>
</div>
<div class="row">
<div class="col span-6">
<label class="acc-label">
{{t "nodeDriver.vmwarevsphere.username.label"}}{{field-required}}
</label>
{{input
type="text"
value=config.username
classNames="form-control"
}}
</div>
<div class="col span-6">
<label class="acc-label">
{{t "nodeDriver.vmwarevsphere.password.label"}}{{field-required}}
</label>
{{input
type="password"
value=config.password
classNames="form-control"
}}
</div>
</div>
<p class="help-block">{{t "nodeDriver.vmwarevsphere.access.help"}}</p>
{{/if}}
</div>
<div class="footer-actions">
{{save-cancel
saveDisabled=saveDisabled
save="save"
cancel=cancelAdd
}}
</div>
</section>

View File

@ -0,0 +1,8 @@
import Component from '@ember/component';
import ModalBase from 'shared/mixins/modal-base';
import layout from './template';
export default Component.extend(ModalBase, {
layout,
classNames: ['large-modal', 'alert'],
});

View File

@ -0,0 +1,4 @@
{{cru-cloud-credential
doneSavingCloudCredential=(action "close")
cancelAdd=(action "cancel")
}}

View File

@ -69,5 +69,9 @@ export default buildRoutes(function() {
this.route('okta'); this.route('okta');
this.route('freeipa'); this.route('freeipa');
}); });
this.route('cloud-keys', function() {
this.route('index', { path: '/' });
});
}); });
}); });

View File

@ -0,0 +1,46 @@
import Controller from '@ember/controller';
import { computed, get } from '@ember/object';
import { inject as service } from '@ember/service';
import layout from './template';
const HEADERS = [
{
name: 'type',
sort: ['displayType'],
searchField: 'displayType',
translationKey: 'generic.type',
type: 'string',
},
{
name: 'name',
sort: ['displayName'],
searchField: 'displayName',
translationKey: 'generic.name',
type: 'string',
},
{
classNames: 'text-right pr-20',
name: 'created',
sort: ['created'],
translationKey: 'generic.created',
},
];
export default Controller.extend({
modal: service(),
layout,
sortBy: 'created',
searchText: '',
headers: HEADERS,
actions: {
addCloudKey() {
this.modal.toggleModal('modal-add-cloud-key');
}
},
filteredContent: computed('model.@each.{id}', function() {
return get(this, 'model').sortBy('id');
}),
});

View File

@ -0,0 +1,11 @@
import Route from '@ember/routing/route';
import { inject as service } from '@ember/service';
// import { get } from '@ember/object';
export default Route.extend({
globalStore: service(),
model(/* params */) {
return this.globalStore.findAll('cloudcredential');
},
});

View File

@ -0,0 +1,59 @@
<section class="header has-tabs clearfix p-0">
<div class="right-buttons">
<button
{{action "addCloudKey"}}
class="btn btn-sm bg-primary right-divider-btn"
disabled={{rbac-prevents resource="cloudcredential" scope="global" permission="create"}}
>
{{t "cloudKeysPage.addKey"}}
</button>
</div>
</section>
<section class="instances">
{{#sortable-table
bulkActions=true
classNames="grid sortable-table"
sortBy=sortBy
descending=descending
headers=headers
searchText=searchText
showHeader=true
body=filteredContent
rightActions=true
as |sortable kind row dt|
}}
{{#if (eq kind "row")}}
<tr class="main-row">
<td valign="middle" class="row-check" style="padding-top: 2px;">
{{check-box nodeId=row.id}}
</td>
<td data-title="{{t "generic.type"}}:" class="clip">
{{row.displayType}}
</td>
<td data-title="{{t "generic.name"}}:" class="clip">
{{row.displayName}}
</td>
<td data-title="{{t "generic.created"}}:" class="text-right pr-20">
{{date-calendar row.created}}
</td>
<td data-title="{{t "generic.actions"}}:" class="actions">
{{action-menu model=row}}
</td>
</tr>
{{else if (eq kind "nomatch")}}
<td
colspan="5"
class="text-center text-muted lacsso pt-20 pb-20"
>
{{t "cloudKeysPage.index.table.noMatch"}}
</td>
{{else if (eq kind "norows")}}
<td
colspan="5"
class="text-center text-muted lacsso pt-20 pb-20"
>
{{t "cloudKeysPage.index.table.noData"}}
</td>
{{/if}}
{{/sortable-table}}
</section>

View File

@ -0,0 +1 @@
export { default } from 'global-admin/components/cru-cloud-credential/component';

View File

@ -0,0 +1 @@
export { default } from 'global-admin/components/modal-add-cloud-key/component';

View File

@ -9,6 +9,7 @@ import Component from '@ember/component';
import NodeDriver from 'shared/mixins/node-driver'; import NodeDriver from 'shared/mixins/node-driver';
import layout from './template'; import layout from './template';
import { INSTANCE_TYPES, nameFromResource, tagsFromResource, REGIONS } from 'shared/utils/amazon'; import { INSTANCE_TYPES, nameFromResource, tagsFromResource, REGIONS } from 'shared/utils/amazon';
import { randomStr } from 'shared/utils/util';
let RANCHER_GROUP = 'rancher-nodes'; let RANCHER_GROUP = 'rancher-nodes';
@ -84,21 +85,28 @@ export default Component.extend(NodeDriver, {
}, },
actions: { actions: {
finishAndSelectCloudCredential(cred) {
if (cred) {
set(this, 'primaryResource.cloudCredentialId', get(cred, 'id'));
this.send('awsLogin');
}
},
awsLogin(cb) { awsLogin(cb) {
let self = this; let self = this;
setProperties(this, { set(this, 'errors', null);
'errors': null,
'config.accessKey': (get(this, 'config.accessKey') || '').trim(),
'config.secretKey': (get(this, 'config.secretKey') || '').trim(),
});
let subnets = []; let subnets = [];
let rName = get(this, 'config.region'); let rName = get(this, 'config.region');
// have to have something in there before we describe the request even though we are going to replace with the actual cred id
let ec2 = new AWS.EC2({ let ec2 = new AWS.EC2({
accessKeyId: get(this, 'config.accessKey'), accessKeyId: randomStr(),
secretAccessKey: get(this, 'config.secretKey'), secretAccessKey: randomStr(),
region: rName, region: rName,
httpOptions: { cloudCredentialId: get(this, 'model.cloudCredentialId') },
}); });
let vpcNames = {}; let vpcNames = {};
@ -110,7 +118,9 @@ export default Component.extend(NodeDriver, {
errors.pushObject(err); errors.pushObject(err);
set(this, 'errors', errors); set(this, 'errors', errors);
if (cb && typeof cb === 'function') {
cb(); cb();
}
return; return;
} }
@ -126,7 +136,9 @@ export default Component.extend(NodeDriver, {
errors.pushObject(err); errors.pushObject(err);
set(this, 'errors', errors); set(this, 'errors', errors);
if (cb && typeof cb === 'function') {
cb(); cb();
}
return; return;
} }
@ -154,7 +166,9 @@ export default Component.extend(NodeDriver, {
'allSubnets': subnets, 'allSubnets': subnets,
'step': 2, 'step': 2,
}); });
if (cb && typeof cb === 'function') {
cb(); cb();
}
}); });
}); });
}, },
@ -164,14 +178,18 @@ export default Component.extend(NodeDriver, {
if ( !get(this, 'selectedZone') ) { if ( !get(this, 'selectedZone') ) {
set(this, 'errors', ['Select an Availability Zone']); set(this, 'errors', ['Select an Availability Zone']);
if (cb && typeof cb === 'function') {
cb(); cb();
}
return; return;
} }
if ( !get(this, 'selectedSubnet') ) { if ( !get(this, 'selectedSubnet') ) {
set(this, 'errors', ['Select a VPC or Subnet']); set(this, 'errors', ['Select a VPC or Subnet']);
if (cb && typeof cb === 'function') {
cb(); cb();
}
return; return;
} }
@ -185,7 +203,9 @@ export default Component.extend(NodeDriver, {
ec2.describeSecurityGroups({ Filters: [filter] }, (err, data) => { ec2.describeSecurityGroups({ Filters: [filter] }, (err, data) => {
if ( err ) { if ( err ) {
set(this, 'errors', [err]); set(this, 'errors', [err]);
if (cb && typeof cb === 'function') {
cb(); cb();
}
return; return;
} }
@ -242,7 +262,9 @@ export default Component.extend(NodeDriver, {
setProperties(this, { step: 4, }); setProperties(this, { step: 4, });
if (cb && typeof cb === 'function') {
cb(); cb();
}
}, },
}, },
@ -257,11 +279,12 @@ export default Component.extend(NodeDriver, {
set(this, 'config.tags', array.join(',')); set(this, 'config.tags', array.join(','));
}), }),
stepDidChange: function() {
stepDidChange: observer('context.step', function() {
scheduleOnce('afterRender', this, () => { scheduleOnce('afterRender', this, () => {
document.body.scrollTop = document.body.scrollHeight; document.body.scrollTop = document.body.scrollHeight;
}); });
}.observes('context.step'), }),
selectedZone: computed('config.{region,zone}', { selectedZone: computed('config.{region,zone}', {
get() { get() {
@ -301,7 +324,7 @@ export default Component.extend(NodeDriver, {
} }
}), }),
zoneChoices: function() { zoneChoices: computed('allSubnets.@each.{zone}', function() {
const choices = (get(this, 'allSubnets') || []).map((subnet) => { const choices = (get(this, 'allSubnets') || []).map((subnet) => {
return get(subnet, 'zone'); return get(subnet, 'zone');
}).sort().uniq(); }).sort().uniq();
@ -315,9 +338,9 @@ export default Component.extend(NodeDriver, {
} }
return choices; return choices;
}.property('allSubnets.@each.{zone}'), }),
subnetChoices: function() { subnetChoices: computed('selectedZone', 'allSubnets.@each.{subnetId,vpcId,zone}', function() {
let out = []; let out = [];
let seenVpcs = []; let seenVpcs = [];
@ -350,7 +373,7 @@ export default Component.extend(NodeDriver, {
}); });
return out.sortBy('sortKey'); return out.sortBy('sortKey');
}.property('selectedZone', 'allSubnets.@each.{subnetId,vpcId,zone}'), }),
selectedSubnet: computed('config.{subnetId,vpcId}', { selectedSubnet: computed('config.{subnetId,vpcId}', {
set(key, val) { set(key, val) {
@ -390,7 +413,6 @@ export default Component.extend(NodeDriver, {
}), }),
bootstrap() { bootstrap() {
let pref = get(this, 'prefs.amazonec2') || {};
let config = get(this, 'globalStore').createRecord({ let config = get(this, 'globalStore').createRecord({
type: 'amazonec2Config', type: 'amazonec2Config',
region: 'us-west-2', region: 'us-west-2',
@ -398,8 +420,6 @@ export default Component.extend(NodeDriver, {
securityGroup: '', securityGroup: '',
zone: 'a', zone: 'a',
rootSize: '16', rootSize: '16',
accessKey: pref.accessKey || '',
secretKey: pref.secretKey || '',
}); });
set(this, 'model.amazonec2Config', config); set(this, 'model.amazonec2Config', config);

View File

@ -2,15 +2,15 @@
<div class="over-hr"><span>{{driverOptionsTitle}}</span></div> <div class="over-hr"><span>{{driverOptionsTitle}}</span></div>
{{#accordion-list-item {{#accordion-list-item
title=(t 'nodeDriver.amazonec2.access.title') title=(t "nodeDriver.amazonec2.access.title")
detail=(t 'nodeDriver.amazonec2.access.detail') detail=(t "nodeDriver.amazonec2.access.detail")
expandAll=expandAll expandAll=expandAll
expand=(action expandFn) expand=(action expandFn)
expandOnInit=true expandOnInit=true
}} }}
<div class="row"> <div class="row">
<div class="col span-4"> <div class="col span-4">
<label class="acc-label">{{t 'nodeDriver.amazonec2.region.label'}}</label> <label class="acc-label">{{t "nodeDriver.amazonec2.region.label"}}</label>
{{#if (eq step 1)}} {{#if (eq step 1)}}
<select class="form-control" onchange={{action (mut config.region) value="target.value"}}> <select class="form-control" onchange={{action (mut config.region) value="target.value"}}>
{{#each regionChoices as |choice|}} {{#each regionChoices as |choice|}}
@ -22,55 +22,49 @@
{{/if}} {{/if}}
</div> </div>
<div class="col span-4"> </div>
<label class="acc-label">{{t 'nodeDriver.amazonec2.accessKey.label'}}</label>
{{#if (eq step 1)}} {{#if (eq step 1)}}
{{input type="text" name="username" classNames="form-control" placeholder=(t 'nodeDriver.amazonec2.accessKey.placeholder') value=config.accessKey}} {{form-auth-cloud-credential
driverName=driverName
errors=errros
primaryResource=primaryResource
cloudCredentials=cloudCredentials
finishAndSelectCloudCredential=(action "finishAndSelectCloudCredential")
progressStep=(action "awsLogin")
cancel=(action "cancel")
}}
{{else}} {{else}}
<div class="row">
<div class="col span-4">
<label class="acc-label">{{t "nodeDriver.amazonec2.accessKey.label"}}</label>
<div>{{config.accessKey}}</div> <div>{{config.accessKey}}</div>
{{/if}}
</div> </div>
<div class="col span-4"> <div class="col span-4">
<label class="acc-label">{{t 'nodeDriver.amazonec2.secretKey.label'}}</label> <label class="acc-label">{{t "nodeDriver.amazonec2.secretKey.label"}}</label>
{{#if (eq step 1)}} <div class="text-muted text-italic">{{t "nodeDriver.amazonec2.secretKey.provided"}}</div>
{{input type="password" name="password" classNames="form-control" placeholder=(t 'nodeDriver.amazonec2.secretKey.placeholder') value=config.secretKey}}
{{else}}
<div class="text-muted text-italic">{{t 'nodeDriver.amazonec2.secretKey.provided'}}</div>
{{/if}}
</div> </div>
</div> </div>
{{#if (eq step 1)}}
<div class="row"> <div class="row">
<div class="span-8 offset-4"> <div class="span-8 offset-4">
<p class="text-info text-small m-0">{{t 'nodeDriver.amazonec2.access.help'}}</p> <p class="text-info text-small m-0">{{t "nodeDriver.amazonec2.access.help"}}</p>
</div> </div>
</div> </div>
{{/if}} {{/if}}
{{top-errors errors=errors}}
{{/accordion-list-item}} {{/accordion-list-item}}
{{#if (eq step 1)}} <div class="{{unless (gte step 2) "hide"}}">
{{top-errors errors=errors}}
{{save-cancel
save="awsLogin"
cancel="cancel"
createLabel="nodeDriver.amazonec2.access.next"
savingLabel="nodeDriver.amazonec2.access.loading"
}}
{{/if}}
<div class="{{unless (gte step 2) 'hide'}}">
{{#accordion-list-item {{#accordion-list-item
title=(t 'nodeDriver.amazonec2.zone.title') title=(t "nodeDriver.amazonec2.zone.title")
detail=(t 'nodeDriver.amazonec2.zone.detail') detail=(t "nodeDriver.amazonec2.zone.detail")
expandAll=expandAll expandAll=expandAll
expand=(action expandFn) expand=(action expandFn)
expandOnInit=true expandOnInit=true
}} }}
{{#if (eq step 2)}} {{#if (eq step 2)}}
<div class="row"> <div class="row">
<label class="acc-label">{{t 'nodeDriver.amazonec2.availabilityZone'}}</label> <label class="acc-label">{{t "nodeDriver.amazonec2.availabilityZone"}}</label>
<select class="form-control" onchange={{action (mut selectedZone) value="target.value"}}> <select class="form-control" onchange={{action (mut selectedZone) value="target.value"}}>
{{#each zoneChoices as |choice|}} {{#each zoneChoices as |choice|}}
<option value={{choice}} selected={{eq selectedZone choice}}>{{choice}}</option> <option value={{choice}} selected={{eq selectedZone choice}}>{{choice}}</option>
@ -79,7 +73,7 @@
</div> </div>
<div class="row"> <div class="row">
<label class="acc-label pt-20">{{t 'nodeDriver.amazonec2.subnet'}}</label> <label class="acc-label pt-20">{{t "nodeDriver.amazonec2.subnet"}}</label>
{{#if subnetChoices.length}} {{#if subnetChoices.length}}
{{#each subnetChoices as |choice|}} {{#each subnetChoices as |choice|}}
{{#if choice.isVpc}} {{#if choice.isVpc}}
@ -115,11 +109,11 @@
{{else}} {{else}}
<div class="row"> <div class="row">
<div class="span-6"> <div class="span-6">
<label class="acc-label">{{t 'nodeDriver.amazonec2.availabilityZone'}}</label> <label class="acc-label">{{t "nodeDriver.amazonec2.availabilityZone"}}</label>
{{config.region}}{{config.zone}} {{config.region}}{{config.zone}}
</div> </div>
<div class="span-6"> <div class="span-6">
<label class="acc-label pt-20">{{t 'nodeDriver.amazonec2.subnet'}}</label> <label class="acc-label pt-20">{{t "nodeDriver.amazonec2.subnet"}}</label>
{{#if config.subnetId}} {{#if config.subnetId}}
{{config.subnetId}} {{config.subnetId}}
{{else}} {{else}}
@ -141,10 +135,10 @@
{{/if}} {{/if}}
</div> </div>
<div class="{{unless (gte step 3) 'hide'}}"> <div class="{{unless (gte step 3) "hide"}}">
{{#accordion-list-item {{#accordion-list-item
title=(t 'nodeDriver.amazonec2.securityGroup.title') title=(t "nodeDriver.amazonec2.securityGroup.title")
detail=(t 'nodeDriver.amazonec2.securityGroup.detail') detail=(t "nodeDriver.amazonec2.securityGroup.detail")
expandAll=expandAll expandAll=expandAll
expand=(action expandFn) expand=(action expandFn)
expandOnInit=true expandOnInit=true
@ -154,23 +148,23 @@
<div class="radio pt-10"> <div class="radio pt-10">
<label> <label>
{{radio-button selection=whichSecurityGroup value="default"}} {{radio-button selection=whichSecurityGroup value="default"}}
{{t 'nodeDriver.amazonec2.securityGroup.defaultCreate' groupName=defaultSecurityGroupName htmlSafe=true}} {{t "nodeDriver.amazonec2.securityGroup.defaultCreate" groupName=defaultSecurityGroupName htmlSafe=true}}
</label> </label>
</div> </div>
{{#if (and settings.isRancher (not isCustomSecurityGroup))}} {{#if (and settings.isRancher (not isCustomSecurityGroup))}}
<p class="text-info ml-20"><a href="{{settings.docsBase}}/installation/references/" target="_blank">{{t 'nodeDriver.amazonec2.portHelp.link'}}</a> {{t 'nodeDriver.amazonec2.portHelp.text'}}</p> <p class="text-info ml-20"><a href="{{settings.docsBase}}/installation/references/" target="_blank">{{t "nodeDriver.amazonec2.portHelp.link"}}</a> {{t "nodeDriver.amazonec2.portHelp.text"}}</p>
{{/if}} {{/if}}
<div class="radio pt-10"> <div class="radio pt-10">
<label> <label>
{{radio-button selection=whichSecurityGroup value="custom"}} {{radio-button selection=whichSecurityGroup value="custom"}}
{{t 'nodeDriver.amazonec2.securityGroup.choose'}} {{t "nodeDriver.amazonec2.securityGroup.choose"}}
</label> </label>
</div> </div>
{{#if isCustomSecurityGroup}} {{#if isCustomSecurityGroup}}
<select class="form-control existing-security-groups" multiple="true" onchange={{action 'multiSecurityGroupSelect' ''}}> <select class="form-control existing-security-groups" multiple="true" onchange={{action "multiSecurityGroupSelect" ""}}>
{{#each allSecurityGroups as |choice|}} {{#each allSecurityGroups as |choice|}}
<option value={{choice.name}} selected={{array-includes selectedSecurityGroup choice.name}}>{{choice.name}} ({{choice.id}})</option> <option value={{choice.name}} selected={{array-includes selectedSecurityGroup choice.name}}>{{choice.name}} ({{choice.id}})</option>
{{/each}} {{/each}}
@ -203,17 +197,17 @@
{{/if}} {{/if}}
</div> </div>
<div class="{{unless (gte step 4) 'hide'}}"> <div class="{{unless (gte step 4) "hide"}}">
{{#accordion-list-item {{#accordion-list-item
title=(t 'nodeDriver.amazonec2.instance.title') title=(t "nodeDriver.amazonec2.instance.title")
detail=(t 'nodeDriver.amazonec2.instance.detail') detail=(t "nodeDriver.amazonec2.instance.detail")
expandAll=expandAll expandAll=expandAll
expand=(action expandFn) expand=(action expandFn)
expandOnInit=true expandOnInit=true
}} }}
<div class="row"> <div class="row">
<div class="col span-6"> <div class="col span-6">
<label class="acc-label">{{t 'nodeDriver.amazonec2.instanceType.label'}}</label> <label class="acc-label">{{t "nodeDriver.amazonec2.instanceType.label"}}</label>
{{new-select {{new-select
classNames="form-control" classNames="form-control"
value=config.instanceType value=config.instanceType
@ -225,59 +219,59 @@
</div> </div>
<div class="col span-6"> <div class="col span-6">
<label class="acc-label">{{t 'nodeDriver.amazonec2.rootSize.label'}}</label> <label class="acc-label">{{t "nodeDriver.amazonec2.rootSize.label"}}</label>
<div class="input-group"> <div class="input-group">
{{input type="text" classNames="form-control" placeholder="" value=config.rootSize}} {{input type="text" classNames="form-control" placeholder="" value=config.rootSize}}
<span class="input-group-addon bg-default">{{t 'nodeDriver.amazonec2.rootSize.unit'}}</span> <span class="input-group-addon bg-default">{{t "nodeDriver.amazonec2.rootSize.unit"}}</span>
</div> </div>
</div> </div>
</div> </div>
<div class="row"> <div class="row">
<div class="col span-6"> <div class="col span-6">
<label class="acc-label">{{t 'nodeDriver.amazonec2.ami.label'}}</label> <label class="acc-label">{{t "nodeDriver.amazonec2.ami.label"}}</label>
{{input type="text" classNames="form-control" placeholder=(t 'nodeDriver.amazonec2.ami.placeholder') value=config.ami}} {{input type="text" classNames="form-control" placeholder=(t "nodeDriver.amazonec2.ami.placeholder") value=config.ami}}
{{#if settings.isRancher}} {{#if settings.isRancher}}
<p class="help-block"> <p class="help-block">
<a href="https://github.com/rancher/os/blob/master/README.md/#user-content-amazon" target="_blank">{{t 'nodeDriver.amazonec2.ami.rancherList'}}</a> <a href="https://github.com/rancher/os/blob/master/README.md/#user-content-amazon" target="_blank">{{t "nodeDriver.amazonec2.ami.rancherList"}}</a>
</p> </p>
{{/if}} {{/if}}
</div> </div>
<div class="col span-6"> <div class="col span-6">
<label class="acc-label">{{t 'nodeDriver.amazonec2.sshUser.label'}}</label> <label class="acc-label">{{t "nodeDriver.amazonec2.sshUser.label"}}</label>
{{input type="text" classNames="form-control" placeholder=(t 'nodeDriver.amazonec2.sshUser.placeholder') value=config.sshUser}} {{input type="text" classNames="form-control" placeholder=(t "nodeDriver.amazonec2.sshUser.placeholder") value=config.sshUser}}
</div> </div>
</div> </div>
<div class="row"> <div class="row">
<div class="col span-6"> <div class="col span-6">
<label class="acc-label">{{t 'nodeDriver.amazonec2.iam.label'}}</label> <label class="acc-label">{{t "nodeDriver.amazonec2.iam.label"}}</label>
{{input type="text" classNames="form-control" value=config.iamInstanceProfile placeholder=(t 'nodeDriver.amazonec2.iam.placeholder')}} {{input type="text" classNames="form-control" value=config.iamInstanceProfile placeholder=(t "nodeDriver.amazonec2.iam.placeholder")}}
</div> </div>
<div class="col span-6"> <div class="col span-6">
<label class="acc-label">{{t 'nodeDriver.amazonec2.privateIp.label'}}</label> <label class="acc-label">{{t "nodeDriver.amazonec2.privateIp.label"}}</label>
<div class="checkbox"> <div class="checkbox">
<label class="acc-label">{{input type="checkbox" checked=config.privateAddressOnly}} {{t 'nodeDriver.amazonec2.onlyPrivate.label'}}</label> <label class="acc-label">{{input type="checkbox" checked=config.privateAddressOnly}} {{t "nodeDriver.amazonec2.onlyPrivate.label"}}</label>
</div> </div>
</div> </div>
</div> </div>
<div class="row"> <div class="row">
<div class="col span-6"> <div class="col span-6">
<label class="acc-label">{{t 'nodeDriver.amazonec2.requestSpotInstance.label'}}</label> <label class="acc-label">{{t "nodeDriver.amazonec2.requestSpotInstance.label"}}</label>
<div class="checkbox"> <div class="checkbox">
<label class="acc-label">{{input type="checkbox" checked=config.requestSpotInstance}} {{t 'nodeDriver.amazonec2.requestSpotInstance.enable'}}</label> <label class="acc-label">{{input type="checkbox" checked=config.requestSpotInstance}} {{t "nodeDriver.amazonec2.requestSpotInstance.enable"}}</label>
</div> </div>
</div> </div>
{{#if config.requestSpotInstance}} {{#if config.requestSpotInstance}}
<div class="col span-6"> <div class="col span-6">
<label class="acc-label">{{t 'nodeDriver.amazonec2.spotPrice.label'}}</label> <label class="acc-label">{{t "nodeDriver.amazonec2.spotPrice.label"}}</label>
<div class="input-group"> <div class="input-group">
{{input type="text" classNames="form-control" placeholder="" value=config.spotPrice}} {{input type="text" classNames="form-control" placeholder="" value=config.spotPrice}}
<span class="input-group-addon bg-default">{{t 'nodeDriver.amazonec2.spotPrice.unit'}}</span> <span class="input-group-addon bg-default">{{t "nodeDriver.amazonec2.spotPrice.unit"}}</span>
</div> </div>
</div> </div>
{{/if}} {{/if}}
@ -303,7 +297,7 @@
{{form-user-labels {{form-user-labels
initialLabels=labelResource.labels initialLabels=labelResource.labels
setLabels=(action 'setLabels') setLabels=(action "setLabels")
expandAll=expandAll expandAll=expandAll
expand=(action expandFn) expand=(action expandFn)
}} }}

View File

@ -46,6 +46,12 @@ export default Component.extend(NodeDriver, {
}); });
}, },
actions: {
finishAndSelectCloudCredential(credential) {
set(this, 'model.cloudCredentialId', get(credential, 'id'))
}
},
evironmentChoiceObserver: observer('config.environment', function() { evironmentChoiceObserver: observer('config.environment', function() {
let environment = get(this, 'config.environment'); let environment = get(this, 'config.environment');
@ -116,8 +122,6 @@ export default Component.extend(NodeDriver, {
let config = get(this, 'globalStore').createRecord({ let config = get(this, 'globalStore').createRecord({
type: CONFIG, type: CONFIG,
subscriptionId: '', subscriptionId: '',
clientId: '',
clientSecret: '',
openPort: ['6443/tcp', '2379/tcp', '2380/tcp', '8472/udp', '4789/udp', '10256/tcp', '10250/tcp', '10251/tcp', '10252/tcp'], openPort: ['6443/tcp', '2379/tcp', '2380/tcp', '8472/udp', '4789/udp', '10256/tcp', '10250/tcp', '10251/tcp', '10252/tcp'],
}); });
@ -146,18 +150,6 @@ export default Component.extend(NodeDriver, {
errors.push('Name is required'); errors.push('Name is required');
} }
if (!get(this, 'config.subscriptionId')) {
errors.push('Subscription ID is required');
}
if (!get(this, 'config.clientId')) {
errors.push('Client ID is requried');
}
if (!get(this, 'config.clientSecret')) {
errors.push('Client Secret is requried');
}
if (errors.length) { if (errors.length) {
set(this, 'errors', errors.uniq()); set(this, 'errors', errors.uniq());

View File

@ -1,147 +1,264 @@
{{#accordion-list showExpandAll=false as | al expandFn |}} {{#accordion-list showExpandAll=false as | al expandFn |}}
<div class="over-hr"><span>{{driverOptionsTitle}}</span></div> <div class="over-hr">
<span>
{{driverOptionsTitle}}
</span>
</div>
{{#accordion-list-item {{#accordion-list-item
title=(t 'nodeDriver.azure.placement.title') title=(t "nodeDriver.azure.access.title")
detail=(t 'nodeDriver.azure.placement.detail') detail=(t "nodeDriver.azure.access.detail")
expandAll=expandAll
expand=(action expandFn)
expandOnInit=true
}}
{{form-auth-cloud-credential
driverName=driverName
errors=errros
primaryResource=primaryResource
cloudCredentials=cloudCredentials
finishAndSelectCloudCredential=(action "finishAndSelectCloudCredential")
progressStep=(action "finishAndSelectCloudCredential")
cancel=(action "cancel")
hideSave=true
}}
{{/accordion-list-item}}
{{#accordion-list-item
title=(t "nodeDriver.azure.placement.title")
detail=(t "nodeDriver.azure.placement.detail")
expandAll=expandAll expandAll=expandAll
expand=(action expandFn) expand=(action expandFn)
expandOnInit=true expandOnInit=true
}} }}
<div class="row"> <div class="row">
<div class="col span-6"> <div class="col span-6">
<label class="acc-label">{{t 'nodeDriver.azure.environment.label'}}</label> <label class="acc-label">
{{new-select classNames="form-control" content=environments optionLabelPath='value' value=config.environment}} {{t "nodeDriver.azure.environment.label"}}
</label>
{{new-select
classNames="form-control"
content=environments
optionLabelPath="value"
value=config.environment
}}
</div> </div>
<div class="col span-6"> <div class="col span-6">
<label class="acc-label">{{t 'nodeDriver.azure.region.label'}}</label> <label class="acc-label">
{{new-select classNames="form-control" content=regionChoices optionLabelPath='displayName' optionValuePath='name' value=config.location}} {{t "nodeDriver.azure.region.label"}}
</label>
{{new-select
classNames="form-control"
content=regionChoices
optionLabelPath="displayName"
optionValuePath="name"
value=config.location
}}
</div> </div>
</div> </div>
<div class="row"> <div class="row">
<div class="col span-6"> <div class="col span-6">
<label class="acc-label">{{t 'nodeDriver.azure.availabilitySet.label'}}</label> <label class="acc-label">
{{input type="text" value=config.availabilitySet classNames="form-control" placeholder=(t 'nodeDriver.azure.availabilitySet.placeholder')}} {{t "nodeDriver.azure.availabilitySet.label"}}
</label>
{{input
type="text"
value=config.availabilitySet
classNames="form-control"
placeholder=(t "nodeDriver.azure.availabilitySet.placeholder")
}}
</div> </div>
<div class="col span-6"> <div class="col span-6">
<label class="acc-label">{{t 'nodeDriver.azure.resourceGroup.label'}}</label> <label class="acc-label">
{{input type="text" value=config.resourceGroup classNames="form-control" placeholder=(t 'nodeDriver.azure.resourceGroup.placeholder')}} {{t "nodeDriver.azure.resourceGroup.label"}}
</label>
{{input
type="text"
value=config.resourceGroup
classNames="form-control"
placeholder=(t "nodeDriver.azure.resourceGroup.placeholder")
}}
</div> </div>
</div> </div>
{{/accordion-list-item}} {{/accordion-list-item}}
{{#accordion-list-item {{#accordion-list-item
title=(t 'nodeDriver.azure.access.title') title=(t "nodeDriver.azure.network.title")
detail=(t 'nodeDriver.azure.access.detail') detail=(t "nodeDriver.azure.network.detail")
expandAll=expandAll expandAll=expandAll
expand=(action expandFn) expand=(action expandFn)
expandOnInit=true expandOnInit=true
}} }}
<div class="row"> <div class="row">
<div class="col span-6"> <div class="col span-6">
<label class="acc-label">{{t 'nodeDriver.azure.subscriptionId.label'}}{{field-required}}</label> <label class="acc-label">
{{input type="text" value=config.subscriptionId classNames="form-control" placeholder=(t 'nodeDriver.azure.subscriptionId.placeholder')}} {{t "nodeDriver.azure.subnet.label"}}
</div> </label>
</div> {{input
type="text"
<div class="row"> value=config.subnet
<div class="col span-6"> classNames="form-control"
<label class="acc-label">{{t 'nodeDriver.azure.clientId.label'}}{{field-required}}</label> placeholder=(t "nodeDriver.azure.subnet.placeholder")
{{input type="text" value=config.clientId classNames="form-control"}}
</div>
<div class="col span-6">
<label class="acc-label">{{t 'nodeDriver.azure.clientSecret.label'}}{{field-required}}</label>
{{input type="password" value=config.clientSecret classNames="form-control"}}
</div>
</div>
{{/accordion-list-item}}
{{#accordion-list-item
title=(t 'nodeDriver.azure.network.title')
detail=(t 'nodeDriver.azure.network.detail')
expandAll=expandAll
expand=(action expandFn)
expandOnInit=true
}} }}
<div class="row">
<div class="col span-6">
<label class="acc-label">{{t 'nodeDriver.azure.subnet.label'}}</label>
{{input type="text" value=config.subnet classNames="form-control" placeholder=(t 'nodeDriver.azure.subnet.placeholder')}}
</div> </div>
<div class="col span-6"> <div class="col span-6">
<label class="acc-label">{{t 'nodeDriver.azure.subnetPrefix.label'}}</label> <label class="acc-label">
{{input type="text" value=config.subnetPrefix classNames="form-control" placeholder=(t 'nodeDriver.azure.subnetPrefix.placeholder')}} {{t "nodeDriver.azure.subnetPrefix.label"}}
</label>
{{input
type="text"
value=config.subnetPrefix
classNames="form-control"
placeholder=(t "nodeDriver.azure.subnetPrefix.placeholder")
}}
</div> </div>
</div> </div>
<div class="row"> <div class="row">
<div class="col span-6"> <div class="col span-6">
<label class="acc-label">{{t 'nodeDriver.azure.vnet.label'}}</label> <label class="acc-label">
{{input type="text" value=config.vnet classNames="form-control" placeholder=(t 'nodeDriver.azure.vnet.placeholder')}} {{t "nodeDriver.azure.vnet.label"}}
</label>
{{input
type="text"
value=config.vnet
classNames="form-control"
placeholder=(t "nodeDriver.azure.vnet.placeholder")
}}
</div> </div>
<div class="col span-6"> <div class="col span-6">
<label class="acc-label">{{t 'nodeDriver.azure.staticPublicIp.label'}}</label> <label class="acc-label">
{{new-select classNames="form-control" content=publicIpChoices optionLabelPath='name' optionValuePath='value' value=publicIpChoice}} {{t "nodeDriver.azure.staticPublicIp.label"}}
</label>
{{new-select
classNames="form-control"
content=publicIpChoices
optionLabelPath="name"
optionValuePath="value"
value=publicIpChoice
}}
</div> </div>
</div> </div>
<div class="row"> <div class="row">
<div class="col span-6"> <div class="col span-6">
<label class="acc-label">{{t 'nodeDriver.azure.privateIpAddress.label'}}</label> <label class="acc-label">
{{input type="text" value=config.privateIpAddress classNames="form-control" placeholder=(t 'nodeDriver.azure.privateIpAddress.placeholder')}} {{t "nodeDriver.azure.privateIpAddress.label"}}
</label>
{{input
type="text"
value=config.privateIpAddress
classNames="form-control"
placeholder=(t "nodeDriver.azure.privateIpAddress.placeholder")
}}
</div> </div>
<div class="col span-6"> <div class="col span-6">
<label class="acc-label">{{t 'nodeDriver.azure.usePrivateIp.label'}}</label> <label class="acc-label">
{{t "nodeDriver.azure.usePrivateIp.label"}}
</label>
<div> <div>
{{input type="checkbox" checked=config.usePrivateIp disabled=privateSet}} {{input
type="checkbox"
checked=config.usePrivateIp
disabled=privateSet
}}
</div> </div>
</div> </div>
</div> </div>
{{/accordion-list-item}} {{/accordion-list-item}}
{{#accordion-list-item {{#accordion-list-item
title=(t 'nodeDriver.azure.instance.title') title=(t "nodeDriver.azure.instance.title")
detail=(t 'nodeDriver.azure.instance.detail') detail=(t "nodeDriver.azure.instance.detail")
expandAll=expandAll expandAll=expandAll
expand=(action expandFn) expand=(action expandFn)
expandOnInit=true expandOnInit=true
}} }}
<div class="row"> <div class="row">
<div class="col span-6"> <div class="col span-6">
<label class="acc-label">{{t 'nodeDriver.azure.image.label'}}</label> <label class="acc-label">
{{input type="text" value=config.image classNames="form-control" placeholder=(t 'nodeDriver.azure.image.placeholder')}} {{t "nodeDriver.azure.image.label"}}
</label>
{{input
type="text"
value=config.image
classNames="form-control"
placeholder=(t "nodeDriver.azure.image.placeholder")
}}
</div> </div>
<div class="col span-6"> <div class="col span-6">
<label class="acc-label">{{t 'nodeDriver.azure.size.label'}}</label> <label class="acc-label">
{{new-select classNames="form-control" content=sizeChoices optionLabelPath='value' optionGroupPath='group' value=config.size}} {{t "nodeDriver.azure.size.label"}}
</label>
{{new-select
classNames="form-control"
content=sizeChoices
optionLabelPath="value"
optionGroupPath="group"
value=config.size
}}
</div> </div>
</div> </div>
<div class="row"> <div class="row">
<div class="col span-6"> <div class="col span-6">
<label class="acc-label">{{t 'nodeDriver.azure.dockerPort.label'}}</label> <label class="acc-label">
{{input type="text" value=config.dockerPort classNames="form-control" placeholder=(t 'nodeDriver.azure.dockerPort.placeholder')}} {{t "nodeDriver.azure.dockerPort.label"}}
</label>
{{input
type="text"
value=config.dockerPort
classNames="form-control"
placeholder=(t "nodeDriver.azure.dockerPort.placeholder")
}}
</div> </div>
<div class="col span-6"> <div class="col span-6">
<label class="acc-label">{{t 'nodeDriver.azure.openPort.label'}}</label> <label class="acc-label">
{{input type="text" value=openPorts classNames="form-control" placeholder=(t 'nodeDriver.azure.openPort.placeholder')}} {{t "nodeDriver.azure.openPort.label"}}
</label>
{{input
type="text"
value=openPorts
classNames="form-control"
placeholder=(t "nodeDriver.azure.openPort.placeholder")
}}
</div> </div>
</div> </div>
<div class="row"> <div class="row">
<div class="col span-6"> <div class="col span-6">
<label class="acc-label">{{t 'nodeDriver.azure.sshUser.label'}}</label> <label class="acc-label">
{{input type="text" value=config.sshUser classNames="form-control" placeholder=(t 'nodeDriver.azure.sshUser.placeholder')}} {{t "nodeDriver.azure.sshUser.label"}}
</label>
{{input
type="text"
value=config.sshUser
classNames="form-control"
placeholder=(t "nodeDriver.azure.sshUser.placeholder")
}}
</div> </div>
<div class="col span-6"> <div class="col span-6">
<label class="acc-label">{{t 'nodeDriver.azure.storageType.label'}}</label> <label class="acc-label">
{{new-select classNames="form-control" content=storageTypeChoices optionLabelPath='name' optionValuePath='value' value=config.storageType}} {{t "nodeDriver.azure.storageType.label"}}
</label>
{{new-select
classNames="form-control"
content=storageTypeChoices
optionLabelPath="name"
optionValuePath="value"
value=config.storageType
}}
</div> </div>
</div> </div>
{{/accordion-list-item}} {{/accordion-list-item}}
<div class="over-hr"><span>{{templateOptionsTitle}}</span></div> <div class="over-hr">
<span>
{{templateOptionsTitle}}
</span>
</div>
{{form-name-description {{form-name-description
model=model model=model
@ -150,7 +267,7 @@
{{form-user-labels {{form-user-labels
initialLabels=labelResource.labels initialLabels=labelResource.labels
setLabels=(action 'setLabels') setLabels=(action "setLabels")
expandAll=expandAll expandAll=expandAll
expand=(action expandFn) expand=(action expandFn)
}} }}
@ -160,6 +277,13 @@
showEngineUrl=showEngineUrl showEngineUrl=showEngineUrl
}} }}
{{top-errors errors=errors}} {{top-errors
{{save-cancel save="save" cancel="cancel" editing=editing}} errors=errors
}}
{{save-cancel
save="save"
cancel="cancel"
editing=editing
}}
{{/accordion-list}} {{/accordion-list}}

View File

@ -37,24 +37,28 @@ export default Component.extend(NodeDriver, {
driverName: 'digitalocean', driverName: 'digitalocean',
regionChoices: null, regionChoices: null,
model: null, model: null,
step: 1, step: 1,
sizeChoices: null, sizeChoices: null,
imageChoices: null, imageChoices: null,
tags: null, tags: null,
config: alias('model.digitaloceanConfig'), config: alias('primaryResource.digitaloceanConfig'),
init() { init() {
this._super(...arguments); this._super(...arguments);
const tags = get(this, 'config.tags'); this.initTags();
if (tags) {
set(this, 'tags', tags.split(','));
}
}, },
actions: { actions: {
finishAndSelectCloudCredential(cred) {
if (cred) {
set(this, 'primaryResource.cloudCredentialId', get(cred, 'id'));
this.send('getData');
}
},
getData(cb) { getData(cb) {
let promises = { let promises = {
regions: this.apiRequest('regions'), regions: this.apiRequest('regions'),
@ -103,7 +107,9 @@ export default Component.extend(NodeDriver, {
setProperties(this, { errors, }); setProperties(this, { errors, });
if (cb && typeof cb === 'function') {
cb(); cb();
}
}); });
}, },
}, },
@ -134,6 +140,14 @@ export default Component.extend(NodeDriver, {
return out; return out;
}), }),
initTags() {
const tags = get(this, 'config.tags');
if (tags) {
set(this, 'tags', tags.split(','));
}
},
bootstrap() { bootstrap() {
let config = get(this, 'globalStore').createRecord({ let config = get(this, 'globalStore').createRecord({
type: 'digitaloceanConfig', type: 'digitaloceanConfig',
@ -143,15 +157,16 @@ export default Component.extend(NodeDriver, {
sshUser: 'root' sshUser: 'root'
}); });
const model = get(this, 'model'); const primaryResource = get(this, 'primaryResource');
set(model, 'digitaloceanConfig', config); set(primaryResource, 'digitaloceanConfig', config);
}, },
apiRequest(command, opt, out) { apiRequest(command, opt, out) {
opt = opt || {}; opt = opt || {};
let url = `${ get(this, 'app.proxyEndpoint') }/`; let url = `${ get(this, 'app.proxyEndpoint') }/`;
let cloudCredentialId = get(this, 'primaryResource.cloudCredentialId');
if ( opt.url ) { if ( opt.url ) {
url += opt.url.replace(/^http[s]?\/\//, ''); url += opt.url.replace(/^http[s]?\/\//, '');
@ -164,7 +179,7 @@ export default Component.extend(NodeDriver, {
return fetch(url, { return fetch(url, {
headers: { headers: {
'Accept': 'application/json', 'Accept': 'application/json',
'X-Api-Auth-Header': `Bearer ${ get(this, 'config.accessToken') }`, 'x-api-cattleauth-header': `Bearer credID=${ cloudCredentialId } passwordField=accessToken`,
}, },
}).then((res) => { }).then((res) => {
let body = res.body; let body = res.body;

View File

@ -1,33 +1,40 @@
<section class="horizontal-form"> <section class="horizontal-form">
{{#if (eq step 1)}}
<div class="box mt-20">
<div class="row">
<label class="acc-label">{{t 'nodeDriver.digitalocean.accessToken.label'}}</label>
{{input type="password" value=config.accessToken classNames="form-control" placeholder=(t 'nodeDriver.digitalocean.accessToken.placeholder')}}
<p class="text-info">{{t 'nodeDriver.digitalocean.accessToken.help' htmlSafe=true}}</p>
</div>
</div>
{{top-errors errors=errors}}
{{save-cancel
save="getData"
cancel="cancel"
createLabel="nodeDriver.digitalocean.authAccountButton"
savingLabel="generic.loading"
}}
{{else}}
{{#accordion-list showExpandAll=false as | al expandFn |}} {{#accordion-list showExpandAll=false as | al expandFn |}}
{{#if (eq step 1)}}
{{#accordion-list-item
title=(t "nodeDriver.digitalocean.accessToken.label")
detail=(t "nodeDriver.digitalocean.accessToken.help" htmlSafe=true)
expandAll=expandAll
expand=(action expandFn)
expandOnInit=true
}}
{{form-auth-cloud-credential
driverName=driverName
errors=errros
primaryResource=primaryResource
cloudCredentials=cloudCredentials
finishAndSelectCloudCredential=(action "finishAndSelectCloudCredential")
progressStep=(action "getData")
cancel=(action "cancel")
createLabel="nodeDriver.digitalocean.authAccountButton"
}}
{{/accordion-list-item}}
{{top-errors errors=errors}}
{{else}}
<div class="over-hr mb-20"><span>{{driverOptionsTitle}}</span></div> <div class="over-hr mb-20"><span>{{driverOptionsTitle}}</span></div>
{{#accordion-list-item {{#accordion-list-item
title=(t 'nodeDriver.digitalocean.droplet.title') title=(t "nodeDriver.digitalocean.droplet.title")
detail=(t 'nodeDriver.digitalocean.droplet.detail') detail=(t "nodeDriver.digitalocean.droplet.detail")
expandAll=expandAll expandAll=expandAll
expand=(action expandFn) expand=(action expandFn)
expandOnInit=true expandOnInit=true
}} }}
<div class="row"> <div class="row">
<div class="col span-6"> <div class="col span-6">
<label class="acc-label">{{t 'nodeDriver.digitalocean.region.label'}}</label> <label class="acc-label">{{t "nodeDriver.digitalocean.region.label"}}</label>
<select class="form-control" onchange={{action (mut config.region) value="target.value"}}> <select class="form-control" onchange={{action (mut config.region) value="target.value"}}>
{{#each regionChoices as |choice|}} {{#each regionChoices as |choice|}}
<option value={{choice.slug}} selected={{eq config.region choice.slug}}>{{choice.name}}</option> <option value={{choice.slug}} selected={{eq config.region choice.slug}}>{{choice.name}}</option>
@ -35,17 +42,17 @@
</select> </select>
</div> </div>
<div class="col span-6"> <div class="col span-6">
<label class="acc-label">{{t 'nodeDriver.digitalocean.size.label'}}</label> <label class="acc-label">{{t "nodeDriver.digitalocean.size.label"}}</label>
<select class="form-control" onchange={{action (mut config.size) value="target.value"}}> <select class="form-control" onchange={{action (mut config.size) value="target.value"}}>
{{#each filteredSizeChoices as |choice|}} {{#each filteredSizeChoices as |choice|}}
<option value={{choice.slug}} selected={{eq config.size choice.slug}}>{{t 'nodeDriver.digitalocean.sizeLabel' memoryGb=choice.memoryGb highMem=choice.highMem slug=choice.slug disk=choice.disk vcpus=choice.vcpus}}</option> <option value={{choice.slug}} selected={{eq config.size choice.slug}}>{{t "nodeDriver.digitalocean.sizeLabel" memoryGb=choice.memoryGb highMem=choice.highMem slug=choice.slug disk=choice.disk vcpus=choice.vcpus}}</option>
{{/each}} {{/each}}
</select> </select>
</div> </div>
</div> </div>
<div class="row"> <div class="row">
<div class="col span-6"> <div class="col span-6">
<label class="acc-label">{{t 'nodeDriver.digitalocean.image.label'}}</label> <label class="acc-label">{{t "nodeDriver.digitalocean.image.label"}}</label>
<select class="form-control" onchange={{action (mut config.image) value="target.value"}}> <select class="form-control" onchange={{action (mut config.image) value="target.value"}}>
{{#each imageChoices as |choice|}} {{#each imageChoices as |choice|}}
<option value={{choice.slug}} disabled={{choice.disabled}} selected={{eq config.image choice.slug}}>{{choice.distribution}} {{choice.name}}</option> <option value={{choice.slug}} disabled={{choice.disabled}} selected={{eq config.image choice.slug}}>{{choice.distribution}} {{choice.name}}</option>
@ -53,33 +60,33 @@
</select> </select>
</div> </div>
<div class="col span-6"> <div class="col span-6">
<label class="acc-label">{{t 'nodeDriver.digitalocean.sshUser.label'}}</label> <label class="acc-label">{{t "nodeDriver.digitalocean.sshUser.label"}}</label>
{{input type="text" value=config.sshUser classNames="form-control" placeholder=(t 'nodeDriver.digitalocean.sshUser.placeholder')}} {{input type="text" value=config.sshUser classNames="form-control" placeholder=(t "nodeDriver.digitalocean.sshUser.placeholder")}}
</div> </div>
</div> </div>
<div class="row"> <div class="row">
<div class="col span-3 text-center"> <div class="col span-3 text-center">
<label class="acc-label"> <label class="acc-label">
{{input type="checkbox" checked=config.backups}} {{input type="checkbox" checked=config.backups}}
{{t 'nodeDriver.digitalocean.backups.label'}} {{t "nodeDriver.digitalocean.backups.label"}}
</label> </label>
</div> </div>
<div class="col span-3 text-center"> <div class="col span-3 text-center">
<label class="acc-label"> <label class="acc-label">
{{input type="checkbox" checked=config.privateNetworking}} {{input type="checkbox" checked=config.privateNetworking}}
{{t 'nodeDriver.digitalocean.privateNetworking.label'}} {{t "nodeDriver.digitalocean.privateNetworking.label"}}
</label> </label>
</div> </div>
<div class="col span-3 text-center"> <div class="col span-3 text-center">
<label class="acc-label"> <label class="acc-label">
{{input type="checkbox" checked=config.ipv6}} {{input type="checkbox" checked=config.ipv6}}
{{t 'nodeDriver.digitalocean.ipv6.label'}} {{t "nodeDriver.digitalocean.ipv6.label"}}
</label> </label>
</div> </div>
<div class="col span-3 text-center"> <div class="col span-3 text-center">
<label class="acc-label"> <label class="acc-label">
{{input type="checkbox" checked=config.monitoring}} {{input type="checkbox" checked=config.monitoring}}
{{t 'nodeDriver.digitalocean.monitoring.label'}} {{t "nodeDriver.digitalocean.monitoring.label"}}
</label> </label>
</div> </div>
</div> </div>
@ -87,9 +94,9 @@
<div class="col span-6"> <div class="col span-6">
{{form-value-array {{form-value-array
initialValues=tags initialValues=tags
addActionLabel='nodeDriver.digitalocean.tags.addActionLabel' addActionLabel="nodeDriver.digitalocean.tags.addActionLabel"
valueLabel='nodeDriver.digitalocean.tags.valueLabel' valueLabel="nodeDriver.digitalocean.tags.valueLabel"
valuePlaceholder='nodeDriver.digitalocean.tags.placeholder' valuePlaceholder="nodeDriver.digitalocean.tags.placeholder"
changed=(action (mut tags)) changed=(action (mut tags))
}} }}
</div> </div>
@ -105,7 +112,7 @@
{{form-user-labels {{form-user-labels
initialLabels=labelResource.labels initialLabels=labelResource.labels
setLabels=(action 'setLabels') setLabels=(action "setLabels")
expandAll=expandAll expandAll=expandAll
expand=(action expandFn) expand=(action expandFn)
}} }}
@ -114,9 +121,9 @@
machine=model machine=model
showEngineUrl=showEngineUrl showEngineUrl=showEngineUrl
}} }}
{{/accordion-list}}
{{top-errors errors=errors}} {{top-errors errors=errors}}
{{save-cancel save="save" cancel="cancel" editing=editing}} {{save-cancel save="save" cancel="cancel" editing=editing}}
{{/if}} {{/if}}
{{/accordion-list}}
</section> </section>

View File

@ -23,7 +23,7 @@ const stringsToParams = (params, str) => {
} }
return params; return params;
} };
const paramsToStrings = (strs, param) => { const paramsToStrings = (strs, param) => {
if (param.value && param.key) { if (param.value && param.key) {
@ -31,27 +31,35 @@ const paramsToStrings = (strs, param) => {
} }
return strs; return strs;
} };
const initialVAppOptions = { const initialVAppOptions = {
vappIpprotocol: '', vappIpprotocol: '',
vappIpallocationpolicy: '', vappIpallocationpolicy: '',
vappTransport: '', vappTransport: '',
vappProperty: [] vappProperty: []
} };
const getDefaultVappOptions = (networks) => ({ const getDefaultVappOptions = (networks) => {
return {
vappIpprotocol: 'IPv4', vappIpprotocol: 'IPv4',
vappIpallocationpolicy: 'fixedAllocated', vappIpallocationpolicy: 'fixedAllocated',
vappTransport: 'com.vmware.guestInfo', vappTransport: 'com.vmware.guestInfo',
vappProperty: networksToVappProperties(networks) vappProperty: networksToVappProperties(networks)
}) };
const networksToVappProperties = (networks) => networks.length === 0 };
const networksToVappProperties = (networks) => {
return networks.length === 0
? [] ? []
: networks.reduce(networkToVappProperties, [ : networks.reduce(networkToVappProperties, [
`guestinfo.dns.servers=\${ dns:${ networks[0] } }`, `guestinfo.dns.servers=\${ dns:${ networks[0] } }`,
`guestinfo.dns.domains=\${ searchPath:${ networks[0] } }` `guestinfo.dns.domains=\${ searchPath:${ networks[0] } }`
]) ]);
}
const networkToVappProperties = (props, network, i) => { const networkToVappProperties = (props, network, i) => {
const n = i.toString(); const n = i.toString();
@ -62,7 +70,7 @@ const networkToVappProperties = (props, network, i) => {
); );
return props; return props;
} };
const getInitialVappMode = (c) => { const getInitialVappMode = (c) => {
const vappProperty = c.vappProperty || [] const vappProperty = c.vappProperty || []
@ -89,7 +97,7 @@ const getInitialVappMode = (c) => {
} }
return VAPP_MODE_MANUAL; return VAPP_MODE_MANUAL;
} };
export default Component.extend(NodeDriver, { export default Component.extend(NodeDriver, {
settings: service(), settings: service(),
@ -103,6 +111,7 @@ export default Component.extend(NodeDriver, {
vappMode: VAPP_MODE_DISABLED, vappMode: VAPP_MODE_DISABLED,
config: alias(`model.${ CONFIG }`), config: alias(`model.${ CONFIG }`),
init() { init() {
this._super(...arguments); this._super(...arguments);
this.initKeyValueParams('config.cfgparam', 'initParamArray'); this.initKeyValueParams('config.cfgparam', 'initParamArray');
@ -116,6 +125,9 @@ export default Component.extend(NodeDriver, {
}, },
vappPropertyChanged(array) { vappPropertyChanged(array) {
this.updateKeyValueParams('config.vappProperty', array); this.updateKeyValueParams('config.vappProperty', array);
},
finishAndSelectCloudCredential(credential) {
set(this, 'model.cloudCredentialId', get(credential, 'id'))
} }
}, },

View File

@ -8,29 +8,18 @@
expand=(action expandFn) expand=(action expandFn)
expandOnInit=true expandOnInit=true
}} }}
<div class="row">
<div class="col span-6">
<label class="acc-label">{{t 'nodeDriver.vmwarevsphere.vcenter.label'}}{{field-required}}</label>
{{input type="text" class="form-control" value=config.vcenter placeholder=(t 'nodeDriver.vmwarevsphere.vcenter.placeholder')}}
</div>
<div class="col span-6">
<label class="acc-label">{{t 'nodeDriver.vmwarevsphere.vcenterPort.label'}}{{field-required}}</label>
{{input-integer min=1 max=65535 class="form-control" value=config.vcenterPort}}
</div>
</div>
<div class="row"> {{form-auth-cloud-credential
<div class="col span-6"> driverName=driverName
<label class="acc-label">{{t 'nodeDriver.vmwarevsphere.username.label'}}{{field-required}}</label> errors=errros
{{input type="text" value=config.username classNames="form-control"}} primaryResource=model
</div> cloudCredentials=cloudCredentials
finishAndSelectCloudCredential=(action "finishAndSelectCloudCredential")
progressStep=(action "finishAndSelectCloudCredential")
cancel=(action "cancel")
hideSave=true
}}
<div class="col span-6">
<label class="acc-label">{{t 'nodeDriver.vmwarevsphere.password.label'}}{{field-required}}</label>
{{input type="password" value=config.password classNames="form-control"}}
</div>
</div>
<p class="help-block">{{t 'nodeDriver.vmwarevsphere.access.help'}}</p>
{{/accordion-list-item}} {{/accordion-list-item}}
{{#accordion-list-item {{#accordion-list-item

View File

@ -1,12 +1,14 @@
import { hash } from 'rsvp'; import { hash } from 'rsvp';
import Route from '@ember/routing/route'; import Route from '@ember/routing/route';
import { inject as service } from '@ember/service'; import { inject as service } from '@ember/service';
import { get } from '@ember/object';
export default Route.extend({ export default Route.extend({
globalStore: service(), globalStore: service(),
model() { model() {
return hash({ nodeTemplates: get(this, 'globalStore').findAll('nodeTemplate') }); return hash({
nodeTemplates: this.globalStore.findAll('nodeTemplate'),
cloudCredential: this.globalStore.findAll('cloudcredential'),
});
}, },
}); });

View File

@ -0,0 +1,29 @@
import Component from '@ember/component';
import layout from './template';
import { get, set } from '@ember/object';
export default Component.extend({
layout,
showAddCloudCredential: null,
errors: null,
hideSave: false,
createLabel: 'saveCancel.create',
savingLabel: 'generic.loading',
actions: {
doneSavingCloudCredential(cred) {
if (cred) {
get(this, 'finishAndSelectCloudCredential')(cred)
set(this, 'showAddCloudCredential', false);
}
},
addCloudCredential() {
set(this, 'showAddCloudCredential', true);
},
cancleNewCloudCredential() {
set(this, 'showAddCloudCredential', false);
},
},
});

View File

@ -0,0 +1,70 @@
<div class="row">
<div class="col span-6">
<label class="acc-label" for="cloud-credential-selection">
{{t "formAuthCloudCredential.label"}}
</label>
<div>
<select
id="cloud-credential-selection"
class="form-control inline-block"
onchange={{action (mut primaryResource.cloudCredentialId) value="target.value"}}
disabled={{if showAddCloudCredential true}}
style="max-width: 90%;"
>
{{#unless (not-eq primaryResource.cloudCredentialId primaryResource.cloudCredentialId)}}
<option
value=
selected=true
>
{{t "formAuthCloudCredential.selectCreds.prompt"}}
</option>
{{/unless}}
{{#each cloudCredentials as |choice|}}
<option
value={{choice.id}}
selected={{eq primaryResource.cloudCredentialId choice.id}}
>
{{choice.displayName}}
</option>
{{/each}}
</select>
<button
{{action "addCloudCredential"}}
class="btn btn-sm bg-primary inline-block"
style="margin-top: -5px;"
>
<i class="icon icon-plus"></i>
</button>
</div>
</div>
</div>
<hr/>
{{#if showAddCloudCredential}}
<div class="row">
{{cru-cloud-credential
driverName=driverName
errors=errors
disableHeader=true
doneSavingCloudCredential=(action "doneSavingCloudCredential")
cancelAdd=(action "cancleNewCloudCredential")
}}
</div>
<hr/>
{{else}}
{{#unless hideSave}}
{{save-cancel
saveDisabled=(unless primaryResource.cloudCredentialId true)
save=progressStep
cancel=cancel
createLabel=createLabel
savingLabel=savingLabel
}}
{{/unless}}
{{/if}}

View File

@ -4,20 +4,11 @@ import Mixin from '@ember/object/mixin';
import C from 'ui/utils/constants'; import C from 'ui/utils/constants';
export default Mixin.create({ export default Mixin.create({
classNames: ['modal-container'/* , 'alert' */], classNames: ['modal-container'],
modalService: service('modal'), modalService: service('modal'),
modalOpts: alias('modalService.modalOpts'), modalOpts: alias('modalService.modalOpts'),
// Focus does not want to focus on modal el here, dont know why but
// esc wont work if a modal doesnt have a focused element
// init() {
// this._super(...arguments);
// Ember.run.scheduleOnce('afterRender', ()=> {
// console.log('Focused: ', this.$());
// this.$().focus();
// });
// },
//
keyUp(e) { keyUp(e) {
if (e.which === C.KEY.ESCAPE && this.escToClose()) { if (e.which === C.KEY.ESCAPE && this.escToClose()) {
this.get('modalService').toggleModal(); this.get('modalService').toggleModal();

View File

@ -5,7 +5,7 @@ import Mixin from '@ember/object/mixin';
import NewOrEdit from 'shared/mixins/new-or-edit'; import NewOrEdit from 'shared/mixins/new-or-edit';
import ManageLabels from 'shared/mixins/manage-labels'; import ManageLabels from 'shared/mixins/manage-labels';
import { addAction } from 'ui/utils/add-view-action'; import { addAction } from 'ui/utils/add-view-action';
import { get, set, computed } from '@ember/object'; import { get, set, computed, setProperties } from '@ember/object';
import { ucFirst } from 'shared/utils/util'; import { ucFirst } from 'shared/utils/util';
import C from 'ui/utils/constants'; import C from 'ui/utils/constants';
@ -68,11 +68,13 @@ export default Mixin.create(NewOrEdit, ManageLabels, {
driverName: null, driverName: null,
showEngineUrl: true, // On some drivers this isn't configurable showEngineUrl: true, // On some drivers this isn't configurable
model: null, model: null,
labelResource: alias('model'), labelResource: alias('model'),
actions: { actions: {
finishAndSelectCloudCredential() {},
addLabel: addAction('addLabel', '.key'), addLabel: addAction('addLabel', '.key'),
setLabels(labels) { setLabels(labels) {
@ -94,13 +96,7 @@ export default Mixin.create(NewOrEdit, ManageLabels, {
this._super(...arguments); this._super(...arguments);
if ( !get(this, 'editing') && typeof get(this, 'bootstrap') === 'function') { if ( !get(this, 'editing') && typeof get(this, 'bootstrap') === 'function') {
if ( get(this, 'showEngineUrl') ) { this.initEngineUrl();
set(this, 'model.engineInstallURL', get(this, `settings.${ C.SETTING.ENGINE_URL }`) || '');
set(this, 'model.engineRegistryMirror', []);
} else {
set(this, 'model.engineInstallURL', null);
set(this, 'model.engineRegistryMirror', []);
}
this.bootstrap(); this.bootstrap();
} }
@ -111,6 +107,37 @@ export default Mixin.create(NewOrEdit, ManageLabels, {
// Populate the appropriate *Config field with defaults for your driver // Populate the appropriate *Config field with defaults for your driver
}, },
cloudCredentials: computed('model.cloudCredentialId', 'driverName', function() {
const { driverName } = this;
return this.globalStore.all('cloudcredential').filter((cc) => {
switch (driverName) {
case 'digitalocean':
if (get(cc, 'isDO')) {
return cc;
}
break;
case 'amazonec2':
if (get(cc, 'isAmazon')) {
return cc;
}
break;
case 'azure':
if (get(cc, 'isAzure')) {
return cc;
}
break;
case 'vmwarevsphere':
if (get(cc, 'isVMware')) {
return cc;
}
break;
default:
return;
}
});
}),
driverOptionsTitle: computed('driverName', 'intl.locale', function() { driverOptionsTitle: computed('driverName', 'intl.locale', function() {
const intl = get(this, 'intl'); const intl = get(this, 'intl');
const driver = get(this, 'driverName'); const driver = get(this, 'driverName');
@ -131,6 +158,20 @@ export default Mixin.create(NewOrEdit, ManageLabels, {
return intl.t('nodeDriver.templateOptions', { appName }); return intl.t('nodeDriver.templateOptions', { appName });
}), }),
initEngineUrl() {
let engineInstallURL = null;
let engineRegistryMirror = [];
if ( get(this, 'showEngineUrl') ) {
engineInstallURL = get(this, `settings.${ C.SETTING.ENGINE_URL }`) || '';
}
setProperties(this, {
'model.engineInstallURL': engineInstallURL,
'model.engineRegistryMirror': engineRegistryMirror,
});
},
didInsertElement() { didInsertElement() {
this._super(); this._super();

View File

@ -0,0 +1 @@
export { default } from 'shared/components/form-auth-cloud-credential/component';

View File

@ -643,6 +643,13 @@ catalogPage:
notCompatible: Not Compatible notCompatible: Not Compatible
alreadyDeployed: Already Deployed alreadyDeployed: Already Deployed
cloudKeysPage:
addKey: Add Cloud Key
index:
table:
noData: There are no cloud keys yet
noMatch: No cloud keys match the current search
clusterCatalogPage: clusterCatalogPage:
header: Catalogs header: Catalogs
@ -4690,6 +4697,13 @@ formAnnotations:
errors: errors:
invalidJSON: Annotation JSON format is invalid. invalidJSON: Annotation JSON format is invalid.
topLevelValueInvalid: Annotation JSON top-level value must be an object. topLevelValueInvalid: Annotation JSON top-level value must be an object.
formAuthCloudCredential:
label: Cloud Credentials
selectCreds:
prompt: Select a cloud credential
formJobConfig: formJobConfig:
title: Job Configuration title: Job Configuration
detail: Configure the desired behavior of jobs. detail: Configure the desired behavior of jobs.
@ -5182,6 +5196,35 @@ modalAddPayment:
euro: "Euro (€)" euro: "Euro (€)"
dollar: "US Dollar ($)" dollar: "US Dollar ($)"
modalAddCloudKey:
header: Add Cloud Credential
type: Cloud Credential Type
typeSelect:
prompt: Choose a cloud credential provider type
amazonec2:
accessKey:
label: Access Key
placeholder: Your AWS access key
secretKey:
label: Secret Key
placeholder: Your AWS secret key
azure:
clientId:
label: Client ID
placeholder: Your Client ID
clientSecret:
label: Client Secret
placeholder: Your Client Secret
digitalocean:
accessToken:
label: Access Token
placeholder: Your DigitalOcean API access token
help: |
Paste in a Personal Access Token from the DigitalOcean
<a href="https://cloud.digitalocean.com/settings/api/tokens" target="_blank" rel="nofollow noreferrer noopener">Applications & API</a> screen
vmwarevsphere:
password:
label: Password
modalContainerStop: modalContainerStop:
header: "Are you sure you want to stop" header: "Are you sure you want to stop"
@ -5190,6 +5233,7 @@ modalContainerStop:
label: Timeout label: Timeout
button: Stop button: Stop
modalConfirmDeactivate: modalConfirmDeactivate:
header: Are you sure you want to header: Are you sure you want to
protip: "ProTip: Hold the {key} key while clicking {isServiceButton} to bypass this confirmation." protip: "ProTip: Hold the {key} key while clicking {isServiceButton} to bypass this confirmation."
@ -6735,6 +6779,7 @@ nav:
globalDnsProviders: Providers globalDnsProviders: Providers
multiClusterApps: Multi-Cluster Apps multiClusterApps: Multi-Cluster Apps
security: security:
cloudKeys: Cloud Keys
tab: Security tab: Security
roles: Roles roles: Roles
members: Members members: Members