You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
consul/ui/javascripts/app/routes.js

469 lines
13 KiB

//
// Superclass to be used by all of the main routes below.
//
App.BaseRoute = Ember.Route.extend({
rootKey: '',
condensedView: false,
// Don't record characters in browser history
// for the "search" query item (filter)
queryParams: {
filter: {
replace: true
}
},
getParentAndGrandparent: function(key) {
var parentKey = this.rootKey,
grandParentKey = this.rootKey,
parts = key.split('/');
if (parts.length > 0) {
parts.pop();
parentKey = parts.join("/") + "/";
}
if (parts.length > 1) {
parts.pop();
grandParentKey = parts.join("/") + "/";
}
return {
parent: parentKey,
grandParent: grandParentKey,
isRoot: parentKey === '/'
};
},
removeDuplicateKeys: function(keys, matcher) {
// Loop over the keys
keys.forEach(function(item, index) {
if (item.get('Key') == matcher) {
// If we are in a nested folder and the folder
// name matches our position, remove it
keys.splice(index, 1);
}
});
return keys;
},
actions: {
// Used to link to keys that are not objects,
// like parents and grandParents
linkToKey: function(key) {
if (key == "/") {
this.transitionTo('kv.show', "");
}
else if (key.slice(-1) === '/' || key === this.rootKey) {
this.transitionTo('kv.show', key);
} else {
this.transitionTo('kv.edit', key);
}
}
}
});
//
// The route for choosing datacenters, typically the first route loaded.
//
App.IndexRoute = App.BaseRoute.extend({
// Retrieve the list of datacenters
model: function(params) {
return Ember.$.getJSON(consulHost + '/v1/catalog/datacenters').then(function(data) {
return data;
});
},
afterModel: function(model, transition) {
// If we only have one datacenter, jump
// straight to it and bypass the global
// view
if (model.get('length') === 1) {
this.transitionTo('services', model[0]);
}
}
});
// The parent route for all resources. This keeps the top bar
// functioning, as well as the per-dc requests.
App.DcRoute = App.BaseRoute.extend({
model: function(params) {
var token = App.get('settings.token');
// Return a promise hash to retreieve the
// dcs and nodes used in the header
return Ember.RSVP.hash({
dc: params.dc,
dcs: Ember.$.getJSON(consulHost + '/v1/catalog/datacenters'),
nodes: Ember.$.getJSON(formatUrl(consulHost + '/v1/internal/ui/nodes', params.dc, token)).then(function(data) {
var objs = [];
// Merge the nodes into a list and create objects out of them
data.map(function(obj){
objs.push(App.Node.create(obj));
});
return objs;
}),
coordinates: Ember.$.getJSON(formatUrl(consulHost + '/v1/coordinate/nodes', params.dc, token)).then(function(data) {
return data;
})
});
},
setupController: function(controller, models) {
controller.set('content', models.dc);
controller.set('nodes', models.nodes);
controller.set('dcs', models.dcs);
controller.set('coordinates', models.coordinates);
controller.set('isDropdownVisible', false);
},
});
App.KvIndexRoute = App.BaseRoute.extend({
beforeModel: function() {
this.transitionTo('kv.show', this.rootKey);
}
});
App.KvShowRoute = App.BaseRoute.extend({
model: function(params) {
var key = params.key;
var dc = this.modelFor('dc').dc;
var token = App.get('settings.token');
// Return a promise has with the ?keys for that namespace
// and the original key requested in params
return Ember.RSVP.hash({
key: key,
keys: Ember.$.getJSON(formatUrl(consulHost + '/v1/kv/' + key + '?keys&seperator=/', dc, token)).then(function(data) {
var objs = [];
data.map(function(obj){
objs.push(App.Key.create({Key: obj}));
});
return objs;
})
});
},
setupController: function(controller, models) {
var key = models.key;
var parentKeys = this.getParentAndGrandparent(key);
models.keys = this.removeDuplicateKeys(models.keys, models.key);
controller.set('content', models.keys);
controller.set('parentKey', parentKeys.parent);
controller.set('grandParentKey', parentKeys.grandParent);
controller.set('isRoot', parentKeys.isRoot);
controller.set('newKey', App.Key.create());
controller.set('rootKey', this.rootKey);
}
});
App.KvEditRoute = App.BaseRoute.extend({
model: function(params) {
var key = params.key;
var dc = this.modelFor('dc').dc;
var parentKeys = this.getParentAndGrandparent(key);
var token = App.get('settings.token');
// Return a promise hash to get the data for both columns
return Ember.RSVP.hash({
dc: dc,
token: token,
key: Ember.$.getJSON(formatUrl(consulHost + '/v1/kv/' + key, dc, token)).then(function(data) {
// Convert the returned data to a Key
return App.Key.create().setProperties(data[0]);
}),
keys: keysPromise = Ember.$.getJSON(formatUrl(consulHost + '/v1/kv/' + parentKeys.parent + '?keys&seperator=/', dc, token)).then(function(data) {
var objs = [];
data.map(function(obj){
objs.push(App.Key.create({Key: obj}));
});
return objs;
}),
});
},
// Load the session on the key, if there is one
afterModel: function(models) {
if (models.key.get('isLocked')) {
return Ember.$.getJSON(formatUrl(consulHost + '/v1/session/info/' + models.key.Session, models.dc, models.token)).then(function(data) {
models.session = data[0];
return models;
});
} else {
return models;
}
},
setupController: function(controller, models) {
var key = models.key;
var parentKeys = this.getParentAndGrandparent(key.get('Key'));
models.keys = this.removeDuplicateKeys(models.keys, parentKeys.parent);
controller.set('content', models.key);
controller.set('parentKey', parentKeys.parent);
controller.set('grandParentKey', parentKeys.grandParent);
controller.set('isRoot', parentKeys.isRoot);
controller.set('siblings', models.keys);
controller.set('rootKey', this.rootKey);
controller.set('session', models.session);
}
});
App.ServicesRoute = App.BaseRoute.extend({
model: function(params) {
var dc = this.modelFor('dc').dc;
var token = App.get('settings.token');
// Return a promise to retrieve all of the services
return Ember.$.getJSON(formatUrl(consulHost + '/v1/internal/ui/services', dc, token)).then(function(data) {
var objs = [];
data.map(function(obj){
objs.push(App.Service.create(obj));
});
return objs;
});
},
setupController: function(controller, model) {
controller.set('services', model);
}
});
App.ServicesShowRoute = App.BaseRoute.extend({
model: function(params) {
var dc = this.modelFor('dc').dc;
var token = App.get('settings.token');
// Here we just use the built-in health endpoint, as it gives us everything
// we need.
return Ember.$.getJSON(formatUrl(consulHost + '/v1/health/service/' + params.name, dc, token)).then(function(data) {
var objs = [];
data.map(function(obj){
objs.push(App.Node.create(obj));
});
return objs;
});
},
setupController: function(controller, model) {
var tags = [];
model.map(function(obj){
tags = tags.concat(obj.Service.Tags);
});
tags = tags.filter(function(n){ return n !== undefined; });
tags = tags.uniq().join(', ');
controller.set('content', model);
controller.set('tags', tags);
}
});
function distance(a, b) {
a = a.Coord;
b = b.Coord;
var sum = 0;
for (var i = 0; i < a.Vec.length; i++) {
var diff = a.Vec[i] - b.Vec[i];
sum += diff * diff;
}
var rtt = Math.sqrt(sum) + a.Height + b.Height;
var adjusted = rtt + a.Adjustment + b.Adjustment;
if (adjusted > 0.0) {
rtt = adjusted;
}
return Math.round(rtt * 100000.0) / 100.0;
}
App.NodesShowRoute = App.BaseRoute.extend({
model: function(params) {
var dc = this.modelFor('dc');
var token = App.get('settings.token');
var min = 999999999;
var max = -999999999;
var sum = 0;
var distances = [];
dc.coordinates.forEach(function (node) {
if (params.name == node.Node) {
dc.coordinates.forEach(function (other) {
if (node.Node != other.Node) {
var dist = distance(node, other);
distances.push({ node: other.Node, distance: dist });
sum += dist;
if (dist < min) {
min = dist;
}
if (dist > max) {
max = dist;
}
}
});
distances.sort(function (a, b) {
return a.distance - b.distance;
});
}
});
var n = distances.length;
var halfN = Math.floor(n / 2);
var median;
if (n > 0) {
if (n % 2) {
// odd
median = distances[halfN].distance;
} else {
median = (distances[halfN - 1].distance + distances[halfN].distance) / 2;
}
} else {
median = 0;
min = 0;
max = 0;
}
// Return a promise hash of the node and nodes
return Ember.RSVP.hash({
dc: dc.dc,
token: token,
tomography: {
distances: distances,
n: distances.length,
min: parseInt(min * 100) / 100,
median: parseInt(median * 100) / 100,
max: parseInt(max * 100) / 100
},
node: Ember.$.getJSON(formatUrl(consulHost + '/v1/internal/ui/node/' + params.name, dc.dc, token)).then(function(data) {
return App.Node.create(data);
}),
nodes: Ember.$.getJSON(formatUrl(consulHost + '/v1/internal/ui/node/' + params.name, dc.dc, token)).then(function(data) {
return App.Node.create(data);
})
});
},
// Load the sessions for the node
afterModel: function(models) {
return Ember.$.getJSON(formatUrl(consulHost + '/v1/session/node/' + models.node.Node, models.dc, models.token)).then(function(data) {
models.sessions = data;
return models;
});
},
setupController: function(controller, models) {
controller.set('content', models.node);
controller.set('sessions', models.sessions);
controller.set('tomography', models.tomography);
//
// Since we have 2 column layout, we need to also display the
// list of nodes on the left. Hence setting the attribute
// {{nodes}} on the controller.
//
controller.set('nodes', models.nodes);
}
});
App.NodesRoute = App.BaseRoute.extend({
model: function(params) {
var dc = this.modelFor('dc').dc;
var token = App.get('settings.token');
// Return a promise containing the nodes
return Ember.$.getJSON(formatUrl(consulHost + '/v1/internal/ui/nodes', dc, token)).then(function(data) {
var objs = [];
data.map(function(obj){
objs.push(App.Node.create(obj));
});
return objs;
});
},
setupController: function(controller, model) {
controller.set('nodes', model);
}
});
App.AclsRoute = App.BaseRoute.extend({
model: function(params) {
var dc = this.modelFor('dc').dc;
var token = App.get('settings.token');
// Return a promise containing the ACLS
return Ember.$.getJSON(formatUrl(consulHost + '/v1/acl/list', dc, token)).then(function(data) {
var objs = [];
data.map(function(obj){
if (obj.ID === "anonymous") {
objs.unshift(App.Acl.create(obj));
} else {
objs.push(App.Acl.create(obj));
}
});
return objs;
});
},
actions: {
error: function(error, transition) {
// If consul returns 401, ACLs are disabled
if (error && error.status === 401) {
this.transitionTo('dc.aclsdisabled');
// If consul returns 403, they key isn't authorized for that
// action.
} else if (error && error.status === 403) {
this.transitionTo('dc.unauthorized');
}
return true;
}
},
setupController: function(controller, model) {
controller.set('acls', model);
controller.set('newAcl', App.Acl.create());
}
});
App.AclsShowRoute = App.BaseRoute.extend({
model: function(params) {
var dc = this.modelFor('dc').dc;
var token = App.get('settings.token');
// Return a promise hash of the node and nodes
return Ember.RSVP.hash({
dc: dc,
acl: Ember.$.getJSON(formatUrl(consulHost + '/v1/acl/info/'+ params.id, dc, token)).then(function(data) {
return App.Acl.create(data[0]);
})
});
},
setupController: function(controller, models) {
controller.set('content', models.acl);
}
});
App.SettingsRoute = App.BaseRoute.extend({
model: function(params) {
return App.get('settings');
}
});
// Adds any global parameters we need to set to a url/path
function formatUrl(url, dc, token) {
if (token == null) {
token = "";
}
if (url.indexOf("?") > 0) {
// If our url has existing params
url = url + "&dc=" + dc;
url = url + "&token=" + token;
} else {
// Our url doesn't have params
url = url + "?dc=" + dc;
url = url + "&token=" + token;
}
return url;
}