diff --git a/.eslintrc.js b/.eslintrc.js index c818be986..a94b9812f 100644 --- a/.eslintrc.js +++ b/.eslintrc.js @@ -65,6 +65,7 @@ module.exports = { "no-useless-escape": 0, "ember/use-ember-get-and-set": 0, "ember/order-in-controllers": 0, + "ember/order-in-models": 0, "ember/closure-actions": 0, "ember/order-in-components": 0, "ember/no-on-calls-in-components": 0, diff --git a/app/authenticated/clusters/index/template.hbs b/app/authenticated/clusters/index/template.hbs index b00e99968..a462ec732 100644 --- a/app/authenticated/clusters/index/template.hbs +++ b/app/authenticated/clusters/index/template.hbs @@ -2,24 +2,10 @@

{{t 'clustersPage.header'}}

-
- {{#tooltip-element type="tooltip-basic" model=(t 'clustersPage.mode.list') tooltipTemplate='tooltip-static' aria-describedby="tooltip-base" tooltipFor="tooltipLink"}} - {{#link-to (query-params mode="list") classNames="btn btn-sm bg-default"}}{{/link-to}} - {{/tooltip-element}} - {{#tooltip-element type="tooltip-basic" model=(t 'clustersPage.mode.grouped') tooltipTemplate='tooltip-static' aria-describedby="tooltip-base" tooltipFor="tooltipLink"}} - {{#link-to (query-params mode="grouped") classNames="btn btn-sm bg-default"}}{{/link-to}} - {{/tooltip-element}} -
- {{#link-to "authenticated.clusters.new" class="btn bg-primary btn-sm icon-btn"}} {{t 'clustersPage.newCluster'}} {{/link-to}} - - {{#link-to "authenticated.clusters.new-project" class="btn bg-primary btn-sm icon-btn" disabled=(eq model.clusters.length 0)}} - - {{t 'clustersPage.newProject'}} - {{/link-to}}
@@ -32,36 +18,29 @@ {{/unless}} -{{#if (eq mode "grouped")}} - {{#sortable-table - classNames="grid sortable-table" - body=model.clusters - searchText=searchText - sortBy=sortBy - bulkActions=false - fullRows=true - subRows=true - pagingLabel="pagination.cluster" - headers=headers as |sortable kind inst dt| - }} - {{#if (eq kind "row")}} - {{cluster-row - model=inst - fullColspan=sortable.fullColspan - toggle=(action "toggleExpand" inst.id) - expanded=(array-includes expandedClusters inst.id) - launchOnCluster=(action "launchOnCluster") - useKubernetes=(action "useKubernetes") - dt=dt - }} - {{else if (eq kind "nomatch")}} - {{t 'clusterRow.noClusterMatch'}} - {{else if (eq kind "norows")}} - {{t 'clusterRow.noClusterData'}} - {{/if}} - {{/sortable-table}} -{{else}} - {{project-table model=model.projects showCluster=true}} -{{/if}} - -
{{t 'clustersPage.subtext' appName=settings.appName htmlSafe=true}}
+{{#sortable-table + classNames="grid sortable-table" + body=model.clusters + searchText=searchText + sortBy=sortBy + bulkActions=false + fullRows=true + pagingLabel="pagination.cluster" + headers=headers as |sortable kind inst dt| +}} + {{#if (eq kind "row")}} + {{cluster-row + model=inst + fullColspan=sortable.fullColspan + toggle=(action "toggleExpand" inst.id) + expanded=(array-includes expandedClusters inst.id) + launchOnCluster=(action "launchOnCluster") + useKubernetes=(action "useKubernetes") + dt=dt + }} + {{else if (eq kind "nomatch")}} + {{t 'clusterRow.noClusterMatch'}} + {{else if (eq kind "norows")}} + {{t 'clusterRow.noClusterData'}} + {{/if}} +{{/sortable-table}} diff --git a/app/authenticated/clusters/new/controller.js b/app/authenticated/clusters/new/index/controller.js similarity index 100% rename from app/authenticated/clusters/new/controller.js rename to app/authenticated/clusters/new/index/controller.js diff --git a/app/authenticated/clusters/new/index/route.js b/app/authenticated/clusters/new/index/route.js new file mode 100644 index 000000000..a92fe03cb --- /dev/null +++ b/app/authenticated/clusters/new/index/route.js @@ -0,0 +1,46 @@ +import EmberObject from '@ember/object'; +import { inject as service } from '@ember/service'; +import Route from '@ember/routing/route'; +import C from 'ui/utils/constants'; + +export default Route.extend({ + clusterStore: service('cluster-store'), + catalog: service(), + settings: service(), + + model() { + // TODO - !!FORDEV!! removed for dev sake + let store = this.get('clusterStore'); + let def = JSON.parse(this.get(`settings.${C.SETTING.CLUSTER_TEMPLATE}`)) || {}; + + def.type = 'cluster'; + // def.systemStacks = (def.systemStacks||[]).map((stack) => { + // stack.type = 'stackConfiguration'; + // return stack; + // }); + + let cluster = store.createRecord(def); + + // return this.get('catalog').fetchTemplates({plusInfra: true}).then((templates) => { + // return EmberObject.create({ + // cluster: cluster, + // allTemplates: templates + // }); + // }); + return EmberObject.create({cluster: cluster, allTemplates: []}); + }, + // teardownForComponentState: on('deactivate', function(){ + // this.controller.setProperties({ + // catalogItem: null, + // editCatalog: false, + // selectedTemplateUrl: null, + // catalogInfo: null, + // _catalogInfoCache: null, + // _prefetchInstance: null, + // catalogId: 'all', + // category: null, + // viewCatalog: false, + // newSystemStack: null, + // }); + // }) +}); diff --git a/app/authenticated/clusters/new/index/template.hbs b/app/authenticated/clusters/new/index/template.hbs new file mode 100644 index 000000000..5d5ce6016 --- /dev/null +++ b/app/authenticated/clusters/new/index/template.hbs @@ -0,0 +1,38 @@ +
+

{{t 'clustersPage.newCluster'}}

+
+ +
+
+
+

{{t 'clustersPage.indexPage.rke.header'}}

+
+

{{t 'clustersPage.indexPage.rke.desc'}}

+ +
+
+
+
+

{{t 'clustersPage.indexPage.cloud.header'}}

+
+

{{t 'clustersPage.indexPage.cloud.desc'}}

+ +
+
+
+
+

{{t 'clustersPage.indexPage.import.header'}}

+
+

{{t 'clustersPage.indexPage.import.desc'}}

+ +
+
+
+
+
diff --git a/app/authenticated/clusters/new/rke/controller.js b/app/authenticated/clusters/new/rke/controller.js new file mode 100644 index 000000000..b121cc269 --- /dev/null +++ b/app/authenticated/clusters/new/rke/controller.js @@ -0,0 +1,105 @@ +import Controller from '@ember/controller'; +// import { copy } from '@ember/object/internals'; +import { get, set } from '@ember/object'; +import { inject as service } from '@ember/service'; +import { computed,observer } from '@ember/object'; +import { alias } from '@ember/object/computed'; + +const CONFIG_DEFAULT = { + advertisedHostname: '', + role: null, + type: 'rkeConfigHost', + user: '', + ssh: '', +}; + +export default Controller.extend({ + modalService: service('modal'), + store: service('cluster-store'), + step: null, + loading: null, + newHost: null, + // clusterList: null, + canAdd: null, + cluster: alias('model'), + config: alias('model.rancherKubernetesEngineConfig'), + + init() { + this._super(...arguments); + let newHostConfig = get(this, 'store').createRecord(CONFIG_DEFAULT); + set(newHostConfig, 'role', ['etcd']); + this.setProperties({ + step: 1, + newHost: newHostConfig, + loading: false, + canAdd: false, + }); + }, + + workerList: computed('config.hosts.@each.{role,advertisedHostname,user}', function() { + return ( get(this, 'config.hosts') || [] ).filter((host) => { + return (get(host, 'role')||[]).includes('worker'); + }); + }), + + controlplaneList: computed('config.hosts.@each.{role,advertisedHostname,user}', function() { + return ( get(this, 'config.hosts') || [] ).filter((host) => { + return (get(host, 'role')||[]).includes('controlplane'); + }); + }), + + clusterList: computed('config.hosts.@each.{role,advertisedHostname,user,ssh}', function() { + // unfilterd list of host + return ( get(this, 'config.hosts') || [] ).uniq(); + }), + + hostObserver: observer('newHost.user','newHost.advertisedHostname','newHost.ssh', function() { + let cName = get(this, 'newHost.user'); + let hName = get(this, 'newHost.advertisedHostname'); + let ssh = get(this, 'newHost.ssh'); + (cName.length > 0) && (hName.length > 0) && (ssh.length > 0) ? set(this, 'canAdd', true) : set(this, 'canAdd', false); + }), + + actions: { + save() { + }, + cancel(prev) { + this.send('goToPrevious',prev); + }, + addNew(type) { + // TODO - set role + let neu = CONFIG_DEFAULT; + set(neu, 'role', []); + this.get('modalService').toggleModal('modal-add-cluster', { + newHost: get(this, 'store').createRecord(neu), + list: get(this, type), + }); + }, + go() { + this.incrementProperty('step'); + }, + back() { + this.decrementProperty('step'); + }, + addHost() { + get(this, 'config.hosts').pushObject(get(this, 'newHost')); + set(this, 'newHost', get(this, 'store').createRecord(CONFIG_DEFAULT)); + }, + removeHost(host, roleToRemove) { + let roles = get(host, 'role'); + let at = roles.indexOf(roleToRemove); + if (at >= 0) { + roles.removeAt(at); + if (roles.length === 0) { + get(this, 'config.hosts').removeObject(host); + } + } + }, + useFor(host, role) { + // set knows nothing changed so it will not fire the computed propertiy change events + let roles = get(host, 'role').slice(); //clone the array + roles.addObject(role); + set(host, 'role', roles); + }, + } +}); diff --git a/app/authenticated/clusters/new/rke/route.js b/app/authenticated/clusters/new/rke/route.js new file mode 100644 index 000000000..ebd233b7c --- /dev/null +++ b/app/authenticated/clusters/new/rke/route.js @@ -0,0 +1,21 @@ +import Ember from 'ember'; +import { inject as service } from '@ember/service'; +import { get, set } from '@ember/object'; + +export default Ember.Route.extend({ + store: service('cluster-store'), + model: function(/* params, transition */) { + // TODO - !!FORDEV!! get clusters + let store = get(this, 'store'); + let def = { + type: 'rancherKubernetesEngineConfig', + hosts: [], + network: 'flannel', + auth: 'x509' + }; + let config = store.createRecord(def); + let cluster = this.modelFor('authenticated.clusters.new.index'); + set(cluster, 'rancherKubernetesEngineConfig', config); + return cluster; + } +}); diff --git a/app/authenticated/clusters/new/rke/template.hbs b/app/authenticated/clusters/new/rke/template.hbs new file mode 100644 index 000000000..6bb701464 --- /dev/null +++ b/app/authenticated/clusters/new/rke/template.hbs @@ -0,0 +1,136 @@ +
+ {{t 'clustersPage.addPage.rke.header' count=step}} +
+ +
+
+ {{#if (eq step 1)}} +
+ {{partial "add-cluster"}} +
+ +
+
+
+ {{#each config.hosts as | cluster |}} +
{{t 'clustersPage.addPage.rke.minimums.cluster'}}
+
+
+

{{cluster.advertisedHostname}}

+
+
+ +
+
+ {{else}} +
+
{{t 'clustersPage.addPage.rke.minimums.nocluster'}}
+
+ {{/each}} +
+
+ {{#if loading}} + + {{else}} + {{#if config.hosts.length }} + + {{else}} + + {{/if}} + + {{/if}} +
+
+ {{else if (eq step 2) }} +
+
+ {{#each controlplaneList as | cluster |}} +
{{t 'clustersPage.addPage.rke.minimums.management'}}
+
+
+

{{cluster.advertisedHostname}}

+
+
+ +
+
+ {{else}} +
{{t 'clustersPage.addPage.rke.minimums.noManagement'}}
+ {{/each}} +
+
+
+ {{#if clusterList.length }} +
{{t 'clustersPage.addPage.rke.reuse.nodes'}}
+ {{#each clusterList as | cluster |}} +
+
+

{{cluster.advertisedHostname}}

+
+
+ +
+
+ {{/each}} + {{/if}} +
+
+ {{#if loading}} + + {{else}} + + {{#if controlplaneList.length }} + + {{else}} + + {{/if}} + {{/if}} +
+
+ {{else if (eq step 3) }} +
+
+
{{t 'clustersPage.addPage.rke.minimums.nodes'}}
+ {{#each workerList as | cluster |}} +
+
+

{{cluster.advertisedHostname}}

+
+
+ +
+
+ {{else}} +
+
{{t 'clustersPage.addPage.rke.minimums.noNodes'}}
+
+ {{/each}} +
+
+
+ {{#if clusterList.length }} +
{{t 'clustersPage.addPage.rke.reuse.nodes'}}
+ {{#each clusterList as | cluster |}} +
+
+

{{cluster.advertisedHostname}}

+
+
+ +
+
+ {{/each}} + {{/if}} +
+
+ {{#if loading}} + + {{else}} + + + {{/if}} +
+
+ {{/if}} +
+
\ No newline at end of file diff --git a/app/authenticated/clusters/new/route.js b/app/authenticated/clusters/new/route.js deleted file mode 100644 index b36ca1374..000000000 --- a/app/authenticated/clusters/new/route.js +++ /dev/null @@ -1,45 +0,0 @@ -import { on } from '@ember/object/evented'; -import EmberObject from '@ember/object'; -import { inject as service } from '@ember/service'; -import Route from '@ember/routing/route'; -import C from 'ui/utils/constants'; - -export default Route.extend({ - clusterStore: service('cluster-store'), - catalog: service(), - settings: service(), - - model() { - let store = this.get('clusterStore'); - let def = JSON.parse(this.get(`settings.${C.SETTING.CLUSTER_TEMPLATE}`)) || {}; - - def.type = 'cluster'; - def.systemStacks = (def.systemStacks||[]).map((stack) => { - stack.type = 'stackConfiguration'; - return stack; - }) - - let cluster = store.createRecord(def); - - return this.get('catalog').fetchTemplates({plusInfra: true}).then((templates) => { - return EmberObject.create({ - cluster: cluster, - allTemplates: templates - }); - }); - }, - teardownForComponentState: on('deactivate', function(){ - this.controller.setProperties({ - catalogItem: null, - editCatalog: false, - selectedTemplateUrl: null, - catalogInfo: null, - _catalogInfoCache: null, - _prefetchInstance: null, - catalogId: 'all', - category: null, - viewCatalog: false, - newSystemStack: null, - }); - }) -}); diff --git a/app/authenticated/clusters/new/template.hbs b/app/authenticated/clusters/new/template.hbs deleted file mode 100644 index b22e832bc..000000000 --- a/app/authenticated/clusters/new/template.hbs +++ /dev/null @@ -1,5 +0,0 @@ -{{new-edit-cluster - cancel=(action 'cancel') - model=model - editing=false -}} \ No newline at end of file diff --git a/app/authenticated/clusters/route.js b/app/authenticated/clusters/route.js index bebfb4f4b..6663a8b57 100644 --- a/app/authenticated/clusters/route.js +++ b/app/authenticated/clusters/route.js @@ -1,4 +1,3 @@ -import { hash } from 'rsvp'; import Route from '@ember/routing/route'; import { inject as service } from '@ember/service'; @@ -11,11 +10,12 @@ export default Route.extend({ }, model() { - const scope = this.get('scope'); - - return hash({ - projects: scope.getAll({all: true, removeMissing: true}), - clusters: scope.getAllClusters({removeMissing: true}), + var clusterStore = this.get('clusterStore'); + return clusterStore.find('cluster', null, {url: 'clusters', forceReload: true, removeMissing: true}).then(() => { + //return a live array so its updated + return { + clusters: clusterStore.all('cluster'), + }; }); }, }); diff --git a/app/authenticated/route.js b/app/authenticated/route.js index 1f06c2864..e83d2f7d5 100644 --- a/app/authenticated/route.js +++ b/app/authenticated/route.js @@ -99,7 +99,8 @@ export default Route.extend(Subscribe, { if ( this.get('access.admin') && (!opt || opt === 'prompt') ) { scheduleOnce('afterRender', this, function() { - this.get('modalService').toggleModal('modal-telemetry'); + // TODO - !!FORDEV!! removed for dev sake + // this.get('modalService').toggleModal('modal-telemetry'); }); } else if ( form && !this.get(`prefs.${C.PREFS.FEEDBACK}`) ) diff --git a/app/models/azurekubernetesserviceconfig.js b/app/models/azurekubernetesserviceconfig.js new file mode 100644 index 000000000..cd3c78dcd --- /dev/null +++ b/app/models/azurekubernetesserviceconfig.js @@ -0,0 +1,40 @@ +import Resource from 'ember-api-store/models/resource'; +import PolledResource from 'ui/mixins/cattle-polled-resource'; + +var AzureKubernetesServiceConfig = Resource.extend(PolledResource, { + type: 'azureKubernetesServiceConfig', + + reservedKeys: [], + + actions: { + deactivate() { + return this.doAction('deactivate'); + }, + + activate() { + return this.doAction('activate'); + }, + + edit: function() { + }, + }, + + availableActions: function() { + let a = this.get('actionLinks'); + let l = this.get('links'); + + return [ + { label: 'action.edit', icon: 'icon icon-edit', action: 'edit', enabled: !!l.update }, + { divider: true }, + { label: 'action.activate', icon: 'icon icon-play', action: 'activate', enabled: !!a.activate , bulkable: true}, + { label: 'action.deactivate', icon: 'icon icon-pause', action: 'deactivate', enabled: !!a.deactivate , bulkable: true}, + { divider: true }, + { label: 'action.remove', icon: 'icon icon-trash', action: 'promptDelete', enabled: !!l.remove, altAction: 'delete', bulkable: true }, + { divider: true }, + { label: 'action.viewInApi', icon: 'icon icon-external-link',action: 'goToApi', enabled: true }, + ]; + }.property('actionLinks.{activate,deactivate,restore}','links.{update,remove}'), + +}); + +export default AzureKubernetesServiceConfig; diff --git a/app/models/cluster.js b/app/models/cluster.js index 9c429200e..5b4ad6792 100644 --- a/app/models/cluster.js +++ b/app/models/cluster.js @@ -40,7 +40,7 @@ var Cluster = Resource.extend(PolledResource, { _allProjects: null, - projects: function() { + projects: computed('_allProjects.@each.clusterName', function() { let x = this.get('_allProjects'); if (!x) { @@ -48,9 +48,9 @@ var Cluster = Resource.extend(PolledResource, { } return x.filterBy('clusterName', this.get('id')); - }.property('_allProjects.@each.clusterName'), + }), - defaultProject: function() { + defaultProject: computed('projects.@each.{name,clusterOwner}', function() { let projects = this.get('projects'); let out = projects.findBy('name','Default'); @@ -64,54 +64,54 @@ var Cluster = Resource.extend(PolledResource, { } return out; - }.property('projects.@each.{name,clusterOwner}'), + }), canEdit: computed('actionLinks.{activate,deactivate}','links.{update,remove}', function() { return (this.get('links.update') && this.get('state') === 'inactive') ? true : false; }), - systemProject: function() { + systemProject: computed('projects.@each.{clusterOwner}', function() { return this.get('projects').findBy('clusterOwner', true); - }.property('projects.@each.{clusterOwner}'), + }), - availableActions: function() { -// let a = this.get('actionLinks'); + availableActions: computed('actionLinks.{activate,deactivate}','links.{update,remove}', function() { + // let a = this.get('actionLinks'); let l = this.get('links'); var choices = [ { label: 'action.edit', icon: 'icon icon-edit', action: 'edit', enabled: this.get('canEdit') }, { divider: true }, -// { label: 'action.activate', icon: 'icon icon-play', action: 'activate', enabled: !!a.activate}, -// { label: 'action.deactivate', icon: 'icon icon-pause', action: 'promptStop', enabled: !!a.deactivate, altAction: 'deactivate'}, -// { divider: true }, + // { label: 'action.activate', icon: 'icon icon-play', action: 'activate', enabled: !!a.activate}, + // { label: 'action.deactivate', icon: 'icon icon-pause', action: 'promptStop', enabled: !!a.deactivate, altAction: 'deactivate'}, + // { divider: true }, { label: 'action.remove', icon: 'icon icon-trash', action: 'promptDelete', enabled: !!l.remove, altAction: 'delete' }, { divider: true }, { label: 'action.viewInApi', icon: 'icon icon-external-link',action: 'goToApi', enabled: true }, ]; return choices; - }.property('actionLinks.{activate,deactivate}','links.{update,remove}'), + }), // @TODO real data - numHosts: function() { + numHosts: computed(function() { return 3+Math.round(Math.random()*2); - }.property(), + }), - numCores: function() { + numCores: computed('numHosts', function() { return this.get('numHosts')*8; - }.property('numHosts'), + }), - numGhz: function() { + numGhz: computed('numCores', function() { return 3.4*this.get('numCores'); - }.property('numCores'), + }), - numMem: function() { + numMem: computed('numHosts', function() { return 8*this.get('numHosts'); - }.property('numHosts'), + }), - numStorage: function() { + numStorage: computed('numHosts', function() { return 40*this.get('numHosts'); - }.property('numHosts'), + }), }); Cluster.reopenClass({ diff --git a/app/models/googlekubernetesengineconfig.js b/app/models/googlekubernetesengineconfig.js new file mode 100644 index 000000000..2f4823a2f --- /dev/null +++ b/app/models/googlekubernetesengineconfig.js @@ -0,0 +1,40 @@ +import Resource from 'ember-api-store/models/resource'; +import PolledResource from 'ui/mixins/cattle-polled-resource'; + +var GoogleKubernetesEngineConfig = Resource.extend(PolledResource, { + type: 'googleKubernetesEngineConfig', + + reservedKeys: [], + + actions: { + deactivate() { + return this.doAction('deactivate'); + }, + + activate() { + return this.doAction('activate'); + }, + + edit: function() { + }, + }, + + availableActions: function() { + let a = this.get('actionLinks'); + let l = this.get('links'); + + return [ + { label: 'action.edit', icon: 'icon icon-edit', action: 'edit', enabled: !!l.update }, + { divider: true }, + { label: 'action.activate', icon: 'icon icon-play', action: 'activate', enabled: !!a.activate , bulkable: true}, + { label: 'action.deactivate', icon: 'icon icon-pause', action: 'deactivate', enabled: !!a.deactivate , bulkable: true}, + { divider: true }, + { label: 'action.remove', icon: 'icon icon-trash', action: 'promptDelete', enabled: !!l.remove, altAction: 'delete', bulkable: true }, + { divider: true }, + { label: 'action.viewInApi', icon: 'icon icon-external-link',action: 'goToApi', enabled: true }, + ]; + }.property('actionLinks.{activate,deactivate,restore}','links.{update,remove}'), + +}); + +export default GoogleKubernetesEngineConfig; diff --git a/app/models/namespace.js b/app/models/namespace.js index e0f341168..c4bbb5f9a 100644 --- a/app/models/namespace.js +++ b/app/models/namespace.js @@ -130,7 +130,7 @@ var Stack = Resource.extend(StateCounts, { }, }, - availableActions: function() { + availableActions: computed('actionLinks.{exportconfig}','links.{update,remove}','externalIdInfo.kind','canStartAll','canPauseAll','canStopAll', function() { let a = this.get('actionLinks'); let l = this.get('links'); @@ -156,9 +156,9 @@ var Stack = Resource.extend(StateCounts, { ]; return out; - }.property('actionLinks.{exportconfig}','links.{update,remove}','externalIdInfo.kind','canStartAll','canPauseAll','canStopAll'), + }), - canStartAll: function() { + canStartAll: computed('services.@each.state','actionLinks.startall', function() { if ( !this.hasAction('startall') ) { return false; } @@ -169,9 +169,9 @@ var Stack = Resource.extend(StateCounts, { } return this.get('services').filterBy('actionLinks.activate').get('length') > 0; - }.property('services.@each.state','actionLinks.startall'), + }), - canPauseAll: function() { + canPauseAll: computed('services.@each.state','actionLinks.pauseall', function() { if ( !this.hasAction('pauseall') ) { return false; } @@ -182,9 +182,9 @@ var Stack = Resource.extend(StateCounts, { } return this.get('services').filterBy('actionLinks.pause').get('length') > 0; - }.property('services.@each.state','actionLinks.pauseall'), + }), - canStopAll: function() { + canStopAll: computed('services.@each.state','actionLinks.stopall', function() { if ( !this.hasAction('stopall') ) { return false; } @@ -201,13 +201,13 @@ var Stack = Resource.extend(StateCounts, { } return services.filterBy('actionLinks.deactivate').get('length') > 0 && containers.filterBy('actionLinks.stop').get('length'); - }.property('services.@each.state','actionLinks.stopall'), + }), - canViewConfig: function() { + canViewConfig: computed('actionLinks.exportconfig', function() { return !!this.get('actionLinks.exportconfig'); - }.property('actionLinks.exportconfig'), + }), - combinedState: function() { + combinedState: computed('state', 'healthState', function() { var stack = this.get('state'); var health = this.get('healthState'); @@ -216,15 +216,15 @@ var Stack = Resource.extend(StateCounts, { } else { return stack; } - }.property('state', 'healthState'), + }), - externalIdInfo: function() { + externalIdInfo: computed('externalId', function() { return parseExternalId(this.get('externalId')); - }.property('externalId'), + }), - isDefault: function() { + isDefault: computed('name', function() { return (this.get('name')||'').toLowerCase() === 'default'; - }.property('name'), + }), isEmpty: computed('instances.length', 'services.length', function() { return false; // @TODO-2.0 @@ -236,25 +236,57 @@ var Stack = Resource.extend(StateCounts, { return false; }), - isFromCatalog: function() { + isFromCatalog: computed('externalIdInfo.kind', function() { let kind = this.get('externalIdInfo.kind'); return kind === C.EXTERNAL_ID.KIND_CATALOG || kind === C.EXTERNAL_ID.KIND_SYSTEM_CATALOG; - }.property('externalIdInfo.kind'), + }), // This only works if the templates have already been loaded elsewhere... - catalogTemplate: function() { + catalogTemplate: computed('externalIdInfo.templateId', function() { return this.get('catalog').getTemplateFromCache(this.get('externalIdInfo.templateId')); - }.property('externalIdInfo.templateId'), + }), - icon: function() { + icon: computed('catalogTemplate', function() { let tpl = this.get('catalogTemplate'); if ( tpl ) { return tpl.linkFor('icon'); } - }.property('catalogTemplate'), + }), - normalizedTags: computed('tags.[]', function() { - return normalizedTags(this.get('tags')); + grouping: computed('externalIdInfo.kind','group','system', function() { + var kind = this.get('externalIdInfo.kind'); + + if ( kind === C.EXTERNAL_ID.KIND_KUBERNETES || kind === C.EXTERNAL_ID.KIND_LEGACY_KUBERNETES ) + { + return C.EXTERNAL_ID.KIND_KUBERNETES; + } + else if ( this.get('system') ) + { + return C.EXTERNAL_ID.KIND_INFRA; + } + else + { + return C.EXTERNAL_ID.KIND_USER; + } + }), + + normalizedTags: computed('group', { + get() { + return tagsToArray(this.get('group')); + }, + set(key,value) { + this.set('group', (value||[]).map((x) => normalizeTag(x)).join(', ')); + return value; + } + }), + tags: computed('group', { + get(){ + return tagsToArray(this.get('group'), false); + }, + set(key,value) { + this.set('group', (value||[]).map((x) => normalizeTag(x)).join(', ')); + return value; + } }), hasTags(want) { diff --git a/app/models/rancherkubernetesengineconfig.js b/app/models/rancherkubernetesengineconfig.js new file mode 100644 index 000000000..156fb08a5 --- /dev/null +++ b/app/models/rancherkubernetesengineconfig.js @@ -0,0 +1,41 @@ +import { inject as service } from '@ember/service'; +import Resource from 'ember-api-store/models/resource'; +import PolledResource from 'ui/mixins/cattle-polled-resource'; + +var RancherKubernetesEngineConfig = Resource.extend(PolledResource, { + type: 'rancherKubernetesEngineConfig', + + reservedKeys: [], + + actions: { + deactivate() { + return this.doAction('deactivate'); + }, + + activate() { + return this.doAction('activate'); + }, + + edit: function() { + }, + }, + + availableActions: function() { + let a = this.get('actionLinks'); + let l = this.get('links'); + + return [ + { label: 'action.edit', icon: 'icon icon-edit', action: 'edit', enabled: !!l.update }, + { divider: true }, + { label: 'action.activate', icon: 'icon icon-play', action: 'activate', enabled: !!a.activate , bulkable: true}, + { label: 'action.deactivate', icon: 'icon icon-pause', action: 'deactivate', enabled: !!a.deactivate , bulkable: true}, + { divider: true }, + { label: 'action.remove', icon: 'icon icon-trash', action: 'promptDelete', enabled: !!l.remove, altAction: 'delete', bulkable: true }, + { divider: true }, + { label: 'action.viewInApi', icon: 'icon icon-external-link',action: 'goToApi', enabled: true }, + ]; + }.property('actionLinks.{activate,deactivate,restore}','links.{update,remove}'), + +}); + +export default RancherKubernetesEngineConfig; diff --git a/app/models/stackconfiguration.js b/app/models/stackconfiguration.js index de984e564..1144c5e9f 100644 --- a/app/models/stackconfiguration.js +++ b/app/models/stackconfiguration.js @@ -1,5 +1,7 @@ import Stack from 'ui/models/namespace'; -var StackConfiguration = Stack.extend(); +var StackConfiguration = Stack.extend({ + type: 'stackConfiguration' +}); export default StackConfiguration; diff --git a/app/router.js b/app/router.js index 27a0265da..b8fcddd78 100644 --- a/app/router.js +++ b/app/router.js @@ -40,7 +40,9 @@ Router.map(function() { // Clusters this.route('clusters', {path: '/clusters'}, function() { this.route('index', {path: '/'}); - this.route('new', {path: '/add'}); + this.route('new', {path: '/add'}, function() { + this.route('rke'); + }); this.route('new-project', {path: '/add-env'}); this.route('project', {path: '/env/:project_id'}); diff --git a/app/styles/base/_helpers.scss b/app/styles/base/_helpers.scss index 990b80c97..7b52dbef2 100755 --- a/app/styles/base/_helpers.scss +++ b/app/styles/base/_helpers.scss @@ -245,11 +245,12 @@ .border { - + border: 1px solid $border; + background: lighten($border, 2%); } .border-dash { - border: dashed $border; + border: 1px dashed $border; background: lighten($border, 2%); } diff --git a/app/styles/pages/_cluster-welcome.scss b/app/styles/pages/_cluster-welcome.scss index 0b77f35d8..1f4a64b29 100644 --- a/app/styles/pages/_cluster-welcome.scss +++ b/app/styles/pages/_cluster-welcome.scss @@ -7,12 +7,18 @@ background: $accent-border; } + .box-sm { + padding: 75px 30px 30px 30px; + min-height: 370px; + position: relative; + } .box { padding: 75px 40px 40px 40px; min-height: 410px; position: relative; } + .links { position: absolute; top: 300px; @@ -48,7 +54,8 @@ color: white; } - .box { + .box, + .box-sm { background-color: lighten($link-color, 40%); } } diff --git a/app/templates/-add-cluster.hbs b/app/templates/-add-cluster.hbs new file mode 100644 index 000000000..e565e54ce --- /dev/null +++ b/app/templates/-add-cluster.hbs @@ -0,0 +1,16 @@ +
+
+
+ + {{input type="text" value=newHost.user }} +
+
+ + {{input type="text" value=newHost.advertisedHostname }} +
+
+
+ + {{textarea value=newHost.ssh classNames="form-control no-resize" rows="6"}} +
+
\ No newline at end of file diff --git a/blueprints/modal/index.js b/blueprints/modal/index.js index 193a6bf53..fede46a56 100644 --- a/blueprints/modal/index.js +++ b/blueprints/modal/index.js @@ -5,7 +5,7 @@ module.exports = { // Return custom template variables here. return { // options are large-modal or medium-modal - size: options.entity.options.size || 'medium-modal' + size: options.size || 'medium-modal' }; } diff --git a/lib/.eslintrc.js b/lib/.eslintrc.js index fbecb89bb..7638aba81 100644 --- a/lib/.eslintrc.js +++ b/lib/.eslintrc.js @@ -64,6 +64,7 @@ module.exports = { "no-useless-escape": 0, "ember/use-ember-get-and-set": 0, "ember/order-in-controllers": 0, + "ember/order-in-models": 0, "ember/closure-actions": 0, "ember/order-in-components": 0, "ember/no-on-calls-in-components": 0, @@ -77,6 +78,6 @@ module.exports = { "ember/use-brace-expansion": 0, "ember/no-side-effects": 0, "ember/no-capital-letters-in-routes": 0, - "generator-star-spacing": 0, + "generator-star-spacing": 0 } }; diff --git a/lib/global-admin/addon/components/page-header-environment/template.hbs b/lib/global-admin/addon/components/page-header-environment/template.hbs index c867b6d37..b54557806 100644 --- a/lib/global-admin/addon/components/page-header-environment/template.hbs +++ b/lib/global-admin/addon/components/page-header-environment/template.hbs @@ -42,18 +42,11 @@ {{/if}} -
  • - {{#link-to-external "authenticated.clusters"}} - - {{t 'nav.environment.manage'}} - {{/link-to-external}} -
  • - {{#if (and isOwner projects.current)}}
  • {{#link-to-external "authenticated.clusters.project" projects.current.id class="clip"}}{{t 'nav.environment.edit' name=projects.current.displayName}}{{/link-to-external}}
  • + {{/if}} - {{#if projectIsMissing}} diff --git a/lib/global-admin/addon/components/page-header/template.hbs b/lib/global-admin/addon/components/page-header/template.hbs index 3dbccb9fb..cf879bbf1 100644 --- a/lib/global-admin/addon/components/page-header/template.hbs +++ b/lib/global-admin/addon/components/page-header/template.hbs @@ -102,6 +102,10 @@