From 3cf1a551c8b5e06c03ce016a0185e373101b6527 Mon Sep 17 00:00:00 2001 From: Jaana Dogan Date: Fri, 4 Sep 2026 14:17:24 -0700 Subject: [PATCH] Update default atespace to ate-env and template name to default-template Update default ActorTemplate name and default atespace across the API service, CLI tools, SDKs, and proto definitions: - Change default atespace from "default" to "ate-env" - Change default template name from "default-env" to "default-template" - Update DefaultTemplate and DefaultAtespace constants in internal/apiservice and internal/ate - Update CLI flag defaults and template generator in cmd/ate-env - Update proto comments and regenerate Go and Python protobuf stubs - Update Python SDK DEFAULT_ATESPACE constant, docstrings, fakes, and tests - Update Go client and server test suites to reflect the new defaults - Update documentation and README references --- README.md | 17 +-- clients/go/client_test.go | 2 +- clients/python/README.md | 6 +- clients/python/src/ate_env/client.py | 6 +- clients/python/tests/e2e/test_full_stack.py | 4 +- clients/python/tests/fakes.py | 10 +- clients/python/tests/test_client.py | 8 +- clients/python/tests/test_env_files.py | 2 +- cmd/ate-env/main.go | 9 +- cmd/ate-env/manifest.go | 140 ++++++++++++-------- cmd/ate-env/manifest_test.go | 109 ++++++++++----- internal/apiservice/server.go | 4 +- internal/ate/client.go | 2 +- internal/ate/client_test.go | 2 +- internal/examples/env/main.go | 4 +- internal/mcp/server_test.go | 2 +- proto/ateenv/v1alpha/env.pb.go | 12 +- proto/ateenv/v1alpha/env.proto | 12 +- 18 files changed, 213 insertions(+), 138 deletions(-) diff --git a/README.md b/README.md index 4cdce66..f98fd7b 100644 --- a/README.md +++ b/README.md @@ -48,10 +48,8 @@ Deploy the namespace, worker pool, and API service: ```bash export GOOGLE_CLOUD_PROJECT=$(gcloud config get-value project) ate-env manifest \ - --guest-image gcr.io/$GOOGLE_CLOUD_PROJECT/ate-env-guest@sha256:4f5678b9304a9047551fc95458e7b948c77d6fce5337de1897888daa7e0e4900 \ - --api-image gcr.io/$GOOGLE_CLOUD_PROJECT/ate-env-api@sha256:8579e1eebdd652cd2bfc7a130d4ebc2ce0d7a1a9d2efcb9d04b9eedf597a027d \ - --worker-image gcr.io/$GOOGLE_CLOUD_PROJECT/ateom-gvisor-715889664656de67e44382a8d6ab981d@sha256:7a5f89e9c8ca875eee611b05fdf003b63b260b631362c83c1099073d003e0372 \ - --snapshots-bucket gs://$GOOGLE_CLOUD_PROJECT/ate-env/ | kubectl apply -f - + --api-image gcr.io/$GOOGLE_CLOUD_PROJECT/ate-env-api@sha256:e9c4481903d6ca2c8affbf4323f3cc2925d9eaac6513521535b3c905cdab236b \ + --worker-image gcr.io/$GOOGLE_CLOUD_PROJECT/ateom-gvisor-715889664656de67e44382a8d6ab981d@sha256:7a5f89e9c8ca875eee611b05fdf003b63b260b631362c83c1099073d003e0372 | kubectl apply -f - # Ensure that the pods are running: kubectl get pods -n ate-env @@ -59,11 +57,11 @@ kubectl get pods -n ate-env ### 2. Register the ActorTemplate in Substrate -Substrate manages ActorTemplates directly in its control plane rather than Kubernetes CRDs. Use `ate-env manifest --template-only` to generate the Substrate ActorTemplate manifest: +Substrate manages ActorTemplates directly in its control plane rather than Kubernetes CRDs. Use `ate-env manifest template` to generate the Substrate ActorTemplate manifest: ```bash -ate-env manifest --template-only \ - --guest-image gcr.io/$GOOGLE_CLOUD_PROJECT/ate-env-guest@sha256:4f5678b9304a9047551fc95458e7b948c77d6fce5337de1897888daa7e0e4900 \ +ate-env manifest template \ + --guest-image gcr.io/$GOOGLE_CLOUD_PROJECT/ate-env-guest@sha256:0b37ad8f0d6ae0bfdd01b97dfccd0139b59aedac6ec0f6d351333f0776acd3dd \ --snapshots-bucket gs://$GOOGLE_CLOUD_PROJECT/ate-env/ | kubectl-ate create actor-template -f - ``` @@ -139,9 +137,8 @@ Use "ate-env [command] --help" for more information about a command. $ ate-env manifest --help Manifest generates Kubernetes manifests for everything environments need on a cluster that already runs the Agent Substrate system: the target -namespace, a WorkerPool of pre-warmed workers, the ActorTemplate that -environments are created from, and the ate-env-api service. It prints YAML to -stdout without touching the cluster; apply it with kubectl. +namespace, a WorkerPool of pre-warmed workers, and the ate-env-api service. +It prints YAML to stdout without touching the cluster; apply it with kubectl. ``` ## API diff --git a/clients/go/client_test.go b/clients/go/client_test.go index 6778f45..4fd1d5d 100644 --- a/clients/go/client_test.go +++ b/clients/go/client_test.go @@ -112,7 +112,7 @@ func (f *fixture) create(t *testing.T, id string) *env.Env { sb, err := f.client.Create(t.Context(), &ateenvv1alpha.CreateEnvironmentRequest{ Id: id, Template: &ateenvv1alpha.Template{ - Name: "default-env", + Name: "default-template", Atespace: "envs", }, }) diff --git a/clients/python/README.md b/clients/python/README.md index 4aac688..bdaea8f 100644 --- a/clients/python/README.md +++ b/clients/python/README.md @@ -133,8 +133,8 @@ channel yourself (e.g. custom gRPC options), pass env = await client.create("dev1") ``` -The server instantiates the environment from the `default-env` -ActorTemplate in the `default` atespace unless you override it: +The server instantiates the environment from the `default-template` +ActorTemplate in the `ate-env` atespace unless you override it: ```python env = await client.create("dev1", template_name="my-template", @@ -144,7 +144,7 @@ env = await client.create("dev1", template_name="my-template", To get a handle to an environment that already exists (no RPC is made): ```python -env = client.env("dev1") # atespace defaults to "default" +env = client.env("dev1") # atespace defaults to "ate-env" ``` A freshly created (or suspended) environment starts serving on first diff --git a/clients/python/src/ate_env/client.py b/clients/python/src/ate_env/client.py index 19b98c5..c570545 100644 --- a/clients/python/src/ate_env/client.py +++ b/clients/python/src/ate_env/client.py @@ -12,7 +12,7 @@ __all__ = ["Client", "DEFAULT_ATESPACE"] -DEFAULT_ATESPACE = "default" +DEFAULT_ATESPACE = "ate-env" def _normalize_target(endpoint: str) -> str: @@ -77,8 +77,8 @@ async def create( ) -> Env: """Register and start a new environment; returns a handle to it. - The server fills defaults for the template (name "default-env" in - atespace "default") when none is given. + The server fills defaults for the template (name "default-template" in + atespace "ate-env") when none is given. """ req = env_pb2.CreateEnvironmentRequest(id=id, atespace=atespace) if template_name or template_atespace: diff --git a/clients/python/tests/e2e/test_full_stack.py b/clients/python/tests/e2e/test_full_stack.py index fc85996..257a309 100644 --- a/clients/python/tests/e2e/test_full_stack.py +++ b/clients/python/tests/e2e/test_full_stack.py @@ -33,7 +33,7 @@ TARGET = os.environ.get("ATE_ENV_API_TARGET") READY_TIMEOUT = float(os.environ.get("ATE_ENV_READY_TIMEOUT", "180")) -# Optional ActorTemplate override; the server default is "default-env". +# Optional ActorTemplate override; the server default is "default-template". TEMPLATE = os.environ.get("ATE_ENV_TEMPLATE") pytestmark = pytest.mark.skipif( @@ -90,7 +90,7 @@ async def test_full_lifecycle(client): env_id = f"pye2e-{uuid.uuid4().hex[:8]}" env = await client.create(env_id, template_name=TEMPLATE) assert env.id == env_id - assert env.atespace == "default" + assert env.atespace == "ate-env" try: info = await env.info() diff --git a/clients/python/tests/fakes.py b/clients/python/tests/fakes.py index cf5bcba..025df9c 100644 --- a/clients/python/tests/fakes.py +++ b/clients/python/tests/fakes.py @@ -22,7 +22,7 @@ async def _require_env(context: grpc.aio.ServicerContext) -> tuple[str, str]: env_id = md.get("x-env-id", "") if not env_id: await context.abort(grpc.StatusCode.INVALID_ARGUMENT, "x-env-id header is required") - return env_id, md.get("x-env-atespace", "default") + return env_id, md.get("x-env-atespace", "ate-env") class FakeEnvironmentService(env_pb2_grpc.EnvironmentServiceServicer): @@ -32,7 +32,7 @@ def __init__(self): async def CreateEnvironment(self, request, context): self.last_create_request = request - atespace = request.atespace or "default" + atespace = request.atespace or "ate-env" key = (atespace, request.id) if not request.id: await context.abort(grpc.StatusCode.INVALID_ARGUMENT, "id is required") @@ -41,7 +41,7 @@ async def CreateEnvironment(self, request, context): grpc.StatusCode.ALREADY_EXISTS, f'environment "{request.id}" already exists', ) - template = env_pb2.Template(name="default-env", atespace="default") + template = env_pb2.Template(name="default-template", atespace="ate-env") if request.HasField("template"): if request.template.name: template.name = request.template.name @@ -67,11 +67,11 @@ async def SuspendEnvironment(self, request, context): async def DeleteEnvironment(self, request, context): await self._lookup(request, context) - del self.environments[(request.atespace or "default", request.id)] + del self.environments[(request.atespace or "ate-env", request.id)] return env_pb2.DeleteEnvironmentResponse() async def _lookup(self, request, context): - key = (request.atespace or "default", request.id) + key = (request.atespace or "ate-env", request.id) environment = self.environments.get(key) if environment is None: await context.abort( diff --git a/clients/python/tests/test_client.py b/clients/python/tests/test_client.py index 19d23af..85b3b82 100644 --- a/clients/python/tests/test_client.py +++ b/clients/python/tests/test_client.py @@ -23,10 +23,10 @@ async def test_create_uses_server_defaults(fake_stack): client, fakes = fake_stack env = await client.create("dev1") assert env.id == "dev1" - assert env.atespace == "default" - stored = fakes.environments.environments[("default", "dev1")] - assert stored.template.name == "default-env" - assert stored.template.atespace == "default" + assert env.atespace == "ate-env" + stored = fakes.environments.environments[("ate-env", "dev1")] + assert stored.template.name == "default-template" + assert stored.template.atespace == "ate-env" async def test_create_omits_template_when_not_given(fake_stack): diff --git a/clients/python/tests/test_env_files.py b/clients/python/tests/test_env_files.py index 2d9b5c2..7c79d03 100644 --- a/clients/python/tests/test_env_files.py +++ b/clients/python/tests/test_env_files.py @@ -16,7 +16,7 @@ async def test_read_file_multi_chunk(fake_stack): chunks = [chunk async for chunk in env.read_file("/data.bin")] assert len(chunks) >= 3 assert b"".join(chunks) == content - assert fakes.filesystem.last_env == ("dev1", "default") + assert fakes.filesystem.last_env == ("dev1", "ate-env") async def test_read_file_bytes(fake_stack): diff --git a/cmd/ate-env/main.go b/cmd/ate-env/main.go index c7a761f..a10c30c 100644 --- a/cmd/ate-env/main.go +++ b/cmd/ate-env/main.go @@ -13,6 +13,7 @@ import ( "strings" "github.com/agent-substrate/env/clients/go" + "github.com/agent-substrate/env/internal/apiservice" ateenvv1alpha "github.com/agent-substrate/env/proto/ateenv/v1alpha" "github.com/spf13/cobra" ) @@ -48,7 +49,7 @@ func newGuestCommand(id string) *cobra.Command { }, } guestCmd.PersistentFlags().StringVar(&endpoint, "api", envOr("SUBSTRATE_ENV_API", "127.0.0.1:7777"), "address of the ate-env-api service (e.g. localhost:7777)") - guestCmd.PersistentFlags().StringVar(&atespace, "atespace", "default", "Substrate atespace") + guestCmd.PersistentFlags().StringVar(&atespace, "atespace", apiservice.DefaultAtespace, "Substrate atespace") guestCmd.AddCommand(&cobra.Command{ Use: "read ", @@ -139,7 +140,7 @@ func newCreateCommand() *cobra.Command { }, } cmd.Flags().StringVar(&endpoint, "api", envOr("SUBSTRATE_ENV_API", "127.0.0.1:7777"), "address of the ate-env-api service (e.g. localhost:7777)") - cmd.Flags().StringVar(&atespace, "atespace", "default", "Substrate atespace") + cmd.Flags().StringVar(&atespace, "atespace", apiservice.DefaultAtespace, "Substrate atespace") cmd.Flags().StringVar(&createTemplate, "template", "", "ActorTemplate name (defaults to server default)") cmd.Flags().StringVar(&createTemplateAtespace, "template-atespace", "", "Substrate atespace of the ActorTemplate (defaults to environment atespace)") return cmd @@ -167,7 +168,7 @@ func newSuspendCommand() *cobra.Command { }, } cmd.Flags().StringVar(&endpoint, "api", envOr("SUBSTRATE_ENV_API", "127.0.0.1:7777"), "address of the ate-env-api service (e.g. localhost:7777)") - cmd.Flags().StringVar(&atespace, "atespace", "default", "Substrate atespace") + cmd.Flags().StringVar(&atespace, "atespace", apiservice.DefaultAtespace, "Substrate atespace") return cmd } @@ -193,7 +194,7 @@ func newDeleteCommand() *cobra.Command { }, } cmd.Flags().StringVar(&endpoint, "api", envOr("SUBSTRATE_ENV_API", "127.0.0.1:7777"), "address of the ate-env-api service (e.g. localhost:7777)") - cmd.Flags().StringVar(&atespace, "atespace", "default", "Substrate atespace") + cmd.Flags().StringVar(&atespace, "atespace", apiservice.DefaultAtespace, "Substrate atespace") return cmd } diff --git a/cmd/ate-env/manifest.go b/cmd/ate-env/manifest.go index f4b9594..4f31b41 100644 --- a/cmd/ate-env/manifest.go +++ b/cmd/ate-env/manifest.go @@ -22,25 +22,50 @@ import ( // apiName is the name of the API service Deployment and Service. const apiName = "ate-env-api" +// manifestConfig holds configuration for generating Kubernetes deployment manifests. type manifestConfig struct { - namespace string + namespace string + template string + workerPool string + workerImage string + replicas int32 + apiImage string + apiReplicas int32 + apiPort int32 + poolLabels map[string]string +} + +func (c *manifestConfig) resolveImages() error { + if c.workerImage == "" || c.apiImage == "" { + return errors.New(`--api-image and --worker-image (or --ateom-image) are required; use the +digest-pinned images published by the latest release (the README +quickstart records them), or build and push your own.`) + } + return nil +} + +// templateConfig holds configuration for generating a Substrate ActorTemplate manifest. +type templateConfig struct { template string - workerPool string + atespace string guestImage string - workerImage string - snapshotsBucket string - replicas int32 - apiImage string - apiReplicas int32 - apiPort int32 guestCommand []string + snapshotsBucket string poolLabels map[string]string - atespace string - templateOnly bool +} + +func (c *templateConfig) resolveImages() error { + if c.guestImage == "" { + return errors.New("--guest-image is required; use the digest-pinned ate-env-guest image") + } + if c.snapshotsBucket == "" { + return errors.New("--snapshots-bucket is required; use an object-storage bucket (e.g. gs://bucket/prefix/)") + } + return nil } func newManifestCommand() *cobra.Command { - cfg := manifestConfig{} + mCfg := manifestConfig{} cmd := &cobra.Command{ Use: "manifest", @@ -51,61 +76,66 @@ namespace, a WorkerPool of pre-warmed workers, and the ate-env-api service. It prints YAML to stdout without touching the cluster; apply it with kubectl. To generate the Substrate ActorTemplate manifest (for kubectl-ate), use -the --template-only flag.`, +the "template" subcommand: ate-env manifest template`, Args: cobra.NoArgs, RunE: func(cmd *cobra.Command, args []string) error { - if err := cfg.resolveImages(); err != nil { - return err - } - if cfg.template == "" { - cfg.template = apiservice.DefaultTemplate + if mCfg.template == "" { + mCfg.template = apiservice.DefaultTemplate } - if cfg.workerPool == "" { - cfg.workerPool = cfg.template + "-workerpool" + if mCfg.workerPool == "" { + mCfg.workerPool = mCfg.template + "-workerpool" } - cfg.poolLabels = map[string]string{"workload": cfg.template} - - if cfg.templateOnly { - return writeActorTemplate(cmd.OutOrStdout(), buildActorTemplate(cfg)) + mCfg.poolLabels = map[string]string{"workload": mCfg.template} + if err := mCfg.resolveImages(); err != nil { + return err } - return writeManifests(cmd.OutOrStdout(), buildManifests(cfg)) + return writeManifests(cmd.OutOrStdout(), buildManifests(mCfg)) }, } - cmd.Flags().StringVar(&cfg.namespace, "namespace", apiservice.DefaultNamespace, "Kubernetes namespace to deploy into") - cmd.Flags().StringVar(&cfg.atespace, "atespace", "default", "Substrate atespace for the ActorTemplate") - cmd.Flags().StringVar(&cfg.template, "template", apiservice.DefaultTemplate, "ActorTemplate name") - cmd.Flags().StringVar(&cfg.guestImage, "guest-image", "", "digest-pinned ate-env-guest image (repo@sha256:...)") - cmd.Flags().StringVar(&cfg.workerImage, "worker-image", "", "digest-pinned worker image for the worker pool, e.g. ateom-gvisor built from the Substrate repo") - cmd.Flags().StringVar(&cfg.workerImage, "ateom-image", "", "alias for --worker-image") - cmd.Flags().StringVar(&cfg.snapshotsBucket, "snapshots-bucket", "", "object-storage bucket (with optional prefix) for actor snapshots, e.g. gs://bucket/prefix/") - cmd.Flags().StringVar(&cfg.apiImage, "api-image", "", "digest-pinned ate-env-api image for the API service") - cmd.Flags().Int32Var(&cfg.apiReplicas, "api-replicas", 1, "number of API service replicas") - cmd.Flags().Int32Var(&cfg.apiPort, "api-port", 7777, "port the ate-env-api service listens on") - cmd.Flags().StringVar(&cfg.workerPool, "workerpool", "", "WorkerPool name (defaults to