Skip to content

Commit

Permalink
If running im JupyterHub, provide a dialog that prompts the user to
Browse files Browse the repository at this point in the history
restart the server.
  • Loading branch information
ian-r-rose authored and blink1073 committed Jun 7, 2019
1 parent d99a4e3 commit a33f18d
Show file tree
Hide file tree
Showing 8 changed files with 110 additions and 46 deletions.
17 changes: 1 addition & 16 deletions packages/application-extension/src/index.tsx
Expand Up @@ -2,7 +2,6 @@
// Distributed under the terms of the Modified BSD License.

import {
ConnectionLost,
IConnectionLost,
ILabShell,
ILabStatus,
Expand Down Expand Up @@ -746,19 +745,6 @@ const paths: JupyterFrontEndPlugin<JupyterFrontEnd.IPaths> = {
provides: JupyterFrontEnd.IPaths
};

/**
* The default JupyterLab connection lost provider. This may be overridden
* to provide custom behavior when a connection to the server is lost.
*/
const connectionlost: JupyterFrontEndPlugin<IConnectionLost> = {
id: '@jupyterlab/apputils-extension:connectionlost',
activate: (app: JupyterFrontEnd): IConnectionLost => {
return ConnectionLost;
},
autoStart: true,
provides: IConnectionLost
};

/**
* Export the plugins as default.
*/
Expand All @@ -773,8 +759,7 @@ const plugins: JupyterFrontEndPlugin<any>[] = [
shell,
status,
info,
paths,
connectionlost
paths
];

export default plugins;
3 changes: 2 additions & 1 deletion packages/hub-extension/package.json
Expand Up @@ -32,7 +32,8 @@
"@jupyterlab/application": "^1.0.0-alpha.9",
"@jupyterlab/apputils": "^1.0.0-alpha.9",
"@jupyterlab/coreutils": "^3.0.0-alpha.9",
"@jupyterlab/mainmenu": "^1.0.0-alpha.9"
"@jupyterlab/mainmenu": "^1.0.0-alpha.9",
"@jupyterlab/services": "^4.0.0-alpha.9"
},
"devDependencies": {
"rimraf": "~2.6.2",
Expand Down
78 changes: 76 additions & 2 deletions packages/hub-extension/src/index.ts
Expand Up @@ -3,9 +3,11 @@
| Distributed under the terms of the Modified BSD License.
|----------------------------------------------------------------------------*/

import { ICommandPalette } from '@jupyterlab/apputils';
import { Dialog, ICommandPalette, showDialog } from '@jupyterlab/apputils';

import {
ConnectionLost,
IConnectionLost,
IRouter,
JupyterFrontEnd,
JupyterFrontEndPlugin
Expand All @@ -15,13 +17,17 @@ import { URLExt } from '@jupyterlab/coreutils';

import { IMainMenu } from '@jupyterlab/mainmenu';

import { ServerConnection, ServiceManager } from '@jupyterlab/services';

/**
* The command IDs used by the plugin.
*/
export namespace CommandIDs {
export const controlPanel: string = 'hub:control-panel';

export const logout: string = 'hub:logout';

export const restart: string = 'hub:restart';
}

/**
Expand Down Expand Up @@ -50,6 +56,20 @@ function activateHubExtension(

const { commands } = app;

// TODO: use /spawn/:user/:name
// but that requires jupyterhub 1.0
// and jupyterlab to pass username, servername to PageConfig
const restartUrl =
hubHost + URLExt.join(hubPrefix, `spawn?next=${hubPrefix}home`);

commands.addCommand(CommandIDs.restart, {
label: 'Restart Server',
caption: 'Request that the Hub restart this server',
execute: () => {
window.open(restartUrl, '_blank');
}
});

commands.addCommand(CommandIDs.controlPanel, {
label: 'Hub Control Panel',
caption: 'Open the Hub control panel in a new browser tab',
Expand Down Expand Up @@ -86,4 +106,58 @@ const hubExtension: JupyterFrontEndPlugin<void> = {
autoStart: true
};

export default hubExtension;
/**
* The default JupyterLab connection lost provider. This may be overridden
* to provide custom behavior when a connection to the server is lost.
*
* If the application is being deployed within a JupyterHub context,
* this will provide a dialog that prompts the user to restart the server.
* Otherwise, it shows an error dialog.
*/
const connectionlost: JupyterFrontEndPlugin<IConnectionLost> = {
id: '@jupyterlab/apputils-extension:connectionlost',
requires: [JupyterFrontEnd.IPaths],
activate: (
app: JupyterFrontEnd,
paths: JupyterFrontEnd.IPaths
): IConnectionLost => {
const hubPrefix = paths.urls.hubPrefix || '';
const baseUrl = paths.urls.base;

// Return the default error message if not running on JupyterHub.
if (!hubPrefix) {
return ConnectionLost;
}

// If we are running on JupyterHub, return a dialog
// that prompts the user to restart their server.
let showingError = false;
const onConnectionLost: IConnectionLost = async (
manager: ServiceManager.IManager,
err: ServerConnection.NetworkError
): Promise<void> => {
if (showingError) {
return;
}
showingError = true;
const result = await showDialog({
title: 'Server Not Running',
body: `Your server at ${baseUrl} is not running.
Would you like to restart it?`,
buttons: [
Dialog.okButton({ label: 'Restart' }),
Dialog.cancelButton({ label: 'Dismiss' })
]
});
showingError = false;
if (result.button.accept) {
await app.commands.execute(CommandIDs.restart);
}
};
return onConnectionLost;
},
autoStart: true,
provides: IConnectionLost
};

export default [hubExtension, connectionlost] as JupyterFrontEndPlugin<any>[];
3 changes: 3 additions & 0 deletions packages/hub-extension/tsconfig.json
Expand Up @@ -17,6 +17,9 @@
},
{
"path": "../mainmenu"
},
{
"path": "../services"
}
]
}
13 changes: 8 additions & 5 deletions packages/services/src/kernel/manager.ts
Expand Up @@ -116,7 +116,7 @@ export class KernelManager implements Kernel.IManager {
/**
* A signal emitted when there is a connection failure.
*/
get connectionFailure(): ISignal<this, ServerConnection.NetworkError> {
get connectionFailure(): ISignal<this, Error> {
return this._connectionFailure;
}

Expand Down Expand Up @@ -280,7 +280,12 @@ export class KernelManager implements Kernel.IManager {
*/
protected async requestRunning(): Promise<void> {
const models = await Kernel.listRunning(this.serverSettings).catch(err => {
if (err instanceof ServerConnection.NetworkError) {
// Check for a network error, or a 503 error, which is returned
// by a JupyterHub when a server is shut down.
if (
err instanceof ServerConnection.NetworkError ||
(err.response && err.response.status === 503)
) {
this._connectionFailure.emit(err);
return [] as Kernel.IModel[];
}
Expand Down Expand Up @@ -354,9 +359,7 @@ export class KernelManager implements Kernel.IManager {
private _runningChanged = new Signal<this, Kernel.IModel[]>(this);
private _specs: Kernel.ISpecModels | null = null;
private _specsChanged = new Signal<this, Kernel.ISpecModels>(this);
private _connectionFailure = new Signal<this, ServerConnection.NetworkError>(
this
);
private _connectionFailure = new Signal<this, Error>(this);
}

/**
Expand Down
16 changes: 4 additions & 12 deletions packages/services/src/manager.ts
Expand Up @@ -78,7 +78,7 @@ export class ServiceManager implements ServiceManager.IManager {
/**
* A signal emitted when there is a connection failure with the kernel.
*/
get connectionFailure(): ISignal<this, ServerConnection.NetworkError> {
get connectionFailure(): ISignal<this, Error> {
return this._connectionFailure;
}

Expand Down Expand Up @@ -166,19 +166,14 @@ export class ServiceManager implements ServiceManager.IManager {
return this._readyPromise;
}

private _onConnectionFailure(
sender: any,
err: ServerConnection.NetworkError
): void {
private _onConnectionFailure(sender: any, err: Error): void {
this._connectionFailure.emit(err);
}

private _isDisposed = false;
private _readyPromise: Promise<void>;
private _specsChanged = new Signal<this, Kernel.ISpecModels>(this);
private _connectionFailure = new Signal<this, ServerConnection.NetworkError>(
this
);
private _connectionFailure = new Signal<this, Error>(this);
private _isReady = false;
}

Expand Down Expand Up @@ -253,10 +248,7 @@ export namespace ServiceManager {
/**
* A signal emitted when there is a connection failure with the server.
*/
readonly connectionFailure: ISignal<
IManager,
ServerConnection.NetworkError
>;
readonly connectionFailure: ISignal<IManager, Error>;
}

/**
Expand Down
13 changes: 8 additions & 5 deletions packages/services/src/session/manager.ts
Expand Up @@ -85,7 +85,7 @@ export class SessionManager implements Session.IManager {
/**
* A signal emitted when there is a connection failure.
*/
get connectionFailure(): ISignal<this, ServerConnection.NetworkError> {
get connectionFailure(): ISignal<this, Error> {
return this._connectionFailure;
}

Expand Down Expand Up @@ -289,7 +289,12 @@ export class SessionManager implements Session.IManager {
*/
protected async requestRunning(): Promise<void> {
const models = await Session.listRunning(this.serverSettings).catch(err => {
if (err instanceof ServerConnection.NetworkError) {
// Check for a network error, or a 503 error, which is returned
// by a JupyterHub when a server is shut down.
if (
err instanceof ServerConnection.NetworkError ||
(err.response && err.response.status === 503)
) {
this._connectionFailure.emit(err);
return [] as Session.IModel[];
}
Expand Down Expand Up @@ -380,9 +385,7 @@ export class SessionManager implements Session.IManager {
private _pollSpecs: Poll;
private _ready: Promise<void>;
private _runningChanged = new Signal<this, Session.IModel[]>(this);
private _connectionFailure = new Signal<this, ServerConnection.NetworkError>(
this
);
private _connectionFailure = new Signal<this, Error>(this);
private _sessions = new Set<Session.ISession>();
private _specs: Kernel.ISpecModels | null = null;
private _specsChanged = new Signal<this, Kernel.ISpecModels>(this);
Expand Down
13 changes: 8 additions & 5 deletions packages/services/src/terminal/manager.ts
Expand Up @@ -69,7 +69,7 @@ export class TerminalManager implements TerminalSession.IManager {
/**
* A signal emitted when there is a connection failure.
*/
get connectionFailure(): ISignal<this, ServerConnection.NetworkError> {
get connectionFailure(): ISignal<this, Error> {
return this._connectionFailure;
}

Expand Down Expand Up @@ -246,7 +246,12 @@ export class TerminalManager implements TerminalSession.IManager {
protected async requestRunning(): Promise<void> {
const models = await TerminalSession.listRunning(this.serverSettings).catch(
err => {
if (err instanceof ServerConnection.NetworkError) {
// Check for a network error, or a 503 error, which is returned
// by a JupyterHub when a server is shut down.
if (
err instanceof ServerConnection.NetworkError ||
(err.response && err.response.status === 503)
) {
this._connectionFailure.emit(err);
return [] as TerminalSession.IModel[];
}
Expand Down Expand Up @@ -325,9 +330,7 @@ export class TerminalManager implements TerminalSession.IManager {
private _sessions = new Set<TerminalSession.ISession>();
private _ready: Promise<void>;
private _runningChanged = new Signal<this, TerminalSession.IModel[]>(this);
private _connectionFailure = new Signal<this, ServerConnection.NetworkError>(
this
);
private _connectionFailure = new Signal<this, Error>(this);
}

/**
Expand Down

0 comments on commit a33f18d

Please sign in to comment.