Skip to content

feat(runtime): make fake runtime persistent (#65)#72

Merged
feloy merged 2 commits intokortex-hub:mainfrom
feloy:fake-persist
Mar 16, 2026
Merged

feat(runtime): make fake runtime persistent (#65)#72
feloy merged 2 commits intokortex-hub:mainfrom
feloy:fake-persist

Conversation

@feloy
Copy link
Contributor

@feloy feloy commented Mar 16, 2026

Implements the StorageAware interface for the fake runtime to persist instances to disk, allowing them to be discovered across multiple CLI runs. Instances are saved to instances.json and automatically loaded during initialization. The runtime remains backward compatible, operating in memory-only mode when not initialized with storage.

fixes #65

Implements the StorageAware interface for the fake runtime to persist
instances to disk, allowing them to be discovered across multiple CLI
runs. Instances are saved to instances.json and automatically loaded
during initialization. The runtime remains backward compatible,
operating in memory-only mode when not initialized with storage.

Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
Signed-off-by: Philippe Martin <phmartin@redhat.com>
@codecov-commenter
Copy link

codecov-commenter commented Mar 16, 2026

Codecov Report

❌ Patch coverage is 69.01408% with 22 lines in your changes missing coverage. Please review.

Files with missing lines Patch % Lines
pkg/runtime/fake/fake.go 69.01% 11 Missing and 11 partials ⚠️

📢 Thoughts on this report? Let us know!

@coderabbitai
Copy link

coderabbitai bot commented Mar 16, 2026

📝 Walkthrough

Walkthrough

Adds disk-backed persistence to the fake runtime: new Initialize(storageDir) setup, JSON-serialized on-disk state, load/save logic, and updates to Create/Start/Stop/Remove/Info to persist instance lifecycle across process restarts.

Changes

Cohort / File(s) Summary
Fake Runtime Implementation
pkg/runtime/fake/fake.go
Added storage fields (storageDir, storageFile), Initialize(storageDir), loadFromDisk() / saveToDisk(), and persistedData struct. Converted instanceState to exported JSON-annotated fields (ID, Name, State, Info, Source, Config). Updated Create/Start/Stop/Remove/Info to persist changes and handle missing storage gracefully. Added JSON/filepath/os imports and StorageAware compile-time implementation.
Fake Runtime Tests
pkg/runtime/fake/fake_test.go
Added comprehensive persistence tests: cross-runtime loading, state-change persistence, removal persistence, sequential ID generation across restarts, initialization edge cases (empty/invalid storage), in-memory fallback, and nil-Info handling. Minor comment tweak for idempotent Remove behavior.

Sequence Diagram

sequenceDiagram
    participant CLI as CLI/Client
    participant RT as Fake Runtime
    participant FS as File System

    Note over CLI,RT: Initial run - create and start instance
    CLI->>RT: Initialize(storageDir)
    RT->>FS: loadFromDisk()
    FS-->>RT: existing or empty persistedData
    CLI->>RT: Create(config)
    RT->>RT: allocate ID, create instanceState
    RT->>FS: saveToDisk() (persist instance)
    FS-->>RT: ack
    CLI->>RT: Start(instanceID)
    RT->>RT: update State/Info
    RT->>FS: saveToDisk() (persist state)
    FS-->>RT: ack

    Note over CLI,RT: After restart - recover instances
    CLI->>RT: Initialize(storageDir)
    RT->>FS: loadFromDisk()
    FS-->>RT: persistedData with instances
    RT->>RT: restore in-memory instanceState(s)
    CLI->>RT: Info(instanceID)
    RT-->>CLI: return restored instance (ID, State, Info, Source, Config)
Loading

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~25 minutes

Possibly related PRs

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 9.09% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Title check ✅ Passed The title 'feat(runtime): make fake runtime persistent (#65)' clearly and concisely describes the main change: adding persistence to the fake runtime.
Description check ✅ Passed The description is directly related to the changeset, explaining the StorageAware interface implementation, instances.json persistence, and backward compatibility.
Linked Issues check ✅ Passed The pull request successfully implements disk persistence for fake runtime instances [#65], enabling discovery across CLI runs through StorageAware interface implementation and instances.json storage.
Out of Scope Changes check ✅ Passed All changes are within scope: core persistence logic, storage initialization, state management updates, and comprehensive test coverage for the persistence feature are directly aligned with issue #65 objectives.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

📝 Coding Plan
  • Generate coding plan for human review comments

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

Tip

You can customize the high-level summary generated by CodeRabbit.

Configure the reviews.high_level_summary_instructions setting to provide custom instructions for generating the high-level summary.

Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 3

🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@pkg/runtime/fake/fake.go`:
- Around line 48-60: The persisted JSON currently uses flat fields (e.g.,
persistedData.NextID) which violates the nested-object schema rule; change
persistedData to embed a nested Meta (or Metadata) struct holding NextID (e.g.,
Meta struct { NextID int `json:"next_id"` }) and keep Instances as a
map[string]*instanceState so the top-level JSON becomes { "meta": { "next_id":
... }, "instances": { ... } }; update any code that marshals/unmarshals
persistedData and any accesses of persistedData.NextID or the file load/save
routines to use persistedData.Meta.NextID and ensure instanceState fields remain
as nested objects with appropriate json tags.
- Around line 79-89: In fakeRuntime.Initialize, normalize the incoming
storageDir to an absolute path before storing it: call filepath.Abs(storageDir),
return any error from Abs if it fails, then set f.storageDir to that absolute
path and compute f.storageFile with filepath.Join(absPath, storageFileName)
(preserve the existing locking around the assignment). This ensures
fakeRuntime.Initialize (function Initialize on type fakeRuntime and symbol
storageFileName) always stores an absolute path and uses filepath.Join for the
storage file.
- Around line 122-126: When restoring persisted state in fake.go (setting
f.nextID and f.instances), harden each loaded instanceState by ensuring its Info
map is non-nil so Start/Stop won't panic on inst.Info[...] assignments: after
assigning f.instances = persisted.Instances iterate over the map and for each
instance (type instanceState) if inst.Info == nil set inst.Info =
make(map[string]string) (or the correct value type), also apply the same non-nil
initialization for any other maps used by instance lifecycle methods referenced
by Start/Stop.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: 85b8ff50-59b4-4018-9852-b9090039af02

📥 Commits

Reviewing files that changed from the base of the PR and between 2f5c00b and f6dce5a.

📒 Files selected for processing (2)
  • pkg/runtime/fake/fake.go
  • pkg/runtime/fake/fake_test.go

After loading instances from disk, ensure the Info map is non-nil
to prevent panics in Start/Stop methods. Adds defensive initialization
and comprehensive test coverage for corrupted storage scenarios.

Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
Signed-off-by: Philippe Martin <phmartin@redhat.com>
Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 2

🧹 Nitpick comments (1)
pkg/runtime/fake/fake.go (1)

152-159: Consider atomic write to prevent file corruption.

os.WriteFile directly overwrites the file, which can leave it corrupted if the process crashes mid-write. For a test runtime this may be acceptable, but atomic write (write to temp file, then rename) is safer.

♻️ Optional: Atomic write pattern
-	if err := os.WriteFile(f.storageFile, data, 0644); err != nil {
-		return fmt.Errorf("failed to write storage file: %w", err)
-	}
+	tmpFile := f.storageFile + ".tmp"
+	if err := os.WriteFile(tmpFile, data, 0644); err != nil {
+		return fmt.Errorf("failed to write storage file: %w", err)
+	}
+	if err := os.Rename(tmpFile, f.storageFile); err != nil {
+		return fmt.Errorf("failed to finalize storage file: %w", err)
+	}
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@pkg/runtime/fake/fake.go` around lines 152 - 159, The current write uses
os.WriteFile which can corrupt f.storageFile if interrupted; change the
persistence logic (the block that marshals persisted with json.MarshalIndent and
writes to f.storageFile) to perform an atomic write: write the marshaled data to
a uniquely named temp file in the same directory (e.g., f.storageFile+".tmp" or
use os.CreateTemp), fsync the temp file (File.Sync) and close it, then
atomically replace the target by os.Rename(tempPath, f.storageFile); also handle
cleanup on errors and propagate failures (preserve the existing error wrapping
messages).
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@pkg/runtime/fake/fake.go`:
- Around line 209-214: The code assigns f.instances[id] and then calls
saveToDisk(), which can fail and leave in-memory state inconsistent; modify the
routine so that if saveToDisk() returns an error you roll back the in-memory
mutation: delete f.instances[id] and decrement f.nextID (or restore its previous
value) before returning the fmt.Errorf; do this where f.instances[id] = state
and the subsequent saveToDisk() call occur, referencing f.instances, f.nextID
and saveToDisk() to locate the change and ensure the rollback happens under the
same lock/guard used for mutations.
- Around line 122-137: When loading persisted state into f.nextID and
f.instances, defend against nil map values and invalid nextID: iterate
f.instances and for any nil entry (value == nil) allocate a new instanceState
(with Info initialized) and assign it back into f.instances to avoid panics when
accessing inst.Info; also ensure inst.Info is non-nil for existing entries;
finally validate persisted.NextID and if it's <= 0 set f.nextID to 1 (or max(1,
persisted.NextID)) to avoid ID collisions. Apply these changes around where
f.nextID = persisted.NextID and the loop over f.instances and use the
instanceState and Info symbols to locate the code to update.

---

Nitpick comments:
In `@pkg/runtime/fake/fake.go`:
- Around line 152-159: The current write uses os.WriteFile which can corrupt
f.storageFile if interrupted; change the persistence logic (the block that
marshals persisted with json.MarshalIndent and writes to f.storageFile) to
perform an atomic write: write the marshaled data to a uniquely named temp file
in the same directory (e.g., f.storageFile+".tmp" or use os.CreateTemp), fsync
the temp file (File.Sync) and close it, then atomically replace the target by
os.Rename(tempPath, f.storageFile); also handle cleanup on errors and propagate
failures (preserve the existing error wrapping messages).
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: 6b306ff8-dfa9-4426-84da-1be0cc21336e

📥 Commits

Reviewing files that changed from the base of the PR and between f6dce5a and 3318d27.

📒 Files selected for processing (2)
  • pkg/runtime/fake/fake.go
  • pkg/runtime/fake/fake_test.go
🚧 Files skipped from review as they are similar to previous changes (1)
  • pkg/runtime/fake/fake_test.go

@feloy feloy requested review from benoitf and jeffmaury March 16, 2026 13:00
@feloy feloy merged commit 8c0917a into kortex-hub:main Mar 16, 2026
6 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

make fake runtime persistent

3 participants