-
Notifications
You must be signed in to change notification settings - Fork 299
/
Copy pathjupyterConnection.ts
138 lines (131 loc) · 5.69 KB
/
jupyterConnection.ts
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
// Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the MIT License.
import { inject, injectable } from 'inversify';
import { IExtensionSyncActivationService } from '../../platform/activation/types';
import { IDisposableRegistry } from '../../platform/common/types';
import { noop } from '../../platform/common/utils/misc';
import { RemoteJupyterServerUriProviderError } from '../errors/remoteJupyterServerUriProviderError';
import { BaseError } from '../../platform/errors/types';
import { IJupyterConnection } from '../types';
import {
computeServerId,
createRemoteConnectionInfo,
extractJupyterServerHandleAndId,
generateUriFromRemoteProvider
} from './jupyterUtils';
import {
IJupyterServerUri,
IJupyterServerUriStorage,
IJupyterSessionManager,
IJupyterSessionManagerFactory,
IJupyterUriProviderRegistration,
IServerConnectionType
} from './types';
/**
* Creates IJupyterConnection objects for URIs and 3rd party handles/ids.
*/
@injectable()
export class JupyterConnection implements IExtensionSyncActivationService {
private uriToJupyterServerUri = new Map<string, IJupyterServerUri>();
private pendingTimeouts: (NodeJS.Timeout | number)[] = [];
constructor(
@inject(IJupyterUriProviderRegistration)
private readonly jupyterPickerRegistration: IJupyterUriProviderRegistration,
@inject(IJupyterSessionManagerFactory)
private readonly jupyterSessionManagerFactory: IJupyterSessionManagerFactory,
@inject(IDisposableRegistry)
private readonly disposables: IDisposableRegistry,
@inject(IServerConnectionType) private readonly serverConnectionType: IServerConnectionType,
@inject(IJupyterServerUriStorage) private readonly serverUriStorage: IJupyterServerUriStorage
) {
disposables.push(this);
}
public activate() {
this.serverConnectionType.onDidChange(
() =>
// When server URI changes, clear our pending URI timeouts
this.clearTimeouts(),
this,
this.disposables
);
}
public dispose() {
this.clearTimeouts();
}
private clearTimeouts() {
// eslint-disable-next-line @typescript-eslint/no-explicit-any
this.pendingTimeouts.forEach((t) => clearTimeout(t as any));
this.pendingTimeouts = [];
}
public async createConnectionInfo(options: { serverId: string } | { uri: string }) {
const uri = 'uri' in options ? options.uri : await this.getUriFromServerId(options.serverId);
if (!uri) {
throw new Error('Server Not found');
}
return this.createConnectionInfoFromUri(uri);
}
public async validateRemoteUri(uri: string): Promise<void> {
return this.validateRemoteConnection(await this.createConnectionInfoFromUri(uri));
}
private async getUriFromServerId(serverId: string) {
// Since there's one server per session, don't use a resource to figure out these settings
const savedList = await this.serverUriStorage.getSavedUriList();
return savedList.find((item) => item.serverId === serverId)?.uri;
}
private async createConnectionInfoFromUri(uri: string) {
// Prepare our map of server URIs
await this.updateServerUri(uri);
return createRemoteConnectionInfo(uri, this.getServerUri.bind(this));
}
private async validateRemoteConnection(connection: IJupyterConnection): Promise<void> {
let sessionManager: IJupyterSessionManager | undefined = undefined;
try {
// Attempt to list the running kernels. It will return empty if there are none, but will
// throw if can't connect.
sessionManager = await this.jupyterSessionManagerFactory.create(connection, false);
await Promise.all([sessionManager.getRunningKernels(), sessionManager.getKernelSpecs()]);
// We should throw an exception if any of that fails.
} finally {
connection.dispose();
if (sessionManager) {
sessionManager.dispose().catch(noop);
}
}
}
public async updateServerUri(uri: string): Promise<void> {
const idAndHandle = extractJupyterServerHandleAndId(uri);
if (idAndHandle) {
try {
const serverUri = await this.jupyterPickerRegistration.getJupyterServerUri(
idAndHandle.id,
idAndHandle.handle
);
this.uriToJupyterServerUri.set(uri, serverUri);
// See if there's an expiration date
if (serverUri.expiration) {
const timeoutInMS = serverUri.expiration.getTime() - Date.now();
// Week seems long enough (in case the expiration is ridiculous)
if (timeoutInMS > 0 && timeoutInMS < 604800000) {
this.pendingTimeouts.push(
setTimeout(() => this.updateServerUri(uri).ignoreErrors(), timeoutInMS)
);
}
}
} catch (ex) {
if (ex instanceof BaseError) {
throw ex;
}
const serverId = await computeServerId(
generateUriFromRemoteProvider(idAndHandle.id, idAndHandle.handle)
);
throw new RemoteJupyterServerUriProviderError(idAndHandle.id, idAndHandle.handle, ex, serverId);
}
}
}
private getServerUri(uri: string): IJupyterServerUri | undefined {
const idAndHandle = extractJupyterServerHandleAndId(uri);
if (idAndHandle) {
return this.uriToJupyterServerUri.get(uri);
}
}
}