Class: VagrantPlugins::OrbStack::Provider

Inherits:
Object
  • Object
show all
Defined in:
lib/vagrant-orbstack/provider.rb

Overview

Vagrant provider implementation for OrbStack.

This class implements the Vagrant provider interface, delegating machine lifecycle operations to OrbStack via CLI commands.

Instance Method Summary collapse

Constructor Details

#initialize(machine) ⇒ Provider

Initialize the provider with a machine instance.

Parameters:

  • machine (Vagrant::Machine)

    The machine this provider is for



23
24
25
26
# File 'lib/vagrant-orbstack/provider.rb', line 23

def initialize(machine)
  @machine = machine
  @logger = Log4r::Logger.new('vagrant_orbstack::provider')
end

Instance Method Details

#action(name) ⇒ Vagrant::Action::Builder?

Return action middleware for requested operation.

Creates and returns an Action::Builder containing the appropriate middleware stack for the requested operation. Unsupported actions return nil.

Parameters:

  • name (Symbol)

    The action name (:up, :halt, :destroy, etc.)

Returns:

  • (Vagrant::Action::Builder, nil)

    Action middleware builder or nil



37
38
39
40
# File 'lib/vagrant-orbstack/provider.rb', line 37

def action(name)
  # Returns nil for unsupported actions (future stories, etc.)
  action_builders[name]&.call
end

#id_file_pathPathname

Path to the machine ID file.

Returns:

  • (Pathname)

    Path to the ID file



222
223
224
# File 'lib/vagrant-orbstack/provider.rb', line 222

def id_file_path
  @machine.data_dir.join('id')
end

#invalidate_state_cachevoid

This method returns an undefined value.

Invalidate the state cache.

Clears all cached state entries, forcing the next state query to fetch fresh data from OrbStack CLI. This is typically called by action middleware after state-changing operations (create, start, stop).



105
106
107
# File 'lib/vagrant-orbstack/provider.rb', line 105

def invalidate_state_cache
  state_cache.invalidate_all
end

#machine_id_changedvoid

This method returns an undefined value.

Callback invoked when the machine ID changes.

Persists the new machine ID to the data directory for retrieval in future Vagrant sessions. This is called by Vagrant core when a machine is created or its ID is updated.

The guard clause ensures we only persist when the machine has a valid ID, as some test scenarios may not have @machine.id available.



128
129
130
131
132
133
134
# File 'lib/vagrant-orbstack/provider.rb', line 128

def machine_id_changed
  # Guard clause: Only persist if machine has an ID
  # Some test scenarios may not have @machine.id available
  return unless @machine.respond_to?(:id) && !@machine.id.nil?

  write_machine_id(@machine.id)
end

#metadata_file_pathPathname

Path to the metadata JSON file.

Returns:

  • (Pathname)

    Path to the metadata file



230
231
232
# File 'lib/vagrant-orbstack/provider.rb', line 230

def 
  @machine.data_dir.join('metadata.json')
end

#read_machine_idString?

Read the machine ID from persistent storage.

Reads the machine ID from the id file in the data directory. Returns nil if the file doesn't exist or cannot be read.

Returns:

  • (String, nil)

    The machine ID if found, nil otherwise

Raises:

  • (Errno::EACCES)

    If permission denied (logged and returns nil)

  • (Errno::ENOENT)

    If file not found (logged and returns nil)

  • (Encoding::InvalidByteSequenceError)

    If file contains invalid data (logged and returns nil)



146
147
148
149
150
151
152
153
154
# File 'lib/vagrant-orbstack/provider.rb', line 146

def read_machine_id
  return nil unless File.exist?(id_file_path)

  File.read(id_file_path).strip
rescue Errno::EACCES, Errno::ENOENT, Encoding::InvalidByteSequenceError => e
  # Log error and return nil - graceful degradation for non-critical errors
  @machine.ui&.warn("OrbStack: Could not read machine ID: #{e.message}")
  nil
end

#read_metadataHash

Read machine metadata from persistent storage.

Reads metadata from the metadata.json file in the data directory. Returns an empty hash if the file doesn't exist or contains invalid JSON.

Returns:

  • (Hash)

    The metadata hash, or empty hash if not found

Raises:

  • (JSON::ParserError)

    If JSON is invalid (logged and returns {})

  • (Errno::EACCES)

    If permission denied (logged and returns {})

  • (Errno::ENOENT)

    If file not found (logged and returns {})

  • (Encoding::InvalidByteSequenceError)

    If file contains invalid data (logged and returns {})



187
188
189
190
191
192
193
194
195
# File 'lib/vagrant-orbstack/provider.rb', line 187

