Merge pull request #583 from vincent99/master

Bugs
This commit is contained in:
Vincent Fiduccia 2016-04-08 13:49:47 -07:00
commit dd32c5a049
14 changed files with 203 additions and 52 deletions

View File

@ -1,7 +1,7 @@
import Ember from 'ember'; import Ember from 'ember';
import C from 'ui/utils/constants'; import C from 'ui/utils/constants';
import ManageLabels from 'ui/mixins/manage-labels'; import ManageLabels from 'ui/mixins/manage-labels';
import { parsePort } from 'ui/utils/parse-port'; import { parsePortSpec, parseIpPort } from 'ui/utils/parse-port';
export default Ember.Component.extend(ManageLabels, { export default Ember.Component.extend(ManageLabels, {
initialPorts: null, initialPorts: null,
@ -46,7 +46,7 @@ export default Ember.Component.extend(ManageLabels, {
var out = []; var out = [];
function add(isPublic, str) { function add(isPublic, str) {
var obj = parsePort(str, 'http'); var obj = parsePortSpec(str, 'http');
obj.setProperties({ obj.setProperties({
isPublic: isPublic, isPublic: isPublic,
ssl: sslPorts.indexOf(obj.get('hostPort')) >= 0, ssl: sslPorts.indexOf(obj.get('hostPort')) >= 0,
@ -73,9 +73,12 @@ export default Ember.Component.extend(ManageLabels, {
}.observes('listenersArray.[]'), }.observes('listenersArray.[]'),
sslChanged: function() { sslChanged: function() {
var sslPorts = this.get('listenersArray'). var sslPorts = this.get('listenersArray')
filterBy('host'). .filterBy('ssl',true)
filterBy('ssl',true).map((listener) => { return listener.get('host');}); .map((listener) => { return parseIpPort(listener.get('host'),'http'); })
.filterBy('port')
.map((obj) => { return obj.port; })
.sort().uniq();
if ( sslPorts.get('length') ) if ( sslPorts.get('length') )
{ {

View File

@ -24,6 +24,8 @@ export default Ember.Component.extend({
// Inputs // Inputs
healthCheck: null, healthCheck: null,
errors: null, errors: null,
isService: null,
showStrategy: true,
tagName: '', tagName: '',

View File

@ -128,7 +128,7 @@
</div> </div>
</div> </div>
{{#if isService}} {{#if (and isService showStrategy)}}
<div class="row form-group"> <div class="row form-group">
<div class="col-sm-12 col-md-2 form-label"> <div class="col-sm-12 col-md-2 form-label">
<label>When Unhealthy</label> <label>When Unhealthy</label>

View File

@ -63,9 +63,9 @@ export default Ember.Component.extend({
valuePlaceholder: 'Value', valuePlaceholder: 'Value',
allowEmptyValue: false, allowEmptyValue: false,
addInitialEmptyRow: false, addInitialEmptyRow: false,
allowMultilineValue: true,
ary: null, ary: null,
asMap: null,
actions: { actions: {
add() { add() {
@ -121,9 +121,17 @@ export default Ember.Component.extend({
} }
}, },
asMapObserver: function() { aryObserver: function() {
Ember.run.debounce(this,'fireChanged',100);
}.observes('ary.@each.{key,value}'),
fireChanged() {
if ( this._state === 'destroying' )
{
return;
}
var out = {}; var out = {};
var outStr = '';
this.get('ary').forEach((row) => { this.get('ary').forEach((row) => {
var k = row.get('key').trim(); var k = row.get('key').trim();
@ -132,12 +140,9 @@ export default Ember.Component.extend({
if ( k && (v || this.get('allowEmptyValue')) ) if ( k && (v || this.get('allowEmptyValue')) )
{ {
out[row.get('key').trim()] = row.get('value').trim(); out[row.get('key').trim()] = row.get('value').trim();
outStr += (outStr ? ',' : '') + k + '=' + v;
} }
}); });
this.set('asMap', out);
this.sendAction('changed', out); this.sendAction('changed', out);
this.sendAction('changedStr', outStr); },
}.observes('ary.@each.{key,value}'),
}); });

View File

@ -10,19 +10,23 @@
</tr> </tr>
{{#each ary as |row|}} {{#each ary as |row|}}
<tr> <tr>
<td data-title="Key"> <td class="valign-top" data-title="Key">
{{input-paste pasted="pastedLabels" class="form-control input-sm key" type="text" value=row.key placeholder=keyPlaceholder}} {{input-paste pasted="pastedLabels" class="form-control input-sm key" type="text" value=row.key placeholder=keyPlaceholder}}
</td> </td>
<td class="text-center"> <td class="valign-top text-center">
<p class="form-control-static input-sm">=</p> <p class="form-control-static input-sm">=</p>
</td> </td>
<td data-title="Value"> <td class="valign-top" data-title="Value">
{{#if allowMultilineValue}}
{{textarea-autogrow class="form-control input-sm value" value=row.value placeholder=valuePlaceholder}}
{{else}}
{{input class="form-control input-sm value" type="text" value=row.value placeholder=valuePlaceholder}} {{input class="form-control input-sm value" type="text" value=row.value placeholder=valuePlaceholder}}
{{/if}}
</td> </td>
<td class="text-right"> <td class="valign-top text-right">
<button class="btn btn-primary btn-sm" {{action "remove" row}}><i class="icon icon-minus"/></button> <button class="btn btn-primary btn-sm" {{action "remove" row}}><i class="icon icon-minus"/></button>
</td> </td>
</tr> </tr>

View File

@ -1,5 +1,5 @@
import Ember from 'ember'; import Ember from 'ember';
import { parsePort } from 'ui/utils/parse-port'; import { parsePortSpec } from 'ui/utils/parse-port';
const protocolOptions = [ const protocolOptions = [
{label: 'TCP', value: 'tcp'}, {label: 'TCP', value: 'tcp'},
@ -34,18 +34,15 @@ export default Ember.Component.extend({
if ( typeof value === 'object' ) if ( typeof value === 'object' )
{ {
var existing = !forceNew && !!value.id; var existing = !forceNew && !!value.id;
var pub = ''; var pub = value.publicPort+'';
if ( value.publicPort ) if ( !existing && value.bindAddress )
{ {
if ( value.bindAddress ) { pub = value.bindAddress + ':' + pub;
pub += value.bindAddress + ':';
}
pub += value.publicPort;
} }
out.push({ out.push({
existing: existing, existing: existing,
obj: value, obj: value,
bindAddress: value.bindAddress||null,
public: pub, public: pub,
private: value.privatePort, private: value.privatePort,
protocol: value.protocol, protocol: value.protocol,
@ -54,7 +51,7 @@ export default Ember.Component.extend({
else if ( typeof value === 'string' ) else if ( typeof value === 'string' )
{ {
// Strings, from clone // Strings, from clone
var parsed = parsePort(value); var parsed = parsePortSpec(value);
out.push({ out.push({
existing: false, existing: false,
public: parsed.host, public: parsed.host,

View File

@ -10,9 +10,9 @@
{{#if portsArray.length}} {{#if portsArray.length}}
<table class="table fixed no-lines no-top-padding tight small"> <table class="table fixed no-lines no-top-padding tight small">
<tr class="text-muted"> <tr class="text-muted">
<th>Public (on Host) IP/Port</th> <th>Public Host [IP:]Port</th>
<th width="30">&nbsp;</th> <th width="30">&nbsp;</th>
<th>Private (in Container) Port</th> <th>Private Container Port</th>
<th width="30">&nbsp;</th> <th width="30">&nbsp;</th>
<th width="60">Protocol</th> <th width="60">Protocol</th>
<th width="30">&nbsp;</th> <th width="30">&nbsp;</th>
@ -20,7 +20,16 @@
{{#each portsArray as |port|}} {{#each portsArray as |port|}}
<tr> <tr>
<td> <td>
{{#if (and port.existing port.bindAddress)}}
<div class="input-group">
<span class="input-group-addon input-sm">
{{port.bindAddress}}:
</span>
{{input class="form-control input-sm" type="text" value=port.public placeholder="e.g. 80"}}
</div>
{{else}}
{{input class="form-control input-sm" type="text" value=port.public placeholder="e.g. 80 or 1.2.3.4:80"}} {{input class="form-control input-sm" type="text" value=port.public placeholder="e.g. 80 or 1.2.3.4:80"}}
{{/if}}
</td> </td>
<td class="text-center"> <td class="text-center">
<p class="form-control-static"><i class="icon icon-chevron-right"></i></p> <p class="form-control-static"><i class="icon icon-chevron-right"></i></p>

View File

@ -19,8 +19,10 @@
<section class="horizontal-form container-fluid well"> <section class="horizontal-form container-fluid well">
{{form-healthcheck {{form-healthcheck
isService=true isService=true
showStrategy=false
healthCheck=service.healthCheck healthCheck=service.healthCheck
}} }}
<div class="alert alert-info">Note: This will only be used if the external service is a target of a load balancer.</div>
</section> </section>
{{top-errors errors=errors}} {{top-errors errors=errors}}

View File

@ -2,14 +2,15 @@ import Ember from 'ember';
import { isGecko } from 'ui/utils/platform'; import { isGecko } from 'ui/utils/platform';
export default Ember.TextArea.extend({ export default Ember.TextArea.extend({
tagName: 'textarea',
text: null, text: null,
classNames: [], minHeight: 0,
paddingAndBorder: null,
minHeight: 43,
maxHeight: 200, maxHeight: 200,
tagName: 'textarea',
classNames: ['no-resize'],
didInsertElement() { didInsertElement() {
this.set('minHeight', ( this.get('isSmall') ? 31 : 43));
this.autoSize(); this.autoSize();
this.$().on('paste', () => { this.$().on('paste', () => {
@ -18,9 +19,13 @@ export default Ember.TextArea.extend({
}, },
keyUp() { keyUp() {
this.autoSize(); Ember.run.debounce(this,'autoSize',100);
}, },
isSmall: function() {
return this.$().hasClass('input-sm');
}.property(),
autoSize() { autoSize() {
let el = this.element; let el = this.element;
let $el = $(el); let $el = $(el);
@ -29,7 +34,7 @@ export default Ember.TextArea.extend({
$el.css('height', '1px'); $el.css('height', '1px');
let border = parseInt($el.css('borderTopWidth'),10)||0 + parseInt($el.css('borderBottomWidth'),10)||0; let border = parseInt($el.css('borderTopWidth'),10)||0 + parseInt($el.css('borderBottomWidth'),10)||0;
let magic = ( isGecko ? 1 : 2); // Sigh, but it's wrong without magic fudge let magic = (this.get('isSmall') ? -2 : 0) + ( isGecko ? 1 : 2); // Sigh, but it's wrong without magic fudge
let neu = Math.max(this.get('minHeight'), Math.min(el.scrollHeight + border + magic, this.get('maxHeight'))); let neu = Math.max(this.get('minHeight'), Math.min(el.scrollHeight + border + magic, this.get('maxHeight')));
$el.css('overflowY', (el.scrollHeight > neu ? 'auto' : 'hidden')); $el.css('overflowY', (el.scrollHeight > neu ? 'auto' : 'hidden'));

View File

@ -3,8 +3,8 @@
<tr> <tr>
<th width="90">State</th> <th width="90">State</th>
<th width="150">IP Address</th> <th width="150">IP Address</th>
<th>Public (on Host)</th> <th>Public on Host</th>
<th>Private (in Container)</th> <th>Private in Container</th>
<th width="90">Protocol</th> <th width="90">Protocol</th>
</tr> </tr>
</thead> </thead>

View File

@ -2,17 +2,17 @@ import Service from 'ui/models/service';
import Ember from 'ember'; import Ember from 'ember';
import C from 'ui/utils/constants'; import C from 'ui/utils/constants';
import Util from 'ui/utils/util'; import Util from 'ui/utils/util';
import { parsePort } from 'ui/utils/parse-port'; import { parsePortSpec } from 'ui/utils/parse-port';
const esc = Ember.Handlebars.Utils.escapeExpression; const esc = Ember.Handlebars.Utils.escapeExpression;
function portToStr(spec) { function portToStr(spec) {
var parts = parsePort(spec); var parts = parsePortSpec(spec);
return parts.host + (parts.protocol === 'http' ? '' : '/' + parts.protocol); return parts.host + (parts.protocol === 'http' ? '' : '/' + parts.protocol);
} }
function specToPort(spec) { function specToPort(spec) {
var parts = parsePort(spec); var parts = parsePortSpec(spec);
return parts.hostPort; return parts.hostPort;
} }

View File

@ -29,10 +29,22 @@ B {
background-color : $midGray; background-color : $midGray;
} }
.inline-block { .inline-block {
display : inline-block; display : inline-block;
} }
.valign-top {
vertical-align: top;
}
.valign-middle {
vertical-align: middle;
}
.valign-bottom {
vertical-align: bottom;
}
/********** /**********
* Articles (above the page info/warnings) * Articles (above the page info/warnings)
**********/ **********/

View File

@ -4,7 +4,7 @@ import Ember from 'ember';
// hostIp::containerPort // hostIp::containerPort
// hostPort:containerPort // hostPort:containerPort
// containerPort // containerPort
export function parsePort(str, defaultProtocol='http') { export function parsePortSpec(str, defaultProtocol='http') {
str = str.trim(); str = str.trim();
var match, parts, hostIp = '', hostPort, containerPort, protocol; var match, parts, hostIp = '', hostPort, containerPort, protocol;
@ -59,7 +59,7 @@ export function parsePort(str, defaultProtocol='http') {
}); });
} }
export function stringifyPort(port, defaultProtocol='http') { export function stringifyPortSpec(port, defaultProtocol='http') {
var hostStr = Ember.get(port,'host')||''; var hostStr = Ember.get(port,'host')||'';
var match, hostIp, hostPort; var match, hostIp, hostPort;
if ( match = hostStr.match(/^((.*):)?([^:]+)$/) ) if ( match = hostStr.match(/^((.*):)?([^:]+)$/) )
@ -92,7 +92,81 @@ export function stringifyPort(port, defaultProtocol='http') {
return out; return out;
} }
export default { // port
parsePort: parsePort, // 1.2.3.4
stringifyPort: stringifyPort, // 1.2.3.4:port
// long:ip:v6::str
// [long:ip:v6::str]
// [long:ip:v6::str]:port
export function parseIpPort(str) {
str = str.trim();
let colons = str.replace(/[^:]/g,'').length;
let index;
// IPv6, IPv6+port
index = str.indexOf(']');
if ( colons > 1 )
{
let index = str.indexOf(']');
if ( index > 0 )
{
let ip = str.substr(0,index+1);
let port = null;
if ( str.substr(index+1,1) === ':' ) {
port = portToInt(str.substr(index+2));
}
return ret(ip,port);
}
else
{
return ret('['+str+']',null);
}
}
// IPv4+port
index = str.indexOf(':');
if ( index >= 0 )
{
return ret(str.substr(0,index), str.substr(index+1));
}
// IPv4
if ( str.match(/[^\d]/) )
{
return ret(str,null);
}
// Port
let port = portToInt(str);
if ( port )
{
return ret(null,port);
}
return null;
function ret(ip,port) {
return {
ip: ip || null,
port: portToInt(port)
};
}
}
export function portToInt(str) {
str = (str+'').trim();
if ( str.match(/^\d+$/) )
{
return parseInt(str,10) || null;
}
return null;
}
export default {
parsePortSpec: parsePortSpec,
stringifyPortSpec: stringifyPortSpec,
parseIpPort: parseIpPort,
portToInt: portToInt
}; };

View File

@ -1,5 +1,5 @@
import Ember from 'ember'; import Ember from 'ember';
import { parsePort, stringifyPort } from 'ui/utils/parse-port'; import { parsePortSpec, stringifyPortSpec, parseIpPort } from 'ui/utils/parse-port';
import { module, test } from 'qunit'; import { module, test } from 'qunit';
module('Unit | Utils | parse-port'); module('Unit | Utils | parse-port');
@ -21,28 +21,66 @@ var data = [
data.forEach(function(obj) { data.forEach(function(obj) {
var input = obj.str; var input = obj.str;
var actual = parsePort(input); var actual = parsePortSpec(input);
if ( obj.parsed ) if ( obj.parsed )
{ {
test('it can parse: ' + obj.str, function(assert) { test('it can parse spec: ' + obj.str, function(assert) {
var expected = obj.parsed; var expected = obj.parsed;
Object.keys(expected).forEach((key) => { Object.keys(expected).forEach((key) => {
assert.strictEqual(Ember.get(actual,key), Ember.get(expected, key), key + ' parses correctly'); assert.strictEqual(Ember.get(actual,key), Ember.get(expected, key), key + ' parses correctly');
}); });
}); });
test('it can stringify: ' + obj.str, function(assert) { test('it can stringify spec: ' + obj.str, function(assert) {
var input = obj.parsed; var input = obj.parsed;
var expected = obj.expected || obj.str; var expected = obj.expected || obj.str;
var actual = stringifyPort(input); var actual = stringifyPortSpec(input);
assert.strictEqual(actual, expected, 'Objects are stringified correctly'); assert.strictEqual(actual, expected, 'Objects are stringified correctly');
}); });
} }
else else
{ {
test("it can't parse: " + obj.str, function(assert) { test("it can't parse spec: " + obj.str, function(assert) {
assert.strictEqual(actual, null, 'Invalid data is not parseable'); assert.strictEqual(actual, null, 'Invalid data is not parseable');
}); });
} }
}); });
data = [
{str: '', parsed: null},
{str: '80', parsed: {ip: null, port: 80 }},
{str: 'asdf', parsed: {ip: 'asdf', port: null}},
{str: '1.2.3.4', parsed: {ip: '1.2.3.4', port: null}},
{str: '1.2.3.4:80', parsed: {ip: '1.2.3.4', port: 80 }},
{str: '1.2.3.4:12ab', parsed: {ip: '1.2.3.4', port: null}},
{str: 'asdf:12ab', parsed: {ip: 'asdf', port: null}},
{str: '80asdf', parsed: {ip: '80asdf', port: null}},
{str: '12:34:56::78', parsed: {ip: '[12:34:56::78]', port: null}},
{str: '[12:34:56::78]', parsed: {ip: '[12:34:56::78]', port: null}},
{str: '[12:34:56::78]:80', parsed: {ip: '[12:34:56::78]', port: 80 }},
{str: '[12:34:56::78]:asdf', parsed: {ip: '[12:34:56::78]', port: null}},
{str: '[12:34:56::78]:90:ab', parsed: {ip: '[12:34:56::78]', port: null}},
{str: '2001:0db8:85a3:0000:0000:8a2e:0370:7334', parsed: {ip: '[2001:0db8:85a3:0000:0000:8a2e:0370:7334]', port: null}},
{str: '[2001:0db8:85a3:0000:0000:8a2e:0370:7334]:80', parsed: {ip: '[2001:0db8:85a3:0000:0000:8a2e:0370:7334]', port: 80}},
];
data.forEach(function(obj) {
var input = obj.str;
var actual = parseIpPort(input);
test('it can parse: ' + obj.str, function(assert) {
var expected = obj.parsed;
if ( expected === null )
{
assert.strictEqual(actual, null, input + ' cannot be parsed');
}
else
{
Object.keys(expected).forEach((key) => {
assert.strictEqual(Ember.get(actual,key), Ember.get(expected, key), key + ' parses correctly');
});
}
});
});