Skip to content

Commit 5714065

Browse files
committed
Add dashboard provisioning via k8s-sidecar
k8s-sidecar watches ConfigMaps labeled "hyperdx.io/dashboard: true" across all namespaces and writes dashboard JSON to a shared volume. HyperDX reads and upserts them natively via file-based provisioner. Requires hyperdxio/hyperdx#1962
1 parent b617e54 commit 5714065

File tree

6 files changed

+333
-0
lines changed

6 files changed

+333
-0
lines changed
Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
---
2+
"clickstack": minor
3+
---
4+
5+
feat: add dashboard provisioning via k8s-sidecar that discovers labeled ConfigMaps across namespaces. Requires hyperdxio/hyperdx#1962 (file-based dashboard provisioner).
Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
{{- if and .Values.hyperdx.dashboards.enabled .Values.hyperdx.dashboards.configMaps }}
2+
{{- /*
3+
Inline dashboard ConfigMap; labeled for discovery by the dashboard provisioner
4+
alongside any external dashboard ConfigMaps from application charts.
5+
*/ -}}
6+
apiVersion: v1
7+
kind: ConfigMap
8+
metadata:
9+
name: {{ include "clickstack.fullname" . }}-dashboards
10+
labels:
11+
{{- include "clickstack.labels" . | nindent 4 }}
12+
hyperdx.io/dashboard: "true"
13+
data:
14+
{{- range $key, $value := .Values.hyperdx.dashboards.configMaps }}
15+
{{ $key }}: |
16+
{{- $value | nindent 4 }}
17+
{{- end }}
18+
{{- end }}
Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,39 @@
1+
{{- if .Values.hyperdx.dashboards.enabled }}
2+
{{- /*
3+
RBAC for the dashboard provisioner.
4+
ClusterRole so it can discover labeled dashboard ConfigMaps across all namespaces,
5+
allowing application teams to manage dashboards from their own namespaces.
6+
*/ -}}
7+
apiVersion: v1
8+
kind: ServiceAccount
9+
metadata:
10+
name: {{ include "clickstack.fullname" . }}-dashboard-provisioner
11+
labels:
12+
{{- include "clickstack.labels" . | nindent 4 }}
13+
---
14+
apiVersion: rbac.authorization.k8s.io/v1
15+
kind: ClusterRole
16+
metadata:
17+
name: {{ include "clickstack.fullname" . }}-dashboard-provisioner
18+
labels:
19+
{{- include "clickstack.labels" . | nindent 4 }}
20+
rules:
21+
- apiGroups: [""]
22+
resources: ["configmaps"]
23+
verbs: ["list", "get", "watch"]
24+
---
25+
apiVersion: rbac.authorization.k8s.io/v1
26+
kind: ClusterRoleBinding
27+
metadata:
28+
name: {{ include "clickstack.fullname" . }}-dashboard-provisioner
29+
labels:
30+
{{- include "clickstack.labels" . | nindent 4 }}
31+
roleRef:
32+
apiGroup: rbac.authorization.k8s.io
33+
kind: ClusterRole
34+
name: {{ include "clickstack.fullname" . }}-dashboard-provisioner
35+
subjects:
36+
- kind: ServiceAccount
37+
name: {{ include "clickstack.fullname" . }}-dashboard-provisioner
38+
namespace: {{ .Release.Namespace }}
39+
{{- end }}

charts/clickstack/templates/hyperdx/deployment.yaml

