Compare commits

..

15 Commits

Author SHA1 Message Date
unkinben 0f8c816235 Fix validate-except syntax: trailing semicolon after last entry
named requires every entry in the list to be semicolon-terminated,
including the last one before the closing brace; without it named fails
config parse (missing ';' before '}') and crash-loops.
2026-07-12 22:21:22 +10:00
unkinben 2706632f54 Exempt internal split-horizon zones from resolver DNSSEC validation
ci/woodpecker/pr/pre-commit Pipeline was successful
ci/woodpecker/pr/kubeconform Pipeline was successful
Resolver queries for unkin.net records returned SERVFAIL (broken trust
chain): the in-cluster authoritative serves unkin.net UNSIGNED, but the
public parent publishes a DS record (unkin.net is DNSSEC-signed on the
Internet), so the validating resolver rejects the insecure answer.

Add validate-except for the forwarded internal domains so the resolver
treats them as insecure and does not validate them. unkin.net covers all
*.unkin.net (incl. k8s.syd1.au.unkin.net); 18.198.in-addr.arpa covers
every NN.18.198.in-addr.arpa reverse zone; consul covers the consul TLD.
2026-07-12 22:13:50 +10:00
unkinben 65f18a6380 Bump bind-operator to v0.2.1 (secondary replication fix) (#250)
## Why

v0.2.1 fixes authoritative **secondary replication**, which never actually worked — the master REFUSED the catalog AXFR. Root causes (bind-operator #9): secondaries presented no TSIG key, member zones had no `allow-transfer`, and secondaries pointed at the primary's ephemeral pod IP.

## Changes

- Bump the operator image (`bind-system/deployment.yaml`) and the pulled CRD bundle URL (`bind-system/kustomization.yaml`) to `v0.2.1`.
- Bump the `bind-tsig-api` image (`bind-internal/tsig-api/tsig-api.yaml`) to `v0.2.1`.

CRDs are unchanged from v0.2.0, so the generated kubeconform schemas need no update.

## Validation

- `bind-system` renders with the v0.2.1 CRD bundle; `bind-internal` passes `kubeconform` (56/56); pre-commit clean.

## Deploy note

Existing member zones pick up `allow-transfer` via `modzone`, and secondaries re-point at the stable primary Service ClusterIP with the transfer key, restoring replication without manual BIND surgery. A from-scratch namespace recreate also comes up clean (OrderedReady startup means secondaries snapshot a ClusterIP-correct config). Caveat for a full recreate: the operator regenerates the `client-update` TSIG key, so its new material must be re-synced into puppet eyaml before clients can nsupdate.

Reviewed-on: #250
Co-authored-by: Ben Vincent <ben@unkin.net>
Co-committed-by: Ben Vincent <ben@unkin.net>
2026-07-12 21:43:03 +10:00
unkinben 38ef6c4a09 Bump bind-operator to v0.2.0 and deploy companion TSIG API (#249)
## Why

bind-operator v0.2.0 adds the `BindTSIGAPI` CRD and a companion API that `vault-plugin-secrets-bind-tsig` calls to create, rotate and delete TSIG keys (it does so by managing `BindTSIGKey` resources, which the operator reconciles into key material). This rolls the operator forward and deploys an API instance so Vault never talks to the Kubernetes API directly.

## Changes

- Bump the operator image (`bind-system/deployment.yaml`) and the pulled CRD bundle URL (`bind-system/kustomization.yaml`) to `v0.2.0`.
- Broaden the operator ClusterRole (`bind-system/rbac.yaml`) with `deployments`, `serviceaccounts` and `roles`/`rolebindings`, so the `BindTSIGAPI` reconciler can create the API Deployment and its namespaced Role/RoleBinding.
- Add a `BindTSIGAPI` (`bind-tsig-api`) in `bind-internal`; the operator reconciles it into a Deployment, Service, ConfigMap, master-token Secret and RBAC. Keys are created in `bind-internal`, alongside the authoritative cluster and its existing keys.
- Add the generated kubeconform schema for `BindTSIGAPI`.

## Notes

- The master access token Secret (`bind-tsig-api-token`) is generated by the operator when absent; the operator does not own it, so a `VaultStaticSecret` can later pre-seed/overwrite it to source the token from Vault.
- Validated: both overlays render (`kubectl kustomize`) and pass `kubeconform` (bind-internal 56/56 valid); pre-commit clean.

## Follow-up

- Point `vault-plugin-secrets-bind-tsig` config at `http://bind-tsig-api.bind-internal.svc:8443`.

Reviewed-on: #249
Co-authored-by: Ben Vincent <ben@unkin.net>
Co-committed-by: Ben Vincent <ben@unkin.net>
2026-07-12 19:12:39 +10:00
unkinben 2409a4d3a0 reduce CPU requests across observability, authentik, bind, woodpecker (#248)
Reduce reserved-but-unused CPU requests across several workloads (limits unchanged).

| Workload | CPU request |
|---|---|
| vmstorage (vmcluster) | 1 → 250m |
| vmagent | 500m → 250m |
| authentik server | 250m → 50m |
| authentik worker | 250m → 100m |
| bind authoritative | 100m → 20m |
| bind externaldns | 100m → 20m |
| bind resolvers | 100m → 20m |
| woodpecker agent | 100m → 50m |

Reviewed-on: #248
Co-authored-by: Ben Vincent <ben@unkin.net>
Co-committed-by: Ben Vincent <ben@unkin.net>
2026-07-12 18:41:08 +10:00
unkinben e0e0873ccb cnpg: reduce cluster CPU requests to 50m (#247)
Reduce CloudNativePG cluster CPU requests to `50m` across the platform to cut reserved-but-unused CPU.

Updated (memory and limits unchanged):
- artifactapi, authentik, encapi, paperclip (250m → 50m)
- grafana (100m → 50m)
- litellm (250m → 50m)

Excluded: woodpecker (already 50m), puppet (handled separately).
Reviewed-on: #247
Co-authored-by: Ben Vincent <ben@unkin.net>
Co-committed-by: Ben Vincent <ben@unkin.net>
2026-07-12 18:39:13 +10:00
benvin 3320c823b3 chore: remove paperclip (#245)
no longer using paperclip, remove it from au-syd1 cluster

---------

Co-authored-by: Ben Vincent <ben@unkin.net>
Reviewed-on: #245
2026-07-12 18:05:54 +10:00
benvin 646d76da16 chore: reduce resource requirements of puppet (#246)
- lower reserved CPU for puppet related services

---------

Co-authored-by: Ben Vincent <ben@unkin.net>
Reviewed-on: #246
2026-07-12 18:04:50 +10:00
unkinben 05d2c83258 Authoritative zones: accept puppet client dynamic updates (#244)
Enables per-host RFC2136 updates from puppet (puppet-prod #475 profiles::dns::updater) to the bind-authoritative zones, via the .9 write endpoint.

## Changes
- add **client-update** BindTSIGKey (clusterRef bind-authoritative; operator generates the material into Secret client-update-tsig)
- set `dynamicUpdate: true` + `updateKeyRef: client-update` on all **18** authoritative zones → the operator renders `allow-update { key "client-update"; }`

## Key bridge (manual, per the TSIG plan)
The operator generates the client-update key value; it must reach puppet eyaml (`profiles::dns::updater::key_secret`) for clients to authenticate — until the planned Vault-sync/secret-reflection operator features exist. Get it with:
`kubectl -n bind-internal get secret client-update-tsig -o jsonpath='{.data.secret}' | base64 -d`

## Validated
kustomize build + kubeconform.

Reviewed-on: #244
Co-authored-by: Ben Vincent <ben@unkin.net>
Co-committed-by: Ben Vincent <ben@unkin.net>
2026-07-11 00:57:52 +10:00
unkinben 38ab7199b3 grafana: add CloudNativePG dashboard (follow-up to #239) (#243)
Follow-up to #239, pairing with the CNPG VMPodScrape (#242). Imports the **CloudNativePG** dashboard (grafana.com 20417) as a `GrafanaDashboard` (gzipJson, datasources resolved to the in-cluster VictoriaMetrics uid). Now that #242 collects the postgres metrics, this dashboard renders real data for all CNPG clusters.

Reviewed-on: #243
Co-authored-by: Ben Vincent <ben@unkin.net>
Co-committed-by: Ben Vincent <ben@unkin.net>
2026-07-07 00:10:36 +10:00
unkinben be6d07cf77 observability: scrape CloudNativePG postgres metrics (follow-up to #239) (#242)
Follow-up to #239. Every CNPG instance pod already exposes Prometheus metrics on `:9187` (`metrics` port), but nothing scraped them. Adds a single namespace-wide `VMPodScrape` (`namespaceSelector.any`, `selector cnpg.io/podRole=instance`) so the observability VMAgent collects postgres metrics for **all ~23 CNPG clusters** across the estate (authentik, grafana, woodpecker, artifactapi, puppet, litellm, …). No chart changes. Pairs well with the CNPG grafana.com dashboard (20417) as a further follow-up.

Reviewed-on: #242
Co-authored-by: Ben Vincent <ben@unkin.net>
Co-committed-by: Ben Vincent <ben@unkin.net>
2026-07-07 00:08:32 +10:00
unkinben 8c9eb9befc cert-manager: scrape metrics (follow-up to #239) (#241)
Follow-up to #239. Adds a `VMServiceScrape` for cert-manager's existing webhook (`metrics` :9402) and cainjector (`http-metrics` :9402) services so the observability VMAgent collects them. No chart change needed. (The controller's own metrics need `prometheus.enabled` in the chart to expose a metrics service — separate follow-up.)

Reviewed-on: #241
Co-authored-by: Ben Vincent <ben@unkin.net>
Co-committed-by: Ben Vincent <ben@unkin.net>
2026-07-07 00:08:06 +10:00
unkinben 55e41897ef vmagent: set consul datacenter (fix 0 discovered targets) (#240)
## Why
The Consul SD job discovered **0 targets**. vmagent logs showed it reaching the puppet Consul fine (TLS/connectivity OK) but getting `403` on `GET /v1/agent/self`: the anonymous token `lacks permission 'agent:read'`. VictoriaMetrics calls `/v1/agent/self` only to auto-detect the datacenter; catalog/health reads (what SD actually needs) work anonymously.

## Fix
Set `datacenter: au-syd1` on the consul_sd_config so VM skips the `agent/self` call. No consul token needed.

## Verify after sync
vmagent `/targets` → `consul` job shows the puppet targets (haproxy/ceph/gitea/node/…) up.

Reviewed-on: #240
Co-authored-by: Ben Vincent <ben@unkin.net>
Co-committed-by: Ben Vincent <ben@unkin.net>
2026-07-07 00:05:30 +10:00
unkinben 9a56612ae6 observability: scrape existing app metrics (audit + ceph-csi batch) (#239)
## Why
Audit of existing helm deployments for (a) metrics collection and (b) bundled Grafana dashboards, per request. This PR enables the first, cleanest batch (ceph-csi) and documents the rest for follow-up review.

## Key constraint
The cluster has **no Prometheus-Operator CRDs** (`servicemonitors.monitoring.coreos.com` absent) — only the VM-native `VMServiceScrape`/`VMPodScrape`. So flipping charts' `serviceMonitor: true` toggles would fail. The correct mechanism here is native **VMServiceScrape** CRs, which the observability VMAgent picks up (`selectAllByDefault`, added in #234). Bundled chart dashboards ship as ConfigMaps that grafana-operator v5 ignores (it uses CRs), so "enabling" them means adding `GrafanaDashboard` CRs.

## This PR
- `VMServiceScrape` for **csi-cephfs** and **csi-cephrbd** (their `*-http-metrics` services already exist).

## Audit — metrics (apps already exposing a metrics service; enable via VMServiceScrape)
| App | Metrics service / port | Note |
|-----|------------------------|------|
| csi-cephfs / csi-cephrbd | `*-http-metrics` :8080 | **done here** |
| cert-manager | webhook :9402 `metrics`, cainjector :9402 `http-metrics` | multi-port; easy follow-up |
| argocd | `argocd-*-metrics` :8082/8083/8084/9001 | many components |
| media-apps | `prowlarr/radarr/sonarr-metrics` :9707 (exportarr) | follow-up |
| vm-system / grafana-operator | operator metrics | low value |

## Audit — charts with metrics *toggles* (need endpoint enabled + VMServiceScrape)
authentik, vault, purelb, externaldns, cnpg-system (`enablePodMonitor`), reloader, traefik. These expose `serviceMonitor`/`podMonitor` toggles that assume Prometheus CRDs — recommend enabling the metrics endpoint + a VMServiceScrape rather than the toggle.

## Audit — bundled Grafana dashboards
- **CNPG**: ships a dashboard (grafana.com id 20417) — add as `GrafanaDashboard` (grafanaCom).
- **traefik**: `dashboard: true` option + community dashboards — add as `GrafanaDashboard`.
- Others: none bundled.

## Review ask
Confirm the VMServiceScrape approach; I'll extend to the remaining apps (cert-manager/argocd/media-apps + CNPG/traefik dashboards) in follow-up batches.

Reviewed-on: #239
Co-authored-by: Ben Vincent <ben@unkin.net>
Co-committed-by: Ben Vincent <ben@unkin.net>
2026-07-06 23:57:54 +10:00
unkinben 95c5d71222 grafana: deploy Grafana instance, datasource and dashboards (#238)
## Why
Deploys Grafana in-cluster (observability project) via the grafana-operator, mirroring the puppet Grafana but modernised — **CNPG** for state, **Authentik OIDC** for auth — and ports the live datasource + dashboards in as CRs.

Depends on: grafana-operator (#235, merged), grafana schemas (#236, merged), Authentik OIDC (terraform-authentik #2), Vault seeds (done), and `^grafana/` image proxy (terraform-artifactapi #5).

## Changes (`apps/base/grafana`)
- **CNPG** postgres Cluster + rw Pooler (db `grafana`); **VaultAuth** + **VaultStaticSecrets** pulling `postgres`/`oauth` credentials from `kv/kubernetes/namespace/grafana/default/*`.
- **Grafana CR**: postgres backend via the pooler; Authentik `generic_oauth` (client id/secret from the Vault-synced secret, openid/email/profile scopes, group→role mapping); `root_url` grafana.k8s.syd1.au.unkin.net.
- **1 GrafanaDatasource** — k8s VictoriaMetrics via the operator `vmselect-main` service; reuses the previous default datasource uid so the imported dashboards resolve unedited.
- **13 GrafanaDashboards** (gzipJson) exported from the current grafana.
- **Gateway API** (traefik-internal) + HTTPRoute for grafana.k8s.syd1.au.unkin.net.
- Registered in the observability ApplicationSet + project.

## Review notes
- OAuth `role_attribute_path` maps Authentik group `grafana-admins` → Admin, else Viewer — **confirm the group name**.
- `database.ssl_mode: require` against the CNPG pooler — adjust if the pooler isn't serving TLS.
- The `VictoriaLogs - cluster` dashboard has no in-cluster logs datasource yet (no VictoriaLogs in k8s) — included for completeness, will be empty until one exists.
- `make kubeconform` clean (24 resources, validated against the strict grafana schemas).

Reviewed-on: #238
Co-authored-by: Ben Vincent <ben@unkin.net>
Co-committed-by: Ben Vincent <ben@unkin.net>
2026-07-06 23:54:51 +10:00
76 changed files with 879 additions and 365 deletions
+1 -1
View File
@@ -79,7 +79,7 @@ spec:
cpu: 500m cpu: 500m
memory: 512Mi memory: 512Mi
requests: requests:
cpu: 250m cpu: 50m
memory: 256Mi memory: 256Mi
smartShutdownTimeout: 180 smartShutdownTimeout: 180
startDelay: 3600 startDelay: 3600
+1 -1
View File
@@ -79,7 +79,7 @@ spec:
cpu: 500m cpu: 500m
memory: 512Mi memory: 512Mi
requests: requests:
cpu: 250m cpu: 50m
memory: 256Mi memory: 256Mi
smartShutdownTimeout: 180 smartShutdownTimeout: 180
startDelay: 3600 startDelay: 3600
@@ -31,7 +31,7 @@ spec:
external-dns.alpha.kubernetes.io/hostname: bind-authoritative-primary.k8s.syd1.au.unkin.net external-dns.alpha.kubernetes.io/hostname: bind-authoritative-primary.k8s.syd1.au.unkin.net
resources: resources:
requests: requests:
cpu: 100m cpu: 20m
memory: 128Mi memory: 128Mi
limits: limits:
cpu: "1" cpu: "1"
@@ -9,3 +9,16 @@ metadata:
spec: spec:
clusterRef: bind-authoritative clusterRef: bind-authoritative
algorithm: hmac-sha256 algorithm: hmac-sha256
---
# Client-update key: puppet clients (profiles::dns::updater) nsupdate their own
# records to the authoritative zones with this key. Operator generates the
# material into Secret client-update-tsig; the same value must reach puppet
# eyaml (or the planned Vault-sync bridge) for clients to authenticate.
apiVersion: bind.unkin.net/v1alpha1
kind: BindTSIGKey
metadata:
name: client-update
namespace: bind-internal
spec:
clusterRef: bind-authoritative
algorithm: hmac-sha256
@@ -15,6 +15,8 @@ spec:
zoneName: unkin.net zoneName: unkin.net
type: primary type: primary
defaultTTL: 600 defaultTTL: 600
dynamicUpdate: true
updateKeyRef: client-update
--- ---
apiVersion: bind.unkin.net/v1alpha1 apiVersion: bind.unkin.net/v1alpha1
kind: BindZone kind: BindZone
@@ -26,6 +28,8 @@ spec:
zoneName: main.unkin.net zoneName: main.unkin.net
type: primary type: primary
defaultTTL: 600 defaultTTL: 600
dynamicUpdate: true
updateKeyRef: client-update
--- ---
apiVersion: bind.unkin.net/v1alpha1 apiVersion: bind.unkin.net/v1alpha1
kind: BindZone kind: BindZone
@@ -37,6 +41,8 @@ spec:
zoneName: 13.18.198.in-addr.arpa zoneName: 13.18.198.in-addr.arpa
type: primary type: primary
defaultTTL: 600 defaultTTL: 600
dynamicUpdate: true
updateKeyRef: client-update
--- ---
apiVersion: bind.unkin.net/v1alpha1 apiVersion: bind.unkin.net/v1alpha1
kind: BindZone kind: BindZone
@@ -48,6 +54,8 @@ spec:
zoneName: 14.18.198.in-addr.arpa zoneName: 14.18.198.in-addr.arpa
type: primary type: primary
defaultTTL: 600 defaultTTL: 600
dynamicUpdate: true
updateKeyRef: client-update
--- ---
apiVersion: bind.unkin.net/v1alpha1 apiVersion: bind.unkin.net/v1alpha1
kind: BindZone kind: BindZone
@@ -59,6 +67,8 @@ spec:
zoneName: 15.18.198.in-addr.arpa zoneName: 15.18.198.in-addr.arpa
type: primary type: primary
defaultTTL: 600 defaultTTL: 600
dynamicUpdate: true
updateKeyRef: client-update
--- ---
apiVersion: bind.unkin.net/v1alpha1 apiVersion: bind.unkin.net/v1alpha1
kind: BindZone kind: BindZone
@@ -70,6 +80,8 @@ spec:
zoneName: 16.18.198.in-addr.arpa zoneName: 16.18.198.in-addr.arpa
type: primary type: primary
defaultTTL: 600 defaultTTL: 600
dynamicUpdate: true
updateKeyRef: client-update
--- ---
apiVersion: bind.unkin.net/v1alpha1 apiVersion: bind.unkin.net/v1alpha1
kind: BindZone kind: BindZone
@@ -81,6 +93,8 @@ spec:
zoneName: 17.18.198.in-addr.arpa zoneName: 17.18.198.in-addr.arpa
type: primary type: primary
defaultTTL: 600 defaultTTL: 600
dynamicUpdate: true
updateKeyRef: client-update
--- ---
apiVersion: bind.unkin.net/v1alpha1 apiVersion: bind.unkin.net/v1alpha1
kind: BindZone kind: BindZone
@@ -92,6 +106,8 @@ spec:
zoneName: 19.18.198.in-addr.arpa zoneName: 19.18.198.in-addr.arpa
type: primary type: primary
defaultTTL: 600 defaultTTL: 600
dynamicUpdate: true
updateKeyRef: client-update
--- ---
apiVersion: bind.unkin.net/v1alpha1 apiVersion: bind.unkin.net/v1alpha1
kind: BindZone kind: BindZone
@@ -103,6 +119,8 @@ spec:
zoneName: 20.18.198.in-addr.arpa zoneName: 20.18.198.in-addr.arpa
type: primary type: primary
defaultTTL: 600 defaultTTL: 600
dynamicUpdate: true
updateKeyRef: client-update
--- ---
apiVersion: bind.unkin.net/v1alpha1 apiVersion: bind.unkin.net/v1alpha1
kind: BindZone kind: BindZone
@@ -114,6 +132,8 @@ spec:
zoneName: 21.18.198.in-addr.arpa zoneName: 21.18.198.in-addr.arpa
type: primary type: primary
defaultTTL: 600 defaultTTL: 600
dynamicUpdate: true
updateKeyRef: client-update
--- ---
apiVersion: bind.unkin.net/v1alpha1 apiVersion: bind.unkin.net/v1alpha1
kind: BindZone kind: BindZone
@@ -125,6 +145,8 @@ spec:
zoneName: 22.18.198.in-addr.arpa zoneName: 22.18.198.in-addr.arpa
type: primary type: primary
defaultTTL: 600 defaultTTL: 600
dynamicUpdate: true
updateKeyRef: client-update
--- ---
apiVersion: bind.unkin.net/v1alpha1 apiVersion: bind.unkin.net/v1alpha1
kind: BindZone kind: BindZone
@@ -136,6 +158,8 @@ spec:
zoneName: 23.18.198.in-addr.arpa zoneName: 23.18.198.in-addr.arpa
type: primary type: primary
defaultTTL: 600 defaultTTL: 600
dynamicUpdate: true
updateKeyRef: client-update
--- ---
apiVersion: bind.unkin.net/v1alpha1 apiVersion: bind.unkin.net/v1alpha1
kind: BindZone kind: BindZone
@@ -147,6 +171,8 @@ spec:
zoneName: 24.18.198.in-addr.arpa zoneName: 24.18.198.in-addr.arpa
type: primary type: primary
defaultTTL: 600 defaultTTL: 600
dynamicUpdate: true
updateKeyRef: client-update
--- ---
apiVersion: bind.unkin.net/v1alpha1 apiVersion: bind.unkin.net/v1alpha1
kind: BindZone kind: BindZone
@@ -158,6 +184,8 @@ spec:
zoneName: 25.18.198.in-addr.arpa zoneName: 25.18.198.in-addr.arpa
type: primary type: primary
defaultTTL: 600 defaultTTL: 600
dynamicUpdate: true
updateKeyRef: client-update
--- ---
apiVersion: bind.unkin.net/v1alpha1 apiVersion: bind.unkin.net/v1alpha1
kind: BindZone kind: BindZone
@@ -169,6 +197,8 @@ spec:
zoneName: 26.18.198.in-addr.arpa zoneName: 26.18.198.in-addr.arpa
type: primary type: primary
defaultTTL: 600 defaultTTL: 600
dynamicUpdate: true
updateKeyRef: client-update
--- ---
apiVersion: bind.unkin.net/v1alpha1 apiVersion: bind.unkin.net/v1alpha1
kind: BindZone kind: BindZone
@@ -180,6 +210,8 @@ spec:
zoneName: 27.18.198.in-addr.arpa zoneName: 27.18.198.in-addr.arpa
type: primary type: primary
defaultTTL: 600 defaultTTL: 600
dynamicUpdate: true
updateKeyRef: client-update
--- ---
apiVersion: bind.unkin.net/v1alpha1 apiVersion: bind.unkin.net/v1alpha1
kind: BindZone kind: BindZone
@@ -191,6 +223,8 @@ spec:
zoneName: 28.18.198.in-addr.arpa zoneName: 28.18.198.in-addr.arpa
type: primary type: primary
defaultTTL: 600 defaultTTL: 600
dynamicUpdate: true
updateKeyRef: client-update
--- ---
apiVersion: bind.unkin.net/v1alpha1 apiVersion: bind.unkin.net/v1alpha1
kind: BindZone kind: BindZone
@@ -202,3 +236,5 @@ spec:
zoneName: 29.18.198.in-addr.arpa zoneName: 29.18.198.in-addr.arpa
type: primary type: primary
defaultTTL: 600 defaultTTL: 600
dynamicUpdate: true
updateKeyRef: client-update
@@ -23,7 +23,7 @@ spec:
type: ClusterIP type: ClusterIP
resources: resources:
requests: requests:
cpu: 100m cpu: 20m
memory: 128Mi memory: 128Mi
limits: limits:
cpu: "1" cpu: "1"
@@ -7,3 +7,4 @@ resources:
- authoritative - authoritative
- resolvers - resolvers
- externaldns - externaldns
- tsig-api
+10 -1
View File
@@ -21,9 +21,18 @@ spec:
forwarders: forwarders:
- 8.8.8.8 - 8.8.8.8
- 1.1.1.1 - 1.1.1.1
# The internal split-horizon zones are served UNSIGNED by the in-cluster
# authoritative, but their public parents publish DS records (e.g. unkin.net
# is DNSSEC-signed on the Internet). With dnssec-validation on, the validator
# sees "parent indicates secure" but gets an insecure answer and returns
# SERVFAIL (broken trust chain). Treat the forwarded internal domains as
# insecure so they are not validated. unkin.net covers all *.unkin.net
# (incl. k8s.syd1.au.unkin.net); 18.198.in-addr.arpa covers every reverse zone.
extraOptions:
- "validate-except { unkin.net; 18.198.in-addr.arpa; consul; }"
resources: resources:
requests: requests:
cpu: 100m cpu: 20m
memory: 128Mi memory: 128Mi
limits: limits:
cpu: "1" cpu: "1"
@@ -3,4 +3,4 @@ apiVersion: kustomize.config.k8s.io/v1beta1
kind: Kustomization kind: Kustomization
resources: resources:
- ../../../base/jellyfin - tsig-api.yaml
@@ -0,0 +1,27 @@
---
# Companion TSIG API. The operator reconciles this into a Deployment, Service,
# ConfigMap, master-token Secret and namespaced RBAC. vault-plugin-secrets-bind-tsig
# calls it to create/rotate/delete TSIG keys, which it does by managing
# BindTSIGKey resources in this namespace (the operator reconciles the material).
#
# The master access token Secret (bind-tsig-api-token) is generated by the
# operator if absent; a VaultStaticSecret may later pre-seed/overwrite it so the
# token is sourced from Vault rather than generated in-cluster.
apiVersion: bind.unkin.net/v1alpha1
kind: BindTSIGAPI
metadata:
name: bind-tsig-api
namespace: bind-internal
spec:
image: git.unkin.net/unkin/bind-tsig-api:v0.2.1
replicas: 1
port: 8443
# targetNamespace defaults to this resource's namespace (bind-internal), where
# the authoritative cluster and its keys live.
resources:
requests:
cpu: 25m
memory: 64Mi
limits:
cpu: 250m
memory: 128Mi
+1 -1
View File
@@ -21,7 +21,7 @@ spec:
runAsNonRoot: true runAsNonRoot: true
containers: containers:
- name: operator - name: operator
image: git.unkin.net/unkin/bind-operator:v0.1.5 image: git.unkin.net/unkin/bind-operator:v0.2.1
args: args:
- --metrics-bind-address=:8080 - --metrics-bind-address=:8080
- --health-probe-bind-address=:8081 - --health-probe-bind-address=:8081
+1 -1
View File
@@ -6,6 +6,6 @@ resources:
- namespace.yaml - namespace.yaml
# CRDs are pulled from the bind-operator repo at the matching tag rather than # CRDs are pulled from the bind-operator repo at the matching tag rather than
# vendored here, so they never drift from the operator. # vendored here, so they never drift from the operator.
- https://git.unkin.net/unkin/bind-operator/raw/tag/v0.1.5/config/crd/install.yaml - https://git.unkin.net/unkin/bind-operator/raw/tag/v0.2.1/config/crd/install.yaml
- rbac.yaml - rbac.yaml
- deployment.yaml - deployment.yaml
+9 -1
View File
@@ -23,7 +23,15 @@ rules:
resources: ["pods/exec"] resources: ["pods/exec"]
verbs: ["create", "get"] verbs: ["create", "get"]
- apiGroups: ["apps"] - apiGroups: ["apps"]
resources: ["statefulsets"] resources: ["statefulsets", "deployments"]
verbs: ["get", "list", "watch", "create", "update", "patch", "delete"]
# The BindTSIGAPI reconciler deploys the companion API: a ServiceAccount plus
# a namespaced Role/RoleBinding granting it access to BindTSIGKey + Secrets.
- apiGroups: [""]
resources: ["serviceaccounts"]
verbs: ["get", "list", "watch", "create", "update", "patch", "delete"]
- apiGroups: ["rbac.authorization.k8s.io"]
resources: ["roles", "rolebindings"]
verbs: ["get", "list", "watch", "create", "update", "patch", "delete"] verbs: ["get", "list", "watch", "create", "update", "patch", "delete"]
- apiGroups: [""] - apiGroups: [""]
resources: ["events"] resources: ["events"]
@@ -7,3 +7,4 @@ resources:
- serviceaccount.yaml - serviceaccount.yaml
- clusterrole.yaml - clusterrole.yaml
- clusterrolebinding.yaml - clusterrolebinding.yaml
- vmservicescrape.yaml
@@ -0,0 +1,17 @@
---
# Scrape cert-manager webhook + cainjector metrics endpoints.
# Picked up by the observability VMAgent (selectAllByDefault).
apiVersion: operator.victoriametrics.com/v1beta1
kind: VMServiceScrape
metadata:
name: cert-manager
namespace: cert-manager
spec:
selector:
matchLabels:
app.kubernetes.io/instance: cert-manager
endpoints:
- port: metrics
path: /metrics
- port: http-metrics
path: /metrics
+1
View File
@@ -7,3 +7,4 @@ resources:
- vaultauth.yaml - vaultauth.yaml
- vaultstaticsecret.yaml - vaultstaticsecret.yaml
- storageclass.yaml - storageclass.yaml
- vmservicescrape.yaml
+15
View File
@@ -0,0 +1,15 @@
---
# Scrape the ceph-csi-cephfs nodeplugin + provisioner http-metrics endpoints.
# Picked up by the observability VMAgent (selectAllByDefault).
apiVersion: operator.victoriametrics.com/v1beta1
kind: VMServiceScrape
metadata:
name: ceph-csi-cephfs
namespace: csi-cephfs
spec:
selector:
matchLabels:
app: ceph-csi-cephfs
endpoints:
- port: http-metrics
path: /metrics
+1
View File
@@ -7,3 +7,4 @@ resources:
- vaultauth.yaml - vaultauth.yaml
- vaultstaticsecret.yaml - vaultstaticsecret.yaml
- storageclass.yaml - storageclass.yaml
- vmservicescrape.yaml
@@ -0,0 +1,15 @@
---
# Scrape the ceph-csi-rbd nodeplugin + provisioner http-metrics endpoints.
# Picked up by the observability VMAgent (selectAllByDefault).
apiVersion: operator.victoriametrics.com/v1beta1
kind: VMServiceScrape
metadata:
name: ceph-csi-rbd
namespace: csi-cephrbd
spec:
selector:
matchLabels:
app: ceph-csi-rbd
endpoints:
- port: http-metrics
path: /metrics
+1 -1
View File
@@ -79,7 +79,7 @@ spec:
cpu: 500m cpu: 500m
memory: 512Mi memory: 512Mi
requests: requests:
cpu: 250m cpu: 50m
memory: 256Mi memory: 256Mi
smartShutdownTimeout: 180 smartShutdownTimeout: 180
startDelay: 3600 startDelay: 3600
+58
View File
@@ -0,0 +1,58 @@
---
apiVersion: postgresql.cnpg.io/v1
kind: Cluster
metadata:
name: postgres
namespace: grafana
spec:
affinity:
podAntiAffinityType: preferred
bootstrap:
initdb:
database: grafana
encoding: UTF8
localeCType: C
localeCollate: C
owner: grafana
secret:
name: postgres-credentials
enablePDB: true
enableSuperuserAccess: false
failoverDelay: 0
imageName: ghcr.io/cloudnative-pg/postgresql:18.1-system-trixie
instances: 2
logLevel: info
monitoring:
customQueriesConfigMap:
- key: queries
name: cnpg-default-monitoring
disableDefaultQueries: false
enablePodMonitor: false
postgresql:
parameters:
max_connections: "200"
shared_buffers: 128MB
primaryUpdateMethod: restart
primaryUpdateStrategy: unsupervised
replicationSlots:
highAvailability:
enabled: true
slotPrefix: _cnpg_
synchronizeReplicas:
enabled: true
updateInterval: 30
resources:
limits:
cpu: 500m
memory: 512Mi
requests:
cpu: 50m
memory: 256Mi
smartShutdownTimeout: 180
startDelay: 3600
stopDelay: 1800
storage:
resizeInUseVolumes: true
size: 10Gi
storageClass: cephrbd-fast-delete
switchoverDelay: 3600
+33
View File
@@ -0,0 +1,33 @@
---
apiVersion: postgresql.cnpg.io/v1
kind: Pooler
metadata:
name: postgres-pooler-rw
namespace: grafana
spec:
cluster:
name: postgres
instances: 2
pgbouncer:
parameters:
default_pool_size: "50"
max_client_conn: "200"
paused: false
poolMode: session
template:
metadata:
labels:
app: pooler-rw
spec:
affinity:
podAntiAffinity:
requiredDuringSchedulingIgnoredDuringExecution:
- labelSelector:
matchExpressions:
- key: app
operator: In
values:
- pooler-rw
topologyKey: kubernetes.io/hostname
containers: []
type: rw
@@ -0,0 +1,13 @@
---
apiVersion: grafana.integreatly.org/v1beta1
kind: GrafanaDashboard
metadata:
name: bind9-exporter-dns
namespace: grafana
spec:
instanceSelector:
matchLabels:
dashboards: "grafana"
resyncPeriod: 5m
allowCrossNamespaceImport: false
gzipJson: H4sIAAycS2oC/+1da1PbuBr+Kx5v5wx0A8QJ4ZKZ/QCBnu2Z0naBdmany8kotpKodSwfSaZkMzm/fd9Xsh2HXEhCYiDkC8SWLOm9PM+rq92zSRBwRRTjgbSrPdtnUtnVbz27ETFfvQ/sqlOwPaKI5JFwKWZR3RD+Z28W7Ih5cKslSJMExO4XbBqQhg/ZlIhowW4zL/3NXB7UuM8FPCBaDbJVLFglx4E/lUrBcrahtIB0sIaTQdOsf1knPhVKQqoiokWVaWyHwQ+nWCzYHaLc9knQtatN4kuK2Vog0bebwqDBst3gRHjYvtF7N3DXo9IVLMQqIfGUBd6xdfbxyrqi4pa51LrC5kjFXLkLDaEeU1kpm0y6xP+TEgH5hLrggWrbVWhbK6DqPSjIKZWLxwVUU9i+5txXLNTpPgt+SK11VA7UTO8UFQHxLUyxh2RhCiu0z5KGW1eJDVRSom2n8sXPR8KHi7ZSoazu7bWYakeNXZd39kLqEqkE34tNt5MqRO41fN7Y60AyFXuh4B2q2jSC26iVHXoXcgEpO14gd79LaHX/BgW5pR/5z9QGIQmobyRzue+TUFIvTRzrVYOKUq96k8nYR+0x7zPXztrW3gnVlfYL9p1WJdi/CJnwQec404CbxG9MaxasW9Am2tE+0dImtrjqgo46A6UL0EC/sHAlTUZ9r8aDJmvhcx5tkshXWmDX4KZndzgCylZtQWWb+560tfe6rENQXAfxEIYsaBlxeZgCXOMEHg0i37dRIgmF6/aB08H9j3sg3AAgEjyEEd/IO6ht0ATSkNyPFDogaCFMja3x3RKUBpByS/wIMmOdqJgkWYA3pIlHRQ3BKEBI21ogfksFWJtqA46Yfl+b/iBjeSex/ABSN6iJuzNQ8GfOAtSiZouMQnRjLowwAQ9QEA3Q4VvfI4B9sxvfJJHicJMLRgPDUAgvuPwbME+MXr3IpZ8y1RDfxQbZHUoC+yY2szRo1SqQMTJQ+doWvfhXtlK4FfpRiwVfqZCm3uPdyq5jL8W/AdVolze9el3xKqTRqqRASZ7sWzsWKHIL0V9vcK7qinVoPU7tsUAqErj0t///Zb8JoL3V3bd/2f1tC2oyaSrl/wBoAwQ2kidX74ir0CfQdLRFA+8dF+CqJpMgQYumvDEAoXE58AE0aRIW7CFsXlKJXKwdLXFpsNgGn/ni82AIn6UNPleKT1c752wIfXqAmtZGYixGic+I1N1FaRTeIEJmuhCy/YEGLexnOUVzTeVjexhTAJ667RjvbzJAbLVsfvxbEA+dz/T/xuDCKWlg4L8YGdBJ9mgAXU2WEQHbV05UjgWQ21aa6EZC6Dria3CAwW8WpL+BFAZ9MsURAcnVfc8GAfHSeAJe/GQearc8BFkkCo3XBISGrDKoJdhbv04IyRSI2KCgY4BgxlsmwSXE8lGLYKhqJb6WGScLPCoowqHpc4WupjX3KWsVuBkSl2ZdBDzL/THQDLhnSL0PIGhm4LAsnLLAFZRIugVPuFTKuhtGCSDr2g4TYFmwvvPGb3AH/gFIvzmlorzZth/CZmksNkfQ6IyCMRM5zCijQy9pyxgzO+z4CCMzz6p9/mJdQ5bsoKMHXkYQw7HvSBgeaOfXHlaPVQfsxG6ZF2HESsGu6RzKuiN3LBu6lKnCeK8pNXFXDbguuaPGTM1EXrSIz1unoHRNVYNnkUwG2drYuolZb3TZLHZk1kqQ1H+RjOTMyEhHmZ50ZRofOQdDhIQKOpHXZiQcZ1uEpQRrtdWVnikY4i1j+cVoy1k+bZk6noa1YmitlLS2ZNTZEtC12EI6mpmyEpLathpdawsRvG29taAzt23tWTgvQsSWy6NATSoWHwHKi0AhSICT+ivIgu2sk8xLib2ehVVZ/f7SyPF5cWDsrPfoTaPOBnvYMersor0APxrAzUGQ9gdOPMupdKDYX2q1k7Jugbmp7x0dF98VS8m9OJ9TqTmH9tqR69EM5Hr4Ysi1tF7kmkuXUJOfD57ujPKoJrgFOnkJmGags8LS2l+ZFAcWFaCSFeB09QI4S5fAGRKhtkYhZVw82ASRpQWRylxBxDmYIYocveYu+pPOLOTRR9ck1qEdLrr1C9q5RsXXG11FJ87zwfTg1r2n3sHk7wMP/WplnzmNmk1Q5lzP1Ijbpt70RxaZ2Pgi9XRhTgFvHqUtIAyUa2HB+QXAeSy6gEBxubNEw6XKM4u3LSCOKTYjzdkKY3tpVbE95ucJIV7r7AlD/FqM9Q6zU/tmJu14WpjeX87M/jMbxK0+/MbOg9RJ7qx3zKfWWbx1h2vaMQYr9p96uJdM/IP96s3sKlxCSW9wGw3Mdw3N9y8SQsBB8giHiUA8pMFqJfoU6qXpBwPiPMsXYzzluY+0yqUcVjFwmwRACwknM5b6yoSKtMF+KZ7slw9rg3vWhQ64maQXwN+l+fg7XpqdSuCVpyfwzcrsI2js1rhz0n+813FcNqUlgMqTqEWM7JxETIlkycwd882z4+tx3ebNovPsfeWhnbvOwTSuPX56rnU2XPuIXTB6b9r/Igos5EWhz1xYZB5ZVJ5KSKMbYSYz0VlaxVi+3V8u3Y4TEkwAeF2ZgLCQrWsYXsk+zVNCQQF76LGrk/IyrWLsbNL+whHlj8hQzItYbFlBSMnvQEapnOz7dXI6kfE+gMMtsLt69EzG65h0Ku1PC6RTtm8ZD33k0lAMntcZbXv9J14bGuZpFiNBEzZ7dLidY5MVtnuZm6zuQduamb9Lz3ixfHAkZAeBPncRr3qCPZ6gmU52hxuyW9tl8AlUJyiQnVQr7Xnz0F3yJtL7/HZppLA+6ZpeCM9tpj4WnPooTz0BdLQhsVdCYqCKEJRDV0pe5lDpJPJafEh9GTceuEtqvLxWzspvbF0+SsbW5ZzG1mBb7gN8XtHYeljjUxcEncpmlnotWFj7eErHOczl3jL6EygZTkuNn9jNaep6VHQfznzmI/iHk4vz/KeyRyXuMKlfw5CP1Bfvry5Ormu/zzC/vXLJAWoBrpl4+Yh+ffnlY+3k+vxs7N7KR3VEtGBW2iM51/jdDKLWLTRnDy3vT10+dkqbyLxGkXk589mzR+QVTWenTPWiprM3DDUvQx3NwlDlDUOtGUN1n9XAYdl7DKcLDm+GLOYq/jm8qrOYyxmqqYILqvKLTJdQWXdpp44zW2O6my7zWgek+BTxAxFpfxOR1mDP5T2C8iKh34mYvoCnEbk/qMohOvmr6EIbtjrtWmexXBvKWuvNbZXyVMoqbijrRVLWMFO5eOS6LoSEwuc/rfJ8hvX66Lh1eQmvkFevd6/xK9mJ9gAzORtmWqPhPQxvof9UB80zz3Sn8hzrfiU+jM+ecpQ/Kr+MXH3kMK+5jnHbWJZy2BDmEa7Oa6jjWLTX2aO80WpFnUEpFXQTCcGsQ1KQl49Q2V3ffElFZL5UoqM5XHr62A5epF8MQU4YuB5qnXZCH9QctIY+QJOSIiic+tRVmd008bvwP2ddOH5NffZmP3lVaBHdy/Ujj574A9L0SYP6979k0wGXYmmW+Es0QzlSekSX0SOq+2hKtebg7xa9M9vm5Q8WfhH+VTdwB5KMfExH7x9KZe8vHP8g4DF4V79hY3s2TfyHNyap4LtOGid7L1WCLqZuXNewRRTqV1lvZxA6sM8OpOx8JYLhTsYdPYyLtx3Nqj2DPUe73Vdd7R9xU2JXzF4axZm2FvA9s9faV+OQ+XRK/51LVZ2kdmTOBfWeEPAE5WPJM2p/b+vbf6s3v27DC5T21tkSnw1RjjVEaNLmN8SY91Bt7b7dhtj3oImwzllNpC2kS55uovISTIQxSccsEL0JzdV++nNHv/4SvoGhr+w4T8hgbkl/EyVudj0J9DpO6LDiFPFvWf/V7zHVJZnXaZaL+jeGzRL+cTw7Dt31gS2yD+isBzqrfqC0r/9iNDr0dIGenQjwN348BIaesINRmh2NcQ/AfPrrPA5Z+A2w1M+Gv3+FgScJh7g9k9If+uNfus/xD1uJBfNebQAA
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
@@ -0,0 +1,13 @@
---
apiVersion: grafana.integreatly.org/v1beta1
kind: GrafanaDashboard
metadata:
name: frr-ospf-route-metrics
namespace: grafana
spec:
instanceSelector:
matchLabels:
dashboards: "grafana"
resyncPeriod: 5m
allowCrossNamespaceImport: false
gzipJson: H4sIAAycS2oC/+1ZbW/bNhD+KwJRFC0gF7brZkGAfXDcZgiQpkFcbNiawqCkk82FJjWScuIK/u+7o14sN0lXB0nRJfliS5TIe+655046sWBcKe24E1pZtlcwKaxje58KFuVCukPF9nohS7jjVucmBrrFLTP8Z1PDU644C1kuEjzvdILfyqGg02GrkIHikcQ7nckhZDORNMci1mqkpTY4zUwj/qIbBv1eD3/evAmD3ktcU/E5GRmu0QXPg6EE4yxerSAk3M4izU3CVp/JYCJc22QqbMzln8DN2HHj3mvlZmyvGxL0bPZRa+lE5gekUOfo/qfPIcu4Amk9BakAmYy0SsWU/E4g5bl0nqY5zzKhptUcNzNgZ1om5TVNnjIeWS1zBwjXOsjKJePK66kBUHhlwWWON3dX4fqagWR9ZbeLrqFvegHGIIXe4opcEMmJ9vbQp9chu2B7g5BdeneWfkVGYcHo6ayJrjfxvsRXGqjIqMaUVjT0d26dSJfVYAzKgcFhbQQe+miQf7nTOJiBoRtGM66mMGqtbx1XCcUmJI/yGD60cHAZkydMcpTb57CkGkdY7Tkep1xaQDeQ2YuTtpXqEvIOl66yVqHZPhLEdpv9ryLT88q6QOaP+FLnrtQWDmUynwr1Oxhb0tHrv+q96hIEbqbgSiPtxGHPWmco1suMzKXGTNC0hNhpM8mzojn59Yxpm6VnLBSKuIwBR57Vx2ds5YlNDyn3hpgAaFk4Ej/7MD45CEb1OgGK3+WtrMEFnHf5/6nvwYa+d7bSNzfAr+i7ks49qftYu+NcyrsU+f2q0eC6MHFYduUkFVHBU4HCE9ligFJcmJRUiH+30OXhyWIQnNLyNhAqODjcfzCi3N0Q5S9PovxRoty5A1HuPFBRlpXydSXKwZMo70+U91MlH1h5rJX45kmJP0KJd1kat1NipZpGdhmX4Bx0YiTXipiawxjDqed0D78Udl+bBMwY+WwopOGR7zwg+QuM3rzQiipxzcrhIx6BLGNFpyeSxzDHNdZhiLgZSjFV5WjXD/whEjc74PSujEOv8I0yMfxi7JaeBGwNiexUSPkh47FwKORe2UMmJL7Nzom63ANTOiZhCipZa6LuN6vzhfiylhKGA1tbEp1tbiDLh0RApmUtcRrj1IvRgQfO9vooJS2UG4sv4JOLul54i428EVFeTmy69mqBWr00j9StYOFbPIvt7yYKjHd8jmWF1piizjIvk7AOrfd71S43FXFN8HWaMqoWP1l52vXlqd9t9cw7VX3qb9SnOozrGoHwE2Ezyetq5D+aUEKv9RZpR/ouaT6qlqgSv1FC4fVC6l7zXVFhkSrpidDGNTzfbdWg9nKiQEzxI4qxxXfWipKPA23mnIAVhUjR7dV/tKPHtZkgWgZe1TRrXVacmAOmgMBoPaLikpX591V5+amqS+8OqkvFz6MuLr1BVV1eP6bqQi9vE2n5ROXzCEzx3WXlI73LBEfjYVAUtMjX9SW8JQxwF9qcbw3nuJx3E6D9WwPiNjJboxmO909vgjK6PTf2FnE6HiOMG6C83RYKocCCC0Zhmx/rnMrT90N5V80kOC0U764+jAjviJa3Tw+gpwfQQ3wA7Vx9APX71QNo95E8gGyMXxFgkuTGy2tiAbcdE1ts8WrbbMZ84/X24PS0vdnijQZvK6PX1xfaZjQgNV+nD65NkiGPu5SPNp7BnDc8DHrEgJcXGcQbqJDhH7XnpF8CBPOMMqlUc7ORG+fG+KAVPhFjmScwxI8bteVqp3WDxX9yMEsqD5jr4Gbgt48aiL3WBmwzyQu2MVXWLYwzEC9BTfNaxozS6htxvBloa60apqTKOCk/z7y42tmEjf2X17tRrnO9B1XlbyGvR27rADZY22DH278Nm7Kf1JWJ+BxMGWc6/0I5halq9IX1zX1bsf45+Lz8uhO8B6ylsW228hFGh2B0TP3xZ1ELsb/6F7MoRVUzIAAA
+13
View File
@@ -0,0 +1,13 @@
---
apiVersion: grafana.integreatly.org/v1beta1
kind: GrafanaDashboard
metadata:
name: gitea
namespace: grafana
spec:
instanceSelector:
matchLabels:
dashboards: "grafana"
resyncPeriod: 5m
allowCrossNamespaceImport: false
gzipJson: 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
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
+13
View File
@@ -0,0 +1,13 @@
---
apiVersion: grafana.integreatly.org/v1beta1
kind: GrafanaDashboard
metadata:
name: nzbget
namespace: grafana
spec:
instanceSelector:
matchLabels:
dashboards: "grafana"
resyncPeriod: 5m
allowCrossNamespaceImport: false
gzipJson: H4sIAAycS2oC/+1X227bRhD9FWIfCqegDcmNAtdAH2wHLgzErgEZDVpbMJbkiNx4uUvsDq0blG/vzHJFybHbpg3aIkBfJHEvczlz5gy1EtIYixKVNV4cr4RWHsXx7UpkrdJ4YcTxMBWFROlt63LgI7ho6FuUTk6lkSIVrSroeX8/+bFbSvb3xToVYGSm6SS6FlJRqaL/rXJrzqy2jq65MpN7gzQ5HA7pYzRKk+ErsmlkzU5OttEl3yQnGhx62o0hFNJXmZWuEOsJOywU7rqcKp9L/QtIN0bp8NIarMTxIOXQm+rGWo2qCQlqZR4o/dtJKhppQPsAwYtpN87WgBW0vs+8ADxcjEaFzF9/eMg59akCXZxZM1UlXy1gKluNAeC8y3slalsEe1IDIuznWnqvwu289WhrPiPnyp9aV4AbV3YmjqdSe0qMl8/AIDgofgVnn26wg8vOOMIcRbf8TmagaSk+XmuZQ002aEm2aGk5k+5Eq9J0q4Ow8F4VWJ3LHDnmwcEb4oKTszEuGGWGDQRnq/VPjcwVLjbwFoqMxCCMDaeYAOeuy0tDCabow8ZNKeLzo1rG34SGMp6qftVqrko8wI4vOP/G6sCOGIx0otsMcYfSNlYZHKslRTJKBRMC3hLHncra7mJf2WiAXHoC+5rveQ4fHoHNemLG0yg8yvxBmVDi0tm2YcLSyXonb7KGlQOyqAsfcetrb6dTsd7Q5VKZSznvjdeyach2ZOXWxs51mXmrW2RwPULTkTbyiwICMLTzKHVLhw1Fvk6328Sc7ebRIPRPaxTT4ZQs0ZOltB3VrDNbS8wrCMQNnM8WV9yhdKyJ2iFuqFN1clMREmXVtMhGqFsaqp6KVsLVLoTe+YrSn0Nx9kncMcewxyBtLofeOOBadWju2GE5IDfDQTocTDpaRpEgA5OQYkkZXdsAIh09SgU11eHrVMwDcZm+zLgiMKfPbMtXwk/qnHMR1K54ZQMvOVqQIWgVPqmM5L9QvtFysWnFoEyEyE7jOVVW3J/Mt3fRBUvXeqcl+mp7YkOw4K3Dnl58VLoS8Esli6LlCG9IMd6TPPY8ZFHt9STnL1qbN1wq5STCnllm5P7ewMzfU6cSa+6RmXCfLRD8qlv64eOdOPj2TqxvR/WE9X1KuF0CyjG1XF713pTJdVvAVdzldPqhYajfGLaNBgTAzq0jalIwq+hpvSbrTpqyHwMOphdFbM3Wwyk17VZ8mFh/H7SXwYGaiuy2CHZo+bbe+xzEAkKv7ozYzMwemj/M/1n3/Q4KpzQriTMKg4J3oSRv3yXjBqBIskVy7ewj+XXbMYuqBoqTm/jL4PoqxyIvj+0USZ83U/H/MfmfjMl/fSR+MgU/a4AcxQFy+CcD5NmACG/eT+dDZpEZ/xUNCP7X0GlHVL2odVwsWdzntjX4T6j/SY7qERLSFgN5h/pfmgLPJfFFiy8I4iSYZirSxjAUi16UavkzOB8a57vvuQI9a8NwwNgO8d8WE4tthpeh0PtUvNn+m4pd2u5JxDONosBZKePzkqtMwDs7851oP8mjLyjA4GFp1AezHJSDnIm+CZC6fwbwEP4jsRKufwNsYW6gFQ4AAA==
File diff suppressed because one or more lines are too long
@@ -0,0 +1,13 @@
---
apiVersion: grafana.integreatly.org/v1beta1
kind: GrafanaDashboard
metadata:
name: puppet-report
namespace: grafana
spec:
instanceSelector:
matchLabels:
dashboards: "grafana"
resyncPeriod: 5m
allowCrossNamespaceImport: false
gzipJson: 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
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
@@ -2,36 +2,38 @@
apiVersion: gateway.networking.k8s.io/v1 apiVersion: gateway.networking.k8s.io/v1
kind: Gateway kind: Gateway
metadata: metadata:
name: grafana
namespace: grafana
labels: labels:
app.kubernetes.io/name: grafana
app.kubernetes.io/instance: grafana
traefik.io/instance: internal traefik.io/instance: internal
annotations: annotations:
cert-manager.io/cluster-issuer: vault-issuer cert-manager.io/cluster-issuer: vault-issuer
cert-manager.io/common-name: jellyfin.k8s.syd1.au.unkin.net cert-manager.io/common-name: grafana.k8s.syd1.au.unkin.net
cert-manager.io/private-key-size: "4096" cert-manager.io/private-key-size: "4096"
external-dns.alpha.kubernetes.io/hostname: jellyfin.k8s.syd1.au.unkin.net external-dns.alpha.kubernetes.io/hostname: grafana.k8s.syd1.au.unkin.net
external-dns.alpha.kubernetes.io/target: 198.18.200.4 external-dns.alpha.kubernetes.io/target: 198.18.200.4
name: jellyfin
namespace: jellyfin
spec: spec:
gatewayClassName: traefik-internal gatewayClassName: traefik-internal
listeners: listeners:
- allowedRoutes: - name: http
namespaces:
from: Same
hostname: jellyfin.k8s.syd1.au.unkin.net
name: http
port: 80 port: 80
protocol: HTTP protocol: HTTP
- allowedRoutes: hostname: grafana.k8s.syd1.au.unkin.net
allowedRoutes:
namespaces: namespaces:
from: Same from: Same
hostname: jellyfin.k8s.syd1.au.unkin.net - name: https
name: https
port: 443 port: 443
protocol: HTTPS protocol: HTTPS
hostname: grafana.k8s.syd1.au.unkin.net
allowedRoutes:
namespaces:
from: Same
tls: tls:
mode: Terminate
certificateRefs: certificateRefs:
- group: "" - group: ""
kind: Secret kind: Secret
name: jellyfin-tls name: grafana-tls
mode: Terminate
+60
View File
@@ -0,0 +1,60 @@
---
apiVersion: grafana.integreatly.org/v1beta1
kind: Grafana
metadata:
name: grafana
namespace: grafana
labels:
dashboards: "grafana"
spec:
deployment:
spec:
template:
spec:
containers:
- name: grafana
env:
# DB password + OAuth client secret injected from the
# Vault-synced secrets (GF_ env overrides grafana.ini).
- name: GF_DATABASE_PASSWORD
valueFrom:
secretKeyRef:
name: postgres-credentials
key: password
- name: GF_AUTH_GENERIC_OAUTH_CLIENT_SECRET
valueFrom:
secretKeyRef:
name: oauth-credentials
key: client_secret
resources:
requests:
cpu: 100m
memory: 256Mi
limits:
cpu: "1"
memory: 1Gi
config:
server:
root_url: "https://grafana.k8s.syd1.au.unkin.net"
database:
type: "postgres"
host: "postgres-pooler-rw.grafana.svc.cluster.local:5432"
name: "grafana"
user: "grafana"
ssl_mode: "require"
auth:
disable_login_form: "false"
oauth_auto_login: "false"
"auth.generic_oauth":
enabled: "true"
name: "Authentik"
allow_sign_up: "true"
use_pkce: "true"
client_id: "grafana"
scopes: "openid email profile"
auth_url: "https://identity.unkin.net/application/o/authorize/"
token_url: "https://identity.unkin.net/application/o/token/"
api_url: "https://identity.unkin.net/application/o/userinfo/"
# Authentik groups -> Grafana roles (adjust group name as needed).
role_attribute_path: "contains(groups[*], 'grafana-admins') && 'Admin' || 'Viewer'"
role_attribute_strict: "false"
+22
View File
@@ -0,0 +1,22 @@
---
apiVersion: grafana.integreatly.org/v1beta1
kind: GrafanaDatasource
metadata:
name: victoriametrics
namespace: grafana
spec:
instanceSelector:
matchLabels:
dashboards: "grafana"
# uid matches the previous default datasource so the imported dashboards
# (which hardcode this uid or use the default) resolve without edits.
datasource:
name: "VictoriaMetrics"
type: "prometheus"
uid: "det2y55dac4jkc"
access: "proxy"
url: "http://vmselect-main.observability.svc.cluster.local:8481/select/0/prometheus"
isDefault: true
jsonData:
timeInterval: "15s"
httpMethod: "POST"
@@ -2,15 +2,18 @@
apiVersion: gateway.networking.k8s.io/v1 apiVersion: gateway.networking.k8s.io/v1
kind: HTTPRoute kind: HTTPRoute
metadata: metadata:
name: http-redirect name: grafana-http-redirect
namespace: jellyfin namespace: grafana
labels:
app.kubernetes.io/name: grafana
app.kubernetes.io/instance: grafana
spec: spec:
hostnames: hostnames:
- jellyfin.k8s.syd1.au.unkin.net - grafana.k8s.syd1.au.unkin.net
parentRefs: parentRefs:
- group: gateway.networking.k8s.io - group: gateway.networking.k8s.io
kind: Gateway kind: Gateway
name: jellyfin name: grafana
sectionName: http sectionName: http
rules: rules:
- filters: - filters:
@@ -26,22 +29,25 @@ spec:
apiVersion: gateway.networking.k8s.io/v1 apiVersion: gateway.networking.k8s.io/v1
kind: HTTPRoute kind: HTTPRoute
metadata: metadata:
name: jellyfin-route name: grafana
namespace: jellyfin namespace: grafana
labels:
app.kubernetes.io/name: grafana
app.kubernetes.io/instance: grafana
spec: spec:
hostnames: hostnames:
- jellyfin.k8s.syd1.au.unkin.net - grafana.k8s.syd1.au.unkin.net
parentRefs: parentRefs:
- group: gateway.networking.k8s.io - group: gateway.networking.k8s.io
kind: Gateway kind: Gateway
name: jellyfin name: grafana
sectionName: https sectionName: https
rules: rules:
- backendRefs: - backendRefs:
- group: "" - group: ""
kind: Service kind: Service
name: jellyfin name: grafana-service
port: 8096 port: 3000
weight: 1 weight: 1
matches: matches:
- path: - path:
+28
View File
@@ -0,0 +1,28 @@
---
apiVersion: kustomize.config.k8s.io/v1beta1
kind: Kustomization
resources:
- namespace.yaml
- cnpg_cluster.yaml
- cnpg_pooler.yaml
- vaultauth.yaml
- vaultstaticsecret.yaml
- grafana.yaml
- grafanadatasource.yaml
- gateway.yaml
- httproute.yaml
- dashboards/bind9-exporter-dns.yaml
- dashboards/ceph-cluster.yaml
- dashboards/frr-ospf-route-metrics.yaml
- dashboards/gitea.yaml
- dashboards/haproxy.yaml
- dashboards/media-dashboard.yaml
- dashboards/node-exporter-full.yaml
- dashboards/nzbget.yaml
- dashboards/postgresql-database.yaml
- dashboards/puppet-report.yaml
- dashboards/victorialogs-cluster.yaml
- dashboards/victoriametrics-cluster.yaml
- dashboards/victoriametrics-vmagent.yaml
- dashboards/cloudnativepg.yaml
+7
View File
@@ -0,0 +1,7 @@
---
apiVersion: v1
kind: Namespace
metadata:
labels:
app.kubernetes.io/name: grafana
name: grafana
+18
View File
@@ -0,0 +1,18 @@
---
apiVersion: secrets.hashicorp.com/v1beta1
kind: VaultAuth
metadata:
name: default
namespace: grafana
spec:
allowedNamespaces:
- grafana
kubernetes:
audiences:
- vault
role: default
serviceAccount: default
tokenExpirationSeconds: 600
method: kubernetes
mount: k8s/au/syd1
vaultConnectionRef: vso-system/default
+34
View File
@@ -0,0 +1,34 @@
---
apiVersion: secrets.hashicorp.com/v1beta1
kind: VaultStaticSecret
metadata:
name: postgres-credentials
namespace: grafana
spec:
destination:
create: true
name: postgres-credentials
overwrite: true
hmacSecretData: true
mount: kv
path: kubernetes/namespace/grafana/default/postgres-credentials
refreshAfter: 5m
type: kv-v2
vaultAuthRef: default
---
apiVersion: secrets.hashicorp.com/v1beta1
kind: VaultStaticSecret
metadata:
name: oauth-credentials
namespace: grafana
spec:
destination:
create: true
name: oauth-credentials
overwrite: true
hmacSecretData: true
mount: kv
path: kubernetes/namespace/grafana/default/oauth-credentials
refreshAfter: 5m
type: kv-v2
vaultAuthRef: default
-80
View File
@@ -1,80 +0,0 @@
---
apiVersion: apps/v1
kind: Deployment
metadata:
name: jellyfin
namespace: jellyfin
spec:
# Start single-replica. Scaling to >1 (true HA) is a follow-up once the Redis
# transcode store and RWX transcode scratch are validated end-to-end.
replicas: 1
strategy:
# Config PVC is RWO; a Recreate rollout avoids two pods contending for it.
type: Recreate
selector:
matchLabels:
app: jellyfin
template:
metadata:
labels:
app: jellyfin
spec:
containers:
- name: jellyfin
image: git.unkin.net/unkin/jellyfin-ha:v0.1.0
imagePullPolicy: IfNotPresent
ports:
- name: http
containerPort: 8096
protocol: TCP
env:
# Distributed transcode session store (jellyfin-ha additions).
- name: Jellyfin__TranscodeStore__RedisConnectionString
value: "redis:6379,abortConnect=false"
- name: Jellyfin__TranscodeStore__LeaseDurationSeconds
value: "30"
livenessProbe:
httpGet:
path: /health
port: http
initialDelaySeconds: 30
periodSeconds: 30
timeoutSeconds: 5
failureThreshold: 3
readinessProbe:
httpGet:
path: /health
port: http
initialDelaySeconds: 10
periodSeconds: 10
timeoutSeconds: 5
failureThreshold: 3
resources:
requests:
cpu: "1"
memory: 1Gi
limits:
cpu: "4"
memory: 6Gi
volumeMounts:
- name: config
mountPath: /config
- name: cache
mountPath: /cache
- name: transcode
mountPath: /transcode
- name: media
mountPath: /media
readOnly: true
volumes:
- name: config
persistentVolumeClaim:
claimName: jellyfin-config
- name: cache
emptyDir: {}
- name: transcode
persistentVolumeClaim:
claimName: jellyfin-transcode
- name: media
persistentVolumeClaim:
claimName: jellyfin-media
-16
View File
@@ -1,16 +0,0 @@
---
apiVersion: kustomize.config.k8s.io/v1beta1
kind: Kustomization
resources:
- namespace.yaml
- pvc-config.yaml
- pvc-transcode.yaml
- pvc-media.yaml
- deployment.yaml
- service.yaml
- redis-deployment.yaml
- redis-pvc.yaml
- redis-service.yaml
- gateway.yaml
- httproute.yaml
-5
View File
@@ -1,5 +0,0 @@
---
apiVersion: v1
kind: Namespace
metadata:
name: jellyfin
-15
View File
@@ -1,15 +0,0 @@
---
# Jellyfin config + SQLite library database. Single-writer, block storage.
apiVersion: v1
kind: PersistentVolumeClaim
metadata:
name: jellyfin-config
namespace: jellyfin
spec:
accessModes:
- ReadWriteOnce
resources:
requests:
storage: 20Gi
storageClassName: cephrbd-fast-retain
volumeMode: Filesystem
-17
View File
@@ -1,17 +0,0 @@
---
# Media library, shared read-many across replicas. Retain — this holds the
# actual media and must survive PVC deletion. Empty on first deploy; populating
# it is out of scope for this app.
apiVersion: v1
kind: PersistentVolumeClaim
metadata:
name: jellyfin-media
namespace: jellyfin
spec:
accessModes:
- ReadWriteMany
resources:
requests:
storage: 1Ti
storageClassName: cephfs-raid6-retain
volumeMode: Filesystem
-17
View File
@@ -1,17 +0,0 @@
---
# Shared transcode scratch. ReadWriteMany is the hard requirement for the HA
# fork: a taking-over pod must read the in-flight HLS segments written by the
# pod it replaces. Scratch data, so delete reclaim policy.
apiVersion: v1
kind: PersistentVolumeClaim
metadata:
name: jellyfin-transcode
namespace: jellyfin
spec:
accessModes:
- ReadWriteMany
resources:
requests:
storage: 100Gi
storageClassName: cephfs-raid6-delete
volumeMode: Filesystem
-66
View File
@@ -1,66 +0,0 @@
---
apiVersion: apps/v1
kind: Deployment
metadata:
name: redis
namespace: jellyfin
spec:
replicas: 1
selector:
matchLabels:
app: redis
strategy:
type: Recreate
template:
metadata:
labels:
app: redis
spec:
containers:
- name: redis
image: redis:7-alpine
imagePullPolicy: IfNotPresent
command:
- redis-server
- --save
- "20"
- "1"
ports:
- containerPort: 6379
name: redis
protocol: TCP
livenessProbe:
exec:
command:
- redis-cli
- ping
failureThreshold: 3
initialDelaySeconds: 30
periodSeconds: 30
successThreshold: 1
timeoutSeconds: 5
readinessProbe:
exec:
command:
- redis-cli
- ping
failureThreshold: 3
initialDelaySeconds: 5
periodSeconds: 10
successThreshold: 1
timeoutSeconds: 5
resources:
limits:
cpu: 500m
memory: 512Mi
requests:
cpu: 50m
memory: 128Mi
volumeMounts:
- mountPath: /data
name: data
restartPolicy: Always
volumes:
- name: data
persistentVolumeClaim:
claimName: jellyfin-redis-data
-14
View File
@@ -1,14 +0,0 @@
---
apiVersion: v1
kind: PersistentVolumeClaim
metadata:
name: jellyfin-redis-data
namespace: jellyfin
spec:
accessModes:
- ReadWriteOnce
resources:
requests:
storage: 5Gi
storageClassName: cephrbd-fast-delete
volumeMode: Filesystem
-17
View File
@@ -1,17 +0,0 @@
---
apiVersion: v1
kind: Service
metadata:
name: redis
namespace: jellyfin
spec:
internalTrafficPolicy: Cluster
ports:
- name: redis
port: 6379
protocol: TCP
targetPort: redis
selector:
app: redis
sessionAffinity: None
type: ClusterIP
-17
View File
@@ -1,17 +0,0 @@
---
apiVersion: v1
kind: Service
metadata:
name: jellyfin
namespace: jellyfin
spec:
internalTrafficPolicy: Cluster
ports:
- name: http
port: 8096
protocol: TCP
targetPort: http
selector:
app: jellyfin
sessionAffinity: None
type: ClusterIP
+1 -1
View File
@@ -79,7 +79,7 @@ spec:
cpu: "1" cpu: "1"
memory: 1Gi memory: 1Gi
requests: requests:
cpu: 250m cpu: 50m
memory: 512Mi memory: 512Mi
smartShutdownTimeout: 180 smartShutdownTimeout: 180
startDelay: 3600 startDelay: 3600
@@ -8,3 +8,4 @@ resources:
- vmagent.yaml - vmagent.yaml
- gateway.yaml - gateway.yaml
- httproute.yaml - httproute.yaml
- vmpodscrape-cnpg.yaml
+5 -1
View File
@@ -16,7 +16,7 @@ spec:
- url: http://vminsert-main.observability.svc.cluster.local:8480/insert/0/prometheus/ - url: http://vminsert-main.observability.svc.cluster.local:8480/insert/0/prometheus/
resources: resources:
requests: requests:
cpu: 500m cpu: 250m
memory: 512Mi memory: 512Mi
limits: limits:
cpu: "1" cpu: "1"
@@ -99,6 +99,10 @@ spec:
consul_sd_configs: consul_sd_configs:
- server: consul.service.consul:443 - server: consul.service.consul:443
scheme: https scheme: https
# Set datacenter explicitly so VM does not call /v1/agent/self
# (which needs agent:read that the anonymous token lacks); catalog
# and health reads work anonymously.
datacenter: au-syd1
tls_config: tls_config:
ca_file: /etc/vmagent-tls/ca.crt ca_file: /etc/vmagent-tls/ca.crt
relabel_configs: relabel_configs:
+1 -1
View File
@@ -23,7 +23,7 @@ spec:
storage: 200Gi storage: 200Gi
resources: resources:
requests: requests:
cpu: "1" cpu: 250m
memory: 2Gi memory: 2Gi
limits: limits:
cpu: "2" cpu: "2"
@@ -0,0 +1,17 @@
---
# Scrape metrics (:9187) from every CloudNativePG postgres instance pod in any
# namespace. Picked up by the observability VMAgent (selectAllByDefault).
apiVersion: operator.victoriametrics.com/v1beta1
kind: VMPodScrape
metadata:
name: cnpg-clusters
namespace: observability
spec:
namespaceSelector:
any: true
selector:
matchLabels:
cnpg.io/podRole: instance
podMetricsEndpoints:
- port: metrics
path: /metrics
+1 -1
View File
@@ -79,7 +79,7 @@ spec:
cpu: 500m cpu: 500m
memory: 512Mi memory: 512Mi
requests: requests:
cpu: 250m cpu: 50m
memory: 256Mi memory: 256Mi
smartShutdownTimeout: 180 smartShutdownTimeout: 180
startDelay: 3600 startDelay: 3600
+1 -1
View File
@@ -100,7 +100,7 @@ spec:
cpu: 500m cpu: 500m
memory: 512Mi memory: 512Mi
requests: requests:
cpu: 250m cpu: 50m
memory: 256Mi memory: 256Mi
smartShutdownTimeout: 180 smartShutdownTimeout: 180
startDelay: 3600 startDelay: 3600
+1 -1
View File
@@ -47,7 +47,7 @@ spec:
cpu: 300m cpu: 300m
memory: 1Gi memory: 1Gi
requests: requests:
cpu: 200m cpu: 50m
memory: 512Mi memory: 512Mi
securityContext: securityContext:
runAsUser: 0 runAsUser: 0
+1 -1
View File
@@ -148,7 +148,7 @@ spec:
resources: resources:
requests: requests:
memory: 350Mi memory: 350Mi
cpu: 100m cpu: 50m
limits: limits:
memory: 1024Mi memory: 1024Mi
cpu: 500m cpu: 500m
+2 -2
View File
@@ -38,8 +38,8 @@ spec:
cpu: 1 cpu: 1
memory: 1536Mi memory: 1536Mi
requests: requests:
cpu: 250m cpu: 50m
memory: 512Mi memory: 768Mi
ports: ports:
- containerPort: 8080 - containerPort: 8080
name: pdb-http name: pdb-http
@@ -34,7 +34,7 @@ spec:
cpu: 2 cpu: 2
memory: 3072Mi memory: 3072Mi
requests: requests:
cpu: 500m cpu: 200m
memory: 1024Mi memory: 1024Mi
ports: ports:
- containerPort: 8140 - containerPort: 8140
@@ -38,7 +38,7 @@ spec:
cpu: 2 cpu: 2
memory: 3500Mi memory: 3500Mi
requests: requests:
cpu: 250m cpu: 100m
memory: 1024Mi memory: 1024Mi
ports: ports:
- containerPort: 8140 - containerPort: 8140
+2 -2
View File
@@ -83,7 +83,7 @@ server:
cpu: "2" cpu: "2"
memory: 2Gi memory: 2Gi
requests: requests:
cpu: 250m cpu: 50m
memory: 512Mi memory: 512Mi
worker: worker:
@@ -95,7 +95,7 @@ worker:
cpu: "2" cpu: "2"
memory: 2Gi memory: 2Gi
requests: requests:
cpu: 250m cpu: 100m
memory: 512Mi memory: 512Mi
postgresql: postgresql:
@@ -2,5 +2,7 @@
apiVersion: kustomize.config.k8s.io/v1beta1 apiVersion: kustomize.config.k8s.io/v1beta1
kind: Kustomization kind: Kustomization
namespace: grafana
resources: resources:
- ../../../base/paperclip - ../../../base/grafana
+1 -1
View File
@@ -15,7 +15,7 @@ agent:
reloader.stakater.com/auto: "true" reloader.stakater.com/auto: "true"
resources: resources:
requests: requests:
cpu: 100m cpu: 50m
limits: limits:
memory: 128Mi memory: 128Mi
@@ -4,7 +4,6 @@ kind: Kustomization
resources: resources:
- aitooling.yaml - aitooling.yaml
- media.yaml
- observability.yaml - observability.yaml
- platform.yaml - platform.yaml
- storage.yaml - storage.yaml
-31
View File
@@ -1,31 +0,0 @@
---
apiVersion: argoproj.io/v1alpha1
kind: ApplicationSet
metadata:
name: media-apps
namespace: argocd
spec:
generators:
- git:
repoURL: https://git.unkin.net/unkin/argocd-apps
revision: HEAD
directories:
- path: apps/overlays/*/jellyfin
template:
metadata:
name: 'media-{{path[3]}}'
spec:
project: media
source:
repoURL: https://git.unkin.net/unkin/argocd-apps
targetRevision: HEAD
path: '{{path}}'
destination:
server: https://kubernetes.default.svc
namespace: '{{path[3]}}'
syncPolicy:
automated:
prune: true
selfHeal: true
syncOptions:
- ServerSideApply=true
@@ -11,6 +11,7 @@ spec:
revision: HEAD revision: HEAD
directories: directories:
- path: apps/overlays/*/observability - path: apps/overlays/*/observability
- path: apps/overlays/*/grafana
template: template:
metadata: metadata:
name: 'observability-{{path[3]}}' name: 'observability-{{path[3]}}'
-1
View File
@@ -4,7 +4,6 @@ kind: Kustomization
resources: resources:
- aitooling.yaml - aitooling.yaml
- media.yaml
- observability.yaml - observability.yaml
- platform.yaml - platform.yaml
- storage.yaml - storage.yaml
-19
View File
@@ -1,19 +0,0 @@
---
apiVersion: argoproj.io/v1alpha1
kind: AppProject
metadata:
name: media
namespace: argocd
spec:
description: Media services
sourceRepos:
- https://git.unkin.net/unkin/argocd-apps
destinations:
- namespace: 'jellyfin'
server: https://kubernetes.default.svc
clusterResourceWhitelist:
- group: ''
kind: Namespace
namespaceResourceWhitelist:
- group: '*'
kind: '*'
+2
View File
@@ -11,6 +11,8 @@ spec:
destinations: destinations:
- namespace: 'observability' - namespace: 'observability'
server: https://kubernetes.default.svc server: https://kubernetes.default.svc
- namespace: 'grafana'
server: https://kubernetes.default.svc
clusterResourceWhitelist: clusterResourceWhitelist:
- group: '' - group: ''
kind: Namespace kind: Namespace
@@ -0,0 +1,207 @@
{
"$schema": "http://json-schema.org/draft-07/schema#",
"properties": {
"apiVersion": {
"type": "string"
},
"kind": {
"type": "string"
},
"metadata": {
"type": "object"
},
"spec": {
"properties": {
"env": {
"additionalProperties": {
"type": "string"
},
"type": "object"
},
"image": {
"default": "git.unkin.net/unkin/bind-tsig-api:latest",
"type": "string"
},
"imagePullPolicy": {
"type": "string"
},
"port": {
"default": 8443,
"format": "int32",
"type": "integer"
},
"replicas": {
"default": 1,
"format": "int32",
"type": "integer"
},
"resources": {
"properties": {
"claims": {
"items": {
"properties": {
"name": {
"type": "string"
},
"request": {
"type": "string"
}
},
"required": [
"name"
],
"type": "object"
},
"type": "array",
"x-kubernetes-list-map-keys": [
"name"
],
"x-kubernetes-list-type": "map"
},
"limits": {
"additionalProperties": {
"anyOf": [
{
"type": "integer"
},
{
"type": "string"
}
],
"pattern": "^(\\+|-)?(([0-9]+(\\.[0-9]*)?)|(\\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\\+|-)?(([0-9]+(\\.[0-9]*)?)|(\\.[0-9]+))))?$",
"x-kubernetes-int-or-string": true
},
"type": "object"
},
"requests": {
"additionalProperties": {
"anyOf": [
{
"type": "integer"
},
{
"type": "string"
}
],
"pattern": "^(\\+|-)?(([0-9]+(\\.[0-9]*)?)|(\\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\\+|-)?(([0-9]+(\\.[0-9]*)?)|(\\.[0-9]+))))?$",
"x-kubernetes-int-or-string": true
},
"type": "object"
}
},
"type": "object"
},
"service": {
"properties": {
"annotations": {
"additionalProperties": {
"type": "string"
},
"type": "object"
},
"externalTrafficPolicy": {
"enum": [
"Cluster",
"Local"
],
"type": "string"
},
"loadBalancerIP": {
"type": "string"
},
"type": {
"enum": [
"ClusterIP",
"LoadBalancer",
"NodePort"
],
"type": "string"
}
},
"type": "object"
},
"targetNamespace": {
"type": "string"
},
"tokenSecretName": {
"type": "string"
}
},
"type": "object"
},
"status": {
"properties": {
"conditions": {
"items": {
"properties": {
"lastTransitionTime": {
"format": "date-time",
"type": "string"
},
"message": {
"maxLength": 32768,
"type": "string"
},
"observedGeneration": {
"format": "int64",
"minimum": 0,
"type": "integer"
},
"reason": {
"maxLength": 1024,
"minLength": 1,
"pattern": "^[A-Za-z]([A-Za-z0-9_,:]*[A-Za-z0-9_])?$",
"type": "string"
},
"status": {
"enum": [
"True",
"False",
"Unknown"
],
"type": "string"
},
"type": {
"maxLength": 316,
"pattern": "^([a-z0-9]([-a-z0-9]*[a-z0-9])?(\\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*/)?(([A-Za-z0-9][-A-Za-z0-9_.]*)?[A-Za-z0-9])$",
"type": "string"
}
},
"required": [
"lastTransitionTime",
"message",
"reason",
"status",
"type"
],
"type": "object"
},
"type": "array",
"x-kubernetes-list-map-keys": [
"type"
],
"x-kubernetes-list-type": "map"
},
"endpoint": {
"type": "string"
},
"observedGeneration": {
"format": "int64",
"type": "integer"
},
"phase": {
"type": "string"
},
"readyReplicas": {
"format": "int32",
"type": "integer"
},
"tokenSecret": {
"type": "string"
}
},
"type": "object"
}
},
"type": "object"
}