Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 6 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -61,6 +61,12 @@ demo/
# the overlay root without a `../../` parent traversal (LoadRestrictionsRootOnly).
deploy/kind/prometheus/keystone-operator.json

# Build artifact: staged at deploy time by hack/dizzy.sh stage-dashboards from
# the pinned dizzy release tarball (WITH_DIZZY=true) so the dizzy overlay's
# configMapGenerator can reference the dashboard JSONs from the overlay root
# without a `../../` parent traversal (LoadRestrictionsRootOnly).
deploy/kind/dizzy/dashboards/

# Vendored Helm subchart dependencies. The operator charts depend on the
# operator-library library chart via a file:// path; `helm dependency build`
# vendors it into charts/ from that local source. Chart.lock is committed; the
Expand Down
25 changes: 25 additions & 0 deletions Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -594,6 +594,31 @@ stage-prometheus-dashboard:
deploy-infra:
hack/deploy-infra.sh

.PHONY: dizzy-keystone dizzy-glance
# dizzy-keystone and dizzy-glance run dizzy's chaos churn soak (a 5-minute
# small profile by default) against the quick-start ControlPlane, exporting
# metrics to the dizzy VictoriaMetrics; watch them at
# https://dizzy.127-0-0-1.nip.io.
#
# Variables:
# DIZZY_SCENARIO alternate scenario file.
# DIZZY_ARGS extra dizzy flags, space-separated (e.g. --duration 30m).
# DIZZY_VERSION pin override; the pin itself lives only in hack/dizzy.sh
# so Renovate has exactly one string to bump.
# DIZZY_SECRET ControlPlane admin Secret name override.
# DIZZY_CP_NAMESPACE ControlPlane admin Secret namespace override.
#
# The three preflights are kept separate so the failure modes stay
# distinguishable — the kubectl/cluster-reachability failure is not conflated
# with the dizzy-stack-not-installed failure nor with the missing
# ControlPlane-admin-Secret failure — see review pattern
# .planwerk/review_patterns/distinguish-collapsed-failure-modes-in-preflight-checks.md
dizzy-keystone dizzy-glance: dizzy-%:
@kubectl version --request-timeout=2s >/dev/null 2>&1 || { echo 'kubectl is not configured or no cluster is reachable' >&2; exit 1; }
@kubectl get ns dizzy >/dev/null 2>&1 || { echo 'the dizzy stack is not installed; run `WITH_DIZZY=true make deploy-infra` first' >&2; exit 1; }
@kubectl get secret "$${DIZZY_SECRET:-controlplane-keystone-admin-credentials}" -n "$${DIZZY_CP_NAMESPACE:-openstack}" >/dev/null 2>&1 || { echo 'no ControlPlane admin Secret found; deploy the quick-start ControlPlane and wait for Ready first' >&2; exit 1; }
hack/dizzy.sh chaos $*

.PHONY: refresh-operator-digests
# refresh-operator-digests re-resolves the digest behind the self-built
# operators' :latest images (keystone/c5c3/horizon), refreshes the
Expand Down
41 changes: 38 additions & 3 deletions deploy/kind/base/openstack-gateway.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -3,9 +3,10 @@
# SPDX-License-Identifier: Apache-2.0

