Implement the A1 task from the IoT walking-skeleton roadmap:
- CRD (kube-derive): `iot.nationtech.io/v1alpha1/Deployment`, namespaced,
with `targetDevices`, `score {type, data}`, `rollout.strategy`, and a
status subresource carrying `observedScoreString`.
- Controller: `kube::runtime::Controller` + `finalizer` helper. On Apply,
writes `<device_id>.<deployment_name>` into NATS KV bucket
`desired-state` and patches `.status.observedScoreString` via
server-side apply. Skips KV write + status patch when the score is
unchanged to avoid reconcile-loop churn. On Cleanup, removes the
per-device keys before releasing the finalizer.
- CLI: `gen-crd` subcommand prints the CRD YAML from the Rust types;
`run` (default) starts the controller. `deploy/crd.yaml` is generated
by that subcommand — single source of truth, no drift.
- Deploy manifests: `deploy/operator.yaml` (Namespace, SA, ClusterRole,
ClusterRoleBinding, Deployment) and generated `deploy/crd.yaml`.
Agent fixes surfaced while aligning with the operator's key layout:
- Watch filter: was `starts_with("desired-state.<id>.")` on
`watch_all()`; bucket name is not a key prefix, so it never matched.
Now uses `bucket.watch("<id>.>")` with the NATS wildcard and handles
`Put`/`Delete`/`Purge` distinctly.
- Multi-server connect: was joining `nats.urls` with `","` into a single
malformed URL. Pass the `Vec<String>` to `ConnectOptions::connect`.
- `credentials.type` is now validated (rejects unknown discriminators)
so a v0.2 `zitadel` config doesn't silently fall back to shared creds.
Verification on feat/iot-walking-skeleton:
- cargo clippy --no-deps -D warnings: clean (agent + operator).
- cargo fmt --check: clean.
- x86_64 + aarch64 cross-compile: both build.
- podman module unit tests: pass.
76 lines
1.7 KiB
YAML
76 lines
1.7 KiB
YAML
apiVersion: v1
|
|
kind: Namespace
|
|
metadata:
|
|
name: iot-system
|
|
---
|
|
apiVersion: v1
|
|
kind: ServiceAccount
|
|
metadata:
|
|
name: iot-operator
|
|
namespace: iot-system
|
|
---
|
|
apiVersion: rbac.authorization.k8s.io/v1
|
|
kind: ClusterRole
|
|
metadata:
|
|
name: iot-operator
|
|
rules:
|
|
- apiGroups: ["iot.nationtech.io"]
|
|
resources: ["deployments"]
|
|
verbs: ["get", "list", "watch", "patch", "update"]
|
|
- apiGroups: ["iot.nationtech.io"]
|
|
resources: ["deployments/status"]
|
|
verbs: ["get", "patch", "update"]
|
|
- apiGroups: ["iot.nationtech.io"]
|
|
resources: ["deployments/finalizers"]
|
|
verbs: ["update"]
|
|
---
|
|
apiVersion: rbac.authorization.k8s.io/v1
|
|
kind: ClusterRoleBinding
|
|
metadata:
|
|
name: iot-operator
|
|
roleRef:
|
|
apiGroup: rbac.authorization.k8s.io
|
|
kind: ClusterRole
|
|
name: iot-operator
|
|
subjects:
|
|
- kind: ServiceAccount
|
|
name: iot-operator
|
|
namespace: iot-system
|
|
---
|
|
apiVersion: apps/v1
|
|
kind: Deployment
|
|
metadata:
|
|
name: iot-operator
|
|
namespace: iot-system
|
|
labels:
|
|
app.kubernetes.io/name: iot-operator
|
|
spec:
|
|
replicas: 1
|
|
selector:
|
|
matchLabels:
|
|
app.kubernetes.io/name: iot-operator
|
|
template:
|
|
metadata:
|
|
labels:
|
|
app.kubernetes.io/name: iot-operator
|
|
spec:
|
|
serviceAccountName: iot-operator
|
|
containers:
|
|
- name: operator
|
|
image: ghcr.io/nationtech/iot-operator-v0:latest
|
|
imagePullPolicy: IfNotPresent
|
|
env:
|
|
- name: NATS_URL
|
|
value: nats://nats.iot-system.svc.cluster.local:4222
|
|
- name: KV_BUCKET
|
|
value: desired-state
|
|
- name: RUST_LOG
|
|
value: info
|
|
resources:
|
|
requests:
|
|
cpu: 50m
|
|
memory: 64Mi
|
|
limits:
|
|
cpu: 500m
|
|
memory: 256Mi
|