fix: use k8s api inside branch follower
This commit is contained in:
+84
-8
@@ -1095,8 +1095,8 @@ function readNativeObjectBundle(registry: BranchFollowerRegistry, follower: Foll
|
|||||||
const argo = native.argo;
|
const argo = native.argo;
|
||||||
const runtime = native.runtime;
|
const runtime = native.runtime;
|
||||||
const workloadCommands = (runtime?.workloads ?? []).map((workload, index) => {
|
const workloadCommands = (runtime?.workloads ?? []).map((workload, index) => {
|
||||||
const resource = workload.kind === "Deployment" ? "deployment" : "statefulset";
|
const resource = workload.kind === "Deployment" ? "deployments" : "statefulsets";
|
||||||
return `emit_json ${shQuote(`workload${index}`)} kubectl -n ${shQuote(runtime?.namespace ?? follower.target.namespace)} get ${resource} ${shQuote(workload.name)} -o json`;
|
return `emit_kube_json ${shQuote(`workload${index}`)} ${shQuote(`/apis/apps/v1/namespaces/${runtime?.namespace ?? follower.target.namespace}/${resource}/${workload.name}`)}`;
|
||||||
});
|
});
|
||||||
const script = [
|
const script = [
|
||||||
"set +e",
|
"set +e",
|
||||||
@@ -1155,13 +1155,34 @@ function readNativeObjectBundle(registry: BranchFollowerRegistry, follower: Foll
|
|||||||
"}",
|
"}",
|
||||||
"console.log(JSON.stringify(output));",
|
"console.log(JSON.stringify(output));",
|
||||||
"NODE_COMPACT",
|
"NODE_COMPACT",
|
||||||
"emit_json() {",
|
"cat >\"$tmpdir/kube-get.mjs\" <<'NODE_KUBE_GET'",
|
||||||
|
"import { readFileSync } from 'node:fs';",
|
||||||
|
"import https from 'node:https';",
|
||||||
|
"const path = process.argv[2];",
|
||||||
|
"const host = process.env.KUBERNETES_SERVICE_HOST;",
|
||||||
|
"const port = Number(process.env.KUBERNETES_SERVICE_PORT || '443');",
|
||||||
|
"const token = readFileSync('/var/run/secrets/kubernetes.io/serviceaccount/token', 'utf8').trim();",
|
||||||
|
"const ca = readFileSync('/var/run/secrets/kubernetes.io/serviceaccount/ca.crt');",
|
||||||
|
"const req = https.request({ host, port, path, method: 'GET', ca, headers: { authorization: `Bearer ${token}` } }, (res) => {",
|
||||||
|
" let body = '';",
|
||||||
|
" res.setEncoding('utf8');",
|
||||||
|
" res.on('data', (chunk) => { body += chunk; });",
|
||||||
|
" res.on('end', () => {",
|
||||||
|
" if ((res.statusCode || 0) >= 200 && (res.statusCode || 0) < 300) { process.stdout.write(body); process.exit(0); }",
|
||||||
|
" process.stderr.write(body || `kube api status ${res.statusCode}`);",
|
||||||
|
" process.exit(1);",
|
||||||
|
" });",
|
||||||
|
"});",
|
||||||
|
"req.on('error', (error) => { process.stderr.write(error?.message || String(error)); process.exit(1); });",
|
||||||
|
"req.end();",
|
||||||
|
"NODE_KUBE_GET",
|
||||||
|
"emit_kube_json() {",
|
||||||
" key=\"$1\"",
|
" key=\"$1\"",
|
||||||
" shift",
|
" path=\"$2\"",
|
||||||
" raw=\"$tmpdir/$key.raw\"",
|
" raw=\"$tmpdir/$key.raw\"",
|
||||||
" out=\"$tmpdir/$key.out\"",
|
" out=\"$tmpdir/$key.out\"",
|
||||||
" err=\"$tmpdir/$key.err\"",
|
" err=\"$tmpdir/$key.err\"",
|
||||||
" if \"$@\" >\"$raw\" 2>\"$err\" && node \"$tmpdir/compact-native-object.mjs\" \"$key\" <\"$raw\" >\"$out\" 2>>\"$err\"; then",
|
" if node \"$tmpdir/kube-get.mjs\" \"$path\" >\"$raw\" 2>\"$err\" && node \"$tmpdir/compact-native-object.mjs\" \"$key\" <\"$raw\" >\"$out\" 2>>\"$err\"; then",
|
||||||
" printf 'UNIDESK_NATIVE_JSON\\t%s\\t' \"$key\"",
|
" printf 'UNIDESK_NATIVE_JSON\\t%s\\t' \"$key\"",
|
||||||
" base64 \"$out\" | tr -d '\\n'",
|
" base64 \"$out\" | tr -d '\\n'",
|
||||||
" printf '\\n'",
|
" printf '\\n'",
|
||||||
@@ -1186,7 +1207,7 @@ function readNativeObjectBundle(registry: BranchFollowerRegistry, follower: Foll
|
|||||||
"if [ -d \"$repo_path/objects\" ]; then",
|
"if [ -d \"$repo_path/objects\" ]; then",
|
||||||
" source_commit=$(git --git-dir=\"$repo_path\" rev-parse --verify \"refs/heads/$branch^{commit}\" 2>>\"$source_err\" | head -n 1 | tr -d '\\r' || true)",
|
" source_commit=$(git --git-dir=\"$repo_path\" rev-parse --verify \"refs/heads/$branch^{commit}\" 2>>\"$source_err\" | head -n 1 | tr -d '\\r' || true)",
|
||||||
"else",
|
"else",
|
||||||
" source_commit=$(kubectl -n \"$mirror_ns\" exec \"deploy/$mirror_deploy\" -- env REPO_PATH=\"$repo_path\" BRANCH=\"$branch\" sh -lc 'git --git-dir=\"$REPO_PATH\" rev-parse --verify \"refs/heads/$BRANCH^{commit}\"' 2>>\"$source_err\" | head -n 1 | tr -d '\\r' || true)",
|
" printf 'formal controller/job must mount k8s git-mirror cache at %s; fallback exec is disabled\\n' \"$repo_path\" >>\"$source_err\"",
|
||||||
"fi",
|
"fi",
|
||||||
"case \"$source_commit\" in",
|
"case \"$source_commit\" in",
|
||||||
" [0-9a-f][0-9a-f][0-9a-f][0-9a-f][0-9a-f][0-9a-f][0-9a-f][0-9a-f][0-9a-f][0-9a-f][0-9a-f][0-9a-f][0-9a-f][0-9a-f][0-9a-f][0-9a-f][0-9a-f][0-9a-f][0-9a-f][0-9a-f][0-9a-f][0-9a-f][0-9a-f][0-9a-f][0-9a-f][0-9a-f][0-9a-f][0-9a-f][0-9a-f][0-9a-f][0-9a-f][0-9a-f][0-9a-f][0-9a-f][0-9a-f][0-9a-f][0-9a-f][0-9a-f][0-9a-f][0-9a-f])",
|
" [0-9a-f][0-9a-f][0-9a-f][0-9a-f][0-9a-f][0-9a-f][0-9a-f][0-9a-f][0-9a-f][0-9a-f][0-9a-f][0-9a-f][0-9a-f][0-9a-f][0-9a-f][0-9a-f][0-9a-f][0-9a-f][0-9a-f][0-9a-f][0-9a-f][0-9a-f][0-9a-f][0-9a-f][0-9a-f][0-9a-f][0-9a-f][0-9a-f][0-9a-f][0-9a-f][0-9a-f][0-9a-f][0-9a-f][0-9a-f][0-9a-f][0-9a-f][0-9a-f][0-9a-f][0-9a-f][0-9a-f])",
|
||||||
@@ -1205,12 +1226,12 @@ function readNativeObjectBundle(registry: BranchFollowerRegistry, follower: Foll
|
|||||||
: [
|
: [
|
||||||
"if [ -n \"$source_commit\" ]; then",
|
"if [ -n \"$source_commit\" ]; then",
|
||||||
" sha12=$(printf '%s' \"$source_commit\" | cut -c1-12)",
|
" sha12=$(printf '%s' \"$source_commit\" | cut -c1-12)",
|
||||||
` emit_json pipelineRun kubectl -n ${shQuote(tekton.namespace)} get pipelinerun ${shQuote(`${tekton.pipelineRunPrefix}-`)}"$sha12" -o json`,
|
` emit_kube_json pipelineRun ${shQuote(`/apis/tekton.dev/v1/namespaces/${tekton.namespace}/pipelineruns/${tekton.pipelineRunPrefix}-`)}"$sha12"`,
|
||||||
"fi",
|
"fi",
|
||||||
].join("\n"),
|
].join("\n"),
|
||||||
argo === null
|
argo === null
|
||||||
? "true"
|
? "true"
|
||||||
: `emit_json argoApplication kubectl -n ${shQuote(argo.namespace)} get application ${shQuote(argo.application)} -o json`,
|
: `emit_kube_json argoApplication ${shQuote(`/apis/argoproj.io/v1alpha1/namespaces/${argo.namespace}/applications/${argo.application}`)}`,
|
||||||
...workloadCommands,
|
...workloadCommands,
|
||||||
"exit 0",
|
"exit 0",
|
||||||
].join("\n");
|
].join("\n");
|
||||||
@@ -1533,6 +1554,61 @@ function runKubeScript(registry: BranchFollowerRegistry, options: ParsedOptions,
|
|||||||
function writeFollowerState(registry: BranchFollowerRegistry, state: FollowerState, options: ParsedOptions): CommandResult {
|
function writeFollowerState(registry: BranchFollowerRegistry, state: FollowerState, options: ParsedOptions): CommandResult {
|
||||||
const json = JSON.stringify(state);
|
const json = JSON.stringify(state);
|
||||||
const dataPatch = JSON.stringify({ data: { [state.id]: json, _updatedAt: new Date().toISOString(), _specRef: SPEC_REF } });
|
const dataPatch = JSON.stringify({ data: { [state.id]: json, _updatedAt: new Date().toISOString(), _specRef: SPEC_REF } });
|
||||||
|
if (options.controller) {
|
||||||
|
const patchBase64 = Buffer.from(dataPatch, "utf8").toString("base64");
|
||||||
|
const createBase64 = Buffer.from(JSON.stringify({
|
||||||
|
metadata: {
|
||||||
|
name: registry.controller.stateConfigMapName,
|
||||||
|
namespace: registry.controller.namespace,
|
||||||
|
},
|
||||||
|
data: {
|
||||||
|
_createdAt: new Date().toISOString(),
|
||||||
|
_specRef: SPEC_REF,
|
||||||
|
},
|
||||||
|
}), "utf8").toString("base64");
|
||||||
|
const script = [
|
||||||
|
"set -eu",
|
||||||
|
`PATCH_B64=${shQuote(patchBase64)}`,
|
||||||
|
`CREATE_B64=${shQuote(createBase64)}`,
|
||||||
|
`NAMESPACE=${shQuote(registry.controller.namespace)}`,
|
||||||
|
`CONFIGMAP=${shQuote(registry.controller.stateConfigMapName)}`,
|
||||||
|
"export PATCH_B64 CREATE_B64 NAMESPACE CONFIGMAP",
|
||||||
|
"node <<'NODE_KUBE_PATCH'",
|
||||||
|
"import { readFileSync } from 'node:fs';",
|
||||||
|
"import https from 'node:https';",
|
||||||
|
"const host = process.env.KUBERNETES_SERVICE_HOST;",
|
||||||
|
"const port = Number(process.env.KUBERNETES_SERVICE_PORT || '443');",
|
||||||
|
"const namespace = process.env.NAMESPACE;",
|
||||||
|
"const name = process.env.CONFIGMAP;",
|
||||||
|
"const token = readFileSync('/var/run/secrets/kubernetes.io/serviceaccount/token', 'utf8').trim();",
|
||||||
|
"const ca = readFileSync('/var/run/secrets/kubernetes.io/serviceaccount/ca.crt');",
|
||||||
|
"const patch = Buffer.from(process.env.PATCH_B64 || '', 'base64').toString('utf8');",
|
||||||
|
"const create = Buffer.from(process.env.CREATE_B64 || '', 'base64').toString('utf8');",
|
||||||
|
"function request(method, path, body, contentType) {",
|
||||||
|
" return new Promise((resolve, reject) => {",
|
||||||
|
" const req = https.request({ host, port, path, method, ca, headers: { authorization: `Bearer ${token}`, ...(body ? { 'content-type': contentType || 'application/json', 'content-length': Buffer.byteLength(body) } : {}) } }, (res) => {",
|
||||||
|
" let text = '';",
|
||||||
|
" res.setEncoding('utf8');",
|
||||||
|
" res.on('data', (chunk) => { text += chunk; });",
|
||||||
|
" res.on('end', () => resolve({ status: res.statusCode || 0, text }));",
|
||||||
|
" });",
|
||||||
|
" req.on('error', reject);",
|
||||||
|
" if (body) req.write(body);",
|
||||||
|
" req.end();",
|
||||||
|
" });",
|
||||||
|
"}",
|
||||||
|
"const base = `/api/v1/namespaces/${namespace}/configmaps`;",
|
||||||
|
"let result = await request('PATCH', `${base}/${name}`, patch, 'application/merge-patch+json');",
|
||||||
|
"if (result.status === 404) {",
|
||||||
|
" const created = await request('POST', base, create, 'application/json');",
|
||||||
|
" if (created.status < 200 || created.status >= 300) { process.stderr.write(created.text); process.exit(1); }",
|
||||||
|
" result = await request('PATCH', `${base}/${name}`, patch, 'application/merge-patch+json');",
|
||||||
|
"}",
|
||||||
|
"if (result.status < 200 || result.status >= 300) { process.stderr.write(result.text); process.exit(1); }",
|
||||||
|
"NODE_KUBE_PATCH",
|
||||||
|
].join("\n");
|
||||||
|
return runKubeScript(registry, options, script, "", 10_000);
|
||||||
|
}
|
||||||
const script = [
|
const script = [
|
||||||
"set -eu",
|
"set -eu",
|
||||||
`kubectl -n ${shQuote(registry.controller.namespace)} create configmap ${shQuote(registry.controller.stateConfigMapName)} --from-literal=_createdAt="$(date -Iseconds)" --dry-run=client -o yaml | kubectl apply -f - >/dev/null`,
|
`kubectl -n ${shQuote(registry.controller.namespace)} create configmap ${shQuote(registry.controller.stateConfigMapName)} --from-literal=_createdAt="$(date -Iseconds)" --dry-run=client -o yaml | kubectl apply -f - >/dev/null`,
|
||||||
|
|||||||
Reference in New Issue
Block a user