Compare commits
96 Commits
c056d86da2
...
renovate/o
| Author | SHA1 | Date | |
|---|---|---|---|
| 977722f1b5 | |||
| dfafadb4e3 | |||
| ae42e342ca | |||
| 670312d75b | |||
| 0ce1a797fc | |||
| 3d53b4b10b | |||
| 98f63b1576 | |||
| edba33b552 | |||
| 054df42d8b | |||
| 08db022d0d | |||
| e485a4fc7f | |||
| 9e74ed6a19 | |||
| 42e89c9bb7 | |||
| 99bc04b76a | |||
| 7ee77e33d4 | |||
| 8bdd5f2196 | |||
| 1d8cb85bd4 | |||
| eeb302b63b | |||
| 69b437ed3b | |||
| 54674a6e79 | |||
| a9da405326 | |||
| 264871bf68 | |||
| 6bcd0ba464 | |||
| cb53301926 | |||
| 110817b748 | |||
| 66cb3c9d82 | |||
| 42ae7af649 | |||
| cffcb1cc2d | |||
| a4a7dd6fe6 | |||
| 52b8ca79dc | |||
| 9a1fe1f740 | |||
| e996a60378 | |||
| 0ccd4d93f1 | |||
| d667c6c0fc | |||
| 4254ebc9ef | |||
| 8cf02fea0e | |||
| aa3c74d6a7 | |||
| 289089428e | |||
| a93f6ec36f | |||
| 1d85bf3a88 | |||
| f495debf25 | |||
| bfede17c87 | |||
| 08ca3f4c4e | |||
| 471c0ba62d | |||
| 261141f509 | |||
| 86d5751842 | |||
| 43e531a3ca | |||
| 9a0764268b | |||
| 7c88498756 | |||
| 8717526358 | |||
| b6a7e5092c | |||
| 27f7a5f29a | |||
| 9d0fd0981a | |||
| 51bc53dbbc | |||
| ce0b13ebb3 | |||
| 516e157d39 | |||
| 73d6d1f15a | |||
| c51fc2a5ef | |||
| 8d994e7aa1 | |||
| 5b551c6c6e | |||
| 7e7b3e3d71 | |||
| 9f315b38e3 | |||
| 3e1a806db1 | |||
| f7dba45165 | |||
| c8fac3201a | |||
| 82864a4738 | |||
| b54c05b956 | |||
| afdada25a0 | |||
| 79315d32db | |||
| a2a5cd72a9 | |||
| c2706a8af2 | |||
| 610ca0017e | |||
| 466932347a | |||
| afbcea4e82 | |||
| 20ad26ed31 | |||
| 7a2d1e0437 | |||
| 6b5929fb95 | |||
| 6b64f1a8b8 | |||
| 4b4cec10be | |||
| 1f319d607a | |||
| 7d90001f18 | |||
| 7948f53d1d | |||
| 829a5a3fd8 | |||
| cf28dcb5eb | |||
| 4f1764d192 | |||
| 49f88e4f96 | |||
| 4aca8daecd | |||
| 005b52dc4f | |||
| d39846422b | |||
| bc4f378df3 | |||
| db91415017 | |||
| 3c071b88df | |||
| c5ef5e2273 | |||
| c55c37f0ac | |||
| 493f939551 | |||
| 168f480c75 |
1
.gitignore
vendored
1
.gitignore
vendored
@@ -10,3 +10,4 @@ devenv.local.yaml
|
||||
|
||||
# pre-commit
|
||||
.pre-commit-config.yaml
|
||||
.opencode
|
||||
|
||||
3
.vscode/extensions.json
vendored
3
.vscode/extensions.json
vendored
@@ -2,6 +2,7 @@
|
||||
"recommendations": [
|
||||
"jnoortheen.nix-ide",
|
||||
"detachhead.basedpyright",
|
||||
"mkhl.direnv"
|
||||
"mkhl.direnv",
|
||||
"mermaidchart.vscode-mermaid-chart"
|
||||
]
|
||||
}
|
||||
|
||||
15
.woodpecker/my-first-workflow.yaml
Normal file
15
.woodpecker/my-first-workflow.yaml
Normal file
@@ -0,0 +1,15 @@
|
||||
when:
|
||||
- event: push
|
||||
branch: fresh-start
|
||||
|
||||
steps:
|
||||
- name: build
|
||||
image: debian
|
||||
commands:
|
||||
- echo "This is the build step"
|
||||
- echo "echo hello world" > executable
|
||||
- name: a-test-step
|
||||
image: golang:1.16
|
||||
commands:
|
||||
- echo "Testing ..."
|
||||
- sh executable
|
||||
20
Makefile
20
Makefile
@@ -1,3 +1,7 @@
|
||||
SHELL := /usr/bin/env bash
|
||||
|
||||
.PHONY: install-router gen-talos-config apply-talos-config get-kubeconfig garm-image-build garm-image-push garm-image-build-push
|
||||
|
||||
install-router:
|
||||
ansible-playbook ansible/playbook.yml -i ansible/hosts
|
||||
|
||||
@@ -23,3 +27,19 @@ apply-talos-config:
|
||||
|
||||
get-kubeconfig:
|
||||
talosctl -n anapistula-delrosalae kubeconfig talos/generated/kubeconfig
|
||||
|
||||
garm-image-build:
|
||||
set -euo pipefail; \
|
||||
source apps/garm/image-source.env; \
|
||||
docker build \
|
||||
-f docker/garm/Dockerfile \
|
||||
--build-arg GARM_COMMIT=$$GARM_COMMIT \
|
||||
-t $$GARM_IMAGE \
|
||||
.
|
||||
|
||||
garm-image-push:
|
||||
set -euo pipefail; \
|
||||
source apps/garm/image-source.env; \
|
||||
docker push $$GARM_IMAGE
|
||||
|
||||
garm-image-build-push: garm-image-build garm-image-push
|
||||
|
||||
8
apps/authentik/kustomization.yaml
Normal file
8
apps/authentik/kustomization.yaml
Normal file
@@ -0,0 +1,8 @@
|
||||
apiVersion: kustomize.config.k8s.io/v1beta1
|
||||
kind: Kustomization
|
||||
resources:
|
||||
- namespace.yaml
|
||||
- postgres-volume.yaml
|
||||
- postgres-cluster.yaml
|
||||
- secret.yaml
|
||||
- release.yaml
|
||||
4
apps/authentik/namespace.yaml
Normal file
4
apps/authentik/namespace.yaml
Normal file
@@ -0,0 +1,4 @@
|
||||
apiVersion: v1
|
||||
kind: Namespace
|
||||
metadata:
|
||||
name: authentik
|
||||
23
apps/authentik/postgres-cluster.yaml
Normal file
23
apps/authentik/postgres-cluster.yaml
Normal file
@@ -0,0 +1,23 @@
|
||||
---
|
||||
apiVersion: postgresql.cnpg.io/v1
|
||||
kind: Cluster
|
||||
metadata:
|
||||
name: authentik-postgresql-cluster-lvmhdd
|
||||
namespace: authentik
|
||||
spec:
|
||||
instances: 1
|
||||
|
||||
imageName: ghcr.io/cloudnative-pg/postgresql:17.4
|
||||
|
||||
bootstrap:
|
||||
initdb:
|
||||
database: authentik
|
||||
owner: authentik
|
||||
|
||||
storage:
|
||||
pvcTemplate:
|
||||
storageClassName: hdd-lvmpv
|
||||
resources:
|
||||
requests:
|
||||
storage: 10Gi
|
||||
volumeName: authentik-postgresql-cluster-lvmhdd-1
|
||||
33
apps/authentik/postgres-volume.yaml
Normal file
33
apps/authentik/postgres-volume.yaml
Normal file
@@ -0,0 +1,33 @@
|
||||
apiVersion: local.openebs.io/v1alpha1
|
||||
kind: LVMVolume
|
||||
metadata:
|
||||
labels:
|
||||
kubernetes.io/nodename: anapistula-delrosalae
|
||||
name: authentik-postgresql-cluster-lvmhdd-1
|
||||
namespace: openebs
|
||||
spec:
|
||||
capacity: 10Gi
|
||||
ownerNodeID: anapistula-delrosalae
|
||||
shared: "yes"
|
||||
thinProvision: "no"
|
||||
vgPattern: ^openebs-hdd$
|
||||
volGroup: openebs-hdd
|
||||
---
|
||||
kind: PersistentVolume
|
||||
apiVersion: v1
|
||||
metadata:
|
||||
name: authentik-postgresql-cluster-lvmhdd-1
|
||||
spec:
|
||||
capacity:
|
||||
storage: 10Gi
|
||||
accessModes:
|
||||
- ReadWriteOnce
|
||||
persistentVolumeReclaimPolicy: Retain
|
||||
storageClassName: hdd-lvmpv
|
||||
volumeMode: Filesystem
|
||||
csi:
|
||||
driver: local.csi.openebs.io
|
||||
fsType: btrfs
|
||||
volumeHandle: authentik-postgresql-cluster-lvmhdd-1
|
||||
---
|
||||
# PVCs are dynamically created by the Postgres operator
|
||||
61
apps/authentik/release.yaml
Normal file
61
apps/authentik/release.yaml
Normal file
@@ -0,0 +1,61 @@
|
||||
---
|
||||
apiVersion: source.toolkit.fluxcd.io/v1
|
||||
kind: HelmRepository
|
||||
metadata:
|
||||
name: authentik
|
||||
namespace: authentik
|
||||
spec:
|
||||
interval: 24h
|
||||
url: https://charts.goauthentik.io
|
||||
---
|
||||
apiVersion: helm.toolkit.fluxcd.io/v2
|
||||
kind: HelmRelease
|
||||
metadata:
|
||||
name: authentik
|
||||
namespace: authentik
|
||||
spec:
|
||||
interval: 30m
|
||||
chart:
|
||||
spec:
|
||||
chart: authentik
|
||||
version: 2026.2.1
|
||||
sourceRef:
|
||||
kind: HelmRepository
|
||||
name: authentik
|
||||
namespace: authentik
|
||||
interval: 12h
|
||||
values:
|
||||
authentik:
|
||||
postgresql:
|
||||
host: authentik-postgresql-cluster-lvmhdd-rw
|
||||
name: authentik
|
||||
user: authentik
|
||||
|
||||
global:
|
||||
env:
|
||||
- name: AUTHENTIK_SECRET_KEY
|
||||
valueFrom:
|
||||
secretKeyRef:
|
||||
name: authentik-secret
|
||||
key: secret_key
|
||||
- name: AUTHENTIK_POSTGRESQL__PASSWORD
|
||||
valueFrom:
|
||||
secretKeyRef:
|
||||
name: authentik-postgresql-cluster-lvmhdd-app
|
||||
key: password
|
||||
|
||||
postgresql:
|
||||
enabled: false
|
||||
|
||||
server:
|
||||
ingress:
|
||||
enabled: true
|
||||
ingressClassName: nginx-ingress
|
||||
annotations:
|
||||
cert-manager.io/cluster-issuer: letsencrypt
|
||||
hosts:
|
||||
- authentik.lumpiasty.xyz
|
||||
tls:
|
||||
- secretName: authentik-ingress
|
||||
hosts:
|
||||
- authentik.lumpiasty.xyz
|
||||
38
apps/authentik/secret.yaml
Normal file
38
apps/authentik/secret.yaml
Normal file
@@ -0,0 +1,38 @@
|
||||
---
|
||||
apiVersion: v1
|
||||
kind: ServiceAccount
|
||||
metadata:
|
||||
name: authentik-secret
|
||||
namespace: authentik
|
||||
---
|
||||
apiVersion: secrets.hashicorp.com/v1beta1
|
||||
kind: VaultAuth
|
||||
metadata:
|
||||
name: authentik
|
||||
namespace: authentik
|
||||
spec:
|
||||
method: kubernetes
|
||||
mount: kubernetes
|
||||
kubernetes:
|
||||
role: authentik
|
||||
serviceAccount: authentik-secret
|
||||
---
|
||||
apiVersion: secrets.hashicorp.com/v1beta1
|
||||
kind: VaultStaticSecret
|
||||
metadata:
|
||||
name: authentik-secret
|
||||
namespace: authentik
|
||||
spec:
|
||||
type: kv-v2
|
||||
|
||||
mount: secret
|
||||
path: authentik
|
||||
|
||||
destination:
|
||||
create: true
|
||||
name: authentik-secret
|
||||
type: Opaque
|
||||
transformation:
|
||||
excludeRaw: true
|
||||
|
||||
vaultAuthRef: authentik
|
||||
48
apps/crawl4ai-proxy/deployment.yaml
Normal file
48
apps/crawl4ai-proxy/deployment.yaml
Normal file
@@ -0,0 +1,48 @@
|
||||
apiVersion: apps/v1
|
||||
kind: Deployment
|
||||
metadata:
|
||||
name: crawl4ai-proxy
|
||||
namespace: crawl4ai
|
||||
spec:
|
||||
replicas: 1
|
||||
selector:
|
||||
matchLabels:
|
||||
app: crawl4ai-proxy
|
||||
template:
|
||||
metadata:
|
||||
labels:
|
||||
app: crawl4ai-proxy
|
||||
spec:
|
||||
containers:
|
||||
- name: crawl4ai-proxy
|
||||
image: gitea.lumpiasty.xyz/lumpiasty/crawl4ai-proxy-fit:latest
|
||||
imagePullPolicy: Always
|
||||
env:
|
||||
- name: LISTEN_PORT
|
||||
value: "8000"
|
||||
- name: CRAWL4AI_ENDPOINT
|
||||
value: http://crawl4ai.crawl4ai.svc.cluster.local:11235/crawl
|
||||
ports:
|
||||
- name: http
|
||||
containerPort: 8000
|
||||
readinessProbe:
|
||||
tcpSocket:
|
||||
port: http
|
||||
initialDelaySeconds: 3
|
||||
periodSeconds: 10
|
||||
timeoutSeconds: 2
|
||||
failureThreshold: 6
|
||||
livenessProbe:
|
||||
tcpSocket:
|
||||
port: http
|
||||
initialDelaySeconds: 10
|
||||
periodSeconds: 15
|
||||
timeoutSeconds: 2
|
||||
failureThreshold: 6
|
||||
resources:
|
||||
requests:
|
||||
cpu: 25m
|
||||
memory: 32Mi
|
||||
limits:
|
||||
cpu: 200m
|
||||
memory: 128Mi
|
||||
5
apps/crawl4ai-proxy/kustomization.yaml
Normal file
5
apps/crawl4ai-proxy/kustomization.yaml
Normal file
@@ -0,0 +1,5 @@
|
||||
apiVersion: kustomize.config.k8s.io/v1beta1
|
||||
kind: Kustomization
|
||||
resources:
|
||||
- deployment.yaml
|
||||
- service.yaml
|
||||
14
apps/crawl4ai-proxy/service.yaml
Normal file
14
apps/crawl4ai-proxy/service.yaml
Normal file
@@ -0,0 +1,14 @@
|
||||
apiVersion: v1
|
||||
kind: Service
|
||||
metadata:
|
||||
name: crawl4ai-proxy
|
||||
namespace: crawl4ai
|
||||
spec:
|
||||
type: ClusterIP
|
||||
selector:
|
||||
app: crawl4ai-proxy
|
||||
ports:
|
||||
- name: http
|
||||
port: 8000
|
||||
targetPort: 8000
|
||||
protocol: TCP
|
||||
62
apps/crawl4ai/deployment.yaml
Normal file
62
apps/crawl4ai/deployment.yaml
Normal file
@@ -0,0 +1,62 @@
|
||||
apiVersion: apps/v1
|
||||
kind: Deployment
|
||||
metadata:
|
||||
name: crawl4ai
|
||||
namespace: crawl4ai
|
||||
spec:
|
||||
replicas: 1
|
||||
selector:
|
||||
matchLabels:
|
||||
app: crawl4ai
|
||||
template:
|
||||
metadata:
|
||||
labels:
|
||||
app: crawl4ai
|
||||
spec:
|
||||
containers:
|
||||
- name: crawl4ai
|
||||
image: unclecode/crawl4ai:latest
|
||||
imagePullPolicy: IfNotPresent
|
||||
env:
|
||||
- name: CRAWL4AI_API_TOKEN
|
||||
valueFrom:
|
||||
secretKeyRef:
|
||||
name: crawl4ai-secret
|
||||
key: api_token
|
||||
optional: false
|
||||
- name: MAX_CONCURRENT_TASKS
|
||||
value: "5"
|
||||
ports:
|
||||
- name: http
|
||||
containerPort: 11235
|
||||
readinessProbe:
|
||||
httpGet:
|
||||
path: /health
|
||||
port: http
|
||||
initialDelaySeconds: 10
|
||||
periodSeconds: 10
|
||||
timeoutSeconds: 3
|
||||
failureThreshold: 6
|
||||
livenessProbe:
|
||||
httpGet:
|
||||
path: /health
|
||||
port: http
|
||||
initialDelaySeconds: 30
|
||||
periodSeconds: 15
|
||||
timeoutSeconds: 3
|
||||
failureThreshold: 6
|
||||
resources:
|
||||
requests:
|
||||
cpu: 500m
|
||||
memory: 1Gi
|
||||
limits:
|
||||
cpu: "2"
|
||||
memory: 4Gi
|
||||
volumeMounts:
|
||||
- name: dshm
|
||||
mountPath: /dev/shm
|
||||
volumes:
|
||||
- name: dshm
|
||||
emptyDir:
|
||||
medium: Memory
|
||||
sizeLimit: 1Gi
|
||||
7
apps/crawl4ai/kustomization.yaml
Normal file
7
apps/crawl4ai/kustomization.yaml
Normal file
@@ -0,0 +1,7 @@
|
||||
apiVersion: kustomize.config.k8s.io/v1beta1
|
||||
kind: Kustomization
|
||||
resources:
|
||||
- namespace.yaml
|
||||
- secret.yaml
|
||||
- deployment.yaml
|
||||
- service.yaml
|
||||
4
apps/crawl4ai/namespace.yaml
Normal file
4
apps/crawl4ai/namespace.yaml
Normal file
@@ -0,0 +1,4 @@
|
||||
apiVersion: v1
|
||||
kind: Namespace
|
||||
metadata:
|
||||
name: crawl4ai
|
||||
38
apps/crawl4ai/secret.yaml
Normal file
38
apps/crawl4ai/secret.yaml
Normal file
@@ -0,0 +1,38 @@
|
||||
---
|
||||
apiVersion: v1
|
||||
kind: ServiceAccount
|
||||
metadata:
|
||||
name: crawl4ai-secret
|
||||
namespace: crawl4ai
|
||||
---
|
||||
apiVersion: secrets.hashicorp.com/v1beta1
|
||||
kind: VaultAuth
|
||||
metadata:
|
||||
name: crawl4ai
|
||||
namespace: crawl4ai
|
||||
spec:
|
||||
method: kubernetes
|
||||
mount: kubernetes
|
||||
kubernetes:
|
||||
role: crawl4ai
|
||||
serviceAccount: crawl4ai-secret
|
||||
---
|
||||
apiVersion: secrets.hashicorp.com/v1beta1
|
||||
kind: VaultStaticSecret
|
||||
metadata:
|
||||
name: crawl4ai-secret
|
||||
namespace: crawl4ai
|
||||
spec:
|
||||
type: kv-v2
|
||||
|
||||
mount: secret
|
||||
path: crawl4ai
|
||||
|
||||
destination:
|
||||
create: true
|
||||
name: crawl4ai-secret
|
||||
type: Opaque
|
||||
transformation:
|
||||
excludeRaw: true
|
||||
|
||||
vaultAuthRef: crawl4ai
|
||||
14
apps/crawl4ai/service.yaml
Normal file
14
apps/crawl4ai/service.yaml
Normal file
@@ -0,0 +1,14 @@
|
||||
apiVersion: v1
|
||||
kind: Service
|
||||
metadata:
|
||||
name: crawl4ai
|
||||
namespace: crawl4ai
|
||||
spec:
|
||||
type: ClusterIP
|
||||
selector:
|
||||
app: crawl4ai
|
||||
ports:
|
||||
- name: http
|
||||
port: 11235
|
||||
targetPort: 11235
|
||||
protocol: TCP
|
||||
49
apps/garm/README.md
Normal file
49
apps/garm/README.md
Normal file
@@ -0,0 +1,49 @@
|
||||
# garm
|
||||
|
||||
This app deploys `garm` with external `garm-provider-k8s`.
|
||||
|
||||
- API/UI ingress: `https://garm.lumpiasty.xyz`
|
||||
- Internal service DNS: `http://garm.garm.svc.cluster.local:9997`
|
||||
|
||||
## Vault secret requirements
|
||||
|
||||
`VaultStaticSecret` reads `secret/data/garm` and expects at least:
|
||||
|
||||
- `jwt_auth_secret`
|
||||
- `database_passphrase` (must be 32 characters)
|
||||
|
||||
## Connect garm to Gitea
|
||||
|
||||
After Flux reconciles this app, initialize garm and add Gitea endpoint/credentials.
|
||||
|
||||
```bash
|
||||
# 1) Initialize garm (from your local devenv shell)
|
||||
garm-cli init \
|
||||
--name homelab \
|
||||
--url https://garm.lumpiasty.xyz \
|
||||
--username admin \
|
||||
--email admin@lumpiasty.xyz \
|
||||
--password '<STRONG_ADMIN_PASSWORD>' \
|
||||
--metadata-url http://garm.garm.svc.cluster.local:9997/api/v1/metadata \
|
||||
--callback-url http://garm.garm.svc.cluster.local:9997/api/v1/callbacks \
|
||||
--webhook-url http://garm.garm.svc.cluster.local:9997/webhooks
|
||||
|
||||
# 2) Add Gitea endpoint
|
||||
garm-cli gitea endpoint create \
|
||||
--name local-gitea \
|
||||
--description 'Cluster Gitea' \
|
||||
--base-url http://gitea-http.gitea.svc.cluster.local:80 \
|
||||
--api-base-url http://gitea-http.gitea.svc.cluster.local:80/api/v1
|
||||
|
||||
# 3) Add Gitea PAT credentials
|
||||
garm-cli gitea credentials add \
|
||||
--name gitea-pat \
|
||||
--description 'PAT for garm' \
|
||||
--endpoint local-gitea \
|
||||
--auth-type pat \
|
||||
--pat-oauth-token '<GITEA_PAT_WITH_write:repository,write:organization>'
|
||||
```
|
||||
|
||||
Then add repositories/orgs and create pools against provider `kubernetes_external`.
|
||||
|
||||
If Gitea refuses webhook installation to cluster-local URLs, set `gitea.config.webhook.ALLOWED_HOST_LIST` in `apps/gitea/release.yaml`.
|
||||
19
apps/garm/configmap.yaml
Normal file
19
apps/garm/configmap.yaml
Normal file
@@ -0,0 +1,19 @@
|
||||
apiVersion: v1
|
||||
kind: ConfigMap
|
||||
metadata:
|
||||
name: garm-provider-k8s-config
|
||||
namespace: garm
|
||||
data:
|
||||
provider-config.yaml: |
|
||||
kubeConfigPath: ""
|
||||
runnerNamespace: "garm-runners"
|
||||
podTemplate:
|
||||
spec:
|
||||
restartPolicy: Never
|
||||
flavors:
|
||||
default:
|
||||
requests:
|
||||
cpu: 100m
|
||||
memory: 512Mi
|
||||
limits:
|
||||
memory: 2Gi
|
||||
106
apps/garm/deployment.yaml
Normal file
106
apps/garm/deployment.yaml
Normal file
@@ -0,0 +1,106 @@
|
||||
apiVersion: apps/v1
|
||||
kind: Deployment
|
||||
metadata:
|
||||
name: garm
|
||||
namespace: garm
|
||||
spec:
|
||||
replicas: 1
|
||||
selector:
|
||||
matchLabels:
|
||||
app: garm
|
||||
template:
|
||||
metadata:
|
||||
labels:
|
||||
app: garm
|
||||
spec:
|
||||
serviceAccountName: garm
|
||||
initContainers:
|
||||
- name: render-garm-config
|
||||
image: alpine:3.23
|
||||
env:
|
||||
- name: JWT_AUTH_SECRET
|
||||
valueFrom:
|
||||
secretKeyRef:
|
||||
name: garm-config
|
||||
key: jwt_auth_secret
|
||||
- name: DATABASE_PASSPHRASE
|
||||
valueFrom:
|
||||
secretKeyRef:
|
||||
name: garm-config
|
||||
key: database_passphrase
|
||||
command:
|
||||
- /bin/sh
|
||||
- -ec
|
||||
- |
|
||||
cat <<EOF > /etc/garm/config.toml
|
||||
[default]
|
||||
enable_webhook_management = true
|
||||
|
||||
[logging]
|
||||
enable_log_streamer = true
|
||||
log_format = "text"
|
||||
log_level = "info"
|
||||
log_source = false
|
||||
|
||||
[metrics]
|
||||
enable = true
|
||||
disable_auth = false
|
||||
|
||||
[jwt_auth]
|
||||
secret = "${JWT_AUTH_SECRET}"
|
||||
time_to_live = "8760h"
|
||||
|
||||
[apiserver]
|
||||
bind = "0.0.0.0"
|
||||
port = 9997
|
||||
use_tls = false
|
||||
[apiserver.webui]
|
||||
enable = true
|
||||
|
||||
[database]
|
||||
backend = "sqlite3"
|
||||
passphrase = "${DATABASE_PASSPHRASE}"
|
||||
[database.sqlite3]
|
||||
db_file = "/data/garm.db"
|
||||
busy_timeout_seconds = 5
|
||||
|
||||
[[provider]]
|
||||
name = "kubernetes_external"
|
||||
description = "Kubernetes provider"
|
||||
provider_type = "external"
|
||||
[provider.external]
|
||||
config_file = "/etc/garm/provider-config.yaml"
|
||||
provider_executable = "/opt/garm/providers.d/garm-provider-k8s"
|
||||
environment_variables = ["KUBERNETES_"]
|
||||
EOF
|
||||
volumeMounts:
|
||||
- name: config-dir
|
||||
mountPath: /etc/garm
|
||||
containers:
|
||||
- name: garm
|
||||
image: gitea.lumpiasty.xyz/lumpiasty/garm-k8s:r1380
|
||||
imagePullPolicy: IfNotPresent
|
||||
command:
|
||||
- /bin/garm
|
||||
- --config
|
||||
- /etc/garm/config.toml
|
||||
ports:
|
||||
- name: http
|
||||
containerPort: 9997
|
||||
volumeMounts:
|
||||
- name: data
|
||||
mountPath: /data
|
||||
- name: config-dir
|
||||
mountPath: /etc/garm
|
||||
- name: provider-config
|
||||
mountPath: /etc/garm/provider-config.yaml
|
||||
subPath: provider-config.yaml
|
||||
volumes:
|
||||
- name: data
|
||||
persistentVolumeClaim:
|
||||
claimName: garm-lvmhdd
|
||||
- name: config-dir
|
||||
emptyDir: {}
|
||||
- name: provider-config
|
||||
configMap:
|
||||
name: garm-provider-k8s-config
|
||||
5
apps/garm/image-source.env
Normal file
5
apps/garm/image-source.env
Normal file
@@ -0,0 +1,5 @@
|
||||
# renovate: datasource=github-refs depName=cloudbase/garm versioning=git
|
||||
GARM_COMMIT=818a9dddccba5f2843f185e6a846770988f31fc5
|
||||
GARM_COMMIT_NUMBER=1380
|
||||
GARM_IMAGE_REPO=gitea.lumpiasty.xyz/lumpiasty/garm-k8s
|
||||
GARM_IMAGE=gitea.lumpiasty.xyz/lumpiasty/garm-k8s:r1380
|
||||
24
apps/garm/ingress.yaml
Normal file
24
apps/garm/ingress.yaml
Normal file
@@ -0,0 +1,24 @@
|
||||
apiVersion: networking.k8s.io/v1
|
||||
kind: Ingress
|
||||
metadata:
|
||||
namespace: garm
|
||||
name: garm
|
||||
annotations:
|
||||
cert-manager.io/cluster-issuer: letsencrypt
|
||||
spec:
|
||||
ingressClassName: nginx-ingress
|
||||
rules:
|
||||
- host: garm.lumpiasty.xyz
|
||||
http:
|
||||
paths:
|
||||
- backend:
|
||||
service:
|
||||
name: garm
|
||||
port:
|
||||
number: 9997
|
||||
path: /
|
||||
pathType: Prefix
|
||||
tls:
|
||||
- hosts:
|
||||
- garm.lumpiasty.xyz
|
||||
secretName: garm-ingress
|
||||
11
apps/garm/kustomization.yaml
Normal file
11
apps/garm/kustomization.yaml
Normal file
@@ -0,0 +1,11 @@
|
||||
apiVersion: kustomize.config.k8s.io/v1beta1
|
||||
kind: Kustomization
|
||||
resources:
|
||||
- namespace.yaml
|
||||
- pvc.yaml
|
||||
- configmap.yaml
|
||||
- service.yaml
|
||||
- ingress.yaml
|
||||
- rbac.yaml
|
||||
- secret.yaml
|
||||
- deployment.yaml
|
||||
9
apps/garm/namespace.yaml
Normal file
9
apps/garm/namespace.yaml
Normal file
@@ -0,0 +1,9 @@
|
||||
apiVersion: v1
|
||||
kind: Namespace
|
||||
metadata:
|
||||
name: garm
|
||||
---
|
||||
apiVersion: v1
|
||||
kind: Namespace
|
||||
metadata:
|
||||
name: garm-runners
|
||||
46
apps/garm/pvc.yaml
Normal file
46
apps/garm/pvc.yaml
Normal file
@@ -0,0 +1,46 @@
|
||||
---
|
||||
apiVersion: local.openebs.io/v1alpha1
|
||||
kind: LVMVolume
|
||||
metadata:
|
||||
labels:
|
||||
kubernetes.io/nodename: anapistula-delrosalae
|
||||
name: garm-lvmhdd
|
||||
namespace: openebs
|
||||
spec:
|
||||
capacity: 5Gi
|
||||
ownerNodeID: anapistula-delrosalae
|
||||
shared: "yes"
|
||||
thinProvision: "no"
|
||||
vgPattern: ^openebs-hdd$
|
||||
volGroup: openebs-hdd
|
||||
---
|
||||
kind: PersistentVolume
|
||||
apiVersion: v1
|
||||
metadata:
|
||||
name: garm-lvmhdd
|
||||
spec:
|
||||
capacity:
|
||||
storage: 5Gi
|
||||
accessModes:
|
||||
- ReadWriteOnce
|
||||
persistentVolumeReclaimPolicy: Retain
|
||||
storageClassName: hdd-lvmpv
|
||||
volumeMode: Filesystem
|
||||
csi:
|
||||
driver: local.csi.openebs.io
|
||||
fsType: btrfs
|
||||
volumeHandle: garm-lvmhdd
|
||||
---
|
||||
kind: PersistentVolumeClaim
|
||||
apiVersion: v1
|
||||
metadata:
|
||||
name: garm-lvmhdd
|
||||
namespace: garm
|
||||
spec:
|
||||
accessModes:
|
||||
- ReadWriteOnce
|
||||
resources:
|
||||
requests:
|
||||
storage: 5Gi
|
||||
storageClassName: hdd-lvmpv
|
||||
volumeName: garm-lvmhdd
|
||||
51
apps/garm/rbac.yaml
Normal file
51
apps/garm/rbac.yaml
Normal file
@@ -0,0 +1,51 @@
|
||||
apiVersion: v1
|
||||
kind: ServiceAccount
|
||||
metadata:
|
||||
name: garm
|
||||
namespace: garm
|
||||
---
|
||||
apiVersion: rbac.authorization.k8s.io/v1
|
||||
kind: Role
|
||||
metadata:
|
||||
name: garm-provider-k8s
|
||||
namespace: garm-runners
|
||||
rules:
|
||||
- apiGroups: [""]
|
||||
resources: ["pods", "pods/log", "configmaps", "secrets", "events"]
|
||||
verbs: ["create", "delete", "get", "list", "patch", "update", "watch"]
|
||||
---
|
||||
apiVersion: rbac.authorization.k8s.io/v1
|
||||
kind: RoleBinding
|
||||
metadata:
|
||||
name: garm-provider-k8s
|
||||
namespace: garm-runners
|
||||
subjects:
|
||||
- kind: ServiceAccount
|
||||
name: garm
|
||||
namespace: garm
|
||||
roleRef:
|
||||
kind: Role
|
||||
name: garm-provider-k8s
|
||||
apiGroup: rbac.authorization.k8s.io
|
||||
---
|
||||
apiVersion: rbac.authorization.k8s.io/v1
|
||||
kind: ClusterRole
|
||||
metadata:
|
||||
name: garm-namespace-manager
|
||||
rules:
|
||||
- apiGroups: [""]
|
||||
resources: ["namespaces"]
|
||||
verbs: ["get"]
|
||||
---
|
||||
apiVersion: rbac.authorization.k8s.io/v1
|
||||
kind: ClusterRoleBinding
|
||||
metadata:
|
||||
name: garm-namespace-manager
|
||||
subjects:
|
||||
- kind: ServiceAccount
|
||||
name: garm
|
||||
namespace: garm
|
||||
roleRef:
|
||||
kind: ClusterRole
|
||||
name: garm-namespace-manager
|
||||
apiGroup: rbac.authorization.k8s.io
|
||||
32
apps/garm/secret.yaml
Normal file
32
apps/garm/secret.yaml
Normal file
@@ -0,0 +1,32 @@
|
||||
---
|
||||
apiVersion: secrets.hashicorp.com/v1beta1
|
||||
kind: VaultAuth
|
||||
metadata:
|
||||
name: garm
|
||||
namespace: garm
|
||||
spec:
|
||||
method: kubernetes
|
||||
mount: kubernetes
|
||||
kubernetes:
|
||||
role: garm
|
||||
serviceAccount: garm
|
||||
---
|
||||
apiVersion: secrets.hashicorp.com/v1beta1
|
||||
kind: VaultStaticSecret
|
||||
metadata:
|
||||
name: garm-config
|
||||
namespace: garm
|
||||
spec:
|
||||
type: kv-v2
|
||||
|
||||
mount: secret
|
||||
path: garm
|
||||
|
||||
destination:
|
||||
create: true
|
||||
name: garm-config
|
||||
type: Opaque
|
||||
transformation:
|
||||
excludeRaw: true
|
||||
|
||||
vaultAuthRef: garm
|
||||
14
apps/garm/service.yaml
Normal file
14
apps/garm/service.yaml
Normal file
@@ -0,0 +1,14 @@
|
||||
apiVersion: v1
|
||||
kind: Service
|
||||
metadata:
|
||||
name: garm
|
||||
namespace: garm
|
||||
spec:
|
||||
type: ClusterIP
|
||||
selector:
|
||||
app: garm
|
||||
ports:
|
||||
- name: http
|
||||
port: 9997
|
||||
targetPort: 9997
|
||||
protocol: TCP
|
||||
@@ -72,6 +72,8 @@ spec:
|
||||
indexer:
|
||||
ISSUE_INDEXER_TYPE: bleve
|
||||
REPO_INDEXER_ENABLED: true
|
||||
webhook:
|
||||
ALLOWED_HOST_LIST: garm.garm.svc.cluster.local,woodpecker.lumpiasty.xyz
|
||||
admin:
|
||||
username: GiteaAdmin
|
||||
email: gi@tea.com
|
||||
@@ -88,6 +90,11 @@ spec:
|
||||
# Requirement for sharing ip with other service
|
||||
externalTrafficPolicy: Cluster
|
||||
ipFamilyPolicy: RequireDualStack
|
||||
http:
|
||||
type: ClusterIP
|
||||
# We need the service to be at port 80 specifically
|
||||
# to work around bug of Actions Runner
|
||||
port: 80
|
||||
|
||||
ingress:
|
||||
enabled: true
|
||||
@@ -95,7 +102,7 @@ spec:
|
||||
annotations:
|
||||
cert-manager.io/cluster-issuer: letsencrypt
|
||||
acme.cert-manager.io/http01-edit-in-place: "true"
|
||||
nginx.ingress.kubernetes.io/proxy-body-size: "100m"
|
||||
nginx.ingress.kubernetes.io/proxy-body-size: "1g"
|
||||
hosts:
|
||||
- host: gitea.lumpiasty.xyz
|
||||
paths:
|
||||
|
||||
@@ -18,7 +18,7 @@ spec:
|
||||
chart:
|
||||
spec:
|
||||
chart: immich
|
||||
version: 1.1.1
|
||||
version: 1.2.2
|
||||
sourceRef:
|
||||
kind: HelmRepository
|
||||
name: secustor
|
||||
|
||||
@@ -1,6 +1,9 @@
|
||||
apiVersion: kustomize.config.k8s.io/v1beta1
|
||||
kind: Kustomization
|
||||
resources:
|
||||
- crawl4ai
|
||||
- crawl4ai-proxy
|
||||
- authentik
|
||||
- gitea
|
||||
- renovate
|
||||
- librechat
|
||||
@@ -11,3 +14,5 @@ resources:
|
||||
- searxng
|
||||
- ispeak3
|
||||
- openwebui
|
||||
- garm
|
||||
- woodpecker
|
||||
|
||||
@@ -4,12 +4,16 @@ logToStdout: "both" # proxy and upstream
|
||||
|
||||
macros:
|
||||
base_args: "--no-warmup --port ${PORT}"
|
||||
common_args: "--fit-target 1536 --fit-ctx 65536 --no-warmup --port ${PORT}"
|
||||
common_args: "--fit-target 1536 --no-warmup --port ${PORT}"
|
||||
gemma3_ctx_128k: "--ctx-size 131072"
|
||||
qwen35_ctx_128k: "--ctx-size 131072"
|
||||
qwen35_ctx_256k: "--ctx-size 262144"
|
||||
gemma_sampling: "--prio 2 --temp 1.0 --repeat-penalty 1.0 --min-p 0.00 --top-k 64 --top-p 0.95"
|
||||
qwen35_sampling: "--temp 0.6 --top-p 0.95 --top-k 20 --min-p 0.00 -ctk q4_0 -ctv q4_0"
|
||||
qwen35_35b_args: "--temp 1.0 --min-p 0.00 --top-p 0.95 --top-k 20 -ctk q4_0 -ctv q4_0"
|
||||
qwen35_sampling: "--temp 0.6 --top-p 0.95 --top-k 20 --min-p 0.00 -ctk q8_0 -ctv q8_0"
|
||||
qwen35_35b_args: "--temp 1.0 --min-p 0.00 --top-p 0.95 --top-k 20 -ctk q8_0 -ctv q8_0"
|
||||
qwen35_35b_heretic_mmproj: "--mmproj-url https://huggingface.co/unsloth/Qwen3.5-35B-A3B-GGUF/resolve/main/mmproj-F16.gguf --mmproj /root/.cache/llama.cpp/unsloth_Qwen3.5-35B-A3B-GGUF_mmproj-F16.gguf"
|
||||
qwen35_4b_heretic_mmproj: "--mmproj-url https://huggingface.co/unsloth/Qwen3.5-4B-GGUF/resolve/main/mmproj-F16.gguf --mmproj /root/.cache/llama.cpp/unsloth_Qwen3.5-4B-GGUF_mmproj-F16.gguf"
|
||||
glm47_flash_args: "--temp 0.7 --top-p 1.0 --min-p 0.01 --repeat-penalty 1.0"
|
||||
thinking_on: "--chat-template-kwargs '{\"enable_thinking\": true}'"
|
||||
thinking_off: "--chat-template-kwargs '{\"enable_thinking\": false}'"
|
||||
|
||||
@@ -38,6 +42,7 @@ models:
|
||||
cmd: |
|
||||
/app/llama-server
|
||||
-hf unsloth/gemma-3-12b-it-GGUF:Q4_K_M
|
||||
${gemma3_ctx_128k}
|
||||
${gemma_sampling}
|
||||
${common_args}
|
||||
|
||||
@@ -45,6 +50,7 @@ models:
|
||||
cmd: |
|
||||
/app/llama-server
|
||||
-hf unsloth/gemma-3-12b-it-GGUF:Q4_K_M
|
||||
${gemma3_ctx_128k}
|
||||
${gemma_sampling}
|
||||
--no-mmproj
|
||||
${common_args}
|
||||
@@ -53,6 +59,7 @@ models:
|
||||
cmd: |
|
||||
/app/llama-server
|
||||
-hf unsloth/gemma-3-4b-it-GGUF:Q4_K_M
|
||||
${gemma3_ctx_128k}
|
||||
${gemma_sampling}
|
||||
${common_args}
|
||||
|
||||
@@ -60,6 +67,7 @@ models:
|
||||
cmd: |
|
||||
/app/llama-server
|
||||
-hf unsloth/gemma-3-4b-it-GGUF:Q4_K_M
|
||||
${gemma3_ctx_128k}
|
||||
${gemma_sampling}
|
||||
--no-mmproj
|
||||
${common_args}
|
||||
@@ -75,13 +83,14 @@ models:
|
||||
--top-p 0.95
|
||||
--top-k 40
|
||||
--repeat-penalty 1.0
|
||||
-ctk q4_0 -ctv q4_0
|
||||
-ctk q8_0 -ctv q8_0
|
||||
${common_args}
|
||||
|
||||
"Qwen3.5-35B-A3B-GGUF:Q4_K_M":
|
||||
cmd: |
|
||||
/app/llama-server
|
||||
-hf unsloth/Qwen3.5-35B-A3B-GGUF:Q4_K_M
|
||||
${qwen35_ctx_256k}
|
||||
${qwen35_35b_args}
|
||||
${common_args}
|
||||
|
||||
@@ -89,6 +98,7 @@ models:
|
||||
cmd: |
|
||||
/app/llama-server
|
||||
-hf unsloth/Qwen3.5-35B-A3B-GGUF:Q4_K_M
|
||||
${qwen35_ctx_256k}
|
||||
${qwen35_35b_args}
|
||||
${common_args}
|
||||
${thinking_off}
|
||||
@@ -100,6 +110,7 @@ models:
|
||||
/app/llama-server
|
||||
-hf mradermacher/Qwen3.5-35B-A3B-heretic-GGUF:Q4_K_M
|
||||
${qwen35_35b_heretic_mmproj}
|
||||
${qwen35_ctx_256k}
|
||||
${qwen35_35b_args}
|
||||
${common_args}
|
||||
|
||||
@@ -108,6 +119,7 @@ models:
|
||||
/app/llama-server
|
||||
-hf mradermacher/Qwen3.5-35B-A3B-heretic-GGUF:Q4_K_M
|
||||
${qwen35_35b_heretic_mmproj}
|
||||
${qwen35_ctx_256k}
|
||||
${qwen35_35b_args}
|
||||
${common_args}
|
||||
${thinking_off}
|
||||
@@ -116,6 +128,7 @@ models:
|
||||
cmd: |
|
||||
/app/llama-server
|
||||
-hf unsloth/Qwen3.5-0.8B-GGUF:Q4_K_XL
|
||||
${qwen35_ctx_256k}
|
||||
${qwen35_sampling}
|
||||
${base_args}
|
||||
${thinking_on}
|
||||
@@ -133,6 +146,7 @@ models:
|
||||
cmd: |
|
||||
/app/llama-server
|
||||
-hf unsloth/Qwen3.5-2B-GGUF:Q4_K_M
|
||||
${qwen35_ctx_256k}
|
||||
${qwen35_sampling}
|
||||
${common_args}
|
||||
${thinking_on}
|
||||
@@ -141,6 +155,7 @@ models:
|
||||
cmd: |
|
||||
/app/llama-server
|
||||
-hf unsloth/Qwen3.5-2B-GGUF:Q4_K_M
|
||||
${qwen35_ctx_256k}
|
||||
${qwen35_sampling}
|
||||
${common_args}
|
||||
${thinking_off}
|
||||
@@ -149,6 +164,7 @@ models:
|
||||
cmd: |
|
||||
/app/llama-server
|
||||
-hf unsloth/Qwen3.5-4B-GGUF:Q4_K_M
|
||||
${qwen35_ctx_128k}
|
||||
${qwen35_sampling}
|
||||
${common_args}
|
||||
${thinking_on}
|
||||
@@ -157,6 +173,7 @@ models:
|
||||
cmd: |
|
||||
/app/llama-server
|
||||
-hf unsloth/Qwen3.5-4B-GGUF:Q4_K_M
|
||||
${qwen35_ctx_128k}
|
||||
${qwen35_sampling}
|
||||
${common_args}
|
||||
${thinking_off}
|
||||
@@ -166,6 +183,7 @@ models:
|
||||
/app/llama-server
|
||||
-hf mradermacher/Qwen3.5-4B-heretic-GGUF:Q4_K_M
|
||||
${qwen35_4b_heretic_mmproj}
|
||||
${qwen35_ctx_128k}
|
||||
${qwen35_sampling}
|
||||
${common_args}
|
||||
${thinking_on}
|
||||
@@ -175,6 +193,7 @@ models:
|
||||
/app/llama-server
|
||||
-hf mradermacher/Qwen3.5-4B-heretic-GGUF:Q4_K_M
|
||||
${qwen35_4b_heretic_mmproj}
|
||||
${qwen35_ctx_128k}
|
||||
${qwen35_sampling}
|
||||
${common_args}
|
||||
${thinking_off}
|
||||
@@ -183,6 +202,7 @@ models:
|
||||
cmd: |
|
||||
/app/llama-server
|
||||
-hf unsloth/Qwen3.5-9B-GGUF:Q4_K_M
|
||||
${qwen35_ctx_256k}
|
||||
${qwen35_sampling}
|
||||
${common_args}
|
||||
${thinking_on}
|
||||
@@ -191,6 +211,7 @@ models:
|
||||
cmd: |
|
||||
/app/llama-server
|
||||
-hf unsloth/Qwen3.5-9B-GGUF:Q4_K_M
|
||||
${qwen35_ctx_256k}
|
||||
${qwen35_sampling}
|
||||
${common_args}
|
||||
${thinking_off}
|
||||
@@ -199,6 +220,7 @@ models:
|
||||
cmd: |
|
||||
/app/llama-server
|
||||
-hf unsloth/Qwen3.5-9B-GGUF:Q3_K_M
|
||||
${qwen35_ctx_256k}
|
||||
${qwen35_sampling}
|
||||
${common_args}
|
||||
${thinking_on}
|
||||
@@ -207,6 +229,7 @@ models:
|
||||
cmd: |
|
||||
/app/llama-server
|
||||
-hf unsloth/Qwen3.5-9B-GGUF:Q3_K_M
|
||||
${qwen35_ctx_256k}
|
||||
${qwen35_sampling}
|
||||
${common_args}
|
||||
${thinking_off}
|
||||
@@ -215,6 +238,7 @@ models:
|
||||
cmd: |
|
||||
/app/llama-server
|
||||
-hf unsloth/Qwen3.5-27B-GGUF:Q3_K_M
|
||||
${qwen35_ctx_256k}
|
||||
${qwen35_sampling}
|
||||
${common_args}
|
||||
${thinking_on}
|
||||
@@ -223,6 +247,14 @@ models:
|
||||
cmd: |
|
||||
/app/llama-server
|
||||
-hf unsloth/Qwen3.5-27B-GGUF:Q3_K_M
|
||||
${qwen35_ctx_256k}
|
||||
${qwen35_sampling}
|
||||
${common_args}
|
||||
${thinking_off}
|
||||
|
||||
"GLM-4.7-Flash-GGUF:Q4_K_M":
|
||||
cmd: |
|
||||
/app/llama-server
|
||||
-hf unsloth/GLM-4.7-Flash-GGUF:Q4_K_M
|
||||
${glm47_flash_args}
|
||||
${common_args}
|
||||
|
||||
@@ -18,7 +18,7 @@ spec:
|
||||
spec:
|
||||
containers:
|
||||
- name: llama-swap
|
||||
image: ghcr.io/mostlygeek/llama-swap:v197-vulkan-b8248
|
||||
image: ghcr.io/mostlygeek/llama-swap:v199-vulkan-b8589
|
||||
imagePullPolicy: IfNotPresent
|
||||
command:
|
||||
- /app/llama-swap
|
||||
|
||||
@@ -4,5 +4,6 @@ resources:
|
||||
- namespace.yaml
|
||||
- pvc.yaml
|
||||
- pvc-pipelines.yaml
|
||||
- secret.yaml
|
||||
- release.yaml
|
||||
- ingress.yaml
|
||||
|
||||
@@ -18,7 +18,7 @@ spec:
|
||||
chart:
|
||||
spec:
|
||||
chart: open-webui
|
||||
version: 12.10.0
|
||||
version: 13.0.1
|
||||
sourceRef:
|
||||
kind: HelmRepository
|
||||
name: open-webui
|
||||
@@ -44,3 +44,30 @@ spec:
|
||||
persistence:
|
||||
enabled: true
|
||||
existingClaim: openwebui-pipelines-lvmhdd
|
||||
|
||||
# SSO with Authentik
|
||||
extraEnvVars:
|
||||
- name: WEBUI_URL
|
||||
value: "https://openwebui.lumpiasty.xyz"
|
||||
- name: OAUTH_CLIENT_ID
|
||||
valueFrom:
|
||||
secretKeyRef:
|
||||
name: openwebui-authentik
|
||||
key: client_id
|
||||
- name: OAUTH_CLIENT_SECRET
|
||||
valueFrom:
|
||||
secretKeyRef:
|
||||
name: openwebui-authentik
|
||||
key: client_secret
|
||||
- name: OAUTH_PROVIDER_NAME
|
||||
value: "authentik"
|
||||
- name: OPENID_PROVIDER_URL
|
||||
value: "https://authentik.lumpiasty.xyz/application/o/open-web-ui/.well-known/openid-configuration"
|
||||
- name: OPENID_REDIRECT_URI
|
||||
value: "https://openwebui.lumpiasty.xyz/oauth/oidc/callback"
|
||||
- name: ENABLE_OAUTH_SIGNUP
|
||||
value: "true"
|
||||
- name: ENABLE_LOGIN_FORM
|
||||
value: "false"
|
||||
- name: OAUTH_MERGE_ACCOUNTS_BY_EMAIL
|
||||
value: "true"
|
||||
|
||||
43
apps/openwebui/secret.yaml
Normal file
43
apps/openwebui/secret.yaml
Normal file
@@ -0,0 +1,43 @@
|
||||
---
|
||||
apiVersion: v1
|
||||
kind: ServiceAccount
|
||||
metadata:
|
||||
name: openwebui-secret
|
||||
namespace: openwebui
|
||||
---
|
||||
apiVersion: secrets.hashicorp.com/v1beta1
|
||||
kind: VaultAuth
|
||||
metadata:
|
||||
name: openwebui
|
||||
namespace: openwebui
|
||||
spec:
|
||||
method: kubernetes
|
||||
mount: kubernetes
|
||||
kubernetes:
|
||||
role: openwebui
|
||||
serviceAccount: openwebui-secret
|
||||
---
|
||||
apiVersion: secrets.hashicorp.com/v1beta1
|
||||
kind: VaultStaticSecret
|
||||
metadata:
|
||||
name: openwebui-authentik
|
||||
namespace: openwebui
|
||||
spec:
|
||||
type: kv-v2
|
||||
|
||||
mount: secret
|
||||
path: authentik/openwebui
|
||||
|
||||
destination:
|
||||
create: true
|
||||
name: openwebui-authentik
|
||||
type: Opaque
|
||||
transformation:
|
||||
excludeRaw: true
|
||||
templates:
|
||||
client_id:
|
||||
text: '{{ get .Secrets "client_id" }}'
|
||||
client_secret:
|
||||
text: '{{ get .Secrets "client_secret" }}'
|
||||
|
||||
vaultAuthRef: openwebui
|
||||
@@ -9,3 +9,4 @@ data:
|
||||
RENOVATE_ENDPOINT: https://gitea.lumpiasty.xyz/api/v1
|
||||
RENOVATE_PLATFORM: gitea
|
||||
RENOVATE_GIT_AUTHOR: Renovate Bot <renovate@lumpiasty.xyz>
|
||||
RENOVATE_ALLOWED_COMMANDS: '["^node utils/update-garm-cli-hash\\.mjs$", "^node utils/update-garm-image-pin\\.mjs$"]'
|
||||
|
||||
@@ -15,7 +15,7 @@ spec:
|
||||
- name: renovate
|
||||
# Update this to the latest available and then enable Renovate on
|
||||
# the manifest
|
||||
image: renovate/renovate:43.64.6-full
|
||||
image: renovate/renovate:43.95.0-full
|
||||
envFrom:
|
||||
- secretRef:
|
||||
name: renovate-gitea-token
|
||||
|
||||
8
apps/woodpecker/kustomization.yaml
Normal file
8
apps/woodpecker/kustomization.yaml
Normal file
@@ -0,0 +1,8 @@
|
||||
apiVersion: kustomize.config.k8s.io/v1beta1
|
||||
kind: Kustomization
|
||||
resources:
|
||||
- namespace.yaml
|
||||
- postgres-volume.yaml
|
||||
- postgres-cluster.yaml
|
||||
- release.yaml
|
||||
- secret.yaml
|
||||
5
apps/woodpecker/namespace.yaml
Normal file
5
apps/woodpecker/namespace.yaml
Normal file
@@ -0,0 +1,5 @@
|
||||
---
|
||||
apiVersion: v1
|
||||
kind: Namespace
|
||||
metadata:
|
||||
name: woodpecker
|
||||
23
apps/woodpecker/postgres-cluster.yaml
Normal file
23
apps/woodpecker/postgres-cluster.yaml
Normal file
@@ -0,0 +1,23 @@
|
||||
---
|
||||
apiVersion: postgresql.cnpg.io/v1
|
||||
kind: Cluster
|
||||
metadata:
|
||||
name: woodpecker-postgresql-cluster
|
||||
namespace: woodpecker
|
||||
spec:
|
||||
instances: 1
|
||||
|
||||
imageName: ghcr.io/cloudnative-pg/postgresql:17.4
|
||||
|
||||
bootstrap:
|
||||
initdb:
|
||||
database: woodpecker
|
||||
owner: woodpecker
|
||||
|
||||
storage:
|
||||
pvcTemplate:
|
||||
storageClassName: ssd-lvmpv
|
||||
resources:
|
||||
requests:
|
||||
storage: 10Gi
|
||||
volumeName: woodpecker-postgresql-cluster-lvmssd
|
||||
33
apps/woodpecker/postgres-volume.yaml
Normal file
33
apps/woodpecker/postgres-volume.yaml
Normal file
@@ -0,0 +1,33 @@
|
||||
apiVersion: local.openebs.io/v1alpha1
|
||||
kind: LVMVolume
|
||||
metadata:
|
||||
labels:
|
||||
kubernetes.io/nodename: anapistula-delrosalae
|
||||
name: woodpecker-postgresql-cluster-lvmssd
|
||||
namespace: openebs
|
||||
spec:
|
||||
capacity: 10Gi
|
||||
ownerNodeID: anapistula-delrosalae
|
||||
shared: "yes"
|
||||
thinProvision: "no"
|
||||
vgPattern: ^openebs-ssd$
|
||||
volGroup: openebs-ssd
|
||||
---
|
||||
kind: PersistentVolume
|
||||
apiVersion: v1
|
||||
metadata:
|
||||
name: woodpecker-postgresql-cluster-lvmssd
|
||||
spec:
|
||||
capacity:
|
||||
storage: 10Gi
|
||||
accessModes:
|
||||
- ReadWriteOnce
|
||||
persistentVolumeReclaimPolicy: Retain
|
||||
storageClassName: ssd-lvmpv
|
||||
volumeMode: Filesystem
|
||||
csi:
|
||||
driver: local.csi.openebs.io
|
||||
fsType: btrfs
|
||||
volumeHandle: woodpecker-postgresql-cluster-lvmssd
|
||||
---
|
||||
# PVC is dynamically created by the Postgres operator
|
||||
115
apps/woodpecker/release.yaml
Normal file
115
apps/woodpecker/release.yaml
Normal file
@@ -0,0 +1,115 @@
|
||||
---
|
||||
apiVersion: source.toolkit.fluxcd.io/v1
|
||||
kind: HelmRepository
|
||||
metadata:
|
||||
name: woodpecker
|
||||
namespace: woodpecker
|
||||
spec:
|
||||
interval: 24h
|
||||
url: https://woodpecker-ci.org/
|
||||
---
|
||||
apiVersion: helm.toolkit.fluxcd.io/v2
|
||||
kind: HelmRelease
|
||||
metadata:
|
||||
name: woodpecker
|
||||
namespace: woodpecker
|
||||
spec:
|
||||
interval: 30m
|
||||
chart:
|
||||
spec:
|
||||
chart: woodpecker
|
||||
version: 3.5.1
|
||||
sourceRef:
|
||||
kind: HelmRepository
|
||||
name: woodpecker
|
||||
namespace: woodpecker
|
||||
interval: 12h
|
||||
values:
|
||||
server:
|
||||
enabled: true
|
||||
statefulSet:
|
||||
replicaCount: 1
|
||||
|
||||
persistentVolume:
|
||||
enabled: false # Using Postgresql database
|
||||
|
||||
env:
|
||||
WOODPECKER_HOST: "https://woodpecker.lumpiasty.xyz"
|
||||
# Gitea integration
|
||||
WOODPECKER_GITEA: "true"
|
||||
WOODPECKER_GITEA_URL: "https://gitea.lumpiasty.xyz"
|
||||
# PostgreSQL database configuration
|
||||
WOODPECKER_DATABASE_DRIVER: postgres
|
||||
# Password is loaded from woodpecker-postgresql-cluster-app secret (created by CNPG)
|
||||
WOODPECKER_DATABASE_DATASOURCE:
|
||||
valueFrom:
|
||||
secretKeyRef:
|
||||
name: woodpecker-postgresql-cluster-app
|
||||
key: fqdn-uri
|
||||
# Allow logging in from all accounts on Gitea
|
||||
WOODPECKER_OPEN: "true"
|
||||
# Make lumpiasty admin
|
||||
WOODPECKER_ADMIN: GiteaAdmin
|
||||
|
||||
createAgentSecret: true
|
||||
|
||||
extraSecretNamesForEnvFrom:
|
||||
- woodpecker-secrets
|
||||
|
||||
ingress:
|
||||
enabled: true
|
||||
ingressClassName: nginx-ingress
|
||||
annotations:
|
||||
cert-manager.io/cluster-issuer: letsencrypt
|
||||
acme.cert-manager.io/http01-edit-in-place: "true"
|
||||
hosts:
|
||||
- host: woodpecker.lumpiasty.xyz
|
||||
paths:
|
||||
- path: /
|
||||
backend:
|
||||
serviceName: woodpecker-server
|
||||
servicePort: 80
|
||||
tls:
|
||||
- hosts:
|
||||
- woodpecker.lumpiasty.xyz
|
||||
secretName: woodpecker-ingress
|
||||
|
||||
resources:
|
||||
requests:
|
||||
cpu: 100m
|
||||
memory: 256Mi
|
||||
|
||||
service:
|
||||
type: ClusterIP
|
||||
port: 80
|
||||
|
||||
agent:
|
||||
enabled: true
|
||||
replicaCount: 2
|
||||
|
||||
env:
|
||||
WOODPECKER_SERVER: "woodpecker-server:9000"
|
||||
WOODPECKER_BACKEND: kubernetes
|
||||
WOODPECKER_BACKEND_K8S_NAMESPACE: woodpecker
|
||||
WOODPECKER_BACKEND_K8S_STORAGE_CLASS: ssd-lvmpv
|
||||
WOODPECKER_BACKEND_K8S_VOLUME_SIZE: 10G
|
||||
WOODPECKER_BACKEND_K8S_STORAGE_RWX: false
|
||||
WOODPECKER_CONNECT_RETRY_COUNT: "5"
|
||||
|
||||
mapAgentSecret: true
|
||||
|
||||
extraSecretNamesForEnvFrom:
|
||||
- woodpecker-secrets
|
||||
|
||||
persistence:
|
||||
enabled: false
|
||||
|
||||
serviceAccount:
|
||||
create: true
|
||||
rbac:
|
||||
create: true
|
||||
|
||||
resources:
|
||||
requests:
|
||||
cpu: 100m
|
||||
memory: 128Mi
|
||||
62
apps/woodpecker/secret.yaml
Normal file
62
apps/woodpecker/secret.yaml
Normal file
@@ -0,0 +1,62 @@
|
||||
---
|
||||
apiVersion: v1
|
||||
kind: ServiceAccount
|
||||
metadata:
|
||||
name: woodpecker-secret
|
||||
namespace: woodpecker
|
||||
---
|
||||
apiVersion: secrets.hashicorp.com/v1beta1
|
||||
kind: VaultAuth
|
||||
metadata:
|
||||
name: woodpecker
|
||||
namespace: woodpecker
|
||||
spec:
|
||||
method: kubernetes
|
||||
mount: kubernetes
|
||||
kubernetes:
|
||||
role: woodpecker
|
||||
serviceAccount: woodpecker-secret
|
||||
---
|
||||
# Main woodpecker secrets from Vault
|
||||
# Requires vault kv put secret/woodpecker \
|
||||
# WOODPECKER_AGENT_SECRET="$(openssl rand -hex 32)" \
|
||||
# WOODPECKER_GITEA_CLIENT="<gitea-oauth-client>" \
|
||||
# WOODPECKER_GITEA_SECRET="<gitea-oauth-secret>"
|
||||
# Note: Database password comes from CNPG secret (woodpecker-postgresql-cluster-app)
|
||||
apiVersion: secrets.hashicorp.com/v1beta1
|
||||
kind: VaultStaticSecret
|
||||
metadata:
|
||||
name: woodpecker-secrets
|
||||
namespace: woodpecker
|
||||
spec:
|
||||
type: kv-v2
|
||||
mount: secret
|
||||
path: woodpecker
|
||||
destination:
|
||||
create: true
|
||||
name: woodpecker-secrets
|
||||
type: Opaque
|
||||
transformation:
|
||||
excludeRaw: true
|
||||
vaultAuthRef: woodpecker
|
||||
---
|
||||
# Container registry credentials for Kaniko
|
||||
# Requires vault kv put secret/container-registry \
|
||||
# REGISTRY_USERNAME="<username>" \
|
||||
# REGISTRY_PASSWORD="<token>"
|
||||
apiVersion: secrets.hashicorp.com/v1beta1
|
||||
kind: VaultStaticSecret
|
||||
metadata:
|
||||
name: container-registry
|
||||
namespace: woodpecker
|
||||
spec:
|
||||
type: kv-v2
|
||||
mount: secret
|
||||
path: container-registry
|
||||
destination:
|
||||
create: true
|
||||
name: container-registry
|
||||
type: Opaque
|
||||
transformation:
|
||||
excludeRaw: true
|
||||
vaultAuthRef: woodpecker
|
||||
87
devenv.lock
87
devenv.lock
@@ -3,10 +3,11 @@
|
||||
"devenv": {
|
||||
"locked": {
|
||||
"dir": "src/modules",
|
||||
"lastModified": 1769881431,
|
||||
"lastModified": 1773504385,
|
||||
"narHash": "sha256-ANaeR+xVHxjGz36VI4qlZUbdhrlSE0xU7O7AUJKw3zU=",
|
||||
"owner": "cachix",
|
||||
"repo": "devenv",
|
||||
"rev": "72d5e66e2dd5112766ef4c9565872b51094b542d",
|
||||
"rev": "4bce49e6f60c69e99eeb643efbbf74125cefd329",
|
||||
"type": "github"
|
||||
},
|
||||
"original": {
|
||||
@@ -16,27 +17,13 @@
|
||||
"type": "github"
|
||||
}
|
||||
},
|
||||
"flake-compat": {
|
||||
"flake": false,
|
||||
"locked": {
|
||||
"lastModified": 1767039857,
|
||||
"owner": "NixOS",
|
||||
"repo": "flake-compat",
|
||||
"rev": "5edf11c44bc78a0d334f6334cdaf7d60d732daab",
|
||||
"type": "github"
|
||||
},
|
||||
"original": {
|
||||
"owner": "NixOS",
|
||||
"repo": "flake-compat",
|
||||
"type": "github"
|
||||
}
|
||||
},
|
||||
"flake-utils": {
|
||||
"inputs": {
|
||||
"systems": "systems"
|
||||
},
|
||||
"locked": {
|
||||
"lastModified": 1731533236,
|
||||
"narHash": "sha256-l0KFg5HjrsfsO/JpG+r7fRrqm12kzFHyUHqHCVpMMbI=",
|
||||
"owner": "numtide",
|
||||
"repo": "flake-utils",
|
||||
"rev": "11707dc2f618dd54ca8739b309ec4fc024de578b",
|
||||
@@ -48,47 +35,6 @@
|
||||
"type": "github"
|
||||
}
|
||||
},
|
||||
"git-hooks": {
|
||||
"inputs": {
|
||||
"flake-compat": "flake-compat",
|
||||
"gitignore": "gitignore",
|
||||
"nixpkgs": [
|
||||
"nixpkgs"
|
||||
]
|
||||
},
|
||||
"locked": {
|
||||
"lastModified": 1769069492,
|
||||
"owner": "cachix",
|
||||
"repo": "git-hooks.nix",
|
||||
"rev": "a1ef738813b15cf8ec759bdff5761b027e3e1d23",
|
||||
"type": "github"
|
||||
},
|
||||
"original": {
|
||||
"owner": "cachix",
|
||||
"repo": "git-hooks.nix",
|
||||
"type": "github"
|
||||
}
|
||||
},
|
||||
"gitignore": {
|
||||
"inputs": {
|
||||
"nixpkgs": [
|
||||
"git-hooks",
|
||||
"nixpkgs"
|
||||
]
|
||||
},
|
||||
"locked": {
|
||||
"lastModified": 1762808025,
|
||||
"owner": "hercules-ci",
|
||||
"repo": "gitignore.nix",
|
||||
"rev": "cb5e3fdca1de58ccbc3ef53de65bd372b48f567c",
|
||||
"type": "github"
|
||||
},
|
||||
"original": {
|
||||
"owner": "hercules-ci",
|
||||
"repo": "gitignore.nix",
|
||||
"type": "github"
|
||||
}
|
||||
},
|
||||
"krew2nix": {
|
||||
"inputs": {
|
||||
"flake-utils": "flake-utils",
|
||||
@@ -99,10 +45,11 @@
|
||||
"treefmt-nix": "treefmt-nix"
|
||||
},
|
||||
"locked": {
|
||||
"lastModified": 1769904483,
|
||||
"lastModified": 1773451905,
|
||||
"narHash": "sha256-S/bukFEwbOYQbnR5UpciwYA42aEt1w5LK73GwARhsaA=",
|
||||
"owner": "a1994sc",
|
||||
"repo": "krew2nix",
|
||||
"rev": "17d6ad3375899bd3f7d4d298481536155f3ec13c",
|
||||
"rev": "bc779a8cf59ebf76ae60556bfe2d781a0a4cdbd9",
|
||||
"type": "github"
|
||||
},
|
||||
"original": {
|
||||
@@ -113,10 +60,11 @@
|
||||
},
|
||||
"nixpkgs": {
|
||||
"locked": {
|
||||
"lastModified": 1769461804,
|
||||
"lastModified": 1773389992,
|
||||
"narHash": "sha256-wvfdLLWJ2I9oEpDd9PfMA8osfIZicoQ5MT1jIwNs9Tk=",
|
||||
"owner": "NixOS",
|
||||
"repo": "nixpkgs",
|
||||
"rev": "bfc1b8a4574108ceef22f02bafcf6611380c100d",
|
||||
"rev": "c06b4ae3d6599a672a6210b7021d699c351eebda",
|
||||
"type": "github"
|
||||
},
|
||||
"original": {
|
||||
@@ -129,17 +77,14 @@
|
||||
"root": {
|
||||
"inputs": {
|
||||
"devenv": "devenv",
|
||||
"git-hooks": "git-hooks",
|
||||
"krew2nix": "krew2nix",
|
||||
"nixpkgs": "nixpkgs",
|
||||
"pre-commit-hooks": [
|
||||
"git-hooks"
|
||||
]
|
||||
"nixpkgs": "nixpkgs"
|
||||
}
|
||||
},
|
||||
"systems": {
|
||||
"locked": {
|
||||
"lastModified": 1681028828,
|
||||
"narHash": "sha256-Vy1rq5AaRuLzOxct8nz4T6wlgyUR7zLU309k9mBC768=",
|
||||
"owner": "nix-systems",
|
||||
"repo": "default",
|
||||
"rev": "da67096a3b9bf56a91d16901293e51ba5b49a27e",
|
||||
@@ -154,6 +99,7 @@
|
||||
"systems_2": {
|
||||
"locked": {
|
||||
"lastModified": 1681028828,
|
||||
"narHash": "sha256-Vy1rq5AaRuLzOxct8nz4T6wlgyUR7zLU309k9mBC768=",
|
||||
"owner": "nix-systems",
|
||||
"repo": "default",
|
||||
"rev": "da67096a3b9bf56a91d16901293e51ba5b49a27e",
|
||||
@@ -173,10 +119,11 @@
|
||||
]
|
||||
},
|
||||
"locked": {
|
||||
"lastModified": 1769691507,
|
||||
"lastModified": 1773297127,
|
||||
"narHash": "sha256-6E/yhXP7Oy/NbXtf1ktzmU8SdVqJQ09HC/48ebEGBpk=",
|
||||
"owner": "numtide",
|
||||
"repo": "treefmt-nix",
|
||||
"rev": "28b19c5844cc6e2257801d43f2772a4b4c050a1b",
|
||||
"rev": "71b125cd05fbfd78cab3e070b73544abe24c5016",
|
||||
"type": "github"
|
||||
},
|
||||
"original": {
|
||||
@@ -188,4 +135,4 @@
|
||||
},
|
||||
"root": "root",
|
||||
"version": 7
|
||||
}
|
||||
}
|
||||
@@ -6,6 +6,8 @@ let
|
||||
hvac
|
||||
librouteros
|
||||
]);
|
||||
|
||||
garm-cli = pkgs.callPackage ./nix/garm-cli.nix { };
|
||||
in
|
||||
{
|
||||
# Overlays - apply krew2nix to get kubectl with krew support
|
||||
@@ -41,6 +43,9 @@ in
|
||||
openbao
|
||||
pv-migrate
|
||||
mermaid-cli
|
||||
opencode
|
||||
garm-cli
|
||||
tea
|
||||
];
|
||||
|
||||
# Scripts
|
||||
|
||||
28
docker/garm/Dockerfile
Normal file
28
docker/garm/Dockerfile
Normal file
@@ -0,0 +1,28 @@
|
||||
FROM golang:1.26-alpine AS build
|
||||
|
||||
ARG GARM_COMMIT
|
||||
ARG GARM_PROVIDER_K8S_VERSION=0.3.2
|
||||
|
||||
RUN apk add --no-cache ca-certificates git wget tar build-base util-linux-dev linux-headers
|
||||
|
||||
WORKDIR /src
|
||||
RUN git clone https://github.com/cloudbase/garm.git . && git checkout "${GARM_COMMIT}"
|
||||
|
||||
RUN CGO_ENABLED=1 GOOS=linux GOARCH=amd64 \
|
||||
go build -trimpath \
|
||||
-tags osusergo,netgo,sqlite_omit_load_extension \
|
||||
-ldflags="-linkmode external -extldflags '-static' -s -w" \
|
||||
-o /out/garm ./cmd/garm
|
||||
|
||||
RUN mkdir -p /out/providers.d \
|
||||
&& wget -qO /tmp/garm-provider-k8s.tar.gz "https://github.com/mercedes-benz/garm-provider-k8s/releases/download/v${GARM_PROVIDER_K8S_VERSION}/garm-provider-k8s_Linux_x86_64.tar.gz" \
|
||||
&& tar -xzf /tmp/garm-provider-k8s.tar.gz -C /out/providers.d \
|
||||
&& chmod 0755 /out/providers.d/garm-provider-k8s
|
||||
|
||||
FROM busybox
|
||||
|
||||
COPY --from=build /out/garm /bin/garm
|
||||
COPY --from=build /out/providers.d/garm-provider-k8s /opt/garm/providers.d/garm-provider-k8s
|
||||
COPY --from=build /etc/ssl/certs/ca-certificates.crt /etc/ssl/certs/
|
||||
|
||||
ENTRYPOINT ["/bin/garm"]
|
||||
@@ -18,7 +18,7 @@ spec:
|
||||
chart:
|
||||
spec:
|
||||
chart: cert-manager-webhook-ovh
|
||||
version: 0.9.4
|
||||
version: 0.9.5
|
||||
sourceRef:
|
||||
kind: HelmRepository
|
||||
name: cert-manager-webhook-ovh
|
||||
|
||||
@@ -23,7 +23,7 @@ spec:
|
||||
chart:
|
||||
spec:
|
||||
chart: cert-manager
|
||||
version: v1.20.0
|
||||
version: v1.20.1
|
||||
sourceRef:
|
||||
kind: HelmRepository
|
||||
name: cert-manager
|
||||
|
||||
@@ -23,7 +23,7 @@ spec:
|
||||
chart:
|
||||
spec:
|
||||
chart: cilium
|
||||
version: 1.19.1
|
||||
version: 1.19.2
|
||||
sourceRef:
|
||||
kind: HelmRepository
|
||||
name: cilium
|
||||
|
||||
@@ -23,7 +23,7 @@ spec:
|
||||
chart:
|
||||
spec:
|
||||
chart: k8up
|
||||
version: 4.8.6
|
||||
version: 4.9.0
|
||||
sourceRef:
|
||||
kind: HelmRepository
|
||||
name: k8up-io
|
||||
|
||||
@@ -23,7 +23,7 @@ spec:
|
||||
chart:
|
||||
spec:
|
||||
chart: ingress-nginx
|
||||
version: 4.15.0
|
||||
version: 4.15.1
|
||||
sourceRef:
|
||||
kind: HelmRepository
|
||||
name: ingress-nginx
|
||||
|
||||
@@ -23,7 +23,7 @@ spec:
|
||||
chart:
|
||||
spec:
|
||||
chart: openbao
|
||||
version: 0.25.7
|
||||
version: 0.26.2
|
||||
sourceRef:
|
||||
kind: HelmRepository
|
||||
name: openbao
|
||||
|
||||
45
nix/garm-cli.nix
Normal file
45
nix/garm-cli.nix
Normal file
@@ -0,0 +1,45 @@
|
||||
{ lib, buildGoModule, fetchFromGitHub, installShellFiles }:
|
||||
|
||||
buildGoModule rec {
|
||||
pname = "garm-cli";
|
||||
version = "r1380";
|
||||
garmCommit = "818a9dddccba5f2843f185e6a846770988f31fc5";
|
||||
|
||||
src = fetchFromGitHub {
|
||||
owner = "cloudbase";
|
||||
repo = "garm";
|
||||
rev = garmCommit;
|
||||
hash = "sha256-CTqqabNYUMSrmnQVCWml1/vkDw+OP1uJo1KFhBSZpYY=";
|
||||
};
|
||||
|
||||
subPackages = [ "cmd/garm-cli" ];
|
||||
|
||||
nativeBuildInputs = [ installShellFiles ];
|
||||
|
||||
vendorHash = null;
|
||||
|
||||
ldflags = [
|
||||
"-s"
|
||||
"-w"
|
||||
"-X main.version=${version}"
|
||||
];
|
||||
|
||||
postInstall = ''
|
||||
# We need to set a temporary HOME for the completion scripts as workaround
|
||||
# because garm-cli tries to write config to the home directory
|
||||
# when generating the completion scripts
|
||||
export HOME="$(mktemp -d)"
|
||||
|
||||
installShellCompletion --cmd garm-cli \
|
||||
--bash <($out/bin/garm-cli completion bash) \
|
||||
--fish <($out/bin/garm-cli completion fish) \
|
||||
--zsh <($out/bin/garm-cli completion zsh)
|
||||
'';
|
||||
|
||||
meta = {
|
||||
description = "CLI for GitHub Actions Runner Manager";
|
||||
homepage = "https://github.com/cloudbase/garm";
|
||||
license = lib.licenses.asl20;
|
||||
mainProgram = "garm-cli";
|
||||
};
|
||||
}
|
||||
@@ -10,8 +10,57 @@
|
||||
"gotk-components\\.ya?ml$"
|
||||
]
|
||||
},
|
||||
"customManagers": [
|
||||
{
|
||||
"customType": "regex",
|
||||
"description": "Track garm-cli pinned main commit",
|
||||
"managerFilePatterns": ["^nix/garm-cli\\.nix$"],
|
||||
"matchStrings": ["garmCommit = \\\"(?<currentValue>[a-f0-9]{40})\\\";"],
|
||||
"depNameTemplate": "cloudbase/garm",
|
||||
"datasourceTemplate": "github-refs",
|
||||
"versioningTemplate": "git"
|
||||
},
|
||||
{
|
||||
"customType": "regex",
|
||||
"description": "Track garm-provider-k8s release in garm image Dockerfile",
|
||||
"managerFilePatterns": ["^docker/garm/Dockerfile$"],
|
||||
"matchStrings": ["ARG GARM_PROVIDER_K8S_VERSION=(?<currentValue>[0-9]+\\.[0-9]+\\.[0-9]+)"],
|
||||
"depNameTemplate": "mercedes-benz/garm-provider-k8s",
|
||||
"datasourceTemplate": "github-releases",
|
||||
"versioningTemplate": "semver"
|
||||
},
|
||||
{
|
||||
"customType": "regex",
|
||||
"description": "Track pinned garm main commit",
|
||||
"managerFilePatterns": ["^apps/garm/image-source\\.env$"],
|
||||
"matchStrings": ["GARM_COMMIT=(?<currentValue>[a-f0-9]{40})"],
|
||||
"depNameTemplate": "cloudbase/garm",
|
||||
"datasourceTemplate": "github-refs",
|
||||
"versioningTemplate": "git"
|
||||
}
|
||||
],
|
||||
"prHourlyLimit": 9,
|
||||
"packageRules": [
|
||||
{
|
||||
"matchManagers": ["custom.regex"],
|
||||
"matchDepNames": ["cloudbase/garm"],
|
||||
"matchFileNames": ["nix/garm-cli.nix"],
|
||||
"postUpgradeTasks": {
|
||||
"commands": ["node utils/update-garm-cli-hash.mjs"],
|
||||
"fileFilters": ["nix/garm-cli.nix"],
|
||||
"executionMode": "update"
|
||||
}
|
||||
},
|
||||
{
|
||||
"matchManagers": ["custom.regex"],
|
||||
"matchDepNames": ["cloudbase/garm"],
|
||||
"matchFileNames": ["apps/garm/image-source.env"],
|
||||
"postUpgradeTasks": {
|
||||
"commands": ["node utils/update-garm-image-pin.mjs"],
|
||||
"fileFilters": ["apps/garm/image-source.env", "apps/garm/deployment.yaml"],
|
||||
"executionMode": "update"
|
||||
}
|
||||
},
|
||||
{
|
||||
"matchDatasources": ["docker"],
|
||||
"matchPackageNames": ["ghcr.io/mostlygeek/llama-swap"],
|
||||
|
||||
320
utils/update-garm-cli-hash.mjs
Normal file
320
utils/update-garm-cli-hash.mjs
Normal file
@@ -0,0 +1,320 @@
|
||||
import { createHash } from "node:crypto";
|
||||
import { Buffer } from "node:buffer";
|
||||
import fs from "node:fs";
|
||||
import https from "node:https";
|
||||
import zlib from "node:zlib";
|
||||
|
||||
const nixFile = "nix/garm-cli.nix";
|
||||
|
||||
function die(message) {
|
||||
console.error(message);
|
||||
process.exit(1);
|
||||
}
|
||||
|
||||
function readText(filePath) {
|
||||
try {
|
||||
return fs.readFileSync(filePath, "utf8");
|
||||
} catch {
|
||||
die(`Missing ${filePath}`);
|
||||
}
|
||||
}
|
||||
|
||||
function extractVersion(text) {
|
||||
const match = text.match(/^\s*version\s*=\s*"([^"]+)";/m);
|
||||
if (!match) {
|
||||
die(`Unable to extract version from ${nixFile}`);
|
||||
}
|
||||
return match[1];
|
||||
}
|
||||
|
||||
function extractCommit(text) {
|
||||
const match = text.match(/^\s*garmCommit\s*=\s*"([a-f0-9]{40})";/m);
|
||||
return match ? match[1] : null;
|
||||
}
|
||||
|
||||
function writeU64LE(hash, value) {
|
||||
const buf = Buffer.alloc(8);
|
||||
buf.writeBigUInt64LE(BigInt(value), 0);
|
||||
hash.update(buf);
|
||||
}
|
||||
|
||||
function writeNarString(hash, data) {
|
||||
writeU64LE(hash, data.length);
|
||||
hash.update(data);
|
||||
const pad = (8 - (data.length % 8)) % 8;
|
||||
if (pad) {
|
||||
hash.update(Buffer.alloc(pad));
|
||||
}
|
||||
}
|
||||
|
||||
function writeNarText(hash, text) {
|
||||
writeNarString(hash, Buffer.from(text, "utf8"));
|
||||
}
|
||||
|
||||
function parseOctal(field) {
|
||||
const clean = field.toString("ascii").replace(/\0.*$/, "").trim();
|
||||
if (!clean) {
|
||||
return 0;
|
||||
}
|
||||
return Number.parseInt(clean, 8);
|
||||
}
|
||||
|
||||
function parseTarHeader(block) {
|
||||
const name = block.subarray(0, 100).toString("utf8").replace(/\0.*$/, "");
|
||||
const mode = parseOctal(block.subarray(100, 108));
|
||||
const size = parseOctal(block.subarray(124, 136));
|
||||
const typeflagRaw = block[156];
|
||||
const typeflag = typeflagRaw === 0 ? "0" : String.fromCharCode(typeflagRaw);
|
||||
const linkname = block.subarray(157, 257).toString("utf8").replace(/\0.*$/, "");
|
||||
const prefix = block.subarray(345, 500).toString("utf8").replace(/\0.*$/, "");
|
||||
return {
|
||||
name: prefix ? `${prefix}/${name}` : name,
|
||||
mode,
|
||||
size,
|
||||
typeflag,
|
||||
linkname,
|
||||
};
|
||||
}
|
||||
|
||||
function parsePax(data) {
|
||||
const out = {};
|
||||
let i = 0;
|
||||
while (i < data.length) {
|
||||
let sp = i;
|
||||
while (sp < data.length && data[sp] !== 0x20) sp += 1;
|
||||
if (sp >= data.length) break;
|
||||
const len = Number.parseInt(data.subarray(i, sp).toString("utf8"), 10);
|
||||
if (!Number.isFinite(len) || len <= 0) break;
|
||||
const record = data.subarray(sp + 1, i + len).toString("utf8");
|
||||
const eq = record.indexOf("=");
|
||||
if (eq > 0) {
|
||||
const key = record.slice(0, eq);
|
||||
const value = record.slice(eq + 1).replace(/\n$/, "");
|
||||
out[key] = value;
|
||||
}
|
||||
i += len;
|
||||
}
|
||||
return out;
|
||||
}
|
||||
|
||||
function parseTarEntries(archiveBuffer) {
|
||||
const gz = zlib.gunzipSync(archiveBuffer);
|
||||
const entries = [];
|
||||
let i = 0;
|
||||
let pendingPax = null;
|
||||
let longName = null;
|
||||
let longLink = null;
|
||||
|
||||
while (i + 512 <= gz.length) {
|
||||
const header = gz.subarray(i, i + 512);
|
||||
i += 512;
|
||||
|
||||
if (header.every((b) => b === 0)) {
|
||||
break;
|
||||
}
|
||||
|
||||
const h = parseTarHeader(header);
|
||||
const data = gz.subarray(i, i + h.size);
|
||||
const dataPad = (512 - (h.size % 512)) % 512;
|
||||
i += h.size + dataPad;
|
||||
|
||||
if (h.typeflag === "x") {
|
||||
pendingPax = parsePax(data);
|
||||
continue;
|
||||
}
|
||||
if (h.typeflag === "g") {
|
||||
continue;
|
||||
}
|
||||
if (h.typeflag === "L") {
|
||||
longName = data.toString("utf8").replace(/\0.*$/, "");
|
||||
continue;
|
||||
}
|
||||
if (h.typeflag === "K") {
|
||||
longLink = data.toString("utf8").replace(/\0.*$/, "");
|
||||
continue;
|
||||
}
|
||||
|
||||
const path = pendingPax?.path ?? longName ?? h.name;
|
||||
const linkpath = pendingPax?.linkpath ?? longLink ?? h.linkname;
|
||||
|
||||
entries.push({
|
||||
path,
|
||||
typeflag: h.typeflag,
|
||||
mode: h.mode,
|
||||
linkname: linkpath,
|
||||
data,
|
||||
});
|
||||
|
||||
pendingPax = null;
|
||||
longName = null;
|
||||
longLink = null;
|
||||
}
|
||||
|
||||
return entries;
|
||||
}
|
||||
|
||||
function stripTopDir(path) {
|
||||
const cleaned = path.replace(/^\.?\//, "").replace(/\/$/, "");
|
||||
const idx = cleaned.indexOf("/");
|
||||
if (idx === -1) return "";
|
||||
return cleaned.slice(idx + 1);
|
||||
}
|
||||
|
||||
function ensureDir(root, relPath) {
|
||||
if (!relPath) return root;
|
||||
const parts = relPath.split("/").filter(Boolean);
|
||||
let cur = root;
|
||||
for (const part of parts) {
|
||||
let child = cur.children.get(part);
|
||||
if (!child) {
|
||||
child = { kind: "directory", children: new Map() };
|
||||
cur.children.set(part, child);
|
||||
}
|
||||
if (child.kind !== "directory") {
|
||||
die(`Path conflict while building tree at ${relPath}`);
|
||||
}
|
||||
cur = child;
|
||||
}
|
||||
return cur;
|
||||
}
|
||||
|
||||
function buildTree(entries) {
|
||||
const root = { kind: "directory", children: new Map() };
|
||||
for (const entry of entries) {
|
||||
const rel = stripTopDir(entry.path);
|
||||
if (!rel) {
|
||||
continue;
|
||||
}
|
||||
|
||||
const parts = rel.split("/").filter(Boolean);
|
||||
const name = parts.pop();
|
||||
const parent = ensureDir(root, parts.join("/"));
|
||||
|
||||
if (entry.typeflag === "5") {
|
||||
const existing = parent.children.get(name);
|
||||
if (!existing) {
|
||||
parent.children.set(name, { kind: "directory", children: new Map() });
|
||||
} else if (existing.kind !== "directory") {
|
||||
die(`Path conflict at ${rel}`);
|
||||
}
|
||||
continue;
|
||||
}
|
||||
|
||||
if (entry.typeflag === "2") {
|
||||
parent.children.set(name, { kind: "symlink", target: entry.linkname });
|
||||
continue;
|
||||
}
|
||||
|
||||
if (entry.typeflag === "0") {
|
||||
parent.children.set(name, {
|
||||
kind: "regular",
|
||||
executable: (entry.mode & 0o111) !== 0,
|
||||
contents: Buffer.from(entry.data),
|
||||
});
|
||||
continue;
|
||||
}
|
||||
}
|
||||
return root;
|
||||
}
|
||||
|
||||
function compareUtf8(a, b) {
|
||||
return Buffer.from(a, "utf8").compare(Buffer.from(b, "utf8"));
|
||||
}
|
||||
|
||||
function narDump(hash, node) {
|
||||
if (node.kind === "directory") {
|
||||
writeNarText(hash, "(");
|
||||
writeNarText(hash, "type");
|
||||
writeNarText(hash, "directory");
|
||||
const names = [...node.children.keys()].sort(compareUtf8);
|
||||
for (const name of names) {
|
||||
writeNarText(hash, "entry");
|
||||
writeNarText(hash, "(");
|
||||
writeNarText(hash, "name");
|
||||
writeNarString(hash, Buffer.from(name, "utf8"));
|
||||
writeNarText(hash, "node");
|
||||
narDump(hash, node.children.get(name));
|
||||
writeNarText(hash, ")");
|
||||
}
|
||||
writeNarText(hash, ")");
|
||||
return;
|
||||
}
|
||||
|
||||
if (node.kind === "symlink") {
|
||||
writeNarText(hash, "(");
|
||||
writeNarText(hash, "type");
|
||||
writeNarText(hash, "symlink");
|
||||
writeNarText(hash, "target");
|
||||
writeNarString(hash, Buffer.from(node.target, "utf8"));
|
||||
writeNarText(hash, ")");
|
||||
return;
|
||||
}
|
||||
|
||||
writeNarText(hash, "(");
|
||||
writeNarText(hash, "type");
|
||||
writeNarText(hash, "regular");
|
||||
if (node.executable) {
|
||||
writeNarText(hash, "executable");
|
||||
writeNarText(hash, "");
|
||||
}
|
||||
writeNarText(hash, "contents");
|
||||
writeNarString(hash, node.contents);
|
||||
writeNarText(hash, ")");
|
||||
}
|
||||
|
||||
function fetchBuffer(url) {
|
||||
return new Promise((resolve, reject) => {
|
||||
https
|
||||
.get(url, (res) => {
|
||||
if (res.statusCode && res.statusCode >= 300 && res.statusCode < 400 && res.headers.location) {
|
||||
const redirectUrl = new URL(res.headers.location, url).toString();
|
||||
res.resume();
|
||||
fetchBuffer(redirectUrl).then(resolve, reject);
|
||||
return;
|
||||
}
|
||||
if (!res.statusCode || res.statusCode < 200 || res.statusCode >= 300) {
|
||||
reject(new Error(`Failed to fetch ${url}: ${res.statusCode ?? "unknown"}`));
|
||||
res.resume();
|
||||
return;
|
||||
}
|
||||
const chunks = [];
|
||||
res.on("data", (chunk) => chunks.push(chunk));
|
||||
res.on("end", () => resolve(Buffer.concat(chunks)));
|
||||
})
|
||||
.on("error", reject);
|
||||
});
|
||||
}
|
||||
|
||||
function computeSRIFromGitHubTar(ref) {
|
||||
const url = `https://github.com/cloudbase/garm/archive/${ref}.tar.gz`;
|
||||
return fetchBuffer(url).then((archive) => {
|
||||
const entries = parseTarEntries(archive);
|
||||
const root = buildTree(entries);
|
||||
const hash = createHash("sha256");
|
||||
writeNarText(hash, "nix-archive-1");
|
||||
narDump(hash, root);
|
||||
return `sha256-${hash.digest("base64")}`;
|
||||
});
|
||||
}
|
||||
|
||||
function updateHash(text, sri) {
|
||||
const pattern = /(^\s*hash\s*=\s*")sha256-[^"]+(";)/m;
|
||||
if (!pattern.test(text)) {
|
||||
die(`Unable to update hash in ${nixFile}`);
|
||||
}
|
||||
const next = text.replace(pattern, `$1${sri}$2`);
|
||||
return next;
|
||||
}
|
||||
|
||||
async function main() {
|
||||
const text = readText(nixFile);
|
||||
const version = extractVersion(text);
|
||||
const commit = extractCommit(text);
|
||||
const ref = commit ?? `v${version}`;
|
||||
const sri = await computeSRIFromGitHubTar(ref);
|
||||
const updated = updateHash(text, sri);
|
||||
fs.writeFileSync(nixFile, updated, "utf8");
|
||||
console.log(`Updated ${nixFile} hash to ${sri}`);
|
||||
}
|
||||
|
||||
main().catch((err) => die(err.message));
|
||||
91
utils/update-garm-image-pin.mjs
Normal file
91
utils/update-garm-image-pin.mjs
Normal file
@@ -0,0 +1,91 @@
|
||||
import fs from "node:fs";
|
||||
import os from "node:os";
|
||||
import path from "node:path";
|
||||
import { execFileSync } from "node:child_process";
|
||||
|
||||
const pinFile = "apps/garm/image-source.env";
|
||||
const deploymentFile = "apps/garm/deployment.yaml";
|
||||
|
||||
function fail(message) {
|
||||
console.error(message);
|
||||
process.exit(1);
|
||||
}
|
||||
|
||||
function parseEnvFile(content) {
|
||||
const vars = {};
|
||||
for (const line of content.split(/\r?\n/)) {
|
||||
if (!line || line.startsWith("#")) {
|
||||
continue;
|
||||
}
|
||||
const idx = line.indexOf("=");
|
||||
if (idx === -1) {
|
||||
continue;
|
||||
}
|
||||
const key = line.slice(0, idx).trim();
|
||||
const value = line.slice(idx + 1).trim();
|
||||
vars[key] = value;
|
||||
}
|
||||
return vars;
|
||||
}
|
||||
|
||||
function updateOrAdd(content, key, value) {
|
||||
const pattern = new RegExp(`^${key}=.*$`, "m");
|
||||
if (pattern.test(content)) {
|
||||
return content.replace(pattern, `${key}=${value}`);
|
||||
}
|
||||
return `${content.trimEnd()}\n${key}=${value}\n`;
|
||||
}
|
||||
|
||||
function gitOut(args, options = {}) {
|
||||
return execFileSync("git", args, {
|
||||
encoding: "utf8",
|
||||
...options,
|
||||
}).trim();
|
||||
}
|
||||
|
||||
function gitRun(args, options = {}) {
|
||||
execFileSync("git", args, options);
|
||||
}
|
||||
|
||||
const pinContent = fs.readFileSync(pinFile, "utf8");
|
||||
const vars = parseEnvFile(pinContent);
|
||||
const commit = vars.GARM_COMMIT;
|
||||
const imageRepo = vars.GARM_IMAGE_REPO || "gitea.lumpiasty.xyz/lumpiasty/garm-k8s";
|
||||
|
||||
if (!commit || !/^[0-9a-f]{40}$/.test(commit)) {
|
||||
fail(`Invalid or missing GARM_COMMIT in ${pinFile}`);
|
||||
}
|
||||
|
||||
const tmpDir = fs.mkdtempSync(path.join(os.tmpdir(), "garm-main-"));
|
||||
let commitNumber;
|
||||
try {
|
||||
gitRun(["clone", "--filter=blob:none", "https://github.com/cloudbase/garm.git", tmpDir], {
|
||||
stdio: "ignore",
|
||||
});
|
||||
commitNumber = gitOut(["-C", tmpDir, "rev-list", "--count", commit]);
|
||||
} finally {
|
||||
fs.rmSync(tmpDir, { recursive: true, force: true });
|
||||
}
|
||||
|
||||
if (!/^\d+$/.test(commitNumber)) {
|
||||
fail(`Unable to resolve commit number for ${commit}`);
|
||||
}
|
||||
|
||||
const image = `${imageRepo}:r${commitNumber}`;
|
||||
|
||||
let nextPin = pinContent;
|
||||
nextPin = updateOrAdd(nextPin, "GARM_COMMIT_NUMBER", commitNumber);
|
||||
nextPin = updateOrAdd(nextPin, "GARM_IMAGE_REPO", imageRepo);
|
||||
nextPin = updateOrAdd(nextPin, "GARM_IMAGE", image);
|
||||
fs.writeFileSync(pinFile, nextPin, "utf8");
|
||||
|
||||
const deployment = fs.readFileSync(deploymentFile, "utf8");
|
||||
const imagePattern = /image:\s*(?:ghcr\.io\/cloudbase\/garm:[^\s]+|gitea\.lumpiasty\.xyz\/(?:Lumpiasty|lumpiasty)\/garm(?:-k8s)?:[^\s]+)/;
|
||||
if (!imagePattern.test(deployment)) {
|
||||
fail(`Unable to update garm image in ${deploymentFile}`);
|
||||
}
|
||||
|
||||
const updatedDeployment = deployment.replace(imagePattern, `image: ${image}`);
|
||||
|
||||
fs.writeFileSync(deploymentFile, updatedDeployment, "utf8");
|
||||
console.log(`Pinned garm image to ${image}`);
|
||||
6
vault/kubernetes-roles/authentik.yaml
Normal file
6
vault/kubernetes-roles/authentik.yaml
Normal file
@@ -0,0 +1,6 @@
|
||||
bound_service_account_names:
|
||||
- authentik-secret
|
||||
bound_service_account_namespaces:
|
||||
- authentik
|
||||
token_policies:
|
||||
- authentik
|
||||
6
vault/kubernetes-roles/crawl4ai.yaml
Normal file
6
vault/kubernetes-roles/crawl4ai.yaml
Normal file
@@ -0,0 +1,6 @@
|
||||
bound_service_account_names:
|
||||
- crawl4ai-secret
|
||||
bound_service_account_namespaces:
|
||||
- crawl4ai
|
||||
token_policies:
|
||||
- crawl4ai
|
||||
6
vault/kubernetes-roles/garm.yaml
Normal file
6
vault/kubernetes-roles/garm.yaml
Normal file
@@ -0,0 +1,6 @@
|
||||
bound_service_account_names:
|
||||
- garm
|
||||
bound_service_account_namespaces:
|
||||
- garm
|
||||
token_policies:
|
||||
- garm
|
||||
6
vault/kubernetes-roles/openwebui.yaml
Normal file
6
vault/kubernetes-roles/openwebui.yaml
Normal file
@@ -0,0 +1,6 @@
|
||||
bound_service_account_names:
|
||||
- openwebui-secret
|
||||
bound_service_account_namespaces:
|
||||
- openwebui
|
||||
token_policies:
|
||||
- openwebui
|
||||
6
vault/kubernetes-roles/woodpecker.yaml
Normal file
6
vault/kubernetes-roles/woodpecker.yaml
Normal file
@@ -0,0 +1,6 @@
|
||||
bound_service_account_names:
|
||||
- woodpecker-secret
|
||||
bound_service_account_namespaces:
|
||||
- woodpecker
|
||||
token_policies:
|
||||
- woodpecker
|
||||
3
vault/policy/authentik.hcl
Normal file
3
vault/policy/authentik.hcl
Normal file
@@ -0,0 +1,3 @@
|
||||
path "secret/data/authentik" {
|
||||
capabilities = ["read"]
|
||||
}
|
||||
3
vault/policy/crawl4ai.hcl
Normal file
3
vault/policy/crawl4ai.hcl
Normal file
@@ -0,0 +1,3 @@
|
||||
path "secret/data/crawl4ai" {
|
||||
capabilities = ["read"]
|
||||
}
|
||||
7
vault/policy/garm.hcl
Normal file
7
vault/policy/garm.hcl
Normal file
@@ -0,0 +1,7 @@
|
||||
path "secret/data/garm" {
|
||||
capabilities = ["read"]
|
||||
}
|
||||
|
||||
path "secret/data/backblaze" {
|
||||
capabilities = ["read"]
|
||||
}
|
||||
3
vault/policy/openwebui.hcl
Normal file
3
vault/policy/openwebui.hcl
Normal file
@@ -0,0 +1,3 @@
|
||||
path "secret/data/authentik/openwebui" {
|
||||
capabilities = ["read"]
|
||||
}
|
||||
7
vault/policy/woodpecker.hcl
Normal file
7
vault/policy/woodpecker.hcl
Normal file
@@ -0,0 +1,7 @@
|
||||
path "secret/data/woodpecker" {
|
||||
capabilities = ["read"]
|
||||
}
|
||||
|
||||
path "secret/data/container-registry" {
|
||||
capabilities = ["read"]
|
||||
}
|
||||
Reference in New Issue
Block a user