fix(code-queue): add issue 3 gateway diagnostics
This commit is contained in:
@@ -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",
|
||||
|
||||
Reference in New Issue
Block a user