github.com/justinjmoses/evergreen@v0.0.0-20170530173719-1d50e381ff0d/public/static/js/spawned_hosts.js (about) 1 mciModule.controller('SpawnedHostsCtrl', ['$scope','$window', '$timeout', 'mciSpawnRestService', 'notificationService', function($scope, $window, $timeout, mciSpawnRestService, notificationService) { 2 $scope.userTz = $window.userTz; 3 $scope.hosts = null; 4 $scope.modalOpen = false; 5 $scope.spawnTask = $window.spawnTask; 6 $scope.spawnDistro = $window.spawnDistro; 7 8 // variables for spawning a new host 9 $scope.spawnableDistros = []; 10 $scope.selectedDistro = {}; 11 $scope.userKeys = []; 12 $scope.selectedKey = {}; 13 $scope.userData = {}; 14 $scope.spawnInfo = {}; 15 $scope.extensionLength = {}; 16 $scope.curHostData; 17 $scope.hostExtensionLengths = {}; 18 $scope.maxHostsPerUser = $window.maxHostsPerUser; 19 $scope.spawnReqSent = false; 20 21 // max of 7 days time to expiration 22 $scope.maxHoursToExpiration = 24*7; 23 $scope.saveKey = false; 24 $scope.currKeyName = ''; 25 $scope.newKey = { 26 'name': 'New Key...', 27 'key': '', 28 }; 29 30 var epochTime = moment('Jan 1, 1970'); 31 32 $scope.sortOrders = [ 33 { name: 'Status', by: 'status' }, 34 { name: 'Uptime', by: 'uptime' }, 35 { name: 'Create Time', by: 'creation_time', reverse: false }, 36 { name: 'Distro', by: 'distro' } 37 ]; 38 39 $scope.sortBy = $scope.sortOrders[0]; 40 41 $scope.extensionLengths = [ 42 {display: "1 hour", hours: 1}, 43 {display: "2 hours", hours: 2}, 44 {display: "4 hours", hours: 4}, 45 {display: "6 hours", hours: 6}, 46 {display: "8 hours", hours: 8}, 47 {display: "10 hours", hours: 10}, 48 {display: "12 hours", hours: 12}, 49 {display: "1 day", hours: 24}, 50 {display: "2 days", hours: 24*2}, 51 {display: "3 days", hours: 24*3}, 52 {display: "4 days", hours: 24*4}, 53 {display: "5 days", hours: 24*5}, 54 {display: "6 days", hours: 24*6}, 55 {display: "7 days", hours: 24*7}, 56 ]; 57 58 $scope.setSortBy = function(order) { 59 $scope.sortBy = order; 60 }; 61 62 // Spawn REST API calls 63 $scope.fetchSpawnedHosts = function() { 64 mciSpawnRestService.getSpawnedHosts( 65 'hosts', {}, { 66 success: function(hosts, status) { 67 _.each(hosts, function(host) { 68 host.isTerminated = host.status == 'terminated'; 69 var terminateTime = moment(host.termination_time); 70 // check if the host is terminated to determine uptime 71 if (terminateTime > epochTime) { 72 var uptime = terminateTime.diff(host.creation_time, 'seconds'); 73 host.uptime = moment.duration(uptime, 'seconds').humanize(); 74 } else { 75 var uptime = moment().diff(host.creation_time, 'seconds'); 76 host.uptime = moment.duration(uptime, 'seconds').humanize(); 77 var expiretime = moment().diff(host.expiration_time, 'seconds'); 78 if(+new Date(host.expiration_time) > +new Date("0001-01-01T00:00:00Z")){ 79 host.expires_in = moment.duration(expiretime, 'seconds').humanize(); 80 } 81 } 82 if ($scope.lastSelected && $scope.lastSelected.id == host.id) { 83 $scope.setSelected(host); 84 } 85 }); 86 $scope.hosts = hosts 87 }, 88 error: function(jqXHR, status, errorThrown) { 89 // Avoid errors when leaving the page because of a background refresh 90 if ($scope.hosts == null && !$scope.errorFetchingHosts) { 91 notificationService.pushNotification('Error fetching spawned hosts: ' + jqXHR, 'errorHeader'); 92 $scope.errorFetchingHosts = true; 93 } 94 } 95 } 96 ); 97 } 98 99 // Load immediately, load again in 5 seconds to pick up any slow 100 // spawns / terminates from the pervious post since they are async, and 101 // every 60 seconds after that to pick up changes. 102 $timeout($scope.fetchSpawnedHosts, 1); 103 $timeout($scope.fetchSpawnedHosts, 5000); 104 setInterval(function(){$scope.fetchSpawnedHosts();}, 60000); 105 106 // Returns true if the user can spawn another host. If hosts has not been initialized it 107 // assumes true. 108 $scope.availableHosts = function() { 109 return ($scope.hosts == null) || ($scope.hosts.length < $scope.maxHostsPerUser) 110 } 111 112 $scope.fetchSpawnableDistros = function(selectDistro, cb) { 113 mciSpawnRestService.getSpawnableDistros( 114 'distros', {}, { 115 success: function(distros, status) { 116 $scope.setSpawnableDistros(distros, selectDistro); 117 // If there is a callback to run after the distros were fetched, 118 // execute it. 119 if(cb){ 120 cb(); 121 } 122 }, 123 error: function(jqXHR, status, errorThrown) { 124 notificationService.pushNotification('Error fetching spawnable distros: ' + jqXHR,'errorHeader'); 125 } 126 } 127 ); 128 }; 129 130 $scope.fetchUserKeys = function() { 131 mciSpawnRestService.getUserKeys( 132 'keys', {}, { 133 success: function(keys, status) { 134 $scope.setUserKeys(keys); 135 }, 136 error: function(jqXHR, status, errorThrown) { 137 notificationService.pushNotification('Error fetching user keys: ' + jqXHR,'errorHeader'); 138 } 139 } 140 ); 141 }; 142 143 $scope.spawnHost = function() { 144 $scope.spawnReqSent = true; 145 $scope.spawnInfo.spawnKey = $scope.selectedKey; 146 $scope.spawnInfo.saveKey = $scope.saveKey; 147 $scope.spawnInfo.userData = $scope.userData.text; 148 if($scope.spawnTaskChecked && !!$scope.spawnTask){ 149 $scope.spawnInfo.task_id = $scope.spawnTask.id; 150 } 151 mciSpawnRestService.spawnHost( 152 $scope.spawnInfo, {}, { 153 success: function(data, status) { 154 window.location.href = "/spawn"; 155 }, 156 error: function(jqXHR, status, errorThrown) { 157 $scope.spawnReqSent = false; 158 notificationService.pushNotification('Error spawning host: ' + jqXHR,'errorHeader'); 159 } 160 } 161 ); 162 }; 163 164 $scope.updateRDPPassword = function () { 165 mciSpawnRestService.updateRDPPassword( 166 'updateRDPPassword', 167 $scope.curHostData.id, 168 $scope.curHostData.password, {}, { 169 success: function (data, status) { 170 window.location.href = "/spawn"; 171 }, 172 error: function (jqXHR, status, errorThrown) { 173 notificationService.pushNotification('Error setting host RDP password: ' + jqXHR,'errorHeader'); 174 } 175 } 176 ); 177 }; 178 179 $scope.updateHostExpiration = function() { 180 mciSpawnRestService.extendHostExpiration( 181 'extendHostExpiration', 182 $scope.curHostData.id, 183 $scope.extensionLength.hours.toString(), {}, { 184 success: function (data, status) { 185 window.location.href = "/spawn"; 186 }, 187 error: function (jqXHR, status, errorThrown) { 188 notificationService.pushNotification('Error extending host expiration: ' + jqXHR,'errorHeader'); 189 } 190 } 191 ); 192 }; 193 194 $scope.terminateHost = function() { 195 mciSpawnRestService.terminateHost( 196 'terminate', 197 $scope.curHostData.id, {}, { 198 success: function(data, status) { 199 window.location.href = "/spawn"; 200 }, 201 error: function(jqXHR, status, errorThrown) { 202 notificationService.pushNotification('Error terminating host: ' + jqXHR,'errorHeader'); 203 } 204 } 205 ); 206 }; 207 208 // API helper methods 209 $scope.setSpawnableDistros = function(distros, selectDistroId) { 210 if (distros.length == 0) { 211 distros = []; 212 } 213 distros.forEach(function(spawnableDistro) { 214 $scope.spawnableDistros.push({ 215 'distro': spawnableDistro 216 }); 217 }); 218 if (distros.length > 0) { 219 $scope.spawnableDistros.sort(function(a, b) { 220 if (a.distro.name < b.distro.name) return -1; 221 if (a.distro.name > b.distro.name) return 1; 222 return 0; 223 }); 224 $scope.selectedDistro = $scope.spawnableDistros[0].distro; 225 $scope.spawnInfo = { 226 'distroId': $scope.selectedDistro.name, 227 'spawnKey': $scope.newKey, 228 }; 229 if(selectDistroId){ 230 var selectedIndex = _.findIndex($scope.spawnableDistros, 231 function(x){return x.distro.name == selectDistroId} 232 ) 233 if(selectedIndex>=0){ 234 $scope.selectedDistro = $scope.spawnableDistros[selectedIndex].distro; 235 $scope.spawnInfo.distroId = $scope.selectedDistro.name; 236 } 237 238 } 239 }; 240 }; 241 242 $scope.setExtensionLength = function(extensionLength) { 243 $scope.extensionLength = extensionLength; 244 }; 245 246 $scope.setUserKeys = function(publicKeys) { 247 if (publicKeys == 'null') { 248 publicKeys = []; 249 } 250 _.each(publicKeys, function(publicKey) { 251 $scope.userKeys.push({ 'name': publicKey.name, 'key': publicKey.key }); 252 }); 253 if (publicKeys.length > 0) { 254 $scope.userKeys.sort(function(a, b) { 255 if (a.name < b.name) return -1; 256 if (a.name > b.name) return 1; 257 return 0; 258 }); 259 $scope.updateSelectedKey($scope.userKeys[0]); 260 } else { 261 $scope.updateSelectedKey($scope.newKey); 262 } 263 // disable key name text field by default 264 $('#input-key-name').attr('disabled', true); 265 }; 266 267 268 // User Interface helper functions 269 // set the spawn request distro based on user selection 270 $scope.setSpawnableDistro = function(spawnableDistro) { 271 $scope.selectedDistro = spawnableDistro 272 $scope.spawnInfo.distroId = spawnableDistro.name; 273 }; 274 275 // toggle spawn key based on user selection 276 $scope.updateSelectedKey = function(selectedKey) { 277 $scope.selectedKey.name = selectedKey.name; 278 $scope.selectedKey.key = selectedKey.key; 279 $scope.currKeyName = $scope.selectedKey.name; 280 }; 281 282 // indicates if user wishes to save this key 283 $scope.toggleSaveKey = function() { 284 $scope.saveKey = !$scope.saveKey; 285 } 286 287 $scope.getSpawnStatusLabel = function(host) { 288 if (host) { 289 switch (host.status) { 290 case 'running': 291 return 'label success'; 292 break; 293 case 'provisioning': 294 case 'starting': 295 return 'label block-status-started'; 296 break; 297 case 'decommissioned': 298 case 'unreachable': 299 case 'quarantined': 300 case 'provision failed': 301 return 'label block-status-cancelled'; 302 break; 303 case 'terminated': 304 return 'label block-status-failed'; 305 break; 306 default: 307 return ''; 308 } 309 } 310 } 311 312 // populate for selected host; highlight selected host 313 $scope.setSelected = function(host) { 314 if ($scope.lastSelected) { 315 $scope.lastSelected.selected = ''; 316 } 317 host.selected = 'active-host'; 318 host.password = ''; 319 host.cPassword = ''; 320 $scope.lastSelected = host; 321 $scope.curHostData = host; 322 $scope.curHostData.isTerminated = host.isTerminated; 323 // check if this is a windows host 324 $scope.curHostData.isWinHost = false; 325 if (host.distro.arch.indexOf('win') != -1) { 326 $scope.curHostData.isWinHost = true; 327 } 328 $scope.updateExtensionOptions($scope.curHostData); 329 }; 330 331 $scope.updateExtensionOptions = function(host) { 332 $scope.hostExtensionLengths = []; 333 _.each($scope.extensionLengths, function(extensionLength) { 334 var remainingTimeSec = moment(host.expiration_time).diff(moment(), 'seconds'); 335 var remainingTimeDur = moment.duration(remainingTimeSec, 'seconds'); 336 remainingTimeDur.add(extensionLength.hours, 'hours'); 337 338 // you should only be able to extend duration for a max of 7 days 339 if (remainingTimeDur.as('hours') < $scope.maxHoursToExpiration) { 340 $scope.hostExtensionLengths.push(extensionLength); 341 } 342 }); 343 if ($scope.hostExtensionLengths.length > 0) { 344 $scope.setExtensionLength($scope.hostExtensionLengths[0]); 345 } 346 } 347 348 $scope.setExtensionLength = function(extensionLength) { 349 $scope.extensionLength = extensionLength; 350 } 351 352 $scope.openSpawnModal = function(opt) { 353 $scope.modalOption = opt; 354 $scope.modalOpen = true; 355 356 var modal = $('#spawn-modal').modal('show'); 357 if ($scope.modalOption === 'spawnHost') { 358 $scope.fetchUserKeys(); 359 if ($scope.spawnableDistros.length == 0) { 360 $scope.fetchSpawnableDistros(); 361 } 362 $scope.modalTitle = 'Spawn Host'; 363 modal.on('shown.bs.modal', function() { 364 $scope.modalOpen = true; 365 $scope.$apply(); 366 }); 367 368 modal.on('hide.bs.modal', function() { 369 $scope.modalOpen = false; 370 $scope.$apply(); 371 }); 372 } else if ($scope.modalOption === 'terminateHost') { 373 $scope.modalTitle = 'Terminate Host'; 374 modal.on('shown.bs.modal', function() { 375 $scope.modalOpen = true; 376 $scope.$apply(); 377 }); 378 379 modal.on('hide.bs.modal', function() { 380 $scope.modalOpen = false; 381 $scope.$apply(); 382 }); 383 } else if ($scope.modalOption === 'updateRDPPassword') { 384 $scope.modalTitle = 'Set RDP Password'; 385 modal.on('shown.bs.modal', function () { 386 $('#password-input').focus(); 387 $scope.modalOpen = true; 388 $scope.$apply(); 389 }); 390 391 modal.on('hide.bs.modal', function () { 392 $scope.modalOpen = false; 393 $scope.$apply(); 394 }); 395 } 396 397 }; 398 399 $(document).keyup(function(ev) { 400 if ($scope.modalOpen && ev.keyCode === 13) { 401 if ($scope.modalOption === 'terminateHost') { 402 $scope.terminateHost(); 403 $('#spawn-modal').modal('hide'); 404 } 405 } 406 }); 407 408 if($scope.spawnTask && $scope.spawnDistro){ 409 // find the spawn distro in the spawnable distros list, if it's there, 410 // pre-select it in the modal. 411 $scope.spawnTaskChecked = true 412 setTimeout(function(){ 413 $scope.fetchSpawnableDistros($scope.spawnDistro._id, function(){ 414 $scope.openSpawnModal('spawnHost') 415 }); 416 }, 0) 417 } 418 } 419 420 ]);