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

View File

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

View File

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

View File

@ -63,9 +63,9 @@ export default Ember.Component.extend({
valuePlaceholder: 'Value',
allowEmptyValue: false,
addInitialEmptyRow: false,
allowMultilineValue: true,
ary: null,
asMap: null,
actions: {
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 outStr = '';
this.get('ary').forEach((row) => {
var k = row.get('key').trim();
@ -132,12 +140,9 @@ export default Ember.Component.extend({
if ( k && (v || this.get('allowEmptyValue')) )
{
out[row.get('key').trim()] = row.get('value').trim();
outStr += (outStr ? ',' : '') + k + '=' + v;
}
});
this.set('asMap', out);
this.sendAction('changed', out);
this.sendAction('changedStr', outStr);
}.observes('ary.@each.{key,value}'),
},
});

View File

@ -10,19 +10,23 @@
</tr>
{{#each ary as |row|}}
<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}}
</td>
<td class="text-center">
<td class="valign-top text-center">
<p class="form-control-static input-sm">=</p>
</td>
<td data-title="Value">
{{input class="form-control input-sm value" type="text" value=row.value placeholder=valuePlaceholder}}
<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}}
{{/if}}
</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>
</td>
</tr>

View File

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

View File

@ -10,9 +10,9 @@
{{#if portsArray.length}}
<table class="table fixed no-lines no-top-padding tight small">
<tr class="text-muted">
<th>Public (on Host) IP/Port</th>
<th>Public Host [IP:]Port</th>
<th width="30">&nbsp;</th>
<th>Private (in Container) Port</th>
<th>Private Container Port</th>
<th width="30">&nbsp;</th>
<th width="60">Protocol</th>
<th width="30">&nbsp;</th>
@ -20,7 +20,16 @@
{{#each portsArray as |port|}}
<tr>
<td>
{{input class="form-control input-sm" type="text" value=port.public placeholder="e.g. 80 or 1.2.3.4:80"}}
{{#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"}}
{{/if}}
</td>
<td class="text-center">
<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">
{{form-healthcheck
isService=true
showStrategy=false
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>
{{top-errors errors=errors}}

View File

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

View File

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

View File

@ -2,17 +2,17 @@ import Service from 'ui/models/service';
import Ember from 'ember';
import C from 'ui/utils/constants';
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;
function portToStr(spec) {
var parts = parsePort(spec);
var parts = parsePortSpec(spec);
return parts.host + (parts.protocol === 'http' ? '' : '/' + parts.protocol);
}
function specToPort(spec) {
var parts = parsePort(spec);
var parts = parsePortSpec(spec);
return parts.hostPort;
}

View File

@ -29,10 +29,22 @@ B {
background-color : $midGray;
}
.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)
**********/

View File

@ -4,7 +4,7 @@ import Ember from 'ember';
// hostIp::containerPort
// hostPort:containerPort
// containerPort
export function parsePort(str, defaultProtocol='http') {
export function parsePortSpec(str, defaultProtocol='http') {
str = str.trim();
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 match, hostIp, hostPort;
if ( match = hostStr.match(/^((.*):)?([^:]+)$/) )
@ -92,7 +92,81 @@ export function stringifyPort(port, defaultProtocol='http') {
return out;
}
// port
// 1.2.3.4
// 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 {
parsePort: parsePort,
stringifyPort: stringifyPort,
parsePortSpec: parsePortSpec,
stringifyPortSpec: stringifyPortSpec,
parseIpPort: parseIpPort,
portToInt: portToInt
};

View File

@ -1,5 +1,5 @@
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';
module('Unit | Utils | parse-port');
@ -21,28 +21,66 @@ var data = [
data.forEach(function(obj) {
var input = obj.str;
var actual = parsePort(input);
var actual = parsePortSpec(input);
if ( obj.parsed )
{
test('it can parse: ' + obj.str, function(assert) {
test('it can parse spec: ' + obj.str, function(assert) {
var expected = obj.parsed;
Object.keys(expected).forEach((key) => {
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 expected = obj.expected || obj.str;
var actual = stringifyPort(input);
var actual = stringifyPortSpec(input);
assert.strictEqual(actual, expected, 'Objects are stringified correctly');
});
}
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');
});
}
});
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');
});
}
});
});