# Gateway API wiring for the kind Quick Start — `GatewayClass/envoy`
# plus a three-listener HTTPS Gateway on port 443
# (`keystone.127-0-0-1.nip.io`, `horizon.127-0-0-1.nip.io`, and
# `glance.127-0-0-1.nip.io`, routed by SNI) terminating TLS with
# plus a four-listener HTTPS Gateway on port 443
# (`keystone.127-0-0-1.nip.io`, `horizon.127-0-0-1.nip.io`,
# `glance.127-0-0-1.nip.io`, and `dizzy.127-0-0-1.nip.io`, routed by
# SNI) terminating TLS with
# self-signed certificates issued by the existing `selfsigned-cluster-issuer`
# ClusterIssuer (the same issuer OpenBao uses; see
# `deploy/flux-system/infrastructure/cluster-issuer.yaml`).
Expand Down Expand Up @@ -109,3 +110,37 @@ spec:
allowedRoutes:
namespaces:
from: Same
# Fourth HTTPS listener for the Grafana UI of the opt-in dizzy
# observability overlay (`deploy/kind/dizzy/`, gated on
# WITH_DIZZY=true in hack/deploy-infra.sh). It shares port 443 with
# the listeners above, routed by SNI on its distinct `hostname` and
# terminating with its own certificate; the envoy-nodeport
# StrategicMerge patch keys by `port: 443` and is untouched by
# another listener on the same port.
#
# This is the repo's FIRST listener admitting routes from another
# namespace (`allowedRoutes.namespaces.from: Selector` instead of
# `Same`): the static `dizzy-grafana` HTTPRoute lives next to its
# backend in the gated `dizzy` overlay's namespace, selected via the
# automatic `kubernetes.io/metadata.name` namespace label (no custom
# label needed) — this avoids introducing the repo's first
# ReferenceGrant.
#
# The listener is unconditional: with WITH_DIZZY unset no route
# attaches and requests for this SNI get 404, mirroring how the
# glance listener exists without a Glance service.
- name: https-dizzy
protocol: HTTPS
port: 443
hostname: dizzy.127-0-0-1.nip.io
tls:
mode: Terminate
certificateRefs:
- kind: Secret
name: dizzy-nip-io-tls
allowedRoutes:
namespaces:
from: Selector
selector:
matchLabels:
kubernetes.io/metadata.name: dizzy
36 changes: 36 additions & 0 deletions deploy/kind/dizzy/httproute.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
# SPDX-FileCopyrightText: Copyright 2026 SAP SE or an SAP affiliate company
#
# SPDX-License-Identifier: Apache-2.0

# Static HTTPRoute exposing the dizzy Grafana UI at
# https://dizzy.127-0-0-1.nip.io — the repo's FIRST hand-authored
# HTTPRoute. Every other HTTPRoute in the tree is projected by a service
# operator (keystone/horizon/glance); this one lives next to its backend
# in the gated dizzy overlay because there is no operator projecting it.
#
# It attaches to the `https-dizzy` listener on Gateway `openstack-gw`
# (namespace `openstack`). That listener admits cross-namespace routes via
# `allowedRoutes.namespaces.from: Selector` matching the automatic
# `kubernetes.io/metadata.name: dizzy` label — so NO ReferenceGrant is
# needed: route→Gateway attachment is governed by the listener's
# allowedRoutes, and the backend Service (dizzy-grafana) is same-namespace.
#
# The backendRef targets the grafana chart's Service (port 80 → container
# 3000).
---
apiVersion: gateway.networking.k8s.io/v1
kind: HTTPRoute
metadata:
name: dizzy-grafana
namespace: dizzy
spec:
parentRefs:
- name: openstack-gw
namespace: openstack
sectionName: https-dizzy
hostnames:
- dizzy.127-0-0-1.nip.io
rules:
- backendRefs:
- name: dizzy-grafana
port: 80
76 changes: 76 additions & 0 deletions deploy/kind/dizzy/kustomization.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,76 @@
# SPDX-FileCopyrightText: Copyright 2026 SAP SE or an SAP affiliate company
#
# SPDX-License-Identifier: Apache-2.0