Lines changed: 47 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -47,6 +47,9 @@ spec:
4747
imagePullSecrets:
4848
{{- toYaml .Values.global.imagePullSecrets | nindent 8 }}
4949
{{- end }}
50+
{{- if .Values.hyperdx.dashboards.enabled }}
51+
serviceAccountName: {{ include "clickstack.fullname" . }}-dashboard-provisioner
52+
{{- end }}
5053
{{- if .Values.mongodb.enabled }}
5154
initContainers:
5255
- name: wait-for-mongodb
@@ -118,6 +121,50 @@ spec:
118121
value: {{ tpl .Values.hyperdx.deployment.defaultSources . | quote }}
119122
{{- end }}
120123
{{- end }}
124+
{{- if .Values.hyperdx.dashboards.enabled }}
125+
- name: DASHBOARD_PROVISIONER_DIR
126+
value: "/dashboards"
127+
- name: DASHBOARD_PROVISIONER_ALL_TEAMS
128+
value: "true"
129+
{{- end }}
121130
{{- with .Values.hyperdx.deployment.env }}
122131
{{- toYaml . | nindent 12 }}
123132
{{- end }}
133+
{{- if .Values.hyperdx.dashboards.enabled }}
134+
volumeMounts:
135+
- name: dashboards
136+
mountPath: /dashboards
137+
readOnly: true
138+
{{- end }}
139+
{{- if .Values.hyperdx.dashboards.enabled }}
140+
- name: dashboard-watcher
141+
image: {{ .Values.hyperdx.dashboards.sidecarImage }}
142+
resources:
143+
limits:
144+
cpu: 50m
145+
memory: 64Mi
146+
requests:
147+
cpu: 10m
148+
memory: 32Mi
149+
env:
150+
- name: LABEL
151+
value: "hyperdx.io/dashboard"
152+
- name: LABEL_VALUE
153+
value: "true"
154+
- name: FOLDER
155+
value: "/dashboards"
156+
- name: RESOURCE
157+
value: "configmap"
158+
- name: NAMESPACE
159+
value: "ALL"
160+
- name: UNIQUE_FILENAMES
161+
value: "true"
162+
volumeMounts:
163+
- name: dashboards
164+
mountPath: /dashboards
165+
{{- end }}
166+
{{- if .Values.hyperdx.dashboards.enabled }}
167+
volumes:
168+
- name: dashboards
169+
emptyDir: {}
170+
{{- end }}
Lines changed: 199 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,199 @@
1+
suite: Test Dashboard Provisioner
2+
templates:
3+
- hyperdx/deployment.yaml
4+
- hyperdx/dashboard-configmap.yaml
5+
- hyperdx/dashboard-provisioner-rbac.yaml
6+
tests:
7+
- it: should not render RBAC or ConfigMap when dashboards are disabled
8+
set:
9+
hyperdx:
10+
dashboards:
11+
enabled: false
12+
asserts:
13+
- hasDocuments:
14+
count: 0
15+
template: hyperdx/dashboard-configmap.yaml
16+
- hasDocuments:
17+
count: 0
18+
template: hyperdx/dashboard-provisioner-rbac.yaml
19+
20+
- it: should not add sidecar when dashboards are disabled
21+
set:
22+
hyperdx:
23+
dashboards:
24+
enabled: false
25+
asserts:
26+
- lengthEqual:
27+
path: spec.template.spec.containers
28+
count: 1
29+
template: hyperdx/deployment.yaml
30+
31+
- it: should add dashboard-watcher sidecar when enabled
32+
set:
33+
hyperdx:
34+
dashboards:
35+
enabled: true
36+
asserts:
37+
- lengthEqual:
38+
path: spec.template.spec.containers
39+
count: 2
40+
template: hyperdx/deployment.yaml
41+
- equal:
42+
path: spec.template.spec.containers[1].name
43+
value: dashboard-watcher
44+
template: hyperdx/deployment.yaml
45+
46+
- it: should set DASHBOARD_PROVISIONER_DIR on the app container
47+
set:
48+
hyperdx:
49+
dashboards:
50+
enabled: true
51+
asserts:
52+
- contains:
53+
path: spec.template.spec.containers[0].env
54+
content:
55+
name: DASHBOARD_PROVISIONER_DIR
56+
value: "/dashboards"
57+
template: hyperdx/deployment.yaml
58+
59+
- it: should use the k8s-sidecar image for watcher
60+
set:
61+
hyperdx:
62+
dashboards:
63+
enabled: true
64+
sidecarImage: "kiwigrid/k8s-sidecar:2.5.0"
65+
asserts:
66+
- equal:
67+
path: spec.template.spec.containers[1].image
68+
value: "kiwigrid/k8s-sidecar:2.5.0"
69+
template: hyperdx/deployment.yaml
70+
71+
- it: should configure watcher to discover labeled ConfigMaps across all namespaces
72+
set:
73+
hyperdx:
74+
dashboards:
75+
enabled: true
76+
asserts:
77+
- contains:
78+
path: spec.template.spec.containers[1].env
79+
content:
80+
name: LABEL
81+
value: "hyperdx.io/dashboard"
82+
template: hyperdx/deployment.yaml
83+
- contains:
84+
path: spec.template.spec.containers[1].env
85+
content:
86+
name: NAMESPACE
87+
value: "ALL"
88+
template: hyperdx/deployment.yaml
89+
90+
- it: should share dashboards volume between app and watcher
91+
set:
92+
hyperdx:
93+
dashboards:
94+
enabled: true
95+
asserts:
96+
- contains:
97+
path: spec.template.spec.volumes
98+
content:
99+
name: dashboards
100+
emptyDir: {}
101+
template: hyperdx/deployment.yaml
102+
- contains:
103+
path: spec.template.spec.containers[0].volumeMounts
104+
content:
105+
name: dashboards
106+
mountPath: /dashboards
107+
readOnly: true
108+
template: hyperdx/deployment.yaml
109+
- contains:
110+
path: spec.template.spec.containers[1].volumeMounts
111+
content:
112+
name: dashboards
113+
mountPath: /dashboards
114+
template: hyperdx/deployment.yaml
115+
116+
- it: should use the dashboard-provisioner service account
117+
set:
118+
hyperdx:
119+
dashboards:
120+
enabled: true
121+
asserts:
122+
- equal:
123+
path: spec.template.spec.serviceAccountName
124+
value: RELEASE-NAME-clickstack-dashboard-provisioner
125+
template: hyperdx/deployment.yaml
126+
127+
- it: should create ServiceAccount, ClusterRole, and ClusterRoleBinding
128+
set:
129+
hyperdx:
130+
dashboards:
131+
enabled: true
132+
asserts:
133+
- isKind:
134+
of: ServiceAccount
135+
documentIndex: 0
136+
template: hyperdx/dashboard-provisioner-rbac.yaml
137+
- isKind:
138+
of: ClusterRole
139+
documentIndex: 1
140+
template: hyperdx/dashboard-provisioner-rbac.yaml
141+
- isKind:
142+
of: ClusterRoleBinding
143+
documentIndex: 2
144+
template: hyperdx/dashboard-provisioner-rbac.yaml
145+
146+
- it: should grant cluster-wide configmap list, get, and watch permissions
147+
set:
148+
hyperdx:
149+
dashboards:
150+
enabled: true
151+
asserts:
152+
- equal:
153+
path: rules[0].verbs
154+
value: ["list", "get", "watch"]
155+
documentIndex: 1
156+
template: hyperdx/dashboard-provisioner-rbac.yaml
157+
158+
- it: should not render inline ConfigMap when configMaps is empty
159+
set:
160+
hyperdx:
161+
dashboards:
162+
enabled: true
163+
configMaps: {}
164+
asserts:
165+
- hasDocuments:
166+
count: 0
167+
template: hyperdx/dashboard-configmap.yaml
168+
169+
- it: should render inline ConfigMap with discovery label
170+
set:
171+
hyperdx:
172+
dashboards:
173+
enabled: true
174+
configMaps:
175+
test.json: |
176+
{ "name": "Test", "tiles": [] }
177+
asserts:
178+
- equal:
179+
path: metadata.labels["hyperdx.io/dashboard"]
180+
value: "true"
181+
template: hyperdx/dashboard-configmap.yaml
182+
183+
- it: should include multiple dashboard files in inline ConfigMap
184+
set:
185+
hyperdx:
186+
dashboards:
187+
enabled: true
188+
configMaps:
189+
k8s-overview.json: |
190+
{ "name": "Kubernetes Overview", "tiles": [] }
191+
app-metrics.json: |
192+
{ "name": "App Metrics", "tiles": [] }
193+
asserts:
194+
- isNotNull:
195+
path: data["k8s-overview.json"]
196+
template: hyperdx/dashboard-configmap.yaml
197+
- isNotNull:
198+
path: data["app-metrics.json"]
199+
template: hyperdx/dashboard-configmap.yaml

