@kapeta/local-cluster-service 0.36.1 → 0.38.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/.vscode/launch.json +9 -5
- package/CHANGELOG.md +21 -0
- package/dist/cjs/src/assetManager.d.ts +2 -1
- package/dist/cjs/src/assetManager.js +7 -4
- package/dist/cjs/src/containerManager.d.ts +6 -3
- package/dist/cjs/src/containerManager.js +96 -18
- package/dist/cjs/src/instanceManager.d.ts +16 -0
- package/dist/cjs/src/instanceManager.js +78 -14
- package/dist/cjs/src/operatorManager.d.ts +3 -4
- package/dist/cjs/src/operatorManager.js +3 -4
- package/dist/cjs/src/progressListener.d.ts +8 -1
- package/dist/cjs/src/progressListener.js +12 -1
- package/dist/cjs/src/repositoryManager.js +3 -2
- package/dist/cjs/src/taskManager.d.ts +2 -0
- package/dist/cjs/src/taskManager.js +9 -0
- package/dist/cjs/src/types.d.ts +4 -19
- package/dist/cjs/src/types.js +5 -1
- package/dist/cjs/src/utils/BlockInstanceRunner.js +16 -5
- package/dist/cjs/src/utils/commandLineUtils.d.ts +2 -1
- package/dist/cjs/src/utils/commandLineUtils.js +7 -1
- package/dist/cjs/src/utils/utils.d.ts +3 -3
- package/dist/cjs/src/utils/utils.js +4 -3
- package/dist/esm/src/assetManager.d.ts +2 -1
- package/dist/esm/src/assetManager.js +7 -4
- package/dist/esm/src/containerManager.d.ts +6 -3
- package/dist/esm/src/containerManager.js +96 -18
- package/dist/esm/src/instanceManager.d.ts +16 -0
- package/dist/esm/src/instanceManager.js +78 -14
- package/dist/esm/src/operatorManager.d.ts +3 -4
- package/dist/esm/src/operatorManager.js +3 -4
- package/dist/esm/src/progressListener.d.ts +8 -1
- package/dist/esm/src/progressListener.js +12 -1
- package/dist/esm/src/repositoryManager.js +3 -2
- package/dist/esm/src/taskManager.d.ts +2 -0
- package/dist/esm/src/taskManager.js +9 -0
- package/dist/esm/src/types.d.ts +4 -19
- package/dist/esm/src/types.js +5 -1
- package/dist/esm/src/utils/BlockInstanceRunner.js +16 -5
- package/dist/esm/src/utils/commandLineUtils.d.ts +2 -1
- package/dist/esm/src/utils/commandLineUtils.js +7 -1
- package/dist/esm/src/utils/utils.d.ts +3 -3
- package/dist/esm/src/utils/utils.js +4 -3
- package/package.json +17 -16
- package/src/assetManager.ts +9 -7
- package/src/containerManager.ts +110 -23
- package/src/instanceManager.ts +87 -16
- package/src/operatorManager.ts +11 -6
- package/src/progressListener.ts +15 -1
- package/src/repositoryManager.ts +5 -3
- package/src/taskManager.ts +11 -0
- package/src/types.ts +5 -19
- package/src/utils/BlockInstanceRunner.ts +21 -8
- package/src/utils/commandLineUtils.ts +10 -2
- package/src/utils/utils.ts +5 -6
package/src/instanceManager.ts
CHANGED
@@ -25,14 +25,16 @@ import {
|
|
25
25
|
InstanceOwner,
|
26
26
|
InstanceStatus,
|
27
27
|
InstanceType,
|
28
|
-
|
28
|
+
KIND_BLOCK_TYPE_EXECUTABLE,
|
29
|
+
KIND_BLOCK_TYPE_OPERATOR,
|
30
|
+
KIND_RESOURCE_OPERATOR,
|
29
31
|
LogEntry,
|
30
32
|
OperatorInstanceInfo,
|
31
33
|
OperatorInstancePort,
|
32
34
|
} from './types';
|
33
|
-
import { BlockDefinitionSpec,
|
35
|
+
import { BlockDefinitionSpec, LocalInstance, Plan } from '@kapeta/schemas';
|
34
36
|
import { getBlockInstanceContainerName, getResolvedConfiguration } from './utils/utils';
|
35
|
-
import {
|
37
|
+
import { operatorManager } from './operatorManager';
|
36
38
|
import { normalizeKapetaUri, parseKapetaUri } from '@kapeta/nodejs-utils';
|
37
39
|
import { definitionsManager } from './definitionsManager';
|
38
40
|
import { Task, taskManager } from './taskManager';
|
@@ -290,7 +292,9 @@ export class InstanceManager {
|
|
290
292
|
systemId = normalizeKapetaUri(systemId);
|
291
293
|
const instance = _.find(this._instances, { systemId, instanceId });
|
292
294
|
if (instance && instance.owner === InstanceOwner.EXTERNAL && instance.status !== InstanceStatus.STOPPED) {
|
293
|
-
instance.status
|
295
|
+
if (instance.status != InstanceStatus.FAILED) {
|
296
|
+
instance.status = InstanceStatus.STOPPED;
|
297
|
+
}
|
294
298
|
instance.pid = null;
|
295
299
|
instance.health = null;
|
296
300
|
socketManager.emitSystemEvent(systemId, EVENT_STATUS_CHANGED, instance);
|
@@ -313,12 +317,13 @@ export class InstanceManager {
|
|
313
317
|
return taskManager.add(
|
314
318
|
`plan:start:${systemId}`,
|
315
319
|
async () => {
|
316
|
-
|
317
|
-
|
318
|
-
|
320
|
+
const promises: Promise<InstanceInfo>[] = [];
|
321
|
+
const errors = [];
|
322
|
+
const instanceIds = await this.getAllInstancesExceptKind(systemId, KIND_BLOCK_TYPE_EXECUTABLE);
|
323
|
+
for (const instanceId of instanceIds) {
|
319
324
|
try {
|
320
325
|
promises.push(
|
321
|
-
this.start(systemId,
|
326
|
+
this.start(systemId, instanceId).then((taskOrInstance) => {
|
322
327
|
if (taskOrInstance instanceof Task) {
|
323
328
|
return taskOrInstance.wait();
|
324
329
|
}
|
@@ -381,7 +386,7 @@ export class InstanceManager {
|
|
381
386
|
throw new Error(`Operator block has no local definition: ${blockRef}`);
|
382
387
|
}
|
383
388
|
|
384
|
-
const localConfig = operatorDefinition.definition.spec.local as
|
389
|
+
const localConfig = operatorDefinition.definition.spec.local as LocalInstance;
|
385
390
|
|
386
391
|
let instance = await this.start(systemId, instanceId);
|
387
392
|
if (instance instanceof Task) {
|
@@ -662,8 +667,7 @@ export class InstanceManager {
|
|
662
667
|
|
663
668
|
const out = await this.saveInternalInstance({
|
664
669
|
...instance,
|
665
|
-
type: InstanceType.
|
666
|
-
pid: null,
|
670
|
+
type: InstanceType.DOCKER,
|
667
671
|
health: null,
|
668
672
|
portType: DEFAULT_HEALTH_PORT_TYPE,
|
669
673
|
status: InstanceStatus.FAILED,
|
@@ -807,7 +811,6 @@ export class InstanceManager {
|
|
807
811
|
if (instance.status !== newStatus) {
|
808
812
|
const oldStatus = instance.status;
|
809
813
|
const skipUpdate =
|
810
|
-
(newStatus === InstanceStatus.STOPPED && instance.status === InstanceStatus.FAILED) ||
|
811
814
|
([InstanceStatus.READY, InstanceStatus.UNHEALTHY].includes(newStatus) &&
|
812
815
|
instance.status === InstanceStatus.STOPPING) ||
|
813
816
|
(newStatus === InstanceStatus.STOPPED &&
|
@@ -831,7 +834,7 @@ export class InstanceManager {
|
|
831
834
|
|
832
835
|
if (
|
833
836
|
instance.desiredStatus === DesiredInstanceStatus.RUN &&
|
834
|
-
[InstanceStatus.STOPPED, InstanceStatus.
|
837
|
+
[InstanceStatus.STOPPED, InstanceStatus.STOPPING].includes(newStatus)
|
835
838
|
) {
|
836
839
|
//If the instance is stopped but we want it to run, start it
|
837
840
|
try {
|
@@ -924,11 +927,18 @@ export class InstanceManager {
|
|
924
927
|
}
|
925
928
|
|
926
929
|
if (statusType === 'created') {
|
930
|
+
if (state.ExitCode !== 0) {
|
931
|
+
// Failed during creation
|
932
|
+
return InstanceStatus.FAILED;
|
933
|
+
}
|
927
934
|
return InstanceStatus.STARTING;
|
928
935
|
}
|
929
936
|
|
930
937
|
if (statusType === 'exited' || statusType === 'dead') {
|
931
|
-
|
938
|
+
if (state.ExitCode === 0) {
|
939
|
+
return InstanceStatus.STOPPED;
|
940
|
+
}
|
941
|
+
return InstanceStatus.FAILED;
|
932
942
|
}
|
933
943
|
|
934
944
|
if (statusType === 'removing') {
|
@@ -1008,8 +1018,8 @@ export class InstanceManager {
|
|
1008
1018
|
return false;
|
1009
1019
|
}
|
1010
1020
|
|
1011
|
-
if (parseKapetaUri(provider.kind).fullName ===
|
1012
|
-
const localConfig = provider.data.spec.local as
|
1021
|
+
if (parseKapetaUri(provider.kind).fullName === KIND_BLOCK_TYPE_OPERATOR) {
|
1022
|
+
const localConfig = provider.data.spec.local as LocalInstance;
|
1013
1023
|
return localConfig.singleton ?? false;
|
1014
1024
|
}
|
1015
1025
|
|
@@ -1025,6 +1035,37 @@ export class InstanceManager {
|
|
1025
1035
|
return block.data.kind;
|
1026
1036
|
}
|
1027
1037
|
|
1038
|
+
/**
|
1039
|
+
* Get the kind of an asset. Use the maxDepth parameter to specify how deep to look for the
|
1040
|
+
* kind. For example, if maxDepth is 2, the method will look for the kind of the asset and then
|
1041
|
+
* the kind of the kind.
|
1042
|
+
* @param assetRef The asset reference
|
1043
|
+
* @param maxDepth The maximum depth to look for the kind
|
1044
|
+
* @returns The kind of the asset or null if not found
|
1045
|
+
*/
|
1046
|
+
private async getDeepKindForAssetRef(assetRef: string, maxDepth: number): Promise<string | null> {
|
1047
|
+
if (maxDepth <= 0) {
|
1048
|
+
return null;
|
1049
|
+
}
|
1050
|
+
|
1051
|
+
try {
|
1052
|
+
const asset = await assetManager.getAsset(assetRef);
|
1053
|
+
if (!asset || !asset.data.kind) {
|
1054
|
+
return null;
|
1055
|
+
}
|
1056
|
+
|
1057
|
+
if (maxDepth === 1) {
|
1058
|
+
return asset.data.kind;
|
1059
|
+
} else {
|
1060
|
+
// Recurse with the kind of the current block and one less depth
|
1061
|
+
return await this.getDeepKindForAssetRef(asset.data.kind, maxDepth - 1);
|
1062
|
+
}
|
1063
|
+
} catch (error) {
|
1064
|
+
console.error('Error fetching kind for assetRef:', assetRef, error);
|
1065
|
+
return null;
|
1066
|
+
}
|
1067
|
+
}
|
1068
|
+
|
1028
1069
|
private async isUsingKind(ref: string, kind: string): Promise<boolean> {
|
1029
1070
|
const assetKind = await this.getKindForAssetRef(ref);
|
1030
1071
|
if (!assetKind) {
|
@@ -1048,6 +1089,36 @@ export class InstanceManager {
|
|
1048
1089
|
|
1049
1090
|
return out;
|
1050
1091
|
}
|
1092
|
+
|
1093
|
+
/**
|
1094
|
+
* Get the ids for all block instances except the ones of the specified kind
|
1095
|
+
* @param systemId The plan reference id
|
1096
|
+
* @param kind The kind to exclude. Can be a string or an array of strings
|
1097
|
+
* @returns An array of block instance ids
|
1098
|
+
*/
|
1099
|
+
private async getAllInstancesExceptKind(systemId: string, kind: string | string[]): Promise<string[]> {
|
1100
|
+
const plan = await assetManager.getPlan(systemId);
|
1101
|
+
if (!plan?.spec?.blocks) {
|
1102
|
+
return [];
|
1103
|
+
}
|
1104
|
+
const out: string[] = [];
|
1105
|
+
const excludedKinds = kind instanceof Array ? kind : [kind];
|
1106
|
+
for (const block of plan.spec.blocks) {
|
1107
|
+
const blockKindOfKind = await this.getDeepKindForAssetRef(block.block.ref, 2);
|
1108
|
+
if (!blockKindOfKind) {
|
1109
|
+
continue;
|
1110
|
+
}
|
1111
|
+
|
1112
|
+
const shouldIncludeBlock =
|
1113
|
+
excludedKinds.some((excludedKind) => excludedKind === parseKapetaUri(blockKindOfKind).fullName) ===
|
1114
|
+
false;
|
1115
|
+
if (shouldIncludeBlock) {
|
1116
|
+
out.push(block.id);
|
1117
|
+
}
|
1118
|
+
}
|
1119
|
+
|
1120
|
+
return out;
|
1121
|
+
}
|
1051
1122
|
}
|
1052
1123
|
|
1053
1124
|
export const instanceManager = new InstanceManager();
|
package/src/operatorManager.ts
CHANGED
@@ -16,8 +16,15 @@ import {
|
|
16
16
|
containerManager,
|
17
17
|
} from './containerManager';
|
18
18
|
import FSExtra from 'fs-extra';
|
19
|
-
import {
|
20
|
-
|
19
|
+
import {
|
20
|
+
AnyMap,
|
21
|
+
EnvironmentType,
|
22
|
+
KIND_BLOCK_TYPE_OPERATOR,
|
23
|
+
KIND_RESOURCE_OPERATOR,
|
24
|
+
OperatorInfo,
|
25
|
+
StringMap,
|
26
|
+
} from './types';
|
27
|
+
import { BlockInstance, LocalInstance, Resource } from '@kapeta/schemas';
|
21
28
|
import { definitionsManager } from './definitionsManager';
|
22
29
|
import { getBindHost, toPortInfo } from './utils/utils';
|
23
30
|
import { parseKapetaUri, normalizeKapetaUri } from '@kapeta/nodejs-utils';
|
@@ -25,8 +32,6 @@ import _ from 'lodash';
|
|
25
32
|
import AsyncLock from 'async-lock';
|
26
33
|
import { taskManager } from './taskManager';
|
27
34
|
|
28
|
-
export const KIND_RESOURCE_OPERATOR = 'core/resource-type-operator';
|
29
|
-
export const KIND_BLOCK_OPERATOR = 'core/block-type-operator';
|
30
35
|
const KIND_PLAN = 'core/plan';
|
31
36
|
|
32
37
|
class Operator {
|
@@ -36,7 +41,7 @@ class Operator {
|
|
36
41
|
this._data = data;
|
37
42
|
}
|
38
43
|
|
39
|
-
getLocalData():
|
44
|
+
getLocalData(): LocalInstance {
|
40
45
|
return this._data.definition.spec.local;
|
41
46
|
}
|
42
47
|
|
@@ -68,7 +73,7 @@ class OperatorManager {
|
|
68
73
|
* Get operator definition for resource type
|
69
74
|
*/
|
70
75
|
async getOperator(fullName: string, version: string) {
|
71
|
-
const operators = await definitionsManager.getDefinitions([KIND_RESOURCE_OPERATOR,
|
76
|
+
const operators = await definitionsManager.getDefinitions([KIND_RESOURCE_OPERATOR, KIND_BLOCK_TYPE_OPERATOR]);
|
72
77
|
|
73
78
|
const operator: DefinitionInfo | undefined = operators.find(
|
74
79
|
(operator) =>
|
package/src/progressListener.ts
CHANGED
@@ -7,6 +7,7 @@ import { spawn } from '@kapeta/nodejs-process';
|
|
7
7
|
import { socketManager } from './socketManager';
|
8
8
|
import { LogEntry } from './types';
|
9
9
|
import { format } from 'node:util';
|
10
|
+
import { Task } from './taskManager';
|
10
11
|
|
11
12
|
export class ProgressListener {
|
12
13
|
private readonly systemId: string | undefined;
|
@@ -17,7 +18,7 @@ export class ProgressListener {
|
|
17
18
|
this.instanceId = instanceId;
|
18
19
|
}
|
19
20
|
|
20
|
-
|
21
|
+
protected emitLog(payload: Omit<LogEntry, 'time' | 'source'>) {
|
21
22
|
const logEntry: LogEntry = {
|
22
23
|
...payload,
|
23
24
|
source: 'stdout',
|
@@ -135,3 +136,16 @@ export class ProgressListener {
|
|
135
136
|
});
|
136
137
|
}
|
137
138
|
}
|
139
|
+
|
140
|
+
export class TaskProgressListener extends ProgressListener {
|
141
|
+
private readonly task: Task;
|
142
|
+
|
143
|
+
constructor(task: Task) {
|
144
|
+
super();
|
145
|
+
this.task = task;
|
146
|
+
}
|
147
|
+
|
148
|
+
protected emitLog(payload: Omit<LogEntry, 'time' | 'source'>) {
|
149
|
+
this.task.addLog(payload.message, payload.level);
|
150
|
+
}
|
151
|
+
}
|
package/src/repositoryManager.ts
CHANGED
@@ -10,7 +10,7 @@ import { Actions, AssetVersion, Config, RegistryService } from '@kapeta/nodejs-r
|
|
10
10
|
import { definitionsManager } from './definitionsManager';
|
11
11
|
import { Task, taskManager } from './taskManager';
|
12
12
|
import { normalizeKapetaUri, parseKapetaUri, parseVersion } from '@kapeta/nodejs-utils';
|
13
|
-
import {
|
13
|
+
import { TaskProgressListener } from './progressListener';
|
14
14
|
import { RepositoryWatcher } from './RepositoryWatcher';
|
15
15
|
import { SourceOfChange } from './types';
|
16
16
|
import { cacheManager } from './cacheManager';
|
@@ -191,16 +191,18 @@ class RepositoryManager extends EventEmitter {
|
|
191
191
|
private async scheduleInstallation(refs: string[]): Promise<Task[]> {
|
192
192
|
//We make sure to only install one asset at a time - otherwise unexpected things might happen
|
193
193
|
const createInstaller = (ref: string) => {
|
194
|
-
return async () => {
|
194
|
+
return async (task: Task) => {
|
195
195
|
if (await definitionsManager.exists(ref)) {
|
196
196
|
return;
|
197
197
|
}
|
198
|
+
|
199
|
+
const progressListener = new TaskProgressListener(task);
|
198
200
|
//console.log(`Installing asset: ${ref}`);
|
199
201
|
//Auto-install missing asset
|
200
202
|
try {
|
201
203
|
//We change to a temp dir to avoid issues with the current working directory
|
202
204
|
process.chdir(os.tmpdir());
|
203
|
-
await Actions.install(
|
205
|
+
await Actions.install(progressListener, [ref], {});
|
204
206
|
} catch (e) {
|
205
207
|
console.error(`Failed to install asset: ${ref}`, e);
|
206
208
|
throw e;
|
package/src/taskManager.ts
CHANGED
@@ -7,10 +7,12 @@
|
|
7
7
|
* Class that handles processing background tasks.
|
8
8
|
*/
|
9
9
|
import { socketManager } from './socketManager';
|
10
|
+
import { LogLevel } from './types';
|
10
11
|
|
11
12
|
const EVENT_TASK_UPDATED = 'task-updated';
|
12
13
|
const EVENT_TASK_ADDED = 'task-added';
|
13
14
|
const EVENT_TASK_REMOVED = 'task-removed';
|
15
|
+
const EVENT_TASK_LOG = 'task-log';
|
14
16
|
|
15
17
|
export type TaskRunner<T> = (task: Task<T>) => Promise<T>;
|
16
18
|
|
@@ -94,6 +96,15 @@ export class Task<T = void> implements TaskData<T> {
|
|
94
96
|
socketManager.emitGlobal(EVENT_TASK_UPDATED, this.toData());
|
95
97
|
}
|
96
98
|
|
99
|
+
public addLog(log: string, level: LogLevel = 'INFO') {
|
100
|
+
socketManager.emitGlobal(EVENT_TASK_LOG, {
|
101
|
+
id: this.id,
|
102
|
+
message: log,
|
103
|
+
level,
|
104
|
+
time: Date.now(),
|
105
|
+
});
|
106
|
+
}
|
107
|
+
|
97
108
|
async wait(): Promise<T> {
|
98
109
|
return this.future.promise;
|
99
110
|
}
|
package/src/types.ts
CHANGED
@@ -8,6 +8,11 @@ import { Connection, Resource } from '@kapeta/schemas';
|
|
8
8
|
import { StringBodyRequest } from './middleware/stringBody';
|
9
9
|
import { KapetaRequest } from './middleware/kapeta';
|
10
10
|
|
11
|
+
export const KIND_RESOURCE_OPERATOR = 'core/resource-type-operator';
|
12
|
+
export const KIND_BLOCK_TYPE = 'core/block-type';
|
13
|
+
export const KIND_BLOCK_TYPE_OPERATOR = 'core/block-type-operator';
|
14
|
+
export const KIND_BLOCK_TYPE_EXECUTABLE = 'core/block-type-executable';
|
15
|
+
|
11
16
|
export type StringMap = { [key: string]: string };
|
12
17
|
export type AnyMap = { [key: string]: any };
|
13
18
|
export type SourceOfChange = 'user' | 'filesystem';
|
@@ -68,20 +73,6 @@ export interface Health {
|
|
68
73
|
retries?: number;
|
69
74
|
}
|
70
75
|
|
71
|
-
export type PortInfo = { port: number; type: 'tcp' | 'udp' } | number | string;
|
72
|
-
|
73
|
-
export type LocalImageOptions<Credentials = AnyMap, Options = AnyMap> = {
|
74
|
-
image: string;
|
75
|
-
ports: { [key: string]: PortInfo };
|
76
|
-
credentials?: Credentials;
|
77
|
-
options?: Options;
|
78
|
-
cmd?: string;
|
79
|
-
env?: AnyMap;
|
80
|
-
health?: Health;
|
81
|
-
singleton?: boolean;
|
82
|
-
mounts?: { [key: string]: string };
|
83
|
-
};
|
84
|
-
|
85
76
|
export type InstanceInfo = {
|
86
77
|
systemId: string;
|
87
78
|
instanceId: string;
|
@@ -100,11 +91,6 @@ export type InstanceInfo = {
|
|
100
91
|
portType?: string;
|
101
92
|
};
|
102
93
|
|
103
|
-
interface ResourceRef {
|
104
|
-
blockId: string;
|
105
|
-
resourceName: string;
|
106
|
-
}
|
107
|
-
|
108
94
|
export type ProxyRequestHandler = (req: StringBodyRequest, res: express.Response, info: ProxyRequestInfo) => void;
|
109
95
|
|
110
96
|
export interface OperatorInstancePort {
|
@@ -18,13 +18,14 @@ import {
|
|
18
18
|
} from '../containerManager';
|
19
19
|
import { LogData } from './LogData';
|
20
20
|
import { clusterService } from '../clusterService';
|
21
|
-
import { AnyMap, BlockProcessParams, InstanceType,
|
21
|
+
import { AnyMap, BlockProcessParams, InstanceType, KIND_BLOCK_TYPE_OPERATOR, ProcessInfo, StringMap } from '../types';
|
22
22
|
import { definitionsManager } from '../definitionsManager';
|
23
23
|
import Docker from 'dockerode';
|
24
24
|
import OS from 'node:os';
|
25
|
+
import Path from 'node:path';
|
25
26
|
import { taskManager } from '../taskManager';
|
27
|
+
import { LocalDevContainer, LocalInstance } from '@kapeta/schemas';
|
26
28
|
|
27
|
-
const KIND_BLOCK_TYPE_OPERATOR = 'core/block-type-operator';
|
28
29
|
const KAPETA_SYSTEM_ID = 'KAPETA_SYSTEM_ID';
|
29
30
|
const KAPETA_BLOCK_REF = 'KAPETA_BLOCK_REF';
|
30
31
|
const KAPETA_INSTANCE_ID = 'KAPETA_INSTANCE_ID';
|
@@ -182,6 +183,8 @@ export class BlockInstanceRunner {
|
|
182
183
|
throw new Error('Missing target kind in block definition');
|
183
184
|
}
|
184
185
|
|
186
|
+
const realLocalPath = await FSExtra.realpath(baseDir);
|
187
|
+
|
185
188
|
const kindUri = parseKapetaUri(assetVersion.definition.kind);
|
186
189
|
|
187
190
|
const providerVersion = await getProvider(kindUri);
|
@@ -198,17 +201,29 @@ export class BlockInstanceRunner {
|
|
198
201
|
throw new Error(`Target not found: ${targetKindUri.id}`);
|
199
202
|
}
|
200
203
|
|
201
|
-
const localContainer = targetVersion.definition.spec.local;
|
204
|
+
const localContainer = targetVersion.definition.spec.local as LocalDevContainer;
|
202
205
|
|
203
206
|
if (!localContainer) {
|
204
207
|
throw new Error(`Missing local container information from target: ${targetKindUri.id}`);
|
205
208
|
}
|
206
209
|
|
207
|
-
|
208
|
-
|
210
|
+
let dockerImage = localContainer.image;
|
211
|
+
const isDockerImage = !localContainer.type || localContainer.type.toLowerCase() === 'docker';
|
212
|
+
const isDockerFile = Boolean(localContainer.type && localContainer.type.toLowerCase() === 'dockerfile');
|
213
|
+
if (isDockerImage && !dockerImage) {
|
209
214
|
throw new Error(`Missing docker image information: ${JSON.stringify(localContainer)}`);
|
210
215
|
}
|
211
216
|
|
217
|
+
if (isDockerFile) {
|
218
|
+
dockerImage = blockInfo.fullName + ':local';
|
219
|
+
const dockerFile = Path.join(realLocalPath, localContainer.file ?? 'Dockerfile');
|
220
|
+
if (!FSExtra.existsSync(dockerFile)) {
|
221
|
+
throw new Error(`Dockerfile not found at: ${dockerFile}`);
|
222
|
+
}
|
223
|
+
const task = containerManager.buildDockerImage(dockerFile, blockInfo.fullName + ':local');
|
224
|
+
await task.wait();
|
225
|
+
}
|
226
|
+
|
212
227
|
const containerName = await getBlockInstanceContainerName(this._systemId, blockInstance.id, targetKindUri.id);
|
213
228
|
const startCmd = localContainer.handlers?.onCreate ? localContainer.handlers.onCreate : '';
|
214
229
|
const dockerOpts = localContainer.options ?? {};
|
@@ -235,8 +250,6 @@ export class BlockInstanceRunner {
|
|
235
250
|
HealthCheck = containerManager.toDockerHealth({ cmd: localContainer.healthcheck });
|
236
251
|
}
|
237
252
|
|
238
|
-
const realLocalPath = await FSExtra.realpath(baseDir);
|
239
|
-
|
240
253
|
const Mounts = containerManager.toDockerMounts({
|
241
254
|
[workingDir]: toLocalBindVolume(realLocalPath),
|
242
255
|
});
|
@@ -384,7 +397,7 @@ export class BlockInstanceRunner {
|
|
384
397
|
throw new Error(`Provider did not have local image: ${providerRef}`);
|
385
398
|
}
|
386
399
|
|
387
|
-
const local = spec.local as
|
400
|
+
const local = spec.local as LocalInstance;
|
388
401
|
|
389
402
|
const dockerImage = local.image;
|
390
403
|
const operatorUri = local.singleton ? parseKapetaUri(providerRef) : blockUri;
|
@@ -4,7 +4,7 @@
|
|
4
4
|
*/
|
5
5
|
|
6
6
|
import { spawn, hasApp } from '@kapeta/nodejs-process';
|
7
|
-
import { taskManager } from '../taskManager';
|
7
|
+
import { Task, taskManager } from '../taskManager';
|
8
8
|
|
9
9
|
export async function hasCLI() {
|
10
10
|
return hasApp('kap');
|
@@ -17,11 +17,19 @@ export async function ensureCLI() {
|
|
17
17
|
|
18
18
|
return taskManager.add(
|
19
19
|
`cli:install`,
|
20
|
-
() => {
|
20
|
+
(task: Task) => {
|
21
21
|
const process = spawn('npm', ['install', '-g', '@kapeta/kap'], {
|
22
22
|
shell: true,
|
23
23
|
});
|
24
24
|
|
25
|
+
process.process.stdout?.on('data', (data: any) => {
|
26
|
+
task.addLog(data.toString(), 'INFO');
|
27
|
+
});
|
28
|
+
|
29
|
+
process.process.stderr?.on('data', (data: any) => {
|
30
|
+
task.addLog(data.toString(), 'ERROR');
|
31
|
+
});
|
32
|
+
|
25
33
|
return process.wait();
|
26
34
|
},
|
27
35
|
{
|
package/src/utils/utils.ts
CHANGED
@@ -6,13 +6,12 @@
|
|
6
6
|
import FS from 'node:fs';
|
7
7
|
import YAML from 'yaml';
|
8
8
|
import md5 from 'md5';
|
9
|
-
import { EntityList } from '@kapeta/schemas';
|
9
|
+
import { EntityList, LocalInstancePort, LocalInstancePortType } from '@kapeta/schemas';
|
10
10
|
import _ from 'lodash';
|
11
|
-
import { AnyMap,
|
11
|
+
import { AnyMap, KIND_BLOCK_TYPE_OPERATOR } from '../types';
|
12
12
|
import ClusterConfiguration from '@kapeta/local-cluster-config';
|
13
13
|
import { definitionsManager } from '../definitionsManager';
|
14
14
|
import { normalizeKapetaUri, parseKapetaUri } from '@kapeta/nodejs-utils';
|
15
|
-
import { KIND_BLOCK_OPERATOR } from '../operatorManager';
|
16
15
|
import { assetManager } from '../assetManager';
|
17
16
|
|
18
17
|
export async function getBlockInstanceContainerName(systemId: string, instanceId: string, blockType?: string) {
|
@@ -32,7 +31,7 @@ export async function getBlockInstanceContainerName(systemId: string, instanceId
|
|
32
31
|
throw new Error(`Block type ${blockType} not found`);
|
33
32
|
}
|
34
33
|
if (
|
35
|
-
parseKapetaUri(typeDefinition.definition.kind).fullName ===
|
34
|
+
parseKapetaUri(typeDefinition.definition.kind).fullName === KIND_BLOCK_TYPE_OPERATOR &&
|
36
35
|
typeDefinition.definition.spec?.local?.singleton
|
37
36
|
) {
|
38
37
|
return `kapeta-instance-operator-${md5(normalizeKapetaUri(systemId) + normalizeKapetaUri(blockType))}`;
|
@@ -41,13 +40,13 @@ export async function getBlockInstanceContainerName(systemId: string, instanceId
|
|
41
40
|
return `kapeta-block-instance-${md5(normalizeKapetaUri(systemId) + instanceId)}`;
|
42
41
|
}
|
43
42
|
|
44
|
-
export function toPortInfo(port:
|
43
|
+
export function toPortInfo(port: LocalInstancePort) {
|
45
44
|
if (typeof port === 'number' || typeof port === 'string') {
|
46
45
|
return { port: parseInt(`${port}`), type: 'tcp' };
|
47
46
|
}
|
48
47
|
|
49
48
|
if (!port.type) {
|
50
|
-
port.type =
|
49
|
+
port.type = LocalInstancePortType.TCP;
|
51
50
|
}
|
52
51
|
|
53
52
|
return port;
|