Merge pull request #1442 from pikasTech/fix/1422-health-probe-budget
fix: 放宽 v03 cloud-api 健康探针预算
This commit is contained in:
@@ -374,6 +374,26 @@
|
|||||||
},
|
},
|
||||||
"additionalProperties": false
|
"additionalProperties": false
|
||||||
},
|
},
|
||||||
|
"probeTiming": {
|
||||||
|
"type": "object",
|
||||||
|
"properties": {
|
||||||
|
"initialDelaySeconds": { "type": "integer", "minimum": 0 },
|
||||||
|
"periodSeconds": { "type": "integer", "minimum": 1 },
|
||||||
|
"timeoutSeconds": { "type": "integer", "minimum": 1 },
|
||||||
|
"failureThreshold": { "type": "integer", "minimum": 1 },
|
||||||
|
"successThreshold": { "type": "integer", "minimum": 1 }
|
||||||
|
},
|
||||||
|
"additionalProperties": false
|
||||||
|
},
|
||||||
|
"serviceHealthProbe": {
|
||||||
|
"type": "object",
|
||||||
|
"properties": {
|
||||||
|
"readiness": { "$ref": "#/$defs/probeTiming" },
|
||||||
|
"liveness": { "$ref": "#/$defs/probeTiming" },
|
||||||
|
"startup": { "$ref": "#/$defs/probeTiming" }
|
||||||
|
},
|
||||||
|
"additionalProperties": false
|
||||||
|
},
|
||||||
"serviceDeclaration": {
|
"serviceDeclaration": {
|
||||||
"type": "object",
|
"type": "object",
|
||||||
"required": ["runtimeKind", "entrypoint", "componentPaths"],
|
"required": ["runtimeKind", "entrypoint", "componentPaths"],
|
||||||
@@ -383,6 +403,7 @@
|
|||||||
"artifactKind": { "type": "string", "enum": ["bun-command", "cloud-web", "skills-bundle", "node-command", "go-service"] },
|
"artifactKind": { "type": "string", "enum": ["bun-command", "cloud-web", "skills-bundle", "node-command", "go-service"] },
|
||||||
"healthPath": { "type": "string", "pattern": "^/" },
|
"healthPath": { "type": "string", "pattern": "^/" },
|
||||||
"healthPort": { "type": "integer", "minimum": 1, "maximum": 65535 },
|
"healthPort": { "type": "integer", "minimum": 1, "maximum": 65535 },
|
||||||
|
"healthProbe": { "$ref": "#/$defs/serviceHealthProbe" },
|
||||||
"componentPaths": {
|
"componentPaths": {
|
||||||
"type": "array",
|
"type": "array",
|
||||||
"minItems": 1,
|
"minItems": 1,
|
||||||
|
|||||||
@@ -277,6 +277,16 @@ lanes:
|
|||||||
artifactKind: bun-command
|
artifactKind: bun-command
|
||||||
healthPath: /health/live
|
healthPath: /health/live
|
||||||
healthPort: 6667
|
healthPort: 6667
|
||||||
|
healthProbe:
|
||||||
|
readiness:
|
||||||
|
timeoutSeconds: 3
|
||||||
|
failureThreshold: 5
|
||||||
|
liveness:
|
||||||
|
periodSeconds: 20
|
||||||
|
timeoutSeconds: 5
|
||||||
|
failureThreshold: 6
|
||||||
|
startup:
|
||||||
|
timeoutSeconds: 3
|
||||||
componentPaths:
|
componentPaths:
|
||||||
- cmd/hwlab-cloud-api/
|
- cmd/hwlab-cloud-api/
|
||||||
- cmd/hwlab-codex-api-responses-forwarder/
|
- cmd/hwlab-codex-api-responses-forwarder/
|
||||||
|
|||||||
@@ -527,6 +527,27 @@ function applyEnvReuseStartupProbe(container) {
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function applyServiceHealthProbe(container, healthProbe) {
|
||||||
|
if (!healthProbe || typeof healthProbe !== "object" || Array.isArray(healthProbe)) return;
|
||||||
|
for (const [probeName, key] of [["readinessProbe", "readiness"], ["livenessProbe", "liveness"], ["startupProbe", "startup"]]) {
|
||||||
|
const probe = container?.[probeName];
|
||||||
|
const timing = normalizeProbeTiming(healthProbe[key]);
|
||||||
|
if (!probe || Object.keys(timing).length === 0) continue;
|
||||||
|
Object.assign(probe, timing);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function normalizeProbeTiming(value) {
|
||||||
|
if (!value || typeof value !== "object" || Array.isArray(value)) return {};
|
||||||
|
const result = {};
|
||||||
|
for (const field of ["initialDelaySeconds", "periodSeconds", "timeoutSeconds", "failureThreshold", "successThreshold"]) {
|
||||||
|
const raw = value[field];
|
||||||
|
const minimum = field === "initialDelaySeconds" ? 0 : 1;
|
||||||
|
if (Number.isInteger(raw) && raw >= minimum) result[field] = raw;
|
||||||
|
}
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
function useLocalHealthProbe(container, pathValue = "/health") {
|
function useLocalHealthProbe(container, pathValue = "/health") {
|
||||||
if (!container) return;
|
if (!container) return;
|
||||||
for (const probeName of ["readinessProbe", "livenessProbe"]) {
|
for (const probeName of ["readinessProbe", "livenessProbe"]) {
|
||||||
@@ -1129,6 +1150,9 @@ function transformWorkloads({ workloads, deploy, catalog, source, sourceBranch =
|
|||||||
});
|
});
|
||||||
if (container.name === serviceId) annotateEnvReusePodTemplate(podTemplate.metadata, bootMetadata);
|
if (container.name === serviceId) annotateEnvReusePodTemplate(podTemplate.metadata, bootMetadata);
|
||||||
}
|
}
|
||||||
|
if (runtimeLane && container.name === serviceId) {
|
||||||
|
applyServiceHealthProbe(container, deploy?.lanes?.[profile]?.serviceDeclarations?.[serviceId]?.healthProbe);
|
||||||
|
}
|
||||||
if (serviceId === "hwlab-cloud-api" || serviceId === "hwlab-edge-proxy") {
|
if (serviceId === "hwlab-cloud-api" || serviceId === "hwlab-edge-proxy") {
|
||||||
upsertEnv(container.env, "HWLAB_PUBLIC_ENDPOINT", runtimeEndpoint);
|
upsertEnv(container.env, "HWLAB_PUBLIC_ENDPOINT", runtimeEndpoint);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -201,6 +201,7 @@ export async function createCiPlan(options = {}) {
|
|||||||
artifactKind: model.artifactKind,
|
artifactKind: model.artifactKind,
|
||||||
healthPath: model.healthPath,
|
healthPath: model.healthPath,
|
||||||
healthPort: model.healthPort,
|
healthPort: model.healthPort,
|
||||||
|
healthProbe: model.healthProbe,
|
||||||
componentPaths: model.componentPaths,
|
componentPaths: model.componentPaths,
|
||||||
sharedPaths: model.sharedPaths,
|
sharedPaths: model.sharedPaths,
|
||||||
runtimeDeps: model.runtimeDeps,
|
runtimeDeps: model.runtimeDeps,
|
||||||
@@ -306,6 +307,7 @@ export function componentModelsForServices(serviceIds, laneConfig = null, lane =
|
|||||||
artifactKind: declaration.artifactKind,
|
artifactKind: declaration.artifactKind,
|
||||||
healthPath: declaration.healthPath,
|
healthPath: declaration.healthPath,
|
||||||
healthPort: declaration.healthPort,
|
healthPort: declaration.healthPort,
|
||||||
|
healthProbe: declaration.healthProbe,
|
||||||
env: declaration.env,
|
env: declaration.env,
|
||||||
observable: declaration.observable,
|
observable: declaration.observable,
|
||||||
componentPaths: uniqueSorted(declaration.componentPaths.map(normalizeRepoPath)),
|
componentPaths: uniqueSorted(declaration.componentPaths.map(normalizeRepoPath)),
|
||||||
@@ -338,6 +340,7 @@ function serviceDeclarationFor(laneConfig, serviceId, lane) {
|
|||||||
artifactKind,
|
artifactKind,
|
||||||
healthPath: normalizeDeclarationString(configured?.healthPath) || "/health/live",
|
healthPath: normalizeDeclarationString(configured?.healthPath) || "/health/live",
|
||||||
healthPort: Number.isInteger(configured?.healthPort) ? configured.healthPort : null,
|
healthPort: Number.isInteger(configured?.healthPort) ? configured.healthPort : null,
|
||||||
|
healthProbe: normalizeServiceHealthProbe(configured?.healthProbe),
|
||||||
componentPaths: configured.componentPaths,
|
componentPaths: configured.componentPaths,
|
||||||
env: normalizeServiceEnv(configured?.env),
|
env: normalizeServiceEnv(configured?.env),
|
||||||
observable: configured?.observable === true
|
observable: configured?.observable === true
|
||||||
@@ -349,6 +352,27 @@ function normalizeServiceEnv(value) {
|
|||||||
return Object.fromEntries(Object.entries(value).map(([key, raw]) => [String(key), raw == null ? null : String(raw)]).sort(([left], [right]) => left.localeCompare(right)));
|
return Object.fromEntries(Object.entries(value).map(([key, raw]) => [String(key), raw == null ? null : String(raw)]).sort(([left], [right]) => left.localeCompare(right)));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function normalizeServiceHealthProbe(value) {
|
||||||
|
if (!value || typeof value !== "object" || Array.isArray(value)) return {};
|
||||||
|
const result = {};
|
||||||
|
for (const key of ["readiness", "liveness", "startup"]) {
|
||||||
|
const timing = normalizeProbeTiming(value[key]);
|
||||||
|
if (Object.keys(timing).length > 0) result[key] = timing;
|
||||||
|
}
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
function normalizeProbeTiming(value) {
|
||||||
|
if (!value || typeof value !== "object" || Array.isArray(value)) return {};
|
||||||
|
const result = {};
|
||||||
|
for (const field of ["initialDelaySeconds", "periodSeconds", "timeoutSeconds", "failureThreshold", "successThreshold"]) {
|
||||||
|
const raw = value[field];
|
||||||
|
const minimum = field === "initialDelaySeconds" ? 0 : 1;
|
||||||
|
if (Number.isInteger(raw) && raw >= minimum) result[field] = raw;
|
||||||
|
}
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
function requireDeclarationString(value, lane, label) {
|
function requireDeclarationString(value, lane, label) {
|
||||||
const text = normalizeDeclarationString(value);
|
const text = normalizeDeclarationString(value);
|
||||||
if (!text) throw new Error(`deploy.lanes.${lane}.${label} is required`);
|
if (!text) throw new Error(`deploy.lanes.${lane}.${label} is required`);
|
||||||
@@ -571,6 +595,7 @@ function serviceRuntimeConfigIdentity(deployJson, lane, serviceId) {
|
|||||||
entrypoint: normalizeRepoPath(declaration.entrypoint),
|
entrypoint: normalizeRepoPath(declaration.entrypoint),
|
||||||
healthPath: normalizeDeclarationString(declaration.healthPath),
|
healthPath: normalizeDeclarationString(declaration.healthPath),
|
||||||
healthPort: Number.isInteger(declaration.healthPort) ? declaration.healthPort : null,
|
healthPort: Number.isInteger(declaration.healthPort) ? declaration.healthPort : null,
|
||||||
|
healthProbe: normalizeServiceHealthProbe(declaration.healthProbe),
|
||||||
env: normalizeServiceEnv(declaration.env),
|
env: normalizeServiceEnv(declaration.env),
|
||||||
observable: declaration.observable === true
|
observable: declaration.observable === true
|
||||||
};
|
};
|
||||||
|
|||||||
Reference in New Issue
Block a user