charts/clickstack/values.yaml

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,31 @@ hyperdx:
1515
app: 3000
1616
opamp: 4320
1717

18+
# ── Dashboard provisioning ────────────────────────────────
19+
# Discovers and upserts dashboard JSON into MongoDB (matched by name, never deletes)
20+
#
21+
# Two ways to provide dashboards:
22+
# 1. Inline: set configMaps below with dashboard JSON
23+
# 2. External: any ConfigMap in the cluster with the label "hyperdx.io/dashboard: true"
24+
# will be discovered automatically (ideal for application charts managing their own dashboards)
25+
dashboards:
26+
enabled: false
27+
# Image for the k8s-sidecar that watches for labeled ConfigMaps
28+
sidecarImage: "kiwigrid/k8s-sidecar:2.5.0"
29+
# Inline dashboard definitions - key is filename, value is exported dashboard JSON
30+
configMaps: {}
31+
# Example:
32+
# configMaps:
33+
# k8s-overview.json: |
34+
# { "name": "Kubernetes Overview", "tiles": [...] }
35+
resources:
36+
limits:
37+
cpu: 100m
38+
memory: 128Mi
39+
requests:
40+
cpu: 50m
41+
memory: 64Mi
42+
1843
# ── K8s ConfigMap (clickstack-config) ────────────────────
1944
# Shared non-sensitive environment variables. Used by HyperDX and OTEL collector via envFrom.
2045
# All values support Helm template expressions (rendered via tpl).

0 commit comments

Comments
 (0)