feat(kubernetes): define resource templates #138

Merged
fuzzy merged 2 commits from feat/k8s-resource-templates into main 2026-08-02 19:59:59 +00:00
Owner

What

Define the Kubernetes resource templates in internal/kubernetes/templates.go:

  • Deployment template (#52) — runs a static file server (nginx:alpine by default, configurable via new kubernetes.static_image config) serving content from a shared volume mounted at /var/www/site. Uses emptyDir as the volume source for now; the storage task (#58) formalizes PVC and the production-version symlink.
  • Service template (#53) — exposes the static server on port 80.
  • Ingress/Route templates (#54) — production and preview Ingress builders moved into templates.go.
  • All resources carry app.kubernetes.io/name / app.kubernetes.io/instance labels (also addresses the earlier review suggestion).

Implements PublishSite — creates or updates the Deployment, Service, and Ingress for a production site via generic upsert helpers (Deployment/Service now share the ingress create-or-update pattern).

Config: added kubernetes.static_image (default nginx:alpine).

Tests: fake-clientset coverage for PublishSite (image, port, mount path, service selector/port, ingress host), idempotent re-publish, and Deployment/Service template label/selector shapes.

Why

Phase 4 task #55 — completes the Kubernetes integration layer; PublishSite is what the webhook receiver (#73) and site registration (#67) call.

Testing

  • go build ./...
  • go vet ./...
  • go test -race ./internal/kubernetes/ — 16 tests pass
  • gofmt -l ./cmd ./internal ./pkg clean
  • golangci-lint run — 0 issues
  • pre-commit hooks all pass

Breaking Changes

None.

Notes

  • Deployment uses emptyDir for now; the storage task (#58) will wire PVC and the /var/www/site symlink.
  • Preview Deployment/Service templates are intentionally deferred to Phase 6 (preview deployments).
  • Issues #52–#55 were closed via the Forgejo API as part of this task per the workflow.

Closes #52
Closes #53
Closes #54
Closes #55

## What Define the Kubernetes resource templates in `internal/kubernetes/templates.go`: - **Deployment template (#52)** — runs a static file server (`nginx:alpine` by default, configurable via new `kubernetes.static_image` config) serving content from a shared volume mounted at `/var/www/site`. Uses `emptyDir` as the volume source for now; the storage task (#58) formalizes PVC and the production-version symlink. - **Service template (#53)** — exposes the static server on port 80. - **Ingress/Route templates (#54)** — production and preview Ingress builders moved into `templates.go`. - All resources carry `app.kubernetes.io/name` / `app.kubernetes.io/instance` labels (also addresses the earlier review suggestion). **Implements `PublishSite`** — creates or updates the Deployment, Service, and Ingress for a production site via generic upsert helpers (Deployment/Service now share the ingress create-or-update pattern). **Config**: added `kubernetes.static_image` (default `nginx:alpine`). **Tests**: fake-clientset coverage for `PublishSite` (image, port, mount path, service selector/port, ingress host), idempotent re-publish, and Deployment/Service template label/selector shapes. ## Why Phase 4 task #55 — completes the Kubernetes integration layer; `PublishSite` is what the webhook receiver (#73) and site registration (#67) call. ## Testing - [x] `go build ./...` - [x] `go vet ./...` - [x] `go test -race ./internal/kubernetes/` — 16 tests pass - [x] `gofmt -l ./cmd ./internal ./pkg` clean - [x] `golangci-lint run` — 0 issues - [x] pre-commit hooks all pass ## Breaking Changes None. ## Notes - Deployment uses `emptyDir` for now; the storage task (#58) will wire PVC and the `/var/www/site` symlink. - Preview Deployment/Service templates are intentionally deferred to Phase 6 (preview deployments). - Issues #52–#55 were closed via the Forgejo API as part of this task per the workflow. Closes #52 Closes #53 Closes #54 Closes #55
feat(kubernetes): define resource templates
Some checks failed
Test and Release / lint (pull_request) Successful in 4m44s
Test and Release / test (pull_request) Has been cancelled
3b5e16628f
Define the Kubernetes resource templates in internal/kubernetes:

- Deployment template runs a static file server (nginx:alpine by
  default, configurable via kubernetes.static_image) serving content
  from a shared volume mounted at /var/www/site; emptyDir is the
  volume source for now, with the storage task to formalize PVC and
  the production version symlink
- Service template exposes the static server on port 80
- Ingress templates (production and preview) moved into templates.go,
  all resources labeled with app.kubernetes.io names

Implement PublishSite, which creates or updates the Deployment,
Service, and Ingress for a production site via generic upsert helpers.

Add fake-clientset tests for PublishSite, idempotency, and the
deployment/service template shapes. Mark task done in ROADMAP.md.

closes #52
closes #53
closes #54
closes #55
fuzzy scheduled this pull request to auto merge when all checks succeed 2026-08-02 19:45:11 +00:00
the.auditor requested changes 2026-08-02 19:47:57 +00:00
Dismissed
the.auditor left a comment

Review Summary

Verified locally on the PR head (3b5e166): go build, go vet, go mod verify, gofmt (incl. ./pkg), golangci-lint run all clean; all 16 kubernetes tests pass with -race.

The refactor is solid — generic upsertDeployment/upsertService mirror the ingress pattern, PublishSite correctly orders Deployment → Service → Ingress, selector/label wiring is consistent (service selector matches pod labels, deployment selector matches template labels), and the static_image config is wired through with updated tests.

Blocking Issue

  1. internal/kubernetes/templates.go — the Deployment template mounts site content at /var/www/site but the default image nginx:alpine serves its stock docroot /usr/share/nginx/html. The container spec has no command/args and no ConfigMap mount to override the nginx config, so a site published with the default configuration will serve nginx's welcome page, not the site content. The mount path and the image's docroot simply don't line up.

    Fix options (one is enough):

    • Mount at /usr/share/nginx/html instead of /var/www/site (if keeping stock nginx:alpine), or
    • Ship a small nginx config (ConfigMap or inline command) pointing root at /var/www/site, or
    • Change the static_image default to an image that serves /var/www/site and document the contract.

    Task #52 is "Deployment template (static file server)" — with the default config this template doesn't actually serve the mounted content. If this serving arrangement is intentionally deferred to the storage task (#58), the mount path should at least match what the default image serves today.

Non-blocking Suggestions

  1. No readiness/liveness probes on the container — worth adding once the HTTP API/health endpoint exists; until then a tcpSocket on 80 would do.
  2. No resource requests/limits — add sane defaults so the scheduler isn't left guessing.
  3. replicas: 1 is hardcoded — already tracked by task #113 (configurable replicas). Fine for now.

The blocking item is a small change to align the mount path with what the default image serves (or vice versa).

## Review Summary Verified locally on the PR head (`3b5e166`): `go build`, `go vet`, `go mod verify`, `gofmt` (incl. `./pkg`), `golangci-lint run` all clean; all 16 kubernetes tests pass with `-race`. The refactor is solid — generic `upsertDeployment`/`upsertService` mirror the ingress pattern, `PublishSite` correctly orders Deployment → Service → Ingress, selector/label wiring is consistent (service selector matches pod labels, deployment selector matches template labels), and the `static_image` config is wired through with updated tests. ## Blocking Issue 1. `internal/kubernetes/templates.go` — the Deployment template mounts site content at `/var/www/site` but the default image `nginx:alpine` serves its stock docroot `/usr/share/nginx/html`. The container spec has no `command`/`args` and no ConfigMap mount to override the nginx config, so a site published with the default configuration will serve nginx's welcome page, not the site content. The mount path and the image's docroot simply don't line up. **Fix options** (one is enough): - Mount at `/usr/share/nginx/html` instead of `/var/www/site` (if keeping stock `nginx:alpine`), or - Ship a small nginx config (ConfigMap or inline `command`) pointing `root` at `/var/www/site`, or - Change the `static_image` default to an image that serves `/var/www/site` and document the contract. Task #52 is "Deployment template (static file server)" — with the default config this template doesn't actually serve the mounted content. If this serving arrangement is intentionally deferred to the storage task (#58), the mount path should at least match what the default image serves today. ## Non-blocking Suggestions 1. No readiness/liveness probes on the container — worth adding once the HTTP API/health endpoint exists; until then a `tcpSocket` on 80 would do. 2. No resource requests/limits — add sane defaults so the scheduler isn't left guessing. 3. `replicas: 1` is hardcoded — already tracked by task #113 (configurable replicas). Fine for now. The blocking item is a small change to align the mount path with what the default image serves (or vice versa).
fix(kubernetes): serve site content in default nginx config
All checks were successful
Test and Release / lint (pull_request) Successful in 4m24s
Test and Release / test (pull_request) Successful in 7m18s
6d71488349
The Deployment template mounted site content at /var/www/site, but the
default nginx:alpine image serves its stock docroot
/usr/share/nginx/html, so a site published with the default
configuration would serve nginx's welcome page.

Add an nginx ConfigMap that points the static server root at
/var/www/site, mounted read-only over /etc/nginx/conf.d/default.conf.
PublishSite now upserts the ConfigMap before the Deployment. Add
readiness and liveness TCP probes on port 80 to the static container.
Extend the PublishSite test to cover the ConfigMap, config mount, and
probes.
Author
Owner

Review changes addressed in 6d71488:

Blocking issue — the Deployment now ships an nginx ConfigMap (nginxServerConfig) that points root /var/www/site, mounted read-only over /etc/nginx/conf.d/default.conf with SubPath: default.conf. PublishSite upserts the ConfigMap before the Deployment. Kept the /var/www/site mount path per roadmap #57 (production-version symlink contract). Test extended to verify the ConfigMap data references the mount path and the config mount exists.

Non-blocking #1 — added readiness and liveness tcpSocket probes on port 80 to the static container.

Verified: go build, go vet, gofmt, golangci-lint run clean; all 16 kubernetes tests pass with -race.

Review changes addressed in `6d71488`: **Blocking issue** — the Deployment now ships an nginx ConfigMap (`nginxServerConfig`) that points `root /var/www/site`, mounted read-only over `/etc/nginx/conf.d/default.conf` with `SubPath: default.conf`. `PublishSite` upserts the ConfigMap before the Deployment. Kept the `/var/www/site` mount path per roadmap #57 (production-version symlink contract). Test extended to verify the ConfigMap data references the mount path and the config mount exists. **Non-blocking #1** — added readiness and liveness `tcpSocket` probes on port 80 to the static container. Verified: `go build`, `go vet`, `gofmt`, `golangci-lint run` clean; all 16 kubernetes tests pass with `-race`.
the.auditor left a comment

Review Summary

The blocking issue from the previous review is resolved.

  1. Blocker fixedPublishSite now upserts a ConfigMap (nginxConfigMapTemplate) holding a server config with root /var/www/site; and try_files $uri $uri/ /index.html;, mounted over nginx's stock /etc/nginx/conf.d/default.conf (via SubPath, read-only). The Deployment mount path now matches what the server actually serves, so a site published with the default nginx:alpine config will serve the site content. TestPublishSite asserts the config references the mount path and that both the config volume and probes are wired.
  2. Probes added — readiness and liveness tcpSocket probes on port 80, addressing the earlier suggestion.
  3. Ordering is correct: ConfigMap → Deployment → Service → Ingress.

Verified locally on the PR head (6d71488): go build, go vet, go mod verify, gofmt, golangci-lint run all clean; all 16 kubernetes tests pass with -race.

No blocking issues.

Non-blocking observations:

  1. The ConfigMap uses SubPath mounts, so config changes won't hot-reload into running pods (requires a pod restart). Fine for a per-site static config, but worth knowing when the config becomes dynamic.
  2. Still no resource requests/limits on the container — worth adding sane defaults before this lands in a real cluster.

Approving.

## Review Summary The blocking issue from the previous review is resolved. 1. **Blocker fixed** — `PublishSite` now upserts a `ConfigMap` (`nginxConfigMapTemplate`) holding a server config with `root /var/www/site;` and `try_files $uri $uri/ /index.html;`, mounted over nginx's stock `/etc/nginx/conf.d/default.conf` (via `SubPath`, read-only). The Deployment mount path now matches what the server actually serves, so a site published with the default `nginx:alpine` config will serve the site content. `TestPublishSite` asserts the config references the mount path and that both the config volume and probes are wired. 2. **Probes added** — readiness and liveness `tcpSocket` probes on port 80, addressing the earlier suggestion. 3. Ordering is correct: ConfigMap → Deployment → Service → Ingress. Verified locally on the PR head (`6d71488`): `go build`, `go vet`, `go mod verify`, `gofmt`, `golangci-lint run` all clean; all 16 kubernetes tests pass with `-race`. **No blocking issues.** Non-blocking observations: 1. The ConfigMap uses `SubPath` mounts, so config changes won't hot-reload into running pods (requires a pod restart). Fine for a per-site static config, but worth knowing when the config becomes dynamic. 2. Still no resource requests/limits on the container — worth adding sane defaults before this lands in a real cluster. Approving.
fuzzy merged commit 6d71488349 into main 2026-08-02 19:59:59 +00:00
fuzzy deleted branch feat/k8s-resource-templates 2026-08-02 19:59:59 +00:00
Sign in to join this conversation.
No reviewers
No milestone
No project
No assignees
3 participants
Notifications
Due date
The due date is invalid or out of range. Please use the format "yyyy-mm-dd".

No due date set.

Dependencies

No dependencies set

Reference
thwap/thwap-pagesd!138
No description provided.