agent-platform release v4.10.0
Added
- The chart provisions Agent Substrate’s snapshot store on CAPZ (giantswarm/agent-platform#417, giantswarm/giantswarm#37756):
kagent.harness.snapshotStore.crossplane.provider: capz renders, through the connectivity chart, the Azure storage Account (TLS-only, no public blob access, seven-day soft delete; never deleted by Crossplane, kept by Helm), the blob Container with a lifecycle ManagementPolicy (capz.lifecycleDays, 30 by default), a UserAssignedIdentity with a FederatedIdentityCredential against the cluster’s OIDC issuer for the s3proxy ServiceAccount and a RoleAssignment (Storage Blob Data Contributor, scoped to the container; provider-kubernetes Objects bridge the identity’s generated principalId, clientId and tenantId the way the Loki chart does), and the s3proxy façade (kagent.harness.snapshotStore.s3proxy): Substrate speaks S3 only, so a stateless two-replica substrate-s3proxy Deployment in the release namespace (the gsoci mirror of gaul/s3proxy, non-root on 8080 behind a Service on 80, a PodDisruptionBudget, network policies in both flavours admitting only atelet and ate-api-server, Substrate’s egress to it next to the bundled store’s) translates aws-v4 — a per-installation key pair generated once and kept, the Secret substrate-s3proxy in the release namespace and in ate-system — to azureblob as the identity (DefaultAzureCredential, azure.workload.identity/use). The meta chart derives kagent.harness.snapshotLocation = s3://<capz.containerName>/<prefix> and hands the substrate release AWS_ENDPOINT_URL, AWS_S3_USE_PATH_STYLE, AWS_REGION and the key pair on atelet.extraEnv and ateApiServer.extraEnv next to an installation’s own entries. The façade also runs alone (s3proxy.enabled: true, the Crossplane block off) in front of an account provisioned by hand or a lab’s Azurite (s3proxy.azure.*, an account key). Guards: the provider, each provider’s inputs, the account name’s shape, the endpoint’s scheme, the bundled store on, an own S3 variable, a disagreeing snapshotLocation or s3proxy.azure.*. Off and aws render byte-identically to 4.9.x; make verify-substrate-store extended, ci/test-substrate-store-capz-values.yaml, tests/verify-components.py pins the façade’s image to gsoci and the BOM; hack/agentlab/ carries the lab’s Azurite fixture and overlay. Measured in agentlab against Azurite: one 2 GiB object, in-cluster aws-cli: PUT 9.8 s (209 MB/s), GET 7.4 s (276 MB/s) through the façade into Azurite, against PUT 5.8 s (356 MB/s), GET 3.0 s (684 MB/s) on the bundled RustFS — the façade’s hop costs about 1.7× on writes and 2.5× on reads in the lab; an actor’s golden snapshot restores from the store in ~0.24 s (manifest 11 ms, download 37 ms, restore 190 ms).
Changed
- The WorkerPool is one CPU feature set, not one architecture (giantswarm/agent-platform#429):
kagent.substrateWorkerPool.template.nodeSelector pinned the pool to kubernetes.io/arch: amd64 and the values comment, the READMEs and UPGRADE.md said an actor’s golden snapshot is architecture-specific. The constraint is narrower: the snapshot is a gVisor checkpoint, and gVisor restores it only on a host whose CPU offers every feature the checkpoint recorded — two amd64 nodes of different vendors are not compatible. On the first 4.x installation (2026-09-13) Karpenter had mixed AMD c5a and Intel r6i nodes under the pool; the sre-agent golden snapshot was taken on an AMD worker, every restore on the Intel worker failed incompatible FeatureSet: missing features: cmp_legacy, cr8_legacy, sse4a, misalignsse, topoext, mmxext, fxsr_opt (all AMD-only CPUID flags) every 30 s, and the portal turn surfaced as actor "ai-…" request timed out — the actor never recovers because it stays placed on that worker. The chart’s default stays kubernetes.io/arch: amd64 (the coarse case, and the only label every provider carries); the values comment, the chart README, the README’s “Scheduling” paragraph and UPGRADE.md now name the feature-set constraint and the pin per provider: on CAPA the vendor karpenter.k8s.aws/instance-cpu-manufacturer: amd|intel (Karpenter consolidation may replace a node with another vendor’s at any time) and, where a generation change must be ruled out too, the family karpenter.k8s.aws/instance-family; on CAPZ and the other providers the node pool (giantswarm.io/machine-pool) or node.kubernetes.io/instance-type; and the recognition recipe (the worker log line, kubectl get nodes -L <the label> showing two values under the pool). No render change. Installations act: a CAPA installation whose workers Karpenter provisions adds the vendor its golden snapshots were taken on to kagent.substrateWorkerPool.template.nodeSelector — the fleet template carries karpenter.k8s.aws/instance-cpu-manufacturer: amd for every CAPA installation on the 4.x line (shared-configs, agentPlatform.workerPoolCpuManufacturer), so a Giant Swarm installation acts only to tighten or to opt out. The durable fix is Substrate’s — a restore placed only on a worker whose CPU covers the snapshot’s feature set (giantswarm/giantswarm#37742 row 12).