apiVersion: v1 kind: ConfigMap metadata: name: grafana-provisioning data: datasources.yaml: | apiVersion: 1 datasources: - name: Prometheus type: prometheus access: proxy url: http://prometheus-operated.monitoring.svc.cluster.local:9090 isDefault: true - name: Loki type: loki access: proxy url: http://loki.monitoring.svc.cluster.local:3100 dashboards.yaml: | apiVersion: 1 providers: - name: 'Default' orgId: 1 folder: '' type: file disableDeletion: false editable: true options: path: /var/lib/grafana/dashboards --- apiVersion: v1 kind: ConfigMap metadata: name: grafana-dashboards data: log-aggregator.json: | { "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, "graphTooltip": 0, "id": 1, "links": [], "panels": [ { "datasource": { "type": "loki", "uid": "P8E80F9AEF21F6940" }, "fieldConfig": { "defaults": { "color": { "mode": "palette-classic" }, "custom": { "axisBorderShow": false, "axisCenteredZero": false, "axisColorMode": "text", "axisLabel": "", "axisPlacement": "auto", "barAlignment": 0, "barWidthFactor": 0.6, "drawStyle": "line", "fillOpacity": 0, "gradientMode": "opacity", "hideFrom": { "legend": false, "tooltip": false, "viz": false }, "insertNulls": false, "lineInterpolation": "linear", "lineWidth": 1, "pointSize": 5, "scaleDistribution": { "type": "linear" }, "showPoints": "auto", "showValues": false, "spanNulls": false, "stacking": { "group": "A", "mode": "normal" }, "thresholdsStyle": { "mode": "off" } }, "mappings": [], "thresholds": { "mode": "absolute", "steps": [ { "color": "green", "value": 0 }, { "color": "red", "value": 80 } ] }, "unit": "msg/s" }, "overrides": [] }, "gridPos": { "h": 8, "w": 24, "x": 0, "y": 0 }, "id": 2, "options": { "legend": { "calcs": [], "displayMode": "list", "placement": "bottom", "showLegend": true }, "tooltip": { "hideZeros": false, "mode": "single", "sort": "none" } }, "pluginVersion": "12.2.1", "targets": [ { "datasource": { "type": "loki", "uid": "P8E80F9AEF21F6940" }, "direction": "backward", "editorMode": "code", "expr": "sum by (detected_level) (count_over_time ({app=~\"$app\",namespace=~\"$namespace\"}[1m]))", "legendFormat": "{{`{{detected_level}}`}}", "queryType": "range", "refId": "A" } ], "title": "Log records", "type": "timeseries" }, { "datasource": { "type": "loki", "uid": "P8E80F9AEF21F6940" }, "fieldConfig": { "defaults": {}, "overrides": [] }, "gridPos": { "h": 20, "w": 24, "x": 0, "y": 8 }, "id": 1, "options": { "dedupStrategy": "none", "enableInfiniteScrolling": false, "enableLogDetails": true, "prettifyLogMessage": true, "showCommonLabels": true, "showLabels": true, "showTime": true, "sortOrder": "Descending", "wrapLogMessage": true }, "pluginVersion": "12.2.1", "targets": [ { "datasource": { "type": "loki", "uid": "P8E80F9AEF21F6940" }, "direction": "backward", "editorMode": "code", "expr": "{app=~\"$app\",namespace=~\"$namespace\"}", "queryType": "range", "refId": "A" } ], "title": "Loki", "type": "logs" } ], "preload": false, "refresh": "30s", "schemaVersion": 42, "tags": [], "templating": { "list": [ { "allValue": ".*", "current": { "text": "All", "value": [ "$__all" ] }, "datasource": { "type": "loki", "uid": "P8E80F9AEF21F6940" }, "definition": "", "includeAll": true, "multi": true, "name": "app", "options": [], "query": { "label": "app", "refId": "LokiVariableQueryEditor-VariableQuery", "stream": "", "type": 1 }, "refresh": 1, "regex": "", "sort": 5, "type": "query" }, { "allValue": ".+", "current": { "text": "All", "value": [ "$__all" ] }, "datasource": { "type": "loki", "uid": "P8E80F9AEF21F6940" }, "definition": "", "includeAll": true, "label": "namespace", "multi": true, "name": "namespace", "options": [], "query": { "label": "namespace", "refId": "LokiVariableQueryEditor-VariableQuery", "stream": "", "type": 1 }, "refresh": 1, "regex": "", "type": "query" } ] }, "time": { "from": "now-5m", "to": "now" }, "timepicker": {}, "timezone": "browser", "title": "Log Aggregator", "uid": "lawf6g2", "version": 1 } --- apiVersion: apps/v1 kind: StatefulSet metadata: name: grafana labels: app: grafana spec: serviceName: grafana replicas: 1 selector: matchLabels: app: grafana template: metadata: labels: app: grafana spec: securityContext: fsGroup: 472 containers: - name: grafana image: grafana/grafana:latest imagePullPolicy: IfNotPresent ports: - containerPort: 3000 name: http env: # sqlite DB on PVC - name: GF_DATABASE_TYPE value: sqlite3 - name: GF_DATABASE_PATH value: /var/lib/grafana/grafana.db # Ingress URL (important for OAuth callback + links) - name: GF_SERVER_ROOT_URL value: https://{{ .Values.grafanaHostname }}/ - name: GF_SERVER_SERVE_FROM_SUB_PATH value: "false" # ---- OIDC (Passmower) via Generic OAuth ---- - name: GF_AUTH_GENERIC_OAUTH_ENABLED value: "true" - name: GF_AUTH_GENERIC_OAUTH_NAME value: "Passmower" - name: GF_AUTH_GENERIC_OAUTH_ALLOW_SIGN_UP value: "true" - name: GF_AUTH_GENERIC_OAUTH_USE_ID_TOKEN value: "false" - name: GF_AUTH_GENERIC_OAUTH_ROLE_ATTRIBUTE_PATH value: "contains(groups[*], 'github.com:codemowers:admins') && 'Admin' || 'Viewer'" # matches OIDCClient pkce: false - name: GF_AUTH_GENERIC_OAUTH_USE_PKCE value: "false" - name: GF_AUTH_GENERIC_OAUTH_CLIENT_ID valueFrom: secretKeyRef: name: oidc-client-grafana-{{ .Release.Name }}-owner-secrets key: OIDC_CLIENT_ID - name: GF_AUTH_GENERIC_OAUTH_CLIENT_SECRET valueFrom: secretKeyRef: name: oidc-client-grafana-{{ .Release.Name }}-owner-secrets key: OIDC_CLIENT_SECRET - name: GF_AUTH_GENERIC_OAUTH_SCOPES value: "openid profile groups" # From your existing OIDC secret: auth/token/me endpoints - name: GF_AUTH_GENERIC_OAUTH_AUTH_URL value: "https://auth.ee-lte-1.codemowers.io/auth" - name: GF_AUTH_GENERIC_OAUTH_TOKEN_URL value: "https://auth.ee-lte-1.codemowers.io/token" - name: GF_AUTH_GENERIC_OAUTH_API_URL value: "https://auth.ee-lte-1.codemowers.io/me" - name: GF_AUTH_GENERIC_OAUTH_SIGNOUT_REDIRECT_URL value: https://{{ .Values.grafanaHostname }}/ volumeMounts: - name: grafana-storage mountPath: /var/lib/grafana - name: grafana-provisioning mountPath: /etc/grafana/provisioning/datasources/datasources.yaml subPath: datasources.yaml readOnly: true - name: grafana-provisioning mountPath: /etc/grafana/provisioning/dashboards/dashboards.yaml subPath: dashboards.yaml readOnly: true - name: grafana-dashboards mountPath: /var/lib/grafana/dashboards readOnly: true readinessProbe: httpGet: path: /api/health port: 3000 initialDelaySeconds: 10 periodSeconds: 10 livenessProbe: httpGet: path: /api/health port: 3000 initialDelaySeconds: 30 periodSeconds: 10 volumes: - name: grafana-provisioning configMap: name: grafana-provisioning - name: grafana-dashboards configMap: name: grafana-dashboards volumeClaimTemplates: - metadata: name: grafana-storage spec: accessModes: [ReadWriteOnce] storageClassName: sqlite resources: requests: storage: 5Gi --- apiVersion: v1 kind: Service metadata: name: grafana labels: app: grafana spec: type: ClusterIP selector: app: grafana ports: - name: http port: 3000 targetPort: 3000 --- apiVersion: cert-manager.io/v1 kind: Certificate metadata: name: grafana-{{ .Release.Name }} spec: secretName: grafana-{{ .Release.Name }}-tls dnsNames: - {{ .Values.grafanaHostname }} issuerRef: name: letsencrypt kind: ClusterIssuer --- apiVersion: networking.k8s.io/v1 kind: Ingress metadata: name: grafana-{{ .Release.Name }} annotations: traefik.ingress.kubernetes.io/router.entrypoints: websecure spec: ingressClassName: traefik rules: - host: {{ .Values.grafanaHostname }} http: paths: - pathType: Prefix path: "/" backend: service: name: grafana port: number: 3000 tls: - secretName: grafana-{{ .Release.Name }}-tls --- apiVersion: codemowers.cloud/v1beta1 kind: OIDCClient metadata: name: grafana-{{ .Release.Name }} spec: displayName: Grafana {{ .Release.Name }} uri: https://{{ .Values.grafanaHostname }}/login/generic_oauth redirectUris: - https://{{ .Values.grafanaHostname }}/login/generic_oauth grantTypes: - authorization_code - refresh_token responseTypes: - code availableScopes: - openid - profile - offline_access pkce: false