import Errors from 'ui/utils/errors'; import { get, set, setProperties } from '@ember/object'; import { equal } from '@ember/object/computed'; import { next } from '@ember/runloop'; import { inject as service } from '@ember/service'; import Component from '@ember/component'; import NewOrEdit from 'shared/mixins/new-or-edit'; import { debouncedObserver } from 'ui/utils/debounce'; import C from 'ui/utils/constants'; import ChildHook from 'shared/mixins/child-hook'; import { flattenLabelArrays } from 'shared/mixins/manage-labels'; import layout from './template'; export default Component.extend(NewOrEdit, ChildHook, { clusterStore: service(), intl: service(), prefs: service(), scope: service(), settings: service(), layout, tagName: 'form', isUpgrade: false, service: null, launchConfig: null, launchConfigIndex: null, namespace: null, scale: 1, scaleMode: null, serviceLinksArray: null, isRequestedHost: null, upgradeOptions: null, separateLivenessCheck: false, // Errors from components commandErrors: null, volumeErrors: null, networkingErrors: null, secretsErrors: null, readyCheckErrors: null, liveCheckErrors: null, schedulingErrors: null, securityErrors: null, scaleErrors: null, imageErrors: null, portErrors: null, namespaceErrors: null, labelErrors: null, annotationErrors: null, // ---------------------------------- userLabels: null, advanced: false, header: '', isSidekick: equal('scaleMode', 'sidekick'), init() { window.nec = this; this._super(...arguments); if (get(this, 'launchConfig') && !get(this, 'launchConfig.environmentFrom')) { set(this, 'launchConfig.environmentFrom', []); } const service = get(this, 'service'); if (!get(this, 'isSidekick') && service && !get(service, 'scheduling')) { set(service, 'scheduling', { node: {} }); } if (!get(this, 'isSidekick')) { setProperties(this, { description: get(this, 'service.description'), scale: get(this, 'service.scale'), scheduling: get(this, 'service.scheduling'), }); } else { set(this, 'description', get(this, 'launchConfig.description')); } set(this, 'name', get(this, 'launchConfig.name')); let namespaceId = null; namespaceId = get(this, 'service.namespaceId'); if (namespaceId) { let namespace = get(this, 'clusterStore').getById('namespace', namespaceId); if (namespace) { set(this, 'namespace', namespace); } } if (!get(this, 'separateLivenessCheck')) { const ready = get(this, 'launchConfig.readinessProbe'); const live = get(this, 'launchConfig.livenessProbe'); const readyStr = JSON.stringify(ready); const liveStr = JSON.stringify(live); if (readyStr !== liveStr) { set(this, 'separateLivenessCheck', true); } } if ( !get(this, 'isSidekick') ) { this.labelsChanged(); } }, didInsertElement() { const input = this.$("INPUT[type='text']")[0]; if (input) { input.focus(); } }, actions: { setImage(uuid) { set(this, 'launchConfig.image', uuid); }, setLabels(section, labels) { set(this, `${ section }Labels`, labels); }, setRequestedHostId(hostId) { set(this, 'launchConfig.requestedHostId', hostId); }, setUpgrade(upgrade) { set(this, 'upgradeOptions', upgrade); }, done() { this.sendAction('done'); }, cancel() { this.sendAction('cancel'); }, toggleSeparateLivenessCheck() { set(this, 'separateLivenessCheck', !get(this, 'separateLivenessCheck')); }, removeSidekick(idx) { var ary = get(this, 'primaryService.secondaryLaunchConfigs'); ary.removeAt(idx); }, }, // Labels labelsChanged: debouncedObserver( 'userLabels.@each.{key,value}', function() { let out = flattenLabelArrays( get(this, 'userLabels'), ); set(this, 'service.labels', out); } ), updateHeader: function() { let args = {}; let k = 'newContainer.'; k += `${ get(this, 'isUpgrade') ? 'upgrade' : 'add' }.`; if (get(this, 'isSidekick')) { let svc = get(this, 'service'); if (svc && get(svc, 'id')) { k += 'sidekickName'; args = { name: get(this, 'service.displayName') }; } else { k += 'sidekick'; } } else if (get(this, 'isGlobal')) { k += 'globalService'; } else { k += 'service'; } next(() => { if (this.isDestroyed || this.isDestroying) { return; } set(this, 'header', get(this, 'intl').t(k, args)); }); }.observes('isUpgrade', 'isSidekick', 'isGlobal', 'service.displayName', 'intl.locale').on('init'), // ---------------------------------- // ---------------------------------- // Save // ---------------------------------- validate() { let pr = get(this, 'primaryResource'); let errors = pr.validationErrors() || []; const lc = get(this, 'launchConfig'); const quotaErrors = lc.validateQuota(); errors.pushObjects(quotaErrors); if ( get(quotaErrors, 'length') > 0 ) { setProperties(this, { advanced: true, securitySectionExpanded: true }); } (get(this, 'service.secondaryLaunchConfigs') || []).forEach((slc) => { slc.validationErrors().forEach((err) => { errors.push(`${ get(slc, 'displayName') }: ${ err }`); }); }); // Errors from components errors.pushObjects(get(this, 'commandErrors') || []); errors.pushObjects(get(this, 'volumeErrors') || []); errors.pushObjects(get(this, 'networkingErrors') || []); errors.pushObjects(get(this, 'secretsErrors') || []); errors.pushObjects(get(this, 'readyCheckErrors') || []); errors.pushObjects(get(this, 'liveCheckErrors') || []); errors.pushObjects(get(this, 'schedulingErrors') || []); errors.pushObjects(get(this, 'securityErrors') || []); errors.pushObjects(get(this, 'scaleErrors') || []); errors.pushObjects(get(this, 'imageErrors') || []); errors.pushObjects(get(this, 'portErrors') || []); errors.pushObjects(get(this, 'namespaceErrors') || []); errors.pushObjects(get(this, 'labelErrors') || []); errors.pushObjects(get(this, 'annotationErrors') || []); errors = errors.uniq(); if (get(errors, 'length')) { set(this, 'errors', errors); if ( get(this, 'isSidekick') && !get(this, 'isUpgrade') ) { get(pr, 'secondaryLaunchConfigs').pop(); } return false; } set(this, 'errors', null); return true; }, willSave() { let intl = get(this, 'intl'); let pr; let nameResource; let lc = get(this, 'launchConfig'); let name = (get(this, 'name') || '').trim().toLowerCase(); let service = get(this, 'service'); let readinessProbe = get(lc, 'readinessProbe'); if (!get(this, 'separateLivenessCheck')) { if ( readinessProbe ) { const livenessProbe = Object.assign({}, readinessProbe); set(livenessProbe, 'successThreshold', 1); set(lc, 'livenessProbe', livenessProbe); } else { set(lc, 'livenessProbe', null); } } const uid = get(lc, 'uid'); if ( uid === '' ) { set(lc, 'uid', null); } if (get(this, 'isSidekick')) { let errors = []; if (!service) { errors.push(get(this, 'intl').t('newContainer.errors.noSidekick')); set(this, 'errors', errors); return false; } if (!name) { errors.push(intl.t('validation.required', { key: intl.t('formNameDescription.name.label') })); set(this, 'errors', errors); return false; } pr = service; nameResource = lc; let slc = get(pr, 'secondaryLaunchConfigs'); if (!slc) { slc = []; set(pr, 'secondaryLaunchConfigs', slc); } let lci = get(this, 'launchConfigIndex'); if (lci === undefined || lci === null) { // If it's a new sidekick, add it to the end of the list lci = slc.length; } else { lci = parseInt(lci, 10) } let duplicate = pr.containers.find((x, idx) => idx !== lci + 1 && get(x, 'name').toLowerCase() === name); if (duplicate) { errors.push(intl.t('newContainer.errors.duplicateName', { name, service: get(duplicate, 'displayName') })); set(this, 'errors', errors); return false; } slc[lci] = lc; set(lc, 'name', name); set(pr, 'containers', [pr.containers[0]]); pr.containers.pushObjects(slc); } else { service.clearConfigsExcept(`${ get(this, 'scaleMode') }Config`); if ( get(this, 'scaleMode') === 'statefulSet' && !get(service, 'statefulSetConfig.serviceName') ) { set(service, 'statefulSetConfig.serviceName', name); } pr = service; nameResource = pr; set(pr, 'scale', get(this, 'scale')); const containers = get(pr, 'containers'); if (!containers) { set(pr, 'containers', []); } else { set(lc, 'name', name); containers[0] = lc } } nameResource.setProperties({ name, description: get(this, 'description'), }); set(this, 'primaryResource', pr); set(this, 'originalPrimaryResource', pr); let errors = []; if (!get(this, 'namespace.name')) { errors.push(intl.t('validation.required', { key: intl.t('generic.namespace') })); set(this, 'errors', errors); return false; } set(pr, 'namespaceId', get(this, 'namespace.id') || '__placeholder__'); const self = this; const sup = this._super; pr.updateTimestamp(); return this.applyHooks('_beforeSaveHooks').then(() => { set(pr, 'namespaceId', get(this, 'namespace.id')); return this.applyHooks('_volumeHooks').then(() => sup.apply(self, ...arguments)) .catch((err) => { set(this, 'errors', [Errors.stringify(err)]); }); }) .catch((err) => { set(this, 'errors', [Errors.stringify(err)]); }); }, doneSaving() { if (!get(this, 'isUpgrade')) { let scaleMode = get(this, 'scaleMode'); if (scaleMode === 'sidekick') { // Remember sidekick as service since you're not // likely to want to add many sidekicks in a row scaleMode = 'deployment'; } set(this, `prefs.${ C.PREFS.LAST_SCALE_MODE }`, scaleMode); set(this, `prefs.${ C.PREFS.LAST_IMAGE_PULL_POLICY }`, get(this, 'launchConfig.imagePullPolicy')); set(this, `prefs.${ C.PREFS.LAST_NAMESPACE }`, get(this, 'namespace.id')); } this.sendAction('done'); }, });