@scrypted/server 0.123.33 → 0.123.35

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.
Files changed (39) hide show
  1. package/dist/cluster/cluster-labels.d.ts +5 -0
  2. package/dist/cluster/cluster-labels.js +15 -5
  3. package/dist/cluster/cluster-labels.js.map +1 -1
  4. package/dist/cluster/cluster-setup.js +12 -5
  5. package/dist/cluster/cluster-setup.js.map +1 -1
  6. package/dist/plugin/plugin-host.d.ts +1 -0
  7. package/dist/plugin/plugin-host.js +8 -2
  8. package/dist/plugin/plugin-host.js.map +1 -1
  9. package/dist/plugin/plugin-remote-worker.js +2 -2
  10. package/dist/plugin/plugin-remote-worker.js.map +1 -1
  11. package/dist/plugin/runtime/cluster-fork-worker.js +1 -1
  12. package/dist/plugin/runtime/cluster-fork-worker.js.map +1 -1
  13. package/dist/scrypted-cluster-main.d.ts +13 -3
  14. package/dist/scrypted-cluster-main.js +97 -77
  15. package/dist/scrypted-cluster-main.js.map +1 -1
  16. package/dist/scrypted-server-main.js +19 -8
  17. package/dist/scrypted-server-main.js.map +1 -1
  18. package/dist/services/cluster-fork.d.ts +3 -3
  19. package/dist/services/cluster-fork.js +54 -14
  20. package/dist/services/cluster-fork.js.map +1 -1
  21. package/package.json +1 -1
  22. package/python/cluster_labels.py +4 -1
  23. package/python/cluster_setup.py +16 -7
  24. package/python/plugin_console.py +1 -0
  25. package/python/plugin_pip.py +14 -8
  26. package/python/plugin_remote.py +120 -38
  27. package/python/plugin_repl.py +42 -15
  28. package/python/plugin_volume.py +17 -11
  29. package/python/rpc-iterator-test.py +11 -8
  30. package/python/rpc.py +242 -154
  31. package/python/rpc_reader.py +35 -28
  32. package/src/cluster/cluster-labels.ts +16 -5
  33. package/src/cluster/cluster-setup.ts +12 -5
  34. package/src/plugin/plugin-host.ts +11 -3
  35. package/src/plugin/plugin-remote-worker.ts +4 -5
  36. package/src/plugin/runtime/cluster-fork-worker.ts +1 -1
  37. package/src/scrypted-cluster-main.ts +123 -91
  38. package/src/scrypted-server-main.ts +24 -11
  39. package/src/services/cluster-fork.ts +64 -18
@@ -1,6 +1,6 @@
1
1
  import bodyParser from 'body-parser';
2
2
  import cookieParser from 'cookie-parser';
3
- import crypto from 'crypto';
3
+ import crypto, { scrypt } from 'crypto';
4
4
  import { once } from 'events';
5
5
  import express, { Request } from 'express';
6
6
  import fs from 'fs';
@@ -23,7 +23,7 @@ import { getNpmPackageInfo } from './services/plugin';
23
23
  import { setScryptedUserPassword, UsersService } from './services/users';
24
24
  import { sleep } from './sleep';
25
25
  import { ONE_DAY_MILLISECONDS, UserToken } from './usertoken';
26
- import { createClusterServer } from './scrypted-cluster-main';
26
+ import { createClusterServer, startClusterClient } from './scrypted-cluster-main';
27
27
  import { getScryptedClusterMode } from './cluster/cluster-setup';
28
28
 
29
29
  export type Runtime = ScryptedRuntime;
@@ -45,10 +45,11 @@ installSourceMapSupport({
45
45
  });
46
46
 
47
47
  let workerInspectPort: number = undefined;
48
+ let workerInspectAddress: string = undefined;
48
49
 
49
50
  async function doconnect(): Promise<net.Socket> {
50
51
  return new Promise((resolve, reject) => {
51
- const target = net.connect(workerInspectPort, '127.0.0.1');
52
+ const target = net.connect(workerInspectPort, workerInspectAddress);
52
53
  target.once('error', reject)
53
54
  target.once('connect', () => resolve(target))
54
55
  })
@@ -100,7 +101,7 @@ app.use(bodyParser.raw({ type: 'application/*', limit: 100000000 }) as any)
100
101
  async function start(mainFilename: string, options?: {
101
102
  onRuntimeCreated?: (runtime: ScryptedRuntime) => Promise<void>,
102
103
  }) {
103
- console.log('Cluster server starting.');
104
+ console.log('Scrypted server starting.');
104
105
  const volumeDir = getScryptedVolume();
105
106
  await fs.promises.mkdir(volumeDir, {
106
107
  recursive: true
@@ -350,6 +351,14 @@ async function start(mainFilename: string, options?: {
350
351
 
351
352
  const scrypted = new ScryptedRuntime(mainFilename, db, insecure, secure, app);
352
353
  await options?.onRuntimeCreated?.(scrypted);
354
+
355
+ const clusterMode = getScryptedClusterMode();
356
+ if (clusterMode?.[0] === 'server') {
357
+ console.log('Cluster server starting.');
358
+ const clusterServer = createClusterServer(mainFilename, scrypted, keyPair);
359
+ await listenServerPort('SCRYPTED_CLUSTER_SERVER', clusterMode[2], clusterServer);
360
+ }
361
+
353
362
  await scrypted.start();
354
363
 
355
364
 
@@ -465,11 +474,20 @@ async function start(mainFilename: string, options?: {
465
474
  waitDebug.catch(() => { });
466
475
 
467
476
  workerInspectPort = Math.round(Math.random() * 10000) + 30000;
477
+ workerInspectAddress = '127.0.0.1';
468
478
  try {
469
- await scrypted.installPlugin(plugin, {
479
+ const host = await scrypted.installPlugin(plugin, {
470
480
  waitDebug,
471
481
  inspectPort: workerInspectPort,
472
482
  });
483
+
484
+ const clusterWorkerId = await host.clusterWorkerId;
485
+ if (clusterWorkerId) {
486
+ const clusterWorker = scrypted.clusterWorkers.get(clusterWorkerId);
487
+ if (clusterWorker) {
488
+ workerInspectAddress = clusterWorker.address;
489
+ }
490
+ }
473
491
  }
474
492
  catch (e) {
475
493
  res.header('Content-Type', 'text/plain');
@@ -480,6 +498,7 @@ async function start(mainFilename: string, options?: {
480
498
 
481
499
  res.send({
482
500
  workerInspectPort,
501
+ workerInspectAddress,
483
502
  });
484
503
  });
485
504
 
@@ -725,12 +744,6 @@ async function start(mainFilename: string, options?: {
725
744
  await listenServerPort('SCRYPTED_SECURE_PORT', SCRYPTED_SECURE_PORT, secure);
726
745
  await listenServerPort('SCRYPTED_INSECURE_PORT', SCRYPTED_INSECURE_PORT, insecure);
727
746
 
728
- const clusterMode = getScryptedClusterMode();
729
- if (clusterMode?.[0] === 'server') {
730
- const clusterServer = createClusterServer(scrypted, keyPair);
731
- await listenServerPort('SCRYPTED_CLUSTER_SERVER', clusterMode[2], clusterServer);
732
- }
733
-
734
747
  console.log('#######################################################');
735
748
  console.log(`Scrypted Volume : ${volumeDir}`);
736
749
  console.log(`Scrypted Server (Local) : https://localhost:${SCRYPTED_SECURE_PORT}/`);
@@ -1,14 +1,41 @@
1
- import { ClusterWorker } from "@scrypted/types";
2
1
  import { matchesClusterLabels } from "../cluster/cluster-labels";
3
- import type { ScryptedRuntime } from "../runtime";
4
- import type { ClusterForkOptions, ClusterForkParam, PeerLiveness, RunningClusterWorker } from "../scrypted-cluster-main";
5
2
  import type { RuntimeWorkerOptions } from "../plugin/runtime/runtime-worker";
3
+ import { RpcPeer } from "../rpc";
4
+ import type { ScryptedRuntime } from "../runtime";
5
+ import type { ClusterForkOptions, ClusterForkParam, ClusterForkResultInterface, PeerLiveness, RunningClusterWorker } from "../scrypted-cluster-main";
6
+
7
+ class WrappedForkResult implements ClusterForkResultInterface {
8
+ [RpcPeer.PROPERTY_PROXY_PROPERTIES] = {
9
+ clusterWorkerId: undefined as string,
10
+ };
11
+
12
+ constructor(public clusterWorkerId: string, public forkResult: Promise<ClusterForkResultInterface>) {
13
+ this[RpcPeer.PROPERTY_PROXY_PROPERTIES].clusterWorkerId = clusterWorkerId;
14
+ }
15
+
16
+ async kill() {
17
+ const fr = await this.forkResult.catch(() => { });
18
+ if (!fr)
19
+ return;
20
+ await fr.kill();
21
+ }
22
+
23
+ async getResult() {
24
+ const fr = await this.forkResult;
25
+ return fr.getResult();
26
+ }
27
+
28
+ async waitKilled() {
29
+ const fr = await this.forkResult;
30
+ await fr.waitKilled();
31
+ }
32
+ }
6
33
 
7
34
  export class ClusterForkService {
8
35
  constructor(public runtime: ScryptedRuntime) { }
9
36
 
10
37
  async fork(runtimeWorkerOptions: RuntimeWorkerOptions, options: ClusterForkOptions, peerLiveness: PeerLiveness, getZip: () => Promise<Buffer>) {
11
- const matchingWorkers = [...this.runtime.clusterWorkers.entries()].map(([id, worker]) => ({
38
+ let matchingWorkers = [...this.runtime.clusterWorkers.entries()].map(([id, worker]) => ({
12
39
  worker,
13
40
  matches: matchesClusterLabels(options, worker.labels),
14
41
  }))
@@ -17,7 +44,6 @@ export class ClusterForkService {
17
44
  // and worker id must match if provided
18
45
  return matches && (!options.clusterWorkerId || worker.id === options.clusterWorkerId);
19
46
  });
20
- matchingWorkers.sort((a, b) => b.worker.labels.length - a.worker.labels.length);
21
47
 
22
48
  let worker: RunningClusterWorker;
23
49
 
@@ -26,32 +52,52 @@ export class ClusterForkService {
26
52
  if (options.id)
27
53
  worker = matchingWorkers.find(({ worker }) => [...worker.forks].find(f => f.id === options.id))?.worker;
28
54
 
29
- // TODO: round robin?
30
- worker ||= matchingWorkers[0]?.worker;
31
-
32
55
  if (!worker) {
33
- if (options.clusterWorkerId)
34
- throw new Error(`no worker found for cluster id ${options.clusterWorkerId}`);
35
- throw new Error(`no worker found for cluster labels ${JSON.stringify(options.labels)}`);
56
+ // sort by number of matches, to find the best match.
57
+ matchingWorkers.sort((a, b) => b.matches - a.matches);
58
+
59
+ const bestMatch = matchingWorkers[0];
60
+
61
+ if (!bestMatch) {
62
+ if (options.clusterWorkerId)
63
+ throw new Error(`no worker found for cluster id ${options.clusterWorkerId}`);
64
+ throw new Error(`no worker found for cluster labels ${JSON.stringify(options.labels)}`);
65
+ }
66
+
67
+ // filter out workers that are not equivalent to the best match.
68
+ // this enforces the "prefer" label.
69
+ matchingWorkers = matchingWorkers.filter(({ matches }) => matches === bestMatch.matches)
70
+ // sort by number of forks, to distribute load.
71
+ .sort((a, b) => a.worker.forks.size - b.worker.forks.size);
72
+
73
+ worker = matchingWorkers[0]?.worker;
36
74
  }
37
75
 
38
- const fork: ClusterForkParam = await worker.peer.getParam('fork');
39
- const forkResult = await fork(options.runtime, runtimeWorkerOptions, peerLiveness, getZip);
76
+ console.log('forking to worker', worker.id, options);
77
+
78
+ worker.fork ||= worker.peer.getParam('fork');
79
+ const fork: ClusterForkParam = await worker.fork;
80
+ const forkResultPromise = fork(options.runtime, runtimeWorkerOptions, peerLiveness, getZip);
81
+
40
82
  options.id ||= this.runtime.findPluginDevice(runtimeWorkerOptions.packageJson.name)?._id;
41
83
  worker.forks.add(options);
42
- forkResult.waitKilled().catch(() => { }).finally(() => {
43
- worker.forks.delete(options);
84
+
85
+ forkResultPromise.then(forkResult => {
86
+ forkResult.clusterWorkerId = worker.id;
87
+ forkResult.waitKilled().catch(() => { }).finally(() => {
88
+ worker.forks.delete(options);
89
+ })
44
90
  });
45
91
 
46
- forkResult.clusterWorkerId = worker.id;
47
- return forkResult;
92
+ const ret: ClusterForkResultInterface = new WrappedForkResult(worker.id, forkResultPromise);
93
+ return ret;
48
94
  };
49
95
 
50
96
  async getClusterWorkers() {
51
97
  const ret: any = {};
52
98
  for (const worker of this.runtime.clusterWorkers.values()) {
53
99
  ret[worker.id] = {
54
- name: worker.peer.peerName,
100
+ name: worker.name,
55
101
  labels: worker.labels,
56
102
  forks: [...worker.forks],
57
103
  };