@kapeta/local-cluster-service 0.0.0-96f91ef
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/.eslintrc.cjs +25 -0
- package/.github/workflows/check-license.yml +17 -0
- package/.github/workflows/main.yml +26 -0
- package/.prettierignore +4 -0
- package/.vscode/launch.json +19 -0
- package/CHANGELOG.md +920 -0
- package/LICENSE +38 -0
- package/README.md +36 -0
- package/definitions.d.ts +35 -0
- package/dist/cjs/index.d.ts +34 -0
- package/dist/cjs/index.js +263 -0
- package/dist/cjs/package.json +1 -0
- package/dist/cjs/src/RepositoryWatcher.d.ts +30 -0
- package/dist/cjs/src/RepositoryWatcher.js +332 -0
- package/dist/cjs/src/ai/aiClient.d.ts +20 -0
- package/dist/cjs/src/ai/aiClient.js +74 -0
- package/dist/cjs/src/ai/routes.d.ts +7 -0
- package/dist/cjs/src/ai/routes.js +37 -0
- package/dist/cjs/src/ai/transform.d.ts +11 -0
- package/dist/cjs/src/ai/transform.js +239 -0
- package/dist/cjs/src/ai/types.d.ts +40 -0
- package/dist/cjs/src/ai/types.js +2 -0
- package/dist/cjs/src/api.d.ts +7 -0
- package/dist/cjs/src/api.js +29 -0
- package/dist/cjs/src/assetManager.d.ts +41 -0
- package/dist/cjs/src/assetManager.js +274 -0
- package/dist/cjs/src/assets/routes.d.ts +7 -0
- package/dist/cjs/src/assets/routes.js +165 -0
- package/dist/cjs/src/attachments/routes.d.ts +7 -0
- package/dist/cjs/src/attachments/routes.js +72 -0
- package/dist/cjs/src/authManager.d.ts +16 -0
- package/dist/cjs/src/authManager.js +64 -0
- package/dist/cjs/src/cacheManager.d.ts +20 -0
- package/dist/cjs/src/cacheManager.js +51 -0
- package/dist/cjs/src/clusterService.d.ts +44 -0
- package/dist/cjs/src/clusterService.js +120 -0
- package/dist/cjs/src/codeGeneratorManager.d.ts +14 -0
- package/dist/cjs/src/codeGeneratorManager.js +93 -0
- package/dist/cjs/src/config/routes.d.ts +7 -0
- package/dist/cjs/src/config/routes.js +160 -0
- package/dist/cjs/src/configManager.d.ts +42 -0
- package/dist/cjs/src/configManager.js +136 -0
- package/dist/cjs/src/containerManager.d.ts +148 -0
- package/dist/cjs/src/containerManager.js +958 -0
- package/dist/cjs/src/definitionsManager.d.ts +20 -0
- package/dist/cjs/src/definitionsManager.js +171 -0
- package/dist/cjs/src/filesystem/routes.d.ts +7 -0
- package/dist/cjs/src/filesystem/routes.js +105 -0
- package/dist/cjs/src/filesystemManager.d.ts +27 -0
- package/dist/cjs/src/filesystemManager.js +118 -0
- package/dist/cjs/src/identities/routes.d.ts +7 -0
- package/dist/cjs/src/identities/routes.js +37 -0
- package/dist/cjs/src/instanceManager.d.ts +69 -0
- package/dist/cjs/src/instanceManager.js +910 -0
- package/dist/cjs/src/instances/routes.d.ts +7 -0
- package/dist/cjs/src/instances/routes.js +179 -0
- package/dist/cjs/src/middleware/cors.d.ts +6 -0
- package/dist/cjs/src/middleware/cors.js +14 -0
- package/dist/cjs/src/middleware/kapeta.d.ts +15 -0
- package/dist/cjs/src/middleware/kapeta.js +28 -0
- package/dist/cjs/src/middleware/stringBody.d.ts +9 -0
- package/dist/cjs/src/middleware/stringBody.js +18 -0
- package/dist/cjs/src/networkManager.d.ts +37 -0
- package/dist/cjs/src/networkManager.js +119 -0
- package/dist/cjs/src/operatorManager.d.ts +41 -0
- package/dist/cjs/src/operatorManager.js +211 -0
- package/dist/cjs/src/progressListener.d.ts +31 -0
- package/dist/cjs/src/progressListener.js +133 -0
- package/dist/cjs/src/providerManager.d.ts +11 -0
- package/dist/cjs/src/providerManager.js +84 -0
- package/dist/cjs/src/providers/routes.d.ts +7 -0
- package/dist/cjs/src/providers/routes.js +46 -0
- package/dist/cjs/src/proxy/routes.d.ts +7 -0
- package/dist/cjs/src/proxy/routes.js +115 -0
- package/dist/cjs/src/proxy/types/rest.d.ts +10 -0
- package/dist/cjs/src/proxy/types/rest.js +123 -0
- package/dist/cjs/src/proxy/types/web.d.ts +8 -0
- package/dist/cjs/src/proxy/types/web.js +61 -0
- package/dist/cjs/src/repositoryManager.d.ts +35 -0
- package/dist/cjs/src/repositoryManager.js +247 -0
- package/dist/cjs/src/serviceManager.d.ts +36 -0
- package/dist/cjs/src/serviceManager.js +106 -0
- package/dist/cjs/src/socketManager.d.ts +32 -0
- package/dist/cjs/src/socketManager.js +125 -0
- package/dist/cjs/src/storageService.d.ts +21 -0
- package/dist/cjs/src/storageService.js +81 -0
- package/dist/cjs/src/taskManager.d.ts +70 -0
- package/dist/cjs/src/taskManager.js +181 -0
- package/dist/cjs/src/tasks/routes.d.ts +7 -0
- package/dist/cjs/src/tasks/routes.js +39 -0
- package/dist/cjs/src/traffic/routes.d.ts +7 -0
- package/dist/cjs/src/traffic/routes.js +22 -0
- package/dist/cjs/src/types.d.ts +99 -0
- package/dist/cjs/src/types.js +39 -0
- package/dist/cjs/src/utils/BlockInstanceRunner.d.ts +28 -0
- package/dist/cjs/src/utils/BlockInstanceRunner.js +432 -0
- package/dist/cjs/src/utils/DefaultProviderInstaller.d.ts +15 -0
- package/dist/cjs/src/utils/DefaultProviderInstaller.js +136 -0
- package/dist/cjs/src/utils/InternalConfigProvider.d.ts +38 -0
- package/dist/cjs/src/utils/InternalConfigProvider.js +146 -0
- package/dist/cjs/src/utils/LogData.d.ts +23 -0
- package/dist/cjs/src/utils/LogData.js +46 -0
- package/dist/cjs/src/utils/commandLineUtils.d.ts +8 -0
- package/dist/cjs/src/utils/commandLineUtils.js +39 -0
- package/dist/cjs/src/utils/pathTemplateParser.d.ts +30 -0
- package/dist/cjs/src/utils/pathTemplateParser.js +135 -0
- package/dist/cjs/src/utils/utils.d.ts +40 -0
- package/dist/cjs/src/utils/utils.js +148 -0
- package/dist/cjs/start.d.ts +5 -0
- package/dist/cjs/start.js +17 -0
- package/dist/cjs/test/proxy/types/rest.test.d.ts +5 -0
- package/dist/cjs/test/proxy/types/rest.test.js +48 -0
- package/dist/cjs/test/utils/pathTemplateParser.test.d.ts +5 -0
- package/dist/cjs/test/utils/pathTemplateParser.test.js +27 -0
- package/dist/esm/index.d.ts +34 -0
- package/dist/esm/index.js +263 -0
- package/dist/esm/package.json +1 -0
- package/dist/esm/src/RepositoryWatcher.d.ts +30 -0
- package/dist/esm/src/RepositoryWatcher.js +332 -0
- package/dist/esm/src/ai/aiClient.d.ts +20 -0
- package/dist/esm/src/ai/aiClient.js +74 -0
- package/dist/esm/src/ai/routes.d.ts +7 -0
- package/dist/esm/src/ai/routes.js +37 -0
- package/dist/esm/src/ai/transform.d.ts +11 -0
- package/dist/esm/src/ai/transform.js +239 -0
- package/dist/esm/src/ai/types.d.ts +40 -0
- package/dist/esm/src/ai/types.js +2 -0
- package/dist/esm/src/api.d.ts +7 -0
- package/dist/esm/src/api.js +29 -0
- package/dist/esm/src/assetManager.d.ts +41 -0
- package/dist/esm/src/assetManager.js +274 -0
- package/dist/esm/src/assets/routes.d.ts +7 -0
- package/dist/esm/src/assets/routes.js +165 -0
- package/dist/esm/src/attachments/routes.d.ts +7 -0
- package/dist/esm/src/attachments/routes.js +72 -0
- package/dist/esm/src/authManager.d.ts +16 -0
- package/dist/esm/src/authManager.js +64 -0
- package/dist/esm/src/cacheManager.d.ts +20 -0
- package/dist/esm/src/cacheManager.js +51 -0
- package/dist/esm/src/clusterService.d.ts +44 -0
- package/dist/esm/src/clusterService.js +120 -0
- package/dist/esm/src/codeGeneratorManager.d.ts +14 -0
- package/dist/esm/src/codeGeneratorManager.js +93 -0
- package/dist/esm/src/config/routes.d.ts +7 -0
- package/dist/esm/src/config/routes.js +160 -0
- package/dist/esm/src/configManager.d.ts +42 -0
- package/dist/esm/src/configManager.js +136 -0
- package/dist/esm/src/containerManager.d.ts +148 -0
- package/dist/esm/src/containerManager.js +958 -0
- package/dist/esm/src/definitionsManager.d.ts +20 -0
- package/dist/esm/src/definitionsManager.js +171 -0
- package/dist/esm/src/filesystem/routes.d.ts +7 -0
- package/dist/esm/src/filesystem/routes.js +105 -0
- package/dist/esm/src/filesystemManager.d.ts +27 -0
- package/dist/esm/src/filesystemManager.js +118 -0
- package/dist/esm/src/identities/routes.d.ts +7 -0
- package/dist/esm/src/identities/routes.js +37 -0
- package/dist/esm/src/instanceManager.d.ts +69 -0
- package/dist/esm/src/instanceManager.js +910 -0
- package/dist/esm/src/instances/routes.d.ts +7 -0
- package/dist/esm/src/instances/routes.js +179 -0
- package/dist/esm/src/middleware/cors.d.ts +6 -0
- package/dist/esm/src/middleware/cors.js +14 -0
- package/dist/esm/src/middleware/kapeta.d.ts +15 -0
- package/dist/esm/src/middleware/kapeta.js +28 -0
- package/dist/esm/src/middleware/stringBody.d.ts +9 -0
- package/dist/esm/src/middleware/stringBody.js +18 -0
- package/dist/esm/src/networkManager.d.ts +37 -0
- package/dist/esm/src/networkManager.js +119 -0
- package/dist/esm/src/operatorManager.d.ts +41 -0
- package/dist/esm/src/operatorManager.js +211 -0
- package/dist/esm/src/progressListener.d.ts +31 -0
- package/dist/esm/src/progressListener.js +133 -0
- package/dist/esm/src/providerManager.d.ts +11 -0
- package/dist/esm/src/providerManager.js +84 -0
- package/dist/esm/src/providers/routes.d.ts +7 -0
- package/dist/esm/src/providers/routes.js +46 -0
- package/dist/esm/src/proxy/routes.d.ts +7 -0
- package/dist/esm/src/proxy/routes.js +115 -0
- package/dist/esm/src/proxy/types/rest.d.ts +10 -0
- package/dist/esm/src/proxy/types/rest.js +123 -0
- package/dist/esm/src/proxy/types/web.d.ts +8 -0
- package/dist/esm/src/proxy/types/web.js +61 -0
- package/dist/esm/src/repositoryManager.d.ts +35 -0
- package/dist/esm/src/repositoryManager.js +247 -0
- package/dist/esm/src/serviceManager.d.ts +36 -0
- package/dist/esm/src/serviceManager.js +106 -0
- package/dist/esm/src/socketManager.d.ts +32 -0
- package/dist/esm/src/socketManager.js +125 -0
- package/dist/esm/src/storageService.d.ts +21 -0
- package/dist/esm/src/storageService.js +81 -0
- package/dist/esm/src/taskManager.d.ts +70 -0
- package/dist/esm/src/taskManager.js +181 -0
- package/dist/esm/src/tasks/routes.d.ts +7 -0
- package/dist/esm/src/tasks/routes.js +39 -0
- package/dist/esm/src/traffic/routes.d.ts +7 -0
- package/dist/esm/src/traffic/routes.js +22 -0
- package/dist/esm/src/types.d.ts +99 -0
- package/dist/esm/src/types.js +39 -0
- package/dist/esm/src/utils/BlockInstanceRunner.d.ts +28 -0
- package/dist/esm/src/utils/BlockInstanceRunner.js +432 -0
- package/dist/esm/src/utils/DefaultProviderInstaller.d.ts +15 -0
- package/dist/esm/src/utils/DefaultProviderInstaller.js +136 -0
- package/dist/esm/src/utils/InternalConfigProvider.d.ts +38 -0
- package/dist/esm/src/utils/InternalConfigProvider.js +146 -0
- package/dist/esm/src/utils/LogData.d.ts +23 -0
- package/dist/esm/src/utils/LogData.js +46 -0
- package/dist/esm/src/utils/commandLineUtils.d.ts +8 -0
- package/dist/esm/src/utils/commandLineUtils.js +39 -0
- package/dist/esm/src/utils/pathTemplateParser.d.ts +30 -0
- package/dist/esm/src/utils/pathTemplateParser.js +135 -0
- package/dist/esm/src/utils/utils.d.ts +40 -0
- package/dist/esm/src/utils/utils.js +148 -0
- package/dist/esm/start.d.ts +5 -0
- package/dist/esm/start.js +17 -0
- package/dist/esm/test/proxy/types/rest.test.d.ts +5 -0
- package/dist/esm/test/proxy/types/rest.test.js +48 -0
- package/dist/esm/test/utils/pathTemplateParser.test.d.ts +5 -0
- package/dist/esm/test/utils/pathTemplateParser.test.js +27 -0
- package/index.ts +280 -0
- package/jest.config.js +8 -0
- package/package.json +134 -0
- package/src/RepositoryWatcher.ts +363 -0
- package/src/ai/aiClient.ts +93 -0
- package/src/ai/routes.ts +39 -0
- package/src/ai/transform.ts +275 -0
- package/src/ai/types.ts +45 -0
- package/src/api.ts +32 -0
- package/src/assetManager.ts +355 -0
- package/src/assets/routes.ts +183 -0
- package/src/attachments/routes.ts +79 -0
- package/src/authManager.ts +67 -0
- package/src/cacheManager.ts +59 -0
- package/src/clusterService.ts +142 -0
- package/src/codeGeneratorManager.ts +109 -0
- package/src/config/routes.ts +201 -0
- package/src/configManager.ts +180 -0
- package/src/containerManager.ts +1178 -0
- package/src/definitionsManager.ts +212 -0
- package/src/filesystem/routes.ts +123 -0
- package/src/filesystemManager.ts +133 -0
- package/src/identities/routes.ts +38 -0
- package/src/instanceManager.ts +1160 -0
- package/src/instances/routes.ts +203 -0
- package/src/middleware/cors.ts +14 -0
- package/src/middleware/kapeta.ts +41 -0
- package/src/middleware/stringBody.ts +21 -0
- package/src/networkManager.ts +148 -0
- package/src/operatorManager.ts +294 -0
- package/src/progressListener.ts +151 -0
- package/src/providerManager.ts +97 -0
- package/src/providers/routes.ts +51 -0
- package/src/proxy/routes.ts +153 -0
- package/src/proxy/types/rest.ts +172 -0
- package/src/proxy/types/web.ts +70 -0
- package/src/repositoryManager.ts +291 -0
- package/src/serviceManager.ts +133 -0
- package/src/socketManager.ts +138 -0
- package/src/storageService.ts +97 -0
- package/src/taskManager.ts +247 -0
- package/src/tasks/routes.ts +43 -0
- package/src/traffic/routes.ts +23 -0
- package/src/types.ts +112 -0
- package/src/utils/BlockInstanceRunner.ts +577 -0
- package/src/utils/DefaultProviderInstaller.ts +150 -0
- package/src/utils/InternalConfigProvider.ts +214 -0
- package/src/utils/LogData.ts +50 -0
- package/src/utils/commandLineUtils.ts +45 -0
- package/src/utils/pathTemplateParser.ts +157 -0
- package/src/utils/utils.ts +155 -0
- package/start.ts +14 -0
- package/test/proxy/types/rest.test.ts +54 -0
- package/test/utils/pathTemplateParser.test.ts +29 -0
- package/tsconfig.json +15 -0
@@ -0,0 +1,294 @@
|
|
1
|
+
/**
|
2
|
+
* Copyright 2023 Kapeta Inc.
|
3
|
+
* SPDX-License-Identifier: BUSL-1.1
|
4
|
+
*/
|
5
|
+
|
6
|
+
import { DefinitionInfo } from '@kapeta/local-cluster-config';
|
7
|
+
import Path from 'path';
|
8
|
+
import md5 from 'md5';
|
9
|
+
import { serviceManager } from './serviceManager';
|
10
|
+
import { storageService } from './storageService';
|
11
|
+
import {
|
12
|
+
COMPOSE_LABEL_PROJECT,
|
13
|
+
COMPOSE_LABEL_SERVICE,
|
14
|
+
CONTAINER_LABEL_PORT_PREFIX,
|
15
|
+
ContainerInfo,
|
16
|
+
containerManager,
|
17
|
+
} from './containerManager';
|
18
|
+
import FSExtra from 'fs-extra';
|
19
|
+
import { AnyMap, EnvironmentType, KIND_BLOCK_TYPE_OPERATOR, KIND_RESOURCE_OPERATOR, StringMap } from './types';
|
20
|
+
import { BlockInstance, LocalInstance, Resource } from '@kapeta/schemas';
|
21
|
+
import { definitionsManager } from './definitionsManager';
|
22
|
+
import { getDockerHostIp, getRemoteHostForEnvironment, toPortInfo } from './utils/utils';
|
23
|
+
import { parseKapetaUri, normalizeKapetaUri } from '@kapeta/nodejs-utils';
|
24
|
+
import _ from 'lodash';
|
25
|
+
import AsyncLock from 'async-lock';
|
26
|
+
import { taskManager } from './taskManager';
|
27
|
+
import { ResourceInfo } from '@kapeta/sdk-config';
|
28
|
+
|
29
|
+
const KIND_PLAN = 'core/plan';
|
30
|
+
|
31
|
+
class Operator {
|
32
|
+
private readonly _data: DefinitionInfo;
|
33
|
+
|
34
|
+
constructor(data: DefinitionInfo) {
|
35
|
+
this._data = data;
|
36
|
+
}
|
37
|
+
|
38
|
+
getLocalData(): LocalInstance {
|
39
|
+
return this._data.definition.spec.local;
|
40
|
+
}
|
41
|
+
|
42
|
+
getDefinitionInfo() {
|
43
|
+
return this._data;
|
44
|
+
}
|
45
|
+
|
46
|
+
getCredentials() {
|
47
|
+
return this._data.definition.spec.local.credentials;
|
48
|
+
}
|
49
|
+
}
|
50
|
+
|
51
|
+
class OperatorManager {
|
52
|
+
private _mountDir: string;
|
53
|
+
|
54
|
+
private operatorLock: AsyncLock = new AsyncLock();
|
55
|
+
|
56
|
+
constructor() {
|
57
|
+
this._mountDir = Path.join(storageService.getKapetaBasedir(), 'mounts');
|
58
|
+
|
59
|
+
FSExtra.mkdirpSync(this._mountDir);
|
60
|
+
}
|
61
|
+
|
62
|
+
_getMountPoint(operatorType: string, mountName: string) {
|
63
|
+
return Path.join(this._mountDir, operatorType, mountName);
|
64
|
+
}
|
65
|
+
|
66
|
+
/**
|
67
|
+
* Get operator definition for resource type
|
68
|
+
*/
|
69
|
+
async getOperator(fullName: string, version: string) {
|
70
|
+
const operators = await definitionsManager.getDefinitions([KIND_RESOURCE_OPERATOR, KIND_BLOCK_TYPE_OPERATOR]);
|
71
|
+
|
72
|
+
const operator: DefinitionInfo | undefined = operators.find(
|
73
|
+
(operator) =>
|
74
|
+
operator.definition &&
|
75
|
+
operator.definition.metadata &&
|
76
|
+
operator.definition.metadata.name &&
|
77
|
+
operator.definition.metadata.name.toLowerCase() === fullName.toLowerCase() &&
|
78
|
+
operator.version === version
|
79
|
+
);
|
80
|
+
|
81
|
+
if (!operator) {
|
82
|
+
throw new Error(`Unknown operator type: ${fullName}:${version}`);
|
83
|
+
}
|
84
|
+
|
85
|
+
if (!operator.definition.spec || !operator.definition.spec.local) {
|
86
|
+
throw new Error(`Operator missing local definition: ${fullName}:${version}`);
|
87
|
+
}
|
88
|
+
|
89
|
+
return new Operator(operator);
|
90
|
+
}
|
91
|
+
|
92
|
+
/**
|
93
|
+
* Get information about a specific consumed resource
|
94
|
+
*/
|
95
|
+
async getConsumerResourceInfo(
|
96
|
+
systemId: string,
|
97
|
+
fromServiceId: string,
|
98
|
+
resourceType: string,
|
99
|
+
portType: string,
|
100
|
+
name: string,
|
101
|
+
environment?: EnvironmentType,
|
102
|
+
ensureContainer: boolean = true
|
103
|
+
): Promise<ResourceInfo<any, any>> {
|
104
|
+
systemId = normalizeKapetaUri(systemId);
|
105
|
+
const plans = await definitionsManager.getDefinitions(KIND_PLAN);
|
106
|
+
|
107
|
+
const planUri = parseKapetaUri(systemId);
|
108
|
+
const currentPlan = plans.find(
|
109
|
+
(plan) => plan.definition.metadata.name === planUri.fullName && plan.version === planUri.version
|
110
|
+
);
|
111
|
+
if (!currentPlan) {
|
112
|
+
throw new Error(`Unknown plan: ${systemId}`);
|
113
|
+
}
|
114
|
+
|
115
|
+
const currentInstance = currentPlan.definition.spec.blocks?.find(
|
116
|
+
(instance: BlockInstance) => instance.id === fromServiceId
|
117
|
+
);
|
118
|
+
if (!currentInstance) {
|
119
|
+
throw new Error(`Unknown instance: ${fromServiceId} in plan ${systemId}`);
|
120
|
+
}
|
121
|
+
|
122
|
+
const blockDefinition = await definitionsManager.getDefinition(currentInstance.block.ref);
|
123
|
+
|
124
|
+
if (!blockDefinition) {
|
125
|
+
throw new Error(`Unknown block: ${currentInstance.block.ref} in plan ${systemId}`);
|
126
|
+
}
|
127
|
+
|
128
|
+
const blockResource = blockDefinition.definition.spec?.consumers?.find((resource: Resource) => {
|
129
|
+
if (resource.metadata.name !== name) {
|
130
|
+
return false;
|
131
|
+
}
|
132
|
+
return parseKapetaUri(resource.kind).fullName === resourceType;
|
133
|
+
});
|
134
|
+
|
135
|
+
if (!blockResource) {
|
136
|
+
throw new Error(`Unknown resource: ${name} in block ${currentInstance.block.ref} in plan ${systemId}`);
|
137
|
+
}
|
138
|
+
|
139
|
+
const kindUri = parseKapetaUri(blockResource.kind);
|
140
|
+
const operator = await this.getOperator(resourceType, kindUri.version);
|
141
|
+
const credentials = operator.getCredentials();
|
142
|
+
if (ensureContainer) {
|
143
|
+
await this.ensureOperator(systemId, resourceType, kindUri.version);
|
144
|
+
}
|
145
|
+
|
146
|
+
const hostPort = await serviceManager.ensureServicePort(systemId, resourceType, portType);
|
147
|
+
|
148
|
+
if (!hostPort) {
|
149
|
+
throw new Error('Unknown resource port type : ' + resourceType + '#' + portType);
|
150
|
+
}
|
151
|
+
|
152
|
+
const dbName = name + '_' + fromServiceId.replace(/[^a-z0-9]/gi, '');
|
153
|
+
const safeName = dbName.replace('_', '-');
|
154
|
+
|
155
|
+
return {
|
156
|
+
host: getRemoteHostForEnvironment(environment),
|
157
|
+
port: hostPort,
|
158
|
+
type: portType,
|
159
|
+
protocol: 'tcp',
|
160
|
+
options: {
|
161
|
+
// expose as fullName since that is not operator specific, but unique
|
162
|
+
fullName: safeName,
|
163
|
+
dbName,
|
164
|
+
},
|
165
|
+
credentials,
|
166
|
+
};
|
167
|
+
}
|
168
|
+
|
169
|
+
async getOperatorPorts(systemId: string, kind: string, version: string) {
|
170
|
+
const operator = await this.getOperator(kind, version);
|
171
|
+
|
172
|
+
const operatorData = operator.getLocalData();
|
173
|
+
|
174
|
+
const portTypes = Object.keys(operatorData.ports);
|
175
|
+
|
176
|
+
portTypes.sort();
|
177
|
+
|
178
|
+
const ports: AnyMap = {};
|
179
|
+
|
180
|
+
for (let i = 0; i < portTypes.length; i++) {
|
181
|
+
const portType = portTypes[i];
|
182
|
+
let containerPortInfo = operatorData.ports[portType];
|
183
|
+
const hostPort = await serviceManager.ensureServicePort(systemId, kind, portType);
|
184
|
+
const portInfo = toPortInfo(containerPortInfo);
|
185
|
+
const portId = portInfo.port + '/' + portInfo.type;
|
186
|
+
|
187
|
+
ports[portId] = {
|
188
|
+
type: portType,
|
189
|
+
hostPort,
|
190
|
+
protocol: portInfo.type,
|
191
|
+
};
|
192
|
+
}
|
193
|
+
|
194
|
+
return ports;
|
195
|
+
}
|
196
|
+
|
197
|
+
/**
|
198
|
+
* Ensure we have a running operator of given type
|
199
|
+
*
|
200
|
+
* @param systemId the plan ref
|
201
|
+
* @param kind the full name - e.g. myhandle/rabbitmq
|
202
|
+
* @param version the version of the operator
|
203
|
+
*/
|
204
|
+
async ensureOperator(systemId: string, kind: string, version: string): Promise<ContainerInfo> {
|
205
|
+
systemId = normalizeKapetaUri(systemId);
|
206
|
+
|
207
|
+
const key = `${systemId}#${kind}:${version}`;
|
208
|
+
|
209
|
+
return await this.operatorLock.acquire(key, async () => {
|
210
|
+
const operator = await this.getOperator(kind, version);
|
211
|
+
|
212
|
+
const operatorData = operator.getLocalData();
|
213
|
+
|
214
|
+
const ports = await this.getOperatorPorts(systemId, kind, version);
|
215
|
+
|
216
|
+
const nameParts = [systemId, kind.toLowerCase(), version];
|
217
|
+
|
218
|
+
const containerName = `kapeta-resource-${md5(nameParts.join('_'))}`;
|
219
|
+
|
220
|
+
const PortBindings: { [key: string]: any } = {};
|
221
|
+
const Env: string[] = [];
|
222
|
+
|
223
|
+
const systemUri = parseKapetaUri(systemId);
|
224
|
+
|
225
|
+
const Labels: StringMap = {
|
226
|
+
kapeta: 'true',
|
227
|
+
[COMPOSE_LABEL_PROJECT]: systemUri.id.replace(/[^a-z0-9]/gi, '_'),
|
228
|
+
[COMPOSE_LABEL_SERVICE]: [kind, version].join('_').replace(/[^a-z0-9]/gi, '_'),
|
229
|
+
};
|
230
|
+
|
231
|
+
const operatorMetadata = operator.getDefinitionInfo().definition.metadata;
|
232
|
+
|
233
|
+
const hostIp = getDockerHostIp();
|
234
|
+
|
235
|
+
const ExposedPorts: { [key: string]: any } = {};
|
236
|
+
|
237
|
+
_.forEach(ports, (portInfo: any, containerPort) => {
|
238
|
+
ExposedPorts['' + containerPort] = {};
|
239
|
+
PortBindings['' + containerPort] = [
|
240
|
+
{
|
241
|
+
HostPort: '' + portInfo.hostPort,
|
242
|
+
HostIp: hostIp,
|
243
|
+
},
|
244
|
+
];
|
245
|
+
|
246
|
+
Labels[CONTAINER_LABEL_PORT_PREFIX + portInfo.hostPort] = portInfo.type;
|
247
|
+
});
|
248
|
+
|
249
|
+
const Mounts = await containerManager.createVolumes(systemId, kind, operatorData.mounts);
|
250
|
+
|
251
|
+
_.forEach(operatorData.env, (value, name) => {
|
252
|
+
Env.push(name + '=' + value);
|
253
|
+
});
|
254
|
+
|
255
|
+
const task = taskManager.add(
|
256
|
+
`operator:ensure:${key}`,
|
257
|
+
async () => {
|
258
|
+
let HealthCheck = undefined;
|
259
|
+
|
260
|
+
if (operatorData.health) {
|
261
|
+
HealthCheck = containerManager.toDockerHealth(operatorData.health);
|
262
|
+
}
|
263
|
+
|
264
|
+
const container = await containerManager.ensureContainer({
|
265
|
+
name: containerName,
|
266
|
+
Image: operatorData.image,
|
267
|
+
Hostname: containerName + '.kapeta',
|
268
|
+
Labels,
|
269
|
+
Cmd: operatorData.cmd,
|
270
|
+
ExposedPorts,
|
271
|
+
Env,
|
272
|
+
HealthCheck,
|
273
|
+
HostConfig: {
|
274
|
+
PortBindings,
|
275
|
+
Mounts,
|
276
|
+
},
|
277
|
+
});
|
278
|
+
|
279
|
+
await containerManager.waitForReady(container);
|
280
|
+
|
281
|
+
return new ContainerInfo(container);
|
282
|
+
},
|
283
|
+
{
|
284
|
+
name: `Ensuring ${operatorMetadata.title ?? operatorMetadata.name}`,
|
285
|
+
systemId,
|
286
|
+
}
|
287
|
+
);
|
288
|
+
|
289
|
+
return task.wait();
|
290
|
+
});
|
291
|
+
}
|
292
|
+
}
|
293
|
+
|
294
|
+
export const operatorManager = new OperatorManager();
|
@@ -0,0 +1,151 @@
|
|
1
|
+
/**
|
2
|
+
* Copyright 2023 Kapeta Inc.
|
3
|
+
* SPDX-License-Identifier: BUSL-1.1
|
4
|
+
*/
|
5
|
+
|
6
|
+
import { spawn } from '@kapeta/nodejs-process';
|
7
|
+
import { socketManager } from './socketManager';
|
8
|
+
import { LogEntry } from './types';
|
9
|
+
import { format } from 'node:util';
|
10
|
+
import { Task } from './taskManager';
|
11
|
+
|
12
|
+
export class ProgressListener {
|
13
|
+
private readonly systemId: string | undefined;
|
14
|
+
private readonly instanceId: string | undefined;
|
15
|
+
|
16
|
+
constructor(systemId?: string, instanceId?: string) {
|
17
|
+
this.systemId = systemId;
|
18
|
+
this.instanceId = instanceId;
|
19
|
+
}
|
20
|
+
|
21
|
+
protected emitLog(payload: Omit<LogEntry, 'time' | 'source'>) {
|
22
|
+
const logEntry: LogEntry = {
|
23
|
+
...payload,
|
24
|
+
source: 'stdout',
|
25
|
+
time: Date.now(),
|
26
|
+
};
|
27
|
+
if (this.systemId && this.instanceId) {
|
28
|
+
socketManager.emitInstanceLog(this.systemId, this.instanceId, logEntry);
|
29
|
+
return;
|
30
|
+
}
|
31
|
+
|
32
|
+
if (this.systemId) {
|
33
|
+
socketManager.emitSystemLog(this.systemId, logEntry);
|
34
|
+
return;
|
35
|
+
}
|
36
|
+
|
37
|
+
socketManager.emitGlobalLog(logEntry);
|
38
|
+
}
|
39
|
+
|
40
|
+
run(command: string, directory?: string): Promise<{ exit: number; signal: NodeJS.Signals | null; output: string }> {
|
41
|
+
this.info(`Running command "${command}"`);
|
42
|
+
|
43
|
+
return new Promise(async (resolve, reject) => {
|
44
|
+
try {
|
45
|
+
const chunks: Buffer[] = [];
|
46
|
+
const child = spawn(command, [], {
|
47
|
+
cwd: directory ? directory : process.cwd(),
|
48
|
+
shell: true,
|
49
|
+
});
|
50
|
+
|
51
|
+
child.onData((data) => {
|
52
|
+
this.emitLog({
|
53
|
+
level: data.type === 'stdout' ? 'INFO' : 'WARN',
|
54
|
+
message: data.line,
|
55
|
+
});
|
56
|
+
});
|
57
|
+
|
58
|
+
if (child.process.stdout) {
|
59
|
+
child.process.stdout.on('data', (data) => {
|
60
|
+
chunks.push(data);
|
61
|
+
});
|
62
|
+
}
|
63
|
+
|
64
|
+
child.process.on('exit', (exit, signal) => {
|
65
|
+
if (exit !== 0) {
|
66
|
+
this.warn(`Command "${command}" failed: ${exit}`);
|
67
|
+
reject(new Error(`Command "${command}" exited with code ${exit}`));
|
68
|
+
} else {
|
69
|
+
this.info(`Command OK: "${command}"`);
|
70
|
+
resolve({ exit, signal, output: Buffer.concat(chunks).toString() });
|
71
|
+
}
|
72
|
+
});
|
73
|
+
|
74
|
+
child.process.on('error', (err) => {
|
75
|
+
this.warn(`"${command}" failed: "${err.message}"`);
|
76
|
+
reject(err);
|
77
|
+
});
|
78
|
+
|
79
|
+
await child.wait();
|
80
|
+
} catch (e) {
|
81
|
+
reject(e);
|
82
|
+
}
|
83
|
+
});
|
84
|
+
}
|
85
|
+
|
86
|
+
async progress(label: string, callback: () => void | Promise<void>) {
|
87
|
+
this.info(`${label}: started`);
|
88
|
+
try {
|
89
|
+
const result = await callback();
|
90
|
+
this.info(`${label}: done`);
|
91
|
+
return result;
|
92
|
+
} catch (e: any) {
|
93
|
+
this.warn(`${label}: failed. ${e.message}`);
|
94
|
+
throw e;
|
95
|
+
}
|
96
|
+
}
|
97
|
+
|
98
|
+
async check(message: string, ok: boolean | Promise<boolean> | (() => Promise<boolean>)) {
|
99
|
+
const wasOk = await ok;
|
100
|
+
this.info(`${message}: ${wasOk}`);
|
101
|
+
}
|
102
|
+
|
103
|
+
start(label: string) {
|
104
|
+
this.info(label);
|
105
|
+
}
|
106
|
+
|
107
|
+
showValue(label: string, value: any) {
|
108
|
+
this.info(`${label}: ${value}`);
|
109
|
+
}
|
110
|
+
|
111
|
+
error(msg: string, ...args: any[]) {
|
112
|
+
this.emitLog({
|
113
|
+
message: format(msg, args),
|
114
|
+
level: 'ERROR',
|
115
|
+
});
|
116
|
+
}
|
117
|
+
|
118
|
+
warn(msg: string, ...args: any[]) {
|
119
|
+
this.emitLog({
|
120
|
+
message: format(msg, args),
|
121
|
+
level: 'WARN',
|
122
|
+
});
|
123
|
+
}
|
124
|
+
|
125
|
+
info(msg: string, ...args: any[]) {
|
126
|
+
this.emitLog({
|
127
|
+
message: format(msg, args),
|
128
|
+
level: 'INFO',
|
129
|
+
});
|
130
|
+
}
|
131
|
+
|
132
|
+
debug(msg: string, ...args: any[]) {
|
133
|
+
this.emitLog({
|
134
|
+
message: format(msg, args),
|
135
|
+
level: 'DEBUG',
|
136
|
+
});
|
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
|
+
}
|
@@ -0,0 +1,97 @@
|
|
1
|
+
/**
|
2
|
+
* Copyright 2023 Kapeta Inc.
|
3
|
+
* SPDX-License-Identifier: BUSL-1.1
|
4
|
+
*/
|
5
|
+
|
6
|
+
import Path from 'path';
|
7
|
+
import FSExtra from 'fs-extra';
|
8
|
+
import { definitionsManager } from './definitionsManager';
|
9
|
+
import { cacheManager } from './cacheManager';
|
10
|
+
import request from 'request';
|
11
|
+
import { DefinitionInfo } from '@kapeta/local-cluster-config';
|
12
|
+
|
13
|
+
const PROVIDER_FILE_BASE = 'https://providers.kapeta.com/files';
|
14
|
+
|
15
|
+
class ProviderManager {
|
16
|
+
async getWebProviders(): Promise<DefinitionInfo[]> {
|
17
|
+
const providers = await definitionsManager.getProviderDefinitions();
|
18
|
+
return providers.filter((providerDefinition) => providerDefinition.hasWeb);
|
19
|
+
}
|
20
|
+
|
21
|
+
async getProviderWebJS(handle: string, name: string, version: string, sourceMap: boolean = false) {
|
22
|
+
const fullName = `${handle}/${name}`;
|
23
|
+
const id = `${handle}/${name}/${version}/web.js${sourceMap ? '.map' : ''}`;
|
24
|
+
|
25
|
+
const cacheKey = `provider:web:${id}`;
|
26
|
+
|
27
|
+
const file = cacheManager.get<string>(cacheKey);
|
28
|
+
if (file && (await FSExtra.pathExists(file))) {
|
29
|
+
return FSExtra.readFile(file, 'utf8');
|
30
|
+
}
|
31
|
+
|
32
|
+
const providers = await this.getWebProviders();
|
33
|
+
const installedProvider = providers.find((providerDefinition) => {
|
34
|
+
return providerDefinition.definition.metadata.name === fullName && providerDefinition.version === version;
|
35
|
+
});
|
36
|
+
|
37
|
+
if (installedProvider) {
|
38
|
+
//Check locally installed providers
|
39
|
+
const path = Path.join(installedProvider.path, 'web', handle, `${name}.js${sourceMap ? '.map' : ''}`);
|
40
|
+
if (await FSExtra.pathExists(path)) {
|
41
|
+
cacheManager.set(cacheKey, path, 24 * 60 * 60 * 1000);
|
42
|
+
return FSExtra.readFile(path);
|
43
|
+
}
|
44
|
+
}
|
45
|
+
|
46
|
+
if (version === 'local') {
|
47
|
+
return null;
|
48
|
+
}
|
49
|
+
|
50
|
+
const url = `${PROVIDER_FILE_BASE}/${id}`;
|
51
|
+
return new Promise((resolve, reject) => {
|
52
|
+
console.log('Loading provider from %s', url);
|
53
|
+
request.get(url, (error, response, body) => {
|
54
|
+
if (error) {
|
55
|
+
reject(error);
|
56
|
+
return;
|
57
|
+
}
|
58
|
+
if (response.statusCode === 404) {
|
59
|
+
resolve(null);
|
60
|
+
return;
|
61
|
+
}
|
62
|
+
|
63
|
+
if (response.statusCode !== 200) {
|
64
|
+
reject(new Error(`Failed to load provider from ${url}: ${body}`));
|
65
|
+
return;
|
66
|
+
}
|
67
|
+
|
68
|
+
resolve(body);
|
69
|
+
});
|
70
|
+
});
|
71
|
+
}
|
72
|
+
}
|
73
|
+
|
74
|
+
definitionsManager
|
75
|
+
.getProviderDefinitions()
|
76
|
+
.then((providerDefinitions) => {
|
77
|
+
if (providerDefinitions.length > 0) {
|
78
|
+
console.log('## Loaded the following providers ##');
|
79
|
+
providerDefinitions.forEach((providerDefinition) => {
|
80
|
+
console.log(
|
81
|
+
' - %s[%s:%s]',
|
82
|
+
providerDefinition.definition.kind,
|
83
|
+
providerDefinition.definition.metadata.name,
|
84
|
+
providerDefinition.version
|
85
|
+
);
|
86
|
+
console.log(' from %s', providerDefinition.path);
|
87
|
+
});
|
88
|
+
console.log('##');
|
89
|
+
} else {
|
90
|
+
console.log('## No providers found ##');
|
91
|
+
}
|
92
|
+
})
|
93
|
+
.catch((e) => {
|
94
|
+
console.error('Failed to load providers', e);
|
95
|
+
});
|
96
|
+
|
97
|
+
export const providerManager = new ProviderManager();
|
@@ -0,0 +1,51 @@
|
|
1
|
+
/**
|
2
|
+
* Copyright 2023 Kapeta Inc.
|
3
|
+
* SPDX-License-Identifier: BUSL-1.1
|
4
|
+
*/
|
5
|
+
|
6
|
+
import Router from 'express-promise-router';
|
7
|
+
import { providerManager } from '../providerManager';
|
8
|
+
|
9
|
+
import { corsHandler } from '../middleware/cors';
|
10
|
+
import { Request, Response } from 'express';
|
11
|
+
|
12
|
+
const router = Router();
|
13
|
+
|
14
|
+
router.use('/', corsHandler);
|
15
|
+
|
16
|
+
router.get('/', async (req: Request, res: Response) => {
|
17
|
+
const result = await providerManager.getWebProviders();
|
18
|
+
|
19
|
+
res.send(result);
|
20
|
+
});
|
21
|
+
|
22
|
+
router.get('/asset/:handle/:name/:version/web.js', async (req: Request, res: Response) => {
|
23
|
+
const { handle, name, version } = req.params;
|
24
|
+
let result = await providerManager.getProviderWebJS(handle, name, version);
|
25
|
+
|
26
|
+
if (!result) {
|
27
|
+
res.status(404).send('');
|
28
|
+
} else {
|
29
|
+
if (version !== 'local') {
|
30
|
+
res.setHeader('Cache-Control', 'max-age=31536000, immutable');
|
31
|
+
}
|
32
|
+
res.send(result.toString().replace(`${name}.js.map`, 'web.js.map'));
|
33
|
+
}
|
34
|
+
});
|
35
|
+
|
36
|
+
router.get('/asset/:handle/:name/:version/web.js.map', async (req: Request, res: Response) => {
|
37
|
+
const { handle, name, version } = req.params;
|
38
|
+
const result = await providerManager.getProviderWebJS(handle, name, version, true);
|
39
|
+
|
40
|
+
if (!result) {
|
41
|
+
res.status(404).send('');
|
42
|
+
} else {
|
43
|
+
// Only cache successful requests
|
44
|
+
if (version !== 'local') {
|
45
|
+
res.setHeader('Cache-Control', 'max-age=31536000, immutable');
|
46
|
+
}
|
47
|
+
res.send(result);
|
48
|
+
}
|
49
|
+
});
|
50
|
+
|
51
|
+
export default router;
|