def 
  return {} unless File.exist?()

  JSON.parse(File.read())
rescue JSON::ParserError, Errno::EACCES, Errno::ENOENT, Encoding::InvalidByteSequenceError => e
  # Log error and return empty hash - graceful degradation for non-critical errors
  @machine.ui&.warn("OrbStack: Could not read metadata: #{e.message}")
  {}
end

#ssh_infoHash?

Provide SSH connection information for the machine.

Returns SSH connection parameters for Vagrant to connect to the machine using OrbStack's SSH proxy architecture.

CRITICAL: OrbStack uses SSH proxy at localhost:32222, NOT direct SSH to VM IP.

Returns nil if the machine is not running.

Returns:

  • (Hash, nil)

    SSH connection parameters with keys:

    • :host - Always '127.0.0.1' (OrbStack SSH proxy, NOT VM IP)
    • :port - Always 32222 (OrbStack SSH proxy port, NOT 22)
    • :username - Machine ID for proxy routing
    • :private_key_path - OrbStack's auto-generated ED25519 key
    • :forward_agent - Whether to forward SSH agent (from config)


58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
# File 'lib/vagrant-orbstack/provider.rb', line 58

def ssh_info
  # Return nil if machine is not running
  current_state = state
  return nil if %i[not_created stopped].include?(current_state.id)

  # Return OrbStack SSH proxy configuration
  {
    host: '127.0.0.1',
    port: 32_222,
    username: @machine.id,
    private_key_path: File.expand_path('~/.orbstack/ssh/id_ed25519'),
    proxy_command: orbstack_proxy_command,
    forward_agent: @machine.provider_config.forward_agent
  }
end

#stateVagrant::MachineState

Return current machine state.

Queries OrbStack CLI to determine the current state of the machine. Results are cached with a 5-second TTL to reduce redundant CLI calls. State is automatically invalidated when state-changing actions occur.

Returns:

  • (Vagrant::MachineState)

    Current state of the machine



82
83
84
85
86
87
88
89
90
91
92
93
94
95
# File 'lib/vagrant-orbstack/provider.rb', line 82

def state
  # Return early if machine ID is nil
  return not_created_state('The machine has not been created') if @machine.id.nil?

  # Check cache first
  cached_state = state_cache.get(@machine.id)
  return cached_state if cached_state

  # Cache miss: Query OrbStack CLI
  query_and_cache_state
rescue StandardError => e
  # Handle query errors gracefully
  handle_state_query_error(e)
end

#to_sString

Human-readable provider description.

Returns:

  • (String)

    Provider name



113
114
115
# File 'lib/vagrant-orbstack/provider.rb', line 113

def to_s
  'OrbStack'
end

#write_machine_id(machine_id) ⇒ void

This method returns an undefined value.

Write the machine ID to persistent storage.

Writes the machine ID to the id file in the data directory. Creates the directory if it doesn't exist.

Parameters:

  • machine_id (String)

    The machine ID to persist

Raises:

  • (Errno::EACCES)

    If permission denied

  • (Errno::ENOSPC)

    If disk is full

  • (Errno::EROFS)

    If filesystem is read-only



167
168
169
170
171
172
173
174
# File 'lib/vagrant-orbstack/provider.rb', line 167

def write_machine_id(machine_id)
  ensure_data_dir_exists
  File.write(id_file_path, machine_id)
rescue Errno::EACCES, Errno::ENOSPC, Errno::EROFS => e
  # Log error and re-raise - critical errors that cannot be ignored
  @machine.ui&.error("OrbStack: Could not write machine ID: #{e.message}")
  raise
end

#write_metadata(metadata) ⇒ void

This method returns an undefined value.

Write machine metadata to persistent storage.

Writes metadata to the metadata.json file in the data directory. Creates the directory if it doesn't exist. Formats JSON for readability.

Parameters:

  • metadata (Hash)

    The metadata hash to persist

Raises:

  • (JSON::ParserError)

    If JSON generation fails

  • (Errno::EACCES)

    If permission denied

  • (Errno::ENOSPC)

    If disk is full

  • (Errno::EROFS)

    If filesystem is read-only



209
210
211
212
213
214
215
216
# File 'lib/vagrant-orbstack/provider.rb', line 209

def ()
  ensure_data_dir_exists
  File.write(, JSON.pretty_generate())
rescue JSON::ParserError, Errno::EACCES, Errno::ENOSPC, Errno::EROFS => e
  # Log error and re-raise - critical errors that cannot be ignored
  @machine.ui&.error("OrbStack: Could not write metadata: #{e.message}")
  raise
end