fix: preinstall 71freq hwpod runtime config

This commit is contained in:
lyon
2026-06-26 10:38:53 +08:00
parent 6cfb3b47cd
commit 9356b25821
6 changed files with 371 additions and 6 deletions
+60 -2
View File
@@ -932,6 +932,33 @@ function applyDeployEnv(envList, deployService, namespace, profile = "dev") {
}
}
function applyDeployConfigMapMounts(podSpec, container, deployService) {
const mounts = Array.isArray(deployService?.configMapMounts) ? deployService.configMapMounts : [];
if (mounts.length === 0 || !podSpec || !container) return;
podSpec.volumes ??= [];
container.volumeMounts ??= [];
for (const mount of mounts) {
const name = String(mount?.name ?? "").trim();
const configMapName = String(mount?.configMapName ?? "").trim();
const mountPath = String(mount?.mountPath ?? "").trim();
if (!name || !configMapName || !mountPath) continue;
const items = Array.isArray(mount.items)
? mount.items
.map((item) => ({ key: String(item?.key ?? "").trim(), path: String(item?.path ?? "").trim() }))
.filter((item) => item.key && item.path)
: [];
const volume = { name, configMap: { name: configMapName } };
if (items.length > 0) volume.configMap.items = items;
const existingVolume = podSpec.volumes.find((entry) => entry?.name === name);
if (existingVolume) Object.assign(existingVolume, volume);
else podSpec.volumes.push(volume);
const volumeMount = { name, mountPath, readOnly: mount.readOnly !== false };
const existingMount = container.volumeMounts.find((entry) => entry?.name === name);
if (existingMount) Object.assign(existingMount, volumeMount);
else container.volumeMounts.push(volumeMount);
}
}
function removeV02LegacySimulatorEnv(envList) {
if (!Array.isArray(envList)) return envList;
return envList.filter((entry) => !v02RemovedServiceEnvNames.has(entry?.name));
@@ -1327,6 +1354,7 @@ function transformWorkloads({ workloads, deploy, catalog, source, sourceBranch =
if (Object.hasOwn(entry, "value")) entry.value = namespaceScopedHost(entry.value, namespace);
}
applyDeployEnv(container.env, deployService, namespace, profile);
applyDeployConfigMapMounts(podTemplate.spec, container, deployService);
if (runtimeLane) container.env = removeV02LegacySimulatorEnv(container.env);
upsertEnv(container.env, "HWLAB_ENVIRONMENT", profileLabel);
upsertEnv(container.env, "HWLAB_COMMIT_ID", runtimeCommit);
@@ -5368,9 +5396,37 @@ function workbenchRuntimeRedisManifest({ profile = "v03", config, source }) {
};
}
function runtimeKustomization({ profile = "dev", includeDeviceAgent = profile === "dev", externalPostgres = false, workbenchRedis = false } = {}) {
function runtimeConfigMapsForProfile(deploy, profile) {
if (!isRuntimeLane(profile)) return [];
const configMaps = deploy?.lanes?.[profile]?.configMaps;
if (!Array.isArray(configMaps)) return [];
return configMaps
.map((configMap) => cloneJson(configMap))
.filter((configMap) => typeof configMap?.name === "string" && configMap.name.trim() && configMap.data && typeof configMap.data === "object" && !Array.isArray(configMap.data));
}
function runtimeConfigMapsManifest({ configMaps, namespace, labels, annotations }) {
return {
apiVersion: "v1",
kind: "List",
items: configMaps.map((configMap) => ({
apiVersion: "v1",
kind: "ConfigMap",
metadata: {
name: configMap.name,
namespace,
labels: { ...labels, ...(configMap.labels ?? {}) },
annotations: { ...annotations, ...(configMap.annotations ?? {}) }
},
data: Object.fromEntries(Object.entries(configMap.data).map(([key, value]) => [key, String(value)]))
}))
};
}
function runtimeKustomization({ profile = "dev", includeDeviceAgent = profile === "dev", externalPostgres = false, workbenchRedis = false, runtimeConfigMaps = false } = {}) {
const resources = ["namespace.yaml", "services.yaml", "health-contract.yaml", "workloads.yaml", "deepseek-proxy.yaml"];
if (!isRuntimeLane(profile)) resources.splice(1, 0, "code-agent-codex-config.yaml");
if (runtimeConfigMaps) resources.splice(3, 0, "configmaps.yaml");
if (isRuntimeLane(profile)) {
if (externalPostgres) resources.push("external-postgres.yaml");
else resources.push("postgres.yaml");
@@ -5510,6 +5566,7 @@ async function plannedFiles(args) {
const includeDeviceAgent = profile === "dev";
const externalPostgres = externalPostgresConfigForLane(deploy, profile);
const workbenchRedis = workbenchRuntimeRedisConfigForProfile(deploy, profile);
const runtimeConfigMaps = runtimeConfigMapsForProfile(deploy, profile);
const profileLabels = { ...baseLabels, "hwlab.pikastech.local/environment": runtimeLabelForProfile(profile), "hwlab.pikastech.local/profile": runtimeLabelForProfile(profile) };
const runtimeNamespace = cloneJson(namespaceTemplate);
runtimeNamespace.metadata.name = namespace;
@@ -5517,11 +5574,12 @@ async function plannedFiles(args) {
annotate(runtimeNamespace.metadata, annotations);
const runtimePath = runtimePathForProfile(profile);
const endpoints = profileEndpoint(args, profile);
putJson(`${runtimePath}/kustomization.yaml`, runtimeKustomization({ profile, includeDeviceAgent, externalPostgres: Boolean(externalPostgres), workbenchRedis: Boolean(workbenchRedis) }));
putJson(`${runtimePath}/kustomization.yaml`, runtimeKustomization({ profile, includeDeviceAgent, externalPostgres: Boolean(externalPostgres), workbenchRedis: Boolean(workbenchRedis), runtimeConfigMaps: runtimeConfigMaps.length > 0 }));
putJson(`${runtimePath}/namespace.yaml`, runtimeNamespace);
if (!isRuntimeLane(profile)) putJson(`${runtimePath}/code-agent-codex-config.yaml`, transformListNamespace(config, namespace, profileLabels, annotations));
putJson(`${runtimePath}/services.yaml`, transformServices({ services, namespace, labels: profileLabels, annotations, profile, deploy }));
putJson(`${runtimePath}/health-contract.yaml`, transformHealthContract(health, namespace, profileLabels, annotations, endpoints.runtimeEndpoint, endpoints.webEndpoint, profile));
if (runtimeConfigMaps.length > 0) putJson(`${runtimePath}/configmaps.yaml`, runtimeConfigMapsManifest({ configMaps: runtimeConfigMaps, namespace, labels: profileLabels, annotations }));
putJson(`${runtimePath}/workloads.yaml`, transformWorkloads({ workloads, deploy, catalog: artifactCatalog, source, sourceBranch: args.sourceBranch, gitReadUrl: args.gitReadUrl, registryPrefix: args.registryPrefix, runtimeEndpoint: endpoints.runtimeEndpoint, webEndpoint: endpoints.webEndpoint, profile, useDeployImages: args.useDeployImages, metricsSidecarSha256 }));
putJson(`${runtimePath}/deepseek-proxy.yaml`, deepSeekProxyManifest({ profile, source, sourceBranch: args.sourceBranch, sourceRepo: args.sourceRepo, gitReadUrl: args.gitReadUrl, deploy, registryPrefix: args.registryPrefix, catalog: artifactCatalog, useDeployImages: args.useDeployImages, metricsSidecarSha256 }));
if (isRuntimeLane(profile) && externalPostgres) putJson(`${runtimePath}/external-postgres.yaml`, externalPostgresManifest({ profile, config: externalPostgres, source }));
+14
View File
@@ -532,6 +532,7 @@ test("v03 render keeps node identity as data instead of generated structure", as
assert.ok(generatedPaths.includes("argocd/application-v03.yaml"));
assert.ok(generatedPaths.includes("tekton-v03/pipeline.yaml"));
assert.ok(generatedPaths.includes("runtime-v03/external-postgres.yaml"));
assert.ok(generatedPaths.includes("runtime-v03/configmaps.yaml"));
const pipeline = await readFile(path.join(outDir, "tekton-v03", "pipeline.yaml"), "utf8");
assert.match(pipeline, /--external-service-report-dir \/workspace\/source\/service-results/u);
assert.match(pipeline, /--ci-plan-path \/workspace\/source\/affected-services\.json/u);
@@ -542,6 +543,19 @@ test("v03 render keeps node identity as data instead of generated structure", as
const workloads = await readFile(path.join(outDir, "runtime-v03", "workloads.yaml"), "utf8");
const workloadsJson = JSON.parse(workloads);
const configMapsJson = JSON.parse(await readFile(path.join(outDir, "runtime-v03", "configmaps.yaml"), "utf8"));
const configMapNames = (configMapsJson.items ?? []).map((item) => item.metadata?.name).sort();
assert.deepEqual(configMapNames, ["hwlab-v03-hwpod-preinstalled-specs", "hwlab-v03-project-management-bootstrap"]);
const cloudApi = (workloadsJson.items ?? []).find((item) => item.kind === "Deployment" && item.metadata?.name === "hwlab-cloud-api");
const cloudApiContainer = collectContainersFromItem(cloudApi).find((container) => container.name === "hwlab-cloud-api");
assert.ok((cloudApi?.spec?.template?.spec?.volumes ?? []).some((volume) => volume.name === "hwpod-preinstalled-specs" && volume.configMap?.name === "hwlab-v03-hwpod-preinstalled-specs"));
assert.ok((cloudApiContainer?.volumeMounts ?? []).some((mount) => mount.name === "hwpod-preinstalled-specs" && mount.mountPath === "/etc/hwlab/hwpod-specs"));
assert.ok((cloudApiContainer?.env ?? []).some((entry) => entry.name === "HWLAB_HWPOD_SPEC_REGISTRY_DIRS" && entry.value === "/etc/hwlab/hwpod-specs"));
const projectManagement = (workloadsJson.items ?? []).find((item) => item.kind === "Deployment" && item.metadata?.name === "hwlab-project-management");
const projectManagementContainer = collectContainersFromItem(projectManagement).find((container) => container.name === "hwlab-project-management");
assert.ok((projectManagement?.spec?.template?.spec?.volumes ?? []).some((volume) => volume.name === "project-management-bootstrap" && volume.configMap?.name === "hwlab-v03-project-management-bootstrap"));
assert.ok((projectManagementContainer?.volumeMounts ?? []).some((mount) => mount.name === "project-management-bootstrap" && mount.mountPath === "/etc/hwlab/project-management"));
assert.ok((projectManagementContainer?.env ?? []).some((entry) => entry.name === "HWLAB_PROJECT_MANAGEMENT_BOOTSTRAP_SOURCES_PATH" && entry.value === "/etc/hwlab/project-management/sources.json"));
const externalPostgres = JSON.parse(await readFile(path.join(outDir, "runtime-v03", "external-postgres.yaml"), "utf8"));
assert.deepEqual((externalPostgres.items ?? []).map((item) => item.kind), ["Service", "EndpointSlice"]);
const externalPostgresSlice = (externalPostgres.items ?? []).find((item) => item.kind === "EndpointSlice");