From 8aafaf1fba0752647249c90892ab7b89dbd23cca Mon Sep 17 00:00:00 2001 From: Rikpat Date: Tue, 3 Sep 2024 13:30:45 +0200 Subject: [PATCH] ran pre-commit --- docs/configs/kubernetes.md | 10 +-- src/pages/api/widgets/kubernetes.js | 2 +- src/utils/config/service-helpers.js | 125 +++++++++++++++------------- 3 files changed, 74 insertions(+), 63 deletions(-) diff --git a/docs/configs/kubernetes.md b/docs/configs/kubernetes.md index 7ac1afdd..640f52eb 100644 --- a/docs/configs/kubernetes.md +++ b/docs/configs/kubernetes.md @@ -146,7 +146,7 @@ Similarly to Docker service discovery, there currently is no rigid ordering to d ## CRDs -Homepage also comes with Kubernetes CRDs for services. These CRDs have same structure and properties as regular service YAML definition, with added properties of `group`, `weight`, `podSelector` and `instances`, used as described above. +Homepage also comes with Kubernetes CRDs for services. These CRDs have same structure and properties as regular service YAML definition, with added properties of `group`, `weight`, `podSelector` and `instances`, used as described above. Compared to annotations, CRD approach can use Kubernetes secrets and configMaps to populate attributes of the `widget` object. To do this, instead of using the name, ex. `key`, use `keyFrom`, to match kubernetes standards. Then use either `secretKeyRef` or `configMapKeyRef` object, see example: @@ -161,18 +161,18 @@ metadata: spec: description: TV Show Management Application group: Media - href: 'https://sonarr.example.org/' + href: "https://sonarr.example.org/" icon: sonarr.svg widget: type: sonarr - url: 'http://sonarr.media.svc.cluster.local:8989' + url: "http://sonarr.media.svc.cluster.local:8989" keyFrom: secretKeyRef: key: SONARR_API_KEY name: arr-secrets ``` -Some attributes have values inferred from the definition itself, `app` is read from *app.kubernetes.io/name* annotation or metadata.name, namespace is also read from metadata. +Some attributes have values inferred from the definition itself, `app` is read from _app.kubernetes.io/name_ annotation or metadata.name, namespace is also read from metadata. For secrets and configMaps, if `namespace` is not specified in ref object, it assumes it's in the same namespace as CRD. If you want to specify a default namespace (for example for security reason when assigning secrets:read permission for only one namespace), you can specify `defaultSecretNamespace` and `defaultConfigMapNamespace` in `kubernetes.yaml` file. @@ -180,4 +180,4 @@ For secrets and configMaps, if `namespace` is not specified in ref object, it as --- mode: cluster defaultSecretNamespace: homepage -``` \ No newline at end of file +``` diff --git a/src/pages/api/widgets/kubernetes.js b/src/pages/api/widgets/kubernetes.js index b5cd70a1..dd08968d 100644 --- a/src/pages/api/widgets/kubernetes.js +++ b/src/pages/api/widgets/kubernetes.js @@ -8,7 +8,7 @@ const logger = createLogger("kubernetes-widget"); export default async function handler(req, res) { try { - const kc = makeKubeConfig(getKubernetesConfig()) + const kc = makeKubeConfig(getKubernetesConfig()); if (!kc) { return res.status(500).send({ error: "No kubernetes configuration", diff --git a/src/utils/config/service-helpers.js b/src/utils/config/service-helpers.js index a9071a22..8af1f312 100644 --- a/src/utils/config/service-helpers.js +++ b/src/utils/config/service-helpers.js @@ -183,7 +183,6 @@ export async function servicesFromKubernetes() { const ANNOTATION_WIDGET_BASE = `${ANNOTATION_BASE}/widget.`; const { instanceName } = getSettings(); - try { const config = getKubernetesConfig(); const kc = makeKubeConfig(config); @@ -204,7 +203,7 @@ export async function servicesFromKubernetes() { const traefikContainoExists = await checkCRD(kc, "ingressroutes.traefik.containo.us"); const traefikExists = await checkCRD(kc, "ingressroutes.traefik.io"); - const homepageServiceExists = await checkCRD(kc, "homepageservices.gethomepage.dev") + const homepageServiceExists = await checkCRD(kc, "homepageservices.gethomepage.dev"); const traefikIngressListContaino = await crd .listClusterCustomObject("traefik.containo.us", "v1alpha1", "ingressroutes") @@ -251,7 +250,7 @@ export async function servicesFromKubernetes() { return []; } - let homepageServices = [] + let homepageServices = []; if (homepageServiceExists) { homepageServices = await crd .listClusterCustomObject("gethomepage.dev", "v1", "homepageservices") @@ -265,63 +264,75 @@ export async function servicesFromKubernetes() { ); return []; }); - await Promise.all(homepageServices.items.map(async (service) => { - const { spec, metadata } = service; - // Enter default values from metadata or annotations - spec.app = spec.app || metadata.annotations["app.kubernetes.io/name"] || metadata.name - spec.namespace = spec.namespace || metadata.namespace - spec.name = spec.name || metadata.name - spec.weight = spec.weight || "0" - spec.icon = spec.icon || "" - spec.description = spec.description || "" - // Parse values from secrets and configmaps in widget - if (spec.widget) { - const parsedWidget = {} - await Promise.all(Object.keys(spec.widget).map(async key => { - // To keep up with kubernetes standard valueFrom - if (key.endsWith("From")) { - if (spec.widget[key].secretKeyRef) { - const { secretKeyRef } = spec.widget[key] - const secret = await core.readNamespacedSecret(secretKeyRef.name, secretKeyRef.namespace || config.defaultSecretNamespace || metadata.namespace) - const base64secret = secret.body.data[secretKeyRef.key] - if (!base64secret) { - logger.error( - "Error getting secret value: Secret %s in namespace %s doesn't contain key %s", - spec.widget[key].secretKeyRef.name, - metadata.namespace, - spec.widget[key].secretKeyRef.key, - ); - return + await Promise.all( + homepageServices.items.map(async (service) => { + const { spec, metadata } = service; + // Enter default values from metadata or annotations + spec.app = spec.app || metadata.annotations["app.kubernetes.io/name"] || metadata.name; + spec.namespace = spec.namespace || metadata.namespace; + spec.name = spec.name || metadata.name; + spec.weight = spec.weight || "0"; + spec.icon = spec.icon || ""; + spec.description = spec.description || ""; + // Parse values from secrets and configmaps in widget + if (spec.widget) { + const parsedWidget = {}; + await Promise.all( + Object.keys(spec.widget).map(async (key) => { + // To keep up with kubernetes standard valueFrom + if (key.endsWith("From")) { + if (spec.widget[key].secretKeyRef) { + const { secretKeyRef } = spec.widget[key]; + const secret = await core.readNamespacedSecret( + secretKeyRef.name, + secretKeyRef.namespace || config.defaultSecretNamespace || metadata.namespace, + ); + const base64secret = secret.body.data[secretKeyRef.key]; + if (!base64secret) { + logger.error( + "Error getting secret value: Secret %s in namespace %s doesn't contain key %s", + spec.widget[key].secretKeyRef.name, + metadata.namespace, + spec.widget[key].secretKeyRef.key, + ); + return; + } + parsedWidget[key.substring(0, key.length - 4)] = Buffer.from(base64secret, "base64").toString( + "utf8", + ); + } else if (spec.widget[key].configMapKeyRef) { + const { configMapKeyRef } = spec.widget[key]; + const configMap = await core.readNamespacedConfigMap( + configMapKeyRef.name, + configMapKeyRef.namespace || config.defaultConfigMapNamespace || metadata.namespace, + ); + const configMapValue = configMap.body.data[configMapKeyRef.key]; + if (!configMapValue) { + logger.error( + "Error getting configMap value: ConfigMap %s in namespace %s doesn't contain key %s", + spec.widget[key].configMapKey.name, + metadata.namespace, + spec.widget[key].configMapKey.key, + ); + return; + } + parsedWidget[key.substring(0, key.length - 4)] = configMapValue; + } + } else { + parsedWidget[key] = spec.widget[key]; } - parsedWidget[key.substring(0, key.length - 4)] = Buffer.from(base64secret, "base64").toString("utf8") - } else if (spec.widget[key].configMapKeyRef) { - const { configMapKeyRef } = spec.widget[key] - const configMap = await core.readNamespacedConfigMap(configMapKeyRef.name, configMapKeyRef.namespace || config.defaultConfigMapNamespace || metadata.namespace) - const configMapValue = configMap.body.data[configMapKeyRef.key] - if (!configMapValue) { - logger.error( - "Error getting configMap value: ConfigMap %s in namespace %s doesn't contain key %s", - spec.widget[key].configMapKey.name, - metadata.namespace, - spec.widget[key].configMapKey.key, - ); - return - } - parsedWidget[key.substring(0, key.length - 4)] = configMapValue - } - } else { - parsedWidget[key] = spec.widget[key] - } - })) - spec.widget = parsedWidget - } - })) + }), + ); + spec.widget = parsedWidget; + } + }), + ); } const services = [ ...homepageServices.items - .filter(service => !service.spec.instances || service.spec.instances.includes(instanceName)) - .map(service => service.spec), + .filter((service) => !service.spec.instances || service.spec.instances.includes(instanceName)) + .map((service) => service.spec), ...ingressList.items .filter( (ingress) => @@ -377,7 +388,7 @@ export async function servicesFromKubernetes() { } return constructedService; - }) + }), ]; const mappedServiceGroups = []; @@ -403,7 +414,7 @@ export async function servicesFromKubernetes() { return mappedServiceGroups; } catch (e) { - console.log(e) + console.log(e); if (e) logger.error(e); throw e; }