feat: initial harness spec v1.0.0 + reference template
Adds the normative contract every platform agent must conform to, plus a
working reference template under template/ that ticks every box out of
the box.
Spec is derived from the production agents shipped in
cjot-backstage-az/agents/ on branch sandbox/jonathan:
- agent-registry, agent-factory, decomposer, discovery-agent,
golden-path, modernization-factory, modernization-factory-v2,
policy-transformer, scaffold-agent, support-intake-agent, the-watcher
Contents:
- SPEC.md normative contract (13 sections + conformance checklist)
- CHANGELOG.md spec versioning (1.0.0)
- docs/
registration.md self-registration with the agent-gateway
observability.md OTel logs/metrics/traces wiring
manifest.md /.well-known/agent.json schema + AgentSkill
serialisation (pydantic vs protobuf)
kubernetes.md k8s deployment shape, mandatory cross-refs,
per-namespace agents, resource sizing
deployment.md .image-version, ACR build, deploy scripts, rollback
deviations.md tracked debts against the spec
- template/
.env.local.example, .gitignore, .image-version (0.1.0),
Dockerfile (python:3.12-slim, non-root UID 1001, HEALTHCHECK),
requirements.txt (harness floor + optional LLM stack),
app/agent.py (Starlette entry, lifespan + self-registration,
defensive _skill_dict for pydantic vs protobuf),
app/logging_setup.py (canonical OTel log bridge — copy verbatim),
app/metrics.py (meter + example counter/histogram),
app/skills.py (AGENT_CONFIG + AgentSkill list),
k8s/configmap.yaml + deployment.yaml (Deployment + Service,
OTel annotations + 6-var env block,
agents-sa + agents-kv-spc bindings),
scripts/deploy.sh (auto-bump + az acr build + apply + rollout),
scripts/full-deploy.sh (preflight + deploy + post-deploy smoke),
scripts/deploy-local.sh (docker/podman + .env.local)
This commit is contained in:
133
docs/deployment.md
Normal file
133
docs/deployment.md
Normal file
@@ -0,0 +1,133 @@
|
||||
# Build & deployment
|
||||
|
||||
> Normative requirements: [SPEC.md §10](../SPEC.md#10-build--deployment-must)
|
||||
|
||||
Every agent ships three deploy scripts under `scripts/` that wrap the same build → push → apply → roll-out pipeline at three layers of fidelity.
|
||||
|
||||
## `.image-version`
|
||||
|
||||
A single-line file at the agent root:
|
||||
|
||||
```
|
||||
1.0.3
|
||||
```
|
||||
|
||||
Conventions:
|
||||
|
||||
- Semver only.
|
||||
- **MUST** match `OTEL_RESOURCE_ATTRIBUTES`' `service.version` value at all times.
|
||||
- Bumped whenever a code change ships. The patch number is auto-incremented by `scripts/deploy.sh` when no `--tag` is given.
|
||||
- Pure image-version-only commits are reserved for *redeploy reruns* (e.g. rebuilding without code changes to refresh dependencies). They **SHOULD** be rare.
|
||||
|
||||
## ACR
|
||||
|
||||
All images are pushed to:
|
||||
|
||||
```
|
||||
bstagecjotdevacr.azurecr.io/<agent-name>:<X.Y.Z>
|
||||
bstagecjotdevacr.azurecr.io/<agent-name>:latest
|
||||
```
|
||||
|
||||
Both tags are pushed every build so that pinning to `:latest` works for sandbox/dev clusters and pinning to `:<version>` works for prod clusters.
|
||||
|
||||
## `scripts/deploy.sh`
|
||||
|
||||
The fast path. Used for routine redeploys.
|
||||
|
||||
```bash
|
||||
#!/bin/bash
|
||||
set -euo pipefail
|
||||
SCRIPT_DIR=$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)
|
||||
|
||||
ACR_NAME="bstagecjotdevacr"
|
||||
APP_NAME="<agent-name>"
|
||||
NAMESPACE="agents"
|
||||
VERSION_FILE="${SCRIPT_DIR}/../.image-version"
|
||||
TAG=""
|
||||
|
||||
while [[ "$#" -gt 0 ]]; do
|
||||
case $1 in
|
||||
--tag) TAG="$2"; shift ;;
|
||||
*) echo "unknown arg: $1"; exit 2 ;;
|
||||
esac
|
||||
shift
|
||||
done
|
||||
|
||||
if [ -z "$TAG" ]; then
|
||||
CURRENT=$(cat "$VERSION_FILE" 2>/dev/null || echo "1.0.0")
|
||||
IFS='.' read -r MAJOR MINOR PATCH <<< "$CURRENT"
|
||||
PATCH=$((PATCH + 1))
|
||||
TAG="${MAJOR}.${MINOR}.${PATCH}"
|
||||
echo "$TAG" > "$VERSION_FILE"
|
||||
fi
|
||||
|
||||
IMAGE="${ACR_NAME}.azurecr.io/${APP_NAME}:${TAG}"
|
||||
echo "Building ${IMAGE} ..."
|
||||
az acr build --registry "$ACR_NAME" \
|
||||
--image "${APP_NAME}:${TAG}" \
|
||||
--image "${APP_NAME}:latest" \
|
||||
"${SCRIPT_DIR}/.."
|
||||
|
||||
echo "Applying manifests ..."
|
||||
kubectl apply -f "${SCRIPT_DIR}/../k8s/configmap.yaml"
|
||||
kubectl apply -f "${SCRIPT_DIR}/../k8s/deployment.yaml"
|
||||
|
||||
kubectl -n "$NAMESPACE" rollout status "deployment/${APP_NAME}" --timeout=180s
|
||||
```
|
||||
|
||||
Behaviour:
|
||||
|
||||
- Auto-bumps the patch on `.image-version` if `--tag` is not given.
|
||||
- Rebuilds and re-pushes both `:<tag>` and `:latest`.
|
||||
- Applies `configmap.yaml` and `deployment.yaml` in order.
|
||||
- Blocks until the rollout completes (or fails) within 180 s.
|
||||
|
||||
## `scripts/full-deploy.sh`
|
||||
|
||||
The "first time" path. Adds preflight + post-deploy smoke.
|
||||
|
||||
Preflight (refuse to continue if any check fails):
|
||||
|
||||
- `kubectl` available and pointed at a cluster.
|
||||
- `az` available and logged in.
|
||||
- Target namespace exists.
|
||||
- ServiceAccount `agents-sa` exists in the namespace.
|
||||
- SecretProviderClass `agents-kv-spc` exists in the namespace.
|
||||
|
||||
After `rollout status` succeeds:
|
||||
|
||||
- `kubectl exec deployment/<agent> -- curl -fs http://localhost:<port>/health`
|
||||
- `kubectl exec deployment/<agent> -- curl -fs http://localhost:<port>/.well-known/agent.json | jq -e '.name'`
|
||||
|
||||
Failures abort with a non-zero exit code so CI can block.
|
||||
|
||||
## `scripts/deploy-local.sh`
|
||||
|
||||
For dev loop on a workstation without cluster access.
|
||||
|
||||
- Builds the image locally with `docker` or `podman`.
|
||||
- Reads `.env.local` (sibling of `.env.local.example`).
|
||||
- Runs the container with `-p <port>:<port>` and `--env-file`.
|
||||
|
||||
Useful for iterating on prompt changes without touching the cluster.
|
||||
|
||||
## CI integration
|
||||
|
||||
The platform's GitHub Actions / Gitea Actions runners invoke `scripts/deploy.sh --tag $GITHUB_SHA` (or similar) when an agent's directory changes on the deploy branch. Pull requests trigger a build-only run that pushes a `:<sha>` tag without applying manifests.
|
||||
|
||||
## Rolling back
|
||||
|
||||
There is no automated rollback. To roll back:
|
||||
|
||||
```bash
|
||||
kubectl -n agents set image deployment/<agent-name> \
|
||||
<agent-name>=bstagecjotdevacr.azurecr.io/<agent-name>:<previous-version>
|
||||
|
||||
kubectl -n agents rollout status deployment/<agent-name> --timeout=180s
|
||||
```
|
||||
|
||||
If the rollback ships a different `service.version`, also revert `OTEL_RESOURCE_ATTRIBUTES` in the ConfigMap or the dashboards will misattribute. Prefer a forward-fix (new patch version) over a rollback whenever possible.
|
||||
|
||||
## Re-registration after redeploy
|
||||
|
||||
The agent re-registers with the gateway on every pod start. No manual step is required after a rollout. If the gateway pod is also restarting at the same time, expect 1–2 `WARNING`s in the agent log before registration succeeds — see [registration.md](registration.md#failure-modes--resolution).
|
||||
Reference in New Issue
Block a user