362967f0c9
When some services had no tags(Tags is null) and some did have tags redundant commas were added to the tags list.
471 lines
13 KiB
JavaScript
471 lines
13 KiB
JavaScript
//
|
|
// 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){
|
|
if (obj.Service.Tags !== null) {
|
|
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;
|
|
}
|