Skip to content
Open
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
18 changes: 12 additions & 6 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -56,7 +56,7 @@ signed, and meant to be used by digest:

The digests are in the release notes and in the `images_<tag>.txt` asset; the
release's `ate-env` binary defaults to them. The worker image
(`--worker-image`, `ateom-gvisor`) comes from the
(`--worker-image`, `ateworker-gvisor`) comes from the
[Substrate repo](https://github.com/agent-substrate/substrate) and must match the
Substrate version on your cluster. See [docs/release.md](docs/release.md) for
verification, pinning and how releases are cut.
Expand All @@ -65,23 +65,25 @@ verification, pinning and how releases are cut.

Prerequisites: a cluster with [Agent Substrate](https://github.com/agent-substrate/substrate)
installed and a snapshots bucket. ate-env is built against Substrate
[`da35a983`](https://github.com/agent-substrate/substrate/commit/da35a983) (2026-10-13). The cluster
[`6edd1fcc`](https://github.com/agent-substrate/substrate/commit/6edd1fcc) (2026-10-15). The cluster
needs a Substrate recent enough to accept what ate-env writes: the WorkerPool declares
`spec.sandboxClasses`, the ActorTemplate uses `snapshotConfig` and `wakeupProbe`, and `--worker-image`
must be the `ateom` image published by that Substrate install.
`spec.sandboxClasses`, the ActorTemplate uses `snapshotConfig` and `wakeupProbe`, names its sandbox
class as the string `gvisor`, and declares CPU and memory limits, and `--worker-image` must be the
`ateworker-gvisor` image published by that Substrate install. A Substrate older than 2026-10-14 rejects
the string sandbox class; use an ate-env from before this bump with it.

### 1. Deploy the Kubernetes Resources

Deploy the namespace, worker pool, and API service:

```bash
# With a release binary the API image defaults to the release's. The worker
# image is ateom-gvisor from the Substrate repo at your cluster's version; in
# image is ateworker-gvisor from the Substrate repo at your cluster's version; in
# that checkout, `go run ./cmd/ate-setup publish worker-images` pushes it and
# prints the digest-pinned ref. Use the digest: a :latest tag is whatever was
# built last, and a stale worker image registers workers with no capacity.
ate-env manifest \
--worker-image <registry>/ateom-gvisor@sha256:<digest> | kubectl apply -f -
--worker-image <registry>/ateworker-gvisor@sha256:<digest> | kubectl apply -f -
# From a source build, add: --api-image ghcr.io/agent-substrate/env/ate-env-api@sha256:<digest>

# Ensure that the pods are running:
Expand All @@ -101,6 +103,10 @@ ate-env manifest template \
# From a source build, add: --guest-image ghcr.io/agent-substrate/env/ate-env-guest@sha256:<digest>
```

Each actor gets 2 CPUs and 4 GiB unless you pass `--cpu` and `--memory` (Kubernetes quantities such as
`500m` or `8Gi`). Substrate requires both limits and books them against worker capacity; templates
derived per rootfs image inherit them.

That template's container is the guest image itself. To run another image unmodified, name
it with `--rootfs-image`: the guest is then mounted into it as a read-only OCI image volume at
`/ate` and started from there, so the rootfs image is never rebuilt.
Expand Down
53 changes: 46 additions & 7 deletions cmd/ate-env/manifest.go
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@ import (
"google.golang.org/protobuf/proto"
appsv1 "k8s.io/api/apps/v1"
corev1 "k8s.io/api/core/v1"
"k8s.io/apimachinery/pkg/api/resource"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/apimachinery/pkg/util/intstr"
"sigs.k8s.io/yaml"
Expand All @@ -42,6 +43,15 @@ const apiName = "ate-env-api"
// one, so a pool declared with it is schedulable on a stock install.
const sandboxConfigName = "gvisor-default"

// Default compute limits for each actor. Substrate requires both on every
// ActorTemplate since substrate#2285: the scheduler books them against worker
// capacity. Two CPUs and 4 GiB match the Python client's ResourceLimits
// default; size them for the workload with --cpu and --memory.
const (
defaultActorCPU = "2"
defaultActorMemory = "4Gi"
)

// manifestConfig holds configuration for generating Kubernetes deployment manifests.
type manifestConfig struct {
namespace string
Expand All @@ -56,8 +66,8 @@ type manifestConfig struct {

func (c *manifestConfig) resolveImages() error {
if c.workerImage == "" {
return errors.New(`--worker-image (or --ateom-image) is required: the worker image is
ateom-gvisor from the Substrate repo and must match the Substrate version
return errors.New(`--worker-image is required: the worker image is
ateworker-gvisor from the Substrate repo and must match the Substrate version
deployed on your cluster. From a Substrate checkout at that version run
"go run ./cmd/ate-setup publish worker-images": it pushes the images and
prints the digest-pinned refs to use. Pin by digest: a registry's :latest
Expand Down Expand Up @@ -93,6 +103,10 @@ type templateConfig struct {
sidecars []string
// sidecarReadyz are URLs the guest's /readyz waits for.
sidecarReadyz []string
// cpu and memory are the actor's compute limits as Kubernetes quantities.
// Empty means defaultActorCPU and defaultActorMemory.
cpu string
memory string
}

// runtimeLayer is a parsed --layer value.
Expand Down Expand Up @@ -148,6 +162,18 @@ binaries default it to the ate-env-guest image published with the release
if c.snapshotsBucket == "" {
return errors.New("--snapshots-bucket is required; use an object-storage bucket (e.g. gs://bucket/prefix/)")
}
for _, l := range []struct{ flag, value string }{{"--cpu", c.cpu}, {"--memory", c.memory}} {
if l.value == "" {
continue
}
q, err := resource.ParseQuantity(l.value)
if err != nil {
return fmt.Errorf("%s %q is not a Kubernetes quantity (e.g. 2, 500m, 4Gi): %w", l.flag, l.value, err)
}
if q.Sign() <= 0 {
return fmt.Errorf("%s must be greater than zero, got %q", l.flag, l.value)
}
}
if c.rootfsImage != "" {
if _, err := apiservice.ImageDigest(c.rootfsImage); err != nil {
return fmt.Errorf("--rootfs-image: %w", err)
Expand Down Expand Up @@ -203,8 +229,8 @@ the "template" subcommand: ate-env manifest template`,

cmd.Flags().StringVar(&mCfg.namespace, "namespace", apiservice.DefaultNamespace, "Kubernetes namespace to deploy into")
cmd.Flags().StringVar(&mCfg.template, "template", apiservice.DefaultTemplate, "ActorTemplate name")
cmd.Flags().StringVar(&mCfg.workerImage, "worker-image", "", "digest-pinned worker image for the worker pool, e.g. ateom-gvisor built from the Substrate repo")
cmd.Flags().StringVar(&mCfg.workerImage, "ateom-image", "", "alias for --worker-image")
cmd.Flags().StringVar(&mCfg.workerImage, "worker-image", "", "digest-pinned worker image for the worker pool, e.g. ateworker-gvisor built from the Substrate repo")
cmd.Flags().StringVar(&mCfg.workerImage, "ateom-image", "", "alias for --worker-image, kept from before Substrate renamed ateom to ateworker")
cmd.Flags().StringVar(&mCfg.apiImage, "api-image", defaultAPIImage, imageDefaultHelp("digest-pinned ate-env-api image for the API service", defaultAPIImage))
cmd.Flags().Int32Var(&mCfg.apiReplicas, "api-replicas", 1, "number of API service replicas")
cmd.Flags().Int32Var(&mCfg.apiPort, "api-port", 7777, "port the ate-env-api service listens on")
Expand Down Expand Up @@ -250,6 +276,8 @@ It prints YAML to stdout without touching the cluster.`,
cmd.Flags().StringArrayVar(&tCfg.layers, "layer", nil, "extra runtime mounted as a read-only image volume, as name=image@sha256:...=/mount/path (repeatable)")
cmd.Flags().StringArrayVar(&tCfg.sidecars, "sidecar", nil, "command the guest starts and keeps running next to itself, an absolute binary path plus arguments, e.g. '/opt/opensandbox/execd --port 44772' (repeatable)")
cmd.Flags().StringArrayVar(&tCfg.sidecarReadyz, "sidecar-readyz", nil, "URL the actor's readiness waits for, e.g. http://127.0.0.1:44772/ready (repeatable)")
cmd.Flags().StringVar(&tCfg.cpu, "cpu", defaultActorCPU, "CPU limit of each actor, a Kubernetes quantity such as 2 or 500m; Substrate requires one")
cmd.Flags().StringVar(&tCfg.memory, "memory", defaultActorMemory, "memory limit of each actor, a Kubernetes quantity such as 4Gi; Substrate requires one")

return cmd
}
Expand Down Expand Up @@ -387,7 +415,7 @@ func buildWorkerPool(cfg manifestConfig) *atev1alpha1.WorkerPool {
// the SandboxConfig the workers run; the template names the same
// one in sandboxConfig.configName.
SandboxClasses: []atev1alpha1.WorkerPoolSandboxClass{{
Name: atev1alpha1.SandboxClassGvisor,
Name: atev1alpha1.SandboxClassGvisor, // the template's sandboxConfig.sandboxClass is the same string
ConfigRef: &atev1alpha1.SandboxConfigReference{Name: sandboxConfigName},
}},
},
Expand All @@ -412,7 +440,7 @@ func buildActorTemplate(cfg templateConfig) *ateapipb.ActorTemplate {
Name: "PORT",
Value: "80",
}},
WakeupProbe: &ateapipb.ContainerWakeupProbe{
WakeupProbe: &ateapipb.WakeupProbe{
HttpGet: &ateapipb.HttpGetAction{
Path: "/readyz",
Port: 80,
Expand All @@ -427,10 +455,21 @@ func buildActorTemplate(cfg templateConfig) *ateapipb.ActorTemplate {
PreferredFidelity: ateapipb.SnapshotFidelity_SNAPSHOT_FIDELITY_MEMORY,
},
SandboxConfig: &ateapipb.SandboxConfig{
SandboxClass: ateapipb.SandboxClass_SANDBOX_CLASS_GVISOR,
SandboxClass: string(atev1alpha1.SandboxClassGvisor),
ConfigName: sandboxConfigName,
},
Resources: &ateapipb.Resources{Limits: []*ateapipb.Limits{
{Name: "cpu", Quantity: orDefault(cfg.cpu, defaultActorCPU)},
{Name: "memory", Quantity: orDefault(cfg.memory, defaultActorMemory)},
}},
}
}

func orDefault(v, def string) string {
if v == "" {
return def
}
return v
}

// buildTemplate returns the ActorTemplate for cfg: the guest template from
Expand Down
54 changes: 49 additions & 5 deletions cmd/ate-env/manifest_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -116,11 +116,10 @@ func TestBuildManifests(t *testing.T) {
if sc.Name != atev1alpha1.SandboxClassGvisor || sc.ConfigRef == nil || sc.ConfigRef.Name != sandboxConfigName {
t.Errorf("workerpool sandboxClasses[0] = %+v, want gvisor with configRef %q", sc, sandboxConfigName)
}
// The pool's class is a CRD string and the template's a proto enum: two
// vocabularies for one thing. A pool of the wrong class never hosts the
// template's actors.
tmplClass := strings.ToLower(strings.TrimPrefix(
buildActorTemplate(testTemplateConfig()).GetSandboxConfig().GetSandboxClass().String(), "SANDBOX_CLASS_"))
// The pool's class and the template's are the same string since
// substrate#2586 made the proto field a string. A pool of the wrong class
// never hosts the template's actors.
tmplClass := buildActorTemplate(testTemplateConfig()).GetSandboxConfig().GetSandboxClass()
if tmplClass != string(sc.Name) {
t.Errorf("workerpool sandbox class %q != template sandbox class %q", sc.Name, tmplClass)
}
Expand Down Expand Up @@ -176,6 +175,44 @@ func TestBuildActorTemplate(t *testing.T) {
if probe.GetHttpGet() == nil || probe.GetHttpGet().GetPath() != "/readyz" || probe.GetHttpGet().GetPort() != 80 {
t.Errorf("wakeupProbe = %+v, want HTTP GET /readyz on port 80", probe)
}
// Substrate rejects an ActorTemplate without both limits (substrate#2285).
if got := limits(template); got["cpu"] != defaultActorCPU || got["memory"] != defaultActorMemory || len(got) != 2 {
t.Errorf("resources.limits = %v, want cpu=%s memory=%s", got, defaultActorCPU, defaultActorMemory)
}
if got := template.GetSandboxConfig().GetSandboxClass(); got != "gvisor" {
t.Errorf("sandbox class = %q, want gvisor (Substrate accepts gvisor or microvm)", got)
}
}

func limits(tmpl *ateapipb.ActorTemplate) map[string]string {
m := map[string]string{}
for _, l := range tmpl.GetResources().GetLimits() {
m[l.GetName()] = l.GetQuantity()
}
return m
}

func TestBuildActorTemplateResources(t *testing.T) {
cfg := testTemplateConfig()
cfg.cpu, cfg.memory = "500m", "1Gi"
if got := limits(buildActorTemplate(cfg)); got["cpu"] != "500m" || got["memory"] != "1Gi" {
t.Errorf("resources.limits = %v, want cpu=500m memory=1Gi", got)
}
}

func TestTemplateConfigRejectsBadResources(t *testing.T) {
for _, tc := range []struct{ cpu, memory string }{
{"two", ""},
{"", "4GB!"},
{"0", ""},
{"", "-1Gi"},
} {
cfg := testTemplateConfig()
cfg.cpu, cfg.memory = tc.cpu, tc.memory
if err := cfg.resolveImages(); err == nil {
t.Errorf("cpu=%q memory=%q: want error, got nil", tc.cpu, tc.memory)
}
}
}

func TestBuildManifestsCustomAPIPort(t *testing.T) {
Expand Down Expand Up @@ -291,6 +328,8 @@ func TestManifestTemplateCommandExecution(t *testing.T) {
"template",
"--guest-image=example.com/guest@sha256:aaaa",
"--snapshots-bucket=gs://bucket/prefix/",
"--cpu=4",
"--memory=8Gi",
})
if err := cmd.Execute(); err != nil {
t.Fatalf("execute manifest template: %v", err)
Expand All @@ -299,6 +338,11 @@ func TestManifestTemplateCommandExecution(t *testing.T) {
if !strings.Contains(out, "image: example.com/guest@sha256:aaaa") || !strings.Contains(out, "storageLocation: gs://bucket/prefix/") {
t.Errorf("expected guest image and storage location in output:\n%s", out)
}
for _, want := range []string{"sandboxClass: gvisor", "name: cpu\n quantity: \"4\"", "name: memory\n quantity: 8Gi"} {
if !strings.Contains(out, want) {
t.Errorf("output lacks %q:\n%s", want, out)
}
}
}

const testHex64 = "0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef"
Expand Down
2 changes: 1 addition & 1 deletion docs/release.md
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@ and asks for both flags. `make build-cli` reproduces a release-style binary
locally from `ATE_ENV_API_IMAGE` and `ATE_ENV_GUEST_IMAGE`.

The worker image (`--worker-image`) is not published here: it is
`ateom-gvisor` from the [Substrate repo](https://github.com/agent-substrate/substrate)
`ateworker-gvisor` from the [Substrate repo](https://github.com/agent-substrate/substrate)
and must match the Substrate version deployed on the cluster. From a Substrate
checkout at that version, `go run ./cmd/ate-setup publish worker-images` builds
and pushes it and prints the digest-pinned ref. Use the digest: a registry's
Expand Down
2 changes: 1 addition & 1 deletion go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ module github.com/agent-substrate/env
go 1.27.0

require (
github.com/agent-substrate/substrate v0.4.1-0.20261013175625-da35a983ed34
github.com/agent-substrate/substrate v0.5.1-0.20261015061533-6edd1fccfc9d
github.com/google/uuid v1.6.0
github.com/modelcontextprotocol/go-sdk v1.8.0
github.com/spf13/cobra v1.10.2
Expand Down
4 changes: 2 additions & 2 deletions go.sum
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
github.com/Masterminds/semver/v3 v3.4.0 h1:Zog+i5UMtVoCU8oKka5P7i9q9HgrJeGzI9SA1Xbatp0=
github.com/Masterminds/semver/v3 v3.4.0/go.mod h1:4V+yj/TJE1HU9XfppCwVMZq3I84lprf4nC11bSS5beM=
github.com/agent-substrate/substrate v0.4.1-0.20261013175625-da35a983ed34 h1:pGiVJjwYbQMDJQj6le/4izVcIz0tHO1i2jQJ8EyKpCU=
github.com/agent-substrate/substrate v0.4.1-0.20261013175625-da35a983ed34/go.mod h1:XVQwhYEmOlb+ILs9BvSCHXYhgo2F6/N2qN4DmRwogQI=
github.com/agent-substrate/substrate v0.5.1-0.20261015061533-6edd1fccfc9d h1:LZW4RXPOy7+7b6K/eNw5jh6udMTdbkG26OuB2VHU1Ag=
github.com/agent-substrate/substrate v0.5.1-0.20261015061533-6edd1fccfc9d/go.mod h1:2Pm1s1pBoB1u4OycVtezqda2BVCjnwcmRQXobWTV3eQ=
github.com/beorn7/perks v1.0.1 h1:VlbKKnNfV8bJzeqoa4cOKqO6bYr3WgKZxO8Z16+hsOM=
github.com/beorn7/perks v1.0.1/go.mod h1:G2ZrVWU2WbWT9wwq4/hrbKbnv/1ERSJQ0ibhJ6rlkpw=
github.com/cespare/xxhash/v2 v2.3.0 h1:UL815xU9SqsFlibzuggzjXhog7bL6oX9BbNZnL2UFvs=
Expand Down
8 changes: 5 additions & 3 deletions integrations/nemo-gym/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -53,11 +53,13 @@ On the cluster:

```bash
ate-env manifest \
--guest-image <ate-env-guest image> \
--api-image <ate-env-api image> \
--ateom-image <ateom-gvisor image matching your substrate build> \
--snapshots-bucket gs://<bucket>/ate-env/ | kubectl apply -f -
--worker-image <ateworker-gvisor image matching your substrate build> | kubectl apply -f -
kubectl get pods -n ate-env # api + warm workers Running
# once ate-env-api is Running (it creates the ate-env atespace templates live in):
ate-env manifest template \
--guest-image <ate-env-guest image> \
--snapshots-bucket gs://<bucket>/ate-env/ | kubectl-ate create actor-template -f -
```

If your `ateapi` requires authentication (it does on any standard install), the
Expand Down
4 changes: 2 additions & 2 deletions internal/apiservice/imagetemplate_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -36,10 +36,10 @@ func modeABase() *ateapipb.ActorTemplate {
Image: testGuestImage,
Command: []string{"/ko-app/ate-env-guest", "-workspace", "/workspace"},
Env: []*ateapipb.EnvVar{{Name: "PORT", Value: "80"}},
WakeupProbe: &ateapipb.ContainerWakeupProbe{HttpGet: &ateapipb.HttpGetAction{Path: "/readyz", Port: 80}},
WakeupProbe: &ateapipb.WakeupProbe{HttpGet: &ateapipb.HttpGetAction{Path: "/readyz", Port: 80}},
}},
SnapshotConfig: &ateapipb.SnapshotConfig{StorageLocation: "gs://b/p/", PreferredFidelity: ateapipb.SnapshotFidelity_SNAPSHOT_FIDELITY_MEMORY},
SandboxConfig: &ateapipb.SandboxConfig{SandboxClass: ateapipb.SandboxClass_SANDBOX_CLASS_GVISOR, ConfigName: "gvisor-default"},
SandboxConfig: &ateapipb.SandboxConfig{SandboxClass: "gvisor", ConfigName: "gvisor-default"},
Status: &ateapipb.ActorTemplateStatus{},
}
}
Expand Down
8 changes: 4 additions & 4 deletions internal/apiservice/server.go
Original file line number Diff line number Diff line change
Expand Up @@ -524,11 +524,11 @@ func ActorStatusToEnvStatus(st *ateapipb.ActorStatus) ateenvv1alpha.EnvironmentS
case ateapipb.ActorState_ACTOR_STATE_SUSPENDING:
return ateenvv1alpha.EnvironmentStatus_ENVIRONMENT_STATUS_SUSPENDING
case ateapipb.ActorState_ACTOR_STATE_SUSPENDED:
// Since substrate#2620 a paused actor is SUSPENDED too, with a
// node-local latest snapshot; Substrate has no PAUSED or PAUSING
// state. ate-env never pauses, so ENVIRONMENT_STATUS_PAUSED and
// _PAUSING are no longer produced.
return ateenvv1alpha.EnvironmentStatus_ENVIRONMENT_STATUS_SUSPENDED
case ateapipb.ActorState_ACTOR_STATE_PAUSING:
return ateenvv1alpha.EnvironmentStatus_ENVIRONMENT_STATUS_PAUSING
case ateapipb.ActorState_ACTOR_STATE_PAUSED:
return ateenvv1alpha.EnvironmentStatus_ENVIRONMENT_STATUS_PAUSED
case ateapipb.ActorState_ACTOR_STATE_CRASHED:
return ateenvv1alpha.EnvironmentStatus_ENVIRONMENT_STATUS_CRASHED
case ateapipb.ActorState_ACTOR_STATE_DELETING:
Expand Down
6 changes: 2 additions & 4 deletions internal/apiservice/server_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -568,8 +568,6 @@ func TestActorStatusToEnvStatus(t *testing.T) {
{"running", &ateapipb.ActorStatus{State: ateapipb.ActorState_ACTOR_STATE_RUNNING}, ateenvv1alpha.EnvironmentStatus_ENVIRONMENT_STATUS_RUNNING},
{"suspending", &ateapipb.ActorStatus{State: ateapipb.ActorState_ACTOR_STATE_SUSPENDING}, ateenvv1alpha.EnvironmentStatus_ENVIRONMENT_STATUS_SUSPENDING},
{"suspended", &ateapipb.ActorStatus{State: ateapipb.ActorState_ACTOR_STATE_SUSPENDED}, ateenvv1alpha.EnvironmentStatus_ENVIRONMENT_STATUS_SUSPENDED},
{"pausing", &ateapipb.ActorStatus{State: ateapipb.ActorState_ACTOR_STATE_PAUSING}, ateenvv1alpha.EnvironmentStatus_ENVIRONMENT_STATUS_PAUSING},
{"paused", &ateapipb.ActorStatus{State: ateapipb.ActorState_ACTOR_STATE_PAUSED}, ateenvv1alpha.EnvironmentStatus_ENVIRONMENT_STATUS_PAUSED},
{"crashed", &ateapipb.ActorStatus{State: ateapipb.ActorState_ACTOR_STATE_CRASHED}, ateenvv1alpha.EnvironmentStatus_ENVIRONMENT_STATUS_CRASHED},
{"deleting", &ateapipb.ActorStatus{State: ateapipb.ActorState_ACTOR_STATE_DELETING}, ateenvv1alpha.EnvironmentStatus_ENVIRONMENT_STATUS_DELETING},
}
Expand All @@ -596,10 +594,10 @@ func seedGuestTemplate(control *fakecontrol.Server, atespace, name string) {
Image: testGuestImage,
Command: []string{"/ko-app/ate-env-guest"},
Env: []*ateapipb.EnvVar{{Name: "PORT", Value: "80"}},
WakeupProbe: &ateapipb.ContainerWakeupProbe{HttpGet: &ateapipb.HttpGetAction{Path: "/readyz", Port: 80}},
WakeupProbe: &ateapipb.WakeupProbe{HttpGet: &ateapipb.HttpGetAction{Path: "/readyz", Port: 80}},
}},
SnapshotConfig: &ateapipb.SnapshotConfig{StorageLocation: "gs://bucket/ate-env/"},
SandboxConfig: &ateapipb.SandboxConfig{SandboxClass: ateapipb.SandboxClass_SANDBOX_CLASS_GVISOR, ConfigName: "gvisor-default"},
SandboxConfig: &ateapipb.SandboxConfig{SandboxClass: "gvisor", ConfigName: "gvisor-default"},
})
}

Expand Down
3 changes: 2 additions & 1 deletion internal/internaltest/fakecontrol/fakecontrol.go
Original file line number Diff line number Diff line change
Expand Up @@ -283,7 +283,8 @@ func (s *Server) PauseActor(ctx context.Context, req *ateapipb.PauseActorRequest
if err != nil {
return nil, err
}
a.Status = &ateapipb.ActorStatus{State: ateapipb.ActorState_ACTOR_STATE_PAUSED}
// Substrate reports a paused actor as SUSPENDED (substrate#2620).
a.Status = &ateapipb.ActorStatus{State: ateapipb.ActorState_ACTOR_STATE_SUSPENDED}
return &ateapipb.PauseActorResponse{Actor: clone(a)}, nil
}

Expand Down
Loading