From 17ded87439d877d035e8706bd69848157b62d7d0 Mon Sep 17 00:00:00 2001 From: Ben Vincent Date: Fri, 24 Jul 2026 23:09:46 +1000 Subject: [PATCH] pdbmux: ship as k8s container, drop per-VM systemd/RPM delivery The estate direction is all-in-kubernetes, so pdbmux (a long-running daemon) should run as an in-cluster service rather than an RPM-installed systemd unit on each VM. The RPM is for workstation/VM CLI tools only; a daemon does not belong there. - Remove packaging/pdbmux.service and drop pdbmux (binary, systemd unit, completions) from the RPM/nfpm spec and build-rpm.sh. - Keep pdbmux in the Makefile build and the test suite. - Add Dockerfile.pdbmux building a static CGO_ENABLED=0 binary on distroless (mirrors encapi's image style). - Add .woodpecker/docker.yaml to build+push git.unkin.net/unkin/pdbmux: on v* tags via the docker-buildx plugin (droneci/DRONECI_PASSWORD creds, same as encapi), with k8s resources set. - Update README/AGENTS.md: deployment is k8s, config via PDBMUX_* env. --- .woodpecker/docker.yaml | 34 +++++++++++++++++++++++++++++ AGENTS.md | 13 ++++++++--- Dockerfile.pdbmux | 24 ++++++++++++++++++++ README.md | 47 +++++++++++++++++++++++++++++----------- packaging/nfpm.yaml | 28 +++++------------------- packaging/pdbmux.service | 31 -------------------------- scripts/build-rpm.sh | 7 ++++-- 7 files changed, 112 insertions(+), 72 deletions(-) create mode 100644 .woodpecker/docker.yaml create mode 100644 Dockerfile.pdbmux delete mode 100644 packaging/pdbmux.service diff --git a/.woodpecker/docker.yaml b/.woodpecker/docker.yaml new file mode 100644 index 0000000..e40c74d --- /dev/null +++ b/.woodpecker/docker.yaml @@ -0,0 +1,34 @@ +# Build and push the pdbmux container image on a v* tag. pdbmux is a k8s-only +# daemon (deployed via argocd-apps), so it ships as an image rather than in the +# RPM. Mirrors the estate convention (encapi's docker.yaml): the +# woodpeckerci/plugin-docker-buildx plugin pushes to the Gitea registry using +# the droneci / DRONECI_PASSWORD credentials. +when: + - event: tag + ref: refs/tags/v* + +steps: + - name: docker-pdbmux + image: woodpeckerci/plugin-docker-buildx + settings: + registry: git.unkin.net + repo: git.unkin.net/unkin/pdbmux + dockerfile: Dockerfile.pdbmux + build_args: + VERSION: ${CI_COMMIT_TAG} + username: droneci + password: + from_secret: DRONECI_PASSWORD + tags: + - ${CI_COMMIT_TAG} + - latest + backend_options: + kubernetes: + serviceAccountName: default + resources: + requests: + memory: 1Gi + cpu: 1 + limits: + memory: 2Gi + cpu: 2 diff --git a/AGENTS.md b/AGENTS.md index 6780a52..d7673b9 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -33,10 +33,10 @@ internal/puppet/ # shared: config, puppetdb nodes query, board URLs go.mod # Go module (module name: node-lookup) go.sum # dependency checksums Makefile # build / test / lint / completions / rpm / version-bump targets -packaging/nfpm.yaml # nfpm spec (envsubst-templated) for the RPM (all 4 binaries) -packaging/pdbmux.service # systemd unit for the pdbmux daemon +packaging/nfpm.yaml # nfpm spec (envsubst-templated) for the RPM (CLI tools only) +Dockerfile.pdbmux # container image for the k8s-only pdbmux daemon scripts/build-rpm.sh # generates completions + packages the RPM with nfpm -.woodpecker/ # CI: build, test, pre-commit (PR) + release (tag) +.woodpecker/ # CI: build, test, pre-commit (PR) + release/docker (tag) dist/ # build output: binaries, completions, RPM (not committed) ``` @@ -63,6 +63,13 @@ make rpm # build the binary + package it into dist/*.rpm via nfpm and bundles them alongside `/usr/bin/node-lookup`. On a `v*` tag the release pipeline builds the RPM and `PUT`s it to the artifactapi `rpm-internal` repo. +The RPM contains the workstation/VM CLI tools only (`node-lookup`, `pburl`, +`pblastreport`). `pdbmux` is a k8s-only daemon and is deliberately excluded from +the RPM — it is released as a container image +(`git.unkin.net/unkin/pdbmux:`, built by `.woodpecker/docker.yaml` from +`Dockerfile.pdbmux`) and deployed via `argocd-apps`. `make build` and +`go test ./...` still cover pdbmux. + ## Shell completions Cobra provides a `completion` subcommand: diff --git a/Dockerfile.pdbmux b/Dockerfile.pdbmux new file mode 100644 index 0000000..35ae319 --- /dev/null +++ b/Dockerfile.pdbmux @@ -0,0 +1,24 @@ +# Container image for pdbmux, the merging PuppetDB proxy daemon. This repo ships +# several CLI tools (node-lookup/pburl/pblastreport) as an RPM, but pdbmux is a +# k8s-only service, so it gets its own Dockerfile (Dockerfile.pdbmux) and image. +FROM golang:1.25-alpine AS builder + +RUN apk add --no-cache git + +WORKDIR /build + +COPY go.mod go.sum ./ +RUN go mod download + +COPY . . + +ARG VERSION=dev +RUN CGO_ENABLED=0 go build -ldflags="-s -w -X main.version=${VERSION}" -o pdbmux ./cmd/pdbmux + +FROM gcr.io/distroless/static-debian12:nonroot + +COPY --from=builder /build/pdbmux /usr/local/bin/pdbmux + +EXPOSE 8080 + +ENTRYPOINT ["pdbmux", "serve"] diff --git a/README.md b/README.md index fbe2a92..5d5e6f8 100644 --- a/README.md +++ b/README.md @@ -1,10 +1,15 @@ # node-lookup tools -PuppetDB CLIs and one proxy daemon, shipped together in a single RPM: +PuppetDB CLIs plus one proxy daemon. + +The CLIs ship together in a single RPM for workstations/VMs: - **`node-lookup`** — query and filter PuppetDB node facts. - **`pburl`** — print each host's Puppetboard node-page URL. - **`pblastreport`** — print each host's last Puppet report time + Puppetboard URL. + +The proxy daemon is deployed to Kubernetes as a container image (not in the RPM): + - **`pdbmux`** — merging HTTP proxy over two PuppetDBs (see below). See [AGENTS.md](AGENTS.md) for the CLI tools' flags, config, and internals. This @@ -71,11 +76,12 @@ unknown fields survive untouched. Precedence (lowest → highest): **defaults < config file < env vars (`PDBMUX_*`) < flags**. -Config file: `$XDG_CONFIG_HOME/pdbmux/config.yaml` (as an RPM/systemd service: -`/etc/pdbmux/config.yaml`). +Config file: `$XDG_CONFIG_HOME/pdbmux/config.yaml`. In Kubernetes, configuration +is supplied entirely via `PDBMUX_*` env vars (no config file), which is the +supported deployment path — see [Deployment](#deployment). ```yaml -# ~/.config/pdbmux/config.yaml (or /etc/pdbmux/config.yaml under systemd) +# ~/.config/pdbmux/config.yaml (local dev; in k8s use PDBMUX_* env instead) listen: ":8080" backends: - name: old @@ -123,15 +129,30 @@ NODE_LOOKUP_URL=http://localhost:8080/pdb/query/v4/facts pblastreport somehost ### Deployment -The RPM installs `/usr/bin/pdbmux` plus a systemd unit at -`/usr/lib/systemd/system/pdbmux.service` (reads `/etc/pdbmux/config.yaml`): +`pdbmux` runs **in Kubernetes** as a container, in line with the all-in-k8s +estate direction — it is not shipped as a per-VM RPM/systemd service. The image +is built and pushed on every `v*` tag (`.woodpecker/docker.yaml`) to: -```bash -systemctl enable --now pdbmux -curl -s localhost:8080/healthz +``` +git.unkin.net/unkin/pdbmux: ``` -Suited to run as an RPM-managed daemon on a VM alongside the existing Puppet -infra during migration; the same static binary is container-ready for a future -k8s deployment (single listener, `/healthz` liveness/readiness probe, config via -`PDBMUX_*` env). +It is a minimal static (`CGO_ENABLED=0`) binary on a distroless base +(`Dockerfile.pdbmux`), configured entirely via `PDBMUX_*` env vars, with a +single HTTP listener and `/healthz` for liveness/readiness probes. + +The Deployment/Service/Gateway manifests live in the estate's `argocd-apps` repo +under `apps/base/pdbmux/` (namespace `pdbmux`, 2 replicas), and it is exposed to +VM/workstation `node-lookup` consumers over HTTPS at: + +``` +https://pdbmux.k8s.syd1.au.unkin.net +``` + +Locally you can still run the binary directly for development: + +```bash +PDBMUX_BACKENDS='old=http://puppetdbapi.service.consul:8080,new=http://puppetdb.puppet.svc.cluster.local:8080' \ + pdbmux serve +curl -s localhost:8080/healthz +``` diff --git a/packaging/nfpm.yaml b/packaging/nfpm.yaml index 0b80b24..ae40ee5 100644 --- a/packaging/nfpm.yaml +++ b/packaging/nfpm.yaml @@ -42,18 +42,12 @@ contents: mode: 0755 owner: root group: root - - src: dist/pdbmux - dst: /usr/bin/pdbmux - file_info: - mode: 0755 - owner: root - group: root - # systemd unit for the pdbmux proxy daemon (config: /etc/pdbmux/config.yaml). - - src: packaging/pdbmux.service - dst: /usr/lib/systemd/system/pdbmux.service - file_info: - mode: 0644 + # NOTE: pdbmux is intentionally NOT shipped in this RPM. It is a k8s-only + # daemon (deployed via argocd-apps as a container image), not a workstation/VM + # CLI tool, so it has no place in the CLI package. pdbmux is still built and + # tested in this repo (see Makefile) and released as a container image + # (.woodpecker/docker.yaml). # Shell completions (generated by scripts/build-rpm.sh before packaging). - src: dist/completions/node-lookup.bash @@ -92,15 +86,3 @@ contents: dst: /usr/share/fish/vendor_completions.d/pblastreport.fish file_info: mode: 0644 - - src: dist/completions/pdbmux.bash - dst: /usr/share/bash-completion/completions/pdbmux - file_info: - mode: 0644 - - src: dist/completions/_pdbmux - dst: /usr/share/zsh/site-functions/_pdbmux - file_info: - mode: 0644 - - src: dist/completions/pdbmux.fish - dst: /usr/share/fish/vendor_completions.d/pdbmux.fish - file_info: - mode: 0644 diff --git a/packaging/pdbmux.service b/packaging/pdbmux.service deleted file mode 100644 index a58461b..0000000 --- a/packaging/pdbmux.service +++ /dev/null @@ -1,31 +0,0 @@ -[Unit] -Description=pdbmux - merging proxy over old + new PuppetDB during migration -Documentation=https://git.unkin.net/unkin/node-lookup -After=network-online.target -Wants=network-online.target - -[Service] -Type=simple -# Configure via /etc/pdbmux/config.yaml or PDBMUX_* env in this file / a drop-in. -# Example env overrides: -# Environment=PDBMUX_LISTEN=:8080 -# Environment=PDBMUX_MERGE=freshness -Environment=XDG_CONFIG_HOME=/etc -ExecStart=/usr/bin/pdbmux serve -Restart=on-failure -RestartSec=5 - -# Hardening: pdbmux only makes outbound HTTP(S) and listens on a socket. -DynamicUser=yes -NoNewPrivileges=yes -ProtectSystem=strict -ProtectHome=yes -PrivateTmp=yes -PrivateDevices=yes -ProtectKernelTunables=yes -ProtectControlGroups=yes -RestrictAddressFamilies=AF_INET AF_INET6 -RestrictNamespaces=yes - -[Install] -WantedBy=multi-user.target diff --git a/scripts/build-rpm.sh b/scripts/build-rpm.sh index e885215..87aaab2 100755 --- a/scripts/build-rpm.sh +++ b/scripts/build-rpm.sh @@ -12,7 +12,10 @@ cd "${ROOT_DIR}" VERSION="${1:-${CI_COMMIT_TAG:-0.0.0-dev}}" VERSION="${VERSION#v}" # strip a leading v BINARY="node-lookup" -BINARIES=(node-lookup pburl pblastreport pdbmux) +# RPM ships the workstation/VM CLI tools only. pdbmux is a k8s-only daemon and +# is deliberately excluded from the RPM (it is released as a container image); +# it is still built + tested in this repo via the Makefile. +BINARIES=(node-lookup pburl pblastreport) DIST="dist" for b in "${BINARIES[@]}"; do @@ -37,7 +40,7 @@ export PACKAGE_VERSION="${VERSION}" export PACKAGE_RELEASE="1" export PACKAGE_ARCH="amd64" export PACKAGE_PLATFORM="linux" -export PACKAGE_DESCRIPTION="CLI tools for PuppetDB: node-lookup (fact lookup/filtering), pburl and pblastreport (Puppetboard URLs and last-report times), plus pdbmux (merging proxy over the old + new PuppetDB during migration)" +export PACKAGE_DESCRIPTION="CLI tools for PuppetDB: node-lookup (fact lookup/filtering), pburl and pblastreport (Puppetboard URLs and last-report times)" export PACKAGE_MAINTAINER="Ben Vincent " export PACKAGE_HOMEPAGE="https://git.unkin.net/unkin/node-lookup" export PACKAGE_LICENSE="MIT"