@kadi.build/deploy-ability 0.0.1
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/README.md +523 -0
- package/dist/constants.d.ts +82 -0
- package/dist/constants.d.ts.map +1 -0
- package/dist/constants.js +82 -0
- package/dist/constants.js.map +1 -0
- package/dist/errors/certificate-error.d.ts +95 -0
- package/dist/errors/certificate-error.d.ts.map +1 -0
- package/dist/errors/certificate-error.js +111 -0
- package/dist/errors/certificate-error.js.map +1 -0
- package/dist/errors/deployment-error.d.ts +122 -0
- package/dist/errors/deployment-error.d.ts.map +1 -0
- package/dist/errors/deployment-error.js +185 -0
- package/dist/errors/deployment-error.js.map +1 -0
- package/dist/errors/index.d.ts +13 -0
- package/dist/errors/index.d.ts.map +1 -0
- package/dist/errors/index.js +18 -0
- package/dist/errors/index.js.map +1 -0
- package/dist/errors/profile-error.d.ts +106 -0
- package/dist/errors/profile-error.d.ts.map +1 -0
- package/dist/errors/profile-error.js +127 -0
- package/dist/errors/profile-error.js.map +1 -0
- package/dist/errors/provider-error.d.ts +104 -0
- package/dist/errors/provider-error.d.ts.map +1 -0
- package/dist/errors/provider-error.js +120 -0
- package/dist/errors/provider-error.js.map +1 -0
- package/dist/errors/wallet-error.d.ts +131 -0
- package/dist/errors/wallet-error.d.ts.map +1 -0
- package/dist/errors/wallet-error.js +154 -0
- package/dist/errors/wallet-error.js.map +1 -0
- package/dist/index.d.ts +49 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +53 -0
- package/dist/index.js.map +1 -0
- package/dist/targets/akash/bid-selectors.d.ts +251 -0
- package/dist/targets/akash/bid-selectors.d.ts.map +1 -0
- package/dist/targets/akash/bid-selectors.js +322 -0
- package/dist/targets/akash/bid-selectors.js.map +1 -0
- package/dist/targets/akash/bid-types.d.ts +297 -0
- package/dist/targets/akash/bid-types.d.ts.map +1 -0
- package/dist/targets/akash/bid-types.js +89 -0
- package/dist/targets/akash/bid-types.js.map +1 -0
- package/dist/targets/akash/blockchain-client.d.ts +577 -0
- package/dist/targets/akash/blockchain-client.d.ts.map +1 -0
- package/dist/targets/akash/blockchain-client.js +803 -0
- package/dist/targets/akash/blockchain-client.js.map +1 -0
- package/dist/targets/akash/certificate-manager.d.ts +228 -0
- package/dist/targets/akash/certificate-manager.d.ts.map +1 -0
- package/dist/targets/akash/certificate-manager.js +395 -0
- package/dist/targets/akash/certificate-manager.js.map +1 -0
- package/dist/targets/akash/constants.d.ts +231 -0
- package/dist/targets/akash/constants.d.ts.map +1 -0
- package/dist/targets/akash/constants.js +225 -0
- package/dist/targets/akash/constants.js.map +1 -0
- package/dist/targets/akash/deployer.d.ts +136 -0
- package/dist/targets/akash/deployer.d.ts.map +1 -0
- package/dist/targets/akash/deployer.js +599 -0
- package/dist/targets/akash/deployer.js.map +1 -0
- package/dist/targets/akash/environment.d.ts +241 -0
- package/dist/targets/akash/environment.d.ts.map +1 -0
- package/dist/targets/akash/environment.js +245 -0
- package/dist/targets/akash/environment.js.map +1 -0
- package/dist/targets/akash/index.d.ts +1113 -0
- package/dist/targets/akash/index.d.ts.map +1 -0
- package/dist/targets/akash/index.js +909 -0
- package/dist/targets/akash/index.js.map +1 -0
- package/dist/targets/akash/lease-monitor.d.ts +51 -0
- package/dist/targets/akash/lease-monitor.d.ts.map +1 -0
- package/dist/targets/akash/lease-monitor.js +110 -0
- package/dist/targets/akash/lease-monitor.js.map +1 -0
- package/dist/targets/akash/logs.d.ts +71 -0
- package/dist/targets/akash/logs.d.ts.map +1 -0
- package/dist/targets/akash/logs.js +311 -0
- package/dist/targets/akash/logs.js.map +1 -0
- package/dist/targets/akash/logs.types.d.ts +102 -0
- package/dist/targets/akash/logs.types.d.ts.map +1 -0
- package/dist/targets/akash/logs.types.js +9 -0
- package/dist/targets/akash/logs.types.js.map +1 -0
- package/dist/targets/akash/pricing.d.ts +247 -0
- package/dist/targets/akash/pricing.d.ts.map +1 -0
- package/dist/targets/akash/pricing.js +246 -0
- package/dist/targets/akash/pricing.js.map +1 -0
- package/dist/targets/akash/provider-client.d.ts +114 -0
- package/dist/targets/akash/provider-client.d.ts.map +1 -0
- package/dist/targets/akash/provider-client.js +318 -0
- package/dist/targets/akash/provider-client.js.map +1 -0
- package/dist/targets/akash/provider-metadata.d.ts +228 -0
- package/dist/targets/akash/provider-metadata.d.ts.map +1 -0
- package/dist/targets/akash/provider-metadata.js +14 -0
- package/dist/targets/akash/provider-metadata.js.map +1 -0
- package/dist/targets/akash/provider-service.d.ts +133 -0
- package/dist/targets/akash/provider-service.d.ts.map +1 -0
- package/dist/targets/akash/provider-service.js +391 -0
- package/dist/targets/akash/provider-service.js.map +1 -0
- package/dist/targets/akash/query-client.d.ts +125 -0
- package/dist/targets/akash/query-client.d.ts.map +1 -0
- package/dist/targets/akash/query-client.js +332 -0
- package/dist/targets/akash/query-client.js.map +1 -0
- package/dist/targets/akash/sdl-generator.d.ts +31 -0
- package/dist/targets/akash/sdl-generator.d.ts.map +1 -0
- package/dist/targets/akash/sdl-generator.js +279 -0
- package/dist/targets/akash/sdl-generator.js.map +1 -0
- package/dist/targets/akash/types.d.ts +285 -0
- package/dist/targets/akash/types.d.ts.map +1 -0
- package/dist/targets/akash/types.js +54 -0
- package/dist/targets/akash/types.js.map +1 -0
- package/dist/targets/akash/wallet-manager.d.ts +526 -0
- package/dist/targets/akash/wallet-manager.d.ts.map +1 -0
- package/dist/targets/akash/wallet-manager.js +953 -0
- package/dist/targets/akash/wallet-manager.js.map +1 -0
- package/dist/targets/local/compose-generator.d.ts +244 -0
- package/dist/targets/local/compose-generator.d.ts.map +1 -0
- package/dist/targets/local/compose-generator.js +324 -0
- package/dist/targets/local/compose-generator.js.map +1 -0
- package/dist/targets/local/deployer.d.ts +82 -0
- package/dist/targets/local/deployer.d.ts.map +1 -0
- package/dist/targets/local/deployer.js +367 -0
- package/dist/targets/local/deployer.js.map +1 -0
- package/dist/targets/local/engine-manager.d.ts +155 -0
- package/dist/targets/local/engine-manager.d.ts.map +1 -0
- package/dist/targets/local/engine-manager.js +250 -0
- package/dist/targets/local/engine-manager.js.map +1 -0
- package/dist/targets/local/index.d.ts +40 -0
- package/dist/targets/local/index.d.ts.map +1 -0
- package/dist/targets/local/index.js +43 -0
- package/dist/targets/local/index.js.map +1 -0
- package/dist/targets/local/network-manager.d.ts +160 -0
- package/dist/targets/local/network-manager.d.ts.map +1 -0
- package/dist/targets/local/network-manager.js +337 -0
- package/dist/targets/local/network-manager.js.map +1 -0
- package/dist/targets/local/types.d.ts +327 -0
- package/dist/targets/local/types.d.ts.map +1 -0
- package/dist/targets/local/types.js +9 -0
- package/dist/targets/local/types.js.map +1 -0
- package/dist/types/common.d.ts +585 -0
- package/dist/types/common.d.ts.map +1 -0
- package/dist/types/common.js +13 -0
- package/dist/types/common.js.map +1 -0
- package/dist/types/index.d.ts +15 -0
- package/dist/types/index.d.ts.map +1 -0
- package/dist/types/index.js +12 -0
- package/dist/types/index.js.map +1 -0
- package/dist/types/options.d.ts +329 -0
- package/dist/types/options.d.ts.map +1 -0
- package/dist/types/options.js +10 -0
- package/dist/types/options.js.map +1 -0
- package/dist/types/profiles.d.ts +329 -0
- package/dist/types/profiles.d.ts.map +1 -0
- package/dist/types/profiles.js +27 -0
- package/dist/types/profiles.js.map +1 -0
- package/dist/types/results.d.ts +443 -0
- package/dist/types/results.d.ts.map +1 -0
- package/dist/types/results.js +64 -0
- package/dist/types/results.js.map +1 -0
- package/dist/types/validators.d.ts +118 -0
- package/dist/types/validators.d.ts.map +1 -0
- package/dist/types/validators.js +198 -0
- package/dist/types/validators.js.map +1 -0
- package/dist/utils/command-runner.d.ts +128 -0
- package/dist/utils/command-runner.d.ts.map +1 -0
- package/dist/utils/command-runner.js +210 -0
- package/dist/utils/command-runner.js.map +1 -0
- package/dist/utils/index.d.ts +10 -0
- package/dist/utils/index.d.ts.map +1 -0
- package/dist/utils/index.js +10 -0
- package/dist/utils/index.js.map +1 -0
- package/dist/utils/logger.d.ts +68 -0
- package/dist/utils/logger.d.ts.map +1 -0
- package/dist/utils/logger.js +93 -0
- package/dist/utils/logger.js.map +1 -0
- package/dist/utils/profile-loader.d.ts +76 -0
- package/dist/utils/profile-loader.d.ts.map +1 -0
- package/dist/utils/profile-loader.js +194 -0
- package/dist/utils/profile-loader.js.map +1 -0
- package/dist/utils/registry/index.d.ts +27 -0
- package/dist/utils/registry/index.d.ts.map +1 -0
- package/dist/utils/registry/index.js +29 -0
- package/dist/utils/registry/index.js.map +1 -0
- package/dist/utils/registry/manager.d.ts +319 -0
- package/dist/utils/registry/manager.d.ts.map +1 -0
- package/dist/utils/registry/manager.js +671 -0
- package/dist/utils/registry/manager.js.map +1 -0
- package/dist/utils/registry/setup.d.ts +135 -0
- package/dist/utils/registry/setup.d.ts.map +1 -0
- package/dist/utils/registry/setup.js +207 -0
- package/dist/utils/registry/setup.js.map +1 -0
- package/dist/utils/registry/transformer.d.ts +92 -0
- package/dist/utils/registry/transformer.d.ts.map +1 -0
- package/dist/utils/registry/transformer.js +131 -0
- package/dist/utils/registry/transformer.js.map +1 -0
- package/dist/utils/registry/types.d.ts +241 -0
- package/dist/utils/registry/types.d.ts.map +1 -0
- package/dist/utils/registry/types.js +10 -0
- package/dist/utils/registry/types.js.map +1 -0
- package/docs/EXAMPLES.md +293 -0
- package/docs/PLACEMENT.md +433 -0
- package/docs/STORAGE.md +318 -0
- package/docs/building-provider-reliability-tracker.md +2581 -0
- package/package.json +109 -0
|
@@ -0,0 +1,671 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Temporary Container Registry Manager
|
|
3
|
+
*
|
|
4
|
+
* Manages a temporary container registry for making local Docker images accessible
|
|
5
|
+
* to Akash providers during deployment. Wraps the TunneledContainerRegistry from
|
|
6
|
+
* @kadi.build/container-registry-ability with deployment-specific logic.
|
|
7
|
+
*
|
|
8
|
+
* **Core Responsibilities:**
|
|
9
|
+
* - Start/stop temporary registry with public tunnel
|
|
10
|
+
* - Detect which images exist locally vs remotely
|
|
11
|
+
* - Push local images to temporary registry
|
|
12
|
+
* - Track image → registry URL mappings
|
|
13
|
+
* - Provide credentials for SDL generation
|
|
14
|
+
*
|
|
15
|
+
* @module utils/registry/manager
|
|
16
|
+
*/
|
|
17
|
+
import path from 'node:path';
|
|
18
|
+
import fs from 'node:fs';
|
|
19
|
+
import os from 'node:os';
|
|
20
|
+
import { fileURLToPath } from 'node:url';
|
|
21
|
+
import { execSync } from 'node:child_process';
|
|
22
|
+
import dotenv from 'dotenv';
|
|
23
|
+
import debug from 'debug';
|
|
24
|
+
// @ts-ignore - Using existing TunneledContainerRegistry from JavaScript package
|
|
25
|
+
import { TunneledContainerRegistry } from '@kadi.build/container-registry-ability';
|
|
26
|
+
/**
|
|
27
|
+
* Debug logger for registry operations
|
|
28
|
+
*
|
|
29
|
+
* Enable with: DEBUG=deploy-ability:registry
|
|
30
|
+
*/
|
|
31
|
+
const log = debug('deploy-ability:registry');
|
|
32
|
+
/**
|
|
33
|
+
* Temporary Container Registry Manager
|
|
34
|
+
*
|
|
35
|
+
* Manages the lifecycle of a temporary container registry that makes local images
|
|
36
|
+
* accessible to Akash providers. This class handles starting the registry, detecting
|
|
37
|
+
* local images, pushing them, and tracking URL transformations.
|
|
38
|
+
*
|
|
39
|
+
* **Typical Workflow:**
|
|
40
|
+
* ```typescript
|
|
41
|
+
* const manager = new TemporaryContainerRegistryManager(logger);
|
|
42
|
+
*
|
|
43
|
+
* // 1. Start registry
|
|
44
|
+
* await manager.startTemporaryRegistry({
|
|
45
|
+
* tunnelService: 'serveo',
|
|
46
|
+
* containerEngine: 'docker'
|
|
47
|
+
* });
|
|
48
|
+
*
|
|
49
|
+
* // 2. Add local images from profile
|
|
50
|
+
* await manager.addLocalImagesToTemporaryRegistry(profile, 'docker');
|
|
51
|
+
*
|
|
52
|
+
* // 3. Get transformed image URLs
|
|
53
|
+
* const url = manager.getPublicImageUrl('frontend', 'my-app:latest');
|
|
54
|
+
*
|
|
55
|
+
* // 4. Get credentials for SDL
|
|
56
|
+
* const creds = manager.getRegistryCredentials();
|
|
57
|
+
*
|
|
58
|
+
* // 5. Stop registry when done
|
|
59
|
+
* await manager.stopTemporaryRegistry();
|
|
60
|
+
* ```
|
|
61
|
+
*/
|
|
62
|
+
export class TemporaryContainerRegistryManager {
|
|
63
|
+
logger;
|
|
64
|
+
registry = null;
|
|
65
|
+
registryInfo = null;
|
|
66
|
+
/**
|
|
67
|
+
* Maps original image names to their public registry URLs
|
|
68
|
+
*
|
|
69
|
+
* Key: Original image name (e.g., "my-app:latest")
|
|
70
|
+
* Value: Container mapping with registry URL and metadata
|
|
71
|
+
*/
|
|
72
|
+
containerMappings = new Map();
|
|
73
|
+
/**
|
|
74
|
+
* Create a new registry manager
|
|
75
|
+
*
|
|
76
|
+
* @param logger - Logger for progress and error messages
|
|
77
|
+
*/
|
|
78
|
+
constructor(logger) {
|
|
79
|
+
this.logger = logger;
|
|
80
|
+
}
|
|
81
|
+
/**
|
|
82
|
+
* Start temporary registry with public tunnel
|
|
83
|
+
*
|
|
84
|
+
* Creates a local container registry on the specified port and exposes it publicly
|
|
85
|
+
* via a tunnel service (ngrok, serveo, or bore). The registry is used to make
|
|
86
|
+
* local Docker images accessible to Akash providers during deployment.
|
|
87
|
+
*
|
|
88
|
+
* **What This Does:**
|
|
89
|
+
* 1. Loads environment variables (.env) for tunnel configuration
|
|
90
|
+
* 2. Starts local registry container on specified port
|
|
91
|
+
* 3. Creates public tunnel (ngrok/serveo/bore)
|
|
92
|
+
* 4. Generates authentication credentials
|
|
93
|
+
* 5. Returns when registry is accessible
|
|
94
|
+
*
|
|
95
|
+
* **Tunnel Services:**
|
|
96
|
+
* - **ngrok**: Most reliable, requires auth token (NGROK_AUTH_TOKEN env var)
|
|
97
|
+
* - **serveo**: Free SSH-based tunnel, no signup required
|
|
98
|
+
* - **bore**: Modern alternative using bore.pub
|
|
99
|
+
*
|
|
100
|
+
* @param options - Configuration options for registry and tunnel
|
|
101
|
+
* @throws Error if registry fails to start or tunnel cannot be established
|
|
102
|
+
*
|
|
103
|
+
* @example
|
|
104
|
+
* ```typescript
|
|
105
|
+
* await manager.startTemporaryRegistry({
|
|
106
|
+
* port: 3000,
|
|
107
|
+
* tunnelService: 'serveo',
|
|
108
|
+
* containerEngine: 'docker',
|
|
109
|
+
* registryDuration: 600000, // 10 minutes
|
|
110
|
+
* autoShutdown: false
|
|
111
|
+
* });
|
|
112
|
+
* ```
|
|
113
|
+
*/
|
|
114
|
+
async startTemporaryRegistry(options) {
|
|
115
|
+
if (this.registry) {
|
|
116
|
+
log('Temporary registry already running');
|
|
117
|
+
return;
|
|
118
|
+
}
|
|
119
|
+
log('Starting temporary container registry...');
|
|
120
|
+
try {
|
|
121
|
+
// Load environment variables for tunnel configuration
|
|
122
|
+
// Checks two locations:
|
|
123
|
+
// 1. Module directory (dist/.env) - plugin-specific overrides
|
|
124
|
+
// 2. Package root (.env) - project-wide configuration
|
|
125
|
+
const moduleDir = path.dirname(fileURLToPath(import.meta.url));
|
|
126
|
+
const pluginEnvPath = path.join(moduleDir, '.env');
|
|
127
|
+
if (fs.existsSync(pluginEnvPath)) {
|
|
128
|
+
dotenv.config({ path: pluginEnvPath });
|
|
129
|
+
log('Loaded env from: %s', pluginEnvPath);
|
|
130
|
+
}
|
|
131
|
+
const packageRootEnvPath = path.join(moduleDir, '..', '..', '..', '.env');
|
|
132
|
+
if (fs.existsSync(packageRootEnvPath)) {
|
|
133
|
+
dotenv.config({ path: packageRootEnvPath });
|
|
134
|
+
log('Loaded env from: %s', packageRootEnvPath);
|
|
135
|
+
}
|
|
136
|
+
// Resolve tunnel configuration from environment variables
|
|
137
|
+
// CLI options take precedence over environment variables
|
|
138
|
+
const envNgrokToken = process.env.NGROK_AUTH_TOKEN || undefined;
|
|
139
|
+
const envNgrokRegion = process.env.NGROK_REGION || undefined;
|
|
140
|
+
const envNgrokProtocol = process.env.NGROK_PROTOCOL || undefined;
|
|
141
|
+
// Create TunneledContainerRegistry instance
|
|
142
|
+
this.registry = new TunneledContainerRegistry({
|
|
143
|
+
port: options.port || 3000,
|
|
144
|
+
tunnelService: options.tunnelService || 'serveo',
|
|
145
|
+
tunnelOptions: {
|
|
146
|
+
// Allow CLI options to override env if provided
|
|
147
|
+
authToken: options.tunnelAuthToken || envNgrokToken,
|
|
148
|
+
region: options.tunnelRegion || envNgrokRegion,
|
|
149
|
+
protocol: options.tunnelProtocol || envNgrokProtocol,
|
|
150
|
+
subdomain: options.tunnelSubdomain
|
|
151
|
+
},
|
|
152
|
+
enableMonitoring: false,
|
|
153
|
+
// Disable verbose logging - only show errors
|
|
154
|
+
// User can enable with DEBUG=deploy-ability:* environment variable if needed
|
|
155
|
+
enableLogging: false,
|
|
156
|
+
logLevel: 'error',
|
|
157
|
+
// Enable auto-shutdown by default to cleanup resources after deployment completes
|
|
158
|
+
// Can be disabled by passing options.autoShutdown = false
|
|
159
|
+
autoShutdown: options.autoShutdown ?? true,
|
|
160
|
+
containerType: options.containerEngine || 'docker',
|
|
161
|
+
duration: options.registryDuration
|
|
162
|
+
});
|
|
163
|
+
// Start the registry and get connection info
|
|
164
|
+
await this.registry.start();
|
|
165
|
+
this.registryInfo = this.registry.getRegistryInfo();
|
|
166
|
+
// Validate registry info
|
|
167
|
+
if (!this.registryInfo) {
|
|
168
|
+
throw new Error('Failed to get registry information after starting registry');
|
|
169
|
+
}
|
|
170
|
+
if (!this.registryInfo.localUrl) {
|
|
171
|
+
throw new Error('Registry started but no local URL available');
|
|
172
|
+
}
|
|
173
|
+
// Determine the primary registry URL (prefer tunnel over local)
|
|
174
|
+
const primaryRegistryUrl = this.registryInfo.tunnelUrl || this.registryInfo.localUrl;
|
|
175
|
+
log('Registry started at: %s', primaryRegistryUrl);
|
|
176
|
+
await this.displayRegistryAccessInformation();
|
|
177
|
+
}
|
|
178
|
+
catch (error) {
|
|
179
|
+
// Clean up on failure
|
|
180
|
+
await this.stopTemporaryRegistry();
|
|
181
|
+
throw new Error(`Failed to start temporary registry: ${error}`);
|
|
182
|
+
}
|
|
183
|
+
}
|
|
184
|
+
/**
|
|
185
|
+
* Display registry access information including domain and credentials
|
|
186
|
+
*
|
|
187
|
+
* Logs registry connection details for debugging purposes.
|
|
188
|
+
* Only logs if debug logging is enabled (DEBUG=deploy-ability:registry).
|
|
189
|
+
*/
|
|
190
|
+
async displayRegistryAccessInformation() {
|
|
191
|
+
if (!this.registryInfo) {
|
|
192
|
+
return;
|
|
193
|
+
}
|
|
194
|
+
try {
|
|
195
|
+
// Try to get command help from registry (provides formatted domain)
|
|
196
|
+
const commandHelp = await this.registry.generateCommandHelp();
|
|
197
|
+
const registryDomain = commandHelp?.registry?.registryDomain ||
|
|
198
|
+
(this.registryInfo.tunnelUrl || this.registryInfo.localUrl).replace(/^https?:\/\//, '');
|
|
199
|
+
log('Registry domain: %s', registryDomain);
|
|
200
|
+
if (this.registryInfo.credentials) {
|
|
201
|
+
log('Access key: %s', this.registryInfo.credentials.accessKey);
|
|
202
|
+
log('Secret key: [hidden]');
|
|
203
|
+
}
|
|
204
|
+
}
|
|
205
|
+
catch (error) {
|
|
206
|
+
// Fallback display if command generation fails
|
|
207
|
+
if (this.registryInfo.credentials) {
|
|
208
|
+
log('Registry credentials available');
|
|
209
|
+
log('Username: %s', this.registryInfo.credentials.accessKey);
|
|
210
|
+
log('Password: [hidden]');
|
|
211
|
+
}
|
|
212
|
+
}
|
|
213
|
+
}
|
|
214
|
+
/**
|
|
215
|
+
* Check if a container image exists locally
|
|
216
|
+
*
|
|
217
|
+
* Uses `docker images -q` or `podman images -q` to check if an image exists
|
|
218
|
+
* in the local container engine. This is the ground truth for whether we need
|
|
219
|
+
* to add an image to the temporary registry.
|
|
220
|
+
*
|
|
221
|
+
* **Why This Works:**
|
|
222
|
+
* - Returns image hash if image exists locally
|
|
223
|
+
* - Returns empty string if image doesn't exist
|
|
224
|
+
* - Works for both tagged and untagged images
|
|
225
|
+
* - Handles shorthand names correctly (e.g., "nginx" vs "docker.io/library/nginx")
|
|
226
|
+
*
|
|
227
|
+
* **Reality-Based Detection:**
|
|
228
|
+
* Instead of guessing based on image name patterns (checking for "/" in the name),
|
|
229
|
+
* we check the actual state of the local container engine. This prevents false
|
|
230
|
+
* positives and false negatives.
|
|
231
|
+
*
|
|
232
|
+
* @param imageName - Full image name with tag (e.g., "my-app:latest")
|
|
233
|
+
* @param engine - Container engine to use
|
|
234
|
+
* @returns True if image exists locally, false otherwise
|
|
235
|
+
*/
|
|
236
|
+
checkImageExistsLocally(imageName, engine) {
|
|
237
|
+
try {
|
|
238
|
+
const result = execSync(`${engine} images -q ${imageName}`, {
|
|
239
|
+
encoding: 'utf8',
|
|
240
|
+
stdio: ['pipe', 'pipe', 'pipe'] // Suppress stderr
|
|
241
|
+
});
|
|
242
|
+
return result.trim().length > 0;
|
|
243
|
+
}
|
|
244
|
+
catch (error) {
|
|
245
|
+
// Command failed (engine not available or other error)
|
|
246
|
+
return false;
|
|
247
|
+
}
|
|
248
|
+
}
|
|
249
|
+
/**
|
|
250
|
+
* Add local images from deployment profile to the temporary registry
|
|
251
|
+
*
|
|
252
|
+
* **Simplified Logic (Reality-Based Detection):**
|
|
253
|
+
*
|
|
254
|
+
* Instead of guessing if an image is "local" based on name patterns (like checking
|
|
255
|
+
* if it has "/" in the name), we simply check if the image actually exists locally
|
|
256
|
+
* using `docker images -q <image>`.
|
|
257
|
+
*
|
|
258
|
+
* **This approach:**
|
|
259
|
+
* 1. Is more accurate (checks reality, not heuristics)
|
|
260
|
+
* 2. Is simpler (one check instead of multiple conditions)
|
|
261
|
+
* 3. Handles edge cases automatically:
|
|
262
|
+
* - Docker Hub shorthand ("nginx" → exists remotely, not locally)
|
|
263
|
+
* - Custom registries with default namespaces
|
|
264
|
+
* - Images that "look local" but are actually remote
|
|
265
|
+
*
|
|
266
|
+
* **Decision Flow:**
|
|
267
|
+
* - Image exists locally → Add to temporary registry and make publicly accessible
|
|
268
|
+
* - Image doesn't exist locally → Treat as remote reference (don't add to registry)
|
|
269
|
+
*
|
|
270
|
+
* **Why This Works:**
|
|
271
|
+
* The temporary registry is ONLY needed for images that exist locally but need to
|
|
272
|
+
* be made publicly accessible to Akash providers. If an image doesn't exist locally,
|
|
273
|
+
* either:
|
|
274
|
+
* - It's a remote image (providers can pull it directly)
|
|
275
|
+
* - It doesn't exist anywhere (deployment will fail later with clear error)
|
|
276
|
+
*
|
|
277
|
+
* @param profile - Deployment profile containing services with images
|
|
278
|
+
* @param containerEngine - Container engine to use for operations
|
|
279
|
+
* @throws Error if a required local image cannot be added to registry
|
|
280
|
+
*
|
|
281
|
+
* @example
|
|
282
|
+
* ```typescript
|
|
283
|
+
* await manager.addLocalImagesToTemporaryRegistry(profile, 'docker');
|
|
284
|
+
* // Checks each service image, adds local ones to registry
|
|
285
|
+
* ```
|
|
286
|
+
*/
|
|
287
|
+
async addLocalImagesToTemporaryRegistry(profile, containerEngine) {
|
|
288
|
+
log('Checking which service images exist locally...');
|
|
289
|
+
for (const [serviceName, serviceConfig] of Object.entries(profile.services)) {
|
|
290
|
+
const imageName = serviceConfig.image;
|
|
291
|
+
// Check if image exists locally (reality-based, not name-based)
|
|
292
|
+
if (this.checkImageExistsLocally(imageName, containerEngine)) {
|
|
293
|
+
// Image exists locally → add to temporary registry
|
|
294
|
+
log('Image found locally for service %s: %s', serviceName, imageName);
|
|
295
|
+
try {
|
|
296
|
+
// Use intelligent fallback strategy (tar file → container engine)
|
|
297
|
+
const mapping = await this.addContainerIntelligently(imageName, serviceName, containerEngine);
|
|
298
|
+
// Store the mapping for later SDL generation
|
|
299
|
+
this.containerMappings.set(imageName, mapping);
|
|
300
|
+
}
|
|
301
|
+
catch (error) {
|
|
302
|
+
this.logger.error(`❌ Failed to add local image '${imageName}' for service '${serviceName}': ${error.message}`);
|
|
303
|
+
throw error; // Stop deployment if we can't host a required local image
|
|
304
|
+
}
|
|
305
|
+
}
|
|
306
|
+
else {
|
|
307
|
+
// Image doesn't exist locally → treat as remote
|
|
308
|
+
log('Image not found locally, treating as remote for service %s: %s', serviceName, imageName);
|
|
309
|
+
}
|
|
310
|
+
}
|
|
311
|
+
log('Successfully processed %d local images', this.containerMappings.size);
|
|
312
|
+
}
|
|
313
|
+
/**
|
|
314
|
+
* Intelligent container addition with fallback strategies
|
|
315
|
+
*
|
|
316
|
+
* Tries multiple strategies to add a container image to the registry:
|
|
317
|
+
* 1. **Strategy 1**: Load from tar file (from kadi-build export cache)
|
|
318
|
+
* 2. **Strategy 2**: Load from container engine (docker/podman)
|
|
319
|
+
* 3. **Failure**: Show helpful error with suggestions
|
|
320
|
+
*
|
|
321
|
+
* **Why Multiple Strategies:**
|
|
322
|
+
* - Tar files are faster and don't require container engine running
|
|
323
|
+
* - Container engine is the fallback if tar file not available
|
|
324
|
+
* - Provides clear guidance if both fail
|
|
325
|
+
*
|
|
326
|
+
* @param imageName - Full image name with tag
|
|
327
|
+
* @param serviceName - Service name from profile
|
|
328
|
+
* @param containerEngine - Container engine to use
|
|
329
|
+
* @returns Container mapping with registry URL
|
|
330
|
+
* @throws Error if image cannot be added with any strategy
|
|
331
|
+
*/
|
|
332
|
+
async addContainerIntelligently(imageName, serviceName, containerEngine) {
|
|
333
|
+
// Parse image name into repository and tag
|
|
334
|
+
const repoName = imageName.includes(':')
|
|
335
|
+
? imageName.split(':')[0]
|
|
336
|
+
: imageName;
|
|
337
|
+
const imageTag = imageName.includes(':')
|
|
338
|
+
? imageName.split(':')[1]
|
|
339
|
+
: 'latest';
|
|
340
|
+
log('Attempting to add container: %s', imageName);
|
|
341
|
+
// Strategy 1: Try to find and use tar file from kadi-build
|
|
342
|
+
const tarPath = this.findKadiBuildTarFile(imageName);
|
|
343
|
+
if (tarPath) {
|
|
344
|
+
try {
|
|
345
|
+
log('Loading container from tar file: %s', tarPath);
|
|
346
|
+
const containerInfo = await this.registry.addContainer({
|
|
347
|
+
type: 'tar',
|
|
348
|
+
name: repoName || imageName, // Fallback to imageName if repoName is undefined
|
|
349
|
+
path: tarPath
|
|
350
|
+
});
|
|
351
|
+
log('Container loaded from tar file with alias: %s', containerInfo.alias);
|
|
352
|
+
return this.createContainerMapping(imageName, serviceName, containerInfo, repoName || imageName, // Use imageName as fallback
|
|
353
|
+
imageTag || 'latest' // Use 'latest' as fallback
|
|
354
|
+
);
|
|
355
|
+
}
|
|
356
|
+
catch (error) {
|
|
357
|
+
log('Failed to load from tar file: %s', error.message);
|
|
358
|
+
log('Falling back to container engine...');
|
|
359
|
+
}
|
|
360
|
+
}
|
|
361
|
+
// Strategy 2: Try to add from container engine (docker/podman)
|
|
362
|
+
try {
|
|
363
|
+
log('Attempting to add from %s engine: %s', containerEngine, imageName);
|
|
364
|
+
const containerInfo = await this.registry.addContainer({
|
|
365
|
+
type: containerEngine,
|
|
366
|
+
name: repoName || imageName, // Fallback to imageName if repoName is undefined
|
|
367
|
+
image: imageName
|
|
368
|
+
});
|
|
369
|
+
log('Container loaded from %s with alias: %s', containerEngine, containerInfo.alias);
|
|
370
|
+
return this.createContainerMapping(imageName, serviceName, containerInfo, repoName || imageName, // Use imageName as fallback
|
|
371
|
+
imageTag || 'latest' // Use 'latest' as fallback
|
|
372
|
+
);
|
|
373
|
+
}
|
|
374
|
+
catch (error) {
|
|
375
|
+
log('Failed to load from %s: %s', containerEngine, error.message);
|
|
376
|
+
}
|
|
377
|
+
// Strategy 3: Show helpful error message
|
|
378
|
+
this.showKadiBuildSuggestion(imageName, containerEngine);
|
|
379
|
+
throw new Error(`Could not add container ${imageName}. See suggestions above.`);
|
|
380
|
+
}
|
|
381
|
+
/**
|
|
382
|
+
* Create container mapping for registry URLs
|
|
383
|
+
*
|
|
384
|
+
* Transforms a local image reference into a complete registry URL mapping
|
|
385
|
+
* that can be used in SDL generation. Includes service name tracking for
|
|
386
|
+
* better debugging and error messages.
|
|
387
|
+
*
|
|
388
|
+
* @param originalImage - Original image name from agent.json (e.g., "my-app")
|
|
389
|
+
* @param serviceName - Service name from profile (e.g., "frontend")
|
|
390
|
+
* @param containerInfo - Container info from registry with alias
|
|
391
|
+
* @param repoName - Repository name extracted from image
|
|
392
|
+
* @param imageTag - Image tag (e.g., "latest")
|
|
393
|
+
* @returns Complete container mapping with registry URL
|
|
394
|
+
*/
|
|
395
|
+
async createContainerMapping(originalImage, serviceName, containerInfo, repoName, imageTag) {
|
|
396
|
+
const actualAlias = containerInfo.alias;
|
|
397
|
+
const registryDomain = this.getPreferredDomain();
|
|
398
|
+
const registryImageUrl = `${registryDomain}/${actualAlias}:${imageTag}`;
|
|
399
|
+
const mapping = {
|
|
400
|
+
originalImage,
|
|
401
|
+
serviceName,
|
|
402
|
+
registryUrl: registryImageUrl,
|
|
403
|
+
repoName,
|
|
404
|
+
imageTag,
|
|
405
|
+
actualAlias
|
|
406
|
+
};
|
|
407
|
+
log('Container available at: %s', registryImageUrl);
|
|
408
|
+
// Verify container is accessible
|
|
409
|
+
this.verifyContainerInRegistry(actualAlias);
|
|
410
|
+
return mapping;
|
|
411
|
+
}
|
|
412
|
+
/**
|
|
413
|
+
* Get public image URL for a specific service
|
|
414
|
+
*
|
|
415
|
+
* This is the key method that SDL generator calls to get the transformed
|
|
416
|
+
* registry URL for a local image.
|
|
417
|
+
*
|
|
418
|
+
* **Returns null if:**
|
|
419
|
+
* - Image is not local (no mapping exists)
|
|
420
|
+
* - Image wasn't added to registry
|
|
421
|
+
* - Service name doesn't match
|
|
422
|
+
*
|
|
423
|
+
* @param serviceName - Service name from profile
|
|
424
|
+
* @param originalImage - Original image name from profile
|
|
425
|
+
* @returns Public registry URL or null if not a local image
|
|
426
|
+
*
|
|
427
|
+
* @example
|
|
428
|
+
* ```typescript
|
|
429
|
+
* const url = manager.getPublicImageUrl('frontend', 'my-app:latest');
|
|
430
|
+
* // Returns: "abc123.serveo.net/my-app:latest" or null
|
|
431
|
+
* ```
|
|
432
|
+
*/
|
|
433
|
+
getPublicImageUrl(serviceName, originalImage) {
|
|
434
|
+
const mapping = this.containerMappings.get(originalImage);
|
|
435
|
+
if (mapping && mapping.serviceName === serviceName) {
|
|
436
|
+
return mapping.registryUrl;
|
|
437
|
+
}
|
|
438
|
+
return null;
|
|
439
|
+
}
|
|
440
|
+
/**
|
|
441
|
+
* Get registry credentials for SDL generation
|
|
442
|
+
*
|
|
443
|
+
* Returns authentication credentials that should be added to the SDL services
|
|
444
|
+
* section so Akash providers can authenticate with the temporary registry.
|
|
445
|
+
*
|
|
446
|
+
* @returns Registry credentials or null if not available
|
|
447
|
+
*
|
|
448
|
+
* @example
|
|
449
|
+
* ```typescript
|
|
450
|
+
* const creds = manager.getRegistryCredentials();
|
|
451
|
+
* // Use in SDL: { host: "abc123.serveo.net", username: "...", password: "..." }
|
|
452
|
+
* ```
|
|
453
|
+
*/
|
|
454
|
+
getRegistryCredentials() {
|
|
455
|
+
if (!this.registryInfo?.credentials) {
|
|
456
|
+
log('No registry credentials available');
|
|
457
|
+
return null;
|
|
458
|
+
}
|
|
459
|
+
log('Registry info - tunnel: %s, local: %s', this.registryInfo.tunnelUrl, this.registryInfo.localUrl);
|
|
460
|
+
const host = this.getRegistryDomain();
|
|
461
|
+
// Convert to the format expected by SDL generation
|
|
462
|
+
const credentials = {
|
|
463
|
+
host: host,
|
|
464
|
+
username: this.registryInfo.credentials.accessKey,
|
|
465
|
+
password: this.registryInfo.credentials.secretKey
|
|
466
|
+
};
|
|
467
|
+
log('Returning registry credentials - host: %s, username: %s', credentials.host, credentials.username);
|
|
468
|
+
return credentials;
|
|
469
|
+
}
|
|
470
|
+
/**
|
|
471
|
+
* Find tar file from kadi-build export directory
|
|
472
|
+
*
|
|
473
|
+
* Searches for container tar files exported by kadi-build in common locations:
|
|
474
|
+
* 1. ~/.kadi/tmp/container-registry-exports/containers/ (primary)
|
|
475
|
+
* 2. ./container-exports/ (backup)
|
|
476
|
+
* 3. /tmp/container-registry-exports/containers/ (fallback)
|
|
477
|
+
*
|
|
478
|
+
* **Why This Matters:**
|
|
479
|
+
* Tar files are faster to load and don't require the container engine to be running.
|
|
480
|
+
*
|
|
481
|
+
* @param imageName - Image name to search for (e.g., "my-app:0.0.1")
|
|
482
|
+
* @returns Path to tar file or null if not found
|
|
483
|
+
*/
|
|
484
|
+
findKadiBuildTarFile(imageName) {
|
|
485
|
+
// Generate the expected filename pattern
|
|
486
|
+
// Example: "my-app:0.0.1" -> "my-app-0.0.1.tar"
|
|
487
|
+
const expectedFilename = `${imageName.replace(/[^a-zA-Z0-9.-]/g, '-')}.tar`;
|
|
488
|
+
// Common locations where kadi-build saves tar files
|
|
489
|
+
const possiblePaths = [
|
|
490
|
+
// User's home directory .kadi cache (primary location)
|
|
491
|
+
path.join(os.homedir(), '.kadi', 'tmp', 'container-registry-exports', 'containers'),
|
|
492
|
+
// Current working directory exports (backup location)
|
|
493
|
+
path.join(process.cwd(), 'container-exports'),
|
|
494
|
+
// Temporary directory exports (fallback location)
|
|
495
|
+
path.join(os.tmpdir(), 'container-registry-exports', 'containers')
|
|
496
|
+
];
|
|
497
|
+
for (const basePath of possiblePaths) {
|
|
498
|
+
const fullPath = path.join(basePath, expectedFilename);
|
|
499
|
+
if (fs.existsSync(fullPath)) {
|
|
500
|
+
log('Found tar file for %s: %s', imageName, fullPath);
|
|
501
|
+
return fullPath;
|
|
502
|
+
}
|
|
503
|
+
log('Checked: %s - not found', fullPath);
|
|
504
|
+
}
|
|
505
|
+
return null;
|
|
506
|
+
}
|
|
507
|
+
/**
|
|
508
|
+
* Show helpful suggestion to run kadi-build
|
|
509
|
+
*
|
|
510
|
+
* Displays actionable suggestions when a container cannot be found or added.
|
|
511
|
+
* Helps users understand what went wrong and how to fix it.
|
|
512
|
+
*
|
|
513
|
+
* @param imageName - Image that couldn't be added
|
|
514
|
+
* @param containerType - Container engine used
|
|
515
|
+
*/
|
|
516
|
+
showKadiBuildSuggestion(imageName, containerType) {
|
|
517
|
+
this.logger.log(`\n🔧 CONTAINER NOT FOUND: ${imageName}`);
|
|
518
|
+
this.logger.log(`\nThis could mean:`);
|
|
519
|
+
this.logger.log(` • The container hasn't been built yet`);
|
|
520
|
+
this.logger.log(` • The container name doesn't match what's available`);
|
|
521
|
+
this.logger.log(` • The container was built with a different tool`);
|
|
522
|
+
this.logger.log(`\n💡 SUGGESTED SOLUTIONS:`);
|
|
523
|
+
this.logger.log(`\n1. Build the container first:`);
|
|
524
|
+
this.logger.log(` ${containerType} build -t ${imageName} .`);
|
|
525
|
+
this.logger.log(`\n2. Check available containers:`);
|
|
526
|
+
this.logger.log(` ${containerType} images | grep ${imageName.split(':')[0]}`);
|
|
527
|
+
this.logger.log(`\n3. Verify the image name in your agent.json matches the built container`);
|
|
528
|
+
this.logger.log(`\n4. If using a different tag, ensure it exists:`);
|
|
529
|
+
this.logger.log(` ${containerType} images ${imageName.split(':')[0]}`);
|
|
530
|
+
}
|
|
531
|
+
/**
|
|
532
|
+
* Verify container is accessible in the registry
|
|
533
|
+
*
|
|
534
|
+
* Checks that a container with the given alias is present in the registry.
|
|
535
|
+
* Logs a warning if not found and shows available containers for debugging.
|
|
536
|
+
*
|
|
537
|
+
* @param actualAlias - Container alias to verify
|
|
538
|
+
*/
|
|
539
|
+
verifyContainerInRegistry(actualAlias) {
|
|
540
|
+
try {
|
|
541
|
+
const containers = this.registry.listContainers();
|
|
542
|
+
const foundContainer = containers.find((c) => c.alias === actualAlias);
|
|
543
|
+
if (foundContainer) {
|
|
544
|
+
log('Verified container in registry with alias: %s', foundContainer.alias);
|
|
545
|
+
}
|
|
546
|
+
else {
|
|
547
|
+
this.logger.warn(` ⚠️ Warning: Container not found in registry with alias: ${actualAlias}`);
|
|
548
|
+
this.logger.log(` Available containers: ${containers.map((c) => c.alias).join(', ')}`);
|
|
549
|
+
}
|
|
550
|
+
}
|
|
551
|
+
catch (error) {
|
|
552
|
+
// Silently ignore verification errors - registry might not support listing
|
|
553
|
+
}
|
|
554
|
+
}
|
|
555
|
+
/**
|
|
556
|
+
* Get the registry domain (without protocol)
|
|
557
|
+
*
|
|
558
|
+
* Returns the domain to use in image URLs. Prefers tunnel domain over local.
|
|
559
|
+
*
|
|
560
|
+
* @returns Registry domain (e.g., "abc123.serveo.net" or "localhost:3000")
|
|
561
|
+
*/
|
|
562
|
+
getRegistryDomain() {
|
|
563
|
+
try {
|
|
564
|
+
const preferredDomain = this.getPreferredDomain();
|
|
565
|
+
if (preferredDomain) {
|
|
566
|
+
log('Registry domain from getRegistryUrls: %s', preferredDomain);
|
|
567
|
+
return preferredDomain;
|
|
568
|
+
}
|
|
569
|
+
}
|
|
570
|
+
catch (error) {
|
|
571
|
+
log('Could not get registry URLs: %s', error);
|
|
572
|
+
}
|
|
573
|
+
// Fallback to parsing from registry info
|
|
574
|
+
if (this.registryInfo) {
|
|
575
|
+
const url = this.registryInfo.tunnelUrl || this.registryInfo.localUrl;
|
|
576
|
+
if (url) {
|
|
577
|
+
const domain = url.replace(/^https?:\/\//, '');
|
|
578
|
+
log('Registry domain from registryInfo: %s', domain);
|
|
579
|
+
return domain;
|
|
580
|
+
}
|
|
581
|
+
}
|
|
582
|
+
this.logger.error('❌ Could not determine registry domain!');
|
|
583
|
+
return '';
|
|
584
|
+
}
|
|
585
|
+
/**
|
|
586
|
+
* Get registry URL components including local and tunnel endpoints
|
|
587
|
+
*
|
|
588
|
+
* @returns Registry URLs with both local and tunnel information
|
|
589
|
+
*/
|
|
590
|
+
getRegistryUrls() {
|
|
591
|
+
if (!this.registryInfo) {
|
|
592
|
+
throw new Error('Registry not started');
|
|
593
|
+
}
|
|
594
|
+
const localUrl = this.registryInfo.localUrl;
|
|
595
|
+
const tunnelUrl = this.registryInfo.tunnelUrl || null;
|
|
596
|
+
// Extract domains (remove protocol)
|
|
597
|
+
const localDomain = localUrl.replace(/^https?:\/\//, '');
|
|
598
|
+
const tunnelDomain = tunnelUrl
|
|
599
|
+
? tunnelUrl.replace(/^https?:\/\//, '')
|
|
600
|
+
: null;
|
|
601
|
+
return {
|
|
602
|
+
localUrl,
|
|
603
|
+
localDomain,
|
|
604
|
+
tunnelUrl,
|
|
605
|
+
tunnelDomain
|
|
606
|
+
};
|
|
607
|
+
}
|
|
608
|
+
/**
|
|
609
|
+
* Get the preferred domain (tunnel if available, otherwise local)
|
|
610
|
+
*
|
|
611
|
+
* @returns Domain without protocol
|
|
612
|
+
*/
|
|
613
|
+
getPreferredDomain() {
|
|
614
|
+
const urls = this.getRegistryUrls();
|
|
615
|
+
return urls.tunnelDomain || urls.localDomain;
|
|
616
|
+
}
|
|
617
|
+
/**
|
|
618
|
+
* Check if registry is running
|
|
619
|
+
*
|
|
620
|
+
* @returns True if registry is started and accessible
|
|
621
|
+
*/
|
|
622
|
+
isRunning() {
|
|
623
|
+
return this.registry !== null && this.registryInfo !== null;
|
|
624
|
+
}
|
|
625
|
+
/**
|
|
626
|
+
* Display container mappings for debugging
|
|
627
|
+
*
|
|
628
|
+
* Shows how original image names were transformed to registry URLs.
|
|
629
|
+
* Useful for troubleshooting deployment issues.
|
|
630
|
+
*/
|
|
631
|
+
async displayContainerMappings() {
|
|
632
|
+
this.logger.log('\n🔍 Container image mappings:');
|
|
633
|
+
if (this.containerMappings.size === 0) {
|
|
634
|
+
this.logger.log(' No local images processed');
|
|
635
|
+
return;
|
|
636
|
+
}
|
|
637
|
+
for (const [originalImage, mapping] of this.containerMappings) {
|
|
638
|
+
this.logger.log(` ${mapping.serviceName}:`);
|
|
639
|
+
this.logger.log(` Original: ${originalImage}`);
|
|
640
|
+
this.logger.log(` Registry: ${mapping.registryUrl}`);
|
|
641
|
+
this.logger.log(` Alias: ${mapping.actualAlias}`);
|
|
642
|
+
}
|
|
643
|
+
}
|
|
644
|
+
/**
|
|
645
|
+
* Stop the temporary registry and cleanup resources
|
|
646
|
+
*
|
|
647
|
+
* Shuts down the registry container and tunnel. Should be called after
|
|
648
|
+
* deployment completes and providers have pulled all images.
|
|
649
|
+
*
|
|
650
|
+
* **Safe to call multiple times** - idempotent operation.
|
|
651
|
+
*/
|
|
652
|
+
async stopTemporaryRegistry() {
|
|
653
|
+
if (!this.registry) {
|
|
654
|
+
return;
|
|
655
|
+
}
|
|
656
|
+
log('Stopping temporary registry...');
|
|
657
|
+
try {
|
|
658
|
+
// Stop the registry instance
|
|
659
|
+
await this.registry.stop();
|
|
660
|
+
// Clear state
|
|
661
|
+
this.registry = null;
|
|
662
|
+
this.registryInfo = null;
|
|
663
|
+
this.containerMappings.clear();
|
|
664
|
+
log('Temporary registry stopped and cleaned up');
|
|
665
|
+
}
|
|
666
|
+
catch (error) {
|
|
667
|
+
this.logger.warn(`⚠️ Error during registry cleanup: ${error}`);
|
|
668
|
+
}
|
|
669
|
+
}
|
|
670
|
+
}
|
|
671
|
+
//# sourceMappingURL=manager.js.map
|