diff --git a/cmd/ate-setup/internal/demos/autoscaledworkerpool/autoscaledworkerpool.go b/cmd/ate-setup/internal/demos/autoscaledworkerpool/autoscaledworkerpool.go index e28ddbc95..85edbab42 100644 --- a/cmd/ate-setup/internal/demos/autoscaledworkerpool/autoscaledworkerpool.go +++ b/cmd/ate-setup/internal/demos/autoscaledworkerpool/autoscaledworkerpool.go @@ -47,8 +47,8 @@ func init() { DemoName: "demo-autoscaled-workerpool", Short: "A WorkerPool scaled by an HPA over custom metrics (Kind only)", Template: "demos/autoscaled-workerpool/autoscaled-workerpool.yaml.tmpl", - Deployments: []steps.TemplateRef{{Namespace: namespace, Name: "counter"}}, - ActorTemplates: []steps.TemplateRef{{Namespace: namespace, Name: "counter"}}, + Deployments: []steps.TemplateRef{{Atespace: namespace, Name: "counter"}}, + ActorTemplates: []steps.TemplateRef{{Atespace: namespace, Name: "counter"}}, }}) } diff --git a/cmd/ate-setup/internal/demos/claudemultiplex/claudemultiplex.go b/cmd/ate-setup/internal/demos/claudemultiplex/claudemultiplex.go index b532ea9a7..f56a44554 100644 --- a/cmd/ate-setup/internal/demos/claudemultiplex/claudemultiplex.go +++ b/cmd/ate-setup/internal/demos/claudemultiplex/claudemultiplex.go @@ -50,9 +50,9 @@ const ( // agents are the actors the demo template declares. Their actors are removed // before the manifests at delete time. var agents = []steps.TemplateRef{ - {Namespace: namespace, Name: "agent-luna"}, - {Namespace: namespace, Name: "agent-mars"}, - {Namespace: namespace, Name: "agent-orion"}, + {Atespace: namespace, Name: "agent-luna"}, + {Atespace: namespace, Name: "agent-mars"}, + {Atespace: namespace, Name: "agent-orion"}, } type demo struct{} diff --git a/cmd/ate-setup/internal/demos/counter/counter.go b/cmd/ate-setup/internal/demos/counter/counter.go index 5edf5be1a..7b0d50fcb 100644 --- a/cmd/ate-setup/internal/demos/counter/counter.go +++ b/cmd/ate-setup/internal/demos/counter/counter.go @@ -61,8 +61,8 @@ func init() { DemoName: "demo-counter", Short: "A counter actor exercising snapshot, resume, and atenet ingress", Template: template, - Deployments: []steps.TemplateRef{{Namespace: namespace, Name: "counter"}}, - ActorTemplates: []steps.TemplateRef{{Namespace: namespace, Name: "counter"}}, + Deployments: []steps.TemplateRef{{Atespace: namespace, Name: "counter"}}, + ActorTemplates: []steps.TemplateRef{{Atespace: namespace, Name: "counter"}}, }}) } diff --git a/cmd/ate-setup/internal/demos/egress/egress.go b/cmd/ate-setup/internal/demos/egress/egress.go index f325f89d6..768014c4c 100644 --- a/cmd/ate-setup/internal/demos/egress/egress.go +++ b/cmd/ate-setup/internal/demos/egress/egress.go @@ -28,7 +28,7 @@ func init() { DemoName: "demo-egress", Short: "Egress policy enforcement through atenet", Template: "demos/egress/egress.yaml.tmpl", - Deployments: []steps.TemplateRef{{Namespace: namespace, Name: "egress"}}, - ActorTemplates: []steps.TemplateRef{{Namespace: namespace, Name: "egress"}}, + Deployments: []steps.TemplateRef{{Atespace: namespace, Name: "egress"}}, + ActorTemplates: []steps.TemplateRef{{Atespace: namespace, Name: "egress"}}, }) } diff --git a/cmd/ate-setup/internal/demos/multitemplate/multitemplate.go b/cmd/ate-setup/internal/demos/multitemplate/multitemplate.go index 421d30367..6b8698e43 100644 --- a/cmd/ate-setup/internal/demos/multitemplate/multitemplate.go +++ b/cmd/ate-setup/internal/demos/multitemplate/multitemplate.go @@ -26,10 +26,10 @@ func init() { DemoName: "demo-multi-template", Short: "Two ActorTemplates sharing one WorkerPool", Template: "demos/multi-template/multi-template.yaml.tmpl", - Deployments: []steps.TemplateRef{{Namespace: "ate-demo-multi-template-pool", Name: "shared-pool"}}, + Deployments: []steps.TemplateRef{{Atespace: "ate-demo-multi-template-pool", Name: "shared-pool"}}, ActorTemplates: []steps.TemplateRef{ - {Namespace: "ate-demo-multi-template-counter", Name: "counter"}, - {Namespace: "ate-demo-multi-template-fspersist", Name: "fspersist"}, + {Atespace: "ate-demo-multi-template-counter", Name: "counter"}, + {Atespace: "ate-demo-multi-template-fspersist", Name: "fspersist"}, }, }) } diff --git a/cmd/ate-setup/internal/demos/parking/parking.go b/cmd/ate-setup/internal/demos/parking/parking.go index c443b519f..38cf1ebb8 100644 --- a/cmd/ate-setup/internal/demos/parking/parking.go +++ b/cmd/ate-setup/internal/demos/parking/parking.go @@ -28,7 +28,7 @@ func init() { DemoName: "demo-parking", Short: "Actor parking and unparking on a small WorkerPool", Template: "demos/parking/parking.yaml.tmpl", - Deployments: []steps.TemplateRef{{Namespace: namespace, Name: "parking"}}, - ActorTemplates: []steps.TemplateRef{{Namespace: namespace, Name: "parking"}}, + Deployments: []steps.TemplateRef{{Atespace: namespace, Name: "parking"}}, + ActorTemplates: []steps.TemplateRef{{Atespace: namespace, Name: "parking"}}, }) } diff --git a/cmd/ate-setup/internal/demos/sandbox/sandbox.go b/cmd/ate-setup/internal/demos/sandbox/sandbox.go index 3e5ec1adf..a71231892 100644 --- a/cmd/ate-setup/internal/demos/sandbox/sandbox.go +++ b/cmd/ate-setup/internal/demos/sandbox/sandbox.go @@ -28,7 +28,7 @@ func init() { DemoName: "demo-sandbox", Short: "An on-demand sandbox actor driven by the sandbox client", Template: "demos/sandbox/sandbox.yaml.tmpl", - ActorTemplates: []steps.TemplateRef{{Namespace: namespace, Name: "sandbox-template"}}, + ActorTemplates: []steps.TemplateRef{{Atespace: namespace, Name: "sandbox-template"}}, // There is no workload to come up, and the template is exercised on // demand, so the install does not block on readiness. SkipReadinessWait: true, diff --git a/cmd/ate-setup/internal/demos/simple.go b/cmd/ate-setup/internal/demos/simple.go index 16ad4affa..b2b168d08 100644 --- a/cmd/ate-setup/internal/demos/simple.go +++ b/cmd/ate-setup/internal/demos/simple.go @@ -142,12 +142,12 @@ func (d *Simple) WaitReady(ctx context.Context, e *steps.Env) error { } log.Stepf("Waiting for %s to be ready...", d.DemoName) for _, ref := range d.Deployments { - if err := e.Kube.RolloutStatus(ctx, kube.KindDeployment, ref.Namespace, ref.Name, steps.DemoTimeout); err != nil { + if err := e.Kube.RolloutStatus(ctx, kube.KindDeployment, ref.Atespace, ref.Name, steps.DemoTimeout); err != nil { return err } } for _, ref := range d.ActorTemplates { - if err := WaitActorTemplateReady(ctx, e, ref.Namespace, ref.Name); err != nil { + if err := WaitActorTemplateReady(ctx, e, ref.Atespace, ref.Name); err != nil { return err } } diff --git a/cmd/ate-setup/internal/steps/actors.go b/cmd/ate-setup/internal/steps/actors.go index d7018de0b..33c490592 100644 --- a/cmd/ate-setup/internal/steps/actors.go +++ b/cmd/ate-setup/internal/steps/actors.go @@ -27,8 +27,8 @@ import ( // against it because the demo manifests own the template, not the actors that // were created from it. type TemplateRef struct { - Namespace string - Name string + Atespace string + Name string } // DeleteDemoActors removes every actor created from the given ActorTemplates. @@ -66,9 +66,11 @@ func (e *Env) DeleteDemoActors(ctx context.Context, refs ...TemplateRef) error { } for _, ref := range refs { - log.Stepf("Deleting actors for %s/%s", ref.Namespace, ref.Name) + log.Stepf("Deleting actors for %s/%s", ref.Atespace, ref.Name) for _, actor := range actors { - if actor.GetActorTemplateNamespace() != ref.Namespace || actor.GetActorTemplateName() != ref.Name { + // Actors name their template through the actor_template ref; demo + // templates keep the CRD namespace as the ref's atespace. + if actor.GetActorTemplate().GetAtespace() != ref.Atespace || actor.GetActorTemplate().GetName() != ref.Name { continue } actorRef := resources.ActorRefFromActor(actor) diff --git a/demos/egress/README.md b/demos/egress/README.md index 33d6adfd6..b2ece9a4b 100644 --- a/demos/egress/README.md +++ b/demos/egress/README.md @@ -75,7 +75,14 @@ intercepted and carried over mTLS to a gateway that verifies who is making the r ```bash ./hack/install-ate.sh --deploy-demo-egress -kubectl wait --for=condition=Ready actortemplate/egress -n ate-demo-egress --timeout=5m +``` + +The install applies the worker pool, creates the `ate-demo-egress` atespace and +the `egress` ActorTemplate (a substrate resource, not a CRD) through the ate +API, and blocks until the template's golden snapshot is built: + +```bash +kubectl ate get actor-template egress -a ate-demo-egress ``` ## Run the automated test (easiest) @@ -104,15 +111,15 @@ kubectl -n egress-target create deployment whoami --image=traefik/whoami kubectl -n egress-target expose deployment whoami --port=80 TARGET_IP=$(kubectl -n egress-target get svc whoami -o jsonpath='{.spec.clusterIP}') -# 2. Create and resume an Actor. -kubectl ate create atespace demo -kubectl ate create actor egress-demo -a demo --template ate-demo-egress/egress -kubectl ate resume actor egress-demo -a demo # wait for ACTOR_STATE_RUNNING +# 2. Create and resume an Actor in the demo's atespace: --template-ref +# resolves the template by name within the actor's own atespace. +kubectl ate create actor egress-demo -a ate-demo-egress --template-ref egress +kubectl ate resume actor egress-demo -a ate-demo-egress # wait for ACTOR_STATE_RUNNING # 3. Drive the Actor's egress through the ingress gateway. kubectl -n ate-system port-forward service/atenet-router 8000:80 & curl -s -X POST http://localhost:8000/ \ - -H 'Host: egress-demo.demo.actors.resources.substrate.ate.dev' \ + -H 'Host: egress-demo.ate-demo-egress.actors.resources.substrate.ate.dev' \ -H 'Content-Type: application/json' \ -d "{\"url\":\"http://${TARGET_IP}:80/\"}" ``` @@ -122,11 +129,11 @@ curl -s -X POST http://localhost:8000/ \ ```bash # The egress gateway logs each tunneled CONNECT against the verified peer certificate: kubectl -n ate-system logs deploy/atenet-egress | grep '\[egress\]' -# [egress] authority=:80 peer_san=spiffe://substrate-actor.local/atespace/demo/actor/egress-demo … code=200 … +# [egress] authority=:80 peer_san=spiffe://substrate-actor.local/atespace/ate-demo-egress/actor/egress-demo … code=200 … # The co-located ext_proc sidecar logs the identity decision, including the UID it authorized on: kubectl -n ate-system logs deploy/atenet-egress -c ext-proc | grep -i 'egress identity\|egress denied' -# egress identity authenticated atespace=demo actor=egress-demo actorUid=… destination=:80 +# egress identity authenticated atespace=ate-demo-egress actor=egress-demo actorUid=… destination=:80 ``` The `whoami` body shows `RemoteAddr: ` — proof the request egressed diff --git a/demos/egress/egress-microvm-mitm-template.yaml.tmpl b/demos/egress/egress-microvm-mitm-template.yaml.tmpl new file mode 100644 index 000000000..2ac7a9f16 --- /dev/null +++ b/demos/egress/egress-microvm-mitm-template.yaml.tmpl @@ -0,0 +1,86 @@ +# Copyright 2026 Google LLC +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +# Micro-VM MITM variant of the egress demo template. Delivery of +# the projected bundle into the sandbox is what differs by class — a read-only +# bind for gVisor, the unified virtio-fs share for the guest here — so the +# projection is worth proving on both. +# +# Kept in step with egress-microvm-template.yaml.tmpl; the deltas +# are the names, the projected trust bundle, and the two SSL_CERT_* variables. + +metadata: + atespace: ate-demo-egress-microvm-mitm + name: egress-microvm-mitm +workerSelector: + matchLabels: + workload: egress-microvm-mitm +containers: +- name: egress + image: ko://github.com/agent-substrate/substrate/demos/egress + command: ["/ko-app/egress"] + # The demo fetches with a stock net/http client, so its roots come from + # crypto/x509's system pool — which on Unix reads both of these. + # + # SSL_CERT_FILE alone is not enough to make the projected bundle the whole + # story: it replaces the default cert FILE list, but the default cert + # DIRECTORY list is still scanned, and the base image keeps its public + # roots in /etc/ssl/certs. Pointing SSL_CERT_DIR at the projection too + # makes the anchor set exactly the gateway CA, so a successful HTTPS fetch + # proves the projected bundle validated the minted leaf. Under sdsmint the + # public roots are useless anyway: every origin is fronted by the gateway. + env: + - name: SSL_CERT_FILE + value: /run/ate/trust-bundle.pem + - name: SSL_CERT_DIR + value: /run/ate + volumeMounts: + # the bundle lands at /run/ate/trust-bundle.pem + - name: system-info + mountPath: /run/ate + readyz: + httpGet: + path: /readyz + port: 80 + # See counter-substrate-microvm-template.yaml.tmpl: a micro-VM guest needs + # more than readyz's 30s default once CI's single kind node is under load. + timeoutSeconds: 120 +volumes: +- name: system-info + type: SystemInfo + systemInfo: + dataSources: + # The trust anchors for the per-SNI leaves the MITM egress gateway mints. + # Only allowlisted bundle names resolve; this is the one supported today. + - trustBundle: + name: egress-mitm.ate.dev + path: trust-bundle.pem +# Sandbox size: without these the guest boots at the kata config's default +# (2GiB). ateom applies them to the VM (see internal/sizing). Quantities are +# strings in the proto. +resources: + limits: + - name: cpu + quantity: "1" + - name: memory + quantity: 512Mi +sandboxConfig: + sandboxClass: SANDBOX_CLASS_MICROVM + # Deliberately not the class default; installed cluster-wide by + # hack/install-microvm-deps.sh, so a missing or stale install fails loudly. + configName: microvm +snapshotsConfig: + onPause: SNAPSHOT_CONTENT_SCOPE_FULL + onCommit: SNAPSHOT_CONTENT_SCOPE_FULL + storageLocation: gs://${BUCKET_NAME}/ate-demo-egress-microvm-mitm/ diff --git a/demos/egress/egress-microvm-mitm.yaml.tmpl b/demos/egress/egress-microvm-mitm.yaml.tmpl index 09e179855..1e58c66e6 100644 --- a/demos/egress/egress-microvm-mitm.yaml.tmpl +++ b/demos/egress/egress-microvm-mitm.yaml.tmpl @@ -12,19 +12,12 @@ # See the License for the specific language governing permissions and # limitations under the License. -# Micro-VM (kata + cloud-hypervisor) variant of the MITM egress demo, so the -# networking suite's egress tests run against the sdsmint gateway on both -# sandbox classes. Delivery of the projected bundle into the sandbox is what -# differs by class — a read-only bind for gVisor, the unified virtio-fs share -# for the guest here — so the projection is worth proving on both. -# -# As in egress-mitm.yaml.tmpl, this REQUIRES an sdsmint install -# (--experimental-use-sdsmint). -# -# Kept in step with egress-microvm.yaml.tmpl; the deltas are the names, the -# projected trust bundle, and the two SSL_CERT_* variables. The cluster-wide -# `microvm` SandboxConfig referenced below by name is installed by -# hack/install-microvm-deps.sh. +# Worker pool for the micro-VM MITM variant of the egress demo, so +# the networking suite's egress tests run against the sdsmint gateway on both +# sandbox classes. As in egress-mitm.yaml.tmpl, this REQUIRES an +# sdsmint install (--experimental-use-sdsmint). The cluster-wide `microvm` +# SandboxConfig referenced by the pool is installed by +# hack/install-microvm-deps.sh apiVersion: v1 kind: Namespace @@ -45,66 +38,9 @@ spec: sandboxClass: microvm sandboxConfigName: microvm workerImage: ko://github.com/agent-substrate/substrate/cmd/ateom-microvm - # No template.resources, unlike counter-microvm: an unlimited ateom container - # reports no capacity, which the scheduler reads as unconstrained, and the pod - # requests nothing so it stays placeable next to the counter-microvm pool's - # reservation on CI's single kind node. What actually bounds the memory here - # is the ActorTemplate's resources below, which size the guest. - ---- - -apiVersion: ate.dev/v1alpha1 -kind: ActorTemplate -metadata: - name: egress-microvm-mitm - namespace: ate-demo-egress-microvm-mitm -spec: - # Must match the WorkerPool's sandboxClass: a snapshot is not portable across - # sandbox classes, so only pools of the same class are eligible to run this - # template's actors. - sandboxClass: microvm - volumes: - - name: system-info - systemInfo: - dataSources: - # The trust anchors for the per-SNI leaves the MITM egress gateway mints. - # Only allowlisted bundle names resolve; this is the one supported today. - - trustBundle: - name: egress-mitm.ate.dev - path: trust-bundle.pem - containers: - - name: egress - image: ko://github.com/agent-substrate/substrate/demos/egress - command: ["/ko-app/egress"] - # Both variables, for the reason egress-mitm.yaml.tmpl spells out: - # SSL_CERT_FILE replaces the default cert file list but not the default cert - # DIRECTORY scan, so SSL_CERT_DIR has to point at the projection too for the - # anchor set to be exactly the gateway CA. - env: - - name: SSL_CERT_FILE - value: /run/ate/trust-bundle.pem - - name: SSL_CERT_DIR - value: /run/ate - volumeMounts: - - name: system-info - mountPath: /run/ate # the bundle lands at /run/ate/trust-bundle.pem - readyz: - httpGet: - path: /readyz - port: 80 - # See counter-microvm.yaml.tmpl: a micro-VM guest needs more than - # readyz's 30s default once CI's single kind node is under load. - timeoutSeconds: 120 - # Sandbox size: without these the guest boots at the kata config's default - # (2GiB). ateom applies them to the VM (see internal/sizing). - resources: - limits: - cpu: "1" - memory: 512Mi - workerSelector: - matchLabels: - workload: egress-microvm-mitm - snapshotsConfig: - onPause: Full - onCommit: Full - location: gs://${BUCKET_NAME}/ate-demo-egress-microvm-mitm/ + # No template.resources, unlike counter-substrate-microvm: an unlimited + # ateom container reports no capacity, which the scheduler reads as + # unconstrained, and the pod requests nothing so it stays placeable next to + # the counter pool's reservation on CI's single kind node. What actually + # bounds the memory here is the ActorTemplate's resources, which size the + # guest. diff --git a/demos/egress/egress-microvm-template.yaml.tmpl b/demos/egress/egress-microvm-template.yaml.tmpl new file mode 100644 index 000000000..657213297 --- /dev/null +++ b/demos/egress/egress-microvm-template.yaml.tmpl @@ -0,0 +1,58 @@ +# Copyright 2026 Google LLC +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +# Substrate ActorTemplate resource (protojson-shaped ateapipb.ActorTemplate, +# NOT the ate.dev/v1alpha1 CRD) for the micro-VM egress demo. The actor's +# outbound TCP is redirected into atunnel by nftables in the ateom +# (prepareActorEgress), which the micro-VM ateom implements the same way the +# gVisor one does — this fixture is what proves it end to end. +# +# Kept in step with egress-template.yaml.tmpl; the deltas are the +# runtime fields and the sandbox size. + +metadata: + atespace: ate-demo-egress-microvm + name: egress-microvm +workerSelector: + matchLabels: + workload: egress-microvm +containers: +- name: egress + image: ko://github.com/agent-substrate/substrate/demos/egress + command: ["/ko-app/egress"] + readyz: + httpGet: + path: /readyz + port: 80 + # See counter-substrate-microvm-template.yaml.tmpl: a micro-VM guest needs + # more than readyz's 30s default once CI's single kind node is under load. + timeoutSeconds: 120 +# Sandbox size: without these the guest boots at the kata config's default +# (2GiB). ateom applies them to the VM (see internal/sizing). Quantities are +# strings in the proto. +resources: + limits: + - name: cpu + quantity: "1" + - name: memory + quantity: 512Mi +sandboxConfig: + sandboxClass: SANDBOX_CLASS_MICROVM + # Deliberately not the class default; installed cluster-wide by + # hack/install-microvm-deps.sh, so a missing or stale install fails loudly. + configName: microvm +snapshotsConfig: + onPause: SNAPSHOT_CONTENT_SCOPE_FULL + onCommit: SNAPSHOT_CONTENT_SCOPE_FULL + storageLocation: gs://${BUCKET_NAME}/ate-demo-egress-microvm/ diff --git a/demos/egress/egress-microvm.yaml.tmpl b/demos/egress/egress-microvm.yaml.tmpl index 0b2c43f48..641f84763 100644 --- a/demos/egress/egress-microvm.yaml.tmpl +++ b/demos/egress/egress-microvm.yaml.tmpl @@ -12,15 +12,13 @@ # See the License for the specific language governing permissions and # limitations under the License. -# Micro-VM (kata + cloud-hypervisor) variant of the egress demo, so the -# networking suite's egress tests run against both sandbox classes. The actor's -# outbound TCP is redirected into atunnel by nftables in the ateom -# (prepareActorEgress), which the micro-VM ateom implements the same way the -# gVisor one does — this fixture is what proves it end to end. -# -# Kept in step with egress.yaml.tmpl; the deltas are the runtime fields and the -# sandbox size. The cluster-wide `microvm` SandboxConfig referenced below by -# name is installed by hack/install-microvm-deps.sh. +# Worker pool for the micro-VM (kata + cloud-hypervisor) variant of the +# egress demo, so the networking suite's egress tests run against both +# sandbox classes. The ActorTemplate is a substrate resource, not a CRD: it +# lives in egress-microvm-template.yaml.tmpl and is created through the +# ate API with `kubectl ate create actor-template`. The cluster-wide `microvm` +# SandboxConfig referenced by the pool is installed by +# hack/install-microvm-deps.sh apiVersion: v1 kind: Namespace @@ -41,45 +39,9 @@ spec: sandboxClass: microvm sandboxConfigName: microvm workerImage: ko://github.com/agent-substrate/substrate/cmd/ateom-microvm - # No template.resources, unlike counter-microvm: an unlimited ateom container - # reports no capacity, which the scheduler reads as unconstrained, and the pod - # requests nothing so it stays placeable next to the counter-microvm pool's - # reservation on CI's single kind node. What actually bounds the memory here - # is the ActorTemplate's resources below, which size the guest. - ---- - -apiVersion: ate.dev/v1alpha1 -kind: ActorTemplate -metadata: - name: egress-microvm - namespace: ate-demo-egress-microvm -spec: - # Must match the WorkerPool's sandboxClass: a snapshot is not portable across - # sandbox classes, so only pools of the same class are eligible to run this - # template's actors. - sandboxClass: microvm - containers: - - name: egress - image: ko://github.com/agent-substrate/substrate/demos/egress - command: ["/ko-app/egress"] - readyz: - httpGet: - path: /readyz - port: 80 - # See counter-microvm.yaml.tmpl: a micro-VM guest needs more than - # readyz's 30s default once CI's single kind node is under load. - timeoutSeconds: 120 - # Sandbox size: without these the guest boots at the kata config's default - # (2GiB). ateom applies them to the VM (see internal/sizing). - resources: - limits: - cpu: "1" - memory: 512Mi - workerSelector: - matchLabels: - workload: egress-microvm - snapshotsConfig: - onPause: Full - onCommit: Full - location: gs://${BUCKET_NAME}/ate-demo-egress-microvm/ + # No template.resources, unlike counter-substrate-microvm: an unlimited + # ateom container reports no capacity, which the scheduler reads as + # unconstrained, and the pod requests nothing so it stays placeable next to + # the counter pool's reservation on CI's single kind node. What actually + # bounds the memory here is the ActorTemplate's resources, which size the + # guest. diff --git a/demos/egress/egress-mitm-template.yaml.tmpl b/demos/egress/egress-mitm-template.yaml.tmpl new file mode 100644 index 000000000..180bf3acd --- /dev/null +++ b/demos/egress/egress-mitm-template.yaml.tmpl @@ -0,0 +1,74 @@ +# Copyright 2026 Google LLC +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +# MITM variant of the egress demo template: the same workload as +# egress-template.yaml.tmpl, but trusting only the egress gateway +# CA. The networking suite builds its egress actors from this fixture instead +# of the plain one when E2E_EGRESS_MITM is set, because under an sdsmint +# gateway TestActorEgressHTTPS's origin certificate is a per-SNI leaf the +# gateway minted, which chains to no public CA. +# +# Kept in step with egress-template.yaml.tmpl; the deltas are the +# names, the projected trust bundle, and the two SSL_CERT_* variables. + +metadata: + atespace: ate-demo-egress-mitm + name: egress-mitm +workerSelector: + matchLabels: + workload: egress-mitm +containers: +- name: egress + image: ko://github.com/agent-substrate/substrate/demos/egress + command: ["/ko-app/egress"] + # The demo fetches with a stock net/http client, so its roots come from + # crypto/x509's system pool — which on Unix reads both of these. + # + # SSL_CERT_FILE alone is not enough to make the projected bundle the whole + # story: it replaces the default cert FILE list, but the default cert + # DIRECTORY list is still scanned, and the base image keeps its public + # roots in /etc/ssl/certs. Pointing SSL_CERT_DIR at the projection too + # makes the anchor set exactly the gateway CA, so a successful HTTPS fetch + # proves the projected bundle validated the minted leaf. Under sdsmint the + # public roots are useless anyway: every origin is fronted by the gateway. + env: + - name: SSL_CERT_FILE + value: /run/ate/trust-bundle.pem + - name: SSL_CERT_DIR + value: /run/ate + volumeMounts: + # the bundle lands at /run/ate/trust-bundle.pem + - name: system-info + mountPath: /run/ate + readyz: + httpGet: + path: /readyz + port: 80 +volumes: +- name: system-info + type: SystemInfo + systemInfo: + dataSources: + # The trust anchors for the per-SNI leaves the MITM egress gateway mints. + # Only allowlisted bundle names resolve; this is the one supported today. + - trustBundle: + name: egress-mitm.ate.dev + path: trust-bundle.pem +sandboxConfig: + sandboxClass: SANDBOX_CLASS_GVISOR + configName: gvisor-default +snapshotsConfig: + onPause: SNAPSHOT_CONTENT_SCOPE_FULL + onCommit: SNAPSHOT_CONTENT_SCOPE_FULL + storageLocation: gs://${BUCKET_NAME}/ate-demo-egress-mitm/ diff --git a/demos/egress/egress-mitm.yaml.tmpl b/demos/egress/egress-mitm.yaml.tmpl index eca6596a3..8ede7925b 100644 --- a/demos/egress/egress-mitm.yaml.tmpl +++ b/demos/egress/egress-mitm.yaml.tmpl @@ -12,17 +12,11 @@ # See the License for the specific language governing permissions and # limitations under the License. -# MITM variant of the egress demo: the same workload as egress.yaml.tmpl, but -# trusting only the egress gateway CA. The networking suite builds its egress -# actors from this fixture instead of the plain one when E2E_EGRESS_MITM is set, -# because under an sdsmint gateway TestActorEgressHTTPS's origin certificate is -# a per-SNI leaf the gateway minted, which chains to no public CA. -# -# Kept as its own fixture rather than a flag on the egress demo: it REQUIRES an -# sdsmint install (--experimental-use-sdsmint). -# -# Kept in step with egress.yaml.tmpl; the deltas are the names, the projected -# trust bundle, and the two SSL_CERT_* variables. +# Worker pool for the MITM variant of the egress demo: the same +# workload as egress.yaml.tmpl, but its template (in +# egress-mitm-template.yaml.tmpl) trusts only the egress gateway CA. +# Kept as its own fixture rather than a flag on the egress demo: it REQUIRES +# an sdsmint install (--experimental-use-sdsmint). apiVersion: v1 kind: Namespace @@ -41,54 +35,3 @@ metadata: spec: replicas: 2 workerImage: ko://github.com/agent-substrate/substrate/cmd/ateom-gvisor - ---- - -apiVersion: ate.dev/v1alpha1 -kind: ActorTemplate -metadata: - name: egress-mitm - namespace: ate-demo-egress-mitm -spec: - volumes: - - name: system-info - systemInfo: - dataSources: - # The trust anchors for the per-SNI leaves the MITM egress gateway mints. - # Only allowlisted bundle names resolve; this is the one supported today. - - trustBundle: - name: egress-mitm.ate.dev - path: trust-bundle.pem - containers: - - name: egress - image: ko://github.com/agent-substrate/substrate/demos/egress - command: ["/ko-app/egress"] - # The demo fetches with a stock net/http client, so its roots come from - # crypto/x509's system pool — which on Unix reads both of these. - # - # SSL_CERT_FILE alone is not enough to make the projected bundle the whole - # story: it replaces the default cert FILE list, but the default cert - # DIRECTORY list is still scanned, and the base image keeps its public - # roots in /etc/ssl/certs. Pointing SSL_CERT_DIR at the projection too - # makes the anchor set exactly the gateway CA, so a successful HTTPS fetch - # proves the projected bundle validated the minted leaf. Under sdsmint the - # public roots are useless anyway: every origin is fronted by the gateway. - env: - - name: SSL_CERT_FILE - value: /run/ate/trust-bundle.pem - - name: SSL_CERT_DIR - value: /run/ate - volumeMounts: - - name: system-info - mountPath: /run/ate # the bundle lands at /run/ate/trust-bundle.pem - readyz: - httpGet: - path: /readyz - port: 80 - workerSelector: - matchLabels: - workload: egress-mitm - snapshotsConfig: - onPause: Full - onCommit: Full - location: gs://${BUCKET_NAME}/ate-demo-egress-mitm/ diff --git a/demos/egress/egress-template.yaml.tmpl b/demos/egress/egress-template.yaml.tmpl new file mode 100644 index 000000000..a3e3e7c41 --- /dev/null +++ b/demos/egress/egress-template.yaml.tmpl @@ -0,0 +1,40 @@ +# Copyright 2026 Google LLC +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +# Substrate ActorTemplate resource (protojson-shaped ateapipb.ActorTemplate, +# NOT the ate.dev/v1alpha1 CRD) for the egress demo. Applied with +# `kubectl ate create actor-template -f -` after `ko resolve` replaces the +# ko:// image reference; the ate-demo-egress atespace must exist. + +metadata: + atespace: ate-demo-egress + name: egress +workerSelector: + matchLabels: + workload: egress +containers: +- name: egress + image: ko://github.com/agent-substrate/substrate/demos/egress + command: ["/ko-app/egress"] + readyz: + httpGet: + path: /readyz + port: 80 +sandboxConfig: + sandboxClass: SANDBOX_CLASS_GVISOR + configName: gvisor-default +snapshotsConfig: + onPause: SNAPSHOT_CONTENT_SCOPE_FULL + onCommit: SNAPSHOT_CONTENT_SCOPE_FULL + storageLocation: gs://${BUCKET_NAME}/ate-demo-egress/ diff --git a/demos/egress/egress.yaml.tmpl b/demos/egress/egress.yaml.tmpl index 6dc6b9a66..6c63569ec 100644 --- a/demos/egress/egress.yaml.tmpl +++ b/demos/egress/egress.yaml.tmpl @@ -12,6 +12,10 @@ # See the License for the specific language governing permissions and # limitations under the License. +# Worker pool for the egress demo. The ActorTemplate is a substrate resource, +# not a CRD: it lives in egress-template.yaml.tmpl and is created through the +# ate API with `kubectl ate create actor-template`. + apiVersion: v1 kind: Namespace metadata: @@ -29,27 +33,3 @@ metadata: spec: replicas: 2 workerImage: ko://github.com/agent-substrate/substrate/cmd/ateom-gvisor - ---- - -apiVersion: ate.dev/v1alpha1 -kind: ActorTemplate -metadata: - name: egress - namespace: ate-demo-egress -spec: - containers: - - name: egress - image: ko://github.com/agent-substrate/substrate/demos/egress - command: ["/ko-app/egress"] - readyz: - httpGet: - path: /readyz - port: 80 - workerSelector: - matchLabels: - workload: egress - snapshotsConfig: - onPause: Full - onCommit: Full - location: gs://${BUCKET_NAME}/ate-demo-egress/ diff --git a/demos/egress/test-egress.sh b/demos/egress/test-egress.sh index 309d2cae2..71bcf2a29 100755 --- a/demos/egress/test-egress.sh +++ b/demos/egress/test-egress.sh @@ -33,9 +33,11 @@ set -o errexit -o nounset -o pipefail CTX="${KUBECTL_CONTEXT:-kind-kind}" -ATESPACE="${ATESPACE:-demo}" +# The actor lives in the demo's atespace: --template-ref resolves the +# template by name within the actor's own atespace. +ATESPACE="${ATESPACE:-ate-demo-egress}" ACTOR="${ACTOR:-egress-demo}" -TEMPLATE="${TEMPLATE:-ate-demo-egress/egress}" +TEMPLATE="${TEMPLATE:-egress}" TARGET_NS="${TARGET_NS:-egress-target}" PROBE_POD="egress-identity-probe" @@ -78,7 +80,7 @@ info "target ClusterIP = ${TARGET_IP}" log "create + resume Actor ${ATESPACE}/${ACTOR}" ${KATE} create atespace "${ATESPACE}" >/dev/null 2>&1 || true -${KATE} create actor "${ACTOR}" -a "${ATESPACE}" --template "${TEMPLATE}" >/dev/null 2>&1 || true +${KATE} create actor "${ACTOR}" -a "${ATESPACE}" --template-ref "${TEMPLATE}" >/dev/null 2>&1 || true ${KATE} resume actor "${ACTOR}" -a "${ATESPACE}" >/dev/null 2>&1 || true for _ in $(seq 1 30); do ${KATE} get actors -a "${ATESPACE}" 2>/dev/null | grep -q "ACTOR_STATE_RUNNING" && break diff --git a/hack/install-ate.sh b/hack/install-ate.sh index 43f4ee41a..7f4292ac5 100755 --- a/hack/install-ate.sh +++ b/hack/install-ate.sh @@ -878,6 +878,102 @@ wait_actortemplate_ready() { return 1 } +# deploy_substrate_demo deploys a demo whose ActorTemplate is a substrate +# resource: apply the pool manifest, wait for the pool rollout, create the +# template through the ate API, and block on its golden snapshot. +# deploy_substrate_demo \ +#