Files
pikasTech-HWLAB/scripts/src/gitops-render/infra-manifests.mjs
T

255 lines
13 KiB
JavaScript

import assert from "node:assert/strict";
import {
ciToolsRunnerImage,
configObject,
defaultOutDir,
label,
optionalConfigString,
requiredConfigPositiveInteger,
requiredConfigString,
runtimeLaneMirrorSpecs
} from "./core.mjs";
import { renderTemplate, shellSingleQuote } from "./tekton-scripts.mjs";
function registryManifest() {
return {
apiVersion: "v1",
kind: "List",
items: [
{ apiVersion: "v1", kind: "Namespace", metadata: { name: "hwlab-ci" } },
{
apiVersion: "apps/v1",
kind: "Deployment",
metadata: { name: "hwlab-registry", namespace: "hwlab-ci", labels: { "app.kubernetes.io/name": "hwlab-registry" } },
spec: {
replicas: 1,
selector: { matchLabels: { "app.kubernetes.io/name": "hwlab-registry" } },
template: {
metadata: { labels: { "app.kubernetes.io/name": "hwlab-registry" } },
spec: {
hostNetwork: true,
dnsPolicy: "ClusterFirstWithHostNet",
containers: [{
name: "registry",
image: "registry:2.8.3",
imagePullPolicy: "IfNotPresent",
ports: [{ name: "registry", containerPort: 5000, hostPort: 5000 }],
env: [{ name: "REGISTRY_STORAGE_DELETE_ENABLED", value: "true" }],
volumeMounts: [{ name: "storage", mountPath: "/var/lib/registry" }],
readinessProbe: { httpGet: { path: "/v2/", port: "registry" } },
livenessProbe: { httpGet: { path: "/v2/", port: "registry" } }
}],
volumes: [{ name: "storage", hostPath: { path: "/var/lib/hwlab/registry", type: "DirectoryOrCreate" } }]
}
}
}
},
{
apiVersion: "v1",
kind: "Service",
metadata: { name: "hwlab-registry", namespace: "hwlab-ci", labels: { "app.kubernetes.io/name": "hwlab-registry" } },
spec: { selector: { "app.kubernetes.io/name": "hwlab-registry" }, ports: [{ name: "registry", port: 5000, targetPort: "registry" }] }
}
]
};
}
function uniqueStrings(values) {
return Array.from(new Set(values.filter((value) => typeof value === "string" && value.length > 0)));
}
function renderGitMirrorSshPrelude({ direct, proxySummary, proxyCommand, proxyUrl, proxyNoProxy }) {
if (direct) {
return renderTemplate("git-mirror-ssh-direct.sh", {
"__HWLAB_PROXY_SUMMARY_SHELL__": shellSingleQuote(proxySummary)
});
}
return renderTemplate("git-mirror-ssh-proxied.sh", {
"// __HWLAB_GIT_MIRROR_PROXY_CONNECT_MJS__": renderTemplate("git-mirror-proxy-connect.mjs"),
"__HWLAB_PROXY_SUMMARY_SHELL__": shellSingleQuote(proxySummary),
"__HWLAB_PROXY_COMMAND_SHELL__": shellSingleQuote(proxyCommand),
"__HWLAB_PROXY_URL_SHELL__": shellSingleQuote(proxyUrl),
"__HWLAB_PROXY_NO_PROXY_SHELL__": shellSingleQuote(proxyNoProxy)
});
}
function gitMirrorConfigScripts({
fetchConfigCommands,
gitMirrorSshPrelude,
gitopsBranchArgs,
gitopsBranchesJson,
mirrorBranchesJson,
requiredRefArgs,
sourceBranchArgs,
sourceBranchesJson,
sourceSnapshotRefPrefix
}) {
const gitopsBranchesJsonShell = shellSingleQuote(gitopsBranchesJson);
return {
"sync.sh": renderTemplate("git-mirror-sync.sh", {
"# __HWLAB_GIT_MIRROR_SSH_PRELUDE__": gitMirrorSshPrelude,
"__HWLAB_SOURCE_SNAPSHOT_REF_PREFIX_SHELL__": shellSingleQuote(sourceSnapshotRefPrefix),
"# __HWLAB_FETCH_CONFIG_COMMANDS__": fetchConfigCommands,
"__HWLAB_REQUIRED_REF_ARGS__": requiredRefArgs,
"__HWLAB_SOURCE_BRANCH_ARGS__": sourceBranchArgs,
"__HWLAB_GITOPS_BRANCH_ARGS__": gitopsBranchArgs,
"__HWLAB_MIRROR_BRANCHES_JSON_SHELL__": shellSingleQuote(mirrorBranchesJson),
"__HWLAB_SOURCE_BRANCHES_JSON_SHELL__": shellSingleQuote(sourceBranchesJson),
"__HWLAB_GITOPS_BRANCHES_JSON_SHELL__": gitopsBranchesJsonShell
}),
"install-hooks.sh": renderTemplate("git-mirror-install-hooks.sh", {
"__HWLAB_GITOPS_BRANCHES_JSON_SHELL__": gitopsBranchesJsonShell
}),
"flush.sh": renderTemplate("git-mirror-flush.sh", {
"# __HWLAB_GIT_MIRROR_SSH_PRELUDE__": gitMirrorSshPrelude,
"__HWLAB_GITOPS_BRANCH_ARGS__": gitopsBranchArgs,
"__HWLAB_GITOPS_BRANCHES_JSON_SHELL__": gitopsBranchesJsonShell
}),
"http.mjs": renderTemplate("git-mirror-http.mjs")
};
}
function devopsInfraGitMirrorManifest(args, deploy) {
const mirrorSpecs = runtimeLaneMirrorSpecs(deploy, { defaultNodeId: args.nodeId, defaultGitopsRoot: defaultOutDir });
assert.ok(mirrorSpecs.length > 0, "runtime lane git mirror requires at least one deploy.lanes entry");
const sourceBranches = uniqueStrings(mirrorSpecs.map((spec) => spec.sourceBranch));
const gitopsBranches = uniqueStrings(mirrorSpecs.map((spec) => spec.gitopsBranch));
const mirrorBranches = uniqueStrings([...sourceBranches, ...gitopsBranches]);
const fetchConfigCommands = mirrorBranches
.map((branch) => `git -C "$repo_path" config --add remote.origin.fetch '+refs/heads/${branch}:refs/mirror-stage/heads/${branch}'`)
.join("\n");
const requiredRefArgs = mirrorBranches.map((branch) => shellSingleQuote(`refs/mirror-stage/heads/${branch}`)).join(" ");
const sourceBranchArgs = sourceBranches.map((branch) => shellSingleQuote(branch)).join(" ");
const gitopsBranchArgs = gitopsBranches.map((branch) => shellSingleQuote(branch)).join(" ");
const mirrorBranchesJson = JSON.stringify(mirrorBranches);
const sourceBranchesJson = JSON.stringify(sourceBranches);
const gitopsBranchesJson = JSON.stringify(gitopsBranches);
const sourceSnapshotRefPrefix = "refs/unidesk/snapshots/hwlab-node-runtime";
const laneConfig = configObject(configObject(deploy.lanes, "deploy.lanes")[args.lane], `deploy.lanes.${args.lane}`);
const gitMirrorConfig = configObject(laneConfig.gitMirror, `deploy.lanes.${args.lane}.gitMirror`);
const gitMirrorProxy = configObject(gitMirrorConfig.egressProxy, `deploy.lanes.${args.lane}.gitMirror.egressProxy`);
const gitMirrorProxyMode = requiredConfigString(gitMirrorProxy, "mode", `deploy.lanes.${args.lane}.gitMirror.egressProxy`);
assert.ok(gitMirrorProxyMode === "node-global" || gitMirrorProxyMode === "host-proxy-client" || gitMirrorProxyMode === "direct", `deploy.lanes.${args.lane}.gitMirror.egressProxy.mode must be node-global, host-proxy-client, or direct`);
const useDirectGitMirrorProxy = gitMirrorProxyMode === "direct";
const useHostGitMirrorProxy = gitMirrorProxyMode === "host-proxy-client";
const gitMirrorNamespace = requiredConfigString(gitMirrorConfig, "namespace", `deploy.lanes.${args.lane}.gitMirror`);
const gitMirrorReadName = requiredConfigString(gitMirrorConfig, "serviceReadName", `deploy.lanes.${args.lane}.gitMirror`);
const gitMirrorWriteName = requiredConfigString(gitMirrorConfig, "serviceWriteName", `deploy.lanes.${args.lane}.gitMirror`);
const gitMirrorCachePvcName = requiredConfigString(gitMirrorConfig, "cachePvcName", `deploy.lanes.${args.lane}.gitMirror`);
const gitMirrorCachePvcStorage = requiredConfigString(gitMirrorConfig, "cachePvcStorage", `deploy.lanes.${args.lane}.gitMirror`);
const gitMirrorCacheHostPath = optionalConfigString(gitMirrorConfig, "cacheHostPath", `deploy.lanes.${args.lane}.gitMirror`);
const gitMirrorServicePort = requiredConfigPositiveInteger(gitMirrorConfig, "servicePort", `deploy.lanes.${args.lane}.gitMirror`);
const gitMirrorConfigMapName = requiredConfigString(gitMirrorConfig, "syncConfigMapName", `deploy.lanes.${args.lane}.gitMirror`);
const proxyNamespace = useDirectGitMirrorProxy || useHostGitMirrorProxy ? "" : requiredConfigString(gitMirrorProxy, "namespace", `deploy.lanes.${args.lane}.gitMirror.egressProxy`);
const proxyServiceName = useDirectGitMirrorProxy || useHostGitMirrorProxy ? "" : requiredConfigString(gitMirrorProxy, "serviceName", `deploy.lanes.${args.lane}.gitMirror.egressProxy`);
const proxyPort = useDirectGitMirrorProxy ? 0 : requiredConfigPositiveInteger(gitMirrorProxy, "port", `deploy.lanes.${args.lane}.gitMirror.egressProxy`);
const proxyClientName = useDirectGitMirrorProxy ? "" : requiredConfigString(gitMirrorProxy, "clientName", `deploy.lanes.${args.lane}.gitMirror.egressProxy`);
const proxyNoProxy = !useDirectGitMirrorProxy && Array.isArray(gitMirrorProxy.noProxy) ? gitMirrorProxy.noProxy.filter((value) => typeof value === "string" && value.length > 0).join(",") : "";
const proxyHost = useHostGitMirrorProxy
? requiredConfigString(gitMirrorProxy, "host", `deploy.lanes.${args.lane}.gitMirror.egressProxy`)
: `${proxyServiceName}.${proxyNamespace}.svc.cluster.local`;
const proxyUrl = `http://${proxyHost}:${proxyPort}`;
const proxySummary = useDirectGitMirrorProxy
? "git-mirror-egress-proxy mode=direct required=false transport=ssh source=yaml"
: `git-mirror-egress-proxy client=${proxyClientName} mode=${gitMirrorProxyMode} required=true host=${proxyHost} port=${proxyPort} ssh=GIT_SSH-wrapper source=yaml`;
const proxyCommand = useDirectGitMirrorProxy ? "" : `ProxyCommand=node /tmp/hwlab-github-proxy-connect.mjs ${proxyHost} ${proxyPort} %h %p`;
const gitMirrorSshPrelude = renderGitMirrorSshPrelude({
direct: useDirectGitMirrorProxy,
proxySummary,
proxyCommand,
proxyUrl,
proxyNoProxy
});
const cacheVolume = gitMirrorCacheHostPath === null
? { name: "cache", persistentVolumeClaim: { claimName: gitMirrorCachePvcName } }
: { name: "cache", hostPath: { path: gitMirrorCacheHostPath, type: "DirectoryOrCreate" } };
const labels = {
"app.kubernetes.io/part-of": "hwlab-node-control-plane",
"hwlab.pikastech.local/node": args.nodeId,
"hwlab.pikastech.local/lane": args.lane
};
const configLabels = { ...labels, "app.kubernetes.io/name": "git-mirror" };
const readLabels = { ...labels, "app.kubernetes.io/name": gitMirrorReadName, "hwlab.pikastech.local/git-mirror-mode": "read" };
const writeLabels = { ...labels, "app.kubernetes.io/name": gitMirrorWriteName, "hwlab.pikastech.local/git-mirror-mode": "write" };
return {
apiVersion: "v1",
kind: "List",
items: [
{ apiVersion: "v1", kind: "Namespace", metadata: { name: gitMirrorNamespace } },
...(gitMirrorCacheHostPath === null ? [{
apiVersion: "v1",
kind: "PersistentVolumeClaim",
metadata: { name: gitMirrorCachePvcName, namespace: gitMirrorNamespace, labels: configLabels },
spec: { accessModes: ["ReadWriteOnce"], resources: { requests: { storage: gitMirrorCachePvcStorage } } }
}] : []),
{
apiVersion: "v1",
kind: "ConfigMap",
metadata: { name: gitMirrorConfigMapName, namespace: gitMirrorNamespace, labels: configLabels },
data: gitMirrorConfigScripts({
fetchConfigCommands,
gitMirrorSshPrelude,
gitopsBranchArgs,
gitopsBranchesJson,
mirrorBranchesJson,
requiredRefArgs,
sourceBranchArgs,
sourceBranchesJson,
sourceSnapshotRefPrefix
})
},
{
apiVersion: "apps/v1",
kind: "Deployment",
metadata: { name: gitMirrorReadName, namespace: gitMirrorNamespace, labels: readLabels },
spec: {
replicas: 1,
selector: { matchLabels: { "app.kubernetes.io/name": gitMirrorReadName } },
template: {
metadata: { labels: readLabels },
spec: {
containers: [{
name: "git-mirror",
image: ciToolsRunnerImage,
imagePullPolicy: "IfNotPresent",
command: ["node"],
args: ["/etc/git-mirror/http.mjs"],
ports: [{ name: "http", containerPort: 8080 }],
readinessProbe: { httpGet: { path: "/pikasTech/HWLAB.git/HEAD", port: "http" }, initialDelaySeconds: 5, periodSeconds: 10 },
livenessProbe: { httpGet: { path: "/", port: "http" }, initialDelaySeconds: 10, periodSeconds: 30 },
volumeMounts: [
{ name: "cache", mountPath: "/cache" },
{ name: "config", mountPath: "/etc/git-mirror", readOnly: true }
]
}],
volumes: [
cacheVolume,
{ name: "config", configMap: { name: gitMirrorConfigMapName, defaultMode: 0o555 } }
]
}
}
}
},
{
apiVersion: "v1",
kind: "Service",
metadata: { name: gitMirrorReadName, namespace: gitMirrorNamespace, labels: readLabels },
spec: { selector: { "app.kubernetes.io/name": gitMirrorReadName }, ports: [{ name: "http", port: gitMirrorServicePort, targetPort: "http" }] }
},
{
apiVersion: "v1",
kind: "Service",
metadata: { name: gitMirrorWriteName, namespace: gitMirrorNamespace, labels: writeLabels },
spec: { selector: { "app.kubernetes.io/name": gitMirrorReadName }, ports: [{ name: "http", port: gitMirrorServicePort, targetPort: "http" }] }
}
]
};
}
export {
devopsInfraGitMirrorManifest,
registryManifest,
uniqueStrings
};