quality / commitlint (push) Skipped
CD / update-wiki (push) Successful in 8s
quality / changes (push) Successful in 5s
quality / docker-check (push) Skipped
quality / web (push) Successful in 51s
quality / api (push) Successful in 43s
CD / quality (push) Successful in 1m43s
CD / publish (push) Successful in 1m38s
- Added functionality to toggle individual IPs for services, allowing for dynamic management of IP health status. - Enhanced the health check configuration in the UI, enabling users to set parameters directly from the settings page. - Updated service views to include IP health tracking, improving visibility into the status of each IP associated with a service. - Refactored relevant components to support the new IP toggling feature, ensuring a seamless user experience. This commit significantly enhances the health management capabilities of services, providing users with more control over IP configurations and health monitoring.
163 lines
4.7 KiB
TypeScript
163 lines
4.7 KiB
TypeScript
import { describe, expect, it } from "vitest";
|
|
import type { ServiceBindingView } from "@cfdm/shared";
|
|
import { resolveBindingIpsForSync } from "../src/services/vps-tracker-sync.js";
|
|
|
|
function binding(
|
|
partial: Partial<ServiceBindingView> &
|
|
Pick<ServiceBindingView, "id" | "hostname" | "zone_name">,
|
|
): ServiceBindingView {
|
|
return {
|
|
domain_id: 1,
|
|
service_id: 1,
|
|
dns_record_id: null,
|
|
group_id: null,
|
|
group_name: null,
|
|
service_name: "svc",
|
|
service_slug: "svc",
|
|
target_ip: null,
|
|
target_ips: [],
|
|
target_ip_weights: {},
|
|
target_ip_priorities: {},
|
|
cname_target: null,
|
|
lb_mode: "off",
|
|
health_check_enabled: false,
|
|
health_check_type: "tcp",
|
|
health_check_port: null,
|
|
health_check_path: null,
|
|
health_check_expected_status: null,
|
|
health_check_interval_sec: 60,
|
|
health_check_timeout_ms: 3000,
|
|
health_check_verify_tls: true,
|
|
sync_status: null,
|
|
created_at: "",
|
|
updated_at: "",
|
|
...partial,
|
|
};
|
|
}
|
|
|
|
describe("resolveBindingIpsForSync", () => {
|
|
it("uses A-record IPs when present", async () => {
|
|
const a = binding({
|
|
id: 1,
|
|
hostname: "ihome",
|
|
zone_name: "rkns.top",
|
|
target_ips: ["10.0.0.5"],
|
|
});
|
|
const index = { byFqdn: new Map([["ihome.rkns.top", a]]) };
|
|
const ips = await resolveBindingIpsForSync(a, ["9.9.9.9"], index);
|
|
expect(ips).toEqual(["10.0.0.5"]);
|
|
});
|
|
|
|
it("resolves CNAME via local binding chain to VPS IP", async () => {
|
|
const target = binding({
|
|
id: 1,
|
|
hostname: "ihome",
|
|
zone_name: "rkns.top",
|
|
target_ips: ["203.0.113.10"],
|
|
});
|
|
const cname = binding({
|
|
id: 2,
|
|
hostname: "imsk",
|
|
zone_name: "rkns.top",
|
|
cname_target: "ihome.rkns.top",
|
|
target_ips: [],
|
|
});
|
|
const index = {
|
|
byFqdn: new Map([
|
|
["ihome.rkns.top", target],
|
|
["imsk.rkns.top", cname],
|
|
]),
|
|
};
|
|
const ips = await resolveBindingIpsForSync(cname, [], index);
|
|
expect(ips).toEqual(["203.0.113.10"]);
|
|
});
|
|
|
|
it("resolves short CNAME target relative to zone", async () => {
|
|
const target = binding({
|
|
id: 1,
|
|
hostname: "ihome",
|
|
zone_name: "rkns.top",
|
|
target_ips: ["203.0.113.11"],
|
|
});
|
|
const cname = binding({
|
|
id: 2,
|
|
hostname: "imsk",
|
|
zone_name: "rkns.top",
|
|
cname_target: "ihome",
|
|
target_ips: [],
|
|
});
|
|
const index = {
|
|
byFqdn: new Map([
|
|
["ihome.rkns.top", target],
|
|
["imsk.rkns.top", cname],
|
|
]),
|
|
};
|
|
const ips = await resolveBindingIpsForSync(cname, [], index);
|
|
expect(ips).toEqual(["203.0.113.11"]);
|
|
});
|
|
|
|
it("falls back to service IPs when CNAME target unknown locally and DNS fails", async () => {
|
|
const cname = binding({
|
|
id: 2,
|
|
hostname: "imsk",
|
|
zone_name: "rkns.top",
|
|
cname_target: "definitely-not-resolvable-xyz.invalid",
|
|
target_ips: [],
|
|
});
|
|
const index = { byFqdn: new Map([["imsk.rkns.top", cname]]) };
|
|
const ips = await resolveBindingIpsForSync(cname, ["198.51.100.7"], index);
|
|
expect(ips).toEqual(["198.51.100.7"]);
|
|
});
|
|
|
|
it("does not treat CNAME hostname in target_ips as an IP", async () => {
|
|
const target = binding({
|
|
id: 1,
|
|
hostname: "ihome",
|
|
zone_name: "rkns.top",
|
|
target_ips: ["203.0.113.10"],
|
|
});
|
|
const cname = binding({
|
|
id: 2,
|
|
hostname: "imsk",
|
|
zone_name: "rkns.top",
|
|
cname_target: "ihome.rkns.top",
|
|
// как в прод: JOIN dns_record кладёт CNAME content в target_ip → target_ips
|
|
target_ips: ["ihome.rkns.top"],
|
|
});
|
|
const index = {
|
|
byFqdn: new Map([
|
|
["ihome.rkns.top", target],
|
|
["imsk.rkns.top", cname],
|
|
]),
|
|
};
|
|
const ips = await resolveBindingIpsForSync(cname, [], index);
|
|
expect(ips).toEqual(["203.0.113.10"]);
|
|
});
|
|
|
|
it("treats missing target_ips as empty instead of throwing", async () => {
|
|
const cname = binding({
|
|
id: 2,
|
|
hostname: "imsk",
|
|
zone_name: "rkns.top",
|
|
cname_target: "ihome.rkns.top",
|
|
});
|
|
delete (cname as { target_ips?: string[] }).target_ips;
|
|
const index = { byFqdn: new Map([["imsk.rkns.top", cname]]) };
|
|
const ips = await resolveBindingIpsForSync(cname, ["198.51.100.9"], index);
|
|
expect(ips).toEqual(["198.51.100.9"]);
|
|
});
|
|
|
|
it("prefers service IPs over empty CNAME resolution chain", async () => {
|
|
const cname = binding({
|
|
id: 2,
|
|
hostname: "mhome",
|
|
zone_name: "rkns.top",
|
|
cname_target: "macloud.rkns.top",
|
|
target_ips: ["macloud.rkns.top"],
|
|
});
|
|
const index = { byFqdn: new Map([["mhome.rkns.top", cname]]) };
|
|
const ips = await resolveBindingIpsForSync(cname, ["203.0.113.55"], index);
|
|
expect(ips).toEqual(["203.0.113.55"]);
|
|
});
|
|
});
|