fix(code-queue): add issue 3 gateway diagnostics

This commit is contained in:
Codex
2026-05-19 03:32:16 +00:00
parent c00c695197
commit 9d46ca2531
18 changed files with 1494 additions and 39 deletions
@@ -16,7 +16,8 @@
"route": {
"kind": "kubernetes-service",
"serviceName": "code-queue-scheduler",
"servicePort": 4222
"servicePort": 4222,
"deploymentName": "code-queue"
},
"activeInstanceId": "D601",
"singleWriter": true,
@@ -127,7 +128,8 @@
"route": {
"kind": "kubernetes-service",
"serviceName": "code-queue-scheduler",
"servicePort": 4222
"servicePort": 4222,
"deploymentName": "code-queue"
},
"activeInstanceId": "D601-scheduler",
"singleWriter": true,
@@ -146,5 +148,81 @@
],
"requireAllInstancesHealthy": false
}
},
{
"apiVersion": "unidesk.ai/k3s/v1",
"kind": "ManagedKubernetesService",
"metadata": {
"name": "d601-provider-egress-proxy",
"namespace": "unidesk"
},
"spec": {
"adapterServiceId": "k3sctl-adapter",
"controlPlane": {
"type": "kubernetes",
"cluster": "unidesk-k3s",
"context": "unidesk-k3s"
},
"route": {
"kind": "kubernetes-service",
"serviceName": "d601-provider-egress-proxy",
"servicePort": 18789,
"deploymentName": "d601-provider-egress-proxy"
},
"activeInstanceId": "D601-provider-egress",
"singleWriter": false,
"expectedNodeIds": [
"D601"
],
"instances": [
{
"id": "D601-provider-egress",
"nodeId": "D601",
"role": "primary",
"baseUrl": "kubernetes://unidesk/services/d601-provider-egress-proxy:18789",
"healthPath": "/__unidesk/egress-proxy/health",
"healthMode": "pod-ready"
}
],
"requireAllInstancesHealthy": false
}
},
{
"apiVersion": "unidesk.ai/k3s/v1",
"kind": "ManagedKubernetesService",
"metadata": {
"name": "d601-tcp-egress-gateway",
"namespace": "unidesk"
},
"spec": {
"adapterServiceId": "k3sctl-adapter",
"controlPlane": {
"type": "kubernetes",
"cluster": "unidesk-k3s",
"context": "unidesk-k3s"
},
"route": {
"kind": "kubernetes-service",
"serviceName": "d601-tcp-egress-gateway",
"servicePort": 18080,
"deploymentName": "d601-tcp-egress-gateway"
},
"activeInstanceId": "D601-tcp-egress",
"singleWriter": false,
"expectedNodeIds": [
"D601"
],
"instances": [
{
"id": "D601-tcp-egress",
"nodeId": "D601",
"role": "primary",
"baseUrl": "kubernetes://unidesk/services/d601-tcp-egress-gateway:18080",
"healthPath": "/health",
"healthMode": "service-proxy"
}
],
"requireAllInstancesHealthy": false
}
}
]
@@ -430,6 +430,10 @@ function nativeServiceRef(service: ManagedService): { namespace: string; service
};
}
function deploymentName(service: ManagedService): string {
return routeString(service, "deploymentName", service.id);
}
function serviceProxyApiPath(service: ManagedService, targetPath: string): string {
const { serviceName, servicePort } = nativeServiceRef(service);
const safeTargetPath = targetPath.startsWith("/") ? targetPath : `/${targetPath}`;
@@ -922,6 +926,7 @@ async function probeEndpoint(endpoint: ManagedEndpoint): Promise<JsonRecord> {
async function probeKubernetesServiceActive(service: ManagedService): Promise<JsonRecord> {
const endpoint = activeEndpoint(service);
if (endpoint.healthMode === "pod-ready") return await probeKubernetesPodReady(service, endpoint);
return probeKubernetesEndpoint(service, endpoint, true);
}
@@ -995,6 +1000,68 @@ function podSummary(item: unknown): JsonRecord {
};
}
function conditionTrue(item: unknown, type: string): boolean {
const conditions = jsonAtPath(item, "status.conditions");
return Array.isArray(conditions) && conditions.some((condition) => {
const record = typeof condition === "object" && condition !== null ? condition as Record<string, unknown> : {};
return record.type === type && record.status === "True";
});
}
function integerAtPath(item: unknown, path: string): number {
const value = jsonAtPath(item, path);
return typeof value === "number" && Number.isFinite(value) ? Math.floor(value) : 0;
}
async function deploymentAvailabilityCheck(service: ManagedService): Promise<JsonRecord> {
const name = deploymentName(service);
const namespace = service.namespace;
try {
const deployment = await kubeApiJson(`/apis/apps/v1/namespaces/${encodeURIComponent(namespace)}/deployments/${encodeURIComponent(name)}`, config.healthTimeoutMs);
const desiredReplicas = Math.max(1, integerAtPath(deployment, "spec.replicas"));
const availableReplicas = integerAtPath(deployment, "status.availableReplicas");
const readyReplicas = integerAtPath(deployment, "status.readyReplicas");
const updatedReplicas = integerAtPath(deployment, "status.updatedReplicas");
const unavailableReplicas = integerAtPath(deployment, "status.unavailableReplicas");
const available = conditionTrue(deployment, "Available") || availableReplicas >= desiredReplicas;
return {
ok: available,
name,
namespace,
available,
desiredReplicas,
availableReplicas,
readyReplicas,
updatedReplicas,
unavailableReplicas,
};
} catch (error) {
return { ok: false, name, namespace, available: false, error: errorToJson(error) };
}
}
async function endpointNonEmptyCheck(service: ManagedService): Promise<JsonRecord> {
const { namespace, serviceName } = nativeServiceRef(service);
try {
const endpoints = await kubeApiJson(`/api/v1/namespaces/${encodeURIComponent(namespace)}/endpoints/${encodeURIComponent(serviceName)}`, config.healthTimeoutMs);
const subsets = Array.isArray(endpoints.subsets) ? endpoints.subsets : [];
const addresses = subsets.flatMap((subset) => {
const record = typeof subset === "object" && subset !== null ? subset as Record<string, unknown> : {};
return Array.isArray(record.addresses) ? record.addresses : [];
});
const readyAddressCount = addresses.length;
return {
ok: readyAddressCount > 0,
serviceName,
namespace,
readyAddressCount,
subsetCount: subsets.length,
};
} catch (error) {
return { ok: false, serviceName, namespace, readyAddressCount: 0, error: errorToJson(error) };
}
}
async function probeKubernetesPodReady(service: ManagedService, endpoint: ManagedEndpoint): Promise<JsonRecord> {
const checkedAt = new Date().toISOString();
const { namespace } = kubernetesEndpointServiceRef(service, endpoint);
@@ -1195,6 +1262,8 @@ async function serviceDiagnostics(service: ManagedService): Promise<JsonRecord>
status: "unhealthy",
error: errorToJson(error),
} satisfies JsonRecord));
const deployment = await deploymentAvailabilityCheck(service);
const endpoint = await endpointNonEmptyCheck(service);
const kubernetesApiServiceProxyOk = kubernetesApiServiceProxy.ok === true;
const targetServiceOk = targetService.ok === true;
const checks = {
@@ -1212,8 +1281,11 @@ async function serviceDiagnostics(service: ManagedService): Promise<JsonRecord>
},
targetService,
managedService,
deployment,
endpoint,
} satisfies Record<string, JsonValue>;
const ok = kubernetesApiServiceProxyOk && targetServiceOk;
const managedServiceOk = managedService.ok === true;
const ok = kubernetesApiServiceProxyOk && (targetServiceOk || managedServiceOk) && deployment.ok === true && endpoint.ok === true;
return {
ok,
service: "k3sctl-adapter",