From 5d3a22e29fa895b9a36a1e074d512012ee71c94f Mon Sep 17 00:00:00 2001 From: Vincent Fiduccia Date: Mon, 22 May 2017 20:57:47 -0700 Subject: [PATCH] Split listeners and portRules --- app/apps-tab/index/route.js | 11 +- app/balancers/index/template.hbs | 10 +- app/catalog-tab/launch/controller.js | 2 +- app/components/edit-balancer/component.js | 117 -------- app/components/edit-balancer/template.hbs | 4 +- .../form-balancer-listeners/component.js | 207 ++++++++++++++ .../form-balancer-listeners/template.hbs | 73 +++++ .../form-balancer-rules/component.js | 96 ++----- .../form-balancer-rules/template.hbs | 264 ++++++++---------- .../form-ssl-termination/component.js | 4 + .../schema/input-container/component.js | 52 ++++ .../schema/input-container/template.hbs | 10 + .../schema/input-service/component.js | 1 - app/mixins/grouped-instances.js | 4 +- app/models/container.js | 6 +- app/models/instance.js | 3 + app/models/loadbalancerservice.js | 4 - app/services/all-containers.js | 57 ++++ app/services/endpoint.js | 9 +- app/signup/template.hbs | 6 +- app/stack/index/controller.js | 26 +- app/stack/index/template.hbs | 6 +- app/stack/route.js | 8 +- app/styles/components/_forms.scss | 2 +- app/verify/template.hbs | 4 +- translations/en-us.yaml | 65 +++-- 26 files changed, 621 insertions(+), 430 deletions(-) create mode 100644 app/components/form-balancer-listeners/component.js create mode 100644 app/components/form-balancer-listeners/template.hbs create mode 100644 app/components/schema/input-container/component.js create mode 100644 app/components/schema/input-container/template.hbs create mode 100644 app/services/all-containers.js diff --git a/app/apps-tab/index/route.js b/app/apps-tab/index/route.js index dbc627351..512f5ce34 100644 --- a/app/apps-tab/index/route.js +++ b/app/apps-tab/index/route.js @@ -15,11 +15,14 @@ export default Ember.Route.extend({ let extInfo = parseExternalId(stack.get('externalId')); deps.push(catalog.fetchTemplate(extInfo.templateId, false)); }); - return Ember.RSVP.all(deps).then((ahray) => { - ahray.forEach((ary) => { - let stck = stacks.findBy('externalIdInfo.templateId', ary.id); - stck.catalogTemplateInfo = ary; // need that generica catalog icon + + return Ember.RSVP.all(deps).then((templates) => { + templates.forEach((template) => { + stacks.filterBy('externalIdInfo.templateId', template.id).forEach((stack) => { + Ember.set(stack,'catalogTemplateInfo',template); + }); }); + return Ember.Object.create({ stacks: stacks, }); diff --git a/app/balancers/index/template.hbs b/app/balancers/index/template.hbs index f338e71a7..309dcda62 100644 --- a/app/balancers/index/template.hbs +++ b/app/balancers/index/template.hbs @@ -62,14 +62,6 @@ {{/if}} {{/sortable-table}} {{else}} -
- -
+ {{empty-table resource="container" newRoute="balancers.new" newTranslationKey="nav.containers.addBalancer"}} {{/if}} diff --git a/app/catalog-tab/launch/controller.js b/app/catalog-tab/launch/controller.js index d7b0cdbdc..dc5e62304 100644 --- a/app/catalog-tab/launch/controller.js +++ b/app/catalog-tab/launch/controller.js @@ -9,7 +9,7 @@ export default Ember.Controller.extend({ actions: { cancel() { - this.transitionToRoute(this.get('parentRoute')); + this.send('goToPrevious','apps-tab.index'); } }, }); diff --git a/app/components/edit-balancer/component.js b/app/components/edit-balancer/component.js index af105b950..65c772ecb 100644 --- a/app/components/edit-balancer/component.js +++ b/app/components/edit-balancer/component.js @@ -16,7 +16,6 @@ export default Ember.Component.extend(NewOrEdit, { isGlobal : null, isRequestedHost : null, upgradeOptions : null, - hasUnsupportedPorts : false, // Errors from components ruleErrors : null, @@ -30,7 +29,6 @@ export default Ember.Component.extend(NewOrEdit, { this._super(...arguments); this.labelsChanged(); this.get('service').initPorts(); - this.updatePorts(); }, actions: { @@ -64,119 +62,6 @@ export default Ember.Component.extend(NewOrEdit, { return this.get('intl').t(k); }.property('intl.locale','needsUpgrade','isService','isVm','service.secondaryLaunchConfigs.length'), - // ---------------------------------- - // Ports - // ---------------------------------- - updatePorts() { - let rules = this.get('service.lbConfig.portRules')||[]; - let publish = []; - let expose = []; - - // Set ports and publish on the launch config - rules.forEach((rule) => { - // The inner one eliminates null/undefined, then the outer one - // converts integers to string (so they can be re-parsed later) - let srcStr = ((rule.get('sourcePort')||'')+'').trim(); - let src = parseInt(srcStr,10); - if ( !src || src < 1 || src > 65535 ) { - return; - } - - let entry = src+":"+src+"/"+rule.get('ipProtocol'); - if ( rule.get('access') === 'public' ) { - // Source IP applies only to public rules - let ip = rule.get('sourceIp'); - if ( ip ) { - // IPv6 - if ( ip.indexOf(":") >= 0 && ip.substr(0,1) !== '[' ) { - entry = '['+ip+']:' + entry; - } else { - entry = ip + ':' + entry; - } - } - - publish.push(entry); - } else { - expose.push(entry); - } - }); - - this.get('service.launchConfig').setProperties({ - ports: publish.uniq(), - expose: expose.uniq(), - }); - }, - - shouldUpdatePorts: function() { - Ember.run.once(this,'updatePorts'); - }.observes('service.lbConfig.portRules.@each.{sourceIp,sourcePort,access,protocol}'), - - - validateRules() { - let intl = this.get('intl'); - let rules = this.get('service.lbConfig.portRules'); - let errors = []; - let seen = {}; - - // Set ports and publish on the launch config - // And also do a bunch of validation while we're here - rules.forEach((rule) => { - // The inner one eliminates null/undefined, then the outer one - // converts integers to string (so they can be re-parsed later) - let srcStr = ((rule.get('sourcePort')||'')+'').trim(); - let tgtStr = ((rule.get('targetPort')||'')+'').trim(); - - if ( !srcStr ) { - errors.push(intl.t('newBalancer.error.noSourcePort')); - return; - } - - let src = parseInt(srcStr,10); - if ( !src || src < 1 || src > 65535 ) { - errors.push(intl.t('newBalancer.error.invalidSourcePort', {num: srcStr})); - } else if ( !tgtStr ) { - tgtStr = srcStr; - } - - let tgt = parseInt(tgtStr,10); - if ( !tgt || tgt < 1 || tgt > 65535 ) { - errors.push(intl.t('newBalancer.error.invalidTargetPort', {num: tgtStr})); - return; - } - - let sourceIp = rule.get('sourceIp'); - let key; - if ( sourceIp ) { - key = '['+sourceIp+']:' + src; - } else { - key = '[0.0.0.0]:' + src; - } - - let access = rule.get('access'); - let id = access + '-' + rule.get('protocol') + '-' + src; - - if ( seen[key] ) { - if ( seen[key] !== id ) { - errors.push(intl.t('newBalancer.error.mixedPort', {num: src})); - } - } else { - seen[key] = id; - } - - if ( !rule.get('serviceId') && !rule.get('selector') ) { - errors.push(intl.t('newBalancer.error.noTarget')); - } - - // Make ports always numeric - rule.setProperties({ - sourcePort: src, - targetPort: tgt, - }); - }); - - this.set('ruleErrors', errors); - }, - needsUpgrade: function() { function arrayToStr(map) { map = map || {}; @@ -278,8 +163,6 @@ export default Ember.Component.extend(NewOrEdit, { // Save // ---------------------------------- willSave() { - this.validateRules(); - let ok = this._super(...arguments); if ( ok && !this.get('isUpgrade') ) { // Set the stack ID diff --git a/app/components/edit-balancer/template.hbs b/app/components/edit-balancer/template.hbs index fde2e7e35..8a8226402 100644 --- a/app/components/edit-balancer/template.hbs +++ b/app/components/edit-balancer/template.hbs @@ -34,9 +34,11 @@
- {{form-balancer-rules + {{form-balancer-listeners service=service + errors=ruleErrors }} +
{{container/form-scheduling diff --git a/app/components/form-balancer-listeners/component.js b/app/components/form-balancer-listeners/component.js new file mode 100644 index 000000000..911717052 --- /dev/null +++ b/app/components/form-balancer-listeners/component.js @@ -0,0 +1,207 @@ +import Ember from 'ember'; +import { parsePortSpec } from 'ui/utils/parse-port'; + +export default Ember.Component.extend({ + intl: Ember.inject.service(), + + service: null, + + ports: null, + protocolChoices: null, + showBackend: null, + errors: null, + + onInit: function() { + let rules = this.get('service.lbConfig.portRules'); + let ports = []; + + rules.forEach((rule) => { + let kind = 'service'; + if ( !!rule.selector ) { + kind= 'selector'; + } else if ( rule.instanceId ) { + rule.kind = 'instance'; + } + + rule.set('kind', kind); + }); + + (this.get('service.launchConfig.ports')||[]).forEach((str) => { + let parsed = parsePortSpec(str); + let obj = Ember.Object.create({ + access: 'public', + protocol: null, + sourcePort: parsed.hostPort, + sourceIp: parsed.hostIp, + rules: [], + }); + ports.push(obj); + }); + + (this.get('service.launchConfig.expose')||[]).forEach((str) => { + let parsed = parsePortSpec(str); + let obj = Ember.Object.create({ + access: 'internal', + protocol: null, + sourcePort: parsed.hostPort, + sourceIp: null, + rules: [], + }); + ports.push(obj); + }); + + // Filter the rules into the right port + ports.forEach((obj) => { + obj.set('rules',rules.filter((x) => { + return x.sourcePort === obj.sourcePort + })); + obj.set('protocol', obj.get('rules.firstObject.protocol')||'http'); + }); + + this.set('ports', ports); + if ( ports.length === 0 ) { + this.send('addPort'); + } + + let protos = this.get('store').getById('schema','portrule').optionsFor('protocol'); + protos.removeObject('udp'); + protos.sort(); + this.set('protocolChoices', protos); + + if ( this.get('showBackend') === null ) { + let hasName = !!rules.findBy('backendName'); + this.set('showBackend', hasName); + } + }.on('init'), + + shouldFlattenAndValidate: function() { + Ember.run.once(this,'flattenAndValidate'); + }.observes('ports.@each.{sourcePort,protocol,access,sourceIp,rules}'), + + flattenAndValidate() { + let intl = this.get('intl'); + let ports = this.get('ports'); + let errors = []; + let rules = []; + let publish = []; + let expose = []; + let seen = {}; + + // Set ports and publish on the launch config + // And also do a bunch of validation while we're here + ports.forEach((port) => { + // 1. Set expose/ports and ensure valid ports/protocols + let srcStr = ((port.get('sourcePort')||'')+'').trim(); + if ( !srcStr ) { + errors.push(intl.t('newBalancer.error.noSourcePort')); + return; + } + + let src = parseInt(srcStr,10); + if ( !src || src < 1 || src > 65535 ) { + errors.push(intl.t('newBalancer.error.invalidSourcePort', {num: srcStr})); + } + + let sourceIp = port.get('sourceIp'); + let uniqueKey; + if ( sourceIp ) { + uniqueKey = '['+sourceIp+']:' + src; + } else { + uniqueKey = '[0.0.0.0]:' + src; + } + + let access = port.get('access'); + let protocol = port.get('protocol'); + let id = access + '-' + protocol + '-' + src; + + if ( seen[uniqueKey] ) { + if ( seen[uniqueKey] !== id ) { + errors.push(intl.t('newBalancer.error.mixedPort', {num: src})); + return; + } + } else { + seen[uniqueKey] = id; + } + + let entry = src+":"+src+"/"+ ( protocol === 'udp' ? 'udp' : 'tcp'); + if ( access === 'public' ) { + // Source IP applies only to public rules + if ( sourceIp ) { + // IPv6 + if ( sourceIp.indexOf(":") >= 0 && sourceIp.substr(0,1) !== '[' ) { + entry = '['+sourceIp+']:' + entry; + } else { + entry = sourceIp + ':' + entry; + } + } + + publish.push(entry); + } else { + expose.push(entry); + } + + // 2. Set rules + port.get('rules').forEach((rule) => { + // The inner one eliminates null/undefined, then the outer one + // converts integers to string (so they can be re-parsed later) + let tgtStr = ((rule.get('targetPort')||'')+'').trim(); + if ( !tgtStr ) { + errors.push(intl.t('newBalancer.error.noTargetPort')); + return; + } + + let tgt = parseInt(tgtStr,10); + if ( !tgt || tgt < 1 || tgt > 65535 ) { + errors.push(intl.t('newBalancer.error.invalidTargetPort', {num: tgtStr})); + return; + } + + if ( !rule.get('serviceId') && !rule.get('instanceId') && !rule.get('selector') ) { + errors.push(intl.t('newBalancer.error.noTarget')); + } + + // Make ports always numeric + rule.setProperties({ + protocol: protocol, + sourcePort: src, + targetPort: tgt, + }); + + rules.push(rule); + }); + }); + + this.setProperties({ + 'service.launchConfig.ports': publish.uniq(), + 'service.launchConfig.expose': expose.uniq(), + 'errors': errors.uniq(), + 'service.lbConfig.portRules': rules.sortBy('priority') + }); + }, + + actions: { + addPort() { + let port = Ember.Object.create({ + access: 'public', + protocol: 'http', + port: null, + sourceIp: null, + rules: [], + }); + + this.get('ports').pushObject(port); + }, + + removePort(port) { + this.get('ports').removeObject(port); + }, + + showBackend() { + this.set('showBackend', true); + }, + + rulesChanged() { + this.shouldFlattenAndValidate(); + }, + }, +}); diff --git a/app/components/form-balancer-listeners/template.hbs b/app/components/form-balancer-listeners/template.hbs new file mode 100644 index 000000000..fcf7e966c --- /dev/null +++ b/app/components/form-balancer-listeners/template.hbs @@ -0,0 +1,73 @@ +
+ + + {{#unless showBackend}} +
+ +
+ {{/unless}} +
+

+ {{t 'formBalancerListeners.help'}} +

+
+ +{{#each ports as |port|}} +
+
+
+ + {{input-integer class="form-control input-sm" min="1" max="65535" value=port.sourcePort placeholder=(t 'formBalancerListeners.sourcePort.placeholder')}} +
+ +
+ + +
+ +
+ + +
+ +
+ + {{#if (eq port.access "public")}} + {{input type="text" class="form-control input-sm" value=port.sourceIp placeholder=(t 'formBalancerListeners.sourceIp.placeholder')}} + {{else}} +
{{t 'generic.na'}}
+ {{/if}} +
+
+ +
+ + {{form-balancer-rules + rules=port.rules + protocol=port.protocol + rulesChanged=(action 'rulesChanged') + singleTarget=false + showBackend=showBackend + editing=true + }} +
+ +
+
+{{else}} +
{{t 'formBalancerListeners.noRules'}}
+{{/each}} diff --git a/app/components/form-balancer-rules/component.js b/app/components/form-balancer-rules/component.js index f6f70dab8..3ebc219bb 100644 --- a/app/components/form-balancer-rules/component.js +++ b/app/components/form-balancer-rules/component.js @@ -1,56 +1,19 @@ import Ember from 'ember'; -import { parsePortSpec } from 'ui/utils/parse-port'; export default Ember.Component.extend({ - intl: Ember.inject.service(), - - service: null, - ruleType: 'portRule', - showListeners: Ember.computed.equal('ruleType','portRule'), - rules: null, - protocolChoices: null, - showBackend: null, - showIp: null, + singleTarget: true, + protocol: null, + editing: true, - onInit: function() { - let rules = this.get('service.lbConfig.portRules'); - if ( !rules ) { - rules = []; - this.set('service.lbConfig.portRules', rules); - } + ruleType: 'portRule', - rules.forEach((rule) => { - rule.isSelector = !!rule.selector; - }); - - this.set('rules', rules); - if ( rules.length === 0 ) { - this.send('addRule'); - } - - let protos = this.get('store').getById('schema','portrule').optionsFor('protocol'); - protos.removeObject('udp'); - protos.sort(); - this.set('protocolChoices', protos); - - if ( this.get('showBackend') === null ) { - let hasName = !!rules.findBy('backendName'); - this.set('showBackend', hasName); - } - - if ( this.get('showIp') === null ) { - this.get('service.launchConfig.ports').forEach((port) => { - let parsed = parsePortSpec(port,'tcp'); - if ( parsed.hostIp ) { - this.set('showIp', true); - } - }); - } - }.on('init'), + rulesChanged: function() { + this.sendAction('rulesChanged'); + }.observes('rules.@each.{hostname,path,kind,instanceId,serviceId,selector,targetPort,backendName}'), actions: { - addRule(isSelector) { + addRule(kind) { let max = 0; let rules = this.get('rules'); rules.forEach((rule) => { @@ -59,9 +22,8 @@ export default Ember.Component.extend({ rules.pushObject(this.get('store').createRecord({ type: this.get('ruleType'), - access: 'public', - isSelector: isSelector, - protocol: 'http', + kind: kind, + protocol: this.get('protocol'), priority: max+1, })); }, @@ -93,16 +55,15 @@ export default Ember.Component.extend({ removeRule(rule) { this.get('rules').removeObject(rule); }, - - showBackend() { - this.set('showBackend', true); - }, - - showIp() { - this.set('showIp', true); - }, }, + protocolChanged: function() { + let protocol = this.get('protocol'); + this.get('rules').forEach((rule) => { + rule.set('protocol', protocol); + }); + }.observes('protocol'), + updatePriorities() { let pri = 1; this.get('rules').forEach((rule) => { @@ -110,27 +71,4 @@ export default Ember.Component.extend({ pri++; }); }, - - minPriority: function() { - let val = null; - this.get('rules').forEach((rule) => { - let cur = rule.get('priority'); - if ( val === null ) { - val = cur; - } else { - val = Math.min(val, cur); - } - }); - - return val; - }.property('rules.@each.priority'), - - maxPriority: function() { - let val = 0; - this.get('rules').forEach((rule) => { - val = Math.max(val, rule.get('priority')); - }); - - return val; - }.property('rules.@each.priority'), }); diff --git a/app/components/form-balancer-rules/template.hbs b/app/components/form-balancer-rules/template.hbs index 42e835e69..ca8bd45d7 100644 --- a/app/components/form-balancer-rules/template.hbs +++ b/app/components/form-balancer-rules/template.hbs @@ -1,159 +1,121 @@ -
-
- -
-
- {{#unless editing}} - + {{else}} + - + - {{/unless}} -
-
- -
-
- {{#if rules.length}} - - - - {{#if showListeners}} - - - {{#if showIp}} - - {{/if}} - - - - - - - {{else}} - - - - {{/if}} - {{#if showBackend}} - - {{/if}} - {{#if showListeners}} - - {{/if}} - - - - - {{#each rules as |rule idx|}} - - {{#if showListeners}} - - - - - {{#if showIp}} - - {{/if}} - - - {{/if}} - - - - {{#if showListeners}} - - {{/if}} - - - - {{#if showListeners}} - - {{/if}} - - - - {{#if showBackend}} - - {{/if}} - - {{#if showListeners}} - - {{/if}} - - {{/each}} - -
 {{t 'formBalancerRules.access.label'}}{{field-required}}{{t 'formBalancerRules.sourceIp.label'}}{{t 'formBalancerRules.protocol.label'}}{{field-required}}{{t 'formBalancerRules.hostname.label'}}{{t 'formBalancerRules.sourcePort.label'}}{{field-required}}{{t 'formBalancerRules.path.label'}}{{t 'formBalancerRules.target'}}{{field-required}}{{t 'formBalancerRules.targetPort.label'}}{{field-required}}{{t 'formBalancerRules.hostname.label'}}{{t 'formBalancerRules.path.label'}}{{t 'formBalancerRules.targetPort.label'}}{{field-required}}{{t 'formBalancerRules.backendName.label'}} 
- - - - - - {{#if (eq rule.access "public")}} - {{input type="text" class="form-control input-sm" value=rule.sourceIp placeholder=(t 'formBalancerRules.sourceIp.placeholder')}} - {{else}} - {{t 'generic.na'}} - {{/if}} - - - - {{#if rule.canHostname}} - {{input type="text" class="form-control input-sm" value=rule.hostname placeholder=(t 'formBalancerRules.hostname.placeholder')}} - {{else}} - {{t 'generic.na'}} - {{/if}} - - {{input-integer class="form-control input-sm" min="1" max="65535" value=rule.sourcePort placeholder=(t 'formBalancerRules.sourcePort.placeholder')}} - - {{#if rule.canPath}} - {{input type="text" class="form-control input-sm" value=rule.path placeholder=(t 'formBalancerRules.path.placeholder')}} - {{else}} - {{t 'generic.na'}} - {{/if}} - - {{#if rule.isSelector}} - {{input type="text" class="form-control input-sm" value=rule.selector placeholder=(t 'formBalancerRules.selector.placeholder')}} - {{else}} - {{schema/input-service selectClass="form-control input-sm" canBalanceTo=true selected=rule.serviceId}} - {{/if}} - - {{input-integer class="form-control input-sm" min="1" max="65535" value=rule.targetPort placeholder=(t 'formBalancerRules.targetPort.placeholder')}} - - {{input type="text" class="form-control input-sm" value=rule.backendName placeholder=(t 'formBalancerRules.backendName.placeholder')}} - - -
-

- {{t 'formBalancerRules.help.prefix'}} - {{#unless showBackend}} - - {{t 'formBalancerRules.help.showBackendLink'}} - - {{/unless}} - {{#unless showIp}} - - {{t 'formBalancerRules.help.showIpLink'}} - - {{/unless}} - {{t 'formBalancerRules.help.suffix'}} -

- {{else}} - {{t 'formBalancerRules.noRules'}} {{/if}} -
+ {{/if}}
+ +{{#if rules.length}} + + + + {{#unless singleTarget}} + + {{/unless}} + + + + + + {{#if singleTarget}} + + {{else}} + + {{/if}} + + + + {{#if showBackend}} + + {{/if}} + + {{#if editing}} + + {{/if}} + + + + + {{#each rules as |rule idx|}} + + {{#unless singleTarget}} + + {{/unless}} + + + + + + {{#if singleTarget}} + + {{else}} + + {{/if}} + + + + {{#if showBackend}} + + {{/if}} + + {{#if editing}} + + {{/if}} + + {{/each}} + +
 {{t 'formBalancerRules.hostname.label'}}{{t 'formBalancerRules.path.label'}} {{t 'formBalancerRules.target'}}{{field-required}}{{t 'formBalancerRules.targetPort.label'}}{{field-required}}{{t 'formBalancerRules.backendName.label'}} 
+ + + + {{#if rule.canHostname}} + {{input type="text" class="input-sm" value=rule.hostname placeholder=(t 'formBalancerRules.hostname.placeholder')}} + {{else}} + {{t 'generic.na'}} + {{/if}} + + {{#if rule.canPath}} + {{input type="text" class="input-sm" value=rule.path placeholder=(t 'formBalancerRules.path.placeholder')}} + {{else}} + {{t 'generic.na'}} + {{/if}} +   + {{#if (eq rule.kind 'selector')}} + {{input type="text" class="input-sm" value=rule.selector placeholder=(t 'formBalancerRules.selector.placeholder')}} + {{else if (eq rule.kind 'instance')}} + {{schema/input-container selectClass="input-sm" selected=rule.instanceId}} + {{else}} + {{schema/input-service selectClass="input-sm" canBalanceTo=true selected=rule.serviceId}} + {{/if}} + + {{input-integer class="input-sm" min="1" max="65535" value=rule.targetPort placeholder=(t 'formBalancerRules.targetPort.placeholder')}} + + {{input type="text" class="input-sm" value=rule.backendName placeholder=(t 'formBalancerRules.backendName.placeholder')}} + + +
+{{else}} + {{t 'formBalancerRules.noRules'}} +{{/if}} diff --git a/app/components/form-ssl-termination/component.js b/app/components/form-ssl-termination/component.js index 12dc6f440..3b00474d5 100644 --- a/app/components/form-ssl-termination/component.js +++ b/app/components/form-ssl-termination/component.js @@ -74,6 +74,10 @@ export default Ember.Component.extend({ count++; } + if ( this.get('lbConfig.needsCertificate') && !count ) { + k = STATUS.INCOMPLETE; + } + if ( count ) { k = STATUS.COUNTCONFIGURED; } diff --git a/app/components/schema/input-container/component.js b/app/components/schema/input-container/component.js new file mode 100644 index 000000000..72e64e8ee --- /dev/null +++ b/app/components/schema/input-container/component.js @@ -0,0 +1,52 @@ +import Ember from 'ember'; + +export default Ember.Component.extend({ + allContainers : Ember.inject.service(), + + selected: null, // Selected service ID + exclude: null, // ID or array of IDs to exclude from list + + // For use as a catalog question + field: null, // Read default from a schema resourceField + value: null, // stackName/serviceName string output + + init() { + this._super(...arguments); + + let def = this.get('field.default'); + if ( def && !this.get('selected') ) { + + let match = this.get('allContainers.list').findBy('name', def); + this.set('selected', match || null); + } + }, + + grouped: function() { + let list = this.get('allContainers.list'); + + let exclude = this.get('exclude'); + if ( exclude ) { + if ( !Ember.isArray(exclude) ) { + exclude = [exclude]; + } + + list = list.filter(x => !exclude.includes(x.id)); + } + + return this.get('allContainers').group(list); + }.property('allContainers.list.[]','canBalanceTo','canHaveContainers'), + + selectedChanged: function() { + let id = this.get('selected'); + let str = null; + + if ( id ) { + let service = this.get('allContainers').byId(id); + if ( service ) { + str = service.get('stack.name') + '/' + service.get('name'); + } + } + + this.set('value', str); + }.observes('selected'), +}); diff --git a/app/components/schema/input-container/template.hbs b/app/components/schema/input-container/template.hbs new file mode 100644 index 000000000..d909d9b90 --- /dev/null +++ b/app/components/schema/input-container/template.hbs @@ -0,0 +1,10 @@ + diff --git a/app/components/schema/input-service/component.js b/app/components/schema/input-service/component.js index c0dcac20a..9510f06fa 100644 --- a/app/components/schema/input-service/component.js +++ b/app/components/schema/input-service/component.js @@ -70,6 +70,5 @@ export default Ember.Component.extend({ } this.set('value', str); - console.log('val', str); }.observes('selected'), }); diff --git a/app/mixins/grouped-instances.js b/app/mixins/grouped-instances.js index 6eb40b554..1c4a064b8 100644 --- a/app/mixins/grouped-instances.js +++ b/app/mixins/grouped-instances.js @@ -51,8 +51,8 @@ export default Ember.Mixin.create({ // let version = instance.get('version')||""; let k8sName = (instance.get('labels')||{})[C.LABEL.K8S_POD_NAMESPACE] || ''; - let stackId = instance.get('primaryStack.id') || ''; - let stackName = instance.get('primaryStack.displayName') || ''; + let stackId = instance.get('stack.id') || ''; + let stackName = instance.get('stack.displayName') || ''; let groupId, groupName; if ( k8sName ) { diff --git a/app/models/container.js b/app/models/container.js index a352fb8df..132b288fc 100644 --- a/app/models/container.js +++ b/app/models/container.js @@ -30,17 +30,13 @@ var Container = Instance.extend({ primaryHost : denormalizeId('hostId'), services : denormalizeIdArray('serviceIds'), primaryService : Ember.computed.alias('services.firstObject'), - primaryStack : Ember.computed.alias('primaryService.stack'), - referencedStack : denormalizeId('stackId'), referencedService : denormalizeId('serviceId'), service: Ember.computed('primaryService','referencedService', function() { return this.get('referencedService') || this.get('primaryService'); }), - stack: Ember.computed('primaryStack','referencedStack', function() { - return this.get('referencedStack') || this.get('primaryStack'); - }), + stack: denormalizeId('stackId'), actions: { restart: function() { diff --git a/app/models/instance.js b/app/models/instance.js index d63cbc41f..8048cf71a 100644 --- a/app/models/instance.js +++ b/app/models/instance.js @@ -2,8 +2,11 @@ import Ember from 'ember'; import Resource from 'ember-api-store/models/resource'; import C from 'ui/utils/constants'; import { formatSi } from 'ui/utils/util'; +import { denormalizeId } from 'ember-api-store/utils/denormalize'; export default Resource.extend({ + stack: denormalizeId('stackId'), + isSystem: function() { if ( this.get('system') ) { return true; diff --git a/app/models/loadbalancerservice.js b/app/models/loadbalancerservice.js index ccb5a0ca6..33a5f42ea 100644 --- a/app/models/loadbalancerservice.js +++ b/app/models/loadbalancerservice.js @@ -27,10 +27,6 @@ var LoadBalancerService = Service.extend({ let publish = this.get('launchConfig.ports')||[]; publish.forEach((str) => { let spec = parsePortSpec(str,'tcp'); - if ( !spec.hostPort || spec.hostIp ) { - this.set('hasUnsupportedPorts', true); - } - if ( spec.hostPort ) { rules.filterBy('sourcePort', spec.hostPort).forEach((rule) => { rule.set('access', 'public'); diff --git a/app/services/all-containers.js b/app/services/all-containers.js new file mode 100644 index 000000000..71e8f2c36 --- /dev/null +++ b/app/services/all-containers.js @@ -0,0 +1,57 @@ +import Ember from 'ember'; + +export default Ember.Service.extend({ + intl: Ember.inject.service(), + store: Ember.inject.service(), + prefs: Ember.inject.service(), + + list: function() { + let intl = this.get('intl'); + let showSystem = this.get('prefs.showSystemResources'); + + return this.get('_allInstances').filter((inst) => !inst.get('serviceId') && (!inst.get('isSystem') || showSystem)).map((inst) => { + let stackName = 'Standalone'; + if ( inst.get('stack') ) { + stackName = inst.get('stack.displayName') || '('+inst.get('stackId')+')'; + } + + return { + group: intl.t('allServices.stackGroup', {name: stackName}), + id: inst.get('id'), + stackName: stackName, + name: inst.get('displayName'), + obj: inst, + }; + }); + }.property('_allInstances.@each.{id,system,displayName}','prefs.showSystemResources'), + + grouped: function() { + return this.group(this.get('list')); + }.property('list.[]'), + + group(list) { + let out = {}; + + list.slice().sortBy('group','name','id').forEach((inst) => { + let ary = out[inst.group]; + if( !ary ) { + ary = []; + out[inst.group] = ary; + } + + ary.push(inst); + }); + + return out; + }, + + _allInstances: function() { + let store = this.get('store'); + store.find('instance'); + return store.all('instance'); + }.property(), + + byId(id) { + return this.get('store').getById('instance', id); + }, +}); diff --git a/app/services/endpoint.js b/app/services/endpoint.js index d120b81d0..3c0157f64 100644 --- a/app/services/endpoint.js +++ b/app/services/endpoint.js @@ -8,7 +8,12 @@ export default Ember.Service.extend({ settings: Ember.inject.service(), absolute: function() { - var url = this.get('app.apiServer'); + let setting = this.get(`settings.${C.SETTING.API_HOST}`); + if ( setting && setting.indexOf('http') !== 0 ) { + setting = 'http://' + setting; + } + + let url = setting || this.get('app.apiServer'); // If the URL is relative, add on the current base URL from the browser if ( url.indexOf('http') !== 0 ) @@ -20,7 +25,7 @@ export default Ember.Service.extend({ url = url.replace(/\/+$/,'') + '/'; return url; - }.property('app.apiServer'), + }.property(`settings.${C.SETTING.API_HOST}`,'app.apiServer'), host: function() { var a = document.createElement('a'); diff --git a/app/signup/template.hbs b/app/signup/template.hbs index ee5d7e5fa..c2050f2dc 100644 --- a/app/signup/template.hbs +++ b/app/signup/template.hbs @@ -20,14 +20,14 @@

{{t 'signupPage.header'}}

- +
{{input type="text" value=model.name}}
- +
{{input type="email" value=model.email}}
@@ -38,4 +38,4 @@
{{/if}} -{{/authorize-user}} \ No newline at end of file +{{/authorize-user}} diff --git a/app/stack/index/controller.js b/app/stack/index/controller.js index f090707df..2044e7149 100644 --- a/app/stack/index/controller.js +++ b/app/stack/index/controller.js @@ -1,6 +1,8 @@ import Ember from 'ember'; export default Ember.Controller.extend({ + prefs: Ember.inject.service(), + showAddtlInfo: false, selectedService: null, @@ -61,14 +63,6 @@ export default Ember.Controller.extend({ }, ], - stackContainers: Ember.computed('model.stack.services.@each.healthState', function() { - var neu = []; - this.get('model.stack.services').forEach((service) => { - neu = neu.concat(service.get('instances')); - }); - return neu; - }), - getType(ownType, real=true) { return this.get('model.services').filter((service) => { if (real ? (service.get('isReal') && service.get('kind') === ownType) : (service.get('kind') === ownType)) { @@ -78,18 +72,28 @@ export default Ember.Controller.extend({ }); }, - scalingGroups: Ember.computed('model.services.@each.healthState', function() { + scalingGroups: Ember.computed('model.services.[]', function() { return this.getType('scalingGroup'); }), - loadBalancers: Ember.computed('model.services.@each.healthState', function() { + loadBalancers: Ember.computed('model.services.[]', function() { return this.getType('loadBalancerService'); }), - dnsServices: Ember.computed('model.services.@each.healthState', function() { + dnsServices: Ember.computed('model.services.[]', function() { return this.getType('dnsService', false).concat(this.getType('externalService', false)); }), + instances: Ember.computed('model.instances.[]','prefs.showSystemResources', function() { + let out = this.get('model.instances').filterBy('stackId', this.get('model.stack.id')); + out = out.filterBy('serviceId', null); + if ( !this.get('prefs.showSystemResources') ) { + out = out.filterBy('isSystem', false); + } + + return out; + }), + instanceCount: function() { var count = 0; (this.get('model.stack.services')||[]).forEach((service) => { diff --git a/app/stack/index/template.hbs b/app/stack/index/template.hbs index 83d6cd312..29c9015b7 100644 --- a/app/stack/index/template.hbs +++ b/app/stack/index/template.hbs @@ -12,15 +12,15 @@ {{#accordion-list-item title=(t 'stackPage.containers.header') detail=(t 'stackPage.containers.detail') - status=(t 'stackPage.containers.status' count=model.instances.length) - statusClass=(if model.instances.length 'bg-success' 'text-muted') + status=(t 'stackPage.containers.status' count=instances.length) + statusClass=(if instances.length 'bg-success' 'text-muted') expandAll=al.expandAll expand=(action expandFn) componentName='container-table' as | parent | }} {{component parent.intent - body=model.instances + body=instances search=true sortBy=sortBy stickyHeader=false diff --git a/app/stack/route.js b/app/stack/route.js index 4f1a99e8f..8e94fdb7a 100644 --- a/app/stack/route.js +++ b/app/stack/route.js @@ -3,16 +3,10 @@ import Ember from 'ember'; export default Ember.Route.extend({ model: function(params) { var store = this.get('store'); - var all = this.modelFor('stacks'); return store.find('stack', params.stack_id).then((stack) => { - var neu = []; - stack.get('services').forEach((service) => { - neu = neu.concat(service.get('instances')); - }); return Ember.Object.create({ stack: stack, - all: all, - instances: neu, + instances: store.all('instance'), services: stack.get('services') }); }); diff --git a/app/styles/components/_forms.scss b/app/styles/components/_forms.scss index ecd48b4b4..fa18c06ac 100644 --- a/app/styles/components/_forms.scss +++ b/app/styles/components/_forms.scss @@ -112,7 +112,7 @@ textarea { .form-control-static { line-height: 24px; - padding: 5px 0; + padding: 3px 0; border: 2px solid transparent; } diff --git a/app/verify/template.hbs b/app/verify/template.hbs index ea71a30af..47d08e777 100644 --- a/app/verify/template.hbs +++ b/app/verify/template.hbs @@ -6,7 +6,7 @@

{{t 'verifyPage.subtext'}}

- +
{{input type="text" value=model.name id="login-user-name"}}
@@ -40,4 +40,4 @@
{{/unless}} -{{/authorize-user}} \ No newline at end of file +{{/authorize-user}} diff --git a/translations/en-us.yaml b/translations/en-us.yaml index ad2a9684c..9d78f1fd9 100644 --- a/translations/en-us.yaml +++ b/translations/en-us.yaml @@ -1170,7 +1170,7 @@ stackPage: backLink: Back to all stacks containers: header: Containers - detail: A list of containers in this stack + detail: Standalone Containers that are not part of a Scaling Group or Load Balancer status: | {count, plural, =0 {No containers} @@ -1179,7 +1179,7 @@ stackPage: } scalingGroups: header: Scaling Groups - detail: 'A list of scaling groups this stack is attached to.' + detail: '' status: | {count, plural, =0 {No groups} @@ -1188,7 +1188,7 @@ stackPage: } loadBalancers: header: Load Balancers - detail: 'A list of load balancers in this stack' + detail: '' status: | {count, plural, =0 {No balancers} @@ -1196,13 +1196,13 @@ stackPage: other {# balancers} } dnsServices: - header: DNS Services - detail: 'A list of DNS Services in this stack' + header: DNS Entries + detail: '' status: | {count, plural, - =0 {No rules} - =1 {# rule} - other {# rules} + =0 {No entries} + =1 {# entry} + other {# entries} } newStack: @@ -1915,19 +1915,20 @@ formBalancerConfig: See haproxy documentation fore more info about specific options that can go into the the config file. When overriding the backend or similar lines which include the IP address of the target container, use $IP where the address goes and {appName} will generate the appropriate line(s). config: prompt: Custom haproxy.cfg content -formBalancerRules: - label: Port Rules - detail: 'These properties show the port mapping details of your container' + +formBalancerListeners: + label: Listeners & Target Rules + detail: Control the mapping of requests coming into the balancer to the desired target. status: | {count, plural, =0 {No rules} =1 {# rule} other {# rules} } - noRules: No Rules - addServiceLabel: Add Scaling Group Rule - addSelectorLabel: Add Selector Rule - target: Target + noRules: No Ports + addPortLabel: Add a Listening Port + removePortLabel: Remove this Listening Port + showBackendLabel: "Customize backend names" access: label: Access public: Public @@ -1936,10 +1937,19 @@ formBalancerRules: label: Protocol sourceIp: label: Host IP - placeholder: e.g. 1.2.3.4 + placeholder: "e.g. 1.2.3.4; Default: All" sourcePort: - label: Port + label: Listening Port placeholder: e.g. 80 + help: "Host and Path rules are matched top-to-bottom in the order shown. Backends will be named randomly by default; to customize the generated backends, provide a name and then refer to that name in your custom haproxy.cfg." + +formBalancerRules: + label: Target Rules + noRules: No Rules + addServiceLabel: Add a Service + addInstanceLabel: Add a Container + addSelectorLabel: Add a Selector + addTargetLabel: Add a Rule path: label: Path placeholder: e.g. /foo @@ -1956,16 +1966,14 @@ formBalancerRules: label: Priority moveUp: Move Up moveDown: Move Down - serviceId: + target: Target + container: + label: Container + service: label: Service selector: label: Selector placeholder: e.g. foo=bar - help: - prefix: "Host and Path rules are matched top-to-bottom in the order shown. Backends will be named randomly by default; to customize the generated backends, provide a name and then refer to that in the custom haproxy.cfg. " - showBackendLink: "Show custom backend names." - showIpLink: "Show host IP address options." - suffix: "" formCloudHost: title: Host @@ -2101,7 +2109,7 @@ formHealthCheck: label: Host Header placeholder: e.g. www.example.com port: - label: Port + label: Listening Port placeholder: e.g. 80 initializingTimeout: label: Initializing Timeout @@ -2431,7 +2439,7 @@ formStickiness: indirect: Indirect sendHeader: Send no-cache header onPost: Only set cookie on POST - noPorts: There are no HTTP Port Rules configured. + noPorts: There are no HTTP Listeners configured. placeholder: sticky: e.g. sticky @@ -3360,12 +3368,13 @@ newBalancer: edit: Edit Load Balancer upgrade: Upgrade Load Balancer error: - noRules: "Choose one or more port rules to listen on" + noRules: "You must have one or more listening ports and target rules" noSourcePort: "Source Port is required on each rule" invalidSourcePort: "Invalid source port: '{num}'" invalidTargetPort: "Invalid target port: '{num}'" mixedPort: "Port {num} has multiple rules with conflicting access/protcols" noTarget: "Target is required on each rule" + noTargetPort: "Target Port is required on each rule" needsCertificate: "A certificate is required because there are SSL/TLS port rules" newCatalog: @@ -3614,10 +3623,12 @@ schema: n: "False" inputCertificate: prompt: Choose a Certificate... + inputContainer: + prompt: Choose a Container... inputEnum: option: Choose an option... inputHost: - label: Select Host + label: Choose a Host... inputService: prompt: Choose a Service... inputSecret: