@kapeta/local-cluster-service 0.12.0 → 0.13.0
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/CHANGELOG.md +14 -0
- package/dist/cjs/index.js +2 -0
- package/dist/cjs/src/assetManager.d.ts +3 -1
- package/dist/cjs/src/assetManager.js +20 -4
- package/dist/cjs/src/assets/routes.js +22 -1
- package/dist/cjs/src/containerManager.d.ts +1 -1
- package/dist/cjs/src/containerManager.js +132 -122
- package/dist/cjs/src/instanceManager.d.ts +4 -3
- package/dist/cjs/src/instanceManager.js +87 -60
- package/dist/cjs/src/instances/routes.js +21 -11
- package/dist/cjs/src/operatorManager.d.ts +5 -3
- package/dist/cjs/src/operatorManager.js +34 -22
- package/dist/cjs/src/providerManager.js +1 -1
- package/dist/cjs/src/repositoryManager.d.ts +2 -4
- package/dist/cjs/src/repositoryManager.js +51 -66
- package/dist/cjs/src/socketManager.js +1 -1
- package/dist/cjs/src/taskManager.d.ts +64 -0
- package/dist/cjs/src/taskManager.js +163 -0
- package/dist/cjs/src/tasks/routes.d.ts +3 -0
- package/dist/cjs/src/tasks/routes.js +35 -0
- package/dist/cjs/src/utils/BlockInstanceRunner.js +0 -1
- package/dist/esm/index.js +2 -0
- package/dist/esm/src/assetManager.d.ts +3 -1
- package/dist/esm/src/assetManager.js +20 -4
- package/dist/esm/src/assets/routes.js +22 -1
- package/dist/esm/src/containerManager.d.ts +1 -1
- package/dist/esm/src/containerManager.js +132 -122
- package/dist/esm/src/instanceManager.d.ts +4 -3
- package/dist/esm/src/instanceManager.js +87 -60
- package/dist/esm/src/instances/routes.js +21 -11
- package/dist/esm/src/operatorManager.d.ts +5 -3
- package/dist/esm/src/operatorManager.js +34 -22
- package/dist/esm/src/providerManager.js +1 -1
- package/dist/esm/src/repositoryManager.d.ts +2 -4
- package/dist/esm/src/repositoryManager.js +51 -66
- package/dist/esm/src/socketManager.js +1 -1
- package/dist/esm/src/taskManager.d.ts +64 -0
- package/dist/esm/src/taskManager.js +159 -0
- package/dist/esm/src/tasks/routes.d.ts +3 -0
- package/dist/esm/src/tasks/routes.js +30 -0
- package/dist/esm/src/utils/BlockInstanceRunner.js +0 -1
- package/index.ts +2 -0
- package/package.json +1 -1
- package/src/assetManager.ts +28 -4
- package/src/assets/routes.ts +23 -1
- package/src/containerManager.ts +153 -142
- package/src/instanceManager.ts +116 -70
- package/src/instances/routes.ts +20 -12
- package/src/operatorManager.ts +46 -26
- package/src/providerManager.ts +1 -1
- package/src/repositoryManager.ts +65 -63
- package/src/socketManager.ts +1 -1
- package/src/taskManager.ts +225 -0
- package/src/tasks/routes.ts +38 -0
- package/src/utils/BlockInstanceRunner.ts +0 -4
@@ -0,0 +1,225 @@
|
|
1
|
+
/**
|
2
|
+
* Class that handles processing background tasks.
|
3
|
+
*/
|
4
|
+
import { socketManager } from './socketManager';
|
5
|
+
|
6
|
+
const EVENT_TASK_UPDATED = 'task-updated';
|
7
|
+
const EVENT_TASK_ADDED = 'task-added';
|
8
|
+
const EVENT_TASK_REMOVED = 'task-removed';
|
9
|
+
|
10
|
+
export type TaskRunner<T> = (task: Task<T>) => Promise<T>;
|
11
|
+
|
12
|
+
export enum TaskStatus {
|
13
|
+
PENDING = 'PENDING',
|
14
|
+
RUNNING = 'RUNNING',
|
15
|
+
COMPLETED = 'COMPLETED',
|
16
|
+
FAILED = 'FAILED',
|
17
|
+
}
|
18
|
+
|
19
|
+
interface Future<T = void> {
|
20
|
+
promise: Promise<T>;
|
21
|
+
resolve: (result: T) => void;
|
22
|
+
reject: (e: any) => void;
|
23
|
+
}
|
24
|
+
|
25
|
+
interface TaskMetadata {
|
26
|
+
name: string;
|
27
|
+
/**
|
28
|
+
* A unique prefix for the task. If defined only 1 task with this ID prefix will be executed at a time
|
29
|
+
*/
|
30
|
+
group?: string;
|
31
|
+
progress?: number;
|
32
|
+
|
33
|
+
[key: string]: any;
|
34
|
+
}
|
35
|
+
|
36
|
+
interface TaskData<T = void> {
|
37
|
+
id: string;
|
38
|
+
status: TaskStatus;
|
39
|
+
errorMessage?: string;
|
40
|
+
metadata: TaskMetadata;
|
41
|
+
future: Future<T>;
|
42
|
+
run: TaskRunner<T>;
|
43
|
+
}
|
44
|
+
|
45
|
+
export class Task<T = void> implements TaskData<T> {
|
46
|
+
private data: TaskData<T>;
|
47
|
+
|
48
|
+
constructor(task: TaskData<T>) {
|
49
|
+
this.data = task;
|
50
|
+
}
|
51
|
+
|
52
|
+
get id() {
|
53
|
+
return this.data.id;
|
54
|
+
}
|
55
|
+
|
56
|
+
get status() {
|
57
|
+
return this.data.status;
|
58
|
+
}
|
59
|
+
|
60
|
+
get errorMessage() {
|
61
|
+
return this.data.errorMessage;
|
62
|
+
}
|
63
|
+
|
64
|
+
get metadata() {
|
65
|
+
return this.data.metadata;
|
66
|
+
}
|
67
|
+
|
68
|
+
get future() {
|
69
|
+
return this.data.future;
|
70
|
+
}
|
71
|
+
|
72
|
+
get run() {
|
73
|
+
return this.data.run;
|
74
|
+
}
|
75
|
+
|
76
|
+
set status(status: TaskStatus) {
|
77
|
+
this.data.status = status;
|
78
|
+
}
|
79
|
+
|
80
|
+
set errorMessage(errorMessage: string | undefined) {
|
81
|
+
this.data.errorMessage = errorMessage;
|
82
|
+
}
|
83
|
+
|
84
|
+
set metadata(metadata: TaskMetadata) {
|
85
|
+
this.data.metadata = metadata;
|
86
|
+
}
|
87
|
+
|
88
|
+
public emitUpdate() {
|
89
|
+
socketManager.emitGlobal(EVENT_TASK_UPDATED, this.toData());
|
90
|
+
}
|
91
|
+
|
92
|
+
async wait(): Promise<T> {
|
93
|
+
return this.future.promise;
|
94
|
+
}
|
95
|
+
|
96
|
+
toData() {
|
97
|
+
return { ...this.data };
|
98
|
+
}
|
99
|
+
}
|
100
|
+
|
101
|
+
function createFuture<T>(): Future<T> {
|
102
|
+
let resolve: (arg: T) => void = () => {};
|
103
|
+
let reject: () => void = () => {};
|
104
|
+
const promise = new Promise<T>((res, rej) => {
|
105
|
+
resolve = res;
|
106
|
+
reject = rej;
|
107
|
+
});
|
108
|
+
|
109
|
+
return {
|
110
|
+
promise,
|
111
|
+
resolve,
|
112
|
+
reject,
|
113
|
+
};
|
114
|
+
}
|
115
|
+
|
116
|
+
class TaskManager {
|
117
|
+
private _tasks: Task<any>[] = [];
|
118
|
+
|
119
|
+
public add<T>(id: string, runner: TaskRunner<T>, metadata: TaskMetadata): Task<T> {
|
120
|
+
const existingTask = this.get(id);
|
121
|
+
if (existingTask) {
|
122
|
+
return existingTask;
|
123
|
+
}
|
124
|
+
|
125
|
+
const future = createFuture<T>();
|
126
|
+
|
127
|
+
const task = new Task<T>({
|
128
|
+
id,
|
129
|
+
status: TaskStatus.PENDING,
|
130
|
+
metadata,
|
131
|
+
future,
|
132
|
+
run: runner,
|
133
|
+
});
|
134
|
+
|
135
|
+
this._tasks.push(task);
|
136
|
+
|
137
|
+
socketManager.emitGlobal(EVENT_TASK_ADDED, task.toData());
|
138
|
+
|
139
|
+
this.invokeTask(task).catch(() => {});
|
140
|
+
|
141
|
+
return task;
|
142
|
+
}
|
143
|
+
|
144
|
+
async waitFor(filter: (task: Task<any>) => boolean) {
|
145
|
+
const tasks = this._tasks.filter(filter);
|
146
|
+
while (tasks.length > 0) {
|
147
|
+
const task = tasks.shift();
|
148
|
+
if (!task) {
|
149
|
+
continue;
|
150
|
+
}
|
151
|
+
try {
|
152
|
+
await task.wait();
|
153
|
+
} catch (e) {
|
154
|
+
//Ignore
|
155
|
+
}
|
156
|
+
}
|
157
|
+
}
|
158
|
+
|
159
|
+
public get(taskId: string) {
|
160
|
+
return this._tasks.find((t) => t.id === taskId);
|
161
|
+
}
|
162
|
+
|
163
|
+
public exists(taskId: string) {
|
164
|
+
return !!this.get(taskId);
|
165
|
+
}
|
166
|
+
|
167
|
+
public remove(taskId: string) {
|
168
|
+
const task = this.get(taskId);
|
169
|
+
if (!task) {
|
170
|
+
return;
|
171
|
+
}
|
172
|
+
|
173
|
+
if (task.status === TaskStatus.RUNNING) {
|
174
|
+
throw new Error('Cannot remove a running task');
|
175
|
+
}
|
176
|
+
|
177
|
+
this._tasks = this._tasks.filter((t) => t.id !== taskId);
|
178
|
+
socketManager.emitGlobal(EVENT_TASK_REMOVED, task.toData());
|
179
|
+
}
|
180
|
+
|
181
|
+
public list(): TaskData[] {
|
182
|
+
return this._tasks.map((t) => t.toData());
|
183
|
+
}
|
184
|
+
|
185
|
+
private async invokeTask(task: Task<any>): Promise<void> {
|
186
|
+
if (task.metadata.group) {
|
187
|
+
const existingTaskInGroup = this._tasks.find(
|
188
|
+
(t) => t.id !== task.id && t.metadata.group === task.metadata.group && t.status === TaskStatus.RUNNING
|
189
|
+
);
|
190
|
+
|
191
|
+
if (existingTaskInGroup) {
|
192
|
+
console.log('existingTaskInGroup', existingTaskInGroup.toData());
|
193
|
+
return;
|
194
|
+
}
|
195
|
+
}
|
196
|
+
|
197
|
+
try {
|
198
|
+
task.status = TaskStatus.RUNNING;
|
199
|
+
task.emitUpdate();
|
200
|
+
const result = await task.run(task);
|
201
|
+
task.status = TaskStatus.COMPLETED;
|
202
|
+
task.future.resolve(result);
|
203
|
+
task.emitUpdate();
|
204
|
+
} catch (e: any) {
|
205
|
+
task.errorMessage = e.message;
|
206
|
+
task.status = TaskStatus.FAILED;
|
207
|
+
task.future.reject(e);
|
208
|
+
task.emitUpdate();
|
209
|
+
} finally {
|
210
|
+
this.remove(task.id);
|
211
|
+
}
|
212
|
+
|
213
|
+
if (task.metadata.group) {
|
214
|
+
const nextTaskInGroup = this._tasks.find(
|
215
|
+
(t) => t.id !== task.id && t.metadata.group === task.metadata.group && t.status === TaskStatus.PENDING
|
216
|
+
);
|
217
|
+
if (nextTaskInGroup) {
|
218
|
+
console.log('nextTaskInGroup', nextTaskInGroup.toData());
|
219
|
+
return this.invokeTask(nextTaskInGroup);
|
220
|
+
}
|
221
|
+
}
|
222
|
+
}
|
223
|
+
}
|
224
|
+
|
225
|
+
export const taskManager = new TaskManager();
|
@@ -0,0 +1,38 @@
|
|
1
|
+
import Router from 'express-promise-router';
|
2
|
+
import { Request, Response } from 'express';
|
3
|
+
|
4
|
+
import { corsHandler } from '../middleware/cors';
|
5
|
+
import { taskManager } from '../taskManager';
|
6
|
+
|
7
|
+
const router = Router();
|
8
|
+
|
9
|
+
router.use('/', corsHandler);
|
10
|
+
|
11
|
+
/**
|
12
|
+
* Get all current tasks
|
13
|
+
*/
|
14
|
+
router.get('/', (req: Request, res: Response) => {
|
15
|
+
res.send(taskManager.list());
|
16
|
+
});
|
17
|
+
|
18
|
+
router.get('/:taskId', (req: Request, res: Response) => {
|
19
|
+
const task = taskManager.get(req.params.taskId);
|
20
|
+
if (!task) {
|
21
|
+
res.status(404).send({ error: 'Task not found' });
|
22
|
+
return;
|
23
|
+
}
|
24
|
+
|
25
|
+
res.send(task.toData());
|
26
|
+
});
|
27
|
+
|
28
|
+
router.delete('/:taskId', (req: Request, res: Response) => {
|
29
|
+
try {
|
30
|
+
taskManager.remove(req.params.taskId);
|
31
|
+
res.send({ ok: true });
|
32
|
+
} catch (e: any) {
|
33
|
+
res.status(400).send({ error: e.message });
|
34
|
+
return;
|
35
|
+
}
|
36
|
+
});
|
37
|
+
|
38
|
+
export default router;
|
@@ -5,12 +5,10 @@ import { KapetaURI, parseKapetaUri } from '@kapeta/nodejs-utils';
|
|
5
5
|
import { serviceManager } from '../serviceManager';
|
6
6
|
import { containerManager, DockerMounts, toLocalBindVolume } from '../containerManager';
|
7
7
|
import { LogData } from './LogData';
|
8
|
-
import EventEmitter from 'events';
|
9
8
|
import { clusterService } from '../clusterService';
|
10
9
|
import { AnyMap, BlockProcessParams, InstanceType, ProcessInfo, StringMap } from '../types';
|
11
10
|
import { Container } from 'node-docker-api/lib/container';
|
12
11
|
import { definitionsManager } from '../definitionsManager';
|
13
|
-
import md5 from 'md5';
|
14
12
|
|
15
13
|
const KIND_BLOCK_TYPE_OPERATOR = 'core/block-type-operator';
|
16
14
|
const KAPETA_SYSTEM_ID = 'KAPETA_SYSTEM_ID';
|
@@ -415,8 +413,6 @@ export class BlockInstanceRunner {
|
|
415
413
|
private async ensureContainer(opts: any) {
|
416
414
|
const container = await containerManager.ensureContainer(opts);
|
417
415
|
|
418
|
-
await containerManager.waitForReady(container);
|
419
|
-
|
420
416
|
return this._handleContainer(container);
|
421
417
|
}
|
422
418
|
|