import Ember from 'ember'; import Socket from 'ui/utils/socket'; import Util from 'ui/utils/util'; import AuthenticatedRouteMixin from 'ui/mixins/authenticated-route'; import C from 'ui/utils/constants'; export default Ember.Route.extend(AuthenticatedRouteMixin, { socket: null, model: function(params, transition) { var store = this.get('store'); var session = this.get('session'); // Load schemas return store.find('schema', null, {url: 'schemas'}).then((/*schemas*/) => { var isAdmin = session.get(C.USER_TYPE_SESSION_KEY) === C.USER_TYPE_ADMIN; // Save whether the user is an admin or not. // For cattle >= v0.6 the token response will have userType: admin/user, for older look for a schema this.set('app.isAuthenticationAdmin', isAdmin || store.hasRecordFor('schema','githubconfig')); // Load all the projects var supportsProjects = this.get('app.authenticationEnabled') && store.hasRecordFor('schema','project'); this.set('app.projectsEnabled', supportsProjects); if ( supportsProjects ) { return store.find('project').catch(() => { this.set('app.projectsEnabled', false); return Ember.RSVP.resolve(); }); } else { return Ember.RSVP.resolve(); } }).catch((err) => { if ( err.status === 401 ) { this.send('logout',transition,true); } else { this.send('error',err); } }); }, setupController: function(controller /*, model*/) { this._super.apply(this,arguments); if ( this.get('app.projectsEnabled') ) { var all = this.get('store').all('project'); controller.set('projects', all); var projectId = this.get('session').get(C.PROJECT_SESSION_KEY); if ( projectId ) { var project = all.filterBy('id', projectId)[0]; if ( project ) { controller.set('project', project); } else { this.get('session').set(C.PROJECT_SESSION_KEY, undefined); this.set('project', null); } } else { controller.set('project', null); } } }, actions: { error: function(err,transition) { // Unauthorized error, send back to login screen if ( err.status === 401 ) { this.send('logout',transition,true); return false; } else { // Bubble up return true; } }, switchProject: function(projectId) { this.get('session').set(C.PROJECT_SESSION_KEY, projectId); this.get('store').reset(); this.transitionTo('index'); }, setPageName: function(str) { this.controller.set('pageName',str); }, setPageLayout: function(opt) { this.controller.set('pageName', opt.label || ''); this.controller.set('backRoute', opt.backRoute || null); this.controller.set('backPrevious', opt.backPrevious || null); this.controller.set('addRoute', opt.addRoute || null); if ( typeof opt.hasAside === 'undefined' ) { this.controller.set('hasAside', false); this.controller.set('asideColor', ''); } else { this.controller.set('hasAside', !!opt.hasAside); this.controller.set('asideColor', opt.hasAside||''); } }, // Raw message from the WebSocket wsMessage: function(/*data*/) { //console.log('wsMessage',data); }, // WebSocket connected wsConnected: function(tries,msec) { var msg = 'WebSocket connected'; if (tries > 0) { msg += ' (after '+ tries + ' ' + (tries === 1 ? 'try' : 'tries'); if (msec) { msg += ', ' + (msec/1000) + ' sec'; } msg += ')'; } console.log(msg); }, // WebSocket disconnected wsDisconnected: function() { console.log('WebSocket disconnected'); }, wsPing: function() { console.log('WebSocket ping'); }, /* agentChanged: function(change) { if (!change || !change.data || !change.data.resource) { return; } //console.log('Agent Changed:', change); var agent = change.data.resource; var id = agent.id; delete agent.hosts; var hosts = this.controllerFor('hosts'); hosts.forEach(function(host) { if ( host.get('agent.id') === id ) { host.get('agent').setProperties(agent); } }); }, */ machineChanged: function(change) { console.log('Machine changed:',change); }, 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); } console.log('Host changed:',change); }, containerChanged: 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); }, loadBalancerConfigChanged: function(change) { this._includeChanged('loadBalancer', 'loadBalancerListeners', 'loadBalancerListeners', 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) { // @TODO Change to registryId when the backend changes var key = 'registryId'; if ( Object.keys(change.data.resource).indexOf(key) === -1 ) { key = 'storagePoolId'; } this._includeChanged('registry', 'credentials', key, change.data.resource); } }, enter: function() { var store = this.get('store'); var boundTypeify = store._typeify.bind(store); var url = "ws://"+window.location.host + this.get('app.wsEndpoint'); var session = this.get('session'); var jwt = session.get('jwt'); if ( jwt ) { url += (url.indexOf('?') >= 0 ? '&' : '?') + 'token=' + encodeURIComponent(jwt); } var projectId = session.get('projectId'); if ( projectId ) { url += (url.indexOf('?') >= 0 ? '&' : '?') + 'projectId=' + encodeURIComponent(projectId); } var socket = Socket.create({ url: url }); socket.on('message', (event) => { var d = JSON.parse(event.data, boundTypeify); this._trySend('wsMessage',d); var str = d.name; if ( d.resourceType ) { str += ' ' + d.resourceType; if ( d.resourceId ) { str += ' ' + d.resourceId; } } var action; if ( d.name === 'resource.change' ) { action = d.resourceType+'Changed'; } else if ( d.name === 'ping' ) { action = 'wsPing'; } if ( action ) { this._trySend(action,d); } }); socket.on('connected', (tries, after) => { this._trySend('wsConnected', tries, after); }); socket.on('disconnected', () => { this._trySend('wsDisconnected', this.get('tries')); }); this.set('socket', socket); socket.connect(); }, exit: function() { var socket = this.get('socket'); if ( socket ) { socket.disconnect(); } // Forget all the things this.get('store').reset(); }, _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(() => {}); }); }, });