Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add status updates when connecting to compute instance. Close #1928 #1929

Merged
merged 1 commit into from
Sep 29, 2020
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion src/common/compute/interactive/message.js
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@
}(this, function() {
const Constants = makeEnum('STDOUT', 'STDERR', 'RUN', 'ADD_ARTIFACT', 'KILL',
'ADD_FILE', 'REMOVE_FILE', 'ADD_USER_DATA', 'COMPLETE', 'ERROR', 'SET_ENV',
'SAVE_ARTIFACT');
'SAVE_ARTIFACT', 'STATUS');

function makeEnum() {
const names = Array.prototype.slice.call(arguments);
Expand Down
4 changes: 2 additions & 2 deletions src/common/compute/interactive/session-with-queue.js
Original file line number Diff line number Diff line change
Expand Up @@ -63,8 +63,8 @@ define([
}
}

static async new(id, config) {
return await Session.new(id, config, SessionWithQueue);
static new(id, config) {
return Session.new(id, config, SessionWithQueue);
}
}

Expand Down
78 changes: 50 additions & 28 deletions src/common/compute/interactive/session.js
Original file line number Diff line number Diff line change
Expand Up @@ -163,38 +163,60 @@ define([
return new Session(this.channel);
}

static async new(computeID, config={}, SessionClass=InteractiveSession) {
const channel = await createMessageChannel(computeID, config);
const session = new SessionClass(channel);
return session;
}
}

async function createMessageChannel(computeID, config) {
const address = gmeConfig.extensions.InteractiveComputeHost ||
getDefaultServerURL();

const connectedWs = await new Promise((resolve, reject) => {
const ws = new WebSocket(address);
ws.onopen = () => {
ws.send(JSON.stringify([computeID, config, getGMEToken()]));
ws.onmessage = async (wsMsg) => {
const data = await Task.getMessageData(wsMsg);

const msg = Message.decode(data);
if (msg.type === Message.COMPLETE) {
const err = msg.data;
if (err) {
static new(computeID, config={}, SessionClass=InteractiveSession) {
const address = gmeConfig.extensions.InteractiveComputeHost ||
getDefaultServerURL();

let createSession;
createSession = new PromiseEvents(function(resolve, reject) {
const ws = new WebSocket(address);
ws.onopen = () => {
ws.send(JSON.stringify([computeID, config, getGMEToken()]));
ws.onmessage = async (wsMsg) => {
const data = await Task.getMessageData(wsMsg);

const msg = Message.decode(data);
if (msg.type === Message.COMPLETE) {
const err = msg.data;
if (err) {
reject(err);
} else {
const channel = new MessageChannel(ws);
const session = new SessionClass(channel);
resolve(session);
}
} else if (msg.type === Message.ERROR) {
const err = msg.data;
reject(err);
} else {
resolve(ws);
} else if (msg.type === Message.STATUS) {
createSession.emit('update', msg.data);
}
}
};
};
};
});
});

return createSession;
}
}

return new MessageChannel(connectedWs);
class PromiseEvents extends Promise {
constructor(fn) {
super(fn);
this._handlers = {};
}

on(event, fn) {
if (!this._handlers[event]) {
this._handlers[event] = [];
}
this._handlers[event].push(fn);
}

emit(event) {
const handlers = this._handlers[event] || [];
const args = Array.prototype.slice.call(arguments, 1);
handlers.forEach(fn => fn.apply(null, args));
}
}

function getDefaultServerURL() {
Expand Down
5 changes: 4 additions & 1 deletion src/routers/InteractiveCompute/Session.js
Original file line number Diff line number Diff line change
Expand Up @@ -40,11 +40,14 @@ class Session extends EventEmitter {
const name = 'DeepForge Interactive Session';
const computeJob = new ComputeJob(hash, name);
this.jobInfo = this.compute.startJob(computeJob);
this.compute.on('update', async (id, status) =>
this.clientSocket.send(Message.encode(-1, Message.STATUS, status))
);
this.compute.on('end', async (id, info) => {
const isError = this.clientSocket.readyState === WebSocket.OPEN &&
info.status !== ComputeClient.SUCCESS;
if (isError) {
this.clientSocket.send(Message.encode(Message.ERROR, info));
this.clientSocket.send(Message.encode(-1, Message.ERROR, info));
}
await this.compute.purgeJob(computeJob);
});
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,8 @@ define([
DeepForge,
) {
const COMPUTE_MESSAGE = 'Compute Required. Click to configure.';
const COMPUTE_LOADING_MESSAGE = 'Connecting to Compute Instance...';
const LoaderHTML = '<div class="lds-ripple"><div></div><div></div></div>';
class InteractiveEditorWidget {
constructor(container) {
this.showComputeShield(container);
Expand All @@ -22,13 +24,21 @@ define([
showComputeShield(container) {
const overlay = $('<div>', {class: 'compute-shield'});
container.append(overlay);
const msg = $('<span>');
msg.text(COMPUTE_MESSAGE);
overlay.append($('<div>', {class: 'filler'}));
const loader = $(LoaderHTML);
overlay.append(loader);
const msg = $('<span>', {class: 'title'});
overlay.append(msg);
const subtitle = $('<span>', {class: 'subtitle'});
overlay.append(subtitle);
msg.text(COMPUTE_MESSAGE);
loader.addClass('hidden');
subtitle.addClass('hidden');

overlay.on('click', async () => {
const {id, config} = await this.promptComputeConfig();
try {
this.session = await this.createInteractiveSession(id, config);
this.session = await this.createInteractiveSession(id, config, overlay);
const features = this.getCapabilities();
if (features.save) {
DeepForge.registerAction('Save', 'save', 10, () => this.save());
Expand All @@ -38,6 +48,10 @@ define([
} catch (err) {
const title = 'Compute Creation Error';
const body = 'Unable to create compute. Please verify the credentials are correct.';
msg.text(COMPUTE_MESSAGE);
loader.addClass('hidden');
subtitle.addClass('hidden');

// TODO: Detect authorization errors...
const dialog = new InformDialog(title, body);
dialog.show();
Expand Down Expand Up @@ -94,15 +108,43 @@ define([
return {id, config};
}

async createInteractiveSession(computeId, config) {
return await Session.new(computeId, config);
showComputeLoadingStatus(status, overlay) {
const msg = overlay.find('.subtitle');
const loader = overlay.find('.lds-ripple');
const title = overlay.find('.title');

title.text(COMPUTE_LOADING_MESSAGE);
loader.removeClass('hidden');
msg.removeClass('hidden');
return msg;
}

updateComputeLoadingStatus(status, subtitle) {
const displayText = status === 'running' ?
'Configuring environment' :
status.substring(0, 1).toUpperCase() + status.substring(1);
subtitle.text(`${displayText}...`);
}

async createInteractiveSession(computeId, config, overlay) {
const createSession = Session.new(computeId, config);

const msg = this.showComputeLoadingStatus(status, overlay);
this.updateComputeLoadingStatus('Connecting', msg);
createSession.on(
'update',
status => this.updateComputeLoadingStatus(status, msg)
);
const session = await createSession;
return session;
}

destroy() {
const features = this.getCapabilities();
if (features.save) {
DeepForge.unregisterAction('Save');
}
this.session.close();
}

updateNode(/*desc*/) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -13,10 +13,46 @@
top: 0;
z-index: 100; }

.compute-shield span {
.compute-shield .invisible {
visibility: hidden; }
.compute-shield .filler {
margin-top: 25%; }
.compute-shield .title {
color: whitesmoke;
display: block;
font-size: 2em;
margin: auto;
padding-top: 25%;
text-align: center; }
.compute-shield .subtitle {
color: whitesmoke;
display: block;
font-size: 1.5em;
margin: auto;
text-align: center; }
.compute-shield .lds-ripple {
margin: auto;
display: block;
position: relative;
width: 80px;
height: 80px; }
.compute-shield .lds-ripple div {
position: absolute;
border: 4px solid #fff;
opacity: 1;
border-radius: 50%;
animation: lds-ripple 1s cubic-bezier(0, 0.2, 0.8, 1) infinite; }
.compute-shield .lds-ripple div:nth-child(2) {
animation-delay: -0.5s; }
@keyframes lds-ripple {
0% {
top: 36px;
left: 36px;
width: 0;
height: 0;
opacity: 1; }
100% {
top: 0px;
left: 0px;
width: 72px;
height: 72px;
opacity: 0; } }
Original file line number Diff line number Diff line change
Expand Up @@ -16,11 +16,62 @@
z-index: 100;
}

.compute-shield span {
color: whitesmoke;
display: block;
font-size: 2em;
margin: auto;
padding-top: 25%;
text-align: center;
.compute-shield {
.hidden {
display: none;
}

.filler {
margin-top: 25%;
}

.title {
color: whitesmoke;
display: block;
font-size: 2em;
margin: auto;
text-align: center;
}

.subtitle {
color: whitesmoke;
display: block;
font-size: 1.5em;
margin: auto;
text-align: center;
}

.lds-ripple {
margin: auto;
display: block;
position: relative;
width: 80px;
height: 80px;
}
.lds-ripple div {
position: absolute;
border: 4px solid #fff;
opacity: 1;
border-radius: 50%;
animation: lds-ripple 1s cubic-bezier(0, 0.2, 0.8, 1) infinite;
}
.lds-ripple div:nth-child(2) {
animation-delay: -0.5s;
}
@keyframes lds-ripple {
0% {
top: 36px;
left: 36px;
width: 0;
height: 0;
opacity: 1;
}
100% {
top: 0px;
left: 0px;
width: 72px;
height: 72px;
opacity: 0;
}
}
}
11 changes: 2 additions & 9 deletions src/visualizers/widgets/TensorPlotter/TensorPlotterWidget.js
Original file line number Diff line number Diff line change
Expand Up @@ -64,13 +64,6 @@ define([
this._logger.debug('ctor finished');
}

async createInteractiveSession(computeId, config) {
const session = await Session.new(computeId, config);
this.initSession(session);
this.artifactLoader.session = session;
return session;
}

async getAuthenticationConfig (dataInfo) {
const {backend} = dataInfo;
const metadata = Storage.getStorageMetadata(backend);
Expand All @@ -87,8 +80,8 @@ define([
}
}

async initSession (session) {
await session.whenConnected();
async onComputeInitialized (session) {
this.artifactLoader.session = session;
const initCode = await this.getInitializationCode();
await session.addFile('utils/init.py', initCode);
await session.addFile('utils/explorer_helpers.py', HELPERS_PY);
Expand Down