diff options
Diffstat (limited to 'lib/hyperstack/state.rb')
| -rw-r--r-- | lib/hyperstack/state.rb | 57 |
1 files changed, 57 insertions, 0 deletions
diff --git a/lib/hyperstack/state.rb b/lib/hyperstack/state.rb new file mode 100644 index 0000000..0ce2687 --- /dev/null +++ b/lib/hyperstack/state.rb @@ -0,0 +1,57 @@ +# frozen_string_literal: true + +require 'json' + +module HyperstackVM + # Persists VM state to a JSON file with atomic writes (write-to-tmp + rename). + # Used to track provisioned VM ID, IP, WireGuard keys, and related metadata. + class StateStore + def initialize(path) + @path = path + end + + attr_reader :path + + def load + return nil unless File.exist?(@path) + + JSON.parse(File.read(@path)) + rescue JSON::ParserError => e + raise Error, "Failed to parse state file #{@path}: #{e.message}" + end + + def save(payload) + temp_path = "#{@path}.tmp" + File.write(temp_path, JSON.pretty_generate(payload)) + File.rename(temp_path, @path) + end + + def delete + File.delete(@path) if File.exist?(@path) + end + end + + # Thread-safe output wrapper that prepends a fixed prefix to each line. + # Used by create-both so interleaved output from VM1 and VM2 threads is distinguishable. + # #print buffers partial lines until a newline is received, then flushes with the prefix. + class PrefixedOutput + def initialize(prefix, delegate, mutex) + @prefix = prefix + @delegate = delegate + @mutex = mutex + @buffer = +'' + end + + def puts(msg = '') + @mutex.synchronize { @delegate.puts("#{@prefix}#{msg}") } + end + + def print(msg) + @buffer << msg.to_s + while (idx = @buffer.index("\n")) + line = @buffer.slice!(0, idx + 1) + @mutex.synchronize { @delegate.print("#{@prefix}#{line}") } + end + end + end +end |
