The homelab pushes to a project called "homelab"; this cluster's is apps-registry. Harbor rejects a push to a missing project with "unauthorized: project homelab not found" — the word unauthorized sends you looking at the robot account, when the credentials were never the problem. Only the harbor_project default changes. The com/homelab and org/homelab paths in this repo are the library's own package and resource paths and have nothing to do with the registry; renaming those would break the library. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01LEsTefWWifp4ikvhHF5s6N
117 lines
6.1 KiB
Markdown
117 lines
6.1 KiB
Markdown
# devops-lib (GKE)
|
|
|
|
Jenkins Shared Library for the CI/CD pipeline on the GKE cluster. The GCP
|
|
counterpart of the homelab repo of the same name, and a copy rather than a
|
|
shared repo because several values here are cluster-specific in ways that
|
|
would break the other cluster if crossed over.
|
|
|
|
Register it in Jenkins under the name `devops-lib`, exactly as on the
|
|
homelab. Consuming repos then need no change at all — the same two-line
|
|
Jenkinsfile works on either cluster, and which library it resolves to is a
|
|
property of the Jenkins it runs on.
|
|
|
|
**What differs from the homelab copy**, all of it a consequence of GKE
|
|
being a real cloud rather than one VM:
|
|
|
|
- **The registry hostname** is `harbor.35.238.248.203.nip.io`, in the push
|
|
target and in all five fallback Dockerfiles.
|
|
- **Harbor speaks TLS.** The homelab's dind passes `--insecure-registry`;
|
|
here the pod mounts the private CA into dockerd's trust store instead.
|
|
Node trust covers pulls only — a push is a separate client.
|
|
- **`helm_repo_url` uses cluster DNS**, since the clone happens inside a
|
|
build pod. The homelab points it at an ingress hostname.
|
|
- **`build-tools` is not in this repo.** It lives in
|
|
`devops-base-images-gcp` alongside the mirrored base images, and is
|
|
referenced here only by tag.
|
|
|
|
Everything else — the stage flow, the fallback templates, the hooks
|
|
contract — is unchanged. The library was already adapted from a much
|
|
larger, company-wide one; see git history for what was removed.
|
|
|
|
## Using it in a service repo
|
|
|
|
The entire Jenkinsfile is 2 lines:
|
|
|
|
```groovy
|
|
@Library('devops-lib') _
|
|
homelabPipeline(repo_name: 'my-service')
|
|
```
|
|
|
|
`repo_name` is the only required key. Everything else has a sensible
|
|
default — override any of them by passing extra keys to `homelabPipeline`,
|
|
or by committing a `config.yaml` to the service repo's own root (merged
|
|
in after checkout; repo-committed values win over the Jenkinsfile call).
|
|
|
|
| Key | Default | Notes |
|
|
|---|---|---|
|
|
| `service_name` | `repo_name` | Second path segment under `devops-helm-charts/values/` |
|
|
| `argo_app_name` | `repo_name` | Must match the ArgoCD Application's `metadata.name` |
|
|
| `harbor_project` | `apps-registry` | Must already exist in Harbor, and be public unless you also wire an `imagePullSecret` — the app values assume anonymous pull. A push to a missing project fails as `unauthorized: project <name> not found` |
|
|
| `helm_repo_url` | `devops-helm-charts-gcp`, over cluster DNS | `http://gitea-http.gitea.svc.cluster.local:3000/gitadmin/…` — pod-to-pod, so it never leaves the cluster and comes back through the ingress |
|
|
| `image_tag_yq_path` | `.deployment.image.tag` | **Override this if the app's chart isn't `1.0.0`** — e.g. `sts-2.0.0` uses `.podtemplate.image.tag` instead. Getting this wrong doesn't fail loudly: `yq -i` creates the path if missing rather than erroring, silently leaving the real field un-bumped. |
|
|
| `dockerBuildVersion` | none | Only read when the repo has **no Dockerfile of its own** — picks a fallback template (see below). No default; either ship a Dockerfile or set this. |
|
|
|
|
## Pipeline stages
|
|
|
|
`checkOut → loadConfig → runHooks(pre_build) → buildDocker →
|
|
runHooks(post_build) → updateHelmTag → syncArgoApp → notify`, all inside
|
|
a `podTemplate` (`resources/org/homelab/dind-pod.yaml`) via
|
|
`node(POD_LABEL) { ... }`.
|
|
|
|
- **`loadConfig`** — if the repo has a `config.yaml` at its root, its
|
|
keys are merged into the pipeline config (repo values win).
|
|
- **`runHooks`** — reads `config.yaml`'s `hooks.pre_build`/`hooks.post_build`
|
|
lists, each `{name, script, interpreter, requirements, blocking,
|
|
timeout_seconds}`. Blocking by default; `blocking: false` demotes a
|
|
failure to advisory (log + continue). Script paths must be
|
|
repo-relative (no `..`, no absolute paths).
|
|
- **`buildDocker`** — uses the repo's own `Dockerfile` if present;
|
|
otherwise renders one from `resources/com/homelab/<lang>-Dockerfile`
|
|
based on `dockerBuildVersion` (e.g. `go-1.22`, `node-20`,
|
|
`python-3.12`, `java-21`, `php-8.3`). All fallback templates pull base
|
|
images from Harbor's `base-images` project (mirrored via the separate
|
|
`devops-base-images-gcp` repo), not Docker Hub directly. Only versions
|
|
actually mirrored there resolve — an unmirrored tag fails the build
|
|
rather than silently falling back to Docker Hub.
|
|
- **`updateHelmTag`** — clones `devops-helm-charts`, bumps the image tag
|
|
via `yq` at `image_tag_yq_path`, commits, pushes to `main`.
|
|
- **`syncArgoApp`** — calls the ArgoCD REST API to sync `argo_app_name`.
|
|
|
|
## Adding a new language's fallback template
|
|
|
|
1. Add the base image to `devops-base-images/images.txt`, re-mirror it
|
|
into Harbor.
|
|
2. Add `resources/com/homelab/<lang>-Dockerfile`, parametrized by
|
|
`${version}` (rendered via `constructTemplate.groovy`'s
|
|
`SimpleTemplateEngine` wrapper).
|
|
3. Add a case for it in `buildDocker.groovy`'s `templates` map.
|
|
|
|
## Build-tools image
|
|
|
|
The `docker-cli` container runs
|
|
`harbor.35.238.248.203.nip.io/base-images/build-tools:1`, which bakes in
|
|
git, yq, bash, python3 with pip and venv, and curl, so nothing is installed
|
|
on demand on every build.
|
|
|
|
**It is not built here.** The Dockerfile lives in `devops-base-images-gcp`,
|
|
next to the mirrored base images, because it is the same kind of artefact:
|
|
built by hand, occasionally, and pushed to Harbor. A Jenkins job could not
|
|
build it anyway — it is the image Jenkins builds *in*.
|
|
|
|
`dind-pod.yaml` pins the tag, so rebuilding the image rolls nothing out
|
|
until that pin is bumped. Bump the tag rather than overwriting one.
|
|
|
|
## Registry trust
|
|
|
|
`dind-pod.yaml` mounts the `registry-ca` ConfigMap (published by
|
|
`devops-infra-argo-config-gcp`) into the dind container at
|
|
`/etc/docker/certs.d/harbor.35.238.248.203.nip.io/ca.crt`.
|
|
|
|
Without it, pushes fail TLS verification while pulls of the same image
|
|
succeed, which reads like a broken registry. The reason is that the two are
|
|
different clients: pulls are performed by containerd on the node, which was
|
|
told to trust this CA when the node pool was created, whereas the push comes
|
|
from dockerd inside the build pod, which has its own trust store. The
|
|
directory name must be the registry hostname exactly — dockerd looks the
|
|
path up by host and silently ignores a mismatch.
|