Upgrade options, balancer

This commit is contained in:
Vincent Fiduccia 2017-05-16 01:17:40 -07:00
parent e12ae6540b
commit edb91c1101
No known key found for this signature in database
GPG Key ID: 2B29AD6BB2BB2582
21 changed files with 189 additions and 134 deletions

View File

@ -1,4 +1,4 @@
{{new-balancer
{{edit-balancer
service=model.service
existing=model.existing
editing=upgrade

View File

@ -70,6 +70,7 @@ export default Ember.Component.extend(ManageLabels, ContainerChoices,{
isContainerNetwork: Ember.computed.equal('instance.networkMode','container'),
isManagedNetwork: Ember.computed.equal('instance.networkMode','managed'),
isHostNetwork: Ember.computed.equal('instance.networkMode','host'),
isBridgeNetwork: Ember.computed.equal('instance.networkMode','bridge'),
initNetwork: function() {
var isService = this.get('isService')||false;

View File

@ -7,7 +7,7 @@
expand=(action expandFn)
}}
<div class="row inline-form">
<div class="col {{if (or isContainerNetwork isHostNetwork) 'span-6' (if isManagedNetwork 'span-4' 'span-12')}}">
<div class="col {{if (or isContainerNetwork isHostNetwork isBridgeNetwork) 'span-6' (if isManagedNetwork 'span-4' 'span-12')}}">
<label>{{t 'formNetwork.networkMode.label'}}</label>
{{#input-or-display editable=editing value=instance.networkMode}}
{{new-select
@ -20,7 +20,7 @@
<p class="help-block">{{t (concat-str 'formNetwork.networkMode' instance.networkMode 'detail' character='.')}}</p>
</div>
{{#if (or isContainerNetwork isHostNetwork)}}
{{#if (or isContainerNetwork isHostNetwork isBridgeNetwork)}}
<div class="col span-6">
{{#if isContainerNetwork}}
<label>{{t 'formNetwork.container.label'}}</label>
@ -34,7 +34,7 @@
value=instance.networkContainerId
}}
{{/input-or-display}}
{{else if isHostNetwork}}
{{else if (or isHostNetwork isBridgeNetwork)}}
<label>{{t 'formNetwork.dns.label'}}</label>
<div>
{{#input-or-display editable=editing value=dnsDiscovery}}

View File

@ -0,0 +1,10 @@
import Ember from 'ember';
export default Ember.Component.extend({
service: null,
editing: null,
isUpgrade: null,
choices: null,
classNames: ['accordion-wrapper'],
});

View File

@ -0,0 +1,65 @@
{{#accordion-list-item
title=(t 'formUpgrade.title')
detail=(t 'formUpgrade.detail' appName=settings.appName)
status=status
statusClass=statusClass
expandAll=expandAll
expand=(action expandFn)
}}
{{#input-or-display editable=editing value=service.startFirst}}
<div class="row">
<div class="col span-11-of-23">
<label>{{t 'formUpgrade.behavior'}}</label>
<div class="radio small mt-10">
<label>{{radio-button selection=service.startFirst value=false}} {{t 'formUpgrade.stopFirst.label'}}</label>
<p class="help-block">{{t 'formUpgrade.stopFirst.detail'}}</p>
</div>
</div>
<div class="col span-11-of-23 offset-1-of-23">
<label>&nbsp;</label>
<div class="radio small mt-10">
<label>{{radio-button selection=service.startFirst value=true}} {{t 'formUpgrade.startFirst.label'}}</label>
<p class="help-block">{{t 'formUpgrade.startFirst.detail'}}</p>
</div>
</div>
</div>
{{/input-or-display}}
<hr/>
<div class="row">
<div class="col span-11-of-23">
<label>{{t 'formUpgrade.batchSize.label'}}</label>
{{#input-or-display editable=editing value=service.batchSize}}
<div class="input-group">
{{input-integer min=1 step=100 classNames="form-control" value=service.batchSize}}
<span class="input-group-addon bg-default">{{t 'generic.containers'}}</span>
</div>
<p class="help-block">{{t 'formUpgrade.batchSize.detail'}}</p>
{{/input-or-display}}
</div>
<div class="col span-11-of-23 offset-1-of-23">
<label>{{t 'formUpgrade.interval.label'}}</label>
{{#input-or-display editable=editing value=service.interval}}
<div class="input-group">
{{input-integer min=0 classNames="form-control" value=service.interval}}
<span class="input-group-addon bg-default">{{t 'generic.millisecond'}}</span>
</div>
{{/input-or-display}}
<p class="help-block">{{t 'formUpgrade.interval.detail'}}</p>
</div>
</div>
<hr/>
<div class="row">
<div class="col-23-of-23">
<label>{{t 'formUpgrade.force.label'}}</label>
<p class="help-block">{{t 'formUpgrade.force.detail'}}</p>
@TODO API for force-upgrade
</div>
</div>
{{/accordion-list-item}}

View File

@ -145,6 +145,7 @@ export default Ember.Component.extend(NewOrEdit, SelectTab, {
index: index,
name: item.get('name') || intl.t('newContainer.emptySidekick', {num: index+1}),
enabled: !isUpgrade
uiId: item.get('uiId'),
});
});

View File

@ -4,7 +4,7 @@
<section class="header header-sidekick clearfix">
<h2 class="inline-block">{{t headerToken name=configName}}</h2>
<div class="right-buttons p-0">
<button class="btn bg-transparent" {{action "removeSidekick" launchConfigIndex}}>{{t 'newContainer.removeSidekick'}}</button>
<button class="btn bg-transparent p-0 text-small text-right" {{action "removeSidekick" launchConfigIndex}}>{{t 'newContainer.removeSidekick'}}</button>
</div>
</section>
{{else}}
@ -195,6 +195,17 @@
expandFn=expandFn
}}
{{#if (and isService (not isSidekick))}}
{{container/form-upgrade
service=service
editing=true
isUpgrade=isUpgrade
choices=launchConfigChoices
expandAll=al.expandAll
expandFn=expandFn
}}
{{/if}}
{{container/form-volumes
classNames="accordion-wrapper"
instance=launchConfig
@ -228,7 +239,7 @@
{{top-errors errors=errors}}
{{#save-cancel saveDisabled=noLaunchConfigsEnabled createLabel=(if isUpgrade 'newContainer.saveUpgrade' 'newContainer.saveNew') save="save" cancel="cancel"}}
{{#if isService}}
<button class="btn bg-transparent" {{action "addSidekick"}}>{{t 'newContainer.addSidekick'}}</button>
<button class="btn bg-transparent text-right p-0 pt-5 text-small" style="position: absolute; right: 0; margin-top: 8px;" {{action "addSidekick"}}>{{t 'newContainer.addSidekick'}}</button>
{{/if}}
{{/save-cancel}}
{{/unless}}

View File

@ -49,10 +49,6 @@ export default Ember.Component.extend(NewOrEdit, {
setLabels(section,labels) {
this.set(section+'Labels', labels);
},
setUpgrade(upgrade) {
this.set('upgradeOptions', upgrade);
},
},
headerLabel: function() {

View File

@ -1,47 +1,80 @@
<section class="header clearfix">
<h1>{{headerLabel}} {{help-btn link="/cattle/adding-load-balancers/"}}</h1>
<h1>{{headerLabel}}</h1>
</section>
<section class="horizontal-form container-fluid">
{{#liquid-if needsUpgrade}}
{{form-upgrade
optionsChanged=(action 'setUpgrade')
<div class="row">
<div class="col span-11-of-23">
{{form-name-description
model=service
namePlaceholder="servicePage.newBalancer.form.name.placeholder"
descriptionPlaceholder="servicePage.newBalancer.form.description.placeholder"
rowClass=""
colClass=""
bothColClass=""
}}
{{/liquid-if}}
</div>
<div class="{{if needsUpgrade 'hide'}}">
<div class="col span-11-of-23 offset-1-of-23">
{{container/form-scale
initialLabels=launchConfig.labels
initialScale=service.scale
editing=editing
isGlobal=isGlobal
isService=true
errors=scaleErrors
setLabels=(action 'setLabels' 'scale')
setScale=(action 'setScale')
}}
{{form-stack
stack=stack
errors=stackErrors
}}
</div>
</div>
<section class="horizontal-form container-fluid">
{{container/form-upgrade
optionsChanged=(action 'setUpgrade')
}}
</section>
<section class="horizontal-form container-fluid">
{{form-name-description
model=service
namePlaceholder="servicePage.newBalancer.form.name.placeholder"
descriptionPlaceholder="servicePage.newBalancer.form.description.placeholder"
}}
{{form-balancer-rules
service=service
}}
</section>
{{container/form-scheduling
isService=true
isGlobal=isGlobal
canRequestHost=false
initialHostId=launchConfig.requestedHostId
initialLabels=launchConfig.labels
errors=schedulingErrors
allHosts=allHosts
setLabels=(action 'setLabels' 'scheduling')
}}
{{#advanced-section advanced=false}}
{{#accordion-list as | al expandFn | }}
{{form-user-labels
initialLabels=launchConfig.labels
setLabels=(action 'setLabels' 'user')
expandAll=al.expandAll
expandFn=expandFn
}}
{{/accordion-list}}
{{/advanced-section}}
{{#select-tab initialTab='ssl' as |component|}}
<div class="tabs">
<ul class="tab-header" style="display: inline-block" role="tablist">
<li role="tab" class="tab" data-section="ssl" {{action "selectTab" "ssl" target=component}}><a href="#">{{t 'newBalancer.tabs.ssl'}}</a></li>
<li role="tab" class="tab" data-section="stickiness" {{action "selectTab" "stickiness" target=component}}><a href="#">{{t 'newBalancer.tabs.stickiness'}}</a></li>
<li role="tab" class="tab" data-section="custom" {{action "selectTab" "custom" target=component}}><a href="#">{{t 'newBalancer.tabs.custom'}}</a></li>
<li role="tab" class="tab" data-section="labels" {{action "selectTab" "labels" target=component}}><a href="#">{{t 'newBalancer.tabs.labels'}}</a></li>
<li role="tab" class="tab" data-section="scheduling" {{action "selectTab" "scheduling" target=component}}><a href="#">{{t 'newBalancer.tabs.scheduling'}}</a></li>
</ul>
<div class="tab-content" role="tabpanel">
@ -71,23 +104,9 @@
</div>
<div class="section" data-section="labels">
{{form-user-labels
initialLabels=launchConfig.labels
setLabels=(action 'setLabels' 'user')
}}
</div>
<div class="section" data-section="scheduling">
{{container/form-scheduling
isService=true
isGlobal=isGlobal
canRequestHost=false
initialHostId=launchConfig.requestedHostId
initialLabels=launchConfig.labels
errors=schedulingErrors
allHosts=allHosts
setLabels=(action 'setLabels' 'scheduling')
}}
</div>
</div>
</div>

View File

@ -1,44 +0,0 @@
import Ember from 'ember';
export default Ember.Component.extend({
choices : null,
index : null,
batchSize : 1,
interval : 2,
startFirst : false,
init() {
this._super(...arguments);
this.optionsDidChange();
},
optionsDidChange: function() {
this.sendAction('optionsChanged', {
batchSize: parseInt(this.get('batchSize'),10),
intervalMillis: parseInt(this.get('interval'),10)*1000,
startFirst: this.get('startFirst'),
});
}.observes('batchSize','interval','startFirst'),
choicesDidChange: function() {
var index = this.get('index');
var obj = this.get('choices').filterBy('index',index)[0];
if ( !obj || !obj.enabled ) {
var first = this.get('choices').filterBy('enabled',true)[0];
if ( first )
{
this.sendAction('switch', first.index);
}
else
{
this.sendAction('switch', null);
}
}
}.observes('choices.@each.enabled'),
hasSidekicks: function() {
return this.get('choices.length') > 1;
}.property('choices.length'),
});

View File

@ -1,33 +0,0 @@
<div class="row">
<div class="col span-4">
<label>{{t 'formUpgrade.size'}}</label>
{{input-integer min="1" class="form-control" value=batchSize}}
</div>
<div class="col span-4">
<label>{{t 'formUpgrade.interval'}}</label>
<div class="input-group">
{{input-integer class="form-control" value=interval}}
<span class="input-group-addon bg-default">{{t 'formUpgrade.sec'}}</span>
</div>
</div>
<div class="col span-4">
<label>{{t 'formUpgrade.behavior'}}</label>
<div>
<label class="checkbox ml-20" style="color: inherit; text-transform: none;">{{input type="checkbox" checked=startFirst}} {{t 'formUpgrade.startStop'}}</label>
</div>
</div>
</div>
{{#if hasSidekicks}}
<div class="row">
<div class="col span-2 col-inline">
<label>{{t 'formUpgrade.whichServices'}}</label>
</div>
<div class="col span-8">
{{#each choices as |choice|}}
<label class="checkbox mr-10" style="color: inherit; text-transform: none;">{{input type="checkbox" checked=choice.enabled}} {{choice.name}}</label>
{{/each}}
</div>
</div>
{{/if}}

View File

@ -15,7 +15,7 @@
</div>
<div class="col span-4 input-group">
{{input-integer classNames="form-control" value=inputTimeout style="max-width:120px;"}}
<div class="input-group-addon bg-default">{{t 'modalContainerStop.uom'}}</div>
<div class="input-group-addon bg-default">{{t 'generic.seconds'}}</div>
</div>
</div>
<div class="row text-info mb-20">

View File

@ -9,6 +9,7 @@ import { task } from 'ember-concurrency';
export default Ember.Component.extend(NewOrEdit, {
intl: Ember.inject.service(),
catalog: Ember.inject.service(),
projects: Ember.inject.service(),
settings: Ember.inject.service(),
@ -140,9 +141,7 @@ export default Ember.Component.extend(NewOrEdit, {
this.set('stackResource.environment', current);
}
var selectedTemplateModel = yield this.get('store').request({
url: url
}).then((response) => {
var selectedTemplateModel = yield this.get('catalog').fetchByUrl(url).then((response) => {
if (response.questions) {
response.questions.forEach((item) => {
// This will be the component that is rendered to edit this answer

View File

@ -10,7 +10,7 @@ export default Ember.Component.extend({
cancelDisabled: false,
savedLabel: 'saveCancel.saved',
forFooter: true,
classNames: ['text-center' , 'p-20'],
classNames: ['text-center', 'pt-20', 'pb-20'],
classNamesBindings: ['forFooter:footer-actions'],
saving: false,

View File

@ -36,8 +36,6 @@
{{/if}}
</div>
<div>
A: {{get subMatches model.id}}<br/>
B: {{model.instances.length}}<br/>
{{#if showInstanceCount}}
{{#if subMatches}}
{{#if (lt (get subMatches model.id) model.instances.length)}}

View File

@ -1,4 +1,3 @@
import Ember from 'ember';
import Service from 'ui/models/service';
export default Service.extend({

View File

@ -7,6 +7,12 @@ export default Resource.extend({
settings: Ember.inject.service(),
intl: Ember.inject.service(),
headers: function() {
return {
[C.HEADER.PROJECT_ID]: this.get('projects.current.id')
};
}.property('project.current.id'),
cleanProjectUrl: Ember.computed('links.project', function() {
let projectUrl = this.get('links.project');
let pattern = new RegExp('^([a-z]+://|//)', 'i');

View File

@ -2,6 +2,14 @@ import Resource from 'ember-api-store/models/resource';
import C from 'ui/utils/constants';
export default Resource.extend({
projects: Ember.inject.service(),
headers: function() {
return {
[C.HEADER.PROJECT_ID]: this.get('projects.current.id')
};
}.property('project.current.id'),
filesAsArray: function() {
var obj = (this.get('files')||{});
var out = [];

View File

@ -95,7 +95,7 @@ export default Ember.Service.extend({
}
let url = this._addLimits(`${this.get('app.catalogEndpoint')}/templates`, qp);
return this.get('store').request({url: url, headers: {[C.HEADER.PROJECT_ID]: this.get('projects.current.id')},}).then((res) => {
return this.get('store').request({url: url, headers: {[C.HEADER.PROJECT_ID]: this.get('projects.current.id')}}).then((res) => {
res.catalogId = catalogId;
this.set('templateCache', res);
return this.filter(res, params.category, templateBase, plusInfra);
@ -108,6 +108,10 @@ export default Ember.Service.extend({
});
},
fetchByUrl(url) {
return this.get('store').request({url: url, headers: {[C.HEADER.PROJECT_ID]: this.get('projects.current.id')}});
},
filter(data, category, templateBase, plusInfra) {
let bases = [];

View File

@ -1,5 +1,6 @@
import Ember from 'ember';
import C from 'ui/utils/constants';
import Util from 'ui/utils/util';
export default Ember.Service.extend({
cookies: Ember.inject.service(),

View File

@ -13,8 +13,9 @@ generic:
cancel: Cancel
closeModal: Close
command: Command
confirm: Confirm
collapseAll: Collapse All
confirm: Confirm
containers: Containers
created: Created
createdDate: "Created {date}"
default: Default
@ -42,6 +43,7 @@ generic:
loading: "Loading..."
megabyte: 'MB'
mibibyte: 'MiB'
millisecond: 'ms'
missing: "*%MISSING%*"
moreActions: More Actions
na: 'n/a'
@ -55,6 +57,7 @@ generic:
saved: Saved
saving: Saving
search: Search
seconds: seconds
select: Select
service: Service
stack: Stack
@ -2469,12 +2472,24 @@ formUserLabels:
protip: "ProTip: Paste one or more lines of key=value pairs into any key field for easy bulk entry."
formUpgrade:
size: Batch Size
interval: Batch Interval
sec: sec
behavior: Start Behavior
startStop: Start before Stopping
whichServices: Which Services
title: Upgrade
detail: Configure how containers are replaced when performing an upgrade.
batchSize:
label: Batch Size
detail: Up to this many containers will be replaced at once.
interval:
label: Batch Interval
detail: After finishing a batch of containers, wait this long before starting the next batch
behavior: Replacement Behavior
startFirst:
label: Start new, then stop old
detail: Start new containers before stopping the old ones. If the service publishes a port, there must be enough eligible hosts availeblt to run Batch Size new containers, because the old containers are still actively using the ports.
stopFirst:
label: Stop old, then start new
detail: Stop the old containers (up to the Batch Size), then start new ones. This allows the reuse of published ports. Adjust Batch Size to ensure that you keep enough active capacity to run your application.
force:
label: Force Redeploy
detail: During an upgrade, containers are normally only replaced if part of their definition has changed. To force one to redeploy for this upgrade, select it here.
formValueArray:
addActionLabel: Add Value
@ -3190,7 +3205,6 @@ modalAuditlogInfo:
response: "Response Object:"
modalContainerStop:
uom: seconds
header: "Are you sure you want to stop"
helpText: "The container will be forcibly terminated if it does not stop itself before the timeout expires"
protip: "ProTip: Hold the {key} key while clicking stop to bypass this confirmation."
@ -3415,7 +3429,7 @@ newContainer:
keyPlaceholder: e.g. FOO
valueLabel: Value
valuePlaceholder: e.g. bar
emptyPrimaryService: "(Primary Service)"
emptyPrimaryService: "(Primary)"
emptySidekick: "(Sidekick #{num})"
addSidekickContainer: Add Sidekick Container
addSidekickVm: Add Sidekick VM