@kapeta/local-cluster-service 0.41.0 → 0.42.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 CHANGED
@@ -1,3 +1,18 @@
1
+ # [0.42.0](https://github.com/kapetacom/local-cluster-service/compare/v0.41.1...v0.42.0) (2024-04-18)
2
+
3
+
4
+ ### Features
5
+
6
+ * Add support for file config values ([#141](https://github.com/kapetacom/local-cluster-service/issues/141)) ([437eb75](https://github.com/kapetacom/local-cluster-service/commit/437eb75a1c5c94979cca4fedae6a2d1756f0043d))
7
+
8
+ ## [0.41.1](https://github.com/kapetacom/local-cluster-service/compare/v0.41.0...v0.41.1) (2024-04-16)
9
+
10
+
11
+ ### Bug Fixes
12
+
13
+ * better missing file handling [CORE-2685] ([1baf002](https://github.com/kapetacom/local-cluster-service/commit/1baf002c18c6acc92a4f6ca1bff37cda9270e487))
14
+ * bump local-cluster-config w/ improved error handling ([f9a945f](https://github.com/kapetacom/local-cluster-service/commit/f9a945f7761e472e45bc55839b40e563c5298719))
15
+
1
16
  # [0.41.0](https://github.com/kapetacom/local-cluster-service/compare/v0.40.6...v0.41.0) (2024-04-12)
2
17
 
3
18
 
package/definitions.d.ts CHANGED
@@ -7,6 +7,10 @@ declare module 'recursive-watch' {
7
7
  export default function watch(path: string, callback: (filename: string) => void): () => void;
8
8
  }
9
9
 
10
+ declare module 'parse-data-uri' {
11
+ export default function parseDataUri(uri: string): { data: Buffer; mediaType: string };
12
+ }
13
+
10
14
  declare module '@kapeta/nodejs-registry-utils' {
11
15
  import { Dependency, Kind } from '@kapeta/schemas';
12
16
 
@@ -25,10 +25,17 @@ const node_os_1 = __importDefault(require("node:os"));
25
25
  const CACHE_TTL = 60 * 60 * 1000; // 1 hour
26
26
  const UPGRADE_CHECK_INTERVAL = 10 * 60 * 1000; // 10 minutes
27
27
  const toKey = (ref) => `assetManager:asset:${ref}`;
28
- function filterExists(asset) {
29
- return fs_extra_1.default.existsSync(asset.path) && fs_extra_1.default.existsSync(asset.ymlPath);
30
- }
31
28
  function enrichAsset(asset) {
29
+ let exists = true;
30
+ let path = asset.path;
31
+ let ymlPath = asset.ymlPath;
32
+ try {
33
+ path = fs_extra_1.default.realpathSync(path);
34
+ ymlPath = fs_extra_1.default.realpathSync(ymlPath);
35
+ }
36
+ catch (e) {
37
+ exists = false;
38
+ }
32
39
  return {
33
40
  ref: `kapeta://${asset.definition.metadata.name}:${asset.version}`,
34
41
  editable: asset.version === 'local', //Only local versions are editable
@@ -36,8 +43,8 @@ function enrichAsset(asset) {
36
43
  version: asset.version,
37
44
  kind: asset.definition.kind,
38
45
  data: asset.definition,
39
- path: fs_extra_1.default.realpathSync(asset.path),
40
- ymlPath: fs_extra_1.default.realpathSync(asset.ymlPath),
46
+ path,
47
+ ymlPath,
41
48
  };
42
49
  }
43
50
  function compareRefs(a, b) {
@@ -86,7 +93,7 @@ class AssetManager {
86
93
  assetKinds.push('core/plan');
87
94
  }
88
95
  const assets = await definitionsManager_1.definitionsManager.getDefinitions(assetKinds);
89
- return assets.filter(filterExists).map(enrichAsset);
96
+ return assets.map(enrichAsset).filter((a) => a.exists);
90
97
  }
91
98
  async getPlans() {
92
99
  return this.getAssets(['core/plan']);
@@ -124,9 +131,14 @@ class AssetManager {
124
131
  throw new Error('Asset not found: ' + ref);
125
132
  }
126
133
  if (definitionInfo) {
127
- const asset = enrichAsset(definitionInfo);
128
- cacheManager_1.cacheManager.set(cacheKey, asset, CACHE_TTL);
129
- return asset;
134
+ try {
135
+ const asset = enrichAsset(definitionInfo);
136
+ cacheManager_1.cacheManager.set(cacheKey, asset, CACHE_TTL);
137
+ return asset;
138
+ }
139
+ catch (e) {
140
+ console.error('Failed to enrich asset', e);
141
+ }
130
142
  }
131
143
  return undefined;
132
144
  }
@@ -15,6 +15,8 @@ const instanceManager_1 = require("../instanceManager");
15
15
  const cors_1 = require("../middleware/cors");
16
16
  const kapeta_1 = require("../middleware/kapeta");
17
17
  const stringBody_1 = require("../middleware/stringBody");
18
+ const assetManager_1 = require("../assetManager");
19
+ const definitionsManager_1 = require("../definitionsManager");
18
20
  const router = (0, express_promise_router_1.default)();
19
21
  router.use('/', cors_1.corsHandler);
20
22
  router.use('/', kapeta_1.kapetaHeaders);
@@ -26,7 +28,7 @@ router.get('/instance', async (req, res) => {
26
28
  try {
27
29
  let config = {};
28
30
  if (req.kapeta.instanceId) {
29
- config = await configManager_1.configManager.getConfigForBlockInstance(req.kapeta.systemId, req.kapeta.instanceId);
31
+ config = await configManager_1.configManager.getConfigForBlockInstance(req.kapeta.systemId, req.kapeta.instanceId, req.kapeta?.environment === 'docker');
30
32
  }
31
33
  else {
32
34
  config = configManager_1.configManager.getConfigForSystem(req.kapeta.systemId);
@@ -49,7 +51,19 @@ router.put('/instance', async (req, res) => {
49
51
  config = {};
50
52
  }
51
53
  if (req.kapeta.instanceId) {
52
- configManager_1.configManager.setConfigForSection(req.kapeta.systemId, req.kapeta.instanceId, config);
54
+ const blockInstance = await assetManager_1.assetManager.getBlockInstance(req.kapeta.systemId, req.kapeta.instanceId);
55
+ if (!blockInstance) {
56
+ throw new Error(`Block instance not found: ${req.kapeta.instanceId} in plan ${req.kapeta.systemId}`);
57
+ }
58
+ const blockDefinition = await definitionsManager_1.definitionsManager.getDefinition(blockInstance.block.ref);
59
+ if (!blockDefinition) {
60
+ throw new Error(`Block definition not found: ${blockInstance.block.ref}`);
61
+ }
62
+ let configEntityList = {};
63
+ if (blockDefinition.definition.spec.configuration) {
64
+ configEntityList = blockDefinition.definition.spec.configuration;
65
+ }
66
+ await configManager_1.configManager.setConfigForBlockInstance(req.kapeta.systemId, req.kapeta.instanceId, configEntityList, config);
53
67
  //Restart the instance if it is running after config change
54
68
  await instanceManager_1.instanceManager.prepareForRestart(req.kapeta.systemId, req.kapeta.instanceId);
55
69
  }
@@ -2,6 +2,8 @@
2
2
  * Copyright 2023 Kapeta Inc.
3
3
  * SPDX-License-Identifier: BUSL-1.1
4
4
  */
5
+ import { EntityList } from '@kapeta/schemas';
6
+ import { StringMap } from './types';
5
7
  export declare const SYSTEM_ID = "$plan";
6
8
  type AnyMap = {
7
9
  [key: string]: any;
@@ -16,8 +18,12 @@ declare class ConfigManager {
16
18
  _forSystem(systemId: string): any;
17
19
  setConfigForSystem(systemId: string, config: AnyMap): void;
18
20
  getConfigForSystem(systemId: string): AnyMap;
19
- getConfigForBlockInstance(systemId: string, instanceId: string): Promise<import("./types").AnyMap>;
21
+ getConfigForBlockInstance(systemId: string, instanceId: string, rewritePaths?: boolean): Promise<import("./types").AnyMap>;
22
+ rewriteFilePaths(configSchema: EntityList, config: AnyMap, toBaseDir?: string): StringMap;
20
23
  setConfigForSection(systemId: string, sectionId: string, config: AnyMap): void;
24
+ getFilesBaseDir(): string;
25
+ private saveFile;
26
+ setConfigForBlockInstance(systemId: string, instanceId: string, configEntityList: EntityList, config: AnyMap): Promise<void>;
21
27
  getConfigForSection(systemId: string, sectionId: string): any;
22
28
  /**
23
29
  * Try to identify the plan and instance in a plan automatically based on the block reference
@@ -3,12 +3,22 @@
3
3
  * Copyright 2023 Kapeta Inc.
4
4
  * SPDX-License-Identifier: BUSL-1.1
5
5
  */
6
+ var __importDefault = (this && this.__importDefault) || function (mod) {
7
+ return (mod && mod.__esModule) ? mod : { "default": mod };
8
+ };
6
9
  Object.defineProperty(exports, "__esModule", { value: true });
7
10
  exports.configManager = exports.SYSTEM_ID = void 0;
8
- const storageService_1 = require("./storageService");
9
11
  const assetManager_1 = require("./assetManager");
12
+ const schemas_1 = require("@kapeta/schemas");
13
+ const storageService_1 = require("./storageService");
10
14
  const nodejs_utils_1 = require("@kapeta/nodejs-utils");
11
15
  const utils_1 = require("./utils/utils");
16
+ const parse_data_uri_1 = __importDefault(require("parse-data-uri"));
17
+ const local_cluster_config_1 = __importDefault(require("@kapeta/local-cluster-config"));
18
+ const path_1 = __importDefault(require("path"));
19
+ const node_uuid_1 = __importDefault(require("node-uuid"));
20
+ const fs_extra_1 = __importDefault(require("fs-extra"));
21
+ const lodash_1 = __importDefault(require("lodash"));
12
22
  exports.SYSTEM_ID = '$plan';
13
23
  class ConfigManager {
14
24
  _config;
@@ -31,14 +41,49 @@ class ConfigManager {
31
41
  systemId = (0, nodejs_utils_1.normalizeKapetaUri)(systemId);
32
42
  return this._forSystem(systemId);
33
43
  }
34
- async getConfigForBlockInstance(systemId, instanceId) {
44
+ async getConfigForBlockInstance(systemId, instanceId, rewritePaths = false) {
35
45
  const blockInstance = await assetManager_1.assetManager.getBlockInstance(systemId, instanceId);
36
46
  const blockAsset = await assetManager_1.assetManager.getAsset(blockInstance.block.ref, true);
37
47
  if (!blockAsset) {
38
48
  throw new Error(`Block definition not found: ${blockInstance.block.ref}`);
39
49
  }
40
50
  const instanceConfig = this.getConfigForSection(systemId, instanceId);
41
- return (0, utils_1.getResolvedConfiguration)(blockAsset.data.spec.configuration, instanceConfig, blockInstance.defaultConfiguration);
51
+ const config = lodash_1.default.cloneDeep((0, utils_1.getResolvedConfiguration)(blockAsset.data.spec.configuration, instanceConfig, blockInstance.defaultConfiguration));
52
+ if (rewritePaths) {
53
+ this.rewriteFilePaths(blockAsset.data.spec.configuration, config);
54
+ }
55
+ return config;
56
+ }
57
+ rewriteFilePaths(configSchema, config, toBaseDir = '/files') {
58
+ const fileMap = {};
59
+ if (!configSchema || !configSchema.types) {
60
+ return fileMap;
61
+ }
62
+ const fromBaseDir = this.getFilesBaseDir();
63
+ for (const entity of configSchema.types) {
64
+ if (entity.type !== schemas_1.EntityType.Dto) {
65
+ continue;
66
+ }
67
+ if (!entity.properties || !config[entity.name]) {
68
+ continue;
69
+ }
70
+ const innerValue = config[entity.name];
71
+ for (const key in entity.properties) {
72
+ const prop = entity.properties[key];
73
+ if (prop.type !== 'file') {
74
+ continue;
75
+ }
76
+ const value = innerValue[key];
77
+ if (!value || !value.startsWith(fromBaseDir)) {
78
+ continue;
79
+ }
80
+ const src = value;
81
+ const dst = toBaseDir + '/' + value.substring(fromBaseDir.length + 1);
82
+ fileMap[src] = dst;
83
+ innerValue[key] = dst;
84
+ }
85
+ }
86
+ return fileMap;
42
87
  }
43
88
  setConfigForSection(systemId, sectionId, config) {
44
89
  systemId = (0, nodejs_utils_1.normalizeKapetaUri)(systemId);
@@ -46,6 +91,47 @@ class ConfigManager {
46
91
  systemConfig[sectionId] = config || {};
47
92
  storageService_1.storageService.put('config', systemId, systemConfig);
48
93
  }
94
+ getFilesBaseDir() {
95
+ return path_1.default.join(local_cluster_config_1.default.getKapetaBasedir(), 'files');
96
+ }
97
+ async saveFile(value) {
98
+ const filename = node_uuid_1.default.v4();
99
+ const basePath = this.getFilesBaseDir();
100
+ await fs_extra_1.default.mkdirp(basePath);
101
+ const filepath = path_1.default.join(basePath, filename);
102
+ await fs_extra_1.default.writeFile(filepath, value);
103
+ return filepath;
104
+ }
105
+ async setConfigForBlockInstance(systemId, instanceId, configEntityList, config) {
106
+ systemId = (0, nodejs_utils_1.normalizeKapetaUri)(systemId);
107
+ let systemConfig = this._forSystem(systemId);
108
+ let configValue = config || {};
109
+ if (configEntityList.types) {
110
+ for (let entity of configEntityList.types) {
111
+ if (entity.type !== schemas_1.EntityType.Dto) {
112
+ continue;
113
+ }
114
+ if (!configValue[entity.name] || !entity.properties) {
115
+ continue;
116
+ }
117
+ const innerValues = configValue[entity.name];
118
+ for (let key in innerValues) {
119
+ if (!entity.properties[key]) {
120
+ continue;
121
+ }
122
+ if (entity.properties[key].type === 'file' &&
123
+ typeof innerValues[key] === 'string' &&
124
+ innerValues[key].startsWith('data:')) {
125
+ // This was a file upload - save the file to disk and replace value with path
126
+ const data = (0, parse_data_uri_1.default)(innerValues[key]);
127
+ innerValues[key] = await this.saveFile(data.data);
128
+ }
129
+ }
130
+ }
131
+ }
132
+ systemConfig[instanceId] = configValue;
133
+ storageService_1.storageService.put('config', systemId, systemConfig);
134
+ }
49
135
  getConfigForSection(systemId, sectionId) {
50
136
  systemId = (0, nodejs_utils_1.normalizeKapetaUri)(systemId);
51
137
  const systemConfig = this._forSystem(systemId);
@@ -17,7 +17,6 @@ const socketManager_1 = require("./socketManager");
17
17
  const serviceManager_1 = require("./serviceManager");
18
18
  const assetManager_1 = require("./assetManager");
19
19
  const containerManager_1 = require("./containerManager");
20
- const configManager_1 = require("./configManager");
21
20
  const types_1 = require("./types");
22
21
  const utils_1 = require("./utils/utils");
23
22
  const operatorManager_1 = require("./operatorManager");
@@ -501,12 +500,11 @@ class InstanceManager {
501
500
  return existingInstance;
502
501
  }
503
502
  }
504
- const resolvedConfig = await configManager_1.configManager.getConfigForBlockInstance(systemId, instanceId);
505
503
  const task = taskManager_1.taskManager.add(`instance:start:${systemId}:${instanceId}`, async () => {
506
504
  const runner = new BlockInstanceRunner_1.BlockInstanceRunner(systemId);
507
505
  const startTime = Date.now();
508
506
  try {
509
- const processInfo = await runner.start(blockRef, instanceId, resolvedConfig);
507
+ const processInfo = await runner.start(blockRef, instanceId);
510
508
  instance.status = types_1.InstanceStatus.STARTING;
511
509
  return this.saveInternalInstance({
512
510
  ...instance,
@@ -31,7 +31,6 @@ export interface LogEntry {
31
31
  export interface BlockProcessParams {
32
32
  id: string;
33
33
  ref: string;
34
- configuration?: AnyMap;
35
34
  }
36
35
  export declare enum InstanceType {
37
36
  DOCKER = "docker",
@@ -2,7 +2,7 @@
2
2
  * Copyright 2023 Kapeta Inc.
3
3
  * SPDX-License-Identifier: BUSL-1.1
4
4
  */
5
- import { AnyMap, ProcessInfo } from '../types';
5
+ import { ProcessInfo } from '../types';
6
6
  export declare function resolvePortType(portType: string): string;
7
7
  export declare class BlockInstanceRunner {
8
8
  private readonly _systemId;
@@ -11,12 +11,13 @@ export declare class BlockInstanceRunner {
11
11
  * Start a block
12
12
  *
13
13
  */
14
- start(blockRef: string, instanceId: string, configuration: AnyMap): Promise<ProcessInfo>;
14
+ start(blockRef: string, instanceId: string): Promise<ProcessInfo>;
15
15
  private _execute;
16
16
  /**
17
17
  * Starts local process
18
18
  */
19
19
  private _startLocalProcess;
20
+ private prepareBinds;
20
21
  private _startDockerProcess;
21
22
  private _startOperatorProcess;
22
23
  /**
@@ -24,6 +24,7 @@ const taskManager_1 = require("../taskManager");
24
24
  const InternalConfigProvider_1 = require("./InternalConfigProvider");
25
25
  const config_mapper_1 = require("@kapeta/config-mapper");
26
26
  const crypto_1 = __importDefault(require("crypto"));
27
+ const configManager_1 = require("../configManager");
27
28
  const KAPETA_SYSTEM_ID = 'KAPETA_SYSTEM_ID';
28
29
  const KAPETA_BLOCK_REF = 'KAPETA_BLOCK_REF';
29
30
  const KAPETA_INSTANCE_ID = 'KAPETA_INSTANCE_ID';
@@ -89,11 +90,10 @@ class BlockInstanceRunner {
89
90
  * Start a block
90
91
  *
91
92
  */
92
- async start(blockRef, instanceId, configuration) {
93
+ async start(blockRef, instanceId) {
93
94
  return this._execute({
94
95
  ref: blockRef,
95
96
  id: instanceId,
96
- configuration,
97
97
  });
98
98
  }
99
99
  async _execute(blockInstance) {
@@ -112,7 +112,10 @@ class BlockInstanceRunner {
112
112
  }
113
113
  const baseDir = local_cluster_config_1.default.getRepositoryAssetPath(blockUri.handle, blockUri.name, blockUri.version);
114
114
  const realBaseDir = await fs_extra_1.default.realpath(baseDir);
115
- const internalConfigProvider = await (0, InternalConfigProvider_1.createInternalConfigProvider)(this._systemId, blockInstance.id, assetVersion);
115
+ const config = await configManager_1.configManager.getConfigForBlockInstance(this._systemId, blockInstance.id);
116
+ const configSchema = (assetVersion.definition.spec.configuration ? assetVersion.definition.spec.configuration : {});
117
+ const fileMapping = configManager_1.configManager.rewriteFilePaths(configSchema, config);
118
+ const internalConfigProvider = await (0, InternalConfigProvider_1.createInternalConfigProvider)(this._systemId, blockInstance.id, assetVersion, config);
116
119
  // Resolve the environment variables
117
120
  const blockVariables = await (0, config_mapper_1.resolveKapetaVariables)(realBaseDir, internalConfigProvider);
118
121
  const variables = await this.appendConnectedBlockOperatorEnv(internalConfigProvider, blockInstance, blockVariables);
@@ -133,10 +136,10 @@ class BlockInstanceRunner {
133
136
  //We need a port type to know how to connect to the block consistently
134
137
  const portTypes = getServiceProviderPorts(assetVersion, providerVersion);
135
138
  if (blockUri.version === 'local') {
136
- processInfo = await this._startLocalProcess(blockInstance, blockUri, env, assetVersion);
139
+ processInfo = await this._startLocalProcess(blockInstance, blockUri, env, assetVersion, fileMapping);
137
140
  }
138
141
  else {
139
- processInfo = await this._startDockerProcess(blockInstance, blockUri, env, assetVersion);
142
+ processInfo = await this._startDockerProcess(blockInstance, blockUri, env, assetVersion, fileMapping);
140
143
  }
141
144
  if (portTypes.length > 0) {
142
145
  processInfo.portType = portTypes[0];
@@ -147,7 +150,7 @@ class BlockInstanceRunner {
147
150
  /**
148
151
  * Starts local process
149
152
  */
150
- async _startLocalProcess(blockInstance, blockInfo, env, assetVersion) {
153
+ async _startLocalProcess(blockInstance, blockInfo, env, assetVersion, fileMapping) {
151
154
  const baseDir = local_cluster_config_1.default.getRepositoryAssetPath(blockInfo.handle, blockInfo.name, blockInfo.version);
152
155
  if (!fs_extra_1.default.existsSync(baseDir)) {
153
156
  throw new Error(`Local block not registered correctly - expected symlink here: ${baseDir}.\n` +
@@ -211,14 +214,7 @@ class BlockInstanceRunner {
211
214
  if (localContainer.healthcheck) {
212
215
  HealthCheck = containerManager_1.containerManager.toDockerHealth({ cmd: localContainer.healthcheck });
213
216
  }
214
- if (env[config_mapper_1.KAPETA_CONFIG_ENV_VAR]) {
215
- // If we have a config file, we need to bind it to the container and adjust the env var
216
- const localConfig = `/${config_mapper_1.KAPETA_ENV_CONFIG_FILE}`;
217
- Binds.push(`${(0, containerManager_1.toLocalBindVolume)(env[config_mapper_1.KAPETA_CONFIG_ENV_VAR])}:${localConfig}:ro`);
218
- // We also provide the hash to detect changes to the config file
219
- env['KAPETA_CONFIG_HASH'] = await this.getFileHash(env[config_mapper_1.KAPETA_CONFIG_ENV_VAR]);
220
- env[config_mapper_1.KAPETA_CONFIG_ENV_VAR] = localConfig;
221
- }
217
+ await this.prepareBinds(env, Binds, fileMapping);
222
218
  const Mounts = isDockerImage
223
219
  ? // For docker images we mount the local directory to the working directory
224
220
  containerManager_1.containerManager.toDockerMounts({
@@ -271,7 +267,22 @@ class BlockInstanceRunner {
271
267
  },
272
268
  });
273
269
  }
274
- async _startDockerProcess(blockInstance, blockInfo, env, assetVersion) {
270
+ async prepareBinds(env, Binds, fileMapping) {
271
+ if (env[config_mapper_1.KAPETA_CONFIG_ENV_VAR]) {
272
+ // If we have a config file, we need to bind it to the container and adjust the env var
273
+ const localConfig = `/${config_mapper_1.KAPETA_ENV_CONFIG_FILE}`;
274
+ Binds.push(`${(0, containerManager_1.toLocalBindVolume)(env[config_mapper_1.KAPETA_CONFIG_ENV_VAR])}:${localConfig}:ro`);
275
+ // We also provide the hash to detect changes to the config file
276
+ env['KAPETA_CONFIG_HASH'] = await this.getFileHash(env[config_mapper_1.KAPETA_CONFIG_ENV_VAR]);
277
+ env[config_mapper_1.KAPETA_CONFIG_ENV_VAR] = localConfig;
278
+ }
279
+ if (fileMapping) {
280
+ Object.entries(fileMapping).forEach(([src, dst]) => {
281
+ Binds.push(`${(0, containerManager_1.toLocalBindVolume)(src)}:${dst}:ro`);
282
+ });
283
+ }
284
+ }
285
+ async _startDockerProcess(blockInstance, blockInfo, env, assetVersion, fileMapping) {
275
286
  const { versionFile } = local_cluster_config_1.default.getRepositoryAssetInfoPath(blockInfo.handle, blockInfo.name, blockInfo.version);
276
287
  const versionYml = versionFile;
277
288
  if (!fs_extra_1.default.existsSync(versionYml)) {
@@ -296,14 +307,7 @@ class BlockInstanceRunner {
296
307
  const innerHome = process.platform === 'win32' ? '/root/.kapeta' : local_cluster_config_1.default.getKapetaBasedir();
297
308
  const systemUri = (0, nodejs_utils_1.parseKapetaUri)(this._systemId);
298
309
  const Binds = [`${(0, containerManager_1.toLocalBindVolume)(local_cluster_config_1.default.getKapetaBasedir())}:${innerHome}`];
299
- if (env[config_mapper_1.KAPETA_CONFIG_ENV_VAR]) {
300
- // If we have a config file, we need to bind it to the container and adjust the env var
301
- const localConfig = `/${config_mapper_1.KAPETA_ENV_CONFIG_FILE}`;
302
- Binds.push(`${(0, containerManager_1.toLocalBindVolume)(env[config_mapper_1.KAPETA_CONFIG_ENV_VAR])}:${localConfig}:ro`);
303
- // We also provide the hash to detect changes to the config file
304
- env['KAPETA_CONFIG_HASH'] = await this.getFileHash(env[config_mapper_1.KAPETA_CONFIG_ENV_VAR]);
305
- env[config_mapper_1.KAPETA_CONFIG_ENV_VAR] = localConfig;
306
- }
310
+ await this.prepareBinds(env, Binds, fileMapping);
307
311
  return this.ensureContainer({
308
312
  Image: dockerImage,
309
313
  name: containerName,
@@ -486,7 +490,8 @@ class BlockInstanceRunner {
486
490
  const plan = await internalConfigProvider.getPlan();
487
491
  const connectedBlockIds = [
488
492
  ...new Set(plan.spec.connections
489
- .filter((connection) => connection.provider.blockId == blockInstance.id || connection.consumer.blockId == blockInstance.id)
493
+ .filter((connection) => connection.provider.blockId == blockInstance.id ||
494
+ connection.consumer.blockId == blockInstance.id)
490
495
  .flatMap((connection) => [connection.provider.blockId, connection.consumer.blockId])
491
496
  .filter((blockId) => blockId !== blockInstance.id)),
492
497
  ];
@@ -35,4 +35,4 @@ export declare class InternalConfigProvider implements ConfigProvider {
35
35
  getBlock(ref: any): Promise<Definition>;
36
36
  getPlan(): Promise<Plan>;
37
37
  }
38
- export declare function createInternalConfigProvider(systemId: string, instanceId: string, info: DefinitionInfo): Promise<InternalConfigProvider>;
38
+ export declare function createInternalConfigProvider(systemId: string, instanceId: string, info: DefinitionInfo, config: AnyMap): Promise<InternalConfigProvider>;
@@ -5,7 +5,6 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
5
5
  Object.defineProperty(exports, "__esModule", { value: true });
6
6
  exports.createInternalConfigProvider = exports.InternalConfigProvider = void 0;
7
7
  const nodejs_utils_1 = require("@kapeta/nodejs-utils");
8
- const configManager_1 = require("../configManager");
9
8
  const lodash_1 = __importDefault(require("lodash"));
10
9
  const serviceManager_1 = require("../serviceManager");
11
10
  const operatorManager_1 = require("../operatorManager");
@@ -139,8 +138,7 @@ class InternalConfigProvider {
139
138
  }
140
139
  }
141
140
  exports.InternalConfigProvider = InternalConfigProvider;
142
- async function createInternalConfigProvider(systemId, instanceId, info) {
143
- const config = await configManager_1.configManager.getConfigForBlockInstance(systemId, instanceId);
141
+ async function createInternalConfigProvider(systemId, instanceId, info, config) {
144
142
  return new InternalConfigProvider(systemId, instanceId, info, config);
145
143
  }
146
144
  exports.createInternalConfigProvider = createInternalConfigProvider;
@@ -25,10 +25,17 @@ const node_os_1 = __importDefault(require("node:os"));
25
25
  const CACHE_TTL = 60 * 60 * 1000; // 1 hour
26
26
  const UPGRADE_CHECK_INTERVAL = 10 * 60 * 1000; // 10 minutes
27
27
  const toKey = (ref) => `assetManager:asset:${ref}`;
28
- function filterExists(asset) {
29
- return fs_extra_1.default.existsSync(asset.path) && fs_extra_1.default.existsSync(asset.ymlPath);
30
- }
31
28
  function enrichAsset(asset) {
29
+ let exists = true;
30
+ let path = asset.path;
31
+ let ymlPath = asset.ymlPath;
32
+ try {
33
+ path = fs_extra_1.default.realpathSync(path);
34
+ ymlPath = fs_extra_1.default.realpathSync(ymlPath);
35
+ }
36
+ catch (e) {
37
+ exists = false;
38
+ }
32
39
  return {
33
40
  ref: `kapeta://${asset.definition.metadata.name}:${asset.version}`,
34
41
  editable: asset.version === 'local', //Only local versions are editable
@@ -36,8 +43,8 @@ function enrichAsset(asset) {
36
43
  version: asset.version,
37
44
  kind: asset.definition.kind,
38
45
  data: asset.definition,
39
- path: fs_extra_1.default.realpathSync(asset.path),
40
- ymlPath: fs_extra_1.default.realpathSync(asset.ymlPath),
46
+ path,
47
+ ymlPath,
41
48
  };
42
49
  }
43
50
  function compareRefs(a, b) {
@@ -86,7 +93,7 @@ class AssetManager {
86
93
  assetKinds.push('core/plan');
87
94
  }
88
95
  const assets = await definitionsManager_1.definitionsManager.getDefinitions(assetKinds);
89
- return assets.filter(filterExists).map(enrichAsset);
96
+ return assets.map(enrichAsset).filter((a) => a.exists);
90
97
  }
91
98
  async getPlans() {
92
99
  return this.getAssets(['core/plan']);
@@ -124,9 +131,14 @@ class AssetManager {
124
131
  throw new Error('Asset not found: ' + ref);
125
132
  }
126
133
  if (definitionInfo) {
127
- const asset = enrichAsset(definitionInfo);
128
- cacheManager_1.cacheManager.set(cacheKey, asset, CACHE_TTL);
129
- return asset;
134
+ try {
135
+ const asset = enrichAsset(definitionInfo);
136
+ cacheManager_1.cacheManager.set(cacheKey, asset, CACHE_TTL);
137
+ return asset;
138
+ }
139
+ catch (e) {
140
+ console.error('Failed to enrich asset', e);
141
+ }
130
142
  }
131
143
  return undefined;
132
144
  }
@@ -15,6 +15,8 @@ const instanceManager_1 = require("../instanceManager");
15
15
  const cors_1 = require("../middleware/cors");
16
16
  const kapeta_1 = require("../middleware/kapeta");
17
17
  const stringBody_1 = require("../middleware/stringBody");
18
+ const assetManager_1 = require("../assetManager");
19
+ const definitionsManager_1 = require("../definitionsManager");
18
20
  const router = (0, express_promise_router_1.default)();
19
21
  router.use('/', cors_1.corsHandler);
20
22
  router.use('/', kapeta_1.kapetaHeaders);
@@ -26,7 +28,7 @@ router.get('/instance', async (req, res) => {
26
28
  try {
27
29
  let config = {};
28
30
  if (req.kapeta.instanceId) {
29
- config = await configManager_1.configManager.getConfigForBlockInstance(req.kapeta.systemId, req.kapeta.instanceId);
31
+ config = await configManager_1.configManager.getConfigForBlockInstance(req.kapeta.systemId, req.kapeta.instanceId, req.kapeta?.environment === 'docker');
30
32
  }
31
33
  else {
32
34
  config = configManager_1.configManager.getConfigForSystem(req.kapeta.systemId);
@@ -49,7 +51,19 @@ router.put('/instance', async (req, res) => {
49
51
  config = {};
50
52
  }
51
53
  if (req.kapeta.instanceId) {
52
- configManager_1.configManager.setConfigForSection(req.kapeta.systemId, req.kapeta.instanceId, config);
54
+ const blockInstance = await assetManager_1.assetManager.getBlockInstance(req.kapeta.systemId, req.kapeta.instanceId);
55
+ if (!blockInstance) {
56
+ throw new Error(`Block instance not found: ${req.kapeta.instanceId} in plan ${req.kapeta.systemId}`);
57
+ }
58
+ const blockDefinition = await definitionsManager_1.definitionsManager.getDefinition(blockInstance.block.ref);
59
+ if (!blockDefinition) {
60
+ throw new Error(`Block definition not found: ${blockInstance.block.ref}`);
61
+ }
62
+ let configEntityList = {};
63
+ if (blockDefinition.definition.spec.configuration) {
64
+ configEntityList = blockDefinition.definition.spec.configuration;
65
+ }
66
+ await configManager_1.configManager.setConfigForBlockInstance(req.kapeta.systemId, req.kapeta.instanceId, configEntityList, config);
53
67
  //Restart the instance if it is running after config change
54
68
  await instanceManager_1.instanceManager.prepareForRestart(req.kapeta.systemId, req.kapeta.instanceId);
55
69
  }
@@ -2,6 +2,8 @@
2
2
  * Copyright 2023 Kapeta Inc.
3
3
  * SPDX-License-Identifier: BUSL-1.1
4
4
  */
5
+ import { EntityList } from '@kapeta/schemas';
6
+ import { StringMap } from './types';
5
7
  export declare const SYSTEM_ID = "$plan";
6
8
  type AnyMap = {
7
9
  [key: string]: any;
@@ -16,8 +18,12 @@ declare class ConfigManager {
16
18
  _forSystem(systemId: string): any;
17
19
  setConfigForSystem(systemId: string, config: AnyMap): void;
18
20
  getConfigForSystem(systemId: string): AnyMap;
19
- getConfigForBlockInstance(systemId: string, instanceId: string): Promise<import("./types").AnyMap>;
21
+ getConfigForBlockInstance(systemId: string, instanceId: string, rewritePaths?: boolean): Promise<import("./types").AnyMap>;
22
+ rewriteFilePaths(configSchema: EntityList, config: AnyMap, toBaseDir?: string): StringMap;
20
23
  setConfigForSection(systemId: string, sectionId: string, config: AnyMap): void;
24
+ getFilesBaseDir(): string;
25
+ private saveFile;
26
+ setConfigForBlockInstance(systemId: string, instanceId: string, configEntityList: EntityList, config: AnyMap): Promise<void>;
21
27
  getConfigForSection(systemId: string, sectionId: string): any;
22
28
  /**
23
29
  * Try to identify the plan and instance in a plan automatically based on the block reference