@norskvideo/norsk-manager-sdk 1.0.356
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/lib/src/sdk.d.ts +629 -0
- package/lib/src/sdk.js +1423 -0
- package/lib/src/shared/utils.d.ts +17 -0
- package/lib/src/shared/utils.js +176 -0
- package/package.json +41 -0
- package/src/sdk.ts +2423 -0
- package/tsconfig.json +20 -0
package/src/sdk.ts
ADDED
|
@@ -0,0 +1,2423 @@
|
|
|
1
|
+
import * as grpc from "@grpc/grpc-js";
|
|
2
|
+
import { Empty, Timestamp } from "@bufbuild/protobuf";
|
|
3
|
+
import {
|
|
4
|
+
Version as CommonVersion,
|
|
5
|
+
Log_Level,
|
|
6
|
+
} from "@norskvideo/norsk-api/lib/shared/common_pb";
|
|
7
|
+
import { ManagerClient } from "@norskvideo/norsk-api/lib/manager_grpc_pb";
|
|
8
|
+
import * as CommonPB from "@norskvideo/norsk-api/lib/shared/common_pb";
|
|
9
|
+
import * as ManagerPB from "@norskvideo/norsk-api/lib/manager_pb";
|
|
10
|
+
import { debuglog, provideFull } from "./shared/utils";
|
|
11
|
+
import * as utils from "./shared/utils";
|
|
12
|
+
import * as util from "util";
|
|
13
|
+
import { SurfaceCall } from "@grpc/grpc-js/build/src/call";
|
|
14
|
+
|
|
15
|
+
// TODOs
|
|
16
|
+
//
|
|
17
|
+
// manager will need public & private ports to listen for grpc traffic
|
|
18
|
+
// - public port will need client-cert provided - all startup config (until we do real saas)
|
|
19
|
+
//
|
|
20
|
+
|
|
21
|
+
/** @public */
|
|
22
|
+
export type JobId = string;
|
|
23
|
+
|
|
24
|
+
/** @internal */
|
|
25
|
+
function toJobId(jobId: JobId): CommonPB.JobId {
|
|
26
|
+
return provideFull(CommonPB.JobId, { jobId: jobId });
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
/** @internal */
|
|
30
|
+
function fromJobId(jobId: CommonPB.JobId): JobId {
|
|
31
|
+
return jobId.jobId;
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
/** @public */
|
|
35
|
+
export type Role = string;
|
|
36
|
+
|
|
37
|
+
/** @public */
|
|
38
|
+
export type ContainerUrl = string;
|
|
39
|
+
|
|
40
|
+
/** @internal */
|
|
41
|
+
function toContainerUrl(url: ContainerUrl): CommonPB.ContainerUrl {
|
|
42
|
+
return provideFull(CommonPB.ContainerUrl, {
|
|
43
|
+
url,
|
|
44
|
+
});
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
/** @internal */
|
|
48
|
+
function fromContainerUrl(containerUrl: CommonPB.ContainerUrl): ContainerUrl {
|
|
49
|
+
return containerUrl.url;
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
/** @public */
|
|
53
|
+
export type DockerContainer = { codeType: "docker"; url: ContainerUrl };
|
|
54
|
+
|
|
55
|
+
/** @internal */
|
|
56
|
+
function toDockerContainer(
|
|
57
|
+
container: DockerContainer
|
|
58
|
+
): CommonPB.DockerContainer {
|
|
59
|
+
return provideFull(CommonPB.DockerContainer, {
|
|
60
|
+
containerUrl: toContainerUrl(container.url),
|
|
61
|
+
});
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
/** @internal */
|
|
65
|
+
function fromDockerContainer(
|
|
66
|
+
container: CommonPB.DockerContainer
|
|
67
|
+
): DockerContainer {
|
|
68
|
+
return {
|
|
69
|
+
codeType: "docker",
|
|
70
|
+
url: fromContainerUrl(utils.mandatory(container.containerUrl)),
|
|
71
|
+
};
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
/** @public */
|
|
75
|
+
export type ClientCode = DockerContainer;
|
|
76
|
+
|
|
77
|
+
/** @internal */
|
|
78
|
+
export type ClientCodePB = CommonPB.Service["container"];
|
|
79
|
+
|
|
80
|
+
/** @internal */
|
|
81
|
+
function toClientCode(clientCode: ClientCode): ClientCodePB {
|
|
82
|
+
switch (clientCode.codeType) {
|
|
83
|
+
case "docker":
|
|
84
|
+
return utils.mkCase({ docker: toDockerContainer(clientCode) });
|
|
85
|
+
}
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
/** @internal */
|
|
89
|
+
function fromClientCode(clientCode: ClientCodePB): ClientCode {
|
|
90
|
+
switch (clientCode.case) {
|
|
91
|
+
case "docker":
|
|
92
|
+
return fromDockerContainer(clientCode.value);
|
|
93
|
+
case undefined:
|
|
94
|
+
throw new Error();
|
|
95
|
+
}
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
/** @public */
|
|
99
|
+
export type JobHistoryJobCreated = {
|
|
100
|
+
event: "created";
|
|
101
|
+
timestamp: Date;
|
|
102
|
+
};
|
|
103
|
+
|
|
104
|
+
/** @public */
|
|
105
|
+
export type JobHistoryJobUpdated = {
|
|
106
|
+
event: "updated";
|
|
107
|
+
timestamp: Date;
|
|
108
|
+
previousJob: Job;
|
|
109
|
+
};
|
|
110
|
+
|
|
111
|
+
/** @public */
|
|
112
|
+
export type JobHistoryJobProvisioned = {
|
|
113
|
+
event: "provisioned";
|
|
114
|
+
timestamp: Date;
|
|
115
|
+
role: Role;
|
|
116
|
+
nodeMetadata: NodeMetadata;
|
|
117
|
+
};
|
|
118
|
+
|
|
119
|
+
/** @public */
|
|
120
|
+
export type JobHistoryJobRunning = {
|
|
121
|
+
event: "running";
|
|
122
|
+
timestamp: Date;
|
|
123
|
+
role: Role;
|
|
124
|
+
nodeMetadata: NodeMetadata;
|
|
125
|
+
runningNodeMetadata: RunningNodeMetadata;
|
|
126
|
+
};
|
|
127
|
+
|
|
128
|
+
/** @public */
|
|
129
|
+
export type JobHistoryJobStopping = {
|
|
130
|
+
event: "stopping";
|
|
131
|
+
timestamp: Date;
|
|
132
|
+
role: Role;
|
|
133
|
+
nodeMetadata: NodeMetadata;
|
|
134
|
+
runningNodeMetadata?: RunningNodeMetadata;
|
|
135
|
+
reason:
|
|
136
|
+
| "nodeStopped"
|
|
137
|
+
| "nodeTerminated"
|
|
138
|
+
| "userRequested"
|
|
139
|
+
| "unknownJob"
|
|
140
|
+
| "jobFailed";
|
|
141
|
+
};
|
|
142
|
+
|
|
143
|
+
/** @public */
|
|
144
|
+
export type JobHistoryJobStopped = {
|
|
145
|
+
event: "stopped";
|
|
146
|
+
timestamp: Date;
|
|
147
|
+
role: Role;
|
|
148
|
+
nodeMetadata: NodeMetadata;
|
|
149
|
+
runningNodeMetadata?: RunningNodeMetadata;
|
|
150
|
+
};
|
|
151
|
+
|
|
152
|
+
/** @public */
|
|
153
|
+
export type JobHistoryJobCompleted = {
|
|
154
|
+
event: "completed";
|
|
155
|
+
timestamp: Date;
|
|
156
|
+
};
|
|
157
|
+
|
|
158
|
+
/** @public */
|
|
159
|
+
export type JobHistoryEntry =
|
|
160
|
+
| JobHistoryJobCreated
|
|
161
|
+
| JobHistoryJobUpdated
|
|
162
|
+
| JobHistoryJobProvisioned
|
|
163
|
+
| JobHistoryJobRunning
|
|
164
|
+
| JobHistoryJobStopping
|
|
165
|
+
| JobHistoryJobStopped
|
|
166
|
+
| JobHistoryJobCompleted;
|
|
167
|
+
|
|
168
|
+
/** @public */
|
|
169
|
+
export type JobState = "pre" | "active" | "post";
|
|
170
|
+
|
|
171
|
+
/** @public */
|
|
172
|
+
export type ServiceRestart = "never" | "onFailure" | "always";
|
|
173
|
+
|
|
174
|
+
/** @public */
|
|
175
|
+
export type RestartIntensity = {
|
|
176
|
+
count: number;
|
|
177
|
+
period: number;
|
|
178
|
+
};
|
|
179
|
+
|
|
180
|
+
/** @public */
|
|
181
|
+
export type EphemeralHostPort = {
|
|
182
|
+
type: "ephemeral";
|
|
183
|
+
};
|
|
184
|
+
|
|
185
|
+
/** @public */
|
|
186
|
+
export type RangeHostPort = {
|
|
187
|
+
type: "range";
|
|
188
|
+
minPort: number;
|
|
189
|
+
maxPort: number;
|
|
190
|
+
};
|
|
191
|
+
|
|
192
|
+
/** @public */
|
|
193
|
+
export type SpecificHostPort = {
|
|
194
|
+
type: "specific";
|
|
195
|
+
port: number;
|
|
196
|
+
};
|
|
197
|
+
|
|
198
|
+
/** @public */
|
|
199
|
+
export type HostPort = EphemeralHostPort | RangeHostPort | SpecificHostPort;
|
|
200
|
+
|
|
201
|
+
/** @public */
|
|
202
|
+
export type PortMapping = {
|
|
203
|
+
containerPort: number;
|
|
204
|
+
protocol?: "tcp" | "udp";
|
|
205
|
+
hostPort: HostPort;
|
|
206
|
+
hostAddress?: string;
|
|
207
|
+
};
|
|
208
|
+
|
|
209
|
+
/** @public */
|
|
210
|
+
export type EnvironmentVariable = {
|
|
211
|
+
name: string;
|
|
212
|
+
value: string;
|
|
213
|
+
};
|
|
214
|
+
|
|
215
|
+
/** @public */
|
|
216
|
+
export type VolumeMount = {
|
|
217
|
+
volumeName: string;
|
|
218
|
+
containerPath: string;
|
|
219
|
+
readOnly: boolean;
|
|
220
|
+
};
|
|
221
|
+
|
|
222
|
+
/** @public */
|
|
223
|
+
export type ServiceParameters = {
|
|
224
|
+
portMappings?: PortMapping[];
|
|
225
|
+
environmentVariables?: EnvironmentVariable[];
|
|
226
|
+
sharedMemorySize?: bigint;
|
|
227
|
+
cpuSet?: number[];
|
|
228
|
+
volumeMounts?: VolumeMount[];
|
|
229
|
+
};
|
|
230
|
+
|
|
231
|
+
/** @public */
|
|
232
|
+
export type Service = {
|
|
233
|
+
name: string;
|
|
234
|
+
container: ClientCode;
|
|
235
|
+
restart?: ServiceRestart;
|
|
236
|
+
dependsOn?: Set<string>;
|
|
237
|
+
configuration?: string;
|
|
238
|
+
restartIntensity?: RestartIntensity;
|
|
239
|
+
command?: string;
|
|
240
|
+
parameters?: ServiceParameters;
|
|
241
|
+
};
|
|
242
|
+
|
|
243
|
+
/** @public */
|
|
244
|
+
export type Job = {
|
|
245
|
+
jobId: JobId;
|
|
246
|
+
cloud: Cloud;
|
|
247
|
+
description: string;
|
|
248
|
+
tags: Map<string, string>;
|
|
249
|
+
startDateTime: Date;
|
|
250
|
+
currentHash: bigint;
|
|
251
|
+
services: Service[];
|
|
252
|
+
volumes?: string[];
|
|
253
|
+
managerConfiguration?: string;
|
|
254
|
+
norskMediaVersion: Version;
|
|
255
|
+
norskServiceParameters?: ServiceParameters;
|
|
256
|
+
state: JobState;
|
|
257
|
+
shape: string;
|
|
258
|
+
availabilityDomain: string;
|
|
259
|
+
subnet: string;
|
|
260
|
+
architecture: string;
|
|
261
|
+
};
|
|
262
|
+
|
|
263
|
+
/** @public */
|
|
264
|
+
export type CreateJob = Omit<Job, "state" | "currentHash">;
|
|
265
|
+
|
|
266
|
+
/** @public */
|
|
267
|
+
export type JobWithHistory = {
|
|
268
|
+
job: Job;
|
|
269
|
+
history: JobHistoryEntry[];
|
|
270
|
+
};
|
|
271
|
+
|
|
272
|
+
/** @internal */
|
|
273
|
+
function toJobState(state: JobState): ManagerPB.Job_JobState {
|
|
274
|
+
switch (state) {
|
|
275
|
+
case "pre":
|
|
276
|
+
return ManagerPB.Job_JobState.PRE;
|
|
277
|
+
case "active":
|
|
278
|
+
return ManagerPB.Job_JobState.ACTIVE;
|
|
279
|
+
case "post":
|
|
280
|
+
return ManagerPB.Job_JobState.POST;
|
|
281
|
+
}
|
|
282
|
+
}
|
|
283
|
+
|
|
284
|
+
/** @internal */
|
|
285
|
+
function toPortMapping({
|
|
286
|
+
containerPort,
|
|
287
|
+
protocol,
|
|
288
|
+
hostPort,
|
|
289
|
+
hostAddress,
|
|
290
|
+
}: PortMapping): CommonPB.PortMapping {
|
|
291
|
+
var protocolPB: CommonPB.ProtocolFamily;
|
|
292
|
+
var hostPortPB: CommonPB.PortMapping["hostPort"];
|
|
293
|
+
switch (protocol) {
|
|
294
|
+
case "tcp":
|
|
295
|
+
protocolPB = CommonPB.ProtocolFamily.TCP;
|
|
296
|
+
break;
|
|
297
|
+
case "udp":
|
|
298
|
+
protocolPB = CommonPB.ProtocolFamily.UDP;
|
|
299
|
+
break;
|
|
300
|
+
case undefined:
|
|
301
|
+
protocolPB = CommonPB.ProtocolFamily.ANY;
|
|
302
|
+
break;
|
|
303
|
+
}
|
|
304
|
+
switch (hostPort.type) {
|
|
305
|
+
case "ephemeral":
|
|
306
|
+
hostPortPB = {
|
|
307
|
+
case: "ephemeral",
|
|
308
|
+
value: provideFull(CommonPB.HostPortEphemeral, {}),
|
|
309
|
+
};
|
|
310
|
+
break;
|
|
311
|
+
case "range":
|
|
312
|
+
hostPortPB = {
|
|
313
|
+
case: "range",
|
|
314
|
+
value: provideFull(CommonPB.HostPortRange, {
|
|
315
|
+
minPort: hostPort.minPort,
|
|
316
|
+
maxPort: hostPort.maxPort,
|
|
317
|
+
}),
|
|
318
|
+
};
|
|
319
|
+
break;
|
|
320
|
+
case "specific":
|
|
321
|
+
hostPortPB = {
|
|
322
|
+
case: "specific",
|
|
323
|
+
value: provideFull(CommonPB.HostPortSpecific, {
|
|
324
|
+
port: hostPort.port,
|
|
325
|
+
}),
|
|
326
|
+
};
|
|
327
|
+
break;
|
|
328
|
+
}
|
|
329
|
+
return provideFull(CommonPB.PortMapping, {
|
|
330
|
+
containerPort,
|
|
331
|
+
protocol: protocolPB,
|
|
332
|
+
hostPort: hostPortPB,
|
|
333
|
+
hostAddress: hostAddress || "",
|
|
334
|
+
});
|
|
335
|
+
}
|
|
336
|
+
|
|
337
|
+
/** @internal */
|
|
338
|
+
function fromPortMapping({
|
|
339
|
+
containerPort,
|
|
340
|
+
protocol: protocolPB,
|
|
341
|
+
hostPort: hostPortPB,
|
|
342
|
+
hostAddress,
|
|
343
|
+
}: CommonPB.PortMapping): PortMapping {
|
|
344
|
+
var protocol: PortMapping["protocol"];
|
|
345
|
+
var hostPort: HostPort;
|
|
346
|
+
switch (protocolPB) {
|
|
347
|
+
case CommonPB.ProtocolFamily.TCP:
|
|
348
|
+
protocol = "tcp";
|
|
349
|
+
break;
|
|
350
|
+
case CommonPB.ProtocolFamily.UDP:
|
|
351
|
+
protocol = "udp";
|
|
352
|
+
break;
|
|
353
|
+
case CommonPB.ProtocolFamily.ANY:
|
|
354
|
+
protocol = undefined;
|
|
355
|
+
break;
|
|
356
|
+
}
|
|
357
|
+
switch (hostPortPB.case) {
|
|
358
|
+
case "ephemeral":
|
|
359
|
+
hostPort = { type: "ephemeral" };
|
|
360
|
+
break;
|
|
361
|
+
case "range":
|
|
362
|
+
hostPort = {
|
|
363
|
+
type: "range",
|
|
364
|
+
minPort: hostPortPB.value.minPort,
|
|
365
|
+
maxPort: hostPortPB.value.maxPort,
|
|
366
|
+
};
|
|
367
|
+
break;
|
|
368
|
+
case "specific":
|
|
369
|
+
hostPort = {
|
|
370
|
+
type: "specific",
|
|
371
|
+
port: hostPortPB.value.port,
|
|
372
|
+
};
|
|
373
|
+
break;
|
|
374
|
+
case undefined:
|
|
375
|
+
hostPort = { type: "ephemeral" };
|
|
376
|
+
break;
|
|
377
|
+
}
|
|
378
|
+
return {
|
|
379
|
+
containerPort,
|
|
380
|
+
protocol,
|
|
381
|
+
hostPort,
|
|
382
|
+
hostAddress: hostAddress || "",
|
|
383
|
+
};
|
|
384
|
+
}
|
|
385
|
+
|
|
386
|
+
/** @internal */
|
|
387
|
+
function toEnvironmentVariable(
|
|
388
|
+
env: EnvironmentVariable
|
|
389
|
+
): CommonPB.EnvironmentVariable {
|
|
390
|
+
return provideFull(CommonPB.EnvironmentVariable, env);
|
|
391
|
+
}
|
|
392
|
+
|
|
393
|
+
/** @internal */
|
|
394
|
+
function fromEnvironmentVariable({
|
|
395
|
+
name,
|
|
396
|
+
value,
|
|
397
|
+
}: CommonPB.EnvironmentVariable): EnvironmentVariable {
|
|
398
|
+
return { name, value };
|
|
399
|
+
}
|
|
400
|
+
|
|
401
|
+
/** @internal */
|
|
402
|
+
function toServiceParameters({
|
|
403
|
+
portMappings,
|
|
404
|
+
environmentVariables,
|
|
405
|
+
sharedMemorySize,
|
|
406
|
+
cpuSet,
|
|
407
|
+
volumeMounts,
|
|
408
|
+
}: ServiceParameters): CommonPB.ServiceParameters {
|
|
409
|
+
return provideFull(CommonPB.ServiceParameters, {
|
|
410
|
+
portMappings: portMappings ? portMappings.map(toPortMapping) : [],
|
|
411
|
+
environmentVariables: environmentVariables
|
|
412
|
+
? environmentVariables.map(toEnvironmentVariable)
|
|
413
|
+
: [],
|
|
414
|
+
sharedMemorySize: sharedMemorySize
|
|
415
|
+
? provideFull(CommonPB.OptionalInt64, { value: sharedMemorySize })
|
|
416
|
+
: undefined,
|
|
417
|
+
cpuSet: cpuSet || [],
|
|
418
|
+
volumeMounts: volumeMounts
|
|
419
|
+
? volumeMounts.map((v) => provideFull(CommonPB.VolumeMount, v))
|
|
420
|
+
: [],
|
|
421
|
+
});
|
|
422
|
+
}
|
|
423
|
+
|
|
424
|
+
/** @internal */
|
|
425
|
+
function fromServiceParameters({
|
|
426
|
+
portMappings,
|
|
427
|
+
environmentVariables,
|
|
428
|
+
sharedMemorySize,
|
|
429
|
+
cpuSet,
|
|
430
|
+
volumeMounts,
|
|
431
|
+
}: CommonPB.ServiceParameters): ServiceParameters {
|
|
432
|
+
return {
|
|
433
|
+
portMappings: portMappings.map(fromPortMapping),
|
|
434
|
+
environmentVariables: environmentVariables.map(fromEnvironmentVariable),
|
|
435
|
+
sharedMemorySize: sharedMemorySize ? sharedMemorySize.value : undefined,
|
|
436
|
+
cpuSet,
|
|
437
|
+
volumeMounts,
|
|
438
|
+
};
|
|
439
|
+
}
|
|
440
|
+
|
|
441
|
+
/** @internal */
|
|
442
|
+
function toService({
|
|
443
|
+
name,
|
|
444
|
+
container,
|
|
445
|
+
restart,
|
|
446
|
+
dependsOn,
|
|
447
|
+
configuration,
|
|
448
|
+
restartIntensity,
|
|
449
|
+
command,
|
|
450
|
+
parameters,
|
|
451
|
+
}: Service): CommonPB.Service {
|
|
452
|
+
var restartPB;
|
|
453
|
+
switch (restart) {
|
|
454
|
+
case "never":
|
|
455
|
+
restartPB = CommonPB.Service_ServiceRestart.NEVER;
|
|
456
|
+
break;
|
|
457
|
+
case "onFailure":
|
|
458
|
+
restartPB = CommonPB.Service_ServiceRestart.ON_FAILURE;
|
|
459
|
+
break;
|
|
460
|
+
case "always":
|
|
461
|
+
restartPB = CommonPB.Service_ServiceRestart.ALWAYS;
|
|
462
|
+
break;
|
|
463
|
+
case undefined:
|
|
464
|
+
restartPB = CommonPB.Service_ServiceRestart.ALWAYS;
|
|
465
|
+
break;
|
|
466
|
+
}
|
|
467
|
+
return provideFull(CommonPB.Service, {
|
|
468
|
+
name,
|
|
469
|
+
container: toClientCode(container),
|
|
470
|
+
restart: restartPB,
|
|
471
|
+
dependsOn: Array.from(dependsOn?.keys() || []),
|
|
472
|
+
configuration: configuration || "",
|
|
473
|
+
restartCount: restartIntensity ? restartIntensity.count : 5,
|
|
474
|
+
restartPeriod: restartIntensity ? restartIntensity.period : 1000,
|
|
475
|
+
command: command || "",
|
|
476
|
+
parameters: parameters ? toServiceParameters(parameters) : undefined,
|
|
477
|
+
});
|
|
478
|
+
}
|
|
479
|
+
|
|
480
|
+
/** @internal */
|
|
481
|
+
function fromService({
|
|
482
|
+
name,
|
|
483
|
+
container,
|
|
484
|
+
restart: restartPB,
|
|
485
|
+
dependsOn: dependsOnPB,
|
|
486
|
+
configuration,
|
|
487
|
+
restartCount,
|
|
488
|
+
restartPeriod,
|
|
489
|
+
command,
|
|
490
|
+
parameters,
|
|
491
|
+
}: CommonPB.Service): Service {
|
|
492
|
+
var restart: ServiceRestart;
|
|
493
|
+
switch (restartPB) {
|
|
494
|
+
case CommonPB.Service_ServiceRestart.NEVER:
|
|
495
|
+
restart = "never";
|
|
496
|
+
break;
|
|
497
|
+
case CommonPB.Service_ServiceRestart.ON_FAILURE:
|
|
498
|
+
restart = "onFailure";
|
|
499
|
+
break;
|
|
500
|
+
case CommonPB.Service_ServiceRestart.ALWAYS:
|
|
501
|
+
restart = "always";
|
|
502
|
+
break;
|
|
503
|
+
}
|
|
504
|
+
const dependsOn: Set<string> = dependsOnPB.reduce(
|
|
505
|
+
(acc, val) => acc.add(val),
|
|
506
|
+
new Set<string>()
|
|
507
|
+
);
|
|
508
|
+
return {
|
|
509
|
+
name,
|
|
510
|
+
container: fromClientCode(container),
|
|
511
|
+
restart,
|
|
512
|
+
dependsOn,
|
|
513
|
+
configuration,
|
|
514
|
+
restartIntensity: { count: restartCount, period: restartPeriod },
|
|
515
|
+
command,
|
|
516
|
+
parameters: parameters ? fromServiceParameters(parameters) : undefined,
|
|
517
|
+
};
|
|
518
|
+
}
|
|
519
|
+
|
|
520
|
+
/** @internal */
|
|
521
|
+
function toJob({
|
|
522
|
+
jobId,
|
|
523
|
+
description,
|
|
524
|
+
tags,
|
|
525
|
+
startDateTime,
|
|
526
|
+
currentHash = BigInt(0),
|
|
527
|
+
services,
|
|
528
|
+
volumes,
|
|
529
|
+
managerConfiguration,
|
|
530
|
+
norskMediaVersion,
|
|
531
|
+
norskServiceParameters,
|
|
532
|
+
state,
|
|
533
|
+
shape,
|
|
534
|
+
availabilityDomain,
|
|
535
|
+
subnet,
|
|
536
|
+
architecture,
|
|
537
|
+
}: Job): ManagerPB.Job {
|
|
538
|
+
return provideFull(ManagerPB.Job, {
|
|
539
|
+
jobId: toJobId(jobId),
|
|
540
|
+
description: description,
|
|
541
|
+
tags: Object.fromEntries(tags.entries()),
|
|
542
|
+
startDateTime: Timestamp.fromDate(startDateTime),
|
|
543
|
+
currentHash: currentHash,
|
|
544
|
+
services: services.map(toService),
|
|
545
|
+
volumes: volumes || [],
|
|
546
|
+
managerConfiguration: managerConfiguration || "",
|
|
547
|
+
norskMediaVersion: toVersion(norskMediaVersion),
|
|
548
|
+
norskServiceParameters: norskServiceParameters
|
|
549
|
+
? toServiceParameters(norskServiceParameters)
|
|
550
|
+
: undefined,
|
|
551
|
+
state: toJobState(state),
|
|
552
|
+
shape,
|
|
553
|
+
availabilityDomain,
|
|
554
|
+
subnet,
|
|
555
|
+
architecture
|
|
556
|
+
});
|
|
557
|
+
}
|
|
558
|
+
|
|
559
|
+
/** @internal */
|
|
560
|
+
function fromJobState(state: ManagerPB.Job_JobState): JobState {
|
|
561
|
+
switch (state) {
|
|
562
|
+
case ManagerPB.Job_JobState.PRE:
|
|
563
|
+
return "pre";
|
|
564
|
+
case ManagerPB.Job_JobState.ACTIVE:
|
|
565
|
+
return "active";
|
|
566
|
+
case ManagerPB.Job_JobState.POST:
|
|
567
|
+
return "post";
|
|
568
|
+
}
|
|
569
|
+
}
|
|
570
|
+
|
|
571
|
+
/** @internal */
|
|
572
|
+
function fromJob(job: ManagerPB.Job): Job {
|
|
573
|
+
return {
|
|
574
|
+
jobId: fromJobId(utils.mandatory(job.jobId)),
|
|
575
|
+
cloud: job.architecture == "" ? "aws" : "oci",
|
|
576
|
+
description: job.description,
|
|
577
|
+
tags: new Map(Object.entries(job.tags)),
|
|
578
|
+
startDateTime: utils.mandatory(job.startDateTime).toDate(),
|
|
579
|
+
currentHash: job.currentHash,
|
|
580
|
+
services: job.services.map(fromService),
|
|
581
|
+
volumes: job.volumes,
|
|
582
|
+
managerConfiguration: job.managerConfiguration,
|
|
583
|
+
norskMediaVersion: fromVersion(job.norskMediaVersion),
|
|
584
|
+
norskServiceParameters: job.norskServiceParameters
|
|
585
|
+
? fromServiceParameters(job.norskServiceParameters)
|
|
586
|
+
: undefined,
|
|
587
|
+
state: fromJobState(job.state),
|
|
588
|
+
shape: job.shape,
|
|
589
|
+
availabilityDomain: job.availabilityDomain,
|
|
590
|
+
subnet: job.subnet,
|
|
591
|
+
architecture: job.architecture
|
|
592
|
+
};
|
|
593
|
+
}
|
|
594
|
+
|
|
595
|
+
/** @internal */
|
|
596
|
+
function fromJobHistoryJobCreated(
|
|
597
|
+
entry: ManagerPB.JobHistoryJobCreated
|
|
598
|
+
): JobHistoryJobCreated {
|
|
599
|
+
return {
|
|
600
|
+
event: "created",
|
|
601
|
+
timestamp: utils.mandatory(entry.timestamp).toDate(),
|
|
602
|
+
};
|
|
603
|
+
}
|
|
604
|
+
|
|
605
|
+
/** @internal */
|
|
606
|
+
function fromJobHistoryJobUpdated(
|
|
607
|
+
entry: ManagerPB.JobHistoryJobUpdated
|
|
608
|
+
): JobHistoryJobUpdated {
|
|
609
|
+
return {
|
|
610
|
+
event: "updated",
|
|
611
|
+
timestamp: utils.mandatory(entry.timestamp).toDate(),
|
|
612
|
+
previousJob: fromJob(utils.mandatory(entry.previousJob)),
|
|
613
|
+
};
|
|
614
|
+
}
|
|
615
|
+
|
|
616
|
+
/** @internal */
|
|
617
|
+
function fromJobHistoryJobProvisioned(
|
|
618
|
+
entry: ManagerPB.JobHistoryJobProvisioned
|
|
619
|
+
): JobHistoryJobProvisioned {
|
|
620
|
+
return {
|
|
621
|
+
event: "provisioned",
|
|
622
|
+
timestamp: utils.mandatory(entry.timestamp).toDate(),
|
|
623
|
+
role: entry.role,
|
|
624
|
+
nodeMetadata: fromNodeMetadata(utils.mandatory(entry.nodeMetadata)),
|
|
625
|
+
};
|
|
626
|
+
}
|
|
627
|
+
|
|
628
|
+
/** @internal */
|
|
629
|
+
function fromJobHistoryJobRunning(
|
|
630
|
+
entry: ManagerPB.JobHistoryJobRunning
|
|
631
|
+
): JobHistoryJobRunning {
|
|
632
|
+
return {
|
|
633
|
+
event: "running",
|
|
634
|
+
timestamp: utils.mandatory(entry.timestamp).toDate(),
|
|
635
|
+
role: entry.role,
|
|
636
|
+
nodeMetadata: fromNodeMetadata(utils.mandatory(entry.nodeMetadata)),
|
|
637
|
+
runningNodeMetadata: fromRunningNodeMetadata(
|
|
638
|
+
utils.mandatory(entry.runningNodeMetadata)
|
|
639
|
+
),
|
|
640
|
+
};
|
|
641
|
+
}
|
|
642
|
+
|
|
643
|
+
/** @internal */
|
|
644
|
+
function fromJobHistoryJobStopping(
|
|
645
|
+
entry: ManagerPB.JobHistoryJobStopping
|
|
646
|
+
): JobHistoryJobStopping {
|
|
647
|
+
let reason: JobHistoryJobStopping["reason"];
|
|
648
|
+
switch (entry.reason) {
|
|
649
|
+
case ManagerPB.JobHistoryJobStopping_Reason.JOB_STOPPED_NODE_STOPPED:
|
|
650
|
+
reason = "nodeStopped";
|
|
651
|
+
break;
|
|
652
|
+
case ManagerPB.JobHistoryJobStopping_Reason.JOB_STOPPED_NODE_TERMINATED:
|
|
653
|
+
reason = "nodeTerminated";
|
|
654
|
+
break;
|
|
655
|
+
case ManagerPB.JobHistoryJobStopping_Reason.JOB_STOPPED_USER_REQUESTED:
|
|
656
|
+
reason = "userRequested";
|
|
657
|
+
break;
|
|
658
|
+
case ManagerPB.JobHistoryJobStopping_Reason.JOB_STOPPED_UNKNOWN_JOB:
|
|
659
|
+
reason = "unknownJob";
|
|
660
|
+
break;
|
|
661
|
+
case ManagerPB.JobHistoryJobStopping_Reason.JOB_STOPPED_JOB_FAILED:
|
|
662
|
+
reason = "jobFailed";
|
|
663
|
+
break;
|
|
664
|
+
default: {
|
|
665
|
+
const exhaustiveCheck: never = entry.reason;
|
|
666
|
+
throw new Error(`Unhandled case: ${exhaustiveCheck}`);
|
|
667
|
+
}
|
|
668
|
+
}
|
|
669
|
+
return {
|
|
670
|
+
event: "stopping",
|
|
671
|
+
timestamp: utils.mandatory(entry.timestamp).toDate(),
|
|
672
|
+
role: entry.role,
|
|
673
|
+
nodeMetadata: fromNodeMetadata(utils.mandatory(entry.nodeMetadata)),
|
|
674
|
+
runningNodeMetadata: utils.mapOptional(
|
|
675
|
+
fromRunningNodeMetadata,
|
|
676
|
+
entry.runningNodeMetadata
|
|
677
|
+
),
|
|
678
|
+
reason: reason,
|
|
679
|
+
};
|
|
680
|
+
}
|
|
681
|
+
|
|
682
|
+
/** @internal */
|
|
683
|
+
function fromJobHistoryJobStopped(
|
|
684
|
+
entry: ManagerPB.JobHistoryJobStopped
|
|
685
|
+
): JobHistoryJobStopped {
|
|
686
|
+
return {
|
|
687
|
+
event: "stopped",
|
|
688
|
+
timestamp: utils.mandatory(entry.timestamp).toDate(),
|
|
689
|
+
role: entry.role,
|
|
690
|
+
nodeMetadata: fromNodeMetadata(utils.mandatory(entry.nodeMetadata)),
|
|
691
|
+
runningNodeMetadata: utils.mapOptional(
|
|
692
|
+
fromRunningNodeMetadata,
|
|
693
|
+
entry.runningNodeMetadata
|
|
694
|
+
),
|
|
695
|
+
};
|
|
696
|
+
}
|
|
697
|
+
|
|
698
|
+
/** @internal */
|
|
699
|
+
function fromJobHistoryJobCompleted(
|
|
700
|
+
entry: ManagerPB.JobHistoryJobCompleted
|
|
701
|
+
): JobHistoryJobCompleted {
|
|
702
|
+
return {
|
|
703
|
+
event: "completed",
|
|
704
|
+
timestamp: utils.mandatory(entry.timestamp).toDate(),
|
|
705
|
+
};
|
|
706
|
+
}
|
|
707
|
+
|
|
708
|
+
/** @internal */
|
|
709
|
+
function fromJobHistoryEntry(
|
|
710
|
+
historyEntry: ManagerPB.JobHistoryEntry
|
|
711
|
+
): JobHistoryEntry {
|
|
712
|
+
const event = historyEntry.historyEntry.case;
|
|
713
|
+
|
|
714
|
+
switch (event) {
|
|
715
|
+
case undefined:
|
|
716
|
+
throw Error();
|
|
717
|
+
case "jobCreated":
|
|
718
|
+
return fromJobHistoryJobCreated(historyEntry.historyEntry.value);
|
|
719
|
+
case "jobUpdated":
|
|
720
|
+
return fromJobHistoryJobUpdated(historyEntry.historyEntry.value);
|
|
721
|
+
case "jobProvisioned":
|
|
722
|
+
return fromJobHistoryJobProvisioned(historyEntry.historyEntry.value);
|
|
723
|
+
case "jobRunning":
|
|
724
|
+
return fromJobHistoryJobRunning(historyEntry.historyEntry.value);
|
|
725
|
+
case "jobStopping":
|
|
726
|
+
return fromJobHistoryJobStopping(historyEntry.historyEntry.value);
|
|
727
|
+
case "jobStopped":
|
|
728
|
+
return fromJobHistoryJobStopped(historyEntry.historyEntry.value);
|
|
729
|
+
case "jobCompleted":
|
|
730
|
+
return fromJobHistoryJobCompleted(historyEntry.historyEntry.value);
|
|
731
|
+
default: {
|
|
732
|
+
const exhaustiveCheck: never = event;
|
|
733
|
+
throw new Error(`Unhandled case: ${exhaustiveCheck}`);
|
|
734
|
+
}
|
|
735
|
+
}
|
|
736
|
+
}
|
|
737
|
+
|
|
738
|
+
/** @internal */
|
|
739
|
+
function fromJobWithHistory(
|
|
740
|
+
jobWithHistory: ManagerPB.JobWithHistory
|
|
741
|
+
): JobWithHistory {
|
|
742
|
+
return {
|
|
743
|
+
job: fromJob(utils.mandatory(jobWithHistory.job)),
|
|
744
|
+
history: jobWithHistory.historyEntry.map(fromJobHistoryEntry),
|
|
745
|
+
};
|
|
746
|
+
}
|
|
747
|
+
|
|
748
|
+
/** @public */
|
|
749
|
+
export type AwsNodeId = string;
|
|
750
|
+
|
|
751
|
+
/** @public */
|
|
752
|
+
export type PhysicalNodeId = string;
|
|
753
|
+
|
|
754
|
+
/** @public */
|
|
755
|
+
export type NodeId = string;
|
|
756
|
+
|
|
757
|
+
/** @public */
|
|
758
|
+
export type Version = "latest" | "recommended" | "previousRecommended" | "LTS";
|
|
759
|
+
|
|
760
|
+
/** @public */
|
|
761
|
+
export type Cloud = "aws" | "oci";
|
|
762
|
+
|
|
763
|
+
/** @internal */
|
|
764
|
+
function toNodeId(nodeId: NodeId): CommonPB.NodeId {
|
|
765
|
+
return provideFull(CommonPB.NodeId, { nodeId: nodeId });
|
|
766
|
+
}
|
|
767
|
+
|
|
768
|
+
/** @internal */
|
|
769
|
+
function fromNodeId(nodeId: CommonPB.NodeId): NodeId {
|
|
770
|
+
return nodeId.nodeId;
|
|
771
|
+
}
|
|
772
|
+
|
|
773
|
+
/** @public */
|
|
774
|
+
export type AwsInstanceId = string;
|
|
775
|
+
|
|
776
|
+
/** @public */
|
|
777
|
+
export type AwsInstanceType = string;
|
|
778
|
+
|
|
779
|
+
/** @public */
|
|
780
|
+
export type AwsRegion = string;
|
|
781
|
+
|
|
782
|
+
export type OciRegion = string;
|
|
783
|
+
|
|
784
|
+
/** @public */
|
|
785
|
+
export class AwsLaunchTemplateName {
|
|
786
|
+
name: string;
|
|
787
|
+
version?: string;
|
|
788
|
+
}
|
|
789
|
+
|
|
790
|
+
/** @public */
|
|
791
|
+
export class AwsLaunchTemplateId {
|
|
792
|
+
id: string;
|
|
793
|
+
version?: string;
|
|
794
|
+
}
|
|
795
|
+
|
|
796
|
+
/** @public */
|
|
797
|
+
export type DaemonVersion = string;
|
|
798
|
+
|
|
799
|
+
/** @public */
|
|
800
|
+
export type AwsNodeCreate = {
|
|
801
|
+
nodeId: NodeId;
|
|
802
|
+
tags: Map<string, string>;
|
|
803
|
+
region: AwsRegion;
|
|
804
|
+
instanceType: string;
|
|
805
|
+
template?: AwsLaunchTemplateName | AwsLaunchTemplateId;
|
|
806
|
+
workerImageVersion?: Version;
|
|
807
|
+
workerDaemonVersion?: Version;
|
|
808
|
+
};
|
|
809
|
+
|
|
810
|
+
/** @public */
|
|
811
|
+
export type OciNodeCreate = {
|
|
812
|
+
nodeId: NodeId;
|
|
813
|
+
tags: Map<string, string>;
|
|
814
|
+
availabilityDomain: OciRegion;
|
|
815
|
+
architecture: string;
|
|
816
|
+
shape: string;
|
|
817
|
+
subnet: string;
|
|
818
|
+
workerImageVersion?: Version;
|
|
819
|
+
workerDaemonVersion?: Version;
|
|
820
|
+
};
|
|
821
|
+
|
|
822
|
+
function toVersion(version: Version): ManagerPB.Version {
|
|
823
|
+
switch (version) {
|
|
824
|
+
case "latest":
|
|
825
|
+
return ManagerPB.Version.LATEST;
|
|
826
|
+
case "recommended":
|
|
827
|
+
return ManagerPB.Version.RECOMMENDED;
|
|
828
|
+
case "previousRecommended":
|
|
829
|
+
return ManagerPB.Version.PREVIOUS_RECOMMENDED;
|
|
830
|
+
case "LTS":
|
|
831
|
+
return ManagerPB.Version.LTS;
|
|
832
|
+
}
|
|
833
|
+
}
|
|
834
|
+
|
|
835
|
+
function fromVersion(version: ManagerPB.Version): Version {
|
|
836
|
+
switch (version) {
|
|
837
|
+
case ManagerPB.Version.LATEST:
|
|
838
|
+
return "latest";
|
|
839
|
+
case ManagerPB.Version.RECOMMENDED:
|
|
840
|
+
return "recommended";
|
|
841
|
+
case ManagerPB.Version.PREVIOUS_RECOMMENDED:
|
|
842
|
+
return "previousRecommended";
|
|
843
|
+
case ManagerPB.Version.LTS:
|
|
844
|
+
return "LTS";
|
|
845
|
+
}
|
|
846
|
+
}
|
|
847
|
+
|
|
848
|
+
function toAwsLaunchTemplate(
|
|
849
|
+
template: AwsLaunchTemplateName | AwsLaunchTemplateId
|
|
850
|
+
): ManagerPB.AwsLaunchTemplate {
|
|
851
|
+
if (template instanceof AwsLaunchTemplateName) {
|
|
852
|
+
return provideFull(ManagerPB.AwsLaunchTemplate, {
|
|
853
|
+
template: { value: template.name, case: "name" },
|
|
854
|
+
version: template.version || "",
|
|
855
|
+
});
|
|
856
|
+
} else {
|
|
857
|
+
return provideFull(ManagerPB.AwsLaunchTemplate, {
|
|
858
|
+
template: { value: template.id, case: "id" },
|
|
859
|
+
version: template.version || "",
|
|
860
|
+
});
|
|
861
|
+
}
|
|
862
|
+
}
|
|
863
|
+
|
|
864
|
+
function toAwsNodeCreate(node: AwsNodeCreate): ManagerPB.CreateAwsNodeRequest {
|
|
865
|
+
return provideFull(ManagerPB.CreateAwsNodeRequest, {
|
|
866
|
+
nodeId: toNodeId(node.nodeId),
|
|
867
|
+
tags: Object.fromEntries(node.tags.entries()),
|
|
868
|
+
region: node.region,
|
|
869
|
+
instanceType: node.instanceType,
|
|
870
|
+
workerImageVersion: toVersion(node.workerImageVersion || "recommended"),
|
|
871
|
+
workerDaemonVersion: toVersion(node.workerDaemonVersion || "recommended"),
|
|
872
|
+
template: utils.mapOptional(toAwsLaunchTemplate, node.template),
|
|
873
|
+
});
|
|
874
|
+
}
|
|
875
|
+
|
|
876
|
+
function toOciNodeCreate(node: OciNodeCreate): ManagerPB.CreateOciNodeRequest {
|
|
877
|
+
return provideFull(ManagerPB.CreateOciNodeRequest, {
|
|
878
|
+
nodeId: toNodeId(node.nodeId),
|
|
879
|
+
tags: Object.fromEntries(node.tags.entries()),
|
|
880
|
+
workerImageVersion: toVersion(node.workerImageVersion || "recommended"),
|
|
881
|
+
workerDaemonVersion: toVersion(node.workerDaemonVersion || "recommended"),
|
|
882
|
+
availabilityDomain: node.availabilityDomain,
|
|
883
|
+
architecture: node.architecture,
|
|
884
|
+
shape: node.shape,
|
|
885
|
+
subnet: node.subnet,
|
|
886
|
+
});
|
|
887
|
+
}
|
|
888
|
+
|
|
889
|
+
/** @internal */
|
|
890
|
+
function fromNodeMetadata(nodeMetadata: CommonPB.NodeMetadata): NodeMetadata {
|
|
891
|
+
return {
|
|
892
|
+
nodeId: fromNodeId(utils.mandatory(nodeMetadata.nodeId)),
|
|
893
|
+
tags: new Map(Object.entries(nodeMetadata.tags)),
|
|
894
|
+
createdAt: nodeMetadata.createdAt?.toDate(),
|
|
895
|
+
providerMetadata: fromProviderMetadata(
|
|
896
|
+
utils.mandatory(nodeMetadata.providerMetadata)
|
|
897
|
+
),
|
|
898
|
+
// daemonVersion: awsNode.daemonVersion,
|
|
899
|
+
};
|
|
900
|
+
}
|
|
901
|
+
|
|
902
|
+
function fromAwsMetadata(nodeDetails: CommonPB.AwsMetadata): AwsMetadata {
|
|
903
|
+
return {
|
|
904
|
+
provider: "aws",
|
|
905
|
+
instanceType: nodeDetails.instanceType,
|
|
906
|
+
region: nodeDetails.region,
|
|
907
|
+
};
|
|
908
|
+
}
|
|
909
|
+
|
|
910
|
+
function fromOciMetadata(_nodeDetails: CommonPB.OciMetadata): OciMetadata {
|
|
911
|
+
return {
|
|
912
|
+
provider: "oci",
|
|
913
|
+
};
|
|
914
|
+
}
|
|
915
|
+
|
|
916
|
+
function fromProviderMetadata(
|
|
917
|
+
provider: CommonPB.ProviderMetadata
|
|
918
|
+
): ProviderMetadata {
|
|
919
|
+
const providerType = provider.metadata.case;
|
|
920
|
+
switch (providerType) {
|
|
921
|
+
case undefined:
|
|
922
|
+
throw Error();
|
|
923
|
+
case "aws":
|
|
924
|
+
return fromAwsMetadata(provider.metadata.value);
|
|
925
|
+
case "oci":
|
|
926
|
+
return fromOciMetadata(provider.metadata.value);
|
|
927
|
+
case "test":
|
|
928
|
+
throw Error();
|
|
929
|
+
default: {
|
|
930
|
+
const exhaustiveCheck: never = providerType;
|
|
931
|
+
throw new Error(`Unhandled case: ${exhaustiveCheck}`);
|
|
932
|
+
}
|
|
933
|
+
}
|
|
934
|
+
}
|
|
935
|
+
|
|
936
|
+
function fromRunningAwsMetadata(
|
|
937
|
+
metadata: CommonPB.RunningAwsMetadata
|
|
938
|
+
): RunningAwsMetadata {
|
|
939
|
+
return { provider: "aws", instanceId: metadata.instanceId };
|
|
940
|
+
}
|
|
941
|
+
|
|
942
|
+
function fromRunningOciMetadata(
|
|
943
|
+
_metadata: CommonPB.RunningOciMetadata
|
|
944
|
+
): RunningOciMetadata {
|
|
945
|
+
return { provider: "oci" };
|
|
946
|
+
}
|
|
947
|
+
|
|
948
|
+
function fromRunningNodeProviderMetadata(
|
|
949
|
+
provider: CommonPB.RunningNodeProviderMetadata
|
|
950
|
+
): RunningNodeProviderMetadata {
|
|
951
|
+
const providerType = provider.instance.case;
|
|
952
|
+
switch (providerType) {
|
|
953
|
+
case undefined:
|
|
954
|
+
throw Error();
|
|
955
|
+
case "aws":
|
|
956
|
+
return fromRunningAwsMetadata(provider.instance.value);
|
|
957
|
+
case "oci":
|
|
958
|
+
return fromRunningOciMetadata(provider.instance.value);
|
|
959
|
+
case "test":
|
|
960
|
+
throw Error();
|
|
961
|
+
default: {
|
|
962
|
+
const exhaustiveCheck: never = providerType;
|
|
963
|
+
throw new Error(`Unhandled case: ${exhaustiveCheck}`);
|
|
964
|
+
}
|
|
965
|
+
}
|
|
966
|
+
}
|
|
967
|
+
|
|
968
|
+
/** @public */
|
|
969
|
+
export type RunningAwsMetadata = { provider: "aws"; instanceId: AwsInstanceId };
|
|
970
|
+
|
|
971
|
+
/** @public */
|
|
972
|
+
export type RunningOciMetadata = { provider: "oci" };
|
|
973
|
+
|
|
974
|
+
/** @public */
|
|
975
|
+
export type RunningNodeProviderMetadata =
|
|
976
|
+
| RunningAwsMetadata
|
|
977
|
+
| RunningOciMetadata;
|
|
978
|
+
|
|
979
|
+
/** @public */
|
|
980
|
+
export type RunningNodeMetadata = {
|
|
981
|
+
publicDnsName?: string;
|
|
982
|
+
privateDnsName: string;
|
|
983
|
+
privateIpAddress: string;
|
|
984
|
+
providerMetadata: RunningNodeProviderMetadata;
|
|
985
|
+
};
|
|
986
|
+
|
|
987
|
+
function fromRunningNodeMetadata(
|
|
988
|
+
runningNodeMetadata: CommonPB.RunningNodeMetadata
|
|
989
|
+
): RunningNodeMetadata {
|
|
990
|
+
return {
|
|
991
|
+
publicDnsName: runningNodeMetadata.publicDnsName,
|
|
992
|
+
privateDnsName: runningNodeMetadata.privateDnsName,
|
|
993
|
+
privateIpAddress: runningNodeMetadata.privateIpAddress,
|
|
994
|
+
providerMetadata: fromRunningNodeProviderMetadata(
|
|
995
|
+
utils.mandatory(runningNodeMetadata.providerMetadata)
|
|
996
|
+
),
|
|
997
|
+
};
|
|
998
|
+
}
|
|
999
|
+
|
|
1000
|
+
/** @public */
|
|
1001
|
+
export type AwsMetadata = {
|
|
1002
|
+
provider: "aws";
|
|
1003
|
+
instanceType: AwsInstanceType;
|
|
1004
|
+
region: AwsRegion;
|
|
1005
|
+
};
|
|
1006
|
+
|
|
1007
|
+
/** @public */
|
|
1008
|
+
export type OciMetadata = {
|
|
1009
|
+
provider: "oci";
|
|
1010
|
+
};
|
|
1011
|
+
|
|
1012
|
+
/** @public */
|
|
1013
|
+
export type ProviderMetadata = AwsMetadata | OciMetadata;
|
|
1014
|
+
|
|
1015
|
+
/** @public */
|
|
1016
|
+
export type NodeMetadata = {
|
|
1017
|
+
nodeId: NodeId;
|
|
1018
|
+
tags: Map<string, string>;
|
|
1019
|
+
createdAt?: Date;
|
|
1020
|
+
providerMetadata: ProviderMetadata;
|
|
1021
|
+
};
|
|
1022
|
+
|
|
1023
|
+
/** @public */
|
|
1024
|
+
export type PhysicalNode = {
|
|
1025
|
+
nodeType: "physical";
|
|
1026
|
+
id: PhysicalNodeId;
|
|
1027
|
+
tags: Map<string, string>;
|
|
1028
|
+
ipAddress: string;
|
|
1029
|
+
};
|
|
1030
|
+
|
|
1031
|
+
/** @internal */
|
|
1032
|
+
function fromPhysicalNode(physicalNode: ManagerPB.PhysicalNode): PhysicalNode {
|
|
1033
|
+
return {
|
|
1034
|
+
nodeType: "physical",
|
|
1035
|
+
id: physicalNode.id,
|
|
1036
|
+
tags: new Map(Object.entries(physicalNode.tags)),
|
|
1037
|
+
ipAddress: physicalNode.ipAddress,
|
|
1038
|
+
};
|
|
1039
|
+
}
|
|
1040
|
+
|
|
1041
|
+
/** @public */
|
|
1042
|
+
export type Log = {
|
|
1043
|
+
level:
|
|
1044
|
+
| "emergency"
|
|
1045
|
+
| "alert"
|
|
1046
|
+
| "critical"
|
|
1047
|
+
| "error"
|
|
1048
|
+
| "warning"
|
|
1049
|
+
| "notice"
|
|
1050
|
+
| "info"
|
|
1051
|
+
| "debug";
|
|
1052
|
+
timestamp: Date;
|
|
1053
|
+
message: string;
|
|
1054
|
+
};
|
|
1055
|
+
|
|
1056
|
+
/**
|
|
1057
|
+
* @public
|
|
1058
|
+
* Top level Norsk configuration
|
|
1059
|
+
*/
|
|
1060
|
+
export interface NorskSettings {
|
|
1061
|
+
/**
|
|
1062
|
+
* Callback URL to listen on for gRPC session with Norsk Media
|
|
1063
|
+
*
|
|
1064
|
+
*/
|
|
1065
|
+
url?: string;
|
|
1066
|
+
onAttemptingToConnect?: () => void;
|
|
1067
|
+
onConnecting?: () => void;
|
|
1068
|
+
onReady?: () => void;
|
|
1069
|
+
onFailedToConnect?: () => void;
|
|
1070
|
+
onShutdown?: () => void;
|
|
1071
|
+
onHello?: (version: CommonVersion) => void;
|
|
1072
|
+
onLogEvent?: (log: Log) => void;
|
|
1073
|
+
}
|
|
1074
|
+
|
|
1075
|
+
/** @public */
|
|
1076
|
+
export type RunningJob = { jobId: JobId; role: Role; nodeId: NodeId };
|
|
1077
|
+
|
|
1078
|
+
/** @internal */
|
|
1079
|
+
function fromRunningJob(runningJob: ManagerPB.RunningJob): RunningJob {
|
|
1080
|
+
const key = utils.mandatory(runningJob.jobKey);
|
|
1081
|
+
return {
|
|
1082
|
+
jobId: fromJobId(utils.mandatory(key.jobId)),
|
|
1083
|
+
role: key.role,
|
|
1084
|
+
nodeId: fromNodeId(utils.mandatory(runningJob.nodeId)),
|
|
1085
|
+
};
|
|
1086
|
+
}
|
|
1087
|
+
|
|
1088
|
+
/**
|
|
1089
|
+
* @public
|
|
1090
|
+
* Job is almost ready to start - "almost" defined by property passed into
|
|
1091
|
+
* the list functions - todo, sort out this description
|
|
1092
|
+
* */
|
|
1093
|
+
export type JobPending = {
|
|
1094
|
+
event: "jobPending";
|
|
1095
|
+
jobWithHistory: JobWithHistory;
|
|
1096
|
+
};
|
|
1097
|
+
|
|
1098
|
+
/** @internal */
|
|
1099
|
+
function fromJobPending(event: ManagerPB.JobPending): JobPending | undefined {
|
|
1100
|
+
return utils.mapOptional(
|
|
1101
|
+
(jobWithHistory) => ({
|
|
1102
|
+
event: "jobPending",
|
|
1103
|
+
jobWithHistory: fromJobWithHistory(jobWithHistory),
|
|
1104
|
+
}),
|
|
1105
|
+
event.jobWithHistory
|
|
1106
|
+
);
|
|
1107
|
+
}
|
|
1108
|
+
|
|
1109
|
+
/**
|
|
1110
|
+
* @public
|
|
1111
|
+
* Job details have been updated
|
|
1112
|
+
* */
|
|
1113
|
+
export type JobUpdated = {
|
|
1114
|
+
event: "jobUpdated";
|
|
1115
|
+
jobWithHistory: JobWithHistory;
|
|
1116
|
+
};
|
|
1117
|
+
|
|
1118
|
+
/** @internal */
|
|
1119
|
+
function fromJobUpdated(event: ManagerPB.JobUpdated): JobUpdated | undefined {
|
|
1120
|
+
return utils.mapOptional(
|
|
1121
|
+
(jobWithHistory) => ({
|
|
1122
|
+
event: "jobUpdated",
|
|
1123
|
+
jobWithHistory: fromJobWithHistory(jobWithHistory),
|
|
1124
|
+
}),
|
|
1125
|
+
event.jobWithHistory
|
|
1126
|
+
);
|
|
1127
|
+
}
|
|
1128
|
+
|
|
1129
|
+
/**
|
|
1130
|
+
* @public
|
|
1131
|
+
* A job has been updated such that it no longer matches the search criteria
|
|
1132
|
+
* */
|
|
1133
|
+
export type JobOutOfWindow = {
|
|
1134
|
+
event: "jobOutOfWindow";
|
|
1135
|
+
jobWithHistory: JobWithHistory;
|
|
1136
|
+
};
|
|
1137
|
+
|
|
1138
|
+
/** @internal */
|
|
1139
|
+
function fromJobOutOfWindow(
|
|
1140
|
+
event: ManagerPB.JobOutOfWindow
|
|
1141
|
+
): JobOutOfWindow | undefined {
|
|
1142
|
+
return utils.mapOptional(
|
|
1143
|
+
(jobWithHistory) => ({
|
|
1144
|
+
event: "jobOutOfWindow",
|
|
1145
|
+
jobWithHistory: fromJobWithHistory(jobWithHistory),
|
|
1146
|
+
}),
|
|
1147
|
+
event.jobWithHistory
|
|
1148
|
+
);
|
|
1149
|
+
}
|
|
1150
|
+
|
|
1151
|
+
/**
|
|
1152
|
+
* @public
|
|
1153
|
+
* A job has been deleted
|
|
1154
|
+
* */
|
|
1155
|
+
export type JobDeleted = {
|
|
1156
|
+
event: "jobDeleted";
|
|
1157
|
+
jobId: JobId;
|
|
1158
|
+
};
|
|
1159
|
+
|
|
1160
|
+
/** @public */
|
|
1161
|
+
type JobInfoServiceStarted = {
|
|
1162
|
+
type: "serviceStarted";
|
|
1163
|
+
serviceName: string;
|
|
1164
|
+
};
|
|
1165
|
+
|
|
1166
|
+
/** @public */
|
|
1167
|
+
type JobInfoServiceRestarting = {
|
|
1168
|
+
type: "serviceRestarting";
|
|
1169
|
+
serviceName: string;
|
|
1170
|
+
};
|
|
1171
|
+
|
|
1172
|
+
/** @public */
|
|
1173
|
+
type JobInfoContainerEvent = {
|
|
1174
|
+
type: "containerEvent";
|
|
1175
|
+
serviceName: string;
|
|
1176
|
+
text: string;
|
|
1177
|
+
};
|
|
1178
|
+
|
|
1179
|
+
/** @public */
|
|
1180
|
+
type JobInfoComposeStage = "build" | "create" | "start" | "stop" | "down";
|
|
1181
|
+
|
|
1182
|
+
/** @public */
|
|
1183
|
+
type JobInfoComponseMessage = {
|
|
1184
|
+
type: "composeMessage";
|
|
1185
|
+
stage: JobInfoComposeStage;
|
|
1186
|
+
text: string;
|
|
1187
|
+
};
|
|
1188
|
+
|
|
1189
|
+
/** @public */
|
|
1190
|
+
type JobInfoMessage =
|
|
1191
|
+
| JobInfoServiceStarted
|
|
1192
|
+
| JobInfoServiceRestarting
|
|
1193
|
+
| JobInfoContainerEvent
|
|
1194
|
+
| JobInfoComponseMessage;
|
|
1195
|
+
|
|
1196
|
+
/**
|
|
1197
|
+
* @public
|
|
1198
|
+
* Informational messages about a job
|
|
1199
|
+
* */
|
|
1200
|
+
export type JobInfo = {
|
|
1201
|
+
event: "jobInfo";
|
|
1202
|
+
jobId: JobId;
|
|
1203
|
+
role: Role;
|
|
1204
|
+
timestamp: Date;
|
|
1205
|
+
message: JobInfoMessage;
|
|
1206
|
+
};
|
|
1207
|
+
|
|
1208
|
+
/** @internal */
|
|
1209
|
+
function fromJobInfo(event: CommonPB.JobInfoMessage): JobInfo | undefined {
|
|
1210
|
+
var message: JobInfoMessage;
|
|
1211
|
+
|
|
1212
|
+
switch (event.message.case) {
|
|
1213
|
+
case "serviceStarted":
|
|
1214
|
+
message = {
|
|
1215
|
+
type: "serviceStarted",
|
|
1216
|
+
serviceName: event.message.value.serviceName,
|
|
1217
|
+
};
|
|
1218
|
+
break;
|
|
1219
|
+
case "serviceRestarting":
|
|
1220
|
+
message = {
|
|
1221
|
+
type: "serviceRestarting",
|
|
1222
|
+
serviceName: event.message.value.serviceName,
|
|
1223
|
+
};
|
|
1224
|
+
break;
|
|
1225
|
+
case "containerEvent":
|
|
1226
|
+
message = {
|
|
1227
|
+
type: "containerEvent",
|
|
1228
|
+
serviceName: event.message.value.serviceName,
|
|
1229
|
+
text: event.message.value.text,
|
|
1230
|
+
};
|
|
1231
|
+
break;
|
|
1232
|
+
case "composeMessage": {
|
|
1233
|
+
var stage: JobInfoComposeStage;
|
|
1234
|
+
switch (event.message.value.stage) {
|
|
1235
|
+
case CommonPB.JobInfoComponseMessage_Stage.BUILD:
|
|
1236
|
+
stage = "build";
|
|
1237
|
+
break;
|
|
1238
|
+
case CommonPB.JobInfoComponseMessage_Stage.CREATE:
|
|
1239
|
+
stage = "create";
|
|
1240
|
+
break;
|
|
1241
|
+
case CommonPB.JobInfoComponseMessage_Stage.START:
|
|
1242
|
+
stage = "start";
|
|
1243
|
+
break;
|
|
1244
|
+
case CommonPB.JobInfoComponseMessage_Stage.STOP:
|
|
1245
|
+
stage = "stop";
|
|
1246
|
+
break;
|
|
1247
|
+
case CommonPB.JobInfoComponseMessage_Stage.DOWN:
|
|
1248
|
+
stage = "down";
|
|
1249
|
+
break;
|
|
1250
|
+
}
|
|
1251
|
+
message = {
|
|
1252
|
+
type: "composeMessage",
|
|
1253
|
+
stage,
|
|
1254
|
+
text: event.message.value.text,
|
|
1255
|
+
};
|
|
1256
|
+
break;
|
|
1257
|
+
}
|
|
1258
|
+
case undefined:
|
|
1259
|
+
return undefined;
|
|
1260
|
+
}
|
|
1261
|
+
return {
|
|
1262
|
+
event: "jobInfo",
|
|
1263
|
+
jobId: fromJobId(utils.mandatory(event.jobKey?.jobId)),
|
|
1264
|
+
role: utils.mandatory(event.jobKey).role,
|
|
1265
|
+
timestamp: utils.mandatory(event.timestamp).toDate(),
|
|
1266
|
+
message,
|
|
1267
|
+
};
|
|
1268
|
+
}
|
|
1269
|
+
|
|
1270
|
+
/** @internal */
|
|
1271
|
+
function fromJobDeleted(event: ManagerPB.JobDeleted): JobDeleted | undefined {
|
|
1272
|
+
return utils.mapOptional(
|
|
1273
|
+
(jobId) => ({
|
|
1274
|
+
event: "jobDeleted",
|
|
1275
|
+
jobId: fromJobId(jobId),
|
|
1276
|
+
}),
|
|
1277
|
+
event.jobId
|
|
1278
|
+
);
|
|
1279
|
+
}
|
|
1280
|
+
|
|
1281
|
+
/**
|
|
1282
|
+
* @public
|
|
1283
|
+
* A node is starting
|
|
1284
|
+
* */
|
|
1285
|
+
export type NodeStarting = {
|
|
1286
|
+
event: "nodeStarting";
|
|
1287
|
+
nodeMetadata: NodeMetadata;
|
|
1288
|
+
};
|
|
1289
|
+
|
|
1290
|
+
/** @internal */
|
|
1291
|
+
function fromNodeStarting(
|
|
1292
|
+
event: ManagerPB.NodeStarting
|
|
1293
|
+
): NodeStarting | undefined {
|
|
1294
|
+
return utils.mapOptional(
|
|
1295
|
+
(nodeMetadata) => ({
|
|
1296
|
+
event: "nodeStarting",
|
|
1297
|
+
nodeMetadata: fromNodeMetadata(nodeMetadata),
|
|
1298
|
+
}),
|
|
1299
|
+
event.nodeMetadata
|
|
1300
|
+
);
|
|
1301
|
+
}
|
|
1302
|
+
|
|
1303
|
+
/**
|
|
1304
|
+
* @public
|
|
1305
|
+
* A node has started
|
|
1306
|
+
* */
|
|
1307
|
+
export type NodeStarted = {
|
|
1308
|
+
event: "nodeStarted";
|
|
1309
|
+
nodeMetadata: NodeMetadata;
|
|
1310
|
+
runningNodeMetadata: RunningNodeMetadata;
|
|
1311
|
+
};
|
|
1312
|
+
|
|
1313
|
+
/** @internal */
|
|
1314
|
+
function fromNodeStarted(
|
|
1315
|
+
event: ManagerPB.NodeStarted
|
|
1316
|
+
): NodeStarted | undefined {
|
|
1317
|
+
if (event.nodeMetadata && event.runningNodeMetadata) {
|
|
1318
|
+
return {
|
|
1319
|
+
event: "nodeStarted",
|
|
1320
|
+
nodeMetadata: fromNodeMetadata(event.nodeMetadata),
|
|
1321
|
+
runningNodeMetadata: fromRunningNodeMetadata(event.runningNodeMetadata),
|
|
1322
|
+
};
|
|
1323
|
+
}
|
|
1324
|
+
return undefined;
|
|
1325
|
+
}
|
|
1326
|
+
|
|
1327
|
+
/**
|
|
1328
|
+
* @public
|
|
1329
|
+
* A node is stopping
|
|
1330
|
+
* */
|
|
1331
|
+
export type NodeStopping = {
|
|
1332
|
+
event: "nodeStopping";
|
|
1333
|
+
nodeId: NodeId;
|
|
1334
|
+
reason:
|
|
1335
|
+
| "createFailed"
|
|
1336
|
+
| "pendingTimeExceeded"
|
|
1337
|
+
| "startupTimeExceeded"
|
|
1338
|
+
| "initialisationHealthPingTimeExceeded"
|
|
1339
|
+
| "healthPingTimeExceeded"
|
|
1340
|
+
| "duplicateJob"
|
|
1341
|
+
| "providerStop"
|
|
1342
|
+
| "providerTerminate"
|
|
1343
|
+
| "userRequested"
|
|
1344
|
+
| "unknownNode";
|
|
1345
|
+
};
|
|
1346
|
+
|
|
1347
|
+
/** @internal */
|
|
1348
|
+
function fromNodeStopping(
|
|
1349
|
+
event: ManagerPB.NodeStopping
|
|
1350
|
+
): NodeStopping | undefined {
|
|
1351
|
+
let reason: NodeStopping["reason"];
|
|
1352
|
+
switch (event.reason) {
|
|
1353
|
+
case ManagerPB.NodeStopping_Reason.CREATE_FAILED:
|
|
1354
|
+
reason = "createFailed";
|
|
1355
|
+
break;
|
|
1356
|
+
case ManagerPB.NodeStopping_Reason.PENDING_TIME_EXCEEDED:
|
|
1357
|
+
reason = "pendingTimeExceeded";
|
|
1358
|
+
break;
|
|
1359
|
+
case ManagerPB.NodeStopping_Reason.STARTUP_TIME_EXCEEDED:
|
|
1360
|
+
reason = "startupTimeExceeded";
|
|
1361
|
+
break;
|
|
1362
|
+
case ManagerPB.NodeStopping_Reason.INITIALISATION_HEALTH_PING_TIME_EXCEEDED:
|
|
1363
|
+
reason = "initialisationHealthPingTimeExceeded";
|
|
1364
|
+
break;
|
|
1365
|
+
case ManagerPB.NodeStopping_Reason.HEALTH_PING_TIME_EXCEEDED:
|
|
1366
|
+
reason = "healthPingTimeExceeded";
|
|
1367
|
+
break;
|
|
1368
|
+
case ManagerPB.NodeStopping_Reason.DUPLICATE_JOB:
|
|
1369
|
+
reason = "duplicateJob";
|
|
1370
|
+
break;
|
|
1371
|
+
case ManagerPB.NodeStopping_Reason.PROVIDER_STOP:
|
|
1372
|
+
reason = "providerStop";
|
|
1373
|
+
break;
|
|
1374
|
+
case ManagerPB.NodeStopping_Reason.PROVIDER_TERMINATE:
|
|
1375
|
+
reason = "providerTerminate";
|
|
1376
|
+
break;
|
|
1377
|
+
case ManagerPB.NodeStopping_Reason.USER_REQUESTED:
|
|
1378
|
+
reason = "userRequested";
|
|
1379
|
+
break;
|
|
1380
|
+
case ManagerPB.NodeStopping_Reason.UNKNOWN_NODE:
|
|
1381
|
+
reason = "unknownNode";
|
|
1382
|
+
break;
|
|
1383
|
+
|
|
1384
|
+
default: {
|
|
1385
|
+
const exhaustiveCheck: never = event.reason;
|
|
1386
|
+
throw new Error(`Unhandled case: ${exhaustiveCheck}`);
|
|
1387
|
+
}
|
|
1388
|
+
}
|
|
1389
|
+
return utils.mapOptional(
|
|
1390
|
+
(nodeId) => ({
|
|
1391
|
+
event: "nodeStopping",
|
|
1392
|
+
nodeId: fromNodeId(nodeId),
|
|
1393
|
+
reason: reason,
|
|
1394
|
+
}),
|
|
1395
|
+
event.nodeId
|
|
1396
|
+
);
|
|
1397
|
+
}
|
|
1398
|
+
|
|
1399
|
+
/** @public
|
|
1400
|
+
* A node has stopped
|
|
1401
|
+
* */
|
|
1402
|
+
export type NodeStopped = {
|
|
1403
|
+
event: "nodeStopped";
|
|
1404
|
+
nodeId: NodeId;
|
|
1405
|
+
};
|
|
1406
|
+
|
|
1407
|
+
/** @internal */
|
|
1408
|
+
function fromNodeStopped(
|
|
1409
|
+
event: ManagerPB.NodeStopped
|
|
1410
|
+
): NodeStopped | undefined {
|
|
1411
|
+
return utils.mapOptional(
|
|
1412
|
+
(nodeId) => ({
|
|
1413
|
+
event: "nodeStopped",
|
|
1414
|
+
nodeId: fromNodeId(nodeId),
|
|
1415
|
+
}),
|
|
1416
|
+
event.nodeId
|
|
1417
|
+
);
|
|
1418
|
+
}
|
|
1419
|
+
|
|
1420
|
+
/**
|
|
1421
|
+
* @public
|
|
1422
|
+
* A node is running - used in initial state messages
|
|
1423
|
+
* */
|
|
1424
|
+
export type NodeRunning = {
|
|
1425
|
+
event: "nodeRunning";
|
|
1426
|
+
nodeMetadata: NodeMetadata;
|
|
1427
|
+
runningNodeMetadata: RunningNodeMetadata;
|
|
1428
|
+
instances: RunningJob[];
|
|
1429
|
+
};
|
|
1430
|
+
|
|
1431
|
+
/** @internal */
|
|
1432
|
+
function fromNodeRunning(
|
|
1433
|
+
event: ManagerPB.NodeRunning
|
|
1434
|
+
): NodeRunning | undefined {
|
|
1435
|
+
if (event.nodeMetadata && event.runningNodeMetadata) {
|
|
1436
|
+
return {
|
|
1437
|
+
event: "nodeRunning",
|
|
1438
|
+
nodeMetadata: fromNodeMetadata(event.nodeMetadata),
|
|
1439
|
+
runningNodeMetadata: fromRunningNodeMetadata(event.runningNodeMetadata),
|
|
1440
|
+
instances: event.instances.map(fromRunningJob),
|
|
1441
|
+
};
|
|
1442
|
+
}
|
|
1443
|
+
return undefined;
|
|
1444
|
+
}
|
|
1445
|
+
|
|
1446
|
+
/**
|
|
1447
|
+
* @public
|
|
1448
|
+
* A physical node has connected
|
|
1449
|
+
* */
|
|
1450
|
+
export type PhysicalNodeConnected = {
|
|
1451
|
+
event: "physicalNodeConnected";
|
|
1452
|
+
node: PhysicalNode;
|
|
1453
|
+
instances: RunningJob[];
|
|
1454
|
+
};
|
|
1455
|
+
|
|
1456
|
+
/** @internal */
|
|
1457
|
+
function fromPhysicalNodeConnected(
|
|
1458
|
+
event: ManagerPB.PhysicalNodeConnected
|
|
1459
|
+
): PhysicalNodeConnected | undefined {
|
|
1460
|
+
return utils.mapOptional(
|
|
1461
|
+
(node) => ({
|
|
1462
|
+
event: "physicalNodeConnected",
|
|
1463
|
+
node: fromPhysicalNode(node),
|
|
1464
|
+
instances: event.instances.map(fromRunningJob),
|
|
1465
|
+
}),
|
|
1466
|
+
event.node
|
|
1467
|
+
);
|
|
1468
|
+
}
|
|
1469
|
+
|
|
1470
|
+
/**
|
|
1471
|
+
* @public
|
|
1472
|
+
* The health status of a provider has changed
|
|
1473
|
+
* */
|
|
1474
|
+
export type ProviderHealthChange = {
|
|
1475
|
+
event: "providerHealthChange";
|
|
1476
|
+
health: ProviderHealth;
|
|
1477
|
+
};
|
|
1478
|
+
|
|
1479
|
+
/**
|
|
1480
|
+
* @public
|
|
1481
|
+
* The manager activity stream has closed
|
|
1482
|
+
* */
|
|
1483
|
+
export type EventStreamClosed = {
|
|
1484
|
+
event: "eventStreamClosed";
|
|
1485
|
+
error?: Error;
|
|
1486
|
+
};
|
|
1487
|
+
|
|
1488
|
+
/** @public */
|
|
1489
|
+
export type TagComparison =
|
|
1490
|
+
| { comparison: "eq"; value: string }
|
|
1491
|
+
| { comparison: "neq"; value: string }
|
|
1492
|
+
| { comparison: "re"; value: string }
|
|
1493
|
+
| { comparison: "exists"; value: boolean };
|
|
1494
|
+
|
|
1495
|
+
/** @public */
|
|
1496
|
+
export type TagFilter = {
|
|
1497
|
+
filterType: "tag";
|
|
1498
|
+
tagName: string;
|
|
1499
|
+
comparison: TagComparison;
|
|
1500
|
+
};
|
|
1501
|
+
|
|
1502
|
+
/** internal */
|
|
1503
|
+
function toTagFilter(filter: TagFilter): ManagerPB.TagFilter {
|
|
1504
|
+
let comparison: ManagerPB.TagFilter["comparison"];
|
|
1505
|
+
|
|
1506
|
+
switch (filter.comparison.comparison) {
|
|
1507
|
+
case "eq":
|
|
1508
|
+
comparison = utils.mkCase({ eq: filter.comparison.value });
|
|
1509
|
+
break;
|
|
1510
|
+
case "neq":
|
|
1511
|
+
comparison = utils.mkCase({ neq: filter.comparison.value });
|
|
1512
|
+
break;
|
|
1513
|
+
case "re":
|
|
1514
|
+
comparison = utils.mkCase({ re: filter.comparison.value });
|
|
1515
|
+
break;
|
|
1516
|
+
case "exists":
|
|
1517
|
+
comparison = utils.mkCase({ exists: filter.comparison.value });
|
|
1518
|
+
break;
|
|
1519
|
+
}
|
|
1520
|
+
|
|
1521
|
+
return provideFull(ManagerPB.TagFilter, {
|
|
1522
|
+
tagName: filter.tagName,
|
|
1523
|
+
comparison: comparison,
|
|
1524
|
+
});
|
|
1525
|
+
}
|
|
1526
|
+
|
|
1527
|
+
/** @public */
|
|
1528
|
+
export type IdComparison =
|
|
1529
|
+
| { comparison: "eq"; value: string }
|
|
1530
|
+
| { comparison: "neq"; value: string }
|
|
1531
|
+
| { comparison: "re"; value: string };
|
|
1532
|
+
|
|
1533
|
+
/** @public */
|
|
1534
|
+
export type IdFilter = {
|
|
1535
|
+
filterType: "id";
|
|
1536
|
+
comparison: IdComparison;
|
|
1537
|
+
};
|
|
1538
|
+
|
|
1539
|
+
/** internal */
|
|
1540
|
+
function toIdFilter(filter: IdFilter): ManagerPB.IdFilter {
|
|
1541
|
+
let comparison: ManagerPB.IdFilter["comparison"];
|
|
1542
|
+
|
|
1543
|
+
switch (filter.comparison.comparison) {
|
|
1544
|
+
case "eq":
|
|
1545
|
+
comparison = utils.mkCase({ eq: filter.comparison.value });
|
|
1546
|
+
break;
|
|
1547
|
+
case "neq":
|
|
1548
|
+
comparison = utils.mkCase({ neq: filter.comparison.value });
|
|
1549
|
+
break;
|
|
1550
|
+
case "re":
|
|
1551
|
+
comparison = utils.mkCase({ re: filter.comparison.value });
|
|
1552
|
+
break;
|
|
1553
|
+
}
|
|
1554
|
+
return provideFull(ManagerPB.IdFilter, {
|
|
1555
|
+
comparison: comparison,
|
|
1556
|
+
});
|
|
1557
|
+
}
|
|
1558
|
+
|
|
1559
|
+
/** @public */
|
|
1560
|
+
export type DateComparison =
|
|
1561
|
+
| { comparison: "eq"; value: Date }
|
|
1562
|
+
| { comparison: "lt"; value: Date }
|
|
1563
|
+
| { comparison: "lte"; value: Date }
|
|
1564
|
+
| { comparison: "gt"; value: Date }
|
|
1565
|
+
| { comparison: "gte"; value: Date }
|
|
1566
|
+
| { comparison: "between"; from: Date; to: Date };
|
|
1567
|
+
|
|
1568
|
+
/** @public */
|
|
1569
|
+
export type DateFilter = {
|
|
1570
|
+
filterType: "date";
|
|
1571
|
+
dateType: "start" | "end";
|
|
1572
|
+
comparison: DateComparison;
|
|
1573
|
+
};
|
|
1574
|
+
|
|
1575
|
+
/** internal */
|
|
1576
|
+
function toDateFilter(filter: DateFilter): ManagerPB.DateFilter {
|
|
1577
|
+
let comparison: ManagerPB.DateFilter["comparison"];
|
|
1578
|
+
|
|
1579
|
+
switch (filter.comparison.comparison) {
|
|
1580
|
+
case "eq":
|
|
1581
|
+
comparison = utils.mkCase({
|
|
1582
|
+
eq: Timestamp.fromDate(filter.comparison.value),
|
|
1583
|
+
});
|
|
1584
|
+
break;
|
|
1585
|
+
case "lt":
|
|
1586
|
+
comparison = utils.mkCase({
|
|
1587
|
+
lt: Timestamp.fromDate(filter.comparison.value),
|
|
1588
|
+
});
|
|
1589
|
+
break;
|
|
1590
|
+
case "lte":
|
|
1591
|
+
comparison = utils.mkCase({
|
|
1592
|
+
lte: Timestamp.fromDate(filter.comparison.value),
|
|
1593
|
+
});
|
|
1594
|
+
break;
|
|
1595
|
+
case "gt":
|
|
1596
|
+
comparison = utils.mkCase({
|
|
1597
|
+
gt: Timestamp.fromDate(filter.comparison.value),
|
|
1598
|
+
});
|
|
1599
|
+
break;
|
|
1600
|
+
case "gte":
|
|
1601
|
+
comparison = utils.mkCase({
|
|
1602
|
+
gte: Timestamp.fromDate(filter.comparison.value),
|
|
1603
|
+
});
|
|
1604
|
+
break;
|
|
1605
|
+
case "between":
|
|
1606
|
+
comparison = utils.mkCase({
|
|
1607
|
+
within: provideFull(ManagerPB.DateFilter_Within, {
|
|
1608
|
+
startDate: Timestamp.fromDate(filter.comparison.from),
|
|
1609
|
+
endDate: Timestamp.fromDate(filter.comparison.to),
|
|
1610
|
+
}),
|
|
1611
|
+
});
|
|
1612
|
+
break;
|
|
1613
|
+
}
|
|
1614
|
+
return provideFull(ManagerPB.DateFilter, {
|
|
1615
|
+
comparison: comparison,
|
|
1616
|
+
});
|
|
1617
|
+
}
|
|
1618
|
+
|
|
1619
|
+
/** @public */
|
|
1620
|
+
export type JobFilter = TagFilter | IdFilter | DateFilter;
|
|
1621
|
+
|
|
1622
|
+
/** internal */
|
|
1623
|
+
function toJobFilter(jobFilter: JobFilter): ManagerPB.JobFilter {
|
|
1624
|
+
switch (jobFilter.filterType) {
|
|
1625
|
+
case "tag":
|
|
1626
|
+
return provideFull(ManagerPB.JobFilter, {
|
|
1627
|
+
jobFilter: utils.mkCase({ tagFilter: toTagFilter(jobFilter) }),
|
|
1628
|
+
});
|
|
1629
|
+
case "id":
|
|
1630
|
+
return provideFull(ManagerPB.JobFilter, {
|
|
1631
|
+
jobFilter: utils.mkCase({ idFilter: toIdFilter(jobFilter) }),
|
|
1632
|
+
});
|
|
1633
|
+
case "date":
|
|
1634
|
+
return provideFull(ManagerPB.JobFilter, {
|
|
1635
|
+
jobFilter: utils.mkCase({ dateFilter: toDateFilter(jobFilter) }),
|
|
1636
|
+
});
|
|
1637
|
+
}
|
|
1638
|
+
}
|
|
1639
|
+
|
|
1640
|
+
/**
|
|
1641
|
+
* @public
|
|
1642
|
+
* Configuration for creating an event stream
|
|
1643
|
+
*/
|
|
1644
|
+
export type EventStreamSettings = {
|
|
1645
|
+
pendingWindow: number; // Seconds from now that jobs are considered pending
|
|
1646
|
+
onEvent: (event: EventStreamEvent) => void;
|
|
1647
|
+
};
|
|
1648
|
+
|
|
1649
|
+
/**
|
|
1650
|
+
* @public
|
|
1651
|
+
*/
|
|
1652
|
+
export type EventStreamInitialState = {
|
|
1653
|
+
nodesStarting: NodeStarting[];
|
|
1654
|
+
nodesRunning: NodeRunning[];
|
|
1655
|
+
activeJobs: JobWithHistory[];
|
|
1656
|
+
health: ProviderHealth[];
|
|
1657
|
+
closeStream: () => void;
|
|
1658
|
+
};
|
|
1659
|
+
|
|
1660
|
+
/**
|
|
1661
|
+
* @public
|
|
1662
|
+
*/
|
|
1663
|
+
export type EventStreamEvent =
|
|
1664
|
+
| JobUpdated // Only for jobs that are active or pending - you don't get notification about a job 2 years in the future
|
|
1665
|
+
| JobPending
|
|
1666
|
+
| JobOutOfWindow
|
|
1667
|
+
| JobDeleted
|
|
1668
|
+
| JobInfo
|
|
1669
|
+
| NodeStarting
|
|
1670
|
+
| NodeStarted
|
|
1671
|
+
| NodeStopping
|
|
1672
|
+
| NodeStopped
|
|
1673
|
+
| PhysicalNodeConnected
|
|
1674
|
+
| ProviderHealthChange
|
|
1675
|
+
| EventStreamClosed;
|
|
1676
|
+
|
|
1677
|
+
/**
|
|
1678
|
+
* @public
|
|
1679
|
+
*/
|
|
1680
|
+
export type JobSearchSettings = {
|
|
1681
|
+
filter: JobFilter[];
|
|
1682
|
+
};
|
|
1683
|
+
|
|
1684
|
+
/**
|
|
1685
|
+
* @public
|
|
1686
|
+
*/
|
|
1687
|
+
export type CurrentJob = {
|
|
1688
|
+
job: Job;
|
|
1689
|
+
instances: RunningJob[];
|
|
1690
|
+
};
|
|
1691
|
+
|
|
1692
|
+
/** @internal */
|
|
1693
|
+
function fromCurrentJob(event: ManagerPB.CurrentJob): CurrentJob | undefined {
|
|
1694
|
+
return utils.mapOptional(
|
|
1695
|
+
(job) => ({
|
|
1696
|
+
job: fromJob(job),
|
|
1697
|
+
instances: event.instances.map(fromRunningJob),
|
|
1698
|
+
}),
|
|
1699
|
+
event.job
|
|
1700
|
+
);
|
|
1701
|
+
}
|
|
1702
|
+
|
|
1703
|
+
/**
|
|
1704
|
+
* @public
|
|
1705
|
+
* List of events that can be raised by the jobList API - TODO - link
|
|
1706
|
+
*/
|
|
1707
|
+
export type JobListEvent = JobUpdated | JobPending;
|
|
1708
|
+
|
|
1709
|
+
/**
|
|
1710
|
+
* @public
|
|
1711
|
+
*/
|
|
1712
|
+
export type Health = "healthy" | "unstable" | "failed";
|
|
1713
|
+
|
|
1714
|
+
/**
|
|
1715
|
+
* @public
|
|
1716
|
+
*/
|
|
1717
|
+
export type AwsHealth = {
|
|
1718
|
+
provider: "aws";
|
|
1719
|
+
regions: Map<AwsRegion, Health>;
|
|
1720
|
+
};
|
|
1721
|
+
|
|
1722
|
+
/**
|
|
1723
|
+
* @public
|
|
1724
|
+
*/
|
|
1725
|
+
export type OciHealth = {
|
|
1726
|
+
provider: "oci";
|
|
1727
|
+
};
|
|
1728
|
+
|
|
1729
|
+
/**
|
|
1730
|
+
* @public
|
|
1731
|
+
*/
|
|
1732
|
+
export type ProviderHealth = AwsHealth | OciHealth;
|
|
1733
|
+
|
|
1734
|
+
/** @internal */
|
|
1735
|
+
function fromHealth(health: ManagerPB.Health): Health {
|
|
1736
|
+
switch (health) {
|
|
1737
|
+
case ManagerPB.Health.HEALTHY:
|
|
1738
|
+
return "healthy";
|
|
1739
|
+
case ManagerPB.Health.UNSTABLE:
|
|
1740
|
+
return "unstable";
|
|
1741
|
+
case ManagerPB.Health.FAILED:
|
|
1742
|
+
return "failed";
|
|
1743
|
+
}
|
|
1744
|
+
}
|
|
1745
|
+
|
|
1746
|
+
/** @internal */
|
|
1747
|
+
function fromAwsHealth(health: ManagerPB.AwsHealth): AwsHealth {
|
|
1748
|
+
const map: Map<AwsRegion, Health> = new Map();
|
|
1749
|
+
health.regionHealth.forEach((regionHealth) =>
|
|
1750
|
+
map.set(regionHealth.region, fromHealth(regionHealth.health))
|
|
1751
|
+
);
|
|
1752
|
+
return { provider: "aws", regions: map };
|
|
1753
|
+
}
|
|
1754
|
+
|
|
1755
|
+
/** @internal */
|
|
1756
|
+
function fromOciHealth(_health: ManagerPB.OciHealth): OciHealth {
|
|
1757
|
+
return { provider: "oci" };
|
|
1758
|
+
}
|
|
1759
|
+
|
|
1760
|
+
/** @internal */
|
|
1761
|
+
function fromProviderHealth(health: ManagerPB.ProviderHealth): ProviderHealth {
|
|
1762
|
+
const healthType = health.health.case;
|
|
1763
|
+
switch (healthType) {
|
|
1764
|
+
case undefined:
|
|
1765
|
+
throw Error();
|
|
1766
|
+
case "aws":
|
|
1767
|
+
return fromAwsHealth(health.health.value);
|
|
1768
|
+
case "oci":
|
|
1769
|
+
return fromOciHealth(health.health.value);
|
|
1770
|
+
case "test":
|
|
1771
|
+
throw Error();
|
|
1772
|
+
default: {
|
|
1773
|
+
const exhaustiveCheck: never = healthType;
|
|
1774
|
+
throw new Error(`Unhandled case: ${exhaustiveCheck}`);
|
|
1775
|
+
}
|
|
1776
|
+
}
|
|
1777
|
+
}
|
|
1778
|
+
|
|
1779
|
+
/**
|
|
1780
|
+
* @public
|
|
1781
|
+
* The entrypoint for all Norsk Manager applications
|
|
1782
|
+
*
|
|
1783
|
+
* @example
|
|
1784
|
+
* ```ts
|
|
1785
|
+
* const norsk = await Norsk.connect({ url: "localhost:6790" });
|
|
1786
|
+
* ```
|
|
1787
|
+
*/
|
|
1788
|
+
export class NorskManager {
|
|
1789
|
+
/** @internal */
|
|
1790
|
+
closed: boolean = false;
|
|
1791
|
+
/** @internal */
|
|
1792
|
+
client: ManagerClient;
|
|
1793
|
+
/** @internal */
|
|
1794
|
+
settings: NorskSettings;
|
|
1795
|
+
/** @internal */
|
|
1796
|
+
connectivityState: number;
|
|
1797
|
+
/** @internal */
|
|
1798
|
+
statusStream?: grpc.ClientReadableStream<ManagerPB.NorskStatusEvent>;
|
|
1799
|
+
/** @internal */
|
|
1800
|
+
managerActivityStreams: (
|
|
1801
|
+
| grpc.ClientReadableStream<ManagerPB.EventStreamEvent>
|
|
1802
|
+
| undefined
|
|
1803
|
+
)[] = [];
|
|
1804
|
+
nextEventStreamId: number = 0;
|
|
1805
|
+
/* @internal */
|
|
1806
|
+
public initialised: Promise<void>;
|
|
1807
|
+
/* @internal */
|
|
1808
|
+
public resolveInitialised: () => void;
|
|
1809
|
+
|
|
1810
|
+
/** @public */
|
|
1811
|
+
public static async connect(settings: NorskSettings) {
|
|
1812
|
+
const norsk = new NorskManager(settings);
|
|
1813
|
+
await norsk.initialised;
|
|
1814
|
+
return norsk;
|
|
1815
|
+
}
|
|
1816
|
+
|
|
1817
|
+
/** @internal */
|
|
1818
|
+
constructor(norskSettings: NorskSettings) {
|
|
1819
|
+
this.connectivityState = 0;
|
|
1820
|
+
this.client = new ManagerClient(
|
|
1821
|
+
norskSettings.url ? norskSettings.url : norskHost() + ":" + norskPort(),
|
|
1822
|
+
grpc.credentials.createInsecure()
|
|
1823
|
+
);
|
|
1824
|
+
this.settings = norskSettings;
|
|
1825
|
+
this.connectivityStateWatcher();
|
|
1826
|
+
this.initialised = new Promise((resolve, _reject) => {
|
|
1827
|
+
this.resolveInitialised = resolve;
|
|
1828
|
+
});
|
|
1829
|
+
}
|
|
1830
|
+
|
|
1831
|
+
/**
|
|
1832
|
+
* @public
|
|
1833
|
+
* Norsk Runtime version information
|
|
1834
|
+
*/
|
|
1835
|
+
public version: CommonVersion;
|
|
1836
|
+
|
|
1837
|
+
/**
|
|
1838
|
+
* @public
|
|
1839
|
+
* Close down the Norsk connection
|
|
1840
|
+
*/
|
|
1841
|
+
public close() {
|
|
1842
|
+
this.statusStream?.cancel();
|
|
1843
|
+
for (const stream of this.managerActivityStreams) {
|
|
1844
|
+
stream!.cancel();
|
|
1845
|
+
}
|
|
1846
|
+
try {
|
|
1847
|
+
this.client.close();
|
|
1848
|
+
} catch { }
|
|
1849
|
+
this.closed = true;
|
|
1850
|
+
}
|
|
1851
|
+
|
|
1852
|
+
/**
|
|
1853
|
+
* @public - TODO - this can fail (duplicate id etc)
|
|
1854
|
+
*/
|
|
1855
|
+
public async createJob(job: CreateJob): Promise<Job> {
|
|
1856
|
+
const fn = safeBind(promisifyUnary(this.client.createJob), this.client);
|
|
1857
|
+
const jobFull: Job = { ...job, state: "pre", currentHash: BigInt(0) };
|
|
1858
|
+
const newHash = await fn(toJob(jobFull));
|
|
1859
|
+
jobFull.currentHash = newHash.hash;
|
|
1860
|
+
return jobFull;
|
|
1861
|
+
}
|
|
1862
|
+
|
|
1863
|
+
/**
|
|
1864
|
+
* @public - TODO - this can fail (version check etc)
|
|
1865
|
+
*/
|
|
1866
|
+
public async updateJob(job: Job): Promise<Job> {
|
|
1867
|
+
const fn = safeBind(promisifyUnary(this.client.updateJob), this.client);
|
|
1868
|
+
const newHash = await fn(toJob(job));
|
|
1869
|
+
job.currentHash = newHash.hash;
|
|
1870
|
+
return job;
|
|
1871
|
+
}
|
|
1872
|
+
|
|
1873
|
+
/**
|
|
1874
|
+
* @public - TODO - can it fail?
|
|
1875
|
+
*/
|
|
1876
|
+
public async deleteJob(jobId: JobId): Promise<void> {
|
|
1877
|
+
const fn = safeBind(promisifyUnary(this.client.deleteJob), this.client);
|
|
1878
|
+
await fn(toJobId(jobId));
|
|
1879
|
+
}
|
|
1880
|
+
|
|
1881
|
+
/**
|
|
1882
|
+
* @public - TODO - can it fail?
|
|
1883
|
+
*/
|
|
1884
|
+
public async completeJob(jobId: JobId): Promise<void> {
|
|
1885
|
+
const fn = safeBind(promisifyUnary(this.client.completeJob), this.client);
|
|
1886
|
+
await fn(toJobId(jobId));
|
|
1887
|
+
}
|
|
1888
|
+
|
|
1889
|
+
/**
|
|
1890
|
+
* @public - TODO
|
|
1891
|
+
*/
|
|
1892
|
+
public async createAwsNode(node: AwsNodeCreate): Promise<void> {
|
|
1893
|
+
const fn = safeBind(promisifyUnary(this.client.createAwsNode), this.client);
|
|
1894
|
+
await fn(toAwsNodeCreate(node));
|
|
1895
|
+
return;
|
|
1896
|
+
}
|
|
1897
|
+
|
|
1898
|
+
/**
|
|
1899
|
+
* @public - TODO
|
|
1900
|
+
*/
|
|
1901
|
+
public async createOciNode(node: OciNodeCreate): Promise<void> {
|
|
1902
|
+
const fn = safeBind(promisifyUnary(this.client.createOciNode), this.client);
|
|
1903
|
+
await fn(toOciNodeCreate(node));
|
|
1904
|
+
return;
|
|
1905
|
+
}
|
|
1906
|
+
|
|
1907
|
+
|
|
1908
|
+
/**
|
|
1909
|
+
* @public - TODO
|
|
1910
|
+
*/
|
|
1911
|
+
public async updatePhysicalNode(
|
|
1912
|
+
nodeId: PhysicalNodeId,
|
|
1913
|
+
daemonVersion: string
|
|
1914
|
+
): Promise<void> {
|
|
1915
|
+
const fn = safeBind(
|
|
1916
|
+
promisifyUnary(this.client.updatePhysicalNode),
|
|
1917
|
+
this.client
|
|
1918
|
+
);
|
|
1919
|
+
await fn(
|
|
1920
|
+
provideFull(ManagerPB.UpdatePhysicalNodeRequest, {
|
|
1921
|
+
id: nodeId,
|
|
1922
|
+
daemonVersion: daemonVersion,
|
|
1923
|
+
})
|
|
1924
|
+
);
|
|
1925
|
+
}
|
|
1926
|
+
|
|
1927
|
+
/**
|
|
1928
|
+
* @public - TODO
|
|
1929
|
+
*/
|
|
1930
|
+
public async terminateNode(nodeId: NodeId): Promise<void> {
|
|
1931
|
+
const fn = safeBind(promisifyUnary(this.client.terminateNode), this.client);
|
|
1932
|
+
await fn(toNodeId(nodeId));
|
|
1933
|
+
}
|
|
1934
|
+
|
|
1935
|
+
/**
|
|
1936
|
+
* @public - TODO
|
|
1937
|
+
*/
|
|
1938
|
+
public async startJob(
|
|
1939
|
+
jobId: JobId,
|
|
1940
|
+
role: Role,
|
|
1941
|
+
nodeId: NodeId
|
|
1942
|
+
): Promise<void> {
|
|
1943
|
+
const fn = safeBind(promisifyUnary(this.client.startJob), this.client);
|
|
1944
|
+
await fn(
|
|
1945
|
+
provideFull(ManagerPB.StartJobRequest, {
|
|
1946
|
+
jobId: toJobId(jobId),
|
|
1947
|
+
role: role,
|
|
1948
|
+
nodeId: toNodeId(nodeId),
|
|
1949
|
+
})
|
|
1950
|
+
);
|
|
1951
|
+
}
|
|
1952
|
+
|
|
1953
|
+
/**
|
|
1954
|
+
* @public - TODO
|
|
1955
|
+
*/
|
|
1956
|
+
public async stopJob(
|
|
1957
|
+
jobId: JobId,
|
|
1958
|
+
role: Role,
|
|
1959
|
+
nodeId: NodeId
|
|
1960
|
+
): Promise<void> {
|
|
1961
|
+
const fn = safeBind(promisifyUnary(this.client.stopJob), this.client);
|
|
1962
|
+
await fn(
|
|
1963
|
+
provideFull(ManagerPB.StopJobRequest, {
|
|
1964
|
+
jobId: toJobId(jobId),
|
|
1965
|
+
role: role,
|
|
1966
|
+
nodeId: toNodeId(nodeId),
|
|
1967
|
+
})
|
|
1968
|
+
);
|
|
1969
|
+
}
|
|
1970
|
+
|
|
1971
|
+
/**
|
|
1972
|
+
* @public - TODO - check name
|
|
1973
|
+
* provides a stream of activity that is essential for the creation of client-side
|
|
1974
|
+
* management logic.
|
|
1975
|
+
*/
|
|
1976
|
+
public async eventStream(
|
|
1977
|
+
settings: EventStreamSettings
|
|
1978
|
+
): Promise<[EventStreamInitialState, () => void]> {
|
|
1979
|
+
const stream = this.client.eventStream(
|
|
1980
|
+
provideFull(ManagerPB.EventStreamRequest, {
|
|
1981
|
+
pendingWindowS: settings.pendingWindow,
|
|
1982
|
+
})
|
|
1983
|
+
);
|
|
1984
|
+
|
|
1985
|
+
this.managerActivityStreams[this.nextEventStreamId] = stream;
|
|
1986
|
+
|
|
1987
|
+
const stop = () => {
|
|
1988
|
+
this.managerActivityStreams[this.nextEventStreamId]?.cancel;
|
|
1989
|
+
delete this.managerActivityStreams[this.nextEventStreamId];
|
|
1990
|
+
};
|
|
1991
|
+
|
|
1992
|
+
this.nextEventStreamId++;
|
|
1993
|
+
|
|
1994
|
+
return new Promise((resolve, reject) => {
|
|
1995
|
+
let resolved: boolean = false;
|
|
1996
|
+
const nodesStarting: NodeStarting[] = [];
|
|
1997
|
+
const nodesRunning: NodeRunning[] = [];
|
|
1998
|
+
const activeJobs: JobWithHistory[] = [];
|
|
1999
|
+
|
|
2000
|
+
stream.on("close", () => {
|
|
2001
|
+
if (resolved) {
|
|
2002
|
+
settings.onEvent({ event: "eventStreamClosed" });
|
|
2003
|
+
} else {
|
|
2004
|
+
reject();
|
|
2005
|
+
}
|
|
2006
|
+
});
|
|
2007
|
+
|
|
2008
|
+
stream.on("error", (err: grpc.ServiceError) => {
|
|
2009
|
+
if (err.code == grpc.status.CANCELLED) {
|
|
2010
|
+
// close will get called next...
|
|
2011
|
+
return;
|
|
2012
|
+
}
|
|
2013
|
+
|
|
2014
|
+
if (resolved) {
|
|
2015
|
+
settings.onEvent({ event: "eventStreamClosed", error: err });
|
|
2016
|
+
} else {
|
|
2017
|
+
reject();
|
|
2018
|
+
}
|
|
2019
|
+
});
|
|
2020
|
+
|
|
2021
|
+
stream.on("data", (data: ManagerPB.EventStreamEvent) => {
|
|
2022
|
+
const messageCase = data.event.case;
|
|
2023
|
+
switch (messageCase) {
|
|
2024
|
+
case undefined:
|
|
2025
|
+
break;
|
|
2026
|
+
case "initialDataComplete":
|
|
2027
|
+
resolve([
|
|
2028
|
+
{
|
|
2029
|
+
nodesRunning,
|
|
2030
|
+
nodesStarting,
|
|
2031
|
+
activeJobs,
|
|
2032
|
+
health: data.event.value.health.map(fromProviderHealth),
|
|
2033
|
+
closeStream: () => {
|
|
2034
|
+
stream.cancel();
|
|
2035
|
+
},
|
|
2036
|
+
},
|
|
2037
|
+
stop,
|
|
2038
|
+
]);
|
|
2039
|
+
resolved = true;
|
|
2040
|
+
break;
|
|
2041
|
+
case "jobActive":
|
|
2042
|
+
// should only get jobActive prior to initialDataLoad completing
|
|
2043
|
+
if (!resolved && data.event.value.jobWithHistory) {
|
|
2044
|
+
const jobWithHistory = fromJobWithHistory(
|
|
2045
|
+
data.event.value.jobWithHistory
|
|
2046
|
+
);
|
|
2047
|
+
if (jobWithHistory !== undefined) {
|
|
2048
|
+
activeJobs.push(jobWithHistory);
|
|
2049
|
+
}
|
|
2050
|
+
}
|
|
2051
|
+
break;
|
|
2052
|
+
case "jobUpdated":
|
|
2053
|
+
utils.mapOptional(
|
|
2054
|
+
settings.onEvent,
|
|
2055
|
+
fromJobUpdated(data.event.value)
|
|
2056
|
+
);
|
|
2057
|
+
break;
|
|
2058
|
+
case "jobPending":
|
|
2059
|
+
utils.mapOptional(
|
|
2060
|
+
settings.onEvent,
|
|
2061
|
+
fromJobPending(data.event.value)
|
|
2062
|
+
);
|
|
2063
|
+
break;
|
|
2064
|
+
case "jobDeleted":
|
|
2065
|
+
utils.mapOptional(
|
|
2066
|
+
settings.onEvent,
|
|
2067
|
+
fromJobDeleted(data.event.value)
|
|
2068
|
+
);
|
|
2069
|
+
break;
|
|
2070
|
+
case "jobOutOfWindow":
|
|
2071
|
+
utils.mapOptional(
|
|
2072
|
+
settings.onEvent,
|
|
2073
|
+
fromJobOutOfWindow(data.event.value)
|
|
2074
|
+
);
|
|
2075
|
+
break;
|
|
2076
|
+
case "nodeRunning":
|
|
2077
|
+
// should only get nodeRunning prior to initialDataLoad completing
|
|
2078
|
+
if (!resolved) {
|
|
2079
|
+
const nodeRunningEvent = fromNodeRunning(data.event.value);
|
|
2080
|
+
if (nodeRunningEvent !== undefined) {
|
|
2081
|
+
nodesRunning.push(nodeRunningEvent);
|
|
2082
|
+
}
|
|
2083
|
+
}
|
|
2084
|
+
break;
|
|
2085
|
+
case "nodeStarting":
|
|
2086
|
+
{
|
|
2087
|
+
const nodeStartingEvent = fromNodeStarting(data.event.value);
|
|
2088
|
+
if (nodeStartingEvent) {
|
|
2089
|
+
if (resolved) {
|
|
2090
|
+
settings.onEvent(nodeStartingEvent);
|
|
2091
|
+
} else {
|
|
2092
|
+
nodesStarting.push(nodeStartingEvent);
|
|
2093
|
+
}
|
|
2094
|
+
}
|
|
2095
|
+
}
|
|
2096
|
+
break;
|
|
2097
|
+
case "nodeStarted":
|
|
2098
|
+
utils.mapOptional(
|
|
2099
|
+
settings.onEvent,
|
|
2100
|
+
fromNodeStarted(data.event.value)
|
|
2101
|
+
);
|
|
2102
|
+
break;
|
|
2103
|
+
case "nodeStopping":
|
|
2104
|
+
utils.mapOptional(
|
|
2105
|
+
settings.onEvent,
|
|
2106
|
+
fromNodeStopping(data.event.value)
|
|
2107
|
+
);
|
|
2108
|
+
break;
|
|
2109
|
+
case "nodeStopped":
|
|
2110
|
+
utils.mapOptional(
|
|
2111
|
+
settings.onEvent,
|
|
2112
|
+
fromNodeStopped(data.event.value)
|
|
2113
|
+
);
|
|
2114
|
+
break;
|
|
2115
|
+
case "physicalNodeConnected":
|
|
2116
|
+
utils.mapOptional(
|
|
2117
|
+
settings.onEvent,
|
|
2118
|
+
fromPhysicalNodeConnected(data.event.value)
|
|
2119
|
+
);
|
|
2120
|
+
break;
|
|
2121
|
+
case "providerHealthChange":
|
|
2122
|
+
utils.mapOptional(settings.onEvent, {
|
|
2123
|
+
event: "providerHealthChange",
|
|
2124
|
+
health: fromProviderHealth(data.event.value),
|
|
2125
|
+
});
|
|
2126
|
+
break;
|
|
2127
|
+
case "jobInfo":
|
|
2128
|
+
utils.mapOptional(settings.onEvent, fromJobInfo(data.event.value));
|
|
2129
|
+
break;
|
|
2130
|
+
default:
|
|
2131
|
+
utils.exhaustiveCheck(messageCase);
|
|
2132
|
+
}
|
|
2133
|
+
});
|
|
2134
|
+
});
|
|
2135
|
+
}
|
|
2136
|
+
|
|
2137
|
+
/**
|
|
2138
|
+
* @public
|
|
2139
|
+
* search for jobs by id
|
|
2140
|
+
*/
|
|
2141
|
+
public async jobById(id: JobId): Promise<CurrentJob | undefined> {
|
|
2142
|
+
const jobSearchResults = await this.jobSearch({
|
|
2143
|
+
filter: [
|
|
2144
|
+
{ filterType: "id", comparison: { comparison: "eq", value: id } },
|
|
2145
|
+
],
|
|
2146
|
+
});
|
|
2147
|
+
|
|
2148
|
+
if (jobSearchResults.length != 1) {
|
|
2149
|
+
return undefined;
|
|
2150
|
+
} else {
|
|
2151
|
+
return jobSearchResults[0];
|
|
2152
|
+
}
|
|
2153
|
+
}
|
|
2154
|
+
|
|
2155
|
+
/**
|
|
2156
|
+
* @public
|
|
2157
|
+
* search for jobs by tag / date etc
|
|
2158
|
+
*/
|
|
2159
|
+
public async jobSearch(settings: JobSearchSettings): Promise<CurrentJob[]> {
|
|
2160
|
+
const stream = this.client.jobSearch(
|
|
2161
|
+
provideFull(ManagerPB.JobSearchRequest, {
|
|
2162
|
+
jobFilter: settings.filter.map(toJobFilter),
|
|
2163
|
+
})
|
|
2164
|
+
);
|
|
2165
|
+
|
|
2166
|
+
return new Promise((resolve, reject) => {
|
|
2167
|
+
const jobs: CurrentJob[] = [];
|
|
2168
|
+
// TODO - handle stream errors...
|
|
2169
|
+
|
|
2170
|
+
stream.on("data", (data: ManagerPB.CurrentJob) => {
|
|
2171
|
+
const job = fromCurrentJob(data);
|
|
2172
|
+
if (job !== undefined) {
|
|
2173
|
+
jobs.push(job);
|
|
2174
|
+
}
|
|
2175
|
+
});
|
|
2176
|
+
stream.on("close", () => resolve(jobs));
|
|
2177
|
+
stream.on("error", (err) => reject(err));
|
|
2178
|
+
});
|
|
2179
|
+
}
|
|
2180
|
+
|
|
2181
|
+
/** @internal */
|
|
2182
|
+
handleStatusEvent(data: ManagerPB.NorskStatusEvent) {
|
|
2183
|
+
const messageCase = data.message.case;
|
|
2184
|
+
switch (messageCase) {
|
|
2185
|
+
case undefined:
|
|
2186
|
+
break;
|
|
2187
|
+
case "hello": {
|
|
2188
|
+
debuglog(
|
|
2189
|
+
"Norsk status channel connected: %s",
|
|
2190
|
+
data.message.value.version
|
|
2191
|
+
);
|
|
2192
|
+
if (data.message.value.version === undefined) {
|
|
2193
|
+
throw new Error("Norsk version is undefined");
|
|
2194
|
+
} else {
|
|
2195
|
+
this.version = data.message.value.version;
|
|
2196
|
+
}
|
|
2197
|
+
this.resolveInitialised();
|
|
2198
|
+
this.settings.onHello &&
|
|
2199
|
+
data.message.value.version &&
|
|
2200
|
+
this.settings.onHello(data.message.value.version);
|
|
2201
|
+
break;
|
|
2202
|
+
}
|
|
2203
|
+
case "logEvent": {
|
|
2204
|
+
let level:
|
|
2205
|
+
| "emergency"
|
|
2206
|
+
| "alert"
|
|
2207
|
+
| "critical"
|
|
2208
|
+
| "error"
|
|
2209
|
+
| "warning"
|
|
2210
|
+
| "notice"
|
|
2211
|
+
| "info"
|
|
2212
|
+
| "debug";
|
|
2213
|
+
|
|
2214
|
+
switch (data.message.value.level) {
|
|
2215
|
+
case Log_Level.EMERGENCY:
|
|
2216
|
+
level = "emergency";
|
|
2217
|
+
break;
|
|
2218
|
+
case Log_Level.ALERT:
|
|
2219
|
+
level = "alert";
|
|
2220
|
+
break;
|
|
2221
|
+
case Log_Level.CRITICAL:
|
|
2222
|
+
level = "critical";
|
|
2223
|
+
break;
|
|
2224
|
+
case Log_Level.ERROR:
|
|
2225
|
+
level = "error";
|
|
2226
|
+
break;
|
|
2227
|
+
case Log_Level.WARNING:
|
|
2228
|
+
level = "warning";
|
|
2229
|
+
break;
|
|
2230
|
+
case Log_Level.NOTICE:
|
|
2231
|
+
level = "notice";
|
|
2232
|
+
break;
|
|
2233
|
+
case Log_Level.INFO:
|
|
2234
|
+
level = "info";
|
|
2235
|
+
break;
|
|
2236
|
+
case Log_Level.DEBUG:
|
|
2237
|
+
level = "debug";
|
|
2238
|
+
break;
|
|
2239
|
+
}
|
|
2240
|
+
const log: Log = {
|
|
2241
|
+
level: level,
|
|
2242
|
+
message: data.message.value.msg,
|
|
2243
|
+
timestamp: new Date(Number(data.message.value.timestamp) / 1000),
|
|
2244
|
+
};
|
|
2245
|
+
if (this.settings.onLogEvent) {
|
|
2246
|
+
this.settings.onLogEvent(log);
|
|
2247
|
+
} else {
|
|
2248
|
+
debuglog("Norsk log event: %o", log);
|
|
2249
|
+
}
|
|
2250
|
+
break;
|
|
2251
|
+
}
|
|
2252
|
+
default: {
|
|
2253
|
+
const exhaustiveCheck: never = messageCase;
|
|
2254
|
+
throw new Error(`Unhandled case: ${exhaustiveCheck}`);
|
|
2255
|
+
}
|
|
2256
|
+
}
|
|
2257
|
+
}
|
|
2258
|
+
|
|
2259
|
+
/** @internal */
|
|
2260
|
+
connectivityStateWatcher() {
|
|
2261
|
+
if (this.closed) {
|
|
2262
|
+
return;
|
|
2263
|
+
}
|
|
2264
|
+
const channel = this.client.getChannel();
|
|
2265
|
+
const connectivityState = channel.getConnectivityState(true);
|
|
2266
|
+
let stateStr: string = connectivityState.toString();
|
|
2267
|
+
|
|
2268
|
+
switch (connectivityState) {
|
|
2269
|
+
case 0: {
|
|
2270
|
+
if (this.connectivityState == 1 || this.connectivityState == 2) {
|
|
2271
|
+
this.settings.onShutdown && this.settings.onShutdown();
|
|
2272
|
+
}
|
|
2273
|
+
// Idle
|
|
2274
|
+
stateStr = "idle";
|
|
2275
|
+
this.settings.onAttemptingToConnect &&
|
|
2276
|
+
this.settings.onAttemptingToConnect();
|
|
2277
|
+
break;
|
|
2278
|
+
}
|
|
2279
|
+
case 1: {
|
|
2280
|
+
// Connecting
|
|
2281
|
+
stateStr = "connecting";
|
|
2282
|
+
this.settings.onConnecting && this.settings.onConnecting();
|
|
2283
|
+
break;
|
|
2284
|
+
}
|
|
2285
|
+
case 2: {
|
|
2286
|
+
// Ready
|
|
2287
|
+
stateStr = "ready";
|
|
2288
|
+
this.settings.onReady && this.settings.onReady();
|
|
2289
|
+
this.statusStream = this.client.createStatusChannel(new Empty());
|
|
2290
|
+
this.statusStream.on("data", this.handleStatusEvent.bind(this));
|
|
2291
|
+
this.statusStream.on("error", () => {
|
|
2292
|
+
return;
|
|
2293
|
+
});
|
|
2294
|
+
break;
|
|
2295
|
+
}
|
|
2296
|
+
case 3: {
|
|
2297
|
+
// Transient failure
|
|
2298
|
+
stateStr = "transient failure";
|
|
2299
|
+
this.settings.onFailedToConnect && this.settings.onFailedToConnect();
|
|
2300
|
+
break;
|
|
2301
|
+
}
|
|
2302
|
+
case 4: {
|
|
2303
|
+
// Shutdown
|
|
2304
|
+
stateStr = "shutdown";
|
|
2305
|
+
this.settings.onShutdown && this.settings.onShutdown();
|
|
2306
|
+
break;
|
|
2307
|
+
}
|
|
2308
|
+
}
|
|
2309
|
+
debuglog(
|
|
2310
|
+
"Channel connectivity state change: %d / %s",
|
|
2311
|
+
connectivityState,
|
|
2312
|
+
stateStr
|
|
2313
|
+
);
|
|
2314
|
+
|
|
2315
|
+
this.connectivityState = connectivityState;
|
|
2316
|
+
channel.watchConnectivityState(connectivityState, Infinity, () => {
|
|
2317
|
+
this.connectivityStateWatcher();
|
|
2318
|
+
});
|
|
2319
|
+
}
|
|
2320
|
+
}
|
|
2321
|
+
|
|
2322
|
+
// This is basically AsyncIterable<EventStreamEvent>, except that allows an `any` for the return type of next
|
|
2323
|
+
interface EventStreamAsyncIterable {
|
|
2324
|
+
[Symbol.asyncIterator](): AsyncIterator<EventStreamEvent, EventStreamEvent>;
|
|
2325
|
+
}
|
|
2326
|
+
|
|
2327
|
+
export async function eventStream(
|
|
2328
|
+
norsk: NorskManager,
|
|
2329
|
+
options: {
|
|
2330
|
+
pendingWindow: number;
|
|
2331
|
+
}
|
|
2332
|
+
): Promise<[EventStreamInitialState, EventStreamAsyncIterable]> {
|
|
2333
|
+
let nextEventToReturn = 0;
|
|
2334
|
+
let nextEventToArrive = 0;
|
|
2335
|
+
|
|
2336
|
+
const resolves: ((arg: {
|
|
2337
|
+
value: EventStreamEvent;
|
|
2338
|
+
done: boolean;
|
|
2339
|
+
}) => void)[] = [];
|
|
2340
|
+
const promises: (Promise<IteratorResult<EventStreamEvent>> | undefined)[] =
|
|
2341
|
+
[];
|
|
2342
|
+
|
|
2343
|
+
const iter = {
|
|
2344
|
+
[Symbol.asyncIterator]() {
|
|
2345
|
+
return {
|
|
2346
|
+
next() {
|
|
2347
|
+
const mPromise = promises[nextEventToReturn];
|
|
2348
|
+
if (mPromise) {
|
|
2349
|
+
// If we have a promise available, then just return it
|
|
2350
|
+
delete promises[nextEventToReturn];
|
|
2351
|
+
nextEventToReturn++;
|
|
2352
|
+
return mPromise;
|
|
2353
|
+
} else {
|
|
2354
|
+
const promise: Promise<IteratorResult<EventStreamEvent>> =
|
|
2355
|
+
new Promise((r) => (resolves[nextEventToReturn] = r));
|
|
2356
|
+
nextEventToReturn++;
|
|
2357
|
+
return promise;
|
|
2358
|
+
}
|
|
2359
|
+
},
|
|
2360
|
+
};
|
|
2361
|
+
},
|
|
2362
|
+
};
|
|
2363
|
+
|
|
2364
|
+
const [initialState, _stop] = await norsk.eventStream({
|
|
2365
|
+
pendingWindow: options.pendingWindow,
|
|
2366
|
+
onEvent: (event: EventStreamEvent) => {
|
|
2367
|
+
const done: boolean = event.event == "eventStreamClosed";
|
|
2368
|
+
if (resolves[nextEventToArrive]) {
|
|
2369
|
+
resolves[nextEventToArrive]({ value: event, done: done });
|
|
2370
|
+
delete resolves[nextEventToArrive];
|
|
2371
|
+
nextEventToArrive++;
|
|
2372
|
+
} else {
|
|
2373
|
+
promises[nextEventToArrive] = new Promise((r) =>
|
|
2374
|
+
r({ value: event, done: done })
|
|
2375
|
+
);
|
|
2376
|
+
nextEventToArrive++;
|
|
2377
|
+
}
|
|
2378
|
+
},
|
|
2379
|
+
});
|
|
2380
|
+
|
|
2381
|
+
return [initialState, iter];
|
|
2382
|
+
}
|
|
2383
|
+
|
|
2384
|
+
/** @private */
|
|
2385
|
+
function norskHost(): string {
|
|
2386
|
+
if (process.env.NORSK_HOST) return process.env.NORSK_HOST;
|
|
2387
|
+
return "localhost";
|
|
2388
|
+
}
|
|
2389
|
+
|
|
2390
|
+
/** @private */
|
|
2391
|
+
function norskPort(): string {
|
|
2392
|
+
if (process.env.NORSK_PORT) return process.env.NORSK_PORT;
|
|
2393
|
+
return "6789";
|
|
2394
|
+
}
|
|
2395
|
+
|
|
2396
|
+
/** @private */
|
|
2397
|
+
function promisifyUnary<T, TResult>(requestFn: {
|
|
2398
|
+
(
|
|
2399
|
+
request: T,
|
|
2400
|
+
callback: (error: grpc.ServiceError | null, response: TResult) => void
|
|
2401
|
+
): SurfaceCall;
|
|
2402
|
+
(
|
|
2403
|
+
request: T,
|
|
2404
|
+
metadata: grpc.Metadata,
|
|
2405
|
+
callback: (error: grpc.ServiceError | null, response: TResult) => void
|
|
2406
|
+
): SurfaceCall;
|
|
2407
|
+
(
|
|
2408
|
+
request: T,
|
|
2409
|
+
metadata: grpc.Metadata,
|
|
2410
|
+
options: Partial<grpc.CallOptions>,
|
|
2411
|
+
callback: (error: grpc.ServiceError | null, response: TResult) => void
|
|
2412
|
+
): SurfaceCall;
|
|
2413
|
+
}): (request: T) => Promise<TResult> {
|
|
2414
|
+
return util.promisify(requestFn);
|
|
2415
|
+
}
|
|
2416
|
+
|
|
2417
|
+
/** @private */
|
|
2418
|
+
function safeBind<T, TResult>(
|
|
2419
|
+
fn: (arg: T) => TResult,
|
|
2420
|
+
obj: unknown
|
|
2421
|
+
): (arg: T) => TResult {
|
|
2422
|
+
return fn.bind(obj);
|
|
2423
|
+
}
|