feat: 接入 selfmedia 受控交付
This commit is contained in:
@@ -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")" \
|
||||
|
||||
Reference in New Issue
Block a user