diff --git a/.github/retired-repo-links.json b/.github/retired-repo-links.json index e288c31..698bb58 100644 --- a/.github/retired-repo-links.json +++ b/.github/retired-repo-links.json @@ -5,7 +5,6 @@ "README.md", "CONTRIBUTING.md", "AGENTS.md", "CLAUDE.md", "SECURITY.md", "docs", ".claude/skills", "k8s/bases/README.md", "k8s/bases/apps/README.md", - "k8s/bases/apps/headlamp/README.md", "k8s/bases/infrastructure/README.md", "k8s/bases/infrastructure/audit-log-forwarder/README.md", "k8s/bases/infrastructure/controllers/cert-manager/README.md", diff --git a/AGENTS.md b/AGENTS.md index c14de99..15db750 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -56,7 +56,7 @@ k8s/ # All Kubernetes manifests bootstrap/ # Flux post-build substitution variables (base ConfigMap + SOPS secret) infrastructure/ # Organized by resource type: controllers/, certificates/, gateway/, # cluster-policies/, external-secrets/, alerts/, vault-*/ (OpenBao), etc. - apps/ # App deployments — whoami, homepage, headlamp + apps/ # App deployments — whoami, homepage components/ # Shared Kustomize components: HelmRelease defaults and drift detection # (listed by every HelmRelease-owning kustomization), plus the # opt-in profile transformations diff --git a/docs/ARCHITECTURE.md b/docs/ARCHITECTURE.md index da0ecc3..777782e 100644 --- a/docs/ARCHITECTURE.md +++ b/docs/ARCHITECTURE.md @@ -33,7 +33,6 @@ controllers to save resources (noted inline). ### Demo apps - **Homepage** — a dashboard landing page for the platform -- **Headlamp** — a Kubernetes web UI - **whoami** — a tiny debug echo service They are deliberately lightweight, so a new platform has something to look at. To run **your own** diff --git a/docs/TEMPLATING.md b/docs/TEMPLATING.md index 5b775e9..d58ea74 100644 --- a/docs/TEMPLATING.md +++ b/docs/TEMPLATING.md @@ -73,8 +73,8 @@ ksail --config ksail.prod.yaml cluster update ksail --config ksail.prod.yaml workload reconcile ``` -This profile is transitional. It enables Coroot, removes OpenCost and its Headlamp -plugin, retires the legacy Loki and Alloy log path, and keeps kube-prometheus-stack +This profile is transitional. It enables Coroot, removes OpenCost, +retires the legacy Loki and Alloy log path, and keeps kube-prometheus-stack until the metrics and alerting migration is proven separately. Cost allocation is therefore unavailable in this profile; it does not connect OpenCost to Coroot Prometheus. Coroot is the only observability UI in this profile: it removes the Grafana, Prometheus and Alertmanager @@ -250,8 +250,7 @@ See [`.github/workflows/`](../.github/workflows) for the exact names in use. Kyverno, alerting configs, OpenBao vault, External Secrets Operator, ClusterSecretStore, vault-config Job, vault-seed PushSecrets, vault-backup CronJob, and the rest of the controller set. -- [`k8s/bases/apps/`](../k8s/bases/apps) — the demo applications (homepage, whoami, - headlamp). +- [`k8s/bases/apps/`](../k8s/bases/apps) — the demo applications (homepage, whoami). - [`k8s/components/`](../k8s/components) — shared transformations: the HelmRelease retry defaults and drift detection every HelmRelease-owning kustomization lists, and opt-in profile transformations such as removing OpenCost across controller, diff --git a/docs/TENANTS.md b/docs/TENANTS.md index 827a90d..f834b5d 100644 --- a/docs/TENANTS.md +++ b/docs/TENANTS.md @@ -1,6 +1,6 @@ # Onboarding a GitOps tenant -The platform ships with a few lightweight **demo apps** (homepage, whoami, headlamp) +The platform ships with a few lightweight **demo apps** (homepage, whoami) in [`k8s/bases/apps/`](../k8s/bases/apps). To run **your own** application on the platform, add it as a **tenant**: an application that runs on the cluster but lives in its **own repository**. The tenant repo builds a container image and publishes its diff --git a/hosts b/hosts index c450b57..ac24b20 100644 --- a/hosts +++ b/hosts @@ -1,6 +1,5 @@ 127.0.0.1 dex.platform.lan 127.0.0.1 flux.platform.lan -127.0.0.1 headlamp.platform.lan 127.0.0.1 oauth2-proxy.platform.lan 127.0.0.1 platform.lan 127.0.0.1 whoami.platform.lan diff --git a/k8s/bases/apps/headlamp/README.md b/k8s/bases/apps/headlamp/README.md deleted file mode 100644 index f8387b1..0000000 --- a/k8s/bases/apps/headlamp/README.md +++ /dev/null @@ -1,6 +0,0 @@ -# Headlamp - -Headlamp is a lightweight Kubernetes UI which supports multiple clusters and is extensible at its core. - -- [Documentation](https://headlamp.dev/docs/latest/) -- [Helm Chart](https://github.com/headlamp-k8s/headlamp/tree/main/charts/headlamp) diff --git a/k8s/bases/apps/headlamp/cilium-network-policy.yaml b/k8s/bases/apps/headlamp/cilium-network-policy.yaml deleted file mode 100644 index 6d827e7..0000000 --- a/k8s/bases/apps/headlamp/cilium-network-policy.yaml +++ /dev/null @@ -1,38 +0,0 @@ -apiVersion: cilium.io/v2 -kind: CiliumNetworkPolicy -metadata: - name: allow-headlamp - namespace: headlamp -spec: - endpointSelector: {} - ingress: - # KEDA interceptor proxy routes traffic from gateway - - fromEndpoints: - - matchLabels: - k8s:io.kubernetes.pod.namespace: keda - toPorts: - - ports: - - port: "4466" - protocol: TCP - egress: - # Kube API for dashboard - - toEntities: - - kube-apiserver - # Dex OIDC, external services (via Cloudflare proxy) - - toEntities: - - world - toPorts: - - ports: - - port: "443" - protocol: TCP - # DNS resolution - - toEndpoints: - - matchLabels: - k8s:io.kubernetes.pod.namespace: kube-system - k8s-app: kube-dns - toPorts: - - ports: - - port: "53" - protocol: UDP - - port: "53" - protocol: TCP diff --git a/k8s/bases/apps/headlamp/external-secret.yaml b/k8s/bases/apps/headlamp/external-secret.yaml deleted file mode 100644 index e1daa82..0000000 --- a/k8s/bases/apps/headlamp/external-secret.yaml +++ /dev/null @@ -1,18 +0,0 @@ -apiVersion: external-secrets.io/v1 -kind: ExternalSecret -metadata: - name: headlamp-oidc - namespace: headlamp -spec: - refreshInterval: 1h - secretStoreRef: - name: openbao - kind: ClusterSecretStore - target: - name: headlamp-oidc - creationPolicy: Owner - data: - - secretKey: client-secret - remoteRef: - key: infrastructure/oidc/dex - property: client-secret diff --git a/k8s/bases/apps/headlamp/helm-release.yaml b/k8s/bases/apps/headlamp/helm-release.yaml deleted file mode 100644 index 5db2184..0000000 --- a/k8s/bases/apps/headlamp/helm-release.yaml +++ /dev/null @@ -1,156 +0,0 @@ -apiVersion: helm.toolkit.fluxcd.io/v2 -kind: HelmRelease -metadata: - name: headlamp - namespace: headlamp - labels: - helm.toolkit.fluxcd.io/helm-test: enabled - helm.toolkit.fluxcd.io/remediation: enabled -spec: - interval: 2m - timeout: 10m - chart: - spec: - chart: headlamp - version: 0.42.0 - sourceRef: - kind: HelmRepository - name: headlamp - valuesFrom: - - kind: Secret - name: headlamp-oidc - valuesKey: client-secret - targetPath: config.oidc.clientSecret - optional: true - postRenderers: - - kustomize: - patches: - - target: - kind: Deployment - name: headlamp - patch: | - - op: add - path: /metadata/annotations/secret.reloader.stakater.com~1reload - value: headlamp-oidc - - op: add - path: /spec/strategy - value: - type: RollingUpdate - rollingUpdate: - maxSurge: 0 - maxUnavailable: 1 - - op: add - path: /spec/template/spec/volumes/- - value: - name: tmp-dir - emptyDir: {} - - op: add - path: /spec/template/spec/volumes/- - value: - name: headlamp-config - emptyDir: {} - - op: add - path: /spec/template/spec/containers/0/volumeMounts/- - value: - name: tmp-dir - mountPath: /tmp - - op: add - path: /spec/template/spec/containers/0/volumeMounts/- - value: - name: headlamp-config - mountPath: /home/headlamp/.config - - op: add - path: /spec/template/spec/containers/1/volumeMounts/- - value: - name: tmp-dir - mountPath: /tmp - # Chart hardcodes liveness/readiness with K8s defaults - # (timeoutSeconds: 1, failureThreshold: 3, periodSeconds: 10). - # Headlamp is KEDA-scaled to 0 in prod; every cold start logs - # 1-3 Unhealthy probe warnings while the Go binary initialises. - # Add a startupProbe so liveness/readiness are gated until the - # main container is actually serving. - # initialDelaySeconds skips past the cold-start window so the - # first probe lands on a serving container — zero failure - # events during the merge-queue's 90s steady-state Warning - # check. - - op: add - path: /spec/template/spec/containers/0/startupProbe - value: - httpGet: - path: / - port: http - initialDelaySeconds: 20 - periodSeconds: 5 - timeoutSeconds: 3 - failureThreshold: 12 # 60s grace beyond initial delay - - target: - kind: Deployment - name: headlamp - patch: | - apiVersion: apps/v1 - kind: Deployment - metadata: - name: headlamp - spec: - template: - spec: - volumes: - - name: plugins-dir - emptyDir: null - persistentVolumeClaim: - claimName: headlamp - # https://github.com/kubernetes-sigs/headlamp/blob/main/charts/headlamp/values.yaml - values: - replicaCount: ${headlamp_replicas:=1} - podDisruptionBudget: - enabled: true - minAvailable: 0 - maxUnavailable: null - topologySpreadConstraints: - - maxSkew: 1 - topologyKey: kubernetes.io/hostname - whenUnsatisfiable: ScheduleAnyway - labelSelector: - matchLabels: - app.kubernetes.io/name: headlamp - app.kubernetes.io/instance: headlamp - config: - watchPlugins: true - oidc: - clientID: public-client - issuerURL: https://dex.${domain} - scopes: "profile,email,groups" - pluginsManager: - enabled: true - configContent: | - plugins: - - name: headlamp_flux - source: https://artifacthub.io/packages/headlamp/headlamp-plugins/headlamp_flux - version: 0.6.0 - - name: headlamp_cert-manager - source: https://artifacthub.io/packages/headlamp/headlamp-plugins/headlamp_cert-manager - version: 0.1.0 - - name: headlamp_keda - source: https://artifacthub.io/packages/headlamp/headlamp-plugins/headlamp_keda - version: 0.1.1-beta - - name: headlamp_opencost - source: https://artifacthub.io/packages/headlamp/headlamp-plugins/headlamp_opencost - version: 0.1.3 - - name: headlamp_kubescape - source: https://artifacthub.io/packages/headlamp/kubescape-headlamp-plugin/headlamp_kubescape - version: 0.11.2 - installOptions: - parallel: true - maxConcurrent: 3 - persistentVolumeClaim: - enabled: false - podSecurityContext: - fsGroup: 101 - runAsUser: 100 - runAsGroup: 101 - securityContext: - runAsUser: 100 - runAsGroup: 101 - ingress: - enabled: false diff --git a/k8s/bases/apps/headlamp/helm-repository.yaml b/k8s/bases/apps/headlamp/helm-repository.yaml deleted file mode 100644 index b643968..0000000 --- a/k8s/bases/apps/headlamp/helm-repository.yaml +++ /dev/null @@ -1,7 +0,0 @@ -apiVersion: source.toolkit.fluxcd.io/v1 -kind: HelmRepository -metadata: - name: headlamp - namespace: headlamp -spec: - url: https://kubernetes-sigs.github.io/headlamp/ diff --git a/k8s/bases/apps/headlamp/http-route.yaml b/k8s/bases/apps/headlamp/http-route.yaml deleted file mode 100644 index cb9ea29..0000000 --- a/k8s/bases/apps/headlamp/http-route.yaml +++ /dev/null @@ -1,31 +0,0 @@ -apiVersion: gateway.networking.k8s.io/v1 -kind: HTTPRoute -metadata: - name: headlamp - namespace: headlamp - annotations: - gethomepage.dev/enabled: "true" - gethomepage.dev/name: Headlamp - gethomepage.dev/description: A web UI for managing Kubernetes clusters. - gethomepage.dev/group: Kubernetes - gethomepage.dev/icon: https://pbs.twimg.com/profile_images/1537480067227566080/waXG0X7n_400x400.jpg - gethomepage.dev/href: https://headlamp.${domain} - gethomepage.dev/pod-selector: app.kubernetes.io/name=headlamp -spec: - parentRefs: - - name: platform - namespace: kube-system - sectionName: https - hostnames: - - headlamp.${domain} - rules: - - backendRefs: - - name: keda-add-ons-http-interceptor-proxy - namespace: keda - port: 8080 - filters: - - type: ResponseHeaderModifier - responseHeaderModifier: - set: - - name: Strict-Transport-Security - value: max-age=63072000; includeSubDomains; preload diff --git a/k8s/bases/apps/headlamp/http-scaled-object.yaml b/k8s/bases/apps/headlamp/http-scaled-object.yaml deleted file mode 100644 index 0920854..0000000 --- a/k8s/bases/apps/headlamp/http-scaled-object.yaml +++ /dev/null @@ -1,22 +0,0 @@ ---- -apiVersion: http.keda.sh/v1alpha1 -kind: HTTPScaledObject -metadata: - name: headlamp - namespace: headlamp -spec: - hosts: - - headlamp.${domain} - scaleTargetRef: - name: headlamp - service: headlamp - port: 80 - replicas: - min: 0 - max: 1 # OIDC login stores state in-memory per pod; limit to 1 so the callback always lands on the same pod. - scaledownPeriod: 300 - scalingMetric: - requestRate: - granularity: 1s - targetValue: 100 - window: 1m diff --git a/k8s/bases/apps/headlamp/kustomization.yaml b/k8s/bases/apps/headlamp/kustomization.yaml deleted file mode 100644 index c168bc0..0000000 --- a/k8s/bases/apps/headlamp/kustomization.yaml +++ /dev/null @@ -1,14 +0,0 @@ -apiVersion: kustomize.config.k8s.io/v1beta1 -kind: Kustomization -resources: - - namespace.yaml - - external-secret.yaml - - persistent-volume-claim.yaml - - helm-release.yaml - - helm-repository.yaml - - http-route.yaml - - http-scaled-object.yaml - - cilium-network-policy.yaml -components: - - ../../../components/helmrelease-flux-defaults/ - - ../../../components/helmrelease-drift-detection/ diff --git a/k8s/bases/apps/headlamp/namespace.yaml b/k8s/bases/apps/headlamp/namespace.yaml deleted file mode 100644 index e832f82..0000000 --- a/k8s/bases/apps/headlamp/namespace.yaml +++ /dev/null @@ -1,7 +0,0 @@ -apiVersion: v1 -kind: Namespace -metadata: - name: headlamp - labels: - pod-security.kubernetes.io/enforce: restricted - pod-security.kubernetes.io/enforce-version: latest diff --git a/k8s/bases/apps/headlamp/persistent-volume-claim.yaml b/k8s/bases/apps/headlamp/persistent-volume-claim.yaml deleted file mode 100644 index b3e286b..0000000 --- a/k8s/bases/apps/headlamp/persistent-volume-claim.yaml +++ /dev/null @@ -1,11 +0,0 @@ -apiVersion: v1 -kind: PersistentVolumeClaim -metadata: - name: headlamp - namespace: headlamp -spec: - accessModes: - - ReadWriteOnce - resources: - requests: - storage: 256Mi diff --git a/k8s/bases/apps/kustomization.yaml b/k8s/bases/apps/kustomization.yaml index 1480e33..84443e9 100644 --- a/k8s/bases/apps/kustomization.yaml +++ b/k8s/bases/apps/kustomization.yaml @@ -1,6 +1,5 @@ apiVersion: kustomize.config.k8s.io/v1beta1 kind: Kustomization resources: -- headlamp/ - homepage/ - whoami/ diff --git a/k8s/bases/infrastructure/cluster-security-exceptions/controller-rbac.yaml b/k8s/bases/infrastructure/cluster-security-exceptions/controller-rbac.yaml index 30d5fcb..7111549 100644 --- a/k8s/bases/infrastructure/cluster-security-exceptions/controller-rbac.yaml +++ b/k8s/bases/infrastructure/cluster-security-exceptions/controller-rbac.yaml @@ -2,9 +2,9 @@ # Exempt controllers and middleware that legitimately need broad RBAC. # Infrastructure controllers (Flux, Kyverno, cert-manager, CNPG, Velero, # KEDA, monitoring, KubeVirt, CDI, Longhorn) and platform middleware -# (headlamp cluster dashboard, homepage service discovery, dex identity, -# oauth2-proxy auth, opencost cost monitoring, reloader config watcher) -# all require cluster-wide read or read/write access to function correctly. +# (homepage service discovery, dex identity, oauth2-proxy auth, opencost +# cost monitoring, reloader config watcher) all require cluster-wide read +# or read/write access to function correctly. apiVersion: kubescape.io/v1beta1 kind: ClusterSecurityException metadata: @@ -16,9 +16,8 @@ spec: (Flux), policy engines (Kyverno), certificate managers (cert-manager), database operators (CNPG), backup controllers (Velero), autoscalers (KEDA), monitoring (Prometheus/opencost), virtualisation (KubeVirt, - CDI), storage (Longhorn), cluster dashboards (headlamp), service - discovery (homepage), identity (dex), auth proxies (oauth2-proxy), - and config watchers (reloader). + CDI), storage (Longhorn), service discovery (homepage), identity + (dex), auth proxies (oauth2-proxy), and config watchers (reloader). posture: - controlID: C-0053 action: ignore @@ -60,7 +59,6 @@ spec: - longhorn-system - external-dns - oauth2-proxy - - headlamp - kubelet-serving-cert-approver - opencost - reloader diff --git a/k8s/bases/infrastructure/cluster-security-exceptions/health-probes.yaml b/k8s/bases/infrastructure/cluster-security-exceptions/health-probes.yaml index f1a2fbd..a83bd39 100644 --- a/k8s/bases/infrastructure/cluster-security-exceptions/health-probes.yaml +++ b/k8s/bases/infrastructure/cluster-security-exceptions/health-probes.yaml @@ -3,9 +3,9 @@ # Many controllers, sidecars, and init containers do not expose health # endpoints. Adding probes to upstream charts is impractical and they # typically have their own readiness mechanisms (leader election, watch -# loops). App namespaces (headlamp, homepage) are included because their -# charts deploy init containers and sidecars that lack probe endpoints, -# while main application containers have probes configured. +# loops). The homepage app namespace is included because its chart +# deploys init containers and sidecars that lack probe endpoints, while +# the main application container has probes configured. apiVersion: kubescape.io/v1beta1 kind: ClusterSecurityException metadata: @@ -42,7 +42,6 @@ spec: - external-dns - kubelet-serving-cert-approver - opencost - - headlamp - kubevirt - cdi - dex diff --git a/k8s/bases/infrastructure/cluster-security-exceptions/service-account-tokens.yaml b/k8s/bases/infrastructure/cluster-security-exceptions/service-account-tokens.yaml index f4788aa..90d4c41 100644 --- a/k8s/bases/infrastructure/cluster-security-exceptions/service-account-tokens.yaml +++ b/k8s/bases/infrastructure/cluster-security-exceptions/service-account-tokens.yaml @@ -2,8 +2,7 @@ # Exempt service account token auto-mount controls for infrastructure # and middleware namespaces. Controllers and platform middleware that # manage cluster resources (Flux, Kyverno, cert-manager, KEDA, VPA, -# headlamp dashboard, homepage service discovery, etc.) genuinely need -# API tokens to function. +# homepage service discovery, etc.) genuinely need API tokens to function. apiVersion: kubescape.io/v1beta1 kind: ClusterSecurityException metadata: @@ -12,10 +11,10 @@ spec: reason: >- Infrastructure controllers and platform middleware need Kubernetes API access via SA tokens. This includes GitOps controllers, policy - engines, certificate managers, autoscalers, monitoring, cluster - dashboards (headlamp), service discovery (homepage), identity - providers (dex), auth proxies (oauth2-proxy), and config watchers - (reloader). The platform uses RBAC to restrict what each SA can do. + engines, certificate managers, autoscalers, monitoring, service + discovery (homepage), identity providers (dex), auth proxies + (oauth2-proxy), and config watchers (reloader). The platform uses + RBAC to restrict what each SA can do. posture: - controlID: C-0261 action: ignore @@ -56,5 +55,4 @@ spec: - opencost - dex - homepage - - headlamp - oauth2-proxy diff --git a/k8s/bases/infrastructure/controllers/dex/cilium-network-policy.yaml b/k8s/bases/infrastructure/controllers/dex/cilium-network-policy.yaml index cc30cf7..25d4101 100644 --- a/k8s/bases/infrastructure/controllers/dex/cilium-network-policy.yaml +++ b/k8s/bases/infrastructure/controllers/dex/cilium-network-policy.yaml @@ -13,15 +13,6 @@ spec: - ports: - port: "5556" protocol: TCP - # OIDC token exchange from Headlamp backend (hairpin via gateway ClusterIP - # preserves the originating pod identity instead of using the 'ingress' entity) - - fromEndpoints: - - matchLabels: - k8s:io.kubernetes.pod.namespace: headlamp - toPorts: - - ports: - - port: "5556" - protocol: TCP # gRPC from oauth2-proxy - fromEndpoints: - matchLabels: @@ -33,7 +24,7 @@ spec: - port: "5558" protocol: TCP # OIDC token + userinfo exchange from Grafana (native auth.generic_oauth). - # Same in-cluster path as headlamp/oauth2-proxy: Grafana's server-side + # Same in-cluster path as oauth2-proxy: Grafana's server-side # calls hit the Dex Service directly and arrive with the monitoring pod # identity, so they must be allowed explicitly here. - fromEndpoints: diff --git a/k8s/bases/infrastructure/controllers/dex/helm-release.yaml b/k8s/bases/infrastructure/controllers/dex/helm-release.yaml index c6c10d2..c1fb424 100644 --- a/k8s/bases/infrastructure/controllers/dex/helm-release.yaml +++ b/k8s/bases/infrastructure/controllers/dex/helm-release.yaml @@ -66,7 +66,6 @@ spec: secret: ${dex_client_secret} redirectURIs: - "https://oauth2-proxy.${domain}/oauth2/callback" - - "https://headlamp.${domain}/oidc-callback" - "https://budget.${domain}/oidc/callback" - "https://vault.${domain}/ui/vault/auth/oidc/oidc/callback" - "https://grafana.${domain}/login/generic_oauth" diff --git a/k8s/bases/infrastructure/controllers/keda-http-add-on/reference-grant.yaml b/k8s/bases/infrastructure/controllers/keda-http-add-on/reference-grant.yaml index 684e018..9004c0e 100644 --- a/k8s/bases/infrastructure/controllers/keda-http-add-on/reference-grant.yaml +++ b/k8s/bases/infrastructure/controllers/keda-http-add-on/reference-grant.yaml @@ -6,9 +6,6 @@ metadata: namespace: keda spec: from: - - group: gateway.networking.k8s.io - kind: HTTPRoute - namespace: headlamp - group: gateway.networking.k8s.io kind: HTTPRoute namespace: whoami diff --git a/k8s/bases/infrastructure/controllers/keda/cilium-network-policy.yaml b/k8s/bases/infrastructure/controllers/keda/cilium-network-policy.yaml index 159b416..9c2be31 100644 --- a/k8s/bases/infrastructure/controllers/keda/cilium-network-policy.yaml +++ b/k8s/bases/infrastructure/controllers/keda/cilium-network-policy.yaml @@ -41,13 +41,6 @@ spec: - matchLabels: k8s:io.kubernetes.pod.namespace: keda # HTTP interceptor -> backend services in KEDA-managed namespaces - - toEndpoints: - - matchLabels: - k8s:io.kubernetes.pod.namespace: headlamp - toPorts: - - ports: - - port: "4466" - protocol: TCP - toEndpoints: - matchLabels: k8s:io.kubernetes.pod.namespace: whoami diff --git a/k8s/bases/infrastructure/controllers/opencost/cilium-network-policy.yaml b/k8s/bases/infrastructure/controllers/opencost/cilium-network-policy.yaml index ba31de0..c2c6e59 100644 --- a/k8s/bases/infrastructure/controllers/opencost/cilium-network-policy.yaml +++ b/k8s/bases/infrastructure/controllers/opencost/cilium-network-policy.yaml @@ -16,12 +16,11 @@ spec: protocol: TCP - port: "9090" protocol: TCP - # Headlamp's OpenCost plugin queries the cost API/UI through the - # kube-apiserver service proxy (/api/v1/namespaces/opencost/services/ - # opencost:http-ui/proxy/...), so the request reaches OpenCost from the - # API server. Its source address varies by CNI path (direct, SNAT'd via - # host, or coming from another node), so we allow the three - # Cilium-managed entities the API server may appear as. + # Cost API/UI requests made through the kube-apiserver service proxy + # (/api/v1/namespaces/opencost/services/opencost:http-ui/proxy/...) + # reach OpenCost from the API server. Its source address varies by CNI + # path (direct, SNAT'd via host, or coming from another node), so we + # allow the three Cilium-managed entities the API server may appear as. - fromEntities: - kube-apiserver - host diff --git a/k8s/bases/infrastructure/controllers/opencost/helm-release.yaml b/k8s/bases/infrastructure/controllers/opencost/helm-release.yaml index 9928105..47063b8 100644 --- a/k8s/bases/infrastructure/controllers/opencost/helm-release.yaml +++ b/k8s/bases/infrastructure/controllers/opencost/helm-release.yaml @@ -27,7 +27,7 @@ spec: # deploy gate (.github/actions/check-event-warnings) even though it is # harmless. Two mitigations: # 1. maxUnavailable: 0 — surge the new pod to Ready before the old is - # terminated (matches the homepage/headlamp convention). + # terminated (matches the homepage convention). # 2. preStop.sleep — keep the opencost container serving :9003 for 15s # after it is marked for deletion, so kubelet's probes during drain # land on a live endpoint instead of a torn-down route. Native sleep @@ -127,7 +127,7 @@ spec: # scheduler reserves real baseline and the pod isn't first to be # evicted under node memory pressure. memory: 128Mi - # 256Mi OOM-killed when Headlamp issued the 14d allocation queries — + # 256Mi OOM-killed under 14-day allocation queries — # the cost-model engine holds the full window in memory while # aggregating. 512Mi gives ~4× headroom over the request and is # still well under the chart's 1Gi default. diff --git a/k8s/bases/infrastructure/external-secrets/grafana-oidc.yaml b/k8s/bases/infrastructure/external-secrets/grafana-oidc.yaml index 7da0676..a9c28f9 100644 --- a/k8s/bases/infrastructure/external-secrets/grafana-oidc.yaml +++ b/k8s/bases/infrastructure/external-secrets/grafana-oidc.yaml @@ -15,7 +15,7 @@ spec: data: # Dex 'public-client' secret. Grafana does native OIDC against Dex # (auth.generic_oauth); this is the same shared confidential-client - # secret Headlamp/oauth2-proxy use. + # secret oauth2-proxy uses. - secretKey: client-secret remoteRef: key: infrastructure/oidc/dex diff --git a/k8s/clusters/prod/bootstrap/variables-cluster-config-map.yaml b/k8s/clusters/prod/bootstrap/variables-cluster-config-map.yaml index 8f5ae23..701ae62 100644 --- a/k8s/clusters/prod/bootstrap/variables-cluster-config-map.yaml +++ b/k8s/clusters/prod/bootstrap/variables-cluster-config-map.yaml @@ -38,9 +38,7 @@ data: # KEDA-managed apps: HelmRelease replicas are the initial value only; # KEDA HTTPScaledObject overrides at runtime. Setting the initial to 1 # saves memory until the first KEDA reconciliation. - # headlamp: min 0, max 1 (OIDC state in-memory — must stay single-pod) # whoami: min 0, max 2 - headlamp_replicas: "1" whoami_replicas: "1" hubble_ui_replicas: "1" # Auth chain: 2 replicas for HA during VPA evictions (prevents 502s). diff --git a/k8s/components/observability-coroot/remove-kps-ui/kustomization.yaml b/k8s/components/observability-coroot/remove-kps-ui/kustomization.yaml index 5dd1ec4..04bf9ff 100644 --- a/k8s/components/observability-coroot/remove-kps-ui/kustomization.yaml +++ b/k8s/components/observability-coroot/remove-kps-ui/kustomization.yaml @@ -83,20 +83,20 @@ patches: # first, after remove-opencost-references took OpenCost's), then Grafana's own. patch: |- - op: test - path: /spec/values/config/staticClients/0/redirectURIs/9 + path: /spec/values/config/staticClients/0/redirectURIs/8 value: "https://prometheus.${domain}/oauth2/callback" - op: remove - path: /spec/values/config/staticClients/0/redirectURIs/9 + path: /spec/values/config/staticClients/0/redirectURIs/8 - op: test - path: /spec/values/config/staticClients/0/redirectURIs/6 + path: /spec/values/config/staticClients/0/redirectURIs/5 value: "https://alertmanager.${domain}/oauth2/callback" - op: remove - path: /spec/values/config/staticClients/0/redirectURIs/6 + path: /spec/values/config/staticClients/0/redirectURIs/5 - op: test - path: /spec/values/config/staticClients/0/redirectURIs/4 + path: /spec/values/config/staticClients/0/redirectURIs/3 value: "https://grafana.${domain}/login/generic_oauth" - op: remove - path: /spec/values/config/staticClients/0/redirectURIs/4 + path: /spec/values/config/staticClients/0/redirectURIs/3 - target: version: v1 kind: ConfigMap diff --git a/k8s/components/observability-coroot/remove-opencost-references/kustomization.yaml b/k8s/components/observability-coroot/remove-opencost-references/kustomization.yaml index 1e95522..210e2d6 100644 --- a/k8s/components/observability-coroot/remove-opencost-references/kustomization.yaml +++ b/k8s/components/observability-coroot/remove-opencost-references/kustomization.yaml @@ -6,54 +6,6 @@ kind: Component # from a layer are ignored; the executable contract rejects surviving OpenCost # references or an incomplete oauth2-proxy boundary. patches: - - target: - group: helm.toolkit.fluxcd.io - version: v2 - kind: HelmRelease - name: headlamp - namespace: headlamp - patch: | - - op: test - path: /spec/values/pluginsManager/configContent - value: | - plugins: - - name: headlamp_flux - source: https://artifacthub.io/packages/headlamp/headlamp-plugins/headlamp_flux - version: 0.6.0 - - name: headlamp_cert-manager - source: https://artifacthub.io/packages/headlamp/headlamp-plugins/headlamp_cert-manager - version: 0.1.0 - - name: headlamp_keda - source: https://artifacthub.io/packages/headlamp/headlamp-plugins/headlamp_keda - version: 0.1.1-beta - - name: headlamp_opencost - source: https://artifacthub.io/packages/headlamp/headlamp-plugins/headlamp_opencost - version: 0.1.3 - - name: headlamp_kubescape - source: https://artifacthub.io/packages/headlamp/kubescape-headlamp-plugin/headlamp_kubescape - version: 0.11.2 - installOptions: - parallel: true - maxConcurrent: 3 - - op: replace - path: /spec/values/pluginsManager/configContent - value: | - plugins: - - name: headlamp_flux - source: https://artifacthub.io/packages/headlamp/headlamp-plugins/headlamp_flux - version: 0.6.0 - - name: headlamp_cert-manager - source: https://artifacthub.io/packages/headlamp/headlamp-plugins/headlamp_cert-manager - version: 0.1.0 - - name: headlamp_keda - source: https://artifacthub.io/packages/headlamp/headlamp-plugins/headlamp_keda - version: 0.1.1-beta - - name: headlamp_kubescape - source: https://artifacthub.io/packages/headlamp/kubescape-headlamp-plugin/headlamp_kubescape - version: 0.11.2 - installOptions: - parallel: true - maxConcurrent: 3 - target: version: v1 kind: ConfigMap @@ -284,10 +236,10 @@ patches: name: controller-rbac patch: | - op: test - path: /spec/match/namespaceSelector/matchExpressions/0/values/16 + path: /spec/match/namespaceSelector/matchExpressions/0/values/15 value: opencost - op: replace - path: /spec/match/namespaceSelector/matchExpressions/0/values/16 + path: /spec/match/namespaceSelector/matchExpressions/0/values/15 value: observability - op: test path: /spec/reason @@ -297,9 +249,8 @@ patches: (Flux), policy engines (Kyverno), certificate managers (cert-manager), database operators (CNPG), backup controllers (Velero), autoscalers (KEDA), monitoring (Prometheus/opencost), virtualisation (KubeVirt, - CDI), storage (Longhorn), cluster dashboards (headlamp), service - discovery (homepage), identity (dex), auth proxies (oauth2-proxy), - and config watchers (reloader). + CDI), storage (Longhorn), service discovery (homepage), identity + (dex), auth proxies (oauth2-proxy), and config watchers (reloader). - op: replace path: /spec/reason value: >- @@ -308,9 +259,8 @@ patches: (Flux), policy engines (Kyverno), certificate managers (cert-manager), database operators (CNPG), backup controllers (Velero), autoscalers (KEDA), monitoring (Prometheus/Coroot), virtualisation (KubeVirt, CDI), - storage (Longhorn), cluster dashboards (headlamp), service discovery - (homepage), identity (dex), auth proxies (oauth2-proxy), and config - watchers (reloader). + storage (Longhorn), service discovery (homepage), identity (dex), auth + proxies (oauth2-proxy), and config watchers (reloader). - target: group: kubescape.io version: v1beta1 @@ -344,7 +294,7 @@ patches: namespace: dex patch: |- - op: test - path: /spec/values/config/staticClients/0/redirectURIs/9 + path: /spec/values/config/staticClients/0/redirectURIs/8 value: "https://opencost.${domain}/oauth2/callback" - op: remove - path: /spec/values/config/staticClients/0/redirectURIs/9 + path: /spec/values/config/staticClients/0/redirectURIs/8 diff --git a/k8s/providers/docker/apps/headlamp/patches/trust-platform-ca.yaml b/k8s/providers/docker/apps/headlamp/patches/trust-platform-ca.yaml deleted file mode 100644 index ee84f05..0000000 --- a/k8s/providers/docker/apps/headlamp/patches/trust-platform-ca.yaml +++ /dev/null @@ -1,18 +0,0 @@ -apiVersion: helm.toolkit.fluxcd.io/v2 -kind: HelmRelease -metadata: - name: headlamp - namespace: headlamp -spec: - values: - config: - extraArgs: - - "-oidc-ca-file=/etc/ssl/certs/platform-ca/ca-certificates.crt" - volumes: - - name: trust-bundle - configMap: - name: platform-ca - volumeMounts: - - name: trust-bundle - mountPath: /etc/ssl/certs/platform-ca - readOnly: true diff --git a/k8s/providers/docker/apps/kustomization.yaml b/k8s/providers/docker/apps/kustomization.yaml index c873a44..0b44104 100644 --- a/k8s/providers/docker/apps/kustomization.yaml +++ b/k8s/providers/docker/apps/kustomization.yaml @@ -2,12 +2,5 @@ apiVersion: kustomize.config.k8s.io/v1beta1 kind: Kustomization resources: - - ../../../bases/apps/headlamp/ - ../../../bases/apps/homepage/ - ../../../bases/apps/whoami/ -patches: - - target: - kind: HelmRelease - name: headlamp - namespace: headlamp - path: headlamp/patches/trust-platform-ca.yaml diff --git a/k8s/providers/hetzner/infrastructure/cluster-issuers/cloudflare-origin-ca-trust-bundle.yaml b/k8s/providers/hetzner/infrastructure/cluster-issuers/cloudflare-origin-ca-trust-bundle.yaml index 3ab8335..92d273b 100644 --- a/k8s/providers/hetzner/infrastructure/cluster-issuers/cloudflare-origin-ca-trust-bundle.yaml +++ b/k8s/providers/hetzner/infrastructure/cluster-issuers/cloudflare-origin-ca-trust-bundle.yaml @@ -1,6 +1,6 @@ # Distributes the Cloudflare Origin ECC CA root + system CAs to all namespaces # as the ConfigMap "cloudflare-origin-ca". Required so that in-cluster clients -# (e.g. Headlamp) can verify TLS connections to the Cilium gateway when CoreDNS +# can verify TLS connections to the Cilium gateway when CoreDNS # resolves *.platform.example.com directly to the in-cluster service instead # of routing through Cloudflare's publicly-trusted edge. apiVersion: trust.cert-manager.io/v1alpha1 diff --git a/scripts/validate-observability-option.sh b/scripts/validate-observability-option.sh index 64be86b..324481b 100755 --- a/scripts/validate-observability-option.sh +++ b/scripts/validate-observability-option.sh @@ -720,19 +720,6 @@ assert_opencost_resources_absent() { fi } -# Verifies the exact number of rendered resources that reference OpenCost. -assert_opencost_reference_count() { - local rendered_path="$1" - local expected="$2" - local actual - - actual="$(opencost_reference_count "${rendered_path}")" - if [[ "${actual}" != "${expected}" ]]; then - echo "expected ${expected} OpenCost-referencing resource(s) in ${rendered_path}, found ${actual}" >&2 - return 1 - fi -} - # Verifies that both OpenCost resources and serialized references are absent. assert_opencost_absent() { local rendered_path="$1" @@ -1059,8 +1046,13 @@ for rendered_path in "${local_infrastructure_default}" "${prod_infrastructure_de assert_security_exception_namespace_count "${rendered_path}" service-account-tokens observability 0 assert_heartbeat_policy_exclusion_absent "${rendered_path}" done -assert_opencost_reference_count "${local_apps_default}" 1 -assert_opencost_reference_count "${prod_apps_default}" 1 +# No default app consumes OpenCost. The demo apps prove each absence check +# examined a real apps render rather than an empty one. +for rendered_path in "${local_apps_default}" "${prod_apps_default}"; do + assert_resource_count "${rendered_path}" HelmRelease homepage homepage 1 + assert_resource_count "${rendered_path}" HelmRelease whoami whoami 1 + assert_opencost_absent "${rendered_path}" +done render k8s/providers/docker/infrastructure-controllers-coroot/ "${docker_controllers}" render k8s/providers/docker/infrastructure-coroot/ "${docker_infrastructure}" @@ -1305,7 +1297,7 @@ for config_path in ksail.yaml ksail.prod.yaml; do done for documented_boundary in \ "This profile is transitional." \ - "removes OpenCost and its Headlamp" \ + "It enables Coroot, removes OpenCost," \ "retires the legacy Loki and Alloy log path" \ "stages one hardened \`cluster-heartbeat\` CronJob" \ "uses a separate optional \`CLUSTER_HEARTBEAT_URL\` secret" \