mirror of
https://github.com/fosrl/pangolin.git
synced 2026-06-16 12:27:41 +00:00
Merge branch 'alerting-rules' into trial
This commit is contained in:
@@ -103,7 +103,8 @@ export async function listDomains(
|
||||
|
||||
const [{ count }] = await db
|
||||
.select({ count: sql<number>`count(*)` })
|
||||
.from(domains);
|
||||
.from(orgDomains)
|
||||
.where(eq(orgDomains.orgId, orgId));
|
||||
|
||||
return response<ListDomainsResponse>(res, {
|
||||
data: {
|
||||
|
||||
@@ -2,6 +2,9 @@ export type ListHealthChecksResponse = {
|
||||
healthChecks: {
|
||||
targetHealthCheckId: number;
|
||||
name: string;
|
||||
siteId: number | null;
|
||||
siteName: string | null;
|
||||
siteNiceId: string | null;
|
||||
hcEnabled: boolean;
|
||||
hcHealth: "unknown" | "healthy" | "unhealthy";
|
||||
hcMode: string | null;
|
||||
|
||||
@@ -86,7 +86,8 @@ export async function buildClientConfigurationForNewtClient(
|
||||
// )
|
||||
// );
|
||||
|
||||
if (!client.clientSitesAssociationsCache.isJitMode) { // if we are adding sites through jit then dont add the site to the olm
|
||||
if (!client.clientSitesAssociationsCache.isJitMode) {
|
||||
// if we are adding sites through jit then dont add the site to the olm
|
||||
// update the peer info on the olm
|
||||
// if the peer has not been added yet this will be a no-op
|
||||
await updatePeer(client.clients.clientId, {
|
||||
@@ -189,7 +190,10 @@ export async function buildClientConfigurationForNewtClient(
|
||||
};
|
||||
}
|
||||
|
||||
export async function buildTargetConfigurationForNewtClient(siteId: number) {
|
||||
export async function buildTargetConfigurationForNewtClient(
|
||||
siteId: number,
|
||||
version?: string | null
|
||||
) {
|
||||
// Get all enabled targets with their resource protocol information
|
||||
const allTargets = await db
|
||||
.select({
|
||||
@@ -200,8 +204,15 @@ export async function buildTargetConfigurationForNewtClient(siteId: number) {
|
||||
port: targets.port,
|
||||
internalPort: targets.internalPort,
|
||||
enabled: targets.enabled,
|
||||
protocol: resources.protocol,
|
||||
hcId: targetHealthCheck.targetHealthCheckId,
|
||||
protocol: resources.protocol
|
||||
})
|
||||
.from(targets)
|
||||
.innerJoin(resources, eq(targets.resourceId, resources.resourceId))
|
||||
.where(and(eq(targets.siteId, siteId), eq(targets.enabled, true)));
|
||||
|
||||
const allHealthChecks = await db
|
||||
.select({
|
||||
targetHealthCheckId: targetHealthCheck.targetHealthCheckId,
|
||||
hcEnabled: targetHealthCheck.hcEnabled,
|
||||
hcPath: targetHealthCheck.hcPath,
|
||||
hcScheme: targetHealthCheck.hcScheme,
|
||||
@@ -219,13 +230,8 @@ export async function buildTargetConfigurationForNewtClient(siteId: number) {
|
||||
hcHealthyThreshold: targetHealthCheck.hcHealthyThreshold,
|
||||
hcUnhealthyThreshold: targetHealthCheck.hcUnhealthyThreshold
|
||||
})
|
||||
.from(targets)
|
||||
.innerJoin(resources, eq(targets.resourceId, resources.resourceId))
|
||||
.leftJoin(
|
||||
targetHealthCheck,
|
||||
eq(targets.targetId, targetHealthCheck.targetId)
|
||||
)
|
||||
.where(and(eq(targets.siteId, siteId), eq(targets.enabled, true)));
|
||||
.from(targetHealthCheck)
|
||||
.where(eq(targetHealthCheck.siteId, siteId));
|
||||
|
||||
const { tcpTargets, udpTargets } = allTargets.reduce(
|
||||
(acc, target) => {
|
||||
@@ -249,7 +255,7 @@ export async function buildTargetConfigurationForNewtClient(siteId: number) {
|
||||
{ tcpTargets: [] as string[], udpTargets: [] as string[] }
|
||||
);
|
||||
|
||||
const healthCheckTargets = allTargets.map((target) => {
|
||||
const healthCheckTargets = allHealthChecks.map((target) => {
|
||||
// make sure the stuff is defined
|
||||
const isTCP = target.hcMode?.toLowerCase() === "tcp";
|
||||
if (!target.hcHostname || !target.hcPort || !target.hcInterval) {
|
||||
@@ -273,8 +279,7 @@ export async function buildTargetConfigurationForNewtClient(siteId: number) {
|
||||
}
|
||||
|
||||
return {
|
||||
id: target.targetId,
|
||||
hcId: target.hcId,
|
||||
id: target.targetHealthCheckId,
|
||||
hcEnabled: target.hcEnabled,
|
||||
hcPath: target.hcPath,
|
||||
hcScheme: target.hcScheme,
|
||||
|
||||
@@ -192,7 +192,7 @@ export const handleNewtRegisterMessage: MessageHandler = async (context) => {
|
||||
}
|
||||
|
||||
const { tcpTargets, udpTargets, validHealthCheckTargets } =
|
||||
await buildTargetConfigurationForNewtClient(siteId);
|
||||
await buildTargetConfigurationForNewtClient(siteId, newtVersion);
|
||||
|
||||
logger.debug(
|
||||
`Sending health check targets to newt ${newt.newtId}: ${JSON.stringify(validHealthCheckTargets)}`
|
||||
|
||||
@@ -83,8 +83,7 @@ export async function addTargets(
|
||||
}
|
||||
|
||||
return {
|
||||
id: target.targetId,
|
||||
hcId: hc.targetHealthCheckId,
|
||||
id: hc.targetHealthCheckId,
|
||||
hcEnabled: hc.hcEnabled,
|
||||
hcPath: hc.hcPath,
|
||||
hcScheme: hc.hcScheme,
|
||||
@@ -121,6 +120,96 @@ export async function addTargets(
|
||||
);
|
||||
}
|
||||
|
||||
export async function addStandaloneHealthCheck(
|
||||
newtId: string,
|
||||
healthCheck: TargetHealthCheck,
|
||||
version?: string | null
|
||||
) {
|
||||
const isTCP = healthCheck.hcMode?.toLowerCase() === "tcp";
|
||||
if (
|
||||
!healthCheck.hcHostname ||
|
||||
!healthCheck.hcPort ||
|
||||
!healthCheck.hcInterval
|
||||
) {
|
||||
logger.debug(
|
||||
`Skipping standalone health check ${healthCheck.targetHealthCheckId} due to missing fields`
|
||||
);
|
||||
return;
|
||||
}
|
||||
if (!isTCP && (!healthCheck.hcPath || !healthCheck.hcMethod)) {
|
||||
logger.debug(
|
||||
`Skipping standalone health check ${healthCheck.targetHealthCheckId} due to missing HTTP health check fields`
|
||||
);
|
||||
return;
|
||||
}
|
||||
|
||||
const hcHeadersParse = healthCheck.hcHeaders
|
||||
? JSON.parse(healthCheck.hcHeaders)
|
||||
: null;
|
||||
const hcHeadersSend: { [key: string]: string } = {};
|
||||
if (hcHeadersParse) {
|
||||
hcHeadersParse.forEach((header: { name: string; value: string }) => {
|
||||
hcHeadersSend[header.name] = header.value;
|
||||
});
|
||||
}
|
||||
|
||||
let hcStatus: number | undefined = undefined;
|
||||
if (healthCheck.hcStatus) {
|
||||
const parsedStatus = parseInt(healthCheck.hcStatus.toString());
|
||||
if (!isNaN(parsedStatus)) {
|
||||
hcStatus = parsedStatus;
|
||||
}
|
||||
}
|
||||
|
||||
await sendToClient(
|
||||
newtId,
|
||||
{
|
||||
type: `newt/healthcheck/add`,
|
||||
data: {
|
||||
targets: [
|
||||
{
|
||||
id: healthCheck.targetHealthCheckId,
|
||||
hcEnabled: healthCheck.hcEnabled,
|
||||
hcPath: healthCheck.hcPath,
|
||||
hcScheme: healthCheck.hcScheme,
|
||||
hcMode: healthCheck.hcMode,
|
||||
hcHostname: healthCheck.hcHostname,
|
||||
hcPort: healthCheck.hcPort,
|
||||
hcInterval: healthCheck.hcInterval,
|
||||
hcUnhealthyInterval: healthCheck.hcUnhealthyInterval,
|
||||
hcTimeout: healthCheck.hcTimeout,
|
||||
hcHeaders: hcHeadersSend,
|
||||
hcFollowRedirects: healthCheck.hcFollowRedirects,
|
||||
hcMethod: healthCheck.hcMethod,
|
||||
hcStatus: hcStatus,
|
||||
hcTlsServerName: healthCheck.hcTlsServerName,
|
||||
hcHealthyThreshold: healthCheck.hcHealthyThreshold,
|
||||
hcUnhealthyThreshold: healthCheck.hcUnhealthyThreshold
|
||||
}
|
||||
]
|
||||
}
|
||||
},
|
||||
{ incrementConfigVersion: true, compress: canCompress(version, "newt") }
|
||||
);
|
||||
}
|
||||
|
||||
export async function removeStandaloneHealthCheck(
|
||||
newtId: string,
|
||||
healthCheckId: number,
|
||||
version?: string | null
|
||||
) {
|
||||
await sendToClient(
|
||||
newtId,
|
||||
{
|
||||
type: `newt/healthcheck/remove`,
|
||||
data: {
|
||||
ids: [healthCheckId]
|
||||
}
|
||||
},
|
||||
{ incrementConfigVersion: true, compress: canCompress(version, "newt") }
|
||||
);
|
||||
}
|
||||
|
||||
export async function removeTargets(
|
||||
newtId: string,
|
||||
targets: Target[],
|
||||
|
||||
@@ -25,3 +25,22 @@ export type ListOrgIdpsResponse = {
|
||||
offset: number;
|
||||
};
|
||||
};
|
||||
|
||||
export type ListUserAdminOrgIdpsEntry = {
|
||||
idpId: number;
|
||||
orgId: string;
|
||||
orgName: string;
|
||||
name: string;
|
||||
type: string;
|
||||
variant: string;
|
||||
tags: string | null;
|
||||
};
|
||||
|
||||
export type ListUserAdminOrgIdpsResponse = {
|
||||
idps: ListUserAdminOrgIdpsEntry[];
|
||||
pagination: {
|
||||
total: number;
|
||||
limit: number;
|
||||
offset: number;
|
||||
};
|
||||
};
|
||||
|
||||
@@ -230,6 +230,8 @@ export async function createTarget(
|
||||
.values({
|
||||
orgId: resource.orgId,
|
||||
targetId: newTarget[0].targetId,
|
||||
siteId: targetData.siteId,
|
||||
name: `Resource ${resource.name} - ${targetData.ip}:${targetData.port}`,
|
||||
hcEnabled: targetData.hcEnabled ?? false,
|
||||
hcPath: targetData.hcPath ?? null,
|
||||
hcScheme: targetData.hcScheme ?? null,
|
||||
|
||||
@@ -99,19 +99,19 @@ export const handleHealthcheckStatusMessage: MessageHandler = async (
|
||||
name: targetHealthCheck.name,
|
||||
hcStatus: targetHealthCheck.hcHealth
|
||||
})
|
||||
.from(targets)
|
||||
.from(targetHealthCheck)
|
||||
.innerJoin(
|
||||
targets,
|
||||
eq(targetHealthCheck.targetId, targets.targetId)
|
||||
)
|
||||
.innerJoin(
|
||||
resources,
|
||||
eq(targets.resourceId, resources.resourceId)
|
||||
)
|
||||
.innerJoin(sites, eq(targets.siteId, sites.siteId))
|
||||
.innerJoin(
|
||||
targetHealthCheck,
|
||||
eq(targets.targetId, targetHealthCheck.targetId)
|
||||
)
|
||||
.where(
|
||||
and(
|
||||
eq(targets.targetId, targetIdNum),
|
||||
eq(targetHealthCheck.targetHealthCheckId, targetIdNum),
|
||||
eq(sites.siteId, newt.siteId)
|
||||
)
|
||||
)
|
||||
@@ -142,13 +142,21 @@ export const handleHealthcheckStatusMessage: MessageHandler = async (
|
||||
| "healthy"
|
||||
| "unhealthy"
|
||||
})
|
||||
.where(eq(targetHealthCheck.targetId, targetIdNum));
|
||||
.where(eq(targetHealthCheck.targetId, targetCheck.targetId));
|
||||
|
||||
const orgId = targetCheck.orgId || targetCheck.resourceOrgId; // for backwards compatibility, check both orgId fields because the target health checks dont have the orgId
|
||||
if (!orgId) {
|
||||
logger.warn(
|
||||
`No org ID found for target ${targetId}, skipping status history logging`
|
||||
);
|
||||
continue;
|
||||
}
|
||||
|
||||
// Log the state change to status history
|
||||
await db.insert(statusHistory).values({
|
||||
entityType: "healthCheck",
|
||||
entityId: targetCheck.targetHealthCheckId,
|
||||
orgId: targetCheck.orgId || targetCheck.resourceOrgId,
|
||||
orgId: orgId,
|
||||
status: healthStatus.status,
|
||||
timestamp: Math.floor(Date.now() / 1000)
|
||||
});
|
||||
@@ -170,7 +178,7 @@ export const handleHealthcheckStatusMessage: MessageHandler = async (
|
||||
.where(
|
||||
and(
|
||||
eq(targets.resourceId, targetCheck.resourceId),
|
||||
eq(targets.targetId, targetIdNum) // only check the other targets, not the one we just updated
|
||||
eq(targets.targetId, targetCheck.targetId) // only check the other targets, not the one we just updated
|
||||
)
|
||||
);
|
||||
|
||||
@@ -188,7 +196,7 @@ export const handleHealthcheckStatusMessage: MessageHandler = async (
|
||||
await db.insert(statusHistory).values({
|
||||
entityType: "resource",
|
||||
entityId: targetCheck.resourceId,
|
||||
orgId: targetCheck.orgId || targetCheck.resourceOrgId,
|
||||
orgId: orgId,
|
||||
status: status,
|
||||
timestamp: Math.floor(Date.now() / 1000)
|
||||
});
|
||||
@@ -197,13 +205,13 @@ export const handleHealthcheckStatusMessage: MessageHandler = async (
|
||||
// because we are checking above if there was a change we can fire the alert here because it changed
|
||||
if (healthStatus.status === "unhealthy") {
|
||||
await fireHealthCheckHealthyAlert(
|
||||
targetCheck.orgId || targetCheck.resourceOrgId, // for backwards compatibility, check both orgId fields because the target health checks dont have the orgId
|
||||
orgId,
|
||||
targetCheck.targetHealthCheckId,
|
||||
targetCheck.name
|
||||
);
|
||||
} else if (healthStatus.status === "healthy") {
|
||||
await fireHealthCheckNotHealthyAlert(
|
||||
targetCheck.orgId || targetCheck.resourceOrgId, // for backwards compatibility, check both orgId fields because the target health checks dont have the orgId
|
||||
orgId,
|
||||
targetCheck.targetHealthCheckId,
|
||||
targetCheck.name
|
||||
);
|
||||
|
||||
@@ -228,6 +228,7 @@ export async function updateTarget(
|
||||
const [updatedHc] = await db
|
||||
.update(targetHealthCheck)
|
||||
.set({
|
||||
siteId: parsedBody.data.siteId,
|
||||
hcEnabled: parsedBody.data.hcEnabled || false,
|
||||
hcPath: parsedBody.data.hcPath,
|
||||
hcScheme: parsedBody.data.hcScheme,
|
||||
|
||||
Reference in New Issue
Block a user