diff --git a/benchmarking/workloads/manifests/glutton-storage-template.yaml.tmpl b/benchmarking/workloads/manifests/glutton-storage-template.yaml.tmpl index c64e36e26a..9a699bf502 100644 --- a/benchmarking/workloads/manifests/glutton-storage-template.yaml.tmpl +++ b/benchmarking/workloads/manifests/glutton-storage-template.yaml.tmpl @@ -24,9 +24,10 @@ workerSelector: workload: benchmark-worker volumes: - name: external-storage - externalVolumeTemplate: - capacity: 1Gi - storageClassName: ${STORAGE_CLASS_NAME} + external: + template: + capacity: 1Gi + storageClassName: ${STORAGE_CLASS_NAME} containers: - name: glutton-storage image: ko://go.ate.dev/substrate/cmd/benchmarking/glutton diff --git a/cmd/ate-setup/internal/demos/counter/counter.go b/cmd/ate-setup/internal/demos/counter/counter.go index a424529a2b..ec00f4fcdf 100644 --- a/cmd/ate-setup/internal/demos/counter/counter.go +++ b/cmd/ate-setup/internal/demos/counter/counter.go @@ -40,9 +40,10 @@ func (d *demo) externalVolumeValues(e *steps.Env) map[string]string { "EXTERNAL_VOLUME_MOUNTS": " - name: external-data\n" + " mountPath: /external-data", "EXTERNAL_VOLUMES": "- name: external-data\n" + - " externalVolumeTemplate:\n" + - " capacity: 1Gi\n" + - " storageClassName: " + sc, + " external:\n" + + " template:\n" + + " capacity: 1Gi\n" + + " storageClassName: " + sc, } } diff --git a/cmd/ate-setup/internal/demos/counter/counter_test.go b/cmd/ate-setup/internal/demos/counter/counter_test.go index 05630230c2..2cfdd393b2 100644 --- a/cmd/ate-setup/internal/demos/counter/counter_test.go +++ b/cmd/ate-setup/internal/demos/counter/counter_test.go @@ -69,7 +69,8 @@ func TestExternalVolumeRenders(t *testing.T) { for _, want := range []string{ "--validate-existing-file-path=/external-data/test.txt", "mountPath: /external-data", - "externalVolumeTemplate:", + "external:", + "template:", tc.wantSC, } { if !strings.Contains(string(manifest), want) { diff --git a/cmd/ateapi/internal/apivalidation/actor_template_test.go b/cmd/ateapi/internal/apivalidation/actor_template_test.go index a0e1c883b6..b9a90498f0 100644 --- a/cmd/ateapi/internal/apivalidation/actor_template_test.go +++ b/cmd/ateapi/internal/apivalidation/actor_template_test.go @@ -217,7 +217,7 @@ func TestValidateCreateActorTemplateRequest(t *testing.T) { func TestValidateCreateActorTemplateRequest_PreviewRestricted(t *testing.T) { setPreviewForTest(t, "ALL") req := &ateapipb.CreateActorTemplateRequest{ActorTemplate: validActorTemplate(func(tmpl *ateapipb.ActorTemplate) { - tmpl.Volumes = []*ateapipb.Volume{{Name: "data", ExternalVolumeTemplate: &ateapipb.ExternalVolumeTemplate{Capacity: "10Gi", StorageClassName: "fast-ssd"}}} + tmpl.Volumes = []*ateapipb.Volume{{Name: "data", External: &ateapipb.ExternalVolumeSource{Template: &ateapipb.ExternalVolumeTemplate{Capacity: "10Gi", StorageClassName: "fast-ssd"}}}} })} t.Run("allowed", func(t *testing.T) { @@ -225,7 +225,7 @@ func TestValidateCreateActorTemplateRequest_PreviewRestricted(t *testing.T) { }) t.Run("restricted", func(t *testing.T) { ctx := preview.ContextWithOptOut(context.Background()) - want := field.ErrorList{field.Forbidden(field.NewPath("actor_template", "volumes").Index(0).Child("external_volume_template"), "")} + want := field.ErrorList{field.Forbidden(field.NewPath("actor_template", "volumes").Index(0).Child("external"), "")} assertValidateErr(t, ValidateCreateActorTemplateRequest(ctx, req), want) }) } @@ -832,52 +832,52 @@ func TestValidateActorTemplate(t *testing.T) { }, { name: "valid external volume template", mutate: func(tmpl *ateapipb.ActorTemplate) { - tmpl.Volumes = []*ateapipb.Volume{{Name: "data", ExternalVolumeTemplate: &ateapipb.ExternalVolumeTemplate{Capacity: "10Gi", StorageClassName: "fast-ssd"}}} + tmpl.Volumes = []*ateapipb.Volume{{Name: "data", External: &ateapipb.ExternalVolumeSource{Template: &ateapipb.ExternalVolumeTemplate{Capacity: "10Gi", StorageClassName: "fast-ssd"}}}} }, - want: field.ErrorList{field.Forbidden(field.NewPath("volumes").Index(0).Child("external_volume_template"), "")}, + want: field.ErrorList{field.Forbidden(field.NewPath("volumes").Index(0).Child("external"), "")}, wantWithPreview: map[string]field.ErrorList{"ALL": nil}, }, { name: "external volume template missing capacity", mutate: func(tmpl *ateapipb.ActorTemplate) { - tmpl.Volumes = []*ateapipb.Volume{{Name: "data", ExternalVolumeTemplate: &ateapipb.ExternalVolumeTemplate{StorageClassName: "fast-ssd"}}} + tmpl.Volumes = []*ateapipb.Volume{{Name: "data", External: &ateapipb.ExternalVolumeSource{Template: &ateapipb.ExternalVolumeTemplate{StorageClassName: "fast-ssd"}}}} }, - want: field.ErrorList{field.Forbidden(field.NewPath("volumes").Index(0).Child("external_volume_template"), "")}, - wantWithPreview: map[string]field.ErrorList{"ALL": {field.Required(field.NewPath("volumes").Index(0).Child("external_volume_template", "capacity"), "")}}, + want: field.ErrorList{field.Forbidden(field.NewPath("volumes").Index(0).Child("external"), "")}, + wantWithPreview: map[string]field.ErrorList{"ALL": {field.Required(field.NewPath("volumes").Index(0).Child("external", "template", "capacity"), "")}}, }, { name: "external volume template malformed capacity", mutate: func(tmpl *ateapipb.ActorTemplate) { - tmpl.Volumes = []*ateapipb.Volume{{Name: "data", ExternalVolumeTemplate: &ateapipb.ExternalVolumeTemplate{Capacity: "ten gigs", StorageClassName: "fast-ssd"}}} + tmpl.Volumes = []*ateapipb.Volume{{Name: "data", External: &ateapipb.ExternalVolumeSource{Template: &ateapipb.ExternalVolumeTemplate{Capacity: "ten gigs", StorageClassName: "fast-ssd"}}}} }, - want: field.ErrorList{field.Forbidden(field.NewPath("volumes").Index(0).Child("external_volume_template"), "")}, - wantWithPreview: map[string]field.ErrorList{"ALL": {field.Invalid(field.NewPath("volumes").Index(0).Child("external_volume_template", "capacity"), nil, "")}}, + want: field.ErrorList{field.Forbidden(field.NewPath("volumes").Index(0).Child("external"), "")}, + wantWithPreview: map[string]field.ErrorList{"ALL": {field.Invalid(field.NewPath("volumes").Index(0).Child("external", "template", "capacity"), nil, "")}}, }, { name: "external volume template capacity at the length bound", mutate: func(tmpl *ateapipb.ActorTemplate) { - tmpl.Volumes = []*ateapipb.Volume{{Name: "data", ExternalVolumeTemplate: &ateapipb.ExternalVolumeTemplate{Capacity: strings.Repeat("1", 30) + "Gi", StorageClassName: "fast-ssd"}}} + tmpl.Volumes = []*ateapipb.Volume{{Name: "data", External: &ateapipb.ExternalVolumeSource{Template: &ateapipb.ExternalVolumeTemplate{Capacity: strings.Repeat("1", 30) + "Gi", StorageClassName: "fast-ssd"}}}} }, - want: field.ErrorList{field.Forbidden(field.NewPath("volumes").Index(0).Child("external_volume_template"), "")}, + want: field.ErrorList{field.Forbidden(field.NewPath("volumes").Index(0).Child("external"), "")}, wantWithPreview: map[string]field.ErrorList{"ALL": nil}, }, { name: "external volume template capacity too long", mutate: func(tmpl *ateapipb.ActorTemplate) { - tmpl.Volumes = []*ateapipb.Volume{{Name: "data", ExternalVolumeTemplate: &ateapipb.ExternalVolumeTemplate{Capacity: strings.Repeat("1", 31) + "Gi", StorageClassName: "fast-ssd"}}} + tmpl.Volumes = []*ateapipb.Volume{{Name: "data", External: &ateapipb.ExternalVolumeSource{Template: &ateapipb.ExternalVolumeTemplate{Capacity: strings.Repeat("1", 31) + "Gi", StorageClassName: "fast-ssd"}}}} }, - want: field.ErrorList{field.Forbidden(field.NewPath("volumes").Index(0).Child("external_volume_template"), "")}, - wantWithPreview: map[string]field.ErrorList{"ALL": {field.TooLong(field.NewPath("volumes").Index(0).Child("external_volume_template", "capacity"), nil, 32).WithOrigin("maxLength")}}, + want: field.ErrorList{field.Forbidden(field.NewPath("volumes").Index(0).Child("external"), "")}, + wantWithPreview: map[string]field.ErrorList{"ALL": {field.TooLong(field.NewPath("volumes").Index(0).Child("external", "template", "capacity"), nil, 32).WithOrigin("maxLength")}}, }, { name: "external volume template missing storage_class_name", mutate: func(tmpl *ateapipb.ActorTemplate) { - tmpl.Volumes = []*ateapipb.Volume{{Name: "data", ExternalVolumeTemplate: &ateapipb.ExternalVolumeTemplate{Capacity: "10Gi"}}} + tmpl.Volumes = []*ateapipb.Volume{{Name: "data", External: &ateapipb.ExternalVolumeSource{Template: &ateapipb.ExternalVolumeTemplate{Capacity: "10Gi"}}}} }, - want: field.ErrorList{field.Forbidden(field.NewPath("volumes").Index(0).Child("external_volume_template"), "")}, - wantWithPreview: map[string]field.ErrorList{"ALL": {field.Required(field.NewPath("volumes").Index(0).Child("external_volume_template", "storage_class_name"), "")}}, + want: field.ErrorList{field.Forbidden(field.NewPath("volumes").Index(0).Child("external"), "")}, + wantWithPreview: map[string]field.ErrorList{"ALL": {field.Required(field.NewPath("volumes").Index(0).Child("external", "template", "storage_class_name"), "")}}, }, { name: "external volume template invalid storage_class_name", mutate: func(tmpl *ateapipb.ActorTemplate) { - tmpl.Volumes = []*ateapipb.Volume{{Name: "data", ExternalVolumeTemplate: &ateapipb.ExternalVolumeTemplate{Capacity: "10Gi", StorageClassName: "Fast SSD"}}} + tmpl.Volumes = []*ateapipb.Volume{{Name: "data", External: &ateapipb.ExternalVolumeSource{Template: &ateapipb.ExternalVolumeTemplate{Capacity: "10Gi", StorageClassName: "Fast SSD"}}}} }, - want: field.ErrorList{field.Forbidden(field.NewPath("volumes").Index(0).Child("external_volume_template"), "")}, - wantWithPreview: map[string]field.ErrorList{"ALL": {field.Invalid(field.NewPath("volumes").Index(0).Child("external_volume_template", "storage_class_name"), nil, "").WithOrigin("format=k8s-long-name")}}, + want: field.ErrorList{field.Forbidden(field.NewPath("volumes").Index(0).Child("external"), "")}, + wantWithPreview: map[string]field.ErrorList{"ALL": {field.Invalid(field.NewPath("volumes").Index(0).Child("external", "template", "storage_class_name"), nil, "").WithOrigin("format=k8s-long-name")}}, }, { name: "valid resources", mutate: func(tmpl *ateapipb.ActorTemplate) { diff --git a/cmd/ateapi/internal/apivalidation/shared_volume.go b/cmd/ateapi/internal/apivalidation/shared_volume.go new file mode 100644 index 0000000000..cab51c8abb --- /dev/null +++ b/cmd/ateapi/internal/apivalidation/shared_volume.go @@ -0,0 +1,124 @@ +// 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. + +package apivalidation + +import ( + "context" + "fmt" + + "go.ate.dev/substrate/pkg/proto/ateapipb" + "k8s.io/apimachinery/pkg/api/operation" + "k8s.io/apimachinery/pkg/api/resource" + "k8s.io/apimachinery/pkg/api/validate" + "k8s.io/apimachinery/pkg/util/validation/field" +) + +func ValidateCreateSharedVolumeRequest(ctx context.Context, req *ateapipb.CreateSharedVolumeRequest) field.ErrorList { + op := operation.Operation{Type: operation.Create} + return Validate_CreateSharedVolumeRequest(ctx, op, nil, req, nil) +} + +func ValidateGetSharedVolumeRequest(ctx context.Context, req *ateapipb.GetSharedVolumeRequest) field.ErrorList { + op := operation.Operation{Type: operation.Create} + return Validate_GetSharedVolumeRequest(ctx, op, nil, req, nil) +} + +func ValidateListSharedVolumesRequest(ctx context.Context, req *ateapipb.ListSharedVolumesRequest) field.ErrorList { + op := operation.Operation{Type: operation.Create} + return Validate_ListSharedVolumesRequest(ctx, op, nil, req, nil) +} + +// The request is validated as a create; the result is validated as an update +// once the stored value is known, like UpdateTagRequest. +func ValidateUpdateSharedVolumeRequest(ctx context.Context, req *ateapipb.UpdateSharedVolumeRequest) field.ErrorList { + op := operation.Operation{Type: operation.Create} + return Validate_UpdateSharedVolumeRequest(ctx, op, nil, req, nil) +} + +func ValidateDeleteSharedVolumeRequest(ctx context.Context, req *ateapipb.DeleteSharedVolumeRequest) field.ErrorList { + op := operation.Operation{Type: operation.Create} + return Validate_DeleteSharedVolumeRequest(ctx, op, nil, req, nil) +} + +func ValidateSharedVolumeUpdate(ctx context.Context, fldPath *field.Path, newVal, oldVal *ateapipb.SharedVolume) field.ErrorList { + op := operation.Operation{Type: operation.Update} + return Validate_SharedVolume(ctx, op, fldPath, newVal, oldVal) +} + +func ValidateCustom_CreateSharedVolumeRequest(_ context.Context, _ operation.Operation, p *field.Path, req, _ *ateapipb.CreateSharedVolumeRequest) field.ErrorList { + volume := req.GetSharedVolume() + if volume == nil { + return nil + } + volumePath := p.Child("shared_volume") + storageClassName, volumeID := volume.GetStorageClassName(), volume.GetVolumeId() + + var errs field.ErrorList + // TODO: accept READ_WRITE_MANY once concurrent holders are supported. + if mode := volume.GetAccessMode(); mode != ateapipb.AccessMode_ACCESS_MODE_UNSPECIFIED && mode != ateapipb.AccessMode_ACCESS_MODE_READ_WRITE_ONCE { + errs = append(errs, field.NotSupported(volumePath.Child("access_mode"), mode.String(), []string{ateapipb.AccessMode_ACCESS_MODE_READ_WRITE_ONCE.String()})) + } + switch { + case storageClassName == "" && volumeID == "": + errs = append(errs, field.Required(volumePath.Child("storage_class_name"), "one of storage_class_name or volume_id is required")) + case volumeID != "": + if volume.GetCapacity() != "" { + errs = append(errs, field.Forbidden(volumePath.Child("capacity"), "must not be set with volume_id")) + } + if volume.GetVolumeType() == "" && storageClassName == "" { + errs = append(errs, field.Required(volumePath.Child("volume_type"), "required with volume_id unless storage_class_name is set")) + } + default: + if volume.GetVolumeType() != "" { + errs = append(errs, field.Forbidden(volumePath.Child("volume_type"), "must not be set with storage_class_name; it is taken from the class's provisioner")) + } + if len(volume.GetVolumeContext()) > 0 { + errs = append(errs, field.Forbidden(volumePath.Child("volume_context"), "must not be set with storage_class_name; it is returned by the storage system")) + } + if volume.GetCapacity() == "" { + errs = append(errs, field.Required(volumePath.Child("capacity"), "required with storage_class_name")) + } + } + return errs +} + +func ValidateCustom_SharedVolume_Capacity(_ context.Context, _ operation.Operation, fldPath *field.Path, value, _ *string) field.ErrorList { + if value == nil || *value == "" { + return nil + } + if _, err := resource.ParseQuantity(*value); err != nil { + return field.ErrorList{field.Invalid(fldPath, *value, fmt.Sprintf("must be a Kubernetes resource quantity: %v", err))} + } + return nil +} + +// This exists only because nested subfield tags are not supported yet. +func ValidateCustom_UpdateSharedVolumeRequest_SharedVolume(ctx context.Context, op operation.Operation, fldPath *field.Path, volume, _ *ateapipb.SharedVolume) field.ErrorList { + if volume == nil || volume.Metadata == nil { + return nil + } + errs := Validate_ResourceMetadata(ctx, op, fldPath.Child("metadata"), volume.Metadata, nil) + errs = append(errs, validate.RequiredValue(ctx, op, fldPath.Child("metadata", "atespace"), &volume.Metadata.Atespace, nil)...) + return errs +} + +// An SharedVolume names its storage the same way an ExternalVolume does. +func ValidateCustom_SharedVolume_VolumeType(ctx context.Context, op operation.Operation, fldPath *field.Path, value, oldValue *string) field.ErrorList { + return ValidateCustom_ExternalVolume_VolumeType(ctx, op, fldPath, value, oldValue) +} + +func ValidateCustom_SharedVolume_VolumeId(ctx context.Context, op operation.Operation, fldPath *field.Path, value, oldValue *string) field.ErrorList { + return ValidateCustom_ExternalVolume_StorageVolumeId(ctx, op, fldPath, value, oldValue) +} diff --git a/cmd/ateapi/internal/apivalidation/shared_volume_test.go b/cmd/ateapi/internal/apivalidation/shared_volume_test.go new file mode 100644 index 0000000000..254f0cb8e8 --- /dev/null +++ b/cmd/ateapi/internal/apivalidation/shared_volume_test.go @@ -0,0 +1,117 @@ +// 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. + +package apivalidation + +import ( + "context" + "testing" + + "github.com/google/go-cmp/cmp" + "go.ate.dev/substrate/pkg/proto/ateapipb" + "k8s.io/apimachinery/pkg/api/operation" + "k8s.io/apimachinery/pkg/util/validation/field" +) + +func TestValidateCustomCreateSharedVolumeRequest(t *testing.T) { + tests := []struct { + name string + volume *ateapipb.SharedVolume + wantFields []string + }{ + { + name: "provisioning from a class", + volume: &ateapipb.SharedVolume{StorageClassName: "standard", Capacity: "10Gi"}, + }, + { + name: "registering an existing volume", + volume: &ateapipb.SharedVolume{VolumeId: "disk-1", VolumeType: "mock"}, + }, + { + name: "registering an existing volume under a named class", + volume: &ateapipb.SharedVolume{VolumeId: "disk-1", StorageClassName: "standard"}, + }, + { + name: "neither a class nor an ID", + volume: &ateapipb.SharedVolume{}, + wantFields: []string{"shared_volume.storage_class_name"}, + }, + { + name: "a size for a volume that already exists", + volume: &ateapipb.SharedVolume{VolumeId: "disk-1", VolumeType: "mock", Capacity: "10Gi"}, + wantFields: []string{"shared_volume.capacity"}, + }, + { + name: "an ID with no driver to reach it through", + volume: &ateapipb.SharedVolume{VolumeId: "disk-1"}, + wantFields: []string{"shared_volume.volume_type"}, + }, + { + name: "a driver alongside the class that supplies one", + volume: &ateapipb.SharedVolume{StorageClassName: "standard", VolumeType: "mock", Capacity: "10Gi"}, + wantFields: []string{"shared_volume.volume_type"}, + }, + { + name: "driver metadata for a volume that does not exist yet", + volume: &ateapipb.SharedVolume{StorageClassName: "standard", Capacity: "10Gi", VolumeContext: map[string]string{"fsType": "ext4"}}, + wantFields: []string{"shared_volume.volume_context"}, + }, + { + name: "provisioning without a size", + volume: &ateapipb.SharedVolume{StorageClassName: "standard"}, + wantFields: []string{"shared_volume.capacity"}, + }, + { + name: "read write many is not supported yet", + volume: &ateapipb.SharedVolume{StorageClassName: "standard", Capacity: "10Gi", AccessMode: ateapipb.AccessMode_ACCESS_MODE_READ_WRITE_MANY}, + wantFields: []string{"shared_volume.access_mode"}, + }, + } + for _, tc := range tests { + t.Run(tc.name, func(t *testing.T) { + req := &ateapipb.CreateSharedVolumeRequest{SharedVolume: tc.volume} + errs := ValidateCustom_CreateSharedVolumeRequest(context.Background(), operation.Operation{Type: operation.Create}, nil, req, nil) + + var gotFields []string + for _, err := range errs { + gotFields = append(gotFields, err.Field) + } + if diff := cmp.Diff(tc.wantFields, gotFields); diff != "" { + t.Errorf("offending fields mismatch (-want +got):\n%s", diff) + } + }) + } +} + +func TestValidateCustomSharedVolumeCapacity(t *testing.T) { + tests := []struct { + value string + wantErr bool + }{ + {value: ""}, + {value: "10Gi"}, + {value: "1500m"}, + {value: "ten gigs", wantErr: true}, + {value: "10GiB", wantErr: true}, + } + for _, tc := range tests { + t.Run(tc.value, func(t *testing.T) { + value := tc.value + errs := ValidateCustom_SharedVolume_Capacity(context.Background(), operation.Operation{Type: operation.Create}, field.NewPath("capacity"), &value, nil) + if gotErr := len(errs) > 0; gotErr != tc.wantErr { + t.Errorf("ValidateCustom_SharedVolume_Capacity(%q) errors = %v, wantErr %v", tc.value, errs, tc.wantErr) + } + }) + } +} diff --git a/cmd/ateapi/internal/apivalidation/zz_generated.validation.go b/cmd/ateapi/internal/apivalidation/zz_generated.validation.go index dd490a1a36..2068c9f850 100644 --- a/cmd/ateapi/internal/apivalidation/zz_generated.validation.go +++ b/cmd/ateapi/internal/apivalidation/zz_generated.validation.go @@ -325,6 +325,48 @@ func Validate_Actor( errs = append(errs, fn(fldPath.Child("status"), obj.Status, oldVal, oldObj != nil)...) } + { // field ateapipb.Actor.SharedVolumeBindings + fn := func( + fldPath *field.Path, + obj, oldObj map[string]string, + 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.Immutable(ctx, op, fldPath, obj, oldObj).MarkShortCircuit(); len(e) != 0 { + errs = append(errs, e...) + earlyReturn = true + } + if e := validate.MaxProperties(ctx, op, fldPath, obj, oldObj, 32).MarkShortCircuit(); len(e) != 0 { + errs = append(errs, e...) + earlyReturn = true + } + if e := validate.OptionalMap(ctx, op, fldPath, obj, oldObj).MarkShortCircuit(); len(e) != 0 { + earlyReturn = true + } + if earlyReturn { + return // do not proceed + } + if e := validate.EachMapKey(ctx, op, fldPath, obj, oldObj, validate.ShortName); len(e) != 0 { + errs = append(errs, e...) + } + if e := validate.EachMapVal(ctx, op, fldPath, obj, oldObj, validate.DirectEqual, validate.ShortName); len(e) != 0 { + errs = append(errs, e...) + } + return + } + oldVal := safe.Field(oldObj, + func(oldObj *ateapipb.Actor) map[string]string { + return oldObj.SharedVolumeBindings + }) + errs = append(errs, fn(fldPath.Child("shared_volume_bindings"), obj.SharedVolumeBindings, oldVal, oldObj != nil)...) + } + return errs } @@ -631,6 +673,107 @@ func Validate_ActorMetadataItem( return errs } +// Validate_ActorRef validates an instance of ActorRef according +// to declarative validation rules in the API schema. +func Validate_ActorRef( + ctx context.Context, op operation.Operation, fldPath *field.Path, + obj, oldObj *ateapipb.ActorRef) (errs field.ErrorList) { + + { // field ateapipb.ActorRef.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.MaxLength(ctx, op, fldPath, obj, oldObj, 253); len(e) != 0 { + errs = append(errs, e...) + } + return + } + oldVal := safe.Field(oldObj, + func(oldObj *ateapipb.ActorRef) *string { + return &oldObj.ActorUid + }) + errs = append(errs, fn(fldPath.Child("actor_uid"), &obj.ActorUid, oldVal, oldObj != nil)...) + } + + { // field ateapipb.ActorRef.ActorName + 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.ShortName(ctx, op, fldPath, obj, oldObj); len(e) != 0 { + errs = append(errs, e...) + } + return + } + oldVal := safe.Field(oldObj, + func(oldObj *ateapipb.ActorRef) *string { + return &oldObj.ActorName + }) + errs = append(errs, fn(fldPath.Child("actor_name"), &obj.ActorName, oldVal, oldObj != nil)...) + } + + { // field ateapipb.ActorRef.Active + fn := func( + fldPath *field.Path, + obj, oldObj *bool, + 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.OptionalValue(ctx, op, fldPath, obj, oldObj).MarkShortCircuit(); len(e) != 0 { + earlyReturn = true + } + if earlyReturn { + return // do not proceed + } + return + } + oldVal := safe.Field(oldObj, + func(oldObj *ateapipb.ActorRef) *bool { + return &oldObj.Active + }) + errs = append(errs, fn(fldPath.Child("active"), &obj.Active, oldVal, oldObj != nil)...) + } + + return errs +} + // Validate_ActorSnapshotState validates an instance of ActorSnapshotState according // to declarative validation rules in the API schema. func Validate_ActorSnapshotState( @@ -2369,6 +2512,51 @@ func Validate_CreateGlobalAccessPolicyRequest( return errs } +// Validate_CreateSharedVolumeRequest validates an instance of CreateSharedVolumeRequest according +// to declarative validation rules in the API schema. +func Validate_CreateSharedVolumeRequest( + ctx context.Context, op operation.Operation, fldPath *field.Path, + obj, oldObj *ateapipb.CreateSharedVolumeRequest) (errs field.ErrorList) { + + // custom validation + if e := ValidateCustom_CreateSharedVolumeRequest(ctx, op, fldPath, obj, oldObj); len(e) != 0 { + errs = append(errs, e...) + } + + { // field ateapipb.CreateSharedVolumeRequest.SharedVolume + fn := func( + fldPath *field.Path, + obj, oldObj *ateapipb.SharedVolume, + 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_SharedVolume(ctx, op, fldPath, obj, oldObj)...) + return + } + oldVal := safe.Field(oldObj, + func(oldObj *ateapipb.CreateSharedVolumeRequest) *ateapipb.SharedVolume { + return oldObj.SharedVolume + }) + errs = append(errs, fn(fldPath.Child("shared_volume"), obj.SharedVolume, oldVal, oldObj != nil)...) + } + + return errs +} + // Validate_CreateTagRequest validates an instance of CreateTagRequest according // to declarative validation rules in the API schema. func Validate_CreateTagRequest( @@ -3213,6 +3401,91 @@ func Validate_DeleteOptions( return errs } +// Validate_DeleteSharedVolumeRequest validates an instance of DeleteSharedVolumeRequest according +// to declarative validation rules in the API schema. +func Validate_DeleteSharedVolumeRequest( + ctx context.Context, op operation.Operation, fldPath *field.Path, + obj, oldObj *ateapipb.DeleteSharedVolumeRequest) (errs field.ErrorList) { + + { // field ateapipb.DeleteSharedVolumeRequest.SharedVolume + fn := func( + fldPath *field.Path, + obj, oldObj *ateapipb.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 + } + func() { // cohort = "atespace" + earlyReturn := false + if e := validate.Subfield(ctx, op, fldPath, obj, oldObj, "atespace", + func(o *ateapipb.ObjectRef) *string { return &o.Atespace }, validate.DirectEqual, validate.RequiredValue).MarkShortCircuit(); len(e) != 0 { + errs = append(errs, e...) + earlyReturn = true + } + if e := validate.Subfield(ctx, op, fldPath, obj, oldObj, "atespace", + func(o *ateapipb.ObjectRef) *string { return &o.Atespace }, validate.DirectEqual, validate.OptionalValue).MarkShortCircuit(); len(e) != 0 { + 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 *ateapipb.DeleteSharedVolumeRequest) *ateapipb.ObjectRef { + return oldObj.SharedVolume + }) + errs = append(errs, fn(fldPath.Child("shared_volume"), obj.SharedVolume, oldVal, oldObj != nil)...) + } + + { // field ateapipb.DeleteSharedVolumeRequest.Options + fn := func( + fldPath *field.Path, + obj, oldObj *ateapipb.DeleteOptions, + 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.OptionalPointer(ctx, op, fldPath, obj, oldObj).MarkShortCircuit(); len(e) != 0 { + earlyReturn = true + } + if earlyReturn { + return // do not proceed + } + // call the type's validation function + errs = append(errs, Validate_DeleteOptions(ctx, op, fldPath, obj, oldObj)...) + return + } + oldVal := safe.Field(oldObj, + func(oldObj *ateapipb.DeleteSharedVolumeRequest) *ateapipb.DeleteOptions { + return oldObj.Options + }) + errs = append(errs, fn(fldPath.Child("options"), obj.Options, oldVal, oldObj != nil)...) + } + + return errs +} + // Validate_DeleteTagRequest validates an instance of DeleteTagRequest according // to declarative validation rules in the API schema. func Validate_DeleteTagRequest( @@ -3908,16 +4181,7 @@ func Validate_ExternalVolume( errs = append(errs, fn(fldPath.Child("volume_context"), obj.VolumeContext, oldVal, oldObj != nil)...) } - return errs -} - -// Validate_ExternalVolumeTemplate validates an instance of ExternalVolumeTemplate according -// to declarative validation rules in the API schema. -func Validate_ExternalVolumeTemplate( - ctx context.Context, op operation.Operation, fldPath *field.Path, - obj, oldObj *ateapipb.ExternalVolumeTemplate) (errs field.ErrorList) { - - { // field ateapipb.ExternalVolumeTemplate.Capacity + { // field ateapipb.ExternalVolume.SharedVolumeName fn := func( fldPath *field.Path, obj, oldObj *string, @@ -3930,18 +4194,150 @@ func Validate_ExternalVolumeTemplate( } // call field-attached validations earlyReturn := false - if e := validate.RequiredValue(ctx, op, fldPath, obj, oldObj).MarkShortCircuit(); len(e) != 0 { + if e := validate.OptionalValue(ctx, op, fldPath, obj, oldObj).MarkShortCircuit(); len(e) != 0 { + earlyReturn = true + } + if e := validate.UpdateValue(ctx, op, fldPath, obj, oldObj, + func(a string, b string) bool { return a == b }, validate.NoUnset, validate.NoModify).MarkShortCircuit(); len(e) != 0 { errs = append(errs, e...) earlyReturn = true } if earlyReturn { return // do not proceed } - // custom validation - if e := ValidateCustom_ExternalVolumeTemplate_Capacity(ctx, op, fldPath, obj, oldObj); len(e) != 0 { - errs = append(errs, e...) - } - if e := validate.MaxLength(ctx, op, fldPath, obj, oldObj, 32); len(e) != 0 { + if e := validate.ShortName(ctx, op, fldPath, obj, oldObj); len(e) != 0 { + errs = append(errs, e...) + } + return + } + oldVal := safe.Field(oldObj, + func(oldObj *ateapipb.ExternalVolume) *string { + return &oldObj.SharedVolumeName + }) + errs = append(errs, fn(fldPath.Child("shared_volume_name"), &obj.SharedVolumeName, oldVal, oldObj != nil)...) + } + + return errs +} + +var unionMembershipFor_go_ate_dev_substrate_pkg_proto_ateapipb_ExternalVolumeSource_ = validate.NewUnionMembership(validate.NewUnionMember("template"), validate.NewUnionMember("shared_volume")) + +// Validate_ExternalVolumeSource validates an instance of ExternalVolumeSource according +// to declarative validation rules in the API schema. +func Validate_ExternalVolumeSource( + ctx context.Context, op operation.Operation, fldPath *field.Path, + obj, oldObj *ateapipb.ExternalVolumeSource) (errs field.ErrorList) { + + if e := validate.Union(ctx, op, fldPath, obj, oldObj, unionMembershipFor_go_ate_dev_substrate_pkg_proto_ateapipb_ExternalVolumeSource_, + func(obj *ateapipb.ExternalVolumeSource) bool { + if obj == nil { + return false + } + return obj.Template != nil + }, + func(obj *ateapipb.ExternalVolumeSource) bool { + if obj == nil { + return false + } + return obj.SharedVolume != nil + }); len(e) != 0 { + errs = append(errs, e...) + } + + { // field ateapipb.ExternalVolumeSource.Template + fn := func( + fldPath *field.Path, + obj, oldObj *ateapipb.ExternalVolumeTemplate, + 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.OptionalPointer(ctx, op, fldPath, obj, oldObj).MarkShortCircuit(); len(e) != 0 { + earlyReturn = true + } + if earlyReturn { + return // do not proceed + } + // call the type's validation function + errs = append(errs, Validate_ExternalVolumeTemplate(ctx, op, fldPath, obj, oldObj)...) + return + } + oldVal := safe.Field(oldObj, + func(oldObj *ateapipb.ExternalVolumeSource) *ateapipb.ExternalVolumeTemplate { + return oldObj.Template + }) + errs = append(errs, fn(fldPath.Child("template"), obj.Template, oldVal, oldObj != nil)...) + } + + { // field ateapipb.ExternalVolumeSource.SharedVolume + fn := func( + fldPath *field.Path, + obj, oldObj *ateapipb.SharedVolumeRef, + 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.OptionalPointer(ctx, op, fldPath, obj, oldObj).MarkShortCircuit(); len(e) != 0 { + earlyReturn = true + } + if earlyReturn { + return // do not proceed + } + // call the type's validation function + errs = append(errs, Validate_SharedVolumeRef(ctx, op, fldPath, obj, oldObj)...) + return + } + oldVal := safe.Field(oldObj, + func(oldObj *ateapipb.ExternalVolumeSource) *ateapipb.SharedVolumeRef { + return oldObj.SharedVolume + }) + errs = append(errs, fn(fldPath.Child("shared_volume"), obj.SharedVolume, oldVal, oldObj != nil)...) + } + + return errs +} + +// Validate_ExternalVolumeTemplate validates an instance of ExternalVolumeTemplate according +// to declarative validation rules in the API schema. +func Validate_ExternalVolumeTemplate( + ctx context.Context, op operation.Operation, fldPath *field.Path, + obj, oldObj *ateapipb.ExternalVolumeTemplate) (errs field.ErrorList) { + + { // field ateapipb.ExternalVolumeTemplate.Capacity + 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 + } + // custom validation + if e := ValidateCustom_ExternalVolumeTemplate_Capacity(ctx, op, fldPath, obj, oldObj); len(e) != 0 { + errs = append(errs, e...) + } + if e := validate.MaxLength(ctx, op, fldPath, obj, oldObj, 32); len(e) != 0 { errs = append(errs, e...) } return @@ -4388,6 +4784,61 @@ func Validate_GetGlobalAccessPolicyRequest( return errs } +// Validate_GetSharedVolumeRequest validates an instance of GetSharedVolumeRequest according +// to declarative validation rules in the API schema. +func Validate_GetSharedVolumeRequest( + ctx context.Context, op operation.Operation, fldPath *field.Path, + obj, oldObj *ateapipb.GetSharedVolumeRequest) (errs field.ErrorList) { + + { // field ateapipb.GetSharedVolumeRequest.SharedVolume + fn := func( + fldPath *field.Path, + obj, oldObj *ateapipb.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 + } + func() { // cohort = "atespace" + earlyReturn := false + if e := validate.Subfield(ctx, op, fldPath, obj, oldObj, "atespace", + func(o *ateapipb.ObjectRef) *string { return &o.Atespace }, validate.DirectEqual, validate.RequiredValue).MarkShortCircuit(); len(e) != 0 { + errs = append(errs, e...) + earlyReturn = true + } + if e := validate.Subfield(ctx, op, fldPath, obj, oldObj, "atespace", + func(o *ateapipb.ObjectRef) *string { return &o.Atespace }, validate.DirectEqual, validate.OptionalValue).MarkShortCircuit(); len(e) != 0 { + 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 *ateapipb.GetSharedVolumeRequest) *ateapipb.ObjectRef { + return oldObj.SharedVolume + }) + errs = append(errs, fn(fldPath.Child("shared_volume"), obj.SharedVolume, oldVal, oldObj != nil)...) + } + + return errs +} + // Validate_GetTagRequest validates an instance of GetTagRequest according // to declarative validation rules in the API schema. func Validate_GetTagRequest( @@ -5392,6 +5843,76 @@ func Validate_ListOptions( return errs } +// Validate_ListSharedVolumesRequest validates an instance of ListSharedVolumesRequest according +// to declarative validation rules in the API schema. +func Validate_ListSharedVolumesRequest( + ctx context.Context, op operation.Operation, fldPath *field.Path, + obj, oldObj *ateapipb.ListSharedVolumesRequest) (errs field.ErrorList) { + + { // field ateapipb.ListSharedVolumesRequest.Atespace + 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.OptionalValue(ctx, op, fldPath, obj, oldObj).MarkShortCircuit(); len(e) != 0 { + earlyReturn = true + } + if earlyReturn { + return // do not proceed + } + if e := validate.ShortName(ctx, op, fldPath, obj, oldObj); len(e) != 0 { + errs = append(errs, e...) + } + return + } + oldVal := safe.Field(oldObj, + func(oldObj *ateapipb.ListSharedVolumesRequest) *string { + return &oldObj.Atespace + }) + errs = append(errs, fn(fldPath.Child("atespace"), &obj.Atespace, oldVal, oldObj != nil)...) + } + + { // field ateapipb.ListSharedVolumesRequest.Options + fn := func( + fldPath *field.Path, + obj, oldObj *ateapipb.ListOptions, + 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.OptionalPointer(ctx, op, fldPath, obj, oldObj).MarkShortCircuit(); len(e) != 0 { + earlyReturn = true + } + if earlyReturn { + return // do not proceed + } + // call the type's validation function + errs = append(errs, Validate_ListOptions(ctx, op, fldPath, obj, oldObj)...) + return + } + oldVal := safe.Field(oldObj, + func(oldObj *ateapipb.ListSharedVolumesRequest) *ateapipb.ListOptions { + return oldObj.Options + }) + errs = append(errs, fn(fldPath.Child("options"), obj.Options, oldVal, oldObj != nil)...) + } + + return errs +} + // Validate_ListTagsRequest validates an instance of ListTagsRequest according // to declarative validation rules in the API schema. func Validate_ListTagsRequest( @@ -7564,35 +8085,526 @@ func Validate_Selector( return errs } -// Validate_Snapshot validates an instance of Snapshot according +// Validate_SharedVolume validates an instance of SharedVolume according // to declarative validation rules in the API schema. -func Validate_Snapshot( +func Validate_SharedVolume( ctx context.Context, op operation.Operation, fldPath *field.Path, - obj, oldObj *ateapipb.Snapshot) (errs field.ErrorList) { + obj, oldObj *ateapipb.SharedVolume) (errs field.ErrorList) { - { // field ateapipb.Snapshot.Uuid + { // field ateapipb.SharedVolume.Metadata fn := func( fldPath *field.Path, - obj, oldObj *string, + obj, oldObj *ateapipb.ResourceMetadata, 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) { + if ateDeepEqual(obj, oldObj) { return nil } } // call field-attached validations earlyReturn := false - if e := validate.RequiredValue(ctx, op, fldPath, obj, oldObj).MarkShortCircuit(); len(e) != 0 { + 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 } - if e := validate.ShortName(ctx, op, fldPath, obj, oldObj); len(e) != 0 { - errs = append(errs, e...) - } + func() { // cohort = "atespace" + earlyReturn := false + if e := validate.Subfield(ctx, op, fldPath, obj, oldObj, "atespace", + func(o *ateapipb.ResourceMetadata) *string { return &o.Atespace }, validate.DirectEqual, validate.RequiredValue).MarkShortCircuit(); len(e) != 0 { + errs = append(errs, e...) + earlyReturn = true + } + if e := validate.Subfield(ctx, op, fldPath, obj, oldObj, "atespace", + func(o *ateapipb.ResourceMetadata) *string { return &o.Atespace }, validate.DirectEqual, validate.Immutable).MarkShortCircuit(); len(e) != 0 { + earlyReturn = true + } + if e := validate.Subfield(ctx, op, fldPath, obj, oldObj, "atespace", + func(o *ateapipb.ResourceMetadata) *string { return &o.Atespace }, validate.DirectEqual, validate.OptionalValue).MarkShortCircuit(); len(e) != 0 { + earlyReturn = true + } + if earlyReturn { + return // do not proceed + } + }() + // call the type's validation function + errs = append(errs, Validate_ResourceMetadata(ctx, op, fldPath, obj, oldObj)...) + return + } + oldVal := safe.Field(oldObj, + func(oldObj *ateapipb.SharedVolume) *ateapipb.ResourceMetadata { + return oldObj.Metadata + }) + errs = append(errs, fn(fldPath.Child("metadata"), obj.Metadata, oldVal, oldObj != nil)...) + } + + { // field ateapipb.SharedVolume.DeleteTrigger + fn := func( + fldPath *field.Path, + obj, oldObj *ateapipb.DeleteTrigger, + 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.OptionalValue(ctx, op, fldPath, obj, oldObj).MarkShortCircuit(); len(e) != 0 { + earlyReturn = true + } + if earlyReturn { + return // do not proceed + } + if e := validate.Maximum(ctx, op, fldPath, obj, oldObj, 2); len(e) != 0 { + errs = append(errs, e...) + } + if e := validate.Minimum(ctx, op, fldPath, obj, oldObj, 1); len(e) != 0 { + errs = append(errs, e...) + } + return + } + oldVal := safe.Field(oldObj, + func(oldObj *ateapipb.SharedVolume) *ateapipb.DeleteTrigger { + return &oldObj.DeleteTrigger + }) + errs = append(errs, fn(fldPath.Child("delete_trigger"), &obj.DeleteTrigger, oldVal, oldObj != nil)...) + } + + { // field ateapipb.SharedVolume.StorageClassName + 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.OptionalValue(ctx, op, fldPath, obj, oldObj).MarkShortCircuit(); len(e) != 0 { + earlyReturn = true + } + if e := validate.UpdateValue(ctx, op, fldPath, obj, oldObj, + func(a string, b string) bool { return a == b }, validate.NoUnset, validate.NoModify).MarkShortCircuit(); len(e) != 0 { + errs = append(errs, e...) + earlyReturn = true + } + if earlyReturn { + return // do not proceed + } + if e := validate.LongName(ctx, op, fldPath, obj, oldObj); len(e) != 0 { + errs = append(errs, e...) + } + return + } + oldVal := safe.Field(oldObj, + func(oldObj *ateapipb.SharedVolume) *string { + return &oldObj.StorageClassName + }) + errs = append(errs, fn(fldPath.Child("storage_class_name"), &obj.StorageClassName, oldVal, oldObj != nil)...) + } + + { // field ateapipb.SharedVolume.VolumeId + 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.OptionalValue(ctx, op, fldPath, obj, oldObj).MarkShortCircuit(); len(e) != 0 { + earlyReturn = true + } + if e := validate.UpdateValue(ctx, op, fldPath, obj, oldObj, + func(a string, b string) bool { return a == b }, validate.NoUnset, validate.NoModify).MarkShortCircuit(); len(e) != 0 { + errs = append(errs, e...) + earlyReturn = true + } + if earlyReturn { + return // do not proceed + } + // custom validation + if e := ValidateCustom_SharedVolume_VolumeId(ctx, op, fldPath, obj, oldObj); len(e) != 0 { + errs = append(errs, e...) + } + if e := validate.MaxLength(ctx, op, fldPath, obj, oldObj, 256); len(e) != 0 { + errs = append(errs, e...) + } + return + } + oldVal := safe.Field(oldObj, + func(oldObj *ateapipb.SharedVolume) *string { + return &oldObj.VolumeId + }) + errs = append(errs, fn(fldPath.Child("volume_id"), &obj.VolumeId, oldVal, oldObj != nil)...) + } + + { // field ateapipb.SharedVolume.VolumeType + 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.OptionalValue(ctx, op, fldPath, obj, oldObj).MarkShortCircuit(); len(e) != 0 { + earlyReturn = true + } + if e := validate.UpdateValue(ctx, op, fldPath, obj, oldObj, + func(a string, b string) bool { return a == b }, validate.NoUnset, validate.NoModify).MarkShortCircuit(); len(e) != 0 { + errs = append(errs, e...) + earlyReturn = true + } + if earlyReturn { + return // do not proceed + } + // custom validation + if e := ValidateCustom_SharedVolume_VolumeType(ctx, op, fldPath, obj, oldObj); len(e) != 0 { + errs = append(errs, e...) + } + if e := validate.MaxLength(ctx, op, fldPath, obj, oldObj, 253); len(e) != 0 { + errs = append(errs, e...) + } + return + } + oldVal := safe.Field(oldObj, + func(oldObj *ateapipb.SharedVolume) *string { + return &oldObj.VolumeType + }) + errs = append(errs, fn(fldPath.Child("volume_type"), &obj.VolumeType, oldVal, oldObj != nil)...) + } + + { // field ateapipb.SharedVolume.VolumeContext + fn := func( + fldPath *field.Path, + obj, oldObj map[string]string, + 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.MaxProperties(ctx, op, fldPath, obj, oldObj, 32).MarkShortCircuit(); len(e) != 0 { + errs = append(errs, e...) + earlyReturn = true + } + if e := validate.OptionalMap(ctx, op, fldPath, obj, oldObj).MarkShortCircuit(); len(e) != 0 { + earlyReturn = true + } + if earlyReturn { + return // do not proceed + } + if e := validate.EachMapKey(ctx, op, fldPath, obj, oldObj, + func(ctx context.Context, op operation.Operation, fldPath *field.Path, obj, oldObj *string) field.ErrorList { + return validate.MaxLength(ctx, op, fldPath, obj, oldObj, 128) + }); len(e) != 0 { + errs = append(errs, e...) + } + if e := validate.EachMapVal(ctx, op, fldPath, obj, oldObj, validate.DirectEqual, + func(ctx context.Context, op operation.Operation, fldPath *field.Path, obj, oldObj *string) field.ErrorList { + return validate.MaxLength(ctx, op, fldPath, obj, oldObj, 256) + }); len(e) != 0 { + errs = append(errs, e...) + } + return + } + oldVal := safe.Field(oldObj, + func(oldObj *ateapipb.SharedVolume) map[string]string { + return oldObj.VolumeContext + }) + errs = append(errs, fn(fldPath.Child("volume_context"), obj.VolumeContext, oldVal, oldObj != nil)...) + } + + { // field ateapipb.SharedVolume.Status + fn := func( + fldPath *field.Path, + obj, oldObj *ateapipb.SharedVolumeStatus, + 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.OptionalPointer(ctx, op, fldPath, obj, oldObj).MarkShortCircuit(); len(e) != 0 { + earlyReturn = true + } + if earlyReturn { + return // do not proceed + } + // call the type's validation function + errs = append(errs, Validate_SharedVolumeStatus(ctx, op, fldPath, obj, oldObj)...) + return + } + oldVal := safe.Field(oldObj, + func(oldObj *ateapipb.SharedVolume) *ateapipb.SharedVolumeStatus { + return oldObj.Status + }) + errs = append(errs, fn(fldPath.Child("status"), obj.Status, oldVal, oldObj != nil)...) + } + + { // field ateapipb.SharedVolume.Capacity + 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.OptionalValue(ctx, op, fldPath, obj, oldObj).MarkShortCircuit(); len(e) != 0 { + earlyReturn = true + } + if e := validate.UpdateValue(ctx, op, fldPath, obj, oldObj, + func(a string, b string) bool { return a == b }, validate.NoUnset, validate.NoModify).MarkShortCircuit(); len(e) != 0 { + errs = append(errs, e...) + earlyReturn = true + } + if earlyReturn { + return // do not proceed + } + // custom validation + if e := ValidateCustom_SharedVolume_Capacity(ctx, op, fldPath, obj, oldObj); len(e) != 0 { + errs = append(errs, e...) + } + if e := validate.MaxLength(ctx, op, fldPath, obj, oldObj, 64); len(e) != 0 { + errs = append(errs, e...) + } + return + } + oldVal := safe.Field(oldObj, + func(oldObj *ateapipb.SharedVolume) *string { + return &oldObj.Capacity + }) + errs = append(errs, fn(fldPath.Child("capacity"), &obj.Capacity, oldVal, oldObj != nil)...) + } + + { // field ateapipb.SharedVolume.AccessMode + fn := func( + fldPath *field.Path, + obj, oldObj *ateapipb.AccessMode, + 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.OptionalValue(ctx, op, fldPath, obj, oldObj).MarkShortCircuit(); len(e) != 0 { + earlyReturn = true + } + if e := validate.UpdateValue(ctx, op, fldPath, obj, oldObj, + func(a ateapipb.AccessMode, b ateapipb.AccessMode) bool { return a == b }, validate.NoUnset, validate.NoModify).MarkShortCircuit(); len(e) != 0 { + errs = append(errs, e...) + earlyReturn = true + } + if earlyReturn { + return // do not proceed + } + if e := validate.Maximum(ctx, op, fldPath, obj, oldObj, 2); len(e) != 0 { + errs = append(errs, e...) + } + if e := validate.Minimum(ctx, op, fldPath, obj, oldObj, 1); len(e) != 0 { + errs = append(errs, e...) + } + return + } + oldVal := safe.Field(oldObj, + func(oldObj *ateapipb.SharedVolume) *ateapipb.AccessMode { + return &oldObj.AccessMode + }) + errs = append(errs, fn(fldPath.Child("access_mode"), &obj.AccessMode, oldVal, oldObj != nil)...) + } + + return errs +} + +// Validate_SharedVolumeRef validates an instance of SharedVolumeRef according +// to declarative validation rules in the API schema. +func Validate_SharedVolumeRef( + ctx context.Context, op operation.Operation, fldPath *field.Path, + obj, oldObj *ateapipb.SharedVolumeRef) (errs field.ErrorList) { + + { // field ateapipb.SharedVolumeRef.Name + 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.OptionalValue(ctx, op, fldPath, obj, oldObj).MarkShortCircuit(); len(e) != 0 { + earlyReturn = true + } + if earlyReturn { + return // do not proceed + } + if e := validate.ShortName(ctx, op, fldPath, obj, oldObj); len(e) != 0 { + errs = append(errs, e...) + } + return + } + oldVal := safe.Field(oldObj, + func(oldObj *ateapipb.SharedVolumeRef) *string { + return &oldObj.Name + }) + errs = append(errs, fn(fldPath.Child("name"), &obj.Name, oldVal, oldObj != nil)...) + } + + return errs +} + +// Validate_SharedVolumeStatus validates an instance of SharedVolumeStatus according +// to declarative validation rules in the API schema. +func Validate_SharedVolumeStatus( + ctx context.Context, op operation.Operation, fldPath *field.Path, + obj, oldObj *ateapipb.SharedVolumeStatus) (errs field.ErrorList) { + + { // field ateapipb.SharedVolumeStatus.State + fn := func( + fldPath *field.Path, + obj, oldObj *ateapipb.SharedVolumeState, + 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.OptionalValue(ctx, op, fldPath, obj, oldObj).MarkShortCircuit(); len(e) != 0 { + earlyReturn = true + } + if earlyReturn { + return // do not proceed + } + if e := validate.Maximum(ctx, op, fldPath, obj, oldObj, 2); len(e) != 0 { + errs = append(errs, e...) + } + if e := validate.Minimum(ctx, op, fldPath, obj, oldObj, 1); len(e) != 0 { + errs = append(errs, e...) + } + return + } + oldVal := safe.Field(oldObj, + func(oldObj *ateapipb.SharedVolumeStatus) *ateapipb.SharedVolumeState { + return &oldObj.State + }) + errs = append(errs, fn(fldPath.Child("state"), &obj.State, oldVal, oldObj != nil)...) + } + + { // field ateapipb.SharedVolumeStatus.Refs + fn := func( + fldPath *field.Path, + obj, oldObj []*ateapipb.ActorRef, + 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.PtrSliceNoNils[ateapipb.ActorRef](ctx, op, fldPath, obj, oldObj).MarkShortCircuit(); len(e) != 0 { + errs = append(errs, e...) + earlyReturn = true + } + if e := validate.MaxItems(ctx, op, fldPath, obj, oldObj, 256).MarkShortCircuit(); len(e) != 0 { + errs = append(errs, e...) + earlyReturn = true + } + if e := validate.OptionalSlice(ctx, op, fldPath, obj, oldObj).MarkShortCircuit(); len(e) != 0 { + earlyReturn = true + } + if earlyReturn { + return // do not proceed + } + // lists with map semantics require unique keys + if e := validate.PtrSliceUnique(ctx, op, fldPath, obj, oldObj, + func(a *ateapipb.ActorRef, b *ateapipb.ActorRef) bool { return a.ActorUid == b.ActorUid }); len(e) != 0 { + errs = append(errs, e...) + } + // iterate the list and call the type's validation function + if e := validate.EachPtrSliceVal(ctx, op, fldPath, obj, oldObj, + func(a *ateapipb.ActorRef, b *ateapipb.ActorRef) bool { return a.ActorUid == b.ActorUid }, ateDeepEqual, Validate_ActorRef); len(e) != 0 { + errs = append(errs, e...) + } + return + } + oldVal := safe.Field(oldObj, + func(oldObj *ateapipb.SharedVolumeStatus) []*ateapipb.ActorRef { + return oldObj.Refs + }) + errs = append(errs, fn(fldPath.Child("refs"), obj.Refs, oldVal, oldObj != nil)...) + } + + return errs +} + +// Validate_Snapshot validates an instance of Snapshot according +// to declarative validation rules in the API schema. +func Validate_Snapshot( + ctx context.Context, op operation.Operation, fldPath *field.Path, + obj, oldObj *ateapipb.Snapshot) (errs field.ErrorList) { + + { // field ateapipb.Snapshot.Uuid + 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.ShortName(ctx, op, fldPath, obj, oldObj); len(e) != 0 { + errs = append(errs, e...) + } return } oldVal := safe.Field(oldObj, @@ -9119,6 +10131,59 @@ func Validate_UpdateGlobalAccessPolicyRequest( return errs } +// Validate_UpdateSharedVolumeRequest validates an instance of UpdateSharedVolumeRequest according +// to declarative validation rules in the API schema. +func Validate_UpdateSharedVolumeRequest( + ctx context.Context, op operation.Operation, fldPath *field.Path, + obj, oldObj *ateapipb.UpdateSharedVolumeRequest) (errs field.ErrorList) { + + { // field ateapipb.UpdateSharedVolumeRequest.SharedVolume + fn := func( + fldPath *field.Path, + obj, oldObj *ateapipb.SharedVolume, + 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 + } + // custom validation + if e := ValidateCustom_UpdateSharedVolumeRequest_SharedVolume(ctx, op, fldPath, obj, oldObj); len(e) != 0 { + errs = append(errs, e...) + } + func() { // cohort = "metadata" + earlyReturn := false + if e := validate.Subfield(ctx, op, fldPath, obj, oldObj, "metadata", + func(o *ateapipb.SharedVolume) *ateapipb.ResourceMetadata { return o.Metadata }, ateDeepEqual, validate.RequiredPointer).MarkShortCircuit(); len(e) != 0 { + errs = append(errs, e...) + earlyReturn = true + } + if earlyReturn { + return // do not proceed + } + }() + return + } + oldVal := safe.Field(oldObj, + func(oldObj *ateapipb.UpdateSharedVolumeRequest) *ateapipb.SharedVolume { + return oldObj.SharedVolume + }) + errs = append(errs, fn(fldPath.Child("shared_volume"), obj.SharedVolume, oldVal, oldObj != nil)...) + } + + return errs +} + // Validate_UpdateTagRequest validates an instance of UpdateTagRequest according // to declarative validation rules in the API schema. func Validate_UpdateTagRequest( @@ -9313,7 +10378,7 @@ func Validate_VersionedSandboxCompat( return errs } -var unionMembershipFor_go_ate_dev_substrate_pkg_proto_ateapipb_Volume_ = validate.NewUnionMembership(validate.NewUnionMember("durable_dir"), validate.NewUnionMember("external_volume_template"), validate.NewUnionMember("system_info"), validate.NewUnionMember("image")) +var unionMembershipFor_go_ate_dev_substrate_pkg_proto_ateapipb_Volume_ = validate.NewUnionMembership(validate.NewUnionMember("durable_dir"), validate.NewUnionMember("external"), validate.NewUnionMember("system_info"), validate.NewUnionMember("image")) // Validate_Volume validates an instance of Volume according // to declarative validation rules in the API schema. @@ -9332,7 +10397,7 @@ func Validate_Volume( if obj == nil { return false } - return obj.ExternalVolumeTemplate != nil + return obj.External != nil }, func(obj *ateapipb.Volume) bool { if obj == nil { @@ -9409,10 +10474,10 @@ func Validate_Volume( errs = append(errs, fn(fldPath.Child("durable_dir"), obj.DurableDir, oldVal, oldObj != nil)...) } - { // field ateapipb.Volume.ExternalVolumeTemplate + { // field ateapipb.Volume.External fn := func( fldPath *field.Path, - obj, oldObj *ateapipb.ExternalVolumeTemplate, + obj, oldObj *ateapipb.ExternalVolumeSource, oldValueCorrelated bool) (errs field.ErrorList) { // don't revalidate unchanged data if oldValueCorrelated && op.Type == operation.Update { @@ -9436,14 +10501,14 @@ func Validate_Volume( return // do not proceed } // call the type's validation function - errs = append(errs, Validate_ExternalVolumeTemplate(ctx, op, fldPath, obj, oldObj)...) + errs = append(errs, Validate_ExternalVolumeSource(ctx, op, fldPath, obj, oldObj)...) return } oldVal := safe.Field(oldObj, - func(oldObj *ateapipb.Volume) *ateapipb.ExternalVolumeTemplate { - return oldObj.ExternalVolumeTemplate + func(oldObj *ateapipb.Volume) *ateapipb.ExternalVolumeSource { + return oldObj.External }) - errs = append(errs, fn(fldPath.Child("external_volume_template"), obj.ExternalVolumeTemplate, oldVal, oldObj != nil)...) + errs = append(errs, fn(fldPath.Child("external"), obj.External, oldVal, oldObj != nil)...) } { // field ateapipb.Volume.SystemInfo diff --git a/cmd/ateapi/internal/authz/authorizer.go b/cmd/ateapi/internal/authz/authorizer.go index 68983aeef4..7132a497e3 100644 --- a/cmd/ateapi/internal/authz/authorizer.go +++ b/cmd/ateapi/internal/authz/authorizer.go @@ -54,7 +54,7 @@ type Authorizer struct { // Check verifies that the principal in ctx has relation on object. // Structural hierarchy links (global:root as parent_global of every atespace // and worker, and each atespace as parent_atespace of its actors, actor -// templates and tags) and +// templates, tags and shared volumes) and // the caller's bootstrap owner and system role grants, if any, are injected as OpenFGA // ContextualTuples at evaluation time rather than persisted in the tuple table. func (a *Authorizer) Check(ctx context.Context, relation, object string) error { @@ -132,7 +132,7 @@ func (a *Authorizer) checkRaw(ctx context.Context, user, relation, object string // 1. Deterministic structure: Every `atespace:` and `worker:` // unconditionally has `global:root` as its `parent_global`, and every // `actor:/`, `actor_template:/` and -// `tag:/` has `atespace:` as its +// `tag:/` and `shared_volume:/` has `atespace:` as its // `parent_atespace`. Because these // relationships are derived purely from the object type/ID, storing a row // per resource in the OpenFGA `tuple` table would be redundant. @@ -144,7 +144,7 @@ func contextualTuples(object string) []*openfgav1.TupleKey { switch objectType { case "atespace", "worker": return []*openfgav1.TupleKey{parentGlobalTuple(object)} - case "actor", "actor_template", "tag": + case "actor", "actor_template", "tag", "shared_volume": atespace, _, ok := strings.Cut(id, "/") if !ok { return nil diff --git a/cmd/ateapi/internal/authz/authz_util.go b/cmd/ateapi/internal/authz/authz_util.go index e4c86cc91e..6d1b4c3fc4 100644 --- a/cmd/ateapi/internal/authz/authz_util.go +++ b/cmd/ateapi/internal/authz/authz_util.go @@ -78,6 +78,8 @@ const ( RelationCanCreateTag = "can_create_tag" RelationCanListTags = "can_list_tags" RelationCanUseTag = "can_use_tag" + RelationCanCreateSharedVolume = "can_create_shared_volume" + RelationCanListSharedVolumes = "can_list_shared_volumes" RelationCanMintActorJWT = "can_mint_actor_jwt" RelationCanMintActorCertificate = "can_mint_actor_certificate" @@ -144,6 +146,11 @@ func TagObject(atespace, name string) string { return "tag:" + atespacedID(atespace, name) } +// SharedVolumeObject formats a shared volume as an OpenFGA object string. +func SharedVolumeObject(atespace, name string) string { + return "shared_volume:" + atespacedID(atespace, name) +} + // atespacedID formats the object ID of an atespaced resource as // "/". contextualTuples parses the atespace back out of it. func atespacedID(atespace, name string) string { diff --git a/cmd/ateapi/internal/authz/interceptor_test.go b/cmd/ateapi/internal/authz/interceptor_test.go index 4bab436a23..aa3e081a74 100644 --- a/cmd/ateapi/internal/authz/interceptor_test.go +++ b/cmd/ateapi/internal/authz/interceptor_test.go @@ -547,6 +547,17 @@ func TestUnaryServerInterceptor_LifecycleEgressWorkerAndTagChecks(t *testing.T) Metadata: &ateapipb.ResourceMetadata{Atespace: "team-a", Name: "v1"}, }}} deleteTag := rpcCall{ateapipb.Control_DeleteTag_FullMethodName, &ateapipb.DeleteTagRequest{Tag: tag}} + sharedVolume := &ateapipb.ObjectRef{Atespace: "team-a", Name: "ctx"} + createSharedVolume := rpcCall{ateapipb.Control_CreateSharedVolume_FullMethodName, &ateapipb.CreateSharedVolumeRequest{SharedVolume: &ateapipb.SharedVolume{ + Metadata: &ateapipb.ResourceMetadata{Atespace: "team-a", Name: "ctx"}, + }}} + getSharedVolume := rpcCall{ateapipb.Control_GetSharedVolume_FullMethodName, &ateapipb.GetSharedVolumeRequest{SharedVolume: sharedVolume}} + listSharedVolumes := rpcCall{ateapipb.Control_ListSharedVolumes_FullMethodName, &ateapipb.ListSharedVolumesRequest{Atespace: "team-a"}} + listAllSharedVolumes := rpcCall{ateapipb.Control_ListSharedVolumes_FullMethodName, &ateapipb.ListSharedVolumesRequest{}} + updateSharedVolume := rpcCall{ateapipb.Control_UpdateSharedVolume_FullMethodName, &ateapipb.UpdateSharedVolumeRequest{SharedVolume: &ateapipb.SharedVolume{ + Metadata: &ateapipb.ResourceMetadata{Atespace: "team-a", Name: "ctx"}, + }}} + deleteSharedVolume := rpcCall{ateapipb.Control_DeleteSharedVolume_FullMethodName, &ateapipb.DeleteSharedVolumeRequest{SharedVolume: sharedVolume}} mintJWT := rpcCall{ateapipb.Control_MintActorJwt_FullMethodName, &ateapipb.MintActorJwtRequest{Actor: actor}} mintCert := rpcCall{ateapipb.Control_MintActorCertificate_FullMethodName, &ateapipb.MintActorCertificateRequest{Actor: actor}} // cloneTag creates an actor in atespace from team-a's tag v1, with a @@ -638,6 +649,22 @@ func TestUnaryServerInterceptor_LifecycleEgressWorkerAndTagChecks(t *testing.T) {"viewer cannot update tag", "viewer", updateTag, codes.PermissionDenied}, {"viewer cannot delete tag", "viewer", deleteTag, codes.PermissionDenied}, {"outsider cannot delete tag", "outsider", deleteTag, codes.PermissionDenied}, + + // Shared volumes: viewers read, editors write, all within the owning atespace. + {"editor creates shared volume", "editor", createSharedVolume, codes.OK}, + {"viewer cannot create shared volume", "viewer", createSharedVolume, codes.PermissionDenied}, + {"outsider cannot create shared volume", "outsider", createSharedVolume, codes.PermissionDenied}, + {"viewer gets shared volume", "viewer", getSharedVolume, codes.OK}, + {"global viewer gets shared volume", "global-viewer", getSharedVolume, codes.OK}, + {"outsider cannot get shared volume", "outsider", getSharedVolume, codes.PermissionDenied}, + {"viewer lists shared volumes", "viewer", listSharedVolumes, codes.OK}, + {"outsider cannot list shared volumes", "outsider", listSharedVolumes, codes.PermissionDenied}, + {"global viewer lists all shared volumes", "global-viewer", listAllSharedVolumes, codes.OK}, + {"atespace editor cannot list all shared volumes", "editor", listAllSharedVolumes, codes.PermissionDenied}, + {"editor updates shared volume", "editor", updateSharedVolume, codes.OK}, + {"editor deletes shared volume", "editor", deleteSharedVolume, codes.OK}, + {"viewer cannot update shared volume", "viewer", updateSharedVolume, codes.PermissionDenied}, + {"viewer cannot delete shared volume", "viewer", deleteSharedVolume, codes.PermissionDenied}, {"bootstrap owner deletes tag", "bootstrap-owner", deleteTag, codes.OK}, // Cloning from a tag needs editor on the tag's atespace, whatever its diff --git a/cmd/ateapi/internal/authz/model.fga b/cmd/ateapi/internal/authz/model.fga index ea41388938..2b2c14c263 100644 --- a/cmd/ateapi/internal/authz/model.fga +++ b/cmd/ateapi/internal/authz/model.fga @@ -63,6 +63,7 @@ type global define can_list_actors: viewer or ingress_router or egress_gateway define can_list_actor_templates: viewer define can_list_tags: viewer + define can_list_shared_volumes: viewer # --- Policy Governance --- define can_create_access_policy: owner @@ -97,6 +98,8 @@ type atespace define can_list_actor_templates: viewer define can_create_tag: editor define can_list_tags: viewer + define can_create_shared_volume: editor + define can_list_shared_volumes: viewer define can_get: viewer define can_delete: owner @@ -130,6 +133,16 @@ type tag # Create actors from this tag (CreateActor source_tag). define can_use_tag: editor from parent_atespace +# A shared volume is owned by its atespace; the actors that reference it are +# recorded on the volume, not in the authorization model. +type shared_volume + relations + define parent_atespace: [atespace] + + define can_get: viewer from parent_atespace + define can_update: editor from parent_atespace + define can_delete: editor from parent_atespace + type actor relations define parent_atespace: [atespace] diff --git a/cmd/ateapi/internal/authz/registry.go b/cmd/ateapi/internal/authz/registry.go index c22d81a810..fc19f20cd6 100644 --- a/cmd/ateapi/internal/authz/registry.go +++ b/cmd/ateapi/internal/authz/registry.go @@ -123,6 +123,13 @@ func onTag(relation string, ref resourceRef, fldPath *field.Path) ([]check, fiel return []check{{relation: relation, object: TagObject(ref.GetAtespace(), ref.GetName())}}, nil } +func onSharedVolume(relation string, ref resourceRef, fldPath *field.Path) ([]check, field.ErrorList) { + if errs := validateRef(ref, fldPath); len(errs) > 0 { + return nil, errs + } + return []check{{relation: relation, object: SharedVolumeObject(ref.GetAtespace(), ref.GetName())}}, nil +} + // onSourceTag checks can_use_tag on the tag an actor is created from, if the // request names one. Golden tags are exempt: the handler only accepts one // built from the actor's own template, which can_use_template already covers. @@ -336,6 +343,23 @@ var defaultRPCPermissions = map[string]rpcRule{ return onTag(RelationCanDelete, r.GetTag(), field.NewPath("tag")) })), + // Shared volumes: viewers read, editors write, within the owning atespace. + ateapipb.Control_CreateSharedVolume_FullMethodName: rule(checksOf(func(r *ateapipb.CreateSharedVolumeRequest) ([]check, field.ErrorList) { + return onAtespace(RelationCanCreateSharedVolume, r.GetSharedVolume().GetMetadata().GetAtespace(), field.NewPath("shared_volume", "metadata", "atespace")) + })), + ateapipb.Control_GetSharedVolume_FullMethodName: rule(checksOf(func(r *ateapipb.GetSharedVolumeRequest) ([]check, field.ErrorList) { + return onSharedVolume(RelationCanGet, r.GetSharedVolume(), field.NewPath("shared_volume")) + })), + ateapipb.Control_ListSharedVolumes_FullMethodName: rule(checksOf(func(r *ateapipb.ListSharedVolumesRequest) ([]check, field.ErrorList) { + return onAtespaceOrGlobal(RelationCanListSharedVolumes, r.GetAtespace(), field.NewPath("atespace")) + })), + ateapipb.Control_UpdateSharedVolume_FullMethodName: rule(checksOf(func(r *ateapipb.UpdateSharedVolumeRequest) ([]check, field.ErrorList) { + return onSharedVolume(RelationCanUpdate, r.GetSharedVolume().GetMetadata(), field.NewPath("shared_volume", "metadata")) + })), + ateapipb.Control_DeleteSharedVolume_FullMethodName: rule(checksOf(func(r *ateapipb.DeleteSharedVolumeRequest) ([]check, field.ErrorList) { + return onSharedVolume(RelationCanDelete, r.GetSharedVolume(), field.NewPath("shared_volume")) + })), + // WorkerService is served only to atelet, which each handler // authenticates by its mTLS SPIFFE ID. ateapipb.WorkerService_RegisterWorker_FullMethodName: inHandler("ateletauth: caller is atelet on the Worker's node"), diff --git a/cmd/ateapi/internal/authz/registry_test.go b/cmd/ateapi/internal/authz/registry_test.go index b042904319..61c403fb71 100644 --- a/cmd/ateapi/internal/authz/registry_test.go +++ b/cmd/ateapi/internal/authz/registry_test.go @@ -293,6 +293,34 @@ func TestDefaultRPCPermissions(t *testing.T) { want: []check{{RelationCanMintActorCertificate, "actor:team-a/runner"}}, }, + // Shared volumes. + { + name: "CreateSharedVolume checks its atespace", + fullMethod: ateapipb.Control_CreateSharedVolume_FullMethodName, + req: &ateapipb.CreateSharedVolumeRequest{SharedVolume: &ateapipb.SharedVolume{ + Metadata: &ateapipb.ResourceMetadata{Atespace: "team-a", Name: "ctx"}, + }}, + want: []check{{RelationCanCreateSharedVolume, "atespace:team-a"}}, + }, + { + name: "GetSharedVolume", + fullMethod: ateapipb.Control_GetSharedVolume_FullMethodName, + req: &ateapipb.GetSharedVolumeRequest{SharedVolume: &ateapipb.ObjectRef{Atespace: "team-a", Name: "ctx"}}, + want: []check{{RelationCanGet, "shared_volume:team-a/ctx"}}, + }, + { + name: "ListSharedVolumes across all atespaces", + fullMethod: ateapipb.Control_ListSharedVolumes_FullMethodName, + req: &ateapipb.ListSharedVolumesRequest{}, + want: []check{{RelationCanListSharedVolumes, GlobalRootObject}}, + }, + { + name: "DeleteSharedVolume", + fullMethod: ateapipb.Control_DeleteSharedVolume_FullMethodName, + req: &ateapipb.DeleteSharedVolumeRequest{SharedVolume: &ateapipb.ObjectRef{Atespace: "team-a", Name: "ctx"}}, + want: []check{{RelationCanDelete, "shared_volume:team-a/ctx"}}, + }, + // Tags. { name: "CreateTag checks its source actor's atespace", diff --git a/cmd/ateapi/internal/controlapi/actor.go b/cmd/ateapi/internal/controlapi/actor.go index 9b0ad3f727..126b028f45 100644 --- a/cmd/ateapi/internal/controlapi/actor.go +++ b/cmd/ateapi/internal/controlapi/actor.go @@ -90,17 +90,23 @@ func (s *ServiceImpl) CreateActor(ctx context.Context, inActor *ateapipb.Actor) // Resolve the explicit tag, or freeze the template's current golden default. tagRef := inActor.GetSourceTag() - if tagRef == nil { - tagRef = template.GetStatus().GetGoldenSnapshotStatus().GetGoldenTag() - } else { + switch { + case tagRef != nil: // NOTE: whether the ExternalVolumes gate is enabled or not, we do not // support cloning from a tag that has external volumes. for _, volume := range template.GetVolumes() { - if volume.GetExternalVolumeTemplate() != nil { + if volume.GetExternal().GetTemplate() != nil { // TODO: Permit cloning after CSI volume snapshots are supported. return nil, apierror.FailedPrecondition("Tag cloning does not support ActorTemplates with external volumes") } + if volume.GetExternal().GetSharedVolume() != nil { + return nil, apierror.FailedPrecondition("Tag cloning does not support ActorTemplates that reference a SharedVolume") + } } + case templateHasSharedVolumeRef(template): + // TODO(shared-volumes): restore from the golden once checkpoint and restore mount options agree for external volume refs; see docs/csi-volumes.md. + default: + tagRef = template.GetStatus().GetGoldenSnapshotStatus().GetGoldenTag() } var sourceTag *ateapipb.Tag if tagRef != nil { @@ -131,6 +137,13 @@ func (s *ServiceImpl) CreateActor(ctx context.Context, inActor *ateapipb.Actor) } return nil, resources.ToAPIError(errs) } + if !isGoldenActor(inActor) { + refVols, err := resolveSharedVolumeRefs(ctx, s.store, inActor, template) + if err != nil { + return nil, err + } + initVols = append(initVols, refVols...) + } // Verify that the result is properly valid before storing it. outActor := proto.CloneOf(inActor) @@ -169,6 +182,11 @@ func (s *ServiceImpl) CreateActor(ctx context.Context, inActor *ateapipb.Actor) return nil, fmt.Errorf("while recording actor: %w", err) } + // Resume re-takes any reference missed here, so this does not fail the create. + if err := bindSharedVolumes(ctx, s.store, stored); err != nil { + slog.WarnContext(ctx, "Failed to bind external volumes to new actor", slog.Any("actor", resources.ActorRefFromActor(stored)), slog.Any("err", err)) + } + // Without this an actor that is created and never resumed has no record at // all, at any retention. logActorStateChanged(ctx, stored, ateattr.OperationCreate) @@ -176,6 +194,10 @@ func (s *ServiceImpl) CreateActor(ctx context.Context, inActor *ateapipb.Actor) return stored, nil } +func isGoldenActor(actor *ateapipb.Actor) bool { + return actor.GetMetadata().GetAtespace() == resources.GoldenActorAtespace +} + // resolveTagSource resolves a CreateActor request's source tag // and checks that the tag is usable for creating an Actor in actorAtespace // from template. diff --git a/cmd/ateapi/internal/controlapi/functionaltest/actor_template_test.go b/cmd/ateapi/internal/controlapi/functionaltest/actor_template_test.go index 27df83d3f0..155a180bbd 100644 --- a/cmd/ateapi/internal/controlapi/functionaltest/actor_template_test.go +++ b/cmd/ateapi/internal/controlapi/functionaltest/actor_template_test.go @@ -428,8 +428,8 @@ func TestCreateActorTemplate_ExternalVolumesPreview(t *testing.T) { VolumeMounts: []*ateapipb.VolumeMount{{Name: "data", MountPath: "/data"}}, }}, Volumes: []*ateapipb.Volume{{ - Name: "data", - ExternalVolumeTemplate: &ateapipb.ExternalVolumeTemplate{StorageClassName: "standard", Capacity: "1Gi"}, + Name: "data", + External: &ateapipb.ExternalVolumeSource{Template: &ateapipb.ExternalVolumeTemplate{StorageClassName: "standard", Capacity: "1Gi"}}, }}, Resources: &ateapipb.Resources{Limits: []*ateapipb.Limits{{Name: "cpu", Quantity: "1"}, {Name: "memory", Quantity: "1Gi"}}}, SnapshotConfig: &ateapipb.SnapshotConfig{StorageLocation: "gs://my-bucket/snapshots"}, diff --git a/cmd/ateapi/internal/controlapi/functionaltest/actor_test.go b/cmd/ateapi/internal/controlapi/functionaltest/actor_test.go index b061f3d5b3..4e95c99afa 100644 --- a/cmd/ateapi/internal/controlapi/functionaltest/actor_test.go +++ b/cmd/ateapi/internal/controlapi/functionaltest/actor_test.go @@ -121,10 +121,10 @@ func TestCreateActor_WithExternalVolumes(t *testing.T) { volumes := []*ateapipb.Volume{ { Name: "ext-vol-1", - ExternalVolumeTemplate: &ateapipb.ExternalVolumeTemplate{ + External: &ateapipb.ExternalVolumeSource{Template: &ateapipb.ExternalVolumeTemplate{ StorageClassName: "standard", Capacity: "10Gi", - }, + }}, }, } mounts := []*ateapipb.VolumeMount{ @@ -183,8 +183,8 @@ func TestCreateActor_ExternalVolumesWithoutOptIn(t *testing.T) { createTemplateWithVolumes(t, tc, ns, []*ateapipb.Volume{{ - Name: "ext-vol-1", - ExternalVolumeTemplate: &ateapipb.ExternalVolumeTemplate{StorageClassName: "standard", Capacity: "10Gi"}, + Name: "ext-vol-1", + External: &ateapipb.ExternalVolumeSource{Template: &ateapipb.ExternalVolumeTemplate{StorageClassName: "standard", Capacity: "10Gi"}}, }}, []*ateapipb.VolumeMount{{Name: "ext-vol-1", MountPath: "/data"}}) @@ -215,8 +215,8 @@ func TestActorLifecycle_ExternalVolumesWithoutOptIn(t *testing.T) { createTemplateWithVolumes(t, tc, ns, []*ateapipb.Volume{{ - Name: "data-vol", - ExternalVolumeTemplate: &ateapipb.ExternalVolumeTemplate{StorageClassName: "fast", Capacity: "20Gi"}, + Name: "data-vol", + External: &ateapipb.ExternalVolumeSource{Template: &ateapipb.ExternalVolumeTemplate{StorageClassName: "fast", Capacity: "20Gi"}}, }}, []*ateapipb.VolumeMount{{Name: "data-vol", MountPath: "/mnt/data"}}) createWorkerPod(t, tc, ns, "worker-1", "node1", "pool1") @@ -413,9 +413,9 @@ func TestCreateActor_RejectsSnapshotWithExternalVolumes(t *testing.T) { }}, Volumes: []*ateapipb.Volume{{ Name: "data", - ExternalVolumeTemplate: &ateapipb.ExternalVolumeTemplate{ + External: &ateapipb.ExternalVolumeSource{Template: &ateapipb.ExternalVolumeTemplate{ Capacity: "1Gi", StorageClassName: "standard", - }, + }}, }}, Resources: resources.CPUMemory(1000, 512<<20), }, @@ -1719,10 +1719,10 @@ func TestActorLifecycle_WithExternalVolumes(t *testing.T) { volumes := []*ateapipb.Volume{ { Name: "data-vol", - ExternalVolumeTemplate: &ateapipb.ExternalVolumeTemplate{ + External: &ateapipb.ExternalVolumeSource{Template: &ateapipb.ExternalVolumeTemplate{ StorageClassName: "fast", Capacity: "20Gi", - }, + }}, }, } mounts := []*ateapipb.VolumeMount{ @@ -1867,17 +1867,17 @@ func TestResumeActor_VolumeCreationFailure(t *testing.T) { volumes := []*ateapipb.Volume{ { Name: "succ-vol1", - ExternalVolumeTemplate: &ateapipb.ExternalVolumeTemplate{ + External: &ateapipb.ExternalVolumeSource{Template: &ateapipb.ExternalVolumeTemplate{ StorageClassName: "standard", Capacity: "10Gi", - }, + }}, }, { Name: "fail-vol2", - ExternalVolumeTemplate: &ateapipb.ExternalVolumeTemplate{ + External: &ateapipb.ExternalVolumeSource{Template: &ateapipb.ExternalVolumeTemplate{ StorageClassName: "standard", Capacity: "10Gi", - }, + }}, }, } mounts := []*ateapipb.VolumeMount{ @@ -2011,17 +2011,17 @@ func TestResumeActor_VolumeCreationRetrySuccess(t *testing.T) { volumes := []*ateapipb.Volume{ { Name: "succ-vol1", - ExternalVolumeTemplate: &ateapipb.ExternalVolumeTemplate{ + External: &ateapipb.ExternalVolumeSource{Template: &ateapipb.ExternalVolumeTemplate{ StorageClassName: "standard", Capacity: "10Gi", - }, + }}, }, { Name: "retry-vol2", - ExternalVolumeTemplate: &ateapipb.ExternalVolumeTemplate{ + External: &ateapipb.ExternalVolumeSource{Template: &ateapipb.ExternalVolumeTemplate{ StorageClassName: "standard", Capacity: "10Gi", - }, + }}, }, } retryMounts := []*ateapipb.VolumeMount{ @@ -2167,10 +2167,10 @@ func TestResumeActor_VolumeAttachFailureAndRetry(t *testing.T) { volumes := []*ateapipb.Volume{ { Name: "vol1", - ExternalVolumeTemplate: &ateapipb.ExternalVolumeTemplate{ + External: &ateapipb.ExternalVolumeSource{Template: &ateapipb.ExternalVolumeTemplate{ StorageClassName: "standard", Capacity: "10Gi", - }, + }}, }, } mounts := []*ateapipb.VolumeMount{ @@ -2271,10 +2271,10 @@ func TestResumeActor_VolumeAttachFailure_DeleteActor(t *testing.T) { volumes := []*ateapipb.Volume{ { Name: "vol1", - ExternalVolumeTemplate: &ateapipb.ExternalVolumeTemplate{ + External: &ateapipb.ExternalVolumeSource{Template: &ateapipb.ExternalVolumeTemplate{ StorageClassName: "standard", Capacity: "10Gi", - }, + }}, }, } mounts := []*ateapipb.VolumeMount{ @@ -2428,17 +2428,17 @@ func TestResumeActor_MultiVolumePartialAttachFailure_Retry(t *testing.T) { volumes := []*ateapipb.Volume{ { Name: "vol1", - ExternalVolumeTemplate: &ateapipb.ExternalVolumeTemplate{ + External: &ateapipb.ExternalVolumeSource{Template: &ateapipb.ExternalVolumeTemplate{ StorageClassName: "standard", Capacity: "10Gi", - }, + }}, }, { Name: "vol2", - ExternalVolumeTemplate: &ateapipb.ExternalVolumeTemplate{ + External: &ateapipb.ExternalVolumeSource{Template: &ateapipb.ExternalVolumeTemplate{ StorageClassName: "standard", Capacity: "10Gi", - }, + }}, }, } mounts := []*ateapipb.VolumeMount{ @@ -2600,10 +2600,10 @@ func TestSuspendActor_VolumeDetachFailure_RetrySuccess(t *testing.T) { volumes := []*ateapipb.Volume{ { Name: "vol1", - ExternalVolumeTemplate: &ateapipb.ExternalVolumeTemplate{ + External: &ateapipb.ExternalVolumeSource{Template: &ateapipb.ExternalVolumeTemplate{ StorageClassName: "standard", Capacity: "10Gi", - }, + }}, }, } mounts := []*ateapipb.VolumeMount{ @@ -2712,10 +2712,10 @@ func TestSuspendActor_VolumeDetachFailure_DeleteActorAnyState(t *testing.T) { volumes := []*ateapipb.Volume{ { Name: "vol1", - ExternalVolumeTemplate: &ateapipb.ExternalVolumeTemplate{ + External: &ateapipb.ExternalVolumeSource{Template: &ateapipb.ExternalVolumeTemplate{ StorageClassName: "standard", Capacity: "10Gi", - }, + }}, }, } mounts := []*ateapipb.VolumeMount{ @@ -2813,10 +2813,10 @@ func TestPauseActor_VolumeLifecycle_DetachAndResumeAttach(t *testing.T) { volumes := []*ateapipb.Volume{ { Name: "vol1", - ExternalVolumeTemplate: &ateapipb.ExternalVolumeTemplate{ + External: &ateapipb.ExternalVolumeSource{Template: &ateapipb.ExternalVolumeTemplate{ StorageClassName: "standard", Capacity: "10Gi", - }, + }}, }, } mounts := []*ateapipb.VolumeMount{ @@ -2941,10 +2941,10 @@ func TestPauseActor_VolumeDetachFailure_RetrySuccess(t *testing.T) { volumes := []*ateapipb.Volume{ { Name: "vol1", - ExternalVolumeTemplate: &ateapipb.ExternalVolumeTemplate{ + External: &ateapipb.ExternalVolumeSource{Template: &ateapipb.ExternalVolumeTemplate{ StorageClassName: "standard", Capacity: "10Gi", - }, + }}, }, } mounts := []*ateapipb.VolumeMount{ diff --git a/cmd/ateapi/internal/controlapi/functionaltest/shared_volume_golden_test.go b/cmd/ateapi/internal/controlapi/functionaltest/shared_volume_golden_test.go new file mode 100644 index 0000000000..e9ea942885 --- /dev/null +++ b/cmd/ateapi/internal/controlapi/functionaltest/shared_volume_golden_test.go @@ -0,0 +1,151 @@ +// 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. + +package functionaltest + +import ( + "context" + "testing" + + "go.ate.dev/substrate/pkg/proto/ateapipb" + "google.golang.org/grpc/codes" +) + +func resetAteletCalls(tc *testContext) { + tc.fakeAtelet.Lock.Lock() + defer tc.fakeAtelet.Lock.Unlock() + tc.fakeAtelet.RunCalled, tc.fakeAtelet.RunRequest = false, nil + tc.fakeAtelet.RestoreCalled, tc.fakeAtelet.RestoreRequest = false, nil +} + +func ateletCalls(tc *testContext) (run, restore bool) { + tc.fakeAtelet.Lock.Lock() + defer tc.fakeAtelet.Lock.Unlock() + return tc.fakeAtelet.RunCalled, tc.fakeAtelet.RestoreCalled +} + +func getActor(t *testing.T, tc *testContext, name string) *ateapipb.Actor { + t.Helper() + actor, err := tc.client.GetActor(context.Background(), &ateapipb.GetActorRequest{ + Actor: &ateapipb.ObjectRef{Atespace: testAtespace, Name: name}, + }) + if err != nil { + t.Fatalf("GetActor(%s) failed: %v", name, err) + } + return actor +} + +func TestSharedVolumeRef_ColdBootsThenRestoresOwnSnapshot(t *testing.T) { + ns := namespaceForTest("ns-extvol-coldboot") + tc, _ := setupSharedVolumeTest(t, ns) + defer tc.cleanup() + + createSharedVolume(t, tc, "shared", ateapipb.DeleteTrigger_DELETE_TRIGGER_MANUAL) + tmpl := createTemplateWithVolumes(t, tc, ns, + []*ateapipb.Volume{{Name: "work", External: &ateapipb.ExternalVolumeSource{SharedVolume: &ateapipb.SharedVolumeRef{Name: "shared"}}}}, + []*ateapipb.VolumeMount{{Name: "work", MountPath: "/mnt/work"}}) + if tmpl.GetStatus().GetGoldenSnapshotStatus().GetGoldenTag() == nil { + t.Fatal("test template has no golden tag; the case needs one to prove it is bypassed") + } + workerName := createWorkerPod(t, tc, ns, "worker-1", "node1", "pool1") + ctx := context.Background() + ref := &ateapipb.ObjectRef{Atespace: testAtespace, Name: "borrower"} + + createBorrowingActor(t, tc, "borrower", nil) + if snaps := getActor(t, tc, "borrower").GetStatus().GetSnapshots(); len(snaps) != 0 { + t.Fatalf("new actor carries snapshots %v, want none: it must not seed from the golden", snaps) + } + + waitForWorkerAvailable(t, tc, workerName) + resetAteletCalls(tc) + if _, err := tc.client.ResumeActor(ctx, &ateapipb.ResumeActorRequest{Actor: ref}); err != nil { + t.Fatalf("first ResumeActor failed: %v", err) + } + if run, restore := ateletCalls(tc); !run || restore { + t.Fatalf("first resume: Run=%v Restore=%v, want a cold boot through Run", run, restore) + } + + if _, err := tc.client.SuspendActor(ctx, &ateapipb.SuspendActorRequest{Actor: ref}); err != nil { + t.Fatalf("SuspendActor failed: %v", err) + } + own := durableSnapshotURI(getActor(t, tc, "borrower").GetStatus()) + if own == "" || own == goldenSnapshotURI(t) { + t.Fatalf("snapshot after suspend = %q, want the actor's own", own) + } + + waitForWorkerAvailable(t, tc, workerName) + resetAteletCalls(tc) + if _, err := tc.client.ResumeActor(ctx, &ateapipb.ResumeActorRequest{Actor: ref}); err != nil { + t.Fatalf("second ResumeActor failed: %v", err) + } + if run, restore := ateletCalls(tc); run || !restore { + t.Fatalf("second resume: Run=%v Restore=%v, want a restore", run, restore) + } + if got := tc.fakeAtelet.lastRestoreRequest().GetSnapshot().GetObject().GetSnapshotUri(); got != own { + t.Errorf("second resume restored %q, want the actor's own snapshot %q", got, own) + } +} + +func TestSharedVolumeRef_RejectsSourceTag(t *testing.T) { + ns := namespaceForTest("ns-extvol-sourcetag") + tc, _ := setupSharedVolumeTest(t, ns) + defer tc.cleanup() + + createSharedVolume(t, tc, "shared", ateapipb.DeleteTrigger_DELETE_TRIGGER_MANUAL) + tmpl := createTemplateWithVolumes(t, tc, ns, + []*ateapipb.Volume{{Name: "work", External: &ateapipb.ExternalVolumeSource{SharedVolume: &ateapipb.SharedVolumeRef{Name: "shared"}}}}, + []*ateapipb.VolumeMount{{Name: "work", MountPath: "/mnt/work"}}) + + _, err := tc.client.CreateActor(context.Background(), &ateapipb.CreateActorRequest{Actor: &ateapipb.Actor{ + Metadata: &ateapipb.ResourceMetadata{Atespace: testAtespace, Name: "clone"}, + ActorTemplate: &ateapipb.ObjectRef{Atespace: testAtespace, Name: "tmpl1"}, + SourceTag: tmpl.GetStatus().GetGoldenSnapshotStatus().GetGoldenTag(), + }}) + assertGrpcError(t, err, codes.FailedPrecondition, "Tag cloning does not support ActorTemplates that reference a SharedVolume") +} + +func TestExternalVolumeTemplate_FirstResumeRestoresGolden(t *testing.T) { + ns := namespaceForTest("ns-extvol-owned-golden") + tc, _ := setupSharedVolumeTest(t, ns) + defer tc.cleanup() + + createTemplateWithVolumes(t, tc, ns, + []*ateapipb.Volume{{Name: "work", External: &ateapipb.ExternalVolumeSource{Template: &ateapipb.ExternalVolumeTemplate{StorageClassName: "standard", Capacity: "1Gi"}}}}, + []*ateapipb.VolumeMount{{Name: "work", MountPath: "/mnt/work"}}) + workerName := createWorkerPod(t, tc, ns, "worker-1", "node1", "pool1") + ctx := context.Background() + ref := &ateapipb.ObjectRef{Atespace: testAtespace, Name: "owner"} + + if _, err := tc.client.CreateActor(ctx, &ateapipb.CreateActorRequest{Actor: &ateapipb.Actor{ + Metadata: &ateapipb.ResourceMetadata{Atespace: testAtespace, Name: "owner"}, + ActorTemplate: &ateapipb.ObjectRef{Atespace: testAtespace, Name: "tmpl1"}, + }}); err != nil { + t.Fatalf("CreateActor failed: %v", err) + } + if got := durableSnapshotURI(getActor(t, tc, "owner").GetStatus()); got != goldenSnapshotURI(t) { + t.Fatalf("new actor snapshot = %q, want the golden %q", got, goldenSnapshotURI(t)) + } + + waitForWorkerAvailable(t, tc, workerName) + resetAteletCalls(tc) + if _, err := tc.client.ResumeActor(ctx, &ateapipb.ResumeActorRequest{Actor: ref}); err != nil { + t.Fatalf("ResumeActor failed: %v", err) + } + if run, restore := ateletCalls(tc); run || !restore { + t.Fatalf("first resume: Run=%v Restore=%v, want a restore from the golden", run, restore) + } + if got := tc.fakeAtelet.lastRestoreRequest().GetSnapshot().GetObject().GetSnapshotUri(); got != goldenSnapshotURI(t) { + t.Errorf("first resume restored %q, want the golden %q", got, goldenSnapshotURI(t)) + } +} diff --git a/cmd/ateapi/internal/controlapi/functionaltest/shared_volume_test.go b/cmd/ateapi/internal/controlapi/functionaltest/shared_volume_test.go new file mode 100644 index 0000000000..1a698f37d6 --- /dev/null +++ b/cmd/ateapi/internal/controlapi/functionaltest/shared_volume_test.go @@ -0,0 +1,499 @@ +// 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. + +package functionaltest + +import ( + "context" + "fmt" + "sync" + "testing" + + "github.com/google/go-cmp/cmp" + "go.ate.dev/substrate/internal/preview" + "go.ate.dev/substrate/internal/volume" + "go.ate.dev/substrate/pkg/proto/ateapipb" + "google.golang.org/grpc/codes" + "google.golang.org/protobuf/testing/protocmp" +) + +// sharedVolumePlugin records every storage operation the control plane makes. +type sharedVolumePlugin struct { + volume.VolumePluginControlPlane + + mu sync.Mutex + created []string + deleted []string + attached []string + detached []string +} + +func (p *sharedVolumePlugin) CreateVolume(_ context.Context, req volume.CreateVolumeRequest) (volume.CreateVolumeResponse, error) { + p.mu.Lock() + defer p.mu.Unlock() + p.created = append(p.created, req.Name) + return volume.CreateVolumeResponse{VolumeID: "disk/" + req.Name, VolumeContext: req.Parameters}, nil +} + +func (p *sharedVolumePlugin) DeleteVolume(_ context.Context, volumeID string) error { + p.mu.Lock() + defer p.mu.Unlock() + p.deleted = append(p.deleted, volumeID) + return nil +} + +func (p *sharedVolumePlugin) AttachVolume(_ context.Context, req volume.AttachVolumeRequest) (volume.AttachVolumeResponse, error) { + p.mu.Lock() + defer p.mu.Unlock() + p.attached = append(p.attached, req.VolumeID+"@"+req.Node) + return volume.AttachVolumeResponse{}, nil +} + +func (p *sharedVolumePlugin) DetachVolume(_ context.Context, volumeID, node string) error { + p.mu.Lock() + defer p.mu.Unlock() + p.detached = append(p.detached, volumeID+"@"+node) + return nil +} + +func (p *sharedVolumePlugin) snapshot() (created, deleted, attached, detached []string) { + p.mu.Lock() + defer p.mu.Unlock() + return append([]string(nil), p.created...), append([]string(nil), p.deleted...), + append([]string(nil), p.attached...), append([]string(nil), p.detached...) +} + +func setupSharedVolumeTest(t *testing.T, ns string) (*testContext, *sharedVolumePlugin) { + t.Helper() + preview.InitForTest(t, "ExternalVolumes") + plugin := &sharedVolumePlugin{} + tc := setupTestWithVolumePlugins(t, ns, map[string]volume.VolumePluginControlPlane{ + "substrate.io/mock": plugin, + }) + return tc, plugin +} + +func createSharedVolume(t *testing.T, tc *testContext, name string, trigger ateapipb.DeleteTrigger) *ateapipb.SharedVolume { + t.Helper() + created, err := tc.client.CreateSharedVolume(context.Background(), &ateapipb.CreateSharedVolumeRequest{ + SharedVolume: &ateapipb.SharedVolume{ + Metadata: &ateapipb.ResourceMetadata{Atespace: testAtespace, Name: name}, + StorageClassName: "standard", + Capacity: "10Gi", + DeleteTrigger: trigger, + }, + }) + if err != nil { + t.Fatalf("CreateSharedVolume(%s) failed: %v", name, err) + } + if got := created.GetStatus().GetState(); got != ateapipb.SharedVolumeState_SHARED_VOLUME_STATE_READY { + t.Fatalf("CreateSharedVolume(%s) left the volume in %v, want READY", name, got) + } + return created +} + +func getSharedVolume(t *testing.T, tc *testContext, name string) *ateapipb.SharedVolume { + t.Helper() + stored, err := tc.client.GetSharedVolume(context.Background(), &ateapipb.GetSharedVolumeRequest{ + SharedVolume: &ateapipb.ObjectRef{Atespace: testAtespace, Name: name}, + }) + if err != nil { + t.Fatalf("GetSharedVolume(%s) failed: %v", name, err) + } + return stored +} + +func TestCreateSharedVolume_Provisioned(t *testing.T) { + ns := namespaceForTest("ns-extvol-provisioned") + tc, plugin := setupSharedVolumeTest(t, ns) + defer tc.cleanup() + + created := createSharedVolume(t, tc, "shared", ateapipb.DeleteTrigger_DELETE_TRIGGER_UNSPECIFIED) + + wantDiskName := "substrate-" + created.GetMetadata().GetUid() + gotCreated, _, _, _ := plugin.snapshot() + if diff := cmp.Diff([]string{wantDiskName}, gotCreated); diff != "" { + t.Errorf("provisioned disks mismatch (-want +got):\n%s", diff) + } + if got, want := created.GetVolumeId(), "disk/"+wantDiskName; got != want { + t.Errorf("volume_id = %q, want %q", got, want) + } + if got, want := created.GetVolumeType(), "substrate.io/mock"; got != want { + t.Errorf("volume_type = %q, want %q", got, want) + } + if got := created.GetDeleteTrigger(); got != ateapipb.DeleteTrigger_DELETE_TRIGGER_LAST_ACTOR { + t.Errorf("delete_trigger = %v, want LAST_ACTOR", got) + } + if got := created.GetAccessMode(); got != ateapipb.AccessMode_ACCESS_MODE_READ_WRITE_ONCE { + t.Errorf("access_mode = %v, want READ_WRITE_ONCE", got) + } + + if diff := cmp.Diff(created, getSharedVolume(t, tc, "shared"), protocmp.Transform()); diff != "" { + t.Errorf("GetSharedVolume mismatch (-want +got):\n%s", diff) + } + listed, err := tc.client.ListSharedVolumes(context.Background(), &ateapipb.ListSharedVolumesRequest{Atespace: testAtespace}) + if err != nil { + t.Fatalf("ListSharedVolumes failed: %v", err) + } + if diff := cmp.Diff([]*ateapipb.SharedVolume{created}, listed.GetSharedVolumes(), protocmp.Transform()); diff != "" { + t.Errorf("ListSharedVolumes mismatch (-want +got):\n%s", diff) + } +} + +func TestCreateSharedVolume_Registered(t *testing.T) { + ns := namespaceForTest("ns-extvol-registered") + tc, plugin := setupSharedVolumeTest(t, ns) + defer tc.cleanup() + + created, err := tc.client.CreateSharedVolume(context.Background(), &ateapipb.CreateSharedVolumeRequest{ + SharedVolume: &ateapipb.SharedVolume{ + Metadata: &ateapipb.ResourceMetadata{Atespace: testAtespace, Name: "adopted"}, + VolumeId: "disk/made-elsewhere", + VolumeType: "substrate.io/mock", + }, + }) + if err != nil { + t.Fatalf("CreateSharedVolume failed: %v", err) + } + if got := created.GetStatus().GetState(); got != ateapipb.SharedVolumeState_SHARED_VOLUME_STATE_READY { + t.Errorf("state = %v, want READY", got) + } + if got := created.GetDeleteTrigger(); got != ateapipb.DeleteTrigger_DELETE_TRIGGER_MANUAL { + t.Errorf("delete_trigger = %v, want MANUAL", got) + } + if gotCreated, _, _, _ := plugin.snapshot(); len(gotCreated) != 0 { + t.Errorf("provisioned %v, want nothing provisioned for a registered volume", gotCreated) + } +} + +func TestCreateSharedVolume_Rejected(t *testing.T) { + ns := namespaceForTest("ns-extvol-rejected") + tc, _ := setupSharedVolumeTest(t, ns) + defer tc.cleanup() + + first := createSharedVolume(t, tc, "shared", ateapipb.DeleteTrigger_DELETE_TRIGGER_UNSPECIFIED) + + _, err := tc.client.CreateSharedVolume(context.Background(), &ateapipb.CreateSharedVolumeRequest{ + SharedVolume: &ateapipb.SharedVolume{ + Metadata: &ateapipb.ResourceMetadata{Atespace: testAtespace, Name: "shared"}, + StorageClassName: "fast", + Capacity: "20Gi", + }, + }) + assertGrpcError(t, err, codes.AlreadyExists, + fmt.Sprintf("SharedVolume %s/shared already exists; delete it and create it again to retry", testAtespace)) + if diff := cmp.Diff(first, getSharedVolume(t, tc, "shared"), protocmp.Transform()); diff != "" { + t.Errorf("the rejected create changed the stored volume (-want +got):\n%s", diff) + } + + _, err = tc.client.CreateSharedVolume(context.Background(), &ateapipb.CreateSharedVolumeRequest{ + SharedVolume: &ateapipb.SharedVolume{ + Metadata: &ateapipb.ResourceMetadata{Atespace: testAtespace, Name: "many"}, + StorageClassName: "standard", + Capacity: "10Gi", + AccessMode: ateapipb.AccessMode_ACCESS_MODE_READ_WRITE_MANY, + }, + }) + assertGrpcErrorRegex(t, err, codes.InvalidArgument, "access_mode") +} + +func TestUpdateSharedVolume(t *testing.T) { + ns := namespaceForTest("ns-extvol-update") + tc, _ := setupSharedVolumeTest(t, ns) + defer tc.cleanup() + + created := createSharedVolume(t, tc, "shared", ateapipb.DeleteTrigger_DELETE_TRIGGER_LAST_ACTOR) + toUpdate := func(in *ateapipb.SharedVolume) *ateapipb.SharedVolume { + return &ateapipb.SharedVolume{ + Metadata: in.GetMetadata(), + DeleteTrigger: in.GetDeleteTrigger(), + AccessMode: in.GetAccessMode(), + StorageClassName: in.GetStorageClassName(), + Capacity: in.GetCapacity(), + VolumeId: in.GetVolumeId(), + VolumeType: in.GetVolumeType(), + VolumeContext: in.GetVolumeContext(), + } + } + + req := toUpdate(created) + req.DeleteTrigger = ateapipb.DeleteTrigger_DELETE_TRIGGER_MANUAL + updated, err := tc.client.UpdateSharedVolume(context.Background(), &ateapipb.UpdateSharedVolumeRequest{SharedVolume: req}) + if err != nil { + t.Fatalf("UpdateSharedVolume failed: %v", err) + } + if got := updated.GetDeleteTrigger(); got != ateapipb.DeleteTrigger_DELETE_TRIGGER_MANUAL { + t.Errorf("delete_trigger = %v, want MANUAL", got) + } + + req = toUpdate(updated) + req.VolumeId = "disk/somewhere-else" + _, err = tc.client.UpdateSharedVolume(context.Background(), &ateapipb.UpdateSharedVolumeRequest{SharedVolume: req}) + assertGrpcErrorRegex(t, err, codes.InvalidArgument, "volume_id") + + req = toUpdate(updated) + req.AccessMode = ateapipb.AccessMode_ACCESS_MODE_READ_WRITE_MANY + _, err = tc.client.UpdateSharedVolume(context.Background(), &ateapipb.UpdateSharedVolumeRequest{SharedVolume: req}) + assertGrpcErrorRegex(t, err, codes.InvalidArgument, "access_mode") +} + +func TestDeleteSharedVolume(t *testing.T) { + ns := namespaceForTest("ns-extvol-delete") + tc, plugin := setupSharedVolumeTest(t, ns) + defer tc.cleanup() + + created := createSharedVolume(t, tc, "shared", ateapipb.DeleteTrigger_DELETE_TRIGGER_MANUAL) + ref := &ateapipb.ObjectRef{Atespace: testAtespace, Name: "shared"} + + _, err := tc.client.DeleteSharedVolume(context.Background(), &ateapipb.DeleteSharedVolumeRequest{ + SharedVolume: ref, + Options: &ateapipb.DeleteOptions{Version: created.GetMetadata().GetVersion() + 1}, + }) + assertGrpcError(t, err, codes.Aborted, "concurrent update conflict, please retry") + + if _, err := tc.client.DeleteSharedVolume(context.Background(), &ateapipb.DeleteSharedVolumeRequest{SharedVolume: ref}); err != nil { + t.Fatalf("DeleteSharedVolume failed: %v", err) + } + if _, deleted, _, _ := plugin.snapshot(); !cmp.Equal([]string{created.GetVolumeId()}, deleted) { + t.Errorf("deleted disks = %v, want %v", deleted, []string{created.GetVolumeId()}) + } + _, err = tc.client.DeleteSharedVolume(context.Background(), &ateapipb.DeleteSharedVolumeRequest{SharedVolume: ref}) + assertGrpcError(t, err, codes.NotFound, fmt.Sprintf("SharedVolume %s/shared not found", testAtespace)) +} + +// refStates maps each actor referencing volumeName to whether its reference is active. +func refStates(t *testing.T, tc *testContext, volumeName string) map[string]bool { + t.Helper() + states := make(map[string]bool) + for _, ref := range getSharedVolume(t, tc, volumeName).GetStatus().GetRefs() { + states[ref.GetActorName()] = ref.GetActive() + } + return states +} + +func borrowedMount(t *testing.T, tc *testContext, actorName, volumeName string) *ateapipb.ExternalVolume { + t.Helper() + actor, err := tc.client.GetActor(context.Background(), &ateapipb.GetActorRequest{ + Actor: &ateapipb.ObjectRef{Atespace: testAtespace, Name: actorName}, + }) + if err != nil { + t.Fatalf("GetActor(%s) failed: %v", actorName, err) + } + for _, vol := range actor.GetStatus().GetExternalVolumes() { + if vol.GetName() == volumeName { + return vol + } + } + return nil +} + +func createBorrowingActor(t *testing.T, tc *testContext, name string, bindings map[string]string) { + t.Helper() + if _, err := tc.client.CreateActor(context.Background(), &ateapipb.CreateActorRequest{Actor: &ateapipb.Actor{ + Metadata: &ateapipb.ResourceMetadata{Atespace: testAtespace, Name: name}, + ActorTemplate: &ateapipb.ObjectRef{Atespace: testAtespace, Name: "tmpl1"}, + SharedVolumeBindings: bindings, + }}); err != nil { + t.Fatalf("CreateActor(%s) failed: %v", name, err) + } +} + +func actorState(t *testing.T, tc *testContext, name string) ateapipb.ActorState { + t.Helper() + actor, err := tc.client.GetActor(context.Background(), &ateapipb.GetActorRequest{ + Actor: &ateapipb.ObjectRef{Atespace: testAtespace, Name: name}, + }) + if err != nil { + t.Fatalf("GetActor(%s) failed: %v", name, err) + } + return actor.GetStatus().GetState() +} + +func TestSharedVolume_SequentialHandoff(t *testing.T) { + ns := namespaceForTest("ns-extvol-handoff") + tc, plugin := setupSharedVolumeTest(t, ns) + defer tc.cleanup() + + shared := createSharedVolume(t, tc, "handoff", ateapipb.DeleteTrigger_DELETE_TRIGGER_LAST_ACTOR) + createTemplateWithVolumes(t, tc, ns, + []*ateapipb.Volume{{Name: "work", External: &ateapipb.ExternalVolumeSource{SharedVolume: &ateapipb.SharedVolumeRef{Name: "handoff"}}}}, + []*ateapipb.VolumeMount{{Name: "work", MountPath: "/mnt/work"}}) + workerName := createWorkerPod(t, tc, ns, "worker-1", "node1", "pool1") + ctx := context.Background() + + createBorrowingActor(t, tc, "producer", nil) + createBorrowingActor(t, tc, "consumer", nil) + if diff := cmp.Diff(map[string]bool{"producer": false, "consumer": false}, refStates(t, tc, "handoff")); diff != "" { + t.Errorf("refs after create mismatch (-want +got):\n%s", diff) + } + + waitForWorkerAvailable(t, tc, workerName) + if _, err := tc.client.ResumeActor(ctx, &ateapipb.ResumeActorRequest{Actor: &ateapipb.ObjectRef{Atespace: testAtespace, Name: "producer"}}); err != nil { + t.Fatalf("ResumeActor(producer) failed: %v", err) + } + if diff := cmp.Diff(map[string]bool{"producer": true, "consumer": false}, refStates(t, tc, "handoff")); diff != "" { + t.Errorf("refs while the producer runs mismatch (-want +got):\n%s", diff) + } + if _, err := tc.client.SuspendActor(ctx, &ateapipb.SuspendActorRequest{Actor: &ateapipb.ObjectRef{Atespace: testAtespace, Name: "producer"}}); err != nil { + t.Fatalf("SuspendActor(producer) failed: %v", err) + } + if diff := cmp.Diff(map[string]bool{"producer": false, "consumer": false}, refStates(t, tc, "handoff")); diff != "" { + t.Errorf("refs after the producer stops mismatch (-want +got):\n%s", diff) + } + + waitForWorkerAvailable(t, tc, workerName) + if _, err := tc.client.ResumeActor(ctx, &ateapipb.ResumeActorRequest{Actor: &ateapipb.ObjectRef{Atespace: testAtespace, Name: "consumer"}}); err != nil { + t.Fatalf("ResumeActor(consumer) failed: %v", err) + } + if diff := cmp.Diff(map[string]bool{"producer": false, "consumer": true}, refStates(t, tc, "handoff")); diff != "" { + t.Errorf("refs while the consumer runs mismatch (-want +got):\n%s", diff) + } + for _, name := range []string{"producer", "consumer"} { + if got, want := borrowedMount(t, tc, name, "work").GetStorageVolumeId(), shared.GetVolumeId(); got != want { + t.Errorf("actor %s mounted %q, want the shared volume %q", name, got, want) + } + } + created, deleted, attached, detached := plugin.snapshot() + if diff := cmp.Diff([]string{shared.GetVolumeId() + "@node1", shared.GetVolumeId() + "@node1"}, attached); diff != "" { + t.Errorf("attached volumes mismatch (-want +got):\n%s", diff) + } + if diff := cmp.Diff([]string{shared.GetVolumeId() + "@node1"}, detached); diff != "" { + t.Errorf("detached volumes mismatch (-want +got):\n%s", diff) + } + if len(created) != 1 || len(deleted) != 0 { + t.Errorf("created %v deleted %v, want one provisioning and no deletion", created, deleted) + } + + if _, err := tc.client.SuspendActor(ctx, &ateapipb.SuspendActorRequest{Actor: &ateapipb.ObjectRef{Atespace: testAtespace, Name: "consumer"}}); err != nil { + t.Fatalf("SuspendActor(consumer) failed: %v", err) + } + for _, name := range []string{"producer", "consumer"} { + if _, err := tc.client.DeleteActor(ctx, &ateapipb.DeleteActorRequest{Actor: &ateapipb.ObjectRef{Atespace: testAtespace, Name: name}}); err != nil { + t.Fatalf("DeleteActor(%s) failed: %v", name, err) + } + } + _, err := tc.client.GetSharedVolume(ctx, &ateapipb.GetSharedVolumeRequest{SharedVolume: &ateapipb.ObjectRef{Atespace: testAtespace, Name: "handoff"}}) + assertGrpcError(t, err, codes.NotFound, fmt.Sprintf("SharedVolume %s/handoff not found", testAtespace)) + if _, deleted, _, _ := plugin.snapshot(); !cmp.Equal([]string{shared.GetVolumeId()}, deleted) { + t.Errorf("deleted volumes = %v, want the LAST_ACTOR trigger to delete %q", deleted, shared.GetVolumeId()) + } +} + +func TestSharedVolume_HeldByAnotherActor(t *testing.T) { + ns := namespaceForTest("ns-extvol-held") + tc, plugin := setupSharedVolumeTest(t, ns) + defer tc.cleanup() + + shared := createSharedVolume(t, tc, "held", ateapipb.DeleteTrigger_DELETE_TRIGGER_MANUAL) + createTemplateWithVolumes(t, tc, ns, + []*ateapipb.Volume{{Name: "work", External: &ateapipb.ExternalVolumeSource{SharedVolume: &ateapipb.SharedVolumeRef{Name: "held"}}}}, + []*ateapipb.VolumeMount{{Name: "work", MountPath: "/mnt/work"}}) + firstWorker := createWorkerPod(t, tc, ns, "worker-1", "node1", "pool1") + ctx := context.Background() + + createBorrowingActor(t, tc, "producer", nil) + createBorrowingActor(t, tc, "consumer", nil) + waitForWorkerAvailable(t, tc, firstWorker) + if _, err := tc.client.ResumeActor(ctx, &ateapipb.ResumeActorRequest{Actor: &ateapipb.ObjectRef{Atespace: testAtespace, Name: "producer"}}); err != nil { + t.Fatalf("ResumeActor(producer) failed: %v", err) + } + + secondWorker := createWorkerPod(t, tc, ns, "worker-2", "node1", "pool1") + waitForWorkerAvailable(t, tc, secondWorker) + _, err := tc.client.ResumeActor(ctx, &ateapipb.ResumeActorRequest{Actor: &ateapipb.ObjectRef{Atespace: testAtespace, Name: "consumer"}}) + assertGrpcErrorRegex(t, err, codes.FailedPrecondition, `SharedVolume .*held is already held by actor "producer"`) + if diff := cmp.Diff(map[string]bool{"producer": true, "consumer": false}, refStates(t, tc, "held")); diff != "" { + t.Errorf("refs after the refused resume mismatch (-want +got):\n%s", diff) + } + if got := actorState(t, tc, "consumer"); got != ateapipb.ActorState_ACTOR_STATE_SUSPENDED { + t.Errorf("consumer is in %v, want it left SUSPENDED", got) + } + if _, _, attached, _ := plugin.snapshot(); !cmp.Equal([]string{shared.GetVolumeId() + "@node1"}, attached) { + t.Errorf("attached volumes = %v, want only the producer's attach", attached) + } + + if _, err := tc.client.PauseActor(ctx, &ateapipb.PauseActorRequest{Actor: &ateapipb.ObjectRef{Atespace: testAtespace, Name: "producer"}}); err != nil { + t.Fatalf("PauseActor(producer) failed: %v", err) + } + if _, err := tc.client.ResumeActor(ctx, &ateapipb.ResumeActorRequest{Actor: &ateapipb.ObjectRef{Atespace: testAtespace, Name: "consumer"}}); err != nil { + t.Fatalf("ResumeActor(consumer) after the handoff failed: %v", err) + } + if diff := cmp.Diff(map[string]bool{"producer": false, "consumer": true}, refStates(t, tc, "held")); diff != "" { + t.Errorf("refs after the handoff mismatch (-want +got):\n%s", diff) + } +} + +func TestDeleteSharedVolume_Referenced(t *testing.T) { + ns := namespaceForTest("ns-extvol-referenced") + tc, plugin := setupSharedVolumeTest(t, ns) + defer tc.cleanup() + + createSharedVolume(t, tc, "shared", ateapipb.DeleteTrigger_DELETE_TRIGGER_MANUAL) + createTemplateWithVolumes(t, tc, ns, + []*ateapipb.Volume{{Name: "work", External: &ateapipb.ExternalVolumeSource{SharedVolume: &ateapipb.SharedVolumeRef{Name: "shared"}}}}, + []*ateapipb.VolumeMount{{Name: "work", MountPath: "/mnt/work"}}) + ctx := context.Background() + ref := &ateapipb.ObjectRef{Atespace: testAtespace, Name: "shared"} + + createBorrowingActor(t, tc, "borrower", nil) + _, err := tc.client.DeleteSharedVolume(ctx, &ateapipb.DeleteSharedVolumeRequest{SharedVolume: ref}) + assertGrpcErrorRegex(t, err, codes.FailedPrecondition, `SharedVolume .*shared is still referenced by 1 actor\(s\), starting with "borrower"`) + + if _, err := tc.client.DeleteActor(ctx, &ateapipb.DeleteActorRequest{Actor: &ateapipb.ObjectRef{Atespace: testAtespace, Name: "borrower"}}); err != nil { + t.Fatalf("DeleteActor failed: %v", err) + } + if got := refStates(t, tc, "shared"); len(got) != 0 { + t.Errorf("refs after the actor is deleted = %v, want none", got) + } + if _, deleted, _, _ := plugin.snapshot(); len(deleted) != 0 { + t.Errorf("deleted %v, want a MANUAL volume left in place", deleted) + } + if _, err := tc.client.DeleteSharedVolume(ctx, &ateapipb.DeleteSharedVolumeRequest{SharedVolume: ref}); err != nil { + t.Fatalf("DeleteSharedVolume failed: %v", err) + } +} + +func TestCreateActor_SharedVolumeBindings(t *testing.T) { + ns := namespaceForTest("ns-extvol-bindings") + tc, _ := setupSharedVolumeTest(t, ns) + defer tc.cleanup() + + shared := createSharedVolume(t, tc, "ctx-42", ateapipb.DeleteTrigger_DELETE_TRIGGER_MANUAL) + createTemplateWithVolumes(t, tc, ns, + []*ateapipb.Volume{{Name: "work", External: &ateapipb.ExternalVolumeSource{SharedVolume: &ateapipb.SharedVolumeRef{}}}}, + []*ateapipb.VolumeMount{{Name: "work", MountPath: "/mnt/work"}}) + ctx := context.Background() + create := func(name string, bindings map[string]string) error { + _, err := tc.client.CreateActor(ctx, &ateapipb.CreateActorRequest{Actor: &ateapipb.Actor{ + Metadata: &ateapipb.ResourceMetadata{Atespace: testAtespace, Name: name}, + ActorTemplate: &ateapipb.ObjectRef{Atespace: testAtespace, Name: "tmpl1"}, + SharedVolumeBindings: bindings, + }}) + return err + } + + assertGrpcErrorRegex(t, create("unbound", nil), codes.InvalidArgument, `shared_volume_bindings must bind volume "work"`) + assertGrpcErrorRegex(t, create("stray", map[string]string{"work": "ctx-42", "other": "ctx-42"}), codes.InvalidArgument, `shared_volume_bindings\["other"\]`) + assertGrpcErrorRegex(t, create("missing", map[string]string{"work": "ctx-41"}), codes.FailedPrecondition, fmt.Sprintf("SharedVolume %s/ctx-41 not found", testAtespace)) + + if err := create("bound", map[string]string{"work": "ctx-42"}); err != nil { + t.Fatalf("CreateActor(bound) failed: %v", err) + } + if got, want := borrowedMount(t, tc, "bound", "work").GetStorageVolumeId(), shared.GetVolumeId(); got != want { + t.Errorf("bound actor mounts %q, want %q", got, want) + } + if diff := cmp.Diff(map[string]bool{"bound": false}, refStates(t, tc, "ctx-42")); diff != "" { + t.Errorf("refs mismatch (-want +got):\n%s", diff) + } +} diff --git a/cmd/ateapi/internal/controlapi/service.go b/cmd/ateapi/internal/controlapi/service.go index 750c89f4c6..581e06f49e 100644 --- a/cmd/ateapi/internal/controlapi/service.go +++ b/cmd/ateapi/internal/controlapi/service.go @@ -46,6 +46,7 @@ type RPCService struct { csiDriverConfigLister listersv1alpha1.CSIDriverConfigLister actorWorkflow *ActorWorkflow workerWorkflow *WorkerWorkflow + sharedVolumeWorkflow *SharedVolumeWorkflow instruments *Instruments mu sync.RWMutex volumePlugins map[string]volume.VolumePluginControlPlane @@ -103,7 +104,8 @@ func NewRPCService( actorIDJWTPool: actorIDJWTPool, actorIDCAPool: actorIDCAPool, } - s.actorWorkflow = NewActorWorkflow(impl, workerCache, dialer, sandboxConfigLister, storageClassLister, instruments, egressGatewayAddress, s, objectStorePlugin) + s.sharedVolumeWorkflow = NewSharedVolumeWorkflow(impl, storageClassLister, s) + s.actorWorkflow = NewActorWorkflow(impl, workerCache, dialer, sandboxConfigLister, storageClassLister, instruments, egressGatewayAddress, s, objectStorePlugin, s.sharedVolumeWorkflow) s.workerWorkflow = NewWorkerWorkflow(impl) return s } @@ -123,6 +125,11 @@ type serviceStore interface { ListTags(ctx context.Context, atespace string, opts store.ListOptions) (store.ListResponse[*ateapipb.Tag], error) UpdateTag(ctx context.Context, tagRef resources.TagRef, precondition store.Precondition, mutate func(toUpdate *ateapipb.Tag) error) (*ateapipb.Tag, error) DeleteTag(ctx context.Context, tagRef resources.TagRef, precondition store.DeletePreconditions) (*ateapipb.Tag, error) + CreateSharedVolume(ctx context.Context, volume *ateapipb.SharedVolume) (*ateapipb.SharedVolume, error) + GetSharedVolume(ctx context.Context, volumeRef resources.SharedVolumeRef) (*ateapipb.SharedVolume, error) + ListSharedVolumes(ctx context.Context, atespace string, opts store.ListOptions) (store.ListResponse[*ateapipb.SharedVolume], error) + UpdateSharedVolume(ctx context.Context, volumeRef resources.SharedVolumeRef, precondition store.Precondition, mutate func(toUpdate *ateapipb.SharedVolume) error) (*ateapipb.SharedVolume, error) + DeleteSharedVolume(ctx context.Context, volumeRef resources.SharedVolumeRef, precondition store.DeletePreconditions) (*ateapipb.SharedVolume, error) CreateAtespace(ctx context.Context, atespace *ateapipb.Atespace) (*ateapipb.Atespace, error) GetAtespace(ctx context.Context, name string) (*ateapipb.Atespace, error) ListAtespaces(ctx context.Context, opts store.ListOptions) (store.ListResponse[*ateapipb.Atespace], error) diff --git a/cmd/ateapi/internal/controlapi/shared_volume.go b/cmd/ateapi/internal/controlapi/shared_volume.go new file mode 100644 index 0000000000..65d47f2eb8 --- /dev/null +++ b/cmd/ateapi/internal/controlapi/shared_volume.go @@ -0,0 +1,151 @@ +// 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. + +package controlapi + +import ( + "context" + "errors" + "fmt" + + "go.ate.dev/substrate/cmd/ateapi/internal/apivalidation" + "go.ate.dev/substrate/cmd/ateapi/internal/defaults" + "go.ate.dev/substrate/cmd/ateapi/internal/store" + "go.ate.dev/substrate/internal/apierror" + "go.ate.dev/substrate/internal/resources" + "go.ate.dev/substrate/pkg/proto/ateapipb" + "google.golang.org/protobuf/proto" + "k8s.io/apimachinery/pkg/util/validation/field" +) + +func (s *RPCService) CreateSharedVolume(ctx context.Context, req *ateapipb.CreateSharedVolumeRequest) (*ateapipb.SharedVolume, error) { + inVolume := req.SharedVolume + if inVolume != nil { + scrubResourceMetadataForCreate(inVolume.Metadata) + inVolume.Status = nil + defaults.Apply(inVolume) + } + if errs := apivalidation.ValidateCreateSharedVolumeRequest(ctx, req); len(errs) > 0 { + return nil, resources.ToAPIError(errs) + } + return s.sharedVolumeWorkflow.CreateSharedVolume(ctx, inVolume) +} + +func (s *ServiceImpl) CreateSharedVolume(ctx context.Context, volume *ateapipb.SharedVolume) (*ateapipb.SharedVolume, error) { + return s.store.CreateSharedVolume(ctx, volume) +} + +func (s *RPCService) GetSharedVolume(ctx context.Context, req *ateapipb.GetSharedVolumeRequest) (*ateapipb.SharedVolume, error) { + if errs := apivalidation.ValidateGetSharedVolumeRequest(ctx, req); len(errs) > 0 { + return nil, resources.ToAPIError(errs) + } + volumeRef := resources.SharedVolumeRefFromObjectRef(req.GetSharedVolume()) + volume, err := s.impl.GetSharedVolume(ctx, volumeRef) + if err != nil { + if errors.Is(err, store.ErrNotFound) { + return nil, apierror.NotFound("SharedVolume %s not found", volumeRef) + } + return nil, fmt.Errorf("while getting shared volume: %w", err) + } + return volume, nil +} + +func (s *ServiceImpl) GetSharedVolume(ctx context.Context, volumeRef resources.SharedVolumeRef) (*ateapipb.SharedVolume, error) { + return s.store.GetSharedVolume(ctx, volumeRef) +} + +func (s *RPCService) ListSharedVolumes(ctx context.Context, req *ateapipb.ListSharedVolumesRequest) (*ateapipb.ListSharedVolumesResponse, error) { + if errs := apivalidation.ValidateListSharedVolumesRequest(ctx, req); len(errs) > 0 { + return nil, resources.ToAPIError(errs) + } + page, err := s.impl.ListSharedVolumes(ctx, req.GetAtespace(), toStoreListOptions(req.GetOptions())) + if err != nil { + return nil, mapListError(fmt.Errorf("while listing shared volumes: %w", err)) + } + return &ateapipb.ListSharedVolumesResponse{SharedVolumes: page.Items, NextPageToken: page.NextPageToken}, nil +} + +func (s *ServiceImpl) ListSharedVolumes(ctx context.Context, atespace string, opts store.ListOptions) (store.ListResponse[*ateapipb.SharedVolume], error) { + return s.store.ListSharedVolumes(ctx, atespace, opts) +} + +var errSharedVolumePending = errors.New("shared volume is still being created") + +func (s *RPCService) UpdateSharedVolume(ctx context.Context, req *ateapipb.UpdateSharedVolumeRequest) (*ateapipb.SharedVolume, error) { + inVolume := req.SharedVolume + if inVolume != nil { + scrubResourceMetadataForUpdate(inVolume.Metadata) + inVolume.Status = nil + } + if errs := apivalidation.ValidateUpdateSharedVolumeRequest(ctx, req); len(errs) > 0 { + return nil, resources.ToAPIError(errs) + } + in := req.GetSharedVolume() + volumeRef := resources.SharedVolumeRefFromSharedVolume(in) + + stored, err := s.impl.UpdateSharedVolume(ctx, volumeRef, store.PreconditionFrom(in), func(toUpdate *ateapipb.SharedVolume) error { + if toUpdate.GetStatus().GetState() != ateapipb.SharedVolumeState_SHARED_VOLUME_STATE_READY { + return errSharedVolumePending + } + metadata, volumeStatus := toUpdate.GetMetadata(), toUpdate.GetStatus() + proto.Reset(toUpdate) + proto.Merge(toUpdate, in) + toUpdate.Metadata, toUpdate.Status = metadata, volumeStatus + defaults.Apply(toUpdate) + return nil + }) + if err != nil { + if errors.Is(err, errSharedVolumePending) { + return nil, apierror.FailedPrecondition("SharedVolume %s is still being created", volumeRef) + } + if errors.Is(err, store.ErrImmutableField) || errors.Is(err, store.ErrPreconditionRequired) { + return nil, apierror.InvalidArgument("while updating shared volume %s: %v", volumeRef, err) + } + if errors.Is(err, store.ErrVersionConflict) { + return nil, apierror.Aborted("concurrent update conflict, please retry") + } + if errors.Is(err, store.ErrUIDConflict) { + return nil, apierror.Aborted("SharedVolume %s not found with uid %s", volumeRef, in.GetMetadata().GetUid()) + } + if errors.Is(err, store.ErrNotFound) { + return nil, apierror.NotFound("SharedVolume %s not found", volumeRef) + } + return nil, fmt.Errorf("while updating shared volume: %w", err) + } + return stored, nil +} + +func (s *ServiceImpl) UpdateSharedVolume(ctx context.Context, volumeRef resources.SharedVolumeRef, precondition store.Precondition, mutate func(toUpdate *ateapipb.SharedVolume) error) (*ateapipb.SharedVolume, error) { + return s.store.UpdateSharedVolume(ctx, volumeRef, precondition, func(toUpdate *ateapipb.SharedVolume) error { + oldVal := proto.CloneOf(toUpdate) + if err := mutate(toUpdate); err != nil { + return err + } + if errs := apivalidation.ValidateSharedVolumeUpdate(ctx, field.NewPath("shared_volume"), toUpdate, oldVal); len(errs) > 0 { + return resources.ToAPIError(errs) + } + return nil + }) +} + +func (s *RPCService) DeleteSharedVolume(ctx context.Context, req *ateapipb.DeleteSharedVolumeRequest) (*ateapipb.SharedVolume, error) { + if errs := apivalidation.ValidateDeleteSharedVolumeRequest(ctx, req); len(errs) > 0 { + return nil, resources.ToAPIError(errs) + } + return s.sharedVolumeWorkflow.DeleteSharedVolume(ctx, resources.SharedVolumeRefFromObjectRef(req.GetSharedVolume()), toDeletePreconditions(req.GetOptions())) +} + +func (s *ServiceImpl) DeleteSharedVolume(ctx context.Context, volumeRef resources.SharedVolumeRef, precondition store.DeletePreconditions) (*ateapipb.SharedVolume, error) { + return s.store.DeleteSharedVolume(ctx, volumeRef, precondition) +} diff --git a/cmd/ateapi/internal/controlapi/shared_volume_golden_test.go b/cmd/ateapi/internal/controlapi/shared_volume_golden_test.go new file mode 100644 index 0000000000..2884d4d773 --- /dev/null +++ b/cmd/ateapi/internal/controlapi/shared_volume_golden_test.go @@ -0,0 +1,45 @@ +// 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. + +package controlapi + +import ( + "testing" + + "go.ate.dev/substrate/pkg/proto/ateapipb" +) + +func TestTemplateHasSharedVolumeRef(t *testing.T) { + tests := []struct { + name string + volumes []*ateapipb.Volume + want bool + }{ + {name: "no volumes"}, + {name: "durable dir only", volumes: []*ateapipb.Volume{{Name: "home", DurableDir: &ateapipb.DurableDirVolumeSource{}}}}, + {name: "shared volume template", volumes: []*ateapipb.Volume{{Name: "owned", External: &ateapipb.ExternalVolumeSource{Template: &ateapipb.ExternalVolumeTemplate{StorageClassName: "standard"}}}}}, + {name: "named ref", volumes: []*ateapipb.Volume{{Name: "shared", External: &ateapipb.ExternalVolumeSource{SharedVolume: &ateapipb.SharedVolumeRef{Name: "ctx"}}}}, want: true}, + {name: "unnamed ref after other volumes", volumes: []*ateapipb.Volume{ + {Name: "home", DurableDir: &ateapipb.DurableDirVolumeSource{}}, + {Name: "shared", External: &ateapipb.ExternalVolumeSource{SharedVolume: &ateapipb.SharedVolumeRef{}}}, + }, want: true}, + } + for _, tc := range tests { + t.Run(tc.name, func(t *testing.T) { + if got := templateHasSharedVolumeRef(&ateapipb.ActorTemplate{Volumes: tc.volumes}); got != tc.want { + t.Errorf("templateHasSharedVolumeRef() = %v, want %v", got, tc.want) + } + }) + } +} diff --git a/cmd/ateapi/internal/controlapi/shared_volume_refs.go b/cmd/ateapi/internal/controlapi/shared_volume_refs.go new file mode 100644 index 0000000000..124e85608a --- /dev/null +++ b/cmd/ateapi/internal/controlapi/shared_volume_refs.go @@ -0,0 +1,263 @@ +// 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. + +package controlapi + +import ( + "context" + "errors" + "fmt" + "slices" + + "go.ate.dev/substrate/cmd/ateapi/internal/store" + "go.ate.dev/substrate/internal/apierror" + "go.ate.dev/substrate/internal/resources" + "go.ate.dev/substrate/pkg/proto/ateapipb" + "google.golang.org/grpc/codes" + "google.golang.org/grpc/status" +) + +type sharedVolumeRefStore interface { + GetSharedVolume(ctx context.Context, volumeRef resources.SharedVolumeRef) (*ateapipb.SharedVolume, error) + UpdateSharedVolume(ctx context.Context, volumeRef resources.SharedVolumeRef, precondition store.Precondition, mutate func(toUpdate *ateapipb.SharedVolume) error) (*ateapipb.SharedVolume, error) +} + +type sharedVolumeDeleter interface { + DeleteSharedVolume(ctx context.Context, volumeRef resources.SharedVolumeRef, precondition store.DeletePreconditions) (*ateapipb.SharedVolume, error) +} + +func templateHasSharedVolumeRef(template *ateapipb.ActorTemplate) bool { + for _, vol := range template.GetVolumes() { + if vol.GetExternal().GetSharedVolume() != nil { + return true + } + } + return false +} + +// resolveSharedVolumeRefs checks the actor's bindings against the template +// and returns a mount for every SharedVolume the actor references. +func resolveSharedVolumeRefs(ctx context.Context, st sharedVolumeRefStore, actor *ateapipb.Actor, template *ateapipb.ActorTemplate) ([]*ateapipb.ExternalVolume, error) { + bindings := actor.GetSharedVolumeBindings() + unnamed := make(map[string]bool) + for _, vol := range template.GetVolumes() { + if vol.GetExternal().GetSharedVolume() != nil && vol.GetExternal().GetSharedVolume().GetName() == "" { + unnamed[vol.GetName()] = true + } + } + for slot := range bindings { + if !unnamed[slot] { + return nil, apierror.InvalidArgument("shared_volume_bindings[%q] does not name an unnamed external.shared_volume in the template", slot) + } + } + + var mounts []*ateapipb.ExternalVolume + for _, vol := range template.GetVolumes() { + ref := vol.GetExternal().GetSharedVolume() + if ref == nil { + continue + } + name := ref.GetName() + if name == "" { + if name = bindings[vol.GetName()]; name == "" { + return nil, apierror.InvalidArgument("shared_volume_bindings must bind volume %q", vol.GetName()) + } + } + volume, err := getReadySharedVolume(ctx, st, resources.SharedVolumeRef{Atespace: actor.GetMetadata().GetAtespace(), Name: name}) + if err != nil { + return nil, err + } + mounts = append(mounts, sharedVolumeMount(vol.GetName(), volume)) + } + return mounts, nil +} + +func getReadySharedVolume(ctx context.Context, st sharedVolumeRefStore, volumeRef resources.SharedVolumeRef) (*ateapipb.SharedVolume, error) { + volume, err := st.GetSharedVolume(ctx, volumeRef) + if err != nil { + if errors.Is(err, store.ErrNotFound) { + return nil, apierror.FailedPrecondition("SharedVolume %s not found", volumeRef) + } + return nil, fmt.Errorf("while getting shared volume %s: %w", volumeRef, err) + } + if state := volume.GetStatus().GetState(); state != ateapipb.SharedVolumeState_SHARED_VOLUME_STATE_READY { + return nil, apierror.FailedPrecondition("SharedVolume %s is not ready (state %s)", volumeRef, state) + } + return volume, nil +} + +func sharedVolumeMount(templateVolumeName string, volume *ateapipb.SharedVolume) *ateapipb.ExternalVolume { + return &ateapipb.ExternalVolume{ + Name: templateVolumeName, + SharedVolumeName: volume.GetMetadata().GetName(), + StorageVolumeId: volume.GetVolumeId(), + VolumeType: volume.GetVolumeType(), + VolumeContext: volume.GetVolumeContext(), + } +} + +func borrowedVolumeRefs(actor *ateapipb.Actor) []resources.SharedVolumeRef { + var refs []resources.SharedVolumeRef + for _, vol := range actor.GetStatus().GetExternalVolumes() { + if vol.GetSharedVolumeName() != "" { + refs = append(refs, resources.SharedVolumeRef{Atespace: actor.GetMetadata().GetAtespace(), Name: vol.GetSharedVolumeName()}) + } + } + return refs +} + +func actorRefOf(actor *ateapipb.Actor, active bool) *ateapipb.ActorRef { + return &ateapipb.ActorRef{ + ActorUid: actor.GetMetadata().GetUid(), + ActorName: actor.GetMetadata().GetName(), + Active: active, + } +} + +func findActorRef(volume *ateapipb.SharedVolume, actorUID string) *ateapipb.ActorRef { + for _, ref := range volume.GetStatus().GetRefs() { + if ref.GetActorUid() == actorUID { + return ref + } + } + return nil +} + +func setActorRef(volume *ateapipb.SharedVolume, ref *ateapipb.ActorRef) { + if existing := findActorRef(volume, ref.GetActorUid()); existing != nil { + existing.ActorName, existing.Active = ref.GetActorName(), ref.GetActive() + return + } + if volume.Status == nil { + volume.Status = &ateapipb.SharedVolumeStatus{} + } + volume.Status.Refs = append(volume.Status.Refs, ref) +} + +// updateSharedVolumeRefs applies mutate under the volume's version guard. +// TODO: move references to their own table so claims do not contend on one row. +func updateSharedVolumeRefs(ctx context.Context, st sharedVolumeRefStore, volumeRef resources.SharedVolumeRef, mutate func(*ateapipb.SharedVolume) error) (*ateapipb.SharedVolume, error) { + volume, err := st.GetSharedVolume(ctx, volumeRef) + if err != nil { + return nil, err + } + return st.UpdateSharedVolume(ctx, volumeRef, store.PreconditionFrom(volume), mutate) +} + +// bindSharedVolumes records an inactive reference for every SharedVolume +// the actor mounts. +func bindSharedVolumes(ctx context.Context, st sharedVolumeRefStore, actor *ateapipb.Actor) error { + var errs []error + for _, volumeRef := range borrowedVolumeRefs(actor) { + _, err := updateSharedVolumeRefs(ctx, st, volumeRef, func(toUpdate *ateapipb.SharedVolume) error { + if findActorRef(toUpdate, actor.GetMetadata().GetUid()) == nil { + setActorRef(toUpdate, actorRefOf(actor, false)) + } + return nil + }) + if err != nil { + errs = append(errs, fmt.Errorf("while binding shared volume %s: %w", volumeRef, err)) + } + } + return errors.Join(errs...) +} + +var errSharedVolumeHeld = errors.New("shared volume is held by another actor") + +// claimSharedVolumes marks the actor's references active and refreshes the +// recorded mounts. A READ_WRITE_ONCE volume another actor holds is refused. +// TODO: let READ_WRITE_MANY volumes carry several active references. +func claimSharedVolumes(ctx context.Context, st sharedVolumeRefStore, actor *ateapipb.Actor) ([]*ateapipb.ExternalVolume, error) { + volumes := slices.Clone(actor.GetStatus().GetExternalVolumes()) + actorUID := actor.GetMetadata().GetUid() + for i, mount := range volumes { + if mount.GetSharedVolumeName() == "" { + continue + } + volumeRef := resources.SharedVolumeRef{Atespace: actor.GetMetadata().GetAtespace(), Name: mount.GetSharedVolumeName()} + var holder string + claimed, err := updateSharedVolumeRefs(ctx, st, volumeRef, func(toUpdate *ateapipb.SharedVolume) error { + if toUpdate.GetStatus().GetState() != ateapipb.SharedVolumeState_SHARED_VOLUME_STATE_READY { + return fmt.Errorf("shared volume is not ready (state %s)", toUpdate.GetStatus().GetState()) + } + for _, ref := range toUpdate.GetStatus().GetRefs() { + if ref.GetActive() && ref.GetActorUid() != actorUID { + holder = ref.GetActorName() + return errSharedVolumeHeld + } + } + setActorRef(toUpdate, actorRefOf(actor, true)) + return nil + }) + switch { + case err == nil: + case errors.Is(err, errSharedVolumeHeld): + return nil, apierror.FailedPrecondition("SharedVolume %s is already held by actor %q; pause or suspend it before resuming another actor", volumeRef, holder) + case errors.Is(err, store.ErrNotFound): + return nil, apierror.FailedPrecondition("SharedVolume %s not found", volumeRef) + case errors.Is(err, store.ErrVersionConflict), errors.Is(err, store.ErrUIDConflict): + return nil, apierror.Aborted("concurrent update conflict, please retry") + default: + return nil, apierror.FailedPrecondition("SharedVolume %s: %v", volumeRef, err) + } + volumes[i] = sharedVolumeMount(mount.GetName(), claimed) + } + return volumes, nil +} + +// deactivateSharedVolumes marks the actor's references inactive. +func deactivateSharedVolumes(ctx context.Context, st sharedVolumeRefStore, actor *ateapipb.Actor) error { + var errs []error + for _, volumeRef := range borrowedVolumeRefs(actor) { + _, err := updateSharedVolumeRefs(ctx, st, volumeRef, func(toUpdate *ateapipb.SharedVolume) error { + if ref := findActorRef(toUpdate, actor.GetMetadata().GetUid()); ref != nil { + ref.Active = false + } + return nil + }) + if err != nil && !errors.Is(err, store.ErrNotFound) { + errs = append(errs, fmt.Errorf("while deactivating shared volume %s: %w", volumeRef, err)) + } + } + return errors.Join(errs...) +} + +// releaseSharedVolumes drops the actor's references and deletes each volume +// left without references whose delete trigger is LAST_ACTOR. +func releaseSharedVolumes(ctx context.Context, st sharedVolumeRefStore, deleter sharedVolumeDeleter, actor *ateapipb.Actor) error { + var errs []error + for _, volumeRef := range borrowedVolumeRefs(actor) { + released, err := updateSharedVolumeRefs(ctx, st, volumeRef, func(toUpdate *ateapipb.SharedVolume) error { + toUpdate.Status.Refs = slices.DeleteFunc(toUpdate.GetStatus().GetRefs(), func(ref *ateapipb.ActorRef) bool { + return ref.GetActorUid() == actor.GetMetadata().GetUid() + }) + return nil + }) + if errors.Is(err, store.ErrNotFound) { + continue + } + if err != nil { + errs = append(errs, fmt.Errorf("while releasing shared volume %s: %w", volumeRef, err)) + continue + } + if released.GetDeleteTrigger() != ateapipb.DeleteTrigger_DELETE_TRIGGER_LAST_ACTOR || len(released.GetStatus().GetRefs()) > 0 { + continue + } + _, err = deleter.DeleteSharedVolume(ctx, volumeRef, store.DeletePreconditions{UID: released.GetMetadata().GetUid()}) + if code := status.Code(err); err != nil && code != codes.NotFound && code != codes.FailedPrecondition { + errs = append(errs, fmt.Errorf("while deleting shared volume %s: %w", volumeRef, err)) + } + } + return errors.Join(errs...) +} diff --git a/cmd/ateapi/internal/controlapi/shared_volume_refs_test.go b/cmd/ateapi/internal/controlapi/shared_volume_refs_test.go new file mode 100644 index 0000000000..3870365bb5 --- /dev/null +++ b/cmd/ateapi/internal/controlapi/shared_volume_refs_test.go @@ -0,0 +1,293 @@ +// 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. + +package controlapi + +import ( + "context" + "strings" + "testing" + + "github.com/google/go-cmp/cmp" + "go.ate.dev/substrate/cmd/ateapi/internal/store" + "go.ate.dev/substrate/cmd/ateapi/internal/store/storetest" + "go.ate.dev/substrate/internal/apierror" + "go.ate.dev/substrate/internal/resources" + "go.ate.dev/substrate/pkg/proto/ateapipb" + "google.golang.org/grpc/codes" + "google.golang.org/protobuf/testing/protocmp" +) + +const ( + refTestAtespace = "team-refs" + refTestActorUID = "11111111-1111-1111-1111-111111111111" + refTestOtherUID = "22222222-2222-2222-2222-222222222222" +) + +func refTestTemplate(volumes ...*ateapipb.Volume) *ateapipb.ActorTemplate { + return &ateapipb.ActorTemplate{ + Metadata: &ateapipb.ResourceMetadata{Atespace: refTestAtespace, Name: "tmpl"}, + Volumes: volumes, + } +} + +func refTestActor(volumes ...*ateapipb.ExternalVolume) *ateapipb.Actor { + return &ateapipb.Actor{ + Metadata: &ateapipb.ResourceMetadata{Atespace: refTestAtespace, Name: "consumer", Uid: refTestActorUID}, + Status: &ateapipb.ActorStatus{ExternalVolumes: volumes}, + } +} + +func borrowedMount(volumeName string) *ateapipb.ExternalVolume { + return &ateapipb.ExternalVolume{ + Name: "shared-data", + SharedVolumeName: volumeName, + StorageVolumeId: "disk/" + volumeName, + VolumeType: "mock", + VolumeContext: map[string]string{"type": "pd-ssd"}, + } +} + +func mustStoreReadyVolume(t *testing.T, ctx context.Context, st store.Interface, name string, trigger ateapipb.DeleteTrigger, refs ...*ateapipb.ActorRef) *ateapipb.SharedVolume { + t.Helper() + return storetest.MustCreateSharedVolume(t, ctx, st, &ateapipb.SharedVolume{ + Metadata: &ateapipb.ResourceMetadata{Atespace: refTestAtespace, Name: name}, + DeleteTrigger: trigger, + AccessMode: ateapipb.AccessMode_ACCESS_MODE_READ_WRITE_ONCE, + VolumeId: "disk/" + name, + VolumeType: "mock", + VolumeContext: map[string]string{"type": "pd-ssd"}, + Status: &ateapipb.SharedVolumeStatus{ + State: ateapipb.SharedVolumeState_SHARED_VOLUME_STATE_READY, + Refs: refs, + }, + }) +} + +func mustGetVolume(t *testing.T, ctx context.Context, st store.Interface, name string) *ateapipb.SharedVolume { + t.Helper() + got, err := st.GetSharedVolume(ctx, resources.SharedVolumeRef{Atespace: refTestAtespace, Name: name}) + if err != nil { + t.Fatalf("GetSharedVolume(%q): %v", name, err) + } + return got +} + +// refStates maps each referencing actor's UID to whether its reference is active. +func refStates(volume *ateapipb.SharedVolume) map[string]bool { + states := make(map[string]bool, len(volume.GetStatus().GetRefs())) + for _, ref := range volume.GetStatus().GetRefs() { + states[ref.GetActorUid()] = ref.GetActive() + } + return states +} + +func TestResolveSharedVolumeRefs(t *testing.T) { + ctx := context.Background() + st := newTestPersistence(t) + mustStoreReadyVolume(t, ctx, st, "shared", ateapipb.DeleteTrigger_DELETE_TRIGGER_MANUAL) + mustStoreReadyVolume(t, ctx, st, "bound", ateapipb.DeleteTrigger_DELETE_TRIGGER_MANUAL) + storetest.MustCreateSharedVolume(t, ctx, st, &ateapipb.SharedVolume{ + Metadata: &ateapipb.ResourceMetadata{Atespace: refTestAtespace, Name: "pending"}, + StorageClassName: "standard", + Status: &ateapipb.SharedVolumeStatus{State: ateapipb.SharedVolumeState_SHARED_VOLUME_STATE_PENDING}, + }) + named := &ateapipb.Volume{Name: "shared-data", External: &ateapipb.ExternalVolumeSource{SharedVolume: &ateapipb.SharedVolumeRef{Name: "shared"}}} + unnamed := &ateapipb.Volume{Name: "slot", External: &ateapipb.ExternalVolumeSource{SharedVolume: &ateapipb.SharedVolumeRef{}}} + + tests := []struct { + name string + template *ateapipb.ActorTemplate + bindings map[string]string + want []string + wantCode codes.Code + }{ + {name: "named ref", template: refTestTemplate(named), want: []string{"shared"}}, + {name: "bound slot", template: refTestTemplate(named, unnamed), bindings: map[string]string{"slot": "bound"}, want: []string{"shared", "bound"}}, + {name: "unbound slot", template: refTestTemplate(unnamed), wantCode: codes.InvalidArgument}, + {name: "binding for a named ref", template: refTestTemplate(named), bindings: map[string]string{"shared-data": "bound"}, wantCode: codes.InvalidArgument}, + {name: "binding for an unknown volume", template: refTestTemplate(unnamed), bindings: map[string]string{"slot": "bound", "other": "bound"}, wantCode: codes.InvalidArgument}, + {name: "missing volume", template: refTestTemplate(unnamed), bindings: map[string]string{"slot": "absent"}, wantCode: codes.FailedPrecondition}, + {name: "pending volume", template: refTestTemplate(unnamed), bindings: map[string]string{"slot": "pending"}, wantCode: codes.FailedPrecondition}, + } + for _, tc := range tests { + t.Run(tc.name, func(t *testing.T) { + actor := refTestActor() + actor.SharedVolumeBindings = tc.bindings + mounts, err := resolveSharedVolumeRefs(ctx, st, actor, tc.template) + if apierror.Code(err) != tc.wantCode { + t.Fatalf("resolveSharedVolumeRefs() error = %v, want code %v", err, tc.wantCode) + } + var got []string + for _, mount := range mounts { + if mount.GetStorageVolumeId() != "disk/"+mount.GetSharedVolumeName() { + t.Errorf("mount %v does not carry the volume's handle", mount) + } + got = append(got, mount.GetSharedVolumeName()) + } + if diff := cmp.Diff(tc.want, got); diff != "" { + t.Errorf("resolved volumes mismatch (-want +got):\n%s", diff) + } + }) + } +} + +func TestBindClaimDeactivateRelease(t *testing.T) { + ctx := context.Background() + st := newTestPersistence(t) + mustStoreReadyVolume(t, ctx, st, "shared", ateapipb.DeleteTrigger_DELETE_TRIGGER_MANUAL) + actor := refTestActor(borrowedMount("shared")) + volumeRef := resources.SharedVolumeRef{Atespace: refTestAtespace, Name: "shared"} + + if err := bindSharedVolumes(ctx, st, actor); err != nil { + t.Fatalf("bindSharedVolumes: %v", err) + } + if diff := cmp.Diff(map[string]bool{refTestActorUID: false}, refStates(mustGetVolume(t, ctx, st, "shared"))); diff != "" { + t.Errorf("refs after bind mismatch (-want +got):\n%s", diff) + } + + claimed, err := claimSharedVolumes(ctx, st, actor) + if err != nil { + t.Fatalf("claimSharedVolumes: %v", err) + } + if diff := cmp.Diff(actor.GetStatus().GetExternalVolumes(), claimed, protocmp.Transform()); diff != "" { + t.Errorf("claimed mounts mismatch (-want +got):\n%s", diff) + } + if diff := cmp.Diff(map[string]bool{refTestActorUID: true}, refStates(mustGetVolume(t, ctx, st, "shared"))); diff != "" { + t.Errorf("refs after claim mismatch (-want +got):\n%s", diff) + } + if _, err := claimSharedVolumes(ctx, st, actor); err != nil { + t.Fatalf("second claimSharedVolumes: %v", err) + } + + if err := deactivateSharedVolumes(ctx, st, actor); err != nil { + t.Fatalf("deactivateSharedVolumes: %v", err) + } + if diff := cmp.Diff(map[string]bool{refTestActorUID: false}, refStates(mustGetVolume(t, ctx, st, "shared"))); diff != "" { + t.Errorf("refs after deactivate mismatch (-want +got):\n%s", diff) + } + + deleter := &recordingSharedVolumeDeleter{} + if err := releaseSharedVolumes(ctx, st, deleter, actor); err != nil { + t.Fatalf("releaseSharedVolumes: %v", err) + } + if got := refStates(mustGetVolume(t, ctx, st, "shared")); len(got) != 0 { + t.Errorf("refs after release = %v, want none", got) + } + if len(deleter.deleted) != 0 { + t.Errorf("deleted %v, want a MANUAL volume left in place", deleter.deleted) + } + if err := releaseSharedVolumes(ctx, st, deleter, actor); err != nil { + t.Fatalf("second releaseSharedVolumes: %v", err) + } + if _, err := st.GetSharedVolume(ctx, volumeRef); err != nil { + t.Errorf("GetSharedVolume after release: %v", err) + } +} + +func TestClaimSharedVolumes_Exclusion(t *testing.T) { + ctx := context.Background() + other := &ateapipb.ActorRef{ActorUid: refTestOtherUID, ActorName: "producer"} + + t.Run("active holder blocks", func(t *testing.T) { + st := newTestPersistence(t) + other := &ateapipb.ActorRef{ActorUid: other.ActorUid, ActorName: other.ActorName, Active: true} + mustStoreReadyVolume(t, ctx, st, "shared", ateapipb.DeleteTrigger_DELETE_TRIGGER_MANUAL, other) + + _, err := claimSharedVolumes(ctx, st, refTestActor(borrowedMount("shared"))) + if apierror.Code(err) != codes.FailedPrecondition || !strings.Contains(err.Error(), "producer") { + t.Fatalf("claimSharedVolumes() = %v, want FailedPrecondition naming the holder", err) + } + if diff := cmp.Diff(map[string]bool{refTestOtherUID: true}, refStates(mustGetVolume(t, ctx, st, "shared"))); diff != "" { + t.Errorf("refs after refused claim mismatch (-want +got):\n%s", diff) + } + }) + + t.Run("inactive holder does not block", func(t *testing.T) { + st := newTestPersistence(t) + mustStoreReadyVolume(t, ctx, st, "shared", ateapipb.DeleteTrigger_DELETE_TRIGGER_MANUAL, other) + + if _, err := claimSharedVolumes(ctx, st, refTestActor(borrowedMount("shared"))); err != nil { + t.Fatalf("claimSharedVolumes: %v", err) + } + if diff := cmp.Diff(map[string]bool{refTestOtherUID: false, refTestActorUID: true}, refStates(mustGetVolume(t, ctx, st, "shared"))); diff != "" { + t.Errorf("refs mismatch (-want +got):\n%s", diff) + } + }) + + t.Run("missing volume", func(t *testing.T) { + st := newTestPersistence(t) + storetest.MustCreateAtespace(t, ctx, st, refTestAtespace) + + _, err := claimSharedVolumes(ctx, st, refTestActor(borrowedMount("absent"))) + if apierror.Code(err) != codes.FailedPrecondition { + t.Fatalf("claimSharedVolumes() = %v, want FailedPrecondition", err) + } + }) +} + +func TestReleaseSharedVolumes_LastActorDeletes(t *testing.T) { + ctx := context.Background() + st := newTestPersistence(t) + other := &ateapipb.ActorRef{ActorUid: refTestOtherUID, ActorName: "other"} + mustStoreReadyVolume(t, ctx, st, "shared", ateapipb.DeleteTrigger_DELETE_TRIGGER_LAST_ACTOR, + &ateapipb.ActorRef{ActorUid: refTestActorUID, ActorName: "consumer"}, other) + actor := refTestActor(borrowedMount("shared")) + deleter := &recordingSharedVolumeDeleter{} + + if err := releaseSharedVolumes(ctx, st, deleter, actor); err != nil { + t.Fatalf("releaseSharedVolumes: %v", err) + } + if len(deleter.deleted) != 0 { + t.Fatalf("deleted %v while another actor still references the volume", deleter.deleted) + } + + otherActor := refTestActor(borrowedMount("shared")) + otherActor.Metadata.Uid = refTestOtherUID + if err := releaseSharedVolumes(ctx, st, deleter, otherActor); err != nil { + t.Fatalf("releaseSharedVolumes(other): %v", err) + } + if diff := cmp.Diff([]string{refTestAtespace + "/shared"}, deleter.deleted); diff != "" { + t.Errorf("deleted volumes mismatch (-want +got):\n%s", diff) + } +} + +func TestCrashActorKeepsSharedVolumeRefs(t *testing.T) { + ctx := context.Background() + st := newTestPersistence(t) + + created := storetest.MustCreateActor(t, ctx, st, refTestActor()) + mustStoreReadyVolume(t, ctx, st, "shared", ateapipb.DeleteTrigger_DELETE_TRIGGER_MANUAL, + &ateapipb.ActorRef{ActorUid: created.GetMetadata().GetUid(), ActorName: "consumer", Active: true}) + mustUpdateActorStatus(t, ctx, st, created, func(s *ateapipb.ActorStatus) { + s.State = ateapipb.ActorState_ACTOR_STATE_RUNNING + s.ExternalVolumes = []*ateapipb.ExternalVolume{borrowedMount("shared")} + }) + + if err := crashActor(ctx, st, resources.ActorRef{Atespace: refTestAtespace, Name: "consumer"}, "resume", "worker lost"); err != nil { + t.Fatalf("crashActor: %v", err) + } + if diff := cmp.Diff(map[string]bool{created.GetMetadata().GetUid(): true}, refStates(mustGetVolume(t, ctx, st, "shared"))); diff != "" { + t.Errorf("refs after crash mismatch (-want +got):\n%s", diff) + } +} + +type recordingSharedVolumeDeleter struct { + deleted []string +} + +func (d *recordingSharedVolumeDeleter) DeleteSharedVolume(_ context.Context, volumeRef resources.SharedVolumeRef, _ store.DeletePreconditions) (*ateapipb.SharedVolume, error) { + d.deleted = append(d.deleted, volumeRef.String()) + return nil, nil +} diff --git a/cmd/ateapi/internal/controlapi/shared_volume_test.go b/cmd/ateapi/internal/controlapi/shared_volume_test.go new file mode 100644 index 0000000000..47e99b738a --- /dev/null +++ b/cmd/ateapi/internal/controlapi/shared_volume_test.go @@ -0,0 +1,304 @@ +// 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. + +package controlapi + +import ( + "context" + "testing" + + "github.com/google/go-cmp/cmp" + "go.ate.dev/substrate/cmd/ateapi/internal/store" + "go.ate.dev/substrate/internal/apierror" + "go.ate.dev/substrate/internal/resources" + "go.ate.dev/substrate/internal/volume" + "go.ate.dev/substrate/pkg/proto/ateapipb" + "google.golang.org/grpc/codes" + storagev1 "k8s.io/api/storage/v1" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" +) + +// recordingVolumePlugin answers CreateVolume with a handle derived from the +// name it was asked for, and records both calls so a test can assert on what +// the workflow asked the storage system to do. +type recordingVolumePlugin struct { + volume.VolumePluginControlPlane + + createErr error + deleteErr error + createdIDs []string + deletedIDs []string +} + +func (p *recordingVolumePlugin) CreateVolume(_ context.Context, req volume.CreateVolumeRequest) (volume.CreateVolumeResponse, error) { + if p.createErr != nil { + return volume.CreateVolumeResponse{}, p.createErr + } + p.createdIDs = append(p.createdIDs, req.Name) + return volume.CreateVolumeResponse{VolumeID: "disk/" + req.Name, VolumeContext: map[string]string{"capacity": req.Capacity}}, nil +} + +func (p *recordingVolumePlugin) DeleteVolume(_ context.Context, volumeID string) error { + if p.deleteErr != nil { + return p.deleteErr + } + p.deletedIDs = append(p.deletedIDs, volumeID) + return nil +} + +// newTestSharedVolumeWorkflow builds a SharedVolumeWorkflow over a real +// store, with one StorageClass named "standard" whose reclaim policy the caller +// chooses, and a plugin registered under that class's provisioner. +func newTestSharedVolumeWorkflow(t *testing.T) (*SharedVolumeWorkflow, store.Interface, *recordingVolumePlugin) { + t.Helper() + persistence := newTestPersistence(t) + plugin := &recordingVolumePlugin{} + scLister := &fakeStorageClassLister{ + storageClasses: map[string]*storagev1.StorageClass{ + "standard": { + ObjectMeta: metav1.ObjectMeta{Name: "standard"}, + Provisioner: "mock", + Parameters: map[string]string{"type": "pd-ssd"}, + }, + }, + } + registry := &mockPluginRegistry{plugins: map[string]volume.VolumePluginControlPlane{"mock": plugin}} + return NewSharedVolumeWorkflow(persistence, scLister, registry), persistence, plugin +} + +// TestCreateSharedVolume_Provisioned checks the whole create: the record is +// reserved, the disk is asked for under an ID derived from the record's UID, +// and the handle the driver returned is stamped back. +func TestCreateSharedVolume_Provisioned(t *testing.T) { + ctx := context.Background() + w, _, plugin := newTestSharedVolumeWorkflow(t) + + got, err := w.CreateSharedVolume(ctx, &ateapipb.SharedVolume{ + Metadata: &ateapipb.ResourceMetadata{Atespace: "team-a", Name: "shared"}, + StorageClassName: "standard", + Capacity: "10Gi", + }) + if err != nil { + t.Fatalf("CreateSharedVolume: %v", err) + } + + wantID := sharedVolumeID(got.GetMetadata().GetUid()) + if diff := cmp.Diff([]string{wantID}, plugin.createdIDs); diff != "" { + t.Errorf("provisioned volume names mismatch (-want +got):\n%s", diff) + } + if want := "disk/" + wantID; got.GetVolumeId() != want { + t.Errorf("volume_id = %q, want the handle the driver returned %q", got.GetVolumeId(), want) + } + if got.GetVolumeType() != "mock" { + t.Errorf("volume_type = %q, want the class's provisioner %q", got.GetVolumeType(), "mock") + } + if want := ateapipb.DeleteTrigger_DELETE_TRIGGER_LAST_ACTOR; got.GetDeleteTrigger() != want { + t.Errorf("delete_trigger = %v, want %v", got.GetDeleteTrigger(), want) + } + if want := ateapipb.SharedVolumeState_SHARED_VOLUME_STATE_READY; got.GetStatus().GetState() != want { + t.Errorf("state = %v, want %v", got.GetStatus().GetState(), want) + } + if got.GetVolumeContext()["capacity"] != "10Gi" { + t.Errorf("volume_context = %v, want the context the driver returned for a 10Gi disk", got.GetVolumeContext()) + } +} + +func TestCreateSharedVolume_Registered(t *testing.T) { + ctx := context.Background() + w, _, plugin := newTestSharedVolumeWorkflow(t) + + got, err := w.CreateSharedVolume(ctx, &ateapipb.SharedVolume{ + Metadata: &ateapipb.ResourceMetadata{Atespace: "team-a", Name: "adopted"}, + VolumeId: "projects/p/zones/z/disks/preexisting", + VolumeType: "mock", + VolumeContext: map[string]string{"fsType": "ext4"}, + }) + if err != nil { + t.Fatalf("CreateSharedVolume: %v", err) + } + + if len(plugin.createdIDs) != 0 { + t.Errorf("provisioned %v, want nothing provisioned for a volume that already exists", plugin.createdIDs) + } + if want := "projects/p/zones/z/disks/preexisting"; got.GetVolumeId() != want { + t.Errorf("volume_id = %q, want the supplied %q", got.GetVolumeId(), want) + } + if want := ateapipb.SharedVolumeState_SHARED_VOLUME_STATE_READY; got.GetStatus().GetState() != want { + t.Errorf("state = %v, want %v", got.GetStatus().GetState(), want) + } + if want := ateapipb.DeleteTrigger_DELETE_TRIGGER_MANUAL; got.GetDeleteTrigger() != want { + t.Errorf("delete_trigger = %v, want %v", got.GetDeleteTrigger(), want) + } +} + +func TestCreateSharedVolume_Errors(t *testing.T) { + ctx := context.Background() + + tests := []struct { + name string + volume *ateapipb.SharedVolume + wantCode codes.Code + }{ + { + name: "unknown storage class", + volume: &ateapipb.SharedVolume{ + Metadata: &ateapipb.ResourceMetadata{Atespace: "team-a", Name: "vol"}, + StorageClassName: "nonexistent", + Capacity: "1Gi", + }, + wantCode: codes.FailedPrecondition, + }, + { + name: "unknown atespace", + volume: &ateapipb.SharedVolume{ + Metadata: &ateapipb.ResourceMetadata{Atespace: "no-such-space", Name: "vol"}, + StorageClassName: "standard", + Capacity: "1Gi", + }, + wantCode: codes.FailedPrecondition, + }, + { + name: "no source at all", + volume: &ateapipb.SharedVolume{ + Metadata: &ateapipb.ResourceMetadata{Atespace: "team-a", Name: "vol"}, + }, + wantCode: codes.InvalidArgument, + }, + } + for _, tc := range tests { + t.Run(tc.name, func(t *testing.T) { + w, _, _ := newTestSharedVolumeWorkflow(t) + _, err := w.CreateSharedVolume(ctx, tc.volume) + if got := apierror.Code(err); got != tc.wantCode { + t.Fatalf("CreateSharedVolume error = %v (code %v), want code %v", err, got, tc.wantCode) + } + }) + } +} + +func TestCreateSharedVolume_ReusedName(t *testing.T) { + ctx := context.Background() + w, _, plugin := newTestSharedVolumeWorkflow(t) + + toCreate := func() *ateapipb.SharedVolume { + return &ateapipb.SharedVolume{ + Metadata: &ateapipb.ResourceMetadata{Atespace: "team-a", Name: "shared"}, + StorageClassName: "standard", + Capacity: "10Gi", + } + } + if _, err := w.CreateSharedVolume(ctx, toCreate()); err != nil { + t.Fatalf("CreateSharedVolume: %v", err) + } + _, err := w.CreateSharedVolume(ctx, toCreate()) + if got := apierror.Code(err); got != codes.AlreadyExists { + t.Fatalf("second CreateSharedVolume error = %v (code %v), want code %v", err, got, codes.AlreadyExists) + } + if len(plugin.createdIDs) != 1 { + t.Errorf("provisioned %v, want only the first create to reach the storage system", plugin.createdIDs) + } +} + +func TestDeleteSharedVolume(t *testing.T) { + ctx := context.Background() + w, persistence, plugin := newTestSharedVolumeWorkflow(t) + created, err := w.CreateSharedVolume(ctx, &ateapipb.SharedVolume{ + Metadata: &ateapipb.ResourceMetadata{Atespace: "team-a", Name: "shared"}, + StorageClassName: "standard", + Capacity: "10Gi", + }) + if err != nil { + t.Fatalf("CreateSharedVolume: %v", err) + } + volumeRef := resources.SharedVolumeRefFromSharedVolume(created) + + if _, err := w.DeleteSharedVolume(ctx, volumeRef, store.DeletePreconditions{Version: created.GetMetadata().GetVersion() + 1}); apierror.Code(err) != codes.Aborted { + t.Fatalf("DeleteSharedVolume with a stale version error = %v, want code %v", err, codes.Aborted) + } + if len(plugin.deletedIDs) != 0 { + t.Fatalf("collected %v before the precondition check passed", plugin.deletedIDs) + } + + if _, err := w.DeleteSharedVolume(ctx, volumeRef, store.DeletePreconditions{UID: created.GetMetadata().GetUid()}); err != nil { + t.Fatalf("DeleteSharedVolume: %v", err) + } + if diff := cmp.Diff([]string{created.GetVolumeId()}, plugin.deletedIDs); diff != "" { + t.Errorf("collected disks mismatch (-want +got):\n%s", diff) + } + if _, err := persistence.GetSharedVolume(ctx, volumeRef); err == nil { + t.Errorf("GetSharedVolume after delete succeeded, want the row gone") + } +} + +func TestDeleteSharedVolume_StillReferenced(t *testing.T) { + ctx := context.Background() + w, persistence, plugin := newTestSharedVolumeWorkflow(t) + + created, err := w.CreateSharedVolume(ctx, &ateapipb.SharedVolume{ + Metadata: &ateapipb.ResourceMetadata{Atespace: "team-a", Name: "shared"}, + StorageClassName: "standard", + Capacity: "10Gi", + }) + if err != nil { + t.Fatalf("CreateSharedVolume: %v", err) + } + volumeRef := resources.SharedVolumeRefFromSharedVolume(created) + if _, err := persistence.UpdateSharedVolume(ctx, volumeRef, store.PreconditionFrom(created), func(toUpdate *ateapipb.SharedVolume) error { + toUpdate.Status.Refs = []*ateapipb.ActorRef{{ActorUid: "uid-1", ActorName: "producer"}} + return nil + }); err != nil { + t.Fatalf("UpdateSharedVolume: %v", err) + } + + _, err = w.DeleteSharedVolume(ctx, volumeRef, store.DeletePreconditions{}) + if got := apierror.Code(err); got != codes.FailedPrecondition { + t.Fatalf("DeleteSharedVolume error = %v (code %v), want code %v", err, got, codes.FailedPrecondition) + } + if len(plugin.deletedIDs) != 0 { + t.Errorf("collected %v, want the disk left alone while an actor holds it", plugin.deletedIDs) + } +} + +func TestDeleteSharedVolume_Pending(t *testing.T) { + ctx := context.Background() + w, persistence, plugin := newTestSharedVolumeWorkflow(t) + + pending, err := persistence.CreateSharedVolume(ctx, &ateapipb.SharedVolume{ + Metadata: &ateapipb.ResourceMetadata{Atespace: "team-a", Name: "stranded"}, + StorageClassName: "standard", + VolumeType: "mock", + Status: &ateapipb.SharedVolumeStatus{State: ateapipb.SharedVolumeState_SHARED_VOLUME_STATE_PENDING}, + }) + if err != nil { + t.Fatalf("CreateSharedVolume: %v", err) + } + + if _, err := w.DeleteSharedVolume(ctx, resources.SharedVolumeRefFromSharedVolume(pending), store.DeletePreconditions{}); err != nil { + t.Fatalf("DeleteSharedVolume: %v", err) + } + want := []string{sharedVolumeID(pending.GetMetadata().GetUid())} + if diff := cmp.Diff(want, plugin.deletedIDs); diff != "" { + t.Errorf("collected disks mismatch (-want +got):\n%s", diff) + } +} + +func TestDeleteSharedVolume_NotFound(t *testing.T) { + ctx := context.Background() + w, _, _ := newTestSharedVolumeWorkflow(t) + + _, err := w.DeleteSharedVolume(ctx, resources.SharedVolumeRef{Atespace: "team-a", Name: "nope"}, store.DeletePreconditions{}) + if got := apierror.Code(err); got != codes.NotFound { + t.Fatalf("DeleteSharedVolume error = %v (code %v), want code %v", err, got, codes.NotFound) + } +} diff --git a/cmd/ateapi/internal/controlapi/snapshot_report_test.go b/cmd/ateapi/internal/controlapi/snapshot_report_test.go index b2f3e704b9..4558eb3e71 100644 --- a/cmd/ateapi/internal/controlapi/snapshot_report_test.go +++ b/cmd/ateapi/internal/controlapi/snapshot_report_test.go @@ -387,7 +387,7 @@ func TestApplyLatestDurableLogsStaleReports(t *testing.T) { func TestApplyReportedSnapshotState(t *testing.T) { ctx := context.Background() persistence := newTestPersistence(t) - w := NewActorWorkflow(persistence, nil, nil, nil, nil, nil, "", nil, nil) + w := NewActorWorkflow(persistence, nil, nil, nil, nil, nil, "", nil, nil, nil) tmpl := &ateapipb.ActorTemplate{SnapshotConfig: &ateapipb.SnapshotConfig{StorageLocation: testStorageLocation}} actorRef := resources.ActorRef{Atespace: reportAtespace, Name: "actor-1"} diff --git a/cmd/ateapi/internal/controlapi/volumes.go b/cmd/ateapi/internal/controlapi/volumes.go index fa9e25194e..590c04a3cf 100644 --- a/cmd/ateapi/internal/controlapi/volumes.go +++ b/cmd/ateapi/internal/controlapi/volumes.go @@ -37,8 +37,8 @@ func initialExternalVolumes(ctx context.Context, scLister storagev1listers.Stora } var volumes []*ateapipb.ExternalVolume for _, vol := range template.GetVolumes() { - if vol.GetExternalVolumeTemplate() != nil { - scName := vol.GetExternalVolumeTemplate().GetStorageClassName() + if vol.GetExternal().GetTemplate() != nil { + scName := vol.GetExternal().GetTemplate().GetStorageClassName() sc, err := scLister.Get(scName) if err != nil { if k8serrors.IsNotFound(err) { @@ -74,6 +74,11 @@ func createActorVolumes(ctx context.Context, registry VolumePluginRegistry, scLi for idx, vol := range volumesToCreate { currentIdx = idx + if vol.GetSharedVolumeName() != "" { + resultVolumes = append(resultVolumes, vol) + continue + } + var specVol *ateapipb.Volume volName := vol.GetName() for _, tVol := range template.GetVolumes() { @@ -82,7 +87,7 @@ func createActorVolumes(ctx context.Context, registry VolumePluginRegistry, scLi break } } - if specVol == nil || specVol.GetExternalVolumeTemplate() == nil { + if specVol == nil || specVol.GetExternal().GetTemplate() == nil { return resultVolumes, apierror.NotFound("volume %q not found in template", volName) } @@ -93,7 +98,7 @@ func createActorVolumes(ctx context.Context, registry VolumePluginRegistry, scLi actVolID := actorVolumeID(actorUID, volName) - scName := specVol.GetExternalVolumeTemplate().GetStorageClassName() + scName := specVol.GetExternal().GetTemplate().GetStorageClassName() sc, err := scLister.Get(scName) if err != nil { if k8serrors.IsNotFound(err) { @@ -113,7 +118,7 @@ func createActorVolumes(ctx context.Context, registry VolumePluginRegistry, scLi resp, volErr := plugin.CreateVolume(ctx, volume.CreateVolumeRequest{ Name: actVolID, - Capacity: specVol.GetExternalVolumeTemplate().GetCapacity(), + Capacity: specVol.GetExternal().GetTemplate().GetCapacity(), Parameters: sc.Parameters, }) if volErr != nil { @@ -135,13 +140,16 @@ func createActorVolumes(ctx context.Context, registry VolumePluginRegistry, scLi return resultVolumes, nil } -// deleteActorVolumes deletes all external volumes in the list. +// deleteActorVolumes deletes the external volumes the actor owns. func deleteActorVolumes(ctx context.Context, registry VolumePluginRegistry, actorUID string, volumes []*ateapipb.ExternalVolume) error { if actorUID == "" { return errors.New("actorUID is required") } var errs []error for _, vol := range volumes { + if vol.GetSharedVolumeName() != "" { + continue + } volID := vol.GetStorageVolumeId() if volID == "" { // If the volume hasn't been successfully created yet, it's possible diff --git a/cmd/ateapi/internal/controlapi/volumes_test.go b/cmd/ateapi/internal/controlapi/volumes_test.go index 129cee7193..7f871886f0 100644 --- a/cmd/ateapi/internal/controlapi/volumes_test.go +++ b/cmd/ateapi/internal/controlapi/volumes_test.go @@ -63,9 +63,9 @@ func TestActorVolumesStorageClassErrors(t *testing.T) { tmpl := &ateapipb.ActorTemplate{ Volumes: []*ateapipb.Volume{{ Name: "data-vol", - ExternalVolumeTemplate: &ateapipb.ExternalVolumeTemplate{ + External: &ateapipb.ExternalVolumeSource{Template: &ateapipb.ExternalVolumeTemplate{ StorageClassName: "standard", - }, + }}, }}, } volumes := []*ateapipb.ExternalVolume{{ @@ -120,9 +120,9 @@ func TestInitialActorVolumes(t *testing.T) { Volumes: []*ateapipb.Volume{ { Name: "data-vol-1", - ExternalVolumeTemplate: &ateapipb.ExternalVolumeTemplate{ + External: &ateapipb.ExternalVolumeSource{Template: &ateapipb.ExternalVolumeTemplate{ StorageClassName: "standard", - }, + }}, }, { Name: "scratch-vol", @@ -133,9 +133,9 @@ func TestInitialActorVolumes(t *testing.T) { }, { Name: "data-vol-2", - ExternalVolumeTemplate: &ateapipb.ExternalVolumeTemplate{ + External: &ateapipb.ExternalVolumeSource{Template: &ateapipb.ExternalVolumeTemplate{ StorageClassName: "fast", - }, + }}, }, }, } @@ -179,9 +179,9 @@ func TestCreateActorVolumes(t *testing.T) { Volumes: []*ateapipb.Volume{ { Name: "data-vol", - ExternalVolumeTemplate: &ateapipb.ExternalVolumeTemplate{ + External: &ateapipb.ExternalVolumeSource{Template: &ateapipb.ExternalVolumeTemplate{ StorageClassName: "standard", - }, + }}, }, }, } @@ -190,21 +190,21 @@ func TestCreateActorVolumes(t *testing.T) { Volumes: []*ateapipb.Volume{ { Name: "vol1", - ExternalVolumeTemplate: &ateapipb.ExternalVolumeTemplate{ + External: &ateapipb.ExternalVolumeSource{Template: &ateapipb.ExternalVolumeTemplate{ StorageClassName: "standard", - }, + }}, }, { Name: "vol2", - ExternalVolumeTemplate: &ateapipb.ExternalVolumeTemplate{ + External: &ateapipb.ExternalVolumeSource{Template: &ateapipb.ExternalVolumeTemplate{ StorageClassName: "standard", - }, + }}, }, { Name: "vol3", - ExternalVolumeTemplate: &ateapipb.ExternalVolumeTemplate{ + External: &ateapipb.ExternalVolumeSource{Template: &ateapipb.ExternalVolumeTemplate{ StorageClassName: "standard", - }, + }}, }, }, } @@ -345,8 +345,8 @@ func (emptyIDVolumePlugin) CreateVolume(context.Context, volume.CreateVolumeRequ func TestCreateActorVolumesRejectsEmptyPluginID(t *testing.T) { tmpl := &ateapipb.ActorTemplate{ Volumes: []*ateapipb.Volume{{ - Name: "data-vol", - ExternalVolumeTemplate: &ateapipb.ExternalVolumeTemplate{StorageClassName: "standard"}, + Name: "data-vol", + External: &ateapipb.ExternalVolumeSource{Template: &ateapipb.ExternalVolumeTemplate{StorageClassName: "standard"}}, }}, } registry := &mockPluginRegistry{ @@ -686,3 +686,42 @@ func TestDetachActorVolumes(t *testing.T) { }) } } + +func TestBorrowedVolumesAreNotProvisionedOrDeleted(t *testing.T) { + ctx := context.Background() + template := &ateapipb.ActorTemplate{ + Volumes: []*ateapipb.Volume{{Name: "shared-data", External: &ateapipb.ExternalVolumeSource{SharedVolume: &ateapipb.SharedVolumeRef{Name: "shared"}}}}, + } + scLister := &fakeStorageClassLister{storageClasses: map[string]*storagev1.StorageClass{}} + + initial, err := initialExternalVolumes(ctx, scLister, template) + if err != nil { + t.Fatalf("initialExternalVolumes: %v", err) + } + if len(initial) != 0 { + t.Errorf("initialExternalVolumes() = %v, want no entry for a borrowed volume", initial) + } + + borrowed := []*ateapipb.ExternalVolume{{ + Name: "shared-data", + SharedVolumeName: "shared", + StorageVolumeId: "disk/shared", + VolumeType: "mock", + }} + plugin := &trackingVolumePlugin{} + registry := &mockPluginRegistry{plugins: map[string]volume.VolumePluginControlPlane{"mock": plugin}} + + got, err := createActorVolumes(ctx, registry, scLister, "actor-uid-123", template, borrowed) + if err != nil { + t.Fatalf("createActorVolumes: %v", err) + } + if diff := cmp.Diff(borrowed, got, protocmp.Transform()); diff != "" { + t.Errorf("createActorVolumes() mismatch (-want +got):\n%s", diff) + } + if err := deleteActorVolumes(ctx, registry, "actor-uid-123", borrowed); err != nil { + t.Fatalf("deleteActorVolumes: %v", err) + } + if len(plugin.deletedIDs) != 0 { + t.Errorf("deleted %v, want the borrowed volume left alone", plugin.deletedIDs) + } +} diff --git a/cmd/ateapi/internal/controlapi/workflow.go b/cmd/ateapi/internal/controlapi/workflow.go index c6e7a2a548..e5076d2dd5 100644 --- a/cmd/ateapi/internal/controlapi/workflow.go +++ b/cmd/ateapi/internal/controlapi/workflow.go @@ -106,6 +106,7 @@ func logActorState(ctx context.Context, actor *ateapipb.Actor, opName, state str // already validated, and what the workflow writes is the system's own state. type ActorWorkflow struct { store actorWorkflowStore + sharedVolumes sharedVolumeDeleter workerCache *workercache.Cache scheduler scheduling.Scheduler dialer *AteletDialer @@ -132,9 +133,11 @@ func NewActorWorkflow( egressGatewayAddress string, pluginRegistry VolumePluginRegistry, objectStorePlugin objectstorev1.ControlProviderClient, + sharedVolumes sharedVolumeDeleter, ) *ActorWorkflow { return &ActorWorkflow{ store: store, + sharedVolumes: sharedVolumes, workerCache: workerCache, scheduler: scheduling.New(workerCache), dialer: dialer, @@ -183,6 +186,7 @@ type actorWorkflowStore interface { GetActorTemplate(ctx context.Context, templateRef resources.ActorTemplateRef) (*ateapipb.ActorTemplate, error) DeleteActorTemplate(ctx context.Context, templateRef resources.ActorTemplateRef, precondition store.DeletePreconditions) (*ateapipb.ActorTemplate, error) AcquireLease(ctx context.Context, key string) (*store.Lease, error) + sharedVolumeRefStore } // WorkerWorkflow handles the multi-step operations on a Worker. Like @@ -246,3 +250,7 @@ func (w *ActorWorkflow) acquireActorLease(ctx context.Context, actorRef resource func acquireTagLease(ctx context.Context, holder leaseHolder, tagRef resources.TagRef) (context.Context, *store.Lease, error) { return acquireLease(ctx, holder, "lease:tag:"+tagRef.Atespace+":"+tagRef.Name, "Tag") } + +func acquireSharedVolumeLease(ctx context.Context, holder leaseHolder, volumeRef resources.SharedVolumeRef) (context.Context, *store.Lease, error) { + return acquireLease(ctx, holder, "lease:sharedvolume:"+volumeRef.Atespace+":"+volumeRef.Name, "SharedVolume") +} diff --git a/cmd/ateapi/internal/controlapi/workflow_delete.go b/cmd/ateapi/internal/controlapi/workflow_delete.go index de65c19a54..e911703c89 100644 --- a/cmd/ateapi/internal/controlapi/workflow_delete.go +++ b/cmd/ateapi/internal/controlapi/workflow_delete.go @@ -97,6 +97,10 @@ func (w *ActorWorkflow) DeleteActor(ctx context.Context, actorRef resources.Acto errs = append(errs, fmt.Errorf("while deleting volumes: %w", err)) } + if err := w.ensureSharedVolumeRefsReleased(ctx, actor); err != nil { + errs = append(errs, fmt.Errorf("while releasing external volume references: %w", err)) + } + if err := w.ensureExternalSnapshotsReleased(ctx, actor); err != nil { errs = append(errs, fmt.Errorf("while releasing external snapshots: %w", err)) } @@ -379,6 +383,15 @@ func (w *ActorWorkflow) ensureVolumesDeleted(ctx context.Context, actor *ateapip return nil } +// ensureSharedVolumeRefsReleased drops the actor's SharedVolume references +// and deletes any volume that reached its LAST_ACTOR trigger. +func (w *ActorWorkflow) ensureSharedVolumeRefsReleased(ctx context.Context, actor *ateapipb.Actor) (err error) { + ctx, done := stepSpan(ctx, "ReleaseSharedVolumeRefs") + defer func() { err = done(err) }() + + return releaseSharedVolumes(ctx, w.store, w.sharedVolumes, actor) +} + // ensureExternalSnapshotsReleased collects everything the actor wrote to // object storage, by deleting its own prefix. It runs before the actor is // removed, so a failed attempt is still rediscoverable on the retry. diff --git a/cmd/ateapi/internal/controlapi/workflow_delete_test.go b/cmd/ateapi/internal/controlapi/workflow_delete_test.go index 779f8f4c9b..3d40014b35 100644 --- a/cmd/ateapi/internal/controlapi/workflow_delete_test.go +++ b/cmd/ateapi/internal/controlapi/workflow_delete_test.go @@ -333,7 +333,7 @@ func TestDeleteActor_CollectsInFlightSnapshotWithoutTemplate(t *testing.T) { ctx := context.Background() persistence := newTestPersistence(t) objects := objectstoretest.New() - w := NewActorWorkflow(persistence, nil, nil, nil, nil, nil, "", nil, objectstoreplugintest.ControlClient(objects)) + w := NewActorWorkflow(persistence, nil, nil, nil, nil, nil, "", nil, objectstoreplugintest.ControlClient(objects), nil) actorRef := resources.ActorRef{Atespace: "team-a", Name: "actor-1"} actor := storetest.MustCreateActor(t, ctx, persistence, &ateapipb.Actor{ diff --git a/cmd/ateapi/internal/controlapi/workflow_resume.go b/cmd/ateapi/internal/controlapi/workflow_resume.go index a28938a7be..81364d0b01 100644 --- a/cmd/ateapi/internal/controlapi/workflow_resume.go +++ b/cmd/ateapi/internal/controlapi/workflow_resume.go @@ -208,7 +208,8 @@ func (w *ActorWorkflow) ensureVolumesCreated(ctx context.Context, actorRef resou pending := slices.ContainsFunc(actor.GetStatus().GetExternalVolumes(), func(vol *ateapipb.ExternalVolume) bool { return vol.GetStorageVolumeId() == "" }) - if !pending { + borrows := len(borrowedVolumeRefs(actor)) > 0 + if !pending && !borrows { markSkipped(ctx, "no volumes awaiting creation") return actor, nil } @@ -216,7 +217,14 @@ func (w *ActorWorkflow) ensureVolumesCreated(ctx context.Context, actorRef resou return nil, apierror.FailedPrecondition("cannot create volumes for Actor %s in state %s", actorRef, st) } - volumes, createErr := createActorVolumes(ctx, w.pluginRegistry, w.storageClassLister, actor.GetMetadata().GetUid(), actorTemplate, actor.GetStatus().GetExternalVolumes()) + volumes := actor.GetStatus().GetExternalVolumes() + if borrows { + var err error + if volumes, err = claimSharedVolumes(ctx, w.store, actor); err != nil { + return nil, err + } + } + volumes, createErr := createActorVolumes(ctx, w.pluginRegistry, w.storageClassLister, actor.GetMetadata().GetUid(), actorTemplate, volumes) // createActorVolumes reports the state it got to even when it fails, so both // paths persist the same field. updatePrecondition := store.PreconditionFrom(actor) diff --git a/cmd/ateapi/internal/controlapi/workflow_resume_test.go b/cmd/ateapi/internal/controlapi/workflow_resume_test.go index c70b0faf8c..0a755ec3b2 100644 --- a/cmd/ateapi/internal/controlapi/workflow_resume_test.go +++ b/cmd/ateapi/internal/controlapi/workflow_resume_test.go @@ -1749,8 +1749,8 @@ func TestEnsureVolumesAttached_ReturnsPublishContext(t *testing.T) { worker := &ateapipb.Worker{NodeName: "node-1"} tmpl := &ateapipb.ActorTemplate{ Volumes: []*ateapipb.Volume{ - {Name: "mounted", ExternalVolumeTemplate: &ateapipb.ExternalVolumeTemplate{StorageClassName: "sc"}}, - {Name: "unmounted", ExternalVolumeTemplate: &ateapipb.ExternalVolumeTemplate{StorageClassName: "sc"}}, + {Name: "mounted", External: &ateapipb.ExternalVolumeSource{Template: &ateapipb.ExternalVolumeTemplate{StorageClassName: "sc"}}}, + {Name: "unmounted", External: &ateapipb.ExternalVolumeSource{Template: &ateapipb.ExternalVolumeTemplate{StorageClassName: "sc"}}}, }, Containers: []*ateapipb.Container{ {Name: "main", Image: "img", VolumeMounts: []*ateapipb.VolumeMount{{Name: "mounted", MountPath: "/data"}}}, @@ -1790,8 +1790,8 @@ func (p *createCountingVolumePlugin) CreateVolume(ctx context.Context, req volum func TestEnsureVolumesCreated(t *testing.T) { tmpl := &ateapipb.ActorTemplate{ Volumes: []*ateapipb.Volume{{ - Name: "data", - ExternalVolumeTemplate: &ateapipb.ExternalVolumeTemplate{StorageClassName: "sc"}, + Name: "data", + External: &ateapipb.ExternalVolumeSource{Template: &ateapipb.ExternalVolumeTemplate{StorageClassName: "sc"}}, }}, } tests := []struct { diff --git a/cmd/ateapi/internal/controlapi/workflow_shared_volume.go b/cmd/ateapi/internal/controlapi/workflow_shared_volume.go new file mode 100644 index 0000000000..188e9276ea --- /dev/null +++ b/cmd/ateapi/internal/controlapi/workflow_shared_volume.go @@ -0,0 +1,290 @@ +// 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. + +package controlapi + +import ( + "context" + "errors" + "fmt" + "log/slog" + + "go.ate.dev/substrate/cmd/ateapi/internal/store" + "go.ate.dev/substrate/internal/apierror" + "go.ate.dev/substrate/internal/resources" + "go.ate.dev/substrate/internal/volume" + "go.ate.dev/substrate/pkg/proto/ateapipb" + "google.golang.org/grpc/codes" + storagev1 "k8s.io/api/storage/v1" + k8serrors "k8s.io/apimachinery/pkg/api/errors" + storagev1listers "k8s.io/client-go/listers/storage/v1" +) + +// SharedVolumeWorkflow handles the multi-step create and delete of an +// SharedVolume, each of which spans a store write and a CSI call. +type SharedVolumeWorkflow struct { + store sharedVolumeWorkflowStore + storageClassLister storagev1listers.StorageClassLister + pluginRegistry VolumePluginRegistry +} + +// NewSharedVolumeWorkflow creates a new SharedVolumeWorkflow. +func NewSharedVolumeWorkflow( + store sharedVolumeWorkflowStore, + storageClassLister storagev1listers.StorageClassLister, + pluginRegistry VolumePluginRegistry, +) *SharedVolumeWorkflow { + return &SharedVolumeWorkflow{ + store: store, + storageClassLister: storageClassLister, + pluginRegistry: pluginRegistry, + } +} + +type sharedVolumeWorkflowStore interface { + CreateSharedVolume(ctx context.Context, volume *ateapipb.SharedVolume) (*ateapipb.SharedVolume, error) + GetSharedVolume(ctx context.Context, volumeRef resources.SharedVolumeRef) (*ateapipb.SharedVolume, error) + UpdateSharedVolume(ctx context.Context, volumeRef resources.SharedVolumeRef, precondition store.Precondition, mutate func(toUpdate *ateapipb.SharedVolume) error) (*ateapipb.SharedVolume, error) + DeleteSharedVolume(ctx context.Context, volumeRef resources.SharedVolumeRef, precondition store.DeletePreconditions) (*ateapipb.SharedVolume, error) + AcquireLease(ctx context.Context, key string) (*store.Lease, error) +} + +// CreateSharedVolume reserves the name, provisions the volume under an ID +// derived from the record's UID, and stamps the handle the driver returned. +// A create that dies leaves a PENDING record; delete it to retry. +func (w *SharedVolumeWorkflow) CreateSharedVolume(ctx context.Context, volume *ateapipb.SharedVolume) (*ateapipb.SharedVolume, error) { + volumeRef := resources.SharedVolumeRefFromSharedVolume(volume) + + ctx, lease, err := acquireSharedVolumeLease(ctx, w.store, volumeRef) + if err != nil { + return nil, err + } + defer lease.Close() + + toCreate, provisioning, err := w.resolveExternalVolumeSource(ctx, volume) + if err != nil { + return nil, err + } + reserved, err := w.ensureSharedVolumeReserved(ctx, volumeRef, toCreate) + if err != nil { + return nil, err + } + if provisioning == nil { + return reserved, nil + } + volumeID, volumeContext, err := w.ensureSharedVolumeProvisioned(ctx, reserved, provisioning) + if err != nil { + return nil, err + } + return w.ensureSharedVolumeFinalized(ctx, reserved, volumeID, volumeContext) +} + +type sharedVolumeProvisioning struct { + provisioner string + parameters map[string]string +} + +// resolveExternalVolumeSource returns the record to reserve and, unless the +// request registers an existing volume, what provisioning it needs. +func (w *SharedVolumeWorkflow) resolveExternalVolumeSource(ctx context.Context, volume *ateapipb.SharedVolume) (_ *ateapipb.SharedVolume, _ *sharedVolumeProvisioning, err error) { + _, done := stepSpan(ctx, "ResolveExternalVolumeSource") + defer func() { err = done(err) }() + + toCreate := &ateapipb.SharedVolume{ + Metadata: &ateapipb.ResourceMetadata{ + Atespace: volume.GetMetadata().GetAtespace(), + Name: volume.GetMetadata().GetName(), + }, + DeleteTrigger: volume.GetDeleteTrigger(), + AccessMode: volume.GetAccessMode(), + StorageClassName: volume.GetStorageClassName(), + VolumeId: volume.GetVolumeId(), + VolumeType: volume.GetVolumeType(), + VolumeContext: volume.GetVolumeContext(), + Capacity: volume.GetCapacity(), + Status: &ateapipb.SharedVolumeStatus{State: ateapipb.SharedVolumeState_SHARED_VOLUME_STATE_PENDING}, + } + + var sc *storagev1.StorageClass + if scName := volume.GetStorageClassName(); scName != "" { + var err error + if sc, err = w.storageClassLister.Get(scName); err != nil { + if k8serrors.IsNotFound(err) { + return nil, nil, apierror.FailedPrecondition("StorageClass %q not found", scName) + } + return nil, nil, apierror.Internal("failed to get StorageClass %q: %v", scName, err) + } + } + + if toCreate.VolumeId != "" { + if toCreate.DeleteTrigger == ateapipb.DeleteTrigger_DELETE_TRIGGER_UNSPECIFIED { + toCreate.DeleteTrigger = ateapipb.DeleteTrigger_DELETE_TRIGGER_MANUAL + } + if toCreate.VolumeType == "" && sc != nil { + toCreate.VolumeType = sc.Provisioner + } + toCreate.Status.State = ateapipb.SharedVolumeState_SHARED_VOLUME_STATE_READY + return toCreate, nil, nil + } + if sc == nil { + return nil, nil, apierror.InvalidArgument("one of storage_class_name or volume_id is required") + } + if toCreate.DeleteTrigger == ateapipb.DeleteTrigger_DELETE_TRIGGER_UNSPECIFIED { + toCreate.DeleteTrigger = ateapipb.DeleteTrigger_DELETE_TRIGGER_LAST_ACTOR + } + toCreate.VolumeType = sc.Provisioner + return toCreate, &sharedVolumeProvisioning{provisioner: sc.Provisioner, parameters: sc.Parameters}, nil +} + +func (w *SharedVolumeWorkflow) ensureSharedVolumeReserved(ctx context.Context, volumeRef resources.SharedVolumeRef, toCreate *ateapipb.SharedVolume) (_ *ateapipb.SharedVolume, err error) { + ctx, done := stepSpan(ctx, "ReserveSharedVolume") + defer func() { err = done(err) }() + + stored, err := w.store.CreateSharedVolume(ctx, toCreate) + switch { + case err == nil: + return stored, nil + case errors.Is(err, store.ErrFailedPrecondition): + return nil, apierror.FailedPrecondition("Atespace %s not found", volumeRef.Atespace) + case errors.Is(err, store.ErrAlreadyExists): + return nil, apierror.AlreadyExists("SharedVolume %s already exists; delete it and create it again to retry", volumeRef) + } + return nil, fmt.Errorf("while reserving shared volume %s: %w", volumeRef, err) +} + +// TODO: pass access_mode to CreateVolume as the volume capability once +// READ_WRITE_MANY is accepted. +func (w *SharedVolumeWorkflow) ensureSharedVolumeProvisioned(ctx context.Context, volume *ateapipb.SharedVolume, provisioning *sharedVolumeProvisioning) (_ string, _ map[string]string, err error) { + ctx, done := stepSpan(ctx, "ProvisionSharedVolume") + defer func() { err = done(err) }() + + volumeRef := resources.SharedVolumeRefFromSharedVolume(volume) + plugin, err := w.pluginRegistry.GetPlugin(ctx, provisioning.provisioner) + if err != nil { + return "", nil, apierror.FailedPrecondition("failed to get volume plugin for driver %q (StorageClass %q): %v", provisioning.provisioner, volume.GetStorageClassName(), err) + } + resp, err := plugin.CreateVolume(ctx, createVolumeRequest(sharedVolumeID(volume.GetMetadata().GetUid()), volume.GetCapacity(), provisioning.parameters)) + if err != nil { + return "", nil, apierror.Internal("failed to create shared volume %s: %v", volumeRef, err) + } + return resp.VolumeID, resp.VolumeContext, nil +} + +// createVolumeRequest keeps the request construction out of callers whose +// parameters shadow the volume package. +func createVolumeRequest(name, capacity string, parameters map[string]string) volume.CreateVolumeRequest { + return volume.CreateVolumeRequest{Name: name, Capacity: capacity, Parameters: parameters} +} + +func (w *SharedVolumeWorkflow) ensureSharedVolumeFinalized(ctx context.Context, volume *ateapipb.SharedVolume, volumeID string, volumeContext map[string]string) (_ *ateapipb.SharedVolume, err error) { + ctx, done := stepSpan(ctx, "FinalizeSharedVolume") + defer func() { err = done(err) }() + + volumeRef := resources.SharedVolumeRefFromSharedVolume(volume) + stored, err := w.store.UpdateSharedVolume(ctx, volumeRef, store.PreconditionFrom(volume), func(toUpdate *ateapipb.SharedVolume) error { + toUpdate.VolumeId = volumeID + toUpdate.VolumeContext = volumeContext + toUpdate.Status.State = ateapipb.SharedVolumeState_SHARED_VOLUME_STATE_READY + return nil + }) + if err != nil { + if errors.Is(err, store.ErrVersionConflict) { + return nil, apierror.Aborted("concurrent update conflict, please retry") + } + if errors.Is(err, store.ErrNotFound) || errors.Is(err, store.ErrUIDConflict) { + return nil, apierror.Aborted("SharedVolume %s was deleted while it was being created, please retry", volumeRef) + } + return nil, fmt.Errorf("while finalizing shared volume %s: %w", volumeRef, err) + } + return stored, nil +} + +// DeleteSharedVolume deletes the storage and then removes the record, so a +// failure part way is retried by deleting again. Rejected while any actor +// references the volume. +func (w *SharedVolumeWorkflow) DeleteSharedVolume(ctx context.Context, volumeRef resources.SharedVolumeRef, precondition store.DeletePreconditions) (*ateapipb.SharedVolume, error) { + ctx, lease, err := acquireSharedVolumeLease(ctx, w.store, volumeRef) + if err != nil { + return nil, err + } + defer lease.Close() + + stored, err := w.store.GetSharedVolume(ctx, volumeRef) + if err != nil { + if errors.Is(err, store.ErrNotFound) { + return nil, apierror.NotFound("SharedVolume %s not found", volumeRef) + } + return nil, fmt.Errorf("while getting shared volume: %w", err) + } + if err := precondition.Check(stored.GetMetadata()); err != nil { + return nil, sharedVolumeDeleteConflict(err, volumeRef, precondition) + } + if refs := stored.GetStatus().GetRefs(); len(refs) > 0 { + return nil, apierror.FailedPrecondition("SharedVolume %s is still referenced by %d actor(s), starting with %q", volumeRef, len(refs), refs[0].GetActorName()) + } + if err := w.reclaimSharedVolume(ctx, stored); err != nil { + return nil, err + } + + volume, err := w.store.DeleteSharedVolume(ctx, volumeRef, precondition) + if err != nil { + if errors.Is(err, store.ErrNotFound) { + return nil, apierror.NotFound("SharedVolume %s not found", volumeRef) + } + if errors.Is(err, store.ErrUIDConflict) || errors.Is(err, store.ErrVersionConflict) { + return nil, sharedVolumeDeleteConflict(err, volumeRef, precondition) + } + return nil, fmt.Errorf("while deleting shared volume: %w", err) + } + return volume, nil +} + +func sharedVolumeDeleteConflict(err error, volumeRef resources.SharedVolumeRef, precondition store.DeletePreconditions) error { + if errors.Is(err, store.ErrUIDConflict) { + return apierror.Aborted("SharedVolume %s does not have uid %s", volumeRef, precondition.UID) + } + return apierror.Aborted("concurrent update conflict, please retry") +} + +// TODO: add a reclaim policy that leaves the storage in place. +func (w *SharedVolumeWorkflow) reclaimSharedVolume(ctx context.Context, volume *ateapipb.SharedVolume) (err error) { + ctx, done := stepSpan(ctx, "ReclaimSharedVolume") + defer func() { err = done(err) }() + + volumeRef := resources.SharedVolumeRefFromSharedVolume(volume) + if volume.GetVolumeType() == "" { + markSkipped(ctx, "volume names no driver") + return nil + } + volumeID := volume.GetVolumeId() + if volumeID == "" { + volumeID = sharedVolumeID(volume.GetMetadata().GetUid()) + } + plugin, err := w.pluginRegistry.GetPlugin(ctx, volume.GetVolumeType()) + if err != nil { + return fmt.Errorf("while getting the volume plugin for %q of shared volume %s: %w", volume.GetVolumeType(), volumeRef, err) + } + if err := plugin.DeleteVolume(ctx, volumeID); err != nil { + if apierror.Code(err) == codes.NotFound { + slog.WarnContext(ctx, "Volume not found during delete, assuming already deleted", slog.String("volume_id", volumeID)) + return nil + } + return fmt.Errorf("while reclaiming the storage %q of shared volume %s: %w", volumeID, volumeRef, err) + } + return nil +} + +func sharedVolumeID(volumeUID string) string { + return "substrate-" + volumeUID +} diff --git a/cmd/ateapi/internal/controlapi/workflow_suspend.go b/cmd/ateapi/internal/controlapi/workflow_suspend.go index f4c19f8662..e863fc4b40 100644 --- a/cmd/ateapi/internal/controlapi/workflow_suspend.go +++ b/cmd/ateapi/internal/controlapi/workflow_suspend.go @@ -440,10 +440,10 @@ func newInProgressSnapshotURI(actorTemplate *ateapipb.ActorTemplate, actor *atea return uri, nil } -// ensureVolumesDetached detaches the actor's mounted external volumes from -// its worker node. Detachment is idempotent, so a re-entered workflow safely -// runs it again. spanName distinguishes the suspend and revert steps in -// traces; op labels the volume metrics. +// ensureVolumesDetached detaches the actor's mounted external volumes from its +// worker node and marks its SharedVolume references inactive. Both steps are +// idempotent; spanName distinguishes the suspend and revert steps in traces; +// op labels the volume metrics. // TODO replace re-execution with a proper check on the volumes' attach state. func (w *ActorWorkflow) ensureVolumesDetached(ctx context.Context, actor *ateapipb.Actor, actorTemplate *ateapipb.ActorTemplate, spanName, op string) (err error) { ctx, done := stepSpan(ctx, spanName) @@ -454,7 +454,10 @@ func (w *ActorWorkflow) ensureVolumesDetached(ctx context.Context, actor *ateapi // disabling the gate while in use would be an orphaned volume // attachment, we are NOT checking the ExternalVolumes gate in the // delete/suspend path. - return detachActorVolumes(ctx, w.pluginRegistry, actor, actorTemplate, op) + if err := detachActorVolumes(ctx, w.pluginRegistry, actor, actorTemplate, op); err != nil { + return err + } + return deactivateSharedVolumes(ctx, w.store, actor) } // ensureSuspendedFinalized releases the actor's worker (only when it is still diff --git a/cmd/ateapi/internal/controlapi/workflow_testutil_test.go b/cmd/ateapi/internal/controlapi/workflow_testutil_test.go index 2361997237..9bb7e007b2 100644 --- a/cmd/ateapi/internal/controlapi/workflow_testutil_test.go +++ b/cmd/ateapi/internal/controlapi/workflow_testutil_test.go @@ -61,7 +61,7 @@ func newTestActorWorkflow(t *testing.T, st store.Interface, tmplAtespace, tmplNa }); err != nil && !errors.Is(err, store.ErrAlreadyExists) { t.Fatalf("create test ActorTemplate: %v", err) } - return NewActorWorkflow(st, nil, nil, nil, nil, nil, "", nil, objectstoreplugintest.ControlClient(objectstoretest.New())) + return NewActorWorkflow(st, nil, nil, nil, nil, nil, "", nil, objectstoreplugintest.ControlClient(objectstoretest.New()), nil) } // newFinalizeWorkflow builds an ActorWorkflow over persistence with an diff --git a/cmd/ateapi/internal/controlapi/workload_spec.go b/cmd/ateapi/internal/controlapi/workload_spec.go index be06a1ec56..2010a11973 100644 --- a/cmd/ateapi/internal/controlapi/workload_spec.go +++ b/cmd/ateapi/internal/controlapi/workload_spec.go @@ -55,6 +55,12 @@ func workloadSpecFromActorTemplate(actorTemplate *ateapipb.ActorTemplate, actor // already ensured that only one source is set. for _, vol := range actorTemplate.GetVolumes() { switch { + case vol.GetExternal().GetSharedVolume() != nil && isGoldenActor(actor): + workloadSpec.Volumes = append(workloadSpec.Volumes, &ateletpb.Volume{ + Name: vol.GetName(), + EmptyDir: &ateletpb.EmptyDirVolumeSource{}, + }) + case vol.GetDurableDir() != nil: workloadSpec.Volumes = append(workloadSpec.Volumes, &ateletpb.Volume{ Name: vol.GetName(), @@ -102,8 +108,11 @@ func workloadSpecFromActorTemplate(actorTemplate *ateapipb.ActorTemplate, actor }, }) + case vol.GetExternal().GetTemplate() != nil, vol.GetExternal().GetSharedVolume() != nil: + continue // appended by appendExternalVolumes from the actor's status + default: - continue // Drop unrecognized volumes. + return nil, fmt.Errorf("volume %q has no supported source", vol.GetName()) } } @@ -147,21 +156,24 @@ func workloadSpecFromActorTemplate(actorTemplate *ateapipb.ActorTemplate, actor return workloadSpec, nil } -// appendExternalVolumes maps template external volumes to resolved actor volumes -// and appends them to workloadSpec if they are referenced in container -// volumeMounts. volumePublishContexts carries the attachment metadata of the current -// resume by volume name; it is nil on paths that do not mount. +// appendExternalVolumes maps mounted external volumes, owned or referenced, to +// the handles recorded on the actor and appends them to workloadSpec. +// volumePublishContexts carries the attachment metadata of the current resume +// by volume name; it is nil on paths that do not mount. func appendExternalVolumes(workloadSpec *ateletpb.WorkloadSpec, template *ateapipb.ActorTemplate, actor *ateapipb.Actor, volumePublishContexts map[string]map[string]string) error { if template == nil { return nil } for _, vol := range template.GetVolumes() { - if vol.GetExternalVolumeTemplate() != nil { + if vol.GetExternal().GetTemplate() != nil || vol.GetExternal().GetSharedVolume() != nil { if !isVolumeMounted(vol.GetName(), template) { continue } + if vol.GetExternal().GetSharedVolume() != nil && isGoldenActor(actor) { + continue + } if actor == nil { - return fmt.Errorf("actor is required when externalVolumeTemplate is present") + return fmt.Errorf("actor is required when an external volume is present") } var storageVolID string diff --git a/cmd/ateapi/internal/controlapi/workload_spec_test.go b/cmd/ateapi/internal/controlapi/workload_spec_test.go index 35032f5c2f..8d768d5656 100644 --- a/cmd/ateapi/internal/controlapi/workload_spec_test.go +++ b/cmd/ateapi/internal/controlapi/workload_spec_test.go @@ -15,10 +15,12 @@ package controlapi import ( + "strings" "testing" "github.com/google/go-cmp/cmp" "go.ate.dev/substrate/internal/proto/ateletpb" + "go.ate.dev/substrate/internal/resources" "go.ate.dev/substrate/pkg/proto/ateapipb" "google.golang.org/protobuf/testing/protocmp" ) @@ -215,11 +217,12 @@ func TestWorkloadSpecFromActorTemplate(t *testing.T) { }, }, { - name: "skips non-DurableDir volumes", + name: "leaves unmounted external volumes out", template: &ateapipb.ActorTemplate{ Metadata: &ateapipb.ResourceMetadata{Atespace: "agent-ns", Name: "tmpl1"}, Volumes: []*ateapipb.Volume{ - {Name: "unsupported"}, + {Name: "borrowed", External: &ateapipb.ExternalVolumeSource{SharedVolume: &ateapipb.SharedVolumeRef{Name: "shared"}}}, + {Name: "owned", External: &ateapipb.ExternalVolumeSource{Template: &ateapipb.ExternalVolumeTemplate{StorageClassName: "standard"}}}, {Name: "home", DurableDir: &ateapipb.DurableDirVolumeSource{}}, }, Containers: []*ateapipb.Container{ @@ -390,9 +393,9 @@ func TestAppendExternalVolumes(t *testing.T) { Volumes: []*ateapipb.Volume{ { Name: "vol-1", - ExternalVolumeTemplate: &ateapipb.ExternalVolumeTemplate{ + External: &ateapipb.ExternalVolumeSource{Template: &ateapipb.ExternalVolumeTemplate{ StorageClassName: "pd-standard", - }, + }}, }, { Name: "vol-2", @@ -400,9 +403,9 @@ func TestAppendExternalVolumes(t *testing.T) { }, { Name: "unmounted-vol", - ExternalVolumeTemplate: &ateapipb.ExternalVolumeTemplate{ + External: &ateapipb.ExternalVolumeSource{Template: &ateapipb.ExternalVolumeTemplate{ StorageClassName: "pd-standard", - }, + }}, }, }, } @@ -642,3 +645,90 @@ func TestWorkloadSpecFromActorTemplatePropagatesResources(t *testing.T) { t.Errorf("unlimited container Resources = %v, want nil", r) } } + +func TestWorkloadSpecFromActorTemplateRejectsUnknownVolumeSource(t *testing.T) { + _, err := workloadSpecFromActorTemplate(&ateapipb.ActorTemplate{ + Metadata: &ateapipb.ResourceMetadata{Atespace: "agent-ns", Name: "tmpl-unknown"}, + Volumes: []*ateapipb.Volume{{Name: "unsupported"}}, + }, nil, nil) + if err == nil || !strings.Contains(err.Error(), "unsupported") { + t.Fatalf("workloadSpecFromActorTemplate() error = %v, want one naming the volume", err) + } +} + +func TestWorkloadSpecBorrowedVolume(t *testing.T) { + template := &ateapipb.ActorTemplate{ + Metadata: &ateapipb.ResourceMetadata{Atespace: "space-abc", Name: "tmpl1"}, + Volumes: []*ateapipb.Volume{ + {Name: "borrowed", External: &ateapipb.ExternalVolumeSource{SharedVolume: &ateapipb.SharedVolumeRef{}}}, + {Name: "owned", External: &ateapipb.ExternalVolumeSource{Template: &ateapipb.ExternalVolumeTemplate{StorageClassName: "standard"}}}, + }, + Containers: []*ateapipb.Container{{ + Name: "main", + VolumeMounts: []*ateapipb.VolumeMount{ + {Name: "borrowed", MountPath: "/mnt/shared"}, + {Name: "owned", MountPath: "/mnt/owned"}, + }, + }}, + } + owned := &ateletpb.Volume{ + Name: "owned", + External: &ateletpb.ExternalVolumeSource{ + StorageVolumeId: "disk-1", + VolumeType: "pd.csi.storage.gke.io", + }, + } + + tests := []struct { + name string + actor *ateapipb.Actor + want []*ateletpb.Volume + }{ + { + name: "golden actor gets an empty dir in place of the shared volume", + actor: &ateapipb.Actor{ + Metadata: &ateapipb.ResourceMetadata{Atespace: resources.GoldenActorAtespace, Name: "tmpl1-uid"}, + Status: &ateapipb.ActorStatus{ExternalVolumes: []*ateapipb.ExternalVolume{ + {Name: "owned", StorageVolumeId: "disk-1", VolumeType: "pd.csi.storage.gke.io"}, + }}, + }, + want: []*ateletpb.Volume{ + {Name: "borrowed", EmptyDir: &ateletpb.EmptyDirVolumeSource{}}, + owned, + }, + }, + { + name: "user actor mounts the shared volume's handle", + actor: &ateapipb.Actor{ + Metadata: &ateapipb.ResourceMetadata{Atespace: "space-abc", Name: "consumer"}, + SharedVolumeBindings: map[string]string{"borrowed": "shared"}, + Status: &ateapipb.ActorStatus{ExternalVolumes: []*ateapipb.ExternalVolume{ + {Name: "borrowed", SharedVolumeName: "shared", StorageVolumeId: "disk-shared", VolumeType: "nfs.csi.k8s.io", VolumeContext: map[string]string{"server": "10.0.0.2"}}, + {Name: "owned", StorageVolumeId: "disk-1", VolumeType: "pd.csi.storage.gke.io"}, + }}, + }, + want: []*ateletpb.Volume{ + { + Name: "borrowed", + External: &ateletpb.ExternalVolumeSource{ + StorageVolumeId: "disk-shared", + VolumeType: "nfs.csi.k8s.io", + VolumeContext: map[string]string{"server": "10.0.0.2"}, + }, + }, + owned, + }, + }, + } + for _, tc := range tests { + t.Run(tc.name, func(t *testing.T) { + got, err := workloadSpecFromActorTemplate(template, tc.actor, nil) + if err != nil { + t.Fatalf("workloadSpecFromActorTemplate: %v", err) + } + if diff := cmp.Diff(tc.want, got.GetVolumes(), protocmp.Transform()); diff != "" { + t.Errorf("volumes mismatch (-want +got):\n%s", diff) + } + }) + } +} diff --git a/cmd/ateapi/internal/defaults/defaults.go b/cmd/ateapi/internal/defaults/defaults.go index 73849186a5..b2200d1eb3 100644 --- a/cmd/ateapi/internal/defaults/defaults.go +++ b/cmd/ateapi/internal/defaults/defaults.go @@ -38,6 +38,8 @@ func Apply(m proto.Message) { applyAtespaceDefaults(v) case *ateapipb.EgressPolicy: applyEgressPolicyDefaults(v) + case *ateapipb.SharedVolume: + applySharedVolumeDefaults(v) case *ateapipb.Tag: applyTagDefaults(v) case *ateapipb.Worker: @@ -111,6 +113,15 @@ func applyEgressPolicyDefaults(p *ateapipb.EgressPolicy) { } } +func applySharedVolumeDefaults(v *ateapipb.SharedVolume) { + if v == nil { + return + } + if v.AccessMode == ateapipb.AccessMode_ACCESS_MODE_UNSPECIFIED { + v.AccessMode = ateapipb.AccessMode_ACCESS_MODE_READ_WRITE_ONCE + } +} + func applyTagDefaults(*ateapipb.Tag) {} func applyWorkerDefaults(*ateapipb.Worker) {} diff --git a/cmd/ateapi/internal/defaults/defaults_test.go b/cmd/ateapi/internal/defaults/defaults_test.go index 9c82226087..1e2b179e24 100644 --- a/cmd/ateapi/internal/defaults/defaults_test.go +++ b/cmd/ateapi/internal/defaults/defaults_test.go @@ -57,6 +57,14 @@ func TestApply(t *testing.T) { PreferredFidelity: scopeData, GoldenSnapshotConfig: &ateapipb.GoldenSnapshotConfig{Mode: ateapipb.GoldenSnapshotMode_GOLDEN_SNAPSHOT_MODE_ENABLED}, }}, + }, { + name: "external volume without an access mode gets read write once", + in: &ateapipb.SharedVolume{}, + want: &ateapipb.SharedVolume{AccessMode: ateapipb.AccessMode_ACCESS_MODE_READ_WRITE_ONCE}, + }, { + name: "external volume keeps its access mode", + in: &ateapipb.SharedVolume{AccessMode: ateapipb.AccessMode_ACCESS_MODE_READ_WRITE_MANY}, + want: &ateapipb.SharedVolume{AccessMode: ateapipb.AccessMode_ACCESS_MODE_READ_WRITE_MANY}, }, { name: "container without wakeup probe stays without one", in: &ateapipb.ActorTemplate{Containers: []*ateapipb.Container{{Name: "main"}}}, diff --git a/cmd/ateapi/internal/store/atepg/common_test.go b/cmd/ateapi/internal/store/atepg/common_test.go index accb18c49f..dd0ca2d162 100644 --- a/cmd/ateapi/internal/store/atepg/common_test.go +++ b/cmd/ateapi/internal/store/atepg/common_test.go @@ -28,7 +28,7 @@ import ( // state, so the statement lives here rather than on Persistence. func clearAll(t *testing.T, p *Persistence) { t.Helper() - if _, err := p.pool.Exec(context.Background(), `TRUNCATE atespaces, global_access_policy, atespace_access_policies, actors, actor_egress_policies, actor_templates, tags, workers, worker_assignments, leases, worker_outbox, worker_outbox_trim, tuple, changelog`); err != nil { + if _, err := p.pool.Exec(context.Background(), `TRUNCATE atespaces, global_access_policy, atespace_access_policies, actors, actor_egress_policies, actor_templates, tags, shared_volumes, workers, worker_assignments, leases, worker_outbox, worker_outbox_trim, tuple, changelog`); err != nil { t.Fatalf("truncating tables: %v", err) } } diff --git a/cmd/ateapi/internal/store/atepg/migrations/000004_shared_volumes.sql b/cmd/ateapi/internal/store/atepg/migrations/000004_shared_volumes.sql new file mode 100644 index 0000000000..c63c55dd8d --- /dev/null +++ b/cmd/ateapi/internal/store/atepg/migrations/000004_shared_volumes.sql @@ -0,0 +1,27 @@ +-- 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. + +-- +goose Up + +-- RESTRICT so that deleting an atespace cannot strand provisioned storage. +CREATE TABLE shared_volumes ( + atespace text NOT NULL, + name text NOT NULL, + uid text NOT NULL, + version bigint NOT NULL, + proto bytea NOT NULL, + PRIMARY KEY (atespace, name), + CONSTRAINT shared_volumes_atespace_fk + FOREIGN KEY (atespace) REFERENCES atespaces(name) ON DELETE RESTRICT +); diff --git a/cmd/ateapi/internal/store/atepg/pagetoken.go b/cmd/ateapi/internal/store/atepg/pagetoken.go index 837a84c98b..12b1175094 100644 --- a/cmd/ateapi/internal/store/atepg/pagetoken.go +++ b/cmd/ateapi/internal/store/atepg/pagetoken.go @@ -35,6 +35,7 @@ const ( kindActor resourceKind = "actor" kindActorTemplate resourceKind = "actor-template" kindTag resourceKind = "tag" + kindExternalVol resourceKind = "external-volume" kindWorker resourceKind = "worker" kindWorkerAssign resourceKind = "worker-assignment" ) diff --git a/cmd/ateapi/internal/store/atepg/partition_test.go b/cmd/ateapi/internal/store/atepg/partition_test.go index 3da1a7497b..9b58e6506e 100644 --- a/cmd/ateapi/internal/store/atepg/partition_test.go +++ b/cmd/ateapi/internal/store/atepg/partition_test.go @@ -45,7 +45,7 @@ func TestActorsTablePartitionable(t *testing.T) { // by definition, under each partition key. spansPartitions := map[string][]string{ // A global list walks every atespace. - "atespace": {globalList("actors"), globalList("actor_templates"), globalList("tags")}, + "atespace": {globalList("actors"), globalList("actor_templates"), globalList("tags"), globalList("shared_volumes")}, // A global list walks every name, and a list within one atespace // spans every name hash. "name": {globalList("actors"), scopedActorList}, diff --git a/cmd/ateapi/internal/store/atepg/shared_volume.go b/cmd/ateapi/internal/store/atepg/shared_volume.go new file mode 100644 index 0000000000..7b2334cfc0 --- /dev/null +++ b/cmd/ateapi/internal/store/atepg/shared_volume.go @@ -0,0 +1,297 @@ +// 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. + +package atepg + +import ( + "context" + "errors" + "fmt" + "maps" + + "github.com/jackc/pgx/v5" + "go.ate.dev/substrate/cmd/ateapi/internal/store" + "go.ate.dev/substrate/internal/resources" + "go.ate.dev/substrate/pkg/proto/ateapipb" + "google.golang.org/protobuf/proto" +) + +func (p *Persistence) GetSharedVolume(ctx context.Context, volumeRef resources.SharedVolumeRef) (*ateapipb.SharedVolume, error) { + atespace, name := volumeRef.Atespace, volumeRef.Name + var protoBytes []byte + if err := p.pool.QueryRow(ctx, ` + SELECT proto FROM shared_volumes + WHERE atespace = $1 AND name = $2`, atespace, name).Scan(&protoBytes); err != nil { + if errors.Is(err, pgx.ErrNoRows) { + return nil, store.ErrNotFound + } + return nil, fmt.Errorf("getting shared volume %s/%s: %w", atespace, name, err) + } + volume := &ateapipb.SharedVolume{} + if err := unmarshalStored(protoBytes, volume); err != nil { + return nil, fmt.Errorf("unmarshaling shared volume: %w", err) + } + return volume, nil +} + +func (p *Persistence) ListSharedVolumes(ctx context.Context, atespace string, opts store.ListOptions) (store.ListResponse[*ateapipb.SharedVolume], error) { + opts, err := store.NormalizeListOptions(opts) + if err != nil { + return store.ListResponse[*ateapipb.SharedVolume]{}, err + } + var items []*ateapipb.SharedVolume + var nextToken string + if atespace != "" { + items, nextToken, err = p.listSharedVolumesScoped(ctx, atespace, opts.PageSize, opts.PageToken) + } else { + items, nextToken, err = p.listSharedVolumesGlobal(ctx, opts.PageSize, opts.PageToken) + } + if err != nil { + return store.ListResponse[*ateapipb.SharedVolume]{}, err + } + return store.ListResponse[*ateapipb.SharedVolume]{Items: items, NextPageToken: nextToken}, nil +} + +func (p *Persistence) listSharedVolumesScoped(ctx context.Context, atespace string, pageSize int32, pageTokenStr string) ([]*ateapipb.SharedVolume, string, error) { + token, err := decodePageToken(pageTokenStr, kindExternalVol, atespace, 1) + if err != nil { + return nil, "", err + } + var last *string + if len(token.Last) > 0 { + last = &token.Last[0] + } + rows, err := p.pool.Query(ctx, ` + SELECT name, proto FROM shared_volumes + WHERE atespace = $1 AND ($2::text IS NULL OR name > $2) + ORDER BY name + LIMIT $3`, atespace, last, int64(pageSize)+1) + if err != nil { + return nil, "", fmt.Errorf("listing shared volumes in %q: %w", atespace, err) + } + defer rows.Close() + + var names []string + var result []*ateapipb.SharedVolume + for rows.Next() { + var name string + var protoBytes []byte + if err := rows.Scan(&name, &protoBytes); err != nil { + return nil, "", fmt.Errorf("scanning shared volume row: %w", err) + } + volume := &ateapipb.SharedVolume{} + if err := unmarshalStored(protoBytes, volume); err != nil { + return nil, "", fmt.Errorf("unmarshaling shared volume: %w", err) + } + result = append(result, volume) + names = append(names, name) + } + if err := rows.Err(); err != nil { + return nil, "", fmt.Errorf("listing shared volumes in %q: %w", atespace, err) + } + var nextToken string + if len(result) > int(pageSize) { + result = result[:pageSize] + nextToken = encodePageToken(kindExternalVol, atespace, []string{names[pageSize-1]}) + } + return result, nextToken, nil +} + +func (p *Persistence) listSharedVolumesGlobal(ctx context.Context, pageSize int32, pageTokenStr string) ([]*ateapipb.SharedVolume, string, error) { + token, err := decodePageToken(pageTokenStr, kindExternalVol, "", 2) + if err != nil { + return nil, "", err + } + var lastAtespace, lastName *string + if len(token.Last) == 2 { + lastAtespace, lastName = &token.Last[0], &token.Last[1] + } + rows, err := p.pool.Query(ctx, ` + SELECT atespace, name, proto FROM shared_volumes + WHERE $1::text IS NULL OR (atespace, name) > ($1, $2) + ORDER BY atespace, name + LIMIT $3`, lastAtespace, lastName, int64(pageSize)+1) + if err != nil { + return nil, "", fmt.Errorf("listing shared volumes: %w", err) + } + defer rows.Close() + + type key struct{ atespace, name string } + var keys []key + var result []*ateapipb.SharedVolume + for rows.Next() { + var k key + var protoBytes []byte + if err := rows.Scan(&k.atespace, &k.name, &protoBytes); err != nil { + return nil, "", fmt.Errorf("scanning shared volume row: %w", err) + } + volume := &ateapipb.SharedVolume{} + if err := unmarshalStored(protoBytes, volume); err != nil { + return nil, "", fmt.Errorf("unmarshaling shared volume: %w", err) + } + result = append(result, volume) + keys = append(keys, k) + } + if err := rows.Err(); err != nil { + return nil, "", fmt.Errorf("listing shared volumes: %w", err) + } + var nextToken string + if len(result) > int(pageSize) { + result = result[:pageSize] + last := keys[pageSize-1] + nextToken = encodePageToken(kindExternalVol, "", []string{last.atespace, last.name}) + } + return result, nextToken, nil +} + +func (p *Persistence) CreateSharedVolume(ctx context.Context, volume *ateapipb.SharedVolume) (*ateapipb.SharedVolume, error) { + atespace := volume.GetMetadata().GetAtespace() + name := volume.GetMetadata().GetName() + dbVolume := proto.CloneOf(volume) + setCreateMetadata(dbVolume.Metadata) + protoBytes, err := proto.Marshal(dbVolume) + if err != nil { + return nil, fmt.Errorf("marshaling shared volume: %w", err) + } + + _, err = p.pool.Exec(ctx, ` + INSERT INTO shared_volumes (atespace, name, uid, version, proto) + VALUES ($1, $2, $3, $4, $5)`, atespace, name, + dbVolume.GetMetadata().GetUid(), dbVolume.GetMetadata().GetVersion(), protoBytes) + if err != nil { + if isUniqueViolation(err) { + return nil, store.ErrAlreadyExists + } + if isForeignKeyViolation(err) { + switch pgErrConstraint(err) { + case "shared_volumes_atespace_fk": + return nil, store.ErrFailedPrecondition + default: + return nil, fmt.Errorf("inserting shared volume %s/%s violated unknown foreign key %q: %w", atespace, name, pgErrConstraint(err), err) + } + } + return nil, fmt.Errorf("inserting shared volume %s/%s: %w", atespace, name, err) + } + return dbVolume, nil +} + +func validateUpdateSharedVolumeMutation(storedVolume, mutatedVolume *ateapipb.SharedVolume) error { + if stored, mutated := storedVolume.GetMetadata().GetAtespace(), mutatedVolume.GetMetadata().GetAtespace(); stored != mutated { + return fmt.Errorf("metadata.atespace is immutable: mutation changed it from %q to %q", stored, mutated) + } + if stored, mutated := storedVolume.GetMetadata().GetName(), mutatedVolume.GetMetadata().GetName(); stored != mutated { + return fmt.Errorf("metadata.name is immutable: mutation changed it from %q to %q", stored, mutated) + } + if stored, mutated := storedVolume.GetAccessMode(), mutatedVolume.GetAccessMode(); stored != ateapipb.AccessMode_ACCESS_MODE_UNSPECIFIED && stored != mutated { + return fmt.Errorf("access_mode is immutable once set: mutation changed it from %v to %v", stored, mutated) + } + if stored, mutated := storedVolume.GetStorageClassName(), mutatedVolume.GetStorageClassName(); stored != "" && stored != mutated { + return fmt.Errorf("storage_class_name is immutable once set: mutation changed it from %q to %q", stored, mutated) + } + if stored, mutated := storedVolume.GetCapacity(), mutatedVolume.GetCapacity(); stored != "" && stored != mutated { + return fmt.Errorf("capacity is immutable once set: mutation changed it from %q to %q", stored, mutated) + } + if stored, mutated := storedVolume.GetVolumeId(), mutatedVolume.GetVolumeId(); stored != "" && stored != mutated { + return fmt.Errorf("volume_id is immutable once set: mutation changed it from %q to %q", stored, mutated) + } + if stored, mutated := storedVolume.GetVolumeType(), mutatedVolume.GetVolumeType(); stored != "" && stored != mutated { + return fmt.Errorf("volume_type is immutable once set: mutation changed it from %q to %q", stored, mutated) + } + if stored, mutated := storedVolume.GetVolumeContext(), mutatedVolume.GetVolumeContext(); len(stored) > 0 && !maps.Equal(stored, mutated) { + return fmt.Errorf("volume_context is immutable once set: mutation changed it from %v to %v", stored, mutated) + } + return nil +} + +func (p *Persistence) UpdateSharedVolume(ctx context.Context, volumeRef resources.SharedVolumeRef, precondition store.Precondition, mutate func(*ateapipb.SharedVolume) error) (*ateapipb.SharedVolume, error) { + if err := precondition.Validate(); err != nil { + return nil, err + } + atespace, name := volumeRef.Atespace, volumeRef.Name + var currentUID string + var currentVersion int64 + var currentBytes []byte + if err := p.pool.QueryRow(ctx, ` + SELECT uid, version, proto FROM shared_volumes + WHERE atespace = $1 AND name = $2`, atespace, name).Scan(¤tUID, ¤tVersion, ¤tBytes); err != nil { + if errors.Is(err, pgx.ErrNoRows) { + return nil, store.ErrNotFound + } + return nil, fmt.Errorf("getting shared volume %s/%s for update: %w", atespace, name, err) + } + + dbVolume := &ateapipb.SharedVolume{} + if err := unmarshalStored(currentBytes, dbVolume); err != nil { + return nil, fmt.Errorf("unmarshaling shared volume: %w", err) + } + if err := validateProtoMetadataMatchesColumns(fmt.Sprintf("shared volume %s/%s", atespace, name), dbVolume.GetMetadata(), currentUID, currentVersion); err != nil { + return nil, err + } + if err := precondition.Check(dbVolume.GetMetadata()); err != nil { + return nil, err + } + volumeBeforeMutation := proto.CloneOf(dbVolume) + if err := mutate(dbVolume); err != nil { + return nil, err + } + if err := validateUpdateSharedVolumeMutation(volumeBeforeMutation, dbVolume); err != nil { + return nil, fmt.Errorf("%w: %w", store.ErrImmutableField, err) + } + setUpdateMetadata(dbVolume.Metadata, volumeBeforeMutation.GetMetadata()) + + updatedBytes, err := proto.Marshal(dbVolume) + if err != nil { + return nil, fmt.Errorf("marshaling shared volume: %w", err) + } + commandTag, err := p.pool.Exec(ctx, ` + UPDATE shared_volumes + SET version = $1, proto = $2 + WHERE atespace = $3 AND name = $4 AND uid = $5 AND version = $6`, + dbVolume.GetMetadata().GetVersion(), updatedBytes, atespace, name, currentUID, currentVersion) + if err != nil { + return nil, fmt.Errorf("updating shared volume %s/%s: %w", atespace, name, err) + } + if commandTag.RowsAffected() == 0 { + return nil, store.ErrVersionConflict + } + if commandTag.RowsAffected() != 1 { + return nil, fmt.Errorf("updating shared volume %s/%s affected %d rows, want 1", atespace, name, commandTag.RowsAffected()) + } + return dbVolume, nil +} + +func (p *Persistence) DeleteSharedVolume(ctx context.Context, volumeRef resources.SharedVolumeRef, precondition store.DeletePreconditions) (*ateapipb.SharedVolume, error) { + atespace, name := volumeRef.Atespace, volumeRef.Name + var protoBytes []byte + err := p.pool.QueryRow(ctx, ` + DELETE FROM shared_volumes + WHERE atespace = $1 AND name = $2 + AND ($3::text = '' OR uid = $3::text) + AND ($4::bigint = 0 OR version = $4::bigint) + RETURNING proto`, atespace, name, precondition.UID, precondition.Version).Scan(&protoBytes) + if errors.Is(err, pgx.ErrNoRows) { + var uid string + var version int64 + err := p.pool.QueryRow(ctx, `SELECT uid, version FROM shared_volumes WHERE atespace = $1 AND name = $2`, atespace, name).Scan(&uid, &version) + return nil, mapDeleteError(err, uid, version, precondition) + } + if err != nil { + return nil, fmt.Errorf("deleting shared volume %s/%s: %w", atespace, name, err) + } + volume := &ateapipb.SharedVolume{} + if err := unmarshalStored(protoBytes, volume); err != nil { + return nil, fmt.Errorf("unmarshaling deleted shared volume: %w", err) + } + return volume, nil +} diff --git a/cmd/ateapi/internal/store/store.go b/cmd/ateapi/internal/store/store.go index 254bf64f44..b155618b88 100644 --- a/cmd/ateapi/internal/store/store.go +++ b/cmd/ateapi/internal/store/store.go @@ -159,6 +159,26 @@ type Interface interface { // tag the caller observed. DeleteTag(ctx context.Context, tagRef resources.TagRef, precondition DeletePreconditions) (*ateapipb.Tag, error) + // Stores a new external volume and returns it with server-assigned metadata. + // Returns ErrAlreadyExists if the name is taken, or ErrFailedPrecondition if + // the atespace does not exist. + CreateSharedVolume(ctx context.Context, volume *ateapipb.SharedVolume) (*ateapipb.SharedVolume, error) + + // Fetches an external volume by reference. Returns ErrNotFound if missing. + GetSharedVolume(ctx context.Context, volumeRef resources.SharedVolumeRef) (*ateapipb.SharedVolume, error) + + // Lists external volumes in one atespace, or all atespaces when empty. + ListSharedVolumes(ctx context.Context, atespace string, opts ListOptions) (ListResponse[*ateapipb.SharedVolume], error) + + // UpdateSharedVolume performs a guarded read-modify-write like UpdateTag. + // Everything naming the storage, and access_mode, is set-once and returns + // ErrImmutableField when changed. + UpdateSharedVolume(ctx context.Context, volumeRef resources.SharedVolumeRef, precondition Precondition, mutate func(toUpdate *ateapipb.SharedVolume) error) (*ateapipb.SharedVolume, error) + + // Deletes and returns an external volume record; the storage is the caller's + // job. + DeleteSharedVolume(ctx context.Context, volumeRef resources.SharedVolumeRef, precondition DeletePreconditions) (*ateapipb.SharedVolume, error) + // Stores a new atespace and returns the stored resource with server-assigned // metadata (uid, version, timestamps). The input is not mutated. Returns // ErrAlreadyExists if the name is taken. diff --git a/cmd/ateapi/internal/store/storecontract/contract.go b/cmd/ateapi/internal/store/storecontract/contract.go index 847e7cc926..150b6aa7b3 100644 --- a/cmd/ateapi/internal/store/storecontract/contract.go +++ b/cmd/ateapi/internal/store/storecontract/contract.go @@ -209,6 +209,7 @@ func RunContractTests(t *testing.T, setup func(t *testing.T) store.Interface) { runAtespaceContractTests(t, setup) runActorTemplateContractTests(t, setup) runTagContractTests(t, setup) + runSharedVolumeContractTests(t, setup) runLeaseContractTests(t, setup) runListOptionsContractTests(t, setup) runUnknownFieldContractTests(t, setup) @@ -394,6 +395,7 @@ func runListOptionsContractTests(t *testing.T, setup func(t *testing.T) store.In {"actors", func(opts store.ListOptions) error { _, err := s.ListActors(ctx, "", opts); return err }}, {"actor templates", func(opts store.ListOptions) error { _, err := s.ListActorTemplates(ctx, "", opts); return err }}, {"tags", func(opts store.ListOptions) error { _, err := s.ListTags(ctx, "", opts); return err }}, + {"external volumes", func(opts store.ListOptions) error { _, err := s.ListSharedVolumes(ctx, "", opts); return err }}, {"workers", func(opts store.ListOptions) error { _, err := s.ListWorkers(ctx, opts); return err }}, } for _, call := range calls { @@ -419,6 +421,7 @@ func runListOptionsContractTests(t *testing.T, setup func(t *testing.T) store.In {"actors", func(opts store.ListOptions) error { _, err := s.ListActors(ctx, "", opts); return err }}, {"actor templates", func(opts store.ListOptions) error { _, err := s.ListActorTemplates(ctx, "", opts); return err }}, {"tags", func(opts store.ListOptions) error { _, err := s.ListTags(ctx, "", opts); return err }}, + {"external volumes", func(opts store.ListOptions) error { _, err := s.ListSharedVolumes(ctx, "", opts); return err }}, {"workers", func(opts store.ListOptions) error { _, err := s.ListWorkers(ctx, opts); return err }}, } for _, call := range calls { @@ -1574,6 +1577,302 @@ func storeTag(t *testing.T, s store.Interface, tag *ateapipb.Tag) *ateapipb.Tag return stored } +func newTestPendingSharedVolume(atespace, name string) *ateapipb.SharedVolume { + return &ateapipb.SharedVolume{ + Metadata: &ateapipb.ResourceMetadata{Atespace: atespace, Name: name}, + DeleteTrigger: ateapipb.DeleteTrigger_DELETE_TRIGGER_LAST_ACTOR, + AccessMode: ateapipb.AccessMode_ACCESS_MODE_READ_WRITE_ONCE, + StorageClassName: "standard-rwx", + Status: &ateapipb.SharedVolumeStatus{State: ateapipb.SharedVolumeState_SHARED_VOLUME_STATE_PENDING}, + } +} + +func finalizeSharedVolume(toUpdate *ateapipb.SharedVolume) error { + toUpdate.VolumeId = "substrate-" + toUpdate.GetMetadata().GetUid() + toUpdate.VolumeType = "filestore.csi.storage.gke.io" + toUpdate.VolumeContext = map[string]string{"ip": "10.0.0.2"} + toUpdate.Status.State = ateapipb.SharedVolumeState_SHARED_VOLUME_STATE_READY + return nil +} + +func storeSharedVolume(t *testing.T, s store.Interface, volume *ateapipb.SharedVolume) *ateapipb.SharedVolume { + t.Helper() + reserved, err := s.CreateSharedVolume(context.Background(), volume) + if err != nil { + t.Fatalf("CreateSharedVolume(%q) failed: %v", volume.GetMetadata().GetName(), err) + } + stored, err := s.UpdateSharedVolume(context.Background(), resources.SharedVolumeRefFromSharedVolume(reserved), store.PreconditionFrom(reserved), finalizeSharedVolume) + if err != nil { + t.Fatalf("finalizing external volume %q failed: %v", volume.GetMetadata().GetName(), err) + } + return stored +} + +func runSharedVolumeContractTests(t *testing.T, setup func(t *testing.T) store.Interface) { + t.Helper() + + t.Run("SharedVolume_Lifecycle", func(t *testing.T) { + s := setup(t) + ctx := context.Background() + mustCreateAtespace(t, s, "team-a") + + input := newTestPendingSharedVolume("team-a", "scratch") + pending, err := s.CreateSharedVolume(ctx, input) + if err != nil { + t.Fatalf("CreateSharedVolume failed: %v", err) + } + if pending.GetMetadata().GetVersion() != 1 || pending.GetMetadata().GetUid() == "" { + t.Errorf("created volume metadata = %v, want server-owned uid and version 1", pending.GetMetadata()) + } + if input.GetMetadata().GetUid() != "" || input.GetMetadata().GetVersion() != 0 { + t.Errorf("CreateSharedVolume mutated its input metadata: %v", input.GetMetadata()) + } + + volumeRef := resources.SharedVolumeRef{Atespace: "team-a", Name: "scratch"} + volume, err := s.GetSharedVolume(ctx, volumeRef) + if err != nil { + t.Fatalf("GetSharedVolume failed: %v", err) + } + if volume.GetVolumeId() != "" { + t.Errorf("reserved volume id = %q, want unset", volume.GetVolumeId()) + } + if diff := cmp.Diff(input, volume, protocmp.Transform(), ignoreUID, ignoreVersion, ignoreTimestamps); diff != "" { + t.Errorf("stored external volume mismatch (-want +got):\n%s", diff) + } + if _, err := s.GetSharedVolume(ctx, resources.SharedVolumeRef{Atespace: "team-a", Name: "missing"}); !errors.Is(err, store.ErrNotFound) { + t.Errorf("missing GetSharedVolume = %v, want ErrNotFound", err) + } + + ready, err := s.UpdateSharedVolume(ctx, volumeRef, store.PreconditionFrom(volume), finalizeSharedVolume) + if err != nil { + t.Fatalf("finalizing external volume failed: %v", err) + } + if got, want := ready.GetVolumeId(), "substrate-"+volume.GetMetadata().GetUid(); got != want { + t.Errorf("finalized volume id = %q, want %q", got, want) + } + if ready.GetStatus().GetState() != ateapipb.SharedVolumeState_SHARED_VOLUME_STATE_READY { + t.Errorf("finalized volume state = %v, want READY", ready.GetStatus().GetState()) + } + + claimed, err := s.UpdateSharedVolume(ctx, volumeRef, store.PreconditionFrom(ready), func(toUpdate *ateapipb.SharedVolume) error { + toUpdate.DeleteTrigger = ateapipb.DeleteTrigger_DELETE_TRIGGER_MANUAL + toUpdate.Status.Refs = append(toUpdate.Status.Refs, &ateapipb.ActorRef{ActorUid: "actor-uid-1", ActorName: "producer", Active: true}) + return nil + }) + if err != nil { + t.Fatalf("UpdateSharedVolume failed: %v", err) + } + if claimed.GetDeleteTrigger() != ateapipb.DeleteTrigger_DELETE_TRIGGER_MANUAL || len(claimed.GetStatus().GetRefs()) != 1 { + t.Errorf("updated volume = %v, want MANUAL and one ref", claimed) + } + if claimed.GetMetadata().GetVersion() != ready.GetMetadata().GetVersion()+1 { + t.Errorf("updated volume version = %d, want %d", claimed.GetMetadata().GetVersion(), ready.GetMetadata().GetVersion()+1) + } + if _, err := s.UpdateSharedVolume(ctx, volumeRef, store.PreconditionFrom(ready), func(toUpdate *ateapipb.SharedVolume) error { + return nil + }); !errors.Is(err, store.ErrVersionConflict) { + t.Errorf("stale UpdateSharedVolume = %v, want ErrVersionConflict", err) + } + if _, err := s.DeleteAtespace(ctx, "team-a", store.DeletePreconditions{}); !errors.Is(err, store.ErrFailedPrecondition) { + t.Errorf("DeleteAtespace with an external volume = %v, want ErrFailedPrecondition", err) + } + + if _, err := s.DeleteSharedVolume(ctx, volumeRef, store.DeletePreconditions{Version: ready.GetMetadata().GetVersion()}); !errors.Is(err, store.ErrVersionConflict) { + t.Errorf("stale DeleteSharedVolume = %v, want ErrVersionConflict", err) + } + deleted, err := s.DeleteSharedVolume(ctx, volumeRef, store.DeletePreconditions{UID: claimed.GetMetadata().GetUid()}) + if err != nil || !proto.Equal(deleted, claimed) { + t.Errorf("DeleteSharedVolume = (%v, %v), want the claimed volume", deleted, err) + } + if _, err := s.GetSharedVolume(ctx, volumeRef); !errors.Is(err, store.ErrNotFound) { + t.Errorf("deleted GetSharedVolume = %v, want ErrNotFound", err) + } + }) + + t.Run("SharedVolume_ImmutableFields", func(t *testing.T) { + s := setup(t) + ctx := context.Background() + mustCreateAtespace(t, s, "team-a") + volume := storeSharedVolume(t, s, newTestPendingSharedVolume("team-a", "scratch")) + + tests := []struct { + name string + mutate func(*ateapipb.SharedVolume) + }{ + { + name: "access mode", + mutate: func(toUpdate *ateapipb.SharedVolume) { + toUpdate.AccessMode = ateapipb.AccessMode_ACCESS_MODE_READ_WRITE_MANY + }, + }, + { + name: "storage class name", + mutate: func(toUpdate *ateapipb.SharedVolume) { toUpdate.StorageClassName = "premium-rwx" }, + }, + { + name: "clearing the storage class name", + mutate: func(toUpdate *ateapipb.SharedVolume) { toUpdate.StorageClassName = "" }, + }, + { + name: "volume id", + mutate: func(toUpdate *ateapipb.SharedVolume) { toUpdate.VolumeId = "substrate-somewhere-else" }, + }, + { + name: "clearing the volume id", + mutate: func(toUpdate *ateapipb.SharedVolume) { toUpdate.VolumeId = "" }, + }, + { + name: "volume type", + mutate: func(toUpdate *ateapipb.SharedVolume) { toUpdate.VolumeType = "pd.csi.storage.gke.io" }, + }, + { + name: "volume context", + mutate: func(toUpdate *ateapipb.SharedVolume) { toUpdate.VolumeContext = map[string]string{"ip": "10.0.0.3"} }, + }, + { + name: "clearing the volume context", + mutate: func(toUpdate *ateapipb.SharedVolume) { toUpdate.VolumeContext = nil }, + }, + } + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + _, err := s.UpdateSharedVolume(ctx, resources.SharedVolumeRef{Atespace: "team-a", Name: "scratch"}, store.PreconditionFrom(volume), func(toUpdate *ateapipb.SharedVolume) error { + tt.mutate(toUpdate) + return nil + }) + if !errors.Is(err, store.ErrImmutableField) { + t.Errorf("UpdateSharedVolume error = %v, want one matching store.ErrImmutableField", err) + } + }) + } + }) + + t.Run("CreateSharedVolume_ReusedName", func(t *testing.T) { + s := setup(t) + ctx := context.Background() + mustCreateAtespace(t, s, "team-a") + input := newTestPendingSharedVolume("team-a", "scratch") + if _, err := s.CreateSharedVolume(ctx, input); err != nil { + t.Fatalf("CreateSharedVolume failed: %v", err) + } + if _, err := s.CreateSharedVolume(ctx, input); !errors.Is(err, store.ErrAlreadyExists) { + t.Errorf("duplicate CreateSharedVolume = %v, want ErrAlreadyExists", err) + } + + stored, err := s.GetSharedVolume(ctx, resources.SharedVolumeRef{Atespace: "team-a", Name: "scratch"}) + if err != nil { + t.Fatalf("GetSharedVolume failed: %v", err) + } + if stored.GetMetadata().GetVersion() != 1 { + t.Errorf("volume version after rejected create = %d, want 1", stored.GetMetadata().GetVersion()) + } + }) + + t.Run("CreateSharedVolume_UnknownAtespace", func(t *testing.T) { + s := setup(t) + ctx := context.Background() + + if _, err := s.CreateSharedVolume(ctx, newTestPendingSharedVolume("team-missing", "scratch")); !errors.Is(err, store.ErrFailedPrecondition) { + t.Errorf("CreateSharedVolume in an unknown atespace = %v, want ErrFailedPrecondition", err) + } + }) + + t.Run("UpdateSharedVolume_MissingPrecondition", func(t *testing.T) { + s := setup(t) + ctx := context.Background() + mustCreateAtespace(t, s, "team-a") + created := storeSharedVolume(t, s, newTestPendingSharedVolume("team-a", "scratch")) + + tests := []struct { + name string + precondition store.Precondition + }{ + { + name: "no precondition", + precondition: store.Precondition{}, + }, + { + name: "guarding on only a uid", + precondition: store.Precondition{UID: created.GetMetadata().GetUid()}, + }, + { + name: "guarding on only a version", + precondition: store.Precondition{Version: created.GetMetadata().GetVersion()}, + }, + } + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + _, err := s.UpdateSharedVolume(ctx, resources.SharedVolumeRef{Atespace: "team-a", Name: "scratch"}, tt.precondition, func(toUpdate *ateapipb.SharedVolume) error { + t.Fatal("mutate ran for a blind write") + return nil + }) + if !errors.Is(err, store.ErrPreconditionRequired) { + t.Errorf("UpdateSharedVolume error = %v, want one matching store.ErrPreconditionRequired", err) + } + }) + } + }) + + t.Run("UpdateSharedVolume_NotFound", func(t *testing.T) { + s := setup(t) + ctx := context.Background() + mustCreateAtespace(t, s, "team-a") + + _, err := s.UpdateSharedVolume(ctx, resources.SharedVolumeRef{Atespace: "team-a", Name: "missing"}, store.Precondition{UID: "u", Version: 1}, func(*ateapipb.SharedVolume) error { return nil }) + if !errors.Is(err, store.ErrNotFound) { + t.Errorf("UpdateSharedVolume on a missing volume = %v, want ErrNotFound", err) + } + if _, err := s.DeleteSharedVolume(ctx, resources.SharedVolumeRef{Atespace: "team-a", Name: "missing"}, store.DeletePreconditions{}); !errors.Is(err, store.ErrNotFound) { + t.Errorf("DeleteSharedVolume on a missing volume = %v, want ErrNotFound", err) + } + }) + + t.Run("ListSharedVolumes_PaginationAndScope", func(t *testing.T) { + s := setup(t) + ctx := context.Background() + for _, atespace := range []string{"team-a", "team-b"} { + mustCreateAtespace(t, s, atespace) + for i := 0; i < 3; i++ { + storeSharedVolume(t, s, newTestPendingSharedVolume(atespace, fmt.Sprintf("volume-%d", i))) + } + } + + var scoped []*ateapipb.SharedVolume + for token := ""; ; { + page, err := s.ListSharedVolumes(ctx, "team-a", store.ListOptions{PageSize: 2, PageToken: token}) + if err != nil { + t.Fatalf("scoped ListSharedVolumes failed: %v", err) + } + scoped = append(scoped, page.Items...) + if page.NextPageToken == "" { + break + } + token = page.NextPageToken + } + if len(scoped) != 3 { + t.Errorf("scoped ListSharedVolumes returned %d volumes, want 3", len(scoped)) + } + + var global []*ateapipb.SharedVolume + for token := ""; ; { + page, err := s.ListSharedVolumes(ctx, "", store.ListOptions{PageSize: 2, PageToken: token}) + if err != nil { + t.Fatalf("global ListSharedVolumes failed: %v", err) + } + global = append(global, page.Items...) + if page.NextPageToken == "" { + break + } + token = page.NextPageToken + } + if len(global) != 6 { + t.Errorf("global ListSharedVolumes returned %d volumes, want 6", len(global)) + } + }) +} + func runWorkerContractTests(t *testing.T, setup func(t *testing.T) store.Interface) { t.Helper() diff --git a/cmd/ateapi/internal/store/storetest/storetest.go b/cmd/ateapi/internal/store/storetest/storetest.go index 3385bb4112..65917074a8 100644 --- a/cmd/ateapi/internal/store/storetest/storetest.go +++ b/cmd/ateapi/internal/store/storetest/storetest.go @@ -148,6 +148,19 @@ func MustCreateTag(t *testing.T, ctx context.Context, s store.Interface, tag *at return created } +// MustCreateSharedVolume ensures volume's parent atespace exists, then stores volume as given. +func MustCreateSharedVolume(t *testing.T, ctx context.Context, s store.Interface, volume *ateapipb.SharedVolume) *ateapipb.SharedVolume { + t.Helper() + atespace := volume.GetMetadata().GetAtespace() + name := volume.GetMetadata().GetName() + MustCreateAtespace(t, ctx, s, atespace) + created, err := s.CreateSharedVolume(ctx, volume) + if err != nil { + t.Fatalf("creating test external volume %q/%q: %v", atespace, name, err) + } + return created +} + // RunTests runs m and terminates the shared PostgreSQL container afterwards. // Packages with no other TestMain work should use it as their whole TestMain; // the rest must call [Shutdown] themselves. diff --git a/cmd/atelet/internal/apivalidation/zz_generated.validation.go b/cmd/atelet/internal/apivalidation/zz_generated.validation.go index 10cc29c62e..327a4850b6 100644 --- a/cmd/atelet/internal/apivalidation/zz_generated.validation.go +++ b/cmd/atelet/internal/apivalidation/zz_generated.validation.go @@ -4225,7 +4225,7 @@ func Validate_VersionedSandboxCompat( return errs } -var unionMembershipFor_go_ate_dev_substrate_internal_proto_ateletpb_Volume_ = validate.NewUnionMembership(validate.NewUnionMember("durable_dir"), validate.NewUnionMember("external"), validate.NewUnionMember("system_info"), validate.NewUnionMember("image")) +var unionMembershipFor_go_ate_dev_substrate_internal_proto_ateletpb_Volume_ = validate.NewUnionMembership(validate.NewUnionMember("durable_dir"), validate.NewUnionMember("external"), validate.NewUnionMember("system_info"), validate.NewUnionMember("image"), validate.NewUnionMember("empty_dir")) // Validate_Volume validates an instance of Volume according // to declarative validation rules in the API schema. @@ -4257,6 +4257,12 @@ func Validate_Volume( return false } return obj.Image != nil + }, + func(obj *ateletpb.Volume) bool { + if obj == nil { + return false + } + return obj.EmptyDir != nil }); len(e) != 0 { errs = append(errs, e...) } @@ -4411,6 +4417,34 @@ func Validate_Volume( errs = append(errs, fn(fldPath.Child("image"), obj.Image, oldVal, oldObj != nil)...) } + { // field ateletpb.Volume.EmptyDir + fn := func( + fldPath *field.Path, + obj, oldObj *ateletpb.EmptyDirVolumeSource, + 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.OptionalPointer(ctx, op, fldPath, obj, oldObj).MarkShortCircuit(); len(e) != 0 { + earlyReturn = true + } + if earlyReturn { + return // do not proceed + } + return + } + oldVal := safe.Field(oldObj, + func(oldObj *ateletpb.Volume) *ateletpb.EmptyDirVolumeSource { + return oldObj.EmptyDir + }) + errs = append(errs, fn(fldPath.Child("empty_dir"), obj.EmptyDir, oldVal, oldObj != nil)...) + } + return errs } diff --git a/cmd/atelet/main.go b/cmd/atelet/main.go index b93b09e6d5..5c2b24f456 100644 --- a/cmd/atelet/main.go +++ b/cmd/atelet/main.go @@ -1771,7 +1771,7 @@ func buildWorkerWorkloadSpec(spec *ateletpb.WorkloadSpec, containerSpecs []*atew Name: volName, MountPath: vm.GetMountPath(), }) - case vol.GetExternal() != nil: + case vol.GetExternal() != nil, vol.GetEmptyDir() != nil: extMounts = append(extMounts, &ateworkerpb.VolumeMount{ Name: volName, MountPath: vm.GetMountPath(), diff --git a/cmd/atelet/main_test.go b/cmd/atelet/main_test.go index d040c0bf2d..21f681110a 100644 --- a/cmd/atelet/main_test.go +++ b/cmd/atelet/main_test.go @@ -799,6 +799,7 @@ func TestBuildWorkerWorkloadSpecForwardsDurableDirMounts(t *testing.T) { {Name: "data", DurableDir: &ateletpb.DurableDirVolumeSource{}}, {Name: "cache", DurableDir: &ateletpb.DurableDirVolumeSource{}}, {Name: "scratch", External: &ateletpb.ExternalVolumeSource{}}, + {Name: "empty", EmptyDir: &ateletpb.EmptyDirVolumeSource{}}, {Name: "system-info", SystemInfo: &ateletpb.SystemInfoVolumeSource{}}, }, Containers: []*ateletpb.Container{ @@ -808,6 +809,7 @@ func TestBuildWorkerWorkloadSpecForwardsDurableDirMounts(t *testing.T) { {Name: "data", MountPath: "/home/counter"}, {Name: "cache", MountPath: "/var/cache"}, {Name: "scratch", MountPath: "/scratch"}, + {Name: "empty", MountPath: "/empty"}, {Name: "system-info", MountPath: "/run/ate"}, }, }, @@ -832,6 +834,7 @@ func TestBuildWorkerWorkloadSpecForwardsDurableDirMounts(t *testing.T) { }, ExternalVolumeMounts: []*ateworkerpb.VolumeMount{ {Name: "scratch", MountPath: "/scratch"}, + {Name: "empty", MountPath: "/empty"}, }, SystemInfoVolumeMounts: []*ateworkerpb.VolumeMount{ {Name: "system-info", MountPath: "/run/ate"}, diff --git a/cmd/atelet/volumes.go b/cmd/atelet/volumes.go index d1d04bc4bb..4f121f560b 100644 --- a/cmd/atelet/volumes.go +++ b/cmd/atelet/volumes.go @@ -36,11 +36,17 @@ func (s *Atelet) mountExternalVolumes(ctx context.Context, actorUID string, volu // disabling the gate while in use would be an orphaned volume // attachment, we are NOT checking the ExternalVolumes gate in the // mount/unmount logic. + hostPath := ateletpath.ExternalVolumeHostPath(actorUID, vol.GetName()) + if vol.GetEmptyDir() != nil { + if err := resetEmptyDir(hostPath); err != nil { + return err + } + continue + } ext := vol.GetExternal() if ext == nil { continue } - hostPath := ateletpath.ExternalVolumeHostPath(actorUID, vol.GetName()) if err := os.MkdirAll(hostPath, 0o750); err != nil { return fmt.Errorf("failed to create mount point %q: %w", hostPath, err) } @@ -61,6 +67,16 @@ func (s *Atelet) mountExternalVolumes(ctx context.Context, actorUID string, volu return nil } +func resetEmptyDir(hostPath string) error { + if err := os.RemoveAll(hostPath); err != nil { + return fmt.Errorf("failed to clear empty dir %q: %w", hostPath, err) + } + if err := os.MkdirAll(hostPath, 0o750); err != nil { + return fmt.Errorf("failed to create empty dir %q: %w", hostPath, err) + } + return nil +} + func (s *Atelet) unmountExternalVolumes(ctx context.Context, actorUID string, volumes []*ateletpb.Volume) error { var errs []error for _, vol := range volumes { diff --git a/cmd/atelet/volumes_test.go b/cmd/atelet/volumes_test.go index 4679f9d716..72fc98015d 100644 --- a/cmd/atelet/volumes_test.go +++ b/cmd/atelet/volumes_test.go @@ -552,3 +552,23 @@ func TestDirectCSIDriverConfigGetter(t *testing.T) { t.Fatalf("expected error for missing driver, got nil") } } + +func TestResetEmptyDirClearsPreviousContents(t *testing.T) { + dir := filepath.Join(t.TempDir(), "volumes", "shared") + if err := os.MkdirAll(dir, 0o750); err != nil { + t.Fatal(err) + } + if err := os.WriteFile(filepath.Join(dir, "stale"), []byte("x"), 0o600); err != nil { + t.Fatal(err) + } + if err := resetEmptyDir(dir); err != nil { + t.Fatalf("resetEmptyDir: %v", err) + } + entries, err := os.ReadDir(dir) + if err != nil { + t.Fatalf("ReadDir: %v", err) + } + if len(entries) != 0 { + t.Fatalf("dir still has %d entries after reset", len(entries)) + } +} diff --git a/cmd/kubectl-ate/internal/cmd/actor.go b/cmd/kubectl-ate/internal/cmd/actor.go index be7b57c711..afe32b2c1b 100644 --- a/cmd/kubectl-ate/internal/cmd/actor.go +++ b/cmd/kubectl-ate/internal/cmd/actor.go @@ -47,6 +47,7 @@ var ( createActorAtespaceFlag string createActorTemplateFlag string createActorTagFlag string + createActorVolumeFlag map[string]string deleteActorAtespaceFlag string deleteActorAnyStateFlag bool pauseActorAtespaceFlag string @@ -118,7 +119,7 @@ var createActorCmd = &cobra.Command{ Short: "Create an actor", Args: exactlyOneArg("", "atespace", "template"), RunE: func(cmd *cobra.Command, args []string) error { - request, err := buildCreateActorRequest(args[0], createActorAtespaceFlag, createActorTemplateFlag, createActorTagFlag) + request, err := buildCreateActorRequest(args[0], createActorAtespaceFlag, createActorTemplateFlag, createActorTagFlag, createActorVolumeFlag) if err != nil { return wrapUsageError(err) } @@ -244,7 +245,7 @@ var logsActorsCmd = &cobra.Command{ RunE: runLogsActor, } -func buildCreateActorRequest(actorName, atespace, template, tag string) (*ateapipb.CreateActorRequest, error) { +func buildCreateActorRequest(actorName, atespace, template, tag string, volumes map[string]string) (*ateapipb.CreateActorRequest, error) { templateRef, err := parseAtespacedName(template, atespace) if err != nil { return nil, err @@ -256,6 +257,9 @@ func buildCreateActorRequest(actorName, atespace, template, tag string) (*ateapi }, ActorTemplate: templateRef, } + if len(volumes) > 0 { + actor.SharedVolumeBindings = volumes + } if tag != "" { ref, err := parseAtespacedName(tag, atespace) @@ -740,6 +744,7 @@ func init() { createActorCmd.Flags().StringVar(&createActorTemplateFlag, "template", "", "actor-template to derive the actor from, as /, or just to use the actor's own atespace (see --atespace)") createActorCmd.Flags().StringVarP(&createActorAtespaceFlag, "atespace", "a", "", "atespace to create the actor in") createActorCmd.Flags().StringVar(&createActorTagFlag, "tag", "", "tag to initialize the actor from, as /, or just to use the actor's own atespace (see --atespace)") + createActorCmd.Flags().StringToStringVar(&createActorVolumeFlag, "volume", nil, "bind an unnamed external.sharedVolume in the template to a SharedVolume, as =; repeatable") createCmd.AddCommand(createActorCmd) deleteActorCmd.Flags().StringVarP(&deleteActorAtespaceFlag, "atespace", "a", "", "the actor's atespace") diff --git a/cmd/kubectl-ate/internal/cmd/actor_test.go b/cmd/kubectl-ate/internal/cmd/actor_test.go index 634369c2b8..27ed3a4d59 100644 --- a/cmd/kubectl-ate/internal/cmd/actor_test.go +++ b/cmd/kubectl-ate/internal/cmd/actor_test.go @@ -48,6 +48,7 @@ func TestBuildCreateActorRequest(t *testing.T) { name string templateRef string tag string + volumes map[string]string want *ateapipb.Actor wantErr bool }{ @@ -87,13 +88,23 @@ func TestBuildCreateActorRequest(t *testing.T) { ActorTemplate: &ateapipb.ObjectRef{Atespace: "shared-templates", Name: "counter"}, }, }, + { + name: "volume bindings", + templateRef: "counter", + volumes: map[string]string{"shared": "ctx-42"}, + want: &ateapipb.Actor{ + Metadata: &ateapipb.ResourceMetadata{Atespace: "demo", Name: "my-counter"}, + ActorTemplate: &ateapipb.ObjectRef{Atespace: "demo", Name: "counter"}, + SharedVolumeBindings: map[string]string{"shared": "ctx-42"}, + }, + }, {name: "malformed template ref", templateRef: "a/b/c", wantErr: true}, {name: "malformed tag", templateRef: "counter", tag: "a/b/c", wantErr: true}, } for _, test := range tests { t.Run(test.name, func(t *testing.T) { - got, err := buildCreateActorRequest("my-counter", "demo", test.templateRef, test.tag) + got, err := buildCreateActorRequest("my-counter", "demo", test.templateRef, test.tag, test.volumes) if (err != nil) != test.wantErr { t.Fatalf("buildCreateActorRequest error = %v, wantErr %t", err, test.wantErr) } diff --git a/cmd/kubectl-ate/internal/cmd/shared_volume.go b/cmd/kubectl-ate/internal/cmd/shared_volume.go new file mode 100644 index 0000000000..f15032cf04 --- /dev/null +++ b/cmd/kubectl-ate/internal/cmd/shared_volume.go @@ -0,0 +1,268 @@ +// 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. + +package cmd + +import ( + "context" + "fmt" + "strings" + + "github.com/spf13/cobra" + "go.ate.dev/substrate/cmd/kubectl-ate/internal/printer" + "go.ate.dev/substrate/internal/ateclient" + "go.ate.dev/substrate/pkg/proto/ateapipb" + "google.golang.org/grpc" +) + +var ( + sharedVolumeAtespaceFlag string + sharedVolumeAllAtespacesFlag bool + createSharedVolumeAtespaceFlag string + createSharedVolumeClassFlag string + createSharedVolumeCapacityFlag string + createSharedVolumeVolumeIDFlag string + createSharedVolumeTypeFlag string + createSharedVolumeContextFlag map[string]string + createSharedVolumeDeleteTriggerFlag string + createSharedVolumeAccessModeFlag string + updateSharedVolumeAtespaceFlag string + updateSharedVolumeDeleteTriggerFlag string + deleteSharedVolumeAtespaceFlag string +) + +var getSharedVolumesCmd = &cobra.Command{ + Use: "sharedvolumes [volume-name ...]", + Aliases: []string{"sharedvolume", "sv"}, + Short: "List or get shared volumes", + RunE: func(cmd *cobra.Command, args []string) error { + if sharedVolumeAllAtespacesFlag && sharedVolumeAtespaceFlag != "" { + return fmt.Errorf("--atespace and -A/--all-atespaces are mutually exclusive") + } + if len(args) > 0 && sharedVolumeAtespaceFlag == "" { + return fmt.Errorf("--atespace is required when getting shared volumes") + } + if len(args) == 0 && !sharedVolumeAllAtespacesFlag && sharedVolumeAtespaceFlag == "" { + return fmt.Errorf("specify --atespace , or -A/--all-atespaces") + } + + ctx := cmd.Context() + client, err := ateclient.NewClient(ctx, kubeconfig, k8sContext, endpoint, tokenFile, traceEnabled) + if err != nil { + return fmt.Errorf("failed to connect to ate-api-server: %w", err) + } + defer client.Close() + + if len(args) > 0 { + volumes := make([]*ateapipb.SharedVolume, 0, len(args)) + for _, name := range args { + volume, err := client.GetSharedVolume(ctx, &ateapipb.GetSharedVolumeRequest{ + SharedVolume: &ateapipb.ObjectRef{Atespace: sharedVolumeAtespaceFlag, Name: name}, + }) + if err != nil { + return fmt.Errorf("failed to get shared volume %q: %w", name, err) + } + volumes = append(volumes, volume) + } + if len(volumes) == 1 { + return printer.PrintSharedVolumeTo(cmd.OutOrStdout(), volumes[0], outputFmt) + } + return printer.PrintSharedVolumesTo(cmd.OutOrStdout(), volumes, outputFmt) + } + + var volumes []*ateapipb.SharedVolume + pageToken := "" + for { + resp, err := client.ListSharedVolumes(ctx, &ateapipb.ListSharedVolumesRequest{ + Atespace: sharedVolumeAtespaceFlag, Options: &ateapipb.ListOptions{PageSize: 1000, PageToken: pageToken}, + }) + if err != nil { + return fmt.Errorf("failed to list shared volumes: %w", err) + } + volumes = append(volumes, resp.GetSharedVolumes()...) + pageToken = resp.GetNextPageToken() + if pageToken == "" { + break + } + } + return printer.PrintSharedVolumesTo(cmd.OutOrStdout(), volumes, outputFmt) + }, +} + +var createSharedVolumeCmd = &cobra.Command{ + Use: "sharedvolume ", + Aliases: []string{"sharedvolumes", "sv"}, + Short: "Create an shared volume actors can share", + Long: "Create an shared volume actors can share.\n\n" + + "Name a storage class with --storage-class to provision a new volume, or\n" + + "supply --volume-id to register a volume that already exists, but not both.", + Args: cobra.ExactArgs(1), + RunE: func(cmd *cobra.Command, args []string) error { + trigger, err := parseDeleteTrigger(createSharedVolumeDeleteTriggerFlag) + if err != nil { + return err + } + mode, err := parseAccessMode(createSharedVolumeAccessModeFlag) + if err != nil { + return err + } + ctx := cmd.Context() + client, err := ateclient.NewClient(ctx, kubeconfig, k8sContext, endpoint, tokenFile, traceEnabled) + if err != nil { + return fmt.Errorf("failed to connect to ate-api-server: %w", err) + } + defer client.Close() + + volume, err := client.CreateSharedVolume(ctx, &ateapipb.CreateSharedVolumeRequest{ + SharedVolume: &ateapipb.SharedVolume{ + Metadata: &ateapipb.ResourceMetadata{Atespace: createSharedVolumeAtespaceFlag, Name: args[0]}, + DeleteTrigger: trigger, + AccessMode: mode, + StorageClassName: createSharedVolumeClassFlag, + Capacity: createSharedVolumeCapacityFlag, + VolumeId: createSharedVolumeVolumeIDFlag, + VolumeType: createSharedVolumeTypeFlag, + VolumeContext: createSharedVolumeContextFlag, + }, + }) + if err != nil { + return fmt.Errorf("failed to create shared volume: %w", err) + } + return printer.PrintSharedVolumeTo(cmd.OutOrStdout(), volume, outputFmt) + }, +} + +var updateSharedVolumeCmd = &cobra.Command{ + Use: "sharedvolume ", + Aliases: []string{"sharedvolumes", "sv"}, + Short: "Change an shared volume's delete trigger", + Args: cobra.ExactArgs(1), + RunE: func(cmd *cobra.Command, args []string) error { + trigger, err := parseDeleteTrigger(updateSharedVolumeDeleteTriggerFlag) + if err != nil { + return err + } + ctx := cmd.Context() + client, err := ateclient.NewClient(ctx, kubeconfig, k8sContext, endpoint, tokenFile, traceEnabled) + if err != nil { + return fmt.Errorf("failed to connect to ate-api-server: %w", err) + } + defer client.Close() + + ref := &ateapipb.ObjectRef{Atespace: updateSharedVolumeAtespaceFlag, Name: args[0]} + resp, err := updateSharedVolumeDeleteTrigger(ctx, client, ref, trigger) + if err != nil { + return err + } + return printer.PrintSharedVolumeTo(cmd.OutOrStdout(), resp, outputFmt) + }, +} + +var deleteSharedVolumeCmd = &cobra.Command{ + Use: "sharedvolume ", + Aliases: []string{"sharedvolumes", "sv"}, + Short: "Delete an shared volume and its storage", + Args: cobra.ExactArgs(1), + RunE: func(cmd *cobra.Command, args []string) error { + ctx := cmd.Context() + client, err := ateclient.NewClient(ctx, kubeconfig, k8sContext, endpoint, tokenFile, traceEnabled) + if err != nil { + return fmt.Errorf("failed to connect to ate-api-server: %w", err) + } + defer client.Close() + + _, err = client.DeleteSharedVolume(ctx, &ateapipb.DeleteSharedVolumeRequest{ + SharedVolume: &ateapipb.ObjectRef{Atespace: deleteSharedVolumeAtespaceFlag, Name: args[0]}, + }) + if err != nil { + return fmt.Errorf("failed to delete shared volume: %w", err) + } + fmt.Fprintf(cmd.OutOrStdout(), "shared volume %q deleted\n", args[0]) + return nil + }, +} + +type sharedVolumeClient interface { + GetSharedVolume(ctx context.Context, in *ateapipb.GetSharedVolumeRequest, opts ...grpc.CallOption) (*ateapipb.SharedVolume, error) + UpdateSharedVolume(ctx context.Context, in *ateapipb.UpdateSharedVolumeRequest, opts ...grpc.CallOption) (*ateapipb.SharedVolume, error) +} + +// updateSharedVolumeDeleteTrigger reads the volume first so the update carries +// the uid and version the server requires as preconditions. +func updateSharedVolumeDeleteTrigger(ctx context.Context, client sharedVolumeClient, ref *ateapipb.ObjectRef, trigger ateapipb.DeleteTrigger) (*ateapipb.SharedVolume, error) { + volume, err := client.GetSharedVolume(ctx, &ateapipb.GetSharedVolumeRequest{SharedVolume: ref}) + if err != nil { + return nil, fmt.Errorf("failed to get shared volume %q: %w", ref.GetName(), err) + } + volume.DeleteTrigger = trigger + + resp, err := client.UpdateSharedVolume(ctx, &ateapipb.UpdateSharedVolumeRequest{SharedVolume: volume}) + if err != nil { + return nil, fmt.Errorf("failed to update shared volume: %w", err) + } + return resp, nil +} + +func parseDeleteTrigger(value string) (ateapipb.DeleteTrigger, error) { + switch strings.ToLower(value) { + case "": + return ateapipb.DeleteTrigger_DELETE_TRIGGER_UNSPECIFIED, nil + case "manual": + return ateapipb.DeleteTrigger_DELETE_TRIGGER_MANUAL, nil + case "last-actor", "lastactor": + return ateapipb.DeleteTrigger_DELETE_TRIGGER_LAST_ACTOR, nil + default: + return ateapipb.DeleteTrigger_DELETE_TRIGGER_UNSPECIFIED, fmt.Errorf("invalid delete trigger %q; must be manual or last-actor", value) + } +} + +func parseAccessMode(value string) (ateapipb.AccessMode, error) { + switch strings.ToLower(value) { + case "": + return ateapipb.AccessMode_ACCESS_MODE_UNSPECIFIED, nil + case "rwo", "readwriteonce": + return ateapipb.AccessMode_ACCESS_MODE_READ_WRITE_ONCE, nil + case "rwx", "readwritemany": + return ateapipb.AccessMode_ACCESS_MODE_READ_WRITE_MANY, nil + default: + return ateapipb.AccessMode_ACCESS_MODE_UNSPECIFIED, fmt.Errorf("invalid access mode %q; must be rwo or rwx", value) + } +} + +func init() { + getSharedVolumesCmd.Flags().StringVarP(&sharedVolumeAtespaceFlag, "atespace", "a", "", "Atespace to list/get shared volumes in") + getSharedVolumesCmd.Flags().BoolVarP(&sharedVolumeAllAtespacesFlag, "all-atespaces", "A", false, "List shared volumes across all atespaces") + getCmd.AddCommand(getSharedVolumesCmd) + + createSharedVolumeCmd.Flags().StringVarP(&createSharedVolumeAtespaceFlag, "atespace", "a", "", "Atespace to create the shared volume in (required)") + _ = createSharedVolumeCmd.MarkFlagRequired("atespace") + createSharedVolumeCmd.Flags().StringVar(&createSharedVolumeClassFlag, "storage-class", "", "StorageClass to provision the volume from") + createSharedVolumeCmd.Flags().StringVar(&createSharedVolumeCapacityFlag, "capacity", "", "Size to provision, as a Kubernetes quantity (e.g. 10Gi); required with --storage-class") + createSharedVolumeCmd.Flags().StringVar(&createSharedVolumeVolumeIDFlag, "volume-id", "", "Volume ID of a volume that already exists, to register instead of provisioning") + createSharedVolumeCmd.Flags().StringVar(&createSharedVolumeTypeFlag, "volume-type", "", "CSI driver name; required with --volume-id unless --storage-class is set") + createSharedVolumeCmd.Flags().StringToStringVar(&createSharedVolumeContextFlag, "volume-context", nil, "Driver metadata for mounting, as key=value pairs; only valid with --volume-id") + createSharedVolumeCmd.Flags().StringVar(&createSharedVolumeDeleteTriggerFlag, "delete-trigger", "", "What removes the volume: manual or last-actor (default: last-actor when provisioned, manual when registered)") + createSharedVolumeCmd.Flags().StringVar(&createSharedVolumeAccessModeFlag, "access-mode", "", "How many resumed actors may hold the volume: rwo (default) or rwx") + createCmd.AddCommand(createSharedVolumeCmd) + + updateSharedVolumeCmd.Flags().StringVarP(&updateSharedVolumeAtespaceFlag, "atespace", "a", "", "Atespace owning the shared volume (required)") + updateSharedVolumeCmd.Flags().StringVar(&updateSharedVolumeDeleteTriggerFlag, "delete-trigger", "", "What removes the volume: manual or last-actor (required)") + _ = updateSharedVolumeCmd.MarkFlagRequired("atespace") + _ = updateSharedVolumeCmd.MarkFlagRequired("delete-trigger") + updateCmd.AddCommand(updateSharedVolumeCmd) + + deleteSharedVolumeCmd.Flags().StringVarP(&deleteSharedVolumeAtespaceFlag, "atespace", "a", "", "Atespace owning the shared volume (required)") + _ = deleteSharedVolumeCmd.MarkFlagRequired("atespace") + deleteCmd.AddCommand(deleteSharedVolumeCmd) +} diff --git a/cmd/kubectl-ate/internal/cmd/shared_volume_test.go b/cmd/kubectl-ate/internal/cmd/shared_volume_test.go new file mode 100644 index 0000000000..7b752f8dfc --- /dev/null +++ b/cmd/kubectl-ate/internal/cmd/shared_volume_test.go @@ -0,0 +1,138 @@ +// 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. + +package cmd + +import ( + "context" + "testing" + + "github.com/google/go-cmp/cmp" + "go.ate.dev/substrate/pkg/proto/ateapipb" + "google.golang.org/grpc" + "google.golang.org/protobuf/proto" + "google.golang.org/protobuf/testing/protocmp" +) + +type fakeSharedVolumeClient struct { + volume *ateapipb.SharedVolume + + getCalls int + updateReq *ateapipb.UpdateSharedVolumeRequest +} + +func (f *fakeSharedVolumeClient) GetSharedVolume(_ context.Context, _ *ateapipb.GetSharedVolumeRequest, _ ...grpc.CallOption) (*ateapipb.SharedVolume, error) { + f.getCalls++ + return proto.Clone(f.volume).(*ateapipb.SharedVolume), nil +} + +func (f *fakeSharedVolumeClient) UpdateSharedVolume(_ context.Context, in *ateapipb.UpdateSharedVolumeRequest, _ ...grpc.CallOption) (*ateapipb.SharedVolume, error) { + f.updateReq = in + updated := proto.Clone(in.GetSharedVolume()).(*ateapipb.SharedVolume) + updated.Metadata.Version++ + return updated, nil +} + +func testSharedVolume() *ateapipb.SharedVolume { + return &ateapipb.SharedVolume{ + Metadata: &ateapipb.ResourceMetadata{ + Atespace: "space-1", + Name: "vol-1", + Uid: "8ba9b6ee-2e1a-4c0f-9f4e-2f0a0f1c3d55", + Version: 1, + }, + DeleteTrigger: ateapipb.DeleteTrigger_DELETE_TRIGGER_LAST_ACTOR, + AccessMode: ateapipb.AccessMode_ACCESS_MODE_READ_WRITE_ONCE, + VolumeId: "projects/p/zones/z/disks/substrate-8ba9b6ee", + VolumeType: "pd.csi.storage.gke.io", + Status: &ateapipb.SharedVolumeStatus{ + State: ateapipb.SharedVolumeState_SHARED_VOLUME_STATE_READY, + }, + } +} + +func TestUpdateSharedVolumeDeleteTrigger(t *testing.T) { + client := &fakeSharedVolumeClient{volume: testSharedVolume()} + ref := &ateapipb.ObjectRef{Atespace: "space-1", Name: "vol-1"} + want := ateapipb.DeleteTrigger_DELETE_TRIGGER_MANUAL + + resp, err := updateSharedVolumeDeleteTrigger(context.Background(), client, ref, want) + if err != nil { + t.Fatalf("updateSharedVolumeDeleteTrigger() error = %v", err) + } + + if client.getCalls != 1 { + t.Errorf("GetSharedVolume called %d times, want 1", client.getCalls) + } + + sent := client.updateReq.GetSharedVolume().GetMetadata() + if sent.GetUid() != "8ba9b6ee-2e1a-4c0f-9f4e-2f0a0f1c3d55" || sent.GetVersion() != 1 { + t.Errorf("update sent uid %q version %d, want the values just read", sent.GetUid(), sent.GetVersion()) + } + + wantResp := testSharedVolume() + wantResp.DeleteTrigger = want + wantResp.Metadata.Version = 2 + if diff := cmp.Diff(wantResp, resp, protocmp.Transform()); diff != "" { + t.Errorf("returned shared volume mismatch (-want +got):\n%s", diff) + } +} + +func TestParseDeleteTrigger(t *testing.T) { + tests := []struct { + value string + want ateapipb.DeleteTrigger + wantErr bool + }{ + {value: "", want: ateapipb.DeleteTrigger_DELETE_TRIGGER_UNSPECIFIED}, + {value: "manual", want: ateapipb.DeleteTrigger_DELETE_TRIGGER_MANUAL}, + {value: "Last-Actor", want: ateapipb.DeleteTrigger_DELETE_TRIGGER_LAST_ACTOR}, + {value: "atespace", wantErr: true}, + } + for _, tc := range tests { + t.Run(tc.value, func(t *testing.T) { + got, err := parseDeleteTrigger(tc.value) + if (err != nil) != tc.wantErr { + t.Fatalf("parseDeleteTrigger(%q) error = %v, wantErr %v", tc.value, err, tc.wantErr) + } + if got != tc.want { + t.Errorf("parseDeleteTrigger(%q) = %v, want %v", tc.value, got, tc.want) + } + }) + } +} + +func TestParseAccessMode(t *testing.T) { + tests := []struct { + value string + want ateapipb.AccessMode + wantErr bool + }{ + {value: "", want: ateapipb.AccessMode_ACCESS_MODE_UNSPECIFIED}, + {value: "rwo", want: ateapipb.AccessMode_ACCESS_MODE_READ_WRITE_ONCE}, + {value: "ReadWriteMany", want: ateapipb.AccessMode_ACCESS_MODE_READ_WRITE_MANY}, + {value: "rox", wantErr: true}, + } + for _, tc := range tests { + t.Run(tc.value, func(t *testing.T) { + got, err := parseAccessMode(tc.value) + if (err != nil) != tc.wantErr { + t.Fatalf("parseAccessMode(%q) error = %v, wantErr %v", tc.value, err, tc.wantErr) + } + if got != tc.want { + t.Errorf("parseAccessMode(%q) = %v, want %v", tc.value, got, tc.want) + } + }) + } +} diff --git a/cmd/kubectl-ate/internal/printer/printer.go b/cmd/kubectl-ate/internal/printer/printer.go index 7b7cf83412..6460d7256e 100644 --- a/cmd/kubectl-ate/internal/printer/printer.go +++ b/cmd/kubectl-ate/internal/printer/printer.go @@ -388,6 +388,85 @@ func PrintTagTo(out io.Writer, tag *ateapipb.Tag, format string) error { return PrintTagsTo(out, []*ateapipb.Tag{tag}, format) } +// PrintSharedVolumesTo prints external volumes to the provided writer. +func PrintSharedVolumesTo(out io.Writer, volumes []*ateapipb.SharedVolume, format string) error { + slices.SortFunc(volumes, func(a, b *ateapipb.SharedVolume) int { + if c := cmp.Compare(a.GetMetadata().GetAtespace(), b.GetMetadata().GetAtespace()); c != 0 { + return c + } + return cmp.Compare(a.GetMetadata().GetName(), b.GetMetadata().GetName()) + }) + switch format { + case "json", "yaml": + return printProto(out, &ateapipb.ListSharedVolumesResponse{SharedVolumes: volumes}, format) + case "table": + w := tabwriter.NewWriter(out, 0, 0, 3, ' ', 0) + fmt.Fprintln(w, "ATESPACE\tNAME\tSTATE\tVOLUME TYPE\tVOLUME ID\tACCESS MODE\tDELETE TRIGGER\tREFS\tAGE") + for _, volume := range volumes { + volumeType, volumeID := "", "" + if volume.GetVolumeId() != "" { + volumeType, volumeID = volume.GetVolumeType(), volume.GetVolumeId() + } + fmt.Fprintf(w, "%s\t%s\t%s\t%s\t%s\t%s\t%s\t%s\t%s\n", + volume.GetMetadata().GetAtespace(), volume.GetMetadata().GetName(), + sharedVolumeState(volume), volumeType, volumeID, + accessModeLabel(volume.GetAccessMode()), deleteTriggerLabel(volume.GetDeleteTrigger()), + sharedVolumeRefs(volume), formatAge(volume.GetMetadata().GetCreateTime())) + } + return w.Flush() + default: + return fmt.Errorf("unsupported format %q", format) + } +} + +func sharedVolumeState(volume *ateapipb.SharedVolume) string { + if volume.GetStatus().GetState() != ateapipb.SharedVolumeState_SHARED_VOLUME_STATE_READY { + return "Pending" + } + return "Ready" +} + +func accessModeLabel(mode ateapipb.AccessMode) string { + switch mode { + case ateapipb.AccessMode_ACCESS_MODE_READ_WRITE_ONCE: + return "RWO" + case ateapipb.AccessMode_ACCESS_MODE_READ_WRITE_MANY: + return "RWX" + default: + return "" + } +} + +func deleteTriggerLabel(trigger ateapipb.DeleteTrigger) string { + switch trigger { + case ateapipb.DeleteTrigger_DELETE_TRIGGER_MANUAL: + return "Manual" + case ateapipb.DeleteTrigger_DELETE_TRIGGER_LAST_ACTOR: + return "LastActor" + default: + return "" + } +} + +// sharedVolumeRefs renders the reference count as active/total. +func sharedVolumeRefs(volume *ateapipb.SharedVolume) string { + active := 0 + for _, ref := range volume.GetStatus().GetRefs() { + if ref.GetActive() { + active++ + } + } + return fmt.Sprintf("%d/%d", active, len(volume.GetStatus().GetRefs())) +} + +// PrintSharedVolumeTo prints a single external volume to the provided writer. +func PrintSharedVolumeTo(out io.Writer, volume *ateapipb.SharedVolume, format string) error { + if format == "json" || format == "yaml" { + return printProto(out, volume, format) + } + return PrintSharedVolumesTo(out, []*ateapipb.SharedVolume{volume}, format) +} + func sortAtespaces(atespaces []*ateapipb.Atespace) { slices.SortFunc(atespaces, func(a, b *ateapipb.Atespace) int { return cmp.Compare(a.GetMetadata().GetName(), b.GetMetadata().GetName()) diff --git a/cmd/kubectl-ate/internal/printer/printer_test.go b/cmd/kubectl-ate/internal/printer/printer_test.go index fb78ec1261..0aa4451a31 100644 --- a/cmd/kubectl-ate/internal/printer/printer_test.go +++ b/cmd/kubectl-ate/internal/printer/printer_test.go @@ -717,6 +717,116 @@ func TestPrintTagTo_YAML(t *testing.T) { } } +func TestPrintSharedVolumesTo_Table(t *testing.T) { + now := time.Date(2026, 1, 1, 12, 0, 0, 0, time.UTC) + pinNow(t, now) + + var buf bytes.Buffer + volumes := []*ateapipb.SharedVolume{ + { + Metadata: &ateapipb.ResourceMetadata{ + Atespace: "team-a", + Name: "shared", + CreateTime: timestamppb.New(now.Add(-5 * time.Minute)), + }, + DeleteTrigger: ateapipb.DeleteTrigger_DELETE_TRIGGER_LAST_ACTOR, + AccessMode: ateapipb.AccessMode_ACCESS_MODE_READ_WRITE_ONCE, + VolumeId: "projects/p/zones/z/disks/substrate-1", + VolumeType: "pd.csi.storage.gke.io", + Status: &ateapipb.SharedVolumeStatus{ + State: ateapipb.SharedVolumeState_SHARED_VOLUME_STATE_READY, + Refs: []*ateapipb.ActorRef{ + {ActorUid: "uid-1", ActorName: "producer", Active: true}, + {ActorUid: "uid-2", ActorName: "consumer"}, + }, + }, + }, + { + Metadata: &ateapipb.ResourceMetadata{ + Atespace: "team-a", + Name: "adopted", + CreateTime: timestamppb.New(now.Add(-5 * time.Hour)), + }, + DeleteTrigger: ateapipb.DeleteTrigger_DELETE_TRIGGER_MANUAL, + AccessMode: ateapipb.AccessMode_ACCESS_MODE_READ_WRITE_ONCE, + VolumeId: "projects/p/zones/z/disks/preexisting", + VolumeType: "pd.csi.storage.gke.io", + Status: &ateapipb.SharedVolumeStatus{ + State: ateapipb.SharedVolumeState_SHARED_VOLUME_STATE_READY, + }, + }, + { + Metadata: &ateapipb.ResourceMetadata{ + Atespace: "team-a", + Name: "stranded", + CreateTime: timestamppb.New(now.Add(-30 * time.Second)), + }, + DeleteTrigger: ateapipb.DeleteTrigger_DELETE_TRIGGER_LAST_ACTOR, + AccessMode: ateapipb.AccessMode_ACCESS_MODE_READ_WRITE_ONCE, + Status: &ateapipb.SharedVolumeStatus{ + State: ateapipb.SharedVolumeState_SHARED_VOLUME_STATE_PENDING, + }, + }, + } + + if err := PrintSharedVolumesTo(&buf, volumes, "table"); err != nil { + t.Fatalf("unexpected error: %v", err) + } + + expected := `ATESPACE NAME STATE VOLUME TYPE VOLUME ID ACCESS MODE DELETE TRIGGER REFS AGE +team-a adopted Ready pd.csi.storage.gke.io projects/p/zones/z/disks/preexisting RWO Manual 0/0 5h +team-a shared Ready pd.csi.storage.gke.io projects/p/zones/z/disks/substrate-1 RWO LastActor 1/2 5m +team-a stranded Pending RWO LastActor 0/0 30s +` + if diff := cmp.Diff(expected, buf.String()); diff != "" { + t.Errorf("output mismatch (-want +got):\n%s", diff) + } +} + +func TestPrintSharedVolumesTo_Invalid(t *testing.T) { + var buf bytes.Buffer + if err := PrintSharedVolumesTo(&buf, nil, "xml"); err == nil { + t.Errorf("expected error for invalid format, got nil") + } +} + +func TestPrintSharedVolumeTo_JSON(t *testing.T) { + var buf bytes.Buffer + volume := &ateapipb.SharedVolume{Metadata: &ateapipb.ResourceMetadata{Atespace: "team-a", Name: "shared"}} + + if err := PrintSharedVolumeTo(&buf, volume, "json"); err != nil { + t.Fatalf("unexpected error: %v", err) + } + + expected := `{ + "metadata": { + "atespace": "team-a", + "name": "shared" + } +} +` + if diff := cmp.Diff(expected, buf.String()); diff != "" { + t.Errorf("output mismatch (-want +got):\n%s", diff) + } +} + +func TestPrintSharedVolumeTo_YAML(t *testing.T) { + var buf bytes.Buffer + volume := &ateapipb.SharedVolume{Metadata: &ateapipb.ResourceMetadata{Atespace: "team-a", Name: "shared"}} + + if err := PrintSharedVolumeTo(&buf, volume, "yaml"); err != nil { + t.Fatalf("unexpected error: %v", err) + } + + expected := `metadata: + atespace: team-a + name: shared +` + if diff := cmp.Diff(expected, buf.String()); diff != "" { + t.Errorf("output mismatch (-want +got):\n%s", diff) + } +} + func TestPrintAtespacesTo_Table(t *testing.T) { now := time.Date(2026, 1, 1, 12, 0, 0, 0, time.UTC) pinNow(t, now) diff --git a/demos/counter/counter-microvm-csi-test-template.yaml b/demos/counter/counter-microvm-csi-test-template.yaml index 293d0c407d..d5e8dc9d32 100644 --- a/demos/counter/counter-microvm-csi-test-template.yaml +++ b/demos/counter/counter-microvm-csi-test-template.yaml @@ -48,6 +48,7 @@ sandboxConfig: configName: microvm volumes: - name: data - externalVolumeTemplate: - capacity: 1Gi - storageClassName: csi-hostpath-sc + external: + template: + capacity: 1Gi + storageClassName: csi-hostpath-sc diff --git a/demos/sandbox/manual-test-multi-template.yaml b/demos/sandbox/manual-test-multi-template.yaml index 3811c37684..704afac6a4 100644 --- a/demos/sandbox/manual-test-multi-template.yaml +++ b/demos/sandbox/manual-test-multi-template.yaml @@ -50,10 +50,12 @@ sandboxConfig: configName: gvisor-default volumes: - name: vol-hostpath - externalVolumeTemplate: - capacity: 1Gi - storageClassName: csi-hostpath-sc + external: + template: + capacity: 1Gi + storageClassName: csi-hostpath-sc - name: vol-nfs - externalVolumeTemplate: - capacity: 1Gi - storageClassName: csi-nfs-sc + external: + template: + capacity: 1Gi + storageClassName: csi-nfs-sc diff --git a/docs/api-guide.md b/docs/api-guide.md index 075bfa045b..cef813e4cd 100644 --- a/docs/api-guide.md +++ b/docs/api-guide.md @@ -102,7 +102,7 @@ The `ActorTemplate` defines the code, environment, and state-management policies | `sandboxConfig` | `SandboxConfig` | **Required.** The sandbox runtime selection: `sandboxClass` (**required**, `gvisor` or `microvm`) picks the runtime family this template's actors require — only `WorkerPool`s whose `sandboxClasses` entry matches are eligible — and `configName` (**required**) names the cluster-scoped [`SandboxConfig`](#3-sandboxconfig-the-sandbox-itself) object supplying the sandbox binaries. It must reference an existing config of the matching class; `CreateActorTemplate` rejects the template otherwise. | | `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`). | | `snapshotConfig` | `SnapshotConfig` | **Required.** The base object-storage location snapshots are written under, the snapshot fidelity (`preferredFidelity`) every Pause and Suspend captures (`SNAPSHOT_FIDELITY_ROOTFS` requires `sandboxClass: microvm`), 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 `externalVolumeTemplate` (see [CSI Volumes Guide](csi-volumes.md)), or a `systemInfo` volume (see [SystemInfo Volumes](#systeminfo-volumes)). Every declared volume must be mounted by at least one container. A `microvm` template may declare several `durableDir` volumes; a `gvisor` template is limited to one. 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 `external` CSI volume source (see [CSI Volumes Guide](csi-volumes.md)), or a `systemInfo` volume (see [SystemInfo Volumes](#systeminfo-volumes)). Every declared volume must be mounted by at least one container. A `microvm` template may declare several `durableDir` volumes; a `gvisor` template is limited to one. 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 `cpu` and `memory` — see [Sandbox Right-Sizing](#sandbox-right-sizing-resources). Immutable, like the rest of the template. | The sandbox itself — the binaries (e.g. the gVisor `runsc` binary) and the `pauseImage` holding the sandbox's namespaces — comes from the cluster-scoped [`SandboxConfig`](#3-sandboxconfig-the-sandbox-itself) object the template names via `sandboxConfig.configName`. An actor always resolves the config from its current template — repointing the actor at another template requires the same config. diff --git a/docs/api-style-guide.md b/docs/api-style-guide.md index 8a733ad102..484576c71c 100644 --- a/docs/api-style-guide.md +++ b/docs/api-style-guide.md @@ -622,7 +622,7 @@ message Volume { // +k8s:format=k8s-short-name string name = 1; - // Exactly one of durable_dir / external_volume_template / image must be set. + // Exactly one of durable_dir / external / image must be set. // // +k8s:optional // +k8s:unionMember @@ -630,7 +630,7 @@ message Volume { // +k8s:optional // +k8s:unionMember - ExternalVolumeTemplate external_volume_template = 3; + ExternalVolumeSource external = 7; // +k8s:optional // +k8s:unionMember diff --git a/docs/csi-volumes.md b/docs/csi-volumes.md index 9997644c4e..5c4ab5b749 100644 --- a/docs/csi-volumes.md +++ b/docs/csi-volumes.md @@ -16,7 +16,7 @@ do not need it. In Kubernetes, volumes are reconciled asynchronously via standard Kubernetes objects (e.g. `PersistentVolumeClaim`, `PersistentVolume`). Agent Substrate takes a different approach tailored for actor lifecycle operations: -* **No PV or PVC Objects:** External volumes are declaratively defined in the [`ActorTemplate`](api-guide.md#2-actortemplate-the-workload-blueprint) via `externalVolumeTemplate` and provisioned dynamically for each actor instance. Volume operations are coupled directly with the actor lifecycle. +* **No PV or PVC Objects:** External volumes are declaratively defined in the [`ActorTemplate`](api-guide.md#2-actortemplate-the-workload-blueprint) via `external.template` and provisioned dynamically for each actor instance. Volume operations are coupled directly with the actor lifecycle. * **Direct Network-Based CSI Controller:** The Substrate control plane (`ateapi`) communicates directly with the CSI Controller gRPC service over the network (via TCP or DNS endpoints, optionally secured with TLS/mTLS). --- @@ -74,7 +74,7 @@ External volumes are declared on the `ActorTemplate` resource. For complete deta To attach a CSI volume to an actor: -1. Define the volume under `volumes` with an `externalVolumeTemplate`. +1. Define the volume under `volumes` with an `external.template`. 2. Mount the volume inside one or more containers under `containers[].volumeMounts`. #### `volumes[]` @@ -82,14 +82,15 @@ To attach a CSI volume to an actor: ```yaml volumes: - name: my-data-volume - externalVolumeTemplate: - capacity: 10Gi - storageClassName: standard-rwx + external: + template: + capacity: 10Gi + storageClassName: standard-rwx ``` * `name`: Unique DNS-label-compliant volume name. -* `externalVolumeTemplate.capacity`: Quantity string representing the requested volume size (e.g. `1Gi`, `50Gi`). -* `externalVolumeTemplate.storageClassName`: Name of a Kubernetes `StorageClass` present in the cluster whose `provisioner` matches a registered `CSIDriverConfig`. +* `external.template.capacity`: Quantity string representing the requested volume size (e.g. `1Gi`, `50Gi`). +* `external.template.storageClassName`: Name of a Kubernetes `StorageClass` present in the cluster whose `provisioner` matches a registered `CSIDriverConfig`. #### `containers[].volumeMounts[]` @@ -200,7 +201,60 @@ snapshotConfig: storageLocation: gs://my-snapshots-bucket/stateful-agent volumes: - name: shared-storage - externalVolumeTemplate: - capacity: 5Gi - storageClassName: csi-nfs-sc + external: + template: + capacity: 5Gi + storageClassName: csi-nfs-sc ``` + +## 5. Shared Volumes + +A `SharedVolume` is an atespace-scoped CSI volume whose lifetime is +independent of any actor, so several actors can mount the same storage in turn. +Create one from a StorageClass, or register a volume that already exists: + +```bash +kubectl ate create sharedvolume ctx-42 -a demo --storage-class csi-nfs-sc --capacity 10Gi +kubectl ate create sharedvolume imported -a demo --volume-id --volume-type nfs.csi.k8s.io +``` + +Each volume records: + +- `accessMode`: fixed at create. Only `READ_WRITE_ONCE` is accepted today, so + at most one resumed actor holds the volume; a resume that would take a held + volume fails and names the holder. +- `deleteTrigger`: `MANUAL` removes the volume only on an explicit delete; + `LAST_ACTOR` removes it when the last actor referencing it is deleted. + Provisioned volumes default to `LAST_ACTOR`, registered ones to `MANUAL`. + Deleting the volume also deletes the storage. +- `status.refs`: every actor referencing the volume, with `active` set while + the actor is resumed. A referenced volume cannot be deleted. + +A template mounts a `SharedVolume` with `external.sharedVolume`. Name the volume +in the template, or leave the name empty and bind it per actor at creation: + +```yaml +volumes: +- name: shared + external: {sharedVolume: {}} +``` + +```bash +kubectl ate create actor producer-42 -a demo --template producer --volume shared=ctx-42 +kubectl ate create actor consumer-42 -a demo --template consumer --volume shared=ctx-42 +``` + +Bindings are validated and recorded when the actor is created and cannot change +afterwards. + +The golden snapshot is not used for templates that reference a `SharedVolume`. The +first resume of such an actor is a cold boot from the image, and later resumes +restore the actor's own snapshot, which was taken over the real volume. An +explicit `sourceTag` is refused at actor creation. The reason is in gVisor: +the golden actor warms up against a local empty directory at the mount path, +but runsc derives the `disable_file_handle_sharing` mount option from the bind +source's filesystem (NFS and FUSE get it, local disks do not) and refuses a +restore whose mount options differ from the checkpoint's. Supporting golden +restores here is follow-up work; the candidate approaches are a golden actor +that warms up on a scratch volume from the same driver, or an upstream runsc +change that excludes host-derived mount options from restore validation. diff --git a/internal/e2e/suites/combinedvolumes/combinedvolumes_test.go b/internal/e2e/suites/combinedvolumes/combinedvolumes_test.go index c0cc7a39aa..9796eb01d5 100644 --- a/internal/e2e/suites/combinedvolumes/combinedvolumes_test.go +++ b/internal/e2e/suites/combinedvolumes/combinedvolumes_test.go @@ -41,6 +41,8 @@ import ( "go.ate.dev/substrate/internal/preview" "go.ate.dev/substrate/internal/resources" "go.ate.dev/substrate/pkg/proto/ateapipb" + "google.golang.org/grpc/codes" + "google.golang.org/grpc/status" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" ) @@ -69,6 +71,10 @@ const ( // probeWrittenContent is the fixed string the probe's /writefile writes. probeWrittenContent = "written by probe" + sharedVolume = "shared" + sharedPath = "/mnt/ate-shared" + sharedFile = "handoff.txt" + payloadName = "payload.txt" payloadContent = "delivered by an image volume" // shadowedName is written by two layers; the upper one must win. @@ -234,10 +240,10 @@ func createTemplate(ctx context.Context, t *testing.T, clients *e2e.Clients, ns &ateapipb.VolumeMount{Name: extVolume, MountPath: extPathB}) tmpl.Volumes = append(tmpl.Volumes, &ateapipb.Volume{ Name: extVolume, - ExternalVolumeTemplate: &ateapipb.ExternalVolumeTemplate{ + External: &ateapipb.ExternalVolumeSource{Template: &ateapipb.ExternalVolumeTemplate{ Capacity: extCapacity, StorageClassName: storageClass, - }, + }}, }) }, }) @@ -469,3 +475,174 @@ func TestCombinedVolumes(t *testing.T) { }) }) } + +// createHandoffTemplate builds a probe ActorTemplate that references the +// SharedVolume named volumeName. +func createHandoffTemplate(ctx context.Context, t *testing.T, clients *e2e.Clients, ns *e2e.Namespace, volumeName string) *ateapipb.ActorTemplate { + t.Helper() + + env, err := e2e.CheckEnv("BUCKET_NAME") + if err != nil { + t.Fatalf("CheckEnv: %v", err) + } + probeAtespace, _ := e2e.DeployProbe(t, env["BUCKET_NAME"], "combinedvolumes") + src := e2e.SubstrateFixture{ + Atespace: probeAtespace, + Name: probeName, + PoolNamespace: probeAtespace, + PoolName: probeName, + DeployWith: "the combinedvolumes suite's own DeployProbe", + } + return e2e.CreateSubstrateTemplateFrom(ctx, t, clients, ns.Name, src, e2e.SubstrateTemplateOptions{ + Atespace: atespace, + Name: "handoff-" + ns.Name, + PoolName: probeName, + PoolReplicas: 2, + Labels: map[string]string{"combinedvolumes": ns.Name}, + SnapshotConfig: &ateapipb.SnapshotConfig{ + StorageLocation: fmt.Sprintf("gs://%s/%s/", env["BUCKET_NAME"], ns.Name), + }, + Modify: func(tmpl *ateapipb.ActorTemplate) { + tmpl.Containers[0].VolumeMounts = append(tmpl.Containers[0].VolumeMounts, + &ateapipb.VolumeMount{Name: sharedVolume, MountPath: sharedPath}) + tmpl.Volumes = append(tmpl.Volumes, &ateapipb.Volume{ + Name: sharedVolume, + External: &ateapipb.ExternalVolumeSource{SharedVolume: &ateapipb.SharedVolumeRef{Name: volumeName}}, + }) + }, + }) +} + +// createHandoffActor creates a suspended actor from tmpl and registers its +// teardown, which must run before the shared volume's. +func createHandoffActor(ctx context.Context, t *testing.T, clients *e2e.Clients, tmpl *ateapipb.ActorTemplate, name string) resources.ActorRef { + t.Helper() + + actorRef := resources.ActorRef{Atespace: atespace, Name: name} + if _, err := clients.SubstrateAPI.CreateActor(ctx, &ateapipb.CreateActorRequest{ + Actor: &ateapipb.Actor{ + Metadata: &ateapipb.ResourceMetadata{Atespace: actorRef.Atespace, Name: actorRef.Name}, + ActorTemplate: e2e.TemplateRef(tmpl), + }, + }); err != nil { + t.Fatalf("CreateActor(%s): %v", name, err) + } + t.Cleanup(func() { + cleanupCtx := context.Background() + _, _ = clients.SubstrateAPI.SuspendActor(cleanupCtx, &ateapipb.SuspendActorRequest{Actor: actorRef.ToObjectRef()}) + _, _ = clients.SubstrateAPI.DeleteActor(cleanupCtx, &ateapipb.DeleteActorRequest{Actor: actorRef.ToObjectRef()}) + }) + return actorRef +} + +func TestSharedVolumeHandoff(t *testing.T) { + ctx := context.Background() + clients := e2e.GetClients() + ns := e2e.CreateNamespace(t) + + if storageClassOrEmpty(ctx, t, clients) == "" { + t.Skipf("StorageClass %q is not installed", e2e.StorageClass) + } + + volumeName := "shared-" + ns.Name + // The template is created first because it creates the suite's atespace. + tmpl := createHandoffTemplate(ctx, t, clients, ns, volumeName) + + volumeRef := &ateapipb.ObjectRef{Atespace: atespace, Name: volumeName} + if _, err := clients.SubstrateAPI.CreateSharedVolume(ctx, &ateapipb.CreateSharedVolumeRequest{ + SharedVolume: &ateapipb.SharedVolume{ + Metadata: &ateapipb.ResourceMetadata{Atespace: atespace, Name: volumeName}, + StorageClassName: e2e.StorageClass, + Capacity: extCapacity, + DeleteTrigger: ateapipb.DeleteTrigger_DELETE_TRIGGER_MANUAL, + }, + }); err != nil { + t.Fatalf("CreateSharedVolume: %v", err) + } + t.Cleanup(func() { + if _, err := clients.SubstrateAPI.DeleteSharedVolume(context.Background(), &ateapipb.DeleteSharedVolumeRequest{SharedVolume: volumeRef}); err != nil { + t.Errorf("DeleteSharedVolume: %v", err) + } + }) + + producer := createHandoffActor(ctx, t, clients, tmpl, "producer-"+ns.Name) + consumer := createHandoffActor(ctx, t, clients, tmpl, "consumer-"+ns.Name) + + router, err := e2e.NewRouterClient(ctx) + if err != nil { + t.Fatalf("NewRouterClient: %v", err) + } + defer router.Close() + + handoffFile := sharedPath + "/" + sharedFile + if _, err := e2e.ResumeActorAwaitCapacity(t, ctx, clients, &ateapipb.ResumeActorRequest{Actor: producer.ToObjectRef()}); err != nil { + t.Fatalf("ResumeActor(producer): %v", err) + } + requireUnreadable(ctx, t, router, producer, handoffFile) + if got := probeJSON(ctx, t, router, producer, "/writefile?path="+handoffFile); got["error"] != "" { + t.Fatalf("writing %s: %s", handoffFile, got["error"]) + } + requireContent(ctx, t, router, producer, handoffFile, probeWrittenContent) + + _, err = e2e.ResumeActorAwaitCapacity(t, ctx, clients, &ateapipb.ResumeActorRequest{Actor: consumer.ToObjectRef()}) + if status.Code(err) != codes.FailedPrecondition || !strings.Contains(err.Error(), producer.Name) { + t.Fatalf("ResumeActor(consumer) while the producer holds the volume = %v, want FailedPrecondition naming %q", err, producer.Name) + } + blocked, err := clients.SubstrateAPI.GetActor(ctx, &ateapipb.GetActorRequest{Actor: consumer.ToObjectRef()}) + if err != nil { + t.Fatalf("GetActor(consumer): %v", err) + } + if got := blocked.GetStatus().GetState(); got != ateapipb.ActorState_ACTOR_STATE_SUSPENDED { + t.Errorf("consumer state after a refused claim = %s, want ACTOR_STATE_SUSPENDED", got) + } + + if _, err := clients.SubstrateAPI.SuspendActor(ctx, &ateapipb.SuspendActorRequest{Actor: producer.ToObjectRef()}); err != nil { + t.Fatalf("SuspendActor(producer): %v", err) + } + if _, err := e2e.ResumeActorAwaitCapacity(t, ctx, clients, &ateapipb.ResumeActorRequest{Actor: consumer.ToObjectRef()}); err != nil { + t.Fatalf("ResumeActor(consumer): %v", err) + } + requireContent(ctx, t, router, consumer, handoffFile, probeWrittenContent) + + if _, err := clients.SubstrateAPI.DeleteSharedVolume(ctx, &ateapipb.DeleteSharedVolumeRequest{SharedVolume: volumeRef}); status.Code(err) != codes.FailedPrecondition { + t.Errorf("DeleteSharedVolume while referenced = %v, want FailedPrecondition", err) + } + stored, err := clients.SubstrateAPI.GetSharedVolume(ctx, &ateapipb.GetSharedVolumeRequest{SharedVolume: volumeRef}) + if err != nil { + t.Fatalf("GetSharedVolume: %v", err) + } + actor, err := clients.SubstrateAPI.GetActor(ctx, &ateapipb.GetActorRequest{Actor: consumer.ToObjectRef()}) + if err != nil { + t.Fatalf("GetActor(consumer): %v", err) + } + var mounted string + for _, vol := range actor.GetStatus().GetExternalVolumes() { + if vol.GetName() == sharedVolume { + mounted = vol.GetStorageVolumeId() + } + } + if mounted != stored.GetVolumeId() { + t.Errorf("the consumer mounted %q, want the shared volume %q", mounted, stored.GetVolumeId()) + } +} + +func TestSharedVolumeMissing(t *testing.T) { + ctx := context.Background() + clients := e2e.GetClients() + ns := e2e.CreateNamespace(t) + + if storageClassOrEmpty(ctx, t, clients) == "" { + t.Skipf("StorageClass %q is not installed", e2e.StorageClass) + } + + tmpl := createHandoffTemplate(ctx, t, clients, ns, "absent-"+ns.Name) + _, err := clients.SubstrateAPI.CreateActor(ctx, &ateapipb.CreateActorRequest{ + Actor: &ateapipb.Actor{ + Metadata: &ateapipb.ResourceMetadata{Atespace: atespace, Name: "orphan-" + ns.Name}, + ActorTemplate: e2e.TemplateRef(tmpl), + }, + }) + if status.Code(err) != codes.FailedPrecondition { + t.Fatalf("CreateActor with an unresolvable volume reference = %v, want FailedPrecondition", err) + } +} diff --git a/internal/e2e/suites/demo/demo_test.go b/internal/e2e/suites/demo/demo_test.go index b57684cbc2..55bc353807 100644 --- a/internal/e2e/suites/demo/demo_test.go +++ b/internal/e2e/suites/demo/demo_test.go @@ -1474,10 +1474,10 @@ func createActorTemplateWithExternalVolume(ctx context.Context, t *testing.T, cl if !hasExtVol { at.Volumes = append(at.Volumes, &ateapipb.Volume{ Name: "external-data", - ExternalVolumeTemplate: &ateapipb.ExternalVolumeTemplate{ + External: &ateapipb.ExternalVolumeSource{Template: &ateapipb.ExternalVolumeTemplate{ Capacity: "1Gi", StorageClassName: scName, - }, + }}, }) } } diff --git a/internal/proto/ateletpb/atelet.pb.go b/internal/proto/ateletpb/atelet.pb.go index 91554e0af9..7ae1df56f8 100644 --- a/internal/proto/ateletpb/atelet.pb.go +++ b/internal/proto/ateletpb/atelet.pb.go @@ -1962,6 +1962,43 @@ func (*DurableDirVolumeSource) Descriptor() ([]byte, []int) { return file_atelet_proto_rawDescGZIP(), []int{27} } +// EmptyDirVolumeSource is an empty host directory at the external volume path. +type EmptyDirVolumeSource struct { + state protoimpl.MessageState `protogen:"open.v1"` + unknownFields protoimpl.UnknownFields + sizeCache protoimpl.SizeCache +} + +func (x *EmptyDirVolumeSource) Reset() { + *x = EmptyDirVolumeSource{} + mi := &file_atelet_proto_msgTypes[28] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) +} + +func (x *EmptyDirVolumeSource) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*EmptyDirVolumeSource) ProtoMessage() {} + +func (x *EmptyDirVolumeSource) ProtoReflect() protoreflect.Message { + mi := &file_atelet_proto_msgTypes[28] + 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 EmptyDirVolumeSource.ProtoReflect.Descriptor instead. +func (*EmptyDirVolumeSource) Descriptor() ([]byte, []int) { + return file_atelet_proto_rawDescGZIP(), []int{28} +} + type ExternalVolumeSource struct { state protoimpl.MessageState `protogen:"open.v1"` // Required, unlike the actor status counterpart: the control plane only @@ -1993,7 +2030,7 @@ type ExternalVolumeSource struct { func (x *ExternalVolumeSource) Reset() { *x = ExternalVolumeSource{} - mi := &file_atelet_proto_msgTypes[28] + mi := &file_atelet_proto_msgTypes[29] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -2005,7 +2042,7 @@ func (x *ExternalVolumeSource) String() string { func (*ExternalVolumeSource) ProtoMessage() {} func (x *ExternalVolumeSource) ProtoReflect() protoreflect.Message { - mi := &file_atelet_proto_msgTypes[28] + mi := &file_atelet_proto_msgTypes[29] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -2018,7 +2055,7 @@ func (x *ExternalVolumeSource) ProtoReflect() protoreflect.Message { // Deprecated: Use ExternalVolumeSource.ProtoReflect.Descriptor instead. func (*ExternalVolumeSource) Descriptor() ([]byte, []int) { - return file_atelet_proto_rawDescGZIP(), []int{28} + return file_atelet_proto_rawDescGZIP(), []int{29} } func (x *ExternalVolumeSource) GetStorageVolumeId() string { @@ -2063,7 +2100,7 @@ type ImageVolumeSource struct { func (x *ImageVolumeSource) Reset() { *x = ImageVolumeSource{} - mi := &file_atelet_proto_msgTypes[29] + mi := &file_atelet_proto_msgTypes[30] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -2075,7 +2112,7 @@ func (x *ImageVolumeSource) String() string { func (*ImageVolumeSource) ProtoMessage() {} func (x *ImageVolumeSource) ProtoReflect() protoreflect.Message { - mi := &file_atelet_proto_msgTypes[29] + mi := &file_atelet_proto_msgTypes[30] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -2088,7 +2125,7 @@ func (x *ImageVolumeSource) ProtoReflect() protoreflect.Message { // Deprecated: Use ImageVolumeSource.ProtoReflect.Descriptor instead. func (*ImageVolumeSource) Descriptor() ([]byte, []int) { - return file_atelet_proto_rawDescGZIP(), []int{29} + return file_atelet_proto_rawDescGZIP(), []int{30} } func (x *ImageVolumeSource) GetReference() string { @@ -2120,7 +2157,7 @@ type ActorMetadataItem struct { func (x *ActorMetadataItem) Reset() { *x = ActorMetadataItem{} - mi := &file_atelet_proto_msgTypes[30] + mi := &file_atelet_proto_msgTypes[31] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -2132,7 +2169,7 @@ func (x *ActorMetadataItem) String() string { func (*ActorMetadataItem) ProtoMessage() {} func (x *ActorMetadataItem) ProtoReflect() protoreflect.Message { - mi := &file_atelet_proto_msgTypes[30] + mi := &file_atelet_proto_msgTypes[31] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -2145,7 +2182,7 @@ func (x *ActorMetadataItem) ProtoReflect() protoreflect.Message { // Deprecated: Use ActorMetadataItem.ProtoReflect.Descriptor instead. func (*ActorMetadataItem) Descriptor() ([]byte, []int) { - return file_atelet_proto_rawDescGZIP(), []int{30} + return file_atelet_proto_rawDescGZIP(), []int{31} } func (x *ActorMetadataItem) GetField() ActorMetadataField { @@ -2181,7 +2218,7 @@ type ActorMetadataDataSource struct { func (x *ActorMetadataDataSource) Reset() { *x = ActorMetadataDataSource{} - mi := &file_atelet_proto_msgTypes[31] + mi := &file_atelet_proto_msgTypes[32] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -2193,7 +2230,7 @@ func (x *ActorMetadataDataSource) String() string { func (*ActorMetadataDataSource) ProtoMessage() {} func (x *ActorMetadataDataSource) ProtoReflect() protoreflect.Message { - mi := &file_atelet_proto_msgTypes[31] + mi := &file_atelet_proto_msgTypes[32] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -2206,7 +2243,7 @@ func (x *ActorMetadataDataSource) ProtoReflect() protoreflect.Message { // Deprecated: Use ActorMetadataDataSource.ProtoReflect.Descriptor instead. func (*ActorMetadataDataSource) Descriptor() ([]byte, []int) { - return file_atelet_proto_rawDescGZIP(), []int{31} + return file_atelet_proto_rawDescGZIP(), []int{32} } func (x *ActorMetadataDataSource) GetItems() []*ActorMetadataItem { @@ -2240,7 +2277,7 @@ type TrustBundleDataSource struct { func (x *TrustBundleDataSource) Reset() { *x = TrustBundleDataSource{} - mi := &file_atelet_proto_msgTypes[32] + mi := &file_atelet_proto_msgTypes[33] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -2252,7 +2289,7 @@ func (x *TrustBundleDataSource) String() string { func (*TrustBundleDataSource) ProtoMessage() {} func (x *TrustBundleDataSource) ProtoReflect() protoreflect.Message { - mi := &file_atelet_proto_msgTypes[32] + mi := &file_atelet_proto_msgTypes[33] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -2265,7 +2302,7 @@ func (x *TrustBundleDataSource) ProtoReflect() protoreflect.Message { // Deprecated: Use TrustBundleDataSource.ProtoReflect.Descriptor instead. func (*TrustBundleDataSource) Descriptor() ([]byte, []int) { - return file_atelet_proto_rawDescGZIP(), []int{32} + return file_atelet_proto_rawDescGZIP(), []int{33} } func (x *TrustBundleDataSource) GetPath() string { @@ -2300,7 +2337,7 @@ type SystemInfoDataSource struct { func (x *SystemInfoDataSource) Reset() { *x = SystemInfoDataSource{} - mi := &file_atelet_proto_msgTypes[33] + mi := &file_atelet_proto_msgTypes[34] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -2312,7 +2349,7 @@ func (x *SystemInfoDataSource) String() string { func (*SystemInfoDataSource) ProtoMessage() {} func (x *SystemInfoDataSource) ProtoReflect() protoreflect.Message { - mi := &file_atelet_proto_msgTypes[33] + mi := &file_atelet_proto_msgTypes[34] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -2325,7 +2362,7 @@ func (x *SystemInfoDataSource) ProtoReflect() protoreflect.Message { // Deprecated: Use SystemInfoDataSource.ProtoReflect.Descriptor instead. func (*SystemInfoDataSource) Descriptor() ([]byte, []int) { - return file_atelet_proto_rawDescGZIP(), []int{33} + return file_atelet_proto_rawDescGZIP(), []int{34} } func (x *SystemInfoDataSource) GetActorMetadata() *ActorMetadataDataSource { @@ -2358,7 +2395,7 @@ type SystemInfoVolumeSource struct { func (x *SystemInfoVolumeSource) Reset() { *x = SystemInfoVolumeSource{} - mi := &file_atelet_proto_msgTypes[34] + mi := &file_atelet_proto_msgTypes[35] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -2370,7 +2407,7 @@ func (x *SystemInfoVolumeSource) String() string { func (*SystemInfoVolumeSource) ProtoMessage() {} func (x *SystemInfoVolumeSource) ProtoReflect() protoreflect.Message { - mi := &file_atelet_proto_msgTypes[34] + mi := &file_atelet_proto_msgTypes[35] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -2383,7 +2420,7 @@ func (x *SystemInfoVolumeSource) ProtoReflect() protoreflect.Message { // Deprecated: Use SystemInfoVolumeSource.ProtoReflect.Descriptor instead. func (*SystemInfoVolumeSource) Descriptor() ([]byte, []int) { - return file_atelet_proto_rawDescGZIP(), []int{34} + return file_atelet_proto_rawDescGZIP(), []int{35} } func (x *SystemInfoVolumeSource) GetDataSources() []*SystemInfoDataSource { @@ -2399,7 +2436,7 @@ type Volume struct { // +k8s:required // +k8s:format=k8s-short-name Name string `protobuf:"bytes,1,opt,name=name,proto3" json:"name,omitempty"` - // Exactly one of durable_dir / external / system_info / image must be set. + // Exactly one of durable_dir / external / system_info / image / empty_dir must be set. // // +k8s:optional // +k8s:unionMember @@ -2412,14 +2449,17 @@ type Volume struct { SystemInfo *SystemInfoVolumeSource `protobuf:"bytes,4,opt,name=system_info,json=systemInfo,proto3" json:"system_info,omitempty"` // +k8s:optional // +k8s:unionMember - Image *ImageVolumeSource `protobuf:"bytes,5,opt,name=image,proto3" json:"image,omitempty"` + Image *ImageVolumeSource `protobuf:"bytes,5,opt,name=image,proto3" json:"image,omitempty"` + // +k8s:optional + // +k8s:unionMember + EmptyDir *EmptyDirVolumeSource `protobuf:"bytes,6,opt,name=empty_dir,json=emptyDir,proto3" json:"empty_dir,omitempty"` unknownFields protoimpl.UnknownFields sizeCache protoimpl.SizeCache } func (x *Volume) Reset() { *x = Volume{} - mi := &file_atelet_proto_msgTypes[35] + mi := &file_atelet_proto_msgTypes[36] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -2431,7 +2471,7 @@ func (x *Volume) String() string { func (*Volume) ProtoMessage() {} func (x *Volume) ProtoReflect() protoreflect.Message { - mi := &file_atelet_proto_msgTypes[35] + mi := &file_atelet_proto_msgTypes[36] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -2444,7 +2484,7 @@ func (x *Volume) ProtoReflect() protoreflect.Message { // Deprecated: Use Volume.ProtoReflect.Descriptor instead. func (*Volume) Descriptor() ([]byte, []int) { - return file_atelet_proto_rawDescGZIP(), []int{35} + return file_atelet_proto_rawDescGZIP(), []int{36} } func (x *Volume) GetName() string { @@ -2482,6 +2522,13 @@ func (x *Volume) GetImage() *ImageVolumeSource { return nil } +func (x *Volume) GetEmptyDir() *EmptyDirVolumeSource { + if x != nil { + return x.EmptyDir + } + return nil +} + type VolumeMount struct { state protoimpl.MessageState `protogen:"open.v1"` // name must match the name of a Volume. @@ -2501,7 +2548,7 @@ type VolumeMount struct { func (x *VolumeMount) Reset() { *x = VolumeMount{} - mi := &file_atelet_proto_msgTypes[36] + mi := &file_atelet_proto_msgTypes[37] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -2513,7 +2560,7 @@ func (x *VolumeMount) String() string { func (*VolumeMount) ProtoMessage() {} func (x *VolumeMount) ProtoReflect() protoreflect.Message { - mi := &file_atelet_proto_msgTypes[36] + mi := &file_atelet_proto_msgTypes[37] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -2526,7 +2573,7 @@ func (x *VolumeMount) ProtoReflect() protoreflect.Message { // Deprecated: Use VolumeMount.ProtoReflect.Descriptor instead. func (*VolumeMount) Descriptor() ([]byte, []int) { - return file_atelet_proto_rawDescGZIP(), []int{36} + return file_atelet_proto_rawDescGZIP(), []int{37} } func (x *VolumeMount) GetName() string { @@ -2591,7 +2638,7 @@ type Container struct { func (x *Container) Reset() { *x = Container{} - mi := &file_atelet_proto_msgTypes[37] + mi := &file_atelet_proto_msgTypes[38] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -2603,7 +2650,7 @@ func (x *Container) String() string { func (*Container) ProtoMessage() {} func (x *Container) ProtoReflect() protoreflect.Message { - mi := &file_atelet_proto_msgTypes[37] + mi := &file_atelet_proto_msgTypes[38] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -2616,7 +2663,7 @@ func (x *Container) ProtoReflect() protoreflect.Message { // Deprecated: Use Container.ProtoReflect.Descriptor instead. func (*Container) Descriptor() ([]byte, []int) { - return file_atelet_proto_rawDescGZIP(), []int{37} + return file_atelet_proto_rawDescGZIP(), []int{38} } func (x *Container) GetName() string { @@ -2693,7 +2740,7 @@ type SecurityContext struct { func (x *SecurityContext) Reset() { *x = SecurityContext{} - mi := &file_atelet_proto_msgTypes[38] + mi := &file_atelet_proto_msgTypes[39] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -2705,7 +2752,7 @@ func (x *SecurityContext) String() string { func (*SecurityContext) ProtoMessage() {} func (x *SecurityContext) ProtoReflect() protoreflect.Message { - mi := &file_atelet_proto_msgTypes[38] + mi := &file_atelet_proto_msgTypes[39] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -2718,7 +2765,7 @@ func (x *SecurityContext) ProtoReflect() protoreflect.Message { // Deprecated: Use SecurityContext.ProtoReflect.Descriptor instead. func (*SecurityContext) Descriptor() ([]byte, []int) { - return file_atelet_proto_rawDescGZIP(), []int{38} + return file_atelet_proto_rawDescGZIP(), []int{39} } func (x *SecurityContext) GetCapabilities() *Capabilities { @@ -2748,7 +2795,7 @@ type Capabilities struct { func (x *Capabilities) Reset() { *x = Capabilities{} - mi := &file_atelet_proto_msgTypes[39] + mi := &file_atelet_proto_msgTypes[40] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -2760,7 +2807,7 @@ func (x *Capabilities) String() string { func (*Capabilities) ProtoMessage() {} func (x *Capabilities) ProtoReflect() protoreflect.Message { - mi := &file_atelet_proto_msgTypes[39] + mi := &file_atelet_proto_msgTypes[40] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -2773,7 +2820,7 @@ func (x *Capabilities) ProtoReflect() protoreflect.Message { // Deprecated: Use Capabilities.ProtoReflect.Descriptor instead. func (*Capabilities) Descriptor() ([]byte, []int) { - return file_atelet_proto_rawDescGZIP(), []int{39} + return file_atelet_proto_rawDescGZIP(), []int{40} } func (x *Capabilities) GetAdd() []string { @@ -2811,7 +2858,7 @@ type ResourceLimits struct { func (x *ResourceLimits) Reset() { *x = ResourceLimits{} - mi := &file_atelet_proto_msgTypes[40] + mi := &file_atelet_proto_msgTypes[41] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -2823,7 +2870,7 @@ func (x *ResourceLimits) String() string { func (*ResourceLimits) ProtoMessage() {} func (x *ResourceLimits) ProtoReflect() protoreflect.Message { - mi := &file_atelet_proto_msgTypes[40] + mi := &file_atelet_proto_msgTypes[41] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -2836,7 +2883,7 @@ func (x *ResourceLimits) ProtoReflect() protoreflect.Message { // Deprecated: Use ResourceLimits.ProtoReflect.Descriptor instead. func (*ResourceLimits) Descriptor() ([]byte, []int) { - return file_atelet_proto_rawDescGZIP(), []int{40} + return file_atelet_proto_rawDescGZIP(), []int{41} } func (x *ResourceLimits) GetMemoryBytes() int64 { @@ -2870,7 +2917,7 @@ type EnvVar struct { func (x *EnvVar) Reset() { *x = EnvVar{} - mi := &file_atelet_proto_msgTypes[41] + mi := &file_atelet_proto_msgTypes[42] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -2882,7 +2929,7 @@ func (x *EnvVar) String() string { func (*EnvVar) ProtoMessage() {} func (x *EnvVar) ProtoReflect() protoreflect.Message { - mi := &file_atelet_proto_msgTypes[41] + mi := &file_atelet_proto_msgTypes[42] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -2895,7 +2942,7 @@ func (x *EnvVar) ProtoReflect() protoreflect.Message { // Deprecated: Use EnvVar.ProtoReflect.Descriptor instead. func (*EnvVar) Descriptor() ([]byte, []int) { - return file_atelet_proto_rawDescGZIP(), []int{41} + return file_atelet_proto_rawDescGZIP(), []int{42} } func (x *EnvVar) GetName() string { @@ -2930,7 +2977,7 @@ type WakeupProbe struct { func (x *WakeupProbe) Reset() { *x = WakeupProbe{} - mi := &file_atelet_proto_msgTypes[42] + mi := &file_atelet_proto_msgTypes[43] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -2942,7 +2989,7 @@ func (x *WakeupProbe) String() string { func (*WakeupProbe) ProtoMessage() {} func (x *WakeupProbe) ProtoReflect() protoreflect.Message { - mi := &file_atelet_proto_msgTypes[42] + mi := &file_atelet_proto_msgTypes[43] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -2955,7 +3002,7 @@ func (x *WakeupProbe) ProtoReflect() protoreflect.Message { // Deprecated: Use WakeupProbe.ProtoReflect.Descriptor instead. func (*WakeupProbe) Descriptor() ([]byte, []int) { - return file_atelet_proto_rawDescGZIP(), []int{42} + return file_atelet_proto_rawDescGZIP(), []int{43} } func (x *WakeupProbe) GetHttpGet() *HttpGetAction { @@ -2993,7 +3040,7 @@ type HttpGetAction struct { func (x *HttpGetAction) Reset() { *x = HttpGetAction{} - mi := &file_atelet_proto_msgTypes[43] + mi := &file_atelet_proto_msgTypes[44] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -3005,7 +3052,7 @@ func (x *HttpGetAction) String() string { func (*HttpGetAction) ProtoMessage() {} func (x *HttpGetAction) ProtoReflect() protoreflect.Message { - mi := &file_atelet_proto_msgTypes[43] + mi := &file_atelet_proto_msgTypes[44] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -3018,7 +3065,7 @@ func (x *HttpGetAction) ProtoReflect() protoreflect.Message { // Deprecated: Use HttpGetAction.ProtoReflect.Descriptor instead. func (*HttpGetAction) Descriptor() ([]byte, []int) { - return file_atelet_proto_rawDescGZIP(), []int{43} + return file_atelet_proto_rawDescGZIP(), []int{44} } func (x *HttpGetAction) GetPath() string { @@ -3043,7 +3090,7 @@ type RunResponse struct { func (x *RunResponse) Reset() { *x = RunResponse{} - mi := &file_atelet_proto_msgTypes[44] + mi := &file_atelet_proto_msgTypes[45] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -3055,7 +3102,7 @@ func (x *RunResponse) String() string { func (*RunResponse) ProtoMessage() {} func (x *RunResponse) ProtoReflect() protoreflect.Message { - mi := &file_atelet_proto_msgTypes[44] + mi := &file_atelet_proto_msgTypes[45] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -3068,7 +3115,7 @@ func (x *RunResponse) ProtoReflect() protoreflect.Message { // Deprecated: Use RunResponse.ProtoReflect.Descriptor instead. func (*RunResponse) Descriptor() ([]byte, []int) { - return file_atelet_proto_rawDescGZIP(), []int{44} + return file_atelet_proto_rawDescGZIP(), []int{45} } type ObjectStorage struct { @@ -3086,7 +3133,7 @@ type ObjectStorage struct { func (x *ObjectStorage) Reset() { *x = ObjectStorage{} - mi := &file_atelet_proto_msgTypes[45] + mi := &file_atelet_proto_msgTypes[46] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -3098,7 +3145,7 @@ func (x *ObjectStorage) String() string { func (*ObjectStorage) ProtoMessage() {} func (x *ObjectStorage) ProtoReflect() protoreflect.Message { - mi := &file_atelet_proto_msgTypes[45] + mi := &file_atelet_proto_msgTypes[46] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -3111,7 +3158,7 @@ func (x *ObjectStorage) ProtoReflect() protoreflect.Message { // Deprecated: Use ObjectStorage.ProtoReflect.Descriptor instead. func (*ObjectStorage) Descriptor() ([]byte, []int) { - return file_atelet_proto_rawDescGZIP(), []int{45} + return file_atelet_proto_rawDescGZIP(), []int{46} } func (x *ObjectStorage) GetSnapshotUri() string { @@ -3171,7 +3218,7 @@ type Snapshot struct { func (x *Snapshot) Reset() { *x = Snapshot{} - mi := &file_atelet_proto_msgTypes[46] + mi := &file_atelet_proto_msgTypes[47] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -3183,7 +3230,7 @@ func (x *Snapshot) String() string { func (*Snapshot) ProtoMessage() {} func (x *Snapshot) ProtoReflect() protoreflect.Message { - mi := &file_atelet_proto_msgTypes[46] + mi := &file_atelet_proto_msgTypes[47] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -3196,7 +3243,7 @@ func (x *Snapshot) ProtoReflect() protoreflect.Message { // Deprecated: Use Snapshot.ProtoReflect.Descriptor instead. func (*Snapshot) Descriptor() ([]byte, []int) { - return file_atelet_proto_rawDescGZIP(), []int{46} + return file_atelet_proto_rawDescGZIP(), []int{47} } func (x *Snapshot) GetSnapshotUuid() string { @@ -3243,7 +3290,7 @@ type SnapshotReport struct { func (x *SnapshotReport) Reset() { *x = SnapshotReport{} - mi := &file_atelet_proto_msgTypes[47] + mi := &file_atelet_proto_msgTypes[48] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -3255,7 +3302,7 @@ func (x *SnapshotReport) String() string { func (*SnapshotReport) ProtoMessage() {} func (x *SnapshotReport) ProtoReflect() protoreflect.Message { - mi := &file_atelet_proto_msgTypes[47] + mi := &file_atelet_proto_msgTypes[48] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -3268,7 +3315,7 @@ func (x *SnapshotReport) ProtoReflect() protoreflect.Message { // Deprecated: Use SnapshotReport.ProtoReflect.Descriptor instead. func (*SnapshotReport) Descriptor() ([]byte, []int) { - return file_atelet_proto_rawDescGZIP(), []int{47} + return file_atelet_proto_rawDescGZIP(), []int{48} } func (x *SnapshotReport) GetSnapshot() *Snapshot { @@ -3323,7 +3370,7 @@ type ActorSnapshotState struct { func (x *ActorSnapshotState) Reset() { *x = ActorSnapshotState{} - mi := &file_atelet_proto_msgTypes[48] + mi := &file_atelet_proto_msgTypes[49] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -3335,7 +3382,7 @@ func (x *ActorSnapshotState) String() string { func (*ActorSnapshotState) ProtoMessage() {} func (x *ActorSnapshotState) ProtoReflect() protoreflect.Message { - mi := &file_atelet_proto_msgTypes[48] + mi := &file_atelet_proto_msgTypes[49] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -3348,7 +3395,7 @@ func (x *ActorSnapshotState) ProtoReflect() protoreflect.Message { // Deprecated: Use ActorSnapshotState.ProtoReflect.Descriptor instead. func (*ActorSnapshotState) Descriptor() ([]byte, []int) { - return file_atelet_proto_rawDescGZIP(), []int{48} + return file_atelet_proto_rawDescGZIP(), []int{49} } func (x *ActorSnapshotState) GetLatestLocal() *SnapshotReport { @@ -3392,7 +3439,7 @@ type UploadConfig struct { func (x *UploadConfig) Reset() { *x = UploadConfig{} - mi := &file_atelet_proto_msgTypes[49] + mi := &file_atelet_proto_msgTypes[50] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -3404,7 +3451,7 @@ func (x *UploadConfig) String() string { func (*UploadConfig) ProtoMessage() {} func (x *UploadConfig) ProtoReflect() protoreflect.Message { - mi := &file_atelet_proto_msgTypes[49] + mi := &file_atelet_proto_msgTypes[50] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -3417,7 +3464,7 @@ func (x *UploadConfig) ProtoReflect() protoreflect.Message { // Deprecated: Use UploadConfig.ProtoReflect.Descriptor instead. func (*UploadConfig) Descriptor() ([]byte, []int) { - return file_atelet_proto_rawDescGZIP(), []int{49} + return file_atelet_proto_rawDescGZIP(), []int{50} } func (x *UploadConfig) GetMode() UploadMode { @@ -3480,7 +3527,7 @@ type CheckpointRequest struct { func (x *CheckpointRequest) Reset() { *x = CheckpointRequest{} - mi := &file_atelet_proto_msgTypes[50] + mi := &file_atelet_proto_msgTypes[51] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -3492,7 +3539,7 @@ func (x *CheckpointRequest) String() string { func (*CheckpointRequest) ProtoMessage() {} func (x *CheckpointRequest) ProtoReflect() protoreflect.Message { - mi := &file_atelet_proto_msgTypes[50] + mi := &file_atelet_proto_msgTypes[51] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -3505,7 +3552,7 @@ func (x *CheckpointRequest) ProtoReflect() protoreflect.Message { // Deprecated: Use CheckpointRequest.ProtoReflect.Descriptor instead. func (*CheckpointRequest) Descriptor() ([]byte, []int) { - return file_atelet_proto_rawDescGZIP(), []int{50} + return file_atelet_proto_rawDescGZIP(), []int{51} } func (x *CheckpointRequest) GetWorkerPodUid() string { @@ -3584,7 +3631,7 @@ type CheckpointResponse struct { func (x *CheckpointResponse) Reset() { *x = CheckpointResponse{} - mi := &file_atelet_proto_msgTypes[51] + mi := &file_atelet_proto_msgTypes[52] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -3596,7 +3643,7 @@ func (x *CheckpointResponse) String() string { func (*CheckpointResponse) ProtoMessage() {} func (x *CheckpointResponse) ProtoReflect() protoreflect.Message { - mi := &file_atelet_proto_msgTypes[51] + mi := &file_atelet_proto_msgTypes[52] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -3609,7 +3656,7 @@ func (x *CheckpointResponse) ProtoReflect() protoreflect.Message { // Deprecated: Use CheckpointResponse.ProtoReflect.Descriptor instead. func (*CheckpointResponse) Descriptor() ([]byte, []int) { - return file_atelet_proto_rawDescGZIP(), []int{51} + return file_atelet_proto_rawDescGZIP(), []int{52} } func (x *CheckpointResponse) GetState() *ActorSnapshotState { @@ -3684,7 +3731,7 @@ type RestoreRequest struct { func (x *RestoreRequest) Reset() { *x = RestoreRequest{} - mi := &file_atelet_proto_msgTypes[52] + mi := &file_atelet_proto_msgTypes[53] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -3696,7 +3743,7 @@ func (x *RestoreRequest) String() string { func (*RestoreRequest) ProtoMessage() {} func (x *RestoreRequest) ProtoReflect() protoreflect.Message { - mi := &file_atelet_proto_msgTypes[52] + mi := &file_atelet_proto_msgTypes[53] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -3709,7 +3756,7 @@ func (x *RestoreRequest) ProtoReflect() protoreflect.Message { // Deprecated: Use RestoreRequest.ProtoReflect.Descriptor instead. func (*RestoreRequest) Descriptor() ([]byte, []int) { - return file_atelet_proto_rawDescGZIP(), []int{52} + return file_atelet_proto_rawDescGZIP(), []int{53} } func (x *RestoreRequest) GetWorkerPodUid() string { @@ -3804,7 +3851,7 @@ type RestoreResponse struct { func (x *RestoreResponse) Reset() { *x = RestoreResponse{} - mi := &file_atelet_proto_msgTypes[53] + mi := &file_atelet_proto_msgTypes[54] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -3816,7 +3863,7 @@ func (x *RestoreResponse) String() string { func (*RestoreResponse) ProtoMessage() {} func (x *RestoreResponse) ProtoReflect() protoreflect.Message { - mi := &file_atelet_proto_msgTypes[53] + mi := &file_atelet_proto_msgTypes[54] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -3829,7 +3876,7 @@ func (x *RestoreResponse) ProtoReflect() protoreflect.Message { // Deprecated: Use RestoreResponse.ProtoReflect.Descriptor instead. func (*RestoreResponse) Descriptor() ([]byte, []int) { - return file_atelet_proto_rawDescGZIP(), []int{53} + return file_atelet_proto_rawDescGZIP(), []int{54} } type PromoteSnapshotRequest struct { @@ -3866,7 +3913,7 @@ type PromoteSnapshotRequest struct { func (x *PromoteSnapshotRequest) Reset() { *x = PromoteSnapshotRequest{} - mi := &file_atelet_proto_msgTypes[54] + mi := &file_atelet_proto_msgTypes[55] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -3878,7 +3925,7 @@ func (x *PromoteSnapshotRequest) String() string { func (*PromoteSnapshotRequest) ProtoMessage() {} func (x *PromoteSnapshotRequest) ProtoReflect() protoreflect.Message { - mi := &file_atelet_proto_msgTypes[54] + mi := &file_atelet_proto_msgTypes[55] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -3891,7 +3938,7 @@ func (x *PromoteSnapshotRequest) ProtoReflect() protoreflect.Message { // Deprecated: Use PromoteSnapshotRequest.ProtoReflect.Descriptor instead. func (*PromoteSnapshotRequest) Descriptor() ([]byte, []int) { - return file_atelet_proto_rawDescGZIP(), []int{54} + return file_atelet_proto_rawDescGZIP(), []int{55} } func (x *PromoteSnapshotRequest) GetAtespace() string { @@ -3947,7 +3994,7 @@ type PromoteSnapshotResponse struct { func (x *PromoteSnapshotResponse) Reset() { *x = PromoteSnapshotResponse{} - mi := &file_atelet_proto_msgTypes[55] + mi := &file_atelet_proto_msgTypes[56] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -3959,7 +4006,7 @@ func (x *PromoteSnapshotResponse) String() string { func (*PromoteSnapshotResponse) ProtoMessage() {} func (x *PromoteSnapshotResponse) ProtoReflect() protoreflect.Message { - mi := &file_atelet_proto_msgTypes[55] + mi := &file_atelet_proto_msgTypes[56] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -3972,7 +4019,7 @@ func (x *PromoteSnapshotResponse) ProtoReflect() protoreflect.Message { // Deprecated: Use PromoteSnapshotResponse.ProtoReflect.Descriptor instead. func (*PromoteSnapshotResponse) Descriptor() ([]byte, []int) { - return file_atelet_proto_rawDescGZIP(), []int{55} + return file_atelet_proto_rawDescGZIP(), []int{56} } func (x *PromoteSnapshotResponse) GetState() *ActorSnapshotState { @@ -4001,7 +4048,7 @@ type DetachActorRequest struct { func (x *DetachActorRequest) Reset() { *x = DetachActorRequest{} - mi := &file_atelet_proto_msgTypes[56] + mi := &file_atelet_proto_msgTypes[57] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -4013,7 +4060,7 @@ func (x *DetachActorRequest) String() string { func (*DetachActorRequest) ProtoMessage() {} func (x *DetachActorRequest) ProtoReflect() protoreflect.Message { - mi := &file_atelet_proto_msgTypes[56] + mi := &file_atelet_proto_msgTypes[57] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -4026,7 +4073,7 @@ func (x *DetachActorRequest) ProtoReflect() protoreflect.Message { // Deprecated: Use DetachActorRequest.ProtoReflect.Descriptor instead. func (*DetachActorRequest) Descriptor() ([]byte, []int) { - return file_atelet_proto_rawDescGZIP(), []int{56} + return file_atelet_proto_rawDescGZIP(), []int{57} } func (x *DetachActorRequest) GetAtespace() string { @@ -4061,7 +4108,7 @@ type DetachActorResponse struct { func (x *DetachActorResponse) Reset() { *x = DetachActorResponse{} - mi := &file_atelet_proto_msgTypes[57] + mi := &file_atelet_proto_msgTypes[58] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -4073,7 +4120,7 @@ func (x *DetachActorResponse) String() string { func (*DetachActorResponse) ProtoMessage() {} func (x *DetachActorResponse) ProtoReflect() protoreflect.Message { - mi := &file_atelet_proto_msgTypes[57] + mi := &file_atelet_proto_msgTypes[58] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -4086,7 +4133,7 @@ func (x *DetachActorResponse) ProtoReflect() protoreflect.Message { // Deprecated: Use DetachActorResponse.ProtoReflect.Descriptor instead. func (*DetachActorResponse) Descriptor() ([]byte, []int) { - return file_atelet_proto_rawDescGZIP(), []int{57} + return file_atelet_proto_rawDescGZIP(), []int{58} } func (x *DetachActorResponse) GetState() *ActorSnapshotState { @@ -4210,7 +4257,8 @@ const file_atelet_proto_rawDesc = "" + "containers\x18\x01 \x03(\v2\x11.atelet.ContainerR\n" + "containers\x12(\n" + "\avolumes\x18\x02 \x03(\v2\x0e.atelet.VolumeR\avolumes\"\x18\n" + - "\x16DurableDirVolumeSource\"\x9b\x03\n" + + "\x16DurableDirVolumeSource\"\x16\n" + + "\x14EmptyDirVolumeSource\"\x9b\x03\n" + "\x14ExternalVolumeSource\x12*\n" + "\x11storage_volume_id\x18\x01 \x01(\tR\x0fstorageVolumeId\x12\x1f\n" + "\vvolume_type\x18\x02 \x01(\tR\n" + @@ -4237,7 +4285,7 @@ const file_atelet_proto_rawDesc = "" + "\x0eactor_metadata\x18\x01 \x01(\v2\x1f.atelet.ActorMetadataDataSourceR\ractorMetadata\x12@\n" + "\ftrust_bundle\x18\x02 \x01(\v2\x1d.atelet.TrustBundleDataSourceR\vtrustBundle\"Y\n" + "\x16SystemInfoVolumeSource\x12?\n" + - "\fdata_sources\x18\x01 \x03(\v2\x1c.atelet.SystemInfoDataSourceR\vdataSources\"\x89\x02\n" + + "\fdata_sources\x18\x01 \x03(\v2\x1c.atelet.SystemInfoDataSourceR\vdataSources\"\xc4\x02\n" + "\x06Volume\x12\x12\n" + "\x04name\x18\x01 \x01(\tR\x04name\x12?\n" + "\vdurable_dir\x18\x02 \x01(\v2\x1e.atelet.DurableDirVolumeSourceR\n" + @@ -4245,7 +4293,8 @@ const file_atelet_proto_rawDesc = "" + "\bexternal\x18\x03 \x01(\v2\x1c.atelet.ExternalVolumeSourceR\bexternal\x12?\n" + "\vsystem_info\x18\x04 \x01(\v2\x1e.atelet.SystemInfoVolumeSourceR\n" + "systemInfo\x12/\n" + - "\x05image\x18\x05 \x01(\v2\x19.atelet.ImageVolumeSourceR\x05image\"@\n" + + "\x05image\x18\x05 \x01(\v2\x19.atelet.ImageVolumeSourceR\x05image\x129\n" + + "\tempty_dir\x18\x06 \x01(\v2\x1c.atelet.EmptyDirVolumeSourceR\bemptyDir\"@\n" + "\vVolumeMount\x12\x12\n" + "\x04name\x18\x01 \x01(\tR\x04name\x12\x1d\n" + "\n" + @@ -4387,7 +4436,7 @@ func file_atelet_proto_rawDescGZIP() []byte { } var file_atelet_proto_enumTypes = make([]protoimpl.EnumInfo, 3) -var file_atelet_proto_msgTypes = make([]protoimpl.MessageInfo, 62) +var file_atelet_proto_msgTypes = make([]protoimpl.MessageInfo, 63) var file_atelet_proto_goTypes = []any{ (ActorMetadataField)(0), // 0: atelet.ActorMetadataField (SnapshotFidelity)(0), // 1: atelet.SnapshotFidelity @@ -4420,41 +4469,42 @@ var file_atelet_proto_goTypes = []any{ (*SandboxAssets)(nil), // 28: atelet.SandboxAssets (*WorkloadSpec)(nil), // 29: atelet.WorkloadSpec (*DurableDirVolumeSource)(nil), // 30: atelet.DurableDirVolumeSource - (*ExternalVolumeSource)(nil), // 31: atelet.ExternalVolumeSource - (*ImageVolumeSource)(nil), // 32: atelet.ImageVolumeSource - (*ActorMetadataItem)(nil), // 33: atelet.ActorMetadataItem - (*ActorMetadataDataSource)(nil), // 34: atelet.ActorMetadataDataSource - (*TrustBundleDataSource)(nil), // 35: atelet.TrustBundleDataSource - (*SystemInfoDataSource)(nil), // 36: atelet.SystemInfoDataSource - (*SystemInfoVolumeSource)(nil), // 37: atelet.SystemInfoVolumeSource - (*Volume)(nil), // 38: atelet.Volume - (*VolumeMount)(nil), // 39: atelet.VolumeMount - (*Container)(nil), // 40: atelet.Container - (*SecurityContext)(nil), // 41: atelet.SecurityContext - (*Capabilities)(nil), // 42: atelet.Capabilities - (*ResourceLimits)(nil), // 43: atelet.ResourceLimits - (*EnvVar)(nil), // 44: atelet.EnvVar - (*WakeupProbe)(nil), // 45: atelet.WakeupProbe - (*HttpGetAction)(nil), // 46: atelet.HttpGetAction - (*RunResponse)(nil), // 47: atelet.RunResponse - (*ObjectStorage)(nil), // 48: atelet.ObjectStorage - (*Snapshot)(nil), // 49: atelet.Snapshot - (*SnapshotReport)(nil), // 50: atelet.SnapshotReport - (*ActorSnapshotState)(nil), // 51: atelet.ActorSnapshotState - (*UploadConfig)(nil), // 52: atelet.UploadConfig - (*CheckpointRequest)(nil), // 53: atelet.CheckpointRequest - (*CheckpointResponse)(nil), // 54: atelet.CheckpointResponse - (*RestoreRequest)(nil), // 55: atelet.RestoreRequest - (*RestoreResponse)(nil), // 56: atelet.RestoreResponse - (*PromoteSnapshotRequest)(nil), // 57: atelet.PromoteSnapshotRequest - (*PromoteSnapshotResponse)(nil), // 58: atelet.PromoteSnapshotResponse - (*DetachActorRequest)(nil), // 59: atelet.DetachActorRequest - (*DetachActorResponse)(nil), // 60: atelet.DetachActorResponse - nil, // 61: atelet.ArchAssets.FilesEntry - nil, // 62: atelet.SandboxAssets.AssetsEntry - nil, // 63: atelet.ExternalVolumeSource.VolumeContextEntry - nil, // 64: atelet.ExternalVolumeSource.PublishContextEntry - (*timestamppb.Timestamp)(nil), // 65: google.protobuf.Timestamp + (*EmptyDirVolumeSource)(nil), // 31: atelet.EmptyDirVolumeSource + (*ExternalVolumeSource)(nil), // 32: atelet.ExternalVolumeSource + (*ImageVolumeSource)(nil), // 33: atelet.ImageVolumeSource + (*ActorMetadataItem)(nil), // 34: atelet.ActorMetadataItem + (*ActorMetadataDataSource)(nil), // 35: atelet.ActorMetadataDataSource + (*TrustBundleDataSource)(nil), // 36: atelet.TrustBundleDataSource + (*SystemInfoDataSource)(nil), // 37: atelet.SystemInfoDataSource + (*SystemInfoVolumeSource)(nil), // 38: atelet.SystemInfoVolumeSource + (*Volume)(nil), // 39: atelet.Volume + (*VolumeMount)(nil), // 40: atelet.VolumeMount + (*Container)(nil), // 41: atelet.Container + (*SecurityContext)(nil), // 42: atelet.SecurityContext + (*Capabilities)(nil), // 43: atelet.Capabilities + (*ResourceLimits)(nil), // 44: atelet.ResourceLimits + (*EnvVar)(nil), // 45: atelet.EnvVar + (*WakeupProbe)(nil), // 46: atelet.WakeupProbe + (*HttpGetAction)(nil), // 47: atelet.HttpGetAction + (*RunResponse)(nil), // 48: atelet.RunResponse + (*ObjectStorage)(nil), // 49: atelet.ObjectStorage + (*Snapshot)(nil), // 50: atelet.Snapshot + (*SnapshotReport)(nil), // 51: atelet.SnapshotReport + (*ActorSnapshotState)(nil), // 52: atelet.ActorSnapshotState + (*UploadConfig)(nil), // 53: atelet.UploadConfig + (*CheckpointRequest)(nil), // 54: atelet.CheckpointRequest + (*CheckpointResponse)(nil), // 55: atelet.CheckpointResponse + (*RestoreRequest)(nil), // 56: atelet.RestoreRequest + (*RestoreResponse)(nil), // 57: atelet.RestoreResponse + (*PromoteSnapshotRequest)(nil), // 58: atelet.PromoteSnapshotRequest + (*PromoteSnapshotResponse)(nil), // 59: atelet.PromoteSnapshotResponse + (*DetachActorRequest)(nil), // 60: atelet.DetachActorRequest + (*DetachActorResponse)(nil), // 61: atelet.DetachActorResponse + nil, // 62: atelet.ArchAssets.FilesEntry + nil, // 63: atelet.SandboxAssets.AssetsEntry + nil, // 64: atelet.ExternalVolumeSource.VolumeContextEntry + nil, // 65: atelet.ExternalVolumeSource.PublishContextEntry + (*timestamppb.Timestamp)(nil), // 66: google.protobuf.Timestamp } var file_atelet_proto_depIdxs = []int32{ 7, // 0: atelet.RegisterWorkerRequest.capacity:type_name -> atelet.WorkerResources @@ -4475,76 +4525,77 @@ var file_atelet_proto_depIdxs = []int32{ 29, // 15: atelet.RunRequest.spec:type_name -> atelet.WorkloadSpec 28, // 16: atelet.RunRequest.sandbox_assets:type_name -> atelet.SandboxAssets 25, // 17: atelet.RunRequest.egress_gateway:type_name -> atelet.EgressGateway - 61, // 18: atelet.ArchAssets.files:type_name -> atelet.ArchAssets.FilesEntry - 62, // 19: atelet.SandboxAssets.assets:type_name -> atelet.SandboxAssets.AssetsEntry - 40, // 20: atelet.WorkloadSpec.containers:type_name -> atelet.Container - 38, // 21: atelet.WorkloadSpec.volumes:type_name -> atelet.Volume - 63, // 22: atelet.ExternalVolumeSource.volume_context:type_name -> atelet.ExternalVolumeSource.VolumeContextEntry - 64, // 23: atelet.ExternalVolumeSource.publish_context:type_name -> atelet.ExternalVolumeSource.PublishContextEntry + 62, // 18: atelet.ArchAssets.files:type_name -> atelet.ArchAssets.FilesEntry + 63, // 19: atelet.SandboxAssets.assets:type_name -> atelet.SandboxAssets.AssetsEntry + 41, // 20: atelet.WorkloadSpec.containers:type_name -> atelet.Container + 39, // 21: atelet.WorkloadSpec.volumes:type_name -> atelet.Volume + 64, // 22: atelet.ExternalVolumeSource.volume_context:type_name -> atelet.ExternalVolumeSource.VolumeContextEntry + 65, // 23: atelet.ExternalVolumeSource.publish_context:type_name -> atelet.ExternalVolumeSource.PublishContextEntry 0, // 24: atelet.ActorMetadataItem.field:type_name -> atelet.ActorMetadataField - 33, // 25: atelet.ActorMetadataDataSource.items:type_name -> atelet.ActorMetadataItem - 34, // 26: atelet.SystemInfoDataSource.actor_metadata:type_name -> atelet.ActorMetadataDataSource - 35, // 27: atelet.SystemInfoDataSource.trust_bundle:type_name -> atelet.TrustBundleDataSource - 36, // 28: atelet.SystemInfoVolumeSource.data_sources:type_name -> atelet.SystemInfoDataSource + 34, // 25: atelet.ActorMetadataDataSource.items:type_name -> atelet.ActorMetadataItem + 35, // 26: atelet.SystemInfoDataSource.actor_metadata:type_name -> atelet.ActorMetadataDataSource + 36, // 27: atelet.SystemInfoDataSource.trust_bundle:type_name -> atelet.TrustBundleDataSource + 37, // 28: atelet.SystemInfoVolumeSource.data_sources:type_name -> atelet.SystemInfoDataSource 30, // 29: atelet.Volume.durable_dir:type_name -> atelet.DurableDirVolumeSource - 31, // 30: atelet.Volume.external:type_name -> atelet.ExternalVolumeSource - 37, // 31: atelet.Volume.system_info:type_name -> atelet.SystemInfoVolumeSource - 32, // 32: atelet.Volume.image:type_name -> atelet.ImageVolumeSource - 44, // 33: atelet.Container.env:type_name -> atelet.EnvVar - 45, // 34: atelet.Container.wakeup_probe:type_name -> atelet.WakeupProbe - 39, // 35: atelet.Container.volume_mounts:type_name -> atelet.VolumeMount - 41, // 36: atelet.Container.security_context:type_name -> atelet.SecurityContext - 43, // 37: atelet.Container.resources:type_name -> atelet.ResourceLimits - 42, // 38: atelet.SecurityContext.capabilities:type_name -> atelet.Capabilities - 46, // 39: atelet.WakeupProbe.http_get:type_name -> atelet.HttpGetAction - 1, // 40: atelet.Snapshot.fidelity:type_name -> atelet.SnapshotFidelity - 48, // 41: atelet.Snapshot.object:type_name -> atelet.ObjectStorage - 49, // 42: atelet.SnapshotReport.snapshot:type_name -> atelet.Snapshot - 65, // 43: atelet.SnapshotReport.local_commit_time:type_name -> google.protobuf.Timestamp - 65, // 44: atelet.SnapshotReport.durable_complete_time:type_name -> google.protobuf.Timestamp - 50, // 45: atelet.ActorSnapshotState.latest_local:type_name -> atelet.SnapshotReport - 50, // 46: atelet.ActorSnapshotState.uploading:type_name -> atelet.SnapshotReport - 50, // 47: atelet.ActorSnapshotState.latest_durable:type_name -> atelet.SnapshotReport - 65, // 48: atelet.ActorSnapshotState.upload_due_time:type_name -> google.protobuf.Timestamp - 2, // 49: atelet.UploadConfig.mode:type_name -> atelet.UploadMode - 29, // 50: atelet.CheckpointRequest.spec:type_name -> atelet.WorkloadSpec - 49, // 51: atelet.CheckpointRequest.snapshot:type_name -> atelet.Snapshot - 52, // 52: atelet.CheckpointRequest.upload_config:type_name -> atelet.UploadConfig - 51, // 53: atelet.CheckpointResponse.state:type_name -> atelet.ActorSnapshotState - 29, // 54: atelet.RestoreRequest.spec:type_name -> atelet.WorkloadSpec - 49, // 55: atelet.RestoreRequest.snapshot:type_name -> atelet.Snapshot - 25, // 56: atelet.RestoreRequest.egress_gateway:type_name -> atelet.EgressGateway - 28, // 57: atelet.RestoreRequest.sandbox_assets:type_name -> atelet.SandboxAssets - 49, // 58: atelet.PromoteSnapshotRequest.snapshot:type_name -> atelet.Snapshot - 51, // 59: atelet.PromoteSnapshotResponse.state:type_name -> atelet.ActorSnapshotState - 51, // 60: atelet.DetachActorResponse.state:type_name -> atelet.ActorSnapshotState - 26, // 61: atelet.ArchAssets.FilesEntry.value:type_name -> atelet.AssetFile - 27, // 62: atelet.SandboxAssets.AssetsEntry.value:type_name -> atelet.ArchAssets - 20, // 63: atelet.WorkerSupport.MintActorCertificate:input_type -> atelet.MintActorCertificateRequest - 3, // 64: atelet.WorkerSupport.RegisterWorker:input_type -> atelet.RegisterWorkerRequest - 11, // 65: atelet.WorkerSupport.RequestActorSuspend:input_type -> atelet.RequestActorSuspendRequest - 13, // 66: atelet.WorkerSupport.GetActorEgressPolicy:input_type -> atelet.GetActorEgressPolicyRequest - 24, // 67: atelet.Atelet.Run:input_type -> atelet.RunRequest - 53, // 68: atelet.Atelet.Checkpoint:input_type -> atelet.CheckpointRequest - 55, // 69: atelet.Atelet.Restore:input_type -> atelet.RestoreRequest - 57, // 70: atelet.Atelet.PromoteSnapshot:input_type -> atelet.PromoteSnapshotRequest - 59, // 71: atelet.Atelet.DetachActor:input_type -> atelet.DetachActorRequest - 22, // 72: atelet.Atelet.Terminate:input_type -> atelet.TerminateRequest - 21, // 73: atelet.WorkerSupport.MintActorCertificate:output_type -> atelet.MintActorCertificateResponse - 10, // 74: atelet.WorkerSupport.RegisterWorker:output_type -> atelet.RegisterWorkerResponse - 12, // 75: atelet.WorkerSupport.RequestActorSuspend:output_type -> atelet.RequestActorSuspendResponse - 14, // 76: atelet.WorkerSupport.GetActorEgressPolicy:output_type -> atelet.GetActorEgressPolicyResponse - 47, // 77: atelet.Atelet.Run:output_type -> atelet.RunResponse - 54, // 78: atelet.Atelet.Checkpoint:output_type -> atelet.CheckpointResponse - 56, // 79: atelet.Atelet.Restore:output_type -> atelet.RestoreResponse - 58, // 80: atelet.Atelet.PromoteSnapshot:output_type -> atelet.PromoteSnapshotResponse - 60, // 81: atelet.Atelet.DetachActor:output_type -> atelet.DetachActorResponse - 23, // 82: atelet.Atelet.Terminate:output_type -> atelet.TerminateResponse - 73, // [73:83] is the sub-list for method output_type - 63, // [63:73] is the sub-list for method input_type - 63, // [63:63] is the sub-list for extension type_name - 63, // [63:63] is the sub-list for extension extendee - 0, // [0:63] is the sub-list for field type_name + 32, // 30: atelet.Volume.external:type_name -> atelet.ExternalVolumeSource + 38, // 31: atelet.Volume.system_info:type_name -> atelet.SystemInfoVolumeSource + 33, // 32: atelet.Volume.image:type_name -> atelet.ImageVolumeSource + 31, // 33: atelet.Volume.empty_dir:type_name -> atelet.EmptyDirVolumeSource + 45, // 34: atelet.Container.env:type_name -> atelet.EnvVar + 46, // 35: atelet.Container.wakeup_probe:type_name -> atelet.WakeupProbe + 40, // 36: atelet.Container.volume_mounts:type_name -> atelet.VolumeMount + 42, // 37: atelet.Container.security_context:type_name -> atelet.SecurityContext + 44, // 38: atelet.Container.resources:type_name -> atelet.ResourceLimits + 43, // 39: atelet.SecurityContext.capabilities:type_name -> atelet.Capabilities + 47, // 40: atelet.WakeupProbe.http_get:type_name -> atelet.HttpGetAction + 1, // 41: atelet.Snapshot.fidelity:type_name -> atelet.SnapshotFidelity + 49, // 42: atelet.Snapshot.object:type_name -> atelet.ObjectStorage + 50, // 43: atelet.SnapshotReport.snapshot:type_name -> atelet.Snapshot + 66, // 44: atelet.SnapshotReport.local_commit_time:type_name -> google.protobuf.Timestamp + 66, // 45: atelet.SnapshotReport.durable_complete_time:type_name -> google.protobuf.Timestamp + 51, // 46: atelet.ActorSnapshotState.latest_local:type_name -> atelet.SnapshotReport + 51, // 47: atelet.ActorSnapshotState.uploading:type_name -> atelet.SnapshotReport + 51, // 48: atelet.ActorSnapshotState.latest_durable:type_name -> atelet.SnapshotReport + 66, // 49: atelet.ActorSnapshotState.upload_due_time:type_name -> google.protobuf.Timestamp + 2, // 50: atelet.UploadConfig.mode:type_name -> atelet.UploadMode + 29, // 51: atelet.CheckpointRequest.spec:type_name -> atelet.WorkloadSpec + 50, // 52: atelet.CheckpointRequest.snapshot:type_name -> atelet.Snapshot + 53, // 53: atelet.CheckpointRequest.upload_config:type_name -> atelet.UploadConfig + 52, // 54: atelet.CheckpointResponse.state:type_name -> atelet.ActorSnapshotState + 29, // 55: atelet.RestoreRequest.spec:type_name -> atelet.WorkloadSpec + 50, // 56: atelet.RestoreRequest.snapshot:type_name -> atelet.Snapshot + 25, // 57: atelet.RestoreRequest.egress_gateway:type_name -> atelet.EgressGateway + 28, // 58: atelet.RestoreRequest.sandbox_assets:type_name -> atelet.SandboxAssets + 50, // 59: atelet.PromoteSnapshotRequest.snapshot:type_name -> atelet.Snapshot + 52, // 60: atelet.PromoteSnapshotResponse.state:type_name -> atelet.ActorSnapshotState + 52, // 61: atelet.DetachActorResponse.state:type_name -> atelet.ActorSnapshotState + 26, // 62: atelet.ArchAssets.FilesEntry.value:type_name -> atelet.AssetFile + 27, // 63: atelet.SandboxAssets.AssetsEntry.value:type_name -> atelet.ArchAssets + 20, // 64: atelet.WorkerSupport.MintActorCertificate:input_type -> atelet.MintActorCertificateRequest + 3, // 65: atelet.WorkerSupport.RegisterWorker:input_type -> atelet.RegisterWorkerRequest + 11, // 66: atelet.WorkerSupport.RequestActorSuspend:input_type -> atelet.RequestActorSuspendRequest + 13, // 67: atelet.WorkerSupport.GetActorEgressPolicy:input_type -> atelet.GetActorEgressPolicyRequest + 24, // 68: atelet.Atelet.Run:input_type -> atelet.RunRequest + 54, // 69: atelet.Atelet.Checkpoint:input_type -> atelet.CheckpointRequest + 56, // 70: atelet.Atelet.Restore:input_type -> atelet.RestoreRequest + 58, // 71: atelet.Atelet.PromoteSnapshot:input_type -> atelet.PromoteSnapshotRequest + 60, // 72: atelet.Atelet.DetachActor:input_type -> atelet.DetachActorRequest + 22, // 73: atelet.Atelet.Terminate:input_type -> atelet.TerminateRequest + 21, // 74: atelet.WorkerSupport.MintActorCertificate:output_type -> atelet.MintActorCertificateResponse + 10, // 75: atelet.WorkerSupport.RegisterWorker:output_type -> atelet.RegisterWorkerResponse + 12, // 76: atelet.WorkerSupport.RequestActorSuspend:output_type -> atelet.RequestActorSuspendResponse + 14, // 77: atelet.WorkerSupport.GetActorEgressPolicy:output_type -> atelet.GetActorEgressPolicyResponse + 48, // 78: atelet.Atelet.Run:output_type -> atelet.RunResponse + 55, // 79: atelet.Atelet.Checkpoint:output_type -> atelet.CheckpointResponse + 57, // 80: atelet.Atelet.Restore:output_type -> atelet.RestoreResponse + 59, // 81: atelet.Atelet.PromoteSnapshot:output_type -> atelet.PromoteSnapshotResponse + 61, // 82: atelet.Atelet.DetachActor:output_type -> atelet.DetachActorResponse + 23, // 83: atelet.Atelet.Terminate:output_type -> atelet.TerminateResponse + 74, // [74:84] is the sub-list for method output_type + 64, // [64:74] is the sub-list for method input_type + 64, // [64:64] is the sub-list for extension type_name + 64, // [64:64] is the sub-list for extension extendee + 0, // [0:64] is the sub-list for field type_name } func init() { file_atelet_proto_init() } @@ -4553,14 +4604,14 @@ func file_atelet_proto_init() { return } file_atelet_proto_msgTypes[21].OneofWrappers = []any{} - file_atelet_proto_msgTypes[52].OneofWrappers = []any{} + file_atelet_proto_msgTypes[53].OneofWrappers = []any{} type x struct{} out := protoimpl.TypeBuilder{ File: protoimpl.DescBuilder{ GoPackagePath: reflect.TypeOf(x{}).PkgPath(), RawDescriptor: unsafe.Slice(unsafe.StringData(file_atelet_proto_rawDesc), len(file_atelet_proto_rawDesc)), NumEnums: 3, - NumMessages: 62, + NumMessages: 63, NumExtensions: 0, NumServices: 2, }, diff --git a/internal/proto/ateletpb/atelet.proto b/internal/proto/ateletpb/atelet.proto index dca175468d..98bb68739f 100644 --- a/internal/proto/ateletpb/atelet.proto +++ b/internal/proto/ateletpb/atelet.proto @@ -547,6 +547,10 @@ message WorkloadSpec { message DurableDirVolumeSource { } +// EmptyDirVolumeSource is an empty host directory at the external volume path. +message EmptyDirVolumeSource { +} + message ExternalVolumeSource { // Required, unlike the actor status counterpart: the control plane only // sends volumes that finished provisioning. @@ -678,7 +682,7 @@ message Volume { // +k8s:format=k8s-short-name string name = 1; - // Exactly one of durable_dir / external / system_info / image must be set. + // Exactly one of durable_dir / external / system_info / image / empty_dir must be set. // // +k8s:optional // +k8s:unionMember @@ -695,6 +699,10 @@ message Volume { // +k8s:optional // +k8s:unionMember ImageVolumeSource image = 5; + + // +k8s:optional + // +k8s:unionMember + EmptyDirVolumeSource empty_dir = 6; } message VolumeMount { diff --git a/internal/resources/resourceref.go b/internal/resources/resourceref.go index 3c27bd12f0..743d0f7d9a 100644 --- a/internal/resources/resourceref.go +++ b/internal/resources/resourceref.go @@ -107,3 +107,20 @@ func TagRefFromTag(t *ateapipb.Tag) TagRef { Name: t.GetMetadata().GetName(), } } + +// SharedVolumeRef identifies a SharedVolume by the (atespace, name). +type SharedVolumeRef = ResourceRef[*ateapipb.SharedVolume] + +// SharedVolumeRefFromObjectRef converts an ObjectRef to a SharedVolumeRef. +func SharedVolumeRefFromObjectRef(ref *ateapipb.ObjectRef) SharedVolumeRef { + return resourceRefFromObjectRef[*ateapipb.SharedVolume](ref) +} + +// SharedVolumeRefFromSharedVolume returns the reference addressing the +// given volume. +func SharedVolumeRefFromSharedVolume(v *ateapipb.SharedVolume) SharedVolumeRef { + return SharedVolumeRef{ + Atespace: v.GetMetadata().GetAtespace(), + Name: v.GetMetadata().GetName(), + } +} diff --git a/pkg/proto/ateapipb/ateapi.pb.go b/pkg/proto/ateapipb/ateapi.pb.go index f2263209df..ea9620ca25 100644 --- a/pkg/proto/ateapipb/ateapi.pb.go +++ b/pkg/proto/ateapipb/ateapi.pb.go @@ -319,6 +319,163 @@ func (ActorState) EnumDescriptor() ([]byte, []int) { return file_ateapi_proto_rawDescGZIP(), []int{4} } +// DeleteTrigger is what removes a SharedVolume and its storage. +// TODO: add an ATESPACE trigger that removes the volume with its atespace. +type DeleteTrigger int32 + +const ( + DeleteTrigger_DELETE_TRIGGER_UNSPECIFIED DeleteTrigger = 0 + // Only an explicit DeleteSharedVolume removes the volume. + DeleteTrigger_DELETE_TRIGGER_MANUAL DeleteTrigger = 1 + // The volume is removed when the last actor referencing it is deleted. + DeleteTrigger_DELETE_TRIGGER_LAST_ACTOR DeleteTrigger = 2 +) + +// Enum value maps for DeleteTrigger. +var ( + DeleteTrigger_name = map[int32]string{ + 0: "DELETE_TRIGGER_UNSPECIFIED", + 1: "DELETE_TRIGGER_MANUAL", + 2: "DELETE_TRIGGER_LAST_ACTOR", + } + DeleteTrigger_value = map[string]int32{ + "DELETE_TRIGGER_UNSPECIFIED": 0, + "DELETE_TRIGGER_MANUAL": 1, + "DELETE_TRIGGER_LAST_ACTOR": 2, + } +) + +func (x DeleteTrigger) Enum() *DeleteTrigger { + p := new(DeleteTrigger) + *p = x + return p +} + +func (x DeleteTrigger) String() string { + return protoimpl.X.EnumStringOf(x.Descriptor(), protoreflect.EnumNumber(x)) +} + +func (DeleteTrigger) Descriptor() protoreflect.EnumDescriptor { + return file_ateapi_proto_enumTypes[5].Descriptor() +} + +func (DeleteTrigger) Type() protoreflect.EnumType { + return &file_ateapi_proto_enumTypes[5] +} + +func (x DeleteTrigger) Number() protoreflect.EnumNumber { + return protoreflect.EnumNumber(x) +} + +// Deprecated: Use DeleteTrigger.Descriptor instead. +func (DeleteTrigger) EnumDescriptor() ([]byte, []int) { + return file_ateapi_proto_rawDescGZIP(), []int{5} +} + +// AccessMode is how many resumed actors may hold the volume at once. +type AccessMode int32 + +const ( + AccessMode_ACCESS_MODE_UNSPECIFIED AccessMode = 0 + // At most one resumed actor holds the volume. + AccessMode_ACCESS_MODE_READ_WRITE_ONCE AccessMode = 1 + // Any number of resumed actors hold the volume, on any node. + AccessMode_ACCESS_MODE_READ_WRITE_MANY AccessMode = 2 +) + +// Enum value maps for AccessMode. +var ( + AccessMode_name = map[int32]string{ + 0: "ACCESS_MODE_UNSPECIFIED", + 1: "ACCESS_MODE_READ_WRITE_ONCE", + 2: "ACCESS_MODE_READ_WRITE_MANY", + } + AccessMode_value = map[string]int32{ + "ACCESS_MODE_UNSPECIFIED": 0, + "ACCESS_MODE_READ_WRITE_ONCE": 1, + "ACCESS_MODE_READ_WRITE_MANY": 2, + } +) + +func (x AccessMode) Enum() *AccessMode { + p := new(AccessMode) + *p = x + return p +} + +func (x AccessMode) String() string { + return protoimpl.X.EnumStringOf(x.Descriptor(), protoreflect.EnumNumber(x)) +} + +func (AccessMode) Descriptor() protoreflect.EnumDescriptor { + return file_ateapi_proto_enumTypes[6].Descriptor() +} + +func (AccessMode) Type() protoreflect.EnumType { + return &file_ateapi_proto_enumTypes[6] +} + +func (x AccessMode) Number() protoreflect.EnumNumber { + return protoreflect.EnumNumber(x) +} + +// Deprecated: Use AccessMode.Descriptor instead. +func (AccessMode) EnumDescriptor() ([]byte, []int) { + return file_ateapi_proto_rawDescGZIP(), []int{6} +} + +// SharedVolumeState is where a SharedVolume is in its own lifecycle. +type SharedVolumeState int32 + +const ( + SharedVolumeState_SHARED_VOLUME_STATE_UNSPECIFIED SharedVolumeState = 0 + // The name is reserved but no volume handle is known yet. + SharedVolumeState_SHARED_VOLUME_STATE_PENDING SharedVolumeState = 1 + // The volume has a handle and can be referenced. + SharedVolumeState_SHARED_VOLUME_STATE_READY SharedVolumeState = 2 +) + +// Enum value maps for SharedVolumeState. +var ( + SharedVolumeState_name = map[int32]string{ + 0: "SHARED_VOLUME_STATE_UNSPECIFIED", + 1: "SHARED_VOLUME_STATE_PENDING", + 2: "SHARED_VOLUME_STATE_READY", + } + SharedVolumeState_value = map[string]int32{ + "SHARED_VOLUME_STATE_UNSPECIFIED": 0, + "SHARED_VOLUME_STATE_PENDING": 1, + "SHARED_VOLUME_STATE_READY": 2, + } +) + +func (x SharedVolumeState) Enum() *SharedVolumeState { + p := new(SharedVolumeState) + *p = x + return p +} + +func (x SharedVolumeState) String() string { + return protoimpl.X.EnumStringOf(x.Descriptor(), protoreflect.EnumNumber(x)) +} + +func (SharedVolumeState) Descriptor() protoreflect.EnumDescriptor { + return file_ateapi_proto_enumTypes[7].Descriptor() +} + +func (SharedVolumeState) Type() protoreflect.EnumType { + return &file_ateapi_proto_enumTypes[7] +} + +func (x SharedVolumeState) Number() protoreflect.EnumNumber { + return protoreflect.EnumNumber(x) +} + +// Deprecated: Use SharedVolumeState.Descriptor instead. +func (SharedVolumeState) EnumDescriptor() ([]byte, []int) { + return file_ateapi_proto_rawDescGZIP(), []int{7} +} + // GoldenSnapshotMode selects whether a template builds a golden snapshot. type GoldenSnapshotMode int32 @@ -353,11 +510,11 @@ func (x GoldenSnapshotMode) String() string { } func (GoldenSnapshotMode) Descriptor() protoreflect.EnumDescriptor { - return file_ateapi_proto_enumTypes[5].Descriptor() + return file_ateapi_proto_enumTypes[8].Descriptor() } func (GoldenSnapshotMode) Type() protoreflect.EnumType { - return &file_ateapi_proto_enumTypes[5] + return &file_ateapi_proto_enumTypes[8] } func (x GoldenSnapshotMode) Number() protoreflect.EnumNumber { @@ -366,7 +523,7 @@ func (x GoldenSnapshotMode) Number() protoreflect.EnumNumber { // Deprecated: Use GoldenSnapshotMode.Descriptor instead. func (GoldenSnapshotMode) EnumDescriptor() ([]byte, []int) { - return file_ateapi_proto_rawDescGZIP(), []int{5} + return file_ateapi_proto_rawDescGZIP(), []int{8} } // ActorMetadataField selects one identity field of the actor. @@ -406,11 +563,11 @@ func (x ActorMetadataField) String() string { } func (ActorMetadataField) Descriptor() protoreflect.EnumDescriptor { - return file_ateapi_proto_enumTypes[6].Descriptor() + return file_ateapi_proto_enumTypes[9].Descriptor() } func (ActorMetadataField) Type() protoreflect.EnumType { - return &file_ateapi_proto_enumTypes[6] + return &file_ateapi_proto_enumTypes[9] } func (x ActorMetadataField) Number() protoreflect.EnumNumber { @@ -419,7 +576,7 @@ func (x ActorMetadataField) Number() protoreflect.EnumNumber { // Deprecated: Use ActorMetadataField.Descriptor instead. func (ActorMetadataField) EnumDescriptor() ([]byte, []int) { - return file_ateapi_proto_rawDescGZIP(), []int{6} + return file_ateapi_proto_rawDescGZIP(), []int{9} } type WorkerState int32 @@ -463,11 +620,11 @@ func (x WorkerState) String() string { } func (WorkerState) Descriptor() protoreflect.EnumDescriptor { - return file_ateapi_proto_enumTypes[7].Descriptor() + return file_ateapi_proto_enumTypes[10].Descriptor() } func (WorkerState) Type() protoreflect.EnumType { - return &file_ateapi_proto_enumTypes[7] + return &file_ateapi_proto_enumTypes[10] } func (x WorkerState) Number() protoreflect.EnumNumber { @@ -476,7 +633,7 @@ func (x WorkerState) Number() protoreflect.EnumNumber { // Deprecated: Use WorkerState.Descriptor instead. func (WorkerState) EnumDescriptor() ([]byte, []int) { - return file_ateapi_proto_rawDescGZIP(), []int{7} + return file_ateapi_proto_rawDescGZIP(), []int{10} } // Scope is whether a top-level resource belongs to an atespace. @@ -516,11 +673,11 @@ func (x Scope) String() string { } func (Scope) Descriptor() protoreflect.EnumDescriptor { - return file_ateapi_proto_enumTypes[8].Descriptor() + return file_ateapi_proto_enumTypes[11].Descriptor() } func (Scope) Type() protoreflect.EnumType { - return &file_ateapi_proto_enumTypes[8] + return &file_ateapi_proto_enumTypes[11] } func (x Scope) Number() protoreflect.EnumNumber { @@ -529,7 +686,7 @@ func (x Scope) Number() protoreflect.EnumNumber { // Deprecated: Use Scope.Descriptor instead. func (Scope) EnumDescriptor() ([]byte, []int) { - return file_ateapi_proto_rawDescGZIP(), []int{8} + return file_ateapi_proto_rawDescGZIP(), []int{11} } // ObjectSnapshot addresses a snapshot held in object storage. @@ -1011,8 +1168,16 @@ type ExternalVolume struct { // +k8s:optional // +k8s:customValidation # at most 40 KiB in total, ten times the CSI limit for map fields VolumeContext map[string]string `protobuf:"bytes,5,rep,name=volume_context,json=volumeContext,proto3" json:"volume_context,omitempty" protobuf_key:"bytes,1,opt,name=key" protobuf_val:"bytes,2,opt,name=value"` - unknownFields protoimpl.UnknownFields - sizeCache protoimpl.SizeCache + // shared_volume_name names the SharedVolume this mount references, in the + // actor's atespace. Empty when the actor owns the volume itself. + // + // +k8s:optional + // +k8s:format=k8s-short-name + // +k8s:update=NoModify + // +k8s:update=NoUnset + SharedVolumeName string `protobuf:"bytes,6,opt,name=shared_volume_name,json=sharedVolumeName,proto3" json:"shared_volume_name,omitempty"` + unknownFields protoimpl.UnknownFields + sizeCache protoimpl.SizeCache } func (x *ExternalVolume) Reset() { @@ -1073,6 +1238,13 @@ func (x *ExternalVolume) GetVolumeContext() map[string]string { return nil } +func (x *ExternalVolume) GetSharedVolumeName() string { + if x != nil { + return x.SharedVolumeName + } + return "" +} + type Actor struct { state protoimpl.MessageState `protogen:"open.v1"` // Common resource metadata: atespace, name, uid, version, timestamps. @@ -1104,9 +1276,19 @@ type Actor struct { // server and ignored on input. It is always present in output. // // +k8s:optional - Status *ActorStatus `protobuf:"bytes,7,opt,name=status,proto3" json:"status,omitempty"` - unknownFields protoimpl.UnknownFields - sizeCache protoimpl.SizeCache + Status *ActorStatus `protobuf:"bytes,7,opt,name=status,proto3" json:"status,omitempty"` + // shared_volume_bindings maps each unnamed external.shared_volume in the + // template, by volume name, to the SharedVolume this actor mounts. Every + // unnamed ref must be bound; a named ref cannot be rebound. Set at creation. + // + // +k8s:optional + // +k8s:maxProperties=32 + // +k8s:eachKey=+k8s:format=k8s-short-name + // +k8s:eachVal=+k8s:format=k8s-short-name + // +k8s:immutable + SharedVolumeBindings map[string]string `protobuf:"bytes,8,rep,name=shared_volume_bindings,json=sharedVolumeBindings,proto3" json:"shared_volume_bindings,omitempty" protobuf_key:"bytes,1,opt,name=key" protobuf_val:"bytes,2,opt,name=value"` + unknownFields protoimpl.UnknownFields + sizeCache protoimpl.SizeCache } func (x *Actor) Reset() { @@ -1174,6 +1356,13 @@ func (x *Actor) GetStatus() *ActorStatus { return nil } +func (x *Actor) GetSharedVolumeBindings() map[string]string { + if x != nil { + return x.SharedVolumeBindings + } + return nil +} + // EgressPolicy is an egress policy resource nested under an Actor. An Actor has // at most one egress policy resource, named "default". type EgressPolicy struct { @@ -2449,33 +2638,99 @@ func (x *Tag) GetSourceActor() *ObjectRef { return nil } -// Atespace is the isolation boundary an Actor is created into. Global-scoped: -// metadata.atespace is always empty; the atespace's identity is metadata.name. -type Atespace struct { +// SharedVolume is an atespace-scoped CSI volume whose lifetime is independent +// of any actor. Identity is (atespace, name); the storage-level ID is derived +// from the record's UID. +type SharedVolume struct { state protoimpl.MessageState `protogen:"open.v1"` - // Common resource metadata: name, uid, version, timestamps. + // Common resource metadata: atespace, name, uid, version, timestamps. // // +k8s:required - // +k8s:beta(since: "0.0")=+k8s:subfield(atespace)=+k8s:forbidden # TODO: get rid of beta prefix - Metadata *ResourceMetadata `protobuf:"bytes,1,opt,name=metadata,proto3" json:"metadata,omitempty"` + // +k8s:subfield(atespace)=+k8s:required + Metadata *ResourceMetadata `protobuf:"bytes,1,opt,name=metadata,proto3" json:"metadata,omitempty"` + // delete_trigger decides what removes the volume. Defaults to LAST_ACTOR when + // provisioned and MANUAL when registered. + // + // +k8s:optional + // +k8s:minimum=1 + // +k8s:maximum=2 # keep this in sync with the DeleteTrigger enum + // +k8s:mutable + DeleteTrigger DeleteTrigger `protobuf:"varint,2,opt,name=delete_trigger,json=deleteTrigger,proto3,enum=ateapi.DeleteTrigger" json:"delete_trigger,omitempty"` + // storage_class_name names the StorageClass to provision from, or documents + // the class of a registered volume. + // + // +k8s:optional + // +k8s:format=k8s-long-name + // +k8s:update=NoModify + // +k8s:update=NoUnset + StorageClassName string `protobuf:"bytes,3,opt,name=storage_class_name,json=storageClassName,proto3" json:"storage_class_name,omitempty"` + // volume_id is the storage system's volume ID. Supplied to register an + // existing volume, otherwise stamped once provisioning completes. Bounded and + // checked for control characters only. + // + // +k8s:optional + // +k8s:maxLength=256 + // +k8s:update=NoModify + // +k8s:update=NoUnset + // +k8s:customValidation # no control characters + VolumeId string `protobuf:"bytes,4,opt,name=volume_id,json=volumeId,proto3" json:"volume_id,omitempty"` + // volume_type is the CSI driver name. Required alongside volume_id when no + // StorageClass is named; otherwise taken from the class's provisioner. + // + // +k8s:optional + // +k8s:maxLength=253 + // +k8s:update=NoModify + // +k8s:update=NoUnset + // +k8s:customValidation + VolumeType string `protobuf:"bytes,5,opt,name=volume_type,json=volumeType,proto3" json:"volume_type,omitempty"` + // volume_context is driver metadata needed by the node plugin for mounting. + // Attachment metadata is per-node and deliberately not recorded here. + // + // +k8s:optional + // +k8s:maxProperties=32 + // +k8s:eachKey=+k8s:maxLength=128 + // +k8s:eachVal=+k8s:maxLength=256 + VolumeContext map[string]string `protobuf:"bytes,6,rep,name=volume_context,json=volumeContext,proto3" json:"volume_context,omitempty" protobuf_key:"bytes,1,opt,name=key" protobuf_val:"bytes,2,opt,name=value"` + // status is set by the control plane; it is ignored on write. + // + // +k8s:optional + Status *SharedVolumeStatus `protobuf:"bytes,7,opt,name=status,proto3" json:"status,omitempty"` + // capacity to provision, as a Kubernetes resource.Quantity string (e.g. + // "10Gi"). Required when provisioning, forbidden alongside volume_id. + // + // +k8s:optional + // +k8s:maxLength=64 + // +k8s:update=NoModify + // +k8s:update=NoUnset + // +k8s:customValidation # must parse as a resource.Quantity + Capacity string `protobuf:"bytes,8,opt,name=capacity,proto3" json:"capacity,omitempty"` + // access_mode is fixed at create and defaults to READ_WRITE_ONCE. + // TODO: accept READ_WRITE_MANY once concurrent holders are supported. + // + // +k8s:optional + // +k8s:minimum=1 + // +k8s:maximum=2 # keep this in sync with the AccessMode enum + // +k8s:update=NoModify + // +k8s:update=NoUnset + AccessMode AccessMode `protobuf:"varint,9,opt,name=access_mode,json=accessMode,proto3,enum=ateapi.AccessMode" json:"access_mode,omitempty"` unknownFields protoimpl.UnknownFields sizeCache protoimpl.SizeCache } -func (x *Atespace) Reset() { - *x = Atespace{} +func (x *SharedVolume) Reset() { + *x = SharedVolume{} mi := &file_ateapi_proto_msgTypes[22] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } -func (x *Atespace) String() string { +func (x *SharedVolume) String() string { return protoimpl.X.MessageStringOf(x) } -func (*Atespace) ProtoMessage() {} +func (*SharedVolume) ProtoMessage() {} -func (x *Atespace) ProtoReflect() protoreflect.Message { +func (x *SharedVolume) ProtoReflect() protoreflect.Message { mi := &file_ateapi_proto_msgTypes[22] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) @@ -2487,53 +2742,109 @@ func (x *Atespace) ProtoReflect() protoreflect.Message { return mi.MessageOf(x) } -// Deprecated: Use Atespace.ProtoReflect.Descriptor instead. -func (*Atespace) Descriptor() ([]byte, []int) { +// Deprecated: Use SharedVolume.ProtoReflect.Descriptor instead. +func (*SharedVolume) Descriptor() ([]byte, []int) { return file_ateapi_proto_rawDescGZIP(), []int{22} } -func (x *Atespace) GetMetadata() *ResourceMetadata { +func (x *SharedVolume) GetMetadata() *ResourceMetadata { if x != nil { return x.Metadata } return nil } -// ObjectRef references a Substrate resource by its atespace and name. -type ObjectRef struct { +func (x *SharedVolume) GetDeleteTrigger() DeleteTrigger { + if x != nil { + return x.DeleteTrigger + } + return DeleteTrigger_DELETE_TRIGGER_UNSPECIFIED +} + +func (x *SharedVolume) GetStorageClassName() string { + if x != nil { + return x.StorageClassName + } + return "" +} + +func (x *SharedVolume) GetVolumeId() string { + if x != nil { + return x.VolumeId + } + return "" +} + +func (x *SharedVolume) GetVolumeType() string { + if x != nil { + return x.VolumeType + } + return "" +} + +func (x *SharedVolume) GetVolumeContext() map[string]string { + if x != nil { + return x.VolumeContext + } + return nil +} + +func (x *SharedVolume) GetStatus() *SharedVolumeStatus { + if x != nil { + return x.Status + } + return nil +} + +func (x *SharedVolume) GetCapacity() string { + if x != nil { + return x.Capacity + } + return "" +} + +func (x *SharedVolume) GetAccessMode() AccessMode { + if x != nil { + return x.AccessMode + } + return AccessMode_ACCESS_MODE_UNSPECIFIED +} + +// SharedVolumeStatus is the part of a SharedVolume the control plane owns. +type SharedVolumeStatus struct { state protoimpl.MessageState `protogen:"open.v1"` - // The atespace of the referenced resource. This field should be empty if the - // target resource is global-scoped, but references to objects in the same - // atespace as the referrer must still populate this field. + // state is where the volume is in its lifecycle. // - // References to atespaced resources should add `+k8s:subfield(atespace)=+k8s:required` - // References to non-atespaced resources should add `+k8s:subfield(atespace)=+k8s:forbidden` // +k8s:optional - // +k8s:format=k8s-short-name - Atespace string `protobuf:"bytes,1,opt,name=atespace,proto3" json:"atespace,omitempty"` - // The name of the referenced resource. This field is required. + // +k8s:minimum=1 + // +k8s:maximum=2 # keep this in sync with the SharedVolumeState enum + State SharedVolumeState `protobuf:"varint,1,opt,name=state,proto3,enum=ateapi.SharedVolumeState" json:"state,omitempty"` + // refs are the actors referencing the volume, resumed or not. + // TODO: move references to their own table before lifting the bound. // - // +k8s:required - // +k8s:format=k8s-short-name - Name string `protobuf:"bytes,2,opt,name=name,proto3" json:"name,omitempty"` + // +k8s:optional + // +k8s:maxItems=256 + // +k8s:listType=map + // +k8s:listMapKey=actor_uid + Refs []*ActorRef `protobuf:"bytes,2,rep,name=refs,proto3" json:"refs,omitempty"` unknownFields protoimpl.UnknownFields sizeCache protoimpl.SizeCache } -func (x *ObjectRef) Reset() { - *x = ObjectRef{} +func (x *SharedVolumeStatus) Reset() { + *x = SharedVolumeStatus{} mi := &file_ateapi_proto_msgTypes[23] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } -func (x *ObjectRef) String() string { +func (x *SharedVolumeStatus) String() string { return protoimpl.X.MessageStringOf(x) } -func (*ObjectRef) ProtoMessage() {} +func (*SharedVolumeStatus) ProtoMessage() {} -func (x *ObjectRef) ProtoReflect() protoreflect.Message { +func (x *SharedVolumeStatus) ProtoReflect() protoreflect.Message { mi := &file_ateapi_proto_msgTypes[23] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) @@ -2545,23 +2856,210 @@ func (x *ObjectRef) ProtoReflect() protoreflect.Message { return mi.MessageOf(x) } -// Deprecated: Use ObjectRef.ProtoReflect.Descriptor instead. -func (*ObjectRef) Descriptor() ([]byte, []int) { +// Deprecated: Use SharedVolumeStatus.ProtoReflect.Descriptor instead. +func (*SharedVolumeStatus) Descriptor() ([]byte, []int) { return file_ateapi_proto_rawDescGZIP(), []int{23} } -func (x *ObjectRef) GetAtespace() string { +func (x *SharedVolumeStatus) GetState() SharedVolumeState { if x != nil { - return x.Atespace + return x.State } - return "" + return SharedVolumeState_SHARED_VOLUME_STATE_UNSPECIFIED } -func (x *ObjectRef) GetName() string { +func (x *SharedVolumeStatus) GetRefs() []*ActorRef { if x != nil { - return x.Name + return x.Refs } - return "" + return nil +} + +// ActorRef is one actor's reference to a SharedVolume. +type ActorRef struct { + state protoimpl.MessageState `protogen:"open.v1"` + // actor_uid identifies the actor, so a recreated namesake does not inherit it. + // + // +k8s:required + // +k8s:maxLength=253 + ActorUid string `protobuf:"bytes,1,opt,name=actor_uid,json=actorUid,proto3" json:"actor_uid,omitempty"` + // actor_name is the actor's name, for readers. + // + // +k8s:required + // +k8s:format=k8s-short-name + ActorName string `protobuf:"bytes,2,opt,name=actor_name,json=actorName,proto3" json:"actor_name,omitempty"` + // active is true while the actor is resumed and holds the volume mounted. + // + // +k8s:optional + Active bool `protobuf:"varint,3,opt,name=active,proto3" json:"active,omitempty"` + unknownFields protoimpl.UnknownFields + sizeCache protoimpl.SizeCache +} + +func (x *ActorRef) Reset() { + *x = ActorRef{} + mi := &file_ateapi_proto_msgTypes[24] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) +} + +func (x *ActorRef) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*ActorRef) ProtoMessage() {} + +func (x *ActorRef) ProtoReflect() protoreflect.Message { + mi := &file_ateapi_proto_msgTypes[24] + 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 ActorRef.ProtoReflect.Descriptor instead. +func (*ActorRef) Descriptor() ([]byte, []int) { + return file_ateapi_proto_rawDescGZIP(), []int{24} +} + +func (x *ActorRef) GetActorUid() string { + if x != nil { + return x.ActorUid + } + return "" +} + +func (x *ActorRef) GetActorName() string { + if x != nil { + return x.ActorName + } + return "" +} + +func (x *ActorRef) GetActive() bool { + if x != nil { + return x.Active + } + return false +} + +// Atespace is the isolation boundary an Actor is created into. Global-scoped: +// metadata.atespace is always empty; the atespace's identity is metadata.name. +type Atespace struct { + state protoimpl.MessageState `protogen:"open.v1"` + // Common resource metadata: name, uid, version, timestamps. + // + // +k8s:required + // +k8s:beta(since: "0.0")=+k8s:subfield(atespace)=+k8s:forbidden # TODO: get rid of beta prefix + Metadata *ResourceMetadata `protobuf:"bytes,1,opt,name=metadata,proto3" json:"metadata,omitempty"` + unknownFields protoimpl.UnknownFields + sizeCache protoimpl.SizeCache +} + +func (x *Atespace) Reset() { + *x = Atespace{} + mi := &file_ateapi_proto_msgTypes[25] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) +} + +func (x *Atespace) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*Atespace) ProtoMessage() {} + +func (x *Atespace) ProtoReflect() protoreflect.Message { + mi := &file_ateapi_proto_msgTypes[25] + 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 Atespace.ProtoReflect.Descriptor instead. +func (*Atespace) Descriptor() ([]byte, []int) { + return file_ateapi_proto_rawDescGZIP(), []int{25} +} + +func (x *Atespace) GetMetadata() *ResourceMetadata { + if x != nil { + return x.Metadata + } + return nil +} + +// ObjectRef references a Substrate resource by its atespace and name. +type ObjectRef struct { + state protoimpl.MessageState `protogen:"open.v1"` + // The atespace of the referenced resource. This field should be empty if the + // target resource is global-scoped, but references to objects in the same + // atespace as the referrer must still populate this field. + // + // References to atespaced resources should add `+k8s:subfield(atespace)=+k8s:required` + // References to non-atespaced resources should add `+k8s:subfield(atespace)=+k8s:forbidden` + // +k8s:optional + // +k8s:format=k8s-short-name + Atespace string `protobuf:"bytes,1,opt,name=atespace,proto3" json:"atespace,omitempty"` + // The name of the referenced resource. This field is required. + // + // +k8s:required + // +k8s:format=k8s-short-name + Name string `protobuf:"bytes,2,opt,name=name,proto3" json:"name,omitempty"` + unknownFields protoimpl.UnknownFields + sizeCache protoimpl.SizeCache +} + +func (x *ObjectRef) Reset() { + *x = ObjectRef{} + mi := &file_ateapi_proto_msgTypes[26] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) +} + +func (x *ObjectRef) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*ObjectRef) ProtoMessage() {} + +func (x *ObjectRef) ProtoReflect() protoreflect.Message { + mi := &file_ateapi_proto_msgTypes[26] + 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 ObjectRef.ProtoReflect.Descriptor instead. +func (*ObjectRef) Descriptor() ([]byte, []int) { + return file_ateapi_proto_rawDescGZIP(), []int{26} +} + +func (x *ObjectRef) GetAtespace() string { + if x != nil { + return x.Atespace + } + return "" +} + +func (x *ObjectRef) GetName() string { + if x != nil { + return x.Name + } + return "" } type ActorTemplate struct { @@ -2607,7 +3105,7 @@ type ActorTemplate struct { func (x *ActorTemplate) Reset() { *x = ActorTemplate{} - mi := &file_ateapi_proto_msgTypes[24] + mi := &file_ateapi_proto_msgTypes[27] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -2619,7 +3117,7 @@ func (x *ActorTemplate) String() string { func (*ActorTemplate) ProtoMessage() {} func (x *ActorTemplate) ProtoReflect() protoreflect.Message { - mi := &file_ateapi_proto_msgTypes[24] + mi := &file_ateapi_proto_msgTypes[27] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -2632,7 +3130,7 @@ func (x *ActorTemplate) ProtoReflect() protoreflect.Message { // Deprecated: Use ActorTemplate.ProtoReflect.Descriptor instead. func (*ActorTemplate) Descriptor() ([]byte, []int) { - return file_ateapi_proto_rawDescGZIP(), []int{24} + return file_ateapi_proto_rawDescGZIP(), []int{27} } func (x *ActorTemplate) GetMetadata() *ResourceMetadata { @@ -2708,7 +3206,7 @@ type Resources struct { func (x *Resources) Reset() { *x = Resources{} - mi := &file_ateapi_proto_msgTypes[25] + mi := &file_ateapi_proto_msgTypes[28] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -2720,7 +3218,7 @@ func (x *Resources) String() string { func (*Resources) ProtoMessage() {} func (x *Resources) ProtoReflect() protoreflect.Message { - mi := &file_ateapi_proto_msgTypes[25] + mi := &file_ateapi_proto_msgTypes[28] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -2733,7 +3231,7 @@ func (x *Resources) ProtoReflect() protoreflect.Message { // Deprecated: Use Resources.ProtoReflect.Descriptor instead. func (*Resources) Descriptor() ([]byte, []int) { - return file_ateapi_proto_rawDescGZIP(), []int{25} + return file_ateapi_proto_rawDescGZIP(), []int{28} } func (x *Resources) GetLimits() []*Limits { @@ -2761,7 +3259,7 @@ type Limits struct { func (x *Limits) Reset() { *x = Limits{} - mi := &file_ateapi_proto_msgTypes[26] + mi := &file_ateapi_proto_msgTypes[29] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -2773,7 +3271,7 @@ func (x *Limits) String() string { func (*Limits) ProtoMessage() {} func (x *Limits) ProtoReflect() protoreflect.Message { - mi := &file_ateapi_proto_msgTypes[26] + mi := &file_ateapi_proto_msgTypes[29] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -2786,7 +3284,7 @@ func (x *Limits) ProtoReflect() protoreflect.Message { // Deprecated: Use Limits.ProtoReflect.Descriptor instead. func (*Limits) Descriptor() ([]byte, []int) { - return file_ateapi_proto_rawDescGZIP(), []int{26} + return file_ateapi_proto_rawDescGZIP(), []int{29} } func (x *Limits) GetName() string { @@ -2827,7 +3325,7 @@ type GoldenSnapshotStatus struct { func (x *GoldenSnapshotStatus) Reset() { *x = GoldenSnapshotStatus{} - mi := &file_ateapi_proto_msgTypes[27] + mi := &file_ateapi_proto_msgTypes[30] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -2839,7 +3337,7 @@ func (x *GoldenSnapshotStatus) String() string { func (*GoldenSnapshotStatus) ProtoMessage() {} func (x *GoldenSnapshotStatus) ProtoReflect() protoreflect.Message { - mi := &file_ateapi_proto_msgTypes[27] + mi := &file_ateapi_proto_msgTypes[30] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -2852,7 +3350,7 @@ func (x *GoldenSnapshotStatus) ProtoReflect() protoreflect.Message { // Deprecated: Use GoldenSnapshotStatus.ProtoReflect.Descriptor instead. func (*GoldenSnapshotStatus) Descriptor() ([]byte, []int) { - return file_ateapi_proto_rawDescGZIP(), []int{27} + return file_ateapi_proto_rawDescGZIP(), []int{30} } func (x *GoldenSnapshotStatus) GetGoldenTag() *ObjectRef { @@ -2886,7 +3384,7 @@ type ActorTemplateStatus struct { func (x *ActorTemplateStatus) Reset() { *x = ActorTemplateStatus{} - mi := &file_ateapi_proto_msgTypes[28] + mi := &file_ateapi_proto_msgTypes[31] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -2898,7 +3396,7 @@ func (x *ActorTemplateStatus) String() string { func (*ActorTemplateStatus) ProtoMessage() {} func (x *ActorTemplateStatus) ProtoReflect() protoreflect.Message { - mi := &file_ateapi_proto_msgTypes[28] + mi := &file_ateapi_proto_msgTypes[31] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -2911,7 +3409,7 @@ func (x *ActorTemplateStatus) ProtoReflect() protoreflect.Message { // Deprecated: Use ActorTemplateStatus.ProtoReflect.Descriptor instead. func (*ActorTemplateStatus) Descriptor() ([]byte, []int) { - return file_ateapi_proto_rawDescGZIP(), []int{28} + return file_ateapi_proto_rawDescGZIP(), []int{31} } func (x *ActorTemplateStatus) GetGoldenSnapshotStatus() *GoldenSnapshotStatus { @@ -2943,7 +3441,7 @@ type SandboxConfig struct { func (x *SandboxConfig) Reset() { *x = SandboxConfig{} - mi := &file_ateapi_proto_msgTypes[29] + mi := &file_ateapi_proto_msgTypes[32] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -2955,7 +3453,7 @@ func (x *SandboxConfig) String() string { func (*SandboxConfig) ProtoMessage() {} func (x *SandboxConfig) ProtoReflect() protoreflect.Message { - mi := &file_ateapi_proto_msgTypes[29] + mi := &file_ateapi_proto_msgTypes[32] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -2968,7 +3466,7 @@ func (x *SandboxConfig) ProtoReflect() protoreflect.Message { // Deprecated: Use SandboxConfig.ProtoReflect.Descriptor instead. func (*SandboxConfig) Descriptor() ([]byte, []int) { - return file_ateapi_proto_rawDescGZIP(), []int{29} + return file_ateapi_proto_rawDescGZIP(), []int{32} } func (x *SandboxConfig) GetSandboxClass() string { @@ -3021,7 +3519,7 @@ type SnapshotConfig struct { func (x *SnapshotConfig) Reset() { *x = SnapshotConfig{} - mi := &file_ateapi_proto_msgTypes[30] + mi := &file_ateapi_proto_msgTypes[33] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -3033,7 +3531,7 @@ func (x *SnapshotConfig) String() string { func (*SnapshotConfig) ProtoMessage() {} func (x *SnapshotConfig) ProtoReflect() protoreflect.Message { - mi := &file_ateapi_proto_msgTypes[30] + mi := &file_ateapi_proto_msgTypes[33] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -3046,7 +3544,7 @@ func (x *SnapshotConfig) ProtoReflect() protoreflect.Message { // Deprecated: Use SnapshotConfig.ProtoReflect.Descriptor instead. func (*SnapshotConfig) Descriptor() ([]byte, []int) { - return file_ateapi_proto_rawDescGZIP(), []int{30} + return file_ateapi_proto_rawDescGZIP(), []int{33} } func (x *SnapshotConfig) GetStorageLocation() string { @@ -3086,7 +3584,7 @@ type GoldenSnapshotConfig struct { func (x *GoldenSnapshotConfig) Reset() { *x = GoldenSnapshotConfig{} - mi := &file_ateapi_proto_msgTypes[31] + mi := &file_ateapi_proto_msgTypes[34] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -3098,7 +3596,7 @@ func (x *GoldenSnapshotConfig) String() string { func (*GoldenSnapshotConfig) ProtoMessage() {} func (x *GoldenSnapshotConfig) ProtoReflect() protoreflect.Message { - mi := &file_ateapi_proto_msgTypes[31] + mi := &file_ateapi_proto_msgTypes[34] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -3111,7 +3609,7 @@ func (x *GoldenSnapshotConfig) ProtoReflect() protoreflect.Message { // Deprecated: Use GoldenSnapshotConfig.ProtoReflect.Descriptor instead. func (*GoldenSnapshotConfig) Descriptor() ([]byte, []int) { - return file_ateapi_proto_rawDescGZIP(), []int{31} + return file_ateapi_proto_rawDescGZIP(), []int{34} } func (x *GoldenSnapshotConfig) GetMode() GoldenSnapshotMode { @@ -3191,7 +3689,7 @@ type Container struct { func (x *Container) Reset() { *x = Container{} - mi := &file_ateapi_proto_msgTypes[32] + mi := &file_ateapi_proto_msgTypes[35] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -3203,7 +3701,7 @@ func (x *Container) String() string { func (*Container) ProtoMessage() {} func (x *Container) ProtoReflect() protoreflect.Message { - mi := &file_ateapi_proto_msgTypes[32] + mi := &file_ateapi_proto_msgTypes[35] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -3216,7 +3714,7 @@ func (x *Container) ProtoReflect() protoreflect.Message { // Deprecated: Use Container.ProtoReflect.Descriptor instead. func (*Container) Descriptor() ([]byte, []int) { - return file_ateapi_proto_rawDescGZIP(), []int{32} + return file_ateapi_proto_rawDescGZIP(), []int{35} } func (x *Container) GetName() string { @@ -3296,7 +3794,7 @@ type SecurityContext struct { func (x *SecurityContext) Reset() { *x = SecurityContext{} - mi := &file_ateapi_proto_msgTypes[33] + mi := &file_ateapi_proto_msgTypes[36] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -3308,7 +3806,7 @@ func (x *SecurityContext) String() string { func (*SecurityContext) ProtoMessage() {} func (x *SecurityContext) ProtoReflect() protoreflect.Message { - mi := &file_ateapi_proto_msgTypes[33] + mi := &file_ateapi_proto_msgTypes[36] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -3321,7 +3819,7 @@ func (x *SecurityContext) ProtoReflect() protoreflect.Message { // Deprecated: Use SecurityContext.ProtoReflect.Descriptor instead. func (*SecurityContext) Descriptor() ([]byte, []int) { - return file_ateapi_proto_rawDescGZIP(), []int{33} + return file_ateapi_proto_rawDescGZIP(), []int{36} } func (x *SecurityContext) GetCapabilities() *Capabilities { @@ -3359,7 +3857,7 @@ type Capabilities struct { func (x *Capabilities) Reset() { *x = Capabilities{} - mi := &file_ateapi_proto_msgTypes[34] + mi := &file_ateapi_proto_msgTypes[37] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -3371,7 +3869,7 @@ func (x *Capabilities) String() string { func (*Capabilities) ProtoMessage() {} func (x *Capabilities) ProtoReflect() protoreflect.Message { - mi := &file_ateapi_proto_msgTypes[34] + mi := &file_ateapi_proto_msgTypes[37] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -3384,7 +3882,7 @@ func (x *Capabilities) ProtoReflect() protoreflect.Message { // Deprecated: Use Capabilities.ProtoReflect.Descriptor instead. func (*Capabilities) Descriptor() ([]byte, []int) { - return file_ateapi_proto_rawDescGZIP(), []int{34} + return file_ateapi_proto_rawDescGZIP(), []int{37} } func (x *Capabilities) GetAdd() []string { @@ -3422,7 +3920,7 @@ type EnvVar struct { func (x *EnvVar) Reset() { *x = EnvVar{} - mi := &file_ateapi_proto_msgTypes[35] + mi := &file_ateapi_proto_msgTypes[38] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -3434,7 +3932,7 @@ func (x *EnvVar) String() string { func (*EnvVar) ProtoMessage() {} func (x *EnvVar) ProtoReflect() protoreflect.Message { - mi := &file_ateapi_proto_msgTypes[35] + mi := &file_ateapi_proto_msgTypes[38] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -3447,7 +3945,7 @@ func (x *EnvVar) ProtoReflect() protoreflect.Message { // Deprecated: Use EnvVar.ProtoReflect.Descriptor instead. func (*EnvVar) Descriptor() ([]byte, []int) { - return file_ateapi_proto_rawDescGZIP(), []int{35} + return file_ateapi_proto_rawDescGZIP(), []int{38} } func (x *EnvVar) GetName() string { @@ -3487,7 +3985,7 @@ type WakeupProbe struct { func (x *WakeupProbe) Reset() { *x = WakeupProbe{} - mi := &file_ateapi_proto_msgTypes[36] + mi := &file_ateapi_proto_msgTypes[39] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -3499,7 +3997,7 @@ func (x *WakeupProbe) String() string { func (*WakeupProbe) ProtoMessage() {} func (x *WakeupProbe) ProtoReflect() protoreflect.Message { - mi := &file_ateapi_proto_msgTypes[36] + mi := &file_ateapi_proto_msgTypes[39] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -3512,7 +4010,7 @@ func (x *WakeupProbe) ProtoReflect() protoreflect.Message { // Deprecated: Use WakeupProbe.ProtoReflect.Descriptor instead. func (*WakeupProbe) Descriptor() ([]byte, []int) { - return file_ateapi_proto_rawDescGZIP(), []int{36} + return file_ateapi_proto_rawDescGZIP(), []int{39} } func (x *WakeupProbe) GetHttpGet() *HttpGetAction { @@ -3552,7 +4050,7 @@ type HttpGetAction struct { func (x *HttpGetAction) Reset() { *x = HttpGetAction{} - mi := &file_ateapi_proto_msgTypes[37] + mi := &file_ateapi_proto_msgTypes[40] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -3564,7 +4062,7 @@ func (x *HttpGetAction) String() string { func (*HttpGetAction) ProtoMessage() {} func (x *HttpGetAction) ProtoReflect() protoreflect.Message { - mi := &file_ateapi_proto_msgTypes[37] + mi := &file_ateapi_proto_msgTypes[40] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -3577,7 +4075,7 @@ func (x *HttpGetAction) ProtoReflect() protoreflect.Message { // Deprecated: Use HttpGetAction.ProtoReflect.Descriptor instead. func (*HttpGetAction) Descriptor() ([]byte, []int) { - return file_ateapi_proto_rawDescGZIP(), []int{37} + return file_ateapi_proto_rawDescGZIP(), []int{40} } func (x *HttpGetAction) GetPath() string { @@ -3601,17 +4099,19 @@ type Volume struct { // +k8s:required // +k8s:format=k8s-short-name Name string `protobuf:"bytes,1,opt,name=name,proto3" json:"name,omitempty"` - // Exactly one of durable_dir / external_volume_template / image / - // system_info must be set. + // Exactly one of durable_dir / external / image / system_info must be + // set. // // +k8s:optional // +k8s:unionMember DurableDir *DurableDirVolumeSource `protobuf:"bytes,2,opt,name=durable_dir,json=durableDir,proto3" json:"durable_dir,omitempty"` + // external is a CSI volume: provisioned for this actor from a template. + // // +ate:preview=ExternalVolumes // +k8s:ifDisabled(ExternalVolumes)=+k8s:forbidden // +k8s:optional // +k8s:unionMember - ExternalVolumeTemplate *ExternalVolumeTemplate `protobuf:"bytes,3,opt,name=external_volume_template,json=externalVolumeTemplate,proto3" json:"external_volume_template,omitempty"` + External *ExternalVolumeSource `protobuf:"bytes,7,opt,name=external,proto3" json:"external,omitempty"` // +k8s:optional // +k8s:unionMember SystemInfo *SystemInfoVolumeSource `protobuf:"bytes,5,opt,name=system_info,json=systemInfo,proto3" json:"system_info,omitempty"` @@ -3626,7 +4126,7 @@ type Volume struct { func (x *Volume) Reset() { *x = Volume{} - mi := &file_ateapi_proto_msgTypes[38] + mi := &file_ateapi_proto_msgTypes[41] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -3638,7 +4138,7 @@ func (x *Volume) String() string { func (*Volume) ProtoMessage() {} func (x *Volume) ProtoReflect() protoreflect.Message { - mi := &file_ateapi_proto_msgTypes[38] + mi := &file_ateapi_proto_msgTypes[41] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -3651,7 +4151,7 @@ func (x *Volume) ProtoReflect() protoreflect.Message { // Deprecated: Use Volume.ProtoReflect.Descriptor instead. func (*Volume) Descriptor() ([]byte, []int) { - return file_ateapi_proto_rawDescGZIP(), []int{38} + return file_ateapi_proto_rawDescGZIP(), []int{41} } func (x *Volume) GetName() string { @@ -3668,9 +4168,9 @@ func (x *Volume) GetDurableDir() *DurableDirVolumeSource { return nil } -func (x *Volume) GetExternalVolumeTemplate() *ExternalVolumeTemplate { +func (x *Volume) GetExternal() *ExternalVolumeSource { if x != nil { - return x.ExternalVolumeTemplate + return x.External } return nil } @@ -3689,6 +4189,68 @@ func (x *Volume) GetImage() *ImageVolumeSource { return nil } +// ExternalVolumeSource is a CSI volume provisioned for the actor from a template. +type ExternalVolumeSource struct { + state protoimpl.MessageState `protogen:"open.v1"` + // template provisions a volume for this actor alone; it is deleted with the + // actor. + // + // +k8s:optional + // +k8s:unionMember + Template *ExternalVolumeTemplate `protobuf:"bytes,1,opt,name=template,proto3" json:"template,omitempty"` + // shared_volume mounts a SharedVolume that already exists. + // + // +k8s:optional + // +k8s:unionMember + SharedVolume *SharedVolumeRef `protobuf:"bytes,2,opt,name=shared_volume,json=sharedVolume,proto3" json:"shared_volume,omitempty"` + unknownFields protoimpl.UnknownFields + sizeCache protoimpl.SizeCache +} + +func (x *ExternalVolumeSource) Reset() { + *x = ExternalVolumeSource{} + mi := &file_ateapi_proto_msgTypes[42] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) +} + +func (x *ExternalVolumeSource) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*ExternalVolumeSource) ProtoMessage() {} + +func (x *ExternalVolumeSource) ProtoReflect() protoreflect.Message { + mi := &file_ateapi_proto_msgTypes[42] + 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 ExternalVolumeSource.ProtoReflect.Descriptor instead. +func (*ExternalVolumeSource) Descriptor() ([]byte, []int) { + return file_ateapi_proto_rawDescGZIP(), []int{42} +} + +func (x *ExternalVolumeSource) GetTemplate() *ExternalVolumeTemplate { + if x != nil { + return x.Template + } + return nil +} + +func (x *ExternalVolumeSource) GetSharedVolume() *SharedVolumeRef { + if x != nil { + return x.SharedVolume + } + return nil +} + // ImageVolumeSource mounts the contents of an OCI image, read-only. The // reference must include a digest: changing the image invalidates // snapshots. @@ -3707,7 +4269,7 @@ type ImageVolumeSource struct { func (x *ImageVolumeSource) Reset() { *x = ImageVolumeSource{} - mi := &file_ateapi_proto_msgTypes[39] + mi := &file_ateapi_proto_msgTypes[43] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -3719,7 +4281,7 @@ func (x *ImageVolumeSource) String() string { func (*ImageVolumeSource) ProtoMessage() {} func (x *ImageVolumeSource) ProtoReflect() protoreflect.Message { - mi := &file_ateapi_proto_msgTypes[39] + mi := &file_ateapi_proto_msgTypes[43] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -3732,7 +4294,7 @@ func (x *ImageVolumeSource) ProtoReflect() protoreflect.Message { // Deprecated: Use ImageVolumeSource.ProtoReflect.Descriptor instead. func (*ImageVolumeSource) Descriptor() ([]byte, []int) { - return file_ateapi_proto_rawDescGZIP(), []int{39} + return file_ateapi_proto_rawDescGZIP(), []int{43} } func (x *ImageVolumeSource) GetReference() string { @@ -3752,7 +4314,7 @@ type DurableDirVolumeSource struct { func (x *DurableDirVolumeSource) Reset() { *x = DurableDirVolumeSource{} - mi := &file_ateapi_proto_msgTypes[40] + mi := &file_ateapi_proto_msgTypes[44] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -3764,7 +4326,7 @@ func (x *DurableDirVolumeSource) String() string { func (*DurableDirVolumeSource) ProtoMessage() {} func (x *DurableDirVolumeSource) ProtoReflect() protoreflect.Message { - mi := &file_ateapi_proto_msgTypes[40] + mi := &file_ateapi_proto_msgTypes[44] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -3777,7 +4339,7 @@ func (x *DurableDirVolumeSource) ProtoReflect() protoreflect.Message { // Deprecated: Use DurableDirVolumeSource.ProtoReflect.Descriptor instead. func (*DurableDirVolumeSource) Descriptor() ([]byte, []int) { - return file_ateapi_proto_rawDescGZIP(), []int{40} + return file_ateapi_proto_rawDescGZIP(), []int{44} } // ExternalVolumeTemplate provisions an external volume per actor; the volume @@ -3803,7 +4365,7 @@ type ExternalVolumeTemplate struct { func (x *ExternalVolumeTemplate) Reset() { *x = ExternalVolumeTemplate{} - mi := &file_ateapi_proto_msgTypes[41] + mi := &file_ateapi_proto_msgTypes[45] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -3815,7 +4377,7 @@ func (x *ExternalVolumeTemplate) String() string { func (*ExternalVolumeTemplate) ProtoMessage() {} func (x *ExternalVolumeTemplate) ProtoReflect() protoreflect.Message { - mi := &file_ateapi_proto_msgTypes[41] + mi := &file_ateapi_proto_msgTypes[45] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -3828,7 +4390,7 @@ func (x *ExternalVolumeTemplate) ProtoReflect() protoreflect.Message { // Deprecated: Use ExternalVolumeTemplate.ProtoReflect.Descriptor instead. func (*ExternalVolumeTemplate) Descriptor() ([]byte, []int) { - return file_ateapi_proto_rawDescGZIP(), []int{41} + return file_ateapi_proto_rawDescGZIP(), []int{45} } func (x *ExternalVolumeTemplate) GetCapacity() string { @@ -3845,6 +4407,61 @@ func (x *ExternalVolumeTemplate) GetStorageClassName() string { return "" } +// SharedVolumeRef mounts a SharedVolume in the actor's atespace. The +// volume outlives the actor, so several actors can mount it in turn. +// +// Actors created from a template with a SharedVolumeRef do not start from +// the template's golden snapshot: the first resume boots from the image, and +// later resumes restore the actor's own snapshot. +type SharedVolumeRef struct { + state protoimpl.MessageState `protogen:"open.v1"` + // name of the SharedVolume. Left empty, the actor names it at creation in + // Actor.shared_volume_bindings. + // + // +k8s:optional + // +k8s:format=k8s-short-name + Name string `protobuf:"bytes,1,opt,name=name,proto3" json:"name,omitempty"` + unknownFields protoimpl.UnknownFields + sizeCache protoimpl.SizeCache +} + +func (x *SharedVolumeRef) Reset() { + *x = SharedVolumeRef{} + mi := &file_ateapi_proto_msgTypes[46] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) +} + +func (x *SharedVolumeRef) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*SharedVolumeRef) ProtoMessage() {} + +func (x *SharedVolumeRef) ProtoReflect() protoreflect.Message { + mi := &file_ateapi_proto_msgTypes[46] + 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 SharedVolumeRef.ProtoReflect.Descriptor instead. +func (*SharedVolumeRef) Descriptor() ([]byte, []int) { + return file_ateapi_proto_rawDescGZIP(), []int{46} +} + +func (x *SharedVolumeRef) GetName() string { + if x != nil { + return x.Name + } + return "" +} + // SystemInfoVolumeSource is a read-only volume of substrate-generated // per-actor files (identity fields, projected trust bundles), regenerated by // atelet on every Run/Restore. @@ -3865,7 +4482,7 @@ type SystemInfoVolumeSource struct { func (x *SystemInfoVolumeSource) Reset() { *x = SystemInfoVolumeSource{} - mi := &file_ateapi_proto_msgTypes[42] + mi := &file_ateapi_proto_msgTypes[47] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -3877,7 +4494,7 @@ func (x *SystemInfoVolumeSource) String() string { func (*SystemInfoVolumeSource) ProtoMessage() {} func (x *SystemInfoVolumeSource) ProtoReflect() protoreflect.Message { - mi := &file_ateapi_proto_msgTypes[42] + mi := &file_ateapi_proto_msgTypes[47] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -3890,7 +4507,7 @@ func (x *SystemInfoVolumeSource) ProtoReflect() protoreflect.Message { // Deprecated: Use SystemInfoVolumeSource.ProtoReflect.Descriptor instead. func (*SystemInfoVolumeSource) Descriptor() ([]byte, []int) { - return file_ateapi_proto_rawDescGZIP(), []int{42} + return file_ateapi_proto_rawDescGZIP(), []int{47} } func (x *SystemInfoVolumeSource) GetDataSources() []*SystemInfoDataSource { @@ -3916,7 +4533,7 @@ type SystemInfoDataSource struct { func (x *SystemInfoDataSource) Reset() { *x = SystemInfoDataSource{} - mi := &file_ateapi_proto_msgTypes[43] + mi := &file_ateapi_proto_msgTypes[48] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -3928,7 +4545,7 @@ func (x *SystemInfoDataSource) String() string { func (*SystemInfoDataSource) ProtoMessage() {} func (x *SystemInfoDataSource) ProtoReflect() protoreflect.Message { - mi := &file_ateapi_proto_msgTypes[43] + mi := &file_ateapi_proto_msgTypes[48] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -3941,7 +4558,7 @@ func (x *SystemInfoDataSource) ProtoReflect() protoreflect.Message { // Deprecated: Use SystemInfoDataSource.ProtoReflect.Descriptor instead. func (*SystemInfoDataSource) Descriptor() ([]byte, []int) { - return file_ateapi_proto_rawDescGZIP(), []int{43} + return file_ateapi_proto_rawDescGZIP(), []int{48} } func (x *SystemInfoDataSource) GetActorMetadata() *ActorMetadataDataSource { @@ -3978,7 +4595,7 @@ type ActorMetadataDataSource struct { func (x *ActorMetadataDataSource) Reset() { *x = ActorMetadataDataSource{} - mi := &file_ateapi_proto_msgTypes[44] + mi := &file_ateapi_proto_msgTypes[49] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -3990,7 +4607,7 @@ func (x *ActorMetadataDataSource) String() string { func (*ActorMetadataDataSource) ProtoMessage() {} func (x *ActorMetadataDataSource) ProtoReflect() protoreflect.Message { - mi := &file_ateapi_proto_msgTypes[44] + mi := &file_ateapi_proto_msgTypes[49] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -4003,7 +4620,7 @@ func (x *ActorMetadataDataSource) ProtoReflect() protoreflect.Message { // Deprecated: Use ActorMetadataDataSource.ProtoReflect.Descriptor instead. func (*ActorMetadataDataSource) Descriptor() ([]byte, []int) { - return file_ateapi_proto_rawDescGZIP(), []int{44} + return file_ateapi_proto_rawDescGZIP(), []int{49} } func (x *ActorMetadataDataSource) GetItems() []*ActorMetadataItem { @@ -4035,7 +4652,7 @@ type ActorMetadataItem struct { func (x *ActorMetadataItem) Reset() { *x = ActorMetadataItem{} - mi := &file_ateapi_proto_msgTypes[45] + mi := &file_ateapi_proto_msgTypes[50] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -4047,7 +4664,7 @@ func (x *ActorMetadataItem) String() string { func (*ActorMetadataItem) ProtoMessage() {} func (x *ActorMetadataItem) ProtoReflect() protoreflect.Message { - mi := &file_ateapi_proto_msgTypes[45] + mi := &file_ateapi_proto_msgTypes[50] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -4060,7 +4677,7 @@ func (x *ActorMetadataItem) ProtoReflect() protoreflect.Message { // Deprecated: Use ActorMetadataItem.ProtoReflect.Descriptor instead. func (*ActorMetadataItem) Descriptor() ([]byte, []int) { - return file_ateapi_proto_rawDescGZIP(), []int{45} + return file_ateapi_proto_rawDescGZIP(), []int{50} } func (x *ActorMetadataItem) GetField() ActorMetadataField { @@ -4112,7 +4729,7 @@ type TrustBundleDataSource struct { func (x *TrustBundleDataSource) Reset() { *x = TrustBundleDataSource{} - mi := &file_ateapi_proto_msgTypes[46] + mi := &file_ateapi_proto_msgTypes[51] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -4124,7 +4741,7 @@ func (x *TrustBundleDataSource) String() string { func (*TrustBundleDataSource) ProtoMessage() {} func (x *TrustBundleDataSource) ProtoReflect() protoreflect.Message { - mi := &file_ateapi_proto_msgTypes[46] + mi := &file_ateapi_proto_msgTypes[51] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -4137,7 +4754,7 @@ func (x *TrustBundleDataSource) ProtoReflect() protoreflect.Message { // Deprecated: Use TrustBundleDataSource.ProtoReflect.Descriptor instead. func (*TrustBundleDataSource) Descriptor() ([]byte, []int) { - return file_ateapi_proto_rawDescGZIP(), []int{46} + return file_ateapi_proto_rawDescGZIP(), []int{51} } func (x *TrustBundleDataSource) GetNames() []string { @@ -4176,7 +4793,7 @@ type VolumeMount struct { func (x *VolumeMount) Reset() { *x = VolumeMount{} - mi := &file_ateapi_proto_msgTypes[47] + mi := &file_ateapi_proto_msgTypes[52] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -4188,7 +4805,7 @@ func (x *VolumeMount) String() string { func (*VolumeMount) ProtoMessage() {} func (x *VolumeMount) ProtoReflect() protoreflect.Message { - mi := &file_ateapi_proto_msgTypes[47] + mi := &file_ateapi_proto_msgTypes[52] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -4201,7 +4818,7 @@ func (x *VolumeMount) ProtoReflect() protoreflect.Message { // Deprecated: Use VolumeMount.ProtoReflect.Descriptor instead. func (*VolumeMount) Descriptor() ([]byte, []int) { - return file_ateapi_proto_rawDescGZIP(), []int{47} + return file_ateapi_proto_rawDescGZIP(), []int{52} } func (x *VolumeMount) GetName() string { @@ -4230,7 +4847,7 @@ type CreateAtespaceRequest struct { func (x *CreateAtespaceRequest) Reset() { *x = CreateAtespaceRequest{} - mi := &file_ateapi_proto_msgTypes[48] + mi := &file_ateapi_proto_msgTypes[53] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -4242,7 +4859,7 @@ func (x *CreateAtespaceRequest) String() string { func (*CreateAtespaceRequest) ProtoMessage() {} func (x *CreateAtespaceRequest) ProtoReflect() protoreflect.Message { - mi := &file_ateapi_proto_msgTypes[48] + mi := &file_ateapi_proto_msgTypes[53] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -4255,7 +4872,7 @@ func (x *CreateAtespaceRequest) ProtoReflect() protoreflect.Message { // Deprecated: Use CreateAtespaceRequest.ProtoReflect.Descriptor instead. func (*CreateAtespaceRequest) Descriptor() ([]byte, []int) { - return file_ateapi_proto_rawDescGZIP(), []int{48} + return file_ateapi_proto_rawDescGZIP(), []int{53} } func (x *CreateAtespaceRequest) GetAtespace() *Atespace { @@ -4276,7 +4893,7 @@ type GetAtespaceRequest struct { func (x *GetAtespaceRequest) Reset() { *x = GetAtespaceRequest{} - mi := &file_ateapi_proto_msgTypes[49] + mi := &file_ateapi_proto_msgTypes[54] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -4288,7 +4905,7 @@ func (x *GetAtespaceRequest) String() string { func (*GetAtespaceRequest) ProtoMessage() {} func (x *GetAtespaceRequest) ProtoReflect() protoreflect.Message { - mi := &file_ateapi_proto_msgTypes[49] + mi := &file_ateapi_proto_msgTypes[54] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -4301,7 +4918,7 @@ func (x *GetAtespaceRequest) ProtoReflect() protoreflect.Message { // Deprecated: Use GetAtespaceRequest.ProtoReflect.Descriptor instead. func (*GetAtespaceRequest) Descriptor() ([]byte, []int) { - return file_ateapi_proto_rawDescGZIP(), []int{49} + return file_ateapi_proto_rawDescGZIP(), []int{54} } func (x *GetAtespaceRequest) GetAtespace() *ObjectRef { @@ -4323,7 +4940,7 @@ type ListAtespacesRequest struct { func (x *ListAtespacesRequest) Reset() { *x = ListAtespacesRequest{} - mi := &file_ateapi_proto_msgTypes[50] + mi := &file_ateapi_proto_msgTypes[55] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -4335,7 +4952,7 @@ func (x *ListAtespacesRequest) String() string { func (*ListAtespacesRequest) ProtoMessage() {} func (x *ListAtespacesRequest) ProtoReflect() protoreflect.Message { - mi := &file_ateapi_proto_msgTypes[50] + mi := &file_ateapi_proto_msgTypes[55] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -4348,7 +4965,7 @@ func (x *ListAtespacesRequest) ProtoReflect() protoreflect.Message { // Deprecated: Use ListAtespacesRequest.ProtoReflect.Descriptor instead. func (*ListAtespacesRequest) Descriptor() ([]byte, []int) { - return file_ateapi_proto_rawDescGZIP(), []int{50} + return file_ateapi_proto_rawDescGZIP(), []int{55} } func (x *ListAtespacesRequest) GetOptions() *ListOptions { @@ -4370,7 +4987,7 @@ type ListAtespacesResponse struct { func (x *ListAtespacesResponse) Reset() { *x = ListAtespacesResponse{} - mi := &file_ateapi_proto_msgTypes[51] + mi := &file_ateapi_proto_msgTypes[56] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -4382,7 +4999,7 @@ func (x *ListAtespacesResponse) String() string { func (*ListAtespacesResponse) ProtoMessage() {} func (x *ListAtespacesResponse) ProtoReflect() protoreflect.Message { - mi := &file_ateapi_proto_msgTypes[51] + mi := &file_ateapi_proto_msgTypes[56] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -4395,7 +5012,7 @@ func (x *ListAtespacesResponse) ProtoReflect() protoreflect.Message { // Deprecated: Use ListAtespacesResponse.ProtoReflect.Descriptor instead. func (*ListAtespacesResponse) Descriptor() ([]byte, []int) { - return file_ateapi_proto_rawDescGZIP(), []int{51} + return file_ateapi_proto_rawDescGZIP(), []int{56} } func (x *ListAtespacesResponse) GetAtespaces() []*Atespace { @@ -4427,7 +5044,7 @@ type DeleteAtespaceRequest struct { func (x *DeleteAtespaceRequest) Reset() { *x = DeleteAtespaceRequest{} - mi := &file_ateapi_proto_msgTypes[52] + mi := &file_ateapi_proto_msgTypes[57] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -4439,7 +5056,7 @@ func (x *DeleteAtespaceRequest) String() string { func (*DeleteAtespaceRequest) ProtoMessage() {} func (x *DeleteAtespaceRequest) ProtoReflect() protoreflect.Message { - mi := &file_ateapi_proto_msgTypes[52] + mi := &file_ateapi_proto_msgTypes[57] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -4452,7 +5069,7 @@ func (x *DeleteAtespaceRequest) ProtoReflect() protoreflect.Message { // Deprecated: Use DeleteAtespaceRequest.ProtoReflect.Descriptor instead. func (*DeleteAtespaceRequest) Descriptor() ([]byte, []int) { - return file_ateapi_proto_rawDescGZIP(), []int{52} + return file_ateapi_proto_rawDescGZIP(), []int{57} } func (x *DeleteAtespaceRequest) GetAtespace() *ObjectRef { @@ -4483,7 +5100,7 @@ type CreateActorTemplateRequest struct { func (x *CreateActorTemplateRequest) Reset() { *x = CreateActorTemplateRequest{} - mi := &file_ateapi_proto_msgTypes[53] + mi := &file_ateapi_proto_msgTypes[58] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -4495,7 +5112,7 @@ func (x *CreateActorTemplateRequest) String() string { func (*CreateActorTemplateRequest) ProtoMessage() {} func (x *CreateActorTemplateRequest) ProtoReflect() protoreflect.Message { - mi := &file_ateapi_proto_msgTypes[53] + mi := &file_ateapi_proto_msgTypes[58] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -4508,7 +5125,7 @@ func (x *CreateActorTemplateRequest) ProtoReflect() protoreflect.Message { // Deprecated: Use CreateActorTemplateRequest.ProtoReflect.Descriptor instead. func (*CreateActorTemplateRequest) Descriptor() ([]byte, []int) { - return file_ateapi_proto_rawDescGZIP(), []int{53} + return file_ateapi_proto_rawDescGZIP(), []int{58} } func (x *CreateActorTemplateRequest) GetActorTemplate() *ActorTemplate { @@ -4529,7 +5146,7 @@ type GetActorTemplateRequest struct { func (x *GetActorTemplateRequest) Reset() { *x = GetActorTemplateRequest{} - mi := &file_ateapi_proto_msgTypes[54] + mi := &file_ateapi_proto_msgTypes[59] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -4541,7 +5158,7 @@ func (x *GetActorTemplateRequest) String() string { func (*GetActorTemplateRequest) ProtoMessage() {} func (x *GetActorTemplateRequest) ProtoReflect() protoreflect.Message { - mi := &file_ateapi_proto_msgTypes[54] + mi := &file_ateapi_proto_msgTypes[59] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -4554,7 +5171,7 @@ func (x *GetActorTemplateRequest) ProtoReflect() protoreflect.Message { // Deprecated: Use GetActorTemplateRequest.ProtoReflect.Descriptor instead. func (*GetActorTemplateRequest) Descriptor() ([]byte, []int) { - return file_ateapi_proto_rawDescGZIP(), []int{54} + return file_ateapi_proto_rawDescGZIP(), []int{59} } func (x *GetActorTemplateRequest) GetActorTemplate() *ObjectRef { @@ -4582,7 +5199,7 @@ type ListActorTemplatesRequest struct { func (x *ListActorTemplatesRequest) Reset() { *x = ListActorTemplatesRequest{} - mi := &file_ateapi_proto_msgTypes[55] + mi := &file_ateapi_proto_msgTypes[60] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -4594,7 +5211,7 @@ func (x *ListActorTemplatesRequest) String() string { func (*ListActorTemplatesRequest) ProtoMessage() {} func (x *ListActorTemplatesRequest) ProtoReflect() protoreflect.Message { - mi := &file_ateapi_proto_msgTypes[55] + mi := &file_ateapi_proto_msgTypes[60] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -4607,7 +5224,7 @@ func (x *ListActorTemplatesRequest) ProtoReflect() protoreflect.Message { // Deprecated: Use ListActorTemplatesRequest.ProtoReflect.Descriptor instead. func (*ListActorTemplatesRequest) Descriptor() ([]byte, []int) { - return file_ateapi_proto_rawDescGZIP(), []int{55} + return file_ateapi_proto_rawDescGZIP(), []int{60} } func (x *ListActorTemplatesRequest) GetAtespace() string { @@ -4637,7 +5254,7 @@ type ListActorTemplatesResponse struct { func (x *ListActorTemplatesResponse) Reset() { *x = ListActorTemplatesResponse{} - mi := &file_ateapi_proto_msgTypes[56] + mi := &file_ateapi_proto_msgTypes[61] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -4649,7 +5266,7 @@ func (x *ListActorTemplatesResponse) String() string { func (*ListActorTemplatesResponse) ProtoMessage() {} func (x *ListActorTemplatesResponse) ProtoReflect() protoreflect.Message { - mi := &file_ateapi_proto_msgTypes[56] + mi := &file_ateapi_proto_msgTypes[61] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -4662,7 +5279,7 @@ func (x *ListActorTemplatesResponse) ProtoReflect() protoreflect.Message { // Deprecated: Use ListActorTemplatesResponse.ProtoReflect.Descriptor instead. func (*ListActorTemplatesResponse) Descriptor() ([]byte, []int) { - return file_ateapi_proto_rawDescGZIP(), []int{56} + return file_ateapi_proto_rawDescGZIP(), []int{61} } func (x *ListActorTemplatesResponse) GetActorTemplates() []*ActorTemplate { @@ -4694,7 +5311,7 @@ type DeleteActorTemplateRequest struct { func (x *DeleteActorTemplateRequest) Reset() { *x = DeleteActorTemplateRequest{} - mi := &file_ateapi_proto_msgTypes[57] + mi := &file_ateapi_proto_msgTypes[62] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -4706,7 +5323,7 @@ func (x *DeleteActorTemplateRequest) String() string { func (*DeleteActorTemplateRequest) ProtoMessage() {} func (x *DeleteActorTemplateRequest) ProtoReflect() protoreflect.Message { - mi := &file_ateapi_proto_msgTypes[57] + mi := &file_ateapi_proto_msgTypes[62] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -4719,7 +5336,7 @@ func (x *DeleteActorTemplateRequest) ProtoReflect() protoreflect.Message { // Deprecated: Use DeleteActorTemplateRequest.ProtoReflect.Descriptor instead. func (*DeleteActorTemplateRequest) Descriptor() ([]byte, []int) { - return file_ateapi_proto_rawDescGZIP(), []int{57} + return file_ateapi_proto_rawDescGZIP(), []int{62} } func (x *DeleteActorTemplateRequest) GetActorTemplate() *ObjectRef { @@ -4747,7 +5364,7 @@ type GetActorRequest struct { func (x *GetActorRequest) Reset() { *x = GetActorRequest{} - mi := &file_ateapi_proto_msgTypes[58] + mi := &file_ateapi_proto_msgTypes[63] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -4759,7 +5376,7 @@ func (x *GetActorRequest) String() string { func (*GetActorRequest) ProtoMessage() {} func (x *GetActorRequest) ProtoReflect() protoreflect.Message { - mi := &file_ateapi_proto_msgTypes[58] + mi := &file_ateapi_proto_msgTypes[63] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -4772,7 +5389,7 @@ func (x *GetActorRequest) ProtoReflect() protoreflect.Message { // Deprecated: Use GetActorRequest.ProtoReflect.Descriptor instead. func (*GetActorRequest) Descriptor() ([]byte, []int) { - return file_ateapi_proto_rawDescGZIP(), []int{58} + return file_ateapi_proto_rawDescGZIP(), []int{63} } func (x *GetActorRequest) GetActor() *ObjectRef { @@ -4795,7 +5412,7 @@ type CreateActorRequest struct { func (x *CreateActorRequest) Reset() { *x = CreateActorRequest{} - mi := &file_ateapi_proto_msgTypes[59] + mi := &file_ateapi_proto_msgTypes[64] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -4807,7 +5424,7 @@ func (x *CreateActorRequest) String() string { func (*CreateActorRequest) ProtoMessage() {} func (x *CreateActorRequest) ProtoReflect() protoreflect.Message { - mi := &file_ateapi_proto_msgTypes[59] + mi := &file_ateapi_proto_msgTypes[64] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -4820,7 +5437,7 @@ func (x *CreateActorRequest) ProtoReflect() protoreflect.Message { // Deprecated: Use CreateActorRequest.ProtoReflect.Descriptor instead. func (*CreateActorRequest) Descriptor() ([]byte, []int) { - return file_ateapi_proto_rawDescGZIP(), []int{59} + return file_ateapi_proto_rawDescGZIP(), []int{64} } func (x *CreateActorRequest) GetActor() *Actor { @@ -4851,7 +5468,7 @@ type UpdateActorRequest struct { func (x *UpdateActorRequest) Reset() { *x = UpdateActorRequest{} - mi := &file_ateapi_proto_msgTypes[60] + mi := &file_ateapi_proto_msgTypes[65] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -4863,7 +5480,7 @@ func (x *UpdateActorRequest) String() string { func (*UpdateActorRequest) ProtoMessage() {} func (x *UpdateActorRequest) ProtoReflect() protoreflect.Message { - mi := &file_ateapi_proto_msgTypes[60] + mi := &file_ateapi_proto_msgTypes[65] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -4876,7 +5493,7 @@ func (x *UpdateActorRequest) ProtoReflect() protoreflect.Message { // Deprecated: Use UpdateActorRequest.ProtoReflect.Descriptor instead. func (*UpdateActorRequest) Descriptor() ([]byte, []int) { - return file_ateapi_proto_rawDescGZIP(), []int{60} + return file_ateapi_proto_rawDescGZIP(), []int{65} } func (x *UpdateActorRequest) GetActor() *Actor { @@ -4897,7 +5514,7 @@ type SuspendActorRequest struct { func (x *SuspendActorRequest) Reset() { *x = SuspendActorRequest{} - mi := &file_ateapi_proto_msgTypes[61] + mi := &file_ateapi_proto_msgTypes[66] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -4909,7 +5526,7 @@ func (x *SuspendActorRequest) String() string { func (*SuspendActorRequest) ProtoMessage() {} func (x *SuspendActorRequest) ProtoReflect() protoreflect.Message { - mi := &file_ateapi_proto_msgTypes[61] + mi := &file_ateapi_proto_msgTypes[66] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -4922,7 +5539,7 @@ func (x *SuspendActorRequest) ProtoReflect() protoreflect.Message { // Deprecated: Use SuspendActorRequest.ProtoReflect.Descriptor instead. func (*SuspendActorRequest) Descriptor() ([]byte, []int) { - return file_ateapi_proto_rawDescGZIP(), []int{61} + return file_ateapi_proto_rawDescGZIP(), []int{66} } func (x *SuspendActorRequest) GetActor() *ObjectRef { @@ -4941,7 +5558,7 @@ type SuspendActorResponse struct { func (x *SuspendActorResponse) Reset() { *x = SuspendActorResponse{} - mi := &file_ateapi_proto_msgTypes[62] + mi := &file_ateapi_proto_msgTypes[67] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -4953,7 +5570,7 @@ func (x *SuspendActorResponse) String() string { func (*SuspendActorResponse) ProtoMessage() {} func (x *SuspendActorResponse) ProtoReflect() protoreflect.Message { - mi := &file_ateapi_proto_msgTypes[62] + mi := &file_ateapi_proto_msgTypes[67] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -4966,7 +5583,7 @@ func (x *SuspendActorResponse) ProtoReflect() protoreflect.Message { // Deprecated: Use SuspendActorResponse.ProtoReflect.Descriptor instead. func (*SuspendActorResponse) Descriptor() ([]byte, []int) { - return file_ateapi_proto_rawDescGZIP(), []int{62} + return file_ateapi_proto_rawDescGZIP(), []int{67} } func (x *SuspendActorResponse) GetActor() *Actor { @@ -4987,7 +5604,7 @@ type PauseActorRequest struct { func (x *PauseActorRequest) Reset() { *x = PauseActorRequest{} - mi := &file_ateapi_proto_msgTypes[63] + mi := &file_ateapi_proto_msgTypes[68] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -4999,7 +5616,7 @@ func (x *PauseActorRequest) String() string { func (*PauseActorRequest) ProtoMessage() {} func (x *PauseActorRequest) ProtoReflect() protoreflect.Message { - mi := &file_ateapi_proto_msgTypes[63] + mi := &file_ateapi_proto_msgTypes[68] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -5012,7 +5629,7 @@ func (x *PauseActorRequest) ProtoReflect() protoreflect.Message { // Deprecated: Use PauseActorRequest.ProtoReflect.Descriptor instead. func (*PauseActorRequest) Descriptor() ([]byte, []int) { - return file_ateapi_proto_rawDescGZIP(), []int{63} + return file_ateapi_proto_rawDescGZIP(), []int{68} } func (x *PauseActorRequest) GetActor() *ObjectRef { @@ -5031,7 +5648,7 @@ type PauseActorResponse struct { func (x *PauseActorResponse) Reset() { *x = PauseActorResponse{} - mi := &file_ateapi_proto_msgTypes[64] + mi := &file_ateapi_proto_msgTypes[69] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -5043,7 +5660,7 @@ func (x *PauseActorResponse) String() string { func (*PauseActorResponse) ProtoMessage() {} func (x *PauseActorResponse) ProtoReflect() protoreflect.Message { - mi := &file_ateapi_proto_msgTypes[64] + mi := &file_ateapi_proto_msgTypes[69] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -5056,7 +5673,7 @@ func (x *PauseActorResponse) ProtoReflect() protoreflect.Message { // Deprecated: Use PauseActorResponse.ProtoReflect.Descriptor instead. func (*PauseActorResponse) Descriptor() ([]byte, []int) { - return file_ateapi_proto_rawDescGZIP(), []int{64} + return file_ateapi_proto_rawDescGZIP(), []int{69} } func (x *PauseActorResponse) GetActor() *Actor { @@ -5077,7 +5694,7 @@ type ResumeActorRequest struct { func (x *ResumeActorRequest) Reset() { *x = ResumeActorRequest{} - mi := &file_ateapi_proto_msgTypes[65] + mi := &file_ateapi_proto_msgTypes[70] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -5089,7 +5706,7 @@ func (x *ResumeActorRequest) String() string { func (*ResumeActorRequest) ProtoMessage() {} func (x *ResumeActorRequest) ProtoReflect() protoreflect.Message { - mi := &file_ateapi_proto_msgTypes[65] + mi := &file_ateapi_proto_msgTypes[70] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -5102,7 +5719,7 @@ func (x *ResumeActorRequest) ProtoReflect() protoreflect.Message { // Deprecated: Use ResumeActorRequest.ProtoReflect.Descriptor instead. func (*ResumeActorRequest) Descriptor() ([]byte, []int) { - return file_ateapi_proto_rawDescGZIP(), []int{65} + return file_ateapi_proto_rawDescGZIP(), []int{70} } func (x *ResumeActorRequest) GetActor() *ObjectRef { @@ -5124,7 +5741,7 @@ type ResumeActorResponse struct { func (x *ResumeActorResponse) Reset() { *x = ResumeActorResponse{} - mi := &file_ateapi_proto_msgTypes[66] + mi := &file_ateapi_proto_msgTypes[71] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -5136,7 +5753,7 @@ func (x *ResumeActorResponse) String() string { func (*ResumeActorResponse) ProtoMessage() {} func (x *ResumeActorResponse) ProtoReflect() protoreflect.Message { - mi := &file_ateapi_proto_msgTypes[66] + mi := &file_ateapi_proto_msgTypes[71] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -5149,7 +5766,7 @@ func (x *ResumeActorResponse) ProtoReflect() protoreflect.Message { // Deprecated: Use ResumeActorResponse.ProtoReflect.Descriptor instead. func (*ResumeActorResponse) Descriptor() ([]byte, []int) { - return file_ateapi_proto_rawDescGZIP(), []int{66} + return file_ateapi_proto_rawDescGZIP(), []int{71} } func (x *ResumeActorResponse) GetActor() *Actor { @@ -5179,7 +5796,7 @@ type RevertActorRequest struct { func (x *RevertActorRequest) Reset() { *x = RevertActorRequest{} - mi := &file_ateapi_proto_msgTypes[67] + mi := &file_ateapi_proto_msgTypes[72] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -5191,7 +5808,7 @@ func (x *RevertActorRequest) String() string { func (*RevertActorRequest) ProtoMessage() {} func (x *RevertActorRequest) ProtoReflect() protoreflect.Message { - mi := &file_ateapi_proto_msgTypes[67] + mi := &file_ateapi_proto_msgTypes[72] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -5204,7 +5821,7 @@ func (x *RevertActorRequest) ProtoReflect() protoreflect.Message { // Deprecated: Use RevertActorRequest.ProtoReflect.Descriptor instead. func (*RevertActorRequest) Descriptor() ([]byte, []int) { - return file_ateapi_proto_rawDescGZIP(), []int{67} + return file_ateapi_proto_rawDescGZIP(), []int{72} } func (x *RevertActorRequest) GetActor() *ObjectRef { @@ -5226,7 +5843,7 @@ type RevertActorResponse struct { func (x *RevertActorResponse) Reset() { *x = RevertActorResponse{} - mi := &file_ateapi_proto_msgTypes[68] + mi := &file_ateapi_proto_msgTypes[73] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -5238,7 +5855,7 @@ func (x *RevertActorResponse) String() string { func (*RevertActorResponse) ProtoMessage() {} func (x *RevertActorResponse) ProtoReflect() protoreflect.Message { - mi := &file_ateapi_proto_msgTypes[68] + mi := &file_ateapi_proto_msgTypes[73] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -5251,7 +5868,7 @@ func (x *RevertActorResponse) ProtoReflect() protoreflect.Message { // Deprecated: Use RevertActorResponse.ProtoReflect.Descriptor instead. func (*RevertActorResponse) Descriptor() ([]byte, []int) { - return file_ateapi_proto_rawDescGZIP(), []int{68} + return file_ateapi_proto_rawDescGZIP(), []int{73} } func (x *RevertActorResponse) GetActor() *Actor { @@ -5280,7 +5897,7 @@ type DeleteActorRequest struct { func (x *DeleteActorRequest) Reset() { *x = DeleteActorRequest{} - mi := &file_ateapi_proto_msgTypes[69] + mi := &file_ateapi_proto_msgTypes[74] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -5292,7 +5909,7 @@ func (x *DeleteActorRequest) String() string { func (*DeleteActorRequest) ProtoMessage() {} func (x *DeleteActorRequest) ProtoReflect() protoreflect.Message { - mi := &file_ateapi_proto_msgTypes[69] + mi := &file_ateapi_proto_msgTypes[74] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -5305,7 +5922,7 @@ func (x *DeleteActorRequest) ProtoReflect() protoreflect.Message { // Deprecated: Use DeleteActorRequest.ProtoReflect.Descriptor instead. func (*DeleteActorRequest) Descriptor() ([]byte, []int) { - return file_ateapi_proto_rawDescGZIP(), []int{69} + return file_ateapi_proto_rawDescGZIP(), []int{74} } func (x *DeleteActorRequest) GetActor() *ObjectRef { @@ -5349,7 +5966,7 @@ type GetActorEgressPolicyRequest struct { func (x *GetActorEgressPolicyRequest) Reset() { *x = GetActorEgressPolicyRequest{} - mi := &file_ateapi_proto_msgTypes[70] + mi := &file_ateapi_proto_msgTypes[75] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -5361,7 +5978,7 @@ func (x *GetActorEgressPolicyRequest) String() string { func (*GetActorEgressPolicyRequest) ProtoMessage() {} func (x *GetActorEgressPolicyRequest) ProtoReflect() protoreflect.Message { - mi := &file_ateapi_proto_msgTypes[70] + mi := &file_ateapi_proto_msgTypes[75] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -5374,7 +5991,7 @@ func (x *GetActorEgressPolicyRequest) ProtoReflect() protoreflect.Message { // Deprecated: Use GetActorEgressPolicyRequest.ProtoReflect.Descriptor instead. func (*GetActorEgressPolicyRequest) Descriptor() ([]byte, []int) { - return file_ateapi_proto_rawDescGZIP(), []int{70} + return file_ateapi_proto_rawDescGZIP(), []int{75} } func (x *GetActorEgressPolicyRequest) GetActor() *ObjectRef { @@ -5412,7 +6029,7 @@ type CreateActorEgressPolicyRequest struct { func (x *CreateActorEgressPolicyRequest) Reset() { *x = CreateActorEgressPolicyRequest{} - mi := &file_ateapi_proto_msgTypes[71] + mi := &file_ateapi_proto_msgTypes[76] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -5424,7 +6041,7 @@ func (x *CreateActorEgressPolicyRequest) String() string { func (*CreateActorEgressPolicyRequest) ProtoMessage() {} func (x *CreateActorEgressPolicyRequest) ProtoReflect() protoreflect.Message { - mi := &file_ateapi_proto_msgTypes[71] + mi := &file_ateapi_proto_msgTypes[76] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -5437,7 +6054,7 @@ func (x *CreateActorEgressPolicyRequest) ProtoReflect() protoreflect.Message { // Deprecated: Use CreateActorEgressPolicyRequest.ProtoReflect.Descriptor instead. func (*CreateActorEgressPolicyRequest) Descriptor() ([]byte, []int) { - return file_ateapi_proto_rawDescGZIP(), []int{71} + return file_ateapi_proto_rawDescGZIP(), []int{76} } func (x *CreateActorEgressPolicyRequest) GetActor() *ObjectRef { @@ -5475,7 +6092,7 @@ type UpdateActorEgressPolicyRequest struct { func (x *UpdateActorEgressPolicyRequest) Reset() { *x = UpdateActorEgressPolicyRequest{} - mi := &file_ateapi_proto_msgTypes[72] + mi := &file_ateapi_proto_msgTypes[77] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -5487,7 +6104,7 @@ func (x *UpdateActorEgressPolicyRequest) String() string { func (*UpdateActorEgressPolicyRequest) ProtoMessage() {} func (x *UpdateActorEgressPolicyRequest) ProtoReflect() protoreflect.Message { - mi := &file_ateapi_proto_msgTypes[72] + mi := &file_ateapi_proto_msgTypes[77] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -5500,7 +6117,7 @@ func (x *UpdateActorEgressPolicyRequest) ProtoReflect() protoreflect.Message { // Deprecated: Use UpdateActorEgressPolicyRequest.ProtoReflect.Descriptor instead. func (*UpdateActorEgressPolicyRequest) Descriptor() ([]byte, []int) { - return file_ateapi_proto_rawDescGZIP(), []int{72} + return file_ateapi_proto_rawDescGZIP(), []int{77} } func (x *UpdateActorEgressPolicyRequest) GetActor() *ObjectRef { @@ -5541,7 +6158,7 @@ type DeleteActorEgressPolicyRequest struct { func (x *DeleteActorEgressPolicyRequest) Reset() { *x = DeleteActorEgressPolicyRequest{} - mi := &file_ateapi_proto_msgTypes[73] + mi := &file_ateapi_proto_msgTypes[78] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -5553,7 +6170,7 @@ func (x *DeleteActorEgressPolicyRequest) String() string { func (*DeleteActorEgressPolicyRequest) ProtoMessage() {} func (x *DeleteActorEgressPolicyRequest) ProtoReflect() protoreflect.Message { - mi := &file_ateapi_proto_msgTypes[73] + mi := &file_ateapi_proto_msgTypes[78] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -5566,7 +6183,7 @@ func (x *DeleteActorEgressPolicyRequest) ProtoReflect() protoreflect.Message { // Deprecated: Use DeleteActorEgressPolicyRequest.ProtoReflect.Descriptor instead. func (*DeleteActorEgressPolicyRequest) Descriptor() ([]byte, []int) { - return file_ateapi_proto_rawDescGZIP(), []int{73} + return file_ateapi_proto_rawDescGZIP(), []int{78} } func (x *DeleteActorEgressPolicyRequest) GetActor() *ObjectRef { @@ -5601,7 +6218,7 @@ type GetTagRequest struct { func (x *GetTagRequest) Reset() { *x = GetTagRequest{} - mi := &file_ateapi_proto_msgTypes[74] + mi := &file_ateapi_proto_msgTypes[79] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -5613,7 +6230,7 @@ func (x *GetTagRequest) String() string { func (*GetTagRequest) ProtoMessage() {} func (x *GetTagRequest) ProtoReflect() protoreflect.Message { - mi := &file_ateapi_proto_msgTypes[74] + mi := &file_ateapi_proto_msgTypes[79] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -5626,7 +6243,7 @@ func (x *GetTagRequest) ProtoReflect() protoreflect.Message { // Deprecated: Use GetTagRequest.ProtoReflect.Descriptor instead. func (*GetTagRequest) Descriptor() ([]byte, []int) { - return file_ateapi_proto_rawDescGZIP(), []int{74} + return file_ateapi_proto_rawDescGZIP(), []int{79} } func (x *GetTagRequest) GetTag() *ObjectRef { @@ -5665,7 +6282,7 @@ type MintActorJwtRequest struct { func (x *MintActorJwtRequest) Reset() { *x = MintActorJwtRequest{} - mi := &file_ateapi_proto_msgTypes[75] + mi := &file_ateapi_proto_msgTypes[80] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -5677,7 +6294,7 @@ func (x *MintActorJwtRequest) String() string { func (*MintActorJwtRequest) ProtoMessage() {} func (x *MintActorJwtRequest) ProtoReflect() protoreflect.Message { - mi := &file_ateapi_proto_msgTypes[75] + mi := &file_ateapi_proto_msgTypes[80] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -5690,7 +6307,7 @@ func (x *MintActorJwtRequest) ProtoReflect() protoreflect.Message { // Deprecated: Use MintActorJwtRequest.ProtoReflect.Descriptor instead. func (*MintActorJwtRequest) Descriptor() ([]byte, []int) { - return file_ateapi_proto_rawDescGZIP(), []int{75} + return file_ateapi_proto_rawDescGZIP(), []int{80} } func (x *MintActorJwtRequest) GetActor() *ObjectRef { @@ -5745,7 +6362,7 @@ type MintActorJwtResponse struct { func (x *MintActorJwtResponse) Reset() { *x = MintActorJwtResponse{} - mi := &file_ateapi_proto_msgTypes[76] + mi := &file_ateapi_proto_msgTypes[81] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -5757,7 +6374,7 @@ func (x *MintActorJwtResponse) String() string { func (*MintActorJwtResponse) ProtoMessage() {} func (x *MintActorJwtResponse) ProtoReflect() protoreflect.Message { - mi := &file_ateapi_proto_msgTypes[76] + mi := &file_ateapi_proto_msgTypes[81] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -5770,7 +6387,7 @@ func (x *MintActorJwtResponse) ProtoReflect() protoreflect.Message { // Deprecated: Use MintActorJwtResponse.ProtoReflect.Descriptor instead. func (*MintActorJwtResponse) Descriptor() ([]byte, []int) { - return file_ateapi_proto_rawDescGZIP(), []int{76} + return file_ateapi_proto_rawDescGZIP(), []int{81} } func (x *MintActorJwtResponse) GetActorJwt() string { @@ -5815,7 +6432,7 @@ type MintActorCertificateRequest struct { func (x *MintActorCertificateRequest) Reset() { *x = MintActorCertificateRequest{} - mi := &file_ateapi_proto_msgTypes[77] + mi := &file_ateapi_proto_msgTypes[82] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -5827,7 +6444,7 @@ func (x *MintActorCertificateRequest) String() string { func (*MintActorCertificateRequest) ProtoMessage() {} func (x *MintActorCertificateRequest) ProtoReflect() protoreflect.Message { - mi := &file_ateapi_proto_msgTypes[77] + mi := &file_ateapi_proto_msgTypes[82] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -5840,7 +6457,7 @@ func (x *MintActorCertificateRequest) ProtoReflect() protoreflect.Message { // Deprecated: Use MintActorCertificateRequest.ProtoReflect.Descriptor instead. func (*MintActorCertificateRequest) Descriptor() ([]byte, []int) { - return file_ateapi_proto_rawDescGZIP(), []int{77} + return file_ateapi_proto_rawDescGZIP(), []int{82} } func (x *MintActorCertificateRequest) GetActor() *ObjectRef { @@ -5877,7 +6494,7 @@ type MintActorCertificateResponse struct { func (x *MintActorCertificateResponse) Reset() { *x = MintActorCertificateResponse{} - mi := &file_ateapi_proto_msgTypes[78] + mi := &file_ateapi_proto_msgTypes[83] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -5889,7 +6506,7 @@ func (x *MintActorCertificateResponse) String() string { func (*MintActorCertificateResponse) ProtoMessage() {} func (x *MintActorCertificateResponse) ProtoReflect() protoreflect.Message { - mi := &file_ateapi_proto_msgTypes[78] + mi := &file_ateapi_proto_msgTypes[83] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -5902,7 +6519,7 @@ func (x *MintActorCertificateResponse) ProtoReflect() protoreflect.Message { // Deprecated: Use MintActorCertificateResponse.ProtoReflect.Descriptor instead. func (*MintActorCertificateResponse) Descriptor() ([]byte, []int) { - return file_ateapi_proto_rawDescGZIP(), []int{78} + return file_ateapi_proto_rawDescGZIP(), []int{83} } func (x *MintActorCertificateResponse) GetActorCertificates() [][]byte { @@ -5929,7 +6546,7 @@ type ListTagsRequest struct { func (x *ListTagsRequest) Reset() { *x = ListTagsRequest{} - mi := &file_ateapi_proto_msgTypes[79] + mi := &file_ateapi_proto_msgTypes[84] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -5941,7 +6558,7 @@ func (x *ListTagsRequest) String() string { func (*ListTagsRequest) ProtoMessage() {} func (x *ListTagsRequest) ProtoReflect() protoreflect.Message { - mi := &file_ateapi_proto_msgTypes[79] + mi := &file_ateapi_proto_msgTypes[84] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -5954,7 +6571,7 @@ func (x *ListTagsRequest) ProtoReflect() protoreflect.Message { // Deprecated: Use ListTagsRequest.ProtoReflect.Descriptor instead. func (*ListTagsRequest) Descriptor() ([]byte, []int) { - return file_ateapi_proto_rawDescGZIP(), []int{79} + return file_ateapi_proto_rawDescGZIP(), []int{84} } func (x *ListTagsRequest) GetAtespace() string { @@ -5981,7 +6598,7 @@ type ListTagsResponse struct { func (x *ListTagsResponse) Reset() { *x = ListTagsResponse{} - mi := &file_ateapi_proto_msgTypes[80] + mi := &file_ateapi_proto_msgTypes[85] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -5993,7 +6610,7 @@ func (x *ListTagsResponse) String() string { func (*ListTagsResponse) ProtoMessage() {} func (x *ListTagsResponse) ProtoReflect() protoreflect.Message { - mi := &file_ateapi_proto_msgTypes[80] + mi := &file_ateapi_proto_msgTypes[85] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -6006,7 +6623,7 @@ func (x *ListTagsResponse) ProtoReflect() protoreflect.Message { // Deprecated: Use ListTagsResponse.ProtoReflect.Descriptor instead. func (*ListTagsResponse) Descriptor() ([]byte, []int) { - return file_ateapi_proto_rawDescGZIP(), []int{80} + return file_ateapi_proto_rawDescGZIP(), []int{85} } func (x *ListTagsResponse) GetTags() []*Tag { @@ -6051,7 +6668,7 @@ type CreateTagRequest struct { func (x *CreateTagRequest) Reset() { *x = CreateTagRequest{} - mi := &file_ateapi_proto_msgTypes[81] + mi := &file_ateapi_proto_msgTypes[86] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -6063,7 +6680,7 @@ func (x *CreateTagRequest) String() string { func (*CreateTagRequest) ProtoMessage() {} func (x *CreateTagRequest) ProtoReflect() protoreflect.Message { - mi := &file_ateapi_proto_msgTypes[81] + mi := &file_ateapi_proto_msgTypes[86] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -6076,7 +6693,7 @@ func (x *CreateTagRequest) ProtoReflect() protoreflect.Message { // Deprecated: Use CreateTagRequest.ProtoReflect.Descriptor instead. func (*CreateTagRequest) Descriptor() ([]byte, []int) { - return file_ateapi_proto_rawDescGZIP(), []int{81} + return file_ateapi_proto_rawDescGZIP(), []int{86} } func (x *CreateTagRequest) GetTag() *Tag { @@ -6106,7 +6723,7 @@ type UpdateTagRequest struct { func (x *UpdateTagRequest) Reset() { *x = UpdateTagRequest{} - mi := &file_ateapi_proto_msgTypes[82] + mi := &file_ateapi_proto_msgTypes[87] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -6118,7 +6735,7 @@ func (x *UpdateTagRequest) String() string { func (*UpdateTagRequest) ProtoMessage() {} func (x *UpdateTagRequest) ProtoReflect() protoreflect.Message { - mi := &file_ateapi_proto_msgTypes[82] + mi := &file_ateapi_proto_msgTypes[87] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -6131,7 +6748,7 @@ func (x *UpdateTagRequest) ProtoReflect() protoreflect.Message { // Deprecated: Use UpdateTagRequest.ProtoReflect.Descriptor instead. func (*UpdateTagRequest) Descriptor() ([]byte, []int) { - return file_ateapi_proto_rawDescGZIP(), []int{82} + return file_ateapi_proto_rawDescGZIP(), []int{87} } func (x *UpdateTagRequest) GetTag() *Tag { @@ -6156,7 +6773,7 @@ type DeleteTagRequest struct { func (x *DeleteTagRequest) Reset() { *x = DeleteTagRequest{} - mi := &file_ateapi_proto_msgTypes[83] + mi := &file_ateapi_proto_msgTypes[88] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -6168,7 +6785,7 @@ func (x *DeleteTagRequest) String() string { func (*DeleteTagRequest) ProtoMessage() {} func (x *DeleteTagRequest) ProtoReflect() protoreflect.Message { - mi := &file_ateapi_proto_msgTypes[83] + mi := &file_ateapi_proto_msgTypes[88] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -6181,7 +6798,7 @@ func (x *DeleteTagRequest) ProtoReflect() protoreflect.Message { // Deprecated: Use DeleteTagRequest.ProtoReflect.Descriptor instead. func (*DeleteTagRequest) Descriptor() ([]byte, []int) { - return file_ateapi_proto_rawDescGZIP(), []int{83} + return file_ateapi_proto_rawDescGZIP(), []int{88} } func (x *DeleteTagRequest) GetTag() *ObjectRef { @@ -6198,6 +6815,333 @@ func (x *DeleteTagRequest) GetOptions() *DeleteOptions { return nil } +// Request to create a SharedVolume. +// +// +k8s:customValidation # exactly one of storage_class_name and volume_id +type CreateSharedVolumeRequest struct { + state protoimpl.MessageState `protogen:"open.v1"` + // The volume to create. Name a storage class to provision a new volume, or + // supply a volume ID to register one that already exists. status is ignored. + // + // +k8s:required + SharedVolume *SharedVolume `protobuf:"bytes,1,opt,name=shared_volume,json=sharedVolume,proto3" json:"shared_volume,omitempty"` + unknownFields protoimpl.UnknownFields + sizeCache protoimpl.SizeCache +} + +func (x *CreateSharedVolumeRequest) Reset() { + *x = CreateSharedVolumeRequest{} + mi := &file_ateapi_proto_msgTypes[89] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) +} + +func (x *CreateSharedVolumeRequest) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*CreateSharedVolumeRequest) ProtoMessage() {} + +func (x *CreateSharedVolumeRequest) ProtoReflect() protoreflect.Message { + mi := &file_ateapi_proto_msgTypes[89] + 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 CreateSharedVolumeRequest.ProtoReflect.Descriptor instead. +func (*CreateSharedVolumeRequest) Descriptor() ([]byte, []int) { + return file_ateapi_proto_rawDescGZIP(), []int{89} +} + +func (x *CreateSharedVolumeRequest) GetSharedVolume() *SharedVolume { + if x != nil { + return x.SharedVolume + } + return nil +} + +// GetSharedVolumeRequest identifies one SharedVolume. +type GetSharedVolumeRequest struct { + state protoimpl.MessageState `protogen:"open.v1"` + // The volume to get. + // + // +k8s:required + // +k8s:subfield(atespace)=+k8s:required + SharedVolume *ObjectRef `protobuf:"bytes,1,opt,name=shared_volume,json=sharedVolume,proto3" json:"shared_volume,omitempty"` + unknownFields protoimpl.UnknownFields + sizeCache protoimpl.SizeCache +} + +func (x *GetSharedVolumeRequest) Reset() { + *x = GetSharedVolumeRequest{} + mi := &file_ateapi_proto_msgTypes[90] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) +} + +func (x *GetSharedVolumeRequest) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*GetSharedVolumeRequest) ProtoMessage() {} + +func (x *GetSharedVolumeRequest) ProtoReflect() protoreflect.Message { + mi := &file_ateapi_proto_msgTypes[90] + 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 GetSharedVolumeRequest.ProtoReflect.Descriptor instead. +func (*GetSharedVolumeRequest) Descriptor() ([]byte, []int) { + return file_ateapi_proto_rawDescGZIP(), []int{90} +} + +func (x *GetSharedVolumeRequest) GetSharedVolume() *ObjectRef { + if x != nil { + return x.SharedVolume + } + return nil +} + +// ListSharedVolumesRequest selects a page of ExternalVolumes. +type ListSharedVolumesRequest struct { + state protoimpl.MessageState `protogen:"open.v1"` + // The atespace to list from. Empty lists across all atespaces. + // + // +k8s:optional + // +k8s:format=k8s-short-name + Atespace string `protobuf:"bytes,1,opt,name=atespace,proto3" json:"atespace,omitempty"` + // options selects the page size and continuation token. + // + // +k8s:optional + Options *ListOptions `protobuf:"bytes,2,opt,name=options,proto3" json:"options,omitempty"` + unknownFields protoimpl.UnknownFields + sizeCache protoimpl.SizeCache +} + +func (x *ListSharedVolumesRequest) Reset() { + *x = ListSharedVolumesRequest{} + mi := &file_ateapi_proto_msgTypes[91] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) +} + +func (x *ListSharedVolumesRequest) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*ListSharedVolumesRequest) ProtoMessage() {} + +func (x *ListSharedVolumesRequest) ProtoReflect() protoreflect.Message { + mi := &file_ateapi_proto_msgTypes[91] + 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 ListSharedVolumesRequest.ProtoReflect.Descriptor instead. +func (*ListSharedVolumesRequest) Descriptor() ([]byte, []int) { + return file_ateapi_proto_rawDescGZIP(), []int{91} +} + +func (x *ListSharedVolumesRequest) GetAtespace() string { + if x != nil { + return x.Atespace + } + return "" +} + +func (x *ListSharedVolumesRequest) GetOptions() *ListOptions { + if x != nil { + return x.Options + } + return nil +} + +// ListSharedVolumesResponse is one page of ExternalVolumes. +type ListSharedVolumesResponse struct { + state protoimpl.MessageState `protogen:"open.v1"` + // The volumes in this page. + SharedVolumes []*SharedVolume `protobuf:"bytes,1,rep,name=shared_volumes,json=sharedVolumes,proto3" json:"shared_volumes,omitempty"` + // Pagination token for the next page. Empty on the last page. + NextPageToken string `protobuf:"bytes,2,opt,name=next_page_token,json=nextPageToken,proto3" json:"next_page_token,omitempty"` + unknownFields protoimpl.UnknownFields + sizeCache protoimpl.SizeCache +} + +func (x *ListSharedVolumesResponse) Reset() { + *x = ListSharedVolumesResponse{} + mi := &file_ateapi_proto_msgTypes[92] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) +} + +func (x *ListSharedVolumesResponse) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*ListSharedVolumesResponse) ProtoMessage() {} + +func (x *ListSharedVolumesResponse) ProtoReflect() protoreflect.Message { + mi := &file_ateapi_proto_msgTypes[92] + 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 ListSharedVolumesResponse.ProtoReflect.Descriptor instead. +func (*ListSharedVolumesResponse) Descriptor() ([]byte, []int) { + return file_ateapi_proto_rawDescGZIP(), []int{92} +} + +func (x *ListSharedVolumesResponse) GetSharedVolumes() []*SharedVolume { + if x != nil { + return x.SharedVolumes + } + return nil +} + +func (x *ListSharedVolumesResponse) GetNextPageToken() string { + if x != nil { + return x.NextPageToken + } + return "" +} + +// Request to update the mutable fields of a SharedVolume. +type UpdateSharedVolumeRequest struct { + state protoimpl.MessageState `protogen:"open.v1"` + // The volume to update. metadata.atespace and metadata.name identify it; + // metadata.version and metadata.uid are required preconditions. + // + // +k8s:required + // +k8s:opaqueType # updates are handled in 2 steps, do not descend + // +k8s:subfield(metadata)=+k8s:required + // +k8s:customValidation # TODO: when we get nested subfields, require metadata.atespace + SharedVolume *SharedVolume `protobuf:"bytes,1,opt,name=shared_volume,json=sharedVolume,proto3" json:"shared_volume,omitempty"` + unknownFields protoimpl.UnknownFields + sizeCache protoimpl.SizeCache +} + +func (x *UpdateSharedVolumeRequest) Reset() { + *x = UpdateSharedVolumeRequest{} + mi := &file_ateapi_proto_msgTypes[93] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) +} + +func (x *UpdateSharedVolumeRequest) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*UpdateSharedVolumeRequest) ProtoMessage() {} + +func (x *UpdateSharedVolumeRequest) ProtoReflect() protoreflect.Message { + mi := &file_ateapi_proto_msgTypes[93] + 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 UpdateSharedVolumeRequest.ProtoReflect.Descriptor instead. +func (*UpdateSharedVolumeRequest) Descriptor() ([]byte, []int) { + return file_ateapi_proto_rawDescGZIP(), []int{93} +} + +func (x *UpdateSharedVolumeRequest) GetSharedVolume() *SharedVolume { + if x != nil { + return x.SharedVolume + } + return nil +} + +// DeleteSharedVolumeRequest identifies one SharedVolume to delete. +type DeleteSharedVolumeRequest struct { + state protoimpl.MessageState `protogen:"open.v1"` + // The volume to delete. + // + // +k8s:required + // +k8s:subfield(atespace)=+k8s:required + SharedVolume *ObjectRef `protobuf:"bytes,1,opt,name=shared_volume,json=sharedVolume,proto3" json:"shared_volume,omitempty"` + // Optional per-delete preconditions. + // + // +k8s:optional + Options *DeleteOptions `protobuf:"bytes,2,opt,name=options,proto3" json:"options,omitempty"` + unknownFields protoimpl.UnknownFields + sizeCache protoimpl.SizeCache +} + +func (x *DeleteSharedVolumeRequest) Reset() { + *x = DeleteSharedVolumeRequest{} + mi := &file_ateapi_proto_msgTypes[94] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) +} + +func (x *DeleteSharedVolumeRequest) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*DeleteSharedVolumeRequest) ProtoMessage() {} + +func (x *DeleteSharedVolumeRequest) ProtoReflect() protoreflect.Message { + mi := &file_ateapi_proto_msgTypes[94] + 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 DeleteSharedVolumeRequest.ProtoReflect.Descriptor instead. +func (*DeleteSharedVolumeRequest) Descriptor() ([]byte, []int) { + return file_ateapi_proto_rawDescGZIP(), []int{94} +} + +func (x *DeleteSharedVolumeRequest) GetSharedVolume() *ObjectRef { + if x != nil { + return x.SharedVolume + } + return nil +} + +func (x *DeleteSharedVolumeRequest) GetOptions() *DeleteOptions { + if x != nil { + return x.Options + } + return nil +} + // DeleteOptions are the optional guards a Delete request may carry. type DeleteOptions struct { state protoimpl.MessageState `protogen:"open.v1"` @@ -6220,7 +7164,7 @@ type DeleteOptions struct { func (x *DeleteOptions) Reset() { *x = DeleteOptions{} - mi := &file_ateapi_proto_msgTypes[84] + mi := &file_ateapi_proto_msgTypes[95] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -6232,7 +7176,7 @@ func (x *DeleteOptions) String() string { func (*DeleteOptions) ProtoMessage() {} func (x *DeleteOptions) ProtoReflect() protoreflect.Message { - mi := &file_ateapi_proto_msgTypes[84] + mi := &file_ateapi_proto_msgTypes[95] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -6245,7 +7189,7 @@ func (x *DeleteOptions) ProtoReflect() protoreflect.Message { // Deprecated: Use DeleteOptions.ProtoReflect.Descriptor instead. func (*DeleteOptions) Descriptor() ([]byte, []int) { - return file_ateapi_proto_rawDescGZIP(), []int{84} + return file_ateapi_proto_rawDescGZIP(), []int{95} } func (x *DeleteOptions) GetVersion() int64 { @@ -6284,7 +7228,7 @@ type ListOptions struct { func (x *ListOptions) Reset() { *x = ListOptions{} - mi := &file_ateapi_proto_msgTypes[85] + mi := &file_ateapi_proto_msgTypes[96] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -6296,7 +7240,7 @@ func (x *ListOptions) String() string { func (*ListOptions) ProtoMessage() {} func (x *ListOptions) ProtoReflect() protoreflect.Message { - mi := &file_ateapi_proto_msgTypes[85] + mi := &file_ateapi_proto_msgTypes[96] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -6309,7 +7253,7 @@ func (x *ListOptions) ProtoReflect() protoreflect.Message { // Deprecated: Use ListOptions.ProtoReflect.Descriptor instead. func (*ListOptions) Descriptor() ([]byte, []int) { - return file_ateapi_proto_rawDescGZIP(), []int{85} + return file_ateapi_proto_rawDescGZIP(), []int{96} } func (x *ListOptions) GetPageSize() int32 { @@ -6345,7 +7289,7 @@ type ListWorkerActorAssignmentsRequest struct { func (x *ListWorkerActorAssignmentsRequest) Reset() { *x = ListWorkerActorAssignmentsRequest{} - mi := &file_ateapi_proto_msgTypes[86] + mi := &file_ateapi_proto_msgTypes[97] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -6357,7 +7301,7 @@ func (x *ListWorkerActorAssignmentsRequest) String() string { func (*ListWorkerActorAssignmentsRequest) ProtoMessage() {} func (x *ListWorkerActorAssignmentsRequest) ProtoReflect() protoreflect.Message { - mi := &file_ateapi_proto_msgTypes[86] + mi := &file_ateapi_proto_msgTypes[97] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -6370,7 +7314,7 @@ func (x *ListWorkerActorAssignmentsRequest) ProtoReflect() protoreflect.Message // Deprecated: Use ListWorkerActorAssignmentsRequest.ProtoReflect.Descriptor instead. func (*ListWorkerActorAssignmentsRequest) Descriptor() ([]byte, []int) { - return file_ateapi_proto_rawDescGZIP(), []int{86} + return file_ateapi_proto_rawDescGZIP(), []int{97} } func (x *ListWorkerActorAssignmentsRequest) GetWorker() *ObjectRef { @@ -6400,7 +7344,7 @@ type ListWorkerActorAssignmentsResponse struct { func (x *ListWorkerActorAssignmentsResponse) Reset() { *x = ListWorkerActorAssignmentsResponse{} - mi := &file_ateapi_proto_msgTypes[87] + mi := &file_ateapi_proto_msgTypes[98] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -6412,7 +7356,7 @@ func (x *ListWorkerActorAssignmentsResponse) String() string { func (*ListWorkerActorAssignmentsResponse) ProtoMessage() {} func (x *ListWorkerActorAssignmentsResponse) ProtoReflect() protoreflect.Message { - mi := &file_ateapi_proto_msgTypes[87] + mi := &file_ateapi_proto_msgTypes[98] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -6425,7 +7369,7 @@ func (x *ListWorkerActorAssignmentsResponse) ProtoReflect() protoreflect.Message // Deprecated: Use ListWorkerActorAssignmentsResponse.ProtoReflect.Descriptor instead. func (*ListWorkerActorAssignmentsResponse) Descriptor() ([]byte, []int) { - return file_ateapi_proto_rawDescGZIP(), []int{87} + return file_ateapi_proto_rawDescGZIP(), []int{98} } func (x *ListWorkerActorAssignmentsResponse) GetActorAssignments() []*ActorAssignment { @@ -6454,7 +7398,7 @@ type ListWorkersRequest struct { func (x *ListWorkersRequest) Reset() { *x = ListWorkersRequest{} - mi := &file_ateapi_proto_msgTypes[88] + mi := &file_ateapi_proto_msgTypes[99] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -6466,7 +7410,7 @@ func (x *ListWorkersRequest) String() string { func (*ListWorkersRequest) ProtoMessage() {} func (x *ListWorkersRequest) ProtoReflect() protoreflect.Message { - mi := &file_ateapi_proto_msgTypes[88] + mi := &file_ateapi_proto_msgTypes[99] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -6479,7 +7423,7 @@ func (x *ListWorkersRequest) ProtoReflect() protoreflect.Message { // Deprecated: Use ListWorkersRequest.ProtoReflect.Descriptor instead. func (*ListWorkersRequest) Descriptor() ([]byte, []int) { - return file_ateapi_proto_rawDescGZIP(), []int{88} + return file_ateapi_proto_rawDescGZIP(), []int{99} } func (x *ListWorkersRequest) GetOptions() *ListOptions { @@ -6501,7 +7445,7 @@ type ListWorkersResponse struct { func (x *ListWorkersResponse) Reset() { *x = ListWorkersResponse{} - mi := &file_ateapi_proto_msgTypes[89] + mi := &file_ateapi_proto_msgTypes[100] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -6513,7 +7457,7 @@ func (x *ListWorkersResponse) String() string { func (*ListWorkersResponse) ProtoMessage() {} func (x *ListWorkersResponse) ProtoReflect() protoreflect.Message { - mi := &file_ateapi_proto_msgTypes[89] + mi := &file_ateapi_proto_msgTypes[100] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -6526,7 +7470,7 @@ func (x *ListWorkersResponse) ProtoReflect() protoreflect.Message { // Deprecated: Use ListWorkersResponse.ProtoReflect.Descriptor instead. func (*ListWorkersResponse) Descriptor() ([]byte, []int) { - return file_ateapi_proto_rawDescGZIP(), []int{89} + return file_ateapi_proto_rawDescGZIP(), []int{100} } func (x *ListWorkersResponse) GetWorkers() []*Worker { @@ -6556,7 +7500,7 @@ type GetWorkerRequest struct { func (x *GetWorkerRequest) Reset() { *x = GetWorkerRequest{} - mi := &file_ateapi_proto_msgTypes[90] + mi := &file_ateapi_proto_msgTypes[101] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -6568,7 +7512,7 @@ func (x *GetWorkerRequest) String() string { func (*GetWorkerRequest) ProtoMessage() {} func (x *GetWorkerRequest) ProtoReflect() protoreflect.Message { - mi := &file_ateapi_proto_msgTypes[90] + mi := &file_ateapi_proto_msgTypes[101] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -6581,7 +7525,7 @@ func (x *GetWorkerRequest) ProtoReflect() protoreflect.Message { // Deprecated: Use GetWorkerRequest.ProtoReflect.Descriptor instead. func (*GetWorkerRequest) Descriptor() ([]byte, []int) { - return file_ateapi_proto_rawDescGZIP(), []int{90} + return file_ateapi_proto_rawDescGZIP(), []int{101} } func (x *GetWorkerRequest) GetWorker() *ObjectRef { @@ -6603,7 +7547,7 @@ type CreateWorkerRequest struct { func (x *CreateWorkerRequest) Reset() { *x = CreateWorkerRequest{} - mi := &file_ateapi_proto_msgTypes[91] + mi := &file_ateapi_proto_msgTypes[102] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -6615,7 +7559,7 @@ func (x *CreateWorkerRequest) String() string { func (*CreateWorkerRequest) ProtoMessage() {} func (x *CreateWorkerRequest) ProtoReflect() protoreflect.Message { - mi := &file_ateapi_proto_msgTypes[91] + mi := &file_ateapi_proto_msgTypes[102] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -6628,7 +7572,7 @@ func (x *CreateWorkerRequest) ProtoReflect() protoreflect.Message { // Deprecated: Use CreateWorkerRequest.ProtoReflect.Descriptor instead. func (*CreateWorkerRequest) Descriptor() ([]byte, []int) { - return file_ateapi_proto_rawDescGZIP(), []int{91} + return file_ateapi_proto_rawDescGZIP(), []int{102} } func (x *CreateWorkerRequest) GetWorker() *Worker { @@ -6663,7 +7607,7 @@ type UpdateWorkerRequest struct { func (x *UpdateWorkerRequest) Reset() { *x = UpdateWorkerRequest{} - mi := &file_ateapi_proto_msgTypes[92] + mi := &file_ateapi_proto_msgTypes[103] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -6675,7 +7619,7 @@ func (x *UpdateWorkerRequest) String() string { func (*UpdateWorkerRequest) ProtoMessage() {} func (x *UpdateWorkerRequest) ProtoReflect() protoreflect.Message { - mi := &file_ateapi_proto_msgTypes[92] + mi := &file_ateapi_proto_msgTypes[103] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -6688,7 +7632,7 @@ func (x *UpdateWorkerRequest) ProtoReflect() protoreflect.Message { // Deprecated: Use UpdateWorkerRequest.ProtoReflect.Descriptor instead. func (*UpdateWorkerRequest) Descriptor() ([]byte, []int) { - return file_ateapi_proto_rawDescGZIP(), []int{92} + return file_ateapi_proto_rawDescGZIP(), []int{103} } func (x *UpdateWorkerRequest) GetWorker() *Worker { @@ -6715,7 +7659,7 @@ type DeleteWorkerRequest struct { func (x *DeleteWorkerRequest) Reset() { *x = DeleteWorkerRequest{} - mi := &file_ateapi_proto_msgTypes[93] + mi := &file_ateapi_proto_msgTypes[104] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -6727,7 +7671,7 @@ func (x *DeleteWorkerRequest) String() string { func (*DeleteWorkerRequest) ProtoMessage() {} func (x *DeleteWorkerRequest) ProtoReflect() protoreflect.Message { - mi := &file_ateapi_proto_msgTypes[93] + mi := &file_ateapi_proto_msgTypes[104] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -6740,7 +7684,7 @@ func (x *DeleteWorkerRequest) ProtoReflect() protoreflect.Message { // Deprecated: Use DeleteWorkerRequest.ProtoReflect.Descriptor instead. func (*DeleteWorkerRequest) Descriptor() ([]byte, []int) { - return file_ateapi_proto_rawDescGZIP(), []int{93} + return file_ateapi_proto_rawDescGZIP(), []int{104} } func (x *DeleteWorkerRequest) GetWorker() *ObjectRef { @@ -6770,7 +7714,7 @@ type DrainWorkerRequest struct { func (x *DrainWorkerRequest) Reset() { *x = DrainWorkerRequest{} - mi := &file_ateapi_proto_msgTypes[94] + mi := &file_ateapi_proto_msgTypes[105] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -6782,7 +7726,7 @@ func (x *DrainWorkerRequest) String() string { func (*DrainWorkerRequest) ProtoMessage() {} func (x *DrainWorkerRequest) ProtoReflect() protoreflect.Message { - mi := &file_ateapi_proto_msgTypes[94] + mi := &file_ateapi_proto_msgTypes[105] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -6795,7 +7739,7 @@ func (x *DrainWorkerRequest) ProtoReflect() protoreflect.Message { // Deprecated: Use DrainWorkerRequest.ProtoReflect.Descriptor instead. func (*DrainWorkerRequest) Descriptor() ([]byte, []int) { - return file_ateapi_proto_rawDescGZIP(), []int{94} + return file_ateapi_proto_rawDescGZIP(), []int{105} } func (x *DrainWorkerRequest) GetWorker() *ObjectRef { @@ -6816,7 +7760,7 @@ type DrainWorkerResponse struct { func (x *DrainWorkerResponse) Reset() { *x = DrainWorkerResponse{} - mi := &file_ateapi_proto_msgTypes[95] + mi := &file_ateapi_proto_msgTypes[106] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -6828,7 +7772,7 @@ func (x *DrainWorkerResponse) String() string { func (*DrainWorkerResponse) ProtoMessage() {} func (x *DrainWorkerResponse) ProtoReflect() protoreflect.Message { - mi := &file_ateapi_proto_msgTypes[95] + mi := &file_ateapi_proto_msgTypes[106] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -6841,7 +7785,7 @@ func (x *DrainWorkerResponse) ProtoReflect() protoreflect.Message { // Deprecated: Use DrainWorkerResponse.ProtoReflect.Descriptor instead. func (*DrainWorkerResponse) Descriptor() ([]byte, []int) { - return file_ateapi_proto_rawDescGZIP(), []int{95} + return file_ateapi_proto_rawDescGZIP(), []int{106} } func (x *DrainWorkerResponse) GetWorker() *Worker { @@ -6865,7 +7809,7 @@ type MarkWorkerAvailableRequest struct { func (x *MarkWorkerAvailableRequest) Reset() { *x = MarkWorkerAvailableRequest{} - mi := &file_ateapi_proto_msgTypes[96] + mi := &file_ateapi_proto_msgTypes[107] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -6877,7 +7821,7 @@ func (x *MarkWorkerAvailableRequest) String() string { func (*MarkWorkerAvailableRequest) ProtoMessage() {} func (x *MarkWorkerAvailableRequest) ProtoReflect() protoreflect.Message { - mi := &file_ateapi_proto_msgTypes[96] + mi := &file_ateapi_proto_msgTypes[107] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -6890,7 +7834,7 @@ func (x *MarkWorkerAvailableRequest) ProtoReflect() protoreflect.Message { // Deprecated: Use MarkWorkerAvailableRequest.ProtoReflect.Descriptor instead. func (*MarkWorkerAvailableRequest) Descriptor() ([]byte, []int) { - return file_ateapi_proto_rawDescGZIP(), []int{96} + return file_ateapi_proto_rawDescGZIP(), []int{107} } func (x *MarkWorkerAvailableRequest) GetWorker() *ObjectRef { @@ -6911,7 +7855,7 @@ type MarkWorkerAvailableResponse struct { func (x *MarkWorkerAvailableResponse) Reset() { *x = MarkWorkerAvailableResponse{} - mi := &file_ateapi_proto_msgTypes[97] + mi := &file_ateapi_proto_msgTypes[108] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -6923,7 +7867,7 @@ func (x *MarkWorkerAvailableResponse) String() string { func (*MarkWorkerAvailableResponse) ProtoMessage() {} func (x *MarkWorkerAvailableResponse) ProtoReflect() protoreflect.Message { - mi := &file_ateapi_proto_msgTypes[97] + mi := &file_ateapi_proto_msgTypes[108] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -6936,7 +7880,7 @@ func (x *MarkWorkerAvailableResponse) ProtoReflect() protoreflect.Message { // Deprecated: Use MarkWorkerAvailableResponse.ProtoReflect.Descriptor instead. func (*MarkWorkerAvailableResponse) Descriptor() ([]byte, []int) { - return file_ateapi_proto_rawDescGZIP(), []int{97} + return file_ateapi_proto_rawDescGZIP(), []int{108} } func (x *MarkWorkerAvailableResponse) GetWorker() *Worker { @@ -6960,7 +7904,7 @@ type MarkWorkerUnavailableRequest struct { func (x *MarkWorkerUnavailableRequest) Reset() { *x = MarkWorkerUnavailableRequest{} - mi := &file_ateapi_proto_msgTypes[98] + mi := &file_ateapi_proto_msgTypes[109] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -6972,7 +7916,7 @@ func (x *MarkWorkerUnavailableRequest) String() string { func (*MarkWorkerUnavailableRequest) ProtoMessage() {} func (x *MarkWorkerUnavailableRequest) ProtoReflect() protoreflect.Message { - mi := &file_ateapi_proto_msgTypes[98] + mi := &file_ateapi_proto_msgTypes[109] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -6985,7 +7929,7 @@ func (x *MarkWorkerUnavailableRequest) ProtoReflect() protoreflect.Message { // Deprecated: Use MarkWorkerUnavailableRequest.ProtoReflect.Descriptor instead. func (*MarkWorkerUnavailableRequest) Descriptor() ([]byte, []int) { - return file_ateapi_proto_rawDescGZIP(), []int{98} + return file_ateapi_proto_rawDescGZIP(), []int{109} } func (x *MarkWorkerUnavailableRequest) GetWorker() *ObjectRef { @@ -7006,7 +7950,7 @@ type MarkWorkerUnavailableResponse struct { func (x *MarkWorkerUnavailableResponse) Reset() { *x = MarkWorkerUnavailableResponse{} - mi := &file_ateapi_proto_msgTypes[99] + mi := &file_ateapi_proto_msgTypes[110] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -7018,7 +7962,7 @@ func (x *MarkWorkerUnavailableResponse) String() string { func (*MarkWorkerUnavailableResponse) ProtoMessage() {} func (x *MarkWorkerUnavailableResponse) ProtoReflect() protoreflect.Message { - mi := &file_ateapi_proto_msgTypes[99] + mi := &file_ateapi_proto_msgTypes[110] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -7031,7 +7975,7 @@ func (x *MarkWorkerUnavailableResponse) ProtoReflect() protoreflect.Message { // Deprecated: Use MarkWorkerUnavailableResponse.ProtoReflect.Descriptor instead. func (*MarkWorkerUnavailableResponse) Descriptor() ([]byte, []int) { - return file_ateapi_proto_rawDescGZIP(), []int{99} + return file_ateapi_proto_rawDescGZIP(), []int{110} } func (x *MarkWorkerUnavailableResponse) GetWorker() *Worker { @@ -7060,7 +8004,7 @@ type ListActorsRequest struct { func (x *ListActorsRequest) Reset() { *x = ListActorsRequest{} - mi := &file_ateapi_proto_msgTypes[100] + mi := &file_ateapi_proto_msgTypes[111] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -7072,7 +8016,7 @@ func (x *ListActorsRequest) String() string { func (*ListActorsRequest) ProtoMessage() {} func (x *ListActorsRequest) ProtoReflect() protoreflect.Message { - mi := &file_ateapi_proto_msgTypes[100] + mi := &file_ateapi_proto_msgTypes[111] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -7085,7 +8029,7 @@ func (x *ListActorsRequest) ProtoReflect() protoreflect.Message { // Deprecated: Use ListActorsRequest.ProtoReflect.Descriptor instead. func (*ListActorsRequest) Descriptor() ([]byte, []int) { - return file_ateapi_proto_rawDescGZIP(), []int{100} + return file_ateapi_proto_rawDescGZIP(), []int{111} } func (x *ListActorsRequest) GetAtespace() string { @@ -7114,7 +8058,7 @@ type ListActorsResponse struct { func (x *ListActorsResponse) Reset() { *x = ListActorsResponse{} - mi := &file_ateapi_proto_msgTypes[101] + mi := &file_ateapi_proto_msgTypes[112] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -7126,7 +8070,7 @@ func (x *ListActorsResponse) String() string { func (*ListActorsResponse) ProtoMessage() {} func (x *ListActorsResponse) ProtoReflect() protoreflect.Message { - mi := &file_ateapi_proto_msgTypes[101] + mi := &file_ateapi_proto_msgTypes[112] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -7139,7 +8083,7 @@ func (x *ListActorsResponse) ProtoReflect() protoreflect.Message { // Deprecated: Use ListActorsResponse.ProtoReflect.Descriptor instead. func (*ListActorsResponse) Descriptor() ([]byte, []int) { - return file_ateapi_proto_rawDescGZIP(), []int{101} + return file_ateapi_proto_rawDescGZIP(), []int{112} } func (x *ListActorsResponse) GetActors() []*Actor { @@ -7256,7 +8200,7 @@ type Worker struct { func (x *Worker) Reset() { *x = Worker{} - mi := &file_ateapi_proto_msgTypes[102] + mi := &file_ateapi_proto_msgTypes[113] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -7268,7 +8212,7 @@ func (x *Worker) String() string { func (*Worker) ProtoMessage() {} func (x *Worker) ProtoReflect() protoreflect.Message { - mi := &file_ateapi_proto_msgTypes[102] + mi := &file_ateapi_proto_msgTypes[113] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -7281,7 +8225,7 @@ func (x *Worker) ProtoReflect() protoreflect.Message { // Deprecated: Use Worker.ProtoReflect.Descriptor instead. func (*Worker) Descriptor() ([]byte, []int) { - return file_ateapi_proto_rawDescGZIP(), []int{102} + return file_ateapi_proto_rawDescGZIP(), []int{113} } func (x *Worker) GetMetadata() *ResourceMetadata { @@ -7417,7 +8361,7 @@ type WorkerStatus struct { func (x *WorkerStatus) Reset() { *x = WorkerStatus{} - mi := &file_ateapi_proto_msgTypes[103] + mi := &file_ateapi_proto_msgTypes[114] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -7429,7 +8373,7 @@ func (x *WorkerStatus) String() string { func (*WorkerStatus) ProtoMessage() {} func (x *WorkerStatus) ProtoReflect() protoreflect.Message { - mi := &file_ateapi_proto_msgTypes[103] + mi := &file_ateapi_proto_msgTypes[114] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -7442,7 +8386,7 @@ func (x *WorkerStatus) ProtoReflect() protoreflect.Message { // Deprecated: Use WorkerStatus.ProtoReflect.Descriptor instead. func (*WorkerStatus) Descriptor() ([]byte, []int) { - return file_ateapi_proto_rawDescGZIP(), []int{103} + return file_ateapi_proto_rawDescGZIP(), []int{114} } func (x *WorkerStatus) GetState() WorkerState { @@ -7510,7 +8454,7 @@ type SandboxRuntime struct { func (x *SandboxRuntime) Reset() { *x = SandboxRuntime{} - mi := &file_ateapi_proto_msgTypes[104] + mi := &file_ateapi_proto_msgTypes[115] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -7522,7 +8466,7 @@ func (x *SandboxRuntime) String() string { func (*SandboxRuntime) ProtoMessage() {} func (x *SandboxRuntime) ProtoReflect() protoreflect.Message { - mi := &file_ateapi_proto_msgTypes[104] + mi := &file_ateapi_proto_msgTypes[115] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -7535,7 +8479,7 @@ func (x *SandboxRuntime) ProtoReflect() protoreflect.Message { // Deprecated: Use SandboxRuntime.ProtoReflect.Descriptor instead. func (*SandboxRuntime) Descriptor() ([]byte, []int) { - return file_ateapi_proto_rawDescGZIP(), []int{104} + return file_ateapi_proto_rawDescGZIP(), []int{115} } func (x *SandboxRuntime) GetSandboxClass() string { @@ -7586,7 +8530,7 @@ type VersionedSandboxCompat struct { func (x *VersionedSandboxCompat) Reset() { *x = VersionedSandboxCompat{} - mi := &file_ateapi_proto_msgTypes[105] + mi := &file_ateapi_proto_msgTypes[116] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -7598,7 +8542,7 @@ func (x *VersionedSandboxCompat) String() string { func (*VersionedSandboxCompat) ProtoMessage() {} func (x *VersionedSandboxCompat) ProtoReflect() protoreflect.Message { - mi := &file_ateapi_proto_msgTypes[105] + mi := &file_ateapi_proto_msgTypes[116] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -7611,7 +8555,7 @@ func (x *VersionedSandboxCompat) ProtoReflect() protoreflect.Message { // Deprecated: Use VersionedSandboxCompat.ProtoReflect.Descriptor instead. func (*VersionedSandboxCompat) Descriptor() ([]byte, []int) { - return file_ateapi_proto_rawDescGZIP(), []int{105} + return file_ateapi_proto_rawDescGZIP(), []int{116} } func (x *VersionedSandboxCompat) GetSchemaVersion() string { @@ -7647,7 +8591,7 @@ type AttributeEntry struct { func (x *AttributeEntry) Reset() { *x = AttributeEntry{} - mi := &file_ateapi_proto_msgTypes[106] + mi := &file_ateapi_proto_msgTypes[117] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -7659,7 +8603,7 @@ func (x *AttributeEntry) String() string { func (*AttributeEntry) ProtoMessage() {} func (x *AttributeEntry) ProtoReflect() protoreflect.Message { - mi := &file_ateapi_proto_msgTypes[106] + mi := &file_ateapi_proto_msgTypes[117] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -7672,7 +8616,7 @@ func (x *AttributeEntry) ProtoReflect() protoreflect.Message { // Deprecated: Use AttributeEntry.ProtoReflect.Descriptor instead. func (*AttributeEntry) Descriptor() ([]byte, []int) { - return file_ateapi_proto_rawDescGZIP(), []int{106} + return file_ateapi_proto_rawDescGZIP(), []int{117} } func (x *AttributeEntry) GetKey() string { @@ -7711,7 +8655,7 @@ type WorkerResources struct { func (x *WorkerResources) Reset() { *x = WorkerResources{} - mi := &file_ateapi_proto_msgTypes[107] + mi := &file_ateapi_proto_msgTypes[118] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -7723,7 +8667,7 @@ func (x *WorkerResources) String() string { func (*WorkerResources) ProtoMessage() {} func (x *WorkerResources) ProtoReflect() protoreflect.Message { - mi := &file_ateapi_proto_msgTypes[107] + mi := &file_ateapi_proto_msgTypes[118] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -7736,7 +8680,7 @@ func (x *WorkerResources) ProtoReflect() protoreflect.Message { // Deprecated: Use WorkerResources.ProtoReflect.Descriptor instead. func (*WorkerResources) Descriptor() ([]byte, []int) { - return file_ateapi_proto_rawDescGZIP(), []int{107} + return file_ateapi_proto_rawDescGZIP(), []int{118} } func (x *WorkerResources) GetResources() *Resources { @@ -7789,7 +8733,7 @@ type ActorAssignment struct { func (x *ActorAssignment) Reset() { *x = ActorAssignment{} - mi := &file_ateapi_proto_msgTypes[108] + mi := &file_ateapi_proto_msgTypes[119] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -7801,7 +8745,7 @@ func (x *ActorAssignment) String() string { func (*ActorAssignment) ProtoMessage() {} func (x *ActorAssignment) ProtoReflect() protoreflect.Message { - mi := &file_ateapi_proto_msgTypes[108] + mi := &file_ateapi_proto_msgTypes[119] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -7814,7 +8758,7 @@ func (x *ActorAssignment) ProtoReflect() protoreflect.Message { // Deprecated: Use ActorAssignment.ProtoReflect.Descriptor instead. func (*ActorAssignment) Descriptor() ([]byte, []int) { - return file_ateapi_proto_rawDescGZIP(), []int{108} + return file_ateapi_proto_rawDescGZIP(), []int{119} } func (x *ActorAssignment) GetMetadata() *ResourceMetadata { @@ -7885,7 +8829,7 @@ type RegisterWorkerRequest struct { func (x *RegisterWorkerRequest) Reset() { *x = RegisterWorkerRequest{} - mi := &file_ateapi_proto_msgTypes[109] + mi := &file_ateapi_proto_msgTypes[120] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -7897,7 +8841,7 @@ func (x *RegisterWorkerRequest) String() string { func (*RegisterWorkerRequest) ProtoMessage() {} func (x *RegisterWorkerRequest) ProtoReflect() protoreflect.Message { - mi := &file_ateapi_proto_msgTypes[109] + mi := &file_ateapi_proto_msgTypes[120] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -7910,7 +8854,7 @@ func (x *RegisterWorkerRequest) ProtoReflect() protoreflect.Message { // Deprecated: Use RegisterWorkerRequest.ProtoReflect.Descriptor instead. func (*RegisterWorkerRequest) Descriptor() ([]byte, []int) { - return file_ateapi_proto_rawDescGZIP(), []int{109} + return file_ateapi_proto_rawDescGZIP(), []int{120} } func (x *RegisterWorkerRequest) GetWorker() *ObjectRef { @@ -7951,7 +8895,7 @@ type RegisterWorkerResponse struct { func (x *RegisterWorkerResponse) Reset() { *x = RegisterWorkerResponse{} - mi := &file_ateapi_proto_msgTypes[110] + mi := &file_ateapi_proto_msgTypes[121] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -7963,7 +8907,7 @@ func (x *RegisterWorkerResponse) String() string { func (*RegisterWorkerResponse) ProtoMessage() {} func (x *RegisterWorkerResponse) ProtoReflect() protoreflect.Message { - mi := &file_ateapi_proto_msgTypes[110] + mi := &file_ateapi_proto_msgTypes[121] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -7976,7 +8920,7 @@ func (x *RegisterWorkerResponse) ProtoReflect() protoreflect.Message { // Deprecated: Use RegisterWorkerResponse.ProtoReflect.Descriptor instead. func (*RegisterWorkerResponse) Descriptor() ([]byte, []int) { - return file_ateapi_proto_rawDescGZIP(), []int{110} + return file_ateapi_proto_rawDescGZIP(), []int{121} } func (x *RegisterWorkerResponse) GetWorker() *Worker { @@ -8023,7 +8967,7 @@ type MintWorkerActorCertificateRequest struct { func (x *MintWorkerActorCertificateRequest) Reset() { *x = MintWorkerActorCertificateRequest{} - mi := &file_ateapi_proto_msgTypes[111] + mi := &file_ateapi_proto_msgTypes[122] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -8035,7 +8979,7 @@ func (x *MintWorkerActorCertificateRequest) String() string { func (*MintWorkerActorCertificateRequest) ProtoMessage() {} func (x *MintWorkerActorCertificateRequest) ProtoReflect() protoreflect.Message { - mi := &file_ateapi_proto_msgTypes[111] + mi := &file_ateapi_proto_msgTypes[122] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -8048,7 +8992,7 @@ func (x *MintWorkerActorCertificateRequest) ProtoReflect() protoreflect.Message // Deprecated: Use MintWorkerActorCertificateRequest.ProtoReflect.Descriptor instead. func (*MintWorkerActorCertificateRequest) Descriptor() ([]byte, []int) { - return file_ateapi_proto_rawDescGZIP(), []int{111} + return file_ateapi_proto_rawDescGZIP(), []int{122} } func (x *MintWorkerActorCertificateRequest) GetActor() *ObjectRef { @@ -8092,7 +9036,7 @@ type MintWorkerActorCertificateResponse struct { func (x *MintWorkerActorCertificateResponse) Reset() { *x = MintWorkerActorCertificateResponse{} - mi := &file_ateapi_proto_msgTypes[112] + mi := &file_ateapi_proto_msgTypes[123] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -8104,7 +9048,7 @@ func (x *MintWorkerActorCertificateResponse) String() string { func (*MintWorkerActorCertificateResponse) ProtoMessage() {} func (x *MintWorkerActorCertificateResponse) ProtoReflect() protoreflect.Message { - mi := &file_ateapi_proto_msgTypes[112] + mi := &file_ateapi_proto_msgTypes[123] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -8117,7 +9061,7 @@ func (x *MintWorkerActorCertificateResponse) ProtoReflect() protoreflect.Message // Deprecated: Use MintWorkerActorCertificateResponse.ProtoReflect.Descriptor instead. func (*MintWorkerActorCertificateResponse) Descriptor() ([]byte, []int) { - return file_ateapi_proto_rawDescGZIP(), []int{112} + return file_ateapi_proto_rawDescGZIP(), []int{123} } func (x *MintWorkerActorCertificateResponse) GetActorCertificates() [][]byte { @@ -8158,7 +9102,7 @@ type MintWorkerActorJwtRequest struct { func (x *MintWorkerActorJwtRequest) Reset() { *x = MintWorkerActorJwtRequest{} - mi := &file_ateapi_proto_msgTypes[113] + mi := &file_ateapi_proto_msgTypes[124] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -8170,7 +9114,7 @@ func (x *MintWorkerActorJwtRequest) String() string { func (*MintWorkerActorJwtRequest) ProtoMessage() {} func (x *MintWorkerActorJwtRequest) ProtoReflect() protoreflect.Message { - mi := &file_ateapi_proto_msgTypes[113] + mi := &file_ateapi_proto_msgTypes[124] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -8183,7 +9127,7 @@ func (x *MintWorkerActorJwtRequest) ProtoReflect() protoreflect.Message { // Deprecated: Use MintWorkerActorJwtRequest.ProtoReflect.Descriptor instead. func (*MintWorkerActorJwtRequest) Descriptor() ([]byte, []int) { - return file_ateapi_proto_rawDescGZIP(), []int{113} + return file_ateapi_proto_rawDescGZIP(), []int{124} } func (x *MintWorkerActorJwtRequest) GetActor() *ObjectRef { @@ -8229,7 +9173,7 @@ type MintWorkerActorJwtResponse struct { func (x *MintWorkerActorJwtResponse) Reset() { *x = MintWorkerActorJwtResponse{} - mi := &file_ateapi_proto_msgTypes[114] + mi := &file_ateapi_proto_msgTypes[125] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -8241,7 +9185,7 @@ func (x *MintWorkerActorJwtResponse) String() string { func (*MintWorkerActorJwtResponse) ProtoMessage() {} func (x *MintWorkerActorJwtResponse) ProtoReflect() protoreflect.Message { - mi := &file_ateapi_proto_msgTypes[114] + mi := &file_ateapi_proto_msgTypes[125] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -8254,7 +9198,7 @@ func (x *MintWorkerActorJwtResponse) ProtoReflect() protoreflect.Message { // Deprecated: Use MintWorkerActorJwtResponse.ProtoReflect.Descriptor instead. func (*MintWorkerActorJwtResponse) Descriptor() ([]byte, []int) { - return file_ateapi_proto_rawDescGZIP(), []int{114} + return file_ateapi_proto_rawDescGZIP(), []int{125} } func (x *MintWorkerActorJwtResponse) GetActorJwt() string { @@ -8299,7 +9243,7 @@ type RequestActorSuspendRequest struct { func (x *RequestActorSuspendRequest) Reset() { *x = RequestActorSuspendRequest{} - mi := &file_ateapi_proto_msgTypes[115] + mi := &file_ateapi_proto_msgTypes[126] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -8311,7 +9255,7 @@ func (x *RequestActorSuspendRequest) String() string { func (*RequestActorSuspendRequest) ProtoMessage() {} func (x *RequestActorSuspendRequest) ProtoReflect() protoreflect.Message { - mi := &file_ateapi_proto_msgTypes[115] + mi := &file_ateapi_proto_msgTypes[126] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -8324,7 +9268,7 @@ func (x *RequestActorSuspendRequest) ProtoReflect() protoreflect.Message { // Deprecated: Use RequestActorSuspendRequest.ProtoReflect.Descriptor instead. func (*RequestActorSuspendRequest) Descriptor() ([]byte, []int) { - return file_ateapi_proto_rawDescGZIP(), []int{115} + return file_ateapi_proto_rawDescGZIP(), []int{126} } func (x *RequestActorSuspendRequest) GetWorker() *ObjectRef { @@ -8358,7 +9302,7 @@ type RequestActorSuspendResponse struct { func (x *RequestActorSuspendResponse) Reset() { *x = RequestActorSuspendResponse{} - mi := &file_ateapi_proto_msgTypes[116] + mi := &file_ateapi_proto_msgTypes[127] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -8370,7 +9314,7 @@ func (x *RequestActorSuspendResponse) String() string { func (*RequestActorSuspendResponse) ProtoMessage() {} func (x *RequestActorSuspendResponse) ProtoReflect() protoreflect.Message { - mi := &file_ateapi_proto_msgTypes[116] + mi := &file_ateapi_proto_msgTypes[127] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -8383,7 +9327,7 @@ func (x *RequestActorSuspendResponse) ProtoReflect() protoreflect.Message { // Deprecated: Use RequestActorSuspendResponse.ProtoReflect.Descriptor instead. func (*RequestActorSuspendResponse) Descriptor() ([]byte, []int) { - return file_ateapi_proto_rawDescGZIP(), []int{116} + return file_ateapi_proto_rawDescGZIP(), []int{127} } func (x *RequestActorSuspendResponse) GetActor() *Actor { @@ -8414,7 +9358,7 @@ type ReportActorSnapshotStateRequest struct { func (x *ReportActorSnapshotStateRequest) Reset() { *x = ReportActorSnapshotStateRequest{} - mi := &file_ateapi_proto_msgTypes[117] + mi := &file_ateapi_proto_msgTypes[128] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -8426,7 +9370,7 @@ func (x *ReportActorSnapshotStateRequest) String() string { func (*ReportActorSnapshotStateRequest) ProtoMessage() {} func (x *ReportActorSnapshotStateRequest) ProtoReflect() protoreflect.Message { - mi := &file_ateapi_proto_msgTypes[117] + mi := &file_ateapi_proto_msgTypes[128] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -8439,7 +9383,7 @@ func (x *ReportActorSnapshotStateRequest) ProtoReflect() protoreflect.Message { // Deprecated: Use ReportActorSnapshotStateRequest.ProtoReflect.Descriptor instead. func (*ReportActorSnapshotStateRequest) Descriptor() ([]byte, []int) { - return file_ateapi_proto_rawDescGZIP(), []int{117} + return file_ateapi_proto_rawDescGZIP(), []int{128} } func (x *ReportActorSnapshotStateRequest) GetActor() *ObjectRef { @@ -8471,7 +9415,7 @@ type ReportActorSnapshotStateResponse struct { func (x *ReportActorSnapshotStateResponse) Reset() { *x = ReportActorSnapshotStateResponse{} - mi := &file_ateapi_proto_msgTypes[118] + mi := &file_ateapi_proto_msgTypes[129] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -8483,7 +9427,7 @@ func (x *ReportActorSnapshotStateResponse) String() string { func (*ReportActorSnapshotStateResponse) ProtoMessage() {} func (x *ReportActorSnapshotStateResponse) ProtoReflect() protoreflect.Message { - mi := &file_ateapi_proto_msgTypes[118] + mi := &file_ateapi_proto_msgTypes[129] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -8496,7 +9440,7 @@ func (x *ReportActorSnapshotStateResponse) ProtoReflect() protoreflect.Message { // Deprecated: Use ReportActorSnapshotStateResponse.ProtoReflect.Descriptor instead. func (*ReportActorSnapshotStateResponse) Descriptor() ([]byte, []int) { - return file_ateapi_proto_rawDescGZIP(), []int{118} + return file_ateapi_proto_rawDescGZIP(), []int{129} } // ActorSnapshotState is what atelet holds for one Actor on its node. It @@ -8530,7 +9474,7 @@ type ActorSnapshotState struct { func (x *ActorSnapshotState) Reset() { *x = ActorSnapshotState{} - mi := &file_ateapi_proto_msgTypes[119] + mi := &file_ateapi_proto_msgTypes[130] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -8542,7 +9486,7 @@ func (x *ActorSnapshotState) String() string { func (*ActorSnapshotState) ProtoMessage() {} func (x *ActorSnapshotState) ProtoReflect() protoreflect.Message { - mi := &file_ateapi_proto_msgTypes[119] + mi := &file_ateapi_proto_msgTypes[130] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -8555,7 +9499,7 @@ func (x *ActorSnapshotState) ProtoReflect() protoreflect.Message { // Deprecated: Use ActorSnapshotState.ProtoReflect.Descriptor instead. func (*ActorSnapshotState) Descriptor() ([]byte, []int) { - return file_ateapi_proto_rawDescGZIP(), []int{119} + return file_ateapi_proto_rawDescGZIP(), []int{130} } func (x *ActorSnapshotState) GetLatestLocal() *SnapshotReport { @@ -8627,7 +9571,7 @@ type SnapshotReport struct { func (x *SnapshotReport) Reset() { *x = SnapshotReport{} - mi := &file_ateapi_proto_msgTypes[120] + mi := &file_ateapi_proto_msgTypes[131] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -8639,7 +9583,7 @@ func (x *SnapshotReport) String() string { func (*SnapshotReport) ProtoMessage() {} func (x *SnapshotReport) ProtoReflect() protoreflect.Message { - mi := &file_ateapi_proto_msgTypes[120] + mi := &file_ateapi_proto_msgTypes[131] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -8652,7 +9596,7 @@ func (x *SnapshotReport) ProtoReflect() protoreflect.Message { // Deprecated: Use SnapshotReport.ProtoReflect.Descriptor instead. func (*SnapshotReport) Descriptor() ([]byte, []int) { - return file_ateapi_proto_rawDescGZIP(), []int{120} + return file_ateapi_proto_rawDescGZIP(), []int{131} } func (x *SnapshotReport) GetUuid() string { @@ -8722,7 +9666,7 @@ type AccessPolicy struct { func (x *AccessPolicy) Reset() { *x = AccessPolicy{} - mi := &file_ateapi_proto_msgTypes[121] + mi := &file_ateapi_proto_msgTypes[132] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -8734,7 +9678,7 @@ func (x *AccessPolicy) String() string { func (*AccessPolicy) ProtoMessage() {} func (x *AccessPolicy) ProtoReflect() protoreflect.Message { - mi := &file_ateapi_proto_msgTypes[121] + mi := &file_ateapi_proto_msgTypes[132] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -8747,7 +9691,7 @@ func (x *AccessPolicy) ProtoReflect() protoreflect.Message { // Deprecated: Use AccessPolicy.ProtoReflect.Descriptor instead. func (*AccessPolicy) Descriptor() ([]byte, []int) { - return file_ateapi_proto_rawDescGZIP(), []int{121} + return file_ateapi_proto_rawDescGZIP(), []int{132} } func (x *AccessPolicy) GetMetadata() *ResourceMetadata { @@ -8789,7 +9733,7 @@ type Binding struct { func (x *Binding) Reset() { *x = Binding{} - mi := &file_ateapi_proto_msgTypes[122] + mi := &file_ateapi_proto_msgTypes[133] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -8801,7 +9745,7 @@ func (x *Binding) String() string { func (*Binding) ProtoMessage() {} func (x *Binding) ProtoReflect() protoreflect.Message { - mi := &file_ateapi_proto_msgTypes[122] + mi := &file_ateapi_proto_msgTypes[133] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -8814,7 +9758,7 @@ func (x *Binding) ProtoReflect() protoreflect.Message { // Deprecated: Use Binding.ProtoReflect.Descriptor instead. func (*Binding) Descriptor() ([]byte, []int) { - return file_ateapi_proto_rawDescGZIP(), []int{122} + return file_ateapi_proto_rawDescGZIP(), []int{133} } func (x *Binding) GetRole() string { @@ -8846,7 +9790,7 @@ type GetGlobalAccessPolicyRequest struct { func (x *GetGlobalAccessPolicyRequest) Reset() { *x = GetGlobalAccessPolicyRequest{} - mi := &file_ateapi_proto_msgTypes[123] + mi := &file_ateapi_proto_msgTypes[134] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -8858,7 +9802,7 @@ func (x *GetGlobalAccessPolicyRequest) String() string { func (*GetGlobalAccessPolicyRequest) ProtoMessage() {} func (x *GetGlobalAccessPolicyRequest) ProtoReflect() protoreflect.Message { - mi := &file_ateapi_proto_msgTypes[123] + mi := &file_ateapi_proto_msgTypes[134] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -8871,7 +9815,7 @@ func (x *GetGlobalAccessPolicyRequest) ProtoReflect() protoreflect.Message { // Deprecated: Use GetGlobalAccessPolicyRequest.ProtoReflect.Descriptor instead. func (*GetGlobalAccessPolicyRequest) Descriptor() ([]byte, []int) { - return file_ateapi_proto_rawDescGZIP(), []int{123} + return file_ateapi_proto_rawDescGZIP(), []int{134} } func (x *GetGlobalAccessPolicyRequest) GetName() string { @@ -8895,7 +9839,7 @@ type CreateGlobalAccessPolicyRequest struct { func (x *CreateGlobalAccessPolicyRequest) Reset() { *x = CreateGlobalAccessPolicyRequest{} - mi := &file_ateapi_proto_msgTypes[124] + mi := &file_ateapi_proto_msgTypes[135] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -8907,7 +9851,7 @@ func (x *CreateGlobalAccessPolicyRequest) String() string { func (*CreateGlobalAccessPolicyRequest) ProtoMessage() {} func (x *CreateGlobalAccessPolicyRequest) ProtoReflect() protoreflect.Message { - mi := &file_ateapi_proto_msgTypes[124] + mi := &file_ateapi_proto_msgTypes[135] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -8920,7 +9864,7 @@ func (x *CreateGlobalAccessPolicyRequest) ProtoReflect() protoreflect.Message { // Deprecated: Use CreateGlobalAccessPolicyRequest.ProtoReflect.Descriptor instead. func (*CreateGlobalAccessPolicyRequest) Descriptor() ([]byte, []int) { - return file_ateapi_proto_rawDescGZIP(), []int{124} + return file_ateapi_proto_rawDescGZIP(), []int{135} } func (x *CreateGlobalAccessPolicyRequest) GetAccessPolicy() *AccessPolicy { @@ -8944,7 +9888,7 @@ type UpdateGlobalAccessPolicyRequest struct { func (x *UpdateGlobalAccessPolicyRequest) Reset() { *x = UpdateGlobalAccessPolicyRequest{} - mi := &file_ateapi_proto_msgTypes[125] + mi := &file_ateapi_proto_msgTypes[136] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -8956,7 +9900,7 @@ func (x *UpdateGlobalAccessPolicyRequest) String() string { func (*UpdateGlobalAccessPolicyRequest) ProtoMessage() {} func (x *UpdateGlobalAccessPolicyRequest) ProtoReflect() protoreflect.Message { - mi := &file_ateapi_proto_msgTypes[125] + mi := &file_ateapi_proto_msgTypes[136] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -8969,7 +9913,7 @@ func (x *UpdateGlobalAccessPolicyRequest) ProtoReflect() protoreflect.Message { // Deprecated: Use UpdateGlobalAccessPolicyRequest.ProtoReflect.Descriptor instead. func (*UpdateGlobalAccessPolicyRequest) Descriptor() ([]byte, []int) { - return file_ateapi_proto_rawDescGZIP(), []int{125} + return file_ateapi_proto_rawDescGZIP(), []int{136} } func (x *UpdateGlobalAccessPolicyRequest) GetAccessPolicy() *AccessPolicy { @@ -8999,7 +9943,7 @@ type GetAtespaceAccessPolicyRequest struct { func (x *GetAtespaceAccessPolicyRequest) Reset() { *x = GetAtespaceAccessPolicyRequest{} - mi := &file_ateapi_proto_msgTypes[126] + mi := &file_ateapi_proto_msgTypes[137] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -9011,7 +9955,7 @@ func (x *GetAtespaceAccessPolicyRequest) String() string { func (*GetAtespaceAccessPolicyRequest) ProtoMessage() {} func (x *GetAtespaceAccessPolicyRequest) ProtoReflect() protoreflect.Message { - mi := &file_ateapi_proto_msgTypes[126] + mi := &file_ateapi_proto_msgTypes[137] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -9024,7 +9968,7 @@ func (x *GetAtespaceAccessPolicyRequest) ProtoReflect() protoreflect.Message { // Deprecated: Use GetAtespaceAccessPolicyRequest.ProtoReflect.Descriptor instead. func (*GetAtespaceAccessPolicyRequest) Descriptor() ([]byte, []int) { - return file_ateapi_proto_rawDescGZIP(), []int{126} + return file_ateapi_proto_rawDescGZIP(), []int{137} } func (x *GetAtespaceAccessPolicyRequest) GetAtespace() *ObjectRef { @@ -9060,7 +10004,7 @@ type CreateAtespaceAccessPolicyRequest struct { func (x *CreateAtespaceAccessPolicyRequest) Reset() { *x = CreateAtespaceAccessPolicyRequest{} - mi := &file_ateapi_proto_msgTypes[127] + mi := &file_ateapi_proto_msgTypes[138] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -9072,7 +10016,7 @@ func (x *CreateAtespaceAccessPolicyRequest) String() string { func (*CreateAtespaceAccessPolicyRequest) ProtoMessage() {} func (x *CreateAtespaceAccessPolicyRequest) ProtoReflect() protoreflect.Message { - mi := &file_ateapi_proto_msgTypes[127] + mi := &file_ateapi_proto_msgTypes[138] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -9085,7 +10029,7 @@ func (x *CreateAtespaceAccessPolicyRequest) ProtoReflect() protoreflect.Message // Deprecated: Use CreateAtespaceAccessPolicyRequest.ProtoReflect.Descriptor instead. func (*CreateAtespaceAccessPolicyRequest) Descriptor() ([]byte, []int) { - return file_ateapi_proto_rawDescGZIP(), []int{127} + return file_ateapi_proto_rawDescGZIP(), []int{138} } func (x *CreateAtespaceAccessPolicyRequest) GetAtespace() *ObjectRef { @@ -9121,7 +10065,7 @@ type UpdateAtespaceAccessPolicyRequest struct { func (x *UpdateAtespaceAccessPolicyRequest) Reset() { *x = UpdateAtespaceAccessPolicyRequest{} - mi := &file_ateapi_proto_msgTypes[128] + mi := &file_ateapi_proto_msgTypes[139] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -9133,7 +10077,7 @@ func (x *UpdateAtespaceAccessPolicyRequest) String() string { func (*UpdateAtespaceAccessPolicyRequest) ProtoMessage() {} func (x *UpdateAtespaceAccessPolicyRequest) ProtoReflect() protoreflect.Message { - mi := &file_ateapi_proto_msgTypes[128] + mi := &file_ateapi_proto_msgTypes[139] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -9146,7 +10090,7 @@ func (x *UpdateAtespaceAccessPolicyRequest) ProtoReflect() protoreflect.Message // Deprecated: Use UpdateAtespaceAccessPolicyRequest.ProtoReflect.Descriptor instead. func (*UpdateAtespaceAccessPolicyRequest) Descriptor() ([]byte, []int) { - return file_ateapi_proto_rawDescGZIP(), []int{128} + return file_ateapi_proto_rawDescGZIP(), []int{139} } func (x *UpdateAtespaceAccessPolicyRequest) GetAtespace() *ObjectRef { @@ -9187,7 +10131,7 @@ type DeleteAtespaceAccessPolicyRequest struct { func (x *DeleteAtespaceAccessPolicyRequest) Reset() { *x = DeleteAtespaceAccessPolicyRequest{} - mi := &file_ateapi_proto_msgTypes[129] + mi := &file_ateapi_proto_msgTypes[140] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -9199,7 +10143,7 @@ func (x *DeleteAtespaceAccessPolicyRequest) String() string { func (*DeleteAtespaceAccessPolicyRequest) ProtoMessage() {} func (x *DeleteAtespaceAccessPolicyRequest) ProtoReflect() protoreflect.Message { - mi := &file_ateapi_proto_msgTypes[129] + mi := &file_ateapi_proto_msgTypes[140] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -9212,7 +10156,7 @@ func (x *DeleteAtespaceAccessPolicyRequest) ProtoReflect() protoreflect.Message // Deprecated: Use DeleteAtespaceAccessPolicyRequest.ProtoReflect.Descriptor instead. func (*DeleteAtespaceAccessPolicyRequest) Descriptor() ([]byte, []int) { - return file_ateapi_proto_rawDescGZIP(), []int{129} + return file_ateapi_proto_rawDescGZIP(), []int{140} } func (x *DeleteAtespaceAccessPolicyRequest) GetAtespace() *ObjectRef { @@ -9258,7 +10202,7 @@ type ResourceAnnotation struct { func (x *ResourceAnnotation) Reset() { *x = ResourceAnnotation{} - mi := &file_ateapi_proto_msgTypes[130] + mi := &file_ateapi_proto_msgTypes[141] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -9270,7 +10214,7 @@ func (x *ResourceAnnotation) String() string { func (*ResourceAnnotation) ProtoMessage() {} func (x *ResourceAnnotation) ProtoReflect() protoreflect.Message { - mi := &file_ateapi_proto_msgTypes[130] + mi := &file_ateapi_proto_msgTypes[141] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -9283,7 +10227,7 @@ func (x *ResourceAnnotation) ProtoReflect() protoreflect.Message { // Deprecated: Use ResourceAnnotation.ProtoReflect.Descriptor instead. func (*ResourceAnnotation) Descriptor() ([]byte, []int) { - return file_ateapi_proto_rawDescGZIP(), []int{130} + return file_ateapi_proto_rawDescGZIP(), []int{141} } func (x *ResourceAnnotation) GetParents() []string { @@ -9326,7 +10270,7 @@ type MethodAnnotation struct { func (x *MethodAnnotation) Reset() { *x = MethodAnnotation{} - mi := &file_ateapi_proto_msgTypes[131] + mi := &file_ateapi_proto_msgTypes[142] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -9338,7 +10282,7 @@ func (x *MethodAnnotation) String() string { func (*MethodAnnotation) ProtoMessage() {} func (x *MethodAnnotation) ProtoReflect() protoreflect.Message { - mi := &file_ateapi_proto_msgTypes[131] + mi := &file_ateapi_proto_msgTypes[142] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -9351,7 +10295,7 @@ func (x *MethodAnnotation) ProtoReflect() protoreflect.Message { // Deprecated: Use MethodAnnotation.ProtoReflect.Descriptor instead. func (*MethodAnnotation) Descriptor() ([]byte, []int) { - return file_ateapi_proto_rawDescGZIP(), []int{131} + return file_ateapi_proto_rawDescGZIP(), []int{142} } func (x *MethodAnnotation) GetResource() string { @@ -9426,23 +10370,28 @@ const file_ateapi_proto_rawDesc = "" + "\vcreate_time\x18\x05 \x01(\v2\x1a.google.protobuf.TimestampR\n" + "createTime\x12;\n" + "\vupdate_time\x18\x06 \x01(\v2\x1a.google.protobuf.TimestampR\n" + - "updateTime\"\x85\x02\n" + + "updateTime\"\xb3\x02\n" + "\x0eExternalVolume\x12\x12\n" + "\x04name\x18\x01 \x01(\tR\x04name\x12*\n" + "\x11storage_volume_id\x18\x02 \x01(\tR\x0fstorageVolumeId\x12\x1f\n" + "\vvolume_type\x18\x03 \x01(\tR\n" + "volumeType\x12P\n" + - "\x0evolume_context\x18\x05 \x03(\v2).ateapi.ExternalVolume.VolumeContextEntryR\rvolumeContext\x1a@\n" + + "\x0evolume_context\x18\x05 \x03(\v2).ateapi.ExternalVolume.VolumeContextEntryR\rvolumeContext\x12,\n" + + "\x12shared_volume_name\x18\x06 \x01(\tR\x10sharedVolumeName\x1a@\n" + "\x12VolumeContextEntry\x12\x10\n" + "\x03key\x18\x01 \x01(\tR\x03key\x12\x14\n" + - "\x05value\x18\x02 \x01(\tR\x05value:\x028\x01\"\xa1\x02\n" + + "\x05value\x18\x02 \x01(\tR\x05value:\x028\x01\"\xc9\x03\n" + "\x05Actor\x124\n" + "\bmetadata\x18\x01 \x01(\v2\x18.ateapi.ResourceMetadataR\bmetadata\x128\n" + "\x0eactor_template\x18\x04 \x01(\v2\x11.ateapi.ObjectRefR\ractorTemplate\x129\n" + "\x0fworker_selector\x18\x05 \x01(\v2\x10.ateapi.SelectorR\x0eworkerSelector\x120\n" + "\n" + "source_tag\x18\x06 \x01(\v2\x11.ateapi.ObjectRefR\tsourceTag\x12+\n" + - "\x06status\x18\a \x01(\v2\x13.ateapi.ActorStatusR\x06status:\x0e\x82\xb5\x18\n" + + "\x06status\x18\a \x01(\v2\x13.ateapi.ActorStatusR\x06status\x12]\n" + + "\x16shared_volume_bindings\x18\b \x03(\v2'.ateapi.Actor.SharedVolumeBindingsEntryR\x14sharedVolumeBindings\x1aG\n" + + "\x19SharedVolumeBindingsEntry\x12\x10\n" + + "\x03key\x18\x01 \x01(\tR\x03key\x12\x14\n" + + "\x05value\x18\x02 \x01(\tR\x05value:\x028\x01:\x0e\x82\xb5\x18\n" + "\x18\x01\"\x06Actors\"\x8d\x01\n" + "\fEgressPolicy\x124\n" + "\bmetadata\x18\x01 \x01(\v2\x18.ateapi.ResourceMetadataR\bmetadata\x12(\n" + @@ -9514,7 +10463,30 @@ const file_ateapi_proto_rawDesc = "" + "\bmetadata\x18\x01 \x01(\v2\x18.ateapi.ResourceMetadataR\bmetadata\x12)\n" + "\x06status\x18\x02 \x01(\v2\x11.ateapi.TagStatusR\x06status\x12&\n" + "\x05scope\x18\x03 \x01(\x0e2\x10.ateapi.TagScopeR\x05scope\x124\n" + - "\fsource_actor\x18\x04 \x01(\v2\x11.ateapi.ObjectRefR\vsourceActor:\f\x82\xb5\x18\b\x18\x01\"\x04Tags\"S\n" + + "\fsource_actor\x18\x04 \x01(\v2\x11.ateapi.ObjectRefR\vsourceActor:\f\x82\xb5\x18\b\x18\x01\"\x04Tags\"\x9c\x04\n" + + "\fSharedVolume\x124\n" + + "\bmetadata\x18\x01 \x01(\v2\x18.ateapi.ResourceMetadataR\bmetadata\x12<\n" + + "\x0edelete_trigger\x18\x02 \x01(\x0e2\x15.ateapi.DeleteTriggerR\rdeleteTrigger\x12,\n" + + "\x12storage_class_name\x18\x03 \x01(\tR\x10storageClassName\x12\x1b\n" + + "\tvolume_id\x18\x04 \x01(\tR\bvolumeId\x12\x1f\n" + + "\vvolume_type\x18\x05 \x01(\tR\n" + + "volumeType\x12N\n" + + "\x0evolume_context\x18\x06 \x03(\v2'.ateapi.SharedVolume.VolumeContextEntryR\rvolumeContext\x122\n" + + "\x06status\x18\a \x01(\v2\x1a.ateapi.SharedVolumeStatusR\x06status\x12\x1a\n" + + "\bcapacity\x18\b \x01(\tR\bcapacity\x123\n" + + "\vaccess_mode\x18\t \x01(\x0e2\x12.ateapi.AccessModeR\n" + + "accessMode\x1a@\n" + + "\x12VolumeContextEntry\x12\x10\n" + + "\x03key\x18\x01 \x01(\tR\x03key\x12\x14\n" + + "\x05value\x18\x02 \x01(\tR\x05value:\x028\x01:\x15\x82\xb5\x18\x11\x18\x01\"\rSharedVolumes\"k\n" + + "\x12SharedVolumeStatus\x12/\n" + + "\x05state\x18\x01 \x01(\x0e2\x19.ateapi.SharedVolumeStateR\x05state\x12$\n" + + "\x04refs\x18\x02 \x03(\v2\x10.ateapi.ActorRefR\x04refs\"^\n" + + "\bActorRef\x12\x1b\n" + + "\tactor_uid\x18\x01 \x01(\tR\bactorUid\x12\x1d\n" + + "\n" + + "actor_name\x18\x02 \x01(\tR\tactorName\x12\x16\n" + + "\x06active\x18\x03 \x01(\bR\x06active\"S\n" + "\bAtespace\x124\n" + "\bmetadata\x18\x01 \x01(\v2\x18.ateapi.ResourceMetadataR\bmetadata:\x11\x82\xb5\x18\r\x18\x02\"\tAtespaces\";\n" + "\tObjectRef\x12\x1a\n" + @@ -9576,21 +10548,26 @@ const file_ateapi_proto_rawDesc = "" + "\x0ftimeout_seconds\x18\x02 \x01(\x05R\x0etimeoutSeconds\"7\n" + "\rHttpGetAction\x12\x12\n" + "\x04path\x18\x01 \x01(\tR\x04path\x12\x12\n" + - "\x04port\x18\x02 \x01(\x05R\x04port\"\xa9\x02\n" + + "\x04port\x18\x02 \x01(\x05R\x04port\"\x89\x02\n" + "\x06Volume\x12\x12\n" + "\x04name\x18\x01 \x01(\tR\x04name\x12?\n" + "\vdurable_dir\x18\x02 \x01(\v2\x1e.ateapi.DurableDirVolumeSourceR\n" + - "durableDir\x12X\n" + - "\x18external_volume_template\x18\x03 \x01(\v2\x1e.ateapi.ExternalVolumeTemplateR\x16externalVolumeTemplate\x12?\n" + + "durableDir\x128\n" + + "\bexternal\x18\a \x01(\v2\x1c.ateapi.ExternalVolumeSourceR\bexternal\x12?\n" + "\vsystem_info\x18\x05 \x01(\v2\x1e.ateapi.SystemInfoVolumeSourceR\n" + "systemInfo\x12/\n" + - "\x05image\x18\x06 \x01(\v2\x19.ateapi.ImageVolumeSourceR\x05image\"1\n" + + "\x05image\x18\x06 \x01(\v2\x19.ateapi.ImageVolumeSourceR\x05image\"\x90\x01\n" + + "\x14ExternalVolumeSource\x12:\n" + + "\btemplate\x18\x01 \x01(\v2\x1e.ateapi.ExternalVolumeTemplateR\btemplate\x12<\n" + + "\rshared_volume\x18\x02 \x01(\v2\x17.ateapi.SharedVolumeRefR\fsharedVolume\"1\n" + "\x11ImageVolumeSource\x12\x1c\n" + "\treference\x18\x01 \x01(\tR\treference\"\x18\n" + "\x16DurableDirVolumeSource\"b\n" + "\x16ExternalVolumeTemplate\x12\x1a\n" + "\bcapacity\x18\x01 \x01(\tR\bcapacity\x12,\n" + - "\x12storage_class_name\x18\x02 \x01(\tR\x10storageClassName\"Y\n" + + "\x12storage_class_name\x18\x02 \x01(\tR\x10storageClassName\"%\n" + + "\x0fSharedVolumeRef\x12\x12\n" + + "\x04name\x18\x01 \x01(\tR\x04name\"Y\n" + "\x16SystemInfoVolumeSource\x12?\n" + "\fdata_sources\x18\x01 \x03(\v2\x1c.ateapi.SystemInfoDataSourceR\vdataSources\"\xa0\x01\n" + "\x14SystemInfoDataSource\x12F\n" + @@ -9701,6 +10678,21 @@ const file_ateapi_proto_rawDesc = "" + "\x03tag\x18\x01 \x01(\v2\v.ateapi.TagR\x03tag\"h\n" + "\x10DeleteTagRequest\x12#\n" + "\x03tag\x18\x01 \x01(\v2\x11.ateapi.ObjectRefR\x03tag\x12/\n" + + "\aoptions\x18\x02 \x01(\v2\x15.ateapi.DeleteOptionsR\aoptions\"V\n" + + "\x19CreateSharedVolumeRequest\x129\n" + + "\rshared_volume\x18\x01 \x01(\v2\x14.ateapi.SharedVolumeR\fsharedVolume\"P\n" + + "\x16GetSharedVolumeRequest\x126\n" + + "\rshared_volume\x18\x01 \x01(\v2\x11.ateapi.ObjectRefR\fsharedVolume\"e\n" + + "\x18ListSharedVolumesRequest\x12\x1a\n" + + "\batespace\x18\x01 \x01(\tR\batespace\x12-\n" + + "\aoptions\x18\x02 \x01(\v2\x13.ateapi.ListOptionsR\aoptions\"\x80\x01\n" + + "\x19ListSharedVolumesResponse\x12;\n" + + "\x0eshared_volumes\x18\x01 \x03(\v2\x14.ateapi.SharedVolumeR\rsharedVolumes\x12&\n" + + "\x0fnext_page_token\x18\x02 \x01(\tR\rnextPageToken\"V\n" + + "\x19UpdateSharedVolumeRequest\x129\n" + + "\rshared_volume\x18\x01 \x01(\v2\x14.ateapi.SharedVolumeR\fsharedVolume\"\x84\x01\n" + + "\x19DeleteSharedVolumeRequest\x126\n" + + "\rshared_volume\x18\x01 \x01(\v2\x11.ateapi.ObjectRefR\fsharedVolume\x12/\n" + "\aoptions\x18\x02 \x01(\v2\x15.ateapi.DeleteOptionsR\aoptions\";\n" + "\rDeleteOptions\x12\x18\n" + "\aversion\x18\x01 \x01(\x03R\aversion\x12\x10\n" + @@ -9900,7 +10892,20 @@ const file_ateapi_proto_rawDesc = "" + "\x15ACTOR_STATE_SUSPENDED\x10\x04\x12\x17\n" + "\x13ACTOR_STATE_CRASHED\x10\a\x12\x18\n" + "\x14ACTOR_STATE_DELETING\x10\b\x12\x19\n" + - "\x15ACTOR_STATE_REVERTING\x10\t*\x7f\n" + + "\x15ACTOR_STATE_REVERTING\x10\t*i\n" + + "\rDeleteTrigger\x12\x1e\n" + + "\x1aDELETE_TRIGGER_UNSPECIFIED\x10\x00\x12\x19\n" + + "\x15DELETE_TRIGGER_MANUAL\x10\x01\x12\x1d\n" + + "\x19DELETE_TRIGGER_LAST_ACTOR\x10\x02*k\n" + + "\n" + + "AccessMode\x12\x1b\n" + + "\x17ACCESS_MODE_UNSPECIFIED\x10\x00\x12\x1f\n" + + "\x1bACCESS_MODE_READ_WRITE_ONCE\x10\x01\x12\x1f\n" + + "\x1bACCESS_MODE_READ_WRITE_MANY\x10\x02*x\n" + + "\x11SharedVolumeState\x12#\n" + + "\x1fSHARED_VOLUME_STATE_UNSPECIFIED\x10\x00\x12\x1f\n" + + "\x1bSHARED_VOLUME_STATE_PENDING\x10\x01\x12\x1d\n" + + "\x19SHARED_VOLUME_STATE_READY\x10\x02*\x7f\n" + "\x12GoldenSnapshotMode\x12$\n" + " GOLDEN_SNAPSHOT_MODE_UNSPECIFIED\x10\x00\x12 \n" + "\x1cGOLDEN_SNAPSHOT_MODE_ENABLED\x10\x01\x12!\n" + @@ -9918,7 +10923,7 @@ const file_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\xbb\x1f\n" + + "\fSCOPE_GLOBAL\x10\x022\xaf#\n" + "\aControl\x12?\n" + "\bGetActor\x12\x17.ateapi.GetActorRequest\x1a\r.ateapi.Actor\"\v\x82\xb5\x18\a\n" + "\x05Actor\x12E\n" + @@ -9958,7 +10963,17 @@ const file_ateapi_proto_rawDesc = "" + "\tUpdateTag\x12\x18.ateapi.UpdateTagRequest\x1a\v.ateapi.Tag\"\t\x82\xb5\x18\x05\n" + "\x03Tag\x12=\n" + "\tDeleteTag\x12\x18.ateapi.DeleteTagRequest\x1a\v.ateapi.Tag\"\t\x82\xb5\x18\x05\n" + - "\x03Tag\x12T\n" + + "\x03Tag\x12a\n" + + "\x12CreateSharedVolume\x12!.ateapi.CreateSharedVolumeRequest\x1a\x14.ateapi.SharedVolume\"\x12\x82\xb5\x18\x0e\n" + + "\fSharedVolume\x12[\n" + + "\x0fGetSharedVolume\x12\x1e.ateapi.GetSharedVolumeRequest\x1a\x14.ateapi.SharedVolume\"\x12\x82\xb5\x18\x0e\n" + + "\fSharedVolume\x12l\n" + + "\x11ListSharedVolumes\x12 .ateapi.ListSharedVolumesRequest\x1a!.ateapi.ListSharedVolumesResponse\"\x12\x82\xb5\x18\x0e\n" + + "\fSharedVolume\x12a\n" + + "\x12UpdateSharedVolume\x12!.ateapi.UpdateSharedVolumeRequest\x1a\x14.ateapi.SharedVolume\"\x12\x82\xb5\x18\x0e\n" + + "\fSharedVolume\x12a\n" + + "\x12DeleteSharedVolume\x12!.ateapi.DeleteSharedVolumeRequest\x1a\x14.ateapi.SharedVolume\"\x12\x82\xb5\x18\x0e\n" + + "\fSharedVolume\x12T\n" + "\vListWorkers\x12\x1a.ateapi.ListWorkersRequest\x1a\x1b.ateapi.ListWorkersResponse\"\f\x82\xb5\x18\b\n" + "\x06Worker\x12C\n" + "\tGetWorker\x12\x18.ateapi.GetWorkerRequest\x1a\x0e.ateapi.Worker\"\f\x82\xb5\x18\b\n" + @@ -10035,446 +11050,489 @@ func file_ateapi_proto_rawDescGZIP() []byte { return file_ateapi_proto_rawDescData } -var file_ateapi_proto_enumTypes = make([]protoimpl.EnumInfo, 9) -var file_ateapi_proto_msgTypes = make([]protoimpl.MessageInfo, 135) +var file_ateapi_proto_enumTypes = make([]protoimpl.EnumInfo, 12) +var file_ateapi_proto_msgTypes = make([]protoimpl.MessageInfo, 148) var file_ateapi_proto_goTypes = []any{ (SnapshotOwner)(0), // 0: ateapi.SnapshotOwner (SnapshotStorageStatus)(0), // 1: ateapi.SnapshotStorageStatus (SnapshotFidelity)(0), // 2: ateapi.SnapshotFidelity (TagScope)(0), // 3: ateapi.TagScope (ActorState)(0), // 4: ateapi.ActorState - (GoldenSnapshotMode)(0), // 5: ateapi.GoldenSnapshotMode - (ActorMetadataField)(0), // 6: ateapi.ActorMetadataField - (WorkerState)(0), // 7: ateapi.WorkerState - (Scope)(0), // 8: ateapi.Scope - (*ObjectSnapshot)(nil), // 9: ateapi.ObjectSnapshot - (*SnapshotStorage)(nil), // 10: ateapi.SnapshotStorage - (*Snapshot)(nil), // 11: ateapi.Snapshot - (*Selector)(nil), // 12: ateapi.Selector - (*ResourceMetadata)(nil), // 13: ateapi.ResourceMetadata - (*ExternalVolume)(nil), // 14: ateapi.ExternalVolume - (*Actor)(nil), // 15: ateapi.Actor - (*EgressPolicy)(nil), // 16: ateapi.EgressPolicy - (*EgressRule)(nil), // 17: ateapi.EgressRule - (*HttpRule)(nil), // 18: ateapi.HttpRule - (*HttpsRule)(nil), // 19: ateapi.HttpsRule - (*TlsPassthroughRule)(nil), // 20: ateapi.TlsPassthroughRule - (*Ports)(nil), // 21: ateapi.Ports - (*AllPorts)(nil), // 22: ateapi.AllPorts - (*HttpRuleEffects)(nil), // 23: ateapi.HttpRuleEffects - (*CredentialHeader)(nil), // 24: ateapi.CredentialHeader - (*ActorJwtSource)(nil), // 25: ateapi.ActorJwtSource - (*ActorStatus)(nil), // 26: ateapi.ActorStatus - (*ActorCrash)(nil), // 27: ateapi.ActorCrash - (*WorkerAssignment)(nil), // 28: ateapi.WorkerAssignment - (*TagStatus)(nil), // 29: ateapi.TagStatus - (*Tag)(nil), // 30: ateapi.Tag - (*Atespace)(nil), // 31: ateapi.Atespace - (*ObjectRef)(nil), // 32: ateapi.ObjectRef - (*ActorTemplate)(nil), // 33: ateapi.ActorTemplate - (*Resources)(nil), // 34: ateapi.Resources - (*Limits)(nil), // 35: ateapi.Limits - (*GoldenSnapshotStatus)(nil), // 36: ateapi.GoldenSnapshotStatus - (*ActorTemplateStatus)(nil), // 37: ateapi.ActorTemplateStatus - (*SandboxConfig)(nil), // 38: ateapi.SandboxConfig - (*SnapshotConfig)(nil), // 39: ateapi.SnapshotConfig - (*GoldenSnapshotConfig)(nil), // 40: ateapi.GoldenSnapshotConfig - (*Container)(nil), // 41: ateapi.Container - (*SecurityContext)(nil), // 42: ateapi.SecurityContext - (*Capabilities)(nil), // 43: ateapi.Capabilities - (*EnvVar)(nil), // 44: ateapi.EnvVar - (*WakeupProbe)(nil), // 45: ateapi.WakeupProbe - (*HttpGetAction)(nil), // 46: ateapi.HttpGetAction - (*Volume)(nil), // 47: ateapi.Volume - (*ImageVolumeSource)(nil), // 48: ateapi.ImageVolumeSource - (*DurableDirVolumeSource)(nil), // 49: ateapi.DurableDirVolumeSource - (*ExternalVolumeTemplate)(nil), // 50: ateapi.ExternalVolumeTemplate - (*SystemInfoVolumeSource)(nil), // 51: ateapi.SystemInfoVolumeSource - (*SystemInfoDataSource)(nil), // 52: ateapi.SystemInfoDataSource - (*ActorMetadataDataSource)(nil), // 53: ateapi.ActorMetadataDataSource - (*ActorMetadataItem)(nil), // 54: ateapi.ActorMetadataItem - (*TrustBundleDataSource)(nil), // 55: ateapi.TrustBundleDataSource - (*VolumeMount)(nil), // 56: ateapi.VolumeMount - (*CreateAtespaceRequest)(nil), // 57: ateapi.CreateAtespaceRequest - (*GetAtespaceRequest)(nil), // 58: ateapi.GetAtespaceRequest - (*ListAtespacesRequest)(nil), // 59: ateapi.ListAtespacesRequest - (*ListAtespacesResponse)(nil), // 60: ateapi.ListAtespacesResponse - (*DeleteAtespaceRequest)(nil), // 61: ateapi.DeleteAtespaceRequest - (*CreateActorTemplateRequest)(nil), // 62: ateapi.CreateActorTemplateRequest - (*GetActorTemplateRequest)(nil), // 63: ateapi.GetActorTemplateRequest - (*ListActorTemplatesRequest)(nil), // 64: ateapi.ListActorTemplatesRequest - (*ListActorTemplatesResponse)(nil), // 65: ateapi.ListActorTemplatesResponse - (*DeleteActorTemplateRequest)(nil), // 66: ateapi.DeleteActorTemplateRequest - (*GetActorRequest)(nil), // 67: ateapi.GetActorRequest - (*CreateActorRequest)(nil), // 68: ateapi.CreateActorRequest - (*UpdateActorRequest)(nil), // 69: ateapi.UpdateActorRequest - (*SuspendActorRequest)(nil), // 70: ateapi.SuspendActorRequest - (*SuspendActorResponse)(nil), // 71: ateapi.SuspendActorResponse - (*PauseActorRequest)(nil), // 72: ateapi.PauseActorRequest - (*PauseActorResponse)(nil), // 73: ateapi.PauseActorResponse - (*ResumeActorRequest)(nil), // 74: ateapi.ResumeActorRequest - (*ResumeActorResponse)(nil), // 75: ateapi.ResumeActorResponse - (*RevertActorRequest)(nil), // 76: ateapi.RevertActorRequest - (*RevertActorResponse)(nil), // 77: ateapi.RevertActorResponse - (*DeleteActorRequest)(nil), // 78: ateapi.DeleteActorRequest - (*GetActorEgressPolicyRequest)(nil), // 79: ateapi.GetActorEgressPolicyRequest - (*CreateActorEgressPolicyRequest)(nil), // 80: ateapi.CreateActorEgressPolicyRequest - (*UpdateActorEgressPolicyRequest)(nil), // 81: ateapi.UpdateActorEgressPolicyRequest - (*DeleteActorEgressPolicyRequest)(nil), // 82: ateapi.DeleteActorEgressPolicyRequest - (*GetTagRequest)(nil), // 83: ateapi.GetTagRequest - (*MintActorJwtRequest)(nil), // 84: ateapi.MintActorJwtRequest - (*MintActorJwtResponse)(nil), // 85: ateapi.MintActorJwtResponse - (*MintActorCertificateRequest)(nil), // 86: ateapi.MintActorCertificateRequest - (*MintActorCertificateResponse)(nil), // 87: ateapi.MintActorCertificateResponse - (*ListTagsRequest)(nil), // 88: ateapi.ListTagsRequest - (*ListTagsResponse)(nil), // 89: ateapi.ListTagsResponse - (*CreateTagRequest)(nil), // 90: ateapi.CreateTagRequest - (*UpdateTagRequest)(nil), // 91: ateapi.UpdateTagRequest - (*DeleteTagRequest)(nil), // 92: ateapi.DeleteTagRequest - (*DeleteOptions)(nil), // 93: ateapi.DeleteOptions - (*ListOptions)(nil), // 94: ateapi.ListOptions - (*ListWorkerActorAssignmentsRequest)(nil), // 95: ateapi.ListWorkerActorAssignmentsRequest - (*ListWorkerActorAssignmentsResponse)(nil), // 96: ateapi.ListWorkerActorAssignmentsResponse - (*ListWorkersRequest)(nil), // 97: ateapi.ListWorkersRequest - (*ListWorkersResponse)(nil), // 98: ateapi.ListWorkersResponse - (*GetWorkerRequest)(nil), // 99: ateapi.GetWorkerRequest - (*CreateWorkerRequest)(nil), // 100: ateapi.CreateWorkerRequest - (*UpdateWorkerRequest)(nil), // 101: ateapi.UpdateWorkerRequest - (*DeleteWorkerRequest)(nil), // 102: ateapi.DeleteWorkerRequest - (*DrainWorkerRequest)(nil), // 103: ateapi.DrainWorkerRequest - (*DrainWorkerResponse)(nil), // 104: ateapi.DrainWorkerResponse - (*MarkWorkerAvailableRequest)(nil), // 105: ateapi.MarkWorkerAvailableRequest - (*MarkWorkerAvailableResponse)(nil), // 106: ateapi.MarkWorkerAvailableResponse - (*MarkWorkerUnavailableRequest)(nil), // 107: ateapi.MarkWorkerUnavailableRequest - (*MarkWorkerUnavailableResponse)(nil), // 108: ateapi.MarkWorkerUnavailableResponse - (*ListActorsRequest)(nil), // 109: ateapi.ListActorsRequest - (*ListActorsResponse)(nil), // 110: ateapi.ListActorsResponse - (*Worker)(nil), // 111: ateapi.Worker - (*WorkerStatus)(nil), // 112: ateapi.WorkerStatus - (*SandboxRuntime)(nil), // 113: ateapi.SandboxRuntime - (*VersionedSandboxCompat)(nil), // 114: ateapi.VersionedSandboxCompat - (*AttributeEntry)(nil), // 115: ateapi.AttributeEntry - (*WorkerResources)(nil), // 116: ateapi.WorkerResources - (*ActorAssignment)(nil), // 117: ateapi.ActorAssignment - (*RegisterWorkerRequest)(nil), // 118: ateapi.RegisterWorkerRequest - (*RegisterWorkerResponse)(nil), // 119: ateapi.RegisterWorkerResponse - (*MintWorkerActorCertificateRequest)(nil), // 120: ateapi.MintWorkerActorCertificateRequest - (*MintWorkerActorCertificateResponse)(nil), // 121: ateapi.MintWorkerActorCertificateResponse - (*MintWorkerActorJwtRequest)(nil), // 122: ateapi.MintWorkerActorJwtRequest - (*MintWorkerActorJwtResponse)(nil), // 123: ateapi.MintWorkerActorJwtResponse - (*RequestActorSuspendRequest)(nil), // 124: ateapi.RequestActorSuspendRequest - (*RequestActorSuspendResponse)(nil), // 125: ateapi.RequestActorSuspendResponse - (*ReportActorSnapshotStateRequest)(nil), // 126: ateapi.ReportActorSnapshotStateRequest - (*ReportActorSnapshotStateResponse)(nil), // 127: ateapi.ReportActorSnapshotStateResponse - (*ActorSnapshotState)(nil), // 128: ateapi.ActorSnapshotState - (*SnapshotReport)(nil), // 129: ateapi.SnapshotReport - (*AccessPolicy)(nil), // 130: ateapi.AccessPolicy - (*Binding)(nil), // 131: ateapi.Binding - (*GetGlobalAccessPolicyRequest)(nil), // 132: ateapi.GetGlobalAccessPolicyRequest - (*CreateGlobalAccessPolicyRequest)(nil), // 133: ateapi.CreateGlobalAccessPolicyRequest - (*UpdateGlobalAccessPolicyRequest)(nil), // 134: ateapi.UpdateGlobalAccessPolicyRequest - (*GetAtespaceAccessPolicyRequest)(nil), // 135: ateapi.GetAtespaceAccessPolicyRequest - (*CreateAtespaceAccessPolicyRequest)(nil), // 136: ateapi.CreateAtespaceAccessPolicyRequest - (*UpdateAtespaceAccessPolicyRequest)(nil), // 137: ateapi.UpdateAtespaceAccessPolicyRequest - (*DeleteAtespaceAccessPolicyRequest)(nil), // 138: ateapi.DeleteAtespaceAccessPolicyRequest - (*ResourceAnnotation)(nil), // 139: ateapi.ResourceAnnotation - (*MethodAnnotation)(nil), // 140: ateapi.MethodAnnotation - nil, // 141: ateapi.Selector.MatchLabelsEntry - nil, // 142: ateapi.ExternalVolume.VolumeContextEntry - nil, // 143: ateapi.Worker.LabelsEntry - (*timestamppb.Timestamp)(nil), // 144: google.protobuf.Timestamp - (*descriptorpb.MessageOptions)(nil), // 145: google.protobuf.MessageOptions - (*descriptorpb.MethodOptions)(nil), // 146: google.protobuf.MethodOptions + (DeleteTrigger)(0), // 5: ateapi.DeleteTrigger + (AccessMode)(0), // 6: ateapi.AccessMode + (SharedVolumeState)(0), // 7: ateapi.SharedVolumeState + (GoldenSnapshotMode)(0), // 8: ateapi.GoldenSnapshotMode + (ActorMetadataField)(0), // 9: ateapi.ActorMetadataField + (WorkerState)(0), // 10: ateapi.WorkerState + (Scope)(0), // 11: ateapi.Scope + (*ObjectSnapshot)(nil), // 12: ateapi.ObjectSnapshot + (*SnapshotStorage)(nil), // 13: ateapi.SnapshotStorage + (*Snapshot)(nil), // 14: ateapi.Snapshot + (*Selector)(nil), // 15: ateapi.Selector + (*ResourceMetadata)(nil), // 16: ateapi.ResourceMetadata + (*ExternalVolume)(nil), // 17: ateapi.ExternalVolume + (*Actor)(nil), // 18: ateapi.Actor + (*EgressPolicy)(nil), // 19: ateapi.EgressPolicy + (*EgressRule)(nil), // 20: ateapi.EgressRule + (*HttpRule)(nil), // 21: ateapi.HttpRule + (*HttpsRule)(nil), // 22: ateapi.HttpsRule + (*TlsPassthroughRule)(nil), // 23: ateapi.TlsPassthroughRule + (*Ports)(nil), // 24: ateapi.Ports + (*AllPorts)(nil), // 25: ateapi.AllPorts + (*HttpRuleEffects)(nil), // 26: ateapi.HttpRuleEffects + (*CredentialHeader)(nil), // 27: ateapi.CredentialHeader + (*ActorJwtSource)(nil), // 28: ateapi.ActorJwtSource + (*ActorStatus)(nil), // 29: ateapi.ActorStatus + (*ActorCrash)(nil), // 30: ateapi.ActorCrash + (*WorkerAssignment)(nil), // 31: ateapi.WorkerAssignment + (*TagStatus)(nil), // 32: ateapi.TagStatus + (*Tag)(nil), // 33: ateapi.Tag + (*SharedVolume)(nil), // 34: ateapi.SharedVolume + (*SharedVolumeStatus)(nil), // 35: ateapi.SharedVolumeStatus + (*ActorRef)(nil), // 36: ateapi.ActorRef + (*Atespace)(nil), // 37: ateapi.Atespace + (*ObjectRef)(nil), // 38: ateapi.ObjectRef + (*ActorTemplate)(nil), // 39: ateapi.ActorTemplate + (*Resources)(nil), // 40: ateapi.Resources + (*Limits)(nil), // 41: ateapi.Limits + (*GoldenSnapshotStatus)(nil), // 42: ateapi.GoldenSnapshotStatus + (*ActorTemplateStatus)(nil), // 43: ateapi.ActorTemplateStatus + (*SandboxConfig)(nil), // 44: ateapi.SandboxConfig + (*SnapshotConfig)(nil), // 45: ateapi.SnapshotConfig + (*GoldenSnapshotConfig)(nil), // 46: ateapi.GoldenSnapshotConfig + (*Container)(nil), // 47: ateapi.Container + (*SecurityContext)(nil), // 48: ateapi.SecurityContext + (*Capabilities)(nil), // 49: ateapi.Capabilities + (*EnvVar)(nil), // 50: ateapi.EnvVar + (*WakeupProbe)(nil), // 51: ateapi.WakeupProbe + (*HttpGetAction)(nil), // 52: ateapi.HttpGetAction + (*Volume)(nil), // 53: ateapi.Volume + (*ExternalVolumeSource)(nil), // 54: ateapi.ExternalVolumeSource + (*ImageVolumeSource)(nil), // 55: ateapi.ImageVolumeSource + (*DurableDirVolumeSource)(nil), // 56: ateapi.DurableDirVolumeSource + (*ExternalVolumeTemplate)(nil), // 57: ateapi.ExternalVolumeTemplate + (*SharedVolumeRef)(nil), // 58: ateapi.SharedVolumeRef + (*SystemInfoVolumeSource)(nil), // 59: ateapi.SystemInfoVolumeSource + (*SystemInfoDataSource)(nil), // 60: ateapi.SystemInfoDataSource + (*ActorMetadataDataSource)(nil), // 61: ateapi.ActorMetadataDataSource + (*ActorMetadataItem)(nil), // 62: ateapi.ActorMetadataItem + (*TrustBundleDataSource)(nil), // 63: ateapi.TrustBundleDataSource + (*VolumeMount)(nil), // 64: ateapi.VolumeMount + (*CreateAtespaceRequest)(nil), // 65: ateapi.CreateAtespaceRequest + (*GetAtespaceRequest)(nil), // 66: ateapi.GetAtespaceRequest + (*ListAtespacesRequest)(nil), // 67: ateapi.ListAtespacesRequest + (*ListAtespacesResponse)(nil), // 68: ateapi.ListAtespacesResponse + (*DeleteAtespaceRequest)(nil), // 69: ateapi.DeleteAtespaceRequest + (*CreateActorTemplateRequest)(nil), // 70: ateapi.CreateActorTemplateRequest + (*GetActorTemplateRequest)(nil), // 71: ateapi.GetActorTemplateRequest + (*ListActorTemplatesRequest)(nil), // 72: ateapi.ListActorTemplatesRequest + (*ListActorTemplatesResponse)(nil), // 73: ateapi.ListActorTemplatesResponse + (*DeleteActorTemplateRequest)(nil), // 74: ateapi.DeleteActorTemplateRequest + (*GetActorRequest)(nil), // 75: ateapi.GetActorRequest + (*CreateActorRequest)(nil), // 76: ateapi.CreateActorRequest + (*UpdateActorRequest)(nil), // 77: ateapi.UpdateActorRequest + (*SuspendActorRequest)(nil), // 78: ateapi.SuspendActorRequest + (*SuspendActorResponse)(nil), // 79: ateapi.SuspendActorResponse + (*PauseActorRequest)(nil), // 80: ateapi.PauseActorRequest + (*PauseActorResponse)(nil), // 81: ateapi.PauseActorResponse + (*ResumeActorRequest)(nil), // 82: ateapi.ResumeActorRequest + (*ResumeActorResponse)(nil), // 83: ateapi.ResumeActorResponse + (*RevertActorRequest)(nil), // 84: ateapi.RevertActorRequest + (*RevertActorResponse)(nil), // 85: ateapi.RevertActorResponse + (*DeleteActorRequest)(nil), // 86: ateapi.DeleteActorRequest + (*GetActorEgressPolicyRequest)(nil), // 87: ateapi.GetActorEgressPolicyRequest + (*CreateActorEgressPolicyRequest)(nil), // 88: ateapi.CreateActorEgressPolicyRequest + (*UpdateActorEgressPolicyRequest)(nil), // 89: ateapi.UpdateActorEgressPolicyRequest + (*DeleteActorEgressPolicyRequest)(nil), // 90: ateapi.DeleteActorEgressPolicyRequest + (*GetTagRequest)(nil), // 91: ateapi.GetTagRequest + (*MintActorJwtRequest)(nil), // 92: ateapi.MintActorJwtRequest + (*MintActorJwtResponse)(nil), // 93: ateapi.MintActorJwtResponse + (*MintActorCertificateRequest)(nil), // 94: ateapi.MintActorCertificateRequest + (*MintActorCertificateResponse)(nil), // 95: ateapi.MintActorCertificateResponse + (*ListTagsRequest)(nil), // 96: ateapi.ListTagsRequest + (*ListTagsResponse)(nil), // 97: ateapi.ListTagsResponse + (*CreateTagRequest)(nil), // 98: ateapi.CreateTagRequest + (*UpdateTagRequest)(nil), // 99: ateapi.UpdateTagRequest + (*DeleteTagRequest)(nil), // 100: ateapi.DeleteTagRequest + (*CreateSharedVolumeRequest)(nil), // 101: ateapi.CreateSharedVolumeRequest + (*GetSharedVolumeRequest)(nil), // 102: ateapi.GetSharedVolumeRequest + (*ListSharedVolumesRequest)(nil), // 103: ateapi.ListSharedVolumesRequest + (*ListSharedVolumesResponse)(nil), // 104: ateapi.ListSharedVolumesResponse + (*UpdateSharedVolumeRequest)(nil), // 105: ateapi.UpdateSharedVolumeRequest + (*DeleteSharedVolumeRequest)(nil), // 106: ateapi.DeleteSharedVolumeRequest + (*DeleteOptions)(nil), // 107: ateapi.DeleteOptions + (*ListOptions)(nil), // 108: ateapi.ListOptions + (*ListWorkerActorAssignmentsRequest)(nil), // 109: ateapi.ListWorkerActorAssignmentsRequest + (*ListWorkerActorAssignmentsResponse)(nil), // 110: ateapi.ListWorkerActorAssignmentsResponse + (*ListWorkersRequest)(nil), // 111: ateapi.ListWorkersRequest + (*ListWorkersResponse)(nil), // 112: ateapi.ListWorkersResponse + (*GetWorkerRequest)(nil), // 113: ateapi.GetWorkerRequest + (*CreateWorkerRequest)(nil), // 114: ateapi.CreateWorkerRequest + (*UpdateWorkerRequest)(nil), // 115: ateapi.UpdateWorkerRequest + (*DeleteWorkerRequest)(nil), // 116: ateapi.DeleteWorkerRequest + (*DrainWorkerRequest)(nil), // 117: ateapi.DrainWorkerRequest + (*DrainWorkerResponse)(nil), // 118: ateapi.DrainWorkerResponse + (*MarkWorkerAvailableRequest)(nil), // 119: ateapi.MarkWorkerAvailableRequest + (*MarkWorkerAvailableResponse)(nil), // 120: ateapi.MarkWorkerAvailableResponse + (*MarkWorkerUnavailableRequest)(nil), // 121: ateapi.MarkWorkerUnavailableRequest + (*MarkWorkerUnavailableResponse)(nil), // 122: ateapi.MarkWorkerUnavailableResponse + (*ListActorsRequest)(nil), // 123: ateapi.ListActorsRequest + (*ListActorsResponse)(nil), // 124: ateapi.ListActorsResponse + (*Worker)(nil), // 125: ateapi.Worker + (*WorkerStatus)(nil), // 126: ateapi.WorkerStatus + (*SandboxRuntime)(nil), // 127: ateapi.SandboxRuntime + (*VersionedSandboxCompat)(nil), // 128: ateapi.VersionedSandboxCompat + (*AttributeEntry)(nil), // 129: ateapi.AttributeEntry + (*WorkerResources)(nil), // 130: ateapi.WorkerResources + (*ActorAssignment)(nil), // 131: ateapi.ActorAssignment + (*RegisterWorkerRequest)(nil), // 132: ateapi.RegisterWorkerRequest + (*RegisterWorkerResponse)(nil), // 133: ateapi.RegisterWorkerResponse + (*MintWorkerActorCertificateRequest)(nil), // 134: ateapi.MintWorkerActorCertificateRequest + (*MintWorkerActorCertificateResponse)(nil), // 135: ateapi.MintWorkerActorCertificateResponse + (*MintWorkerActorJwtRequest)(nil), // 136: ateapi.MintWorkerActorJwtRequest + (*MintWorkerActorJwtResponse)(nil), // 137: ateapi.MintWorkerActorJwtResponse + (*RequestActorSuspendRequest)(nil), // 138: ateapi.RequestActorSuspendRequest + (*RequestActorSuspendResponse)(nil), // 139: ateapi.RequestActorSuspendResponse + (*ReportActorSnapshotStateRequest)(nil), // 140: ateapi.ReportActorSnapshotStateRequest + (*ReportActorSnapshotStateResponse)(nil), // 141: ateapi.ReportActorSnapshotStateResponse + (*ActorSnapshotState)(nil), // 142: ateapi.ActorSnapshotState + (*SnapshotReport)(nil), // 143: ateapi.SnapshotReport + (*AccessPolicy)(nil), // 144: ateapi.AccessPolicy + (*Binding)(nil), // 145: ateapi.Binding + (*GetGlobalAccessPolicyRequest)(nil), // 146: ateapi.GetGlobalAccessPolicyRequest + (*CreateGlobalAccessPolicyRequest)(nil), // 147: ateapi.CreateGlobalAccessPolicyRequest + (*UpdateGlobalAccessPolicyRequest)(nil), // 148: ateapi.UpdateGlobalAccessPolicyRequest + (*GetAtespaceAccessPolicyRequest)(nil), // 149: ateapi.GetAtespaceAccessPolicyRequest + (*CreateAtespaceAccessPolicyRequest)(nil), // 150: ateapi.CreateAtespaceAccessPolicyRequest + (*UpdateAtespaceAccessPolicyRequest)(nil), // 151: ateapi.UpdateAtespaceAccessPolicyRequest + (*DeleteAtespaceAccessPolicyRequest)(nil), // 152: ateapi.DeleteAtespaceAccessPolicyRequest + (*ResourceAnnotation)(nil), // 153: ateapi.ResourceAnnotation + (*MethodAnnotation)(nil), // 154: ateapi.MethodAnnotation + nil, // 155: ateapi.Selector.MatchLabelsEntry + nil, // 156: ateapi.ExternalVolume.VolumeContextEntry + nil, // 157: ateapi.Actor.SharedVolumeBindingsEntry + nil, // 158: ateapi.SharedVolume.VolumeContextEntry + nil, // 159: ateapi.Worker.LabelsEntry + (*timestamppb.Timestamp)(nil), // 160: google.protobuf.Timestamp + (*descriptorpb.MessageOptions)(nil), // 161: google.protobuf.MessageOptions + (*descriptorpb.MethodOptions)(nil), // 162: google.protobuf.MethodOptions } var file_ateapi_proto_depIdxs = []int32{ 1, // 0: ateapi.SnapshotStorage.status:type_name -> ateapi.SnapshotStorageStatus - 9, // 1: ateapi.SnapshotStorage.object:type_name -> ateapi.ObjectSnapshot + 12, // 1: ateapi.SnapshotStorage.object:type_name -> ateapi.ObjectSnapshot 0, // 2: ateapi.Snapshot.owner:type_name -> ateapi.SnapshotOwner 2, // 3: ateapi.Snapshot.fidelity:type_name -> ateapi.SnapshotFidelity - 10, // 4: ateapi.Snapshot.durable_snapshot:type_name -> ateapi.SnapshotStorage - 113, // 5: ateapi.Snapshot.sandbox_runtime:type_name -> ateapi.SandboxRuntime - 141, // 6: ateapi.Selector.match_labels:type_name -> ateapi.Selector.MatchLabelsEntry - 144, // 7: ateapi.ResourceMetadata.create_time:type_name -> google.protobuf.Timestamp - 144, // 8: ateapi.ResourceMetadata.update_time:type_name -> google.protobuf.Timestamp - 142, // 9: ateapi.ExternalVolume.volume_context:type_name -> ateapi.ExternalVolume.VolumeContextEntry - 13, // 10: ateapi.Actor.metadata:type_name -> ateapi.ResourceMetadata - 32, // 11: ateapi.Actor.actor_template:type_name -> ateapi.ObjectRef - 12, // 12: ateapi.Actor.worker_selector:type_name -> ateapi.Selector - 32, // 13: ateapi.Actor.source_tag:type_name -> ateapi.ObjectRef - 26, // 14: ateapi.Actor.status:type_name -> ateapi.ActorStatus - 13, // 15: ateapi.EgressPolicy.metadata:type_name -> ateapi.ResourceMetadata - 17, // 16: ateapi.EgressPolicy.rules:type_name -> ateapi.EgressRule - 18, // 17: ateapi.EgressRule.http:type_name -> ateapi.HttpRule - 19, // 18: ateapi.EgressRule.https:type_name -> ateapi.HttpsRule - 20, // 19: ateapi.EgressRule.tls_passthrough:type_name -> ateapi.TlsPassthroughRule - 21, // 20: ateapi.HttpRule.ports:type_name -> ateapi.Ports - 23, // 21: ateapi.HttpRule.effects:type_name -> ateapi.HttpRuleEffects - 21, // 22: ateapi.HttpsRule.ports:type_name -> ateapi.Ports - 23, // 23: ateapi.HttpsRule.effects:type_name -> ateapi.HttpRuleEffects - 21, // 24: ateapi.TlsPassthroughRule.ports:type_name -> ateapi.Ports - 22, // 25: ateapi.Ports.all:type_name -> ateapi.AllPorts - 24, // 26: ateapi.HttpRuleEffects.replace_headers:type_name -> ateapi.CredentialHeader - 25, // 27: ateapi.CredentialHeader.actor_jwt:type_name -> ateapi.ActorJwtSource - 4, // 28: ateapi.ActorStatus.state:type_name -> ateapi.ActorState - 28, // 29: ateapi.ActorStatus.worker_assignment:type_name -> ateapi.WorkerAssignment - 14, // 30: ateapi.ActorStatus.external_volumes:type_name -> ateapi.ExternalVolume - 27, // 31: ateapi.ActorStatus.crash:type_name -> ateapi.ActorCrash - 11, // 32: ateapi.ActorStatus.snapshots:type_name -> ateapi.Snapshot - 144, // 33: ateapi.ActorCrash.crash_time:type_name -> google.protobuf.Timestamp - 32, // 34: ateapi.WorkerAssignment.worker:type_name -> ateapi.ObjectRef - 113, // 35: ateapi.WorkerAssignment.sandbox_runtime:type_name -> ateapi.SandboxRuntime - 11, // 36: ateapi.TagStatus.snapshot:type_name -> ateapi.Snapshot - 13, // 37: ateapi.Tag.metadata:type_name -> ateapi.ResourceMetadata - 29, // 38: ateapi.Tag.status:type_name -> ateapi.TagStatus - 3, // 39: ateapi.Tag.scope:type_name -> ateapi.TagScope - 32, // 40: ateapi.Tag.source_actor:type_name -> ateapi.ObjectRef - 13, // 41: ateapi.Atespace.metadata:type_name -> ateapi.ResourceMetadata - 13, // 42: ateapi.ActorTemplate.metadata:type_name -> ateapi.ResourceMetadata - 12, // 43: ateapi.ActorTemplate.worker_selector:type_name -> ateapi.Selector - 41, // 44: ateapi.ActorTemplate.containers:type_name -> ateapi.Container - 47, // 45: ateapi.ActorTemplate.volumes:type_name -> ateapi.Volume - 39, // 46: ateapi.ActorTemplate.snapshot_config:type_name -> ateapi.SnapshotConfig - 38, // 47: ateapi.ActorTemplate.sandbox_config:type_name -> ateapi.SandboxConfig - 34, // 48: ateapi.ActorTemplate.resources:type_name -> ateapi.Resources - 37, // 49: ateapi.ActorTemplate.status:type_name -> ateapi.ActorTemplateStatus - 35, // 50: ateapi.Resources.limits:type_name -> ateapi.Limits - 32, // 51: ateapi.GoldenSnapshotStatus.golden_tag:type_name -> ateapi.ObjectRef - 144, // 52: ateapi.GoldenSnapshotStatus.snapshot_time:type_name -> google.protobuf.Timestamp - 36, // 53: ateapi.ActorTemplateStatus.golden_snapshot_status:type_name -> ateapi.GoldenSnapshotStatus - 2, // 54: ateapi.SnapshotConfig.preferred_fidelity:type_name -> ateapi.SnapshotFidelity - 40, // 55: ateapi.SnapshotConfig.golden_snapshot_config:type_name -> ateapi.GoldenSnapshotConfig - 5, // 56: ateapi.GoldenSnapshotConfig.mode:type_name -> ateapi.GoldenSnapshotMode - 44, // 57: ateapi.Container.env:type_name -> ateapi.EnvVar - 45, // 58: ateapi.Container.wakeup_probe:type_name -> ateapi.WakeupProbe - 56, // 59: ateapi.Container.volume_mounts:type_name -> ateapi.VolumeMount - 42, // 60: ateapi.Container.security_context:type_name -> ateapi.SecurityContext - 34, // 61: ateapi.Container.resources:type_name -> ateapi.Resources - 43, // 62: ateapi.SecurityContext.capabilities:type_name -> ateapi.Capabilities - 46, // 63: ateapi.WakeupProbe.http_get:type_name -> ateapi.HttpGetAction - 49, // 64: ateapi.Volume.durable_dir:type_name -> ateapi.DurableDirVolumeSource - 50, // 65: ateapi.Volume.external_volume_template:type_name -> ateapi.ExternalVolumeTemplate - 51, // 66: ateapi.Volume.system_info:type_name -> ateapi.SystemInfoVolumeSource - 48, // 67: ateapi.Volume.image:type_name -> ateapi.ImageVolumeSource - 52, // 68: ateapi.SystemInfoVolumeSource.data_sources:type_name -> ateapi.SystemInfoDataSource - 53, // 69: ateapi.SystemInfoDataSource.actor_metadata:type_name -> ateapi.ActorMetadataDataSource - 55, // 70: ateapi.SystemInfoDataSource.trust_bundle:type_name -> ateapi.TrustBundleDataSource - 54, // 71: ateapi.ActorMetadataDataSource.items:type_name -> ateapi.ActorMetadataItem - 6, // 72: ateapi.ActorMetadataItem.field:type_name -> ateapi.ActorMetadataField - 31, // 73: ateapi.CreateAtespaceRequest.atespace:type_name -> ateapi.Atespace - 32, // 74: ateapi.GetAtespaceRequest.atespace:type_name -> ateapi.ObjectRef - 94, // 75: ateapi.ListAtespacesRequest.options:type_name -> ateapi.ListOptions - 31, // 76: ateapi.ListAtespacesResponse.atespaces:type_name -> ateapi.Atespace - 32, // 77: ateapi.DeleteAtespaceRequest.atespace:type_name -> ateapi.ObjectRef - 93, // 78: ateapi.DeleteAtespaceRequest.options:type_name -> ateapi.DeleteOptions - 33, // 79: ateapi.CreateActorTemplateRequest.actor_template:type_name -> ateapi.ActorTemplate - 32, // 80: ateapi.GetActorTemplateRequest.actor_template:type_name -> ateapi.ObjectRef - 94, // 81: ateapi.ListActorTemplatesRequest.options:type_name -> ateapi.ListOptions - 33, // 82: ateapi.ListActorTemplatesResponse.actor_templates:type_name -> ateapi.ActorTemplate - 32, // 83: ateapi.DeleteActorTemplateRequest.actor_template:type_name -> ateapi.ObjectRef - 93, // 84: ateapi.DeleteActorTemplateRequest.options:type_name -> ateapi.DeleteOptions - 32, // 85: ateapi.GetActorRequest.actor:type_name -> ateapi.ObjectRef - 15, // 86: ateapi.CreateActorRequest.actor:type_name -> ateapi.Actor - 15, // 87: ateapi.UpdateActorRequest.actor:type_name -> ateapi.Actor - 32, // 88: ateapi.SuspendActorRequest.actor:type_name -> ateapi.ObjectRef - 15, // 89: ateapi.SuspendActorResponse.actor:type_name -> ateapi.Actor - 32, // 90: ateapi.PauseActorRequest.actor:type_name -> ateapi.ObjectRef - 15, // 91: ateapi.PauseActorResponse.actor:type_name -> ateapi.Actor - 32, // 92: ateapi.ResumeActorRequest.actor:type_name -> ateapi.ObjectRef - 15, // 93: ateapi.ResumeActorResponse.actor:type_name -> ateapi.Actor - 32, // 94: ateapi.RevertActorRequest.actor:type_name -> ateapi.ObjectRef - 15, // 95: ateapi.RevertActorResponse.actor:type_name -> ateapi.Actor - 32, // 96: ateapi.DeleteActorRequest.actor:type_name -> ateapi.ObjectRef - 93, // 97: ateapi.DeleteActorRequest.options:type_name -> ateapi.DeleteOptions - 32, // 98: ateapi.GetActorEgressPolicyRequest.actor:type_name -> ateapi.ObjectRef - 32, // 99: ateapi.CreateActorEgressPolicyRequest.actor:type_name -> ateapi.ObjectRef - 16, // 100: ateapi.CreateActorEgressPolicyRequest.egress_policy:type_name -> ateapi.EgressPolicy - 32, // 101: ateapi.UpdateActorEgressPolicyRequest.actor:type_name -> ateapi.ObjectRef - 16, // 102: ateapi.UpdateActorEgressPolicyRequest.egress_policy:type_name -> ateapi.EgressPolicy - 32, // 103: ateapi.DeleteActorEgressPolicyRequest.actor:type_name -> ateapi.ObjectRef - 93, // 104: ateapi.DeleteActorEgressPolicyRequest.options:type_name -> ateapi.DeleteOptions - 32, // 105: ateapi.GetTagRequest.tag:type_name -> ateapi.ObjectRef - 32, // 106: ateapi.MintActorJwtRequest.actor:type_name -> ateapi.ObjectRef - 144, // 107: ateapi.MintActorJwtResponse.expire_time:type_name -> google.protobuf.Timestamp - 32, // 108: ateapi.MintActorCertificateRequest.actor:type_name -> ateapi.ObjectRef - 94, // 109: ateapi.ListTagsRequest.options:type_name -> ateapi.ListOptions - 30, // 110: ateapi.ListTagsResponse.tags:type_name -> ateapi.Tag - 30, // 111: ateapi.CreateTagRequest.tag:type_name -> ateapi.Tag - 30, // 112: ateapi.UpdateTagRequest.tag:type_name -> ateapi.Tag - 32, // 113: ateapi.DeleteTagRequest.tag:type_name -> ateapi.ObjectRef - 93, // 114: ateapi.DeleteTagRequest.options:type_name -> ateapi.DeleteOptions - 32, // 115: ateapi.ListWorkerActorAssignmentsRequest.worker:type_name -> ateapi.ObjectRef - 94, // 116: ateapi.ListWorkerActorAssignmentsRequest.options:type_name -> ateapi.ListOptions - 117, // 117: ateapi.ListWorkerActorAssignmentsResponse.actor_assignments:type_name -> ateapi.ActorAssignment - 94, // 118: ateapi.ListWorkersRequest.options:type_name -> ateapi.ListOptions - 111, // 119: ateapi.ListWorkersResponse.workers:type_name -> ateapi.Worker - 32, // 120: ateapi.GetWorkerRequest.worker:type_name -> ateapi.ObjectRef - 111, // 121: ateapi.CreateWorkerRequest.worker:type_name -> ateapi.Worker - 111, // 122: ateapi.UpdateWorkerRequest.worker:type_name -> ateapi.Worker - 32, // 123: ateapi.DeleteWorkerRequest.worker:type_name -> ateapi.ObjectRef - 93, // 124: ateapi.DeleteWorkerRequest.options:type_name -> ateapi.DeleteOptions - 32, // 125: ateapi.DrainWorkerRequest.worker:type_name -> ateapi.ObjectRef - 111, // 126: ateapi.DrainWorkerResponse.worker:type_name -> ateapi.Worker - 32, // 127: ateapi.MarkWorkerAvailableRequest.worker:type_name -> ateapi.ObjectRef - 111, // 128: ateapi.MarkWorkerAvailableResponse.worker:type_name -> ateapi.Worker - 32, // 129: ateapi.MarkWorkerUnavailableRequest.worker:type_name -> ateapi.ObjectRef - 111, // 130: ateapi.MarkWorkerUnavailableResponse.worker:type_name -> ateapi.Worker - 94, // 131: ateapi.ListActorsRequest.options:type_name -> ateapi.ListOptions - 15, // 132: ateapi.ListActorsResponse.actors:type_name -> ateapi.Actor - 13, // 133: ateapi.Worker.metadata:type_name -> ateapi.ResourceMetadata - 143, // 134: ateapi.Worker.labels:type_name -> ateapi.Worker.LabelsEntry - 112, // 135: ateapi.Worker.status:type_name -> ateapi.WorkerStatus - 7, // 136: ateapi.WorkerStatus.state:type_name -> ateapi.WorkerState - 116, // 137: ateapi.WorkerStatus.capacity:type_name -> ateapi.WorkerResources - 116, // 138: ateapi.WorkerStatus.allocated:type_name -> ateapi.WorkerResources - 113, // 139: ateapi.WorkerStatus.default_runtime:type_name -> ateapi.SandboxRuntime - 113, // 140: ateapi.WorkerStatus.restorable_runtimes:type_name -> ateapi.SandboxRuntime - 114, // 141: ateapi.SandboxRuntime.version:type_name -> ateapi.VersionedSandboxCompat - 115, // 142: ateapi.VersionedSandboxCompat.attributes:type_name -> ateapi.AttributeEntry - 34, // 143: ateapi.WorkerResources.resources:type_name -> ateapi.Resources - 13, // 144: ateapi.ActorAssignment.metadata:type_name -> ateapi.ResourceMetadata - 32, // 145: ateapi.ActorAssignment.actor:type_name -> ateapi.ObjectRef - 34, // 146: ateapi.ActorAssignment.resources:type_name -> ateapi.Resources - 32, // 147: ateapi.RegisterWorkerRequest.worker:type_name -> ateapi.ObjectRef - 116, // 148: ateapi.RegisterWorkerRequest.capacity:type_name -> ateapi.WorkerResources - 113, // 149: ateapi.RegisterWorkerRequest.default_runtime:type_name -> ateapi.SandboxRuntime - 113, // 150: ateapi.RegisterWorkerRequest.restorable_runtimes:type_name -> ateapi.SandboxRuntime - 111, // 151: ateapi.RegisterWorkerResponse.worker:type_name -> ateapi.Worker - 32, // 152: ateapi.MintWorkerActorCertificateRequest.actor:type_name -> ateapi.ObjectRef - 32, // 153: ateapi.MintWorkerActorCertificateRequest.worker:type_name -> ateapi.ObjectRef - 32, // 154: ateapi.MintWorkerActorJwtRequest.actor:type_name -> ateapi.ObjectRef - 144, // 155: ateapi.MintWorkerActorJwtResponse.expire_time:type_name -> google.protobuf.Timestamp - 32, // 156: ateapi.RequestActorSuspendRequest.worker:type_name -> ateapi.ObjectRef - 32, // 157: ateapi.RequestActorSuspendRequest.actor:type_name -> ateapi.ObjectRef - 15, // 158: ateapi.RequestActorSuspendResponse.actor:type_name -> ateapi.Actor - 32, // 159: ateapi.ReportActorSnapshotStateRequest.actor:type_name -> ateapi.ObjectRef - 128, // 160: ateapi.ReportActorSnapshotStateRequest.state:type_name -> ateapi.ActorSnapshotState - 129, // 161: ateapi.ActorSnapshotState.latest_local:type_name -> ateapi.SnapshotReport - 129, // 162: ateapi.ActorSnapshotState.uploading:type_name -> ateapi.SnapshotReport - 129, // 163: ateapi.ActorSnapshotState.latest_durable:type_name -> ateapi.SnapshotReport - 144, // 164: ateapi.ActorSnapshotState.upload_due_time:type_name -> google.protobuf.Timestamp - 2, // 165: ateapi.SnapshotReport.fidelity:type_name -> ateapi.SnapshotFidelity - 9, // 166: ateapi.SnapshotReport.object:type_name -> ateapi.ObjectSnapshot - 144, // 167: ateapi.SnapshotReport.local_commit_time:type_name -> google.protobuf.Timestamp - 144, // 168: ateapi.SnapshotReport.durable_complete_time:type_name -> google.protobuf.Timestamp - 13, // 169: ateapi.AccessPolicy.metadata:type_name -> ateapi.ResourceMetadata - 131, // 170: ateapi.AccessPolicy.bindings:type_name -> ateapi.Binding - 130, // 171: ateapi.CreateGlobalAccessPolicyRequest.access_policy:type_name -> ateapi.AccessPolicy - 130, // 172: ateapi.UpdateGlobalAccessPolicyRequest.access_policy:type_name -> ateapi.AccessPolicy - 32, // 173: ateapi.GetAtespaceAccessPolicyRequest.atespace:type_name -> ateapi.ObjectRef - 32, // 174: ateapi.CreateAtespaceAccessPolicyRequest.atespace:type_name -> ateapi.ObjectRef - 130, // 175: ateapi.CreateAtespaceAccessPolicyRequest.access_policy:type_name -> ateapi.AccessPolicy - 32, // 176: ateapi.UpdateAtespaceAccessPolicyRequest.atespace:type_name -> ateapi.ObjectRef - 130, // 177: ateapi.UpdateAtespaceAccessPolicyRequest.access_policy:type_name -> ateapi.AccessPolicy - 32, // 178: ateapi.DeleteAtespaceAccessPolicyRequest.atespace:type_name -> ateapi.ObjectRef - 93, // 179: ateapi.DeleteAtespaceAccessPolicyRequest.options:type_name -> ateapi.DeleteOptions - 8, // 180: ateapi.ResourceAnnotation.scope:type_name -> ateapi.Scope - 145, // 181: ateapi.resource:extendee -> google.protobuf.MessageOptions - 146, // 182: ateapi.method:extendee -> google.protobuf.MethodOptions - 139, // 183: ateapi.resource:type_name -> ateapi.ResourceAnnotation - 140, // 184: ateapi.method:type_name -> ateapi.MethodAnnotation - 67, // 185: ateapi.Control.GetActor:input_type -> ateapi.GetActorRequest - 68, // 186: ateapi.Control.CreateActor:input_type -> ateapi.CreateActorRequest - 69, // 187: ateapi.Control.UpdateActor:input_type -> ateapi.UpdateActorRequest - 70, // 188: ateapi.Control.SuspendActor:input_type -> ateapi.SuspendActorRequest - 72, // 189: ateapi.Control.PauseActor:input_type -> ateapi.PauseActorRequest - 74, // 190: ateapi.Control.ResumeActor:input_type -> ateapi.ResumeActorRequest - 76, // 191: ateapi.Control.RevertActor:input_type -> ateapi.RevertActorRequest - 78, // 192: ateapi.Control.DeleteActor:input_type -> ateapi.DeleteActorRequest - 79, // 193: ateapi.Control.GetActorEgressPolicy:input_type -> ateapi.GetActorEgressPolicyRequest - 80, // 194: ateapi.Control.CreateActorEgressPolicy:input_type -> ateapi.CreateActorEgressPolicyRequest - 81, // 195: ateapi.Control.UpdateActorEgressPolicy:input_type -> ateapi.UpdateActorEgressPolicyRequest - 82, // 196: ateapi.Control.DeleteActorEgressPolicy:input_type -> ateapi.DeleteActorEgressPolicyRequest - 84, // 197: ateapi.Control.MintActorJwt:input_type -> ateapi.MintActorJwtRequest - 86, // 198: ateapi.Control.MintActorCertificate:input_type -> ateapi.MintActorCertificateRequest - 90, // 199: ateapi.Control.CreateTag:input_type -> ateapi.CreateTagRequest - 83, // 200: ateapi.Control.GetTag:input_type -> ateapi.GetTagRequest - 88, // 201: ateapi.Control.ListTags:input_type -> ateapi.ListTagsRequest - 91, // 202: ateapi.Control.UpdateTag:input_type -> ateapi.UpdateTagRequest - 92, // 203: ateapi.Control.DeleteTag:input_type -> ateapi.DeleteTagRequest - 97, // 204: ateapi.Control.ListWorkers:input_type -> ateapi.ListWorkersRequest - 99, // 205: ateapi.Control.GetWorker:input_type -> ateapi.GetWorkerRequest - 100, // 206: ateapi.Control.CreateWorker:input_type -> ateapi.CreateWorkerRequest - 101, // 207: ateapi.Control.UpdateWorker:input_type -> ateapi.UpdateWorkerRequest - 102, // 208: ateapi.Control.DeleteWorker:input_type -> ateapi.DeleteWorkerRequest - 103, // 209: ateapi.Control.DrainWorker:input_type -> ateapi.DrainWorkerRequest - 105, // 210: ateapi.Control.MarkWorkerAvailable:input_type -> ateapi.MarkWorkerAvailableRequest - 107, // 211: ateapi.Control.MarkWorkerUnavailable:input_type -> ateapi.MarkWorkerUnavailableRequest - 95, // 212: ateapi.Control.ListWorkerActorAssignments:input_type -> ateapi.ListWorkerActorAssignmentsRequest - 109, // 213: ateapi.Control.ListActors:input_type -> ateapi.ListActorsRequest - 57, // 214: ateapi.Control.CreateAtespace:input_type -> ateapi.CreateAtespaceRequest - 58, // 215: ateapi.Control.GetAtespace:input_type -> ateapi.GetAtespaceRequest - 59, // 216: ateapi.Control.ListAtespaces:input_type -> ateapi.ListAtespacesRequest - 61, // 217: ateapi.Control.DeleteAtespace:input_type -> ateapi.DeleteAtespaceRequest - 62, // 218: ateapi.Control.CreateActorTemplate:input_type -> ateapi.CreateActorTemplateRequest - 63, // 219: ateapi.Control.GetActorTemplate:input_type -> ateapi.GetActorTemplateRequest - 64, // 220: ateapi.Control.ListActorTemplates:input_type -> ateapi.ListActorTemplatesRequest - 66, // 221: ateapi.Control.DeleteActorTemplate:input_type -> ateapi.DeleteActorTemplateRequest - 132, // 222: ateapi.Control.GetGlobalAccessPolicy:input_type -> ateapi.GetGlobalAccessPolicyRequest - 133, // 223: ateapi.Control.CreateGlobalAccessPolicy:input_type -> ateapi.CreateGlobalAccessPolicyRequest - 134, // 224: ateapi.Control.UpdateGlobalAccessPolicy:input_type -> ateapi.UpdateGlobalAccessPolicyRequest - 135, // 225: ateapi.Control.GetAtespaceAccessPolicy:input_type -> ateapi.GetAtespaceAccessPolicyRequest - 136, // 226: ateapi.Control.CreateAtespaceAccessPolicy:input_type -> ateapi.CreateAtespaceAccessPolicyRequest - 137, // 227: ateapi.Control.UpdateAtespaceAccessPolicy:input_type -> ateapi.UpdateAtespaceAccessPolicyRequest - 138, // 228: ateapi.Control.DeleteAtespaceAccessPolicy:input_type -> ateapi.DeleteAtespaceAccessPolicyRequest - 118, // 229: ateapi.WorkerService.RegisterWorker:input_type -> ateapi.RegisterWorkerRequest - 120, // 230: ateapi.WorkerService.MintWorkerActorCertificate:input_type -> ateapi.MintWorkerActorCertificateRequest - 122, // 231: ateapi.WorkerService.MintWorkerActorJwt:input_type -> ateapi.MintWorkerActorJwtRequest - 124, // 232: ateapi.WorkerService.RequestActorSuspend:input_type -> ateapi.RequestActorSuspendRequest - 126, // 233: ateapi.WorkerService.ReportActorSnapshotState:input_type -> ateapi.ReportActorSnapshotStateRequest - 15, // 234: ateapi.Control.GetActor:output_type -> ateapi.Actor - 15, // 235: ateapi.Control.CreateActor:output_type -> ateapi.Actor - 15, // 236: ateapi.Control.UpdateActor:output_type -> ateapi.Actor - 71, // 237: ateapi.Control.SuspendActor:output_type -> ateapi.SuspendActorResponse - 73, // 238: ateapi.Control.PauseActor:output_type -> ateapi.PauseActorResponse - 75, // 239: ateapi.Control.ResumeActor:output_type -> ateapi.ResumeActorResponse - 77, // 240: ateapi.Control.RevertActor:output_type -> ateapi.RevertActorResponse - 15, // 241: ateapi.Control.DeleteActor:output_type -> ateapi.Actor - 16, // 242: ateapi.Control.GetActorEgressPolicy:output_type -> ateapi.EgressPolicy - 16, // 243: ateapi.Control.CreateActorEgressPolicy:output_type -> ateapi.EgressPolicy - 16, // 244: ateapi.Control.UpdateActorEgressPolicy:output_type -> ateapi.EgressPolicy - 16, // 245: ateapi.Control.DeleteActorEgressPolicy:output_type -> ateapi.EgressPolicy - 85, // 246: ateapi.Control.MintActorJwt:output_type -> ateapi.MintActorJwtResponse - 87, // 247: ateapi.Control.MintActorCertificate:output_type -> ateapi.MintActorCertificateResponse - 30, // 248: ateapi.Control.CreateTag:output_type -> ateapi.Tag - 30, // 249: ateapi.Control.GetTag:output_type -> ateapi.Tag - 89, // 250: ateapi.Control.ListTags:output_type -> ateapi.ListTagsResponse - 30, // 251: ateapi.Control.UpdateTag:output_type -> ateapi.Tag - 30, // 252: ateapi.Control.DeleteTag:output_type -> ateapi.Tag - 98, // 253: ateapi.Control.ListWorkers:output_type -> ateapi.ListWorkersResponse - 111, // 254: ateapi.Control.GetWorker:output_type -> ateapi.Worker - 111, // 255: ateapi.Control.CreateWorker:output_type -> ateapi.Worker - 111, // 256: ateapi.Control.UpdateWorker:output_type -> ateapi.Worker - 111, // 257: ateapi.Control.DeleteWorker:output_type -> ateapi.Worker - 104, // 258: ateapi.Control.DrainWorker:output_type -> ateapi.DrainWorkerResponse - 106, // 259: ateapi.Control.MarkWorkerAvailable:output_type -> ateapi.MarkWorkerAvailableResponse - 108, // 260: ateapi.Control.MarkWorkerUnavailable:output_type -> ateapi.MarkWorkerUnavailableResponse - 96, // 261: ateapi.Control.ListWorkerActorAssignments:output_type -> ateapi.ListWorkerActorAssignmentsResponse - 110, // 262: ateapi.Control.ListActors:output_type -> ateapi.ListActorsResponse - 31, // 263: ateapi.Control.CreateAtespace:output_type -> ateapi.Atespace - 31, // 264: ateapi.Control.GetAtespace:output_type -> ateapi.Atespace - 60, // 265: ateapi.Control.ListAtespaces:output_type -> ateapi.ListAtespacesResponse - 31, // 266: ateapi.Control.DeleteAtespace:output_type -> ateapi.Atespace - 33, // 267: ateapi.Control.CreateActorTemplate:output_type -> ateapi.ActorTemplate - 33, // 268: ateapi.Control.GetActorTemplate:output_type -> ateapi.ActorTemplate - 65, // 269: ateapi.Control.ListActorTemplates:output_type -> ateapi.ListActorTemplatesResponse - 33, // 270: ateapi.Control.DeleteActorTemplate:output_type -> ateapi.ActorTemplate - 130, // 271: ateapi.Control.GetGlobalAccessPolicy:output_type -> ateapi.AccessPolicy - 130, // 272: ateapi.Control.CreateGlobalAccessPolicy:output_type -> ateapi.AccessPolicy - 130, // 273: ateapi.Control.UpdateGlobalAccessPolicy:output_type -> ateapi.AccessPolicy - 130, // 274: ateapi.Control.GetAtespaceAccessPolicy:output_type -> ateapi.AccessPolicy - 130, // 275: ateapi.Control.CreateAtespaceAccessPolicy:output_type -> ateapi.AccessPolicy - 130, // 276: ateapi.Control.UpdateAtespaceAccessPolicy:output_type -> ateapi.AccessPolicy - 130, // 277: ateapi.Control.DeleteAtespaceAccessPolicy:output_type -> ateapi.AccessPolicy - 119, // 278: ateapi.WorkerService.RegisterWorker:output_type -> ateapi.RegisterWorkerResponse - 121, // 279: ateapi.WorkerService.MintWorkerActorCertificate:output_type -> ateapi.MintWorkerActorCertificateResponse - 123, // 280: ateapi.WorkerService.MintWorkerActorJwt:output_type -> ateapi.MintWorkerActorJwtResponse - 125, // 281: ateapi.WorkerService.RequestActorSuspend:output_type -> ateapi.RequestActorSuspendResponse - 127, // 282: ateapi.WorkerService.ReportActorSnapshotState:output_type -> ateapi.ReportActorSnapshotStateResponse - 234, // [234:283] is the sub-list for method output_type - 185, // [185:234] is the sub-list for method input_type - 183, // [183:185] is the sub-list for extension type_name - 181, // [181:183] is the sub-list for extension extendee - 0, // [0:181] is the sub-list for field type_name + 13, // 4: ateapi.Snapshot.durable_snapshot:type_name -> ateapi.SnapshotStorage + 127, // 5: ateapi.Snapshot.sandbox_runtime:type_name -> ateapi.SandboxRuntime + 155, // 6: ateapi.Selector.match_labels:type_name -> ateapi.Selector.MatchLabelsEntry + 160, // 7: ateapi.ResourceMetadata.create_time:type_name -> google.protobuf.Timestamp + 160, // 8: ateapi.ResourceMetadata.update_time:type_name -> google.protobuf.Timestamp + 156, // 9: ateapi.ExternalVolume.volume_context:type_name -> ateapi.ExternalVolume.VolumeContextEntry + 16, // 10: ateapi.Actor.metadata:type_name -> ateapi.ResourceMetadata + 38, // 11: ateapi.Actor.actor_template:type_name -> ateapi.ObjectRef + 15, // 12: ateapi.Actor.worker_selector:type_name -> ateapi.Selector + 38, // 13: ateapi.Actor.source_tag:type_name -> ateapi.ObjectRef + 29, // 14: ateapi.Actor.status:type_name -> ateapi.ActorStatus + 157, // 15: ateapi.Actor.shared_volume_bindings:type_name -> ateapi.Actor.SharedVolumeBindingsEntry + 16, // 16: ateapi.EgressPolicy.metadata:type_name -> ateapi.ResourceMetadata + 20, // 17: ateapi.EgressPolicy.rules:type_name -> ateapi.EgressRule + 21, // 18: ateapi.EgressRule.http:type_name -> ateapi.HttpRule + 22, // 19: ateapi.EgressRule.https:type_name -> ateapi.HttpsRule + 23, // 20: ateapi.EgressRule.tls_passthrough:type_name -> ateapi.TlsPassthroughRule + 24, // 21: ateapi.HttpRule.ports:type_name -> ateapi.Ports + 26, // 22: ateapi.HttpRule.effects:type_name -> ateapi.HttpRuleEffects + 24, // 23: ateapi.HttpsRule.ports:type_name -> ateapi.Ports + 26, // 24: ateapi.HttpsRule.effects:type_name -> ateapi.HttpRuleEffects + 24, // 25: ateapi.TlsPassthroughRule.ports:type_name -> ateapi.Ports + 25, // 26: ateapi.Ports.all:type_name -> ateapi.AllPorts + 27, // 27: ateapi.HttpRuleEffects.replace_headers:type_name -> ateapi.CredentialHeader + 28, // 28: ateapi.CredentialHeader.actor_jwt:type_name -> ateapi.ActorJwtSource + 4, // 29: ateapi.ActorStatus.state:type_name -> ateapi.ActorState + 31, // 30: ateapi.ActorStatus.worker_assignment:type_name -> ateapi.WorkerAssignment + 17, // 31: ateapi.ActorStatus.external_volumes:type_name -> ateapi.ExternalVolume + 30, // 32: ateapi.ActorStatus.crash:type_name -> ateapi.ActorCrash + 14, // 33: ateapi.ActorStatus.snapshots:type_name -> ateapi.Snapshot + 160, // 34: ateapi.ActorCrash.crash_time:type_name -> google.protobuf.Timestamp + 38, // 35: ateapi.WorkerAssignment.worker:type_name -> ateapi.ObjectRef + 127, // 36: ateapi.WorkerAssignment.sandbox_runtime:type_name -> ateapi.SandboxRuntime + 14, // 37: ateapi.TagStatus.snapshot:type_name -> ateapi.Snapshot + 16, // 38: ateapi.Tag.metadata:type_name -> ateapi.ResourceMetadata + 32, // 39: ateapi.Tag.status:type_name -> ateapi.TagStatus + 3, // 40: ateapi.Tag.scope:type_name -> ateapi.TagScope + 38, // 41: ateapi.Tag.source_actor:type_name -> ateapi.ObjectRef + 16, // 42: ateapi.SharedVolume.metadata:type_name -> ateapi.ResourceMetadata + 5, // 43: ateapi.SharedVolume.delete_trigger:type_name -> ateapi.DeleteTrigger + 158, // 44: ateapi.SharedVolume.volume_context:type_name -> ateapi.SharedVolume.VolumeContextEntry + 35, // 45: ateapi.SharedVolume.status:type_name -> ateapi.SharedVolumeStatus + 6, // 46: ateapi.SharedVolume.access_mode:type_name -> ateapi.AccessMode + 7, // 47: ateapi.SharedVolumeStatus.state:type_name -> ateapi.SharedVolumeState + 36, // 48: ateapi.SharedVolumeStatus.refs:type_name -> ateapi.ActorRef + 16, // 49: ateapi.Atespace.metadata:type_name -> ateapi.ResourceMetadata + 16, // 50: ateapi.ActorTemplate.metadata:type_name -> ateapi.ResourceMetadata + 15, // 51: ateapi.ActorTemplate.worker_selector:type_name -> ateapi.Selector + 47, // 52: ateapi.ActorTemplate.containers:type_name -> ateapi.Container + 53, // 53: ateapi.ActorTemplate.volumes:type_name -> ateapi.Volume + 45, // 54: ateapi.ActorTemplate.snapshot_config:type_name -> ateapi.SnapshotConfig + 44, // 55: ateapi.ActorTemplate.sandbox_config:type_name -> ateapi.SandboxConfig + 40, // 56: ateapi.ActorTemplate.resources:type_name -> ateapi.Resources + 43, // 57: ateapi.ActorTemplate.status:type_name -> ateapi.ActorTemplateStatus + 41, // 58: ateapi.Resources.limits:type_name -> ateapi.Limits + 38, // 59: ateapi.GoldenSnapshotStatus.golden_tag:type_name -> ateapi.ObjectRef + 160, // 60: ateapi.GoldenSnapshotStatus.snapshot_time:type_name -> google.protobuf.Timestamp + 42, // 61: ateapi.ActorTemplateStatus.golden_snapshot_status:type_name -> ateapi.GoldenSnapshotStatus + 2, // 62: ateapi.SnapshotConfig.preferred_fidelity:type_name -> ateapi.SnapshotFidelity + 46, // 63: ateapi.SnapshotConfig.golden_snapshot_config:type_name -> ateapi.GoldenSnapshotConfig + 8, // 64: ateapi.GoldenSnapshotConfig.mode:type_name -> ateapi.GoldenSnapshotMode + 50, // 65: ateapi.Container.env:type_name -> ateapi.EnvVar + 51, // 66: ateapi.Container.wakeup_probe:type_name -> ateapi.WakeupProbe + 64, // 67: ateapi.Container.volume_mounts:type_name -> ateapi.VolumeMount + 48, // 68: ateapi.Container.security_context:type_name -> ateapi.SecurityContext + 40, // 69: ateapi.Container.resources:type_name -> ateapi.Resources + 49, // 70: ateapi.SecurityContext.capabilities:type_name -> ateapi.Capabilities + 52, // 71: ateapi.WakeupProbe.http_get:type_name -> ateapi.HttpGetAction + 56, // 72: ateapi.Volume.durable_dir:type_name -> ateapi.DurableDirVolumeSource + 54, // 73: ateapi.Volume.external:type_name -> ateapi.ExternalVolumeSource + 59, // 74: ateapi.Volume.system_info:type_name -> ateapi.SystemInfoVolumeSource + 55, // 75: ateapi.Volume.image:type_name -> ateapi.ImageVolumeSource + 57, // 76: ateapi.ExternalVolumeSource.template:type_name -> ateapi.ExternalVolumeTemplate + 58, // 77: ateapi.ExternalVolumeSource.shared_volume:type_name -> ateapi.SharedVolumeRef + 60, // 78: ateapi.SystemInfoVolumeSource.data_sources:type_name -> ateapi.SystemInfoDataSource + 61, // 79: ateapi.SystemInfoDataSource.actor_metadata:type_name -> ateapi.ActorMetadataDataSource + 63, // 80: ateapi.SystemInfoDataSource.trust_bundle:type_name -> ateapi.TrustBundleDataSource + 62, // 81: ateapi.ActorMetadataDataSource.items:type_name -> ateapi.ActorMetadataItem + 9, // 82: ateapi.ActorMetadataItem.field:type_name -> ateapi.ActorMetadataField + 37, // 83: ateapi.CreateAtespaceRequest.atespace:type_name -> ateapi.Atespace + 38, // 84: ateapi.GetAtespaceRequest.atespace:type_name -> ateapi.ObjectRef + 108, // 85: ateapi.ListAtespacesRequest.options:type_name -> ateapi.ListOptions + 37, // 86: ateapi.ListAtespacesResponse.atespaces:type_name -> ateapi.Atespace + 38, // 87: ateapi.DeleteAtespaceRequest.atespace:type_name -> ateapi.ObjectRef + 107, // 88: ateapi.DeleteAtespaceRequest.options:type_name -> ateapi.DeleteOptions + 39, // 89: ateapi.CreateActorTemplateRequest.actor_template:type_name -> ateapi.ActorTemplate + 38, // 90: ateapi.GetActorTemplateRequest.actor_template:type_name -> ateapi.ObjectRef + 108, // 91: ateapi.ListActorTemplatesRequest.options:type_name -> ateapi.ListOptions + 39, // 92: ateapi.ListActorTemplatesResponse.actor_templates:type_name -> ateapi.ActorTemplate + 38, // 93: ateapi.DeleteActorTemplateRequest.actor_template:type_name -> ateapi.ObjectRef + 107, // 94: ateapi.DeleteActorTemplateRequest.options:type_name -> ateapi.DeleteOptions + 38, // 95: ateapi.GetActorRequest.actor:type_name -> ateapi.ObjectRef + 18, // 96: ateapi.CreateActorRequest.actor:type_name -> ateapi.Actor + 18, // 97: ateapi.UpdateActorRequest.actor:type_name -> ateapi.Actor + 38, // 98: ateapi.SuspendActorRequest.actor:type_name -> ateapi.ObjectRef + 18, // 99: ateapi.SuspendActorResponse.actor:type_name -> ateapi.Actor + 38, // 100: ateapi.PauseActorRequest.actor:type_name -> ateapi.ObjectRef + 18, // 101: ateapi.PauseActorResponse.actor:type_name -> ateapi.Actor + 38, // 102: ateapi.ResumeActorRequest.actor:type_name -> ateapi.ObjectRef + 18, // 103: ateapi.ResumeActorResponse.actor:type_name -> ateapi.Actor + 38, // 104: ateapi.RevertActorRequest.actor:type_name -> ateapi.ObjectRef + 18, // 105: ateapi.RevertActorResponse.actor:type_name -> ateapi.Actor + 38, // 106: ateapi.DeleteActorRequest.actor:type_name -> ateapi.ObjectRef + 107, // 107: ateapi.DeleteActorRequest.options:type_name -> ateapi.DeleteOptions + 38, // 108: ateapi.GetActorEgressPolicyRequest.actor:type_name -> ateapi.ObjectRef + 38, // 109: ateapi.CreateActorEgressPolicyRequest.actor:type_name -> ateapi.ObjectRef + 19, // 110: ateapi.CreateActorEgressPolicyRequest.egress_policy:type_name -> ateapi.EgressPolicy + 38, // 111: ateapi.UpdateActorEgressPolicyRequest.actor:type_name -> ateapi.ObjectRef + 19, // 112: ateapi.UpdateActorEgressPolicyRequest.egress_policy:type_name -> ateapi.EgressPolicy + 38, // 113: ateapi.DeleteActorEgressPolicyRequest.actor:type_name -> ateapi.ObjectRef + 107, // 114: ateapi.DeleteActorEgressPolicyRequest.options:type_name -> ateapi.DeleteOptions + 38, // 115: ateapi.GetTagRequest.tag:type_name -> ateapi.ObjectRef + 38, // 116: ateapi.MintActorJwtRequest.actor:type_name -> ateapi.ObjectRef + 160, // 117: ateapi.MintActorJwtResponse.expire_time:type_name -> google.protobuf.Timestamp + 38, // 118: ateapi.MintActorCertificateRequest.actor:type_name -> ateapi.ObjectRef + 108, // 119: ateapi.ListTagsRequest.options:type_name -> ateapi.ListOptions + 33, // 120: ateapi.ListTagsResponse.tags:type_name -> ateapi.Tag + 33, // 121: ateapi.CreateTagRequest.tag:type_name -> ateapi.Tag + 33, // 122: ateapi.UpdateTagRequest.tag:type_name -> ateapi.Tag + 38, // 123: ateapi.DeleteTagRequest.tag:type_name -> ateapi.ObjectRef + 107, // 124: ateapi.DeleteTagRequest.options:type_name -> ateapi.DeleteOptions + 34, // 125: ateapi.CreateSharedVolumeRequest.shared_volume:type_name -> ateapi.SharedVolume + 38, // 126: ateapi.GetSharedVolumeRequest.shared_volume:type_name -> ateapi.ObjectRef + 108, // 127: ateapi.ListSharedVolumesRequest.options:type_name -> ateapi.ListOptions + 34, // 128: ateapi.ListSharedVolumesResponse.shared_volumes:type_name -> ateapi.SharedVolume + 34, // 129: ateapi.UpdateSharedVolumeRequest.shared_volume:type_name -> ateapi.SharedVolume + 38, // 130: ateapi.DeleteSharedVolumeRequest.shared_volume:type_name -> ateapi.ObjectRef + 107, // 131: ateapi.DeleteSharedVolumeRequest.options:type_name -> ateapi.DeleteOptions + 38, // 132: ateapi.ListWorkerActorAssignmentsRequest.worker:type_name -> ateapi.ObjectRef + 108, // 133: ateapi.ListWorkerActorAssignmentsRequest.options:type_name -> ateapi.ListOptions + 131, // 134: ateapi.ListWorkerActorAssignmentsResponse.actor_assignments:type_name -> ateapi.ActorAssignment + 108, // 135: ateapi.ListWorkersRequest.options:type_name -> ateapi.ListOptions + 125, // 136: ateapi.ListWorkersResponse.workers:type_name -> ateapi.Worker + 38, // 137: ateapi.GetWorkerRequest.worker:type_name -> ateapi.ObjectRef + 125, // 138: ateapi.CreateWorkerRequest.worker:type_name -> ateapi.Worker + 125, // 139: ateapi.UpdateWorkerRequest.worker:type_name -> ateapi.Worker + 38, // 140: ateapi.DeleteWorkerRequest.worker:type_name -> ateapi.ObjectRef + 107, // 141: ateapi.DeleteWorkerRequest.options:type_name -> ateapi.DeleteOptions + 38, // 142: ateapi.DrainWorkerRequest.worker:type_name -> ateapi.ObjectRef + 125, // 143: ateapi.DrainWorkerResponse.worker:type_name -> ateapi.Worker + 38, // 144: ateapi.MarkWorkerAvailableRequest.worker:type_name -> ateapi.ObjectRef + 125, // 145: ateapi.MarkWorkerAvailableResponse.worker:type_name -> ateapi.Worker + 38, // 146: ateapi.MarkWorkerUnavailableRequest.worker:type_name -> ateapi.ObjectRef + 125, // 147: ateapi.MarkWorkerUnavailableResponse.worker:type_name -> ateapi.Worker + 108, // 148: ateapi.ListActorsRequest.options:type_name -> ateapi.ListOptions + 18, // 149: ateapi.ListActorsResponse.actors:type_name -> ateapi.Actor + 16, // 150: ateapi.Worker.metadata:type_name -> ateapi.ResourceMetadata + 159, // 151: ateapi.Worker.labels:type_name -> ateapi.Worker.LabelsEntry + 126, // 152: ateapi.Worker.status:type_name -> ateapi.WorkerStatus + 10, // 153: ateapi.WorkerStatus.state:type_name -> ateapi.WorkerState + 130, // 154: ateapi.WorkerStatus.capacity:type_name -> ateapi.WorkerResources + 130, // 155: ateapi.WorkerStatus.allocated:type_name -> ateapi.WorkerResources + 127, // 156: ateapi.WorkerStatus.default_runtime:type_name -> ateapi.SandboxRuntime + 127, // 157: ateapi.WorkerStatus.restorable_runtimes:type_name -> ateapi.SandboxRuntime + 128, // 158: ateapi.SandboxRuntime.version:type_name -> ateapi.VersionedSandboxCompat + 129, // 159: ateapi.VersionedSandboxCompat.attributes:type_name -> ateapi.AttributeEntry + 40, // 160: ateapi.WorkerResources.resources:type_name -> ateapi.Resources + 16, // 161: ateapi.ActorAssignment.metadata:type_name -> ateapi.ResourceMetadata + 38, // 162: ateapi.ActorAssignment.actor:type_name -> ateapi.ObjectRef + 40, // 163: ateapi.ActorAssignment.resources:type_name -> ateapi.Resources + 38, // 164: ateapi.RegisterWorkerRequest.worker:type_name -> ateapi.ObjectRef + 130, // 165: ateapi.RegisterWorkerRequest.capacity:type_name -> ateapi.WorkerResources + 127, // 166: ateapi.RegisterWorkerRequest.default_runtime:type_name -> ateapi.SandboxRuntime + 127, // 167: ateapi.RegisterWorkerRequest.restorable_runtimes:type_name -> ateapi.SandboxRuntime + 125, // 168: ateapi.RegisterWorkerResponse.worker:type_name -> ateapi.Worker + 38, // 169: ateapi.MintWorkerActorCertificateRequest.actor:type_name -> ateapi.ObjectRef + 38, // 170: ateapi.MintWorkerActorCertificateRequest.worker:type_name -> ateapi.ObjectRef + 38, // 171: ateapi.MintWorkerActorJwtRequest.actor:type_name -> ateapi.ObjectRef + 160, // 172: ateapi.MintWorkerActorJwtResponse.expire_time:type_name -> google.protobuf.Timestamp + 38, // 173: ateapi.RequestActorSuspendRequest.worker:type_name -> ateapi.ObjectRef + 38, // 174: ateapi.RequestActorSuspendRequest.actor:type_name -> ateapi.ObjectRef + 18, // 175: ateapi.RequestActorSuspendResponse.actor:type_name -> ateapi.Actor + 38, // 176: ateapi.ReportActorSnapshotStateRequest.actor:type_name -> ateapi.ObjectRef + 142, // 177: ateapi.ReportActorSnapshotStateRequest.state:type_name -> ateapi.ActorSnapshotState + 143, // 178: ateapi.ActorSnapshotState.latest_local:type_name -> ateapi.SnapshotReport + 143, // 179: ateapi.ActorSnapshotState.uploading:type_name -> ateapi.SnapshotReport + 143, // 180: ateapi.ActorSnapshotState.latest_durable:type_name -> ateapi.SnapshotReport + 160, // 181: ateapi.ActorSnapshotState.upload_due_time:type_name -> google.protobuf.Timestamp + 2, // 182: ateapi.SnapshotReport.fidelity:type_name -> ateapi.SnapshotFidelity + 12, // 183: ateapi.SnapshotReport.object:type_name -> ateapi.ObjectSnapshot + 160, // 184: ateapi.SnapshotReport.local_commit_time:type_name -> google.protobuf.Timestamp + 160, // 185: ateapi.SnapshotReport.durable_complete_time:type_name -> google.protobuf.Timestamp + 16, // 186: ateapi.AccessPolicy.metadata:type_name -> ateapi.ResourceMetadata + 145, // 187: ateapi.AccessPolicy.bindings:type_name -> ateapi.Binding + 144, // 188: ateapi.CreateGlobalAccessPolicyRequest.access_policy:type_name -> ateapi.AccessPolicy + 144, // 189: ateapi.UpdateGlobalAccessPolicyRequest.access_policy:type_name -> ateapi.AccessPolicy + 38, // 190: ateapi.GetAtespaceAccessPolicyRequest.atespace:type_name -> ateapi.ObjectRef + 38, // 191: ateapi.CreateAtespaceAccessPolicyRequest.atespace:type_name -> ateapi.ObjectRef + 144, // 192: ateapi.CreateAtespaceAccessPolicyRequest.access_policy:type_name -> ateapi.AccessPolicy + 38, // 193: ateapi.UpdateAtespaceAccessPolicyRequest.atespace:type_name -> ateapi.ObjectRef + 144, // 194: ateapi.UpdateAtespaceAccessPolicyRequest.access_policy:type_name -> ateapi.AccessPolicy + 38, // 195: ateapi.DeleteAtespaceAccessPolicyRequest.atespace:type_name -> ateapi.ObjectRef + 107, // 196: ateapi.DeleteAtespaceAccessPolicyRequest.options:type_name -> ateapi.DeleteOptions + 11, // 197: ateapi.ResourceAnnotation.scope:type_name -> ateapi.Scope + 161, // 198: ateapi.resource:extendee -> google.protobuf.MessageOptions + 162, // 199: ateapi.method:extendee -> google.protobuf.MethodOptions + 153, // 200: ateapi.resource:type_name -> ateapi.ResourceAnnotation + 154, // 201: ateapi.method:type_name -> ateapi.MethodAnnotation + 75, // 202: ateapi.Control.GetActor:input_type -> ateapi.GetActorRequest + 76, // 203: ateapi.Control.CreateActor:input_type -> ateapi.CreateActorRequest + 77, // 204: ateapi.Control.UpdateActor:input_type -> ateapi.UpdateActorRequest + 78, // 205: ateapi.Control.SuspendActor:input_type -> ateapi.SuspendActorRequest + 80, // 206: ateapi.Control.PauseActor:input_type -> ateapi.PauseActorRequest + 82, // 207: ateapi.Control.ResumeActor:input_type -> ateapi.ResumeActorRequest + 84, // 208: ateapi.Control.RevertActor:input_type -> ateapi.RevertActorRequest + 86, // 209: ateapi.Control.DeleteActor:input_type -> ateapi.DeleteActorRequest + 87, // 210: ateapi.Control.GetActorEgressPolicy:input_type -> ateapi.GetActorEgressPolicyRequest + 88, // 211: ateapi.Control.CreateActorEgressPolicy:input_type -> ateapi.CreateActorEgressPolicyRequest + 89, // 212: ateapi.Control.UpdateActorEgressPolicy:input_type -> ateapi.UpdateActorEgressPolicyRequest + 90, // 213: ateapi.Control.DeleteActorEgressPolicy:input_type -> ateapi.DeleteActorEgressPolicyRequest + 92, // 214: ateapi.Control.MintActorJwt:input_type -> ateapi.MintActorJwtRequest + 94, // 215: ateapi.Control.MintActorCertificate:input_type -> ateapi.MintActorCertificateRequest + 98, // 216: ateapi.Control.CreateTag:input_type -> ateapi.CreateTagRequest + 91, // 217: ateapi.Control.GetTag:input_type -> ateapi.GetTagRequest + 96, // 218: ateapi.Control.ListTags:input_type -> ateapi.ListTagsRequest + 99, // 219: ateapi.Control.UpdateTag:input_type -> ateapi.UpdateTagRequest + 100, // 220: ateapi.Control.DeleteTag:input_type -> ateapi.DeleteTagRequest + 101, // 221: ateapi.Control.CreateSharedVolume:input_type -> ateapi.CreateSharedVolumeRequest + 102, // 222: ateapi.Control.GetSharedVolume:input_type -> ateapi.GetSharedVolumeRequest + 103, // 223: ateapi.Control.ListSharedVolumes:input_type -> ateapi.ListSharedVolumesRequest + 105, // 224: ateapi.Control.UpdateSharedVolume:input_type -> ateapi.UpdateSharedVolumeRequest + 106, // 225: ateapi.Control.DeleteSharedVolume:input_type -> ateapi.DeleteSharedVolumeRequest + 111, // 226: ateapi.Control.ListWorkers:input_type -> ateapi.ListWorkersRequest + 113, // 227: ateapi.Control.GetWorker:input_type -> ateapi.GetWorkerRequest + 114, // 228: ateapi.Control.CreateWorker:input_type -> ateapi.CreateWorkerRequest + 115, // 229: ateapi.Control.UpdateWorker:input_type -> ateapi.UpdateWorkerRequest + 116, // 230: ateapi.Control.DeleteWorker:input_type -> ateapi.DeleteWorkerRequest + 117, // 231: ateapi.Control.DrainWorker:input_type -> ateapi.DrainWorkerRequest + 119, // 232: ateapi.Control.MarkWorkerAvailable:input_type -> ateapi.MarkWorkerAvailableRequest + 121, // 233: ateapi.Control.MarkWorkerUnavailable:input_type -> ateapi.MarkWorkerUnavailableRequest + 109, // 234: ateapi.Control.ListWorkerActorAssignments:input_type -> ateapi.ListWorkerActorAssignmentsRequest + 123, // 235: ateapi.Control.ListActors:input_type -> ateapi.ListActorsRequest + 65, // 236: ateapi.Control.CreateAtespace:input_type -> ateapi.CreateAtespaceRequest + 66, // 237: ateapi.Control.GetAtespace:input_type -> ateapi.GetAtespaceRequest + 67, // 238: ateapi.Control.ListAtespaces:input_type -> ateapi.ListAtespacesRequest + 69, // 239: ateapi.Control.DeleteAtespace:input_type -> ateapi.DeleteAtespaceRequest + 70, // 240: ateapi.Control.CreateActorTemplate:input_type -> ateapi.CreateActorTemplateRequest + 71, // 241: ateapi.Control.GetActorTemplate:input_type -> ateapi.GetActorTemplateRequest + 72, // 242: ateapi.Control.ListActorTemplates:input_type -> ateapi.ListActorTemplatesRequest + 74, // 243: ateapi.Control.DeleteActorTemplate:input_type -> ateapi.DeleteActorTemplateRequest + 146, // 244: ateapi.Control.GetGlobalAccessPolicy:input_type -> ateapi.GetGlobalAccessPolicyRequest + 147, // 245: ateapi.Control.CreateGlobalAccessPolicy:input_type -> ateapi.CreateGlobalAccessPolicyRequest + 148, // 246: ateapi.Control.UpdateGlobalAccessPolicy:input_type -> ateapi.UpdateGlobalAccessPolicyRequest + 149, // 247: ateapi.Control.GetAtespaceAccessPolicy:input_type -> ateapi.GetAtespaceAccessPolicyRequest + 150, // 248: ateapi.Control.CreateAtespaceAccessPolicy:input_type -> ateapi.CreateAtespaceAccessPolicyRequest + 151, // 249: ateapi.Control.UpdateAtespaceAccessPolicy:input_type -> ateapi.UpdateAtespaceAccessPolicyRequest + 152, // 250: ateapi.Control.DeleteAtespaceAccessPolicy:input_type -> ateapi.DeleteAtespaceAccessPolicyRequest + 132, // 251: ateapi.WorkerService.RegisterWorker:input_type -> ateapi.RegisterWorkerRequest + 134, // 252: ateapi.WorkerService.MintWorkerActorCertificate:input_type -> ateapi.MintWorkerActorCertificateRequest + 136, // 253: ateapi.WorkerService.MintWorkerActorJwt:input_type -> ateapi.MintWorkerActorJwtRequest + 138, // 254: ateapi.WorkerService.RequestActorSuspend:input_type -> ateapi.RequestActorSuspendRequest + 140, // 255: ateapi.WorkerService.ReportActorSnapshotState:input_type -> ateapi.ReportActorSnapshotStateRequest + 18, // 256: ateapi.Control.GetActor:output_type -> ateapi.Actor + 18, // 257: ateapi.Control.CreateActor:output_type -> ateapi.Actor + 18, // 258: ateapi.Control.UpdateActor:output_type -> ateapi.Actor + 79, // 259: ateapi.Control.SuspendActor:output_type -> ateapi.SuspendActorResponse + 81, // 260: ateapi.Control.PauseActor:output_type -> ateapi.PauseActorResponse + 83, // 261: ateapi.Control.ResumeActor:output_type -> ateapi.ResumeActorResponse + 85, // 262: ateapi.Control.RevertActor:output_type -> ateapi.RevertActorResponse + 18, // 263: ateapi.Control.DeleteActor:output_type -> ateapi.Actor + 19, // 264: ateapi.Control.GetActorEgressPolicy:output_type -> ateapi.EgressPolicy + 19, // 265: ateapi.Control.CreateActorEgressPolicy:output_type -> ateapi.EgressPolicy + 19, // 266: ateapi.Control.UpdateActorEgressPolicy:output_type -> ateapi.EgressPolicy + 19, // 267: ateapi.Control.DeleteActorEgressPolicy:output_type -> ateapi.EgressPolicy + 93, // 268: ateapi.Control.MintActorJwt:output_type -> ateapi.MintActorJwtResponse + 95, // 269: ateapi.Control.MintActorCertificate:output_type -> ateapi.MintActorCertificateResponse + 33, // 270: ateapi.Control.CreateTag:output_type -> ateapi.Tag + 33, // 271: ateapi.Control.GetTag:output_type -> ateapi.Tag + 97, // 272: ateapi.Control.ListTags:output_type -> ateapi.ListTagsResponse + 33, // 273: ateapi.Control.UpdateTag:output_type -> ateapi.Tag + 33, // 274: ateapi.Control.DeleteTag:output_type -> ateapi.Tag + 34, // 275: ateapi.Control.CreateSharedVolume:output_type -> ateapi.SharedVolume + 34, // 276: ateapi.Control.GetSharedVolume:output_type -> ateapi.SharedVolume + 104, // 277: ateapi.Control.ListSharedVolumes:output_type -> ateapi.ListSharedVolumesResponse + 34, // 278: ateapi.Control.UpdateSharedVolume:output_type -> ateapi.SharedVolume + 34, // 279: ateapi.Control.DeleteSharedVolume:output_type -> ateapi.SharedVolume + 112, // 280: ateapi.Control.ListWorkers:output_type -> ateapi.ListWorkersResponse + 125, // 281: ateapi.Control.GetWorker:output_type -> ateapi.Worker + 125, // 282: ateapi.Control.CreateWorker:output_type -> ateapi.Worker + 125, // 283: ateapi.Control.UpdateWorker:output_type -> ateapi.Worker + 125, // 284: ateapi.Control.DeleteWorker:output_type -> ateapi.Worker + 118, // 285: ateapi.Control.DrainWorker:output_type -> ateapi.DrainWorkerResponse + 120, // 286: ateapi.Control.MarkWorkerAvailable:output_type -> ateapi.MarkWorkerAvailableResponse + 122, // 287: ateapi.Control.MarkWorkerUnavailable:output_type -> ateapi.MarkWorkerUnavailableResponse + 110, // 288: ateapi.Control.ListWorkerActorAssignments:output_type -> ateapi.ListWorkerActorAssignmentsResponse + 124, // 289: ateapi.Control.ListActors:output_type -> ateapi.ListActorsResponse + 37, // 290: ateapi.Control.CreateAtespace:output_type -> ateapi.Atespace + 37, // 291: ateapi.Control.GetAtespace:output_type -> ateapi.Atespace + 68, // 292: ateapi.Control.ListAtespaces:output_type -> ateapi.ListAtespacesResponse + 37, // 293: ateapi.Control.DeleteAtespace:output_type -> ateapi.Atespace + 39, // 294: ateapi.Control.CreateActorTemplate:output_type -> ateapi.ActorTemplate + 39, // 295: ateapi.Control.GetActorTemplate:output_type -> ateapi.ActorTemplate + 73, // 296: ateapi.Control.ListActorTemplates:output_type -> ateapi.ListActorTemplatesResponse + 39, // 297: ateapi.Control.DeleteActorTemplate:output_type -> ateapi.ActorTemplate + 144, // 298: ateapi.Control.GetGlobalAccessPolicy:output_type -> ateapi.AccessPolicy + 144, // 299: ateapi.Control.CreateGlobalAccessPolicy:output_type -> ateapi.AccessPolicy + 144, // 300: ateapi.Control.UpdateGlobalAccessPolicy:output_type -> ateapi.AccessPolicy + 144, // 301: ateapi.Control.GetAtespaceAccessPolicy:output_type -> ateapi.AccessPolicy + 144, // 302: ateapi.Control.CreateAtespaceAccessPolicy:output_type -> ateapi.AccessPolicy + 144, // 303: ateapi.Control.UpdateAtespaceAccessPolicy:output_type -> ateapi.AccessPolicy + 144, // 304: ateapi.Control.DeleteAtespaceAccessPolicy:output_type -> ateapi.AccessPolicy + 133, // 305: ateapi.WorkerService.RegisterWorker:output_type -> ateapi.RegisterWorkerResponse + 135, // 306: ateapi.WorkerService.MintWorkerActorCertificate:output_type -> ateapi.MintWorkerActorCertificateResponse + 137, // 307: ateapi.WorkerService.MintWorkerActorJwt:output_type -> ateapi.MintWorkerActorJwtResponse + 139, // 308: ateapi.WorkerService.RequestActorSuspend:output_type -> ateapi.RequestActorSuspendResponse + 141, // 309: ateapi.WorkerService.ReportActorSnapshotState:output_type -> ateapi.ReportActorSnapshotStateResponse + 256, // [256:310] is the sub-list for method output_type + 202, // [202:256] is the sub-list for method input_type + 200, // [200:202] is the sub-list for extension type_name + 198, // [198:200] is the sub-list for extension extendee + 0, // [0:198] is the sub-list for field type_name } func init() { file_ateapi_proto_init() } @@ -10487,8 +11545,8 @@ func file_ateapi_proto_init() { File: protoimpl.DescBuilder{ GoPackagePath: reflect.TypeOf(x{}).PkgPath(), RawDescriptor: unsafe.Slice(unsafe.StringData(file_ateapi_proto_rawDesc), len(file_ateapi_proto_rawDesc)), - NumEnums: 9, - NumMessages: 135, + NumEnums: 12, + NumMessages: 148, NumExtensions: 2, NumServices: 2, }, diff --git a/pkg/proto/ateapipb/ateapi.proto b/pkg/proto/ateapipb/ateapi.proto index 0ba0d4a3be..fc98a73439 100644 --- a/pkg/proto/ateapipb/ateapi.proto +++ b/pkg/proto/ateapipb/ateapi.proto @@ -135,6 +135,31 @@ service Control { option (method).resource = "Tag"; } + // Create a SharedVolume, provisioning it or registering an existing volume. + rpc CreateSharedVolume(CreateSharedVolumeRequest) returns (SharedVolume) { + option (method).resource = "SharedVolume"; + } + + // Get a SharedVolume. + rpc GetSharedVolume(GetSharedVolumeRequest) returns (SharedVolume) { + option (method).resource = "SharedVolume"; + } + + // List ExternalVolumes. + rpc ListSharedVolumes(ListSharedVolumesRequest) returns (ListSharedVolumesResponse) { + option (method).resource = "SharedVolume"; + } + + // Update a SharedVolume's delete trigger. + rpc UpdateSharedVolume(UpdateSharedVolumeRequest) returns (SharedVolume) { + option (method).resource = "SharedVolume"; + } + + // Delete a SharedVolume and its storage. Rejected while any Actor references it. + rpc DeleteSharedVolume(DeleteSharedVolumeRequest) returns (SharedVolume) { + option (method).resource = "SharedVolume"; + } + // List Workers. rpc ListWorkers(ListWorkersRequest) returns (ListWorkersResponse) { option (method).resource = "Worker"; @@ -527,6 +552,15 @@ message ExternalVolume { // +k8s:optional // +k8s:customValidation # at most 40 KiB in total, ten times the CSI limit for map fields map volume_context = 5; + + // shared_volume_name names the SharedVolume this mount references, in the + // actor's atespace. Empty when the actor owns the volume itself. + // + // +k8s:optional + // +k8s:format=k8s-short-name + // +k8s:update=NoModify + // +k8s:update=NoUnset + string shared_volume_name = 6; } message Actor { @@ -569,6 +603,17 @@ message Actor { // // +k8s:optional ActorStatus status = 7; + + // shared_volume_bindings maps each unnamed external.shared_volume in the + // template, by volume name, to the SharedVolume this actor mounts. Every + // unnamed ref must be bound; a named ref cannot be rebound. Set at creation. + // + // +k8s:optional + // +k8s:maxProperties=32 + // +k8s:eachKey=+k8s:format=k8s-short-name + // +k8s:eachVal=+k8s:format=k8s-short-name + // +k8s:immutable + map shared_volume_bindings = 8; } // EgressPolicy is an egress policy resource nested under an Actor. An Actor has @@ -1078,6 +1123,162 @@ message Tag { ObjectRef source_actor = 4; } +// DeleteTrigger is what removes a SharedVolume and its storage. +// TODO: add an ATESPACE trigger that removes the volume with its atespace. +enum DeleteTrigger { + DELETE_TRIGGER_UNSPECIFIED = 0; + // Only an explicit DeleteSharedVolume removes the volume. + DELETE_TRIGGER_MANUAL = 1; + // The volume is removed when the last actor referencing it is deleted. + DELETE_TRIGGER_LAST_ACTOR = 2; +} + +// AccessMode is how many resumed actors may hold the volume at once. +enum AccessMode { + ACCESS_MODE_UNSPECIFIED = 0; + // At most one resumed actor holds the volume. + ACCESS_MODE_READ_WRITE_ONCE = 1; + // Any number of resumed actors hold the volume, on any node. + ACCESS_MODE_READ_WRITE_MANY = 2; +} + +// SharedVolumeState is where a SharedVolume is in its own lifecycle. +enum SharedVolumeState { + SHARED_VOLUME_STATE_UNSPECIFIED = 0; + // The name is reserved but no volume handle is known yet. + SHARED_VOLUME_STATE_PENDING = 1; + // The volume has a handle and can be referenced. + SHARED_VOLUME_STATE_READY = 2; +} + +// SharedVolume is an atespace-scoped CSI volume whose lifetime is independent +// of any actor. Identity is (atespace, name); the storage-level ID is derived +// from the record's UID. +message SharedVolume { + option (resource) = { + scope : SCOPE_ATESPACE + plural : "SharedVolumes" + }; + + // Common resource metadata: atespace, name, uid, version, timestamps. + // + // +k8s:required + // +k8s:subfield(atespace)=+k8s:required + ResourceMetadata metadata = 1; + + // delete_trigger decides what removes the volume. Defaults to LAST_ACTOR when + // provisioned and MANUAL when registered. + // + // +k8s:optional + // +k8s:minimum=1 + // +k8s:maximum=2 # keep this in sync with the DeleteTrigger enum + // +k8s:mutable + DeleteTrigger delete_trigger = 2; + + // storage_class_name names the StorageClass to provision from, or documents + // the class of a registered volume. + // + // +k8s:optional + // +k8s:format=k8s-long-name + // +k8s:update=NoModify + // +k8s:update=NoUnset + string storage_class_name = 3; + + // volume_id is the storage system's volume ID. Supplied to register an + // existing volume, otherwise stamped once provisioning completes. Bounded and + // checked for control characters only. + // + // +k8s:optional + // +k8s:maxLength=256 + // +k8s:update=NoModify + // +k8s:update=NoUnset + // +k8s:customValidation # no control characters + string volume_id = 4; + + // volume_type is the CSI driver name. Required alongside volume_id when no + // StorageClass is named; otherwise taken from the class's provisioner. + // + // +k8s:optional + // +k8s:maxLength=253 + // +k8s:update=NoModify + // +k8s:update=NoUnset + // +k8s:customValidation + string volume_type = 5; + + // volume_context is driver metadata needed by the node plugin for mounting. + // Attachment metadata is per-node and deliberately not recorded here. + // + // +k8s:optional + // +k8s:maxProperties=32 + // +k8s:eachKey=+k8s:maxLength=128 + // +k8s:eachVal=+k8s:maxLength=256 + map volume_context = 6; + + // status is set by the control plane; it is ignored on write. + // + // +k8s:optional + SharedVolumeStatus status = 7; + + // capacity to provision, as a Kubernetes resource.Quantity string (e.g. + // "10Gi"). Required when provisioning, forbidden alongside volume_id. + // + // +k8s:optional + // +k8s:maxLength=64 + // +k8s:update=NoModify + // +k8s:update=NoUnset + // +k8s:customValidation # must parse as a resource.Quantity + string capacity = 8; + + // access_mode is fixed at create and defaults to READ_WRITE_ONCE. + // TODO: accept READ_WRITE_MANY once concurrent holders are supported. + // + // +k8s:optional + // +k8s:minimum=1 + // +k8s:maximum=2 # keep this in sync with the AccessMode enum + // +k8s:update=NoModify + // +k8s:update=NoUnset + AccessMode access_mode = 9; +} + +// SharedVolumeStatus is the part of a SharedVolume the control plane owns. +message SharedVolumeStatus { + // state is where the volume is in its lifecycle. + // + // +k8s:optional + // +k8s:minimum=1 + // +k8s:maximum=2 # keep this in sync with the SharedVolumeState enum + SharedVolumeState state = 1; + + // refs are the actors referencing the volume, resumed or not. + // TODO: move references to their own table before lifting the bound. + // + // +k8s:optional + // +k8s:maxItems=256 + // +k8s:listType=map + // +k8s:listMapKey=actor_uid + repeated ActorRef refs = 2; +} + +// ActorRef is one actor's reference to a SharedVolume. +message ActorRef { + // actor_uid identifies the actor, so a recreated namesake does not inherit it. + // + // +k8s:required + // +k8s:maxLength=253 + string actor_uid = 1; + + // actor_name is the actor's name, for readers. + // + // +k8s:required + // +k8s:format=k8s-short-name + string actor_name = 2; + + // active is true while the actor is resumed and holds the volume mounted. + // + // +k8s:optional + bool active = 3; +} + // Atespace is the isolation boundary an Actor is created into. Global-scoped: // metadata.atespace is always empty; the atespace's identity is metadata.name. message Atespace { @@ -1451,18 +1652,21 @@ message Volume { // +k8s:format=k8s-short-name string name = 1; - // Exactly one of durable_dir / external_volume_template / image / - // system_info must be set. + // Exactly one of durable_dir / external / image / system_info must be + // set. // // +k8s:optional // +k8s:unionMember DurableDirVolumeSource durable_dir = 2; + // external is a CSI volume: provisioned for this actor from a template. + // // +ate:preview=ExternalVolumes // +k8s:ifDisabled(ExternalVolumes)=+k8s:forbidden // +k8s:optional // +k8s:unionMember - ExternalVolumeTemplate external_volume_template = 3; + ExternalVolumeSource external = 7; + // +k8s:optional // +k8s:unionMember @@ -1475,6 +1679,22 @@ message Volume { ImageVolumeSource image = 6; } +// ExternalVolumeSource is a CSI volume provisioned for the actor from a template. +message ExternalVolumeSource { + // template provisions a volume for this actor alone; it is deleted with the + // actor. + // + // +k8s:optional + // +k8s:unionMember + ExternalVolumeTemplate template = 1; + + // shared_volume mounts a SharedVolume that already exists. + // + // +k8s:optional + // +k8s:unionMember + SharedVolumeRef shared_volume = 2; +} + // ImageVolumeSource mounts the contents of an OCI image, read-only. The // reference must include a digest: changing the image invalidates // snapshots. @@ -1511,6 +1731,21 @@ message ExternalVolumeTemplate { string storage_class_name = 2; } +// SharedVolumeRef mounts a SharedVolume in the actor's atespace. The +// volume outlives the actor, so several actors can mount it in turn. +// +// Actors created from a template with a SharedVolumeRef do not start from +// the template's golden snapshot: the first resume boots from the image, and +// later resumes restore the actor's own snapshot. +message SharedVolumeRef { + // name of the SharedVolume. Left empty, the actor names it at creation in + // Actor.shared_volume_bindings. + // + // +k8s:optional + // +k8s:format=k8s-short-name + string name = 1; +} + // SystemInfoVolumeSource is a read-only volume of substrate-generated // per-actor files (identity fields, projected trust bundles), regenerated by // atelet on every Run/Restore. @@ -2045,6 +2280,75 @@ message DeleteTagRequest { DeleteOptions options = 2; } +// Request to create a SharedVolume. +// +// +k8s:customValidation # exactly one of storage_class_name and volume_id +message CreateSharedVolumeRequest { + // The volume to create. Name a storage class to provision a new volume, or + // supply a volume ID to register one that already exists. status is ignored. + // + // +k8s:required + SharedVolume shared_volume = 1; +} + +// GetSharedVolumeRequest identifies one SharedVolume. +message GetSharedVolumeRequest { + // The volume to get. + // + // +k8s:required + // +k8s:subfield(atespace)=+k8s:required + ObjectRef shared_volume = 1; +} + +// ListSharedVolumesRequest selects a page of ExternalVolumes. +message ListSharedVolumesRequest { + // The atespace to list from. Empty lists across all atespaces. + // + // +k8s:optional + // +k8s:format=k8s-short-name + string atespace = 1; + + // options selects the page size and continuation token. + // + // +k8s:optional + ListOptions options = 2; +} + +// ListSharedVolumesResponse is one page of ExternalVolumes. +message ListSharedVolumesResponse { + // The volumes in this page. + repeated SharedVolume shared_volumes = 1; + + // Pagination token for the next page. Empty on the last page. + string next_page_token = 2; +} + +// Request to update the mutable fields of a SharedVolume. +message UpdateSharedVolumeRequest { + // The volume to update. metadata.atespace and metadata.name identify it; + // metadata.version and metadata.uid are required preconditions. + // + // +k8s:required + // +k8s:opaqueType # updates are handled in 2 steps, do not descend + // +k8s:subfield(metadata)=+k8s:required + // +k8s:customValidation # TODO: when we get nested subfields, require metadata.atespace + SharedVolume shared_volume = 1; +} + +// DeleteSharedVolumeRequest identifies one SharedVolume to delete. +message DeleteSharedVolumeRequest { + // The volume to delete. + // + // +k8s:required + // +k8s:subfield(atespace)=+k8s:required + ObjectRef shared_volume = 1; + + // Optional per-delete preconditions. + // + // +k8s:optional + DeleteOptions options = 2; +} + // DeleteOptions are the optional guards a Delete request may carry. message DeleteOptions { // Delete only if the resource is still at this version, that is, unchanged diff --git a/pkg/proto/ateapipb/ateapi_grpc.pb.go b/pkg/proto/ateapipb/ateapi_grpc.pb.go index ea0042a580..1487bd022b 100644 --- a/pkg/proto/ateapipb/ateapi_grpc.pb.go +++ b/pkg/proto/ateapipb/ateapi_grpc.pb.go @@ -52,6 +52,11 @@ const ( Control_ListTags_FullMethodName = "/ateapi.Control/ListTags" Control_UpdateTag_FullMethodName = "/ateapi.Control/UpdateTag" Control_DeleteTag_FullMethodName = "/ateapi.Control/DeleteTag" + Control_CreateSharedVolume_FullMethodName = "/ateapi.Control/CreateSharedVolume" + Control_GetSharedVolume_FullMethodName = "/ateapi.Control/GetSharedVolume" + Control_ListSharedVolumes_FullMethodName = "/ateapi.Control/ListSharedVolumes" + Control_UpdateSharedVolume_FullMethodName = "/ateapi.Control/UpdateSharedVolume" + Control_DeleteSharedVolume_FullMethodName = "/ateapi.Control/DeleteSharedVolume" Control_ListWorkers_FullMethodName = "/ateapi.Control/ListWorkers" Control_GetWorker_FullMethodName = "/ateapi.Control/GetWorker" Control_CreateWorker_FullMethodName = "/ateapi.Control/CreateWorker" @@ -140,6 +145,16 @@ type ControlClient interface { // tag that have not yet been suspended still point at that external snapshot // and become unrecoverable, so do not delete a tag while such Actors exist. DeleteTag(ctx context.Context, in *DeleteTagRequest, opts ...grpc.CallOption) (*Tag, error) + // Create a SharedVolume, provisioning it or registering an existing volume. + CreateSharedVolume(ctx context.Context, in *CreateSharedVolumeRequest, opts ...grpc.CallOption) (*SharedVolume, error) + // Get a SharedVolume. + GetSharedVolume(ctx context.Context, in *GetSharedVolumeRequest, opts ...grpc.CallOption) (*SharedVolume, error) + // List ExternalVolumes. + ListSharedVolumes(ctx context.Context, in *ListSharedVolumesRequest, opts ...grpc.CallOption) (*ListSharedVolumesResponse, error) + // Update a SharedVolume's delete trigger. + UpdateSharedVolume(ctx context.Context, in *UpdateSharedVolumeRequest, opts ...grpc.CallOption) (*SharedVolume, error) + // Delete a SharedVolume and its storage. Rejected while any Actor references it. + DeleteSharedVolume(ctx context.Context, in *DeleteSharedVolumeRequest, opts ...grpc.CallOption) (*SharedVolume, error) // List Workers. ListWorkers(ctx context.Context, in *ListWorkersRequest, opts ...grpc.CallOption) (*ListWorkersResponse, error) // Get a Worker. @@ -406,6 +421,56 @@ func (c *controlClient) DeleteTag(ctx context.Context, in *DeleteTagRequest, opt return out, nil } +func (c *controlClient) CreateSharedVolume(ctx context.Context, in *CreateSharedVolumeRequest, opts ...grpc.CallOption) (*SharedVolume, error) { + cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...) + out := new(SharedVolume) + err := c.cc.Invoke(ctx, Control_CreateSharedVolume_FullMethodName, in, out, cOpts...) + if err != nil { + return nil, err + } + return out, nil +} + +func (c *controlClient) GetSharedVolume(ctx context.Context, in *GetSharedVolumeRequest, opts ...grpc.CallOption) (*SharedVolume, error) { + cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...) + out := new(SharedVolume) + err := c.cc.Invoke(ctx, Control_GetSharedVolume_FullMethodName, in, out, cOpts...) + if err != nil { + return nil, err + } + return out, nil +} + +func (c *controlClient) ListSharedVolumes(ctx context.Context, in *ListSharedVolumesRequest, opts ...grpc.CallOption) (*ListSharedVolumesResponse, error) { + cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...) + out := new(ListSharedVolumesResponse) + err := c.cc.Invoke(ctx, Control_ListSharedVolumes_FullMethodName, in, out, cOpts...) + if err != nil { + return nil, err + } + return out, nil +} + +func (c *controlClient) UpdateSharedVolume(ctx context.Context, in *UpdateSharedVolumeRequest, opts ...grpc.CallOption) (*SharedVolume, error) { + cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...) + out := new(SharedVolume) + err := c.cc.Invoke(ctx, Control_UpdateSharedVolume_FullMethodName, in, out, cOpts...) + if err != nil { + return nil, err + } + return out, nil +} + +func (c *controlClient) DeleteSharedVolume(ctx context.Context, in *DeleteSharedVolumeRequest, opts ...grpc.CallOption) (*SharedVolume, error) { + cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...) + out := new(SharedVolume) + err := c.cc.Invoke(ctx, Control_DeleteSharedVolume_FullMethodName, in, out, cOpts...) + if err != nil { + return nil, err + } + return out, nil +} + func (c *controlClient) ListWorkers(ctx context.Context, in *ListWorkersRequest, opts ...grpc.CallOption) (*ListWorkersResponse, error) { cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...) out := new(ListWorkersResponse) @@ -717,6 +782,16 @@ type ControlServer interface { // tag that have not yet been suspended still point at that external snapshot // and become unrecoverable, so do not delete a tag while such Actors exist. DeleteTag(context.Context, *DeleteTagRequest) (*Tag, error) + // Create a SharedVolume, provisioning it or registering an existing volume. + CreateSharedVolume(context.Context, *CreateSharedVolumeRequest) (*SharedVolume, error) + // Get a SharedVolume. + GetSharedVolume(context.Context, *GetSharedVolumeRequest) (*SharedVolume, error) + // List ExternalVolumes. + ListSharedVolumes(context.Context, *ListSharedVolumesRequest) (*ListSharedVolumesResponse, error) + // Update a SharedVolume's delete trigger. + UpdateSharedVolume(context.Context, *UpdateSharedVolumeRequest) (*SharedVolume, error) + // Delete a SharedVolume and its storage. Rejected while any Actor references it. + DeleteSharedVolume(context.Context, *DeleteSharedVolumeRequest) (*SharedVolume, error) // List Workers. ListWorkers(context.Context, *ListWorkersRequest) (*ListWorkersResponse, error) // Get a Worker. @@ -850,6 +925,21 @@ func (UnimplementedControlServer) UpdateTag(context.Context, *UpdateTagRequest) func (UnimplementedControlServer) DeleteTag(context.Context, *DeleteTagRequest) (*Tag, error) { return nil, status.Error(codes.Unimplemented, "method DeleteTag not implemented") } +func (UnimplementedControlServer) CreateSharedVolume(context.Context, *CreateSharedVolumeRequest) (*SharedVolume, error) { + return nil, status.Error(codes.Unimplemented, "method CreateSharedVolume not implemented") +} +func (UnimplementedControlServer) GetSharedVolume(context.Context, *GetSharedVolumeRequest) (*SharedVolume, error) { + return nil, status.Error(codes.Unimplemented, "method GetSharedVolume not implemented") +} +func (UnimplementedControlServer) ListSharedVolumes(context.Context, *ListSharedVolumesRequest) (*ListSharedVolumesResponse, error) { + return nil, status.Error(codes.Unimplemented, "method ListSharedVolumes not implemented") +} +func (UnimplementedControlServer) UpdateSharedVolume(context.Context, *UpdateSharedVolumeRequest) (*SharedVolume, error) { + return nil, status.Error(codes.Unimplemented, "method UpdateSharedVolume not implemented") +} +func (UnimplementedControlServer) DeleteSharedVolume(context.Context, *DeleteSharedVolumeRequest) (*SharedVolume, error) { + return nil, status.Error(codes.Unimplemented, "method DeleteSharedVolume not implemented") +} func (UnimplementedControlServer) ListWorkers(context.Context, *ListWorkersRequest) (*ListWorkersResponse, error) { return nil, status.Error(codes.Unimplemented, "method ListWorkers not implemented") } @@ -1288,6 +1378,96 @@ func _Control_DeleteTag_Handler(srv interface{}, ctx context.Context, dec func(i return interceptor(ctx, in, info, handler) } +func _Control_CreateSharedVolume_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { + in := new(CreateSharedVolumeRequest) + if err := dec(in); err != nil { + return nil, err + } + if interceptor == nil { + return srv.(ControlServer).CreateSharedVolume(ctx, in) + } + info := &grpc.UnaryServerInfo{ + Server: srv, + FullMethod: Control_CreateSharedVolume_FullMethodName, + } + handler := func(ctx context.Context, req interface{}) (interface{}, error) { + return srv.(ControlServer).CreateSharedVolume(ctx, req.(*CreateSharedVolumeRequest)) + } + return interceptor(ctx, in, info, handler) +} + +func _Control_GetSharedVolume_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { + in := new(GetSharedVolumeRequest) + if err := dec(in); err != nil { + return nil, err + } + if interceptor == nil { + return srv.(ControlServer).GetSharedVolume(ctx, in) + } + info := &grpc.UnaryServerInfo{ + Server: srv, + FullMethod: Control_GetSharedVolume_FullMethodName, + } + handler := func(ctx context.Context, req interface{}) (interface{}, error) { + return srv.(ControlServer).GetSharedVolume(ctx, req.(*GetSharedVolumeRequest)) + } + return interceptor(ctx, in, info, handler) +} + +func _Control_ListSharedVolumes_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { + in := new(ListSharedVolumesRequest) + if err := dec(in); err != nil { + return nil, err + } + if interceptor == nil { + return srv.(ControlServer).ListSharedVolumes(ctx, in) + } + info := &grpc.UnaryServerInfo{ + Server: srv, + FullMethod: Control_ListSharedVolumes_FullMethodName, + } + handler := func(ctx context.Context, req interface{}) (interface{}, error) { + return srv.(ControlServer).ListSharedVolumes(ctx, req.(*ListSharedVolumesRequest)) + } + return interceptor(ctx, in, info, handler) +} + +func _Control_UpdateSharedVolume_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { + in := new(UpdateSharedVolumeRequest) + if err := dec(in); err != nil { + return nil, err + } + if interceptor == nil { + return srv.(ControlServer).UpdateSharedVolume(ctx, in) + } + info := &grpc.UnaryServerInfo{ + Server: srv, + FullMethod: Control_UpdateSharedVolume_FullMethodName, + } + handler := func(ctx context.Context, req interface{}) (interface{}, error) { + return srv.(ControlServer).UpdateSharedVolume(ctx, req.(*UpdateSharedVolumeRequest)) + } + return interceptor(ctx, in, info, handler) +} + +func _Control_DeleteSharedVolume_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { + in := new(DeleteSharedVolumeRequest) + if err := dec(in); err != nil { + return nil, err + } + if interceptor == nil { + return srv.(ControlServer).DeleteSharedVolume(ctx, in) + } + info := &grpc.UnaryServerInfo{ + Server: srv, + FullMethod: Control_DeleteSharedVolume_FullMethodName, + } + handler := func(ctx context.Context, req interface{}) (interface{}, error) { + return srv.(ControlServer).DeleteSharedVolume(ctx, req.(*DeleteSharedVolumeRequest)) + } + return interceptor(ctx, in, info, handler) +} + func _Control_ListWorkers_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { in := new(ListWorkersRequest) if err := dec(in); err != nil { @@ -1821,6 +2001,26 @@ var Control_ServiceDesc = grpc.ServiceDesc{ MethodName: "DeleteTag", Handler: _Control_DeleteTag_Handler, }, + { + MethodName: "CreateSharedVolume", + Handler: _Control_CreateSharedVolume_Handler, + }, + { + MethodName: "GetSharedVolume", + Handler: _Control_GetSharedVolume_Handler, + }, + { + MethodName: "ListSharedVolumes", + Handler: _Control_ListSharedVolumes_Handler, + }, + { + MethodName: "UpdateSharedVolume", + Handler: _Control_UpdateSharedVolume_Handler, + }, + { + MethodName: "DeleteSharedVolume", + Handler: _Control_DeleteSharedVolume_Handler, + }, { MethodName: "ListWorkers", Handler: _Control_ListWorkers_Handler, diff --git a/tools/apitool/exemptions.json b/tools/apitool/exemptions.json index 1664fa468b..12f12ea9e4 100644 --- a/tools/apitool/exemptions.json +++ b/tools/apitool/exemptions.json @@ -359,11 +359,6 @@ "subject": "ateapi.Volume", "message": "message has no doc comment" }, - { - "rule": "documented", - "subject": "ateapi.Volume.external_volume_template", - "message": "field has no doc comment" - }, { "rule": "documented", "subject": "ateapi.Volume.system_info",