feat: add v03 secret-plane smoke sync
This commit is contained in:
@@ -0,0 +1,29 @@
|
||||
version: 1
|
||||
kind: hwlab-v03-secret-plane
|
||||
metadata:
|
||||
id: hwlab-v03-secret-plane
|
||||
owner: HWLAB
|
||||
spec: pikasTech/HWLAB#2234
|
||||
boundary: D601/v0.3 only
|
||||
secretPlane:
|
||||
enabled: true
|
||||
issue: pikasTech/HWLAB#2234
|
||||
valuesPrinted: false
|
||||
store:
|
||||
kind: ClusterSecretStore
|
||||
name: hwlab-secret-plane-vault-cluster
|
||||
refreshInterval: 1m
|
||||
secrets:
|
||||
- id: hwlab-secret-plane-smoke
|
||||
externalSecretName: hwlab-secret-plane-smoke
|
||||
targetSecretName: hwlab-secret-plane-smoke
|
||||
purpose: Low-risk D601/v0.3 smoke secret for platform-infra secret-plane integration.
|
||||
data:
|
||||
- targetKey: password
|
||||
remoteRef: hwlab-secret-plane/poc
|
||||
property: password
|
||||
consumers:
|
||||
- serviceId: hwlab-cloud-api
|
||||
kind: Deployment
|
||||
injectAs: env
|
||||
envName: HWLAB_SECRET_PLANE_SMOKE
|
||||
@@ -328,6 +328,7 @@ lanes:
|
||||
accessControl:
|
||||
usersRef: config/hwlab-access-control/users.d601-v03.yaml#spec.users
|
||||
navProfilesRef: config/hwlab-access-control/nav-profiles.yaml#spec.profiles
|
||||
secretPlaneRef: config/hwlab-v03/secrets.yaml#secretPlane
|
||||
workbench:
|
||||
traceTimeline:
|
||||
autoExpandRunning: false
|
||||
@@ -766,6 +767,7 @@ lanes:
|
||||
HWLAB_WORKBENCH_EMPTY_SESSION_GC_BATCH_SIZE: "100"
|
||||
HWLAB_WORKBENCH_EMPTY_SESSION_GC_JITTER_MS: "15000"
|
||||
HWLAB_WORKBENCH_EMPTY_SESSION_GC_FAILURE_BACKOFF_MS: "120000"
|
||||
HWLAB_SECRET_PLANE_SMOKE: secretRef:hwlab-secret-plane-smoke/password
|
||||
configMapMounts:
|
||||
- name: hwpod-preinstalled-specs
|
||||
configMapName: hwlab-v03-hwpod-preinstalled-specs
|
||||
|
||||
@@ -5521,10 +5521,88 @@ function runtimeConfigMapsManifest({ configMaps, namespace, labels, annotations
|
||||
};
|
||||
}
|
||||
|
||||
function runtimeKustomization({ profile = "dev", includeDeviceAgent = profile === "dev", externalPostgres = false, workbenchRedis = false, runtimeConfigMaps = false } = {}) {
|
||||
async function runtimeSecretPlaneConfigForProfile(deploy, profile) {
|
||||
if (!isRuntimeLane(profile)) return null;
|
||||
const laneConfig = deploy?.lanes?.[profile];
|
||||
const secretPlane = laneConfig?.secretPlaneRef
|
||||
? await readConfigRef(laneConfig.secretPlaneRef, `${profile}.secretPlaneRef`)
|
||||
: laneConfig?.secretPlane;
|
||||
if (!secretPlane || typeof secretPlane !== "object" || Array.isArray(secretPlane)) return null;
|
||||
if (secretPlane.enabled !== true) return null;
|
||||
return cloneJson(secretPlane);
|
||||
}
|
||||
|
||||
function runtimeSecretPlaneManifest({ config, namespace, labels, annotations }) {
|
||||
const store = config?.store;
|
||||
assert.ok(store && typeof store === "object" && !Array.isArray(store), "secretPlane.store must be an object");
|
||||
assert.equal(store.kind, "ClusterSecretStore", "secretPlane.store.kind must be ClusterSecretStore for D601/v0.3");
|
||||
assert.ok(typeof store.name === "string" && store.name.trim(), "secretPlane.store.name must be set");
|
||||
const secrets = Array.isArray(config.secrets) ? config.secrets : [];
|
||||
assert.ok(secrets.length > 0, "secretPlane.secrets must not be empty");
|
||||
return {
|
||||
apiVersion: "v1",
|
||||
kind: "List",
|
||||
items: secrets.map((secret, index) => runtimeSecretPlaneExternalSecret({ config, secret, index, namespace, labels, annotations, store }))
|
||||
};
|
||||
}
|
||||
|
||||
function runtimeSecretPlaneExternalSecret({ config, secret, index, namespace, labels, annotations, store }) {
|
||||
const externalSecretName = requiredSecretPlaneString(secret?.externalSecretName, `secretPlane.secrets[${index}].externalSecretName`);
|
||||
const targetSecretName = requiredSecretPlaneString(secret?.targetSecretName, `secretPlane.secrets[${index}].targetSecretName`);
|
||||
const data = Array.isArray(secret?.data) ? secret.data : [];
|
||||
assert.ok(data.length > 0, `secretPlane.secrets[${index}].data must not be empty`);
|
||||
const sourceRefs = data.map((item) => requiredSecretPlaneString(item?.remoteRef, `secretPlane.secrets[${index}].data.remoteRef`));
|
||||
return {
|
||||
apiVersion: "external-secrets.io/v1",
|
||||
kind: "ExternalSecret",
|
||||
metadata: {
|
||||
name: externalSecretName,
|
||||
namespace,
|
||||
labels: {
|
||||
...labels,
|
||||
"app.kubernetes.io/name": externalSecretName,
|
||||
"app.kubernetes.io/component": "external-secret",
|
||||
"hwlab.pikastech.local/secret-plane": String(config.issue ?? "pikasTech/HWLAB#2234")
|
||||
},
|
||||
annotations: {
|
||||
...annotations,
|
||||
"hwlab.pikastech.local/source-ref": sourceRefs.join(","),
|
||||
"hwlab.pikastech.local/values-printed": "false"
|
||||
}
|
||||
},
|
||||
spec: {
|
||||
refreshInterval: requiredSecretPlaneString(config.refreshInterval, "secretPlane.refreshInterval"),
|
||||
secretStoreRef: {
|
||||
name: requiredSecretPlaneString(store.name, "secretPlane.store.name"),
|
||||
kind: store.kind
|
||||
},
|
||||
target: {
|
||||
name: targetSecretName,
|
||||
creationPolicy: "Owner"
|
||||
},
|
||||
data: data.map((item, itemIndex) => ({
|
||||
secretKey: requiredSecretPlaneString(item?.targetKey, `secretPlane.secrets[${index}].data[${itemIndex}].targetKey`),
|
||||
remoteRef: {
|
||||
key: requiredSecretPlaneString(item?.remoteRef, `secretPlane.secrets[${index}].data[${itemIndex}].remoteRef`),
|
||||
property: requiredSecretPlaneString(item?.property, `secretPlane.secrets[${index}].data[${itemIndex}].property`)
|
||||
}
|
||||
}))
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
function requiredSecretPlaneString(value, label) {
|
||||
assert.equal(typeof value, "string", `${label} must be a string`);
|
||||
const trimmed = value.trim();
|
||||
assert.ok(trimmed, `${label} must not be empty`);
|
||||
return trimmed;
|
||||
}
|
||||
|
||||
function runtimeKustomization({ profile = "dev", includeDeviceAgent = profile === "dev", externalPostgres = false, workbenchRedis = false, runtimeConfigMaps = false, externalSecrets = 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 (externalSecrets) resources.splice(resources.indexOf("workloads.yaml"), 0, "external-secrets.yaml");
|
||||
if (isRuntimeLane(profile)) {
|
||||
if (externalPostgres) resources.push("external-postgres.yaml");
|
||||
else resources.push("postgres.yaml");
|
||||
@@ -5666,6 +5744,7 @@ async function plannedFiles(args) {
|
||||
const externalPostgres = externalPostgresConfigForLane(deploy, profile);
|
||||
const workbenchRedis = workbenchRuntimeRedisConfigForProfile(deploy, profile);
|
||||
const runtimeConfigMaps = runtimeConfigMapsForProfile(deploy, profile);
|
||||
const runtimeSecretPlane = await runtimeSecretPlaneConfigForProfile(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;
|
||||
@@ -5673,12 +5752,13 @@ 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), runtimeConfigMaps: runtimeConfigMaps.length > 0 }));
|
||||
putJson(`${runtimePath}/kustomization.yaml`, runtimeKustomization({ profile, includeDeviceAgent, externalPostgres: Boolean(externalPostgres), workbenchRedis: Boolean(workbenchRedis), runtimeConfigMaps: runtimeConfigMaps.length > 0, externalSecrets: Boolean(runtimeSecretPlane) }));
|
||||
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 }));
|
||||
if (runtimeSecretPlane) putJson(`${runtimePath}/external-secrets.yaml`, runtimeSecretPlaneManifest({ config: runtimeSecretPlane, 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 }));
|
||||
|
||||
@@ -566,6 +566,7 @@ test("v03 render keeps node identity as data instead of generated structure", as
|
||||
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"));
|
||||
assert.ok(generatedPaths.includes("runtime-v03/external-secrets.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);
|
||||
@@ -577,11 +578,20 @@ 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 externalSecretsJson = JSON.parse(await readFile(path.join(outDir, "runtime-v03", "external-secrets.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 smokeExternalSecret = (externalSecretsJson.items ?? []).find((item) => item.kind === "ExternalSecret" && item.metadata?.name === "hwlab-secret-plane-smoke");
|
||||
assert.ok(smokeExternalSecret, "expected v03 secret-plane smoke ExternalSecret");
|
||||
assert.equal(smokeExternalSecret.metadata?.namespace, "hwlab-v03");
|
||||
assert.equal(smokeExternalSecret.spec?.secretStoreRef?.kind, "ClusterSecretStore");
|
||||
assert.equal(smokeExternalSecret.spec?.secretStoreRef?.name, "hwlab-secret-plane-vault-cluster");
|
||||
assert.equal(smokeExternalSecret.spec?.target?.name, "hwlab-secret-plane-smoke");
|
||||
assert.deepEqual(smokeExternalSecret.spec?.data, [{ secretKey: "password", remoteRef: { key: "hwlab-secret-plane/poc", property: "password" } }]);
|
||||
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");
|
||||
const cloudApiEnv = new Map((cloudApiContainer?.env ?? []).map((entry) => [entry.name, entry.value]));
|
||||
const cloudApiEnvEntries = new Map((cloudApiContainer?.env ?? []).map((entry) => [entry.name, entry]));
|
||||
assert.equal(cloudApi?.spec?.template?.metadata?.labels?.["hwlab.pikastech.local/source-commit"], sourceRevision);
|
||||
assert.equal(cloudApi?.spec?.template?.metadata?.labels?.["hwlab.pikastech.local/artifact-source-commit"], sourceRevision);
|
||||
assert.equal(cloudApi?.spec?.template?.metadata?.annotations?.["hwlab.pikastech.local/source-commit"], sourceRevision);
|
||||
@@ -591,6 +601,7 @@ test("v03 render keeps node identity as data instead of generated structure", as
|
||||
assert.equal(cloudApiEnv.get("HWLAB_GITOPS_SOURCE_COMMIT"), sourceRevision);
|
||||
assert.equal(cloudApiEnv.get("HWLAB_CODE_AGENT_AGENTRUN_SOURCE_COMMIT"), sourceRevision);
|
||||
assert.match(cloudApiEnv.get("HWLAB_ENVIRONMENT_IMAGE") ?? "", /hwlab-cloud-api-env:env-stale/u);
|
||||
assert.deepEqual(cloudApiEnvEntries.get("HWLAB_SECRET_PLANE_SMOKE")?.valueFrom?.secretKeyRef, { name: "hwlab-secret-plane-smoke", key: "password", optional: true });
|
||||
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"));
|
||||
|
||||
Reference in New Issue
Block a user