Skip to content

Commit 64af4b1

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 d17b156 commit 64af4b1

File tree

6 files changed

+357
-0
lines changed

6 files changed

+357
-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: 49 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,52 @@ 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_INTERVAL
128+
value: {{ mul .Values.hyperdx.dashboards.syncInterval 1000 | quote }}
129+
- name: DASHBOARD_PROVISIONER_ALL_TEAMS
130+
value: "true"
131+
{{- end }}
121132
{{- with .Values.hyperdx.deployment.env }}
122133
{{- toYaml . | nindent 12 }}
123134
{{- end }}
135+
{{- if .Values.hyperdx.dashboards.enabled }}
136+
volumeMounts:
137+
- name: dashboards
138+
mountPath: /dashboards
139+
readOnly: true
140+
{{- end }}
141+
{{- if .Values.hyperdx.dashboards.enabled }}
142+
- name: dashboard-watcher
143+
image: {{ .Values.hyperdx.dashboards.sidecarImage }}
144+
resources:
145+
limits:
146+
cpu: 50m
147+
memory: 64Mi
148+
requests:
149+
cpu: 10m
150+
memory: 32Mi
151+
env:
152+
- name: LABEL
153+
value: "hyperdx.io/dashboard"
154+
- name: LABEL_VALUE
155+
value: "true"
156+
- name: FOLDER
157+
value: "/dashboards"
158+
- name: RESOURCE
159+
value: "configmap"
160+
- name: NAMESPACE
161+
value: "ALL"
162+
- name: UNIQUE_FILENAMES
163+
value: "true"
164+
volumeMounts:
165+
- name: dashboards
166+
mountPath: /dashboards
167+
{{- end }}
168+
{{- if .Values.hyperdx.dashboards.enabled }}
169+
volumes:
170+
- name: dashboards
171+
emptyDir: {}
172+
{{- end }}
Lines changed: 219 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,219 @@
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 use the k8s-sidecar image for watcher
47+
set:
48+
hyperdx:
49+
dashboards:
50+
enabled: true
51+
sidecarImage: "kiwigrid/k8s-sidecar:2.5.0"
52+
asserts:
53+
- equal:
54+
path: spec.template.spec.containers[1].image
55+
value: "kiwigrid/k8s-sidecar:2.5.0"
56+
template: hyperdx/deployment.yaml
57+
58+
- it: should configure watcher to discover labeled ConfigMaps across all namespaces
59+
set:
60+
hyperdx:
61+
dashboards:
62+
enabled: true
63+
asserts:
64+
- contains:
65+
path: spec.template.spec.containers[1].env
66+
content:
67+
name: LABEL
68+
value: "hyperdx.io/dashboard"
69+
template: hyperdx/deployment.yaml
70+
- contains:
71+
path: spec.template.spec.containers[1].env
72+
content:
73+
name: NAMESPACE
74+
value: "ALL"
75+
template: hyperdx/deployment.yaml
76+
- contains:
77+
path: spec.template.spec.containers[1].env
78+
content:
79+
name: UNIQUE_FILENAMES
80+
value: "true"
81+
template: hyperdx/deployment.yaml
82+
83+
- it: should set DASHBOARD_PROVISIONER_DIR on the app container
84+
set:
85+
hyperdx:
86+
dashboards:
87+
enabled: true
88+
asserts:
89+
- contains:
90+
path: spec.template.spec.containers[0].env
91+
content:
92+
name: DASHBOARD_PROVISIONER_DIR
93+
value: "/dashboards"
94+
template: hyperdx/deployment.yaml
95+
96+
- it: should set DASHBOARD_PROVISIONER_INTERVAL in milliseconds
97+
set:
98+
hyperdx:
99+
dashboards:
100+
enabled: true
101+
syncInterval: 60
102+
asserts:
103+
- contains:
104+
path: spec.template.spec.containers[0].env
105+
content:
106+
name: DASHBOARD_PROVISIONER_INTERVAL
107+
value: "60000"
108+
template: hyperdx/deployment.yaml
109+
110+
- it: should share a dashboards volume between app and watcher
111+
set:
112+
hyperdx:
113+
dashboards:
114+
enabled: true
115+
asserts:
116+
- contains:
117+
path: spec.template.spec.volumes
118+
content:
119+
name: dashboards
120+
emptyDir: {}
121+
template: hyperdx/deployment.yaml
122+
- contains:
123+
path: spec.template.spec.containers[0].volumeMounts
124+
content:
125+
name: dashboards
126+
mountPath: /dashboards
127+
readOnly: true
128+
template: hyperdx/deployment.yaml
129+
- contains:
130+
path: spec.template.spec.containers[1].volumeMounts
131+
content:
132+
name: dashboards
133+
mountPath: /dashboards
134+
template: hyperdx/deployment.yaml
135+
136+
- it: should use the dashboard-provisioner service account
137+
set:
138+
hyperdx:
139+
dashboards:
140+
enabled: true
141+
asserts:
142+
- equal:
143+
path: spec.template.spec.serviceAccountName
144+
value: RELEASE-NAME-clickstack-dashboard-provisioner
145+
template: hyperdx/deployment.yaml
146+
147+
- it: should create ServiceAccount, ClusterRole, and ClusterRoleBinding
148+
set:
149+
hyperdx:
150+
dashboards:
151+
enabled: true
152+
asserts:
153+
- isKind:
154+
of: ServiceAccount
155+
documentIndex: 0
156+
template: hyperdx/dashboard-provisioner-rbac.yaml
157+
- isKind:
158+
of: ClusterRole
159+
documentIndex: 1
160+
template: hyperdx/dashboard-provisioner-rbac.yaml
161+
- isKind:
162+
of: ClusterRoleBinding
163+
documentIndex: 2
164+
template: hyperdx/dashboard-provisioner-rbac.yaml
165+
166+
- it: should grant cluster-wide configmap list, get, and watch permissions
167+
set:
168+
hyperdx:
169+
dashboards:
170+
enabled: true
171+
asserts:
172+
- equal:
173+
path: rules[0].verbs
174+
value: ["list", "get", "watch"]
175+
documentIndex: 1
176+
template: hyperdx/dashboard-provisioner-rbac.yaml
177+
178+
- it: should not render inline ConfigMap when configMaps is empty
179+
set:
180+
hyperdx:
181+
dashboards:
182+
enabled: true
183+
configMaps: {}
184+
asserts:
185+
- hasDocuments:
186+
count: 0
187+
template: hyperdx/dashboard-configmap.yaml
188+
189+
- it: should render inline ConfigMap with discovery label
190+
set:
191+
hyperdx:
192+
dashboards:
193+
enabled: true
194+
configMaps:
195+
test.json: |
196+
{ "name": "Test", "tiles": [] }
197+
asserts:
198+
- equal:
199+
path: metadata.labels["hyperdx.io/dashboard"]
200+
value: "true"
201+
template: hyperdx/dashboard-configmap.yaml
202+
203+
- it: should include multiple dashboard files in inline ConfigMap
204+
set:
205+
hyperdx:
206+
dashboards:
207+
enabled: true
208+
configMaps:
209+
k8s-overview.json: |
210+
{ "name": "Kubernetes Overview", "tiles": [] }
211+
app-metrics.json: |
212+
{ "name": "App Metrics", "tiles": [] }
213+
asserts:
214+
- isNotNull:
215+
path: data["k8s-overview.json"]
216+
template: hyperdx/dashboard-configmap.yaml
217+
- isNotNull:
218+
path: data["app-metrics.json"]
219+
template: hyperdx/dashboard-configmap.yaml

charts/clickstack/values.yaml

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,33 @@ 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+
# Seconds between MongoDB upsert cycles
30+
syncInterval: 30
31+
# Inline dashboard definitions - key is filename, value is exported dashboard JSON
32+
configMaps: {}
33+
# Example:
34+
# configMaps:
35+
# k8s-overview.json: |
36+
# { "name": "Kubernetes Overview", "tiles": [...] }
37+
resources:
38+
limits:
39+
cpu: 100m
40+
memory: 128Mi
41+
requests:
42+
cpu: 50m
43+
memory: 64Mi
44+
1845
# ── K8s ConfigMap (clickstack-config) ────────────────────
1946
# Shared non-sensitive environment variables. Used by HyperDX and OTEL collector via envFrom.
2047
# All values support Helm template expressions (rendered via tpl).

0 commit comments

Comments
 (0)