Pod - Woodpecker CI #19
Labels
No labels
blocked
bug
duplicate
effort
high
effort
low
effort
medium
effort
minimal
enhancement
help wanted
invalid
pr
feedback
pr
reviewing
question
status
backlog
status
document
status
done
status
implement
status
pending
status
planning
status
reviewing
waiting
wontfix
No milestone
No project
No assignees
1 participant
Notifications
Due date
No due date set.
Dependencies
No dependencies set.
Reference
tepichord/milner#19
Loading…
Add table
Add a link
Reference in a new issue
No description provided.
Delete branch "%!s()"
Deleting a branch is permanent. Although the deleted branch may continue to exist for a short time before it actually gets removed, it CANNOT be undone in most cases. Continue?
Naina:
Overview
Deploy Woodpecker CI (single server + one Kubernetes-backend agent) with a new
woodpecker/Terraform root that mirrors thevault/pattern: two namespaces, one pinnedhelm_releasefrom the official Woodpecker chart, and a Tailscale operator Ingress reusing the existingingress-proxiesProxyGroup. Forgejo OAuth credentials are sourced from the in-cluster Vault via a one-time documented sync; runtime build pods land in thewoodpecker-runtimenamespace onlocal-pathstorage.Technical Approach
woodpecker/main.tf— copy the providers/remote-state preamble verbatim fromvault/main.tf(control_plane_state_path,cluster_namevariables;terraform_remote_state.cp; kubernetes + helm providers fromlocal.kc), then create the namespaces, release, and ingress:1Assumption check — the agent chart renders its Role/RoleBinding INTO
WOODPECKER_BACKEND_K8S_NAMESPACE, so that namespace must pre-exist2 3; the default clone step (plugin-git) runs as root, hence theprivilegedruntime namespace4.Add
woodpecker/.gitignore— copyvault/.gitignore(.terraform,*.tfstate,!tests/fixtures/*.tfstate,terraform.tfvars,*.auto.tfvars)5. Commit the.terraform.lock.hclgenerated byterraform -chdir=woodpecker init.Expand
woodpecker/values.yaml(fixes the two skeleton bugs: runtime namespace waswoodpecker, RWX left at chart-defaulttrue)6:Assumption check — chart defaults: agent env ships
WOODPECKER_BACKEND_K8S_STORAGE_RWX: trueandWOODPECKER_BACKEND_K8S_VOLUME_SIZE: 10G7; local-path provisioned volumes are single-node (RWO) unless a shared filesystem is configured8.Keep
server.createAgentSecretat the default (true): the chart-generatedwoodpecker-default-agent-secretis an internal trust secret that never needs operator knowledge.9 10ingress-proxies— ProxyGroup is cluster-scoped, owned byvault/main.tf; no new ProxyGroup resource):11No external gRPC (9000) ingress: agents are in-cluster and reach
woodpecker-server:9000over the Service.12 13woodpecker/tests/— copyvault/tests/fixtures/cp.tfstate, thenplan.tftest.hclmirroringvault/tests/plan.tftest.hclwith mock kubernetes/helm providers:14 15Wire the root test suites —
tests/test_static.py: addwoodpecker/to theterraform fmt -check -recursivedirs16;tests/test_terraform.py: add atest_terraform_test_woodpecker()mirroring the vault one17.Document the out-of-band flow — rewrite
woodpecker/README.md(Terraform deploy + secrets) and add a Woodpecker section toOPERATIONS.md:Assumption check — Forgejo OAuth: the callback URL must be exactly
https://<host>/authorize18; thekubectl -n vault exec vault-0 -- vault kvpattern matches existing ops usage (which passes-address=http://127.0.0.1:8200)19.Apply order on a fresh cluster:
vaultroot first (owns the ProxyGroup the ingress annotation references), thenwoodpecker.20 Nothing secret is ever written to the repo.Implementation Details
Files to modify/create
woodpecker/main.tf(new) — providers/remote-state preamble copied fromvault/main.tf; namespaces,helm_release, ingress above.woodpecker/values.yaml(modify) — full file in step 3.woodpecker/values.yamllint note:yamllintalready checks this file (tests/test_static.py).woodpecker/.gitignore(new),woodpecker/.terraform.lock.hcl(new, committed),woodpecker/tests/plan.tftest.hcl+woodpecker/tests/fixtures/cp.tfstate(new).woodpecker/README.md(rewrite),OPERATIONS.md(new §Woodpecker),tests/test_static.py,tests/test_terraform.py(modify).control-plane/,worker/, orvault/.Dependencies / constraints
woodpecker3.7.3 (agent 2.0.1 / server 3.0.1 subcharts, app 3.18.0)21 22; alternativerepository = "https://woodpecker-ci.org/"(deprecated-but-functional, used in the chart README's own snippet) if OCI pulls fail23.vault/main.tf: kubernetes~> 2.35, helm~> 2.17.24woodpeckerin namespacewoodpeckerso chart defaults (woodpecker-server:9000, agent secret name) need no overrides.25git.gloom.garden(webhooks + clone) — verify reachability out-of-band unverified.linux/arm64— no image changes needed.26 27 28Out-of-scope notes (discovered, not requested by the issue)
local-pathStorageClass is referenced but not provisioned anywhere in this repo29 (Talos ships none; Vault uses hostPath, not PVCs30). Verifykubectl get storageclass local-pathexists before apply unverified (out-of-band cluster check), otherwise deploy local-path-provisioner first (separate change).WOODPECKER_SECRET_EXTENSION_ENDPOINT)33; no native Vault store exists (woodpecker-ci/woodpecker#929)34. Optional future work.WOODPECKER_AGENT_SECRETstays chart-generated (auto-rotated internal trust secret), only external Forgejo credentials are Vault-managed.35Acceptance Criteria
woodpecker/root plans offline:terraform -chdir=woodpecker testpasses with mock providers and a copiedcp.tfstatefixture.main.tfcreateswoodpecker(baseline PSA) andwoodpecker-runtime(privileged PSA) namespaces, a pinnedhelm_release(chartwoodpecker, version3.7.3, namespacewoodpecker), and a Tailscale ingress (ingress-proxiesannotation,tls.hosts: ["woodpecker"], backendwoodpecker-server:80) — no duplicate ProxyGroup.values.yamlsets server env (WOODPECKER_HOST, realWOODPECKER_ADMIN, Forgejo ongit.gloom.garden,woodpecker-forgejo-oauthenvFrom) and agent env (Kubernetes backend →woodpecker-runtime,local-path, RWXfalse,replicas: 1, workernodeSelector);yamllintandterraform fmt -checkare green.tests/test_static.pyformatswoodpecker/,tests/test_terraform.pyrunsterraform teston the new root.git grepfor placeholder/real OAuth values finds none); Forgejo OAuth creds live only in Vault, synced to thewoodpecker-forgejo-oauthSecret, with steps + exact callback URL inwoodpecker/README.mdandOPERATIONS.md.Testing Plan
nix develop, no cluster):terraform -chdir=woodpecker apply):kubectl -n woodpecker get statefulset,pvc—woodpecker-server-0and one agentReady, both PVCsBoundonlocal-path.kubectl -n woodpecker logs <agent-pod>— agent connected towoodpecker-server:9000(no auth errors).https://woodpecker.faun-snapper.ts.netloads (LE cert) after admin-console service approval; log in with aWOODPECKER_ADMINForgejo account..woodpecker.yml; verify the pipeline spawns step pods inwoodpecker-runtime(kubectl -n woodpecker-runtime get pods) and a clone succeeds (proves Forgejo reachability + OAuth).woodpecker-forgejo-oauthSecret and confirm the server pod fails env resolution (proves the wiring is live).Related Issues/PRs
local-pathprovisioner if absent.Research Findings
:8000/ gRPC:9000, and the chart Service exposes http on port 80 → 8000. Install isoci://ghcr.io/woodpecker-ci/helm/woodpecker; thehttps://woodpecker-ci.org/repo is deprecated-but-functional.WOODPECKER_BACKEND_K8S_STORAGE_RWXdefaults totrue— unchangeable onlocal-path(RWO hostPath) — so it must be forcedfalse.WOODPECKER_FORGEJO=true/_URL/_CLIENT/_SECRET); the OAuth callback URL must be exactlyhttps://<host>/authorize. No dedicated Helm values exist — they go inserver.env/secrets.extraSecretNamesForEnvFrom. VSO isn't deployed yet, so this plan syncs once manually.linux/arm64; the chart shipsresources: {}by design, and step pods (not the agent) consume CPU/memory in the runtime namespace.privilegedPSA label on Talos (baseline is fine for the chart's ownwoodpeckernamespace pods).ingress-proxiesis reusable from the woodpecker namespace; in-cluster agents need no external gRPC exposure.Pre-Mortem
local-pathStorageClass is missing or the PVCs never bind — The plan flags that no storage provider is provisioned anywhere in the repo and Talos ships none, yet the entire deploy (server SQLite PVC, agent PVC, pipeline workspace volumes) depends onlocal-pathexisting in-cluster. If that out-of-band check is skipped and the class is absent, the StatefulSets stayPendingand Woodpecker never comes up, which no offlineterraform testcan catch.WOODPECKER_HOSTor the registered callback URL doesn't exactly matchhttps://woodpecker.faun-snapper.ts.net/authorize, login and pipeline triggers fail even though the cluster-side rollout looks healthy.ingress-proxiesProxyGroup are all manual prerequisites not owned by thewoodpecker/root. Missing any of them yields a release that appears applied but cannot authenticate, route traffic, or run builds, and the divergence is invisible to the automated test suite.