From 9cefcb43d74692c871498478ec367717e74aa8b9 Mon Sep 17 00:00:00 2001 From: Sneha Aradhey Date: Tue, 11 Aug 2026 21:54:44 +0000 Subject: [PATCH] Implement force delete for actors - Add force option to DeleteActor and create a workflow to cleanup the actor. - Add atelet and ateom rpcs to Terminate a workload - Add runtime implementations to terminate a workload - Add E2E tests, CLI flag, and refactor delete workflow to new style - Fix actor deletion workflow and tests for force delete TAG=agy CONV=a523cd5e-374e-4b76-8d7d-9cc281254145 --- .../internal/controlapi/delete_actor.go | 2 +- .../internal/controlapi/delete_actor_test.go | 141 ++++++ .../internal/controlapi/functional_test.go | 174 ++++++- .../internal/controlapi/workflow_delete.go | 247 +++++++++- .../controlapi/workflow_delete_test.go | 111 ++++- .../internal/controlapi/workflow_suspend.go | 27 +- cmd/atelet/main.go | 63 +++ cmd/atelet/main_test.go | 35 ++ cmd/ateom-gvisor/main.go | 63 ++- cmd/ateom-microvm/checkpoint.go | 38 +- cmd/kubectl-ate/internal/cmd/delete_actor.go | 3 + demos/claude-code-multiplex/ui/server.go | 4 + internal/e2e/suites/demo/demo_test.go | 225 +++++++++ internal/proto/ateletpb/atelet.pb.go | 440 ++++++++++++------ internal/proto/ateletpb/atelet.proto | 20 + internal/proto/ateletpb/atelet_grpc.pb.go | 42 ++ internal/proto/ateompb/ateom.pb.go | 335 +++++++++---- internal/proto/ateompb/ateom.proto | 19 + internal/proto/ateompb/ateom_grpc.pb.go | 42 ++ pkg/proto/ateapipb/ateapi.pb.go | 23 +- pkg/proto/ateapipb/ateapi.proto | 2 + 21 files changed, 1727 insertions(+), 329 deletions(-) diff --git a/cmd/ateapi/internal/controlapi/delete_actor.go b/cmd/ateapi/internal/controlapi/delete_actor.go index 9cbd77c89..d0ad2ff86 100644 --- a/cmd/ateapi/internal/controlapi/delete_actor.go +++ b/cmd/ateapi/internal/controlapi/delete_actor.go @@ -45,7 +45,7 @@ func (s *Service) DeleteActor(ctx context.Context, req *ateapipb.DeleteActorRequ actorRef := resources.ActorRefFromObjectRef(req.GetActor()) setSpanActorRefAttributes(ctx, actorRef) - deleted, err = s.actorWorkflow.DeleteActor(ctx, actorRef) + deleted, err = s.actorWorkflow.DeleteActor(ctx, actorRef, req.GetForce()) if err != nil { return nil, err } diff --git a/cmd/ateapi/internal/controlapi/delete_actor_test.go b/cmd/ateapi/internal/controlapi/delete_actor_test.go index 2148a07eb..fc7f81254 100644 --- a/cmd/ateapi/internal/controlapi/delete_actor_test.go +++ b/cmd/ateapi/internal/controlapi/delete_actor_test.go @@ -207,3 +207,144 @@ func TestDeleteActor_MultipleVolumeDeletionFailures(t *testing.T) { t.Errorf("expected error message to contain both volume failure details, got: %v", errMsg) } } + +func TestDeleteActor_Force_Success(t *testing.T) { + ns := namespaceForTest("ns-delete-force-succ") + tc := setupTest(t, ns) + defer tc.cleanup() + createTemplate(t, tc, ns) + createWorkerPod(t, tc, ns, "worker-1", "node1", "pool1") + + runningActor := &ateapipb.Actor{ + Metadata: &ateapipb.ResourceMetadata{ + Atespace: testAtespace, + Name: "running-actor", + }, + Status: ateapipb.Actor_STATUS_RUNNING, + ActorTemplateNamespace: ns, + ActorTemplateName: "tmpl1", + WorkerAssignment: &ateapipb.WorkerAssignment{ + WorkerNamespace: ns, + WorkerPod: "worker-1", + WorkerPool: "pool1", + }, + } + createdActor, err := tc.persistence.CreateActor(context.Background(), runningActor) + if err != nil { + t.Fatalf("CreateActor: %v", err) + } + + worker, err := tc.persistence.GetWorker(context.Background(), ns, "pool1", "worker-1") + if err != nil { + t.Fatalf("GetWorker: %v", err) + } + worker.Assignment = &ateapipb.Assignment{ + Actor: &ateapipb.ObjectRef{Atespace: testAtespace, Name: "running-actor"}, + ActorUid: createdActor.GetMetadata().GetUid(), + } + if err := tc.persistence.UpdateWorker(context.Background(), worker, worker.Version); err != nil { + t.Fatalf("UpdateWorker: %v", err) + } + + deleted, err := tc.service.DeleteActor(context.Background(), &ateapipb.DeleteActorRequest{ + Actor: &ateapipb.ObjectRef{Atespace: testAtespace, Name: "running-actor"}, + Force: true, + }) + if err != nil { + t.Fatalf("DeleteActor with force failed: %v", err) + } + if deleted.GetMetadata().GetName() != "running-actor" { + t.Errorf("deleted actor name = %q, want %q", deleted.GetMetadata().GetName(), "running-actor") + } + + // Verify actor is removed from store + if _, err := tc.persistence.GetActor(context.Background(), resources.ActorRef{Atespace: testAtespace, Name: "running-actor"}); err == nil { + t.Errorf("expected actor to be deleted from store, but it still exists") + } + + // Verify worker assignment is cleared + w, err := tc.persistence.GetWorker(context.Background(), ns, "pool1", "worker-1") + if err != nil { + t.Fatalf("GetWorker after delete failed: %v", err) + } + if w.Assignment != nil { + t.Errorf("expected worker assignment to be nil, got: %v", w.Assignment) + } + + // Verify atelet Terminate was called + if !tc.fakeAtelet.TerminateCalled { + t.Errorf("expected atelet Terminate to be called for force delete") + } +} + +func TestDeleteActor_Force_SuspendedAllowed(t *testing.T) { + ns := namespaceForTest("ns-delete-force-susp") + tc := setupTest(t, ns) + defer tc.cleanup() + createTemplate(t, tc, ns) + + suspendedActor := &ateapipb.Actor{ + Metadata: &ateapipb.ResourceMetadata{ + Atespace: testAtespace, + Name: "suspended-actor", + }, + Status: ateapipb.Actor_STATUS_SUSPENDED, + ActorTemplateNamespace: ns, + ActorTemplateName: "tmpl1", + } + if _, err := tc.persistence.CreateActor(context.Background(), suspendedActor); err != nil { + t.Fatalf("CreateActor: %v", err) + } + + deleted, err := tc.service.DeleteActor(context.Background(), &ateapipb.DeleteActorRequest{ + Actor: &ateapipb.ObjectRef{Atespace: testAtespace, Name: "suspended-actor"}, + Force: true, + }) + if err != nil { + t.Fatalf("expected DeleteActor with force on suspended actor to succeed, got: %v", err) + } + if deleted.GetMetadata().GetName() != "suspended-actor" { + t.Errorf("deleted.Name = %q, want %q", deleted.GetMetadata().GetName(), "suspended-actor") + } +} + +func TestDeleteActor_Force_WorkerNotFound_Succeeds(t *testing.T) { + ns := namespaceForTest("ns-delete-force-noworker") + tc := setupTest(t, ns) + defer tc.cleanup() + createTemplate(t, tc, ns) + + runningActor := &ateapipb.Actor{ + Metadata: &ateapipb.ResourceMetadata{ + Atespace: testAtespace, + Name: "running-actor", + }, + Status: ateapipb.Actor_STATUS_RUNNING, + ActorTemplateNamespace: ns, + ActorTemplateName: "tmpl1", + WorkerAssignment: &ateapipb.WorkerAssignment{ + WorkerNamespace: ns, + WorkerPod: "non-existent-worker", + WorkerPool: "pool1", + }, + } + if _, err := tc.persistence.CreateActor(context.Background(), runningActor); err != nil { + t.Fatalf("CreateActor: %v", err) + } + + deleted, err := tc.service.DeleteActor(context.Background(), &ateapipb.DeleteActorRequest{ + Actor: &ateapipb.ObjectRef{Atespace: testAtespace, Name: "running-actor"}, + Force: true, + }) + if err != nil { + t.Fatalf("expected DeleteActor with force on non-existent worker to succeed, got: %v", err) + } + if deleted.GetMetadata().GetName() != "running-actor" { + t.Errorf("deleted.Name = %q, want %q", deleted.GetMetadata().GetName(), "running-actor") + } + + // Verify actor is removed from store + if _, err := tc.persistence.GetActor(context.Background(), resources.ActorRef{Atespace: testAtespace, Name: "running-actor"}); err == nil { + t.Errorf("expected actor to be deleted from store, but it still exists") + } +} diff --git a/cmd/ateapi/internal/controlapi/functional_test.go b/cmd/ateapi/internal/controlapi/functional_test.go index f04ba8e3b..719cb7266 100644 --- a/cmd/ateapi/internal/controlapi/functional_test.go +++ b/cmd/ateapi/internal/controlapi/functional_test.go @@ -185,6 +185,10 @@ type FakeAteletServer struct { UploadCalled bool UploadRequest *ateletpb.UploadPausedCheckpointRequest FailUpload error + + TerminateCalled bool + TerminateRequest *ateletpb.TerminateRequest + FailTerminate error } func (f *FakeAteletServer) Reset() { @@ -206,6 +210,10 @@ func (f *FakeAteletServer) Reset() { f.UploadCalled = false f.UploadRequest = nil f.FailUpload = nil + + f.TerminateCalled = false + f.TerminateRequest = nil + f.FailTerminate = nil } func (f *FakeAteletServer) UploadPausedCheckpoint(ctx context.Context, req *ateletpb.UploadPausedCheckpointRequest) (*ateletpb.UploadPausedCheckpointResponse, error) { @@ -220,6 +228,19 @@ func (f *FakeAteletServer) UploadPausedCheckpoint(ctx context.Context, req *atel return &ateletpb.UploadPausedCheckpointResponse{}, nil } +func (f *FakeAteletServer) Terminate(ctx context.Context, req *ateletpb.TerminateRequest) (*ateletpb.TerminateResponse, error) { + f.Lock.Lock() + defer f.Lock.Unlock() + + f.TerminateCalled = true + f.TerminateRequest = proto.Clone(req).(*ateletpb.TerminateRequest) + if f.FailTerminate != nil { + return nil, f.FailTerminate + } + + return &ateletpb.TerminateResponse{}, nil +} + func (f *FakeAteletServer) Run(ctx context.Context, req *ateletpb.RunRequest) (*ateletpb.RunResponse, error) { f.Lock.Lock() defer f.Lock.Unlock() @@ -3326,31 +3347,162 @@ func TestDeleteActor_Crashed(t *testing.T) { t.Fatalf("UpdateActor failed: %v", err) } - deleted, err := tc.client.DeleteActor(context.Background(), &ateapipb.DeleteActorRequest{ + _, err := tc.client.DeleteActor(context.Background(), &ateapipb.DeleteActorRequest{ Actor: &ateapipb.ObjectRef{Atespace: testAtespace, Name: "id1"}, }) + assertGrpcError(t, err, codes.FailedPrecondition, "Actor test-atespace/id1 is not in a deletable status (status: STATUS_CRASHED)") +} + +func TestDeleteActor_NotFound(t *testing.T) { + ns := namespaceForTest("ns-delete-notfound") + tc := setupTest(t, ns) + defer tc.cleanup() + + _, err := tc.client.DeleteActor(context.Background(), &ateapipb.DeleteActorRequest{ + Actor: &ateapipb.ObjectRef{Atespace: testAtespace, Name: "non-existent"}, + }) + assertGrpcError(t, err, codes.NotFound, "Actor test-atespace/non-existent not found") +} + +func TestDeleteActor_Force(t *testing.T) { + ns := namespaceForTest("ns-delete-force") + tc := setupTest(t, ns) + defer tc.cleanup() + + createTemplate(t, tc, ns) + createWorkerPod(t, tc, ns, "worker-1", "node1", "pool1") + + // 1. Create and resume actor to running status + _, err := tc.client.CreateActor(context.Background(), &ateapipb.CreateActorRequest{Actor: &ateapipb.Actor{ + Metadata: &ateapipb.ResourceMetadata{Atespace: testAtespace, Name: "force-actor"}, + ActorTemplateNamespace: ns, + ActorTemplateName: "tmpl1", + }}) + if err != nil { + t.Fatalf("CreateActor failed: %v", err) + } + + resumeResp, err := tc.client.ResumeActor(context.Background(), &ateapipb.ResumeActorRequest{ + Actor: &ateapipb.ObjectRef{Atespace: testAtespace, Name: "force-actor"}, + }) if err != nil { - t.Fatalf("DeleteActor of crashed actor failed: %v", err) + t.Fatalf("ResumeActor failed: %v", err) } - if got := deleted.GetStatus(); got != ateapipb.Actor_STATUS_DELETING { - t.Errorf("deleted actor status = %v, want %v", got, ateapipb.Actor_STATUS_DELETING) + if resumeResp.GetActor().GetStatus() != ateapipb.Actor_STATUS_RUNNING { + t.Fatalf("expected status STATUS_RUNNING, got %v", resumeResp.GetActor().GetStatus()) } + // 2. Force delete running actor + deleted, err := tc.client.DeleteActor(context.Background(), &ateapipb.DeleteActorRequest{ + Actor: &ateapipb.ObjectRef{Atespace: testAtespace, Name: "force-actor"}, + Force: true, + }) + if err != nil { + t.Fatalf("DeleteActor with force failed: %v", err) + } + if deleted.GetMetadata().GetName() != "force-actor" { + t.Errorf("deleted actor name = %q, want %q", deleted.GetMetadata().GetName(), "force-actor") + } + + // Verify actor is removed from store _, err = tc.client.GetActor(context.Background(), &ateapipb.GetActorRequest{ - Actor: &ateapipb.ObjectRef{Atespace: testAtespace, Name: "id1"}, + Actor: &ateapipb.ObjectRef{Atespace: testAtespace, Name: "force-actor"}, }) - assertGrpcError(t, err, codes.NotFound, "Actor test-atespace/id1 not found") + assertGrpcError(t, err, codes.NotFound, "Actor test-atespace/force-actor not found") + + // Verify atelet Terminate was invoked + if !tc.fakeAtelet.TerminateCalled { + t.Errorf("expected atelet Terminate to have been called") + } + + // Verify worker is unassigned + w, err := tc.persistence.GetWorker(context.Background(), ns, "pool1", "worker-1") + if err != nil { + t.Fatalf("GetWorker failed: %v", err) + } + if w.Assignment != nil { + t.Errorf("expected worker assignment to be nil, got: %v", w.Assignment) + } + + // 3. Verify force delete on suspended actor succeeds + _, err = tc.client.CreateActor(context.Background(), &ateapipb.CreateActorRequest{Actor: &ateapipb.Actor{ + Metadata: &ateapipb.ResourceMetadata{Atespace: testAtespace, Name: "susp-actor"}, + ActorTemplateNamespace: ns, + ActorTemplateName: "tmpl1", + }}) + if err != nil { + t.Fatalf("CreateActor failed: %v", err) + } + + deletedSusp, err := tc.client.DeleteActor(context.Background(), &ateapipb.DeleteActorRequest{ + Actor: &ateapipb.ObjectRef{Atespace: testAtespace, Name: "susp-actor"}, + Force: true, + }) + if err != nil { + t.Fatalf("expected DeleteActor with force on suspended actor to succeed, got %v", err) + } + if deletedSusp.GetMetadata().GetName() != "susp-actor" { + t.Errorf("deleted actor name = %q, want %q", deletedSusp.GetMetadata().GetName(), "susp-actor") + } } -func TestDeleteActor_NotFound(t *testing.T) { - ns := namespaceForTest("ns-delete-notfound") +func TestDeleteActor_Force_WorkerFailure(t *testing.T) { + ns := namespaceForTest("ns-delete-force-fail") tc := setupTest(t, ns) defer tc.cleanup() - _, err := tc.client.DeleteActor(context.Background(), &ateapipb.DeleteActorRequest{ - Actor: &ateapipb.ObjectRef{Atespace: testAtespace, Name: "non-existent"}, + createTemplate(t, tc, ns) + createWorkerPod(t, tc, ns, "worker-1", "node1", "pool1") + + // 1. Create and resume actor to running status + _, err := tc.client.CreateActor(context.Background(), &ateapipb.CreateActorRequest{Actor: &ateapipb.Actor{ + Metadata: &ateapipb.ResourceMetadata{Atespace: testAtespace, Name: "force-fail-actor"}, + ActorTemplateNamespace: ns, + ActorTemplateName: "tmpl1", + }}) + if err != nil { + t.Fatalf("CreateActor failed: %v", err) + } + + resumeResp, err := tc.client.ResumeActor(context.Background(), &ateapipb.ResumeActorRequest{ + Actor: &ateapipb.ObjectRef{Atespace: testAtespace, Name: "force-fail-actor"}, }) - assertGrpcError(t, err, codes.NotFound, "Actor test-atespace/non-existent not found") + if err != nil { + t.Fatalf("ResumeActor failed: %v", err) + } + if resumeResp.GetActor().GetStatus() != ateapipb.Actor_STATUS_RUNNING { + t.Fatalf("expected status STATUS_RUNNING, got %v", resumeResp.GetActor().GetStatus()) + } + + // 2. Set atelet to fail Terminate call + tc.fakeAtelet.FailTerminate = status.Error(codes.Internal, "simulated terminate failure") + + // 3. Force delete running actor (should succeed despite worker failure) + deleted, err := tc.client.DeleteActor(context.Background(), &ateapipb.DeleteActorRequest{ + Actor: &ateapipb.ObjectRef{Atespace: testAtespace, Name: "force-fail-actor"}, + Force: true, + }) + if err != nil { + t.Fatalf("DeleteActor with force failed: %v", err) + } + if deleted.GetMetadata().GetName() != "force-fail-actor" { + t.Errorf("deleted actor name = %q, want %q", deleted.GetMetadata().GetName(), "force-fail-actor") + } + + // Verify actor is removed from store + _, err = tc.client.GetActor(context.Background(), &ateapipb.GetActorRequest{ + Actor: &ateapipb.ObjectRef{Atespace: testAtespace, Name: "force-fail-actor"}, + }) + assertGrpcError(t, err, codes.NotFound, "Actor test-atespace/force-fail-actor not found") + + // Verify worker is unassigned + w, err := tc.persistence.GetWorker(context.Background(), ns, "pool1", "worker-1") + if err != nil { + t.Fatalf("GetWorker failed: %v", err) + } + if w.Assignment != nil { + t.Errorf("expected worker assignment to be nil, got: %v", w.Assignment) + } } func assertGrpcErrorRegex(t *testing.T, err error, wantCode codes.Code, wantMsg string) { diff --git a/cmd/ateapi/internal/controlapi/workflow_delete.go b/cmd/ateapi/internal/controlapi/workflow_delete.go index e93516c41..bf1e672f6 100644 --- a/cmd/ateapi/internal/controlapi/workflow_delete.go +++ b/cmd/ateapi/internal/controlapi/workflow_delete.go @@ -18,16 +18,20 @@ import ( "context" "errors" "fmt" + "log/slog" "github.com/agent-substrate/substrate/cmd/ateapi/internal/store" + "github.com/agent-substrate/substrate/internal/proto/ateletpb" "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" + k8serrors "k8s.io/apimachinery/pkg/api/errors" ) // DeleteActor executes the workflow to delete an actor. Idempotent. -func (w *ActorWorkflow) DeleteActor(ctx context.Context, actorRef resources.ActorRef) (*ateapipb.Actor, error) { +func (w *ActorWorkflow) DeleteActor(ctx context.Context, actorRef resources.ActorRef, force bool) (*ateapipb.Actor, error) { ctx, lock, err := w.acquireActorLock(ctx, actorRef) if err != nil { return nil, err @@ -38,12 +42,44 @@ func (w *ActorWorkflow) DeleteActor(ctx context.Context, actorRef resources.Acto if err != nil { return nil, err } + + actorTemplate := (*atev1alpha1.ActorTemplate)(nil) + if actor.GetActorTemplateNamespace() != "" && actor.GetActorTemplateName() != "" { + tmpl, err := w.actorTemplateLister.ActorTemplates(actor.GetActorTemplateNamespace()).Get(actor.GetActorTemplateName()) + if err != nil && !k8serrors.IsNotFound(err) { + return nil, fmt.Errorf("while fetching actor template: %w", err) + } + actorTemplate = tmpl + } + + if actor, err = w.ensureMarkedTerminating(ctx, actorRef, actor, force); err != nil { + return nil, err + } + + if err := w.ensureAteletTerminated(ctx, actorRef, actor, actorTemplate); err != nil { + if force { + slog.WarnContext(ctx, "ignoring atelet termination failure during force delete", "error", err) + } else { + return nil, err + } + } + + if err := w.ensureVolumesDetachedForDelete(ctx, actor, actorTemplate); err != nil { + return nil, err + } + + if actor, err = w.ensureWorkerReleased(ctx, actorRef, actor); err != nil { + return nil, err + } + if actor, err = w.ensureMarkedDeleting(ctx, actorRef, actor); err != nil { return nil, err } + if err := w.ensureVolumesDeleted(ctx, actor); err != nil { return nil, err } + return w.finalizeDeleted(ctx, actorRef) } @@ -62,10 +98,189 @@ func (w *ActorWorkflow) loadActorForDelete(ctx context.Context, actorRef resourc return actor, nil } -// ensureMarkedDeleting transitions the actor and its volumes to DELETING and -// persists the change, returning the stored copy. Skips when a previous -// attempt already marked the actor. -func (w *ActorWorkflow) ensureMarkedDeleting(ctx context.Context, actorRef resources.ActorRef, actor *ateapipb.Actor) (_ *ateapipb.Actor, err error) { +// ensureMarkedTerminating transitions the actor to TERMINATING status. +func (w *ActorWorkflow) ensureMarkedTerminating(ctx context.Context, actorRef resources.ActorRef, actor *ateapipb.Actor, force bool) (updated *ateapipb.Actor, err error) { + ctx, done := stepSpan(ctx, "MarkTerminating") + defer func() { err = done(err) }() + + st := actor.GetStatus() + if st == ateapipb.Actor_STATUS_TERMINATING || st == ateapipb.Actor_STATUS_DELETING { + markSkipped(ctx, "actor already TERMINATING or DELETING") + return actor, nil + } + + if force { + switch st { + case ateapipb.Actor_STATUS_RUNNING, + ateapipb.Actor_STATUS_RESUMING, + ateapipb.Actor_STATUS_SUSPENDING, + ateapipb.Actor_STATUS_PAUSING, + ateapipb.Actor_STATUS_PAUSED, + ateapipb.Actor_STATUS_CRASHED, + ateapipb.Actor_STATUS_SUSPENDED: + // allowed + default: + return nil, status.Errorf(codes.FailedPrecondition, "Actor %s is not in a deletable status (status: %v)", actorRef, st) + } + } else { + switch st { + case ateapipb.Actor_STATUS_SUSPENDED: + // allowed + default: + return nil, status.Errorf(codes.FailedPrecondition, "Actor %s is not in a deletable status (status: %v)", actorRef, st) + } + } + + updated, err = w.store.UpdateActor(ctx, actorRef, func(dbActor *ateapipb.Actor) error { + if err := store.CheckActorPrecondition(dbActor, actor.GetMetadata().GetUid(), actor.GetMetadata().GetVersion()); err != nil { + return err + } + dbActor.Status = ateapipb.Actor_STATUS_TERMINATING + return nil + }) + if err != nil { + if errors.Is(err, store.ErrVersionConflict) { + return nil, status.Error(codes.Aborted, "concurrent update conflict, please retry") + } + return nil, fmt.Errorf("while setting actor status to TERMINATING: %w", err) + } + return updated, nil +} + +// ensureAteletTerminated calls atelet to terminate the workload. +func (w *ActorWorkflow) ensureAteletTerminated(ctx context.Context, actorRef resources.ActorRef, actor *ateapipb.Actor, actorTemplate *atev1alpha1.ActorTemplate) (err error) { + ctx, done := stepSpan(ctx, "CallAteletTerminate") + defer func() { err = done(err) }() + + if actor.GetStatus() == ateapipb.Actor_STATUS_DELETING { + markSkipped(ctx, "actor already DELETING") + return nil + } + + st := actor.GetStatus() + if st != ateapipb.Actor_STATUS_TERMINATING { + return status.Errorf(codes.FailedPrecondition, "CallAteletTerminate prerequisite not met for Actor: %s (got: %v, want %s)", actorRef, st, ateapipb.Actor_STATUS_TERMINATING) + } + if actorTemplate == nil { + return status.Errorf(codes.FailedPrecondition, "actor template %s/%s not found for actor %s", actor.GetActorTemplateNamespace(), actor.GetActorTemplateName(), actorRef) + } + + assignment := actor.GetWorkerAssignment() + if assignment == nil { + slog.InfoContext(ctx, "actor has no worker assignment, skipping CallAteletTerminateStep", slog.Any("actor", actorRef)) + return nil + } + + workerPodNs := assignment.GetWorkerNamespace() + workerPodName := assignment.GetWorkerPod() + + conn, err := w.dialer.DialForWorker(workerPodNs, workerPodName) + if err != nil { + if errors.Is(err, ErrWorkerPodNotFound) { + return status.Errorf(codes.NotFound, "worker pod %s/%s not found: %v", workerPodNs, workerPodName, err) + } + return fmt.Errorf("while connecting to worker pod %s/%s: %w", workerPodNs, workerPodName, err) + } + + client := ateletpb.NewAteomHerderClient(conn) + + workloadSpec, err := workloadSpecFromActorTemplate(actorTemplate, actor) + if err != nil { + return err + } + + req := &ateletpb.TerminateRequest{ + TargetAteomUid: assignment.GetWorkerPodUid(), + Atespace: actor.GetMetadata().GetAtespace(), + ActorName: actor.GetMetadata().GetName(), + ActorUid: actor.GetMetadata().GetUid(), + ActorTemplateNamespace: actor.GetActorTemplateNamespace(), + ActorTemplateName: actor.GetActorTemplateName(), + Spec: workloadSpec, + } + + if _, err := client.Terminate(ctx, req); err != nil { + return fmt.Errorf("while terminating actor on atelet: %w", err) + } + + return nil +} + +// ensureVolumesDetachedForDelete detaches external volumes. +func (w *ActorWorkflow) ensureVolumesDetachedForDelete(ctx context.Context, actor *ateapipb.Actor, actorTemplate *atev1alpha1.ActorTemplate) (err error) { + ctx, done := stepSpan(ctx, "DetachVolumesForDelete") + defer func() { err = done(err) }() + + if actor.GetStatus() == ateapipb.Actor_STATUS_DELETING { + markSkipped(ctx, "actor already DELETING") + return nil + } + + st := actor.GetStatus() + if st != ateapipb.Actor_STATUS_TERMINATING { + return status.Errorf(codes.FailedPrecondition, "DetachVolumesForDelete prerequisite not met for Actor: %s (got: %v, want %s)", actor.GetMetadata().GetName(), st, ateapipb.Actor_STATUS_TERMINATING) + } + if actorTemplate == nil { + return status.Errorf(codes.FailedPrecondition, "actor template %s/%s not found for actor %s", actor.GetActorTemplateNamespace(), actor.GetActorTemplateName(), actor.GetMetadata().GetName()) + } + + return detachActorVolumes(ctx, w.store, w.pluginRegistry, actor, actorTemplate, "delete") +} + +// ensureWorkerReleased releases the worker assigned to the actor. +func (w *ActorWorkflow) ensureWorkerReleased(ctx context.Context, actorRef resources.ActorRef, actor *ateapipb.Actor) (updated *ateapipb.Actor, err error) { + ctx, done := stepSpan(ctx, "ReleaseWorker") + defer func() { err = done(err) }() + + if actor.GetStatus() == ateapipb.Actor_STATUS_DELETING { + markSkipped(ctx, "actor already DELETING") + return actor, nil + } + if actor.GetWorkerAssignment() == nil { + markSkipped(ctx, "worker already released") + return actor, nil + } + + st := actor.GetStatus() + if st != ateapipb.Actor_STATUS_TERMINATING { + return nil, status.Errorf(codes.FailedPrecondition, "ReleaseWorker prerequisite not met for Actor: %s (got: %v, want %s)", actorRef, st, ateapipb.Actor_STATUS_TERMINATING) + } + + latestActor, err := w.store.GetActor(ctx, actorRef) + if err != nil { + return nil, err + } + + if latestActor.GetWorkerAssignment() != nil { + if _, err := releaseWorker(ctx, w.store, latestActor); err != nil { + return nil, err + } + + latestActor, err = w.store.GetActor(ctx, actorRef) + if err != nil { + return nil, err + } + + updatedActor, err := w.store.UpdateActor(ctx, actorRef, func(dbActor *ateapipb.Actor) error { + if err := store.CheckActorPrecondition(dbActor, latestActor.GetMetadata().GetUid(), latestActor.GetMetadata().GetVersion()); err != nil { + return err + } + dbActor.LocalSnapshotInfo = nil + return nil + }) + if err != nil { + if errors.Is(err, store.ErrVersionConflict) { + return nil, status.Error(codes.Aborted, "concurrent update conflict, please retry") + } + return nil, err + } + latestActor = updatedActor + } + return latestActor, nil +} + +// ensureMarkedDeleting transitions the actor to DELETING status. +func (w *ActorWorkflow) ensureMarkedDeleting(ctx context.Context, actorRef resources.ActorRef, actor *ateapipb.Actor) (updated *ateapipb.Actor, err error) { ctx, done := stepSpan(ctx, "MarkDeleting") defer func() { err = done(err) }() @@ -73,12 +288,12 @@ func (w *ActorWorkflow) ensureMarkedDeleting(ctx context.Context, actorRef resou markSkipped(ctx, "actor already DELETING") return actor, nil } - if actor.GetStatus() != ateapipb.Actor_STATUS_SUSPENDED && - actor.GetStatus() != ateapipb.Actor_STATUS_CRASHED { - return nil, status.Errorf(codes.FailedPrecondition, "Actor %s is not in a deletable status (status: %v)", actorRef, actor.GetStatus()) + st := actor.GetStatus() + if st != ateapipb.Actor_STATUS_TERMINATING { + return nil, status.Errorf(codes.FailedPrecondition, "MarkDeleting prerequisite not met for Actor: %s (got: %v, want %s)", actorRef, st, ateapipb.Actor_STATUS_TERMINATING) } - updated, err := w.store.UpdateActor(ctx, actorRef, func(dbActor *ateapipb.Actor) error { + updated, err = w.store.UpdateActor(ctx, actorRef, func(dbActor *ateapipb.Actor) error { if err := store.CheckActorPrecondition(dbActor, actor.GetMetadata().GetUid(), actor.GetMetadata().GetVersion()); err != nil { return err } @@ -97,25 +312,27 @@ func (w *ActorWorkflow) ensureMarkedDeleting(ctx context.Context, actorRef resou return updated, nil } -// ensureVolumesDeleted removes the actor's external volumes. Volume deletion -// is idempotent, so a re-entered workflow can safely run it again. +// ensureVolumesDeleted deletes external volumes. func (w *ActorWorkflow) ensureVolumesDeleted(ctx context.Context, actor *ateapipb.Actor) (err error) { ctx, done := stepSpan(ctx, "DeleteVolumes") defer func() { err = done(err) }() + if actor.GetStatus() != ateapipb.Actor_STATUS_DELETING { + return status.Errorf(codes.FailedPrecondition, "DeleteVolumes prerequisite not met for Actor: %s (got: %v, want %s)", actor.GetMetadata().GetName(), actor.GetStatus(), ateapipb.Actor_STATUS_DELETING) + } + if err := deleteActorVolumes(ctx, w.pluginRegistry, actor.GetMetadata().GetUid(), actor.GetActorVolumes()); err != nil { return status.Errorf(codes.Internal, "while deleting actor volumes: %v", err) } return nil } -// finalizeDeleted removes the actor from the store and returns the deleted -// record. The store enforces that only a DELETING actor can be removed. -func (w *ActorWorkflow) finalizeDeleted(ctx context.Context, actorRef resources.ActorRef) (_ *ateapipb.Actor, err error) { +// finalizeDeleted removes the actor from the store. +func (w *ActorWorkflow) finalizeDeleted(ctx context.Context, actorRef resources.ActorRef) (deleted *ateapipb.Actor, err error) { ctx, done := stepSpan(ctx, "FinalizeDeleted") defer func() { err = done(err) }() - deleted, err := w.store.DeleteActor(ctx, actorRef) + deleted, err = w.store.DeleteActor(ctx, actorRef) if err != nil { if errors.Is(err, store.ErrNotFound) { return nil, status.Errorf(codes.NotFound, "Actor %s not found", actorRef) diff --git a/cmd/ateapi/internal/controlapi/workflow_delete_test.go b/cmd/ateapi/internal/controlapi/workflow_delete_test.go index 88a997f4e..655109ec1 100644 --- a/cmd/ateapi/internal/controlapi/workflow_delete_test.go +++ b/cmd/ateapi/internal/controlapi/workflow_delete_test.go @@ -29,36 +29,67 @@ func TestDeleteActorWorkflow_ExecutionPaths(t *testing.T) { tests := []struct { name string seedStatus ateapipb.Actor_Status + force bool wantErr bool wantCode codes.Code }{ { name: "delete suspended actor succeeds", seedStatus: ateapipb.Actor_STATUS_SUSPENDED, + force: false, wantErr: false, }, { - name: "delete crashed actor succeeds", + name: "delete crashed actor rejected when not forced", seedStatus: ateapipb.Actor_STATUS_CRASHED, - wantErr: false, + force: false, + wantErr: true, + wantCode: codes.FailedPrecondition, }, { name: "delete deleting actor succeeds", seedStatus: ateapipb.Actor_STATUS_DELETING, + force: false, wantErr: false, }, { - name: "delete running actor rejected", + name: "delete running actor rejected when not forced", seedStatus: ateapipb.Actor_STATUS_RUNNING, + force: false, wantErr: true, wantCode: codes.FailedPrecondition, }, { - name: "delete paused actor rejected", + name: "delete paused actor rejected when not forced", seedStatus: ateapipb.Actor_STATUS_PAUSED, + force: false, wantErr: true, wantCode: codes.FailedPrecondition, }, + { + name: "force delete suspended actor succeeds", + seedStatus: ateapipb.Actor_STATUS_SUSPENDED, + force: true, + wantErr: false, + }, + { + name: "force delete running actor succeeds", + seedStatus: ateapipb.Actor_STATUS_RUNNING, + force: true, + wantErr: false, + }, + { + name: "force delete paused actor succeeds", + seedStatus: ateapipb.Actor_STATUS_PAUSED, + force: true, + wantErr: false, + }, + { + name: "force delete crashed actor succeeds", + seedStatus: ateapipb.Actor_STATUS_CRASHED, + force: true, + wantErr: false, + }, } for _, tc := range tests { @@ -71,7 +102,7 @@ func TestDeleteActorWorkflow_ExecutionPaths(t *testing.T) { actorRef := resources.ActorRef{Atespace: "team-a", Name: "id1"} seedWorkflowActor(t, ctx, st, actorRef, "ns", "tmpl1", tc.seedStatus) - deleted, err := w.DeleteActor(ctx, actorRef) + deleted, err := w.DeleteActor(ctx, actorRef, tc.force) if tc.wantErr { if got := status.Code(err); got != tc.wantCode { t.Fatalf("status.Code(err) = %v, want %v (err: %v)", got, tc.wantCode, err) @@ -91,11 +122,69 @@ func TestDeleteActorWorkflow_ExecutionPaths(t *testing.T) { } } +func TestEnsureMarkedTerminating_StatusMatrix(t *testing.T) { + tests := []struct { + name string + force bool + allowed map[ateapipb.Actor_Status]bool + }{ + { + name: "standard delete", + force: false, + allowed: map[ateapipb.Actor_Status]bool{ + ateapipb.Actor_STATUS_SUSPENDED: true, + ateapipb.Actor_STATUS_TERMINATING: true, // skipped + ateapipb.Actor_STATUS_DELETING: true, // skipped + }, + }, + { + name: "force delete", + force: true, + allowed: map[ateapipb.Actor_Status]bool{ + ateapipb.Actor_STATUS_RUNNING: true, + ateapipb.Actor_STATUS_RESUMING: true, + ateapipb.Actor_STATUS_SUSPENDING: true, + ateapipb.Actor_STATUS_PAUSING: true, + ateapipb.Actor_STATUS_PAUSED: true, + ateapipb.Actor_STATUS_CRASHED: true, + ateapipb.Actor_STATUS_TERMINATING: true, // skipped + ateapipb.Actor_STATUS_DELETING: true, // skipped + ateapipb.Actor_STATUS_SUSPENDED: true, + }, + }, + } + + for _, tc := range tests { + t.Run(tc.name, func(t *testing.T) { + for _, seedStatus := range allActorStatuses { + ctx := context.Background() + st, cleanup := storetest.SetupTestStore(t) + w := newTestActorWorkflow(t, st, "ns", "tmpl1") + + actorRef := resources.ActorRef{Atespace: "team-a", Name: "id1"} + seedWorkflowActor(t, ctx, st, actorRef, "ns", "tmpl1", seedStatus) + actor, err := st.GetActor(ctx, actorRef) + if err != nil { + t.Fatalf("status %v: get seeded actor: %v", seedStatus, err) + } + + updated, err := w.ensureMarkedTerminating(ctx, actorRef, actor, tc.force) + assertPrerequisiteResult(t, seedStatus, err, tc.allowed[seedStatus]) + if err == nil && seedStatus != ateapipb.Actor_STATUS_TERMINATING && seedStatus != ateapipb.Actor_STATUS_DELETING { + if updated.GetStatus() != ateapipb.Actor_STATUS_TERMINATING { + t.Errorf("status %v: ensureMarkedTerminating returned actor in %v, want TERMINATING", seedStatus, updated.GetStatus()) + } + } + cleanup() + } + }) + } +} + func TestEnsureMarkedDeleting_StatusMatrix(t *testing.T) { allowed := map[ateapipb.Actor_Status]bool{ - ateapipb.Actor_STATUS_SUSPENDED: true, - ateapipb.Actor_STATUS_CRASHED: true, - ateapipb.Actor_STATUS_DELETING: true, // skipped, not re-marked + ateapipb.Actor_STATUS_TERMINATING: true, + ateapipb.Actor_STATUS_DELETING: true, // skipped } for _, seedStatus := range allActorStatuses { @@ -112,8 +201,10 @@ func TestEnsureMarkedDeleting_StatusMatrix(t *testing.T) { updated, err := w.ensureMarkedDeleting(ctx, actorRef, actor) assertPrerequisiteResult(t, seedStatus, err, allowed[seedStatus]) - if err == nil && updated.GetStatus() != ateapipb.Actor_STATUS_DELETING { - t.Errorf("status %v: ensureMarkedDeleting returned actor in %v, want DELETING", seedStatus, updated.GetStatus()) + if err == nil && seedStatus != ateapipb.Actor_STATUS_DELETING { + if updated.GetStatus() != ateapipb.Actor_STATUS_DELETING { + t.Errorf("status %v: ensureMarkedDeleting returned actor in %v, want DELETING", seedStatus, updated.GetStatus()) + } } cleanup() } diff --git a/cmd/ateapi/internal/controlapi/workflow_suspend.go b/cmd/ateapi/internal/controlapi/workflow_suspend.go index 14258aded..43dddbdcd 100644 --- a/cmd/ateapi/internal/controlapi/workflow_suspend.go +++ b/cmd/ateapi/internal/controlapi/workflow_suspend.go @@ -345,29 +345,10 @@ func (w *ActorWorkflow) ensureSuspendedFinalized(ctx context.Context, actorRef r return nil, err } - // 1. Free the worker (if the actor has one and it hasn't been freed yet) - if assignment := latestActor.GetWorkerAssignment(); assignment != nil { - workerPod := assignment.GetWorkerPod() - - worker, err := w.store.GetWorker(ctx, assignment.GetWorkerNamespace(), assignment.GetWorkerPool(), workerPod) - if err != nil { - if !errors.Is(err, store.ErrNotFound) { - return nil, fmt.Errorf("while getting worker for release: %w", err) - } - slog.WarnContext(ctx, "Worker already gone during finalize suspend, skipping release", "worker", workerPod) - } else { - // Only free it if it still belongs to us - if wass := worker.Assignment; wass != nil { - if wass.GetActorUid() == latestActor.GetMetadata().GetUid() { - worker.Assignment = nil - if err := w.store.UpdateWorker(ctx, worker, worker.Version); err != nil { - if errors.Is(err, store.ErrVersionConflict) { - return nil, status.Error(codes.Aborted, "concurrent update conflict, please retry") - } - return nil, err - } - } - } + // 1. Free the worker (if it hasn't been freed yet) + if latestActor.GetWorkerAssignment() != nil { + if _, err := releaseWorker(ctx, w.store, latestActor); err != nil { + return nil, err } // Re-fetch the actor now that the worker is freed. diff --git a/cmd/atelet/main.go b/cmd/atelet/main.go index c59deadd6..34e876d5b 100644 --- a/cmd/atelet/main.go +++ b/cmd/atelet/main.go @@ -1111,6 +1111,45 @@ func (s *AteomHerder) Restore(ctx context.Context, req *ateletpb.RestoreRequest) return &ateletpb.RestoreResponse{}, nil } +// Terminate terminates any running workload on ateom, unmounts external volumes, +// and resets actor directories on the node. +func (s *AteomHerder) Terminate(ctx context.Context, req *ateletpb.TerminateRequest) (*ateletpb.TerminateResponse, error) { + if err := validateTerminateRequest(req); err != nil { + return nil, status.Errorf(codes.InvalidArgument, "%v", err) + } + + actorRef := resources.ActorRef{Atespace: req.GetAtespace(), Name: req.GetActorName()} + actorUID := req.GetActorUid() + + client, err := s.dialAteom(ctx, req.GetTargetAteomUid()) + if err != nil { + // TODO: handle case where the target ateom is no longer available + return nil, fmt.Errorf("failed to dial ateom for terminate (actor: %s, actorUID: %s): %w", actorRef, actorUID, err) + } + if _, err := client.TerminateWorkload(ctx, &ateompb.TerminateWorkloadRequest{ + Atespace: req.GetAtespace(), + ActorName: req.GetActorName(), + ActorUid: req.GetActorUid(), + ActorTemplateNamespace: req.GetActorTemplateNamespace(), + ActorTemplateName: req.GetActorTemplateName(), + Spec: buildAteomWorkloadSpec(req.GetSpec()), + }); err != nil { + return nil, fmt.Errorf("failed calling ateom.TerminateWorkload (actor: %s, actorUID: %s): %w", actorRef, actorUID, err) + } + + // Unmount external volumes + if err := s.unmountExternalVolumes(ctx, actorUID, req.GetSpec().GetVolumes()); err != nil { + return nil, fmt.Errorf("failed to unmount external volumes during terminate (actor: %s, actorUID: %s): %w", actorRef, actorUID, err) + } + + // Reset actor directories on the node + if err := resetActorDirs(actorUID); err != nil { + return nil, fmt.Errorf("failed to reset actor directories during terminate (actor: %s, actorUID: %s): %w", actorRef, actorUID, err) + } + + return &ateletpb.TerminateResponse{}, nil +} + func (s *AteomHerder) copyLocalCheckpoint(ctx context.Context, snapshotName string, srcDir, dstDir string, files []string) error { for _, fileName := range files { if ctx.Err() != nil { @@ -1677,6 +1716,30 @@ func validateRestoreRequest(req *ateletpb.RestoreRequest) error { return nil } +func validateTerminateRequest(req *ateletpb.TerminateRequest) error { + var errs field.ErrorList + errs = append(errs, resources.ValidateResourceName(req.GetAtespace(), field.NewPath("atespace"))...) + errs = append(errs, resources.ValidateResourceName(req.GetActorName(), field.NewPath("actor_name"))...) + errs = append(errs, resources.ValidateResourceName(req.GetActorUid(), field.NewPath("actor_uid"))...) + for _, msg := range content.IsDNS1123Label(req.GetActorTemplateNamespace()) { + errs = append(errs, field.Invalid(field.NewPath("actor_template_namespace"), req.GetActorTemplateNamespace(), msg)) + } + for _, msg := range content.IsDNS1123Subdomain(req.GetActorTemplateName()) { + errs = append(errs, field.Invalid(field.NewPath("actor_template_name"), req.GetActorTemplateName(), msg)) + } + if len(errs) > 0 { + return errs.ToAggregate() + } + if err := resources.ValidateAteomUID(req.GetTargetAteomUid()); err != nil { + return err + } + names := make([]string, 0, len(req.GetSpec().GetContainers())) + for _, ctr := range req.GetSpec().GetContainers() { + names = append(names, ctr.GetName()) + } + return resources.ValidateContainerNames(names) +} + func validateSnapshotScope(scope ateletpb.SnapshotScope) error { switch scope { case ateletpb.SnapshotScope_SNAPSHOT_SCOPE_FULL, diff --git a/cmd/atelet/main_test.go b/cmd/atelet/main_test.go index b08e203d6..219b96451 100644 --- a/cmd/atelet/main_test.go +++ b/cmd/atelet/main_test.go @@ -649,6 +649,41 @@ func TestRPCBoundariesReject(t *testing.T) { }) wantInvalidArgument(t, "Restore", err) }) + t.Run("Terminate", func(t *testing.T) { + const okTargetAteomUID = "123e4567-e89b-12d3-a456-426614174001" + t.Run("invalid ateom UID", func(t *testing.T) { + _, err := s.Terminate(ctx, &ateletpb.TerminateRequest{ + Atespace: okAtespace, ActorName: okID, + ActorUid: okActorUID, ActorTemplateNamespace: "default", ActorTemplateName: "template", + TargetAteomUid: badUID, Spec: okSpec, + }) + wantInvalidArgument(t, "Terminate", err) + }) + t.Run("missing template namespace", func(t *testing.T) { + _, err := s.Terminate(ctx, &ateletpb.TerminateRequest{ + Atespace: okAtespace, ActorName: okID, + ActorUid: okActorUID, ActorTemplateName: "template", + TargetAteomUid: okTargetAteomUID, Spec: okSpec, + }) + wantInvalidArgument(t, "Terminate", err) + }) + t.Run("missing template name", func(t *testing.T) { + _, err := s.Terminate(ctx, &ateletpb.TerminateRequest{ + Atespace: okAtespace, ActorName: okID, + ActorUid: okActorUID, ActorTemplateNamespace: "default", + TargetAteomUid: okTargetAteomUID, Spec: okSpec, + }) + wantInvalidArgument(t, "Terminate", err) + }) + t.Run("missing target ateom UID", func(t *testing.T) { + _, err := s.Terminate(ctx, &ateletpb.TerminateRequest{ + Atespace: okAtespace, ActorName: okID, + ActorUid: okActorUID, ActorTemplateNamespace: "default", ActorTemplateName: "template", + Spec: okSpec, + }) + wantInvalidArgument(t, "Terminate", err) + }) + }) } func TestBuildAteomWorkloadSpecForwardsReadyz(t *testing.T) { diff --git a/cmd/ateom-gvisor/main.go b/cmd/ateom-gvisor/main.go index d69f2717b..e63a20f14 100644 --- a/cmd/ateom-gvisor/main.go +++ b/cmd/ateom-gvisor/main.go @@ -486,26 +486,7 @@ func (s *AteomService) CheckpointWorkload(ctx context.Context, req *ateompb.Chec // control server for state/delete calls. Keep this as best-effort cleanup: // atelet resets the actor runsc, bundle, pidfile, and checkpoint // directories after uploading the snapshot. - if err := rcmd.cleanupContainersAfterCheckpoint(ctx, req.GetSpec().GetContainers()); err != nil { - slog.WarnContext(ctx, "Failed to clean up runsc containers after checkpoint", - "actor", actorRef, - "actorUID", req.GetActorUid(), - "err", err) - } - - // Detach the overlay rootfs mounts before atelet wipes the bundle dirs - // (deleting a bundle out from under a live mount in this namespace would - // leave the mount orphaned until the pod restarts). Best-effort, same as - // the container cleanup above. - if err := imagecache.UnmountAllUnder(ateompath.OCIBundleDir(req.GetActorUid())); err != nil { - slog.WarnContext(ctx, "Failed to unmount bundle rootfs overlays after checkpoint", - "actorUID", req.GetActorUid(), - "err", err) - } - - if err := ateomnet.CleanupActorNetwork(ctx, s.interiorNetNS); err != nil { - slog.WarnContext(ctx, "Failed to clean up actor network after checkpoint", slog.Any("err", err)) - } + s.terminateWorkload(ctx, actorRef, req.GetActorUid(), req.GetRunscPath(), req.GetSpec().GetContainers()) // Report exactly the files runsc wrote so atelet ships precisely this set // (checkpoint.img plus any pages images), rather than a hardcoded list. @@ -747,6 +728,48 @@ func (s *AteomService) prepareActorEgress(ctx context.Context, actorUID string, return &actorEgress{client: gatewayClient, certificateSource: certificateSource, expiresAt: expiresAt}, nil } +func (s *AteomService) TerminateWorkload(ctx context.Context, req *ateompb.TerminateWorkloadRequest) (*ateompb.TerminateWorkloadResponse, error) { + s.lock.Lock() + defer s.lock.Unlock() + + actorRef := resources.ActorRef{Atespace: req.GetAtespace(), Name: req.GetActorName()} + + // TODO: consider if this should be a hard failure for the terminate case + s.terminateWorkload(ctx, actorRef, req.GetActorUid(), req.GetRunscPath(), req.GetSpec().GetContainers()) + + s.actorLogger.EmitLifecycleLog("Actor terminated", actorRef, req.GetActorUid(), req.GetActorTemplateNamespace(), req.GetActorTemplateName()) + + return &ateompb.TerminateWorkloadResponse{}, nil +} + +func (s *AteomService) terminateWorkload(ctx context.Context, actorRef resources.ActorRef, actorUID, runscPath string, containers []*ateompb.Container) { + if err := s.deactivateActorNetworking(ctx); err != nil { + slog.WarnContext(ctx, "Failed to deactivate actor networking during terminate", slog.Any("err", err)) + } + + rcmd := &runsc{ + path: runscPath, + actorUID: actorUID, + } + + if err := rcmd.cleanupContainersAfterCheckpoint(ctx, containers); err != nil { + slog.WarnContext(ctx, "Failed to clean up runsc containers during terminate", + "actor", actorRef, + "actorUID", actorUID, + "err", err) + } + + if err := imagecache.UnmountAllUnder(ateompath.OCIBundleDir(actorUID)); err != nil { + slog.WarnContext(ctx, "Failed to unmount bundle rootfs overlays during terminate", + "actorUID", actorUID, + "err", err) + } + + if err := ateomnet.CleanupActorNetwork(ctx, s.interiorNetNS); err != nil { + slog.WarnContext(ctx, "Failed to clean up actor network during terminate", slog.Any("err", err)) + } +} + func (s *AteomService) activateActorNetworking(atespace, actorName string, egress *actorEgress) error { if err := s.atunnelIngress.Activate(atespace, actorName); err != nil { return fmt.Errorf("while activating actor ingress: %w", err) diff --git a/cmd/ateom-microvm/checkpoint.go b/cmd/ateom-microvm/checkpoint.go index f55dfe2f9..7013a2236 100644 --- a/cmd/ateom-microvm/checkpoint.go +++ b/cmd/ateom-microvm/checkpoint.go @@ -154,7 +154,7 @@ func (s *AteomService) CheckpointWorkload(ctx context.Context, req *ateompb.Chec // Tear down: the actor returns to "available". Best-effort; the snapshot is // already on disk for atelet to ship. tTeardown := time.Now() - s.teardownActor(ctx, actorUID, ra, client) + s.terminateWorkload(ctx, actorUID) dTeardown := time.Since(tTeardown) delete(s.running, actorUID) @@ -303,3 +303,39 @@ func (s *AteomService) teardownActor(ctx context.Context, id string, ra *running slog.WarnContext(ctx, "Failed to unmount bundle rootfs overlays", slog.String("actorUID", id), slog.Any("err", err)) } } + +// TerminateWorkload stops the running actor, tears down its VMM, and cleans up +// networking and overlays. +func (s *AteomService) TerminateWorkload(ctx context.Context, req *ateompb.TerminateWorkloadRequest) (*ateompb.TerminateWorkloadResponse, error) { + s.lock.Lock() + defer s.lock.Unlock() + + actorRef := resources.ActorRef{Atespace: req.GetAtespace(), Name: req.GetActorName()} + actorUID := req.GetActorUid() + + s.terminateWorkload(ctx, actorUID) + + s.actorLogger.EmitLifecycleLog("Actor terminated", actorRef, actorUID, req.GetActorTemplateNamespace(), req.GetActorTemplateName()) + + return &ateompb.TerminateWorkloadResponse{}, nil +} + +func (s *AteomService) terminateWorkload(ctx context.Context, actorUID string) { + if err := s.deactivateActorNetworking(ctx); err != nil { + slog.WarnContext(ctx, "Failed to deactivate actor networking during terminate", slog.Any("err", err)) + } + + ra := s.running[actorUID] + chSocket := kata.CLHSocketPath(actorUID) + if ra != nil && ra.apiSocket != "" { + chSocket = ra.apiSocket + } + client := ch.NewClient(chSocket) + + s.teardownActor(ctx, actorUID, ra, client) + delete(s.running, actorUID) + + if err := ateomnet.CleanupActorNetwork(ctx, s.interiorNetNS); err != nil { + slog.WarnContext(ctx, "Failed to clean up actor network during terminate", slog.Any("err", err)) + } +} diff --git a/cmd/kubectl-ate/internal/cmd/delete_actor.go b/cmd/kubectl-ate/internal/cmd/delete_actor.go index 2f863d535..ca570d827 100644 --- a/cmd/kubectl-ate/internal/cmd/delete_actor.go +++ b/cmd/kubectl-ate/internal/cmd/delete_actor.go @@ -24,6 +24,7 @@ import ( ) var deleteAtespaceFlag string +var deleteActorForceFlag bool var deleteActorCmd = &cobra.Command{ Use: "actor ", @@ -40,6 +41,7 @@ var deleteActorCmd = &cobra.Command{ actorRef := resources.ActorRef{Atespace: deleteAtespaceFlag, Name: args[0]} _, err = c.ControlClient.DeleteActor(ctx, &ateapipb.DeleteActorRequest{ Actor: actorRef.ToObjectRef(), + Force: deleteActorForceFlag, }) if err != nil { return err @@ -53,5 +55,6 @@ var deleteActorCmd = &cobra.Command{ func init() { deleteActorCmd.Flags().StringVarP(&deleteAtespaceFlag, "atespace", "a", "", "Atespace the actor lives in") _ = deleteActorCmd.MarkFlagRequired("atespace") + deleteActorCmd.Flags().BoolVar(&deleteActorForceFlag, "force", false, "Force delete the actor even if running") deleteCmd.AddCommand(deleteActorCmd) } diff --git a/demos/claude-code-multiplex/ui/server.go b/demos/claude-code-multiplex/ui/server.go index 0df15b8f5..ffb3b30e7 100644 --- a/demos/claude-code-multiplex/ui/server.go +++ b/demos/claude-code-multiplex/ui/server.go @@ -214,6 +214,10 @@ func actorStatusString(s ateapipb.Actor_Status) string { return "Suspending" case ateapipb.Actor_STATUS_SUSPENDED: return "Suspended" + case ateapipb.Actor_STATUS_TERMINATING: + return "Terminating" + case ateapipb.Actor_STATUS_DELETING: + return "Deleting" default: return "?" } diff --git a/internal/e2e/suites/demo/demo_test.go b/internal/e2e/suites/demo/demo_test.go index 8d1e1ad68..eeaacd48e 100644 --- a/internal/e2e/suites/demo/demo_test.go +++ b/internal/e2e/suites/demo/demo_test.go @@ -29,6 +29,8 @@ import ( "github.com/agent-substrate/substrate/internal/resources" "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/types/known/fieldmaskpb" "k8s.io/apimachinery/pkg/api/resource" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" @@ -71,6 +73,14 @@ func TestActorLifecycle(t *testing.T) { name: "SuspendResumeActor", f: suspendActor, }, + { + name: "ForceDeleteActor", + f: forceDeleteActor, + }, + { + name: "ForceDeletePausedActor", + f: forceDeletePausedActor, + }, } for _, tc := range tests { @@ -428,6 +438,77 @@ func TestExternalVolumeLifecycle(t *testing.T) { } } +func TestForceDeleteActorWithExternalVolume(t *testing.T) { + if isMicroVMEnvironment() { + t.Skip("Skipping TestForceDeleteActorWithExternalVolume for microVM environment") + } + + ctx := context.Background() + clients := e2e.GetClients() + nsObj := e2e.CreateNamespace(t) + + _, _ = clients.SubstrateAPI.CreateAtespace(ctx, &ateapipb.CreateAtespaceRequest{ + Atespace: &ateapipb.Atespace{Metadata: &ateapipb.ResourceMetadata{Name: demoAtespace}}, + }) + + at, err := createActorTemplateWithExternalVolume(ctx, t, clients, nsObj, v1alpha1.SnapshotScopeData, v1alpha1.SnapshotScopeData, v1alpha1.ResumeSourceColdBoot) + if err != nil { + t.Fatalf("failed to initialize ActorTemplate: %v", err) + } + + actorName := "force-delete-extvol-" + nsObj.Name + + t.Logf("Creating Actor %q...", actorName) + if _, err := clients.SubstrateAPI.CreateActor(ctx, &ateapipb.CreateActorRequest{Actor: &ateapipb.Actor{ + Metadata: &ateapipb.ResourceMetadata{Atespace: demoAtespace, Name: actorName}, + ActorTemplateNamespace: nsObj.Name, + ActorTemplateName: at.Name, + }}); err != nil { + t.Fatalf("failed to create Actor: %v", err) + } + waitForActorStatus(ctx, t, clients, actorName, ateapipb.Actor_STATUS_SUSPENDED) + + t.Logf("Resuming Actor %q...", actorName) + if _, err := clients.SubstrateAPI.ResumeActor(ctx, &ateapipb.ResumeActorRequest{ + Actor: &ateapipb.ObjectRef{Atespace: demoAtespace, Name: actorName}, + }); err != nil { + t.Fatalf("failed to resume Actor: %v", err) + } + waitForActorStatus(ctx, t, clients, actorName, ateapipb.Actor_STATUS_RUNNING) + + // Verify volume exists in actor (status should be CREATED) + actor, err := clients.SubstrateAPI.GetActor(ctx, &ateapipb.GetActorRequest{ + Actor: &ateapipb.ObjectRef{Atespace: demoAtespace, Name: actorName}, + }) + if err != nil { + t.Fatalf("failed to get actor: %v", err) + } + if len(actor.GetActorVolumes()) == 0 { + t.Fatalf("expected actor to have volumes, got 0") + } + for _, vol := range actor.GetActorVolumes() { + if vol.Status != ateapipb.ExternalVolume_STATUS_CREATED { + t.Fatalf("expected volume %q to be CREATED, got %s", vol.VolumeName, vol.Status) + } + } + + // Force delete the running actor + t.Logf("Force deleting running Actor %q...", actorName) + if _, err := clients.SubstrateAPI.DeleteActor(ctx, &ateapipb.DeleteActorRequest{ + Actor: &ateapipb.ObjectRef{Atespace: demoAtespace, Name: actorName}, + Force: true, + }); err != nil { + t.Fatalf("failed to force delete Actor: %v", err) + } + + // Verify deletion in store + if _, err := clients.SubstrateAPI.GetActor(ctx, &ateapipb.GetActorRequest{ + Actor: &ateapipb.ObjectRef{Atespace: demoAtespace, Name: actorName}, + }); status.Code(err) != codes.NotFound { + t.Fatalf("expected actor %q to be NotFound after force delete, got err: %v", actorName, err) + } +} + // Verify that file and memory counters behavior after pause and suspend, for different snapshot scopes. // Test case: // 1. Create actor. @@ -870,6 +951,150 @@ func suspendActor(ctx context.Context, t *testing.T, clients *e2e.Clients, nsObj return nil } +func forceDeleteActor(ctx context.Context, t *testing.T, clients *e2e.Clients, nsObj *e2e.Namespace, at *v1alpha1.ActorTemplate) error { + actorName := "force-delete-actor-" + nsObj.Name + + // 1. Creating an actor + t.Logf("Creating Actor %q...", actorName) + if _, err := clients.SubstrateAPI.CreateActor(ctx, &ateapipb.CreateActorRequest{Actor: &ateapipb.Actor{ + Metadata: &ateapipb.ResourceMetadata{Atespace: demoAtespace, Name: actorName}, + ActorTemplateNamespace: nsObj.Name, + ActorTemplateName: at.Name, + }}); err != nil { + t.Fatalf("failed to create Actor: %v", err) + } + waitForActorStatus(ctx, t, clients, actorName, ateapipb.Actor_STATUS_SUSPENDED) + + // Verify that force delete on a SUSPENDED actor succeeds + t.Logf("Attempting force delete on suspended Actor %q (should succeed)...", actorName) + if _, err := clients.SubstrateAPI.DeleteActor(ctx, &ateapipb.DeleteActorRequest{ + Actor: &ateapipb.ObjectRef{Atespace: demoAtespace, Name: actorName}, + Force: true, + }); err != nil { + t.Fatalf("expected force delete on suspended actor to succeed, got %v", err) + } + + // 2. Re-creating and Resuming the actor + t.Logf("Re-creating Actor %q...", actorName) + if _, err := clients.SubstrateAPI.CreateActor(ctx, &ateapipb.CreateActorRequest{Actor: &ateapipb.Actor{ + Metadata: &ateapipb.ResourceMetadata{Atespace: demoAtespace, Name: actorName}, + ActorTemplateNamespace: nsObj.Name, + ActorTemplateName: at.Name, + }}); err != nil { + t.Fatalf("failed to create Actor: %v", err) + } + waitForActorStatus(ctx, t, clients, actorName, ateapipb.Actor_STATUS_SUSPENDED) + + t.Logf("Resuming Actor %q...", actorName) + if _, err := clients.SubstrateAPI.ResumeActor(ctx, &ateapipb.ResumeActorRequest{ + Actor: &ateapipb.ObjectRef{Atespace: demoAtespace, Name: actorName}, + }); err != nil { + t.Fatalf("failed to resume Actor: %v", err) + } + waitForActorStatus(ctx, t, clients, actorName, ateapipb.Actor_STATUS_RUNNING) + + // 3. Call the actor to ensure workload is actively serving on worker + resp, err := callActor(t, resources.ActorRef{Atespace: demoAtespace, Name: actorName}) + if err != nil { + t.Fatalf("failed to call actor: %v", err) + } + validateCounterResponse(t, resp, "after creation", 1, 1) + + // 4. Attempt standard DeleteActor without force on running actor (should fail with FailedPrecondition) + t.Logf("Attempting standard delete on running Actor %q (should fail)...", actorName) + if _, err := clients.SubstrateAPI.DeleteActor(ctx, &ateapipb.DeleteActorRequest{ + Actor: &ateapipb.ObjectRef{Atespace: demoAtespace, Name: actorName}, + Force: false, + }); status.Code(err) != codes.FailedPrecondition { + t.Fatalf("expected FailedPrecondition when standard deleting running actor, got %v", err) + } + + // 5. Force delete the running actor + t.Logf("Force deleting running Actor %q...", actorName) + if _, err := clients.SubstrateAPI.DeleteActor(ctx, &ateapipb.DeleteActorRequest{ + Actor: &ateapipb.ObjectRef{Atespace: demoAtespace, Name: actorName}, + Force: true, + }); err != nil { + t.Fatalf("failed to force delete Actor: %v", err) + } + + // 6. Verify deletion in store + if _, err := clients.SubstrateAPI.GetActor(ctx, &ateapipb.GetActorRequest{ + Actor: &ateapipb.ObjectRef{Atespace: demoAtespace, Name: actorName}, + }); status.Code(err) != codes.NotFound { + t.Fatalf("expected actor %q to be NotFound after force delete, got err: %v", actorName, err) + } + + // 7. Verify actor name can be immediately reused + t.Logf("Re-creating Actor %q to verify name reuse...", actorName) + if _, err := clients.SubstrateAPI.CreateActor(ctx, &ateapipb.CreateActorRequest{Actor: &ateapipb.Actor{ + Metadata: &ateapipb.ResourceMetadata{Atespace: demoAtespace, Name: actorName}, + ActorTemplateNamespace: nsObj.Name, + ActorTemplateName: at.Name, + }}); err != nil { + t.Fatalf("failed to recreate Actor: %v", err) + } + defer func() { + clients.SubstrateAPI.DeleteActor(ctx, &ateapipb.DeleteActorRequest{ + Actor: &ateapipb.ObjectRef{Atespace: demoAtespace, Name: actorName}, + }) + }() + waitForActorStatus(ctx, t, clients, actorName, ateapipb.Actor_STATUS_SUSPENDED) + + return nil +} + +func forceDeletePausedActor(ctx context.Context, t *testing.T, clients *e2e.Clients, nsObj *e2e.Namespace, at *v1alpha1.ActorTemplate) error { + actorName := "force-delete-paused-actor-" + nsObj.Name + + // 1. Creating an actor + t.Logf("Creating Actor %q...", actorName) + if _, err := clients.SubstrateAPI.CreateActor(ctx, &ateapipb.CreateActorRequest{Actor: &ateapipb.Actor{ + Metadata: &ateapipb.ResourceMetadata{Atespace: demoAtespace, Name: actorName}, + ActorTemplateNamespace: nsObj.Name, + ActorTemplateName: at.Name, + }}); err != nil { + t.Fatalf("failed to create Actor: %v", err) + } + waitForActorStatus(ctx, t, clients, actorName, ateapipb.Actor_STATUS_SUSPENDED) + + // 2. Resuming the actor + t.Logf("Resuming Actor %q...", actorName) + if _, err := clients.SubstrateAPI.ResumeActor(ctx, &ateapipb.ResumeActorRequest{ + Actor: &ateapipb.ObjectRef{Atespace: demoAtespace, Name: actorName}, + }); err != nil { + t.Fatalf("failed to resume Actor: %v", err) + } + waitForActorStatus(ctx, t, clients, actorName, ateapipb.Actor_STATUS_RUNNING) + + // 3. Pausing the actor + t.Logf("Pausing Actor %q...", actorName) + if _, err := clients.SubstrateAPI.PauseActor(ctx, &ateapipb.PauseActorRequest{ + Actor: &ateapipb.ObjectRef{Atespace: demoAtespace, Name: actorName}, + }); err != nil { + t.Fatalf("failed to pause Actor: %v", err) + } + waitForActorStatus(ctx, t, clients, actorName, ateapipb.Actor_STATUS_PAUSED) + + // 4. Force delete the paused actor + t.Logf("Force deleting paused Actor %q...", actorName) + if _, err := clients.SubstrateAPI.DeleteActor(ctx, &ateapipb.DeleteActorRequest{ + Actor: &ateapipb.ObjectRef{Atespace: demoAtespace, Name: actorName}, + Force: true, + }); err != nil { + t.Fatalf("failed to force delete Actor: %v", err) + } + + // 5. Verify deletion in store + if _, err := clients.SubstrateAPI.GetActor(ctx, &ateapipb.GetActorRequest{ + Actor: &ateapipb.ObjectRef{Atespace: demoAtespace, Name: actorName}, + }); status.Code(err) != codes.NotFound { + t.Fatalf("expected actor %q to be NotFound after force delete, got err: %v", actorName, err) + } + + return nil +} + func createActorTemplateInternal(ctx context.Context, t *testing.T, clients *e2e.Clients, nsObj *e2e.Namespace, name string, onCommit, onPause v1alpha1.SnapshotScope, fromData v1alpha1.ResumeSource, modifyTemplate func(*v1alpha1.ActorTemplate)) (*v1alpha1.ActorTemplate, error) { env, err := e2e.CheckEnv("BUCKET_NAME", "KO_DOCKER_REPO") if err != nil { diff --git a/internal/proto/ateletpb/atelet.pb.go b/internal/proto/ateletpb/atelet.pb.go index c6a5b5814..e4981fbc9 100644 --- a/internal/proto/ateletpb/atelet.pb.go +++ b/internal/proto/ateletpb/atelet.pb.go @@ -301,6 +301,134 @@ func (x *MintActorCertificateResponse) GetActorCertificates() [][]byte { return nil } +type TerminateRequest struct { + state protoimpl.MessageState `protogen:"open.v1"` + TargetAteomUid string `protobuf:"bytes,1,opt,name=target_ateom_uid,json=targetAteomUid,proto3" json:"target_ateom_uid,omitempty"` + Atespace string `protobuf:"bytes,2,opt,name=atespace,proto3" json:"atespace,omitempty"` + ActorName string `protobuf:"bytes,3,opt,name=actor_name,json=actorName,proto3" json:"actor_name,omitempty"` + ActorUid string `protobuf:"bytes,4,opt,name=actor_uid,json=actorUid,proto3" json:"actor_uid,omitempty"` + ActorTemplateNamespace string `protobuf:"bytes,5,opt,name=actor_template_namespace,json=actorTemplateNamespace,proto3" json:"actor_template_namespace,omitempty"` + ActorTemplateName string `protobuf:"bytes,6,opt,name=actor_template_name,json=actorTemplateName,proto3" json:"actor_template_name,omitempty"` + Spec *WorkloadSpec `protobuf:"bytes,7,opt,name=spec,proto3" json:"spec,omitempty"` + unknownFields protoimpl.UnknownFields + sizeCache protoimpl.SizeCache +} + +func (x *TerminateRequest) Reset() { + *x = TerminateRequest{} + mi := &file_atelet_proto_msgTypes[2] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) +} + +func (x *TerminateRequest) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*TerminateRequest) ProtoMessage() {} + +func (x *TerminateRequest) ProtoReflect() protoreflect.Message { + mi := &file_atelet_proto_msgTypes[2] + 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 TerminateRequest.ProtoReflect.Descriptor instead. +func (*TerminateRequest) Descriptor() ([]byte, []int) { + return file_atelet_proto_rawDescGZIP(), []int{2} +} + +func (x *TerminateRequest) GetTargetAteomUid() string { + if x != nil { + return x.TargetAteomUid + } + return "" +} + +func (x *TerminateRequest) GetAtespace() string { + if x != nil { + return x.Atespace + } + return "" +} + +func (x *TerminateRequest) GetActorName() string { + if x != nil { + return x.ActorName + } + return "" +} + +func (x *TerminateRequest) GetActorUid() string { + if x != nil { + return x.ActorUid + } + return "" +} + +func (x *TerminateRequest) GetActorTemplateNamespace() string { + if x != nil { + return x.ActorTemplateNamespace + } + return "" +} + +func (x *TerminateRequest) GetActorTemplateName() string { + if x != nil { + return x.ActorTemplateName + } + return "" +} + +func (x *TerminateRequest) GetSpec() *WorkloadSpec { + if x != nil { + return x.Spec + } + return nil +} + +type TerminateResponse struct { + state protoimpl.MessageState `protogen:"open.v1"` + unknownFields protoimpl.UnknownFields + sizeCache protoimpl.SizeCache +} + +func (x *TerminateResponse) Reset() { + *x = TerminateResponse{} + mi := &file_atelet_proto_msgTypes[3] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) +} + +func (x *TerminateResponse) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*TerminateResponse) ProtoMessage() {} + +func (x *TerminateResponse) ProtoReflect() protoreflect.Message { + mi := &file_atelet_proto_msgTypes[3] + 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 TerminateResponse.ProtoReflect.Descriptor instead. +func (*TerminateResponse) Descriptor() ([]byte, []int) { + return file_atelet_proto_rawDescGZIP(), []int{3} +} + type RunRequest struct { state protoimpl.MessageState `protogen:"open.v1"` TargetAteomUid string `protobuf:"bytes,1,opt,name=target_ateom_uid,json=targetAteomUid,proto3" json:"target_ateom_uid,omitempty"` @@ -322,7 +450,7 @@ type RunRequest struct { func (x *RunRequest) Reset() { *x = RunRequest{} - mi := &file_atelet_proto_msgTypes[2] + mi := &file_atelet_proto_msgTypes[4] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -334,7 +462,7 @@ func (x *RunRequest) String() string { func (*RunRequest) ProtoMessage() {} func (x *RunRequest) ProtoReflect() protoreflect.Message { - mi := &file_atelet_proto_msgTypes[2] + mi := &file_atelet_proto_msgTypes[4] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -347,7 +475,7 @@ func (x *RunRequest) ProtoReflect() protoreflect.Message { // Deprecated: Use RunRequest.ProtoReflect.Descriptor instead. func (*RunRequest) Descriptor() ([]byte, []int) { - return file_atelet_proto_rawDescGZIP(), []int{2} + return file_atelet_proto_rawDescGZIP(), []int{4} } func (x *RunRequest) GetTargetAteomUid() string { @@ -425,7 +553,7 @@ type EgressGateway struct { func (x *EgressGateway) Reset() { *x = EgressGateway{} - mi := &file_atelet_proto_msgTypes[3] + mi := &file_atelet_proto_msgTypes[5] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -437,7 +565,7 @@ func (x *EgressGateway) String() string { func (*EgressGateway) ProtoMessage() {} func (x *EgressGateway) ProtoReflect() protoreflect.Message { - mi := &file_atelet_proto_msgTypes[3] + mi := &file_atelet_proto_msgTypes[5] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -450,7 +578,7 @@ func (x *EgressGateway) ProtoReflect() protoreflect.Message { // Deprecated: Use EgressGateway.ProtoReflect.Descriptor instead. func (*EgressGateway) Descriptor() ([]byte, []int) { - return file_atelet_proto_rawDescGZIP(), []int{3} + return file_atelet_proto_rawDescGZIP(), []int{5} } func (x *EgressGateway) GetAddress() string { @@ -474,7 +602,7 @@ type AssetFile struct { func (x *AssetFile) Reset() { *x = AssetFile{} - mi := &file_atelet_proto_msgTypes[4] + mi := &file_atelet_proto_msgTypes[6] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -486,7 +614,7 @@ func (x *AssetFile) String() string { func (*AssetFile) ProtoMessage() {} func (x *AssetFile) ProtoReflect() protoreflect.Message { - mi := &file_atelet_proto_msgTypes[4] + mi := &file_atelet_proto_msgTypes[6] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -499,7 +627,7 @@ func (x *AssetFile) ProtoReflect() protoreflect.Message { // Deprecated: Use AssetFile.ProtoReflect.Descriptor instead. func (*AssetFile) Descriptor() ([]byte, []int) { - return file_atelet_proto_rawDescGZIP(), []int{4} + return file_atelet_proto_rawDescGZIP(), []int{6} } func (x *AssetFile) GetUrl() string { @@ -527,7 +655,7 @@ type ArchAssets struct { func (x *ArchAssets) Reset() { *x = ArchAssets{} - mi := &file_atelet_proto_msgTypes[5] + mi := &file_atelet_proto_msgTypes[7] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -539,7 +667,7 @@ func (x *ArchAssets) String() string { func (*ArchAssets) ProtoMessage() {} func (x *ArchAssets) ProtoReflect() protoreflect.Message { - mi := &file_atelet_proto_msgTypes[5] + mi := &file_atelet_proto_msgTypes[7] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -552,7 +680,7 @@ func (x *ArchAssets) ProtoReflect() protoreflect.Message { // Deprecated: Use ArchAssets.ProtoReflect.Descriptor instead. func (*ArchAssets) Descriptor() ([]byte, []int) { - return file_atelet_proto_rawDescGZIP(), []int{5} + return file_atelet_proto_rawDescGZIP(), []int{7} } func (x *ArchAssets) GetFiles() map[string]*AssetFile { @@ -582,7 +710,7 @@ type SandboxAssets struct { func (x *SandboxAssets) Reset() { *x = SandboxAssets{} - mi := &file_atelet_proto_msgTypes[6] + mi := &file_atelet_proto_msgTypes[8] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -594,7 +722,7 @@ func (x *SandboxAssets) String() string { func (*SandboxAssets) ProtoMessage() {} func (x *SandboxAssets) ProtoReflect() protoreflect.Message { - mi := &file_atelet_proto_msgTypes[6] + mi := &file_atelet_proto_msgTypes[8] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -607,7 +735,7 @@ func (x *SandboxAssets) ProtoReflect() protoreflect.Message { // Deprecated: Use SandboxAssets.ProtoReflect.Descriptor instead. func (*SandboxAssets) Descriptor() ([]byte, []int) { - return file_atelet_proto_rawDescGZIP(), []int{6} + return file_atelet_proto_rawDescGZIP(), []int{8} } func (x *SandboxAssets) GetSandboxClass() string { @@ -642,7 +770,7 @@ type WorkloadSpec struct { func (x *WorkloadSpec) Reset() { *x = WorkloadSpec{} - mi := &file_atelet_proto_msgTypes[7] + mi := &file_atelet_proto_msgTypes[9] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -654,7 +782,7 @@ func (x *WorkloadSpec) String() string { func (*WorkloadSpec) ProtoMessage() {} func (x *WorkloadSpec) ProtoReflect() protoreflect.Message { - mi := &file_atelet_proto_msgTypes[7] + mi := &file_atelet_proto_msgTypes[9] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -667,7 +795,7 @@ func (x *WorkloadSpec) ProtoReflect() protoreflect.Message { // Deprecated: Use WorkloadSpec.ProtoReflect.Descriptor instead. func (*WorkloadSpec) Descriptor() ([]byte, []int) { - return file_atelet_proto_rawDescGZIP(), []int{7} + return file_atelet_proto_rawDescGZIP(), []int{9} } func (x *WorkloadSpec) GetContainers() []*Container { @@ -692,7 +820,7 @@ type DurableDirVolume struct { func (x *DurableDirVolume) Reset() { *x = DurableDirVolume{} - mi := &file_atelet_proto_msgTypes[8] + mi := &file_atelet_proto_msgTypes[10] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -704,7 +832,7 @@ func (x *DurableDirVolume) String() string { func (*DurableDirVolume) ProtoMessage() {} func (x *DurableDirVolume) ProtoReflect() protoreflect.Message { - mi := &file_atelet_proto_msgTypes[8] + mi := &file_atelet_proto_msgTypes[10] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -717,7 +845,7 @@ func (x *DurableDirVolume) ProtoReflect() protoreflect.Message { // Deprecated: Use DurableDirVolume.ProtoReflect.Descriptor instead. func (*DurableDirVolume) Descriptor() ([]byte, []int) { - return file_atelet_proto_rawDescGZIP(), []int{8} + return file_atelet_proto_rawDescGZIP(), []int{10} } type ExternalVolumeSource struct { @@ -731,7 +859,7 @@ type ExternalVolumeSource struct { func (x *ExternalVolumeSource) Reset() { *x = ExternalVolumeSource{} - mi := &file_atelet_proto_msgTypes[9] + mi := &file_atelet_proto_msgTypes[11] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -743,7 +871,7 @@ func (x *ExternalVolumeSource) String() string { func (*ExternalVolumeSource) ProtoMessage() {} func (x *ExternalVolumeSource) ProtoReflect() protoreflect.Message { - mi := &file_atelet_proto_msgTypes[9] + mi := &file_atelet_proto_msgTypes[11] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -756,7 +884,7 @@ func (x *ExternalVolumeSource) ProtoReflect() protoreflect.Message { // Deprecated: Use ExternalVolumeSource.ProtoReflect.Descriptor instead. func (*ExternalVolumeSource) Descriptor() ([]byte, []int) { - return file_atelet_proto_rawDescGZIP(), []int{9} + return file_atelet_proto_rawDescGZIP(), []int{11} } func (x *ExternalVolumeSource) GetStorageVolumeId() string { @@ -795,7 +923,7 @@ type Volume struct { func (x *Volume) Reset() { *x = Volume{} - mi := &file_atelet_proto_msgTypes[10] + mi := &file_atelet_proto_msgTypes[12] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -807,7 +935,7 @@ func (x *Volume) String() string { func (*Volume) ProtoMessage() {} func (x *Volume) ProtoReflect() protoreflect.Message { - mi := &file_atelet_proto_msgTypes[10] + mi := &file_atelet_proto_msgTypes[12] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -820,7 +948,7 @@ func (x *Volume) ProtoReflect() protoreflect.Message { // Deprecated: Use Volume.ProtoReflect.Descriptor instead. func (*Volume) Descriptor() ([]byte, []int) { - return file_atelet_proto_rawDescGZIP(), []int{10} + return file_atelet_proto_rawDescGZIP(), []int{12} } func (x *Volume) GetName() string { @@ -888,7 +1016,7 @@ type VolumeMount struct { func (x *VolumeMount) Reset() { *x = VolumeMount{} - mi := &file_atelet_proto_msgTypes[11] + mi := &file_atelet_proto_msgTypes[13] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -900,7 +1028,7 @@ func (x *VolumeMount) String() string { func (*VolumeMount) ProtoMessage() {} func (x *VolumeMount) ProtoReflect() protoreflect.Message { - mi := &file_atelet_proto_msgTypes[11] + mi := &file_atelet_proto_msgTypes[13] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -913,7 +1041,7 @@ func (x *VolumeMount) ProtoReflect() protoreflect.Message { // Deprecated: Use VolumeMount.ProtoReflect.Descriptor instead. func (*VolumeMount) Descriptor() ([]byte, []int) { - return file_atelet_proto_rawDescGZIP(), []int{11} + return file_atelet_proto_rawDescGZIP(), []int{13} } func (x *VolumeMount) GetName() string { @@ -945,7 +1073,7 @@ type Container struct { func (x *Container) Reset() { *x = Container{} - mi := &file_atelet_proto_msgTypes[12] + mi := &file_atelet_proto_msgTypes[14] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -957,7 +1085,7 @@ func (x *Container) String() string { func (*Container) ProtoMessage() {} func (x *Container) ProtoReflect() protoreflect.Message { - mi := &file_atelet_proto_msgTypes[12] + mi := &file_atelet_proto_msgTypes[14] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -970,7 +1098,7 @@ func (x *Container) ProtoReflect() protoreflect.Message { // Deprecated: Use Container.ProtoReflect.Descriptor instead. func (*Container) Descriptor() ([]byte, []int) { - return file_atelet_proto_rawDescGZIP(), []int{12} + return file_atelet_proto_rawDescGZIP(), []int{14} } func (x *Container) GetName() string { @@ -1032,7 +1160,7 @@ type EnvEntry struct { func (x *EnvEntry) Reset() { *x = EnvEntry{} - mi := &file_atelet_proto_msgTypes[13] + mi := &file_atelet_proto_msgTypes[15] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -1044,7 +1172,7 @@ func (x *EnvEntry) String() string { func (*EnvEntry) ProtoMessage() {} func (x *EnvEntry) ProtoReflect() protoreflect.Message { - mi := &file_atelet_proto_msgTypes[13] + mi := &file_atelet_proto_msgTypes[15] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -1057,7 +1185,7 @@ func (x *EnvEntry) ProtoReflect() protoreflect.Message { // Deprecated: Use EnvEntry.ProtoReflect.Descriptor instead. func (*EnvEntry) Descriptor() ([]byte, []int) { - return file_atelet_proto_rawDescGZIP(), []int{13} + return file_atelet_proto_rawDescGZIP(), []int{15} } func (x *EnvEntry) GetName() string { @@ -1088,7 +1216,7 @@ type Readyz struct { func (x *Readyz) Reset() { *x = Readyz{} - mi := &file_atelet_proto_msgTypes[14] + mi := &file_atelet_proto_msgTypes[16] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -1100,7 +1228,7 @@ func (x *Readyz) String() string { func (*Readyz) ProtoMessage() {} func (x *Readyz) ProtoReflect() protoreflect.Message { - mi := &file_atelet_proto_msgTypes[14] + mi := &file_atelet_proto_msgTypes[16] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -1113,7 +1241,7 @@ func (x *Readyz) ProtoReflect() protoreflect.Message { // Deprecated: Use Readyz.ProtoReflect.Descriptor instead. func (*Readyz) Descriptor() ([]byte, []int) { - return file_atelet_proto_rawDescGZIP(), []int{14} + return file_atelet_proto_rawDescGZIP(), []int{16} } func (x *Readyz) GetHttpGet() *HTTPGetAction { @@ -1143,7 +1271,7 @@ type HTTPGetAction struct { func (x *HTTPGetAction) Reset() { *x = HTTPGetAction{} - mi := &file_atelet_proto_msgTypes[15] + mi := &file_atelet_proto_msgTypes[17] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -1155,7 +1283,7 @@ func (x *HTTPGetAction) String() string { func (*HTTPGetAction) ProtoMessage() {} func (x *HTTPGetAction) ProtoReflect() protoreflect.Message { - mi := &file_atelet_proto_msgTypes[15] + mi := &file_atelet_proto_msgTypes[17] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -1168,7 +1296,7 @@ func (x *HTTPGetAction) ProtoReflect() protoreflect.Message { // Deprecated: Use HTTPGetAction.ProtoReflect.Descriptor instead. func (*HTTPGetAction) Descriptor() ([]byte, []int) { - return file_atelet_proto_rawDescGZIP(), []int{15} + return file_atelet_proto_rawDescGZIP(), []int{17} } func (x *HTTPGetAction) GetPath() string { @@ -1193,7 +1321,7 @@ type RunResponse struct { func (x *RunResponse) Reset() { *x = RunResponse{} - mi := &file_atelet_proto_msgTypes[16] + mi := &file_atelet_proto_msgTypes[18] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -1205,7 +1333,7 @@ func (x *RunResponse) String() string { func (*RunResponse) ProtoMessage() {} func (x *RunResponse) ProtoReflect() protoreflect.Message { - mi := &file_atelet_proto_msgTypes[16] + mi := &file_atelet_proto_msgTypes[18] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -1218,7 +1346,7 @@ func (x *RunResponse) ProtoReflect() protoreflect.Message { // Deprecated: Use RunResponse.ProtoReflect.Descriptor instead. func (*RunResponse) Descriptor() ([]byte, []int) { - return file_atelet_proto_rawDescGZIP(), []int{16} + return file_atelet_proto_rawDescGZIP(), []int{18} } type LocalCheckpointConfiguration struct { @@ -1234,7 +1362,7 @@ type LocalCheckpointConfiguration struct { func (x *LocalCheckpointConfiguration) Reset() { *x = LocalCheckpointConfiguration{} - mi := &file_atelet_proto_msgTypes[17] + mi := &file_atelet_proto_msgTypes[19] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -1246,7 +1374,7 @@ func (x *LocalCheckpointConfiguration) String() string { func (*LocalCheckpointConfiguration) ProtoMessage() {} func (x *LocalCheckpointConfiguration) ProtoReflect() protoreflect.Message { - mi := &file_atelet_proto_msgTypes[17] + mi := &file_atelet_proto_msgTypes[19] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -1259,7 +1387,7 @@ func (x *LocalCheckpointConfiguration) ProtoReflect() protoreflect.Message { // Deprecated: Use LocalCheckpointConfiguration.ProtoReflect.Descriptor instead. func (*LocalCheckpointConfiguration) Descriptor() ([]byte, []int) { - return file_atelet_proto_rawDescGZIP(), []int{17} + return file_atelet_proto_rawDescGZIP(), []int{19} } func (x *LocalCheckpointConfiguration) GetSnapshotName() string { @@ -1280,7 +1408,7 @@ type ExternalCheckpointConfiguration struct { func (x *ExternalCheckpointConfiguration) Reset() { *x = ExternalCheckpointConfiguration{} - mi := &file_atelet_proto_msgTypes[18] + mi := &file_atelet_proto_msgTypes[20] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -1292,7 +1420,7 @@ func (x *ExternalCheckpointConfiguration) String() string { func (*ExternalCheckpointConfiguration) ProtoMessage() {} func (x *ExternalCheckpointConfiguration) ProtoReflect() protoreflect.Message { - mi := &file_atelet_proto_msgTypes[18] + mi := &file_atelet_proto_msgTypes[20] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -1305,7 +1433,7 @@ func (x *ExternalCheckpointConfiguration) ProtoReflect() protoreflect.Message { // Deprecated: Use ExternalCheckpointConfiguration.ProtoReflect.Descriptor instead. func (*ExternalCheckpointConfiguration) Descriptor() ([]byte, []int) { - return file_atelet_proto_rawDescGZIP(), []int{18} + return file_atelet_proto_rawDescGZIP(), []int{20} } func (x *ExternalCheckpointConfiguration) GetSnapshotUri() string { @@ -1343,7 +1471,7 @@ type CheckpointRequest struct { func (x *CheckpointRequest) Reset() { *x = CheckpointRequest{} - mi := &file_atelet_proto_msgTypes[19] + mi := &file_atelet_proto_msgTypes[21] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -1355,7 +1483,7 @@ func (x *CheckpointRequest) String() string { func (*CheckpointRequest) ProtoMessage() {} func (x *CheckpointRequest) ProtoReflect() protoreflect.Message { - mi := &file_atelet_proto_msgTypes[19] + mi := &file_atelet_proto_msgTypes[21] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -1368,7 +1496,7 @@ func (x *CheckpointRequest) ProtoReflect() protoreflect.Message { // Deprecated: Use CheckpointRequest.ProtoReflect.Descriptor instead. func (*CheckpointRequest) Descriptor() ([]byte, []int) { - return file_atelet_proto_rawDescGZIP(), []int{19} + return file_atelet_proto_rawDescGZIP(), []int{21} } func (x *CheckpointRequest) GetTargetAteomUid() string { @@ -1483,7 +1611,7 @@ type CheckpointResponse struct { func (x *CheckpointResponse) Reset() { *x = CheckpointResponse{} - mi := &file_atelet_proto_msgTypes[20] + mi := &file_atelet_proto_msgTypes[22] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -1495,7 +1623,7 @@ func (x *CheckpointResponse) String() string { func (*CheckpointResponse) ProtoMessage() {} func (x *CheckpointResponse) ProtoReflect() protoreflect.Message { - mi := &file_atelet_proto_msgTypes[20] + mi := &file_atelet_proto_msgTypes[22] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -1508,7 +1636,7 @@ func (x *CheckpointResponse) ProtoReflect() protoreflect.Message { // Deprecated: Use CheckpointResponse.ProtoReflect.Descriptor instead. func (*CheckpointResponse) Descriptor() ([]byte, []int) { - return file_atelet_proto_rawDescGZIP(), []int{20} + return file_atelet_proto_rawDescGZIP(), []int{22} } type UploadPausedCheckpointRequest struct { @@ -1536,7 +1664,7 @@ type UploadPausedCheckpointRequest struct { func (x *UploadPausedCheckpointRequest) Reset() { *x = UploadPausedCheckpointRequest{} - mi := &file_atelet_proto_msgTypes[21] + mi := &file_atelet_proto_msgTypes[23] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -1548,7 +1676,7 @@ func (x *UploadPausedCheckpointRequest) String() string { func (*UploadPausedCheckpointRequest) ProtoMessage() {} func (x *UploadPausedCheckpointRequest) ProtoReflect() protoreflect.Message { - mi := &file_atelet_proto_msgTypes[21] + mi := &file_atelet_proto_msgTypes[23] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -1561,7 +1689,7 @@ func (x *UploadPausedCheckpointRequest) ProtoReflect() protoreflect.Message { // Deprecated: Use UploadPausedCheckpointRequest.ProtoReflect.Descriptor instead. func (*UploadPausedCheckpointRequest) Descriptor() ([]byte, []int) { - return file_atelet_proto_rawDescGZIP(), []int{21} + return file_atelet_proto_rawDescGZIP(), []int{23} } func (x *UploadPausedCheckpointRequest) GetAtespace() string { @@ -1628,7 +1756,7 @@ type UploadPausedCheckpointResponse struct { func (x *UploadPausedCheckpointResponse) Reset() { *x = UploadPausedCheckpointResponse{} - mi := &file_atelet_proto_msgTypes[22] + mi := &file_atelet_proto_msgTypes[24] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -1640,7 +1768,7 @@ func (x *UploadPausedCheckpointResponse) String() string { func (*UploadPausedCheckpointResponse) ProtoMessage() {} func (x *UploadPausedCheckpointResponse) ProtoReflect() protoreflect.Message { - mi := &file_atelet_proto_msgTypes[22] + mi := &file_atelet_proto_msgTypes[24] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -1653,7 +1781,7 @@ func (x *UploadPausedCheckpointResponse) ProtoReflect() protoreflect.Message { // Deprecated: Use UploadPausedCheckpointResponse.ProtoReflect.Descriptor instead. func (*UploadPausedCheckpointResponse) Descriptor() ([]byte, []int) { - return file_atelet_proto_rawDescGZIP(), []int{22} + return file_atelet_proto_rawDescGZIP(), []int{24} } type RestoreRequest struct { @@ -1693,7 +1821,7 @@ type RestoreRequest struct { func (x *RestoreRequest) Reset() { *x = RestoreRequest{} - mi := &file_atelet_proto_msgTypes[23] + mi := &file_atelet_proto_msgTypes[25] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -1705,7 +1833,7 @@ func (x *RestoreRequest) String() string { func (*RestoreRequest) ProtoMessage() {} func (x *RestoreRequest) ProtoReflect() protoreflect.Message { - mi := &file_atelet_proto_msgTypes[23] + mi := &file_atelet_proto_msgTypes[25] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -1718,7 +1846,7 @@ func (x *RestoreRequest) ProtoReflect() protoreflect.Message { // Deprecated: Use RestoreRequest.ProtoReflect.Descriptor instead. func (*RestoreRequest) Descriptor() ([]byte, []int) { - return file_atelet_proto_rawDescGZIP(), []int{23} + return file_atelet_proto_rawDescGZIP(), []int{25} } func (x *RestoreRequest) GetTargetAteomUid() string { @@ -1847,7 +1975,7 @@ type RestoreResponse struct { func (x *RestoreResponse) Reset() { *x = RestoreResponse{} - mi := &file_atelet_proto_msgTypes[24] + mi := &file_atelet_proto_msgTypes[26] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -1859,7 +1987,7 @@ func (x *RestoreResponse) String() string { func (*RestoreResponse) ProtoMessage() {} func (x *RestoreResponse) ProtoReflect() protoreflect.Message { - mi := &file_atelet_proto_msgTypes[24] + mi := &file_atelet_proto_msgTypes[26] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -1872,7 +2000,7 @@ func (x *RestoreResponse) ProtoReflect() protoreflect.Message { // Deprecated: Use RestoreResponse.ProtoReflect.Descriptor instead. func (*RestoreResponse) Descriptor() ([]byte, []int) { - return file_atelet_proto_rawDescGZIP(), []int{24} + return file_atelet_proto_rawDescGZIP(), []int{26} } var File_atelet_proto protoreflect.FileDescriptor @@ -1884,7 +2012,17 @@ const file_atelet_proto_rawDesc = "" + "\x1bcertificate_signing_request\x18\x01 \x01(\fR\x19certificateSigningRequest\x12,\n" + "\x12expected_actor_uid\x18\x02 \x01(\tR\x10expectedActorUid\"M\n" + "\x1cMintActorCertificateResponse\x12-\n" + - "\x12actor_certificates\x18\x01 \x03(\fR\x11actorCertificates\"\xb6\x03\n" + + "\x12actor_certificates\x18\x01 \x03(\fR\x11actorCertificates\"\xa8\x02\n" + + "\x10TerminateRequest\x12(\n" + + "\x10target_ateom_uid\x18\x01 \x01(\tR\x0etargetAteomUid\x12\x1a\n" + + "\batespace\x18\x02 \x01(\tR\batespace\x12\x1d\n" + + "\n" + + "actor_name\x18\x03 \x01(\tR\tactorName\x12\x1b\n" + + "\tactor_uid\x18\x04 \x01(\tR\bactorUid\x128\n" + + "\x18actor_template_namespace\x18\x05 \x01(\tR\x16actorTemplateNamespace\x12.\n" + + "\x13actor_template_name\x18\x06 \x01(\tR\x11actorTemplateName\x12(\n" + + "\x04spec\x18\a \x01(\v2\x14.atelet.WorkloadSpecR\x04spec\"\x13\n" + + "\x11TerminateResponse\"\xb6\x03\n" + "\n" + "RunRequest\x12(\n" + "\x10target_ateom_uid\x18\x01 \x01(\tR\x0etargetAteomUid\x12\x1a\n" + @@ -2026,13 +2164,14 @@ const file_atelet_proto_rawDesc = "" + "\x13SNAPSHOT_SCOPE_DATA\x10\x02\x12!\n" + "\x1dSNAPSHOT_SCOPE_DATA_ON_GOLDEN\x10\x032w\n" + "\x10CredentialBroker\x12c\n" + - "\x14MintActorCertificate\x12#.atelet.MintActorCertificateRequest\x1a$.atelet.MintActorCertificateResponse\"\x002\xaf\x02\n" + + "\x14MintActorCertificate\x12#.atelet.MintActorCertificateRequest\x1a$.atelet.MintActorCertificateResponse\"\x002\xf3\x02\n" + "\vAteomHerder\x120\n" + "\x03Run\x12\x12.atelet.RunRequest\x1a\x13.atelet.RunResponse\"\x00\x12E\n" + "\n" + "Checkpoint\x12\x19.atelet.CheckpointRequest\x1a\x1a.atelet.CheckpointResponse\"\x00\x12<\n" + "\aRestore\x12\x16.atelet.RestoreRequest\x1a\x17.atelet.RestoreResponse\"\x00\x12i\n" + - "\x16UploadPausedCheckpoint\x12%.atelet.UploadPausedCheckpointRequest\x1a&.atelet.UploadPausedCheckpointResponse\"\x00B>ZZ atelet.WorkloadSpec - 9, // 1: atelet.RunRequest.sandbox_assets:type_name -> atelet.SandboxAssets - 6, // 2: atelet.RunRequest.egress_gateway:type_name -> atelet.EgressGateway - 28, // 3: atelet.ArchAssets.files:type_name -> atelet.ArchAssets.FilesEntry - 29, // 4: atelet.SandboxAssets.assets:type_name -> atelet.SandboxAssets.AssetsEntry - 15, // 5: atelet.WorkloadSpec.containers:type_name -> atelet.Container - 13, // 6: atelet.WorkloadSpec.volumes:type_name -> atelet.Volume - 30, // 7: atelet.ExternalVolumeSource.volume_context:type_name -> atelet.ExternalVolumeSource.VolumeContextEntry - 0, // 8: atelet.Volume.type:type_name -> atelet.VolumeType - 11, // 9: atelet.Volume.durable_dir:type_name -> atelet.DurableDirVolume - 12, // 10: atelet.Volume.external:type_name -> atelet.ExternalVolumeSource - 16, // 11: atelet.Container.env:type_name -> atelet.EnvEntry - 17, // 12: atelet.Container.readyz:type_name -> atelet.Readyz - 14, // 13: atelet.Container.volume_mounts:type_name -> atelet.VolumeMount - 18, // 14: atelet.Readyz.http_get:type_name -> atelet.HTTPGetAction - 10, // 15: atelet.CheckpointRequest.spec:type_name -> atelet.WorkloadSpec - 1, // 16: atelet.CheckpointRequest.type:type_name -> atelet.CheckpointType - 20, // 17: atelet.CheckpointRequest.local_config:type_name -> atelet.LocalCheckpointConfiguration - 21, // 18: atelet.CheckpointRequest.external_config:type_name -> atelet.ExternalCheckpointConfiguration - 2, // 19: atelet.CheckpointRequest.scope:type_name -> atelet.SnapshotScope - 2, // 20: atelet.UploadPausedCheckpointRequest.desired_scope:type_name -> atelet.SnapshotScope - 10, // 21: atelet.RestoreRequest.spec:type_name -> atelet.WorkloadSpec - 1, // 22: atelet.RestoreRequest.type:type_name -> atelet.CheckpointType - 20, // 23: atelet.RestoreRequest.local_config:type_name -> atelet.LocalCheckpointConfiguration - 21, // 24: atelet.RestoreRequest.external_config:type_name -> atelet.ExternalCheckpointConfiguration - 2, // 25: atelet.RestoreRequest.scope:type_name -> atelet.SnapshotScope - 6, // 26: atelet.RestoreRequest.egress_gateway:type_name -> atelet.EgressGateway - 7, // 27: atelet.ArchAssets.FilesEntry.value:type_name -> atelet.AssetFile - 8, // 28: atelet.SandboxAssets.AssetsEntry.value:type_name -> atelet.ArchAssets - 3, // 29: atelet.CredentialBroker.MintActorCertificate:input_type -> atelet.MintActorCertificateRequest - 5, // 30: atelet.AteomHerder.Run:input_type -> atelet.RunRequest - 22, // 31: atelet.AteomHerder.Checkpoint:input_type -> atelet.CheckpointRequest - 26, // 32: atelet.AteomHerder.Restore:input_type -> atelet.RestoreRequest - 24, // 33: atelet.AteomHerder.UploadPausedCheckpoint:input_type -> atelet.UploadPausedCheckpointRequest - 4, // 34: atelet.CredentialBroker.MintActorCertificate:output_type -> atelet.MintActorCertificateResponse - 19, // 35: atelet.AteomHerder.Run:output_type -> atelet.RunResponse - 23, // 36: atelet.AteomHerder.Checkpoint:output_type -> atelet.CheckpointResponse - 27, // 37: atelet.AteomHerder.Restore:output_type -> atelet.RestoreResponse - 25, // 38: atelet.AteomHerder.UploadPausedCheckpoint:output_type -> atelet.UploadPausedCheckpointResponse - 34, // [34:39] is the sub-list for method output_type - 29, // [29:34] is the sub-list for method input_type - 29, // [29:29] is the sub-list for extension type_name - 29, // [29:29] is the sub-list for extension extendee - 0, // [0:29] is the sub-list for field type_name + 12, // 0: atelet.TerminateRequest.spec:type_name -> atelet.WorkloadSpec + 12, // 1: atelet.RunRequest.spec:type_name -> atelet.WorkloadSpec + 11, // 2: atelet.RunRequest.sandbox_assets:type_name -> atelet.SandboxAssets + 8, // 3: atelet.RunRequest.egress_gateway:type_name -> atelet.EgressGateway + 30, // 4: atelet.ArchAssets.files:type_name -> atelet.ArchAssets.FilesEntry + 31, // 5: atelet.SandboxAssets.assets:type_name -> atelet.SandboxAssets.AssetsEntry + 17, // 6: atelet.WorkloadSpec.containers:type_name -> atelet.Container + 15, // 7: atelet.WorkloadSpec.volumes:type_name -> atelet.Volume + 32, // 8: atelet.ExternalVolumeSource.volume_context:type_name -> atelet.ExternalVolumeSource.VolumeContextEntry + 0, // 9: atelet.Volume.type:type_name -> atelet.VolumeType + 13, // 10: atelet.Volume.durable_dir:type_name -> atelet.DurableDirVolume + 14, // 11: atelet.Volume.external:type_name -> atelet.ExternalVolumeSource + 18, // 12: atelet.Container.env:type_name -> atelet.EnvEntry + 19, // 13: atelet.Container.readyz:type_name -> atelet.Readyz + 16, // 14: atelet.Container.volume_mounts:type_name -> atelet.VolumeMount + 20, // 15: atelet.Readyz.http_get:type_name -> atelet.HTTPGetAction + 12, // 16: atelet.CheckpointRequest.spec:type_name -> atelet.WorkloadSpec + 1, // 17: atelet.CheckpointRequest.type:type_name -> atelet.CheckpointType + 22, // 18: atelet.CheckpointRequest.local_config:type_name -> atelet.LocalCheckpointConfiguration + 23, // 19: atelet.CheckpointRequest.external_config:type_name -> atelet.ExternalCheckpointConfiguration + 2, // 20: atelet.CheckpointRequest.scope:type_name -> atelet.SnapshotScope + 2, // 21: atelet.UploadPausedCheckpointRequest.desired_scope:type_name -> atelet.SnapshotScope + 12, // 22: atelet.RestoreRequest.spec:type_name -> atelet.WorkloadSpec + 1, // 23: atelet.RestoreRequest.type:type_name -> atelet.CheckpointType + 22, // 24: atelet.RestoreRequest.local_config:type_name -> atelet.LocalCheckpointConfiguration + 23, // 25: atelet.RestoreRequest.external_config:type_name -> atelet.ExternalCheckpointConfiguration + 2, // 26: atelet.RestoreRequest.scope:type_name -> atelet.SnapshotScope + 8, // 27: atelet.RestoreRequest.egress_gateway:type_name -> atelet.EgressGateway + 9, // 28: atelet.ArchAssets.FilesEntry.value:type_name -> atelet.AssetFile + 10, // 29: atelet.SandboxAssets.AssetsEntry.value:type_name -> atelet.ArchAssets + 3, // 30: atelet.CredentialBroker.MintActorCertificate:input_type -> atelet.MintActorCertificateRequest + 7, // 31: atelet.AteomHerder.Run:input_type -> atelet.RunRequest + 24, // 32: atelet.AteomHerder.Checkpoint:input_type -> atelet.CheckpointRequest + 28, // 33: atelet.AteomHerder.Restore:input_type -> atelet.RestoreRequest + 26, // 34: atelet.AteomHerder.UploadPausedCheckpoint:input_type -> atelet.UploadPausedCheckpointRequest + 5, // 35: atelet.AteomHerder.Terminate:input_type -> atelet.TerminateRequest + 4, // 36: atelet.CredentialBroker.MintActorCertificate:output_type -> atelet.MintActorCertificateResponse + 21, // 37: atelet.AteomHerder.Run:output_type -> atelet.RunResponse + 25, // 38: atelet.AteomHerder.Checkpoint:output_type -> atelet.CheckpointResponse + 29, // 39: atelet.AteomHerder.Restore:output_type -> atelet.RestoreResponse + 27, // 40: atelet.AteomHerder.UploadPausedCheckpoint:output_type -> atelet.UploadPausedCheckpointResponse + 6, // 41: atelet.AteomHerder.Terminate:output_type -> atelet.TerminateResponse + 36, // [36:42] is the sub-list for method output_type + 30, // [30:36] is the sub-list for method input_type + 30, // [30:30] is the sub-list for extension type_name + 30, // [30:30] is the sub-list for extension extendee + 0, // [0:30] is the sub-list for field type_name } func init() { file_atelet_proto_init() } @@ -2133,16 +2277,16 @@ func file_atelet_proto_init() { if File_atelet_proto != nil { return } - file_atelet_proto_msgTypes[2].OneofWrappers = []any{} - file_atelet_proto_msgTypes[10].OneofWrappers = []any{ + file_atelet_proto_msgTypes[4].OneofWrappers = []any{} + file_atelet_proto_msgTypes[12].OneofWrappers = []any{ (*Volume_DurableDir)(nil), (*Volume_External)(nil), } - file_atelet_proto_msgTypes[19].OneofWrappers = []any{ + file_atelet_proto_msgTypes[21].OneofWrappers = []any{ (*CheckpointRequest_LocalConfig)(nil), (*CheckpointRequest_ExternalConfig)(nil), } - file_atelet_proto_msgTypes[23].OneofWrappers = []any{ + file_atelet_proto_msgTypes[25].OneofWrappers = []any{ (*RestoreRequest_LocalConfig)(nil), (*RestoreRequest_ExternalConfig)(nil), } @@ -2152,7 +2296,7 @@ func file_atelet_proto_init() { GoPackagePath: reflect.TypeOf(x{}).PkgPath(), RawDescriptor: unsafe.Slice(unsafe.StringData(file_atelet_proto_rawDesc), len(file_atelet_proto_rawDesc)), NumEnums: 3, - NumMessages: 28, + NumMessages: 30, NumExtensions: 0, NumServices: 2, }, diff --git a/internal/proto/ateletpb/atelet.proto b/internal/proto/ateletpb/atelet.proto index 92263d230..b3cda199a 100644 --- a/internal/proto/ateletpb/atelet.proto +++ b/internal/proto/ateletpb/atelet.proto @@ -56,6 +56,26 @@ service AteomHerder { // paused, its sandbox is gone; the checkpoint files plus their manifest // already sit under the actor's local-checkpoints directory. rpc UploadPausedCheckpoint(UploadPausedCheckpointRequest) returns (UploadPausedCheckpointResponse) {} + + // Terminate tells atelet to terminate/kill any running workload for an actor, + // unmount its volumes, and clean up actor state on the node. + rpc Terminate(TerminateRequest) returns (TerminateResponse) {} +} + +message TerminateRequest { + string target_ateom_uid = 1; + + string atespace = 2; + string actor_name = 3; + string actor_uid = 4; + + string actor_template_namespace = 5; + string actor_template_name = 6; + + WorkloadSpec spec = 7; +} + +message TerminateResponse { } message RunRequest { diff --git a/internal/proto/ateletpb/atelet_grpc.pb.go b/internal/proto/ateletpb/atelet_grpc.pb.go index d6fde5ee4..e82cc05b2 100644 --- a/internal/proto/ateletpb/atelet_grpc.pb.go +++ b/internal/proto/ateletpb/atelet_grpc.pb.go @@ -143,6 +143,7 @@ const ( AteomHerder_Checkpoint_FullMethodName = "/atelet.AteomHerder/Checkpoint" AteomHerder_Restore_FullMethodName = "/atelet.AteomHerder/Restore" AteomHerder_UploadPausedCheckpoint_FullMethodName = "/atelet.AteomHerder/UploadPausedCheckpoint" + AteomHerder_Terminate_FullMethodName = "/atelet.AteomHerder/Terminate" ) // AteomHerderClient is the client API for AteomHerder service. @@ -163,6 +164,9 @@ type AteomHerderClient interface { // paused, its sandbox is gone; the checkpoint files plus their manifest // already sit under the actor's local-checkpoints directory. UploadPausedCheckpoint(ctx context.Context, in *UploadPausedCheckpointRequest, opts ...grpc.CallOption) (*UploadPausedCheckpointResponse, error) + // Terminate tells atelet to terminate/kill any running workload for an actor, + // unmount its volumes, and clean up actor state on the node. + Terminate(ctx context.Context, in *TerminateRequest, opts ...grpc.CallOption) (*TerminateResponse, error) } type ateomHerderClient struct { @@ -213,6 +217,16 @@ func (c *ateomHerderClient) UploadPausedCheckpoint(ctx context.Context, in *Uplo return out, nil } +func (c *ateomHerderClient) Terminate(ctx context.Context, in *TerminateRequest, opts ...grpc.CallOption) (*TerminateResponse, error) { + cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...) + out := new(TerminateResponse) + err := c.cc.Invoke(ctx, AteomHerder_Terminate_FullMethodName, in, out, cOpts...) + if err != nil { + return nil, err + } + return out, nil +} + // AteomHerderServer is the server API for AteomHerder service. // All implementations must embed UnimplementedAteomHerderServer // for forward compatibility. @@ -231,6 +245,9 @@ type AteomHerderServer interface { // paused, its sandbox is gone; the checkpoint files plus their manifest // already sit under the actor's local-checkpoints directory. UploadPausedCheckpoint(context.Context, *UploadPausedCheckpointRequest) (*UploadPausedCheckpointResponse, error) + // Terminate tells atelet to terminate/kill any running workload for an actor, + // unmount its volumes, and clean up actor state on the node. + Terminate(context.Context, *TerminateRequest) (*TerminateResponse, error) mustEmbedUnimplementedAteomHerderServer() } @@ -253,6 +270,9 @@ func (UnimplementedAteomHerderServer) Restore(context.Context, *RestoreRequest) func (UnimplementedAteomHerderServer) UploadPausedCheckpoint(context.Context, *UploadPausedCheckpointRequest) (*UploadPausedCheckpointResponse, error) { return nil, status.Error(codes.Unimplemented, "method UploadPausedCheckpoint not implemented") } +func (UnimplementedAteomHerderServer) Terminate(context.Context, *TerminateRequest) (*TerminateResponse, error) { + return nil, status.Error(codes.Unimplemented, "method Terminate not implemented") +} func (UnimplementedAteomHerderServer) mustEmbedUnimplementedAteomHerderServer() {} func (UnimplementedAteomHerderServer) testEmbeddedByValue() {} @@ -346,6 +366,24 @@ func _AteomHerder_UploadPausedCheckpoint_Handler(srv interface{}, ctx context.Co return interceptor(ctx, in, info, handler) } +func _AteomHerder_Terminate_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { + in := new(TerminateRequest) + if err := dec(in); err != nil { + return nil, err + } + if interceptor == nil { + return srv.(AteomHerderServer).Terminate(ctx, in) + } + info := &grpc.UnaryServerInfo{ + Server: srv, + FullMethod: AteomHerder_Terminate_FullMethodName, + } + handler := func(ctx context.Context, req interface{}) (interface{}, error) { + return srv.(AteomHerderServer).Terminate(ctx, req.(*TerminateRequest)) + } + return interceptor(ctx, in, info, handler) +} + // AteomHerder_ServiceDesc is the grpc.ServiceDesc for AteomHerder service. // It's only intended for direct use with grpc.RegisterService, // and not to be introspected or modified (even as a copy) @@ -369,6 +407,10 @@ var AteomHerder_ServiceDesc = grpc.ServiceDesc{ MethodName: "UploadPausedCheckpoint", Handler: _AteomHerder_UploadPausedCheckpoint_Handler, }, + { + MethodName: "Terminate", + Handler: _AteomHerder_Terminate_Handler, + }, }, Streams: []grpc.StreamDesc{}, Metadata: "atelet.proto", diff --git a/internal/proto/ateompb/ateom.pb.go b/internal/proto/ateompb/ateom.pb.go index cfe6b3ed5..aeee361b0 100644 --- a/internal/proto/ateompb/ateom.pb.go +++ b/internal/proto/ateompb/ateom.pb.go @@ -207,6 +207,134 @@ func (StatsSource) EnumDescriptor() ([]byte, []int) { return file_ateom_proto_rawDescGZIP(), []int{2} } +type TerminateWorkloadRequest struct { + state protoimpl.MessageState `protogen:"open.v1"` + Atespace string `protobuf:"bytes,1,opt,name=atespace,proto3" json:"atespace,omitempty"` + ActorName string `protobuf:"bytes,2,opt,name=actor_name,json=actorName,proto3" json:"actor_name,omitempty"` + ActorUid string `protobuf:"bytes,3,opt,name=actor_uid,json=actorUid,proto3" json:"actor_uid,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"` + RunscPath string `protobuf:"bytes,6,opt,name=runsc_path,json=runscPath,proto3" json:"runsc_path,omitempty"` + Spec *WorkloadSpec `protobuf:"bytes,7,opt,name=spec,proto3" json:"spec,omitempty"` + unknownFields protoimpl.UnknownFields + sizeCache protoimpl.SizeCache +} + +func (x *TerminateWorkloadRequest) Reset() { + *x = TerminateWorkloadRequest{} + mi := &file_ateom_proto_msgTypes[0] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) +} + +func (x *TerminateWorkloadRequest) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*TerminateWorkloadRequest) ProtoMessage() {} + +func (x *TerminateWorkloadRequest) ProtoReflect() protoreflect.Message { + mi := &file_ateom_proto_msgTypes[0] + 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 TerminateWorkloadRequest.ProtoReflect.Descriptor instead. +func (*TerminateWorkloadRequest) Descriptor() ([]byte, []int) { + return file_ateom_proto_rawDescGZIP(), []int{0} +} + +func (x *TerminateWorkloadRequest) GetAtespace() string { + if x != nil { + return x.Atespace + } + return "" +} + +func (x *TerminateWorkloadRequest) GetActorName() string { + if x != nil { + return x.ActorName + } + return "" +} + +func (x *TerminateWorkloadRequest) GetActorUid() string { + if x != nil { + return x.ActorUid + } + return "" +} + +func (x *TerminateWorkloadRequest) GetActorTemplateNamespace() string { + if x != nil { + return x.ActorTemplateNamespace + } + return "" +} + +func (x *TerminateWorkloadRequest) GetActorTemplateName() string { + if x != nil { + return x.ActorTemplateName + } + return "" +} + +func (x *TerminateWorkloadRequest) GetRunscPath() string { + if x != nil { + return x.RunscPath + } + return "" +} + +func (x *TerminateWorkloadRequest) GetSpec() *WorkloadSpec { + if x != nil { + return x.Spec + } + return nil +} + +type TerminateWorkloadResponse struct { + state protoimpl.MessageState `protogen:"open.v1"` + unknownFields protoimpl.UnknownFields + sizeCache protoimpl.SizeCache +} + +func (x *TerminateWorkloadResponse) Reset() { + *x = TerminateWorkloadResponse{} + mi := &file_ateom_proto_msgTypes[1] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) +} + +func (x *TerminateWorkloadResponse) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*TerminateWorkloadResponse) ProtoMessage() {} + +func (x *TerminateWorkloadResponse) ProtoReflect() protoreflect.Message { + mi := &file_ateom_proto_msgTypes[1] + 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 TerminateWorkloadResponse.ProtoReflect.Descriptor instead. +func (*TerminateWorkloadResponse) Descriptor() ([]byte, []int) { + return file_ateom_proto_rawDescGZIP(), []int{1} +} + type RunWorkloadRequest struct { state protoimpl.MessageState `protogen:"open.v1"` Atespace string `protobuf:"bytes,1,opt,name=atespace,proto3" json:"atespace,omitempty"` @@ -229,7 +357,7 @@ type RunWorkloadRequest struct { func (x *RunWorkloadRequest) Reset() { *x = RunWorkloadRequest{} - mi := &file_ateom_proto_msgTypes[0] + mi := &file_ateom_proto_msgTypes[2] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -241,7 +369,7 @@ func (x *RunWorkloadRequest) String() string { func (*RunWorkloadRequest) ProtoMessage() {} func (x *RunWorkloadRequest) ProtoReflect() protoreflect.Message { - mi := &file_ateom_proto_msgTypes[0] + mi := &file_ateom_proto_msgTypes[2] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -254,7 +382,7 @@ func (x *RunWorkloadRequest) ProtoReflect() protoreflect.Message { // Deprecated: Use RunWorkloadRequest.ProtoReflect.Descriptor instead. func (*RunWorkloadRequest) Descriptor() ([]byte, []int) { - return file_ateom_proto_rawDescGZIP(), []int{0} + return file_ateom_proto_rawDescGZIP(), []int{2} } func (x *RunWorkloadRequest) GetAtespace() string { @@ -332,7 +460,7 @@ type EgressGateway struct { func (x *EgressGateway) Reset() { *x = EgressGateway{} - mi := &file_ateom_proto_msgTypes[1] + mi := &file_ateom_proto_msgTypes[3] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -344,7 +472,7 @@ func (x *EgressGateway) String() string { func (*EgressGateway) ProtoMessage() {} func (x *EgressGateway) ProtoReflect() protoreflect.Message { - mi := &file_ateom_proto_msgTypes[1] + mi := &file_ateom_proto_msgTypes[3] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -357,7 +485,7 @@ func (x *EgressGateway) ProtoReflect() protoreflect.Message { // Deprecated: Use EgressGateway.ProtoReflect.Descriptor instead. func (*EgressGateway) Descriptor() ([]byte, []int) { - return file_ateom_proto_rawDescGZIP(), []int{1} + return file_ateom_proto_rawDescGZIP(), []int{3} } func (x *EgressGateway) GetAddress() string { @@ -377,7 +505,7 @@ type WorkloadSpec struct { func (x *WorkloadSpec) Reset() { *x = WorkloadSpec{} - mi := &file_ateom_proto_msgTypes[2] + mi := &file_ateom_proto_msgTypes[4] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -389,7 +517,7 @@ func (x *WorkloadSpec) String() string { func (*WorkloadSpec) ProtoMessage() {} func (x *WorkloadSpec) ProtoReflect() protoreflect.Message { - mi := &file_ateom_proto_msgTypes[2] + mi := &file_ateom_proto_msgTypes[4] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -402,7 +530,7 @@ func (x *WorkloadSpec) ProtoReflect() protoreflect.Message { // Deprecated: Use WorkloadSpec.ProtoReflect.Descriptor instead. func (*WorkloadSpec) Descriptor() ([]byte, []int) { - return file_ateom_proto_rawDescGZIP(), []int{2} + return file_ateom_proto_rawDescGZIP(), []int{4} } func (x *WorkloadSpec) GetContainers() []*Container { @@ -425,7 +553,7 @@ type Container struct { func (x *Container) Reset() { *x = Container{} - mi := &file_ateom_proto_msgTypes[3] + mi := &file_ateom_proto_msgTypes[5] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -437,7 +565,7 @@ func (x *Container) String() string { func (*Container) ProtoMessage() {} func (x *Container) ProtoReflect() protoreflect.Message { - mi := &file_ateom_proto_msgTypes[3] + mi := &file_ateom_proto_msgTypes[5] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -450,7 +578,7 @@ func (x *Container) ProtoReflect() protoreflect.Message { // Deprecated: Use Container.ProtoReflect.Descriptor instead. func (*Container) Descriptor() ([]byte, []int) { - return file_ateom_proto_rawDescGZIP(), []int{3} + return file_ateom_proto_rawDescGZIP(), []int{5} } func (x *Container) GetName() string { @@ -488,7 +616,7 @@ type DurableDirVolumeMount struct { func (x *DurableDirVolumeMount) Reset() { *x = DurableDirVolumeMount{} - mi := &file_ateom_proto_msgTypes[4] + mi := &file_ateom_proto_msgTypes[6] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -500,7 +628,7 @@ func (x *DurableDirVolumeMount) String() string { func (*DurableDirVolumeMount) ProtoMessage() {} func (x *DurableDirVolumeMount) ProtoReflect() protoreflect.Message { - mi := &file_ateom_proto_msgTypes[4] + mi := &file_ateom_proto_msgTypes[6] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -513,7 +641,7 @@ func (x *DurableDirVolumeMount) ProtoReflect() protoreflect.Message { // Deprecated: Use DurableDirVolumeMount.ProtoReflect.Descriptor instead. func (*DurableDirVolumeMount) Descriptor() ([]byte, []int) { - return file_ateom_proto_rawDescGZIP(), []int{4} + return file_ateom_proto_rawDescGZIP(), []int{6} } func (x *DurableDirVolumeMount) GetVolumeName() string { @@ -544,7 +672,7 @@ type Readyz struct { func (x *Readyz) Reset() { *x = Readyz{} - mi := &file_ateom_proto_msgTypes[5] + mi := &file_ateom_proto_msgTypes[7] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -556,7 +684,7 @@ func (x *Readyz) String() string { func (*Readyz) ProtoMessage() {} func (x *Readyz) ProtoReflect() protoreflect.Message { - mi := &file_ateom_proto_msgTypes[5] + mi := &file_ateom_proto_msgTypes[7] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -569,7 +697,7 @@ func (x *Readyz) ProtoReflect() protoreflect.Message { // Deprecated: Use Readyz.ProtoReflect.Descriptor instead. func (*Readyz) Descriptor() ([]byte, []int) { - return file_ateom_proto_rawDescGZIP(), []int{5} + return file_ateom_proto_rawDescGZIP(), []int{7} } func (x *Readyz) GetHttpGet() *HTTPGetAction { @@ -599,7 +727,7 @@ type HTTPGetAction struct { func (x *HTTPGetAction) Reset() { *x = HTTPGetAction{} - mi := &file_ateom_proto_msgTypes[6] + mi := &file_ateom_proto_msgTypes[8] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -611,7 +739,7 @@ func (x *HTTPGetAction) String() string { func (*HTTPGetAction) ProtoMessage() {} func (x *HTTPGetAction) ProtoReflect() protoreflect.Message { - mi := &file_ateom_proto_msgTypes[6] + mi := &file_ateom_proto_msgTypes[8] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -624,7 +752,7 @@ func (x *HTTPGetAction) ProtoReflect() protoreflect.Message { // Deprecated: Use HTTPGetAction.ProtoReflect.Descriptor instead. func (*HTTPGetAction) Descriptor() ([]byte, []int) { - return file_ateom_proto_rawDescGZIP(), []int{6} + return file_ateom_proto_rawDescGZIP(), []int{8} } func (x *HTTPGetAction) GetPath() string { @@ -649,7 +777,7 @@ type RunWorkloadResponse struct { func (x *RunWorkloadResponse) Reset() { *x = RunWorkloadResponse{} - mi := &file_ateom_proto_msgTypes[7] + mi := &file_ateom_proto_msgTypes[9] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -661,7 +789,7 @@ func (x *RunWorkloadResponse) String() string { func (*RunWorkloadResponse) ProtoMessage() {} func (x *RunWorkloadResponse) ProtoReflect() protoreflect.Message { - mi := &file_ateom_proto_msgTypes[7] + mi := &file_ateom_proto_msgTypes[9] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -674,7 +802,7 @@ func (x *RunWorkloadResponse) ProtoReflect() protoreflect.Message { // Deprecated: Use RunWorkloadResponse.ProtoReflect.Descriptor instead. func (*RunWorkloadResponse) Descriptor() ([]byte, []int) { - return file_ateom_proto_rawDescGZIP(), []int{7} + return file_ateom_proto_rawDescGZIP(), []int{9} } type CheckpointWorkloadRequest struct { @@ -708,7 +836,7 @@ type CheckpointWorkloadRequest struct { func (x *CheckpointWorkloadRequest) Reset() { *x = CheckpointWorkloadRequest{} - mi := &file_ateom_proto_msgTypes[8] + mi := &file_ateom_proto_msgTypes[10] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -720,7 +848,7 @@ func (x *CheckpointWorkloadRequest) String() string { func (*CheckpointWorkloadRequest) ProtoMessage() {} func (x *CheckpointWorkloadRequest) ProtoReflect() protoreflect.Message { - mi := &file_ateom_proto_msgTypes[8] + mi := &file_ateom_proto_msgTypes[10] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -733,7 +861,7 @@ func (x *CheckpointWorkloadRequest) ProtoReflect() protoreflect.Message { // Deprecated: Use CheckpointWorkloadRequest.ProtoReflect.Descriptor instead. func (*CheckpointWorkloadRequest) Descriptor() ([]byte, []int) { - return file_ateom_proto_rawDescGZIP(), []int{8} + return file_ateom_proto_rawDescGZIP(), []int{10} } func (x *CheckpointWorkloadRequest) GetAtespace() string { @@ -818,7 +946,7 @@ type CheckpointWorkloadResponse struct { func (x *CheckpointWorkloadResponse) Reset() { *x = CheckpointWorkloadResponse{} - mi := &file_ateom_proto_msgTypes[9] + mi := &file_ateom_proto_msgTypes[11] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -830,7 +958,7 @@ func (x *CheckpointWorkloadResponse) String() string { func (*CheckpointWorkloadResponse) ProtoMessage() {} func (x *CheckpointWorkloadResponse) ProtoReflect() protoreflect.Message { - mi := &file_ateom_proto_msgTypes[9] + mi := &file_ateom_proto_msgTypes[11] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -843,7 +971,7 @@ func (x *CheckpointWorkloadResponse) ProtoReflect() protoreflect.Message { // Deprecated: Use CheckpointWorkloadResponse.ProtoReflect.Descriptor instead. func (*CheckpointWorkloadResponse) Descriptor() ([]byte, []int) { - return file_ateom_proto_rawDescGZIP(), []int{9} + return file_ateom_proto_rawDescGZIP(), []int{11} } func (x *CheckpointWorkloadResponse) GetSnapshotFiles() []string { @@ -882,7 +1010,7 @@ type RestoreWorkloadRequest struct { func (x *RestoreWorkloadRequest) Reset() { *x = RestoreWorkloadRequest{} - mi := &file_ateom_proto_msgTypes[10] + mi := &file_ateom_proto_msgTypes[12] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -894,7 +1022,7 @@ func (x *RestoreWorkloadRequest) String() string { func (*RestoreWorkloadRequest) ProtoMessage() {} func (x *RestoreWorkloadRequest) ProtoReflect() protoreflect.Message { - mi := &file_ateom_proto_msgTypes[10] + mi := &file_ateom_proto_msgTypes[12] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -907,7 +1035,7 @@ func (x *RestoreWorkloadRequest) ProtoReflect() protoreflect.Message { // Deprecated: Use RestoreWorkloadRequest.ProtoReflect.Descriptor instead. func (*RestoreWorkloadRequest) Descriptor() ([]byte, []int) { - return file_ateom_proto_rawDescGZIP(), []int{10} + return file_ateom_proto_rawDescGZIP(), []int{12} } func (x *RestoreWorkloadRequest) GetAtespace() string { @@ -1002,7 +1130,7 @@ type RestoreWorkloadResponse struct { func (x *RestoreWorkloadResponse) Reset() { *x = RestoreWorkloadResponse{} - mi := &file_ateom_proto_msgTypes[11] + mi := &file_ateom_proto_msgTypes[13] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -1014,7 +1142,7 @@ func (x *RestoreWorkloadResponse) String() string { func (*RestoreWorkloadResponse) ProtoMessage() {} func (x *RestoreWorkloadResponse) ProtoReflect() protoreflect.Message { - mi := &file_ateom_proto_msgTypes[11] + mi := &file_ateom_proto_msgTypes[13] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -1027,7 +1155,7 @@ func (x *RestoreWorkloadResponse) ProtoReflect() protoreflect.Message { // Deprecated: Use RestoreWorkloadResponse.ProtoReflect.Descriptor instead. func (*RestoreWorkloadResponse) Descriptor() ([]byte, []int) { - return file_ateom_proto_rawDescGZIP(), []int{11} + return file_ateom_proto_rawDescGZIP(), []int{13} } type GetWorkloadStatsRequest struct { @@ -1043,7 +1171,7 @@ type GetWorkloadStatsRequest struct { func (x *GetWorkloadStatsRequest) Reset() { *x = GetWorkloadStatsRequest{} - mi := &file_ateom_proto_msgTypes[12] + mi := &file_ateom_proto_msgTypes[14] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -1055,7 +1183,7 @@ func (x *GetWorkloadStatsRequest) String() string { func (*GetWorkloadStatsRequest) ProtoMessage() {} func (x *GetWorkloadStatsRequest) ProtoReflect() protoreflect.Message { - mi := &file_ateom_proto_msgTypes[12] + mi := &file_ateom_proto_msgTypes[14] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -1068,7 +1196,7 @@ func (x *GetWorkloadStatsRequest) ProtoReflect() protoreflect.Message { // Deprecated: Use GetWorkloadStatsRequest.ProtoReflect.Descriptor instead. func (*GetWorkloadStatsRequest) Descriptor() ([]byte, []int) { - return file_ateom_proto_rawDescGZIP(), []int{12} + return file_ateom_proto_rawDescGZIP(), []int{14} } func (x *GetWorkloadStatsRequest) GetActorUid() string { @@ -1129,7 +1257,7 @@ type GetWorkloadStatsResponse struct { func (x *GetWorkloadStatsResponse) Reset() { *x = GetWorkloadStatsResponse{} - mi := &file_ateom_proto_msgTypes[13] + mi := &file_ateom_proto_msgTypes[15] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -1141,7 +1269,7 @@ func (x *GetWorkloadStatsResponse) String() string { func (*GetWorkloadStatsResponse) ProtoMessage() {} func (x *GetWorkloadStatsResponse) ProtoReflect() protoreflect.Message { - mi := &file_ateom_proto_msgTypes[13] + mi := &file_ateom_proto_msgTypes[15] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -1154,7 +1282,7 @@ func (x *GetWorkloadStatsResponse) ProtoReflect() protoreflect.Message { // Deprecated: Use GetWorkloadStatsResponse.ProtoReflect.Descriptor instead. func (*GetWorkloadStatsResponse) Descriptor() ([]byte, []int) { - return file_ateom_proto_rawDescGZIP(), []int{13} + return file_ateom_proto_rawDescGZIP(), []int{15} } func (x *GetWorkloadStatsResponse) GetAtespace() string { @@ -1245,7 +1373,18 @@ var File_ateom_proto protoreflect.FileDescriptor const file_ateom_proto_rawDesc = "" + "\n" + - "\vateom.proto\x12\x05ateom\"\x9b\x04\n" + + "\vateom.proto\x12\x05ateom\"\xa4\x02\n" + + "\x18TerminateWorkloadRequest\x12\x1a\n" + + "\batespace\x18\x01 \x01(\tR\batespace\x12\x1d\n" + + "\n" + + "actor_name\x18\x02 \x01(\tR\tactorName\x12\x1b\n" + + "\tactor_uid\x18\x03 \x01(\tR\bactorUid\x128\n" + + "\x18actor_template_namespace\x18\x04 \x01(\tR\x16actorTemplateNamespace\x12.\n" + + "\x13actor_template_name\x18\x05 \x01(\tR\x11actorTemplateName\x12\x1d\n" + + "\n" + + "runsc_path\x18\x06 \x01(\tR\trunscPath\x12'\n" + + "\x04spec\x18\a \x01(\v2\x13.ateom.WorkloadSpecR\x04spec\"\x1b\n" + + "\x19TerminateWorkloadResponse\"\x9b\x04\n" + "\x12RunWorkloadRequest\x12\x1a\n" + "\batespace\x18\x01 \x01(\tR\batespace\x12\x1d\n" + "\n" + @@ -1354,12 +1493,13 @@ const file_ateom_proto_rawDesc = "" + "\vStatsSource\x12\x1c\n" + "\x18STATS_SOURCE_UNSPECIFIED\x10\x00\x12\x17\n" + "\x13STATS_SOURCE_CGROUP\x10\x01\x12\x1c\n" + - "\x18STATS_SOURCE_GUEST_AGENT\x10\x022\xd7\x02\n" + + "\x18STATS_SOURCE_GUEST_AGENT\x10\x022\xb1\x03\n" + "\x05Ateom\x12F\n" + "\vRunWorkload\x12\x19.ateom.RunWorkloadRequest\x1a\x1a.ateom.RunWorkloadResponse\"\x00\x12[\n" + "\x12CheckpointWorkload\x12 .ateom.CheckpointWorkloadRequest\x1a!.ateom.CheckpointWorkloadResponse\"\x00\x12R\n" + "\x0fRestoreWorkload\x12\x1d.ateom.RestoreWorkloadRequest\x1a\x1e.ateom.RestoreWorkloadResponse\"\x00\x12U\n" + - "\x10GetWorkloadStats\x12\x1e.ateom.GetWorkloadStatsRequest\x1a\x1f.ateom.GetWorkloadStatsResponse\"\x00B=Z;github.com/agent-substrate/substrate/internal/proto/ateompbb\x06proto3" + "\x10GetWorkloadStats\x12\x1e.ateom.GetWorkloadStatsRequest\x1a\x1f.ateom.GetWorkloadStatsResponse\"\x00\x12X\n" + + "\x11TerminateWorkload\x12\x1f.ateom.TerminateWorkloadRequest\x1a .ateom.TerminateWorkloadResponse\"\x00B=Z;github.com/agent-substrate/substrate/internal/proto/ateompbb\x06proto3" var ( file_ateom_proto_rawDescOnce sync.Once @@ -1374,59 +1514,64 @@ func file_ateom_proto_rawDescGZIP() []byte { } var file_ateom_proto_enumTypes = make([]protoimpl.EnumInfo, 3) -var file_ateom_proto_msgTypes = make([]protoimpl.MessageInfo, 17) +var file_ateom_proto_msgTypes = make([]protoimpl.MessageInfo, 19) var file_ateom_proto_goTypes = []any{ (SnapshotScope)(0), // 0: ateom.SnapshotScope (SandboxClass)(0), // 1: ateom.SandboxClass (StatsSource)(0), // 2: ateom.StatsSource - (*RunWorkloadRequest)(nil), // 3: ateom.RunWorkloadRequest - (*EgressGateway)(nil), // 4: ateom.EgressGateway - (*WorkloadSpec)(nil), // 5: ateom.WorkloadSpec - (*Container)(nil), // 6: ateom.Container - (*DurableDirVolumeMount)(nil), // 7: ateom.DurableDirVolumeMount - (*Readyz)(nil), // 8: ateom.Readyz - (*HTTPGetAction)(nil), // 9: ateom.HTTPGetAction - (*RunWorkloadResponse)(nil), // 10: ateom.RunWorkloadResponse - (*CheckpointWorkloadRequest)(nil), // 11: ateom.CheckpointWorkloadRequest - (*CheckpointWorkloadResponse)(nil), // 12: ateom.CheckpointWorkloadResponse - (*RestoreWorkloadRequest)(nil), // 13: ateom.RestoreWorkloadRequest - (*RestoreWorkloadResponse)(nil), // 14: ateom.RestoreWorkloadResponse - (*GetWorkloadStatsRequest)(nil), // 15: ateom.GetWorkloadStatsRequest - (*GetWorkloadStatsResponse)(nil), // 16: ateom.GetWorkloadStatsResponse - nil, // 17: ateom.RunWorkloadRequest.RuntimeAssetPathsEntry - nil, // 18: ateom.CheckpointWorkloadRequest.RuntimeAssetPathsEntry - nil, // 19: ateom.RestoreWorkloadRequest.RuntimeAssetPathsEntry + (*TerminateWorkloadRequest)(nil), // 3: ateom.TerminateWorkloadRequest + (*TerminateWorkloadResponse)(nil), // 4: ateom.TerminateWorkloadResponse + (*RunWorkloadRequest)(nil), // 5: ateom.RunWorkloadRequest + (*EgressGateway)(nil), // 6: ateom.EgressGateway + (*WorkloadSpec)(nil), // 7: ateom.WorkloadSpec + (*Container)(nil), // 8: ateom.Container + (*DurableDirVolumeMount)(nil), // 9: ateom.DurableDirVolumeMount + (*Readyz)(nil), // 10: ateom.Readyz + (*HTTPGetAction)(nil), // 11: ateom.HTTPGetAction + (*RunWorkloadResponse)(nil), // 12: ateom.RunWorkloadResponse + (*CheckpointWorkloadRequest)(nil), // 13: ateom.CheckpointWorkloadRequest + (*CheckpointWorkloadResponse)(nil), // 14: ateom.CheckpointWorkloadResponse + (*RestoreWorkloadRequest)(nil), // 15: ateom.RestoreWorkloadRequest + (*RestoreWorkloadResponse)(nil), // 16: ateom.RestoreWorkloadResponse + (*GetWorkloadStatsRequest)(nil), // 17: ateom.GetWorkloadStatsRequest + (*GetWorkloadStatsResponse)(nil), // 18: ateom.GetWorkloadStatsResponse + nil, // 19: ateom.RunWorkloadRequest.RuntimeAssetPathsEntry + nil, // 20: ateom.CheckpointWorkloadRequest.RuntimeAssetPathsEntry + nil, // 21: ateom.RestoreWorkloadRequest.RuntimeAssetPathsEntry } var file_ateom_proto_depIdxs = []int32{ - 5, // 0: ateom.RunWorkloadRequest.spec:type_name -> ateom.WorkloadSpec - 17, // 1: ateom.RunWorkloadRequest.runtime_asset_paths:type_name -> ateom.RunWorkloadRequest.RuntimeAssetPathsEntry - 4, // 2: ateom.RunWorkloadRequest.egress_gateway:type_name -> ateom.EgressGateway - 6, // 3: ateom.WorkloadSpec.containers:type_name -> ateom.Container - 8, // 4: ateom.Container.readyz:type_name -> ateom.Readyz - 7, // 5: ateom.Container.durable_dir_volume_mounts:type_name -> ateom.DurableDirVolumeMount - 9, // 6: ateom.Readyz.http_get:type_name -> ateom.HTTPGetAction - 5, // 7: ateom.CheckpointWorkloadRequest.spec:type_name -> ateom.WorkloadSpec - 18, // 8: ateom.CheckpointWorkloadRequest.runtime_asset_paths:type_name -> ateom.CheckpointWorkloadRequest.RuntimeAssetPathsEntry - 0, // 9: ateom.CheckpointWorkloadRequest.scope:type_name -> ateom.SnapshotScope - 5, // 10: ateom.RestoreWorkloadRequest.spec:type_name -> ateom.WorkloadSpec - 19, // 11: ateom.RestoreWorkloadRequest.runtime_asset_paths:type_name -> ateom.RestoreWorkloadRequest.RuntimeAssetPathsEntry - 0, // 12: ateom.RestoreWorkloadRequest.scope:type_name -> ateom.SnapshotScope - 4, // 13: ateom.RestoreWorkloadRequest.egress_gateway:type_name -> ateom.EgressGateway - 1, // 14: ateom.GetWorkloadStatsResponse.sandbox_class:type_name -> ateom.SandboxClass - 2, // 15: ateom.GetWorkloadStatsResponse.source:type_name -> ateom.StatsSource - 3, // 16: ateom.Ateom.RunWorkload:input_type -> ateom.RunWorkloadRequest - 11, // 17: ateom.Ateom.CheckpointWorkload:input_type -> ateom.CheckpointWorkloadRequest - 13, // 18: ateom.Ateom.RestoreWorkload:input_type -> ateom.RestoreWorkloadRequest - 15, // 19: ateom.Ateom.GetWorkloadStats:input_type -> ateom.GetWorkloadStatsRequest - 10, // 20: ateom.Ateom.RunWorkload:output_type -> ateom.RunWorkloadResponse - 12, // 21: ateom.Ateom.CheckpointWorkload:output_type -> ateom.CheckpointWorkloadResponse - 14, // 22: ateom.Ateom.RestoreWorkload:output_type -> ateom.RestoreWorkloadResponse - 16, // 23: ateom.Ateom.GetWorkloadStats:output_type -> ateom.GetWorkloadStatsResponse - 20, // [20:24] is the sub-list for method output_type - 16, // [16:20] is the sub-list for method input_type - 16, // [16:16] is the sub-list for extension type_name - 16, // [16:16] is the sub-list for extension extendee - 0, // [0:16] is the sub-list for field type_name + 7, // 0: ateom.TerminateWorkloadRequest.spec:type_name -> ateom.WorkloadSpec + 7, // 1: ateom.RunWorkloadRequest.spec:type_name -> ateom.WorkloadSpec + 19, // 2: ateom.RunWorkloadRequest.runtime_asset_paths:type_name -> ateom.RunWorkloadRequest.RuntimeAssetPathsEntry + 6, // 3: ateom.RunWorkloadRequest.egress_gateway:type_name -> ateom.EgressGateway + 8, // 4: ateom.WorkloadSpec.containers:type_name -> ateom.Container + 10, // 5: ateom.Container.readyz:type_name -> ateom.Readyz + 9, // 6: ateom.Container.durable_dir_volume_mounts:type_name -> ateom.DurableDirVolumeMount + 11, // 7: ateom.Readyz.http_get:type_name -> ateom.HTTPGetAction + 7, // 8: ateom.CheckpointWorkloadRequest.spec:type_name -> ateom.WorkloadSpec + 20, // 9: ateom.CheckpointWorkloadRequest.runtime_asset_paths:type_name -> ateom.CheckpointWorkloadRequest.RuntimeAssetPathsEntry + 0, // 10: ateom.CheckpointWorkloadRequest.scope:type_name -> ateom.SnapshotScope + 7, // 11: ateom.RestoreWorkloadRequest.spec:type_name -> ateom.WorkloadSpec + 21, // 12: ateom.RestoreWorkloadRequest.runtime_asset_paths:type_name -> ateom.RestoreWorkloadRequest.RuntimeAssetPathsEntry + 0, // 13: ateom.RestoreWorkloadRequest.scope:type_name -> ateom.SnapshotScope + 6, // 14: ateom.RestoreWorkloadRequest.egress_gateway:type_name -> ateom.EgressGateway + 1, // 15: ateom.GetWorkloadStatsResponse.sandbox_class:type_name -> ateom.SandboxClass + 2, // 16: ateom.GetWorkloadStatsResponse.source:type_name -> ateom.StatsSource + 5, // 17: ateom.Ateom.RunWorkload:input_type -> ateom.RunWorkloadRequest + 13, // 18: ateom.Ateom.CheckpointWorkload:input_type -> ateom.CheckpointWorkloadRequest + 15, // 19: ateom.Ateom.RestoreWorkload:input_type -> ateom.RestoreWorkloadRequest + 17, // 20: ateom.Ateom.GetWorkloadStats:input_type -> ateom.GetWorkloadStatsRequest + 3, // 21: ateom.Ateom.TerminateWorkload:input_type -> ateom.TerminateWorkloadRequest + 12, // 22: ateom.Ateom.RunWorkload:output_type -> ateom.RunWorkloadResponse + 14, // 23: ateom.Ateom.CheckpointWorkload:output_type -> ateom.CheckpointWorkloadResponse + 16, // 24: ateom.Ateom.RestoreWorkload:output_type -> ateom.RestoreWorkloadResponse + 18, // 25: ateom.Ateom.GetWorkloadStats:output_type -> ateom.GetWorkloadStatsResponse + 4, // 26: ateom.Ateom.TerminateWorkload:output_type -> ateom.TerminateWorkloadResponse + 22, // [22:27] is the sub-list for method output_type + 17, // [17:22] is the sub-list for method input_type + 17, // [17:17] is the sub-list for extension type_name + 17, // [17:17] is the sub-list for extension extendee + 0, // [0:17] is the sub-list for field type_name } func init() { file_ateom_proto_init() } @@ -1434,15 +1579,15 @@ func file_ateom_proto_init() { if File_ateom_proto != nil { return } - file_ateom_proto_msgTypes[0].OneofWrappers = []any{} - file_ateom_proto_msgTypes[10].OneofWrappers = []any{} + file_ateom_proto_msgTypes[2].OneofWrappers = []any{} + file_ateom_proto_msgTypes[12].OneofWrappers = []any{} type x struct{} out := protoimpl.TypeBuilder{ File: protoimpl.DescBuilder{ GoPackagePath: reflect.TypeOf(x{}).PkgPath(), RawDescriptor: unsafe.Slice(unsafe.StringData(file_ateom_proto_rawDesc), len(file_ateom_proto_rawDesc)), NumEnums: 3, - NumMessages: 17, + NumMessages: 19, NumExtensions: 0, NumServices: 1, }, diff --git a/internal/proto/ateompb/ateom.proto b/internal/proto/ateompb/ateom.proto index 2d6a8d8be..246fad9f5 100644 --- a/internal/proto/ateompb/ateom.proto +++ b/internal/proto/ateompb/ateom.proto @@ -77,6 +77,25 @@ service Ateom { // "booting" distinguishable from "not here" at all, and it means a workload // that dies during boot is attributable rather than anonymous. rpc GetWorkloadStats(GetWorkloadStatsRequest) returns (GetWorkloadStatsResponse) {} + + // TerminateWorkload stops and deletes container workloads and cleans up + // network and bundle overlays on ateom. + rpc TerminateWorkload(TerminateWorkloadRequest) returns (TerminateWorkloadResponse) {} +} + +message TerminateWorkloadRequest { + string atespace = 1; + string actor_name = 2; + string actor_uid = 3; + + string actor_template_namespace = 4; + string actor_template_name = 5; + + string runsc_path = 6; + WorkloadSpec spec = 7; +} + +message TerminateWorkloadResponse { } message RunWorkloadRequest { diff --git a/internal/proto/ateompb/ateom_grpc.pb.go b/internal/proto/ateompb/ateom_grpc.pb.go index 391fd5f60..5d0efc8b0 100644 --- a/internal/proto/ateompb/ateom_grpc.pb.go +++ b/internal/proto/ateompb/ateom_grpc.pb.go @@ -37,6 +37,7 @@ const ( Ateom_CheckpointWorkload_FullMethodName = "/ateom.Ateom/CheckpointWorkload" Ateom_RestoreWorkload_FullMethodName = "/ateom.Ateom/RestoreWorkload" Ateom_GetWorkloadStats_FullMethodName = "/ateom.Ateom/GetWorkloadStats" + Ateom_TerminateWorkload_FullMethodName = "/ateom.Ateom/TerminateWorkload" ) // AteomClient is the client API for Ateom service. @@ -99,6 +100,9 @@ type AteomClient interface { // "booting" distinguishable from "not here" at all, and it means a workload // that dies during boot is attributable rather than anonymous. GetWorkloadStats(ctx context.Context, in *GetWorkloadStatsRequest, opts ...grpc.CallOption) (*GetWorkloadStatsResponse, error) + // TerminateWorkload stops and deletes container workloads and cleans up + // network and bundle overlays on ateom. + TerminateWorkload(ctx context.Context, in *TerminateWorkloadRequest, opts ...grpc.CallOption) (*TerminateWorkloadResponse, error) } type ateomClient struct { @@ -149,6 +153,16 @@ func (c *ateomClient) GetWorkloadStats(ctx context.Context, in *GetWorkloadStats return out, nil } +func (c *ateomClient) TerminateWorkload(ctx context.Context, in *TerminateWorkloadRequest, opts ...grpc.CallOption) (*TerminateWorkloadResponse, error) { + cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...) + out := new(TerminateWorkloadResponse) + err := c.cc.Invoke(ctx, Ateom_TerminateWorkload_FullMethodName, in, out, cOpts...) + if err != nil { + return nil, err + } + return out, nil +} + // AteomServer is the server API for Ateom service. // All implementations must embed UnimplementedAteomServer // for forward compatibility. @@ -209,6 +223,9 @@ type AteomServer interface { // "booting" distinguishable from "not here" at all, and it means a workload // that dies during boot is attributable rather than anonymous. GetWorkloadStats(context.Context, *GetWorkloadStatsRequest) (*GetWorkloadStatsResponse, error) + // TerminateWorkload stops and deletes container workloads and cleans up + // network and bundle overlays on ateom. + TerminateWorkload(context.Context, *TerminateWorkloadRequest) (*TerminateWorkloadResponse, error) mustEmbedUnimplementedAteomServer() } @@ -231,6 +248,9 @@ func (UnimplementedAteomServer) RestoreWorkload(context.Context, *RestoreWorkloa func (UnimplementedAteomServer) GetWorkloadStats(context.Context, *GetWorkloadStatsRequest) (*GetWorkloadStatsResponse, error) { return nil, status.Error(codes.Unimplemented, "method GetWorkloadStats not implemented") } +func (UnimplementedAteomServer) TerminateWorkload(context.Context, *TerminateWorkloadRequest) (*TerminateWorkloadResponse, error) { + return nil, status.Error(codes.Unimplemented, "method TerminateWorkload not implemented") +} func (UnimplementedAteomServer) mustEmbedUnimplementedAteomServer() {} func (UnimplementedAteomServer) testEmbeddedByValue() {} @@ -324,6 +344,24 @@ func _Ateom_GetWorkloadStats_Handler(srv interface{}, ctx context.Context, dec f return interceptor(ctx, in, info, handler) } +func _Ateom_TerminateWorkload_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { + in := new(TerminateWorkloadRequest) + if err := dec(in); err != nil { + return nil, err + } + if interceptor == nil { + return srv.(AteomServer).TerminateWorkload(ctx, in) + } + info := &grpc.UnaryServerInfo{ + Server: srv, + FullMethod: Ateom_TerminateWorkload_FullMethodName, + } + handler := func(ctx context.Context, req interface{}) (interface{}, error) { + return srv.(AteomServer).TerminateWorkload(ctx, req.(*TerminateWorkloadRequest)) + } + return interceptor(ctx, in, info, handler) +} + // Ateom_ServiceDesc is the grpc.ServiceDesc for Ateom service. // It's only intended for direct use with grpc.RegisterService, // and not to be introspected or modified (even as a copy) @@ -347,6 +385,10 @@ var Ateom_ServiceDesc = grpc.ServiceDesc{ MethodName: "GetWorkloadStats", Handler: _Ateom_GetWorkloadStats_Handler, }, + { + MethodName: "TerminateWorkload", + Handler: _Ateom_TerminateWorkload_Handler, + }, }, Streams: []grpc.StreamDesc{}, Metadata: "ateom.proto", diff --git a/pkg/proto/ateapipb/ateapi.pb.go b/pkg/proto/ateapipb/ateapi.pb.go index 87b290519..11d21ddbf 100644 --- a/pkg/proto/ateapipb/ateapi.pb.go +++ b/pkg/proto/ateapipb/ateapi.pb.go @@ -251,6 +251,7 @@ const ( Actor_STATUS_PAUSED Actor_Status = 6 Actor_STATUS_CRASHED Actor_Status = 7 Actor_STATUS_DELETING Actor_Status = 8 + Actor_STATUS_TERMINATING Actor_Status = 9 ) // Enum value maps for Actor_Status. @@ -265,6 +266,7 @@ var ( 6: "STATUS_PAUSED", 7: "STATUS_CRASHED", 8: "STATUS_DELETING", + 9: "STATUS_TERMINATING", } Actor_Status_value = map[string]int32{ "STATUS_UNSPECIFIED": 0, @@ -276,6 +278,7 @@ var ( "STATUS_PAUSED": 6, "STATUS_CRASHED": 7, "STATUS_DELETING": 8, + "STATUS_TERMINATING": 9, } ) @@ -1919,6 +1922,7 @@ func (x *ResumeActorResponse) GetResumed() bool { type DeleteActorRequest struct { state protoimpl.MessageState `protogen:"open.v1"` Actor *ObjectRef `protobuf:"bytes,1,opt,name=actor,proto3" json:"actor,omitempty"` + Force bool `protobuf:"varint,2,opt,name=force,proto3" json:"force,omitempty"` unknownFields protoimpl.UnknownFields sizeCache protoimpl.SizeCache } @@ -1960,6 +1964,13 @@ func (x *DeleteActorRequest) GetActor() *ObjectRef { return nil } +func (x *DeleteActorRequest) GetForce() bool { + if x != nil { + return x.Force + } + return false +} + type GetActorSnapshotRequest struct { state protoimpl.MessageState `protogen:"open.v1"` Snapshot *ActorSnapshotRef `protobuf:"bytes,1,opt,name=snapshot,proto3" json:"snapshot,omitempty"` @@ -3126,7 +3137,7 @@ 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\"\xbe\a\n" + + "\x0fSTATUS_DELETING\x10\x03\"\xd6\a\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" + @@ -3140,7 +3151,7 @@ const file_ateapi_proto_rawDesc = "" + ")in_progress_snapshot_source_actor_version\x18\n" + " \x01(\x03R$inProgressSnapshotSourceActorVersion\x12;\n" + "\ractor_volumes\x18\v \x03(\v2\x16.ateapi.ExternalVolumeR\factorVolumes\x12D\n" + - "\x1fin_progress_local_snapshot_name\x18\f \x01(\tR\x1binProgressLocalSnapshotName\"\xc6\x01\n" + + "\x1fin_progress_local_snapshot_name\x18\f \x01(\tR\x1binProgressLocalSnapshotName\"\xde\x01\n" + "\x06Status\x12\x16\n" + "\x12STATUS_UNSPECIFIED\x10\x00\x12\x13\n" + "\x0fSTATUS_RESUMING\x10\x01\x12\x12\n" + @@ -3150,7 +3161,8 @@ const file_ateapi_proto_rawDesc = "" + "\x0eSTATUS_PAUSING\x10\x05\x12\x11\n" + "\rSTATUS_PAUSED\x10\x06\x12\x12\n" + "\x0eSTATUS_CRASHED\x10\a\x12\x13\n" + - "\x0fSTATUS_DELETING\x10\b\"\xc7\x01\n" + + "\x0fSTATUS_DELETING\x10\b\x12\x16\n" + + "\x12STATUS_TERMINATING\x10\t\"\xc7\x01\n" + "\x10WorkerAssignment\x12)\n" + "\x10worker_namespace\x18\x01 \x01(\tR\x0fworkerNamespace\x12\x1f\n" + "\vworker_pool\x18\x02 \x01(\tR\n" + @@ -3217,9 +3229,10 @@ const file_ateapi_proto_rawDesc = "" + "\x04boot\x18\x02 \x01(\bR\x04boot\"T\n" + "\x13ResumeActorResponse\x12#\n" + "\x05actor\x18\x01 \x01(\v2\r.ateapi.ActorR\x05actor\x12\x18\n" + - "\aresumed\x18\x02 \x01(\bR\aresumed\"=\n" + + "\aresumed\x18\x02 \x01(\bR\aresumed\"S\n" + "\x12DeleteActorRequest\x12'\n" + - "\x05actor\x18\x01 \x01(\v2\x11.ateapi.ObjectRefR\x05actor\"O\n" + + "\x05actor\x18\x01 \x01(\v2\x11.ateapi.ObjectRefR\x05actor\x12\x14\n" + + "\x05force\x18\x02 \x01(\bR\x05force\"O\n" + "\x17GetActorSnapshotRequest\x124\n" + "\bsnapshot\x18\x01 \x01(\v2\x18.ateapi.ActorSnapshotRefR\bsnapshot\"s\n" + "\x19ListActorSnapshotsRequest\x12\x1a\n" + diff --git a/pkg/proto/ateapipb/ateapi.proto b/pkg/proto/ateapipb/ateapi.proto index 1ee06ddea..c989620d2 100644 --- a/pkg/proto/ateapipb/ateapi.proto +++ b/pkg/proto/ateapipb/ateapi.proto @@ -182,6 +182,7 @@ message Actor { STATUS_PAUSED = 6; STATUS_CRASHED = 7; STATUS_DELETING = 8; + STATUS_TERMINATING = 9; } Status status = 4; @@ -374,6 +375,7 @@ message ResumeActorResponse { message DeleteActorRequest { ObjectRef actor = 1; + bool force = 2; } message GetActorSnapshotRequest {