mirror of https://github.com/rancher/ui.git
Split listeners and portRules
This commit is contained in:
parent
e234dfb492
commit
5d3a22e29f
|
|
@ -15,11 +15,14 @@ export default Ember.Route.extend({
|
||||||
let extInfo = parseExternalId(stack.get('externalId'));
|
let extInfo = parseExternalId(stack.get('externalId'));
|
||||||
deps.push(catalog.fetchTemplate(extInfo.templateId, false));
|
deps.push(catalog.fetchTemplate(extInfo.templateId, false));
|
||||||
});
|
});
|
||||||
return Ember.RSVP.all(deps).then((ahray) => {
|
|
||||||
ahray.forEach((ary) => {
|
return Ember.RSVP.all(deps).then((templates) => {
|
||||||
let stck = stacks.findBy('externalIdInfo.templateId', ary.id);
|
templates.forEach((template) => {
|
||||||
stck.catalogTemplateInfo = ary; // need that generica catalog icon
|
stacks.filterBy('externalIdInfo.templateId', template.id).forEach((stack) => {
|
||||||
|
Ember.set(stack,'catalogTemplateInfo',template);
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
return Ember.Object.create({
|
return Ember.Object.create({
|
||||||
stacks: stacks,
|
stacks: stacks,
|
||||||
});
|
});
|
||||||
|
|
|
||||||
|
|
@ -62,14 +62,6 @@
|
||||||
{{/if}}
|
{{/if}}
|
||||||
{{/sortable-table}}
|
{{/sortable-table}}
|
||||||
{{else}}
|
{{else}}
|
||||||
<div class="row">
|
{{empty-table resource="container" newRoute="balancers.new" newTranslationKey="nav.containers.addBalancer"}}
|
||||||
<div class="col span-6 offset-3 text-center pt-40 pb-40">
|
|
||||||
<img style="width: 75%;" src="{{app.baseAssets}}assets/images/resources/container.svg"/>
|
|
||||||
<a class="btn bg-link icon-btn mt-50" href="{{href-to 'balancers.new'}}">
|
|
||||||
<span class="darken"><i class="icon icon-plus text-small"></i></span>
|
|
||||||
<span>{{t 'nav.containers.addBalancer'}}</span>
|
|
||||||
</a>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
{{/if}}
|
{{/if}}
|
||||||
</section>
|
</section>
|
||||||
|
|
|
||||||
|
|
@ -9,7 +9,7 @@ export default Ember.Controller.extend({
|
||||||
|
|
||||||
actions: {
|
actions: {
|
||||||
cancel() {
|
cancel() {
|
||||||
this.transitionToRoute(this.get('parentRoute'));
|
this.send('goToPrevious','apps-tab.index');
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|
|
||||||
|
|
@ -16,7 +16,6 @@ export default Ember.Component.extend(NewOrEdit, {
|
||||||
isGlobal : null,
|
isGlobal : null,
|
||||||
isRequestedHost : null,
|
isRequestedHost : null,
|
||||||
upgradeOptions : null,
|
upgradeOptions : null,
|
||||||
hasUnsupportedPorts : false,
|
|
||||||
|
|
||||||
// Errors from components
|
// Errors from components
|
||||||
ruleErrors : null,
|
ruleErrors : null,
|
||||||
|
|
@ -30,7 +29,6 @@ export default Ember.Component.extend(NewOrEdit, {
|
||||||
this._super(...arguments);
|
this._super(...arguments);
|
||||||
this.labelsChanged();
|
this.labelsChanged();
|
||||||
this.get('service').initPorts();
|
this.get('service').initPorts();
|
||||||
this.updatePorts();
|
|
||||||
},
|
},
|
||||||
|
|
||||||
actions: {
|
actions: {
|
||||||
|
|
@ -64,119 +62,6 @@ export default Ember.Component.extend(NewOrEdit, {
|
||||||
return this.get('intl').t(k);
|
return this.get('intl').t(k);
|
||||||
}.property('intl.locale','needsUpgrade','isService','isVm','service.secondaryLaunchConfigs.length'),
|
}.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() {
|
needsUpgrade: function() {
|
||||||
function arrayToStr(map) {
|
function arrayToStr(map) {
|
||||||
map = map || {};
|
map = map || {};
|
||||||
|
|
@ -278,8 +163,6 @@ export default Ember.Component.extend(NewOrEdit, {
|
||||||
// Save
|
// Save
|
||||||
// ----------------------------------
|
// ----------------------------------
|
||||||
willSave() {
|
willSave() {
|
||||||
this.validateRules();
|
|
||||||
|
|
||||||
let ok = this._super(...arguments);
|
let ok = this._super(...arguments);
|
||||||
if ( ok && !this.get('isUpgrade') ) {
|
if ( ok && !this.get('isUpgrade') ) {
|
||||||
// Set the stack ID
|
// Set the stack ID
|
||||||
|
|
|
||||||
|
|
@ -34,9 +34,11 @@
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<section class="horizontal-form container-fluid">
|
<section class="horizontal-form container-fluid">
|
||||||
{{form-balancer-rules
|
{{form-balancer-listeners
|
||||||
service=service
|
service=service
|
||||||
|
errors=ruleErrors
|
||||||
}}
|
}}
|
||||||
|
<hr/>
|
||||||
</section>
|
</section>
|
||||||
|
|
||||||
{{container/form-scheduling
|
{{container/form-scheduling
|
||||||
|
|
|
||||||
|
|
@ -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();
|
||||||
|
},
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
@ -0,0 +1,73 @@
|
||||||
|
<div>
|
||||||
|
<label>{{t 'formBalancerListeners.label'}}</label>
|
||||||
|
<button class="btn bg-link icon-btn ml-20" {{action "addPort"}}>
|
||||||
|
<span class="darken"><i class="icon icon-plus text-small"></i></span>
|
||||||
|
<span>{{t 'formBalancerListeners.addPortLabel'}}</span>
|
||||||
|
</button>
|
||||||
|
{{#unless showBackend}}
|
||||||
|
<div class="pull-right clearfix">
|
||||||
|
<button class="btn bg-transparent p-0 text-small text-right" {{action "showBackend"}}>
|
||||||
|
{{t 'formBalancerListeners.showBackendLabel'}}
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
{{/unless}}
|
||||||
|
<hr/>
|
||||||
|
<p class="text-info">
|
||||||
|
{{t 'formBalancerListeners.help'}}
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{{#each ports as |port|}}
|
||||||
|
<div class="box mb-10">
|
||||||
|
<div class="row">
|
||||||
|
<div class="col span-3">
|
||||||
|
<label class="acc-label">{{t 'formBalancerListeners.sourcePort.label'}}{{field-required}}</label>
|
||||||
|
{{input-integer class="form-control input-sm" min="1" max="65535" value=port.sourcePort placeholder=(t 'formBalancerListeners.sourcePort.placeholder')}}
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="col span-3">
|
||||||
|
<label class="acc-label">{{t 'formBalancerListeners.protocol.label'}}</label>
|
||||||
|
<select class="form-control input-sm" onchange={{action (mut port.protocol) value="target.value"}}>
|
||||||
|
{{#each protocolChoices as |proto|}}
|
||||||
|
<option value={{proto}} selected={{eq port.protocol proto}}>{{upper-case proto}}</option>
|
||||||
|
{{/each}}
|
||||||
|
</select>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="col span-3">
|
||||||
|
<label class="acc-label">{{t 'formBalancerListeners.access.label'}}</label>
|
||||||
|
<select class="form-control input-sm" onchange={{action (mut port.access) value="target.value"}}>
|
||||||
|
<option value="public" selected={{eq port.access "public"}}>{{t 'formBalancerListeners.access.public'}}</option>
|
||||||
|
<option value="internal" selected={{eq port.access "internal"}}>{{t 'formBalancerListeners.access.internal'}}</option>
|
||||||
|
</select>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="col span-3">
|
||||||
|
<label class="acc-label">{{t 'formBalancerListeners.sourceIp.label'}}</label>
|
||||||
|
{{#if (eq port.access "public")}}
|
||||||
|
{{input type="text" class="form-control input-sm" value=port.sourceIp placeholder=(t 'formBalancerListeners.sourceIp.placeholder')}}
|
||||||
|
{{else}}
|
||||||
|
<div class="text-muted form-control-static">{{t 'generic.na'}}</div>
|
||||||
|
{{/if}}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<hr/>
|
||||||
|
|
||||||
|
{{form-balancer-rules
|
||||||
|
rules=port.rules
|
||||||
|
protocol=port.protocol
|
||||||
|
rulesChanged=(action 'rulesChanged')
|
||||||
|
singleTarget=false
|
||||||
|
showBackend=showBackend
|
||||||
|
editing=true
|
||||||
|
}}
|
||||||
|
<div class="clearfix">
|
||||||
|
<button class="btn bg-transparent p-0 text-small pull-right" {{action "removePort" port}}>
|
||||||
|
{{t 'formBalancerListeners.removePortLabel'}}
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
{{else}}
|
||||||
|
<div class="p-20">{{t 'formBalancerListeners.noRules'}}</div>
|
||||||
|
{{/each}}
|
||||||
|
|
@ -1,56 +1,19 @@
|
||||||
import Ember from 'ember';
|
import Ember from 'ember';
|
||||||
import { parsePortSpec } from 'ui/utils/parse-port';
|
|
||||||
|
|
||||||
export default Ember.Component.extend({
|
export default Ember.Component.extend({
|
||||||
intl: Ember.inject.service(),
|
|
||||||
|
|
||||||
service: null,
|
|
||||||
ruleType: 'portRule',
|
|
||||||
showListeners: Ember.computed.equal('ruleType','portRule'),
|
|
||||||
|
|
||||||
rules: null,
|
rules: null,
|
||||||
protocolChoices: null,
|
singleTarget: true,
|
||||||
showBackend: null,
|
protocol: null,
|
||||||
showIp: null,
|
editing: true,
|
||||||
|
|
||||||
onInit: function() {
|
ruleType: 'portRule',
|
||||||
let rules = this.get('service.lbConfig.portRules');
|
|
||||||
if ( !rules ) {
|
|
||||||
rules = [];
|
|
||||||
this.set('service.lbConfig.portRules', rules);
|
|
||||||
}
|
|
||||||
|
|
||||||
rules.forEach((rule) => {
|
rulesChanged: function() {
|
||||||
rule.isSelector = !!rule.selector;
|
this.sendAction('rulesChanged');
|
||||||
});
|
}.observes('rules.@each.{hostname,path,kind,instanceId,serviceId,selector,targetPort,backendName}'),
|
||||||
|
|
||||||
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'),
|
|
||||||
|
|
||||||
actions: {
|
actions: {
|
||||||
addRule(isSelector) {
|
addRule(kind) {
|
||||||
let max = 0;
|
let max = 0;
|
||||||
let rules = this.get('rules');
|
let rules = this.get('rules');
|
||||||
rules.forEach((rule) => {
|
rules.forEach((rule) => {
|
||||||
|
|
@ -59,9 +22,8 @@ export default Ember.Component.extend({
|
||||||
|
|
||||||
rules.pushObject(this.get('store').createRecord({
|
rules.pushObject(this.get('store').createRecord({
|
||||||
type: this.get('ruleType'),
|
type: this.get('ruleType'),
|
||||||
access: 'public',
|
kind: kind,
|
||||||
isSelector: isSelector,
|
protocol: this.get('protocol'),
|
||||||
protocol: 'http',
|
|
||||||
priority: max+1,
|
priority: max+1,
|
||||||
}));
|
}));
|
||||||
},
|
},
|
||||||
|
|
@ -93,16 +55,15 @@ export default Ember.Component.extend({
|
||||||
removeRule(rule) {
|
removeRule(rule) {
|
||||||
this.get('rules').removeObject(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() {
|
updatePriorities() {
|
||||||
let pri = 1;
|
let pri = 1;
|
||||||
this.get('rules').forEach((rule) => {
|
this.get('rules').forEach((rule) => {
|
||||||
|
|
@ -110,27 +71,4 @@ export default Ember.Component.extend({
|
||||||
pri++;
|
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'),
|
|
||||||
});
|
});
|
||||||
|
|
|
||||||
|
|
@ -1,159 +1,121 @@
|
||||||
<div class="row">
|
<div class="clearfix">
|
||||||
<div class="col span-2 col-inline">
|
<label class="acc-label">{{t 'formBalancerRules.label'}}</label>
|
||||||
<label>{{t 'formBalancerRules.label'}}</label>
|
{{#if editing}}
|
||||||
</div>
|
{{#if singleTarget}}
|
||||||
<div class="col span-8">
|
<button class="btn bg-link icon-btn" {{action "addRule" "target"}}>
|
||||||
{{#unless editing}}
|
<span class="darken"><i class="icon icon-plus text-small"></i></span>
|
||||||
<button class="btn bg-link icon-btn" {{action "addRule" false}}>
|
<span>{{t 'formBalancerRules.addTargetLabel'}}</span>
|
||||||
|
</button>
|
||||||
|
{{else}}
|
||||||
|
<button class="btn bg-link icon-btn" {{action "addRule" "service"}}>
|
||||||
<span class="darken"><i class="icon icon-plus text-small"></i></span>
|
<span class="darken"><i class="icon icon-plus text-small"></i></span>
|
||||||
<span>{{t 'formBalancerRules.addServiceLabel'}}</span>
|
<span>{{t 'formBalancerRules.addServiceLabel'}}</span>
|
||||||
</button>
|
</button>
|
||||||
<button class="btn bg-link icon-btn pl-20" {{action "addRule" true}}>
|
<button class="btn bg-link icon-btn ml-20" {{action "addRule" "instance"}}>
|
||||||
|
<span class="darken"><i class="icon icon-plus text-small"></i></span>
|
||||||
|
<span>{{t 'formBalancerRules.addInstanceLabel'}}</span>
|
||||||
|
</button>
|
||||||
|
<button class="btn bg-link icon-btn ml-20" {{action "addRule" "selector"}}>
|
||||||
<span class="darken"><i class="icon icon-plus text-small"></i></span>
|
<span class="darken"><i class="icon icon-plus text-small"></i></span>
|
||||||
<span>{{t 'formBalancerRules.addSelectorLabel'}}</span>
|
<span>{{t 'formBalancerRules.addSelectorLabel'}}</span>
|
||||||
</button>
|
</button>
|
||||||
{{/unless}}
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div class="row">
|
|
||||||
<div class="">
|
|
||||||
{{#if rules.length}}
|
|
||||||
<table class="grid fixed no-lines no-top-padding tight mb-0">
|
|
||||||
<thead>
|
|
||||||
<tr>
|
|
||||||
{{#if showListeners}}
|
|
||||||
<th width="30"> </th>
|
|
||||||
<th width="100">{{t 'formBalancerRules.access.label'}}{{field-required}}</th>
|
|
||||||
{{#if showIp}}
|
|
||||||
<th>{{t 'formBalancerRules.sourceIp.label'}}</th>
|
|
||||||
{{/if}}
|
|
||||||
<th width="100">{{t 'formBalancerRules.protocol.label'}}{{field-required}}</th>
|
|
||||||
<th class="divided">{{t 'formBalancerRules.hostname.label'}}</th>
|
|
||||||
<th width="100">{{t 'formBalancerRules.sourcePort.label'}}{{field-required}}</th>
|
|
||||||
<th>{{t 'formBalancerRules.path.label'}}</th>
|
|
||||||
<th class="divided">{{t 'formBalancerRules.target'}}{{field-required}}</th>
|
|
||||||
<th width="100">{{t 'formBalancerRules.targetPort.label'}}{{field-required}}</th>
|
|
||||||
{{else}}
|
|
||||||
<th>{{t 'formBalancerRules.hostname.label'}}</th>
|
|
||||||
<th>{{t 'formBalancerRules.path.label'}}</th>
|
|
||||||
<th width="100" class="divided">{{t 'formBalancerRules.targetPort.label'}}{{field-required}}</th>
|
|
||||||
{{/if}}
|
|
||||||
{{#if showBackend}}
|
|
||||||
<th class="divided">{{t 'formBalancerRules.backendName.label'}}</th>
|
|
||||||
{{/if}}
|
|
||||||
{{#if showListeners}}
|
|
||||||
<th width="40"> </th>
|
|
||||||
{{/if}}
|
|
||||||
</tr>
|
|
||||||
</thead>
|
|
||||||
|
|
||||||
<tbody>
|
|
||||||
{{#each rules as |rule idx|}}
|
|
||||||
<tr>
|
|
||||||
{{#if showListeners}}
|
|
||||||
<td data-title="{{t 'formBalancerRules.priority.label'}}">
|
|
||||||
<button class="btn bg-default btn-xs" {{action "moveUp" rule}} disabled={{eq rule.priority minPriority}}>
|
|
||||||
<i class="icon icon-chevron-up"></i>
|
|
||||||
</button>
|
|
||||||
<button class="btn bg-default btn-xs" {{action "moveDown" rule}} disabled={{eq rule.priority maxPriority}}>
|
|
||||||
<i class="icon icon-chevron-down"></i>
|
|
||||||
</button>
|
|
||||||
</td>
|
|
||||||
|
|
||||||
<td data-title="{{t 'formBalancerRules.access.label'}}">
|
|
||||||
<select class="form-control input-sm" onchange={{action (mut rule.access) value="target.value"}}>
|
|
||||||
<option value="public" selected={{eq rule.access "public"}}>{{t 'formBalancerRules.access.public'}}</option>
|
|
||||||
<option value="internal" selected={{eq rule.access "internal"}}>{{t 'formBalancerRules.access.internal'}}</option>
|
|
||||||
</select>
|
|
||||||
</td>
|
|
||||||
|
|
||||||
{{#if showIp}}
|
|
||||||
<td data-title="{{t 'formBalancerRules.sourceIp.label'}}">
|
|
||||||
{{#if (eq rule.access "public")}}
|
|
||||||
{{input type="text" class="form-control input-sm" value=rule.sourceIp placeholder=(t 'formBalancerRules.sourceIp.placeholder')}}
|
|
||||||
{{else}}
|
|
||||||
<span class="text-muted">{{t 'generic.na'}}</span>
|
|
||||||
{{/if}}
|
|
||||||
</td>
|
|
||||||
{{/if}}
|
|
||||||
|
|
||||||
<td data-title="{{t 'formBalancerRules.protocol.label'}}">
|
|
||||||
<select class="form-control input-sm" onchange={{action (mut rule.protocol) value="target.value"}}>
|
|
||||||
{{#each protocolChoices as |proto|}}
|
|
||||||
<option value={{proto}} selected={{eq rule.protocol proto}}>{{upper-case proto}}</option>
|
|
||||||
{{/each}}
|
|
||||||
</select>
|
|
||||||
</td>
|
|
||||||
{{/if}}
|
|
||||||
|
|
||||||
<td class="divided" data-title="{{t 'formBalancerRules.hostname.label'}}">
|
|
||||||
{{#if rule.canHostname}}
|
|
||||||
{{input type="text" class="form-control input-sm" value=rule.hostname placeholder=(t 'formBalancerRules.hostname.placeholder')}}
|
|
||||||
{{else}}
|
|
||||||
<span class="text-muted">{{t 'generic.na'}}</span>
|
|
||||||
{{/if}}
|
|
||||||
</td>
|
|
||||||
|
|
||||||
{{#if showListeners}}
|
|
||||||
<td data-title="{{t 'formBalancerRules.sourcePort.label'}}">
|
|
||||||
{{input-integer class="form-control input-sm" min="1" max="65535" value=rule.sourcePort placeholder=(t 'formBalancerRules.sourcePort.placeholder')}}
|
|
||||||
</td>
|
|
||||||
{{/if}}
|
|
||||||
|
|
||||||
<td data-title="{{t 'formBalancerRules.path.label'}}">
|
|
||||||
{{#if rule.canPath}}
|
|
||||||
{{input type="text" class="form-control input-sm" value=rule.path placeholder=(t 'formBalancerRules.path.placeholder')}}
|
|
||||||
{{else}}
|
|
||||||
<span class="text-muted">{{t 'generic.na'}}</span>
|
|
||||||
{{/if}}
|
|
||||||
</td>
|
|
||||||
|
|
||||||
{{#if showListeners}}
|
|
||||||
<td class="divided" data-title="{{t (if rule.isSelector 'formBalancerRules.selector.label' 'formBalancerRules.serviceId.label')}}">
|
|
||||||
{{#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}}
|
|
||||||
</td>
|
|
||||||
{{/if}}
|
|
||||||
|
|
||||||
<td data-title="{{t 'formBalancerRules.targetPort.label'}}">
|
|
||||||
{{input-integer class="form-control input-sm" min="1" max="65535" value=rule.targetPort placeholder=(t 'formBalancerRules.targetPort.placeholder')}}
|
|
||||||
</td>
|
|
||||||
|
|
||||||
{{#if showBackend}}
|
|
||||||
<td class="divided" data-title="{{t 'formBalancerRules.backendName.label'}}">
|
|
||||||
{{input type="text" class="form-control input-sm" value=rule.backendName placeholder=(t 'formBalancerRules.backendName.placeholder')}}
|
|
||||||
</td>
|
|
||||||
{{/if}}
|
|
||||||
|
|
||||||
{{#if showListeners}}
|
|
||||||
<td class="text-right">
|
|
||||||
<button class="btn bg-primary btn-sm" {{action "removeRule" rule}}><i class="icon icon-minus"/><span class="sr-only">{{t 'generic.remove'}}</span></button>
|
|
||||||
</td>
|
|
||||||
{{/if}}
|
|
||||||
</tr>
|
|
||||||
{{/each}}
|
|
||||||
</tbody>
|
|
||||||
</table>
|
|
||||||
<p class="text-info">
|
|
||||||
{{t 'formBalancerRules.help.prefix'}}
|
|
||||||
{{#unless showBackend}}
|
|
||||||
<a href="#" {{action "showBackend"}}>
|
|
||||||
{{t 'formBalancerRules.help.showBackendLink'}}
|
|
||||||
</a>
|
|
||||||
{{/unless}}
|
|
||||||
{{#unless showIp}}
|
|
||||||
<a href="#" {{action "showIp"}}>
|
|
||||||
{{t 'formBalancerRules.help.showIpLink'}}
|
|
||||||
</a>
|
|
||||||
{{/unless}}
|
|
||||||
{{t 'formBalancerRules.help.suffix'}}
|
|
||||||
</p>
|
|
||||||
{{else}}
|
|
||||||
<span class="text-muted">{{t 'formBalancerRules.noRules'}}</span>
|
|
||||||
{{/if}}
|
{{/if}}
|
||||||
</div>
|
{{/if}}
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
{{#if rules.length}}
|
||||||
|
<table class="grid fixed no-lines no-top-padding mb-0">
|
||||||
|
<thead>
|
||||||
|
<tr>
|
||||||
|
{{#unless singleTarget}}
|
||||||
|
<th width="30"> </th>
|
||||||
|
{{/unless}}
|
||||||
|
|
||||||
|
<th class="divided">{{t 'formBalancerRules.hostname.label'}}</th>
|
||||||
|
|
||||||
|
<th>{{t 'formBalancerRules.path.label'}}</th>
|
||||||
|
|
||||||
|
{{#if singleTarget}}
|
||||||
|
<th width="30"> </th>
|
||||||
|
{{else}}
|
||||||
|
<th class="divided">{{t 'formBalancerRules.target'}}{{field-required}}</th>
|
||||||
|
{{/if}}
|
||||||
|
|
||||||
|
<th width="100" class="divided">{{t 'formBalancerRules.targetPort.label'}}{{field-required}}</th>
|
||||||
|
|
||||||
|
{{#if showBackend}}
|
||||||
|
<th class="divided">{{t 'formBalancerRules.backendName.label'}}</th>
|
||||||
|
{{/if}}
|
||||||
|
|
||||||
|
{{#if editing}}
|
||||||
|
<th width="40"> </th>
|
||||||
|
{{/if}}
|
||||||
|
</tr>
|
||||||
|
</thead>
|
||||||
|
|
||||||
|
<tbody>
|
||||||
|
{{#each rules as |rule idx|}}
|
||||||
|
<tr>
|
||||||
|
{{#unless singleTarget}}
|
||||||
|
<td data-title="{{t 'formBalancerRules.priority.label'}}">
|
||||||
|
<button class="btn bg-default btn-xs" {{action "moveUp" rule}} disabled={{eq idx 0}}>
|
||||||
|
<i class="icon icon-chevron-up"></i>
|
||||||
|
</button>
|
||||||
|
<button class="btn bg-default btn-xs" {{action "moveDown" rule}} disabled={{eq idx (sub rules.length 1)}}>
|
||||||
|
<i class="icon icon-chevron-down"></i>
|
||||||
|
</button>
|
||||||
|
</td>
|
||||||
|
{{/unless}}
|
||||||
|
|
||||||
|
<td class="divided" data-title="{{t 'formBalancerRules.hostname.label'}}">
|
||||||
|
{{#if rule.canHostname}}
|
||||||
|
{{input type="text" class="input-sm" value=rule.hostname placeholder=(t 'formBalancerRules.hostname.placeholder')}}
|
||||||
|
{{else}}
|
||||||
|
<span class="text-muted">{{t 'generic.na'}}</span>
|
||||||
|
{{/if}}
|
||||||
|
</td>
|
||||||
|
|
||||||
|
<td data-title="{{t 'formBalancerRules.path.label'}}">
|
||||||
|
{{#if rule.canPath}}
|
||||||
|
{{input type="text" class="input-sm" value=rule.path placeholder=(t 'formBalancerRules.path.placeholder')}}
|
||||||
|
{{else}}
|
||||||
|
<span class="text-muted">{{t 'generic.na'}}</span>
|
||||||
|
{{/if}}
|
||||||
|
</td>
|
||||||
|
|
||||||
|
{{#if singleTarget}}
|
||||||
|
<td> </td>
|
||||||
|
{{else}}
|
||||||
|
<td class="divided" data-title="{{t (concat-str 'formBalancerRules' rule.kind 'label' character='.')}}">
|
||||||
|
{{#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}}
|
||||||
|
</td>
|
||||||
|
{{/if}}
|
||||||
|
|
||||||
|
<td data-title="{{t 'formBalancerRules.targetPort.label'}}">
|
||||||
|
{{input-integer class="input-sm" min="1" max="65535" value=rule.targetPort placeholder=(t 'formBalancerRules.targetPort.placeholder')}}
|
||||||
|
</td>
|
||||||
|
|
||||||
|
{{#if showBackend}}
|
||||||
|
<td class="divided" data-title="{{t 'formBalancerRules.backendName.label'}}">
|
||||||
|
{{input type="text" class="input-sm" value=rule.backendName placeholder=(t 'formBalancerRules.backendName.placeholder')}}
|
||||||
|
</td>
|
||||||
|
{{/if}}
|
||||||
|
|
||||||
|
{{#if editing}}
|
||||||
|
<td class="text-right">
|
||||||
|
<button class="btn bg-primary btn-sm" {{action "removeRule" rule}}><i class="icon icon-minus"/><span class="sr-only">{{t 'generic.remove'}}</span></button>
|
||||||
|
</td>
|
||||||
|
{{/if}}
|
||||||
|
</tr>
|
||||||
|
{{/each}}
|
||||||
|
</tbody>
|
||||||
|
</table>
|
||||||
|
{{else}}
|
||||||
|
<span class="text-muted">{{t 'formBalancerRules.noRules'}}</span>
|
||||||
|
{{/if}}
|
||||||
|
|
|
||||||
|
|
@ -74,6 +74,10 @@ export default Ember.Component.extend({
|
||||||
count++;
|
count++;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if ( this.get('lbConfig.needsCertificate') && !count ) {
|
||||||
|
k = STATUS.INCOMPLETE;
|
||||||
|
}
|
||||||
|
|
||||||
if ( count ) {
|
if ( count ) {
|
||||||
k = STATUS.COUNTCONFIGURED;
|
k = STATUS.COUNTCONFIGURED;
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -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'),
|
||||||
|
});
|
||||||
|
|
@ -0,0 +1,10 @@
|
||||||
|
<select class="{{selectClass}}" onchange={{action (mut selected) value="target.value"}}>
|
||||||
|
<option selected={{eq selected null}}>{{t 'schema.inputContainer.prompt'}}</option>
|
||||||
|
{{#each-in grouped as |group list|}}
|
||||||
|
<optgroup label={{group}}>
|
||||||
|
{{#each list as |svc|}}
|
||||||
|
<option selected={{eq selected svc.id}} value={{svc.id}}>{{svc.name}}</option>
|
||||||
|
{{/each}}
|
||||||
|
</optgroup>
|
||||||
|
{{/each-in}}
|
||||||
|
</select>
|
||||||
|
|
@ -70,6 +70,5 @@ export default Ember.Component.extend({
|
||||||
}
|
}
|
||||||
|
|
||||||
this.set('value', str);
|
this.set('value', str);
|
||||||
console.log('val', str);
|
|
||||||
}.observes('selected'),
|
}.observes('selected'),
|
||||||
});
|
});
|
||||||
|
|
|
||||||
|
|
@ -51,8 +51,8 @@ export default Ember.Mixin.create({
|
||||||
// let version = instance.get('version')||"";
|
// let version = instance.get('version')||"";
|
||||||
|
|
||||||
let k8sName = (instance.get('labels')||{})[C.LABEL.K8S_POD_NAMESPACE] || '';
|
let k8sName = (instance.get('labels')||{})[C.LABEL.K8S_POD_NAMESPACE] || '';
|
||||||
let stackId = instance.get('primaryStack.id') || '';
|
let stackId = instance.get('stack.id') || '';
|
||||||
let stackName = instance.get('primaryStack.displayName') || '';
|
let stackName = instance.get('stack.displayName') || '';
|
||||||
|
|
||||||
let groupId, groupName;
|
let groupId, groupName;
|
||||||
if ( k8sName ) {
|
if ( k8sName ) {
|
||||||
|
|
|
||||||
|
|
@ -30,17 +30,13 @@ var Container = Instance.extend({
|
||||||
primaryHost : denormalizeId('hostId'),
|
primaryHost : denormalizeId('hostId'),
|
||||||
services : denormalizeIdArray('serviceIds'),
|
services : denormalizeIdArray('serviceIds'),
|
||||||
primaryService : Ember.computed.alias('services.firstObject'),
|
primaryService : Ember.computed.alias('services.firstObject'),
|
||||||
primaryStack : Ember.computed.alias('primaryService.stack'),
|
|
||||||
referencedStack : denormalizeId('stackId'),
|
|
||||||
referencedService : denormalizeId('serviceId'),
|
referencedService : denormalizeId('serviceId'),
|
||||||
|
|
||||||
service: Ember.computed('primaryService','referencedService', function() {
|
service: Ember.computed('primaryService','referencedService', function() {
|
||||||
return this.get('referencedService') || this.get('primaryService');
|
return this.get('referencedService') || this.get('primaryService');
|
||||||
}),
|
}),
|
||||||
|
|
||||||
stack: Ember.computed('primaryStack','referencedStack', function() {
|
stack: denormalizeId('stackId'),
|
||||||
return this.get('referencedStack') || this.get('primaryStack');
|
|
||||||
}),
|
|
||||||
|
|
||||||
actions: {
|
actions: {
|
||||||
restart: function() {
|
restart: function() {
|
||||||
|
|
|
||||||
|
|
@ -2,8 +2,11 @@ import Ember from 'ember';
|
||||||
import Resource from 'ember-api-store/models/resource';
|
import Resource from 'ember-api-store/models/resource';
|
||||||
import C from 'ui/utils/constants';
|
import C from 'ui/utils/constants';
|
||||||
import { formatSi } from 'ui/utils/util';
|
import { formatSi } from 'ui/utils/util';
|
||||||
|
import { denormalizeId } from 'ember-api-store/utils/denormalize';
|
||||||
|
|
||||||
export default Resource.extend({
|
export default Resource.extend({
|
||||||
|
stack: denormalizeId('stackId'),
|
||||||
|
|
||||||
isSystem: function() {
|
isSystem: function() {
|
||||||
if ( this.get('system') ) {
|
if ( this.get('system') ) {
|
||||||
return true;
|
return true;
|
||||||
|
|
|
||||||
|
|
@ -27,10 +27,6 @@ var LoadBalancerService = Service.extend({
|
||||||
let publish = this.get('launchConfig.ports')||[];
|
let publish = this.get('launchConfig.ports')||[];
|
||||||
publish.forEach((str) => {
|
publish.forEach((str) => {
|
||||||
let spec = parsePortSpec(str,'tcp');
|
let spec = parsePortSpec(str,'tcp');
|
||||||
if ( !spec.hostPort || spec.hostIp ) {
|
|
||||||
this.set('hasUnsupportedPorts', true);
|
|
||||||
}
|
|
||||||
|
|
||||||
if ( spec.hostPort ) {
|
if ( spec.hostPort ) {
|
||||||
rules.filterBy('sourcePort', spec.hostPort).forEach((rule) => {
|
rules.filterBy('sourcePort', spec.hostPort).forEach((rule) => {
|
||||||
rule.set('access', 'public');
|
rule.set('access', 'public');
|
||||||
|
|
|
||||||
|
|
@ -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);
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
@ -8,7 +8,12 @@ export default Ember.Service.extend({
|
||||||
settings: Ember.inject.service(),
|
settings: Ember.inject.service(),
|
||||||
|
|
||||||
absolute: function() {
|
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 the URL is relative, add on the current base URL from the browser
|
||||||
if ( url.indexOf('http') !== 0 )
|
if ( url.indexOf('http') !== 0 )
|
||||||
|
|
@ -20,7 +25,7 @@ export default Ember.Service.extend({
|
||||||
url = url.replace(/\/+$/,'') + '/';
|
url = url.replace(/\/+$/,'') + '/';
|
||||||
|
|
||||||
return url;
|
return url;
|
||||||
}.property('app.apiServer'),
|
}.property(`settings.${C.SETTING.API_HOST}`,'app.apiServer'),
|
||||||
|
|
||||||
host: function() {
|
host: function() {
|
||||||
var a = document.createElement('a');
|
var a = document.createElement('a');
|
||||||
|
|
|
||||||
|
|
@ -20,14 +20,14 @@
|
||||||
<h1>{{t 'signupPage.header'}}</h1>
|
<h1>{{t 'signupPage.header'}}</h1>
|
||||||
<form class="form text-left" {{action "register" on='submit'}}>
|
<form class="form text-left" {{action "register" on='submit'}}>
|
||||||
<div class="row">
|
<div class="row">
|
||||||
<label for="login-user-name">{{t 'signupPage.form.labels.loginUsername'}}</label>
|
<label for="login-user-name">{{t 'signupPage.form.labels.loginUsername'}}{{field-required}}</label>
|
||||||
<div name="login-user-name">
|
<div name="login-user-name">
|
||||||
{{input type="text" value=model.name}}
|
{{input type="text" value=model.name}}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="row inline-form pt-15 pb-30">
|
<div class="row inline-form pt-15 pb-30">
|
||||||
<label for="email">{{t 'signupPage.form.labels.email'}}</label>
|
<label for="email">{{t 'signupPage.form.labels.email'}}{{field-required}}</label>
|
||||||
<div name="email">
|
<div name="email">
|
||||||
{{input type="email" value=model.email}}
|
{{input type="email" value=model.email}}
|
||||||
</div>
|
</div>
|
||||||
|
|
@ -38,4 +38,4 @@
|
||||||
</form>
|
</form>
|
||||||
</div>
|
</div>
|
||||||
{{/if}}
|
{{/if}}
|
||||||
{{/authorize-user}}
|
{{/authorize-user}}
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,8 @@
|
||||||
import Ember from 'ember';
|
import Ember from 'ember';
|
||||||
|
|
||||||
export default Ember.Controller.extend({
|
export default Ember.Controller.extend({
|
||||||
|
prefs: Ember.inject.service(),
|
||||||
|
|
||||||
showAddtlInfo: false,
|
showAddtlInfo: false,
|
||||||
selectedService: null,
|
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) {
|
getType(ownType, real=true) {
|
||||||
return this.get('model.services').filter((service) => {
|
return this.get('model.services').filter((service) => {
|
||||||
if (real ? (service.get('isReal') && service.get('kind') === ownType) : (service.get('kind') === ownType)) {
|
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');
|
return this.getType('scalingGroup');
|
||||||
}),
|
}),
|
||||||
|
|
||||||
loadBalancers: Ember.computed('model.services.@each.healthState', function() {
|
loadBalancers: Ember.computed('model.services.[]', function() {
|
||||||
return this.getType('loadBalancerService');
|
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));
|
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() {
|
instanceCount: function() {
|
||||||
var count = 0;
|
var count = 0;
|
||||||
(this.get('model.stack.services')||[]).forEach((service) => {
|
(this.get('model.stack.services')||[]).forEach((service) => {
|
||||||
|
|
|
||||||
|
|
@ -12,15 +12,15 @@
|
||||||
{{#accordion-list-item
|
{{#accordion-list-item
|
||||||
title=(t 'stackPage.containers.header')
|
title=(t 'stackPage.containers.header')
|
||||||
detail=(t 'stackPage.containers.detail')
|
detail=(t 'stackPage.containers.detail')
|
||||||
status=(t 'stackPage.containers.status' count=model.instances.length)
|
status=(t 'stackPage.containers.status' count=instances.length)
|
||||||
statusClass=(if model.instances.length 'bg-success' 'text-muted')
|
statusClass=(if instances.length 'bg-success' 'text-muted')
|
||||||
expandAll=al.expandAll
|
expandAll=al.expandAll
|
||||||
expand=(action expandFn)
|
expand=(action expandFn)
|
||||||
componentName='container-table'
|
componentName='container-table'
|
||||||
as | parent |
|
as | parent |
|
||||||
}}
|
}}
|
||||||
{{component parent.intent
|
{{component parent.intent
|
||||||
body=model.instances
|
body=instances
|
||||||
search=true
|
search=true
|
||||||
sortBy=sortBy
|
sortBy=sortBy
|
||||||
stickyHeader=false
|
stickyHeader=false
|
||||||
|
|
|
||||||
|
|
@ -3,16 +3,10 @@ import Ember from 'ember';
|
||||||
export default Ember.Route.extend({
|
export default Ember.Route.extend({
|
||||||
model: function(params) {
|
model: function(params) {
|
||||||
var store = this.get('store');
|
var store = this.get('store');
|
||||||
var all = this.modelFor('stacks');
|
|
||||||
return store.find('stack', params.stack_id).then((stack) => {
|
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({
|
return Ember.Object.create({
|
||||||
stack: stack,
|
stack: stack,
|
||||||
all: all,
|
instances: store.all('instance'),
|
||||||
instances: neu,
|
|
||||||
services: stack.get('services')
|
services: stack.get('services')
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
|
||||||
|
|
@ -112,7 +112,7 @@ textarea {
|
||||||
|
|
||||||
.form-control-static {
|
.form-control-static {
|
||||||
line-height: 24px;
|
line-height: 24px;
|
||||||
padding: 5px 0;
|
padding: 3px 0;
|
||||||
border: 2px solid transparent;
|
border: 2px solid transparent;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -6,7 +6,7 @@
|
||||||
<h2>{{t 'verifyPage.subtext'}}</h2>
|
<h2>{{t 'verifyPage.subtext'}}</h2>
|
||||||
<form class="form text-left" {{action 'createAcct' on="submit"}}>
|
<form class="form text-left" {{action 'createAcct' on="submit"}}>
|
||||||
<div class="inline-form pt-15 pb-30">
|
<div class="inline-form pt-15 pb-30">
|
||||||
<label for="login-user-name">{{t 'signupPage.form.labels.loginUsername'}}</label>
|
<label for="login-user-name">{{t 'signupPage.form.labels.loginUsername'}}{{field-required}}</label>
|
||||||
<div>
|
<div>
|
||||||
{{input type="text" value=model.name id="login-user-name"}}
|
{{input type="text" value=model.name id="login-user-name"}}
|
||||||
</div>
|
</div>
|
||||||
|
|
@ -40,4 +40,4 @@
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
{{/unless}}
|
{{/unless}}
|
||||||
{{/authorize-user}}
|
{{/authorize-user}}
|
||||||
|
|
|
||||||
|
|
@ -1170,7 +1170,7 @@ stackPage:
|
||||||
backLink: Back to all stacks
|
backLink: Back to all stacks
|
||||||
containers:
|
containers:
|
||||||
header: 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: |
|
status: |
|
||||||
{count, plural,
|
{count, plural,
|
||||||
=0 {No containers}
|
=0 {No containers}
|
||||||
|
|
@ -1179,7 +1179,7 @@ stackPage:
|
||||||
}
|
}
|
||||||
scalingGroups:
|
scalingGroups:
|
||||||
header: Scaling Groups
|
header: Scaling Groups
|
||||||
detail: 'A list of scaling groups this stack is attached to.'
|
detail: ''
|
||||||
status: |
|
status: |
|
||||||
{count, plural,
|
{count, plural,
|
||||||
=0 {No groups}
|
=0 {No groups}
|
||||||
|
|
@ -1188,7 +1188,7 @@ stackPage:
|
||||||
}
|
}
|
||||||
loadBalancers:
|
loadBalancers:
|
||||||
header: Load Balancers
|
header: Load Balancers
|
||||||
detail: 'A list of load balancers in this stack'
|
detail: ''
|
||||||
status: |
|
status: |
|
||||||
{count, plural,
|
{count, plural,
|
||||||
=0 {No balancers}
|
=0 {No balancers}
|
||||||
|
|
@ -1196,13 +1196,13 @@ stackPage:
|
||||||
other {# balancers}
|
other {# balancers}
|
||||||
}
|
}
|
||||||
dnsServices:
|
dnsServices:
|
||||||
header: DNS Services
|
header: DNS Entries
|
||||||
detail: 'A list of DNS Services in this stack'
|
detail: ''
|
||||||
status: |
|
status: |
|
||||||
{count, plural,
|
{count, plural,
|
||||||
=0 {No rules}
|
=0 {No entries}
|
||||||
=1 {# rule}
|
=1 {# entry}
|
||||||
other {# rules}
|
other {# entries}
|
||||||
}
|
}
|
||||||
|
|
||||||
newStack:
|
newStack:
|
||||||
|
|
@ -1915,19 +1915,20 @@ formBalancerConfig:
|
||||||
See <a href="https://cbonte.github.io/haproxy-dconv/1.6/configuration.html" target="_blank" rel="nofollow noopener">haproxy documentation</a> fore more info about specific options that can go into the the config file. When overriding the <code>backend</code> or similar lines which include the IP address of the target container, use <code>$IP</code> where the address goes and {appName} will generate the appropriate line(s).
|
See <a href="https://cbonte.github.io/haproxy-dconv/1.6/configuration.html" target="_blank" rel="nofollow noopener">haproxy documentation</a> fore more info about specific options that can go into the the config file. When overriding the <code>backend</code> or similar lines which include the IP address of the target container, use <code>$IP</code> where the address goes and {appName} will generate the appropriate line(s).
|
||||||
config:
|
config:
|
||||||
prompt: Custom haproxy.cfg content
|
prompt: Custom haproxy.cfg content
|
||||||
formBalancerRules:
|
|
||||||
label: Port Rules
|
formBalancerListeners:
|
||||||
detail: 'These properties show the port mapping details of your container'
|
label: Listeners & Target Rules
|
||||||
|
detail: Control the mapping of requests coming into the balancer to the desired target.
|
||||||
status: |
|
status: |
|
||||||
{count, plural,
|
{count, plural,
|
||||||
=0 {No rules}
|
=0 {No rules}
|
||||||
=1 {# rule}
|
=1 {# rule}
|
||||||
other {# rules}
|
other {# rules}
|
||||||
}
|
}
|
||||||
noRules: No Rules
|
noRules: No Ports
|
||||||
addServiceLabel: Add Scaling Group Rule
|
addPortLabel: Add a Listening Port
|
||||||
addSelectorLabel: Add Selector Rule
|
removePortLabel: Remove this Listening Port
|
||||||
target: Target
|
showBackendLabel: "Customize backend names"
|
||||||
access:
|
access:
|
||||||
label: Access
|
label: Access
|
||||||
public: Public
|
public: Public
|
||||||
|
|
@ -1936,10 +1937,19 @@ formBalancerRules:
|
||||||
label: Protocol
|
label: Protocol
|
||||||
sourceIp:
|
sourceIp:
|
||||||
label: Host IP
|
label: Host IP
|
||||||
placeholder: e.g. 1.2.3.4
|
placeholder: "e.g. 1.2.3.4; Default: All"
|
||||||
sourcePort:
|
sourcePort:
|
||||||
label: Port
|
label: Listening Port
|
||||||
placeholder: e.g. 80
|
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:
|
path:
|
||||||
label: Path
|
label: Path
|
||||||
placeholder: e.g. /foo
|
placeholder: e.g. /foo
|
||||||
|
|
@ -1956,16 +1966,14 @@ formBalancerRules:
|
||||||
label: Priority
|
label: Priority
|
||||||
moveUp: Move Up
|
moveUp: Move Up
|
||||||
moveDown: Move Down
|
moveDown: Move Down
|
||||||
serviceId:
|
target: Target
|
||||||
|
container:
|
||||||
|
label: Container
|
||||||
|
service:
|
||||||
label: Service
|
label: Service
|
||||||
selector:
|
selector:
|
||||||
label: Selector
|
label: Selector
|
||||||
placeholder: e.g. foo=bar
|
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:
|
formCloudHost:
|
||||||
title: Host
|
title: Host
|
||||||
|
|
@ -2101,7 +2109,7 @@ formHealthCheck:
|
||||||
label: Host Header
|
label: Host Header
|
||||||
placeholder: e.g. www.example.com
|
placeholder: e.g. www.example.com
|
||||||
port:
|
port:
|
||||||
label: Port
|
label: Listening Port
|
||||||
placeholder: e.g. 80
|
placeholder: e.g. 80
|
||||||
initializingTimeout:
|
initializingTimeout:
|
||||||
label: Initializing Timeout
|
label: Initializing Timeout
|
||||||
|
|
@ -2431,7 +2439,7 @@ formStickiness:
|
||||||
indirect: Indirect
|
indirect: Indirect
|
||||||
sendHeader: Send no-cache header
|
sendHeader: Send no-cache header
|
||||||
onPost: Only set cookie on POST
|
onPost: Only set cookie on POST
|
||||||
noPorts: There are no HTTP Port Rules configured.
|
noPorts: There are no HTTP Listeners configured.
|
||||||
|
|
||||||
placeholder:
|
placeholder:
|
||||||
sticky: e.g. sticky
|
sticky: e.g. sticky
|
||||||
|
|
@ -3360,12 +3368,13 @@ newBalancer:
|
||||||
edit: Edit Load Balancer
|
edit: Edit Load Balancer
|
||||||
upgrade: Upgrade Load Balancer
|
upgrade: Upgrade Load Balancer
|
||||||
error:
|
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"
|
noSourcePort: "Source Port is required on each rule"
|
||||||
invalidSourcePort: "Invalid source port: '{num}'"
|
invalidSourcePort: "Invalid source port: '{num}'"
|
||||||
invalidTargetPort: "Invalid target port: '{num}'"
|
invalidTargetPort: "Invalid target port: '{num}'"
|
||||||
mixedPort: "Port {num} has multiple rules with conflicting access/protcols"
|
mixedPort: "Port {num} has multiple rules with conflicting access/protcols"
|
||||||
noTarget: "Target is required on each rule"
|
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"
|
needsCertificate: "A certificate is required because there are SSL/TLS port rules"
|
||||||
|
|
||||||
newCatalog:
|
newCatalog:
|
||||||
|
|
@ -3614,10 +3623,12 @@ schema:
|
||||||
n: "False"
|
n: "False"
|
||||||
inputCertificate:
|
inputCertificate:
|
||||||
prompt: Choose a Certificate...
|
prompt: Choose a Certificate...
|
||||||
|
inputContainer:
|
||||||
|
prompt: Choose a Container...
|
||||||
inputEnum:
|
inputEnum:
|
||||||
option: Choose an option...
|
option: Choose an option...
|
||||||
inputHost:
|
inputHost:
|
||||||
label: Select Host
|
label: Choose a Host...
|
||||||
inputService:
|
inputService:
|
||||||
prompt: Choose a Service...
|
prompt: Choose a Service...
|
||||||
inputSecret:
|
inputSecret:
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue