vagrant-orbstack 0.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +7 -0
- data/CHANGELOG.md +155 -0
- data/LICENSE +21 -0
- data/README.md +622 -0
- data/lib/vagrant-orbstack/action/create.rb +177 -0
- data/lib/vagrant-orbstack/action/destroy.rb +90 -0
- data/lib/vagrant-orbstack/action/halt.rb +64 -0
- data/lib/vagrant-orbstack/action/machine_validation.rb +60 -0
- data/lib/vagrant-orbstack/action/reload.rb +56 -0
- data/lib/vagrant-orbstack/action/ssh_run.rb +70 -0
- data/lib/vagrant-orbstack/action/start.rb +68 -0
- data/lib/vagrant-orbstack/action.rb +21 -0
- data/lib/vagrant-orbstack/config.rb +161 -0
- data/lib/vagrant-orbstack/errors.rb +64 -0
- data/lib/vagrant-orbstack/plugin.rb +45 -0
- data/lib/vagrant-orbstack/provider.rb +387 -0
- data/lib/vagrant-orbstack/util/machine_namer.rb +130 -0
- data/lib/vagrant-orbstack/util/orbstack_cli.rb +283 -0
- data/lib/vagrant-orbstack/util/ssh_readiness_checker.rb +115 -0
- data/lib/vagrant-orbstack/util/state_cache.rb +106 -0
- data/lib/vagrant-orbstack/version.rb +7 -0
- data/lib/vagrant-orbstack.rb +36 -0
- data/locales/en.yml +49 -0
- metadata +69 -0
|
@@ -0,0 +1,177 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require 'time'
|
|
4
|
+
require_relative '../util/machine_namer'
|
|
5
|
+
require_relative '../util/orbstack_cli'
|
|
6
|
+
require_relative '../util/ssh_readiness_checker'
|
|
7
|
+
|
|
8
|
+
module VagrantPlugins
|
|
9
|
+
module OrbStack
|
|
10
|
+
module Action
|
|
11
|
+
# Action middleware for creating and starting OrbStack machines
|
|
12
|
+
#
|
|
13
|
+
# This middleware handles the machine creation lifecycle with full
|
|
14
|
+
# idempotency support. It checks the current machine state and:
|
|
15
|
+
# - If running: does nothing (no-op)
|
|
16
|
+
# - If stopped: starts the existing machine
|
|
17
|
+
# - If not created: creates a new machine with unique name
|
|
18
|
+
#
|
|
19
|
+
# After creation, persists machine metadata and invalidates state cache.
|
|
20
|
+
#
|
|
21
|
+
# @example Usage in action builder
|
|
22
|
+
# Vagrant::Action::Builder.new.tap do |b|
|
|
23
|
+
# b.use VagrantPlugins::OrbStack::Action::Create
|
|
24
|
+
# end
|
|
25
|
+
#
|
|
26
|
+
# @api public
|
|
27
|
+
class Create
|
|
28
|
+
# Initialize the middleware.
|
|
29
|
+
#
|
|
30
|
+
# @param app [Object] The next middleware in the chain
|
|
31
|
+
# @param env [Hash] The environment hash containing :machine, :ui, etc.
|
|
32
|
+
# @api public
|
|
33
|
+
def initialize(app, _env)
|
|
34
|
+
@app = app
|
|
35
|
+
end
|
|
36
|
+
|
|
37
|
+
# Execute the middleware.
|
|
38
|
+
#
|
|
39
|
+
# Implements idempotent machine creation:
|
|
40
|
+
# 1. Query current state
|
|
41
|
+
# 2. If running: no-op
|
|
42
|
+
# 3. If stopped: start machine
|
|
43
|
+
# 4. If not created: create new machine
|
|
44
|
+
# 5. Persist metadata and invalidate cache
|
|
45
|
+
# 6. Continue middleware chain
|
|
46
|
+
#
|
|
47
|
+
# @param env [Hash] The environment hash
|
|
48
|
+
# @return [Object] Result from next middleware
|
|
49
|
+
# @raise [MachineNameCollisionError] If name generation fails
|
|
50
|
+
# @raise [OrbStackNotInstalled] If OrbStack CLI not available
|
|
51
|
+
# @raise [CommandTimeoutError] If CLI command times out
|
|
52
|
+
# @raise [CommandExecutionError] If machine creation/start fails
|
|
53
|
+
# @api public
|
|
54
|
+
def call(env)
|
|
55
|
+
handle_machine_state(env)
|
|
56
|
+
@app.call(env)
|
|
57
|
+
end
|
|
58
|
+
|
|
59
|
+
private
|
|
60
|
+
|
|
61
|
+
# Route to appropriate state handler based on current machine state.
|
|
62
|
+
#
|
|
63
|
+
# @param env [Hash] The environment hash
|
|
64
|
+
# @return [void]
|
|
65
|
+
# @api private
|
|
66
|
+
def handle_machine_state(env)
|
|
67
|
+
current_state = env[:machine].provider.state
|
|
68
|
+
|
|
69
|
+
case current_state.id
|
|
70
|
+
when :running then handle_running_machine(env)
|
|
71
|
+
when :stopped then handle_stopped_machine(env)
|
|
72
|
+
else handle_not_created_machine(env)
|
|
73
|
+
end
|
|
74
|
+
end
|
|
75
|
+
|
|
76
|
+
# Handle a machine that is already running (no-op).
|
|
77
|
+
#
|
|
78
|
+
# @param env [Hash] The environment hash
|
|
79
|
+
# @return [void]
|
|
80
|
+
# @api private
|
|
81
|
+
def handle_running_machine(env)
|
|
82
|
+
env[:machine].ui.info('Machine is already running')
|
|
83
|
+
end
|
|
84
|
+
|
|
85
|
+
# Handle a machine that is stopped by starting it.
|
|
86
|
+
#
|
|
87
|
+
# @param env [Hash] The environment hash
|
|
88
|
+
# @return [void]
|
|
89
|
+
# @api private
|
|
90
|
+
def handle_stopped_machine(env)
|
|
91
|
+
machine = env[:machine]
|
|
92
|
+
machine.ui.info('Starting stopped machine...')
|
|
93
|
+
Util::OrbStackCLI.start_machine(machine.id)
|
|
94
|
+
Util::SSHReadinessChecker.wait_for_ready(machine.id, ui: machine.ui)
|
|
95
|
+
machine.provider.invalidate_state_cache
|
|
96
|
+
end
|
|
97
|
+
|
|
98
|
+
# Handle a machine that doesn't exist by creating it.
|
|
99
|
+
#
|
|
100
|
+
# @param env [Hash] The environment hash
|
|
101
|
+
# @return [void]
|
|
102
|
+
# @api private
|
|
103
|
+
def handle_not_created_machine(env)
|
|
104
|
+
create_machine(env)
|
|
105
|
+
end
|
|
106
|
+
|
|
107
|
+
# Create a new OrbStack machine.
|
|
108
|
+
#
|
|
109
|
+
# Generates unique machine name, creates machine via OrbStack CLI,
|
|
110
|
+
# persists metadata, and invalidates state cache.
|
|
111
|
+
#
|
|
112
|
+
# @param env [Hash] The environment hash
|
|
113
|
+
# @return [void]
|
|
114
|
+
# @api private
|
|
115
|
+
def create_machine(env)
|
|
116
|
+
machine = env[:machine]
|
|
117
|
+
ui = machine.ui
|
|
118
|
+
|
|
119
|
+
ui.info('Creating new machine...')
|
|
120
|
+
|
|
121
|
+
machine_name = Util::MachineNamer.generate(machine)
|
|
122
|
+
distro = build_distribution_string(machine.provider_config)
|
|
123
|
+
Util::OrbStackCLI.create_machine(machine_name, distribution: distro)
|
|
124
|
+
Util::SSHReadinessChecker.wait_for_ready(machine_name, ui: ui)
|
|
125
|
+
|
|
126
|
+
persist_metadata(env, machine_name, distro)
|
|
127
|
+
|
|
128
|
+
ui.info("Machine '#{machine_name}' created successfully")
|
|
129
|
+
end
|
|
130
|
+
|
|
131
|
+
# Build distribution string from configuration.
|
|
132
|
+
#
|
|
133
|
+
# Formats the distribution string for OrbStack CLI based on
|
|
134
|
+
# configured distro and version. If version is specified, returns
|
|
135
|
+
# "distro:version", otherwise just "distro".
|
|
136
|
+
#
|
|
137
|
+
# @param config [VagrantPlugins::OrbStack::Config] Provider configuration
|
|
138
|
+
# @return [String] Formatted distribution string
|
|
139
|
+
# @api private
|
|
140
|
+
def build_distribution_string(config)
|
|
141
|
+
return config.distro unless config.version
|
|
142
|
+
|
|
143
|
+
"#{config.distro}:#{config.version}"
|
|
144
|
+
end
|
|
145
|
+
|
|
146
|
+
# Persist machine metadata to Vagrant data directory.
|
|
147
|
+
#
|
|
148
|
+
# Stores machine ID and metadata (name, distribution, timestamp)
|
|
149
|
+
# for future Vagrant operations.
|
|
150
|
+
#
|
|
151
|
+
# @param env [Hash] The environment hash
|
|
152
|
+
# @param machine_name [String] The generated machine name
|
|
153
|
+
# @param distro [String] The distribution string (e.g., "ubuntu:noble")
|
|
154
|
+
# @return [void]
|
|
155
|
+
# @api private
|
|
156
|
+
def persist_metadata(env, machine_name, distro)
|
|
157
|
+
machine = env[:machine]
|
|
158
|
+
provider = machine.provider
|
|
159
|
+
|
|
160
|
+
# Store machine ID (Vagrant core will call machine.id= after this)
|
|
161
|
+
provider.write_machine_id(machine_name)
|
|
162
|
+
|
|
163
|
+
# Store metadata
|
|
164
|
+
metadata = {
|
|
165
|
+
'machine_name' => machine_name,
|
|
166
|
+
'distribution' => distro,
|
|
167
|
+
'created_at' => Time.now.utc.iso8601
|
|
168
|
+
}
|
|
169
|
+
provider.write_metadata(metadata)
|
|
170
|
+
|
|
171
|
+
# Invalidate state cache to force fresh query
|
|
172
|
+
provider.invalidate_state_cache
|
|
173
|
+
end
|
|
174
|
+
end
|
|
175
|
+
end
|
|
176
|
+
end
|
|
177
|
+
end
|
|
@@ -0,0 +1,90 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require_relative '../util/orbstack_cli'
|
|
4
|
+
require_relative 'machine_validation'
|
|
5
|
+
require 'fileutils'
|
|
6
|
+
|
|
7
|
+
module VagrantPlugins
|
|
8
|
+
module OrbStack
|
|
9
|
+
module Action
|
|
10
|
+
# Action middleware for destroying (deleting) OrbStack machines
|
|
11
|
+
#
|
|
12
|
+
# This middleware handles permanent removal of OrbStack machines, including
|
|
13
|
+
# deletion from OrbStack and cleanup of local metadata files. The operation
|
|
14
|
+
# is idempotent and uses a best-effort cleanup strategy: if the OrbStack
|
|
15
|
+
# CLI deletion fails (e.g., machine already deleted or daemon offline), local
|
|
16
|
+
# cleanup continues anyway to ensure Vagrant state remains consistent.
|
|
17
|
+
#
|
|
18
|
+
# @example Usage in action builder
|
|
19
|
+
# Vagrant::Action::Builder.new.tap do |b|
|
|
20
|
+
# b.use VagrantPlugins::OrbStack::Action::Destroy
|
|
21
|
+
# end
|
|
22
|
+
#
|
|
23
|
+
# @api public
|
|
24
|
+
class Destroy
|
|
25
|
+
include MachineValidation
|
|
26
|
+
|
|
27
|
+
# Initialize the middleware.
|
|
28
|
+
#
|
|
29
|
+
# @param app [Object] The next middleware in the chain
|
|
30
|
+
# @param env [Hash] The environment hash containing :machine, :ui, etc.
|
|
31
|
+
# @api public
|
|
32
|
+
def initialize(app, _env)
|
|
33
|
+
@app = app
|
|
34
|
+
end
|
|
35
|
+
|
|
36
|
+
# Execute the middleware.
|
|
37
|
+
#
|
|
38
|
+
# Destroys the machine by:
|
|
39
|
+
# 1. Calling OrbStack CLI delete command (best-effort)
|
|
40
|
+
# 2. Removing id and metadata.json files from data directory
|
|
41
|
+
# 3. Invalidating state cache
|
|
42
|
+
# 4. Continuing middleware chain
|
|
43
|
+
#
|
|
44
|
+
# If the OrbStack CLI delete fails, a warning is logged and cleanup
|
|
45
|
+
# continues. This ensures Vagrant can clean up its local state even
|
|
46
|
+
# if the machine was already deleted manually or OrbStack is offline.
|
|
47
|
+
#
|
|
48
|
+
# @param env [Hash] The environment hash
|
|
49
|
+
# @return [Object] Result from next middleware
|
|
50
|
+
# @raise [ArgumentError] If machine ID is empty string (nil is handled gracefully)
|
|
51
|
+
# @api public
|
|
52
|
+
# rubocop:disable Metrics/MethodLength, Metrics/AbcSize
|
|
53
|
+
def call(env)
|
|
54
|
+
machine = env[:machine]
|
|
55
|
+
ui = env[:ui]
|
|
56
|
+
|
|
57
|
+
# Handle already-destroyed machines gracefully (idempotency)
|
|
58
|
+
if machine.id.nil?
|
|
59
|
+
ui.info('Machine is already destroyed or was never created.')
|
|
60
|
+
return @app.call(env)
|
|
61
|
+
end
|
|
62
|
+
|
|
63
|
+
# Validate machine ID exists
|
|
64
|
+
machine_id = validate_machine_id!(machine, 'destroy')
|
|
65
|
+
|
|
66
|
+
ui.info("Destroying machine '#{machine_id}'...")
|
|
67
|
+
|
|
68
|
+
# Call OrbStack CLI to delete the machine (best-effort)
|
|
69
|
+
begin
|
|
70
|
+
Util::OrbStackCLI.delete_machine(machine_id)
|
|
71
|
+
rescue Errors::CommandExecutionError => e
|
|
72
|
+
ui.warn("Error deleting machine from OrbStack: #{e.message}")
|
|
73
|
+
ui.warn('Continuing with local cleanup...')
|
|
74
|
+
end
|
|
75
|
+
|
|
76
|
+
# Clean up metadata files from data_dir (idempotent)
|
|
77
|
+
FileUtils.rm_f(machine.provider.id_file_path)
|
|
78
|
+
FileUtils.rm_f(machine.provider.metadata_file_path)
|
|
79
|
+
|
|
80
|
+
# Invalidate state cache to ensure fresh state on next query
|
|
81
|
+
machine.provider.invalidate_state_cache
|
|
82
|
+
|
|
83
|
+
# Continue middleware chain
|
|
84
|
+
@app.call(env)
|
|
85
|
+
end
|
|
86
|
+
# rubocop:enable Metrics/MethodLength, Metrics/AbcSize
|
|
87
|
+
end
|
|
88
|
+
end
|
|
89
|
+
end
|
|
90
|
+
end
|
|
@@ -0,0 +1,64 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require_relative '../util/orbstack_cli'
|
|
4
|
+
require_relative 'machine_validation'
|
|
5
|
+
|
|
6
|
+
module VagrantPlugins
|
|
7
|
+
module OrbStack
|
|
8
|
+
module Action
|
|
9
|
+
# Action middleware for halting (stopping) OrbStack machines
|
|
10
|
+
#
|
|
11
|
+
# This middleware handles stopping a running OrbStack machine gracefully.
|
|
12
|
+
# It calls the OrbStack CLI to stop the machine and invalidates the
|
|
13
|
+
# provider's state cache to ensure subsequent state queries are fresh.
|
|
14
|
+
#
|
|
15
|
+
# @example Usage in action builder
|
|
16
|
+
# Vagrant::Action::Builder.new.tap do |b|
|
|
17
|
+
# b.use VagrantPlugins::OrbStack::Action::Halt
|
|
18
|
+
# end
|
|
19
|
+
#
|
|
20
|
+
# @api public
|
|
21
|
+
class Halt
|
|
22
|
+
include MachineValidation
|
|
23
|
+
|
|
24
|
+
# Initialize the middleware.
|
|
25
|
+
#
|
|
26
|
+
# @param app [Object] The next middleware in the chain
|
|
27
|
+
# @param env [Hash] The environment hash containing :machine, :ui, etc.
|
|
28
|
+
# @api public
|
|
29
|
+
def initialize(app, _env)
|
|
30
|
+
@app = app
|
|
31
|
+
end
|
|
32
|
+
|
|
33
|
+
# Execute the middleware.
|
|
34
|
+
#
|
|
35
|
+
# Halts the machine by calling OrbStack CLI stop command,
|
|
36
|
+
# then invalidates the state cache to ensure fresh state queries.
|
|
37
|
+
#
|
|
38
|
+
# @param env [Hash] The environment hash
|
|
39
|
+
# @return [Object] Result from next middleware
|
|
40
|
+
# @raise [CommandExecutionError] If stop command fails
|
|
41
|
+
# @raise [CommandTimeoutError] If stop command times out
|
|
42
|
+
# @api public
|
|
43
|
+
def call(env)
|
|
44
|
+
machine = env[:machine]
|
|
45
|
+
ui = env[:ui]
|
|
46
|
+
|
|
47
|
+
# Validate machine ID exists
|
|
48
|
+
machine_id = validate_machine_id!(machine, 'halt')
|
|
49
|
+
|
|
50
|
+
ui.info("Halting machine '#{machine_id}'...")
|
|
51
|
+
|
|
52
|
+
# Call OrbStack CLI to stop the machine
|
|
53
|
+
Util::OrbStackCLI.stop_machine(machine_id)
|
|
54
|
+
|
|
55
|
+
# Invalidate state cache to ensure fresh state on next query
|
|
56
|
+
machine.provider.invalidate_state_cache
|
|
57
|
+
|
|
58
|
+
# Continue middleware chain
|
|
59
|
+
@app.call(env)
|
|
60
|
+
end
|
|
61
|
+
end
|
|
62
|
+
end
|
|
63
|
+
end
|
|
64
|
+
end
|
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module VagrantPlugins
|
|
4
|
+
module OrbStack
|
|
5
|
+
module Action
|
|
6
|
+
# Machine validation mixin for action middleware.
|
|
7
|
+
#
|
|
8
|
+
# This module provides machine ID validation for **Direct Action Pattern**
|
|
9
|
+
# actions that interact directly with OrbStack CLI. Include this module
|
|
10
|
+
# when your action needs to validate machine existence before performing
|
|
11
|
+
# CLI operations.
|
|
12
|
+
#
|
|
13
|
+
# **IMPORTANT**: Do NOT include this module in Composition Actions that
|
|
14
|
+
# orchestrate other actions. Composition actions delegate validation to
|
|
15
|
+
# their composed actions to avoid redundant checks.
|
|
16
|
+
#
|
|
17
|
+
# ## Usage
|
|
18
|
+
#
|
|
19
|
+
# ### Direct Actions (Include this module)
|
|
20
|
+
# - Create, Halt, Start, Destroy
|
|
21
|
+
# - Any action that directly calls Util::OrbStackCLI
|
|
22
|
+
# - Actions that manage state cache directly
|
|
23
|
+
#
|
|
24
|
+
# ### Composition Actions (Do NOT include)
|
|
25
|
+
# - Reload (composes Halt + Start, which both validate)
|
|
26
|
+
# - Any action that only orchestrates via Action::Builder
|
|
27
|
+
#
|
|
28
|
+
# See docs/DESIGN.md "Action Patterns" for complete pattern documentation.
|
|
29
|
+
#
|
|
30
|
+
# @example Include in a direct action
|
|
31
|
+
# class Destroy
|
|
32
|
+
# include MachineValidation
|
|
33
|
+
#
|
|
34
|
+
# def call(env)
|
|
35
|
+
# machine_id = validate_machine_id!(env[:machine], 'destroy')
|
|
36
|
+
# # ...
|
|
37
|
+
# end
|
|
38
|
+
# end
|
|
39
|
+
#
|
|
40
|
+
# @api public
|
|
41
|
+
module MachineValidation
|
|
42
|
+
# Validate that a machine has a non-nil, non-empty ID.
|
|
43
|
+
#
|
|
44
|
+
# @param machine [Vagrant::Machine] The machine to validate
|
|
45
|
+
# @param action_name [String] The action being performed (for error messages)
|
|
46
|
+
# @return [String] The machine ID if valid
|
|
47
|
+
# @raise [ArgumentError] If machine ID is nil or empty
|
|
48
|
+
# @api public
|
|
49
|
+
def validate_machine_id!(machine, action_name)
|
|
50
|
+
machine_id = machine.id
|
|
51
|
+
if machine_id.nil? || machine_id.empty?
|
|
52
|
+
raise ArgumentError,
|
|
53
|
+
"Cannot #{action_name} machine: machine ID is nil or empty"
|
|
54
|
+
end
|
|
55
|
+
machine_id
|
|
56
|
+
end
|
|
57
|
+
end
|
|
58
|
+
end
|
|
59
|
+
end
|
|
60
|
+
end
|
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module VagrantPlugins
|
|
4
|
+
module OrbStack
|
|
5
|
+
module Action
|
|
6
|
+
# Action middleware for reloading (restarting) OrbStack machines.
|
|
7
|
+
#
|
|
8
|
+
# This is a **Composition Action** that orchestrates Halt + Start actions
|
|
9
|
+
# rather than directly interacting with OrbStack CLI. Following the
|
|
10
|
+
# composition pattern, this class:
|
|
11
|
+
#
|
|
12
|
+
# - Does NOT include MachineValidation (composed actions handle validation)
|
|
13
|
+
# - Does NOT directly call OrbStackCLI (delegates to Halt/Start)
|
|
14
|
+
# - Does NOT manage state cache (composed actions handle invalidation)
|
|
15
|
+
#
|
|
16
|
+
# The actual work is performed by the Action::Builder chain in the
|
|
17
|
+
# provider's action method, which composes: Halt → Start → (optional) Provision
|
|
18
|
+
#
|
|
19
|
+
# This class exists as a pattern consistency marker and for potential future
|
|
20
|
+
# pre/post reload logic that doesn't fit in the composed actions.
|
|
21
|
+
#
|
|
22
|
+
# See docs/DESIGN.md "Action Patterns" section for pattern documentation.
|
|
23
|
+
#
|
|
24
|
+
# @example Usage in action builder
|
|
25
|
+
# Vagrant::Action::Builder.new.tap do |b|
|
|
26
|
+
# b.use VagrantPlugins::OrbStack::Action::Reload
|
|
27
|
+
# end
|
|
28
|
+
#
|
|
29
|
+
# @api public
|
|
30
|
+
class Reload
|
|
31
|
+
# Initialize the middleware.
|
|
32
|
+
#
|
|
33
|
+
# @param app [Object] The next middleware in the chain
|
|
34
|
+
# @param env [Hash] The environment hash containing :machine, :ui, etc.
|
|
35
|
+
# @api public
|
|
36
|
+
def initialize(app, _env)
|
|
37
|
+
@app = app
|
|
38
|
+
end
|
|
39
|
+
|
|
40
|
+
# Execute the middleware.
|
|
41
|
+
#
|
|
42
|
+
# This is a pass-through middleware - the actual reload logic is
|
|
43
|
+
# composed in the provider's action method. This class exists to
|
|
44
|
+
# maintain consistency with the action middleware pattern.
|
|
45
|
+
#
|
|
46
|
+
# @param env [Hash] The environment hash
|
|
47
|
+
# @return [Object] Result from next middleware
|
|
48
|
+
# @api public
|
|
49
|
+
def call(env)
|
|
50
|
+
# Continue middleware chain
|
|
51
|
+
@app.call(env)
|
|
52
|
+
end
|
|
53
|
+
end
|
|
54
|
+
end
|
|
55
|
+
end
|
|
56
|
+
end
|
|
@@ -0,0 +1,70 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require_relative 'machine_validation'
|
|
4
|
+
|
|
5
|
+
module VagrantPlugins
|
|
6
|
+
module OrbStack
|
|
7
|
+
module Action
|
|
8
|
+
# Action middleware for validating SSH readiness before command execution
|
|
9
|
+
#
|
|
10
|
+
# This middleware validates that the machine is in a running state before
|
|
11
|
+
# Vagrant executes SSH commands via `vagrant ssh -c "command"`. It does NOT
|
|
12
|
+
# execute the SSH command itself - Vagrant's built-in SSH layer handles that
|
|
13
|
+
# after this validation middleware approves the operation.
|
|
14
|
+
#
|
|
15
|
+
# Unlike other actions, SSHRun is read-only: it only validates state and does
|
|
16
|
+
# not call OrbStack CLI or modify machine state. This makes it safe to call
|
|
17
|
+
# repeatedly without side effects.
|
|
18
|
+
#
|
|
19
|
+
# @example Usage in action builder
|
|
20
|
+
# Vagrant::Action::Builder.new.tap do |b|
|
|
21
|
+
# b.use VagrantPlugins::OrbStack::Action::SSHRun
|
|
22
|
+
# end
|
|
23
|
+
#
|
|
24
|
+
# @api public
|
|
25
|
+
class SSHRun
|
|
26
|
+
include MachineValidation
|
|
27
|
+
|
|
28
|
+
# Initialize the middleware.
|
|
29
|
+
#
|
|
30
|
+
# @param app [Object] The next middleware in the chain
|
|
31
|
+
# @param env [Hash] The environment hash containing :machine, :ui, etc.
|
|
32
|
+
# @api public
|
|
33
|
+
def initialize(app, _env)
|
|
34
|
+
@app = app
|
|
35
|
+
end
|
|
36
|
+
|
|
37
|
+
# Execute the middleware.
|
|
38
|
+
#
|
|
39
|
+
# Validates that the machine has a valid ID and is in running state.
|
|
40
|
+
# If validation passes, continues the middleware chain for Vagrant to
|
|
41
|
+
# execute the SSH command. If validation fails, raises an error and
|
|
42
|
+
# blocks SSH command execution.
|
|
43
|
+
#
|
|
44
|
+
# This is a read-only operation:
|
|
45
|
+
# - Does NOT call OrbStack CLI (only queries cached state)
|
|
46
|
+
# - Does NOT invalidate state cache (no state changes occur)
|
|
47
|
+
# - Does NOT display UI messages (validation only)
|
|
48
|
+
#
|
|
49
|
+
# @param env [Hash] The environment hash
|
|
50
|
+
# @return [Object] Result from next middleware
|
|
51
|
+
# @raise [ArgumentError] If machine ID is nil or empty (from MachineValidation)
|
|
52
|
+
# @raise [Errors::SSHNotReady] If machine is not in running state
|
|
53
|
+
# @api public
|
|
54
|
+
def call(env)
|
|
55
|
+
machine = env[:machine]
|
|
56
|
+
|
|
57
|
+
# Validate machine ID exists (from MachineValidation)
|
|
58
|
+
validate_machine_id!(machine, 'ssh')
|
|
59
|
+
|
|
60
|
+
# Validate machine is running
|
|
61
|
+
state = machine.provider.state
|
|
62
|
+
raise Errors::SSHNotReady, machine_name: machine.id unless state.id == :running
|
|
63
|
+
|
|
64
|
+
# Continue middleware chain (Vagrant handles SSH execution)
|
|
65
|
+
@app.call(env)
|
|
66
|
+
end
|
|
67
|
+
end
|
|
68
|
+
end
|
|
69
|
+
end
|
|
70
|
+
end
|
|
@@ -0,0 +1,68 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require_relative '../util/orbstack_cli'
|
|
4
|
+
require_relative 'machine_validation'
|
|
5
|
+
|
|
6
|
+
module VagrantPlugins
|
|
7
|
+
module OrbStack
|
|
8
|
+
module Action
|
|
9
|
+
# Action middleware for starting OrbStack machines
|
|
10
|
+
#
|
|
11
|
+
# This middleware handles starting a stopped OrbStack machine.
|
|
12
|
+
# It calls the OrbStack CLI to start the machine and invalidates the
|
|
13
|
+
# provider's state cache to ensure subsequent state queries are fresh.
|
|
14
|
+
#
|
|
15
|
+
# The OrbStack CLI is idempotent - starting an already running machine
|
|
16
|
+
# is safe and will not cause an error.
|
|
17
|
+
#
|
|
18
|
+
# @example Usage in action builder
|
|
19
|
+
# Vagrant::Action::Builder.new.tap do |b|
|
|
20
|
+
# b.use VagrantPlugins::OrbStack::Action::Start
|
|
21
|
+
# end
|
|
22
|
+
#
|
|
23
|
+
# @api public
|
|
24
|
+
class Start
|
|
25
|
+
include MachineValidation
|
|
26
|
+
|
|
27
|
+
# Initialize the middleware.
|
|
28
|
+
#
|
|
29
|
+
# @param app [Object] The next middleware in the chain
|
|
30
|
+
# @param env [Hash] The environment hash containing :machine, :ui, etc.
|
|
31
|
+
# @api public
|
|
32
|
+
def initialize(app, _env)
|
|
33
|
+
@app = app
|
|
34
|
+
end
|
|
35
|
+
|
|
36
|
+
# Execute the middleware.
|
|
37
|
+
#
|
|
38
|
+
# Starts the machine by calling OrbStack CLI start command,
|
|
39
|
+
# then invalidates the state cache to ensure fresh state queries.
|
|
40
|
+
#
|
|
41
|
+
# @param env [Hash] The environment hash
|
|
42
|
+
# @return [Object] Result from next middleware
|
|
43
|
+
# @raise [CommandExecutionError] If start command fails
|
|
44
|
+
# @raise [CommandTimeoutError] If start command times out
|
|
45
|
+
# @raise [OrbStackNotInstalledError] If OrbStack CLI is not available
|
|
46
|
+
# @api public
|
|
47
|
+
def call(env)
|
|
48
|
+
machine = env[:machine]
|
|
49
|
+
ui = env[:ui]
|
|
50
|
+
|
|
51
|
+
# Validate machine ID exists
|
|
52
|
+
machine_id = validate_machine_id!(machine, 'start')
|
|
53
|
+
|
|
54
|
+
ui.info("Starting machine '#{machine_id}'...")
|
|
55
|
+
|
|
56
|
+
# Call OrbStack CLI to start the machine
|
|
57
|
+
Util::OrbStackCLI.start_machine(machine_id)
|
|
58
|
+
|
|
59
|
+
# Invalidate state cache to ensure fresh state on next query
|
|
60
|
+
machine.provider.invalidate_state_cache
|
|
61
|
+
|
|
62
|
+
# Continue middleware chain
|
|
63
|
+
@app.call(env)
|
|
64
|
+
end
|
|
65
|
+
end
|
|
66
|
+
end
|
|
67
|
+
end
|
|
68
|
+
end
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module VagrantPlugins
|
|
4
|
+
module OrbStack
|
|
5
|
+
# Action middleware namespace for OrbStack provider.
|
|
6
|
+
#
|
|
7
|
+
# Contains action classes that implement Vagrant middleware operations
|
|
8
|
+
# for OrbStack machine lifecycle management (create, halt, destroy, etc).
|
|
9
|
+
#
|
|
10
|
+
# @api public
|
|
11
|
+
module Action
|
|
12
|
+
# Action middleware autoload definitions
|
|
13
|
+
autoload :Create, 'vagrant-orbstack/action/create'
|
|
14
|
+
autoload :Destroy, 'vagrant-orbstack/action/destroy'
|
|
15
|
+
autoload :Halt, 'vagrant-orbstack/action/halt'
|
|
16
|
+
autoload :Reload, 'vagrant-orbstack/action/reload'
|
|
17
|
+
autoload :SSHRun, 'vagrant-orbstack/action/ssh_run'
|
|
18
|
+
autoload :Start, 'vagrant-orbstack/action/start'
|
|
19
|
+
end
|
|
20
|
+
end
|
|
21
|
+
end
|