# Opt-in kind overlay for the dizzy observability stack — applied only when
# the operator explicitly requests it (WITH_DIZZY=true make deploy-infra).
#
# Bundles everything needed to render a working VictoriaMetrics + Grafana
# stack on a kind cluster:
# 1. The dizzy Namespace (declared inline so the overlay is
# self-contained; unlike the prometheus overlay's Namespace it lives
# ONLY here — production ships no dizzy stack — see namespace.yaml).
# 2. Two HelmRepository sources declared into `flux-system`
# (victoria-metrics, grafana) so deploy/flux-system/** stays untouched.
# 3. Two HelmReleases (VictoriaMetrics-single, Grafana) with kind-tuned
# values baked directly into the release files.
# 4. The static `dizzy-grafana` HTTPRoute attaching to the `https-dizzy`
# Gateway listener.
# 5. A configMapGenerator-produced ConfigMap wrapping the three dizzy
# dashboard JSONs for the Grafana dashboard provider.
#
# All resource references are local to this overlay (no parent-directory
# `../../` paths). The overlay therefore renders cleanly under kustomize's
# default `LoadRestrictionsRootOnly` security check, which means
# `kubectl apply -k deploy/kind/dizzy` works without `--load-restrictor`.
# kubectl's embedded kustomize does NOT expose `--load-restrictor`
# (kubernetes/kubectl#948), so keeping the overlay self-contained is the
# only way to ship `kubectl apply -k` as the production caller in
# hack/deploy-infra.sh — same constraint that drove the chaos-mesh and
# prometheus overlays.
#
# STAGING CONTRACT the dashboards/ directory is git-ignored (see
# .gitignore) and does NOT exist in a fresh checkout. hack/dizzy.sh
# stage-dashboards populates it from the pinned dizzy release tarball
# BEFORE `kustomize build` can resolve the configMapGenerator's `files:`
# below (LoadRestrictionsRootOnly forbids reaching outside the overlay for
# them). `WITH_DIZZY=true make deploy-infra` runs the staging
# automatically; a raw `kustomize build deploy/kind/dizzy` on a fresh
# checkout FAILS until stage-dashboards has run.
---
apiVersion: kustomize.config.k8s.io/v1beta1
kind: Kustomization

# NO top-level `namespace:` on purpose: the two HelmRepository sources live
# in `flux-system` while everything else lives in `dizzy`. A global
# namespace would clobber the HelmRepositories into `dizzy` and break the
# HelmRelease sourceRefs, so each resource carries its own namespace.

resources:
- namespace.yaml
- source-victoria-metrics.yaml
- source-grafana.yaml
- release-victoria-metrics.yaml
- release-grafana.yaml
- httproute.yaml

configMapGenerator:
# Wraps the three dizzy dashboard JSONs so the Grafana dashboard provider
# (dashboardsConfigMaps.dizzy in release-grafana.yaml) mounts them. The
# files below are staged at deploy time by hack/dizzy.sh stage-dashboards
# and are NOT tracked in git (see .gitignore); the canonical source is the
# pinned dizzy release tarball.
- name: grafana-dashboards
namespace: dizzy
files:
- dashboards/overview.json
- dashboards/api-operations.json
- dashboards/time-to-ready.json
options:
# disableNameSuffixHash keeps the ConfigMap name stable
# (`grafana-dashboards`, no `-<hash>` suffix) because the grafana
# chart references it by fixed name via dashboardsConfigMaps. The
# trade-off is that Grafana does NOT auto-reload on a dashboard edit
# — acceptable here since the dashboards are a version-pinned asset
# that only changes on a dizzy bump + redeploy.
disableNameSuffixHash: true
35 changes: 35 additions & 0 deletions deploy/kind/dizzy/namespace.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
# SPDX-FileCopyrightText: Copyright 2026 SAP SE or an SAP affiliate company
#
# SPDX-License-Identifier: Apache-2.0

