Files
issue-core/workplans/ISSUE-WP-0003-railiance01-deployment.md
tegwick 3e29bc964d Add railiance01 deployment artifacts and fix container image build
Introduce Dockerfile, entrypoint, and k8s/railiance manifests for the
ArgoCD GitOps pilot (ISSUE-WP-0003). Rename the Gitea PyPI build arg to
GITEA_PYPI_INDEX_URL so pip still resolves dependencies from PyPI.
2026-06-19 21:05:18 +02:00

236 lines
9.9 KiB
Markdown

---
id: ISSUE-WP-0003
type: workplan
title: "Deploy issue-core as a service on railiance01 (ArgoCD GitOps pilot)"
domain: custodian
repo: issue-core
status: active
owner: claude
topic_slug: custodian
created: "2026-06-19"
updated: "2026-06-19"
state_hub_workstream_id: ""
---
# Deploy issue-core as a service on railiance01 (ArgoCD GitOps pilot)
`issue-core` is the authoritative task-lifecycle manager and the REST ingestion
target for activity-core's `IssueSink`. Deployment artifacts (`Dockerfile`,
`docker-entrypoint.sh`, `k8s/railiance/`) are now in-repo; the image builds
locally and `/healthz` returns 200. The railiance01 cluster still has no
`issue-core` namespace or workload — nothing is deployed until T01 push and
T02 ArgoCD bootstrap complete.
This workplan stands up `issue-core` as a first-class in-cluster service on
railiance01 **via ArgoCD GitOps** — making issue-core the cluster's first
declarative Application and turning on the idle GitOps capability.
## Current state (verified 2026-06-19)
- **Deployment artifacts in-repo:** `Dockerfile`, `docker-entrypoint.sh`, and
`k8s/railiance/` (Kustomize: ExternalSecret, ConfigMap, Deployment, Service).
Image builds locally; `docker run` + `GET /healthz` returns 200. Image **not
yet pushed** to `gitea.coulomb.social/coulomb/issue-core:0.2.0`.
- **Dockerfile fix (2026-06-19):** build arg renamed `GITEA_PYPI_INDEX_URL`
`ARG PIP_INDEX_URL` leaked into the build env and pip used Gitea as the sole
index, so dependencies like `click` were not found.
- **railiance01 cluster:** no `issue-core` namespace; no issue-core
Deployment/Service/Pod in any namespace.
- **Dangling reference:** `activity-core/k8s/railiance/20-runtime.yaml` sets
`ISSUE_CORE_URL: http://issue-core.issue-core.svc.cluster.local:8010` — a
service that does not exist, on the **wrong port** (issue-core serves 8765) —
with `ISSUE_SINK_TYPE: "null"` so emission is disabled. It is a placeholder.
- **Packaging precursor is done:** `ISSUE-WP-0002` published
`issue-core==0.2.0` to the Coulomb Gitea PyPI index.
- **ArgoCD is installed but unused:** all 7 components healthy (~290d), but
**0 Applications, 0 ApplicationSets, 0 registered git repos**, only the stock
`default` AppProject. No `kind: Application` manifests exist in any infra repo.
- **Existing deploy pattern is imperative** (the path we are *replacing* for
this service): local `docker build``k3s ctr images import` (side-load, no
registry) → `rsync` manifests → `kubectl apply` (see
`activity-core/k8s/railiance/README.md`).
## Decisions
- **Deployment method = ArgoCD GitOps** (operator decision 2026-06-19).
issue-core is the pilot Application; the imperative side-load pattern is not
used for this service.
- **ArgoCD bootstrap owned by `railiance-platform`** (operator decision
2026-06-19). Platform owns repo registration, AppProject/app-of-apps
conventions, and the External-Secrets/OpenBao plumbing. issue-core only
**contributes** its `Application` manifest + workload manifests into the
agreed GitOps source. T02 is therefore a cross-repo dependency, not
issue-core work — see handoff to railiance-platform.
- **Backend = cluster Gitea (markitect)** (operator decision 2026-06-19).
Ingested tasks route to the existing Gitea backend; no new Postgres/PVC.
- **Secret management = OpenBao.** `ISSUE_CORE_API_KEY` is a shared ingestion
key injected from OpenBao on both issue-core and the activity-core worker.
ops-warden does **not** vend it (see
`~/ops-warden/wiki/playbooks/activity-core-issue-sink.md`). Coordinate the
canonical path with `railiance-platform` (`issue-core-ingestion-api-key`).
- **Image delivery = container registry, not side-load.** GitOps requires a
pullable image tag in a registry the cluster can reach (the Coulomb Gitea
container registry); side-loading defeats declarative reproducibility.
## Open questions
- **GitOps source repo.** Resolved by `railiance-platform` as part of the
bootstrap (T02 dependency): where issue-core's `Application` + manifests are
expected to live (its own `issue-core/k8s/` vs. a platform GitOps repo) and
the AppProject/app-of-apps convention to follow.
- **Registry path & pull secret.** Confirm the Coulomb Gitea container registry
path and the cluster pull-secret posture (tracked in `railiance-forge`
container-registry docs and `railiance-apps-WP-0004` I03).
---
## Container image published to a pullable registry
```task
id: ISSUE-WP-0003-T01
status: in_progress
priority: high
```
**Goal.** A reproducible, registry-hosted image ArgoCD-managed pods can pull.
- [x] Add `Dockerfile` installing `issue-core[api]>=0.2,<0.3` from the Gitea
PyPI index (with explicit PyPI primary index). Entrypoint renders
`backends.json` then `issue serve --host 0.0.0.0 --port 8765`.
- [x] Local build succeeds; `docker run` + `GET /healthz` returns 200.
- [ ] Build and **push to the Coulomb Gitea container registry** (confirm path
per Open questions); tag `0.2.0`.
- [ ] Configure the cluster pull secret so `issue-core` namespace pods can pull.
- [ ] Verify: `POST /issues/` smoke; pushed tag pullable from the cluster.
## ArgoCD bootstrap (railiance-platform dependency) + issue-core Application
```task
id: ISSUE-WP-0003-T02
status: wait
priority: high
```
**Owner split.** ArgoCD bootstrap is **railiance-platform's** (operator
decision 2026-06-19): repo registration in ArgoCD, AppProject/app-of-apps
convention, and the agreed GitOps source layout. This task is `wait` on that
handoff. issue-core's part is to **contribute** the `Application` manifest +
workload manifests into the layout platform defines.
- **(railiance-platform)** Register the GitOps source repo (repository Secret +
creds); define AppProject for cluster services; publish the source-repo/path
convention and sync policy.
- **(issue-core)** Once the convention is known: author the `issue-core` ArgoCD
`Application` manifest (source repo/path/revision → destination `issue-core`
namespace) per the platform layout.
- Verify: `kubectl get applications -n argocd` shows `issue-core`
Synced/Healthy; ArgoCD reconciles a trivial manifest change.
## Kubernetes manifests (namespace, Deployment, Service) in GitOps source
```task
id: ISSUE-WP-0003-T03
status: in_progress
priority: high
```
**Goal.** Declarative manifests in the GitOps source repo, synced by T02.
- [x] `k8s/railiance/` Kustomize bundle (namespace via ArgoCD
`CreateNamespace=true`).
- [x] Deployment: registry image tag `0.2.0`; port 8765; `/healthz` probes;
resource requests/limits; env from ExternalSecret (T04) and ConfigMap (T05).
- [x] Service: ClusterIP on **8765** as
`issue-core.issue-core.svc.cluster.local`.
- [ ] Verify: ArgoCD syncs the manifests; Pod Ready; `/healthz` 200 from a debug
pod (blocked on T01 push + T02 bootstrap + T04 secrets).
## OpenBao secret: ISSUE_CORE_API_KEY
```task
id: ISSUE-WP-0003-T04
status: todo
priority: high
```
**Goal.** The shared ingestion key delivered to both sides from OpenBao.
- Provision `ISSUE_CORE_API_KEY` in OpenBao at the canonical path (coordinate
with `railiance-platform`; catalog id `issue-core-ingestion-api-key`).
- Deliver into the issue-core Deployment (T03) and the activity-core worker
(T06) with the **same** value (External Secrets / Bao injector — match the
cluster's established mechanism).
- Never write the value to Git, manifests, State Hub, or logs.
- Verify: both pods resolve a non-empty key; auth round-trip (401 without,
201 with).
## In-cluster backend config (cluster Gitea / markitect)
```task
id: ISSUE-WP-0003-T05
status: in_progress
priority: medium
```
**Goal.** issue-core's `backends.json` inside the cluster points `default` at
the cluster Gitea (markitect) backend.
- [x] ConfigMap `issue-core-backends` with in-cluster Gitea URL
(`gitea-http.default.svc.cluster.local:3000`); token sentinel `__FROM_ENV__`.
- [x] `docker-entrypoint.sh` renders `~/.config/issue-tracker/backends.json`
from `BACKENDS_TEMPLATE` + `GITEA_BACKEND_TOKEN` at startup.
- [ ] Verify: a `POST /issues/` creates a real Gitea issue and returns
`issue_url` (blocked on T04 secrets + in-cluster deployment).
## Wire activity-core to the live service
```task
id: ISSUE-WP-0003-T06
status: todo
priority: high
```
**Goal.** activity-core emits to the live issue-core Service.
- Fix `activity-core/k8s/railiance/20-runtime.yaml`:
`ISSUE_CORE_URL` port `8010 -> 8765`; flip `ISSUE_SINK_TYPE` `null -> rest`
once issue-core is Ready.
- Inject `ISSUE_CORE_API_KEY` into the activity-core worker from the same
OpenBao secret (T04).
- **Contract gap:** issue-core requires `triggering_event_id` as a UUID;
activity-core cron paths may send non-UUID keys (e.g. `"scheduled"`).
Event-driven emission with real event UUIDs works (the `str()` guard in
`issue_sink.py`, commit f05c56e, handles UUID objects). Align schemas before
enabling `rest` for cron-triggered rules.
- Verify: an activity-core run emits a task that lands in cluster Gitea via
issue-core.
## End-to-end verification + GitOps runbook
```task
id: ISSUE-WP-0003-T07
status: todo
priority: medium
```
**Goal.** Confirm the deployed service is healthy and document the new path.
- ArgoCD Application Synced/Healthy; issue-core Pod Ready; Service reachable
cluster-internal.
- activity-core → issue-core emission returns 201 and creates a Gitea issue.
- Document the GitOps runbook (image build/push, ArgoCD sync, secret rotation,
rollback) in `docs/`.
- Emit an `add_progress_event` milestone to the hub on completion.
---
## See also
- `ISSUE-WP-0002` — Gitea PyPI publication (packaging precursor, finished).
- `railiance-apps-WP-0004` I03 — issue-core packaging/image enablement notes.
- `railiance-forge` — Gitea container registry docs.
- `activity-core/docs/issue-core-emission-boundary.md` — emission contract.
- `activity-core/k8s/railiance/README.md` — the imperative pattern being
superseded for this service.
- `~/ops-warden/wiki/playbooks/activity-core-issue-sink.md` — key routing.