Skip to content

Commit 7d182d9

Browse files
committed
Add dashboard provisioner job for injecting hyperdx dashboards at deploy time
1 parent 2ccd805 commit 7d182d9

6 files changed

Lines changed: 357 additions & 0 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: 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
@@ -121,6 +124,52 @@ spec:
121124
value: {{ tpl .Values.hyperdx.defaultSources . | quote }}
122125
{{- end }}
123126
{{- end }}
127+
{{- if .Values.hyperdx.dashboards.enabled }}
128+
- name: DASHBOARD_PROVISIONER_DIR
129+
value: "/dashboards"
130+
- name: DASHBOARD_PROVISIONER_INTERVAL
131+
value: {{ mul .Values.hyperdx.dashboards.syncInterval 1000 | quote }}
132+
- name: DASHBOARD_PROVISIONER_ALL_TEAMS
133+
value: "true"
134+
{{- end }}
124135
{{- with .Values.hyperdx.env }}
125136
{{- toYaml . | nindent 12 }}
126137
{{- end }}
138+
{{- if .Values.hyperdx.dashboards.enabled }}
139+
volumeMounts:
140+
- name: dashboards
141+
mountPath: /dashboards
142+
readOnly: true
143+
{{- end }}
144+
{{- if .Values.hyperdx.dashboards.enabled }}
145+
- name: dashboard-watcher
146+
image: {{ .Values.hyperdx.dashboards.sidecarImage }}
147+
resources:
148+
limits:
149+
cpu: 50m
150+
memory: 64Mi
151+
requests:
152+
cpu: 10m
153+
memory: 32Mi
154+
env:
155+
- name: LABEL
156+
value: "hyperdx.io/dashboard"
157+
- name: LABEL_VALUE
158+
value: "true"
159+
- name: FOLDER
160+
value: "/dashboards"
161+
- name: RESOURCE
162+
value: "configmap"
163+
- name: NAMESPACE
164+
value: "ALL"
165+
- name: UNIQUE_FILENAMES
166+
value: "true"
167+
volumeMounts:
168+
- name: dashboards
169+
mountPath: /dashboards
170+
{{- end }}
171+
{{- if .Values.hyperdx.dashboards.enabled }}
172+
volumes:
173+
- name: dashboards
174+
emptyDir: {}
175+
{{- 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+
- configmaps/dashboard-configmap.yaml
5+
- 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: configmaps/dashboard-configmap.yaml
16+
- hasDocuments:
17+
count: 0
18+
template: 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: dashboard-provisioner-rbac.yaml
157+
- isKind:
158+
of: ClusterRole
159+
documentIndex: 1
160+
template: dashboard-provisioner-rbac.yaml
161+
- isKind:
162+
of: ClusterRoleBinding
163+
documentIndex: 2
164+
template: 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: 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: configmaps/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: configmaps/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: configmaps/dashboard-configmap.yaml
217+
- isNotNull:
218+
path: data["app-metrics.json"]
219+
template: configmaps/dashboard-configmap.yaml

charts/clickstack/values.yaml

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -74,6 +74,33 @@ hyperdx:
7474
# Additional environment variables can be configured here
7575
# This is preserved for backward compatibility and advanced use cases
7676

77+
# Dashboard provisioning - discovers and upserts dashboard JSON into MongoDB
78+
# Dashboards are matched by name for idempotency (update existing, create new, never delete)
79+
#
80+
# Two ways to provide dashboards:
81+
# 1. Inline: set configMaps below with dashboard JSON
82+
# 2. External: any ConfigMap in the cluster with the label "hyperdx.io/dashboard: true"
83+
# will be discovered automatically (ideal for application charts managing their own dashboards)
84+
dashboards:
85+
enabled: false
86+
# Image for the k8s-sidecar that watches for labeled ConfigMaps
87+
sidecarImage: "kiwigrid/k8s-sidecar:2.5.0"
88+
# Seconds between MongoDB upsert cycles
89+
syncInterval: 30
90+
# Inline dashboard definitions - key is filename, value is exported dashboard JSON
91+
configMaps: {}
92+
# Example:
93+
# configMaps:
94+
# k8s-overview.json: |
95+
# { "name": "Kubernetes Overview", "tiles": [...] }
96+
resources:
97+
limits:
98+
cpu: 100m
99+
memory: 128Mi
100+
requests:
101+
cpu: 50m
102+
memory: 64Mi
103+
77104
# Default connections and sources (ENABLED BY DEFAULT)
78105
# Set to empty string to disable: defaultConnections: "" or defaultSources: ""
79106
#

0 commit comments

Comments
 (0)