diff --git a/jupyterhub/apihandlers/hub.py b/jupyterhub/apihandlers/hub.py index c31765ff..35880681 100644 --- a/jupyterhub/apihandlers/hub.py +++ b/jupyterhub/apihandlers/hub.py @@ -3,6 +3,8 @@ # Copyright (c) Jupyter Development Team. # Distributed under the terms of the Modified BSD License. +import json + from tornado import web from tornado.ioloop import IOLoop @@ -15,31 +17,32 @@ class ShutdownAPIHandler(APIHandler): def post(self): """POST /api/shutdown triggers a clean shutdown - URL parameters: + POST (JSON) parameters: - servers: specify whether single-user servers should be terminated - proxy: specify whether the proxy should be terminated """ from ..app import JupyterHub app = JupyterHub.instance() - proxy = self.get_argument('proxy', '').lower() - if proxy == 'false': - app.cleanup_proxy = False - elif proxy == 'true': - app.cleanup_proxy = True - elif proxy: - raise web.HTTPError(400, "proxy must be true or false, got %r" % proxy) - servers = self.get_argument('servers', '').lower() - if servers == 'false': - app.cleanup_servers = False - elif servers == 'true': - app.cleanup_servers = True - elif servers: - raise web.HTTPError(400, "servers must be true or false, got %r" % servers) + + data = self.get_json_body() + if data: + if 'proxy' in data: + proxy = data['proxy'] + if proxy not in {True, False}: + raise web.HTTPError(400, "proxy must be true or false, got %r" % proxy) + app.cleanup_proxy = proxy + if 'servers' in data: + servers = data['servers'] + if servers not in {True, False}: + raise web.HTTPError(400, "servers must be true or false, got %r" % servers) + app.cleanup_servers = servers # finish the request self.set_status(202) - self.finish() + self.finish(json.dumps({ + "message": "Shutting down Hub" + })) # stop the eventloop, which will trigger cleanup loop = IOLoop.current() diff --git a/share/jupyter/hub/static/js/admin.js b/share/jupyter/hub/static/js/admin.js index 5ff1d978..51b2d378 100644 --- a/share/jupyter/hub/static/js/admin.js +++ b/share/jupyter/hub/static/js/admin.js @@ -169,5 +169,21 @@ require(["jquery", "bootstrap", "moment", "jhapi", "utils"], function ($, bs, mo } }); }); + + $("#shutdown-hub").click(function () { + var dialog = $("#shutdown-hub-dialog"); + dialog.find("input[type=checkbox]").prop("checked", true); + dialog.modal(); + }); + + $("#shutdown-hub-dialog").find(".shutdown-button").click(function () { + var dialog = $("#shutdown-hub-dialog"); + var servers = dialog.find(".shutdown-servers-checkbox").prop("checked"); + var proxy = dialog.find(".shutdown-proxy-checkbox").prop("checked"); + api.shutdown_hub({ + proxy: proxy, + servers: servers, + }); + }); }); diff --git a/share/jupyter/hub/static/js/jhapi.js b/share/jupyter/hub/static/js/jhapi.js index 31051f75..15886e1e 100644 --- a/share/jupyter/hub/static/js/jhapi.js +++ b/share/jupyter/hub/static/js/jhapi.js @@ -10,7 +10,7 @@ define(['jquery', 'utils'], function ($, utils) { var default_options = { type: 'GET', - headers: {'Content-Type': 'application/json'}, + contentType: "application/json", cache: false, dataType : "json", processData: false, @@ -122,5 +122,14 @@ define(['jquery', 'utils'], function ($, utils) { ); }; + JHAPI.prototype.shutdown_hub = function (data, options) { + options = options || {}; + options = update(options, {type: 'POST'}); + if (data) { + options.data = JSON.stringify(data); + } + this.api_request('shutdown', options); + }; + return JHAPI; }); \ No newline at end of file diff --git a/share/jupyter/hub/templates/admin.html b/share/jupyter/hub/templates/admin.html index 99725deb..742bf8cf 100644 --- a/share/jupyter/hub/templates/admin.html +++ b/share/jupyter/hub/templates/admin.html @@ -32,7 +32,8 @@