fix(platform-infra): fast-path converged k3s apply
This commit is contained in:
@@ -29,6 +29,12 @@
|
||||
`platform-infra k3s-cluster plan|apply|status|smoke|registry-smoke
|
||||
--target d601`。`apply` 先收敛 host proxy,再比较 server 声明摘要;
|
||||
内容一致时不得重装或重启。
|
||||
- `apply` 必须先用一次目标侧短查询比较:
|
||||
- server 与 host proxy service 状态;
|
||||
- 独立集群声明摘要;
|
||||
- Node、系统 Deployment 和业务 Pod 状态。
|
||||
- 全部一致时直接返回 `mode=already-converged` 与 `mutation=false`,
|
||||
不分发 proxy/k3s 工件,也不进入长控制通道。
|
||||
- `status` 和 `smoke` 必须证明:
|
||||
- 只有一个 Ready 节点;
|
||||
- server active 且 agent inactive;
|
||||
|
||||
@@ -153,8 +153,10 @@ export interface HostProxyGuestBundle {
|
||||
prepareScript: string;
|
||||
applyScript: string;
|
||||
statusScript: string;
|
||||
serviceName: string;
|
||||
sourceFingerprint: string;
|
||||
materialFingerprint: string;
|
||||
desiredFingerprint: string;
|
||||
valuesPrinted: false;
|
||||
}
|
||||
|
||||
@@ -203,8 +205,10 @@ export function prepareHostProxyGuestBundle(targetId: string): HostProxyGuestBun
|
||||
prepareScript: remotePrepareScript(target.source.client),
|
||||
applyScript: remoteApplyScript(target, material.clientConfigJson),
|
||||
statusScript: remoteStatusScript(target),
|
||||
serviceName: target.source.client.serviceName,
|
||||
sourceFingerprint: target.source.fingerprint,
|
||||
materialFingerprint: material.fingerprint,
|
||||
desiredFingerprint: fingerprint({ target, materialFingerprint: material.fingerprint }),
|
||||
valuesPrinted: false,
|
||||
};
|
||||
}
|
||||
|
||||
@@ -149,9 +149,29 @@ function registryCredentialInit(target: K3sClusterTarget, confirm: boolean): Rec
|
||||
function apply(target: K3sClusterTarget): Record<string, unknown> {
|
||||
const secret = readRegistrySecret(target);
|
||||
const proxyBundle = prepareHostProxyGuestBundle(target.node.hostProxyTargetId);
|
||||
const desired = standaloneDesired(target, secret, proxyBundle.desiredFingerprint);
|
||||
const before = runNode(
|
||||
target,
|
||||
standaloneConvergenceScript(target, desired.desiredHash, proxyBundle.serviceName),
|
||||
false,
|
||||
60_000,
|
||||
);
|
||||
const beforeData = parseFirstJson(before.stdout);
|
||||
if (before.exitCode === 0 && beforeData.ok === true) {
|
||||
return {
|
||||
ok: true,
|
||||
mutation: false,
|
||||
mode: "already-converged",
|
||||
targetId: target.id,
|
||||
topology: "standalone-single-node",
|
||||
status: beforeData,
|
||||
result: { convergence: compact(before) },
|
||||
valuesPrinted: false,
|
||||
};
|
||||
}
|
||||
const proxy = runNodeHostProxy(target, proxyBundle, 240_000);
|
||||
requireSuccess(proxy, "standalone-host-proxy-apply");
|
||||
const server = runNode(target, standaloneApplyScript(target, secret, proxyBundle.materialFingerprint), true, 300_000);
|
||||
const server = runNode(target, standaloneApplyScript(target, secret, desired), true, 300_000);
|
||||
requireSuccess(server, "standalone-k3s-apply");
|
||||
const converged = waitForStatus(target, 180_000);
|
||||
requireSuccess(converged, "standalone-k3s-status");
|
||||
@@ -163,7 +183,12 @@ function apply(target: K3sClusterTarget): Record<string, unknown> {
|
||||
targetId: target.id,
|
||||
topology: "standalone-single-node",
|
||||
status: data,
|
||||
result: { hostProxy: compact(proxy), server: compact(server), status: compact(converged) },
|
||||
result: {
|
||||
convergenceBefore: compact(before),
|
||||
hostProxy: compact(proxy),
|
||||
server: compact(server),
|
||||
status: compact(converged),
|
||||
},
|
||||
valuesPrinted: false,
|
||||
};
|
||||
}
|
||||
@@ -231,7 +256,11 @@ function registrySmoke(target: K3sClusterTarget, confirm: boolean): Record<strin
|
||||
};
|
||||
}
|
||||
|
||||
function standaloneApplyScript(target: K3sClusterTarget, secret: RegistrySecret, proxyFingerprint: string): string {
|
||||
function standaloneDesired(
|
||||
target: K3sClusterTarget,
|
||||
secret: RegistrySecret,
|
||||
proxyFingerprint: string,
|
||||
): { exec: string; desiredHash: string } {
|
||||
const disableArgs = target.k3s.disableComponents.flatMap((component) => ["--disable", component]);
|
||||
const labels = Object.entries(target.node.labels).flatMap(([key, value]) => ["--node-label", `${key}=${value}`]);
|
||||
const exec = [
|
||||
@@ -251,6 +280,15 @@ function standaloneApplyScript(target: K3sClusterTarget, secret: RegistrySecret,
|
||||
registryPassword: secret.password,
|
||||
proxyFingerprint,
|
||||
}));
|
||||
return { exec, desiredHash };
|
||||
}
|
||||
|
||||
function standaloneApplyScript(
|
||||
target: K3sClusterTarget,
|
||||
secret: RegistrySecret,
|
||||
desired: { exec: string; desiredHash: string },
|
||||
): string {
|
||||
const { exec, desiredHash } = desired;
|
||||
return `set -eu
|
||||
desired_hash=${shQuote(desiredHash)}
|
||||
desired_hash_file=/etc/unidesk/k3s-standalone-desired.sha256
|
||||
@@ -290,6 +328,50 @@ systemctl start --no-block k3s
|
||||
`;
|
||||
}
|
||||
|
||||
function standaloneConvergenceScript(
|
||||
target: K3sClusterTarget,
|
||||
desiredHash: string,
|
||||
proxyServiceName: string,
|
||||
): string {
|
||||
return `set -eu
|
||||
active=false; systemctl is-active --quiet k3s && active=true
|
||||
agent_active=false; systemctl is-active --quiet k3s-agent && agent_active=true
|
||||
proxy_active=false; systemctl is-active --quiet ${shellToken(proxyServiceName)} && proxy_active=true
|
||||
desired_current=false; [ "$(cat /etc/unidesk/k3s-standalone-desired.sha256 2>/dev/null || true)" = ${shQuote(desiredHash)} ] && desired_current=true
|
||||
ready="$(k3s kubectl get node ${shQuote(target.node.nodeName)} -o jsonpath='{.status.conditions[?(@.type=="Ready")].status}' 2>/dev/null || true)"
|
||||
node_count="$(k3s kubectl get nodes --no-headers 2>/dev/null | wc -l | tr -d ' ')"
|
||||
business_pods="$(k3s kubectl get pods -A --no-headers 2>/dev/null | awk '$1 != "kube-system" {count++} END {print count+0}')"
|
||||
deployments="$(k3s kubectl get deployments -A --no-headers 2>/dev/null | awk '$1 == "kube-system" {count++} END {print count+0}')"
|
||||
ready_deployments="$(k3s kubectl get deployments -A --no-headers 2>/dev/null | awk '$1 == "kube-system" {split($3, ready, "/"); if (ready[1] == ready[2] && ready[2] != "0" && $5 == ready[2]) count++} END {print count+0}')"
|
||||
python3 - "$active" "$agent_active" "$proxy_active" "$desired_current" "$ready" "$node_count" "$business_pods" "$deployments" "$ready_deployments" <<'PY'
|
||||
import json, sys
|
||||
active, agent, proxy, desired, ready, nodes, business, deployments, ready_deployments = sys.argv[1:]
|
||||
ok = (
|
||||
active == "true"
|
||||
and agent == "false"
|
||||
and proxy == "true"
|
||||
and desired == "true"
|
||||
and ready == "True"
|
||||
and nodes == "1"
|
||||
and business == "0"
|
||||
and deployments == ready_deployments
|
||||
)
|
||||
print(json.dumps({
|
||||
"ok": ok,
|
||||
"serverActive": active == "true",
|
||||
"oldAgentActive": agent == "true",
|
||||
"hostProxyActive": proxy == "true",
|
||||
"desiredHashCurrent": desired == "true",
|
||||
"nodeReady": ready == "True",
|
||||
"nodeCount": int(nodes or 0),
|
||||
"businessPodCount": int(business or 0),
|
||||
"systemDeploymentCount": int(deployments or 0),
|
||||
"systemReadyDeploymentCount": int(ready_deployments or 0),
|
||||
}, separators=(",", ":")))
|
||||
PY
|
||||
`;
|
||||
}
|
||||
|
||||
function standaloneStatusScript(target: K3sClusterTarget): string {
|
||||
return `set -eu
|
||||
active=false; systemctl is-active --quiet k3s && active=true
|
||||
|
||||
Reference in New Issue
Block a user