mirror of https://github.com/rancher/ui.git
Add scheduler rules on containers and services
This commit is contained in:
parent
188211a163
commit
2aba2351f5
|
|
@ -1,4 +1,5 @@
|
||||||
import Ember from 'ember';
|
import Ember from 'ember';
|
||||||
|
import C from 'ui/utils/constants';
|
||||||
|
|
||||||
export default Ember.Component.extend({
|
export default Ember.Component.extend({
|
||||||
model: null,
|
model: null,
|
||||||
|
|
@ -8,7 +9,7 @@ export default Ember.Component.extend({
|
||||||
var obj = this.get('model')||{};
|
var obj = this.get('model')||{};
|
||||||
var keys = Ember.keys(obj);
|
var keys = Ember.keys(obj);
|
||||||
keys.forEach(function(key) {
|
keys.forEach(function(key) {
|
||||||
var isUser = key.indexOf('io.rancher') !== 0;
|
var isUser = key.indexOf(C.LABEL.SYSTEM_PREFIX) !== 0;
|
||||||
out.push(Ember.Object.create({
|
out.push(Ember.Object.create({
|
||||||
key: key,
|
key: key,
|
||||||
value: obj[key],
|
value: obj[key],
|
||||||
|
|
|
||||||
|
|
@ -4,10 +4,10 @@ export default Ember.Component.extend({
|
||||||
tagName: 'input',
|
tagName: 'input',
|
||||||
type: 'radio',
|
type: 'radio',
|
||||||
disabled: false,
|
disabled: false,
|
||||||
attributeBindings: ['name', 'type', 'value', 'checked:checked', 'disabled:disabled'],
|
attributeBindings: ['name', 'type', 'checked:checked', 'disabled:disabled'],
|
||||||
|
|
||||||
click : function() {
|
click : function() {
|
||||||
this.set('selection', this.$().val());
|
this.set('selection', this.get('value'));
|
||||||
},
|
},
|
||||||
|
|
||||||
checked : function() {
|
checked : function() {
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,252 @@
|
||||||
|
import Ember from 'ember';
|
||||||
|
import C from 'ui/utils/constants';
|
||||||
|
|
||||||
|
function splitEquals(str) {
|
||||||
|
var idx = str.indexOf('=');
|
||||||
|
if ( idx === -1 )
|
||||||
|
{
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
return [ str.substr(0,idx) , str.substr(idx+1) ];
|
||||||
|
}
|
||||||
|
|
||||||
|
export default Ember.Component.extend({
|
||||||
|
rule: null,
|
||||||
|
instance: null,
|
||||||
|
allHosts: null,
|
||||||
|
|
||||||
|
tagName: 'TR',
|
||||||
|
|
||||||
|
kind: null,
|
||||||
|
suffix: null,
|
||||||
|
userKey: null,
|
||||||
|
userValue: null,
|
||||||
|
|
||||||
|
actions: {
|
||||||
|
setKey: function(key) {
|
||||||
|
this.set('userKey', key);
|
||||||
|
},
|
||||||
|
|
||||||
|
setValue: function(value) {
|
||||||
|
this.set('userValue', value);
|
||||||
|
},
|
||||||
|
|
||||||
|
remove: function() {
|
||||||
|
this.sendAction('remove', this.get('rule'));
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
init: function() {
|
||||||
|
this._super();
|
||||||
|
|
||||||
|
var key = this.get('rule.key')||'';
|
||||||
|
var value = this.get('rule.value')||'';
|
||||||
|
var splitValue = splitEquals(value);
|
||||||
|
|
||||||
|
var match = key.match(/((_soft)?(_ne)?)$/);
|
||||||
|
if ( match )
|
||||||
|
{
|
||||||
|
this.set('suffix', match[1]);
|
||||||
|
key = key.substr(0, key.length - match[1].length);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
this.set('suffix','');
|
||||||
|
}
|
||||||
|
|
||||||
|
// Convert from an existing key into the 4 fields
|
||||||
|
switch ( key )
|
||||||
|
{
|
||||||
|
case C.LABEL.SCHED_CONTAINER:
|
||||||
|
this.setProperties({
|
||||||
|
kind: 'container_name',
|
||||||
|
userKey: '',
|
||||||
|
userValue: value,
|
||||||
|
});
|
||||||
|
break;
|
||||||
|
case C.LABEL.SCHED_CONTAINER_LABEL:
|
||||||
|
if ( splitValue[0] === C.LABEL.SERVICE_NAME )
|
||||||
|
{
|
||||||
|
this.setProperties({
|
||||||
|
kind: 'service_name',
|
||||||
|
userKey: '',
|
||||||
|
userValue: splitValue[1],
|
||||||
|
});
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
this.setProperties({
|
||||||
|
kind: 'container_label',
|
||||||
|
userKey: splitValue[0],
|
||||||
|
userValue: splitValue[1],
|
||||||
|
});
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case C.LABEL.SCHED_HOST_LABEL:
|
||||||
|
this.setProperties({
|
||||||
|
kind: 'host_label',
|
||||||
|
userKey: splitValue[0],
|
||||||
|
userValue: splitValue[1],
|
||||||
|
});
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
valuesChanged: function() {
|
||||||
|
var key = '';
|
||||||
|
var value = null;
|
||||||
|
|
||||||
|
var userKey = this.get('userKey')||'';
|
||||||
|
var userValue = this.get('userValue')||'';
|
||||||
|
|
||||||
|
switch ( this.get('kind') )
|
||||||
|
{
|
||||||
|
case 'host_label':
|
||||||
|
key = C.LABEL.SCHED_HOST_LABEL;
|
||||||
|
if ( userKey && userValue )
|
||||||
|
{
|
||||||
|
value = encodeURIComponent(userKey) + '=' + encodeURIComponent(userValue);
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case 'container_label':
|
||||||
|
key = C.LABEL.SCHED_CONTAINER_LABEL;
|
||||||
|
if ( userKey && userValue )
|
||||||
|
{
|
||||||
|
value = encodeURIComponent(userKey) + '=' + encodeURIComponent(userValue);
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case 'container_name':
|
||||||
|
key = C.LABEL.SCHED_CONTAINER;
|
||||||
|
if ( userValue )
|
||||||
|
{
|
||||||
|
value = encodeURIComponent(userValue);
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case 'service_name':
|
||||||
|
key = C.LABEL.SCHED_CONTAINER_LABEL;
|
||||||
|
if ( userValue )
|
||||||
|
{
|
||||||
|
value = encodeURIComponent(C.LABEL.SERVICE_NAME) + '=' + encodeURIComponent(userValue);
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
key += this.get('suffix');
|
||||||
|
|
||||||
|
Ember.setProperties(this.get('rule'),{
|
||||||
|
key: key,
|
||||||
|
value: value
|
||||||
|
});
|
||||||
|
}.observes('kind','suffix','userKey','userValue'),
|
||||||
|
|
||||||
|
schedulingRuleSuffixChoices: [
|
||||||
|
{label: 'must', value: ''},
|
||||||
|
{label: 'should', value: '_soft'},
|
||||||
|
{label: 'should not', value: '_soft_ne'},
|
||||||
|
{label: 'must not', value: '_ne'},
|
||||||
|
],
|
||||||
|
|
||||||
|
schedulingRuleKindChoices: [
|
||||||
|
{label: 'host label', value: 'host_label'},
|
||||||
|
{label: 'container with label', value: 'container_label'},
|
||||||
|
{label: 'service with the name', value: 'service_name'},
|
||||||
|
{label: 'container with the name', value: 'container_name'},
|
||||||
|
],
|
||||||
|
|
||||||
|
hostLabelKeyChoices: function() {
|
||||||
|
var out = [];
|
||||||
|
this.get('allHosts').forEach((host) => {
|
||||||
|
var keys = Ember.keys(host.get('labels')||{}).filter((key) => {
|
||||||
|
return key.indexOf(C.LABEL.SYSTEM_PREFIX) !== 0;
|
||||||
|
});
|
||||||
|
out.pushObjects(keys);
|
||||||
|
});
|
||||||
|
|
||||||
|
return out.sort().uniq();
|
||||||
|
}.property('allHosts.@each.labels'),
|
||||||
|
|
||||||
|
hostLabelValueChoices: function() {
|
||||||
|
var key = this.get('userKey');
|
||||||
|
|
||||||
|
var out = [];
|
||||||
|
this.get('allHosts').forEach((host) => {
|
||||||
|
var label = (host.get('labels')||{})[key];
|
||||||
|
if ( label )
|
||||||
|
{
|
||||||
|
var parts = label.split(/\s*,\s*/);
|
||||||
|
out.pushObjects(parts);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
return out.sort().uniq();
|
||||||
|
}.property('userKey','allHosts.@each.labels'),
|
||||||
|
|
||||||
|
allContainers: function() {
|
||||||
|
var out = [];
|
||||||
|
this.get('allHosts').map((host) => {
|
||||||
|
var containers = (host.get('instances')||[]).filter(function(instance) {
|
||||||
|
return instance.get('kind') === 'container' &&
|
||||||
|
!instance.get('systemContainer');
|
||||||
|
});
|
||||||
|
|
||||||
|
out.pushObjects(containers);
|
||||||
|
});
|
||||||
|
|
||||||
|
return out.sortBy('name','id').uniq();
|
||||||
|
}.property('allHosts.@each.instancesUpdated'),
|
||||||
|
|
||||||
|
containerLabelKeyChoices: function() {
|
||||||
|
var out = [];
|
||||||
|
this.get('allContainers').forEach((container) => {
|
||||||
|
var keys = Ember.keys(container.get('labels')||{}).filter((key) => {
|
||||||
|
return key.indexOf(C.LABEL.SYSTEM_PREFIX) !== 0;
|
||||||
|
});
|
||||||
|
out.pushObjects(keys);
|
||||||
|
});
|
||||||
|
|
||||||
|
return out.sort().uniq();
|
||||||
|
}.property('allContainers.@each.labels'),
|
||||||
|
|
||||||
|
containerLabelValueChoices: function() {
|
||||||
|
var key = this.get('userKey');
|
||||||
|
var out = [];
|
||||||
|
this.get('allContainers').forEach((container) => {
|
||||||
|
var label = (container.get('labels')||{})[key];
|
||||||
|
if ( label )
|
||||||
|
{
|
||||||
|
var parts = label.split(/\s*,\s*/);
|
||||||
|
out.pushObjects(parts);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
return out.sort().uniq();
|
||||||
|
}.property('userKey','allContainers.@each.labels'),
|
||||||
|
|
||||||
|
containerValueChoices: function() {
|
||||||
|
var out = [];
|
||||||
|
this.get('allContainers').forEach((container) => {
|
||||||
|
var name = container.get('name');
|
||||||
|
if ( name )
|
||||||
|
{
|
||||||
|
out.push(name);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
return out.sort().uniq();
|
||||||
|
}.property('allContainers.@each.name'),
|
||||||
|
|
||||||
|
serviceValueChoices: function() {
|
||||||
|
var out = [];
|
||||||
|
this.get('allContainers').forEach((container) => {
|
||||||
|
var label = (container.get('labels')||{})[C.LABEL.SERVICE_NAME];
|
||||||
|
if ( label )
|
||||||
|
{
|
||||||
|
var parts = label.split(/\s*,\s*/);
|
||||||
|
out.pushObjects(parts);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
return out.sort().uniq();
|
||||||
|
}.property('allContainers.@each.labels'),
|
||||||
|
});
|
||||||
|
|
@ -0,0 +1,125 @@
|
||||||
|
<td><div class="form-control-static input-sm">The host</div></td>
|
||||||
|
<td>
|
||||||
|
{{view "select"
|
||||||
|
class="form-control input-sm"
|
||||||
|
content=schedulingRuleSuffixChoices
|
||||||
|
value=suffix
|
||||||
|
optionValuePath="content.value"
|
||||||
|
optionLabelPath="content.label"
|
||||||
|
}}
|
||||||
|
</td>
|
||||||
|
<td><div class="form-control-static text-center input-sm">have a</div></td>
|
||||||
|
<td>
|
||||||
|
{{view "select"
|
||||||
|
class="form-control input-sm"
|
||||||
|
content=schedulingRuleKindChoices
|
||||||
|
value=kind
|
||||||
|
optionValuePath="content.value"
|
||||||
|
optionLabelPath="content.label"
|
||||||
|
}}
|
||||||
|
</td>
|
||||||
|
<td>
|
||||||
|
{{#if (not (or (eq kind "service_name") (eq kind "container_name")))}}
|
||||||
|
<div class="form-control-static text-center input-sm">of</div>
|
||||||
|
{{/if}}
|
||||||
|
</td>
|
||||||
|
<td>
|
||||||
|
{{#if (eq kind "host_label")}}
|
||||||
|
<div class="input-group">
|
||||||
|
{{input type="text" class="form-control input-sm" value=userKey}}
|
||||||
|
<div class="input-group-btn">
|
||||||
|
<button type="button" class="btn btn-default btn-sm dropdown-toggle" data-toggle="dropdown" aria-expanded="false"><span class="caret"></span></button>
|
||||||
|
<ul class="dropdown-menu dropdown-menu-right" role="menu">
|
||||||
|
{{#each key in hostLabelKeyChoices}}
|
||||||
|
<li><a {{action "setKey" key}}>{{key}}</a></li>
|
||||||
|
{{/each}}
|
||||||
|
</ul>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
{{/if}}
|
||||||
|
{{#if (eq kind "container_label")}}
|
||||||
|
<div class="input-group">
|
||||||
|
{{input type="text" class="form-control input-sm" value=userKey}}
|
||||||
|
<div class="input-group-btn">
|
||||||
|
<button type="button" class="btn btn-default btn-sm dropdown-toggle" data-toggle="dropdown" aria-expanded="false"><span class="caret"></span></button>
|
||||||
|
<ul class="dropdown-menu dropdown-menu-right" role="menu">
|
||||||
|
{{#each key in containerLabelKeyChoices}}
|
||||||
|
<li><a {{action "setKey" key}}>{{key}}</a></li>
|
||||||
|
{{/each}}
|
||||||
|
</ul>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
{{/if}}
|
||||||
|
</td>
|
||||||
|
<td>
|
||||||
|
{{#if (not (or (eq kind "service_name") (eq kind "container_name")))}}
|
||||||
|
<div class="form-control-static text-center input-sm">=</div>
|
||||||
|
{{/if}}
|
||||||
|
</td>
|
||||||
|
<td>
|
||||||
|
{{#if (eq kind "host_label")}}
|
||||||
|
<div {{bind-attr class="hostLabelValueChoices:input-group"}}>
|
||||||
|
{{input type="text" class="form-control input-sm" value=userValue}}
|
||||||
|
{{#if hostLabelValueChoices.length}}
|
||||||
|
<div class="input-group-btn">
|
||||||
|
<button type="button" class="btn btn-default btn-sm dropdown-toggle" data-toggle="dropdown" aria-expanded="false"><span class="caret"></span></button>
|
||||||
|
<ul class="dropdown-menu dropdown-menu-right" role="menu">
|
||||||
|
{{#each value in hostLabelValueChoices}}
|
||||||
|
<li><a {{action "setValue" value}}>{{value}}</a></li>
|
||||||
|
{{/each}}
|
||||||
|
</ul>
|
||||||
|
</div>
|
||||||
|
{{/if}}
|
||||||
|
</div>
|
||||||
|
{{/if}}
|
||||||
|
{{#if (eq kind "container_label")}}
|
||||||
|
<div {{bind-attr class="containerLabelValueChoices:input-group"}}>
|
||||||
|
{{input type="text" class="form-control input-sm" value=userValue}}
|
||||||
|
{{#if containerLabelValueChoices.length}}
|
||||||
|
<div class="input-group-btn">
|
||||||
|
<button type="button" class="btn btn-default btn-sm dropdown-toggle" data-toggle="dropdown" aria-expanded="false"><span class="caret"></span></button>
|
||||||
|
<ul class="dropdown-menu dropdown-menu-right" role="menu">
|
||||||
|
{{#each value in containerLabelValueChoices}}
|
||||||
|
<li><a {{action "setValue" value}}>{{value}}</a></li>
|
||||||
|
{{/each}}
|
||||||
|
</ul>
|
||||||
|
</div>
|
||||||
|
{{/if}}
|
||||||
|
</div>
|
||||||
|
{{/if}}
|
||||||
|
{{#if (eq kind "service_name")}}
|
||||||
|
<div {{bind-attr class="serviceValueChoices:input-group"}}>
|
||||||
|
{{input type="text" class="form-control input-sm" value=userValue}}
|
||||||
|
{{#if serviceValueChoices.length}}
|
||||||
|
<div class="input-group-btn">
|
||||||
|
<button type="button" class="btn btn-default btn-sm dropdown-toggle" data-toggle="dropdown" aria-expanded="false"><span class="caret"></span></button>
|
||||||
|
<ul class="dropdown-menu dropdown-menu-right" role="menu">
|
||||||
|
{{#each value in serviceValueChoices}}
|
||||||
|
<li><a {{action "setValue" value}}>{{value}}</a></li>
|
||||||
|
{{/each}}
|
||||||
|
</ul>
|
||||||
|
</div>
|
||||||
|
{{/if}}
|
||||||
|
</div>
|
||||||
|
{{/if}}
|
||||||
|
{{#if (eq kind "container_name")}}
|
||||||
|
<div {{bind-attr class="containerValueChoices:input-group"}}>
|
||||||
|
{{input type="text" class="form-control input-sm" value=userValue}}
|
||||||
|
{{#if containerValueChoices.length}}
|
||||||
|
<div class="input-group-btn">
|
||||||
|
<button type="button" class="btn btn-default btn-sm dropdown-toggle" data-toggle="dropdown" aria-expanded="false"><span class="caret"></span></button>
|
||||||
|
<ul class="dropdown-menu dropdown-menu-right" role="menu">
|
||||||
|
{{#each value in containerValueChoices}}
|
||||||
|
<li><a {{action "setValue" value}}>{{value}}</a></li>
|
||||||
|
{{/each}}
|
||||||
|
</ul>
|
||||||
|
</div>
|
||||||
|
{{/if}}
|
||||||
|
</div>
|
||||||
|
{{/if}}
|
||||||
|
</td>
|
||||||
|
<td class="text-right">
|
||||||
|
<div class="form-control-static input-sm">
|
||||||
|
<button {{action "remove"}} class="btn-circle-x" type="button" tabindex="-1"></button>
|
||||||
|
</div>
|
||||||
|
</td>
|
||||||
|
|
@ -12,6 +12,7 @@ export default Ember.View.extend({
|
||||||
addDnsSearch: addAction('addDnsSearch', '.dns-search-value'),
|
addDnsSearch: addAction('addDnsSearch', '.dns-search-value'),
|
||||||
addDevice: addAction('addDevice', '.device-host'),
|
addDevice: addAction('addDevice', '.device-host'),
|
||||||
addLabel: addAction('addLabel', '.label-key'),
|
addLabel: addAction('addLabel', '.label-key'),
|
||||||
|
addSchedulingRule: addAction('addSchedulingRule', '.schedule-rule'),
|
||||||
|
|
||||||
selectTab: function(name) {
|
selectTab: function(name) {
|
||||||
this.set('context.tab',name);
|
this.set('context.tab',name);
|
||||||
|
|
|
||||||
|
|
@ -131,7 +131,15 @@ export default Ember.Mixin.create(Cattle.NewOrEditMixin, EditHealthCheck, EditLa
|
||||||
ary.removeObject(item);
|
ary.removeObject(item);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
},
|
||||||
|
|
||||||
|
addSchedulingRule: function() {
|
||||||
|
this.send('addSystemLabel');
|
||||||
|
},
|
||||||
|
|
||||||
|
removeSchedulingRule: function(obj) {
|
||||||
|
this.send('removeLabel', obj);
|
||||||
|
},
|
||||||
},
|
},
|
||||||
|
|
||||||
// ----------------------------------
|
// ----------------------------------
|
||||||
|
|
@ -161,6 +169,7 @@ export default Ember.Mixin.create(Cattle.NewOrEditMixin, EditHealthCheck, EditLa
|
||||||
this.initMemory();
|
this.initMemory();
|
||||||
this.initLabels();
|
this.initLabels();
|
||||||
this.initHealthCheck();
|
this.initHealthCheck();
|
||||||
|
this.initScheduling();
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
|
|
@ -835,6 +844,36 @@ export default Ember.Mixin.create(Cattle.NewOrEditMixin, EditHealthCheck, EditLa
|
||||||
}.observes('strEntryPoint'),
|
}.observes('strEntryPoint'),
|
||||||
|
|
||||||
// ----------------------------------
|
// ----------------------------------
|
||||||
|
// Scheduling
|
||||||
|
// ----------------------------------
|
||||||
|
initScheduling: function() {
|
||||||
|
if ( this.get('instance.requestedHostId') )
|
||||||
|
{
|
||||||
|
this.set('isRequestedHost',true);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
this.set('isRequestedHost',false);
|
||||||
|
}
|
||||||
|
|
||||||
|
// @TODO import existing for clone
|
||||||
|
},
|
||||||
|
|
||||||
|
isRequestedHost: null,
|
||||||
|
isRequestedHostDidChange: function() {
|
||||||
|
if ( this.get('isRequestedHost') )
|
||||||
|
{
|
||||||
|
if ( !this.get('instance.requestedHostId') )
|
||||||
|
{
|
||||||
|
this.set('instance.requestedHostId', this.get('hostChoices.firstObject.id'));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
this.set('instance.requestedHostId', null);
|
||||||
|
}
|
||||||
|
}.observes('isRequestedHost'),
|
||||||
|
|
||||||
// ----------------------------------
|
// ----------------------------------
|
||||||
// Save
|
// Save
|
||||||
// ----------------------------------
|
// ----------------------------------
|
||||||
|
|
|
||||||
|
|
@ -1,4 +1,5 @@
|
||||||
import Ember from 'ember';
|
import Ember from 'ember';
|
||||||
|
import C from 'ui/utils/constants';
|
||||||
|
|
||||||
export default Ember.Mixin.create({
|
export default Ember.Mixin.create({
|
||||||
labelResource: Ember.computed.alias('primaryResource'),
|
labelResource: Ember.computed.alias('primaryResource'),
|
||||||
|
|
@ -82,6 +83,10 @@ export default Ember.Mixin.create({
|
||||||
return (this.get('labelArray')||[]).filterProperty('isUser',true);
|
return (this.get('labelArray')||[]).filterProperty('isUser',true);
|
||||||
}.property('labelArray.@each.isUser'),
|
}.property('labelArray.@each.isUser'),
|
||||||
|
|
||||||
|
systemLabelArray: function() {
|
||||||
|
return (this.get('labelArray')||[]).filterProperty('isUser',false);
|
||||||
|
}.property('labelArray.@each.isUser'),
|
||||||
|
|
||||||
initFields: function() {
|
initFields: function() {
|
||||||
this._super();
|
this._super();
|
||||||
this.initLabels();
|
this.initLabels();
|
||||||
|
|
@ -95,7 +100,7 @@ export default Ember.Mixin.create({
|
||||||
out.push(Ember.Object.create({
|
out.push(Ember.Object.create({
|
||||||
key: key,
|
key: key,
|
||||||
value: obj[key],
|
value: obj[key],
|
||||||
isUser: key.indexOf('io.rancher') !== 0,
|
isUser: key.indexOf(C.LABEL.SYSTEM_PREFIX) !== 0,
|
||||||
}));
|
}));
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
@ -107,9 +112,13 @@ export default Ember.Mixin.create({
|
||||||
var out = {};
|
var out = {};
|
||||||
this.get('labelArray').forEach(function(row) {
|
this.get('labelArray').forEach(function(row) {
|
||||||
if ( row.key )
|
if ( row.key )
|
||||||
|
{
|
||||||
|
// System labels have to have a value before they're added, users ones can be just key.
|
||||||
|
if ( row.isUser || row.value )
|
||||||
{
|
{
|
||||||
out[row.key] = row.value;
|
out[row.key] = row.value;
|
||||||
}
|
}
|
||||||
|
}
|
||||||
});
|
});
|
||||||
this.set('labelResource.labels', out);
|
this.set('labelResource.labels', out);
|
||||||
}.observes('labelArray.@each.{key,value}'),
|
}.observes('labelArray.@each.{key,value}'),
|
||||||
|
|
|
||||||
|
|
@ -77,18 +77,22 @@ export default Ember.Mixin.create(EditLabels, {
|
||||||
// ----------------------------------
|
// ----------------------------------
|
||||||
// Scheduling
|
// Scheduling
|
||||||
// ----------------------------------
|
// ----------------------------------
|
||||||
isScalePlural: Ember.computed.gt('service.scale', 1),
|
isGlobal: null,
|
||||||
isGlobalStr: null,
|
|
||||||
isGlobal: Ember.computed.equal('isGlobalStr','yes'),
|
|
||||||
initScheduling: function() {
|
initScheduling: function() {
|
||||||
var existing = this.getLabel(C.LABEL.SCHED_GLOBAL);
|
var existing = this.getLabel(C.LABEL.SCHED_GLOBAL);
|
||||||
this.set('isGlobalStr', (!!existing ? 'yes' : 'no'));
|
this.set('isGlobal', !!existing);
|
||||||
|
this._super();
|
||||||
|
if ( this.get('isRequestedHost') )
|
||||||
|
{
|
||||||
|
this.set('isGlobal', false);
|
||||||
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
globalDidChange: function() {
|
globalDidChange: function() {
|
||||||
if ( this.get('isGlobal') )
|
if ( this.get('isGlobal') )
|
||||||
{
|
{
|
||||||
this.setLabel(C.LABEL.SCHED_GLOBAL,'true');
|
this.setLabel(C.LABEL.SCHED_GLOBAL,'true');
|
||||||
|
this.set('isRequestedHost', false);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
|
|
@ -96,6 +100,13 @@ export default Ember.Mixin.create(EditLabels, {
|
||||||
}
|
}
|
||||||
}.observes('isGlobal'),
|
}.observes('isGlobal'),
|
||||||
|
|
||||||
|
isRequestedHostDidChangeGlobal: function() {
|
||||||
|
if ( this.get('isRequestedHost') )
|
||||||
|
{
|
||||||
|
this.set('isGlobal', false);
|
||||||
|
}
|
||||||
|
}.observes('isRequestedHost'),
|
||||||
|
|
||||||
// ----------------------------------
|
// ----------------------------------
|
||||||
// Save
|
// Save
|
||||||
// ----------------------------------
|
// ----------------------------------
|
||||||
|
|
|
||||||
|
|
@ -1,4 +1,5 @@
|
||||||
import Ember from 'ember';
|
import Ember from 'ember';
|
||||||
|
import C from 'ui/utils/constants';
|
||||||
|
|
||||||
export default Ember.Mixin.create({
|
export default Ember.Mixin.create({
|
||||||
labelResource: null,
|
labelResource: null,
|
||||||
|
|
@ -8,7 +9,7 @@ export default Ember.Mixin.create({
|
||||||
var obj = this.get('labelResource.labels')||{};
|
var obj = this.get('labelResource.labels')||{};
|
||||||
var keys = Ember.keys(obj).sort();
|
var keys = Ember.keys(obj).sort();
|
||||||
keys.forEach(function(key) {
|
keys.forEach(function(key) {
|
||||||
var isUser = key.indexOf('io.rancher') !== 0;
|
var isUser = key.indexOf(C.LABEL.SYSTEM_PREFIX) !== 0;
|
||||||
out.push(Ember.Object.create({
|
out.push(Ember.Object.create({
|
||||||
key: key,
|
key: key,
|
||||||
value: obj[key],
|
value: obj[key],
|
||||||
|
|
|
||||||
|
|
@ -1,4 +1,5 @@
|
||||||
import Cattle from 'ui/utils/cattle';
|
import Cattle from 'ui/utils/cattle';
|
||||||
|
import C from 'ui/utils/constants';
|
||||||
|
|
||||||
var Service = Cattle.TransitioningResource.extend({
|
var Service = Cattle.TransitioningResource.extend({
|
||||||
type: 'service',
|
type: 'service',
|
||||||
|
|
@ -9,9 +10,12 @@ var Service = Cattle.TransitioningResource.extend({
|
||||||
}.observes('consumedservices.@each.{id,name,state}'),
|
}.observes('consumedservices.@each.{id,name,state}'),
|
||||||
|
|
||||||
healthState: function() {
|
healthState: function() {
|
||||||
|
var isGlobal = Object.keys(this.get('labels')||{}).indexOf(C.LABEL.SCHED_GLOBAL) >= 0;
|
||||||
|
var instances = this.get('instances')||[];
|
||||||
|
|
||||||
// Get the state of each instance
|
// Get the state of each instance
|
||||||
var healthy = 0;
|
var healthy = 0;
|
||||||
(this.get('instances')||[]).forEach((instance) => {
|
instances.forEach((instance) => {
|
||||||
var resource = instance.get('state');
|
var resource = instance.get('state');
|
||||||
var health = instance.get('healthState');
|
var health = instance.get('healthState');
|
||||||
|
|
||||||
|
|
@ -21,7 +25,7 @@ var Service = Cattle.TransitioningResource.extend({
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
if ( healthy >= this.get('scale') )
|
if ( (isGlobal && healthy >= instances.get('length')) || (!isGlobal && healthy >= this.get('scale')) )
|
||||||
{
|
{
|
||||||
return 'healthy';
|
return 'healthy';
|
||||||
}
|
}
|
||||||
|
|
@ -34,6 +38,7 @@ var Service = Cattle.TransitioningResource.extend({
|
||||||
combinedState: function() {
|
combinedState: function() {
|
||||||
var service = this.get('state');
|
var service = this.get('state');
|
||||||
var health = this.get('healthState');
|
var health = this.get('healthState');
|
||||||
|
|
||||||
if ( ['active','updating-active'].indexOf(service) === -1 )
|
if ( ['active','updating-active'].indexOf(service) === -1 )
|
||||||
{
|
{
|
||||||
// If the service isn't active, return its state
|
// If the service isn't active, return its state
|
||||||
|
|
|
||||||
|
|
@ -10,7 +10,7 @@
|
||||||
</div>
|
</div>
|
||||||
<div class="col-sm-3 col-md-2">
|
<div class="col-sm-3 col-md-2">
|
||||||
<div class="radio small">
|
<div class="radio small">
|
||||||
<label>{{radio-button selection=isGlobalStr value="no"}} Run {{service.scale}} container{{#if isScalePlural}}s{{/if}}</label>
|
<label>{{radio-button selection=isGlobal value=false}} Run {{service.scale}} container{{#if (not (eq service.scale 1))}}s{{/if}}</label>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="col-sm-9 col-md-6">
|
<div class="col-sm-9 col-md-6">
|
||||||
|
|
@ -20,7 +20,7 @@
|
||||||
<div class="row">
|
<div class="row">
|
||||||
<div class="col-sm-12 col-md-8 col-md-push-2">
|
<div class="col-sm-12 col-md-8 col-md-push-2">
|
||||||
<div class="radio small">
|
<div class="radio small">
|
||||||
<label>{{radio-button selection=isGlobalStr value="yes"}} Always run one instance of this container on every host</label>
|
<label>{{radio-button selection=isGlobal value=true}} Always run one instance of this container on every host</label>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
|
||||||
|
|
@ -5,5 +5,6 @@ export default NewContainerView.extend({
|
||||||
actions: {
|
actions: {
|
||||||
addVolumeFromService: addAction('addVolumeFromService', '.volumefromservice-container'),
|
addVolumeFromService: addAction('addVolumeFromService', '.volumefromservice-container'),
|
||||||
addServiceLink: addAction('addServiceLink', '.service-link'),
|
addServiceLink: addAction('addServiceLink', '.service-link'),
|
||||||
|
addSchedulingRule: addAction('addSchedulingRule', '.schedule-rule'),
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|
|
||||||
|
|
@ -174,6 +174,8 @@ fieldset[disabled] .btn {
|
||||||
}
|
}
|
||||||
|
|
||||||
.table {
|
.table {
|
||||||
|
margin-bottom: 0;
|
||||||
|
|
||||||
& > THEAD > TR > TH,
|
& > THEAD > TR > TH,
|
||||||
& > THEAD > TR > TD,
|
& > THEAD > TR > TD,
|
||||||
& > TBODY > TR > TH,
|
& > TBODY > TR > TH,
|
||||||
|
|
@ -418,3 +420,8 @@ FORM LABEL,
|
||||||
.help-block {
|
.help-block {
|
||||||
color: #444;
|
color: #444;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.form-group {
|
||||||
|
padding-bottom: 15px;
|
||||||
|
margin-bottom: 0;
|
||||||
|
}
|
||||||
|
|
|
||||||
|
|
@ -631,7 +631,7 @@ ASIDE {
|
||||||
}
|
}
|
||||||
|
|
||||||
HR {
|
HR {
|
||||||
margin: 0 0 15px 0;
|
margin: 8px 0 8px 0;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -7,7 +7,7 @@
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="row form-group">
|
<div class="row">
|
||||||
<div class="col-sm-12 col-md-2 form-label">
|
<div class="col-sm-12 col-md-2 form-label">
|
||||||
<label for="description">Description</label>
|
<label for="description">Description</label>
|
||||||
</div>
|
</div>
|
||||||
|
|
|
||||||
|
|
@ -1,10 +1,10 @@
|
||||||
<div class="row form-group">
|
<div class="row">
|
||||||
<div class="col-sm-12 col-md-2 form-label">
|
<div class="col-sm-12 col-md-2 form-label">
|
||||||
<label class="form-control-static">Service Links</label>
|
<label>Service Links</label>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="col-sm-12 col-md-8">
|
<div class="col-sm-12 col-md-8">
|
||||||
<div class="form-control-static">
|
<div>
|
||||||
{{#if serviceChoices.length}}
|
{{#if serviceChoices.length}}
|
||||||
<button class="btn-circle-plus" {{action "addServiceLink" target="view"}}></button>
|
<button class="btn-circle-plus" {{action "addServiceLink" target="view"}}></button>
|
||||||
{{else}}
|
{{else}}
|
||||||
|
|
|
||||||
|
|
@ -11,6 +11,7 @@
|
||||||
<li role="presentation" class="tab" data-section="healthcheck" {{action "selectTab" "healthcheck" target=view}}><a>Health Check</a></li>
|
<li role="presentation" class="tab" data-section="healthcheck" {{action "selectTab" "healthcheck" target=view}}><a>Health Check</a></li>
|
||||||
<li role="presentation" class="tab" data-section="security" {{action "selectTab" "security" target=view}}><a>Security/Host</a></li>
|
<li role="presentation" class="tab" data-section="security" {{action "selectTab" "security" target=view}}><a>Security/Host</a></li>
|
||||||
<li role="presentation" class="tab" data-section="labels" {{action "selectTab" "labels" target=view}}><a>Labels</a></li>
|
<li role="presentation" class="tab" data-section="labels" {{action "selectTab" "labels" target=view}}><a>Labels</a></li>
|
||||||
|
<li role="presentation" class="tab" data-section="scheduling" {{action "selectTab" "scheduling" target=view}}><a>Scheduling</a></li>
|
||||||
</ul>
|
</ul>
|
||||||
</section>
|
</section>
|
||||||
|
|
||||||
|
|
@ -46,11 +47,9 @@
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
{{#if isService}}
|
|
||||||
<div class="section" data-section="scheduling">
|
<div class="section" data-section="scheduling">
|
||||||
{{partial "container/new-scheduling"}}
|
{{partial "container/new-scheduling"}}
|
||||||
</div>
|
</div>
|
||||||
{{/if}}
|
|
||||||
|
|
||||||
</section>
|
</section>
|
||||||
</div>
|
</div>
|
||||||
|
|
|
||||||
|
|
@ -120,7 +120,7 @@
|
||||||
</tr>
|
</tr>
|
||||||
{{/each}}
|
{{/each}}
|
||||||
</table>
|
</table>
|
||||||
<div class="text-muted" style="font-size: 12px; margin-top: -20px; margin-bottom: 12px;">
|
<div class="text-muted" style="font-size: 12px; margin-bottom: 12px;">
|
||||||
ProTip: Paste one or more lines of name=value pairs into any name field for easy bulk entry.
|
ProTip: Paste one or more lines of name=value pairs into any name field for easy bulk entry.
|
||||||
</div>
|
</div>
|
||||||
{{/if}}
|
{{/if}}
|
||||||
|
|
|
||||||
|
|
@ -1,4 +1,4 @@
|
||||||
<div class="row form-group">
|
<div class="row">
|
||||||
<div class="col-sm-12 col-md-2 form-label">
|
<div class="col-sm-12 col-md-2 form-label">
|
||||||
<label for="userImageUuid" class="form-control-static">Select Image</label>
|
<label for="userImageUuid" class="form-control-static">Select Image</label>
|
||||||
</div>
|
</div>
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,61 @@
|
||||||
|
<div class="form-group">
|
||||||
|
<div class="radio">
|
||||||
|
<label class="form-control-static">{{radio-button selection=isRequestedHost value=true}}
|
||||||
|
{{#if isService}}
|
||||||
|
Run <b>all</b> containers on a specific host
|
||||||
|
{{else}}
|
||||||
|
Run on a specific host
|
||||||
|
{{/if}}</label>
|
||||||
|
{{#if isRequestedHost}}:
|
||||||
|
{{view "select"
|
||||||
|
class="form-control"
|
||||||
|
content=hostChoices
|
||||||
|
value=instance.requestedHostId
|
||||||
|
optionValuePath="content.id"
|
||||||
|
optionLabelPath="content.name"
|
||||||
|
disabled=(not isRequestedHost)
|
||||||
|
style="display: inline-block; width: auto;"
|
||||||
|
}}
|
||||||
|
{{/if}}
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="radio">
|
||||||
|
<label>{{radio-button selection=isRequestedHost value=false}}
|
||||||
|
{{#if isService}}
|
||||||
|
Automatically pick hosts for each container matching scheduling rules :
|
||||||
|
{{else}}
|
||||||
|
Automatically pick a host matching scheduling rules :
|
||||||
|
{{/if}}
|
||||||
|
</label>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{{#unless isRequestedHost}}
|
||||||
|
<div class="form-control-static">
|
||||||
|
<button class="btn-circle-plus" {{action "addSchedulingRule" target="view"}}></button>
|
||||||
|
</div>
|
||||||
|
{{#if systemLabelArray.length}}
|
||||||
|
<table class="table fixed no-lines no-top-padding tight small">
|
||||||
|
<thead>
|
||||||
|
<tr class="text-muted">
|
||||||
|
<th width="70"></th>
|
||||||
|
<th width="100">Condition</th>
|
||||||
|
<th width="60"></th>
|
||||||
|
<th width="180">Field</th>
|
||||||
|
<th width="30"></th>
|
||||||
|
<th>Key</th>
|
||||||
|
<th width="30"></th>
|
||||||
|
<th>Value</th>
|
||||||
|
<th width="30"></th>
|
||||||
|
</tr>
|
||||||
|
</thead>
|
||||||
|
<tbody>
|
||||||
|
{{#each rule in labelArray}}
|
||||||
|
{{#unless rule.isUser}}
|
||||||
|
{{scheduling-rule-row rule=rule allHosts=allHosts remove="removeSchedulingRule"}}
|
||||||
|
{{/unless}}
|
||||||
|
{{/each}}
|
||||||
|
</tbody>
|
||||||
|
</table>
|
||||||
|
{{/if}}
|
||||||
|
{{/unless}}
|
||||||
|
</div>
|
||||||
|
|
@ -1,16 +1,11 @@
|
||||||
<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 for="requestedHostId" class="form-control-static">Host</label>
|
<label for="privileged" class="form-control-static">Privileged</label>
|
||||||
</div>
|
</div>
|
||||||
<div class="col-sm-12 col-md-3 form-label">
|
<div class="col-sm-12 col-md-3 form-label">
|
||||||
{{view "select"
|
<div class="checkbox form-control-static">
|
||||||
prompt="Automatically pick a host"
|
<label for="privileged">{{input type="checkbox" id="privileged" checked=instance.privileged}} Full access to the host</label>
|
||||||
class="form-control"
|
</div>
|
||||||
content=hostChoices
|
|
||||||
value=instance.requestedHostId
|
|
||||||
optionValuePath="content.id"
|
|
||||||
optionLabelPath="content.name"
|
|
||||||
}}
|
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="col-sm-12 col-md-2 form-label">
|
<div class="col-sm-12 col-md-2 form-label">
|
||||||
|
|
@ -41,18 +36,6 @@
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="row form-group">
|
|
||||||
<div class="col-sm-12 col-md-2 form-label">
|
|
||||||
<label for="privileged" class="form-control-static">Privileged</label>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div class="col-sm-12 col-md-8">
|
|
||||||
<div class="checkbox form-control-static">
|
|
||||||
<label for="privileged">{{input type="checkbox" id="privileged" checked=instance.privileged}} Give the container full access to the host</label>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div class="row">
|
<div class="row">
|
||||||
<div class="col-sm-12 col-md-2 form-label">
|
<div class="col-sm-12 col-md-2 form-label">
|
||||||
<label class="form-control-static">Capabilities</label>
|
<label class="form-control-static">Capabilities</label>
|
||||||
|
|
|
||||||
|
|
@ -30,7 +30,7 @@
|
||||||
</tr>
|
</tr>
|
||||||
{{/each}}
|
{{/each}}
|
||||||
</table>
|
</table>
|
||||||
<div class="text-muted" style="font-size: 12px; margin-top: -20px; margin-bottom: 12px;">
|
<div class="text-muted" style="font-size: 12px; margin-bottom: 12px;">
|
||||||
ProTip: Paste one or more lines of key=value pairs into any key field for easy bulk entry.
|
ProTip: Paste one or more lines of key=value pairs into any key field for easy bulk entry.
|
||||||
</div>
|
</div>
|
||||||
{{/if}}
|
{{/if}}
|
||||||
|
|
|
||||||
|
|
@ -75,7 +75,11 @@ export default {
|
||||||
},
|
},
|
||||||
|
|
||||||
LABEL: {
|
LABEL: {
|
||||||
|
SYSTEM_PREFIX: 'io.rancher.',
|
||||||
SERVICE_NAME: 'io.rancher.service.name',
|
SERVICE_NAME: 'io.rancher.service.name',
|
||||||
SCHED_GLOBAL: 'io.rancher.scheduler.global',
|
SCHED_GLOBAL: 'io.rancher.scheduler.global',
|
||||||
|
SCHED_CONTAINER: 'io.rancher.scheduler.affinity:container',
|
||||||
|
SCHED_HOST_LABEL: 'io.rancher.scheduler.affinity:host_label',
|
||||||
|
SCHED_CONTAINER_LABEL: 'io.rancher.scheduler.affinity:container_label',
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,6 @@
|
||||||
{
|
{
|
||||||
"name": "ui",
|
"name": "ui",
|
||||||
"version": "0.23.0-rc1",
|
"version": "0.23.0-rc4",
|
||||||
"private": true,
|
"private": true,
|
||||||
"directories": {
|
"directories": {
|
||||||
"doc": "doc",
|
"doc": "doc",
|
||||||
|
|
@ -38,6 +38,7 @@
|
||||||
"ember-cli-uglify": "1.0.1",
|
"ember-cli-uglify": "1.0.1",
|
||||||
"ember-export-application-global": "^1.0.2",
|
"ember-export-application-global": "^1.0.2",
|
||||||
"ember-inline-svg": "^0.1.2",
|
"ember-inline-svg": "^0.1.2",
|
||||||
|
"ember-truth-helpers": "0.0.5",
|
||||||
"express": "^4.8.5",
|
"express": "^4.8.5",
|
||||||
"forever-agent": "^0.5.2",
|
"forever-agent": "^0.5.2",
|
||||||
"glob": "^5.0.3",
|
"glob": "^5.0.3",
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue