@kapeta/local-cluster-service 0.37.0 → 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/CHANGELOG.md +7 -0
- package/dist/cjs/src/assetManager.d.ts +2 -1
- package/dist/cjs/src/assetManager.js +3 -2
- package/dist/cjs/src/containerManager.d.ts +6 -3
- package/dist/cjs/src/containerManager.js +96 -18
- package/dist/cjs/src/instanceManager.js +15 -8
- package/dist/cjs/src/operatorManager.d.ts +3 -2
- 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 +0 -19
- package/dist/cjs/src/utils/BlockInstanceRunner.js +15 -3
- 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 +2 -1
- package/dist/esm/src/assetManager.d.ts +2 -1
- package/dist/esm/src/assetManager.js +3 -2
- package/dist/esm/src/containerManager.d.ts +6 -3
- package/dist/esm/src/containerManager.js +96 -18
- package/dist/esm/src/instanceManager.js +15 -8
- package/dist/esm/src/operatorManager.d.ts +3 -2
- 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 +0 -19
- package/dist/esm/src/utils/BlockInstanceRunner.js +15 -3
- 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 +2 -1
- package/package.json +14 -13
- package/src/assetManager.ts +5 -4
- package/src/containerManager.ts +110 -23
- package/src/instanceManager.ts +16 -10
- package/src/operatorManager.ts +2 -3
- package/src/progressListener.ts +15 -1
- package/src/repositoryManager.ts +5 -3
- package/src/taskManager.ts +11 -0
- package/src/types.ts +0 -19
- package/src/utils/BlockInstanceRunner.ts +21 -15
- package/src/utils/commandLineUtils.ts +10 -2
- package/src/utils/utils.ts +4 -4
package/src/containerManager.ts
CHANGED
@@ -14,12 +14,12 @@ import ClusterConfiguration from '@kapeta/local-cluster-config';
|
|
14
14
|
import uuid from 'node-uuid';
|
15
15
|
import md5 from 'md5';
|
16
16
|
import { getBlockInstanceContainerName } from './utils/utils';
|
17
|
-
import {
|
17
|
+
import { InstanceInfo, LogEntry, LogSource } from './types';
|
18
18
|
import { KapetaAPI } from '@kapeta/nodejs-api-client';
|
19
19
|
import { taskManager, Task } from './taskManager';
|
20
20
|
import { EventEmitter } from 'node:events';
|
21
21
|
import StreamValues from 'stream-json/streamers/StreamValues';
|
22
|
-
import {
|
22
|
+
import { LocalInstanceHealth } from '@kapeta/schemas';
|
23
23
|
|
24
24
|
type StringMap = { [key: string]: string };
|
25
25
|
|
@@ -100,7 +100,7 @@ enum DockerPullEventTypes {
|
|
100
100
|
|
101
101
|
type DockerPullEventType = DockerPullEventTypes | string;
|
102
102
|
|
103
|
-
const processJsonStream = <T>(purpose: string, stream:
|
103
|
+
const processJsonStream = <T>(purpose: string, stream: NodeJS.ReadableStream, handler: (d: JSONMessage<T>) => void) =>
|
104
104
|
new Promise<void>((resolve, reject) => {
|
105
105
|
const jsonStream = StreamValues.withParser();
|
106
106
|
jsonStream.on('data', (data: any) => {
|
@@ -296,15 +296,8 @@ class ContainerManager {
|
|
296
296
|
}
|
297
297
|
|
298
298
|
async getContainerByName(containerName: string): Promise<ContainerInfo | undefined> {
|
299
|
-
|
300
|
-
|
301
|
-
return container.Names.indexOf(`/${containerName}`) > -1;
|
302
|
-
});
|
303
|
-
|
304
|
-
if (out) {
|
305
|
-
return this.get(out.Id);
|
306
|
-
}
|
307
|
-
return undefined;
|
299
|
+
// The container can be fetched by name or by id using the same API call
|
300
|
+
return this.get(containerName);
|
308
301
|
}
|
309
302
|
|
310
303
|
async pull(image: string) {
|
@@ -398,6 +391,11 @@ class ContainerManager {
|
|
398
391
|
|
399
392
|
const chunk = chunks[data.id];
|
400
393
|
|
394
|
+
if (data.stream) {
|
395
|
+
// Emit raw output to the task log
|
396
|
+
task.addLog(data.stream);
|
397
|
+
}
|
398
|
+
|
401
399
|
switch (data.status) {
|
402
400
|
case DockerPullEventTypes.PreparingPhase:
|
403
401
|
case DockerPullEventTypes.WaitingPhase:
|
@@ -529,7 +527,7 @@ class ContainerManager {
|
|
529
527
|
return Mounts;
|
530
528
|
}
|
531
529
|
|
532
|
-
toDockerHealth(health:
|
530
|
+
toDockerHealth(health: LocalInstanceHealth) {
|
533
531
|
return {
|
534
532
|
Test: ['CMD-SHELL', health.cmd],
|
535
533
|
Interval: health.interval ? health.interval * NANO_SECOND : 5000 * NANO_SECOND,
|
@@ -679,7 +677,7 @@ class ContainerManager {
|
|
679
677
|
let dockerContainer = null;
|
680
678
|
|
681
679
|
try {
|
682
|
-
dockerContainer =
|
680
|
+
dockerContainer = this.docker().getContainer(name);
|
683
681
|
await dockerContainer.stats();
|
684
682
|
} catch (err) {
|
685
683
|
//Ignore
|
@@ -707,7 +705,7 @@ class ContainerManager {
|
|
707
705
|
];
|
708
706
|
}
|
709
707
|
|
710
|
-
return containerInfo.getLogs();
|
708
|
+
return await containerInfo.getLogs();
|
711
709
|
}
|
712
710
|
|
713
711
|
async stopLogListening(systemId: string, instanceId: string) {
|
@@ -782,6 +780,34 @@ class ContainerManager {
|
|
782
780
|
// Ignore
|
783
781
|
}
|
784
782
|
}
|
783
|
+
|
784
|
+
buildDockerImage(dockerFile: string, imageName: string) {
|
785
|
+
const taskName = `Building docker image: ${imageName}`;
|
786
|
+
const processor = async (task: Task) => {
|
787
|
+
const timeStarted = Date.now();
|
788
|
+
const stream = await this.docker().buildImage(
|
789
|
+
{
|
790
|
+
context: Path.dirname(dockerFile),
|
791
|
+
src: [Path.basename(dockerFile)],
|
792
|
+
},
|
793
|
+
{
|
794
|
+
t: imageName,
|
795
|
+
dockerfile: Path.basename(dockerFile),
|
796
|
+
}
|
797
|
+
);
|
798
|
+
|
799
|
+
await processJsonStream<string>(`image:build:${imageName}`, stream, (data) => {
|
800
|
+
if (data.stream) {
|
801
|
+
// Emit raw output to the task log
|
802
|
+
task.addLog(data.stream);
|
803
|
+
}
|
804
|
+
});
|
805
|
+
};
|
806
|
+
|
807
|
+
return taskManager.add(`docker:image:build:${imageName}`, processor, {
|
808
|
+
name: taskName,
|
809
|
+
});
|
810
|
+
}
|
785
811
|
}
|
786
812
|
|
787
813
|
function readLogBuffer(logBuffer: Buffer) {
|
@@ -1038,17 +1064,78 @@ export class ContainerInfo {
|
|
1038
1064
|
});
|
1039
1065
|
|
1040
1066
|
const out = readLogBuffer(logs);
|
1067
|
+
if (out.length > 0) {
|
1068
|
+
return out;
|
1069
|
+
}
|
1041
1070
|
|
1042
|
-
|
1043
|
-
|
1044
|
-
|
1045
|
-
|
1046
|
-
|
1047
|
-
|
1048
|
-
|
1071
|
+
const status = await this.status();
|
1072
|
+
const healthLogs: LogEntry[] = status?.Health?.Log
|
1073
|
+
? status?.Health?.Log.map((log) => {
|
1074
|
+
return {
|
1075
|
+
source: 'stdout',
|
1076
|
+
level: log.ExitCode === 0 ? 'INFO' : 'ERROR',
|
1077
|
+
time: Date.now(),
|
1078
|
+
message: 'Health check: ' + log.Output,
|
1079
|
+
};
|
1080
|
+
})
|
1081
|
+
: [];
|
1082
|
+
|
1083
|
+
if (status?.Running) {
|
1084
|
+
return [
|
1085
|
+
{
|
1086
|
+
source: 'stdout',
|
1087
|
+
level: 'INFO',
|
1088
|
+
time: Date.now(),
|
1089
|
+
message: 'Container is starting...',
|
1090
|
+
},
|
1091
|
+
...healthLogs,
|
1092
|
+
];
|
1049
1093
|
}
|
1050
1094
|
|
1051
|
-
|
1095
|
+
if (status?.Restarting) {
|
1096
|
+
return [
|
1097
|
+
{
|
1098
|
+
source: 'stdout',
|
1099
|
+
level: 'INFO',
|
1100
|
+
time: Date.now(),
|
1101
|
+
message: 'Container is restarting...',
|
1102
|
+
},
|
1103
|
+
...healthLogs,
|
1104
|
+
];
|
1105
|
+
}
|
1106
|
+
if (status?.Paused) {
|
1107
|
+
return [
|
1108
|
+
{
|
1109
|
+
source: 'stdout',
|
1110
|
+
level: 'INFO',
|
1111
|
+
time: Date.now(),
|
1112
|
+
message: 'Container is paused...',
|
1113
|
+
},
|
1114
|
+
...healthLogs,
|
1115
|
+
];
|
1116
|
+
}
|
1117
|
+
|
1118
|
+
if (status?.Error) {
|
1119
|
+
return [
|
1120
|
+
{
|
1121
|
+
source: 'stderr',
|
1122
|
+
level: 'ERROR',
|
1123
|
+
time: Date.now(),
|
1124
|
+
message: 'Container failed to start:\n' + status.Error,
|
1125
|
+
},
|
1126
|
+
...healthLogs,
|
1127
|
+
];
|
1128
|
+
}
|
1129
|
+
|
1130
|
+
return [
|
1131
|
+
{
|
1132
|
+
source: 'stdout',
|
1133
|
+
level: 'INFO',
|
1134
|
+
time: Date.now(),
|
1135
|
+
message: 'Container not running',
|
1136
|
+
...healthLogs,
|
1137
|
+
},
|
1138
|
+
];
|
1052
1139
|
}
|
1053
1140
|
}
|
1054
1141
|
|
package/src/instanceManager.ts
CHANGED
@@ -28,12 +28,11 @@ import {
|
|
28
28
|
KIND_BLOCK_TYPE_EXECUTABLE,
|
29
29
|
KIND_BLOCK_TYPE_OPERATOR,
|
30
30
|
KIND_RESOURCE_OPERATOR,
|
31
|
-
LocalImageOptions,
|
32
31
|
LogEntry,
|
33
32
|
OperatorInstanceInfo,
|
34
33
|
OperatorInstancePort,
|
35
34
|
} from './types';
|
36
|
-
import { BlockDefinitionSpec,
|
35
|
+
import { BlockDefinitionSpec, LocalInstance, Plan } from '@kapeta/schemas';
|
37
36
|
import { getBlockInstanceContainerName, getResolvedConfiguration } from './utils/utils';
|
38
37
|
import { operatorManager } from './operatorManager';
|
39
38
|
import { normalizeKapetaUri, parseKapetaUri } from '@kapeta/nodejs-utils';
|
@@ -293,7 +292,9 @@ export class InstanceManager {
|
|
293
292
|
systemId = normalizeKapetaUri(systemId);
|
294
293
|
const instance = _.find(this._instances, { systemId, instanceId });
|
295
294
|
if (instance && instance.owner === InstanceOwner.EXTERNAL && instance.status !== InstanceStatus.STOPPED) {
|
296
|
-
instance.status
|
295
|
+
if (instance.status != InstanceStatus.FAILED) {
|
296
|
+
instance.status = InstanceStatus.STOPPED;
|
297
|
+
}
|
297
298
|
instance.pid = null;
|
298
299
|
instance.health = null;
|
299
300
|
socketManager.emitSystemEvent(systemId, EVENT_STATUS_CHANGED, instance);
|
@@ -385,7 +386,7 @@ export class InstanceManager {
|
|
385
386
|
throw new Error(`Operator block has no local definition: ${blockRef}`);
|
386
387
|
}
|
387
388
|
|
388
|
-
const localConfig = operatorDefinition.definition.spec.local as
|
389
|
+
const localConfig = operatorDefinition.definition.spec.local as LocalInstance;
|
389
390
|
|
390
391
|
let instance = await this.start(systemId, instanceId);
|
391
392
|
if (instance instanceof Task) {
|
@@ -666,8 +667,7 @@ export class InstanceManager {
|
|
666
667
|
|
667
668
|
const out = await this.saveInternalInstance({
|
668
669
|
...instance,
|
669
|
-
type: InstanceType.
|
670
|
-
pid: null,
|
670
|
+
type: InstanceType.DOCKER,
|
671
671
|
health: null,
|
672
672
|
portType: DEFAULT_HEALTH_PORT_TYPE,
|
673
673
|
status: InstanceStatus.FAILED,
|
@@ -811,7 +811,6 @@ export class InstanceManager {
|
|
811
811
|
if (instance.status !== newStatus) {
|
812
812
|
const oldStatus = instance.status;
|
813
813
|
const skipUpdate =
|
814
|
-
(newStatus === InstanceStatus.STOPPED && instance.status === InstanceStatus.FAILED) ||
|
815
814
|
([InstanceStatus.READY, InstanceStatus.UNHEALTHY].includes(newStatus) &&
|
816
815
|
instance.status === InstanceStatus.STOPPING) ||
|
817
816
|
(newStatus === InstanceStatus.STOPPED &&
|
@@ -835,7 +834,7 @@ export class InstanceManager {
|
|
835
834
|
|
836
835
|
if (
|
837
836
|
instance.desiredStatus === DesiredInstanceStatus.RUN &&
|
838
|
-
[InstanceStatus.STOPPED, InstanceStatus.
|
837
|
+
[InstanceStatus.STOPPED, InstanceStatus.STOPPING].includes(newStatus)
|
839
838
|
) {
|
840
839
|
//If the instance is stopped but we want it to run, start it
|
841
840
|
try {
|
@@ -928,11 +927,18 @@ export class InstanceManager {
|
|
928
927
|
}
|
929
928
|
|
930
929
|
if (statusType === 'created') {
|
930
|
+
if (state.ExitCode !== 0) {
|
931
|
+
// Failed during creation
|
932
|
+
return InstanceStatus.FAILED;
|
933
|
+
}
|
931
934
|
return InstanceStatus.STARTING;
|
932
935
|
}
|
933
936
|
|
934
937
|
if (statusType === 'exited' || statusType === 'dead') {
|
935
|
-
|
938
|
+
if (state.ExitCode === 0) {
|
939
|
+
return InstanceStatus.STOPPED;
|
940
|
+
}
|
941
|
+
return InstanceStatus.FAILED;
|
936
942
|
}
|
937
943
|
|
938
944
|
if (statusType === 'removing') {
|
@@ -1013,7 +1019,7 @@ export class InstanceManager {
|
|
1013
1019
|
}
|
1014
1020
|
|
1015
1021
|
if (parseKapetaUri(provider.kind).fullName === KIND_BLOCK_TYPE_OPERATOR) {
|
1016
|
-
const localConfig = provider.data.spec.local as
|
1022
|
+
const localConfig = provider.data.spec.local as LocalInstance;
|
1017
1023
|
return localConfig.singleton ?? false;
|
1018
1024
|
}
|
1019
1025
|
|
package/src/operatorManager.ts
CHANGED
@@ -21,11 +21,10 @@ import {
|
|
21
21
|
EnvironmentType,
|
22
22
|
KIND_BLOCK_TYPE_OPERATOR,
|
23
23
|
KIND_RESOURCE_OPERATOR,
|
24
|
-
LocalImageOptions,
|
25
24
|
OperatorInfo,
|
26
25
|
StringMap,
|
27
26
|
} from './types';
|
28
|
-
import { BlockInstance, Resource } from '@kapeta/schemas';
|
27
|
+
import { BlockInstance, LocalInstance, Resource } from '@kapeta/schemas';
|
29
28
|
import { definitionsManager } from './definitionsManager';
|
30
29
|
import { getBindHost, toPortInfo } from './utils/utils';
|
31
30
|
import { parseKapetaUri, normalizeKapetaUri } from '@kapeta/nodejs-utils';
|
@@ -42,7 +41,7 @@ class Operator {
|
|
42
41
|
this._data = data;
|
43
42
|
}
|
44
43
|
|
45
|
-
getLocalData():
|
44
|
+
getLocalData(): LocalInstance {
|
46
45
|
return this._data.definition.spec.local;
|
47
46
|
}
|
48
47
|
|
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
@@ -73,20 +73,6 @@ export interface Health {
|
|
73
73
|
retries?: number;
|
74
74
|
}
|
75
75
|
|
76
|
-
export type PortInfo = { port: number; type: 'tcp' | 'udp' } | number | string;
|
77
|
-
|
78
|
-
export type LocalImageOptions<Credentials = AnyMap, Options = AnyMap> = {
|
79
|
-
image: string;
|
80
|
-
ports: { [key: string]: PortInfo };
|
81
|
-
credentials?: Credentials;
|
82
|
-
options?: Options;
|
83
|
-
cmd?: string;
|
84
|
-
env?: AnyMap;
|
85
|
-
health?: Health;
|
86
|
-
singleton?: boolean;
|
87
|
-
mounts?: { [key: string]: string };
|
88
|
-
};
|
89
|
-
|
90
76
|
export type InstanceInfo = {
|
91
77
|
systemId: string;
|
92
78
|
instanceId: string;
|
@@ -105,11 +91,6 @@ export type InstanceInfo = {
|
|
105
91
|
portType?: string;
|
106
92
|
};
|
107
93
|
|
108
|
-
interface ResourceRef {
|
109
|
-
blockId: string;
|
110
|
-
resourceName: string;
|
111
|
-
}
|
112
|
-
|
113
94
|
export type ProxyRequestHandler = (req: StringBodyRequest, res: express.Response, info: ProxyRequestInfo) => void;
|
114
95
|
|
115
96
|
export interface OperatorInstancePort {
|
@@ -18,19 +18,13 @@ import {
|
|
18
18
|
} from '../containerManager';
|
19
19
|
import { LogData } from './LogData';
|
20
20
|
import { clusterService } from '../clusterService';
|
21
|
-
import {
|
22
|
-
AnyMap,
|
23
|
-
BlockProcessParams,
|
24
|
-
InstanceType,
|
25
|
-
KIND_BLOCK_TYPE_OPERATOR,
|
26
|
-
LocalImageOptions,
|
27
|
-
ProcessInfo,
|
28
|
-
StringMap,
|
29
|
-
} from '../types';
|
21
|
+
import { AnyMap, BlockProcessParams, InstanceType, KIND_BLOCK_TYPE_OPERATOR, ProcessInfo, StringMap } from '../types';
|
30
22
|
import { definitionsManager } from '../definitionsManager';
|
31
23
|
import Docker from 'dockerode';
|
32
24
|
import OS from 'node:os';
|
25
|
+
import Path from 'node:path';
|
33
26
|
import { taskManager } from '../taskManager';
|
27
|
+
import { LocalDevContainer, LocalInstance } from '@kapeta/schemas';
|
34
28
|
|
35
29
|
const KAPETA_SYSTEM_ID = 'KAPETA_SYSTEM_ID';
|
36
30
|
const KAPETA_BLOCK_REF = 'KAPETA_BLOCK_REF';
|
@@ -189,6 +183,8 @@ export class BlockInstanceRunner {
|
|
189
183
|
throw new Error('Missing target kind in block definition');
|
190
184
|
}
|
191
185
|
|
186
|
+
const realLocalPath = await FSExtra.realpath(baseDir);
|
187
|
+
|
192
188
|
const kindUri = parseKapetaUri(assetVersion.definition.kind);
|
193
189
|
|
194
190
|
const providerVersion = await getProvider(kindUri);
|
@@ -205,17 +201,29 @@ export class BlockInstanceRunner {
|
|
205
201
|
throw new Error(`Target not found: ${targetKindUri.id}`);
|
206
202
|
}
|
207
203
|
|
208
|
-
const localContainer = targetVersion.definition.spec.local;
|
204
|
+
const localContainer = targetVersion.definition.spec.local as LocalDevContainer;
|
209
205
|
|
210
206
|
if (!localContainer) {
|
211
207
|
throw new Error(`Missing local container information from target: ${targetKindUri.id}`);
|
212
208
|
}
|
213
209
|
|
214
|
-
|
215
|
-
|
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) {
|
216
214
|
throw new Error(`Missing docker image information: ${JSON.stringify(localContainer)}`);
|
217
215
|
}
|
218
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
|
+
|
219
227
|
const containerName = await getBlockInstanceContainerName(this._systemId, blockInstance.id, targetKindUri.id);
|
220
228
|
const startCmd = localContainer.handlers?.onCreate ? localContainer.handlers.onCreate : '';
|
221
229
|
const dockerOpts = localContainer.options ?? {};
|
@@ -242,8 +250,6 @@ export class BlockInstanceRunner {
|
|
242
250
|
HealthCheck = containerManager.toDockerHealth({ cmd: localContainer.healthcheck });
|
243
251
|
}
|
244
252
|
|
245
|
-
const realLocalPath = await FSExtra.realpath(baseDir);
|
246
|
-
|
247
253
|
const Mounts = containerManager.toDockerMounts({
|
248
254
|
[workingDir]: toLocalBindVolume(realLocalPath),
|
249
255
|
});
|
@@ -391,7 +397,7 @@ export class BlockInstanceRunner {
|
|
391
397
|
throw new Error(`Provider did not have local image: ${providerRef}`);
|
392
398
|
}
|
393
399
|
|
394
|
-
const local = spec.local as
|
400
|
+
const local = spec.local as LocalInstance;
|
395
401
|
|
396
402
|
const dockerImage = local.image;
|
397
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,9 +6,9 @@
|
|
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, KIND_BLOCK_TYPE_OPERATOR
|
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';
|
@@ -40,13 +40,13 @@ export async function getBlockInstanceContainerName(systemId: string, instanceId
|
|
40
40
|
return `kapeta-block-instance-${md5(normalizeKapetaUri(systemId) + instanceId)}`;
|
41
41
|
}
|
42
42
|
|
43
|
-
export function toPortInfo(port:
|
43
|
+
export function toPortInfo(port: LocalInstancePort) {
|
44
44
|
if (typeof port === 'number' || typeof port === 'string') {
|
45
45
|
return { port: parseInt(`${port}`), type: 'tcp' };
|
46
46
|
}
|
47
47
|
|
48
48
|
if (!port.type) {
|
49
|
-
port.type =
|
49
|
+
port.type = LocalInstancePortType.TCP;
|
50
50
|
}
|
51
51
|
|
52
52
|
return port;
|