diff --git a/cmd/ateapi/internal/controlapi/functional_test.go b/cmd/ateapi/internal/controlapi/functional_test.go index 1c49a26c7..2afd61fe4 100644 --- a/cmd/ateapi/internal/controlapi/functional_test.go +++ b/cmd/ateapi/internal/controlapi/functional_test.go @@ -868,6 +868,7 @@ func TestResumeActor(t *testing.T) { ActorTemplate: "tmpl1", ActorId: id, Ip: "127.0.0.1", + NodeName: "node1", } if diff := cmp.Diff(wantWorker, actorWorker, protocmp.Transform(), protocmp.IgnoreFields(&ateapipb.Worker{}, "version"), protocmp.IgnoreFields(&ateapipb.Worker{}, "worker_pod_uid")); diff != "" { @@ -1123,14 +1124,112 @@ func TestSuspendActor(t *testing.T) { ActorTemplateNamespace: ns, ActorTemplateName: "tmpl1", Status: ateapipb.Actor_STATUS_SUSPENDED, - LastSnapshot: fmt.Sprintf("gs://my-bucket/%s/tmpl1/%s/", ns, id), + LatestSnapshotInfo: &ateapipb.SnapshotInfo{ + Type: ateapipb.SnapshotType_SNAPSHOT_TYPE_EXTERNAL, + Data: &ateapipb.SnapshotInfo_External{ + External: &ateapipb.ExternalSnapshotInfo{ + SnapshotUriPrefix: fmt.Sprintf("gs://fake-fake-fake/%s/", id), + }, + }, + }, }, } - if diff := cmp.Diff(want, getResp, protocmp.Transform(), protocmp.IgnoreFields(&ateapipb.Actor{}, "version", "last_snapshot", "ateom_pod_uid")); diff != "" { + if diff := cmp.Diff(want, getResp, + protocmp.Transform(), + protocmp.IgnoreFields(&ateapipb.Actor{}, "version"), + protocmp.IgnoreFields(&ateapipb.Actor{}, "ateom_pod_uid"), + protocmp.FilterField(&ateapipb.ExternalSnapshotInfo{}, "snapshot_uri_prefix", cmp.Comparer(func(x, y string) bool { + return strings.HasPrefix(y, x) + })), + ); diff != "" { t.Errorf("GetActor response mismatch (-want +got):\n%s", diff) } +} + +// TestPauseActor tests the full workflow of pausing a running actor. +// Workflow: +// 1. Creates a mock ActorTemplate. +// 2. Creates a mock Atelet Pod on 'node1'. +// 3. Creates a mock worker Pod on 'node1'. +// 4. Waits for the WorkerPoolSyncer to mirror the worker to Redis. +// 5. Creates an actor. +// 6. Calls ResumeActor to transition it to RUNNING. +// 7. Calls PauseActor RPC. +// 8. Verifies that the fake Atelet received the Pause call. +func TestPauseActor(t *testing.T) { + ns := namespaceForTest("ns-pause") + tc := setupTest(t, ns) + defer tc.cleanup() + + createTemplate(t, tc, ns) + + createWorkerPod(t, tc, ns, "worker-1", "node1") + + _, err := tc.client.CreateActor(context.Background(), &ateapipb.CreateActorRequest{ + ActorTemplateNamespace: ns, + ActorTemplateName: "tmpl1", + ActorId: "id1", + }) + if err != nil { + t.Fatalf("CreateActor failed: %v", err) + } + id := "id1" + + // Resume first to make it running + _, err = tc.client.ResumeActor(context.Background(), &ateapipb.ResumeActorRequest{ + ActorId: id, + }) + if err != nil { + t.Fatalf("ResumeActor failed: %v", err) + } + + // Pause + _, err = tc.client.PauseActor(context.Background(), &ateapipb.PauseActorRequest{ + ActorId: id, + }) + if err != nil { + t.Fatalf("PauseActor failed: %v", err) + } + if !tc.fakeAtelet.CheckpointCalled { + t.Errorf("expected atelet Checkpoint to be called") + } + + getResp, err := tc.client.GetActor(context.Background(), &ateapipb.GetActorRequest{ + ActorId: id, + }) + if err != nil { + t.Fatalf("GetActor failed: %v", err) + } + want := &ateapipb.GetActorResponse{ + Actor: &ateapipb.Actor{ + ActorId: id, + ActorTemplateNamespace: ns, + ActorTemplateName: "tmpl1", + Status: ateapipb.Actor_STATUS_PAUSED, + LatestSnapshotInfo: &ateapipb.SnapshotInfo{ + Type: ateapipb.SnapshotType_SNAPSHOT_TYPE_LOCAL, + Data: &ateapipb.SnapshotInfo_Local{ + Local: &ateapipb.LocalSnapshotInfo{ + SnapshotPrefix: "id1", + NodeVmsWithLocalSnapshots: []string{"node1"}, + }, + }, + }, + }, + } + + if diff := cmp.Diff(want, getResp, + protocmp.Transform(), + protocmp.IgnoreFields(&ateapipb.Actor{}, "version"), + protocmp.IgnoreFields(&ateapipb.Actor{}, "ateom_pod_uid"), + protocmp.FilterField(&ateapipb.LocalSnapshotInfo{}, "snapshot_prefix", cmp.Comparer(func(x, y string) bool { + return strings.HasPrefix(y, x) + })), + ); diff != "" { + t.Errorf("GetActor response mismatch (-want +got):\n%s", diff) + } } // TestValidation tests the negative validation cases for all gRPC methods. diff --git a/cmd/ateapi/internal/controlapi/pause_actor.go b/cmd/ateapi/internal/controlapi/pause_actor.go new file mode 100644 index 000000000..74f023298 --- /dev/null +++ b/cmd/ateapi/internal/controlapi/pause_actor.go @@ -0,0 +1,51 @@ +// Copyright 2026 Google LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package controlapi + +import ( + "context" + "errors" + + "github.com/agent-substrate/substrate/cmd/ateapi/internal/store" + "github.com/agent-substrate/substrate/pkg/proto/ateapipb" + "google.golang.org/grpc/codes" + "google.golang.org/grpc/status" +) + +func (s *Service) PauseActor(ctx context.Context, req *ateapipb.PauseActorRequest) (*ateapipb.PauseActorResponse, error) { + if err := validatePauseActorRequest(req); err != nil { + return nil, err + } + + actor, err := s.actorWorkflow.PauseActor(ctx, req.GetActorId()) + if err != nil { + if errors.Is(err, store.ErrPersistenceRetry) { + return nil, status.Error(codes.Aborted, "concurrent update conflict, please retry") + } + if errors.Is(err, store.ErrNotFound) { + return nil, status.Errorf(codes.NotFound, "Actor %s not found", req.GetActorId()) + } + return nil, err + } + + return &ateapipb.PauseActorResponse{Actor: actor}, nil +} + +func validatePauseActorRequest(req *ateapipb.PauseActorRequest) error { + if req.GetActorId() == "" { + return status.Error(codes.InvalidArgument, "id is required") + } + return nil +} diff --git a/cmd/ateapi/internal/controlapi/syncer.go b/cmd/ateapi/internal/controlapi/syncer.go index d693d347c..60694b5ec 100644 --- a/cmd/ateapi/internal/controlapi/syncer.go +++ b/cmd/ateapi/internal/controlapi/syncer.go @@ -120,6 +120,7 @@ func (s *WorkerPoolSyncer) syncWorkerToStore(ctx context.Context, pod *corev1.Po WorkerPod: pod.Name, Ip: pod.Status.PodIP, WorkerPodUid: string(pod.UID), + NodeName: pod.Spec.NodeName, }) if err != nil && !errors.Is(err, store.ErrAlreadyExists) { slog.ErrorContext(ctx, "Failed to create worker in store", slog.Any("err", err)) diff --git a/cmd/ateapi/internal/controlapi/syncer_test.go b/cmd/ateapi/internal/controlapi/syncer_test.go index 6ac7ed0ee..c05b6f110 100644 --- a/cmd/ateapi/internal/controlapi/syncer_test.go +++ b/cmd/ateapi/internal/controlapi/syncer_test.go @@ -183,7 +183,15 @@ func TestSyncer_DeleteBoundWorker_ClearsActor(t *testing.T) { ActorId: actorID, ActorTemplateNamespace: ns, ActorTemplateName: "tmpl", Status: ateapipb.Actor_STATUS_RUNNING, AteomPodNamespace: ns, AteomPodName: pod, AteomPodIp: ip, - LastSnapshot: "gs://snapshots/last", InProgressSnapshot: "gs://snapshots/partial", + InProgressSnapshot: "gs://snapshots/partial", + LatestSnapshotInfo: &ateapipb.SnapshotInfo{ + Type: ateapipb.SnapshotType_SNAPSHOT_TYPE_EXTERNAL, + Data: &ateapipb.SnapshotInfo_External{ + External: &ateapipb.ExternalSnapshotInfo{ + SnapshotUriPrefix: "gs://snapshots/last", + }, + }, + }, }); err != nil { t.Fatalf("create actor: %v", err) } @@ -210,7 +218,7 @@ func TestSyncer_DeleteBoundWorker_ClearsActor(t *testing.T) { if got.AteomPodName != "" || got.AteomPodNamespace != "" || got.AteomPodIp != "" || got.InProgressSnapshot != "" { t.Errorf("bind fields not cleared: %+v", got) } - if got.LastSnapshot == "" { - t.Errorf("LastSnapshot must be preserved") + if got.GetLatestSnapshotInfo().GetExternal().SnapshotUriPrefix == "" { + t.Errorf("External SnapshotUriPrefix must be preserved") } } diff --git a/cmd/ateapi/internal/controlapi/workflow.go b/cmd/ateapi/internal/controlapi/workflow.go index 37abb258a..1e8a23f2c 100644 --- a/cmd/ateapi/internal/controlapi/workflow.go +++ b/cmd/ateapi/internal/controlapi/workflow.go @@ -191,6 +191,35 @@ func (w *ActorWorkflow) SuspendActor(ctx context.Context, id string) (*ateapipb. return state.Actor, nil } +// PauseActor executes the workflow to pause a running actor. Idempotent. +func (w *ActorWorkflow) PauseActor(ctx context.Context, id string) (*ateapipb.Actor, error) { + input := &PauseInput{ + ActorID: id, + } + state := &PauseState{} + + // Acquire lock and get the timeout context for the workflow + // Lock TTL is 7 seconds, with 2 seconds padding for workflow timeout + ctx, releaseLock, err := w.acquireActorLock(ctx, id, 30*time.Second, 2*time.Second) + if err != nil { + return nil, err + } + defer releaseLock() + + steps := []WorkflowStep[*PauseInput, *PauseState]{ + &LoadActorForPauseStep{store: w.store, actorTemplateLister: w.actorTemplateLister}, + &MarkPausingStep{store: w.store}, + &CallAteletPauseStep{dialer: w.dialer}, + &FinalizePausedStep{store: w.store}, + } + + if err := RunWorkflow(ctx, input, state, steps); err != nil { + return nil, err + } + + return state.Actor, nil +} + func (w *ActorWorkflow) acquireActorLock(ctx context.Context, id string, ttl time.Duration, padding time.Duration) (context.Context, func(), error) { lockKey := "lock:actor:" + id lockValue := uuid.New().String() diff --git a/cmd/ateapi/internal/controlapi/workflow_pause.go b/cmd/ateapi/internal/controlapi/workflow_pause.go new file mode 100644 index 000000000..4617c465e --- /dev/null +++ b/cmd/ateapi/internal/controlapi/workflow_pause.go @@ -0,0 +1,262 @@ +// Copyright 2026 Google LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package controlapi + +import ( + "context" + "crypto/rand" + "errors" + "fmt" + "log/slog" + "time" + + "github.com/agent-substrate/substrate/cmd/ateapi/internal/store" + "github.com/agent-substrate/substrate/internal/proto/ateletpb" + atev1alpha1 "github.com/agent-substrate/substrate/pkg/api/v1alpha1" + listersv1alpha1 "github.com/agent-substrate/substrate/pkg/client/listers/api/v1alpha1" + "github.com/agent-substrate/substrate/pkg/proto/ateapipb" + "k8s.io/apimachinery/pkg/util/wait" +) + +// PauseInput holds the immutable parameters requested by the client. +type PauseInput struct { + ActorID string +} + +// PauseState holds the mutable state loaded and modified during execution. +type PauseState struct { + Actor *ateapipb.Actor + ActorTemplate *atev1alpha1.ActorTemplate +} + +type LoadActorForPauseStep struct { + store store.Interface + actorTemplateLister listersv1alpha1.ActorTemplateLister +} + +func (s *LoadActorForPauseStep) Name() string { return "LoadActorForPause" } +func (s *LoadActorForPauseStep) IsComplete(ctx context.Context, input *PauseInput, state *PauseState) (bool, error) { + // Always run to get the freshest state + return false, nil +} +func (s *LoadActorForPauseStep) Execute(ctx context.Context, input *PauseInput, state *PauseState) error { + actor, err := s.store.GetActor(ctx, input.ActorID) + if err != nil { + return err + } + state.Actor = actor + + actorTemplate, err := s.actorTemplateLister.ActorTemplates(actor.GetActorTemplateNamespace()).Get(actor.GetActorTemplateName()) + if err != nil { + return fmt.Errorf("while getting ActorTemplate: %w", err) + } + state.ActorTemplate = actorTemplate + + return nil +} + +func (s *LoadActorForPauseStep) RetryBackoff() *wait.Backoff { return nil } + +type MarkPausingStep struct { + store store.Interface +} + +func (s *MarkPausingStep) Name() string { return "MarkPausing" } +func (s *MarkPausingStep) IsComplete(ctx context.Context, input *PauseInput, state *PauseState) (bool, error) { + // Fast forward if we've already marked our intent or if we are further along. + return state.Actor.GetStatus() == ateapipb.Actor_STATUS_PAUSING || state.Actor.GetStatus() == ateapipb.Actor_STATUS_PAUSED, nil +} +func (s *MarkPausingStep) Execute(ctx context.Context, input *PauseInput, state *PauseState) error { + if state.Actor.GetStatus() != ateapipb.Actor_STATUS_RUNNING { + return nil + } + + state.Actor.Status = ateapipb.Actor_STATUS_PAUSING + state.Actor.InProgressSnapshot = fmt.Sprintf("%s-%s-%s", state.Actor.GetActorId(), time.Now().Format(time.RFC3339), rand.Text()) + return s.store.UpdateActor(ctx, state.Actor, state.Actor.GetVersion()) +} + +func (s *MarkPausingStep) RetryBackoff() *wait.Backoff { return nil } + +type CallAteletPauseStep struct { + dialer *AteletDialer +} + +func (s *CallAteletPauseStep) Name() string { return "CallAteletPause" } +func (s *CallAteletPauseStep) IsComplete(ctx context.Context, input *PauseInput, state *PauseState) (bool, error) { + // If we are already PAUSED, we've already called Atelet + return state.Actor.GetStatus() == ateapipb.Actor_STATUS_PAUSED, nil +} +func (s *CallAteletPauseStep) Execute(ctx context.Context, input *PauseInput, state *PauseState) error { + if state.Actor.GetAteomPodNamespace() == "" { + return fmt.Errorf("actor is in PAUSING state but has no active worker") + } + + ateletConn, err := s.dialer.DialForWorker(state.Actor.GetAteomPodNamespace(), state.Actor.GetAteomPodName()) + if err != nil { + if errors.Is(err, ErrWorkerPodNotFound) { + slog.Warn("Skipping pause for dangling worker pod", "namespace", state.Actor.GetAteomPodNamespace(), "pod", state.Actor.GetAteomPodName()) + return nil + } + return fmt.Errorf("while getting atelet conn for worker pod: %w", err) + } + client := ateletpb.NewAteomHerderClient(ateletConn) + + runscCfg := &ateletpb.RunscConfig{} + if state.ActorTemplate.Spec.Runsc.AMD64 != nil { + runscCfg.Amd64 = &ateletpb.RunscPlatformConfig{ + Sha256Hash: state.ActorTemplate.Spec.Runsc.AMD64.SHA256Hash, + Url: state.ActorTemplate.Spec.Runsc.AMD64.URL, + } + } + if state.ActorTemplate.Spec.Runsc.ARM64 != nil { + runscCfg.Arm64 = &ateletpb.RunscPlatformConfig{ + Sha256Hash: state.ActorTemplate.Spec.Runsc.ARM64.SHA256Hash, + Url: state.ActorTemplate.Spec.Runsc.ARM64.URL, + } + } + if state.ActorTemplate.Spec.Runsc.Authentication.GCP != nil { + authnCfg := &ateletpb.AuthenticationConfig{} + authnCfg.Gcp = &ateletpb.GCPAuthenticationConfig{Use: true} + runscCfg.Authentication = authnCfg + } + + req := &ateletpb.CheckpointRequest{ + TargetAteomUid: state.Actor.GetAteomPodUid(), + ActorTemplateNamespace: state.Actor.GetActorTemplateNamespace(), + ActorTemplateName: state.Actor.GetActorTemplateName(), + ActorId: state.Actor.GetActorId(), + Runsc: runscCfg, + Spec: &ateletpb.WorkloadSpec{ + PauseImage: state.ActorTemplate.Spec.PauseImage, + }, + Type: ateletpb.CheckpointType_CHECKPOINT_TYPE_LOCAL, + Config: &ateletpb.CheckpointRequest_LocalConfig{ + LocalConfig: &ateletpb.LocalCheckpointConfiguration{ + SnapshotPrefix: state.Actor.InProgressSnapshot, + }, + }, + } + for _, ctr := range state.ActorTemplate.Spec.Containers { + ateletCtr := &ateletpb.Container{ + Name: ctr.Name, + Image: ctr.Image, + Command: ctr.Command, + } + for _, env := range ctr.Env { + var val string + if env.Value != nil { + val = *env.Value + } + ateletEnv := &ateletpb.EnvEntry{ + Name: env.Name, + Value: val, + } + ateletCtr.Env = append(ateletCtr.Env, ateletEnv) + } + req.Spec.Containers = append(req.Spec.Containers, ateletCtr) + } + _, err = client.Checkpoint(ctx, req) + if err != nil { + return fmt.Errorf("while checkpointing workload: %w", err) + } + + return nil +} + +func (s *CallAteletPauseStep) RetryBackoff() *wait.Backoff { return nil } + +type FinalizePausedStep struct { + store store.Interface +} + +func (s *FinalizePausedStep) Name() string { return "FinalizePaused" } +func (s *FinalizePausedStep) IsComplete(ctx context.Context, input *PauseInput, state *PauseState) (bool, error) { + // The workflow is completely done ONLY if the status is PAUSED *and* we've successfully freed the worker. + return state.Actor.GetStatus() == ateapipb.Actor_STATUS_PAUSED && state.Actor.GetAteomPodNamespace() == "", nil +} +func (s *FinalizePausedStep) Execute(ctx context.Context, input *PauseInput, state *PauseState) error { + latestActor, err := s.store.GetActor(ctx, input.ActorID) + if err != nil { + return err + } + + // 1. Free the worker (if it hasn't been freed yet) + if latestActor.GetAteomPodNamespace() != "" { + workerNs := latestActor.GetAteomPodNamespace() + workerPod := latestActor.GetAteomPodName() + + workerPool := state.ActorTemplate.Spec.WorkerPoolRef.Name + + worker, err := s.store.GetWorker(ctx, workerNs, workerPool, workerPod) + nodeName := "" + if err != nil { + if !errors.Is(err, store.ErrNotFound) { + return fmt.Errorf("while getting worker for release: %w", err) + } + slog.Warn("Worker already gone during finalize pause, skipping release", "worker", workerPod) + } else { + // TODO(dberkov) - what if worker does not belong to this actor? + nodeName = worker.GetNodeName() + // Only free it if it still belongs to us + if worker.GetActorId() == input.ActorID { + worker.ActorNamespace = "" + worker.ActorTemplate = "" + worker.ActorId = "" + + err = s.store.UpdateWorker(ctx, worker, worker.Version) + if err != nil { + return err + } + } + } + + // 2. Safely clear ActiveWorker now that the worker object in DB is freed + latestActor, err = s.store.GetActor(ctx, input.ActorID) + if err != nil { + return err + } + latestActor.Status = ateapipb.Actor_STATUS_PAUSED + // TODO(dberkov) - what if we still don't know the node name? Maybe move to CRASHED status? + if nodeName == "" { + slog.Warn("Node name not found during finalize pause", "actor", input.ActorID) + } + // TODO(dberkov) - what if InProgressSnapshot is empty? That shouldn't be possible. + if latestActor.InProgressSnapshot != "" { + latestActor.LatestSnapshotInfo = &ateapipb.SnapshotInfo{ + Type: ateapipb.SnapshotType_SNAPSHOT_TYPE_LOCAL, + Data: &ateapipb.SnapshotInfo_Local{ + Local: &ateapipb.LocalSnapshotInfo{ + SnapshotPrefix: latestActor.InProgressSnapshot, + NodeVmsWithLocalSnapshots: []string{nodeName}, + }, + }, + } + latestActor.InProgressSnapshot = "" + } + latestActor.AteomPodNamespace = "" + latestActor.AteomPodName = "" + latestActor.AteomPodIp = "" + err = s.store.UpdateActor(ctx, latestActor, latestActor.GetVersion()) + if err != nil { + return err + } + } + + state.Actor = latestActor + return nil +} + +func (s *FinalizePausedStep) RetryBackoff() *wait.Backoff { return nil } diff --git a/cmd/ateapi/internal/controlapi/workflow_resume.go b/cmd/ateapi/internal/controlapi/workflow_resume.go index 9f06eec0d..83678d7ed 100644 --- a/cmd/ateapi/internal/controlapi/workflow_resume.go +++ b/cmd/ateapi/internal/controlapi/workflow_resume.go @@ -20,6 +20,7 @@ import ( "fmt" "log/slog" "math/rand" + "slices" "time" "github.com/agent-substrate/substrate/cmd/ateapi/internal/store" @@ -103,7 +104,7 @@ func (s *AssignWorkerStep) Execute(ctx context.Context, input *ResumeInput, stat // If not, find a free one using randomized shuffling if assignedWorker == nil { - pickedWorker := s.findFreeWorker(workers, state.ActorTemplate.Spec.WorkerPoolRef.Namespace, state.ActorTemplate.Spec.WorkerPoolRef.Name) + pickedWorker := s.findFreeWorker(workers, state.ActorTemplate.Spec.WorkerPoolRef.Namespace, state.ActorTemplate.Spec.WorkerPoolRef.Name, state.Actor.GetLatestSnapshotInfo().GetLocal().GetNodeVmsWithLocalSnapshots()) if pickedWorker == nil { return status.Errorf(codes.FailedPrecondition, "no free workers available") } @@ -141,11 +142,13 @@ func (s *AssignWorkerStep) RetryBackoff() *wait.Backoff { } } -func (s *AssignWorkerStep) findFreeWorker(workers []*ateapipb.Worker, workerPoolNamespace, workerPoolName string) *ateapipb.Worker { +func (s *AssignWorkerStep) findFreeWorker(workers []*ateapipb.Worker, workerPoolNamespace, workerPoolName string, nodesRestrictions []string) *ateapipb.Worker { var freeWorkers []*ateapipb.Worker for _, worker := range workers { if worker.GetActorId() == "" && worker.GetWorkerPool() == workerPoolName && worker.GetWorkerNamespace() == workerPoolNamespace { - freeWorkers = append(freeWorkers, worker) + if len(nodesRestrictions) == 0 || slices.Contains(nodesRestrictions, worker.GetNodeName()) { + freeWorkers = append(freeWorkers, worker) + } } } @@ -199,7 +202,7 @@ func (s *CallAteletRestoreStep) Execute(ctx context.Context, input *ResumeInput, runscCfg.Authentication = authnCfg } - if state.Actor.LastSnapshot != "" { + if state.Actor.GetLatestSnapshotInfo().GetType() != ateapipb.SnapshotType_SNAPSHOT_TYPE_UNSPECIFIED { slog.InfoContext(ctx, "Actor has snapshot; Restoring from snapshot") req := &ateletpb.RestoreRequest{ @@ -209,8 +212,26 @@ func (s *CallAteletRestoreStep) Execute(ctx context.Context, input *ResumeInput, ActorId: state.Actor.GetActorId(), Runsc: runscCfg, Spec: workloadSpec, - SnapshotUriPrefix: state.Actor.GetLastSnapshot(), } + switch state.Actor.GetLatestSnapshotInfo().GetType() { + case ateapipb.SnapshotType_SNAPSHOT_TYPE_LOCAL: + req.Type = ateletpb.CheckpointType_CHECKPOINT_TYPE_LOCAL + req.Config = &ateletpb.RestoreRequest_LocalConfig{ + LocalConfig: &ateletpb.LocalCheckpointConfiguration{ + SnapshotPrefix: state.Actor.GetLatestSnapshotInfo().GetLocal().SnapshotPrefix, + }, + } + case ateapipb.SnapshotType_SNAPSHOT_TYPE_EXTERNAL: + req.Type = ateletpb.CheckpointType_CHECKPOINT_TYPE_EXTERNAL + req.Config = &ateletpb.RestoreRequest_ExternalConfig{ + ExternalConfig: &ateletpb.ExternalCheckpointConfiguration{ + SnapshotUriPrefix: state.Actor.GetLatestSnapshotInfo().GetExternal().SnapshotUriPrefix, + }, + } + default: + return fmt.Errorf("unsupported snapshot type: %v", state.Actor.GetLatestSnapshotInfo().GetType()) + } + _, err = client.Restore(ctx, req) if err != nil { return fmt.Errorf("while restoring workload: %w", err) @@ -228,7 +249,12 @@ func (s *CallAteletRestoreStep) Execute(ctx context.Context, input *ResumeInput, ActorId: state.Actor.GetActorId(), Runsc: runscCfg, Spec: workloadSpec, - SnapshotUriPrefix: snapshot, + Type: ateletpb.CheckpointType_CHECKPOINT_TYPE_EXTERNAL, + Config: &ateletpb.RestoreRequest_ExternalConfig{ + ExternalConfig: &ateletpb.ExternalCheckpointConfiguration{ + SnapshotUriPrefix: snapshot, + }, + }, } _, err = client.Restore(ctx, req) if err != nil { diff --git a/cmd/ateapi/internal/controlapi/workflow_suspend.go b/cmd/ateapi/internal/controlapi/workflow_suspend.go index d8210574b..cd3d63870 100644 --- a/cmd/ateapi/internal/controlapi/workflow_suspend.go +++ b/cmd/ateapi/internal/controlapi/workflow_suspend.go @@ -144,7 +144,12 @@ func (s *CallAteletSuspendStep) Execute(ctx context.Context, input *SuspendInput Spec: &ateletpb.WorkloadSpec{ PauseImage: state.ActorTemplate.Spec.PauseImage, }, - SnapshotUriPrefix: state.Actor.GetInProgressSnapshot(), + Type: ateletpb.CheckpointType_CHECKPOINT_TYPE_EXTERNAL, + Config: &ateletpb.CheckpointRequest_ExternalConfig{ + ExternalConfig: &ateletpb.ExternalCheckpointConfiguration{ + SnapshotUriPrefix: state.Actor.GetInProgressSnapshot(), + }, + }, } for _, ctr := range state.ActorTemplate.Spec.Containers { ateletCtr := &ateletpb.Container{ @@ -224,7 +229,14 @@ func (s *FinalizeSuspendedStep) Execute(ctx context.Context, input *SuspendInput } latestActor.Status = ateapipb.Actor_STATUS_SUSPENDED if latestActor.InProgressSnapshot != "" { - latestActor.LastSnapshot = latestActor.InProgressSnapshot + latestActor.LatestSnapshotInfo = &ateapipb.SnapshotInfo{ + Type: ateapipb.SnapshotType_SNAPSHOT_TYPE_EXTERNAL, + Data: &ateapipb.SnapshotInfo_External{ + External: &ateapipb.ExternalSnapshotInfo{ + SnapshotUriPrefix: latestActor.InProgressSnapshot, + }, + }, + } latestActor.InProgressSnapshot = "" } latestActor.AteomPodNamespace = "" diff --git a/cmd/ateapi/internal/store/ateredis/ateredis_test.go b/cmd/ateapi/internal/store/ateredis/ateredis_test.go index 61e058887..eb41fc6d4 100644 --- a/cmd/ateapi/internal/store/ateredis/ateredis_test.go +++ b/cmd/ateapi/internal/store/ateredis/ateredis_test.go @@ -408,14 +408,28 @@ func TestListActors(t *testing.T) { ActorTemplateNamespace: "ns1", ActorTemplateName: "tmpl1", Status: ateapipb.Actor_STATUS_SUSPENDED, - LastSnapshot: "gs://b1/f1", + LatestSnapshotInfo: &ateapipb.SnapshotInfo{ + Type: ateapipb.SnapshotType_SNAPSHOT_TYPE_EXTERNAL, + Data: &ateapipb.SnapshotInfo_External{ + External: &ateapipb.ExternalSnapshotInfo{ + SnapshotUriPrefix: "gs://b1/f1", + }, + }, + }, } actor2 := &ateapipb.Actor{ ActorId: "id2", ActorTemplateNamespace: "ns1", ActorTemplateName: "tmpl1", Status: ateapipb.Actor_STATUS_SUSPENDED, - LastSnapshot: "gs://b1/f2", + LatestSnapshotInfo: &ateapipb.SnapshotInfo{ + Type: ateapipb.SnapshotType_SNAPSHOT_TYPE_EXTERNAL, + Data: &ateapipb.SnapshotInfo_External{ + External: &ateapipb.ExternalSnapshotInfo{ + SnapshotUriPrefix: "gs://b1/f2", + }, + }, + }, } if err := s.CreateActor(ctx, actor1); err != nil { diff --git a/cmd/atelet/main.go b/cmd/atelet/main.go index c4425bade..f300994e2 100644 --- a/cmd/atelet/main.go +++ b/cmd/atelet/main.go @@ -21,6 +21,7 @@ import ( "encoding/hex" "errors" "fmt" + "io" "log/slog" "net" "os" @@ -390,9 +391,53 @@ func (s *AteomHerder) Checkpoint(ctx context.Context, req *ateletpb.CheckpointRe return nil, fmt.Errorf("while calling ateom.CheckpointWorkload: %w", err) } - prefix := strings.TrimSuffix(req.GetSnapshotUriPrefix(), "/") ns, tmpl, actorID := req.GetActorTemplateNamespace(), req.GetActorTemplateName(), req.GetActorId() + switch req.GetType() { + case ateletpb.CheckpointType_CHECKPOINT_TYPE_EXTERNAL: + if err := s.uploadExternalCheckpoint(ctx, req, checkpointDir); err != nil { + return nil, err + } + case ateletpb.CheckpointType_CHECKPOINT_TYPE_LOCAL: + if err := s.moveLocalCheckpoint(ctx, req, checkpointDir); err != nil { + return nil, err + } + default: + return nil, fmt.Errorf("unexpected checkpoint type: %v", req.GetType()) + } + + if err := resetActorDirs(ns, tmpl, actorID); err != nil { + return nil, fmt.Errorf("while resetting actor dirs: %w", err) + } + + return &ateletpb.CheckpointResponse{}, nil +} + +func (s *AteomHerder) moveLocalCheckpoint(ctx context.Context, req *ateletpb.CheckpointRequest, checkpointDir string) error { + localCheckpointPath := filepath.Join(ateompath.LocalCheckpointsDir(req.GetActorTemplateNamespace(), req.GetActorTemplateName(), req.GetActorId()), req.GetLocalConfig().GetSnapshotPrefix()) + if err := os.MkdirAll(localCheckpointPath, 0o700); err != nil { + return fmt.Errorf("while creating local checkpoint directory: %w", err) + } + + ns, tmpl := req.GetActorTemplateNamespace(), req.GetActorTemplateName() + + for _, fileName := range []string{"checkpoint.img", "pages.img", "pages_meta.img"} { + src := filepath.Join(checkpointDir, fileName) + dst := filepath.Join(localCheckpointPath, fileName) + recordSnapshotSize(ctx, strings.TrimSuffix(fileName, ".img"), src, ns, tmpl) + + if err := os.Rename(src, dst); err != nil { + return fmt.Errorf("failed to move %s to %s: %w", src, dst, err) + } + } + + return nil +} + +func (s *AteomHerder) uploadExternalCheckpoint(ctx context.Context, req *ateletpb.CheckpointRequest, checkpointDir string) error { + ns, tmpl := req.GetActorTemplateNamespace(), req.GetActorTemplateName() + prefix := strings.TrimSuffix(req.GetExternalConfig().GetSnapshotUriPrefix(), "/") + checkpointImgPath := filepath.Join(checkpointDir, "checkpoint.img") pagesImgPath := filepath.Join(checkpointDir, "pages.img") pagesMetaImgPath := filepath.Join(checkpointDir, "pages_meta.img") @@ -404,7 +449,7 @@ func (s *AteomHerder) Checkpoint(ctx context.Context, req *ateletpb.CheckpointRe prefix+"/checkpoint.img.zstd", checkpointImgPath, ); err != nil { - return nil, fmt.Errorf("while uploading checkpoint.img to GCS: %w", err) + return fmt.Errorf("while uploading checkpoint.img to GCS: %w", err) } recordSnapshotSize(ctx, "pages", pagesImgPath, ns, tmpl) @@ -412,21 +457,16 @@ func (s *AteomHerder) Checkpoint(ctx context.Context, req *ateletpb.CheckpointRe prefix+"/pages.img.zstd", pagesImgPath, ); err != nil { - return nil, err + return err } recordSnapshotSize(ctx, "pages_meta", pagesMetaImgPath, ns, tmpl) if err := uploadIfExists(ctx, s.gcsClient, prefix+"/pages_meta.img.zstd", pagesMetaImgPath, ); err != nil { - return nil, err - } - - if err := resetActorDirs(ns, tmpl, actorID); err != nil { - return nil, fmt.Errorf("while resetting actor dirs: %w", err) + return err } - - return &ateletpb.CheckpointResponse{}, nil + return nil } func (s *AteomHerder) Restore(ctx context.Context, req *ateletpb.RestoreRequest) (*ateletpb.RestoreResponse, error) { @@ -446,27 +486,19 @@ func (s *AteomHerder) Restore(ctx context.Context, req *ateletpb.RestoreRequest) } checkpointDir := ateompath.RestoreStateDir(req.GetActorTemplateNamespace(), req.GetActorTemplateName(), req.GetActorId()) - checkpointImgPath := filepath.Join(checkpointDir, "checkpoint.img") - pagesImgPath := filepath.Join(checkpointDir, "pages.img") - pagesMetaImgPath := filepath.Join(checkpointDir, "pages_meta.img") - - prefix := strings.TrimSuffix(req.GetSnapshotUriPrefix(), "/") - g, gCtx := errgroup.WithContext(ctx) - for _, dl := range []struct{ remote, local string }{ - {prefix + "/checkpoint.img.zstd", checkpointImgPath}, - {prefix + "/pages.img.zstd", pagesImgPath}, - {prefix + "/pages_meta.img.zstd", pagesMetaImgPath}, - } { - dl := dl - g.Go(func() error { - if err := ategcs.FetchLocalFileFromGCSWithZstd(gCtx, s.gcsClient, dl.remote, dl.local); err != nil { - return fmt.Errorf("while downloading %s from GCS: %w", filepath.Base(dl.remote), err) - } - return nil - }) - } - if err := g.Wait(); err != nil { - return nil, err + switch req.GetType() { + case ateletpb.CheckpointType_CHECKPOINT_TYPE_EXTERNAL: + if err := s.downloadExternalCheckpoint(ctx, req.GetExternalConfig().GetSnapshotUriPrefix(), checkpointDir); err != nil { + return nil, err + } + case ateletpb.CheckpointType_CHECKPOINT_TYPE_LOCAL: + // TODO(dberkov): the old pause checkpoint files are not deleted after they are copied to checkpointDir. This needs to be fixed in following PR. + localCheckpointDir := ateompath.LocalCheckpointsDir(req.GetActorTemplateNamespace(), req.GetActorTemplateName(), req.GetActorId()) + if err := s.copyLocalCheckpoint(ctx, req.GetLocalConfig().GetSnapshotPrefix(), localCheckpointDir, checkpointDir); err != nil { + return nil, err + } + default: + return nil, fmt.Errorf("unexpected checkpoint type: %v", req.GetType()) } if err := s.prepareOCIBundles(ctx, ns, tmpl, actorID, @@ -495,6 +527,73 @@ func (s *AteomHerder) Restore(ctx context.Context, req *ateletpb.RestoreRequest) return &ateletpb.RestoreResponse{}, nil } +func (s *AteomHerder) copyLocalCheckpoint(ctx context.Context, snapshotPrefix string, srcDir, dstDir string) error { + for _, fileName := range []string{"checkpoint.img", "pages.img", "pages_meta.img"} { + if ctx.Err() != nil { + return fmt.Errorf("context cancelled: %w", ctx.Err()) + } + src := filepath.Join(srcDir, snapshotPrefix, fileName) + dst := filepath.Join(dstDir, fileName) + if _, err := copyFile(src, dst); err != nil { + return fmt.Errorf("failed to copy %s to %s: %w", src, dst, err) + } + } + + return nil +} + +func copyFile(src, dst string) (int64, error) { + sourceFileStat, err := os.Stat(src) + if err != nil { + return 0, err + } + + if !sourceFileStat.Mode().IsRegular() { + return 0, fmt.Errorf("%s is not a regular file", src) + } + + source, err := os.Open(src) + if err != nil { + return 0, err + } + defer source.Close() + + destination, err := os.Create(dst) + if err != nil { + return 0, err + } + defer destination.Close() + nBytes, err := io.Copy(destination, source) + return nBytes, err +} + +func (s *AteomHerder) downloadExternalCheckpoint(ctx context.Context, snapshotUriPrefix string, dstDir string) error { + checkpointImgPath := filepath.Join(dstDir, "checkpoint.img") + pagesImgPath := filepath.Join(dstDir, "pages.img") + pagesMetaImgPath := filepath.Join(dstDir, "pages_meta.img") + + prefix := strings.TrimSuffix(snapshotUriPrefix, "/") + g, gCtx := errgroup.WithContext(ctx) + for _, dl := range []struct{ remote, local string }{ + {prefix + "/checkpoint.img.zstd", checkpointImgPath}, + {prefix + "/pages.img.zstd", pagesImgPath}, + {prefix + "/pages_meta.img.zstd", pagesMetaImgPath}, + } { + dl := dl + g.Go(func() error { + if err := ategcs.FetchLocalFileFromGCSWithZstd(gCtx, s.gcsClient, dl.remote, dl.local); err != nil { + return fmt.Errorf("while downloading %s from GCS: %w", filepath.Base(dl.remote), err) + } + return nil + }) + } + if err := g.Wait(); err != nil { + return err + } + + return nil +} + // fetchRunscAndPrep ensures the static files dir exists and downloads the // runsc binary at the version pinned by the request. Returns the local // runsc path. @@ -649,8 +748,17 @@ func validateCheckpointRequest(req *ateletpb.CheckpointRequest) error { if err := validateActorRequest(req.GetActorTemplateNamespace(), req.GetActorTemplateName(), req.GetActorId(), req.GetTargetAteomUid(), req.GetSpec()); err != nil { return err } - if err := resources.ValidateSnapshotURIPrefix(req.GetSnapshotUriPrefix()); err != nil { - return err + switch req.GetType() { + case ateletpb.CheckpointType_CHECKPOINT_TYPE_EXTERNAL: + if err := resources.ValidateSnapshotURIPrefix(req.GetExternalConfig().GetSnapshotUriPrefix()); err != nil { + return err + } + case ateletpb.CheckpointType_CHECKPOINT_TYPE_LOCAL: + if req.GetLocalConfig().GetSnapshotPrefix() == "" { + return fmt.Errorf("snapshot prefix must be non-empty for type %s", req.GetType().String()) + } + default: + return fmt.Errorf("invalid checkpoint type: %v", req.GetType()) } return nil } @@ -659,8 +767,17 @@ func validateRestoreRequest(req *ateletpb.RestoreRequest) error { if err := validateActorRequest(req.GetActorTemplateNamespace(), req.GetActorTemplateName(), req.GetActorId(), req.GetTargetAteomUid(), req.GetSpec()); err != nil { return err } - if err := resources.ValidateSnapshotURIPrefix(req.GetSnapshotUriPrefix()); err != nil { - return err + switch req.GetType() { + case ateletpb.CheckpointType_CHECKPOINT_TYPE_EXTERNAL: + if err := resources.ValidateSnapshotURIPrefix(req.GetExternalConfig().GetSnapshotUriPrefix()); err != nil { + return err + } + case ateletpb.CheckpointType_CHECKPOINT_TYPE_LOCAL: + if req.GetLocalConfig().GetSnapshotPrefix() == "" { + return fmt.Errorf("snapshot prefix must be non-empty for type %s", req.GetType().String()) + } + default: + return fmt.Errorf("invalid checkpoint type: %v", req.GetType()) } return nil } diff --git a/cmd/atelet/main_test.go b/cmd/atelet/main_test.go index aad83fa61..fbb513ba8 100644 --- a/cmd/atelet/main_test.go +++ b/cmd/atelet/main_test.go @@ -70,7 +70,12 @@ func validCheckpointRequest() *ateletpb.CheckpointRequest { ActorId: "counter-1", TargetAteomUid: "422938ba-8860-4983-a25d-d6bcb0a69d4e", Spec: &ateletpb.WorkloadSpec{Containers: []*ateletpb.Container{{Name: "worker"}}}, - SnapshotUriPrefix: "gs://bucket/actors/1/snapshots/2/", + Type: ateletpb.CheckpointType_CHECKPOINT_TYPE_EXTERNAL, + Config: &ateletpb.CheckpointRequest_ExternalConfig{ + ExternalConfig: &ateletpb.ExternalCheckpointConfiguration{ + SnapshotUriPrefix: "gs://bucket/actors/1/snapshots/2/", + }, + }, } } @@ -81,7 +86,12 @@ func validRestoreRequest() *ateletpb.RestoreRequest { ActorId: "counter-1", TargetAteomUid: "422938ba-8860-4983-a25d-d6bcb0a69d4e", Spec: &ateletpb.WorkloadSpec{Containers: []*ateletpb.Container{{Name: "worker"}}}, - SnapshotUriPrefix: "gs://bucket/actors/1/snapshots/2/", + Type: ateletpb.CheckpointType_CHECKPOINT_TYPE_EXTERNAL, + Config: &ateletpb.RestoreRequest_ExternalConfig{ + ExternalConfig: &ateletpb.ExternalCheckpointConfiguration{ + SnapshotUriPrefix: "gs://bucket/actors/1/snapshots/2/", + }, + }, } } @@ -109,21 +119,32 @@ func TestValidateRunRequest(t *testing.T) { // Checkpoint and Restore must reject a bad snapshot URI prefix even when // every common field is valid. func TestValidateCheckpointRequest(t *testing.T) { + makeReq := func(opts ...func(*ateletpb.CheckpointRequest)) *ateletpb.CheckpointRequest { + r := validCheckpointRequest() + for _, opt := range opts { + opt(r) + } + return r + } + tests := []struct { name string - mutate func(*ateletpb.CheckpointRequest) + req *ateletpb.CheckpointRequest wantErr bool }{ - {"valid", func(*ateletpb.CheckpointRequest) {}, false}, - {"empty snapshot uri", func(r *ateletpb.CheckpointRequest) { r.SnapshotUriPrefix = "" }, true}, - {"bucketless snapshot uri", func(r *ateletpb.CheckpointRequest) { r.SnapshotUriPrefix = "relative/path" }, true}, - {"invalid ateom uid", func(r *ateletpb.CheckpointRequest) { r.TargetAteomUid = "../escape" }, true}, + {"valid", makeReq(), false}, + {"empty snapshot uri", makeReq(func(r *ateletpb.CheckpointRequest) { r.GetExternalConfig().SnapshotUriPrefix = "" }), true}, + {"bucketless snapshot uri", makeReq(func(r *ateletpb.CheckpointRequest) { r.GetExternalConfig().SnapshotUriPrefix = "relative/path" }), true}, + {"invalid ateom uid", makeReq(func(r *ateletpb.CheckpointRequest) { r.TargetAteomUid = "../escape" }), true}, + {"invalid local snapshot prefix", makeReq(func(r *ateletpb.CheckpointRequest) { + r.Type = ateletpb.CheckpointType_CHECKPOINT_TYPE_LOCAL + r.Config = &ateletpb.CheckpointRequest_LocalConfig{LocalConfig: &ateletpb.LocalCheckpointConfiguration{SnapshotPrefix: ""}} + }), true}, + {"unspecified snapshot type", makeReq(func(r *ateletpb.CheckpointRequest) { r.Type = ateletpb.CheckpointType_CHECKPOINT_TYPE_UNSPECIFIED }), true}, } for _, tc := range tests { t.Run(tc.name, func(t *testing.T) { - req := validCheckpointRequest() - tc.mutate(req) - if err := validateCheckpointRequest(req); (err != nil) != tc.wantErr { + if err := validateCheckpointRequest(tc.req); (err != nil) != tc.wantErr { t.Errorf("validateCheckpointRequest err = %v, wantErr %v", err, tc.wantErr) } }) @@ -131,21 +152,32 @@ func TestValidateCheckpointRequest(t *testing.T) { } func TestValidateRestoreRequest(t *testing.T) { + makeReq := func(opts ...func(*ateletpb.RestoreRequest)) *ateletpb.RestoreRequest { + r := validRestoreRequest() + for _, opt := range opts { + opt(r) + } + return r + } + tests := []struct { name string - mutate func(*ateletpb.RestoreRequest) + req *ateletpb.RestoreRequest wantErr bool }{ - {"valid", func(*ateletpb.RestoreRequest) {}, false}, - {"empty snapshot uri", func(r *ateletpb.RestoreRequest) { r.SnapshotUriPrefix = "" }, true}, - {"bucketless snapshot uri", func(r *ateletpb.RestoreRequest) { r.SnapshotUriPrefix = "relative/path" }, true}, - {"invalid ateom uid", func(r *ateletpb.RestoreRequest) { r.TargetAteomUid = "../escape" }, true}, + {"valid", makeReq(), false}, + {"empty snapshot uri", makeReq(func(r *ateletpb.RestoreRequest) { r.GetExternalConfig().SnapshotUriPrefix = "" }), true}, + {"bucketless snapshot uri", makeReq(func(r *ateletpb.RestoreRequest) { r.GetExternalConfig().SnapshotUriPrefix = "relative/path" }), true}, + {"invalid ateom uid", makeReq(func(r *ateletpb.RestoreRequest) { r.TargetAteomUid = "../escape" }), true}, + {"invalid local snapshot prefix", makeReq(func(r *ateletpb.RestoreRequest) { + r.Type = ateletpb.CheckpointType_CHECKPOINT_TYPE_LOCAL + r.Config = &ateletpb.RestoreRequest_LocalConfig{LocalConfig: &ateletpb.LocalCheckpointConfiguration{SnapshotPrefix: ""}} + }), true}, + {"unspecified snapshot type", makeReq(func(r *ateletpb.RestoreRequest) { r.Type = ateletpb.CheckpointType_CHECKPOINT_TYPE_UNSPECIFIED }), true}, } for _, tc := range tests { t.Run(tc.name, func(t *testing.T) { - req := validRestoreRequest() - tc.mutate(req) - if err := validateRestoreRequest(req); (err != nil) != tc.wantErr { + if err := validateRestoreRequest(tc.req); (err != nil) != tc.wantErr { t.Errorf("validateRestoreRequest err = %v, wantErr %v", err, tc.wantErr) } }) diff --git a/cmd/kubectl-ate/internal/cmd/pause.go b/cmd/kubectl-ate/internal/cmd/pause.go new file mode 100644 index 000000000..fc16a517b --- /dev/null +++ b/cmd/kubectl-ate/internal/cmd/pause.go @@ -0,0 +1,28 @@ +// Copyright 2026 Google LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package cmd + +import ( + "github.com/spf13/cobra" +) + +var pauseCmd = &cobra.Command{ + Use: "pause", + Short: "Pause a resource", +} + +func init() { + rootCmd.AddCommand(pauseCmd) +} diff --git a/cmd/kubectl-ate/internal/cmd/pause_actor.go b/cmd/kubectl-ate/internal/cmd/pause_actor.go new file mode 100644 index 000000000..e7a7e75ff --- /dev/null +++ b/cmd/kubectl-ate/internal/cmd/pause_actor.go @@ -0,0 +1,51 @@ +// Copyright 2026 Google LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package cmd + +import ( + "fmt" + + "github.com/agent-substrate/substrate/cmd/kubectl-ate/internal/printer" + "github.com/agent-substrate/substrate/internal/ateclient" + "github.com/agent-substrate/substrate/pkg/proto/ateapipb" + "github.com/spf13/cobra" +) + +var pauseActorCmd = &cobra.Command{ + Use: "actor [actor-id]", + Short: "Pause an actor", + Args: cobra.ExactArgs(1), + RunE: func(cmd *cobra.Command, args []string) error { + ctx := cmd.Context() + apiClient, err := ateclient.NewClient(ctx, kubeconfig, k8sContext, endpoint, traceEnabled) + if err != nil { + return fmt.Errorf("failed to connect to ate-api-server: %w", err) + } + defer apiClient.Close() + + resp, err := apiClient.PauseActor(ctx, &ateapipb.PauseActorRequest{ + ActorId: args[0], + }) + if err != nil { + return fmt.Errorf("failed to pause actor: %w", err) + } + + return printer.PrintActor(resp.GetActor(), outputFmt) + }, +} + +func init() { + pauseCmd.AddCommand(pauseActorCmd) +} diff --git a/docs/architecture.md b/docs/architecture.md index 883a3c7ed..3a94fe5b9 100644 --- a/docs/architecture.md +++ b/docs/architecture.md @@ -326,7 +326,7 @@ Triggered by an inbound request at the Gateway or an explicit API call. 2. **Assignment**: The Control Plane claims a warm worker from the `WorkerPool`. - 3. **Hydration**: The `atelet` supervisor coordinates with the `ateom` process inside the worker pod to restore the `GoldenSnapshot` (for first-run) or the `LastSnapshot` (for recurring runs) into the sandbox. + 3. **Hydration**: The `atelet` supervisor coordinates with the `ateom` process inside the worker pod to restore the `GoldenSnapshot` (for first-run) or the `LatestSnapshotInfo` (for recurring runs) into the sandbox. 4. **Status**: Status transitions to `STATUS_RUNNING`. The actor now has an active Worker IP. @@ -345,7 +345,7 @@ Triggered by an explicit `SuspendActor` call. 3. **Reclaim**: The physical worker is wiped and returned to the `WorkerPool`. 4. **Status**: Status transitions back to `STATUS_SUSPENDED`, now pointing to - the `LastSnapshot` for future resumptions. + the `LatestSnapshotInfo` for future resumptions. ### Phase 4: Deletion diff --git a/internal/ateompath/ateompath.go b/internal/ateompath/ateompath.go index 304d53f5f..d1e0d4084 100644 --- a/internal/ateompath/ateompath.go +++ b/internal/ateompath/ateompath.go @@ -104,6 +104,13 @@ func CheckpointStateDir(actorTemplateNamespace, actorTemplateName, actorID strin ) } +func LocalCheckpointsDir(actorTemplateNamespace, actorTemplateName, actorID string) string { + return filepath.Join( + ActorPath(actorTemplateNamespace, actorTemplateName, actorID), + "local-checkpoint", + ) +} + // RestoreStateDir is the local directory to use to restore an actor from a // checkpoint downloaded from GCS. // diff --git a/internal/controllers/actortemplate_controller.go b/internal/controllers/actortemplate_controller.go index 3df86bc4e..372890116 100644 --- a/internal/controllers/actortemplate_controller.go +++ b/internal/controllers/actortemplate_controller.go @@ -134,8 +134,12 @@ func (r *ActorTemplateReconciler) Reconcile(ctx context.Context, req ctrl.Reques return ctrl.Result{}, fmt.Errorf("while suspending golden actor: %w", err) } + if resp.GetActor().GetLatestSnapshotInfo().GetType() != ateapipb.SnapshotType_SNAPSHOT_TYPE_EXTERNAL { + return ctrl.Result{}, fmt.Errorf("unexpected snapshot type for golden actor: %v", resp.GetActor().GetLatestSnapshotInfo().GetType()) + } + // Transition to PhaseReady - at.Status.GoldenSnapshot = resp.GetActor().GetLastSnapshot() + at.Status.GoldenSnapshot = resp.GetActor().GetLatestSnapshotInfo().GetExternal().SnapshotUriPrefix at.Status.Phase = atev1alpha1.PhaseReady meta.SetStatusCondition(&at.Status.Conditions, metav1.Condition{ Type: "Ready", diff --git a/internal/e2e/suites/demo/demo_test.go b/internal/e2e/suites/demo/demo_test.go index b1c2e5b09..4b807a8f3 100644 --- a/internal/e2e/suites/demo/demo_test.go +++ b/internal/e2e/suites/demo/demo_test.go @@ -26,18 +26,242 @@ import ( metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" ) -func TestDemo3(t *testing.T) { - env, err := e2e.CheckEnv("BUCKET_NAME", "KO_DOCKER_REPO") - if err != nil { - t.Fatalf("CheckEnv failed: %v", err) - } - +func TestActorLifecycle(t *testing.T) { // Create namespace nsObj := e2e.CreateNamespace(t) ctx := context.Background() clients := e2e.GetClients() + // Create actor template. + at, err := createActorTemplate(ctx, t, clients, nsObj) + if err != nil { + t.Fatalf("failed to initialize ActorTemplate: %v", err) + } + + tests := []struct { + name string + f func(ctx context.Context, t *testing.T, clients *e2e.Clients, ns *e2e.Namespace, at *v1alpha1.ActorTemplate) error + }{ + { + name: "CreateActor", + f: createActor, + }, + { + name: "PauseResumeActor", + f: pauseActor, + }, + { + name: "SuspendResumeActor", + f: suspendActor, + }, + } + + for _, tc := range tests { + t.Run(tc.name, func(t *testing.T) { + t.Parallel() + if err := tc.f(ctx, t, clients, nsObj, at); err != nil { + t.Errorf("Test %q failed: %v", tc.name, err) + } + }) + } + +} + +func createActor(ctx context.Context, t *testing.T, clients *e2e.Clients, nsObj *e2e.Namespace, at *v1alpha1.ActorTemplate) error { + // Create an Actor using the ATE API. + actorID := "demo-actor-1-" + nsObj.Name + + t.Logf("Creating Actor %q using Substrate API...", actorID) + createResp, err := clients.SubstrateAPI.CreateActor(ctx, &ateapipb.CreateActorRequest{ + ActorId: actorID, + ActorTemplateNamespace: nsObj.Name, + ActorTemplateName: at.Name, + }) + if err != nil { + t.Fatalf("failed to create Actor: %v", err) + } + t.Logf("Successfully created Actor: %s", createResp.GetActor().GetActorId()) + defer func() { + clients.SubstrateAPI.DeleteActor(ctx, &ateapipb.DeleteActorRequest{ + ActorId: actorID, + }) + }() + + listResp, err := clients.SubstrateAPI.ListActors(ctx, &ateapipb.ListActorsRequest{}) + if err != nil { + t.Fatalf("ListActors RPC failed: %v", err) + } + + var myActors []*ateapipb.Actor + for _, actor := range listResp.GetActors() { + if actor.GetActorTemplateNamespace() == nsObj.Name && actor.GetActorId() == actorID { + myActors = append(myActors, actor) + } + } + + // Check that we have our Actor created. + if len(myActors) != 1 { + t.Fatalf("expected actor %s in namespace %s, got %d actors: %v", actorID, nsObj.Name, len(myActors), myActors) + } + + actor := myActors[0] + if actor.GetActorId() != actorID { + t.Errorf("expected actor ID %s, got %s", actorID, actor.GetActorId()) + } + if actor.GetActorTemplateName() != at.Name { + t.Errorf("expected actor template name %s, got %s", at.Name, actor.GetActorTemplateName()) + } + if actor.Status != ateapipb.Actor_STATUS_SUSPENDED { + t.Errorf("expected actor status to be SUSPENDED, got %v", actor.Status) + } + + t.Logf("Successfully queried Substrate API. Found %d active actors total, %d in our namespace %s.", + len(listResp.GetActors()), len(myActors), nsObj.Name) + + return nil +} + +func pauseActor(ctx context.Context, t *testing.T, clients *e2e.Clients, nsObj *e2e.Namespace, at *v1alpha1.ActorTemplate) error { + actorID := "pause-actor-" + nsObj.Name + + // Creating an actor + t.Logf("Creating Actor %q...", actorID) + if _, err := clients.SubstrateAPI.CreateActor(ctx, &ateapipb.CreateActorRequest{ + ActorId: actorID, + ActorTemplateNamespace: nsObj.Name, + ActorTemplateName: at.Name, + }); err != nil { + t.Fatalf("failed to create Actor: %v", err) + } + waitForActorStatus(ctx, t, clients, actorID, ateapipb.Actor_STATUS_SUSPENDED) + + // Resuming the actor + t.Logf("Resuming Actor %q...", actorID) + if _, err := clients.SubstrateAPI.ResumeActor(ctx, &ateapipb.ResumeActorRequest{ + ActorId: actorID, + }); err != nil { + t.Fatalf("failed to resume Actor: %v", err) + } + waitForActorStatus(ctx, t, clients, actorID, ateapipb.Actor_STATUS_RUNNING) + + // Pausing the actor + t.Logf("Pausing Actor %q...", actorID) + if _, err := clients.SubstrateAPI.PauseActor(ctx, &ateapipb.PauseActorRequest{ + ActorId: actorID, + }); err != nil { + t.Fatalf("failed to pause Actor: %v", err) + } + waitForActorStatus(ctx, t, clients, actorID, ateapipb.Actor_STATUS_PAUSED) + + // Resuming the actor again + t.Logf("Resuming Actor %q again...", actorID) + if _, err := clients.SubstrateAPI.ResumeActor(ctx, &ateapipb.ResumeActorRequest{ + ActorId: actorID, + }); err != nil { + t.Fatalf("failed to resume Actor again: %v", err) + } + waitForActorStatus(ctx, t, clients, actorID, ateapipb.Actor_STATUS_RUNNING) + + // Suspending the actor before deletion + t.Logf("Suspending Actor %q before deletion...", actorID) + if _, err := clients.SubstrateAPI.SuspendActor(ctx, &ateapipb.SuspendActorRequest{ + ActorId: actorID, + }); err != nil { + t.Fatalf("failed to suspend Actor: %v", err) + } + waitForActorStatus(ctx, t, clients, actorID, ateapipb.Actor_STATUS_SUSPENDED) + + // Deleting the actor + t.Logf("Deleting Actor %q...", actorID) + if _, err := clients.SubstrateAPI.DeleteActor(ctx, &ateapipb.DeleteActorRequest{ + ActorId: actorID, + }); err != nil { + t.Fatalf("failed to delete Actor: %v", err) + } + // Verify deletion + if _, err := clients.SubstrateAPI.GetActor(ctx, &ateapipb.GetActorRequest{ + ActorId: actorID, + }); err == nil { + t.Fatalf("expected actor %q to be deleted, but it still exists", actorID) + } + + return nil +} + +func suspendActor(ctx context.Context, t *testing.T, clients *e2e.Clients, nsObj *e2e.Namespace, at *v1alpha1.ActorTemplate) error { + actorID := "suspend-actor-" + nsObj.Name + + // Creating an actor + t.Logf("Creating Actor %q...", actorID) + if _, err := clients.SubstrateAPI.CreateActor(ctx, &ateapipb.CreateActorRequest{ + ActorId: actorID, + ActorTemplateNamespace: nsObj.Name, + ActorTemplateName: at.Name, + }); err != nil { + t.Fatalf("failed to create Actor: %v", err) + } + waitForActorStatus(ctx, t, clients, actorID, ateapipb.Actor_STATUS_SUSPENDED) + + // Resuming the actor + t.Logf("Resuming Actor %q...", actorID) + if _, err := clients.SubstrateAPI.ResumeActor(ctx, &ateapipb.ResumeActorRequest{ + ActorId: actorID, + }); err != nil { + t.Fatalf("failed to resume Actor: %v", err) + } + waitForActorStatus(ctx, t, clients, actorID, ateapipb.Actor_STATUS_RUNNING) + + // Pausing the actor + t.Logf("Pausing Actor %q...", actorID) + if _, err := clients.SubstrateAPI.PauseActor(ctx, &ateapipb.PauseActorRequest{ + ActorId: actorID, + }); err != nil { + t.Fatalf("failed to pause Actor: %v", err) + } + waitForActorStatus(ctx, t, clients, actorID, ateapipb.Actor_STATUS_PAUSED) + + // Resuming the actor again + t.Logf("Resuming Actor %q again...", actorID) + if _, err := clients.SubstrateAPI.ResumeActor(ctx, &ateapipb.ResumeActorRequest{ + ActorId: actorID, + }); err != nil { + t.Fatalf("failed to resume Actor again: %v", err) + } + waitForActorStatus(ctx, t, clients, actorID, ateapipb.Actor_STATUS_RUNNING) + + // Suspending the actor before deletion + t.Logf("Suspending Actor %q before deletion...", actorID) + if _, err := clients.SubstrateAPI.SuspendActor(ctx, &ateapipb.SuspendActorRequest{ + ActorId: actorID, + }); err != nil { + t.Fatalf("failed to suspend Actor: %v", err) + } + waitForActorStatus(ctx, t, clients, actorID, ateapipb.Actor_STATUS_SUSPENDED) + + // Deleting the actor + t.Logf("Deleting Actor %q...", actorID) + if _, err := clients.SubstrateAPI.DeleteActor(ctx, &ateapipb.DeleteActorRequest{ + ActorId: actorID, + }); err != nil { + t.Fatalf("failed to delete Actor: %v", err) + } + // Verify deletion + if _, err := clients.SubstrateAPI.GetActor(ctx, &ateapipb.GetActorRequest{ + ActorId: actorID, + }); err == nil { + t.Fatalf("expected actor %q to be deleted, but it still exists", actorID) + } + + return nil +} + +func createActorTemplate(ctx context.Context, t *testing.T, clients *e2e.Clients, nsObj *e2e.Namespace) (*v1alpha1.ActorTemplate, error) { + env, err := e2e.CheckEnv("BUCKET_NAME", "KO_DOCKER_REPO") + if err != nil { + t.Fatalf("CheckEnv failed: %v", err) + } + // Query existing WorkerPool and ActorTemplate to get the resolved container images existingWp, err := clients.SubstrateK8s.ApiV1alpha1().WorkerPools("ate-demo-counter").Get(ctx, "counter", metav1.GetOptions{}) if err != nil { @@ -115,48 +339,25 @@ func TestDemo3(t *testing.T) { } } - // Create an Actor using the ATE API. - actorID := "demo-actor-1-" + nsObj.Name - - t.Logf("Creating Actor %q using Substrate API...", actorID) - createResp, err := clients.SubstrateAPI.CreateActor(ctx, &ateapipb.CreateActorRequest{ - ActorId: actorID, - ActorTemplateNamespace: nsObj.Name, - ActorTemplateName: at.Name, - }) - if err != nil { - t.Fatalf("failed to create Actor: %v", err) - } - t.Logf("Successfully created Actor: %s", createResp.GetActor().GetActorId()) - - listResp, err := clients.SubstrateAPI.ListActors(ctx, &ateapipb.ListActorsRequest{}) - if err != nil { - t.Fatalf("ListActors RPC failed: %v", err) - } + return at, nil +} - var myActors []*ateapipb.Actor - for _, actor := range listResp.GetActors() { - if actor.GetActorTemplateNamespace() == nsObj.Name && actor.GetActorId() == actorID { - myActors = append(myActors, actor) +func waitForActorStatus(ctx context.Context, t *testing.T, clients *e2e.Clients, actorID string, expectedStatus ateapipb.Actor_Status) { + t.Helper() + t.Logf("Waiting for Actor %q to be %v...", actorID, expectedStatus) + timeout := 60 * time.Second + deadline := time.Now().Add(timeout) + for time.Now().Before(deadline) { + resp, err := clients.SubstrateAPI.GetActor(ctx, &ateapipb.GetActorRequest{ + ActorId: actorID, + }) + if err == nil { + if resp.GetActor().GetStatus() == expectedStatus { + t.Logf("Actor %q reached status %v", actorID, expectedStatus) + return + } } + time.Sleep(1 * time.Second) } - - // Check that we have our Actor created. - if len(myActors) != 1 { - t.Fatalf("expected actor %s in namespace %s, got %d actors: %v", actorID, nsObj.Name, len(myActors), myActors) - } - - actor := myActors[0] - if actor.GetActorId() != actorID { - t.Errorf("expected actor ID %s, got %s", actorID, actor.GetActorId()) - } - if actor.GetActorTemplateName() != at.Name { - t.Errorf("expected actor template name %s, got %s", at.Name, actor.GetActorTemplateName()) - } - if actor.Status != ateapipb.Actor_STATUS_SUSPENDED { - t.Errorf("expected actor status to be SUSPENDED, got %v", actor.Status) - } - - t.Logf("Successfully queried Substrate API. Found %d active actors total, %d in our namespace %s.", - len(listResp.GetActors()), len(myActors), nsObj.Name) + t.Fatalf("timed out waiting for actor %q to reach status %v", actorID, expectedStatus) } diff --git a/internal/proto/ateletpb/atelet.pb.go b/internal/proto/ateletpb/atelet.pb.go index e3a55dd50..a9bb734a7 100644 --- a/internal/proto/ateletpb/atelet.pb.go +++ b/internal/proto/ateletpb/atelet.pb.go @@ -35,6 +35,58 @@ const ( _ = protoimpl.EnforceVersion(protoimpl.MaxVersion - 20) ) +type CheckpointType int32 + +const ( + // Should never happen + CheckpointType_CHECKPOINT_TYPE_UNSPECIFIED CheckpointType = 0 + // Save snapshot only in local filesystem + CheckpointType_CHECKPOINT_TYPE_LOCAL CheckpointType = 1 + // Save snapshot to object storage + CheckpointType_CHECKPOINT_TYPE_EXTERNAL CheckpointType = 2 +) + +// Enum value maps for CheckpointType. +var ( + CheckpointType_name = map[int32]string{ + 0: "CHECKPOINT_TYPE_UNSPECIFIED", + 1: "CHECKPOINT_TYPE_LOCAL", + 2: "CHECKPOINT_TYPE_EXTERNAL", + } + CheckpointType_value = map[string]int32{ + "CHECKPOINT_TYPE_UNSPECIFIED": 0, + "CHECKPOINT_TYPE_LOCAL": 1, + "CHECKPOINT_TYPE_EXTERNAL": 2, + } +) + +func (x CheckpointType) Enum() *CheckpointType { + p := new(CheckpointType) + *p = x + return p +} + +func (x CheckpointType) String() string { + return protoimpl.X.EnumStringOf(x.Descriptor(), protoreflect.EnumNumber(x)) +} + +func (CheckpointType) Descriptor() protoreflect.EnumDescriptor { + return file_atelet_proto_enumTypes[0].Descriptor() +} + +func (CheckpointType) Type() protoreflect.EnumType { + return &file_atelet_proto_enumTypes[0] +} + +func (x CheckpointType) Number() protoreflect.EnumNumber { + return protoreflect.EnumNumber(x) +} + +// Deprecated: Use CheckpointType.Descriptor instead. +func (CheckpointType) EnumDescriptor() ([]byte, []int) { + return file_atelet_proto_rawDescGZIP(), []int{0} +} + 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"` @@ -532,14 +584,54 @@ func (*RunResponse) Descriptor() ([]byte, []int) { return file_atelet_proto_rawDescGZIP(), []int{8} } -type CheckpointRequest 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"` - ActorTemplateNamespace string `protobuf:"bytes,3,opt,name=actor_template_namespace,json=actorTemplateNamespace,proto3" json:"actor_template_namespace,omitempty"` - ActorTemplateName string `protobuf:"bytes,4,opt,name=actor_template_name,json=actorTemplateName,proto3" json:"actor_template_name,omitempty"` - ActorId string `protobuf:"bytes,5,opt,name=actor_id,json=actorId,proto3" json:"actor_id,omitempty"` - Runsc *RunscConfig `protobuf:"bytes,6,opt,name=runsc,proto3" json:"runsc,omitempty"` - Spec *WorkloadSpec `protobuf:"bytes,7,opt,name=spec,proto3" json:"spec,omitempty"` +type LocalCheckpointConfiguration struct { + state protoimpl.MessageState `protogen:"open.v1"` + // A sub-directory on the local filesystem below which the checkpoint data will be + // stored. The structure of the checkpoint should generally be treated as opaque. + SnapshotPrefix string `protobuf:"bytes,1,opt,name=snapshot_prefix,json=snapshotPrefix,proto3" json:"snapshot_prefix,omitempty"` + unknownFields protoimpl.UnknownFields + sizeCache protoimpl.SizeCache +} + +func (x *LocalCheckpointConfiguration) Reset() { + *x = LocalCheckpointConfiguration{} + mi := &file_atelet_proto_msgTypes[9] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) +} + +func (x *LocalCheckpointConfiguration) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*LocalCheckpointConfiguration) ProtoMessage() {} + +func (x *LocalCheckpointConfiguration) ProtoReflect() protoreflect.Message { + mi := &file_atelet_proto_msgTypes[9] + 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 LocalCheckpointConfiguration.ProtoReflect.Descriptor instead. +func (*LocalCheckpointConfiguration) Descriptor() ([]byte, []int) { + return file_atelet_proto_rawDescGZIP(), []int{9} +} + +func (x *LocalCheckpointConfiguration) GetSnapshotPrefix() string { + if x != nil { + return x.SnapshotPrefix + } + return "" +} + +type ExternalCheckpointConfiguration struct { + state protoimpl.MessageState `protogen:"open.v1"` // An object storage URI prefix below which the checkpoint data will be // stored. // @@ -550,14 +642,71 @@ type CheckpointRequest struct { // can restore a checkpoint. // // For example: "gs://bucket/actors/1234/snapshots/5678/" - SnapshotUriPrefix string `protobuf:"bytes,8,opt,name=snapshot_uri_prefix,json=snapshotUriPrefix,proto3" json:"snapshot_uri_prefix,omitempty"` + SnapshotUriPrefix string `protobuf:"bytes,1,opt,name=snapshot_uri_prefix,json=snapshotUriPrefix,proto3" json:"snapshot_uri_prefix,omitempty"` unknownFields protoimpl.UnknownFields sizeCache protoimpl.SizeCache } +func (x *ExternalCheckpointConfiguration) Reset() { + *x = ExternalCheckpointConfiguration{} + mi := &file_atelet_proto_msgTypes[10] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) +} + +func (x *ExternalCheckpointConfiguration) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*ExternalCheckpointConfiguration) ProtoMessage() {} + +func (x *ExternalCheckpointConfiguration) ProtoReflect() protoreflect.Message { + mi := &file_atelet_proto_msgTypes[10] + 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 ExternalCheckpointConfiguration.ProtoReflect.Descriptor instead. +func (*ExternalCheckpointConfiguration) Descriptor() ([]byte, []int) { + return file_atelet_proto_rawDescGZIP(), []int{10} +} + +func (x *ExternalCheckpointConfiguration) GetSnapshotUriPrefix() string { + if x != nil { + return x.SnapshotUriPrefix + } + return "" +} + +type CheckpointRequest 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"` + ActorTemplateNamespace string `protobuf:"bytes,3,opt,name=actor_template_namespace,json=actorTemplateNamespace,proto3" json:"actor_template_namespace,omitempty"` + ActorTemplateName string `protobuf:"bytes,4,opt,name=actor_template_name,json=actorTemplateName,proto3" json:"actor_template_name,omitempty"` + ActorId string `protobuf:"bytes,5,opt,name=actor_id,json=actorId,proto3" json:"actor_id,omitempty"` + Runsc *RunscConfig `protobuf:"bytes,6,opt,name=runsc,proto3" json:"runsc,omitempty"` + Spec *WorkloadSpec `protobuf:"bytes,7,opt,name=spec,proto3" json:"spec,omitempty"` + Type CheckpointType `protobuf:"varint,9,opt,name=type,proto3,enum=atelet.CheckpointType" json:"type,omitempty"` + // The checkpoint configuration, depending on the type. + // + // Types that are valid to be assigned to Config: + // + // *CheckpointRequest_LocalConfig + // *CheckpointRequest_ExternalConfig + Config isCheckpointRequest_Config `protobuf_oneof:"config"` + unknownFields protoimpl.UnknownFields + sizeCache protoimpl.SizeCache +} + func (x *CheckpointRequest) Reset() { *x = CheckpointRequest{} - mi := &file_atelet_proto_msgTypes[9] + mi := &file_atelet_proto_msgTypes[11] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -569,7 +718,7 @@ func (x *CheckpointRequest) String() string { func (*CheckpointRequest) ProtoMessage() {} func (x *CheckpointRequest) 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 { @@ -582,7 +731,7 @@ func (x *CheckpointRequest) ProtoReflect() protoreflect.Message { // Deprecated: Use CheckpointRequest.ProtoReflect.Descriptor instead. func (*CheckpointRequest) Descriptor() ([]byte, []int) { - return file_atelet_proto_rawDescGZIP(), []int{9} + return file_atelet_proto_rawDescGZIP(), []int{11} } func (x *CheckpointRequest) GetTargetAteomUid() string { @@ -627,13 +776,54 @@ func (x *CheckpointRequest) GetSpec() *WorkloadSpec { return nil } -func (x *CheckpointRequest) GetSnapshotUriPrefix() string { +func (x *CheckpointRequest) GetType() CheckpointType { if x != nil { - return x.SnapshotUriPrefix + return x.Type } - return "" + return CheckpointType_CHECKPOINT_TYPE_UNSPECIFIED } +func (x *CheckpointRequest) GetConfig() isCheckpointRequest_Config { + if x != nil { + return x.Config + } + return nil +} + +func (x *CheckpointRequest) GetLocalConfig() *LocalCheckpointConfiguration { + if x != nil { + if x, ok := x.Config.(*CheckpointRequest_LocalConfig); ok { + return x.LocalConfig + } + } + return nil +} + +func (x *CheckpointRequest) GetExternalConfig() *ExternalCheckpointConfiguration { + if x != nil { + if x, ok := x.Config.(*CheckpointRequest_ExternalConfig); ok { + return x.ExternalConfig + } + } + return nil +} + +type isCheckpointRequest_Config interface { + isCheckpointRequest_Config() +} + +type CheckpointRequest_LocalConfig struct { + LocalConfig *LocalCheckpointConfiguration `protobuf:"bytes,10,opt,name=local_config,json=localConfig,proto3,oneof"` +} + +type CheckpointRequest_ExternalConfig struct { + ExternalConfig *ExternalCheckpointConfiguration `protobuf:"bytes,11,opt,name=external_config,json=externalConfig,proto3,oneof"` +} + +func (*CheckpointRequest_LocalConfig) isCheckpointRequest_Config() {} + +func (*CheckpointRequest_ExternalConfig) isCheckpointRequest_Config() {} + type CheckpointResponse struct { state protoimpl.MessageState `protogen:"open.v1"` unknownFields protoimpl.UnknownFields @@ -642,7 +832,7 @@ type CheckpointResponse struct { func (x *CheckpointResponse) Reset() { *x = CheckpointResponse{} - mi := &file_atelet_proto_msgTypes[10] + mi := &file_atelet_proto_msgTypes[12] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -654,7 +844,7 @@ func (x *CheckpointResponse) String() string { func (*CheckpointResponse) ProtoMessage() {} func (x *CheckpointResponse) 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 { @@ -667,7 +857,7 @@ func (x *CheckpointResponse) ProtoReflect() protoreflect.Message { // Deprecated: Use CheckpointResponse.ProtoReflect.Descriptor instead. func (*CheckpointResponse) Descriptor() ([]byte, []int) { - return file_atelet_proto_rawDescGZIP(), []int{10} + return file_atelet_proto_rawDescGZIP(), []int{12} } type RestoreRequest struct { @@ -678,15 +868,21 @@ type RestoreRequest struct { ActorId string `protobuf:"bytes,5,opt,name=actor_id,json=actorId,proto3" json:"actor_id,omitempty"` Runsc *RunscConfig `protobuf:"bytes,6,opt,name=runsc,proto3" json:"runsc,omitempty"` Spec *WorkloadSpec `protobuf:"bytes,7,opt,name=spec,proto3" json:"spec,omitempty"` - // The object storage URI prefix of the snapshot to restore. - SnapshotUriPrefix string `protobuf:"bytes,8,opt,name=snapshot_uri_prefix,json=snapshotUriPrefix,proto3" json:"snapshot_uri_prefix,omitempty"` - unknownFields protoimpl.UnknownFields - sizeCache protoimpl.SizeCache + Type CheckpointType `protobuf:"varint,9,opt,name=type,proto3,enum=atelet.CheckpointType" json:"type,omitempty"` + // The checkpoint configuration, depending on the type. + // + // Types that are valid to be assigned to Config: + // + // *RestoreRequest_LocalConfig + // *RestoreRequest_ExternalConfig + Config isRestoreRequest_Config `protobuf_oneof:"config"` + unknownFields protoimpl.UnknownFields + sizeCache protoimpl.SizeCache } func (x *RestoreRequest) Reset() { *x = RestoreRequest{} - mi := &file_atelet_proto_msgTypes[11] + mi := &file_atelet_proto_msgTypes[13] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -698,7 +894,7 @@ func (x *RestoreRequest) String() string { func (*RestoreRequest) ProtoMessage() {} func (x *RestoreRequest) 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 { @@ -711,7 +907,7 @@ func (x *RestoreRequest) ProtoReflect() protoreflect.Message { // Deprecated: Use RestoreRequest.ProtoReflect.Descriptor instead. func (*RestoreRequest) Descriptor() ([]byte, []int) { - return file_atelet_proto_rawDescGZIP(), []int{11} + return file_atelet_proto_rawDescGZIP(), []int{13} } func (x *RestoreRequest) GetTargetAteomUid() string { @@ -756,13 +952,54 @@ func (x *RestoreRequest) GetSpec() *WorkloadSpec { return nil } -func (x *RestoreRequest) GetSnapshotUriPrefix() string { +func (x *RestoreRequest) GetType() CheckpointType { if x != nil { - return x.SnapshotUriPrefix + return x.Type } - return "" + return CheckpointType_CHECKPOINT_TYPE_UNSPECIFIED +} + +func (x *RestoreRequest) GetConfig() isRestoreRequest_Config { + if x != nil { + return x.Config + } + return nil +} + +func (x *RestoreRequest) GetLocalConfig() *LocalCheckpointConfiguration { + if x != nil { + if x, ok := x.Config.(*RestoreRequest_LocalConfig); ok { + return x.LocalConfig + } + } + return nil +} + +func (x *RestoreRequest) GetExternalConfig() *ExternalCheckpointConfiguration { + if x != nil { + if x, ok := x.Config.(*RestoreRequest_ExternalConfig); ok { + return x.ExternalConfig + } + } + return nil +} + +type isRestoreRequest_Config interface { + isRestoreRequest_Config() } +type RestoreRequest_LocalConfig struct { + LocalConfig *LocalCheckpointConfiguration `protobuf:"bytes,10,opt,name=local_config,json=localConfig,proto3,oneof"` +} + +type RestoreRequest_ExternalConfig struct { + ExternalConfig *ExternalCheckpointConfiguration `protobuf:"bytes,11,opt,name=external_config,json=externalConfig,proto3,oneof"` +} + +func (*RestoreRequest_LocalConfig) isRestoreRequest_Config() {} + +func (*RestoreRequest_ExternalConfig) isRestoreRequest_Config() {} + type RestoreResponse struct { state protoimpl.MessageState `protogen:"open.v1"` unknownFields protoimpl.UnknownFields @@ -771,7 +1008,7 @@ type RestoreResponse struct { func (x *RestoreResponse) Reset() { *x = RestoreResponse{} - mi := &file_atelet_proto_msgTypes[12] + mi := &file_atelet_proto_msgTypes[14] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -783,7 +1020,7 @@ func (x *RestoreResponse) String() string { func (*RestoreResponse) ProtoMessage() {} func (x *RestoreResponse) 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 { @@ -796,7 +1033,7 @@ func (x *RestoreResponse) ProtoReflect() protoreflect.Message { // Deprecated: Use RestoreResponse.ProtoReflect.Descriptor instead. func (*RestoreResponse) Descriptor() ([]byte, []int) { - return file_atelet_proto_rawDescGZIP(), []int{12} + return file_atelet_proto_rawDescGZIP(), []int{14} } var File_atelet_proto protoreflect.FileDescriptor @@ -838,25 +1075,41 @@ const file_atelet_proto_rawDesc = "" + "\bEnvEntry\x12\x12\n" + "\x04name\x18\x01 \x01(\tR\x04name\x12\x14\n" + "\x05value\x18\x02 \x01(\tR\x05value\"\r\n" + - "\vRunResponse\"\xc7\x02\n" + + "\vRunResponse\"G\n" + + "\x1cLocalCheckpointConfiguration\x12'\n" + + "\x0fsnapshot_prefix\x18\x01 \x01(\tR\x0esnapshotPrefix\"Q\n" + + "\x1fExternalCheckpointConfiguration\x12.\n" + + "\x13snapshot_uri_prefix\x18\x01 \x01(\tR\x11snapshotUriPrefix\"\xf2\x03\n" + "\x11CheckpointRequest\x12(\n" + "\x10target_ateom_uid\x18\x01 \x01(\tR\x0etargetAteomUid\x128\n" + "\x18actor_template_namespace\x18\x03 \x01(\tR\x16actorTemplateNamespace\x12.\n" + "\x13actor_template_name\x18\x04 \x01(\tR\x11actorTemplateName\x12\x19\n" + "\bactor_id\x18\x05 \x01(\tR\aactorId\x12)\n" + "\x05runsc\x18\x06 \x01(\v2\x13.atelet.RunscConfigR\x05runsc\x12(\n" + - "\x04spec\x18\a \x01(\v2\x14.atelet.WorkloadSpecR\x04spec\x12.\n" + - "\x13snapshot_uri_prefix\x18\b \x01(\tR\x11snapshotUriPrefix\"\x14\n" + - "\x12CheckpointResponse\"\xc4\x02\n" + + "\x04spec\x18\a \x01(\v2\x14.atelet.WorkloadSpecR\x04spec\x12*\n" + + "\x04type\x18\t \x01(\x0e2\x16.atelet.CheckpointTypeR\x04type\x12I\n" + + "\flocal_config\x18\n" + + " \x01(\v2$.atelet.LocalCheckpointConfigurationH\x00R\vlocalConfig\x12R\n" + + "\x0fexternal_config\x18\v \x01(\v2'.atelet.ExternalCheckpointConfigurationH\x00R\x0eexternalConfigB\b\n" + + "\x06configJ\x04\b\b\x10\t\"\x14\n" + + "\x12CheckpointResponse\"\xef\x03\n" + "\x0eRestoreRequest\x12(\n" + "\x10target_ateom_uid\x18\x01 \x01(\tR\x0etargetAteomUid\x128\n" + "\x18actor_template_namespace\x18\x03 \x01(\tR\x16actorTemplateNamespace\x12.\n" + "\x13actor_template_name\x18\x04 \x01(\tR\x11actorTemplateName\x12\x19\n" + "\bactor_id\x18\x05 \x01(\tR\aactorId\x12)\n" + "\x05runsc\x18\x06 \x01(\v2\x13.atelet.RunscConfigR\x05runsc\x12(\n" + - "\x04spec\x18\a \x01(\v2\x14.atelet.WorkloadSpecR\x04spec\x12.\n" + - "\x13snapshot_uri_prefix\x18\b \x01(\tR\x11snapshotUriPrefix\"\x11\n" + - "\x0fRestoreResponse2\xc4\x01\n" + + "\x04spec\x18\a \x01(\v2\x14.atelet.WorkloadSpecR\x04spec\x12*\n" + + "\x04type\x18\t \x01(\x0e2\x16.atelet.CheckpointTypeR\x04type\x12I\n" + + "\flocal_config\x18\n" + + " \x01(\v2$.atelet.LocalCheckpointConfigurationH\x00R\vlocalConfig\x12R\n" + + "\x0fexternal_config\x18\v \x01(\v2'.atelet.ExternalCheckpointConfigurationH\x00R\x0eexternalConfigB\b\n" + + "\x06configJ\x04\b\b\x10\t\"\x11\n" + + "\x0fRestoreResponse*j\n" + + "\x0eCheckpointType\x12\x1f\n" + + "\x1bCHECKPOINT_TYPE_UNSPECIFIED\x10\x00\x12\x19\n" + + "\x15CHECKPOINT_TYPE_LOCAL\x10\x01\x12\x1c\n" + + "\x18CHECKPOINT_TYPE_EXTERNAL\x10\x022\xc4\x01\n" + "\vAteomHerder\x120\n" + "\x03Run\x12\x12.atelet.RunRequest\x1a\x13.atelet.RunResponse\"\x00\x12E\n" + "\n" + @@ -875,46 +1128,56 @@ func file_atelet_proto_rawDescGZIP() []byte { return file_atelet_proto_rawDescData } -var file_atelet_proto_msgTypes = make([]protoimpl.MessageInfo, 13) +var file_atelet_proto_enumTypes = make([]protoimpl.EnumInfo, 1) +var file_atelet_proto_msgTypes = make([]protoimpl.MessageInfo, 15) var file_atelet_proto_goTypes = []any{ - (*RunRequest)(nil), // 0: atelet.RunRequest - (*GCPAuthenticationConfig)(nil), // 1: atelet.GCPAuthenticationConfig - (*AuthenticationConfig)(nil), // 2: atelet.AuthenticationConfig - (*RunscPlatformConfig)(nil), // 3: atelet.RunscPlatformConfig - (*RunscConfig)(nil), // 4: atelet.RunscConfig - (*WorkloadSpec)(nil), // 5: atelet.WorkloadSpec - (*Container)(nil), // 6: atelet.Container - (*EnvEntry)(nil), // 7: atelet.EnvEntry - (*RunResponse)(nil), // 8: atelet.RunResponse - (*CheckpointRequest)(nil), // 9: atelet.CheckpointRequest - (*CheckpointResponse)(nil), // 10: atelet.CheckpointResponse - (*RestoreRequest)(nil), // 11: atelet.RestoreRequest - (*RestoreResponse)(nil), // 12: atelet.RestoreResponse + (CheckpointType)(0), // 0: atelet.CheckpointType + (*RunRequest)(nil), // 1: atelet.RunRequest + (*GCPAuthenticationConfig)(nil), // 2: atelet.GCPAuthenticationConfig + (*AuthenticationConfig)(nil), // 3: atelet.AuthenticationConfig + (*RunscPlatformConfig)(nil), // 4: atelet.RunscPlatformConfig + (*RunscConfig)(nil), // 5: atelet.RunscConfig + (*WorkloadSpec)(nil), // 6: atelet.WorkloadSpec + (*Container)(nil), // 7: atelet.Container + (*EnvEntry)(nil), // 8: atelet.EnvEntry + (*RunResponse)(nil), // 9: atelet.RunResponse + (*LocalCheckpointConfiguration)(nil), // 10: atelet.LocalCheckpointConfiguration + (*ExternalCheckpointConfiguration)(nil), // 11: atelet.ExternalCheckpointConfiguration + (*CheckpointRequest)(nil), // 12: atelet.CheckpointRequest + (*CheckpointResponse)(nil), // 13: atelet.CheckpointResponse + (*RestoreRequest)(nil), // 14: atelet.RestoreRequest + (*RestoreResponse)(nil), // 15: atelet.RestoreResponse } var file_atelet_proto_depIdxs = []int32{ - 4, // 0: atelet.RunRequest.runsc:type_name -> atelet.RunscConfig - 5, // 1: atelet.RunRequest.spec:type_name -> atelet.WorkloadSpec - 1, // 2: atelet.AuthenticationConfig.gcp:type_name -> atelet.GCPAuthenticationConfig - 3, // 3: atelet.RunscConfig.amd64:type_name -> atelet.RunscPlatformConfig - 3, // 4: atelet.RunscConfig.arm64:type_name -> atelet.RunscPlatformConfig - 2, // 5: atelet.RunscConfig.authentication:type_name -> atelet.AuthenticationConfig - 6, // 6: atelet.WorkloadSpec.containers:type_name -> atelet.Container - 7, // 7: atelet.Container.env:type_name -> atelet.EnvEntry - 4, // 8: atelet.CheckpointRequest.runsc:type_name -> atelet.RunscConfig - 5, // 9: atelet.CheckpointRequest.spec:type_name -> atelet.WorkloadSpec - 4, // 10: atelet.RestoreRequest.runsc:type_name -> atelet.RunscConfig - 5, // 11: atelet.RestoreRequest.spec:type_name -> atelet.WorkloadSpec - 0, // 12: atelet.AteomHerder.Run:input_type -> atelet.RunRequest - 9, // 13: atelet.AteomHerder.Checkpoint:input_type -> atelet.CheckpointRequest - 11, // 14: atelet.AteomHerder.Restore:input_type -> atelet.RestoreRequest - 8, // 15: atelet.AteomHerder.Run:output_type -> atelet.RunResponse - 10, // 16: atelet.AteomHerder.Checkpoint:output_type -> atelet.CheckpointResponse - 12, // 17: atelet.AteomHerder.Restore:output_type -> atelet.RestoreResponse - 15, // [15:18] is the sub-list for method output_type - 12, // [12:15] is the sub-list for method input_type - 12, // [12:12] is the sub-list for extension type_name - 12, // [12:12] is the sub-list for extension extendee - 0, // [0:12] is the sub-list for field type_name + 5, // 0: atelet.RunRequest.runsc:type_name -> atelet.RunscConfig + 6, // 1: atelet.RunRequest.spec:type_name -> atelet.WorkloadSpec + 2, // 2: atelet.AuthenticationConfig.gcp:type_name -> atelet.GCPAuthenticationConfig + 4, // 3: atelet.RunscConfig.amd64:type_name -> atelet.RunscPlatformConfig + 4, // 4: atelet.RunscConfig.arm64:type_name -> atelet.RunscPlatformConfig + 3, // 5: atelet.RunscConfig.authentication:type_name -> atelet.AuthenticationConfig + 7, // 6: atelet.WorkloadSpec.containers:type_name -> atelet.Container + 8, // 7: atelet.Container.env:type_name -> atelet.EnvEntry + 5, // 8: atelet.CheckpointRequest.runsc:type_name -> atelet.RunscConfig + 6, // 9: atelet.CheckpointRequest.spec:type_name -> atelet.WorkloadSpec + 0, // 10: atelet.CheckpointRequest.type:type_name -> atelet.CheckpointType + 10, // 11: atelet.CheckpointRequest.local_config:type_name -> atelet.LocalCheckpointConfiguration + 11, // 12: atelet.CheckpointRequest.external_config:type_name -> atelet.ExternalCheckpointConfiguration + 5, // 13: atelet.RestoreRequest.runsc:type_name -> atelet.RunscConfig + 6, // 14: atelet.RestoreRequest.spec:type_name -> atelet.WorkloadSpec + 0, // 15: atelet.RestoreRequest.type:type_name -> atelet.CheckpointType + 10, // 16: atelet.RestoreRequest.local_config:type_name -> atelet.LocalCheckpointConfiguration + 11, // 17: atelet.RestoreRequest.external_config:type_name -> atelet.ExternalCheckpointConfiguration + 1, // 18: atelet.AteomHerder.Run:input_type -> atelet.RunRequest + 12, // 19: atelet.AteomHerder.Checkpoint:input_type -> atelet.CheckpointRequest + 14, // 20: atelet.AteomHerder.Restore:input_type -> atelet.RestoreRequest + 9, // 21: atelet.AteomHerder.Run:output_type -> atelet.RunResponse + 13, // 22: atelet.AteomHerder.Checkpoint:output_type -> atelet.CheckpointResponse + 15, // 23: atelet.AteomHerder.Restore:output_type -> atelet.RestoreResponse + 21, // [21:24] is the sub-list for method output_type + 18, // [18:21] is the sub-list for method input_type + 18, // [18:18] is the sub-list for extension type_name + 18, // [18:18] is the sub-list for extension extendee + 0, // [0:18] is the sub-list for field type_name } func init() { file_atelet_proto_init() } @@ -922,18 +1185,27 @@ func file_atelet_proto_init() { if File_atelet_proto != nil { return } + file_atelet_proto_msgTypes[11].OneofWrappers = []any{ + (*CheckpointRequest_LocalConfig)(nil), + (*CheckpointRequest_ExternalConfig)(nil), + } + file_atelet_proto_msgTypes[13].OneofWrappers = []any{ + (*RestoreRequest_LocalConfig)(nil), + (*RestoreRequest_ExternalConfig)(nil), + } type x struct{} out := protoimpl.TypeBuilder{ File: protoimpl.DescBuilder{ GoPackagePath: reflect.TypeOf(x{}).PkgPath(), RawDescriptor: unsafe.Slice(unsafe.StringData(file_atelet_proto_rawDesc), len(file_atelet_proto_rawDesc)), - NumEnums: 0, - NumMessages: 13, + NumEnums: 1, + NumMessages: 15, NumExtensions: 0, NumServices: 1, }, GoTypes: file_atelet_proto_goTypes, DependencyIndexes: file_atelet_proto_depIdxs, + EnumInfos: file_atelet_proto_enumTypes, MessageInfos: file_atelet_proto_msgTypes, }.Build() File_atelet_proto = out.File diff --git a/internal/proto/ateletpb/atelet.proto b/internal/proto/ateletpb/atelet.proto index 792cffc98..acaf7a730 100644 --- a/internal/proto/ateletpb/atelet.proto +++ b/internal/proto/ateletpb/atelet.proto @@ -90,17 +90,13 @@ message EnvEntry { message RunResponse { } -message CheckpointRequest { - string target_ateom_uid = 1; - - string actor_template_namespace = 3; - string actor_template_name = 4; - string actor_id = 5; - - RunscConfig runsc = 6; - - WorkloadSpec spec = 7; +message LocalCheckpointConfiguration { + // A sub-directory on the local filesystem below which the checkpoint data will be + // stored. The structure of the checkpoint should generally be treated as opaque. + string snapshot_prefix = 1; +} +message ExternalCheckpointConfiguration { // An object storage URI prefix below which the checkpoint data will be // stored. // @@ -111,7 +107,39 @@ message CheckpointRequest { // can restore a checkpoint. // // For example: "gs://bucket/actors/1234/snapshots/5678/" - string snapshot_uri_prefix = 8; + string snapshot_uri_prefix = 1; +} + +enum CheckpointType { + // Should never happen + CHECKPOINT_TYPE_UNSPECIFIED = 0; + // Save snapshot only in local filesystem + CHECKPOINT_TYPE_LOCAL = 1; + // Save snapshot to object storage + CHECKPOINT_TYPE_EXTERNAL = 2; +} + +message CheckpointRequest { + string target_ateom_uid = 1; + + string actor_template_namespace = 3; + string actor_template_name = 4; + string actor_id = 5; + + RunscConfig runsc = 6; + + WorkloadSpec spec = 7; + + // deprecated snapshot_uri_prefix + reserved 8; + + CheckpointType type = 9; + + // The checkpoint configuration, depending on the type. + oneof config { + LocalCheckpointConfiguration local_config = 10; + ExternalCheckpointConfiguration external_config = 11; + } } message CheckpointResponse { @@ -129,8 +157,16 @@ message RestoreRequest { WorkloadSpec spec = 7; - // The object storage URI prefix of the snapshot to restore. - string snapshot_uri_prefix = 8; + // deprecated snapshot_uri_prefix + reserved 8; + + CheckpointType type = 9; + + // The checkpoint configuration, depending on the type. + oneof config { + LocalCheckpointConfiguration local_config = 10; + ExternalCheckpointConfiguration external_config = 11; + } } message RestoreResponse { diff --git a/pkg/proto/ateapipb/ateapi.pb.go b/pkg/proto/ateapipb/ateapi.pb.go index a11f341fa..24978de35 100644 --- a/pkg/proto/ateapipb/ateapi.pb.go +++ b/pkg/proto/ateapipb/ateapi.pb.go @@ -37,6 +37,55 @@ const ( _ = protoimpl.EnforceVersion(protoimpl.MaxVersion - 20) ) +type SnapshotType int32 + +const ( + SnapshotType_SNAPSHOT_TYPE_UNSPECIFIED SnapshotType = 0 + SnapshotType_SNAPSHOT_TYPE_LOCAL SnapshotType = 1 + SnapshotType_SNAPSHOT_TYPE_EXTERNAL SnapshotType = 2 +) + +// Enum value maps for SnapshotType. +var ( + SnapshotType_name = map[int32]string{ + 0: "SNAPSHOT_TYPE_UNSPECIFIED", + 1: "SNAPSHOT_TYPE_LOCAL", + 2: "SNAPSHOT_TYPE_EXTERNAL", + } + SnapshotType_value = map[string]int32{ + "SNAPSHOT_TYPE_UNSPECIFIED": 0, + "SNAPSHOT_TYPE_LOCAL": 1, + "SNAPSHOT_TYPE_EXTERNAL": 2, + } +) + +func (x SnapshotType) Enum() *SnapshotType { + p := new(SnapshotType) + *p = x + return p +} + +func (x SnapshotType) String() string { + return protoimpl.X.EnumStringOf(x.Descriptor(), protoreflect.EnumNumber(x)) +} + +func (SnapshotType) Descriptor() protoreflect.EnumDescriptor { + return file_ateapi_proto_enumTypes[0].Descriptor() +} + +func (SnapshotType) Type() protoreflect.EnumType { + return &file_ateapi_proto_enumTypes[0] +} + +func (x SnapshotType) Number() protoreflect.EnumNumber { + return protoreflect.EnumNumber(x) +} + +// Deprecated: Use SnapshotType.Descriptor instead. +func (SnapshotType) EnumDescriptor() ([]byte, []int) { + return file_ateapi_proto_rawDescGZIP(), []int{0} +} + type Actor_Status int32 const ( @@ -45,6 +94,8 @@ const ( Actor_STATUS_RUNNING Actor_Status = 2 Actor_STATUS_SUSPENDING Actor_Status = 3 Actor_STATUS_SUSPENDED Actor_Status = 4 + Actor_STATUS_PAUSING Actor_Status = 5 + Actor_STATUS_PAUSED Actor_Status = 6 ) // Enum value maps for Actor_Status. @@ -55,6 +106,8 @@ var ( 2: "STATUS_RUNNING", 3: "STATUS_SUSPENDING", 4: "STATUS_SUSPENDED", + 5: "STATUS_PAUSING", + 6: "STATUS_PAUSED", } Actor_Status_value = map[string]int32{ "STATUS_UNSPECIFIED": 0, @@ -62,6 +115,8 @@ var ( "STATUS_RUNNING": 2, "STATUS_SUSPENDING": 3, "STATUS_SUSPENDED": 4, + "STATUS_PAUSING": 5, + "STATUS_PAUSED": 6, } ) @@ -76,11 +131,11 @@ func (x Actor_Status) String() string { } func (Actor_Status) Descriptor() protoreflect.EnumDescriptor { - return file_ateapi_proto_enumTypes[0].Descriptor() + return file_ateapi_proto_enumTypes[1].Descriptor() } func (Actor_Status) Type() protoreflect.EnumType { - return &file_ateapi_proto_enumTypes[0] + return &file_ateapi_proto_enumTypes[1] } func (x Actor_Status) Number() protoreflect.EnumNumber { @@ -89,9 +144,196 @@ func (x Actor_Status) Number() protoreflect.EnumNumber { // Deprecated: Use Actor_Status.Descriptor instead. func (Actor_Status) EnumDescriptor() ([]byte, []int) { - return file_ateapi_proto_rawDescGZIP(), []int{0, 0} + return file_ateapi_proto_rawDescGZIP(), []int{3, 0} +} + +type ExternalSnapshotInfo struct { + state protoimpl.MessageState `protogen:"open.v1"` + SnapshotUriPrefix string `protobuf:"bytes,1,opt,name=snapshot_uri_prefix,json=snapshotUriPrefix,proto3" json:"snapshot_uri_prefix,omitempty"` + unknownFields protoimpl.UnknownFields + sizeCache protoimpl.SizeCache } +func (x *ExternalSnapshotInfo) Reset() { + *x = ExternalSnapshotInfo{} + mi := &file_ateapi_proto_msgTypes[0] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) +} + +func (x *ExternalSnapshotInfo) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*ExternalSnapshotInfo) ProtoMessage() {} + +func (x *ExternalSnapshotInfo) ProtoReflect() protoreflect.Message { + mi := &file_ateapi_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 ExternalSnapshotInfo.ProtoReflect.Descriptor instead. +func (*ExternalSnapshotInfo) Descriptor() ([]byte, []int) { + return file_ateapi_proto_rawDescGZIP(), []int{0} +} + +func (x *ExternalSnapshotInfo) GetSnapshotUriPrefix() string { + if x != nil { + return x.SnapshotUriPrefix + } + return "" +} + +type LocalSnapshotInfo struct { + state protoimpl.MessageState `protogen:"open.v1"` + SnapshotPrefix string `protobuf:"bytes,1,opt,name=snapshot_prefix,json=snapshotPrefix,proto3" json:"snapshot_prefix,omitempty"` + // Node VMs that have local snapshots for this actor, while it's PAUSED. + NodeVmsWithLocalSnapshots []string `protobuf:"bytes,2,rep,name=node_vms_with_local_snapshots,json=nodeVmsWithLocalSnapshots,proto3" json:"node_vms_with_local_snapshots,omitempty"` + unknownFields protoimpl.UnknownFields + sizeCache protoimpl.SizeCache +} + +func (x *LocalSnapshotInfo) Reset() { + *x = LocalSnapshotInfo{} + mi := &file_ateapi_proto_msgTypes[1] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) +} + +func (x *LocalSnapshotInfo) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*LocalSnapshotInfo) ProtoMessage() {} + +func (x *LocalSnapshotInfo) ProtoReflect() protoreflect.Message { + mi := &file_ateapi_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 LocalSnapshotInfo.ProtoReflect.Descriptor instead. +func (*LocalSnapshotInfo) Descriptor() ([]byte, []int) { + return file_ateapi_proto_rawDescGZIP(), []int{1} +} + +func (x *LocalSnapshotInfo) GetSnapshotPrefix() string { + if x != nil { + return x.SnapshotPrefix + } + return "" +} + +func (x *LocalSnapshotInfo) GetNodeVmsWithLocalSnapshots() []string { + if x != nil { + return x.NodeVmsWithLocalSnapshots + } + return nil +} + +type SnapshotInfo struct { + state protoimpl.MessageState `protogen:"open.v1"` + Type SnapshotType `protobuf:"varint,1,opt,name=type,proto3,enum=ateapi.SnapshotType" json:"type,omitempty"` + // Types that are valid to be assigned to Data: + // + // *SnapshotInfo_External + // *SnapshotInfo_Local + Data isSnapshotInfo_Data `protobuf_oneof:"data"` + unknownFields protoimpl.UnknownFields + sizeCache protoimpl.SizeCache +} + +func (x *SnapshotInfo) Reset() { + *x = SnapshotInfo{} + mi := &file_ateapi_proto_msgTypes[2] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) +} + +func (x *SnapshotInfo) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*SnapshotInfo) ProtoMessage() {} + +func (x *SnapshotInfo) ProtoReflect() protoreflect.Message { + mi := &file_ateapi_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 SnapshotInfo.ProtoReflect.Descriptor instead. +func (*SnapshotInfo) Descriptor() ([]byte, []int) { + return file_ateapi_proto_rawDescGZIP(), []int{2} +} + +func (x *SnapshotInfo) GetType() SnapshotType { + if x != nil { + return x.Type + } + return SnapshotType_SNAPSHOT_TYPE_UNSPECIFIED +} + +func (x *SnapshotInfo) GetData() isSnapshotInfo_Data { + if x != nil { + return x.Data + } + return nil +} + +func (x *SnapshotInfo) GetExternal() *ExternalSnapshotInfo { + if x != nil { + if x, ok := x.Data.(*SnapshotInfo_External); ok { + return x.External + } + } + return nil +} + +func (x *SnapshotInfo) GetLocal() *LocalSnapshotInfo { + if x != nil { + if x, ok := x.Data.(*SnapshotInfo_Local); ok { + return x.Local + } + } + return nil +} + +type isSnapshotInfo_Data interface { + isSnapshotInfo_Data() +} + +type SnapshotInfo_External struct { + External *ExternalSnapshotInfo `protobuf:"bytes,2,opt,name=external,proto3,oneof"` +} + +type SnapshotInfo_Local struct { + Local *LocalSnapshotInfo `protobuf:"bytes,3,opt,name=local,proto3,oneof"` +} + +func (*SnapshotInfo_External) isSnapshotInfo_Data() {} + +func (*SnapshotInfo_Local) isSnapshotInfo_Data() {} + type Actor struct { state protoimpl.MessageState `protogen:"open.v1"` ActorId string `protobuf:"bytes,1,opt,name=actor_id,json=actorId,proto3" json:"actor_id,omitempty"` @@ -102,16 +344,16 @@ type Actor struct { AteomPodNamespace string `protobuf:"bytes,6,opt,name=ateom_pod_namespace,json=ateomPodNamespace,proto3" json:"ateom_pod_namespace,omitempty"` AteomPodName string `protobuf:"bytes,7,opt,name=ateom_pod_name,json=ateomPodName,proto3" json:"ateom_pod_name,omitempty"` AteomPodIp string `protobuf:"bytes,8,opt,name=ateom_pod_ip,json=ateomPodIp,proto3" json:"ateom_pod_ip,omitempty"` - LastSnapshot string `protobuf:"bytes,9,opt,name=last_snapshot,json=lastSnapshot,proto3" json:"last_snapshot,omitempty"` InProgressSnapshot string `protobuf:"bytes,10,opt,name=in_progress_snapshot,json=inProgressSnapshot,proto3" json:"in_progress_snapshot,omitempty"` AteomPodUid string `protobuf:"bytes,11,opt,name=ateom_pod_uid,json=ateomPodUid,proto3" json:"ateom_pod_uid,omitempty"` + LatestSnapshotInfo *SnapshotInfo `protobuf:"bytes,12,opt,name=latest_snapshot_info,json=latestSnapshotInfo,proto3" json:"latest_snapshot_info,omitempty"` unknownFields protoimpl.UnknownFields sizeCache protoimpl.SizeCache } func (x *Actor) Reset() { *x = Actor{} - mi := &file_ateapi_proto_msgTypes[0] + mi := &file_ateapi_proto_msgTypes[3] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -123,7 +365,7 @@ func (x *Actor) String() string { func (*Actor) ProtoMessage() {} func (x *Actor) ProtoReflect() protoreflect.Message { - mi := &file_ateapi_proto_msgTypes[0] + mi := &file_ateapi_proto_msgTypes[3] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -136,7 +378,7 @@ func (x *Actor) ProtoReflect() protoreflect.Message { // Deprecated: Use Actor.ProtoReflect.Descriptor instead. func (*Actor) Descriptor() ([]byte, []int) { - return file_ateapi_proto_rawDescGZIP(), []int{0} + return file_ateapi_proto_rawDescGZIP(), []int{3} } func (x *Actor) GetActorId() string { @@ -195,13 +437,6 @@ func (x *Actor) GetAteomPodIp() string { return "" } -func (x *Actor) GetLastSnapshot() string { - if x != nil { - return x.LastSnapshot - } - return "" -} - func (x *Actor) GetInProgressSnapshot() string { if x != nil { return x.InProgressSnapshot @@ -216,6 +451,13 @@ func (x *Actor) GetAteomPodUid() string { return "" } +func (x *Actor) GetLatestSnapshotInfo() *SnapshotInfo { + if x != nil { + return x.LatestSnapshotInfo + } + return nil +} + type GetActorRequest struct { state protoimpl.MessageState `protogen:"open.v1"` ActorId string `protobuf:"bytes,1,opt,name=actor_id,json=actorId,proto3" json:"actor_id,omitempty"` @@ -225,7 +467,7 @@ type GetActorRequest struct { func (x *GetActorRequest) Reset() { *x = GetActorRequest{} - mi := &file_ateapi_proto_msgTypes[1] + mi := &file_ateapi_proto_msgTypes[4] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -237,7 +479,7 @@ func (x *GetActorRequest) String() string { func (*GetActorRequest) ProtoMessage() {} func (x *GetActorRequest) ProtoReflect() protoreflect.Message { - mi := &file_ateapi_proto_msgTypes[1] + mi := &file_ateapi_proto_msgTypes[4] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -250,7 +492,7 @@ func (x *GetActorRequest) ProtoReflect() protoreflect.Message { // Deprecated: Use GetActorRequest.ProtoReflect.Descriptor instead. func (*GetActorRequest) Descriptor() ([]byte, []int) { - return file_ateapi_proto_rawDescGZIP(), []int{1} + return file_ateapi_proto_rawDescGZIP(), []int{4} } func (x *GetActorRequest) GetActorId() string { @@ -269,7 +511,7 @@ type GetActorResponse struct { func (x *GetActorResponse) Reset() { *x = GetActorResponse{} - mi := &file_ateapi_proto_msgTypes[2] + mi := &file_ateapi_proto_msgTypes[5] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -281,7 +523,7 @@ func (x *GetActorResponse) String() string { func (*GetActorResponse) ProtoMessage() {} func (x *GetActorResponse) ProtoReflect() protoreflect.Message { - mi := &file_ateapi_proto_msgTypes[2] + mi := &file_ateapi_proto_msgTypes[5] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -294,7 +536,7 @@ func (x *GetActorResponse) ProtoReflect() protoreflect.Message { // Deprecated: Use GetActorResponse.ProtoReflect.Descriptor instead. func (*GetActorResponse) Descriptor() ([]byte, []int) { - return file_ateapi_proto_rawDescGZIP(), []int{2} + return file_ateapi_proto_rawDescGZIP(), []int{5} } func (x *GetActorResponse) GetActor() *Actor { @@ -319,7 +561,7 @@ type CreateActorRequest struct { func (x *CreateActorRequest) Reset() { *x = CreateActorRequest{} - mi := &file_ateapi_proto_msgTypes[3] + mi := &file_ateapi_proto_msgTypes[6] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -331,7 +573,7 @@ func (x *CreateActorRequest) String() string { func (*CreateActorRequest) ProtoMessage() {} func (x *CreateActorRequest) ProtoReflect() protoreflect.Message { - mi := &file_ateapi_proto_msgTypes[3] + mi := &file_ateapi_proto_msgTypes[6] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -344,7 +586,7 @@ func (x *CreateActorRequest) ProtoReflect() protoreflect.Message { // Deprecated: Use CreateActorRequest.ProtoReflect.Descriptor instead. func (*CreateActorRequest) Descriptor() ([]byte, []int) { - return file_ateapi_proto_rawDescGZIP(), []int{3} + return file_ateapi_proto_rawDescGZIP(), []int{6} } func (x *CreateActorRequest) GetActorId() string { @@ -377,7 +619,7 @@ type CreateActorResponse struct { func (x *CreateActorResponse) Reset() { *x = CreateActorResponse{} - mi := &file_ateapi_proto_msgTypes[4] + mi := &file_ateapi_proto_msgTypes[7] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -389,7 +631,7 @@ func (x *CreateActorResponse) String() string { func (*CreateActorResponse) ProtoMessage() {} func (x *CreateActorResponse) ProtoReflect() protoreflect.Message { - mi := &file_ateapi_proto_msgTypes[4] + mi := &file_ateapi_proto_msgTypes[7] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -402,7 +644,7 @@ func (x *CreateActorResponse) ProtoReflect() protoreflect.Message { // Deprecated: Use CreateActorResponse.ProtoReflect.Descriptor instead. func (*CreateActorResponse) Descriptor() ([]byte, []int) { - return file_ateapi_proto_rawDescGZIP(), []int{4} + return file_ateapi_proto_rawDescGZIP(), []int{7} } func (x *CreateActorResponse) GetActor() *Actor { @@ -421,7 +663,7 @@ type SuspendActorRequest struct { func (x *SuspendActorRequest) Reset() { *x = SuspendActorRequest{} - mi := &file_ateapi_proto_msgTypes[5] + mi := &file_ateapi_proto_msgTypes[8] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -433,7 +675,7 @@ func (x *SuspendActorRequest) String() string { func (*SuspendActorRequest) ProtoMessage() {} func (x *SuspendActorRequest) ProtoReflect() protoreflect.Message { - mi := &file_ateapi_proto_msgTypes[5] + mi := &file_ateapi_proto_msgTypes[8] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -446,7 +688,7 @@ func (x *SuspendActorRequest) ProtoReflect() protoreflect.Message { // Deprecated: Use SuspendActorRequest.ProtoReflect.Descriptor instead. func (*SuspendActorRequest) Descriptor() ([]byte, []int) { - return file_ateapi_proto_rawDescGZIP(), []int{5} + return file_ateapi_proto_rawDescGZIP(), []int{8} } func (x *SuspendActorRequest) GetActorId() string { @@ -465,7 +707,7 @@ type SuspendActorResponse struct { func (x *SuspendActorResponse) Reset() { *x = SuspendActorResponse{} - mi := &file_ateapi_proto_msgTypes[6] + mi := &file_ateapi_proto_msgTypes[9] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -477,7 +719,7 @@ func (x *SuspendActorResponse) String() string { func (*SuspendActorResponse) ProtoMessage() {} func (x *SuspendActorResponse) ProtoReflect() protoreflect.Message { - mi := &file_ateapi_proto_msgTypes[6] + mi := &file_ateapi_proto_msgTypes[9] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -490,7 +732,7 @@ func (x *SuspendActorResponse) ProtoReflect() protoreflect.Message { // Deprecated: Use SuspendActorResponse.ProtoReflect.Descriptor instead. func (*SuspendActorResponse) Descriptor() ([]byte, []int) { - return file_ateapi_proto_rawDescGZIP(), []int{6} + return file_ateapi_proto_rawDescGZIP(), []int{9} } func (x *SuspendActorResponse) GetActor() *Actor { @@ -500,6 +742,94 @@ func (x *SuspendActorResponse) GetActor() *Actor { return nil } +type PauseActorRequest struct { + state protoimpl.MessageState `protogen:"open.v1"` + ActorId string `protobuf:"bytes,1,opt,name=actor_id,json=actorId,proto3" json:"actor_id,omitempty"` + unknownFields protoimpl.UnknownFields + sizeCache protoimpl.SizeCache +} + +func (x *PauseActorRequest) Reset() { + *x = PauseActorRequest{} + mi := &file_ateapi_proto_msgTypes[10] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) +} + +func (x *PauseActorRequest) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*PauseActorRequest) ProtoMessage() {} + +func (x *PauseActorRequest) ProtoReflect() protoreflect.Message { + mi := &file_ateapi_proto_msgTypes[10] + 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 PauseActorRequest.ProtoReflect.Descriptor instead. +func (*PauseActorRequest) Descriptor() ([]byte, []int) { + return file_ateapi_proto_rawDescGZIP(), []int{10} +} + +func (x *PauseActorRequest) GetActorId() string { + if x != nil { + return x.ActorId + } + return "" +} + +type PauseActorResponse struct { + state protoimpl.MessageState `protogen:"open.v1"` + Actor *Actor `protobuf:"bytes,1,opt,name=actor,proto3" json:"actor,omitempty"` + unknownFields protoimpl.UnknownFields + sizeCache protoimpl.SizeCache +} + +func (x *PauseActorResponse) Reset() { + *x = PauseActorResponse{} + mi := &file_ateapi_proto_msgTypes[11] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) +} + +func (x *PauseActorResponse) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*PauseActorResponse) ProtoMessage() {} + +func (x *PauseActorResponse) ProtoReflect() protoreflect.Message { + mi := &file_ateapi_proto_msgTypes[11] + 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 PauseActorResponse.ProtoReflect.Descriptor instead. +func (*PauseActorResponse) Descriptor() ([]byte, []int) { + return file_ateapi_proto_rawDescGZIP(), []int{11} +} + +func (x *PauseActorResponse) GetActor() *Actor { + if x != nil { + return x.Actor + } + return nil +} + type ResumeActorRequest struct { state protoimpl.MessageState `protogen:"open.v1"` ActorId string `protobuf:"bytes,1,opt,name=actor_id,json=actorId,proto3" json:"actor_id,omitempty"` @@ -511,7 +841,7 @@ type ResumeActorRequest struct { func (x *ResumeActorRequest) Reset() { *x = ResumeActorRequest{} - mi := &file_ateapi_proto_msgTypes[7] + mi := &file_ateapi_proto_msgTypes[12] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -523,7 +853,7 @@ func (x *ResumeActorRequest) String() string { func (*ResumeActorRequest) ProtoMessage() {} func (x *ResumeActorRequest) ProtoReflect() protoreflect.Message { - mi := &file_ateapi_proto_msgTypes[7] + mi := &file_ateapi_proto_msgTypes[12] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -536,7 +866,7 @@ func (x *ResumeActorRequest) ProtoReflect() protoreflect.Message { // Deprecated: Use ResumeActorRequest.ProtoReflect.Descriptor instead. func (*ResumeActorRequest) Descriptor() ([]byte, []int) { - return file_ateapi_proto_rawDescGZIP(), []int{7} + return file_ateapi_proto_rawDescGZIP(), []int{12} } func (x *ResumeActorRequest) GetActorId() string { @@ -562,7 +892,7 @@ type ResumeActorResponse struct { func (x *ResumeActorResponse) Reset() { *x = ResumeActorResponse{} - mi := &file_ateapi_proto_msgTypes[8] + mi := &file_ateapi_proto_msgTypes[13] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -574,7 +904,7 @@ func (x *ResumeActorResponse) String() string { func (*ResumeActorResponse) ProtoMessage() {} func (x *ResumeActorResponse) ProtoReflect() protoreflect.Message { - mi := &file_ateapi_proto_msgTypes[8] + mi := &file_ateapi_proto_msgTypes[13] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -587,7 +917,7 @@ func (x *ResumeActorResponse) ProtoReflect() protoreflect.Message { // Deprecated: Use ResumeActorResponse.ProtoReflect.Descriptor instead. func (*ResumeActorResponse) Descriptor() ([]byte, []int) { - return file_ateapi_proto_rawDescGZIP(), []int{8} + return file_ateapi_proto_rawDescGZIP(), []int{13} } func (x *ResumeActorResponse) GetActor() *Actor { @@ -606,7 +936,7 @@ type DeleteActorRequest struct { func (x *DeleteActorRequest) Reset() { *x = DeleteActorRequest{} - mi := &file_ateapi_proto_msgTypes[9] + mi := &file_ateapi_proto_msgTypes[14] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -618,7 +948,7 @@ func (x *DeleteActorRequest) String() string { func (*DeleteActorRequest) ProtoMessage() {} func (x *DeleteActorRequest) ProtoReflect() protoreflect.Message { - mi := &file_ateapi_proto_msgTypes[9] + mi := &file_ateapi_proto_msgTypes[14] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -631,7 +961,7 @@ func (x *DeleteActorRequest) ProtoReflect() protoreflect.Message { // Deprecated: Use DeleteActorRequest.ProtoReflect.Descriptor instead. func (*DeleteActorRequest) Descriptor() ([]byte, []int) { - return file_ateapi_proto_rawDescGZIP(), []int{9} + return file_ateapi_proto_rawDescGZIP(), []int{14} } func (x *DeleteActorRequest) GetActorId() string { @@ -649,7 +979,7 @@ type DeleteActorResponse struct { func (x *DeleteActorResponse) Reset() { *x = DeleteActorResponse{} - mi := &file_ateapi_proto_msgTypes[10] + mi := &file_ateapi_proto_msgTypes[15] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -661,7 +991,7 @@ func (x *DeleteActorResponse) String() string { func (*DeleteActorResponse) ProtoMessage() {} func (x *DeleteActorResponse) ProtoReflect() protoreflect.Message { - mi := &file_ateapi_proto_msgTypes[10] + mi := &file_ateapi_proto_msgTypes[15] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -674,7 +1004,7 @@ func (x *DeleteActorResponse) ProtoReflect() protoreflect.Message { // Deprecated: Use DeleteActorResponse.ProtoReflect.Descriptor instead. func (*DeleteActorResponse) Descriptor() ([]byte, []int) { - return file_ateapi_proto_rawDescGZIP(), []int{10} + return file_ateapi_proto_rawDescGZIP(), []int{15} } type ListWorkersRequest struct { @@ -685,7 +1015,7 @@ type ListWorkersRequest struct { func (x *ListWorkersRequest) Reset() { *x = ListWorkersRequest{} - mi := &file_ateapi_proto_msgTypes[11] + mi := &file_ateapi_proto_msgTypes[16] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -697,7 +1027,7 @@ func (x *ListWorkersRequest) String() string { func (*ListWorkersRequest) ProtoMessage() {} func (x *ListWorkersRequest) ProtoReflect() protoreflect.Message { - mi := &file_ateapi_proto_msgTypes[11] + mi := &file_ateapi_proto_msgTypes[16] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -710,7 +1040,7 @@ func (x *ListWorkersRequest) ProtoReflect() protoreflect.Message { // Deprecated: Use ListWorkersRequest.ProtoReflect.Descriptor instead. func (*ListWorkersRequest) Descriptor() ([]byte, []int) { - return file_ateapi_proto_rawDescGZIP(), []int{11} + return file_ateapi_proto_rawDescGZIP(), []int{16} } type ListWorkersResponse struct { @@ -722,7 +1052,7 @@ type ListWorkersResponse struct { func (x *ListWorkersResponse) Reset() { *x = ListWorkersResponse{} - mi := &file_ateapi_proto_msgTypes[12] + mi := &file_ateapi_proto_msgTypes[17] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -734,7 +1064,7 @@ func (x *ListWorkersResponse) String() string { func (*ListWorkersResponse) ProtoMessage() {} func (x *ListWorkersResponse) ProtoReflect() protoreflect.Message { - mi := &file_ateapi_proto_msgTypes[12] + mi := &file_ateapi_proto_msgTypes[17] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -747,7 +1077,7 @@ func (x *ListWorkersResponse) ProtoReflect() protoreflect.Message { // Deprecated: Use ListWorkersResponse.ProtoReflect.Descriptor instead. func (*ListWorkersResponse) Descriptor() ([]byte, []int) { - return file_ateapi_proto_rawDescGZIP(), []int{12} + return file_ateapi_proto_rawDescGZIP(), []int{17} } func (x *ListWorkersResponse) GetWorkers() []*Worker { @@ -774,7 +1104,7 @@ type ListActorsRequest struct { func (x *ListActorsRequest) Reset() { *x = ListActorsRequest{} - mi := &file_ateapi_proto_msgTypes[13] + mi := &file_ateapi_proto_msgTypes[18] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -786,7 +1116,7 @@ func (x *ListActorsRequest) String() string { func (*ListActorsRequest) ProtoMessage() {} func (x *ListActorsRequest) ProtoReflect() protoreflect.Message { - mi := &file_ateapi_proto_msgTypes[13] + mi := &file_ateapi_proto_msgTypes[18] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -799,7 +1129,7 @@ func (x *ListActorsRequest) ProtoReflect() protoreflect.Message { // Deprecated: Use ListActorsRequest.ProtoReflect.Descriptor instead. func (*ListActorsRequest) Descriptor() ([]byte, []int) { - return file_ateapi_proto_rawDescGZIP(), []int{13} + return file_ateapi_proto_rawDescGZIP(), []int{18} } func (x *ListActorsRequest) GetPageSize() int32 { @@ -830,7 +1160,7 @@ type ListActorsResponse struct { func (x *ListActorsResponse) Reset() { *x = ListActorsResponse{} - mi := &file_ateapi_proto_msgTypes[14] + mi := &file_ateapi_proto_msgTypes[19] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -842,7 +1172,7 @@ func (x *ListActorsResponse) String() string { func (*ListActorsResponse) ProtoMessage() {} func (x *ListActorsResponse) ProtoReflect() protoreflect.Message { - mi := &file_ateapi_proto_msgTypes[14] + mi := &file_ateapi_proto_msgTypes[19] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -855,7 +1185,7 @@ func (x *ListActorsResponse) ProtoReflect() protoreflect.Message { // Deprecated: Use ListActorsResponse.ProtoReflect.Descriptor instead. func (*ListActorsResponse) Descriptor() ([]byte, []int) { - return file_ateapi_proto_rawDescGZIP(), []int{14} + return file_ateapi_proto_rawDescGZIP(), []int{19} } func (x *ListActorsResponse) GetActors() []*Actor { @@ -883,13 +1213,14 @@ type Worker struct { Ip string `protobuf:"bytes,7,opt,name=ip,proto3" json:"ip,omitempty"` Version int64 `protobuf:"varint,8,opt,name=version,proto3" json:"version,omitempty"` WorkerPodUid string `protobuf:"bytes,9,opt,name=worker_pod_uid,json=workerPodUid,proto3" json:"worker_pod_uid,omitempty"` + NodeName string `protobuf:"bytes,10,opt,name=node_name,json=nodeName,proto3" json:"node_name,omitempty"` unknownFields protoimpl.UnknownFields sizeCache protoimpl.SizeCache } func (x *Worker) Reset() { *x = Worker{} - mi := &file_ateapi_proto_msgTypes[15] + mi := &file_ateapi_proto_msgTypes[20] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -901,7 +1232,7 @@ func (x *Worker) String() string { func (*Worker) ProtoMessage() {} func (x *Worker) ProtoReflect() protoreflect.Message { - mi := &file_ateapi_proto_msgTypes[15] + mi := &file_ateapi_proto_msgTypes[20] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -914,7 +1245,7 @@ func (x *Worker) ProtoReflect() protoreflect.Message { // Deprecated: Use Worker.ProtoReflect.Descriptor instead. func (*Worker) Descriptor() ([]byte, []int) { - return file_ateapi_proto_rawDescGZIP(), []int{15} + return file_ateapi_proto_rawDescGZIP(), []int{20} } func (x *Worker) GetWorkerNamespace() string { @@ -980,6 +1311,13 @@ func (x *Worker) GetWorkerPodUid() string { return "" } +func (x *Worker) GetNodeName() string { + if x != nil { + return x.NodeName + } + return "" +} + type DebugClearRequest struct { state protoimpl.MessageState `protogen:"open.v1"` unknownFields protoimpl.UnknownFields @@ -988,7 +1326,7 @@ type DebugClearRequest struct { func (x *DebugClearRequest) Reset() { *x = DebugClearRequest{} - mi := &file_ateapi_proto_msgTypes[16] + mi := &file_ateapi_proto_msgTypes[21] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -1000,7 +1338,7 @@ func (x *DebugClearRequest) String() string { func (*DebugClearRequest) ProtoMessage() {} func (x *DebugClearRequest) ProtoReflect() protoreflect.Message { - mi := &file_ateapi_proto_msgTypes[16] + mi := &file_ateapi_proto_msgTypes[21] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -1013,7 +1351,7 @@ func (x *DebugClearRequest) ProtoReflect() protoreflect.Message { // Deprecated: Use DebugClearRequest.ProtoReflect.Descriptor instead. func (*DebugClearRequest) Descriptor() ([]byte, []int) { - return file_ateapi_proto_rawDescGZIP(), []int{16} + return file_ateapi_proto_rawDescGZIP(), []int{21} } type DebugClearResponse struct { @@ -1024,7 +1362,7 @@ type DebugClearResponse struct { func (x *DebugClearResponse) Reset() { *x = DebugClearResponse{} - mi := &file_ateapi_proto_msgTypes[17] + mi := &file_ateapi_proto_msgTypes[22] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -1036,7 +1374,7 @@ func (x *DebugClearResponse) String() string { func (*DebugClearResponse) ProtoMessage() {} func (x *DebugClearResponse) ProtoReflect() protoreflect.Message { - mi := &file_ateapi_proto_msgTypes[17] + mi := &file_ateapi_proto_msgTypes[22] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -1049,7 +1387,7 @@ func (x *DebugClearResponse) ProtoReflect() protoreflect.Message { // Deprecated: Use DebugClearResponse.ProtoReflect.Descriptor instead. func (*DebugClearResponse) Descriptor() ([]byte, []int) { - return file_ateapi_proto_rawDescGZIP(), []int{17} + return file_ateapi_proto_rawDescGZIP(), []int{22} } type MintJWTRequest struct { @@ -1064,7 +1402,7 @@ type MintJWTRequest struct { func (x *MintJWTRequest) Reset() { *x = MintJWTRequest{} - mi := &file_ateapi_proto_msgTypes[18] + mi := &file_ateapi_proto_msgTypes[23] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -1076,7 +1414,7 @@ func (x *MintJWTRequest) String() string { func (*MintJWTRequest) ProtoMessage() {} func (x *MintJWTRequest) ProtoReflect() protoreflect.Message { - mi := &file_ateapi_proto_msgTypes[18] + mi := &file_ateapi_proto_msgTypes[23] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -1089,7 +1427,7 @@ func (x *MintJWTRequest) ProtoReflect() protoreflect.Message { // Deprecated: Use MintJWTRequest.ProtoReflect.Descriptor instead. func (*MintJWTRequest) Descriptor() ([]byte, []int) { - return file_ateapi_proto_rawDescGZIP(), []int{18} + return file_ateapi_proto_rawDescGZIP(), []int{23} } func (x *MintJWTRequest) GetAudience() []string { @@ -1149,7 +1487,7 @@ type MintJWTResponse struct { func (x *MintJWTResponse) Reset() { *x = MintJWTResponse{} - mi := &file_ateapi_proto_msgTypes[19] + mi := &file_ateapi_proto_msgTypes[24] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -1161,7 +1499,7 @@ func (x *MintJWTResponse) String() string { func (*MintJWTResponse) ProtoMessage() {} func (x *MintJWTResponse) ProtoReflect() protoreflect.Message { - mi := &file_ateapi_proto_msgTypes[19] + mi := &file_ateapi_proto_msgTypes[24] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -1174,7 +1512,7 @@ func (x *MintJWTResponse) ProtoReflect() protoreflect.Message { // Deprecated: Use MintJWTResponse.ProtoReflect.Descriptor instead. func (*MintJWTResponse) Descriptor() ([]byte, []int) { - return file_ateapi_proto_rawDescGZIP(), []int{19} + return file_ateapi_proto_rawDescGZIP(), []int{24} } func (x *MintJWTResponse) GetSessionJwt() string { @@ -1199,7 +1537,7 @@ type MintCertRequest struct { func (x *MintCertRequest) Reset() { *x = MintCertRequest{} - mi := &file_ateapi_proto_msgTypes[20] + mi := &file_ateapi_proto_msgTypes[25] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -1211,7 +1549,7 @@ func (x *MintCertRequest) String() string { func (*MintCertRequest) ProtoMessage() {} func (x *MintCertRequest) ProtoReflect() protoreflect.Message { - mi := &file_ateapi_proto_msgTypes[20] + mi := &file_ateapi_proto_msgTypes[25] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -1224,7 +1562,7 @@ func (x *MintCertRequest) ProtoReflect() protoreflect.Message { // Deprecated: Use MintCertRequest.ProtoReflect.Descriptor instead. func (*MintCertRequest) Descriptor() ([]byte, []int) { - return file_ateapi_proto_rawDescGZIP(), []int{20} + return file_ateapi_proto_rawDescGZIP(), []int{25} } func (x *MintCertRequest) GetAppId() string { @@ -1267,7 +1605,7 @@ type MintCertResponse struct { func (x *MintCertResponse) Reset() { *x = MintCertResponse{} - mi := &file_ateapi_proto_msgTypes[21] + mi := &file_ateapi_proto_msgTypes[26] ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) ms.StoreMessageInfo(mi) } @@ -1279,7 +1617,7 @@ func (x *MintCertResponse) String() string { func (*MintCertResponse) ProtoMessage() {} func (x *MintCertResponse) ProtoReflect() protoreflect.Message { - mi := &file_ateapi_proto_msgTypes[21] + mi := &file_ateapi_proto_msgTypes[26] if x != nil { ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) if ms.LoadMessageInfo() == nil { @@ -1292,7 +1630,7 @@ func (x *MintCertResponse) ProtoReflect() protoreflect.Message { // Deprecated: Use MintCertResponse.ProtoReflect.Descriptor instead. func (*MintCertResponse) Descriptor() ([]byte, []int) { - return file_ateapi_proto_rawDescGZIP(), []int{21} + return file_ateapi_proto_rawDescGZIP(), []int{26} } func (x *MintCertResponse) GetSessionCertificates() [][]byte { @@ -1306,7 +1644,17 @@ var File_ateapi_proto protoreflect.FileDescriptor const file_ateapi_proto_rawDesc = "" + "\n" + - "\fateapi.proto\x12\x06ateapi\"\xbf\x04\n" + + "\fateapi.proto\x12\x06ateapi\"F\n" + + "\x14ExternalSnapshotInfo\x12.\n" + + "\x13snapshot_uri_prefix\x18\x01 \x01(\tR\x11snapshotUriPrefix\"~\n" + + "\x11LocalSnapshotInfo\x12'\n" + + "\x0fsnapshot_prefix\x18\x01 \x01(\tR\x0esnapshotPrefix\x12@\n" + + "\x1dnode_vms_with_local_snapshots\x18\x02 \x03(\tR\x19nodeVmsWithLocalSnapshots\"\xaf\x01\n" + + "\fSnapshotInfo\x12(\n" + + "\x04type\x18\x01 \x01(\x0e2\x14.ateapi.SnapshotTypeR\x04type\x12:\n" + + "\bexternal\x18\x02 \x01(\v2\x1c.ateapi.ExternalSnapshotInfoH\x00R\bexternal\x121\n" + + "\x05local\x18\x03 \x01(\v2\x19.ateapi.LocalSnapshotInfoH\x00R\x05localB\x06\n" + + "\x04data\"\x90\x05\n" + "\x05Actor\x12\x19\n" + "\bactor_id\x18\x01 \x01(\tR\aactorId\x12\x18\n" + "\aversion\x18\x02 \x01(\x03R\aversion\x128\n" + @@ -1316,17 +1664,20 @@ const file_ateapi_proto_rawDesc = "" + "\x13ateom_pod_namespace\x18\x06 \x01(\tR\x11ateomPodNamespace\x12$\n" + "\x0eateom_pod_name\x18\a \x01(\tR\fateomPodName\x12 \n" + "\fateom_pod_ip\x18\b \x01(\tR\n" + - "ateomPodIp\x12#\n" + - "\rlast_snapshot\x18\t \x01(\tR\flastSnapshot\x120\n" + + "ateomPodIp\x120\n" + "\x14in_progress_snapshot\x18\n" + " \x01(\tR\x12inProgressSnapshot\x12\"\n" + - "\rateom_pod_uid\x18\v \x01(\tR\vateomPodUid\"v\n" + + "\rateom_pod_uid\x18\v \x01(\tR\vateomPodUid\x12F\n" + + "\x14latest_snapshot_info\x18\f \x01(\v2\x14.ateapi.SnapshotInfoR\x12latestSnapshotInfo\"\x9d\x01\n" + "\x06Status\x12\x16\n" + "\x12STATUS_UNSPECIFIED\x10\x00\x12\x13\n" + "\x0fSTATUS_RESUMING\x10\x01\x12\x12\n" + "\x0eSTATUS_RUNNING\x10\x02\x12\x15\n" + "\x11STATUS_SUSPENDING\x10\x03\x12\x14\n" + - "\x10STATUS_SUSPENDED\x10\x04\",\n" + + "\x10STATUS_SUSPENDED\x10\x04\x12\x12\n" + + "\x0eSTATUS_PAUSING\x10\x05\x12\x11\n" + + "\rSTATUS_PAUSED\x10\x06J\x04\b\t\x10\n" + + "\",\n" + "\x0fGetActorRequest\x12\x19\n" + "\bactor_id\x18\x01 \x01(\tR\aactorId\"7\n" + "\x10GetActorResponse\x12#\n" + @@ -1340,6 +1691,10 @@ const file_ateapi_proto_rawDesc = "" + "\x13SuspendActorRequest\x12\x19\n" + "\bactor_id\x18\x01 \x01(\tR\aactorId\";\n" + "\x14SuspendActorResponse\x12#\n" + + "\x05actor\x18\x01 \x01(\v2\r.ateapi.ActorR\x05actor\".\n" + + "\x11PauseActorRequest\x12\x19\n" + + "\bactor_id\x18\x01 \x01(\tR\aactorId\"9\n" + + "\x12PauseActorResponse\x12#\n" + "\x05actor\x18\x01 \x01(\v2\r.ateapi.ActorR\x05actor\"C\n" + "\x12ResumeActorRequest\x12\x19\n" + "\bactor_id\x18\x01 \x01(\tR\aactorId\x12\x12\n" + @@ -1358,7 +1713,7 @@ const file_ateapi_proto_rawDesc = "" + "page_token\x18\x02 \x01(\tR\tpageToken\"c\n" + "\x12ListActorsResponse\x12%\n" + "\x06actors\x18\x01 \x03(\v2\r.ateapi.ActorR\x06actors\x12&\n" + - "\x0fnext_page_token\x18\x02 \x01(\tR\rnextPageToken\"\xae\x02\n" + + "\x0fnext_page_token\x18\x02 \x01(\tR\rnextPageToken\"\xcb\x02\n" + "\x06Worker\x12)\n" + "\x10worker_namespace\x18\x01 \x01(\tR\x0fworkerNamespace\x12\x1f\n" + "\vworker_pool\x18\x02 \x01(\tR\n" + @@ -1370,7 +1725,9 @@ const file_ateapi_proto_rawDesc = "" + "\bactor_id\x18\x06 \x01(\tR\aactorId\x12\x0e\n" + "\x02ip\x18\a \x01(\tR\x02ip\x12\x18\n" + "\aversion\x18\b \x01(\x03R\aversion\x12$\n" + - "\x0eworker_pod_uid\x18\t \x01(\tR\fworkerPodUid\"\x13\n" + + "\x0eworker_pod_uid\x18\t \x01(\tR\fworkerPodUid\x12\x1b\n" + + "\tnode_name\x18\n" + + " \x01(\tR\bnodeName\"\x13\n" + "\x11DebugClearRequest\"\x14\n" + "\x12DebugClearResponse\"{\n" + "\x0eMintJWTRequest\x12\x1a\n" + @@ -1389,11 +1746,17 @@ const file_ateapi_proto_rawDesc = "" + "session_id\x18\x03 \x01(\tR\tsessionId\x12>\n" + "\x1bcertificate_signing_request\x18\x04 \x01(\fR\x19certificateSigningRequest\"E\n" + "\x10MintCertResponse\x121\n" + - "\x14session_certificates\x18\x01 \x03(\fR\x13sessionCertificates2\xcd\x04\n" + + "\x14session_certificates\x18\x01 \x03(\fR\x13sessionCertificates*b\n" + + "\fSnapshotType\x12\x1d\n" + + "\x19SNAPSHOT_TYPE_UNSPECIFIED\x10\x00\x12\x17\n" + + "\x13SNAPSHOT_TYPE_LOCAL\x10\x01\x12\x1a\n" + + "\x16SNAPSHOT_TYPE_EXTERNAL\x10\x022\x94\x05\n" + "\aControl\x12?\n" + "\bGetActor\x12\x17.ateapi.GetActorRequest\x1a\x18.ateapi.GetActorResponse\"\x00\x12H\n" + "\vCreateActor\x12\x1a.ateapi.CreateActorRequest\x1a\x1b.ateapi.CreateActorResponse\"\x00\x12K\n" + - "\fSuspendActor\x12\x1b.ateapi.SuspendActorRequest\x1a\x1c.ateapi.SuspendActorResponse\"\x00\x12H\n" + + "\fSuspendActor\x12\x1b.ateapi.SuspendActorRequest\x1a\x1c.ateapi.SuspendActorResponse\"\x00\x12E\n" + + "\n" + + "PauseActor\x12\x19.ateapi.PauseActorRequest\x1a\x1a.ateapi.PauseActorResponse\"\x00\x12H\n" + "\vResumeActor\x12\x1a.ateapi.ResumeActorRequest\x1a\x1b.ateapi.ResumeActorResponse\"\x00\x12H\n" + "\vDeleteActor\x12\x1a.ateapi.DeleteActorRequest\x1a\x1b.ateapi.DeleteActorResponse\"\x00\x12H\n" + "\vListWorkers\x12\x1a.ateapi.ListWorkersRequest\x1a\x1b.ateapi.ListWorkersResponse\"\x00\x12E\n" + @@ -1417,66 +1780,79 @@ func file_ateapi_proto_rawDescGZIP() []byte { return file_ateapi_proto_rawDescData } -var file_ateapi_proto_enumTypes = make([]protoimpl.EnumInfo, 1) -var file_ateapi_proto_msgTypes = make([]protoimpl.MessageInfo, 22) +var file_ateapi_proto_enumTypes = make([]protoimpl.EnumInfo, 2) +var file_ateapi_proto_msgTypes = make([]protoimpl.MessageInfo, 27) var file_ateapi_proto_goTypes = []any{ - (Actor_Status)(0), // 0: ateapi.Actor.Status - (*Actor)(nil), // 1: ateapi.Actor - (*GetActorRequest)(nil), // 2: ateapi.GetActorRequest - (*GetActorResponse)(nil), // 3: ateapi.GetActorResponse - (*CreateActorRequest)(nil), // 4: ateapi.CreateActorRequest - (*CreateActorResponse)(nil), // 5: ateapi.CreateActorResponse - (*SuspendActorRequest)(nil), // 6: ateapi.SuspendActorRequest - (*SuspendActorResponse)(nil), // 7: ateapi.SuspendActorResponse - (*ResumeActorRequest)(nil), // 8: ateapi.ResumeActorRequest - (*ResumeActorResponse)(nil), // 9: ateapi.ResumeActorResponse - (*DeleteActorRequest)(nil), // 10: ateapi.DeleteActorRequest - (*DeleteActorResponse)(nil), // 11: ateapi.DeleteActorResponse - (*ListWorkersRequest)(nil), // 12: ateapi.ListWorkersRequest - (*ListWorkersResponse)(nil), // 13: ateapi.ListWorkersResponse - (*ListActorsRequest)(nil), // 14: ateapi.ListActorsRequest - (*ListActorsResponse)(nil), // 15: ateapi.ListActorsResponse - (*Worker)(nil), // 16: ateapi.Worker - (*DebugClearRequest)(nil), // 17: ateapi.DebugClearRequest - (*DebugClearResponse)(nil), // 18: ateapi.DebugClearResponse - (*MintJWTRequest)(nil), // 19: ateapi.MintJWTRequest - (*MintJWTResponse)(nil), // 20: ateapi.MintJWTResponse - (*MintCertRequest)(nil), // 21: ateapi.MintCertRequest - (*MintCertResponse)(nil), // 22: ateapi.MintCertResponse + (SnapshotType)(0), // 0: ateapi.SnapshotType + (Actor_Status)(0), // 1: ateapi.Actor.Status + (*ExternalSnapshotInfo)(nil), // 2: ateapi.ExternalSnapshotInfo + (*LocalSnapshotInfo)(nil), // 3: ateapi.LocalSnapshotInfo + (*SnapshotInfo)(nil), // 4: ateapi.SnapshotInfo + (*Actor)(nil), // 5: ateapi.Actor + (*GetActorRequest)(nil), // 6: ateapi.GetActorRequest + (*GetActorResponse)(nil), // 7: ateapi.GetActorResponse + (*CreateActorRequest)(nil), // 8: ateapi.CreateActorRequest + (*CreateActorResponse)(nil), // 9: ateapi.CreateActorResponse + (*SuspendActorRequest)(nil), // 10: ateapi.SuspendActorRequest + (*SuspendActorResponse)(nil), // 11: ateapi.SuspendActorResponse + (*PauseActorRequest)(nil), // 12: ateapi.PauseActorRequest + (*PauseActorResponse)(nil), // 13: ateapi.PauseActorResponse + (*ResumeActorRequest)(nil), // 14: ateapi.ResumeActorRequest + (*ResumeActorResponse)(nil), // 15: ateapi.ResumeActorResponse + (*DeleteActorRequest)(nil), // 16: ateapi.DeleteActorRequest + (*DeleteActorResponse)(nil), // 17: ateapi.DeleteActorResponse + (*ListWorkersRequest)(nil), // 18: ateapi.ListWorkersRequest + (*ListWorkersResponse)(nil), // 19: ateapi.ListWorkersResponse + (*ListActorsRequest)(nil), // 20: ateapi.ListActorsRequest + (*ListActorsResponse)(nil), // 21: ateapi.ListActorsResponse + (*Worker)(nil), // 22: ateapi.Worker + (*DebugClearRequest)(nil), // 23: ateapi.DebugClearRequest + (*DebugClearResponse)(nil), // 24: ateapi.DebugClearResponse + (*MintJWTRequest)(nil), // 25: ateapi.MintJWTRequest + (*MintJWTResponse)(nil), // 26: ateapi.MintJWTResponse + (*MintCertRequest)(nil), // 27: ateapi.MintCertRequest + (*MintCertResponse)(nil), // 28: ateapi.MintCertResponse } var file_ateapi_proto_depIdxs = []int32{ - 0, // 0: ateapi.Actor.status:type_name -> ateapi.Actor.Status - 1, // 1: ateapi.GetActorResponse.actor:type_name -> ateapi.Actor - 1, // 2: ateapi.CreateActorResponse.actor:type_name -> ateapi.Actor - 1, // 3: ateapi.SuspendActorResponse.actor:type_name -> ateapi.Actor - 1, // 4: ateapi.ResumeActorResponse.actor:type_name -> ateapi.Actor - 16, // 5: ateapi.ListWorkersResponse.workers:type_name -> ateapi.Worker - 1, // 6: ateapi.ListActorsResponse.actors:type_name -> ateapi.Actor - 2, // 7: ateapi.Control.GetActor:input_type -> ateapi.GetActorRequest - 4, // 8: ateapi.Control.CreateActor:input_type -> ateapi.CreateActorRequest - 6, // 9: ateapi.Control.SuspendActor:input_type -> ateapi.SuspendActorRequest - 8, // 10: ateapi.Control.ResumeActor:input_type -> ateapi.ResumeActorRequest - 10, // 11: ateapi.Control.DeleteActor:input_type -> ateapi.DeleteActorRequest - 12, // 12: ateapi.Control.ListWorkers:input_type -> ateapi.ListWorkersRequest - 14, // 13: ateapi.Control.ListActors:input_type -> ateapi.ListActorsRequest - 17, // 14: ateapi.Control.DebugClear:input_type -> ateapi.DebugClearRequest - 19, // 15: ateapi.SessionIdentity.MintJWT:input_type -> ateapi.MintJWTRequest - 21, // 16: ateapi.SessionIdentity.MintCert:input_type -> ateapi.MintCertRequest - 3, // 17: ateapi.Control.GetActor:output_type -> ateapi.GetActorResponse - 5, // 18: ateapi.Control.CreateActor:output_type -> ateapi.CreateActorResponse - 7, // 19: ateapi.Control.SuspendActor:output_type -> ateapi.SuspendActorResponse - 9, // 20: ateapi.Control.ResumeActor:output_type -> ateapi.ResumeActorResponse - 11, // 21: ateapi.Control.DeleteActor:output_type -> ateapi.DeleteActorResponse - 13, // 22: ateapi.Control.ListWorkers:output_type -> ateapi.ListWorkersResponse - 15, // 23: ateapi.Control.ListActors:output_type -> ateapi.ListActorsResponse - 18, // 24: ateapi.Control.DebugClear:output_type -> ateapi.DebugClearResponse - 20, // 25: ateapi.SessionIdentity.MintJWT:output_type -> ateapi.MintJWTResponse - 22, // 26: ateapi.SessionIdentity.MintCert:output_type -> ateapi.MintCertResponse - 17, // [17:27] is the sub-list for method output_type - 7, // [7:17] is the sub-list for method input_type - 7, // [7:7] is the sub-list for extension type_name - 7, // [7:7] is the sub-list for extension extendee - 0, // [0:7] is the sub-list for field type_name + 0, // 0: ateapi.SnapshotInfo.type:type_name -> ateapi.SnapshotType + 2, // 1: ateapi.SnapshotInfo.external:type_name -> ateapi.ExternalSnapshotInfo + 3, // 2: ateapi.SnapshotInfo.local:type_name -> ateapi.LocalSnapshotInfo + 1, // 3: ateapi.Actor.status:type_name -> ateapi.Actor.Status + 4, // 4: ateapi.Actor.latest_snapshot_info:type_name -> ateapi.SnapshotInfo + 5, // 5: ateapi.GetActorResponse.actor:type_name -> ateapi.Actor + 5, // 6: ateapi.CreateActorResponse.actor:type_name -> ateapi.Actor + 5, // 7: ateapi.SuspendActorResponse.actor:type_name -> ateapi.Actor + 5, // 8: ateapi.PauseActorResponse.actor:type_name -> ateapi.Actor + 5, // 9: ateapi.ResumeActorResponse.actor:type_name -> ateapi.Actor + 22, // 10: ateapi.ListWorkersResponse.workers:type_name -> ateapi.Worker + 5, // 11: ateapi.ListActorsResponse.actors:type_name -> ateapi.Actor + 6, // 12: ateapi.Control.GetActor:input_type -> ateapi.GetActorRequest + 8, // 13: ateapi.Control.CreateActor:input_type -> ateapi.CreateActorRequest + 10, // 14: ateapi.Control.SuspendActor:input_type -> ateapi.SuspendActorRequest + 12, // 15: ateapi.Control.PauseActor:input_type -> ateapi.PauseActorRequest + 14, // 16: ateapi.Control.ResumeActor:input_type -> ateapi.ResumeActorRequest + 16, // 17: ateapi.Control.DeleteActor:input_type -> ateapi.DeleteActorRequest + 18, // 18: ateapi.Control.ListWorkers:input_type -> ateapi.ListWorkersRequest + 20, // 19: ateapi.Control.ListActors:input_type -> ateapi.ListActorsRequest + 23, // 20: ateapi.Control.DebugClear:input_type -> ateapi.DebugClearRequest + 25, // 21: ateapi.SessionIdentity.MintJWT:input_type -> ateapi.MintJWTRequest + 27, // 22: ateapi.SessionIdentity.MintCert:input_type -> ateapi.MintCertRequest + 7, // 23: ateapi.Control.GetActor:output_type -> ateapi.GetActorResponse + 9, // 24: ateapi.Control.CreateActor:output_type -> ateapi.CreateActorResponse + 11, // 25: ateapi.Control.SuspendActor:output_type -> ateapi.SuspendActorResponse + 13, // 26: ateapi.Control.PauseActor:output_type -> ateapi.PauseActorResponse + 15, // 27: ateapi.Control.ResumeActor:output_type -> ateapi.ResumeActorResponse + 17, // 28: ateapi.Control.DeleteActor:output_type -> ateapi.DeleteActorResponse + 19, // 29: ateapi.Control.ListWorkers:output_type -> ateapi.ListWorkersResponse + 21, // 30: ateapi.Control.ListActors:output_type -> ateapi.ListActorsResponse + 24, // 31: ateapi.Control.DebugClear:output_type -> ateapi.DebugClearResponse + 26, // 32: ateapi.SessionIdentity.MintJWT:output_type -> ateapi.MintJWTResponse + 28, // 33: ateapi.SessionIdentity.MintCert:output_type -> ateapi.MintCertResponse + 23, // [23:34] is the sub-list for method output_type + 12, // [12:23] is the sub-list for method input_type + 12, // [12:12] is the sub-list for extension type_name + 12, // [12:12] is the sub-list for extension extendee + 0, // [0:12] is the sub-list for field type_name } func init() { file_ateapi_proto_init() } @@ -1484,13 +1860,17 @@ func file_ateapi_proto_init() { if File_ateapi_proto != nil { return } + file_ateapi_proto_msgTypes[2].OneofWrappers = []any{ + (*SnapshotInfo_External)(nil), + (*SnapshotInfo_Local)(nil), + } type x struct{} out := protoimpl.TypeBuilder{ File: protoimpl.DescBuilder{ GoPackagePath: reflect.TypeOf(x{}).PkgPath(), RawDescriptor: unsafe.Slice(unsafe.StringData(file_ateapi_proto_rawDesc), len(file_ateapi_proto_rawDesc)), - NumEnums: 1, - NumMessages: 22, + NumEnums: 2, + NumMessages: 27, NumExtensions: 0, NumServices: 2, }, diff --git a/pkg/proto/ateapipb/ateapi.proto b/pkg/proto/ateapipb/ateapi.proto index 8cce2b41c..a4ebec098 100644 --- a/pkg/proto/ateapipb/ateapi.proto +++ b/pkg/proto/ateapipb/ateapi.proto @@ -32,6 +32,9 @@ service Control { // Suspend a given actor to a new snapshot. rpc SuspendActor(SuspendActorRequest) returns (SuspendActorResponse) {} + // Pause a given actor and keep its snapshots on node VM. + rpc PauseActor(PauseActorRequest) returns (PauseActorResponse) {} + // Resume an actor from its latest snapshot. rpc ResumeActor(ResumeActorRequest) returns (ResumeActorResponse) {} @@ -48,6 +51,30 @@ service Control { rpc DebugClear(DebugClearRequest) returns (DebugClearResponse) {} } +enum SnapshotType { + SNAPSHOT_TYPE_UNSPECIFIED = 0; + SNAPSHOT_TYPE_LOCAL = 1; + SNAPSHOT_TYPE_EXTERNAL = 2; +} + +message ExternalSnapshotInfo { + string snapshot_uri_prefix = 1; +} + +message LocalSnapshotInfo { + string snapshot_prefix = 1; + // Node VMs that have local snapshots for this actor, while it's PAUSED. + repeated string node_vms_with_local_snapshots = 2; +} + +message SnapshotInfo { + SnapshotType type = 1; + oneof data { + ExternalSnapshotInfo external = 2; + LocalSnapshotInfo local = 3; + } +} + message Actor { string actor_id = 1; int64 version = 2; @@ -61,6 +88,8 @@ message Actor { STATUS_RUNNING = 2; STATUS_SUSPENDING = 3; STATUS_SUSPENDED = 4; + STATUS_PAUSING = 5; + STATUS_PAUSED = 6; } Status status = 5; @@ -68,9 +97,11 @@ message Actor { string ateom_pod_name = 7; string ateom_pod_ip = 8; - string last_snapshot = 9; + // last_snapshot + reserved 9; string in_progress_snapshot = 10; string ateom_pod_uid = 11; + SnapshotInfo latest_snapshot_info = 12; } message GetActorRequest { @@ -105,6 +136,14 @@ message SuspendActorResponse { Actor actor = 1; } +message PauseActorRequest { + string actor_id = 1; +} + +message PauseActorResponse { + Actor actor = 1; +} + message ResumeActorRequest { string actor_id = 1; @@ -162,6 +201,7 @@ message Worker { string ip = 7; int64 version = 8; string worker_pod_uid = 9; + string node_name = 10; } message DebugClearRequest {} diff --git a/pkg/proto/ateapipb/ateapi_grpc.pb.go b/pkg/proto/ateapipb/ateapi_grpc.pb.go index c79b0cbff..bd8ebf726 100644 --- a/pkg/proto/ateapipb/ateapi_grpc.pb.go +++ b/pkg/proto/ateapipb/ateapi_grpc.pb.go @@ -38,6 +38,7 @@ const ( Control_GetActor_FullMethodName = "/ateapi.Control/GetActor" Control_CreateActor_FullMethodName = "/ateapi.Control/CreateActor" Control_SuspendActor_FullMethodName = "/ateapi.Control/SuspendActor" + Control_PauseActor_FullMethodName = "/ateapi.Control/PauseActor" Control_ResumeActor_FullMethodName = "/ateapi.Control/ResumeActor" Control_DeleteActor_FullMethodName = "/ateapi.Control/DeleteActor" Control_ListWorkers_FullMethodName = "/ateapi.Control/ListWorkers" @@ -57,6 +58,8 @@ type ControlClient interface { CreateActor(ctx context.Context, in *CreateActorRequest, opts ...grpc.CallOption) (*CreateActorResponse, error) // Suspend a given actor to a new snapshot. SuspendActor(ctx context.Context, in *SuspendActorRequest, opts ...grpc.CallOption) (*SuspendActorResponse, error) + // Pause a given actor and keep its snapshots on node VM. + PauseActor(ctx context.Context, in *PauseActorRequest, opts ...grpc.CallOption) (*PauseActorResponse, error) // Resume an actor from its latest snapshot. ResumeActor(ctx context.Context, in *ResumeActorRequest, opts ...grpc.CallOption) (*ResumeActorResponse, error) // Delete an actor. Only suspended actors can be deleted. @@ -107,6 +110,16 @@ func (c *controlClient) SuspendActor(ctx context.Context, in *SuspendActorReques return out, nil } +func (c *controlClient) PauseActor(ctx context.Context, in *PauseActorRequest, opts ...grpc.CallOption) (*PauseActorResponse, error) { + cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...) + out := new(PauseActorResponse) + err := c.cc.Invoke(ctx, Control_PauseActor_FullMethodName, in, out, cOpts...) + if err != nil { + return nil, err + } + return out, nil +} + func (c *controlClient) ResumeActor(ctx context.Context, in *ResumeActorRequest, opts ...grpc.CallOption) (*ResumeActorResponse, error) { cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...) out := new(ResumeActorResponse) @@ -169,6 +182,8 @@ type ControlServer interface { CreateActor(context.Context, *CreateActorRequest) (*CreateActorResponse, error) // Suspend a given actor to a new snapshot. SuspendActor(context.Context, *SuspendActorRequest) (*SuspendActorResponse, error) + // Pause a given actor and keep its snapshots on node VM. + PauseActor(context.Context, *PauseActorRequest) (*PauseActorResponse, error) // Resume an actor from its latest snapshot. ResumeActor(context.Context, *ResumeActorRequest) (*ResumeActorResponse, error) // Delete an actor. Only suspended actors can be deleted. @@ -198,6 +213,9 @@ func (UnimplementedControlServer) CreateActor(context.Context, *CreateActorReque func (UnimplementedControlServer) SuspendActor(context.Context, *SuspendActorRequest) (*SuspendActorResponse, error) { return nil, status.Error(codes.Unimplemented, "method SuspendActor not implemented") } +func (UnimplementedControlServer) PauseActor(context.Context, *PauseActorRequest) (*PauseActorResponse, error) { + return nil, status.Error(codes.Unimplemented, "method PauseActor not implemented") +} func (UnimplementedControlServer) ResumeActor(context.Context, *ResumeActorRequest) (*ResumeActorResponse, error) { return nil, status.Error(codes.Unimplemented, "method ResumeActor not implemented") } @@ -288,6 +306,24 @@ func _Control_SuspendActor_Handler(srv interface{}, ctx context.Context, dec fun return interceptor(ctx, in, info, handler) } +func _Control_PauseActor_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { + in := new(PauseActorRequest) + if err := dec(in); err != nil { + return nil, err + } + if interceptor == nil { + return srv.(ControlServer).PauseActor(ctx, in) + } + info := &grpc.UnaryServerInfo{ + Server: srv, + FullMethod: Control_PauseActor_FullMethodName, + } + handler := func(ctx context.Context, req interface{}) (interface{}, error) { + return srv.(ControlServer).PauseActor(ctx, req.(*PauseActorRequest)) + } + return interceptor(ctx, in, info, handler) +} + func _Control_ResumeActor_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { in := new(ResumeActorRequest) if err := dec(in); err != nil { @@ -397,6 +433,10 @@ var Control_ServiceDesc = grpc.ServiceDesc{ MethodName: "SuspendActor", Handler: _Control_SuspendActor_Handler, }, + { + MethodName: "PauseActor", + Handler: _Control_PauseActor_Handler, + }, { MethodName: "ResumeActor", Handler: _Control_ResumeActor_Handler,