Skip to content

Commit a437052

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 a786287 commit a437052

7 files changed

Lines changed: 351 additions & 2 deletions

File tree

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: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
{{- if .Values.hyperdx.dashboards.enabled }}
2+
apiVersion: rbac.authorization.k8s.io/v1
3+
kind: ClusterRole
4+
metadata:
5+
name: {{ include "clickstack.fullname" . }}-dashboard-provisioner
6+
labels:
7+
{{- include "clickstack.labels" . | nindent 4 }}
8+
rules:
9+
- apiGroups: [""]
10+
resources: ["configmaps"]
11+
verbs: ["list", "get", "watch"]
12+
---
13+
apiVersion: rbac.authorization.k8s.io/v1
14+
kind: ClusterRoleBinding
15+
metadata:
16+
name: {{ include "clickstack.fullname" . }}-dashboard-provisioner
17+
labels:
18+
{{- include "clickstack.labels" . | nindent 4 }}
19+
roleRef:
20+
apiGroup: rbac.authorization.k8s.io
21+
kind: ClusterRole
22+
name: {{ include "clickstack.fullname" . }}-dashboard-provisioner
23+
subjects:
24+
- kind: ServiceAccount
25+
name: {{ .Values.hyperdx.serviceAccount.name | default (include "clickstack.hyperdx.fullname" .) }}
26+
namespace: {{ .Release.Namespace }}
27+
{{- end }}

charts/clickstack/templates/hyperdx/deployment.yaml

Lines changed: 45 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -45,7 +45,7 @@ spec:
4545
{{- if .Values.hyperdx.deployment.priorityClassName }}
4646
priorityClassName: {{ .Values.hyperdx.deployment.priorityClassName | quote }}
4747
{{- end }}
48-
{{- if or .Values.hyperdx.serviceAccount.create .Values.hyperdx.serviceAccount.name }}
48+
{{- if or .Values.hyperdx.serviceAccount.create .Values.hyperdx.serviceAccount.name .Values.hyperdx.dashboards.enabled }}
4949
serviceAccountName: {{ .Values.hyperdx.serviceAccount.name | default (include "clickstack.hyperdx.fullname" .) }}
5050
{{- end }}
5151
{{- if .Values.global.imagePullSecrets }}
@@ -125,6 +125,50 @@ spec:
125125
value: {{ tpl .Values.hyperdx.deployment.defaultSources . | quote }}
126126
{{- end }}
127127
{{- end }}
128+
{{- if .Values.hyperdx.dashboards.enabled }}
129+
- name: DASHBOARD_PROVISIONER_DIR
130+
value: "/dashboards"
131+
- name: DASHBOARD_PROVISIONER_ALL_TEAMS
132+
value: "true"
133+
{{- end }}
128134
{{- with .Values.hyperdx.deployment.env }}
129135
{{- toYaml . | nindent 12 }}
130136
{{- end }}
137+
{{- if .Values.hyperdx.dashboards.enabled }}
138+
volumeMounts:
139+
- name: dashboards
140+
mountPath: /dashboards
141+
readOnly: true
142+
{{- end }}
143+
{{- if .Values.hyperdx.dashboards.enabled }}
144+
- name: dashboard-watcher
145+
image: {{ .Values.hyperdx.dashboards.sidecarImage }}
146+
resources:
147+
limits:
148+
cpu: 50m
149+
memory: 64Mi
150+
requests:
151+
cpu: 10m
152+
memory: 32Mi
153+
env:
154+
- name: LABEL
155+
value: "hyperdx.io/dashboard"
156+
- name: LABEL_VALUE
157+
value: "true"
158+
- name: FOLDER
159+
value: "/dashboards"
160+
- name: RESOURCE
161+
value: "configmap"
162+
- name: NAMESPACE
163+
value: "ALL"
164+
- name: UNIQUE_FILENAMES
165+
value: "true"
166+
volumeMounts:
167+
- name: dashboards
168+
mountPath: /dashboards
169+
{{- end }}
170+
{{- if .Values.hyperdx.dashboards.enabled }}
171+
volumes:
172+
- name: dashboards
173+
emptyDir: {}
174+
{{- end }}

