ui/lib/shared/addon/components/container/new-edit/component.js

388 lines
11 KiB
JavaScript

import { equal } from '@ember/object/computed';
import { next } from '@ember/runloop';
import { resolve, reject, all } from 'rsvp';
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 { flattenLabelArrays } from 'shared/mixins/manage-labels';
import Util from 'ui/utils/util';
import layout from './template';
export default Component.extend(NewOrEdit, {
layout,
intl: service(),
prefs: service(),
settings: service(),
tagName: 'form',
isService: false,
isUpgrade: false,
service: null,
launchConfig: null,
launchConfigIndex: null, // null: not valid here, -1: Primary LC, 0-n: Sidekick SLCs
stack: null,
scale: 1,
mode: null,
serviceLinksArray: null,
isRequestedHost: null,
upgradeOptions: null,
volumesToCreate: null,
// Errors from components
commandErrors: null,
volumeErrors: null,
networkingErrors: null,
secretsErrors: null,
healthCheckErrors: null,
schedulingErrors: null,
securityErrors: null,
scaleErrors: null,
imageErrors: null,
portErrors: null,
stackErrors: null,
metadataErrors: null,
actions: {
setImage(uuid) {
this.set('launchConfig.image', uuid);
},
setLabels(section,labels) {
this.set(section+'Labels', labels);
},
setRequestedHostId(hostId) {
this.set('launchConfig.requestedHostId', hostId);
},
setUpgrade(upgrade) {
this.set('upgradeOptions', upgrade);
},
done() {
this.sendAction('done');
},
cancel() {
this.sendAction('cancel');
},
addSidekick() {
var ary = this.get('service.secondaryLaunchConfigs');
ary.pushObject(this.get('store').createRecord({
type: 'secondaryLaunchConfig',
kind: 'container',
tty: true,
stdinOpen: true,
restartPolicy: {name: 'always'},
labels: { [C.LABEL.PULL_IMAGE]: C.LABEL.PULL_IMAGE_VALUE },
uiId: Util.randomStr(),
}));
},
removeSidekick(idx) {
var ary = this.get('primaryService.secondaryLaunchConfigs');
ary.removeAt(idx);
},
},
init() {
window.nec = this;
this._super(...arguments);
// Tell cattle that we're sending the whole thing, not a diff.
this.set('service.completeLaunchConfigs', true);
if ( !this.get('launchConfig.secrets') ) {
this.set('launchConfig.secrets', []);
}
if ( !this.get('launchConfig.metadata') ) {
this.set('launchConfig.metadata', {});
}
if ( this.get('isService') && !this.get('isSidekick') ) {
this.setProperties({
name: this.get('service.name'),
description: this.get('service.description'),
scale: this.get('service.scale'),
});
} else {
this.setProperties({
name: this.get('launchConfig.name'),
description: this.get('launchConfig.description'),
});
}
let stackId = null;
if ( this.get('isService') ) {
stackId = this.get('service.stackId');
} else {
stackId = this.get('launchConfig.stackId');
}
if ( stackId ) {
let stack = this.get('store').getById('stack', stackId);
if ( stack ) {
this.set('stack', stack);
}
}
this.labelsChanged();
},
didInsertElement() {
this.$("INPUT[type='text']")[0].focus();
},
// ----------------------------------
// Labels
// ----------------------------------
userLabels: null,
scaleLabels: null,
securityLabels: null,
commandLabels: null,
schedulingLabels: null,
networkingLabels: null,
labelsChanged: debouncedObserver(
'userLabels.@each.{key,value}',
'scaleLabels.@each.{key,value}',
'securityLabels.@each.{key,value}',
'commandLabels.@each.{key,value}',
'schedulingLabels.@each.{key,value}',
'networkingLabels.@each.{key,value}',
function() {
let out = flattenLabelArrays(
this.get('userLabels'),
this.get('scaleLabels'),
this.get('securityLabels'),
this.get('commandLabels'),
this.get('schedulingLabels'),
this.get('networkingLabels')
);
var config = this.get('launchConfig');
if ( config )
{
this.set('launchConfig.labels', out);
}
}
),
// ----------------------------------
// Save
// ----------------------------------
validate() {
let pr = this.get('primaryResource');
let errors = pr.validationErrors() || [];
if ( this.get('isService') )
{
(this.get('service.secondaryLaunchConfigs')||[]).forEach((slc) => {
slc.validationErrors().forEach((err) => {
errors.push(slc.get('displayName') + ': ' + err);
});
});
}
// Errors from components
errors.pushObjects(this.get('commandErrors')||[]);
errors.pushObjects(this.get('volumeErrors')||[]);
errors.pushObjects(this.get('networkingErrors')||[]);
errors.pushObjects(this.get('secretsErrors')||[]);
errors.pushObjects(this.get('healthCheckErrors')||[]);
errors.pushObjects(this.get('schedulingErrors')||[]);
errors.pushObjects(this.get('securityErrors')||[]);
errors.pushObjects(this.get('scaleErrors')||[]);
errors.pushObjects(this.get('imageErrors')||[]);
errors.pushObjects(this.get('portErrors')||[]);
errors.pushObjects(this.get('stackErrors')||[]);
errors.pushObjects(this.get('metadataErrors')||[]);
errors = errors.uniq();
if ( errors.get('length') )
{
this.set('errors', errors);
return false;
}
this.set('errors', null);
return true;
},
willSave() {
let intl = this.get('intl');
let pr;
let nameResource;
let lc = this.get('launchConfig').clone();
let name = (this.get('name')||'').trim().toLowerCase();
if ( this.get('isSidekick') ) {
let service = this.get('service');
let errors = [];
if ( !service ) {
errors.push(this.get('intl').t('newContainer.errors.noSidekick'));
this.set('errors', errors);
return false;
}
if ( !name ) {
errors.push(intl.t('validation.required', {key: intl.t('formNameDescription.name.label')}));
this.set('errors', errors);
return false;
}
pr = service.clone();
let def = lc.serialize();
def.type = 'secondaryLaunchConfig';
let sidekick = this.get('store').createRecord(def);
nameResource = sidekick;
let slc = pr.get('secondaryLaunchConfigs');
if ( !slc ) {
slc = [];
pr.set('secondaryLaunchConfigs', slc);
}
let lci = this.get('launchConfigIndex');
if ( lci === undefined || lci === null ) {
// If it's a new sidekick, add it to the end of the list
lci = slc.length;
}
let duplicate = slc.find((x, idx) => {
return idx !== lci && x.get('name').toLowerCase() === name;
});
if ( duplicate ) {
errors.push(intl.t('newContainer.errors.duplicateName', {name: name, service: duplicate.get('displayName')}));
this.set('errors', errors);
return false;
}
slc[lci] = sidekick;
} else if ( this.get('isService') ) {
pr = this.get('service').clone();
nameResource = pr;
pr.set('launchConfig', lc);
pr.set('scale', this.get('scale'));
} else {
// Convert the launch config to a container
let lc = this.get('launchConfig').serialize();
lc.type = 'container';
pr = this.get('store').createRecord(lc);
nameResource = pr;
}
pr.set('completeUpdate', true);
nameResource.setProperties({
name: this.get('name'),
description: this.get('description'),
});
this.set('primaryResource', pr);
this.set('originalPrimaryResource', pr);
let ok = this.validate();
return ok;
},
doSave() {
let pr = this.get('primaryResource');
let stackPromise = resolve();
if ( !this.get('isUpgrade') ) {
// Set the stack ID
if ( this.get('stack.id') ) {
pr.set('stackId', this.get('stack.id'));
} else if ( this.get('stack') ) {
stackPromise = this.get('stack').save().then((newStack) => {
pr.set('stackId', newStack.get('id'));
});
} else {
// This shouldn't happen since willSave checked it...
return reject('No Stack');
}
}
let self = this;
let sup = self._super;
return stackPromise.then(() => {
let volumes = this.get('volumesToCreate');
let volumesPromise = resolve();
if ( volumes && volumes.get('length') ) {
volumesPromise = all(volumes.map((volume) => {
volume.set('stackId', this.get('stack.id'));
return volume.save();
}));
}
return volumesPromise.then(() => {
if ( this.get('isUpgrade') && !this.get('isService') ) {
// Container upgrade
return this.get('launchConfig').doAction('upgrade', {config: this.get('launchConfig')});
} else {
// Container create or Service create/upgrade
return sup.apply(self,arguments);
}
});
});
},
doneSaving() {
if ( !this.get('isUpgrade') ) {
let mode = this.get('mode');
if ( mode === 'sidekick' ) {
// Remember sidekick as service since you're not
// likely to want to add many sidekicks in a row
mode = 'service';
}
this.set(`prefs.${C.PREFS.LAST_SCALE_MODE}`, mode);
this.set(`prefs.${C.PREFS.LAST_STACK}`, this.get('stack.id'));
}
this.sendAction('done');
},
header: '',
updateHeader: function() {
let args = {};
let k = 'newContainer.';
k += (this.get('isUpgrade') ? 'upgrade' : 'add') + '.';
if ( this.get('isSidekick') ) {
let svc = this.get('service');
if ( svc ) {
k += 'sidekickName';
args = {name: this.get('service.displayName')};
} else {
k += 'sidekick';
}
} else if ( this.get('isGlobal') ) {
k += 'globalService';
} else if ( this.get('isService') ) {
k += 'service';
} else {
k += 'container';
}
next(() => {
this.set('header', this.get('intl').t(k, args));
});
}.observes('isUpgrade','isService','isSidekick','isGlobal','service.displayName','intl.locale').on('init'),
supportsSecrets: function() {
return !!this.get('store').getById('schema','secret');
}.property(),
isSidekick: equal('mode','sidekick'),
});