import { CustomObjectsApi } from "@kubernetes/client-node"; import { substituteEnvironmentVars } from "utils/config/config"; import { ANNOTATION_BASE, ANNOTATION_WIDGET_BASE, getKubeConfig, HTTPROUTE_API_GROUP, HTTPROUTE_API_VERSION, } from "utils/config/kubernetes"; import * as shvl from "utils/config/shvl"; import createLogger from "utils/logger"; const logger = createLogger("resource-helpers"); const kc = getKubeConfig(); const getSchemaFromGateway = async (parentRef) => { const crd = kc.makeApiClient(CustomObjectsApi); const schema = await crd .getNamespacedCustomObject({ group: HTTPROUTE_API_GROUP, version: HTTPROUTE_API_VERSION, namespace: parentRef.namespace, plural: "gateways", name: parentRef.name, }) .then((response) => { const listener = response.spec.listeners.find((l) => l.name === parentRef.sectionName) ?? response.spec.listeners[0]; return listener.protocol.toLowerCase(); }) .catch((error) => { logger.error("Error getting gateways: %d %s %s", error.statusCode, error.body, error.response); logger.debug(error); return "http"; }); return schema; }; async function getUrlFromHttpRoute(resource) { let url = null; const hasHostName = resource.spec?.hostnames; if (hasHostName) { if (resource.spec.rules[0].matches[0].path.type !== "RegularExpression") { const urlHost = resource.spec.hostnames[0]; const urlPath = resource.spec.rules[0].matches[0].path.value; const urlSchema = await getSchemaFromGateway(resource.spec.parentRefs[0]); url = `${urlSchema}://${urlHost}${urlPath}`; } } return url; } function getUrlFromIngress(resource) { const urlHost = resource.spec.rules[0].host; const urlPath = resource.spec.rules[0].http.paths[0].path; const urlSchema = resource.spec.tls ? "https" : "http"; return `${urlSchema}://${urlHost}${urlPath}`; } async function getUrlSchema(resource) { const isHttpRoute = resource.kind === "HTTPRoute"; let urlSchema; if (isHttpRoute) { urlSchema = getUrlFromHttpRoute(resource); } else { urlSchema = getUrlFromIngress(resource); } return urlSchema; } export function isDiscoverable(resource, instanceName) { return ( resource.metadata.annotations && resource.metadata.annotations[`${ANNOTATION_BASE}/enabled`] === "true" && (!resource.metadata.annotations[`${ANNOTATION_BASE}/instance`] || resource.metadata.annotations[`${ANNOTATION_BASE}/instance`] === instanceName || `${ANNOTATION_BASE}/instance.${instanceName}` in resource.metadata.annotations) ); } export async function constructedServiceFromResource(resource) { let constructedService = { app: resource.metadata.annotations[`${ANNOTATION_BASE}/app`] || resource.metadata.name, namespace: resource.metadata.namespace, href: resource.metadata.annotations[`${ANNOTATION_BASE}/href`] || (await getUrlSchema(resource)), name: resource.metadata.annotations[`${ANNOTATION_BASE}/name`] || resource.metadata.name, group: resource.metadata.annotations[`${ANNOTATION_BASE}/group`] || "Kubernetes", weight: resource.metadata.annotations[`${ANNOTATION_BASE}/weight`] || "0", icon: resource.metadata.annotations[`${ANNOTATION_BASE}/icon`] || "", description: resource.metadata.annotations[`${ANNOTATION_BASE}/description`] || "", external: false, type: "service", }; if (resource.metadata.annotations[`${ANNOTATION_BASE}/external`]) { constructedService.external = String(resource.metadata.annotations[`${ANNOTATION_BASE}/external`]).toLowerCase() === "true"; } if (resource.metadata.annotations[`${ANNOTATION_BASE}/pod-selector`] !== undefined) { constructedService.podSelector = resource.metadata.annotations[`${ANNOTATION_BASE}/pod-selector`]; } if (resource.metadata.annotations[`${ANNOTATION_BASE}/ping`]) { constructedService.ping = resource.metadata.annotations[`${ANNOTATION_BASE}/ping`]; } if (resource.metadata.annotations[`${ANNOTATION_BASE}/siteMonitor`]) { constructedService.siteMonitor = resource.metadata.annotations[`${ANNOTATION_BASE}/siteMonitor`]; } if (resource.metadata.annotations[`${ANNOTATION_BASE}/statusStyle`]) { constructedService.statusStyle = resource.metadata.annotations[`${ANNOTATION_BASE}/statusStyle`]; } Object.keys(resource.metadata.annotations).forEach((annotation) => { if (annotation.startsWith(ANNOTATION_WIDGET_BASE)) { shvl.set( constructedService, annotation.replace(`${ANNOTATION_BASE}/`, ""), resource.metadata.annotations[annotation], ); } }); try { constructedService = JSON.parse(substituteEnvironmentVars(JSON.stringify(constructedService))); } catch (e) { logger.error("Error attempting k8s environment variable substitution."); logger.debug(e); } return constructedService; }