diff --git a/cmd/api/api/instances.go b/cmd/api/api/instances.go index e3131562..49350260 100644 --- a/cmd/api/api/instances.go +++ b/cmd/api/api/instances.go @@ -1121,6 +1121,22 @@ func instanceToOAPI(inst instances.Instance) oapi.Instance { oapiInst.Gpu = gpu } + // Expose phase accounting (cumulative time in each lifecycle phase). The + // snapshot rolls in time accrued in the current phase since the last + // transition, so consumers don't need a separate "live since" calculation. + if inst.Phases.Current != "" { + current := string(inst.Phases.Current) + oapiInst.CurrentPhase = ¤t + since := inst.Phases.Since + oapiInst.CurrentPhaseSince = &since + snapshot := inst.Phases.Snapshot(time.Now()) + out := make(map[string]int64, len(snapshot)) + for k, v := range snapshot { + out[string(k)] = v + } + oapiInst.PhaseDurationsMs = &out + } + return oapiInst } diff --git a/cmd/api/api/instances_test.go b/cmd/api/api/instances_test.go index 7a664db8..039aa917 100644 --- a/cmd/api/api/instances_test.go +++ b/cmd/api/api/instances_test.go @@ -11,6 +11,7 @@ import ( "github.com/kernel/hypeman/lib/autostandby" "github.com/kernel/hypeman/lib/hypervisor" "github.com/kernel/hypeman/lib/instances" + "github.com/kernel/hypeman/lib/instances/phasetracking" mw "github.com/kernel/hypeman/lib/middleware" "github.com/kernel/hypeman/lib/oapi" "github.com/kernel/hypeman/lib/paths" @@ -531,6 +532,64 @@ func TestCreateInstance_InvalidStandbyCompressionDelayInSnapshotPolicy(t *testin assert.Contains(t, badReq.Message, "standby_compression_delay") } +func TestInstanceToOAPI_EmitsPhaseAccounting(t *testing.T) { + t.Parallel() + + t0 := time.Now().Add(-10 * time.Minute) + tr := phasetracking.Tracker{} + tr.Record(phasetracking.PhaseRunning, t0) + tr.Record(phasetracking.PhaseStandby, t0.Add(60*time.Second)) + tr.Record(phasetracking.PhaseRunning, t0.Add(60*time.Second+5*time.Minute)) + + inst := instances.Instance{ + StoredMetadata: instances.StoredMetadata{ + Id: "inst-phases", + Name: "inst-phases", + Image: "docker.io/library/alpine:latest", + CreatedAt: t0, + HypervisorType: hypervisor.TypeCloudHypervisor, + Phases: tr, + }, + State: instances.StateRunning, + } + + oapiInst := instanceToOAPI(inst) + + require.NotNil(t, oapiInst.CurrentPhase) + assert.Equal(t, "running", *oapiInst.CurrentPhase) + require.NotNil(t, oapiInst.CurrentPhaseSince) + require.NotNil(t, oapiInst.PhaseDurationsMs) + + durations := *oapiInst.PhaseDurationsMs + // Standby stint was a completed 300s window — no live accrual since. + assert.Equal(t, int64(300_000), durations["standby"]) + // Running = 60s completed + live time since latest Record. The + // recorded-at instant is in the past, so this must be >= 60s. + assert.GreaterOrEqual(t, durations["running"], int64(60_000), + "running should include the completed 60s stint") +} + +func TestInstanceToOAPI_OmitsPhaseFieldsWhenUnset(t *testing.T) { + t.Parallel() + + inst := instances.Instance{ + StoredMetadata: instances.StoredMetadata{ + Id: "inst-no-phases", + Name: "inst-no-phases", + Image: "docker.io/library/alpine:latest", + CreatedAt: time.Now(), + HypervisorType: hypervisor.TypeCloudHypervisor, + }, + State: instances.StateStopped, + } + + oapiInst := instanceToOAPI(inst) + + assert.Nil(t, oapiInst.CurrentPhase) + assert.Nil(t, oapiInst.CurrentPhaseSince) + assert.Nil(t, oapiInst.PhaseDurationsMs) +} + func TestInstanceToOAPI_EmitsStandbyCompressionDelayInSnapshotPolicy(t *testing.T) { t.Parallel() diff --git a/lib/instances/create.go b/lib/instances/create.go index b0af9254..3e4a73e8 100644 --- a/lib/instances/create.go +++ b/lib/instances/create.go @@ -13,6 +13,7 @@ import ( "github.com/kernel/hypeman/lib/guestmemory" "github.com/kernel/hypeman/lib/hypervisor" "github.com/kernel/hypeman/lib/images" + "github.com/kernel/hypeman/lib/instances/phasetracking" "github.com/kernel/hypeman/lib/logger" "github.com/kernel/hypeman/lib/network" "github.com/kernel/hypeman/lib/system" @@ -496,6 +497,7 @@ func (m *manager) createInstance( } bootStart := time.Now().UTC() stored.StartedAt = &bootStart + stored.Phases.Record(phasetracking.PhaseCreated, bootStart) // 18. Save metadata log.DebugContext(ctx, "saving instance metadata", "instance_id", id) @@ -528,7 +530,11 @@ func (m *manager) createInstance( reservedResources = false } - // 20. Persist runtime metadata updates after VM boot. + // 20. Persist runtime metadata updates after VM boot. The VMM is up but + // guest boot markers have not yet been written, so we are in Initializing; + // persistBootMarkers will advance us to Running once the markers appear + // in the serial log. + stored.Phases.Record(phasetracking.PhaseInitializing, time.Now().UTC()) meta = &metadata{StoredMetadata: *stored} if err := m.saveMetadata(meta); err != nil { // VM is running but metadata failed - log but don't fail diff --git a/lib/instances/firecracker_test.go b/lib/instances/firecracker_test.go index 25973930..2ee440b1 100644 --- a/lib/instances/firecracker_test.go +++ b/lib/instances/firecracker_test.go @@ -17,6 +17,7 @@ import ( "github.com/kernel/hypeman/lib/devices" "github.com/kernel/hypeman/lib/hypervisor" "github.com/kernel/hypeman/lib/images" + "github.com/kernel/hypeman/lib/instances/phasetracking" "github.com/kernel/hypeman/lib/network" "github.com/kernel/hypeman/lib/paths" "github.com/kernel/hypeman/lib/resources" @@ -163,6 +164,7 @@ func TestFirecrackerStandbyAndRestore(t *testing.T) { inst, err = waitForInstanceState(ctx, mgr, inst.Id, StateRunning, integrationTestTimeout(20*time.Second)) require.NoError(t, err) require.NoError(t, waitForExecAgent(ctx, mgr, inst.Id, 30*time.Second)) + assert.Equal(t, phasetracking.PhaseRunning, inst.Phases.Current, "fresh instance should be in running phase") firstFilePath := "/tmp/firecracker-standby-first.txt" secondFilePath := "/tmp/firecracker-standby-second.txt" @@ -222,10 +224,14 @@ func TestFirecrackerStandbyAndRestore(t *testing.T) { t.Logf("first standby (full snapshot expected) took %v", firstStandbyDuration) assert.Equal(t, StateStandby, inst.State) assert.True(t, inst.HasSnapshot) + assert.Equal(t, phasetracking.PhaseStandby, inst.Phases.Current, "standby transition should set current phase") + assert.Greater(t, inst.Phases.Cumulative[phasetracking.PhaseRunning], int64(0), "first running stint should be accrued after standby") firstRestoreRunningDuration, _ := restoreAndMeasure("first") assert.False(t, inst.HasSnapshot, "running instances should not expose retained snapshot bases as standby snapshots") assertRetainedBaseState() + assert.Equal(t, phasetracking.PhaseRunning, inst.Phases.Current, "restored instance should be in running phase") + assert.Greater(t, inst.Phases.Cumulative[phasetracking.PhaseStandby], int64(0), "first standby stint should be accrued after restore") t.Logf("first full-cycle timings: standby=%v restore-to-running=%v", firstStandbyDuration, firstRestoreRunningDuration) assertGuestFileContents(firstFilePath, firstFileContents) @@ -245,6 +251,7 @@ func TestFirecrackerStandbyAndRestore(t *testing.T) { secondRestoreRunningDuration, _ := restoreAndMeasure("second") assert.False(t, inst.HasSnapshot, "running instances should not expose retained snapshot bases as standby snapshots") assertRetainedBaseState() + assert.Equal(t, phasetracking.PhaseRunning, inst.Phases.Current, "second restore should land back in running") t.Logf("second diff-cycle timings: standby=%v restore-to-running=%v", secondStandbyDuration, secondRestoreRunningDuration) assertGuestFileContents(secondFilePath, secondFileContents) diff --git a/lib/instances/fork.go b/lib/instances/fork.go index 4ce7ee6e..d3f837dc 100644 --- a/lib/instances/fork.go +++ b/lib/instances/fork.go @@ -13,6 +13,7 @@ import ( "github.com/kernel/hypeman/lib/forkvm" "github.com/kernel/hypeman/lib/guest" "github.com/kernel/hypeman/lib/hypervisor" + "github.com/kernel/hypeman/lib/instances/phasetracking" "github.com/kernel/hypeman/lib/logger" "github.com/kernel/hypeman/lib/network" "github.com/nrednav/cuid2" @@ -267,7 +268,7 @@ func (m *manager) forkInstanceFromStoppedOrStandby(ctx context.Context, id strin return nil, fmt.Errorf("get vm starter: %w", err) } - now := time.Now() + now := time.Now().UTC() forkMeta := cloneStoredMetadataWithoutPendingStandbyCompression(meta.StoredMetadata) forkMeta.Id = forkID forkMeta.Name = req.Name @@ -280,6 +281,17 @@ func (m *manager) forkInstanceFromStoppedOrStandby(ctx context.Context, id strin forkMeta.VsockSocket = m.paths.InstanceSocket(forkID, hypervisor.VsockSocketNameForType(forkMeta.HypervisorType)) forkMeta.ExitCode = nil forkMeta.ExitMessage = "" + // Forks are new instances; phase accounting must not inherit the source's + // cumulative durations. The first transition into the fork's runtime + // phase (Standby for snapshot forks, Stopped for stopped forks) will be + // recorded by the appropriate operation when the fork is acted on. + forkMeta.Phases.Reset() + switch source.State { + case StateStandby: + forkMeta.Phases.Record(phasetracking.PhaseStandby, now) + case StateStopped: + forkMeta.Phases.Record(phasetracking.PhaseStopped, now) + } // Keep the original CID for snapshot-based forks. // Rewriting CID in restored memory snapshots is not reliable across @@ -514,6 +526,7 @@ func cloneStoredMetadata(src StoredMetadata) StoredMetadata { exitCode := *src.ExitCode dst.ExitCode = &exitCode } + dst.Phases = src.Phases.Clone() return dst } diff --git a/lib/instances/fork_test.go b/lib/instances/fork_test.go index f73e892b..0dd769e3 100644 --- a/lib/instances/fork_test.go +++ b/lib/instances/fork_test.go @@ -18,6 +18,7 @@ import ( "github.com/kernel/hypeman/lib/guest" "github.com/kernel/hypeman/lib/hypervisor" "github.com/kernel/hypeman/lib/images" + "github.com/kernel/hypeman/lib/instances/phasetracking" "github.com/kernel/hypeman/lib/paths" snapshotstore "github.com/kernel/hypeman/lib/snapshot" "github.com/stretchr/testify/assert" @@ -548,6 +549,14 @@ func TestForkCloudHypervisorFromRunningNetwork(t *testing.T) { assert.NotEqual(t, sourceAfterFork.MAC, forked.MAC) assertGuestHasOnlyExpectedIPv4(t, forked, forked.IP, 30*time.Second) assertHostCanReachNginx(t, forked.IP, 80, 60*time.Second) + + // Fork must start with a fresh phase ledger and not inherit the source's + // accumulated running time. The source has completed at least one running + // stint by now (running -> internal-standby -> running); the fork is still + // in its first running stint and so has no completed running ms logged. + assert.Equal(t, phasetracking.PhaseRunning, forked.Phases.Current) + assert.Zero(t, forked.Phases.Cumulative[phasetracking.PhaseRunning], "fork should not inherit source's running ledger") + assert.Greater(t, sourceAfterFork.Phases.Cumulative[phasetracking.PhaseRunning], int64(0), "source's pre-fork running stint should be cumulated") } func assertHostCanReachNginx(t *testing.T, ip string, port int, timeout time.Duration) { diff --git a/lib/instances/phasetracking/phasetracking.go b/lib/instances/phasetracking/phasetracking.go new file mode 100644 index 00000000..53f007d9 --- /dev/null +++ b/lib/instances/phasetracking/phasetracking.go @@ -0,0 +1,114 @@ +// Package phasetracking accumulates cumulative time-in-phase for instance +// lifecycle phases. The tracker is embedded in instance metadata and updated +// at every externally-observable state transition so consumers can use the +// resulting durations for billing, observability, and analytics. +// +// Phases mirror the externally-observable values of instances.State (lowercased +// so they remain stable in the API surface even if the internal enum is +// renamed). The Initializing→Running transition is detected lazily when guest +// boot markers are persisted, so the tracker reflects the same view of guest +// readiness that the public State machine reports — not the bare moment the +// VMM process came up. +// +// Transient internal substates that no external observer can see — for example +// the Paused/Shutdown steps inside a single Standby or Stop orchestration — are +// intentionally not recorded; they would be sub-millisecond blips inside a +// non-yielding function call that adds noise without truth. +// +// Only the transition orchestration sites in lib/instances should call Record. +// The tracker intentionally does not subscribe to the lifecycle event bus — +// that bus is best-effort and lossy, which is unsuitable for billing. +package phasetracking + +import "time" + +// Phase is the canonical lifecycle phase name. Values mirror instances.State +// lowercased so they remain stable in the API surface even if the internal +// State enum is renamed. +type Phase string + +const ( + PhaseStopped Phase = "stopped" + PhaseCreated Phase = "created" + PhaseInitializing Phase = "initializing" + PhaseRunning Phase = "running" + PhaseStandby Phase = "standby" +) + +// Tracker accumulates cumulative wall-clock time spent in each phase. +// +// Invariants: +// - Cumulative[phase] is the total ms spent in `phase` across all prior +// completed visits to that phase. +// - Time spent in the *current* phase (since `Since`) is NOT yet rolled into +// Cumulative — callers that want "live" totals should use Snapshot. +// - Current and Since must be updated atomically with Cumulative; that's +// the contract of Record. Direct mutation is not supported. +// +// The zero value is valid: it represents an instance that has not entered +// any phase yet. The first Record call sets Current and Since without +// accruing time (there is no prior phase to accrue from). +type Tracker struct { + Current Phase `json:"current,omitempty"` + Since time.Time `json:"since,omitempty"` + Cumulative map[Phase]int64 `json:"cumulative,omitempty"` +} + +// Record transitions into newPhase as of `now`, first accruing time-in-current +// into Cumulative. Safe to call on a zero-value Tracker (first transition has +// no prior phase, so no accrual happens). +// +// `now` is a parameter rather than time.Now() so tests can pin time and so +// callers can use the same `now` value they're persisting elsewhere on the +// metadata (e.g. StartedAt) without drift. +func (t *Tracker) Record(newPhase Phase, now time.Time) { + if t.Cumulative == nil { + t.Cumulative = make(map[Phase]int64) + } + if t.Current != "" && !t.Since.IsZero() { + elapsed := now.Sub(t.Since).Milliseconds() + if elapsed > 0 { + t.Cumulative[t.Current] += elapsed + } + } + t.Current = newPhase + t.Since = now +} + +// Snapshot returns a copy of Cumulative with the in-flight time-in-current +// rolled in, without mutating the tracker. Use this when reporting "running +// time so far" — typically in the API response path. +func (t Tracker) Snapshot(now time.Time) map[Phase]int64 { + out := make(map[Phase]int64, len(t.Cumulative)+1) + for k, v := range t.Cumulative { + out[k] = v + } + if t.Current != "" && !t.Since.IsZero() { + elapsed := now.Sub(t.Since).Milliseconds() + if elapsed > 0 { + out[t.Current] += elapsed + } + } + return out +} + +// Reset clears all accumulated state. Used when forking — the fork is a new +// instance and must not inherit the source's phase history. +func (t *Tracker) Reset() { + t.Current = "" + t.Since = time.Time{} + t.Cumulative = nil +} + +// Clone returns a deep copy of the tracker. The returned tracker shares no +// state with the receiver, so independent Record/Reset calls do not interfere. +func (t Tracker) Clone() Tracker { + dst := t + if t.Cumulative != nil { + dst.Cumulative = make(map[Phase]int64, len(t.Cumulative)) + for k, v := range t.Cumulative { + dst.Cumulative[k] = v + } + } + return dst +} diff --git a/lib/instances/phasetracking/phasetracking_test.go b/lib/instances/phasetracking/phasetracking_test.go new file mode 100644 index 00000000..84c7ed62 --- /dev/null +++ b/lib/instances/phasetracking/phasetracking_test.go @@ -0,0 +1,223 @@ +package phasetracking + +import ( + "encoding/json" + "testing" + "time" +) + +func TestRecord_FirstTransitionAccruesNothing(t *testing.T) { + var tr Tracker + now := time.Date(2026, 5, 11, 0, 0, 0, 0, time.UTC) + + tr.Record(PhaseRunning, now) + + if tr.Current != PhaseRunning { + t.Errorf("Current = %q, want %q", tr.Current, PhaseRunning) + } + if !tr.Since.Equal(now) { + t.Errorf("Since = %v, want %v", tr.Since, now) + } + if got := tr.Cumulative[PhaseRunning]; got != 0 { + t.Errorf("Cumulative[running] = %d, want 0 on first transition", got) + } +} + +func TestRecord_AccruesPriorPhaseOnTransition(t *testing.T) { + var tr Tracker + t0 := time.Date(2026, 5, 11, 0, 0, 0, 0, time.UTC) + + tr.Record(PhaseRunning, t0) + tr.Record(PhaseStandby, t0.Add(30*time.Second)) + tr.Record(PhaseRunning, t0.Add(30*time.Second+5*time.Minute)) + tr.Record(PhaseStopped, t0.Add(30*time.Second+5*time.Minute+10*time.Second)) + + if got, want := tr.Cumulative[PhaseRunning], int64(40_000); got != want { + t.Errorf("Cumulative[running] = %d, want %d", got, want) + } + if got, want := tr.Cumulative[PhaseStandby], int64(300_000); got != want { + t.Errorf("Cumulative[standby] = %d, want %d", got, want) + } + if tr.Current != PhaseStopped { + t.Errorf("Current = %q, want %q", tr.Current, PhaseStopped) + } +} + +func TestRecord_ZeroOrNegativeElapsedIsNoOp(t *testing.T) { + var tr Tracker + t0 := time.Date(2026, 5, 11, 0, 0, 0, 0, time.UTC) + + tr.Record(PhaseRunning, t0) + // Same instant — no time elapsed. + tr.Record(PhaseStandby, t0) + // Backward clock — also no accrual. + tr.Record(PhaseRunning, t0.Add(-5*time.Second)) + + if got := tr.Cumulative[PhaseRunning]; got != 0 { + t.Errorf("Cumulative[running] = %d, want 0 (zero/negative elapsed)", got) + } + if got := tr.Cumulative[PhaseStandby]; got != 0 { + t.Errorf("Cumulative[standby] = %d, want 0 (zero/negative elapsed)", got) + } +} + +func TestSnapshot_IncludesLiveTime(t *testing.T) { + var tr Tracker + t0 := time.Date(2026, 5, 11, 0, 0, 0, 0, time.UTC) + + tr.Record(PhaseRunning, t0) + tr.Record(PhaseStandby, t0.Add(60*time.Second)) + + // Now we're 10s into standby. Cumulative shouldn't yet include this. + live := tr.Snapshot(t0.Add(70 * time.Second)) + + if got, want := tr.Cumulative[PhaseRunning], int64(60_000); got != want { + t.Errorf("Cumulative[running] = %d, want %d", got, want) + } + if _, present := tr.Cumulative[PhaseStandby]; present { + t.Errorf("Cumulative should not yet contain standby") + } + if got, want := live[PhaseRunning], int64(60_000); got != want { + t.Errorf("Snapshot[running] = %d, want %d", got, want) + } + if got, want := live[PhaseStandby], int64(10_000); got != want { + t.Errorf("Snapshot[standby] = %d, want %d", got, want) + } +} + +func TestSnapshot_DoesNotMutate(t *testing.T) { + var tr Tracker + t0 := time.Date(2026, 5, 11, 0, 0, 0, 0, time.UTC) + tr.Record(PhaseRunning, t0) + + _ = tr.Snapshot(t0.Add(5 * time.Minute)) + + if got := tr.Cumulative[PhaseRunning]; got != 0 { + t.Errorf("Cumulative[running] mutated by Snapshot: got %d, want 0", got) + } +} + +func TestReset_ClearsAll(t *testing.T) { + var tr Tracker + t0 := time.Date(2026, 5, 11, 0, 0, 0, 0, time.UTC) + tr.Record(PhaseRunning, t0) + tr.Record(PhaseStandby, t0.Add(time.Minute)) + + tr.Reset() + + if tr.Current != "" || !tr.Since.IsZero() || tr.Cumulative != nil { + t.Errorf("Reset did not clear all fields: %+v", tr) + } +} + +func TestJSONRoundTrip_PreservesTracker(t *testing.T) { + t0 := time.Date(2026, 5, 11, 0, 0, 0, 0, time.UTC) + tr := Tracker{ + Current: PhaseStandby, + Since: t0, + Cumulative: map[Phase]int64{ + PhaseRunning: 12_345, + PhaseStandby: 67_890, + }, + } + + data, err := json.Marshal(tr) + if err != nil { + t.Fatalf("marshal: %v", err) + } + var got Tracker + if err := json.Unmarshal(data, &got); err != nil { + t.Fatalf("unmarshal: %v", err) + } + if got.Current != tr.Current || !got.Since.Equal(tr.Since) { + t.Errorf("roundtrip current/since mismatch: %+v", got) + } + if got.Cumulative[PhaseRunning] != 12_345 || got.Cumulative[PhaseStandby] != 67_890 { + t.Errorf("roundtrip cumulative mismatch: %+v", got.Cumulative) + } +} + +func TestJSONRoundTrip_ZeroValueOmitted(t *testing.T) { + // A fresh metadata file written before this feature shipped will not + // contain the `phases` field. Unmarshalling must succeed and produce a + // zero-value Tracker so the first Record call works as a fresh start. + var tr Tracker + if err := json.Unmarshal([]byte(`{}`), &tr); err != nil { + t.Fatalf("unmarshal empty: %v", err) + } + tr.Record(PhaseRunning, time.Now()) + if tr.Current != PhaseRunning { + t.Errorf("Current after fresh Record = %q, want running", tr.Current) + } +} + +// Regression: cloneStoredMetadata used to shallow-copy the embedded Tracker, +// which aliased the Cumulative map between source and destination. A +// subsequent Record on either side mutated both. Clone must produce a +// fully independent tracker. +func TestClone_IsDeepCopy(t *testing.T) { + t0 := time.Date(2026, 5, 11, 0, 0, 0, 0, time.UTC) + src := Tracker{ + Current: PhaseRunning, + Since: t0, + Cumulative: map[Phase]int64{ + PhaseRunning: 1_000, + PhaseStandby: 2_000, + }, + } + + dst := src.Clone() + + // Mutating the clone must not affect the source. + dst.Record(PhaseStandby, t0.Add(5*time.Second)) + if got, want := src.Cumulative[PhaseRunning], int64(1_000); got != want { + t.Errorf("source running mutated by clone.Record: got %d, want %d", got, want) + } + if src.Current != PhaseRunning { + t.Errorf("source Current mutated by clone.Record: got %q, want %q", src.Current, PhaseRunning) + } + + // And vice-versa: mutating the source must not affect the clone. + src.Record(PhaseStopped, t0.Add(10*time.Second)) + if dst.Current != PhaseStandby { + t.Errorf("clone Current mutated by src.Record: got %q, want %q", dst.Current, PhaseStandby) + } + if got := dst.Cumulative[PhaseStopped]; got != 0 { + t.Errorf("clone cumulative leaked from src.Record: got %d, want 0", got) + } +} + +func TestClone_ZeroValueSafe(t *testing.T) { + var src Tracker + dst := src.Clone() + if dst.Current != "" || !dst.Since.IsZero() || dst.Cumulative != nil { + t.Errorf("clone of zero value is not zero: %+v", dst) + } + // Recording on the clone must not panic and must not touch source. + dst.Record(PhaseRunning, time.Now()) + if src.Current != "" || src.Cumulative != nil { + t.Errorf("zero source mutated by clone.Record: %+v", src) + } +} + +// Regression: a session that spends 60s running then 300s in standby then +// 30s running again must report 90s running and 300s standby for billing. +func TestRecord_BillingScenario(t *testing.T) { + var tr Tracker + t0 := time.Date(2026, 5, 11, 0, 0, 0, 0, time.UTC) + + tr.Record(PhaseRunning, t0) + tr.Record(PhaseStandby, t0.Add(60*time.Second)) + tr.Record(PhaseRunning, t0.Add(60*time.Second+300*time.Second)) + tr.Record(PhaseStopped, t0.Add(60*time.Second+300*time.Second+30*time.Second)) + + billableRunning := tr.Cumulative[PhaseRunning] + standby := tr.Cumulative[PhaseStandby] + + if billableRunning != 90_000 { + t.Errorf("billable running ms = %d, want 90000", billableRunning) + } + if standby != 300_000 { + t.Errorf("standby ms = %d, want 300000", standby) + } +} diff --git a/lib/instances/qemu_test.go b/lib/instances/qemu_test.go index 1695b3b8..015f8078 100644 --- a/lib/instances/qemu_test.go +++ b/lib/instances/qemu_test.go @@ -22,6 +22,7 @@ import ( "github.com/kernel/hypeman/lib/hypervisor/qemu" "github.com/kernel/hypeman/lib/images" "github.com/kernel/hypeman/lib/ingress" + "github.com/kernel/hypeman/lib/instances/phasetracking" "github.com/kernel/hypeman/lib/network" "github.com/kernel/hypeman/lib/paths" "github.com/kernel/hypeman/lib/resources" @@ -838,6 +839,7 @@ func TestQEMUStandbyAndRestore(t *testing.T) { // Wait for VM to be fully running before standby err = waitForQEMUReady(ctx, inst.SocketPath, 10*time.Second) require.NoError(t, err, "QEMU VM should reach running state") + assert.Equal(t, phasetracking.PhaseRunning, inst.Phases.Current, "fresh instance should be in running phase") // Standby instance t.Log("Standing by instance...") @@ -845,6 +847,8 @@ func TestQEMUStandbyAndRestore(t *testing.T) { require.NoError(t, err) assert.Equal(t, StateStandby, inst.State) assert.True(t, inst.HasSnapshot) + assert.Equal(t, phasetracking.PhaseStandby, inst.Phases.Current, "standby transition should set current phase") + assert.Greater(t, inst.Phases.Cumulative[phasetracking.PhaseRunning], int64(0), "running stint should be accrued after standby") t.Log("Instance in standby") // Verify snapshot exists @@ -873,6 +877,8 @@ func TestQEMUStandbyAndRestore(t *testing.T) { // Wait for VM to be running again err = waitForQEMUReady(ctx, inst.SocketPath, 10*time.Second) require.NoError(t, err, "QEMU VM should reach running state after restore") + assert.Equal(t, phasetracking.PhaseRunning, inst.Phases.Current, "restored instance should be in running phase") + assert.Greater(t, inst.Phases.Cumulative[phasetracking.PhaseStandby], int64(0), "standby stint should be accrued after restore") // Cleanup t.Log("Cleaning up...") diff --git a/lib/instances/query.go b/lib/instances/query.go index e48eb2fc..2be21939 100644 --- a/lib/instances/query.go +++ b/lib/instances/query.go @@ -15,6 +15,7 @@ import ( "time" "github.com/kernel/hypeman/lib/hypervisor" + "github.com/kernel/hypeman/lib/instances/phasetracking" "github.com/kernel/hypeman/lib/logger" "go.opentelemetry.io/otel/attribute" ) @@ -219,6 +220,59 @@ func deriveRunningState(stored *StoredMetadata) State { return StateRunning } +// runningPhaseFromMarkers returns PhaseRunning with the wall-clock timestamp at +// which the guest crossed the Initializing→Running boundary, derived from the +// boot markers. If the markers do not yet indicate a Running guest, it returns +// PhaseInitializing and the zero time. The returned transition time is the +// later of ProgramStartedAt / GuestAgentReadyAt (or ProgramStartedAt alone +// when the guest agent is skipped), which is the moment the guest actually +// became Running per deriveRunningState's rule. +func runningPhaseFromMarkers(stored *StoredMetadata) (phasetracking.Phase, time.Time) { + if stored.ProgramStartedAt == nil { + return phasetracking.PhaseInitializing, time.Time{} + } + if stored.SkipGuestAgent { + return phasetracking.PhaseRunning, *stored.ProgramStartedAt + } + if stored.GuestAgentReadyAt == nil { + return phasetracking.PhaseInitializing, time.Time{} + } + transition := *stored.ProgramStartedAt + if stored.GuestAgentReadyAt.After(transition) { + transition = *stored.GuestAgentReadyAt + } + return phasetracking.PhaseRunning, transition +} + +// advancePhaseIfRunning promotes stored.Phases from Initializing to Running +// when the boot markers indicate the guest has crossed that boundary. The +// transition timestamp is the marker time, not now, so the Initializing +// duration reflects actual guest boot time rather than the wall clock when +// hydration happened to observe the markers. +// +// Called from both the in-memory hydrate path (so the Instance returned to +// callers reflects the new phase immediately) and the persist path (so the +// updated phase is written to disk). Idempotent. +func advancePhaseIfRunning(stored *StoredMetadata) { + if stored.Phases.Current != phasetracking.PhaseInitializing { + return + } + phase, transitionAt := runningPhaseFromMarkers(stored) + if phase != phasetracking.PhaseRunning { + return + } + // Clamp transitionAt forward to Phases.Since. After a restore-from- + // early-standby the markers we just parsed can carry timestamps from + // the pre-standby boot session, which predate Phases.Since (set at + // restore time). Letting Since move backwards would over-count Running + // on the next transition by the entire standby interval — billing- + // critical, since the field feeds duration accounting. + if transitionAt.Before(stored.Phases.Since) { + transitionAt = stored.Phases.Since + } + stored.Phases.Record(phasetracking.PhaseRunning, transitionAt) +} + // hydrateBootMarkersFromLogs fills missing boot markers from serial logs. // Returns true when at least one missing marker was found and populated. func (m *manager) hydrateBootMarkersFromLogs(ctx context.Context, stored *StoredMetadata) bool { @@ -248,6 +302,7 @@ func (m *manager) hydrateBootMarkersFromLogs(ctx context.Context, stored *Stored hydrated = true } if hydrated { + advancePhaseIfRunning(stored) m.clearBootMarkerRescan(stored.Id) } else { m.deferBootMarkerRescan(stored.Id) @@ -601,6 +656,8 @@ func (m *manager) persistBootMarkers(ctx context.Context, id string) { return } + advancePhaseIfRunning(&meta.StoredMetadata) + if err := m.saveMetadata(meta); err != nil { log.WarnContext(ctx, "failed to persist boot markers", "instance_id", id, "error", err) } else { diff --git a/lib/instances/query_test.go b/lib/instances/query_test.go index 4a18708d..6bc8745b 100644 --- a/lib/instances/query_test.go +++ b/lib/instances/query_test.go @@ -7,6 +7,7 @@ import ( "time" "github.com/kernel/hypeman/lib/hypervisor" + "github.com/kernel/hypeman/lib/instances/phasetracking" "github.com/kernel/hypeman/lib/paths" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" @@ -170,6 +171,195 @@ func TestDeriveRunningState(t *testing.T) { } } +func TestRunningPhaseFromMarkers(t *testing.T) { + t.Parallel() + + program := time.Date(2026, 5, 11, 12, 0, 0, 0, time.UTC) + agent := program.Add(2 * time.Second) + agentBeforeProgram := program.Add(-1 * time.Second) + + tests := []struct { + name string + stored StoredMetadata + wantPhase phasetracking.Phase + wantTransAt time.Time + wantHasTrans bool + }{ + { + name: "no markers → initializing", + stored: StoredMetadata{}, + wantPhase: phasetracking.PhaseInitializing, + }, + { + name: "skip-agent + program only → running at program time", + stored: StoredMetadata{ + ProgramStartedAt: &program, + SkipGuestAgent: true, + }, + wantPhase: phasetracking.PhaseRunning, + wantTransAt: program, + wantHasTrans: true, + }, + { + name: "program without agent → still initializing", + stored: StoredMetadata{ + ProgramStartedAt: &program, + }, + wantPhase: phasetracking.PhaseInitializing, + }, + { + name: "both markers, agent later → running at agent time", + stored: StoredMetadata{ + ProgramStartedAt: &program, + GuestAgentReadyAt: &agent, + }, + wantPhase: phasetracking.PhaseRunning, + wantTransAt: agent, + wantHasTrans: true, + }, + { + name: "both markers, program later → running at program time", + stored: StoredMetadata{ + ProgramStartedAt: &program, + GuestAgentReadyAt: &agentBeforeProgram, + }, + wantPhase: phasetracking.PhaseRunning, + wantTransAt: program, + wantHasTrans: true, + }, + } + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + gotPhase, gotAt := runningPhaseFromMarkers(&tt.stored) + assert.Equal(t, tt.wantPhase, gotPhase) + if tt.wantHasTrans { + assert.True(t, gotAt.Equal(tt.wantTransAt), "transition time = %v, want %v", gotAt, tt.wantTransAt) + } else { + assert.True(t, gotAt.IsZero(), "expected zero transition time, got %v", gotAt) + } + }) + } +} + +func TestAdvancePhaseIfRunning(t *testing.T) { + t.Parallel() + + bootStart := time.Date(2026, 5, 11, 12, 0, 0, 0, time.UTC) + program := bootStart.Add(500 * time.Millisecond) + agent := bootStart.Add(3 * time.Second) + + t.Run("initializing with no markers stays initializing", func(t *testing.T) { + stored := StoredMetadata{} + stored.Phases.Record(phasetracking.PhaseInitializing, bootStart) + + advancePhaseIfRunning(&stored) + assert.Equal(t, phasetracking.PhaseInitializing, stored.Phases.Current) + }) + + t.Run("initializing with complete markers advances using marker time", func(t *testing.T) { + stored := StoredMetadata{ + ProgramStartedAt: &program, + GuestAgentReadyAt: &agent, + } + stored.Phases.Record(phasetracking.PhaseInitializing, bootStart) + + advancePhaseIfRunning(&stored) + + assert.Equal(t, phasetracking.PhaseRunning, stored.Phases.Current) + assert.True(t, stored.Phases.Since.Equal(agent), "Since should be marker time, got %v", stored.Phases.Since) + // Initializing duration should reflect bootStart→agent, not now. + wantMs := agent.Sub(bootStart).Milliseconds() + assert.Equal(t, wantMs, stored.Phases.Cumulative[phasetracking.PhaseInitializing]) + }) + + t.Run("idempotent: already running stays running", func(t *testing.T) { + stored := StoredMetadata{ + ProgramStartedAt: &program, + GuestAgentReadyAt: &agent, + } + stored.Phases.Record(phasetracking.PhaseRunning, agent) + runningSince := stored.Phases.Since + + advancePhaseIfRunning(&stored) + + assert.Equal(t, phasetracking.PhaseRunning, stored.Phases.Current) + assert.True(t, stored.Phases.Since.Equal(runningSince), "Since should not move on idempotent call") + }) + + t.Run("non-initializing current phase is left alone", func(t *testing.T) { + // A Standby instance whose markers still indicate Running must not be + // flipped back to Running — phase transitions are driven by lifecycle + // orchestration, not by stale markers. + stored := StoredMetadata{ + ProgramStartedAt: &program, + GuestAgentReadyAt: &agent, + } + stored.Phases.Record(phasetracking.PhaseStandby, bootStart.Add(10*time.Second)) + + advancePhaseIfRunning(&stored) + assert.Equal(t, phasetracking.PhaseStandby, stored.Phases.Current) + }) + + t.Run("marker time before Since is clamped forward", func(t *testing.T) { + // Restore-from-early-standby: the instance was standbyed mid-boot + // before markers ever hydrated. Phases.Since is set at restore time. + // When the markers eventually parse, they may carry pre-standby + // timestamps (older than restore). Letting Since walk backwards would + // over-count Running on the next transition by the full standby + // interval — billing-critical. + restoreTime := bootStart.Add(1 * time.Hour) + stored := StoredMetadata{ + ProgramStartedAt: &program, // 500ms after bootStart — predates restore + GuestAgentReadyAt: &agent, // 3s after bootStart — also predates restore + } + stored.Phases.Record(phasetracking.PhaseInitializing, restoreTime) + + advancePhaseIfRunning(&stored) + + assert.Equal(t, phasetracking.PhaseRunning, stored.Phases.Current) + assert.True(t, stored.Phases.Since.Equal(restoreTime), + "Since must not move backwards; got %v, want %v", stored.Phases.Since, restoreTime) + // No Initializing duration is credited — elapsed at the clamp is zero. + assert.Zero(t, stored.Phases.Cumulative[phasetracking.PhaseInitializing]) + }) +} + +func TestHydrateBootMarkersFromLogs_AdvancesPhaseOnRunningTransition(t *testing.T) { + t.Parallel() + + tmpDir := t.TempDir() + m := &manager{ + paths: paths.New(tmpDir), + } + + bootStart := time.Date(2026, 5, 11, 12, 0, 0, 0, time.UTC) + m.now = func() time.Time { return bootStart.Add(5 * time.Second) } + + meta := &StoredMetadata{ + Id: "phase-advance-test", + SkipGuestAgent: false, + StartedAt: &bootStart, + } + meta.Phases.Record(phasetracking.PhaseInitializing, bootStart) + + logPath := m.paths.InstanceAppLog(meta.Id) + require.NoError(t, os.MkdirAll(filepath.Dir(logPath), 0o755)) + require.NoError(t, os.WriteFile(logPath, []byte( + "HYPEMAN-AGENT-READY ts=2026-05-11T12:00:02Z\n"+ + "HYPEMAN-PROGRAM-START ts=2026-05-11T12:00:01Z mode=exec\n", + ), 0o644)) + + hydrated := m.hydrateBootMarkersFromLogs(t.Context(), meta) + require.True(t, hydrated) + require.NotNil(t, meta.ProgramStartedAt) + require.NotNil(t, meta.GuestAgentReadyAt) + + assert.Equal(t, phasetracking.PhaseRunning, meta.Phases.Current, "phase should advance to running") + // Initializing duration = bootStart → max(program, agent) = 2s. + assert.Equal(t, int64(2_000), meta.Phases.Cumulative[phasetracking.PhaseInitializing]) +} + func TestHydrateBootMarkersFromLogs_RescanThrottle(t *testing.T) { t.Parallel() diff --git a/lib/instances/restore.go b/lib/instances/restore.go index 268f769b..d848ee48 100644 --- a/lib/instances/restore.go +++ b/lib/instances/restore.go @@ -306,7 +306,12 @@ func (m *manager) restoreInstance( // 9. Persist runtime metadata updates without resetting StartedAt. // Restore resumes an existing boot; preserving StartedAt keeps marker - // hydration scoped to the original boot timeline. + // hydration scoped to the original boot timeline. The boot markers from + // the prior boot are preserved across standby, so in the common case the + // guest is back in Running immediately; if the instance was standbyed + // before markers ever hydrated we resume in Initializing. + resumePhase, _ := runningPhaseFromMarkers(stored) + stored.Phases.Record(resumePhase, time.Now().UTC()) meta = &metadata{StoredMetadata: *stored} if err := m.saveMetadata(meta); err != nil { // VM is running but metadata failed diff --git a/lib/instances/standby.go b/lib/instances/standby.go index d9de8053..03b27410 100644 --- a/lib/instances/standby.go +++ b/lib/instances/standby.go @@ -11,6 +11,7 @@ import ( "github.com/kernel/hypeman/lib/guest" "github.com/kernel/hypeman/lib/hypervisor" + "github.com/kernel/hypeman/lib/instances/phasetracking" "github.com/kernel/hypeman/lib/logger" "github.com/kernel/hypeman/lib/network" snapshotstore "github.com/kernel/hypeman/lib/snapshot" @@ -211,7 +212,7 @@ func (m *manager) standbyInstance( } // 10. Update timestamp and clear PID (hypervisor no longer running) - now := time.Now() + now := time.Now().UTC() stored.StoppedAt = &now stored.HypervisorPID = nil stored.PendingStandbyCompression = nil @@ -221,6 +222,7 @@ func (m *manager) standbyInstance( NotBefore: m.nowUTC().Add(compressionDelay), } } + stored.Phases.Record(phasetracking.PhaseStandby, now) meta = &metadata{StoredMetadata: *stored} if err := m.saveMetadata(meta); err != nil { diff --git a/lib/instances/start.go b/lib/instances/start.go index 8da3026e..181aaa8d 100644 --- a/lib/instances/start.go +++ b/lib/instances/start.go @@ -7,6 +7,7 @@ import ( "github.com/kernel/hypeman/lib/devices" "github.com/kernel/hypeman/lib/egressproxy" + "github.com/kernel/hypeman/lib/instances/phasetracking" "github.com/kernel/hypeman/lib/logger" "github.com/kernel/hypeman/lib/network" "go.opentelemetry.io/otel/attribute" @@ -209,7 +210,9 @@ func (m *manager) startInstance( // Success - release cleanup stack (prevent cleanup) cu.Release() - // 7. Update metadata (set PID, StartedAt) + // 7. Update metadata (set PID, StartedAt). Boot markers were cleared at + // the top of this function, so we are in Initializing until they hydrate. + stored.Phases.Record(phasetracking.PhaseInitializing, time.Now().UTC()) meta = &metadata{StoredMetadata: *stored} if err := m.saveMetadata(meta); err != nil { // VM is running but metadata failed - log but don't fail diff --git a/lib/instances/stop.go b/lib/instances/stop.go index 64b00307..3613323b 100644 --- a/lib/instances/stop.go +++ b/lib/instances/stop.go @@ -12,6 +12,7 @@ import ( "github.com/kernel/hypeman/lib/devices" "github.com/kernel/hypeman/lib/guest" "github.com/kernel/hypeman/lib/hypervisor" + "github.com/kernel/hypeman/lib/instances/phasetracking" "github.com/kernel/hypeman/lib/logger" "github.com/kernel/hypeman/lib/network" "go.opentelemetry.io/otel/attribute" @@ -297,13 +298,14 @@ func (m *manager) stopInstance( } // 10. Update metadata (clear PID, mdev UUID, set StoppedAt) - now := time.Now() + now := time.Now().UTC() stored.StoppedAt = &now stored.HypervisorPID = nil stored.GPUMdevUUID = "" // Clear mdev UUID since we destroyed it // Boot markers are per-boot-run and must not carry across stop/restore/start. stored.ProgramStartedAt = nil stored.GuestAgentReadyAt = nil + stored.Phases.Record(phasetracking.PhaseStopped, now) meta = &metadata{StoredMetadata: *stored} if err := m.saveMetadata(meta); err != nil { diff --git a/lib/instances/types.go b/lib/instances/types.go index 56243a0d..bf539714 100644 --- a/lib/instances/types.go +++ b/lib/instances/types.go @@ -5,6 +5,7 @@ import ( "github.com/kernel/hypeman/lib/autostandby" "github.com/kernel/hypeman/lib/hypervisor" + "github.com/kernel/hypeman/lib/instances/phasetracking" "github.com/kernel/hypeman/lib/snapshot" "github.com/kernel/hypeman/lib/tags" ) @@ -153,6 +154,12 @@ type StoredMetadata struct { // Exit information (populated from serial console sentinel when VM stops) ExitCode *int // App exit code, nil if VM hasn't exited ExitMessage string // Human-readable description of exit (e.g., "command not found", "killed by signal 9 (SIGKILL) - OOM") + + // Cumulative time spent in each lifecycle phase. Updated at every state + // transition by transition orchestration sites (create/start/stop/standby/ + // restore/fork). Consumers use Snapshot() to read live values that include + // time accrued in the current phase. See lib/instances/phasetracking. + Phases phasetracking.Tracker `json:"phases,omitempty"` } // Instance represents a virtual machine instance with derived runtime state diff --git a/lib/oapi/oapi.go b/lib/oapi/oapi.go index 093dd8f5..919636aa 100644 --- a/lib/oapi/oapi.go +++ b/lib/oapi/oapi.go @@ -863,6 +863,12 @@ type Instance struct { // CreatedAt Creation timestamp (RFC3339) CreatedAt time.Time `json:"created_at"` + // CurrentPhase The lifecycle phase the instance is currently in. + CurrentPhase *string `json:"current_phase,omitempty"` + + // CurrentPhaseSince When the instance entered current_phase. + CurrentPhaseSince *time.Time `json:"current_phase_since,omitempty"` + // DiskIoBps Disk I/O rate limit (human-readable, e.g., "100MB/s") DiskIoBps *string `json:"disk_io_bps,omitempty"` @@ -920,6 +926,14 @@ type Instance struct { // OverlaySize Writable overlay disk size (human-readable) OverlaySize *string `json:"overlay_size,omitempty"` + // PhaseDurationsMs Cumulative milliseconds the instance has spent in each lifecycle + // phase, including time accrued in the current phase up to the + // response time. Keys mirror instance states lowercased + // (running, standby, paused, stopped, created, initializing, + // shutdown). Consumers (e.g. billing) sum the phases they consider + // billable. + PhaseDurationsMs *map[string]int64 `json:"phase_durations_ms,omitempty"` + // Size Base memory size (human-readable) Size *string `json:"size,omitempty"` SnapshotPolicy *SnapshotPolicy `json:"snapshot_policy,omitempty"` @@ -15654,273 +15668,277 @@ func (sh *strictHandler) GetVolume(w http.ResponseWriter, r *http.Request, id st // Base64 encoded, gzipped, json marshaled Swagger object var swaggerSpec = []string{ - "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", } // GetSwagger returns the content of the embedded swagger specification file diff --git a/openapi.yaml b/openapi.yaml index e6446965..96c6ed61 100644 --- a/openapi.yaml +++ b/openapi.yaml @@ -878,7 +878,31 @@ components: $ref: "#/components/schemas/SnapshotPolicy" auto_standby: $ref: "#/components/schemas/AutoStandbyPolicy" - + phase_durations_ms: + type: object + description: | + Cumulative milliseconds the instance has spent in each lifecycle + phase, including time accrued in the current phase up to the + response time. Keys mirror instance states lowercased + (running, standby, paused, stopped, created, initializing, + shutdown). Consumers (e.g. billing) sum the phases they consider + billable. + additionalProperties: + type: integer + format: int64 + example: + running: 60000 + standby: 300000 + current_phase: + type: string + description: The lifecycle phase the instance is currently in. + example: running + current_phase_since: + type: string + format: date-time + description: When the instance entered current_phase. + example: "2026-05-11T14:00:00Z" + PathInfo: type: object required: [exists]