Merge pull request #495 from vincent99/master

K8s, PL fixes, Subscription fixes
This commit is contained in:
Vincent Fiduccia 2016-03-02 01:28:17 -07:00
commit 6028533916
46 changed files with 773 additions and 580 deletions

View File

@ -3,6 +3,7 @@ import C from 'ui/utils/constants';
export default Ember.Controller.extend({
settings: Ember.inject.service(),
projects: Ember.inject.service(),
queryParams: ['backToAdd'],
backToAdd: false,
@ -30,7 +31,6 @@ export default Ember.Controller.extend({
}
if (this.get('backToAdd')) {
propsOut[C.SETTING.API_HOST] = model.host;
} else {
@ -59,7 +59,7 @@ export default Ember.Controller.extend({
if (this.get('backToAdd')) {
this.transitionToRoute('hosts.new');
this.transitionToRoute('hosts.new', this.get('projects.current.id'));
} else {
this.send('goToPrevious');

View File

@ -5,6 +5,7 @@ export default Ember.Route.extend({
cookies: Ember.inject.service(),
github: Ember.inject.service(),
access: Ember.inject.service(),
settings: Ember.inject.service(),
previousParams: null,
previousRoute: null,
@ -163,7 +164,13 @@ export default Ember.Route.extend({
}
},
updateWindowTitle: function() {
document.title = this.get('settings.appName');
}.observes('settings.appName'),
beforeModel() {
this.updateWindowTitle();
var agent = window.navigator.userAgent.toLowerCase();
if ( agent.indexOf('msie ') >= 0 || agent.indexOf('trident/') >= 0 || agent.indexOf('edge/') >= 0 )
{

View File

@ -18,7 +18,7 @@
<div class="loadfield">
<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>
{{/if}}

View File

@ -11,4 +11,13 @@ export default Ember.Controller.extend({
hasKubernetes: false,
hasSystem: false,
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'),
});

View File

@ -4,14 +4,11 @@ export default Ember.Route.extend({
projects: Ember.inject.service(),
redirect() {
this.get('projects').selectDefault().then((project) => {
var project = this.get('projects.current');
if ( project ) {
this.replaceWith('authenticated.project', project.get('id'));
} else {
this.replaceWith('settings.projects');
}
}).catch(() => {
this.replaceWith('settings.projects');
});
},
});

View File

@ -1,41 +1,23 @@
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';
export default Ember.Route.extend({
k8s : Ember.inject.service(),
prefs : Ember.inject.service(),
projects : Ember.inject.service(),
access : Ember.inject.service(),
userTheme : Ember.inject.service('user-theme'),
socket : null,
pingTimer : null,
k8sUidBlacklist: null,
model(params, transition) {
if ( !params.project_id )
{
// If there isn't a project, pick one
return this.get('projects').selectDefault().then((project) => {
if ( project )
{
this.replaceWith('authenticated.project', project.get('id'));
}
else
var project = this.modelFor('authenticated').project;
if ( !project )
{
this.replaceWith('settings.projects');
}
}).catch(() => {
this.replaceWith('settings.projects');
});
return;
}
return this.loadProject(params.project_id).then((project) => {
this.set(`tab-session.${C.TABSESSION.PROJECT}`, project.get('id'));
this.get('projects').setCurrent(project);
// If the project ID in the URL is out of sync somehow, bail
if ( project.get('id') !== params.project_id )
{
this.replaceWith('authenticated');
return;
}
return this.loadSchemas().then(() => {
return this.loadStacks().then((stacks) => {
@ -46,7 +28,6 @@ export default Ember.Route.extend({
stacks: stacks,
});
});
});
}).catch((err) => {
return this.loadingError(err, transition, null);
});
@ -65,10 +46,6 @@ export default Ember.Route.extend({
return ret;
},
loadProject(id) {
return this.get('store').find('project', id);
},
loadSchemas() {
var store = this.get('store');
store.resetType('schema');
@ -79,345 +56,4 @@ export default Ember.Route.extend({
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(() => {});
});
},
});

View File

@ -1,8 +1,9 @@
import Ember from 'ember';
import C from 'ui/utils/constants';
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(),
projects : Ember.inject.service(),
k8s : Ember.inject.service(),
@ -29,26 +30,35 @@ export default Ember.Route.extend({
preferences: this.loadPreferences(),
settings: this.loadPublicSettings(),
}).then((hash) => {
var projectId = null;
if ( transition.params && transition.params['authenticated.project'] && transition.params['authenticated.project'].project_id )
{
return hash;
projectId = transition.params['authenticated.project'].project_id;
}
else
{
// If not going to a project-specific page, make sure a project is selected
return this.get('projects').selectDefault().then(() => {
return hash;
// Make sure a valid project is selected
return this.get('projects').selectDefault(projectId).then((project) => {
hash.project = project;
return this.loadKubernetes(project, hash).then((out) => {
return Ember.Object.create(out);
});
});
}
}).catch((err) => {
return this.loadingError(err, transition, Ember.Object.create({
projects: [],
project: null,
}));
});
},
activate() {
this._super();
this.connectSubscribe();
},
deactivate() {
this._super();
this.disconnectSubscribe();
// Forget all the things
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() {
var svc = this.get('projects');
return svc.getAll().then((all) => {
@ -126,6 +166,13 @@ export default Ember.Route.extend({
this.refresh();
},
refreshKubernetes() {
var model = this.get('controller.model');
this.loadKubernetes(model.get('project')).then((hash) => {
model.setProperties(hash);
});
},
switchNamespace(namespaceId) {
var route = window.lc('application').get('currentRouteName');
var okRoutes = [

View File

@ -136,11 +136,13 @@ export default Ember.Component.extend({
var requestLine='';
if ( path )
{
requestLine = method + ' ' + path + ' ' + version;
requestLine = method + ' ' + path + ' "' + version;
if ( host )
{
requestLine += '\r\nHost:\\ ' + host;
requestLine += '\r\nHost: ' + host;
}
requestLine += '"';
}
this.set('healthCheck.requestLine', requestLine);
}

View File

@ -119,7 +119,7 @@
</div>
{{/if}}
{{#if isKubernetesTab}}
{{#if (and isKubernetesTab k8s.namespace)}}
<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">
<i class="icon icon-thumbnails icon-fw"></i>&nbsp;{{k8s.namespace.displayName}}

View File

@ -168,7 +168,7 @@ export default Ember.Component.extend(NewOrEdit, Sortable, {
doneSaving: function() {
var out = this._super();
this.get('projects').refreshAll();
this.get('router').transitionTo('settings.projects.detail', this.get('project.id'), {queryParams: {editing: false}});
this.sendAction('done');
return out;
},
});

View File

@ -56,17 +56,10 @@
<div class="{{if showEdit 'col-sm-12 col-md-8' 'col-xs-12'}}">
<div class="well">
{{#if showEdit}}
{{#if (and accessEnabled showEdit)}}
<div class="row form-group">
<div class="col-sm-12">
{{#if accessEnabled}}
{{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>
{{/if}}
@ -118,6 +111,11 @@
{{/each}}
</tbody>
</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}}
</div>
</div>

View File

@ -14,16 +14,16 @@
{{#unless settings.isPrivateLabel}}
<div class="failplane">
<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 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" />
</div>
<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_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_cloud4.svg" width="300" height="142" />
<img class="cloud" src="{{app.baseAssets}}assets/images/logos/fail_cloud5.svg" width="320" height="152" />
<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-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-cloud4.svg" width="300" height="142" />
<img class="cloud" src="{{app.baseAssets}}assets/images/logos/fail-cloud5.svg" width="320" height="152" />
<div class="wave"></div>
{{/unless}}

View File

@ -4,7 +4,7 @@
<meta charset="utf-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<link rel="icon" href="{{content-for 'baseAssets'}}favicon.ico">
<title>{{content-for 'appName'}}</title>
<title>UI</title>
<!-- {{content-for 'version'}} -->
<meta name="description" content="">
<meta name="viewport" content="width=694">

View File

@ -3,9 +3,17 @@ import Ember from 'ember';
export default Ember.Route.extend({
k8s: Ember.inject.service(),
redirect() {
if ( !this.modelFor('authenticated').kubernetesReady )
{
this.transitionTo('k8s-tab.waiting');
}
},
model() {
var k8s = this.get('k8s');
if ( this.modelFor('authenticated').kubernetesReady )
{
return Ember.RSVP.hash({
namespaces: k8s.allNamespaces(),
services: k8s.allServices(),
@ -17,5 +25,6 @@ export default Ember.Route.extend({
k8s.setProperties(hash);
});
});
}
},
});

View File

@ -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'));
}
});

View File

@ -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'),
});
},
});

View File

@ -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}}

344
app/mixins/subscribe.js Normal file
View File

@ -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(() => {});
});
},
});

View File

@ -1,6 +1,7 @@
import Resource from 'ember-api-store/models/resource';
import C from 'ui/utils/constants';
import { normalizeType } from 'ember-api-store/utils/normalize';
import Util from 'ui/utils/util';
var K8sResource = Resource.extend({
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) {
@ -71,6 +91,7 @@ var K8sResource = Resource.extend({
availableActions: function() {
var choices = [
{ 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 },
{ label: 'Delete', icon: 'icon icon-trash', action: 'promptDelete', enabled: true, altAction: 'delete', color: 'text-warning' },
];

View File

@ -143,6 +143,7 @@ Router.map(function() {
// Kubernetes
this.route('k8s-tab', {path: '/kubernetes', resetNamespace: true}, function() {
this.route('index', {path: '/'});
this.route('waiting', {path: '/waiting'});
this.route('apply', {path: '/apply'});
this.route('kubectl', {path: '/kubectl'});

View File

@ -259,7 +259,7 @@ export default Ember.Service.extend({
}
// Un-namespaced things are cacheable
var str = `${C.K8S.BASE}/namespaces/`;
var str = `${C.K8S.BASE_VERSION}/namespaces/`;
var pos = opt.url.indexOf(str);
if ( pos >= 0 )
{
@ -317,7 +317,7 @@ export default Ember.Service.extend({
{
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);
@ -424,6 +424,15 @@ export default Ember.Service.extend({
}.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) {
return this._find(`${C.K8S.TYPE_PREFIX}${type}`, null, {
@ -495,7 +504,7 @@ export default Ember.Service.extend({
selectNamespace(desiredName) {
var self = this;
return this.allNamespaces().then((all) => {
// Asked fror a specific one
// Asked for a specific one
var obj = objForName(desiredName);
if ( obj )
{

View File

@ -37,20 +37,20 @@ export default Ember.Service.extend({
});
},
selectDefault: function() {
selectDefault: function(desired) {
var self = this;
var tabSession = this.get('tab-session');
// The one specifically asked for
return this._activeProjectFromId(desired).then(select)
.catch(() => {
// Try the project ID in the session
return this._activeProjectFromId(tabSession.get(C.TABSESSION.PROJECT)).then(select)
.catch(() => {
// Then the default project ID from the prefs
return this._activeProjectFromId(this.get('prefs').get(C.PREFS.PROJECT_DEFAULT)).then(select)
.catch(() => {
// Setting this now and then just below breaks API uniqueness checking
// this.get('prefs').set(C.PREFS.PROJECT_DEFAULT, "");
// Then the first active project
// Then the first active project you're a member of
var project = this.get('active.firstObject');
if ( project )
{
@ -58,6 +58,7 @@ export default Ember.Service.extend({
}
else if ( this.get('access.admin') )
{
// Then if you're an admin the first active of any kind
return this.getAll().then((all) => {
var firstActive = all.filterBy('state','active')[0];
if ( firstActive )
@ -78,6 +79,7 @@ export default Ember.Service.extend({
}
});
});
});
function select(project, overwriteDefault) {
if ( project )
@ -109,19 +111,7 @@ export default Ember.Service.extend({
setCurrent: function(project) {
this.set('current', project);
if ( project && project.get('kubernetes') )
{
return this.get('k8s').allNamespaces().then(() => {
return this.get('k8s').selectNamespace().then(() => {
return project;
});
});
}
else
{
return project;
}
return Ember.RSVP.resolve(project);
},
_activeProjectFromId: function(projectId) {
@ -129,9 +119,10 @@ export default Ember.Service.extend({
if ( !projectId )
{
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' )
{
resolve(project);

View File

@ -66,7 +66,7 @@ export default Ember.Service.extend(Ember.Evented, {
obj.save().then(() => {
this.notifyPropertyChange(normalizeName(key));
}).catch((err) => {
this.trigger('gotError', err);
console.log('Error saving setting:', err);
}).finally(() => {
this.decrementProperty('promiseCount');
});

View File

@ -7,8 +7,14 @@ export default Ember.Controller.extend({
queryParams: ['editing'],
actions: {
done() {
this.transitionTo('settings.projects').then(() => {
this.send('refreshKubernetes');
});
},
cancel() {
this.transitionTo('settings.projects.detail', this.get('model.project.id'), {queryParams: {editing: false}});
this.transitionTo('settings.projects');
},
},
});

View File

@ -2,5 +2,6 @@
model=model
showEdit=editing
editing=true
done=(action "done")
cancel=(action "cancel")
}}

View File

@ -2,6 +2,11 @@ import Ember from 'ember';
export default Ember.Controller.extend({
actions: {
done() {
this.send('refreshKubernetes');
this.send('goToPrevious');
},
cancel() {
this.send('goToPrevious');
}

View File

@ -2,5 +2,6 @@
model=model
showEdit=true
editing=false
done=(action "done")
cancel=(action "cancel")
}}

View File

@ -5,7 +5,7 @@
<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.
{{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>
{{#link-to "hosts.new" class="btn btn-primary"}}Add Host{{/link-to}}
</section>
@ -19,7 +19,7 @@
A service is simply a group of containers created from the same Docker image but extends Docker's &quot;link&quot; 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>
<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>
<a class="btn btn-primary" {{action "newService"}}>Add Service</a>
{{#link-to "applications-tab.catalog" class="btn btn-primary"}}Add From Catalog{{/link-to}}

View File

@ -8,6 +8,6 @@
}
&.rancher {
background-image: url('images/logos/provider-orchestration.svg');
background-image: url('images/logos/provider-clustering.svg');
}
}

View File

@ -1,9 +1,9 @@
{{#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}}
{{#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}}
{{#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}}
{{#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}}

View File

@ -1,8 +1,8 @@
{{#link-to "hosts"}}<i class="icon icon-host"></i>Hosts{{/link-to}}
{{#link-to "containers"}}<i class="icon icon-box"></i>Containers{{/link-to}}
{{#link-to "hosts" project.id}}<i class="icon icon-host"></i>Hosts{{/link-to}}
{{#link-to "containers" project.id}}<i class="icon icon-box"></i>Containers{{/link-to}}
{{#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}}
{{#link-to "storagepools"}}<i class="icon icon-hdd"></i>Storage Pools{{/link-to}}
{{#link-to "certificates"}}<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 "storagepools" project.id}}<i class="icon icon-hdd"></i>Storage Pools{{/link-to}}
{{#link-to "certificates" project.id}}<i class="icon icon-certificate"></i>Certificates{{/link-to}}
{{#link-to "registries" project.id}}<i class="icon icon-database"></i>Registries{{/link-to}}

View File

@ -1,4 +1,6 @@
{{#link-to "k8s-tab.namespace.services" namespace.id}}<i class="icon icon-compass"></i>Services{{/link-to}}
{{#link-to "k8s-tab.namespace.rcs" namespace.id}}<i class="icon icon-tachometer"></i>RCs{{/link-to}}
{{#link-to "k8s-tab.namespace.pods" namespace.id}}<i class="icon icon-containers"></i>Pods{{/link-to}}
{{#link-to "k8s-tab.kubectl"}}<i class="icon icon-terminal"></i>kubectl{{/link-to}}
{{#if namespace.id}}
{{#link-to "k8s-tab.namespace.services" project.id namespace.id}}<i class="icon icon-compass"></i>Services{{/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.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}}

View File

@ -200,7 +200,8 @@ var C = {
},
K8S: {
BASE: 'api/v1',
BASE: 'api',
BASE_VERSION: 'api/v1',
TYPE_PREFIX: 'k8s-',
ID_SEPARATOR: '::'
}

View File

@ -63,9 +63,7 @@ module.exports = function(environment) {
apiEndpoint: '/v1',
catalogServer: '',
catalogEndpoint: '/v1-catalog',
kubernetesServer: '',
kubernetesEndpoint: '/r/kubernetes',
kubectlServer: '',
kubectlEndpoint: '/r/kubectld:8091/v1-kubectl',
proxyEndpoint: '/v1/proxy',
wsEndpoint: '/v1/subscribe' +
@ -138,28 +136,6 @@ module.exports = function(environment) {
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;
if ( pl )
{

View File

@ -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```
This replaces the entire folder, so you must supply all the files or they will be missing.
| File | Usage |
|:--------------------|:----------------------------------------------------------|
|:------------------------|:----------------------------------------------------------|
| dark.svg | On the Login screen when access control is enabled |
| fail-*.svg | On the branded error screen (for ui.pl="rancher" only) |
| graphic.svg | The parachuting cow part of the image on the About screen |
| login-bg.jpg | Background for the login screen box |
| main.svg | Top-left corner of the main header |
| main-loading.svg | On the branded loading screen (for ui.pl="rancher" only) |
| 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 |
| text.svg | The text part of hte image on the About screen |
| provider-clustering.svg | "Cattle" environment clustering provider |
| text.svg | The text part of the image on the About screen |

View File

Before

Width:  |  Height:  |  Size: 772 B

After

Width:  |  Height:  |  Size: 772 B

View File

Before

Width:  |  Height:  |  Size: 771 B

After

Width:  |  Height:  |  Size: 771 B

View File

Before

Width:  |  Height:  |  Size: 744 B

After

Width:  |  Height:  |  Size: 744 B

View File

Before

Width:  |  Height:  |  Size: 772 B

After

Width:  |  Height:  |  Size: 772 B

View File

Before

Width:  |  Height:  |  Size: 766 B

After

Width:  |  Height:  |  Size: 766 B

View File

Before

Width:  |  Height:  |  Size: 3.4 KiB

After

Width:  |  Height:  |  Size: 3.4 KiB

View File

Before

Width:  |  Height:  |  Size: 3.4 KiB

After

Width:  |  Height:  |  Size: 3.4 KiB

View File

Before

Width:  |  Height:  |  Size: 1.2 KiB

After

Width:  |  Height:  |  Size: 1.2 KiB

View File

Before

Width:  |  Height:  |  Size: 3.0 KiB

After

Width:  |  Height:  |  Size: 3.0 KiB

View File

Before

Width:  |  Height:  |  Size: 7.7 KiB

After

Width:  |  Height:  |  Size: 7.7 KiB

View File

@ -75,49 +75,7 @@ module.exports = function(app, options) {
console.log('Catalog Proxy', req.method, 'to', req.url);
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) {
console.log('Proxy Error: on', req.method,'to', req.url,':', err);