@uofx/cli 1.0.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/LICENSE +40 -0
- package/README.md +444 -0
- package/THIRD-PARTY-NOTICES.txt +894 -0
- package/dist/application/dtos/index.js +24 -0
- package/dist/application/dtos/request/delete-instance.request.dto.js +3 -0
- package/dist/application/dtos/request/get-config.request.dto.js +3 -0
- package/dist/application/dtos/request/get-credentials.request.dto.js +3 -0
- package/dist/application/dtos/request/index.js +27 -0
- package/dist/application/dtos/request/install-instance.request.dto.js +3 -0
- package/dist/application/dtos/request/list-charts.request.dto.js +3 -0
- package/dist/application/dtos/request/set-config.request.dto.js +16 -0
- package/dist/application/dtos/request/setup-environment.request.dto.js +16 -0
- package/dist/application/dtos/request/show-logs.request.dto.js +19 -0
- package/dist/application/dtos/request/start-instance.request.dto.js +3 -0
- package/dist/application/dtos/request/stop-instance.request.dto.js +3 -0
- package/dist/application/dtos/response/credentials.response.dto.js +3 -0
- package/dist/application/dtos/response/delete-instance.response.dto.js +3 -0
- package/dist/application/dtos/response/index.js +26 -0
- package/dist/application/dtos/response/install-instance.response.dto.js +3 -0
- package/dist/application/dtos/response/instance-list.response.dto.js +3 -0
- package/dist/application/dtos/response/instance-status.response.dto.js +3 -0
- package/dist/application/dtos/response/setup-result.response.dto.js +3 -0
- package/dist/application/dtos/response/show-logs.response.dto.js +3 -0
- package/dist/application/dtos/response/start-instance.response.dto.js +3 -0
- package/dist/application/dtos/response/stop-instance.response.dto.js +3 -0
- package/dist/application/index.js +25 -0
- package/dist/application/interfaces/index.js +24 -0
- package/dist/application/interfaces/use-case.interface.js +3 -0
- package/dist/application/use-cases/config/get-config.use-case.js +66 -0
- package/dist/application/use-cases/config/set-config.use-case.js +49 -0
- package/dist/application/use-cases/credentials/get-credentials.use-case.js +57 -0
- package/dist/application/use-cases/index.js +28 -0
- package/dist/application/use-cases/instance/delete-instance.use-case.js +81 -0
- package/dist/application/use-cases/instance/index.js +23 -0
- package/dist/application/use-cases/instance/install-instance.use-case.js +424 -0
- package/dist/application/use-cases/instance/list-charts.use-case.js +43 -0
- package/dist/application/use-cases/instance/list-instances.use-case.js +62 -0
- package/dist/application/use-cases/instance/start-instance.use-case.js +154 -0
- package/dist/application/use-cases/instance/stop-instance.use-case.js +55 -0
- package/dist/application/use-cases/logs/show-logs.use-case.js +66 -0
- package/dist/application/use-cases/setup/setup-environment.use-case.js +53 -0
- package/dist/cli.js +286 -0
- package/dist/constants/config-defaults.js +23 -0
- package/dist/constants/defaults.js +89 -0
- package/dist/constants/deployment.js +39 -0
- package/dist/constants/environments.js +93 -0
- package/dist/constants/index.js +53 -0
- package/dist/constants/oci-artifacts.js +25 -0
- package/dist/constants/paths.js +92 -0
- package/dist/constants/timeouts.js +60 -0
- package/dist/di/container.js +34 -0
- package/dist/di/index.js +22 -0
- package/dist/di/modules/application.module.js +54 -0
- package/dist/di/modules/infrastructure.module.js +206 -0
- package/dist/di/modules/interceptor.module.js +68 -0
- package/dist/di/modules/presentation.module.js +31 -0
- package/dist/di/tokens.js +149 -0
- package/dist/domain/decorators/sensitive.decorator.js +39 -0
- package/dist/domain/entities/credentials-resolver.entity.js +127 -0
- package/dist/domain/entities/credentials.entity.js +65 -0
- package/dist/domain/entities/delete-instance-validation.entity.js +100 -0
- package/dist/domain/entities/deployment-parameters.entity.js +120 -0
- package/dist/domain/entities/environment-validation.entity.js +125 -0
- package/dist/domain/entities/index.js +29 -0
- package/dist/domain/entities/instance-lifecycle-state.entity.js +100 -0
- package/dist/domain/entities/instance-list-aggregator.entity.js +104 -0
- package/dist/domain/entities/instance-metadata.entity.js +86 -0
- package/dist/domain/entities/instance-status.entity.js +79 -0
- package/dist/domain/entities/instance.entity.js +128 -0
- package/dist/domain/entities/log-filter.entity.js +141 -0
- package/dist/domain/index.js +29 -0
- package/dist/domain/interfaces/safe-loggable.interface.js +3 -0
- package/dist/domain/ports/app-config.port.js +3 -0
- package/dist/domain/ports/base-image.port.js +3 -0
- package/dist/domain/ports/chart-version.port.js +3 -0
- package/dist/domain/ports/correlation-id.port.js +3 -0
- package/dist/domain/ports/credentials.port.js +3 -0
- package/dist/domain/ports/deployment.port.js +3 -0
- package/dist/domain/ports/error-handler.port.js +3 -0
- package/dist/domain/ports/index.js +46 -0
- package/dist/domain/ports/instance-manager.port.js +3 -0
- package/dist/domain/ports/instance-metadata.port.js +8 -0
- package/dist/domain/ports/instance-storage.port.js +3 -0
- package/dist/domain/ports/k8s-deployer.port.js +3 -0
- package/dist/domain/ports/logger.port.js +14 -0
- package/dist/domain/ports/output.port.js +3 -0
- package/dist/domain/ports/runtime-environment.port.js +6 -0
- package/dist/domain/ports/user-interaction.port.js +9 -0
- package/dist/domain/ports/user-settings.port.js +3 -0
- package/dist/domain/types/index.js +22 -0
- package/dist/domain/types/logger.types.js +29 -0
- package/dist/domain/types/validation.types.js +9 -0
- package/dist/domain/value-objects/acr-credentials.value-object.js +92 -0
- package/dist/domain/value-objects/chart-version.value-object.js +124 -0
- package/dist/domain/value-objects/config-log-level.value-object.js +84 -0
- package/dist/domain/value-objects/connection-info.value-object.js +65 -0
- package/dist/domain/value-objects/index.js +25 -0
- package/dist/domain/value-objects/instance-name.value-object.js +91 -0
- package/dist/domain/value-objects/jwt-key.value-object.js +97 -0
- package/dist/domain/value-objects/mssql-password.value-object.js +140 -0
- package/dist/domain/value-objects/rsa-key-pair.value-object.js +181 -0
- package/dist/index.js +6 -0
- package/dist/infrastructure/config/app-config.interface.js +3 -0
- package/dist/infrastructure/config/app-config.service.js +280 -0
- package/dist/infrastructure/config/config-validator.js +31 -0
- package/dist/infrastructure/config/crypto.service.js +125 -0
- package/dist/infrastructure/deployment/deployment.adapter.js +118 -0
- package/dist/infrastructure/deployment/interfaces/acr-credential-manager.interface.js +3 -0
- package/dist/infrastructure/deployment/interfaces/app-manager.interface.js +3 -0
- package/dist/infrastructure/deployment/interfaces/helm-registry.interface.js +3 -0
- package/dist/infrastructure/deployment/interfaces/infra-manager.interface.js +3 -0
- package/dist/infrastructure/deployment/interfaces/k8s-job-runner.interface.js +3 -0
- package/dist/infrastructure/deployment/interfaces/mssql-database-init.interface.js +3 -0
- package/dist/infrastructure/deployment/interfaces/mssql-helm-deployment.interface.js +3 -0
- package/dist/infrastructure/deployment/interfaces/mssql-storage.interface.js +3 -0
- package/dist/infrastructure/deployment/interfaces/mssql-user-manager.interface.js +3 -0
- package/dist/infrastructure/deployment/interfaces/oci-artifact.interface.js +3 -0
- package/dist/infrastructure/deployment/interfaces/secret-manager.interface.js +3 -0
- package/dist/infrastructure/deployment/interfaces/service-manager.interface.js +3 -0
- package/dist/infrastructure/deployment/interfaces/version-compatibility.interface.js +3 -0
- package/dist/infrastructure/deployment/services/acr-credential-manager.service.js +144 -0
- package/dist/infrastructure/deployment/services/app-manager.service.js +193 -0
- package/dist/infrastructure/deployment/services/base-helm-deployment.service.js +163 -0
- package/dist/infrastructure/deployment/services/helm-registry.service.js +126 -0
- package/dist/infrastructure/deployment/services/infra-manager.service.js +130 -0
- package/dist/infrastructure/deployment/services/k8s-job-runner.service.js +194 -0
- package/dist/infrastructure/deployment/services/mssql-database-init.service.js +139 -0
- package/dist/infrastructure/deployment/services/mssql-helm-deployment.service.js +100 -0
- package/dist/infrastructure/deployment/services/mssql-storage.service.js +54 -0
- package/dist/infrastructure/deployment/services/mssql-user-manager.service.js +66 -0
- package/dist/infrastructure/deployment/services/oci-artifact.service.js +289 -0
- package/dist/infrastructure/deployment/services/secret-manager.service.js +179 -0
- package/dist/infrastructure/deployment/services/service-manager.service.js +82 -0
- package/dist/infrastructure/deployment/services/version-compatibility.service.js +291 -0
- package/dist/infrastructure/environment/interfaces/hardware-info.interface.js +3 -0
- package/dist/infrastructure/environment/interfaces/network-checker.interface.js +3 -0
- package/dist/infrastructure/environment/services/hardware-info.service.js +135 -0
- package/dist/infrastructure/environment/services/network-checker.service.js +142 -0
- package/dist/infrastructure/environment/windows-environment.adapter.js +162 -0
- package/dist/infrastructure/errors/app-error.js +73 -0
- package/dist/infrastructure/errors/error-handler.interface.js +3 -0
- package/dist/infrastructure/errors/error-handler.js +218 -0
- package/dist/infrastructure/errors/exit-codes.js +27 -0
- package/dist/infrastructure/errors/index.js +25 -0
- package/dist/infrastructure/execution/builders/base-command.builder.js +122 -0
- package/dist/infrastructure/execution/builders/host-command.builder.js +58 -0
- package/dist/infrastructure/execution/builders/windows-host-command.builder.js +50 -0
- package/dist/infrastructure/execution/builders/wsl-command.builder.js +29 -0
- package/dist/infrastructure/execution/command-builder.js +252 -0
- package/dist/infrastructure/execution/command-executor.service.js +230 -0
- package/dist/infrastructure/execution/environments/wsl-execution.environment.js +70 -0
- package/dist/infrastructure/execution/execution-environment.factory.js +53 -0
- package/dist/infrastructure/execution/index.js +25 -0
- package/dist/infrastructure/execution/interfaces/command-builder.interface.js +3 -0
- package/dist/infrastructure/execution/interfaces/command-executor.interface.js +3 -0
- package/dist/infrastructure/execution/interfaces/execution-environment-factory.interface.js +3 -0
- package/dist/infrastructure/execution/interfaces/execution-environment.interface.js +7 -0
- package/dist/infrastructure/execution/interfaces/host-command-builder.interface.js +3 -0
- package/dist/infrastructure/execution/interfaces/index.js +23 -0
- package/dist/infrastructure/execution/interfaces/script-executor.interface.js +3 -0
- package/dist/infrastructure/execution/script-executor.service.js +171 -0
- package/dist/infrastructure/http/http-client.service.js +176 -0
- package/dist/infrastructure/http/index.js +18 -0
- package/dist/infrastructure/http/interfaces/http-client.interface.js +3 -0
- package/dist/infrastructure/interceptors/index.js +8 -0
- package/dist/infrastructure/interceptors/interceptor.factory.js +44 -0
- package/dist/infrastructure/interceptors/interceptor.interface.js +3 -0
- package/dist/infrastructure/interceptors/logging.interceptor.js +171 -0
- package/dist/infrastructure/logger/correlation-id.adapter.js +68 -0
- package/dist/infrastructure/logger/index.js +23 -0
- package/dist/infrastructure/logger/interfaces/index.js +22 -0
- package/dist/infrastructure/logger/interfaces/log-reader.repository.interface.js +7 -0
- package/dist/infrastructure/logger/interfaces/log-writer.repository.interface.js +7 -0
- package/dist/infrastructure/logger/logger.adapter.js +274 -0
- package/dist/infrastructure/logger/services/file-log-reader.repository.js +148 -0
- package/dist/infrastructure/logger/services/file-log-writer.repository.js +307 -0
- package/dist/infrastructure/logger/services/index.js +22 -0
- package/dist/infrastructure/persistence/index.js +25 -0
- package/dist/infrastructure/persistence/instance-metadata.adapter.js +100 -0
- package/dist/infrastructure/persistence/instance-storage.adapter.js +64 -0
- package/dist/infrastructure/persistence/interfaces/config.repository.interface.js +3 -0
- package/dist/infrastructure/persistence/interfaces/index.js +22 -0
- package/dist/infrastructure/persistence/interfaces/instance.repository.interface.js +3 -0
- package/dist/infrastructure/persistence/services/file-system-config.repository.js +168 -0
- package/dist/infrastructure/persistence/services/file-system-instance.repository.js +170 -0
- package/dist/infrastructure/persistence/services/index.js +22 -0
- package/dist/infrastructure/persistence/user-settings.adapter.js +55 -0
- package/dist/infrastructure/platform-detector.js +71 -0
- package/dist/infrastructure/platforms/windows/interfaces/microk8s.interface.js +3 -0
- package/dist/infrastructure/platforms/windows/interfaces/rootfs-manager.interface.js +3 -0
- package/dist/infrastructure/platforms/windows/interfaces/windows-features.interface.js +3 -0
- package/dist/infrastructure/platforms/windows/interfaces/windows-info.interface.js +3 -0
- package/dist/infrastructure/platforms/windows/interfaces/wsl-config.interface.js +3 -0
- package/dist/infrastructure/platforms/windows/interfaces/wsl-info.interface.js +3 -0
- package/dist/infrastructure/platforms/windows/interfaces/wsl-instance-inspection.interface.js +3 -0
- package/dist/infrastructure/platforms/windows/interfaces/wsl-instance-lifecycle.interface.js +3 -0
- package/dist/infrastructure/platforms/windows/interfaces/wsl-instance-naming.interface.js +3 -0
- package/dist/infrastructure/platforms/windows/interfaces/wsl-manager.interface.js +3 -0
- package/dist/infrastructure/platforms/windows/interfaces/wsl-resources.interface.js +8 -0
- package/dist/infrastructure/platforms/windows/interfaces/wsl-updater.interface.js +3 -0
- package/dist/infrastructure/platforms/windows/interfaces/wslconfig-parser.interface.js +3 -0
- package/dist/infrastructure/platforms/windows/parsers/wsl-version.parser.js +133 -0
- package/dist/infrastructure/platforms/windows/services/microk8s.service.js +168 -0
- package/dist/infrastructure/platforms/windows/services/rootfs-manager.service.js +336 -0
- package/dist/infrastructure/platforms/windows/services/windows-features.service.js +191 -0
- package/dist/infrastructure/platforms/windows/services/windows-info.service.js +138 -0
- package/dist/infrastructure/platforms/windows/services/wsl-config.service.js +171 -0
- package/dist/infrastructure/platforms/windows/services/wsl-info.service.js +226 -0
- package/dist/infrastructure/platforms/windows/services/wsl-instance-inspection.service.js +325 -0
- package/dist/infrastructure/platforms/windows/services/wsl-instance-lifecycle.service.js +442 -0
- package/dist/infrastructure/platforms/windows/services/wsl-instance-naming.service.js +93 -0
- package/dist/infrastructure/platforms/windows/services/wsl-updater.service.js +273 -0
- package/dist/infrastructure/platforms/windows/services/wslconfig-parser.service.js +222 -0
- package/dist/infrastructure/platforms/windows/wsl-base-image.adapter.js +41 -0
- package/dist/infrastructure/platforms/windows/wsl-instance-manager.adapter.js +150 -0
- package/dist/infrastructure/utils/error-formatter.util.js +29 -0
- package/dist/infrastructure/utils/file-operations.util.js +201 -0
- package/dist/infrastructure/utils/input-validator.util.js +152 -0
- package/dist/infrastructure/utils/retry.util.js +98 -0
- package/dist/presentation/controllers/config.controller.js +146 -0
- package/dist/presentation/controllers/credentials.controller.js +105 -0
- package/dist/presentation/controllers/index.js +25 -0
- package/dist/presentation/controllers/instance.controller.js +363 -0
- package/dist/presentation/controllers/logs.controller.js +103 -0
- package/dist/presentation/controllers/setup.controller.js +175 -0
- package/dist/presentation/interfaces/cli-options.interface.js +8 -0
- package/dist/presentation/prompts/acr-credentials.prompt.js +76 -0
- package/dist/presentation/prompts/index.js +21 -0
- package/dist/presentation/ui/cli-progress.service.js +193 -0
- package/dist/presentation/ui/constants/output-symbols.js +42 -0
- package/dist/presentation/ui/index.js +27 -0
- package/dist/presentation/ui/interaction.service.js +276 -0
- package/dist/presentation/ui/interfaces/cli-progress.interface.js +9 -0
- package/dist/presentation/ui/interfaces/output-formatter.interface.js +23 -0
- package/dist/presentation/ui/log-level.enum.js +66 -0
- package/dist/presentation/ui/output-builder.service.js +378 -0
- package/dist/presentation/ui/output-formatter.service.js +393 -0
- package/package.json +65 -0
|
@@ -0,0 +1,163 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.BaseHelmDeploymentService = void 0;
|
|
4
|
+
const retry_util_1 = require("../../utils/retry.util");
|
|
5
|
+
/**
|
|
6
|
+
* Helm 部署基類
|
|
7
|
+
*
|
|
8
|
+
* 提供 Helm Chart 部署的共通流程:
|
|
9
|
+
* 1. Registry 登入
|
|
10
|
+
* 2. 拉取 Chart
|
|
11
|
+
* 3. 前置處理(可選)
|
|
12
|
+
* 4. 安裝 Chart
|
|
13
|
+
* 5. 後置處理(可選)
|
|
14
|
+
* 6. 清理 Chart 檔案
|
|
15
|
+
*
|
|
16
|
+
* 子類需實作:
|
|
17
|
+
* - getDeploymentConfig(): 取得部署配置
|
|
18
|
+
* - installChart(): 安裝 Chart 邏輯
|
|
19
|
+
*
|
|
20
|
+
* 可選覆寫:
|
|
21
|
+
* - preInstall(): 安裝前處理
|
|
22
|
+
* - postInstall(): 安裝後處理
|
|
23
|
+
*/
|
|
24
|
+
class BaseHelmDeploymentService {
|
|
25
|
+
constructor(helmRegistry, logger, envFactory) {
|
|
26
|
+
this.helmRegistry = helmRegistry;
|
|
27
|
+
this.logger = logger;
|
|
28
|
+
this.envFactory = envFactory;
|
|
29
|
+
}
|
|
30
|
+
/**
|
|
31
|
+
* 部署 Helm Chart(模板方法)
|
|
32
|
+
* @param instanceName - WSL 實例名稱
|
|
33
|
+
* @param params - 部署參數
|
|
34
|
+
* @param chartVersion - Chart 版本
|
|
35
|
+
* @param output - 輸出介面
|
|
36
|
+
*/
|
|
37
|
+
async deployWithHelm(instanceName, params, chartVersion, output) {
|
|
38
|
+
const config = this.getDeploymentConfig(chartVersion);
|
|
39
|
+
this.logger.debug(`[${this.getServiceName()}] Starting deployment`, {
|
|
40
|
+
chart: config.chartName,
|
|
41
|
+
version: config.chartVersion,
|
|
42
|
+
instance: instanceName,
|
|
43
|
+
});
|
|
44
|
+
// 1. Registry 登入
|
|
45
|
+
await this.helmRegistry.login(instanceName, params.acrCredentials);
|
|
46
|
+
// 2. 拉取 Chart
|
|
47
|
+
const chartInfo = {
|
|
48
|
+
chartName: config.chartName,
|
|
49
|
+
version: config.chartVersion,
|
|
50
|
+
acrName: params.acrCredentials.name,
|
|
51
|
+
repository: config.repository,
|
|
52
|
+
};
|
|
53
|
+
const chartPath = await this.helmRegistry.pullChart(instanceName, chartInfo, output);
|
|
54
|
+
try {
|
|
55
|
+
// 3. 前置處理
|
|
56
|
+
await this.preInstall(instanceName, params, output);
|
|
57
|
+
// 4. 安裝 Chart
|
|
58
|
+
await this.installChart(instanceName, chartPath, params, output);
|
|
59
|
+
// 5. 後置處理
|
|
60
|
+
await this.postInstall(instanceName, params, output);
|
|
61
|
+
this.logger.debug(`[${this.getServiceName()}] Deployment completed`, {
|
|
62
|
+
chart: config.chartName,
|
|
63
|
+
instance: instanceName,
|
|
64
|
+
});
|
|
65
|
+
}
|
|
66
|
+
finally {
|
|
67
|
+
// 6. 清理 Chart 檔案
|
|
68
|
+
await this.helmRegistry.cleanupChart(instanceName, chartPath);
|
|
69
|
+
}
|
|
70
|
+
}
|
|
71
|
+
/**
|
|
72
|
+
* 安裝前處理(可選覆寫)
|
|
73
|
+
* @param instanceName - WSL 實例名稱
|
|
74
|
+
* @param params - 部署參數
|
|
75
|
+
* @param output - 輸出介面
|
|
76
|
+
*/
|
|
77
|
+
async preInstall(_instanceName, _params, _output) {
|
|
78
|
+
// 預設不做任何處理,子類可覆寫
|
|
79
|
+
}
|
|
80
|
+
/**
|
|
81
|
+
* 安裝後處理(可選覆寫)
|
|
82
|
+
* @param instanceName - WSL 實例名稱
|
|
83
|
+
* @param params - 部署參數
|
|
84
|
+
* @param output - 輸出介面
|
|
85
|
+
*/
|
|
86
|
+
async postInstall(_instanceName, _params, _output) {
|
|
87
|
+
// 預設不做任何處理,子類可覆寫
|
|
88
|
+
}
|
|
89
|
+
// =========================================================================
|
|
90
|
+
// 共享輔助方法
|
|
91
|
+
// =========================================================================
|
|
92
|
+
/**
|
|
93
|
+
* 建立 Helm upgrade --install 命令建構器
|
|
94
|
+
* @param instanceName - WSL 實例名稱
|
|
95
|
+
* @param options - Helm 安裝選項
|
|
96
|
+
* @returns ICommandBuilder 實例
|
|
97
|
+
*/
|
|
98
|
+
createHelmUpgradeBuilder(instanceName, options) {
|
|
99
|
+
if (!this.envFactory) {
|
|
100
|
+
throw new Error('ExecutionEnvironmentFactory is required for createHelmUpgradeBuilder');
|
|
101
|
+
}
|
|
102
|
+
const env = this.envFactory.getForInstance(instanceName);
|
|
103
|
+
const builder = env.helm('upgrade')
|
|
104
|
+
.arg('--install')
|
|
105
|
+
.arg('--force');
|
|
106
|
+
// 添加 --wait 和 --timeout
|
|
107
|
+
if (options.wait) {
|
|
108
|
+
builder.arg('--wait');
|
|
109
|
+
if (options.timeout) {
|
|
110
|
+
builder.arg('--timeout', options.timeout);
|
|
111
|
+
}
|
|
112
|
+
}
|
|
113
|
+
// Release 名稱和 Chart 路徑
|
|
114
|
+
builder.arg(options.releaseName);
|
|
115
|
+
builder.arg(`"${options.chartPath}"`);
|
|
116
|
+
// 命名空間
|
|
117
|
+
const namespace = options.namespace ?? 'uofx';
|
|
118
|
+
builder.arg('-n', namespace);
|
|
119
|
+
builder.arg('--create-namespace');
|
|
120
|
+
// 添加 --set 參數
|
|
121
|
+
if (options.setArgs) {
|
|
122
|
+
for (const setArg of options.setArgs) {
|
|
123
|
+
builder.arg('--set', setArg);
|
|
124
|
+
}
|
|
125
|
+
}
|
|
126
|
+
// 添加敏感的 --set 參數
|
|
127
|
+
if (options.sensitiveSetArgs) {
|
|
128
|
+
for (const { key, value } of options.sensitiveSetArgs) {
|
|
129
|
+
builder.sensitiveArg('--set', `${key}="${value}"`);
|
|
130
|
+
}
|
|
131
|
+
}
|
|
132
|
+
// 從 stdin 讀取 values
|
|
133
|
+
if (options.sensitiveStdin) {
|
|
134
|
+
builder.arg('-f', '-');
|
|
135
|
+
builder.sensitiveStdin(options.sensitiveStdin);
|
|
136
|
+
}
|
|
137
|
+
return builder;
|
|
138
|
+
}
|
|
139
|
+
/**
|
|
140
|
+
* 執行 Helm 命令並帶有重試機制
|
|
141
|
+
* @param builder - ICommandBuilder 實例
|
|
142
|
+
* @param chartName - Chart 名稱(用於錯誤訊息)
|
|
143
|
+
* @param output - 輸出介面
|
|
144
|
+
*/
|
|
145
|
+
async executeWithRetry(builder, chartName, output) {
|
|
146
|
+
const serviceName = this.getServiceName();
|
|
147
|
+
await (0, retry_util_1.retryWithBackoff)(async () => {
|
|
148
|
+
const result = await builder.exec();
|
|
149
|
+
if (result.exitCode !== 0) {
|
|
150
|
+
throw new Error(result.stderr);
|
|
151
|
+
}
|
|
152
|
+
}, (0, retry_util_1.createHelmRetryOptions)({
|
|
153
|
+
onRetry: (attempt) => {
|
|
154
|
+
output?.warning(`Retrying install (${attempt}/3)...`).flush();
|
|
155
|
+
this.logger.warn(`[${serviceName}] Retrying install (${attempt}/3)...`);
|
|
156
|
+
},
|
|
157
|
+
})).catch((error) => {
|
|
158
|
+
throw new Error(`Failed to install ${chartName} chart: ${error.message}`);
|
|
159
|
+
});
|
|
160
|
+
}
|
|
161
|
+
}
|
|
162
|
+
exports.BaseHelmDeploymentService = BaseHelmDeploymentService;
|
|
163
|
+
//# sourceMappingURL=base-helm-deployment.service.js.map
|
|
@@ -0,0 +1,126 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) {
|
|
3
|
+
var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
|
|
4
|
+
if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
|
|
5
|
+
else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
|
|
6
|
+
return c > 3 && r && Object.defineProperty(target, key, r), r;
|
|
7
|
+
};
|
|
8
|
+
var __metadata = (this && this.__metadata) || function (k, v) {
|
|
9
|
+
if (typeof Reflect === "object" && typeof Reflect.metadata === "function") return Reflect.metadata(k, v);
|
|
10
|
+
};
|
|
11
|
+
var __param = (this && this.__param) || function (paramIndex, decorator) {
|
|
12
|
+
return function (target, key) { decorator(target, key, paramIndex); }
|
|
13
|
+
};
|
|
14
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
15
|
+
exports.HelmRegistryService = void 0;
|
|
16
|
+
const tsyringe_1 = require("tsyringe");
|
|
17
|
+
const tokens_1 = require("../../../di/tokens");
|
|
18
|
+
const acr_credentials_value_object_1 = require("../../../domain/value-objects/acr-credentials.value-object");
|
|
19
|
+
const sensitive_decorator_1 = require("../../../domain/decorators/sensitive.decorator");
|
|
20
|
+
/**
|
|
21
|
+
* Helm Registry 服務
|
|
22
|
+
*
|
|
23
|
+
* 負責管理 Helm Registry 相關操作,提供:
|
|
24
|
+
* - ACR (Azure Container Registry) 登入
|
|
25
|
+
* - Helm Chart 拉取
|
|
26
|
+
* - Chart 檔案清理
|
|
27
|
+
*
|
|
28
|
+
* 此服務整合了原先散落在 AppManagerService、InfraManagerService、
|
|
29
|
+
* ServiceManagerService 中的重複邏輯,遵循 DRY (Don't Repeat Yourself) 原則
|
|
30
|
+
*
|
|
31
|
+
* 實作 IHelmRegistry Domain Port
|
|
32
|
+
*/
|
|
33
|
+
let HelmRegistryService = class HelmRegistryService {
|
|
34
|
+
constructor(envFactory, logger) {
|
|
35
|
+
this.envFactory = envFactory;
|
|
36
|
+
this.logger = logger;
|
|
37
|
+
}
|
|
38
|
+
/**
|
|
39
|
+
* 登入 Helm Registry (ACR)
|
|
40
|
+
*
|
|
41
|
+
* 使用 `helm registry login` 命令透過 stdin 傳遞密碼,避免密碼出現在命令列歷史中
|
|
42
|
+
*
|
|
43
|
+
* @param instanceName - WSL 實例名稱
|
|
44
|
+
* @param credentials - ACR 登入憑證
|
|
45
|
+
* @throws {Error} 登入失敗時拋出錯誤
|
|
46
|
+
*/
|
|
47
|
+
async login(instanceName, credentials) {
|
|
48
|
+
const { name: acrName, account: acrAccount, password: acrPassword } = credentials;
|
|
49
|
+
const registry = `${acrName}.azurecr.io`;
|
|
50
|
+
this.logger.debug(`[HelmRegistry] Login to registry ${registry}...`);
|
|
51
|
+
const env = this.envFactory.getForInstance(instanceName);
|
|
52
|
+
const builder = env.helm('registry login')
|
|
53
|
+
.arg(registry)
|
|
54
|
+
.arg('--username', acrAccount)
|
|
55
|
+
.arg('--password-stdin')
|
|
56
|
+
.sensitiveStdin(acrPassword);
|
|
57
|
+
const result = await builder.exec();
|
|
58
|
+
if (result.exitCode !== 0) {
|
|
59
|
+
throw new Error(`Failed to login to helm registry: ${result.stderr}`);
|
|
60
|
+
}
|
|
61
|
+
this.logger.debug(`[HelmRegistry] Successfully logged in to ${registry}`);
|
|
62
|
+
}
|
|
63
|
+
/**
|
|
64
|
+
* 從 ACR 拉取 Helm Chart
|
|
65
|
+
*
|
|
66
|
+
* Chart 會被下載到 /tmp 目錄
|
|
67
|
+
*
|
|
68
|
+
* @param instanceName - WSL 實例名稱
|
|
69
|
+
* @param chartInfo - Chart 資訊 (名稱、版本、ACR 名稱)
|
|
70
|
+
* @param progress - 進度通知(可選)
|
|
71
|
+
* @returns Promise<string> - Chart 檔案的完整路徑 (如 '/tmp/uofx-1.0.0.tgz')
|
|
72
|
+
* @throws {Error} 拉取失敗時拋出錯誤
|
|
73
|
+
*/
|
|
74
|
+
async pullChart(instanceName, chartInfo, output) {
|
|
75
|
+
const { chartName, version, acrName, repository = 'helm' } = chartInfo;
|
|
76
|
+
const registry = `${acrName}.azurecr.io`;
|
|
77
|
+
const chartUrl = `oci://${registry}/${repository}/${chartName}`;
|
|
78
|
+
const destDir = '/tmp';
|
|
79
|
+
this.logger.debug(`[HelmRegistry] Pulling chart ${chartUrl} version ${version}...`);
|
|
80
|
+
output?.item('arrow', `Pulling ${chartName} chart (v${version})...`).flush();
|
|
81
|
+
const env = this.envFactory.getForInstance(instanceName);
|
|
82
|
+
const builder = env.helm('pull')
|
|
83
|
+
.arg(chartUrl)
|
|
84
|
+
.arg('--version', version)
|
|
85
|
+
.arg('--destination', destDir);
|
|
86
|
+
const result = await builder.exec();
|
|
87
|
+
if (result.exitCode !== 0) {
|
|
88
|
+
throw new Error(`Failed to pull ${chartName} chart: ${result.stderr}`);
|
|
89
|
+
}
|
|
90
|
+
const chartPath = `${destDir}/${chartName}-${version}.tgz`;
|
|
91
|
+
this.logger.debug(`[HelmRegistry] Chart pulled successfully: ${chartPath}`);
|
|
92
|
+
return chartPath;
|
|
93
|
+
}
|
|
94
|
+
/**
|
|
95
|
+
* 清理暫存的 Chart 檔案
|
|
96
|
+
*
|
|
97
|
+
* 從 WSL 實例的 /tmp 目錄中刪除指定的 Chart 檔案
|
|
98
|
+
* 此操作不會拋出錯誤,即使檔案不存在也會靜默成功
|
|
99
|
+
*
|
|
100
|
+
* @param instanceName - WSL 實例名稱
|
|
101
|
+
* @param chartPath - Chart 檔案的完整路徑
|
|
102
|
+
*/
|
|
103
|
+
async cleanupChart(instanceName, chartPath) {
|
|
104
|
+
this.logger.debug(`[HelmRegistry] Cleaning up chart: ${chartPath}`);
|
|
105
|
+
const env = this.envFactory.getForInstance(instanceName);
|
|
106
|
+
const builder = env.shell('rm')
|
|
107
|
+
.arg('-f')
|
|
108
|
+
.arg(chartPath);
|
|
109
|
+
await builder.exec();
|
|
110
|
+
this.logger.debug(`[HelmRegistry] Chart cleaned up: ${chartPath}`);
|
|
111
|
+
}
|
|
112
|
+
};
|
|
113
|
+
exports.HelmRegistryService = HelmRegistryService;
|
|
114
|
+
__decorate([
|
|
115
|
+
__param(1, (0, sensitive_decorator_1.Sensitive)()),
|
|
116
|
+
__metadata("design:type", Function),
|
|
117
|
+
__metadata("design:paramtypes", [String, acr_credentials_value_object_1.AcrCredentials]),
|
|
118
|
+
__metadata("design:returntype", Promise)
|
|
119
|
+
], HelmRegistryService.prototype, "login", null);
|
|
120
|
+
exports.HelmRegistryService = HelmRegistryService = __decorate([
|
|
121
|
+
(0, tsyringe_1.injectable)(),
|
|
122
|
+
__param(0, (0, tsyringe_1.inject)(tokens_1.TOKENS.Internal.IExecutionEnvironmentFactory)),
|
|
123
|
+
__param(1, (0, tsyringe_1.inject)(tokens_1.TOKENS.ILoggerPort)),
|
|
124
|
+
__metadata("design:paramtypes", [Object, Object])
|
|
125
|
+
], HelmRegistryService);
|
|
126
|
+
//# sourceMappingURL=helm-registry.service.js.map
|
|
@@ -0,0 +1,130 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) {
|
|
3
|
+
var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
|
|
4
|
+
if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
|
|
5
|
+
else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
|
|
6
|
+
return c > 3 && r && Object.defineProperty(target, key, r), r;
|
|
7
|
+
};
|
|
8
|
+
var __metadata = (this && this.__metadata) || function (k, v) {
|
|
9
|
+
if (typeof Reflect === "object" && typeof Reflect.metadata === "function") return Reflect.metadata(k, v);
|
|
10
|
+
};
|
|
11
|
+
var __param = (this && this.__param) || function (paramIndex, decorator) {
|
|
12
|
+
return function (target, key) { decorator(target, key, paramIndex); }
|
|
13
|
+
};
|
|
14
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
15
|
+
exports.InfraManagerService = void 0;
|
|
16
|
+
const tsyringe_1 = require("tsyringe");
|
|
17
|
+
const tokens_1 = require("../../../di/tokens");
|
|
18
|
+
const deployment_parameters_entity_1 = require("../../../domain/entities/deployment-parameters.entity");
|
|
19
|
+
const sensitive_decorator_1 = require("../../../domain/decorators/sensitive.decorator");
|
|
20
|
+
const base_helm_deployment_service_1 = require("./base-helm-deployment.service");
|
|
21
|
+
const paths_1 = require("../../../constants/paths");
|
|
22
|
+
/**
|
|
23
|
+
* 基礎設施管理服務
|
|
24
|
+
*
|
|
25
|
+
* 負責 uofx-infra Helm chart 的部署管理
|
|
26
|
+
* 繼承 BaseHelmDeploymentService 使用模板方法模式
|
|
27
|
+
*/
|
|
28
|
+
let InfraManagerService = class InfraManagerService extends base_helm_deployment_service_1.BaseHelmDeploymentService {
|
|
29
|
+
constructor(envFactory, helmRegistry, logger) {
|
|
30
|
+
super(helmRegistry, logger, envFactory);
|
|
31
|
+
}
|
|
32
|
+
/**
|
|
33
|
+
* 部署 uofx-infra 基礎設施
|
|
34
|
+
*/
|
|
35
|
+
async deployInfra(instanceName, params, chartVersion, output) {
|
|
36
|
+
output?.item('arrow', 'Deploying uofx-infra chart...').flush();
|
|
37
|
+
await this.deployWithHelm(instanceName, params, chartVersion, output);
|
|
38
|
+
}
|
|
39
|
+
// =========================================================================
|
|
40
|
+
// BaseHelmDeploymentService 抽象方法實作
|
|
41
|
+
// =========================================================================
|
|
42
|
+
getServiceName() {
|
|
43
|
+
return 'Infra';
|
|
44
|
+
}
|
|
45
|
+
getDeploymentConfig(chartVersion) {
|
|
46
|
+
return {
|
|
47
|
+
chartName: 'uofx-infra',
|
|
48
|
+
chartVersion,
|
|
49
|
+
};
|
|
50
|
+
}
|
|
51
|
+
/**
|
|
52
|
+
* 安裝前處理:建立儲存目錄
|
|
53
|
+
*/
|
|
54
|
+
async preInstall(instanceName, _params, _output) {
|
|
55
|
+
await this.createStorageDirectories(instanceName);
|
|
56
|
+
}
|
|
57
|
+
async installChart(instanceName, chartPath, params, output) {
|
|
58
|
+
const { name: acrName, account: acrAccount, password: acrPassword } = params.acrCredentials;
|
|
59
|
+
const registry = `${acrName}.azurecr.io`;
|
|
60
|
+
const builder = this.createHelmUpgradeBuilder(instanceName, {
|
|
61
|
+
releaseName: 'uofx-infra',
|
|
62
|
+
chartPath,
|
|
63
|
+
setArgs: [
|
|
64
|
+
`k8sType="microk8s"`,
|
|
65
|
+
`traefik.web.TLS.enable=true`,
|
|
66
|
+
`traefik.admin.TLS.enable=true`,
|
|
67
|
+
`imageCredentials.registry="${registry}"`,
|
|
68
|
+
`imageCredentials.username="${acrAccount}"`,
|
|
69
|
+
`storage.type="local"`,
|
|
70
|
+
`storage.local.defaultPath="${paths_1.LINUX_PATHS.UOFX_STORAGE_BASE}"`,
|
|
71
|
+
`storage.local.mqShare.path="/rabbitmq"`,
|
|
72
|
+
`storage.local.lokiShare.path="/loki"`,
|
|
73
|
+
`storage.local.redisShare.path="/redis"`,
|
|
74
|
+
`storage.local.fileShare.path="/file"`,
|
|
75
|
+
`storage.local.syncShare.path="/system"`,
|
|
76
|
+
`storage.local.searchShare.path="/search"`,
|
|
77
|
+
`loki.retentionPeriod="365d"`,
|
|
78
|
+
`installType="install"`,
|
|
79
|
+
`enabledSearch=true`,
|
|
80
|
+
],
|
|
81
|
+
sensitiveSetArgs: [
|
|
82
|
+
{ key: 'imageCredentials.password', value: acrPassword },
|
|
83
|
+
],
|
|
84
|
+
});
|
|
85
|
+
this.logger.debug('[Infra] Deploying infrastructure chart', {
|
|
86
|
+
chart: 'uofx-infra',
|
|
87
|
+
chartPath: chartPath,
|
|
88
|
+
namespace: 'uofx',
|
|
89
|
+
instance: instanceName,
|
|
90
|
+
});
|
|
91
|
+
output?.indent().item('arrow', 'Installing uofx-infra chart...').outdent().flush();
|
|
92
|
+
await this.executeWithRetry(builder, 'uofx-infra', output);
|
|
93
|
+
}
|
|
94
|
+
// =========================================================================
|
|
95
|
+
// 私有方法
|
|
96
|
+
// =========================================================================
|
|
97
|
+
/**
|
|
98
|
+
* 建立持久化存儲目錄
|
|
99
|
+
*/
|
|
100
|
+
async createStorageDirectories(instanceName) {
|
|
101
|
+
this.logger.debug(`[Infra] Creating storage directories for ${instanceName}...`);
|
|
102
|
+
const baseDir = paths_1.LINUX_PATHS.UOFX_STORAGE_BASE;
|
|
103
|
+
const subDirs = ['rabbitmq', 'loki', 'redis', 'file', 'system', 'search'];
|
|
104
|
+
const directories = subDirs.map(sub => `${baseDir}/${sub}`);
|
|
105
|
+
const env = this.envFactory.getForInstance(instanceName);
|
|
106
|
+
const builder = env.shell('mkdir')
|
|
107
|
+
.arg('-p')
|
|
108
|
+
.args(...directories);
|
|
109
|
+
const result = await builder.exec();
|
|
110
|
+
if (result.exitCode !== 0) {
|
|
111
|
+
throw new Error(`Failed to create storage directories: ${result.stderr}`);
|
|
112
|
+
}
|
|
113
|
+
this.logger.debug('[Infra] Storage directories created');
|
|
114
|
+
}
|
|
115
|
+
};
|
|
116
|
+
exports.InfraManagerService = InfraManagerService;
|
|
117
|
+
__decorate([
|
|
118
|
+
__param(1, (0, sensitive_decorator_1.Sensitive)()),
|
|
119
|
+
__metadata("design:type", Function),
|
|
120
|
+
__metadata("design:paramtypes", [String, deployment_parameters_entity_1.DeploymentParameters, String, Object]),
|
|
121
|
+
__metadata("design:returntype", Promise)
|
|
122
|
+
], InfraManagerService.prototype, "deployInfra", null);
|
|
123
|
+
exports.InfraManagerService = InfraManagerService = __decorate([
|
|
124
|
+
(0, tsyringe_1.injectable)(),
|
|
125
|
+
__param(0, (0, tsyringe_1.inject)(tokens_1.TOKENS.Internal.IExecutionEnvironmentFactory)),
|
|
126
|
+
__param(1, (0, tsyringe_1.inject)(tokens_1.TOKENS.Internal.IHelmRegistry)),
|
|
127
|
+
__param(2, (0, tsyringe_1.inject)(tokens_1.TOKENS.ILoggerPort)),
|
|
128
|
+
__metadata("design:paramtypes", [Object, Object, Object])
|
|
129
|
+
], InfraManagerService);
|
|
130
|
+
//# sourceMappingURL=infra-manager.service.js.map
|
|
@@ -0,0 +1,194 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) {
|
|
3
|
+
var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
|
|
4
|
+
if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
|
|
5
|
+
else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
|
|
6
|
+
return c > 3 && r && Object.defineProperty(target, key, r), r;
|
|
7
|
+
};
|
|
8
|
+
var __metadata = (this && this.__metadata) || function (k, v) {
|
|
9
|
+
if (typeof Reflect === "object" && typeof Reflect.metadata === "function") return Reflect.metadata(k, v);
|
|
10
|
+
};
|
|
11
|
+
var __param = (this && this.__param) || function (paramIndex, decorator) {
|
|
12
|
+
return function (target, key) { decorator(target, key, paramIndex); }
|
|
13
|
+
};
|
|
14
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
15
|
+
exports.K8sJobRunnerService = void 0;
|
|
16
|
+
const tsyringe_1 = require("tsyringe");
|
|
17
|
+
const tokens_1 = require("../../../di/tokens");
|
|
18
|
+
const deployment_parameters_entity_1 = require("../../../domain/entities/deployment-parameters.entity");
|
|
19
|
+
const sensitive_decorator_1 = require("../../../domain/decorators/sensitive.decorator");
|
|
20
|
+
/**
|
|
21
|
+
* Kubernetes Job 執行服務
|
|
22
|
+
*
|
|
23
|
+
* 負責執行 Kubernetes Job 的完整生命週期
|
|
24
|
+
*/
|
|
25
|
+
let K8sJobRunnerService = class K8sJobRunnerService {
|
|
26
|
+
constructor(envFactory, userManagerService, logger) {
|
|
27
|
+
this.envFactory = envFactory;
|
|
28
|
+
this.userManagerService = userManagerService;
|
|
29
|
+
this.logger = logger;
|
|
30
|
+
}
|
|
31
|
+
/**
|
|
32
|
+
* 建立 Image Pull Secret (用於拉取 Job 映像檔)
|
|
33
|
+
* @param instanceName WSL 實例名稱
|
|
34
|
+
* @param params 部署參數
|
|
35
|
+
*/
|
|
36
|
+
async createImagePullSecret(instanceName, params) {
|
|
37
|
+
const { name: acrName, account: acrAccount, password: acrPassword } = params.acrCredentials;
|
|
38
|
+
const registry = `${acrName}.azurecr.io`;
|
|
39
|
+
const env = this.envFactory.getForInstance(instanceName);
|
|
40
|
+
const builder = env.kubectl('create secret docker-registry')
|
|
41
|
+
.arg('image-pull-secret-test')
|
|
42
|
+
.arg('-n', 'mssql')
|
|
43
|
+
.arg(`--docker-server=${registry}`)
|
|
44
|
+
.arg(`--docker-username=${acrAccount}`)
|
|
45
|
+
.sensitiveArg('--docker-password', acrPassword);
|
|
46
|
+
// 忽略錯誤如果 Secret 已存在
|
|
47
|
+
const deleteBuilder = env.kubectl('delete')
|
|
48
|
+
.arg('secret')
|
|
49
|
+
.arg('image-pull-secret-test')
|
|
50
|
+
.arg('-n', 'mssql');
|
|
51
|
+
await deleteBuilder.exec();
|
|
52
|
+
const result = await builder.exec();
|
|
53
|
+
if (result.exitCode !== 0) {
|
|
54
|
+
throw new Error(`Failed to create image pull secret: ${result.stderr}`);
|
|
55
|
+
}
|
|
56
|
+
}
|
|
57
|
+
/**
|
|
58
|
+
* 刪除 Image Pull Secret
|
|
59
|
+
* @param instanceName WSL 實例名稱
|
|
60
|
+
*/
|
|
61
|
+
async deleteImagePullSecret(instanceName) {
|
|
62
|
+
const env = this.envFactory.getForInstance(instanceName);
|
|
63
|
+
const builder = env.kubectl('delete')
|
|
64
|
+
.arg('secret')
|
|
65
|
+
.arg('image-pull-secret-test')
|
|
66
|
+
.arg('-n', 'mssql');
|
|
67
|
+
await builder.exec();
|
|
68
|
+
}
|
|
69
|
+
/**
|
|
70
|
+
* 執行 Kubernetes Job
|
|
71
|
+
* @param instanceName WSL 實例名稱
|
|
72
|
+
* @param jobName Job 名稱
|
|
73
|
+
* @param imageName 映像檔名稱
|
|
74
|
+
* @param version 映像檔版本
|
|
75
|
+
* @param params 部署參數
|
|
76
|
+
*/
|
|
77
|
+
async runJob(instanceName, jobName, imageName, version, params) {
|
|
78
|
+
const registry = `${params.acrCredentials.name}.azurecr.io`;
|
|
79
|
+
const image = `${registry}/${imageName}:${version}`;
|
|
80
|
+
const env = this.envFactory.getForInstance(instanceName);
|
|
81
|
+
// Job YAML 模板
|
|
82
|
+
const jobYaml = {
|
|
83
|
+
apiVersion: 'batch/v1',
|
|
84
|
+
kind: 'Job',
|
|
85
|
+
metadata: {
|
|
86
|
+
name: jobName,
|
|
87
|
+
namespace: 'mssql'
|
|
88
|
+
},
|
|
89
|
+
spec: {
|
|
90
|
+
template: {
|
|
91
|
+
spec: {
|
|
92
|
+
imagePullSecrets: [{ name: 'image-pull-secret-test' }],
|
|
93
|
+
containers: [{
|
|
94
|
+
name: 'init-db',
|
|
95
|
+
image: image,
|
|
96
|
+
env: [
|
|
97
|
+
{ name: 'SQL_HOST', value: 'database-mssql-latest.mssql' },
|
|
98
|
+
{ name: 'SQL_USER', value: 'sa' },
|
|
99
|
+
{ name: 'SQL_PASSWORD', value: params.saPassword.toString() },
|
|
100
|
+
{ name: 'SQL_DATABASE', value: jobName === 'db-init' ? 'uofx_dev' : 'uofx_search' },
|
|
101
|
+
{ name: 'ADMIN_PASSWORD', value: params.adminPassword.toString() },
|
|
102
|
+
{ name: 'DISABLE_COLOR', value: '1' }
|
|
103
|
+
],
|
|
104
|
+
command: ['/bin/bash', '-c', 'cd /src && ./uofxDbHelper.sh -A']
|
|
105
|
+
}],
|
|
106
|
+
restartPolicy: 'Never'
|
|
107
|
+
}
|
|
108
|
+
},
|
|
109
|
+
backoffLimit: 2
|
|
110
|
+
}
|
|
111
|
+
};
|
|
112
|
+
// 如果 Job 已存在則刪除
|
|
113
|
+
const deleteExistingBuilder = env.kubectl('delete')
|
|
114
|
+
.arg('job')
|
|
115
|
+
.arg(jobName)
|
|
116
|
+
.arg('-n', 'mssql');
|
|
117
|
+
await deleteExistingBuilder.exec();
|
|
118
|
+
// 應用 Job
|
|
119
|
+
const applyBuilder = env.kubectl('apply')
|
|
120
|
+
.arg('-f', '-')
|
|
121
|
+
.sensitiveStdin(JSON.stringify(jobYaml));
|
|
122
|
+
const applyResult = await applyBuilder.exec();
|
|
123
|
+
if (applyResult.exitCode !== 0) {
|
|
124
|
+
throw new Error(`Failed to start job ${jobName}: ${applyResult.stderr}`);
|
|
125
|
+
}
|
|
126
|
+
// 等待完成
|
|
127
|
+
const waitBuilder = env.kubectl('wait')
|
|
128
|
+
.arg('--for=condition=complete')
|
|
129
|
+
.arg(`job/${jobName}`)
|
|
130
|
+
.arg('-n', 'mssql')
|
|
131
|
+
.arg('--timeout=300s');
|
|
132
|
+
const waitResult = await waitBuilder.exec();
|
|
133
|
+
if (waitResult.exitCode !== 0) {
|
|
134
|
+
// 失敗時檢查日誌
|
|
135
|
+
const logsBuilder = env.kubectl('logs')
|
|
136
|
+
.arg(`job/${jobName}`)
|
|
137
|
+
.arg('-n', 'mssql');
|
|
138
|
+
const logs = await logsBuilder.exec();
|
|
139
|
+
this.logger.error(new Error(`Job ${jobName} logs:
|
|
140
|
+
${logs.stdout}`));
|
|
141
|
+
throw new Error(`Job ${jobName} failed or timed out`);
|
|
142
|
+
}
|
|
143
|
+
// 清理 Job
|
|
144
|
+
const cleanupBuilder = env.kubectl('delete')
|
|
145
|
+
.arg('job')
|
|
146
|
+
.arg(jobName)
|
|
147
|
+
.arg('-n', 'mssql');
|
|
148
|
+
await cleanupBuilder.exec();
|
|
149
|
+
}
|
|
150
|
+
/**
|
|
151
|
+
* 執行所有 MSSQL 初始化 Jobs
|
|
152
|
+
* @param instanceName WSL 實例名稱
|
|
153
|
+
* @param params 部署參數
|
|
154
|
+
* @param chartVersion Chart 版本
|
|
155
|
+
* @param progress 進度通知回調
|
|
156
|
+
*/
|
|
157
|
+
async runInitJobs(instanceName, params, chartVersion, output) {
|
|
158
|
+
output?.item('arrow', 'Initializing databases...').flush();
|
|
159
|
+
// 1. 建立臨時 Secret
|
|
160
|
+
output?.indent().item('arrow', 'Creating image pull secret...').outdent().flush();
|
|
161
|
+
await this.createImagePullSecret(instanceName, params);
|
|
162
|
+
try {
|
|
163
|
+
// 2. 執行 InitDb
|
|
164
|
+
output?.indent().item('arrow', 'Running db-init job...').outdent().flush();
|
|
165
|
+
await this.runJob(instanceName, 'db-init', 'uofx-k8s-db-helper', chartVersion, params);
|
|
166
|
+
// 3. 建立管理員使用者
|
|
167
|
+
output?.indent().item('arrow', 'Creating admin user...').outdent().flush();
|
|
168
|
+
await this.userManagerService.createAdminUser(instanceName, params);
|
|
169
|
+
// 4. 執行 InitSearchDb
|
|
170
|
+
output?.indent().item('arrow', 'Running search-db-init job...').outdent().flush();
|
|
171
|
+
await this.runJob(instanceName, 'search-db-init', 'uofx-k8s-search-db-helper', chartVersion, params);
|
|
172
|
+
}
|
|
173
|
+
finally {
|
|
174
|
+
// 5. 清理 Secret
|
|
175
|
+
output?.indent().item('arrow', 'Cleaning up resources...').outdent().flush();
|
|
176
|
+
await this.deleteImagePullSecret(instanceName);
|
|
177
|
+
}
|
|
178
|
+
}
|
|
179
|
+
};
|
|
180
|
+
exports.K8sJobRunnerService = K8sJobRunnerService;
|
|
181
|
+
__decorate([
|
|
182
|
+
__param(1, (0, sensitive_decorator_1.Sensitive)()),
|
|
183
|
+
__metadata("design:type", Function),
|
|
184
|
+
__metadata("design:paramtypes", [String, deployment_parameters_entity_1.DeploymentParameters, String, Object]),
|
|
185
|
+
__metadata("design:returntype", Promise)
|
|
186
|
+
], K8sJobRunnerService.prototype, "runInitJobs", null);
|
|
187
|
+
exports.K8sJobRunnerService = K8sJobRunnerService = __decorate([
|
|
188
|
+
(0, tsyringe_1.injectable)(),
|
|
189
|
+
__param(0, (0, tsyringe_1.inject)(tokens_1.TOKENS.Internal.IExecutionEnvironmentFactory)),
|
|
190
|
+
__param(1, (0, tsyringe_1.inject)(tokens_1.TOKENS.Internal.IMssqlUserManager)),
|
|
191
|
+
__param(2, (0, tsyringe_1.inject)(tokens_1.TOKENS.ILoggerPort)),
|
|
192
|
+
__metadata("design:paramtypes", [Object, Object, Object])
|
|
193
|
+
], K8sJobRunnerService);
|
|
194
|
+
//# sourceMappingURL=k8s-job-runner.service.js.map
|