# New resource — didn't exist in the original devops-lib. Homelab's real # pipelines built on static agents (node('slave02')) with Docker already # available; this homelab's Jenkins uses dynamic per-build Kubernetes # agents (agent.enabled in the jenkins chart), which don't have a Docker # daemon by default. This pod template adds one as a sidecar container — # the "docker" container runs privileged dind, "docker-cli" is what the # pipeline actually execs into via container('docker-cli'), talking to # its sibling over localhost since containers in one pod share a network # namespace. apiVersion: v1 kind: Pod spec: containers: - name: docker image: docker:27-dind securityContext: privileged: true # No --insecure-registry and no mounted CA: Harbor is reached at # harbor.infra.deployshed.com, which carries a publicly trusted Let's # Encrypt certificate, so dockerd's own trust store already accepts it # with nothing configured. # # This previously mounted the private registry CA here, because the # registry was a nip.io name that no public CA can issue for (nip.io is # not on the public suffix list, and every *.nip.io certificate shares # one rate limit), so cert-manager signed it from a private CA instead. # A pull was fine — the node pool was told to trust that CA and # containerd performs pulls — but a push is a different client with its # own trust store, which is why dockerd needed the CA at # /etc/docker/certs.d//ca.crt. Moving to a real domain # removes the whole arrangement rather than repointing it. # # The registry is still named by its ingress hostname rather than # harbor-core.harbor.svc.cluster.local, for a reason that still holds: # cluster DNS resolves from this pod but not from the node's containerd # doing the real Deployment pull, and Docker matches both stored # credentials and trust by exact hostname — so push and pull have to # name the registry identically. env: - name: DOCKER_TLS_CERTDIR value: "" volumeMounts: - name: docker-graph-storage mountPath: /var/lib/docker - name: docker-cli # Custom image, built and pushed by hand from devops-base-images-gcp # (build-tools.Dockerfile there) — bakes in git/yq/bash/python3 with # pip and venv/curl so nothing needs installing on every single build, # which was slow and quietly undermined reproducibility. # # It lives in the base-images project rather than homelab because that # project is public: this pod pulls the image before any credential is # available to it. # # Versioned tag, never :latest — rebuilding the tools image must not # roll out until this pin is bumped deliberately. image: harbor.infra.deployshed.com/base-images/build-tools:1 command: ["cat"] tty: true env: - name: DOCKER_HOST value: tcp://localhost:2375 # docker-config below mounts the Harbor push-auth secret # read-only at /root/.docker (needed so `docker push` finds # config.json without an explicit `docker login` step) — but # modern `docker build` defaults to BuildKit/buildx, which wants # to create its own state dir at /root/.docker/buildx and fails # with "read-only file system" since the whole mount is # read-only. Forcing the classic builder avoids needing to write # there at all. - name: DOCKER_BUILDKIT value: "0" # For syncArgoApp.groovy — read directly from the ESO-managed # Secret, not a Jenkins-native credential (nothing in this # pipeline uses Jenkins' own credential store; staying consistent # rather than mixing the two approaches). - name: ARGOCD_TOKEN valueFrom: secretKeyRef: name: argocd-jenkins-ci-token key: token # Without this, every build using this pod template hard-fails # to even start until the token secret exists — including the # very first demo-go-app run, before the manual # `argocd account generate-token` bootstrap step has happened. optional: true volumeMounts: - name: docker-config mountPath: /root/.docker readOnly: true volumes: - name: docker-graph-storage emptyDir: {} - name: docker-config secret: secretName: harbor-robot-dockerconfig # kubernetes.io/dockerconfigjson secrets store their data under # the fixed key `.dockerconfigjson` (mandated, since that's what # kubelet reads for imagePullSecrets) — without this remap, the # mounted file at /root/.docker is literally named # `.dockerconfigjson`, not `config.json`, which is the only # filename the docker CLI itself ever reads for stored # credentials. Docker found nothing there and silently pushed # unauthenticated, which Harbor correctly rejected as # unauthorized — confirmed the robot account/credentials # themselves were fine the whole time by requesting a push token # directly from Harbor's token endpoint with the same username/ # password and getting one back with push access granted. items: - key: .dockerconfigjson path: config.json