diff --git a/server/src/domain/checks/check.docker.aggregation.ts b/server/src/domain/checks/check.docker.aggregation.ts index 37d6d8e87..225d9a68c 100644 --- a/server/src/domain/checks/check.docker.aggregation.ts +++ b/server/src/domain/checks/check.docker.aggregation.ts @@ -1,5 +1,6 @@ import CheckModel from "@/domain/checks/check.model.js"; import { DockerContainerStatsBucket, DockerStatsBucket } from "@/domain/checks/check.type.js"; +import { EXCLUDE_DEGRADED_EGRESS_MATCH } from "@/domain/checks/check.query.js"; import mongoose from "mongoose"; type DateRange = { start: Date; end: Date }; @@ -9,6 +10,7 @@ export const getDockerTotalChecks = async (monitorId: string, dates: DateRange): "metadata.monitorId": new mongoose.Types.ObjectId(monitorId), "metadata.type": "docker", createdAt: { $gte: dates.start, $lte: dates.end }, + ...EXCLUDE_DEGRADED_EGRESS_MATCH, }); export const getDockerUpChecks = async (monitorId: string, dates: DateRange): Promise<{ totalChecks: number }> => { @@ -16,6 +18,7 @@ export const getDockerUpChecks = async (monitorId: string, dates: DateRange): Pr "metadata.monitorId": new mongoose.Types.ObjectId(monitorId), "metadata.type": "docker", createdAt: { $gte: dates.start, $lte: dates.end }, + ...EXCLUDE_DEGRADED_EGRESS_MATCH, status: true, }); return { totalChecks: count }; @@ -28,6 +31,7 @@ export const getDockerStats = async (monitorId: string, dates: DateRange, dateSt "metadata.monitorId": new mongoose.Types.ObjectId(monitorId), "metadata.type": "docker", createdAt: { $gte: dates.start, $lte: dates.end }, + ...EXCLUDE_DEGRADED_EGRESS_MATCH, }, }, { @@ -48,6 +52,7 @@ export const getDockerLatestCheck = async (monitorId: string) => CheckModel.findOne({ "metadata.monitorId": new mongoose.Types.ObjectId(monitorId), "metadata.type": "docker", + ...EXCLUDE_DEGRADED_EGRESS_MATCH, }) .sort({ createdAt: -1 }) .select("containers containerSummary createdAt") @@ -65,6 +70,7 @@ export const getDockerContainerStats = async ( "metadata.monitorId": new mongoose.Types.ObjectId(monitorId), "metadata.type": "docker", createdAt: { $gte: dates.start, $lte: dates.end }, + ...EXCLUDE_DEGRADED_EGRESS_MATCH, "containers.name": containerName, }, }, @@ -88,6 +94,7 @@ export const getDockerContainerLatestCheck = async (monitorId: string, container "metadata.monitorId": new mongoose.Types.ObjectId(monitorId), "metadata.type": "docker", "containers.name": containerName, + ...EXCLUDE_DEGRADED_EGRESS_MATCH, }) .sort({ createdAt: -1 }) .select("containers createdAt") diff --git a/server/src/domain/checks/check.hardware.aggregations.ts b/server/src/domain/checks/check.hardware.aggregations.ts index 6c672b630..3e0ee2bd7 100644 --- a/server/src/domain/checks/check.hardware.aggregations.ts +++ b/server/src/domain/checks/check.hardware.aggregations.ts @@ -1,6 +1,7 @@ import { CheckModel } from "@/domain/checks/check.model.js"; import mongoose from "mongoose"; import type { HardwareDiskStats, HardwareNetStats } from "@/domain/checks/check.type.js"; +import { EXCLUDE_DEGRADED_EGRESS_MATCH } from "@/domain/checks/check.query.js"; type DateRange = { start: Date; end: Date }; type HardwareUpChecks = { totalChecks: number }; @@ -70,6 +71,7 @@ export const getHardwareTotalChecks = async (monitorId: string, dates: DateRange "metadata.monitorId": new mongoose.Types.ObjectId(monitorId), "metadata.type": "hardware", createdAt: { $gte: dates.start, $lte: dates.end }, + ...EXCLUDE_DEGRADED_EGRESS_MATCH, }); }; @@ -78,6 +80,7 @@ export const getHardwareUpChecks = async (monitorId: string, dates: DateRange): "metadata.monitorId": new mongoose.Types.ObjectId(monitorId), "metadata.type": "hardware", createdAt: { $gte: dates.start, $lte: dates.end }, + ...EXCLUDE_DEGRADED_EGRESS_MATCH, status: true, }); return { totalChecks: count }; @@ -95,6 +98,7 @@ export const getHardwareStats = async (monitorId: string, dates: DateRange, date "metadata.monitorId": new mongoose.Types.ObjectId(monitorId), "metadata.type": "hardware", createdAt: { $gte: dates.start, $lte: dates.end }, + ...EXCLUDE_DEGRADED_EGRESS_MATCH, }, }, { $sort: { createdAt: 1 } }, diff --git a/server/test/integration/checksRepositoryEgressExclusion.test.ts b/server/test/integration/checksRepositoryEgressExclusion.test.ts index 0f0950928..f7ac18daa 100644 --- a/server/test/integration/checksRepositoryEgressExclusion.test.ts +++ b/server/test/integration/checksRepositoryEgressExclusion.test.ts @@ -3,7 +3,13 @@ import mongoose from "mongoose"; import { MongoMemoryServer } from "mongodb-memory-server"; import MongoChecksRepository from "../../src/domain/checks/check.repository.mongo.ts"; import { CheckModel } from "../../src/domain/checks/check.model.ts"; -import type { Check, DockerChecksResult, PageSpeedChecksResult, UptimeChecksResult } from "../../src/domain/checks/check.type.ts"; +import type { + Check, + DockerChecksResult, + HardwareChecksResult, + PageSpeedChecksResult, + UptimeChecksResult, +} from "../../src/domain/checks/check.type.ts"; import { NETWORK_ERROR } from "../../src/types/network.ts"; import type { ILogger } from "../../src/utils/logger.ts"; import { createMockLogger } from "../helpers/createMockLogger.ts"; @@ -181,18 +187,25 @@ describe("MongoChecksRepository degraded-egress exclusion", () => { expect(resolve.checks.map((check) => check.egressStatus).sort()).toEqual([undefined, "degraded"].sort()); }); - it("leaves the docker aggregates alone, since a docker check is never attributed to egress", async () => { + it("excludes degraded checks from the docker and hardware aggregates", async () => { const dockerMonitor = new mongoose.Types.ObjectId(); + const hardwareMonitor = new mongoose.Types.ObjectId(); const dockerMeta = { monitorId: dockerMonitor, teamId: TEAM_ID, type: "docker" }; + const hardwareMeta = { monitorId: hardwareMonitor, teamId: TEAM_ID, type: "hardware" }; - // The schema accepts the field on any type, so this pins the scope decision rather than the schema: - // the producer never sets it for docker or hardware, so their aggregations must not filter on it. + // A Capture agent is reached by an outbound HTTP request and a docker daemon can be reached over TCP, + // so both are egress attributable and their checks can carry the flag. await seedCheck({ metadata: dockerMeta }); await seedDegradedFailure({ metadata: dockerMeta }); + await seedCheck({ metadata: hardwareMeta }); + await seedDegradedFailure({ metadata: hardwareMeta }); const docker = (await repo.findByDateRangeAndMonitorId(dockerMonitor.toString(), "day", { type: "docker" })) as DockerChecksResult; + const hardware = (await repo.findByDateRangeAndMonitorId(hardwareMonitor.toString(), "day", { type: "hardware" })) as HardwareChecksResult; - expect(docker.aggregateData.totalChecks).toBe(2); - expect(docker.aggregate[0]).toMatchObject({ upCount: 1, totalCount: 2 }); + expect(docker.aggregateData.totalChecks).toBe(1); + expect(docker.aggregate[0]).toMatchObject({ upCount: 1, totalCount: 1 }); + expect(hardware.aggregateData.totalChecks).toBe(1); + expect(hardware.upChecks.totalChecks).toBe(1); }); });