From e9b54dcd3cabc8f3fef0bf99a505d2ce356dada3 Mon Sep 17 00:00:00 2001 From: Jared Tabor Date: Sun, 28 Aug 2016 13:01:59 -0700 Subject: [PATCH] switching socket.io-client for ReconnectingWebSocket library --- awx/ui/client/src/app.js | 101 ++++++-------- awx/ui/client/src/config.js | 2 +- .../src/job-detail/job-detail.controller.js | 8 +- .../client/src/job-detail/job-detail.route.js | 32 ++--- awx/ui/client/src/shared/Socket.js | 125 ++++-------------- .../log/standard-out-log.controller.js | 14 +- awx/ui/package.json | 5 +- awx/ui/webpack.config.js | 2 +- 8 files changed, 94 insertions(+), 195 deletions(-) diff --git a/awx/ui/client/src/app.js b/awx/ui/client/src/app.js index 0f925812f6..76506bf43e 100644 --- a/awx/ui/client/src/app.js +++ b/awx/ui/client/src/app.js @@ -713,80 +713,53 @@ var tower = angular.module('Tower', [ $rootScope.removeOpenSocket(); } $rootScope.removeOpenSocket = $rootScope.$on('OpenSocket', function() { - // Listen for job changes and issue callbacks to initiate - // DOM updates function openSocket() { - var schedule_socket, control_socket; - - sock = Socket({ scope: $rootScope, endpoint: "jobs" }); - sock.init(); - sock.on("status_changed", function(data) { - $log.debug('Job ' + data.unified_job_id + - ' status changed to ' + data.status + - ' send to ' + $location.$$url); - - // this acts as a router...it emits the proper - // value based on what URL the user is currently - // accessing. - if ($state.is('jobs')) { - $rootScope.$emit('JobStatusChange-jobs', data); - } else if ($state.includes('jobDetail') || - $state.is('adHocJobStdout') || - $state.is('inventorySyncStdout') || - $state.is('managementJobStdout') || - $state.is('scmUpdateStdout')) { - - $log.debug("sending status to standard out"); - $rootScope.$emit('JobStatusChange-jobStdout', data); - } - if ($state.includes('jobDetail')) { - $rootScope.$emit('JobStatusChange-jobDetails', data); - } else if ($state.is('dashboard')) { - $rootScope.$emit('JobStatusChange-home', data); - } else if ($state.is('portalMode')) { - $rootScope.$emit('JobStatusChange-portal', data); - } else if ($state.is('projects')) { - $rootScope.$emit('JobStatusChange-projects', data); - } else if ($state.is('inventoryManage')) { - $rootScope.$emit('JobStatusChange-inventory', data); - } - }); - sock.on("summary_complete", function(data) { - $log.debug('Job summary_complete ' + data.unified_job_id); - $rootScope.$emit('JobSummaryComplete', data); - }); - - schedule_socket = Socket({ - scope: $rootScope, - endpoint: "schedules" - }); - schedule_socket.init(); - schedule_socket.on("schedule_changed", function(data) { - $log.debug('Schedule ' + data.unified_job_id + ' status changed to ' + data.status); - $rootScope.$emit('ScheduleStatusChange', data); - }); - - control_socket = Socket({ - scope: $rootScope, - endpoint: "control" - }); - control_socket.init(); - control_socket.on("limit_reached", function(data) { - $log.debug(data.reason); - $rootScope.sessionTimer.expireSession('session_limit'); - $state.go('signOut'); - }); + $rootScope.socket = Socket({ scope: $rootScope}); + $rootScope.socket.init(); } openSocket(); setTimeout(function() { $rootScope.$apply(function() { - sock.checkStatus(); + $rootScope.socket.checkStatus(); $log.debug('socket status: ' + $rootScope.socketStatus); }); }, 2000); }); - + // {'groups': + // {'jobs': ['status_changed', 'summary'], + // 'schedules': ['changed'], + // 'ad_hoc_command_events': [ids,], + // 'job_events': [ids,], + // 'control': ['limit_reached'], + // } + // } + $rootScope.$on('$stateChangeStart', function(event, toState, toParams, fromState) { + if(toState.name === 'dashboard'){ + $rootScope.socket.emit('{"groups":{"jobs": ["status_changed"]}}'); + } + else if(toState.name === 'jobDetail'){ + $rootScope.socket.emit(`{"groups":{"jobs": ["status_changed", "summary"]},{"job_events":[${toParams.id}]}}`); + } + else if(toState.name === 'jobStdout'){ + $rootScope.socket.emit('{"groups":{"jobs": ["status_changed"]}}'); + } + else if(toState.name === 'jobs'){ + $rootScope.socket.emit('{"groups":{"jobs": ["status_changed"]}, {"schedules": ["changed"]}}'); + } + else if(toState.name === 'portalMode'){ + $rootScope.socket.emit('{"groups":{"jobs": ["status_changed"]}}'); + } + else if(toState.name === 'projects'){ + $rootScope.socket.emit('{"groups":{"jobs": ["status_changed"]}}'); + } + else if(toState.name === 'inventory'){ + $rootScope.socket.emit('{"groups":{"jobs": ["status_changed"]}}'); + } + else if(toState.name === 'adHocJobStdout'){ + $rootScope.socket.emit(`{"groups":{"ad_hoc_command_events": [${toParams.id}]}}`); + } + }); $rootScope.$on('$stateChangeSuccess', function(event, toState, toParams, fromState) { diff --git a/awx/ui/client/src/config.js b/awx/ui/client/src/config.js index 55956732a6..0aa7dbaf17 100644 --- a/awx/ui/client/src/config.js +++ b/awx/ui/client/src/config.js @@ -28,7 +28,7 @@ // custom_login_info: "example notice" // have a notice displayed in the login modal for users. note that, as a security measure, custom html is not supported and will be escaped. tooltip_delay: { show: 500, hide: 100 }, // Default number of milliseconds to delay displaying/hiding tooltips - debug_mode: false, // Enable console logging messages + debug_mode: true, // Enable console logging messages password_length: 8, // Minimum user password length. Set to 0 to not set a limit password_hasLowercase: true, // require a lowercase letter in the password diff --git a/awx/ui/client/src/job-detail/job-detail.controller.js b/awx/ui/client/src/job-detail/job-detail.controller.js index 9181b21b20..d883464938 100644 --- a/awx/ui/client/src/job-detail/job-detail.controller.js +++ b/awx/ui/client/src/job-detail/job-detail.controller.js @@ -198,7 +198,7 @@ export default "

Unreachable

\n" + "

Failed

\n"; function openSocket() { - $rootScope.event_socket.on("job_events-" + job_id, function(data) { + $rootScope.socket.on("job_events-" + job_id, function(data) { // update elapsed time on each event received scope.job_status.elapsed = GetElapsed({ start: scope.job.created, @@ -213,9 +213,9 @@ export default }); // Unbind $rootScope socket event binding(s) so that they don't get triggered // in another instance of this controller - scope.$on('$destroy', function() { - $rootScope.event_socket.removeAllListeners("job_events-" + job_id); - }); + // scope.$on('$destroy', function() { + // $rootScope.socket.removeAllListeners("job_events-" + job_id); + // }); } openSocket(); diff --git a/awx/ui/client/src/job-detail/job-detail.route.js b/awx/ui/client/src/job-detail/job-detail.route.js index dda2722511..d08bcfecf8 100644 --- a/awx/ui/client/src/job-detail/job-detail.route.js +++ b/awx/ui/client/src/job-detail/job-detail.route.js @@ -13,22 +13,22 @@ export default { parent: 'jobs', label: "{{ job.id }} - {{ job.name }}" }, - resolve: { - jobEventsSocket: ['Socket', '$rootScope', function(Socket, $rootScope) { - if (!$rootScope.event_socket) { - $rootScope.event_socket = Socket({ - scope: $rootScope, - endpoint: "job_events" - }); - $rootScope.event_socket.init(); - // returns should really be providing $rootScope.event_socket - // otherwise, we have to inject the entire $rootScope into the controller - return true; - } else { - return true; - } - }] - }, + // resolve: { + // jobEventsSocket: ['Socket', '$rootScope', function(Socket, $rootScope) { + // if (!$rootScope.event_socket) { + // $rootScope.event_socket = Socket({ + // scope: $rootScope, + // endpoint: "job_events" + // }); + // $rootScope.event_socket.init(); + // // returns should really be providing $rootScope.event_socket + // // otherwise, we have to inject the entire $rootScope into the controller + // return true; + // } else { + // return true; + // } + // }] + // }, templateUrl: templateUrl('job-detail/job-detail'), controller: 'JobDetailController' }; diff --git a/awx/ui/client/src/shared/Socket.js b/awx/ui/client/src/shared/Socket.js index 886b51d4a3..5bc01d6879 100644 --- a/awx/ui/client/src/shared/Socket.js +++ b/awx/ui/client/src/shared/Socket.js @@ -22,16 +22,16 @@ * @methodOf shared.function:Socket * @description */ +import ReconnectingWebSocket from 'reconnectingwebsocket' export default angular.module('SocketIO', ['Utilities']) .factory('Socket', ['$rootScope', '$location', '$log', 'Authorization', 'Store', function ($rootScope, $location, $log, Authorization, Store) { return function(params) { var scope = params.scope, - host = $location.host(), + host = window.location.host, endpoint = params.endpoint, protocol = $location.protocol(), - io = require('socket.io-client'), config, socketPort, url; @@ -45,7 +45,9 @@ angular.module('SocketIO', ['Utilities']) config = Store('AnsibleConfig'); socketPort = config.websocket_port; } - url = protocol + '://' + host + ':' + socketPort + '/socket.io/' + endpoint; + + url = "ws://" + host + "/websocket/"; + // url = protocol + '://' + host + ':' + socketPort + '/socket.io/' + endpoint; $log.debug('opening socket connection to: ' + url); function getSocketTip(status) { @@ -75,86 +77,10 @@ angular.module('SocketIO', ['Utilities']) // We have a valid session token, so attempt socket connection $log.debug('Socket connecting to: ' + url); self.scope.socket_url = url; - self.socket = io.connect(url, { - query: "Token="+token, - headers: - { - 'Authorization': 'Token ' + token, // i don't think these are actually inserted into the header--jt - 'X-Auth-Token': 'Token ' + token - }, - 'connect timeout': 3000, - 'try multiple transports': false, - 'max reconnection attempts': 10, - 'reconnection limit': 2000, - 'force new connection': true - }); - - self.socket.on('connection', function() { - $log.debug('Socket connecting...'); - self.scope.$apply(function () { - self.scope.socketStatus = 'connecting'; - self.scope.socketTip = getSocketTip(self.scope.socketStatus); - }); - }); - self.socket.on('connect', function() { - $log.debug('Socket connection established'); - self.scope.$apply(function () { - self.scope.socketStatus = 'ok'; - self.scope.socketTip = getSocketTip(self.scope.socketStatus); - }); - }); - self.socket.on('connect_failed', function(reason) { - var r = reason || 'connection refused by host', - token_actual = Authorization.getToken(); - - $log.debug('Socket connection failed: ' + r); - - if (token_actual === token) { - self.socket.socket.disconnect(); - } - - self.scope.$apply(function () { - self.scope.socketStatus = 'error'; - self.scope.socketTip = getSocketTip(self.scope.socketStatus); - }); - - }); - self.socket.on('diconnect', function() { - $log.debug('Socket disconnected'); - self.scope.$apply(function() { - self.scope.socketStatus = 'error'; - self.scope.socketTip = getSocketTip(self.scope.socketStatus); - }); - }); - self.socket.on('error', function(reason) { - var r = reason || 'connection refused by host'; - $log.debug('Socket error: ' + r); - $log.error('Socket error: ' + r); - self.scope.$apply(function() { - self.scope.socketStatus = 'error'; - self.scope.socketTip = getSocketTip(self.scope.socketStatus); - }); - }); - self.socket.on('reconnecting', function() { - $log.debug('Socket attempting reconnect...'); - self.scope.$apply(function() { - self.scope.socketStatus = 'connecting'; - self.scope.socketTip = getSocketTip(self.scope.socketStatus); - }); - }); - self.socket.on('reconnect', function() { - $log.debug('Socket reconnected'); - self.scope.$apply(function() { - self.scope.socketStatus = 'ok'; - self.scope.socketTip = getSocketTip(self.scope.socketStatus); - }); - }); - self.socket.on('reconnect_failed', function(reason) { - $log.error('Socket reconnect failed: ' + reason); - self.scope.$apply(function() { - self.scope.socketStatus = 'error'; - self.scope.socketTip = getSocketTip(self.scope.socketStatus); - }); + self.socket = new ReconnectingWebSocket(url, null, { + debug: true, + timeoutInterval: 3000, + maxReconnectAttempts: 10 }); } else { @@ -167,21 +93,19 @@ angular.module('SocketIO', ['Utilities']) // Check connection status var self = this; if(self){ - if(self.socket){ - if(self.socket.socket){ - if (self.socket.socket.connected) { - self.scope.socketStatus = 'ok'; - } - else if (self.socket.socket.connecting || self.socket.reconnecting) { - self.scope.socketStatus = 'connecting'; - } - else { - self.scope.socketStatus = 'error'; - } - self.scope.socketTip = getSocketTip(self.scope.socketStatus); - return self.scope.socketStatus; + if(self.socket){ + if (self.socket.readyState === 0 ) { + self.scope.socketStatus = 'connecting'; + } + else if (self.socket.readyState === 1){ + self.scope.socketStatus = 'ok'; + } + else if (self.socket.readyState === 2 || self.socket.readyState === 3 ){ + self.scope.socketStatus = 'error'; + } + self.scope.socketTip = getSocketTip(self.scope.socketStatus); + return self.scope.socketStatus; } - } } }, @@ -189,7 +113,8 @@ angular.module('SocketIO', ['Utilities']) var self = this; if(self){ if(self.socket){ - self.socket.on(eventName, function () { + self.socket.onmessage(eventName, function (e) { + console.log('Received From Server: ' + e.data); var args = arguments; self.scope.$apply(function () { callback.apply(self.socket, args); @@ -201,7 +126,7 @@ angular.module('SocketIO', ['Utilities']) }, emit: function (eventName, data, callback) { var self = this; - self.socket.emit(eventName, data, function () { + self.socket.send(eventName, data, function () { var args = arguments; self.scope.$apply(function () { if (callback) { @@ -217,7 +142,7 @@ angular.module('SocketIO', ['Utilities']) var self = this; if(self){ if(self.socket){ - self.socket.removeAllListeners(eventName); + self.socket.removeEventListener(eventName); } } }, diff --git a/awx/ui/client/src/standard-out/log/standard-out-log.controller.js b/awx/ui/client/src/standard-out/log/standard-out-log.controller.js index 06e9cd7b64..51d1a8ea7b 100644 --- a/awx/ui/client/src/standard-out/log/standard-out-log.controller.js +++ b/awx/ui/client/src/standard-out/log/standard-out-log.controller.js @@ -22,7 +22,7 @@ export default ['$log', '$rootScope', '$scope', '$state', '$stateParams', 'Proce function openSockets() { if ($state.current.name === 'jobDetail') { $log.debug("socket watching on job_events-" + job_id); - $rootScope.event_socket.on("job_events-" + job_id, function() { + $rootScope.socket.on("job_events-" + job_id, function() { $log.debug("socket fired on job_events-" + job_id); if (api_complete) { event_queue++; @@ -30,9 +30,9 @@ export default ['$log', '$rootScope', '$scope', '$state', '$stateParams', 'Proce }); // Unbind $rootScope socket event binding(s) so that they don't get triggered // in another instance of this controller - $scope.$on('$destroy', function() { - $rootScope.event_socket.removeAllListeners("job_events-" + job_id); - }); + // $scope.$on('$destroy', function() { + // $rootScope.socket.removeAllListeners("job_events-" + job_id); + // }); } if ($state.current.name === 'adHocJobStdout') { $log.debug("socket watching on ad_hoc_command_events-" + job_id); @@ -44,9 +44,9 @@ export default ['$log', '$rootScope', '$scope', '$state', '$stateParams', 'Proce }); // Unbind $rootScope socket event binding(s) so that they don't get triggered // in another instance of this controller - $scope.$on('$destroy', function() { - $rootScope.adhoc_event_socket.removeAllListeners("ad_hoc_command_events-" + job_id); - }); + // $scope.$on('$destroy', function() { + // $rootScope.adhoc_event_socket.removeAllListeners("ad_hoc_command_events-" + job_id); + // }); } } diff --git a/awx/ui/package.json b/awx/ui/package.json index 414150fa49..5e5d474a6f 100644 --- a/awx/ui/package.json +++ b/awx/ui/package.json @@ -99,7 +99,8 @@ "moment": "^2.10.2", "ng-toast": "leigh-johnson/ngToast#2.0.1", "nvd3": "leigh-johnson/nvd3#1.7.1", - "select2": "^4.0.2", - "socket.io-client": "^0.9.17" + "reconnectingwebsocket": "^1.0.0", + "rrule": "jkbrzt/rrule#4ff63b2f8524fd6d5ba6e80db770953b5cd08a0c", + "select2": "^4.0.2" } } diff --git a/awx/ui/webpack.config.js b/awx/ui/webpack.config.js index a5e631aa9b..57d8dd880c 100644 --- a/awx/ui/webpack.config.js +++ b/awx/ui/webpack.config.js @@ -27,7 +27,7 @@ var vendorPkgs = [ 'ng-toast', 'nvd3', 'select2', - 'socket.io-client', + 'reconnectingwebsocket' ]; var dev = {