fix: support D601 v03 git mirror sync (#327)

Co-authored-by: Codex <codex@noreply.local>
This commit is contained in:
Lyon
2026-06-13 15:22:44 +08:00
committed by GitHub
parent 7ba28c8f40
commit 4e7c10d132
4 changed files with 643 additions and 27 deletions
+314 -14
View File
@@ -80,6 +80,7 @@ interface ControlPlaneTargetSpec {
serviceWriteName: string;
cachePvcName: string;
cachePvcStorage: string;
cacheHostPath: string | null;
servicePort: number;
deploymentReplicas: number;
secretName: string;
@@ -245,7 +246,7 @@ function infraStatus(_config: ControlPlaneConfig, node: ControlPlaneNodeSpec, ta
&& boolField(gitMirror, "namespaceExists")
&& boolField(gitMirror, "readServiceExists")
&& boolField(gitMirror, "writeServiceExists")
&& boolField(gitMirror, "cachePvcExists")
&& (boolField(gitMirror, "cachePvcExists") || boolField(gitMirror, "cacheHostPathReady"))
&& boolField(registry, "ready")
&& boolField(registry, "toolsImageReady")
&& boolField(argo, "installed")
@@ -272,6 +273,7 @@ function infraStatus(_config: ControlPlaneConfig, node: ControlPlaneNodeSpec, ta
gitMirrorReadServiceExists: boolField(gitMirror, "readServiceExists"),
gitMirrorWriteServiceExists: boolField(gitMirror, "writeServiceExists"),
gitMirrorCachePvcExists: boolField(gitMirror, "cachePvcExists"),
gitMirrorCacheHostPathReady: boolField(gitMirror, "cacheHostPathReady"),
gitMirrorReadReady: boolField(gitMirror, "readDeploymentReady"),
gitMirrorWriteReady: boolField(gitMirror, "writeDeploymentReady"),
argoInstalled: boolField(argo, "installed"),
@@ -781,6 +783,7 @@ function targetSpec(raw: Record<string, unknown>, index: number): ControlPlaneTa
serviceWriteName,
cachePvcName: stringField(gitMirror, "cachePvcName", `${path}.gitMirror`),
cachePvcStorage: stringField(gitMirror, "cachePvcStorage", `${path}.gitMirror`),
cacheHostPath: optionalStringField(gitMirror, "cacheHostPath", `${path}.gitMirror`) ?? null,
servicePort: numberField(gitMirror, "servicePort", `${path}.gitMirror`),
deploymentReplicas: nonNegativeIntegerField(gitMirror, "deploymentReplicas", `${path}.gitMirror`),
secretName: stringField(gitMirror, "secretName", `${path}.gitMirror`),
@@ -820,22 +823,28 @@ function renderInfraManifest(_node: ControlPlaneNodeSpec, target: ControlPlaneTa
}
manifests.push(
{ apiVersion: "v1", kind: "ServiceAccount", metadata: { name: target.tekton.serviceAccountName, namespace: target.ciNamespace, labels } },
{
apiVersion: "v1",
kind: "PersistentVolumeClaim",
metadata: { name: target.gitMirror.cachePvcName, namespace: target.gitMirror.namespace, labels: { ...labels, "app.kubernetes.io/name": "git-mirror" } },
spec: { accessModes: ["ReadWriteOnce"], resources: { requests: { storage: target.gitMirror.cachePvcStorage } } },
},
{
apiVersion: "v1",
kind: "ConfigMap",
metadata: { name: target.gitMirror.syncConfigMapName, namespace: target.gitMirror.namespace, labels: { ...labels, "app.kubernetes.io/name": "git-mirror" } },
data: {
"repositories.json": JSON.stringify([{ repository: target.source.repository, sourceBranch: target.source.branch, gitopsBranch: target.gitops.branch }], null, 2),
"sync.sh": "#!/bin/sh\nset -eu\necho d601-hwlab-git-mirror-sync-placeholder\ncat /etc/git-mirror/repositories.json\n",
"flush.sh": "#!/bin/sh\nset -eu\necho d601-hwlab-git-mirror-flush-placeholder\ncat /etc/git-mirror/repositories.json\n",
"repositories.json": JSON.stringify([{ key: target.id, repository: target.source.repository, sourceBranch: target.source.branch, gitopsBranch: target.gitops.branch }], null, 2),
"server.js": gitMirrorServerJs(),
"status.sh": gitMirrorStatusShell(),
"sync.sh": gitMirrorSyncShell(_node, target),
"flush.sh": gitMirrorFlushShell(_node, target),
},
},
);
if (target.gitMirror.cacheHostPath === null) {
manifests.push({
apiVersion: "v1",
kind: "PersistentVolumeClaim",
metadata: { name: target.gitMirror.cachePvcName, namespace: target.gitMirror.namespace, labels: { ...labels, "app.kubernetes.io/name": "git-mirror" } },
spec: { accessModes: ["ReadWriteOnce"], resources: { requests: { storage: target.gitMirror.cachePvcStorage } } },
});
}
manifests.push(
service(target.gitMirror.serviceReadName, target.gitMirror.namespace, labels, target.gitMirror.servicePort),
service(target.gitMirror.serviceWriteName, target.gitMirror.namespace, labels, target.gitMirror.servicePort),
gitMirrorDeployment(target.gitMirror.serviceReadName, target.gitMirror.namespace, labels, target, "read"),
@@ -888,14 +897,301 @@ function gitMirrorDeployment(name: string, namespace: string, labels: Record<str
template: {
metadata: { labels: { ...labels, "app.kubernetes.io/name": name, "hwlab.pikastech.local/git-mirror-mode": mode } },
spec: {
containers: [{ name: "git-mirror", image: target.tekton.toolsImage.output, command: ["/bin/sh", "-c", "sleep infinity"], ports: [{ name: "http", containerPort: target.gitMirror.servicePort }], volumeMounts: [{ name: "cache", mountPath: "/cache" }, { name: "config", mountPath: "/etc/git-mirror" }] }],
volumes: [{ name: "cache", persistentVolumeClaim: { claimName: target.gitMirror.cachePvcName } }, { name: "config", configMap: { name: target.gitMirror.syncConfigMapName } }],
containers: [{
name: "git-mirror",
image: target.tekton.toolsImage.output,
command: ["node", "/etc/git-mirror/server.js"],
env: [
{ name: "PORT", value: String(target.gitMirror.servicePort) },
{ name: "GIT_PROJECT_ROOT", value: "/cache" },
{ name: "GIT_MIRROR_MODE", value: mode },
],
ports: [{ name: "http", containerPort: target.gitMirror.servicePort }],
volumeMounts: [{ name: "cache", mountPath: "/cache" }, { name: "config", mountPath: "/etc/git-mirror" }],
}],
volumes: [
{ name: "cache", ...(target.gitMirror.cacheHostPath === null ? { persistentVolumeClaim: { claimName: target.gitMirror.cachePvcName } } : { hostPath: { path: target.gitMirror.cacheHostPath, type: "DirectoryOrCreate" } }) },
{ name: "config", configMap: { name: target.gitMirror.syncConfigMapName, defaultMode: 0o755 } },
],
},
},
},
};
}
function gitMirrorServerJs(): string {
return String.raw`const http = require('node:http');
const { spawn } = require('node:child_process');
const projectRoot = process.env.GIT_PROJECT_ROOT || '/cache';
const port = Number.parseInt(process.env.PORT || '8080', 10);
function sendHealth(res) {
res.writeHead(200, { 'content-type': 'application/json' });
res.end(JSON.stringify({ ok: true, mode: process.env.GIT_MIRROR_MODE || null, projectRoot }));
}
function parseHeaders(buffer) {
const crlf = buffer.indexOf('\r\n\r\n');
const lf = buffer.indexOf('\n\n');
const headerEnd = crlf >= 0 ? crlf + 4 : (lf >= 0 ? lf + 2 : -1);
if (headerEnd < 0) return null;
const headerText = buffer.slice(0, headerEnd).toString('latin1').trim();
const rest = buffer.slice(headerEnd);
const headers = {};
let status = 200;
for (const line of headerText.split(/\r?\n/u)) {
const index = line.indexOf(':');
if (index < 0) continue;
const key = line.slice(0, index).trim();
const value = line.slice(index + 1).trim();
if (key.toLowerCase() === 'status') {
const parsed = Number.parseInt(value.split(' ')[0] || '', 10);
if (Number.isInteger(parsed)) status = parsed;
} else {
headers[key] = value;
}
}
return { status, headers, rest };
}
function handleGit(req, res) {
const url = new URL(req.url || '/', 'http://git-mirror.local');
const env = {
...process.env,
GIT_PROJECT_ROOT: projectRoot,
GIT_HTTP_EXPORT_ALL: '1',
PATH_INFO: decodeURIComponent(url.pathname),
REQUEST_METHOD: req.method || 'GET',
QUERY_STRING: url.search.slice(1),
CONTENT_TYPE: req.headers['content-type'] || '',
CONTENT_LENGTH: req.headers['content-length'] || '',
REMOTE_USER: 'git',
};
const child = spawn('git', ['http-backend'], { env });
let pending = Buffer.alloc(0);
let headersSent = false;
child.stderr.on('data', (chunk) => process.stderr.write(chunk));
child.on('error', (error) => {
if (!headersSent) {
res.writeHead(500, { 'content-type': 'text/plain' });
headersSent = true;
}
res.end(String(error && error.message || error));
});
child.stdout.on('data', (chunk) => {
if (headersSent) {
res.write(chunk);
return;
}
pending = Buffer.concat([pending, chunk]);
const parsed = parseHeaders(pending);
if (!parsed) return;
headersSent = true;
res.writeHead(parsed.status, parsed.headers);
if (parsed.rest.length) res.write(parsed.rest);
});
child.on('close', (code) => {
if (!headersSent) {
res.writeHead(code === 0 ? 200 : 500, { 'content-type': 'text/plain' });
headersSent = true;
if (pending.length) res.write(pending);
}
res.end();
});
req.pipe(child.stdin);
}
http.createServer((req, res) => {
if ((req.url || '').startsWith('/healthz')) return sendHealth(res);
return handleGit(req, res);
}).listen(port, '0.0.0.0', () => {
console.log(JSON.stringify({ event: 'git-mirror-http-started', port, projectRoot, mode: process.env.GIT_MIRROR_MODE || null }));
});
`;
}
function gitMirrorStatusShell(): string {
return String.raw`#!/bin/sh
set -eu
node <<'NODE'
const { execFileSync } = require('node:child_process');
const { readFileSync, existsSync } = require('node:fs');
const repositories = JSON.parse(readFileSync('/etc/git-mirror/repositories.json', 'utf8'));
function readJson(path) {
try {
return existsSync(path) ? JSON.parse(readFileSync(path, 'utf8')) : null;
} catch {
return null;
}
}
function rev(repo, ref) {
try {
return execFileSync('git', ['--git-dir=' + repo, 'rev-parse', '--verify', ref + '^{commit}'], { encoding: 'utf8' }).trim();
} catch {
return null;
}
}
const items = {};
for (const spec of repositories) {
const repoPath = '/cache/' + spec.repository + '.git';
const localSource = rev(repoPath, 'refs/heads/' + spec.sourceBranch);
const githubSource = rev(repoPath, 'refs/mirror-stage/heads/' + spec.sourceBranch);
const localGitops = rev(repoPath, 'refs/heads/' + spec.gitopsBranch);
const githubGitops = rev(repoPath, 'refs/mirror-stage/heads/' + spec.gitopsBranch);
items[spec.key] = {
repository: spec.repository,
sourceBranch: spec.sourceBranch,
localSource,
githubSource,
gitopsBranch: spec.gitopsBranch,
localGitops,
githubGitops,
sourceInSync: Boolean(localSource && githubSource && localSource === githubSource),
gitopsInSync: Boolean(localGitops && githubGitops && localGitops === githubGitops),
pendingFlush: Boolean(localGitops && (!githubGitops || localGitops !== githubGitops)),
};
}
const first = items[repositories[0]?.key] || {};
const pendingFlush = Object.values(items).some((item) => Boolean(item.pendingFlush));
console.log(JSON.stringify({
localSource: first.localSource || null,
githubSource: first.githubSource || null,
localGitops: first.localGitops || null,
githubGitops: first.githubGitops || null,
pendingFlush,
flushNeeded: pendingFlush,
githubInSync: Object.values(items).every((item) => item.sourceInSync === true && item.gitopsInSync === true),
repositories: items,
lastSync: readJson('/cache/HWLAB.last-sync.json'),
lastFlush: readJson('/cache/HWLAB.last-flush.json'),
}));
NODE
`;
}
function gitMirrorProxyPrelude(node: ControlPlaneNodeSpec, target: ControlPlaneTargetSpec): string {
const proxyHost = node.egressProxy?.serviceName === undefined ? "127.0.0.1" : `${node.egressProxy.serviceName}.${node.egressProxy.namespace}.svc.cluster.local`;
const proxyPort = node.egressProxy?.port ?? 10808;
return [
"mkdir -p /root/.ssh",
"cp /git-ssh/ssh-privatekey /root/.ssh/id_rsa",
"chmod 0400 /root/.ssh/id_rsa",
"cat > /tmp/hwlab-github-proxy-connect.cjs <<'NODE_PROXY'",
"#!/usr/bin/env node",
"const net = require('node:net');",
"const [proxyHost, proxyPortRaw, targetHost, targetPortRaw] = process.argv.slice(2);",
"const proxyPort = Number.parseInt(proxyPortRaw || '', 10);",
"const targetPort = Number.parseInt(targetPortRaw || '', 10);",
"if (!proxyHost || !Number.isInteger(proxyPort) || !targetHost || !Number.isInteger(targetPort)) process.exit(64);",
"const socket = net.createConnection({ host: proxyHost, port: proxyPort });",
"let buffer = Buffer.alloc(0);",
"socket.setTimeout(10000, () => { socket.destroy(); process.exit(65); });",
"socket.on('connect', () => socket.write('CONNECT ' + targetHost + ':' + targetPort + ' HTTP/1.1\\r\\nHost: ' + targetHost + ':' + targetPort + '\\r\\nProxy-Connection: Keep-Alive\\r\\n\\r\\n'));",
"socket.on('error', () => process.exit(66));",
"function onData(chunk) {",
" buffer = Buffer.concat([buffer, chunk]);",
" const headerEnd = buffer.indexOf('\\r\\n\\r\\n');",
" if (headerEnd === -1 && buffer.length < 8192) return;",
" const head = buffer.slice(0, headerEnd + 4).toString('latin1');",
" const statusLine = head.split('\\r\\n', 1)[0] || '';",
" const statusCode = Number.parseInt(statusLine.split(' ')[1] || '', 10);",
" if (!statusLine.startsWith('HTTP/1.') || !Number.isInteger(statusCode) || statusCode < 200 || statusCode > 299) { socket.destroy(); process.exit(67); }",
" socket.off('data', onData);",
" socket.setTimeout(0);",
" const rest = buffer.slice(headerEnd + 4);",
" if (rest.length) process.stdout.write(rest);",
" process.stdin.pipe(socket);",
" socket.pipe(process.stdout);",
"}",
"socket.on('data', onData);",
"socket.on('close', () => process.exit(0));",
"NODE_PROXY",
"chmod 0700 /tmp/hwlab-github-proxy-connect.cjs",
`export GIT_SSH_COMMAND=${shQuote(`ssh -i /root/.ssh/id_rsa -o IdentitiesOnly=yes -o BatchMode=yes -o StrictHostKeyChecking=accept-new -o UserKnownHostsFile=/root/.ssh/known_hosts -o ConnectTimeout=15 -o ServerAliveInterval=5 -o ServerAliveCountMax=1 -o ProxyCommand='node /tmp/hwlab-github-proxy-connect.cjs ${proxyHost} ${proxyPort} %h %p'`)}`,
`repository=${shQuote(target.source.repository)}`,
`source_branch=${shQuote(target.source.branch)}`,
`gitops_branch=${shQuote(target.gitops.branch)}`,
"repo=\"/cache/${repository}.git\"",
"remote=\"ssh://git@ssh.github.com:443/${repository}.git\"",
].join("\n");
}
function gitMirrorSyncShell(node: ControlPlaneNodeSpec, target: ControlPlaneTargetSpec): string {
return [
"#!/bin/sh",
"set -eu",
"started_at=$(date -u +%Y-%m-%dT%H:%M:%SZ)",
gitMirrorProxyPrelude(node, target),
"mkdir -p \"$(dirname \"$repo\")\"",
"if [ -d \"$repo/objects\" ] && [ -f \"$repo/HEAD\" ]; then",
" git --git-dir=\"$repo\" remote set-url origin \"$remote\" || git --git-dir=\"$repo\" remote add origin \"$remote\"",
"else",
" rm -rf \"$repo\"",
" git init --bare \"$repo\"",
" git --git-dir=\"$repo\" remote add origin \"$remote\"",
"fi",
"git --git-dir=\"$repo\" config uploadpack.allowReachableSHA1InWant true",
"git --git-dir=\"$repo\" config uploadpack.allowAnySHA1InWant true",
"git --git-dir=\"$repo\" config http.uploadpack true",
"git --git-dir=\"$repo\" config http.receivepack true",
"timeout 240 git --git-dir=\"$repo\" fetch origin \"+refs/heads/${source_branch}:refs/mirror-stage/heads/${source_branch}\"",
"source_sha=$(git --git-dir=\"$repo\" rev-parse --verify \"refs/mirror-stage/heads/${source_branch}^{commit}\")",
"git --git-dir=\"$repo\" update-ref \"refs/heads/${source_branch}\" \"$source_sha\"",
"if timeout 240 git --git-dir=\"$repo\" fetch origin \"+refs/heads/${gitops_branch}:refs/mirror-stage/heads/${gitops_branch}\"; then",
" github_gitops=$(git --git-dir=\"$repo\" rev-parse --verify \"refs/mirror-stage/heads/${gitops_branch}^{commit}\" 2>/dev/null || true)",
" local_gitops=$(git --git-dir=\"$repo\" rev-parse --verify \"refs/heads/${gitops_branch}^{commit}\" 2>/dev/null || true)",
" if [ -z \"$local_gitops\" ] && [ -n \"$github_gitops\" ]; then",
" git --git-dir=\"$repo\" update-ref \"refs/heads/${gitops_branch}\" \"$github_gitops\"",
" elif [ -n \"$local_gitops\" ] && [ -n \"$github_gitops\" ] && [ \"$local_gitops\" != \"$github_gitops\" ] && git --git-dir=\"$repo\" merge-base --is-ancestor \"$local_gitops\" \"$github_gitops\"; then",
" git --git-dir=\"$repo\" update-ref \"refs/heads/${gitops_branch}\" \"$github_gitops\"",
" fi",
"fi",
"git --git-dir=\"$repo\" update-server-info",
"export repository source_branch gitops_branch started_at",
"node <<'NODE' | tee /cache/HWLAB.last-sync.json",
"const { execFileSync } = require('node:child_process');",
"const repository = process.env.repository;",
"const sourceBranch = process.env.source_branch;",
"const gitopsBranch = process.env.gitops_branch;",
"const repoPath = `/cache/${repository}.git`;",
"function rev(ref) { try { return execFileSync('git', ['--git-dir=' + repoPath, 'rev-parse', '--verify', ref + '^{commit}'], { encoding: 'utf8' }).trim(); } catch { return null; } }",
"const localSource = rev(`refs/heads/${sourceBranch}`);",
"const githubSource = rev(`refs/mirror-stage/heads/${sourceBranch}`);",
"const localGitops = rev(`refs/heads/${gitopsBranch}`);",
"const githubGitops = rev(`refs/mirror-stage/heads/${gitopsBranch}`);",
"const pendingFlush = Boolean(localGitops && (!githubGitops || localGitops !== githubGitops));",
"console.log(JSON.stringify({ event: 'git-mirror-sync', repo: repository, status: 'succeeded', startedAt: process.env.started_at, syncedAt: new Date().toISOString(), localSource, githubSource, gitopsBranch, localGitops, githubGitops, sourceInSync: Boolean(localSource && githubSource && localSource === githubSource), gitopsInSync: Boolean(localGitops && githubGitops && localGitops === githubGitops), pendingFlush }));",
"NODE",
"cat /cache/HWLAB.last-sync.json",
"",
].join("\n");
}
function gitMirrorFlushShell(node: ControlPlaneNodeSpec, target: ControlPlaneTargetSpec): string {
return [
"#!/bin/sh",
"set -eu",
"started_at=$(date -u +%Y-%m-%dT%H:%M:%SZ)",
gitMirrorProxyPrelude(node, target),
"test -d \"$repo/objects\"",
"git --git-dir=\"$repo\" remote set-url origin \"$remote\" || git --git-dir=\"$repo\" remote add origin \"$remote\"",
"local_gitops=$(git --git-dir=\"$repo\" rev-parse --verify \"refs/heads/${gitops_branch}^{commit}\" 2>/dev/null || true)",
"if [ -n \"$local_gitops\" ]; then",
" git --git-dir=\"$repo\" -c remote.origin.mirror=false push origin \"refs/heads/${gitops_branch}:refs/heads/${gitops_branch}\"",
" git --git-dir=\"$repo\" fetch origin \"+refs/heads/${gitops_branch}:refs/mirror-stage/heads/${gitops_branch}\"",
"fi",
"github_gitops=$(git --git-dir=\"$repo\" rev-parse --verify \"refs/mirror-stage/heads/${gitops_branch}^{commit}\" 2>/dev/null || true)",
"pending=false; if [ -n \"$local_gitops\" ] && { [ -z \"$github_gitops\" ] || [ \"$local_gitops\" != \"$github_gitops\" ]; }; then pending=true; fi",
"export repository gitops_branch started_at local_gitops github_gitops pending",
"node <<'NODE' | tee /cache/HWLAB.last-flush.json",
"const payload = { event: 'git-mirror-flush', repo: process.env.repository, status: 'succeeded', startedAt: process.env.started_at, flushedAt: new Date().toISOString(), gitopsBranch: process.env.gitops_branch, localGitops: process.env.local_gitops || null, githubGitops: process.env.github_gitops || null, pendingFlush: process.env.pending === 'true' };",
"console.log(JSON.stringify(payload));",
"NODE",
"cat /cache/HWLAB.last-flush.json",
"",
].join("\n");
}
function argoDesiredManifest(target: ControlPlaneTargetSpec): Record<string, unknown>[] {
return [argoProjectSkeleton(target), argoApplicationSkeleton(target)];
}
@@ -973,6 +1269,7 @@ function expectedSummary(node: ControlPlaneNodeSpec, target: ControlPlaneTargetS
writeUrl: target.gitMirror.writeUrl,
cachePvc: target.gitMirror.cachePvcName,
cachePvcStorage: target.gitMirror.cachePvcStorage,
cacheHostPath: target.gitMirror.cacheHostPath,
servicePort: target.gitMirror.servicePort,
deploymentReplicas: target.gitMirror.deploymentReplicas,
syncConfigMap: target.gitMirror.syncConfigMapName,
@@ -1020,6 +1317,7 @@ write_deploy=${shQuote(target.gitMirror.serviceWriteName)}
read_svc=${shQuote(target.gitMirror.serviceReadName)}
write_svc=${shQuote(target.gitMirror.serviceWriteName)}
cache_pvc=${shQuote(target.gitMirror.cachePvcName)}
cache_host_path=${shQuote(target.gitMirror.cacheHostPath ?? "")}
pipeline=${shQuote(target.tekton.pipelineName)}
service_account=${shQuote(target.tekton.serviceAccountName)}
argo_ns=${shQuote(target.argo.namespace)}
@@ -1042,6 +1340,8 @@ tools_repo=\${tools_repo_tag%:*}
tools_tag=\${tools_repo_tag##*:}
tools_image_ready=false
if [ "$tools_repo" != "$tools_repo_tag" ] && command -v curl >/dev/null 2>&1; then curl -fsS --max-time 5 "http://$registry/v2/$tools_repo/manifests/$tools_tag" >/tmp/hwlab-tools-image.out 2>/tmp/hwlab-tools-image.err && tools_image_ready=true; fi
cache_host_path_ready=false
if [ -n "$cache_host_path" ] && kubectl -n "$gitmirror_ns" exec deploy/"$read_deploy" -- sh -lc 'test -d /cache' >/dev/null 2>&1; then cache_host_path_ready=true; fi
python3 - "$required_crds_json" "$argo_deployments_json" "$argo_statefulsets_json" <<'PY' >/tmp/hwlab-node-status-fragments.json
import json, subprocess, sys
required_crds=json.loads(sys.argv[1])
@@ -1067,7 +1367,7 @@ print(json.dumps({"crds": crds, "deployments": deploy, "statefulSets": sts, "crd
PY
argo_fragment=$(cat /tmp/hwlab-node-status-fragments.json 2>/dev/null || printf '{}')
cat <<JSON
{"observedAt":"$(date -u +%Y-%m-%dT%H:%M:%SZ)","node":"$node","lane":"$lane","components":{"tekton":{"installed":$(kubectl get crd pipelines.tekton.dev pipelineruns.tekton.dev >/dev/null 2>&1 && printf true || printf false),"controllerReady":$(deploy_ready tekton-pipelines tekton-pipelines-controller),"webhookReady":$(deploy_ready tekton-pipelines tekton-pipelines-webhook)},"ciNamespace":{"name":"$ci_ns","exists":$(exists_ns "$ci_ns"),"serviceAccountExists":$(exists_res "$ci_ns" serviceaccount "$service_account"),"pipelineExists":$(exists_res "$ci_ns" pipeline "$pipeline")},"gitMirror":{"namespace":"$gitmirror_ns","namespaceExists":$(exists_ns "$gitmirror_ns"),"readDeploymentReady":$(deploy_ready "$gitmirror_ns" "$read_deploy"),"writeDeploymentReady":$(deploy_ready "$gitmirror_ns" "$write_deploy"),"readServiceExists":$(exists_res "$gitmirror_ns" service "$read_svc"),"writeServiceExists":$(exists_res "$gitmirror_ns" service "$write_svc"),"readEndpointsReady":$(endpoint_ready "$gitmirror_ns" "$read_svc"),"writeEndpointsReady":$(endpoint_ready "$gitmirror_ns" "$write_svc"),"cachePvcExists":$(exists_res "$gitmirror_ns" pvc "$cache_pvc"),"summary":{"localSource":null,"githubSource":null,"localGitops":null,"githubGitops":null,"pendingFlush":null,"flushNeeded":null,"githubInSync":null}},"argo":{"namespace":"$argo_ns","namespaceExists":$(exists_ns "$argo_ns"),"installed":$(kubectl get crd applications.argoproj.io appprojects.argoproj.io >/dev/null 2>&1 && printf true || printf false),"projectExists":$(kubectl -n "$argo_ns" get appproject "$argo_project" >/dev/null 2>&1 && printf true || printf false),"applicationExists":$(kubectl -n "$argo_ns" get application "$argo_app" >/dev/null 2>&1 && printf true || printf false),"install":$argo_fragment},"registry":{"endpoint":"$registry","ready":$registry_ready,"toolsImage":"$tools_image","toolsImageReady":$tools_image_ready},"runtimeNamespace":{"name":"$runtime_ns","exists":$(exists_ns "$runtime_ns")}}}
{"observedAt":"$(date -u +%Y-%m-%dT%H:%M:%SZ)","node":"$node","lane":"$lane","components":{"tekton":{"installed":$(kubectl get crd pipelines.tekton.dev pipelineruns.tekton.dev >/dev/null 2>&1 && printf true || printf false),"controllerReady":$(deploy_ready tekton-pipelines tekton-pipelines-controller),"webhookReady":$(deploy_ready tekton-pipelines tekton-pipelines-webhook)},"ciNamespace":{"name":"$ci_ns","exists":$(exists_ns "$ci_ns"),"serviceAccountExists":$(exists_res "$ci_ns" serviceaccount "$service_account"),"pipelineExists":$(exists_res "$ci_ns" pipeline "$pipeline")},"gitMirror":{"namespace":"$gitmirror_ns","namespaceExists":$(exists_ns "$gitmirror_ns"),"readDeploymentReady":$(deploy_ready "$gitmirror_ns" "$read_deploy"),"writeDeploymentReady":$(deploy_ready "$gitmirror_ns" "$write_deploy"),"readServiceExists":$(exists_res "$gitmirror_ns" service "$read_svc"),"writeServiceExists":$(exists_res "$gitmirror_ns" service "$write_svc"),"readEndpointsReady":$(endpoint_ready "$gitmirror_ns" "$read_svc"),"writeEndpointsReady":$(endpoint_ready "$gitmirror_ns" "$write_svc"),"cachePvcExists":$(exists_res "$gitmirror_ns" pvc "$cache_pvc"),"cacheHostPath":"$cache_host_path","cacheHostPathReady":$cache_host_path_ready,"summary":{"localSource":null,"githubSource":null,"localGitops":null,"githubGitops":null,"pendingFlush":null,"flushNeeded":null,"githubInSync":null}},"argo":{"namespace":"$argo_ns","namespaceExists":$(exists_ns "$argo_ns"),"installed":$(kubectl get crd applications.argoproj.io appprojects.argoproj.io >/dev/null 2>&1 && printf true || printf false),"projectExists":$(kubectl -n "$argo_ns" get appproject "$argo_project" >/dev/null 2>&1 && printf true || printf false),"applicationExists":$(kubectl -n "$argo_ns" get application "$argo_app" >/dev/null 2>&1 && printf true || printf false),"install":$argo_fragment},"registry":{"endpoint":"$registry","ready":$registry_ready,"toolsImage":"$tools_image","toolsImageReady":$tools_image_ready},"runtimeNamespace":{"name":"$runtime_ns","exists":$(exists_ns "$runtime_ns")}}}
JSON
`;
}
@@ -1139,7 +1439,7 @@ function statusNext(
|| !boolField(gitMirror, "namespaceExists")
|| !boolField(gitMirror, "readServiceExists")
|| !boolField(gitMirror, "writeServiceExists")
|| !boolField(gitMirror, "cachePvcExists");
|| (!boolField(gitMirror, "cachePvcExists") && !boolField(gitMirror, "cacheHostPathReady"));
const blockers: string[] = [];
if (!boolField(registry, "ready")) blockers.push("node-local-registry-not-ready");
if (!boolField(registry, "toolsImageReady")) blockers.push("tools-image-missing");