feat: 接入 selfmedia 受控交付

This commit is contained in:
Codex
2026-07-13 07:56:37 +02:00
parent 48aa08bf73
commit 80ca187cb3
17 changed files with 1845 additions and 71 deletions
@@ -202,6 +202,50 @@ roleRef:
EOF
}
pac_repository_secret_manifest() {
node -e '
const fs = require("node:fs");
const input = fs.readFileSync(0);
const split = input.indexOf(0);
if (split < 0) process.exit(2);
const token = input.subarray(0, split);
const webhook = input.subarray(split + 1);
const data = {};
data[process.env.UNIDESK_PAC_TOKEN_KEY] = token.toString("base64");
data[process.env.UNIDESK_PAC_WEBHOOK_SECRET_KEY] = webhook.toString("base64");
process.stdout.write(JSON.stringify({
apiVersion: "v1",
kind: "Secret",
metadata: { name: process.env.UNIDESK_PAC_SECRET_NAME, namespace: process.env.UNIDESK_PAC_TARGET_NAMESPACE },
type: "Opaque",
data,
}));
'
}
argo_repository_secret_manifest() {
node -e '
const fs = require("node:fs");
const token = fs.readFileSync(0);
const encoded = (value) => Buffer.from(value, "utf8").toString("base64");
process.stdout.write(JSON.stringify({
apiVersion: "v1",
kind: "Secret",
metadata: {
name: process.env.UNIDESK_PAC_ARGO_REPOSITORY_SECRET_NAME,
namespace: process.env.UNIDESK_PAC_ARGO_NAMESPACE,
labels: { "argocd.argoproj.io/secret-type": "repository" },
},
type: "Opaque",
data: {
url: encoded(process.env.UNIDESK_PAC_ARGO_REPOSITORY_URL || ""),
username: encoded(process.env.UNIDESK_PAC_ARGO_REPOSITORY_USERNAME || ""),
password: token.toString("base64"),
},
}));
'
}
apply_release() {
tmp=$(mktemp)
printf '%s' "$UNIDESK_PAC_RELEASE_MANIFEST_B64" | base64 -d > "$tmp"
@@ -238,15 +282,25 @@ apply_action() {
else
consumer_rbac_manifest | kubectl apply --server-side --force-conflicts --field-manager="$UNIDESK_PAC_FIELD_MANAGER" -f - >/dev/null
fi
if [ -n "${UNIDESK_PAC_RUNNER_SERVICE_ACCOUNT_MANIFEST_B64:-}" ]; then
runner_tmp=$(mktemp)
printf '%s' "$UNIDESK_PAC_RUNNER_SERVICE_ACCOUNT_MANIFEST_B64" | base64 -d >"$runner_tmp"
kubectl apply --server-side --force-conflicts --field-manager="$UNIDESK_PAC_FIELD_MANAGER-runner-bootstrap" -f "$runner_tmp" >/dev/null
rm -f "$runner_tmp"
fi
token=$(ensure_token)
test -n "$token"
kubectl -n "$UNIDESK_PAC_TARGET_NAMESPACE" create secret generic "$UNIDESK_PAC_SECRET_NAME" \
--from-literal="$UNIDESK_PAC_TOKEN_KEY=$token" \
--from-literal="$UNIDESK_PAC_WEBHOOK_SECRET_KEY=$UNIDESK_PAC_WEBHOOK_SECRET" \
--dry-run=client -o yaml | kubectl apply -f - >/dev/null
{ printf '%s' "$token"; printf '\0'; printf '%s' "$UNIDESK_PAC_WEBHOOK_SECRET"; } \
| pac_repository_secret_manifest \
| kubectl apply --server-side --force-conflicts --field-manager="$UNIDESK_PAC_FIELD_MANAGER-repository-secret" -f - >/dev/null
repository_manifest | kubectl apply --server-side --force-conflicts --field-manager="$UNIDESK_PAC_FIELD_MANAGER" -f - >/dev/null
argo_bootstrap=false
if [ -n "${UNIDESK_PAC_ARGO_BOOTSTRAP_MANIFEST_B64:-}" ]; then
if [ -n "${UNIDESK_PAC_ARGO_REPOSITORY_SECRET_NAME:-}" ]; then
printf '%s' "$token" \
| argo_repository_secret_manifest \
| kubectl apply --server-side --force-conflicts --field-manager="$UNIDESK_PAC_FIELD_MANAGER-argocd-repository" -f - >/dev/null
fi
argo_tmp=$(mktemp)
printf '%s' "$UNIDESK_PAC_ARGO_BOOTSTRAP_MANIFEST_B64" | base64 -d >"$argo_tmp"
kubectl apply --server-side --force-conflicts --field-manager="$UNIDESK_PAC_FIELD_MANAGER" -f "$argo_tmp" >/dev/null
@@ -1287,11 +1341,121 @@ process.stdout.write(JSON.stringify(rows));
NODE
}
consumer_bootstrap_state() {
if [ -z "${UNIDESK_PAC_RUNNER_SERVICE_ACCOUNT_NAME:-}" ] && [ -z "${UNIDESK_PAC_ARGO_REPOSITORY_SECRET_NAME:-}" ]; then
printf '{"configured":false,"ready":null,"reasons":["consumer-bootstrap-not-declared"],"valuesPrinted":false}'
return
fi
sa_file=$(mktemp)
role_binding_file=$(mktemp)
argo_secret_file=$(mktemp)
if [ -n "${UNIDESK_PAC_RUNNER_SERVICE_ACCOUNT_NAME:-}" ]; then
kubectl -n "$UNIDESK_PAC_TARGET_NAMESPACE" get serviceaccount "$UNIDESK_PAC_RUNNER_SERVICE_ACCOUNT_NAME" -o json >"$sa_file" 2>/dev/null || printf '{}' >"$sa_file"
kubectl -n "$UNIDESK_PAC_TARGET_NAMESPACE" get rolebinding "$UNIDESK_PAC_RUNNER_ROLE_BINDING_NAME" -o json >"$role_binding_file" 2>/dev/null || printf '{}' >"$role_binding_file"
else
printf '{}' >"$sa_file"
printf '{}' >"$role_binding_file"
fi
if [ -n "${UNIDESK_PAC_ARGO_REPOSITORY_SECRET_NAME:-}" ]; then
kubectl -n "$UNIDESK_PAC_ARGO_NAMESPACE" get secret "$UNIDESK_PAC_ARGO_REPOSITORY_SECRET_NAME" -o json >"$argo_secret_file" 2>/dev/null || printf '{}' >"$argo_secret_file"
else
printf '{}' >"$argo_secret_file"
fi
node - "$sa_file" "$role_binding_file" "$argo_secret_file" <<'NODE'
const crypto = require('node:crypto');
const fs = require('node:fs');
function read(path) {
try { return JSON.parse(fs.readFileSync(path, 'utf8') || '{}'); } catch { return {}; }
}
function fingerprint(value) {
return `sha256:${crypto.createHash('sha256').update(value).digest('hex')}`;
}
const sa = read(process.argv[2]);
const roleBinding = read(process.argv[3]);
const argoSecret = read(process.argv[4]);
const namespace = process.env.UNIDESK_PAC_TARGET_NAMESPACE || '';
const runnerName = process.env.UNIDESK_PAC_RUNNER_SERVICE_ACCOUNT_NAME || '';
const roleBindingName = process.env.UNIDESK_PAC_RUNNER_ROLE_BINDING_NAME || '';
const argoNamespace = process.env.UNIDESK_PAC_ARGO_NAMESPACE || '';
const argoSecretName = process.env.UNIDESK_PAC_ARGO_REPOSITORY_SECRET_NAME || '';
const argoUrl = process.env.UNIDESK_PAC_ARGO_REPOSITORY_URL || '';
const reasons = [];
const runnerConfigured = runnerName.length > 0;
const saExists = !runnerConfigured || sa.metadata?.name === runnerName;
const automountDisabled = !runnerConfigured || sa.automountServiceAccountToken === false;
const subjects = Array.isArray(roleBinding.subjects) ? roleBinding.subjects : [];
const roleBindingExact = !runnerConfigured || (
roleBinding.metadata?.name === roleBindingName
&& roleBinding.metadata?.namespace === namespace
&& subjects.length === 1
&& subjects[0]?.kind === 'ServiceAccount'
&& subjects[0]?.name === runnerName
&& subjects[0]?.namespace === namespace
&& roleBinding.roleRef?.apiGroup === 'rbac.authorization.k8s.io'
&& roleBinding.roleRef?.kind === 'Role'
&& roleBinding.roleRef?.name === 'unidesk-pac-consumer-runner'
);
if (!saExists) reasons.push('runner-service-account-missing');
if (!automountDisabled) reasons.push('runner-service-account-automount-not-false');
if (!roleBindingExact) reasons.push('runner-rolebinding-drifted-or-missing');
const argoConfigured = argoSecretName.length > 0;
const argoExists = !argoConfigured || (argoSecret.metadata?.name === argoSecretName && argoSecret.metadata?.namespace === argoNamespace);
const argoLabelReady = !argoConfigured || argoSecret.metadata?.labels?.['argocd.argoproj.io/secret-type'] === 'repository';
const keys = Object.keys(argoSecret.data || {}).sort();
const expectedKeys = ['password', 'url', 'username'];
const keysReady = !argoConfigured || JSON.stringify(keys) === JSON.stringify(expectedKeys);
let observedUrlFingerprint = null;
if (argoConfigured && typeof argoSecret.data?.url === 'string') {
try { observedUrlFingerprint = fingerprint(Buffer.from(argoSecret.data.url, 'base64').toString('utf8')); } catch {}
}
const expectedUrlFingerprint = argoConfigured ? fingerprint(argoUrl) : null;
const urlReady = !argoConfigured || observedUrlFingerprint === expectedUrlFingerprint;
const passwordPresent = !argoConfigured || (typeof argoSecret.data?.password === 'string' && argoSecret.data.password.length > 0);
if (!argoExists) reasons.push('argocd-repository-secret-missing');
if (!argoLabelReady) reasons.push('argocd-repository-secret-label-drifted');
if (!keysReady) reasons.push('argocd-repository-secret-keys-drifted');
if (!urlReady) reasons.push('argocd-repository-secret-url-drifted');
if (!passwordPresent) reasons.push('argocd-repository-secret-password-missing');
process.stdout.write(JSON.stringify({
configured: runnerConfigured || argoConfigured,
ready: reasons.length === 0,
runner: {
configured: runnerConfigured,
serviceAccount: runnerName || null,
exists: saExists,
automountServiceAccountToken: sa.metadata?.name === runnerName ? sa.automountServiceAccountToken ?? null : null,
automountDisabled,
roleBinding: roleBindingName || null,
roleBindingExact,
},
argoRepository: {
configured: argoConfigured,
secretName: argoSecretName || null,
exists: argoExists,
labelReady: argoLabelReady,
expectedKeys,
observedKeys: keys,
keysReady,
expectedUrlFingerprint,
observedUrlFingerprint,
urlReady,
passwordPresent,
passwordDecoded: false,
},
reasons,
valuesPrinted: false,
}));
NODE
rm -f "$sa_file" "$role_binding_file" "$argo_secret_file"
}
status_action() {
crd=$(kubectl get crd repositories.pipelinesascode.tekton.dev -o name 2>/dev/null || true)
controller_ready=$(kubectl -n "$UNIDESK_PAC_RELEASE_NAMESPACE" get deploy "$UNIDESK_PAC_CONTROLLER_SERVICE_NAME" -o jsonpath='{.status.readyReplicas}/{.spec.replicas}' 2>/dev/null || echo "0/0")
repository_condition=$(condition_status "$UNIDESK_PAC_TARGET_NAMESPACE" repository "$UNIDESK_PAC_REPOSITORY_NAME")
prepare_admission_provenance_state
consumer_bootstrap=$(consumer_bootstrap_state)
bootstrap_ready=$(UNIDESK_PAC_BOOTSTRAP_STATE="$consumer_bootstrap" node -e 'const s=JSON.parse(process.env.UNIDESK_PAC_BOOTSTRAP_STATE||"{}"); process.stdout.write(s.configured===true&&s.ready!==true?"false":"true")')
pipelines=$(pipeline_rows)
latest=$(printf '%s' "$pipelines" | node -e 'const fs=require("fs"); const a=JSON.parse(fs.readFileSync(0,"utf8")||"[]"); process.stdout.write(a[0]?.name||"")')
export UNIDESK_PAC_TARGET_PIPELINERUN="$latest"
@@ -1316,9 +1480,10 @@ NODE
runtime=$(json_normalize "$(runtime_summary)")
diagnostics=$(cicd_diagnostics "$pipelines" "$artifact" "$argo" "$runtime")
admission_ready=$(UNIDESK_PAC_STATE="$UNIDESK_PAC_ADMISSION_STATE_JSON" node -e 'const s=JSON.parse(process.env.UNIDESK_PAC_STATE||"{}"); process.stdout.write(s.ready===true?"true":"false")')
diagnostics=$(UNIDESK_PAC_DIAGNOSTICS="$diagnostics" UNIDESK_PAC_STATE="$UNIDESK_PAC_ADMISSION_STATE_JSON" node <<'NODE'
diagnostics=$(UNIDESK_PAC_DIAGNOSTICS="$diagnostics" UNIDESK_PAC_STATE="$UNIDESK_PAC_ADMISSION_STATE_JSON" UNIDESK_PAC_BOOTSTRAP_STATE="$consumer_bootstrap" node <<'NODE'
const diagnostics = JSON.parse(process.env.UNIDESK_PAC_DIAGNOSTICS || '{}');
const admissionProvenance = JSON.parse(process.env.UNIDESK_PAC_STATE || '{}');
const consumerBootstrap = JSON.parse(process.env.UNIDESK_PAC_BOOTSTRAP_STATE || '{}');
if (process.env.UNIDESK_PAC_DELIVERY_PROVENANCE_REQUIRED === '1' && admissionProvenance.ready !== true) {
process.stdout.write(JSON.stringify({
...diagnostics,
@@ -1329,16 +1494,28 @@ if (process.env.UNIDESK_PAC_DELIVERY_PROVENANCE_REQUIRED === '1' && admissionPro
admissionProvenance,
valuesPrinted: false,
}));
} else if (consumerBootstrap.configured === true && consumerBootstrap.ready !== true) {
process.stdout.write(JSON.stringify({
...diagnostics,
ok: false,
code: 'pac-consumer-bootstrap-not-ready',
phase: 'consumer-bootstrap-not-ready',
hint: 'declared runner ServiceAccount/RoleBinding or Argo repository credential is missing or drifted',
admissionProvenance,
consumerBootstrap,
valuesPrinted: false,
}));
} else {
process.stdout.write(JSON.stringify({ ...diagnostics, admissionProvenance, valuesPrinted: false }));
process.stdout.write(JSON.stringify({ ...diagnostics, admissionProvenance, consumerBootstrap, valuesPrinted: false }));
}
NODE
)
printf '{"ok":%s,"crdPresent":%s,"controllerReady":"%s","admissionProvenance":%s,"repository":{"name":"%s","repo":"%s/%s","url":"%s","condition":"%s"},"repositoryCondition":"%s","webhooks":%s,"pipelineRuns":%s,"taskRuns":%s,"artifact":%s,"argo":%s,"runtime":%s,"diagnostics":%s,"valuesPrinted":false}\n' \
"$( [ -n "$crd" ] && [ "$controller_ready" != "0/0" ] && { [ "${UNIDESK_PAC_DELIVERY_PROVENANCE_REQUIRED:-0}" != "1" ] || [ "$admission_ready" = "true" ]; } && echo true || echo false )" \
printf '{"ok":%s,"crdPresent":%s,"controllerReady":"%s","admissionProvenance":%s,"consumerBootstrap":%s,"repository":{"name":"%s","repo":"%s/%s","url":"%s","condition":"%s"},"repositoryCondition":"%s","webhooks":%s,"pipelineRuns":%s,"taskRuns":%s,"artifact":%s,"argo":%s,"runtime":%s,"diagnostics":%s,"valuesPrinted":false}\n' \
"$( [ -n "$crd" ] && [ "$controller_ready" != "0/0" ] && [ "$bootstrap_ready" = "true" ] && { [ "${UNIDESK_PAC_DELIVERY_PROVENANCE_REQUIRED:-0}" != "1" ] || [ "$admission_ready" = "true" ]; } && echo true || echo false )" \
"$( [ -n "$crd" ] && echo true || echo false )" \
"$(json_string "$controller_ready")" \
"$UNIDESK_PAC_ADMISSION_STATE_JSON" \
"$consumer_bootstrap" \
"$(json_string "$UNIDESK_PAC_REPOSITORY_NAME")" \
"$(json_string "$UNIDESK_PAC_GITEA_OWNER")" \
"$(json_string "$UNIDESK_PAC_GITEA_REPO")" \