From 1c6f18b67c61533e18842565172ce43ed82a2a94 Mon Sep 17 00:00:00 2001 From: Danijel Simeunovic Date: Tue, 28 Apr 2026 20:38:59 +0200 Subject: [PATCH] homepage --- README.md | 2 +- bootstrap.sh | 1 - docs/REFERENCE.md | 53 ++++++++++++++ infra/base/homepage/homepage-extra-rbac.yaml | 21 ++++++ .../homepage-widget-credentials-sealed.yaml | 16 ++++ infra/base/homepage/homepage.yaml | 43 +++++++++++ infra/base/homepage/kustomization.yaml | 6 ++ infra/base/kustomization.yaml | 1 + infra/overlays/aks-dev/kustomization.yaml | 10 +++ infra/values/aks-dev/homepage-values.yaml | 15 ++++ infra/values/base/argocd-values.yaml | 6 ++ infra/values/base/gitea-values.yaml | 9 +++ infra/values/base/grafana-values.yaml | 10 +++ infra/values/base/homepage-values.yaml | 73 +++++++++++++++++++ infra/values/base/keycloak-values.yaml | 6 ++ infra/values/upc-dev/databunker-values.yaml | 7 ++ infra/values/upc-dev/homepage-values.yaml | 15 ++++ shared-prompts | 2 +- 18 files changed, 293 insertions(+), 3 deletions(-) create mode 100644 infra/base/homepage/homepage-extra-rbac.yaml create mode 100644 infra/base/homepage/homepage-widget-credentials-sealed.yaml create mode 100644 infra/base/homepage/homepage.yaml create mode 100644 infra/base/homepage/kustomization.yaml create mode 100644 infra/values/aks-dev/homepage-values.yaml create mode 100644 infra/values/base/homepage-values.yaml create mode 100644 infra/values/upc-dev/homepage-values.yaml diff --git a/README.md b/README.md index 198cd80..8b300fe 100644 --- a/README.md +++ b/README.md @@ -57,7 +57,7 @@ This repository contains the complete GitOps configuration for our Kubernetes cl ### What's Inside -- **Infrastructure Applications**: Traefik, Cert-Manager, Kyverno, Prometheus, Grafana, Loki, Tempo, Sealed Secrets +- **Infrastructure Applications**: Traefik, Cert-Manager, Kyverno, Prometheus, Grafana, Loki, Tempo, Sealed Secrets, Homepage (platform dashboard) - **Business Applications**: MCP10X, MusicMan, Dot-AI Stack, ArgoCD MCP - **Policies**: Kyverno security policies for secret management, namespace controls, pod verification - **Monitoring**: Full observability stack with metrics, logs, traces, and alerting diff --git a/bootstrap.sh b/bootstrap.sh index 75af6fd..b2a9794 100644 --- a/bootstrap.sh +++ b/bootstrap.sh @@ -28,7 +28,6 @@ Bootstrap() Gitea() { echo "Installing secret..." - kubectl apply -f "secrets/" kubectl apply -f "private/${CLUSTER}/gitea-repo-main.yaml" kubectl apply -f "private/${CLUSTER}/main.key" } diff --git a/docs/REFERENCE.md b/docs/REFERENCE.md index 524a943..bfa7f46 100644 --- a/docs/REFERENCE.md +++ b/docs/REFERENCE.md @@ -725,6 +725,59 @@ TLS terminates at Traefik; ArgoCD runs in insecure mode behind the proxy. ## Infrastructure Components +### Homepage (Platform Dashboard) + +**Chart**: `jameswynn/homepage` +**Namespace**: `homepage` +**URL**: `https://start.forteapps.net` + +Platform dashboard that auto-discovers deployed apps via Kubernetes service annotations. + +**Discovery mechanism**: Services annotated with `gethomepage.dev/enabled: "true"` appear in the dashboard. Apps not deployed = annotations absent = not shown. Fully dynamic per environment. + +**Annotated services**: +| Service | Namespace | Group | Widget | +|---------|-----------|-------|--------| +| `gitea-http` | `gitea` | DevOps | `gitea` | +| `argocd-server` | `argocd` | DevOps | `argocd` | +| `keycloak` | `keycloak` | Identity | none | +| `grafana` | `monitoring` | Monitoring | `grafana` | +| `karpor-server` | `karpor` | DevOps | none | + +**Adding a new app**: Annotate the app's Service in its Helm values: +```yaml +service: + annotations: + gethomepage.dev/enabled: "true" + gethomepage.dev/name: "My App" + gethomepage.dev/description: "What it does" + gethomepage.dev/group: "GroupName" + gethomepage.dev/icon: "icon-name" # https://github.com/walkxcode/dashboard-icons + gethomepage.dev/href: "https://myapp.forteapps.net" + # Optional live widget: + gethomepage.dev/widget.type: "myapp" + gethomepage.dev/widget.url: "https://myapp.forteapps.net" + # gethomepage.dev/widget.key: "{{HOMEPAGE_VAR_MYAPP_TOKEN}}" +``` + +**Widget API credentials**: Inject via env vars into the Homepage pod: +```yaml +# In homepage-values.yaml per environment +env: +- name: HOMEPAGE_VAR_GRAFANA_TOKEN + valueFrom: + secretKeyRef: + name: homepage-widget-credentials + key: grafana-token +``` +Then reference as `gethomepage.dev/widget.key: "{{HOMEPAGE_VAR_GRAFANA_TOKEN}}"`. + +**Values files**: +- `infra/values/base/homepage-values.yaml` — RBAC, kubernetes mode, layout +- `infra/values/{env}/homepage-values.yaml` — hostname per environment + +--- + ### Traefik **Chart**: `traefik/traefik` diff --git a/infra/base/homepage/homepage-extra-rbac.yaml b/infra/base/homepage/homepage-extra-rbac.yaml new file mode 100644 index 0000000..1549ab3 --- /dev/null +++ b/infra/base/homepage/homepage-extra-rbac.yaml @@ -0,0 +1,21 @@ +apiVersion: rbac.authorization.k8s.io/v1 +kind: ClusterRole +metadata: + name: homepage-services-reader +rules: +- apiGroups: [""] + resources: ["services"] + verbs: ["get", "list", "watch"] +--- +apiVersion: rbac.authorization.k8s.io/v1 +kind: ClusterRoleBinding +metadata: + name: homepage-services-reader +roleRef: + apiGroup: rbac.authorization.k8s.io + kind: ClusterRole + name: homepage-services-reader +subjects: +- kind: ServiceAccount + name: homepage + namespace: homepage diff --git a/infra/base/homepage/homepage-widget-credentials-sealed.yaml b/infra/base/homepage/homepage-widget-credentials-sealed.yaml new file mode 100644 index 0000000..3ab5e63 --- /dev/null +++ b/infra/base/homepage/homepage-widget-credentials-sealed.yaml @@ -0,0 +1,16 @@ +--- +apiVersion: bitnami.com/v1alpha1 +kind: SealedSecret +metadata: + creationTimestamp: null + name: homepage-widget-credentials + namespace: homepage +spec: + encryptedData: + HOMEPAGE_VAR_GITEA_TOKEN: AgAVN1C931EQpn+sodr3CpjlhORfJVTW8aUr+pGZQb+65Pb8QLGeVGVa7Jv60gDJUX3r+93/jMrEbCOeDL6I4qCz/V35wMCxFZLnXIdkmto0W4MKt6cK8To1/OP7EhQJOGBlSuOFsrwoy+HDtvLIqmyF0nrxhTusm9/NHrw+gCVwSTPhiAX1MCuSOSRWpbXvyNphW8j7aqUaV6ixDt424Fe4alEIShYELcS3EX/VPgsf2p2bhvBRCQOh3LEprkuxSFMuPfCBk06TPTbIN4saNVm0Ke0zW/pxkVNSiIxEnKjOmpPJtacsfWN7du+nQbx276G2qvWrf+iawJVq0Z/SLikA/NUFBL6EjSRfgE3cSOri8sbxsd0AycsFGyp98EM29wE+WOQl52M/lwl02EmCivqkICSO7Jp9pM1ScbmRMa5vcnupsGbVDxhRKLqxhAskt/BXDkRzvHN31gH3YmelES3JuqNMHV0urFxmX2oOX9Pxbtv63csc+zhy1Ui5aoex7TPnLdk7kYLSAE2MSrzT6wHvVhBC5kNnDYVrLehvJrT+eNh0MOLx2wkuJmIOxRAGUyNi5DfDnP6qnvj2aefEymLuOXAIUXH8DbeBtrjsd74HX2hhIfBlPkXvhJR3ks7i5RXjK2/YYHkgJ+nJoW80S9N7ciaRy103g74TNJZt6QzzL5Vb80qZ6yQOD4G081KmTLDmhHjJVIIv9M3nLh2s0IeBV3/Z5qHZmtjN7sSaKAn4MIr5FaH9quhx + HOMEPAGE_VAR_GRAFANA_TOKEN: AgBloBlOlP+R/4VizE1CGpj0wyiwU14BemAnuUpld7OvOGc67dwfDPyponkQXjAZg3UU2cZ70A51WUAuVlAr+25Ktlf/FW2OBqj+1BJOCqMMyu+kv026yjX2aB8dKGzlTxgF8aji+j1mC8vP3vvmgI4Zf2HQAH7uFwLfeo8+QnV5EyhcExSS0xDne+VtOP9jNXbPRayry0DdyRVtaeKAiZacO+45oAJWszWOwmoMTg9FZQkLjER6Q0tyI6NnoNObsFCnh56chZTdzBOYtmPnwld1bP2FjoJDqn8AfRwbPTIj7t0eFP7WLUO7GQKpxVl+pFwJLb5xCOw2+HNtp1BhNCu7icuc0P88IlvwzkbN0lXJbYigVOzyjEo8f/al1DXPM4WaB/Nqmr7Mtt8KTRh2WMVTgiX5jsu25D0rGDvY9gqfBBqswkRhCLsG0v0EN32zXj1/52KYdmB7pk/+2lMwSaGMS11MOenHeU1Z95fGxm9f3EGF0E8xlFr4FowgsNwr+tJQqpM0bT/4mZnaQbGWtKPFizMtsfQFm+rHFcNCrGaOuecslmiIJs8lTm18KlrncsGfxNS64tVXk+LvydU0rwybvpg2rQjEWtAl1IQsaaiz96OAlYxxK1MGxN7KE6F8R4kfnWTZ5Fs1KMmd/DOIVBXyCbqXxk8pbekmaIeNSfv92JNZ0QNJWsBa2vgQ24WI2pb4XiR0BvtLpt3BVlZUcSK92SzUblWmYWVMwHYCJkEeEUV1PhYEmyiN+V/Kq5Qb + template: + metadata: + creationTimestamp: null + name: homepage-widget-credentials + namespace: homepage diff --git a/infra/base/homepage/homepage.yaml b/infra/base/homepage/homepage.yaml new file mode 100644 index 0000000..5caf146 --- /dev/null +++ b/infra/base/homepage/homepage.yaml @@ -0,0 +1,43 @@ +apiVersion: argoproj.io/v1alpha1 +kind: Application +metadata: + name: homepage + namespace: argocd + annotations: + argocd.argoproj.io/sync-wave: "3" + labels: + app.kubernetes.io/name: homepage + app.kubernetes.io/part-of: platform + app.kubernetes.io/managed-by: argocd + finalizers: + - resources-finalizer.argocd.argoproj.io +spec: + project: default + + sources: + - repoURL: https://jameswynn.github.io/helm-charts + chart: homepage + targetRevision: "2.1.0" + helm: + releaseName: homepage + valueFiles: + - $values/infra/values/base/homepage-values.yaml + - $values/infra/values/upc-dev/homepage-values.yaml + + - repoURL: ssh://git@git.forteapps.net:2222/Forte/launchpad.git + targetRevision: HEAD + ref: values + + destination: + server: https://kubernetes.default.svc + namespace: homepage + + syncPolicy: + automated: + prune: true + selfHeal: true + allowEmpty: false + syncOptions: + - CreateNamespace=true + - Validate=true + - ServerSideApply=true diff --git a/infra/base/homepage/kustomization.yaml b/infra/base/homepage/kustomization.yaml new file mode 100644 index 0000000..d2c23da --- /dev/null +++ b/infra/base/homepage/kustomization.yaml @@ -0,0 +1,6 @@ +apiVersion: kustomize.config.k8s.io/v1beta1 +kind: Kustomization +resources: +- homepage.yaml +- homepage-widget-credentials-sealed.yaml +- homepage-extra-rbac.yaml diff --git a/infra/base/kustomization.yaml b/infra/base/kustomization.yaml index 1f216f1..7c18dc8 100644 --- a/infra/base/kustomization.yaml +++ b/infra/base/kustomization.yaml @@ -21,3 +21,4 @@ resources: - grafana-dashboards - karpor - databunker +- homepage diff --git a/infra/overlays/aks-dev/kustomization.yaml b/infra/overlays/aks-dev/kustomization.yaml index 60edc78..9e7fa41 100644 --- a/infra/overlays/aks-dev/kustomization.yaml +++ b/infra/overlays/aks-dev/kustomization.yaml @@ -13,9 +13,19 @@ resources: - ../../base/prometheus - ../../base/sealedsecrets - ../../base/tempo +- ../../base/homepage - ../../base/traefik-application patches: +# Homepage: swap upc-dev → aks-dev +- target: + kind: Application + name: homepage + patch: | + - op: replace + path: /spec/sources/0/helm/valueFiles/1 + value: $values/infra/values/aks-dev/homepage-values.yaml + # Traefik: swap upc-dev → aks-dev - target: kind: Application diff --git a/infra/values/aks-dev/homepage-values.yaml b/infra/values/aks-dev/homepage-values.yaml new file mode 100644 index 0000000..101157e --- /dev/null +++ b/infra/values/aks-dev/homepage-values.yaml @@ -0,0 +1,15 @@ +ingress: + main: + enabled: true + ingressClassName: traefik + annotations: + cert-manager.io/cluster-issuer: letsencrypt-prod + hosts: + - host: start.forteapps.net + paths: + - path: / + pathType: Prefix + tls: + - secretName: homepage-tls + hosts: + - start.forteapps.net diff --git a/infra/values/base/argocd-values.yaml b/infra/values/base/argocd-values.yaml index 662812b..ddd6962 100644 --- a/infra/values/base/argocd-values.yaml +++ b/infra/values/base/argocd-values.yaml @@ -35,6 +35,12 @@ server: ingressClassName: traefik annotations: cert-manager.io/cluster-issuer: letsencrypt-prod + gethomepage.dev/enabled: "true" + gethomepage.dev/name: "ArgoCD" + gethomepage.dev/description: "GitOps continuous delivery" + gethomepage.dev/group: "DevOps" + gethomepage.dev/icon: "argo-cd" + gethomepage.dev/href: "https://argocd.forteapps.net" tls: true extraArgs: - --insecure diff --git a/infra/values/base/gitea-values.yaml b/infra/values/base/gitea-values.yaml index 619f779..e322a4b 100644 --- a/infra/values/base/gitea-values.yaml +++ b/infra/values/base/gitea-values.yaml @@ -114,6 +114,15 @@ ingress: className: traefik annotations: cert-manager.io/cluster-issuer: letsencrypt-prod + gethomepage.dev/enabled: "true" + gethomepage.dev/name: "Gitea" + gethomepage.dev/description: "Git hosting & CI/CD" + gethomepage.dev/group: "DevOps" + gethomepage.dev/icon: "gitea" + gethomepage.dev/href: "https://git.forteapps.net" + gethomepage.dev/widget.type: "gitea" + gethomepage.dev/widget.url: "https://git.forteapps.net" + gethomepage.dev/widget.key: "{{HOMEPAGE_VAR_GITEA_TOKEN}}" hosts: - host: git.forteapps.net paths: diff --git a/infra/values/base/grafana-values.yaml b/infra/values/base/grafana-values.yaml index 81e2dbf..9d229f5 100644 --- a/infra/values/base/grafana-values.yaml +++ b/infra/values/base/grafana-values.yaml @@ -3,11 +3,21 @@ ingress: ingressClassName: traefik annotations: cert-manager.io/cluster-issuer: letsencrypt-prod + gethomepage.dev/enabled: "true" + gethomepage.dev/name: "Grafana" + gethomepage.dev/description: "Metrics & observability dashboards" + gethomepage.dev/group: "Monitoring" + gethomepage.dev/icon: "grafana" + gethomepage.dev/href: "https://grafana.forteapps.net" tls: - secretName: grafana-tls hosts: - grafana.forteapps.net +persistence: + enabled: true + size: 1Gi + resources: requests: cpu: 50m diff --git a/infra/values/base/homepage-values.yaml b/infra/values/base/homepage-values.yaml new file mode 100644 index 0000000..22066ed --- /dev/null +++ b/infra/values/base/homepage-values.yaml @@ -0,0 +1,73 @@ +# Homepage Helm Values +# Chart: jameswynn/homepage — https://gethomepage.dev +# Discovery: K8s service annotations (gethomepage.dev/*) +# Each deployed app annotates its own Service — apps not deployed = not visible. + +# RBAC ClusterRole — required for cluster-wide service annotation scanning +enableRbac: true + +serviceAccount: + create: true + name: homepage + +config: + # Scan all namespaces for services with gethomepage.dev/enabled: "true" + kubernetes: + mode: cluster + traefik: true + + settings: + title: "Forte Platform" + headerStyle: clean + layout: + Apps: + style: row + columns: 3 + Identity: + style: row + columns: 2 + DevOps: + style: row + columns: 2 + Monitoring: + style: row + columns: 1 + + # Top-of-page cluster overview widget + widgets: + - kubernetes: + cluster: + show: true + cpu: true + memory: true + showLabel: true + label: "Cluster" + nodes: + show: true + cpu: true + memory: true + showLabel: true + # In-cluster entries come from K8s service annotations. + # External (out-of-cluster) services are listed here statically. + bookmarks: [] + services: + - Apps: + - Forte Feedback: + href: https://feedback.forteapps.net + description: Fortes internal feedback app + icon: forte + +resources: + requests: + cpu: 10m + memory: 128Mi + limits: + cpu: 100m + memory: 256Mi + +env: +- name: HOMEPAGE_ALLOWED_HOSTS + value: start.forteapps.net +envFrom: +- secretRef: + name: homepage-widget-credentials diff --git a/infra/values/base/keycloak-values.yaml b/infra/values/base/keycloak-values.yaml index 109d14d..bfa2b2f 100644 --- a/infra/values/base/keycloak-values.yaml +++ b/infra/values/base/keycloak-values.yaml @@ -18,6 +18,12 @@ ingress: ingressClassName: traefik annotations: cert-manager.io/cluster-issuer: letsencrypt-prod + gethomepage.dev/enabled: "true" + gethomepage.dev/name: "Keycloak" + gethomepage.dev/description: "Identity & access management" + gethomepage.dev/group: "Identity" + gethomepage.dev/icon: "keycloak" + gethomepage.dev/href: "https://id.forteapps.net" metrics: enabled: true diff --git a/infra/values/upc-dev/databunker-values.yaml b/infra/values/upc-dev/databunker-values.yaml index ab60a39..fcbda4e 100644 --- a/infra/values/upc-dev/databunker-values.yaml +++ b/infra/values/upc-dev/databunker-values.yaml @@ -1,3 +1,10 @@ ingress: enabled: true host: databunker.forteapps.net + annotations: + gethomepage.dev/enabled: "true" + gethomepage.dev/name: "Databunker" + gethomepage.dev/description: "Secure Database for PII and PCI Records" + gethomepage.dev/group: "Identity" + gethomepage.dev/icon: "adminer" + gethomepage.dev/href: "https://databunker.forteapps.net" diff --git a/infra/values/upc-dev/homepage-values.yaml b/infra/values/upc-dev/homepage-values.yaml new file mode 100644 index 0000000..101157e --- /dev/null +++ b/infra/values/upc-dev/homepage-values.yaml @@ -0,0 +1,15 @@ +ingress: + main: + enabled: true + ingressClassName: traefik + annotations: + cert-manager.io/cluster-issuer: letsencrypt-prod + hosts: + - host: start.forteapps.net + paths: + - path: / + pathType: Prefix + tls: + - secretName: homepage-tls + hosts: + - start.forteapps.net diff --git a/shared-prompts b/shared-prompts index c5bc55b..b79858d 160000 --- a/shared-prompts +++ b/shared-prompts @@ -1 +1 @@ -Subproject commit c5bc55b3d7bef7c2430bf9cb07b51c99055c85da +Subproject commit b79858d73c292e8aea2f1213abf3ad205b720a2a