diff --git a/.github/workflows/helm-e2e.yaml b/.github/workflows/helm-e2e.yaml index 62edbf740e..272d6d9b60 100644 --- a/.github/workflows/helm-e2e.yaml +++ b/.github/workflows/helm-e2e.yaml @@ -16,7 +16,7 @@ name: helm-e2e on: pull_request: push: - branches: [main] + branches: [main, release-1.0] permissions: contents: read jobs: @@ -103,6 +103,7 @@ jobs: --set 'atenetRouter.ingressAllowedSPIFFEIDs[0]=spiffe://cluster.local/ns/ate-e2e-router-client/sa/router-client' \ --set 'atelet.extraArgs[0]=--localhost-registry-replacement=kind-registry:5000' \ --set 'atelet.extraArgs[1]=--snapshot-upload-delay=2s' \ + --set 'atelet.extraArgs[2]=--snapshot-detach-delay=30s' \ --set otel.endpoint=http://opentelemetry-collector.otel-system.svc:4317 \ --wait --timeout=10m - name: Enable NFS diff --git a/.github/workflows/helm-verify.yaml b/.github/workflows/helm-verify.yaml index 8d6dc6e918..9ec0ad0578 100644 --- a/.github/workflows/helm-verify.yaml +++ b/.github/workflows/helm-verify.yaml @@ -20,7 +20,7 @@ name: helm-verify on: pull_request: push: - branches: [main] + branches: [main, release-1.0] permissions: contents: read jobs: diff --git a/.github/workflows/pr-workflow.yaml b/.github/workflows/pr-workflow.yaml index 97d2912653..fdf3639db0 100644 --- a/.github/workflows/pr-workflow.yaml +++ b/.github/workflows/pr-workflow.yaml @@ -17,7 +17,7 @@ on: pull_request: merge_group: push: - branches: [main] + branches: [main, release-1.0] schedule: # Weekly run on the default branch keeps the micro-VM asset cache warm (GitHub # evicts caches idle for 7 days). The push-to-main run populates the cache that diff --git a/.ko.yaml b/.ko.yaml index 80d54c7973..6465081548 100644 --- a/.ko.yaml +++ b/.ko.yaml @@ -19,6 +19,15 @@ defaultPlatforms: - linux/arm64 baseImageOverrides: + # The actor images built here write at the root of their filesystem and into + # volumes atelet creates as root. A container runs as its image's USER, so + # they stay on the root base. + go.ate.dev/substrate/demos/counter: gcr.io/distroless/static-debian13:latest@sha256:15f8637509792e05b97d83451b0b6ccfa4abd548f997e406ac1e72c87fe1fa78 + go.ate.dev/substrate/demos/egress: gcr.io/distroless/static-debian13:latest@sha256:15f8637509792e05b97d83451b0b6ccfa4abd548f997e406ac1e72c87fe1fa78 + go.ate.dev/substrate/demos/multi-template/fspersist: gcr.io/distroless/static-debian13:latest@sha256:15f8637509792e05b97d83451b0b6ccfa4abd548f997e406ac1e72c87fe1fa78 + go.ate.dev/substrate/internal/e2e/fixtures/probe: gcr.io/distroless/static-debian13:latest@sha256:15f8637509792e05b97d83451b0b6ccfa4abd548f997e406ac1e72c87fe1fa78 + go.ate.dev/substrate/internal/e2e/fixtures/testserver: gcr.io/distroless/static-debian13:latest@sha256:15f8637509792e05b97d83451b0b6ccfa4abd548f997e406ac1e72c87fe1fa78 + go.ate.dev/substrate/cmd/benchmarking/glutton: gcr.io/distroless/static-debian13:latest@sha256:15f8637509792e05b97d83451b0b6ccfa4abd548f997e406ac1e72c87fe1fa78 go.ate.dev/substrate/demos/sandbox: alpine:3.24.3@sha256:be2656061b536c1883c4034790e21d0a09c5aaaf51968b89368a3c0371ea2c9a # atelet needs glibc to run the node's kubelet credential provider, which # some platforms ship dynamically linked (e.g. AKS, GKE Ubuntu). diff --git a/README.md b/README.md index d6dd7a40ea..2b662472a4 100644 --- a/README.md +++ b/README.md @@ -10,14 +10,24 @@ **Zero-trust sandboxing for millions of agents on Kubernetes.** [![License](https://img.shields.io/badge/License-Apache_2.0-blue.svg)](https://opensource.org/licenses/Apache-2.0) +[![Release](https://img.shields.io/github/v/release/agent-substrate/substrate)](https://github.com/agent-substrate/substrate/releases) +[![CI](https://github.com/agent-substrate/substrate/actions/workflows/pr-workflow.yaml/badge.svg?branch=main)](https://github.com/agent-substrate/substrate/actions/workflows/pr-workflow.yaml?query=branch%3Amain) +[![govulncheck](https://github.com/agent-substrate/substrate/actions/workflows/govulncheck.yaml/badge.svg)](https://github.com/agent-substrate/substrate/actions/workflows/govulncheck.yaml) +[![GitHub stars](https://img.shields.io/github/stars/agent-substrate/substrate?style=flat)](https://github.com/agent-substrate/substrate/stargazers) + +agent-substrate/substrate on Trendshift: #1 repository of the day [Quickstart](#quickstart-development) · [Security](docs/security/security.md) · [Architecture](docs/concepts/architecture.md) · [Contributing](CONTRIBUTING.md) + + Agents are idle most of the time. Agent Substrate suspends idle agents and resumes them on request onto a small pool of warm worker pods. + + ## What is Agent Substrate? -Agent Substrate is a security-hardened, zero-trust runtime for sandboxed agents, engineered to safely run millions of agents and tools with 10x higher density than standard containers. Purpose-built for the era of autonomous agents, Substrate delivers fast resume operations in less than 400ms, with more than 1500 activations per second. Agents get isolated compute, identity, networking, and storage. Sensitive credentials are kept out of reach from untrusted code. It supports multiple sandbox technologies, including microVMs and gVisor, while enabling a consistent lifecycle and management of all sandboxes. +Agent Substrate is a security-hardened, zero-trust runtime for sandboxed agents, engineered to safely run millions of agents and tools with 10x higher density than standard containers. Purpose-built for the era of autonomous agents, Substrate delivers fast resume operations in less than 500ms, with more than 500 activations per second. Agents get isolated compute, identity, networking, and storage. Sensitive credentials are kept out of reach from untrusted code. It supports multiple sandbox technologies, including microVMs and gVisor, while enabling a consistent lifecycle and management of all sandboxes. At its core, Agent Substrate maps a larger set of “actors” (stateful workloads such as agents) onto a smaller pool of ready “workers”. Because agents spend most of their lives waiting on models, tools, or humans, Substrate snapshots idle actors out of memory and restores them when a request arrives. diff --git a/api/ateapi/v1/ateapi.pb.go b/api/ateapi/v1/ateapi.pb.go index 44de8a372c..cb23d97db8 100644 --- a/api/ateapi/v1/ateapi.pb.go +++ b/api/ateapi/v1/ateapi.pb.go @@ -3135,7 +3135,10 @@ type SnapshotConfig struct { // storage_location is the base object-storage URI snapshots of actors on // this version are stored under. If unset, ate-api-server's // --snapshot-location is used. Requires the PerTemplateSnapshotLocation preview - // gate. + // gate. Each path segment may contain only ASCII letters, digits, '_' and + // '-'. Each owner's snapshots are stored under + // /atespaces/..., so no segment may be "atespaces" in any + // letter case. // // +ate:preview=PerTemplateSnapshotLocation // +k8s:ifDisabled(PerTemplateSnapshotLocation)=+k8s:forbidden @@ -5943,131 +5946,6 @@ func (x *MintActorJwtResponse) GetExpireTime() *timestamppb.Timestamp { return nil } -// Request message for MintActorCertificate. -type MintActorCertificateRequest struct { - state protoimpl.MessageState `protogen:"open.v1"` - // The actor for which the certificate should be issued. - // - // Must be a valid actor that currently exists according to the actor store. - // - // +k8s:required - Actor *ObjectRef `protobuf:"bytes,1,opt,name=actor,proto3" json:"actor,omitempty"` - // The UID of the actor --- used to guard against deletion and recreation of - // an actor with the same name. - // - // +k8s:required - // +k8s:format=k8s-uuid - ActorUid string `protobuf:"bytes,2,opt,name=actor_uid,json=actorUid,proto3" json:"actor_uid,omitempty"` - // Request contains DER encoded bytes of a x509 certificate signing request. - // The signer will ignore the contents of the CSR except to extract the - // subject public key. - // - // +k8s:required - // +k8s:maxBytes=16384 - CertificateSigningRequest []byte `protobuf:"bytes,3,opt,name=certificate_signing_request,json=certificateSigningRequest,proto3" json:"certificate_signing_request,omitempty"` - unknownFields protoimpl.UnknownFields - sizeCache protoimpl.SizeCache -} - -func (x *MintActorCertificateRequest) Reset() { - *x = MintActorCertificateRequest{} - mi := &file_api_ateapi_v1_ateapi_proto_msgTypes[78] - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - ms.StoreMessageInfo(mi) -} - -func (x *MintActorCertificateRequest) String() string { - return protoimpl.X.MessageStringOf(x) -} - -func (*MintActorCertificateRequest) ProtoMessage() {} - -func (x *MintActorCertificateRequest) ProtoReflect() protoreflect.Message { - mi := &file_api_ateapi_v1_ateapi_proto_msgTypes[78] - if x != nil { - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - if ms.LoadMessageInfo() == nil { - ms.StoreMessageInfo(mi) - } - return ms - } - return mi.MessageOf(x) -} - -// Deprecated: Use MintActorCertificateRequest.ProtoReflect.Descriptor instead. -func (*MintActorCertificateRequest) Descriptor() ([]byte, []int) { - return file_api_ateapi_v1_ateapi_proto_rawDescGZIP(), []int{78} -} - -func (x *MintActorCertificateRequest) GetActor() *ObjectRef { - if x != nil { - return x.Actor - } - return nil -} - -func (x *MintActorCertificateRequest) GetActorUid() string { - if x != nil { - return x.ActorUid - } - return "" -} - -func (x *MintActorCertificateRequest) GetCertificateSigningRequest() []byte { - if x != nil { - return x.CertificateSigningRequest - } - return nil -} - -// Response message for MintActorCertificate. -type MintActorCertificateResponse struct { - state protoimpl.MessageState `protogen:"open.v1"` - // Response contains a list of DER encoded certificates. The first entry is the - // leaf certificate, and any remaining entries are intermediates in - // leaf-to-root order. - ActorCertificates [][]byte `protobuf:"bytes,1,rep,name=actor_certificates,json=actorCertificates,proto3" json:"actor_certificates,omitempty"` - unknownFields protoimpl.UnknownFields - sizeCache protoimpl.SizeCache -} - -func (x *MintActorCertificateResponse) Reset() { - *x = MintActorCertificateResponse{} - mi := &file_api_ateapi_v1_ateapi_proto_msgTypes[79] - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - ms.StoreMessageInfo(mi) -} - -func (x *MintActorCertificateResponse) String() string { - return protoimpl.X.MessageStringOf(x) -} - -func (*MintActorCertificateResponse) ProtoMessage() {} - -func (x *MintActorCertificateResponse) ProtoReflect() protoreflect.Message { - mi := &file_api_ateapi_v1_ateapi_proto_msgTypes[79] - if x != nil { - ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) - if ms.LoadMessageInfo() == nil { - ms.StoreMessageInfo(mi) - } - return ms - } - return mi.MessageOf(x) -} - -// Deprecated: Use MintActorCertificateResponse.ProtoReflect.Descriptor instead. -func (*MintActorCertificateResponse) Descriptor() ([]byte, []int) { - return file_api_ateapi_v1_ateapi_proto_rawDescGZIP(), []int{79} -} - -func (x *MintActorCertificateResponse) GetActorCertificates() [][]byte { - if x != nil { - return x.ActorCertificates - } - return nil -} - type ListTagsRequest struct { state protoimpl.MessageState `protogen:"open.v1"` // The atespace to list tags from. Empty lists across all atespaces. @@ -6085,7 +5963,7 @@ type ListTagsRequest struct { func (x *ListTagsRequest) Reset() { *x = ListTagsRequest{} - mi := &file_api_ateapi_v1_ateapi_proto_msgTypes[80] + mi := &file_api_ateapi_v1_ateapi_proto_msgTypes[78] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -6097,7 +5975,7 @@ func (x *ListTagsRequest) String() string { func (*ListTagsRequest) ProtoMessage() {} func (x *ListTagsRequest) ProtoReflect() protoreflect.Message { - mi := &file_api_ateapi_v1_ateapi_proto_msgTypes[80] + mi := &file_api_ateapi_v1_ateapi_proto_msgTypes[78] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -6110,7 +5988,7 @@ func (x *ListTagsRequest) ProtoReflect() protoreflect.Message { // Deprecated: Use ListTagsRequest.ProtoReflect.Descriptor instead. func (*ListTagsRequest) Descriptor() ([]byte, []int) { - return file_api_ateapi_v1_ateapi_proto_rawDescGZIP(), []int{80} + return file_api_ateapi_v1_ateapi_proto_rawDescGZIP(), []int{78} } func (x *ListTagsRequest) GetAtespace() string { @@ -6137,7 +6015,7 @@ type ListTagsResponse struct { func (x *ListTagsResponse) Reset() { *x = ListTagsResponse{} - mi := &file_api_ateapi_v1_ateapi_proto_msgTypes[81] + mi := &file_api_ateapi_v1_ateapi_proto_msgTypes[79] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -6149,7 +6027,7 @@ func (x *ListTagsResponse) String() string { func (*ListTagsResponse) ProtoMessage() {} func (x *ListTagsResponse) ProtoReflect() protoreflect.Message { - mi := &file_api_ateapi_v1_ateapi_proto_msgTypes[81] + mi := &file_api_ateapi_v1_ateapi_proto_msgTypes[79] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -6162,7 +6040,7 @@ func (x *ListTagsResponse) ProtoReflect() protoreflect.Message { // Deprecated: Use ListTagsResponse.ProtoReflect.Descriptor instead. func (*ListTagsResponse) Descriptor() ([]byte, []int) { - return file_api_ateapi_v1_ateapi_proto_rawDescGZIP(), []int{81} + return file_api_ateapi_v1_ateapi_proto_rawDescGZIP(), []int{79} } func (x *ListTagsResponse) GetTags() []*Tag { @@ -6207,7 +6085,7 @@ type CreateTagRequest struct { func (x *CreateTagRequest) Reset() { *x = CreateTagRequest{} - mi := &file_api_ateapi_v1_ateapi_proto_msgTypes[82] + mi := &file_api_ateapi_v1_ateapi_proto_msgTypes[80] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -6219,7 +6097,7 @@ func (x *CreateTagRequest) String() string { func (*CreateTagRequest) ProtoMessage() {} func (x *CreateTagRequest) ProtoReflect() protoreflect.Message { - mi := &file_api_ateapi_v1_ateapi_proto_msgTypes[82] + mi := &file_api_ateapi_v1_ateapi_proto_msgTypes[80] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -6232,7 +6110,7 @@ func (x *CreateTagRequest) ProtoReflect() protoreflect.Message { // Deprecated: Use CreateTagRequest.ProtoReflect.Descriptor instead. func (*CreateTagRequest) Descriptor() ([]byte, []int) { - return file_api_ateapi_v1_ateapi_proto_rawDescGZIP(), []int{82} + return file_api_ateapi_v1_ateapi_proto_rawDescGZIP(), []int{80} } func (x *CreateTagRequest) GetTag() *Tag { @@ -6262,7 +6140,7 @@ type UpdateTagRequest struct { func (x *UpdateTagRequest) Reset() { *x = UpdateTagRequest{} - mi := &file_api_ateapi_v1_ateapi_proto_msgTypes[83] + mi := &file_api_ateapi_v1_ateapi_proto_msgTypes[81] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -6274,7 +6152,7 @@ func (x *UpdateTagRequest) String() string { func (*UpdateTagRequest) ProtoMessage() {} func (x *UpdateTagRequest) ProtoReflect() protoreflect.Message { - mi := &file_api_ateapi_v1_ateapi_proto_msgTypes[83] + mi := &file_api_ateapi_v1_ateapi_proto_msgTypes[81] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -6287,7 +6165,7 @@ func (x *UpdateTagRequest) ProtoReflect() protoreflect.Message { // Deprecated: Use UpdateTagRequest.ProtoReflect.Descriptor instead. func (*UpdateTagRequest) Descriptor() ([]byte, []int) { - return file_api_ateapi_v1_ateapi_proto_rawDescGZIP(), []int{83} + return file_api_ateapi_v1_ateapi_proto_rawDescGZIP(), []int{81} } func (x *UpdateTagRequest) GetTag() *Tag { @@ -6312,7 +6190,7 @@ type DeleteTagRequest struct { func (x *DeleteTagRequest) Reset() { *x = DeleteTagRequest{} - mi := &file_api_ateapi_v1_ateapi_proto_msgTypes[84] + mi := &file_api_ateapi_v1_ateapi_proto_msgTypes[82] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -6324,7 +6202,7 @@ func (x *DeleteTagRequest) String() string { func (*DeleteTagRequest) ProtoMessage() {} func (x *DeleteTagRequest) ProtoReflect() protoreflect.Message { - mi := &file_api_ateapi_v1_ateapi_proto_msgTypes[84] + mi := &file_api_ateapi_v1_ateapi_proto_msgTypes[82] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -6337,7 +6215,7 @@ func (x *DeleteTagRequest) ProtoReflect() protoreflect.Message { // Deprecated: Use DeleteTagRequest.ProtoReflect.Descriptor instead. func (*DeleteTagRequest) Descriptor() ([]byte, []int) { - return file_api_ateapi_v1_ateapi_proto_rawDescGZIP(), []int{84} + return file_api_ateapi_v1_ateapi_proto_rawDescGZIP(), []int{82} } func (x *DeleteTagRequest) GetTag() *ObjectRef { @@ -6376,7 +6254,7 @@ type DeleteOptions struct { func (x *DeleteOptions) Reset() { *x = DeleteOptions{} - mi := &file_api_ateapi_v1_ateapi_proto_msgTypes[85] + mi := &file_api_ateapi_v1_ateapi_proto_msgTypes[83] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -6388,7 +6266,7 @@ func (x *DeleteOptions) String() string { func (*DeleteOptions) ProtoMessage() {} func (x *DeleteOptions) ProtoReflect() protoreflect.Message { - mi := &file_api_ateapi_v1_ateapi_proto_msgTypes[85] + mi := &file_api_ateapi_v1_ateapi_proto_msgTypes[83] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -6401,7 +6279,7 @@ func (x *DeleteOptions) ProtoReflect() protoreflect.Message { // Deprecated: Use DeleteOptions.ProtoReflect.Descriptor instead. func (*DeleteOptions) Descriptor() ([]byte, []int) { - return file_api_ateapi_v1_ateapi_proto_rawDescGZIP(), []int{85} + return file_api_ateapi_v1_ateapi_proto_rawDescGZIP(), []int{83} } func (x *DeleteOptions) GetVersion() int64 { @@ -6440,7 +6318,7 @@ type ListOptions struct { func (x *ListOptions) Reset() { *x = ListOptions{} - mi := &file_api_ateapi_v1_ateapi_proto_msgTypes[86] + mi := &file_api_ateapi_v1_ateapi_proto_msgTypes[84] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -6452,7 +6330,7 @@ func (x *ListOptions) String() string { func (*ListOptions) ProtoMessage() {} func (x *ListOptions) ProtoReflect() protoreflect.Message { - mi := &file_api_ateapi_v1_ateapi_proto_msgTypes[86] + mi := &file_api_ateapi_v1_ateapi_proto_msgTypes[84] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -6465,7 +6343,7 @@ func (x *ListOptions) ProtoReflect() protoreflect.Message { // Deprecated: Use ListOptions.ProtoReflect.Descriptor instead. func (*ListOptions) Descriptor() ([]byte, []int) { - return file_api_ateapi_v1_ateapi_proto_rawDescGZIP(), []int{86} + return file_api_ateapi_v1_ateapi_proto_rawDescGZIP(), []int{84} } func (x *ListOptions) GetPageSize() int32 { @@ -6501,7 +6379,7 @@ type ListWorkerActorAssignmentsRequest struct { func (x *ListWorkerActorAssignmentsRequest) Reset() { *x = ListWorkerActorAssignmentsRequest{} - mi := &file_api_ateapi_v1_ateapi_proto_msgTypes[87] + mi := &file_api_ateapi_v1_ateapi_proto_msgTypes[85] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -6513,7 +6391,7 @@ func (x *ListWorkerActorAssignmentsRequest) String() string { func (*ListWorkerActorAssignmentsRequest) ProtoMessage() {} func (x *ListWorkerActorAssignmentsRequest) ProtoReflect() protoreflect.Message { - mi := &file_api_ateapi_v1_ateapi_proto_msgTypes[87] + mi := &file_api_ateapi_v1_ateapi_proto_msgTypes[85] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -6526,7 +6404,7 @@ func (x *ListWorkerActorAssignmentsRequest) ProtoReflect() protoreflect.Message // Deprecated: Use ListWorkerActorAssignmentsRequest.ProtoReflect.Descriptor instead. func (*ListWorkerActorAssignmentsRequest) Descriptor() ([]byte, []int) { - return file_api_ateapi_v1_ateapi_proto_rawDescGZIP(), []int{87} + return file_api_ateapi_v1_ateapi_proto_rawDescGZIP(), []int{85} } func (x *ListWorkerActorAssignmentsRequest) GetWorker() *ObjectRef { @@ -6556,7 +6434,7 @@ type ListWorkerActorAssignmentsResponse struct { func (x *ListWorkerActorAssignmentsResponse) Reset() { *x = ListWorkerActorAssignmentsResponse{} - mi := &file_api_ateapi_v1_ateapi_proto_msgTypes[88] + mi := &file_api_ateapi_v1_ateapi_proto_msgTypes[86] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -6568,7 +6446,7 @@ func (x *ListWorkerActorAssignmentsResponse) String() string { func (*ListWorkerActorAssignmentsResponse) ProtoMessage() {} func (x *ListWorkerActorAssignmentsResponse) ProtoReflect() protoreflect.Message { - mi := &file_api_ateapi_v1_ateapi_proto_msgTypes[88] + mi := &file_api_ateapi_v1_ateapi_proto_msgTypes[86] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -6581,7 +6459,7 @@ func (x *ListWorkerActorAssignmentsResponse) ProtoReflect() protoreflect.Message // Deprecated: Use ListWorkerActorAssignmentsResponse.ProtoReflect.Descriptor instead. func (*ListWorkerActorAssignmentsResponse) Descriptor() ([]byte, []int) { - return file_api_ateapi_v1_ateapi_proto_rawDescGZIP(), []int{88} + return file_api_ateapi_v1_ateapi_proto_rawDescGZIP(), []int{86} } func (x *ListWorkerActorAssignmentsResponse) GetActorAssignments() []*ActorAssignment { @@ -6610,7 +6488,7 @@ type ListWorkersRequest struct { func (x *ListWorkersRequest) Reset() { *x = ListWorkersRequest{} - mi := &file_api_ateapi_v1_ateapi_proto_msgTypes[89] + mi := &file_api_ateapi_v1_ateapi_proto_msgTypes[87] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -6622,7 +6500,7 @@ func (x *ListWorkersRequest) String() string { func (*ListWorkersRequest) ProtoMessage() {} func (x *ListWorkersRequest) ProtoReflect() protoreflect.Message { - mi := &file_api_ateapi_v1_ateapi_proto_msgTypes[89] + mi := &file_api_ateapi_v1_ateapi_proto_msgTypes[87] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -6635,7 +6513,7 @@ func (x *ListWorkersRequest) ProtoReflect() protoreflect.Message { // Deprecated: Use ListWorkersRequest.ProtoReflect.Descriptor instead. func (*ListWorkersRequest) Descriptor() ([]byte, []int) { - return file_api_ateapi_v1_ateapi_proto_rawDescGZIP(), []int{89} + return file_api_ateapi_v1_ateapi_proto_rawDescGZIP(), []int{87} } func (x *ListWorkersRequest) GetOptions() *ListOptions { @@ -6657,7 +6535,7 @@ type ListWorkersResponse struct { func (x *ListWorkersResponse) Reset() { *x = ListWorkersResponse{} - mi := &file_api_ateapi_v1_ateapi_proto_msgTypes[90] + mi := &file_api_ateapi_v1_ateapi_proto_msgTypes[88] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -6669,7 +6547,7 @@ func (x *ListWorkersResponse) String() string { func (*ListWorkersResponse) ProtoMessage() {} func (x *ListWorkersResponse) ProtoReflect() protoreflect.Message { - mi := &file_api_ateapi_v1_ateapi_proto_msgTypes[90] + mi := &file_api_ateapi_v1_ateapi_proto_msgTypes[88] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -6682,7 +6560,7 @@ func (x *ListWorkersResponse) ProtoReflect() protoreflect.Message { // Deprecated: Use ListWorkersResponse.ProtoReflect.Descriptor instead. func (*ListWorkersResponse) Descriptor() ([]byte, []int) { - return file_api_ateapi_v1_ateapi_proto_rawDescGZIP(), []int{90} + return file_api_ateapi_v1_ateapi_proto_rawDescGZIP(), []int{88} } func (x *ListWorkersResponse) GetWorkers() []*Worker { @@ -6712,7 +6590,7 @@ type GetWorkerRequest struct { func (x *GetWorkerRequest) Reset() { *x = GetWorkerRequest{} - mi := &file_api_ateapi_v1_ateapi_proto_msgTypes[91] + mi := &file_api_ateapi_v1_ateapi_proto_msgTypes[89] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -6724,7 +6602,7 @@ func (x *GetWorkerRequest) String() string { func (*GetWorkerRequest) ProtoMessage() {} func (x *GetWorkerRequest) ProtoReflect() protoreflect.Message { - mi := &file_api_ateapi_v1_ateapi_proto_msgTypes[91] + mi := &file_api_ateapi_v1_ateapi_proto_msgTypes[89] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -6737,7 +6615,7 @@ func (x *GetWorkerRequest) ProtoReflect() protoreflect.Message { // Deprecated: Use GetWorkerRequest.ProtoReflect.Descriptor instead. func (*GetWorkerRequest) Descriptor() ([]byte, []int) { - return file_api_ateapi_v1_ateapi_proto_rawDescGZIP(), []int{91} + return file_api_ateapi_v1_ateapi_proto_rawDescGZIP(), []int{89} } func (x *GetWorkerRequest) GetWorker() *ObjectRef { @@ -6759,7 +6637,7 @@ type CreateWorkerRequest struct { func (x *CreateWorkerRequest) Reset() { *x = CreateWorkerRequest{} - mi := &file_api_ateapi_v1_ateapi_proto_msgTypes[92] + mi := &file_api_ateapi_v1_ateapi_proto_msgTypes[90] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -6771,7 +6649,7 @@ func (x *CreateWorkerRequest) String() string { func (*CreateWorkerRequest) ProtoMessage() {} func (x *CreateWorkerRequest) ProtoReflect() protoreflect.Message { - mi := &file_api_ateapi_v1_ateapi_proto_msgTypes[92] + mi := &file_api_ateapi_v1_ateapi_proto_msgTypes[90] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -6784,7 +6662,7 @@ func (x *CreateWorkerRequest) ProtoReflect() protoreflect.Message { // Deprecated: Use CreateWorkerRequest.ProtoReflect.Descriptor instead. func (*CreateWorkerRequest) Descriptor() ([]byte, []int) { - return file_api_ateapi_v1_ateapi_proto_rawDescGZIP(), []int{92} + return file_api_ateapi_v1_ateapi_proto_rawDescGZIP(), []int{90} } func (x *CreateWorkerRequest) GetWorker() *Worker { @@ -6819,7 +6697,7 @@ type UpdateWorkerRequest struct { func (x *UpdateWorkerRequest) Reset() { *x = UpdateWorkerRequest{} - mi := &file_api_ateapi_v1_ateapi_proto_msgTypes[93] + mi := &file_api_ateapi_v1_ateapi_proto_msgTypes[91] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -6831,7 +6709,7 @@ func (x *UpdateWorkerRequest) String() string { func (*UpdateWorkerRequest) ProtoMessage() {} func (x *UpdateWorkerRequest) ProtoReflect() protoreflect.Message { - mi := &file_api_ateapi_v1_ateapi_proto_msgTypes[93] + mi := &file_api_ateapi_v1_ateapi_proto_msgTypes[91] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -6844,7 +6722,7 @@ func (x *UpdateWorkerRequest) ProtoReflect() protoreflect.Message { // Deprecated: Use UpdateWorkerRequest.ProtoReflect.Descriptor instead. func (*UpdateWorkerRequest) Descriptor() ([]byte, []int) { - return file_api_ateapi_v1_ateapi_proto_rawDescGZIP(), []int{93} + return file_api_ateapi_v1_ateapi_proto_rawDescGZIP(), []int{91} } func (x *UpdateWorkerRequest) GetWorker() *Worker { @@ -6871,7 +6749,7 @@ type DeleteWorkerRequest struct { func (x *DeleteWorkerRequest) Reset() { *x = DeleteWorkerRequest{} - mi := &file_api_ateapi_v1_ateapi_proto_msgTypes[94] + mi := &file_api_ateapi_v1_ateapi_proto_msgTypes[92] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -6883,7 +6761,7 @@ func (x *DeleteWorkerRequest) String() string { func (*DeleteWorkerRequest) ProtoMessage() {} func (x *DeleteWorkerRequest) ProtoReflect() protoreflect.Message { - mi := &file_api_ateapi_v1_ateapi_proto_msgTypes[94] + mi := &file_api_ateapi_v1_ateapi_proto_msgTypes[92] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -6896,7 +6774,7 @@ func (x *DeleteWorkerRequest) ProtoReflect() protoreflect.Message { // Deprecated: Use DeleteWorkerRequest.ProtoReflect.Descriptor instead. func (*DeleteWorkerRequest) Descriptor() ([]byte, []int) { - return file_api_ateapi_v1_ateapi_proto_rawDescGZIP(), []int{94} + return file_api_ateapi_v1_ateapi_proto_rawDescGZIP(), []int{92} } func (x *DeleteWorkerRequest) GetWorker() *ObjectRef { @@ -6926,7 +6804,7 @@ type DrainWorkerRequest struct { func (x *DrainWorkerRequest) Reset() { *x = DrainWorkerRequest{} - mi := &file_api_ateapi_v1_ateapi_proto_msgTypes[95] + mi := &file_api_ateapi_v1_ateapi_proto_msgTypes[93] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -6938,7 +6816,7 @@ func (x *DrainWorkerRequest) String() string { func (*DrainWorkerRequest) ProtoMessage() {} func (x *DrainWorkerRequest) ProtoReflect() protoreflect.Message { - mi := &file_api_ateapi_v1_ateapi_proto_msgTypes[95] + mi := &file_api_ateapi_v1_ateapi_proto_msgTypes[93] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -6951,7 +6829,7 @@ func (x *DrainWorkerRequest) ProtoReflect() protoreflect.Message { // Deprecated: Use DrainWorkerRequest.ProtoReflect.Descriptor instead. func (*DrainWorkerRequest) Descriptor() ([]byte, []int) { - return file_api_ateapi_v1_ateapi_proto_rawDescGZIP(), []int{95} + return file_api_ateapi_v1_ateapi_proto_rawDescGZIP(), []int{93} } func (x *DrainWorkerRequest) GetWorker() *ObjectRef { @@ -6972,7 +6850,7 @@ type DrainWorkerResponse struct { func (x *DrainWorkerResponse) Reset() { *x = DrainWorkerResponse{} - mi := &file_api_ateapi_v1_ateapi_proto_msgTypes[96] + mi := &file_api_ateapi_v1_ateapi_proto_msgTypes[94] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -6984,7 +6862,7 @@ func (x *DrainWorkerResponse) String() string { func (*DrainWorkerResponse) ProtoMessage() {} func (x *DrainWorkerResponse) ProtoReflect() protoreflect.Message { - mi := &file_api_ateapi_v1_ateapi_proto_msgTypes[96] + mi := &file_api_ateapi_v1_ateapi_proto_msgTypes[94] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -6997,7 +6875,7 @@ func (x *DrainWorkerResponse) ProtoReflect() protoreflect.Message { // Deprecated: Use DrainWorkerResponse.ProtoReflect.Descriptor instead. func (*DrainWorkerResponse) Descriptor() ([]byte, []int) { - return file_api_ateapi_v1_ateapi_proto_rawDescGZIP(), []int{96} + return file_api_ateapi_v1_ateapi_proto_rawDescGZIP(), []int{94} } func (x *DrainWorkerResponse) GetWorker() *Worker { @@ -7021,7 +6899,7 @@ type MarkWorkerAvailableRequest struct { func (x *MarkWorkerAvailableRequest) Reset() { *x = MarkWorkerAvailableRequest{} - mi := &file_api_ateapi_v1_ateapi_proto_msgTypes[97] + mi := &file_api_ateapi_v1_ateapi_proto_msgTypes[95] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -7033,7 +6911,7 @@ func (x *MarkWorkerAvailableRequest) String() string { func (*MarkWorkerAvailableRequest) ProtoMessage() {} func (x *MarkWorkerAvailableRequest) ProtoReflect() protoreflect.Message { - mi := &file_api_ateapi_v1_ateapi_proto_msgTypes[97] + mi := &file_api_ateapi_v1_ateapi_proto_msgTypes[95] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -7046,7 +6924,7 @@ func (x *MarkWorkerAvailableRequest) ProtoReflect() protoreflect.Message { // Deprecated: Use MarkWorkerAvailableRequest.ProtoReflect.Descriptor instead. func (*MarkWorkerAvailableRequest) Descriptor() ([]byte, []int) { - return file_api_ateapi_v1_ateapi_proto_rawDescGZIP(), []int{97} + return file_api_ateapi_v1_ateapi_proto_rawDescGZIP(), []int{95} } func (x *MarkWorkerAvailableRequest) GetWorker() *ObjectRef { @@ -7067,7 +6945,7 @@ type MarkWorkerAvailableResponse struct { func (x *MarkWorkerAvailableResponse) Reset() { *x = MarkWorkerAvailableResponse{} - mi := &file_api_ateapi_v1_ateapi_proto_msgTypes[98] + mi := &file_api_ateapi_v1_ateapi_proto_msgTypes[96] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -7079,7 +6957,7 @@ func (x *MarkWorkerAvailableResponse) String() string { func (*MarkWorkerAvailableResponse) ProtoMessage() {} func (x *MarkWorkerAvailableResponse) ProtoReflect() protoreflect.Message { - mi := &file_api_ateapi_v1_ateapi_proto_msgTypes[98] + mi := &file_api_ateapi_v1_ateapi_proto_msgTypes[96] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -7092,7 +6970,7 @@ func (x *MarkWorkerAvailableResponse) ProtoReflect() protoreflect.Message { // Deprecated: Use MarkWorkerAvailableResponse.ProtoReflect.Descriptor instead. func (*MarkWorkerAvailableResponse) Descriptor() ([]byte, []int) { - return file_api_ateapi_v1_ateapi_proto_rawDescGZIP(), []int{98} + return file_api_ateapi_v1_ateapi_proto_rawDescGZIP(), []int{96} } func (x *MarkWorkerAvailableResponse) GetWorker() *Worker { @@ -7116,7 +6994,7 @@ type MarkWorkerUnavailableRequest struct { func (x *MarkWorkerUnavailableRequest) Reset() { *x = MarkWorkerUnavailableRequest{} - mi := &file_api_ateapi_v1_ateapi_proto_msgTypes[99] + mi := &file_api_ateapi_v1_ateapi_proto_msgTypes[97] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -7128,7 +7006,7 @@ func (x *MarkWorkerUnavailableRequest) String() string { func (*MarkWorkerUnavailableRequest) ProtoMessage() {} func (x *MarkWorkerUnavailableRequest) ProtoReflect() protoreflect.Message { - mi := &file_api_ateapi_v1_ateapi_proto_msgTypes[99] + mi := &file_api_ateapi_v1_ateapi_proto_msgTypes[97] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -7141,7 +7019,7 @@ func (x *MarkWorkerUnavailableRequest) ProtoReflect() protoreflect.Message { // Deprecated: Use MarkWorkerUnavailableRequest.ProtoReflect.Descriptor instead. func (*MarkWorkerUnavailableRequest) Descriptor() ([]byte, []int) { - return file_api_ateapi_v1_ateapi_proto_rawDescGZIP(), []int{99} + return file_api_ateapi_v1_ateapi_proto_rawDescGZIP(), []int{97} } func (x *MarkWorkerUnavailableRequest) GetWorker() *ObjectRef { @@ -7162,7 +7040,7 @@ type MarkWorkerUnavailableResponse struct { func (x *MarkWorkerUnavailableResponse) Reset() { *x = MarkWorkerUnavailableResponse{} - mi := &file_api_ateapi_v1_ateapi_proto_msgTypes[100] + mi := &file_api_ateapi_v1_ateapi_proto_msgTypes[98] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -7174,7 +7052,7 @@ func (x *MarkWorkerUnavailableResponse) String() string { func (*MarkWorkerUnavailableResponse) ProtoMessage() {} func (x *MarkWorkerUnavailableResponse) ProtoReflect() protoreflect.Message { - mi := &file_api_ateapi_v1_ateapi_proto_msgTypes[100] + mi := &file_api_ateapi_v1_ateapi_proto_msgTypes[98] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -7187,7 +7065,7 @@ func (x *MarkWorkerUnavailableResponse) ProtoReflect() protoreflect.Message { // Deprecated: Use MarkWorkerUnavailableResponse.ProtoReflect.Descriptor instead. func (*MarkWorkerUnavailableResponse) Descriptor() ([]byte, []int) { - return file_api_ateapi_v1_ateapi_proto_rawDescGZIP(), []int{100} + return file_api_ateapi_v1_ateapi_proto_rawDescGZIP(), []int{98} } func (x *MarkWorkerUnavailableResponse) GetWorker() *Worker { @@ -7216,7 +7094,7 @@ type ListActorsRequest struct { func (x *ListActorsRequest) Reset() { *x = ListActorsRequest{} - mi := &file_api_ateapi_v1_ateapi_proto_msgTypes[101] + mi := &file_api_ateapi_v1_ateapi_proto_msgTypes[99] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -7228,7 +7106,7 @@ func (x *ListActorsRequest) String() string { func (*ListActorsRequest) ProtoMessage() {} func (x *ListActorsRequest) ProtoReflect() protoreflect.Message { - mi := &file_api_ateapi_v1_ateapi_proto_msgTypes[101] + mi := &file_api_ateapi_v1_ateapi_proto_msgTypes[99] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -7241,7 +7119,7 @@ func (x *ListActorsRequest) ProtoReflect() protoreflect.Message { // Deprecated: Use ListActorsRequest.ProtoReflect.Descriptor instead. func (*ListActorsRequest) Descriptor() ([]byte, []int) { - return file_api_ateapi_v1_ateapi_proto_rawDescGZIP(), []int{101} + return file_api_ateapi_v1_ateapi_proto_rawDescGZIP(), []int{99} } func (x *ListActorsRequest) GetAtespace() string { @@ -7270,7 +7148,7 @@ type ListActorsResponse struct { func (x *ListActorsResponse) Reset() { *x = ListActorsResponse{} - mi := &file_api_ateapi_v1_ateapi_proto_msgTypes[102] + mi := &file_api_ateapi_v1_ateapi_proto_msgTypes[100] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -7282,7 +7160,7 @@ func (x *ListActorsResponse) String() string { func (*ListActorsResponse) ProtoMessage() {} func (x *ListActorsResponse) ProtoReflect() protoreflect.Message { - mi := &file_api_ateapi_v1_ateapi_proto_msgTypes[102] + mi := &file_api_ateapi_v1_ateapi_proto_msgTypes[100] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -7295,7 +7173,7 @@ func (x *ListActorsResponse) ProtoReflect() protoreflect.Message { // Deprecated: Use ListActorsResponse.ProtoReflect.Descriptor instead. func (*ListActorsResponse) Descriptor() ([]byte, []int) { - return file_api_ateapi_v1_ateapi_proto_rawDescGZIP(), []int{102} + return file_api_ateapi_v1_ateapi_proto_rawDescGZIP(), []int{100} } func (x *ListActorsResponse) GetActors() []*Actor { @@ -7383,7 +7261,7 @@ type Worker struct { func (x *Worker) Reset() { *x = Worker{} - mi := &file_api_ateapi_v1_ateapi_proto_msgTypes[103] + mi := &file_api_ateapi_v1_ateapi_proto_msgTypes[101] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -7395,7 +7273,7 @@ func (x *Worker) String() string { func (*Worker) ProtoMessage() {} func (x *Worker) ProtoReflect() protoreflect.Message { - mi := &file_api_ateapi_v1_ateapi_proto_msgTypes[103] + mi := &file_api_ateapi_v1_ateapi_proto_msgTypes[101] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -7408,7 +7286,7 @@ func (x *Worker) ProtoReflect() protoreflect.Message { // Deprecated: Use Worker.ProtoReflect.Descriptor instead. func (*Worker) Descriptor() ([]byte, []int) { - return file_api_ateapi_v1_ateapi_proto_rawDescGZIP(), []int{103} + return file_api_ateapi_v1_ateapi_proto_rawDescGZIP(), []int{101} } func (x *Worker) GetMetadata() *ResourceMetadata { @@ -7550,7 +7428,7 @@ type WorkerStatus struct { func (x *WorkerStatus) Reset() { *x = WorkerStatus{} - mi := &file_api_ateapi_v1_ateapi_proto_msgTypes[104] + mi := &file_api_ateapi_v1_ateapi_proto_msgTypes[102] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -7562,7 +7440,7 @@ func (x *WorkerStatus) String() string { func (*WorkerStatus) ProtoMessage() {} func (x *WorkerStatus) ProtoReflect() protoreflect.Message { - mi := &file_api_ateapi_v1_ateapi_proto_msgTypes[104] + mi := &file_api_ateapi_v1_ateapi_proto_msgTypes[102] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -7575,7 +7453,7 @@ func (x *WorkerStatus) ProtoReflect() protoreflect.Message { // Deprecated: Use WorkerStatus.ProtoReflect.Descriptor instead. func (*WorkerStatus) Descriptor() ([]byte, []int) { - return file_api_ateapi_v1_ateapi_proto_rawDescGZIP(), []int{104} + return file_api_ateapi_v1_ateapi_proto_rawDescGZIP(), []int{102} } func (x *WorkerStatus) GetState() WorkerState { @@ -7663,7 +7541,7 @@ type WorkerSandboxClass struct { func (x *WorkerSandboxClass) Reset() { *x = WorkerSandboxClass{} - mi := &file_api_ateapi_v1_ateapi_proto_msgTypes[105] + mi := &file_api_ateapi_v1_ateapi_proto_msgTypes[103] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -7675,7 +7553,7 @@ func (x *WorkerSandboxClass) String() string { func (*WorkerSandboxClass) ProtoMessage() {} func (x *WorkerSandboxClass) ProtoReflect() protoreflect.Message { - mi := &file_api_ateapi_v1_ateapi_proto_msgTypes[105] + mi := &file_api_ateapi_v1_ateapi_proto_msgTypes[103] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -7688,7 +7566,7 @@ func (x *WorkerSandboxClass) ProtoReflect() protoreflect.Message { // Deprecated: Use WorkerSandboxClass.ProtoReflect.Descriptor instead. func (*WorkerSandboxClass) Descriptor() ([]byte, []int) { - return file_api_ateapi_v1_ateapi_proto_rawDescGZIP(), []int{105} + return file_api_ateapi_v1_ateapi_proto_rawDescGZIP(), []int{103} } func (x *WorkerSandboxClass) GetSandboxClass() string { @@ -7739,7 +7617,7 @@ type WorkerSandboxRuntime struct { func (x *WorkerSandboxRuntime) Reset() { *x = WorkerSandboxRuntime{} - mi := &file_api_ateapi_v1_ateapi_proto_msgTypes[106] + mi := &file_api_ateapi_v1_ateapi_proto_msgTypes[104] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -7751,7 +7629,7 @@ func (x *WorkerSandboxRuntime) String() string { func (*WorkerSandboxRuntime) ProtoMessage() {} func (x *WorkerSandboxRuntime) ProtoReflect() protoreflect.Message { - mi := &file_api_ateapi_v1_ateapi_proto_msgTypes[106] + mi := &file_api_ateapi_v1_ateapi_proto_msgTypes[104] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -7764,7 +7642,7 @@ func (x *WorkerSandboxRuntime) ProtoReflect() protoreflect.Message { // Deprecated: Use WorkerSandboxRuntime.ProtoReflect.Descriptor instead. func (*WorkerSandboxRuntime) Descriptor() ([]byte, []int) { - return file_api_ateapi_v1_ateapi_proto_rawDescGZIP(), []int{106} + return file_api_ateapi_v1_ateapi_proto_rawDescGZIP(), []int{104} } func (x *WorkerSandboxRuntime) GetVersion() *VersionedSandboxCompat { @@ -7805,7 +7683,7 @@ type RuntimeOverhead struct { func (x *RuntimeOverhead) Reset() { *x = RuntimeOverhead{} - mi := &file_api_ateapi_v1_ateapi_proto_msgTypes[107] + mi := &file_api_ateapi_v1_ateapi_proto_msgTypes[105] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -7817,7 +7695,7 @@ func (x *RuntimeOverhead) String() string { func (*RuntimeOverhead) ProtoMessage() {} func (x *RuntimeOverhead) ProtoReflect() protoreflect.Message { - mi := &file_api_ateapi_v1_ateapi_proto_msgTypes[107] + mi := &file_api_ateapi_v1_ateapi_proto_msgTypes[105] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -7830,7 +7708,7 @@ func (x *RuntimeOverhead) ProtoReflect() protoreflect.Message { // Deprecated: Use RuntimeOverhead.ProtoReflect.Descriptor instead. func (*RuntimeOverhead) Descriptor() ([]byte, []int) { - return file_api_ateapi_v1_ateapi_proto_rawDescGZIP(), []int{107} + return file_api_ateapi_v1_ateapi_proto_rawDescGZIP(), []int{105} } func (x *RuntimeOverhead) GetMemoryBytes() int64 { @@ -7869,7 +7747,7 @@ type ActorSandboxRuntime struct { func (x *ActorSandboxRuntime) Reset() { *x = ActorSandboxRuntime{} - mi := &file_api_ateapi_v1_ateapi_proto_msgTypes[108] + mi := &file_api_ateapi_v1_ateapi_proto_msgTypes[106] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -7881,7 +7759,7 @@ func (x *ActorSandboxRuntime) String() string { func (*ActorSandboxRuntime) ProtoMessage() {} func (x *ActorSandboxRuntime) ProtoReflect() protoreflect.Message { - mi := &file_api_ateapi_v1_ateapi_proto_msgTypes[108] + mi := &file_api_ateapi_v1_ateapi_proto_msgTypes[106] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -7894,7 +7772,7 @@ func (x *ActorSandboxRuntime) ProtoReflect() protoreflect.Message { // Deprecated: Use ActorSandboxRuntime.ProtoReflect.Descriptor instead. func (*ActorSandboxRuntime) Descriptor() ([]byte, []int) { - return file_api_ateapi_v1_ateapi_proto_rawDescGZIP(), []int{108} + return file_api_ateapi_v1_ateapi_proto_rawDescGZIP(), []int{106} } func (x *ActorSandboxRuntime) GetSandboxClass() string { @@ -7945,7 +7823,7 @@ type VersionedSandboxCompat struct { func (x *VersionedSandboxCompat) Reset() { *x = VersionedSandboxCompat{} - mi := &file_api_ateapi_v1_ateapi_proto_msgTypes[109] + mi := &file_api_ateapi_v1_ateapi_proto_msgTypes[107] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -7957,7 +7835,7 @@ func (x *VersionedSandboxCompat) String() string { func (*VersionedSandboxCompat) ProtoMessage() {} func (x *VersionedSandboxCompat) ProtoReflect() protoreflect.Message { - mi := &file_api_ateapi_v1_ateapi_proto_msgTypes[109] + mi := &file_api_ateapi_v1_ateapi_proto_msgTypes[107] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -7970,7 +7848,7 @@ func (x *VersionedSandboxCompat) ProtoReflect() protoreflect.Message { // Deprecated: Use VersionedSandboxCompat.ProtoReflect.Descriptor instead. func (*VersionedSandboxCompat) Descriptor() ([]byte, []int) { - return file_api_ateapi_v1_ateapi_proto_rawDescGZIP(), []int{109} + return file_api_ateapi_v1_ateapi_proto_rawDescGZIP(), []int{107} } func (x *VersionedSandboxCompat) GetSchemaVersion() string { @@ -8006,7 +7884,7 @@ type AttributeEntry struct { func (x *AttributeEntry) Reset() { *x = AttributeEntry{} - mi := &file_api_ateapi_v1_ateapi_proto_msgTypes[110] + mi := &file_api_ateapi_v1_ateapi_proto_msgTypes[108] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -8018,7 +7896,7 @@ func (x *AttributeEntry) String() string { func (*AttributeEntry) ProtoMessage() {} func (x *AttributeEntry) ProtoReflect() protoreflect.Message { - mi := &file_api_ateapi_v1_ateapi_proto_msgTypes[110] + mi := &file_api_ateapi_v1_ateapi_proto_msgTypes[108] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -8031,7 +7909,7 @@ func (x *AttributeEntry) ProtoReflect() protoreflect.Message { // Deprecated: Use AttributeEntry.ProtoReflect.Descriptor instead. func (*AttributeEntry) Descriptor() ([]byte, []int) { - return file_api_ateapi_v1_ateapi_proto_rawDescGZIP(), []int{110} + return file_api_ateapi_v1_ateapi_proto_rawDescGZIP(), []int{108} } func (x *AttributeEntry) GetKey() string { @@ -8078,7 +7956,7 @@ type WorkerResources struct { func (x *WorkerResources) Reset() { *x = WorkerResources{} - mi := &file_api_ateapi_v1_ateapi_proto_msgTypes[111] + mi := &file_api_ateapi_v1_ateapi_proto_msgTypes[109] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -8090,7 +7968,7 @@ func (x *WorkerResources) String() string { func (*WorkerResources) ProtoMessage() {} func (x *WorkerResources) ProtoReflect() protoreflect.Message { - mi := &file_api_ateapi_v1_ateapi_proto_msgTypes[111] + mi := &file_api_ateapi_v1_ateapi_proto_msgTypes[109] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -8103,7 +7981,7 @@ func (x *WorkerResources) ProtoReflect() protoreflect.Message { // Deprecated: Use WorkerResources.ProtoReflect.Descriptor instead. func (*WorkerResources) Descriptor() ([]byte, []int) { - return file_api_ateapi_v1_ateapi_proto_rawDescGZIP(), []int{111} + return file_api_ateapi_v1_ateapi_proto_rawDescGZIP(), []int{109} } func (x *WorkerResources) GetCpuMillicores() int64 { @@ -8159,7 +8037,7 @@ type ActorAssignment struct { func (x *ActorAssignment) Reset() { *x = ActorAssignment{} - mi := &file_api_ateapi_v1_ateapi_proto_msgTypes[112] + mi := &file_api_ateapi_v1_ateapi_proto_msgTypes[110] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -8171,7 +8049,7 @@ func (x *ActorAssignment) String() string { func (*ActorAssignment) ProtoMessage() {} func (x *ActorAssignment) ProtoReflect() protoreflect.Message { - mi := &file_api_ateapi_v1_ateapi_proto_msgTypes[112] + mi := &file_api_ateapi_v1_ateapi_proto_msgTypes[110] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -8184,7 +8062,7 @@ func (x *ActorAssignment) ProtoReflect() protoreflect.Message { // Deprecated: Use ActorAssignment.ProtoReflect.Descriptor instead. func (*ActorAssignment) Descriptor() ([]byte, []int) { - return file_api_ateapi_v1_ateapi_proto_rawDescGZIP(), []int{112} + return file_api_ateapi_v1_ateapi_proto_rawDescGZIP(), []int{110} } func (x *ActorAssignment) GetMetadata() *ResourceMetadata { @@ -8229,7 +8107,7 @@ type AcquireWorkerEpochRequest struct { func (x *AcquireWorkerEpochRequest) Reset() { *x = AcquireWorkerEpochRequest{} - mi := &file_api_ateapi_v1_ateapi_proto_msgTypes[113] + mi := &file_api_ateapi_v1_ateapi_proto_msgTypes[111] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -8241,7 +8119,7 @@ func (x *AcquireWorkerEpochRequest) String() string { func (*AcquireWorkerEpochRequest) ProtoMessage() {} func (x *AcquireWorkerEpochRequest) ProtoReflect() protoreflect.Message { - mi := &file_api_ateapi_v1_ateapi_proto_msgTypes[113] + mi := &file_api_ateapi_v1_ateapi_proto_msgTypes[111] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -8254,7 +8132,7 @@ func (x *AcquireWorkerEpochRequest) ProtoReflect() protoreflect.Message { // Deprecated: Use AcquireWorkerEpochRequest.ProtoReflect.Descriptor instead. func (*AcquireWorkerEpochRequest) Descriptor() ([]byte, []int) { - return file_api_ateapi_v1_ateapi_proto_rawDescGZIP(), []int{113} + return file_api_ateapi_v1_ateapi_proto_rawDescGZIP(), []int{111} } func (x *AcquireWorkerEpochRequest) GetWorker() *ObjectRef { @@ -8274,7 +8152,7 @@ type AcquireWorkerEpochResponse struct { func (x *AcquireWorkerEpochResponse) Reset() { *x = AcquireWorkerEpochResponse{} - mi := &file_api_ateapi_v1_ateapi_proto_msgTypes[114] + mi := &file_api_ateapi_v1_ateapi_proto_msgTypes[112] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -8286,7 +8164,7 @@ func (x *AcquireWorkerEpochResponse) String() string { func (*AcquireWorkerEpochResponse) ProtoMessage() {} func (x *AcquireWorkerEpochResponse) ProtoReflect() protoreflect.Message { - mi := &file_api_ateapi_v1_ateapi_proto_msgTypes[114] + mi := &file_api_ateapi_v1_ateapi_proto_msgTypes[112] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -8299,7 +8177,7 @@ func (x *AcquireWorkerEpochResponse) ProtoReflect() protoreflect.Message { // Deprecated: Use AcquireWorkerEpochResponse.ProtoReflect.Descriptor instead. func (*AcquireWorkerEpochResponse) Descriptor() ([]byte, []int) { - return file_api_ateapi_v1_ateapi_proto_rawDescGZIP(), []int{114} + return file_api_ateapi_v1_ateapi_proto_rawDescGZIP(), []int{112} } func (x *AcquireWorkerEpochResponse) GetEpoch() int64 { @@ -8342,7 +8220,7 @@ type RegisterWorkerRequest struct { func (x *RegisterWorkerRequest) Reset() { *x = RegisterWorkerRequest{} - mi := &file_api_ateapi_v1_ateapi_proto_msgTypes[115] + mi := &file_api_ateapi_v1_ateapi_proto_msgTypes[113] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -8354,7 +8232,7 @@ func (x *RegisterWorkerRequest) String() string { func (*RegisterWorkerRequest) ProtoMessage() {} func (x *RegisterWorkerRequest) ProtoReflect() protoreflect.Message { - mi := &file_api_ateapi_v1_ateapi_proto_msgTypes[115] + mi := &file_api_ateapi_v1_ateapi_proto_msgTypes[113] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -8367,7 +8245,7 @@ func (x *RegisterWorkerRequest) ProtoReflect() protoreflect.Message { // Deprecated: Use RegisterWorkerRequest.ProtoReflect.Descriptor instead. func (*RegisterWorkerRequest) Descriptor() ([]byte, []int) { - return file_api_ateapi_v1_ateapi_proto_rawDescGZIP(), []int{115} + return file_api_ateapi_v1_ateapi_proto_rawDescGZIP(), []int{113} } func (x *RegisterWorkerRequest) GetWorker() *ObjectRef { @@ -8408,7 +8286,7 @@ type RegisterWorkerResponse struct { func (x *RegisterWorkerResponse) Reset() { *x = RegisterWorkerResponse{} - mi := &file_api_ateapi_v1_ateapi_proto_msgTypes[116] + mi := &file_api_ateapi_v1_ateapi_proto_msgTypes[114] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -8420,7 +8298,7 @@ func (x *RegisterWorkerResponse) String() string { func (*RegisterWorkerResponse) ProtoMessage() {} func (x *RegisterWorkerResponse) ProtoReflect() protoreflect.Message { - mi := &file_api_ateapi_v1_ateapi_proto_msgTypes[116] + mi := &file_api_ateapi_v1_ateapi_proto_msgTypes[114] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -8433,7 +8311,7 @@ func (x *RegisterWorkerResponse) ProtoReflect() protoreflect.Message { // Deprecated: Use RegisterWorkerResponse.ProtoReflect.Descriptor instead. func (*RegisterWorkerResponse) Descriptor() ([]byte, []int) { - return file_api_ateapi_v1_ateapi_proto_rawDescGZIP(), []int{116} + return file_api_ateapi_v1_ateapi_proto_rawDescGZIP(), []int{114} } func (x *RegisterWorkerResponse) GetWorker() *Worker { @@ -8473,7 +8351,7 @@ type SetWorkerSandboxRuntimesRequest struct { func (x *SetWorkerSandboxRuntimesRequest) Reset() { *x = SetWorkerSandboxRuntimesRequest{} - mi := &file_api_ateapi_v1_ateapi_proto_msgTypes[117] + mi := &file_api_ateapi_v1_ateapi_proto_msgTypes[115] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -8485,7 +8363,7 @@ func (x *SetWorkerSandboxRuntimesRequest) String() string { func (*SetWorkerSandboxRuntimesRequest) ProtoMessage() {} func (x *SetWorkerSandboxRuntimesRequest) ProtoReflect() protoreflect.Message { - mi := &file_api_ateapi_v1_ateapi_proto_msgTypes[117] + mi := &file_api_ateapi_v1_ateapi_proto_msgTypes[115] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -8498,7 +8376,7 @@ func (x *SetWorkerSandboxRuntimesRequest) ProtoReflect() protoreflect.Message { // Deprecated: Use SetWorkerSandboxRuntimesRequest.ProtoReflect.Descriptor instead. func (*SetWorkerSandboxRuntimesRequest) Descriptor() ([]byte, []int) { - return file_api_ateapi_v1_ateapi_proto_rawDescGZIP(), []int{117} + return file_api_ateapi_v1_ateapi_proto_rawDescGZIP(), []int{115} } func (x *SetWorkerSandboxRuntimesRequest) GetWorker() *ObjectRef { @@ -8532,7 +8410,7 @@ type SetWorkerSandboxRuntimesResponse struct { func (x *SetWorkerSandboxRuntimesResponse) Reset() { *x = SetWorkerSandboxRuntimesResponse{} - mi := &file_api_ateapi_v1_ateapi_proto_msgTypes[118] + mi := &file_api_ateapi_v1_ateapi_proto_msgTypes[116] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -8544,7 +8422,7 @@ func (x *SetWorkerSandboxRuntimesResponse) String() string { func (*SetWorkerSandboxRuntimesResponse) ProtoMessage() {} func (x *SetWorkerSandboxRuntimesResponse) ProtoReflect() protoreflect.Message { - mi := &file_api_ateapi_v1_ateapi_proto_msgTypes[118] + mi := &file_api_ateapi_v1_ateapi_proto_msgTypes[116] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -8557,7 +8435,7 @@ func (x *SetWorkerSandboxRuntimesResponse) ProtoReflect() protoreflect.Message { // Deprecated: Use SetWorkerSandboxRuntimesResponse.ProtoReflect.Descriptor instead. func (*SetWorkerSandboxRuntimesResponse) Descriptor() ([]byte, []int) { - return file_api_ateapi_v1_ateapi_proto_rawDescGZIP(), []int{118} + return file_api_ateapi_v1_ateapi_proto_rawDescGZIP(), []int{116} } func (x *SetWorkerSandboxRuntimesResponse) GetWorker() *Worker { @@ -8604,7 +8482,7 @@ type MintWorkerActorCertificateRequest struct { func (x *MintWorkerActorCertificateRequest) Reset() { *x = MintWorkerActorCertificateRequest{} - mi := &file_api_ateapi_v1_ateapi_proto_msgTypes[119] + mi := &file_api_ateapi_v1_ateapi_proto_msgTypes[117] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -8616,7 +8494,7 @@ func (x *MintWorkerActorCertificateRequest) String() string { func (*MintWorkerActorCertificateRequest) ProtoMessage() {} func (x *MintWorkerActorCertificateRequest) ProtoReflect() protoreflect.Message { - mi := &file_api_ateapi_v1_ateapi_proto_msgTypes[119] + mi := &file_api_ateapi_v1_ateapi_proto_msgTypes[117] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -8629,7 +8507,7 @@ func (x *MintWorkerActorCertificateRequest) ProtoReflect() protoreflect.Message // Deprecated: Use MintWorkerActorCertificateRequest.ProtoReflect.Descriptor instead. func (*MintWorkerActorCertificateRequest) Descriptor() ([]byte, []int) { - return file_api_ateapi_v1_ateapi_proto_rawDescGZIP(), []int{119} + return file_api_ateapi_v1_ateapi_proto_rawDescGZIP(), []int{117} } func (x *MintWorkerActorCertificateRequest) GetActor() *ObjectRef { @@ -8673,7 +8551,7 @@ type MintWorkerActorCertificateResponse struct { func (x *MintWorkerActorCertificateResponse) Reset() { *x = MintWorkerActorCertificateResponse{} - mi := &file_api_ateapi_v1_ateapi_proto_msgTypes[120] + mi := &file_api_ateapi_v1_ateapi_proto_msgTypes[118] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -8685,7 +8563,7 @@ func (x *MintWorkerActorCertificateResponse) String() string { func (*MintWorkerActorCertificateResponse) ProtoMessage() {} func (x *MintWorkerActorCertificateResponse) ProtoReflect() protoreflect.Message { - mi := &file_api_ateapi_v1_ateapi_proto_msgTypes[120] + mi := &file_api_ateapi_v1_ateapi_proto_msgTypes[118] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -8698,7 +8576,7 @@ func (x *MintWorkerActorCertificateResponse) ProtoReflect() protoreflect.Message // Deprecated: Use MintWorkerActorCertificateResponse.ProtoReflect.Descriptor instead. func (*MintWorkerActorCertificateResponse) Descriptor() ([]byte, []int) { - return file_api_ateapi_v1_ateapi_proto_rawDescGZIP(), []int{120} + return file_api_ateapi_v1_ateapi_proto_rawDescGZIP(), []int{118} } func (x *MintWorkerActorCertificateResponse) GetActorCertificates() [][]byte { @@ -8739,7 +8617,7 @@ type MintWorkerActorJwtRequest struct { func (x *MintWorkerActorJwtRequest) Reset() { *x = MintWorkerActorJwtRequest{} - mi := &file_api_ateapi_v1_ateapi_proto_msgTypes[121] + mi := &file_api_ateapi_v1_ateapi_proto_msgTypes[119] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -8751,7 +8629,7 @@ func (x *MintWorkerActorJwtRequest) String() string { func (*MintWorkerActorJwtRequest) ProtoMessage() {} func (x *MintWorkerActorJwtRequest) ProtoReflect() protoreflect.Message { - mi := &file_api_ateapi_v1_ateapi_proto_msgTypes[121] + mi := &file_api_ateapi_v1_ateapi_proto_msgTypes[119] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -8764,7 +8642,7 @@ func (x *MintWorkerActorJwtRequest) ProtoReflect() protoreflect.Message { // Deprecated: Use MintWorkerActorJwtRequest.ProtoReflect.Descriptor instead. func (*MintWorkerActorJwtRequest) Descriptor() ([]byte, []int) { - return file_api_ateapi_v1_ateapi_proto_rawDescGZIP(), []int{121} + return file_api_ateapi_v1_ateapi_proto_rawDescGZIP(), []int{119} } func (x *MintWorkerActorJwtRequest) GetActor() *ObjectRef { @@ -8812,7 +8690,7 @@ type MintWorkerActorJwtResponse struct { func (x *MintWorkerActorJwtResponse) Reset() { *x = MintWorkerActorJwtResponse{} - mi := &file_api_ateapi_v1_ateapi_proto_msgTypes[122] + mi := &file_api_ateapi_v1_ateapi_proto_msgTypes[120] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -8824,7 +8702,7 @@ func (x *MintWorkerActorJwtResponse) String() string { func (*MintWorkerActorJwtResponse) ProtoMessage() {} func (x *MintWorkerActorJwtResponse) ProtoReflect() protoreflect.Message { - mi := &file_api_ateapi_v1_ateapi_proto_msgTypes[122] + mi := &file_api_ateapi_v1_ateapi_proto_msgTypes[120] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -8837,7 +8715,7 @@ func (x *MintWorkerActorJwtResponse) ProtoReflect() protoreflect.Message { // Deprecated: Use MintWorkerActorJwtResponse.ProtoReflect.Descriptor instead. func (*MintWorkerActorJwtResponse) Descriptor() ([]byte, []int) { - return file_api_ateapi_v1_ateapi_proto_rawDescGZIP(), []int{122} + return file_api_ateapi_v1_ateapi_proto_rawDescGZIP(), []int{120} } func (x *MintWorkerActorJwtResponse) GetActorJwt() string { @@ -8885,7 +8763,7 @@ type RequestActorSuspendRequest struct { func (x *RequestActorSuspendRequest) Reset() { *x = RequestActorSuspendRequest{} - mi := &file_api_ateapi_v1_ateapi_proto_msgTypes[123] + mi := &file_api_ateapi_v1_ateapi_proto_msgTypes[121] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -8897,7 +8775,7 @@ func (x *RequestActorSuspendRequest) String() string { func (*RequestActorSuspendRequest) ProtoMessage() {} func (x *RequestActorSuspendRequest) ProtoReflect() protoreflect.Message { - mi := &file_api_ateapi_v1_ateapi_proto_msgTypes[123] + mi := &file_api_ateapi_v1_ateapi_proto_msgTypes[121] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -8910,7 +8788,7 @@ func (x *RequestActorSuspendRequest) ProtoReflect() protoreflect.Message { // Deprecated: Use RequestActorSuspendRequest.ProtoReflect.Descriptor instead. func (*RequestActorSuspendRequest) Descriptor() ([]byte, []int) { - return file_api_ateapi_v1_ateapi_proto_rawDescGZIP(), []int{123} + return file_api_ateapi_v1_ateapi_proto_rawDescGZIP(), []int{121} } func (x *RequestActorSuspendRequest) GetWorker() *ObjectRef { @@ -8944,7 +8822,7 @@ type RequestActorSuspendResponse struct { func (x *RequestActorSuspendResponse) Reset() { *x = RequestActorSuspendResponse{} - mi := &file_api_ateapi_v1_ateapi_proto_msgTypes[124] + mi := &file_api_ateapi_v1_ateapi_proto_msgTypes[122] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -8956,7 +8834,7 @@ func (x *RequestActorSuspendResponse) String() string { func (*RequestActorSuspendResponse) ProtoMessage() {} func (x *RequestActorSuspendResponse) ProtoReflect() protoreflect.Message { - mi := &file_api_ateapi_v1_ateapi_proto_msgTypes[124] + mi := &file_api_ateapi_v1_ateapi_proto_msgTypes[122] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -8969,7 +8847,7 @@ func (x *RequestActorSuspendResponse) ProtoReflect() protoreflect.Message { // Deprecated: Use RequestActorSuspendResponse.ProtoReflect.Descriptor instead. func (*RequestActorSuspendResponse) Descriptor() ([]byte, []int) { - return file_api_ateapi_v1_ateapi_proto_rawDescGZIP(), []int{124} + return file_api_ateapi_v1_ateapi_proto_rawDescGZIP(), []int{122} } func (x *RequestActorSuspendResponse) GetActor() *Actor { @@ -9009,7 +8887,7 @@ type ReportActorCrashedRequest struct { func (x *ReportActorCrashedRequest) Reset() { *x = ReportActorCrashedRequest{} - mi := &file_api_ateapi_v1_ateapi_proto_msgTypes[125] + mi := &file_api_ateapi_v1_ateapi_proto_msgTypes[123] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -9021,7 +8899,7 @@ func (x *ReportActorCrashedRequest) String() string { func (*ReportActorCrashedRequest) ProtoMessage() {} func (x *ReportActorCrashedRequest) ProtoReflect() protoreflect.Message { - mi := &file_api_ateapi_v1_ateapi_proto_msgTypes[125] + mi := &file_api_ateapi_v1_ateapi_proto_msgTypes[123] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -9034,7 +8912,7 @@ func (x *ReportActorCrashedRequest) ProtoReflect() protoreflect.Message { // Deprecated: Use ReportActorCrashedRequest.ProtoReflect.Descriptor instead. func (*ReportActorCrashedRequest) Descriptor() ([]byte, []int) { - return file_api_ateapi_v1_ateapi_proto_rawDescGZIP(), []int{125} + return file_api_ateapi_v1_ateapi_proto_rawDescGZIP(), []int{123} } func (x *ReportActorCrashedRequest) GetWorker() *ObjectRef { @@ -9075,7 +8953,7 @@ type ReportActorCrashedResponse struct { func (x *ReportActorCrashedResponse) Reset() { *x = ReportActorCrashedResponse{} - mi := &file_api_ateapi_v1_ateapi_proto_msgTypes[126] + mi := &file_api_ateapi_v1_ateapi_proto_msgTypes[124] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -9087,7 +8965,7 @@ func (x *ReportActorCrashedResponse) String() string { func (*ReportActorCrashedResponse) ProtoMessage() {} func (x *ReportActorCrashedResponse) ProtoReflect() protoreflect.Message { - mi := &file_api_ateapi_v1_ateapi_proto_msgTypes[126] + mi := &file_api_ateapi_v1_ateapi_proto_msgTypes[124] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -9100,7 +8978,7 @@ func (x *ReportActorCrashedResponse) ProtoReflect() protoreflect.Message { // Deprecated: Use ReportActorCrashedResponse.ProtoReflect.Descriptor instead. func (*ReportActorCrashedResponse) Descriptor() ([]byte, []int) { - return file_api_ateapi_v1_ateapi_proto_rawDescGZIP(), []int{126} + return file_api_ateapi_v1_ateapi_proto_rawDescGZIP(), []int{124} } func (x *ReportActorCrashedResponse) GetActor() *Actor { @@ -9131,7 +9009,7 @@ type ReportActorSnapshotStateRequest struct { func (x *ReportActorSnapshotStateRequest) Reset() { *x = ReportActorSnapshotStateRequest{} - mi := &file_api_ateapi_v1_ateapi_proto_msgTypes[127] + mi := &file_api_ateapi_v1_ateapi_proto_msgTypes[125] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -9143,7 +9021,7 @@ func (x *ReportActorSnapshotStateRequest) String() string { func (*ReportActorSnapshotStateRequest) ProtoMessage() {} func (x *ReportActorSnapshotStateRequest) ProtoReflect() protoreflect.Message { - mi := &file_api_ateapi_v1_ateapi_proto_msgTypes[127] + mi := &file_api_ateapi_v1_ateapi_proto_msgTypes[125] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -9156,7 +9034,7 @@ func (x *ReportActorSnapshotStateRequest) ProtoReflect() protoreflect.Message { // Deprecated: Use ReportActorSnapshotStateRequest.ProtoReflect.Descriptor instead. func (*ReportActorSnapshotStateRequest) Descriptor() ([]byte, []int) { - return file_api_ateapi_v1_ateapi_proto_rawDescGZIP(), []int{127} + return file_api_ateapi_v1_ateapi_proto_rawDescGZIP(), []int{125} } func (x *ReportActorSnapshotStateRequest) GetActor() *ObjectRef { @@ -9188,7 +9066,7 @@ type ReportActorSnapshotStateResponse struct { func (x *ReportActorSnapshotStateResponse) Reset() { *x = ReportActorSnapshotStateResponse{} - mi := &file_api_ateapi_v1_ateapi_proto_msgTypes[128] + mi := &file_api_ateapi_v1_ateapi_proto_msgTypes[126] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -9200,7 +9078,7 @@ func (x *ReportActorSnapshotStateResponse) String() string { func (*ReportActorSnapshotStateResponse) ProtoMessage() {} func (x *ReportActorSnapshotStateResponse) ProtoReflect() protoreflect.Message { - mi := &file_api_ateapi_v1_ateapi_proto_msgTypes[128] + mi := &file_api_ateapi_v1_ateapi_proto_msgTypes[126] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -9213,7 +9091,7 @@ func (x *ReportActorSnapshotStateResponse) ProtoReflect() protoreflect.Message { // Deprecated: Use ReportActorSnapshotStateResponse.ProtoReflect.Descriptor instead. func (*ReportActorSnapshotStateResponse) Descriptor() ([]byte, []int) { - return file_api_ateapi_v1_ateapi_proto_rawDescGZIP(), []int{128} + return file_api_ateapi_v1_ateapi_proto_rawDescGZIP(), []int{126} } // ActorSnapshotState is what atelet holds for one Actor on its node. It @@ -9247,7 +9125,7 @@ type ActorSnapshotState struct { func (x *ActorSnapshotState) Reset() { *x = ActorSnapshotState{} - mi := &file_api_ateapi_v1_ateapi_proto_msgTypes[129] + mi := &file_api_ateapi_v1_ateapi_proto_msgTypes[127] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -9259,7 +9137,7 @@ func (x *ActorSnapshotState) String() string { func (*ActorSnapshotState) ProtoMessage() {} func (x *ActorSnapshotState) ProtoReflect() protoreflect.Message { - mi := &file_api_ateapi_v1_ateapi_proto_msgTypes[129] + mi := &file_api_ateapi_v1_ateapi_proto_msgTypes[127] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -9272,7 +9150,7 @@ func (x *ActorSnapshotState) ProtoReflect() protoreflect.Message { // Deprecated: Use ActorSnapshotState.ProtoReflect.Descriptor instead. func (*ActorSnapshotState) Descriptor() ([]byte, []int) { - return file_api_ateapi_v1_ateapi_proto_rawDescGZIP(), []int{129} + return file_api_ateapi_v1_ateapi_proto_rawDescGZIP(), []int{127} } func (x *ActorSnapshotState) GetLatestLocal() *SnapshotReport { @@ -9344,7 +9222,7 @@ type SnapshotReport struct { func (x *SnapshotReport) Reset() { *x = SnapshotReport{} - mi := &file_api_ateapi_v1_ateapi_proto_msgTypes[130] + mi := &file_api_ateapi_v1_ateapi_proto_msgTypes[128] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -9356,7 +9234,7 @@ func (x *SnapshotReport) String() string { func (*SnapshotReport) ProtoMessage() {} func (x *SnapshotReport) ProtoReflect() protoreflect.Message { - mi := &file_api_ateapi_v1_ateapi_proto_msgTypes[130] + mi := &file_api_ateapi_v1_ateapi_proto_msgTypes[128] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -9369,7 +9247,7 @@ func (x *SnapshotReport) ProtoReflect() protoreflect.Message { // Deprecated: Use SnapshotReport.ProtoReflect.Descriptor instead. func (*SnapshotReport) Descriptor() ([]byte, []int) { - return file_api_ateapi_v1_ateapi_proto_rawDescGZIP(), []int{130} + return file_api_ateapi_v1_ateapi_proto_rawDescGZIP(), []int{128} } func (x *SnapshotReport) GetUuid() string { @@ -9439,7 +9317,7 @@ type AccessPolicy struct { func (x *AccessPolicy) Reset() { *x = AccessPolicy{} - mi := &file_api_ateapi_v1_ateapi_proto_msgTypes[131] + mi := &file_api_ateapi_v1_ateapi_proto_msgTypes[129] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -9451,7 +9329,7 @@ func (x *AccessPolicy) String() string { func (*AccessPolicy) ProtoMessage() {} func (x *AccessPolicy) ProtoReflect() protoreflect.Message { - mi := &file_api_ateapi_v1_ateapi_proto_msgTypes[131] + mi := &file_api_ateapi_v1_ateapi_proto_msgTypes[129] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -9464,7 +9342,7 @@ func (x *AccessPolicy) ProtoReflect() protoreflect.Message { // Deprecated: Use AccessPolicy.ProtoReflect.Descriptor instead. func (*AccessPolicy) Descriptor() ([]byte, []int) { - return file_api_ateapi_v1_ateapi_proto_rawDescGZIP(), []int{131} + return file_api_ateapi_v1_ateapi_proto_rawDescGZIP(), []int{129} } func (x *AccessPolicy) GetMetadata() *ResourceMetadata { @@ -9506,7 +9384,7 @@ type Binding struct { func (x *Binding) Reset() { *x = Binding{} - mi := &file_api_ateapi_v1_ateapi_proto_msgTypes[132] + mi := &file_api_ateapi_v1_ateapi_proto_msgTypes[130] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -9518,7 +9396,7 @@ func (x *Binding) String() string { func (*Binding) ProtoMessage() {} func (x *Binding) ProtoReflect() protoreflect.Message { - mi := &file_api_ateapi_v1_ateapi_proto_msgTypes[132] + mi := &file_api_ateapi_v1_ateapi_proto_msgTypes[130] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -9531,7 +9409,7 @@ func (x *Binding) ProtoReflect() protoreflect.Message { // Deprecated: Use Binding.ProtoReflect.Descriptor instead. func (*Binding) Descriptor() ([]byte, []int) { - return file_api_ateapi_v1_ateapi_proto_rawDescGZIP(), []int{132} + return file_api_ateapi_v1_ateapi_proto_rawDescGZIP(), []int{130} } func (x *Binding) GetRole() string { @@ -9563,7 +9441,7 @@ type GetGlobalAccessPolicyRequest struct { func (x *GetGlobalAccessPolicyRequest) Reset() { *x = GetGlobalAccessPolicyRequest{} - mi := &file_api_ateapi_v1_ateapi_proto_msgTypes[133] + mi := &file_api_ateapi_v1_ateapi_proto_msgTypes[131] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -9575,7 +9453,7 @@ func (x *GetGlobalAccessPolicyRequest) String() string { func (*GetGlobalAccessPolicyRequest) ProtoMessage() {} func (x *GetGlobalAccessPolicyRequest) ProtoReflect() protoreflect.Message { - mi := &file_api_ateapi_v1_ateapi_proto_msgTypes[133] + mi := &file_api_ateapi_v1_ateapi_proto_msgTypes[131] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -9588,7 +9466,7 @@ func (x *GetGlobalAccessPolicyRequest) ProtoReflect() protoreflect.Message { // Deprecated: Use GetGlobalAccessPolicyRequest.ProtoReflect.Descriptor instead. func (*GetGlobalAccessPolicyRequest) Descriptor() ([]byte, []int) { - return file_api_ateapi_v1_ateapi_proto_rawDescGZIP(), []int{133} + return file_api_ateapi_v1_ateapi_proto_rawDescGZIP(), []int{131} } func (x *GetGlobalAccessPolicyRequest) GetName() string { @@ -9612,7 +9490,7 @@ type CreateGlobalAccessPolicyRequest struct { func (x *CreateGlobalAccessPolicyRequest) Reset() { *x = CreateGlobalAccessPolicyRequest{} - mi := &file_api_ateapi_v1_ateapi_proto_msgTypes[134] + mi := &file_api_ateapi_v1_ateapi_proto_msgTypes[132] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -9624,7 +9502,7 @@ func (x *CreateGlobalAccessPolicyRequest) String() string { func (*CreateGlobalAccessPolicyRequest) ProtoMessage() {} func (x *CreateGlobalAccessPolicyRequest) ProtoReflect() protoreflect.Message { - mi := &file_api_ateapi_v1_ateapi_proto_msgTypes[134] + mi := &file_api_ateapi_v1_ateapi_proto_msgTypes[132] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -9637,7 +9515,7 @@ func (x *CreateGlobalAccessPolicyRequest) ProtoReflect() protoreflect.Message { // Deprecated: Use CreateGlobalAccessPolicyRequest.ProtoReflect.Descriptor instead. func (*CreateGlobalAccessPolicyRequest) Descriptor() ([]byte, []int) { - return file_api_ateapi_v1_ateapi_proto_rawDescGZIP(), []int{134} + return file_api_ateapi_v1_ateapi_proto_rawDescGZIP(), []int{132} } func (x *CreateGlobalAccessPolicyRequest) GetAccessPolicy() *AccessPolicy { @@ -9661,7 +9539,7 @@ type UpdateGlobalAccessPolicyRequest struct { func (x *UpdateGlobalAccessPolicyRequest) Reset() { *x = UpdateGlobalAccessPolicyRequest{} - mi := &file_api_ateapi_v1_ateapi_proto_msgTypes[135] + mi := &file_api_ateapi_v1_ateapi_proto_msgTypes[133] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -9673,7 +9551,7 @@ func (x *UpdateGlobalAccessPolicyRequest) String() string { func (*UpdateGlobalAccessPolicyRequest) ProtoMessage() {} func (x *UpdateGlobalAccessPolicyRequest) ProtoReflect() protoreflect.Message { - mi := &file_api_ateapi_v1_ateapi_proto_msgTypes[135] + mi := &file_api_ateapi_v1_ateapi_proto_msgTypes[133] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -9686,7 +9564,7 @@ func (x *UpdateGlobalAccessPolicyRequest) ProtoReflect() protoreflect.Message { // Deprecated: Use UpdateGlobalAccessPolicyRequest.ProtoReflect.Descriptor instead. func (*UpdateGlobalAccessPolicyRequest) Descriptor() ([]byte, []int) { - return file_api_ateapi_v1_ateapi_proto_rawDescGZIP(), []int{135} + return file_api_ateapi_v1_ateapi_proto_rawDescGZIP(), []int{133} } func (x *UpdateGlobalAccessPolicyRequest) GetAccessPolicy() *AccessPolicy { @@ -9716,7 +9594,7 @@ type GetAtespaceAccessPolicyRequest struct { func (x *GetAtespaceAccessPolicyRequest) Reset() { *x = GetAtespaceAccessPolicyRequest{} - mi := &file_api_ateapi_v1_ateapi_proto_msgTypes[136] + mi := &file_api_ateapi_v1_ateapi_proto_msgTypes[134] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -9728,7 +9606,7 @@ func (x *GetAtespaceAccessPolicyRequest) String() string { func (*GetAtespaceAccessPolicyRequest) ProtoMessage() {} func (x *GetAtespaceAccessPolicyRequest) ProtoReflect() protoreflect.Message { - mi := &file_api_ateapi_v1_ateapi_proto_msgTypes[136] + mi := &file_api_ateapi_v1_ateapi_proto_msgTypes[134] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -9741,7 +9619,7 @@ func (x *GetAtespaceAccessPolicyRequest) ProtoReflect() protoreflect.Message { // Deprecated: Use GetAtespaceAccessPolicyRequest.ProtoReflect.Descriptor instead. func (*GetAtespaceAccessPolicyRequest) Descriptor() ([]byte, []int) { - return file_api_ateapi_v1_ateapi_proto_rawDescGZIP(), []int{136} + return file_api_ateapi_v1_ateapi_proto_rawDescGZIP(), []int{134} } func (x *GetAtespaceAccessPolicyRequest) GetAtespace() *ObjectRef { @@ -9777,7 +9655,7 @@ type CreateAtespaceAccessPolicyRequest struct { func (x *CreateAtespaceAccessPolicyRequest) Reset() { *x = CreateAtespaceAccessPolicyRequest{} - mi := &file_api_ateapi_v1_ateapi_proto_msgTypes[137] + mi := &file_api_ateapi_v1_ateapi_proto_msgTypes[135] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -9789,7 +9667,7 @@ func (x *CreateAtespaceAccessPolicyRequest) String() string { func (*CreateAtespaceAccessPolicyRequest) ProtoMessage() {} func (x *CreateAtespaceAccessPolicyRequest) ProtoReflect() protoreflect.Message { - mi := &file_api_ateapi_v1_ateapi_proto_msgTypes[137] + mi := &file_api_ateapi_v1_ateapi_proto_msgTypes[135] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -9802,7 +9680,7 @@ func (x *CreateAtespaceAccessPolicyRequest) ProtoReflect() protoreflect.Message // Deprecated: Use CreateAtespaceAccessPolicyRequest.ProtoReflect.Descriptor instead. func (*CreateAtespaceAccessPolicyRequest) Descriptor() ([]byte, []int) { - return file_api_ateapi_v1_ateapi_proto_rawDescGZIP(), []int{137} + return file_api_ateapi_v1_ateapi_proto_rawDescGZIP(), []int{135} } func (x *CreateAtespaceAccessPolicyRequest) GetAtespace() *ObjectRef { @@ -9838,7 +9716,7 @@ type UpdateAtespaceAccessPolicyRequest struct { func (x *UpdateAtespaceAccessPolicyRequest) Reset() { *x = UpdateAtespaceAccessPolicyRequest{} - mi := &file_api_ateapi_v1_ateapi_proto_msgTypes[138] + mi := &file_api_ateapi_v1_ateapi_proto_msgTypes[136] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -9850,7 +9728,7 @@ func (x *UpdateAtespaceAccessPolicyRequest) String() string { func (*UpdateAtespaceAccessPolicyRequest) ProtoMessage() {} func (x *UpdateAtespaceAccessPolicyRequest) ProtoReflect() protoreflect.Message { - mi := &file_api_ateapi_v1_ateapi_proto_msgTypes[138] + mi := &file_api_ateapi_v1_ateapi_proto_msgTypes[136] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -9863,7 +9741,7 @@ func (x *UpdateAtespaceAccessPolicyRequest) ProtoReflect() protoreflect.Message // Deprecated: Use UpdateAtespaceAccessPolicyRequest.ProtoReflect.Descriptor instead. func (*UpdateAtespaceAccessPolicyRequest) Descriptor() ([]byte, []int) { - return file_api_ateapi_v1_ateapi_proto_rawDescGZIP(), []int{138} + return file_api_ateapi_v1_ateapi_proto_rawDescGZIP(), []int{136} } func (x *UpdateAtespaceAccessPolicyRequest) GetAtespace() *ObjectRef { @@ -9904,7 +9782,7 @@ type DeleteAtespaceAccessPolicyRequest struct { func (x *DeleteAtespaceAccessPolicyRequest) Reset() { *x = DeleteAtespaceAccessPolicyRequest{} - mi := &file_api_ateapi_v1_ateapi_proto_msgTypes[139] + mi := &file_api_ateapi_v1_ateapi_proto_msgTypes[137] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -9916,7 +9794,7 @@ func (x *DeleteAtespaceAccessPolicyRequest) String() string { func (*DeleteAtespaceAccessPolicyRequest) ProtoMessage() {} func (x *DeleteAtespaceAccessPolicyRequest) ProtoReflect() protoreflect.Message { - mi := &file_api_ateapi_v1_ateapi_proto_msgTypes[139] + mi := &file_api_ateapi_v1_ateapi_proto_msgTypes[137] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -9929,7 +9807,7 @@ func (x *DeleteAtespaceAccessPolicyRequest) ProtoReflect() protoreflect.Message // Deprecated: Use DeleteAtespaceAccessPolicyRequest.ProtoReflect.Descriptor instead. func (*DeleteAtespaceAccessPolicyRequest) Descriptor() ([]byte, []int) { - return file_api_ateapi_v1_ateapi_proto_rawDescGZIP(), []int{139} + return file_api_ateapi_v1_ateapi_proto_rawDescGZIP(), []int{137} } func (x *DeleteAtespaceAccessPolicyRequest) GetAtespace() *ObjectRef { @@ -9975,7 +9853,7 @@ type ResourceAnnotation struct { func (x *ResourceAnnotation) Reset() { *x = ResourceAnnotation{} - mi := &file_api_ateapi_v1_ateapi_proto_msgTypes[140] + mi := &file_api_ateapi_v1_ateapi_proto_msgTypes[138] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -9987,7 +9865,7 @@ func (x *ResourceAnnotation) String() string { func (*ResourceAnnotation) ProtoMessage() {} func (x *ResourceAnnotation) ProtoReflect() protoreflect.Message { - mi := &file_api_ateapi_v1_ateapi_proto_msgTypes[140] + mi := &file_api_ateapi_v1_ateapi_proto_msgTypes[138] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -10000,7 +9878,7 @@ func (x *ResourceAnnotation) ProtoReflect() protoreflect.Message { // Deprecated: Use ResourceAnnotation.ProtoReflect.Descriptor instead. func (*ResourceAnnotation) Descriptor() ([]byte, []int) { - return file_api_ateapi_v1_ateapi_proto_rawDescGZIP(), []int{140} + return file_api_ateapi_v1_ateapi_proto_rawDescGZIP(), []int{138} } func (x *ResourceAnnotation) GetParents() []string { @@ -10043,7 +9921,7 @@ type MethodAnnotation struct { func (x *MethodAnnotation) Reset() { *x = MethodAnnotation{} - mi := &file_api_ateapi_v1_ateapi_proto_msgTypes[141] + mi := &file_api_ateapi_v1_ateapi_proto_msgTypes[139] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -10055,7 +9933,7 @@ func (x *MethodAnnotation) String() string { func (*MethodAnnotation) ProtoMessage() {} func (x *MethodAnnotation) ProtoReflect() protoreflect.Message { - mi := &file_api_ateapi_v1_ateapi_proto_msgTypes[141] + mi := &file_api_ateapi_v1_ateapi_proto_msgTypes[139] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -10068,7 +9946,7 @@ func (x *MethodAnnotation) ProtoReflect() protoreflect.Message { // Deprecated: Use MethodAnnotation.ProtoReflect.Descriptor instead. func (*MethodAnnotation) Descriptor() ([]byte, []int) { - return file_api_ateapi_v1_ateapi_proto_rawDescGZIP(), []int{141} + return file_api_ateapi_v1_ateapi_proto_rawDescGZIP(), []int{139} } func (x *MethodAnnotation) GetResource() string { @@ -10400,13 +10278,7 @@ const file_api_ateapi_v1_ateapi_proto_rawDesc = "" + "\x14MintActorJwtResponse\x12 \n" + "\tactor_jwt\x18\x01 \x01(\tB\x03\x80\x01\x01R\bactorJwt\x12;\n" + "\vexpire_time\x18\x02 \x01(\v2\x1a.google.protobuf.TimestampR\n" + - "expireTime\"\xb0\x01\n" + - "\x1bMintActorCertificateRequest\x124\n" + - "\x05actor\x18\x01 \x01(\v2\x1e.substrate.ateapi.v1.ObjectRefR\x05actor\x12\x1b\n" + - "\tactor_uid\x18\x02 \x01(\tR\bactorUid\x12>\n" + - "\x1bcertificate_signing_request\x18\x03 \x01(\fR\x19certificateSigningRequest\"M\n" + - "\x1cMintActorCertificateResponse\x12-\n" + - "\x12actor_certificates\x18\x01 \x03(\fR\x11actorCertificates\"i\n" + + "expireTime\"i\n" + "\x0fListTagsRequest\x12\x1a\n" + "\batespace\x18\x01 \x01(\tR\batespace\x12:\n" + "\aoptions\x18\x02 \x01(\v2 .substrate.ateapi.v1.ListOptionsR\aoptions\"h\n" + @@ -10666,7 +10538,7 @@ const file_api_ateapi_v1_ateapi_proto_rawDesc = "" + "\x05Scope\x12\x15\n" + "\x11SCOPE_UNSPECIFIED\x10\x00\x12\x12\n" + "\x0eSCOPE_ATESPACE\x10\x01\x12\x10\n" + - "\fSCOPE_GLOBAL\x10\x022\xd5'\n" + + "\fSCOPE_GLOBAL\x10\x022\xca&\n" + "\aControl\x12Y\n" + "\bGetActor\x12$.substrate.ateapi.v1.GetActorRequest\x1a\x1a.substrate.ateapi.v1.Actor\"\v\x82\xb5\x18\a\n" + "\x05Actor\x12_\n" + @@ -10691,8 +10563,6 @@ const file_api_ateapi_v1_ateapi_proto_rawDesc = "" + "\x17DeleteActorEgressPolicy\x123.substrate.ateapi.v1.DeleteActorEgressPolicyRequest\x1a!.substrate.ateapi.v1.EgressPolicy\"\x12\x82\xb5\x18\x0e\n" + "\fEgressPolicy\x12p\n" + "\fMintActorJwt\x12(.substrate.ateapi.v1.MintActorJwtRequest\x1a).substrate.ateapi.v1.MintActorJwtResponse\"\v\x82\xb5\x18\a\n" + - "\x05Actor\x12\x88\x01\n" + - "\x14MintActorCertificate\x120.substrate.ateapi.v1.MintActorCertificateRequest\x1a1.substrate.ateapi.v1.MintActorCertificateResponse\"\v\x82\xb5\x18\a\n" + "\x05Actor\x12W\n" + "\tCreateTag\x12%.substrate.ateapi.v1.CreateTagRequest\x1a\x18.substrate.ateapi.v1.Tag\"\t\x82\xb5\x18\x05\n" + "\x03Tag\x12Q\n" + @@ -10784,7 +10654,7 @@ func file_api_ateapi_v1_ateapi_proto_rawDescGZIP() []byte { } var file_api_ateapi_v1_ateapi_proto_enumTypes = make([]protoimpl.EnumInfo, 10) -var file_api_ateapi_v1_ateapi_proto_msgTypes = make([]protoimpl.MessageInfo, 145) +var file_api_ateapi_v1_ateapi_proto_msgTypes = make([]protoimpl.MessageInfo, 143) var file_api_ateapi_v1_ateapi_proto_goTypes = []any{ (SnapshotOwner)(0), // 0: substrate.ateapi.v1.SnapshotOwner (SnapshotStorageState)(0), // 1: substrate.ateapi.v1.SnapshotStorageState @@ -10874,76 +10744,74 @@ var file_api_ateapi_v1_ateapi_proto_goTypes = []any{ (*GetTagRequest)(nil), // 85: substrate.ateapi.v1.GetTagRequest (*MintActorJwtRequest)(nil), // 86: substrate.ateapi.v1.MintActorJwtRequest (*MintActorJwtResponse)(nil), // 87: substrate.ateapi.v1.MintActorJwtResponse - (*MintActorCertificateRequest)(nil), // 88: substrate.ateapi.v1.MintActorCertificateRequest - (*MintActorCertificateResponse)(nil), // 89: substrate.ateapi.v1.MintActorCertificateResponse - (*ListTagsRequest)(nil), // 90: substrate.ateapi.v1.ListTagsRequest - (*ListTagsResponse)(nil), // 91: substrate.ateapi.v1.ListTagsResponse - (*CreateTagRequest)(nil), // 92: substrate.ateapi.v1.CreateTagRequest - (*UpdateTagRequest)(nil), // 93: substrate.ateapi.v1.UpdateTagRequest - (*DeleteTagRequest)(nil), // 94: substrate.ateapi.v1.DeleteTagRequest - (*DeleteOptions)(nil), // 95: substrate.ateapi.v1.DeleteOptions - (*ListOptions)(nil), // 96: substrate.ateapi.v1.ListOptions - (*ListWorkerActorAssignmentsRequest)(nil), // 97: substrate.ateapi.v1.ListWorkerActorAssignmentsRequest - (*ListWorkerActorAssignmentsResponse)(nil), // 98: substrate.ateapi.v1.ListWorkerActorAssignmentsResponse - (*ListWorkersRequest)(nil), // 99: substrate.ateapi.v1.ListWorkersRequest - (*ListWorkersResponse)(nil), // 100: substrate.ateapi.v1.ListWorkersResponse - (*GetWorkerRequest)(nil), // 101: substrate.ateapi.v1.GetWorkerRequest - (*CreateWorkerRequest)(nil), // 102: substrate.ateapi.v1.CreateWorkerRequest - (*UpdateWorkerRequest)(nil), // 103: substrate.ateapi.v1.UpdateWorkerRequest - (*DeleteWorkerRequest)(nil), // 104: substrate.ateapi.v1.DeleteWorkerRequest - (*DrainWorkerRequest)(nil), // 105: substrate.ateapi.v1.DrainWorkerRequest - (*DrainWorkerResponse)(nil), // 106: substrate.ateapi.v1.DrainWorkerResponse - (*MarkWorkerAvailableRequest)(nil), // 107: substrate.ateapi.v1.MarkWorkerAvailableRequest - (*MarkWorkerAvailableResponse)(nil), // 108: substrate.ateapi.v1.MarkWorkerAvailableResponse - (*MarkWorkerUnavailableRequest)(nil), // 109: substrate.ateapi.v1.MarkWorkerUnavailableRequest - (*MarkWorkerUnavailableResponse)(nil), // 110: substrate.ateapi.v1.MarkWorkerUnavailableResponse - (*ListActorsRequest)(nil), // 111: substrate.ateapi.v1.ListActorsRequest - (*ListActorsResponse)(nil), // 112: substrate.ateapi.v1.ListActorsResponse - (*Worker)(nil), // 113: substrate.ateapi.v1.Worker - (*WorkerStatus)(nil), // 114: substrate.ateapi.v1.WorkerStatus - (*WorkerSandboxClass)(nil), // 115: substrate.ateapi.v1.WorkerSandboxClass - (*WorkerSandboxRuntime)(nil), // 116: substrate.ateapi.v1.WorkerSandboxRuntime - (*RuntimeOverhead)(nil), // 117: substrate.ateapi.v1.RuntimeOverhead - (*ActorSandboxRuntime)(nil), // 118: substrate.ateapi.v1.ActorSandboxRuntime - (*VersionedSandboxCompat)(nil), // 119: substrate.ateapi.v1.VersionedSandboxCompat - (*AttributeEntry)(nil), // 120: substrate.ateapi.v1.AttributeEntry - (*WorkerResources)(nil), // 121: substrate.ateapi.v1.WorkerResources - (*ActorAssignment)(nil), // 122: substrate.ateapi.v1.ActorAssignment - (*AcquireWorkerEpochRequest)(nil), // 123: substrate.ateapi.v1.AcquireWorkerEpochRequest - (*AcquireWorkerEpochResponse)(nil), // 124: substrate.ateapi.v1.AcquireWorkerEpochResponse - (*RegisterWorkerRequest)(nil), // 125: substrate.ateapi.v1.RegisterWorkerRequest - (*RegisterWorkerResponse)(nil), // 126: substrate.ateapi.v1.RegisterWorkerResponse - (*SetWorkerSandboxRuntimesRequest)(nil), // 127: substrate.ateapi.v1.SetWorkerSandboxRuntimesRequest - (*SetWorkerSandboxRuntimesResponse)(nil), // 128: substrate.ateapi.v1.SetWorkerSandboxRuntimesResponse - (*MintWorkerActorCertificateRequest)(nil), // 129: substrate.ateapi.v1.MintWorkerActorCertificateRequest - (*MintWorkerActorCertificateResponse)(nil), // 130: substrate.ateapi.v1.MintWorkerActorCertificateResponse - (*MintWorkerActorJwtRequest)(nil), // 131: substrate.ateapi.v1.MintWorkerActorJwtRequest - (*MintWorkerActorJwtResponse)(nil), // 132: substrate.ateapi.v1.MintWorkerActorJwtResponse - (*RequestActorSuspendRequest)(nil), // 133: substrate.ateapi.v1.RequestActorSuspendRequest - (*RequestActorSuspendResponse)(nil), // 134: substrate.ateapi.v1.RequestActorSuspendResponse - (*ReportActorCrashedRequest)(nil), // 135: substrate.ateapi.v1.ReportActorCrashedRequest - (*ReportActorCrashedResponse)(nil), // 136: substrate.ateapi.v1.ReportActorCrashedResponse - (*ReportActorSnapshotStateRequest)(nil), // 137: substrate.ateapi.v1.ReportActorSnapshotStateRequest - (*ReportActorSnapshotStateResponse)(nil), // 138: substrate.ateapi.v1.ReportActorSnapshotStateResponse - (*ActorSnapshotState)(nil), // 139: substrate.ateapi.v1.ActorSnapshotState - (*SnapshotReport)(nil), // 140: substrate.ateapi.v1.SnapshotReport - (*AccessPolicy)(nil), // 141: substrate.ateapi.v1.AccessPolicy - (*Binding)(nil), // 142: substrate.ateapi.v1.Binding - (*GetGlobalAccessPolicyRequest)(nil), // 143: substrate.ateapi.v1.GetGlobalAccessPolicyRequest - (*CreateGlobalAccessPolicyRequest)(nil), // 144: substrate.ateapi.v1.CreateGlobalAccessPolicyRequest - (*UpdateGlobalAccessPolicyRequest)(nil), // 145: substrate.ateapi.v1.UpdateGlobalAccessPolicyRequest - (*GetAtespaceAccessPolicyRequest)(nil), // 146: substrate.ateapi.v1.GetAtespaceAccessPolicyRequest - (*CreateAtespaceAccessPolicyRequest)(nil), // 147: substrate.ateapi.v1.CreateAtespaceAccessPolicyRequest - (*UpdateAtespaceAccessPolicyRequest)(nil), // 148: substrate.ateapi.v1.UpdateAtespaceAccessPolicyRequest - (*DeleteAtespaceAccessPolicyRequest)(nil), // 149: substrate.ateapi.v1.DeleteAtespaceAccessPolicyRequest - (*ResourceAnnotation)(nil), // 150: substrate.ateapi.v1.ResourceAnnotation - (*MethodAnnotation)(nil), // 151: substrate.ateapi.v1.MethodAnnotation - nil, // 152: substrate.ateapi.v1.Selector.MatchLabelsEntry - nil, // 153: substrate.ateapi.v1.ExternalVolumeStatus.VolumeContextEntry - nil, // 154: substrate.ateapi.v1.Worker.LabelsEntry - (*timestamppb.Timestamp)(nil), // 155: google.protobuf.Timestamp - (*descriptorpb.MessageOptions)(nil), // 156: google.protobuf.MessageOptions - (*descriptorpb.MethodOptions)(nil), // 157: google.protobuf.MethodOptions + (*ListTagsRequest)(nil), // 88: substrate.ateapi.v1.ListTagsRequest + (*ListTagsResponse)(nil), // 89: substrate.ateapi.v1.ListTagsResponse + (*CreateTagRequest)(nil), // 90: substrate.ateapi.v1.CreateTagRequest + (*UpdateTagRequest)(nil), // 91: substrate.ateapi.v1.UpdateTagRequest + (*DeleteTagRequest)(nil), // 92: substrate.ateapi.v1.DeleteTagRequest + (*DeleteOptions)(nil), // 93: substrate.ateapi.v1.DeleteOptions + (*ListOptions)(nil), // 94: substrate.ateapi.v1.ListOptions + (*ListWorkerActorAssignmentsRequest)(nil), // 95: substrate.ateapi.v1.ListWorkerActorAssignmentsRequest + (*ListWorkerActorAssignmentsResponse)(nil), // 96: substrate.ateapi.v1.ListWorkerActorAssignmentsResponse + (*ListWorkersRequest)(nil), // 97: substrate.ateapi.v1.ListWorkersRequest + (*ListWorkersResponse)(nil), // 98: substrate.ateapi.v1.ListWorkersResponse + (*GetWorkerRequest)(nil), // 99: substrate.ateapi.v1.GetWorkerRequest + (*CreateWorkerRequest)(nil), // 100: substrate.ateapi.v1.CreateWorkerRequest + (*UpdateWorkerRequest)(nil), // 101: substrate.ateapi.v1.UpdateWorkerRequest + (*DeleteWorkerRequest)(nil), // 102: substrate.ateapi.v1.DeleteWorkerRequest + (*DrainWorkerRequest)(nil), // 103: substrate.ateapi.v1.DrainWorkerRequest + (*DrainWorkerResponse)(nil), // 104: substrate.ateapi.v1.DrainWorkerResponse + (*MarkWorkerAvailableRequest)(nil), // 105: substrate.ateapi.v1.MarkWorkerAvailableRequest + (*MarkWorkerAvailableResponse)(nil), // 106: substrate.ateapi.v1.MarkWorkerAvailableResponse + (*MarkWorkerUnavailableRequest)(nil), // 107: substrate.ateapi.v1.MarkWorkerUnavailableRequest + (*MarkWorkerUnavailableResponse)(nil), // 108: substrate.ateapi.v1.MarkWorkerUnavailableResponse + (*ListActorsRequest)(nil), // 109: substrate.ateapi.v1.ListActorsRequest + (*ListActorsResponse)(nil), // 110: substrate.ateapi.v1.ListActorsResponse + (*Worker)(nil), // 111: substrate.ateapi.v1.Worker + (*WorkerStatus)(nil), // 112: substrate.ateapi.v1.WorkerStatus + (*WorkerSandboxClass)(nil), // 113: substrate.ateapi.v1.WorkerSandboxClass + (*WorkerSandboxRuntime)(nil), // 114: substrate.ateapi.v1.WorkerSandboxRuntime + (*RuntimeOverhead)(nil), // 115: substrate.ateapi.v1.RuntimeOverhead + (*ActorSandboxRuntime)(nil), // 116: substrate.ateapi.v1.ActorSandboxRuntime + (*VersionedSandboxCompat)(nil), // 117: substrate.ateapi.v1.VersionedSandboxCompat + (*AttributeEntry)(nil), // 118: substrate.ateapi.v1.AttributeEntry + (*WorkerResources)(nil), // 119: substrate.ateapi.v1.WorkerResources + (*ActorAssignment)(nil), // 120: substrate.ateapi.v1.ActorAssignment + (*AcquireWorkerEpochRequest)(nil), // 121: substrate.ateapi.v1.AcquireWorkerEpochRequest + (*AcquireWorkerEpochResponse)(nil), // 122: substrate.ateapi.v1.AcquireWorkerEpochResponse + (*RegisterWorkerRequest)(nil), // 123: substrate.ateapi.v1.RegisterWorkerRequest + (*RegisterWorkerResponse)(nil), // 124: substrate.ateapi.v1.RegisterWorkerResponse + (*SetWorkerSandboxRuntimesRequest)(nil), // 125: substrate.ateapi.v1.SetWorkerSandboxRuntimesRequest + (*SetWorkerSandboxRuntimesResponse)(nil), // 126: substrate.ateapi.v1.SetWorkerSandboxRuntimesResponse + (*MintWorkerActorCertificateRequest)(nil), // 127: substrate.ateapi.v1.MintWorkerActorCertificateRequest + (*MintWorkerActorCertificateResponse)(nil), // 128: substrate.ateapi.v1.MintWorkerActorCertificateResponse + (*MintWorkerActorJwtRequest)(nil), // 129: substrate.ateapi.v1.MintWorkerActorJwtRequest + (*MintWorkerActorJwtResponse)(nil), // 130: substrate.ateapi.v1.MintWorkerActorJwtResponse + (*RequestActorSuspendRequest)(nil), // 131: substrate.ateapi.v1.RequestActorSuspendRequest + (*RequestActorSuspendResponse)(nil), // 132: substrate.ateapi.v1.RequestActorSuspendResponse + (*ReportActorCrashedRequest)(nil), // 133: substrate.ateapi.v1.ReportActorCrashedRequest + (*ReportActorCrashedResponse)(nil), // 134: substrate.ateapi.v1.ReportActorCrashedResponse + (*ReportActorSnapshotStateRequest)(nil), // 135: substrate.ateapi.v1.ReportActorSnapshotStateRequest + (*ReportActorSnapshotStateResponse)(nil), // 136: substrate.ateapi.v1.ReportActorSnapshotStateResponse + (*ActorSnapshotState)(nil), // 137: substrate.ateapi.v1.ActorSnapshotState + (*SnapshotReport)(nil), // 138: substrate.ateapi.v1.SnapshotReport + (*AccessPolicy)(nil), // 139: substrate.ateapi.v1.AccessPolicy + (*Binding)(nil), // 140: substrate.ateapi.v1.Binding + (*GetGlobalAccessPolicyRequest)(nil), // 141: substrate.ateapi.v1.GetGlobalAccessPolicyRequest + (*CreateGlobalAccessPolicyRequest)(nil), // 142: substrate.ateapi.v1.CreateGlobalAccessPolicyRequest + (*UpdateGlobalAccessPolicyRequest)(nil), // 143: substrate.ateapi.v1.UpdateGlobalAccessPolicyRequest + (*GetAtespaceAccessPolicyRequest)(nil), // 144: substrate.ateapi.v1.GetAtespaceAccessPolicyRequest + (*CreateAtespaceAccessPolicyRequest)(nil), // 145: substrate.ateapi.v1.CreateAtespaceAccessPolicyRequest + (*UpdateAtespaceAccessPolicyRequest)(nil), // 146: substrate.ateapi.v1.UpdateAtespaceAccessPolicyRequest + (*DeleteAtespaceAccessPolicyRequest)(nil), // 147: substrate.ateapi.v1.DeleteAtespaceAccessPolicyRequest + (*ResourceAnnotation)(nil), // 148: substrate.ateapi.v1.ResourceAnnotation + (*MethodAnnotation)(nil), // 149: substrate.ateapi.v1.MethodAnnotation + nil, // 150: substrate.ateapi.v1.Selector.MatchLabelsEntry + nil, // 151: substrate.ateapi.v1.ExternalVolumeStatus.VolumeContextEntry + nil, // 152: substrate.ateapi.v1.Worker.LabelsEntry + (*timestamppb.Timestamp)(nil), // 153: google.protobuf.Timestamp + (*descriptorpb.MessageOptions)(nil), // 154: google.protobuf.MessageOptions + (*descriptorpb.MethodOptions)(nil), // 155: google.protobuf.MethodOptions } var file_api_ateapi_v1_ateapi_proto_depIdxs = []int32{ 1, // 0: substrate.ateapi.v1.SnapshotStorage.state:type_name -> substrate.ateapi.v1.SnapshotStorageState @@ -10951,13 +10819,13 @@ var file_api_ateapi_v1_ateapi_proto_depIdxs = []int32{ 0, // 2: substrate.ateapi.v1.Snapshot.owner:type_name -> substrate.ateapi.v1.SnapshotOwner 2, // 3: substrate.ateapi.v1.Snapshot.fidelity:type_name -> substrate.ateapi.v1.SnapshotFidelity 11, // 4: substrate.ateapi.v1.Snapshot.durable_snapshot:type_name -> substrate.ateapi.v1.SnapshotStorage - 118, // 5: substrate.ateapi.v1.Snapshot.sandbox_runtime:type_name -> substrate.ateapi.v1.ActorSandboxRuntime - 152, // 6: substrate.ateapi.v1.Selector.match_labels:type_name -> substrate.ateapi.v1.Selector.MatchLabelsEntry - 155, // 7: substrate.ateapi.v1.ResourceMetadata.create_time:type_name -> google.protobuf.Timestamp - 155, // 8: substrate.ateapi.v1.ResourceMetadata.update_time:type_name -> google.protobuf.Timestamp - 155, // 9: substrate.ateapi.v1.ResourceMetadata.delete_time:type_name -> google.protobuf.Timestamp + 116, // 5: substrate.ateapi.v1.Snapshot.sandbox_runtime:type_name -> substrate.ateapi.v1.ActorSandboxRuntime + 150, // 6: substrate.ateapi.v1.Selector.match_labels:type_name -> substrate.ateapi.v1.Selector.MatchLabelsEntry + 153, // 7: substrate.ateapi.v1.ResourceMetadata.create_time:type_name -> google.protobuf.Timestamp + 153, // 8: substrate.ateapi.v1.ResourceMetadata.update_time:type_name -> google.protobuf.Timestamp + 153, // 9: substrate.ateapi.v1.ResourceMetadata.delete_time:type_name -> google.protobuf.Timestamp 16, // 10: substrate.ateapi.v1.ExternalVolumes.volumes:type_name -> substrate.ateapi.v1.ExternalVolumeStatus - 153, // 11: substrate.ateapi.v1.ExternalVolumeStatus.volume_context:type_name -> substrate.ateapi.v1.ExternalVolumeStatus.VolumeContextEntry + 151, // 11: substrate.ateapi.v1.ExternalVolumeStatus.volume_context:type_name -> substrate.ateapi.v1.ExternalVolumeStatus.VolumeContextEntry 14, // 12: substrate.ateapi.v1.Actor.metadata:type_name -> substrate.ateapi.v1.ResourceMetadata 35, // 13: substrate.ateapi.v1.Actor.actor_template:type_name -> substrate.ateapi.v1.ObjectRef 13, // 14: substrate.ateapi.v1.Actor.worker_selector:type_name -> substrate.ateapi.v1.Selector @@ -10982,9 +10850,9 @@ var file_api_ateapi_v1_ateapi_proto_depIdxs = []int32{ 15, // 33: substrate.ateapi.v1.ActorStatus.external_volumes:type_name -> substrate.ateapi.v1.ExternalVolumes 30, // 34: substrate.ateapi.v1.ActorStatus.crash:type_name -> substrate.ateapi.v1.ActorCrash 12, // 35: substrate.ateapi.v1.ActorStatus.snapshots:type_name -> substrate.ateapi.v1.Snapshot - 155, // 36: substrate.ateapi.v1.ActorCrash.crash_time:type_name -> google.protobuf.Timestamp + 153, // 36: substrate.ateapi.v1.ActorCrash.crash_time:type_name -> google.protobuf.Timestamp 35, // 37: substrate.ateapi.v1.WorkerAssignment.worker:type_name -> substrate.ateapi.v1.ObjectRef - 118, // 38: substrate.ateapi.v1.WorkerAssignment.sandbox_runtime:type_name -> substrate.ateapi.v1.ActorSandboxRuntime + 116, // 38: substrate.ateapi.v1.WorkerAssignment.sandbox_runtime:type_name -> substrate.ateapi.v1.ActorSandboxRuntime 12, // 39: substrate.ateapi.v1.TagStatus.snapshot:type_name -> substrate.ateapi.v1.Snapshot 14, // 40: substrate.ateapi.v1.Tag.metadata:type_name -> substrate.ateapi.v1.ResourceMetadata 32, // 41: substrate.ateapi.v1.Tag.status:type_name -> substrate.ateapi.v1.TagStatus @@ -11001,7 +10869,7 @@ var file_api_ateapi_v1_ateapi_proto_depIdxs = []int32{ 40, // 52: substrate.ateapi.v1.ActorTemplate.status:type_name -> substrate.ateapi.v1.ActorTemplateStatus 38, // 53: substrate.ateapi.v1.ResourceRequirements.limits:type_name -> substrate.ateapi.v1.Resources 35, // 54: substrate.ateapi.v1.GoldenSnapshotStatus.golden_tag:type_name -> substrate.ateapi.v1.ObjectRef - 155, // 55: substrate.ateapi.v1.GoldenSnapshotStatus.snapshot_time:type_name -> google.protobuf.Timestamp + 153, // 55: substrate.ateapi.v1.GoldenSnapshotStatus.snapshot_time:type_name -> google.protobuf.Timestamp 39, // 56: substrate.ateapi.v1.ActorTemplateStatus.golden_snapshot_status:type_name -> substrate.ateapi.v1.GoldenSnapshotStatus 2, // 57: substrate.ateapi.v1.SnapshotConfig.preferred_fidelity:type_name -> substrate.ateapi.v1.SnapshotFidelity 42, // 58: substrate.ateapi.v1.SnapshotConfig.golden_snapshot_config:type_name -> substrate.ateapi.v1.GoldenSnapshotConfig @@ -11025,16 +10893,16 @@ var file_api_ateapi_v1_ateapi_proto_depIdxs = []int32{ 6, // 76: substrate.ateapi.v1.ActorMetadataItem.field:type_name -> substrate.ateapi.v1.ActorMetadataField 34, // 77: substrate.ateapi.v1.CreateAtespaceRequest.atespace:type_name -> substrate.ateapi.v1.Atespace 35, // 78: substrate.ateapi.v1.GetAtespaceRequest.atespace:type_name -> substrate.ateapi.v1.ObjectRef - 96, // 79: substrate.ateapi.v1.ListAtespacesRequest.options:type_name -> substrate.ateapi.v1.ListOptions + 94, // 79: substrate.ateapi.v1.ListAtespacesRequest.options:type_name -> substrate.ateapi.v1.ListOptions 34, // 80: substrate.ateapi.v1.ListAtespacesResponse.atespaces:type_name -> substrate.ateapi.v1.Atespace 35, // 81: substrate.ateapi.v1.DeleteAtespaceRequest.atespace:type_name -> substrate.ateapi.v1.ObjectRef - 95, // 82: substrate.ateapi.v1.DeleteAtespaceRequest.options:type_name -> substrate.ateapi.v1.DeleteOptions + 93, // 82: substrate.ateapi.v1.DeleteAtespaceRequest.options:type_name -> substrate.ateapi.v1.DeleteOptions 36, // 83: substrate.ateapi.v1.CreateActorTemplateRequest.actor_template:type_name -> substrate.ateapi.v1.ActorTemplate 35, // 84: substrate.ateapi.v1.GetActorTemplateRequest.actor_template:type_name -> substrate.ateapi.v1.ObjectRef - 96, // 85: substrate.ateapi.v1.ListActorTemplatesRequest.options:type_name -> substrate.ateapi.v1.ListOptions + 94, // 85: substrate.ateapi.v1.ListActorTemplatesRequest.options:type_name -> substrate.ateapi.v1.ListOptions 36, // 86: substrate.ateapi.v1.ListActorTemplatesResponse.actor_templates:type_name -> substrate.ateapi.v1.ActorTemplate 35, // 87: substrate.ateapi.v1.DeleteActorTemplateRequest.actor_template:type_name -> substrate.ateapi.v1.ObjectRef - 95, // 88: substrate.ateapi.v1.DeleteActorTemplateRequest.options:type_name -> substrate.ateapi.v1.DeleteOptions + 93, // 88: substrate.ateapi.v1.DeleteActorTemplateRequest.options:type_name -> substrate.ateapi.v1.DeleteOptions 35, // 89: substrate.ateapi.v1.GetActorRequest.actor:type_name -> substrate.ateapi.v1.ObjectRef 17, // 90: substrate.ateapi.v1.CreateActorRequest.actor:type_name -> substrate.ateapi.v1.Actor 17, // 91: substrate.ateapi.v1.UpdateActorRequest.actor:type_name -> substrate.ateapi.v1.Actor @@ -11047,208 +10915,205 @@ var file_api_ateapi_v1_ateapi_proto_depIdxs = []int32{ 35, // 98: substrate.ateapi.v1.RevertActorRequest.actor:type_name -> substrate.ateapi.v1.ObjectRef 17, // 99: substrate.ateapi.v1.RevertActorResponse.actor:type_name -> substrate.ateapi.v1.Actor 35, // 100: substrate.ateapi.v1.DeleteActorRequest.actor:type_name -> substrate.ateapi.v1.ObjectRef - 95, // 101: substrate.ateapi.v1.DeleteActorRequest.options:type_name -> substrate.ateapi.v1.DeleteOptions + 93, // 101: substrate.ateapi.v1.DeleteActorRequest.options:type_name -> substrate.ateapi.v1.DeleteOptions 35, // 102: substrate.ateapi.v1.GetActorEgressPolicyRequest.actor:type_name -> substrate.ateapi.v1.ObjectRef 35, // 103: substrate.ateapi.v1.CreateActorEgressPolicyRequest.actor:type_name -> substrate.ateapi.v1.ObjectRef 18, // 104: substrate.ateapi.v1.CreateActorEgressPolicyRequest.egress_policy:type_name -> substrate.ateapi.v1.EgressPolicy 35, // 105: substrate.ateapi.v1.UpdateActorEgressPolicyRequest.actor:type_name -> substrate.ateapi.v1.ObjectRef 18, // 106: substrate.ateapi.v1.UpdateActorEgressPolicyRequest.egress_policy:type_name -> substrate.ateapi.v1.EgressPolicy 35, // 107: substrate.ateapi.v1.DeleteActorEgressPolicyRequest.actor:type_name -> substrate.ateapi.v1.ObjectRef - 95, // 108: substrate.ateapi.v1.DeleteActorEgressPolicyRequest.options:type_name -> substrate.ateapi.v1.DeleteOptions + 93, // 108: substrate.ateapi.v1.DeleteActorEgressPolicyRequest.options:type_name -> substrate.ateapi.v1.DeleteOptions 35, // 109: substrate.ateapi.v1.GetTagRequest.tag:type_name -> substrate.ateapi.v1.ObjectRef 35, // 110: substrate.ateapi.v1.MintActorJwtRequest.actor:type_name -> substrate.ateapi.v1.ObjectRef - 155, // 111: substrate.ateapi.v1.MintActorJwtResponse.expire_time:type_name -> google.protobuf.Timestamp - 35, // 112: substrate.ateapi.v1.MintActorCertificateRequest.actor:type_name -> substrate.ateapi.v1.ObjectRef - 96, // 113: substrate.ateapi.v1.ListTagsRequest.options:type_name -> substrate.ateapi.v1.ListOptions - 33, // 114: substrate.ateapi.v1.ListTagsResponse.tags:type_name -> substrate.ateapi.v1.Tag - 33, // 115: substrate.ateapi.v1.CreateTagRequest.tag:type_name -> substrate.ateapi.v1.Tag - 33, // 116: substrate.ateapi.v1.UpdateTagRequest.tag:type_name -> substrate.ateapi.v1.Tag - 35, // 117: substrate.ateapi.v1.DeleteTagRequest.tag:type_name -> substrate.ateapi.v1.ObjectRef - 95, // 118: substrate.ateapi.v1.DeleteTagRequest.options:type_name -> substrate.ateapi.v1.DeleteOptions - 35, // 119: substrate.ateapi.v1.ListWorkerActorAssignmentsRequest.worker:type_name -> substrate.ateapi.v1.ObjectRef - 96, // 120: substrate.ateapi.v1.ListWorkerActorAssignmentsRequest.options:type_name -> substrate.ateapi.v1.ListOptions - 122, // 121: substrate.ateapi.v1.ListWorkerActorAssignmentsResponse.actor_assignments:type_name -> substrate.ateapi.v1.ActorAssignment - 96, // 122: substrate.ateapi.v1.ListWorkersRequest.options:type_name -> substrate.ateapi.v1.ListOptions - 113, // 123: substrate.ateapi.v1.ListWorkersResponse.workers:type_name -> substrate.ateapi.v1.Worker - 35, // 124: substrate.ateapi.v1.GetWorkerRequest.worker:type_name -> substrate.ateapi.v1.ObjectRef - 113, // 125: substrate.ateapi.v1.CreateWorkerRequest.worker:type_name -> substrate.ateapi.v1.Worker - 113, // 126: substrate.ateapi.v1.UpdateWorkerRequest.worker:type_name -> substrate.ateapi.v1.Worker - 35, // 127: substrate.ateapi.v1.DeleteWorkerRequest.worker:type_name -> substrate.ateapi.v1.ObjectRef - 95, // 128: substrate.ateapi.v1.DeleteWorkerRequest.options:type_name -> substrate.ateapi.v1.DeleteOptions - 35, // 129: substrate.ateapi.v1.DrainWorkerRequest.worker:type_name -> substrate.ateapi.v1.ObjectRef - 113, // 130: substrate.ateapi.v1.DrainWorkerResponse.worker:type_name -> substrate.ateapi.v1.Worker - 35, // 131: substrate.ateapi.v1.MarkWorkerAvailableRequest.worker:type_name -> substrate.ateapi.v1.ObjectRef - 113, // 132: substrate.ateapi.v1.MarkWorkerAvailableResponse.worker:type_name -> substrate.ateapi.v1.Worker - 35, // 133: substrate.ateapi.v1.MarkWorkerUnavailableRequest.worker:type_name -> substrate.ateapi.v1.ObjectRef - 113, // 134: substrate.ateapi.v1.MarkWorkerUnavailableResponse.worker:type_name -> substrate.ateapi.v1.Worker - 96, // 135: substrate.ateapi.v1.ListActorsRequest.options:type_name -> substrate.ateapi.v1.ListOptions - 17, // 136: substrate.ateapi.v1.ListActorsResponse.actors:type_name -> substrate.ateapi.v1.Actor - 14, // 137: substrate.ateapi.v1.Worker.metadata:type_name -> substrate.ateapi.v1.ResourceMetadata - 154, // 138: substrate.ateapi.v1.Worker.labels:type_name -> substrate.ateapi.v1.Worker.LabelsEntry - 114, // 139: substrate.ateapi.v1.Worker.status:type_name -> substrate.ateapi.v1.WorkerStatus - 8, // 140: substrate.ateapi.v1.WorkerStatus.state:type_name -> substrate.ateapi.v1.WorkerState - 121, // 141: substrate.ateapi.v1.WorkerStatus.capacity:type_name -> substrate.ateapi.v1.WorkerResources - 121, // 142: substrate.ateapi.v1.WorkerStatus.allocated:type_name -> substrate.ateapi.v1.WorkerResources - 115, // 143: substrate.ateapi.v1.WorkerStatus.sandbox_classes:type_name -> substrate.ateapi.v1.WorkerSandboxClass - 116, // 144: substrate.ateapi.v1.WorkerSandboxClass.default_runtime:type_name -> substrate.ateapi.v1.WorkerSandboxRuntime - 116, // 145: substrate.ateapi.v1.WorkerSandboxClass.restorable_runtimes:type_name -> substrate.ateapi.v1.WorkerSandboxRuntime - 119, // 146: substrate.ateapi.v1.WorkerSandboxRuntime.version:type_name -> substrate.ateapi.v1.VersionedSandboxCompat - 117, // 147: substrate.ateapi.v1.WorkerSandboxRuntime.actor_overhead:type_name -> substrate.ateapi.v1.RuntimeOverhead - 119, // 148: substrate.ateapi.v1.ActorSandboxRuntime.version:type_name -> substrate.ateapi.v1.VersionedSandboxCompat - 120, // 149: substrate.ateapi.v1.VersionedSandboxCompat.attributes:type_name -> substrate.ateapi.v1.AttributeEntry - 14, // 150: substrate.ateapi.v1.ActorAssignment.metadata:type_name -> substrate.ateapi.v1.ResourceMetadata - 35, // 151: substrate.ateapi.v1.ActorAssignment.actor:type_name -> substrate.ateapi.v1.ObjectRef - 38, // 152: substrate.ateapi.v1.ActorAssignment.resources:type_name -> substrate.ateapi.v1.Resources - 35, // 153: substrate.ateapi.v1.AcquireWorkerEpochRequest.worker:type_name -> substrate.ateapi.v1.ObjectRef - 35, // 154: substrate.ateapi.v1.RegisterWorkerRequest.worker:type_name -> substrate.ateapi.v1.ObjectRef - 121, // 155: substrate.ateapi.v1.RegisterWorkerRequest.capacity:type_name -> substrate.ateapi.v1.WorkerResources - 113, // 156: substrate.ateapi.v1.RegisterWorkerResponse.worker:type_name -> substrate.ateapi.v1.Worker - 35, // 157: substrate.ateapi.v1.SetWorkerSandboxRuntimesRequest.worker:type_name -> substrate.ateapi.v1.ObjectRef - 115, // 158: substrate.ateapi.v1.SetWorkerSandboxRuntimesRequest.sandbox_classes:type_name -> substrate.ateapi.v1.WorkerSandboxClass - 113, // 159: substrate.ateapi.v1.SetWorkerSandboxRuntimesResponse.worker:type_name -> substrate.ateapi.v1.Worker - 35, // 160: substrate.ateapi.v1.MintWorkerActorCertificateRequest.actor:type_name -> substrate.ateapi.v1.ObjectRef - 35, // 161: substrate.ateapi.v1.MintWorkerActorCertificateRequest.worker:type_name -> substrate.ateapi.v1.ObjectRef - 35, // 162: substrate.ateapi.v1.MintWorkerActorJwtRequest.actor:type_name -> substrate.ateapi.v1.ObjectRef - 155, // 163: substrate.ateapi.v1.MintWorkerActorJwtResponse.expire_time:type_name -> google.protobuf.Timestamp - 35, // 164: substrate.ateapi.v1.RequestActorSuspendRequest.worker:type_name -> substrate.ateapi.v1.ObjectRef - 35, // 165: substrate.ateapi.v1.RequestActorSuspendRequest.actor:type_name -> substrate.ateapi.v1.ObjectRef - 17, // 166: substrate.ateapi.v1.RequestActorSuspendResponse.actor:type_name -> substrate.ateapi.v1.Actor - 35, // 167: substrate.ateapi.v1.ReportActorCrashedRequest.worker:type_name -> substrate.ateapi.v1.ObjectRef - 35, // 168: substrate.ateapi.v1.ReportActorCrashedRequest.actor:type_name -> substrate.ateapi.v1.ObjectRef - 17, // 169: substrate.ateapi.v1.ReportActorCrashedResponse.actor:type_name -> substrate.ateapi.v1.Actor - 35, // 170: substrate.ateapi.v1.ReportActorSnapshotStateRequest.actor:type_name -> substrate.ateapi.v1.ObjectRef - 139, // 171: substrate.ateapi.v1.ReportActorSnapshotStateRequest.state:type_name -> substrate.ateapi.v1.ActorSnapshotState - 140, // 172: substrate.ateapi.v1.ActorSnapshotState.latest_local:type_name -> substrate.ateapi.v1.SnapshotReport - 140, // 173: substrate.ateapi.v1.ActorSnapshotState.uploading:type_name -> substrate.ateapi.v1.SnapshotReport - 140, // 174: substrate.ateapi.v1.ActorSnapshotState.latest_durable:type_name -> substrate.ateapi.v1.SnapshotReport - 155, // 175: substrate.ateapi.v1.ActorSnapshotState.upload_due_time:type_name -> google.protobuf.Timestamp - 2, // 176: substrate.ateapi.v1.SnapshotReport.fidelity:type_name -> substrate.ateapi.v1.SnapshotFidelity - 10, // 177: substrate.ateapi.v1.SnapshotReport.object:type_name -> substrate.ateapi.v1.ObjectSnapshot - 155, // 178: substrate.ateapi.v1.SnapshotReport.local_commit_time:type_name -> google.protobuf.Timestamp - 155, // 179: substrate.ateapi.v1.SnapshotReport.durable_complete_time:type_name -> google.protobuf.Timestamp - 14, // 180: substrate.ateapi.v1.AccessPolicy.metadata:type_name -> substrate.ateapi.v1.ResourceMetadata - 142, // 181: substrate.ateapi.v1.AccessPolicy.bindings:type_name -> substrate.ateapi.v1.Binding - 141, // 182: substrate.ateapi.v1.CreateGlobalAccessPolicyRequest.access_policy:type_name -> substrate.ateapi.v1.AccessPolicy - 141, // 183: substrate.ateapi.v1.UpdateGlobalAccessPolicyRequest.access_policy:type_name -> substrate.ateapi.v1.AccessPolicy - 35, // 184: substrate.ateapi.v1.GetAtespaceAccessPolicyRequest.atespace:type_name -> substrate.ateapi.v1.ObjectRef - 35, // 185: substrate.ateapi.v1.CreateAtespaceAccessPolicyRequest.atespace:type_name -> substrate.ateapi.v1.ObjectRef - 141, // 186: substrate.ateapi.v1.CreateAtespaceAccessPolicyRequest.access_policy:type_name -> substrate.ateapi.v1.AccessPolicy - 35, // 187: substrate.ateapi.v1.UpdateAtespaceAccessPolicyRequest.atespace:type_name -> substrate.ateapi.v1.ObjectRef - 141, // 188: substrate.ateapi.v1.UpdateAtespaceAccessPolicyRequest.access_policy:type_name -> substrate.ateapi.v1.AccessPolicy - 35, // 189: substrate.ateapi.v1.DeleteAtespaceAccessPolicyRequest.atespace:type_name -> substrate.ateapi.v1.ObjectRef - 95, // 190: substrate.ateapi.v1.DeleteAtespaceAccessPolicyRequest.options:type_name -> substrate.ateapi.v1.DeleteOptions - 9, // 191: substrate.ateapi.v1.ResourceAnnotation.scope:type_name -> substrate.ateapi.v1.Scope - 156, // 192: substrate.ateapi.v1.resource:extendee -> google.protobuf.MessageOptions - 157, // 193: substrate.ateapi.v1.method:extendee -> google.protobuf.MethodOptions - 150, // 194: substrate.ateapi.v1.resource:type_name -> substrate.ateapi.v1.ResourceAnnotation - 151, // 195: substrate.ateapi.v1.method:type_name -> substrate.ateapi.v1.MethodAnnotation - 70, // 196: substrate.ateapi.v1.Control.GetActor:input_type -> substrate.ateapi.v1.GetActorRequest - 71, // 197: substrate.ateapi.v1.Control.CreateActor:input_type -> substrate.ateapi.v1.CreateActorRequest - 72, // 198: substrate.ateapi.v1.Control.UpdateActor:input_type -> substrate.ateapi.v1.UpdateActorRequest - 73, // 199: substrate.ateapi.v1.Control.SuspendActor:input_type -> substrate.ateapi.v1.SuspendActorRequest - 76, // 200: substrate.ateapi.v1.Control.ResumeActor:input_type -> substrate.ateapi.v1.ResumeActorRequest - 78, // 201: substrate.ateapi.v1.Control.RevertActor:input_type -> substrate.ateapi.v1.RevertActorRequest - 80, // 202: substrate.ateapi.v1.Control.DeleteActor:input_type -> substrate.ateapi.v1.DeleteActorRequest - 81, // 203: substrate.ateapi.v1.Control.GetActorEgressPolicy:input_type -> substrate.ateapi.v1.GetActorEgressPolicyRequest - 82, // 204: substrate.ateapi.v1.Control.CreateActorEgressPolicy:input_type -> substrate.ateapi.v1.CreateActorEgressPolicyRequest - 83, // 205: substrate.ateapi.v1.Control.UpdateActorEgressPolicy:input_type -> substrate.ateapi.v1.UpdateActorEgressPolicyRequest - 84, // 206: substrate.ateapi.v1.Control.DeleteActorEgressPolicy:input_type -> substrate.ateapi.v1.DeleteActorEgressPolicyRequest - 86, // 207: substrate.ateapi.v1.Control.MintActorJwt:input_type -> substrate.ateapi.v1.MintActorJwtRequest - 88, // 208: substrate.ateapi.v1.Control.MintActorCertificate:input_type -> substrate.ateapi.v1.MintActorCertificateRequest - 92, // 209: substrate.ateapi.v1.Control.CreateTag:input_type -> substrate.ateapi.v1.CreateTagRequest - 85, // 210: substrate.ateapi.v1.Control.GetTag:input_type -> substrate.ateapi.v1.GetTagRequest - 90, // 211: substrate.ateapi.v1.Control.ListTags:input_type -> substrate.ateapi.v1.ListTagsRequest - 93, // 212: substrate.ateapi.v1.Control.UpdateTag:input_type -> substrate.ateapi.v1.UpdateTagRequest - 94, // 213: substrate.ateapi.v1.Control.DeleteTag:input_type -> substrate.ateapi.v1.DeleteTagRequest - 99, // 214: substrate.ateapi.v1.Control.ListWorkers:input_type -> substrate.ateapi.v1.ListWorkersRequest - 101, // 215: substrate.ateapi.v1.Control.GetWorker:input_type -> substrate.ateapi.v1.GetWorkerRequest - 102, // 216: substrate.ateapi.v1.Control.CreateWorker:input_type -> substrate.ateapi.v1.CreateWorkerRequest - 103, // 217: substrate.ateapi.v1.Control.UpdateWorker:input_type -> substrate.ateapi.v1.UpdateWorkerRequest - 104, // 218: substrate.ateapi.v1.Control.DeleteWorker:input_type -> substrate.ateapi.v1.DeleteWorkerRequest - 105, // 219: substrate.ateapi.v1.Control.DrainWorker:input_type -> substrate.ateapi.v1.DrainWorkerRequest - 107, // 220: substrate.ateapi.v1.Control.MarkWorkerAvailable:input_type -> substrate.ateapi.v1.MarkWorkerAvailableRequest - 109, // 221: substrate.ateapi.v1.Control.MarkWorkerUnavailable:input_type -> substrate.ateapi.v1.MarkWorkerUnavailableRequest - 97, // 222: substrate.ateapi.v1.Control.ListWorkerActorAssignments:input_type -> substrate.ateapi.v1.ListWorkerActorAssignmentsRequest - 111, // 223: substrate.ateapi.v1.Control.ListActors:input_type -> substrate.ateapi.v1.ListActorsRequest - 60, // 224: substrate.ateapi.v1.Control.CreateAtespace:input_type -> substrate.ateapi.v1.CreateAtespaceRequest - 61, // 225: substrate.ateapi.v1.Control.GetAtespace:input_type -> substrate.ateapi.v1.GetAtespaceRequest - 62, // 226: substrate.ateapi.v1.Control.ListAtespaces:input_type -> substrate.ateapi.v1.ListAtespacesRequest - 64, // 227: substrate.ateapi.v1.Control.DeleteAtespace:input_type -> substrate.ateapi.v1.DeleteAtespaceRequest - 65, // 228: substrate.ateapi.v1.Control.CreateActorTemplate:input_type -> substrate.ateapi.v1.CreateActorTemplateRequest - 66, // 229: substrate.ateapi.v1.Control.GetActorTemplate:input_type -> substrate.ateapi.v1.GetActorTemplateRequest - 67, // 230: substrate.ateapi.v1.Control.ListActorTemplates:input_type -> substrate.ateapi.v1.ListActorTemplatesRequest - 69, // 231: substrate.ateapi.v1.Control.DeleteActorTemplate:input_type -> substrate.ateapi.v1.DeleteActorTemplateRequest - 143, // 232: substrate.ateapi.v1.Control.GetGlobalAccessPolicy:input_type -> substrate.ateapi.v1.GetGlobalAccessPolicyRequest - 144, // 233: substrate.ateapi.v1.Control.CreateGlobalAccessPolicy:input_type -> substrate.ateapi.v1.CreateGlobalAccessPolicyRequest - 145, // 234: substrate.ateapi.v1.Control.UpdateGlobalAccessPolicy:input_type -> substrate.ateapi.v1.UpdateGlobalAccessPolicyRequest - 146, // 235: substrate.ateapi.v1.Control.GetAtespaceAccessPolicy:input_type -> substrate.ateapi.v1.GetAtespaceAccessPolicyRequest - 147, // 236: substrate.ateapi.v1.Control.CreateAtespaceAccessPolicy:input_type -> substrate.ateapi.v1.CreateAtespaceAccessPolicyRequest - 148, // 237: substrate.ateapi.v1.Control.UpdateAtespaceAccessPolicy:input_type -> substrate.ateapi.v1.UpdateAtespaceAccessPolicyRequest - 149, // 238: substrate.ateapi.v1.Control.DeleteAtespaceAccessPolicy:input_type -> substrate.ateapi.v1.DeleteAtespaceAccessPolicyRequest - 123, // 239: substrate.ateapi.v1.WorkerService.AcquireWorkerEpoch:input_type -> substrate.ateapi.v1.AcquireWorkerEpochRequest - 125, // 240: substrate.ateapi.v1.WorkerService.RegisterWorker:input_type -> substrate.ateapi.v1.RegisterWorkerRequest - 127, // 241: substrate.ateapi.v1.WorkerService.SetWorkerSandboxRuntimes:input_type -> substrate.ateapi.v1.SetWorkerSandboxRuntimesRequest - 129, // 242: substrate.ateapi.v1.WorkerService.MintWorkerActorCertificate:input_type -> substrate.ateapi.v1.MintWorkerActorCertificateRequest - 131, // 243: substrate.ateapi.v1.WorkerService.MintWorkerActorJwt:input_type -> substrate.ateapi.v1.MintWorkerActorJwtRequest - 133, // 244: substrate.ateapi.v1.WorkerService.RequestActorSuspend:input_type -> substrate.ateapi.v1.RequestActorSuspendRequest - 137, // 245: substrate.ateapi.v1.WorkerService.ReportActorSnapshotState:input_type -> substrate.ateapi.v1.ReportActorSnapshotStateRequest - 135, // 246: substrate.ateapi.v1.WorkerService.ReportActorCrashed:input_type -> substrate.ateapi.v1.ReportActorCrashedRequest - 17, // 247: substrate.ateapi.v1.Control.GetActor:output_type -> substrate.ateapi.v1.Actor - 17, // 248: substrate.ateapi.v1.Control.CreateActor:output_type -> substrate.ateapi.v1.Actor - 17, // 249: substrate.ateapi.v1.Control.UpdateActor:output_type -> substrate.ateapi.v1.Actor - 74, // 250: substrate.ateapi.v1.Control.SuspendActor:output_type -> substrate.ateapi.v1.SuspendActorResponse - 77, // 251: substrate.ateapi.v1.Control.ResumeActor:output_type -> substrate.ateapi.v1.ResumeActorResponse - 79, // 252: substrate.ateapi.v1.Control.RevertActor:output_type -> substrate.ateapi.v1.RevertActorResponse - 17, // 253: substrate.ateapi.v1.Control.DeleteActor:output_type -> substrate.ateapi.v1.Actor - 18, // 254: substrate.ateapi.v1.Control.GetActorEgressPolicy:output_type -> substrate.ateapi.v1.EgressPolicy - 18, // 255: substrate.ateapi.v1.Control.CreateActorEgressPolicy:output_type -> substrate.ateapi.v1.EgressPolicy - 18, // 256: substrate.ateapi.v1.Control.UpdateActorEgressPolicy:output_type -> substrate.ateapi.v1.EgressPolicy - 18, // 257: substrate.ateapi.v1.Control.DeleteActorEgressPolicy:output_type -> substrate.ateapi.v1.EgressPolicy - 87, // 258: substrate.ateapi.v1.Control.MintActorJwt:output_type -> substrate.ateapi.v1.MintActorJwtResponse - 89, // 259: substrate.ateapi.v1.Control.MintActorCertificate:output_type -> substrate.ateapi.v1.MintActorCertificateResponse - 33, // 260: substrate.ateapi.v1.Control.CreateTag:output_type -> substrate.ateapi.v1.Tag - 33, // 261: substrate.ateapi.v1.Control.GetTag:output_type -> substrate.ateapi.v1.Tag - 91, // 262: substrate.ateapi.v1.Control.ListTags:output_type -> substrate.ateapi.v1.ListTagsResponse - 33, // 263: substrate.ateapi.v1.Control.UpdateTag:output_type -> substrate.ateapi.v1.Tag - 33, // 264: substrate.ateapi.v1.Control.DeleteTag:output_type -> substrate.ateapi.v1.Tag - 100, // 265: substrate.ateapi.v1.Control.ListWorkers:output_type -> substrate.ateapi.v1.ListWorkersResponse - 113, // 266: substrate.ateapi.v1.Control.GetWorker:output_type -> substrate.ateapi.v1.Worker - 113, // 267: substrate.ateapi.v1.Control.CreateWorker:output_type -> substrate.ateapi.v1.Worker - 113, // 268: substrate.ateapi.v1.Control.UpdateWorker:output_type -> substrate.ateapi.v1.Worker - 113, // 269: substrate.ateapi.v1.Control.DeleteWorker:output_type -> substrate.ateapi.v1.Worker - 106, // 270: substrate.ateapi.v1.Control.DrainWorker:output_type -> substrate.ateapi.v1.DrainWorkerResponse - 108, // 271: substrate.ateapi.v1.Control.MarkWorkerAvailable:output_type -> substrate.ateapi.v1.MarkWorkerAvailableResponse - 110, // 272: substrate.ateapi.v1.Control.MarkWorkerUnavailable:output_type -> substrate.ateapi.v1.MarkWorkerUnavailableResponse - 98, // 273: substrate.ateapi.v1.Control.ListWorkerActorAssignments:output_type -> substrate.ateapi.v1.ListWorkerActorAssignmentsResponse - 112, // 274: substrate.ateapi.v1.Control.ListActors:output_type -> substrate.ateapi.v1.ListActorsResponse - 34, // 275: substrate.ateapi.v1.Control.CreateAtespace:output_type -> substrate.ateapi.v1.Atespace - 34, // 276: substrate.ateapi.v1.Control.GetAtespace:output_type -> substrate.ateapi.v1.Atespace - 63, // 277: substrate.ateapi.v1.Control.ListAtespaces:output_type -> substrate.ateapi.v1.ListAtespacesResponse - 34, // 278: substrate.ateapi.v1.Control.DeleteAtespace:output_type -> substrate.ateapi.v1.Atespace - 36, // 279: substrate.ateapi.v1.Control.CreateActorTemplate:output_type -> substrate.ateapi.v1.ActorTemplate - 36, // 280: substrate.ateapi.v1.Control.GetActorTemplate:output_type -> substrate.ateapi.v1.ActorTemplate - 68, // 281: substrate.ateapi.v1.Control.ListActorTemplates:output_type -> substrate.ateapi.v1.ListActorTemplatesResponse - 36, // 282: substrate.ateapi.v1.Control.DeleteActorTemplate:output_type -> substrate.ateapi.v1.ActorTemplate - 141, // 283: substrate.ateapi.v1.Control.GetGlobalAccessPolicy:output_type -> substrate.ateapi.v1.AccessPolicy - 141, // 284: substrate.ateapi.v1.Control.CreateGlobalAccessPolicy:output_type -> substrate.ateapi.v1.AccessPolicy - 141, // 285: substrate.ateapi.v1.Control.UpdateGlobalAccessPolicy:output_type -> substrate.ateapi.v1.AccessPolicy - 141, // 286: substrate.ateapi.v1.Control.GetAtespaceAccessPolicy:output_type -> substrate.ateapi.v1.AccessPolicy - 141, // 287: substrate.ateapi.v1.Control.CreateAtespaceAccessPolicy:output_type -> substrate.ateapi.v1.AccessPolicy - 141, // 288: substrate.ateapi.v1.Control.UpdateAtespaceAccessPolicy:output_type -> substrate.ateapi.v1.AccessPolicy - 141, // 289: substrate.ateapi.v1.Control.DeleteAtespaceAccessPolicy:output_type -> substrate.ateapi.v1.AccessPolicy - 124, // 290: substrate.ateapi.v1.WorkerService.AcquireWorkerEpoch:output_type -> substrate.ateapi.v1.AcquireWorkerEpochResponse - 126, // 291: substrate.ateapi.v1.WorkerService.RegisterWorker:output_type -> substrate.ateapi.v1.RegisterWorkerResponse - 128, // 292: substrate.ateapi.v1.WorkerService.SetWorkerSandboxRuntimes:output_type -> substrate.ateapi.v1.SetWorkerSandboxRuntimesResponse - 130, // 293: substrate.ateapi.v1.WorkerService.MintWorkerActorCertificate:output_type -> substrate.ateapi.v1.MintWorkerActorCertificateResponse - 132, // 294: substrate.ateapi.v1.WorkerService.MintWorkerActorJwt:output_type -> substrate.ateapi.v1.MintWorkerActorJwtResponse - 134, // 295: substrate.ateapi.v1.WorkerService.RequestActorSuspend:output_type -> substrate.ateapi.v1.RequestActorSuspendResponse - 138, // 296: substrate.ateapi.v1.WorkerService.ReportActorSnapshotState:output_type -> substrate.ateapi.v1.ReportActorSnapshotStateResponse - 136, // 297: substrate.ateapi.v1.WorkerService.ReportActorCrashed:output_type -> substrate.ateapi.v1.ReportActorCrashedResponse - 247, // [247:298] is the sub-list for method output_type - 196, // [196:247] is the sub-list for method input_type - 194, // [194:196] is the sub-list for extension type_name - 192, // [192:194] is the sub-list for extension extendee - 0, // [0:192] is the sub-list for field type_name + 153, // 111: substrate.ateapi.v1.MintActorJwtResponse.expire_time:type_name -> google.protobuf.Timestamp + 94, // 112: substrate.ateapi.v1.ListTagsRequest.options:type_name -> substrate.ateapi.v1.ListOptions + 33, // 113: substrate.ateapi.v1.ListTagsResponse.tags:type_name -> substrate.ateapi.v1.Tag + 33, // 114: substrate.ateapi.v1.CreateTagRequest.tag:type_name -> substrate.ateapi.v1.Tag + 33, // 115: substrate.ateapi.v1.UpdateTagRequest.tag:type_name -> substrate.ateapi.v1.Tag + 35, // 116: substrate.ateapi.v1.DeleteTagRequest.tag:type_name -> substrate.ateapi.v1.ObjectRef + 93, // 117: substrate.ateapi.v1.DeleteTagRequest.options:type_name -> substrate.ateapi.v1.DeleteOptions + 35, // 118: substrate.ateapi.v1.ListWorkerActorAssignmentsRequest.worker:type_name -> substrate.ateapi.v1.ObjectRef + 94, // 119: substrate.ateapi.v1.ListWorkerActorAssignmentsRequest.options:type_name -> substrate.ateapi.v1.ListOptions + 120, // 120: substrate.ateapi.v1.ListWorkerActorAssignmentsResponse.actor_assignments:type_name -> substrate.ateapi.v1.ActorAssignment + 94, // 121: substrate.ateapi.v1.ListWorkersRequest.options:type_name -> substrate.ateapi.v1.ListOptions + 111, // 122: substrate.ateapi.v1.ListWorkersResponse.workers:type_name -> substrate.ateapi.v1.Worker + 35, // 123: substrate.ateapi.v1.GetWorkerRequest.worker:type_name -> substrate.ateapi.v1.ObjectRef + 111, // 124: substrate.ateapi.v1.CreateWorkerRequest.worker:type_name -> substrate.ateapi.v1.Worker + 111, // 125: substrate.ateapi.v1.UpdateWorkerRequest.worker:type_name -> substrate.ateapi.v1.Worker + 35, // 126: substrate.ateapi.v1.DeleteWorkerRequest.worker:type_name -> substrate.ateapi.v1.ObjectRef + 93, // 127: substrate.ateapi.v1.DeleteWorkerRequest.options:type_name -> substrate.ateapi.v1.DeleteOptions + 35, // 128: substrate.ateapi.v1.DrainWorkerRequest.worker:type_name -> substrate.ateapi.v1.ObjectRef + 111, // 129: substrate.ateapi.v1.DrainWorkerResponse.worker:type_name -> substrate.ateapi.v1.Worker + 35, // 130: substrate.ateapi.v1.MarkWorkerAvailableRequest.worker:type_name -> substrate.ateapi.v1.ObjectRef + 111, // 131: substrate.ateapi.v1.MarkWorkerAvailableResponse.worker:type_name -> substrate.ateapi.v1.Worker + 35, // 132: substrate.ateapi.v1.MarkWorkerUnavailableRequest.worker:type_name -> substrate.ateapi.v1.ObjectRef + 111, // 133: substrate.ateapi.v1.MarkWorkerUnavailableResponse.worker:type_name -> substrate.ateapi.v1.Worker + 94, // 134: substrate.ateapi.v1.ListActorsRequest.options:type_name -> substrate.ateapi.v1.ListOptions + 17, // 135: substrate.ateapi.v1.ListActorsResponse.actors:type_name -> substrate.ateapi.v1.Actor + 14, // 136: substrate.ateapi.v1.Worker.metadata:type_name -> substrate.ateapi.v1.ResourceMetadata + 152, // 137: substrate.ateapi.v1.Worker.labels:type_name -> substrate.ateapi.v1.Worker.LabelsEntry + 112, // 138: substrate.ateapi.v1.Worker.status:type_name -> substrate.ateapi.v1.WorkerStatus + 8, // 139: substrate.ateapi.v1.WorkerStatus.state:type_name -> substrate.ateapi.v1.WorkerState + 119, // 140: substrate.ateapi.v1.WorkerStatus.capacity:type_name -> substrate.ateapi.v1.WorkerResources + 119, // 141: substrate.ateapi.v1.WorkerStatus.allocated:type_name -> substrate.ateapi.v1.WorkerResources + 113, // 142: substrate.ateapi.v1.WorkerStatus.sandbox_classes:type_name -> substrate.ateapi.v1.WorkerSandboxClass + 114, // 143: substrate.ateapi.v1.WorkerSandboxClass.default_runtime:type_name -> substrate.ateapi.v1.WorkerSandboxRuntime + 114, // 144: substrate.ateapi.v1.WorkerSandboxClass.restorable_runtimes:type_name -> substrate.ateapi.v1.WorkerSandboxRuntime + 117, // 145: substrate.ateapi.v1.WorkerSandboxRuntime.version:type_name -> substrate.ateapi.v1.VersionedSandboxCompat + 115, // 146: substrate.ateapi.v1.WorkerSandboxRuntime.actor_overhead:type_name -> substrate.ateapi.v1.RuntimeOverhead + 117, // 147: substrate.ateapi.v1.ActorSandboxRuntime.version:type_name -> substrate.ateapi.v1.VersionedSandboxCompat + 118, // 148: substrate.ateapi.v1.VersionedSandboxCompat.attributes:type_name -> substrate.ateapi.v1.AttributeEntry + 14, // 149: substrate.ateapi.v1.ActorAssignment.metadata:type_name -> substrate.ateapi.v1.ResourceMetadata + 35, // 150: substrate.ateapi.v1.ActorAssignment.actor:type_name -> substrate.ateapi.v1.ObjectRef + 38, // 151: substrate.ateapi.v1.ActorAssignment.resources:type_name -> substrate.ateapi.v1.Resources + 35, // 152: substrate.ateapi.v1.AcquireWorkerEpochRequest.worker:type_name -> substrate.ateapi.v1.ObjectRef + 35, // 153: substrate.ateapi.v1.RegisterWorkerRequest.worker:type_name -> substrate.ateapi.v1.ObjectRef + 119, // 154: substrate.ateapi.v1.RegisterWorkerRequest.capacity:type_name -> substrate.ateapi.v1.WorkerResources + 111, // 155: substrate.ateapi.v1.RegisterWorkerResponse.worker:type_name -> substrate.ateapi.v1.Worker + 35, // 156: substrate.ateapi.v1.SetWorkerSandboxRuntimesRequest.worker:type_name -> substrate.ateapi.v1.ObjectRef + 113, // 157: substrate.ateapi.v1.SetWorkerSandboxRuntimesRequest.sandbox_classes:type_name -> substrate.ateapi.v1.WorkerSandboxClass + 111, // 158: substrate.ateapi.v1.SetWorkerSandboxRuntimesResponse.worker:type_name -> substrate.ateapi.v1.Worker + 35, // 159: substrate.ateapi.v1.MintWorkerActorCertificateRequest.actor:type_name -> substrate.ateapi.v1.ObjectRef + 35, // 160: substrate.ateapi.v1.MintWorkerActorCertificateRequest.worker:type_name -> substrate.ateapi.v1.ObjectRef + 35, // 161: substrate.ateapi.v1.MintWorkerActorJwtRequest.actor:type_name -> substrate.ateapi.v1.ObjectRef + 153, // 162: substrate.ateapi.v1.MintWorkerActorJwtResponse.expire_time:type_name -> google.protobuf.Timestamp + 35, // 163: substrate.ateapi.v1.RequestActorSuspendRequest.worker:type_name -> substrate.ateapi.v1.ObjectRef + 35, // 164: substrate.ateapi.v1.RequestActorSuspendRequest.actor:type_name -> substrate.ateapi.v1.ObjectRef + 17, // 165: substrate.ateapi.v1.RequestActorSuspendResponse.actor:type_name -> substrate.ateapi.v1.Actor + 35, // 166: substrate.ateapi.v1.ReportActorCrashedRequest.worker:type_name -> substrate.ateapi.v1.ObjectRef + 35, // 167: substrate.ateapi.v1.ReportActorCrashedRequest.actor:type_name -> substrate.ateapi.v1.ObjectRef + 17, // 168: substrate.ateapi.v1.ReportActorCrashedResponse.actor:type_name -> substrate.ateapi.v1.Actor + 35, // 169: substrate.ateapi.v1.ReportActorSnapshotStateRequest.actor:type_name -> substrate.ateapi.v1.ObjectRef + 137, // 170: substrate.ateapi.v1.ReportActorSnapshotStateRequest.state:type_name -> substrate.ateapi.v1.ActorSnapshotState + 138, // 171: substrate.ateapi.v1.ActorSnapshotState.latest_local:type_name -> substrate.ateapi.v1.SnapshotReport + 138, // 172: substrate.ateapi.v1.ActorSnapshotState.uploading:type_name -> substrate.ateapi.v1.SnapshotReport + 138, // 173: substrate.ateapi.v1.ActorSnapshotState.latest_durable:type_name -> substrate.ateapi.v1.SnapshotReport + 153, // 174: substrate.ateapi.v1.ActorSnapshotState.upload_due_time:type_name -> google.protobuf.Timestamp + 2, // 175: substrate.ateapi.v1.SnapshotReport.fidelity:type_name -> substrate.ateapi.v1.SnapshotFidelity + 10, // 176: substrate.ateapi.v1.SnapshotReport.object:type_name -> substrate.ateapi.v1.ObjectSnapshot + 153, // 177: substrate.ateapi.v1.SnapshotReport.local_commit_time:type_name -> google.protobuf.Timestamp + 153, // 178: substrate.ateapi.v1.SnapshotReport.durable_complete_time:type_name -> google.protobuf.Timestamp + 14, // 179: substrate.ateapi.v1.AccessPolicy.metadata:type_name -> substrate.ateapi.v1.ResourceMetadata + 140, // 180: substrate.ateapi.v1.AccessPolicy.bindings:type_name -> substrate.ateapi.v1.Binding + 139, // 181: substrate.ateapi.v1.CreateGlobalAccessPolicyRequest.access_policy:type_name -> substrate.ateapi.v1.AccessPolicy + 139, // 182: substrate.ateapi.v1.UpdateGlobalAccessPolicyRequest.access_policy:type_name -> substrate.ateapi.v1.AccessPolicy + 35, // 183: substrate.ateapi.v1.GetAtespaceAccessPolicyRequest.atespace:type_name -> substrate.ateapi.v1.ObjectRef + 35, // 184: substrate.ateapi.v1.CreateAtespaceAccessPolicyRequest.atespace:type_name -> substrate.ateapi.v1.ObjectRef + 139, // 185: substrate.ateapi.v1.CreateAtespaceAccessPolicyRequest.access_policy:type_name -> substrate.ateapi.v1.AccessPolicy + 35, // 186: substrate.ateapi.v1.UpdateAtespaceAccessPolicyRequest.atespace:type_name -> substrate.ateapi.v1.ObjectRef + 139, // 187: substrate.ateapi.v1.UpdateAtespaceAccessPolicyRequest.access_policy:type_name -> substrate.ateapi.v1.AccessPolicy + 35, // 188: substrate.ateapi.v1.DeleteAtespaceAccessPolicyRequest.atespace:type_name -> substrate.ateapi.v1.ObjectRef + 93, // 189: substrate.ateapi.v1.DeleteAtespaceAccessPolicyRequest.options:type_name -> substrate.ateapi.v1.DeleteOptions + 9, // 190: substrate.ateapi.v1.ResourceAnnotation.scope:type_name -> substrate.ateapi.v1.Scope + 154, // 191: substrate.ateapi.v1.resource:extendee -> google.protobuf.MessageOptions + 155, // 192: substrate.ateapi.v1.method:extendee -> google.protobuf.MethodOptions + 148, // 193: substrate.ateapi.v1.resource:type_name -> substrate.ateapi.v1.ResourceAnnotation + 149, // 194: substrate.ateapi.v1.method:type_name -> substrate.ateapi.v1.MethodAnnotation + 70, // 195: substrate.ateapi.v1.Control.GetActor:input_type -> substrate.ateapi.v1.GetActorRequest + 71, // 196: substrate.ateapi.v1.Control.CreateActor:input_type -> substrate.ateapi.v1.CreateActorRequest + 72, // 197: substrate.ateapi.v1.Control.UpdateActor:input_type -> substrate.ateapi.v1.UpdateActorRequest + 73, // 198: substrate.ateapi.v1.Control.SuspendActor:input_type -> substrate.ateapi.v1.SuspendActorRequest + 76, // 199: substrate.ateapi.v1.Control.ResumeActor:input_type -> substrate.ateapi.v1.ResumeActorRequest + 78, // 200: substrate.ateapi.v1.Control.RevertActor:input_type -> substrate.ateapi.v1.RevertActorRequest + 80, // 201: substrate.ateapi.v1.Control.DeleteActor:input_type -> substrate.ateapi.v1.DeleteActorRequest + 81, // 202: substrate.ateapi.v1.Control.GetActorEgressPolicy:input_type -> substrate.ateapi.v1.GetActorEgressPolicyRequest + 82, // 203: substrate.ateapi.v1.Control.CreateActorEgressPolicy:input_type -> substrate.ateapi.v1.CreateActorEgressPolicyRequest + 83, // 204: substrate.ateapi.v1.Control.UpdateActorEgressPolicy:input_type -> substrate.ateapi.v1.UpdateActorEgressPolicyRequest + 84, // 205: substrate.ateapi.v1.Control.DeleteActorEgressPolicy:input_type -> substrate.ateapi.v1.DeleteActorEgressPolicyRequest + 86, // 206: substrate.ateapi.v1.Control.MintActorJwt:input_type -> substrate.ateapi.v1.MintActorJwtRequest + 90, // 207: substrate.ateapi.v1.Control.CreateTag:input_type -> substrate.ateapi.v1.CreateTagRequest + 85, // 208: substrate.ateapi.v1.Control.GetTag:input_type -> substrate.ateapi.v1.GetTagRequest + 88, // 209: substrate.ateapi.v1.Control.ListTags:input_type -> substrate.ateapi.v1.ListTagsRequest + 91, // 210: substrate.ateapi.v1.Control.UpdateTag:input_type -> substrate.ateapi.v1.UpdateTagRequest + 92, // 211: substrate.ateapi.v1.Control.DeleteTag:input_type -> substrate.ateapi.v1.DeleteTagRequest + 97, // 212: substrate.ateapi.v1.Control.ListWorkers:input_type -> substrate.ateapi.v1.ListWorkersRequest + 99, // 213: substrate.ateapi.v1.Control.GetWorker:input_type -> substrate.ateapi.v1.GetWorkerRequest + 100, // 214: substrate.ateapi.v1.Control.CreateWorker:input_type -> substrate.ateapi.v1.CreateWorkerRequest + 101, // 215: substrate.ateapi.v1.Control.UpdateWorker:input_type -> substrate.ateapi.v1.UpdateWorkerRequest + 102, // 216: substrate.ateapi.v1.Control.DeleteWorker:input_type -> substrate.ateapi.v1.DeleteWorkerRequest + 103, // 217: substrate.ateapi.v1.Control.DrainWorker:input_type -> substrate.ateapi.v1.DrainWorkerRequest + 105, // 218: substrate.ateapi.v1.Control.MarkWorkerAvailable:input_type -> substrate.ateapi.v1.MarkWorkerAvailableRequest + 107, // 219: substrate.ateapi.v1.Control.MarkWorkerUnavailable:input_type -> substrate.ateapi.v1.MarkWorkerUnavailableRequest + 95, // 220: substrate.ateapi.v1.Control.ListWorkerActorAssignments:input_type -> substrate.ateapi.v1.ListWorkerActorAssignmentsRequest + 109, // 221: substrate.ateapi.v1.Control.ListActors:input_type -> substrate.ateapi.v1.ListActorsRequest + 60, // 222: substrate.ateapi.v1.Control.CreateAtespace:input_type -> substrate.ateapi.v1.CreateAtespaceRequest + 61, // 223: substrate.ateapi.v1.Control.GetAtespace:input_type -> substrate.ateapi.v1.GetAtespaceRequest + 62, // 224: substrate.ateapi.v1.Control.ListAtespaces:input_type -> substrate.ateapi.v1.ListAtespacesRequest + 64, // 225: substrate.ateapi.v1.Control.DeleteAtespace:input_type -> substrate.ateapi.v1.DeleteAtespaceRequest + 65, // 226: substrate.ateapi.v1.Control.CreateActorTemplate:input_type -> substrate.ateapi.v1.CreateActorTemplateRequest + 66, // 227: substrate.ateapi.v1.Control.GetActorTemplate:input_type -> substrate.ateapi.v1.GetActorTemplateRequest + 67, // 228: substrate.ateapi.v1.Control.ListActorTemplates:input_type -> substrate.ateapi.v1.ListActorTemplatesRequest + 69, // 229: substrate.ateapi.v1.Control.DeleteActorTemplate:input_type -> substrate.ateapi.v1.DeleteActorTemplateRequest + 141, // 230: substrate.ateapi.v1.Control.GetGlobalAccessPolicy:input_type -> substrate.ateapi.v1.GetGlobalAccessPolicyRequest + 142, // 231: substrate.ateapi.v1.Control.CreateGlobalAccessPolicy:input_type -> substrate.ateapi.v1.CreateGlobalAccessPolicyRequest + 143, // 232: substrate.ateapi.v1.Control.UpdateGlobalAccessPolicy:input_type -> substrate.ateapi.v1.UpdateGlobalAccessPolicyRequest + 144, // 233: substrate.ateapi.v1.Control.GetAtespaceAccessPolicy:input_type -> substrate.ateapi.v1.GetAtespaceAccessPolicyRequest + 145, // 234: substrate.ateapi.v1.Control.CreateAtespaceAccessPolicy:input_type -> substrate.ateapi.v1.CreateAtespaceAccessPolicyRequest + 146, // 235: substrate.ateapi.v1.Control.UpdateAtespaceAccessPolicy:input_type -> substrate.ateapi.v1.UpdateAtespaceAccessPolicyRequest + 147, // 236: substrate.ateapi.v1.Control.DeleteAtespaceAccessPolicy:input_type -> substrate.ateapi.v1.DeleteAtespaceAccessPolicyRequest + 121, // 237: substrate.ateapi.v1.WorkerService.AcquireWorkerEpoch:input_type -> substrate.ateapi.v1.AcquireWorkerEpochRequest + 123, // 238: substrate.ateapi.v1.WorkerService.RegisterWorker:input_type -> substrate.ateapi.v1.RegisterWorkerRequest + 125, // 239: substrate.ateapi.v1.WorkerService.SetWorkerSandboxRuntimes:input_type -> substrate.ateapi.v1.SetWorkerSandboxRuntimesRequest + 127, // 240: substrate.ateapi.v1.WorkerService.MintWorkerActorCertificate:input_type -> substrate.ateapi.v1.MintWorkerActorCertificateRequest + 129, // 241: substrate.ateapi.v1.WorkerService.MintWorkerActorJwt:input_type -> substrate.ateapi.v1.MintWorkerActorJwtRequest + 131, // 242: substrate.ateapi.v1.WorkerService.RequestActorSuspend:input_type -> substrate.ateapi.v1.RequestActorSuspendRequest + 135, // 243: substrate.ateapi.v1.WorkerService.ReportActorSnapshotState:input_type -> substrate.ateapi.v1.ReportActorSnapshotStateRequest + 133, // 244: substrate.ateapi.v1.WorkerService.ReportActorCrashed:input_type -> substrate.ateapi.v1.ReportActorCrashedRequest + 17, // 245: substrate.ateapi.v1.Control.GetActor:output_type -> substrate.ateapi.v1.Actor + 17, // 246: substrate.ateapi.v1.Control.CreateActor:output_type -> substrate.ateapi.v1.Actor + 17, // 247: substrate.ateapi.v1.Control.UpdateActor:output_type -> substrate.ateapi.v1.Actor + 74, // 248: substrate.ateapi.v1.Control.SuspendActor:output_type -> substrate.ateapi.v1.SuspendActorResponse + 77, // 249: substrate.ateapi.v1.Control.ResumeActor:output_type -> substrate.ateapi.v1.ResumeActorResponse + 79, // 250: substrate.ateapi.v1.Control.RevertActor:output_type -> substrate.ateapi.v1.RevertActorResponse + 17, // 251: substrate.ateapi.v1.Control.DeleteActor:output_type -> substrate.ateapi.v1.Actor + 18, // 252: substrate.ateapi.v1.Control.GetActorEgressPolicy:output_type -> substrate.ateapi.v1.EgressPolicy + 18, // 253: substrate.ateapi.v1.Control.CreateActorEgressPolicy:output_type -> substrate.ateapi.v1.EgressPolicy + 18, // 254: substrate.ateapi.v1.Control.UpdateActorEgressPolicy:output_type -> substrate.ateapi.v1.EgressPolicy + 18, // 255: substrate.ateapi.v1.Control.DeleteActorEgressPolicy:output_type -> substrate.ateapi.v1.EgressPolicy + 87, // 256: substrate.ateapi.v1.Control.MintActorJwt:output_type -> substrate.ateapi.v1.MintActorJwtResponse + 33, // 257: substrate.ateapi.v1.Control.CreateTag:output_type -> substrate.ateapi.v1.Tag + 33, // 258: substrate.ateapi.v1.Control.GetTag:output_type -> substrate.ateapi.v1.Tag + 89, // 259: substrate.ateapi.v1.Control.ListTags:output_type -> substrate.ateapi.v1.ListTagsResponse + 33, // 260: substrate.ateapi.v1.Control.UpdateTag:output_type -> substrate.ateapi.v1.Tag + 33, // 261: substrate.ateapi.v1.Control.DeleteTag:output_type -> substrate.ateapi.v1.Tag + 98, // 262: substrate.ateapi.v1.Control.ListWorkers:output_type -> substrate.ateapi.v1.ListWorkersResponse + 111, // 263: substrate.ateapi.v1.Control.GetWorker:output_type -> substrate.ateapi.v1.Worker + 111, // 264: substrate.ateapi.v1.Control.CreateWorker:output_type -> substrate.ateapi.v1.Worker + 111, // 265: substrate.ateapi.v1.Control.UpdateWorker:output_type -> substrate.ateapi.v1.Worker + 111, // 266: substrate.ateapi.v1.Control.DeleteWorker:output_type -> substrate.ateapi.v1.Worker + 104, // 267: substrate.ateapi.v1.Control.DrainWorker:output_type -> substrate.ateapi.v1.DrainWorkerResponse + 106, // 268: substrate.ateapi.v1.Control.MarkWorkerAvailable:output_type -> substrate.ateapi.v1.MarkWorkerAvailableResponse + 108, // 269: substrate.ateapi.v1.Control.MarkWorkerUnavailable:output_type -> substrate.ateapi.v1.MarkWorkerUnavailableResponse + 96, // 270: substrate.ateapi.v1.Control.ListWorkerActorAssignments:output_type -> substrate.ateapi.v1.ListWorkerActorAssignmentsResponse + 110, // 271: substrate.ateapi.v1.Control.ListActors:output_type -> substrate.ateapi.v1.ListActorsResponse + 34, // 272: substrate.ateapi.v1.Control.CreateAtespace:output_type -> substrate.ateapi.v1.Atespace + 34, // 273: substrate.ateapi.v1.Control.GetAtespace:output_type -> substrate.ateapi.v1.Atespace + 63, // 274: substrate.ateapi.v1.Control.ListAtespaces:output_type -> substrate.ateapi.v1.ListAtespacesResponse + 34, // 275: substrate.ateapi.v1.Control.DeleteAtespace:output_type -> substrate.ateapi.v1.Atespace + 36, // 276: substrate.ateapi.v1.Control.CreateActorTemplate:output_type -> substrate.ateapi.v1.ActorTemplate + 36, // 277: substrate.ateapi.v1.Control.GetActorTemplate:output_type -> substrate.ateapi.v1.ActorTemplate + 68, // 278: substrate.ateapi.v1.Control.ListActorTemplates:output_type -> substrate.ateapi.v1.ListActorTemplatesResponse + 36, // 279: substrate.ateapi.v1.Control.DeleteActorTemplate:output_type -> substrate.ateapi.v1.ActorTemplate + 139, // 280: substrate.ateapi.v1.Control.GetGlobalAccessPolicy:output_type -> substrate.ateapi.v1.AccessPolicy + 139, // 281: substrate.ateapi.v1.Control.CreateGlobalAccessPolicy:output_type -> substrate.ateapi.v1.AccessPolicy + 139, // 282: substrate.ateapi.v1.Control.UpdateGlobalAccessPolicy:output_type -> substrate.ateapi.v1.AccessPolicy + 139, // 283: substrate.ateapi.v1.Control.GetAtespaceAccessPolicy:output_type -> substrate.ateapi.v1.AccessPolicy + 139, // 284: substrate.ateapi.v1.Control.CreateAtespaceAccessPolicy:output_type -> substrate.ateapi.v1.AccessPolicy + 139, // 285: substrate.ateapi.v1.Control.UpdateAtespaceAccessPolicy:output_type -> substrate.ateapi.v1.AccessPolicy + 139, // 286: substrate.ateapi.v1.Control.DeleteAtespaceAccessPolicy:output_type -> substrate.ateapi.v1.AccessPolicy + 122, // 287: substrate.ateapi.v1.WorkerService.AcquireWorkerEpoch:output_type -> substrate.ateapi.v1.AcquireWorkerEpochResponse + 124, // 288: substrate.ateapi.v1.WorkerService.RegisterWorker:output_type -> substrate.ateapi.v1.RegisterWorkerResponse + 126, // 289: substrate.ateapi.v1.WorkerService.SetWorkerSandboxRuntimes:output_type -> substrate.ateapi.v1.SetWorkerSandboxRuntimesResponse + 128, // 290: substrate.ateapi.v1.WorkerService.MintWorkerActorCertificate:output_type -> substrate.ateapi.v1.MintWorkerActorCertificateResponse + 130, // 291: substrate.ateapi.v1.WorkerService.MintWorkerActorJwt:output_type -> substrate.ateapi.v1.MintWorkerActorJwtResponse + 132, // 292: substrate.ateapi.v1.WorkerService.RequestActorSuspend:output_type -> substrate.ateapi.v1.RequestActorSuspendResponse + 136, // 293: substrate.ateapi.v1.WorkerService.ReportActorSnapshotState:output_type -> substrate.ateapi.v1.ReportActorSnapshotStateResponse + 134, // 294: substrate.ateapi.v1.WorkerService.ReportActorCrashed:output_type -> substrate.ateapi.v1.ReportActorCrashedResponse + 245, // [245:295] is the sub-list for method output_type + 195, // [195:245] is the sub-list for method input_type + 193, // [193:195] is the sub-list for extension type_name + 191, // [191:193] is the sub-list for extension extendee + 0, // [0:191] is the sub-list for field type_name } func init() { file_api_ateapi_v1_ateapi_proto_init() } @@ -11262,7 +11127,7 @@ func file_api_ateapi_v1_ateapi_proto_init() { GoPackagePath: reflect.TypeOf(x{}).PkgPath(), RawDescriptor: unsafe.Slice(unsafe.StringData(file_api_ateapi_v1_ateapi_proto_rawDesc), len(file_api_ateapi_v1_ateapi_proto_rawDesc)), NumEnums: 10, - NumMessages: 145, + NumMessages: 143, NumExtensions: 2, NumServices: 2, }, diff --git a/api/ateapi/v1/ateapi.proto b/api/ateapi/v1/ateapi.proto index 7a59f6a098..af06c0a70e 100644 --- a/api/ateapi/v1/ateapi.proto +++ b/api/ateapi/v1/ateapi.proto @@ -90,16 +90,6 @@ service Control { option (method).resource = "Actor"; } - // Create a Substrate-issued SPIFFE certificate asserting the actor identity. - // - // SPIFFE URI: spiffe://${trustdomain}/actor/${atespace}/${actor} - // - // * Called by the egress gateway when actor client certificate injection is - // configured for outbound requests. - rpc MintActorCertificate(MintActorCertificateRequest) returns (MintActorCertificateResponse) { - option (method).resource = "Actor"; - } - // Tag the external snapshot a suspended Actor holds. The tag gets its own // copy of that snapshot, so suspending or deleting the Actor afterwards // cannot collect it. When the Actor's latest snapshot is not uploaded yet, @@ -1279,7 +1269,10 @@ message SnapshotConfig { // storage_location is the base object-storage URI snapshots of actors on // this version are stored under. If unset, ate-api-server's // --snapshot-location is used. Requires the PerTemplateSnapshotLocation preview - // gate. + // gate. Each path segment may contain only ASCII letters, digits, '_' and + // '-'. Each owner's snapshots are stored under + // /atespaces/..., so no segment may be "atespaces" in any + // letter case. // // +ate:preview=PerTemplateSnapshotLocation // +k8s:ifDisabled(PerTemplateSnapshotLocation)=+k8s:forbidden @@ -2013,39 +2006,6 @@ message MintActorJwtResponse { google.protobuf.Timestamp expire_time = 2; } -// Request message for MintActorCertificate. -message MintActorCertificateRequest { - // The actor for which the certificate should be issued. - // - // Must be a valid actor that currently exists according to the actor store. - // - // +k8s:required - ObjectRef actor = 1; - - // The UID of the actor --- used to guard against deletion and recreation of - // an actor with the same name. - // - // +k8s:required - // +k8s:format=k8s-uuid - string actor_uid = 2; - - // Request contains DER encoded bytes of a x509 certificate signing request. - // The signer will ignore the contents of the CSR except to extract the - // subject public key. - // - // +k8s:required - // +k8s:maxBytes=16384 - bytes certificate_signing_request = 3; -} - -// Response message for MintActorCertificate. -message MintActorCertificateResponse { - // Response contains a list of DER encoded certificates. The first entry is the - // leaf certificate, and any remaining entries are intermediates in - // leaf-to-root order. - repeated bytes actor_certificates = 1; -} - message ListTagsRequest { // The atespace to list tags from. Empty lists across all atespaces. // diff --git a/api/ateapi/v1/ateapi_grpc.pb.go b/api/ateapi/v1/ateapi_grpc.pb.go index 4c2cadb5d9..50f21cc0c4 100644 --- a/api/ateapi/v1/ateapi_grpc.pb.go +++ b/api/ateapi/v1/ateapi_grpc.pb.go @@ -45,7 +45,6 @@ const ( Control_UpdateActorEgressPolicy_FullMethodName = "/substrate.ateapi.v1.Control/UpdateActorEgressPolicy" Control_DeleteActorEgressPolicy_FullMethodName = "/substrate.ateapi.v1.Control/DeleteActorEgressPolicy" Control_MintActorJwt_FullMethodName = "/substrate.ateapi.v1.Control/MintActorJwt" - Control_MintActorCertificate_FullMethodName = "/substrate.ateapi.v1.Control/MintActorCertificate" Control_CreateTag_FullMethodName = "/substrate.ateapi.v1.Control/CreateTag" Control_GetTag_FullMethodName = "/substrate.ateapi.v1.Control/GetTag" Control_ListTags_FullMethodName = "/substrate.ateapi.v1.Control/ListTags" @@ -115,13 +114,6 @@ type ControlClient interface { // // * Called by the egress gateway when actor JWT injection is configured for outbound requests. MintActorJwt(ctx context.Context, in *MintActorJwtRequest, opts ...grpc.CallOption) (*MintActorJwtResponse, error) - // Create a Substrate-issued SPIFFE certificate asserting the actor identity. - // - // SPIFFE URI: spiffe://${trustdomain}/actor/${atespace}/${actor} - // - // * Called by the egress gateway when actor client certificate injection is - // configured for outbound requests. - MintActorCertificate(ctx context.Context, in *MintActorCertificateRequest, opts ...grpc.CallOption) (*MintActorCertificateResponse, error) // Tag the external snapshot a suspended Actor holds. The tag gets its own // copy of that snapshot, so suspending or deleting the Actor afterwards // cannot collect it. When the Actor's latest snapshot is not uploaded yet, @@ -333,16 +325,6 @@ func (c *controlClient) MintActorJwt(ctx context.Context, in *MintActorJwtReques return out, nil } -func (c *controlClient) MintActorCertificate(ctx context.Context, in *MintActorCertificateRequest, opts ...grpc.CallOption) (*MintActorCertificateResponse, error) { - cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...) - out := new(MintActorCertificateResponse) - err := c.cc.Invoke(ctx, Control_MintActorCertificate_FullMethodName, in, out, cOpts...) - if err != nil { - return nil, err - } - return out, nil -} - func (c *controlClient) CreateTag(ctx context.Context, in *CreateTagRequest, opts ...grpc.CallOption) (*Tag, error) { cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...) out := new(Tag) @@ -680,13 +662,6 @@ type ControlServer interface { // // * Called by the egress gateway when actor JWT injection is configured for outbound requests. MintActorJwt(context.Context, *MintActorJwtRequest) (*MintActorJwtResponse, error) - // Create a Substrate-issued SPIFFE certificate asserting the actor identity. - // - // SPIFFE URI: spiffe://${trustdomain}/actor/${atespace}/${actor} - // - // * Called by the egress gateway when actor client certificate injection is - // configured for outbound requests. - MintActorCertificate(context.Context, *MintActorCertificateRequest) (*MintActorCertificateResponse, error) // Tag the external snapshot a suspended Actor holds. The tag gets its own // copy of that snapshot, so suspending or deleting the Actor afterwards // cannot collect it. When the Actor's latest snapshot is not uploaded yet, @@ -814,9 +789,6 @@ func (UnimplementedControlServer) DeleteActorEgressPolicy(context.Context, *Dele func (UnimplementedControlServer) MintActorJwt(context.Context, *MintActorJwtRequest) (*MintActorJwtResponse, error) { return nil, status.Error(codes.Unimplemented, "method MintActorJwt not implemented") } -func (UnimplementedControlServer) MintActorCertificate(context.Context, *MintActorCertificateRequest) (*MintActorCertificateResponse, error) { - return nil, status.Error(codes.Unimplemented, "method MintActorCertificate not implemented") -} func (UnimplementedControlServer) CreateTag(context.Context, *CreateTagRequest) (*Tag, error) { return nil, status.Error(codes.Unimplemented, "method CreateTag not implemented") } @@ -1144,24 +1116,6 @@ func _Control_MintActorJwt_Handler(srv interface{}, ctx context.Context, dec fun return interceptor(ctx, in, info, handler) } -func _Control_MintActorCertificate_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { - in := new(MintActorCertificateRequest) - if err := dec(in); err != nil { - return nil, err - } - if interceptor == nil { - return srv.(ControlServer).MintActorCertificate(ctx, in) - } - info := &grpc.UnaryServerInfo{ - Server: srv, - FullMethod: Control_MintActorCertificate_FullMethodName, - } - handler := func(ctx context.Context, req interface{}) (interface{}, error) { - return srv.(ControlServer).MintActorCertificate(ctx, req.(*MintActorCertificateRequest)) - } - return interceptor(ctx, in, info, handler) -} - func _Control_CreateTag_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { in := new(CreateTagRequest) if err := dec(in); err != nil { @@ -1757,10 +1711,6 @@ var Control_ServiceDesc = grpc.ServiceDesc{ MethodName: "MintActorJwt", Handler: _Control_MintActorJwt_Handler, }, - { - MethodName: "MintActorCertificate", - Handler: _Control_MintActorCertificate_Handler, - }, { MethodName: "CreateTag", Handler: _Control_CreateTag_Handler, diff --git a/benchmarking/locust/Dockerfile b/benchmarking/locust/Dockerfile index 15c704772d..724a7aed11 100644 --- a/benchmarking/locust/Dockerfile +++ b/benchmarking/locust/Dockerfile @@ -61,6 +61,7 @@ COPY benchmarking/locust/cluster_facts.py /app/cluster_facts.py COPY benchmarking/locust/server_telemetry.py /app/server_telemetry.py COPY benchmarking/locust/phase_breakdown.py /app/phase_breakdown.py COPY benchmarking/locust/ladder_steps.py /app/ladder_steps.py +COPY benchmarking/locust/turnover_window.py /app/turnover_window.py COPY benchmarking/analysis/phase_report.py /app/phase_report.py ENV PYTHONPATH=/app:/app/deps diff --git a/benchmarking/locust/common/agentsession_config.py b/benchmarking/locust/common/agentsession_config.py index 9e43dfcbdb..eb5c10fcf8 100644 --- a/benchmarking/locust/common/agentsession_config.py +++ b/benchmarking/locust/common/agentsession_config.py @@ -96,3 +96,14 @@ def add_agentsession_arguments(parser: LocustArgumentParser) -> None: ), include_in_web_ui=True, ) + parser.add_argument( + "--agentsession-awake", + type=int, + default=0, + env_var="LOCUST_AGENTSESSION_AWAKE", + help=( + "Turnover mode when > 0: keep this many sessions awake and swap " + "--target-rps sessions/s with parked ones (default: 0, off)" + ), + include_in_web_ui=True, + ) diff --git a/benchmarking/locust/common/boomer_config.py b/benchmarking/locust/common/boomer_config.py index 41151c5a6a..cf38419472 100644 --- a/benchmarking/locust/common/boomer_config.py +++ b/benchmarking/locust/common/boomer_config.py @@ -81,6 +81,7 @@ class that consumes it. "--agentsession-snapshot-steps": str, "--agentsession-phase-in": float, "--agentsession-resume-retry-budget": float, + "--agentsession-awake": int, "--total-actors": int, "--spawn-concurrency": int, "--actor-deadline": float, diff --git a/benchmarking/locust/common/rate_config.py b/benchmarking/locust/common/rate_config.py index 47e28984dd..ed69a43152 100644 --- a/benchmarking/locust/common/rate_config.py +++ b/benchmarking/locust/common/rate_config.py @@ -13,7 +13,7 @@ # limitations under the License. """Request-rate runtime flag, for boomer user classes that pace -(resumepaused).""" +(resumepaused, and agentsession in turnover mode).""" from locust import events from locust.argument_parser import LocustArgumentParser @@ -27,8 +27,8 @@ def add_rate_arguments(parser: LocustArgumentParser) -> None: default=0.0, env_var="LOCUST_TARGET_RPS", help=( - "Requests per second the whole boomer worker paces to, for user " - "classes that pace (resumepaused). 0 removes the cap. " + "Requests/s cap for resumepaused, or swaps/s for agentsession " + "turnover mode (--agentsession-awake). 0 disables both. " "A --ladder step with @target_rps changes it while the run " "continues" ), diff --git a/benchmarking/locust/runner.py b/benchmarking/locust/runner.py index 03bf485e89..1ac58fea90 100644 --- a/benchmarking/locust/runner.py +++ b/benchmarking/locust/runner.py @@ -55,6 +55,7 @@ from server_telemetry import extract_and_record_server_telemetry from phase_breakdown import append_phase_breakdown from ladder_steps import append_ladder_steps +from turnover_window import append_turnover_window, fill_done_ts # Path inside the locust image to the boomer-worker binary baked in by # benchmarking/locust/Dockerfile. @@ -638,6 +639,18 @@ def main() -> None: except Exception as e: tee(logs, f"Warning: Failed to record cluster facts: {e}") + # A turnover run is measured from the end of its fill, past the + # registration of its fleet: the server telemetry's steady window and + # the turnover_window row start there. Other runs have no such line. + turnover_start = None + try: + with open(logs_path, errors="replace") as f: + turnover_start = fill_done_ts(f) + if turnover_start is not None: + tee(logs, f"Turnover fill done at {turnover_start}; measuring the turnover from there") + except Exception as e: + tee(logs, f"Warning: Failed to read the turnover start: {e}") + # Server telemetry is a Prometheus time-window query, so it runs # either way. A run too loaded to write a CSV is the one its # bin-packing, PSI and snapshot numbers matter most for. @@ -655,6 +668,7 @@ def main() -> None: logs=logs, lag_s=args.atelet_lag_s, worker_pods=diff_worker_pods(pods_before, pods_after, evicted_uids), + steady_not_before=turnover_start, ) except Exception as e: tee(logs, f"Warning: Failed to harvest server telemetry: {e}") @@ -672,6 +686,16 @@ def main() -> None: except Exception as e: tee(logs, f"Warning: Failed to summarize ladder steps: {e}") + if turnover_start is not None: + try: + if append_turnover_window( + jsonl_path, turnover_start, stats_history_csv, data_ts, + args.tag, args.name, logs, + ): + tee(logs, f"Appended the turnover_window row to {jsonl_path}") + except Exception as e: + tee(logs, f"Warning: Failed to summarize the turnover window: {e}") + # The phase breakdown is additive too. It reads the node logs now, # while the pods still exist: the orchestrator deletes them as soon # as this process exits. The window reaches back to this run's start @@ -708,8 +732,6 @@ def main() -> None: (work_dir / f"{args.name}_failures.csv", "failures.csv"), (work_dir / f"{args.name}_stats_history.csv", "stats_history.csv"), (server_summary_json, "server_summary.json"), - # TODO: remove after data migration - (jsonl_path, f"{args.name}.jsonl"), ] for src, basename in files: if not src.exists(): diff --git a/benchmarking/locust/server_telemetry.py b/benchmarking/locust/server_telemetry.py index ba9d6a90ac..97a1c615ef 100644 --- a/benchmarking/locust/server_telemetry.py +++ b/benchmarking/locust/server_telemetry.py @@ -163,6 +163,27 @@ def get_steady_state_window( return start_ts, end_ts +def start_steady_window_at( + window: tuple[int, int], + not_before: int | None, + log: Callable[[str], None] | None = None, +) -> tuple[int, int]: + """The steady-state window, started at not_before when that falls inside + [start, end). A turnover run is measured from the end of its fill + (turnover_window.py), which comes after all its users have started.""" + start, end = window + if not_before is None: + return start, end + if start <= not_before < end: + return not_before, end + if log is not None: + log( + f"Turnover start {not_before} lies outside detected steady-state " + f"window [{start}, {end}]; keeping [{start}, {end}]" + ) + return start, end + + def _parse_instant_float(res: list[dict[str, Any]]) -> float | None: if res and "value" in res[0]: try: @@ -802,12 +823,14 @@ def extract_and_record_server_telemetry( logs: TextIO | None = None, lag_s: int = 70, worker_pods: dict[str, int | None] | None = None, + steady_not_before: int | None = None, ) -> None: """Entry point called by runner.py to query Prometheus and persist artifacts. Waits until `end_ts + lag_s` first, so the atelet's last export of the run has been scraped; lag_s must cover its export interval plus one scrape. `worker_pods` (WORKER_POD_FIELDS) joins `oom_events`; null when not given. + `steady_not_before`, when inside the steady-state window, starts it. """ def log(msg: str) -> None: if logs: @@ -820,8 +843,10 @@ def log(msg: str) -> None: time.sleep(wait_s) log(f"Harvesting Prometheus metrics from {prom_url} over [{start_ts}, {end_ts}]...") - steady_start, steady_end = get_steady_state_window( - stats_history_csv, start_ts, end_ts + steady_start, steady_end = start_steady_window_at( + get_steady_state_window(stats_history_csv, start_ts, end_ts), + steady_not_before, + log=log, ) log( f"Detected steady-state window: [{steady_start}, {steady_end}] " diff --git a/benchmarking/locust/turnover_window.py b/benchmarking/locust/turnover_window.py new file mode 100644 index 0000000000..b542180be6 --- /dev/null +++ b/benchmarking/locust/turnover_window.py @@ -0,0 +1,166 @@ +# 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. + +"""The turnover window of an agentsession run in turnover mode. + +In turnover mode (--agentsession-awake) a run first registers its whole +fleet: each session creates its actor, runs step 1 and parks. The conductor +then wakes the awake sessions, and only after that fill does it swap at +--target-rps. stats.jsonl's other rows cover the whole run, so their rates +divide by the time spent registering. This finds the end of the fill in the +line boomer logs then, and writes one `turnover_window` row with each +request's count, failures and rate from there to the end of the run, read +from stats_history.csv. runner.py also starts the server telemetry's +steady-state window there. + +Like the server telemetry, this is additive: runner.py logs a failure here +and keeps the locust measurements. +""" + +import csv +import json +import re +from datetime import datetime, timezone +from pathlib import Path +from typing import Any, Iterable, TextIO + +# What agentsession logs once its conductor has woken the awake sessions, +# right before it starts to swap. +FILL_DONE_MSG = "agentsession: turnover fill done" + +# The prefix runner.py puts on each line boomer writes to the run's log. +BOOMER_PREFIX = "[boomer] " + +# An RFC 3339 time as Go writes it. Its fraction can hold nanoseconds, which +# datetime does not parse, and only the seconds matter here. +_TIME_RE = re.compile(r"^(\d{4}-\d\d-\d\dT\d\d:\d\d:\d\d)(?:\.\d+)?(Z|[+-]\d\d:\d\d)$") + + +def _unix(ts: str) -> int: + m = _TIME_RE.match(ts) + if not m: + raise ValueError(f"not an RFC 3339 time: {ts!r}") + t = datetime.strptime(m[1], "%Y-%m-%dT%H:%M:%S").replace(tzinfo=timezone.utc) + offset = 0 + if m[2] != "Z": + sign = 1 if m[2][0] == "+" else -1 + offset = sign * (int(m[2][1:3]) * 3600 + int(m[2][4:6]) * 60) + return int(t.timestamp()) - offset + + +def fill_done_ts(lines: Iterable[str]) -> int | None: + """Unix seconds of the first fill-done line in the run's log, or None + when there is none: the run was not in turnover mode, or its fill never + finished.""" + for line in lines: + if not line.startswith(BOOMER_PREFIX) or FILL_DONE_MSG not in line: + continue + try: + entry = json.loads(line[len(BOOMER_PREFIX):]) + if entry.get("msg") == FILL_DONE_MSG: + return _unix(entry["time"]) + except (AttributeError, KeyError, TypeError, ValueError): + continue + return None + + +def window_counts( + stats_history_csv: Path, start: int +) -> tuple[int, dict[str, tuple[int, int]]] | None: + """The time of the last sample in stats_history.csv, and each request's + requests and failures from start to it, or None when no sample comes + after start. The counts come from the cumulative columns of the + per-request rows that --csv-full-history writes: the last sample's minus + the last one at or before start.""" + before: dict[str, tuple[int, int]] = {} + after: dict[str, tuple[int, int]] = {} + end = None + with open(stats_history_csv, encoding="utf-8") as f: + for row in csv.DictReader(f): + name = row.get("Name") or "" + if name in ("", "Aggregated", "Total"): + continue + try: + ts = int(row["Timestamp"]) + counts = (int(row["Total Request Count"]), int(row["Total Failure Count"])) + except (KeyError, TypeError, ValueError): + continue + if ts <= start: + before[name] = counts + else: + after[name] = counts + end = ts if end is None else max(end, ts) + if end is None: + return None + return end, { + name: (requests - before.get(name, (0, 0))[0], failures - before.get(name, (0, 0))[1]) + for name, (requests, failures) in after.items() + } + + +def window_record( + start: int, + end: int, + counts: dict[str, tuple[int, int]], + timestamp: str, + tag: str, + test_name: str, +) -> dict[str, Any]: + """The window as a stats.jsonl row: each request's count, failures and + rate. The rate counts every request, failed or not, like locust's + Requests/s.""" + secs = end - start + m: dict[str, Any] = {"window_start": start, "window_end": end, "window_s": secs} + for name, (requests, failures) in sorted(counts.items()): + m[f"requests_{name}"] = requests + m[f"failures_{name}"] = failures + m[f"rps_{name}"] = round(requests / secs, 4) if secs > 0 else None + return { + "timestamp": timestamp, + "tag": tag, + "test_name": test_name, + "metric": "turnover_window", + "measurements": m, + } + + +def append_turnover_window( + jsonl_path: Path, + start: int, + stats_history_csv: Path, + timestamp: str, + tag: str, + test_name: str, + logs: TextIO, +) -> bool: + """Append the turnover_window row of a run whose fill ended at start to + jsonl_path, and return whether there was one to append.""" + if not stats_history_csv.exists(): + _log(logs, f"No {stats_history_csv}; no turnover_window row") + return False + counts = window_counts(stats_history_csv, start) + if counts is None: + _log(logs, f"No stats_history sample after the turnover start {start}; no turnover_window row") + return False + end, per_request = counts + row = window_record(start, end, per_request, timestamp, tag, test_name) + with open(jsonl_path, "a", encoding="utf-8") as f: + f.write(json.dumps(row) + "\n") + return True + + +def _log(logs: TextIO, msg: str) -> None: + print(msg, flush=True) + logs.write(msg + "\n") + logs.flush() diff --git a/benchmarking/locust/unit_tests/test_boomer_config.py b/benchmarking/locust/unit_tests/test_boomer_config.py index 56a547c515..c6a8a4630d 100644 --- a/benchmarking/locust/unit_tests/test_boomer_config.py +++ b/benchmarking/locust/unit_tests/test_boomer_config.py @@ -60,6 +60,17 @@ def test_build_config_json_phase_in_and_retry_budget(self): self.assertEqual(parsed["agentsession_phase_in"], 300.0) self.assertEqual(parsed["agentsession_resume_retry_budget"], 0.0) + def test_build_config_json_turnover(self): + raw = boomer_config.build_config_json([ + "--agentsession-awake", + "650", + "--target-rps", + "2.5", + ]) + parsed = json.loads(raw) + self.assertEqual(parsed["agentsession_awake"], 650) + self.assertEqual(parsed["target_rps"], 2.5) + if __name__ == "__main__": unittest.main() diff --git a/benchmarking/locust/unit_tests/test_server_telemetry.py b/benchmarking/locust/unit_tests/test_server_telemetry.py index 9e4525f21c..24d80da26d 100644 --- a/benchmarking/locust/unit_tests/test_server_telemetry.py +++ b/benchmarking/locust/unit_tests/test_server_telemetry.py @@ -808,6 +808,35 @@ def ladder(jsonl_path, log_path, prom_url, *_): self.assertIn("Failed to summarize ladder steps: prometheus down", out.getvalue()) self.assertTrue(upload.called) + # The fill-done line of a turnover run starts the telemetry's steady + # window and the turnover_window row; a run without one gets neither. + @mock.patch.object(runner, "upload") + def test_turnover_start_reaches_telemetry_and_window(self, _up): + for line, start in ( + ('[boomer] {"time":"2026-10-30T00:04:10Z","msg":"agentsession: turnover fill done"}\n', + 1793318650), + ('[boomer] {"time":"2026-10-30T00:04:10Z","msg":"dynconfig applied"}\n', None), + ): + with self.subTest(start=start): + def run_test(_args, _csv_prefix, logs, _traces, line=line): + logs.write(line) + logs.flush() + return 1 + + telemetry, window = mock.Mock(), mock.Mock(return_value=True) + with mock.patch.object(runner, "run_test", run_test), \ + mock.patch.object(runner, "extract_and_record_server_telemetry", telemetry), \ + mock.patch.object(runner, "append_turnover_window", window), \ + mock.patch.object(sys, "argv", ARGV + ["--no-cluster-facts", + "--allow-empty-stats"]), \ + contextlib.redirect_stdout(io.StringIO()): + runner.main() + self.assertEqual(telemetry.call_args.kwargs["steady_not_before"], start) + if start is None: + self.assertFalse(window.called) + else: + self.assertEqual(window.call_args.args[1], start) + if __name__ == "__main__": unittest.main() diff --git a/benchmarking/locust/unit_tests/test_turnover_window.py b/benchmarking/locust/unit_tests/test_turnover_window.py new file mode 100644 index 0000000000..776ec5e679 --- /dev/null +++ b/benchmarking/locust/unit_tests/test_turnover_window.py @@ -0,0 +1,187 @@ +# 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. + +"""Unit tests for turnover_window.py. + +Run via: python3 benchmarking/locust/unit_tests/test_turnover_window.py +""" + +import io +import json +import sys +import tempfile +import unittest +from pathlib import Path + +sys.path.insert(0, str(Path(__file__).resolve().parent.parent)) + +import server_telemetry +import turnover_window + +T0 = 1793318400 # 2026-10-30 00:00:00 UTC + +LOG = """\ +[locust] [2026-10-30 00:00:00,000] host/INFO/locust.main: Starting Locust 2.32.7 +[locust] [2026-10-30 00:00:01,000] host/INFO/x: agentsession: turnover fill done +[boomer] {"time":"2026-10-30T00:01:05.123456789Z","level":"INFO","msg":"agentsession: loaded script"} +[boomer] {"time":"2026-10-30T00:04:10.987654321Z","level":"INFO","msg":"agentsession: turnover fill done","awake":350,"parked":850} +[boomer] {"time":"2026-10-30T00:09:00Z","level":"INFO","msg":"agentsession: turnover fill done","awake":350,"parked":850} +""" + +HEADER = "Timestamp,User Count,Type,Name,Requests/s,Failures/s,Total Request Count,Total Failure Count\n" + + +def history(rows): + """A stats_history.csv body from (offset from T0, name, requests, failures).""" + return HEADER + "".join( + f"{T0 + t},1200,{'' if name == 'Aggregated' else 'grpc'},{name},0,0,{n},{f}\n" + for t, name, n, f in rows + ) + + +HISTORY = history([ + (240, "SuspendActorAsync_Register", 1200, 3), + (240, "ResumeActor", 350, 0), + (240, "Aggregated", 1550, 3), + (250, "SuspendActorAsync_Register", 1200, 3), + (250, "ResumeActor", 350, 0), + (250, "Aggregated", 1550, 3), + (260, "SuspendActorAsync_Register", 1200, 3), + (260, "ResumeActor", 400, 1), + (260, "SuspendActorAsync", 50, 0), + (260, "Aggregated", 1650, 4), + (350, "SuspendActorAsync_Register", 1200, 3), + (350, "ResumeActor", 850, 2), + (350, "SuspendActorAsync", 500, 1), + (350, "Aggregated", 2550, 6), +]) + + +class FillDoneTest(unittest.TestCase): + # Only boomer's own line counts, and only its first. + def test_finds_the_first_boomer_fill_done_line(self): + self.assertEqual(turnover_window.fill_done_ts(LOG.splitlines()), T0 + 250) + + def test_a_run_without_one_has_none(self): + self.assertIsNone(turnover_window.fill_done_ts(LOG.splitlines()[:3])) + + def test_reads_an_offset_time(self): + line = '[boomer] {"time":"2026-10-30T02:04:10+02:00","msg":"agentsession: turnover fill done"}' + self.assertEqual(turnover_window.fill_done_ts([line]), T0 + 250) + + def test_skips_a_line_it_cannot_read(self): + lines = [ + '[boomer] {"msg":"agentsession: turnover fill done"', + '[boomer] {"time":"yesterday","msg":"agentsession: turnover fill done"}', + '[boomer] {"time":"2026-10-30T00:09:00Z","msg":"agentsession: turnover fill done"}', + ] + self.assertEqual(turnover_window.fill_done_ts(lines), T0 + 540) + + +class WindowCountsTest(unittest.TestCase): + def counts(self, body, start): + with tempfile.TemporaryDirectory() as td: + path = Path(td) / "stats_history.csv" + path.write_text(body) + return turnover_window.window_counts(path, start) + + # Counts from the last sample at or before the start; a request first + # seen after it starts from zero. + def test_counts_from_the_start_to_the_last_sample(self): + self.assertEqual(self.counts(HISTORY, T0 + 250), (T0 + 350, { + "ResumeActor": (500, 2), + "SuspendActorAsync": (500, 1), + "SuspendActorAsync_Register": (0, 0), + })) + + def test_no_sample_after_the_start_gives_none(self): + self.assertIsNone(self.counts(HISTORY, T0 + 350)) + + +class AppendTest(unittest.TestCase): + def append(self, body, start): + with tempfile.TemporaryDirectory() as td: + d = Path(td) + if body is not None: + (d / "stats_history.csv").write_text(body) + jsonl = d / "stats.jsonl" + jsonl.write_text('{"metric": "locust"}\n') + ok = turnover_window.append_turnover_window( + jsonl, start, d / "stats_history.csv", "ts", "tag", "test", io.StringIO()) + return ok, [json.loads(line) for line in jsonl.read_text().splitlines()] + + def test_appends_the_window_row(self): + ok, rows = self.append(HISTORY, T0 + 250) + self.assertTrue(ok) + self.assertEqual(rows[1], { + "timestamp": "ts", + "tag": "tag", + "test_name": "test", + "metric": "turnover_window", + "measurements": { + "window_start": T0 + 250, + "window_end": T0 + 350, + "window_s": 100, + "requests_ResumeActor": 500, + "failures_ResumeActor": 2, + "rps_ResumeActor": 5.0, + "requests_SuspendActorAsync": 500, + "failures_SuspendActorAsync": 1, + "rps_SuspendActorAsync": 5.0, + "requests_SuspendActorAsync_Register": 0, + "failures_SuspendActorAsync_Register": 0, + "rps_SuspendActorAsync_Register": 0.0, + }, + }) + + def test_appends_nothing_without_a_history(self): + self.assertEqual(self.append(None, T0 + 250), (False, [{"metric": "locust"}])) + + def test_appends_nothing_without_a_sample_after_the_start(self): + self.assertEqual(self.append(HISTORY, T0 + 350), (False, [{"metric": "locust"}])) + + +class SteadyWindowTest(unittest.TestCase): + def test_a_start_inside_the_window_starts_it(self): + logged: list[str] = [] + self.assertEqual( + server_telemetry.start_steady_window_at((100, 500), 250, log=logged.append), + (250, 500), + ) + self.assertEqual( + server_telemetry.start_steady_window_at((100, 500), 100, log=logged.append), + (100, 500), + ) + self.assertEqual( + server_telemetry.start_steady_window_at((100, 500), None, log=logged.append), + (100, 500), + ) + self.assertEqual(logged, []) + + def test_a_start_outside_the_window_logs_and_leaves_it(self): + for not_before in (50, 500, 600): + with self.subTest(not_before=not_before): + logged: list[str] = [] + self.assertEqual( + server_telemetry.start_steady_window_at( + (100, 500), not_before, log=logged.append + ), + (100, 500), + ) + self.assertEqual(len(logged), 1) + self.assertIn(str(not_before), logged[0]) + + +if __name__ == "__main__": + unittest.main() diff --git a/charts/substrate/README.md b/charts/substrate/README.md index dfc2c73898..f80beb3809 100644 --- a/charts/substrate/README.md +++ b/charts/substrate/README.md @@ -148,6 +148,11 @@ This replaces the previous `v5` config. Existing `v5` memory snapshots need a compatible runtime; falling back to a durable filesystem snapshot does not restore the actor's memory. +The [image cache](../../internal/imagecache/README.md) now uses layout version 3 +to preserve image ownership and permissions. Atelet refuses version 2 caches. +Stop the actors on each node before deleting its old image cache +(`/var/lib/ate/image-cache` by default). Atelet recreates it on startup. + When upgrading an installation with RustFS enabled, delete its completed bucket initializer Job before running `helm upgrade`. The default Job name is `rustfs-bucket-init` in the release namespace. This update changes the AWS CLI diff --git a/charts/substrate/templates/atelet.yaml b/charts/substrate/templates/atelet.yaml index d5a846760d..5abad9d1c5 100644 --- a/charts/substrate/templates/atelet.yaml +++ b/charts/substrate/templates/atelet.yaml @@ -215,7 +215,10 @@ spec: drop: - ALL add: + - CHOWN - DAC_OVERRIDE + - FOWNER + - FSETID resources: {{ toYaml .Values.atelet.resources | indent 10 }} env: diff --git a/charts/substrate/tests/control_plane_test.yaml b/charts/substrate/tests/control_plane_test.yaml index 2f9955400f..de9b50b2fe 100644 --- a/charts/substrate/tests/control_plane_test.yaml +++ b/charts/substrate/tests/control_plane_test.yaml @@ -114,6 +114,21 @@ tests: key: cloud.google.com/compute-class operator: Exists effect: NoSchedule +- it: lets atelet preserve image ownership and permissions + template: atelet.yaml + documentSelector: + path: kind + value: DaemonSet + asserts: + - equal: + path: spec.template.spec.containers[0].securityContext + value: + allowPrivilegeEscalation: false + runAsUser: 0 + runAsGroup: 0 + capabilities: + drop: [ALL] + add: [CHOWN, DAC_OVERRIDE, FOWNER, FSETID] - it: probes the credential provider on its health listener template: k8s-credential-provider.yaml documentSelector: diff --git a/cmd/ate-setup/internal/ko/ko.go b/cmd/ate-setup/internal/ko/ko.go index 139fa6bd63..c2b719c5c3 100644 --- a/cmd/ate-setup/internal/ko/ko.go +++ b/cmd/ate-setup/internal/ko/ko.go @@ -33,6 +33,9 @@ import ( "strings" "time" + "k8s.io/apimachinery/pkg/util/wait" + "k8s.io/client-go/util/retry" + "go.ate.dev/substrate/cmd/ate-setup/internal/log" ) @@ -162,6 +165,37 @@ func (r *Runner) BuildTagged(ctx context.Context, tag string, labels []string, i return refs, nil } +// buildBackoff paces the retries of an image that fails to build or push: 3 +// attempts, 10s then 20s apart. ghcr.io can briefly deny pushes to a package it +// has just created. +var buildBackoff = wait.Backoff{Steps: 3, Duration: 10 * time.Second, Factor: 2} + +// BuildEach runs BuildTagged once per import path, retrying each failure per +// buildBackoff. It returns the refs in importPaths order. +func (r *Runner) BuildEach(ctx context.Context, tag string, labels []string, importPaths ...string) ([]string, error) { + refs := make([]string, 0, len(importPaths)) + for _, path := range importPaths { + var ref string + logAndRetry := func(err error) bool { + log.Warnf("%s: %v", path, err) + return true + } + err := retry.OnError(buildBackoff, logAndRetry, func() error { + built, err := r.BuildTagged(ctx, tag, labels, path) + if err != nil { + return err + } + ref = built[0] + return nil + }) + if err != nil { + return nil, err + } + refs = append(refs, ref) + } + return refs, nil +} + // ResolveBytes resolves an in-memory manifest, such as kustomize output. func (r *Runner) ResolveBytes(ctx context.Context, manifest []byte) ([]byte, error) { return r.Resolve(ctx, "-", manifest) diff --git a/cmd/ate-setup/internal/ko/ko_test.go b/cmd/ate-setup/internal/ko/ko_test.go index 8c5377a60b..415676b4be 100644 --- a/cmd/ate-setup/internal/ko/ko_test.go +++ b/cmd/ate-setup/internal/ko/ko_test.go @@ -15,12 +15,17 @@ package ko import ( + "errors" "os" "os/exec" "path/filepath" "slices" + "strconv" "strings" "testing" + "time" + + "k8s.io/apimachinery/pkg/util/wait" ) // Without --base-import-paths ko publishes cmd/atelet as "atelet-", and @@ -174,3 +179,80 @@ func TestBuildTaggedRejectsMissingRefs(t *testing.T) { t.Error("BuildTagged() error = nil, want one for 1 ref from 2 packages") } } + +// flakyKo writes a ko stand-in that fails the first failures builds of each +// package, printing stderr, then prints a ref for it. It returns the +// directory where it counts builds per package. +func flakyKo(t *testing.T, failures int, stderr string) (binary, dir string) { + t.Helper() + dir = t.TempDir() + binary = filepath.Join(dir, "ko") + script := "#!/bin/sh\n" + + "pkg=$(basename \"$2\")\n" + + "echo run >> " + dir + "/runs-$pkg\n" + + "if [ $(wc -l < " + dir + "/runs-$pkg) -le " + strconv.Itoa(failures) + " ]; then echo '" + stderr + "' >&2; exit 1; fi\n" + + "echo repo/$pkg:v1@sha256:a\n" + if err := os.WriteFile(binary, []byte(script), 0o755); err != nil { + t.Fatal(err) + } + return binary, dir +} + +// koRuns returns how many times flakyKo built pkg. +func koRuns(t *testing.T, dir, pkg string) int { + t.Helper() + raw, err := os.ReadFile(filepath.Join(dir, "runs-"+pkg)) + if errors.Is(err, os.ErrNotExist) { + return 0 + } + if err != nil { + t.Fatal(err) + } + return strings.Count(string(raw), "run") +} + +// fastBackoff shortens buildBackoff for the test. +func fastBackoff(t *testing.T) { + t.Helper() + original := buildBackoff + buildBackoff = wait.Backoff{Steps: original.Steps, Duration: time.Millisecond, Factor: 1} + t.Cleanup(func() { buildBackoff = original }) +} + +const deniedStderr = "PATCH https://ghcr.io/v2/agent-substrate/substrate/ateapi/blobs/upload/1: DENIED: permission_denied: write_package" + +func TestBuildEachRetriesEachImage(t *testing.T) { + fastBackoff(t) + binary, dir := flakyKo(t, 2, deniedStderr) + r := &Runner{Root: t.TempDir(), binary: binary} + + refs, err := r.BuildEach(t.Context(), "v1", nil, "./cmd/ateapi", "./cmd/atelet") + if err != nil { + t.Fatalf("BuildEach() error = %v", err) + } + if want := []string{"repo/ateapi:v1@sha256:a", "repo/atelet:v1@sha256:a"}; !slices.Equal(refs, want) { + t.Errorf("BuildEach() = %q, want %q", refs, want) + } + for _, pkg := range []string{"ateapi", "atelet"} { + if got := koRuns(t, dir, pkg); got != 3 { + t.Errorf("ko built %s %d times, want 3", pkg, got) + } + } +} + +func TestBuildEachStopsAfterAttempts(t *testing.T) { + fastBackoff(t) + binary, dir := flakyKo(t, 100, deniedStderr) + r := &Runner{Root: t.TempDir(), binary: binary} + + _, err := r.BuildEach(t.Context(), "v1", nil, "./cmd/ateapi", "./cmd/atelet") + if err == nil { + t.Error("BuildEach() error = nil, want one after 3 failed attempts") + } + if got := koRuns(t, dir, "ateapi"); got != 3 { + t.Errorf("ko built ateapi %d times, want 3", got) + } + if got := koRuns(t, dir, "atelet"); got != 0 { + t.Errorf("ko built atelet %d times, want 0", got) + } +} diff --git a/cmd/ate-setup/internal/steps/overlay_test.go b/cmd/ate-setup/internal/steps/overlay_test.go index 78766c35b6..78ef677a99 100644 --- a/cmd/ate-setup/internal/steps/overlay_test.go +++ b/cmd/ate-setup/internal/steps/overlay_test.go @@ -789,7 +789,7 @@ func TestRenderFakeDataPlane(t *testing.T) { // Under --e2e-tuning every path that applies atelet appends the e2e tuning // flags without dropping the overlay's own args, and a non-e2e kind render -// keeps atelet on its default snapshot upload delay. +// keeps atelet on its default snapshot delays. func TestRenderE2ETuning(t *testing.T) { root := repoRoot(t) const ateletDS = "atelet" @@ -805,41 +805,41 @@ func TestRenderE2ETuning(t *testing.T) { cfg: config.Config{Router: config.RouterEnvoy}, path: func(e *Env) string { return e.Cfg.Path(SystemOverlay(e.Cfg)) }, wantAtelet: true, - wantArgs: []string{"--grpc-server-cred-bundle=/run/podidentity.podcert.ate.dev/credential-bundle.pem", "--snapshot-upload-delay=2s"}, + wantArgs: []string{"--grpc-server-cred-bundle=/run/podidentity.podcert.ate.dev/credential-bundle.pem", "--snapshot-upload-delay=2s", "--snapshot-detach-delay=30s"}, }, { name: "kind bundle", cfg: config.Config{Router: config.RouterEnvoy, Kind: true}, path: func(e *Env) string { return e.Cfg.Path(SystemOverlay(e.Cfg)) }, wantAtelet: true, - wantArgs: []string{"--localhost-registry-replacement=kind-registry:5000", "--log-level=debug", "--snapshot-upload-delay=2s"}, + wantArgs: []string{"--localhost-registry-replacement=kind-registry:5000", "--log-level=debug", "--snapshot-upload-delay=2s", "--snapshot-detach-delay=30s"}, }, { name: "agentgateway bundle", cfg: config.Config{Router: config.RouterAgentgateway}, path: func(e *Env) string { return e.Cfg.Path(SystemOverlay(e.Cfg)) }, wantAtelet: true, - wantArgs: []string{"--grpc-server-cred-bundle=/run/podidentity.podcert.ate.dev/credential-bundle.pem", "--snapshot-upload-delay=2s"}, + wantArgs: []string{"--grpc-server-cred-bundle=/run/podidentity.podcert.ate.dev/credential-bundle.pem", "--snapshot-upload-delay=2s", "--snapshot-detach-delay=30s"}, }, { name: "kind agentgateway bundle", cfg: config.Config{Router: config.RouterAgentgateway, Kind: true}, path: func(e *Env) string { return e.Cfg.Path(SystemOverlay(e.Cfg)) }, wantAtelet: true, - wantArgs: []string{"--localhost-registry-replacement=kind-registry:5000", "--log-level=debug", "--snapshot-upload-delay=2s"}, + wantArgs: []string{"--localhost-registry-replacement=kind-registry:5000", "--log-level=debug", "--snapshot-upload-delay=2s", "--snapshot-detach-delay=30s"}, }, { name: "atelet file", path: func(e *Env) string { return e.Cfg.Manifest("atelet.yaml") }, wantAtelet: true, - wantArgs: []string{"--grpc-server-cred-bundle=/run/podidentity.podcert.ate.dev/credential-bundle.pem", "--snapshot-upload-delay=2s"}, + wantArgs: []string{"--grpc-server-cred-bundle=/run/podidentity.podcert.ate.dev/credential-bundle.pem", "--snapshot-upload-delay=2s", "--snapshot-detach-delay=30s"}, }, { name: "kind atelet overlay", cfg: config.Config{Kind: true}, path: func(e *Env) string { return e.ateletManifestPath() }, wantAtelet: true, - wantArgs: []string{"--localhost-registry-replacement=kind-registry:5000", "--log-level=debug", "--snapshot-upload-delay=2s"}, + wantArgs: []string{"--localhost-registry-replacement=kind-registry:5000", "--log-level=debug", "--snapshot-upload-delay=2s", "--snapshot-detach-delay=30s"}, }, { name: "api server file without atelet", @@ -873,7 +873,7 @@ func TestRenderE2ETuning(t *testing.T) { }) } - t.Run("kind without e2e-tuning omits snapshot-upload-delay", func(t *testing.T) { + t.Run("kind without e2e-tuning omits snapshot delays", func(t *testing.T) { e := &Env{Cfg: &config.Config{Root: root, Kind: true}} rendered, err := e.render(e.ateletManifestPath()) if err != nil { @@ -885,7 +885,7 @@ func TestRenderE2ETuning(t *testing.T) { t.Errorf("kind atelet args = %v, missing --log-level=debug", args) } for _, arg := range args { - if strings.HasPrefix(arg, "--snapshot-upload-delay") { + if strings.HasPrefix(arg, "--snapshot-upload-delay") || strings.HasPrefix(arg, "--snapshot-detach-delay") { t.Errorf("kind atelet without --e2e-tuning carries %q in args %v", arg, args) } } diff --git a/cmd/ate-setup/internal/steps/publish.go b/cmd/ate-setup/internal/steps/publish.go index f4cd66da22..0933c84a09 100644 --- a/cmd/ate-setup/internal/steps/publish.go +++ b/cmd/ate-setup/internal/steps/publish.go @@ -259,7 +259,7 @@ func (e *Env) PublishReleaseImages(ctx context.Context, w io.Writer, opts Publis } refs = append(refs, ref) } - koRefs, err := runner.BuildTagged(ctx, tag, labels, pkgs...) + koRefs, err := runner.BuildEach(ctx, tag, labels, pkgs...) if err != nil { return err } diff --git a/cmd/ateapi/internal/apivalidation/actor.go b/cmd/ateapi/internal/apivalidation/actor.go index 138e4f5e4a..81cb30fc7f 100644 --- a/cmd/ateapi/internal/apivalidation/actor.go +++ b/cmd/ateapi/internal/apivalidation/actor.go @@ -82,12 +82,6 @@ func ValidateMintActorJWTRequest(ctx context.Context, req *ateapi.MintActorJwtRe return Validate_MintActorJwtRequest(ctx, op, nil, req, nil) } -func ValidateMintActorCertificateRequest(ctx context.Context, req *ateapi.MintActorCertificateRequest) field.ErrorList { - // Call the generated validation. - op := MakeCreateOp(ctx) - return Validate_MintActorCertificateRequest(ctx, op, nil, req, nil) -} - func ValidateMintWorkerActorCertificateRequest(ctx context.Context, req *ateapi.MintWorkerActorCertificateRequest) field.ErrorList { op := MakeCreateOp(ctx) return Validate_MintWorkerActorCertificateRequest(ctx, op, nil, req, nil) diff --git a/cmd/ateapi/internal/apivalidation/actor_template.go b/cmd/ateapi/internal/apivalidation/actor_template.go index 492af02aae..cfcde252a3 100644 --- a/cmd/ateapi/internal/apivalidation/actor_template.go +++ b/cmd/ateapi/internal/apivalidation/actor_template.go @@ -163,9 +163,9 @@ func ValidateCustom_Container_Image(_ context.Context, _ operation.Operation, fl return resources.ValidatePinnedImage(fldPath, *value) } -// ValidateCustom_SnapshotConfig_StorageLocation ensures an -// ActorTemplate's snapshotConfig.storageLocation is a well-formed -// URI with a bucket, so a bad location fails fast. +// ValidateCustom_SnapshotConfig_StorageLocation checks an ActorTemplate's +// snapshotConfig.storageLocation with resources.ValidateSnapshotLocation, so a +// bad location fails fast. func ValidateCustom_SnapshotConfig_StorageLocation(_ context.Context, _ operation.Operation, fldPath *field.Path, value, _ *string) field.ErrorList { if err := resources.ValidateSnapshotLocation(*value); err != nil { return field.ErrorList{field.Invalid(fldPath, *value, err.Error())} diff --git a/cmd/ateapi/internal/apivalidation/actor_template_test.go b/cmd/ateapi/internal/apivalidation/actor_template_test.go index 4c111dc51b..e4db259726 100644 --- a/cmd/ateapi/internal/apivalidation/actor_template_test.go +++ b/cmd/ateapi/internal/apivalidation/actor_template_test.go @@ -184,6 +184,18 @@ func TestValidateCreateActorTemplateRequest(t *testing.T) { tmpl.SnapshotConfig.StorageLocation = "gs://my-bucket/snapshots?versions=true" })}, field.ErrorList{field.Invalid(field.NewPath("actor_template", "snapshot_config", "storage_location"), "gs://my-bucket/snapshots?versions=true", "")}, + }, { + "storage_location under another actor's prefix", + &ateapi.CreateActorTemplateRequest{ActorTemplate: validActorTemplate(func(tmpl *ateapi.ActorTemplate) { + tmpl.SnapshotConfig.StorageLocation = "gs://my-bucket/snapshots/atespaces/team-a/actors/0b4e7c4e-2f3a-4d43-9a5e-0f6d1c2b3a4d" + })}, + field.ErrorList{field.Invalid(field.NewPath("actor_template", "snapshot_config", "storage_location"), "gs://my-bucket/snapshots/atespaces/team-a/actors/0b4e7c4e-2f3a-4d43-9a5e-0f6d1c2b3a4d", "")}, + }, { + "storage_location with a dot in a path segment", + &ateapi.CreateActorTemplateRequest{ActorTemplate: validActorTemplate(func(tmpl *ateapi.ActorTemplate) { + tmpl.SnapshotConfig.StorageLocation = "gs://my-bucket/snapshots.v1" + })}, + field.ErrorList{field.Invalid(field.NewPath("actor_template", "snapshot_config", "storage_location"), "gs://my-bucket/snapshots.v1", "")}, }, { // preferred_fidelity has no default of its own at this layer, so leaving it // unset is a required violation. diff --git a/cmd/ateapi/internal/apivalidation/actor_test.go b/cmd/ateapi/internal/apivalidation/actor_test.go index 272ba567cc..750439ec1e 100644 --- a/cmd/ateapi/internal/apivalidation/actor_test.go +++ b/cmd/ateapi/internal/apivalidation/actor_test.go @@ -1032,49 +1032,6 @@ func TestValidateSuspendActorRequest(t *testing.T) { } } -func TestValidateMintActorCertificateRequest(t *testing.T) { - validUID := "3b9f1e77-2c4d-4a80-91be-6d5c8f0a7e21" - tests := []struct { - name string - req *ateapi.MintActorCertificateRequest - want field.ErrorList - }{{ - "valid", - &ateapi.MintActorCertificateRequest{ - Actor: &ateapi.ObjectRef{Atespace: "ns1", Name: "id1"}, - ActorUid: validUID, - CertificateSigningRequest: []byte("csr"), - }, - nil, - }, { - "missing actor", - &ateapi.MintActorCertificateRequest{ - ActorUid: validUID, - CertificateSigningRequest: []byte("csr"), - }, - field.ErrorList{field.Required(field.NewPath("actor"), "")}, - }, { - "missing actor_uid", - &ateapi.MintActorCertificateRequest{ - Actor: &ateapi.ObjectRef{Atespace: "ns1", Name: "id1"}, - CertificateSigningRequest: []byte("csr"), - }, - field.ErrorList{field.Required(field.NewPath("actor_uid"), "")}, - }, { - "missing csr", - &ateapi.MintActorCertificateRequest{ - Actor: &ateapi.ObjectRef{Atespace: "ns1", Name: "id1"}, - ActorUid: validUID, - }, - field.ErrorList{field.Required(field.NewPath("certificate_signing_request"), "")}, - }} - for _, tt := range tests { - t.Run(tt.name, func(t *testing.T) { - assertValidateErr(t, ValidateMintActorCertificateRequest(context.Background(), tt.req), tt.want) - }) - } -} - // validActor returns a minimal Actor which should pass input validation. func validActor(mods ...func(*ateapi.Actor)) *ateapi.Actor { a := &ateapi.Actor{ diff --git a/cmd/ateapi/internal/apivalidation/zz_generated.validation.go b/cmd/ateapi/internal/apivalidation/zz_generated.validation.go index bfe1672f01..9d177ca23f 100644 --- a/cmd/ateapi/internal/apivalidation/zz_generated.validation.go +++ b/cmd/ateapi/internal/apivalidation/zz_generated.validation.go @@ -5878,110 +5878,6 @@ func Validate_MarkWorkerUnavailableRequest( return errs } -// Validate_MintActorCertificateRequest validates an instance of MintActorCertificateRequest according -// to declarative validation rules in the API schema. -func Validate_MintActorCertificateRequest( - ctx context.Context, op operation.Operation, fldPath *field.Path, - obj, oldObj *v1.MintActorCertificateRequest) (errs field.ErrorList) { - - { // field v1.MintActorCertificateRequest.Actor - fn := func( - fldPath *field.Path, - obj, oldObj *v1.ObjectRef, - oldValueCorrelated bool) (errs field.ErrorList) { - // don't revalidate unchanged data - if oldValueCorrelated && op.Type == operation.Update { - if ateDeepEqual(obj, oldObj) { - return nil - } - } - // call field-attached validations - earlyReturn := false - if e := validate.RequiredPointer(ctx, op, fldPath, obj, oldObj).MarkShortCircuit(); len(e) != 0 { - errs = append(errs, e...) - earlyReturn = true - } - if earlyReturn { - return // do not proceed - } - // call the type's validation function - errs = append(errs, Validate_ObjectRef(ctx, op, fldPath, obj, oldObj)...) - return - } - oldVal := safe.Field(oldObj, - func(oldObj *v1.MintActorCertificateRequest) *v1.ObjectRef { - return oldObj.Actor - }) - errs = append(errs, fn(fldPath.Child("actor"), obj.Actor, oldVal, oldObj != nil)...) - } - - { // field v1.MintActorCertificateRequest.ActorUid - fn := func( - fldPath *field.Path, - obj, oldObj *string, - oldValueCorrelated bool) (errs field.ErrorList) { - // don't revalidate unchanged data - if oldValueCorrelated && op.Type == operation.Update { - if obj == oldObj || (obj != nil && oldObj != nil && *obj == *oldObj) { - return nil - } - } - // call field-attached validations - earlyReturn := false - if e := validate.RequiredValue(ctx, op, fldPath, obj, oldObj).MarkShortCircuit(); len(e) != 0 { - errs = append(errs, e...) - earlyReturn = true - } - if earlyReturn { - return // do not proceed - } - if e := validate.UUID(ctx, op, fldPath, obj, oldObj); len(e) != 0 { - errs = append(errs, e...) - } - return - } - oldVal := safe.Field(oldObj, - func(oldObj *v1.MintActorCertificateRequest) *string { - return &oldObj.ActorUid - }) - errs = append(errs, fn(fldPath.Child("actor_uid"), &obj.ActorUid, oldVal, oldObj != nil)...) - } - - { // field v1.MintActorCertificateRequest.CertificateSigningRequest - fn := func( - fldPath *field.Path, - obj, oldObj []byte, - oldValueCorrelated bool) (errs field.ErrorList) { - // don't revalidate unchanged data - if oldValueCorrelated && op.Type == operation.Update { - if ateDeepEqual(obj, oldObj) { - return nil - } - } - // call field-attached validations - earlyReturn := false - if e := validate.RequiredSlice(ctx, op, fldPath, obj, oldObj).MarkShortCircuit(); len(e) != 0 { - errs = append(errs, e...) - earlyReturn = true - } - if earlyReturn { - return // do not proceed - } - if e := validate.MaxBytesSlice(ctx, op, fldPath, obj, oldObj, 16384); len(e) != 0 { - errs = append(errs, e...) - } - return - } - oldVal := safe.Field(oldObj, - func(oldObj *v1.MintActorCertificateRequest) []byte { - return oldObj.CertificateSigningRequest - }) - errs = append(errs, fn(fldPath.Child("certificate_signing_request"), obj.CertificateSigningRequest, oldVal, oldObj != nil)...) - } - - return errs -} - // Validate_MintActorJwtRequest validates an instance of MintActorJwtRequest according // to declarative validation rules in the API schema. func Validate_MintActorJwtRequest( diff --git a/cmd/ateapi/internal/authz/authz_util.go b/cmd/ateapi/internal/authz/authz_util.go index 1e1734d6cd..45b52375c5 100644 --- a/cmd/ateapi/internal/authz/authz_util.go +++ b/cmd/ateapi/internal/authz/authz_util.go @@ -79,7 +79,6 @@ const ( RelationCanListTags = "can_list_tags" RelationCanUseTag = "can_use_tag" RelationCanMintActorJWT = "can_mint_actor_jwt" - RelationCanMintActorCertificate = "can_mint_actor_certificate" // maxTuplesPerWrite is OpenFGA's default maximum number of tuples allowed in a single Write request. maxTuplesPerWrite = 100 diff --git a/cmd/ateapi/internal/authz/interceptor_test.go b/cmd/ateapi/internal/authz/interceptor_test.go index b20bd6a7a6..ea6d6dece8 100644 --- a/cmd/ateapi/internal/authz/interceptor_test.go +++ b/cmd/ateapi/internal/authz/interceptor_test.go @@ -484,7 +484,6 @@ func TestUnaryServerInterceptor_LifecycleEgressWorkerAndTagChecks(t *testing.T) }}} deleteTag := rpcCall{ateapi.Control_DeleteTag_FullMethodName, &ateapi.DeleteTagRequest{Tag: tag}} mintJWT := rpcCall{ateapi.Control_MintActorJwt_FullMethodName, &ateapi.MintActorJwtRequest{Actor: actor}} - mintCert := rpcCall{ateapi.Control_MintActorCertificate_FullMethodName, &ateapi.MintActorCertificateRequest{Actor: actor}} // cloneTag creates an actor in atespace from team-a's tag v1, with a // template in that same atespace so only the tag check can differ. cloneTag := func(atespace string, sourceTag *ateapi.ObjectRef) rpcCall { @@ -586,7 +585,7 @@ func TestUnaryServerInterceptor_LifecycleEgressWorkerAndTagChecks(t *testing.T) // Only the egress gateway mints actor credentials; not even owners. {"editor cannot mint actor JWT", "editor", mintJWT, codes.PermissionDenied}, {"global owner cannot mint actor JWT", "global-owner", mintJWT, codes.PermissionDenied}, - {"bootstrap owner cannot mint actor certificate", "bootstrap-owner", mintCert, codes.PermissionDenied}, + {"bootstrap owner cannot mint actor JWT", "bootstrap-owner", mintJWT, codes.PermissionDenied}, } interceptor := UnaryServerInterceptor(authorizer) @@ -661,7 +660,6 @@ func TestUnaryServerInterceptor_SystemPrincipals(t *testing.T) { markUnavailable := rpcCall{ateapi.Control_MarkWorkerUnavailable_FullMethodName, &ateapi.MarkWorkerUnavailableRequest{Worker: worker}} listAssignments := rpcCall{ateapi.Control_ListWorkerActorAssignments_FullMethodName, &ateapi.ListWorkerActorAssignmentsRequest{Worker: worker}} mintJWT := rpcCall{ateapi.Control_MintActorJwt_FullMethodName, &ateapi.MintActorJwtRequest{Actor: actor}} - mintCert := rpcCall{ateapi.Control_MintActorCertificate_FullMethodName, &ateapi.MintActorCertificateRequest{Actor: actor}} mtls := func(id string) principal.PrincipalInfo { return principal.PrincipalInfo{ID: id, Kind: principal.KindMTLS} @@ -699,7 +697,6 @@ func TestUnaryServerInterceptor_SystemPrincipals(t *testing.T) { {"egress gateway lists actors", mtls(egressID), listAllActors, codes.OK}, {"egress gateway cannot get worker", mtls(egressID), getWorker, codes.PermissionDenied}, {"egress gateway mints actor JWT", mtls(egressID), mintJWT, codes.OK}, - {"egress gateway mints actor certificate", mtls(egressID), mintCert, codes.OK}, // atenet-router resumes actors. Both it and atenet-egress list actors // as their ateapi health check. @@ -711,7 +708,7 @@ func TestUnaryServerInterceptor_SystemPrincipals(t *testing.T) { {"ingress router cannot get egress policy", mtls(routerID), getEgress, codes.PermissionDenied}, {"ingress router cannot create worker", mtls(routerID), createWorker, codes.PermissionDenied}, {"ingress router cannot mint actor JWT", mtls(routerID), mintJWT, codes.PermissionDenied}, - {"controller cannot mint actor certificate", mtls(controllerID), mintCert, codes.PermissionDenied}, + {"controller cannot mint actor JWT", mtls(controllerID), mintJWT, codes.PermissionDenied}, // atelet reads egress policies for the worker that enforces them, and // nothing else. diff --git a/cmd/ateapi/internal/authz/model.fga b/cmd/ateapi/internal/authz/model.fga index 1d42a82e3e..5f71197ffe 100644 --- a/cmd/ateapi/internal/authz/model.fga +++ b/cmd/ateapi/internal/authz/model.fga @@ -169,7 +169,6 @@ type actor # Credentials asserting the actor's own identity. Only the egress gateway # mints them; an editor holding one could impersonate the actor. define can_mint_actor_jwt: egress_gateway from parent_atespace - define can_mint_actor_certificate: egress_gateway from parent_atespace # Data Plane Ingress: Routers (or editors) can resume define can_resume: [user] or editor from parent_atespace or ingress_router from parent_atespace diff --git a/cmd/ateapi/internal/authz/model_test.fga.yaml b/cmd/ateapi/internal/authz/model_test.fga.yaml index 02741ca1db..3962a8072b 100644 --- a/cmd/ateapi/internal/authz/model_test.fga.yaml +++ b/cmd/ateapi/internal/authz/model_test.fga.yaml @@ -401,7 +401,6 @@ tests: can_get: true can_get_egress_policy: true can_mint_actor_jwt: true - can_mint_actor_certificate: true can_update: false can_delete: false can_create_egress_policy: false @@ -430,7 +429,6 @@ tests: can_update_egress_policy: false can_delete_egress_policy: false can_mint_actor_jwt: false - can_mint_actor_certificate: false can_resume: false can_suspend: false - user: user:sys-atelet @@ -456,7 +454,6 @@ tests: can_resume: false can_suspend: false can_mint_actor_jwt: false - can_mint_actor_certificate: false - user: user:sys-api-server-sidecar object: atespace:space-1 assertions: @@ -567,14 +564,11 @@ tests: object: actor:runner-1 assertions: can_mint_actor_jwt: false - can_mint_actor_certificate: false - user: user:bob # Atespace editor object: actor:runner-1 assertions: can_mint_actor_jwt: false - can_mint_actor_certificate: false - user: user:sys-router object: actor:runner-1 assertions: can_mint_actor_jwt: false - can_mint_actor_certificate: false diff --git a/cmd/ateapi/internal/authz/registry.go b/cmd/ateapi/internal/authz/registry.go index 741e716bbd..951a27480b 100644 --- a/cmd/ateapi/internal/authz/registry.go +++ b/cmd/ateapi/internal/authz/registry.go @@ -298,9 +298,6 @@ var defaultRPCPermissions = map[string]rpcRule{ ateapi.Control_MintActorJwt_FullMethodName: rule(checksOf(func(r *ateapi.MintActorJwtRequest) ([]check, field.ErrorList) { return onActor(RelationCanMintActorJWT, r.GetActor(), field.NewPath("actor")) })), - ateapi.Control_MintActorCertificate_FullMethodName: rule(checksOf(func(r *ateapi.MintActorCertificateRequest) ([]check, field.ErrorList) { - return onActor(RelationCanMintActorCertificate, r.GetActor(), field.NewPath("actor")) - })), // Tags. CreateTag stores the tag in its source actor's atespace, so that is // the atespace checked. Validation also requires metadata.atespace to diff --git a/cmd/ateapi/internal/authz/registry_test.go b/cmd/ateapi/internal/authz/registry_test.go index 57d2b6bc77..51ada93341 100644 --- a/cmd/ateapi/internal/authz/registry_test.go +++ b/cmd/ateapi/internal/authz/registry_test.go @@ -281,12 +281,6 @@ func TestDefaultRPCPermissions(t *testing.T) { req: &ateapi.MintActorJwtRequest{Actor: actorRef}, want: []check{{RelationCanMintActorJWT, "actor:team-a/runner"}}, }, - { - name: "MintActorCertificate", - fullMethod: ateapi.Control_MintActorCertificate_FullMethodName, - req: &ateapi.MintActorCertificateRequest{Actor: actorRef}, - want: []check{{RelationCanMintActorCertificate, "actor:team-a/runner"}}, - }, // Tags. { @@ -603,12 +597,6 @@ func TestDefaultRPCPermissions_MalformedIdentifierFailsClosed(t *testing.T) { req: &ateapi.MintActorJwtRequest{}, wantField: "actor.atespace", }, - { - name: "MintActorCertificate with an invalid name", - fullMethod: ateapi.Control_MintActorCertificate_FullMethodName, - req: &ateapi.MintActorCertificateRequest{Actor: &ateapi.ObjectRef{Atespace: "team-a", Name: "a/b"}}, - wantField: "actor.name", - }, { name: "CreateTag without a source actor", fullMethod: ateapi.Control_CreateTag_FullMethodName, diff --git a/cmd/ateapi/internal/controlapi/access_policy_test.go b/cmd/ateapi/internal/controlapi/access_policy_test.go index b612ce8ab3..a76facb096 100644 --- a/cmd/ateapi/internal/controlapi/access_policy_test.go +++ b/cmd/ateapi/internal/controlapi/access_policy_test.go @@ -48,7 +48,7 @@ func TestAccessPolicy_GlobalAndAtespaceGovernance(t *testing.T) { } persistence.SetPolicyManager(policyManager) - svc := NewRPCService(persistence, nil, nil, nil, nil, nil, "", 0, nil, nil, "", nil, nil, "") + svc := NewRPCService(persistence, nil, nil, nil, nil, nil, "", 0, nil, nil, "", nil, "") interceptor := authz.UnaryServerInterceptor(authorizer) userCtx := func(id string) context.Context { diff --git a/cmd/ateapi/internal/controlapi/actor.go b/cmd/ateapi/internal/controlapi/actor.go index bd092788e0..8cac775de9 100644 --- a/cmd/ateapi/internal/controlapi/actor.go +++ b/cmd/ateapi/internal/controlapi/actor.go @@ -17,11 +17,8 @@ package controlapi import ( "context" "crypto/rand" - "crypto/x509" "errors" "fmt" - "log/slog" - "net/url" "slices" "time" @@ -34,10 +31,7 @@ import ( "go.ate.dev/substrate/internal/ateattr" "go.ate.dev/substrate/internal/preview" "go.ate.dev/substrate/internal/resources" - "go.ate.dev/substrate/internal/substratex509" "go.opentelemetry.io/otel/attribute" - "google.golang.org/grpc/credentials" - "google.golang.org/grpc/peer" "google.golang.org/protobuf/proto" "google.golang.org/protobuf/types/known/timestamppb" "k8s.io/apimachinery/pkg/util/validation/field" @@ -608,87 +602,3 @@ func (s *RPCService) MintActorJwt(ctx context.Context, req *ateapi.MintActorJwtR ExpireTime: timestamppb.New(expiresAt), }, nil } - -func (s *RPCService) MintActorCertificate(ctx context.Context, req *ateapi.MintActorCertificateRequest) (*ateapi.MintActorCertificateResponse, error) { - if errs := apivalidation.ValidateMintActorCertificateRequest(ctx, req); len(errs) > 0 { - return nil, resources.ToAPIError(errs) - } - - // With authz enforced, the interceptor requires can_mint_actor_certificate - // on the actor, which only the egress gateway holds. - - // Check that the caller authenticated with a client certificate --- we - // should not allow bootstrapping a proof-of-possession credential from a - // bearer credential. Note, we don't care that it was a certificate issued - // by Substrate, or something else. - // - // TODO(authz): Perhaps this can be handled with an OpenFGA condition. - p, ok := peer.FromContext(ctx) - if !ok { - return nil, apierror.Unauthenticated("no peer transport information found") - } - tlsInfo, ok := p.AuthInfo.(credentials.TLSInfo) - if !ok { - return nil, apierror.Unauthenticated("unexpected peer transport credentials") - } - if len(tlsInfo.State.PeerCertificates) == 0 { - return nil, apierror.Unauthenticated("could not verify peer certificate") - } - - // Verify that this actor exists in the store. It doesn't need to be - // running, since we may need to issue certificates during actor boot / resume. - dbActor, err := s.impl.GetActor(ctx, resources.ActorRefFromObjectRef(req.GetActor())) - if errors.Is(err, store.ErrNotFound) { - return nil, apierror.NotFound("actor not found") - } else if err != nil { - return nil, fmt.Errorf("while retrieving actor: %w", err) - } - if dbActor.GetMetadata().GetUid() != req.GetActorUid() { - return nil, apierror.Aborted("conflict; actor has been deleted and recreated") - } - - // Parse the CSR - csr, err := x509.ParseCertificateRequest(req.GetCertificateSigningRequest()) - if err != nil { - return nil, fmt.Errorf("while parsing CSR: %w", err) - } - if err := csr.CheckSignature(); err != nil { - slog.ErrorContext(ctx, "Failed to verify CSR signature", slog.Any("err", err)) - return nil, apierror.InvalidArgument("Failed to verify CSR signature") - } - - template := &x509.Certificate{ - URIs: []*url.URL{resources.ActorSPIFFEID(resources.ActorRef{ - Atespace: dbActor.GetMetadata().GetAtespace(), - Name: dbActor.GetMetadata().GetName(), - })}, - NotBefore: time.Now().Add(-5 * time.Minute), - NotAfter: time.Now().Add(time.Hour), - KeyUsage: x509.KeyUsageDigitalSignature, - ExtKeyUsage: []x509.ExtKeyUsage{x509.ExtKeyUsageClientAuth, x509.ExtKeyUsageServerAuth}, - BasicConstraintsValid: true, - IsCA: false, - } - - err = substratex509.AddActorIdentityToCertificate( - &substratex509.ActorIdentity{ - Atespace: dbActor.GetMetadata().GetAtespace(), - ActorName: dbActor.GetMetadata().GetName(), - ActorUid: dbActor.GetMetadata().GetUid(), - }, - template, - ) - if err != nil { - return nil, fmt.Errorf("while adding Substrate extension: %w", err) - } - - // Sign and return the actor cert. - chain, err := s.actorIDCAPool.CreateCertificate(template, csr.PublicKey) - if err != nil { - return nil, fmt.Errorf("while signing certificate: %w", err) - } - - return &ateapi.MintActorCertificateResponse{ - ActorCertificates: chain, - }, nil -} diff --git a/cmd/ateapi/internal/controlapi/actor_test.go b/cmd/ateapi/internal/controlapi/actor_test.go index f1c3b5fd96..1c8ea23255 100644 --- a/cmd/ateapi/internal/controlapi/actor_test.go +++ b/cmd/ateapi/internal/controlapi/actor_test.go @@ -16,16 +16,10 @@ package controlapi import ( "context" - "crypto/ecdsa" - "crypto/elliptic" - "crypto/rand" - "crypto/tls" - "crypto/x509" "errors" "fmt" "strings" "testing" - "time" "github.com/google/go-cmp/cmp" "go.ate.dev/substrate/api/ateapi/v1" @@ -34,13 +28,9 @@ import ( "go.ate.dev/substrate/cmd/ateapi/internal/store/storetest" "go.ate.dev/substrate/internal/apierror" "go.ate.dev/substrate/internal/ateattr" - "go.ate.dev/substrate/internal/localca" "go.ate.dev/substrate/internal/preview" "go.ate.dev/substrate/internal/resources" - "go.ate.dev/substrate/internal/substratex509" "google.golang.org/grpc/codes" - "google.golang.org/grpc/credentials" - "google.golang.org/grpc/peer" "google.golang.org/protobuf/proto" "google.golang.org/protobuf/testing/protocmp" "google.golang.org/protobuf/types/known/timestamppb" @@ -1069,101 +1059,3 @@ func TestCreateActor_GoldenTagDefault(t *testing.T) { }) } } - -type fakeActorServiceStore struct { - serviceStore - actors map[resources.ActorRef]*ateapi.Actor -} - -func (f *fakeActorServiceStore) GetActor(_ context.Context, actorRef resources.ActorRef) (*ateapi.Actor, error) { - a, ok := f.actors[actorRef] - if !ok { - return nil, store.ErrNotFound - } - return a, nil -} - -func TestMintActorCertificate(t *testing.T) { - ctx := peer.NewContext(context.Background(), &peer.Peer{ - AuthInfo: credentials.TLSInfo{ - State: tls.ConnectionState{ - PeerCertificates: []*x509.Certificate{{}}, - }, - }, - }) - - actorUID := "3b9f1e77-2c4d-4a80-91be-6d5c8f0a7e21" - created := &ateapi.Actor{ - Metadata: &ateapi.ResourceMetadata{Atespace: testAtespace, Name: testActorID, Uid: actorUID}, - ActorTemplate: &ateapi.ObjectRef{Atespace: testAtespace, Name: "tmpl-1"}, - Status: &ateapi.ActorStatus{State: ateapi.ActorState_ACTOR_STATE_RUNNING}, - } - fakeStore := &fakeActorServiceStore{ - actors: map[resources.ActorRef]*ateapi.Actor{ - {Atespace: testAtespace, Name: testActorID}: created, - }, - } - - ca, err := localca.GenerateCA("1", localca.KeyTypeECDSAP256, 24*time.Hour) - if err != nil { - t.Fatalf("GenerateCA: %v", err) - } - caPool := &localca.ConcretePool{ - CAs: []*localca.CA{ca}, - ActiveForSigning: "1", - } - - svc := &RPCService{ - impl: fakeStore, - actorIDCAPool: caPool, - } - - key, err := ecdsa.GenerateKey(elliptic.P256(), rand.Reader) - if err != nil { - t.Fatalf("GenerateKey: %v", err) - } - csr, err := x509.CreateCertificateRequest(rand.Reader, &x509.CertificateRequest{}, key) - if err != nil { - t.Fatalf("CreateCertificateRequest: %v", err) - } - - resp, err := svc.MintActorCertificate(ctx, &ateapi.MintActorCertificateRequest{ - Actor: &ateapi.ObjectRef{Atespace: testAtespace, Name: testActorID}, - ActorUid: created.GetMetadata().GetUid(), - CertificateSigningRequest: csr, - }) - if err != nil { - t.Fatalf("MintActorCertificate() failed: %v", err) - } - - chain := resp.GetActorCertificates() - if len(chain) == 0 { - t.Fatal("MintActorCertificate() returned empty chain") - } - - leaf, err := x509.ParseCertificate(chain[0]) - if err != nil { - t.Fatalf("ParseCertificate: %v", err) - } - if !key.PublicKey.Equal(leaf.PublicKey) { - t.Error("leaf public key does not match CSR key") - } - - wantURI := fmt.Sprintf("spiffe://substrate-actor.local/actor/%s/%s", testAtespace, testActorID) - if len(leaf.URIs) != 1 || leaf.URIs[0].String() != wantURI { - t.Errorf("leaf URIs = %v, want [%s]", leaf.URIs, wantURI) - } - - identity, err := substratex509.ActorIdentityFromCertificate(leaf) - if err != nil { - t.Fatalf("ActorIdentityFromCertificate: %v", err) - } - wantIdentity := &substratex509.ActorIdentity{ - Atespace: testAtespace, - ActorName: testActorID, - ActorUid: created.GetMetadata().GetUid(), - } - if diff := cmp.Diff(wantIdentity, identity); diff != "" { - t.Errorf("ActorIdentity mismatch (-want +got):\n%s", diff) - } -} diff --git a/cmd/ateapi/internal/controlapi/functionaltest/common_test.go b/cmd/ateapi/internal/controlapi/functionaltest/common_test.go index e628c0145e..7a34330bcb 100644 --- a/cmd/ateapi/internal/controlapi/functionaltest/common_test.go +++ b/cmd/ateapi/internal/controlapi/functionaltest/common_test.go @@ -30,7 +30,6 @@ import ( "go.ate.dev/substrate/cmd/ateapi/internal/workercache" "go.ate.dev/substrate/internal/ateinterceptors" "go.ate.dev/substrate/internal/installdefaults" - "go.ate.dev/substrate/internal/localca" "go.ate.dev/substrate/internal/localjwtauthority" "go.ate.dev/substrate/internal/objectstore/objectstoretest" "go.ate.dev/substrate/internal/objectstoreplugin/objectstoreplugintest" @@ -217,18 +216,6 @@ func setupTestWithOptions(t *testing.T, ns string, options testOptions) *testCon ActiveForSigning: "1", } - actorCA, err := localca.GenerateCA("1", localca.KeyTypeECDSAP256, 365*24*time.Hour) - if err != nil { - t.Fatalf("Error generating actor CA: %v", err) - } - - actorCAPool := &localca.ConcretePool{ - CAs: []*localca.CA{ - actorCA, - }, - ActiveForSigning: "1", - } - objectStore := objectstoretest.New() service := controlapi.NewRPCService( @@ -244,7 +231,6 @@ func setupTestWithOptions(t *testing.T, ns string, options testOptions) *testCon objectstoreplugintest.ControlClient(objectStore), testActorJWTIssuer, actorJWTAuthorityPool, - actorCAPool, testStorageLocation, ) diff --git a/cmd/ateapi/internal/controlapi/service.go b/cmd/ateapi/internal/controlapi/service.go index 65a2fc9d53..9144beaa93 100644 --- a/cmd/ateapi/internal/controlapi/service.go +++ b/cmd/ateapi/internal/controlapi/service.go @@ -23,7 +23,6 @@ import ( "go.ate.dev/substrate/api/objectstore/v1" "go.ate.dev/substrate/cmd/ateapi/internal/store" "go.ate.dev/substrate/cmd/ateapi/internal/workercache" - "go.ate.dev/substrate/internal/localca" "go.ate.dev/substrate/internal/localjwtauthority" "go.ate.dev/substrate/internal/resources" "go.ate.dev/substrate/internal/volume" @@ -53,7 +52,6 @@ type RPCService struct { actorJWTIssuer string actorIDJWTPool localjwtauthority.Pool - actorIDCAPool localca.Pool } var _ ateapi.ControlServer = (*RPCService)(nil) @@ -90,7 +88,6 @@ func NewRPCService( objectStorePlugin objectstore.ControlProviderClient, actorJWTIssuer string, actorIDJWTPool localjwtauthority.Pool, - actorIDCAPool localca.Pool, snapshotLocation string, ) *RPCService { impl := newServiceImpl(persistence, storageClassLister) @@ -106,7 +103,6 @@ func NewRPCService( objectStorePlugin: objectStorePlugin, actorJWTIssuer: actorJWTIssuer, actorIDJWTPool: actorIDJWTPool, - actorIDCAPool: actorIDCAPool, } s.actorWorkflow = NewActorWorkflow(impl, workerCache, dialer, storageClassLister, instruments, egressGatewayAddress, s, actorWorkflowDeadline, objectStorePlugin) s.actorWorkflow.clusterSnapshotLocation = snapshotLocation diff --git a/cmd/ateapi/internal/controlapi/workflow_tag.go b/cmd/ateapi/internal/controlapi/workflow_tag.go index ba20448362..29e23048cb 100644 --- a/cmd/ateapi/internal/controlapi/workflow_tag.go +++ b/cmd/ateapi/internal/controlapi/workflow_tag.go @@ -274,7 +274,7 @@ func (w *ActorWorkflow) checkTagCreatable(ctx context.Context, tagRef resources. return err } if err := resources.ValidateSnapshotLocation(location); err != nil { - return fmt.Errorf("invalid storage location for tag %s: %w", tagRef, err) + return apierror.FailedPrecondition("invalid storage location for tag %s: %w", tagRef, err) } return nil } diff --git a/cmd/ateapi/internal/controlapi/workflow_tag_test.go b/cmd/ateapi/internal/controlapi/workflow_tag_test.go index e82a75213e..d9d7c42991 100644 --- a/cmd/ateapi/internal/controlapi/workflow_tag_test.go +++ b/cmd/ateapi/internal/controlapi/workflow_tag_test.go @@ -158,6 +158,34 @@ func TestTagActorSnapshot_ActorRepointedToAnotherTemplate(t *testing.T) { } } +// TestTagActorSnapshot_LocationOutsideSegmentRules verifies that a tag of a +// template whose stored location breaks the segment rules fails with +// FailedPrecondition. +func TestTagActorSnapshot_LocationOutsideSegmentRules(t *testing.T) { + ctx := context.Background() + persistence := newTestPersistence(t) + template, err := persistence.CreateActorTemplate(ctx, &ateapi.ActorTemplate{ + Metadata: &ateapi.ResourceMetadata{Atespace: "team-a", Name: "sub-tmpl"}, + SnapshotConfig: &ateapi.SnapshotConfig{StorageLocation: "gs://ate-snapshots/a.b/"}, + }) + if err != nil { + t.Fatalf("CreateActorTemplate: %v", err) + } + w, objects := newFinalizeWorkflow(persistence) + actor, _ := seedTagSource(t, ctx, persistence, objects, template, "actor-1", "manifest.json") + + _, err = w.TagActorSnapshot(ctx, tagToCreate(resources.ActorRefFromActor(actor), "v1")) + if code := apierror.Code(err); code != codes.FailedPrecondition { + t.Fatalf("TagActorSnapshot error = %v (code %v), want code %v", err, code, codes.FailedPrecondition) + } + if !strings.Contains(err.Error(), "invalid storage location") { + t.Errorf("TagActorSnapshot error = %v, want it to name the invalid storage location", err) + } + if _, err := persistence.GetTag(ctx, resources.TagRef{Atespace: "team-a", Name: "v1"}); !errors.Is(err, store.ErrNotFound) { + t.Errorf("GetTag after failed create: err = %v, want ErrNotFound", err) + } +} + // TestTagActorSnapshot_Preconditions verifies what a tag may be taken from: // a suspended actor's finished external snapshot, and nothing else. func TestTagActorSnapshot_Preconditions(t *testing.T) { @@ -316,7 +344,8 @@ func TestTagActorSnapshot_PromotesLocalSnapshot(t *testing.T) { nodeLost bool // nameTaken seeds a tag under the name the create asks for. nameTaken bool - // badLocation points the template at an unusable storage location. + // badLocation points the template at a storage location with an + // atespaces segment. badLocation bool // noTemplateUID records no template the local snapshot was built // under, a broken row. @@ -354,9 +383,9 @@ func TestTagActorSnapshot_PromotesLocalSnapshot(t *testing.T) { wantCode: codes.AlreadyExists, }, { - name: "the storage location is unusable", + name: "the template's storage location has an atespaces segment", badLocation: true, - wantCode: codes.Internal, + wantCode: codes.FailedPrecondition, }, { name: "the paused snapshot records no template", @@ -494,7 +523,7 @@ func TestTagActorSnapshot_PromotesLocalSnapshot(t *testing.T) { } if tt.badLocation { _, err := persistence.UpdateActorTemplate(ctx, resources.ActorTemplateRefFromActorTemplate(template), store.PreconditionFrom(template), func(tmpl *ateapi.ActorTemplate) error { - tmpl.SnapshotConfig.StorageLocation = "gs://ate-snapshots/team-a/?query" + tmpl.SnapshotConfig.StorageLocation = "gs://ate-snapshots/root/atespaces/team-b/actors/cc10" return nil }) if err != nil { diff --git a/cmd/ateapi/main.go b/cmd/ateapi/main.go index 2bf57909b2..acc4fa1e82 100644 --- a/cmd/ateapi/main.go +++ b/cmd/ateapi/main.go @@ -363,7 +363,6 @@ func main() { objectstore.NewControlProviderClient(objectStorePluginConn), *actorJWTIssuer, actorIDJWTAuthorityPool, - actorIDCAPool, *snapshotLocation, ) diff --git a/cmd/atelet/imagevolume_test.go b/cmd/atelet/imagevolume_test.go index 387688a823..c5a2bd17cb 100644 --- a/cmd/atelet/imagevolume_test.go +++ b/cmd/atelet/imagevolume_test.go @@ -75,11 +75,26 @@ func singleFileLayer(t *testing.T, path, body string) v1.Layer { // pushTestImage pushes the image and returns its digest-pinned reference, // which is what validated workload specs carry. func pushTestImage(t *testing.T, ref string, layers ...v1.Layer) string { + t.Helper() + return pushTestImageWithConfig(t, ref, v1.Config{}, layers...) +} + +// pushTestImageWithConfig is pushTestImage with the image config set. +func pushTestImageWithConfig(t *testing.T, ref string, cfg v1.Config, layers ...v1.Layer) string { t.Helper() img, err := mutate.AppendLayers(empty.Image, layers...) if err != nil { t.Fatalf("mutate.AppendLayers: %v", err) } + cf, err := img.ConfigFile() + if err != nil { + t.Fatalf("img.ConfigFile: %v", err) + } + cf = cf.DeepCopy() + cf.Config = cfg + if img, err = mutate.ConfigFile(img, cf); err != nil { + t.Fatalf("mutate.ConfigFile: %v", err) + } tag, err := name.ParseReference(ref, name.Insecure) if err != nil { t.Fatalf("name.ParseReference(%q): %v", ref, err) diff --git a/cmd/atelet/main.go b/cmd/atelet/main.go index 1987f2bae9..0f822093e5 100644 --- a/cmd/atelet/main.go +++ b/cmd/atelet/main.go @@ -236,7 +236,10 @@ func main() { }() } - startDevicePlugins(ctx) + // stopped on SIGTERM because the replacement pod starts while we drain and + // registers the same socket, and kubelet can wedge the resource when two + // registrations for one socket overlap + startDevicePlugins(shutdownCtx) workerDialer := newWorkerDialer(256) @@ -2278,19 +2281,25 @@ func (s *Atelet) prepareOCIBundles( envs = append(envs, fmt.Sprintf("%s=%s", env.GetName(), env.GetValue())) } g.Go(func() error { - imageConfig, err := prepareOCIDirectory(gCtx, s.imageCache, actorUID, ctr.GetName(), ctr.GetImage(), spec.GetVolumes(), ctr.GetVolumeMounts()) + img, cwd, err := prepareOCIDirectory(gCtx, s.imageCache, actorUID, ctr.GetName(), ctr.GetImage(), spec.GetVolumes(), ctr.GetVolumeMounts()) if err != nil { return wrapFileSystemErr(fmt.Sprintf("while creating %q OCI bundle", ctr.GetName()), err) } - args, err := resolveProcessArgs(imageConfig, ctr.GetCommand(), ctr.GetArgs()) + args, err := resolveProcessArgs(&img.Config, ctr.GetCommand(), ctr.GetArgs()) if err != nil { return fmt.Errorf("while resolving process args for container %q: %w", ctr.GetName(), err) } + user, err := resolveImageUser(img) + if err != nil { + return fmt.Errorf("while resolving user for container %q: %w", ctr.GetName(), err) + } containerSpecs[i] = &ateworkerpb.ContainerSpec{ Args: args, - Env: resolveActorEnv(imageConfig, envs), + Env: resolveActorEnv(&img.Config, envs), Capabilities: resolveCapabilities(ctr.GetSecurityContext().GetCapabilities()), Resources: toAteworkerResources(ctr.GetResources()), + User: user, + Cwd: cwd, } return nil }) diff --git a/cmd/atelet/main_test.go b/cmd/atelet/main_test.go index d3daf4e277..162d1fba5b 100644 --- a/cmd/atelet/main_test.go +++ b/cmd/atelet/main_test.go @@ -34,6 +34,7 @@ import ( "time" "github.com/google/go-cmp/cmp" + v1 "github.com/google/go-containerregistry/pkg/v1" "github.com/spf13/pflag" "go.ate.dev/substrate/cmd/atelet/internal/actordirs" "go.ate.dev/substrate/cmd/atelet/internal/ateletpath" @@ -42,6 +43,7 @@ import ( "go.ate.dev/substrate/internal/ateattr" "go.ate.dev/substrate/internal/ateinterceptors" "go.ate.dev/substrate/internal/atelet" + "go.ate.dev/substrate/internal/imagecache" "go.ate.dev/substrate/internal/proto/ateletpb" "go.ate.dev/substrate/internal/proto/ateworkerpb" "go.ate.dev/substrate/internal/resources" @@ -265,12 +267,69 @@ func TestPrepareOCIBundlesFillsContainerSpec(t *testing.T) { Env: []string{"GREETING=hello", "PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin"}, Capabilities: []string{"CAP_AUDIT_WRITE", "CAP_NET_ADMIN", "CAP_NET_BIND_SERVICE"}, Resources: &ateworkerpb.ResourceRequirements{Limits: &ateworkerpb.Resources{MemoryBytes: 1 << 30, CpuMillicores: 500}}, + Cwd: "/", }} if diff := cmp.Diff(want, got, protocmp.Transform()); diff != "" { t.Errorf("prepareOCIBundles container specs mismatch (-want +got):\n%s", diff) } } +// TestPrepareOCIBundlesResolvesUsers pins that a container runs as its image's +// USER, resolved against the image's own passwd and group files; a USER the +// image cannot resolve fails the run. +func TestPrepareOCIBundlesResolvesUsers(t *testing.T) { + useTempNodeDirs(t) + host := imageVolumeTestRegistry(t) + etc := []v1.Layer{ + singleFileLayer(t, "etc/passwd", "root:x:0:0:root:/root:/bin/sh\nnonroot:x:65532:65532::/home/nonroot:/sbin/nologin\n"), + singleFileLayer(t, "etc/group", "root:x:0:\nnonroot:x:65532:\nvideo:x:44:nonroot\n"), + } + app := pushTestImageWithConfig(t, host+"/app:v1", v1.Config{User: "nonroot", Cmd: []string{"/bin/app"}}, etc...) + s := &Atelet{imageCache: newImageVolumeStore(t)} + + spec := &ateletpb.WorkloadSpec{Containers: []*ateletpb.Container{{Name: "app", Image: app}}} + got, err := s.prepareOCIBundles(t.Context(), "actor-uid-1", resources.ActorRef{}, spec) + if err != nil { + t.Fatalf("prepareOCIBundles: %v", err) + } + want := &ateworkerpb.ProcessUser{Uid: 65532, Gid: 65532, AdditionalGids: []uint32{44}} + if diff := cmp.Diff(want, got[0].GetUser(), protocmp.Transform()); diff != "" { + t.Errorf("app user mismatch (-want +got):\n%s", diff) + } + + unresolvable := pushTestImageWithConfig(t, host+"/nobody:v1", v1.Config{User: "nobody", Cmd: []string{"/bin/app"}}) + spec = &ateletpb.WorkloadSpec{Containers: []*ateletpb.Container{{Name: "app", Image: unresolvable}}} + if _, err := s.prepareOCIBundles(t.Context(), "actor-uid-2", resources.ActorRef{}, spec); err == nil { + t.Error("prepareOCIBundles succeeded with a USER the image has no passwd entry for, want an error") + } +} + +// TestPrepareOCIBundlesStartsInTheImageWorkdir pins that a container starts in +// its image's WORKDIR, created through the overlay when the image lacks it. +func TestPrepareOCIBundlesStartsInTheImageWorkdir(t *testing.T) { + useTempNodeDirs(t) + host := imageVolumeTestRegistry(t) + image := pushTestImageWithConfig(t, host+"/app:v1", v1.Config{Cmd: []string{"/bin/app"}, WorkingDir: "/home/nonroot"}) + spec := &ateletpb.WorkloadSpec{Containers: []*ateletpb.Container{{Name: "app", Image: image}}} + + const actorUID = "actor-uid-1" + s := &Atelet{imageCache: newImageVolumeStore(t)} + got, err := s.prepareOCIBundles(t.Context(), actorUID, resources.ActorRef{}, spec) + if err != nil { + t.Fatalf("prepareOCIBundles: %v", err) + } + if cwd := got[0].GetCwd(); cwd != "/home/nonroot" { + t.Errorf("Cwd = %q, want /home/nonroot", cwd) + } + overlay, err := imagecache.ReadSpec(ateletpath.OCIBundlePath(actorUID, "app")) + if err != nil { + t.Fatalf("imagecache.ReadSpec: %v", err) + } + if !slices.Contains(overlay.ExtraDirs, "/home/nonroot") { + t.Errorf("ExtraDirs = %v, want the working directory", overlay.ExtraDirs) + } +} + func TestSnapshotManifestRejectsNonLocalFile(t *testing.T) { manifest, err := json.Marshal(sandboxAssetsRecord{ SandboxClass: "gvisor", diff --git a/cmd/atelet/oci.go b/cmd/atelet/oci.go index 3f72633d5b..1cf5a1a9ca 100644 --- a/cmd/atelet/oci.go +++ b/cmd/atelet/oci.go @@ -15,17 +15,23 @@ package main import ( + "bytes" "context" + "errors" "fmt" + "io" + "io/fs" "os" "path" "sort" "strings" v1 "github.com/google/go-containerregistry/pkg/v1" + "github.com/moby/sys/user" "go.ate.dev/substrate/cmd/atelet/internal/ateletpath" "go.ate.dev/substrate/internal/imagecache" "go.ate.dev/substrate/internal/proto/ateletpb" + "go.ate.dev/substrate/internal/proto/ateworkerpb" "go.opentelemetry.io/otel" "go.opentelemetry.io/otel/attribute" "golang.org/x/sync/errgroup" @@ -74,7 +80,10 @@ func resolveCapabilities(caps *ateletpb.Capabilities) []string { return out } -func prepareOCIDirectory(ctx context.Context, imageCache *imagecache.Store, actorUID, containerName, ref string, volumes []*ateletpb.Volume, volumeMounts []*ateletpb.VolumeMount) (*v1.Config, error) { +// prepareOCIDirectory pulls the container's image and writes the overlay spec +// the worker composes its rootfs from. It returns the image and the process's +// working directory, which the worker creates through the overlay. +func prepareOCIDirectory(ctx context.Context, imageCache *imagecache.Store, actorUID, containerName, ref string, volumes []*ateletpb.Volume, volumeMounts []*ateletpb.VolumeMount) (*imagecache.Image, string, error) { tracer := otel.Tracer("prepareOCIDirectory") ctx, span := tracer.Start(ctx, "prepareOCIDirectory") @@ -87,18 +96,18 @@ func prepareOCIDirectory(ctx context.Context, imageCache *imagecache.Store, acto // actordirs.ResetActorDirs already wiped the bundle dir on the Run/Restore // path). if err := imagecache.RemoveAllWritable(bundlePath); err != nil { - return nil, fmt.Errorf("while clearing bundle %q: %w", bundlePath, err) + return nil, "", fmt.Errorf("while clearing bundle %q: %w", bundlePath, err) } // The bundle's rootfs is composed by worker as an overlay mount just before // the workload runs: the cached image layers are the read-only lowerdirs, // and the bundle-local upper/work hold this actor's private writes (wiped // between runs, preserving the pristine-rootfs-per-run contract the old - // full re-untar provided). atelet only prepares the (empty) directories — - // it deliberately runs with no capabilities, so it cannot mount. + // full re-untar provided). atelet only prepares the (empty) directories: + // it has no CAP_SYS_ADMIN, so it cannot mount. for _, d := range []string{"rootfs", "upper", "work"} { if err := os.MkdirAll(path.Join(bundlePath, d), 0o700); err != nil { - return nil, fmt.Errorf("in os.MkdirAll for container bundle dir: %w", err) + return nil, "", fmt.Errorf("in os.MkdirAll for container bundle dir: %w", err) } } @@ -120,26 +129,45 @@ func prepareOCIDirectory(ctx context.Context, imageCache *imagecache.Store, acto return err }) if err := g.Wait(); err != nil { - return nil, err + return nil, "", err + } + cwd, err := resolveCwd(&img.Config) + if err != nil { + return nil, "", fmt.Errorf("while resolving the working directory of container %q: %w", containerName, err) } - // Every bind target must exist in the rootfs for the mount to attach; - // worker creates them through the mounted overlay (they land in the - // actor's upper). + // Every bind target must exist in the rootfs for the mount to attach, and + // the working directory for the process to start; worker creates them + // through the mounted overlay (they land in the actor's upper). var extraDirs []string for _, vm := range volumeMounts { extraDirs = append(extraDirs, vm.GetMountPath()) } + if cwd != "/" { + extraDirs = append(extraDirs, cwd) + } if err := imagecache.WriteSpec(bundlePath, &imagecache.OverlaySpec{ ImageDigest: img.Digest.String(), Layers: img.LayerDirs, ExtraDirs: extraDirs, ImageVolumes: imageVolumes, }); err != nil { - return nil, fmt.Errorf("while writing overlay spec: %w", err) + return nil, "", fmt.Errorf("while writing overlay spec: %w", err) } - return &img.Config, nil + return img, cwd, nil +} + +// resolveCwd reads the image's WORKDIR; none means "/". The OCI spec requires +// an absolute path. +func resolveCwd(imageCfg *v1.Config) (string, error) { + if imageCfg == nil || imageCfg.WorkingDir == "" { + return "/", nil + } + if !path.IsAbs(imageCfg.WorkingDir) { + return "", fmt.Errorf("image WorkingDir %q is not absolute", imageCfg.WorkingDir) + } + return path.Clean(imageCfg.WorkingDir), nil } // resolveImageVolumes pulls the image behind every image-typed volume this @@ -241,3 +269,52 @@ func resolveProcessArgs(imageCfg *v1.Config, command, args []string) ([]string, } return argv, nil } + +// resolveImageUser resolves the image's USER against the image's own +// /etc/passwd and /etc/group, either of which may be absent. No USER is root, +// reported as nil. +func resolveImageUser(img *imagecache.Image) (*ateworkerpb.ProcessUser, error) { + if img.Config.User == "" { + return nil, nil + } + passwd, err := readOptionalImageFile(img, "etc/passwd") + if err != nil { + return nil, err + } + group, err := readOptionalImageFile(img, "etc/group") + if err != nil { + return nil, err + } + return resolveUser(img.Config.User, passwd, group) +} + +// readOptionalImageFile returns a nil reader for a file the image lacks. +func readOptionalImageFile(img *imagecache.Image, name string) (io.Reader, error) { + b, err := img.ReadFile(name) + if errors.Is(err, fs.ErrNotExist) { + return nil, nil + } + if err != nil { + return nil, err + } + return bytes.NewReader(b), nil +} + +// resolveUser follows Docker's rules for USER "user[:group]": numeric ids as +// is, a bare uid with its login group from passwd (gid 0 without an entry), +// names looked up or an error, group memberships as supplementary groups. +// Ids are bounded to int32. A nil reader means the image lacks the file. +func resolveUser(userSpec string, passwd, group io.Reader) (*ateworkerpb.ProcessUser, error) { + if strings.Count(userSpec, ":") > 1 { + return nil, fmt.Errorf("image User %q: want user[:group]", userSpec) + } + u, err := user.GetExecUser(userSpec, nil, passwd, group) + if err != nil { + return nil, fmt.Errorf("image User %q: %w", userSpec, err) + } + identity := &ateworkerpb.ProcessUser{Uid: uint32(u.Uid), Gid: uint32(u.Gid)} + for _, gid := range u.Sgids { + identity.AdditionalGids = append(identity.AdditionalGids, uint32(gid)) + } + return identity, nil +} diff --git a/cmd/atelet/oci_test.go b/cmd/atelet/oci_test.go index c76d2d0d03..1eed244303 100644 --- a/cmd/atelet/oci_test.go +++ b/cmd/atelet/oci_test.go @@ -15,11 +15,16 @@ package main import ( + "io" "slices" + "strings" "testing" + "github.com/google/go-cmp/cmp" v1 "github.com/google/go-containerregistry/pkg/v1" "go.ate.dev/substrate/internal/proto/ateletpb" + "go.ate.dev/substrate/internal/proto/ateworkerpb" + "google.golang.org/protobuf/testing/protocmp" ) func TestResolveActorEnv(t *testing.T) { @@ -176,6 +181,72 @@ func withCaps(in []string, add ...string) []string { return out } +func TestResolveUser(t *testing.T) { + const passwd = "root:x:0:0:root:/root:/bin/sh\n" + + "jovyan:x:1000:100::/home/jovyan:/bin/bash\n" + + "nonroot:x:65532:65532::/home/nonroot:/sbin/nologin\n" + const group = "root:x:0:\n" + + "users:x:100:\n" + + "nonroot:x:65532:\n" + + "video:x:44:nonroot,jovyan\n" + + "audio:x:29:nonroot\n" + tests := []struct { + name string + user string + passwd, group string // empty: the image lacks the file + want *ateworkerpb.ProcessUser + wantErr bool + }{ + {name: "bare uid, no passwd", user: "1000", want: &ateworkerpb.ProcessUser{Uid: 1000}}, + {name: "bare uid takes its login group and memberships", user: "1000", passwd: passwd, group: group, want: &ateworkerpb.ProcessUser{Uid: 1000, Gid: 100, AdditionalGids: []uint32{44}}}, + {name: "bare uid without a passwd entry", user: "1001", passwd: passwd, group: group, want: &ateworkerpb.ProcessUser{Uid: 1001}}, + {name: "uid:gid passes through", user: "1000:2000", passwd: passwd, group: group, want: &ateworkerpb.ProcessUser{Uid: 1000, Gid: 2000}}, + {name: "named user", user: "nonroot", passwd: passwd, group: group, want: &ateworkerpb.ProcessUser{Uid: 65532, Gid: 65532, AdditionalGids: []uint32{44, 29}}}, + {name: "named user and group", user: "jovyan:video", passwd: passwd, group: group, want: &ateworkerpb.ProcessUser{Uid: 1000, Gid: 44}}, + {name: "named user missing from passwd", user: "nobody", passwd: passwd, wantErr: true}, + {name: "named group missing from group", user: "1000:staff", passwd: passwd, group: group, wantErr: true}, + {name: "above int32", user: "2147483648", wantErr: true}, + {name: "third field", user: "1000:1000:1", wantErr: true}, + } + for _, tc := range tests { + t.Run(tc.name, func(t *testing.T) { + var passwdR, groupR io.Reader + if tc.passwd != "" { + passwdR = strings.NewReader(tc.passwd) + } + if tc.group != "" { + groupR = strings.NewReader(tc.group) + } + got, err := resolveUser(tc.user, passwdR, groupR) + if (err != nil) != tc.wantErr { + t.Fatalf("resolveUser(%q) = %v, %v; wantErr %v", tc.user, got, err, tc.wantErr) + } + if diff := cmp.Diff(tc.want, got, protocmp.Transform()); err == nil && diff != "" { + t.Errorf("resolveUser(%q) mismatch (-want +got):\n%s", tc.user, diff) + } + }) + } +} + +func TestResolveCwd(t *testing.T) { + for _, tc := range []struct { + image *v1.Config + want string + wantErr bool + }{ + {image: nil, want: "/"}, + {image: &v1.Config{}, want: "/"}, + {image: &v1.Config{WorkingDir: "/home/jovyan"}, want: "/home/jovyan"}, + {image: &v1.Config{WorkingDir: "/app/"}, want: "/app"}, + {image: &v1.Config{WorkingDir: "app"}, wantErr: true}, + } { + got, err := resolveCwd(tc.image) + if (err != nil) != tc.wantErr || got != tc.want { + t.Errorf("resolveCwd(%+v) = %q, %v; want %q, wantErr %v", tc.image, got, err, tc.want, tc.wantErr) + } + } +} + func TestResolveCapabilities(t *testing.T) { tests := []struct { name string diff --git a/cmd/atenet/internal/router/xds_test.go b/cmd/atenet/internal/router/xds_test.go index e616ed70b4..78ce5ff873 100644 --- a/cmd/atenet/internal/router/xds_test.go +++ b/cmd/atenet/internal/router/xds_test.go @@ -27,12 +27,14 @@ import ( "net" "os" "path/filepath" + "slices" "strings" "testing" "time" "google.golang.org/grpc" "google.golang.org/grpc/credentials/insecure" + "sigs.k8s.io/yaml" clusterv3 "github.com/envoyproxy/go-control-plane/envoy/config/cluster/v3" corev3 "github.com/envoyproxy/go-control-plane/envoy/config/core/v3" @@ -618,6 +620,95 @@ func TestXdsServer_ServesSecretOverSds(t *testing.T) { } } +// TestXdsServer_AnswersClusterSecretsBeforeListeners replays Envoy's startup +// over delta xDS, which the router manifest must configure. While warming +// clusters, Envoy subscribes only to the secrets the clusters reference, and +// asks for listeners (and their secrets) only after. The server has to answer +// that partial secret request, or Envoy stalls until its 15s +// initial_fetch_timeout. +func TestXdsServer_AnswersClusterSecretsBeforeListeners(t *testing.T) { + var bootstrap struct { + DynamicResources struct { + AdsConfig struct { + APIType string `json:"api_type"` + } `json:"ads_config"` + } `json:"dynamic_resources"` + } + if err := yaml.Unmarshal([]byte(envoyConfigFrom(t, routerManifestPath, "atenet-router-envoy-config")), &bootstrap); err != nil { + t.Fatalf("parsing envoy.yaml: %v", err) + } + if got := bootstrap.DynamicResources.AdsConfig.APIType; got != "DELTA_GRPC" { + t.Fatalf("Router Envoy ads_config.api_type is %q; it must be DELTA_GRPC, since the state-of-the-world ADS cache ignores a partial secret request", got) + } + + server := NewXdsServer(18000) + server.SetConfig(50053, "127.0.0.1") + server.SetTlsConfig(8443, "/run/servicedns.podcert.ate.dev/credential-bundle.pem") + server.SetDownstreamClientAuth("/run/podidentity.podcert.ate.dev/trust-bundle.pem", []string{"spiffe://cluster.local/ns/demo/sa/client"}) + server.SetUpstreamTls("/run/podidentity.podcert.ate.dev/credential-bundle.pem", "/run/podidentity.podcert.ate.dev/trust-bundle.pem") + server.SetUpstreamConnect(true) + + lis, err := net.Listen("tcp", "127.0.0.1:0") + if err != nil { + t.Fatalf("Failed to create tcp listener: %v", err) + } + ctx, cancel := context.WithCancel(context.Background()) + t.Cleanup(cancel) + go server.Serve(ctx, lis) + + conn, err := grpc.NewClient(lis.Addr().String(), grpc.WithTransportCredentials(insecure.NewCredentials())) + if err != nil { + t.Fatalf("Failed to dial xDS server: %v", err) + } + t.Cleanup(func() { conn.Close() }) + + streamCtx, streamCancel := context.WithTimeout(ctx, 10*time.Second) + t.Cleanup(streamCancel) + stream, err := discoverygrpc.NewAggregatedDiscoveryServiceClient(conn).DeltaAggregatedResources(streamCtx) + if err != nil { + t.Fatalf("Failed to open delta ADS stream: %v", err) + } + + if err := stream.Send(&discoverygrpc.DeltaDiscoveryRequest{ + Node: &corev3.Node{Id: NodeID}, + TypeUrl: resourcev3.ClusterType, + }); err != nil { + t.Fatalf("Failed to send CDS request: %v", err) + } + cds, err := stream.Recv() + if err != nil { + t.Fatalf("Failed to receive CDS response: %v", err) + } + if got := cds.GetTypeUrl(); got != resourcev3.ClusterType { + t.Fatalf("Expected a CDS response, got %s", got) + } + + clusterSecrets := []string{UpstreamCertSecretName, UpstreamTrustSecretName} + if err := stream.Send(&discoverygrpc.DeltaDiscoveryRequest{ + TypeUrl: resourcev3.SecretType, + ResourceNamesSubscribe: clusterSecrets, + }); err != nil { + t.Fatalf("Failed to send SDS request: %v", err) + } + // A server that ignores the request leaves Recv blocked until streamCtx + // expires. + sds, err := stream.Recv() + if err != nil { + t.Fatalf("No SDS response for the cluster secrets: %v", err) + } + if got := sds.GetTypeUrl(); got != resourcev3.SecretType { + t.Fatalf("Expected an SDS response, got %s", got) + } + var names []string + for _, r := range sds.GetResources() { + names = append(names, r.GetName()) + } + slices.Sort(names) + if want := slices.Sorted(slices.Values(clusterSecrets)); !slices.Equal(names, want) { + t.Errorf("Expected SDS response with secrets %v, got %v", want, names) + } +} + // Symlink names used by kubelet's AtomicWriter in projected volumes. const ( dataDirName = "..data" diff --git a/cmd/ateworker-microvm/internal/kata/specconv_linux_test.go b/cmd/ateworker-microvm/internal/kata/specconv_linux_test.go index 4b7d3afdc7..b211346f9f 100644 --- a/cmd/ateworker-microvm/internal/kata/specconv_linux_test.go +++ b/cmd/ateworker-microvm/internal/kata/specconv_linux_test.go @@ -17,6 +17,7 @@ package kata import ( + "slices" "testing" specs "github.com/opencontainers/runtime-spec/specs-go" @@ -174,3 +175,22 @@ func TestSpecToAgentPB_ForwardsSysctl(t *testing.T) { t.Fatalf("Linux.Sysctl = %v, want %s=0 carried through", got.GetLinux().GetSysctl(), key) } } + +// The agent starts the process as the user it is sent; a dropped identity +// would run the workload as root in the guest. +func TestSpecToAgentPB_ForwardsProcessUser(t *testing.T) { + got := SpecToAgentPB(&specs.Spec{ + Process: &specs.Process{User: specs.User{UID: 65532, GID: 65534, AdditionalGids: []uint32{44}}}, + }) + u := got.GetProcess().GetUser() + if u == nil || u.UID != 65532 || u.GID != 65534 || !slices.Equal(u.AdditionalGids, []uint32{44}) { + t.Fatalf("Process.User = %v, want 65532:65534 +44", u) + } +} + +func TestSpecToAgentPB_ForwardsCwd(t *testing.T) { + got := SpecToAgentPB(&specs.Spec{Process: &specs.Process{Cwd: "/work"}}) + if got.GetProcess().GetCwd() != "/work" { + t.Fatalf("Process.Cwd = %q, want /work", got.GetProcess().GetCwd()) + } +} diff --git a/demos/jupyter/jupyter-template.yaml.tmpl b/demos/jupyter/jupyter-template.yaml.tmpl index df8cd69396..1877a397a1 100644 --- a/demos/jupyter/jupyter-template.yaml.tmpl +++ b/demos/jupyter/jupyter-template.yaml.tmpl @@ -28,7 +28,6 @@ containers: command: - jupyter - lab - - "--allow-root" - "--ServerApp.token=''" - "--ServerApp.password=''" - "--ServerApp.allow_origin='*'" diff --git a/docs/api-style-guide.md b/docs/api-style-guide.md index 518c9e16e6..74136326b7 100644 --- a/docs/api-style-guide.md +++ b/docs/api-style-guide.md @@ -283,7 +283,7 @@ Rules: - RPC name **must** begin with `Update` followed by the singular resource name. - Response **must** be the resource itself — not an `UpdateActorResponse` wrapper. - **Output-only** fields — server-managed, never set by the client (`uid`, `version`, `create_time`, `update_time`, `delete_time`, and the whole `status` submessage). Whatever the request carries in them is ignored and the server's own values are kept. - - **Immutable** fields — caller-set at creation but fixed thereafter (`atespace`, `name`, and resource-specific ones such as an actor's `source_snapshot_tag`). A request that changes one - including by omitting it, which would clear it - **must** return `INVALID_ARGUMENT` naming the field. + - **Immutable** fields (`atespace`, `name`, and resource-specific ones such as an actor's `source_tag`) are caller-set at creation and fixed thereafter. A request that changes one, including by omitting it, **must** return `INVALID_ARGUMENT` naming the field. - The embedded resource field **must** be named after the resource's own snake_case type name (e.g. `actor` for `Actor`), not a generic name like `resource` or `body`. - The request **must** have no other fields, apart from its parent's `ObjectRef` for a subresource (see section #2.4). - A request carrying a field the server does not know, at any level of nesting, **must** return `INVALID_ARGUMENT` naming the field, as for every RPC. Fields only the client does not know are a different case: a client that modifies the resource it read sends them back unchanged, so an update from an older client does not clear fields a newer server set. diff --git a/docs/assets/readme-hero.svg b/docs/assets/readme-hero.svg new file mode 100644 index 0000000000..ce9e5a214d --- /dev/null +++ b/docs/assets/readme-hero.svg @@ -0,0 +1,392 @@ + + +Agent Substrate multiplexes 250 agents onto 8 warm worker pods +Agents are idle most of the time. Idle agents suspend, resume on any ready worker, and keep their memory and filesystem state. The atenet router routes requests and resumes suspended agents onto 8 warm worker pods. + + + +HOW IT WORKS +Agents are idle most of the time. +Stop paying for it. + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +250 agents +most of them suspended + +AGENT SUBSTRATE + + + + +atenet router + + + +suspend when idle + +resume on request +Agent Substrate +routes & resumes on request + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +8 warm worker pods +several agents per pod, each in its own sandbox + + + + + + +Idle agents suspend, resume on any ready worker, +and keep their memory and filesystem state. + +Visit ate.dev + + +$ kubectl ate create actor my-agent --template counter + diff --git a/docs/concepts/architecture.md b/docs/concepts/architecture.md index c7920e27f7..e1da31fa4a 100644 --- a/docs/concepts/architecture.md +++ b/docs/concepts/architecture.md @@ -12,7 +12,7 @@ status_note: > ## Overview -Agent Substrate is a security-hardened, zero-trust runtime for sandboxed agents, engineered to safely run millions of agents and tools with 10x higher density than standard containers. Purpose-built for the era of autonomous agents, Substrate delivers fast resume operations in less than 400ms, with more than 1500 activations per second. Agents get isolated compute, identity, networking, and storage. Sensitive credentials are kept out of reach from untrusted code. It supports multiple sandbox technologies, including microVMs and gVisor, while enabling a consistent lifecycle and management of all sandboxes. +Agent Substrate is a security-hardened, zero-trust runtime for sandboxed agents, engineered to safely run millions of agents and tools with 10x higher density than standard containers. Purpose-built for the era of autonomous agents, Substrate delivers fast resume operations in less than 500ms, with more than 500 activations per second. Agents get isolated compute, identity, networking, and storage. Sensitive credentials are kept out of reach from untrusted code. It supports multiple sandbox technologies, including microVMs and gVisor, while enabling a consistent lifecycle and management of all sandboxes. At its core, Agent Substrate maps a larger set of “actors” (applications such as agents) onto a smaller set of ready “workers”, relying on @@ -224,6 +224,10 @@ administrative operations and actor environment definitions. to receive resumed actor states. Optional `spec.template` fields configure worker pod node selection, tolerations, priority class, and node affinity. + * **SandboxConfig** (Kubernetes CRD): A cluster-scoped resource that lists the + sandbox runtime versions and assets for a sandbox class (`gvisor` or + `microvm`). Each `WorkerPool` resolves a `SandboxConfig` for its class. + * **ActorTemplate** (ate API resource): An immutable definition of an actor-version. It encapsulates the container image, configuration, and environment required to generate a "golden" snapshot. ActorTemplates are @@ -238,12 +242,13 @@ actors and workers. They are stored in a high-performance, low-latency state store (PostgreSQL) to support real-time operations. * **Actor**: A specific instance of an ActorTemplate. An Actor record tracks - its globally unique identifier, physical location (Worker IP), current - state (RUNNING or SUSPENDED), and version-specific state metadata. + its globally unique identifier, physical location (Worker IP), lifecycle + state (such as `RUNNING` or `SUSPENDED`), and version-specific state metadata. - * **Worker**: A representation of a worker pod in the WorkerPool. It tracks - the worker's unique identifier, current status (IDLE or BUSY), and the - Actor it is currently hosting (if any). + * **Worker**: A representation of a worker pod in a WorkerPool. It tracks + the worker's unique identifier, status (`ACTIVE`, `UNAVAILABLE`, or + `DRAINING`), capacity and allocated resources, and the Actors it currently + hosts (each represented by an `ActorAssignment` subresource). ### Architectural Rationale @@ -277,10 +282,12 @@ classDiagram class WorkerPool { <> } + class SandboxConfig { + <> + } class Deployment class WorkerPod { ateworker - runsc } } @@ -290,21 +297,26 @@ classDiagram } class Actor { <> - status - snapshotRefs + status.state + status.snapshots } class Worker { <> - actorName - podIP + status.state + status.ips + } + class ActorAssignment { + <> } } ActorTemplate ..> WorkerPool : worker_selector (labels) + WorkerPool ..> SandboxConfig : sandboxClasses WorkerPool ..> Deployment : reconciled by atecontroller Deployment "1" *-- "*" WorkerPod : manages Actor "*" --> "1" ActorTemplate : actor_template - Actor "0..1" --> "0..1" Worker : runs on + Worker "1" *-- "*" ActorAssignment + ActorAssignment "0..1" --> "1" Actor : actor Worker "1" --> "1" WorkerPod : maps to ``` @@ -331,9 +343,9 @@ manage actor lifecycles. The node-level subsystem manages the physical execution of sandboxes and the movement of snapshots. - * **atelet**: A lightweight supervisor running on each node as a DaemonSet. It acts as the "Herder," managing a pool of physical pods and communicating with the Control Plane. + * **atelet**: A lightweight supervisor running on each node as a DaemonSet. It manages local worker pods and communicates with the Control Plane. - * **ateworker**: A specialized sandbox-herder container image — one per sandbox class (`ateworker-gvisor`, `ateworker-microvm`) — that runs inside the physical worker pods. It provides a gRPC interface for `atelet` to trigger `RunWorkload`, `CheckpointWorkload`, and `RestoreWorkload` operations. This separation ensures that the physical pod lifecycle remains decoupled from the sandboxed agent process. + * **ateworker**: The worker container image (`ateworker-gvisor` or `ateworker-microvm`, one per sandbox class) that runs inside each physical worker pod. It provides a gRPC interface for `atelet` to trigger `RunWorkload`, `CheckpointWorkload`, and `RestoreWorkload` operations. This separation ensures that the physical pod lifecycle remains decoupled from the sandboxed agent process. * **Lifecycle Management**: The `ateworker` process invokes the sandbox runtime to checkpoint or restore processes within the physical pod boundaries — `runsc` for gVisor, or the Kata + Cloud Hypervisor stack for microVMs. (Note: the gVisor backend currently requires a `runsc` version with the `--allow-connected-on-save` flag to work around a bug in networking resumption during checkpointing.) @@ -343,9 +355,9 @@ The node-level subsystem manages the physical execution of sandboxes and the mov ### Sandbox Classes -A `WorkerPool` selects a **sandbox class** (`spec.sandboxClasses[].name`), and each class has a matching `ateworker` herder image. The sandbox binaries themselves are not baked into the worker image. They come at runtime from the cluster-scoped [`SandboxConfig`](../reference/api-guide.md#3-sandboxconfig-the-sandbox-itself) the `WorkerPool` resolves for its class (an explicit `configRef`, else the class default). Each snapshot records the sandbox runtime it was taken on, and a memory restore runs on a worker that still offers that runtime, so restores stay reproducible across runtime upgrades. +A `WorkerPool` selects a **sandbox class** (`spec.sandboxClasses[].name`), and each class has a matching `ateworker` image. The sandbox binaries themselves are not baked into the worker image. They come at runtime from the cluster-scoped [`SandboxConfig`](../reference/api-guide.md#3-sandboxconfig-the-sandbox-itself) the `WorkerPool` resolves for its class (an explicit `configRef`, else the class default). Each snapshot records the sandbox runtime it was taken on, and a memory restore runs on a worker that still offers that runtime, so restores stay reproducible across runtime upgrades. For workload-visible differences between the classes, see [Sandbox Classes](../reference/sandbox-classes.md). - * **gVisor** (`ateworker-gvisor`, the default): Runs the workload under `runsc` for kernel-level sandboxing. Suspend and resume leverage gVisor's native checkpoint/restore of the sandboxed process tree. `DurableDir` volumes and each container's rootfs overlay are saved as separate filesystem checkpoint bundles (`fs/` and `rootfs/`). A `MEMORY` snapshot splits them out of the process checkpoint; `VOLUMES` and `ROOTFS` snapshots save only the bundles they need with `runsc fscheckpoint`, without memory, and restore them on a cold boot. + * **gVisor** (`ateworker-gvisor`, the default): Runs the workload under `runsc` for kernel-level sandboxing. Suspend and resume use gVisor's native checkpoint/restore of the sandboxed process tree. `DurableDir` volumes and each container's rootfs overlay are saved as separate filesystem checkpoint bundles (`fs/` and `rootfs/`). A `MEMORY` snapshot splits them out of the process checkpoint. `VOLUMES` and `ROOTFS` snapshots save only the bundles they need with `runsc fscheckpoint`, without memory, and restore them on a cold boot. * **microVM** (`ateworker-microvm`): Runs the workload inside a [Kata Containers](https://katacontainers.io/) guest on the [Cloud Hypervisor](https://www.cloudhypervisor.org/) VMM. Suspend and resume capture a memory-only VM snapshot and restore it on-demand using `userfaultfd` memory demand-paging. Container rootfs writes are host-backed: the overlay is assembled on the host (read-only OCI image lower plus a per-actor writable upper) and served to the guest over the single virtio-fs share, so they cost reclaimable host page cache rather than guest RAM, and `ROOTFS` and `MEMORY` snapshots ship the upper as its own tar (a `ROOTFS` restore puts it back and cold-boots on it). `DurableDir` volumes travel over that same share and are likewise shipped as a tar, so a `VOLUMES` snapshot can capture them without any guest memory. Each volume is a subdirectory of the share, so an actor can have several at no extra cost in devices. @@ -418,7 +430,7 @@ sequenceDiagram Tunnel-->>Atelet: ready Atelet-->>API: worker pod IP API-->>Gateway: worker assignment - Gateway->>Tunnel: mTLS tunnel to worker port 443 + Gateway->>Tunnel: mTLS CONNECT tunnel to worker port 8443 Tunnel->>A: forward request to Actor port A-->>Tunnel: response Tunnel-->>Gateway: response @@ -530,21 +542,15 @@ After deletion, the state of the actor (i.e., memory+disk snapshots) is garbage ## State Management & Persistence -Agent Substrate distinguishes between two types of state, which are currently -captured together in a single versioned snapshot: - - 1. **Memory Snapshot**: The exact RAM state of the process. +A snapshot captures up to three cumulative layers of actor state, controlled by `snapshotConfig.preferredFidelity` on the `ActorTemplate`: - 2. **Working Volume (Disk)**: The files written to the container's writable - layer (the "working memory"). + 1. **`VOLUMES`**: The contents of `DurableDir` volumes mounted into the actor's containers. + 2. **`ROOTFS`**: `VOLUMES` plus files written to each container's writable rootfs overlay since boot. + 3. **`MEMORY`**: `ROOTFS` plus the running process tree and RAM state (`MEMORY` is the default). -In the current implementation, both memory and disk states are tied to the -specific version of the code (ActorTemplate). This ensures strict consistency -during resumption. +`MEMORY` and `ROOTFS` state is tied to the `ActorTemplate` (`actorTemplateUid`). `MEMORY` state is also tied to the `sandboxRuntime` version it was captured under. Changing an actor's template while suspended keeps its `VOLUMES` data, and its next resume cold-boots the containers. Resume falls back to `ROOTFS` and cold-boots under the default runtime if no worker that offers the snapshot's runtime version has room. -Snapshots are stored durably in **Google Cloud Storage (GCS)**. This model -allows the physical compute resources in the `WorkerPool` to be fully reclaimed -while an actor is idle, without losing any process or filesystem progress. +Snapshots are stored durably in object storage (GCS or S3) through `objectstore-plugin`. This allows worker capacity in the `WorkerPool` to be reclaimed while an actor is suspended. ## Security & Isolation diff --git a/docs/get-started/overview.md b/docs/get-started/overview.md index 224482845a..001ad8ce11 100644 --- a/docs/get-started/overview.md +++ b/docs/get-started/overview.md @@ -40,8 +40,8 @@ Substrate decouples the **Actor** (a stateful agent or workload instance) from t
Pre-Warmed Worker Pod (WorkerPool) - Worker (ateom + Sandbox) - ateom herder · atunnel mTLS · microVM / gVisor hosting Actor + Worker (ateworker + Sandbox) + ateworker · atunnel mTLS · microVM / gVisor hosting Actor
@@ -50,6 +50,6 @@ Substrate decouples the **Actor** (a stateful agent or workload instance) from t
-* **Core Platform Terms at a Glance:** Each actor lives in an `atespace` (`/`), traffic enters via `atenet`, node supervision and snapshot streaming are handled by `atelet`, and sandbox execution inside each warm `Worker` is managed by `ateom` (internally prefixed `ate` across binaries and CRDs). See the **[Glossary](../reference/glossary.md)** for complete definitions. +* **Core Platform Terms at a Glance:** Each actor lives in an `atespace` (`/`), traffic enters via `atenet-router`, node supervision and snapshot streaming are handled by `atelet`, and sandbox execution inside each warm `Worker` is managed by `ateworker` (internally prefixed `ate` across binaries and CRDs). See the **[Glossary](../reference/glossary.md)** for complete definitions. * **Framework Agnostic:** Runs standard OCI containers inside zero-trust sandboxes—supporting ADK, LangChain, Hermes, OpenClaw, MCP servers, and stateful coding environments out of the box. -* **Ready to try it?** Jump straight to **[Quickstart](quickstart.md)** to launch your first actor locally on Kind, follow the **[GKE Installation Guide](https://docs.cloud.google.com/kubernetes-engine/ai-ml/install-overview-substrate)** to deploy on Google Kubernetes Engine, or explore the **[Architecture](../concepts/architecture.md)**. +* **Ready to try it?** Jump straight to **[Quickstart](quickstart.md)** to launch your first actor locally on [kind](https://kind.sigs.k8s.io/), follow the **[GKE Installation Guide](https://docs.cloud.google.com/kubernetes-engine/ai-ml/install-overview-substrate)** to deploy on Google Kubernetes Engine, or explore the **[Architecture](../concepts/architecture.md)**. diff --git a/docs/get-started/quickstart.md b/docs/get-started/quickstart.md index 1d37504b9d..376e96310a 100644 --- a/docs/get-started/quickstart.md +++ b/docs/get-started/quickstart.md @@ -5,25 +5,25 @@ weight: 2 aliases: - "/docs/getting-started/" description: > - Deploy Agent Substrate on a local Kind cluster and invoke your first stateful actor. + Deploy Agent Substrate on a local kind cluster and invoke your first stateful actor. --- # Quickstart & Installation -Deploy the Agent Substrate control plane and the stateful **Counter** sample application on a local `kind` cluster. +Deploy the Agent Substrate control plane and the stateful **Counter** sample application on a local [kind](https://kind.sigs.k8s.io/) cluster. > [!NOTE] -> **Prerequisites:** Ensure [Go](https://go.dev/doc/install), [`kubectl`](https://kubernetes.io/docs/tasks/tools/), and [Docker](https://www.docker.com/) are installed (`kind` is managed automatically via Go). A Linux host that will run more than about a thousand sandboxes also needs a larger kernel neighbor table (`net.ipv4.neigh.default.gc_thresh1/2/3` = 4096/16384/32768); `hack/create-kind-cluster.sh` warns when the host is at the default of 1024. +> **Prerequisites:** Ensure [Go](https://go.dev/doc/install), [`kubectl`](https://kubernetes.io/docs/tasks/tools/), and [Docker](https://www.docker.com/) are installed ([`kind`](https://kind.sigs.k8s.io/) is managed automatically via Go). A Linux host that will run more than about a thousand sandboxes also needs a larger kernel neighbor table (`net.ipv4.neigh.default.gc_thresh1/2/3` = 4096/16384/32768); `hack/create-kind-cluster.sh` warns when the host is at the default of 1024. ## 1. Deploy Agent Substrate & Create an Actor Run the following commands from the repository root to provision a local `kind` cluster, install Agent Substrate, and create a `counter` actor: ```bash -# 1. Create a local Kind cluster and image registry (IPv4; IP_FAMILY=dual|ipv6 overrides) +# 1. Create a local kind cluster and image registry (IPv4; IP_FAMILY=dual|ipv6 overrides) hack/create-kind-cluster.sh -# 2. Target the local Kind cluster in every shell that runs ate-setup +# 2. Target the local kind cluster in every shell that runs ate-setup export ATE_INSTALL_KIND=true # 3. Install Agent Substrate, PostgreSQL, and snapshot storage (rustfs) @@ -66,13 +66,13 @@ data: OTEL_METRIC_EXPORT_TIMEOUT: "10000" OTEL_LOGS_EXPORTER: otlp --- -# ActorTemplate snapshotConfig rendered for Kind (local bucket) +# ActorTemplate snapshotConfig from the counter demo snapshotConfig: - onPause: SNAPSHOT_CONTENT_SCOPE_FULL - onCommit: SNAPSHOT_CONTENT_SCOPE_FULL - storageLocation: gs://ate-snapshots/ate-demo-counter/ + preferredFidelity: SNAPSHOT_FIDELITY_MEMORY ``` +The template sets no `storageLocation`, so its snapshots go under ate-api-server's `--snapshot-location`. The kind profile points that at the local `rustfs` bucket, `gs://ate-snapshots/`. + ## 3. Verify Your Running Actor With `kubectl ate proxy actor` running in your first terminal, open a **second terminal** and send an HTTP `POST` request to wake and increment your actor: diff --git a/docs/guides/authentication.md b/docs/guides/authentication.md index 4fee62633a..53130bf284 100644 --- a/docs/guides/authentication.md +++ b/docs/guides/authentication.md @@ -3,7 +3,7 @@ title: "Authentication" linkTitle: "Authentication" weight: 3 description: > - How clients authenticate to ate-api. + How clients authenticate to ate-api-server over SPIFFE mTLS or OIDC JWTs. --- # ate-api Authentication diff --git a/docs/guides/authorization.md b/docs/guides/authorization.md index 62fde72a62..b90e5cac65 100644 --- a/docs/guides/authorization.md +++ b/docs/guides/authorization.md @@ -3,7 +3,7 @@ title: "Authorization" linkTitle: "Authorization" weight: 4 description: > - How ate-api checks every call against an OpenFGA model, and how to manage roles and access. + How ate-api-server checks every call against an OpenFGA model, and how to manage roles and access. --- # ate-api Authorization @@ -143,7 +143,7 @@ through their pod certificates. `ate-api-server`'s `--controller-spiffe-id`, | Role | Held by | Allows | |---|---|---| | `controller` | `ate-controller` | Create, get, list, update, delete and drain workers | -| `egress_gateway` | `atenet-egress`, and any other SPIFFE IDs passed with `--egress-gateway-spiffe-id`, which ate-setup sets from `ATE_EGRESS_GATEWAY_EXTRA_SPIFFE_IDS` | Get actors and their egress policies; list actors across all atespaces; mint actor JWTs and certificates | +| `egress_gateway` | `atenet-egress`, and any other SPIFFE IDs passed with `--egress-gateway-spiffe-id`, which ate-setup sets from `ATE_EGRESS_GATEWAY_EXTRA_SPIFFE_IDS` | Get actors and their egress policies; list actors across all atespaces; mint actor JWTs | | `ingress_router` | `atenet-router` | Resume actors; list actors across all atespaces | | `atelet` | `atelet` | Get actors' egress policies, for the worker that enforces them | | `api_server_sidecar` | the `ate-api-server` pod identity, which its sidecars share, today the objectstore control plugin | Get actors, to derive the snapshot prefixes a per-actor storage credential may cover | @@ -183,7 +183,7 @@ viewer, and a global role holds the same role in every atespace. So | `GetActorTemplate` | `can_get`, the template | Viewer of its atespace | | `DeleteActorTemplate` | `can_delete`, the template | Editor of its atespace | | `ListActorTemplates` | `can_list_actor_templates`, the atespace, or global if none is given | Atespace viewer; global viewer for all atespaces | -| `CreateActor` | 1. `can_create_actor`, the actor's atespace
2. `can_use_template`, the actor template
3. `can_use_tag`, the `sourceTag`, if one is given | 1. Atespace editor
2. Viewer of the template's atespace, which may be another one
3. Editor of the tag's atespace. Golden tags skip this check: the handler only accepts one built from the actor's own template. | +| `CreateActor` | 1. `can_create_actor`, the actor's atespace
2. `can_use_template`, the actor template
3. `can_use_tag`, the `sourceTag`, if one is given | 1. Atespace editor
2. Viewer of the template's atespace, which may be another one
3. Editor of the tag's atespace. When `sourceTag` is omitted, the handler resolves the template's golden tag without checking `can_use_tag`. | | `UpdateActor` | 1. `can_update`, the actor
2. `can_use_template`, the template in the request | 1. Editor of its atespace
2. Viewer of the template's atespace, so an actor can't be switched to a template its editor can't use | | `GetActor` | `can_get`, the actor | Viewer of its atespace; `atenet-egress`; the objectstore control plugin | | `DeleteActor` | `can_delete`, the actor | Editor of its atespace | @@ -192,7 +192,7 @@ viewer, and a global role holds the same role in every atespace. So | `ResumeActor` | `can_resume`, the actor | Editor of its atespace; `atenet-router` | | `GetActorEgressPolicy` | `can_get_egress_policy`, the actor | Viewer of its atespace; `atenet-egress`; `atelet` | | `CreateActorEgressPolicy`, `UpdateActorEgressPolicy`, `DeleteActorEgressPolicy` | `can_create_egress_policy` / `can_update_egress_policy` / `can_delete_egress_policy`, the actor | Editor of its atespace | -| `MintActorJwt`, `MintActorCertificate` | `can_mint_actor_jwt` / `can_mint_actor_certificate`, the actor | `atenet-egress` only | +| `MintActorJwt` | `can_mint_actor_jwt`, the actor | `atenet-egress` only | ### Tags diff --git a/docs/guides/benchmarking.md b/docs/guides/benchmarking.md index 2e290a0279..178ee6c5c4 100644 --- a/docs/guides/benchmarking.md +++ b/docs/guides/benchmarking.md @@ -389,7 +389,8 @@ deployment fails loudly instead of showing up as OOM-flaky steps. * `--lifecycle-mode sync|async|none` — synchronous suspend (`sync`, uploads the snapshot before returning), asynchronous suspend (`async`, default, uploads the snapshot in the background), or `none` to keep actors - resident in memory between steps without hibernating. + resident in memory between steps without hibernating. A resident session + runs step 1 as soon as its actor serves, before step 1's think gap. * `--agentsession-snapshot-steps` — comma-separated 1-based step numbers after which the actor is hibernated (for example, `3,10,20,30,40,50,61`); between all other steps the actor stays resident. @@ -407,6 +408,39 @@ deployment fails loudly instead of showing up as OOM-flaky steps. attempt). Each attempt is its own `ResumeActor` or `WakeFirstTouch` sample; the whole wait lands in `TimeToWake`. +* `--agentsession-awake` — turnover mode when above 0 (default 0, off). Each + session registers by creating its actor, running step 1, and parking it. + After registration is idle for 1 minute, the worker wakes this many sessions + and swaps `--target-rps` idle awake sessions with parked ones each second + (both per worker; tracked in `agentsession_turnover_sessions`). Each woken + session runs its next step immediately (ignoring the script's think times). + Requires a fixed user count, and a stop from the master ends turnover for + that worker process until it restarts. See + [Analyzing a Turnover Run](#analyzing-a-turnover-run) for its results. + +#### Analyzing a Turnover Run + +A turnover run registers its whole fleet before it swaps, so `stats.csv` and +the whole-run rows of `stats.jsonl` would blend the registration into the +turnover. Three things keep them apart: + +* The park each session makes as it registers is recorded as + `SuspendActor_Register` (`SuspendActorAsync_Register` in async mode), so + the `SuspendActor` and `SuspendActorAsync` rows hold only the turnover's + parks. +* The runner finds the end of the fill in boomer's log line + `agentsession: turnover fill done` and adds a `turnover_window` row to + `stats.jsonl` ([`locust/turnover_window.py`](../../benchmarking/locust/turnover_window.py)). + It holds each request's count (`requests_`), failures + (`failures_`) and rate (`rps_`) from there to the end of the run + (`window_start`, `window_end`), read from `stats_history.csv`. Read the swap + rate from `rps_TimeToWake` against `--target-rps`. +* The server telemetry's steady-state window starts at the same point, so the + memory, PSI and snapshot figures cover the turnover only. + +The fill's wakes stay in the `TimeToWake` and `WakeFirstTouch` rows (and +`ResumeActor` in explicit resume mode): there are `--agentsession-awake` of +them, at most 8 at a time. #### Writing an agent-session script @@ -953,7 +987,9 @@ actually did, independent of what the load generator reported. Every distribution reports p50, p90, p95 and p99 over the steady-state window. The steady-state window runs from the first to the last Locust sample at 90% or more of the run's peak user count, so ramp-up and teardown are left out. Under -a step-ladder load shape it covers only the top step. +a step-ladder load shape it covers only the top step. In a turnover run it +starts when the fill ends instead (see +[Analyzing a Turnover Run](#analyzing-a-turnover-run)). Counts, means and `checkpoint_mb_s` come from the growth of the histogram's sum and count across the window, and the size, restore and checkpoint percentiles are interpolated inside its buckets. PSI is a one-minute rolling average read diff --git a/docs/guides/csi-deployment.md b/docs/guides/csi-deployment.md index d8ae238a1a..c9367ef751 100644 --- a/docs/guides/csi-deployment.md +++ b/docs/guides/csi-deployment.md @@ -307,7 +307,7 @@ The CSI Node plugin runs as a DaemonSet on each worker node and handles `NodeSta ### Mount Propagation Requirements -When the CSI Node plugin mounts an external volume (such as an NFS share or a formatted block device), the filesystem mount is created inside the plugin container. For Substrate's node supervisor (`atelet`) and worker sandboxes (`ateworker-gvisor`) on the host to see this filesystem mount, the following requirements must be met: +When the CSI Node plugin mounts an external volume (such as an NFS share or a formatted block device), the filesystem mount is created inside the plugin container. For Substrate's node supervisor (`atelet`) and worker sandboxes (`ateworker-gvisor`, `ateworker-microvm`) on the host to see this filesystem mount, the following requirements must be met: 1. **Bidirectional Mount Propagation (`mountPropagation: Bidirectional`):** The volume mount on the host Substrate target directory (e.g. `/var/lib/ate`) in the CSI Node plugin container must have `mountPropagation: Bidirectional`. This ensures that any mounts made by the CSI plugin inside the container propagate back to the host filesystem. 2. **Unix Domain Socket Accessibility:** The CSI Node plugin must place its Unix domain socket under `/var/lib/kubelet/plugins//` (or the path defined in `CSIDriverConfig.spec.nodeSocketOverride`), which is shared with `atelet`. diff --git a/docs/guides/observability.md b/docs/guides/observability.md index cd3cd08a81..911465189a 100644 --- a/docs/guides/observability.md +++ b/docs/guides/observability.md @@ -290,7 +290,7 @@ Agent Substrate emits foundational OpenTelemetry system and server metrics to mo | `atelet.snapshot.size` | atelet | histogram | uncompressed allocated size in bytes of each snapshot image written during checkpoint (`st_blocks * 512`, excluding sparse holes in microVM `memory-ranges`) (labels `file.name`, `ate.template.atespace`, `ate.template.name`) | | `ate.workerpool.desired_workers` | atecontroller | up/down counter | number of worker pods requested for a WorkerPool, from `spec.replicas` (labels `ate.workerpool.namespace`, `ate.workerpool.name`) | | `ate.workerpool.ready_workers` | atecontroller | up/down counter | number of worker pods currently ready for a WorkerPool, from `status.readyReplicas` (labels `ate.workerpool.namespace`, `ate.workerpool.name`) | -| `ate.workerpool.workers` | ateapi | up/down counter | live worker count per pool, split by state (`idle`/`partial`/`at_capacity`/`unschedulable`) and sandbox class to provide fleet capacity and saturation at a glance. Every ateapi replica reports the whole fleet: take the `max` by pool namespace, pool, state, and sandbox class first, then `sum` (example query below the table) | +| `ate.workerpool.workers` | ateapi | up/down counter | live worker count per pool, split by state (`idle`/`partial`/`at_capacity`/`unschedulable`) and sandbox class to provide fleet capacity and saturation at a glance. Every ateapi replica reports the whole fleet: take the `avg` by pool namespace, pool, state, and sandbox class first, then `sum` (example query below the table) | | `ate.actor.lifecycle.operation.duration` | ateapi | histogram | how long each actor operation (create/resume/suspend/delete/revert) takes and whether it failed (`error.type` present = failure, absent = success); labeled by operation, template, pool (`ate.workerpool.namespace` + `ate.workerpool.name`), sandbox class, snapshot kind on resume, and snapshot fidelity on resume and suspend; already-running resume no-ops are not recorded so the histogram tracks actual activations, not router traffic | | `ate.scheduler.assignment.duration` | ateapi | histogram | time of one attempt to assign a worker to an actor, with one record for each resume (ateapi retries an attempt that loses a race for a worker and records only the last attempt), with the outcome (`assigned` / `no_capacity` / `error`), the assigned pool (`ate.workerpool.namespace` + `ate.workerpool.name`) and sandbox class to catch scheduling latency and capacity starvation problems | | `ate.actor.restore.duration` | atelet | histogram | how long each phase of a restore takes on the worker node, which is where cold-start latency actually goes once ateapi hands off (labels `ate.snapshot.phase`, `ate.snapshot.kind`, `ate.snapshot.fidelity`, `ate.template.atespace`, `ate.template.name`, `ate.sandbox.class`) | @@ -313,11 +313,11 @@ For `ate.workerpool.desired_workers` and `ate.workerpool.ready_workers`: * **Autoscaling Control Loop & Anti-Windup**: `desired - ready > 0` sustained beyond a few minutes indicates undelivered capacity due to node pool exhaustion, quota limits, or stuck worker pods, serving as anti-windup input for demand-reactive capacity scaling. For `ate.workerpool.workers`: -* Every ateapi replica reports the whole fleet, so a plain `sum` counts each worker once per replica ([#3055](https://github.com/agent-substrate/substrate/issues/3055)). Take the `max` across replicas first, keeping the pool namespace, pool, state, and sandbox class, then `sum`. Workers per pool: +* Every ateapi replica reports the whole fleet, so a plain `sum` counts each worker once per replica ([#3055](https://github.com/agent-substrate/substrate/issues/3055)). The replicas read separate worker caches and can disagree for a few seconds: a worker that changed state sits under its old state on one replica and its new state on the other ([#3162](https://github.com/agent-substrate/substrate/issues/3162)). Take the `avg` across replicas first, keeping the pool namespace, pool, state, and sandbox class, then `sum`. A `max` would keep that worker under both states and the summed pool outgrows its real size. The average keeps the sum equal to the pool size; the counts can be fractional while the replicas disagree. Workers per pool: ```promql sum by ("ate.workerpool.namespace", "ate.workerpool.name") ( - max by ("ate.workerpool.namespace", "ate.workerpool.name", "ate.worker.state", "ate.sandbox.class") ( + avg by ("ate.workerpool.namespace", "ate.workerpool.name", "ate.worker.state", "ate.sandbox.class") ( {"ate.workerpool.workers"} ) ) diff --git a/docs/guides/operator-install.md b/docs/guides/operator-install.md index d04bb8d80a..15b2c62d3e 100644 --- a/docs/guides/operator-install.md +++ b/docs/guides/operator-install.md @@ -332,6 +332,8 @@ demos, the [benchmarks](benchmarking.md#settings), and the ate-api-server stores every actor and tag snapshot under a cluster-wide `--snapshot-location`, an object-storage URI such as `gs://BUCKET/PREFIX/`. +Its path segments may contain only ASCII letters, digits, `_` and `-` (see +[Snapshot Storage Layout](../reference/api-guide.md#snapshot-storage-layout)). Set it with `SNAPSHOT_LOCATION` (or ate-setup's `--snapshot-location`). It has no default, and ate-setup fails before deploying without it unless the `PerTemplateSnapshotLocation` preview gate is enabled. Even with the gate, the diff --git a/docs/kubernetes-credential-provider.md b/docs/kubernetes-credential-provider.md index 1a8e494b97..fb89156501 100644 --- a/docs/kubernetes-credential-provider.md +++ b/docs/kubernetes-credential-provider.md @@ -1,3 +1,7 @@ +--- +docsite: false +--- + # Kubernetes credential provider The `k8s-credential-provider` Deployment follows the provider from @@ -62,13 +66,13 @@ For the manifest installer, set your grants in `manifests/egress-credential-injection/namespace-policy.yaml`, then deploy: ```sh -kubectl kustomize manifests/egress-credential-injection | ko apply -f - +kubectl apply -f manifests/egress-credential-injection/namespace-policy.yaml go run ./cmd/ate-setup deploy atenet \ - --atenet-dataplane=agentgateway --credential-provider='{"enabled":false}' + --atenet-dataplane=agentgateway --credential-provider='{"name":"k8s.io"}' ``` -The provider is deployed separately above, so the installer's provider selection -is disabled. The fork's agentgateway manifest already points at that provider. +The installer deploys the provider and configures agentgateway to use it. +It preserves the existing namespace policy ConfigMap when redeploying. Set grants under `data.namespace-policy.yaml` in the policy ConfigMap, using `policies:` with the same list as the Helm values. After applying policy changes, restart the provider with `kubectl rollout restart -n ate-system diff --git a/docs/metrics/registry/metrics.yaml b/docs/metrics/registry/metrics.yaml index 41f9171fdf..6ff75f258c 100644 --- a/docs/metrics/registry/metrics.yaml +++ b/docs/metrics/registry/metrics.yaml @@ -846,13 +846,18 @@ groups: brief: The number of workers in each pool, divided by state and by sandbox class. note: > ateapi reads this value with a callback. Each ateapi replica reports the - whole fleet, and the default install runs two replicas. Thus a sum across - the replicas counts each worker once for each replica. Take the maximum - across the replicas first, by pool namespace, pool, state and sandbox - class, and add after that. Within one replica you can add the counts - together. The sum across the states is the size of the pool. The sum - across the pools is the size of the fleet. Thus this instrument is an - UpDownCounter + whole fleet from its own worker cache, and the default install runs two + replicas. Thus a sum across the replicas counts each worker once for + each replica. The caches can disagree for a few seconds, and a worker + that changed state then sits under its old state on one replica and + under its new state on the other. Take the average across the replicas + first, by pool namespace, pool, state and sandbox class, and add after + that. A maximum would count that worker twice, once under each state. + The average keeps the sum across the states equal to the size of the + pool, and the counts can be fractional while the replicas disagree. + Within one replica you can add the counts together. The sum across the + states is the size of the pool. The sum across the pools is the size of + the fleet. Thus this instrument is an UpDownCounter and not a gauge. ateapi sets every state to 0 for each known pool. Thus a full pool or an empty pool reports 0. An absent series would stop an alert on idle == 0. diff --git a/docs/reference/api-guide.md b/docs/reference/api-guide.md index e22e77f2c5..764a908b93 100644 --- a/docs/reference/api-guide.md +++ b/docs/reference/api-guide.md @@ -6,7 +6,7 @@ card_icon: "fa-solid fa-code" aliases: - "/docs/guides/api-guide/" description: > - Define WorkerPools and ActorTemplates, the workload blueprints of Agent Substrate. + Define WorkerPools, ActorTemplates, and SandboxConfigs, and call the Control Plane gRPC API. --- # Substrate API Guide: WorkerPool & ActorTemplate @@ -15,14 +15,14 @@ This guide explains how to configure Substrate resources to deploy high-density, ## 1. WorkerPool: The Physical Capacity -The `WorkerPool` defines the pool of physical "warm" compute capacity. It manages a fleet of standby pods (herders) that are ready to receive and execute actor states. +The `WorkerPool` defines the pool of physical "warm" compute capacity. It manages a fleet of standby worker pods that are ready to receive and execute actor states. ### Specification (`WorkerPoolSpec`) | Field | Type | Description | | :--- | :--- | :--- | | `replicas` | `int32` | **Required.** Number of physical standby pods to maintain in the cluster. | -| `workerImage` | `string` | **Required.** The container image for the `ateworker` herder process (e.g. `ko://go.ate.dev/substrate/cmd/ateworker-gvisor`). | +| `workerImage` | `string` | **Required.** The container image for the `ateworker` process (e.g. `ko://go.ate.dev/substrate/cmd/ateworker-gvisor`). | | `sandboxClasses` | `[]WorkerPoolSandboxClass` | **Required.** The sandbox runtime families the pool runs; exactly one entry today. Each entry has `name` (**required**, `gvisor` or `microvm`, immutable), which drives the worker pod shape (e.g. KVM device mounts, node placement), and `configRef.name` (optional), which names a cluster-scoped [`SandboxConfig`](#3-sandboxconfig-the-sandbox-itself) of the same class. Without `configRef`, the pool uses the class default (see [Class default](#class-default)). The controller records the result in `status.sandboxClasses`. The sandbox binaries an actor boots with come from the `SandboxConfig` the pool of its assigned worker resolved. | | `template` | `WorkerPoolPodTemplate` | **Optional.** Metadata, scheduling, and resource settings for worker workloads. | @@ -111,13 +111,13 @@ The `ActorTemplate` defines the code, environment, and state-management policies | Field | Type | Description | | :--- | :--- | :--- | | `containers` | `[]Container` | **Required.** The workload definition — see [Container Fields](#container-fields) below. Each container may also declare an optional `wakeupProbe` HTTP probe — see [Container Wakeup Probe](#container-wakeup-probe-wakeupprobe). | -| `workerSelector` | `*Selector` | Optional. Gates which `WorkerPool`s actors from this template may use, by matching against each pool's labels (`matchLabels`). If unset, all pools are eligible (subject to the actor's own `worker_selector`). The pool decides the sandbox class, so this is also how a template pins one: if your cluster has pools of more than one sandbox class and the workload needs a particular class, make sure the selector points only at pools of that class. An actor booting with no snapshot and no selector may land on any class, and keeps it from its first checkpoint on. | +| `workerSelector` | `*Selector` | Optional. Gates which `WorkerPool`s actors from this template may use, by matching against each pool's labels (`matchLabels`). If unset, all pools are eligible (subject to the actor's own `worker_selector`). The pool sets the sandbox class, so this is also how a template pins one: if your cluster has pools of more than one sandbox class and the workload needs a particular class, make sure the selector points only at pools of that class. An actor booting with no snapshot and no selector may run on any class, and keeps it from its first checkpoint on. An actor created from the golden snapshot or a `sourceTag` runs on that snapshot's class. See [Sandbox Classes](sandbox-classes.md) for what differs between classes. | | `snapshotConfig` | `SnapshotConfig` | Optional; omitted fields take their defaults. The base object-storage location snapshots are written under (a preview field that requires the `PerTemplateSnapshotLocation` gate), the snapshot fidelity (`preferredFidelity`) every Suspend captures, and automated golden snapshot creation (`goldenSnapshotConfig.mode`). See [Snapshot Storage Layout](#snapshot-storage-layout). | -| `volumes` | `[]Volume` | Optional. Volumes the containers may mount, each a `durableDir`, an `external` CSI volume source (see [CSI Volumes Guide](../guides/csi-volumes.md)), or a `systemInfo` volume (see [SystemInfo Volumes](#systeminfo-volumes)). Every declared volume must be mounted by at least one container. A `durableDir` is created `0777`, as Kubernetes creates an emptyDir, so a container running as any user can write it. A snapshot restores it with the mode it had, so the container's own chmod survives suspend and resume. | +| `volumes` | `[]Volume` | Optional. Volumes the containers may mount, each a `durableDir`, an `image` read-only OCI image volume (`image.reference`, pinned by digest), an `external` CSI volume source (preview, behind the `ExternalVolumes` gate; see [CSI Volumes Guide](../guides/csi-volumes.md)), or a `systemInfo` volume (see [SystemInfo Volumes](#systeminfo-volumes)). Every declared volume must be mounted by at least one container. A `durableDir` is created `0777`, as Kubernetes creates an emptyDir, so a container running as any user can write it. A snapshot restores it with the mode it had, so the container's own chmod survives suspend and resume. | | `resources` | `*ResourceRequirements` | Required. Declares each actor's compute size via `limits`, which must set both `cpuMillicores` and `memoryBytes` — see [Sandbox Right-Sizing](#sandbox-right-sizing-resources). Immutable, like the rest of the template. | | `goldenEgressPolicy` | `EgressPolicyTemplate` | **Preview.** Optional egress policy for the golden actor during initialization. Requires at least one rule, using the same rule defaults and validation as `EgressPolicy`. See [Golden Egress Policy](#golden-egress-policy-goldenegresspolicy). | -The sandbox runtime and its binaries (e.g. the gVisor `runsc` binary, or a microVM kernel/firmware/config) are configured on the [`WorkerPool`](#1-workerpool-the-compute-foundation) via its `sandboxClasses` and the cluster-scoped [`SandboxConfig`](#3-sandboxconfig-the-sandbox-itself) it resolves, rather than on the `ActorTemplate`. An `ActorTemplate` selects its `WorkerPool`s via `workerSelector` (AND'd with the actor's `worker_selector`). When an actor runs on a worker, the runtime it runs under is stamped onto the actor's assignment and snapshots (`sandboxRuntime`: the sandbox class and the runtime's compatibility version) so subsequent resumes prefer runtime-compatible workers. +The sandbox runtime and its binaries (e.g. the gVisor `runsc` binary, or a microVM kernel/firmware/config) are configured on the [`WorkerPool`](#1-workerpool-the-physical-capacity) via its `sandboxClasses` and the cluster-scoped [`SandboxConfig`](#3-sandboxconfig-the-sandbox-itself) it resolves, rather than on the `ActorTemplate`. An `ActorTemplate` selects its `WorkerPool`s via `workerSelector` (AND'd with the actor's `worker_selector`). When an actor runs on a worker, the runtime it runs under is stamped onto the actor's assignment and snapshots (`sandboxRuntime`: the sandbox class and the runtime's compatibility version) so subsequent resumes prefer runtime-compatible workers. ### Golden Egress Policy (`goldenEgressPolicy`) @@ -171,11 +171,11 @@ continue to work. Unlike a Pod, an actor is sized by its **`limits`** (CPU and Memory): the size is a property of the template, baked into snapshots, so it lives on the immutable `ActorTemplate`. Both are required. Declared limits do two things: 1. **Size the sandbox.** The limits are supplied to the sandbox over the actor RPCs (control plane → atelet → ateworker): - - **gVisor (`ateworker-gvisor`)** — applied to the container OCI spec: `limits.cpuMillicores` sets the cgroup v2 CPU quota (`cpu.max`) and the Sentry vCPU count (`--cpu-num-from-quota`); `limits.memoryBytes` caps the sandbox's memory and bounds the virtual total memory it reports (so JVM/Go do not over-allocate from host RAM). When the node's kernel has cgroup memory pressure (PSI) enabled, an actor that stays over its limit is killed and reported crashed, and other actors on its worker keep running. - - **microVM (`ateworker-microvm`)** — `limits.cpuMillicores` sets Cloud Hypervisor `BootVcpus` / `MaxVcpus` (rounded up to whole vCPUs); `limits.memoryBytes` sets guest RAM, all of it. The microVM worker refuses to boot an actor with less than 128 MiB. What the actor costs outside the guest comes out of the worker's capacity instead (see [Worker Capacity](#worker-capacity-spectemplateresources)). When the node's kernel has cgroup memory pressure (PSI) enabled, an actor whose VMM and virtiofsd stay over what it books is killed and reported crashed, and other actors on its worker keep running. -2. **Gate scheduling.** An actor is only placed on a `WorkerPool` whose [worker capacity](#worker-capacity-spectemplateresources) is `>=` these limits, plus its runtime's overhead on that worker. + - **gVisor (`ateworker-gvisor`):** The limits go into the container OCI spec. `limits.cpuMillicores` sets the cgroup v2 CPU quota (`cpu.max`) and the CPU count the Sentry reports (`--cpu-num-from-quota`, at least 2; see [CPU Count and Throttling](sandbox-classes.md#cpu-count-and-throttling)). `limits.memoryBytes` caps the sandbox's memory and bounds the virtual total memory it reports (so JVM/Go do not over-allocate from host RAM). When the node's kernel has cgroup memory pressure (PSI) enabled, an actor that stays over its limit is killed and reported crashed, and other actors on its worker keep running. + - **microVM (`ateworker-microvm`):** `limits.cpuMillicores` sets Cloud Hypervisor `BootVcpus` / `MaxVcpus` (rounded up to whole vCPUs, minimum 1), and the host caps the VMM and `virtiofsd` at the same limit with cgroup v2 `cpu.max`. `limits.memoryBytes` sets guest RAM, all of it. The microVM worker refuses to boot an actor with less than 128 MiB. What the actor costs outside the guest comes out of the worker's capacity instead (see [Worker Capacity](#worker-capacity-spectemplateresources)). When the node's kernel has cgroup memory pressure (PSI) enabled, an actor whose VMM and virtiofsd stay over what it books is killed and reported crashed, and other actors on its worker keep running. +2. **Gate scheduling.** An actor is only placed on a worker whose remaining [capacity](#worker-capacity-spectemplateresources) is `>=` these limits, plus its runtime's overhead on that worker. -`requests` are not consulted today (an actor occupies its whole worker). Because the size is baked into snapshots, a **microVM MEMORY-fidelity restore reuses the size in the snapshot**; changing an actor's limits takes effect on its next cold boot. +There is no `requests` field. To change an actor's limits, point the suspended actor at a new template with `UpdateActor`. Its next resume restores its `VOLUMES` data and cold-boots. Container environment variables support literal `value` entries only. Values are not interpolated (`$(VAR)` references are not expanded), and Kubernetes `envFrom`/`valueFrom` sources are not supported. @@ -305,14 +305,20 @@ Each entry in `containers` describes one process to run in the actor's sandbox. | `wakeupProbe` | `WakeupProbe` | Optional. HTTP wakeup probe — see [Container Wakeup Probe](#container-wakeup-probe-wakeupprobe). | | `volumeMounts` | `[]VolumeMount` | Optional. Mounts a `volumes` entry (e.g. `durableDir`) into this container. | | `securityContext` | `SecurityContext` | Optional. Security settings for the container process — see [Container Capabilities](#container-capabilities-securitycontextcapabilities). | -| `resources` | `ContainerResources` | Optional. Compute limits for this container, enforced inside the actor's sandbox. Only `limits` is supported, and only `cpu` and `memory`. See [Per-container limits](#per-container-limits). | +| `resources` | `ResourceRequirements` | Optional. Compute limits for this container (`limits.cpuMillicores` and `limits.memoryBytes`), enforced inside the actor's sandbox. See [Per-container limits](#per-container-limits). | `command` and `args` resolve against the container image's `ENTRYPOINT`/`CMD` the same way [Kubernetes Pod `command`/`args`](https://kubernetes.io/docs/tasks/inject-data-application/define-command-argument-container/) resolve against `ENTRYPOINT`/`CMD`. If the resolved argv is empty — the image sets neither `ENTRYPOINT` nor `CMD`, and the container sets neither `command` nor `args` — `Run`/`Restore` fails. +The process runs as the image's `USER`, resolved as Docker does against the image's own `/etc/passwd` and `/etc/group`: a numeric `uid:gid` as is; a bare uid with its login group, or gid 0 when it has no entry; a name must have an entry or `Run`/`Restore` fails. Group memberships become supplementary groups. Ids are at most 2147483647. No `USER` means root. There is no `runAsUser` field. + +The process starts in the image's `WORKDIR`, or in `/` when the image sets none. A `WORKDIR` the image lacks is created in the actor's writable layer. There is no `workingDir` field. + ### Container Capabilities (`securityContext.capabilities`) Each container runs with a default set of Linux capabilities — `AUDIT_WRITE`, `KILL` and `NET_BIND_SERVICE`. `securityContext.capabilities` adjusts that set, mirroring `securityContext.capabilities` on a Kubernetes Pod container. +A container running as root has that set as its effective capabilities. A container whose image `USER` is not root starts with no effective capabilities, as under Kubernetes, because ambient capabilities are not supported. Its set then only limits what a setuid-root program can gain. + | Field | Type | Description | | :--- | :--- | :--- | | `securityContext.capabilities.add` | `[]string` | Optional. Capabilities to grant on top of the default set. `ALL` is **not** accepted here. | @@ -331,8 +337,10 @@ Each container runs with a default set of Linux capabilities — `AUDIT_WRITE`, - **`ALL` in `add` is rejected.** Kubernetes accepts it in the API and relies on PodSecurity admission to deny it; Substrate has no equivalent policy layer yet, so it is refused at admission instead. Name the capabilities the container needs. - **Ambient capabilities are not supported** ([gvisor#3166](https://github.com/google/gvisor/issues/3166)). -- **Low ports.** A container may bind a port below 1024 without `NET_BIND_SERVICE` on both runtimes: gVisor's network stack has no privileged-port limit, and the micro-VM guest sets `net.ipv4.ip_unprivileged_port_start=0` to match. +- **Low ports.** A container may bind a port below 1024 without `NET_BIND_SERVICE` on both runtimes: gVisor's network stack has no privileged-port limit, and the microVM guest sets `net.ipv4.ip_unprivileged_port_start=0` to match. +- **`NET_RAW`.** gVisor removes it even when `add` lists it, and microVM grants it. See [Capabilities (`NET_RAW`)](sandbox-classes.md#capabilities-net_raw). - **Setuid binaries work.** Neither runtime sets `no_new_privileges`, as for a Kubernetes container with `allowPrivilegeEscalation: true`; gVisor runs with `--allow-suid`. +- **Image files owned by other users.** Image files keep the owner their image's layers record, as under containerd. A container running as its image's `USER` can use what the image gave that user, for example after `RUN chown -R app:app /app` and `USER app`. A container running as root, because its image sets no `USER` or `USER root`, has no `DAC_OVERRIDE` or `DAC_READ_SEARCH` by default. On a file or directory another user owns, it gets only what the permission bits grant to others. Add `DAC_OVERRIDE` if it writes to such files, or `DAC_READ_SEARCH` if it only reads them. The sandbox — gVisor or microVM — remains the isolation boundary; capabilities constrain the workload *inside* it. @@ -344,13 +352,13 @@ A container may cap its own CPU and memory so it cannot starve or kill its sibli containers: - name: trainer resources: - limits: {memory: 1500Mi} + limits: {memoryBytes: 1572864000} # 1500Mi - name: sidecar resources: - limits: {memory: 256Mi, cpu: "0.2"} + limits: {memoryBytes: 268435456, cpuMillicores: 200} # 256Mi, 0.2 CPU ``` -A container that exceeds its memory limit is OOM-killed on its own; the actor's other containers are unaffected. A `cpu` limit below `10m` is raised to `10m`, because the kernel rejects a CFS quota under 1ms. +A container that exceeds its memory limit is OOM-killed on its own; the actor's other containers are unaffected. A `cpuMillicores` limit below 10 is raised to 10, because the kernel rejects a CFS quota under 1ms. Per-container limits are microVM only today. gVisor applies cgroup limits at the sandbox level: one sentry backs every container in the actor, so a per-container cgroup is created and then stays empty ([google/gvisor#190](https://github.com/google/gvisor/issues/190)). On a gVisor worker the per-container limits are accepted but not enforced; only the sandbox-level size applies. @@ -434,6 +442,10 @@ records its snapshot's URI in `status.snapshot` right after it is reserved, before anything is copied, so a tag whose creation failed still names any partial copy to collect. +Every owner's prefix starts with `/atespaces/`. So where a location is set, through `--snapshot-location` or a template's `storageLocation`, each path segment may contain only ASCII letters, digits, `_` and `-`, and no segment may be `atespaces` in any letter case. The bucket is not restricted. A location such as `gs://my-bucket/atespaces/team-a/actors/` would otherwise put one owner's objects under another owner's or atespace's prefix, inside its grants and its cleanup. + +ate-api-server refuses to start with a `--snapshot-location` that breaks these rules, `ate-setup deploy` rejects it, and creating a template rejects such a `storageLocation`. Creating a tag checks the location the tag is stored under and fails with `FAILED_PRECONDITION` on one that breaks them. + An owner is collected by deleting everything under its prefix, and it can delete nothing else. That is what makes a borrowed snapshot safe: an actor created from a tag points at a URI under `tags/`, which its own prefix does not cover. See [Snapshot lifetime](#snapshot-lifetime). An `Actor` reports its snapshots in the server-managed `status.snapshots` (and @@ -455,7 +467,7 @@ a `Snapshot` with the following fields: - `locality`: The node holding the snapshot's local files. For attachment rules, see `ActorStatus.assigned_node` in the [API schema](../../api/ateapi/v1/ateapi.proto). - `durableSnapshot`: The copy in object storage, with its URI in - `object.snapshotUri` and a `status` that is `PENDING` while the copy is being + `object.snapshotUri` and a `state` that is `PENDING` while the copy is being written and `COMPLETED` once it can be restored from. A synchronous suspend uploads the copy before returning (`COMPLETED`); an asynchronous suspend records the copy as `PENDING`, and atelet uploads it in the background. A @@ -488,7 +500,11 @@ a `Snapshot` with the following fields: case. A snapshot without `sandboxRuntime` or with `SNAPSHOT_FIDELITY_ROOTFS` or - `SNAPSHOT_FIDELITY_VOLUMES` imposes no runtime placement constraint. Because a + `SNAPSHOT_FIDELITY_VOLUMES` imposes no runtime-version placement constraint. + On gVisor, such a restore runs under the worker's default `runsc`, which may + not be the version that wrote its files (see the + [Version Skew Policy](version-skew-policy.md#sandboxconfig)). A snapshot that + records a sandbox class always restores on a worker of that class. Because a `ROOTFS` restore reuses the container's rootfs upper layer over the template's image, pools selected by an `ActorTemplate` must share a CPU architecture. @@ -660,7 +676,7 @@ The node can cut the 30 minutes short. GKE node upgrades and cluster autoscaler ## 5. Best Practices * **Startup Logic:** Place expensive initialization (loading large models, establishing baseline connections) in your application's entry point. When golden snapshots are enabled, this initialization can be captured in the Golden Snapshot. Actors that cold-boot run initialization again. -* **Placement:** The `WorkerPool` decides the sandbox class (`sandboxClasses[].name`), so use the template's `workerSelector` to target the pools whose class your workload needs — pool selection is by label match, not by namespace or RBAC. Without a selector an actor may land on any pool. +* **Placement:** The `WorkerPool` sets the sandbox class (`sandboxClasses[].name`). Use the template's `workerSelector` to target the pools whose class your workload needs (see [Sandbox Classes](sandbox-classes.md)). Pool selection matches pool labels only. A pool's namespace and RBAC do not limit which templates can select it. Without a selector an actor may run on any pool. * **Version Management:** When updating code, create a new `ActorTemplate` (e.g. `v2`). Substrate treats each template as an immutable state root. Suspended actors can be repointed to the new template via `UpdateActor` (`kubectl ate update actor --template`). * **Eviction:** Give every actor a `SIGTERM` handler that does not exit, and have the actor suspended within 30 minutes of it. See [Eviction](#eviction). @@ -676,7 +692,7 @@ The Substrate Control Plane (`ate-api-server`) exposes a gRPC interface for mana Registers a new logical actor in the system. * **Request:** `CreateActorRequest` * `actor`: `Actor` — the actor to create. Its `metadata` carries the atespace and name (name must be a DNS-1123 label); the `actor_template` ref (atespace + name) selects the `ActorTemplate`. - * `actor.source_tag`: (Optional) `ObjectRef` of a `Tag` to seed the actor from. The tag must be taken under the same `ActorTemplate`, and either in the actor's own atespace or `PUBLISHED`. Nothing is copied: the new actor's `status.snapshots` points at the tag's snapshot, under the tag's prefix, until its own first suspend. + * `actor.source_tag`: (Optional) `ObjectRef` of a `Tag` to seed the actor from. The tag must be taken under the same `ActorTemplate`, must not be marked for deletion, and must be either in the actor's own atespace or `PUBLISHED`. Nothing is copied: the new actor's `status.snapshots` points at the tag's snapshot, under the tag's prefix, until its own first suspend. * `actor.worker_selector`: (Optional) Per-actor `WorkerSelector` merged with the template's `worker_selector` at resume time (actor keys override template keys). * **Response:** the initialized `Actor`. @@ -688,7 +704,7 @@ Replaces the mutable fields of an existing actor with the ones in the request (` * `worker_selector` may be updated or cleared (`nil`) in any state, but not while another operation is changing the actor. The updated selector takes effect on the next `ResumeActor`. * A suspended actor with a local snapshot is initially pinned to its node (see `locality` in [Snapshot Storage Layout](#snapshot-storage-layout)). `ResumeActor` first looks for a worker there matching the template and actor selectors. If none is available but a worker satisfies the remaining constraints without node or runtime restrictions, resume detaches the checkpoint and retries placement. If no eligible worker has room, it returns `RESOURCE_EXHAUSTED`. * **Response:** the updated `Actor`. -* **Errors:** `INVALID_ARGUMENT` if `uid` or `version` is unset, or if the request changes an immutable field — including by leaving one unset; `FAILED_PRECONDITION` if a change to `actor_template` breaks one of the rules above; `ABORTED` if either guard no longer matches the stored resource, or while another operation is changing the actor (retry the same request). +* **Errors:** `INVALID_ARGUMENT` if `uid` or `version` is unset, or if the request changes an immutable field (including by leaving one unset). `FAILED_PRECONDITION` if the actor is marked for deletion or if a change to `actor_template` breaks one of the rules above. `ABORTED` if either guard no longer matches the stored resource, or while another operation is changing the actor (retry the same request). Because the guards are required and only a read supplies them, an update is always a read-modify-write. To Update an `Actor`, you must first `GetActor`/`CreateActor`, instead of building a new one — see [§7.2 of the API style guide](../api-style-guide.md#72-using-version-and-uid-to-guard-writes) for why reconstructing the message can silently drop data. @@ -745,10 +761,10 @@ Query the state of logical actors. * **GetActor:** Retrieves a single actor by ID. * **ListActors:** Lists all actors currently tracked in the database. -#### `ListWorkers` +#### `GetWorker` / `ListWorkers` / `ListWorkerActorAssignments` Query the physical resource pool. -* **Request:** `ListWorkersRequest` -* **Response:** `ListWorkersResponse` containing a list of `Worker` objects (Pods) and their current assignment status. +* **GetWorker / ListWorkers:** Retrieve one or more `Worker` records, including each worker's `status.state`, `status.capacity`, `status.allocated`, and `status.sandbox_classes`. +* **ListWorkerActorAssignments:** Lists the `ActorAssignment` subresources for the actors currently hosted on a worker. --- @@ -756,7 +772,7 @@ Query the physical resource pool. ate-api-server mints credentials that identify an actor to services outside Substrate. They name the logical actor, so they stay the same as the actor moves between workers. This is distinct from the `actorMetadata` data source described under [SystemInfo Volumes](#systeminfo-volumes), which only tells an actor its own identity fields (name, atespace, uid). -Both RPCs are on `substrate.ateapi.v1.Control`. Only the egress gateway may call them; see [authorization.md](../guides/authorization.md). It calls `MintActorJwt` when an egress policy injects an actor JWT into a request. +`MintActorJwt` is on `substrate.ateapi.v1.Control`. Only the egress gateway may call it; see [authorization.md](../guides/authorization.md). It calls this RPC when an egress policy injects an actor JWT into a request. ### `MintActorJwt` @@ -774,13 +790,6 @@ The token's header carries `typ: JWT`, the algorithm of the pool's active signin Relying parties find the signing keys through OpenID Connect Discovery at `/.well-known/openid-configuration`, which ate-idp-server serves in every install. -### `MintActorCertificate` - -* **Request:** `actor`; `actor_uid`, which must match the actor's current UID, so that a deleted and recreated actor isn't mistaken for the old one (`ABORTED` otherwise); and `certificate_signing_request`, a DER-encoded CSR. The signer checks the CSR's signature and uses only its public key. -* **Response:** `actor_certificates`, DER certificates with the leaf first, followed by any intermediates. - -The caller must connect with a client certificate; a bearer token is not enough. The leaf is valid for one hour, allows client and server authentication, carries the SPIFFE URI `spiffe://substrate-actor.local/actor//`, and has the `ActorIdentity` X.509 extension with the actor's atespace, name, and UID. - --- ## 8. Framework & Ecosystem Integration diff --git a/docs/reference/glossary.md b/docs/reference/glossary.md index c6ab5b604b..0a2f398889 100644 --- a/docs/reference/glossary.md +++ b/docs/reference/glossary.md @@ -22,7 +22,7 @@ For how the pieces fit together, see the [Architecture](../concepts/architecture overall platform and runtime, while **`ate`** (originally *Agent Task / Execution Engine*) is the internal prefix used across Kubernetes namespaces (`ate-system`), control-plane binaries (`ate-api-server`, `atecontroller`), - node and pod daemons (`atelet`, `ateom`), networking components (`atenet`, + node and pod daemons (`atelet`, `ateworker`), networking components (`atenet`, `atunnel`), HTTP routing headers (`ate-target-actor`), and the `kubectl-ate` CLI plugin. @@ -45,10 +45,10 @@ For how the pieces fit together, see the [Architecture](../concepts/architecture - **SandboxConfig** (Kubernetes CRD): a cluster-scoped resource holding the sandbox binaries for one runtime family (the gVisor `runsc` binary, or a - microVM kernel/firmware/config). An actor resolves its sandbox at first cold - boot from the config its `ActorTemplate` names (naming one is currently - required; per-class cluster defaults are planned), so one config pins the - runtime version for many templates. + microVM kernel/firmware/config). Each `WorkerPool` resolves a `SandboxConfig` + for its sandbox class, either by explicit `configRef` or via the class + default. A new or cold-booted actor gets the config's default version. A + memory restore gets the version its snapshot was taken with. ## Records (dynamic state, in the control-plane store) @@ -58,18 +58,24 @@ for etcd. - **Atespace**: the isolation boundary an Actor belongs to, and the first half of its identity: an Actor is addressed by `(atespace, name)`, so the same name can exist in two atespaces. Atespaces are global-scoped, not Kubernetes - namespaces — an `ActorTemplate` also lives in an atespace, while + namespaces. `ActorTemplate`s and `Tag`s also live in an atespace, while `WorkerPool`s live in Kubernetes namespaces. One must exist before any - Actor can be created in it, and it can only be deleted once empty. + resource can be created in it, and it can only be deleted once its actors, + actor templates, and tags are gone. - **Actor**: a single instance derived from an `ActorTemplate`, identified by a DNS-1123 name. It is the unit that is suspended and resumed, and it moves between workers over its lifetime. An Actor record tracks its lifecycle status and snapshot references. +- **Tag**: a durable, atespace-scoped snapshot record created from a suspended + Actor (or by `ate-api-server` for a template's golden snapshot). Each Tag + holds its own copy of the snapshot in object storage and can be scoped to its + own atespace or published across atespaces. + - **Worker**: a record representing one worker pod in a `WorkerPool`. A Worker hosts several Actors at once, each in its own sandbox, up to its actor limit - and its compute capacity; many more are multiplexed across a pool over time. + and its compute capacity. Many more are multiplexed across a pool over time. ## Components @@ -153,8 +159,8 @@ for etcd. `ActorTemplate` is created, from a temporary "golden" boot of the workload. Templates with golden snapshot creation disabled do not create one. By default an Actor of that template is first restored from this shared - snapshot when it is available; otherwise it cold-boots. It is always a - `MEMORY` capture. + snapshot when it is available, and otherwise cold-boots. It is captured at + the template's `preferredFidelity` (`MEMORY` by default). - **Last Snapshot**: the most recent per-Actor snapshot, written on Suspend and used to restore that specific Actor on the next Resume. diff --git a/docs/reference/kubectl-ate.md b/docs/reference/kubectl-ate.md index 67dd5dff92..ae6d377316 100644 --- a/docs/reference/kubectl-ate.md +++ b/docs/reference/kubectl-ate.md @@ -59,6 +59,7 @@ These flags can be appended to any command: | `--endpoint` | | Manual gRPC endpoint override (e.g., `localhost:8080`) | | | `--token-file` | | Path to a bearer token for ate-api authentication, or `-` for stdin | Kubernetes ServiceAccount token | | `--output` | `-o` | Output format (`table`, `json`, `yaml`) | `table` | +| `--preview` | | Allow the use of preview features enabled on the server | `false` | | `--trace` | | Enable on-demand tracing for the request | `false` | | `--verbose` | `-v` | Increase output verbosity; may be repeated or stacked (e.g., `-v`, `-vv`, `-vvvvv`) | `0` | @@ -82,14 +83,19 @@ kubectl ate get actors -A # Get a specific actor by name and output as raw YAML kubectl ate get actor --atespace -o yaml -# List all physical workers and see which actors are assigned to them +# List all physical workers or get specific workers by name kubectl ate get workers +kubectl ate get worker -# Filter workers by Kubernetes namespace, assigned-actor atespace, or -# worker pool labels (same flags as `top workers`) +# Filter workers by Kubernetes namespace, assigned-actor atespace, +# worker pool labels, or sandbox class (same flags as `top workers`) kubectl ate get workers -n kubectl ate get workers -a kubectl ate get workers -l +kubectl ate get workers --sandbox-class + +# Show live CPU and memory usage of worker pods +kubectl ate top workers ``` > **Note:** `get actors` requires either `--atespace ` / `-a ` (one atespace) or `-A`/`--all-atespaces` (all atespaces) — there is no default atespace. Getting a single actor always requires `--atespace`/`-a`, since an actor is addressed by `(atespace, name)`. `-a` (lower-case) scopes to one atespace; `-A` (upper-case) spans all. @@ -104,8 +110,8 @@ kubectl ate get workers -l | `NAME` | The actor's name. User-provided for application actors; UUID for the golden actor that each template materialises while building its golden tag. | | `TEMPLATE` | The `ActorTemplate` the actor references, displayed as `/`. | | `STATE` | Current lifecycle state (`ACTOR_STATE_RESUMING`, `ACTOR_STATE_RUNNING`, `ACTOR_STATE_SUSPENDING`, `ACTOR_STATE_SUSPENDED`, `ACTOR_STATE_CRASHED`, `ACTOR_STATE_DELETING`, `ACTOR_STATE_REVERTING`). | -| `WORKER POD` | The worker pod (namespace/name) currently hosting the actor. Empty while suspended. | -| `WORKER IP` | The pod IP of that worker. Empty while suspended. | +| `WORKER POD` | The worker pod (`/`) currently hosting the actor, or `` while suspended. | +| `WORKER IPS` | Comma-separated pod IPs of that worker. Empty while suspended. | | `VERSION` | Monotonic integer that increments on every state transition (resume / suspend / checkpoint). Useful for distinguishing snapshots. | | `AGE` | Time elapsed since the actor was created. | @@ -113,11 +119,14 @@ kubectl ate get workers -l | Column | Meaning | |---|---| -| `NAMESPACE` | The `WorkerPool` namespace. | +| `NAME` | The worker's name. The control plane assigns it. Treat it as opaque. | | `POOL` | The `WorkerPool` name. | -| `POD` | The worker pod name. | -| `STATUS` | `FREE` (idle, ready to receive an actor) or `ASSIGNED` (currently hosting an actor). | -| `ASSIGNED ACTOR` | If `STATUS=ASSIGNED`, the actor reference `/