Runbooks
Each runbook assumes you hold a valid kubeconfig (from the secure channel —
never from this repo) and that connectivity checks stay read-only. Substitute
<bsn> (e.g. app1001) and <env> (e.g. dev) as needed.
1. Check app health
- List pods in the app’s namespace:
kubectl -n <bsn>-<env> get pods. - All pods
RunningandREADY 1/1→ healthy. - Cross-check Argo CD: the Application for the app shows
HealthyandSynced(see runbook 8). - If anything is not Running, jump to runbook 7.
2. Re-sync an Argo CD Application
- Identify the Application name (convention: derived from BSN; check
inventory.json→workload_cluster.apps). - Hard refresh to detect drift:
argocd app get <app> --hard-refresh(or trigger refresh via the API/CLI you have). - Sync:
argocd app sync <app>. - Watch until
Health: Healthy,Sync: Synced. - If sync fails, do not retry blindly — read the sync result message, then see runbook 10 (roll back) if the new state is bad.
3. Read app logs
kubectl -n <bsn>-<env> logs deploy/<name> --tail=200for the current logs.- For a crashed pod:
kubectl -n <bsn>-<env> logs <pod> --previous. - For the
log-simnamespace, logs are also forwarded: query Loki from Grafana Explore (local Loki for the tooling plane + forwarded streams, Grafana Cloud Loki for the aggregator’s output) with LogQL such as{log_type="application"}. Remember the local Loki holds aggregator-path events twice by design — count distinct events, not raw lines (see observability.md). - Other CRC namespaces are not forwarded yet; their logs are per-pod and ephemeral. Capture anything you need into the issue thread before the pod is recycled.
4. Rotate a Harbor robot account
Harbor is live on the tooling plane (
rhels/team#107). Creating or rotating robot-account credentials remains a human-only secrets step — see agent-protocol.md.
- In Harbor, create a new robot account with the minimum scopes the CI needs
(
push/pullon the app’s project). - Update the CI credential in the secure store (human step — see agent-protocol.md: human-only secrets steps).
- Trigger a test build; confirm push succeeds.
- Delete the old robot account only after the new credential is proven in CI.
- Record the rotation date on the tracking issue; never record the credential.
5. Add a new app appDDDD
- Pick the next free BSN (
appDDDD, 7 chars, zero-padded). - Create the private IAC repo
rhels/IAC.<bsn>(human or org admin step if you lack permissions). - Add the namespace manifest
<bsn>-dev, RBAC (admin/view per cluster type), and the ApplicationSet entry inrhels/local-k8s-gitopsfollowing the existing apps’ shape (lowercased AppProject, derived destination namespace, controller-access shim at sync-wave -1 where gated). - Open a GitHub issue with the plan; get peer review; get human authorization.
- Build, let Argo CD sync, verify Healthy/Synced, post evidence on the issue.
- Regenerate
inventory.jsonso the new app appears.
6. Rotate a secret
- Identify every consumer of the secret (CI workflows, Argo CD, app pods) and its Vault path (see vault.md for the layout).
- Create the replacement value via the owning system (human step for credentials — agents never invent or display secret values).
- Update the KV entry in Vault (agent write gate: the owner-placed token
file + the in-pod CLI — see vault.md). Consumers wired as
Secrets Operator tenants pick the new value up on the secret’s refresh
interval; roll any other consuming pods
(
kubectl -n <ns> rollout restart). - Verify consumers work with the new value (test build / app health / the health suite’s login checks where the secret is a login).
- Revoke the old value at the issuer.
- Never write the value into git, chat logs kept in the repo, or these docs.
7. Diagnose ImagePullBackOff
kubectl -n <bsn>-<env> describe pod <pod>— read theFailed to pull imageevent for the exact error.- Common causes, in order:
- Wrong image reference or tag in the IAC repo → fix the repo, let Argo CD sync.
- Registry unreachable from the cluster → check networking (runbook: private tailnet path); report if the tunnel is down.
- Missing imagePullSecret → add it in the IAC repo.
- Kyverno
require-signed-imagesis Audit today (rhels/team#107; GitOpsc5d749f586d43640a61591f0bcdb94ac04d98e0d); it reports unsigned images inPolicyReports and does not block pulls. After the Enforce flip, an unsigned image rejected at admission means the pipeline must sign the image — do not bypass the policy, fix the signing step. Do not treat Enforce as live.
- Fix forward in git; never
kubectl set imageby hand — the change must live in the IAC repo or Argo CD will revert it.
8. Check Argo CD sync status
- List Applications:
argocd app list(or the CRDs:kubectl get applications -n <argocd-namespace>). - For each app:
Sync: SyncedandHealth: Healthyis the steady state. OutOfSync→ inspect the diff (argocd app diff <app>); if the drift is unexpected, re-sync (runbook 2) or fix the repo.Degraded/Unknownhealth → treat as an incident: check pods (runbook 1), logs (runbook 3).
9. Verify inventory freshness
- Read
inventory.json: checkgenerated_atandsource. source: "live"and recent → trust it.source: "recorded"or stale → re-runpython3 inventory/generate-inventory.pyfrom the repo root; it will attempt a live read and fall back to recorded.- After regenerating, diff against the previous file and note what changed on
the relevant issue or in chat. Copy the result to
agent-docs/docs/inventory.json.
10. Roll back a bad sync
- Confirm the app is unhealthy after a sync (runbook 1) and the cause is the new state, not the platform.
- In the app’s IAC repo, revert the offending commit (
git revert), push. - Argo CD syncs the revert automatically; or force with
argocd app sync <app>. - Verify Healthy/Synced; post the revert and the evidence on the issue.
- Open a follow-up issue for the root cause — do not re-land the change without a reviewed plan.
11. Unseal Vault after a tooling-host reboot
Vault seals at every tooling-host boot; nothing is broken — this is the sequence (background: vault.md).
- Owner step (human only): on the tooling host, run the Vault bootstrap, which takes the unseal key from the owner’s Keychain. Agents never hold the unseal key and cannot do this step.
- Agent verification: check Vault status from inside the pod — the CLI
runs via the tooling cluster (
kubectl -n vault exec);sealedmust readfalse. - Check the Secrets Operator tenants recover: on CRC, the production
VaultStaticSecrets reportSYNCED=Trueagain (the two proof tenants’SYNCED=Falseis a known, separate quirk). - If an agent needs the write gate, confirm the owner has re-placed the token file; do not ask for the token in chat.
- Re-run the health suite: Vault-dependent logins and the delivery check should return to green.
12. Verify log delivery end to end
The standing proof that the pipeline delivers to both Lokis (background: observability.md).
- On-demand: from the
lab-agentopscheckout, run the suite’s single check:run_suite.py --check grafana-cloud-log-delivery(same--envthe hourly run uses). - PASS means: 10 marker events emitted, and each Loki — local and Grafana Cloud — holds all 10 by distinct sequence count.
- Raw local counts near double are expected (dual local paths), not a failure signal.
- Scheduled proof: the
log-delivery-testCronJob inlog-simruns the same check from inside CRC every 3 hours; read its latest job’s logs before digging by hand. - If it fails, reconcile per-hop counters (collector → aggregator sinks → Loki) per the trial recipe in observability.md — the hop where the distinct count shrinks is the loser.
13. A lab CronJob check failed — what now
- Identify the job run:
kubectl -n log-sim get jobsfor the delivery test; the login-test CronJob reports the same way in its namespace. - Read the failed job’s logs — the check prints per-target results; separate “the target failed” from “the test path failed” (tunnel, forwards, image) before concluding anything about the target.
- One failure is signal, not incident: the alerting rule (landing — see observability.md) fires only on the second consecutive failure.
- Cross-check with the hourly health suite’s latest report and fresh direct probes; the suite and the CronJobs measure overlapping paths and disagreeing results are themselves diagnostic.
- File/fix in git like any other lab change; never patch the CronJob in place.