diff --git a/app/apps-tab/detail/controller.js b/app/apps-tab/detail/controller.js index c404773fb..6cb8a6d01 100644 --- a/app/apps-tab/detail/controller.js +++ b/app/apps-tab/detail/controller.js @@ -204,6 +204,16 @@ export default Controller.extend({ this.set('expandedInstances',[]); }, + publicEndpoints: computed('model.app.workloads.@each.publicEndpoints', function() { + let out = []; + get(this, 'model.app.workloads').forEach((workload) => { + (get(workload, 'publicEndpoints') || []).forEach((endpoint) => { + out.push(endpoint); + }); + }); + return out; + }), + workloadsAndPods: computed('model.app.workloads', 'model.app.pods', function() { let out = []; out = this.get('model.app.pods').filter(obj => !obj.get('workloadId')); diff --git a/app/apps-tab/detail/template.hbs b/app/apps-tab/detail/template.hbs index 001f1a5ef..db5c2d828 100644 --- a/app/apps-tab/detail/template.hbs +++ b/app/apps-tab/detail/template.hbs @@ -54,6 +54,18 @@ expandOnInit=false }} + {{#accordion-list-item + title=(t 'appDetailPage.endpoints.title') + detail=(t 'appDetailPage.endpoints.detail') + expandAll=al.expandAll + expand=(action expandFn) + expandOnInit=true + }} + {{form-endpoints + model=publicEndpoints + }} + {{/accordion-list-item}} + {{#accordion-list-item title=(t 'appDetailPage.workloads.title') detail=(t 'appDetailPage.workloads.detail') diff --git a/app/authenticated/cluster/projects/index/controller.js b/app/authenticated/cluster/projects/index/controller.js index 8fb9a88f2..5e120922e 100644 --- a/app/authenticated/cluster/projects/index/controller.js +++ b/app/authenticated/cluster/projects/index/controller.js @@ -24,8 +24,11 @@ export default Controller.extend({ return get(this,'model.projects').filterBy('clusterId', get(this,'scope.currentCluster.id')); }), - projectsWithoutNamespaces: computed('projects.@each.{id,state,clusterId}', 'rows.@each.namespaces', function(){ - return get(this, 'projects').filter(p => get(p, 'namespaces.length') <= 0); + projectsWithoutNamespaces: computed('projects.@each.{id,state,clusterId}', 'rows.@each.projectId', function(){ + return get(this, 'projects').filter(p => { + const namespaces = get(this, 'rows').filterBy('projectId', get(p, 'id')) || []; + return get(namespaces, 'length') <= 0; + }); }), }); diff --git a/app/authenticated/project/route.js b/app/authenticated/project/route.js index fd4259c18..30b623e3a 100644 --- a/app/authenticated/project/route.js +++ b/app/authenticated/project/route.js @@ -33,6 +33,7 @@ export default Route.extend(Preload,{ this.preload('dnsRecord'), this.preload('secret'), this.preload('service'), + this.preload('configmap'), this.preload('namespacedSecret'), this.preload('persistentVolumeClaim'), ]).then(() => { diff --git a/app/catalog-tab/launch/route.js b/app/catalog-tab/launch/route.js index 6d7df984e..fcefdeec4 100644 --- a/app/catalog-tab/launch/route.js +++ b/app/catalog-tab/launch/route.js @@ -98,19 +98,14 @@ export default Route.extend({ }); }); }, - setupController(controller, model) { - this._super(controller, model); - if (model.upgradeTemplate) { - controller.set('showName', false); - } - }, resetController: function (controller, isExiting/*, transition*/) { if (isExiting) { controller.set('namespaceId', null); + controller.set('template', null); controller.set('upgrade', null); controller.set('catalog', null); - controller.set('namespaceId', null); + controller.set('appId', null); } } }); diff --git a/app/catalog-tab/launch/template.hbs b/app/catalog-tab/launch/template.hbs index 2286a5c35..be782cccf 100644 --- a/app/catalog-tab/launch/template.hbs +++ b/app/catalog-tab/launch/template.hbs @@ -4,7 +4,6 @@ catalogApp=model.catalogApp namespaceResource=model.namespace parentRoute=parentRoute - showName=showName upgrade=model.upgradeTemplate templateResource=model.tpl templateKind=model.tplKind diff --git a/app/instance-initializers/nav.js b/app/instance-initializers/nav.js index 4d9ad44a2..55c1ed43f 100644 --- a/app/instance-initializers/nav.js +++ b/app/instance-initializers/nav.js @@ -152,6 +152,9 @@ const rootNav = [ ctx: [getClusterId], resource: ['project'], resourceScope: 'global', + condition: function() { + return this.get('cluster.isReady'); + } }, { scope: 'cluster', diff --git a/app/models/namespace.js b/app/models/namespace.js index c03083c70..f05ea523d 100644 --- a/app/models/namespace.js +++ b/app/models/namespace.js @@ -46,14 +46,16 @@ var Namespace = Resource.extend(StateCounts, { catalog: service(), scope: service(), router: service(), + projectStore: service('store'), globalStore: service(), + clusterStore: service(), - pods: hasMany('id', 'pod', 'namespaceId', 'store'), - workloads: hasMany('id', 'workload', 'namespaceId', 'store'), - services: hasMany('id', 'service', 'namespaceId', 'store'), - secrets: hasMany('id', 'namespacedSecret', 'namespaceId', 'store'), - ingress: hasMany('id', 'ingress', 'namespaceId', 'store'), - volumes: hasMany('id', 'persistentVolumeClaim', 'namespaceId', 'store'), + pods: hasMany('id', 'pod', 'namespaceId', 'projectStore', null, 'clusterStore'), + workloads: hasMany('id', 'workload', 'namespaceId', 'projectStore', null, 'clusterStore'), + services: hasMany('id', 'service', 'namespaceId', 'projectStore', null, 'clusterStore'), + secrets: hasMany('id', 'namespacedSecret', 'namespaceId', 'projectStore', null, 'clusterStore'), + ingress: hasMany('id', 'ingress', 'namespaceId', 'projectStore', null, 'clusterStore'), + volumes: hasMany('id', 'persistentVolumeClaim', 'namespaceId', 'projectStore', null, 'clusterStore'), project: reference('projectId', 'project', 'globalStore'), init() { diff --git a/app/models/storageclass.js b/app/models/storageclass.js index 6ac63d28a..e6500c6ef 100644 --- a/app/models/storageclass.js +++ b/app/models/storageclass.js @@ -62,6 +62,10 @@ export default Resource.extend({ }); }, + resetDefault() { + this.setDefault(false) + }, + edit() { get(this, 'router').transitionTo('authenticated.cluster.storage.classes.detail.edit', get(this, 'id')); }, @@ -80,8 +84,8 @@ export default Resource.extend({ annotations[DEFAULT_ANNOTATION] = 'true'; annotations[BETA_ANNOTATION] = 'true'; } else { - delete annotations[DEFAULT_ANNOTATION]; - delete annotations[BETA_ANNOTATION]; + annotations[DEFAULT_ANNOTATION] = 'false'; + annotations[BETA_ANNOTATION] = 'false'; } this.save(); @@ -92,6 +96,7 @@ export default Resource.extend({ let out = [ { label: 'action.makeDefault', icon: 'icon icon-star-fill', action: 'makeDefault', enabled: !isDefault }, + { label: 'action.resetDefault', icon: 'icon icon-star-line', action: 'resetDefault', enabled: isDefault }, ]; return out; diff --git a/app/models/template.js b/app/models/template.js index e69bcddea..10e22c70f 100644 --- a/app/models/template.js +++ b/app/models/template.js @@ -52,7 +52,7 @@ const Template = Resource.extend({ }.property('category','categories.[]'), categoryLowerArray: function() { - return this.get('categoryArray').map(x => (x||'').toLowerCase()); + return this.get('categoryArray').map(x => (x||'').underscore().toLowerCase()); }.property('categoryArray.[]'), supported: function() { diff --git a/app/styles/components/_catalog-box.scss b/app/styles/components/_catalog-box.scss index e77107253..93043591c 100644 --- a/app/styles/components/_catalog-box.scss +++ b/app/styles/components/_catalog-box.scss @@ -1,4 +1,4 @@ -$container-height: 300px; +$container-height: 321px; $container-width: 283px; $font-size: 12px; $line-height: 1.4; diff --git a/lib/global-admin/addon/catalog/controller.js b/lib/global-admin/addon/catalog/controller.js index 1e1f34d7e..9e01b78df 100644 --- a/lib/global-admin/addon/catalog/controller.js +++ b/lib/global-admin/addon/catalog/controller.js @@ -1,7 +1,7 @@ import { inject as service } from '@ember/service'; import Controller from '@ember/controller'; import C from 'ui/utils/constants'; -import { computed, get } from '@ember/object'; +import { computed, get, set } from '@ember/object'; import { send } from 'ember-metal/events'; export default Controller.extend({ @@ -11,15 +11,30 @@ export default Controller.extend({ settings: service(), catalog: service(), + togglingHelmIncubator: false, + togglingHelmStable: false, + togglingLibrary: false, + actions: { disableLibrary() { + if( get(this, 'togglingLibrary') ) { + return; + } + + set(this, 'togglingLibrary', true); get(this, 'library').delete().catch((err) => { get(this, 'growl').fromError('Error removing Library', err); }).finally(() => { - send(this, 'refresh') + set(this, 'togglingLibrary', false); + send(this, 'refresh'); }); }, enableLibrary() { + if( get(this, 'togglingLibrary') ) { + return; + } + + set(this, 'togglingLibrary', true); get(this, 'globalStore').createRecord({ type: 'catalog', name: C.CATALOG.LIBRARY_KEY, @@ -29,18 +44,30 @@ export default Controller.extend({ }).save().catch((err) => { get(this, 'growl').fromError('Error saving Library', err); }).finally(() => { - send(this, 'refresh') + set(this, 'togglingLibrary', false); + send(this, 'refresh'); }); }, disableHelmIncubator() { + if( get(this, 'togglingHelmIncubator') ) { + return; + } + + set(this, 'togglingHelmIncubator', true); get(this, 'helmIncubator').delete().catch((err) => { get(this, 'growl').fromError('Error removing Incubator', err); }).finally(() => { - send(this, 'refresh') + set(this, 'togglingHelmIncubator', false); + send(this, 'refresh'); }); }, enableHelmIncubator() { + if( get(this, 'togglingHelmIncubator') ) { + return; + } + + set(this, 'togglingHelmIncubator', true); get(this, 'globalStore').createRecord({ type: 'catalog', name: C.CATALOG.HELM_INCUBATOR_KEY, @@ -50,18 +77,30 @@ export default Controller.extend({ }).save().catch((err) => { get(this, 'growl').fromError('Error saving Incubator', err); }).finally(() => { - send(this, 'refresh') + set(this, 'togglingHelmIncubator', false); + send(this, 'refresh'); }); }, disableHelmStable() { + if( get(this, 'togglingHelmStable') ) { + return; + } + + set(this, 'togglingHelmStable', true); get(this, 'helmStable').delete().catch((err) => { get(this, 'growl').fromError('Error removing Stable', err); }).finally(() => { - send(this, 'refresh') + set(this, 'togglingHelmStable', false); + send(this, 'refresh'); }); }, enableHelmStable() { + if( get(this, 'togglingHelmStable') ) { + return; + } + + set(this, 'togglingHelmStable', true); get(this, 'globalStore').createRecord({ type: 'catalog', name: C.CATALOG.HELM_STABLE_KEY, @@ -71,28 +110,8 @@ export default Controller.extend({ }).save().catch((err) => { get(this, 'growl').fromError('Error saving Stable', err); }).finally(() => { - send(this, 'refresh') - }); - }, - - enableLibrary() { - get(this, 'globalStore').createRecord({ - type: 'catalog', - name: C.CATALOG.LIBRARY_KEY, - url: C.CATALOG.LIBRARY_VALUE, - branch: C.CATALOG.LIBRARY_BRANCH, - }).save().catch((err) => { - get(this, 'growl').fromError('Error saving Library', err); - }).finally(() => { - send(this, 'refresh') - }); - }, - - disableLibrary() { - get(this, 'stdLibrary').delete().catch((err) => { - get(this, 'growl').fromError('Error removing Library', err); - }).finally(() => { - send(this, 'refresh') + set(this, 'togglingHelmStable', false); + send(this, 'refresh'); }); }, diff --git a/lib/global-admin/addon/catalog/template.hbs b/lib/global-admin/addon/catalog/template.hbs index b1f65cf83..eada436a5 100644 --- a/lib/global-admin/addon/catalog/template.hbs +++ b/lib/global-admin/addon/catalog/template.hbs @@ -14,13 +14,13 @@
{{#if library}}
- +
{{else}}
- +
{{/if}}
@@ -34,13 +34,13 @@
{{#if helmStable}}
- +
{{else}}
- +
{{/if}}
@@ -54,13 +54,13 @@
{{#if helmIncubator}}
- +
{{else}}
- +
{{/if}}
diff --git a/lib/shared/addon/all-storage-classes/service.js b/lib/shared/addon/all-storage-classes/service.js new file mode 100644 index 000000000..8dda30de1 --- /dev/null +++ b/lib/shared/addon/all-storage-classes/service.js @@ -0,0 +1,24 @@ +import { get, set, computed } from '@ember/object'; +import { alias } from '@ember/object/computed'; +import Service, { inject as service } from '@ember/service'; + +export default Service.extend({ + clusterStore: service(), + + _allStorageClasses: null, + + init() { + const clusterStore = get(this, 'clusterStore'); + set(this, '_allStorageClasses', clusterStore.all('storageclass')); + }, + + storageClasses: computed('_allStorageClasses.[]', function () { + return get(this, '_allStorageClasses').sortBy('name'); + }), + + list: alias('storageClasses'), + + byId(id) { + return get(this, '_allStorageClasses').findBy('id', id); + }, +}); diff --git a/lib/shared/addon/catalog/service.js b/lib/shared/addon/catalog/service.js index 2cb8a1cb5..2ce83b2a2 100644 --- a/lib/shared/addon/catalog/service.js +++ b/lib/shared/addon/catalog/service.js @@ -3,7 +3,7 @@ import Service, { inject as service } from '@ember/service'; import { addQueryParams, uniqKeys } from 'shared/utils/util'; import C from 'shared/utils/constants'; import EmberObject from '@ember/object' -import { get } from '@ember/object'; +import { set, get, observer } from '@ember/object'; import { /* parseExternalId, */ parseHelmExternalId } from 'ui/utils/parse-externalid'; import { allSettled } from 'rsvp'; @@ -14,11 +14,37 @@ export default Service.extend({ settings: service(), store: service('store'), scope: service(), - app: service(), + app: service(), templateCache: null, catalogs: null, + _allCatalogs: null, + _refreshMap: null, + + init() { + const store = get(this,'globalStore'); + set(this, '_allCatalogs', store.all('catalog')); + set(this, '_refreshMap', {}); + }, + + catalogsDidChange: observer('_allCatalogs.@each.state', '_refreshMap', function() { + if ( get(this, 'templateCache') !== null ) { + const oldRefreshMap = get(this, '_refreshMap'); + const newRefreshMap = {}; + (get(this, '_allCatalogs') || []).forEach((c) => { + newRefreshMap[get(c, 'id')] = get(c, 'lastRefreshTimestamp'); + }); + let needRefresh = false; + for (let k of new Set([...Object.keys(newRefreshMap), ...Object.keys(oldRefreshMap)])) { + if ( !oldRefreshMap.hasOwnProperty(k) || !newRefreshMap.hasOwnProperty(k) || oldRefreshMap[k] !== newRefreshMap[k] ) { + needRefresh = true; + } + } + set(this, 'needRefresh', needRefresh); + } + }), + reset() { this.setProperties({ templateCache: null, @@ -99,11 +125,18 @@ export default Service.extend({ } // If the catalogIds dont match we need to go get the other catalog from the store since we do not cache all catalogs - if ( cache && cache.catalogId === catalogId) + if ( cache && cache.catalogId === catalogId && !get(this, 'needRefresh') ) { return resolve(this.filter(cache, params.category)); } + const catalogs = get(this, '_allCatalogs'); + const refreshMap = {}; + catalogs.forEach((c) => { + refreshMap[get(c, 'id')] = get(c, 'lastRefreshTimestamp'); + }); + set(this, '_refreshMap', refreshMap); + let url = this._addLimits(`${get(this,'app.apiEndpoint')}/templates`, qp); return get(this,'store').request({url: url, headers: {[C.HEADER.PROJECT_ID]: get(this,'scope.currentProject.id')}}).then((res) => { res.catalogId = catalogId; @@ -132,15 +165,15 @@ export default Service.extend({ }, filter(data, category) { - category = (category||'all').toLowerCase(); + category = (category||'').toLowerCase(); let categories = []; data.forEach((obj) => { categories.pushObjects(obj.get('categoryArray')); }); categories = uniqKeys(categories); - categories.unshift('all'); + categories.unshift(''); data = data.filter((tpl) => { - if ( category !== 'all' && !tpl.get('categoryLowerArray').includes(category) ) { + if ( category !== '' && !tpl.get('categoryLowerArray').includes(category) ) { return false; } diff --git a/lib/shared/addon/components/catalog-index/component.js b/lib/shared/addon/components/catalog-index/component.js index 4fe06606b..2deebad69 100644 --- a/lib/shared/addon/components/catalog-index/component.js +++ b/lib/shared/addon/components/catalog-index/component.js @@ -70,7 +70,7 @@ export default Component.extend({ categories.sort().forEach((ctgy) => { let normalized = ctgy.underscore(); - if (out[normalized] && ctgy !== 'all') { + if (out[normalized] && ctgy) { out[normalized].count++; } else { out[normalized] = { diff --git a/lib/shared/addon/components/catalog-index/template.hbs b/lib/shared/addon/components/catalog-index/template.hbs index 0604fdad1..73cf4b753 100644 --- a/lib/shared/addon/components/catalog-index/template.hbs +++ b/lib/shared/addon/components/catalog-index/template.hbs @@ -37,7 +37,7 @@ {{#if opt.localizedLabel}} {{t opt.localizedLabel}} {{else}} - {{opt.label}} +
{{opt.label}}
{{/if}} @@ -92,9 +92,10 @@ {{#each arrangedContent as |catalogItem|}} {{#catalog-box model=catalogItem showSource=showCatalogDropdown as |section|}} {{#if (eq section 'body')}} -

- {{catalogItem.displayName}} +

+ {{catalogItem.displayName}}

+
({{t 'generic.from'}} {{catalogItem.catalogId}})
{{catalogItem.description}}
{{else if (eq section 'footer')}} diff --git a/lib/shared/addon/components/cluster-driver/driver-amazoneks/template.hbs b/lib/shared/addon/components/cluster-driver/driver-amazoneks/template.hbs index b5e4c84e5..3e421f13b 100644 --- a/lib/shared/addon/components/cluster-driver/driver-amazoneks/template.hbs +++ b/lib/shared/addon/components/cluster-driver/driver-amazoneks/template.hbs @@ -51,6 +51,7 @@ {{#if (eq step 1)}} {{top-errors errors=errors}} {{top-errors errors=otherErrors}} + {{top-errors errors=clusterErrors}} {{save-cancel save="awsLogin" cancel=close @@ -95,6 +96,7 @@ {{top-errors errors=errors}} {{top-errors errors=otherErrors}} + {{top-errors errors=clusterErrors}} {{save-cancel editing=(eq mode 'edit') save="driverSave" cancel=close}} {{/if}} {{/accordion-list}} diff --git a/lib/shared/addon/components/cluster-driver/driver-azureaks/template.hbs b/lib/shared/addon/components/cluster-driver/driver-azureaks/template.hbs index 50bf91f81..b10252f58 100644 --- a/lib/shared/addon/components/cluster-driver/driver-azureaks/template.hbs +++ b/lib/shared/addon/components/cluster-driver/driver-azureaks/template.hbs @@ -190,4 +190,5 @@ {{top-errors errors=errors}} {{top-errors errors=otherErrors}} +{{top-errors errors=clusterErrors}} {{save-cancel editing=(eq mode 'edit') save="driverSave" cancel=close}} diff --git a/lib/shared/addon/components/cluster-driver/driver-googlegke/template.hbs b/lib/shared/addon/components/cluster-driver/driver-googlegke/template.hbs index aea6122c9..d2cf804a5 100644 --- a/lib/shared/addon/components/cluster-driver/driver-googlegke/template.hbs +++ b/lib/shared/addon/components/cluster-driver/driver-googlegke/template.hbs @@ -22,6 +22,7 @@ {{top-errors errors=errors}} {{top-errors errors=otherErrors}} + {{top-errors errors=clusterErrors}} {{save-cancel createLabel="clusterNew.googlegke.checkServiceAccount" savingLabel="clusterNew.googlegke.checkingServiceAccount" @@ -138,5 +139,6 @@ {{top-errors errors=errors}} {{top-errors errors=otherErrors}} + {{top-errors errors=clusterErrors}} {{save-cancel editing=(eq mode 'edit') save="driverSave" cancel=close}} {{/if}} diff --git a/lib/shared/addon/components/cluster-driver/driver-import/component.js b/lib/shared/addon/components/cluster-driver/driver-import/component.js index 12294e27c..302ec6a10 100644 --- a/lib/shared/addon/components/cluster-driver/driver-import/component.js +++ b/lib/shared/addon/components/cluster-driver/driver-import/component.js @@ -54,6 +54,13 @@ export default Component.extend(ClusterDriver, { set(this, 'token', token); set(this, 'loading', false); + }).catch((err) => { + if ( this.isDestroyed || this.isDestroying ) { + return; + } + + get(this,'growl').fromError('Error getting command', err); + set(this, 'loading', false); }); } }); diff --git a/lib/shared/addon/components/cluster-driver/driver-import/template.hbs b/lib/shared/addon/components/cluster-driver/driver-import/template.hbs index 3a53113b9..c2d0e4025 100644 --- a/lib/shared/addon/components/cluster-driver/driver-import/template.hbs +++ b/lib/shared/addon/components/cluster-driver/driver-import/template.hbs @@ -35,6 +35,7 @@ {{#if isEdit}} {{top-errors errors=otherErrors}} + {{top-errors errors=clusterErrors}} {{save-cancel save="driverSave" editing=true cancel="close"}} {{else}}
+ {{#unless customizeNamespace}} +
+ {{t 'newCatalog.customizeNamespace' namespaceId=primaryResource.name htmlSafe=true}} + {{#unless editing}} + + {{/unless}} +
+ {{/unless}}
- {{#if showName}} - {{#advanced-section}} -
+ {{#if customizeNamespace}} +
- {{form-namespace - namespace=primaryResource - mode='reuse' - errors=namespaceErrors - }} - {{/advanced-section}} + {{form-namespace + namespace=primaryResource + mode='reuse' + errors=namespaceErrors + }} {{/if}}
diff --git a/lib/shared/addon/components/page-header/component.js b/lib/shared/addon/components/page-header/component.js index 464a1999e..92d0a4fad 100644 --- a/lib/shared/addon/components/page-header/component.js +++ b/lib/shared/addon/components/page-header/component.js @@ -136,6 +136,7 @@ export default Component.extend(HoverDropdown, { }.observes( 'pageScope', 'clusterId', + 'cluster.isReady', 'projectId', 'stacks.@each.group', `prefs.${C.PREFS.ACCESS_WARNING}`, diff --git a/lib/shared/addon/components/schema/input-secret/component.js b/lib/shared/addon/components/schema/input-secret/component.js index dc4a8c6a8..a7041d1a9 100644 --- a/lib/shared/addon/components/schema/input-secret/component.js +++ b/lib/shared/addon/components/schema/input-secret/component.js @@ -25,8 +25,8 @@ export default Component.extend({ init() { this._super(...arguments); - set(this,'projectSecrets', get(this,'store').all('secret')); - set(this,'namespaceSecrets', get(this,'store').all('namespacedSecret')); + set(this,'projectSecrets', get(this,'store').all('secret').filterBy('type','secret')); + set(this,'namespaceSecrets', get(this,'store').all('namespacedSecret').filterBy('type','namespacedSecret')); let def = get(this,'value') || get(this,'field.default'); if ( def && !get(this,'selected') ) { diff --git a/lib/shared/addon/components/schema/input-storageclass/component.js b/lib/shared/addon/components/schema/input-storageclass/component.js new file mode 100644 index 000000000..04eab13c0 --- /dev/null +++ b/lib/shared/addon/components/schema/input-storageclass/component.js @@ -0,0 +1,50 @@ +import { alias } from '@ember/object/computed'; +import { get, set, observer} from '@ember/object'; +import { next } from '@ember/runloop'; +import { inject as service } from '@ember/service'; +import Component from '@ember/component'; +import layout from './template'; + +export default Component.extend({ + layout, + allStorageClasses: service(), + + field: null, + value: null, + + selected: null, + disabled: false, + + storageClassesOptions: alias('allStorageClasses.list'), + + init() { + this._super(...arguments); + + const def = get(this,'value') || get(this,'field.default'); + if ( def && !get(this,'selected') ) { + const exact = get(this, 'storageClassesOptions').findBy('id', def); + + next(() => { + if ( exact ) { + set(this, 'selected', get(exact, 'id') || null); + } else { + set(this, 'selected', null); + } + }); + } + }, + + selectedChanged: observer('selected', function() { + let id = get(this,'selected'); + let str = null; + + if ( id ) { + const storageClass = get(this, 'storageClassesOptions').findBy('id', id); + if ( storageClass ) { + str = get(storageClass, 'id'); + } + } + + set(this,'value', str); + }), +}); diff --git a/lib/shared/addon/components/schema/input-storageclass/template.hbs b/lib/shared/addon/components/schema/input-storageclass/template.hbs new file mode 100644 index 000000000..2f6c8c4fe --- /dev/null +++ b/lib/shared/addon/components/schema/input-storageclass/template.hbs @@ -0,0 +1,9 @@ +{{searchable-select + content=storageClassesOptions + class="form-control" + value=selected + prompt=(t 'schema.inputStorageClass.prompt') + optionValuePath="id" + optionLabelPath="displayName" + placeholder=(t 'schema.inputStorageClass.prompt') +}} \ No newline at end of file diff --git a/lib/shared/addon/utils/constants.js b/lib/shared/addon/utils/constants.js index 1d36a66b4..05cd728d8 100644 --- a/lib/shared/addon/utils/constants.js +++ b/lib/shared/addon/utils/constants.js @@ -574,6 +574,7 @@ C.SUPPORTED_SCHEMA_INPUTS= [ 'multiline', 'password', 'service', + 'storageclass', 'string', 'masked', 'secret', diff --git a/lib/shared/addon/utils/util.js b/lib/shared/addon/utils/util.js index c9a51e536..542983f8c 100644 --- a/lib/shared/addon/utils/util.js +++ b/lib/shared/addon/utils/util.js @@ -104,8 +104,8 @@ export function absoluteUrl(url) { } export function validateEndpoint(str) { - //credit to https://stackoverflow.com/questions/9208814/validate-ipv4-ipv6-and-hostname - return /^(([0-9]|[1-9][0-9]|1[0-9]{2}|2[0-4][0-9]|25[0-5])\.){3}([0-9]|[1-9][0-9]|1[0-9]{2}|2[0-4][0-9]|25[0-5])$|^(([a-zA-Z]|[a-zA-Z][a-zA-Z0-9\-]*[a-zA-Z0-9])\.)*([A-Za-z]|[A-Za-z][A-Za-z0-9\-]*[A-Za-z0-9])$|^\s*((([0-9A-Fa-f]{1,4}:){7}([0-9A-Fa-f]{1,4}|:))|(([0-9A-Fa-f]{1,4}:){6}(:[0-9A-Fa-f]{1,4}|((25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)(\.(25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)){3})|:))|(([0-9A-Fa-f]{1,4}:){5}(((:[0-9A-Fa-f]{1,4}){1,2})|:((25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)(\.(25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)){3})|:))|(([0-9A-Fa-f]{1,4}:){4}(((:[0-9A-Fa-f]{1,4}){1,3})|((:[0-9A-Fa-f]{1,4})?:((25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)(\.(25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)){3}))|:))|(([0-9A-Fa-f]{1,4}:){3}(((:[0-9A-Fa-f]{1,4}){1,4})|((:[0-9A-Fa-f]{1,4}){0,2}:((25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)(\.(25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)){3}))|:))|(([0-9A-Fa-f]{1,4}:){2}(((:[0-9A-Fa-f]{1,4}){1,5})|((:[0-9A-Fa-f]{1,4}){0,3}:((25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)(\.(25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)){3}))|:))|(([0-9A-Fa-f]{1,4}:){1}(((:[0-9A-Fa-f]{1,4}){1,6})|((:[0-9A-Fa-f]{1,4}){0,4}:((25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)(\.(25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)){3}))|:))|(:(((:[0-9A-Fa-f]{1,4}){1,7})|((:[0-9A-Fa-f]{1,4}){0,5}:((25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)(\.(25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)){3}))|:)))(%.+)?\s*$/.test(str); + //credit to https://stackoverflow.com/questions/4460586/javascript-regular-expression-to-check-for-ip-addresses + return /^(25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\.(25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\.(25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\.(25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)$/.test(str); } export function addAuthorization(url, user, pass) { diff --git a/lib/shared/app/all-storage-classes/service.js b/lib/shared/app/all-storage-classes/service.js new file mode 100644 index 000000000..9596a3e22 --- /dev/null +++ b/lib/shared/app/all-storage-classes/service.js @@ -0,0 +1 @@ +export { default } from 'shared/all-storage-classes/service'; diff --git a/lib/shared/app/components/form-endpoints/component.js b/lib/shared/app/components/form-endpoints/component.js new file mode 100644 index 000000000..b6e90f11c --- /dev/null +++ b/lib/shared/app/components/form-endpoints/component.js @@ -0,0 +1 @@ +export { default } from 'shared/components/form-endpoints/component'; \ No newline at end of file diff --git a/lib/shared/app/components/schema/input-storageclass/component.js b/lib/shared/app/components/schema/input-storageclass/component.js new file mode 100644 index 000000000..37717d444 --- /dev/null +++ b/lib/shared/app/components/schema/input-storageclass/component.js @@ -0,0 +1 @@ +export { default } from 'shared/components/schema/input-storageclass/component'; diff --git a/translations/en-us.yaml b/translations/en-us.yaml index 272c89f20..b46ceaa22 100644 --- a/translations/en-us.yaml +++ b/translations/en-us.yaml @@ -22,6 +22,7 @@ generic: containers: Containers created: Created createdDate: "Created {date}" + customize: Customize default: Default description: Description details: Details @@ -39,6 +40,7 @@ generic: entrypoint: Entrypoint environment: Environment expandAll: Expand All + from: from gigabyte: 'GB' generic: 'Generic' hardware: Hardware @@ -178,6 +180,13 @@ accountsPage: appDetailPage: header: "App: {appName}" + endpoints: + title: Endpoints + detail: 'Public Endpoints of this application' + endpoint: Endpoint + protocol: Protocol + noData: No public endpoints were created for this application. + noMatch: No public endpoints match the current search. notes: title: Notes detail: 'Instructions on how to use this application' @@ -1957,7 +1966,7 @@ confirmDelete: largeDeleteText: '{key} and {othersCount} others' containerLogs: - title: "Logs: {instanceName}" + title: "Logs: " onlyCombined: "Note: Only combined stdout/stderr logs are available for this container because it was run with the TTY (-t) flag." combined: Combined stdout: Standard Out @@ -3934,6 +3943,9 @@ inputPassword: inputTextFile: tooltip: Read from a file +uploadFile: + label: Read from File + podsSection: title: Pods detail: Pods in this workload @@ -4277,12 +4289,14 @@ newCatalog: maintainedBy: Maintained by community members maintainer: "Maintainer:" newNamespace: New Namespace + customizeNamespace: "This application will be deployed into the {namespaceId} namespace" newAppDetail: Choose application version and namespace for the application appInfo: Detailed Descriptions appInfoDetail: Application information and user guid noConfig: This template has no configuration options official: Officially Certified preview: Preview + seeMore: More information... saveConfigure: Configure saveNew: Launch saveUpgrade: Upgrade @@ -5064,6 +5078,8 @@ schema: custom: Custom inputDnsRecord: prompt: Choose a Service... + inputStorageClass: + prompt: Use the default class... inputSecret: prompt: Choose a Secret... @@ -5334,6 +5350,7 @@ action: garbageCollect: Cleanup logs: View Logs makeDefault: Set as default + resetDefault: Reset default nodeConfig: Download Keys move: Move pause: Pause Orchestration diff --git a/translations/es-es.yaml b/translations/es-es.yaml index 6ad3c8d24..9e95a75a9 100644 --- a/translations/es-es.yaml +++ b/translations/es-es.yaml @@ -1076,7 +1076,7 @@ confirmDelete: cancelAction: Cancelar largeDeleteText: '{key} y {othersCount} otros' containerLogs: - title: 'Registros: {instanceName}' + title: 'Registros: ' onlyCombined: "Nota: Solo estarán disponibles los registros combinados de stdout y stderr para este contenedor porque fue lanzado son la instrucción TTY (-t)." combined: Combinado stdout: Standard Out diff --git a/translations/fr-fr.yaml b/translations/fr-fr.yaml index 4cbc81be6..9699fd47e 100644 --- a/translations/fr-fr.yaml +++ b/translations/fr-fr.yaml @@ -990,7 +990,7 @@ confirmDelete: cancelAction: Annuler largeDeleteText: '{key} et {othersCount} d’autres' containerLogs: - title: 'Journaux : {instanceName}' + title: 'Journaux : ' onlyCombined: "Remarque : Seuls les journaux combinés des sorties standard et d'erreurs est disponible pour ce conteneur parce qu’il a été exécuté avec l'option TTY (-t) ." combined: Combiné stdout: Sortie Standard diff --git a/translations/hu-hu.yaml b/translations/hu-hu.yaml index 952bd01f3..85f5013a6 100644 --- a/translations/hu-hu.yaml +++ b/translations/hu-hu.yaml @@ -830,7 +830,7 @@ confirmDelete: cancelAction: Mégsem largeDeleteText: '{key} és {othersCount} mások' containerLogs: - title: 'Naplók: {instanceName}' + title: 'Naplók: ' combined: Kombinált stdout: Szabványos kimenet stderr: Standard hiba diff --git a/translations/ja-jp.yaml b/translations/ja-jp.yaml index 9da21019a..3636a5657 100644 --- a/translations/ja-jp.yaml +++ b/translations/ja-jp.yaml @@ -1086,7 +1086,7 @@ confirmDelete: confirmAction: 削除 cancelAction: キャンセル containerLogs: - title: 'ログ: {instanceName}' + title: 'ログ: ' onlyCombined: "Note: このコンテナでは TTY (-t) フラグが有効なため、標準出力/標準エラー出力が結合されたログのみが利用可能です。" combined: 結合された出力 stdout: 標準出力 diff --git a/translations/ru-ru.yaml b/translations/ru-ru.yaml index f4bf62056..ab1a44515 100644 --- a/translations/ru-ru.yaml +++ b/translations/ru-ru.yaml @@ -929,7 +929,7 @@ confirmDelete: confirmAction: Удалить cancelAction: Отмена containerLogs: - title: 'Логи: {instanceName}' + title: 'Логи: ' combined: Комбинированный stderr: Ошибки protip: 'Подсказка: удерживайте клавишу {key}, чтобы открыть журнал событий в новом окне.' diff --git a/translations/uk-ua.yaml b/translations/uk-ua.yaml index 81f303bed..c573fdad5 100644 --- a/translations/uk-ua.yaml +++ b/translations/uk-ua.yaml @@ -1105,7 +1105,7 @@ confirmDelete: confirmAction: Видалити cancelAction: Відміна containerLogs: - title: 'Логи: {instanceName}' + title: 'Логи: ' onlyCombined: "Примітка: Для цього контейнера доступні комбіновані stdout/stderr журнали, тому що він був запущений з TTY (-t)." combined: Комбіновані stdout: Стандартний вивід diff --git a/translations/zh-hans.yaml b/translations/zh-hans.yaml index 582e97981..6f90b5af0 100644 --- a/translations/zh-hans.yaml +++ b/translations/zh-hans.yaml @@ -1137,7 +1137,7 @@ confirmDelete: cancelAction: 取消 largeDeleteText: '{key} 及 {othersCount} 其他' containerLogs: - title: '日志: {instanceName}' + title: '日志: ' onlyCombined: "注意: 此容器运行时带有TTY(-t)参数,仅有合并的标准输出和标准错误日志可见" combined: 合并日志 stdout: 标准输出