fix: bootstrap PaC admission provenance
This commit is contained in:
@@ -166,10 +166,16 @@ test("PaC apply checks the Gitea repository before dry-run return and cluster mu
|
||||
const preflight = remote.indexOf("if ! repository_preflight", action);
|
||||
const dryRun = remote.indexOf('if [ "$UNIDESK_PAC_DRY_RUN" = "1" ]', action);
|
||||
const release = remote.indexOf(" apply_release", action);
|
||||
const admission = remote.indexOf(" apply_admission_manifest", release);
|
||||
const rbac = remote.indexOf(" rbac_tmp=$(mktemp)", admission);
|
||||
expect(action).toBeGreaterThanOrEqual(0);
|
||||
expect(preflight).toBeGreaterThan(action);
|
||||
expect(dryRun).toBeGreaterThan(preflight);
|
||||
expect(release).toBeGreaterThan(dryRun);
|
||||
expect(admission).toBeGreaterThan(release);
|
||||
expect(rbac).toBeGreaterThan(admission);
|
||||
expect(remote).toContain('"admission":{"applied":%s,"ready":%s}');
|
||||
expect(remote).toContain('"admissionProvenance":%s');
|
||||
expect(remote).toContain('"error":"gitea-repository-missing"');
|
||||
});
|
||||
|
||||
@@ -197,7 +203,7 @@ test("PaC bootstrap projects typed stages and compact JSON", () => {
|
||||
const projection = projectPacBootstrapResult(result);
|
||||
expect(projection.ok).toBe(true);
|
||||
expect(JSON.stringify(projection)).not.toContain("pipelinesAsCodeApply");
|
||||
expect((projection.stages as Record<string, unknown>[]).map((item) => item.id)).toEqual(["gitea-init", "pac-controller", "tekton-consumer", "gitops-argo"]);
|
||||
expect((projection.stages as Record<string, unknown>[]).map((item) => item.id)).toEqual(["gitea-init", "pac-controller", "pac-admission", "tekton-consumer", "gitops-argo"]);
|
||||
const rendered = renderPacBootstrap(result).renderedText;
|
||||
expect(rendered).toContain("PRECONDITIONS");
|
||||
expect(rendered).toContain("yaml-repository-exact-match");
|
||||
@@ -237,6 +243,6 @@ test("PaC bootstrap distinguishes GitHub access failure from Gitea and PaC stage
|
||||
});
|
||||
expect((projection.blockers as Record<string, unknown>[])).toEqual([{ code: "github-repository-not-found-or-no-access", stage: "github-upstream", reason: "repository unavailable" }]);
|
||||
expect((projection.preconditions as Record<string, unknown>[])[2]).toMatchObject({ ok: null, code: "gitea-bootstrap-skipped" });
|
||||
expect((projection.stages as Record<string, unknown>[]).map((item) => item.state)).toEqual(["skipped", "skipped", "skipped", "skipped"]);
|
||||
expect((projection.stages as Record<string, unknown>[]).map((item) => item.state)).toEqual(["skipped", "skipped", "skipped", "skipped", "skipped"]);
|
||||
expect((projection.next as Record<string, unknown>).kind).toBe("read-only-status");
|
||||
});
|
||||
|
||||
@@ -69,6 +69,7 @@ export function projectPacBootstrapResult(result: Record<string, unknown>): Reco
|
||||
const gitea = record(result.giteaBootstrap);
|
||||
const apply = record(result.pipelinesAsCodeApply);
|
||||
const remote = record(apply.remote);
|
||||
const admission = record(remote.admission);
|
||||
const blockers = bootstrapBlockers(githubPreflight, gitea, apply, remote);
|
||||
const dryRun = result.mode === "dry-run";
|
||||
const repositoryMissing = remote.error === "gitea-repository-missing";
|
||||
@@ -101,6 +102,7 @@ export function projectPacBootstrapResult(result: Record<string, unknown>): Reco
|
||||
stages: [
|
||||
{ id: "gitea-init", ok: giteaSkipped ? null : giteaReady, state: giteaSkipped ? "skipped" : giteaReady ? (dryRun ? "planned" : "ready") : "failed", mutation: gitea.mutation === true },
|
||||
{ id: "pac-controller", ok: applySkipped ? null : applyReady, state: applySkipped ? "skipped" : repositoryMissing ? "waiting-for-gitea-confirm" : applyReady ? (dryRun ? "planned" : "ready") : "failed", mutation: apply.mutation === true },
|
||||
{ id: "pac-admission", ok: applySkipped ? null : dryRun ? true : admission.ready === true, state: applySkipped ? "skipped" : repositoryMissing ? "waiting-for-gitea-confirm" : dryRun ? "planned" : admission.ready === true ? "ready" : "failed", mutation: admission.applied === true },
|
||||
{ id: "tekton-consumer", ok: applySkipped ? null : applyReady, state: applySkipped ? "skipped" : repositoryMissing ? "waiting-for-gitea-confirm" : applyReady ? (dryRun ? "planned" : "ready") : "failed", mutation: apply.mutation === true },
|
||||
{ id: "gitops-argo", ok: applySkipped ? null : applyReady, state: applySkipped ? "skipped" : repositoryMissing ? "waiting-for-gitea-confirm" : applyReady ? (dryRun ? "planned" : "ready") : "failed", mutation: apply.mutation === true },
|
||||
],
|
||||
|
||||
@@ -284,6 +284,13 @@ apply_release() {
|
||||
rm -f "$tmp"
|
||||
}
|
||||
|
||||
apply_admission_manifest() {
|
||||
tmp=$(mktemp)
|
||||
printf '%s' "$UNIDESK_PAC_ADMISSION_MANIFEST_B64" | base64 -d > "$tmp"
|
||||
kubectl apply --server-side --force-conflicts --field-manager="$UNIDESK_PAC_FIELD_MANAGER-admission-bootstrap" -f "$tmp" >/dev/null
|
||||
rm -f "$tmp"
|
||||
}
|
||||
|
||||
wait_ready() {
|
||||
timeout="${UNIDESK_PAC_WAIT_TIMEOUT_SECONDS:-55}"
|
||||
end=$(( $(now_epoch) + timeout ))
|
||||
@@ -313,6 +320,11 @@ apply_action() {
|
||||
return
|
||||
fi
|
||||
apply_release
|
||||
admission_applied=false
|
||||
if [ "${UNIDESK_PAC_DELIVERY_PROVENANCE_REQUIRED:-0}" = "1" ]; then
|
||||
apply_admission_manifest
|
||||
admission_applied=true
|
||||
fi
|
||||
kubectl create ns "$UNIDESK_PAC_TARGET_NAMESPACE" --dry-run=client -o yaml | kubectl apply -f - >/dev/null
|
||||
if [ "${UNIDESK_PAC_DELIVERY_PROVENANCE_REQUIRED:-0}" = "1" ]; then
|
||||
rbac_tmp=$(mktemp)
|
||||
@@ -351,7 +363,16 @@ apply_action() {
|
||||
hook_id=$(ensure_webhook)
|
||||
ready=false
|
||||
if wait_ready; then ready=true; fi
|
||||
printf '{"ok":%s,"mode":"confirmed","mutation":true,"controllerReady":%s,"argoBootstrap":%s,"preflight":{"repositoryExists":true,"httpStatus":"2xx","repository":"%s/%s","valuesPrinted":false},"webhook":{"present":true,"id":"%s","url":"%s"},"repository":"%s","namespace":"%s","valuesPrinted":false}\n' "$ready" "$ready" "$argo_bootstrap" "$(json_string "$UNIDESK_PAC_GITEA_OWNER")" "$(json_string "$UNIDESK_PAC_GITEA_REPO")" "$(json_string "$hook_id")" "$(json_string "$UNIDESK_PAC_WEBHOOK_URL")" "$(json_string "$UNIDESK_PAC_REPOSITORY_NAME")" "$(json_string "$UNIDESK_PAC_TARGET_NAMESPACE")"
|
||||
admission_ready=true
|
||||
if [ "${UNIDESK_PAC_DELIVERY_PROVENANCE_REQUIRED:-0}" = "1" ]; then
|
||||
admission_ready=false
|
||||
if wait_admission_ready; then admission_ready=true; fi
|
||||
else
|
||||
prepare_admission_provenance_state
|
||||
fi
|
||||
ok=false
|
||||
if [ "$ready" = true ] && [ "$admission_ready" = true ]; then ok=true; fi
|
||||
printf '{"ok":%s,"mode":"confirmed","mutation":true,"controllerReady":%s,"admission":{"applied":%s,"ready":%s},"admissionProvenance":%s,"argoBootstrap":%s,"preflight":{"repositoryExists":true,"httpStatus":"2xx","repository":"%s/%s","valuesPrinted":false},"webhook":{"present":true,"id":"%s","url":"%s"},"repository":"%s","namespace":"%s","valuesPrinted":false}\n' "$ok" "$ready" "$admission_applied" "$admission_ready" "$UNIDESK_PAC_ADMISSION_STATE_JSON" "$argo_bootstrap" "$(json_string "$UNIDESK_PAC_GITEA_OWNER")" "$(json_string "$UNIDESK_PAC_GITEA_REPO")" "$(json_string "$hook_id")" "$(json_string "$UNIDESK_PAC_WEBHOOK_URL")" "$(json_string "$UNIDESK_PAC_REPOSITORY_NAME")" "$(json_string "$UNIDESK_PAC_TARGET_NAMESPACE")"
|
||||
}
|
||||
|
||||
condition_status() {
|
||||
@@ -415,6 +436,19 @@ prepare_admission_provenance_state() {
|
||||
export UNIDESK_PAC_ADMISSION_STATE_JSON
|
||||
}
|
||||
|
||||
wait_admission_ready() {
|
||||
timeout="${UNIDESK_PAC_WAIT_TIMEOUT_SECONDS:-55}"
|
||||
end=$(( $(now_epoch) + timeout ))
|
||||
while :; do
|
||||
prepare_admission_provenance_state
|
||||
if printf '%s' "$UNIDESK_PAC_ADMISSION_STATE_JSON" | node -e 'let input="";process.stdin.on("data",chunk=>input+=chunk);process.stdin.on("end",()=>process.exit(JSON.parse(input).ready===true?0:1));'; then
|
||||
return 0
|
||||
fi
|
||||
if [ "$(now_epoch)" -ge "$end" ]; then return 1; fi
|
||||
sleep 2
|
||||
done
|
||||
}
|
||||
|
||||
pipeline_rows() {
|
||||
payload_file=$(mktemp)
|
||||
kubectl -n "$UNIDESK_PAC_TARGET_NAMESPACE" get pipelinerun -o json >"$payload_file" 2>/dev/null || printf '{"items":[]}' >"$payload_file"
|
||||
|
||||
@@ -28,7 +28,7 @@ import {
|
||||
type PacSourceArtifactRuntimeObservation,
|
||||
type PacSourceArtifactSpec,
|
||||
} from "./platform-infra-pipelines-as-code-source-artifact";
|
||||
import { pacAdmissionDesiredIdentity } from "./platform-infra-pac-provenance";
|
||||
import { pacAdmissionDesiredIdentity, renderPacAdmissionDesiredFragment } from "./platform-infra-pac-provenance";
|
||||
import { pacConsumerRbacDesiredIdentity, renderPacConsumerRbacDesiredFragment } from "./platform-infra-pac-consumer-rbac";
|
||||
import { runGiteaMirrorBootstrapPreflight, runPlatformInfraGiteaCommand } from "./platform-infra-gitea";
|
||||
import { readGiteaConfig } from "./platform-infra-gitea-config";
|
||||
@@ -1754,6 +1754,7 @@ function remoteScript(action: "apply" | "status" | "history" | "debug-step", pac
|
||||
UNIDESK_PAC_PIPELINE_RUN_PREFIX: consumer.pipelineRunPrefix,
|
||||
UNIDESK_PAC_CONSUMER_CONFIG_B64: Buffer.from(JSON.stringify(consumerConfig), "utf8").toString("base64"),
|
||||
UNIDESK_PAC_DELIVERY_PROVENANCE_REQUIRED: consumer.deliveryProvenance?.required === true ? "1" : "0",
|
||||
UNIDESK_PAC_ADMISSION_MANIFEST_B64: Buffer.from(renderPacAdmissionDesiredFragment(target.id), "utf8").toString("base64"),
|
||||
UNIDESK_PAC_CONSUMER_RBAC_MANIFEST_B64: Buffer.from(renderPacConsumerRbacDesiredFragment(target.id), "utf8").toString("base64"),
|
||||
UNIDESK_PAC_RUNNER_SERVICE_ACCOUNT_MANIFEST_B64: Buffer.from(runnerServiceAccountManifest(consumer), "utf8").toString("base64"),
|
||||
UNIDESK_PAC_RUNNER_SERVICE_ACCOUNT_NAME: consumer.runnerServiceAccount?.name ?? "",
|
||||
|
||||
Reference in New Issue
Block a user