import { computed, get } from '@ember/object'; import { inject as service } from '@ember/service'; import Resource from 'ember-api-store/models/resource'; import { parseExternalId } from 'ui/utils/parse-externalid'; import C from 'ui/utils/constants'; import { download } from 'shared/utils/util'; import { hasMany, reference } from 'ember-api-store/utils/denormalize'; import StateCounts from 'ui/mixins/state-counts'; export function activeIcon(ns) { if ( ns.get('system') ) { return 'icon icon-gear'; } else { return 'icon icon-layers'; } } export function tagsToArray(str, normalize=true) { return (str||'').split(/\s*,\s*/) .map((tag) => { if (normalize) { return normalizeTag(tag); } else { return tag; } }) .filter((tag) => tag.length > 0); } export function normalizeTag(name) { return (name||'').trim().toLowerCase(); } export function normalizeTags(ary) { return (ary||[]).map(normalizeTag).filter(str => str.length > 0); } export function tagChoices(all) { let choices = []; (all||[]).forEach((ns) => { choices.addObjects(ns.get('tags')||[]); }); return choices; } var Namespace = Resource.extend(StateCounts, { type: 'namespace', k8s: service(), modalService: service('modal'), catalog: service(), scope: service(), router: service(), globalStore: service(), pods: hasMany('id', 'pod', 'namespaceId', 'store'), workloads: hasMany('id', 'workload', 'namespaceId', 'store'), project: reference('projectId', 'project', 'globalStore'), init() { this._super(...arguments); // @TODO-2.0 this.defineStateCounts('services', 'serviceStates', 'serviceCountSort'); }, actions: { startAll() { return this.doAction('startall'); }, pauseAll() { return this.doAction('pauseall'); }, stopAll() { return this.doAction('stopall'); }, promptStop() { this.get('modalService').toggleModal('modal-confirm-deactivate', { originalModel: this, action: 'stopAll' }); }, edit() { this.get('modalService').toggleModal('modal-edit-namespace', this); }, exportConfig() { download(this.linkFor('composeConfig')); }, addContainer() { this.get('router').transitionTo('containers.run', this.get('projectId'), {queryParams: {namespaceId: this.get('id')}}); }, viewCode() { this.get('router').transitionTo('stack.code', this.get('id')); }, viewGraph() { this.get('router').transitionTo('stack.graph', this.get('id')); }, delete() { return this._super().then(() => { if ( this.get('application.currentRouteName') === 'stack.index' ) { this.get('router').transitionTo('containers'); } }); }, move() { this.get('modalService').toggleModal('modal-move-namespace', this); }, }, availableActions: computed('actionLinks.{exportconfig}','links.{update,remove}','externalIdInfo.kind','canStartAll','canPauseAll','canStopAll', function() { let a = this.get('actionLinks'); let l = this.get('links'); if ( this.get('externalIdInfo.kind') === C.EXTERNAL_ID.KIND_KUBERNETES ) { return []; } let out = [ { label: 'action.move', icon: 'icon icon-fork', action: 'move', enabled: true, bulkable: true}, { label: 'action.addContainer', icon: 'icon icon-container', action: 'addContainer', enabled: !!this.get('projectId') }, { divider: true }, { label: 'action.pause', icon: 'icon icon-pause', action: 'pauseAll', enabled: this.get('canPauseAll')}, { label: 'action.startAll', icon: 'icon icon-play', action: 'startAll', enabled: this.get('canStartAll')}, { label: 'action.stopAll', icon: 'icon icon-stop', action: 'promptStop', enabled: this.get('canStopAll'), altAction: 'stopAll' }, { divider: true }, { label: 'action.edit', icon: 'icon icon-edit', action: 'edit', enabled: !!l.update }, { label: 'action.viewConfig', icon: 'icon icon-files', action: 'viewCode', enabled: !!a.exportconfig }, { label: 'action.exportConfig', icon: 'icon icon-download', action: 'exportConfig', enabled: !!a.exportconfig }, // { label: 'action.viewGraph', icon: 'icon icon-share', action: 'viewGraph', enabled: true }, { divider: true }, { label: 'action.remove', icon: 'icon icon-trash', action: 'promptDelete', enabled: !!l.remove, bulkable: true, altAction: 'delete'}, { divider: true }, { label: 'action.viewInApi', icon: 'icon icon-external-link', action: 'goToApi', enabled: true }, ]; return out; }), canStartAll: computed('services.@each.state','actionLinks.startall', function() { if ( !this.hasAction('startall') ) { return false; } var count = this.get('services.length') || 0; if ( count === 0 ) { return false; } return this.get('services').filterBy('actionLinks.activate').get('length') > 0; }), canPauseAll: computed('services.@each.state','actionLinks.pauseall', function() { if ( !this.hasAction('pauseall') ) { return false; } var count = this.get('services.length') || 0; if ( count === 0 ) { return false; } return this.get('services').filterBy('actionLinks.pause').get('length') > 0; }), canStopAll: computed('services.@each.state','actionLinks.stopall', function() { if ( !this.hasAction('stopall') ) { return false; } var services = this.get('workloads'); var containers = this.get('pods').filterBy('workloadId', null); var countS = (services.length || 0); var countC = (containers.length || 0); if ( (countS + countC) === 0 ) { return false; } return services.filterBy('actionLinks.deactivate').get('length') > 0 && containers.filterBy('actionLinks.stop').get('length'); }), canViewConfig: computed('actionLinks.exportconfig', function() { return !!this.get('actionLinks.exportconfig'); }), combinedState: computed('state', 'healthState', function() { var stack = this.get('state'); var health = this.get('healthState'); if ( stack === 'active' && health ) { return health; } else { return stack; } }), externalIdInfo: computed('externalId', function() { return parseExternalId(this.get('externalId')); }), isDefault: computed('name', function() { return (this.get('name')||'').toLowerCase() === 'default'; }), isEmpty: computed('pods.length', 'workloads.length', function() { return (get(this, 'pods.length')||0 + get(this, 'workloads.length')||0) === 0; }), 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; }), // This only works if the templates have already been loaded elsewhere... catalogTemplate: computed('externalIdInfo.templateId', function() { return this.get('catalog').getTemplateFromCache(this.get('externalIdInfo.templateId')); }), icon: computed('catalogTemplate', function() { let tpl = this.get('catalogTemplate'); if ( tpl ) { return tpl.linkFor('icon'); } }), 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('tags.[]', function() { return normalizeTags(this.get('tags')); }), hasTags(want) { if ( !want || !want.length ) { return true; } want = normalizeTags(want); let have = this.get('normalizedTags'); for ( let i = 0 ; i < want.length ; i++ ) { if ( !have.includes(want[i]) ) { return false; } } return true; }, }); Namespace.reopenClass({ stateMap: { 'active': {icon: activeIcon, color: 'text-success'}, 'rolling-back': {icon: 'icon icon-history', color: 'text-info'}, 'upgraded': {icon: 'icon icon-arrow-circle-up', color: 'text-info'}, 'upgrading': {icon: 'icon icon-arrow-circle-up', color: 'text-info'}, } }); export default Namespace;