// // 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 retrieve 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) { var segment = node.Segment; dc.coordinates.forEach(function (other) { if (node.Node != other.Node && other.Segment == segment) { var dist = distance(node, other); distances.push({ node: other.Node, distance: dist, segment: segment }); 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 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); }) }); }, // 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); } }); 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 ACLs 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; }