Repository navigation
Run golden snapshots in the template's atespace - #2543
Zoe Zhao (zoez7) merged 3 commits into
Conversation
872b864 to
100eadf
Compare
f2af7af to
a3ae7df
Compare
|
Zoe Zhao (@zoez7), could you review this PR? Real gVisor E2E verification is complete, and the results and upgrade notes are in the PR description. |
5764167 to
5e9cfd7
Compare
|
Sorry for the delay, will review today |
14fffe7 to
1abd2d4
Compare
| // warmup_actor_uid identifies the Actor whose RUNNING state starts the timer. | ||
| // +k8s:optional | ||
| // +k8s:format=k8s-uuid | ||
| string warmup_actor_uid = 4; |
There was a problem hiding this comment.
Let's keep the public API surface minimal and drop the revision-binding changes (warmup_actor_uid, warmup_actor_version, and the revision preconditions) from this PR. We don't need revision binding here.
There was a problem hiding this comment.
Removed UID/version binding and its added preconditions.
| // A different revision starts a fresh timer, even if the Actor is RUNNING again. | ||
| // +k8s:optional | ||
| // +k8s:minimum=1 | ||
| int64 warmup_actor_version = 5; |
There was a problem hiding this comment.
Same concern as above. This is controller-internal state, but once it is in GoldenSnapshotStatus it becomes public API that clients can read and start depending on. I'd prefer not to expose it.
There was a problem hiding this comment.
Removed the added fields from GoldenSnapshotStatus.
| // actors live in. | ||
| GoldenActorAtespace = "ate-golden" | ||
| // InternalResourceNamePrefix reserves actor and tag names for controllers. | ||
| InternalResourceNamePrefix = "ate-internal-" |
There was a problem hiding this comment.
To keep the API surface minimal, can we avoid reserving special names? A reserved prefix is a new rule every client has to learn, and it adds special cases to CreateActor, CreateTag, and authz.
There was a problem hiding this comment.
Removed the reserved prefix; Golden resources use the template UID.
|
|
||
| You need git, go and kubectl, and crane for prebuilt images. Agree on a time with actor owners: their running actors get `SIGTERM` in step 3. | ||
|
|
||
| ### Golden snapshots from older releases |
There was a problem hiding this comment.
🤖 should-fix 🟡 – The table at the top of this runbook says pre-1.0 minor upgrades are not supported and require a reinstall, and AGENTS.md rules out compatibility work before v1.0.0. This release-specific migration guide contradicts both and will go stale in a generic runbook. Please move it to the PR description or release notes, and drop the new Confirm before you begin heading with it.
There was a problem hiding this comment.
Moved the notes to the PR description and restored this file to main.
| UpdateTag(ctx context.Context, tagRef resources.TagRef, precondition store.Precondition, mutate func(toUpdate *ateapipb.Tag) error) (*ateapipb.Tag, error) | ||
| DeleteTag(ctx context.Context, tagRef resources.TagRef, precondition store.DeletePreconditions) (*ateapipb.Tag, error) | ||
| GetActorTemplate(ctx context.Context, templateRef resources.ActorTemplateRef) (*ateapipb.ActorTemplate, error) | ||
| ListActorTemplates(ctx context.Context, atespace string, opts store.ListOptions) (store.ListResponse[*ateapipb.ActorTemplate], error) |
There was a problem hiding this comment.
Once acquireTagTemplateLease is removed from DeleteTag, actorWorkflowStore no longer needs ListActorTemplates. Let's remove this from actorWorkflowStore to keep this PR minimal.
There was a problem hiding this comment.
Removed ListActorTemplates from actorWorkflowStore.
| // 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" | ||
|
|
||
| "github.com/agent-substrate/substrate/internal/resources" | ||
| ) | ||
|
|
||
| type reservedCreationKey struct{} | ||
| type heldTemplateLeaseKey struct{} | ||
|
|
||
| // withReservedResourceCreation marks ctx as permitted to create reserved | ||
| // names; only controller-internal callers set it. | ||
| func withReservedResourceCreation(ctx context.Context) context.Context { | ||
| return context.WithValue(ctx, reservedCreationKey{}, true) | ||
| } | ||
|
|
||
| func reservedResourceCreationAllowed(ctx context.Context) bool { | ||
| allowed, _ := ctx.Value(reservedCreationKey{}).(bool) | ||
| return allowed | ||
| } | ||
|
|
||
| // withTemplateLease records that the caller holds the named template lease for | ||
| // the lifetime of ctx. | ||
| func withTemplateLease(ctx context.Context, ref resources.ActorTemplateRef) context.Context { | ||
| return context.WithValue(ctx, heldTemplateLeaseKey{}, ref) | ||
| } | ||
|
|
||
| func holdsTemplateLease(ctx context.Context, ref resources.ActorTemplateRef) bool { | ||
| held, ok := ctx.Value(heldTemplateLeaseKey{}).(resources.ActorTemplateRef) | ||
| return ok && held == ref | ||
| } |
There was a problem hiding this comment.
Please remove this entire file (cmd/ateapi/internal/controlapi/internal_call.go); both context keys in it are unnecessary. Context-value flags hide which callers may skip checks: without reserved names, withReservedResourceCreation / reservedResourceCreationAllowed have nothing to unlock, and withTemplateLease / holdsTemplateLease only exist so DeleteTag can skip re-acquiring the template lease, which DeleteTag should not acquire in the first place.
There was a problem hiding this comment.
Deleted internal_call.go and both context flags.
| // acquireTagTemplateLease serializes deletion with publication by the source | ||
| // template. It protects references regardless of the tag's name. | ||
| func (w *ActorWorkflow) acquireTagTemplateLease(ctx context.Context, tag *ateapipb.Tag, deletingTemplate resources.ActorTemplateRef) (context.Context, *store.Lease, error) { | ||
| tagRef := resources.TagRefFromTag(tag) | ||
| var owner *ateapipb.ActorTemplate | ||
| pageToken := "" | ||
| for { | ||
| page, err := w.store.ListActorTemplates(ctx, tagRef.Atespace, store.ListOptions{PageSize: 100, PageToken: pageToken}) | ||
| if err != nil { | ||
| return ctx, nil, fmt.Errorf("while checking template references to tag %s: %w", tagRef, err) | ||
| } | ||
| for _, tmpl := range page.Items { | ||
| ref := resources.ActorTemplateRefFromActorTemplate(tmpl) | ||
| if golden := tmpl.GetStatus().GetGoldenSnapshotStatus().GetGoldenTag(); golden != nil && resources.TagRefFromObjectRef(golden) == tagRef && ref != deletingTemplate { | ||
| return ctx, nil, apierror.FailedPrecondition("Tag %s is referenced by ActorTemplate %s", tagRef, ref) | ||
| } | ||
| if tmpl.GetMetadata().GetUid() == tag.GetStatus().GetActorTemplateUid() { | ||
| owner = tmpl | ||
| } | ||
| } | ||
| if page.NextPageToken == "" { | ||
| break | ||
| } | ||
| pageToken = page.NextPageToken | ||
| } | ||
| if owner == nil || owner.GetSnapshotConfig().GetGoldenSnapshotConfig().GetMode() == ateapipb.GoldenSnapshotMode_GOLDEN_SNAPSHOT_MODE_DISABLED || goldenSnapshotDone(owner.GetStatus().GetGoldenSnapshotStatus()) { | ||
| return ctx, nil, nil | ||
| } | ||
| ref := resources.ActorTemplateRefFromActorTemplate(owner) | ||
| var lease *store.Lease | ||
| if !holdsTemplateLease(ctx, ref) { | ||
| var err error | ||
| ctx, lease, err = acquireLease(ctx, w.store, "lease:actortemplate:"+ref.Atespace+":"+ref.Name, "ActorTemplate "+ref.String()) | ||
| if err != nil { | ||
| return ctx, nil, err | ||
| } | ||
| } | ||
| current, err := w.store.GetActorTemplate(ctx, ref) | ||
| if errors.Is(err, store.ErrNotFound) { | ||
| return ctx, lease, nil | ||
| } | ||
| if err != nil { | ||
| if lease != nil { | ||
| lease.Close() | ||
| } | ||
| return ctx, nil, err | ||
| } | ||
| if golden := current.GetStatus().GetGoldenSnapshotStatus().GetGoldenTag(); golden != nil && resources.TagRefFromObjectRef(golden) == tagRef && ref != deletingTemplate { | ||
| if lease != nil { | ||
| lease.Close() | ||
| } | ||
| return ctx, nil, apierror.FailedPrecondition("Tag %s is referenced by ActorTemplate %s", tagRef, ref) | ||
| } | ||
| return ctx, lease, nil | ||
| } |
There was a problem hiding this comment.
Please revert all changes to cmd/ateapi/internal/controlapi/workflow_tag.go. We don't need the revision-binding precondition on TagActorSnapshot / loadActorForTag, and we don't need acquireTagTemplateLease in DeleteTag (golden tag deletion protection can be done in a separate follow-up PR).
There was a problem hiding this comment.
Restored workflow_tag.go to main; tag deletion protection is outside this PR.
1abd2d4 to
eb2be87
Compare
|
Zoe Zhao (@zoez7) I've simplified the PR as requested and completed validation, including gVisor E2E; please take another look. |
| declared[vol.GetName()] = vol | ||
| } | ||
| microVM := value.GetSandboxConfig().GetSandboxClass() == string(atev1alpha1.SandboxClassMicroVM) | ||
| hasSnapshotVolume := false |
There was a problem hiding this comment.
The changes in this file is not related to moving the golden actor's atespace, let's remove it for now.
There was a problem hiding this comment.
Removed the VOLUMES validation commit and rebased onto main.
| SourceActor: &ateapipb.ObjectRef{Atespace: testAtespace, Name: created.GetMetadata().GetUid()}, | ||
| Scope: ateapipb.TagScope_TAG_SCOPE_PUBLISHED, | ||
| Status: &ateapipb.TagStatus{ | ||
| Snapshot: newDurableSnapshot(1, ateapipb.SnapshotOwner_SNAPSHOT_OWNER_TAG, ateapipb.SnapshotFidelity_SNAPSHOT_FIDELITY_MEMORY, created.GetMetadata().GetUid(), goldenSnapshotName, goldenSnapshotURI(t)), |
There was a problem hiding this comment.
nit: createDataFidelityTemplate sets PreferredFidelity: SNAPSHOT_FIDELITY_VOLUMES on the template, and now that preferredFidelity() in workflow_suspend.go no longer forces golden snapshots to SNAPSHOT_FIDELITY_MEMORY, a VOLUMES template's golden tag has SNAPSHOT_FIDELITY_VOLUMES fidelity. Seed the golden tag here with SNAPSHOT_FIDELITY_VOLUMES so the fixture matches what the reconciler produces.
| Snapshot: newDurableSnapshot(1, ateapipb.SnapshotOwner_SNAPSHOT_OWNER_TAG, ateapipb.SnapshotFidelity_SNAPSHOT_FIDELITY_MEMORY, created.GetMetadata().GetUid(), goldenSnapshotName, goldenSnapshotURI(t)), | |
| Snapshot: newDurableSnapshot(1, ateapipb.SnapshotOwner_SNAPSHOT_OWNER_TAG, ateapipb.SnapshotFidelity_SNAPSHOT_FIDELITY_VOLUMES, created.GetMetadata().GetUid(), goldenSnapshotName, goldenSnapshotURI(t)), |
There was a problem hiding this comment.
Updated the Golden fixture to VOLUMES to match the template.
260e824 to
204b8fb
Compare
|
Zoe Zhao (@zoez7) Please re-review the updated PR with both comments addressed and full race, verify-all, and gVisor E2E passing. |
6576a2d to
88e5ae8
Compare
Place generated actors and published tags beside their templates, using template UIDs as resource names. Remove the system-atespace exceptions for lifecycle operations, snapshot fidelity, authorization, and metrics. Keep the reconciler's existing RPCs and state machine, and cover tenant storage ownership, snapshot inheritance, restore, and cleanup.
88e5ae8 to
703cf68
Compare
Closes #2533.
Move Golden actors and tags from the shared
ate-goldenatespace into their ActorTemplate's atespace, named after the template UID. Golden resources use the tenant's ordinary authorization, lifecycle policies, and storage grants. Remove the special handling for the dedicated system atespace.Golden capture follows the template's
preferredFidelity. MEMORY preserves process state. VOLUMES and ROOTFS restore persisted data and start new processes. Durable snapshot metrics useate.snapshot.kind=latest, including Golden snapshots.Tests cover tenant storage ownership, snapshot inheritance, restore, and cleanup. The VOLUMES Golden fixture matches the template's fidelity.
Validation
Validated
204b8fb3, rebased onto main7abc3172, on Linux with Go 1.27.2. The tested source matches the PR's 13,851 tracked files.go test -race -count=1 ./...: 166 packages passed, including PostgreSQL/envtest functional and authorization tests.tools/apitoolrace tests. Local validation and actor-attribute race tests also passed.hack/verify-all.sh, including code generation, lint, modules, licenses, metrics, migrations, and formatting. Regeneration left the source unchanged.TestGoldenSnapshotLifecycleandTestDurableDirLifecycle: Golden MEMORY/VOLUMES creation, tenant storage ownership, inheritance, resume, template cleanup, and durable-directory lifecycle. JUnit reports zero failures and two ROOTFS skips.All Go compilation and tests ran as a non-root user. The runtime copy uses a host-only Linux 5.14 OverlayFS fallback; source checks use the unmodified PR source. Micro-VM E2E and root-gated tests were not run.
Operator notes
ate-goldenresources may still own snapshots borrowed by actors.ate.snapshot.kind=golden. Thelatestseries now includes Golden and other durable snapshots.AI assistance: Codex helped implement and validate this change.