|
|
|
@@ -7,6 +7,14 @@ import path from "node:path";
|
|
|
|
|
import { promisify } from "node:util";
|
|
|
|
|
import { fileURLToPath } from "node:url";
|
|
|
|
|
|
|
|
|
|
import {
|
|
|
|
|
applyRuntimeLaneDeployConfig,
|
|
|
|
|
defaultEndpointForRuntimeLane,
|
|
|
|
|
defaultPortForRuntimeLane,
|
|
|
|
|
isRuntimeLane,
|
|
|
|
|
runtimeLaneConfig,
|
|
|
|
|
versionNameForRuntimeLane
|
|
|
|
|
} from "./src/g14-gitops-lane.ts";
|
|
|
|
|
import { readStructuredFile } from "./src/structured-config.mjs";
|
|
|
|
|
|
|
|
|
|
const execFileAsync = promisify(execFile);
|
|
|
|
@@ -17,6 +25,8 @@ const defaultSourceRepo = process.env.HWLAB_G14_GIT_URL || "git@github.com:pikas
|
|
|
|
|
const defaultGitReadUrl = process.env.HWLAB_G14_GIT_READ_URL || null;
|
|
|
|
|
const defaultV02GitReadUrl = process.env.HWLAB_V02_GIT_READ_URL || "http://git-mirror-http.devops-infra.svc.cluster.local/pikasTech/HWLAB.git";
|
|
|
|
|
const defaultV02GitWriteUrl = process.env.HWLAB_V02_GIT_WRITE_URL || "http://git-mirror-write.devops-infra.svc.cluster.local/pikasTech/HWLAB.git";
|
|
|
|
|
const defaultRuntimeLaneGitReadUrl = process.env.HWLAB_RUNTIME_LANE_GIT_READ_URL || defaultV02GitReadUrl;
|
|
|
|
|
const defaultRuntimeLaneGitWriteUrl = process.env.HWLAB_RUNTIME_LANE_GIT_WRITE_URL || defaultV02GitWriteUrl;
|
|
|
|
|
const defaultBranch = process.env.HWLAB_G14_BRANCH || "G14";
|
|
|
|
|
const defaultGitopsBranch = process.env.HWLAB_G14_GITOPS_BRANCH || "G14-gitops";
|
|
|
|
|
const defaultCatalogPath = process.env.HWLAB_ARTIFACT_CATALOG_PATH || "deploy/artifact-catalog.dev.json";
|
|
|
|
@@ -97,11 +107,12 @@ const primitiveValidationTasks = Object.freeze([
|
|
|
|
|
{
|
|
|
|
|
name: "g14-contract-check",
|
|
|
|
|
commands: [
|
|
|
|
|
"node --check scripts/g14-gitops-render.mjs",
|
|
|
|
|
"npm run gitops:ts:check",
|
|
|
|
|
"node scripts/run-bun.mjs scripts/g14-gitops-render.mjs --help >/dev/null",
|
|
|
|
|
"render_check_revision=$(git rev-parse HEAD)",
|
|
|
|
|
"render_check_dir=$(mktemp -d)",
|
|
|
|
|
"node scripts/g14-gitops-render.mjs --lane \"$(params.lane)\" --catalog-path \"$(params.catalog-path)\" --image-tag-mode \"$(params.image-tag-mode)\" --source-branch \"$(params.source-branch)\" --gitops-branch \"$(params.gitops-branch)\" --out \"$render_check_dir\" --source-revision \"$render_check_revision\"",
|
|
|
|
|
"node scripts/g14-gitops-render.mjs --lane \"$(params.lane)\" --catalog-path \"$(params.catalog-path)\" --image-tag-mode \"$(params.image-tag-mode)\" --source-branch \"$(params.source-branch)\" --gitops-branch \"$(params.gitops-branch)\" --out \"$render_check_dir\" --check --source-revision \"$render_check_revision\"",
|
|
|
|
|
"node scripts/run-bun.mjs scripts/g14-gitops-render.mjs --lane \"$(params.lane)\" --catalog-path \"$(params.catalog-path)\" --image-tag-mode \"$(params.image-tag-mode)\" --source-branch \"$(params.source-branch)\" --gitops-branch \"$(params.gitops-branch)\" --out \"$render_check_dir\" --source-revision \"$render_check_revision\"",
|
|
|
|
|
"node scripts/run-bun.mjs scripts/g14-gitops-render.mjs --lane \"$(params.lane)\" --catalog-path \"$(params.catalog-path)\" --image-tag-mode \"$(params.image-tag-mode)\" --source-branch \"$(params.source-branch)\" --gitops-branch \"$(params.gitops-branch)\" --out \"$render_check_dir\" --check --source-revision \"$render_check_revision\"",
|
|
|
|
|
`node --input-type=module <<'NODE'
|
|
|
|
|
import { readdirSync, readFileSync, statSync } from "node:fs";
|
|
|
|
|
import path from "node:path";
|
|
|
|
@@ -124,6 +135,7 @@ const forbiddenCiFragments = [
|
|
|
|
|
];
|
|
|
|
|
const scanTargets = [
|
|
|
|
|
"scripts/g14-gitops-render.mjs",
|
|
|
|
|
"scripts/src/g14-gitops-lane.ts",
|
|
|
|
|
"scripts/artifact-publish.mjs",
|
|
|
|
|
"scripts/g14-artifact-publish.mjs",
|
|
|
|
|
"scripts/src/g14-ci-plan-lib.mjs",
|
|
|
|
@@ -234,23 +246,26 @@ function parseArgs(argv) {
|
|
|
|
|
else if (arg === "--help" || arg === "-h") args.help = true;
|
|
|
|
|
else throw new Error(`unknown argument ${arg}`);
|
|
|
|
|
}
|
|
|
|
|
if (args.lane === "v02") {
|
|
|
|
|
if (args.sourceBranch === defaultBranch) args.sourceBranch = "v0.2";
|
|
|
|
|
if (args.gitopsBranch === defaultGitopsBranch) args.gitopsBranch = "v0.2-gitops";
|
|
|
|
|
if (args.catalogPath === defaultCatalogPath) args.catalogPath = "deploy/artifact-catalog.v02.json";
|
|
|
|
|
if (args.runtimeEndpoint === defaultRuntimeEndpoint) args.runtimeEndpoint = defaultV02RuntimeEndpoint;
|
|
|
|
|
if (args.webEndpoint === defaultWebEndpoint) args.webEndpoint = defaultV02WebEndpoint;
|
|
|
|
|
if (isRuntimeLane(args.lane)) {
|
|
|
|
|
const versionName = versionNameForRuntimeLane(args.lane);
|
|
|
|
|
const defaultLaneEndpoint = defaultEndpointForRuntimeLane(args.lane);
|
|
|
|
|
if (args.sourceBranch === defaultBranch) args.sourceBranch = versionName;
|
|
|
|
|
if (args.gitopsBranch === defaultGitopsBranch) args.gitopsBranch = `${versionName}-gitops`;
|
|
|
|
|
if (args.catalogPath === defaultCatalogPath) args.catalogPath = `deploy/artifact-catalog.${args.lane}.json`;
|
|
|
|
|
if (args.runtimeEndpoint === defaultRuntimeEndpoint) args.runtimeEndpoint = defaultLaneEndpoint;
|
|
|
|
|
if (args.webEndpoint === defaultWebEndpoint) args.webEndpoint = defaultLaneEndpoint;
|
|
|
|
|
if (args.imageTagMode === "short") args.imageTagMode = "full";
|
|
|
|
|
if (!args.gitReadUrl) args.gitReadUrl = defaultV02GitReadUrl;
|
|
|
|
|
if (!args.gitWriteUrl) args.gitWriteUrl = defaultV02GitWriteUrl;
|
|
|
|
|
if (!args.gitReadUrl) args.gitReadUrl = defaultRuntimeLaneGitReadUrl;
|
|
|
|
|
if (!args.gitWriteUrl) args.gitWriteUrl = defaultRuntimeLaneGitWriteUrl;
|
|
|
|
|
}
|
|
|
|
|
if (!args.gitReadUrl) args.gitReadUrl = args.sourceRepo;
|
|
|
|
|
if (!args.gitWriteUrl) args.gitWriteUrl = args.sourceRepo;
|
|
|
|
|
assert.ok(["g14", "v02"].includes(args.lane), `unknown lane ${args.lane}`);
|
|
|
|
|
assert.ok(args.lane === "g14" || isRuntimeLane(args.lane), `unknown lane ${args.lane}`);
|
|
|
|
|
assert.ok(["short", "full"].includes(args.imageTagMode), `unknown image tag mode ${args.imageTagMode}`);
|
|
|
|
|
if (args.lane === "v02") {
|
|
|
|
|
assert.equal(args.sourceBranch, "v0.2", "v02 source branch must be v0.2");
|
|
|
|
|
assert.equal(args.gitopsBranch, "v0.2-gitops", "v02 GitOps branch must be v0.2-gitops");
|
|
|
|
|
if (isRuntimeLane(args.lane)) {
|
|
|
|
|
const versionName = versionNameForRuntimeLane(args.lane);
|
|
|
|
|
assert.equal(args.sourceBranch, versionName, `${args.lane} source branch must be ${versionName}`);
|
|
|
|
|
assert.equal(args.gitopsBranch, `${versionName}-gitops`, `${args.lane} GitOps branch must be ${versionName}-gitops`);
|
|
|
|
|
}
|
|
|
|
|
return args;
|
|
|
|
|
}
|
|
|
|
@@ -263,24 +278,24 @@ function readOption(argv, index, name) {
|
|
|
|
|
|
|
|
|
|
function usage() {
|
|
|
|
|
return [
|
|
|
|
|
"usage: node scripts/g14-gitops-render.mjs [options]",
|
|
|
|
|
"usage: node scripts/run-bun.mjs scripts/g14-gitops-render.mjs [options]",
|
|
|
|
|
"",
|
|
|
|
|
"Render G14-only Kubernetes-native CI/CD manifests from built-in Tekton CI primitives, deploy/deploy.yaml, and deploy/k8s/*.",
|
|
|
|
|
"",
|
|
|
|
|
"options:",
|
|
|
|
|
" --lane LANE g14 or v02; default: g14",
|
|
|
|
|
" --lane LANE g14 or runtime lane such as v02/v03/v04; default: g14",
|
|
|
|
|
` --out DIR default: ${defaultOutDir}`,
|
|
|
|
|
` --catalog-path PATH default: ${defaultCatalogPath}`,
|
|
|
|
|
" --image-tag-mode MODE short or full; v02 uses full source commit IDs",
|
|
|
|
|
` --source-repo URL default: ${defaultSourceRepo}`,
|
|
|
|
|
" --git-read-url URL read-only source URL; v02 defaults to devops-infra git mirror",
|
|
|
|
|
" --git-write-url URL GitOps write URL; v02 defaults to devops-infra git mirror write relay",
|
|
|
|
|
" --git-read-url URL read-only source URL; runtime lanes default to devops-infra git mirror",
|
|
|
|
|
" --git-write-url URL GitOps write URL; runtime lanes default to devops-infra git mirror write relay",
|
|
|
|
|
` --source-branch BRANCH default: ${defaultBranch}`,
|
|
|
|
|
` --gitops-branch BRANCH default: ${defaultGitopsBranch}`,
|
|
|
|
|
" --source-revision SHA source commit used for image tags and runtime annotations; default: git rev-parse HEAD",
|
|
|
|
|
` --registry-prefix PREFIX default: ${defaultRegistryPrefix}`,
|
|
|
|
|
` --runtime-endpoint URL default: ${defaultRuntimeEndpoint}; v02 default: ${defaultV02RuntimeEndpoint}`,
|
|
|
|
|
` --web-endpoint URL default: ${defaultWebEndpoint}; v02 default: ${defaultV02WebEndpoint}`,
|
|
|
|
|
` --runtime-endpoint URL default: ${defaultRuntimeEndpoint}; runtime lane default is derived from deploy.yaml/public URL`,
|
|
|
|
|
` --web-endpoint URL default: ${defaultWebEndpoint}; runtime lane default is derived from deploy.yaml/public URL`,
|
|
|
|
|
` --prod-runtime-endpoint URL default: ${defaultProdRuntimeEndpoint}`,
|
|
|
|
|
` --prod-web-endpoint URL default: ${defaultProdWebEndpoint}`,
|
|
|
|
|
" --use-deploy-images default and only supported mode: render workload images from deploy/artifact-catalog.dev.json per service",
|
|
|
|
@@ -559,32 +574,38 @@ function runtimeImageTagForService(options) {
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
function artifactEnvironment(args) {
|
|
|
|
|
return args.lane === "v02" ? "v02" : "dev";
|
|
|
|
|
return isRuntimeLane(args.lane) ? args.lane : "dev";
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
function artifactEndpoint(args) {
|
|
|
|
|
return args.lane === "v02" ? defaultV02RuntimeEndpoint : defaultRuntimeEndpoint;
|
|
|
|
|
return isRuntimeLane(args.lane) ? args.runtimeEndpoint : defaultRuntimeEndpoint;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
function serviceIdsForLane(lane, deploy = null) {
|
|
|
|
|
return lane === "v02" ? v02ServiceIdsFromDeploy(deploy) : defaultServiceIds;
|
|
|
|
|
return isRuntimeLane(lane) ? runtimeLaneServiceIdsFromDeploy(deploy, lane) : defaultServiceIds;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
function serviceIdsForProfile(profile, deploy = null) {
|
|
|
|
|
return profile === "v02" ? v02ServiceIdsFromDeploy(deploy) : defaultServiceIds;
|
|
|
|
|
return isRuntimeLane(profile) ? runtimeLaneServiceIdsFromDeploy(deploy, profile) : defaultServiceIds;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
function v02ServiceIdsFromDeploy(deploy) {
|
|
|
|
|
const serviceIds = uniquePreserveOrder((Array.isArray(deploy?.lanes?.v02?.envReuseServices) ? deploy.lanes.v02.envReuseServices : [])
|
|
|
|
|
function runtimeLaneServiceIdsFromDeploy(deploy, lane) {
|
|
|
|
|
const laneConfig = runtimeLaneConfig(deploy, lane);
|
|
|
|
|
const serviceIds = uniquePreserveOrder((Array.isArray(laneConfig.envReuseServices) ? laneConfig.envReuseServices : [])
|
|
|
|
|
.map((item) => String(item ?? "").trim())
|
|
|
|
|
.filter(Boolean));
|
|
|
|
|
assert.ok(serviceIds.length > 0, "deploy.lanes.v02.envReuseServices must not be empty");
|
|
|
|
|
assert.ok(serviceIds.length > 0, `deploy.lanes.${lane}.envReuseServices must not be empty`);
|
|
|
|
|
return serviceIds;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
function v02ObservableServicesForDeploy(deploy) {
|
|
|
|
|
const declarations = deploy?.lanes?.v02?.serviceDeclarations ?? {};
|
|
|
|
|
const serviceIds = new Set(v02ServiceIdsFromDeploy(deploy));
|
|
|
|
|
function v02ServiceIdsFromDeploy(deploy) {
|
|
|
|
|
return runtimeLaneServiceIdsFromDeploy(deploy, "v02");
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
function runtimeLaneObservableServicesForDeploy(deploy, lane) {
|
|
|
|
|
const laneConfig = runtimeLaneConfig(deploy, lane);
|
|
|
|
|
const declarations = laneConfig.serviceDeclarations ?? {};
|
|
|
|
|
const serviceIds = new Set(runtimeLaneServiceIdsFromDeploy(deploy, lane));
|
|
|
|
|
const declared = Object.entries(declarations)
|
|
|
|
|
.filter(([serviceId, declaration]) => serviceIds.has(serviceId) && declaration?.observable !== false && Number.isInteger(declaration?.healthPort))
|
|
|
|
|
.map(([serviceId, declaration]) => ({
|
|
|
|
@@ -595,12 +616,20 @@ function v02ObservableServicesForDeploy(deploy) {
|
|
|
|
|
return [...declared, ...v02ExtraObservableServices];
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
function v02ObservableServicesForDeploy(deploy) {
|
|
|
|
|
return runtimeLaneObservableServicesForDeploy(deploy, "v02");
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
function v02ObservableService(deploy, serviceId) {
|
|
|
|
|
return v02ObservableServicesForDeploy(deploy).find((item) => item.serviceId === serviceId) ?? null;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
function runtimeLaneObservableServiceIdsForDeploy(deploy, lane) {
|
|
|
|
|
return new Set(runtimeLaneObservableServicesForDeploy(deploy, lane).map((item) => item.serviceId));
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
function v02ObservableServiceIdsForDeploy(deploy) {
|
|
|
|
|
return new Set(v02ObservableServicesForDeploy(deploy).map((item) => item.serviceId));
|
|
|
|
|
return runtimeLaneObservableServiceIdsForDeploy(deploy, "v02");
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
function servicesParamForLane(lane, deploy = null) {
|
|
|
|
@@ -624,7 +653,7 @@ function isV02RemovedServiceId(serviceId) {
|
|
|
|
|
|
|
|
|
|
function artifactCatalogSkeleton({ args, deploy, source }) {
|
|
|
|
|
const environment = artifactEnvironment(args);
|
|
|
|
|
const namespace = args.lane === "v02" ? "hwlab-v02" : "hwlab-dev";
|
|
|
|
|
const namespace = isRuntimeLane(args.lane) ? namespaceNameForProfile(args.lane) : "hwlab-dev";
|
|
|
|
|
const tag = imageTagForSource(args, source);
|
|
|
|
|
return {
|
|
|
|
|
catalogVersion: "v1",
|
|
|
|
@@ -642,7 +671,7 @@ function artifactCatalogSkeleton({ args, deploy, source }) {
|
|
|
|
|
publishedAt: null
|
|
|
|
|
},
|
|
|
|
|
allowedProfiles: [environment],
|
|
|
|
|
forbiddenProfiles: args.lane === "v02" ? ["dev", "prod"] : ["prod"],
|
|
|
|
|
forbiddenProfiles: isRuntimeLane(args.lane) ? ["dev", "prod"] : ["prod"],
|
|
|
|
|
services: serviceIdsForLane(args.lane, deploy).map((serviceId) => artifactCatalogSkeletonService({ args, deploy, source, serviceId, environment, namespace, tag }))
|
|
|
|
|
};
|
|
|
|
|
}
|
|
|
|
@@ -687,47 +716,48 @@ function artifactCatalogSkeletonService({ args, deploy, source, serviceId, envir
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
function namespaceNameForProfile(profile) {
|
|
|
|
|
if (profile === "v02") return "hwlab-v02";
|
|
|
|
|
if (isRuntimeLane(profile)) return `hwlab-${profile}`;
|
|
|
|
|
return profile === "prod" ? "hwlab-prod" : "hwlab-dev";
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
function runtimePathForProfile(profile) {
|
|
|
|
|
if (profile === "v02") return "runtime-v02";
|
|
|
|
|
if (isRuntimeLane(profile)) return `runtime-${profile}`;
|
|
|
|
|
return profile === "prod" ? "runtime-prod" : "runtime-dev";
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
function runtimeLabelForProfile(profile) {
|
|
|
|
|
if (profile === "v02") return "v02";
|
|
|
|
|
if (isRuntimeLane(profile)) return profile;
|
|
|
|
|
return profile === "prod" ? "prod" : "dev";
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
function gitopsTargetForProfile(profile) {
|
|
|
|
|
return profile === "v02" ? "v02" : "g14";
|
|
|
|
|
return isRuntimeLane(profile) ? profile : "g14";
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
function profileEndpoint(args, profile) {
|
|
|
|
|
if (profile === "v02") return { runtimeEndpoint: args.runtimeEndpoint, webEndpoint: args.webEndpoint };
|
|
|
|
|
if (isRuntimeLane(profile)) return { runtimeEndpoint: args.runtimeEndpoint, webEndpoint: args.webEndpoint };
|
|
|
|
|
return profile === "prod"
|
|
|
|
|
? { runtimeEndpoint: args.prodRuntimeEndpoint, webEndpoint: args.prodWebEndpoint }
|
|
|
|
|
: { runtimeEndpoint: args.runtimeEndpoint, webEndpoint: args.webEndpoint };
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
function laneRuntimeProfiles(args) {
|
|
|
|
|
return args.lane === "v02" ? ["v02"] : ["dev", "prod"];
|
|
|
|
|
return isRuntimeLane(args.lane) ? [args.lane] : ["dev", "prod"];
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
function laneNames(args) {
|
|
|
|
|
if (args.lane === "v02") {
|
|
|
|
|
if (isRuntimeLane(args.lane)) {
|
|
|
|
|
const namespace = namespaceNameForProfile(args.lane);
|
|
|
|
|
return {
|
|
|
|
|
gitopsTarget: "v02",
|
|
|
|
|
pipeline: "hwlab-v02-ci-image-publish",
|
|
|
|
|
poller: "hwlab-v02-branch-poller",
|
|
|
|
|
reconciler: "hwlab-v02-control-plane-reconciler",
|
|
|
|
|
serviceAccount: "hwlab-v02-tekton-runner",
|
|
|
|
|
pipelineRunPrefix: "hwlab-v02-ci-poll-",
|
|
|
|
|
runtimeNamespace: "hwlab-v02",
|
|
|
|
|
runtimePath: "deploy/gitops/g14/runtime-v02",
|
|
|
|
|
argoApplications: ["argocd/hwlab-g14-v02"]
|
|
|
|
|
gitopsTarget: args.lane,
|
|
|
|
|
pipeline: `${namespace}-ci-image-publish`,
|
|
|
|
|
poller: `${namespace}-branch-poller`,
|
|
|
|
|
reconciler: `${namespace}-control-plane-reconciler`,
|
|
|
|
|
serviceAccount: `${namespace}-tekton-runner`,
|
|
|
|
|
pipelineRunPrefix: `${namespace}-ci-poll-`,
|
|
|
|
|
runtimeNamespace: namespace,
|
|
|
|
|
runtimePath: `deploy/gitops/g14/runtime-${args.lane}`,
|
|
|
|
|
argoApplications: [`argocd/hwlab-g14-${args.lane}`]
|
|
|
|
|
};
|
|
|
|
|
}
|
|
|
|
|
return {
|
|
|
|
@@ -744,7 +774,7 @@ function laneNames(args) {
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
function argoApplicationName(profile) {
|
|
|
|
|
return profile === "v02" ? "hwlab-g14-v02" : `hwlab-g14-${profile}`;
|
|
|
|
|
return `hwlab-g14-${profile}`;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
function imageTagForSource(args, source) {
|
|
|
|
@@ -1126,13 +1156,13 @@ function transformListNamespace(value, namespace, labels, annotations) {
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
function transformServices({ services, namespace, labels, annotations, profile = "dev", deploy = null }) {
|
|
|
|
|
const v02ObservableServiceIds = profile === "v02" ? v02ObservableServiceIdsForDeploy(deploy) : new Set();
|
|
|
|
|
const observableServiceIds = isRuntimeLane(profile) ? runtimeLaneObservableServiceIdsForDeploy(deploy, profile) : new Set();
|
|
|
|
|
const result = transformListNamespace(services, namespace, labels, annotations);
|
|
|
|
|
if (result.kind === "List") {
|
|
|
|
|
result.items = (result.items ?? []).filter((item) => !(profile === "v02" && isV02RemovedServiceId(serviceIdForWorkload(item, null))));
|
|
|
|
|
result.items = (result.items ?? []).filter((item) => !(isRuntimeLane(profile) && isV02RemovedServiceId(serviceIdForWorkload(item, null))));
|
|
|
|
|
for (const item of result.items) {
|
|
|
|
|
const serviceId = serviceIdForWorkload(item, null);
|
|
|
|
|
if (profile !== "v02" || !v02ObservableServiceIds.has(serviceId)) continue;
|
|
|
|
|
if (!isRuntimeLane(profile) || !observableServiceIds.has(serviceId)) continue;
|
|
|
|
|
label(item.metadata ??= {}, v02MetricsLabels(serviceId));
|
|
|
|
|
upsertV02MetricsPort(item);
|
|
|
|
|
}
|
|
|
|
@@ -1140,17 +1170,18 @@ function transformServices({ services, namespace, labels, annotations, profile =
|
|
|
|
|
return result;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
function observabilityManifest({ deploy, namespace, labels, annotations, metricsSidecarScript }) {
|
|
|
|
|
function observabilityManifest({ deploy, profile, namespace, labels, annotations, metricsSidecarScript }) {
|
|
|
|
|
assert.ok(isRuntimeLane(profile), `observability profile must be a runtime lane, got ${profile}`);
|
|
|
|
|
const baseLabels = {
|
|
|
|
|
...labels,
|
|
|
|
|
"app.kubernetes.io/part-of": "hwlab",
|
|
|
|
|
"hwlab.pikastech.local/monitoring": "enabled"
|
|
|
|
|
};
|
|
|
|
|
const serviceMonitors = v02ObservableServicesForDeploy(deploy).map((service) => ({
|
|
|
|
|
const serviceMonitors = runtimeLaneObservableServicesForDeploy(deploy, profile).map((service) => ({
|
|
|
|
|
apiVersion: "monitoring.coreos.com/v1",
|
|
|
|
|
kind: "ServiceMonitor",
|
|
|
|
|
metadata: {
|
|
|
|
|
name: `hwlab-v02-${service.serviceId}`,
|
|
|
|
|
name: `hwlab-${profile}-${service.serviceId}`,
|
|
|
|
|
namespace,
|
|
|
|
|
labels: { ...baseLabels, "hwlab.pikastech.local/service-id": service.serviceId },
|
|
|
|
|
annotations
|
|
|
|
@@ -1159,7 +1190,7 @@ function observabilityManifest({ deploy, namespace, labels, annotations, metrics
|
|
|
|
|
namespaceSelector: { matchNames: [namespace] },
|
|
|
|
|
selector: {
|
|
|
|
|
matchLabels: {
|
|
|
|
|
"hwlab.pikastech.local/gitops-target": "v02",
|
|
|
|
|
"hwlab.pikastech.local/gitops-target": profile,
|
|
|
|
|
"hwlab.pikastech.local/monitoring": "enabled",
|
|
|
|
|
"hwlab.pikastech.local/service-id": service.serviceId
|
|
|
|
|
}
|
|
|
|
@@ -1178,30 +1209,30 @@ function observabilityManifest({ deploy, namespace, labels, annotations, metrics
|
|
|
|
|
{
|
|
|
|
|
apiVersion: "v1",
|
|
|
|
|
kind: "ConfigMap",
|
|
|
|
|
metadata: { name: "hwlab-v02-metrics-sidecar", namespace, labels: baseLabels, annotations },
|
|
|
|
|
metadata: { name: `hwlab-${profile}-metrics-sidecar`, namespace, labels: baseLabels, annotations },
|
|
|
|
|
data: { "metrics-sidecar.mjs": metricsSidecarScript }
|
|
|
|
|
},
|
|
|
|
|
...serviceMonitors,
|
|
|
|
|
{
|
|
|
|
|
apiVersion: "monitoring.coreos.com/v1",
|
|
|
|
|
kind: "PrometheusRule",
|
|
|
|
|
metadata: { name: "hwlab-v02-observability", namespace, labels: baseLabels, annotations },
|
|
|
|
|
metadata: { name: `hwlab-${profile}-observability`, namespace, labels: baseLabels, annotations },
|
|
|
|
|
spec: {
|
|
|
|
|
groups: [{
|
|
|
|
|
name: "hwlab-v02-observability",
|
|
|
|
|
name: `hwlab-${profile}-observability`,
|
|
|
|
|
rules: [
|
|
|
|
|
{
|
|
|
|
|
alert: "HWLABV02MetricsTargetDown",
|
|
|
|
|
expr: 'up{namespace="hwlab-v02"} == 0',
|
|
|
|
|
alert: `HWLAB${profile.toUpperCase()}MetricsTargetDown`,
|
|
|
|
|
expr: `up{namespace="${namespace}"} == 0`,
|
|
|
|
|
for: "5m",
|
|
|
|
|
labels: { severity: "warning", lane: "v02" },
|
|
|
|
|
annotations: { summary: "HWLAB v0.2 metrics target is down", description: "Prometheus cannot scrape a HWLAB v0.2 metrics target." }
|
|
|
|
|
labels: { severity: "warning", lane: profile },
|
|
|
|
|
annotations: { summary: `HWLAB ${versionNameForRuntimeLane(profile)} metrics target is down`, description: `Prometheus cannot scrape a HWLAB ${versionNameForRuntimeLane(profile)} metrics target.` }
|
|
|
|
|
},
|
|
|
|
|
{
|
|
|
|
|
alert: "HWLABV02ServiceHealthProbeFailed",
|
|
|
|
|
expr: 'hwlab_service_health_probe_success{namespace="hwlab-v02"} == 0',
|
|
|
|
|
alert: `HWLAB${profile.toUpperCase()}ServiceHealthProbeFailed`,
|
|
|
|
|
expr: `hwlab_service_health_probe_success{namespace="${namespace}"} == 0`,
|
|
|
|
|
for: "5m",
|
|
|
|
|
labels: { severity: "warning", lane: "v02" },
|
|
|
|
|
labels: { severity: "warning", lane: profile },
|
|
|
|
|
annotations: { summary: "HWLAB v0.2 service health probe failed", description: "The metrics sidecar cannot reach the service health endpoint inside the pod network." }
|
|
|
|
|
}
|
|
|
|
|
]
|
|
|
|
@@ -1920,8 +1951,8 @@ if [ -s /workspace/source/dev-artifacts.json ]; then
|
|
|
|
|
node scripts/refresh-artifact-catalog.mjs --lane "$lane" --catalog-path "$catalog_path" --deploy-config deploy/deploy.yaml --image-tag-mode "$(params.image-tag-mode)" --target-ref "$(params.revision)" --publish-report /workspace/source/dev-artifacts.json --write
|
|
|
|
|
fi
|
|
|
|
|
gitops_render_started_ms="$(ci_now_ms)"
|
|
|
|
|
node scripts/g14-gitops-render.mjs --lane "$lane" --catalog-path "$catalog_path" --image-tag-mode "$(params.image-tag-mode)" --source-revision "$(params.revision)" --source-repo "$(params.git-url)" --source-branch "$(params.source-branch)" --gitops-branch "$(params.gitops-branch)" --registry-prefix "$(params.registry-prefix)" --use-deploy-images
|
|
|
|
|
node scripts/g14-gitops-render.mjs --lane "$lane" --catalog-path "$catalog_path" --image-tag-mode "$(params.image-tag-mode)" --source-revision "$(params.revision)" --source-repo "$(params.git-url)" --source-branch "$(params.source-branch)" --gitops-branch "$(params.gitops-branch)" --registry-prefix "$(params.registry-prefix)" --use-deploy-images --check
|
|
|
|
|
node scripts/run-bun.mjs scripts/g14-gitops-render.mjs --lane "$lane" --catalog-path "$catalog_path" --image-tag-mode "$(params.image-tag-mode)" --source-revision "$(params.revision)" --source-repo "$(params.git-url)" --source-branch "$(params.source-branch)" --gitops-branch "$(params.gitops-branch)" --registry-prefix "$(params.registry-prefix)" --use-deploy-images
|
|
|
|
|
node scripts/run-bun.mjs scripts/g14-gitops-render.mjs --lane "$lane" --catalog-path "$catalog_path" --image-tag-mode "$(params.image-tag-mode)" --source-revision "$(params.revision)" --source-repo "$(params.git-url)" --source-branch "$(params.source-branch)" --gitops-branch "$(params.gitops-branch)" --registry-prefix "$(params.registry-prefix)" --use-deploy-images --check
|
|
|
|
|
ci_timing_emit gitops-render succeeded "$gitops_render_started_ms"
|
|
|
|
|
check_source_head before-push
|
|
|
|
|
workdir="$(mktemp -d)"
|
|
|
|
@@ -2087,8 +2118,8 @@ revision="$(git rev-parse HEAD)"
|
|
|
|
|
: "\${LANE:?LANE is required}"
|
|
|
|
|
: "\${CATALOG_PATH:?CATALOG_PATH is required}"
|
|
|
|
|
: "\${IMAGE_TAG_MODE:?IMAGE_TAG_MODE is required}"
|
|
|
|
|
node scripts/g14-gitops-render.mjs --lane "$LANE" --catalog-path "$CATALOG_PATH" --image-tag-mode "$IMAGE_TAG_MODE" --source-revision "$revision" --source-repo "$GIT_URL" --source-branch "$SOURCE_BRANCH" --gitops-branch "$GITOPS_BRANCH" --registry-prefix "$REGISTRY_PREFIX"
|
|
|
|
|
node scripts/g14-gitops-render.mjs --lane "$LANE" --catalog-path "$CATALOG_PATH" --image-tag-mode "$IMAGE_TAG_MODE" --source-revision "$revision" --source-repo "$GIT_URL" --source-branch "$SOURCE_BRANCH" --gitops-branch "$GITOPS_BRANCH" --registry-prefix "$REGISTRY_PREFIX" --check
|
|
|
|
|
node scripts/run-bun.mjs scripts/g14-gitops-render.mjs --lane "$LANE" --catalog-path "$CATALOG_PATH" --image-tag-mode "$IMAGE_TAG_MODE" --source-revision "$revision" --source-repo "$GIT_URL" --source-branch "$SOURCE_BRANCH" --gitops-branch "$GITOPS_BRANCH" --registry-prefix "$REGISTRY_PREFIX"
|
|
|
|
|
node scripts/run-bun.mjs scripts/g14-gitops-render.mjs --lane "$LANE" --catalog-path "$CATALOG_PATH" --image-tag-mode "$IMAGE_TAG_MODE" --source-revision "$revision" --source-repo "$GIT_URL" --source-branch "$SOURCE_BRANCH" --gitops-branch "$GITOPS_BRANCH" --registry-prefix "$REGISTRY_PREFIX" --check
|
|
|
|
|
node <<'NODE'
|
|
|
|
|
const fs = require("node:fs");
|
|
|
|
|
const https = require("node:https");
|
|
|
|
@@ -2357,20 +2388,21 @@ NODE
|
|
|
|
|
|
|
|
|
|
function ciLaneSettings(argsOrLane = "g14") {
|
|
|
|
|
const lane = typeof argsOrLane === "string" ? argsOrLane : argsOrLane.lane;
|
|
|
|
|
if (lane === "v02") {
|
|
|
|
|
if (isRuntimeLane(lane)) {
|
|
|
|
|
const namespace = namespaceNameForProfile(lane);
|
|
|
|
|
return {
|
|
|
|
|
lane: "v02",
|
|
|
|
|
profile: "v02",
|
|
|
|
|
gitopsTarget: "v02",
|
|
|
|
|
pipelineName: "hwlab-v02-ci-image-publish",
|
|
|
|
|
pipelineRunPrefix: "hwlab-v02-ci-poll",
|
|
|
|
|
serviceAccountName: "hwlab-v02-tekton-runner",
|
|
|
|
|
pollerName: "hwlab-v02-branch-poller",
|
|
|
|
|
controlPlaneReconcilerName: "hwlab-v02-control-plane-reconciler",
|
|
|
|
|
tektonDir: "tekton-v02",
|
|
|
|
|
catalogPath: "deploy/artifact-catalog.v02.json",
|
|
|
|
|
imageTagMode: "full",
|
|
|
|
|
runtimeNamespace: "hwlab-v02"
|
|
|
|
|
lane,
|
|
|
|
|
profile: lane,
|
|
|
|
|
gitopsTarget: lane,
|
|
|
|
|
pipelineName: `${namespace}-ci-image-publish`,
|
|
|
|
|
pipelineRunPrefix: `${namespace}-ci-poll`,
|
|
|
|
|
serviceAccountName: `${namespace}-tekton-runner`,
|
|
|
|
|
pollerName: `${namespace}-branch-poller`,
|
|
|
|
|
controlPlaneReconcilerName: `${namespace}-control-plane-reconciler`,
|
|
|
|
|
tektonDir: `tekton-${lane}`,
|
|
|
|
|
catalogPath: typeof argsOrLane === "object" ? argsOrLane.catalogPath : `deploy/artifact-catalog.${lane}.json`,
|
|
|
|
|
imageTagMode: typeof argsOrLane === "object" ? argsOrLane.imageTagMode : "full",
|
|
|
|
|
runtimeNamespace: namespace
|
|
|
|
|
};
|
|
|
|
|
}
|
|
|
|
|
return {
|
|
|
|
@@ -2391,7 +2423,10 @@ function ciLaneSettings(argsOrLane = "g14") {
|
|
|
|
|
|
|
|
|
|
function tektonRbac(args = { lane: "g14" }) {
|
|
|
|
|
const settings = ciLaneSettings(args);
|
|
|
|
|
if (settings.lane === "v02") {
|
|
|
|
|
if (isRuntimeLane(settings.lane)) {
|
|
|
|
|
const runtimeObserverName = `${settings.runtimeNamespace}-runtime-observer`;
|
|
|
|
|
const pipelineRunWriterName = `${settings.runtimeNamespace}-pipelinerun-writer`;
|
|
|
|
|
const argoReconcilerName = `${settings.runtimeNamespace}-argocd-reconciler`;
|
|
|
|
|
return {
|
|
|
|
|
apiVersion: "v1",
|
|
|
|
|
kind: "List",
|
|
|
|
@@ -2401,7 +2436,7 @@ function tektonRbac(args = { lane: "g14" }) {
|
|
|
|
|
{
|
|
|
|
|
apiVersion: "rbac.authorization.k8s.io/v1",
|
|
|
|
|
kind: "Role",
|
|
|
|
|
metadata: { name: "hwlab-v02-runtime-observer", namespace: "hwlab-v02" },
|
|
|
|
|
metadata: { name: runtimeObserverName, namespace: settings.runtimeNamespace },
|
|
|
|
|
rules: [
|
|
|
|
|
{ apiGroups: [""], resources: ["pods", "services", "configmaps"], verbs: ["get", "list", "watch"] },
|
|
|
|
|
{ apiGroups: ["apps"], resources: ["deployments", "statefulsets"], verbs: ["get", "list", "watch"] },
|
|
|
|
@@ -2411,14 +2446,14 @@ function tektonRbac(args = { lane: "g14" }) {
|
|
|
|
|
{
|
|
|
|
|
apiVersion: "rbac.authorization.k8s.io/v1",
|
|
|
|
|
kind: "RoleBinding",
|
|
|
|
|
metadata: { name: "hwlab-v02-runtime-observer", namespace: "hwlab-v02" },
|
|
|
|
|
metadata: { name: runtimeObserverName, namespace: settings.runtimeNamespace },
|
|
|
|
|
subjects: [{ kind: "ServiceAccount", name: settings.serviceAccountName, namespace: "hwlab-ci" }],
|
|
|
|
|
roleRef: { apiGroup: "rbac.authorization.k8s.io", kind: "Role", name: "hwlab-v02-runtime-observer" }
|
|
|
|
|
roleRef: { apiGroup: "rbac.authorization.k8s.io", kind: "Role", name: runtimeObserverName }
|
|
|
|
|
},
|
|
|
|
|
{
|
|
|
|
|
apiVersion: "rbac.authorization.k8s.io/v1",
|
|
|
|
|
kind: "Role",
|
|
|
|
|
metadata: { name: "hwlab-v02-pipelinerun-writer", namespace: "hwlab-ci" },
|
|
|
|
|
metadata: { name: pipelineRunWriterName, namespace: "hwlab-ci" },
|
|
|
|
|
rules: [
|
|
|
|
|
{ apiGroups: ["tekton.dev"], resources: ["pipelineruns"], verbs: ["get", "list", "create"] },
|
|
|
|
|
{ apiGroups: ["tekton.dev"], resources: ["pipelines"], verbs: ["get", "patch", "create"] },
|
|
|
|
@@ -2430,14 +2465,14 @@ function tektonRbac(args = { lane: "g14" }) {
|
|
|
|
|
{
|
|
|
|
|
apiVersion: "rbac.authorization.k8s.io/v1",
|
|
|
|
|
kind: "RoleBinding",
|
|
|
|
|
metadata: { name: "hwlab-v02-pipelinerun-writer", namespace: "hwlab-ci" },
|
|
|
|
|
metadata: { name: pipelineRunWriterName, namespace: "hwlab-ci" },
|
|
|
|
|
subjects: [{ kind: "ServiceAccount", name: settings.serviceAccountName, namespace: "hwlab-ci" }],
|
|
|
|
|
roleRef: { apiGroup: "rbac.authorization.k8s.io", kind: "Role", name: "hwlab-v02-pipelinerun-writer" }
|
|
|
|
|
roleRef: { apiGroup: "rbac.authorization.k8s.io", kind: "Role", name: pipelineRunWriterName }
|
|
|
|
|
},
|
|
|
|
|
{
|
|
|
|
|
apiVersion: "rbac.authorization.k8s.io/v1",
|
|
|
|
|
kind: "Role",
|
|
|
|
|
metadata: { name: "hwlab-v02-argocd-reconciler", namespace: "argocd" },
|
|
|
|
|
metadata: { name: argoReconcilerName, namespace: "argocd" },
|
|
|
|
|
rules: [
|
|
|
|
|
{ apiGroups: ["argoproj.io"], resources: ["applications", "appprojects"], verbs: ["get", "patch", "create"] },
|
|
|
|
|
{ apiGroups: ["rbac.authorization.k8s.io"], resources: ["roles", "rolebindings"], verbs: ["get", "patch", "create"] }
|
|
|
|
@@ -2446,9 +2481,9 @@ function tektonRbac(args = { lane: "g14" }) {
|
|
|
|
|
{
|
|
|
|
|
apiVersion: "rbac.authorization.k8s.io/v1",
|
|
|
|
|
kind: "RoleBinding",
|
|
|
|
|
metadata: { name: "hwlab-v02-argocd-reconciler", namespace: "argocd" },
|
|
|
|
|
metadata: { name: argoReconcilerName, namespace: "argocd" },
|
|
|
|
|
subjects: [{ kind: "ServiceAccount", name: settings.serviceAccountName, namespace: "hwlab-ci" }],
|
|
|
|
|
roleRef: { apiGroup: "rbac.authorization.k8s.io", kind: "Role", name: "hwlab-v02-argocd-reconciler" }
|
|
|
|
|
roleRef: { apiGroup: "rbac.authorization.k8s.io", kind: "Role", name: argoReconcilerName }
|
|
|
|
|
}
|
|
|
|
|
]
|
|
|
|
|
};
|
|
|
|
@@ -4004,15 +4039,16 @@ function contentType(filePath) {
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
function argoProject(args = { lane: "g14" }) {
|
|
|
|
|
if (args.lane === "v02") {
|
|
|
|
|
if (isRuntimeLane(args.lane)) {
|
|
|
|
|
const namespace = namespaceNameForProfile(args.lane);
|
|
|
|
|
return {
|
|
|
|
|
apiVersion: "argoproj.io/v1alpha1",
|
|
|
|
|
kind: "AppProject",
|
|
|
|
|
metadata: { name: "hwlab-v02", namespace: "argocd" },
|
|
|
|
|
metadata: { name: namespace, namespace: "argocd" },
|
|
|
|
|
spec: {
|
|
|
|
|
description: "HWLAB v0.2 additive GitOps project on G14; DEV/PROD remain outside this lane.",
|
|
|
|
|
description: `HWLAB ${versionNameForRuntimeLane(args.lane)} additive GitOps project on G14; DEV/PROD remain outside this lane.`,
|
|
|
|
|
sourceRepos: [args.gitReadUrl],
|
|
|
|
|
destinations: [{ server: "https://kubernetes.default.svc", namespace: "hwlab-v02" }],
|
|
|
|
|
destinations: [{ server: "https://kubernetes.default.svc", namespace }],
|
|
|
|
|
clusterResourceWhitelist: [{ group: "", kind: "Namespace" }],
|
|
|
|
|
namespaceResourceWhitelist: [{ group: "*", kind: "*" }]
|
|
|
|
|
}
|
|
|
|
@@ -4039,8 +4075,8 @@ function argoApplication(args, profile = "dev") {
|
|
|
|
|
const namespace = namespaceNameForProfile(profile);
|
|
|
|
|
const runtimePath = runtimePathForProfile(profile);
|
|
|
|
|
const gitopsTarget = gitopsTargetForProfile(profile);
|
|
|
|
|
const project = profile === "v02" ? "hwlab-v02" : "hwlab-g14";
|
|
|
|
|
const repoURL = profile === "v02" ? args.gitReadUrl : args.sourceRepo;
|
|
|
|
|
const project = isRuntimeLane(profile) ? namespace : "hwlab-g14";
|
|
|
|
|
const repoURL = isRuntimeLane(profile) ? args.gitReadUrl : args.sourceRepo;
|
|
|
|
|
return {
|
|
|
|
|
apiVersion: "argoproj.io/v1alpha1",
|
|
|
|
|
kind: "Application",
|
|
|
|
@@ -4053,7 +4089,7 @@ function argoApplication(args, profile = "dev") {
|
|
|
|
|
project,
|
|
|
|
|
source: { repoURL, targetRevision: args.gitopsBranch, path: `deploy/gitops/g14/${runtimePath}` },
|
|
|
|
|
destination: { server: "https://kubernetes.default.svc", namespace },
|
|
|
|
|
syncPolicy: { automated: { prune: profile === "v02", selfHeal: true }, syncOptions: ["CreateNamespace=true", "ApplyOutOfSyncOnly=true"] }
|
|
|
|
|
syncPolicy: { automated: { prune: isRuntimeLane(profile), selfHeal: true }, syncOptions: ["CreateNamespace=true", "ApplyOutOfSyncOnly=true"] }
|
|
|
|
|
}
|
|
|
|
|
};
|
|
|
|
|
}
|
|
|
|
@@ -4061,11 +4097,11 @@ function argoApplication(args, profile = "dev") {
|
|
|
|
|
function g14FrpcManifest({ profile = "dev", source = null } = {}) {
|
|
|
|
|
const namespace = namespaceNameForProfile(profile);
|
|
|
|
|
const profileLabel = runtimeLabelForProfile(profile);
|
|
|
|
|
const deploymentName = profile === "v02" ? "hwlab-v02-frpc" : profile === "prod" ? "hwlab-g14-prod-frpc" : "hwlab-g14-frpc";
|
|
|
|
|
const deploymentName = isRuntimeLane(profile) ? `hwlab-${profile}-frpc` : profile === "prod" ? "hwlab-g14-prod-frpc" : "hwlab-g14-frpc";
|
|
|
|
|
const configName = `${deploymentName}-config`;
|
|
|
|
|
const proxyPrefix = profile === "v02" ? "hwlab-v02" : profile === "prod" ? "hwlab-g14-prod" : "hwlab-g14";
|
|
|
|
|
const webRemotePort = profile === "v02" ? 19666 : profile === "prod" ? 18666 : 17666;
|
|
|
|
|
const edgeRemotePort = profile === "v02" ? 19667 : profile === "prod" ? 18667 : 17667;
|
|
|
|
|
const proxyPrefix = isRuntimeLane(profile) ? `hwlab-${profile}` : profile === "prod" ? "hwlab-g14-prod" : "hwlab-g14";
|
|
|
|
|
const webRemotePort = isRuntimeLane(profile) ? defaultPortForRuntimeLane(profile) : profile === "prod" ? 18666 : 17666;
|
|
|
|
|
const edgeRemotePort = isRuntimeLane(profile) ? webRemotePort + 1 : profile === "prod" ? 18667 : 17667;
|
|
|
|
|
const labels = {
|
|
|
|
|
"app.kubernetes.io/name": deploymentName,
|
|
|
|
|
"app.kubernetes.io/part-of": "hwlab",
|
|
|
|
@@ -4075,7 +4111,7 @@ function g14FrpcManifest({ profile = "dev", source = null } = {}) {
|
|
|
|
|
};
|
|
|
|
|
const sourceCommitLabel = source?.full ? { "hwlab.pikastech.local/source-commit": source.full } : {};
|
|
|
|
|
const renderedLabels = { ...labels, ...sourceCommitLabel };
|
|
|
|
|
const podNetwork = profile === "v02" ? {
|
|
|
|
|
const podNetwork = isRuntimeLane(profile) ? {
|
|
|
|
|
hostNetwork: true,
|
|
|
|
|
dnsPolicy: "ClusterFirstWithHostNet"
|
|
|
|
|
} : {};
|
|
|
|
@@ -4589,24 +4625,25 @@ function deviceAgent71FreqManifest({ profile = "dev", source, registryPrefix, ca
|
|
|
|
|
};
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
function v02PostgresManifest({ migrationSql, source }) {
|
|
|
|
|
const namespace = "hwlab-v02";
|
|
|
|
|
const name = "hwlab-v02-postgres";
|
|
|
|
|
function v02PostgresManifest({ profile = "v02", migrationSql, source }) {
|
|
|
|
|
const namespace = namespaceNameForProfile(profile);
|
|
|
|
|
const name = `${namespace}-postgres`;
|
|
|
|
|
const dbName = `hwlab_${profile}`;
|
|
|
|
|
const labels = {
|
|
|
|
|
"app.kubernetes.io/name": name,
|
|
|
|
|
"app.kubernetes.io/part-of": "hwlab",
|
|
|
|
|
"hwlab.pikastech.local/environment": "v02",
|
|
|
|
|
"hwlab.pikastech.local/gitops-target": "v02",
|
|
|
|
|
"hwlab.pikastech.local/profile": "v02",
|
|
|
|
|
"hwlab.pikastech.local/environment": profile,
|
|
|
|
|
"hwlab.pikastech.local/gitops-target": profile,
|
|
|
|
|
"hwlab.pikastech.local/profile": profile,
|
|
|
|
|
"hwlab.pikastech.local/source-commit": source.full
|
|
|
|
|
};
|
|
|
|
|
const migrationSha256 = createHash("sha256").update(migrationSql).digest("hex");
|
|
|
|
|
const templateLabels = {
|
|
|
|
|
"app.kubernetes.io/name": name,
|
|
|
|
|
"app.kubernetes.io/part-of": "hwlab",
|
|
|
|
|
"hwlab.pikastech.local/environment": "v02",
|
|
|
|
|
"hwlab.pikastech.local/gitops-target": "v02",
|
|
|
|
|
"hwlab.pikastech.local/profile": "v02",
|
|
|
|
|
"hwlab.pikastech.local/environment": profile,
|
|
|
|
|
"hwlab.pikastech.local/gitops-target": profile,
|
|
|
|
|
"hwlab.pikastech.local/profile": profile,
|
|
|
|
|
"hwlab.pikastech.local/migration-sha256": k8sLabelHash(migrationSha256)
|
|
|
|
|
};
|
|
|
|
|
const templateAnnotations = { "hwlab.pikastech.local/migration-sha256": migrationSha256 };
|
|
|
|
@@ -4642,9 +4679,9 @@ function v02PostgresManifest({ migrationSql, source }) {
|
|
|
|
|
image: "127.0.0.1:5000/hwlab/postgres:16-alpine",
|
|
|
|
|
imagePullPolicy: "IfNotPresent",
|
|
|
|
|
env: [
|
|
|
|
|
{ name: "POSTGRES_DB", value: "hwlab_v02" },
|
|
|
|
|
{ name: "POSTGRES_USER", value: "hwlab_v02" },
|
|
|
|
|
{ name: "POSTGRES_PASSWORD", valueFrom: { secretKeyRef: { name: "hwlab-v02-postgres", key: "POSTGRES_PASSWORD" } } }
|
|
|
|
|
{ name: "POSTGRES_DB", value: dbName },
|
|
|
|
|
{ name: "POSTGRES_USER", value: dbName },
|
|
|
|
|
{ name: "POSTGRES_PASSWORD", valueFrom: { secretKeyRef: { name, key: "POSTGRES_PASSWORD" } } }
|
|
|
|
|
],
|
|
|
|
|
ports: [{ name: "postgres", containerPort: 5432 }],
|
|
|
|
|
readinessProbe: { tcpSocket: { port: "postgres" }, initialDelaySeconds: 5, periodSeconds: 10 },
|
|
|
|
@@ -4667,17 +4704,17 @@ function v02PostgresManifest({ migrationSql, source }) {
|
|
|
|
|
};
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
function v02OpenFgaManifest({ source }) {
|
|
|
|
|
const namespace = "hwlab-v02";
|
|
|
|
|
function v02OpenFgaManifest({ profile = "v02", source }) {
|
|
|
|
|
const namespace = namespaceNameForProfile(profile);
|
|
|
|
|
const name = "hwlab-openfga";
|
|
|
|
|
const image = "127.0.0.1:5000/hwlab/openfga:v1.17.0";
|
|
|
|
|
const labels = {
|
|
|
|
|
"app.kubernetes.io/name": name,
|
|
|
|
|
"app.kubernetes.io/part-of": "hwlab",
|
|
|
|
|
"hwlab.pikastech.local/component": "authorization",
|
|
|
|
|
"hwlab.pikastech.local/environment": "v02",
|
|
|
|
|
"hwlab.pikastech.local/gitops-target": "v02",
|
|
|
|
|
"hwlab.pikastech.local/profile": "v02",
|
|
|
|
|
"hwlab.pikastech.local/environment": profile,
|
|
|
|
|
"hwlab.pikastech.local/gitops-target": profile,
|
|
|
|
|
"hwlab.pikastech.local/profile": profile,
|
|
|
|
|
"hwlab.pikastech.local/service-id": name,
|
|
|
|
|
"hwlab.pikastech.local/source-commit": source.full
|
|
|
|
|
};
|
|
|
|
@@ -4685,9 +4722,9 @@ function v02OpenFgaManifest({ source }) {
|
|
|
|
|
const selector = { "app.kubernetes.io/name": name };
|
|
|
|
|
const env = [
|
|
|
|
|
{ name: "OPENFGA_DATASTORE_ENGINE", value: "postgres" },
|
|
|
|
|
{ name: "OPENFGA_DATASTORE_URI", valueFrom: { secretKeyRef: { name: "hwlab-v02-openfga", key: "datastore-uri" } } },
|
|
|
|
|
{ name: "OPENFGA_DATASTORE_URI", valueFrom: { secretKeyRef: { name: `hwlab-${profile}-openfga`, key: "datastore-uri" } } },
|
|
|
|
|
{ name: "OPENFGA_AUTHN_METHOD", value: "preshared" },
|
|
|
|
|
{ name: "OPENFGA_AUTHN_PRESHARED_KEYS", valueFrom: { secretKeyRef: { name: "hwlab-v02-openfga", key: "authn-preshared-key" } } },
|
|
|
|
|
{ name: "OPENFGA_AUTHN_PRESHARED_KEYS", valueFrom: { secretKeyRef: { name: `hwlab-${profile}-openfga`, key: "authn-preshared-key" } } },
|
|
|
|
|
{ name: "OPENFGA_PLAYGROUND_ENABLED", value: "false" },
|
|
|
|
|
{ name: "OPENFGA_HTTP_ADDR", value: "0.0.0.0:8080" },
|
|
|
|
|
{ name: "OPENFGA_GRPC_ADDR", value: "0.0.0.0:8081" }
|
|
|
|
@@ -4815,17 +4852,26 @@ async function plannedFiles(args) {
|
|
|
|
|
readJson("deploy/k8s/dev/health-contract.yaml"),
|
|
|
|
|
readJson("deploy/k8s/base/workloads.yaml")
|
|
|
|
|
]);
|
|
|
|
|
args = applyRuntimeLaneDeployConfig(args, deploy, {
|
|
|
|
|
defaultBranch,
|
|
|
|
|
defaultGitopsBranch,
|
|
|
|
|
defaultCatalogPath,
|
|
|
|
|
defaultRuntimeEndpoint,
|
|
|
|
|
defaultWebEndpoint,
|
|
|
|
|
defaultV02RuntimeEndpoint,
|
|
|
|
|
defaultSourceRepo
|
|
|
|
|
});
|
|
|
|
|
const source = await resolveSourceRevision(args.sourceRevision);
|
|
|
|
|
source.imageTag = imageTagForSource(args, source);
|
|
|
|
|
let artifactCatalog = await readJsonIfPresent(args.catalogPath, null);
|
|
|
|
|
if (!artifactCatalog && args.lane === "v02") artifactCatalog = artifactCatalogSkeleton({ args, deploy, source });
|
|
|
|
|
if (!artifactCatalog && isRuntimeLane(args.lane)) artifactCatalog = artifactCatalogSkeleton({ args, deploy, source });
|
|
|
|
|
if (!artifactCatalog) artifactCatalog = await readJson(args.catalogPath);
|
|
|
|
|
const settings = ciLaneSettings(args);
|
|
|
|
|
const profiles = laneRuntimeProfiles(args);
|
|
|
|
|
const migrationSql = args.lane === "v02"
|
|
|
|
|
const migrationSql = isRuntimeLane(args.lane)
|
|
|
|
|
? await readFile(path.join(repoRoot, "internal/db/migrations/0001_cloud_core_skeleton.sql"), "utf8")
|
|
|
|
|
: null;
|
|
|
|
|
const metricsSidecarScript = args.lane === "v02"
|
|
|
|
|
const metricsSidecarScript = isRuntimeLane(args.lane)
|
|
|
|
|
? await readFile(path.join(repoRoot, "internal/dev-entrypoint/metrics-sidecar.mjs"), "utf8")
|
|
|
|
|
: null;
|
|
|
|
|
const metricsSidecarSha256 = metricsSidecarScript ? createHash("sha256").update(metricsSidecarScript).digest("hex") : null;
|
|
|
|
@@ -4840,7 +4886,7 @@ async function plannedFiles(args) {
|
|
|
|
|
}));
|
|
|
|
|
|
|
|
|
|
const sourceDescriptor = {
|
|
|
|
|
kind: args.lane === "v02" ? "hwlab-v02-gitops-source" : "hwlab-g14-gitops-source",
|
|
|
|
|
kind: isRuntimeLane(args.lane) ? `hwlab-${args.lane}-gitops-source` : "hwlab-g14-gitops-source",
|
|
|
|
|
sourceCommit: source.full,
|
|
|
|
|
sourceShortCommit: source.short,
|
|
|
|
|
sourceImageTag: source.imageTag,
|
|
|
|
@@ -4853,7 +4899,7 @@ async function plannedFiles(args) {
|
|
|
|
|
registryPrefix: args.registryPrefix,
|
|
|
|
|
runtimePaths: profiles.map((profile) => `deploy/gitops/g14/${runtimePathForProfile(profile)}`),
|
|
|
|
|
publicEndpoints,
|
|
|
|
|
frpDeployments: Object.fromEntries(profiles.map((profile) => [profile, `${namespaceNameForProfile(profile)}/${profile === "v02" ? "hwlab-v02-frpc" : profile === "prod" ? "hwlab-g14-prod-frpc" : "hwlab-g14-frpc"}`])),
|
|
|
|
|
frpDeployments: Object.fromEntries(profiles.map((profile) => [profile, `${namespaceNameForProfile(profile)}/${isRuntimeLane(profile) ? `hwlab-${profile}-frpc` : profile === "prod" ? "hwlab-g14-prod-frpc" : "hwlab-g14-frpc"}`])),
|
|
|
|
|
tektonPipeline: `hwlab-ci/${settings.pipelineName}`,
|
|
|
|
|
argoApplications: profiles.map((profile) => `argocd/${argoApplicationName(profile)}`)
|
|
|
|
|
};
|
|
|
|
@@ -4867,10 +4913,10 @@ async function plannedFiles(args) {
|
|
|
|
|
putJson("source.json", sourceDescriptor);
|
|
|
|
|
putText("README.md", readme({ source, args }));
|
|
|
|
|
putJson("registry/registry.yaml", registryManifest());
|
|
|
|
|
if (args.lane === "v02") putJson("devops-infra/git-mirror.yaml", devopsInfraGitMirrorManifest());
|
|
|
|
|
if (isRuntimeLane(args.lane)) putJson("devops-infra/git-mirror.yaml", devopsInfraGitMirrorManifest(args));
|
|
|
|
|
putJson(`${settings.tektonDir}/rbac.yaml`, tektonRbac(args));
|
|
|
|
|
putJson(`${settings.tektonDir}/pipeline.yaml`, tektonPipeline(args, deploy));
|
|
|
|
|
if (args.lane !== "v02") {
|
|
|
|
|
if (!isRuntimeLane(args.lane)) {
|
|
|
|
|
putJson(`${settings.tektonDir}/poller.yaml`, tektonPollerCronJob(args, deploy));
|
|
|
|
|
putJson(`${settings.tektonDir}/control-plane-reconciler.yaml`, tektonControlPlaneReconcilerCronJob(args));
|
|
|
|
|
}
|
|
|
|
@@ -4890,14 +4936,14 @@ async function plannedFiles(args) {
|
|
|
|
|
const endpoints = profileEndpoint(args, profile);
|
|
|
|
|
putJson(`${runtimePath}/kustomization.yaml`, runtimeKustomization({ profile, includeDeviceAgent }));
|
|
|
|
|
putJson(`${runtimePath}/namespace.yaml`, runtimeNamespace);
|
|
|
|
|
if (profile !== "v02") putJson(`${runtimePath}/code-agent-codex-config.yaml`, transformListNamespace(config, namespace, profileLabels, annotations));
|
|
|
|
|
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));
|
|
|
|
|
putJson(`${runtimePath}/workloads.yaml`, transformWorkloads({ workloads, deploy, catalog: artifactCatalog, source, sourceBranch: args.sourceBranch, 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, deploy, registryPrefix: args.registryPrefix, catalog: artifactCatalog, useDeployImages: args.useDeployImages, metricsSidecarSha256 }));
|
|
|
|
|
if (profile === "v02") putJson(`${runtimePath}/postgres.yaml`, v02PostgresManifest({ migrationSql, source }));
|
|
|
|
|
if (profile === "v02") putJson(`${runtimePath}/openfga.yaml`, v02OpenFgaManifest({ source }));
|
|
|
|
|
if (profile === "v02") putJson(`${runtimePath}/observability.yaml`, observabilityManifest({ deploy, namespace, labels: profileLabels, annotations, metricsSidecarScript }));
|
|
|
|
|
if (isRuntimeLane(profile)) putJson(`${runtimePath}/postgres.yaml`, v02PostgresManifest({ profile, migrationSql, source }));
|
|
|
|
|
if (isRuntimeLane(profile)) putJson(`${runtimePath}/openfga.yaml`, v02OpenFgaManifest({ profile, source }));
|
|
|
|
|
if (isRuntimeLane(profile)) putJson(`${runtimePath}/observability.yaml`, observabilityManifest({ deploy, profile, namespace, labels: profileLabels, annotations, metricsSidecarScript }));
|
|
|
|
|
if (includeDeviceAgent) putJson(`${runtimePath}/device-agent-71-freq.yaml`, deviceAgent71FreqManifest({ profile, source, registryPrefix: args.registryPrefix, catalog: artifactCatalog, useDeployImages: args.useDeployImages }));
|
|
|
|
|
putJson(`${runtimePath}/g14-frpc.yaml`, g14FrpcManifest({ profile, source }));
|
|
|
|
|
}
|
|
|
|
@@ -4913,8 +4959,8 @@ async function writeFiles(files) {
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
async function cleanStaleGeneratedOutput(args) {
|
|
|
|
|
if (args.lane !== "v02") return;
|
|
|
|
|
for (const relativePath of ["runtime-v02", "tekton-v02"]) {
|
|
|
|
|
if (!isRuntimeLane(args.lane)) return;
|
|
|
|
|
for (const relativePath of [`runtime-${args.lane}`, `tekton-${args.lane}`]) {
|
|
|
|
|
await rm(generatedPath(path.join(args.outDir, relativePath)), { recursive: true, force: true });
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|