diff --git a/app/authenticated/cluster/edit/route.js b/app/authenticated/cluster/edit/route.js
new file mode 100644
index 000000000..03f7e1523
--- /dev/null
+++ b/app/authenticated/cluster/edit/route.js
@@ -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);
+ }
+});
diff --git a/app/authenticated/cluster/edit/template.hbs b/app/authenticated/cluster/edit/template.hbs
new file mode 100644
index 000000000..5bb4e4e30
--- /dev/null
+++ b/app/authenticated/cluster/edit/template.hbs
@@ -0,0 +1,6 @@
+{{cru-cluster
+ model=model
+ initialProvider=model.cluster.provider
+ mode="edit"
+ close=(action "close")
+}}
diff --git a/app/models/cluster.js b/app/models/cluster.js
index 4cc3dba9f..69b39a3e4 100644
--- a/app/models/cluster.js
+++ b/app/models/cluster.js
@@ -17,13 +17,14 @@ export default Resource.extend(ResourceUsage, {
namespaces: hasMany('id', 'namespace', 'clusterId'),
projects: hasMany('id', 'project', 'clusterId'),
nodes: hasMany('id', 'node', 'clusterId'),
+ nodePools: hasMany('id', 'nodePool', 'clusterId'),
machines: alias('nodes'),
clusterRoleTemplateBindings: hasMany('id', 'clusterRoleTemplateBinding', 'clusterId'),
roleTemplateBindings: alias('clusterRoleTemplateBindings'),
actions: {
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) {
@@ -55,6 +56,31 @@ export default Resource.extend(ResourceUsage, {
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() {
const intl = get(this, 'intl');
const pools = get(this,'nodePools')||[];
diff --git a/app/styles/pages/_host.scss b/app/styles/pages/_host.scss
index 63f57aefb..a2cb62856 100644
--- a/app/styles/pages/_host.scss
+++ b/app/styles/pages/_host.scss
@@ -75,7 +75,7 @@
justify-content: center;
& > .nav-box-item {
- min-height : 150px;
+ min-height : 120px;
flex : 0 1 auto;
position : relative;
outline : 0;
diff --git a/lib/global-admin/addon/clusters/new/controller.js b/lib/global-admin/addon/clusters/new/controller.js
index 08fc902b7..1258d7d37 100644
--- a/lib/global-admin/addon/clusters/new/controller.js
+++ b/lib/global-admin/addon/clusters/new/controller.js
@@ -1,104 +1,15 @@
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';
-const MEMBER_CONFIG = {
- type: 'clusterRoleTemplateBinding',
-};
-
-export default Controller.extend(ViewNewEdit, ChildHook, {
- globalStore: service(),
- intl: service(),
-
+export default Controller.extend({
cluster: alias('model.cluster'),
- primaryResource: alias('model.cluster'),
- step: 1,
provider: 'googlegke',
- memberConfig: MEMBER_CONFIG,
-
queryParams: ['provider'],
actions: {
- clickNext() {
- this.$('BUTTON[type="submit"]').click();
- },
-
close() {
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);
- },
});
diff --git a/lib/global-admin/addon/clusters/new/template.hbs b/lib/global-admin/addon/clusters/new/template.hbs
index 2ac7acfce..67596e810 100644
--- a/lib/global-admin/addon/clusters/new/template.hbs
+++ b/lib/global-admin/addon/clusters/new/template.hbs
@@ -1,68 +1,6 @@
-
-
-{{#if (eq step 1)}}
-
-{{else}}
- {{component driverInfo.driverComponent
- editing=false
- nodeComponent=driverInfo.nodeComponent
- nodeWhich=driverInfo.nodeWhich
- model=model
- close=(action 'close')
- }}
-{{/if}}
-
+{{cru-cluster
+ model=model
+ provider=provider
+ mode="new"
+ close=(action "close")
+}}
diff --git a/lib/global-admin/addon/components/cluster-driver/driver-googlegke/template.hbs b/lib/global-admin/addon/components/cluster-driver/driver-googlegke/template.hbs
index d16dc8777..f536a4293 100644
--- a/lib/global-admin/addon/components/cluster-driver/driver-googlegke/template.hbs
+++ b/lib/global-admin/addon/components/cluster-driver/driver-googlegke/template.hbs
@@ -136,5 +136,5 @@
{{/accordion-list}}
{{top-errors errors=errors}}
- {{save-cancel save="save" cancel=close}}
+ {{save-cancel save="driverSave" cancel=close}}
{{/if}}
diff --git a/lib/global-admin/addon/components/cluster-driver/driver-rke/component.js b/lib/global-admin/addon/components/cluster-driver/driver-rke/component.js
index 6afb012f0..8e4c9727a 100644
--- a/lib/global-admin/addon/components/cluster-driver/driver-rke/component.js
+++ b/lib/global-admin/addon/components/cluster-driver/driver-rke/component.js
@@ -1,5 +1,6 @@
import Component from '@ember/component'
import ClusterDriver from 'global-admin/mixins/cluster-driver';
+import { resolve } from 'rsvp';
import { equal } from '@ember/object/computed';
import { get, set, computed, observer } from '@ember/object';
import { satisfies } from 'shared/utils/parse-version';
@@ -62,6 +63,8 @@ export default Component.extend(ClusterDriver, {
configField: 'rancherKubernetesEngineConfig',
headers,
+ model: null,
+
initialVersion: null,
networkChoices: [
@@ -131,31 +134,18 @@ export default Component.extend(ClusterDriver, {
this.set('labels', out);
},
- addPool() {
- let nodePools = get(this, 'cluster.nodePools');
- if ( !nodePools ) {
- nodePools = [];
- set(this, 'cluster.nodePools', nodePools);
- }
+ driverSave(cb) {
+ cb = cb || function() {};
- let templateId = null;
- const lastNode = nodePools[nodePools.length-1];
- if ( lastNode ) {
- templateId = get(lastNode, 'nodeTemplateId');
- }
+ resolve(this.willSave()).then((ok) => {
+ if ( !ok ) {
+ // Validation or something else said not to save
+ cb(false);
+ return;
+ }
- nodePools.pushObject(get(this, 'globalStore').createRecord({
- 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'));
- }
+ this.sendAction('save', cb);
+ });
},
},
@@ -177,7 +167,7 @@ export default Component.extend(ClusterDriver, {
const intl = get(this, 'intl');
this._super(...arguments);
- let errors = this.get('errors')||[];
+ const errors = get(this,'errors')||[];
if ( !get(this, 'isCustom') ) {
if ( !get(this, 'etcdOk') ) {
@@ -197,7 +187,6 @@ export default Component.extend(ClusterDriver, {
return errors.length === 0;
},
-
doneSaving() {
if ( get(this, 'isCustom') ){
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() {
const versions = get(this, 'versionChoices')||[];
const current = get(this, 'config.kubernetesVersion');
diff --git a/lib/global-admin/addon/components/cluster-driver/driver-rke/template.hbs b/lib/global-admin/addon/components/cluster-driver/driver-rke/template.hbs
index 742c166bc..96900f64d 100644
--- a/lib/global-admin/addon/components/cluster-driver/driver-rke/template.hbs
+++ b/lib/global-admin/addon/components/cluster-driver/driver-rke/template.hbs
@@ -1,100 +1,12 @@
{{#if (eq step 1)}}
{{#accordion-list showExpandAll=false as |al expandFn|}}
{{#unless isCustom}}
- {{#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)
+ {{cru-node-pools
+ cluster=cluster
+ driver=nodeWhich
+ nodeTemplates=model.nodeTemplates
+ registerHook=(action "registerHook")
}}
- {{#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")}}
-
- |
-
- {{input class="input-sm" value=node.hostnamePrefix}}
-
- |
-
-
- {{input class="input-sm" type="number" min="1" value=node.quantity}}
- x
-
- |
-
- {{#if filteredNodeTemplates.length}}
-
- {{else}}
-
- {{/if}}
- |
-
- {{input type="checkbox" checked=node.etcd}}
- |
-
- {{input type="checkbox" checked=node.controlPlane}}
- |
-
- {{input type="checkbox" checked=node.worker}}
- |
-
-
- |
-
- {{else if (eq kind "norows")}}
- | {{t 'nodesPage.table.noData'}} |
- {{else if (eq kind "suffix")}}
-
- | {{t 'clusterNew.rke.role.requirements.label'}} |
-
-
- {{t 'clusterNew.rke.role.requirements.etcd'}}
- |
-
-
- {{t 'clusterNew.rke.role.requirements.controlplane'}}
- |
-
-
- {{t 'clusterNew.rke.role.requirements.worker'}}
- |
- |
-
- {{/if}}
- {{/sortable-table}}
-
-
-
-
- {{/accordion-list-item}}
{{/unless}}
{{#accordion-list-item
@@ -185,7 +97,7 @@
{{/accordion-list}}
{{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}}
diff --git a/lib/global-admin/addon/components/cru-cluster/component.js b/lib/global-admin/addon/components/cru-cluster/component.js
new file mode 100644
index 000000000..e232ac9bf
--- /dev/null
+++ b/lib/global-admin/addon/components/cru-cluster/component.js
@@ -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');
+ }
+ },
+});
diff --git a/lib/global-admin/addon/components/cru-cluster/template.hbs b/lib/global-admin/addon/components/cru-cluster/template.hbs
new file mode 100644
index 000000000..5c84ea8d9
--- /dev/null
+++ b/lib/global-admin/addon/components/cru-cluster/template.hbs
@@ -0,0 +1,71 @@
+
+
+
+ {{#each providerChoices as |choice|}}
+ {{#link-to (query-params provider=choice.name) class=(concat "col span-2 nav-box-item driver machine-driver " choice.name)}}
+
{{choice.displayName}}
+ {{/link-to}}
+ {{/each}}
+
+
+{{#if (or isEdit (eq step 1))}}
+
+{{/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}}
diff --git a/lib/global-admin/addon/components/cru-node-pools/component.js b/lib/global-admin/addon/components/cru-node-pools/component.js
new file mode 100644
index 000000000..af3dc07ee
--- /dev/null
+++ b/lib/global-admin/addon/components/cru-node-pools/component.js
@@ -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;
+ }),
+
+});
diff --git a/lib/global-admin/addon/components/cru-node-pools/template.hbs b/lib/global-admin/addon/components/cru-node-pools/template.hbs
new file mode 100644
index 000000000..cdabd90f2
--- /dev/null
+++ b/lib/global-admin/addon/components/cru-node-pools/template.hbs
@@ -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")}}
+
+ |
+
+ {{input class="input-sm" value=pool.hostnamePrefix}}
+
+ |
+
+
+ {{input class="input-sm" type="number" min="1" value=pool.quantity}}
+ x
+
+ |
+
+ {{#if filteredNodeTemplates.length}}
+
+ {{else}}
+
+ {{/if}}
+ |
+
+ {{input type="checkbox" checked=pool.etcd}}
+ |
+
+ {{input type="checkbox" checked=pool.controlPlane}}
+ |
+
+ {{input type="checkbox" checked=pool.worker}}
+ |
+
+
+ |
+
+ {{else if (eq kind "norows")}}
+
| {{t 'nodesPage.table.noData'}} |
+ {{else if (eq kind "suffix")}}
+
+ | {{t 'clusterNew.rke.role.requirements.label'}} |
+
+
+ {{t 'clusterNew.rke.role.requirements.etcd'}}
+ |
+
+
+ {{t 'clusterNew.rke.role.requirements.controlplane'}}
+ |
+
+
+ {{t 'clusterNew.rke.role.requirements.worker'}}
+ |
+ |
+
+ {{/if}}
+ {{/sortable-table}}
+
+
+
+
+{{/accordion-list-item}}
diff --git a/lib/global-admin/addon/mixins/cluster-driver.js b/lib/global-admin/addon/mixins/cluster-driver.js
index e77a72571..5fe93a348 100644
--- a/lib/global-admin/addon/mixins/cluster-driver.js
+++ b/lib/global-admin/addon/mixins/cluster-driver.js
@@ -1,37 +1,62 @@
import Mixin from '@ember/object/mixin';
-import ViewNewEdit from 'shared/mixins/view-new-edit';
-import { get, computed } from '@ember/object';
+import { get, set, computed } from '@ember/object';
import { alias } from '@ember/object/computed';
+import { resolve } from 'rsvp';
import { inject as service } from '@ember/service';
-export default Mixin.create(ViewNewEdit, {
+export default Mixin.create({
configField: '
',
- close: null, // action to finish adding
+ mode: null,
+ save: null, // Action to save
+ close: null, // Action on complete
+ registerHook: null,
globalStore: service(),
- router: service(),
cluster: alias('model.cluster'),
primaryResource: alias('model.cluster'),
errors: null,
- init() {
- this._super(...arguments);
- },
-
- actions: {
- close() {
- this.sendAction('close');
- }
- },
-
config: computed('configField', function() {
const field = 'cluster.' + get(this, 'configField');
return get(this, field);
}),
- doneSaving() {
- this.get('router').transitionTo('global-admin.clusters.index');
+ actions: {
+ 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;
},
});
diff --git a/lib/shared/addon/components/form-members/component.js b/lib/shared/addon/components/form-members/component.js
index ed4948f1c..5b5207b8e 100644
--- a/lib/shared/addon/components/form-members/component.js
+++ b/lib/shared/addon/components/form-members/component.js
@@ -140,6 +140,10 @@ export default Component.extend({
return PromiseAll(add.map(x => x.save())).then(() => {
return PromiseAll(update.map(x => x.save())).then(() => {
return PromiseAll(remove.map(x => x.delete())).then(() => {
+ if ( this.isDestroyed || this.isDestroying ) {
+ return;
+ }
+
set(this, 'memberArray', []);
return get(this, 'primaryResource');
});
diff --git a/translations/en-us.yaml b/translations/en-us.yaml
index c9b71a72c..b8f7d1c32 100644
--- a/translations/en-us.yaml
+++ b/translations/en-us.yaml
@@ -645,6 +645,7 @@ clustersPage:
header: Clusters
newCluster: Add Cluster
newClusterName: "Add Cluster: {name}"
+ editClusterName: "Edit Cluster: {name}"
cluster:
label: Cluster Name
provider:
@@ -3141,7 +3142,7 @@ clusterNew:
placeholder: e.g. Cluster for dev and test workloads
members:
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:
label: Provider
detail: Choose where the nodes for the cluster will come from
@@ -4165,6 +4166,11 @@ pagination:
=0 {No Nodes}
=1 {{count} {count, plural, =1 {Node} other {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: |
{pages, plural,
=0 {No Node Templates}