Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0

### Fixed

- **The Substrate egress gateway's ephemeral storage is bounded and kagent's oauth2-proxy runs the gsoci copy of its image** (giantswarm/agent-platform#880). Kyverno's `require-emptydir-requests-and-limits` reported `Deployment ate-system/atenet-egress` (its `drain-signal` emptyDir unbounded, `agentgateway` and `ext-proc` without resources) and `restrict-image-registries` reported `kagent/kagent-oauth2-proxy` (`quay.io/oauth2-proxy/oauth2-proxy`, the subchart's default). `substrate.atenetEgress` carries ephemeral-storage requests `16Mi` and limits `256Mi` for both containers and a `16Mi` `sizeLimit` for the emptyDir, CPU and memory left to the installation. The Substrate line renders the bound from those keys from the release `agent-platform.substrate.egressStorageFloor` names (1.8.0, giantswarm/substrate#255); an older release prunes them, so below that floor the substrate HelmRelease carries one kustomize strategic-merge patch (a Flux postRenderer) that merges the same bound by name into the Deployment the release renders — the names are upstream's canonical `ate-system` render and the range confines one minor, so a Substrate patch keeps them, and a re-pin past the floor retires the patch; a null key bounds nothing. `kagent.oauth2-proxy.image` pins `gsoci.azurecr.io/giantswarm/oauth2-proxy:v7.15.5`, the mirror of the subchart's default at the same digest. `make verify-substrate-egress-ephemeral-storage` (the floor's chart with the forwarded values passes the rule with the patch and fails it without, the floor is not stale, nulls render no patch) and `make verify-kagent-oauth2-proxy-image` (the pin is the gsoci copy of the subchart the kagent floor bundles and is published).
- **`mistral-small-4` serves text only** (giantswarm/agent-platform#591, giantswarm/llm-d#37). The shipped preset crash-looped before serving on `llm-d-cuda` v0.8.0: the image's vLLM lacks `fetch_images` on its Mistral image processor, which the image's transformers calls during multimodal profiling. The preset now carries `--limit-mm-per-prompt.image=0` (the dotted form; the JSON form's quotes do not survive the llm-d container's shell wrapper) and no longer declares `vision`; a request with an image is refused by the engine. Image inputs return once the runtime's vLLM carries vllm-project/vllm#45180.

### Added
Expand Down
14 changes: 14 additions & 0 deletions Makefile.custom.mk
Original file line number Diff line number Diff line change
Expand Up @@ -4201,6 +4201,20 @@ verify-substrate-images: ## Assert the substrate chart's third-party images (gia
@python3 tests/verify-substrate-images.py $(CHART_DIR)
@echo "ok: $@"

.PHONY: verify-substrate-egress-ephemeral-storage
verify-substrate-egress-ephemeral-storage: ## Assert the Substrate egress gateway's ephemeral storage is bounded on the release components.substrate.versionRange pins (giantswarm/agent-platform#880, Kyverno's require-emptydir-requests-and-limits): the substrate chart at the range's floor, rendered with the values the meta chart forwards, passes the rule on its atenet-egress pod template — by itself from the release agent-platform.substrate.egressStorageFloor names (the forwarded substrate.atenetEgress keys), below it with the one kustomize strategic-merge patch the substrate HelmRelease's postRenderers carry merged by name (every container and volume the patch names rendered by the release; the chart's own render fails the rule there, so the floor is not stale); the three knobs null render no patch; the forwarded block carries values.yaml's substrate.atenetEgress verbatim. Network: gsoci.azurecr.io. Needs PyYAML.
@echo "====> $@ ($(CHART_DIR))"
@python3 -c 'import yaml' 2>/dev/null || { echo "FAIL: PyYAML is not installed (apt: python3-yaml, pip: pyyaml)"; exit 1; }
@python3 tests/verify-substrate-egress-ephemeral-storage.py $(CHART_DIR)
@echo "ok: $@"

.PHONY: verify-kagent-oauth2-proxy-image
verify-kagent-oauth2-proxy-image: ## Assert kagent's oauth2-proxy runs the gsoci copy of its subchart's default image (giantswarm/agent-platform#880, the fleet's restrict-image-registries): kagent.oauth2-proxy.image reaches the kagent HelmRelease verbatim and names gsoci.azurecr.io, giantswarm/<the default's image name> and v<appVersion> of the oauth2-proxy subchart the kagent chart at the range's floor bundles — so a kagent re-pin that bumps the subchart moves the pin — and the copy is published. Network: gsoci.azurecr.io. Needs PyYAML.
@echo "====> $@ ($(CHART_DIR))"
@python3 -c 'import yaml' 2>/dev/null || { echo "FAIL: PyYAML is not installed (apt: python3-yaml, pip: pyyaml)"; exit 1; }
@python3 tests/verify-kagent-oauth2-proxy-image.py $(CHART_DIR)
@echo "ok: $@"

.PHONY: verify-scenarios
verify-scenarios: ## Assert the ATS scenario inputs (tests/ats/scenarios.py) and the `make e2e` values overlay (tests/e2e_overlay.py) offline: the kind and eks defaults, every refusal naming its variable, the derived muster base URL, the base-URL --set, and the overlay's shapes.
@echo "====> $@"
Expand Down
8 changes: 8 additions & 0 deletions helm/agent-platform/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -851,6 +851,9 @@ The map is merged into each component's own `nodeSelector` (`muster.nodeSelector
| kagent.otel.logs.enabled | string | `"auto"` | |
| kagent.oauth2-proxy.enabled | bool | `false` | |
| kagent.oauth2-proxy.fullnameOverride | string | `"kagent-oauth2-proxy"` | |
| kagent.oauth2-proxy.image.registry | string | `"gsoci.azurecr.io"` | |
| kagent.oauth2-proxy.image.repository | string | `"giantswarm/oauth2-proxy"` | |
| kagent.oauth2-proxy.image.tag | string | `"v7.15.5"` | |
| kagent.oauth2-proxy.namespaceOverride | string | `"kagent"` | |
| kagent.oauth2-proxy.redis.enabled | bool | `false` | |
| kagent.oauth2-proxy.sessionStorage.type | string | `"cookie"` | |
Expand Down Expand Up @@ -1427,6 +1430,11 @@ The map is merged into each component's own `nodeSelector` (`muster.nodeSelector
| substrate.images.rustfs | string | `"gsoci.azurecr.io/giantswarm/rustfs:1.0.0-beta.3@sha256:378642b05b7dcb4849fb77ebe6aca4ced1c3f66e7e504247df95a5c9018d3358"` | |
| substrate.images.awsCli | string | `"gsoci.azurecr.io/giantswarm/aws-cli:2.17.0@sha256:643507c10ada7964ca6157b3d799f030b90577643da9955d319a77399ed80d73"` | |
| substrate.images.pause | string | `"gsoci.azurecr.io/giantswarm/pause:3.10.2@sha256:f548e0e8e3dc1896ca956272154dde3314e8cc4fde0a57577ee9fa1c63f5baf4"` | |
| substrate.atenetEgress.resources.requests.ephemeral-storage | string | `"16Mi"` | |
| substrate.atenetEgress.resources.limits.ephemeral-storage | string | `"256Mi"` | |
| substrate.atenetEgress.extProc.resources.requests.ephemeral-storage | string | `"16Mi"` | |
| substrate.atenetEgress.extProc.resources.limits.ephemeral-storage | string | `"256Mi"` | |
| substrate.atenetEgress.drainSignal.sizeLimit | string | `"16Mi"` | |
| substrate.atelet.storageBackend | string | `"s3"` | |
| substrate.atelet.nodeSelector | object | `{}` | |
| substrate.atelet.tolerations | list | `[]` | |
Expand Down
51 changes: 51 additions & 0 deletions helm/agent-platform/templates/_helpers.tpl
Original file line number Diff line number Diff line change
Expand Up @@ -993,6 +993,57 @@ Usage: include "agent-platform.substrate.workerPoolAteletAffinity" $root | fromJ
{{- end -}}
{{- end -}}

{{/*
The first release of the Substrate line whose chart bounds the egress
gateway's ephemeral storage itself: atenetEgress.resources,
atenetEgress.extProc.resources and atenetEgress.drainSignal.sizeLimit
(giantswarm/substrate#255). From it on the substrate: block's keys reach the
chart; below it agent-platform.substrate.egressStoragePatch applies the same
bound as a postRenderer (giantswarm/agent-platform#880). One line, no comment
inside the define: tests/verify-substrate-egress-ephemeral-storage.py reads
the value from this file.
*/}}
{{- define "agent-platform.substrate.egressStorageFloor" -}}1.8.0{{- end -}}

{{/*
The Flux postRenderer that bounds the atenet-egress Deployment's ephemeral
storage while the pinned Substrate release predates
agent-platform.substrate.egressStorageFloor, as JSON ({} from the floor on,
and with every knob of substrate.atenetEgress null): one kustomize
strategic-merge patch on the Deployment the substrate release renders — its
containers agentgateway and ext-proc merged by name with
substrate.atenetEgress.resources and .extProc.resources, its volume
drain-signal with substrate.atenetEgress.drainSignal.sizeLimit. Those names
are upstream's canonical ate-system render, unchanged across the line's
releases, and components.substrate.versionRange confines one minor
(agent-platform.substrate.validateRange), so a Substrate patch within it keeps
them; a re-pin past the floor retires the patch and the chart renders the
bound from the forwarded keys. Kyverno's require-emptydir-requests-and-limits
skips a volume that carries a sizeLimit and otherwise requires
ephemeral-storage requests and limits on every container mounting it; the
patch sets both, as the hook Jobs do.
Usage: include "agent-platform.substrate.egressStoragePatch" $root | fromJson
*/}}
{{- define "agent-platform.substrate.egressStoragePatch" -}}
{{- $pin := regexReplaceAll "-.*$" (include "agent-platform.substrate.pinnedVersion" .) "" -}}
{{- $egress := dig "atenetEgress" dict (.Values.substrate | default dict) | default dict -}}
{{- $gateway := dig "resources" dict $egress | default dict -}}
{{- $extProc := dig "resources" dict (dig "extProc" dict $egress | default dict) | default dict -}}
{{- $sizeLimit := dig "sizeLimit" "" (dig "drainSignal" dict $egress | default dict) | default "" -}}
{{- $spec := dict -}}
{{- $containers := list -}}
{{- with $gateway }}{{ $containers = append $containers (dict "name" "agentgateway" "resources" .) }}{{ end -}}
{{- with $extProc }}{{ $containers = append $containers (dict "name" "ext-proc" "resources" .) }}{{ end -}}
{{- with $containers }}{{ $_ := set $spec "containers" . }}{{ end -}}
{{- with $sizeLimit }}{{ $_ := set $spec "volumes" (list (dict "name" "drain-signal" "emptyDir" (dict "sizeLimit" .))) }}{{ end -}}
{{- if or (not $spec) (ge ((semver $pin).Compare (semver (include "agent-platform.substrate.egressStorageFloor" .))) 0) -}}
{{- dict | toJson -}}
{{- else -}}
{{- $patch := dict "apiVersion" "apps/v1" "kind" "Deployment" "metadata" (dict "name" "atenet-egress") "spec" (dict "template" (dict "spec" $spec)) -}}
{{- dict "kustomize" (dict "patches" (list (dict "target" (dict "kind" "Deployment" "name" "atenet-egress") "patch" (toYaml $patch)))) | toJson -}}
{{- end -}}
{{- end -}}

{{/*
Fail the render when kagent.substrateWorkerPool.template would not reach the
cluster as written (giantswarm/agent-platform#457, #472). The kagent chart
Expand Down
8 changes: 8 additions & 0 deletions helm/agent-platform/templates/components.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -457,6 +457,14 @@ spec:
{{- end }}
{{- $postRenderers = append $postRenderers (dict "kustomize" (dict "patches" $patches)) }}
{{- end }}
{{- /* The egress gateway's ephemeral-storage bound, while the pinned
Substrate release predates the one whose chart renders it from the
forwarded keys (agent-platform.substrate.egressStoragePatch). */ -}}
{{- if eq $key "substrate" }}
{{- with include "agent-platform.substrate.egressStoragePatch" $root | fromJson }}
{{- $postRenderers = append $postRenderers . }}
{{- end }}
{{- end }}
{{- with $postRenderers }}
postRenderers:
{{- toYaml . | nindent 4 }}
Expand Down
43 changes: 43 additions & 0 deletions helm/agent-platform/values.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -2988,6 +2988,17 @@ kagent: # @schema skipProperties: true; additionalProperties: true
# documented above before flipping this to true.
enabled: false
fullnameOverride: kagent-oauth2-proxy
# The proxy's image: the gsoci copy of the subchart's default
# (quay.io/oauth2-proxy/oauth2-proxy at the subchart's appVersion), the
# same digest, since the fleet's restrict-image-registries admits gsoci
# only (giantswarm/agent-platform#880). The tag holds the appVersion of the
# oauth2-proxy subchart the kagent release at the range's floor bundles;
# `make verify-kagent-oauth2-proxy-image` moves it with the floor and
# requires the copy to be published.
image:
registry: gsoci.azurecr.io
repository: giantswarm/oauth2-proxy
tag: v7.15.5
# Co-locate with kagent resources so the templates ConfigMap (created in
# kagent namespace by the kagent chart) is reachable.
namespaceOverride: kagent
Expand Down Expand Up @@ -5522,6 +5533,38 @@ substrate: # @schema skipProperties: true; additionalProperties: true
# admits only gsoci boots its sandboxes; registry.k8s.io redirects every
# pull to a Google Artifact Registry host.
pause: gsoci.azurecr.io/giantswarm/pause:3.10.2@sha256:f548e0e8e3dc1896ca956272154dde3314e8cc4fde0a57577ee9fa1c63f5baf4
# The egress gateway's ephemeral storage (giantswarm/agent-platform#880).
# Kyverno's require-emptydir-requests-and-limits reports a container that
# mounts an emptyDir without a sizeLimit unless it requests and limits
# ephemeral-storage, and the Substrate chart rendered atenet-egress's two
# containers without resources and its drain-signal emptyDir unbounded. The
# line takes these three keys from the release
# agent-platform.substrate.egressStorageFloor names and renders the bound
# itself; an older release prunes them in silence, so while the range's
# floor is below it the meta chart applies the same bound as a kustomize
# patch on the substrate release's Deployment (a Flux postRenderer, built
# from these values; agent-platform.substrate.egressStoragePatch) and drops
# the patch the moment the floor reaches that release. Only the ephemeral
# storage is bounded: both containers write nothing but their logs and the
# drain marker; CPU and memory are the installation's to size, merged over
# the chart's defaults. A null key bounds nothing: the chart's own default
# from the floor on, no patch for it below.
atenetEgress:
# The agentgateway container.
resources:
requests:
ephemeral-storage: 16Mi
limits:
ephemeral-storage: 256Mi
extProc:
resources:
requests:
ephemeral-storage: 16Mi
limits:
ephemeral-storage: 256Mi
# The emptyDir the ext-proc writes its drain marker to: one small file.
drainSignal:
sizeLimit: 16Mi
atelet:
# The snapshot store's API: s3 (AWS S3 and S3-compatible stores) or gcs.
storageBackend: s3
Expand Down
67 changes: 67 additions & 0 deletions tests/golden/meta-ci-engine-off.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -778,6 +778,10 @@ spec:
name: kagent-oauth2-proxy-templates
name: custom-templates
fullnameOverride: kagent-oauth2-proxy
image:
registry: gsoci.azurecr.io
repository: giantswarm/oauth2-proxy
tag: v7.15.5
metrics:
enabled: true
serviceMonitor:
Expand Down Expand Up @@ -1330,6 +1334,20 @@ spec:
preemptionPolicy: Never
storageBackend: s3
tolerations: []
atenetEgress:
drainSignal:
sizeLimit: 16Mi
extProc:
resources:
limits:
ephemeral-storage: 256Mi
requests:
ephemeral-storage: 16Mi
resources:
limits:
ephemeral-storage: 256Mi
requests:
ephemeral-storage: 16Mi
createNamespace: false
credentialProvider:
namespacePolicies:
Expand Down Expand Up @@ -1864,6 +1882,10 @@ spec:
name: kagent-oauth2-proxy-templates
name: custom-templates
fullnameOverride: kagent-oauth2-proxy
image:
registry: gsoci.azurecr.io
repository: giantswarm/oauth2-proxy
tag: v7.15.5
metrics:
enabled: true
serviceMonitor:
Expand Down Expand Up @@ -2354,6 +2376,37 @@ spec:
dependsOn:
- name: substrate-crds
- name: agent-platform-connectivity
postRenderers:
- kustomize:
patches:
- patch: |-
apiVersion: apps/v1
kind: Deployment
metadata:
name: atenet-egress
spec:
template:
spec:
containers:
- name: agentgateway
resources:
limits:
ephemeral-storage: 256Mi
requests:
ephemeral-storage: 16Mi
- name: ext-proc
resources:
limits:
ephemeral-storage: 256Mi
requests:
ephemeral-storage: 16Mi
volumes:
- emptyDir:
sizeLimit: 16Mi
name: drain-signal
target:
kind: Deployment
name: atenet-egress
valuesFrom:
- kind: ConfigMap
name: kagent-images
Expand All @@ -2368,6 +2421,20 @@ spec:
preemptionPolicy: Never
storageBackend: s3
tolerations: []
atenetEgress:
drainSignal:
sizeLimit: 16Mi
extProc:
resources:
limits:
ephemeral-storage: 256Mi
requests:
ephemeral-storage: 16Mi
resources:
limits:
ephemeral-storage: 256Mi
requests:
ephemeral-storage: 16Mi
createNamespace: false
credentialProvider:
namespacePolicies:
Expand Down
18 changes: 18 additions & 0 deletions tests/golden/meta-default.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -990,6 +990,10 @@ spec:
name: kagent-oauth2-proxy-templates
name: custom-templates
fullnameOverride: kagent-oauth2-proxy
image:
registry: gsoci.azurecr.io
repository: giantswarm/oauth2-proxy
tag: v7.15.5
metrics:
enabled: true
serviceMonitor:
Expand Down Expand Up @@ -1542,6 +1546,20 @@ spec:
preemptionPolicy: Never
storageBackend: s3
tolerations: []
atenetEgress:
drainSignal:
sizeLimit: 16Mi
extProc:
resources:
limits:
ephemeral-storage: 256Mi
requests:
ephemeral-storage: 16Mi
resources:
limits:
ephemeral-storage: 256Mi
requests:
ephemeral-storage: 16Mi
createNamespace: false
credentialProvider:
namespacePolicies:
Expand Down
Loading
Loading