mirror of https://github.com/rancher/ui.git
Nodepool objects, cluster edit
This commit is contained in:
parent
583bafd6cf
commit
2cfe986d22
|
|
@ -0,0 +1,29 @@
|
||||||
|
import { get, set } from '@ember/object';
|
||||||
|
import { inject as service } from '@ember/service';
|
||||||
|
import Route from '@ember/routing/route';
|
||||||
|
import { hash/* , all */ } from 'rsvp';
|
||||||
|
|
||||||
|
export default Route.extend({
|
||||||
|
access: service(),
|
||||||
|
globalStore: service(),
|
||||||
|
|
||||||
|
model(params) {
|
||||||
|
let globalStore = this.get('globalStore');
|
||||||
|
|
||||||
|
return hash({
|
||||||
|
cluster: globalStore.find('cluster', params.cluster_id),
|
||||||
|
nodeTemplates: globalStore.findAll('nodeTemplate'),
|
||||||
|
nodeDrivers: globalStore.findAll('nodeDriver'),
|
||||||
|
psps: globalStore.findAll('podSecurityPolicyTemplate'),
|
||||||
|
roleTemplates: globalStore.findAll('roleTemplate'),
|
||||||
|
users: globalStore.findAll('user'),
|
||||||
|
clusterRoleTemplateBinding: globalStore.findAll('clusterRoleTemplateBinding'),
|
||||||
|
me: get(this, 'access.me'),
|
||||||
|
});
|
||||||
|
},
|
||||||
|
|
||||||
|
setupController(controller/*, model*/) {
|
||||||
|
this._super(...arguments);
|
||||||
|
set(controller, 'step', 1);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
@ -0,0 +1,6 @@
|
||||||
|
{{cru-cluster
|
||||||
|
model=model
|
||||||
|
initialProvider=model.cluster.provider
|
||||||
|
mode="edit"
|
||||||
|
close=(action "close")
|
||||||
|
}}
|
||||||
|
|
@ -17,13 +17,14 @@ export default Resource.extend(ResourceUsage, {
|
||||||
namespaces: hasMany('id', 'namespace', 'clusterId'),
|
namespaces: hasMany('id', 'namespace', 'clusterId'),
|
||||||
projects: hasMany('id', 'project', 'clusterId'),
|
projects: hasMany('id', 'project', 'clusterId'),
|
||||||
nodes: hasMany('id', 'node', 'clusterId'),
|
nodes: hasMany('id', 'node', 'clusterId'),
|
||||||
|
nodePools: hasMany('id', 'nodePool', 'clusterId'),
|
||||||
machines: alias('nodes'),
|
machines: alias('nodes'),
|
||||||
clusterRoleTemplateBindings: hasMany('id', 'clusterRoleTemplateBinding', 'clusterId'),
|
clusterRoleTemplateBindings: hasMany('id', 'clusterRoleTemplateBinding', 'clusterId'),
|
||||||
roleTemplateBindings: alias('clusterRoleTemplateBindings'),
|
roleTemplateBindings: alias('clusterRoleTemplateBindings'),
|
||||||
|
|
||||||
actions: {
|
actions: {
|
||||||
edit() {
|
edit() {
|
||||||
this.get('router').transitionTo('global-admin.clusters.detail.edit', this.get('id'));
|
this.get('router').transitionTo('authenticated.cluster.edit', this.get('id'));
|
||||||
},
|
},
|
||||||
|
|
||||||
scaleDownPool(uuid) {
|
scaleDownPool(uuid) {
|
||||||
|
|
@ -55,6 +56,31 @@ export default Resource.extend(ResourceUsage, {
|
||||||
return null;
|
return null;
|
||||||
}),
|
}),
|
||||||
|
|
||||||
|
provider: computed('configName','nodePools.@each.nodeTemplateId', function() {
|
||||||
|
const intl = get(this, 'intl');
|
||||||
|
const pools = get(this,'nodePools')||[];
|
||||||
|
const firstTemplate = get(pools,'firstObject.nodeTemplate');
|
||||||
|
|
||||||
|
switch ( get(this,'configName') ) {
|
||||||
|
case 'azureKubernetesServiceConfig':
|
||||||
|
return 'azureaks';
|
||||||
|
case 'googleKubernetesEngineConfig':
|
||||||
|
return 'googlegke';
|
||||||
|
case 'rancherKubernetesEngineConfig':
|
||||||
|
if ( !!pools ) {
|
||||||
|
if ( firstTemplate ) {
|
||||||
|
return get(firstTemplate, 'driver');
|
||||||
|
} else {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
return 'custom';
|
||||||
|
}
|
||||||
|
default:
|
||||||
|
return 'import';
|
||||||
|
}
|
||||||
|
}),
|
||||||
|
|
||||||
displayProvider: computed('configName','nodePools.@each.nodeTemplateId', function() {
|
displayProvider: computed('configName','nodePools.@each.nodeTemplateId', function() {
|
||||||
const intl = get(this, 'intl');
|
const intl = get(this, 'intl');
|
||||||
const pools = get(this,'nodePools')||[];
|
const pools = get(this,'nodePools')||[];
|
||||||
|
|
|
||||||
|
|
@ -75,7 +75,7 @@
|
||||||
justify-content: center;
|
justify-content: center;
|
||||||
|
|
||||||
& > .nav-box-item {
|
& > .nav-box-item {
|
||||||
min-height : 150px;
|
min-height : 120px;
|
||||||
flex : 0 1 auto;
|
flex : 0 1 auto;
|
||||||
position : relative;
|
position : relative;
|
||||||
outline : 0;
|
outline : 0;
|
||||||
|
|
|
||||||
|
|
@ -1,104 +1,15 @@
|
||||||
import Controller from '@ember/controller';
|
import Controller from '@ember/controller';
|
||||||
import { get, set, computed } from '@ember/object';
|
|
||||||
import { inject as service } from '@ember/service';
|
|
||||||
import ViewNewEdit from 'shared/mixins/view-new-edit';
|
|
||||||
import ChildHook from 'shared/mixins/child-hook';
|
|
||||||
import { alias } from '@ember/object/computed';
|
import { alias } from '@ember/object/computed';
|
||||||
|
|
||||||
const MEMBER_CONFIG = {
|
export default Controller.extend({
|
||||||
type: 'clusterRoleTemplateBinding',
|
|
||||||
};
|
|
||||||
|
|
||||||
export default Controller.extend(ViewNewEdit, ChildHook, {
|
|
||||||
globalStore: service(),
|
|
||||||
intl: service(),
|
|
||||||
|
|
||||||
cluster: alias('model.cluster'),
|
cluster: alias('model.cluster'),
|
||||||
primaryResource: alias('model.cluster'),
|
|
||||||
|
|
||||||
step: 1,
|
|
||||||
provider: 'googlegke',
|
provider: 'googlegke',
|
||||||
memberConfig: MEMBER_CONFIG,
|
|
||||||
|
|
||||||
queryParams: ['provider'],
|
queryParams: ['provider'],
|
||||||
|
|
||||||
actions: {
|
actions: {
|
||||||
clickNext() {
|
|
||||||
this.$('BUTTON[type="submit"]').click();
|
|
||||||
},
|
|
||||||
|
|
||||||
close() {
|
close() {
|
||||||
this.send('goToPrevious', 'global-admin.clusters.index');
|
this.send('goToPrevious', 'global-admin.clusters.index');
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
|
||||||
providerChoices: computed('nodeDrivers.[]','intl.locale', function() {
|
|
||||||
const intl = get(this, 'intl');
|
|
||||||
|
|
||||||
const out = [
|
|
||||||
{name: 'googlegke', driver: 'googlegke'},
|
|
||||||
// {name: 'amazoneks', driver: 'amazoneks'},
|
|
||||||
{name: 'azureaks', driver: 'azureaks'},
|
|
||||||
];
|
|
||||||
|
|
||||||
get(this, 'model.nodeDrivers').filterBy('active',true).sortBy('name').forEach((driver) => {
|
|
||||||
const name = get(driver, 'name');
|
|
||||||
const hasUi = get(driver, 'hasUi');
|
|
||||||
|
|
||||||
out.push({
|
|
||||||
name: name,
|
|
||||||
driver: 'rke',
|
|
||||||
nodeComponent: hasUi ? name : 'generic',
|
|
||||||
nodeWhich: name,
|
|
||||||
});
|
|
||||||
}),
|
|
||||||
|
|
||||||
out.push({name: 'custom', driver: 'rke', nodeWhich: 'custom'});
|
|
||||||
out.push({name: 'import', driver: 'import'});
|
|
||||||
|
|
||||||
out.forEach((driver) => {
|
|
||||||
const key = `clusterNew.${driver.name}.label`;
|
|
||||||
if ( !get(driver,'displayName') && intl.exists(key) ) {
|
|
||||||
set(driver, 'displayName', intl.t(key));
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
return out;
|
|
||||||
}),
|
|
||||||
|
|
||||||
driverInfo: computed('provider', function() {
|
|
||||||
const name = get(this, 'provider');
|
|
||||||
const choices = get(this, 'providerChoices');
|
|
||||||
const entry = choices.findBy('name', name);
|
|
||||||
if ( entry ) {
|
|
||||||
return {
|
|
||||||
name: entry.name,
|
|
||||||
driverComponent: `cluster-driver/driver-${entry.driver}`,
|
|
||||||
nodeComponent: `node-driver/driver-${entry.nodeComponent}`,
|
|
||||||
nodeWhich: entry.nodeWhich,
|
|
||||||
};
|
|
||||||
}
|
|
||||||
}),
|
|
||||||
|
|
||||||
willSave() {
|
|
||||||
const cluster = get(this, 'cluster');
|
|
||||||
const field = get(this, 'configField');
|
|
||||||
cluster.clearProvidersExcept(field);
|
|
||||||
return this._super(...arguments);
|
|
||||||
},
|
|
||||||
|
|
||||||
didSave() {
|
|
||||||
const originalCluster = get(this, 'cluster');
|
|
||||||
return originalCluster.waitForCondition('BackingNamespaceCreated').then(() => {
|
|
||||||
return this.applyHooks().then(() => {
|
|
||||||
const clone = originalCluster.clone();
|
|
||||||
set(this, 'cluster', clone);
|
|
||||||
return clone;
|
|
||||||
});
|
|
||||||
});
|
|
||||||
},
|
|
||||||
|
|
||||||
doneSaving() {
|
|
||||||
set(this, 'step', 2);
|
|
||||||
},
|
|
||||||
});
|
});
|
||||||
|
|
|
||||||
|
|
@ -1,68 +1,6 @@
|
||||||
<section class="header">
|
{{cru-cluster
|
||||||
<h1>{{t (if (eq step 1) 'clustersPage.newCluster' 'clustersPage.newClusterName') name=cluster.displayName}}</h1>
|
model=model
|
||||||
</section>
|
provider=provider
|
||||||
|
mode="new"
|
||||||
{{#if (eq step 1)}}
|
close=(action "close")
|
||||||
<form onsubmit={{action "clickNext"}}>
|
}}
|
||||||
<div class="row">
|
|
||||||
{{form-name-description
|
|
||||||
model=cluster
|
|
||||||
nameRequired=true
|
|
||||||
nameLabel='clusterNew.name.label'
|
|
||||||
namePlaceholder='clusterNew.name.placeholder'
|
|
||||||
descriptionPlaceholder='clusterNew.description.placeholder'
|
|
||||||
}}
|
|
||||||
</div>
|
|
||||||
|
|
||||||
{{#accordion-list showExpandAll=false as |al expandFn|}}
|
|
||||||
{{#accordion-list-item
|
|
||||||
title=(t 'clusterNew.members.label')
|
|
||||||
detail=(t 'clusterNew.members.detail')
|
|
||||||
showExpand=false
|
|
||||||
expandOnInit=true
|
|
||||||
expandAll=al.expandAll
|
|
||||||
expand=(action expandFn)
|
|
||||||
}}
|
|
||||||
{{form-members
|
|
||||||
editing=true
|
|
||||||
memberConfig=memberConfig
|
|
||||||
primaryResource=cluster
|
|
||||||
creator=model.me
|
|
||||||
roles=model.roleTemplates
|
|
||||||
users=model.users
|
|
||||||
type="cluster"
|
|
||||||
registerHook=(action "registerHook")
|
|
||||||
}}
|
|
||||||
{{/accordion-list-item}}
|
|
||||||
|
|
||||||
{{#accordion-list-item
|
|
||||||
title=(t 'clusterNew.config.label')
|
|
||||||
detail=(t 'clusterNew.config.detail')
|
|
||||||
showExpand=false
|
|
||||||
expandOnInit=true
|
|
||||||
expandAll=al.expandAll
|
|
||||||
expand=(action expandFn)
|
|
||||||
}}
|
|
||||||
<div class="row nav nav-boxes checked-active">
|
|
||||||
{{#each providerChoices as |choice|}}
|
|
||||||
{{#link-to (query-params provider=choice.name) class=(concat "col span-2 nav-box-item driver machine-driver " choice.name)}}
|
|
||||||
<p class="sr-only">{{choice.displayName}}</p>
|
|
||||||
{{/link-to}}
|
|
||||||
{{/each}}
|
|
||||||
</div>
|
|
||||||
{{/accordion-list-item}}
|
|
||||||
{{/accordion-list}}
|
|
||||||
|
|
||||||
{{top-errors errors=errors}}
|
|
||||||
{{save-cancel createLabel='saveCancel.next' save='save' cancel='close'}}
|
|
||||||
</form>
|
|
||||||
{{else}}
|
|
||||||
{{component driverInfo.driverComponent
|
|
||||||
editing=false
|
|
||||||
nodeComponent=driverInfo.nodeComponent
|
|
||||||
nodeWhich=driverInfo.nodeWhich
|
|
||||||
model=model
|
|
||||||
close=(action 'close')
|
|
||||||
}}
|
|
||||||
{{/if}}
|
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -136,5 +136,5 @@
|
||||||
{{/accordion-list}}
|
{{/accordion-list}}
|
||||||
|
|
||||||
{{top-errors errors=errors}}
|
{{top-errors errors=errors}}
|
||||||
{{save-cancel save="save" cancel=close}}
|
{{save-cancel save="driverSave" cancel=close}}
|
||||||
{{/if}}
|
{{/if}}
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,6 @@
|
||||||
import Component from '@ember/component'
|
import Component from '@ember/component'
|
||||||
import ClusterDriver from 'global-admin/mixins/cluster-driver';
|
import ClusterDriver from 'global-admin/mixins/cluster-driver';
|
||||||
|
import { resolve } from 'rsvp';
|
||||||
import { equal } from '@ember/object/computed';
|
import { equal } from '@ember/object/computed';
|
||||||
import { get, set, computed, observer } from '@ember/object';
|
import { get, set, computed, observer } from '@ember/object';
|
||||||
import { satisfies } from 'shared/utils/parse-version';
|
import { satisfies } from 'shared/utils/parse-version';
|
||||||
|
|
@ -62,6 +63,8 @@ export default Component.extend(ClusterDriver, {
|
||||||
configField: 'rancherKubernetesEngineConfig',
|
configField: 'rancherKubernetesEngineConfig',
|
||||||
headers,
|
headers,
|
||||||
|
|
||||||
|
model: null,
|
||||||
|
|
||||||
initialVersion: null,
|
initialVersion: null,
|
||||||
|
|
||||||
networkChoices: [
|
networkChoices: [
|
||||||
|
|
@ -131,31 +134,18 @@ export default Component.extend(ClusterDriver, {
|
||||||
this.set('labels', out);
|
this.set('labels', out);
|
||||||
},
|
},
|
||||||
|
|
||||||
addPool() {
|
driverSave(cb) {
|
||||||
let nodePools = get(this, 'cluster.nodePools');
|
cb = cb || function() {};
|
||||||
if ( !nodePools ) {
|
|
||||||
nodePools = [];
|
|
||||||
set(this, 'cluster.nodePools', nodePools);
|
|
||||||
}
|
|
||||||
|
|
||||||
let templateId = null;
|
resolve(this.willSave()).then((ok) => {
|
||||||
const lastNode = nodePools[nodePools.length-1];
|
if ( !ok ) {
|
||||||
if ( lastNode ) {
|
// Validation or something else said not to save
|
||||||
templateId = get(lastNode, 'nodeTemplateId');
|
cb(false);
|
||||||
}
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
nodePools.pushObject(get(this, 'globalStore').createRecord({
|
this.sendAction('save', cb);
|
||||||
type: 'nodePool',
|
});
|
||||||
nodeTemplateId: templateId
|
|
||||||
}));
|
|
||||||
},
|
|
||||||
|
|
||||||
addNodeTemplate(node) {
|
|
||||||
get(this,'modalService').toggleModal('modal-edit-node-template', {nodeTemplate: null, driver: get(this, 'nodeWhich'), onAdd: onAdd});
|
|
||||||
|
|
||||||
function onAdd(nodeTemplate) {
|
|
||||||
set(node, 'nodeTemplateId', get(nodeTemplate, 'id'));
|
|
||||||
}
|
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
|
||||||
|
|
@ -177,7 +167,7 @@ export default Component.extend(ClusterDriver, {
|
||||||
const intl = get(this, 'intl');
|
const intl = get(this, 'intl');
|
||||||
|
|
||||||
this._super(...arguments);
|
this._super(...arguments);
|
||||||
let errors = this.get('errors')||[];
|
const errors = get(this,'errors')||[];
|
||||||
|
|
||||||
if ( !get(this, 'isCustom') ) {
|
if ( !get(this, 'isCustom') ) {
|
||||||
if ( !get(this, 'etcdOk') ) {
|
if ( !get(this, 'etcdOk') ) {
|
||||||
|
|
@ -197,7 +187,6 @@ export default Component.extend(ClusterDriver, {
|
||||||
return errors.length === 0;
|
return errors.length === 0;
|
||||||
},
|
},
|
||||||
|
|
||||||
|
|
||||||
doneSaving() {
|
doneSaving() {
|
||||||
if ( get(this, 'isCustom') ){
|
if ( get(this, 'isCustom') ){
|
||||||
const cluster = get(this,'cluster');
|
const cluster = get(this,'cluster');
|
||||||
|
|
@ -210,41 +199,6 @@ export default Component.extend(ClusterDriver, {
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
filteredNodeTemplates: computed('nodeWhich','model.nodeTemplates.@each.{state,driver}', function() {
|
|
||||||
const driver = get(this, 'nodeWhich');
|
|
||||||
let templates = get(this, 'model.nodeTemplates').filterBy('state','active').filterBy('driver', driver);
|
|
||||||
return templates;
|
|
||||||
}),
|
|
||||||
|
|
||||||
_nodeCountFor(role) {
|
|
||||||
let count = 0;
|
|
||||||
(get(this, 'cluster.nodePools')||[]).filterBy(role,true).forEach((pool) => {
|
|
||||||
let more = get(pool, 'quantity');
|
|
||||||
if ( more ) {
|
|
||||||
more = parseInt(more, 10);
|
|
||||||
}
|
|
||||||
|
|
||||||
count += more;
|
|
||||||
});
|
|
||||||
|
|
||||||
return count;
|
|
||||||
},
|
|
||||||
|
|
||||||
etcdOk: computed('cluster.nodePools.@each.{quantity,etcd}', function() {
|
|
||||||
let count = this._nodeCountFor('etcd');
|
|
||||||
return count === 1 || count === 3 || count === 5
|
|
||||||
}),
|
|
||||||
|
|
||||||
controlPlaneOk: computed('cluster.nodePools.@each.{quantity,controlPlane}', function() {
|
|
||||||
let count = this._nodeCountFor('controlPlane');
|
|
||||||
return count >= 1;
|
|
||||||
}),
|
|
||||||
|
|
||||||
workerOk: computed('cluster.nodePools.@each.{quantity,worker}', function() {
|
|
||||||
let count = this._nodeCountFor('worker');
|
|
||||||
return count >= 1;
|
|
||||||
}),
|
|
||||||
|
|
||||||
versionChanged: observer('config.kubernetesVersion','versionChoices.[]', function() {
|
versionChanged: observer('config.kubernetesVersion','versionChoices.[]', function() {
|
||||||
const versions = get(this, 'versionChoices')||[];
|
const versions = get(this, 'versionChoices')||[];
|
||||||
const current = get(this, 'config.kubernetesVersion');
|
const current = get(this, 'config.kubernetesVersion');
|
||||||
|
|
|
||||||
|
|
@ -1,100 +1,12 @@
|
||||||
{{#if (eq step 1)}}
|
{{#if (eq step 1)}}
|
||||||
{{#accordion-list showExpandAll=false as |al expandFn|}}
|
{{#accordion-list showExpandAll=false as |al expandFn|}}
|
||||||
{{#unless isCustom}}
|
{{#unless isCustom}}
|
||||||
{{#accordion-list-item
|
{{cru-node-pools
|
||||||
title=(t 'clusterNew.rke.nodes.title')
|
cluster=cluster
|
||||||
detail=(t 'clusterNew.rke.nodes.detail')
|
driver=nodeWhich
|
||||||
showExpand=false
|
nodeTemplates=model.nodeTemplates
|
||||||
expandOnInit=true
|
registerHook=(action "registerHook")
|
||||||
expandAll=al.expandAll
|
|
||||||
expand=(action expandFn)
|
|
||||||
}}
|
}}
|
||||||
{{#sortable-table
|
|
||||||
classNames="grid sortable-table"
|
|
||||||
body=cluster.nodePools
|
|
||||||
suffix=true
|
|
||||||
search=false
|
|
||||||
bulkActions=false
|
|
||||||
rowActions=false
|
|
||||||
pagingLabel="pagination.node"
|
|
||||||
headers=headers
|
|
||||||
as |sortable kind node dt|
|
|
||||||
}}
|
|
||||||
{{#if (eq kind "row")}}
|
|
||||||
<tr class="main-row">
|
|
||||||
<td data-title="{{dt.hostnamePrefix}}">
|
|
||||||
<div class="mr-20">
|
|
||||||
{{input class="input-sm" value=node.hostnamePrefix}}
|
|
||||||
</div>
|
|
||||||
</td>
|
|
||||||
<td data-title="{{dt.quantity}}">
|
|
||||||
<div class="input-group mr-20">
|
|
||||||
{{input class="input-sm" type="number" min="1" value=node.quantity}}
|
|
||||||
<span class="input-group-addon bg-default">x</span>
|
|
||||||
</div>
|
|
||||||
</td>
|
|
||||||
<td data-title="{{dt.nodeTemplate}}">
|
|
||||||
{{#if filteredNodeTemplates.length}}
|
|
||||||
<div class="input-group input-sm">
|
|
||||||
{{new-select
|
|
||||||
class="input-sm"
|
|
||||||
content=filteredNodeTemplates
|
|
||||||
prompt="clusterNew.rke.nodes.templatePrompt"
|
|
||||||
localizedPrompt=true
|
|
||||||
optionLabelPath="displayName"
|
|
||||||
optionValuePath="id"
|
|
||||||
value=node.nodeTemplateId
|
|
||||||
}}
|
|
||||||
<div class="input-group-btn bg-primary">
|
|
||||||
<button type="button" class="btn btn-sm bg-primary" {{action "addNodeTemplate" node}}><i class="icon icon-plus"></i></button>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
{{else}}
|
|
||||||
<button class="btn bg-primary" {{action "addNodeTemplate" node}}>{{t 'clusterNew.rke.nodes.addTemplate'}}</button>
|
|
||||||
{{/if}}
|
|
||||||
</td>
|
|
||||||
<td data-title="{{dt.etcd}}" class="text-center">
|
|
||||||
{{input type="checkbox" checked=node.etcd}}
|
|
||||||
</td>
|
|
||||||
<td data-title="{{dt.controlplane}}" class="text-center">
|
|
||||||
{{input type="checkbox" checked=node.controlPlane}}
|
|
||||||
</td>
|
|
||||||
<td data-title="{{dt.worker}}" class="text-center">
|
|
||||||
{{input type="checkbox" checked=node.worker}}
|
|
||||||
</td>
|
|
||||||
<td data-title="{{dt.remove}}" class="text-center">
|
|
||||||
<button class="btn bg-primary btn-sm" {{action "removeNode" node}}><i class="icon icon-minus"/></button>
|
|
||||||
</td>
|
|
||||||
</tr>
|
|
||||||
{{else if (eq kind "norows")}}
|
|
||||||
<tr><td colspan="{{sortable.fullColspan}}" class="text-center text-muted pt-40 pb-40">{{t 'nodesPage.table.noData'}}</td></tr>
|
|
||||||
{{else if (eq kind "suffix")}}
|
|
||||||
<tr class="banner bg-info suffix">
|
|
||||||
<td colspan="3" class="pl-20 text-bold">{{t 'clusterNew.rke.role.requirements.label'}}</td>
|
|
||||||
<td class="text-center {{if etcdOk "text-success" "text-error text-bold"}}">
|
|
||||||
<i class="icon {{if etcdOk "icon-success" "icon-x-circle"}}"></i>
|
|
||||||
{{t 'clusterNew.rke.role.requirements.etcd'}}
|
|
||||||
</td>
|
|
||||||
<td class="text-center {{if controlPlaneOk "text-success" "text-error text-bold"}}">
|
|
||||||
<i class="icon {{if controlPlaneOk "icon-success" "icon-x-circle"}}"></i>
|
|
||||||
{{t 'clusterNew.rke.role.requirements.controlplane'}}
|
|
||||||
</td>
|
|
||||||
<td class="text-center {{if workerOk "text-success" "text-error text-bold"}}">
|
|
||||||
<i class="icon {{if workerOk "icon-success" "icon-x-circle"}}"></i>
|
|
||||||
{{t 'clusterNew.rke.role.requirements.worker'}}
|
|
||||||
</td>
|
|
||||||
<td></td>
|
|
||||||
</tr>
|
|
||||||
{{/if}}
|
|
||||||
{{/sortable-table}}
|
|
||||||
|
|
||||||
<div class="mt-20">
|
|
||||||
<button class="btn bg-primary icon-btn p-0" {{action "addPool"}}>
|
|
||||||
<span class="darken"><i class="icon icon-plus text-small"/></span>
|
|
||||||
<span>{{t 'clusterNew.rke.nodes.add'}}</span>
|
|
||||||
</button>
|
|
||||||
</div>
|
|
||||||
{{/accordion-list-item}}
|
|
||||||
{{/unless}}
|
{{/unless}}
|
||||||
|
|
||||||
{{#accordion-list-item
|
{{#accordion-list-item
|
||||||
|
|
@ -185,7 +97,7 @@
|
||||||
{{/accordion-list}}
|
{{/accordion-list}}
|
||||||
|
|
||||||
{{top-errors errors=errors}}
|
{{top-errors errors=errors}}
|
||||||
{{save-cancel createLabel=(if isCustom 'saveCancel.next' 'saveCancel.create') save="save" cancel=cancel}}
|
{{save-cancel createLabel=(if isCustom 'saveCancel.next' 'saveCancel.create') save=(action 'driverSave') cancel=cancel}}
|
||||||
{{else}}
|
{{else}}
|
||||||
|
|
||||||
<div class="mt-20">
|
<div class="mt-20">
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,124 @@
|
||||||
|
import Component from '@ember/component';
|
||||||
|
import { get, set, computed } from '@ember/object';
|
||||||
|
import { inject as service } from '@ember/service';
|
||||||
|
import ViewNewEdit from 'shared/mixins/view-new-edit';
|
||||||
|
import ChildHook from 'shared/mixins/child-hook';
|
||||||
|
import { alias } from '@ember/object/computed';
|
||||||
|
|
||||||
|
const MEMBER_CONFIG = {
|
||||||
|
type: 'clusterRoleTemplateBinding',
|
||||||
|
};
|
||||||
|
|
||||||
|
export default Component.extend(ViewNewEdit, ChildHook, {
|
||||||
|
globalStore: service(),
|
||||||
|
intl: service(),
|
||||||
|
access: service(),
|
||||||
|
|
||||||
|
cluster: alias('model.cluster'),
|
||||||
|
primaryResource: alias('model.cluster'),
|
||||||
|
|
||||||
|
step: 1,
|
||||||
|
initialProvider: null,
|
||||||
|
memberConfig: MEMBER_CONFIG,
|
||||||
|
|
||||||
|
init() {
|
||||||
|
this._super(...arguments);
|
||||||
|
|
||||||
|
// On edit pass in initialProvider, for create just set provider directly
|
||||||
|
const initialProvider = get(this, 'initialProvider');
|
||||||
|
if ( initialProvider ) {
|
||||||
|
set(this, 'provider', initialProvider);
|
||||||
|
}
|
||||||
|
|
||||||
|
if ( get(this, 'cluster.id') && initialProvider ){
|
||||||
|
set(this,'step', 2);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
actions: {
|
||||||
|
clickNext() {
|
||||||
|
this.$('BUTTON[type="submit"]').click();
|
||||||
|
},
|
||||||
|
|
||||||
|
close() {
|
||||||
|
this.send('goToPrevious', 'global-admin.clusters.index');
|
||||||
|
},
|
||||||
|
|
||||||
|
maybeSave(cb) {
|
||||||
|
const info = get(this, 'driverInfo');
|
||||||
|
if ( info.preSave ) {
|
||||||
|
this.send('save',cb);
|
||||||
|
} else {
|
||||||
|
this.doneSaving();
|
||||||
|
}
|
||||||
|
},
|
||||||
|
},
|
||||||
|
|
||||||
|
providerChoices: computed('nodeDrivers.[]','intl.locale', function() {
|
||||||
|
const intl = get(this, 'intl');
|
||||||
|
|
||||||
|
const out = [
|
||||||
|
{name: 'googlegke', driver: 'googlegke'},
|
||||||
|
// {name: 'amazoneks', driver: 'amazoneks'},
|
||||||
|
{name: 'azureaks', driver: 'azureaks'},
|
||||||
|
];
|
||||||
|
|
||||||
|
get(this, 'model.nodeDrivers').filterBy('active',true).sortBy('name').forEach((driver) => {
|
||||||
|
const name = get(driver, 'name');
|
||||||
|
const hasUi = get(driver, 'hasUi');
|
||||||
|
|
||||||
|
out.push({
|
||||||
|
name: name,
|
||||||
|
driver: 'rke',
|
||||||
|
nodeComponent: hasUi ? name : 'generic',
|
||||||
|
nodeWhich: name,
|
||||||
|
});
|
||||||
|
}),
|
||||||
|
|
||||||
|
out.push({name: 'custom', driver: 'rke', nodeWhich: 'custom', preSave: true});
|
||||||
|
out.push({name: 'import', driver: 'import', preSave: true});
|
||||||
|
|
||||||
|
out.forEach((driver) => {
|
||||||
|
const key = `clusterNew.${driver.name}.label`;
|
||||||
|
if ( !get(driver,'displayName') && intl.exists(key) ) {
|
||||||
|
set(driver, 'displayName', intl.t(key));
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
return out;
|
||||||
|
}),
|
||||||
|
|
||||||
|
driverInfo: computed('provider', function() {
|
||||||
|
const name = get(this, 'provider');
|
||||||
|
const choices = get(this, 'providerChoices');
|
||||||
|
const entry = choices.findBy('name', name);
|
||||||
|
if ( entry ) {
|
||||||
|
return {
|
||||||
|
name: entry.name,
|
||||||
|
driverComponent: `cluster-driver/driver-${entry.driver}`,
|
||||||
|
nodeComponent: `node-driver/driver-${entry.nodeComponent}`,
|
||||||
|
nodeWhich: entry.nodeWhich,
|
||||||
|
preSave: !!entry.preSave,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}),
|
||||||
|
|
||||||
|
didSave() {
|
||||||
|
const originalCluster = get(this, 'cluster');
|
||||||
|
return originalCluster.waitForCondition('BackingNamespaceCreated').then(() => {
|
||||||
|
return this.applyHooks().then(() => {
|
||||||
|
const clone = originalCluster.clone();
|
||||||
|
set(this, 'cluster', clone);
|
||||||
|
return clone;
|
||||||
|
});
|
||||||
|
});
|
||||||
|
},
|
||||||
|
|
||||||
|
doneSaving() {
|
||||||
|
if ( get(this, 'driverInfo.preSave') ) {
|
||||||
|
set(this, 'step', 2);
|
||||||
|
} else {
|
||||||
|
this.sendAction('close');
|
||||||
|
}
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
@ -0,0 +1,71 @@
|
||||||
|
<section class="header">
|
||||||
|
<h1>
|
||||||
|
{{#if cluster.id}}
|
||||||
|
{{t 'clustersPage.editClusterName' name=cluster.displayName}}
|
||||||
|
{{else if (eq step 1)}}
|
||||||
|
{{t 'clustersPage.newCluster'}}
|
||||||
|
{{else}}
|
||||||
|
{{t 'clustersPage.newClusterName' name=cluster.displayName}}
|
||||||
|
{{/if}}
|
||||||
|
</h1>
|
||||||
|
</section>
|
||||||
|
|
||||||
|
<div class="row nav nav-boxes checked-active">
|
||||||
|
{{#each providerChoices as |choice|}}
|
||||||
|
{{#link-to (query-params provider=choice.name) class=(concat "col span-2 nav-box-item driver machine-driver " choice.name)}}
|
||||||
|
<p class="sr-only">{{choice.displayName}}</p>
|
||||||
|
{{/link-to}}
|
||||||
|
{{/each}}
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{{#if (or isEdit (eq step 1))}}
|
||||||
|
<form onsubmit={{action "clickNext"}}>
|
||||||
|
<div class="row">
|
||||||
|
{{form-name-description
|
||||||
|
model=model.cluster
|
||||||
|
nameRequired=true
|
||||||
|
nameLabel='clusterNew.name.label'
|
||||||
|
namePlaceholder='clusterNew.name.placeholder'
|
||||||
|
descriptionPlaceholder='clusterNew.description.placeholder'
|
||||||
|
}}
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{{#accordion-list showExpandAll=false as |al expandFn|}}
|
||||||
|
{{#accordion-list-item
|
||||||
|
title=(t 'clusterNew.members.label')
|
||||||
|
detail=(t 'clusterNew.members.detail')
|
||||||
|
expandAll=al.expandAll
|
||||||
|
everExpanded=true
|
||||||
|
expand=(action expandFn)
|
||||||
|
}}
|
||||||
|
{{form-members
|
||||||
|
editing=notView
|
||||||
|
memberConfig=memberConfig
|
||||||
|
primaryResource=cluster
|
||||||
|
creator=model.me
|
||||||
|
roles=model.roleTemplates
|
||||||
|
users=model.users
|
||||||
|
type="cluster"
|
||||||
|
registerHook=(action "registerHook")
|
||||||
|
}}
|
||||||
|
{{/accordion-list-item}}
|
||||||
|
{{/accordion-list}}
|
||||||
|
|
||||||
|
{{top-errors errors=errors}}
|
||||||
|
{{#if (and isNew driverInfo.preSave)}}
|
||||||
|
{{save-cancel createLabel='saveCancel.next' save='maybeSave' cancel='close'}}
|
||||||
|
{{/if}}
|
||||||
|
</form>
|
||||||
|
{{/if}}
|
||||||
|
|
||||||
|
{{#unless (and isNew driverInfo.preSave)}}
|
||||||
|
{{component driverInfo.driverComponent
|
||||||
|
mode=mode
|
||||||
|
nodeComponent=driverInfo.nodeComponent
|
||||||
|
nodeWhich=driverInfo.nodeWhich
|
||||||
|
model=model
|
||||||
|
save=(action 'save')
|
||||||
|
close=(action 'close')
|
||||||
|
registerHook=(action "registerHook")
|
||||||
|
}}
|
||||||
|
{{/unless}}
|
||||||
|
|
@ -0,0 +1,117 @@
|
||||||
|
import Component from '@ember/component';
|
||||||
|
import layout from './template';
|
||||||
|
import { computed, get, set } from '@ember/object';
|
||||||
|
import { inject as service } from '@ember/service';
|
||||||
|
import { all as PromiseAll } from 'rsvp';
|
||||||
|
|
||||||
|
export default Component.extend({
|
||||||
|
layout,
|
||||||
|
globalStore: service(),
|
||||||
|
|
||||||
|
cluster: null,
|
||||||
|
nodeTemplates: null,
|
||||||
|
driver: null, // docker-machine driver
|
||||||
|
|
||||||
|
originalPools: null,
|
||||||
|
nodePools: null,
|
||||||
|
|
||||||
|
init() {
|
||||||
|
this._super(...arguments);
|
||||||
|
const originalPools = (get(this,'cluster.nodePools')||[]).slice();
|
||||||
|
set(this, 'originalPools', originalPools);
|
||||||
|
set(this, 'nodePools', originalPools.slice());
|
||||||
|
this.sendAction('registerHook', this.savePools.bind(this), 'savePools');
|
||||||
|
},
|
||||||
|
|
||||||
|
actions: {
|
||||||
|
addPool() {
|
||||||
|
let nodePools = get(this, 'nodePools');
|
||||||
|
|
||||||
|
let templateId = null;
|
||||||
|
const lastNode = nodePools[nodePools.length-1];
|
||||||
|
if ( lastNode ) {
|
||||||
|
templateId = get(lastNode, 'nodeTemplateId');
|
||||||
|
}
|
||||||
|
|
||||||
|
nodePools.pushObject(get(this, 'globalStore').createRecord({
|
||||||
|
type: 'nodePool',
|
||||||
|
nodeTemplateId: templateId
|
||||||
|
}));
|
||||||
|
},
|
||||||
|
|
||||||
|
removePool(pool) {
|
||||||
|
get(this, 'nodePools').removeObject(pool);
|
||||||
|
},
|
||||||
|
|
||||||
|
addNodeTemplate(node) {
|
||||||
|
get(this,'modalService').toggleModal('modal-edit-node-template', {
|
||||||
|
nodeTemplate: null,
|
||||||
|
driver: get(this, 'driver'),
|
||||||
|
onAdd: function(nodeTemplate) {
|
||||||
|
set(node, 'nodeTemplateId', get(nodeTemplate, 'id'));
|
||||||
|
},
|
||||||
|
});
|
||||||
|
},
|
||||||
|
},
|
||||||
|
|
||||||
|
savePools: function() {
|
||||||
|
const nodePools = get(this, 'nodePools');
|
||||||
|
const original = get(this, 'originalPools');
|
||||||
|
|
||||||
|
const remove = [];
|
||||||
|
original.forEach((pool) => {
|
||||||
|
if ( !nodePools.includes(pool) ) {
|
||||||
|
// Remove
|
||||||
|
remove.push(pool);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
const clusterId = get(this, 'cluster.id');
|
||||||
|
nodePools.forEach((pool) => {
|
||||||
|
set(pool, 'clusterId', clusterId);
|
||||||
|
});
|
||||||
|
|
||||||
|
|
||||||
|
return PromiseAll(nodePools.map(x => x.save())).then(() => {
|
||||||
|
return PromiseAll(remove.map(x => x.delete())).then(() => {
|
||||||
|
return get(this, 'cluster');
|
||||||
|
});
|
||||||
|
});
|
||||||
|
},
|
||||||
|
|
||||||
|
filteredNodeTemplates: computed('driver','nodeTemplates.@each.{state,driver}', function() {
|
||||||
|
const driver = get(this, 'driver');
|
||||||
|
let templates = get(this, 'nodeTemplates').filterBy('state','active').filterBy('driver', driver);
|
||||||
|
return templates;
|
||||||
|
}),
|
||||||
|
|
||||||
|
_nodeCountFor(role) {
|
||||||
|
let count = 0;
|
||||||
|
(get(this, 'cluster.nodePools')||[]).filterBy(role,true).forEach((pool) => {
|
||||||
|
let more = get(pool, 'quantity');
|
||||||
|
if ( more ) {
|
||||||
|
more = parseInt(more, 10);
|
||||||
|
}
|
||||||
|
|
||||||
|
count += more;
|
||||||
|
});
|
||||||
|
|
||||||
|
return count;
|
||||||
|
},
|
||||||
|
|
||||||
|
etcdOk: computed('cluster.nodePools.@each.{quantity,etcd}', function() {
|
||||||
|
let count = this._nodeCountFor('etcd');
|
||||||
|
return count === 1 || count === 3 || count === 5
|
||||||
|
}),
|
||||||
|
|
||||||
|
controlPlaneOk: computed('cluster.nodePools.@each.{quantity,controlPlane}', function() {
|
||||||
|
let count = this._nodeCountFor('controlPlane');
|
||||||
|
return count >= 1;
|
||||||
|
}),
|
||||||
|
|
||||||
|
workerOk: computed('cluster.nodePools.@each.{quantity,worker}', function() {
|
||||||
|
let count = this._nodeCountFor('worker');
|
||||||
|
return count >= 1;
|
||||||
|
}),
|
||||||
|
|
||||||
|
});
|
||||||
|
|
@ -0,0 +1,94 @@
|
||||||
|
{{#accordion-list-item
|
||||||
|
title=(t 'clusterNew.rke.nodes.title')
|
||||||
|
detail=(t 'clusterNew.rke.nodes.detail')
|
||||||
|
showExpand=false
|
||||||
|
expandOnInit=true
|
||||||
|
expandAll=al.expandAll
|
||||||
|
expand=(action expandFn)
|
||||||
|
}}
|
||||||
|
{{#sortable-table
|
||||||
|
classNames="grid sortable-table"
|
||||||
|
body=model
|
||||||
|
suffix=true
|
||||||
|
search=false
|
||||||
|
bulkActions=false
|
||||||
|
rowActions=false
|
||||||
|
pagingLabel="pagination.nodePool"
|
||||||
|
headers=headers
|
||||||
|
as |sortable kind pool dt|
|
||||||
|
}}
|
||||||
|
{{#if (eq kind "row")}}
|
||||||
|
<tr class="main-row">
|
||||||
|
<td data-title="{{dt.hostnamePrefix}}">
|
||||||
|
<div class="mr-20">
|
||||||
|
{{input class="input-sm" value=pool.hostnamePrefix}}
|
||||||
|
</div>
|
||||||
|
</td>
|
||||||
|
<td data-title="{{dt.quantity}}">
|
||||||
|
<div class="input-group mr-20">
|
||||||
|
{{input class="input-sm" type="number" min="1" value=pool.quantity}}
|
||||||
|
<span class="input-group-addon bg-default">x</span>
|
||||||
|
</div>
|
||||||
|
</td>
|
||||||
|
<td data-title="{{dt.nodeTemplate}}">
|
||||||
|
{{#if filteredNodeTemplates.length}}
|
||||||
|
<div class="input-group input-sm">
|
||||||
|
{{new-select
|
||||||
|
class="input-sm"
|
||||||
|
content=filteredNodeTemplates
|
||||||
|
prompt="clusterNew.rke.nodes.templatePrompt"
|
||||||
|
localizedPrompt=true
|
||||||
|
optionLabelPath="displayName"
|
||||||
|
optionValuePath="id"
|
||||||
|
value=pool.nodeTemplateId
|
||||||
|
}}
|
||||||
|
<div class="input-group-btn bg-primary">
|
||||||
|
<button type="button" class="btn btn-sm bg-primary" {{action "addNodeTemplate" pool}}><i class="icon icon-plus"></i></button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
{{else}}
|
||||||
|
<button class="btn bg-primary" {{action "addNodeTemplate" pool}}>{{t 'clusterNew.rke.nodes.addTemplate'}}</button>
|
||||||
|
{{/if}}
|
||||||
|
</td>
|
||||||
|
<td data-title="{{dt.etcd}}" class="text-center">
|
||||||
|
{{input type="checkbox" checked=pool.etcd}}
|
||||||
|
</td>
|
||||||
|
<td data-title="{{dt.controlplane}}" class="text-center">
|
||||||
|
{{input type="checkbox" checked=pool.controlPlane}}
|
||||||
|
</td>
|
||||||
|
<td data-title="{{dt.worker}}" class="text-center">
|
||||||
|
{{input type="checkbox" checked=pool.worker}}
|
||||||
|
</td>
|
||||||
|
<td data-title="{{dt.remove}}" class="text-center">
|
||||||
|
<button class="btn bg-primary btn-sm" {{action "removePool" pool}}><i class="icon icon-minus"/></button>
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
{{else if (eq kind "norows")}}
|
||||||
|
<tr><td colspan="{{sortable.fullColspan}}" class="text-center text-muted pt-40 pb-40">{{t 'nodesPage.table.noData'}}</td></tr>
|
||||||
|
{{else if (eq kind "suffix")}}
|
||||||
|
<tr class="banner bg-info suffix">
|
||||||
|
<td colspan="3" class="pl-20 text-bold">{{t 'clusterNew.rke.role.requirements.label'}}</td>
|
||||||
|
<td class="text-center {{if etcdOk "text-success" "text-error text-bold"}}">
|
||||||
|
<i class="icon {{if etcdOk "icon-success" "icon-x-circle"}}"></i>
|
||||||
|
{{t 'clusterNew.rke.role.requirements.etcd'}}
|
||||||
|
</td>
|
||||||
|
<td class="text-center {{if controlPlaneOk "text-success" "text-error text-bold"}}">
|
||||||
|
<i class="icon {{if controlPlaneOk "icon-success" "icon-x-circle"}}"></i>
|
||||||
|
{{t 'clusterNew.rke.role.requirements.controlplane'}}
|
||||||
|
</td>
|
||||||
|
<td class="text-center {{if workerOk "text-success" "text-error text-bold"}}">
|
||||||
|
<i class="icon {{if workerOk "icon-success" "icon-x-circle"}}"></i>
|
||||||
|
{{t 'clusterNew.rke.role.requirements.worker'}}
|
||||||
|
</td>
|
||||||
|
<td></td>
|
||||||
|
</tr>
|
||||||
|
{{/if}}
|
||||||
|
{{/sortable-table}}
|
||||||
|
|
||||||
|
<div class="mt-20">
|
||||||
|
<button class="btn bg-primary icon-btn p-0" {{action "addPool"}}>
|
||||||
|
<span class="darken"><i class="icon icon-plus text-small"/></span>
|
||||||
|
<span>{{t 'clusterNew.rke.nodes.add'}}</span>
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
{{/accordion-list-item}}
|
||||||
|
|
@ -1,37 +1,62 @@
|
||||||
import Mixin from '@ember/object/mixin';
|
import Mixin from '@ember/object/mixin';
|
||||||
import ViewNewEdit from 'shared/mixins/view-new-edit';
|
import { get, set, computed } from '@ember/object';
|
||||||
import { get, computed } from '@ember/object';
|
|
||||||
import { alias } from '@ember/object/computed';
|
import { alias } from '@ember/object/computed';
|
||||||
|
import { resolve } from 'rsvp';
|
||||||
import { inject as service } from '@ember/service';
|
import { inject as service } from '@ember/service';
|
||||||
|
|
||||||
export default Mixin.create(ViewNewEdit, {
|
export default Mixin.create({
|
||||||
configField: '<override me>',
|
configField: '<override me>',
|
||||||
|
|
||||||
close: null, // action to finish adding
|
mode: null,
|
||||||
|
save: null, // Action to save
|
||||||
|
close: null, // Action on complete
|
||||||
|
registerHook: null,
|
||||||
|
|
||||||
globalStore: service(),
|
globalStore: service(),
|
||||||
router: service(),
|
|
||||||
|
|
||||||
cluster: alias('model.cluster'),
|
cluster: alias('model.cluster'),
|
||||||
primaryResource: alias('model.cluster'),
|
primaryResource: alias('model.cluster'),
|
||||||
errors: null,
|
errors: null,
|
||||||
|
|
||||||
init() {
|
|
||||||
this._super(...arguments);
|
|
||||||
},
|
|
||||||
|
|
||||||
actions: {
|
|
||||||
close() {
|
|
||||||
this.sendAction('close');
|
|
||||||
}
|
|
||||||
},
|
|
||||||
|
|
||||||
config: computed('configField', function() {
|
config: computed('configField', function() {
|
||||||
const field = 'cluster.' + get(this, 'configField');
|
const field = 'cluster.' + get(this, 'configField');
|
||||||
return get(this, field);
|
return get(this, field);
|
||||||
}),
|
}),
|
||||||
|
|
||||||
doneSaving() {
|
actions: {
|
||||||
this.get('router').transitionTo('global-admin.clusters.index');
|
driverSave(cb) {
|
||||||
|
resolve(this.willSave()).then((ok) => {
|
||||||
|
if ( !ok ) {
|
||||||
|
// Validation or something else said not to save
|
||||||
|
cb(false);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
this.sendAction('save', cb);
|
||||||
|
});
|
||||||
|
},
|
||||||
|
|
||||||
|
registerHook() {
|
||||||
|
const args = [].slice.call(arguments);
|
||||||
|
args.unshift('registerHook');
|
||||||
|
this.sendAction.apply(this, args);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
willSave() {
|
||||||
|
const cluster = get(this, 'cluster');
|
||||||
|
const field = get(this, 'configField');
|
||||||
|
cluster.clearProvidersExcept(field);
|
||||||
|
|
||||||
|
set(this, 'errors',null);
|
||||||
|
const ok = this.validate();
|
||||||
|
return ok;
|
||||||
|
},
|
||||||
|
|
||||||
|
validate() {
|
||||||
|
const model = get(this,'cluster');
|
||||||
|
const errors = model.validationErrors();
|
||||||
|
set(this, 'errors', errors);
|
||||||
|
return errors.length === 0;
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|
|
||||||
|
|
@ -140,6 +140,10 @@ export default Component.extend({
|
||||||
return PromiseAll(add.map(x => x.save())).then(() => {
|
return PromiseAll(add.map(x => x.save())).then(() => {
|
||||||
return PromiseAll(update.map(x => x.save())).then(() => {
|
return PromiseAll(update.map(x => x.save())).then(() => {
|
||||||
return PromiseAll(remove.map(x => x.delete())).then(() => {
|
return PromiseAll(remove.map(x => x.delete())).then(() => {
|
||||||
|
if ( this.isDestroyed || this.isDestroying ) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
set(this, 'memberArray', []);
|
set(this, 'memberArray', []);
|
||||||
return get(this, 'primaryResource');
|
return get(this, 'primaryResource');
|
||||||
});
|
});
|
||||||
|
|
|
||||||
|
|
@ -645,6 +645,7 @@ clustersPage:
|
||||||
header: Clusters
|
header: Clusters
|
||||||
newCluster: Add Cluster
|
newCluster: Add Cluster
|
||||||
newClusterName: "Add Cluster: {name}"
|
newClusterName: "Add Cluster: {name}"
|
||||||
|
editClusterName: "Edit Cluster: {name}"
|
||||||
cluster:
|
cluster:
|
||||||
label: Cluster Name
|
label: Cluster Name
|
||||||
provider:
|
provider:
|
||||||
|
|
@ -3141,7 +3142,7 @@ clusterNew:
|
||||||
placeholder: e.g. Cluster for dev and test workloads
|
placeholder: e.g. Cluster for dev and test workloads
|
||||||
members:
|
members:
|
||||||
label: Member Roles
|
label: Member Roles
|
||||||
detail: Control who as access to the cluster and what permission they have to change it
|
detail: Control who has access to the cluster and what permission they have to change it.
|
||||||
config:
|
config:
|
||||||
label: Provider
|
label: Provider
|
||||||
detail: Choose where the nodes for the cluster will come from
|
detail: Choose where the nodes for the cluster will come from
|
||||||
|
|
@ -4165,6 +4166,11 @@ pagination:
|
||||||
=0 {No Nodes}
|
=0 {No Nodes}
|
||||||
=1 {{count} {count, plural, =1 {Node} other {Nodes}}}
|
=1 {{count} {count, plural, =1 {Node} other {Nodes}}}
|
||||||
other {{from} - {to} of {count} Nodes}}
|
other {{from} - {to} of {count} Nodes}}
|
||||||
|
nodePool: |
|
||||||
|
{pages, plural,
|
||||||
|
=0 {No Node Pools}
|
||||||
|
=1 {{count} {count, plural, =1 {Node Pool} other {Node Pools}}}
|
||||||
|
other {{from} - {to} of {count} Node Pools}}
|
||||||
nodeTemplate: |
|
nodeTemplate: |
|
||||||
{pages, plural,
|
{pages, plural,
|
||||||
=0 {No Node Templates}
|
=0 {No Node Templates}
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue