@kapeta/local-cluster-service 0.32.0 → 0.32.2
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/src/RepositoryWatcher.js +30 -6
- package/dist/cjs/src/assetManager.js +5 -2
- package/dist/cjs/src/containerManager.d.ts +2 -0
- package/dist/cjs/src/containerManager.js +21 -0
- package/dist/cjs/src/definitionsManager.js +2 -2
- package/dist/cjs/src/instanceManager.js +2 -2
- package/dist/cjs/src/operatorManager.js +1 -2
- package/dist/cjs/src/repositoryManager.js +1 -1
- package/dist/esm/src/RepositoryWatcher.js +30 -6
- package/dist/esm/src/assetManager.js +5 -2
- package/dist/esm/src/containerManager.d.ts +2 -0
- package/dist/esm/src/containerManager.js +21 -0
- package/dist/esm/src/definitionsManager.js +2 -2
- package/dist/esm/src/instanceManager.js +2 -2
- package/dist/esm/src/operatorManager.js +1 -2
- package/dist/esm/src/repositoryManager.js +1 -1
- package/package.json +1 -1
- package/src/RepositoryWatcher.ts +34 -6
- package/src/assetManager.ts +5 -1
- package/src/containerManager.ts +30 -0
- package/src/definitionsManager.ts +2 -2
- package/src/instanceManager.ts +2 -2
- package/src/operatorManager.ts +1 -3
- package/src/repositoryManager.ts +1 -1
package/CHANGELOG.md
CHANGED
@@ -1,3 +1,17 @@
|
|
1
|
+
## [0.32.2](https://github.com/kapetacom/local-cluster-service/compare/v0.32.1...v0.32.2) (2023-12-25)
|
2
|
+
|
3
|
+
|
4
|
+
### Bug Fixes
|
5
|
+
|
6
|
+
* Use volumes instead of host mounts for faster speeds ([#108](https://github.com/kapetacom/local-cluster-service/issues/108)) ([eac48ea](https://github.com/kapetacom/local-cluster-service/commit/eac48ea767dfc8773a7d0990106fe1f3f29bfec4))
|
7
|
+
|
8
|
+
## [0.32.1](https://github.com/kapetacom/local-cluster-service/compare/v0.32.0...v0.32.1) (2023-12-23)
|
9
|
+
|
10
|
+
|
11
|
+
### Bug Fixes
|
12
|
+
|
13
|
+
* Several smaller fixes ([4036345](https://github.com/kapetacom/local-cluster-service/commit/40363457597d36c97481e94e646a6b45d113bb09))
|
14
|
+
|
1
15
|
# [0.32.0](https://github.com/kapetacom/local-cluster-service/compare/v0.31.0...v0.32.0) (2023-12-20)
|
2
16
|
|
3
17
|
|
@@ -20,6 +20,24 @@ const cacheManager_1 = require("./cacheManager");
|
|
20
20
|
const node_events_1 = require("node:events");
|
21
21
|
const assetManager_1 = require("./assetManager");
|
22
22
|
const KAPETA_YML_RX = /^kapeta.ya?ml$/;
|
23
|
+
let definitions;
|
24
|
+
let definitionTimeout;
|
25
|
+
function getDefinitionsDebounced() {
|
26
|
+
if (definitionTimeout) {
|
27
|
+
clearTimeout(definitionTimeout);
|
28
|
+
definitionTimeout = undefined;
|
29
|
+
}
|
30
|
+
if (!definitions) {
|
31
|
+
definitions = local_cluster_config_1.default.getDefinitions();
|
32
|
+
}
|
33
|
+
else {
|
34
|
+
definitionTimeout = setTimeout(() => {
|
35
|
+
definitionTimeout = undefined;
|
36
|
+
definitions = undefined;
|
37
|
+
}, 500);
|
38
|
+
}
|
39
|
+
return definitions;
|
40
|
+
}
|
23
41
|
class RepositoryWatcher extends node_events_1.EventEmitter {
|
24
42
|
watcher;
|
25
43
|
disabled = false;
|
@@ -186,7 +204,7 @@ class RepositoryWatcher extends node_events_1.EventEmitter {
|
|
186
204
|
}
|
187
205
|
async checkForChange(assetIdentity, sourceOfChange) {
|
188
206
|
const ymlPath = node_path_1.default.join(this.getRepositoryPath(assetIdentity), 'kapeta.yml');
|
189
|
-
const newDefinitions =
|
207
|
+
const newDefinitions = getDefinitionsDebounced();
|
190
208
|
const newDefinition = newDefinitions.find((d) => d.ymlPath === ymlPath);
|
191
209
|
let currentDefinition = this.allDefinitions.find((d) => d.ymlPath === ymlPath);
|
192
210
|
const ymlExists = await this.exists(ymlPath);
|
@@ -265,11 +283,17 @@ class RepositoryWatcher extends node_events_1.EventEmitter {
|
|
265
283
|
}
|
266
284
|
catch (e) { }
|
267
285
|
if (symbolicLink) {
|
268
|
-
|
269
|
-
|
270
|
-
|
271
|
-
|
272
|
-
|
286
|
+
try {
|
287
|
+
const realPath = node_path_1.default.join(await fs_extra_1.default.realpath(path), 'kapeta.yml');
|
288
|
+
if (await this.exists(realPath)) {
|
289
|
+
//console.log('Watching symlink target %s => %s', path, realPath);
|
290
|
+
this.watcher?.add(realPath);
|
291
|
+
this.symbolicLinks[path] = realPath;
|
292
|
+
}
|
293
|
+
}
|
294
|
+
catch (e) {
|
295
|
+
// Remove the symlink - it's broken
|
296
|
+
await fs_extra_1.default.remove(path);
|
273
297
|
}
|
274
298
|
}
|
275
299
|
}
|
@@ -24,10 +24,13 @@ const node_os_1 = __importDefault(require("node:os"));
|
|
24
24
|
const CACHE_TTL = 60 * 60 * 1000; // 1 hour
|
25
25
|
const UPGRADE_CHECK_INTERVAL = 10 * 60 * 1000; // 10 minutes
|
26
26
|
const toKey = (ref) => `assetManager:asset:${ref}`;
|
27
|
+
function filterExists(asset) {
|
28
|
+
return fs_extra_1.default.existsSync(asset.path);
|
29
|
+
}
|
27
30
|
function enrichAsset(asset) {
|
28
31
|
return {
|
29
32
|
ref: `kapeta://${asset.definition.metadata.name}:${asset.version}`,
|
30
|
-
editable: asset.version === 'local',
|
33
|
+
editable: asset.version === 'local', //Only local versions are editable
|
31
34
|
exists: true,
|
32
35
|
version: asset.version,
|
33
36
|
kind: asset.definition.kind,
|
@@ -81,7 +84,7 @@ class AssetManager {
|
|
81
84
|
assetKinds.push('core/plan');
|
82
85
|
}
|
83
86
|
const assets = await definitionsManager_1.definitionsManager.getDefinitions(assetKinds);
|
84
|
-
return assets.map(enrichAsset);
|
87
|
+
return assets.filter(filterExists).map(enrichAsset);
|
85
88
|
}
|
86
89
|
async getPlans() {
|
87
90
|
return this.getAssets(['core/plan']);
|
@@ -22,6 +22,7 @@ export interface DockerMounts {
|
|
22
22
|
Type: string;
|
23
23
|
ReadOnly: boolean;
|
24
24
|
Consistency: string;
|
25
|
+
Labels?: StringMap;
|
25
26
|
}
|
26
27
|
export type DockerContainerStatus = 'created' | 'running' | 'paused' | 'restarting' | 'removing' | 'exited' | 'dead';
|
27
28
|
export type DockerContainerHealth = 'starting' | 'healthy' | 'unhealthy' | 'none';
|
@@ -48,6 +49,7 @@ declare class ContainerManager {
|
|
48
49
|
isAlive(): boolean;
|
49
50
|
getMountPoint(systemId: string, ref: string, mountName: string): string;
|
50
51
|
createMounts(systemId: string, kind: string, mountOpts: StringMap | null | undefined): Promise<StringMap>;
|
52
|
+
createVolumes(systemId: string, kind: string, mountOpts: StringMap | null | undefined): Promise<DockerMounts[]>;
|
51
53
|
ping(): Promise<void>;
|
52
54
|
docker(): Docker;
|
53
55
|
getContainerByName(containerName: string): Promise<ContainerInfo | undefined>;
|
@@ -165,6 +165,27 @@ class ContainerManager {
|
|
165
165
|
}
|
166
166
|
return mounts;
|
167
167
|
}
|
168
|
+
async createVolumes(systemId, kind, mountOpts) {
|
169
|
+
const Mounts = [];
|
170
|
+
if (mountOpts) {
|
171
|
+
const mountOptList = Object.entries(mountOpts);
|
172
|
+
for (const [mountName, containerPath] of mountOptList) {
|
173
|
+
const volumeName = `${systemId}_${kind}_${mountName}`.replace(/[^a-z0-9]/gi, '_');
|
174
|
+
Mounts.push({
|
175
|
+
Target: containerPath,
|
176
|
+
Source: volumeName,
|
177
|
+
Type: 'volume',
|
178
|
+
ReadOnly: false,
|
179
|
+
Consistency: 'consistent',
|
180
|
+
Labels: {
|
181
|
+
[exports.COMPOSE_LABEL_PROJECT]: systemId.replace(/[^a-z0-9]/gi, '_'),
|
182
|
+
[exports.COMPOSE_LABEL_SERVICE]: kind.replace(/[^a-z0-9]/gi, '_'),
|
183
|
+
},
|
184
|
+
});
|
185
|
+
}
|
186
|
+
}
|
187
|
+
return Mounts;
|
188
|
+
}
|
168
189
|
async ping() {
|
169
190
|
try {
|
170
191
|
const pingResult = await this.docker().ping();
|
@@ -131,9 +131,9 @@ class DefinitionsManager {
|
|
131
131
|
const definitions = await this.getDefinitions();
|
132
132
|
return definitions.find((d) => {
|
133
133
|
if (!uri.version) {
|
134
|
-
return d.definition.metadata.name === uri.fullName;
|
134
|
+
return d.definition.metadata.name.toLowerCase() === uri.fullName.toLowerCase();
|
135
135
|
}
|
136
|
-
return (0, nodejs_utils_1.parseKapetaUri)(`${d.definition.metadata.name}:${d.version}`).
|
136
|
+
return (0, nodejs_utils_1.parseKapetaUri)(`${d.definition.metadata.name}:${d.version}`).equals(uri);
|
137
137
|
});
|
138
138
|
}
|
139
139
|
async getLatestDefinition(name) {
|
@@ -25,7 +25,7 @@ const nodejs_utils_1 = require("@kapeta/nodejs-utils");
|
|
25
25
|
const definitionsManager_1 = require("./definitionsManager");
|
26
26
|
const taskManager_1 = require("./taskManager");
|
27
27
|
const CHECK_INTERVAL = 5000;
|
28
|
-
const DEFAULT_HEALTH_PORT_TYPE = '
|
28
|
+
const DEFAULT_HEALTH_PORT_TYPE = 'http';
|
29
29
|
const MIN_TIME_RUNNING = 30000; //If something didnt run for more than 30 secs - it failed
|
30
30
|
class InstanceManager {
|
31
31
|
_interval = undefined;
|
@@ -198,7 +198,7 @@ class InstanceManager {
|
|
198
198
|
}
|
199
199
|
getHealthUrl(info, address) {
|
200
200
|
let healthUrl = null;
|
201
|
-
let health = info.health;
|
201
|
+
let health = info.health ?? '/.kapeta/health';
|
202
202
|
if (health) {
|
203
203
|
if (health.startsWith('/')) {
|
204
204
|
health = health.substring(1);
|
@@ -146,7 +146,6 @@ class OperatorManager {
|
|
146
146
|
hostPort,
|
147
147
|
};
|
148
148
|
}
|
149
|
-
const mounts = await containerManager_1.containerManager.createMounts(systemId, resourceType, operatorData.mounts);
|
150
149
|
const nameParts = [systemId, resourceType.toLowerCase(), version];
|
151
150
|
const containerName = `kapeta-resource-${(0, md5_1.default)(nameParts.join('_'))}`;
|
152
151
|
const PortBindings = {};
|
@@ -170,7 +169,7 @@ class OperatorManager {
|
|
170
169
|
];
|
171
170
|
Labels[containerManager_1.CONTAINER_LABEL_PORT_PREFIX + portInfo.hostPort] = portInfo.type;
|
172
171
|
});
|
173
|
-
const Mounts = containerManager_1.containerManager.
|
172
|
+
const Mounts = await containerManager_1.containerManager.createVolumes(systemId, resourceType, operatorData.mounts);
|
174
173
|
lodash_1.default.forEach(operatorData.env, (value, name) => {
|
175
174
|
Env.push(name + '=' + value);
|
176
175
|
});
|
@@ -183,7 +183,7 @@ class RepositoryManager extends node_events_1.EventEmitter {
|
|
183
183
|
if (await definitionsManager_1.definitionsManager.exists(ref)) {
|
184
184
|
return;
|
185
185
|
}
|
186
|
-
throw new Error(`Failed to
|
186
|
+
throw new Error(`Failed to find asset after installation: ${ref}. Please try again`);
|
187
187
|
};
|
188
188
|
};
|
189
189
|
const tasks = [];
|
@@ -20,6 +20,24 @@ const cacheManager_1 = require("./cacheManager");
|
|
20
20
|
const node_events_1 = require("node:events");
|
21
21
|
const assetManager_1 = require("./assetManager");
|
22
22
|
const KAPETA_YML_RX = /^kapeta.ya?ml$/;
|
23
|
+
let definitions;
|
24
|
+
let definitionTimeout;
|
25
|
+
function getDefinitionsDebounced() {
|
26
|
+
if (definitionTimeout) {
|
27
|
+
clearTimeout(definitionTimeout);
|
28
|
+
definitionTimeout = undefined;
|
29
|
+
}
|
30
|
+
if (!definitions) {
|
31
|
+
definitions = local_cluster_config_1.default.getDefinitions();
|
32
|
+
}
|
33
|
+
else {
|
34
|
+
definitionTimeout = setTimeout(() => {
|
35
|
+
definitionTimeout = undefined;
|
36
|
+
definitions = undefined;
|
37
|
+
}, 500);
|
38
|
+
}
|
39
|
+
return definitions;
|
40
|
+
}
|
23
41
|
class RepositoryWatcher extends node_events_1.EventEmitter {
|
24
42
|
watcher;
|
25
43
|
disabled = false;
|
@@ -186,7 +204,7 @@ class RepositoryWatcher extends node_events_1.EventEmitter {
|
|
186
204
|
}
|
187
205
|
async checkForChange(assetIdentity, sourceOfChange) {
|
188
206
|
const ymlPath = node_path_1.default.join(this.getRepositoryPath(assetIdentity), 'kapeta.yml');
|
189
|
-
const newDefinitions =
|
207
|
+
const newDefinitions = getDefinitionsDebounced();
|
190
208
|
const newDefinition = newDefinitions.find((d) => d.ymlPath === ymlPath);
|
191
209
|
let currentDefinition = this.allDefinitions.find((d) => d.ymlPath === ymlPath);
|
192
210
|
const ymlExists = await this.exists(ymlPath);
|
@@ -265,11 +283,17 @@ class RepositoryWatcher extends node_events_1.EventEmitter {
|
|
265
283
|
}
|
266
284
|
catch (e) { }
|
267
285
|
if (symbolicLink) {
|
268
|
-
|
269
|
-
|
270
|
-
|
271
|
-
|
272
|
-
|
286
|
+
try {
|
287
|
+
const realPath = node_path_1.default.join(await fs_extra_1.default.realpath(path), 'kapeta.yml');
|
288
|
+
if (await this.exists(realPath)) {
|
289
|
+
//console.log('Watching symlink target %s => %s', path, realPath);
|
290
|
+
this.watcher?.add(realPath);
|
291
|
+
this.symbolicLinks[path] = realPath;
|
292
|
+
}
|
293
|
+
}
|
294
|
+
catch (e) {
|
295
|
+
// Remove the symlink - it's broken
|
296
|
+
await fs_extra_1.default.remove(path);
|
273
297
|
}
|
274
298
|
}
|
275
299
|
}
|
@@ -24,10 +24,13 @@ const node_os_1 = __importDefault(require("node:os"));
|
|
24
24
|
const CACHE_TTL = 60 * 60 * 1000; // 1 hour
|
25
25
|
const UPGRADE_CHECK_INTERVAL = 10 * 60 * 1000; // 10 minutes
|
26
26
|
const toKey = (ref) => `assetManager:asset:${ref}`;
|
27
|
+
function filterExists(asset) {
|
28
|
+
return fs_extra_1.default.existsSync(asset.path);
|
29
|
+
}
|
27
30
|
function enrichAsset(asset) {
|
28
31
|
return {
|
29
32
|
ref: `kapeta://${asset.definition.metadata.name}:${asset.version}`,
|
30
|
-
editable: asset.version === 'local',
|
33
|
+
editable: asset.version === 'local', //Only local versions are editable
|
31
34
|
exists: true,
|
32
35
|
version: asset.version,
|
33
36
|
kind: asset.definition.kind,
|
@@ -81,7 +84,7 @@ class AssetManager {
|
|
81
84
|
assetKinds.push('core/plan');
|
82
85
|
}
|
83
86
|
const assets = await definitionsManager_1.definitionsManager.getDefinitions(assetKinds);
|
84
|
-
return assets.map(enrichAsset);
|
87
|
+
return assets.filter(filterExists).map(enrichAsset);
|
85
88
|
}
|
86
89
|
async getPlans() {
|
87
90
|
return this.getAssets(['core/plan']);
|
@@ -22,6 +22,7 @@ export interface DockerMounts {
|
|
22
22
|
Type: string;
|
23
23
|
ReadOnly: boolean;
|
24
24
|
Consistency: string;
|
25
|
+
Labels?: StringMap;
|
25
26
|
}
|
26
27
|
export type DockerContainerStatus = 'created' | 'running' | 'paused' | 'restarting' | 'removing' | 'exited' | 'dead';
|
27
28
|
export type DockerContainerHealth = 'starting' | 'healthy' | 'unhealthy' | 'none';
|
@@ -48,6 +49,7 @@ declare class ContainerManager {
|
|
48
49
|
isAlive(): boolean;
|
49
50
|
getMountPoint(systemId: string, ref: string, mountName: string): string;
|
50
51
|
createMounts(systemId: string, kind: string, mountOpts: StringMap | null | undefined): Promise<StringMap>;
|
52
|
+
createVolumes(systemId: string, kind: string, mountOpts: StringMap | null | undefined): Promise<DockerMounts[]>;
|
51
53
|
ping(): Promise<void>;
|
52
54
|
docker(): Docker;
|
53
55
|
getContainerByName(containerName: string): Promise<ContainerInfo | undefined>;
|
@@ -165,6 +165,27 @@ class ContainerManager {
|
|
165
165
|
}
|
166
166
|
return mounts;
|
167
167
|
}
|
168
|
+
async createVolumes(systemId, kind, mountOpts) {
|
169
|
+
const Mounts = [];
|
170
|
+
if (mountOpts) {
|
171
|
+
const mountOptList = Object.entries(mountOpts);
|
172
|
+
for (const [mountName, containerPath] of mountOptList) {
|
173
|
+
const volumeName = `${systemId}_${kind}_${mountName}`.replace(/[^a-z0-9]/gi, '_');
|
174
|
+
Mounts.push({
|
175
|
+
Target: containerPath,
|
176
|
+
Source: volumeName,
|
177
|
+
Type: 'volume',
|
178
|
+
ReadOnly: false,
|
179
|
+
Consistency: 'consistent',
|
180
|
+
Labels: {
|
181
|
+
[exports.COMPOSE_LABEL_PROJECT]: systemId.replace(/[^a-z0-9]/gi, '_'),
|
182
|
+
[exports.COMPOSE_LABEL_SERVICE]: kind.replace(/[^a-z0-9]/gi, '_'),
|
183
|
+
},
|
184
|
+
});
|
185
|
+
}
|
186
|
+
}
|
187
|
+
return Mounts;
|
188
|
+
}
|
168
189
|
async ping() {
|
169
190
|
try {
|
170
191
|
const pingResult = await this.docker().ping();
|
@@ -131,9 +131,9 @@ class DefinitionsManager {
|
|
131
131
|
const definitions = await this.getDefinitions();
|
132
132
|
return definitions.find((d) => {
|
133
133
|
if (!uri.version) {
|
134
|
-
return d.definition.metadata.name === uri.fullName;
|
134
|
+
return d.definition.metadata.name.toLowerCase() === uri.fullName.toLowerCase();
|
135
135
|
}
|
136
|
-
return (0, nodejs_utils_1.parseKapetaUri)(`${d.definition.metadata.name}:${d.version}`).
|
136
|
+
return (0, nodejs_utils_1.parseKapetaUri)(`${d.definition.metadata.name}:${d.version}`).equals(uri);
|
137
137
|
});
|
138
138
|
}
|
139
139
|
async getLatestDefinition(name) {
|
@@ -25,7 +25,7 @@ const nodejs_utils_1 = require("@kapeta/nodejs-utils");
|
|
25
25
|
const definitionsManager_1 = require("./definitionsManager");
|
26
26
|
const taskManager_1 = require("./taskManager");
|
27
27
|
const CHECK_INTERVAL = 5000;
|
28
|
-
const DEFAULT_HEALTH_PORT_TYPE = '
|
28
|
+
const DEFAULT_HEALTH_PORT_TYPE = 'http';
|
29
29
|
const MIN_TIME_RUNNING = 30000; //If something didnt run for more than 30 secs - it failed
|
30
30
|
class InstanceManager {
|
31
31
|
_interval = undefined;
|
@@ -198,7 +198,7 @@ class InstanceManager {
|
|
198
198
|
}
|
199
199
|
getHealthUrl(info, address) {
|
200
200
|
let healthUrl = null;
|
201
|
-
let health = info.health;
|
201
|
+
let health = info.health ?? '/.kapeta/health';
|
202
202
|
if (health) {
|
203
203
|
if (health.startsWith('/')) {
|
204
204
|
health = health.substring(1);
|
@@ -146,7 +146,6 @@ class OperatorManager {
|
|
146
146
|
hostPort,
|
147
147
|
};
|
148
148
|
}
|
149
|
-
const mounts = await containerManager_1.containerManager.createMounts(systemId, resourceType, operatorData.mounts);
|
150
149
|
const nameParts = [systemId, resourceType.toLowerCase(), version];
|
151
150
|
const containerName = `kapeta-resource-${(0, md5_1.default)(nameParts.join('_'))}`;
|
152
151
|
const PortBindings = {};
|
@@ -170,7 +169,7 @@ class OperatorManager {
|
|
170
169
|
];
|
171
170
|
Labels[containerManager_1.CONTAINER_LABEL_PORT_PREFIX + portInfo.hostPort] = portInfo.type;
|
172
171
|
});
|
173
|
-
const Mounts = containerManager_1.containerManager.
|
172
|
+
const Mounts = await containerManager_1.containerManager.createVolumes(systemId, resourceType, operatorData.mounts);
|
174
173
|
lodash_1.default.forEach(operatorData.env, (value, name) => {
|
175
174
|
Env.push(name + '=' + value);
|
176
175
|
});
|
@@ -183,7 +183,7 @@ class RepositoryManager extends node_events_1.EventEmitter {
|
|
183
183
|
if (await definitionsManager_1.definitionsManager.exists(ref)) {
|
184
184
|
return;
|
185
185
|
}
|
186
|
-
throw new Error(`Failed to
|
186
|
+
throw new Error(`Failed to find asset after installation: ${ref}. Please try again`);
|
187
187
|
};
|
188
188
|
};
|
189
189
|
const tasks = [];
|
package/package.json
CHANGED
package/src/RepositoryWatcher.ts
CHANGED
@@ -15,6 +15,7 @@ import { SourceOfChange, WatchEventName } from './types';
|
|
15
15
|
import { cacheManager } from './cacheManager';
|
16
16
|
import { EventEmitter } from 'node:events';
|
17
17
|
import { assetManager } from './assetManager';
|
18
|
+
import { definitionsManager } from './definitionsManager';
|
18
19
|
|
19
20
|
interface AssetIdentity {
|
20
21
|
handle: string;
|
@@ -22,6 +23,27 @@ interface AssetIdentity {
|
|
22
23
|
version: string;
|
23
24
|
}
|
24
25
|
const KAPETA_YML_RX = /^kapeta.ya?ml$/;
|
26
|
+
|
27
|
+
let definitions: DefinitionInfo[] | undefined;
|
28
|
+
let definitionTimeout: NodeJS.Timeout | undefined;
|
29
|
+
|
30
|
+
function getDefinitionsDebounced() {
|
31
|
+
if (definitionTimeout) {
|
32
|
+
clearTimeout(definitionTimeout);
|
33
|
+
definitionTimeout = undefined;
|
34
|
+
}
|
35
|
+
if (!definitions) {
|
36
|
+
definitions = ClusterConfiguration.getDefinitions();
|
37
|
+
} else {
|
38
|
+
definitionTimeout = setTimeout(() => {
|
39
|
+
definitionTimeout = undefined;
|
40
|
+
definitions = undefined;
|
41
|
+
}, 500);
|
42
|
+
}
|
43
|
+
|
44
|
+
return definitions;
|
45
|
+
}
|
46
|
+
|
25
47
|
export class RepositoryWatcher extends EventEmitter {
|
26
48
|
private watcher?: FSWatcher;
|
27
49
|
private disabled: boolean = false;
|
@@ -208,7 +230,8 @@ export class RepositoryWatcher extends EventEmitter {
|
|
208
230
|
|
209
231
|
private async checkForChange(assetIdentity: AssetIdentity, sourceOfChange: SourceOfChange) {
|
210
232
|
const ymlPath = Path.join(this.getRepositoryPath(assetIdentity), 'kapeta.yml');
|
211
|
-
const newDefinitions =
|
233
|
+
const newDefinitions = getDefinitionsDebounced();
|
234
|
+
|
212
235
|
const newDefinition = newDefinitions.find((d) => d.ymlPath === ymlPath);
|
213
236
|
let currentDefinition = this.allDefinitions.find((d) => d.ymlPath === ymlPath);
|
214
237
|
const ymlExists = await this.exists(ymlPath);
|
@@ -290,11 +313,16 @@ export class RepositoryWatcher extends EventEmitter {
|
|
290
313
|
} catch (e) {}
|
291
314
|
|
292
315
|
if (symbolicLink) {
|
293
|
-
|
294
|
-
|
295
|
-
|
296
|
-
|
297
|
-
|
316
|
+
try {
|
317
|
+
const realPath = Path.join(await FS.realpath(path), 'kapeta.yml');
|
318
|
+
if (await this.exists(realPath)) {
|
319
|
+
//console.log('Watching symlink target %s => %s', path, realPath);
|
320
|
+
this.watcher?.add(realPath);
|
321
|
+
this.symbolicLinks[path] = realPath;
|
322
|
+
}
|
323
|
+
} catch (e) {
|
324
|
+
// Remove the symlink - it's broken
|
325
|
+
await FS.remove(path);
|
298
326
|
}
|
299
327
|
}
|
300
328
|
} catch (e) {
|
package/src/assetManager.ts
CHANGED
@@ -36,6 +36,10 @@ export interface EnrichedAsset {
|
|
36
36
|
ymlPath: string;
|
37
37
|
}
|
38
38
|
|
39
|
+
function filterExists(asset: DefinitionInfo): boolean {
|
40
|
+
return FS.existsSync(asset.path);
|
41
|
+
}
|
42
|
+
|
39
43
|
function enrichAsset(asset: DefinitionInfo): EnrichedAsset {
|
40
44
|
return {
|
41
45
|
ref: `kapeta://${asset.definition.metadata.name}:${asset.version}`,
|
@@ -101,7 +105,7 @@ class AssetManager {
|
|
101
105
|
|
102
106
|
const assets = await definitionsManager.getDefinitions(assetKinds);
|
103
107
|
|
104
|
-
return assets.map(enrichAsset);
|
108
|
+
return assets.filter(filterExists).map(enrichAsset);
|
105
109
|
}
|
106
110
|
|
107
111
|
async getPlans(): Promise<EnrichedAsset[]> {
|
package/src/containerManager.ts
CHANGED
@@ -37,6 +37,7 @@ export interface DockerMounts {
|
|
37
37
|
Type: string;
|
38
38
|
ReadOnly: boolean;
|
39
39
|
Consistency: string;
|
40
|
+
Labels?: StringMap;
|
40
41
|
}
|
41
42
|
|
42
43
|
interface JSONProgress {
|
@@ -249,6 +250,35 @@ class ContainerManager {
|
|
249
250
|
return mounts;
|
250
251
|
}
|
251
252
|
|
253
|
+
async createVolumes(
|
254
|
+
systemId: string,
|
255
|
+
kind: string,
|
256
|
+
mountOpts: StringMap | null | undefined
|
257
|
+
): Promise<DockerMounts[]> {
|
258
|
+
const Mounts: DockerMounts[] = [];
|
259
|
+
|
260
|
+
if (mountOpts) {
|
261
|
+
const mountOptList = Object.entries(mountOpts);
|
262
|
+
for (const [mountName, containerPath] of mountOptList) {
|
263
|
+
const volumeName = `${systemId}_${kind}_${mountName}`.replace(/[^a-z0-9]/gi, '_');
|
264
|
+
|
265
|
+
Mounts.push({
|
266
|
+
Target: containerPath,
|
267
|
+
Source: volumeName,
|
268
|
+
Type: 'volume',
|
269
|
+
ReadOnly: false,
|
270
|
+
Consistency: 'consistent',
|
271
|
+
Labels: {
|
272
|
+
[COMPOSE_LABEL_PROJECT]: systemId.replace(/[^a-z0-9]/gi, '_'),
|
273
|
+
[COMPOSE_LABEL_SERVICE]: kind.replace(/[^a-z0-9]/gi, '_'),
|
274
|
+
},
|
275
|
+
});
|
276
|
+
}
|
277
|
+
}
|
278
|
+
|
279
|
+
return Mounts;
|
280
|
+
}
|
281
|
+
|
252
282
|
async ping() {
|
253
283
|
try {
|
254
284
|
const pingResult = await this.docker().ping();
|
@@ -164,9 +164,9 @@ class DefinitionsManager {
|
|
164
164
|
const definitions = await this.getDefinitions();
|
165
165
|
return definitions.find((d) => {
|
166
166
|
if (!uri.version) {
|
167
|
-
return d.definition.metadata.name === uri.fullName;
|
167
|
+
return d.definition.metadata.name.toLowerCase() === uri.fullName.toLowerCase();
|
168
168
|
}
|
169
|
-
return parseKapetaUri(`${d.definition.metadata.name}:${d.version}`).
|
169
|
+
return parseKapetaUri(`${d.definition.metadata.name}:${d.version}`).equals(uri);
|
170
170
|
});
|
171
171
|
}
|
172
172
|
|
package/src/instanceManager.ts
CHANGED
@@ -27,7 +27,7 @@ import { definitionsManager } from './definitionsManager';
|
|
27
27
|
import { Task, taskManager } from './taskManager';
|
28
28
|
|
29
29
|
const CHECK_INTERVAL = 5000;
|
30
|
-
const DEFAULT_HEALTH_PORT_TYPE = '
|
30
|
+
const DEFAULT_HEALTH_PORT_TYPE = 'http';
|
31
31
|
|
32
32
|
const MIN_TIME_RUNNING = 30000; //If something didnt run for more than 30 secs - it failed
|
33
33
|
|
@@ -264,7 +264,7 @@ export class InstanceManager {
|
|
264
264
|
|
265
265
|
private getHealthUrl(info: Omit<InstanceInfo, 'systemId' | 'instanceId'>, address: string) {
|
266
266
|
let healthUrl = null;
|
267
|
-
let health = info.health;
|
267
|
+
let health = info.health ?? '/.kapeta/health';
|
268
268
|
if (health) {
|
269
269
|
if (health.startsWith('/')) {
|
270
270
|
health = health.substring(1);
|
package/src/operatorManager.ts
CHANGED
@@ -201,8 +201,6 @@ class OperatorManager {
|
|
201
201
|
};
|
202
202
|
}
|
203
203
|
|
204
|
-
const mounts = await containerManager.createMounts(systemId, resourceType, operatorData.mounts);
|
205
|
-
|
206
204
|
const nameParts = [systemId, resourceType.toLowerCase(), version];
|
207
205
|
|
208
206
|
const containerName = `kapeta-resource-${md5(nameParts.join('_'))}`;
|
@@ -236,7 +234,7 @@ class OperatorManager {
|
|
236
234
|
Labels[CONTAINER_LABEL_PORT_PREFIX + portInfo.hostPort] = portInfo.type;
|
237
235
|
});
|
238
236
|
|
239
|
-
const Mounts = containerManager.
|
237
|
+
const Mounts = await containerManager.createVolumes(systemId, resourceType, operatorData.mounts);
|
240
238
|
|
241
239
|
_.forEach(operatorData.env, (value, name) => {
|
242
240
|
Env.push(name + '=' + value);
|
package/src/repositoryManager.ts
CHANGED
@@ -209,7 +209,7 @@ class RepositoryManager extends EventEmitter {
|
|
209
209
|
if (await definitionsManager.exists(ref)) {
|
210
210
|
return;
|
211
211
|
}
|
212
|
-
throw new Error(`Failed to
|
212
|
+
throw new Error(`Failed to find asset after installation: ${ref}. Please try again`);
|
213
213
|
};
|
214
214
|
};
|
215
215
|
|