fix: migrate JD01 registry to k3s workload

This commit is contained in:
Codex
2026-06-29 12:11:40 +00:00
parent 50bb9fbfe8
commit 912090ecd4
6 changed files with 405 additions and 17 deletions
+238 -12
View File
@@ -12,11 +12,13 @@ import {
type ControlPlaneK3sInstallSpec,
type ControlPlaneK3sNodeSpec,
type ControlPlaneNodeSpec,
type ControlPlaneRegistrySpec,
type ControlPlaneRuntimeProxySpec,
type ControlPlaneTargetSpec,
type TektonInstallOptions,
type ToolsImageOptions,
} from "./hwlab-node-control-plane-model";
import { controlPlaneRegistrySummary } from "./hwlab-node-control-plane-registry";
export function argoDesiredManifest(target: ControlPlaneTargetSpec): Record<string, unknown>[] {
return [argoProjectSkeleton(target), argoApplicationSkeleton(target)];
@@ -60,7 +62,7 @@ export function planSummary(node: ControlPlaneNodeSpec, target: ControlPlaneTarg
ciNamespace: target.ciNamespace,
runtimeNamespace: target.runtimeNamespace,
k3sNodeConfig: k3sNodeConfigPlan(node),
registry: node.registry.endpoint,
registry: controlPlaneRegistrySummary(node.registry),
egressProxy: controlPlaneEgressProxySummary(node.egressProxy),
sourceBranch: target.source.branch,
gitopsBranch: target.gitops.branch,
@@ -147,7 +149,7 @@ export function expectedSummary(node: ControlPlaneNodeSpec, target: ControlPlane
expectedDeployments: target.argo.install.expectedDeployments,
expectedStatefulSets: target.argo.install.expectedStatefulSets,
},
registry: node.registry.endpoint,
registry: controlPlaneRegistrySummary(node.registry),
imagePolicy: {
noPrivateInputImages: true,
buildInput: { sourceKind: target.tekton.toolsImage.sourceKind, context: target.tekton.toolsImage.context, dockerfile: target.tekton.toolsImage.dockerfile ?? null, dockerfileInline: target.tekton.toolsImage.dockerfileInline ?? null, composeFile: target.tekton.toolsImage.composeFile ?? null, publicBaseImages: target.tekton.toolsImage.publicBaseImages },
@@ -713,6 +715,7 @@ argo_app=${shQuote(target.argo.applicationName)}
argo_observer_role=${shQuote(target.tekton.argoObserverRbac.roleName)}
argo_observer_rolebinding=${shQuote(target.tekton.argoObserverRbac.roleBindingName)}
registry=${shQuote(nodeSpec.registry.endpoint)}
registry_spec_json=${shQuote(JSON.stringify(controlPlaneRegistrySummary(nodeSpec.registry)))}
tools_image=${shQuote(target.tekton.toolsImage.output)}
tekton_required_crds_json=${shQuote(tektonRequiredCrds)}
tekton_deployment_namespaces_json=${shQuote(tektonDeploymentNamespaces)}
@@ -838,8 +841,67 @@ print(json.dumps({
}))
PY
)
registry_ready=false
if command -v curl >/dev/null 2>&1; then curl -fsS --max-time 3 "http://$registry/v2/" >/tmp/hwlab-registry.out 2>/tmp/hwlab-registry.err && registry_ready=true; fi
registry_endpoint_ready=false
if command -v curl >/dev/null 2>&1; then curl -fsS --max-time 3 "http://$registry/v2/" >/tmp/hwlab-registry.out 2>/tmp/hwlab-registry.err && registry_endpoint_ready=true; fi
registry_workload_json=$(python3 - "$registry_spec_json" "$registry_endpoint_ready" <<'PY'
import json, subprocess, sys
spec=json.loads(sys.argv[1])
endpoint_ready=sys.argv[2] == "true"
def run(args):
return subprocess.run(args, stdout=subprocess.PIPE, stderr=subprocess.PIPE, text=True)
def exists(kind, namespace, name):
return run(["kubectl", "-n", namespace, "get", kind, name]).returncode == 0
def deploy_ready(namespace, name):
proc=run(["kubectl", "-n", namespace, "get", "deploy", name, "-o", "json"])
if proc.returncode != 0:
return {"exists": False, "ready": False, "desired": None, "readyReplicas": None}
try:
obj=json.loads(proc.stdout or "{}")
except Exception:
obj={}
desired=int(obj.get("spec", {}).get("replicas") or 0)
ready=int(obj.get("status", {}).get("readyReplicas") or 0)
return {"exists": True, "ready": desired > 0 and ready == desired, "desired": desired, "readyReplicas": ready}
if spec.get("mode") != "k8s-workload":
print(json.dumps({"mode": spec.get("mode") or "host-docker", "managed": False, "ready": endpoint_ready, "endpointReady": endpoint_ready, "valuesPrinted": False}))
raise SystemExit(0)
ns=str(spec.get("namespace") or "")
deploy=str(spec.get("deploymentName") or "")
svc=str(spec.get("serviceName") or "")
pvc=str(spec.get("pvcName") or "")
deployment=deploy_ready(ns, deploy)
namespace_exists=run(["kubectl", "get", "ns", ns]).returncode == 0
pvc_exists=exists("pvc", ns, pvc)
service_exists=exists("service", ns, svc)
ready=endpoint_ready and namespace_exists and pvc_exists and service_exists and deployment.get("ready") is True
print(json.dumps({
"mode": "k8s-workload",
"managed": True,
"ready": ready,
"endpointReady": endpoint_ready,
"namespace": ns,
"namespaceExists": namespace_exists,
"deploymentName": deploy,
"deployment": deployment,
"serviceName": svc,
"serviceExists": service_exists,
"pvcName": pvc,
"pvcExists": pvc_exists,
"seededFromOldRegistry": False,
"zeroSeeded": True,
"valuesPrinted": False,
}))
PY
)
registry_ready=$(python3 - "$registry_workload_json" <<'PY'
import json, sys
try:
data=json.loads(sys.argv[1] or "{}")
except Exception:
data={}
print("true" if data.get("ready") is True else "false")
PY
)
tools_repo_tag=\${tools_image#\${registry}/}
tools_repo=\${tools_repo_tag%:*}
tools_tag=\${tools_repo_tag##*:}
@@ -1114,7 +1176,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":{"k3sNodeConfig":$k3s_fragment,"tekton":{"installed":$tekton_installed,"controllerReady":$(deploy_ready tekton-pipelines tekton-pipelines-controller),"webhookReady":$(deploy_ready tekton-pipelines tekton-pipelines-webhook),"install":$tekton_fragment},"ciNamespace":{"name":"$ci_ns","exists":$(exists_ns "$ci_ns"),"serviceAccountExists":$(exists_res "$ci_ns" serviceaccount "$service_account"),"pipelineExists":$(exists_res "$ci_ns" pipeline "$pipeline"),"gitWorkspaceSecret":$ci_git_workspace_json},"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,"egressProxy":$gitmirror_egress_proxy_json,"githubTransport":$github_transport_json,"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),"argoObserverRbac":{"roleName":"$argo_observer_role","roleExists":$argo_observer_role_exists,"roleBindingName":"$argo_observer_rolebinding","roleBindingExists":$argo_observer_rolebinding_exists,"serviceAccountNamespace":"$ci_ns","serviceAccountName":"$service_account","canGetApplication":$argo_observer_can_get_application,"ready":$argo_observer_ready},"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"),"runtimeObserverRbac":{"roleName":"$runtime_observer_role","roleExists":$runtime_observer_role_exists,"roleBindingName":"$runtime_observer_rolebinding","roleBindingExists":$runtime_observer_rolebinding_exists,"serviceAccountNamespace":"$ci_ns","serviceAccountName":"$service_account","canListDeployments":$runtime_observer_can_list_deployments,"canListStatefulSets":$runtime_observer_can_list_statefulsets,"ready":$runtime_observer_ready}}}}
{"observedAt":"$(date -u +%Y-%m-%dT%H:%M:%SZ)","node":"$node","lane":"$lane","components":{"k3sNodeConfig":$k3s_fragment,"tekton":{"installed":$tekton_installed,"controllerReady":$(deploy_ready tekton-pipelines tekton-pipelines-controller),"webhookReady":$(deploy_ready tekton-pipelines tekton-pipelines-webhook),"install":$tekton_fragment},"ciNamespace":{"name":"$ci_ns","exists":$(exists_ns "$ci_ns"),"serviceAccountExists":$(exists_res "$ci_ns" serviceaccount "$service_account"),"pipelineExists":$(exists_res "$ci_ns" pipeline "$pipeline"),"gitWorkspaceSecret":$ci_git_workspace_json},"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,"egressProxy":$gitmirror_egress_proxy_json,"githubTransport":$github_transport_json,"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),"argoObserverRbac":{"roleName":"$argo_observer_role","roleExists":$argo_observer_role_exists,"roleBindingName":"$argo_observer_rolebinding","roleBindingExists":$argo_observer_rolebinding_exists,"serviceAccountNamespace":"$ci_ns","serviceAccountName":"$service_account","canGetApplication":$argo_observer_can_get_application,"ready":$argo_observer_ready},"install":$argo_fragment},"registry":{"endpoint":"$registry","ready":$registry_ready,"endpointReady":$registry_endpoint_ready,"workload":$registry_workload_json,"toolsImage":"$tools_image","toolsImageReady":$tools_image_ready},"runtimeNamespace":{"name":"$runtime_ns","exists":$(exists_ns "$runtime_ns"),"runtimeObserverRbac":{"roleName":"$runtime_observer_role","roleExists":$runtime_observer_role_exists,"roleBindingName":"$runtime_observer_rolebinding","roleBindingExists":$runtime_observer_rolebinding_exists,"serviceAccountNamespace":"$ci_ns","serviceAccountName":"$service_account","canListDeployments":$runtime_observer_can_list_deployments,"canListStatefulSets":$runtime_observer_can_list_statefulsets,"ready":$runtime_observer_ready}}}}
JSON
`;
}
@@ -1126,26 +1188,154 @@ set +e
manifest=$(mktemp /tmp/hwlab-node-infra.XXXXXX.yaml)
printf %s ${shQuote(encoded)} | base64 -d >"$manifest"
field_manager=${shQuote(controlPlaneFieldManager(target))}
${registryPreApplyScript(node)}
kubectl apply --server-side --force-conflicts --field-manager="$field_manager" -f "$manifest" >/tmp/hwlab-node-infra-apply.out 2>/tmp/hwlab-node-infra-apply.err
kubectl_rc=$?
${k3sApplyScriptFragment(node.k3s, target)}
python3 - "$kubectl_rc" "$k3s_report_file" <<'PY'
${registryPostApplyScript(node)}
python3 - "$kubectl_rc" "$k3s_report_file" "$registry_pre_report_file" "$registry_report_file" <<'PY'
import json, pathlib, sys
k3s_report = {}
try:
k3s_report = json.loads(pathlib.Path(sys.argv[2]).read_text(errors='replace'))
except Exception as exc:
k3s_report = {"managed": None, "ok": False, "parseError": str(exc)}
registry_pre = {}
try:
registry_pre = json.loads(pathlib.Path(sys.argv[3]).read_text(errors='replace'))
except Exception as exc:
registry_pre = {"managed": None, "ok": False, "parseError": str(exc)}
registry_report = {}
try:
registry_report = json.loads(pathlib.Path(sys.argv[4]).read_text(errors='replace'))
except Exception as exc:
registry_report = {"managed": None, "ok": False, "parseError": str(exc)}
out=pathlib.Path('/tmp/hwlab-node-infra-apply.out').read_text(errors='replace') if pathlib.Path('/tmp/hwlab-node-infra-apply.out').exists() else ''
err=pathlib.Path('/tmp/hwlab-node-infra-apply.err').read_text(errors='replace') if pathlib.Path('/tmp/hwlab-node-infra-apply.err').exists() else ''
print(json.dumps({'k3sNodeConfig': k3s_report, 'kubernetesApply': {'applyExitCode': int(sys.argv[1]), 'stdoutPreview': out[-2000:], 'stderrPreview': err[-2000:], 'runtimeRolloutTriggered': False, 'pk01Touched': False}}, ensure_ascii=False))
print(json.dumps({'k3sNodeConfig': k3s_report, 'registryMigration': {'preApply': registry_pre, 'postApply': registry_report}, 'kubernetesApply': {'applyExitCode': int(sys.argv[1]), 'stdoutPreview': out[-2000:], 'stderrPreview': err[-2000:], 'runtimeRolloutTriggered': False, 'pk01Touched': False}}, ensure_ascii=False))
PY
rm -f "$manifest"
if [ "$registry_pre_rc" != 0 ]; then exit "$registry_pre_rc"; fi
if [ "$kubectl_rc" != 0 ]; then exit "$kubectl_rc"; fi
if [ "$registry_rc" != 0 ]; then exit "$registry_rc"; fi
exit "$k3s_rc"
`;
}
export function registryPreApplyScript(node: ControlPlaneNodeSpec): string {
if (node.registry.mode !== "k8s-workload") {
return `
registry_pre_report_file=$(mktemp /tmp/hwlab-node-registry-pre.XXXXXX.json)
printf '{"managed":false,"ok":true,"mutation":false}\\n' >"$registry_pre_report_file"
registry_pre_rc=0
`;
}
const installRegistry = node.k3s?.install?.localRegistry ?? null;
return `
registry_pre_report_file=$(mktemp /tmp/hwlab-node-registry-pre.XXXXXX.json)
host_registry_name=${shQuote(installRegistry?.containerName ?? "")}
registry_pre_rc=0
host_registry_exists=false
host_registry_running=false
docker_stop_rc=0
if [ -n "$host_registry_name" ] && command -v docker >/dev/null 2>&1; then
if docker ps -a --format '{{.Names}}' 2>/dev/null | grep -Fx "$host_registry_name" >/dev/null 2>&1; then
host_registry_exists=true
if docker ps --format '{{.Names}}' 2>/dev/null | grep -Fx "$host_registry_name" >/dev/null 2>&1; then host_registry_running=true; fi
docker stop "$host_registry_name" >/tmp/hwlab-registry-docker-stop.out 2>/tmp/hwlab-registry-docker-stop.err || docker_stop_rc=$?
if [ "$docker_stop_rc" != 0 ]; then registry_pre_rc=$docker_stop_rc; fi
fi
fi
python3 - "$host_registry_exists" "$host_registry_running" "$docker_stop_rc" "$host_registry_name" <<'PY' >"$registry_pre_report_file"
import json, pathlib, sys
def tail(path):
p = pathlib.Path(path)
return p.read_text(errors='replace')[-1000:] if p.exists() else ''
print(json.dumps({
"managed": True,
"ok": int(sys.argv[3] or "0") == 0,
"mutation": sys.argv[1] == "true",
"mode": "k8s-workload",
"hostDockerRegistryName": sys.argv[4] or None,
"hostDockerRegistryExists": sys.argv[1] == "true",
"hostDockerRegistryWasRunning": sys.argv[2] == "true",
"stoppedBeforeApply": sys.argv[1] == "true" and int(sys.argv[3] or "0") == 0,
"seededFromOldRegistry": False,
"zeroSeeded": True,
"dockerStopExitCode": int(sys.argv[3] or "0"),
"dockerStopStderrTail": tail('/tmp/hwlab-registry-docker-stop.err'),
"valuesPrinted": False,
}, ensure_ascii=False))
PY
`;
}
export function registryPostApplyScript(node: ControlPlaneNodeSpec): string {
if (node.registry.mode !== "k8s-workload") {
return `
registry_report_file=$(mktemp /tmp/hwlab-node-registry.XXXXXX.json)
printf '{"managed":false,"ok":true,"mutation":false}\\n' >"$registry_report_file"
registry_rc=0
`;
}
return `
registry_report_file=$(mktemp /tmp/hwlab-node-registry.XXXXXX.json)
registry_ns=${shQuote(node.registry.namespace)}
registry_deploy=${shQuote(node.registry.deploymentName)}
registry_pvc=${shQuote(node.registry.pvcName)}
registry_endpoint=${shQuote(node.registry.endpoint)}
registry_rc=0
registry_pv=
registry_pv_path=
registry_pvc_bound=false
for _ in $(seq 1 60); do
registry_phase=$(kubectl -n "$registry_ns" get pvc "$registry_pvc" -o 'jsonpath={.status.phase}' 2>/dev/null || true)
if [ "$registry_phase" = Bound ]; then registry_pvc_bound=true; break; fi
sleep 2
done
if [ "$registry_pvc_bound" = true ]; then
registry_pv=$(kubectl -n "$registry_ns" get pvc "$registry_pvc" -o 'jsonpath={.spec.volumeName}' 2>/dev/null || true)
if [ -n "$registry_pv" ]; then
registry_pv_path=$(kubectl get pv "$registry_pv" -o 'jsonpath={.spec.hostPath.path}' 2>/dev/null || true)
if [ -z "$registry_pv_path" ]; then registry_pv_path=$(kubectl get pv "$registry_pv" -o 'jsonpath={.spec.local.path}' 2>/dev/null || true); fi
fi
fi
kubectl -n "$registry_ns" delete pod -l "app.kubernetes.io/name=$registry_deploy" --ignore-not-found >/tmp/hwlab-registry-pod-delete.out 2>/tmp/hwlab-registry-pod-delete.err || true
kubectl -n "$registry_ns" rollout status deploy/"$registry_deploy" --timeout=180s >/tmp/hwlab-registry-rollout.out 2>/tmp/hwlab-registry-rollout.err
registry_rollout_rc=$?
registry_endpoint_ready=false
if command -v curl >/dev/null 2>&1; then curl -fsS --max-time 5 "http://$registry_endpoint/v2/" >/tmp/hwlab-registry-endpoint.out 2>/tmp/hwlab-registry-endpoint.err && registry_endpoint_ready=true; fi
if [ "$registry_rollout_rc" != 0 ] || [ "$registry_endpoint_ready" != true ]; then registry_rc=1; fi
python3 - "$registry_rc" "$registry_pvc_bound" "$registry_pv" "$registry_pv_path" "$registry_rollout_rc" "$registry_endpoint_ready" "$registry_ns" "$registry_deploy" "$registry_pvc" "$registry_endpoint" <<'PY' >"$registry_report_file"
import json, pathlib, sys
def tail(path):
p = pathlib.Path(path)
return p.read_text(errors='replace')[-1000:] if p.exists() else ''
payload = {
"managed": True,
"ok": int(sys.argv[1]) == 0,
"mutation": True,
"mode": "k8s-workload",
"namespace": sys.argv[7],
"deploymentName": sys.argv[8],
"pvcName": sys.argv[9],
"endpoint": sys.argv[10],
"pvcBound": sys.argv[2] == "true",
"pvName": sys.argv[3] or None,
"pvPathPresent": bool(sys.argv[4]),
"seededFromOldRegistry": False,
"zeroSeeded": True,
"rolloutExitCode": int(sys.argv[5] or "1"),
"endpointReady": sys.argv[6] == "true",
"rolloutStdoutTail": tail('/tmp/hwlab-registry-rollout.out'),
"rolloutStderrTail": tail('/tmp/hwlab-registry-rollout.err'),
"valuesPrinted": False,
}
print(json.dumps(payload, ensure_ascii=False))
PY
`;
}
export function controlPlaneFieldManager(target: ControlPlaneTargetSpec): string {
return `unidesk-hwlab-${target.node.toLowerCase()}-${target.lane}-control-plane`;
}
@@ -1395,7 +1585,7 @@ export function toolsImageStatus(node: ControlPlaneNodeSpec, target: ControlPlan
toolsImageReady: boolean;
result: Record<string, unknown>;
} {
const result = runTransK3s(node.kubeRoute, registryStatusScript(node.registry.endpoint, target.tekton.toolsImage.output), timeoutSeconds);
const result = runTransK3s(node.kubeRoute, registryStatusScript(node.registry, target.tekton.toolsImage.output), timeoutSeconds);
const parsed = parseRemoteJson(result.stdout);
const status = typeof parsed === "object" && parsed !== null ? parsed as Record<string, unknown> : {};
return {
@@ -1492,13 +1682,49 @@ export function statusNext(
return next;
}
export function registryStatusScript(registryEndpoint: string, toolsImage: string): string {
export function registryStatusScript(registrySpec: ControlPlaneRegistrySpec, toolsImage: string): string {
const registryEndpoint = registrySpec.endpoint;
const registrySpecJson = JSON.stringify(controlPlaneRegistrySummary(registrySpec));
return `
set +e
registry=${shQuote(registryEndpoint)}
registry_spec_json=${shQuote(registrySpecJson)}
tools_image=${shQuote(toolsImage)}
registry_ready=false
if command -v curl >/dev/null 2>&1; then curl -fsS --max-time 3 "http://$registry/v2/" >/tmp/hwlab-registry.out 2>/tmp/hwlab-registry.err && registry_ready=true; fi
registry_endpoint_ready=false
if command -v curl >/dev/null 2>&1; then curl -fsS --max-time 3 "http://$registry/v2/" >/tmp/hwlab-registry.out 2>/tmp/hwlab-registry.err && registry_endpoint_ready=true; fi
registry_workload_json=$(python3 - "$registry_spec_json" "$registry_endpoint_ready" <<'PY'
import json, subprocess, sys
spec=json.loads(sys.argv[1])
endpoint_ready=sys.argv[2] == "true"
def run(args):
return subprocess.run(args, stdout=subprocess.PIPE, stderr=subprocess.PIPE, text=True)
def exists(kind, namespace, name):
return run(["kubectl", "-n", namespace, "get", kind, name]).returncode == 0
def deploy_ready(namespace, name):
proc=run(["kubectl", "-n", namespace, "get", "deploy", name, "-o", "json"])
if proc.returncode != 0:
return False
data=json.loads(proc.stdout or "{}")
desired=int(data.get("spec", {}).get("replicas") or 0)
ready=int(data.get("status", {}).get("readyReplicas") or 0)
return desired > 0 and ready == desired
if spec.get("mode") != "k8s-workload":
print(json.dumps({"mode": spec.get("mode") or "host-docker", "managed": False, "ready": endpoint_ready, "endpointReady": endpoint_ready, "valuesPrinted": False}))
raise SystemExit(0)
ns=str(spec.get("namespace") or "")
ready=endpoint_ready and run(["kubectl", "get", "ns", ns]).returncode == 0 and exists("pvc", ns, str(spec.get("pvcName") or "")) and exists("service", ns, str(spec.get("serviceName") or "")) and deploy_ready(ns, str(spec.get("deploymentName") or ""))
print(json.dumps({"mode":"k8s-workload","managed":True,"ready":ready,"endpointReady":endpoint_ready,"namespace":ns,"deploymentName":spec.get("deploymentName"),"serviceName":spec.get("serviceName"),"pvcName":spec.get("pvcName"),"seededFromOldRegistry":False,"zeroSeeded":True,"valuesPrinted":False}))
PY
)
registry_ready=$(python3 - "$registry_workload_json" <<'PY'
import json, sys
try:
data=json.loads(sys.argv[1] or "{}")
except Exception:
data={}
print("true" if data.get("ready") is True else "false")
PY
)
tools_repo_tag=\${tools_image#\${registry}/}
tools_repo=\${tools_repo_tag%:*}
tools_tag=\${tools_repo_tag##*:}
@@ -1506,7 +1732,7 @@ tools_image_ready=false
manifest_accept='application/vnd.oci.image.manifest.v1+json, application/vnd.docker.distribution.manifest.v2+json, application/vnd.oci.image.index.v1+json, application/vnd.docker.distribution.manifest.list.v2+json'
if [ "$tools_repo" != "$tools_repo_tag" ] && command -v curl >/dev/null 2>&1; then curl -fsS --max-time 5 -H "Accept: $manifest_accept" "http://$registry/v2/$tools_repo/manifests/$tools_tag" >/tmp/hwlab-tools-image.out 2>/tmp/hwlab-tools-image.err && tools_image_ready=true; fi
cat <<JSON
{"registry":"$registry","toolsImage":"$tools_image","registryReady":$registry_ready,"toolsImageReady":$tools_image_ready}
{"registry":"$registry","toolsImage":"$tools_image","registryReady":$registry_ready,"registryEndpointReady":$registry_endpoint_ready,"registryWorkload":$registry_workload_json,"toolsImageReady":$tools_image_ready}
JSON
`;
}