homepage/src/utils/kubernetes/kubernetes-routes.js
djeinstine 5289765125
Support for Kubernetes gateway API (#4)
Added support for Kubernetes Gateway-API
2024-11-13 11:51:05 +01:00

212 lines
6.1 KiB
JavaScript

import { CustomObjectsApi, NetworkingV1Api, CoreV1Api, ApiextensionsV1Api } from "@kubernetes/client-node";
import getKubeArguments from "utils/config/kubernetes";
import createLogger from "utils/logger";
const logger = createLogger("service-helpers");
const kubeArguments = getKubeArguments();
const kc = kubeArguments.config;
const apiGroup = "gateway.networking.k8s.io";
const version = "v1";
let crd;
let core;
let networking;
let routingType;
let traefik;
export async function checkCRD(name) {
const apiExtensions = kc.makeApiClient(ApiextensionsV1Api);
const exist = await apiExtensions
.readCustomResourceDefinitionStatus(name)
.then(() => true)
.catch(async (error) => {
if (error.statusCode === 403) {
logger.error(
"Error checking if CRD %s exists. Make sure to add the following permission to your RBAC: %d %s %s",
name,
error.statusCode,
error.body.message,
);
}
return false;
});
return exist;
}
const getSchemaFromGateway = async (gatewayRef) => {
const schema = await crd
.getNamespacedCustomObject(apiGroup, version, gatewayRef.namespace, "gateways", gatewayRef.name)
.then((response) => {
const listner = response.body.spec.listeners.filter((listener) => listener.name === gatewayRef.sectionName)[0];
return listner.protocol.toLowerCase();
})
.catch((error) => {
logger.error("Error getting gateways: %d %s %s", error.statusCode, error.body, error.response);
logger.debug(error);
return "";
});
return schema;
};
async function getUrlFromHttpRoute(ingress) {
const urlHost = ingress.spec.hostnames[0];
const urlPath = ingress.spec.rules[0].matches[0].path.value;
const urlSchema = (await getSchemaFromGateway(ingress.spec.parentRefs[0])) ? "https" : "http";
return `${urlSchema}://${urlHost}${urlPath}`;
}
function getUrlFromIngress(ingress) {
const urlHost = ingress.spec.rules[0].host;
const urlPath = ingress.spec.rules[0].http.paths[0].path;
const urlSchema = ingress.spec.tls ? "https" : "http";
return `${urlSchema}://${urlHost}${urlPath}`;
}
async function getHttpRouteList() {
// httproutes
const getHttpRoute = async (namespace) =>
crd
.listNamespacedCustomObject(apiGroup, version, namespace, "httproutes")
.then((response) => {
const [httpRoute] = response.body.items;
return httpRoute;
})
.catch((error) => {
logger.error("Error getting httproutes: %d %s %s", error.statusCode, error.body, error.response);
logger.debug(error);
return null;
});
// namespaces
const namespaces = await core
.listNamespace()
.then((response) => response.body.items.map((ns) => ns.metadata.name))
.catch((error) => {
logger.error("Error getting namespaces: %d %s %s", error.statusCode, error.body, error.response);
logger.debug(error);
return null;
});
let httpRouteList = [];
if (namespaces) {
const httpRouteListUnfiltered = await Promise.all(
namespaces.map(async (namespace) => {
const httpRoute = await getHttpRoute(namespace);
return httpRoute;
}),
);
httpRouteList = httpRouteListUnfiltered.filter((httpRoute) => httpRoute !== undefined);
}
return httpRouteList;
}
async function getIngressList(ANNOTATION_BASE) {
const ingressList = await networking
.listIngressForAllNamespaces(null, null, null, null)
.then((response) => response.body)
.catch((error) => {
logger.error("Error getting ingresses: %d %s %s", error.statusCode, error.body, error.response);
logger.debug(error);
return null;
});
if (traefik) {
const traefikContainoExists = await checkCRD("ingressroutes.traefik.containo.us");
const traefikExists = await checkCRD("ingressroutes.traefik.io");
const traefikIngressListContaino = await crd
.listClusterCustomObject("traefik.containo.us", "v1alpha1", "ingressroutes")
.then((response) => response.body)
.catch(async (error) => {
if (traefikContainoExists) {
logger.error(
"Error getting traefik ingresses from traefik.containo.us: %d %s %s",
error.statusCode,
error.body,
error.response,
);
logger.debug(error);
}
return [];
});
const traefikIngressListIo = await crd
.listClusterCustomObject("traefik.io", "v1alpha1", "ingressroutes")
.then((response) => response.body)
.catch(async (error) => {
if (traefikExists) {
logger.error(
"Error getting traefik ingresses from traefik.io: %d %s %s",
error.statusCode,
error.body,
error.response,
);
logger.debug(error);
}
return [];
});
const traefikIngressList = [...(traefikIngressListContaino?.items ?? []), ...(traefikIngressListIo?.items ?? [])];
if (traefikIngressList.length > 0) {
const traefikServices = traefikIngressList.filter(
(ingress) => ingress.metadata.annotations && ingress.metadata.annotations[`${ANNOTATION_BASE}/href`],
);
ingressList.items.push(...traefikServices);
}
}
return ingressList.items;
}
export async function getRouteList(ANNOTATION_BASE) {
let routeList = [];
if (!kc) {
return [];
}
crd = kc.makeApiClient(CustomObjectsApi);
core = kc.makeApiClient(CoreV1Api);
networking = kc.makeApiClient(NetworkingV1Api);
routingType = kubeArguments.route;
traefik = kubeArguments.traefik;
switch (routingType) {
case "ingress":
routeList = await getIngressList(ANNOTATION_BASE);
break;
case "gateway":
routeList = await getHttpRouteList();
break;
default:
routeList = await getIngressList(ANNOTATION_BASE);
}
return routeList;
}
export async function getUrlSchema(route) {
let urlSchema;
switch (routingType) {
case "ingress":
urlSchema = getUrlFromIngress(route);
break;
case "gateway":
urlSchema = await getUrlFromHttpRoute(route);
break;
default:
urlSchema = getUrlFromIngress(route);
}
return urlSchema;
}