3 Commits

Author SHA1 Message Date
24904ef084 Add grafana dashboard configmap (#24)
* feat: Add Grafana dashboard as ConfigMap

Adds the Grafana dashboard for iperf3-monitor as a ConfigMap to the Helm chart.

The dashboard is sourced from the project's README and stored in
`charts/iperf3-monitor/grafana/iperf3-dashboard.json`.

A new template `charts/iperf3-monitor/templates/grafana-dashboard-configmap.yaml`
creates the ConfigMap, loading the dashboard JSON and labeling it with
`grafana_dashboard: "1"` to enable auto-discovery by Grafana.

* feat: Add Grafana dashboard as ConfigMap

Adds the Grafana dashboard for iperf3-monitor as a ConfigMap to the Helm chart.

The dashboard is sourced from the project's README and stored in
`charts/iperf3-monitor/grafana/iperf3-dashboard.json`.

A new template `charts/iperf3-monitor/templates/grafana-dashboard-configmap.yaml`
creates the ConfigMap, loading the dashboard JSON and labeling it with
`grafana_dashboard: "1"` to enable auto-discovery by Grafana.

* fix: Correct Helm chart label in Grafana dashboard ConfigMap

Updates the `helm.sh/chart` label in the Grafana dashboard ConfigMap
to use `{{ .Chart.Name }}-{{ .Chart.Version | replace "+" "_" }}`.
This resolves a Helm linting error caused by an incorrect template reference.

The previous commit added the Grafana dashboard as a ConfigMap:
feat: Add Grafana dashboard as ConfigMap

Adds the Grafana dashboard for iperf3-monitor as a ConfigMap to the Helm chart.

The dashboard is sourced from the project's README and stored in
`charts/iperf3-monitor/grafana/iperf3-dashboard.json`.

A new template `charts/iperf3-monitor/templates/grafana-dashboard-configmap.yaml`
creates the ConfigMap, loading the dashboard JSON and labeling it with
`grafana_dashboard: "1"` to enable auto-discovery by Grafana.

---------

Co-authored-by: google-labs-jules[bot] <161369871+google-labs-jules[bot]@users.noreply.github.com>
2025-07-02 14:03:50 +05:30
966985dc3e Jules/align helm release workflow (#22)
* ci: Align Helm dependency setup in release workflow

Adds missing Helm dependency setup steps (repo add, dependency build) to the release workflow, mirroring the CI workflow. This ensures that dependencies are correctly handled during linting and packaging in the release process.

* refactor: Scope exporter RBAC to namespace for least privilege

Changed the exporter's ClusterRole and ClusterRoleBinding to a namespaced Role and RoleBinding.

This modification ensures that the exporter, by default, only has permissions to get, list, and watch pods within its own installation namespace. This aligns with the default behavior of IPERF_SERVER_NAMESPACE, which also defaults to the pod's own namespace, thereby adhering more strictly to the principle of least privilege.

Verified with `helm template` that the Role and RoleBinding are correctly created within the release namespace.

* fix: Add 'v' prefix to default image tag for exporter

Updated the logic in `charts/iperf3-monitor/templates/exporter-controller.yaml`
to ensure that when the exporter's image tag is not specified in
`values.yaml`, it defaults to `v<Chart.AppVersion>` instead of just
`<Chart.AppVersion>`.

This change ensures the default tag matches image tagging conventions
where a 'v' prefix is used for versions (e.g., `v0.1.0`).
If an image tag is explicitly provided in `values.yaml`, that tag is
used directly without modification.

Verified with `helm template` for both default and custom tag scenarios.

---------

Co-authored-by: google-labs-jules[bot] <161369871+google-labs-jules[bot]@users.noreply.github.com>
2025-07-02 13:29:08 +05:30
d3cb92eb0f Jules/align helm release workflow (#21)
* ci: Align Helm dependency setup in release workflow

Adds missing Helm dependency setup steps (repo add, dependency build) to the release workflow, mirroring the CI workflow. This ensures that dependencies are correctly handled during linting and packaging in the release process.

* refactor: Scope exporter RBAC to namespace for least privilege

Changed the exporter's ClusterRole and ClusterRoleBinding to a namespaced Role and RoleBinding.

This modification ensures that the exporter, by default, only has permissions to get, list, and watch pods within its own installation namespace. This aligns with the default behavior of IPERF_SERVER_NAMESPACE, which also defaults to the pod's own namespace, thereby adhering more strictly to the principle of least privilege.

Verified with `helm template` that the Role and RoleBinding are correctly created within the release namespace.

---------

Co-authored-by: google-labs-jules[bot] <161369871+google-labs-jules[bot]@users.noreply.github.com>
2025-07-02 12:57:00 +05:30
6 changed files with 223 additions and 12 deletions

View File

@@ -0,0 +1,194 @@
{
"__inputs": [],
"__requires": [
{
"type": "grafana",
"id": "grafana",
"name": "Grafana",
"version": "8.0.0"
},
{
"type": "datasource",
"id": "prometheus",
"name": "Prometheus",
"version": "1.0.0"
}
],
"annotations": {
"list": [
{
"builtIn": 1,
"datasource": {
"type": "grafana",
"uid": "-- Grafana --"
},
"enable": true,
"hide": true,
"iconColor": "rgba(0, 211, 255, 1)",
"name": "Annotations & Alerts",
"type": "dashboard"
}
]
},
"editable": true,
"fiscalYearStartMonth": 0,
"gnetId": null,
"graphTooltip": 0,
"id": null,
"links": [],
"panels": [
{
"datasource": {
"type": "prometheus",
"uid": "prometheus"
},
"gridPos": {
"h": 9,
"w": 24,
"x": 0,
"y": 0
},
"id": 2,
"targets": [
{
"expr": "avg(iperf_network_bandwidth_mbps) by (source_node, destination_node)",
"format": "heatmap",
"legendFormat": "{{source_node}} -> {{destination_node}}",
"refId": "A"
}
],
"cards": { "cardPadding": null, "cardRound": null },
"color": {
"mode": "spectrum",
"scheme": "red-yellow-green",
"exponent": 0.5,
"reverse": false
},
"dataFormat": "tsbuckets",
"yAxis": { "show": true, "format": "short" },
"xAxis": { "show": true }
},
{
"title": "Bandwidth Over Time (Source: $source_node, Dest: $destination_node)",
"type": "timeseries",
"datasource": {
"type": "prometheus",
"uid": "prometheus"
},
"gridPos": {
"h": 8,
"w": 12,
"x": 0,
"y": 9
},
"targets": [
{
"expr": "iperf_network_bandwidth_mbps{source_node=~\"^$source_node$\", destination_node=~\"^$destination_node$\", protocol=~\"^$protocol$\"}",
"legendFormat": "Bandwidth",
"refId": "A"
}
],
"fieldConfig": {
"defaults": {
"unit": "mbps"
}
}
},
{
"title": "Jitter Over Time (Source: $source_node, Dest: $destination_node)",
"type": "timeseries",
"datasource": {
"type": "prometheus",
"uid": "prometheus"
},
"gridPos": {
"h": 8,
"w": 12,
"x": 12,
"y": 9
},
"targets": [
{
"expr": "iperf_network_jitter_ms{source_node=~\"^$source_node$\", destination_node=~\"^$destination_node$\", protocol=\"udp\"}",
"legendFormat": "Jitter",
"refId": "A"
}
],
"fieldConfig": {
"defaults": {
"unit": "ms"
}
}
}
],
"refresh": "30s",
"schemaVersion": 36,
"style": "dark",
"tags": ["iperf3", "network", "kubernetes"],
"templating": {
"list": [
{
"current": {},
"datasource": {
"type": "prometheus",
"uid": "prometheus"
},
"definition": "label_values(iperf_network_bandwidth_mbps, source_node)",
"hide": 0,
"includeAll": false,
"multi": false,
"name": "source_node",
"options": [],
"query": "label_values(iperf_network_bandwidth_mbps, source_node)",
"refresh": 1,
"regex": "",
"skipUrlSync": false,
"sort": 1,
"type": "query"
},
{
"current": {},
"datasource": {
"type": "prometheus",
"uid": "prometheus"
},
"definition": "label_values(iperf_network_bandwidth_mbps{source_node=~\"^$source_node$\"}, destination_node)",
"hide": 0,
"includeAll": false,
"multi": false,
"name": "destination_node",
"options": [],
"query": "label_values(iperf_network_bandwidth_mbps{source_node=~\"^$source_node$\"}, destination_node)",
"refresh": 1,
"regex": "",
"skipUrlSync": false,
"sort": 1,
"type": "query"
},
{
"current": { "selected": true, "text": "tcp", "value": "tcp" },
"hide": 0,
"includeAll": false,
"multi": false,
"name": "protocol",
"options": [
{ "selected": true, "text": "tcp", "value": "tcp" },
{ "selected": false, "text": "udp", "value": "udp" }
],
"query": "tcp,udp",
"skipUrlSync": false,
"type": "custom"
}
]
},
"time": {
"from": "now-1h",
"to": "now"
},
"timepicker": {},
"timezone": "browser",
"title": "Kubernetes iperf3 Network Performance",
"uid": "k8s-iperf3-dashboard",
"version": 1,
"weekStart": ""
}

View File

@@ -77,7 +77,7 @@ Proceed with modifications only if the exporter controller is defined.
{{- if $exporterContainerCfg -}} {{- if $exporterContainerCfg -}}
{{- if not $exporterContainerCfg.image.tag -}} {{- if not $exporterContainerCfg.image.tag -}}
{{- if $chart.AppVersion -}} {{- if $chart.AppVersion -}}
{{- $_ := set $exporterContainerCfg.image "tag" $chart.AppVersion -}} {{- $_ := set $exporterContainerCfg.image "tag" (printf "v%s" $chart.AppVersion) -}}
{{- else -}} {{- else -}}
{{- fail (printf "Error: Container image tag is not specified for controller '%s', container '%s', and Chart.AppVersion is also empty." $exporterControllerKey "exporter") -}} {{- fail (printf "Error: Container image tag is not specified for controller '%s', container '%s', and Chart.AppVersion is also empty." $exporterControllerKey "exporter") -}}
{{- end -}} {{- end -}}

View File

@@ -0,0 +1,13 @@
apiVersion: v1
kind: ConfigMap
metadata:
name: {{ .Release.Name }}-grafana-dashboard
labels:
grafana_dashboard: "1"
app.kubernetes.io/name: {{ include "iperf3-monitor.name" . }}
helm.sh/chart: {{ .Chart.Name }}-{{ .Chart.Version | replace "+" "_" }}
app.kubernetes.io/instance: {{ .Release.Name }}
app.kubernetes.io/managed-by: {{ .Release.Service }}
data:
iperf3-dashboard.json: |
{{ .Files.Get "grafana/iperf3-dashboard.json" | nindent 4 }}

View File

@@ -7,9 +7,10 @@ metadata:
{{- include "iperf3-monitor.labels" . | nindent 4 }} {{- include "iperf3-monitor.labels" . | nindent 4 }}
--- ---
apiVersion: rbac.authorization.k8s.io/v1 apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRole kind: Role
metadata: metadata:
name: {{ include "iperf3-monitor.fullname" . }}-role name: {{ include "iperf3-monitor.fullname" . }}-role
namespace: {{ .Release.Namespace }}
labels: labels:
{{- include "iperf3-monitor.labels" . | nindent 4 }} {{- include "iperf3-monitor.labels" . | nindent 4 }}
rules: rules:
@@ -18,9 +19,10 @@ rules:
verbs: ["get", "list", "watch"] verbs: ["get", "list", "watch"]
--- ---
apiVersion: rbac.authorization.k8s.io/v1 apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRoleBinding kind: RoleBinding
metadata: metadata:
name: {{ include "iperf3-monitor.fullname" . }}-rb name: {{ include "iperf3-monitor.fullname" . }}-rb
namespace: {{ .Release.Namespace }}
labels: labels:
{{- include "iperf3-monitor.labels" . | nindent 4 }} {{- include "iperf3-monitor.labels" . | nindent 4 }}
subjects: subjects:
@@ -28,7 +30,7 @@ subjects:
name: {{ include "iperf3-monitor.serviceAccountName" . }} name: {{ include "iperf3-monitor.serviceAccountName" . }}
namespace: {{ .Release.Namespace }} namespace: {{ .Release.Namespace }}
roleRef: roleRef:
kind: ClusterRole kind: Role # Changed from ClusterRole
name: {{ include "iperf3-monitor.fullname" . }}-role name: {{ include "iperf3-monitor.fullname" . }}-role
apiGroup: rbac.authorization.k8s.io apiGroup: rbac.authorization.k8s.io
{{- end -}} {{- end -}}

View File

@@ -11,7 +11,7 @@ spec:
{{- include "iperf3-monitor.selectorLabels" . | nindent 4 }} {{- include "iperf3-monitor.selectorLabels" . | nindent 4 }}
app.kubernetes.io/component: exporter app.kubernetes.io/component: exporter
ports: ports:
- name: metrics - name: metrics # Assuming 'metrics' is the intended name, aligns with values structure
port: {{ .Values.service.port }} port: {{ .Values.service.main.ports.metrics.port }}
targetPort: {{ .Values.service.targetPort }} targetPort: {{ .Values.service.main.ports.metrics.targetPort }}
protocol: TCP protocol: {{ .Values.service.main.ports.metrics.protocol | default "TCP" }}

View File

@@ -86,13 +86,15 @@ controllers:
# key: mykey # key: mykey
# -- Ports for the exporter container. # -- Ports for the exporter container.
# Expected by Kubernetes and bjw-s common library as a list of objects.
ports: ports:
metrics: # Name of the port, will be used in Service definition - name: metrics # Name of the port, referenced by Service's targetPort
# -- Port number for the metrics endpoint on the container. # -- Port number for the metrics endpoint on the container.
port: 9876 # Default, should match service.targetPort containerPort: 9876
# -- Protocol for the metrics port. # -- Protocol for the metrics port.
protocol: TCP # Common library defaults to TCP if not specified. protocol: TCP
enabled: true # This port is enabled # -- Whether this port definition is enabled. Specific to bjw-s common library.
enabled: true
# -- CPU and memory resource requests and limits for the exporter container. # -- CPU and memory resource requests and limits for the exporter container.
resources: resources: