diff --git a/.github/workflows/keep-ci.yml b/.github/workflows/keep-ci.yml index e10b6a6..4a6e208 100644 --- a/.github/workflows/keep-ci.yml +++ b/.github/workflows/keep-ci.yml @@ -39,6 +39,7 @@ jobs: set -euo pipefail cargo fmt --all -- --check cargo clippy --locked --workspace --all-targets --all-features -- -D warnings + cargo test --locked -p keep --features operator --test operator cargo test --locked --workspace - name: Release contract shell: bash diff --git a/crates/keep/tests/operator.rs b/crates/keep/tests/operator.rs index aebdd17..92cf1d6 100644 --- a/crates/keep/tests/operator.rs +++ b/crates/keep/tests/operator.rs @@ -250,3 +250,18 @@ fn rustls_provider_install_is_idempotent() { install_default_crypto_provider(); install_default_crypto_provider(); } + +/// R6 (#14) — the committed `k8s/operator/crd.yaml` is byte-equal to the generator output. +#[test] +fn committed_crd_matches_generator() { + let _ = include_str!("../../../k8s/operator/crd.yaml"); + let crd_path = + std::path::Path::new(env!("CARGO_MANIFEST_DIR")).join("../../k8s/operator/crd.yaml"); + let committed = std::fs::read(&crd_path).expect("read committed k8s/operator/crd.yaml"); + let generated = cli_std::artifact::ensure_trailing_newline(&crd_yaml()); + assert_eq!( + committed.as_slice(), + generated.as_bytes(), + "k8s/operator/crd.yaml does not match generator output; regenerate with:\n cargo run -p keep --features operator -- k8s crd render --out k8s/operator/crd.yaml" + ); +} diff --git a/k8s/operator/crd.yaml b/k8s/operator/crd.yaml index 46f7562..aebf581 100644 --- a/k8s/operator/crd.yaml +++ b/k8s/operator/crd.yaml @@ -63,6 +63,10 @@ spec: description: Per-node engine shard count (`KEEP_SHARDS`) for multi-core scaling — distinct from `shardCount` (the cluster-routing fan-out). Defaults to 256. type: integer minimum: 0.0 + evictionPolicy: + description: Memory eviction policy (`KEEP_EVICTION_POLICY`, default `allkeys-lru`). + nullable: true + type: string graceSecs: default: 30 description: Graceful drain window on SIGTERM (seconds); tracks `terminationGracePeriodSeconds`. Defaults to 30. @@ -77,6 +81,14 @@ spec: description: Log level (`trace|debug|info|warn|error`). Defaults to `info`. nullable: true type: string + maxDiskBytes: + description: Optional maximum disk quota for `/data/blobs/` (`KEEP_MAX_DISK_BYTES`, e.g. `8Gi`). + nullable: true + type: string + maxmemory: + description: Optional explicit memory cap (`KEEP_MAXMEMORY`, e.g. `4Gi` or bytes). When unset, the pod auto-detects its cgroup memory limit and applies `KEEP_MAXMEMORY_RATIO` (default 0.80). + nullable: true + type: string replicasPerShard: default: 1 description: Starting/minimum members per shard. With startup-static raft-runtime membership this is also the fixed desired value; a future membership controller may plan whole replica layers above this floor. @@ -99,6 +111,11 @@ spec: default: 1 type: integer minimum: 0.0 + spillThresholdBytes: + description: Byte size threshold above which `Bytes` payloads spill to `/data/blobs/` (`KEEP_SPILL_THRESHOLD_BYTES`). + type: integer + minimum: 0.0 + nullable: true storage: default: 10Gi description: Per-pod durable disk-tier (WAL + snapshots) PVC size. Defaults to `10Gi`.