From ed4bc0edfb64586868f2883022a9ca7b7200948d Mon Sep 17 00:00:00 2001 From: NekoPunch Date: Fri, 14 Aug 2026 03:05:37 -0700 Subject: [PATCH] feat(ateapi): record last_resume_time on actors Timestamps the RUNNING transition, one of #277's two remaining fields (create_time and update_time already live on ResourceMetadata). total_running_duration stays out: it is a cross-replica subtraction that clock skew can drive negative into a monotonic counter, and the crash cut-off semantics are undefined. Rollout: ateredis unmarshals with strict protojson, so roll all ateapi replicas forward together and clear lastResumeTime from stored actors before any rollback. Claude-Session: https://claude.ai/code/session_01XuQqkwLf5Zx6CSZFHSC6hb --- .../internal/controlapi/functional_test.go | 6 +- .../internal/controlapi/workflow_resume.go | 2 + .../controlapi/workflow_resume_test.go | 30 ++ .../controlapi/workflow_suspend_test.go | 35 ++ pkg/proto/ateapipb/ateapi.pb.go | 315 +++++++++--------- pkg/proto/ateapipb/ateapi.proto | 3 + 6 files changed, 237 insertions(+), 154 deletions(-) diff --git a/cmd/ateapi/internal/controlapi/functional_test.go b/cmd/ateapi/internal/controlapi/functional_test.go index 0087b0da1..3130046ff 100644 --- a/cmd/ateapi/internal/controlapi/functional_test.go +++ b/cmd/ateapi/internal/controlapi/functional_test.go @@ -79,6 +79,7 @@ var ( ignoreUID = protocmp.IgnoreFields(&ateapipb.ResourceMetadata{}, "uid") ignoreVersion = protocmp.IgnoreFields(&ateapipb.ResourceMetadata{}, "version") ignoreTimestamps = protocmp.IgnoreFields(&ateapipb.ResourceMetadata{}, "create_time", "update_time") + ignoreResumeTime = protocmp.IgnoreFields(&ateapipb.Actor{}, "last_resume_time") ) func TestMain(m *testing.M) { @@ -1750,7 +1751,7 @@ func TestResumeActor(t *testing.T) { WorkerPodIp: "127.0.0.1", }, } - if diff := cmp.Diff(want, getResp, protocmp.Transform(), ignoreUID, ignoreVersion, ignoreTimestamps, protocmp.IgnoreFields(&ateapipb.WorkerAssignment{}, "worker_pod_uid")); diff != "" { + if diff := cmp.Diff(want, getResp, protocmp.Transform(), ignoreUID, ignoreVersion, ignoreTimestamps, ignoreResumeTime, protocmp.IgnoreFields(&ateapipb.WorkerAssignment{}, "worker_pod_uid")); diff != "" { t.Errorf("GetActor response mismatch (-want +got):\n%s", diff) } @@ -2226,7 +2227,7 @@ func TestSuspendActor(t *testing.T) { ignoreUID, ignoreVersion, ignoreTimestamps, - protocmp.IgnoreFields(&ateapipb.Actor{}, "latest_snapshot"), + protocmp.IgnoreFields(&ateapipb.Actor{}, "latest_snapshot", "last_resume_time"), ); diff != "" { t.Errorf("GetActor response mismatch (-want +got):\n%s", diff) } @@ -2318,6 +2319,7 @@ func TestPauseActor(t *testing.T) { ignoreUID, ignoreVersion, ignoreTimestamps, + ignoreResumeTime, protocmp.IgnoreFields(&ateapipb.WorkerAssignment{}, "worker_pod_uid"), protocmp.IgnoreFields(&ateapipb.LocalSnapshotInfo{}, "snapshot_name"), ); diff != "" { diff --git a/cmd/ateapi/internal/controlapi/workflow_resume.go b/cmd/ateapi/internal/controlapi/workflow_resume.go index bdf999a3f..cbb747b44 100644 --- a/cmd/ateapi/internal/controlapi/workflow_resume.go +++ b/cmd/ateapi/internal/controlapi/workflow_resume.go @@ -31,6 +31,7 @@ import ( "google.golang.org/grpc/codes" "google.golang.org/grpc/status" "google.golang.org/protobuf/proto" + "google.golang.org/protobuf/types/known/timestamppb" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" "k8s.io/apimachinery/pkg/labels" "k8s.io/apimachinery/pkg/util/wait" @@ -762,6 +763,7 @@ func (w *ActorWorkflow) finalizeRunning(ctx context.Context, actorRef resources. storedActor, err := w.store.UpdateActor(ctx, actorRef, store.WithPrecondition(latestActor, func(toUpdate *ateapipb.Actor) error { toUpdate.Status = ateapipb.Actor_STATUS_RUNNING + toUpdate.LastResumeTime = timestamppb.Now() return nil })) if err != nil { diff --git a/cmd/ateapi/internal/controlapi/workflow_resume_test.go b/cmd/ateapi/internal/controlapi/workflow_resume_test.go index b9b1274b4..1b1957ec8 100644 --- a/cmd/ateapi/internal/controlapi/workflow_resume_test.go +++ b/cmd/ateapi/internal/controlapi/workflow_resume_test.go @@ -611,6 +611,36 @@ func TestResumeActor_CrashesOnMissingWorkerAssignment(t *testing.T) { } } +func TestFinalizeRunning_SetsLastResumeTime(t *testing.T) { + ctx := context.Background() + st, cleanup := storetest.SetupTestStore(t) + defer cleanup() + w := newTestActorWorkflow(t, st, "ns", "tmpl1") + + ref := resources.ActorRef{Atespace: "team-a", Name: "id1"} + seedWorkflowActor(t, ctx, st, ref, "ns", "tmpl1", ateapipb.Actor_STATUS_RESUMING) + + before := time.Now() + if _, err := w.finalizeRunning(ctx, ref); err != nil { + t.Fatalf("finalizeRunning: %v", err) + } + + got, err := st.GetActor(ctx, ref) + if err != nil { + t.Fatalf("GetActor: %v", err) + } + if got.GetStatus() != ateapipb.Actor_STATUS_RUNNING { + t.Errorf("status = %v, want RUNNING", got.GetStatus()) + } + lrt := got.GetLastResumeTime() + if lrt == nil { + t.Fatal("LastResumeTime not set") + } + if ts := lrt.AsTime(); ts.Before(before.Add(-time.Second)) || ts.After(time.Now().Add(time.Second)) { + t.Errorf("LastResumeTime = %v, want within test window", ts) + } +} + // TestValidateAssignedWorker_WorkerOwnership verifies that RESUMING recovery // only proceeds on a worker whose assignment still names this actor: the // recovery path loads the worker by pod name only, so the assignment may have diff --git a/cmd/ateapi/internal/controlapi/workflow_suspend_test.go b/cmd/ateapi/internal/controlapi/workflow_suspend_test.go index 38c42c318..2c8ec2b05 100644 --- a/cmd/ateapi/internal/controlapi/workflow_suspend_test.go +++ b/cmd/ateapi/internal/controlapi/workflow_suspend_test.go @@ -18,6 +18,7 @@ import ( "context" "errors" "testing" + "time" "github.com/agent-substrate/substrate/cmd/ateapi/internal/store" "github.com/agent-substrate/substrate/cmd/ateapi/internal/store/ateredis" @@ -30,6 +31,7 @@ import ( "github.com/redis/go-redis/v9" "google.golang.org/grpc/codes" "google.golang.org/grpc/status" + "google.golang.org/protobuf/types/known/timestamppb" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" "k8s.io/client-go/tools/cache" ) @@ -366,6 +368,39 @@ func TestEnsureSuspendedFinalized_NoAssignment(t *testing.T) { } } +// LastResumeTime records the last transition into RUNNING, so suspending must +// leave it alone: it stays queryable while the actor is suspended. +func TestEnsureSuspendedFinalized_KeepsLastResumeTime(t *testing.T) { + ctx := context.Background() + persistence := newTestPersistence(t) + + resumedAt := timestamppb.New(time.Date(2026, 1, 1, 0, 0, 0, 0, time.UTC)) + actor := &ateapipb.Actor{ + Metadata: &ateapipb.ResourceMetadata{Atespace: "team-a", Name: "actor-1"}, + Status: ateapipb.Actor_STATUS_SUSPENDING, + InProgressSnapshotName: "2026-01-01t00-00-00z-abc", + InProgressSnapshotSourceActorVersion: 1, + LastResumeTime: resumedAt, + } + if _, err := persistence.CreateActor(ctx, actor); err != nil { + t.Fatalf("CreateActor: %v", err) + } + + w := &ActorWorkflow{store: persistence} + tmpl := &atev1alpha1.ActorTemplate{Spec: atev1alpha1.ActorTemplateSpec{SnapshotsConfig: atev1alpha1.SnapshotsConfig{Location: "gs://snapshots"}}} + stored, err := w.ensureSuspendedFinalized(ctx, resources.ActorRef{Atespace: "team-a", Name: "actor-1"}, tmpl) + if err != nil { + t.Fatalf("ensureSuspendedFinalized: %v", err) + } + + if stored.GetStatus() != ateapipb.Actor_STATUS_SUSPENDED { + t.Errorf("status = %v, want SUSPENDED", stored.GetStatus()) + } + if got := stored.GetLastResumeTime(); !got.AsTime().Equal(resumedAt.AsTime()) { + t.Errorf("LastResumeTime = %v, want %v preserved through suspend", got.AsTime(), resumedAt.AsTime()) + } +} + func TestEnsureSuspendedFinalized_ReleasesOnlyOwnWorker(t *testing.T) { tests := []struct { name string diff --git a/pkg/proto/ateapipb/ateapi.pb.go b/pkg/proto/ateapipb/ateapi.pb.go index 5510e03a3..538a39066 100644 --- a/pkg/proto/ateapipb/ateapi.pb.go +++ b/pkg/proto/ateapipb/ateapi.pb.go @@ -833,6 +833,8 @@ type Actor struct { LocalSnapshotInfo *LocalSnapshotInfo `protobuf:"bytes,9,opt,name=local_snapshot_info,json=localSnapshotInfo,proto3" json:"local_snapshot_info,omitempty"` // Actor version captured when the current durable snapshot began. InProgressSnapshotSourceActorVersion int64 `protobuf:"varint,10,opt,name=in_progress_snapshot_source_actor_version,json=inProgressSnapshotSourceActorVersion,proto3" json:"in_progress_snapshot_source_actor_version,omitempty"` + // Time of the most recent transition into STATUS_RUNNING. + LastResumeTime *timestamppb.Timestamp `protobuf:"bytes,17,opt,name=last_resume_time,json=lastResumeTime,proto3" json:"last_resume_time,omitempty"` // Volumes attached to the actor. These volumes only live as long as the actor. // They are deleted when the actor is deleted. ActorVolumes []*ExternalVolume `protobuf:"bytes,11,rep,name=actor_volumes,json=actorVolumes,proto3" json:"actor_volumes,omitempty"` @@ -952,6 +954,13 @@ func (x *Actor) GetInProgressSnapshotSourceActorVersion() int64 { return 0 } +func (x *Actor) GetLastResumeTime() *timestamppb.Timestamp { + if x != nil { + return x.LastResumeTime + } + return nil +} + func (x *Actor) GetActorVolumes() []*ExternalVolume { if x != nil { return x.ActorVolumes @@ -5118,7 +5127,7 @@ const file_ateapi_proto_rawDesc = "" + "\x12STATUS_UNSPECIFIED\x10\x00\x12\x12\n" + "\x0eSTATUS_PENDING\x10\x01\x12\x12\n" + "\x0eSTATUS_CREATED\x10\x02\x12\x13\n" + - "\x0fSTATUS_DELETING\x10\x03\"\xcd\b\n" + + "\x0fSTATUS_DELETING\x10\x03\"\x93\t\n" + "\x05Actor\x124\n" + "\bmetadata\x18\x01 \x01(\v2\x18.ateapi.ResourceMetadataR\bmetadata\x128\n" + "\x18actor_template_namespace\x18\x02 \x01(\tR\x16actorTemplateNamespace\x12.\n" + @@ -5131,7 +5140,8 @@ const file_ateapi_proto_rawDesc = "" + "\x0flatest_snapshot\x18\b \x01(\v2\x11.ateapi.ObjectRefR\x0elatestSnapshot\x12I\n" + "\x13local_snapshot_info\x18\t \x01(\v2\x19.ateapi.LocalSnapshotInfoR\x11localSnapshotInfo\x12W\n" + ")in_progress_snapshot_source_actor_version\x18\n" + - " \x01(\x03R$inProgressSnapshotSourceActorVersion\x12;\n" + + " \x01(\x03R$inProgressSnapshotSourceActorVersion\x12D\n" + + "\x10last_resume_time\x18\x11 \x01(\v2\x1a.google.protobuf.TimestampR\x0elastResumeTime\x12;\n" + "\ractor_volumes\x18\v \x03(\v2\x16.ateapi.ExternalVolumeR\factorVolumes\x12D\n" + "\x1fin_progress_local_snapshot_name\x18\f \x01(\tR\x1binProgressLocalSnapshotName\x12D\n" + "\x0fsource_snapshot\x18\x0e \x01(\v2\x1b.ateapi.ActorSnapshotSourceR\x0esourceSnapshot\"\xc6\x01\n" + @@ -5604,156 +5614,157 @@ var file_ateapi_proto_depIdxs = []int32{ 10, // 10: ateapi.Actor.worker_selector:type_name -> ateapi.Selector 18, // 11: ateapi.Actor.latest_snapshot:type_name -> ateapi.ObjectRef 9, // 12: ateapi.Actor.local_snapshot_info:type_name -> ateapi.LocalSnapshotInfo - 12, // 13: ateapi.Actor.actor_volumes:type_name -> ateapi.ExternalVolume - 19, // 14: ateapi.Actor.source_snapshot:type_name -> ateapi.ActorSnapshotSource - 11, // 15: ateapi.ActorSnapshot.metadata:type_name -> ateapi.ResourceMetadata - 18, // 16: ateapi.ActorSnapshot.source_actor:type_name -> ateapi.ObjectRef - 0, // 17: ateapi.ActorSnapshot.content_scope:type_name -> ateapi.SnapshotContentScope - 18, // 18: ateapi.ActorSnapshot.actor_template_version:type_name -> ateapi.ObjectRef - 11, // 19: ateapi.ActorSnapshotTag.metadata:type_name -> ateapi.ResourceMetadata - 18, // 20: ateapi.ActorSnapshotTag.snapshot:type_name -> ateapi.ObjectRef - 1, // 21: ateapi.ActorSnapshotTag.scope:type_name -> ateapi.ActorSnapshotTagScope - 11, // 22: ateapi.Atespace.metadata:type_name -> ateapi.ResourceMetadata - 18, // 23: ateapi.ActorSnapshotSource.tag:type_name -> ateapi.ObjectRef - 18, // 24: ateapi.ActorSnapshotSource.snapshot:type_name -> ateapi.ObjectRef - 11, // 25: ateapi.ActorTemplate.metadata:type_name -> ateapi.ResourceMetadata - 18, // 26: ateapi.ActorTemplate.default_version_on_create:type_name -> ateapi.ObjectRef - 11, // 27: ateapi.ActorTemplateVersion.metadata:type_name -> ateapi.ResourceMetadata - 18, // 28: ateapi.ActorTemplateVersion.actor_template:type_name -> ateapi.ObjectRef - 10, // 29: ateapi.ActorTemplateVersion.worker_selector:type_name -> ateapi.Selector - 26, // 30: ateapi.ActorTemplateVersion.containers:type_name -> ateapi.Container - 30, // 31: ateapi.ActorTemplateVersion.volumes:type_name -> ateapi.Volume - 24, // 32: ateapi.ActorTemplateVersion.snapshots_config:type_name -> ateapi.SnapshotsConfig - 23, // 33: ateapi.ActorTemplateVersion.sandbox_config:type_name -> ateapi.SandboxConfig - 18, // 34: ateapi.ActorTemplateVersion.golden_snapshot:type_name -> ateapi.ObjectRef - 22, // 35: ateapi.ActorTemplateVersion.phase:type_name -> ateapi.ActorTemplateVersionPhase - 7, // 36: ateapi.ActorTemplateVersionPhase.phase:type_name -> ateapi.ActorTemplateVersionPhase.Phase - 2, // 37: ateapi.SandboxConfig.sandbox_class:type_name -> ateapi.SandboxClass - 34, // 38: ateapi.SandboxConfig.sandbox_assets:type_name -> ateapi.SandboxAssets - 0, // 39: ateapi.SnapshotsConfig.on_pause:type_name -> ateapi.SnapshotContentScope - 0, // 40: ateapi.SnapshotsConfig.on_commit:type_name -> ateapi.SnapshotContentScope - 25, // 41: ateapi.SnapshotsConfig.on_resume:type_name -> ateapi.OnResumeConfig - 3, // 42: ateapi.OnResumeConfig.from_data:type_name -> ateapi.ResumeSource - 27, // 43: ateapi.Container.env:type_name -> ateapi.EnvVar - 28, // 44: ateapi.Container.readyz:type_name -> ateapi.ContainerReadyz - 33, // 45: ateapi.Container.volume_mounts:type_name -> ateapi.VolumeMount - 29, // 46: ateapi.ContainerReadyz.http_get:type_name -> ateapi.HTTPGetAction - 31, // 47: ateapi.Volume.durable_dir:type_name -> ateapi.DurableDirVolumeSource - 32, // 48: ateapi.Volume.external_volume_template:type_name -> ateapi.ExternalVolumeTemplate - 2, // 49: ateapi.SandboxAssets.sandbox_class:type_name -> ateapi.SandboxClass - 86, // 50: ateapi.SandboxAssets.assets:type_name -> ateapi.SandboxAssets.AssetsEntry - 87, // 51: ateapi.ArchAssets.files:type_name -> ateapi.ArchAssets.FilesEntry - 17, // 52: ateapi.CreateAtespaceRequest.atespace:type_name -> ateapi.Atespace - 18, // 53: ateapi.GetAtespaceRequest.atespace:type_name -> ateapi.ObjectRef - 17, // 54: ateapi.ListAtespacesResponse.atespaces:type_name -> ateapi.Atespace - 18, // 55: ateapi.DeleteAtespaceRequest.atespace:type_name -> ateapi.ObjectRef - 20, // 56: ateapi.CreateActorTemplateRequest.actor_template:type_name -> ateapi.ActorTemplate - 18, // 57: ateapi.GetActorTemplateRequest.actor_template:type_name -> ateapi.ObjectRef - 20, // 58: ateapi.UpdateActorTemplateRequest.actor_template:type_name -> ateapi.ActorTemplate - 90, // 59: ateapi.UpdateActorTemplateRequest.update_mask:type_name -> google.protobuf.FieldMask - 20, // 60: ateapi.ListActorTemplatesResponse.actor_templates:type_name -> ateapi.ActorTemplate - 18, // 61: ateapi.DeleteActorTemplateRequest.actor_template:type_name -> ateapi.ObjectRef - 21, // 62: ateapi.CreateActorTemplateVersionRequest.actor_template_version:type_name -> ateapi.ActorTemplateVersion - 18, // 63: ateapi.GetActorTemplateVersionRequest.actor_template_version:type_name -> ateapi.ObjectRef - 18, // 64: ateapi.ListActorTemplateVersionsRequest.actor_template:type_name -> ateapi.ObjectRef - 21, // 65: ateapi.ListActorTemplateVersionsResponse.actor_template_versions:type_name -> ateapi.ActorTemplateVersion - 18, // 66: ateapi.DeleteActorTemplateVersionRequest.actor_template_version:type_name -> ateapi.ObjectRef - 18, // 67: ateapi.GetActorRequest.actor:type_name -> ateapi.ObjectRef - 13, // 68: ateapi.CreateActorRequest.actor:type_name -> ateapi.Actor - 13, // 69: ateapi.UpdateActorRequest.actor:type_name -> ateapi.Actor - 90, // 70: ateapi.UpdateActorRequest.update_mask:type_name -> google.protobuf.FieldMask - 18, // 71: ateapi.SuspendActorRequest.actor:type_name -> ateapi.ObjectRef - 13, // 72: ateapi.SuspendActorResponse.actor:type_name -> ateapi.Actor - 18, // 73: ateapi.PauseActorRequest.actor:type_name -> ateapi.ObjectRef - 13, // 74: ateapi.PauseActorResponse.actor:type_name -> ateapi.Actor - 18, // 75: ateapi.ResumeActorRequest.actor:type_name -> ateapi.ObjectRef - 13, // 76: ateapi.ResumeActorResponse.actor:type_name -> ateapi.Actor - 18, // 77: ateapi.DeleteActorRequest.actor:type_name -> ateapi.ObjectRef - 18, // 78: ateapi.GetActorSnapshotRequest.snapshot:type_name -> ateapi.ObjectRef - 18, // 79: ateapi.GetActorSnapshotTagRequest.tag:type_name -> ateapi.ObjectRef - 15, // 80: ateapi.ListActorSnapshotsResponse.snapshots:type_name -> ateapi.ActorSnapshot - 16, // 81: ateapi.CreateActorSnapshotTagRequest.actor_snapshot_tag:type_name -> ateapi.ActorSnapshotTag - 16, // 82: ateapi.UpdateActorSnapshotTagRequest.tag:type_name -> ateapi.ActorSnapshotTag - 90, // 83: ateapi.UpdateActorSnapshotTagRequest.update_mask:type_name -> google.protobuf.FieldMask - 18, // 84: ateapi.DeleteActorSnapshotTagRequest.tag:type_name -> ateapi.ObjectRef - 74, // 85: ateapi.ListWorkersResponse.workers:type_name -> ateapi.Worker - 13, // 86: ateapi.ListActorsResponse.actors:type_name -> ateapi.Actor - 76, // 87: ateapi.Worker.assignment:type_name -> ateapi.Assignment - 88, // 88: ateapi.Worker.labels:type_name -> ateapi.Worker.LabelsEntry - 8, // 89: ateapi.Worker.state:type_name -> ateapi.Worker.State - 75, // 90: ateapi.Worker.capacity:type_name -> ateapi.WorkerCapacity - 77, // 91: ateapi.Assignment.actor_template:type_name -> ateapi.KubeNamespacedObjectRef - 18, // 92: ateapi.Assignment.actor:type_name -> ateapi.ObjectRef - 4, // 93: ateapi.MintCertRequest.purpose:type_name -> ateapi.ActorCertificatePurpose - 35, // 94: ateapi.SandboxAssets.AssetsEntry.value:type_name -> ateapi.ArchAssets - 36, // 95: ateapi.ArchAssets.FilesEntry.value:type_name -> ateapi.AssetFile - 53, // 96: ateapi.Control.GetActor:input_type -> ateapi.GetActorRequest - 54, // 97: ateapi.Control.CreateActor:input_type -> ateapi.CreateActorRequest - 55, // 98: ateapi.Control.UpdateActor:input_type -> ateapi.UpdateActorRequest - 56, // 99: ateapi.Control.SuspendActor:input_type -> ateapi.SuspendActorRequest - 58, // 100: ateapi.Control.PauseActor:input_type -> ateapi.PauseActorRequest - 60, // 101: ateapi.Control.ResumeActor:input_type -> ateapi.ResumeActorRequest - 62, // 102: ateapi.Control.DeleteActor:input_type -> ateapi.DeleteActorRequest - 63, // 103: ateapi.Control.GetActorSnapshot:input_type -> ateapi.GetActorSnapshotRequest - 64, // 104: ateapi.Control.GetActorSnapshotTag:input_type -> ateapi.GetActorSnapshotTagRequest - 65, // 105: ateapi.Control.ListActorSnapshots:input_type -> ateapi.ListActorSnapshotsRequest - 67, // 106: ateapi.Control.CreateActorSnapshotTag:input_type -> ateapi.CreateActorSnapshotTagRequest - 68, // 107: ateapi.Control.UpdateActorSnapshotTag:input_type -> ateapi.UpdateActorSnapshotTagRequest - 69, // 108: ateapi.Control.DeleteActorSnapshotTag:input_type -> ateapi.DeleteActorSnapshotTagRequest - 70, // 109: ateapi.Control.ListWorkers:input_type -> ateapi.ListWorkersRequest - 72, // 110: ateapi.Control.ListActors:input_type -> ateapi.ListActorsRequest - 37, // 111: ateapi.Control.CreateAtespace:input_type -> ateapi.CreateAtespaceRequest - 38, // 112: ateapi.Control.GetAtespace:input_type -> ateapi.GetAtespaceRequest - 39, // 113: ateapi.Control.ListAtespaces:input_type -> ateapi.ListAtespacesRequest - 41, // 114: ateapi.Control.DeleteAtespace:input_type -> ateapi.DeleteAtespaceRequest - 42, // 115: ateapi.Control.CreateActorTemplate:input_type -> ateapi.CreateActorTemplateRequest - 43, // 116: ateapi.Control.GetActorTemplate:input_type -> ateapi.GetActorTemplateRequest - 44, // 117: ateapi.Control.UpdateActorTemplate:input_type -> ateapi.UpdateActorTemplateRequest - 45, // 118: ateapi.Control.ListActorTemplates:input_type -> ateapi.ListActorTemplatesRequest - 47, // 119: ateapi.Control.DeleteActorTemplate:input_type -> ateapi.DeleteActorTemplateRequest - 48, // 120: ateapi.Control.CreateActorTemplateVersion:input_type -> ateapi.CreateActorTemplateVersionRequest - 49, // 121: ateapi.Control.GetActorTemplateVersion:input_type -> ateapi.GetActorTemplateVersionRequest - 50, // 122: ateapi.Control.ListActorTemplateVersions:input_type -> ateapi.ListActorTemplateVersionsRequest - 52, // 123: ateapi.Control.DeleteActorTemplateVersion:input_type -> ateapi.DeleteActorTemplateVersionRequest - 78, // 124: ateapi.Debug.DebugClear:input_type -> ateapi.DebugClearRequest - 80, // 125: ateapi.ActorIdentity.MintJWT:input_type -> ateapi.MintJWTRequest - 82, // 126: ateapi.ActorIdentity.MintCert:input_type -> ateapi.MintCertRequest - 13, // 127: ateapi.Control.GetActor:output_type -> ateapi.Actor - 13, // 128: ateapi.Control.CreateActor:output_type -> ateapi.Actor - 13, // 129: ateapi.Control.UpdateActor:output_type -> ateapi.Actor - 57, // 130: ateapi.Control.SuspendActor:output_type -> ateapi.SuspendActorResponse - 59, // 131: ateapi.Control.PauseActor:output_type -> ateapi.PauseActorResponse - 61, // 132: ateapi.Control.ResumeActor:output_type -> ateapi.ResumeActorResponse - 13, // 133: ateapi.Control.DeleteActor:output_type -> ateapi.Actor - 15, // 134: ateapi.Control.GetActorSnapshot:output_type -> ateapi.ActorSnapshot - 16, // 135: ateapi.Control.GetActorSnapshotTag:output_type -> ateapi.ActorSnapshotTag - 66, // 136: ateapi.Control.ListActorSnapshots:output_type -> ateapi.ListActorSnapshotsResponse - 16, // 137: ateapi.Control.CreateActorSnapshotTag:output_type -> ateapi.ActorSnapshotTag - 16, // 138: ateapi.Control.UpdateActorSnapshotTag:output_type -> ateapi.ActorSnapshotTag - 16, // 139: ateapi.Control.DeleteActorSnapshotTag:output_type -> ateapi.ActorSnapshotTag - 71, // 140: ateapi.Control.ListWorkers:output_type -> ateapi.ListWorkersResponse - 73, // 141: ateapi.Control.ListActors:output_type -> ateapi.ListActorsResponse - 17, // 142: ateapi.Control.CreateAtespace:output_type -> ateapi.Atespace - 17, // 143: ateapi.Control.GetAtespace:output_type -> ateapi.Atespace - 40, // 144: ateapi.Control.ListAtespaces:output_type -> ateapi.ListAtespacesResponse - 17, // 145: ateapi.Control.DeleteAtespace:output_type -> ateapi.Atespace - 20, // 146: ateapi.Control.CreateActorTemplate:output_type -> ateapi.ActorTemplate - 20, // 147: ateapi.Control.GetActorTemplate:output_type -> ateapi.ActorTemplate - 20, // 148: ateapi.Control.UpdateActorTemplate:output_type -> ateapi.ActorTemplate - 46, // 149: ateapi.Control.ListActorTemplates:output_type -> ateapi.ListActorTemplatesResponse - 20, // 150: ateapi.Control.DeleteActorTemplate:output_type -> ateapi.ActorTemplate - 21, // 151: ateapi.Control.CreateActorTemplateVersion:output_type -> ateapi.ActorTemplateVersion - 21, // 152: ateapi.Control.GetActorTemplateVersion:output_type -> ateapi.ActorTemplateVersion - 51, // 153: ateapi.Control.ListActorTemplateVersions:output_type -> ateapi.ListActorTemplateVersionsResponse - 21, // 154: ateapi.Control.DeleteActorTemplateVersion:output_type -> ateapi.ActorTemplateVersion - 79, // 155: ateapi.Debug.DebugClear:output_type -> ateapi.DebugClearResponse - 81, // 156: ateapi.ActorIdentity.MintJWT:output_type -> ateapi.MintJWTResponse - 83, // 157: ateapi.ActorIdentity.MintCert:output_type -> ateapi.MintCertResponse - 127, // [127:158] is the sub-list for method output_type - 96, // [96:127] is the sub-list for method input_type - 96, // [96:96] is the sub-list for extension type_name - 96, // [96:96] is the sub-list for extension extendee - 0, // [0:96] is the sub-list for field type_name + 89, // 13: ateapi.Actor.last_resume_time:type_name -> google.protobuf.Timestamp + 12, // 14: ateapi.Actor.actor_volumes:type_name -> ateapi.ExternalVolume + 19, // 15: ateapi.Actor.source_snapshot:type_name -> ateapi.ActorSnapshotSource + 11, // 16: ateapi.ActorSnapshot.metadata:type_name -> ateapi.ResourceMetadata + 18, // 17: ateapi.ActorSnapshot.source_actor:type_name -> ateapi.ObjectRef + 0, // 18: ateapi.ActorSnapshot.content_scope:type_name -> ateapi.SnapshotContentScope + 18, // 19: ateapi.ActorSnapshot.actor_template_version:type_name -> ateapi.ObjectRef + 11, // 20: ateapi.ActorSnapshotTag.metadata:type_name -> ateapi.ResourceMetadata + 18, // 21: ateapi.ActorSnapshotTag.snapshot:type_name -> ateapi.ObjectRef + 1, // 22: ateapi.ActorSnapshotTag.scope:type_name -> ateapi.ActorSnapshotTagScope + 11, // 23: ateapi.Atespace.metadata:type_name -> ateapi.ResourceMetadata + 18, // 24: ateapi.ActorSnapshotSource.tag:type_name -> ateapi.ObjectRef + 18, // 25: ateapi.ActorSnapshotSource.snapshot:type_name -> ateapi.ObjectRef + 11, // 26: ateapi.ActorTemplate.metadata:type_name -> ateapi.ResourceMetadata + 18, // 27: ateapi.ActorTemplate.default_version_on_create:type_name -> ateapi.ObjectRef + 11, // 28: ateapi.ActorTemplateVersion.metadata:type_name -> ateapi.ResourceMetadata + 18, // 29: ateapi.ActorTemplateVersion.actor_template:type_name -> ateapi.ObjectRef + 10, // 30: ateapi.ActorTemplateVersion.worker_selector:type_name -> ateapi.Selector + 26, // 31: ateapi.ActorTemplateVersion.containers:type_name -> ateapi.Container + 30, // 32: ateapi.ActorTemplateVersion.volumes:type_name -> ateapi.Volume + 24, // 33: ateapi.ActorTemplateVersion.snapshots_config:type_name -> ateapi.SnapshotsConfig + 23, // 34: ateapi.ActorTemplateVersion.sandbox_config:type_name -> ateapi.SandboxConfig + 18, // 35: ateapi.ActorTemplateVersion.golden_snapshot:type_name -> ateapi.ObjectRef + 22, // 36: ateapi.ActorTemplateVersion.phase:type_name -> ateapi.ActorTemplateVersionPhase + 7, // 37: ateapi.ActorTemplateVersionPhase.phase:type_name -> ateapi.ActorTemplateVersionPhase.Phase + 2, // 38: ateapi.SandboxConfig.sandbox_class:type_name -> ateapi.SandboxClass + 34, // 39: ateapi.SandboxConfig.sandbox_assets:type_name -> ateapi.SandboxAssets + 0, // 40: ateapi.SnapshotsConfig.on_pause:type_name -> ateapi.SnapshotContentScope + 0, // 41: ateapi.SnapshotsConfig.on_commit:type_name -> ateapi.SnapshotContentScope + 25, // 42: ateapi.SnapshotsConfig.on_resume:type_name -> ateapi.OnResumeConfig + 3, // 43: ateapi.OnResumeConfig.from_data:type_name -> ateapi.ResumeSource + 27, // 44: ateapi.Container.env:type_name -> ateapi.EnvVar + 28, // 45: ateapi.Container.readyz:type_name -> ateapi.ContainerReadyz + 33, // 46: ateapi.Container.volume_mounts:type_name -> ateapi.VolumeMount + 29, // 47: ateapi.ContainerReadyz.http_get:type_name -> ateapi.HTTPGetAction + 31, // 48: ateapi.Volume.durable_dir:type_name -> ateapi.DurableDirVolumeSource + 32, // 49: ateapi.Volume.external_volume_template:type_name -> ateapi.ExternalVolumeTemplate + 2, // 50: ateapi.SandboxAssets.sandbox_class:type_name -> ateapi.SandboxClass + 86, // 51: ateapi.SandboxAssets.assets:type_name -> ateapi.SandboxAssets.AssetsEntry + 87, // 52: ateapi.ArchAssets.files:type_name -> ateapi.ArchAssets.FilesEntry + 17, // 53: ateapi.CreateAtespaceRequest.atespace:type_name -> ateapi.Atespace + 18, // 54: ateapi.GetAtespaceRequest.atespace:type_name -> ateapi.ObjectRef + 17, // 55: ateapi.ListAtespacesResponse.atespaces:type_name -> ateapi.Atespace + 18, // 56: ateapi.DeleteAtespaceRequest.atespace:type_name -> ateapi.ObjectRef + 20, // 57: ateapi.CreateActorTemplateRequest.actor_template:type_name -> ateapi.ActorTemplate + 18, // 58: ateapi.GetActorTemplateRequest.actor_template:type_name -> ateapi.ObjectRef + 20, // 59: ateapi.UpdateActorTemplateRequest.actor_template:type_name -> ateapi.ActorTemplate + 90, // 60: ateapi.UpdateActorTemplateRequest.update_mask:type_name -> google.protobuf.FieldMask + 20, // 61: ateapi.ListActorTemplatesResponse.actor_templates:type_name -> ateapi.ActorTemplate + 18, // 62: ateapi.DeleteActorTemplateRequest.actor_template:type_name -> ateapi.ObjectRef + 21, // 63: ateapi.CreateActorTemplateVersionRequest.actor_template_version:type_name -> ateapi.ActorTemplateVersion + 18, // 64: ateapi.GetActorTemplateVersionRequest.actor_template_version:type_name -> ateapi.ObjectRef + 18, // 65: ateapi.ListActorTemplateVersionsRequest.actor_template:type_name -> ateapi.ObjectRef + 21, // 66: ateapi.ListActorTemplateVersionsResponse.actor_template_versions:type_name -> ateapi.ActorTemplateVersion + 18, // 67: ateapi.DeleteActorTemplateVersionRequest.actor_template_version:type_name -> ateapi.ObjectRef + 18, // 68: ateapi.GetActorRequest.actor:type_name -> ateapi.ObjectRef + 13, // 69: ateapi.CreateActorRequest.actor:type_name -> ateapi.Actor + 13, // 70: ateapi.UpdateActorRequest.actor:type_name -> ateapi.Actor + 90, // 71: ateapi.UpdateActorRequest.update_mask:type_name -> google.protobuf.FieldMask + 18, // 72: ateapi.SuspendActorRequest.actor:type_name -> ateapi.ObjectRef + 13, // 73: ateapi.SuspendActorResponse.actor:type_name -> ateapi.Actor + 18, // 74: ateapi.PauseActorRequest.actor:type_name -> ateapi.ObjectRef + 13, // 75: ateapi.PauseActorResponse.actor:type_name -> ateapi.Actor + 18, // 76: ateapi.ResumeActorRequest.actor:type_name -> ateapi.ObjectRef + 13, // 77: ateapi.ResumeActorResponse.actor:type_name -> ateapi.Actor + 18, // 78: ateapi.DeleteActorRequest.actor:type_name -> ateapi.ObjectRef + 18, // 79: ateapi.GetActorSnapshotRequest.snapshot:type_name -> ateapi.ObjectRef + 18, // 80: ateapi.GetActorSnapshotTagRequest.tag:type_name -> ateapi.ObjectRef + 15, // 81: ateapi.ListActorSnapshotsResponse.snapshots:type_name -> ateapi.ActorSnapshot + 16, // 82: ateapi.CreateActorSnapshotTagRequest.actor_snapshot_tag:type_name -> ateapi.ActorSnapshotTag + 16, // 83: ateapi.UpdateActorSnapshotTagRequest.tag:type_name -> ateapi.ActorSnapshotTag + 90, // 84: ateapi.UpdateActorSnapshotTagRequest.update_mask:type_name -> google.protobuf.FieldMask + 18, // 85: ateapi.DeleteActorSnapshotTagRequest.tag:type_name -> ateapi.ObjectRef + 74, // 86: ateapi.ListWorkersResponse.workers:type_name -> ateapi.Worker + 13, // 87: ateapi.ListActorsResponse.actors:type_name -> ateapi.Actor + 76, // 88: ateapi.Worker.assignment:type_name -> ateapi.Assignment + 88, // 89: ateapi.Worker.labels:type_name -> ateapi.Worker.LabelsEntry + 8, // 90: ateapi.Worker.state:type_name -> ateapi.Worker.State + 75, // 91: ateapi.Worker.capacity:type_name -> ateapi.WorkerCapacity + 77, // 92: ateapi.Assignment.actor_template:type_name -> ateapi.KubeNamespacedObjectRef + 18, // 93: ateapi.Assignment.actor:type_name -> ateapi.ObjectRef + 4, // 94: ateapi.MintCertRequest.purpose:type_name -> ateapi.ActorCertificatePurpose + 35, // 95: ateapi.SandboxAssets.AssetsEntry.value:type_name -> ateapi.ArchAssets + 36, // 96: ateapi.ArchAssets.FilesEntry.value:type_name -> ateapi.AssetFile + 53, // 97: ateapi.Control.GetActor:input_type -> ateapi.GetActorRequest + 54, // 98: ateapi.Control.CreateActor:input_type -> ateapi.CreateActorRequest + 55, // 99: ateapi.Control.UpdateActor:input_type -> ateapi.UpdateActorRequest + 56, // 100: ateapi.Control.SuspendActor:input_type -> ateapi.SuspendActorRequest + 58, // 101: ateapi.Control.PauseActor:input_type -> ateapi.PauseActorRequest + 60, // 102: ateapi.Control.ResumeActor:input_type -> ateapi.ResumeActorRequest + 62, // 103: ateapi.Control.DeleteActor:input_type -> ateapi.DeleteActorRequest + 63, // 104: ateapi.Control.GetActorSnapshot:input_type -> ateapi.GetActorSnapshotRequest + 64, // 105: ateapi.Control.GetActorSnapshotTag:input_type -> ateapi.GetActorSnapshotTagRequest + 65, // 106: ateapi.Control.ListActorSnapshots:input_type -> ateapi.ListActorSnapshotsRequest + 67, // 107: ateapi.Control.CreateActorSnapshotTag:input_type -> ateapi.CreateActorSnapshotTagRequest + 68, // 108: ateapi.Control.UpdateActorSnapshotTag:input_type -> ateapi.UpdateActorSnapshotTagRequest + 69, // 109: ateapi.Control.DeleteActorSnapshotTag:input_type -> ateapi.DeleteActorSnapshotTagRequest + 70, // 110: ateapi.Control.ListWorkers:input_type -> ateapi.ListWorkersRequest + 72, // 111: ateapi.Control.ListActors:input_type -> ateapi.ListActorsRequest + 37, // 112: ateapi.Control.CreateAtespace:input_type -> ateapi.CreateAtespaceRequest + 38, // 113: ateapi.Control.GetAtespace:input_type -> ateapi.GetAtespaceRequest + 39, // 114: ateapi.Control.ListAtespaces:input_type -> ateapi.ListAtespacesRequest + 41, // 115: ateapi.Control.DeleteAtespace:input_type -> ateapi.DeleteAtespaceRequest + 42, // 116: ateapi.Control.CreateActorTemplate:input_type -> ateapi.CreateActorTemplateRequest + 43, // 117: ateapi.Control.GetActorTemplate:input_type -> ateapi.GetActorTemplateRequest + 44, // 118: ateapi.Control.UpdateActorTemplate:input_type -> ateapi.UpdateActorTemplateRequest + 45, // 119: ateapi.Control.ListActorTemplates:input_type -> ateapi.ListActorTemplatesRequest + 47, // 120: ateapi.Control.DeleteActorTemplate:input_type -> ateapi.DeleteActorTemplateRequest + 48, // 121: ateapi.Control.CreateActorTemplateVersion:input_type -> ateapi.CreateActorTemplateVersionRequest + 49, // 122: ateapi.Control.GetActorTemplateVersion:input_type -> ateapi.GetActorTemplateVersionRequest + 50, // 123: ateapi.Control.ListActorTemplateVersions:input_type -> ateapi.ListActorTemplateVersionsRequest + 52, // 124: ateapi.Control.DeleteActorTemplateVersion:input_type -> ateapi.DeleteActorTemplateVersionRequest + 78, // 125: ateapi.Debug.DebugClear:input_type -> ateapi.DebugClearRequest + 80, // 126: ateapi.ActorIdentity.MintJWT:input_type -> ateapi.MintJWTRequest + 82, // 127: ateapi.ActorIdentity.MintCert:input_type -> ateapi.MintCertRequest + 13, // 128: ateapi.Control.GetActor:output_type -> ateapi.Actor + 13, // 129: ateapi.Control.CreateActor:output_type -> ateapi.Actor + 13, // 130: ateapi.Control.UpdateActor:output_type -> ateapi.Actor + 57, // 131: ateapi.Control.SuspendActor:output_type -> ateapi.SuspendActorResponse + 59, // 132: ateapi.Control.PauseActor:output_type -> ateapi.PauseActorResponse + 61, // 133: ateapi.Control.ResumeActor:output_type -> ateapi.ResumeActorResponse + 13, // 134: ateapi.Control.DeleteActor:output_type -> ateapi.Actor + 15, // 135: ateapi.Control.GetActorSnapshot:output_type -> ateapi.ActorSnapshot + 16, // 136: ateapi.Control.GetActorSnapshotTag:output_type -> ateapi.ActorSnapshotTag + 66, // 137: ateapi.Control.ListActorSnapshots:output_type -> ateapi.ListActorSnapshotsResponse + 16, // 138: ateapi.Control.CreateActorSnapshotTag:output_type -> ateapi.ActorSnapshotTag + 16, // 139: ateapi.Control.UpdateActorSnapshotTag:output_type -> ateapi.ActorSnapshotTag + 16, // 140: ateapi.Control.DeleteActorSnapshotTag:output_type -> ateapi.ActorSnapshotTag + 71, // 141: ateapi.Control.ListWorkers:output_type -> ateapi.ListWorkersResponse + 73, // 142: ateapi.Control.ListActors:output_type -> ateapi.ListActorsResponse + 17, // 143: ateapi.Control.CreateAtespace:output_type -> ateapi.Atespace + 17, // 144: ateapi.Control.GetAtespace:output_type -> ateapi.Atespace + 40, // 145: ateapi.Control.ListAtespaces:output_type -> ateapi.ListAtespacesResponse + 17, // 146: ateapi.Control.DeleteAtespace:output_type -> ateapi.Atespace + 20, // 147: ateapi.Control.CreateActorTemplate:output_type -> ateapi.ActorTemplate + 20, // 148: ateapi.Control.GetActorTemplate:output_type -> ateapi.ActorTemplate + 20, // 149: ateapi.Control.UpdateActorTemplate:output_type -> ateapi.ActorTemplate + 46, // 150: ateapi.Control.ListActorTemplates:output_type -> ateapi.ListActorTemplatesResponse + 20, // 151: ateapi.Control.DeleteActorTemplate:output_type -> ateapi.ActorTemplate + 21, // 152: ateapi.Control.CreateActorTemplateVersion:output_type -> ateapi.ActorTemplateVersion + 21, // 153: ateapi.Control.GetActorTemplateVersion:output_type -> ateapi.ActorTemplateVersion + 51, // 154: ateapi.Control.ListActorTemplateVersions:output_type -> ateapi.ListActorTemplateVersionsResponse + 21, // 155: ateapi.Control.DeleteActorTemplateVersion:output_type -> ateapi.ActorTemplateVersion + 79, // 156: ateapi.Debug.DebugClear:output_type -> ateapi.DebugClearResponse + 81, // 157: ateapi.ActorIdentity.MintJWT:output_type -> ateapi.MintJWTResponse + 83, // 158: ateapi.ActorIdentity.MintCert:output_type -> ateapi.MintCertResponse + 128, // [128:159] is the sub-list for method output_type + 97, // [97:128] is the sub-list for method input_type + 97, // [97:97] is the sub-list for extension type_name + 97, // [97:97] is the sub-list for extension extendee + 0, // [0:97] is the sub-list for field type_name } func init() { file_ateapi_proto_init() } diff --git a/pkg/proto/ateapipb/ateapi.proto b/pkg/proto/ateapipb/ateapi.proto index cff5ab44d..9a323c192 100644 --- a/pkg/proto/ateapipb/ateapi.proto +++ b/pkg/proto/ateapipb/ateapi.proto @@ -244,6 +244,9 @@ message Actor { // Actor version captured when the current durable snapshot began. int64 in_progress_snapshot_source_actor_version = 10; + // Time of the most recent transition into STATUS_RUNNING. + google.protobuf.Timestamp last_resume_time = 17; + // Volumes attached to the actor. These volumes only live as long as the actor. // They are deleted when the actor is deleted. repeated ExternalVolume actor_volumes = 11;