charts/clickstack/templates/hyperdx/serviceaccount.yaml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
{{- if .Values.hyperdx.serviceAccount.create }}
1+
{{- if or .Values.hyperdx.serviceAccount.create (and .Values.hyperdx.dashboards.enabled (not .Values.hyperdx.serviceAccount.name)) }}
22
apiVersion: v1
33
kind: ServiceAccount
44
metadata:
Lines changed: 230 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,230 @@
1+
suite: Test Dashboard Provisioner
2+
templates:
3+
- hyperdx/deployment.yaml
4+
- hyperdx/dashboard-configmap.yaml
5+
- hyperdx/dashboard-provisioner-rbac.yaml
6+
- hyperdx/serviceaccount.yaml
7+
tests:
8+
- it: should not render RBAC or ConfigMap when dashboards are disabled
9+
set:
10+
hyperdx:
11+
dashboards:
12+
enabled: false
13+
asserts:
14+
- hasDocuments:
15+
count: 0
16+
template: hyperdx/dashboard-configmap.yaml
17+
- hasDocuments:
18+
count: 0
19+
template: hyperdx/dashboard-provisioner-rbac.yaml
20+
21+
- it: should not add sidecar when dashboards are disabled
22+
set:
23+
hyperdx:
24+
dashboards:
25+
enabled: false
26+
asserts:
27+
- lengthEqual:
28+
path: spec.template.spec.containers
29+
count: 1
30+
template: hyperdx/deployment.yaml
31+
32+
- it: should add dashboard-watcher sidecar when enabled
33+
set:
34+
hyperdx:
35+
dashboards:
36+
enabled: true
37+
asserts:
38+
- lengthEqual:
39+
path: spec.template.spec.containers
40+
count: 2
41+
template: hyperdx/deployment.yaml
42+
- equal:
43+
path: spec.template.spec.containers[1].name
44+
value: dashboard-watcher
45+
template: hyperdx/deployment.yaml
46+
47+
- it: should set DASHBOARD_PROVISIONER_DIR on the app container
48+
set:
49+
hyperdx:
50+
dashboards:
51+
enabled: true
52+
asserts:
53+
- contains:
54+
path: spec.template.spec.containers[0].env
55+
content:
56+
name: DASHBOARD_PROVISIONER_DIR
57+
value: "/dashboards"
58+
template: hyperdx/deployment.yaml
59+
60+
- it: should use the k8s-sidecar image for watcher
61+
set:
62+
hyperdx:
63+
dashboards:
64+
enabled: true
65+
sidecarImage: "kiwigrid/k8s-sidecar:2.5.0"
66+
asserts:
67+
- equal:
68+
path: spec.template.spec.containers[1].image
69+
value: "kiwigrid/k8s-sidecar:2.5.0"
70+
template: hyperdx/deployment.yaml
71+
72+
- it: should configure watcher to discover labeled ConfigMaps across all namespaces
73+
set:
74+
hyperdx:
75+
dashboards:
76+
enabled: true
77+
asserts:
78+
- contains:
79+
path: spec.template.spec.containers[1].env
80+
content:
81+
name: LABEL
82+
value: "hyperdx.io/dashboard"
83+
template: hyperdx/deployment.yaml
84+
- contains:
85+
path: spec.template.spec.containers[1].env
86+
content:
87+
name: NAMESPACE
88+
value: "ALL"
89+
template: hyperdx/deployment.yaml
90+
91+
- it: should share dashboards volume between app and watcher
92+
set:
93+
hyperdx:
94+
dashboards:
95+
enabled: true
96+
asserts:
97+
- contains:
98+
path: spec.template.spec.volumes
99+
content:
100+
name: dashboards
101+
emptyDir: {}
102+
template: hyperdx/deployment.yaml
103+
- contains:
104+
path: spec.template.spec.containers[0].volumeMounts
105+
content:
106+
name: dashboards
107+
mountPath: /dashboards
108+
readOnly: true
109+
template: hyperdx/deployment.yaml
110+
- contains:
111+
path: spec.template.spec.containers[1].volumeMounts
112+
content:
113+
name: dashboards
114+
mountPath: /dashboards
115+
template: hyperdx/deployment.yaml
116+
117+
- it: should auto-create SA when dashboards enabled and no user SA configured
118+
set:
119+
hyperdx:
120+
dashboards:
121+
enabled: true
122+
serviceAccount:
123+
create: false
124+
name: ""
125+
asserts:
126+
- equal:
127+
path: spec.template.spec.serviceAccountName
128+
value: RELEASE-NAME-clickstack-app
129+
template: hyperdx/deployment.yaml
130+
- hasDocuments:
131+
count: 1
132+
template: hyperdx/serviceaccount.yaml
133+
134+
- it: should use existing user SA and not create a new one
135+
set:
136+
hyperdx:
137+
dashboards:
138+
enabled: true
139+
serviceAccount:
140+
create: false
141+
name: "my-custom-sa"
142+
asserts:
143+
- equal:
144+
path: spec.template.spec.serviceAccountName
145+
value: my-custom-sa
146+
template: hyperdx/deployment.yaml
147+
- hasDocuments:
148+
count: 0
149+
template: hyperdx/serviceaccount.yaml
150+
151+
- it: should bind ClusterRole to the user SA when configured
152+
set:
153+
hyperdx:
154+
dashboards:
155+
enabled: true
156+
serviceAccount:
157+
name: "my-custom-sa"
158+
asserts:
159+
- equal:
160+
path: subjects[0].name
161+
value: my-custom-sa
162+
documentIndex: 1
163+
template: hyperdx/dashboard-provisioner-rbac.yaml
164+
165+
- it: should bind ClusterRole to the default SA when no user SA configured
166+
set:
167+
hyperdx:
168+
dashboards:
169+
enabled: true
170+
asserts:
171+
- equal:
172+
path: subjects[0].name
173+
value: RELEASE-NAME-clickstack-app
174+
documentIndex: 1
175+
template: hyperdx/dashboard-provisioner-rbac.yaml
176+
177+
- it: should grant cluster-wide configmap list, get, and watch permissions
178+
set:
179+
hyperdx:
180+
dashboards:
181+
enabled: true
182+
asserts:
183+
- equal:
184+
path: rules[0].verbs
185+
value: ["list", "get", "watch"]
186+
documentIndex: 0
187+
template: hyperdx/dashboard-provisioner-rbac.yaml
188+
189+
- it: should not render inline ConfigMap when configMaps is empty
190+
set:
191+
hyperdx:
192+
dashboards:
193+
enabled: true
194+
configMaps: {}
195+
asserts:
196+
- hasDocuments:
197+
count: 0
198+
template: hyperdx/dashboard-configmap.yaml
199+
200+
- it: should render inline ConfigMap with discovery label
201+
set:
202+
hyperdx:
203+
dashboards:
204+
enabled: true
205+
configMaps:
206+
test.json: |
207+
{ "name": "Test", "tiles": [] }
208+
asserts:
209+
- equal:
210+
path: metadata.labels["hyperdx.io/dashboard"]
211+
value: "true"
212+
template: hyperdx/dashboard-configmap.yaml
213+
214+
- it: should include multiple dashboard files in inline ConfigMap
215+
set:
216+
hyperdx:
217+
dashboards:
218+
enabled: true
219+
configMaps:
220+
k8s-overview.json: |
221+
{ "name": "Kubernetes Overview", "tiles": [] }
222+
app-metrics.json: |
223+
{ "name": "App Metrics", "tiles": [] }
224+
asserts:
225+
- isNotNull:
226+
path: data["k8s-overview.json"]
227+
template: hyperdx/dashboard-configmap.yaml
228+
- isNotNull:
229+
path: data["app-metrics.json"]
230+
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)