mirror of
https://github.com/agent-substrate/substrate.git
synced 2026-10-02 03:24:42 +08:00
api: remove the legacy actor_template fields and resolve templates from the store only
Remove actor_template_namespace/actor_template_name and ActorAssignment.actor_template from the public API now that every caller addresses templates by the actor_template ref. controlapi resolves actor templates from the store only, dropping the CRD conversion fallback, and its test fixtures are built as substrate protos. Update the remaining readers of the removed fields (atenet router, kubectl-ate, demos UI, benchmarking, docs, apitool exemptions) and fix the affected functional tests and the Create actor script.
This commit is contained in:
File diff suppressed because one or more lines are too long
@@ -402,10 +402,9 @@ func seedActor(t *testing.T, ctx context.Context, st store.Interface, f actorFix
|
||||
actorRef := resources.ActorRef{Atespace: testAtespace, Name: testActorName}
|
||||
|
||||
actor := &ateapipb.Actor{
|
||||
Metadata: &ateapipb.ResourceMetadata{Atespace: actorRef.Atespace, Name: actorRef.Name},
|
||||
Status: &ateapipb.ActorStatus{State: f.state},
|
||||
ActorTemplateNamespace: "ate-demo",
|
||||
ActorTemplateName: "counter",
|
||||
Metadata: &ateapipb.ResourceMetadata{Atespace: actorRef.Atespace, Name: actorRef.Name},
|
||||
Status: &ateapipb.ActorStatus{State: f.state},
|
||||
ActorTemplate: &ateapipb.ObjectRef{Atespace: "ate-demo", Name: "counter"},
|
||||
}
|
||||
if !f.noPlacement {
|
||||
workerName := testWorkerName
|
||||
|
||||
@@ -71,7 +71,7 @@ func (s *ServiceImpl) CreateActor(ctx context.Context, inActor *ateapipb.Actor)
|
||||
// will still exist later. Checking it here produces a nice error UX, but
|
||||
// we still have to handle the template not existing later, which makes the
|
||||
// UX inconsistent, at best. Is it actually worth checking at all?
|
||||
template, err := resolveActorTemplate(ctx, s.store, s.actorTemplateLister, inActor)
|
||||
template, err := resolveActorTemplate(ctx, s.store, inActor)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
@@ -17,9 +17,7 @@ package controlapi
|
||||
import (
|
||||
"testing"
|
||||
|
||||
atev1alpha1 "github.com/agent-substrate/substrate/pkg/api/v1alpha1"
|
||||
corev1 "k8s.io/api/core/v1"
|
||||
"k8s.io/apimachinery/pkg/api/resource"
|
||||
"github.com/agent-substrate/substrate/pkg/proto/ateapipb"
|
||||
)
|
||||
|
||||
// TestActorResourceLimits covers the actor-side extraction: the CPU/memory limits
|
||||
@@ -27,50 +25,37 @@ import (
|
||||
func TestActorResourceLimits(t *testing.T) {
|
||||
tests := []struct {
|
||||
name string
|
||||
res *corev1.ResourceRequirements
|
||||
resources *ateapipb.Resources
|
||||
wantCPU int64
|
||||
wantMemory int64
|
||||
}{
|
||||
{
|
||||
name: "nil resources yields zero",
|
||||
res: nil,
|
||||
resources: nil,
|
||||
wantCPU: 0,
|
||||
wantMemory: 0,
|
||||
},
|
||||
{
|
||||
name: "cpu and memory limits are read",
|
||||
res: &corev1.ResourceRequirements{
|
||||
Limits: corev1.ResourceList{
|
||||
corev1.ResourceCPU: resource.MustParse("2"),
|
||||
corev1.ResourceMemory: resource.MustParse("4Gi"),
|
||||
},
|
||||
},
|
||||
resources: &ateapipb.Resources{Limits: []*ateapipb.Limits{
|
||||
{Name: "cpu", Quantity: "2"},
|
||||
{Name: "memory", Quantity: "4Gi"},
|
||||
}},
|
||||
wantCPU: 2000,
|
||||
wantMemory: 4 << 30,
|
||||
},
|
||||
{
|
||||
name: "millicpu is preserved",
|
||||
res: &corev1.ResourceRequirements{
|
||||
Limits: corev1.ResourceList{corev1.ResourceCPU: resource.MustParse("1500m")},
|
||||
},
|
||||
resources: &ateapipb.Resources{Limits: []*ateapipb.Limits{
|
||||
{Name: "cpu", Quantity: "1500m"},
|
||||
}},
|
||||
wantCPU: 1500,
|
||||
wantMemory: 0,
|
||||
},
|
||||
{
|
||||
name: "requests are ignored; only limits size the actor",
|
||||
res: &corev1.ResourceRequirements{
|
||||
Requests: corev1.ResourceList{
|
||||
corev1.ResourceCPU: resource.MustParse("1"),
|
||||
corev1.ResourceMemory: resource.MustParse("1Gi"),
|
||||
},
|
||||
},
|
||||
wantCPU: 0,
|
||||
wantMemory: 0,
|
||||
},
|
||||
}
|
||||
for _, tc := range tests {
|
||||
t.Run(tc.name, func(t *testing.T) {
|
||||
tmpl := mustTemplateFromCRD(&atev1alpha1.ActorTemplate{Spec: atev1alpha1.ActorTemplateSpec{Resources: tc.res}})
|
||||
tmpl := &ateapipb.ActorTemplate{Resources: tc.resources}
|
||||
cpu, mem, err := actorResourceLimits(tmpl)
|
||||
if err != nil {
|
||||
t.Fatalf("actorResourceLimits() error: %v", err)
|
||||
|
||||
@@ -279,3 +279,39 @@ func (s *ServiceImpl) UpdateActorTemplate(ctx context.Context, templateRef resou
|
||||
// TODO: implement this
|
||||
return s.store.UpdateActorTemplate(ctx, templateRef, precondition, mutate)
|
||||
}
|
||||
|
||||
// actorTemplateGetter is the storage subset template resolution needs.
|
||||
type actorTemplateGetter interface {
|
||||
GetActorTemplate(ctx context.Context, templateRef resources.ActorTemplateRef) (*ateapipb.ActorTemplate, error)
|
||||
}
|
||||
|
||||
// errActorTemplateNotFound matches (via errors.Is) resolution failures where
|
||||
// the actor names a template that does not exist. Most callers return the
|
||||
// error as is — it already carries FailedPrecondition — while delete
|
||||
// tolerates it and cleans up without the template.
|
||||
var errActorTemplateNotFound = status.New(codes.FailedPrecondition, "actor template not found").Err()
|
||||
|
||||
// resolveActorTemplate resolves the substrate ActorTemplate the actor's
|
||||
// actor_template ref names. A missing template surfaces as
|
||||
// errActorTemplateNotFound.
|
||||
func resolveActorTemplate(ctx context.Context, st actorTemplateGetter, actor *ateapipb.Actor) (*ateapipb.ActorTemplate, error) {
|
||||
templateRef := resources.ActorTemplateRefFromObjectRef(actor.GetActorTemplate())
|
||||
template, err := st.GetActorTemplate(ctx, templateRef)
|
||||
if errors.Is(err, store.ErrNotFound) {
|
||||
return nil, fmt.Errorf("%w; ObjectRef: %s ", errActorTemplateNotFound, templateRef)
|
||||
}
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("while getting ActorTemplate: %w", err)
|
||||
}
|
||||
return template, nil
|
||||
}
|
||||
|
||||
// actorTemplateObjectRef returns a fresh copy of the actor's template
|
||||
// reference — fresh so records built from it never alias the actor message.
|
||||
func actorTemplateObjectRef(actor *ateapipb.Actor) *ateapipb.ObjectRef {
|
||||
ref := actor.GetActorTemplate()
|
||||
if ref == nil {
|
||||
return nil
|
||||
}
|
||||
return &ateapipb.ObjectRef{Atespace: ref.GetAtespace(), Name: ref.GetName()}
|
||||
}
|
||||
|
||||
@@ -16,8 +16,10 @@ package controlapi
|
||||
|
||||
import (
|
||||
"context"
|
||||
"errors"
|
||||
"testing"
|
||||
|
||||
"github.com/agent-substrate/substrate/cmd/ateapi/internal/store"
|
||||
"github.com/agent-substrate/substrate/pkg/proto/ateapipb"
|
||||
"github.com/google/go-cmp/cmp"
|
||||
"google.golang.org/grpc/codes"
|
||||
@@ -334,3 +336,101 @@ func TestValidateDeleteActorTemplateRequest(t *testing.T) {
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
// seedSubstrateTemplate stores a minimal substrate ActorTemplate in team-a.
|
||||
func seedSubstrateTemplate(t *testing.T, ctx context.Context, persistence store.Interface, name string) *ateapipb.ActorTemplate {
|
||||
t.Helper()
|
||||
stored, err := persistence.CreateActorTemplate(ctx, &ateapipb.ActorTemplate{
|
||||
Metadata: &ateapipb.ResourceMetadata{Atespace: "team-a", Name: name},
|
||||
SnapshotsConfig: &ateapipb.SnapshotsConfig{
|
||||
StorageLocation: "gs://ate-snapshots/team-a/",
|
||||
},
|
||||
SandboxConfig: &ateapipb.SandboxConfig{
|
||||
SandboxClass: ateapipb.SandboxClass_SANDBOX_CLASS_GVISOR,
|
||||
ConfigName: "gvisor",
|
||||
},
|
||||
})
|
||||
if err != nil {
|
||||
t.Fatalf("CreateActorTemplate: %v", err)
|
||||
}
|
||||
return stored
|
||||
}
|
||||
|
||||
// TestResolveActorTemplate verifies the resolver reads the substrate resource
|
||||
// the actor's actor_template reference names.
|
||||
func TestResolveActorTemplate(t *testing.T) {
|
||||
ctx := context.Background()
|
||||
persistence := newTestPersistence(t)
|
||||
stored := seedSubstrateTemplate(t, ctx, persistence, "sub-tmpl")
|
||||
|
||||
t.Run("ref reads the store", func(t *testing.T) {
|
||||
actor := &ateapipb.Actor{ActorTemplate: &ateapipb.ObjectRef{Atespace: "team-a", Name: "sub-tmpl"}}
|
||||
got, err := resolveActorTemplate(ctx, persistence, actor)
|
||||
if err != nil {
|
||||
t.Fatalf("resolveActorTemplate: %v", err)
|
||||
}
|
||||
if got.GetMetadata().GetUid() != stored.GetMetadata().GetUid() {
|
||||
t.Errorf("template uid = %q, want the stored substrate template %q", got.GetMetadata().GetUid(), stored.GetMetadata().GetUid())
|
||||
}
|
||||
})
|
||||
|
||||
t.Run("ref to a missing template is FailedPrecondition", func(t *testing.T) {
|
||||
actor := &ateapipb.Actor{ActorTemplate: &ateapipb.ObjectRef{Atespace: "team-a", Name: "absent"}}
|
||||
_, err := resolveActorTemplate(ctx, persistence, actor)
|
||||
if got := status.Code(err); got != codes.FailedPrecondition {
|
||||
t.Fatalf("status.Code = %v, want FailedPrecondition (err: %v)", got, err)
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
// TestResolveActorTemplate_NotFound verifies a vanished template and an actor
|
||||
// naming no template at all surface errActorTemplateNotFound, so callers like
|
||||
// delete can tolerate them.
|
||||
func TestResolveActorTemplate_NotFound(t *testing.T) {
|
||||
ctx := context.Background()
|
||||
persistence := newTestPersistence(t)
|
||||
stored := seedSubstrateTemplate(t, ctx, persistence, "sub-tmpl")
|
||||
|
||||
tests := []struct {
|
||||
name string
|
||||
actor *ateapipb.Actor
|
||||
wantNotFound bool
|
||||
}{
|
||||
{"ref resolves", &ateapipb.Actor{ActorTemplate: &ateapipb.ObjectRef{Atespace: "team-a", Name: "sub-tmpl"}}, false},
|
||||
{"ref to deleted template", &ateapipb.Actor{ActorTemplate: &ateapipb.ObjectRef{Atespace: "team-a", Name: "gone"}}, true},
|
||||
{"no template named at all", &ateapipb.Actor{}, true},
|
||||
}
|
||||
for _, tc := range tests {
|
||||
t.Run(tc.name, func(t *testing.T) {
|
||||
got, err := resolveActorTemplate(ctx, persistence, tc.actor)
|
||||
if tc.wantNotFound {
|
||||
if !errors.Is(err, errActorTemplateNotFound) {
|
||||
t.Fatalf("resolveActorTemplate err = %v, want errActorTemplateNotFound", err)
|
||||
}
|
||||
return
|
||||
}
|
||||
if err != nil {
|
||||
t.Fatalf("resolveActorTemplate: %v", err)
|
||||
}
|
||||
if got.GetMetadata().GetUid() != stored.GetMetadata().GetUid() {
|
||||
t.Errorf("template uid = %q, want %q", got.GetMetadata().GetUid(), stored.GetMetadata().GetUid())
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
// TestActorTemplateObjectRef pins that snapshot and assignment records get a
|
||||
// fresh copy of the reference, never the actor's own message.
|
||||
func TestActorTemplateObjectRef(t *testing.T) {
|
||||
if got := actorTemplateObjectRef(&ateapipb.Actor{}); got != nil {
|
||||
t.Errorf("actorTemplateObjectRef(no ref) = %v, want nil", got)
|
||||
}
|
||||
actor := &ateapipb.Actor{ActorTemplate: &ateapipb.ObjectRef{Atespace: "team-a", Name: "tmpl1"}}
|
||||
got := actorTemplateObjectRef(actor)
|
||||
if got == actor.GetActorTemplate() {
|
||||
t.Error("actorTemplateObjectRef aliases the actor's reference")
|
||||
}
|
||||
if got.GetAtespace() != "team-a" || got.GetName() != "tmpl1" {
|
||||
t.Errorf("actorTemplateObjectRef = %v, want team-a/tmpl1", got)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -89,6 +89,10 @@ func TestValidateCreateActorRequest(t *testing.T) {
|
||||
"valid actor.actor_template",
|
||||
validReq(validActor(withActorTemplate("as", "tmpl"))),
|
||||
nil,
|
||||
}, {
|
||||
"missing actor.actor_template",
|
||||
validReq(validActor(func(a *ateapipb.Actor) { a.ActorTemplate = nil })),
|
||||
field.ErrorList{field.Required(field.NewPath("actor", "actor_template"), "")},
|
||||
}, {
|
||||
"missing actor.actor_template.atespace",
|
||||
validReq(validActor(withActorTemplate("", "tmpl"))),
|
||||
@@ -221,15 +225,15 @@ func TestValidateActorUpdate(t *testing.T) {
|
||||
validOutput(withMetadata(func(m *ateapipb.ResourceMetadata) { m.Name = "invalid value" })),
|
||||
field.ErrorList{field.Invalid(field.NewPath("metadata", "name"), nil, "").WithOrigin("immutable")},
|
||||
}, {
|
||||
"change actor.actor_template",
|
||||
"change actor.actor_template is allowed",
|
||||
validInput(withActorTemplate("as1", "nm1")),
|
||||
validOutput(withActorTemplate("as2", "nm2")),
|
||||
field.ErrorList{field.Invalid(field.NewPath("actor_template"), nil, "").WithOrigin("immutable")},
|
||||
nil,
|
||||
}, {
|
||||
"clear actor.actor_template",
|
||||
validInput(withActorTemplate("as", "nm")),
|
||||
validOutput(func(a *ateapipb.Actor) { a.ActorTemplate = nil }),
|
||||
field.ErrorList{field.Invalid(field.NewPath("actor_template"), nil, "").WithOrigin("immutable")},
|
||||
field.ErrorList{field.Required(field.NewPath("actor_template"), "")},
|
||||
}, {
|
||||
"add actor.source_snapshot_tag",
|
||||
validInput(),
|
||||
@@ -711,7 +715,7 @@ func TestUpdateActor_DeleteRecreateRace(t *testing.T) {
|
||||
}
|
||||
},
|
||||
}
|
||||
svc := &RPCService{impl: newServiceImpl(racing, nil, nil)}
|
||||
svc := &RPCService{impl: newServiceImpl(racing, nil)}
|
||||
|
||||
// The client asserts "only update the actor with uid A".
|
||||
original.WorkerSelector = &ateapipb.Selector{MatchLabels: map[string]string{"tier": "paid"}}
|
||||
@@ -772,7 +776,7 @@ func TestUpdateActor_ConcurrentDisjointUpdates(t *testing.T) {
|
||||
}
|
||||
},
|
||||
}
|
||||
svc := &RPCService{impl: newServiceImpl(racing, nil, nil)}
|
||||
svc := &RPCService{impl: newServiceImpl(racing, nil)}
|
||||
|
||||
// Update operation is changing the worker_selector field, not the actor's state (like the concurrent op)
|
||||
// This update must fail: the racing update bumped the version.
|
||||
@@ -874,7 +878,7 @@ func rpcServiceWithActor(t *testing.T, actor *ateapipb.Actor) (*RPCService, *ate
|
||||
t.Cleanup(cleanup)
|
||||
|
||||
created := storetest.MustCreateActor(t, context.Background(), persistence, actor)
|
||||
return &RPCService{impl: newServiceImpl(persistence, nil, nil)}, created
|
||||
return &RPCService{impl: newServiceImpl(persistence, nil)}, created
|
||||
}
|
||||
|
||||
func TestValidateDeleteActorRequest(t *testing.T) {
|
||||
|
||||
@@ -20,13 +20,6 @@ import (
|
||||
"github.com/agent-substrate/substrate/pkg/proto/ateapipb"
|
||||
)
|
||||
|
||||
func toActorSnapshotContentScope(in atev1alpha1.SnapshotScope) ateapipb.SnapshotContentScope {
|
||||
if in == atev1alpha1.SnapshotScopeData {
|
||||
return ateapipb.SnapshotContentScope_SNAPSHOT_CONTENT_SCOPE_DATA
|
||||
}
|
||||
return ateapipb.SnapshotContentScope_SNAPSHOT_CONTENT_SCOPE_FULL
|
||||
}
|
||||
|
||||
func actorSnapshotContentScopeToAtelet(in ateapipb.SnapshotContentScope) ateletpb.SnapshotScope {
|
||||
if in == ateapipb.SnapshotContentScope_SNAPSHOT_CONTENT_SCOPE_DATA {
|
||||
return ateletpb.SnapshotScope_SNAPSHOT_SCOPE_DATA
|
||||
|
||||
@@ -18,46 +18,39 @@ import (
|
||||
"testing"
|
||||
|
||||
"github.com/agent-substrate/substrate/internal/proto/ateletpb"
|
||||
atev1alpha1 "github.com/agent-substrate/substrate/pkg/api/v1alpha1"
|
||||
"github.com/agent-substrate/substrate/pkg/proto/ateapipb"
|
||||
)
|
||||
|
||||
// TestSnapshotScopeToAtelet covers the wire scope derivation for CRD scopes
|
||||
// converted to proto content scopes: unknown and empty CRD scopes fall back
|
||||
// to Full, matching the CRD's default.
|
||||
// TestSnapshotScopeToAtelet covers the wire scope derivation for template
|
||||
// content scopes: an unset scope falls back to Full.
|
||||
func TestSnapshotScopeToAtelet(t *testing.T) {
|
||||
tests := []struct {
|
||||
name string
|
||||
in atev1alpha1.SnapshotScope
|
||||
in ateapipb.SnapshotContentScope
|
||||
expected ateletpb.SnapshotScope
|
||||
}{
|
||||
{
|
||||
name: "Full scope",
|
||||
in: atev1alpha1.SnapshotScopeFull,
|
||||
in: ateapipb.SnapshotContentScope_SNAPSHOT_CONTENT_SCOPE_FULL,
|
||||
expected: ateletpb.SnapshotScope_SNAPSHOT_SCOPE_FULL,
|
||||
},
|
||||
{
|
||||
name: "Data scope",
|
||||
in: atev1alpha1.SnapshotScopeData,
|
||||
in: ateapipb.SnapshotContentScope_SNAPSHOT_CONTENT_SCOPE_DATA,
|
||||
expected: ateletpb.SnapshotScope_SNAPSHOT_SCOPE_DATA,
|
||||
},
|
||||
{
|
||||
name: "Default scope (empty)",
|
||||
in: "",
|
||||
expected: ateletpb.SnapshotScope_SNAPSHOT_SCOPE_FULL,
|
||||
},
|
||||
{
|
||||
name: "Default scope (unknown)",
|
||||
in: "unknown",
|
||||
name: "Default scope (unspecified)",
|
||||
in: ateapipb.SnapshotContentScope_SNAPSHOT_CONTENT_SCOPE_UNSPECIFIED,
|
||||
expected: ateletpb.SnapshotScope_SNAPSHOT_SCOPE_FULL,
|
||||
},
|
||||
}
|
||||
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
result := actorSnapshotContentScopeToAtelet(toActorSnapshotContentScope(tt.in))
|
||||
result := actorSnapshotContentScopeToAtelet(tt.in)
|
||||
if result != tt.expected {
|
||||
t.Errorf("actorSnapshotContentScopeToAtelet(toActorSnapshotContentScope(%q)) = %v, want %v", tt.in, result, tt.expected)
|
||||
t.Errorf("actorSnapshotContentScopeToAtelet(%v) = %v, want %v", tt.in, result, tt.expected)
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
@@ -669,14 +669,14 @@ func TestUpdateActor_Success(t *testing.T) {
|
||||
|
||||
// TestUpdateActor verifies a typical RMW UpdateActor flow: a
|
||||
// client reads an actor, modifies it and send an UpdateActor request.
|
||||
// Output-only fields it sets are ignored, and changes to immutable fields are
|
||||
// rejected.
|
||||
// Output-only fields it sets are ignored, and the mutable actor_template ref
|
||||
// can repoint the actor at another template.
|
||||
func TestUpdateActor(t *testing.T) {
|
||||
ns := namespaceForTest("ns-update-replace")
|
||||
tc := setupTest(t, ns)
|
||||
defer tc.cleanup()
|
||||
|
||||
createTemplate(t, tc, ns)
|
||||
tmpl := createTemplate(t, tc, ns)
|
||||
|
||||
created, err := tc.client.CreateActor(context.Background(), &ateapipb.CreateActorRequest{Actor: &ateapipb.Actor{
|
||||
Metadata: &ateapipb.ResourceMetadata{Atespace: testAtespace, Name: "id1"},
|
||||
@@ -710,12 +710,24 @@ func TestUpdateActor(t *testing.T) {
|
||||
t.Errorf("UpdateActor response mismatch (-want +got):\n%s", diff)
|
||||
}
|
||||
|
||||
// Immutable field
|
||||
updatedActor.ActorTemplate = &ateapipb.ObjectRef{Name: "new-templace", Atespace: "atespace"}
|
||||
_, err = tc.client.UpdateActor(context.Background(), &ateapipb.UpdateActorRequest{
|
||||
Actor: updatedActor,
|
||||
})
|
||||
assertGrpcErrorRegex(t, err, codes.InvalidArgument, "actor.actor_template: Invalid value: null: field is immutable")
|
||||
// Mutable template ref: repoint the actor at a second template.
|
||||
tmpl2 := proto.Clone(tmpl).(*ateapipb.ActorTemplate)
|
||||
tmpl2.Metadata = &ateapipb.ResourceMetadata{Atespace: testAtespace, Name: "tmpl2"}
|
||||
tmpl2.Status = nil
|
||||
if _, err := tc.client.CreateActorTemplate(context.Background(), &ateapipb.CreateActorTemplateRequest{ActorTemplate: tmpl2}); err != nil {
|
||||
t.Fatalf("CreateActorTemplate failed: %v", err)
|
||||
}
|
||||
|
||||
updatedActor.ActorTemplate = &ateapipb.ObjectRef{Atespace: testAtespace, Name: "tmpl2"}
|
||||
repointed, err := tc.client.UpdateActor(context.Background(), &ateapipb.UpdateActorRequest{Actor: updatedActor})
|
||||
if err != nil {
|
||||
t.Fatalf("UpdateActor failed: %v", err)
|
||||
}
|
||||
wantActor.Metadata.Version = 3
|
||||
wantActor.ActorTemplate = &ateapipb.ObjectRef{Atespace: testAtespace, Name: "tmpl2"}
|
||||
if diff := cmp.Diff(wantActor, repointed, protocmp.Transform(), ignoreUID, ignoreTimestamps); diff != "" {
|
||||
t.Errorf("UpdateActor response mismatch after template repoint (-want +got):\n%s", diff)
|
||||
}
|
||||
}
|
||||
|
||||
// TestUpdateActor_Preconditions verifies the required version and uid guards
|
||||
|
||||
@@ -127,7 +127,6 @@ func setupTestWithVolumePlugins(t *testing.T, ns string, plugins map[string]volu
|
||||
scLister := scFactory.Storage().V1().StorageClasses().Lister()
|
||||
|
||||
substrateInformerFactory := externalversions.NewSharedInformerFactory(substrateClient, 0)
|
||||
actorTemplateLister := substrateInformerFactory.Api().V1alpha1().ActorTemplates().Lister()
|
||||
workerPoolLister := substrateInformerFactory.Api().V1alpha1().WorkerPools().Lister()
|
||||
sandboxConfigLister := substrateInformerFactory.Api().V1alpha1().SandboxConfigs().Lister()
|
||||
csiDriverConfigLister := substrateInformerFactory.Api().V1alpha1().CSIDriverConfigs().Lister()
|
||||
@@ -177,7 +176,7 @@ func setupTestWithVolumePlugins(t *testing.T, ns string, plugins map[string]volu
|
||||
mockDriverName: mockPlugin,
|
||||
}
|
||||
}
|
||||
service := controlapi.NewRPCService(persistence, wc, actorTemplateLister, workerPoolLister, sandboxConfigLister, csiDriverConfigLister, scLister, dialer, instruments, "", volPlugins)
|
||||
service := controlapi.NewRPCService(persistence, wc, workerPoolLister, sandboxConfigLister, csiDriverConfigLister, scLister, dialer, instruments, "", volPlugins)
|
||||
|
||||
// 5. Start REAL gRPC Server for ATE API
|
||||
grpcServer := grpc.NewServer(grpc.ChainUnaryInterceptor(
|
||||
|
||||
@@ -213,9 +213,9 @@ func TestLifecycleOpDurationShape(t *testing.T) {
|
||||
WorkerAssignment: &ateapipb.WorkerAssignment{WorkerNamespace: "ate-workers", WorkerPool: "pool-a"},
|
||||
},
|
||||
}
|
||||
template := mustTemplateFromCRD(&atev1alpha1.ActorTemplate{
|
||||
Spec: atev1alpha1.ActorTemplateSpec{SandboxClass: atev1alpha1.SandboxClassGvisor},
|
||||
})
|
||||
template := &ateapipb.ActorTemplate{
|
||||
SandboxConfig: &ateapipb.SandboxConfig{SandboxClass: ateapipb.SandboxClass_SANDBOX_CLASS_GVISOR},
|
||||
}
|
||||
inst.recordLifecycleOp(context.Background(), ateattr.OperationResume, time.Now(), nil,
|
||||
lifecycleOpAttrs(actor, template, ateattr.SnapshotKindLatest, ateattr.SnapshotScopeDataOnGolden)...)
|
||||
|
||||
|
||||
@@ -62,7 +62,6 @@ type VolumePluginRegistry interface {
|
||||
func NewRPCService(
|
||||
persistence store.Interface,
|
||||
workerCache *workercache.Cache,
|
||||
actorTemplateLister listersv1alpha1.ActorTemplateLister,
|
||||
workerPoolLister listersv1alpha1.WorkerPoolLister,
|
||||
sandboxConfigLister listersv1alpha1.SandboxConfigLister,
|
||||
csiDriverConfigLister listersv1alpha1.CSIDriverConfigLister,
|
||||
@@ -72,7 +71,7 @@ func NewRPCService(
|
||||
egressGatewayAddress string,
|
||||
volumePlugins map[string]volume.VolumePluginControlPlane,
|
||||
) *RPCService {
|
||||
impl := newServiceImpl(persistence, actorTemplateLister, storageClassLister)
|
||||
impl := newServiceImpl(persistence, storageClassLister)
|
||||
s := &RPCService{
|
||||
impl: impl,
|
||||
persistence: persistence,
|
||||
@@ -83,7 +82,7 @@ func NewRPCService(
|
||||
instruments: instruments,
|
||||
volumePlugins: volumePlugins,
|
||||
}
|
||||
s.actorWorkflow = NewActorWorkflow(impl, workerCache, dialer, actorTemplateLister, workerPoolLister, sandboxConfigLister, storageClassLister, instruments, egressGatewayAddress, s)
|
||||
s.actorWorkflow = NewActorWorkflow(impl, workerCache, dialer, workerPoolLister, sandboxConfigLister, storageClassLister, instruments, egressGatewayAddress, s)
|
||||
s.workerWorkflow = NewWorkerWorkflow(impl)
|
||||
return s
|
||||
}
|
||||
@@ -150,8 +149,7 @@ type ServiceImpl struct {
|
||||
// methods we need to trap.
|
||||
store store.Interface
|
||||
|
||||
actorTemplateLister listersv1alpha1.ActorTemplateLister
|
||||
storageClassLister storagev1listers.StorageClassLister
|
||||
storageClassLister storagev1listers.StorageClassLister
|
||||
}
|
||||
|
||||
var _ store.Interface = (*ServiceImpl)(nil)
|
||||
@@ -160,13 +158,11 @@ var _ store.Interface = (*ServiceImpl)(nil)
|
||||
// implementation layer.
|
||||
func newServiceImpl(
|
||||
persistence store.Interface,
|
||||
actorTemplateLister listersv1alpha1.ActorTemplateLister,
|
||||
storageClassLister storagev1listers.StorageClassLister,
|
||||
) *ServiceImpl {
|
||||
s := &ServiceImpl{
|
||||
store: persistence,
|
||||
actorTemplateLister: actorTemplateLister,
|
||||
storageClassLister: storageClassLister,
|
||||
store: persistence,
|
||||
storageClassLister: storageClassLister,
|
||||
}
|
||||
return s
|
||||
}
|
||||
|
||||
@@ -1,309 +0,0 @@
|
||||
// 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"
|
||||
"maps"
|
||||
"sort"
|
||||
|
||||
"github.com/agent-substrate/substrate/cmd/ateapi/internal/store"
|
||||
"github.com/agent-substrate/substrate/internal/resources"
|
||||
atev1alpha1 "github.com/agent-substrate/substrate/pkg/api/v1alpha1"
|
||||
listersv1alpha1 "github.com/agent-substrate/substrate/pkg/client/listers/api/v1alpha1"
|
||||
"github.com/agent-substrate/substrate/pkg/proto/ateapipb"
|
||||
"google.golang.org/grpc/codes"
|
||||
"google.golang.org/grpc/status"
|
||||
corev1 "k8s.io/api/core/v1"
|
||||
k8serrors "k8s.io/apimachinery/pkg/api/errors"
|
||||
"k8s.io/apimachinery/pkg/api/resource"
|
||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||
)
|
||||
|
||||
// actorTemplateGetter is the storage subset template resolution needs.
|
||||
type actorTemplateGetter interface {
|
||||
GetActorTemplate(ctx context.Context, templateRef resources.ActorTemplateRef) (*ateapipb.ActorTemplate, error)
|
||||
}
|
||||
|
||||
// errActorTemplateNotFound matches (via errors.Is) resolution failures where
|
||||
// the actor names a template that does not exist. Most callers return the
|
||||
// error as is — it already carries FailedPrecondition — while delete
|
||||
// tolerates it and cleans up without the template.
|
||||
var errActorTemplateNotFound = status.New(codes.FailedPrecondition, "actor template not found").Err()
|
||||
|
||||
// resolveActorTemplate resolves an actor's template from whichever reference
|
||||
// form it carries: the substrate ActorTemplate resource when actor_template
|
||||
// is set, the ActorTemplate CRD otherwise. A missing template surfaces as a
|
||||
// templateNotFoundError either way.
|
||||
func resolveActorTemplate(ctx context.Context, st actorTemplateGetter, lister listersv1alpha1.ActorTemplateLister, actor *ateapipb.Actor) (*ateapipb.ActorTemplate, error) {
|
||||
if ref := actor.GetActorTemplate(); ref != nil {
|
||||
templateRef := resources.ActorTemplateRefFromObjectRef(ref)
|
||||
template, err := st.GetActorTemplate(ctx, templateRef)
|
||||
if errors.Is(err, store.ErrNotFound) {
|
||||
return nil, fmt.Errorf("%w; ObjectRef: %s ", errActorTemplateNotFound, templateRef)
|
||||
}
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("while getting ActorTemplate: %w", err)
|
||||
}
|
||||
return template, nil
|
||||
}
|
||||
// TODO: remove this fallback when we cut over to substrate resources.
|
||||
crd, err := lister.ActorTemplates(actor.GetActorTemplateNamespace()).Get(actor.GetActorTemplateName())
|
||||
if err != nil {
|
||||
if k8serrors.IsNotFound(err) {
|
||||
return nil, fmt.Errorf("%w; CRD %s/%s ", errActorTemplateNotFound, actor.GetActorTemplateNamespace(), actor.GetActorTemplateName())
|
||||
}
|
||||
return nil, fmt.Errorf("while getting ActorTemplate: %w", err)
|
||||
}
|
||||
return actorTemplateFromCRD(crd)
|
||||
}
|
||||
|
||||
// actorTemplateObjectRef returns a fresh copy of the actor's substrate
|
||||
// template reference, or nil for CRD-backed actors — fresh so records built
|
||||
// from it never alias the actor message.
|
||||
func actorTemplateObjectRef(actor *ateapipb.Actor) *ateapipb.ObjectRef {
|
||||
ref := actor.GetActorTemplate()
|
||||
if ref == nil {
|
||||
return nil
|
||||
}
|
||||
return &ateapipb.ObjectRef{Atespace: ref.GetAtespace(), Name: ref.GetName()}
|
||||
}
|
||||
|
||||
// actorTemplateFromCRD projects an ActorTemplate CRD onto the substrate
|
||||
// ActorTemplate proto, so the workflows handle a single template type while
|
||||
// both the CRD and the substrate resource exist during the migration.
|
||||
// It refuses a template whose workerSelector uses matchExpressions: the
|
||||
// substrate Selector supports equality matching only, and dropping the
|
||||
// expressions would schedule actors onto a wider pool set than declared.
|
||||
func actorTemplateFromCRD(t *atev1alpha1.ActorTemplate) (*ateapipb.ActorTemplate, error) {
|
||||
if t == nil {
|
||||
return nil, status.Error(codes.Internal, "nil ActorTemplate")
|
||||
}
|
||||
if sel := t.Spec.WorkerSelector; sel != nil && len(sel.MatchExpressions) > 0 {
|
||||
return nil, status.Errorf(codes.FailedPrecondition,
|
||||
"ActorTemplate %s/%s workerSelector uses matchExpressions; only matchLabels is supported",
|
||||
t.Namespace, t.Name)
|
||||
}
|
||||
out := &ateapipb.ActorTemplate{
|
||||
Metadata: &ateapipb.ResourceMetadata{
|
||||
// Using the t.Namespace as Atespace is ok because this is used in memory only,
|
||||
// never persists to store.
|
||||
Atespace: t.Namespace,
|
||||
Name: t.Name,
|
||||
Uid: string(t.GetUID()),
|
||||
},
|
||||
WorkerSelector: selectorFromLabelSelector(t.Spec.WorkerSelector),
|
||||
SnapshotsConfig: &ateapipb.SnapshotsConfig{
|
||||
StorageLocation: t.Spec.SnapshotsConfig.Location,
|
||||
OnPause: toActorSnapshotContentScope(t.Spec.SnapshotsConfig.OnPause),
|
||||
OnCommit: toActorSnapshotContentScope(t.Spec.SnapshotsConfig.OnCommit),
|
||||
OnResume: &ateapipb.OnResumeConfig{
|
||||
FromData: resumeSourceFromCRD(t.Spec.SnapshotsConfig.OnResume.FromData),
|
||||
},
|
||||
},
|
||||
// The CRD names no SandboxConfig object: CRD-backed actors resolve
|
||||
// sandbox assets from their worker pool instead.
|
||||
SandboxConfig: &ateapipb.SandboxConfig{
|
||||
SandboxClass: sandboxClassFromCRD(t.Spec.SandboxClass),
|
||||
},
|
||||
Resources: resourcesFromCRD(t.Spec.Resources),
|
||||
Status: templateStatusFromCRD(t.Status),
|
||||
}
|
||||
for i := range t.Spec.Containers {
|
||||
out.Containers = append(out.Containers, containerFromCRD(&t.Spec.Containers[i]))
|
||||
}
|
||||
for i := range t.Spec.Volumes {
|
||||
out.Volumes = append(out.Volumes, volumeFromCRD(&t.Spec.Volumes[i]))
|
||||
}
|
||||
return out, nil
|
||||
}
|
||||
|
||||
// selectorFromLabelSelector converts the equality half of the CRD selector;
|
||||
// actorTemplateFromCRD has already rejected any matchExpressions.
|
||||
func selectorFromLabelSelector(sel *metav1.LabelSelector) *ateapipb.Selector {
|
||||
if sel == nil {
|
||||
return nil
|
||||
}
|
||||
out := &ateapipb.Selector{}
|
||||
if len(sel.MatchLabels) > 0 {
|
||||
out.MatchLabels = maps.Clone(sel.MatchLabels)
|
||||
}
|
||||
return out
|
||||
}
|
||||
|
||||
func resumeSourceFromCRD(in atev1alpha1.ResumeSource) ateapipb.ResumeSource {
|
||||
// Unset defaults to ColdBoot, mirroring the CRD's kubebuilder default.
|
||||
if in == atev1alpha1.ResumeSourceGolden {
|
||||
return ateapipb.ResumeSource_RESUME_SOURCE_GOLDEN
|
||||
}
|
||||
return ateapipb.ResumeSource_RESUME_SOURCE_COLD_BOOT
|
||||
}
|
||||
|
||||
func sandboxClassFromCRD(in atev1alpha1.SandboxClass) ateapipb.SandboxClass {
|
||||
switch in {
|
||||
case atev1alpha1.SandboxClassGvisor:
|
||||
return ateapipb.SandboxClass_SANDBOX_CLASS_GVISOR
|
||||
case atev1alpha1.SandboxClassMicroVM:
|
||||
return ateapipb.SandboxClass_SANDBOX_CLASS_MICROVM
|
||||
default:
|
||||
return ateapipb.SandboxClass_SANDBOX_CLASS_UNSPECIFIED
|
||||
}
|
||||
}
|
||||
|
||||
func resourcesFromCRD(in *corev1.ResourceRequirements) *ateapipb.Resources {
|
||||
if in == nil {
|
||||
return nil
|
||||
}
|
||||
return limitsFromCRD(in.Limits)
|
||||
}
|
||||
|
||||
func limitsFromCRD(limits map[corev1.ResourceName]resource.Quantity) *ateapipb.Resources {
|
||||
if len(limits) == 0 {
|
||||
return nil
|
||||
}
|
||||
names := make([]string, 0, len(limits))
|
||||
for name := range limits {
|
||||
names = append(names, string(name))
|
||||
}
|
||||
// Map iteration is unordered; sort for a deterministic proto.
|
||||
sort.Strings(names)
|
||||
out := &ateapipb.Resources{}
|
||||
for _, name := range names {
|
||||
q := limits[corev1.ResourceName(name)]
|
||||
out.Limits = append(out.Limits, &ateapipb.Limits{Name: name, Quantity: q.String()})
|
||||
}
|
||||
return out
|
||||
}
|
||||
|
||||
func containerFromCRD(c *atev1alpha1.Container) *ateapipb.Container {
|
||||
out := &ateapipb.Container{
|
||||
Name: c.Name,
|
||||
Image: c.Image,
|
||||
Command: append([]string(nil), c.Command...),
|
||||
Args: append([]string(nil), c.Args...),
|
||||
}
|
||||
for _, env := range c.Env {
|
||||
out.Env = append(out.Env, &ateapipb.EnvVar{Name: env.Name, Value: env.Value})
|
||||
}
|
||||
if r := c.Readyz; r != nil {
|
||||
out.Readyz = &ateapipb.ContainerReadyz{TimeoutSeconds: r.TimeoutSeconds}
|
||||
if r.HTTPGet != nil {
|
||||
out.Readyz.HttpGet = &ateapipb.HTTPGetAction{Path: r.HTTPGet.Path, Port: r.HTTPGet.Port}
|
||||
}
|
||||
}
|
||||
for _, m := range c.VolumeMounts {
|
||||
out.VolumeMounts = append(out.VolumeMounts, &ateapipb.VolumeMount{Name: m.Name, MountPath: m.MountPath})
|
||||
}
|
||||
if sc := c.SecurityContext; sc != nil && sc.Capabilities != nil {
|
||||
out.SecurityContext = &ateapipb.SecurityContext{
|
||||
Capabilities: &ateapipb.Capabilities{
|
||||
Add: capabilityNames(sc.Capabilities.Add),
|
||||
Drop: capabilityNames(sc.Capabilities.Drop),
|
||||
},
|
||||
}
|
||||
}
|
||||
if r := c.Resources; r != nil {
|
||||
out.Resources = limitsFromCRD(r.Limits)
|
||||
}
|
||||
return out
|
||||
}
|
||||
|
||||
func capabilityNames(in []atev1alpha1.Capability) []string {
|
||||
if len(in) == 0 {
|
||||
return nil
|
||||
}
|
||||
out := make([]string, 0, len(in))
|
||||
for _, c := range in {
|
||||
out = append(out, string(c))
|
||||
}
|
||||
return out
|
||||
}
|
||||
|
||||
func volumeFromCRD(v *atev1alpha1.Volume) *ateapipb.Volume {
|
||||
out := &ateapipb.Volume{Name: v.Name}
|
||||
switch {
|
||||
case v.DurableDir != nil:
|
||||
out.Type = "DurableDir"
|
||||
out.DurableDir = &ateapipb.DurableDirVolumeSource{}
|
||||
case v.ExternalVolumeTemplate != nil:
|
||||
out.Type = "ExternalVolumeTemplate"
|
||||
out.ExternalVolumeTemplate = &ateapipb.ExternalVolumeTemplate{
|
||||
Capacity: v.ExternalVolumeTemplate.Capacity.String(),
|
||||
StorageClassName: v.ExternalVolumeTemplate.StorageClassName,
|
||||
}
|
||||
case v.Image != nil:
|
||||
out.Type = "Image"
|
||||
out.Image = &ateapipb.ImageVolumeSource{Reference: v.Image.Reference}
|
||||
case v.SystemInfo != nil:
|
||||
out.Type = "SystemInfo"
|
||||
out.SystemInfo = systemInfoFromCRD(v.SystemInfo)
|
||||
}
|
||||
return out
|
||||
}
|
||||
|
||||
func systemInfoFromCRD(in *atev1alpha1.SystemInfoVolumeSource) *ateapipb.SystemInfoVolumeSource {
|
||||
out := &ateapipb.SystemInfoVolumeSource{}
|
||||
for _, ds := range in.DataSources {
|
||||
switch {
|
||||
case ds.ActorMetadata != nil:
|
||||
meta := &ateapipb.ActorMetadataDataSource{}
|
||||
for _, item := range ds.ActorMetadata.Items {
|
||||
meta.Items = append(meta.Items, &ateapipb.ActorMetadataItem{
|
||||
Field: actorMetadataFieldFromCRD(item.Field),
|
||||
Path: item.Path,
|
||||
})
|
||||
}
|
||||
out.DataSources = append(out.DataSources, &ateapipb.SystemInfoDataSource{ActorMetadata: meta})
|
||||
case ds.TrustBundle != nil:
|
||||
out.DataSources = append(out.DataSources, &ateapipb.SystemInfoDataSource{
|
||||
TrustBundle: &ateapipb.TrustBundleDataSource{Name: ds.TrustBundle.Name, Path: ds.TrustBundle.Path},
|
||||
})
|
||||
}
|
||||
}
|
||||
return out
|
||||
}
|
||||
|
||||
func actorMetadataFieldFromCRD(in atev1alpha1.ActorMetadataField) ateapipb.ActorMetadataField {
|
||||
switch in {
|
||||
case atev1alpha1.ActorMetadataFieldName:
|
||||
return ateapipb.ActorMetadataField_ACTOR_METADATA_FIELD_NAME
|
||||
case atev1alpha1.ActorMetadataFieldAtespace:
|
||||
return ateapipb.ActorMetadataField_ACTOR_METADATA_FIELD_ATESPACE
|
||||
case atev1alpha1.ActorMetadataFieldUID:
|
||||
return ateapipb.ActorMetadataField_ACTOR_METADATA_FIELD_UID
|
||||
default:
|
||||
return ateapipb.ActorMetadataField_ACTOR_METADATA_FIELD_UNSPECIFIED
|
||||
}
|
||||
}
|
||||
|
||||
// templateStatusFromCRD maps only what the substrate proto models: the CRD's
|
||||
// phase machinery belongs to the CRD controller, while converted templates
|
||||
// are consumed by the actor workflows, which need the golden snapshot ref.
|
||||
func templateStatusFromCRD(in atev1alpha1.ActorTemplateStatus) *ateapipb.ActorTemplateStatus {
|
||||
out := &ateapipb.ActorTemplateStatus{}
|
||||
if in.GoldenSnapshot != "" {
|
||||
// The CRD stores only the snapshot name; golden snapshots always live
|
||||
// in the reserved golden atespace.
|
||||
out.GoldenSnapshotStatus = &ateapipb.GoldenSnapshotStatus{
|
||||
GoldenSnapshot: &ateapipb.ObjectRef{
|
||||
Atespace: resources.GoldenActorAtespace,
|
||||
Name: in.GoldenSnapshot,
|
||||
},
|
||||
}
|
||||
}
|
||||
return out
|
||||
}
|
||||
@@ -1,361 +0,0 @@
|
||||
// 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"
|
||||
"testing"
|
||||
|
||||
"github.com/agent-substrate/substrate/cmd/ateapi/internal/store"
|
||||
"github.com/agent-substrate/substrate/internal/resources"
|
||||
atev1alpha1 "github.com/agent-substrate/substrate/pkg/api/v1alpha1"
|
||||
listersv1alpha1 "github.com/agent-substrate/substrate/pkg/client/listers/api/v1alpha1"
|
||||
"github.com/agent-substrate/substrate/pkg/proto/ateapipb"
|
||||
"github.com/google/go-cmp/cmp"
|
||||
"google.golang.org/grpc/codes"
|
||||
"google.golang.org/grpc/status"
|
||||
"google.golang.org/protobuf/testing/protocmp"
|
||||
corev1 "k8s.io/api/core/v1"
|
||||
"k8s.io/apimachinery/pkg/api/resource"
|
||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||
"k8s.io/apimachinery/pkg/types"
|
||||
"k8s.io/client-go/tools/cache"
|
||||
)
|
||||
|
||||
// TestActorTemplateFromCRD pins the full CRD-to-proto projection: every field
|
||||
// the workflows consume must survive the conversion, since both template
|
||||
// sources share the proto code path during the migration.
|
||||
func TestActorTemplateFromCRD(t *testing.T) {
|
||||
crd := &atev1alpha1.ActorTemplate{
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
Namespace: "ate-demo-counter-microvm-csi",
|
||||
Name: "counter-microvm-csi",
|
||||
UID: types.UID("9a1b6f9e-6a3f-4a3e-9a51-0c2f3a34d001"),
|
||||
},
|
||||
Spec: atev1alpha1.ActorTemplateSpec{
|
||||
SandboxClass: atev1alpha1.SandboxClassMicroVM,
|
||||
WorkerSelector: &metav1.LabelSelector{
|
||||
MatchLabels: map[string]string{"workload": "counter-microvm-csi"},
|
||||
},
|
||||
Containers: []atev1alpha1.Container{{
|
||||
Name: "counter",
|
||||
Image: "ko://github.com/agent-substrate/substrate/demos/counter",
|
||||
Command: []string{"/ko-app/counter"},
|
||||
Args: []string{"--port=80"},
|
||||
Env: []atev1alpha1.EnvVar{{Name: "COUNTER_DATA_DIR", Value: "/home/counter"}},
|
||||
Readyz: &atev1alpha1.ContainerReadyz{
|
||||
HTTPGet: &atev1alpha1.HTTPGetAction{Path: "/readyz", Port: 80},
|
||||
TimeoutSeconds: 30,
|
||||
},
|
||||
VolumeMounts: []atev1alpha1.VolumeMount{{Name: "data", MountPath: "/home/counter"}},
|
||||
SecurityContext: &atev1alpha1.SecurityContext{
|
||||
Capabilities: &atev1alpha1.Capabilities{
|
||||
Add: []atev1alpha1.Capability{"NET_BIND_SERVICE"},
|
||||
Drop: []atev1alpha1.Capability{"ALL"},
|
||||
},
|
||||
},
|
||||
}},
|
||||
Volumes: []atev1alpha1.Volume{
|
||||
{Name: "durable", VolumeSource: atev1alpha1.VolumeSource{DurableDir: &atev1alpha1.DurableDirVolumeSource{}}},
|
||||
{Name: "data", VolumeSource: atev1alpha1.VolumeSource{
|
||||
ExternalVolumeTemplate: &atev1alpha1.ExternalVolumeTemplate{
|
||||
Capacity: resource.MustParse("1Gi"),
|
||||
StorageClassName: "csi-hostpath-sc",
|
||||
},
|
||||
}},
|
||||
{Name: "model", VolumeSource: atev1alpha1.VolumeSource{
|
||||
Image: &atev1alpha1.ImageVolumeSource{Reference: "ko://github.com/agent-substrate/substrate/demos/counter"},
|
||||
}},
|
||||
{Name: "system", VolumeSource: atev1alpha1.VolumeSource{
|
||||
SystemInfo: &atev1alpha1.SystemInfoVolumeSource{
|
||||
DataSources: []atev1alpha1.SystemInfoDataSource{
|
||||
{ActorMetadata: &atev1alpha1.ActorMetadataDataSource{Items: []atev1alpha1.ActorMetadataItem{
|
||||
{Field: atev1alpha1.ActorMetadataFieldName, Path: "actor/name"},
|
||||
{Field: atev1alpha1.ActorMetadataFieldAtespace, Path: "actor/atespace"},
|
||||
{Field: atev1alpha1.ActorMetadataFieldUID, Path: "actor/uid"},
|
||||
}}},
|
||||
{TrustBundle: &atev1alpha1.TrustBundleDataSource{Name: "egress-mitm.ate.dev", Path: "tls/egress-ca.pem"}},
|
||||
},
|
||||
},
|
||||
}},
|
||||
},
|
||||
SnapshotsConfig: atev1alpha1.SnapshotsConfig{
|
||||
Location: "gs://ate-snapshots/ate-demo-counter-microvm-csi/",
|
||||
OnPause: atev1alpha1.SnapshotScopeFull,
|
||||
OnCommit: atev1alpha1.SnapshotScopeData,
|
||||
OnResume: atev1alpha1.OnResumeConfig{FromData: atev1alpha1.ResumeSourceGolden},
|
||||
},
|
||||
Resources: &corev1.ResourceRequirements{
|
||||
Limits: corev1.ResourceList{
|
||||
corev1.ResourceCPU: resource.MustParse("1500m"),
|
||||
corev1.ResourceMemory: resource.MustParse("512Mi"),
|
||||
},
|
||||
},
|
||||
},
|
||||
Status: atev1alpha1.ActorTemplateStatus{
|
||||
Phase: atev1alpha1.PhaseReady,
|
||||
GoldenSnapshot: "2026-01-01t00-00-00z-abc",
|
||||
},
|
||||
}
|
||||
|
||||
want := &ateapipb.ActorTemplate{
|
||||
Metadata: &ateapipb.ResourceMetadata{
|
||||
Atespace: "ate-demo-counter-microvm-csi",
|
||||
Name: "counter-microvm-csi",
|
||||
Uid: "9a1b6f9e-6a3f-4a3e-9a51-0c2f3a34d001",
|
||||
},
|
||||
WorkerSelector: &ateapipb.Selector{
|
||||
MatchLabels: map[string]string{"workload": "counter-microvm-csi"},
|
||||
},
|
||||
Containers: []*ateapipb.Container{{
|
||||
Name: "counter",
|
||||
Image: "ko://github.com/agent-substrate/substrate/demos/counter",
|
||||
Command: []string{"/ko-app/counter"},
|
||||
Args: []string{"--port=80"},
|
||||
Env: []*ateapipb.EnvVar{{Name: "COUNTER_DATA_DIR", Value: "/home/counter"}},
|
||||
Readyz: &ateapipb.ContainerReadyz{
|
||||
HttpGet: &ateapipb.HTTPGetAction{Path: "/readyz", Port: 80},
|
||||
TimeoutSeconds: 30,
|
||||
},
|
||||
VolumeMounts: []*ateapipb.VolumeMount{{Name: "data", MountPath: "/home/counter"}},
|
||||
SecurityContext: &ateapipb.SecurityContext{
|
||||
Capabilities: &ateapipb.Capabilities{Add: []string{"NET_BIND_SERVICE"}, Drop: []string{"ALL"}},
|
||||
},
|
||||
}},
|
||||
Volumes: []*ateapipb.Volume{
|
||||
{Name: "durable", Type: "DurableDir", DurableDir: &ateapipb.DurableDirVolumeSource{}},
|
||||
{Name: "data", Type: "ExternalVolumeTemplate", ExternalVolumeTemplate: &ateapipb.ExternalVolumeTemplate{
|
||||
Capacity: "1Gi",
|
||||
StorageClassName: "csi-hostpath-sc",
|
||||
}},
|
||||
{Name: "model", Type: "Image", Image: &ateapipb.ImageVolumeSource{Reference: "ko://github.com/agent-substrate/substrate/demos/counter"}},
|
||||
{Name: "system", Type: "SystemInfo", SystemInfo: &ateapipb.SystemInfoVolumeSource{
|
||||
DataSources: []*ateapipb.SystemInfoDataSource{
|
||||
{ActorMetadata: &ateapipb.ActorMetadataDataSource{Items: []*ateapipb.ActorMetadataItem{
|
||||
{Field: ateapipb.ActorMetadataField_ACTOR_METADATA_FIELD_NAME, Path: "actor/name"},
|
||||
{Field: ateapipb.ActorMetadataField_ACTOR_METADATA_FIELD_ATESPACE, Path: "actor/atespace"},
|
||||
{Field: ateapipb.ActorMetadataField_ACTOR_METADATA_FIELD_UID, Path: "actor/uid"},
|
||||
}}},
|
||||
{TrustBundle: &ateapipb.TrustBundleDataSource{Name: "egress-mitm.ate.dev", Path: "tls/egress-ca.pem"}},
|
||||
},
|
||||
}},
|
||||
},
|
||||
SnapshotsConfig: &ateapipb.SnapshotsConfig{
|
||||
StorageLocation: "gs://ate-snapshots/ate-demo-counter-microvm-csi/",
|
||||
OnPause: ateapipb.SnapshotContentScope_SNAPSHOT_CONTENT_SCOPE_FULL,
|
||||
OnCommit: ateapipb.SnapshotContentScope_SNAPSHOT_CONTENT_SCOPE_DATA,
|
||||
OnResume: &ateapipb.OnResumeConfig{FromData: ateapipb.ResumeSource_RESUME_SOURCE_GOLDEN},
|
||||
},
|
||||
SandboxConfig: &ateapipb.SandboxConfig{SandboxClass: ateapipb.SandboxClass_SANDBOX_CLASS_MICROVM},
|
||||
Resources: &ateapipb.Resources{Limits: []*ateapipb.Limits{
|
||||
{Name: "cpu", Quantity: "1500m"},
|
||||
{Name: "memory", Quantity: "512Mi"},
|
||||
}},
|
||||
Status: &ateapipb.ActorTemplateStatus{
|
||||
GoldenSnapshotStatus: &ateapipb.GoldenSnapshotStatus{
|
||||
GoldenSnapshot: &ateapipb.ObjectRef{
|
||||
Atespace: resources.GoldenActorAtespace,
|
||||
Name: "2026-01-01t00-00-00z-abc",
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
got := mustTemplateFromCRD(crd)
|
||||
if diff := cmp.Diff(want, got, protocmp.Transform()); diff != "" {
|
||||
t.Errorf("actorTemplateFromCRD mismatch (-want +got):\n%s", diff)
|
||||
}
|
||||
}
|
||||
|
||||
// TestActorTemplateFromCRD_Defaults pins the conversion of a minimal CRD:
|
||||
// unset scopes normalize to the CRD defaults (Full / ColdBoot), and no
|
||||
// golden snapshot yields an empty status.
|
||||
func TestActorTemplateFromCRD_Defaults(t *testing.T) {
|
||||
got := mustTemplateFromCRD(&atev1alpha1.ActorTemplate{
|
||||
ObjectMeta: metav1.ObjectMeta{Namespace: "ns1", Name: "tmpl1"},
|
||||
})
|
||||
|
||||
want := &ateapipb.ActorTemplate{
|
||||
Metadata: &ateapipb.ResourceMetadata{Atespace: "ns1", Name: "tmpl1"},
|
||||
SnapshotsConfig: &ateapipb.SnapshotsConfig{
|
||||
OnPause: ateapipb.SnapshotContentScope_SNAPSHOT_CONTENT_SCOPE_FULL,
|
||||
OnCommit: ateapipb.SnapshotContentScope_SNAPSHOT_CONTENT_SCOPE_FULL,
|
||||
OnResume: &ateapipb.OnResumeConfig{FromData: ateapipb.ResumeSource_RESUME_SOURCE_COLD_BOOT},
|
||||
},
|
||||
SandboxConfig: &ateapipb.SandboxConfig{SandboxClass: ateapipb.SandboxClass_SANDBOX_CLASS_UNSPECIFIED},
|
||||
Status: &ateapipb.ActorTemplateStatus{},
|
||||
}
|
||||
if diff := cmp.Diff(want, got, protocmp.Transform()); diff != "" {
|
||||
t.Errorf("actorTemplateFromCRD mismatch (-want +got):\n%s", diff)
|
||||
}
|
||||
|
||||
if _, err := actorTemplateFromCRD(nil); status.Code(err) != codes.Internal {
|
||||
t.Errorf("actorTemplateFromCRD(nil) error = %v, want Internal", err)
|
||||
}
|
||||
}
|
||||
|
||||
// TestActorTemplateFromCRD_RejectsMatchExpressions pins the equality-only
|
||||
// selector contract: conversion refuses a template with matchExpressions
|
||||
// rather than dropping them and scheduling onto a wider pool set.
|
||||
func TestActorTemplateFromCRD_RejectsMatchExpressions(t *testing.T) {
|
||||
_, err := actorTemplateFromCRD(&atev1alpha1.ActorTemplate{
|
||||
ObjectMeta: metav1.ObjectMeta{Namespace: "ns1", Name: "tmpl1"},
|
||||
Spec: atev1alpha1.ActorTemplateSpec{
|
||||
WorkerSelector: &metav1.LabelSelector{
|
||||
MatchExpressions: []metav1.LabelSelectorRequirement{
|
||||
{Key: "tier", Operator: metav1.LabelSelectorOpIn, Values: []string{"1"}},
|
||||
},
|
||||
},
|
||||
},
|
||||
})
|
||||
if status.Code(err) != codes.FailedPrecondition {
|
||||
t.Fatalf("actorTemplateFromCRD error = %v, want FailedPrecondition", err)
|
||||
}
|
||||
}
|
||||
|
||||
// mustTemplateFromCRD converts a fixture that must be convertible; rejection
|
||||
// paths call actorTemplateFromCRD directly.
|
||||
func mustTemplateFromCRD(crd *atev1alpha1.ActorTemplate) *ateapipb.ActorTemplate {
|
||||
out, err := actorTemplateFromCRD(crd)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
return out
|
||||
}
|
||||
|
||||
// seedSubstrateTemplate stores a minimal substrate ActorTemplate in team-a.
|
||||
func seedSubstrateTemplate(t *testing.T, ctx context.Context, persistence store.Interface, name string) *ateapipb.ActorTemplate {
|
||||
t.Helper()
|
||||
stored, err := persistence.CreateActorTemplate(ctx, &ateapipb.ActorTemplate{
|
||||
Metadata: &ateapipb.ResourceMetadata{Atespace: "team-a", Name: name},
|
||||
SnapshotsConfig: &ateapipb.SnapshotsConfig{
|
||||
StorageLocation: "gs://ate-snapshots/team-a/",
|
||||
},
|
||||
SandboxConfig: &ateapipb.SandboxConfig{
|
||||
SandboxClass: ateapipb.SandboxClass_SANDBOX_CLASS_GVISOR,
|
||||
ConfigName: "gvisor",
|
||||
},
|
||||
})
|
||||
if err != nil {
|
||||
t.Fatalf("CreateActorTemplate: %v", err)
|
||||
}
|
||||
return stored
|
||||
}
|
||||
|
||||
// TestResolveActorTemplate_PrefersSubstrateRef verifies the resolver reads
|
||||
// the substrate resource when the actor carries an actor_template reference,
|
||||
// and falls back to the converted CRD for legacy actors.
|
||||
func TestResolveActorTemplate_PrefersSubstrateRef(t *testing.T) {
|
||||
ctx := context.Background()
|
||||
persistence := newTestPersistence(t)
|
||||
|
||||
indexer := cache.NewIndexer(cache.MetaNamespaceKeyFunc, cache.Indexers{})
|
||||
if err := indexer.Add(&atev1alpha1.ActorTemplate{
|
||||
ObjectMeta: metav1.ObjectMeta{Namespace: "ns", Name: "crd-tmpl", UID: types.UID("crd-uid-1")},
|
||||
}); err != nil {
|
||||
t.Fatalf("add template to indexer: %v", err)
|
||||
}
|
||||
lister := listersv1alpha1.NewActorTemplateLister(indexer)
|
||||
stored := seedSubstrateTemplate(t, ctx, persistence, "sub-tmpl")
|
||||
|
||||
t.Run("ref mode reads the store", func(t *testing.T) {
|
||||
actor := &ateapipb.Actor{ActorTemplate: &ateapipb.ObjectRef{Atespace: "team-a", Name: "sub-tmpl"}}
|
||||
got, err := resolveActorTemplate(ctx, persistence, lister, actor)
|
||||
if err != nil {
|
||||
t.Fatalf("resolveActorTemplate: %v", err)
|
||||
}
|
||||
if got.GetMetadata().GetUid() != stored.GetMetadata().GetUid() {
|
||||
t.Errorf("template uid = %q, want the stored substrate template %q", got.GetMetadata().GetUid(), stored.GetMetadata().GetUid())
|
||||
}
|
||||
})
|
||||
|
||||
t.Run("ref to a missing template is FailedPrecondition", func(t *testing.T) {
|
||||
actor := &ateapipb.Actor{ActorTemplate: &ateapipb.ObjectRef{Atespace: "team-a", Name: "absent"}}
|
||||
_, err := resolveActorTemplate(ctx, persistence, lister, actor)
|
||||
if got := status.Code(err); got != codes.FailedPrecondition {
|
||||
t.Fatalf("status.Code = %v, want FailedPrecondition (err: %v)", got, err)
|
||||
}
|
||||
})
|
||||
|
||||
t.Run("legacy mode converts the CRD", func(t *testing.T) {
|
||||
actor := &ateapipb.Actor{ActorTemplateNamespace: "ns", ActorTemplateName: "crd-tmpl"}
|
||||
got, err := resolveActorTemplate(ctx, persistence, lister, actor)
|
||||
if err != nil {
|
||||
t.Fatalf("resolveActorTemplate: %v", err)
|
||||
}
|
||||
if got.GetMetadata().GetUid() != "crd-uid-1" {
|
||||
t.Errorf("template uid = %q, want the CRD uid", got.GetMetadata().GetUid())
|
||||
}
|
||||
if got.GetMetadata().GetAtespace() != "ns" || got.GetMetadata().GetName() != "crd-tmpl" {
|
||||
t.Errorf("template identity = %s/%s, want ns/crd-tmpl", got.GetMetadata().GetAtespace(), got.GetMetadata().GetName())
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
// TestResolveActorTemplate_NotFound verifies a vanished template (either
|
||||
// form) and an actor naming no template at all surface
|
||||
// errActorTemplateNotFound, so callers like delete can tolerate them.
|
||||
func TestResolveActorTemplate_NotFound(t *testing.T) {
|
||||
ctx := context.Background()
|
||||
persistence := newTestPersistence(t)
|
||||
indexer := cache.NewIndexer(cache.MetaNamespaceKeyFunc, cache.Indexers{})
|
||||
lister := listersv1alpha1.NewActorTemplateLister(indexer)
|
||||
stored := seedSubstrateTemplate(t, ctx, persistence, "sub-tmpl")
|
||||
|
||||
tests := []struct {
|
||||
name string
|
||||
actor *ateapipb.Actor
|
||||
wantNotFound bool
|
||||
}{
|
||||
{"ref mode resolves", &ateapipb.Actor{ActorTemplate: &ateapipb.ObjectRef{Atespace: "team-a", Name: "sub-tmpl"}}, false},
|
||||
{"ref to deleted template", &ateapipb.Actor{ActorTemplate: &ateapipb.ObjectRef{Atespace: "team-a", Name: "gone"}}, true},
|
||||
{"legacy CRD gone", &ateapipb.Actor{ActorTemplateNamespace: "ns", ActorTemplateName: "gone"}, true},
|
||||
{"no template named at all", &ateapipb.Actor{}, true},
|
||||
}
|
||||
for _, tc := range tests {
|
||||
t.Run(tc.name, func(t *testing.T) {
|
||||
got, err := resolveActorTemplate(ctx, persistence, lister, tc.actor)
|
||||
if tc.wantNotFound {
|
||||
if !errors.Is(err, errActorTemplateNotFound) {
|
||||
t.Fatalf("resolveActorTemplate err = %v, want errActorTemplateNotFound", err)
|
||||
}
|
||||
return
|
||||
}
|
||||
if err != nil {
|
||||
t.Fatalf("resolveActorTemplate: %v", err)
|
||||
}
|
||||
if got.GetMetadata().GetUid() != stored.GetMetadata().GetUid() {
|
||||
t.Errorf("template uid = %q, want %q", got.GetMetadata().GetUid(), stored.GetMetadata().GetUid())
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
// TestActorTemplateObjectRef pins that snapshot and assignment records get a
|
||||
// fresh copy of the reference, never the actor's own message.
|
||||
func TestActorTemplateObjectRef(t *testing.T) {
|
||||
if got := actorTemplateObjectRef(&ateapipb.Actor{}); got != nil {
|
||||
t.Errorf("actorTemplateObjectRef(no ref) = %v, want nil", got)
|
||||
}
|
||||
actor := &ateapipb.Actor{ActorTemplate: &ateapipb.ObjectRef{Atespace: "team-a", Name: "tmpl1"}}
|
||||
got := actorTemplateObjectRef(actor)
|
||||
if got == actor.GetActorTemplate() {
|
||||
t.Error("actorTemplateObjectRef aliases the actor's reference")
|
||||
}
|
||||
if got.GetAtespace() != "team-a" || got.GetName() != "tmpl1" {
|
||||
t.Errorf("actorTemplateObjectRef = %v, want team-a/tmpl1", got)
|
||||
}
|
||||
}
|
||||
@@ -23,7 +23,6 @@ import (
|
||||
"google.golang.org/protobuf/testing/protocmp"
|
||||
|
||||
"github.com/agent-substrate/substrate/internal/volume"
|
||||
atev1alpha1 "github.com/agent-substrate/substrate/pkg/api/v1alpha1"
|
||||
"github.com/agent-substrate/substrate/pkg/proto/ateapipb"
|
||||
storagev1 "k8s.io/api/storage/v1"
|
||||
k8serrors "k8s.io/apimachinery/pkg/api/errors"
|
||||
@@ -51,37 +50,32 @@ func (f *fakeStorageClassLister) Get(name string) (*storagev1.StorageClass, erro
|
||||
var _ storagev1listers.StorageClassLister = (*fakeStorageClassLister)(nil)
|
||||
|
||||
func TestInitialActorVolumes_PendingState(t *testing.T) {
|
||||
tmpl := mustTemplateFromCRD(&atev1alpha1.ActorTemplate{
|
||||
Spec: atev1alpha1.ActorTemplateSpec{
|
||||
Volumes: []atev1alpha1.Volume{
|
||||
{
|
||||
Name: "data-vol-1",
|
||||
VolumeSource: atev1alpha1.VolumeSource{
|
||||
ExternalVolumeTemplate: &atev1alpha1.ExternalVolumeTemplate{
|
||||
StorageClassName: "standard",
|
||||
},
|
||||
},
|
||||
tmpl := &ateapipb.ActorTemplate{
|
||||
Volumes: []*ateapipb.Volume{
|
||||
{
|
||||
Name: "data-vol-1",
|
||||
Type: "ExternalVolumeTemplate",
|
||||
ExternalVolumeTemplate: &ateapipb.ExternalVolumeTemplate{
|
||||
StorageClassName: "standard",
|
||||
},
|
||||
{
|
||||
Name: "scratch-vol",
|
||||
},
|
||||
{
|
||||
Name: "durable-vol",
|
||||
VolumeSource: atev1alpha1.VolumeSource{
|
||||
DurableDir: &atev1alpha1.DurableDirVolumeSource{},
|
||||
},
|
||||
},
|
||||
{
|
||||
Name: "data-vol-2",
|
||||
VolumeSource: atev1alpha1.VolumeSource{
|
||||
ExternalVolumeTemplate: &atev1alpha1.ExternalVolumeTemplate{
|
||||
StorageClassName: "fast",
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
Name: "scratch-vol",
|
||||
},
|
||||
{
|
||||
Name: "durable-vol",
|
||||
Type: "DurableDir",
|
||||
DurableDir: &ateapipb.DurableDirVolumeSource{},
|
||||
},
|
||||
{
|
||||
Name: "data-vol-2",
|
||||
Type: "ExternalVolumeTemplate",
|
||||
ExternalVolumeTemplate: &ateapipb.ExternalVolumeTemplate{
|
||||
StorageClassName: "fast",
|
||||
},
|
||||
},
|
||||
},
|
||||
})
|
||||
}
|
||||
|
||||
want := []*ateapipb.ExternalVolume{
|
||||
{
|
||||
@@ -120,51 +114,43 @@ func TestInitialActorVolumes_PendingState(t *testing.T) {
|
||||
func TestCreateActorVolumes(t *testing.T) {
|
||||
ctx := context.Background()
|
||||
|
||||
standardTmpl := mustTemplateFromCRD(&atev1alpha1.ActorTemplate{
|
||||
Spec: atev1alpha1.ActorTemplateSpec{
|
||||
Volumes: []atev1alpha1.Volume{
|
||||
{
|
||||
Name: "data-vol",
|
||||
VolumeSource: atev1alpha1.VolumeSource{
|
||||
ExternalVolumeTemplate: &atev1alpha1.ExternalVolumeTemplate{
|
||||
StorageClassName: "standard",
|
||||
},
|
||||
},
|
||||
standardTmpl := &ateapipb.ActorTemplate{
|
||||
Volumes: []*ateapipb.Volume{
|
||||
{
|
||||
Name: "data-vol",
|
||||
Type: "ExternalVolumeTemplate",
|
||||
ExternalVolumeTemplate: &ateapipb.ExternalVolumeTemplate{
|
||||
StorageClassName: "standard",
|
||||
},
|
||||
},
|
||||
},
|
||||
})
|
||||
}
|
||||
|
||||
multiVolTmpl := mustTemplateFromCRD(&atev1alpha1.ActorTemplate{
|
||||
Spec: atev1alpha1.ActorTemplateSpec{
|
||||
Volumes: []atev1alpha1.Volume{
|
||||
{
|
||||
Name: "vol1",
|
||||
VolumeSource: atev1alpha1.VolumeSource{
|
||||
ExternalVolumeTemplate: &atev1alpha1.ExternalVolumeTemplate{
|
||||
StorageClassName: "standard",
|
||||
},
|
||||
},
|
||||
multiVolTmpl := &ateapipb.ActorTemplate{
|
||||
Volumes: []*ateapipb.Volume{
|
||||
{
|
||||
Name: "vol1",
|
||||
Type: "ExternalVolumeTemplate",
|
||||
ExternalVolumeTemplate: &ateapipb.ExternalVolumeTemplate{
|
||||
StorageClassName: "standard",
|
||||
},
|
||||
{
|
||||
Name: "vol2",
|
||||
VolumeSource: atev1alpha1.VolumeSource{
|
||||
ExternalVolumeTemplate: &atev1alpha1.ExternalVolumeTemplate{
|
||||
StorageClassName: "standard",
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
Name: "vol2",
|
||||
Type: "ExternalVolumeTemplate",
|
||||
ExternalVolumeTemplate: &ateapipb.ExternalVolumeTemplate{
|
||||
StorageClassName: "standard",
|
||||
},
|
||||
{
|
||||
Name: "vol3",
|
||||
VolumeSource: atev1alpha1.VolumeSource{
|
||||
ExternalVolumeTemplate: &atev1alpha1.ExternalVolumeTemplate{
|
||||
StorageClassName: "standard",
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
Name: "vol3",
|
||||
Type: "ExternalVolumeTemplate",
|
||||
ExternalVolumeTemplate: &ateapipb.ExternalVolumeTemplate{
|
||||
StorageClassName: "standard",
|
||||
},
|
||||
},
|
||||
},
|
||||
})
|
||||
}
|
||||
|
||||
tests := []struct {
|
||||
name string
|
||||
@@ -247,11 +233,7 @@ func TestCreateActorVolumes(t *testing.T) {
|
||||
},
|
||||
{
|
||||
name: "volume not found in template returns error",
|
||||
tmpl: mustTemplateFromCRD(&atev1alpha1.ActorTemplate{
|
||||
Spec: atev1alpha1.ActorTemplateSpec{
|
||||
Volumes: []atev1alpha1.Volume{},
|
||||
},
|
||||
}),
|
||||
tmpl: &ateapipb.ActorTemplate{},
|
||||
inputVolumes: []*ateapipb.ExternalVolume{
|
||||
{
|
||||
VolumeName: "missing-vol",
|
||||
|
||||
@@ -54,9 +54,9 @@ func newAPIWorker(name string) *ateapipb.Worker {
|
||||
|
||||
func newAPIAssignment(actorUID string) *ateapipb.ActorAssignment {
|
||||
return &ateapipb.ActorAssignment{
|
||||
ActorTemplate: &ateapipb.KubeNamespacedObjectRef{Namespace: "ate-system", Name: "tmpl"},
|
||||
Actor: &ateapipb.ObjectRef{Atespace: "team-a", Name: "actor-1"},
|
||||
ActorUid: actorUID,
|
||||
ActorTemplateRef: &ateapipb.ObjectRef{Atespace: "ate-system", Name: "tmpl"},
|
||||
Actor: &ateapipb.ObjectRef{Atespace: "team-a", Name: "actor-1"},
|
||||
ActorUid: actorUID,
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -72,7 +72,6 @@ type ActorWorkflow struct {
|
||||
workerCache *workercache.Cache
|
||||
scheduler scheduling.Scheduler
|
||||
dialer *AteletDialer
|
||||
actorTemplateLister listersv1alpha1.ActorTemplateLister
|
||||
workerPoolLister listersv1alpha1.WorkerPoolLister
|
||||
sandboxConfigLister listersv1alpha1.SandboxConfigLister
|
||||
storageClassLister storagev1listers.StorageClassLister
|
||||
@@ -86,7 +85,6 @@ func NewActorWorkflow(
|
||||
store actorWorkflowStore,
|
||||
workerCache *workercache.Cache,
|
||||
dialer *AteletDialer,
|
||||
actorTemplateLister listersv1alpha1.ActorTemplateLister,
|
||||
workerPoolLister listersv1alpha1.WorkerPoolLister,
|
||||
sandboxConfigLister listersv1alpha1.SandboxConfigLister,
|
||||
storageClassLister storagev1listers.StorageClassLister,
|
||||
@@ -99,7 +97,6 @@ func NewActorWorkflow(
|
||||
workerCache: workerCache,
|
||||
scheduler: scheduling.New(workerCache, scheduling.WithMeter(otel.Meter("ateapi"))),
|
||||
dialer: dialer,
|
||||
actorTemplateLister: actorTemplateLister,
|
||||
workerPoolLister: workerPoolLister,
|
||||
sandboxConfigLister: sandboxConfigLister,
|
||||
storageClassLister: storageClassLister,
|
||||
|
||||
@@ -52,7 +52,7 @@ func (w *ActorWorkflow) DeleteActor(ctx context.Context, actorRef resources.Acto
|
||||
var errs []error
|
||||
// Cleanup stays best-effort: an unresolvable template is recorded and
|
||||
// the remaining steps run without it, like a missing one.
|
||||
actorTemplate, err := resolveActorTemplate(ctx, w.store, w.actorTemplateLister, actor)
|
||||
actorTemplate, err := resolveActorTemplate(ctx, w.store, actor)
|
||||
if errors.Is(err, errActorTemplateNotFound) {
|
||||
actorTemplate, err = nil, nil
|
||||
}
|
||||
@@ -162,8 +162,8 @@ func (w *ActorWorkflow) ensureAteletTerminated(ctx context.Context, actorRef res
|
||||
// all external volumes recorded on the actor so atelet can unmount them on the node.
|
||||
slog.WarnContext(ctx, "actor template not found, constructing fallback workload spec for atelet terminate",
|
||||
slog.String("actor", actorRef.Name),
|
||||
slog.String("templateNamespace", actor.GetActorTemplateNamespace()),
|
||||
slog.String("templateName", actor.GetActorTemplateName()))
|
||||
slog.String("templateAtespace", actor.GetActorTemplate().GetAtespace()),
|
||||
slog.String("templateName", actor.GetActorTemplate().GetName()))
|
||||
workloadSpec = &ateletpb.WorkloadSpec{}
|
||||
for _, vol := range actor.GetStatus().GetActorVolumes() {
|
||||
// StorageVolumeId is only populated once the volume is provisioned.
|
||||
@@ -188,8 +188,8 @@ func (w *ActorWorkflow) ensureAteletTerminated(ctx context.Context, actorRef res
|
||||
Atespace: actor.GetMetadata().GetAtespace(),
|
||||
ActorName: actor.GetMetadata().GetName(),
|
||||
ActorUid: actor.GetMetadata().GetUid(),
|
||||
ActorTemplateAtespace: actorTemplate.GetMetadata().GetAtespace(),
|
||||
ActorTemplateName: actorTemplate.GetMetadata().GetName(),
|
||||
ActorTemplateAtespace: actor.GetActorTemplate().GetAtespace(),
|
||||
ActorTemplateName: actor.GetActorTemplate().GetName(),
|
||||
Spec: workloadSpec,
|
||||
}
|
||||
|
||||
|
||||
@@ -102,7 +102,7 @@ func (w *ActorWorkflow) loadActorForPause(ctx context.Context, actorRef resource
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
actorTemplate, err := resolveActorTemplate(ctx, w.store, w.actorTemplateLister, actor)
|
||||
actorTemplate, err := resolveActorTemplate(ctx, w.store, actor)
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
@@ -190,8 +190,8 @@ func (w *ActorWorkflow) ensureAteletPaused(ctx context.Context, actorRef resourc
|
||||
TargetAteomUid: assignment.GetWorkerPodUid(),
|
||||
Atespace: actor.GetMetadata().GetAtespace(),
|
||||
ActorName: actor.GetMetadata().GetName(),
|
||||
ActorTemplateAtespace: actorTemplate.GetMetadata().GetAtespace(),
|
||||
ActorTemplateName: actorTemplate.GetMetadata().GetName(),
|
||||
ActorTemplateAtespace: actor.GetActorTemplate().GetAtespace(),
|
||||
ActorTemplateName: actor.GetActorTemplate().GetName(),
|
||||
Spec: workloadSpec,
|
||||
Type: ateletpb.CheckpointType_CHECKPOINT_TYPE_LOCAL,
|
||||
Config: &ateletpb.CheckpointRequest_LocalConfig{
|
||||
|
||||
@@ -20,7 +20,6 @@ import (
|
||||
|
||||
"github.com/agent-substrate/substrate/cmd/ateapi/internal/store/storetest"
|
||||
"github.com/agent-substrate/substrate/internal/resources"
|
||||
atev1alpha1 "github.com/agent-substrate/substrate/pkg/api/v1alpha1"
|
||||
"github.com/agent-substrate/substrate/pkg/proto/ateapipb"
|
||||
"google.golang.org/grpc/codes"
|
||||
"google.golang.org/grpc/status"
|
||||
@@ -60,7 +59,7 @@ func TestEnsurePausedFinalized_WorkerGone(t *testing.T) {
|
||||
// Intentionally NOT creating the worker in store, simulates worker already gone.
|
||||
|
||||
w := &ActorWorkflow{store: st}
|
||||
finalized, err := w.ensurePausedFinalized(ctx, actorRef, mustTemplateFromCRD(&atev1alpha1.ActorTemplate{}))
|
||||
finalized, err := w.ensurePausedFinalized(ctx, actorRef, &ateapipb.ActorTemplate{})
|
||||
if err != nil {
|
||||
t.Fatalf("ensurePausedFinalized: %v", err)
|
||||
}
|
||||
@@ -95,12 +94,12 @@ func TestEnsurePausedFinalized_WorkerGone(t *testing.T) {
|
||||
func TestEnsurePausedFinalized_RecordsContentScope(t *testing.T) {
|
||||
tests := []struct {
|
||||
name string
|
||||
onPause atev1alpha1.SnapshotScope
|
||||
onPause ateapipb.SnapshotContentScope
|
||||
want ateapipb.SnapshotContentScope
|
||||
}{
|
||||
{"data", atev1alpha1.SnapshotScopeData, ateapipb.SnapshotContentScope_SNAPSHOT_CONTENT_SCOPE_DATA},
|
||||
{"full", atev1alpha1.SnapshotScopeFull, ateapipb.SnapshotContentScope_SNAPSHOT_CONTENT_SCOPE_FULL},
|
||||
{"unset defaults to full", "", ateapipb.SnapshotContentScope_SNAPSHOT_CONTENT_SCOPE_FULL},
|
||||
{"data", ateapipb.SnapshotContentScope_SNAPSHOT_CONTENT_SCOPE_DATA, ateapipb.SnapshotContentScope_SNAPSHOT_CONTENT_SCOPE_DATA},
|
||||
{"full", ateapipb.SnapshotContentScope_SNAPSHOT_CONTENT_SCOPE_FULL, ateapipb.SnapshotContentScope_SNAPSHOT_CONTENT_SCOPE_FULL},
|
||||
{"unset defaults to full", ateapipb.SnapshotContentScope_SNAPSHOT_CONTENT_SCOPE_UNSPECIFIED, ateapipb.SnapshotContentScope_SNAPSHOT_CONTENT_SCOPE_FULL},
|
||||
}
|
||||
for _, tc := range tests {
|
||||
t.Run(tc.name, func(t *testing.T) {
|
||||
@@ -137,9 +136,9 @@ func TestEnsurePausedFinalized_RecordsContentScope(t *testing.T) {
|
||||
}
|
||||
|
||||
w := &ActorWorkflow{store: st}
|
||||
tmpl := mustTemplateFromCRD(&atev1alpha1.ActorTemplate{
|
||||
Spec: atev1alpha1.ActorTemplateSpec{SnapshotsConfig: atev1alpha1.SnapshotsConfig{OnPause: tc.onPause}},
|
||||
})
|
||||
tmpl := &ateapipb.ActorTemplate{
|
||||
SnapshotsConfig: &ateapipb.SnapshotsConfig{OnPause: tc.onPause},
|
||||
}
|
||||
got, err := w.ensurePausedFinalized(ctx, actorRef, tmpl)
|
||||
if err != nil {
|
||||
t.Fatalf("ensurePausedFinalized: %v", err)
|
||||
@@ -284,7 +283,7 @@ func TestEnsureAteletPaused_DanglingWorkerDoesNotRecordPhantomSnapshot(t *testin
|
||||
created := storetest.MustCreateActor(t, ctx, persistence, actor)
|
||||
|
||||
w := &ActorWorkflow{store: persistence, dialer: newDanglingDialer()}
|
||||
if _, err := w.ensureAteletPaused(ctx, resources.ActorRef{Atespace: "team-a", Name: "actor-1"}, created, mustTemplateFromCRD(&atev1alpha1.ActorTemplate{})); err == nil {
|
||||
if _, err := w.ensureAteletPaused(ctx, resources.ActorRef{Atespace: "team-a", Name: "actor-1"}, created, &ateapipb.ActorTemplate{}); err == nil {
|
||||
t.Fatal("ensureAteletPaused: want error for dangling worker, got nil")
|
||||
}
|
||||
|
||||
|
||||
@@ -172,7 +172,7 @@ func (w *ActorWorkflow) loadActorForResume(ctx context.Context, actorRef resourc
|
||||
return actor, nil, src, nil
|
||||
}
|
||||
|
||||
actorTemplate, err := resolveActorTemplate(ctx, w.store, w.actorTemplateLister, actor)
|
||||
actorTemplate, err := resolveActorTemplate(ctx, w.store, actor)
|
||||
if err != nil {
|
||||
return nil, nil, src, err
|
||||
}
|
||||
@@ -516,14 +516,7 @@ func (w *ActorWorkflow) assignWorkerAttempt(ctx context.Context, actorRef resour
|
||||
},
|
||||
ActorUid: actor.GetMetadata().GetUid(),
|
||||
}
|
||||
if ref := actorTemplateObjectRef(actor); ref != nil {
|
||||
assignment.ActorTemplateRef = ref
|
||||
} else {
|
||||
assignment.ActorTemplate = &ateapipb.KubeNamespacedObjectRef{
|
||||
Namespace: actor.GetActorTemplateNamespace(),
|
||||
Name: actor.GetActorTemplateName(),
|
||||
}
|
||||
}
|
||||
assignment.ActorTemplateRef = actorTemplateObjectRef(actor)
|
||||
|
||||
// Workers() returns pointers directly from the cache, so the claim is written
|
||||
// by mutating the store's own copy; the cached one is only read, for the
|
||||
@@ -686,8 +679,8 @@ func (w *ActorWorkflow) ensureAteletRestored(ctx context.Context, actorRef resou
|
||||
TargetAteomUid: assignment.GetWorkerPodUid(),
|
||||
Atespace: actor.GetMetadata().GetAtespace(),
|
||||
ActorName: actor.GetMetadata().GetName(),
|
||||
ActorTemplateAtespace: actorTemplate.GetMetadata().GetAtespace(),
|
||||
ActorTemplateName: actorTemplate.GetMetadata().GetName(),
|
||||
ActorTemplateAtespace: actor.GetActorTemplate().GetAtespace(),
|
||||
ActorTemplateName: actor.GetActorTemplate().GetName(),
|
||||
Spec: workloadSpec,
|
||||
ActorUid: actor.GetMetadata().Uid,
|
||||
EgressGateway: egressGateway,
|
||||
@@ -734,8 +727,8 @@ func (w *ActorWorkflow) ensureAteletRestored(ctx context.Context, actorRef resou
|
||||
TargetAteomUid: assignment.GetWorkerPodUid(),
|
||||
Atespace: actor.GetMetadata().GetAtespace(),
|
||||
ActorName: actor.GetMetadata().GetName(),
|
||||
ActorTemplateAtespace: actorTemplate.GetMetadata().GetAtespace(),
|
||||
ActorTemplateName: actorTemplate.GetMetadata().GetName(),
|
||||
ActorTemplateAtespace: actor.GetActorTemplate().GetAtespace(),
|
||||
ActorTemplateName: actor.GetActorTemplate().GetName(),
|
||||
Spec: workloadSpec,
|
||||
Type: ateletpb.CheckpointType_CHECKPOINT_TYPE_EXTERNAL,
|
||||
Config: &ateletpb.RestoreRequest_ExternalConfig{
|
||||
@@ -769,8 +762,8 @@ func (w *ActorWorkflow) ensureAteletRestored(ctx context.Context, actorRef resou
|
||||
TargetAteomUid: assignment.GetWorkerPodUid(),
|
||||
Atespace: actor.GetMetadata().GetAtespace(),
|
||||
ActorName: actor.GetMetadata().GetName(),
|
||||
ActorTemplateAtespace: actorTemplate.GetMetadata().GetAtespace(),
|
||||
ActorTemplateName: actorTemplate.GetMetadata().GetName(),
|
||||
ActorTemplateAtespace: actor.GetActorTemplate().GetAtespace(),
|
||||
ActorTemplateName: actor.GetActorTemplate().GetName(),
|
||||
SandboxAssets: sandboxAssets,
|
||||
Spec: workloadSpec,
|
||||
ActorUid: actor.GetMetadata().Uid,
|
||||
|
||||
@@ -28,12 +28,10 @@ import (
|
||||
"github.com/agent-substrate/substrate/cmd/ateapi/internal/store/storetest"
|
||||
"github.com/agent-substrate/substrate/cmd/ateapi/internal/workercache"
|
||||
"github.com/agent-substrate/substrate/internal/resources"
|
||||
atev1alpha1 "github.com/agent-substrate/substrate/pkg/api/v1alpha1"
|
||||
"github.com/agent-substrate/substrate/pkg/proto/ateapipb"
|
||||
"google.golang.org/grpc/codes"
|
||||
"google.golang.org/grpc/status"
|
||||
"google.golang.org/protobuf/proto"
|
||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||
)
|
||||
|
||||
// TestSchedulerRecordable guards the retry-dedup rule: the assignment loop
|
||||
@@ -110,7 +108,7 @@ func TestAssignWorkerAttempt_MissingSelectedWorkerIsRetried(t *testing.T) {
|
||||
actor, wc := seedAssignFixture(t, ctx, persistence)
|
||||
st := &updateWorkerErrorStore{Interface: persistence, err: store.ErrNotFound}
|
||||
w := &ActorWorkflow{store: st, workerCache: wc, scheduler: scheduling.New(wc)}
|
||||
tmpl := mustTemplateFromCRD(&atev1alpha1.ActorTemplate{Spec: atev1alpha1.ActorTemplateSpec{SandboxClass: atev1alpha1.SandboxClassGvisor}})
|
||||
tmpl := &ateapipb.ActorTemplate{SandboxConfig: &ateapipb.SandboxConfig{SandboxClass: ateapipb.SandboxClass_SANDBOX_CLASS_GVISOR}}
|
||||
|
||||
_, _, err := w.assignWorkerAttempt(ctx, resources.ActorRef{Atespace: "team-a", Name: "id1"}, actor, tmpl)
|
||||
if !errors.Is(err, store.ErrVersionConflict) {
|
||||
@@ -131,7 +129,7 @@ func TestEnsureWorkerAssigned_ConflictExhaustionIsRetryable(t *testing.T) {
|
||||
actor, wc := seedAssignFixture(t, ctx, persistence)
|
||||
st := &updateWorkerErrorStore{Interface: persistence, err: store.ErrVersionConflict}
|
||||
w := &ActorWorkflow{store: st, workerCache: wc, scheduler: scheduling.New(wc)}
|
||||
tmpl := mustTemplateFromCRD(&atev1alpha1.ActorTemplate{Spec: atev1alpha1.ActorTemplateSpec{SandboxClass: atev1alpha1.SandboxClassGvisor}})
|
||||
tmpl := &ateapipb.ActorTemplate{SandboxConfig: &ateapipb.SandboxConfig{SandboxClass: ateapipb.SandboxClass_SANDBOX_CLASS_GVISOR}}
|
||||
|
||||
_, _, err := w.ensureWorkerAssigned(ctx, resources.ActorRef{Atespace: "team-a", Name: "id1"}, actor, tmpl)
|
||||
if !errors.Is(err, store.ErrVersionConflict) {
|
||||
@@ -190,9 +188,6 @@ func TestAssignWorkerAttempt_StampsSubstrateTemplateRef(t *testing.T) {
|
||||
if assignment.GetActorTemplateRef().GetAtespace() != "team-a" || assignment.GetActorTemplateRef().GetName() != "sub-tmpl" {
|
||||
t.Errorf("assignment ActorTemplateRef = %v, want team-a/sub-tmpl", assignment.GetActorTemplateRef())
|
||||
}
|
||||
if assignment.GetActorTemplate() != nil {
|
||||
t.Errorf("assignment legacy ActorTemplate = %v, want nil for a ref-mode actor", assignment.GetActorTemplate())
|
||||
}
|
||||
}
|
||||
|
||||
func TestAssignWorkerAttempt_SkipsWorkerAssignedInOtherAtespace(t *testing.T) {
|
||||
@@ -231,9 +226,9 @@ func TestAssignWorkerAttempt_SkipsWorkerAssignedInOtherAtespace(t *testing.T) {
|
||||
actor := &ateapipb.Actor{
|
||||
Metadata: &ateapipb.ResourceMetadata{Atespace: "team-a", Name: "shared", Uid: "actor-uid"},
|
||||
}
|
||||
tmpl := mustTemplateFromCRD(&atev1alpha1.ActorTemplate{
|
||||
Spec: atev1alpha1.ActorTemplateSpec{SandboxClass: atev1alpha1.SandboxClassGvisor},
|
||||
})
|
||||
tmpl := &ateapipb.ActorTemplate{
|
||||
SandboxConfig: &ateapipb.SandboxConfig{SandboxClass: ateapipb.SandboxClass_SANDBOX_CLASS_GVISOR},
|
||||
}
|
||||
_, _, err := w.assignWorkerAttempt(ctx, resources.ActorRef{Atespace: "team-a", Name: "shared"}, actor, tmpl)
|
||||
if status.Code(err) != codes.ResourceExhausted {
|
||||
t.Fatalf("assignWorkerAttempt() error = %v, want ResourceExhausted (no free workers)", err)
|
||||
@@ -306,9 +301,9 @@ func TestAssignWorkerAttempt_ReleasesIneligibleStaleWorkerInBackground(t *testin
|
||||
}
|
||||
|
||||
w := &ActorWorkflow{store: persistence, workerCache: wc, scheduler: scheduling.New(wc)}
|
||||
tmpl := mustTemplateFromCRD(&atev1alpha1.ActorTemplate{
|
||||
Spec: atev1alpha1.ActorTemplateSpec{SandboxClass: atev1alpha1.SandboxClassGvisor},
|
||||
})
|
||||
tmpl := &ateapipb.ActorTemplate{
|
||||
SandboxConfig: &ateapipb.SandboxConfig{SandboxClass: ateapipb.SandboxClass_SANDBOX_CLASS_GVISOR},
|
||||
}
|
||||
_, worker, err := w.assignWorkerAttempt(ctx, resources.ActorRef{Atespace: "team-a", Name: "id1"}, actor, tmpl)
|
||||
if err != nil {
|
||||
t.Fatalf("assignWorkerAttempt() error = %v, want nil (release must not fail the resume)", err)
|
||||
@@ -404,9 +399,9 @@ func TestAssignWorkerAttempt_RetryAfterConflictPicksFreshWorker(t *testing.T) {
|
||||
}
|
||||
|
||||
w := &ActorWorkflow{store: persistence, workerCache: wc, scheduler: scheduling.New(wc)}
|
||||
tmpl := mustTemplateFromCRD(&atev1alpha1.ActorTemplate{
|
||||
Spec: atev1alpha1.ActorTemplateSpec{SandboxClass: atev1alpha1.SandboxClassGvisor},
|
||||
})
|
||||
tmpl := &ateapipb.ActorTemplate{
|
||||
SandboxConfig: &ateapipb.SandboxConfig{SandboxClass: ateapipb.SandboxClass_SANDBOX_CLASS_GVISOR},
|
||||
}
|
||||
_, worker, err := w.assignWorkerAttempt(ctx, resources.ActorRef{Atespace: "team-a", Name: "id1"}, actor, tmpl)
|
||||
if err != nil {
|
||||
t.Fatalf("assignWorkerAttempt() on retry = %v, want nil (must re-pick a free worker)", err)
|
||||
@@ -550,9 +545,9 @@ func TestAssignWorkerAttempt_ConflictRefreshesActor(t *testing.T) {
|
||||
}}
|
||||
|
||||
w := &ActorWorkflow{store: st, workerCache: wc, scheduler: scheduling.New(wc)}
|
||||
tmpl := mustTemplateFromCRD(&atev1alpha1.ActorTemplate{
|
||||
Spec: atev1alpha1.ActorTemplateSpec{SandboxClass: atev1alpha1.SandboxClassGvisor},
|
||||
})
|
||||
tmpl := &ateapipb.ActorTemplate{
|
||||
SandboxConfig: &ateapipb.SandboxConfig{SandboxClass: ateapipb.SandboxClass_SANDBOX_CLASS_GVISOR},
|
||||
}
|
||||
refreshed, _, err := w.assignWorkerAttempt(ctx, resources.ActorRef{Atespace: "team-a", Name: "id1"}, actor, tmpl)
|
||||
|
||||
if tc.wantRetry {
|
||||
@@ -678,7 +673,7 @@ func TestEnsureWorkerAssigned_RejectsNonResumableStates(t *testing.T) {
|
||||
continue
|
||||
}
|
||||
actor := &ateapipb.Actor{Status: &ateapipb.ActorStatus{State: st}, Metadata: &ateapipb.ResourceMetadata{Name: "id1", Uid: "actor-uid-1"}}
|
||||
_, _, err := w.ensureWorkerAssigned(ctx, resources.ActorRef{Name: "id1"}, actor, mustTemplateFromCRD(&atev1alpha1.ActorTemplate{}))
|
||||
_, _, err := w.ensureWorkerAssigned(ctx, resources.ActorRef{Name: "id1"}, actor, &ateapipb.ActorTemplate{})
|
||||
assertPrerequisiteResult(t, st, err, false)
|
||||
}
|
||||
}
|
||||
@@ -877,7 +872,7 @@ func TestValidateAssignedWorker_WorkerOwnership(t *testing.T) {
|
||||
},
|
||||
},
|
||||
}
|
||||
tmpl := mustTemplateFromCRD(&atev1alpha1.ActorTemplate{Spec: atev1alpha1.ActorTemplateSpec{SandboxClass: atev1alpha1.SandboxClassGvisor}})
|
||||
tmpl := &ateapipb.ActorTemplate{SandboxConfig: &ateapipb.SandboxConfig{SandboxClass: ateapipb.SandboxClass_SANDBOX_CLASS_GVISOR}}
|
||||
_, err = w.validateAssignedWorker(ctx, resources.ActorRef{Atespace: "team-a", Name: "shared"}, resumingActor, tmpl)
|
||||
if got := status.Code(err); got != tt.wantCode {
|
||||
t.Fatalf("status.Code(err) = %v, want %v (err: %v)", got, tt.wantCode, err)
|
||||
@@ -915,17 +910,17 @@ func TestLoadActorForResume_OnGoldenDataResume(t *testing.T) {
|
||||
|
||||
tests := []struct {
|
||||
name string
|
||||
fromData atev1alpha1.ResumeSource
|
||||
fromData ateapipb.ResumeSource
|
||||
// paused seeds the actor with LocalSnapshotInfo (a pause checkpoint)
|
||||
// instead of a durable snapshot; onPause is the template's pause
|
||||
// scope, contentScope the durable snapshot's recorded content.
|
||||
paused bool
|
||||
onPause atev1alpha1.SnapshotScope
|
||||
onPause ateapipb.SnapshotContentScope
|
||||
contentScope ateapipb.SnapshotContentScope
|
||||
// goldenSnapshot is ActorTemplate.Status.GoldenSnapshot; seedGolden
|
||||
// controls whether the golden ActorSnapshot row it names exists, and
|
||||
// goldenScope the scope it records (zero value UNSPECIFIED is treated
|
||||
// as Full for legacy snapshots).
|
||||
// goldenSnapshot names the template status's golden snapshot;
|
||||
// seedGolden controls whether the golden ActorSnapshot row it names
|
||||
// exists, and goldenScope the scope it records (zero value UNSPECIFIED
|
||||
// is treated as Full for legacy snapshots).
|
||||
goldenSnapshot string
|
||||
seedGolden bool
|
||||
goldenScope ateapipb.SnapshotContentScope
|
||||
@@ -934,7 +929,7 @@ func TestLoadActorForResume_OnGoldenDataResume(t *testing.T) {
|
||||
}{
|
||||
{
|
||||
name: "resolves golden location for Data durable snapshot",
|
||||
fromData: atev1alpha1.ResumeSourceGolden,
|
||||
fromData: ateapipb.ResumeSource_RESUME_SOURCE_GOLDEN,
|
||||
contentScope: ateapipb.SnapshotContentScope_SNAPSHOT_CONTENT_SCOPE_DATA,
|
||||
goldenSnapshot: "golden-1",
|
||||
seedGolden: true,
|
||||
@@ -944,9 +939,9 @@ func TestLoadActorForResume_OnGoldenDataResume(t *testing.T) {
|
||||
},
|
||||
{
|
||||
name: "resolves golden location for paused actor with Data onPause",
|
||||
fromData: atev1alpha1.ResumeSourceGolden,
|
||||
fromData: ateapipb.ResumeSource_RESUME_SOURCE_GOLDEN,
|
||||
paused: true,
|
||||
onPause: atev1alpha1.SnapshotScopeData,
|
||||
onPause: ateapipb.SnapshotContentScope_SNAPSHOT_CONTENT_SCOPE_DATA,
|
||||
goldenSnapshot: "golden-1",
|
||||
seedGolden: true,
|
||||
goldenScope: ateapipb.SnapshotContentScope_SNAPSHOT_CONTENT_SCOPE_FULL,
|
||||
@@ -957,9 +952,9 @@ func TestLoadActorForResume_OnGoldenDataResume(t *testing.T) {
|
||||
// A Full pause snapshot restores from its own content; the policy
|
||||
// only governs data-only restores.
|
||||
name: "leaves golden location empty for paused actor with Full onPause",
|
||||
fromData: atev1alpha1.ResumeSourceGolden,
|
||||
fromData: ateapipb.ResumeSource_RESUME_SOURCE_GOLDEN,
|
||||
paused: true,
|
||||
onPause: atev1alpha1.SnapshotScopeFull,
|
||||
onPause: ateapipb.SnapshotContentScope_SNAPSHOT_CONTENT_SCOPE_FULL,
|
||||
goldenSnapshot: "golden-1",
|
||||
seedGolden: true,
|
||||
goldenScope: ateapipb.SnapshotContentScope_SNAPSHOT_CONTENT_SCOPE_FULL,
|
||||
@@ -968,7 +963,7 @@ func TestLoadActorForResume_OnGoldenDataResume(t *testing.T) {
|
||||
},
|
||||
{
|
||||
name: "fails when golden snapshot is not Full",
|
||||
fromData: atev1alpha1.ResumeSourceGolden,
|
||||
fromData: ateapipb.ResumeSource_RESUME_SOURCE_GOLDEN,
|
||||
contentScope: ateapipb.SnapshotContentScope_SNAPSHOT_CONTENT_SCOPE_DATA,
|
||||
goldenSnapshot: "golden-1",
|
||||
seedGolden: true,
|
||||
@@ -977,13 +972,13 @@ func TestLoadActorForResume_OnGoldenDataResume(t *testing.T) {
|
||||
},
|
||||
{
|
||||
name: "fails when template has no golden snapshot",
|
||||
fromData: atev1alpha1.ResumeSourceGolden,
|
||||
fromData: ateapipb.ResumeSource_RESUME_SOURCE_GOLDEN,
|
||||
contentScope: ateapipb.SnapshotContentScope_SNAPSHOT_CONTENT_SCOPE_DATA,
|
||||
wantCode: codes.FailedPrecondition,
|
||||
},
|
||||
{
|
||||
name: "fails when golden snapshot data is missing",
|
||||
fromData: atev1alpha1.ResumeSourceGolden,
|
||||
fromData: ateapipb.ResumeSource_RESUME_SOURCE_GOLDEN,
|
||||
contentScope: ateapipb.SnapshotContentScope_SNAPSHOT_CONTENT_SCOPE_DATA,
|
||||
goldenSnapshot: "golden-1",
|
||||
wantCode: codes.DataLoss,
|
||||
@@ -992,7 +987,7 @@ func TestLoadActorForResume_OnGoldenDataResume(t *testing.T) {
|
||||
// A Full snapshot restores from its own content even under
|
||||
// Golden fromData (e.g. taken before the template switched).
|
||||
name: "leaves golden location empty for Full snapshot",
|
||||
fromData: atev1alpha1.ResumeSourceGolden,
|
||||
fromData: ateapipb.ResumeSource_RESUME_SOURCE_GOLDEN,
|
||||
contentScope: ateapipb.SnapshotContentScope_SNAPSHOT_CONTENT_SCOPE_FULL,
|
||||
goldenSnapshot: "golden-1",
|
||||
seedGolden: true,
|
||||
@@ -1002,7 +997,7 @@ func TestLoadActorForResume_OnGoldenDataResume(t *testing.T) {
|
||||
},
|
||||
{
|
||||
name: "leaves golden location empty under ColdBoot fromData",
|
||||
fromData: atev1alpha1.ResumeSourceColdBoot,
|
||||
fromData: ateapipb.ResumeSource_RESUME_SOURCE_COLD_BOOT,
|
||||
contentScope: ateapipb.SnapshotContentScope_SNAPSHOT_CONTENT_SCOPE_DATA,
|
||||
goldenSnapshot: "golden-1",
|
||||
seedGolden: true,
|
||||
@@ -1052,16 +1047,21 @@ func TestLoadActorForResume_OnGoldenDataResume(t *testing.T) {
|
||||
seedWorkflowActor(t, ctx, persistence, actorRef, "ns", "tmpl1", actorState, seedOpts...)
|
||||
|
||||
storetest.MustCreateAtespace(t, ctx, persistence, "ns")
|
||||
if _, err := persistence.CreateActorTemplate(ctx, mustTemplateFromCRD(&atev1alpha1.ActorTemplate{
|
||||
ObjectMeta: metav1.ObjectMeta{Namespace: "ns", Name: "tmpl1"},
|
||||
Spec: atev1alpha1.ActorTemplateSpec{
|
||||
SnapshotsConfig: atev1alpha1.SnapshotsConfig{
|
||||
OnPause: tt.onPause,
|
||||
OnResume: atev1alpha1.OnResumeConfig{FromData: tt.fromData},
|
||||
},
|
||||
tmpl := &ateapipb.ActorTemplate{
|
||||
Metadata: &ateapipb.ResourceMetadata{Atespace: "ns", Name: "tmpl1"},
|
||||
SnapshotsConfig: &ateapipb.SnapshotsConfig{
|
||||
OnPause: tt.onPause,
|
||||
OnResume: &ateapipb.OnResumeConfig{FromData: tt.fromData},
|
||||
},
|
||||
Status: atev1alpha1.ActorTemplateStatus{GoldenSnapshot: tt.goldenSnapshot},
|
||||
})); err != nil {
|
||||
}
|
||||
if tt.goldenSnapshot != "" {
|
||||
tmpl.Status = &ateapipb.ActorTemplateStatus{
|
||||
GoldenSnapshotStatus: &ateapipb.GoldenSnapshotStatus{
|
||||
GoldenSnapshot: &ateapipb.ObjectRef{Atespace: resources.GoldenActorAtespace, Name: tt.goldenSnapshot},
|
||||
},
|
||||
}
|
||||
}
|
||||
if _, err := persistence.CreateActorTemplate(ctx, tmpl); err != nil {
|
||||
t.Fatalf("create template: %v", err)
|
||||
}
|
||||
|
||||
@@ -1104,10 +1104,14 @@ func TestLoadActorForResume_GoldenFallbackRejectsNonFullGolden(t *testing.T) {
|
||||
seedWorkflowActor(t, ctx, persistence, actorRef, "ns", "tmpl1", ateapipb.ActorState_ACTOR_STATE_SUSPENDED)
|
||||
|
||||
storetest.MustCreateAtespace(t, ctx, persistence, "ns")
|
||||
if _, err := persistence.CreateActorTemplate(ctx, mustTemplateFromCRD(&atev1alpha1.ActorTemplate{
|
||||
ObjectMeta: metav1.ObjectMeta{Namespace: "ns", Name: "tmpl1"},
|
||||
Status: atev1alpha1.ActorTemplateStatus{GoldenSnapshot: "golden-1"},
|
||||
})); err != nil {
|
||||
if _, err := persistence.CreateActorTemplate(ctx, &ateapipb.ActorTemplate{
|
||||
Metadata: &ateapipb.ResourceMetadata{Atespace: "ns", Name: "tmpl1"},
|
||||
Status: &ateapipb.ActorTemplateStatus{
|
||||
GoldenSnapshotStatus: &ateapipb.GoldenSnapshotStatus{
|
||||
GoldenSnapshot: &ateapipb.ObjectRef{Atespace: resources.GoldenActorAtespace, Name: "golden-1"},
|
||||
},
|
||||
},
|
||||
}); err != nil {
|
||||
t.Fatalf("create template: %v", err)
|
||||
}
|
||||
|
||||
|
||||
@@ -109,7 +109,7 @@ func (w *ActorWorkflow) loadActorForSuspend(ctx context.Context, actorRef resour
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
actorTemplate, err := resolveActorTemplate(ctx, w.store, w.actorTemplateLister, actor)
|
||||
actorTemplate, err := resolveActorTemplate(ctx, w.store, actor)
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
@@ -247,8 +247,8 @@ func (w *ActorWorkflow) ensureAteletSuspended(ctx context.Context, actorRef reso
|
||||
TargetAteomUid: assignment.GetWorkerPodUid(),
|
||||
Atespace: actor.GetMetadata().GetAtespace(),
|
||||
ActorName: actor.GetMetadata().GetName(),
|
||||
ActorTemplateAtespace: actorTemplate.GetMetadata().GetAtespace(),
|
||||
ActorTemplateName: actorTemplate.GetMetadata().GetName(),
|
||||
ActorTemplateAtespace: actor.GetActorTemplate().GetAtespace(),
|
||||
ActorTemplateName: actor.GetActorTemplate().GetName(),
|
||||
Spec: workloadSpec,
|
||||
Type: ateletpb.CheckpointType_CHECKPOINT_TYPE_EXTERNAL,
|
||||
Config: &ateletpb.CheckpointRequest_ExternalConfig{
|
||||
@@ -303,8 +303,8 @@ func (w *ActorWorkflow) ensurePausedSnapshotUploaded(ctx context.Context, actorR
|
||||
Atespace: actor.GetMetadata().GetAtespace(),
|
||||
ActorName: actor.GetMetadata().GetName(),
|
||||
ActorUid: actor.GetMetadata().GetUid(),
|
||||
ActorTemplateAtespace: actorTemplate.GetMetadata().GetAtespace(),
|
||||
ActorTemplateName: actorTemplate.GetMetadata().GetName(),
|
||||
ActorTemplateAtespace: actor.GetActorTemplate().GetAtespace(),
|
||||
ActorTemplateName: actor.GetActorTemplate().GetName(),
|
||||
LocalSnapshotName: local.GetSnapshotName(),
|
||||
DestinationSnapshotUri: snapshotURI.String(),
|
||||
// The commit scope, like a running-origin suspend; atelet converts
|
||||
@@ -379,15 +379,13 @@ func (w *ActorWorkflow) ensureSuspendedFinalized(ctx context.Context, actorRef r
|
||||
snapshot := &ateapipb.ActorSnapshot{
|
||||
Metadata: &ateapipb.ResourceMetadata{Atespace: actorRef.Atespace, Name: snapshotName},
|
||||
Status: &ateapipb.ActorSnapshotStatus{
|
||||
SourceActor: actorRef.ToObjectRef(),
|
||||
SourceActorUid: latestActor.GetMetadata().GetUid(),
|
||||
SourceActorVersion: latestActor.GetStatus().GetInProgressSnapshotSourceActorVersion(),
|
||||
ActorTemplateNamespace: latestActor.GetActorTemplateNamespace(),
|
||||
ActorTemplateName: latestActor.GetActorTemplateName(),
|
||||
ActorTemplate: actorTemplateObjectRef(latestActor),
|
||||
ActorTemplateUid: actorTemplate.GetMetadata().GetUid(),
|
||||
ContentScope: commitSnapshotScope(actorRef.Atespace, actorTemplate),
|
||||
SnapshotUri: snapshotURI.String(),
|
||||
SourceActor: actorRef.ToObjectRef(),
|
||||
SourceActorUid: latestActor.GetMetadata().GetUid(),
|
||||
SourceActorVersion: latestActor.GetStatus().GetInProgressSnapshotSourceActorVersion(),
|
||||
ActorTemplate: actorTemplateObjectRef(latestActor),
|
||||
ActorTemplateUid: actorTemplate.GetMetadata().GetUid(),
|
||||
ContentScope: commitSnapshotScope(actorRef.Atespace, actorTemplate),
|
||||
SnapshotUri: snapshotURI.String(),
|
||||
},
|
||||
}
|
||||
// ErrAlreadyExists means a previous attempt crashed after creating
|
||||
|
||||
@@ -22,7 +22,6 @@ import (
|
||||
"github.com/agent-substrate/substrate/cmd/ateapi/internal/store"
|
||||
"github.com/agent-substrate/substrate/cmd/ateapi/internal/store/storetest"
|
||||
"github.com/agent-substrate/substrate/internal/resources"
|
||||
atev1alpha1 "github.com/agent-substrate/substrate/pkg/api/v1alpha1"
|
||||
"github.com/agent-substrate/substrate/pkg/proto/ateapipb"
|
||||
"google.golang.org/grpc/codes"
|
||||
"google.golang.org/grpc/status"
|
||||
@@ -36,9 +35,9 @@ func TestEnsureMarkedSuspending_SnapshotName(t *testing.T) {
|
||||
Metadata: &ateapipb.ResourceMetadata{Atespace: "team-a", Name: "actor-1"},
|
||||
Status: &ateapipb.ActorStatus{State: ateapipb.ActorState_ACTOR_STATE_RUNNING},
|
||||
})
|
||||
tmpl := mustTemplateFromCRD(&atev1alpha1.ActorTemplate{Spec: atev1alpha1.ActorTemplateSpec{
|
||||
SnapshotsConfig: atev1alpha1.SnapshotsConfig{Location: "gs://bucket/root/"},
|
||||
}})
|
||||
tmpl := &ateapipb.ActorTemplate{
|
||||
SnapshotsConfig: &ateapipb.SnapshotsConfig{StorageLocation: "gs://bucket/root/"},
|
||||
}
|
||||
w := &ActorWorkflow{store: persistence}
|
||||
marked, err := w.ensureMarkedSuspending(ctx, resources.ActorRef{Atespace: "team-a", Name: "actor-1"}, actor, tmpl)
|
||||
if err != nil {
|
||||
@@ -78,7 +77,7 @@ func TestEnsureMarkedSuspending_ReentryKeepsPersistedSnapshotLocation(t *testing
|
||||
},
|
||||
})
|
||||
w := &ActorWorkflow{store: persistence}
|
||||
marked, err := w.ensureMarkedSuspending(ctx, resources.ActorRef{Atespace: "team-a", Name: "actor-1"}, actor, mustTemplateFromCRD(&atev1alpha1.ActorTemplate{}))
|
||||
marked, err := w.ensureMarkedSuspending(ctx, resources.ActorRef{Atespace: "team-a", Name: "actor-1"}, actor, &ateapipb.ActorTemplate{})
|
||||
if err != nil {
|
||||
t.Fatalf("ensureMarkedSuspending: %v", err)
|
||||
}
|
||||
@@ -169,9 +168,9 @@ func TestEnsureMarkedSuspending_StateMatrix(t *testing.T) {
|
||||
Status: &ateapipb.ActorStatus{State: seedState},
|
||||
})
|
||||
|
||||
tmpl := mustTemplateFromCRD(&atev1alpha1.ActorTemplate{Spec: atev1alpha1.ActorTemplateSpec{
|
||||
SnapshotsConfig: atev1alpha1.SnapshotsConfig{Location: "gs://snapshots"},
|
||||
}})
|
||||
tmpl := &ateapipb.ActorTemplate{
|
||||
SnapshotsConfig: &ateapipb.SnapshotsConfig{StorageLocation: "gs://snapshots"},
|
||||
}
|
||||
marked, err := w.ensureMarkedSuspending(ctx, actorRef, actor, tmpl)
|
||||
assertPrerequisiteResult(t, seedState, err, allowed[seedState])
|
||||
if err == nil && marked.GetStatus().GetState() != ateapipb.ActorState_ACTOR_STATE_SUSPENDING {
|
||||
@@ -257,7 +256,7 @@ func TestEnsureAteletSuspended_DanglingWorkerDoesNotRecordPhantomSnapshot(t *tes
|
||||
created := storetest.MustCreateActor(t, ctx, persistence, actor)
|
||||
|
||||
w := &ActorWorkflow{store: persistence, dialer: newDanglingDialer()}
|
||||
if _, err := w.ensureAteletSuspended(ctx, resources.ActorRef{Atespace: "team-a", Name: "actor-1"}, created, mustTemplateFromCRD(&atev1alpha1.ActorTemplate{})); err == nil {
|
||||
if _, err := w.ensureAteletSuspended(ctx, resources.ActorRef{Atespace: "team-a", Name: "actor-1"}, created, &ateapipb.ActorTemplate{}); err == nil {
|
||||
t.Fatal("ensureAteletSuspended: want error for dangling worker, got nil")
|
||||
}
|
||||
|
||||
@@ -308,7 +307,7 @@ func TestEnsureSuspendedFinalized_NoAssignment(t *testing.T) {
|
||||
created := storetest.MustCreateActor(t, ctx, persistence, actor)
|
||||
|
||||
w := &ActorWorkflow{store: persistence}
|
||||
tmpl := mustTemplateFromCRD(&atev1alpha1.ActorTemplate{Spec: atev1alpha1.ActorTemplateSpec{SnapshotsConfig: atev1alpha1.SnapshotsConfig{Location: "gs://snapshots"}}})
|
||||
tmpl := &ateapipb.ActorTemplate{SnapshotsConfig: &ateapipb.SnapshotsConfig{StorageLocation: "gs://snapshots"}}
|
||||
stored, err := w.ensureSuspendedFinalized(ctx, resources.ActorRef{Atespace: "team-a", Name: "actor-1"}, tmpl)
|
||||
if err != nil {
|
||||
t.Fatalf("ensureSuspendedFinalized: %v", err)
|
||||
@@ -449,7 +448,7 @@ func TestEnsureSuspendedFinalized_ReleasesOnlyOwnWorker(t *testing.T) {
|
||||
}
|
||||
|
||||
w := &ActorWorkflow{store: persistence}
|
||||
tmpl := mustTemplateFromCRD(&atev1alpha1.ActorTemplate{Spec: atev1alpha1.ActorTemplateSpec{SnapshotsConfig: atev1alpha1.SnapshotsConfig{Location: "gs://bucket/root"}}})
|
||||
tmpl := &ateapipb.ActorTemplate{SnapshotsConfig: &ateapipb.SnapshotsConfig{StorageLocation: "gs://bucket/root"}}
|
||||
if _, err := w.ensureSuspendedFinalized(ctx, resources.ActorRef{Atespace: "team-a", Name: "shared"}, tmpl); err != nil {
|
||||
t.Fatalf("ensureSuspendedFinalized: %v", err)
|
||||
}
|
||||
@@ -490,7 +489,7 @@ func TestEnsureSuspendedFinalized_SnapshotSourceActorVersion(t *testing.T) {
|
||||
})
|
||||
|
||||
w := &ActorWorkflow{store: persistence}
|
||||
tmpl := mustTemplateFromCRD(&atev1alpha1.ActorTemplate{Spec: atev1alpha1.ActorTemplateSpec{SnapshotsConfig: atev1alpha1.SnapshotsConfig{Location: "gs://snapshots"}}})
|
||||
tmpl := &ateapipb.ActorTemplate{SnapshotsConfig: &ateapipb.SnapshotsConfig{StorageLocation: "gs://snapshots"}}
|
||||
final, err := w.ensureSuspendedFinalized(ctx, resources.ActorRef{Atespace: "team-a", Name: "actor-1"}, tmpl)
|
||||
if err != nil {
|
||||
t.Fatalf("ensureSuspendedFinalized: %v", err)
|
||||
@@ -515,23 +514,23 @@ func TestEnsureSuspendedFinalized_SnapshotSourceActorVersion(t *testing.T) {
|
||||
// golden snapshot is the base an OnGolden data resume combines into, so the
|
||||
// template's onCommit must not thin it down to a data-only capture.
|
||||
func TestCommitSnapshotScope(t *testing.T) {
|
||||
tmpl := func(onCommit atev1alpha1.SnapshotScope) *ateapipb.ActorTemplate {
|
||||
return mustTemplateFromCRD(&atev1alpha1.ActorTemplate{Spec: atev1alpha1.ActorTemplateSpec{
|
||||
SnapshotsConfig: atev1alpha1.SnapshotsConfig{OnCommit: onCommit},
|
||||
}})
|
||||
tmpl := func(onCommit ateapipb.SnapshotContentScope) *ateapipb.ActorTemplate {
|
||||
return &ateapipb.ActorTemplate{
|
||||
SnapshotsConfig: &ateapipb.SnapshotsConfig{OnCommit: onCommit},
|
||||
}
|
||||
}
|
||||
fullScope := ateapipb.SnapshotContentScope_SNAPSHOT_CONTENT_SCOPE_FULL
|
||||
dataScope := ateapipb.SnapshotContentScope_SNAPSHOT_CONTENT_SCOPE_DATA
|
||||
tests := []struct {
|
||||
name string
|
||||
atespace string
|
||||
onCommit atev1alpha1.SnapshotScope
|
||||
onCommit ateapipb.SnapshotContentScope
|
||||
want ateapipb.SnapshotContentScope
|
||||
}{
|
||||
{"golden actor ignores Data onCommit", resources.GoldenActorAtespace, atev1alpha1.SnapshotScopeData, fullScope},
|
||||
{"golden actor keeps Full onCommit", resources.GoldenActorAtespace, atev1alpha1.SnapshotScopeFull, fullScope},
|
||||
{"regular actor uses Data onCommit", "team-a", atev1alpha1.SnapshotScopeData, dataScope},
|
||||
{"regular actor uses Full onCommit", "team-a", atev1alpha1.SnapshotScopeFull, fullScope},
|
||||
{"golden actor ignores Data onCommit", resources.GoldenActorAtespace, dataScope, fullScope},
|
||||
{"golden actor keeps Full onCommit", resources.GoldenActorAtespace, fullScope, fullScope},
|
||||
{"regular actor uses Data onCommit", "team-a", dataScope, dataScope},
|
||||
{"regular actor uses Full onCommit", "team-a", fullScope, fullScope},
|
||||
}
|
||||
for _, tc := range tests {
|
||||
t.Run(tc.name, func(t *testing.T) {
|
||||
@@ -572,24 +571,26 @@ func TestIsPausedOriginSuspend(t *testing.T) {
|
||||
// suspend is rejected before the actor leaves PAUSED when the pause captured
|
||||
// Data but the template commits Full: an upload cannot fabricate memory.
|
||||
func TestEnsureMarkedSuspending_PausedScopeRejection(t *testing.T) {
|
||||
tmpl := func(onPause, onCommit atev1alpha1.SnapshotScope) *ateapipb.ActorTemplate {
|
||||
return mustTemplateFromCRD(&atev1alpha1.ActorTemplate{Spec: atev1alpha1.ActorTemplateSpec{
|
||||
SnapshotsConfig: atev1alpha1.SnapshotsConfig{OnPause: onPause, OnCommit: onCommit, Location: "gs://snapshots"},
|
||||
}})
|
||||
tmpl := func(onPause, onCommit ateapipb.SnapshotContentScope) *ateapipb.ActorTemplate {
|
||||
return &ateapipb.ActorTemplate{
|
||||
SnapshotsConfig: &ateapipb.SnapshotsConfig{OnPause: onPause, OnCommit: onCommit, StorageLocation: "gs://snapshots"},
|
||||
}
|
||||
}
|
||||
fullScope := ateapipb.SnapshotContentScope_SNAPSHOT_CONTENT_SCOPE_FULL
|
||||
dataScope := ateapipb.SnapshotContentScope_SNAPSHOT_CONTENT_SCOPE_DATA
|
||||
tests := []struct {
|
||||
name string
|
||||
captured ateapipb.SnapshotContentScope
|
||||
tmpl *ateapipb.ActorTemplate
|
||||
wantErr bool
|
||||
}{
|
||||
{"data capture cannot commit full", ateapipb.SnapshotContentScope_SNAPSHOT_CONTENT_SCOPE_DATA, tmpl(atev1alpha1.SnapshotScopeData, atev1alpha1.SnapshotScopeFull), true},
|
||||
{"data capture commits data", ateapipb.SnapshotContentScope_SNAPSHOT_CONTENT_SCOPE_DATA, tmpl(atev1alpha1.SnapshotScopeData, atev1alpha1.SnapshotScopeData), false},
|
||||
{"full capture commits full", ateapipb.SnapshotContentScope_SNAPSHOT_CONTENT_SCOPE_FULL, tmpl(atev1alpha1.SnapshotScopeFull, atev1alpha1.SnapshotScopeFull), false},
|
||||
{"full capture commits data via conversion", ateapipb.SnapshotContentScope_SNAPSHOT_CONTENT_SCOPE_FULL, tmpl(atev1alpha1.SnapshotScopeFull, atev1alpha1.SnapshotScopeData), false},
|
||||
{"data capture cannot commit full", dataScope, tmpl(dataScope, fullScope), true},
|
||||
{"data capture commits data", dataScope, tmpl(dataScope, dataScope), false},
|
||||
{"full capture commits full", fullScope, tmpl(fullScope, fullScope), false},
|
||||
{"full capture commits data via conversion", fullScope, tmpl(fullScope, dataScope), false},
|
||||
// Actors paused before content_scope existed fall back to the
|
||||
// template's onPause.
|
||||
{"unset capture falls back to onPause", ateapipb.SnapshotContentScope_SNAPSHOT_CONTENT_SCOPE_UNSPECIFIED, tmpl(atev1alpha1.SnapshotScopeData, atev1alpha1.SnapshotScopeFull), true},
|
||||
{"unset capture falls back to onPause", ateapipb.SnapshotContentScope_SNAPSHOT_CONTENT_SCOPE_UNSPECIFIED, tmpl(dataScope, fullScope), true},
|
||||
}
|
||||
for _, tc := range tests {
|
||||
t.Run(tc.name, func(t *testing.T) {
|
||||
@@ -637,7 +638,7 @@ func TestEnsurePausedSnapshotUploaded_Preconditions(t *testing.T) {
|
||||
},
|
||||
})
|
||||
|
||||
if _, err := w.ensurePausedSnapshotUploaded(ctx, resources.ActorRef{Atespace: "team-a", Name: "actor-1"}, created, mustTemplateFromCRD(&atev1alpha1.ActorTemplate{})); err == nil {
|
||||
if _, err := w.ensurePausedSnapshotUploaded(ctx, resources.ActorRef{Atespace: "team-a", Name: "actor-1"}, created, &ateapipb.ActorTemplate{}); err == nil {
|
||||
t.Fatal("ensurePausedSnapshotUploaded = nil, want error for missing node record")
|
||||
}
|
||||
|
||||
@@ -664,7 +665,7 @@ func TestEnsurePausedSnapshotUploaded_Preconditions(t *testing.T) {
|
||||
},
|
||||
})
|
||||
|
||||
tmpl := mustTemplateFromCRD(&atev1alpha1.ActorTemplate{Spec: atev1alpha1.ActorTemplateSpec{SnapshotsConfig: atev1alpha1.SnapshotsConfig{Location: "gs://snapshots"}}})
|
||||
tmpl := &ateapipb.ActorTemplate{SnapshotsConfig: &ateapipb.SnapshotsConfig{StorageLocation: "gs://snapshots"}}
|
||||
_, err := w.ensurePausedSnapshotUploaded(ctx, resources.ActorRef{Atespace: "team-a", Name: "actor-1"}, created, tmpl)
|
||||
if !errors.Is(err, ErrNoAteletOnNode) {
|
||||
t.Fatalf("ensurePausedSnapshotUploaded = %v, want ErrNoAteletOnNode", err)
|
||||
|
||||
@@ -54,7 +54,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, nil, "", nil)
|
||||
return NewActorWorkflow(st, nil, nil, nil, nil, nil, nil, "", nil)
|
||||
}
|
||||
|
||||
// seedWorkflowActor stores an actor with the given state, bound to the given
|
||||
|
||||
@@ -18,37 +18,31 @@ import (
|
||||
"testing"
|
||||
|
||||
"github.com/agent-substrate/substrate/internal/proto/ateletpb"
|
||||
atev1alpha1 "github.com/agent-substrate/substrate/pkg/api/v1alpha1"
|
||||
"github.com/agent-substrate/substrate/pkg/proto/ateapipb"
|
||||
"github.com/google/go-cmp/cmp"
|
||||
"google.golang.org/protobuf/testing/protocmp"
|
||||
corev1 "k8s.io/api/core/v1"
|
||||
"k8s.io/apimachinery/pkg/api/resource"
|
||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||
)
|
||||
|
||||
func TestWorkloadSpecFromActorTemplate(t *testing.T) {
|
||||
tests := []struct {
|
||||
name string
|
||||
template *atev1alpha1.ActorTemplate
|
||||
template *ateapipb.ActorTemplate
|
||||
want *ateletpb.WorkloadSpec
|
||||
}{
|
||||
{
|
||||
name: "converts DurableDir volume and mounts",
|
||||
template: &atev1alpha1.ActorTemplate{
|
||||
ObjectMeta: metav1.ObjectMeta{Name: "tmpl1", Namespace: "agent-ns"},
|
||||
Spec: atev1alpha1.ActorTemplateSpec{
|
||||
Volumes: []atev1alpha1.Volume{
|
||||
{Name: "home", VolumeSource: atev1alpha1.VolumeSource{DurableDir: &atev1alpha1.DurableDirVolumeSource{}}},
|
||||
},
|
||||
Containers: []atev1alpha1.Container{
|
||||
{
|
||||
Name: "main",
|
||||
Image: "main",
|
||||
VolumeMounts: []atev1alpha1.VolumeMount{
|
||||
{Name: "home", MountPath: "/home/user"},
|
||||
{Name: "home", MountPath: "/workspace"},
|
||||
},
|
||||
template: &ateapipb.ActorTemplate{
|
||||
Metadata: &ateapipb.ResourceMetadata{Atespace: "agent-ns", Name: "tmpl1"},
|
||||
Volumes: []*ateapipb.Volume{
|
||||
{Name: "home", Type: "DurableDir", DurableDir: &ateapipb.DurableDirVolumeSource{}},
|
||||
},
|
||||
Containers: []*ateapipb.Container{
|
||||
{
|
||||
Name: "main",
|
||||
Image: "main",
|
||||
VolumeMounts: []*ateapipb.VolumeMount{
|
||||
{Name: "home", MountPath: "/home/user"},
|
||||
{Name: "home", MountPath: "/workspace"},
|
||||
},
|
||||
},
|
||||
},
|
||||
@@ -74,34 +68,31 @@ func TestWorkloadSpecFromActorTemplate(t *testing.T) {
|
||||
},
|
||||
{
|
||||
name: "converts SystemInfo volume with actorMetadata items",
|
||||
template: &atev1alpha1.ActorTemplate{
|
||||
ObjectMeta: metav1.ObjectMeta{Name: "tmpl1", Namespace: "agent-ns"},
|
||||
Spec: atev1alpha1.ActorTemplateSpec{
|
||||
Volumes: []atev1alpha1.Volume{
|
||||
{
|
||||
Name: "system-info",
|
||||
VolumeSource: atev1alpha1.VolumeSource{
|
||||
SystemInfo: &atev1alpha1.SystemInfoVolumeSource{
|
||||
DataSources: []atev1alpha1.SystemInfoDataSource{
|
||||
{ActorMetadata: &atev1alpha1.ActorMetadataDataSource{
|
||||
Items: []atev1alpha1.ActorMetadataItem{
|
||||
{Field: atev1alpha1.ActorMetadataFieldName, Path: "actor-name"},
|
||||
{Field: atev1alpha1.ActorMetadataFieldAtespace, Path: "atespace"},
|
||||
{Field: atev1alpha1.ActorMetadataFieldUID, Path: "identity/actor-uid"},
|
||||
},
|
||||
}},
|
||||
template: &ateapipb.ActorTemplate{
|
||||
Metadata: &ateapipb.ResourceMetadata{Atespace: "agent-ns", Name: "tmpl1"},
|
||||
Volumes: []*ateapipb.Volume{
|
||||
{
|
||||
Name: "system-info",
|
||||
Type: "SystemInfo",
|
||||
SystemInfo: &ateapipb.SystemInfoVolumeSource{
|
||||
DataSources: []*ateapipb.SystemInfoDataSource{
|
||||
{ActorMetadata: &ateapipb.ActorMetadataDataSource{
|
||||
Items: []*ateapipb.ActorMetadataItem{
|
||||
{Field: ateapipb.ActorMetadataField_ACTOR_METADATA_FIELD_NAME, Path: "actor-name"},
|
||||
{Field: ateapipb.ActorMetadataField_ACTOR_METADATA_FIELD_ATESPACE, Path: "atespace"},
|
||||
{Field: ateapipb.ActorMetadataField_ACTOR_METADATA_FIELD_UID, Path: "identity/actor-uid"},
|
||||
},
|
||||
},
|
||||
}},
|
||||
},
|
||||
},
|
||||
},
|
||||
Containers: []atev1alpha1.Container{
|
||||
{
|
||||
Name: "main",
|
||||
Image: "main",
|
||||
VolumeMounts: []atev1alpha1.VolumeMount{
|
||||
{Name: "system-info", MountPath: "/run/ate"},
|
||||
},
|
||||
},
|
||||
Containers: []*ateapipb.Container{
|
||||
{
|
||||
Name: "main",
|
||||
Image: "main",
|
||||
VolumeMounts: []*ateapipb.VolumeMount{
|
||||
{Name: "system-info", MountPath: "/run/ate"},
|
||||
},
|
||||
},
|
||||
},
|
||||
@@ -140,21 +131,19 @@ func TestWorkloadSpecFromActorTemplate(t *testing.T) {
|
||||
},
|
||||
{
|
||||
name: "converts Image volume and mounts",
|
||||
template: &atev1alpha1.ActorTemplate{
|
||||
ObjectMeta: metav1.ObjectMeta{Name: "tmpl1", Namespace: "agent-ns"},
|
||||
Spec: atev1alpha1.ActorTemplateSpec{
|
||||
Volumes: []atev1alpha1.Volume{
|
||||
{Name: "home", VolumeSource: atev1alpha1.VolumeSource{DurableDir: &atev1alpha1.DurableDirVolumeSource{}}},
|
||||
{Name: "agent", VolumeSource: atev1alpha1.VolumeSource{Image: &atev1alpha1.ImageVolumeSource{Reference: "example.com/agent@sha256:abc"}}},
|
||||
},
|
||||
Containers: []atev1alpha1.Container{
|
||||
{
|
||||
Name: "main",
|
||||
Image: "main",
|
||||
VolumeMounts: []atev1alpha1.VolumeMount{
|
||||
{Name: "home", MountPath: "/home/user"},
|
||||
{Name: "agent", MountPath: "/ate"},
|
||||
},
|
||||
template: &ateapipb.ActorTemplate{
|
||||
Metadata: &ateapipb.ResourceMetadata{Atespace: "agent-ns", Name: "tmpl1"},
|
||||
Volumes: []*ateapipb.Volume{
|
||||
{Name: "home", Type: "DurableDir", DurableDir: &ateapipb.DurableDirVolumeSource{}},
|
||||
{Name: "agent", Type: "Image", Image: &ateapipb.ImageVolumeSource{Reference: "example.com/agent@sha256:abc"}},
|
||||
},
|
||||
Containers: []*ateapipb.Container{
|
||||
{
|
||||
Name: "main",
|
||||
Image: "main",
|
||||
VolumeMounts: []*ateapipb.VolumeMount{
|
||||
{Name: "home", MountPath: "/home/user"},
|
||||
{Name: "agent", MountPath: "/ate"},
|
||||
},
|
||||
},
|
||||
},
|
||||
@@ -184,28 +173,25 @@ func TestWorkloadSpecFromActorTemplate(t *testing.T) {
|
||||
},
|
||||
{
|
||||
name: "converts trustBundle data sources carrying the name for node-side resolution",
|
||||
template: &atev1alpha1.ActorTemplate{
|
||||
ObjectMeta: metav1.ObjectMeta{Name: "tmpl1", Namespace: "agent-ns"},
|
||||
Spec: atev1alpha1.ActorTemplateSpec{
|
||||
Volumes: []atev1alpha1.Volume{
|
||||
{
|
||||
Name: "system-info",
|
||||
VolumeSource: atev1alpha1.VolumeSource{
|
||||
SystemInfo: &atev1alpha1.SystemInfoVolumeSource{
|
||||
DataSources: []atev1alpha1.SystemInfoDataSource{
|
||||
{TrustBundle: &atev1alpha1.TrustBundleDataSource{Name: "egress-trust", Path: "trust/ca.pem"}},
|
||||
},
|
||||
},
|
||||
template: &ateapipb.ActorTemplate{
|
||||
Metadata: &ateapipb.ResourceMetadata{Atespace: "agent-ns", Name: "tmpl1"},
|
||||
Volumes: []*ateapipb.Volume{
|
||||
{
|
||||
Name: "system-info",
|
||||
Type: "SystemInfo",
|
||||
SystemInfo: &ateapipb.SystemInfoVolumeSource{
|
||||
DataSources: []*ateapipb.SystemInfoDataSource{
|
||||
{TrustBundle: &ateapipb.TrustBundleDataSource{Name: "egress-trust", Path: "trust/ca.pem"}},
|
||||
},
|
||||
},
|
||||
},
|
||||
Containers: []atev1alpha1.Container{
|
||||
{
|
||||
Name: "main",
|
||||
Image: "main",
|
||||
VolumeMounts: []atev1alpha1.VolumeMount{
|
||||
{Name: "system-info", MountPath: "/run/substrate/certs"},
|
||||
},
|
||||
},
|
||||
Containers: []*ateapipb.Container{
|
||||
{
|
||||
Name: "main",
|
||||
Image: "main",
|
||||
VolumeMounts: []*ateapipb.VolumeMount{
|
||||
{Name: "system-info", MountPath: "/run/substrate/certs"},
|
||||
},
|
||||
},
|
||||
},
|
||||
@@ -240,20 +226,18 @@ func TestWorkloadSpecFromActorTemplate(t *testing.T) {
|
||||
},
|
||||
{
|
||||
name: "skips non-DurableDir volumes",
|
||||
template: &atev1alpha1.ActorTemplate{
|
||||
ObjectMeta: metav1.ObjectMeta{Name: "tmpl1", Namespace: "agent-ns"},
|
||||
Spec: atev1alpha1.ActorTemplateSpec{
|
||||
Volumes: []atev1alpha1.Volume{
|
||||
{Name: "unsupported", VolumeSource: atev1alpha1.VolumeSource{}},
|
||||
{Name: "home", VolumeSource: atev1alpha1.VolumeSource{DurableDir: &atev1alpha1.DurableDirVolumeSource{}}},
|
||||
},
|
||||
Containers: []atev1alpha1.Container{
|
||||
{
|
||||
Name: "main",
|
||||
Image: "main",
|
||||
VolumeMounts: []atev1alpha1.VolumeMount{
|
||||
{Name: "home", MountPath: "/workspace"},
|
||||
},
|
||||
template: &ateapipb.ActorTemplate{
|
||||
Metadata: &ateapipb.ResourceMetadata{Atespace: "agent-ns", Name: "tmpl1"},
|
||||
Volumes: []*ateapipb.Volume{
|
||||
{Name: "unsupported"},
|
||||
{Name: "home", Type: "DurableDir", DurableDir: &ateapipb.DurableDirVolumeSource{}},
|
||||
},
|
||||
Containers: []*ateapipb.Container{
|
||||
{
|
||||
Name: "main",
|
||||
Image: "main",
|
||||
VolumeMounts: []*ateapipb.VolumeMount{
|
||||
{Name: "home", MountPath: "/workspace"},
|
||||
},
|
||||
},
|
||||
},
|
||||
@@ -278,15 +262,13 @@ func TestWorkloadSpecFromActorTemplate(t *testing.T) {
|
||||
},
|
||||
{
|
||||
name: "container without volume mounts has none",
|
||||
template: &atev1alpha1.ActorTemplate{
|
||||
ObjectMeta: metav1.ObjectMeta{Name: "tmpl1", Namespace: "agent-ns"},
|
||||
Spec: atev1alpha1.ActorTemplateSpec{
|
||||
Volumes: []atev1alpha1.Volume{
|
||||
{Name: "home", VolumeSource: atev1alpha1.VolumeSource{DurableDir: &atev1alpha1.DurableDirVolumeSource{}}},
|
||||
},
|
||||
Containers: []atev1alpha1.Container{
|
||||
{Name: "main", Image: "main"},
|
||||
},
|
||||
template: &ateapipb.ActorTemplate{
|
||||
Metadata: &ateapipb.ResourceMetadata{Atespace: "agent-ns", Name: "tmpl1"},
|
||||
Volumes: []*ateapipb.Volume{
|
||||
{Name: "home", Type: "DurableDir", DurableDir: &ateapipb.DurableDirVolumeSource{}},
|
||||
},
|
||||
Containers: []*ateapipb.Container{
|
||||
{Name: "main", Image: "main"},
|
||||
},
|
||||
},
|
||||
want: &ateletpb.WorkloadSpec{
|
||||
@@ -301,17 +283,15 @@ func TestWorkloadSpecFromActorTemplate(t *testing.T) {
|
||||
},
|
||||
{
|
||||
name: "maps literal env",
|
||||
template: &atev1alpha1.ActorTemplate{
|
||||
ObjectMeta: metav1.ObjectMeta{Name: "tmpl1", Namespace: "agent-ns"},
|
||||
Spec: atev1alpha1.ActorTemplateSpec{
|
||||
Containers: []atev1alpha1.Container{
|
||||
{
|
||||
Name: "main",
|
||||
Image: "main",
|
||||
Env: []atev1alpha1.EnvVar{
|
||||
{Name: "LITERAL", Value: "plain"},
|
||||
{Name: "EMPTY", Value: ""},
|
||||
},
|
||||
template: &ateapipb.ActorTemplate{
|
||||
Metadata: &ateapipb.ResourceMetadata{Atespace: "agent-ns", Name: "tmpl1"},
|
||||
Containers: []*ateapipb.Container{
|
||||
{
|
||||
Name: "main",
|
||||
Image: "main",
|
||||
Env: []*ateapipb.EnvVar{
|
||||
{Name: "LITERAL", Value: "plain"},
|
||||
{Name: "EMPTY", Value: ""},
|
||||
},
|
||||
},
|
||||
},
|
||||
@@ -329,16 +309,14 @@ func TestWorkloadSpecFromActorTemplate(t *testing.T) {
|
||||
},
|
||||
{
|
||||
name: "maps command and args",
|
||||
template: &atev1alpha1.ActorTemplate{
|
||||
ObjectMeta: metav1.ObjectMeta{Name: "tmpl1", Namespace: "agent-ns"},
|
||||
Spec: atev1alpha1.ActorTemplateSpec{
|
||||
Containers: []atev1alpha1.Container{
|
||||
{
|
||||
Name: "main",
|
||||
Image: "main",
|
||||
Command: []string{"/entrypoint"},
|
||||
Args: []string{"--foo", "--bar"},
|
||||
},
|
||||
template: &ateapipb.ActorTemplate{
|
||||
Metadata: &ateapipb.ResourceMetadata{Atespace: "agent-ns", Name: "tmpl1"},
|
||||
Containers: []*ateapipb.Container{
|
||||
{
|
||||
Name: "main",
|
||||
Image: "main",
|
||||
Command: []string{"/entrypoint"},
|
||||
Args: []string{"--foo", "--bar"},
|
||||
},
|
||||
},
|
||||
},
|
||||
@@ -355,7 +333,7 @@ func TestWorkloadSpecFromActorTemplate(t *testing.T) {
|
||||
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
got, err := workloadSpecFromActorTemplate(mustTemplateFromCRD(tt.template), nil)
|
||||
got, err := workloadSpecFromActorTemplate(tt.template, nil)
|
||||
if err != nil {
|
||||
t.Fatalf("workloadSpecFromActorTemplate failed: %v", err)
|
||||
}
|
||||
@@ -367,25 +345,23 @@ func TestWorkloadSpecFromActorTemplate(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestWorkloadSpecFromActorTemplatePropagatesReadyz(t *testing.T) {
|
||||
got, err := workloadSpecFromActorTemplate(mustTemplateFromCRD(&atev1alpha1.ActorTemplate{
|
||||
ObjectMeta: metav1.ObjectMeta{Name: "tmpl-readyz", Namespace: "agent-ns"},
|
||||
Spec: atev1alpha1.ActorTemplateSpec{
|
||||
Containers: []atev1alpha1.Container{
|
||||
{
|
||||
Name: "with-probe",
|
||||
Image: "main",
|
||||
Readyz: &atev1alpha1.ContainerReadyz{
|
||||
HTTPGet: &atev1alpha1.HTTPGetAction{Path: "/health", Port: 8080},
|
||||
TimeoutSeconds: 45,
|
||||
},
|
||||
},
|
||||
{
|
||||
Name: "without-probe",
|
||||
Image: "side",
|
||||
got, err := workloadSpecFromActorTemplate(&ateapipb.ActorTemplate{
|
||||
Metadata: &ateapipb.ResourceMetadata{Atespace: "agent-ns", Name: "tmpl-readyz"},
|
||||
Containers: []*ateapipb.Container{
|
||||
{
|
||||
Name: "with-probe",
|
||||
Image: "main",
|
||||
Readyz: &ateapipb.ContainerReadyz{
|
||||
HttpGet: &ateapipb.HTTPGetAction{Path: "/health", Port: 8080},
|
||||
TimeoutSeconds: 45,
|
||||
},
|
||||
},
|
||||
{
|
||||
Name: "without-probe",
|
||||
Image: "side",
|
||||
},
|
||||
},
|
||||
}), nil)
|
||||
}, nil)
|
||||
if err != nil {
|
||||
t.Fatalf("workloadSpecFromActorTemplate failed: %v", err)
|
||||
}
|
||||
@@ -412,42 +388,37 @@ func TestWorkloadSpecFromActorTemplatePropagatesReadyz(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestAppendExternalVolumes(t *testing.T) {
|
||||
template := mustTemplateFromCRD(&atev1alpha1.ActorTemplate{
|
||||
Spec: atev1alpha1.ActorTemplateSpec{
|
||||
Containers: []atev1alpha1.Container{
|
||||
{
|
||||
Name: "main",
|
||||
VolumeMounts: []atev1alpha1.VolumeMount{
|
||||
{Name: "vol-1", MountPath: "/mnt/vol1"},
|
||||
},
|
||||
},
|
||||
},
|
||||
Volumes: []atev1alpha1.Volume{
|
||||
{
|
||||
Name: "vol-1",
|
||||
VolumeSource: atev1alpha1.VolumeSource{
|
||||
ExternalVolumeTemplate: &atev1alpha1.ExternalVolumeTemplate{
|
||||
StorageClassName: "pd-standard",
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
Name: "vol-2",
|
||||
VolumeSource: atev1alpha1.VolumeSource{
|
||||
DurableDir: &atev1alpha1.DurableDirVolumeSource{},
|
||||
},
|
||||
},
|
||||
{
|
||||
Name: "unmounted-vol",
|
||||
VolumeSource: atev1alpha1.VolumeSource{
|
||||
ExternalVolumeTemplate: &atev1alpha1.ExternalVolumeTemplate{
|
||||
StorageClassName: "pd-standard",
|
||||
},
|
||||
},
|
||||
template := &ateapipb.ActorTemplate{
|
||||
Containers: []*ateapipb.Container{
|
||||
{
|
||||
Name: "main",
|
||||
VolumeMounts: []*ateapipb.VolumeMount{
|
||||
{Name: "vol-1", MountPath: "/mnt/vol1"},
|
||||
},
|
||||
},
|
||||
},
|
||||
})
|
||||
Volumes: []*ateapipb.Volume{
|
||||
{
|
||||
Name: "vol-1",
|
||||
Type: "ExternalVolumeTemplate",
|
||||
ExternalVolumeTemplate: &ateapipb.ExternalVolumeTemplate{
|
||||
StorageClassName: "pd-standard",
|
||||
},
|
||||
},
|
||||
{
|
||||
Name: "vol-2",
|
||||
Type: "DurableDir",
|
||||
DurableDir: &ateapipb.DurableDirVolumeSource{},
|
||||
},
|
||||
{
|
||||
Name: "unmounted-vol",
|
||||
Type: "ExternalVolumeTemplate",
|
||||
ExternalVolumeTemplate: &ateapipb.ExternalVolumeTemplate{
|
||||
StorageClassName: "pd-standard",
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
actor := &ateapipb.Actor{
|
||||
Metadata: &ateapipb.ResourceMetadata{
|
||||
@@ -504,34 +475,32 @@ func TestAppendExternalVolumes(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestWorkloadSpecFromActorTemplatePropagatesSecurityContext(t *testing.T) {
|
||||
got, err := workloadSpecFromActorTemplate(mustTemplateFromCRD(&atev1alpha1.ActorTemplate{
|
||||
ObjectMeta: metav1.ObjectMeta{Name: "tmpl-caps", Namespace: "agent-ns"},
|
||||
Spec: atev1alpha1.ActorTemplateSpec{
|
||||
Containers: []atev1alpha1.Container{
|
||||
{
|
||||
Name: "adjusted",
|
||||
Image: "main",
|
||||
SecurityContext: &atev1alpha1.SecurityContext{
|
||||
Capabilities: &atev1alpha1.Capabilities{
|
||||
Add: []atev1alpha1.Capability{"NET_ADMIN"},
|
||||
Drop: []atev1alpha1.Capability{"ALL"},
|
||||
},
|
||||
got, err := workloadSpecFromActorTemplate(&ateapipb.ActorTemplate{
|
||||
Metadata: &ateapipb.ResourceMetadata{Atespace: "agent-ns", Name: "tmpl-caps"},
|
||||
Containers: []*ateapipb.Container{
|
||||
{
|
||||
Name: "adjusted",
|
||||
Image: "main",
|
||||
SecurityContext: &ateapipb.SecurityContext{
|
||||
Capabilities: &ateapipb.Capabilities{
|
||||
Add: []string{"NET_ADMIN"},
|
||||
Drop: []string{"ALL"},
|
||||
},
|
||||
},
|
||||
{
|
||||
Name: "unset",
|
||||
Image: "side",
|
||||
},
|
||||
{
|
||||
// An empty capabilities block asks for no adjustment, so
|
||||
// nothing is put on the wire for it.
|
||||
Name: "empty",
|
||||
Image: "third",
|
||||
SecurityContext: &atev1alpha1.SecurityContext{Capabilities: &atev1alpha1.Capabilities{}},
|
||||
},
|
||||
},
|
||||
{
|
||||
Name: "unset",
|
||||
Image: "side",
|
||||
},
|
||||
{
|
||||
// An empty capabilities block asks for no adjustment, so
|
||||
// nothing is put on the wire for it.
|
||||
Name: "empty",
|
||||
Image: "third",
|
||||
SecurityContext: &ateapipb.SecurityContext{Capabilities: &ateapipb.Capabilities{}},
|
||||
},
|
||||
},
|
||||
}), nil)
|
||||
}, nil)
|
||||
if err != nil {
|
||||
t.Fatalf("workloadSpecFromActorTemplate failed: %v", err)
|
||||
}
|
||||
@@ -634,25 +603,21 @@ func TestToAteletResources(t *testing.T) {
|
||||
// literal leaves every test in the repository green while limits never leave
|
||||
// ate-api-server.
|
||||
func TestWorkloadSpecFromActorTemplatePropagatesResources(t *testing.T) {
|
||||
got, err := workloadSpecFromActorTemplate(mustTemplateFromCRD(&atev1alpha1.ActorTemplate{
|
||||
ObjectMeta: metav1.ObjectMeta{Name: "tmpl-limits", Namespace: "agent-ns"},
|
||||
Spec: atev1alpha1.ActorTemplateSpec{
|
||||
SandboxClass: atev1alpha1.SandboxClassMicroVM,
|
||||
Containers: []atev1alpha1.Container{
|
||||
{
|
||||
Name: "limited",
|
||||
Image: "main",
|
||||
Resources: &atev1alpha1.ContainerResources{
|
||||
Limits: atev1alpha1.ContainerResourceList{
|
||||
corev1.ResourceMemory: resource.MustParse("256Mi"),
|
||||
corev1.ResourceCPU: resource.MustParse("200m"),
|
||||
},
|
||||
},
|
||||
},
|
||||
{Name: "unlimited", Image: "main"},
|
||||
got, err := workloadSpecFromActorTemplate(&ateapipb.ActorTemplate{
|
||||
Metadata: &ateapipb.ResourceMetadata{Atespace: "agent-ns", Name: "tmpl-limits"},
|
||||
SandboxConfig: &ateapipb.SandboxConfig{SandboxClass: ateapipb.SandboxClass_SANDBOX_CLASS_MICROVM},
|
||||
Containers: []*ateapipb.Container{
|
||||
{
|
||||
Name: "limited",
|
||||
Image: "main",
|
||||
Resources: &ateapipb.Resources{Limits: []*ateapipb.Limits{
|
||||
{Name: "cpu", Quantity: "200m"},
|
||||
{Name: "memory", Quantity: "256Mi"},
|
||||
}},
|
||||
},
|
||||
{Name: "unlimited", Image: "main"},
|
||||
},
|
||||
}), nil)
|
||||
}, nil)
|
||||
if err != nil {
|
||||
t.Fatalf("workloadSpecFromActorTemplate failed: %v", err)
|
||||
}
|
||||
|
||||
@@ -87,76 +87,6 @@ func Validate_Actor(
|
||||
errs = append(errs, fn(fldPath.Child("metadata"), obj.Metadata, oldVal, oldObj != nil)...)
|
||||
}
|
||||
|
||||
{ // field ateapipb.Actor.ActorTemplateNamespace
|
||||
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.Immutable(ctx, op, fldPath, obj, oldObj).MarkShortCircuit(); len(e) != 0 {
|
||||
errs = append(errs, e...)
|
||||
earlyReturn = true
|
||||
}
|
||||
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.Actor) *string {
|
||||
return &oldObj.ActorTemplateNamespace
|
||||
})
|
||||
errs = append(errs, fn(fldPath.Child("actor_template_namespace"), &obj.ActorTemplateNamespace, oldVal, oldObj != nil)...)
|
||||
}
|
||||
|
||||
{ // field ateapipb.Actor.ActorTemplateName
|
||||
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.Immutable(ctx, op, fldPath, obj, oldObj).MarkShortCircuit(); len(e) != 0 {
|
||||
errs = append(errs, e...)
|
||||
earlyReturn = true
|
||||
}
|
||||
if e := validate.OptionalValue(ctx, op, fldPath, obj, oldObj).MarkShortCircuit(); len(e) != 0 {
|
||||
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.Actor) *string {
|
||||
return &oldObj.ActorTemplateName
|
||||
})
|
||||
errs = append(errs, fn(fldPath.Child("actor_template_name"), &obj.ActorTemplateName, oldVal, oldObj != nil)...)
|
||||
}
|
||||
|
||||
{ // field ateapipb.Actor.ActorTemplate
|
||||
fn := func(
|
||||
fldPath *field.Path,
|
||||
@@ -170,13 +100,10 @@ func Validate_Actor(
|
||||
}
|
||||
// call field-attached validations
|
||||
earlyReturn := false
|
||||
if e := validate.Immutable(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 e := validate.OptionalPointer(ctx, op, fldPath, obj, oldObj).MarkShortCircuit(); len(e) != 0 {
|
||||
earlyReturn = true
|
||||
}
|
||||
if earlyReturn {
|
||||
return // do not proceed
|
||||
}
|
||||
|
||||
@@ -158,10 +158,9 @@ func TestUpdateActor_ConcurrentWriteReturnsConflict(t *testing.T) {
|
||||
ctx := context.Background()
|
||||
createTestAtespace(t, s, "team-a")
|
||||
created, err := s.CreateActor(ctx, &ateapipb.Actor{
|
||||
Metadata: &ateapipb.ResourceMetadata{Atespace: "team-a", Name: "actor-a"},
|
||||
ActorTemplateNamespace: "default",
|
||||
ActorTemplateName: "template-a",
|
||||
Status: &ateapipb.ActorStatus{State: ateapipb.ActorState_ACTOR_STATE_SUSPENDED},
|
||||
Metadata: &ateapipb.ResourceMetadata{Atespace: "team-a", Name: "actor-a"},
|
||||
ActorTemplate: &ateapipb.ObjectRef{Atespace: "default", Name: "template-a"},
|
||||
Status: &ateapipb.ActorStatus{State: ateapipb.ActorState_ACTOR_STATE_SUSPENDED},
|
||||
})
|
||||
if err != nil {
|
||||
t.Fatalf("CreateActor failed: %v", err)
|
||||
@@ -353,10 +352,9 @@ func TestCreateActor_MissingAtespace_FailedPrecondition(t *testing.T) {
|
||||
ctx := context.Background()
|
||||
|
||||
actor := &ateapipb.Actor{
|
||||
Metadata: &ateapipb.ResourceMetadata{Name: "id1", Atespace: "no-such-atespace"},
|
||||
ActorTemplateNamespace: "ns1",
|
||||
ActorTemplateName: "tmpl1",
|
||||
Status: &ateapipb.ActorStatus{State: ateapipb.ActorState_ACTOR_STATE_SUSPENDED},
|
||||
Metadata: &ateapipb.ResourceMetadata{Name: "id1", Atespace: "no-such-atespace"},
|
||||
ActorTemplate: &ateapipb.ObjectRef{Atespace: "ns1", Name: "tmpl1"},
|
||||
Status: &ateapipb.ActorStatus{State: ateapipb.ActorState_ACTOR_STATE_SUSPENDED},
|
||||
}
|
||||
if _, err := s.CreateActor(ctx, actor); !errors.Is(err, store.ErrFailedPrecondition) {
|
||||
t.Errorf("CreateActor with missing atespace = %v, want ErrFailedPrecondition", err)
|
||||
|
||||
@@ -336,10 +336,9 @@ func runActorContractTests(t *testing.T, setup func(t *testing.T) store.Interfac
|
||||
mustCreateAtespace(t, s, testAtespace)
|
||||
|
||||
actor := &ateapipb.Actor{
|
||||
Metadata: &ateapipb.ResourceMetadata{Name: "session-1", Atespace: testAtespace},
|
||||
ActorTemplateNamespace: "default",
|
||||
ActorTemplateName: "test-template",
|
||||
Status: &ateapipb.ActorStatus{State: ateapipb.ActorState_ACTOR_STATE_SUSPENDED},
|
||||
Metadata: &ateapipb.ResourceMetadata{Name: "session-1", Atespace: testAtespace},
|
||||
ActorTemplate: &ateapipb.ObjectRef{Atespace: "default", Name: "test-template"},
|
||||
Status: &ateapipb.ActorStatus{State: ateapipb.ActorState_ACTOR_STATE_SUSPENDED},
|
||||
}
|
||||
|
||||
created, err := s.CreateActor(ctx, actor)
|
||||
@@ -382,10 +381,9 @@ func runActorContractTests(t *testing.T, setup func(t *testing.T) store.Interfac
|
||||
mustCreateAtespace(t, s, testAtespace)
|
||||
|
||||
actor := &ateapipb.Actor{
|
||||
Metadata: &ateapipb.ResourceMetadata{Name: "session-1", Atespace: testAtespace},
|
||||
ActorTemplateNamespace: "default",
|
||||
ActorTemplateName: "test-template",
|
||||
Status: &ateapipb.ActorStatus{State: ateapipb.ActorState_ACTOR_STATE_SUSPENDED},
|
||||
Metadata: &ateapipb.ResourceMetadata{Name: "session-1", Atespace: testAtespace},
|
||||
ActorTemplate: &ateapipb.ObjectRef{Atespace: "default", Name: "test-template"},
|
||||
Status: &ateapipb.ActorStatus{State: ateapipb.ActorState_ACTOR_STATE_SUSPENDED},
|
||||
}
|
||||
|
||||
if _, err := s.CreateActor(ctx, actor); err != nil {
|
||||
@@ -402,10 +400,9 @@ func runActorContractTests(t *testing.T, setup func(t *testing.T) store.Interfac
|
||||
mustCreateAtespace(t, s, testAtespace)
|
||||
|
||||
actor := &ateapipb.Actor{
|
||||
Metadata: &ateapipb.ResourceMetadata{Name: "session-1", Atespace: testAtespace},
|
||||
ActorTemplateNamespace: "default",
|
||||
ActorTemplateName: "test-template",
|
||||
Status: &ateapipb.ActorStatus{State: ateapipb.ActorState_ACTOR_STATE_SUSPENDED},
|
||||
Metadata: &ateapipb.ResourceMetadata{Name: "session-1", Atespace: testAtespace},
|
||||
ActorTemplate: &ateapipb.ObjectRef{Atespace: "default", Name: "test-template"},
|
||||
Status: &ateapipb.ActorStatus{State: ateapipb.ActorState_ACTOR_STATE_SUSPENDED},
|
||||
}
|
||||
|
||||
created, err := s.CreateActor(ctx, actor)
|
||||
@@ -459,10 +456,9 @@ func runActorContractTests(t *testing.T, setup func(t *testing.T) store.Interfac
|
||||
mustCreateAtespace(t, s, testAtespace)
|
||||
|
||||
actor := &ateapipb.Actor{
|
||||
Metadata: &ateapipb.ResourceMetadata{Name: "session-1", Atespace: testAtespace},
|
||||
ActorTemplateNamespace: "default",
|
||||
ActorTemplateName: "test-template",
|
||||
Status: &ateapipb.ActorStatus{State: ateapipb.ActorState_ACTOR_STATE_SUSPENDED},
|
||||
Metadata: &ateapipb.ResourceMetadata{Name: "session-1", Atespace: testAtespace},
|
||||
ActorTemplate: &ateapipb.ObjectRef{Atespace: "default", Name: "test-template"},
|
||||
Status: &ateapipb.ActorStatus{State: ateapipb.ActorState_ACTOR_STATE_SUSPENDED},
|
||||
}
|
||||
|
||||
if _, err := s.CreateActor(ctx, actor); err != nil {
|
||||
@@ -502,10 +498,9 @@ func runActorContractTests(t *testing.T, setup func(t *testing.T) store.Interfac
|
||||
|
||||
newActor := func() *ateapipb.Actor {
|
||||
return &ateapipb.Actor{
|
||||
Metadata: &ateapipb.ResourceMetadata{Name: "session-1", Atespace: testAtespace},
|
||||
ActorTemplateNamespace: "default",
|
||||
ActorTemplateName: "test-template",
|
||||
Status: &ateapipb.ActorStatus{State: ateapipb.ActorState_ACTOR_STATE_SUSPENDED},
|
||||
Metadata: &ateapipb.ResourceMetadata{Name: "session-1", Atespace: testAtespace},
|
||||
ActorTemplate: &ateapipb.ObjectRef{Atespace: "default", Name: "test-template"},
|
||||
Status: &ateapipb.ActorStatus{State: ateapipb.ActorState_ACTOR_STATE_SUSPENDED},
|
||||
}
|
||||
}
|
||||
|
||||
@@ -557,10 +552,9 @@ func runActorContractTests(t *testing.T, setup func(t *testing.T) store.Interfac
|
||||
mustCreateAtespace(t, s, testAtespace)
|
||||
|
||||
created, err := s.CreateActor(ctx, &ateapipb.Actor{
|
||||
Metadata: &ateapipb.ResourceMetadata{Name: "session-1", Atespace: testAtespace},
|
||||
ActorTemplateNamespace: "default",
|
||||
ActorTemplateName: "test-template",
|
||||
Status: &ateapipb.ActorStatus{State: ateapipb.ActorState_ACTOR_STATE_SUSPENDED},
|
||||
Metadata: &ateapipb.ResourceMetadata{Name: "session-1", Atespace: testAtespace},
|
||||
ActorTemplate: &ateapipb.ObjectRef{Atespace: "default", Name: "test-template"},
|
||||
Status: &ateapipb.ActorStatus{State: ateapipb.ActorState_ACTOR_STATE_SUSPENDED},
|
||||
})
|
||||
if err != nil {
|
||||
t.Fatalf("CreateActor failed: %v", err)
|
||||
@@ -589,10 +583,9 @@ func runActorContractTests(t *testing.T, setup func(t *testing.T) store.Interfac
|
||||
mustCreateAtespace(t, s, testAtespace)
|
||||
|
||||
created, err := s.CreateActor(ctx, &ateapipb.Actor{
|
||||
Metadata: &ateapipb.ResourceMetadata{Name: "session-1", Atespace: testAtespace},
|
||||
ActorTemplateNamespace: "default",
|
||||
ActorTemplateName: "test-template",
|
||||
Status: &ateapipb.ActorStatus{State: ateapipb.ActorState_ACTOR_STATE_SUSPENDED},
|
||||
Metadata: &ateapipb.ResourceMetadata{Name: "session-1", Atespace: testAtespace},
|
||||
ActorTemplate: &ateapipb.ObjectRef{Atespace: "default", Name: "test-template"},
|
||||
Status: &ateapipb.ActorStatus{State: ateapipb.ActorState_ACTOR_STATE_SUSPENDED},
|
||||
})
|
||||
if err != nil {
|
||||
t.Fatalf("CreateActor failed: %v", err)
|
||||
@@ -654,10 +647,9 @@ func runActorContractTests(t *testing.T, setup func(t *testing.T) store.Interfac
|
||||
mustCreateAtespace(t, s, testAtespace)
|
||||
|
||||
actor := &ateapipb.Actor{
|
||||
Metadata: &ateapipb.ResourceMetadata{Name: "session-1", Atespace: testAtespace},
|
||||
ActorTemplateNamespace: "default",
|
||||
ActorTemplateName: "test-template",
|
||||
Status: &ateapipb.ActorStatus{State: tt.state},
|
||||
Metadata: &ateapipb.ResourceMetadata{Name: "session-1", Atespace: testAtespace},
|
||||
ActorTemplate: &ateapipb.ObjectRef{Atespace: "default", Name: "test-template"},
|
||||
Status: &ateapipb.ActorStatus{State: tt.state},
|
||||
}
|
||||
if _, err := s.CreateActor(ctx, actor); err != nil {
|
||||
t.Fatalf("CreateActor failed: %v", err)
|
||||
@@ -718,18 +710,16 @@ func runActorContractTests(t *testing.T, setup func(t *testing.T) store.Interfac
|
||||
mustCreateAtespace(t, s, testAtespace)
|
||||
|
||||
actor1 := &ateapipb.Actor{
|
||||
Metadata: &ateapipb.ResourceMetadata{Name: "id1", Atespace: testAtespace},
|
||||
ActorTemplateNamespace: "ns1",
|
||||
ActorTemplateName: "tmpl1",
|
||||
Metadata: &ateapipb.ResourceMetadata{Name: "id1", Atespace: testAtespace},
|
||||
ActorTemplate: &ateapipb.ObjectRef{Atespace: "ns1", Name: "tmpl1"},
|
||||
Status: &ateapipb.ActorStatus{
|
||||
State: ateapipb.ActorState_ACTOR_STATE_SUSPENDED,
|
||||
LatestSnapshot: &ateapipb.ObjectRef{Atespace: testAtespace, Name: "snapshot-1"},
|
||||
},
|
||||
}
|
||||
actor2 := &ateapipb.Actor{
|
||||
Metadata: &ateapipb.ResourceMetadata{Name: "id2", Atespace: testAtespace},
|
||||
ActorTemplateNamespace: "ns1",
|
||||
ActorTemplateName: "tmpl1",
|
||||
Metadata: &ateapipb.ResourceMetadata{Name: "id2", Atespace: testAtespace},
|
||||
ActorTemplate: &ateapipb.ObjectRef{Atespace: "ns1", Name: "tmpl1"},
|
||||
Status: &ateapipb.ActorStatus{
|
||||
State: ateapipb.ActorState_ACTOR_STATE_SUSPENDED,
|
||||
LatestSnapshot: &ateapipb.ObjectRef{Atespace: testAtespace, Name: "snapshot-2"},
|
||||
@@ -762,10 +752,9 @@ func runActorContractTests(t *testing.T, setup func(t *testing.T) store.Interfac
|
||||
|
||||
for i := 0; i < 5; i++ {
|
||||
actor := &ateapipb.Actor{
|
||||
Metadata: &ateapipb.ResourceMetadata{Name: fmt.Sprintf("name%d", i), Atespace: testAtespace},
|
||||
ActorTemplateNamespace: "ns1",
|
||||
ActorTemplateName: "tmpl1",
|
||||
Status: &ateapipb.ActorStatus{State: ateapipb.ActorState_ACTOR_STATE_SUSPENDED},
|
||||
Metadata: &ateapipb.ResourceMetadata{Name: fmt.Sprintf("name%d", i), Atespace: testAtespace},
|
||||
ActorTemplate: &ateapipb.ObjectRef{Atespace: "ns1", Name: "tmpl1"},
|
||||
Status: &ateapipb.ActorStatus{State: ateapipb.ActorState_ACTOR_STATE_SUSPENDED},
|
||||
}
|
||||
if _, err := s.CreateActor(ctx, actor); err != nil {
|
||||
t.Fatalf("failed to create actor %d: %v", i, err)
|
||||
@@ -806,10 +795,9 @@ func runActorContractTests(t *testing.T, setup func(t *testing.T) store.Interfac
|
||||
|
||||
mkActor := func(atespace, name string) *ateapipb.Actor {
|
||||
return &ateapipb.Actor{
|
||||
Metadata: &ateapipb.ResourceMetadata{Name: name, Atespace: atespace},
|
||||
ActorTemplateNamespace: "ns1",
|
||||
ActorTemplateName: "tmpl1",
|
||||
Status: &ateapipb.ActorStatus{State: ateapipb.ActorState_ACTOR_STATE_SUSPENDED},
|
||||
Metadata: &ateapipb.ResourceMetadata{Name: name, Atespace: atespace},
|
||||
ActorTemplate: &ateapipb.ObjectRef{Atespace: "ns1", Name: "tmpl1"},
|
||||
Status: &ateapipb.ActorStatus{State: ateapipb.ActorState_ACTOR_STATE_SUSPENDED},
|
||||
}
|
||||
}
|
||||
for _, a := range []*ateapipb.Actor{mkActor("team-a", "a1"), mkActor("team-a", "a2"), mkActor("team-b", "b1")} {
|
||||
@@ -1256,8 +1244,8 @@ func runWorkerContractTests(t *testing.T, setup func(t *testing.T) store.Interfa
|
||||
defer watch.Close()
|
||||
|
||||
assignment := &ateapipb.ActorAssignment{
|
||||
ActorTemplate: &ateapipb.KubeNamespacedObjectRef{Namespace: "default", Name: "test-template"},
|
||||
Actor: &ateapipb.ObjectRef{Name: "session-1"},
|
||||
ActorTemplateRef: &ateapipb.ObjectRef{Atespace: "default", Name: "test-template"},
|
||||
Actor: &ateapipb.ObjectRef{Name: "session-1"},
|
||||
}
|
||||
updated, err := s.UpdateWorker(ctx, testWorkerName, store.PreconditionFrom(created), func(toUpdate *ateapipb.Worker) error {
|
||||
toUpdate.Status.Assignment = assignment
|
||||
|
||||
+1
-2
@@ -151,7 +151,6 @@ func main() {
|
||||
}
|
||||
|
||||
ateFactory := externalversions.NewSharedInformerFactory(ateClient, 0)
|
||||
actorTemplateLister := ateFactory.Api().V1alpha1().ActorTemplates().Lister()
|
||||
workerPoolLister := ateFactory.Api().V1alpha1().WorkerPools().Lister()
|
||||
sandboxConfigLister := ateFactory.Api().V1alpha1().SandboxConfigs().Lister()
|
||||
csiDriverConfigLister := ateFactory.Api().V1alpha1().CSIDriverConfigs().Lister()
|
||||
@@ -187,7 +186,7 @@ func main() {
|
||||
|
||||
volPlugins := make(map[string]volume.VolumePluginControlPlane)
|
||||
ateletDialer := controlapi.NewAteletDialer(workerPodInformer.GetIndexer(), ateletPodInformer.GetIndexer(), *ateletClientCredBundle, *podIdentityCACerts)
|
||||
controlSrv := controlapi.NewRPCService(persistence, workerCache, actorTemplateLister, workerPoolLister, sandboxConfigLister, csiDriverConfigLister, storageClassLister, ateletDialer, instruments, *egressGatewayAddress, volPlugins)
|
||||
controlSrv := controlapi.NewRPCService(persistence, workerCache, workerPoolLister, sandboxConfigLister, csiDriverConfigLister, storageClassLister, ateletDialer, instruments, *egressGatewayAddress, volPlugins)
|
||||
|
||||
// Drive stored ActorTemplates through the golden actor flow.
|
||||
templateReconciler := controlapi.NewActorTemplateReconciler(persistence, controlSrv, sandboxConfigLister)
|
||||
|
||||
@@ -100,8 +100,10 @@ func (r *ActorTemplateReconciler) Reconcile(ctx context.Context, req ctrl.Reques
|
||||
Atespace: resources.GoldenActorAtespace,
|
||||
Name: actorName,
|
||||
},
|
||||
ActorTemplateNamespace: at.ObjectMeta.Namespace,
|
||||
ActorTemplateName: at.ObjectMeta.Name,
|
||||
ActorTemplate: &ateapipb.ObjectRef{
|
||||
Atespace: at.ObjectMeta.Namespace,
|
||||
Name: at.ObjectMeta.Name,
|
||||
},
|
||||
},
|
||||
}
|
||||
_, err = r.AteClient.CreateActor(ctx, createReq)
|
||||
|
||||
@@ -155,7 +155,7 @@ func (s *Server) processRequestHeaders(
|
||||
}
|
||||
|
||||
elapsed := time.Since(start)
|
||||
s.recordRouteDuration(ctx, elapsed, res.TemplateNamespace, res.TemplateName, classifyOutcome(err), res.resume())
|
||||
s.recordRouteDuration(ctx, elapsed, res.TemplateAtespace, res.TemplateName, classifyOutcome(err), res.resume())
|
||||
|
||||
if err != nil {
|
||||
slog.ErrorContext(ctx, "Error during ext_proc RequestHeaders processing",
|
||||
|
||||
@@ -52,12 +52,12 @@ type Result struct {
|
||||
// /statusz page. Empty for handlers that do not pick an upstream.
|
||||
Target string
|
||||
|
||||
// TemplateNamespace and TemplateName identify the actor template the
|
||||
// TemplateAtespace and TemplateName identify the actor template the
|
||||
// request resolved to. They are the low-cardinality attributes on the
|
||||
// route-duration metric, and are empty when the direction has no template
|
||||
// (or the request failed before resolving one).
|
||||
TemplateNamespace string
|
||||
TemplateName string
|
||||
TemplateAtespace string
|
||||
TemplateName string
|
||||
|
||||
// Resume is the actor-resume outcome, as one of the ateattr.RouterResume*
|
||||
// values. Empty means "none" — the direction never resumes an actor, or the
|
||||
|
||||
@@ -136,9 +136,9 @@ func (h *Handler) HandleRequestHeaders(ctx context.Context, md *extproc.RequestM
|
||||
// Actor template identity, used as low-cardinality route-latency metric
|
||||
// attributes.
|
||||
res := extproc.Result{
|
||||
TemplateNamespace: actor.GetActorTemplateNamespace(),
|
||||
TemplateName: actor.GetActorTemplateName(),
|
||||
Resume: string(resumeOutcome),
|
||||
TemplateAtespace: actor.GetActorTemplate().GetAtespace(),
|
||||
TemplateName: actor.GetActorTemplate().GetName(),
|
||||
Resume: string(resumeOutcome),
|
||||
}
|
||||
|
||||
workerIP := actor.GetStatus().GetWorkerAssignment().GetWorkerPodIp()
|
||||
|
||||
@@ -16,7 +16,6 @@ package cmd
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"strings"
|
||||
|
||||
"github.com/agent-substrate/substrate/cmd/kubectl-ate/internal/printer"
|
||||
"github.com/agent-substrate/substrate/internal/ateclient"
|
||||
@@ -24,7 +23,6 @@ import (
|
||||
"github.com/spf13/cobra"
|
||||
)
|
||||
|
||||
var templateFlag string
|
||||
var templateRefFlag string
|
||||
var atespaceFlag string
|
||||
var sourceSnapshotTagFlag string
|
||||
@@ -34,7 +32,7 @@ var createActorCmd = &cobra.Command{
|
||||
Short: "Create an actor",
|
||||
Args: cobra.ExactArgs(1),
|
||||
RunE: func(cmd *cobra.Command, args []string) error {
|
||||
request, err := buildCreateActorRequest(args[0], atespaceFlag, templateFlag, templateRefFlag, sourceSnapshotTagFlag)
|
||||
request, err := buildCreateActorRequest(args[0], atespaceFlag, templateRefFlag, sourceSnapshotTagFlag)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
@@ -56,30 +54,19 @@ var createActorCmd = &cobra.Command{
|
||||
}
|
||||
|
||||
// buildCreateActorRequest assembles the CreateActor request from the command
|
||||
// flags. Exactly one of template (legacy CRD reference) and templateRef
|
||||
// (substrate ActorTemplate resource) is set; cobra enforces that.
|
||||
func buildCreateActorRequest(actorName, atespace, template, templateRef, snapshotTag string) (*ateapipb.CreateActorRequest, error) {
|
||||
// flags.
|
||||
func buildCreateActorRequest(actorName, atespace, templateName, snapshotTag string) (*ateapipb.CreateActorRequest, error) {
|
||||
actor := &ateapipb.Actor{
|
||||
Metadata: &ateapipb.ResourceMetadata{
|
||||
Atespace: atespace,
|
||||
Name: actorName,
|
||||
},
|
||||
ActorTemplate: &ateapipb.ObjectRef{
|
||||
Atespace: atespace,
|
||||
Name: templateName,
|
||||
},
|
||||
}
|
||||
if templateRef != "" {
|
||||
// The template name is resolved in the actor's atespace; cross-atespace
|
||||
// references are intentionally not expressible here.
|
||||
if strings.Contains(templateRef, "/") {
|
||||
return nil, fmt.Errorf("malformed --template-ref: %s (expected a bare template name, resolved in the actor's atespace)", templateRef)
|
||||
}
|
||||
actor.ActorTemplate = &ateapipb.ObjectRef{Atespace: atespace, Name: templateRef}
|
||||
} else {
|
||||
parts := strings.Split(template, "/")
|
||||
if len(parts) != 2 {
|
||||
return nil, fmt.Errorf("malformed --template: %s (expected <namespace>/<name>)", template)
|
||||
}
|
||||
actor.ActorTemplateNamespace = parts[0]
|
||||
actor.ActorTemplateName = parts[1]
|
||||
}
|
||||
|
||||
if snapshotTag != "" {
|
||||
ref, err := parseNamespacedName(snapshotTag)
|
||||
if err != nil {
|
||||
@@ -91,11 +78,9 @@ func buildCreateActorRequest(actorName, atespace, template, templateRef, snapsho
|
||||
}
|
||||
|
||||
func init() {
|
||||
createActorCmd.Flags().StringVarP(&templateFlag, "template", "t", "", "Legacy ActorTemplate CRD to derive the actor from, in <namespace>/<name> format")
|
||||
// TODO: rename "template-ref" to "template" when we fully cutover.
|
||||
// TODO: rename "template-ref" to "template" now that the legacy CRD flag is gone.
|
||||
createActorCmd.Flags().StringVar(&templateRefFlag, "template-ref", "", "Name of the substrate ActorTemplate resource to derive the actor from, resolved in the actor's atespace (--atespace)")
|
||||
createActorCmd.MarkFlagsMutuallyExclusive("template", "template-ref")
|
||||
createActorCmd.MarkFlagsOneRequired("template", "template-ref")
|
||||
_ = createActorCmd.MarkFlagRequired("template-ref")
|
||||
createActorCmd.Flags().StringVarP(&atespaceFlag, "atespace", "a", "", "Atespace to create the actor in (required)")
|
||||
_ = createActorCmd.MarkFlagRequired("atespace")
|
||||
createActorCmd.Flags().StringVar(&sourceSnapshotTagFlag, "snapshot-tag", "", "Initialize from an ActorSnapshot tag in <atespace>/<name> format")
|
||||
|
||||
@@ -25,7 +25,6 @@ import (
|
||||
func TestBuildCreateActorRequest(t *testing.T) {
|
||||
tests := []struct {
|
||||
name string
|
||||
template string
|
||||
templateRef string
|
||||
snapshotTag string
|
||||
want *ateapipb.Actor
|
||||
@@ -49,13 +48,12 @@ func TestBuildCreateActorRequest(t *testing.T) {
|
||||
SourceSnapshotTag: &ateapipb.ObjectRef{Atespace: "demo", Name: "before-upgrade"},
|
||||
},
|
||||
},
|
||||
{name: "malformed template ref", templateRef: "ate-demo-counter-substrate/counter", wantErr: true},
|
||||
{name: "malformed snapshot tag", templateRef: "counter", snapshotTag: "before-upgrade", wantErr: true},
|
||||
}
|
||||
|
||||
for _, test := range tests {
|
||||
t.Run(test.name, func(t *testing.T) {
|
||||
got, err := buildCreateActorRequest("my-counter", "demo", test.template, test.templateRef, test.snapshotTag)
|
||||
got, err := buildCreateActorRequest("my-counter", "demo", test.templateRef, test.snapshotTag)
|
||||
if (err != nil) != test.wantErr {
|
||||
t.Fatalf("buildCreateActorRequest error = %v, wantErr %t", err, test.wantErr)
|
||||
}
|
||||
|
||||
@@ -33,8 +33,8 @@ func TestGetWorkersRunner_Filters(t *testing.T) {
|
||||
Labels: map[string]string{"ate.dev/worker-pool": "counter"},
|
||||
Status: &ateapipb.WorkerStatus{
|
||||
Assignment: &ateapipb.ActorAssignment{
|
||||
ActorTemplate: &ateapipb.KubeNamespacedObjectRef{Namespace: "ns-1", Name: "counter"},
|
||||
Actor: &ateapipb.ObjectRef{Atespace: "space-a", Name: "actor-a"},
|
||||
ActorTemplateRef: &ateapipb.ObjectRef{Atespace: "ns-1", Name: "counter"},
|
||||
Actor: &ateapipb.ObjectRef{Atespace: "space-a", Name: "actor-a"},
|
||||
},
|
||||
},
|
||||
},
|
||||
@@ -53,8 +53,8 @@ func TestGetWorkersRunner_Filters(t *testing.T) {
|
||||
Labels: map[string]string{"ate.dev/worker-pool": "counter"},
|
||||
Status: &ateapipb.WorkerStatus{
|
||||
Assignment: &ateapipb.ActorAssignment{
|
||||
ActorTemplate: &ateapipb.KubeNamespacedObjectRef{Namespace: "ns-2", Name: "counter"},
|
||||
Actor: &ateapipb.ObjectRef{Atespace: "space-b", Name: "actor-b"},
|
||||
ActorTemplateRef: &ateapipb.ObjectRef{Atespace: "ns-2", Name: "counter"},
|
||||
Actor: &ateapipb.ObjectRef{Atespace: "space-b", Name: "actor-b"},
|
||||
},
|
||||
},
|
||||
},
|
||||
|
||||
@@ -125,13 +125,6 @@ func (r *TopWorkersRunner) Run(ctx context.Context) error {
|
||||
wass.GetActor().GetAtespace(),
|
||||
wass.GetActor().GetName(),
|
||||
)
|
||||
} else if tpl := wass.GetActorTemplate(); tpl != nil && tpl.GetNamespace() != "" {
|
||||
assignedActor = fmt.Sprintf("%s/%s/%s/%s",
|
||||
tpl.GetNamespace(),
|
||||
tpl.GetName(),
|
||||
wass.GetActor().GetAtespace(),
|
||||
wass.GetActor().GetName(),
|
||||
)
|
||||
} else {
|
||||
assignedActor = fmt.Sprintf("%s/%s",
|
||||
wass.GetActor().GetAtespace(),
|
||||
|
||||
@@ -60,13 +60,10 @@ func sortActors(actors []*ateapipb.Actor) {
|
||||
}
|
||||
|
||||
// actorTemplateDisplay renders the template an actor was created from, in
|
||||
// "<atespace>/<name>" form for substrate-resource references and
|
||||
// "<namespace>/<name>" form for legacy CRD references.
|
||||
// "<atespace>/<name>" form.
|
||||
func actorTemplateDisplay(a *ateapipb.Actor) string {
|
||||
if ref := a.GetActorTemplate(); ref != nil {
|
||||
return ref.GetAtespace() + "/" + ref.GetName()
|
||||
}
|
||||
return a.GetActorTemplateNamespace() + "/" + a.GetActorTemplateName()
|
||||
ref := a.GetActorTemplate()
|
||||
return ref.GetAtespace() + "/" + ref.GetName()
|
||||
}
|
||||
|
||||
// PrintActorsTo prints a slice of actors to the provided writer.
|
||||
@@ -136,14 +133,9 @@ func PrintWorkersTo(out io.Writer, workers []*ateapipb.Worker, format string) er
|
||||
assignedActor := "<none>"
|
||||
if wass := worker.GetStatus().GetAssignment(); wass != nil {
|
||||
status = "ASSIGNED"
|
||||
// The assignment names the template either as a substrate
|
||||
// resource ref or as a legacy CRD ref; exactly one is set.
|
||||
template := wass.GetActorTemplate().GetNamespace() + "/" + wass.GetActorTemplate().GetName()
|
||||
if ref := wass.GetActorTemplateRef(); ref != nil {
|
||||
template = ref.GetAtespace() + "/" + ref.GetName()
|
||||
}
|
||||
assignedActor = fmt.Sprintf("%s/%s/%s",
|
||||
template, wass.GetActor().GetAtespace(), wass.GetActor().GetName())
|
||||
ref := wass.GetActorTemplateRef()
|
||||
assignedActor = fmt.Sprintf("%s/%s/%s/%s",
|
||||
ref.GetAtespace(), ref.GetName(), wass.GetActor().GetAtespace(), wass.GetActor().GetName())
|
||||
}
|
||||
|
||||
fmt.Fprintf(w, "%s\t%s\t%s\t%s\t%s\t%s\n", ns, pool, class, pod, status, assignedActor)
|
||||
|
||||
@@ -66,8 +66,7 @@ func TestPrintActorsTo_Table(t *testing.T) {
|
||||
Version: 2,
|
||||
CreateTime: timestamppb.New(now.Add(-5 * time.Minute)),
|
||||
},
|
||||
ActorTemplateNamespace: "default",
|
||||
ActorTemplateName: "template-1",
|
||||
ActorTemplate: &ateapipb.ObjectRef{Atespace: "default", Name: "template-1"},
|
||||
Status: &ateapipb.ActorStatus{
|
||||
State: ateapipb.ActorState_ACTOR_STATE_RUNNING,
|
||||
WorkerAssignment: &ateapipb.WorkerAssignment{
|
||||
@@ -156,9 +155,8 @@ func TestPrintActorsTo_Table_Sorted(t *testing.T) {
|
||||
Atespace: "team-b",
|
||||
CreateTime: timestamppb.New(now.Add(-72 * time.Hour)),
|
||||
},
|
||||
ActorTemplateNamespace: "default",
|
||||
ActorTemplateName: "template-1",
|
||||
Status: &ateapipb.ActorStatus{State: ateapipb.ActorState_ACTOR_STATE_SUSPENDED},
|
||||
ActorTemplate: &ateapipb.ObjectRef{Atespace: "default", Name: "template-1"},
|
||||
Status: &ateapipb.ActorStatus{State: ateapipb.ActorState_ACTOR_STATE_SUSPENDED},
|
||||
},
|
||||
{
|
||||
Metadata: &ateapipb.ResourceMetadata{
|
||||
@@ -166,9 +164,8 @@ func TestPrintActorsTo_Table_Sorted(t *testing.T) {
|
||||
Atespace: "team-a",
|
||||
CreateTime: timestamppb.New(now.Add(-5 * time.Minute)),
|
||||
},
|
||||
ActorTemplateNamespace: "default",
|
||||
ActorTemplateName: "template-1",
|
||||
Status: &ateapipb.ActorStatus{State: ateapipb.ActorState_ACTOR_STATE_RUNNING},
|
||||
ActorTemplate: &ateapipb.ObjectRef{Atespace: "default", Name: "template-1"},
|
||||
Status: &ateapipb.ActorStatus{State: ateapipb.ActorState_ACTOR_STATE_RUNNING},
|
||||
},
|
||||
{
|
||||
Metadata: &ateapipb.ResourceMetadata{
|
||||
@@ -176,9 +173,8 @@ func TestPrintActorsTo_Table_Sorted(t *testing.T) {
|
||||
Atespace: "team-a",
|
||||
CreateTime: timestamppb.New(now.Add(-5 * time.Hour)),
|
||||
},
|
||||
ActorTemplateNamespace: "other",
|
||||
ActorTemplateName: "template-2",
|
||||
Status: &ateapipb.ActorStatus{State: ateapipb.ActorState_ACTOR_STATE_SUSPENDED},
|
||||
ActorTemplate: &ateapipb.ObjectRef{Atespace: "other", Name: "template-2"},
|
||||
Status: &ateapipb.ActorStatus{State: ateapipb.ActorState_ACTOR_STATE_SUSPENDED},
|
||||
},
|
||||
}
|
||||
|
||||
@@ -247,9 +243,9 @@ func TestPrintWorkersTo_Table(t *testing.T) {
|
||||
SandboxClass: "gvisor",
|
||||
Status: &ateapipb.WorkerStatus{
|
||||
Assignment: &ateapipb.ActorAssignment{
|
||||
ActorTemplate: &ateapipb.KubeNamespacedObjectRef{
|
||||
Namespace: "default",
|
||||
Name: "template-1",
|
||||
ActorTemplateRef: &ateapipb.ObjectRef{
|
||||
Atespace: "default",
|
||||
Name: "template-1",
|
||||
},
|
||||
Actor: &ateapipb.ObjectRef{
|
||||
Atespace: "space-1",
|
||||
@@ -273,44 +269,6 @@ default pool-1 gvisor pod-1 ASSIGNED default/template-1/space-1/id-1
|
||||
}
|
||||
}
|
||||
|
||||
// A worker assigned to an actor created from a substrate ActorTemplate
|
||||
// carries only ActorTemplateRef; the printer must not dereference the legacy
|
||||
// CRD ref (regression test for a nil-pointer panic).
|
||||
func TestPrintWorkersTo_Table_TemplateRef(t *testing.T) {
|
||||
var buf bytes.Buffer
|
||||
workers := []*ateapipb.Worker{
|
||||
{
|
||||
WorkerNamespace: "default",
|
||||
WorkerPool: "pool-1",
|
||||
WorkerPod: "pod-1",
|
||||
SandboxClass: "gvisor",
|
||||
Status: &ateapipb.WorkerStatus{
|
||||
Assignment: &ateapipb.ActorAssignment{
|
||||
ActorTemplateRef: &ateapipb.ObjectRef{
|
||||
Atespace: "ate-demo-counter-substrate",
|
||||
Name: "counter",
|
||||
},
|
||||
Actor: &ateapipb.ObjectRef{
|
||||
Atespace: "space-1",
|
||||
Name: "id-1",
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
if err := PrintWorkersTo(&buf, workers, "table"); err != nil {
|
||||
t.Fatalf("unexpected error: %v", err)
|
||||
}
|
||||
|
||||
expected := `NAMESPACE POOL CLASS POD STATUS ASSIGNED ACTOR
|
||||
default pool-1 gvisor pod-1 ASSIGNED ate-demo-counter-substrate/counter/space-1/id-1
|
||||
`
|
||||
if diff := cmp.Diff(expected, buf.String()); diff != "" {
|
||||
t.Errorf("output mismatch (-want +got):\n%s", diff)
|
||||
}
|
||||
}
|
||||
|
||||
func TestPrintWorkersTo_Table_Free(t *testing.T) {
|
||||
var buf bytes.Buffer
|
||||
workers := []*ateapipb.Worker{
|
||||
|
||||
@@ -375,8 +375,8 @@ func handlePods(w http.ResponseWriter, r *http.Request) {
|
||||
// Filter to the demo namespace when set — workers may live
|
||||
// in their own pool namespace (worker_namespace) so we
|
||||
// compare against actor_namespace too.
|
||||
if tpl := wk.GetStatus().GetAssignment().GetActorTemplate(); tpl != nil {
|
||||
if ns, wkns := namespace, tpl.GetNamespace(); ns != "" && wkns != "" && wkns != ns {
|
||||
if ref := wk.GetStatus().GetAssignment().GetActorTemplateRef(); ref != nil {
|
||||
if ns, wkns := namespace, ref.GetAtespace(); ns != "" && wkns != "" && wkns != ns {
|
||||
continue
|
||||
}
|
||||
}
|
||||
@@ -413,7 +413,7 @@ func handleActors(w http.ResponseWriter, r *http.Request) {
|
||||
}
|
||||
actors := make([]actorSummary, 0, len(resp.GetActors()))
|
||||
for _, a := range resp.GetActors() {
|
||||
if namespace != "" && a.GetActorTemplateNamespace() != "" && a.GetActorTemplateNamespace() != namespace {
|
||||
if namespace != "" && a.GetActorTemplate().GetAtespace() != "" && a.GetActorTemplate().GetAtespace() != namespace {
|
||||
continue
|
||||
}
|
||||
// Carry the template name as the meta message so the UI's
|
||||
@@ -422,7 +422,7 @@ func handleActors(w http.ResponseWriter, r *http.Request) {
|
||||
// substrate Actors there's no equivalent, so the template
|
||||
// name is the closest semantic match).
|
||||
msg := ""
|
||||
if t := a.GetActorTemplateName(); t != "" {
|
||||
if t := a.GetActorTemplate().GetName(); t != "" {
|
||||
msg = "template: " + t
|
||||
}
|
||||
actors = append(actors, actorSummary{
|
||||
|
||||
+2
-2
@@ -473,13 +473,13 @@ The Substrate Control Plane (`ate-api-server`) exposes a gRPC interface for mana
|
||||
#### `CreateActor`
|
||||
Registers a new logical actor in the system.
|
||||
* **Request:** `CreateActorRequest`
|
||||
* `actor`: `Actor` — the actor to create. Its `metadata` carries the atespace and name (name must be a DNS-1123 label); `actor_template_namespace` and `actor_template_name` select the `ActorTemplate`.
|
||||
* `actor`: `Actor` — the actor to create. Its `metadata` carries the atespace and name (name must be a DNS-1123 label); the `actor_template` ref (atespace + name) selects the `ActorTemplate`.
|
||||
* **Response:** the initialized `Actor`.
|
||||
|
||||
#### `UpdateActor`
|
||||
Replaces the mutable fields of an existing actor with the ones in the request.
|
||||
* **Request:** `UpdateActorRequest`
|
||||
* `actor`: `Actor` — the complete replacement actor. `metadata.atespace` and `metadata.name` identify the resource; `metadata.uid` and `metadata.version` are **required** preconditions. `metadata` and `status` are server-owned and whatever the request carries in them is ignored. `actor_template_namespace`, `actor_template_name`, `actor_template` and `source_snapshot_tag` are immutable.
|
||||
* `actor`: `Actor` — the complete replacement actor. `metadata.atespace` and `metadata.name` identify the resource; `metadata.uid` and `metadata.version` are **required** preconditions. `metadata` and `status` are server-owned and whatever the request carries in them is ignored. `source_snapshot_tag` is immutable.
|
||||
* **Response:** the updated `Actor`.
|
||||
* **Errors:** `INVALID_ARGUMENT` if `uid` or `version` is unset, or if the request changes an immutable field — including by leaving one unset; `ABORTED` if either guard no longer matches the stored resource.
|
||||
|
||||
|
||||
@@ -54,9 +54,9 @@ func TestFindUnknownFields(t *testing.T) {
|
||||
in: func() proto.Message {
|
||||
return &ateapipb.CreateActorRequest{
|
||||
Actor: &ateapipb.Actor{
|
||||
Metadata: &ateapipb.ResourceMetadata{Atespace: "team-a", Name: "actor-1"},
|
||||
ActorTemplateName: "tmpl1",
|
||||
WorkerSelector: &ateapipb.Selector{MatchLabels: map[string]string{"tier": "paid"}},
|
||||
Metadata: &ateapipb.ResourceMetadata{Atespace: "team-a", Name: "actor-1"},
|
||||
ActorTemplate: &ateapipb.ObjectRef{Atespace: "team-a", Name: "tmpl1"},
|
||||
WorkerSelector: &ateapipb.Selector{MatchLabels: map[string]string{"tier": "paid"}},
|
||||
},
|
||||
}
|
||||
},
|
||||
@@ -106,7 +106,7 @@ func TestFindUnknownFields(t *testing.T) {
|
||||
{
|
||||
name: "several unknown fields on one message collapse to a single error",
|
||||
in: func() proto.Message {
|
||||
m := &ateapipb.Actor{ActorTemplateName: "tmpl1"}
|
||||
m := &ateapipb.Actor{ActorTemplate: &ateapipb.ObjectRef{Name: "tmpl1"}}
|
||||
m.ProtoReflect().SetUnknown(append(unknownField(9998), unknownField(9999)...))
|
||||
return m
|
||||
},
|
||||
@@ -117,8 +117,8 @@ func TestFindUnknownFields(t *testing.T) {
|
||||
in: func() proto.Message {
|
||||
return &ateapipb.ListActorsResponse{
|
||||
Actors: []*ateapipb.Actor{
|
||||
{ActorTemplateName: "tmpl1"},
|
||||
withUnknown(&ateapipb.Actor{ActorTemplateName: "tmpl2"}, 9999),
|
||||
{ActorTemplate: &ateapipb.ObjectRef{Name: "tmpl1"}},
|
||||
withUnknown(&ateapipb.Actor{ActorTemplate: &ateapipb.ObjectRef{Name: "tmpl2"}}, 9999),
|
||||
},
|
||||
}
|
||||
},
|
||||
@@ -159,7 +159,7 @@ func TestRejectUnknownFieldsUnaryInterceptor(t *testing.T) {
|
||||
|
||||
t.Run("clean request reaches the handler", func(t *testing.T) {
|
||||
resp, err := RejectUnknownFieldsUnaryInterceptor(context.Background(),
|
||||
&ateapipb.CreateActorRequest{Actor: &ateapipb.Actor{ActorTemplateName: "tmpl1"}}, info, handler)
|
||||
&ateapipb.CreateActorRequest{Actor: &ateapipb.Actor{ActorTemplate: &ateapipb.ObjectRef{Name: "tmpl1"}}}, info, handler)
|
||||
if err != nil {
|
||||
t.Fatalf("interceptor error = %v, want nil", err)
|
||||
}
|
||||
|
||||
@@ -191,9 +191,8 @@ func (u *durDirUser) create(ctx context.Context) error {
|
||||
return u.tracedCall(ctx, "CreateActor", func(callCtx context.Context, tr *metadata.MD) error {
|
||||
_, err := u.cfg.APIStub.CreateActor(callCtx, &ateapipb.CreateActorRequest{
|
||||
Actor: &ateapipb.Actor{
|
||||
Metadata: &ateapipb.ResourceMetadata{Atespace: u.cfg.Atespace, Name: u.actorName},
|
||||
ActorTemplateNamespace: templateNS,
|
||||
ActorTemplateName: u.templateName,
|
||||
Metadata: &ateapipb.ResourceMetadata{Atespace: u.cfg.Atespace, Name: u.actorName},
|
||||
ActorTemplate: &ateapipb.ObjectRef{Atespace: templateNS, Name: u.templateName},
|
||||
},
|
||||
}, grpc.Trailer(tr))
|
||||
return err
|
||||
|
||||
@@ -204,9 +204,8 @@ func (u *gluttonUser) create(ctx context.Context) error {
|
||||
return u.tracedCall(ctx, "CreateActor", func(callCtx context.Context, tr *metadata.MD) error {
|
||||
_, err := u.cfg.APIStub.CreateActor(callCtx, &ateapipb.CreateActorRequest{
|
||||
Actor: &ateapipb.Actor{
|
||||
Metadata: &ateapipb.ResourceMetadata{Atespace: u.cfg.Atespace, Name: u.actorName},
|
||||
ActorTemplateNamespace: templateNS,
|
||||
ActorTemplateName: templateName,
|
||||
Metadata: &ateapipb.ResourceMetadata{Atespace: u.cfg.Atespace, Name: u.actorName},
|
||||
ActorTemplate: &ateapipb.ObjectRef{Atespace: templateNS, Name: templateName},
|
||||
},
|
||||
}, grpc.Trailer(tr))
|
||||
return err
|
||||
|
||||
+133
-248
@@ -859,23 +859,10 @@ type Actor struct {
|
||||
// +k8s:required
|
||||
// +k8s:subfield(atespace)=+k8s:required
|
||||
Metadata *ResourceMetadata `protobuf:"bytes,1,opt,name=metadata,proto3" json:"metadata,omitempty"`
|
||||
// The template is named by the legacy CRD namespace/name pair below or by
|
||||
// the substrate actor_template ref; actor_template takes precedence when
|
||||
// set.
|
||||
//
|
||||
// TODO: delete both fields once we start using actor_template below.
|
||||
// +k8s:optional
|
||||
// +k8s:format=k8s-short-name
|
||||
// +k8s:immutable
|
||||
ActorTemplateNamespace string `protobuf:"bytes,2,opt,name=actor_template_namespace,json=actorTemplateNamespace,proto3" json:"actor_template_namespace,omitempty"`
|
||||
// +k8s:optional
|
||||
// +k8s:format=k8s-long-name
|
||||
// +k8s:immutable
|
||||
ActorTemplateName string `protobuf:"bytes,3,opt,name=actor_template_name,json=actorTemplateName,proto3" json:"actor_template_name,omitempty"`
|
||||
// TODO: replace with full actor_template spec if we decide to make each Actor self-contained.
|
||||
// +k8s:optional # TODO: this should be required?
|
||||
// +k8s:required
|
||||
// +k8s:subfield(atespace)=+k8s:required
|
||||
// +k8s:immutable # TODO: is this mutable?
|
||||
// +k8s:mutable
|
||||
ActorTemplate *ObjectRef `protobuf:"bytes,4,opt,name=actor_template,json=actorTemplate,proto3" json:"actor_template,omitempty"`
|
||||
// worker_selector is the per-actor placement constraint. The scheduler
|
||||
// evaluates the AND of this selector and the template's workerSelector to
|
||||
@@ -938,20 +925,6 @@ func (x *Actor) GetMetadata() *ResourceMetadata {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (x *Actor) GetActorTemplateNamespace() string {
|
||||
if x != nil {
|
||||
return x.ActorTemplateNamespace
|
||||
}
|
||||
return ""
|
||||
}
|
||||
|
||||
func (x *Actor) GetActorTemplateName() string {
|
||||
if x != nil {
|
||||
return x.ActorTemplateName
|
||||
}
|
||||
return ""
|
||||
}
|
||||
|
||||
func (x *Actor) GetActorTemplate() *ObjectRef {
|
||||
if x != nil {
|
||||
return x.ActorTemplate
|
||||
@@ -1755,14 +1728,12 @@ func (x *ActorSnapshot) GetStatus() *ActorSnapshotStatus {
|
||||
type ActorSnapshotStatus struct {
|
||||
state protoimpl.MessageState `protogen:"open.v1"`
|
||||
// +k8s:opaqueType
|
||||
SourceActor *ObjectRef `protobuf:"bytes,1,opt,name=source_actor,json=sourceActor,proto3" json:"source_actor,omitempty"`
|
||||
SourceActorUid string `protobuf:"bytes,2,opt,name=source_actor_uid,json=sourceActorUid,proto3" json:"source_actor_uid,omitempty"`
|
||||
SourceActorVersion int64 `protobuf:"varint,3,opt,name=source_actor_version,json=sourceActorVersion,proto3" json:"source_actor_version,omitempty"`
|
||||
ActorTemplateNamespace string `protobuf:"bytes,4,opt,name=actor_template_namespace,json=actorTemplateNamespace,proto3" json:"actor_template_namespace,omitempty"`
|
||||
ActorTemplateName string `protobuf:"bytes,5,opt,name=actor_template_name,json=actorTemplateName,proto3" json:"actor_template_name,omitempty"`
|
||||
ActorTemplateUid string `protobuf:"bytes,6,opt,name=actor_template_uid,json=actorTemplateUid,proto3" json:"actor_template_uid,omitempty"`
|
||||
ContentScope SnapshotContentScope `protobuf:"varint,7,opt,name=content_scope,json=contentScope,proto3,enum=ateapi.SnapshotContentScope" json:"content_scope,omitempty"`
|
||||
SnapshotUri string `protobuf:"bytes,8,opt,name=snapshot_uri,json=snapshotUri,proto3" json:"snapshot_uri,omitempty"`
|
||||
SourceActor *ObjectRef `protobuf:"bytes,1,opt,name=source_actor,json=sourceActor,proto3" json:"source_actor,omitempty"`
|
||||
SourceActorUid string `protobuf:"bytes,2,opt,name=source_actor_uid,json=sourceActorUid,proto3" json:"source_actor_uid,omitempty"`
|
||||
SourceActorVersion int64 `protobuf:"varint,3,opt,name=source_actor_version,json=sourceActorVersion,proto3" json:"source_actor_version,omitempty"`
|
||||
ActorTemplateUid string `protobuf:"bytes,6,opt,name=actor_template_uid,json=actorTemplateUid,proto3" json:"actor_template_uid,omitempty"`
|
||||
ContentScope SnapshotContentScope `protobuf:"varint,7,opt,name=content_scope,json=contentScope,proto3,enum=ateapi.SnapshotContentScope" json:"content_scope,omitempty"`
|
||||
SnapshotUri string `protobuf:"bytes,8,opt,name=snapshot_uri,json=snapshotUri,proto3" json:"snapshot_uri,omitempty"`
|
||||
// Immutable reference to the actor_template where the snapshot was created from.
|
||||
// +k8s:opaqueType
|
||||
ActorTemplate *ObjectRef `protobuf:"bytes,9,opt,name=actor_template,json=actorTemplate,proto3" json:"actor_template,omitempty"`
|
||||
@@ -1821,20 +1792,6 @@ func (x *ActorSnapshotStatus) GetSourceActorVersion() int64 {
|
||||
return 0
|
||||
}
|
||||
|
||||
func (x *ActorSnapshotStatus) GetActorTemplateNamespace() string {
|
||||
if x != nil {
|
||||
return x.ActorTemplateNamespace
|
||||
}
|
||||
return ""
|
||||
}
|
||||
|
||||
func (x *ActorSnapshotStatus) GetActorTemplateName() string {
|
||||
if x != nil {
|
||||
return x.ActorTemplateName
|
||||
}
|
||||
return ""
|
||||
}
|
||||
|
||||
func (x *ActorSnapshotStatus) GetActorTemplateUid() string {
|
||||
if x != nil {
|
||||
return x.ActorTemplateUid
|
||||
@@ -5878,14 +5835,11 @@ func (x *WorkerCapacity) GetMemoryBytes() int64 {
|
||||
// WorkerAssignment.
|
||||
type ActorAssignment struct {
|
||||
state protoimpl.MessageState `protogen:"open.v1"`
|
||||
// The KubeNamespacedObjectRef actor_template names the CRD-backed template
|
||||
// of an actor created with the legacy CRD path
|
||||
// actor_template_ref names the substrate ActorTemplate resource.
|
||||
// Exactly one is set.
|
||||
ActorTemplate *KubeNamespacedObjectRef `protobuf:"bytes,1,opt,name=actor_template,json=actorTemplate,proto3" json:"actor_template,omitempty"`
|
||||
// +k8s:opaqueType
|
||||
Actor *ObjectRef `protobuf:"bytes,2,opt,name=actor,proto3" json:"actor,omitempty"`
|
||||
ActorUid string `protobuf:"bytes,3,opt,name=actor_uid,json=actorUid,proto3" json:"actor_uid,omitempty"`
|
||||
// actor_template_ref names the substrate ActorTemplate resource the
|
||||
// assigned actor was created from.
|
||||
// +k8s:opaqueType
|
||||
ActorTemplateRef *ObjectRef `protobuf:"bytes,4,opt,name=actor_template_ref,json=actorTemplateRef,proto3" json:"actor_template_ref,omitempty"`
|
||||
unknownFields protoimpl.UnknownFields
|
||||
@@ -5922,13 +5876,6 @@ func (*ActorAssignment) Descriptor() ([]byte, []int) {
|
||||
return file_ateapi_proto_rawDescGZIP(), []int{87}
|
||||
}
|
||||
|
||||
func (x *ActorAssignment) GetActorTemplate() *KubeNamespacedObjectRef {
|
||||
if x != nil {
|
||||
return x.ActorTemplate
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (x *ActorAssignment) GetActor() *ObjectRef {
|
||||
if x != nil {
|
||||
return x.Actor
|
||||
@@ -5950,58 +5897,6 @@ func (x *ActorAssignment) GetActorTemplateRef() *ObjectRef {
|
||||
return nil
|
||||
}
|
||||
|
||||
type KubeNamespacedObjectRef struct {
|
||||
state protoimpl.MessageState `protogen:"open.v1"`
|
||||
Namespace string `protobuf:"bytes,1,opt,name=namespace,proto3" json:"namespace,omitempty"`
|
||||
Name string `protobuf:"bytes,2,opt,name=name,proto3" json:"name,omitempty"`
|
||||
unknownFields protoimpl.UnknownFields
|
||||
sizeCache protoimpl.SizeCache
|
||||
}
|
||||
|
||||
func (x *KubeNamespacedObjectRef) Reset() {
|
||||
*x = KubeNamespacedObjectRef{}
|
||||
mi := &file_ateapi_proto_msgTypes[88]
|
||||
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||
ms.StoreMessageInfo(mi)
|
||||
}
|
||||
|
||||
func (x *KubeNamespacedObjectRef) String() string {
|
||||
return protoimpl.X.MessageStringOf(x)
|
||||
}
|
||||
|
||||
func (*KubeNamespacedObjectRef) ProtoMessage() {}
|
||||
|
||||
func (x *KubeNamespacedObjectRef) ProtoReflect() protoreflect.Message {
|
||||
mi := &file_ateapi_proto_msgTypes[88]
|
||||
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 KubeNamespacedObjectRef.ProtoReflect.Descriptor instead.
|
||||
func (*KubeNamespacedObjectRef) Descriptor() ([]byte, []int) {
|
||||
return file_ateapi_proto_rawDescGZIP(), []int{88}
|
||||
}
|
||||
|
||||
func (x *KubeNamespacedObjectRef) GetNamespace() string {
|
||||
if x != nil {
|
||||
return x.Namespace
|
||||
}
|
||||
return ""
|
||||
}
|
||||
|
||||
func (x *KubeNamespacedObjectRef) GetName() string {
|
||||
if x != nil {
|
||||
return x.Name
|
||||
}
|
||||
return ""
|
||||
}
|
||||
|
||||
type DebugClearRequest struct {
|
||||
state protoimpl.MessageState `protogen:"open.v1"`
|
||||
unknownFields protoimpl.UnknownFields
|
||||
@@ -6010,7 +5905,7 @@ type DebugClearRequest struct {
|
||||
|
||||
func (x *DebugClearRequest) Reset() {
|
||||
*x = DebugClearRequest{}
|
||||
mi := &file_ateapi_proto_msgTypes[89]
|
||||
mi := &file_ateapi_proto_msgTypes[88]
|
||||
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||
ms.StoreMessageInfo(mi)
|
||||
}
|
||||
@@ -6022,7 +5917,7 @@ func (x *DebugClearRequest) String() string {
|
||||
func (*DebugClearRequest) ProtoMessage() {}
|
||||
|
||||
func (x *DebugClearRequest) ProtoReflect() protoreflect.Message {
|
||||
mi := &file_ateapi_proto_msgTypes[89]
|
||||
mi := &file_ateapi_proto_msgTypes[88]
|
||||
if x != nil {
|
||||
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||
if ms.LoadMessageInfo() == nil {
|
||||
@@ -6035,7 +5930,7 @@ func (x *DebugClearRequest) ProtoReflect() protoreflect.Message {
|
||||
|
||||
// Deprecated: Use DebugClearRequest.ProtoReflect.Descriptor instead.
|
||||
func (*DebugClearRequest) Descriptor() ([]byte, []int) {
|
||||
return file_ateapi_proto_rawDescGZIP(), []int{89}
|
||||
return file_ateapi_proto_rawDescGZIP(), []int{88}
|
||||
}
|
||||
|
||||
type DebugClearResponse struct {
|
||||
@@ -6046,7 +5941,7 @@ type DebugClearResponse struct {
|
||||
|
||||
func (x *DebugClearResponse) Reset() {
|
||||
*x = DebugClearResponse{}
|
||||
mi := &file_ateapi_proto_msgTypes[90]
|
||||
mi := &file_ateapi_proto_msgTypes[89]
|
||||
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||
ms.StoreMessageInfo(mi)
|
||||
}
|
||||
@@ -6058,7 +5953,7 @@ func (x *DebugClearResponse) String() string {
|
||||
func (*DebugClearResponse) ProtoMessage() {}
|
||||
|
||||
func (x *DebugClearResponse) ProtoReflect() protoreflect.Message {
|
||||
mi := &file_ateapi_proto_msgTypes[90]
|
||||
mi := &file_ateapi_proto_msgTypes[89]
|
||||
if x != nil {
|
||||
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||
if ms.LoadMessageInfo() == nil {
|
||||
@@ -6071,7 +5966,7 @@ func (x *DebugClearResponse) ProtoReflect() protoreflect.Message {
|
||||
|
||||
// Deprecated: Use DebugClearResponse.ProtoReflect.Descriptor instead.
|
||||
func (*DebugClearResponse) Descriptor() ([]byte, []int) {
|
||||
return file_ateapi_proto_rawDescGZIP(), []int{90}
|
||||
return file_ateapi_proto_rawDescGZIP(), []int{89}
|
||||
}
|
||||
|
||||
type MintJWTRequest struct {
|
||||
@@ -6086,7 +5981,7 @@ type MintJWTRequest struct {
|
||||
|
||||
func (x *MintJWTRequest) Reset() {
|
||||
*x = MintJWTRequest{}
|
||||
mi := &file_ateapi_proto_msgTypes[91]
|
||||
mi := &file_ateapi_proto_msgTypes[90]
|
||||
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||
ms.StoreMessageInfo(mi)
|
||||
}
|
||||
@@ -6098,7 +5993,7 @@ func (x *MintJWTRequest) String() string {
|
||||
func (*MintJWTRequest) ProtoMessage() {}
|
||||
|
||||
func (x *MintJWTRequest) ProtoReflect() protoreflect.Message {
|
||||
mi := &file_ateapi_proto_msgTypes[91]
|
||||
mi := &file_ateapi_proto_msgTypes[90]
|
||||
if x != nil {
|
||||
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||
if ms.LoadMessageInfo() == nil {
|
||||
@@ -6111,7 +6006,7 @@ func (x *MintJWTRequest) ProtoReflect() protoreflect.Message {
|
||||
|
||||
// Deprecated: Use MintJWTRequest.ProtoReflect.Descriptor instead.
|
||||
func (*MintJWTRequest) Descriptor() ([]byte, []int) {
|
||||
return file_ateapi_proto_rawDescGZIP(), []int{91}
|
||||
return file_ateapi_proto_rawDescGZIP(), []int{90}
|
||||
}
|
||||
|
||||
func (x *MintJWTRequest) GetAudience() []string {
|
||||
@@ -6170,7 +6065,7 @@ type MintJWTResponse struct {
|
||||
|
||||
func (x *MintJWTResponse) Reset() {
|
||||
*x = MintJWTResponse{}
|
||||
mi := &file_ateapi_proto_msgTypes[92]
|
||||
mi := &file_ateapi_proto_msgTypes[91]
|
||||
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||
ms.StoreMessageInfo(mi)
|
||||
}
|
||||
@@ -6182,7 +6077,7 @@ func (x *MintJWTResponse) String() string {
|
||||
func (*MintJWTResponse) ProtoMessage() {}
|
||||
|
||||
func (x *MintJWTResponse) ProtoReflect() protoreflect.Message {
|
||||
mi := &file_ateapi_proto_msgTypes[92]
|
||||
mi := &file_ateapi_proto_msgTypes[91]
|
||||
if x != nil {
|
||||
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||
if ms.LoadMessageInfo() == nil {
|
||||
@@ -6195,7 +6090,7 @@ func (x *MintJWTResponse) ProtoReflect() protoreflect.Message {
|
||||
|
||||
// Deprecated: Use MintJWTResponse.ProtoReflect.Descriptor instead.
|
||||
func (*MintJWTResponse) Descriptor() ([]byte, []int) {
|
||||
return file_ateapi_proto_rawDescGZIP(), []int{92}
|
||||
return file_ateapi_proto_rawDescGZIP(), []int{91}
|
||||
}
|
||||
|
||||
func (x *MintJWTResponse) GetActorJwt() string {
|
||||
@@ -6231,7 +6126,7 @@ type MintCertRequest struct {
|
||||
|
||||
func (x *MintCertRequest) Reset() {
|
||||
*x = MintCertRequest{}
|
||||
mi := &file_ateapi_proto_msgTypes[93]
|
||||
mi := &file_ateapi_proto_msgTypes[92]
|
||||
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||
ms.StoreMessageInfo(mi)
|
||||
}
|
||||
@@ -6243,7 +6138,7 @@ func (x *MintCertRequest) String() string {
|
||||
func (*MintCertRequest) ProtoMessage() {}
|
||||
|
||||
func (x *MintCertRequest) ProtoReflect() protoreflect.Message {
|
||||
mi := &file_ateapi_proto_msgTypes[93]
|
||||
mi := &file_ateapi_proto_msgTypes[92]
|
||||
if x != nil {
|
||||
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||
if ms.LoadMessageInfo() == nil {
|
||||
@@ -6256,7 +6151,7 @@ func (x *MintCertRequest) ProtoReflect() protoreflect.Message {
|
||||
|
||||
// Deprecated: Use MintCertRequest.ProtoReflect.Descriptor instead.
|
||||
func (*MintCertRequest) Descriptor() ([]byte, []int) {
|
||||
return file_ateapi_proto_rawDescGZIP(), []int{93}
|
||||
return file_ateapi_proto_rawDescGZIP(), []int{92}
|
||||
}
|
||||
|
||||
func (x *MintCertRequest) GetWorker() *ObjectRef {
|
||||
@@ -6299,7 +6194,7 @@ type MintCertResponse struct {
|
||||
|
||||
func (x *MintCertResponse) Reset() {
|
||||
*x = MintCertResponse{}
|
||||
mi := &file_ateapi_proto_msgTypes[94]
|
||||
mi := &file_ateapi_proto_msgTypes[93]
|
||||
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||
ms.StoreMessageInfo(mi)
|
||||
}
|
||||
@@ -6311,7 +6206,7 @@ func (x *MintCertResponse) String() string {
|
||||
func (*MintCertResponse) ProtoMessage() {}
|
||||
|
||||
func (x *MintCertResponse) ProtoReflect() protoreflect.Message {
|
||||
mi := &file_ateapi_proto_msgTypes[94]
|
||||
mi := &file_ateapi_proto_msgTypes[93]
|
||||
if x != nil {
|
||||
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||
if ms.LoadMessageInfo() == nil {
|
||||
@@ -6324,7 +6219,7 @@ func (x *MintCertResponse) ProtoReflect() protoreflect.Message {
|
||||
|
||||
// Deprecated: Use MintCertResponse.ProtoReflect.Descriptor instead.
|
||||
func (*MintCertResponse) Descriptor() ([]byte, []int) {
|
||||
return file_ateapi_proto_rawDescGZIP(), []int{94}
|
||||
return file_ateapi_proto_rawDescGZIP(), []int{93}
|
||||
}
|
||||
|
||||
func (x *MintCertResponse) GetActorCertificates() [][]byte {
|
||||
@@ -6372,11 +6267,9 @@ const file_ateapi_proto_rawDesc = "" +
|
||||
"\x12STATUS_UNSPECIFIED\x10\x00\x12\x12\n" +
|
||||
"\x0eSTATUS_PENDING\x10\x01\x12\x12\n" +
|
||||
"\x0eSTATUS_CREATED\x10\x02\x12\x13\n" +
|
||||
"\x0fSTATUS_DELETING\x10\x03\"\x8c\x03\n" +
|
||||
"\x0fSTATUS_DELETING\x10\x03\"\xa2\x02\n" +
|
||||
"\x05Actor\x124\n" +
|
||||
"\bmetadata\x18\x01 \x01(\v2\x18.ateapi.ResourceMetadataR\bmetadata\x128\n" +
|
||||
"\x18actor_template_namespace\x18\x02 \x01(\tR\x16actorTemplateNamespace\x12.\n" +
|
||||
"\x13actor_template_name\x18\x03 \x01(\tR\x11actorTemplateName\x128\n" +
|
||||
"\x0eactor_template\x18\x04 \x01(\v2\x11.ateapi.ObjectRefR\ractorTemplate\x129\n" +
|
||||
"\x0fworker_selector\x18\x05 \x01(\v2\x10.ateapi.SelectorR\x0eworkerSelector\x12A\n" +
|
||||
"\x13source_snapshot_tag\x18\x06 \x01(\v2\x11.ateapi.ObjectRefR\x11sourceSnapshotTag\x12+\n" +
|
||||
@@ -6424,13 +6317,11 @@ const file_ateapi_proto_rawDesc = "" +
|
||||
"\rworker_pod_ip\x18\x05 \x01(\tR\vworkerPodIp\"z\n" +
|
||||
"\rActorSnapshot\x124\n" +
|
||||
"\bmetadata\x18\x01 \x01(\v2\x18.ateapi.ResourceMetadataR\bmetadata\x123\n" +
|
||||
"\x06status\x18\x02 \x01(\v2\x1b.ateapi.ActorSnapshotStatusR\x06status\"\xdf\x03\n" +
|
||||
"\x06status\x18\x02 \x01(\v2\x1b.ateapi.ActorSnapshotStatusR\x06status\"\xf5\x02\n" +
|
||||
"\x13ActorSnapshotStatus\x124\n" +
|
||||
"\fsource_actor\x18\x01 \x01(\v2\x11.ateapi.ObjectRefR\vsourceActor\x12(\n" +
|
||||
"\x10source_actor_uid\x18\x02 \x01(\tR\x0esourceActorUid\x120\n" +
|
||||
"\x14source_actor_version\x18\x03 \x01(\x03R\x12sourceActorVersion\x128\n" +
|
||||
"\x18actor_template_namespace\x18\x04 \x01(\tR\x16actorTemplateNamespace\x12.\n" +
|
||||
"\x13actor_template_name\x18\x05 \x01(\tR\x11actorTemplateName\x12,\n" +
|
||||
"\x14source_actor_version\x18\x03 \x01(\x03R\x12sourceActorVersion\x12,\n" +
|
||||
"\x12actor_template_uid\x18\x06 \x01(\tR\x10actorTemplateUid\x12A\n" +
|
||||
"\rcontent_scope\x18\a \x01(\x0e2\x1c.ateapi.SnapshotContentScopeR\fcontentScope\x12!\n" +
|
||||
"\fsnapshot_uri\x18\b \x01(\tR\vsnapshotUri\x128\n" +
|
||||
@@ -6665,15 +6556,11 @@ const file_ateapi_proto_rawDesc = "" +
|
||||
"assignment\"P\n" +
|
||||
"\x0eWorkerCapacity\x12\x1b\n" +
|
||||
"\tcpu_milli\x18\x01 \x01(\x03R\bcpuMilli\x12!\n" +
|
||||
"\fmemory_bytes\x18\x02 \x01(\x03R\vmemoryBytes\"\xe0\x01\n" +
|
||||
"\x0fActorAssignment\x12F\n" +
|
||||
"\x0eactor_template\x18\x01 \x01(\v2\x1f.ateapi.KubeNamespacedObjectRefR\ractorTemplate\x12'\n" +
|
||||
"\fmemory_bytes\x18\x02 \x01(\x03R\vmemoryBytes\"\x98\x01\n" +
|
||||
"\x0fActorAssignment\x12'\n" +
|
||||
"\x05actor\x18\x02 \x01(\v2\x11.ateapi.ObjectRefR\x05actor\x12\x1b\n" +
|
||||
"\tactor_uid\x18\x03 \x01(\tR\bactorUid\x12?\n" +
|
||||
"\x12actor_template_ref\x18\x04 \x01(\v2\x11.ateapi.ObjectRefR\x10actorTemplateRef\"K\n" +
|
||||
"\x17KubeNamespacedObjectRef\x12\x1c\n" +
|
||||
"\tnamespace\x18\x01 \x01(\tR\tnamespace\x12\x12\n" +
|
||||
"\x04name\x18\x02 \x01(\tR\x04name\"\x13\n" +
|
||||
"\x12actor_template_ref\x18\x04 \x01(\v2\x11.ateapi.ObjectRefR\x10actorTemplateRef\"\x13\n" +
|
||||
"\x11DebugClearRequest\"\x14\n" +
|
||||
"\x12DebugClearResponse\"\x84\x01\n" +
|
||||
"\x0eMintJWTRequest\x12\x1a\n" +
|
||||
@@ -6785,7 +6672,7 @@ func file_ateapi_proto_rawDescGZIP() []byte {
|
||||
}
|
||||
|
||||
var file_ateapi_proto_enumTypes = make([]protoimpl.EnumInfo, 9)
|
||||
var file_ateapi_proto_msgTypes = make([]protoimpl.MessageInfo, 98)
|
||||
var file_ateapi_proto_msgTypes = make([]protoimpl.MessageInfo, 97)
|
||||
var file_ateapi_proto_goTypes = []any{
|
||||
(SnapshotContentScope)(0), // 0: ateapi.SnapshotContentScope
|
||||
(ActorSnapshotTagScope)(0), // 1: ateapi.ActorSnapshotTagScope
|
||||
@@ -6884,26 +6771,25 @@ var file_ateapi_proto_goTypes = []any{
|
||||
(*WorkerStatus)(nil), // 94: ateapi.WorkerStatus
|
||||
(*WorkerCapacity)(nil), // 95: ateapi.WorkerCapacity
|
||||
(*ActorAssignment)(nil), // 96: ateapi.ActorAssignment
|
||||
(*KubeNamespacedObjectRef)(nil), // 97: ateapi.KubeNamespacedObjectRef
|
||||
(*DebugClearRequest)(nil), // 98: ateapi.DebugClearRequest
|
||||
(*DebugClearResponse)(nil), // 99: ateapi.DebugClearResponse
|
||||
(*MintJWTRequest)(nil), // 100: ateapi.MintJWTRequest
|
||||
(*MintJWTResponse)(nil), // 101: ateapi.MintJWTResponse
|
||||
(*MintCertRequest)(nil), // 102: ateapi.MintCertRequest
|
||||
(*MintCertResponse)(nil), // 103: ateapi.MintCertResponse
|
||||
nil, // 104: ateapi.Selector.MatchLabelsEntry
|
||||
nil, // 105: ateapi.ExternalVolume.VolumeContextEntry
|
||||
nil, // 106: ateapi.Worker.LabelsEntry
|
||||
(*timestamppb.Timestamp)(nil), // 107: google.protobuf.Timestamp
|
||||
(*emptypb.Empty)(nil), // 108: google.protobuf.Empty
|
||||
(*DebugClearRequest)(nil), // 97: ateapi.DebugClearRequest
|
||||
(*DebugClearResponse)(nil), // 98: ateapi.DebugClearResponse
|
||||
(*MintJWTRequest)(nil), // 99: ateapi.MintJWTRequest
|
||||
(*MintJWTResponse)(nil), // 100: ateapi.MintJWTResponse
|
||||
(*MintCertRequest)(nil), // 101: ateapi.MintCertRequest
|
||||
(*MintCertResponse)(nil), // 102: ateapi.MintCertResponse
|
||||
nil, // 103: ateapi.Selector.MatchLabelsEntry
|
||||
nil, // 104: ateapi.ExternalVolume.VolumeContextEntry
|
||||
nil, // 105: ateapi.Worker.LabelsEntry
|
||||
(*timestamppb.Timestamp)(nil), // 106: google.protobuf.Timestamp
|
||||
(*emptypb.Empty)(nil), // 107: google.protobuf.Empty
|
||||
}
|
||||
var file_ateapi_proto_depIdxs = []int32{
|
||||
0, // 0: ateapi.LocalSnapshotInfo.content_scope:type_name -> ateapi.SnapshotContentScope
|
||||
104, // 1: ateapi.Selector.match_labels:type_name -> ateapi.Selector.MatchLabelsEntry
|
||||
107, // 2: ateapi.ResourceMetadata.create_time:type_name -> google.protobuf.Timestamp
|
||||
107, // 3: ateapi.ResourceMetadata.update_time:type_name -> google.protobuf.Timestamp
|
||||
103, // 1: ateapi.Selector.match_labels:type_name -> ateapi.Selector.MatchLabelsEntry
|
||||
106, // 2: ateapi.ResourceMetadata.create_time:type_name -> google.protobuf.Timestamp
|
||||
106, // 3: ateapi.ResourceMetadata.update_time:type_name -> google.protobuf.Timestamp
|
||||
8, // 4: ateapi.ExternalVolume.status:type_name -> ateapi.ExternalVolume.Status
|
||||
105, // 5: ateapi.ExternalVolume.volume_context:type_name -> ateapi.ExternalVolume.VolumeContextEntry
|
||||
104, // 5: ateapi.ExternalVolume.volume_context:type_name -> ateapi.ExternalVolume.VolumeContextEntry
|
||||
11, // 6: ateapi.Actor.metadata:type_name -> ateapi.ResourceMetadata
|
||||
27, // 7: ateapi.Actor.actor_template:type_name -> ateapi.ObjectRef
|
||||
10, // 8: ateapi.Actor.worker_selector:type_name -> ateapi.Selector
|
||||
@@ -6913,7 +6799,7 @@ var file_ateapi_proto_depIdxs = []int32{
|
||||
15, // 12: ateapi.EgressPolicy.rules:type_name -> ateapi.EgressRule
|
||||
16, // 13: ateapi.EgressRule.hostnames:type_name -> ateapi.HostnameRule
|
||||
17, // 14: ateapi.EgressRule.ip_blocks:type_name -> ateapi.IPBlockRule
|
||||
108, // 15: ateapi.EgressRule.all:type_name -> google.protobuf.Empty
|
||||
107, // 15: ateapi.EgressRule.all:type_name -> google.protobuf.Empty
|
||||
18, // 16: ateapi.HostnameRule.effects:type_name -> ateapi.EgressRuleEffects
|
||||
19, // 17: ateapi.EgressRuleEffects.inject_static_headers:type_name -> ateapi.CredentialHeaderInjection
|
||||
2, // 18: ateapi.ActorStatus.state:type_name -> ateapi.ActorState
|
||||
@@ -6943,7 +6829,7 @@ var file_ateapi_proto_depIdxs = []int32{
|
||||
32, // 42: ateapi.ActorTemplate.status:type_name -> ateapi.ActorTemplateStatus
|
||||
30, // 43: ateapi.Resources.limits:type_name -> ateapi.Limits
|
||||
27, // 44: ateapi.GoldenSnapshotStatus.golden_snapshot:type_name -> ateapi.ObjectRef
|
||||
107, // 45: ateapi.GoldenSnapshotStatus.take_golden_snapshot_at:type_name -> google.protobuf.Timestamp
|
||||
106, // 45: ateapi.GoldenSnapshotStatus.take_golden_snapshot_at:type_name -> google.protobuf.Timestamp
|
||||
31, // 46: ateapi.ActorTemplateStatus.golden_snapshot_status:type_name -> ateapi.GoldenSnapshotStatus
|
||||
3, // 47: ateapi.SandboxConfig.sandbox_class:type_name -> ateapi.SandboxClass
|
||||
0, // 48: ateapi.SnapshotsConfig.on_pause:type_name -> ateapi.SnapshotContentScope
|
||||
@@ -7005,91 +6891,90 @@ var file_ateapi_proto_depIdxs = []int32{
|
||||
27, // 104: ateapi.DrainWorkerRequest.worker:type_name -> ateapi.ObjectRef
|
||||
13, // 105: ateapi.ListActorsResponse.actors:type_name -> ateapi.Actor
|
||||
11, // 106: ateapi.Worker.metadata:type_name -> ateapi.ResourceMetadata
|
||||
106, // 107: ateapi.Worker.labels:type_name -> ateapi.Worker.LabelsEntry
|
||||
105, // 107: ateapi.Worker.labels:type_name -> ateapi.Worker.LabelsEntry
|
||||
95, // 108: ateapi.Worker.capacity:type_name -> ateapi.WorkerCapacity
|
||||
94, // 109: ateapi.Worker.status:type_name -> ateapi.WorkerStatus
|
||||
6, // 110: ateapi.WorkerStatus.state:type_name -> ateapi.WorkerState
|
||||
96, // 111: ateapi.WorkerStatus.assignment:type_name -> ateapi.ActorAssignment
|
||||
97, // 112: ateapi.ActorAssignment.actor_template:type_name -> ateapi.KubeNamespacedObjectRef
|
||||
27, // 113: ateapi.ActorAssignment.actor:type_name -> ateapi.ObjectRef
|
||||
27, // 114: ateapi.ActorAssignment.actor_template_ref:type_name -> ateapi.ObjectRef
|
||||
27, // 115: ateapi.MintCertRequest.worker:type_name -> ateapi.ObjectRef
|
||||
7, // 116: ateapi.MintCertRequest.purpose:type_name -> ateapi.ActorCertificatePurpose
|
||||
62, // 117: ateapi.Control.GetActor:input_type -> ateapi.GetActorRequest
|
||||
63, // 118: ateapi.Control.CreateActor:input_type -> ateapi.CreateActorRequest
|
||||
64, // 119: ateapi.Control.UpdateActor:input_type -> ateapi.UpdateActorRequest
|
||||
65, // 120: ateapi.Control.SuspendActor:input_type -> ateapi.SuspendActorRequest
|
||||
67, // 121: ateapi.Control.PauseActor:input_type -> ateapi.PauseActorRequest
|
||||
69, // 122: ateapi.Control.ResumeActor:input_type -> ateapi.ResumeActorRequest
|
||||
71, // 123: ateapi.Control.DeleteActor:input_type -> ateapi.DeleteActorRequest
|
||||
72, // 124: ateapi.Control.GetActorEgressPolicy:input_type -> ateapi.GetActorEgressPolicyRequest
|
||||
73, // 125: ateapi.Control.CreateActorEgressPolicy:input_type -> ateapi.CreateActorEgressPolicyRequest
|
||||
74, // 126: ateapi.Control.UpdateActorEgressPolicy:input_type -> ateapi.UpdateActorEgressPolicyRequest
|
||||
75, // 127: ateapi.Control.DeleteActorEgressPolicy:input_type -> ateapi.DeleteActorEgressPolicyRequest
|
||||
76, // 128: ateapi.Control.GetActorSnapshot:input_type -> ateapi.GetActorSnapshotRequest
|
||||
77, // 129: ateapi.Control.GetActorSnapshotTag:input_type -> ateapi.GetActorSnapshotTagRequest
|
||||
78, // 130: ateapi.Control.ListActorSnapshots:input_type -> ateapi.ListActorSnapshotsRequest
|
||||
80, // 131: ateapi.Control.CreateActorSnapshotTag:input_type -> ateapi.CreateActorSnapshotTagRequest
|
||||
81, // 132: ateapi.Control.UpdateActorSnapshotTag:input_type -> ateapi.UpdateActorSnapshotTagRequest
|
||||
82, // 133: ateapi.Control.DeleteActorSnapshotTag:input_type -> ateapi.DeleteActorSnapshotTagRequest
|
||||
84, // 134: ateapi.Control.ListWorkers:input_type -> ateapi.ListWorkersRequest
|
||||
86, // 135: ateapi.Control.GetWorker:input_type -> ateapi.GetWorkerRequest
|
||||
87, // 136: ateapi.Control.CreateWorker:input_type -> ateapi.CreateWorkerRequest
|
||||
88, // 137: ateapi.Control.UpdateWorker:input_type -> ateapi.UpdateWorkerRequest
|
||||
89, // 138: ateapi.Control.DeleteWorker:input_type -> ateapi.DeleteWorkerRequest
|
||||
90, // 139: ateapi.Control.DrainWorker:input_type -> ateapi.DrainWorkerRequest
|
||||
91, // 140: ateapi.Control.ListActors:input_type -> ateapi.ListActorsRequest
|
||||
52, // 141: ateapi.Control.CreateAtespace:input_type -> ateapi.CreateAtespaceRequest
|
||||
53, // 142: ateapi.Control.GetAtespace:input_type -> ateapi.GetAtespaceRequest
|
||||
54, // 143: ateapi.Control.ListAtespaces:input_type -> ateapi.ListAtespacesRequest
|
||||
56, // 144: ateapi.Control.DeleteAtespace:input_type -> ateapi.DeleteAtespaceRequest
|
||||
57, // 145: ateapi.Control.CreateActorTemplate:input_type -> ateapi.CreateActorTemplateRequest
|
||||
58, // 146: ateapi.Control.GetActorTemplate:input_type -> ateapi.GetActorTemplateRequest
|
||||
59, // 147: ateapi.Control.ListActorTemplates:input_type -> ateapi.ListActorTemplatesRequest
|
||||
61, // 148: ateapi.Control.DeleteActorTemplate:input_type -> ateapi.DeleteActorTemplateRequest
|
||||
98, // 149: ateapi.Debug.DebugClear:input_type -> ateapi.DebugClearRequest
|
||||
100, // 150: ateapi.ActorIdentity.MintJWT:input_type -> ateapi.MintJWTRequest
|
||||
102, // 151: ateapi.ActorIdentity.MintCert:input_type -> ateapi.MintCertRequest
|
||||
13, // 152: ateapi.Control.GetActor:output_type -> ateapi.Actor
|
||||
13, // 153: ateapi.Control.CreateActor:output_type -> ateapi.Actor
|
||||
13, // 154: ateapi.Control.UpdateActor:output_type -> ateapi.Actor
|
||||
66, // 155: ateapi.Control.SuspendActor:output_type -> ateapi.SuspendActorResponse
|
||||
68, // 156: ateapi.Control.PauseActor:output_type -> ateapi.PauseActorResponse
|
||||
70, // 157: ateapi.Control.ResumeActor:output_type -> ateapi.ResumeActorResponse
|
||||
13, // 158: ateapi.Control.DeleteActor:output_type -> ateapi.Actor
|
||||
14, // 159: ateapi.Control.GetActorEgressPolicy:output_type -> ateapi.EgressPolicy
|
||||
14, // 160: ateapi.Control.CreateActorEgressPolicy:output_type -> ateapi.EgressPolicy
|
||||
14, // 161: ateapi.Control.UpdateActorEgressPolicy:output_type -> ateapi.EgressPolicy
|
||||
14, // 162: ateapi.Control.DeleteActorEgressPolicy:output_type -> ateapi.EgressPolicy
|
||||
23, // 163: ateapi.Control.GetActorSnapshot:output_type -> ateapi.ActorSnapshot
|
||||
25, // 164: ateapi.Control.GetActorSnapshotTag:output_type -> ateapi.ActorSnapshotTag
|
||||
79, // 165: ateapi.Control.ListActorSnapshots:output_type -> ateapi.ListActorSnapshotsResponse
|
||||
25, // 166: ateapi.Control.CreateActorSnapshotTag:output_type -> ateapi.ActorSnapshotTag
|
||||
25, // 167: ateapi.Control.UpdateActorSnapshotTag:output_type -> ateapi.ActorSnapshotTag
|
||||
25, // 168: ateapi.Control.DeleteActorSnapshotTag:output_type -> ateapi.ActorSnapshotTag
|
||||
85, // 169: ateapi.Control.ListWorkers:output_type -> ateapi.ListWorkersResponse
|
||||
93, // 170: ateapi.Control.GetWorker:output_type -> ateapi.Worker
|
||||
93, // 171: ateapi.Control.CreateWorker:output_type -> ateapi.Worker
|
||||
93, // 172: ateapi.Control.UpdateWorker:output_type -> ateapi.Worker
|
||||
93, // 173: ateapi.Control.DeleteWorker:output_type -> ateapi.Worker
|
||||
93, // 174: ateapi.Control.DrainWorker:output_type -> ateapi.Worker
|
||||
92, // 175: ateapi.Control.ListActors:output_type -> ateapi.ListActorsResponse
|
||||
26, // 176: ateapi.Control.CreateAtespace:output_type -> ateapi.Atespace
|
||||
26, // 177: ateapi.Control.GetAtespace:output_type -> ateapi.Atespace
|
||||
55, // 178: ateapi.Control.ListAtespaces:output_type -> ateapi.ListAtespacesResponse
|
||||
26, // 179: ateapi.Control.DeleteAtespace:output_type -> ateapi.Atespace
|
||||
28, // 180: ateapi.Control.CreateActorTemplate:output_type -> ateapi.ActorTemplate
|
||||
28, // 181: ateapi.Control.GetActorTemplate:output_type -> ateapi.ActorTemplate
|
||||
60, // 182: ateapi.Control.ListActorTemplates:output_type -> ateapi.ListActorTemplatesResponse
|
||||
28, // 183: ateapi.Control.DeleteActorTemplate:output_type -> ateapi.ActorTemplate
|
||||
99, // 184: ateapi.Debug.DebugClear:output_type -> ateapi.DebugClearResponse
|
||||
101, // 185: ateapi.ActorIdentity.MintJWT:output_type -> ateapi.MintJWTResponse
|
||||
103, // 186: ateapi.ActorIdentity.MintCert:output_type -> ateapi.MintCertResponse
|
||||
152, // [152:187] is the sub-list for method output_type
|
||||
117, // [117:152] is the sub-list for method input_type
|
||||
117, // [117:117] is the sub-list for extension type_name
|
||||
117, // [117:117] is the sub-list for extension extendee
|
||||
0, // [0:117] is the sub-list for field type_name
|
||||
27, // 112: ateapi.ActorAssignment.actor:type_name -> ateapi.ObjectRef
|
||||
27, // 113: ateapi.ActorAssignment.actor_template_ref:type_name -> ateapi.ObjectRef
|
||||
27, // 114: ateapi.MintCertRequest.worker:type_name -> ateapi.ObjectRef
|
||||
7, // 115: ateapi.MintCertRequest.purpose:type_name -> ateapi.ActorCertificatePurpose
|
||||
62, // 116: ateapi.Control.GetActor:input_type -> ateapi.GetActorRequest
|
||||
63, // 117: ateapi.Control.CreateActor:input_type -> ateapi.CreateActorRequest
|
||||
64, // 118: ateapi.Control.UpdateActor:input_type -> ateapi.UpdateActorRequest
|
||||
65, // 119: ateapi.Control.SuspendActor:input_type -> ateapi.SuspendActorRequest
|
||||
67, // 120: ateapi.Control.PauseActor:input_type -> ateapi.PauseActorRequest
|
||||
69, // 121: ateapi.Control.ResumeActor:input_type -> ateapi.ResumeActorRequest
|
||||
71, // 122: ateapi.Control.DeleteActor:input_type -> ateapi.DeleteActorRequest
|
||||
72, // 123: ateapi.Control.GetActorEgressPolicy:input_type -> ateapi.GetActorEgressPolicyRequest
|
||||
73, // 124: ateapi.Control.CreateActorEgressPolicy:input_type -> ateapi.CreateActorEgressPolicyRequest
|
||||
74, // 125: ateapi.Control.UpdateActorEgressPolicy:input_type -> ateapi.UpdateActorEgressPolicyRequest
|
||||
75, // 126: ateapi.Control.DeleteActorEgressPolicy:input_type -> ateapi.DeleteActorEgressPolicyRequest
|
||||
76, // 127: ateapi.Control.GetActorSnapshot:input_type -> ateapi.GetActorSnapshotRequest
|
||||
77, // 128: ateapi.Control.GetActorSnapshotTag:input_type -> ateapi.GetActorSnapshotTagRequest
|
||||
78, // 129: ateapi.Control.ListActorSnapshots:input_type -> ateapi.ListActorSnapshotsRequest
|
||||
80, // 130: ateapi.Control.CreateActorSnapshotTag:input_type -> ateapi.CreateActorSnapshotTagRequest
|
||||
81, // 131: ateapi.Control.UpdateActorSnapshotTag:input_type -> ateapi.UpdateActorSnapshotTagRequest
|
||||
82, // 132: ateapi.Control.DeleteActorSnapshotTag:input_type -> ateapi.DeleteActorSnapshotTagRequest
|
||||
84, // 133: ateapi.Control.ListWorkers:input_type -> ateapi.ListWorkersRequest
|
||||
86, // 134: ateapi.Control.GetWorker:input_type -> ateapi.GetWorkerRequest
|
||||
87, // 135: ateapi.Control.CreateWorker:input_type -> ateapi.CreateWorkerRequest
|
||||
88, // 136: ateapi.Control.UpdateWorker:input_type -> ateapi.UpdateWorkerRequest
|
||||
89, // 137: ateapi.Control.DeleteWorker:input_type -> ateapi.DeleteWorkerRequest
|
||||
90, // 138: ateapi.Control.DrainWorker:input_type -> ateapi.DrainWorkerRequest
|
||||
91, // 139: ateapi.Control.ListActors:input_type -> ateapi.ListActorsRequest
|
||||
52, // 140: ateapi.Control.CreateAtespace:input_type -> ateapi.CreateAtespaceRequest
|
||||
53, // 141: ateapi.Control.GetAtespace:input_type -> ateapi.GetAtespaceRequest
|
||||
54, // 142: ateapi.Control.ListAtespaces:input_type -> ateapi.ListAtespacesRequest
|
||||
56, // 143: ateapi.Control.DeleteAtespace:input_type -> ateapi.DeleteAtespaceRequest
|
||||
57, // 144: ateapi.Control.CreateActorTemplate:input_type -> ateapi.CreateActorTemplateRequest
|
||||
58, // 145: ateapi.Control.GetActorTemplate:input_type -> ateapi.GetActorTemplateRequest
|
||||
59, // 146: ateapi.Control.ListActorTemplates:input_type -> ateapi.ListActorTemplatesRequest
|
||||
61, // 147: ateapi.Control.DeleteActorTemplate:input_type -> ateapi.DeleteActorTemplateRequest
|
||||
97, // 148: ateapi.Debug.DebugClear:input_type -> ateapi.DebugClearRequest
|
||||
99, // 149: ateapi.ActorIdentity.MintJWT:input_type -> ateapi.MintJWTRequest
|
||||
101, // 150: ateapi.ActorIdentity.MintCert:input_type -> ateapi.MintCertRequest
|
||||
13, // 151: ateapi.Control.GetActor:output_type -> ateapi.Actor
|
||||
13, // 152: ateapi.Control.CreateActor:output_type -> ateapi.Actor
|
||||
13, // 153: ateapi.Control.UpdateActor:output_type -> ateapi.Actor
|
||||
66, // 154: ateapi.Control.SuspendActor:output_type -> ateapi.SuspendActorResponse
|
||||
68, // 155: ateapi.Control.PauseActor:output_type -> ateapi.PauseActorResponse
|
||||
70, // 156: ateapi.Control.ResumeActor:output_type -> ateapi.ResumeActorResponse
|
||||
13, // 157: ateapi.Control.DeleteActor:output_type -> ateapi.Actor
|
||||
14, // 158: ateapi.Control.GetActorEgressPolicy:output_type -> ateapi.EgressPolicy
|
||||
14, // 159: ateapi.Control.CreateActorEgressPolicy:output_type -> ateapi.EgressPolicy
|
||||
14, // 160: ateapi.Control.UpdateActorEgressPolicy:output_type -> ateapi.EgressPolicy
|
||||
14, // 161: ateapi.Control.DeleteActorEgressPolicy:output_type -> ateapi.EgressPolicy
|
||||
23, // 162: ateapi.Control.GetActorSnapshot:output_type -> ateapi.ActorSnapshot
|
||||
25, // 163: ateapi.Control.GetActorSnapshotTag:output_type -> ateapi.ActorSnapshotTag
|
||||
79, // 164: ateapi.Control.ListActorSnapshots:output_type -> ateapi.ListActorSnapshotsResponse
|
||||
25, // 165: ateapi.Control.CreateActorSnapshotTag:output_type -> ateapi.ActorSnapshotTag
|
||||
25, // 166: ateapi.Control.UpdateActorSnapshotTag:output_type -> ateapi.ActorSnapshotTag
|
||||
25, // 167: ateapi.Control.DeleteActorSnapshotTag:output_type -> ateapi.ActorSnapshotTag
|
||||
85, // 168: ateapi.Control.ListWorkers:output_type -> ateapi.ListWorkersResponse
|
||||
93, // 169: ateapi.Control.GetWorker:output_type -> ateapi.Worker
|
||||
93, // 170: ateapi.Control.CreateWorker:output_type -> ateapi.Worker
|
||||
93, // 171: ateapi.Control.UpdateWorker:output_type -> ateapi.Worker
|
||||
93, // 172: ateapi.Control.DeleteWorker:output_type -> ateapi.Worker
|
||||
93, // 173: ateapi.Control.DrainWorker:output_type -> ateapi.Worker
|
||||
92, // 174: ateapi.Control.ListActors:output_type -> ateapi.ListActorsResponse
|
||||
26, // 175: ateapi.Control.CreateAtespace:output_type -> ateapi.Atespace
|
||||
26, // 176: ateapi.Control.GetAtespace:output_type -> ateapi.Atespace
|
||||
55, // 177: ateapi.Control.ListAtespaces:output_type -> ateapi.ListAtespacesResponse
|
||||
26, // 178: ateapi.Control.DeleteAtespace:output_type -> ateapi.Atespace
|
||||
28, // 179: ateapi.Control.CreateActorTemplate:output_type -> ateapi.ActorTemplate
|
||||
28, // 180: ateapi.Control.GetActorTemplate:output_type -> ateapi.ActorTemplate
|
||||
60, // 181: ateapi.Control.ListActorTemplates:output_type -> ateapi.ListActorTemplatesResponse
|
||||
28, // 182: ateapi.Control.DeleteActorTemplate:output_type -> ateapi.ActorTemplate
|
||||
98, // 183: ateapi.Debug.DebugClear:output_type -> ateapi.DebugClearResponse
|
||||
100, // 184: ateapi.ActorIdentity.MintJWT:output_type -> ateapi.MintJWTResponse
|
||||
102, // 185: ateapi.ActorIdentity.MintCert:output_type -> ateapi.MintCertResponse
|
||||
151, // [151:186] is the sub-list for method output_type
|
||||
116, // [116:151] is the sub-list for method input_type
|
||||
116, // [116:116] is the sub-list for extension type_name
|
||||
116, // [116:116] is the sub-list for extension extendee
|
||||
0, // [0:116] is the sub-list for field type_name
|
||||
}
|
||||
|
||||
func init() { file_ateapi_proto_init() }
|
||||
@@ -7103,7 +6988,7 @@ func file_ateapi_proto_init() {
|
||||
GoPackagePath: reflect.TypeOf(x{}).PkgPath(),
|
||||
RawDescriptor: unsafe.Slice(unsafe.StringData(file_ateapi_proto_rawDesc), len(file_ateapi_proto_rawDesc)),
|
||||
NumEnums: 9,
|
||||
NumMessages: 98,
|
||||
NumMessages: 97,
|
||||
NumExtensions: 0,
|
||||
NumServices: 3,
|
||||
},
|
||||
|
||||
@@ -263,24 +263,10 @@ message Actor {
|
||||
// +k8s:subfield(atespace)=+k8s:required
|
||||
ResourceMetadata metadata = 1;
|
||||
|
||||
// The template is named by the legacy CRD namespace/name pair below or by
|
||||
// the substrate actor_template ref; actor_template takes precedence when
|
||||
// set.
|
||||
//
|
||||
// TODO: delete both fields once we start using actor_template below.
|
||||
// +k8s:optional
|
||||
// +k8s:format=k8s-short-name
|
||||
// +k8s:immutable
|
||||
string actor_template_namespace = 2;
|
||||
// +k8s:optional
|
||||
// +k8s:format=k8s-long-name
|
||||
// +k8s:immutable
|
||||
string actor_template_name = 3;
|
||||
|
||||
// TODO: replace with full actor_template spec if we decide to make each Actor self-contained.
|
||||
// +k8s:optional # TODO: this should be required?
|
||||
// +k8s:required
|
||||
// +k8s:subfield(atespace)=+k8s:required
|
||||
// +k8s:immutable # TODO: is this mutable?
|
||||
// +k8s:mutable
|
||||
ObjectRef actor_template = 4;
|
||||
|
||||
// worker_selector is the per-actor placement constraint. The scheduler
|
||||
@@ -571,8 +557,6 @@ message ActorSnapshotStatus {
|
||||
ObjectRef source_actor = 1;
|
||||
string source_actor_uid = 2;
|
||||
int64 source_actor_version = 3;
|
||||
string actor_template_namespace = 4;
|
||||
string actor_template_name = 5;
|
||||
string actor_template_uid = 6;
|
||||
SnapshotContentScope content_scope = 7;
|
||||
string snapshot_uri = 8;
|
||||
@@ -1360,23 +1344,15 @@ message WorkerCapacity {
|
||||
// ActorAssignment names the Actor currently bound to a Worker — the inverse of
|
||||
// WorkerAssignment.
|
||||
message ActorAssignment {
|
||||
// The KubeNamespacedObjectRef actor_template names the CRD-backed template
|
||||
// of an actor created with the legacy CRD path
|
||||
// actor_template_ref names the substrate ActorTemplate resource.
|
||||
// Exactly one is set.
|
||||
KubeNamespacedObjectRef actor_template = 1;
|
||||
// +k8s:opaqueType
|
||||
ObjectRef actor = 2;
|
||||
string actor_uid = 3;
|
||||
// actor_template_ref names the substrate ActorTemplate resource the
|
||||
// assigned actor was created from.
|
||||
// +k8s:opaqueType
|
||||
ObjectRef actor_template_ref = 4;
|
||||
}
|
||||
|
||||
message KubeNamespacedObjectRef {
|
||||
string namespace = 1;
|
||||
string name = 2;
|
||||
}
|
||||
|
||||
// Debug is the RPC interface for administrative and debugging operations
|
||||
// (such as wiping state during development).
|
||||
service Debug {
|
||||
|
||||
@@ -9,21 +9,11 @@
|
||||
"subject": "ateapi.Actor",
|
||||
"message": "message has no doc comment"
|
||||
},
|
||||
{
|
||||
"rule": "documented",
|
||||
"subject": "ateapi.Actor.actor_template_name",
|
||||
"message": "field has no doc comment"
|
||||
},
|
||||
{
|
||||
"rule": "documented",
|
||||
"subject": "ateapi.ActorAssignment.actor",
|
||||
"message": "field has no doc comment"
|
||||
},
|
||||
{
|
||||
"rule": "documented",
|
||||
"subject": "ateapi.ActorAssignment.actor_template_ref",
|
||||
"message": "field has no doc comment"
|
||||
},
|
||||
{
|
||||
"rule": "documented",
|
||||
"subject": "ateapi.ActorAssignment.actor_uid",
|
||||
@@ -54,16 +44,6 @@
|
||||
"subject": "ateapi.ActorSnapshotStatus",
|
||||
"message": "message has no doc comment"
|
||||
},
|
||||
{
|
||||
"rule": "documented",
|
||||
"subject": "ateapi.ActorSnapshotStatus.actor_template_name",
|
||||
"message": "field has no doc comment"
|
||||
},
|
||||
{
|
||||
"rule": "documented",
|
||||
"subject": "ateapi.ActorSnapshotStatus.actor_template_namespace",
|
||||
"message": "field has no doc comment"
|
||||
},
|
||||
{
|
||||
"rule": "documented",
|
||||
"subject": "ateapi.ActorSnapshotStatus.actor_template_uid",
|
||||
@@ -349,21 +329,6 @@
|
||||
"subject": "ateapi.HTTPGetAction.port",
|
||||
"message": "field has no doc comment"
|
||||
},
|
||||
{
|
||||
"rule": "documented",
|
||||
"subject": "ateapi.KubeNamespacedObjectRef",
|
||||
"message": "message has no doc comment"
|
||||
},
|
||||
{
|
||||
"rule": "documented",
|
||||
"subject": "ateapi.KubeNamespacedObjectRef.name",
|
||||
"message": "field has no doc comment"
|
||||
},
|
||||
{
|
||||
"rule": "documented",
|
||||
"subject": "ateapi.KubeNamespacedObjectRef.namespace",
|
||||
"message": "field has no doc comment"
|
||||
},
|
||||
{
|
||||
"rule": "documented",
|
||||
"subject": "ateapi.Limits",
|
||||
|
||||
Reference in New Issue
Block a user