Merge pull request #495 from vincent99/master
K8s, PL fixes, Subscription fixes
|
|
@ -3,6 +3,7 @@ import C from 'ui/utils/constants';
|
||||||
|
|
||||||
export default Ember.Controller.extend({
|
export default Ember.Controller.extend({
|
||||||
settings: Ember.inject.service(),
|
settings: Ember.inject.service(),
|
||||||
|
projects: Ember.inject.service(),
|
||||||
|
|
||||||
queryParams: ['backToAdd'],
|
queryParams: ['backToAdd'],
|
||||||
backToAdd: false,
|
backToAdd: false,
|
||||||
|
|
@ -30,7 +31,6 @@ export default Ember.Controller.extend({
|
||||||
}
|
}
|
||||||
|
|
||||||
if (this.get('backToAdd')) {
|
if (this.get('backToAdd')) {
|
||||||
|
|
||||||
propsOut[C.SETTING.API_HOST] = model.host;
|
propsOut[C.SETTING.API_HOST] = model.host;
|
||||||
} else {
|
} else {
|
||||||
|
|
||||||
|
|
@ -59,7 +59,7 @@ export default Ember.Controller.extend({
|
||||||
|
|
||||||
if (this.get('backToAdd')) {
|
if (this.get('backToAdd')) {
|
||||||
|
|
||||||
this.transitionToRoute('hosts.new');
|
this.transitionToRoute('hosts.new', this.get('projects.current.id'));
|
||||||
} else {
|
} else {
|
||||||
|
|
||||||
this.send('goToPrevious');
|
this.send('goToPrevious');
|
||||||
|
|
|
||||||
|
|
@ -5,6 +5,7 @@ export default Ember.Route.extend({
|
||||||
cookies: Ember.inject.service(),
|
cookies: Ember.inject.service(),
|
||||||
github: Ember.inject.service(),
|
github: Ember.inject.service(),
|
||||||
access: Ember.inject.service(),
|
access: Ember.inject.service(),
|
||||||
|
settings: Ember.inject.service(),
|
||||||
|
|
||||||
previousParams: null,
|
previousParams: null,
|
||||||
previousRoute: null,
|
previousRoute: null,
|
||||||
|
|
@ -163,7 +164,13 @@ export default Ember.Route.extend({
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
|
updateWindowTitle: function() {
|
||||||
|
document.title = this.get('settings.appName');
|
||||||
|
}.observes('settings.appName'),
|
||||||
|
|
||||||
beforeModel() {
|
beforeModel() {
|
||||||
|
this.updateWindowTitle();
|
||||||
|
|
||||||
var agent = window.navigator.userAgent.toLowerCase();
|
var agent = window.navigator.userAgent.toLowerCase();
|
||||||
if ( agent.indexOf('msie ') >= 0 || agent.indexOf('trident/') >= 0 || agent.indexOf('edge/') >= 0 )
|
if ( agent.indexOf('msie ') >= 0 || agent.indexOf('trident/') >= 0 || agent.indexOf('edge/') >= 0 )
|
||||||
{
|
{
|
||||||
|
|
|
||||||
|
|
@ -18,7 +18,7 @@
|
||||||
|
|
||||||
<div class="loadfield">
|
<div class="loadfield">
|
||||||
<div class="grass"></div>
|
<div class="grass"></div>
|
||||||
<img class="cow" src="{{app.baseAssets}}assets/images/logos/main_blue.svg" />
|
<img class="cow" src="{{app.baseAssets}}assets/images/logos/main-loading.svg" />
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
{{/if}}
|
{{/if}}
|
||||||
|
|
|
||||||
|
|
@ -11,4 +11,13 @@ export default Ember.Controller.extend({
|
||||||
hasKubernetes: false,
|
hasKubernetes: false,
|
||||||
hasSystem: false,
|
hasSystem: false,
|
||||||
hasVm: Ember.computed.alias('settings.hasVm'),
|
hasVm: Ember.computed.alias('settings.hasVm'),
|
||||||
|
|
||||||
|
init() {
|
||||||
|
this._super();
|
||||||
|
this.k8sChanged();
|
||||||
|
},
|
||||||
|
|
||||||
|
k8sChanged: function() {
|
||||||
|
this.set('hasKubernetes', !!this.get('projects.current.kubernetes'));
|
||||||
|
}.observes('projects.current.kubernetes'),
|
||||||
});
|
});
|
||||||
|
|
|
||||||
|
|
@ -4,14 +4,11 @@ export default Ember.Route.extend({
|
||||||
projects: Ember.inject.service(),
|
projects: Ember.inject.service(),
|
||||||
|
|
||||||
redirect() {
|
redirect() {
|
||||||
this.get('projects').selectDefault().then((project) => {
|
var project = this.get('projects.current');
|
||||||
if ( project ) {
|
if ( project ) {
|
||||||
this.replaceWith('authenticated.project', project.get('id'));
|
this.replaceWith('authenticated.project', project.get('id'));
|
||||||
} else {
|
} else {
|
||||||
this.replaceWith('settings.projects');
|
|
||||||
}
|
|
||||||
}).catch(() => {
|
|
||||||
this.replaceWith('settings.projects');
|
this.replaceWith('settings.projects');
|
||||||
});
|
}
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|
|
||||||
|
|
@ -1,50 +1,31 @@
|
||||||
import Ember from 'ember';
|
import Ember from 'ember';
|
||||||
import Socket from 'ui/utils/socket';
|
|
||||||
import Util from 'ui/utils/util';
|
|
||||||
import C from 'ui/utils/constants';
|
|
||||||
import { hasThings } from 'ui/authenticated/project/controller';
|
import { hasThings } from 'ui/authenticated/project/controller';
|
||||||
|
|
||||||
export default Ember.Route.extend({
|
export default Ember.Route.extend({
|
||||||
k8s : Ember.inject.service(),
|
|
||||||
prefs : Ember.inject.service(),
|
|
||||||
projects : Ember.inject.service(),
|
|
||||||
access : Ember.inject.service(),
|
access : Ember.inject.service(),
|
||||||
userTheme : Ember.inject.service('user-theme'),
|
|
||||||
|
|
||||||
socket : null,
|
|
||||||
pingTimer : null,
|
|
||||||
k8sUidBlacklist: null,
|
|
||||||
|
|
||||||
model(params, transition) {
|
model(params, transition) {
|
||||||
if ( !params.project_id )
|
var project = this.modelFor('authenticated').project;
|
||||||
|
if ( !project )
|
||||||
{
|
{
|
||||||
// If there isn't a project, pick one
|
this.replaceWith('settings.projects');
|
||||||
return this.get('projects').selectDefault().then((project) => {
|
return;
|
||||||
if ( project )
|
|
||||||
{
|
|
||||||
this.replaceWith('authenticated.project', project.get('id'));
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
this.replaceWith('settings.projects');
|
|
||||||
}
|
|
||||||
}).catch(() => {
|
|
||||||
this.replaceWith('settings.projects');
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return this.loadProject(params.project_id).then((project) => {
|
// If the project ID in the URL is out of sync somehow, bail
|
||||||
this.set(`tab-session.${C.TABSESSION.PROJECT}`, project.get('id'));
|
if ( project.get('id') !== params.project_id )
|
||||||
this.get('projects').setCurrent(project);
|
{
|
||||||
|
this.replaceWith('authenticated');
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
return this.loadSchemas().then(() => {
|
return this.loadSchemas().then(() => {
|
||||||
return this.loadStacks().then((stacks) => {
|
return this.loadStacks().then((stacks) => {
|
||||||
hasThings(stacks, project, window.lc('authenticated'));
|
hasThings(stacks, project, window.lc('authenticated'));
|
||||||
|
|
||||||
return Ember.Object.create({
|
return Ember.Object.create({
|
||||||
project: project,
|
project: project,
|
||||||
stacks: stacks,
|
stacks: stacks,
|
||||||
});
|
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
}).catch((err) => {
|
}).catch((err) => {
|
||||||
|
|
@ -65,10 +46,6 @@ export default Ember.Route.extend({
|
||||||
return ret;
|
return ret;
|
||||||
},
|
},
|
||||||
|
|
||||||
loadProject(id) {
|
|
||||||
return this.get('store').find('project', id);
|
|
||||||
},
|
|
||||||
|
|
||||||
loadSchemas() {
|
loadSchemas() {
|
||||||
var store = this.get('store');
|
var store = this.get('store');
|
||||||
store.resetType('schema');
|
store.resetType('schema');
|
||||||
|
|
@ -79,345 +56,4 @@ export default Ember.Route.extend({
|
||||||
return this.get('store').findAllUnremoved('environment');
|
return this.get('store').findAllUnremoved('environment');
|
||||||
},
|
},
|
||||||
|
|
||||||
activate() {
|
|
||||||
this._super();
|
|
||||||
|
|
||||||
console.log('Activate socket for', this.get(`tab-session.${C.TABSESSION.PROJECT}`));
|
|
||||||
var store = this.get('store');
|
|
||||||
var boundTypeify = store._typeify.bind(store);
|
|
||||||
|
|
||||||
if ( !this.get('k8sUidBlacklist') )
|
|
||||||
{
|
|
||||||
this.set('k8sUidBlacklist', []);
|
|
||||||
}
|
|
||||||
|
|
||||||
var url = "ws://"+window.location.host + this.get('app.wsEndpoint');
|
|
||||||
|
|
||||||
url = Util.addQueryParam(url, 'projectId', this.get(`tab-session.${C.TABSESSION.PROJECT}`));
|
|
||||||
var socket = Socket.create({
|
|
||||||
url: url
|
|
||||||
});
|
|
||||||
this.set('socket', socket);
|
|
||||||
|
|
||||||
socket.on('message', (event) => {
|
|
||||||
var d = JSON.parse(event.data, boundTypeify);
|
|
||||||
//this._trySend('subscribeMessage',d);
|
|
||||||
|
|
||||||
if ( d.name === 'resource.change' )
|
|
||||||
{
|
|
||||||
this._trySend(d.resourceType+'Changed', d);
|
|
||||||
}
|
|
||||||
else if ( d.name === 'service.kubernetes.change' )
|
|
||||||
{
|
|
||||||
var changeType = (Ember.get(d, 'data.type')||'').toLowerCase();
|
|
||||||
var obj = Ember.get(d, 'data.object');
|
|
||||||
if ( changeType && obj )
|
|
||||||
{
|
|
||||||
this._trySend('k8sResourceChanged', changeType, obj);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else if ( d.name === 'ping' )
|
|
||||||
{
|
|
||||||
this._trySend('subscribePing', d);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
socket.on('connected', (tries, after) => {
|
|
||||||
this._trySend('subscribeConnected', tries, after);
|
|
||||||
});
|
|
||||||
|
|
||||||
socket.on('disconnected', () => {
|
|
||||||
this._trySend('subscribeDisconnected', this.get('tries'));
|
|
||||||
});
|
|
||||||
|
|
||||||
socket.connect();
|
|
||||||
},
|
|
||||||
|
|
||||||
deactivate() {
|
|
||||||
this._super();
|
|
||||||
this.closeSocket();
|
|
||||||
},
|
|
||||||
|
|
||||||
resetController() {
|
|
||||||
this._super();
|
|
||||||
this.closeSocket();
|
|
||||||
},
|
|
||||||
|
|
||||||
closeSocket() {
|
|
||||||
var socket = this.get('socket');
|
|
||||||
if ( socket )
|
|
||||||
{
|
|
||||||
socket.disconnect();
|
|
||||||
this.set('socket', null);
|
|
||||||
}
|
|
||||||
|
|
||||||
Ember.run.cancel(this.get('pingTimer'));
|
|
||||||
},
|
|
||||||
|
|
||||||
actions: {
|
|
||||||
// Raw message from the WebSocket
|
|
||||||
//subscribeMessage: function(/*data*/) {
|
|
||||||
//console.log('subscribeMessage',data);
|
|
||||||
//},
|
|
||||||
|
|
||||||
// WebSocket connected
|
|
||||||
subscribeConnected: function(tries,msec) {
|
|
||||||
var msg = 'Subscribe connected';
|
|
||||||
if (tries > 0)
|
|
||||||
{
|
|
||||||
msg += ' (after '+ tries + ' ' + (tries === 1 ? 'try' : 'tries');
|
|
||||||
if (msec)
|
|
||||||
{
|
|
||||||
msg += ', ' + (msec/1000) + ' sec';
|
|
||||||
}
|
|
||||||
|
|
||||||
msg += ')';
|
|
||||||
}
|
|
||||||
console.log(msg);
|
|
||||||
},
|
|
||||||
|
|
||||||
// WebSocket disconnected
|
|
||||||
subscribeDisconnected: function() {
|
|
||||||
console.log('Subscribe disconnected');
|
|
||||||
this.closeSocket();
|
|
||||||
},
|
|
||||||
|
|
||||||
subscribePing: function() {
|
|
||||||
console.log('Subscribe ping');
|
|
||||||
if ( this.get('pingTimer') )
|
|
||||||
{
|
|
||||||
Ember.run.cancel(this.get('pingTimer'));
|
|
||||||
}
|
|
||||||
|
|
||||||
this.set('pingTimer', Ember.run.later(this, function() {
|
|
||||||
console.log('Subscribe missed 2 pings...');
|
|
||||||
if ( this.get('socket') )
|
|
||||||
{
|
|
||||||
this.get('socket').connect();
|
|
||||||
}
|
|
||||||
}, 11000));
|
|
||||||
},
|
|
||||||
|
|
||||||
hostChanged: function(change) {
|
|
||||||
// If the host has a physicalHostId, ensure it is in the machine's hosts array.
|
|
||||||
var host = change.data.resource;
|
|
||||||
var machine = this.get('store').getById('machine', host.get('physicalHostId'));
|
|
||||||
if ( machine )
|
|
||||||
{
|
|
||||||
machine.get('hosts').addObject(host);
|
|
||||||
}
|
|
||||||
},
|
|
||||||
|
|
||||||
containerChanged: function(change) {
|
|
||||||
this._includeChanged('host', 'instances', 'hosts', change.data.resource);
|
|
||||||
},
|
|
||||||
|
|
||||||
virtualMachineChanged: function(change) {
|
|
||||||
this._includeChanged('host', 'instances', 'hosts', change.data.resource);
|
|
||||||
},
|
|
||||||
|
|
||||||
instanceChanged: function(change) {
|
|
||||||
this._includeChanged('host', 'instances', 'hosts', change.data.resource);
|
|
||||||
},
|
|
||||||
|
|
||||||
ipAddressChanged: function(change) {
|
|
||||||
this._includeChanged('host', 'ipAddresses', 'hosts', change.data.resource);
|
|
||||||
// this._includeChanged('container', 'container', 'ipAddresses', 'containers', change.data.resource);
|
|
||||||
},
|
|
||||||
|
|
||||||
loadBalancerTargetChanged: function(change) {
|
|
||||||
this._includeChanged('loadBalancer', 'loadBalancerTargets', 'loadBalancerId', change.data.resource);
|
|
||||||
},
|
|
||||||
|
|
||||||
loadBalancerChanged: function(change) {
|
|
||||||
var balancer = change.data.resource;
|
|
||||||
var config = balancer.get('loadBalancerConfig');
|
|
||||||
var balancers = config.get('loadBalancers');
|
|
||||||
if ( !balancers )
|
|
||||||
{
|
|
||||||
balancers = [];
|
|
||||||
config.set('loadBalancers',balancers);
|
|
||||||
}
|
|
||||||
|
|
||||||
if ( config.get('state') === 'removed' )
|
|
||||||
{
|
|
||||||
balancers.removeObject(balancer);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
balancers.addObject(balancer);
|
|
||||||
}
|
|
||||||
},
|
|
||||||
|
|
||||||
mountChanged: function(change) {
|
|
||||||
var mount = change.data.resource;
|
|
||||||
var volume = this.get('store').getById('volume', mount.get('volumeId'));
|
|
||||||
if ( volume )
|
|
||||||
{
|
|
||||||
var mounts = volume.get('mounts');
|
|
||||||
if ( !Ember.isArray(mounts) )
|
|
||||||
{
|
|
||||||
mounts = [];
|
|
||||||
volume.set('mounts',mounts);
|
|
||||||
}
|
|
||||||
|
|
||||||
var existingMount = mounts.filterBy('id', mount.get('id')).get('firstObject');
|
|
||||||
if ( existingMount )
|
|
||||||
{
|
|
||||||
existingMount.setProperties(mount);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
mounts.pushObject(mount);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
},
|
|
||||||
|
|
||||||
registryCredentialChanged: function(change) {
|
|
||||||
this._includeChanged('registry', 'credentials', 'registryId', change.data.resource);
|
|
||||||
},
|
|
||||||
|
|
||||||
loadBalancerServiceChanged: function(change) {
|
|
||||||
this._includeChanged('environment', 'services', 'environmentId', change.data.resource);
|
|
||||||
},
|
|
||||||
|
|
||||||
dnsServiceChanged: function(change) {
|
|
||||||
this._includeChanged('environment', 'services', 'environmentId', change.data.resource);
|
|
||||||
},
|
|
||||||
|
|
||||||
externalServiceChanged: function(change) {
|
|
||||||
this._includeChanged('environment', 'services', 'environmentId', change.data.resource);
|
|
||||||
},
|
|
||||||
|
|
||||||
serviceChanged: function(change) {
|
|
||||||
this._includeChanged('environment', 'services', 'environmentId', change.data.resource);
|
|
||||||
},
|
|
||||||
|
|
||||||
kubernetesServiceChanged: function(change) {
|
|
||||||
this._includeChanged('environment', 'services', 'environmentId', change.data.resource);
|
|
||||||
},
|
|
||||||
|
|
||||||
k8sResourceChanged: function(changeType, obj) {
|
|
||||||
//console.log('k8s change', changeType, (obj && obj.metadata && obj.metadata.uid ? obj.metadata.uid : 'none'));
|
|
||||||
if ( obj && obj.metadata && obj.metadata.uid && this.get('k8sUidBlacklist').indexOf(obj.metadata.uid) >= 0 )
|
|
||||||
{
|
|
||||||
//console.log('^-- Ignoring', changeType, 'for removed resource');
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
var resource = this.get('k8s')._typeify(obj);
|
|
||||||
|
|
||||||
if ( changeType === 'deleted' )
|
|
||||||
{
|
|
||||||
this.get('k8sUidBlacklist').pushObject(obj.metadata.uid);
|
|
||||||
this.get('store')._remove(resource.get('type'), resource);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
},
|
|
||||||
|
|
||||||
_trySend: function(/*arguments*/) {
|
|
||||||
try
|
|
||||||
{
|
|
||||||
this.send.apply(this,arguments);
|
|
||||||
}
|
|
||||||
catch (err)
|
|
||||||
{
|
|
||||||
if ( err instanceof Ember.Error && err.message.indexOf('Nothing handled the action') === 0 )
|
|
||||||
{
|
|
||||||
// Don't care
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
throw err;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
},
|
|
||||||
|
|
||||||
|
|
||||||
// Update the `?include=`-ed arrays of a host,
|
|
||||||
// e.g. when an instance changes:
|
|
||||||
// Update the destProperty='instances' array on all models of type resourceName='hosts'.
|
|
||||||
// to match the list in the the 'changed' resource's expectedProperty='hosts'
|
|
||||||
// _includeChanged( 'host', 'hosts', 'instances', 'hosts', instance)
|
|
||||||
_includeChanged: function(resourceName, destProperty, expectedProperty, changed) {
|
|
||||||
if (!changed)
|
|
||||||
{
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
var changedId = changed.get('id');
|
|
||||||
var store = this.get('store');
|
|
||||||
|
|
||||||
//console.log('Include changed',resourceName,destProperty,expectedProperty,changedId);
|
|
||||||
|
|
||||||
// All the resources
|
|
||||||
var all = store.all(resourceName);
|
|
||||||
|
|
||||||
// IDs the resource should be on
|
|
||||||
var expectedIds = [];
|
|
||||||
var expected = changed.get(expectedProperty)||[];
|
|
||||||
if ( !Ember.isArray(expected) )
|
|
||||||
{
|
|
||||||
expected = [expected];
|
|
||||||
}
|
|
||||||
|
|
||||||
if ( changed.get('state') !== 'purged' )
|
|
||||||
{
|
|
||||||
expectedIds = expected.map(function(item) {
|
|
||||||
if ( typeof item === 'object' )
|
|
||||||
{
|
|
||||||
return item.get('id');
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
return item;
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
// IDs it is currently on
|
|
||||||
var curIds = [];
|
|
||||||
all.forEach(function(item) {
|
|
||||||
var existing = (item.get(destProperty)||[]).filterBy('id', changedId);
|
|
||||||
if ( existing.length )
|
|
||||||
{
|
|
||||||
curIds.push(item.get('id'));
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
// Remove from resources the changed shouldn't be on
|
|
||||||
var remove = Util.arrayDiff(curIds, expectedIds);
|
|
||||||
remove.forEach((id) => {
|
|
||||||
//console.log('Remove',id);
|
|
||||||
store.find(resourceName, id).then((item) => {
|
|
||||||
var list = item.get(destProperty);
|
|
||||||
if ( list )
|
|
||||||
{
|
|
||||||
//console.log('Removing',changedId,'from',item.get('id'));
|
|
||||||
list.removeObjects(list.filterBy('id', changedId));
|
|
||||||
}
|
|
||||||
}).catch(() => {});
|
|
||||||
});
|
|
||||||
|
|
||||||
// Add or update resources the changed should be on
|
|
||||||
expectedIds.forEach((id) => {
|
|
||||||
//console.log('Expect',id);
|
|
||||||
store.find(resourceName, id).then((item) => {
|
|
||||||
var list = item.get(destProperty);
|
|
||||||
if ( !list )
|
|
||||||
{
|
|
||||||
list = [];
|
|
||||||
//console.log('Adding empty to',item.get('id'), destProperty);
|
|
||||||
item.set(destProperty, list);
|
|
||||||
}
|
|
||||||
|
|
||||||
var existing = list.filterBy('id', changedId);
|
|
||||||
if ( existing.length === 0)
|
|
||||||
{
|
|
||||||
//console.log('Adding',changedId,'to',item.get('id'), destProperty);
|
|
||||||
list.pushObject(changed);
|
|
||||||
}
|
|
||||||
}).catch(() => {});
|
|
||||||
});
|
|
||||||
},
|
|
||||||
});
|
});
|
||||||
|
|
|
||||||
|
|
@ -1,8 +1,9 @@
|
||||||
import Ember from 'ember';
|
import Ember from 'ember';
|
||||||
import C from 'ui/utils/constants';
|
import C from 'ui/utils/constants';
|
||||||
import Service from 'ui/models/service';
|
import Service from 'ui/models/service';
|
||||||
|
import Subscribe from 'ui/mixins/subscribe';
|
||||||
|
|
||||||
export default Ember.Route.extend({
|
export default Ember.Route.extend(Subscribe, {
|
||||||
prefs : Ember.inject.service(),
|
prefs : Ember.inject.service(),
|
||||||
projects : Ember.inject.service(),
|
projects : Ember.inject.service(),
|
||||||
k8s : Ember.inject.service(),
|
k8s : Ember.inject.service(),
|
||||||
|
|
@ -29,26 +30,35 @@ export default Ember.Route.extend({
|
||||||
preferences: this.loadPreferences(),
|
preferences: this.loadPreferences(),
|
||||||
settings: this.loadPublicSettings(),
|
settings: this.loadPublicSettings(),
|
||||||
}).then((hash) => {
|
}).then((hash) => {
|
||||||
|
var projectId = null;
|
||||||
if ( transition.params && transition.params['authenticated.project'] && transition.params['authenticated.project'].project_id )
|
if ( transition.params && transition.params['authenticated.project'] && transition.params['authenticated.project'].project_id )
|
||||||
{
|
{
|
||||||
return hash;
|
projectId = transition.params['authenticated.project'].project_id;
|
||||||
}
|
}
|
||||||
else
|
|
||||||
{
|
// Make sure a valid project is selected
|
||||||
// If not going to a project-specific page, make sure a project is selected
|
return this.get('projects').selectDefault(projectId).then((project) => {
|
||||||
return this.get('projects').selectDefault().then(() => {
|
hash.project = project;
|
||||||
return hash;
|
return this.loadKubernetes(project, hash).then((out) => {
|
||||||
|
return Ember.Object.create(out);
|
||||||
});
|
});
|
||||||
}
|
});
|
||||||
}).catch((err) => {
|
}).catch((err) => {
|
||||||
return this.loadingError(err, transition, Ember.Object.create({
|
return this.loadingError(err, transition, Ember.Object.create({
|
||||||
projects: [],
|
projects: [],
|
||||||
|
project: null,
|
||||||
}));
|
}));
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
|
|
||||||
|
activate() {
|
||||||
|
this._super();
|
||||||
|
this.connectSubscribe();
|
||||||
|
},
|
||||||
|
|
||||||
deactivate() {
|
deactivate() {
|
||||||
this._super();
|
this._super();
|
||||||
|
this.disconnectSubscribe();
|
||||||
|
|
||||||
// Forget all the things
|
// Forget all the things
|
||||||
this.reset();
|
this.reset();
|
||||||
|
|
@ -81,6 +91,36 @@ export default Ember.Route.extend({
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
|
|
||||||
|
loadKubernetes(project, hash) {
|
||||||
|
hash = hash || {};
|
||||||
|
|
||||||
|
if ( !project.get('kubernetes') )
|
||||||
|
{
|
||||||
|
hash.kubernetesReady = false;
|
||||||
|
return Ember.RSVP.resolve(hash);
|
||||||
|
}
|
||||||
|
|
||||||
|
var svc = this.get('k8s');
|
||||||
|
return svc.isReady().then((ready) => {
|
||||||
|
if ( ready )
|
||||||
|
{
|
||||||
|
return this.get('k8s').allNamespaces().then((all) => {
|
||||||
|
return this.get('k8s').selectNamespace().then((ns) => {
|
||||||
|
hash.kubernetesReady = true;
|
||||||
|
hash.namespaces = all;
|
||||||
|
hash.namespace = ns;
|
||||||
|
return hash;
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
hash.kubernetesReady = false;
|
||||||
|
return Ember.RSVP.resolve(hash);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
},
|
||||||
|
|
||||||
loadProjects() {
|
loadProjects() {
|
||||||
var svc = this.get('projects');
|
var svc = this.get('projects');
|
||||||
return svc.getAll().then((all) => {
|
return svc.getAll().then((all) => {
|
||||||
|
|
@ -126,6 +166,13 @@ export default Ember.Route.extend({
|
||||||
this.refresh();
|
this.refresh();
|
||||||
},
|
},
|
||||||
|
|
||||||
|
refreshKubernetes() {
|
||||||
|
var model = this.get('controller.model');
|
||||||
|
this.loadKubernetes(model.get('project')).then((hash) => {
|
||||||
|
model.setProperties(hash);
|
||||||
|
});
|
||||||
|
},
|
||||||
|
|
||||||
switchNamespace(namespaceId) {
|
switchNamespace(namespaceId) {
|
||||||
var route = window.lc('application').get('currentRouteName');
|
var route = window.lc('application').get('currentRouteName');
|
||||||
var okRoutes = [
|
var okRoutes = [
|
||||||
|
|
|
||||||
|
|
@ -136,11 +136,13 @@ export default Ember.Component.extend({
|
||||||
var requestLine='';
|
var requestLine='';
|
||||||
if ( path )
|
if ( path )
|
||||||
{
|
{
|
||||||
requestLine = method + ' ' + path + ' ' + version;
|
requestLine = method + ' ' + path + ' "' + version;
|
||||||
if ( host )
|
if ( host )
|
||||||
{
|
{
|
||||||
requestLine += '\r\nHost:\\ ' + host;
|
requestLine += '\r\nHost: ' + host;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
requestLine += '"';
|
||||||
}
|
}
|
||||||
this.set('healthCheck.requestLine', requestLine);
|
this.set('healthCheck.requestLine', requestLine);
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -119,7 +119,7 @@
|
||||||
</div>
|
</div>
|
||||||
{{/if}}
|
{{/if}}
|
||||||
|
|
||||||
{{#if isKubernetesTab}}
|
{{#if (and isKubernetesTab k8s.namespace)}}
|
||||||
<div class="dropdown btn-group project-btn pull-right">
|
<div class="dropdown btn-group project-btn pull-right">
|
||||||
<button type="button" class="btn btn-link dropdown-toggle text-left clip" data-toggle="dropdown" aria-expanded="false">
|
<button type="button" class="btn btn-link dropdown-toggle text-left clip" data-toggle="dropdown" aria-expanded="false">
|
||||||
<i class="icon icon-thumbnails icon-fw"></i> {{k8s.namespace.displayName}}
|
<i class="icon icon-thumbnails icon-fw"></i> {{k8s.namespace.displayName}}
|
||||||
|
|
|
||||||
|
|
@ -168,7 +168,7 @@ export default Ember.Component.extend(NewOrEdit, Sortable, {
|
||||||
doneSaving: function() {
|
doneSaving: function() {
|
||||||
var out = this._super();
|
var out = this._super();
|
||||||
this.get('projects').refreshAll();
|
this.get('projects').refreshAll();
|
||||||
this.get('router').transitionTo('settings.projects.detail', this.get('project.id'), {queryParams: {editing: false}});
|
this.sendAction('done');
|
||||||
return out;
|
return out;
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|
|
||||||
|
|
@ -56,17 +56,10 @@
|
||||||
|
|
||||||
<div class="{{if showEdit 'col-sm-12 col-md-8' 'col-xs-12'}}">
|
<div class="{{if showEdit 'col-sm-12 col-md-8' 'col-xs-12'}}">
|
||||||
<div class="well">
|
<div class="well">
|
||||||
{{#if showEdit}}
|
{{#if (and accessEnabled showEdit)}}
|
||||||
<div class="row form-group">
|
<div class="row form-group">
|
||||||
<div class="col-sm-12">
|
<div class="col-sm-12">
|
||||||
{{#if accessEnabled}}
|
{{input-identity action="checkMember" onError="error"}}
|
||||||
{{input-identity action="checkMember" onError="error"}}
|
|
||||||
{{else}}
|
|
||||||
<p class="help-block text-center r-mt0">
|
|
||||||
Access Control is not enabled.
|
|
||||||
Anybody with access to the API/UI acts as an admin and will be able to use any environment.
|
|
||||||
</p>
|
|
||||||
{{/if}}
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
{{/if}}
|
{{/if}}
|
||||||
|
|
@ -118,6 +111,11 @@
|
||||||
{{/each}}
|
{{/each}}
|
||||||
</tbody>
|
</tbody>
|
||||||
</table>
|
</table>
|
||||||
|
{{else}}
|
||||||
|
<p class="help-block text-center">
|
||||||
|
Access Control is not enabled.<br/>
|
||||||
|
Anybody with access to the API/UI acts as an admin and will be able to use any environment.
|
||||||
|
</p>
|
||||||
{{/if}}
|
{{/if}}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
|
||||||
|
|
@ -14,16 +14,16 @@
|
||||||
|
|
||||||
{{#unless settings.isPrivateLabel}}
|
{{#unless settings.isPrivateLabel}}
|
||||||
<div class="failplane">
|
<div class="failplane">
|
||||||
<img src="{{app.baseAssets}}assets/images/logos/fail_plane.svg" width="484" height="269" />
|
<img src="{{app.baseAssets}}assets/images/logos/fail-plane.svg" width="484" height="269" />
|
||||||
<img class="failprop" src="{{app.baseAssets}}assets/images/logos/fail_prop.svg" width="123" height="246" />
|
<img class="failprop" src="{{app.baseAssets}}assets/images/logos/fail-prop.svg" width="123" height="246" />
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<img class="failcow" src="{{app.baseAssets}}assets/images/logos/fail_cowparachute.svg" width="178" height="255"/>
|
<img class="failcow" src="{{app.baseAssets}}assets/images/logos/fail-cowparachute.svg" width="178" height="255"/>
|
||||||
<img class="cloud" src="{{app.baseAssets}}assets/images/logos/fail_cloud1.svg" width="254" height="121" />
|
<img class="cloud" src="{{app.baseAssets}}assets/images/logos/fail-cloud1.svg" width="254" height="121" />
|
||||||
<img class="cloud" src="{{app.baseAssets}}assets/images/logos/fail_cloud2.svg" width="151" height="91" />
|
<img class="cloud" src="{{app.baseAssets}}assets/images/logos/fail-cloud2.svg" width="151" height="91" />
|
||||||
<img class="cloud" src="{{app.baseAssets}}assets/images/logos/fail_cloud3.svg" width="175" height="102" />
|
<img class="cloud" src="{{app.baseAssets}}assets/images/logos/fail-cloud3.svg" width="175" height="102" />
|
||||||
<img class="cloud" src="{{app.baseAssets}}assets/images/logos/fail_cloud4.svg" width="300" height="142" />
|
<img class="cloud" src="{{app.baseAssets}}assets/images/logos/fail-cloud4.svg" width="300" height="142" />
|
||||||
<img class="cloud" src="{{app.baseAssets}}assets/images/logos/fail_cloud5.svg" width="320" height="152" />
|
<img class="cloud" src="{{app.baseAssets}}assets/images/logos/fail-cloud5.svg" width="320" height="152" />
|
||||||
|
|
||||||
<div class="wave"></div>
|
<div class="wave"></div>
|
||||||
{{/unless}}
|
{{/unless}}
|
||||||
|
|
|
||||||
|
|
@ -4,7 +4,7 @@
|
||||||
<meta charset="utf-8">
|
<meta charset="utf-8">
|
||||||
<meta http-equiv="X-UA-Compatible" content="IE=edge">
|
<meta http-equiv="X-UA-Compatible" content="IE=edge">
|
||||||
<link rel="icon" href="{{content-for 'baseAssets'}}favicon.ico">
|
<link rel="icon" href="{{content-for 'baseAssets'}}favicon.ico">
|
||||||
<title>{{content-for 'appName'}}</title>
|
<title>UI</title>
|
||||||
<!-- {{content-for 'version'}} -->
|
<!-- {{content-for 'version'}} -->
|
||||||
<meta name="description" content="">
|
<meta name="description" content="">
|
||||||
<meta name="viewport" content="width=694">
|
<meta name="viewport" content="width=694">
|
||||||
|
|
|
||||||
|
|
@ -3,19 +3,28 @@ import Ember from 'ember';
|
||||||
export default Ember.Route.extend({
|
export default Ember.Route.extend({
|
||||||
k8s: Ember.inject.service(),
|
k8s: Ember.inject.service(),
|
||||||
|
|
||||||
|
redirect() {
|
||||||
|
if ( !this.modelFor('authenticated').kubernetesReady )
|
||||||
|
{
|
||||||
|
this.transitionTo('k8s-tab.waiting');
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
model() {
|
model() {
|
||||||
var k8s = this.get('k8s');
|
var k8s = this.get('k8s');
|
||||||
|
if ( this.modelFor('authenticated').kubernetesReady )
|
||||||
return Ember.RSVP.hash({
|
{
|
||||||
namespaces: k8s.allNamespaces(),
|
return Ember.RSVP.hash({
|
||||||
services: k8s.allServices(),
|
namespaces: k8s.allNamespaces(),
|
||||||
rcs: k8s.allRCs(),
|
services: k8s.allServices(),
|
||||||
pods: k8s.allPods(),
|
rcs: k8s.allRCs(),
|
||||||
containers: this.get('store').findAll('container'),
|
pods: k8s.allPods(),
|
||||||
}).then((hash) => {
|
containers: this.get('store').findAll('container'),
|
||||||
return k8s.selectNamespace().then(() => {
|
}).then((hash) => {
|
||||||
k8s.setProperties(hash);
|
return k8s.selectNamespace().then(() => {
|
||||||
|
k8s.setProperties(hash);
|
||||||
|
});
|
||||||
});
|
});
|
||||||
});
|
}
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,100 @@
|
||||||
|
import Ember from 'ember';
|
||||||
|
import { debouncedObserver } from 'ui/utils/debounce';
|
||||||
|
|
||||||
|
export default Ember.Controller.extend({
|
||||||
|
k8s: Ember.inject.service(),
|
||||||
|
settings: Ember.inject.service(),
|
||||||
|
|
||||||
|
timer: null,
|
||||||
|
currentStep: 0,
|
||||||
|
|
||||||
|
steps: [
|
||||||
|
'Add at least one host',
|
||||||
|
'Waiting for a host to be active',
|
||||||
|
'Creating Kubernetes system stack',
|
||||||
|
'Starting services',
|
||||||
|
'Waiting for Kubernetes API',
|
||||||
|
'Creating Namespace',
|
||||||
|
],
|
||||||
|
|
||||||
|
updateStep: debouncedObserver('model.hosts.@each.state','model.stacks.@each.{state,externalId}', function() {
|
||||||
|
if ( (this.get('model.hosts.length') + this.get('model.machines.length')) === 0 )
|
||||||
|
{
|
||||||
|
this.set('currentStep', 0);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if ( this.get('model.hosts').filterBy('state','active').get('length') === 0 )
|
||||||
|
{
|
||||||
|
this.set('currentStep', 1);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
var stack = this.get('model.stacks').filterBy('externalId','system://kubernetes')[0];
|
||||||
|
if ( !stack )
|
||||||
|
{
|
||||||
|
this.set('currentStep', 2);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if ( stack.get('state') !== 'active' )
|
||||||
|
{
|
||||||
|
if ( stack.get('state') === 'inactive' )
|
||||||
|
{
|
||||||
|
stack.doAction('activate');
|
||||||
|
}
|
||||||
|
|
||||||
|
this.set('currentStep', 3);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
var services = this.get('model.services').filterBy('environmentId', stack.get('id'));
|
||||||
|
var num = services.get('length');
|
||||||
|
var active = services.filterBy('state','active').get('length');
|
||||||
|
if ( num === 0 || active < num )
|
||||||
|
{
|
||||||
|
this.set('currentStep', 3);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
this.set('currentStep', 4);
|
||||||
|
this.get('k8s').isReady().then((ready) => {
|
||||||
|
if ( ready )
|
||||||
|
{
|
||||||
|
this.get('k8s').getNamespace('default').then(() => {
|
||||||
|
this.set('currentStep', 6);
|
||||||
|
}).catch(() => {
|
||||||
|
this.set('currentStep', 5);
|
||||||
|
reschedule();
|
||||||
|
});
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
reschedule();
|
||||||
|
}
|
||||||
|
}).catch(() => {
|
||||||
|
reschedule();
|
||||||
|
});
|
||||||
|
|
||||||
|
var self = this;
|
||||||
|
function reschedule() {
|
||||||
|
self.set('timer', Ember.run.later(self, 'updateStep', 5000));
|
||||||
|
}
|
||||||
|
}),
|
||||||
|
|
||||||
|
onInit: function() {
|
||||||
|
this.updateStep();
|
||||||
|
}.on('init'),
|
||||||
|
|
||||||
|
stepChanged: function(){
|
||||||
|
if ( this.get('currentStep') === 6 )
|
||||||
|
{
|
||||||
|
this.send('refreshKubernetes');
|
||||||
|
this.transitionTo('k8s-tab.index');
|
||||||
|
}
|
||||||
|
}.observes('currentStep'),
|
||||||
|
|
||||||
|
deactivate() {
|
||||||
|
Ember.run.cancel(this.get('timer'));
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
@ -0,0 +1,19 @@
|
||||||
|
import Ember from 'ember';
|
||||||
|
|
||||||
|
export default Ember.Route.extend({
|
||||||
|
redirect() {
|
||||||
|
if ( this.modelFor('authenticated').kubernetesReady )
|
||||||
|
{
|
||||||
|
this.transitionTo('k8s-tab.index');
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
model() {
|
||||||
|
return Ember.RSVP.hash({
|
||||||
|
hosts: this.get('store').findAllUnremoved('host'),
|
||||||
|
machines: this.get('store').findAllUnremoved('machine'),
|
||||||
|
stacks: this.get('store').findAllUnremoved('environment'),
|
||||||
|
services: this.get('store').findAllUnremoved('service'),
|
||||||
|
});
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
@ -0,0 +1,35 @@
|
||||||
|
{{#if (gt currentStep 0)}}
|
||||||
|
<div class="well r-mt40 text" style="width: 50%; margin-left: 25%">
|
||||||
|
<h2 class="loading">Setting Up</h2>
|
||||||
|
|
||||||
|
<ul class="list-unstyled r-mt20">
|
||||||
|
{{#each steps as |step index|}}
|
||||||
|
<li class="{{if (lt currentStep index) 'text-muted'}}">
|
||||||
|
{{#if (eq currentStep index)}}
|
||||||
|
<i class="icon icon-spinner icon-spin" />
|
||||||
|
{{else}}
|
||||||
|
{{#if (gt currentStep index)}}
|
||||||
|
<i class="icon icon-check text-success" />
|
||||||
|
{{else}}
|
||||||
|
<i class="icon icon-circle-o" />
|
||||||
|
{{/if}}
|
||||||
|
{{/if}}
|
||||||
|
|
||||||
|
<span>{{step}}</span>
|
||||||
|
</li>
|
||||||
|
{{/each}}
|
||||||
|
</ul>
|
||||||
|
</div>
|
||||||
|
{{else}}
|
||||||
|
<section class="welcome well">
|
||||||
|
<i class="icon icon-host"></i>
|
||||||
|
<h2>Adding your first Host</h2>
|
||||||
|
<p>
|
||||||
|
Before launching Kubernetes, you must first add at least one Linux host that supports Docker 1.9.1+ and be able to reach the {{settings.appName}} server via HTTP.
|
||||||
|
{{settings.appName}} supports adding Linux hosts in the form of a virtual or physical machine from any public cloud providers, privately hosted clouds, or even bare metal servers.
|
||||||
|
{{#unless settings.isPrivateLabel}}<a href="{{docsBase}}/rancher-ui/infrastructure/hosts/" target="_blank">Learn More</a>{{/unless}}
|
||||||
|
</p>
|
||||||
|
{{#link-to "hosts.new" class="btn btn-primary"}}Add Host{{/link-to}}
|
||||||
|
</section>
|
||||||
|
{{/if}}
|
||||||
|
|
||||||
|
|
@ -0,0 +1,344 @@
|
||||||
|
import Ember from 'ember';
|
||||||
|
import Socket from 'ui/utils/socket';
|
||||||
|
import Util from 'ui/utils/util';
|
||||||
|
import C from 'ui/utils/constants';
|
||||||
|
|
||||||
|
export default Ember.Mixin.create({
|
||||||
|
k8s : Ember.inject.service(),
|
||||||
|
projects : Ember.inject.service(),
|
||||||
|
'tab-session' : Ember.inject.service(),
|
||||||
|
|
||||||
|
subscribeSocket : null,
|
||||||
|
pingTimer : null,
|
||||||
|
k8sUidBlacklist : null,
|
||||||
|
|
||||||
|
connectSubscribe() {
|
||||||
|
var projectId = this.get(`tab-session.${C.TABSESSION.PROJECT}`);
|
||||||
|
|
||||||
|
console.log('Connect socket for', projectId);
|
||||||
|
|
||||||
|
var store = this.get('store');
|
||||||
|
var boundTypeify = store._typeify.bind(store);
|
||||||
|
|
||||||
|
if ( !this.get('k8sUidBlacklist') )
|
||||||
|
{
|
||||||
|
this.set('k8sUidBlacklist', []);
|
||||||
|
}
|
||||||
|
|
||||||
|
var url = Util.addQueryParam("ws://"+window.location.host + this.get('app.wsEndpoint'), 'projectId', projectId);
|
||||||
|
var socket = Socket.create({url: url});
|
||||||
|
socket._projectId = projectId;
|
||||||
|
this.set('subscribeSocket', socket);
|
||||||
|
|
||||||
|
socket.on('message', (event) => {
|
||||||
|
var d = JSON.parse(event.data, boundTypeify);
|
||||||
|
//this._trySend('subscribeMessage',d);
|
||||||
|
|
||||||
|
if ( d.name === 'resource.change' )
|
||||||
|
{
|
||||||
|
this._trySend(d.resourceType+'Changed', d);
|
||||||
|
}
|
||||||
|
else if ( d.name === 'service.kubernetes.change' )
|
||||||
|
{
|
||||||
|
var changeType = (Ember.get(d, 'data.type')||'').toLowerCase();
|
||||||
|
var obj = Ember.get(d, 'data.object');
|
||||||
|
if ( changeType && obj )
|
||||||
|
{
|
||||||
|
this._trySend('k8sResourceChanged', changeType, obj);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else if ( d.name === 'ping' )
|
||||||
|
{
|
||||||
|
this._trySend('subscribePing', d);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
socket.on('connected', (tries, after) => {
|
||||||
|
this._trySend('subscribeConnected', tries, after);
|
||||||
|
});
|
||||||
|
|
||||||
|
socket.on('disconnected', () => {
|
||||||
|
this._trySend('subscribeDisconnected', this.get('tries'));
|
||||||
|
});
|
||||||
|
|
||||||
|
socket.connect();
|
||||||
|
},
|
||||||
|
|
||||||
|
disconnectSubscribe() {
|
||||||
|
Ember.run.cancel(this.get('pingTimer'));
|
||||||
|
|
||||||
|
var socket = this.get('subscribeSocket');
|
||||||
|
if ( socket )
|
||||||
|
{
|
||||||
|
console.log('Disconnect socket for', socket._projectId);
|
||||||
|
socket.disconnect();
|
||||||
|
this.set('socket', null);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
actions: {
|
||||||
|
// Raw message from the WebSocket
|
||||||
|
//subscribeMessage: function(/*data*/) {
|
||||||
|
//console.log('subscribeMessage',data);
|
||||||
|
//},
|
||||||
|
|
||||||
|
// WebSocket connected
|
||||||
|
subscribeConnected: function(tries,msec) {
|
||||||
|
var msg = 'Subscribe connected';
|
||||||
|
if (tries > 0)
|
||||||
|
{
|
||||||
|
msg += ' (after '+ tries + ' ' + (tries === 1 ? 'try' : 'tries');
|
||||||
|
if (msec)
|
||||||
|
{
|
||||||
|
msg += ', ' + (msec/1000) + ' sec';
|
||||||
|
}
|
||||||
|
|
||||||
|
msg += ')';
|
||||||
|
}
|
||||||
|
console.log(msg);
|
||||||
|
},
|
||||||
|
|
||||||
|
// WebSocket disconnected
|
||||||
|
subscribeDisconnected: function() {
|
||||||
|
console.log('Subscribe disconnected');
|
||||||
|
this.disconnectSubscribe();
|
||||||
|
},
|
||||||
|
|
||||||
|
subscribePing: function() {
|
||||||
|
console.log('Subscribe ping');
|
||||||
|
if ( this.get('pingTimer') )
|
||||||
|
{
|
||||||
|
Ember.run.cancel(this.get('pingTimer'));
|
||||||
|
}
|
||||||
|
|
||||||
|
this.set('pingTimer', Ember.run.later(this, function() {
|
||||||
|
console.log('Subscribe missed 2 pings...');
|
||||||
|
if ( this.get('subscribeSocket') )
|
||||||
|
{
|
||||||
|
this.get('subscribeSocket').connect();
|
||||||
|
}
|
||||||
|
}, 11000));
|
||||||
|
},
|
||||||
|
|
||||||
|
hostChanged: function(change) {
|
||||||
|
// If the host has a physicalHostId, ensure it is in the machine's hosts array.
|
||||||
|
var host = change.data.resource;
|
||||||
|
var machine = this.get('store').getById('machine', host.get('physicalHostId'));
|
||||||
|
if ( machine )
|
||||||
|
{
|
||||||
|
machine.get('hosts').addObject(host);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
containerChanged: function(change) {
|
||||||
|
this._includeChanged('host', 'instances', 'hosts', change.data.resource);
|
||||||
|
},
|
||||||
|
|
||||||
|
virtualMachineChanged: function(change) {
|
||||||
|
this._includeChanged('host', 'instances', 'hosts', change.data.resource);
|
||||||
|
},
|
||||||
|
|
||||||
|
instanceChanged: function(change) {
|
||||||
|
this._includeChanged('host', 'instances', 'hosts', change.data.resource);
|
||||||
|
},
|
||||||
|
|
||||||
|
ipAddressChanged: function(change) {
|
||||||
|
this._includeChanged('host', 'ipAddresses', 'hosts', change.data.resource);
|
||||||
|
// this._includeChanged('container', 'container', 'ipAddresses', 'containers', change.data.resource);
|
||||||
|
},
|
||||||
|
|
||||||
|
loadBalancerTargetChanged: function(change) {
|
||||||
|
this._includeChanged('loadBalancer', 'loadBalancerTargets', 'loadBalancerId', change.data.resource);
|
||||||
|
},
|
||||||
|
|
||||||
|
loadBalancerChanged: function(change) {
|
||||||
|
var balancer = change.data.resource;
|
||||||
|
var config = balancer.get('loadBalancerConfig');
|
||||||
|
var balancers = config.get('loadBalancers');
|
||||||
|
if ( !balancers )
|
||||||
|
{
|
||||||
|
balancers = [];
|
||||||
|
config.set('loadBalancers',balancers);
|
||||||
|
}
|
||||||
|
|
||||||
|
if ( config.get('state') === 'removed' )
|
||||||
|
{
|
||||||
|
balancers.removeObject(balancer);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
balancers.addObject(balancer);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
mountChanged: function(change) {
|
||||||
|
var mount = change.data.resource;
|
||||||
|
var volume = this.get('store').getById('volume', mount.get('volumeId'));
|
||||||
|
if ( volume )
|
||||||
|
{
|
||||||
|
var mounts = volume.get('mounts');
|
||||||
|
if ( !Ember.isArray(mounts) )
|
||||||
|
{
|
||||||
|
mounts = [];
|
||||||
|
volume.set('mounts',mounts);
|
||||||
|
}
|
||||||
|
|
||||||
|
var existingMount = mounts.filterBy('id', mount.get('id')).get('firstObject');
|
||||||
|
if ( existingMount )
|
||||||
|
{
|
||||||
|
existingMount.setProperties(mount);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
mounts.pushObject(mount);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
registryCredentialChanged: function(change) {
|
||||||
|
this._includeChanged('registry', 'credentials', 'registryId', change.data.resource);
|
||||||
|
},
|
||||||
|
|
||||||
|
loadBalancerServiceChanged: function(change) {
|
||||||
|
this._includeChanged('environment', 'services', 'environmentId', change.data.resource);
|
||||||
|
},
|
||||||
|
|
||||||
|
dnsServiceChanged: function(change) {
|
||||||
|
this._includeChanged('environment', 'services', 'environmentId', change.data.resource);
|
||||||
|
},
|
||||||
|
|
||||||
|
externalServiceChanged: function(change) {
|
||||||
|
this._includeChanged('environment', 'services', 'environmentId', change.data.resource);
|
||||||
|
},
|
||||||
|
|
||||||
|
serviceChanged: function(change) {
|
||||||
|
this._includeChanged('environment', 'services', 'environmentId', change.data.resource);
|
||||||
|
},
|
||||||
|
|
||||||
|
kubernetesServiceChanged: function(change) {
|
||||||
|
this._includeChanged('environment', 'services', 'environmentId', change.data.resource);
|
||||||
|
},
|
||||||
|
|
||||||
|
k8sResourceChanged: function(changeType, obj) {
|
||||||
|
//console.log('k8s change', changeType, (obj && obj.metadata && obj.metadata.uid ? obj.metadata.uid : 'none'));
|
||||||
|
if ( obj && obj.metadata && obj.metadata.uid && this.get('k8sUidBlacklist').indexOf(obj.metadata.uid) >= 0 )
|
||||||
|
{
|
||||||
|
//console.log('^-- Ignoring', changeType, 'for removed resource');
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
var resource = this.get('k8s')._typeify(obj);
|
||||||
|
|
||||||
|
if ( changeType === 'deleted' )
|
||||||
|
{
|
||||||
|
this.get('k8sUidBlacklist').pushObject(obj.metadata.uid);
|
||||||
|
this.get('store')._remove(resource.get('type'), resource);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
_trySend: function(/*arguments*/) {
|
||||||
|
try
|
||||||
|
{
|
||||||
|
this.send.apply(this,arguments);
|
||||||
|
}
|
||||||
|
catch (err)
|
||||||
|
{
|
||||||
|
if ( err instanceof Ember.Error && err.message.indexOf('Nothing handled the action') === 0 )
|
||||||
|
{
|
||||||
|
// Don't care
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
throw err;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
// Update the `?include=`-ed arrays of a host,
|
||||||
|
// e.g. when an instance changes:
|
||||||
|
// Update the destProperty='instances' array on all models of type resourceName='hosts'.
|
||||||
|
// to match the list in the the 'changed' resource's expectedProperty='hosts'
|
||||||
|
// _includeChanged( 'host', 'hosts', 'instances', 'hosts', instance)
|
||||||
|
_includeChanged: function(resourceName, destProperty, expectedProperty, changed) {
|
||||||
|
if (!changed)
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
var changedId = changed.get('id');
|
||||||
|
var store = this.get('store');
|
||||||
|
|
||||||
|
//console.log('Include changed',resourceName,destProperty,expectedProperty,changedId);
|
||||||
|
|
||||||
|
// All the resources
|
||||||
|
var all = store.all(resourceName);
|
||||||
|
|
||||||
|
// IDs the resource should be on
|
||||||
|
var expectedIds = [];
|
||||||
|
var expected = changed.get(expectedProperty)||[];
|
||||||
|
if ( !Ember.isArray(expected) )
|
||||||
|
{
|
||||||
|
expected = [expected];
|
||||||
|
}
|
||||||
|
|
||||||
|
if ( changed.get('state') !== 'purged' )
|
||||||
|
{
|
||||||
|
expectedIds = expected.map(function(item) {
|
||||||
|
if ( typeof item === 'object' )
|
||||||
|
{
|
||||||
|
return item.get('id');
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
return item;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
// IDs it is currently on
|
||||||
|
var curIds = [];
|
||||||
|
all.forEach(function(item) {
|
||||||
|
var existing = (item.get(destProperty)||[]).filterBy('id', changedId);
|
||||||
|
if ( existing.length )
|
||||||
|
{
|
||||||
|
curIds.push(item.get('id'));
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
// Remove from resources the changed shouldn't be on
|
||||||
|
var remove = Util.arrayDiff(curIds, expectedIds);
|
||||||
|
remove.forEach((id) => {
|
||||||
|
//console.log('Remove',id);
|
||||||
|
store.find(resourceName, id).then((item) => {
|
||||||
|
var list = item.get(destProperty);
|
||||||
|
if ( list )
|
||||||
|
{
|
||||||
|
//console.log('Removing',changedId,'from',item.get('id'));
|
||||||
|
list.removeObjects(list.filterBy('id', changedId));
|
||||||
|
}
|
||||||
|
}).catch(() => {});
|
||||||
|
});
|
||||||
|
|
||||||
|
// Add or update resources the changed should be on
|
||||||
|
expectedIds.forEach((id) => {
|
||||||
|
//console.log('Expect',id);
|
||||||
|
store.find(resourceName, id).then((item) => {
|
||||||
|
var list = item.get(destProperty);
|
||||||
|
if ( !list )
|
||||||
|
{
|
||||||
|
list = [];
|
||||||
|
//console.log('Adding empty to',item.get('id'), destProperty);
|
||||||
|
item.set(destProperty, list);
|
||||||
|
}
|
||||||
|
|
||||||
|
var existing = list.filterBy('id', changedId);
|
||||||
|
if ( existing.length === 0)
|
||||||
|
{
|
||||||
|
//console.log('Adding',changedId,'to',item.get('id'), destProperty);
|
||||||
|
list.pushObject(changed);
|
||||||
|
}
|
||||||
|
}).catch(() => {});
|
||||||
|
});
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
@ -1,6 +1,7 @@
|
||||||
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 { normalizeType } from 'ember-api-store/utils/normalize';
|
import { normalizeType } from 'ember-api-store/utils/normalize';
|
||||||
|
import Util from 'ui/utils/util';
|
||||||
|
|
||||||
var K8sResource = Resource.extend({
|
var K8sResource = Resource.extend({
|
||||||
actions: {
|
actions: {
|
||||||
|
|
@ -12,6 +13,25 @@ var K8sResource = Resource.extend({
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
|
|
||||||
|
goToApi: function() {
|
||||||
|
var endpoint = this.get('endpoint.absolute'); // http://e.f.g.h/ , does not include version. e.f.g.h is where the API actually is.
|
||||||
|
var projectId = this.get(`tab-session.${C.TABSESSION.PROJECT}`);
|
||||||
|
var relative = this.linkFor('self').replace(/^\/r\/kubernetes/,'');
|
||||||
|
var url = `${endpoint}r/projects/${projectId}/kubernetes${relative}`;
|
||||||
|
|
||||||
|
// For local development where API doesn't match origin, add basic auth token
|
||||||
|
if ( url.indexOf(window.location.origin) !== 0 )
|
||||||
|
{
|
||||||
|
var token = this.get('cookies').get(C.COOKIE.TOKEN);
|
||||||
|
if ( token )
|
||||||
|
{
|
||||||
|
url = Util.addAuthorization(url, C.USER.BASIC_BEARER, token);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
window.open(url, '_blank');
|
||||||
|
},
|
||||||
},
|
},
|
||||||
|
|
||||||
linkFor: function(name) {
|
linkFor: function(name) {
|
||||||
|
|
@ -70,9 +90,10 @@ var K8sResource = Resource.extend({
|
||||||
|
|
||||||
availableActions: function() {
|
availableActions: function() {
|
||||||
var choices = [
|
var choices = [
|
||||||
{ label: 'Edit', icon: 'icon icon-edit', action: 'edit', enabled: true },
|
{ label: 'Edit', icon: 'icon icon-edit', action: 'edit', enabled: true },
|
||||||
|
{ label: 'View in API', icon: 'icon icon-external-link', action: 'goToApi', enabled: true },
|
||||||
{ divider: true },
|
{ divider: true },
|
||||||
{ label: 'Delete', icon: 'icon icon-trash', action: 'promptDelete', enabled: true, altAction: 'delete', color: 'text-warning' },
|
{ label: 'Delete', icon: 'icon icon-trash', action: 'promptDelete', enabled: true, altAction: 'delete', color: 'text-warning' },
|
||||||
];
|
];
|
||||||
|
|
||||||
return choices;
|
return choices;
|
||||||
|
|
|
||||||
|
|
@ -143,6 +143,7 @@ Router.map(function() {
|
||||||
// Kubernetes
|
// Kubernetes
|
||||||
this.route('k8s-tab', {path: '/kubernetes', resetNamespace: true}, function() {
|
this.route('k8s-tab', {path: '/kubernetes', resetNamespace: true}, function() {
|
||||||
this.route('index', {path: '/'});
|
this.route('index', {path: '/'});
|
||||||
|
this.route('waiting', {path: '/waiting'});
|
||||||
|
|
||||||
this.route('apply', {path: '/apply'});
|
this.route('apply', {path: '/apply'});
|
||||||
this.route('kubectl', {path: '/kubectl'});
|
this.route('kubectl', {path: '/kubectl'});
|
||||||
|
|
|
||||||
|
|
@ -259,7 +259,7 @@ export default Ember.Service.extend({
|
||||||
}
|
}
|
||||||
|
|
||||||
// Un-namespaced things are cacheable
|
// Un-namespaced things are cacheable
|
||||||
var str = `${C.K8S.BASE}/namespaces/`;
|
var str = `${C.K8S.BASE_VERSION}/namespaces/`;
|
||||||
var pos = opt.url.indexOf(str);
|
var pos = opt.url.indexOf(str);
|
||||||
if ( pos >= 0 )
|
if ( pos >= 0 )
|
||||||
{
|
{
|
||||||
|
|
@ -317,7 +317,7 @@ export default Ember.Service.extend({
|
||||||
{
|
{
|
||||||
if ( opt.url.substr(0,1) !== '/' )
|
if ( opt.url.substr(0,1) !== '/' )
|
||||||
{
|
{
|
||||||
opt.url = `${self.get('app.kubernetesEndpoint')}/${C.K8S.BASE}/` + opt.url;
|
opt.url = `${self.get('app.kubernetesEndpoint')}/${C.K8S.BASE_VERSION}/` + opt.url;
|
||||||
}
|
}
|
||||||
|
|
||||||
return findWithUrl(opt.url);
|
return findWithUrl(opt.url);
|
||||||
|
|
@ -424,6 +424,15 @@ export default Ember.Service.extend({
|
||||||
}.property('containers.@each.externalId'),
|
}.property('containers.@each.externalId'),
|
||||||
|
|
||||||
|
|
||||||
|
isReady() {
|
||||||
|
return this.request({
|
||||||
|
url: `${this.get('app.kubernetesEndpoint')}/${C.K8S.BASE}`
|
||||||
|
}).then(() => {
|
||||||
|
return true;
|
||||||
|
}).catch(() => {
|
||||||
|
return Ember.RSVP.resolve(false);
|
||||||
|
});
|
||||||
|
},
|
||||||
|
|
||||||
_getCollection(type, resourceName) {
|
_getCollection(type, resourceName) {
|
||||||
return this._find(`${C.K8S.TYPE_PREFIX}${type}`, null, {
|
return this._find(`${C.K8S.TYPE_PREFIX}${type}`, null, {
|
||||||
|
|
@ -495,7 +504,7 @@ export default Ember.Service.extend({
|
||||||
selectNamespace(desiredName) {
|
selectNamespace(desiredName) {
|
||||||
var self = this;
|
var self = this;
|
||||||
return this.allNamespaces().then((all) => {
|
return this.allNamespaces().then((all) => {
|
||||||
// Asked fror a specific one
|
// Asked for a specific one
|
||||||
var obj = objForName(desiredName);
|
var obj = objForName(desiredName);
|
||||||
if ( obj )
|
if ( obj )
|
||||||
{
|
{
|
||||||
|
|
|
||||||
|
|
@ -37,45 +37,47 @@ export default Ember.Service.extend({
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
|
|
||||||
selectDefault: function() {
|
selectDefault: function(desired) {
|
||||||
var self = this;
|
var self = this;
|
||||||
var tabSession = this.get('tab-session');
|
var tabSession = this.get('tab-session');
|
||||||
|
|
||||||
// Try the project ID in the session
|
// The one specifically asked for
|
||||||
return this._activeProjectFromId(tabSession.get(C.TABSESSION.PROJECT)).then(select)
|
return this._activeProjectFromId(desired).then(select)
|
||||||
.catch(() => {
|
.catch(() => {
|
||||||
// Then the default project ID from the prefs
|
// Try the project ID in the session
|
||||||
return this._activeProjectFromId(this.get('prefs').get(C.PREFS.PROJECT_DEFAULT)).then(select)
|
return this._activeProjectFromId(tabSession.get(C.TABSESSION.PROJECT)).then(select)
|
||||||
.catch(() => {
|
.catch(() => {
|
||||||
// Setting this now and then just below breaks API uniqueness checking
|
// Then the default project ID from the prefs
|
||||||
// this.get('prefs').set(C.PREFS.PROJECT_DEFAULT, "");
|
return this._activeProjectFromId(this.get('prefs').get(C.PREFS.PROJECT_DEFAULT)).then(select)
|
||||||
|
.catch(() => {
|
||||||
// Then the first active project
|
// Then the first active project you're a member of
|
||||||
var project = this.get('active.firstObject');
|
var project = this.get('active.firstObject');
|
||||||
if ( project )
|
if ( project )
|
||||||
{
|
{
|
||||||
return select(project, true);
|
return select(project, true);
|
||||||
}
|
}
|
||||||
else if ( this.get('access.admin') )
|
else if ( this.get('access.admin') )
|
||||||
{
|
{
|
||||||
return this.getAll().then((all) => {
|
// Then if you're an admin the first active of any kind
|
||||||
var firstActive = all.filterBy('state','active')[0];
|
return this.getAll().then((all) => {
|
||||||
if ( firstActive )
|
var firstActive = all.filterBy('state','active')[0];
|
||||||
{
|
if ( firstActive )
|
||||||
return select(firstActive, true);
|
{
|
||||||
}
|
return select(firstActive, true);
|
||||||
else
|
}
|
||||||
{
|
else
|
||||||
|
{
|
||||||
|
return fail();
|
||||||
|
}
|
||||||
|
}).catch(() => {
|
||||||
return fail();
|
return fail();
|
||||||
}
|
});
|
||||||
}).catch(() => {
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
return fail();
|
return fail();
|
||||||
});
|
}
|
||||||
}
|
});
|
||||||
else
|
|
||||||
{
|
|
||||||
return fail();
|
|
||||||
}
|
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
@ -109,19 +111,7 @@ export default Ember.Service.extend({
|
||||||
|
|
||||||
setCurrent: function(project) {
|
setCurrent: function(project) {
|
||||||
this.set('current', project);
|
this.set('current', project);
|
||||||
|
return Ember.RSVP.resolve(project);
|
||||||
if ( project && project.get('kubernetes') )
|
|
||||||
{
|
|
||||||
return this.get('k8s').allNamespaces().then(() => {
|
|
||||||
return this.get('k8s').selectNamespace().then(() => {
|
|
||||||
return project;
|
|
||||||
});
|
|
||||||
});
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
return project;
|
|
||||||
}
|
|
||||||
},
|
},
|
||||||
|
|
||||||
_activeProjectFromId: function(projectId) {
|
_activeProjectFromId: function(projectId) {
|
||||||
|
|
@ -129,9 +119,10 @@ export default Ember.Service.extend({
|
||||||
if ( !projectId )
|
if ( !projectId )
|
||||||
{
|
{
|
||||||
reject();
|
reject();
|
||||||
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
this.get('store').find('project', projectId).then((project) => {
|
this.get('store').find('project', projectId, {url: 'projects/'+encodeURIComponent(projectId)}).then((project) => {
|
||||||
if ( project.get('state') === 'active' )
|
if ( project.get('state') === 'active' )
|
||||||
{
|
{
|
||||||
resolve(project);
|
resolve(project);
|
||||||
|
|
|
||||||
|
|
@ -66,7 +66,7 @@ export default Ember.Service.extend(Ember.Evented, {
|
||||||
obj.save().then(() => {
|
obj.save().then(() => {
|
||||||
this.notifyPropertyChange(normalizeName(key));
|
this.notifyPropertyChange(normalizeName(key));
|
||||||
}).catch((err) => {
|
}).catch((err) => {
|
||||||
this.trigger('gotError', err);
|
console.log('Error saving setting:', err);
|
||||||
}).finally(() => {
|
}).finally(() => {
|
||||||
this.decrementProperty('promiseCount');
|
this.decrementProperty('promiseCount');
|
||||||
});
|
});
|
||||||
|
|
|
||||||
|
|
@ -7,8 +7,14 @@ export default Ember.Controller.extend({
|
||||||
queryParams: ['editing'],
|
queryParams: ['editing'],
|
||||||
|
|
||||||
actions: {
|
actions: {
|
||||||
|
done() {
|
||||||
|
this.transitionTo('settings.projects').then(() => {
|
||||||
|
this.send('refreshKubernetes');
|
||||||
|
});
|
||||||
|
},
|
||||||
|
|
||||||
cancel() {
|
cancel() {
|
||||||
this.transitionTo('settings.projects.detail', this.get('model.project.id'), {queryParams: {editing: false}});
|
this.transitionTo('settings.projects');
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|
|
||||||
|
|
@ -2,5 +2,6 @@
|
||||||
model=model
|
model=model
|
||||||
showEdit=editing
|
showEdit=editing
|
||||||
editing=true
|
editing=true
|
||||||
|
done=(action "done")
|
||||||
cancel=(action "cancel")
|
cancel=(action "cancel")
|
||||||
}}
|
}}
|
||||||
|
|
|
||||||
|
|
@ -2,6 +2,11 @@ import Ember from 'ember';
|
||||||
|
|
||||||
export default Ember.Controller.extend({
|
export default Ember.Controller.extend({
|
||||||
actions: {
|
actions: {
|
||||||
|
done() {
|
||||||
|
this.send('refreshKubernetes');
|
||||||
|
this.send('goToPrevious');
|
||||||
|
},
|
||||||
|
|
||||||
cancel() {
|
cancel() {
|
||||||
this.send('goToPrevious');
|
this.send('goToPrevious');
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -2,5 +2,6 @@
|
||||||
model=model
|
model=model
|
||||||
showEdit=true
|
showEdit=true
|
||||||
editing=false
|
editing=false
|
||||||
|
done=(action "done")
|
||||||
cancel=(action "cancel")
|
cancel=(action "cancel")
|
||||||
}}
|
}}
|
||||||
|
|
|
||||||
|
|
@ -5,7 +5,7 @@
|
||||||
<p>
|
<p>
|
||||||
Before adding your first service or launching a container, you must add at least a single Linux host that supports Docker 1.9.1+ and be able to reach the {{settings.appName}} server via HTTP.
|
Before adding your first service or launching a container, you must add at least a single Linux host that supports Docker 1.9.1+ and be able to reach the {{settings.appName}} server via HTTP.
|
||||||
{{settings.appName}} supports adding Linux hosts in the form of a virtual or physical machine from any public cloud providers, privately hosted clouds, or even bare metal servers.
|
{{settings.appName}} supports adding Linux hosts in the form of a virtual or physical machine from any public cloud providers, privately hosted clouds, or even bare metal servers.
|
||||||
{{#unless settings.isPrivateLabel}}<a href="{[docsBase}}/rancher-ui/infrastructure/hosts/" target="_blank">Learn More</a>{{/unless}}
|
{{#unless settings.isPrivateLabel}}<a href="{{docsBase}}/rancher-ui/infrastructure/hosts/" target="_blank">Learn More</a>{{/unless}}
|
||||||
</p>
|
</p>
|
||||||
{{#link-to "hosts.new" class="btn btn-primary"}}Add Host{{/link-to}}
|
{{#link-to "hosts.new" class="btn btn-primary"}}Add Host{{/link-to}}
|
||||||
</section>
|
</section>
|
||||||
|
|
@ -19,7 +19,7 @@
|
||||||
A service is simply a group of containers created from the same Docker image but extends Docker's "link" concept to leverage {{settings.appName}}'s lightweight distributed DNS service for service discovery.
|
A service is simply a group of containers created from the same Docker image but extends Docker's "link" concept to leverage {{settings.appName}}'s lightweight distributed DNS service for service discovery.
|
||||||
Services can be added individually or by deploying an item from the Catalog.</p>
|
Services can be added individually or by deploying an item from the Catalog.</p>
|
||||||
<p>A service is also capable of leveraging other {{settings.appName}} built-in services such as load balancers, health monitoring, upgrade support, and high-availability.
|
<p>A service is also capable of leveraging other {{settings.appName}} built-in services such as load balancers, health monitoring, upgrade support, and high-availability.
|
||||||
{{#unless settings.isPrivateLabel}}<a href="{[docsBase}}/rancher-ui/applications/stacks/adding-services/" target="_blank">Learn More</a>{{/unless}}
|
{{#unless settings.isPrivateLabel}}<a href="{{docsBase}}/rancher-ui/applications/stacks/adding-services/" target="_blank">Learn More</a>{{/unless}}
|
||||||
</p>
|
</p>
|
||||||
<a class="btn btn-primary" {{action "newService"}}>Add Service</a>
|
<a class="btn btn-primary" {{action "newService"}}>Add Service</a>
|
||||||
{{#link-to "applications-tab.catalog" class="btn btn-primary"}}Add From Catalog{{/link-to}}
|
{{#link-to "applications-tab.catalog" class="btn btn-primary"}}Add From Catalog{{/link-to}}
|
||||||
|
|
|
||||||
|
|
@ -8,6 +8,6 @@
|
||||||
}
|
}
|
||||||
|
|
||||||
&.rancher {
|
&.rancher {
|
||||||
background-image: url('images/logos/provider-orchestration.svg');
|
background-image: url('images/logos/provider-clustering.svg');
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,9 +1,9 @@
|
||||||
{{#if hasKubernetes}}
|
{{#if hasKubernetes}}
|
||||||
{{#link-to "environments" (query-params which="not-kubernetes")}}<i class="icon icon-layers"></i>Stacks{{/link-to}}
|
{{#link-to "environments" project.id (query-params which="not-kubernetes")}}<i class="icon icon-layers"></i>Stacks{{/link-to}}
|
||||||
{{else}}
|
{{else}}
|
||||||
{{#link-to "environments" (query-params which="user")}}<i class="icon icon-layers"></i> Stacks{{/link-to}}
|
{{#link-to "environments" project.id (query-params which="user")}}<i class="icon icon-layers"></i> Stacks{{/link-to}}
|
||||||
{{#if hasSystem}}
|
{{#if hasSystem}}
|
||||||
{{#link-to "environments" (query-params which="system")}}<i class="icon icon-network"></i>System{{/link-to}}
|
{{#link-to "environments" project.id (query-params which="system")}}<i class="icon icon-network"></i>System{{/link-to}}
|
||||||
{{/if}}
|
{{/if}}
|
||||||
{{/if}}
|
{{/if}}
|
||||||
{{#link-to "applications-tab.catalog"}}<i class="icon icon-catalog"></i> Catalog{{/link-to}}
|
{{#link-to "applications-tab.catalog" project.id}}<i class="icon icon-catalog"></i> Catalog{{/link-to}}
|
||||||
|
|
|
||||||
|
|
@ -1,8 +1,8 @@
|
||||||
{{#link-to "hosts"}}<i class="icon icon-host"></i>Hosts{{/link-to}}
|
{{#link-to "hosts" project.id}}<i class="icon icon-host"></i>Hosts{{/link-to}}
|
||||||
{{#link-to "containers"}}<i class="icon icon-box"></i>Containers{{/link-to}}
|
{{#link-to "containers" project.id}}<i class="icon icon-box"></i>Containers{{/link-to}}
|
||||||
{{#if hasVm}}
|
{{#if hasVm}}
|
||||||
{{#link-to "virtualmachines"}}<i class="icon icon-vm"></i>Virtual Machines{{/link-to}}
|
{{#link-to "virtualmachines" project.id}}<i class="icon icon-vm"></i>Virtual Machines{{/link-to}}
|
||||||
{{/if}}
|
{{/if}}
|
||||||
{{#link-to "storagepools"}}<i class="icon icon-hdd"></i>Storage Pools{{/link-to}}
|
{{#link-to "storagepools" project.id}}<i class="icon icon-hdd"></i>Storage Pools{{/link-to}}
|
||||||
{{#link-to "certificates"}}<i class="icon icon-certificate"></i>Certificates{{/link-to}}
|
{{#link-to "certificates" project.id}}<i class="icon icon-certificate"></i>Certificates{{/link-to}}
|
||||||
{{#link-to "registries" role="menuitem" tabindex="-1"}}<i class="icon icon-database"></i>Registries{{/link-to}}
|
{{#link-to "registries" project.id}}<i class="icon icon-database"></i>Registries{{/link-to}}
|
||||||
|
|
|
||||||
|
|
@ -1,4 +1,6 @@
|
||||||
{{#link-to "k8s-tab.namespace.services" namespace.id}}<i class="icon icon-compass"></i>Services{{/link-to}}
|
{{#if namespace.id}}
|
||||||
{{#link-to "k8s-tab.namespace.rcs" namespace.id}}<i class="icon icon-tachometer"></i>RCs{{/link-to}}
|
{{#link-to "k8s-tab.namespace.services" project.id namespace.id}}<i class="icon icon-compass"></i>Services{{/link-to}}
|
||||||
{{#link-to "k8s-tab.namespace.pods" namespace.id}}<i class="icon icon-containers"></i>Pods{{/link-to}}
|
{{#link-to "k8s-tab.namespace.rcs" project.id namespace.id}}<i class="icon icon-tachometer"></i>RCs{{/link-to}}
|
||||||
{{#link-to "k8s-tab.kubectl"}}<i class="icon icon-terminal"></i>kubectl{{/link-to}}
|
{{#link-to "k8s-tab.namespace.pods" project.id namespace.id}}<i class="icon icon-containers"></i>Pods{{/link-to}}
|
||||||
|
{{#link-to "k8s-tab.kubectl" project.id}}<i class="icon icon-terminal"></i>kubectl{{/link-to}}
|
||||||
|
{{/if}}
|
||||||
|
|
|
||||||
|
|
@ -200,7 +200,8 @@ var C = {
|
||||||
},
|
},
|
||||||
|
|
||||||
K8S: {
|
K8S: {
|
||||||
BASE: 'api/v1',
|
BASE: 'api',
|
||||||
|
BASE_VERSION: 'api/v1',
|
||||||
TYPE_PREFIX: 'k8s-',
|
TYPE_PREFIX: 'k8s-',
|
||||||
ID_SEPARATOR: '::'
|
ID_SEPARATOR: '::'
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -63,9 +63,7 @@ module.exports = function(environment) {
|
||||||
apiEndpoint: '/v1',
|
apiEndpoint: '/v1',
|
||||||
catalogServer: '',
|
catalogServer: '',
|
||||||
catalogEndpoint: '/v1-catalog',
|
catalogEndpoint: '/v1-catalog',
|
||||||
kubernetesServer: '',
|
|
||||||
kubernetesEndpoint: '/r/kubernetes',
|
kubernetesEndpoint: '/r/kubernetes',
|
||||||
kubectlServer: '',
|
|
||||||
kubectlEndpoint: '/r/kubectld:8091/v1-kubectl',
|
kubectlEndpoint: '/r/kubectld:8091/v1-kubectl',
|
||||||
proxyEndpoint: '/v1/proxy',
|
proxyEndpoint: '/v1/proxy',
|
||||||
wsEndpoint: '/v1/subscribe' +
|
wsEndpoint: '/v1/subscribe' +
|
||||||
|
|
@ -138,28 +136,6 @@ module.exports = function(environment) {
|
||||||
ENV.APP.catalogServer = '';
|
ENV.APP.catalogServer = '';
|
||||||
}
|
}
|
||||||
|
|
||||||
// Override the K8s server/endpoint with environment var
|
|
||||||
server = process.env.KUBERNETES;
|
|
||||||
if ( server )
|
|
||||||
{
|
|
||||||
ENV.APP.kubernetesServer = normalizeHost(server,8090);
|
|
||||||
}
|
|
||||||
else if (environment === 'production')
|
|
||||||
{
|
|
||||||
ENV.APP.kubernetesServer = '';
|
|
||||||
}
|
|
||||||
|
|
||||||
// Override the Kubectl server/endpoint with environment var
|
|
||||||
server = process.env.KUBECTL;
|
|
||||||
if ( server )
|
|
||||||
{
|
|
||||||
ENV.APP.kubectlServer = normalizeHost(server,8091);
|
|
||||||
}
|
|
||||||
else if (environment === 'production')
|
|
||||||
{
|
|
||||||
ENV.APP.kubectlServer = '';
|
|
||||||
}
|
|
||||||
|
|
||||||
var pl = process.env.PL;
|
var pl = process.env.PL;
|
||||||
if ( pl )
|
if ( pl )
|
||||||
{
|
{
|
||||||
|
|
|
||||||
|
|
@ -1,15 +1,33 @@
|
||||||
This folder contians all the images that have the logo in them. You can replace them by running rancher/server (>= v0.44) with:
|
# Private Labeling #
|
||||||
|
|
||||||
|
Setting `/v1/settings/ui.pl` to a value other than `rancher` will (on rancher/server >= v0.61):
|
||||||
|
- Change (most) places that say "Rancher" in the UI to the value you provide.
|
||||||
|
- This does not include things like "rancher-compose.yml" that are expected to have a certain name.
|
||||||
|
- Disable the footer and help links to Rancher Labs-specific docs, forums, and repos.
|
||||||
|
- Change the loading screen to a generic spinner.
|
||||||
|
- Remove the animations/images to make the error screen generic.
|
||||||
|
|
||||||
|
You can set this manually/through the API after installation, or by setting the `CATTLE_UI.PL` environment
|
||||||
|
variable when running the `rancher/server` container.
|
||||||
|
|
||||||
|
# Images #
|
||||||
|
This folder contains all the images that have the Rancher-specific content in them.
|
||||||
|
You can replace them by running the `rancher/server` container (>= v0.44) with:
|
||||||
|
|
||||||
```-v /path/to/your/logos:/usr/share/cattle/war/assets/images/logos```
|
```-v /path/to/your/logos:/usr/share/cattle/war/assets/images/logos```
|
||||||
|
|
||||||
This replaces the entire folder, so you must supply all the files or they will be missing.
|
This replaces the entire folder, so you must supply all the files or they will be missing.
|
||||||
|
|
||||||
| File | Usage |
|
| File | Usage |
|
||||||
|:--------------------|:----------------------------------------------------------|
|
|:------------------------|:----------------------------------------------------------|
|
||||||
| dark.svg | On the Login screen when access control is enabled |
|
| dark.svg | On the Login screen when access control is enabled |
|
||||||
| graphic.svg | The parachuting cow part of the image on the About screen |
|
| fail-*.svg | On the branded error screen (for ui.pl="rancher" only) |
|
||||||
| login-bg.jpg | Background for the login screen box |
|
| graphic.svg | The parachuting cow part of the image on the About screen |
|
||||||
| main.svg | Top-left corner of the main header |
|
| login-bg.jpg | Background for the login screen box |
|
||||||
| provider-custom.svg | Custom "Add Host" provider |
|
| main.svg | Top-left corner of the main header |
|
||||||
| provider-local.svg | Local "Access Control" provider |
|
| main-loading.svg | On the branded loading screen (for ui.pl="rancher" only) |
|
||||||
| text.svg | The text part of hte image on the About screen |
|
| main_k8s.svg | Top-left corner of the main header, for k8s tab |
|
||||||
|
| provider-custom.svg | Custom "Add Host" provider |
|
||||||
|
| provider-local.svg | Local "Access Control" provider |
|
||||||
|
| provider-clustering.svg | "Cattle" environment clustering provider |
|
||||||
|
| text.svg | The text part of the image on the About screen |
|
||||||
|
|
|
||||||
|
Before Width: | Height: | Size: 772 B After Width: | Height: | Size: 772 B |
|
Before Width: | Height: | Size: 771 B After Width: | Height: | Size: 771 B |
|
Before Width: | Height: | Size: 744 B After Width: | Height: | Size: 744 B |
|
Before Width: | Height: | Size: 772 B After Width: | Height: | Size: 772 B |
|
Before Width: | Height: | Size: 766 B After Width: | Height: | Size: 766 B |
|
Before Width: | Height: | Size: 3.4 KiB After Width: | Height: | Size: 3.4 KiB |
|
Before Width: | Height: | Size: 3.4 KiB After Width: | Height: | Size: 3.4 KiB |
|
Before Width: | Height: | Size: 1.2 KiB After Width: | Height: | Size: 1.2 KiB |
|
Before Width: | Height: | Size: 3.0 KiB After Width: | Height: | Size: 3.0 KiB |
|
Before Width: | Height: | Size: 7.7 KiB After Width: | Height: | Size: 7.7 KiB |
|
|
@ -75,49 +75,7 @@ module.exports = function(app, options) {
|
||||||
console.log('Catalog Proxy', req.method, 'to', req.url);
|
console.log('Catalog Proxy', req.method, 'to', req.url);
|
||||||
catalogProxy.web(req, res);
|
catalogProxy.web(req, res);
|
||||||
});
|
});
|
||||||
|
}
|
||||||
// Kubernetes API
|
|
||||||
var kubernetesPath = config.kubernetesEndpoint;
|
|
||||||
// Default catalog to the regular API
|
|
||||||
var kubernetesServer = config.kubernetesServer || config.apiServer;
|
|
||||||
console.log('Proxying Kubernetes to', kubernetesServer);
|
|
||||||
app.use(kubernetesPath, function(req, res, next) {
|
|
||||||
req.headers['X-Forwarded-Proto'] = req.protocol;
|
|
||||||
var kubernetesProxy = HttpProxy.createProxyServer({
|
|
||||||
xfwd: false,
|
|
||||||
target: kubernetesServer
|
|
||||||
});
|
|
||||||
|
|
||||||
kubernetesProxy.on('error', onProxyError);
|
|
||||||
|
|
||||||
// Don't include root path in proxied request
|
|
||||||
// req.url = path.join(kubernetesPath, req.url);
|
|
||||||
|
|
||||||
console.log('Kubernetes Proxy', req.method, 'to', req.url);
|
|
||||||
kubernetesProxy.web(req, res);
|
|
||||||
});
|
|
||||||
|
|
||||||
// Kubectl API
|
|
||||||
var kubectlPath = config.kubectlEndpoint;
|
|
||||||
// Default catalog to the regular API
|
|
||||||
var kubectlServer = config.kubectlServer || config.kubernetesServer || config.apiServer;
|
|
||||||
console.log('Proxying Kubectl to', kubectlServer);
|
|
||||||
app.use(kubectlPath, function(req, res, next) {
|
|
||||||
req.headers['X-Forwarded-Proto'] = req.protocol;
|
|
||||||
var kubectlProxy = HttpProxy.createProxyServer({
|
|
||||||
xfwd: false,
|
|
||||||
target: kubectlServer
|
|
||||||
});
|
|
||||||
|
|
||||||
kubectlProxy.on('error', onProxyError);
|
|
||||||
|
|
||||||
// include root path in proxied request
|
|
||||||
req.url = path.join(kubectlPath, req.url);
|
|
||||||
|
|
||||||
console.log('Kubectl Proxy', req.method, 'to', req.url);
|
|
||||||
kubectlProxy.web(req, res);
|
|
||||||
});
|
|
||||||
};
|
|
||||||
|
|
||||||
function onProxyError(err, req, res) {
|
function onProxyError(err, req, res) {
|
||||||
console.log('Proxy Error: on', req.method,'to', req.url,':', err);
|
console.log('Proxy Error: on', req.method,'to', req.url,':', err);
|
||||||
|
|
|
||||||