From 2262a91372760b2312db39e3fc7b8992df0f23f5 Mon Sep 17 00:00:00 2001 From: Vincent Fiduccia Date: Fri, 23 Oct 2015 15:48:39 -0700 Subject: [PATCH] Service Upgrade --- app/components/form-image/component.js | 4 +- .../form-name-description/component.js | 1 + .../form-name-description/template.hbs | 2 +- app/components/form-upgrade/component.js | 19 +++++ app/components/form-upgrade/template.hbs | 25 +++++++ app/components/new-container/component.js | 69 ++++++++++++++++++- app/components/new-container/template.hbs | 46 ++++++++----- app/components/service-pod/template.hbs | 2 +- app/mixins/cattle-transitioning-resource.js | 51 ++++++++++---- app/models/service.js | 66 +++++++++++------- app/service/new/controller.js | 3 +- app/service/new/route.js | 10 +++ app/service/new/template.hbs | 1 + 13 files changed, 235 insertions(+), 64 deletions(-) create mode 100644 app/components/form-upgrade/component.js create mode 100644 app/components/form-upgrade/template.hbs diff --git a/app/components/form-image/component.js b/app/components/form-image/component.js index 6382da0e9..04454962c 100644 --- a/app/components/form-image/component.js +++ b/app/components/form-image/component.js @@ -13,9 +13,9 @@ export default Ember.Component.extend({ didInitAttrs() { var initial; - if ( this.get('value') ) + if ( this.get('initialValue') ) { - initial = (this.get('value')||'').replace(/^docker:/,''); + initial = (this.get('initialValue')||'').replace(/^docker:/,''); } if ( !initial ) diff --git a/app/components/form-name-description/component.js b/app/components/form-name-description/component.js index 835823617..4e9d14f5a 100644 --- a/app/components/form-name-description/component.js +++ b/app/components/form-name-description/component.js @@ -4,6 +4,7 @@ export default Ember.Component.extend({ // Inputs model: null, namePlaceholder: '', + nameDisabled: false, descriptionPlaceholder: '', tagName: '', diff --git a/app/components/form-name-description/template.hbs b/app/components/form-name-description/template.hbs index ea0fa2698..efa81142d 100644 --- a/app/components/form-name-description/template.hbs +++ b/app/components/form-name-description/template.hbs @@ -3,7 +3,7 @@
- {{input type="text" value=model.name classNames="form-control" placeholder=namePlaceholder}} + {{input type="text" value=model.name classNames="form-control" placeholder=namePlaceholder disabled=nameDisabled}}
diff --git a/app/components/form-upgrade/component.js b/app/components/form-upgrade/component.js new file mode 100644 index 000000000..c6afab1bc --- /dev/null +++ b/app/components/form-upgrade/component.js @@ -0,0 +1,19 @@ +import Ember from 'ember'; + +export default Ember.Component.extend({ + batchSize: 1, + interval: 2, + startFirst: false, + + didInitAttrs() { + this.optionsChanged(); + }, + + optionsChanged() { + this.sendAction('changed', { + batchSize: this.get('batchSize'), + intervalMillis: this.get('interval')*1000, + startFirst: this.get('startFirst'), + }); + }, +}); diff --git a/app/components/form-upgrade/template.hbs b/app/components/form-upgrade/template.hbs new file mode 100644 index 000000000..3cad917e4 --- /dev/null +++ b/app/components/form-upgrade/template.hbs @@ -0,0 +1,25 @@ +
+
+ +
+
+
+
+ + {{input type="number" min="1" class="form-control" value=batchSize}} +
+
+ +
+ {{input type="number" class="form-control" value=interval}} + sec +
+ +
+
+ + +
+
+
+
diff --git a/app/components/new-container/component.js b/app/components/new-container/component.js index 9c99658e8..8f8ab75da 100644 --- a/app/components/new-container/component.js +++ b/app/components/new-container/component.js @@ -7,6 +7,7 @@ export default Ember.Component.extend(NewOrEdit, SelectTab, { isStandalone: true, isService: false, isSidekick: false, + isUpgrade: false, primaryResource: null, launchConfig: null, service: null, @@ -18,6 +19,7 @@ export default Ember.Component.extend(NewOrEdit, SelectTab, { isRequestedHost: null, portsAsStrArray: null, launchConfigIndex: -1, + upgradeOptions: null, // Errors from components commandErrors: null, @@ -90,6 +92,10 @@ export default Ember.Component.extend(NewOrEdit, SelectTab, { this.set('serviceLinksArray', links); }, + setUpgrade(upgrade) { + this.set('upgradeOptions', upgrade); + }, + done() { this.sendAction('done'); }, @@ -124,6 +130,48 @@ export default Ember.Component.extend(NewOrEdit, SelectTab, { } }.property('launchConfigIndex'), + activeLabel: function() { + var idx = this.get('launchConfigIndex'); + var str = ''; + + if ( this.get('hasSidekicks') ) + { + if ( idx === -1 ) + { + str = 'Primary Service: '; + } + else + { + str += 'Sidekick Service: '; + } + } + + if ( idx === -1 ) + { + if ( this.get('service.name') ) + { + str += this.get('service.name'); + } + else + { + str += '(No name)'; + } + } + else + { + if ( this.get('activeLaunchConfig.name') ) + { + str += this.get('activeLaunchConfig.name'); + } + else + { + str += '(Sidekick #' + (idx+1) + ')'; + } + } + + return str; + }.property('service.name','activeLaunchConfig.name','launchConfigIndex','hasSidekicks'), + // ---------------------------------- // Labels // ---------------------------------- @@ -178,8 +226,27 @@ export default Ember.Component.extend(NewOrEdit, SelectTab, { return this._super.apply(this,arguments); }, + doSave() { + if ( this.get('isService') && this.get('isUpgrade') ) + { + return this.get('service').doAction('upgrade', { + inServiceStrategy: { + batchSize: this.get('upgradeOptions.batchSize'), + intervalMillis: this.get('upgradeOptions.intervalMillis'), + launchConfig: this.get('service.launchConfig'), + secondaryLaunchConfigs: this.get('service.secondaryLaunchConfigs'), + startFirst: this.get('upgradeOptions.startFirst'), + }, + }); + } + else + { + return this._super.apply(this,arguments); + } + }, + didSave() { - if ( this.get('isService') ) + if ( this.get('isService') && !this.get('isUpgrade') ) { // Returns a promise return this.setServiceLinks(); diff --git a/app/components/new-container/template.hbs b/app/components/new-container/template.hbs index 9725cfa8d..a57ad2906 100644 --- a/app/components/new-container/template.hbs +++ b/app/components/new-container/template.hbs @@ -1,6 +1,6 @@ {{#unless isSidekick}}
-

Add +

{{if isUpgrade 'Upgrade' 'Add'}} {{#if isService}} Service {{#if hasSidekicks}} @@ -13,7 +13,7 @@

{{top-errors errors=errors}} - {{#if (and isService (not isSidekick))}} + {{#if isService}} {{form-scale initialLabels=launchConfig.labels initialScale=service.scale @@ -24,25 +24,29 @@ setScale=(action 'setScale') }} - {{form-divider}} + {{#if isUpgrade}} + {{form-upgrade + changed=(action 'setUpgrade') + }} + {{/if}} - {{#if hasSidekicks}} -
-
- -
- -
+ {{#if (and isUpgrade (not hasSidekicks))}} + {{form-divider}} + {{else}} +
+ -
+
{{/if}} {{/if}} @@ -51,7 +55,12 @@
- {{form-name-description model=primaryResource namePlaceholder="e.g. myapp" descriptionPlaceholder="My application"}} + {{form-name-description + model=primaryResource + namePlaceholder="e.g. myapp" + nameDisabled=isUpgrade + descriptionPlaceholder="My application" + }} {{form-divider}} @@ -61,7 +70,7 @@ {{form-ports initialPorts=launchConfig.ports errors=portErrors portsAsStrArray=launchConfig.ports}} - {{#if isService}} + {{#if (and isService (not isSidekick))}} {{form-divider}} {{form-service-links @@ -150,6 +159,7 @@ {{new-container isService=true isSidekick=true + isUpgrade=isUpgrade launchConfig=slc service=slc primaryResource=slc @@ -160,12 +170,10 @@ {{/each}} {{/if}} + {{#unless isSidekick}} - {{#save-cancel save="save" cancel="cancel"}} - {{#if isService}} - - {{/if}} - {{#if (not-eq launchConfigIndex -1)}} + {{#save-cancel createLabel=(if isUpgrade 'Upgrade' 'Create') save="save" cancel="cancel"}} + {{#if (and (not isUpgrade) (not-eq launchConfigIndex -1))}} {{/if}} {{/save-cancel}} diff --git a/app/components/service-pod/template.hbs b/app/components/service-pod/template.hbs index 1f3a03e9a..b4297f73a 100644 --- a/app/components/service-pod/template.hbs +++ b/app/components/service-pod/template.hbs @@ -4,7 +4,7 @@
{{model.displayState}}
-
+
{{#link-to "service" model.environmentId model.id}}{{model.displayName}}{{/link-to}} {{#if model.showTransitioningMessage}} diff --git a/app/mixins/cattle-transitioning-resource.js b/app/mixins/cattle-transitioning-resource.js index 9b3d1e121..4df2d8960 100644 --- a/app/mixins/cattle-transitioning-resource.js +++ b/app/mixins/cattle-transitioning-resource.js @@ -113,12 +113,16 @@ export default Ember.Mixin.create({ isDeleted: Ember.computed.equal('state','removed'), isPurged: Ember.computed.equal('state','purged'), + relevantState: function() { + return this.get('combinedState') || this.get('state'); + }.property('combinedState','state'), + displayState: function() { - var state = this.get('state')||''; + var state = this.get('relevantState')||''; return state.split(/-/).map((word) => { return Util.ucFirst(word); }).join('-'); - }.property('state'), + }.property('relevantState'), showTransitioningMessage: function() { var trans = this.get('transitioning'); @@ -127,42 +131,63 @@ export default Ember.Mixin.create({ stateIcon: function() { var trans = this.get('transitioning'); + var icon = ''; if ( trans === 'yes' ) { - return 'icon icon-spinner icon-spin'; + icon = 'icon icon-spinner icon-spin'; } else if ( trans === 'error' ) { - return 'icon icon-alert text-danger'; + icon = 'icon icon-alert text-danger'; } else { var map = this.constructor.stateMap; - var key = (this.get('state')||'').toLowerCase(); + var key = (this.get('relevantState')||'').toLowerCase(); if ( map && map[key] && map[key].icon !== undefined) { if ( typeof map[key].icon === 'function' ) { - return map[key].icon(this); + icon = map[key].icon(this); } else { - return map[key].icon; + icon = map[key].icon; } } - if ( defaultStateMap[key] && defaultStateMap[key].icon ) + if ( !icon && defaultStateMap[key] && defaultStateMap[key].icon ) { - return defaultStateMap[key].icon; + icon = defaultStateMap[key].icon; } - return this.constructor.defaultStateIcon; + if ( !icon ) + { + icon = this.constructor.defaultStateIcon; + } + + if ( icon.indexOf('fa-') >= 0 ) + { + if ( icon.indexOf('fa ') === -1 ) + { + icon = 'fa ' + icon; + } + } + else + { + if ( icon.indexOf('icon ') === -1 ) + { + icon = 'icon ' + icon; + } + } } - }.property('state','transitioning'), + + return icon; + }.property('relevantState','transitioning'), stateColor: function() { var map = this.constructor.stateMap; - var key = (this.get('state')||'').toLowerCase(); + var key = (this.get('relevantState')||'').toLowerCase(); if ( map && map[key] && map[key].color !== undefined ) { if ( typeof map[key].color === 'function' ) @@ -181,7 +206,7 @@ export default Ember.Mixin.create({ } return this.constructor.defaultStateColor; - }.property('state','transitioning'), + }.property('relevantState','transitioning'), stateBackground: function() { return this.get('stateColor').replace("text-","bg-"); diff --git a/app/models/service.js b/app/models/service.js index e7fa192f8..bda7eebd1 100644 --- a/app/models/service.js +++ b/app/models/service.js @@ -13,32 +13,31 @@ var Service = Resource.extend(ReadLabels, { type: 'service', actions: { - activate: function() { + activate() { return this.doAction('activate'); }, - deactivate: function() { + deactivate() { return this.doAction('deactivate'); }, - cancelUpgrade: function() { + cancelUpgrade() { return this.doAction('cancelupgrade'); }, - cancelRollback: function() { + cancelRollback() { return this.doAction('cancelrollback'); }, - finishUpgrade: function() { + finishUpgrade() { return this.doAction('finishupgrade'); }, - rollback: function() { + rollback() { return this.doAction('rollback'); }, - - edit: function() { + edit() { var type = this.get('type').toLowerCase(); if ( type === 'loadbalancerservice' ) { @@ -72,12 +71,12 @@ var Service = Resource.extend(ReadLabels, { } }, - scaleUp: function() { + scaleUp() { this.incrementProperty('scale'); this.saveScale(); }, - scaleDown: function() { + scaleDown() { if ( this.get('scale') >= 1 ) { this.decrementProperty('scale'); @@ -85,7 +84,15 @@ var Service = Resource.extend(ReadLabels, { } }, - clone: function() { + upgrade() { + this.get('application').transitionToRoute('service.new', {queryParams: { + serviceId: this.get('id'), + upgrade: true, + environmentId: this.get('environmentId'), + }}); + }, + + clone() { var route; switch ( this.get('type').toLowerCase() ) { @@ -104,7 +111,7 @@ var Service = Resource.extend(ReadLabels, { }, scaleTimer: null, - saveScale: function() { + saveScale() { if ( this.get('scaleTimer') ) { Ember.run.cancel(this.get('scaleTimer')); @@ -121,19 +128,20 @@ var Service = Resource.extend(ReadLabels, { var a = this.get('actionLinks'); var choices = [ - { label: 'Start', icon: 'icon icon-play', action: 'activate', enabled: !!a.activate, color: 'text-success'}, - { label: 'Stop', icon: 'icon icon-pause', action: 'deactivate', enabled: !!a.deactivate, color: 'text-danger'}, - { label: 'Delete', icon: 'icon icon-trash', action: 'promptDelete', enabled: !!a.remove, altAction: 'delete', color: 'text-warning' }, - { label: 'Purge', icon: '', action: 'purge', enabled: !!a.purge }, + { label: 'Start', icon: 'icon icon-play', action: 'activate', enabled: !!a.activate, color: 'text-success'}, + { label: 'Stop', icon: 'icon icon-pause', action: 'deactivate', enabled: !!a.deactivate, color: 'text-danger'}, + { label: 'Delete', icon: 'icon icon-trash', action: 'promptDelete', enabled: !!a.remove, altAction: 'delete', color: 'text-warning' }, + { label: 'Purge', icon: '', action: 'purge', enabled: !!a.purge }, { divider: true }, - { label: 'Finish Upgrade', icon: 'fa fa-thumbs-o-up', action: 'finishUpgrade', enabled: !!a.finishupgrade }, - { label: 'Cancel Upgrade', icon: 'fa fa-life-ring', action: 'cancelUpgrade', enabled: !!a.cancelupgrade }, - { label: 'Rollback', icon: 'fa fa-history', action: 'rollback', enabled: !!a.rollback }, - { label: 'Cancel Rollback', icon: 'fa fa-life-ring', action: 'cancelRollback', enabled: !!a.cancelrollback }, + { label: 'Upgrade', icon: 'fa fa-arrow-circle-o-up',action: 'upgrade', enabled: !!a.upgrade }, + { label: 'Finish Upgrade', icon: 'fa fa-thumbs-o-up', action: 'finishUpgrade', enabled: !!a.finishupgrade }, + { label: 'Cancel Upgrade', icon: 'fa fa-life-ring', action: 'cancelUpgrade', enabled: !!a.cancelupgrade }, + { label: 'Rollback', icon: 'fa fa-history', action: 'rollback', enabled: !!a.rollback }, + { label: 'Cancel Rollback', icon: 'fa fa-life-ring', action: 'cancelRollback', enabled: !!a.cancelrollback }, { divider: true }, - { label: 'View in API', icon: 'icon icon-externallink', action: 'goToApi', enabled: true }, - { label: 'Clone', icon: 'icon icon-copy', action: 'clone', enabled: true }, - { label: 'Edit', icon: 'icon icon-edit', action: 'edit', enabled: !!a.update }, + { label: 'View in API', icon: 'icon icon-externallink', action: 'goToApi', enabled: true }, + { label: 'Clone', icon: 'icon icon-copy', action: 'clone', enabled: true }, + { label: 'Edit', icon: 'icon icon-edit', action: 'edit', enabled: !!a.update }, ]; return choices; @@ -356,9 +364,15 @@ Service.reopenClass({ }, stateMap: { - 'active': {icon: activeIcon, color: 'text-success'}, - 'upgrading': {icon: 'icon-arrow-up', color: 'text-info'}, - 'canceling-upgrade':{icon: 'icon-arrow-down', color: 'text-info'}, + 'active': {icon: activeIcon, color: 'text-success'}, + 'canceled-rollback': {icon: 'fa fa-life-ring', color: 'text-info'}, + 'canceled-upgrade': {icon: 'fa fa-life-ring', color: 'text-info'}, + 'canceling-rollback': {icon: 'fa fa-life-ring', color: 'text-info'}, + 'canceling-upgrade': {icon: 'fa fa-life-ring', color: 'text-info'}, + 'finishing-upgrade': {icon: 'fa fa-arrow-circle-o-up', color: 'text-info'}, + 'rolling-back': {icon: 'fa fa-history', color: 'text-info'}, + 'upgraded': {icon: 'fa fa-arrow-circle-o-up', color: 'text-info'}, + 'upgrading': {icon: 'fa fa-arrow-circle-o-up', color: 'text-info'}, } }); diff --git a/app/service/new/controller.js b/app/service/new/controller.js index 83f7cbb5a..2d976ac12 100644 --- a/app/service/new/controller.js +++ b/app/service/new/controller.js @@ -1,10 +1,11 @@ import Ember from 'ember'; export default Ember.Controller.extend({ - queryParams: ['environmentId','serviceId','containerId'], + queryParams: ['environmentId','serviceId','containerId','upgrade'], environmentId: null, serviceId: null, containerId: null, + upgrade: null, actions: { done() { diff --git a/app/service/new/route.js b/app/service/new/route.js index 1f4d7c5d4..ee963f327 100644 --- a/app/service/new/route.js +++ b/app/service/new/route.js @@ -28,6 +28,15 @@ export default Ember.Route.extend({ var serviceLinks = []; var secondaryLaunchConfigs = []; + if ( params.upgrade ) + { + return Ember.Object.create({ + service: serviceOrContainer.clone(), + allHosts: allHosts, + allServices: allServices, + }); + } + var instanceData, serviceData, healthCheckData; if ( serviceOrContainer ) { @@ -100,6 +109,7 @@ export default Ember.Route.extend({ controller.set('environmentId', null); controller.set('serviceId', null); controller.set('containerId', null); + controller.set('upgrade', null); } } }); diff --git a/app/service/new/template.hbs b/app/service/new/template.hbs index bb5342394..3d2be9564 100644 --- a/app/service/new/template.hbs +++ b/app/service/new/template.hbs @@ -1,5 +1,6 @@ {{new-container isService=true + isUpgrade=upgrade isSidekick=false launchConfig=model.service.launchConfig service=model.service