refactor: Enhance health check target queries to use FQDN for hostname resolution and improve group health check logic in the database layer
Build, Test, and Push CFDM Docker Image / test (push) Successful in 3m40s
Build, Test, and Push CFDM Docker Image / build-and-push (push) Successful in 1m57s
Build, Test, and Push CFDM Docker Image / update-wiki (push) Successful in 6s
Build, Test, and Push CFDM Docker Image / create-release (push) Has been skipped
Build, Test, and Push CFDM Docker Image / test (push) Successful in 3m40s
Build, Test, and Push CFDM Docker Image / build-and-push (push) Successful in 1m57s
Build, Test, and Push CFDM Docker Image / update-wiki (push) Successful in 6s
Build, Test, and Push CFDM Docker Image / create-release (push) Has been skipped
This commit is contained in:
Vendored
+18
-6
@@ -1847,6 +1847,7 @@ async function createGroup2(db, cf, body) {
|
|||||||
async function updateGroup2(db, cf, id, body) {
|
async function updateGroup2(db, cf, id, body) {
|
||||||
const groupType = body.type?.trim() || "custom";
|
const groupType = body.type?.trim() || "custom";
|
||||||
const previous = repos6.getServiceGroup(db, id);
|
const previous = repos6.getServiceGroup(db, id);
|
||||||
|
const name = body.name ?? previous.name;
|
||||||
const oldDomain = previous.domain?.trim();
|
const oldDomain = previous.domain?.trim();
|
||||||
if (oldDomain) {
|
if (oldDomain) {
|
||||||
await cleanupStaleGroupFqdnBindings(db, cf, id, oldDomain);
|
await cleanupStaleGroupFqdnBindings(db, cf, id, oldDomain);
|
||||||
@@ -1856,7 +1857,7 @@ async function updateGroup2(db, cf, id, body) {
|
|||||||
let group = repos6.updateServiceGroup(
|
let group = repos6.updateServiceGroup(
|
||||||
db,
|
db,
|
||||||
id,
|
id,
|
||||||
body.name,
|
name,
|
||||||
groupType,
|
groupType,
|
||||||
body.icon ?? null,
|
body.icon ?? null,
|
||||||
domain,
|
domain,
|
||||||
@@ -2562,6 +2563,7 @@ import { healthStatusQuerySchema } from "@cfdm/shared";
|
|||||||
|
|
||||||
// src/services/health-check-service.ts
|
// src/services/health-check-service.ts
|
||||||
import { connect as connect2 } from "net";
|
import { connect as connect2 } from "net";
|
||||||
|
import { Agent, fetch as undiciFetch } from "undici";
|
||||||
import { repos as repos10 } from "@cfdm/db";
|
import { repos as repos10 } from "@cfdm/db";
|
||||||
function tcpProbe(ip, port, timeoutMs) {
|
function tcpProbe(ip, port, timeoutMs) {
|
||||||
return new Promise((resolve4) => {
|
return new Promise((resolve4) => {
|
||||||
@@ -2603,14 +2605,24 @@ function tcpProbe(ip, port, timeoutMs) {
|
|||||||
async function httpProbe(ip, target, timeoutMs) {
|
async function httpProbe(ip, target, timeoutMs) {
|
||||||
const started = Date.now();
|
const started = Date.now();
|
||||||
const path = target.path?.trim() || "/";
|
const path = target.path?.trim() || "/";
|
||||||
const url = `http://${ip}${path.startsWith("/") ? path : `/${path}`}`;
|
const pathWithSlash = path.startsWith("/") ? path : `/${path}`;
|
||||||
const hostHeader = target.hostname || ip;
|
const port = target.port ?? 80;
|
||||||
|
const useTls = port === 443;
|
||||||
|
const urlHost = useTls ? target.hostname || ip : ip;
|
||||||
|
const url = `${useTls ? "https" : "http"}://${urlHost}${pathWithSlash}`;
|
||||||
|
const dispatcher = useTls && target.hostname ? new Agent({
|
||||||
|
connect: {
|
||||||
|
servername: target.hostname,
|
||||||
|
rejectUnauthorized: false
|
||||||
|
}
|
||||||
|
}) : void 0;
|
||||||
try {
|
try {
|
||||||
const response = await fetch(url, {
|
const response = await undiciFetch(url, {
|
||||||
method: "GET",
|
method: "GET",
|
||||||
headers: { Host: hostHeader },
|
headers: { Host: target.hostname || ip },
|
||||||
signal: AbortSignal.timeout(timeoutMs),
|
signal: AbortSignal.timeout(timeoutMs),
|
||||||
redirect: "manual"
|
redirect: "manual",
|
||||||
|
dispatcher
|
||||||
});
|
});
|
||||||
const latency = Date.now() - started;
|
const latency = Date.now() - started;
|
||||||
if (target.expected_status != null) {
|
if (target.expected_status != null) {
|
||||||
|
|||||||
Vendored
+9
-4
@@ -1126,9 +1126,10 @@ function deleteIpHealthStatusForIp(db, scope, refId, ip) {
|
|||||||
).run();
|
).run();
|
||||||
}
|
}
|
||||||
function listHealthCheckTargets(db) {
|
function listHealthCheckTargets(db) {
|
||||||
|
const fqdnExpr = sql2`CASE WHEN sb.hostname = '@' OR sb.hostname IS NULL THEN d.zone_name ELSE sb.hostname || '.' || d.zone_name END`;
|
||||||
const bindingTargets = db.all(sql2`
|
const bindingTargets = db.all(sql2`
|
||||||
SELECT 'binding' AS scope, sb.id AS ref_id, sbi.ip,
|
SELECT 'binding' AS scope, sb.id AS ref_id, sbi.ip,
|
||||||
sb.hostname AS hostname,
|
${fqdnExpr} AS hostname,
|
||||||
sb.health_check_type AS type,
|
sb.health_check_type AS type,
|
||||||
sb.health_check_port AS port,
|
sb.health_check_port AS port,
|
||||||
sb.health_check_path AS path,
|
sb.health_check_path AS path,
|
||||||
@@ -1136,6 +1137,7 @@ function listHealthCheckTargets(db) {
|
|||||||
sb.health_check_timeout_ms AS timeout_ms
|
sb.health_check_timeout_ms AS timeout_ms
|
||||||
FROM service_binding_ips sbi
|
FROM service_binding_ips sbi
|
||||||
JOIN service_bindings sb ON sb.id = sbi.binding_id
|
JOIN service_bindings sb ON sb.id = sbi.binding_id
|
||||||
|
JOIN domains d ON d.id = sb.domain_id
|
||||||
WHERE sb.health_check_enabled = 1
|
WHERE sb.health_check_enabled = 1
|
||||||
`);
|
`);
|
||||||
const groupTargets = db.all(sql2`
|
const groupTargets = db.all(sql2`
|
||||||
@@ -1156,7 +1158,7 @@ function listHealthCheckTargets(db) {
|
|||||||
`);
|
`);
|
||||||
const groupInheritedBindingTargets = db.all(sql2`
|
const groupInheritedBindingTargets = db.all(sql2`
|
||||||
SELECT 'binding' AS scope, sb.id AS ref_id, sbi.ip,
|
SELECT 'binding' AS scope, sb.id AS ref_id, sbi.ip,
|
||||||
sb.hostname AS hostname,
|
${fqdnExpr} AS hostname,
|
||||||
sg.health_check_type AS type,
|
sg.health_check_type AS type,
|
||||||
sg.health_check_port AS port,
|
sg.health_check_port AS port,
|
||||||
sg.health_check_path AS path,
|
sg.health_check_path AS path,
|
||||||
@@ -1164,6 +1166,7 @@ function listHealthCheckTargets(db) {
|
|||||||
sg.health_check_timeout_ms AS timeout_ms
|
sg.health_check_timeout_ms AS timeout_ms
|
||||||
FROM service_binding_ips sbi
|
FROM service_binding_ips sbi
|
||||||
JOIN service_bindings sb ON sb.id = sbi.binding_id
|
JOIN service_bindings sb ON sb.id = sbi.binding_id
|
||||||
|
JOIN domains d ON d.id = sb.domain_id
|
||||||
JOIN services s ON s.id = sb.service_id
|
JOIN services s ON s.id = sb.service_id
|
||||||
JOIN service_groups sg ON sg.id = s.service_group_id
|
JOIN service_groups sg ON sg.id = s.service_group_id
|
||||||
WHERE sg.health_check_enabled = 1
|
WHERE sg.health_check_enabled = 1
|
||||||
@@ -1174,13 +1177,14 @@ function listHealthCheckTargets(db) {
|
|||||||
`);
|
`);
|
||||||
const cnameBindingTargets = db.all(sql2`
|
const cnameBindingTargets = db.all(sql2`
|
||||||
SELECT 'binding' AS scope, sb.id AS ref_id, sb.cname_target AS ip,
|
SELECT 'binding' AS scope, sb.id AS ref_id, sb.cname_target AS ip,
|
||||||
sb.hostname AS hostname,
|
${fqdnExpr} AS hostname,
|
||||||
sb.health_check_type AS type,
|
sb.health_check_type AS type,
|
||||||
sb.health_check_port AS port,
|
sb.health_check_port AS port,
|
||||||
sb.health_check_path AS path,
|
sb.health_check_path AS path,
|
||||||
sb.health_check_expected_status AS expected_status,
|
sb.health_check_expected_status AS expected_status,
|
||||||
sb.health_check_timeout_ms AS timeout_ms
|
sb.health_check_timeout_ms AS timeout_ms
|
||||||
FROM service_bindings sb
|
FROM service_bindings sb
|
||||||
|
JOIN domains d ON d.id = sb.domain_id
|
||||||
JOIN services s ON s.id = sb.service_id
|
JOIN services s ON s.id = sb.service_id
|
||||||
WHERE sb.health_check_enabled = 1
|
WHERE sb.health_check_enabled = 1
|
||||||
AND sb.cname_target IS NOT NULL
|
AND sb.cname_target IS NOT NULL
|
||||||
@@ -1189,13 +1193,14 @@ function listHealthCheckTargets(db) {
|
|||||||
`);
|
`);
|
||||||
const groupInheritedCnameBindingTargets = db.all(sql2`
|
const groupInheritedCnameBindingTargets = db.all(sql2`
|
||||||
SELECT 'binding' AS scope, sb.id AS ref_id, sb.cname_target AS ip,
|
SELECT 'binding' AS scope, sb.id AS ref_id, sb.cname_target AS ip,
|
||||||
sb.hostname AS hostname,
|
${fqdnExpr} AS hostname,
|
||||||
sg.health_check_type AS type,
|
sg.health_check_type AS type,
|
||||||
sg.health_check_port AS port,
|
sg.health_check_port AS port,
|
||||||
sg.health_check_path AS path,
|
sg.health_check_path AS path,
|
||||||
sg.health_check_expected_status AS expected_status,
|
sg.health_check_expected_status AS expected_status,
|
||||||
sg.health_check_timeout_ms AS timeout_ms
|
sg.health_check_timeout_ms AS timeout_ms
|
||||||
FROM service_bindings sb
|
FROM service_bindings sb
|
||||||
|
JOIN domains d ON d.id = sb.domain_id
|
||||||
JOIN services s ON s.id = sb.service_id
|
JOIN services s ON s.id = sb.service_id
|
||||||
JOIN service_groups sg ON sg.id = s.service_group_id
|
JOIN service_groups sg ON sg.id = s.service_group_id
|
||||||
WHERE sg.health_check_enabled = 1
|
WHERE sg.health_check_enabled = 1
|
||||||
|
|||||||
@@ -1486,9 +1486,14 @@ export function deleteIpHealthStatusForIp(
|
|||||||
// --- Health Check Targets ---
|
// --- Health Check Targets ---
|
||||||
|
|
||||||
export function listHealthCheckTargets(db: Db): HealthCheckTarget[] {
|
export function listHealthCheckTargets(db: Db): HealthCheckTarget[] {
|
||||||
|
// FQDN for a binding: "@" => zone_name, else "<hostname>.<zone_name>".
|
||||||
|
// Used as SNI / Host header for HTTP(S) probes — the raw `sb.hostname` is just the record name
|
||||||
|
// (e.g. "de" or "@"), which would break TLS SNI (ssl alert 112 "unrecognized name").
|
||||||
|
const fqdnExpr = sql`CASE WHEN sb.hostname = '@' OR sb.hostname IS NULL THEN d.zone_name ELSE sb.hostname || '.' || d.zone_name END`;
|
||||||
|
|
||||||
const bindingTargets = db.all<HealthCheckTarget>(sql`
|
const bindingTargets = db.all<HealthCheckTarget>(sql`
|
||||||
SELECT 'binding' AS scope, sb.id AS ref_id, sbi.ip,
|
SELECT 'binding' AS scope, sb.id AS ref_id, sbi.ip,
|
||||||
sb.hostname AS hostname,
|
${fqdnExpr} AS hostname,
|
||||||
sb.health_check_type AS type,
|
sb.health_check_type AS type,
|
||||||
sb.health_check_port AS port,
|
sb.health_check_port AS port,
|
||||||
sb.health_check_path AS path,
|
sb.health_check_path AS path,
|
||||||
@@ -1496,6 +1501,7 @@ export function listHealthCheckTargets(db: Db): HealthCheckTarget[] {
|
|||||||
sb.health_check_timeout_ms AS timeout_ms
|
sb.health_check_timeout_ms AS timeout_ms
|
||||||
FROM service_binding_ips sbi
|
FROM service_binding_ips sbi
|
||||||
JOIN service_bindings sb ON sb.id = sbi.binding_id
|
JOIN service_bindings sb ON sb.id = sbi.binding_id
|
||||||
|
JOIN domains d ON d.id = sb.domain_id
|
||||||
WHERE sb.health_check_enabled = 1
|
WHERE sb.health_check_enabled = 1
|
||||||
`);
|
`);
|
||||||
|
|
||||||
@@ -1522,7 +1528,7 @@ export function listHealthCheckTargets(db: Db): HealthCheckTarget[] {
|
|||||||
// their own health_check_enabled=1 (covered by bindingTargets above).
|
// their own health_check_enabled=1 (covered by bindingTargets above).
|
||||||
const groupInheritedBindingTargets = db.all<HealthCheckTarget>(sql`
|
const groupInheritedBindingTargets = db.all<HealthCheckTarget>(sql`
|
||||||
SELECT 'binding' AS scope, sb.id AS ref_id, sbi.ip,
|
SELECT 'binding' AS scope, sb.id AS ref_id, sbi.ip,
|
||||||
sb.hostname AS hostname,
|
${fqdnExpr} AS hostname,
|
||||||
sg.health_check_type AS type,
|
sg.health_check_type AS type,
|
||||||
sg.health_check_port AS port,
|
sg.health_check_port AS port,
|
||||||
sg.health_check_path AS path,
|
sg.health_check_path AS path,
|
||||||
@@ -1530,6 +1536,7 @@ export function listHealthCheckTargets(db: Db): HealthCheckTarget[] {
|
|||||||
sg.health_check_timeout_ms AS timeout_ms
|
sg.health_check_timeout_ms AS timeout_ms
|
||||||
FROM service_binding_ips sbi
|
FROM service_binding_ips sbi
|
||||||
JOIN service_bindings sb ON sb.id = sbi.binding_id
|
JOIN service_bindings sb ON sb.id = sbi.binding_id
|
||||||
|
JOIN domains d ON d.id = sb.domain_id
|
||||||
JOIN services s ON s.id = sb.service_id
|
JOIN services s ON s.id = sb.service_id
|
||||||
JOIN service_groups sg ON sg.id = s.service_group_id
|
JOIN service_groups sg ON sg.id = s.service_group_id
|
||||||
WHERE sg.health_check_enabled = 1
|
WHERE sg.health_check_enabled = 1
|
||||||
@@ -1542,13 +1549,14 @@ export function listHealthCheckTargets(db: Db): HealthCheckTarget[] {
|
|||||||
// CNAME-bindings with their own health_check_enabled: probe the CNAME target host.
|
// CNAME-bindings with their own health_check_enabled: probe the CNAME target host.
|
||||||
const cnameBindingTargets = db.all<HealthCheckTarget>(sql`
|
const cnameBindingTargets = db.all<HealthCheckTarget>(sql`
|
||||||
SELECT 'binding' AS scope, sb.id AS ref_id, sb.cname_target AS ip,
|
SELECT 'binding' AS scope, sb.id AS ref_id, sb.cname_target AS ip,
|
||||||
sb.hostname AS hostname,
|
${fqdnExpr} AS hostname,
|
||||||
sb.health_check_type AS type,
|
sb.health_check_type AS type,
|
||||||
sb.health_check_port AS port,
|
sb.health_check_port AS port,
|
||||||
sb.health_check_path AS path,
|
sb.health_check_path AS path,
|
||||||
sb.health_check_expected_status AS expected_status,
|
sb.health_check_expected_status AS expected_status,
|
||||||
sb.health_check_timeout_ms AS timeout_ms
|
sb.health_check_timeout_ms AS timeout_ms
|
||||||
FROM service_bindings sb
|
FROM service_bindings sb
|
||||||
|
JOIN domains d ON d.id = sb.domain_id
|
||||||
JOIN services s ON s.id = sb.service_id
|
JOIN services s ON s.id = sb.service_id
|
||||||
WHERE sb.health_check_enabled = 1
|
WHERE sb.health_check_enabled = 1
|
||||||
AND sb.cname_target IS NOT NULL
|
AND sb.cname_target IS NOT NULL
|
||||||
@@ -1560,13 +1568,14 @@ export function listHealthCheckTargets(db: Db): HealthCheckTarget[] {
|
|||||||
// (inherit group config). Only for bindings without their own health_check_enabled.
|
// (inherit group config). Only for bindings without their own health_check_enabled.
|
||||||
const groupInheritedCnameBindingTargets = db.all<HealthCheckTarget>(sql`
|
const groupInheritedCnameBindingTargets = db.all<HealthCheckTarget>(sql`
|
||||||
SELECT 'binding' AS scope, sb.id AS ref_id, sb.cname_target AS ip,
|
SELECT 'binding' AS scope, sb.id AS ref_id, sb.cname_target AS ip,
|
||||||
sb.hostname AS hostname,
|
${fqdnExpr} AS hostname,
|
||||||
sg.health_check_type AS type,
|
sg.health_check_type AS type,
|
||||||
sg.health_check_port AS port,
|
sg.health_check_port AS port,
|
||||||
sg.health_check_path AS path,
|
sg.health_check_path AS path,
|
||||||
sg.health_check_expected_status AS expected_status,
|
sg.health_check_expected_status AS expected_status,
|
||||||
sg.health_check_timeout_ms AS timeout_ms
|
sg.health_check_timeout_ms AS timeout_ms
|
||||||
FROM service_bindings sb
|
FROM service_bindings sb
|
||||||
|
JOIN domains d ON d.id = sb.domain_id
|
||||||
JOIN services s ON s.id = sb.service_id
|
JOIN services s ON s.id = sb.service_id
|
||||||
JOIN service_groups sg ON sg.id = s.service_group_id
|
JOIN service_groups sg ON sg.id = s.service_group_id
|
||||||
WHERE sg.health_check_enabled = 1
|
WHERE sg.health_check_enabled = 1
|
||||||
|
|||||||
Reference in New Issue
Block a user