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/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.yaml b/infra/base/homepage/homepage.yaml new file mode 100644 index 0000000..ffb507c --- /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: "1.2.3" + 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..84f7241 --- /dev/null +++ b/infra/base/homepage/kustomization.yaml @@ -0,0 +1,4 @@ +apiVersion: kustomize.config.k8s.io/v1beta1 +kind: Kustomization +resources: +- homepage.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..8d39490 100644 --- a/infra/values/base/argocd-values.yaml +++ b/infra/values/base/argocd-values.yaml @@ -30,6 +30,17 @@ configs: "server.insecure": true "reposerver.enable.git.submodule": "false" server: + service: + annotations: + gethomepage.dev/enabled: "true" + gethomepage.dev/name: "ArgoCD" + gethomepage.dev/description: "GitOps continuous delivery" + gethomepage.dev/group: "DevOps" + gethomepage.dev/icon: "argocd" + gethomepage.dev/href: "https://argocd.forteapps.net" + gethomepage.dev/widget.type: "argocd" + gethomepage.dev/widget.url: "https://argocd.forteapps.net" + # gethomepage.dev/widget.key: "{{HOMEPAGE_VAR_ARGOCD_TOKEN}}" ingress: enabled: true ingressClassName: traefik diff --git a/infra/values/base/gitea-values.yaml b/infra/values/base/gitea-values.yaml index 619f779..ed2cc62 100644 --- a/infra/values/base/gitea-values.yaml +++ b/infra/values/base/gitea-values.yaml @@ -175,8 +175,19 @@ redis-cluster: test: enabled: false -# -- SSH service (ClusterIP, exposed externally via Traefik TCP IngressRoute on port 2222) +# -- Services: HTTP (homepage discovery) + SSH (Traefik TCP IngressRoute on port 2222) service: + http: + annotations: + 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}}" ssh: type: ClusterIP port: 22 diff --git a/infra/values/base/grafana-values.yaml b/infra/values/base/grafana-values.yaml index 81e2dbf..f3a183c 100644 --- a/infra/values/base/grafana-values.yaml +++ b/infra/values/base/grafana-values.yaml @@ -16,6 +16,19 @@ resources: cpu: 100m memory: 256Mi +service: + annotations: + 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" + gethomepage.dev/widget.type: "grafana" + gethomepage.dev/widget.url: "https://grafana.forteapps.net" + # gethomepage.dev/widget.username: "{{HOMEPAGE_VAR_GRAFANA_USER}}" + # gethomepage.dev/widget.password: "{{HOMEPAGE_VAR_GRAFANA_PASSWORD}}" + adminUser: admin adminPassword: "forte" diff --git a/infra/values/base/homepage-values.yaml b/infra/values/base/homepage-values.yaml new file mode 100644 index 0000000..9245a83 --- /dev/null +++ b/infra/values/base/homepage-values.yaml @@ -0,0 +1,58 @@ +# 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 + + settings: + title: "Forte Platform" + headerStyle: clean + layout: + DevOps: + style: row + columns: 4 + Identity: + style: row + columns: 4 + Monitoring: + style: row + columns: 4 + + # Top-of-page cluster overview widget + widgets: + - kubernetes: + cluster: + show: true + cpu: true + memory: true + showLabel: true + label: "Cluster" + nodes: + show: false + + # Services section empty — all entries come from K8s annotations + services: [] + + # Widget API credentials (optional — add via SealedSecret + envFrom below) + # Homepage reads HOMEPAGE_VAR_* env vars and substitutes them in widget annotations. + # Example: gethomepage.dev/widget.key: "{{HOMEPAGE_VAR_GRAFANA_TOKEN}}" + # To enable: create a sealed secret and add envFrom to load it. + +resources: + requests: + cpu: 10m + memory: 64Mi + limits: + cpu: 100m + memory: 128Mi diff --git a/infra/values/base/keycloak-values.yaml b/infra/values/base/keycloak-values.yaml index 109d14d..5616a4c 100644 --- a/infra/values/base/keycloak-values.yaml +++ b/infra/values/base/keycloak-values.yaml @@ -19,6 +19,15 @@ ingress: annotations: cert-manager.io/cluster-issuer: letsencrypt-prod +service: + annotations: + 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 prometheusRule: 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