# kind-only opt-in Namespace for the dizzy observability stack
# (VictoriaMetrics + Grafana).
#
# DECISION Unlike the monitoring Namespace of the prometheus overlay, this
# Namespace is declared ONLY here and must NOT be added to
# deploy/flux-system/namespaces.yaml: production ships no dizzy stack at
# all, so a `dizzy` Namespace under the production flux-system bundle would
# be dead weight. The inline copy in this overlay is kept because:
# 1. The overlay is applied STANDALONE via `kubectl apply -k
# deploy/kind/dizzy` (gated on WITH_DIZZY=true in
# hack/deploy-infra.sh); it is NOT a child of deploy/kind/base, so
# there is no parent overlay guaranteed to create the Namespace
# first.
# 2. kubectl's embedded kustomize cannot pass --load-restrictor
# (kubernetes/kubectl#948), so the overlay MUST be self-contained
# under the default LoadRestrictionsRootOnly check — a `../../`
# reference into deploy/flux-system/namespaces.yaml would refuse to
# render (same constraint that drove the chaos-mesh and prometheus
# overlay layouts).
# 3. `kubectl apply -k` is idempotent for Namespace resources, so a
# re-apply is a no-op rather than a conflict.
#
# The Namespace name doubles as the route-admission key: the
# `https-dizzy` Gateway listener admits HTTPRoutes from namespaces
# labelled `kubernetes.io/metadata.name: dizzy` (the automatic label
# every Namespace carries), so no custom label is needed here.
---
apiVersion: v1
kind: Namespace
metadata:
name: dizzy
92 changes: 92 additions & 0 deletions deploy/kind/dizzy/release-grafana.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,92 @@
# SPDX-FileCopyrightText: Copyright 2026 SAP SE or an SAP affiliate company
#
# SPDX-License-Identifier: Apache-2.0

# Grafana HelmRelease — anonymous read-only dashboard frontend for the
# dizzy observability stack, reached at https://dizzy.127-0-0-1.nip.io via
# the static HTTPRoute in httproute.yaml. Opt-in: reconciled only when the
# dizzy overlay is applied (WITH_DIZZY=true make deploy-infra).
#
# SERVICE NAME the grafana chart's fullname helper collapses to the bare
# release name when that name already contains "grafana", so release
# `dizzy-grafana` renders Service `dizzy-grafana` (port 80 → container
# 3000) — the name httproute.yaml's backendRef targets.
#
# VALUES LAYOUT VERIFIED against grafana 10.5.15 with `helm show values`
# and `helm template`: the `grafana.ini`, `datasources`,
# `dashboardProviders`, and `dashboardsConfigMaps` top-level keys exist
# with these shapes, the Service defaults to port 80, and the rendered
# Service is `dizzy-grafana`. No corrections were needed.
---
apiVersion: helm.toolkit.fluxcd.io/v2
kind: HelmRelease
metadata:
name: dizzy-grafana
namespace: dizzy
spec:
interval: 30m
chart:
spec:
chart: grafana
version: ">=10.0.0 <11.0.0"
sourceRef:
kind: HelmRepository
name: grafana
namespace: flux-system
install:
createNamespace: true
remediation:
retries: 3
upgrade:
remediation:
retries: 3
values:
grafana.ini:
auth.anonymous:
# Anonymous Viewer access — kind-only localhost posture: the stack
# is reachable solely via the host's :443 bridge, so a login form
# would only get in the way of the local demo.
enabled: true
org_role: Viewer
auth:
disable_login_form: true
analytics:
reporting_enabled: false
dashboards:
default_home_dashboard_path: /var/lib/grafana/dashboards/dizzy/overview.json
datasources:
datasources.yaml:
apiVersion: 1
datasources:
- name: VictoriaMetrics
type: prometheus
# The three provisioned dashboards reference this datasource by
# the fixed uid `victoriametrics`, so it must stay verbatim.
uid: victoriametrics
access: proxy
# Matches the VictoriaMetrics server Service name from
# release-victoria-metrics.yaml.
url: http://dizzy-victoria-metrics-server.dizzy.svc:8428
isDefault: true
jsonData:
# Mirrors OTEL_METRIC_EXPORT_INTERVAL=15000 pinned in
# hack/dizzy.sh, so the graph step matches the ingest cadence.
timeInterval: "15s"
dashboardProviders:
dashboardproviders.yaml:
apiVersion: 1
providers:
- name: dizzy
folder: ""
type: file
disableDeletion: false
options:
path: /var/lib/grafana/dashboards/dizzy
dashboardsConfigMaps:
dizzy: grafana-dashboards
resources:
requests:
cpu: 50m
memory: 128Mi
limits:
memory: 256Mi
Loading
Loading