Compare commits

..
2 Commits
Author SHA1 Message Date
duynguyen 128712c4c9 add: Infinity datasource plugin for actual-http-api, wired into Grafana
Installs yesoreyeram-infinity-datasource on the Grafana pod so it can call
Actual's own ActualQL run-query endpoint directly (group-by/sum over any
field), instead of being limited to the fixed counters
actual-budget-exporter publishes. The datasource connection itself (URL +
x-api-key) is added by hand in the Grafana UI, not provisioned here — keeps
the API key out of git without needing a dedicated K8s Secret for it.
2026-09-03 17:30:27 +07:00
duynguyen ed040bc606 chore: manual sync for leaf apps, keep root app-of-apps automated
Convention: only the root app-of-apps (platform) auto-syncs so it picks up
new/changed child Application definitions from Git. Every leaf service app
(vault, headlamp, harbor, jenkins, sonarqube, external-secrets,
kube-prometheus-stack) now requires an explicit manual Sync — matches
cluster-bootstrap and homelab-services' existing ignis convention.
2026-09-02 16:22:21 +07:00
9 changed files with 48 additions and 21 deletions
+34
View File
@@ -120,6 +120,40 @@ kubectl exec -n jenkins deploy/jenkins -c jenkins -- cat /run/secrets/additional
is set inside its own database on first boot, so it cannot be swapped via a is set inside its own database on first boot, so it cannot be swapped via a
`kubectl patch` the way ArgoCD's can. `kubectl patch` the way ArgoCD's can.
**Grafana → actual-http-api datasource** (Infinity plugin, queries
`homelab-docker-compose-prod/actual-http-api`'s ActualQL `run-query` endpoint
directly — see `homelab-services` README for why the Prometheus exporter
alone can't answer arbitrary group-by/amount questions). The chart install
just gets the plugin onto the pod; the datasource itself is added by hand in
the Grafana UI (not provisioned/GitOps) so the API key never needs its own
K8s Secret:
1. Grafana → **Connections → Data sources → Add data source** → search
**Infinity** → select it.
2. URL: `http://192.168.1.41:8003`.
3. **Auth** → enable **Custom HTTP Headers** → add header
`x-api-key` = `<actual-http-api API_KEY, same value as its compose
file>`. Grafana stores this encrypted in its own DB (uses
`kube-prometheus-stack-grafana`'s secret key), not in git.
4. **Save & test**.
Datasource then shows up in Grafana as whatever name you gave it — build a panel
with method `POST`, URL path `/budgets/<sync-id>/run-query`, and an
`ActualQLquery` JSON body, e.g. sum of $ per category:
```json
{
"ActualQLquery": {
"table": "transactions",
"groupBy": ["category"],
"select": ["category", { "total": { "$sum": "amount" } }]
}
}
```
Keep the panel's refresh interval long (≥1m) — each call is a real login
against `actual_server`, same rate-limit risk hit by the exporter earlier.
**Headlamp login token** (ServiceAccount created by `headlamp/rbac.yaml`): **Headlamp login token** (ServiceAccount created by `headlamp/rbac.yaml`):
```bash ```bash
@@ -25,10 +25,8 @@ spec:
destination: destination:
server: https://kubernetes.default.svc server: https://kubernetes.default.svc
namespace: external-secrets namespace: external-secrets
# Leaf app — manual sync only; root `platform` app-of-apps stays automated.
syncPolicy: syncPolicy:
automated:
prune: true
selfHeal: true
syncOptions: syncOptions:
- CreateNamespace=true - CreateNamespace=true
- ServerSideApply=true - ServerSideApply=true
+1 -3
View File
@@ -25,10 +25,8 @@ spec:
destination: destination:
server: https://kubernetes.default.svc server: https://kubernetes.default.svc
namespace: harbor namespace: harbor
# Leaf app — manual sync only; root `platform` app-of-apps stays automated.
syncPolicy: syncPolicy:
automated:
prune: true
selfHeal: true
syncOptions: syncOptions:
- CreateNamespace=true - CreateNamespace=true
- SkipDryRunOnMissingResource=true - SkipDryRunOnMissingResource=true
+1 -3
View File
@@ -25,10 +25,8 @@ spec:
destination: destination:
server: https://kubernetes.default.svc server: https://kubernetes.default.svc
namespace: headlamp namespace: headlamp
# Leaf app — manual sync only; root `platform` app-of-apps stays automated.
syncPolicy: syncPolicy:
automated:
prune: true
selfHeal: true
syncOptions: syncOptions:
- CreateNamespace=true - CreateNamespace=true
- SkipDryRunOnMissingResource=true - SkipDryRunOnMissingResource=true
+1 -3
View File
@@ -25,10 +25,8 @@ spec:
destination: destination:
server: https://kubernetes.default.svc server: https://kubernetes.default.svc
namespace: jenkins namespace: jenkins
# Leaf app — manual sync only; root `platform` app-of-apps stays automated.
syncPolicy: syncPolicy:
automated:
prune: true
selfHeal: true
syncOptions: syncOptions:
- CreateNamespace=true - CreateNamespace=true
- SkipDryRunOnMissingResource=true - SkipDryRunOnMissingResource=true
@@ -36,10 +36,8 @@ spec:
jsonPointers: jsonPointers:
- /spec/rules/0/backendRefs/0/group - /spec/rules/0/backendRefs/0/group
- /spec/rules/0/backendRefs/0/weight - /spec/rules/0/backendRefs/0/weight
# Leaf app — manual sync only; root `platform` app-of-apps stays automated.
syncPolicy: syncPolicy:
automated:
prune: true
selfHeal: true
syncOptions: syncOptions:
- CreateNamespace=true - CreateNamespace=true
- ServerSideApply=true - ServerSideApply=true
@@ -3,6 +3,13 @@ grafana:
enabled: true enabled: true
storageClassName: nfs-delete storageClassName: nfs-delete
size: 5Gi size: 5Gi
# Installs the Infinity datasource plugin so Grafana can query a plain
# REST API (actual-http-api) directly — see README for wiring it up.
# The datasource itself (URL + x-api-key) is added by hand in the Grafana
# UI, not provisioned here — avoids a second hand-created Secret just for
# this one credential.
plugins:
- yesoreyeram-infinity-datasource
prometheus: prometheus:
prometheusSpec: prometheusSpec:
+1 -3
View File
@@ -25,10 +25,8 @@ spec:
destination: destination:
server: https://kubernetes.default.svc server: https://kubernetes.default.svc
namespace: sonarqube namespace: sonarqube
# Leaf app — manual sync only; root `platform` app-of-apps stays automated.
syncPolicy: syncPolicy:
automated:
prune: true
selfHeal: true
syncOptions: syncOptions:
- CreateNamespace=true - CreateNamespace=true
- SkipDryRunOnMissingResource=true - SkipDryRunOnMissingResource=true
+1 -3
View File
@@ -25,10 +25,8 @@ spec:
destination: destination:
server: https://kubernetes.default.svc server: https://kubernetes.default.svc
namespace: vault namespace: vault
# Leaf app — manual sync only; root `platform` app-of-apps stays automated.
syncPolicy: syncPolicy:
automated:
prune: true
selfHeal: true
syncOptions: syncOptions:
- CreateNamespace=true - CreateNamespace=true
- SkipDryRunOnMissingResource=true - SkipDryRunOnMissingResource=true