]> source.dussan.org Git - sonarqube.git/commitdiff
SONAR-1976 Administrators should be able to request a server restart from web console
authorStas Vilchik <vilchiks@gmail.com>
Wed, 13 Jan 2016 14:05:58 +0000 (15:05 +0100)
committerStas Vilchik <vilchiks@gmail.com>
Wed, 13 Jan 2016 14:06:05 +0000 (15:06 +0100)
server/sonar-web/src/main/js/api/system.js
server/sonar-web/src/main/js/apps/system/main.js
server/sonar-web/src/main/js/apps/update-center/header-view.js
server/sonar-web/src/main/js/apps/update-center/templates/update-center-header.hbs
server/sonar-web/src/main/less/components/page.less

index a5972203853c9ee9bd2e4024003a44a7461c1f48..4ce89bf320e77f335059e9959b0b7d43c9155903 100644 (file)
@@ -29,3 +29,31 @@ export function getSystemInfo () {
   const url = window.baseUrl + '/api/system/info';
   return getJSON(url);
 }
+
+export function getStatus () {
+  const url = window.baseUrl + '/api/system/status';
+  return getJSON(url);
+}
+
+export function restart () {
+  const url = window.baseUrl + '/api/system/restart';
+  return post(url);
+}
+
+const POLLING_INTERVAL = 2000;
+
+function pollStatus (cb) {
+  setTimeout(() => {
+    getStatus()
+        .then(() => cb())
+        .catch(() => pollStatus(cb));
+  }, POLLING_INTERVAL);
+}
+
+function promiseStatus () {
+  return new Promise(resolve => pollStatus(resolve));
+}
+
+export function restartAndWait () {
+  return restart().then(promiseStatus);
+}
index 17615da126501aa0a229c715b838d7c69a1fa859..b09a129e8789551e0e78fe31924d54fafc31cf15 100644 (file)
@@ -19,7 +19,7 @@
  */
 import _ from 'underscore';
 import React from 'react';
-import { getSystemInfo } from '../../api/system';
+import { getSystemInfo, restartAndWait } from '../../api/system';
 import Section from './section';
 import { translate } from '../../helpers/l10n';
 
@@ -27,6 +27,10 @@ const SECTIONS_ORDER = ['SonarQube', 'Database', 'Plugins', 'System', 'ElasticSe
   'ComputeEngine'];
 
 export default React.createClass({
+  getInitialState() {
+    return { restarting: false };
+  },
+
   componentDidMount() {
     getSystemInfo().then(info => this.setState({ sections: this.parseSections(info) }));
   },
@@ -53,9 +57,16 @@ export default React.createClass({
     return _.sortBy(items, 'name');
   },
 
+  handleServerRestart () {
+    this.setState({ restarting: true });
+    restartAndWait().then(() => {
+      document.location.reload();
+    });
+  },
+
   render() {
     let sections = null;
-    if (this.state && this.state.sections) {
+    if (this.state.sections) {
       sections = this.state.sections.map(section => {
         return <Section key={section.name} section={section.name} items={section.items}/>;
       });
@@ -67,6 +78,16 @@ export default React.createClass({
         <div className="page-actions">
           <a className="spacer-right" href={window.baseUrl + '/api/system/logs'} id="logs-link">Logs</a>
           <a href={window.baseUrl + '/api/system/info'} id="download-link">Download</a>
+          {this.state.restarting ? (
+              <i className="spinner"/>
+          ) : (
+              <button
+                  id="restart-server-button"
+                  className="big-spacer-left"
+                  onClick={this.handleServerRestart}>
+                Restart Server
+              </button>
+          )}
         </div>
       </header>
       {sections}
index 4d2672f4f851e71be4e8894230d9fd5e4af7cc37..0ed3a8f89624fecde63a454f8dee312586eacdcc 100644 (file)
@@ -20,6 +20,7 @@
 import _ from 'underscore';
 import Marionette from 'backbone.marionette';
 import Template from './templates/update-center-header.hbs';
+import { restartAndWait } from '../../api/system';
 
 export default Marionette.ItemView.extend({
   template: Template,
@@ -29,17 +30,31 @@ export default Marionette.ItemView.extend({
   },
 
   events: {
+    'click .js-restart': 'restart',
     'click .js-cancel-all': 'cancelAll'
   },
 
-  cancelAll: function () {
+  initialize () {
+    this.restarting = false;
+  },
+
+  restart () {
+    this.restarting = true;
+    this.render();
+    restartAndWait().then(() => {
+      document.location.reload(true);
+    });
+  },
+
+  cancelAll () {
     this.collection.cancelAll();
   },
 
-  serializeData: function () {
+  serializeData () {
     return _.extend(Marionette.ItemView.prototype.serializeData.apply(this, arguments), {
       installing: this.collection._installedCount,
-      uninstalling: this.collection._uninstalledCount
+      uninstalling: this.collection._uninstalledCount,
+      restarting: this.restarting
     });
   }
 });
index 0bcfd3cd7343b85d889c05d087b006ca063e307b..518c96ce81bcf024198fce220677647d4b975e4a 100644 (file)
         {{/if}}
       </p>
     </div>
-    <div class="button-group pull-right">
-      <button class="js-cancel-all button-red">Revert</button>
+    <div class="pull-right">
+      {{#if restarting}}
+        <i class="spinner"></i>
+      {{else}}
+        <button class="js-restart">Restart</button>
+        <button class="js-cancel-all button-red">Revert</button>
+      {{/if}}
     </div>
   </div>
 {{/any}}
index 8d43b4bc2ab443bc55f8063e95a608061e04117f..0c1a834ddc7200697aea30597090d0a931ba162e 100644 (file)
@@ -86,10 +86,15 @@ body {
   float: right;
   margin-bottom: 10px;
   margin-left: 10px;
+  line-height: @formControlHeight;
 
   .badge {
     margin: 3px 0;
   }
+
+  .spinner {
+    top: 0 !important;
+  }
 }
 
 .page-description {