vagrant-ssh-config-manager 0.8.2
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/.bundle/config +2 -0
- data/.gitignore +5 -0
- data/Gemfile +15 -0
- data/README.md +267 -0
- data/Rakefile +22 -0
- data/lib/vagrant-ssh-config-manager/action/destroy.rb +84 -0
- data/lib/vagrant-ssh-config-manager/action/halt.rb +68 -0
- data/lib/vagrant-ssh-config-manager/action/provision.rb +82 -0
- data/lib/vagrant-ssh-config-manager/action/reload.rb +106 -0
- data/lib/vagrant-ssh-config-manager/action/up.rb +99 -0
- data/lib/vagrant-ssh-config-manager/config.rb +194 -0
- data/lib/vagrant-ssh-config-manager/file_locker.rb +140 -0
- data/lib/vagrant-ssh-config-manager/file_manager.rb +245 -0
- data/lib/vagrant-ssh-config-manager/include_manager.rb +251 -0
- data/lib/vagrant-ssh-config-manager/plugin.rb +56 -0
- data/lib/vagrant-ssh-config-manager/ssh_config_manager.rb +2150 -0
- data/lib/vagrant-ssh-config-manager/ssh_info_extractor.rb +443 -0
- data/lib/vagrant-ssh-config-manager/version.rb +5 -0
- data/lib/vagrant-ssh-config-manager.rb +30 -0
- data/vagrant-ssh-config-manager.gemspec +35 -0
- metadata +133 -0
@@ -0,0 +1,443 @@
|
|
1
|
+
module VagrantPlugins
|
2
|
+
module SshConfigManager
|
3
|
+
class SshInfoExtractor
|
4
|
+
def initialize(machine)
|
5
|
+
@machine = machine
|
6
|
+
@logger = Log4r::Logger.new("vagrant::plugins::ssh_config_manager::ssh_info_extractor")
|
7
|
+
end
|
8
|
+
|
9
|
+
# Extract SSH information from Vagrant's internal APIs
|
10
|
+
# This replicates what 'vagrant ssh-config' does but using internal methods
|
11
|
+
def extract_ssh_info
|
12
|
+
begin
|
13
|
+
ssh_info = @machine.ssh_info
|
14
|
+
return nil if ssh_info.nil?
|
15
|
+
|
16
|
+
# Additional validation for SSH info completeness
|
17
|
+
return nil unless valid_ssh_info?(ssh_info)
|
18
|
+
|
19
|
+
# Get the SSH configuration similar to what vagrant ssh-config provides
|
20
|
+
config = build_ssh_config(ssh_info)
|
21
|
+
|
22
|
+
@logger.info("Extracted SSH info for machine: #{@machine.name}") if @logger
|
23
|
+
config
|
24
|
+
rescue Vagrant::Errors::SSHNotReady => e
|
25
|
+
@logger.debug("SSH not ready for machine #{@machine.name}: #{e.message}") if @logger
|
26
|
+
nil
|
27
|
+
rescue Vagrant::Errors::SSHUnavailable => e
|
28
|
+
@logger.debug("SSH unavailable for machine #{@machine.name}: #{e.message}") if @logger
|
29
|
+
nil
|
30
|
+
rescue => e
|
31
|
+
@logger.warn("Failed to extract SSH info for machine #{@machine.name}: #{e.message}") if @logger
|
32
|
+
nil
|
33
|
+
end
|
34
|
+
end
|
35
|
+
|
36
|
+
# Check if the machine supports SSH with comprehensive validation
|
37
|
+
def ssh_capable?
|
38
|
+
# Simplified check - if machine is running and has SSH info, assume SSH is available
|
39
|
+
@machine &&
|
40
|
+
@machine.state &&
|
41
|
+
@machine.state.id == :running &&
|
42
|
+
@machine.ssh_info &&
|
43
|
+
ssh_communicator?
|
44
|
+
rescue => e
|
45
|
+
@logger.debug("SSH capability check failed: #{e.message}") if @logger
|
46
|
+
false
|
47
|
+
end
|
48
|
+
|
49
|
+
# Comprehensive check for SSH support
|
50
|
+
def machine_supports_ssh?
|
51
|
+
# Basic checks
|
52
|
+
return false unless @machine
|
53
|
+
return false unless @machine.communicate_ready?
|
54
|
+
|
55
|
+
# Check if machine state supports SSH
|
56
|
+
return false unless machine_state_supports_ssh?
|
57
|
+
|
58
|
+
# Check if communicator is SSH-based
|
59
|
+
return false unless ssh_communicator?
|
60
|
+
|
61
|
+
# Check if provider supports SSH
|
62
|
+
return false unless provider_supports_ssh?
|
63
|
+
|
64
|
+
true
|
65
|
+
rescue => e
|
66
|
+
@logger.debug("Machine SSH support check failed: #{e.message}")
|
67
|
+
false
|
68
|
+
end
|
69
|
+
|
70
|
+
private
|
71
|
+
|
72
|
+
# Check if machine state allows SSH connections
|
73
|
+
def machine_state_supports_ssh?
|
74
|
+
state = @machine.state
|
75
|
+
return false unless state
|
76
|
+
|
77
|
+
# Common states that support SSH
|
78
|
+
ssh_ready_states = [:running, :up, :active, :created]
|
79
|
+
ssh_ready_states.include?(state.id)
|
80
|
+
rescue
|
81
|
+
false
|
82
|
+
end
|
83
|
+
|
84
|
+
# Check if the communicator is SSH-based
|
85
|
+
def ssh_communicator?
|
86
|
+
communicator = @machine.config.vm.communicator
|
87
|
+
communicator.nil? || communicator == :ssh
|
88
|
+
rescue
|
89
|
+
# Default to assuming SSH if we can't determine
|
90
|
+
true
|
91
|
+
end
|
92
|
+
|
93
|
+
# Check if the provider supports SSH
|
94
|
+
def provider_supports_ssh?
|
95
|
+
provider_name = @machine.provider_name
|
96
|
+
return true unless provider_name
|
97
|
+
|
98
|
+
# List of providers known to support SSH
|
99
|
+
ssh_providers = [
|
100
|
+
:virtualbox, :vmware_desktop, :vmware_fusion, :vmware_workstation,
|
101
|
+
:libvirt, :kvm, :qemu, :parallels, :hyper_v, :lxc, :docker,
|
102
|
+
:aws, :azure, :google, :digitalocean, :linode, :vultr
|
103
|
+
]
|
104
|
+
|
105
|
+
# Providers known to NOT support SSH
|
106
|
+
non_ssh_providers = [:winrm]
|
107
|
+
|
108
|
+
return false if non_ssh_providers.include?(provider_name)
|
109
|
+
|
110
|
+
# If it's a known SSH provider or unknown (assume SSH), return true
|
111
|
+
ssh_providers.include?(provider_name) || !non_ssh_providers.include?(provider_name)
|
112
|
+
rescue
|
113
|
+
# Default to assuming SSH support if we can't determine
|
114
|
+
true
|
115
|
+
end
|
116
|
+
|
117
|
+
# Validate that SSH info contains required fields
|
118
|
+
def valid_ssh_info?(ssh_info)
|
119
|
+
return false if ssh_info.nil?
|
120
|
+
return false if ssh_info[:host].nil? || ssh_info[:host].to_s.strip.empty?
|
121
|
+
return false if ssh_info[:port].nil? || ssh_info[:port].to_i <= 0
|
122
|
+
|
123
|
+
# Username is not strictly required (can default to 'vagrant')
|
124
|
+
# but if present, it shouldn't be empty
|
125
|
+
if ssh_info[:username]
|
126
|
+
return false if ssh_info[:username].to_s.strip.empty?
|
127
|
+
end
|
128
|
+
|
129
|
+
true
|
130
|
+
rescue
|
131
|
+
false
|
132
|
+
end
|
133
|
+
|
134
|
+
# Enhanced SSH info extraction with edge case handling
|
135
|
+
def extract_ssh_info_safe
|
136
|
+
return nil unless machine_supports_ssh?
|
137
|
+
|
138
|
+
retries = 0
|
139
|
+
max_retries = 3
|
140
|
+
retry_delay = 1
|
141
|
+
|
142
|
+
begin
|
143
|
+
ssh_info = @machine.ssh_info
|
144
|
+
return nil unless valid_ssh_info?(ssh_info)
|
145
|
+
|
146
|
+
build_ssh_config(ssh_info)
|
147
|
+
rescue Vagrant::Errors::SSHNotReady => e
|
148
|
+
retries += 1
|
149
|
+
if retries <= max_retries
|
150
|
+
@logger.debug("SSH not ready, retrying in #{retry_delay}s (attempt #{retries}/#{max_retries})")
|
151
|
+
sleep(retry_delay)
|
152
|
+
retry_delay *= 2 # Exponential backoff
|
153
|
+
retry
|
154
|
+
else
|
155
|
+
@logger.debug("SSH still not ready after #{max_retries} attempts")
|
156
|
+
nil
|
157
|
+
end
|
158
|
+
rescue => e
|
159
|
+
@logger.warn("SSH info extraction failed: #{e.message}")
|
160
|
+
nil
|
161
|
+
end
|
162
|
+
end
|
163
|
+
|
164
|
+
# Safe host name generation with fallbacks
|
165
|
+
def generate_host_name
|
166
|
+
begin
|
167
|
+
# Generate a unique host name based on project directory and machine name
|
168
|
+
project_name = File.basename(@machine.env.root_path)
|
169
|
+
machine_name = @machine.name.to_s
|
170
|
+
|
171
|
+
# Sanitize names for SSH config
|
172
|
+
project_name = sanitize_name(project_name)
|
173
|
+
machine_name = sanitize_name(machine_name)
|
174
|
+
|
175
|
+
host_name = "#{project_name}-#{machine_name}"
|
176
|
+
|
177
|
+
# Ensure the host name is not empty after sanitization
|
178
|
+
if host_name.strip.empty? || host_name == '-'
|
179
|
+
host_name = "vagrant-#{@machine.id || 'unknown'}"
|
180
|
+
end
|
181
|
+
|
182
|
+
host_name
|
183
|
+
rescue => e
|
184
|
+
@logger.debug("Host name generation failed: #{e.message}")
|
185
|
+
"vagrant-#{@machine.name || 'unknown'}"
|
186
|
+
end
|
187
|
+
end
|
188
|
+
|
189
|
+
# Normalize SSH config data to ensure consistency
|
190
|
+
def normalize_ssh_config(config)
|
191
|
+
normalized = {}
|
192
|
+
|
193
|
+
config.each do |key, value|
|
194
|
+
# Normalize key names to proper SSH config format
|
195
|
+
normalized_key = normalize_config_key(key)
|
196
|
+
normalized_value = normalize_config_value(key, value)
|
197
|
+
|
198
|
+
normalized[normalized_key] = normalized_value if normalized_value
|
199
|
+
end
|
200
|
+
|
201
|
+
normalized
|
202
|
+
end
|
203
|
+
|
204
|
+
# Parse SSH config entries back from file format
|
205
|
+
def parse_ssh_config_entry(entry_text)
|
206
|
+
config = {}
|
207
|
+
current_host = nil
|
208
|
+
|
209
|
+
entry_text.split("\n").each do |line|
|
210
|
+
line = line.strip
|
211
|
+
next if line.empty? || line.start_with?('#')
|
212
|
+
|
213
|
+
if line.start_with?('Host ')
|
214
|
+
current_host = line.sub(/^Host\s+/, '').strip
|
215
|
+
config['Host'] = current_host
|
216
|
+
elsif current_host && line.include?(' ')
|
217
|
+
key, value = line.split(' ', 2)
|
218
|
+
config[key.strip] = value.strip
|
219
|
+
end
|
220
|
+
end
|
221
|
+
|
222
|
+
config
|
223
|
+
end
|
224
|
+
|
225
|
+
# Convert normalized config back to SSH config file format
|
226
|
+
def to_ssh_config_format(config)
|
227
|
+
lines = []
|
228
|
+
|
229
|
+
# Host entry always comes first
|
230
|
+
if config['Host']
|
231
|
+
lines << "Host #{config['Host']}"
|
232
|
+
|
233
|
+
# Add other entries in a logical order
|
234
|
+
ssh_option_order = %w[
|
235
|
+
HostName User Port IdentityFile IdentitiesOnly
|
236
|
+
StrictHostKeyChecking UserKnownHostsFile PasswordAuthentication
|
237
|
+
LogLevel ProxyCommand Compression CompressionLevel
|
238
|
+
ConnectTimeout ForwardAgent ForwardX11
|
239
|
+
]
|
240
|
+
|
241
|
+
ssh_option_order.each do |key|
|
242
|
+
if config[key]
|
243
|
+
lines << " #{key} #{config[key]}"
|
244
|
+
end
|
245
|
+
end
|
246
|
+
|
247
|
+
# Add any remaining options not in the predefined order
|
248
|
+
config.each do |key, value|
|
249
|
+
next if key == 'Host' || ssh_option_order.include?(key)
|
250
|
+
lines << " #{key} #{value}"
|
251
|
+
end
|
252
|
+
end
|
253
|
+
|
254
|
+
lines.join("\n")
|
255
|
+
end
|
256
|
+
|
257
|
+
private
|
258
|
+
|
259
|
+
def build_ssh_config(ssh_info)
|
260
|
+
config = {
|
261
|
+
'Host' => generate_host_name,
|
262
|
+
'HostName' => ssh_info[:host],
|
263
|
+
'User' => ssh_info[:username] || 'vagrant',
|
264
|
+
'Port' => ssh_info[:port] || 22
|
265
|
+
}
|
266
|
+
|
267
|
+
# Add SSH key information
|
268
|
+
if ssh_info[:private_key_path] && !ssh_info[:private_key_path].empty?
|
269
|
+
# Use the first private key if multiple are provided
|
270
|
+
key_path = ssh_info[:private_key_path].is_a?(Array) ?
|
271
|
+
ssh_info[:private_key_path].first :
|
272
|
+
ssh_info[:private_key_path]
|
273
|
+
config['IdentityFile'] = key_path
|
274
|
+
config['IdentitiesOnly'] = 'yes'
|
275
|
+
end
|
276
|
+
|
277
|
+
# Add common SSH options for Vagrant VMs
|
278
|
+
config['StrictHostKeyChecking'] = 'no'
|
279
|
+
config['UserKnownHostsFile'] = '/dev/null'
|
280
|
+
config['PasswordAuthentication'] = 'no'
|
281
|
+
config['LogLevel'] = 'FATAL'
|
282
|
+
|
283
|
+
# Add proxy command if using a proxy
|
284
|
+
if ssh_info[:proxy_command]
|
285
|
+
config['ProxyCommand'] = ssh_info[:proxy_command]
|
286
|
+
end
|
287
|
+
|
288
|
+
# Add comprehensive SSH options from the machine config
|
289
|
+
add_comprehensive_ssh_options(config, ssh_info)
|
290
|
+
|
291
|
+
# Normalize the final configuration
|
292
|
+
normalize_ssh_config(config)
|
293
|
+
end
|
294
|
+
|
295
|
+
# Add comprehensive SSH options support
|
296
|
+
def add_comprehensive_ssh_options(config, ssh_info)
|
297
|
+
return unless ssh_info[:config] && ssh_info[:config].ssh
|
298
|
+
|
299
|
+
ssh_config = ssh_info[:config].ssh
|
300
|
+
|
301
|
+
# Connection options
|
302
|
+
config['Compression'] = ssh_config.compression ? 'yes' : 'no' if ssh_config.compression != nil
|
303
|
+
config['CompressionLevel'] = ssh_config.compression_level.to_s if ssh_config.compression_level
|
304
|
+
config['ConnectTimeout'] = ssh_config.connect_timeout.to_s if ssh_config.connect_timeout
|
305
|
+
config['ConnectionAttempts'] = ssh_config.connection_attempts.to_s if ssh_config.connection_attempts
|
306
|
+
config['ServerAliveInterval'] = ssh_config.server_alive_interval.to_s if ssh_config.server_alive_interval
|
307
|
+
config['ServerAliveCountMax'] = ssh_config.server_alive_count_max.to_s if ssh_config.server_alive_count_max
|
308
|
+
|
309
|
+
# Authentication options
|
310
|
+
config['ForwardAgent'] = ssh_config.forward_agent ? 'yes' : 'no' if ssh_config.forward_agent != nil
|
311
|
+
config['PubkeyAuthentication'] = ssh_config.pubkey_authentication ? 'yes' : 'no' if ssh_config.pubkey_authentication != nil
|
312
|
+
config['PreferredAuthentications'] = ssh_config.preferred_authentications if ssh_config.preferred_authentications
|
313
|
+
|
314
|
+
# Forwarding options
|
315
|
+
config['ForwardX11'] = ssh_config.forward_x11 ? 'yes' : 'no' if ssh_config.forward_x11 != nil
|
316
|
+
config['ForwardX11Trusted'] = ssh_config.forward_x11_trusted ? 'yes' : 'no' if ssh_config.forward_x11_trusted != nil
|
317
|
+
|
318
|
+
# Security options
|
319
|
+
config['StrictHostKeyChecking'] = ssh_config.verify_host_key ? 'yes' : 'no' if ssh_config.verify_host_key != nil
|
320
|
+
config['CheckHostIP'] = ssh_config.check_host_ip ? 'yes' : 'no' if ssh_config.check_host_ip != nil
|
321
|
+
|
322
|
+
# Protocol options
|
323
|
+
config['Protocol'] = ssh_config.protocol if ssh_config.protocol
|
324
|
+
config['Ciphers'] = ssh_config.ciphers.join(',') if ssh_config.ciphers && !ssh_config.ciphers.empty?
|
325
|
+
config['MACs'] = ssh_config.macs.join(',') if ssh_config.macs && !ssh_config.macs.empty?
|
326
|
+
config['KexAlgorithms'] = ssh_config.kex_algorithms.join(',') if ssh_config.kex_algorithms && !ssh_config.kex_algorithms.empty?
|
327
|
+
|
328
|
+
# Terminal options
|
329
|
+
config['RequestTTY'] = ssh_config.pty ? 'yes' : 'no' if ssh_config.pty != nil
|
330
|
+
config['RemoteCommand'] = ssh_config.remote_command if ssh_config.remote_command
|
331
|
+
|
332
|
+
# File and directory options
|
333
|
+
config['ControlMaster'] = ssh_config.control_master if ssh_config.control_master
|
334
|
+
config['ControlPath'] = ssh_config.control_path if ssh_config.control_path
|
335
|
+
config['ControlPersist'] = ssh_config.control_persist.to_s if ssh_config.control_persist
|
336
|
+
|
337
|
+
# Logging options
|
338
|
+
config['LogLevel'] = ssh_config.log_level.to_s.upcase if ssh_config.log_level
|
339
|
+
config['SyslogFacility'] = ssh_config.syslog_facility if ssh_config.syslog_facility
|
340
|
+
|
341
|
+
# Banner and environment
|
342
|
+
config['Banner'] = ssh_config.banner if ssh_config.banner
|
343
|
+
config['SendEnv'] = ssh_config.send_env.join(' ') if ssh_config.send_env && !ssh_config.send_env.empty?
|
344
|
+
config['SetEnv'] = ssh_config.set_env.map { |k, v| "#{k}=#{v}" }.join(' ') if ssh_config.set_env && !ssh_config.set_env.empty?
|
345
|
+
|
346
|
+
# Keep alive options
|
347
|
+
config['TCPKeepAlive'] = ssh_config.tcp_keep_alive ? 'yes' : 'no' if ssh_config.tcp_keep_alive != nil
|
348
|
+
|
349
|
+
# Escape character
|
350
|
+
config['EscapeChar'] = ssh_config.escape_char if ssh_config.escape_char
|
351
|
+
|
352
|
+
# Gateway options
|
353
|
+
config['ProxyJump'] = ssh_config.proxy_jump if ssh_config.proxy_jump
|
354
|
+
|
355
|
+
# Add any custom options that might be defined
|
356
|
+
if ssh_config.respond_to?(:extra_options) && ssh_config.extra_options
|
357
|
+
ssh_config.extra_options.each do |key, value|
|
358
|
+
config[key.to_s] = value.to_s
|
359
|
+
end
|
360
|
+
end
|
361
|
+
end
|
362
|
+
|
363
|
+
def generate_host_name
|
364
|
+
# Generate a unique host name based on project directory and machine name
|
365
|
+
project_name = File.basename(@machine.env.root_path)
|
366
|
+
machine_name = @machine.name.to_s
|
367
|
+
|
368
|
+
# Sanitize names for SSH config
|
369
|
+
project_name = sanitize_name(project_name)
|
370
|
+
machine_name = sanitize_name(machine_name)
|
371
|
+
|
372
|
+
"#{project_name}-#{machine_name}"
|
373
|
+
end
|
374
|
+
|
375
|
+
def sanitize_name(name)
|
376
|
+
# Remove or replace characters that might cause issues in SSH config
|
377
|
+
name.gsub(/[^a-zA-Z0-9\-_]/, '-').gsub(/-+/, '-').gsub(/^-|-$/, '')
|
378
|
+
end
|
379
|
+
|
380
|
+
def normalize_config_key(key)
|
381
|
+
# Convert various key formats to standard SSH config format
|
382
|
+
key_mappings = {
|
383
|
+
'hostname' => 'HostName',
|
384
|
+
'host_name' => 'HostName',
|
385
|
+
'user' => 'User',
|
386
|
+
'username' => 'User',
|
387
|
+
'port' => 'Port',
|
388
|
+
'identity_file' => 'IdentityFile',
|
389
|
+
'identityfile' => 'IdentityFile',
|
390
|
+
'identities_only' => 'IdentitiesOnly',
|
391
|
+
'identitiesonly' => 'IdentitiesOnly',
|
392
|
+
'strict_host_key_checking' => 'StrictHostKeyChecking',
|
393
|
+
'stricthostkeychecking' => 'StrictHostKeyChecking',
|
394
|
+
'user_known_hosts_file' => 'UserKnownHostsFile',
|
395
|
+
'userknownhostsfile' => 'UserKnownHostsFile',
|
396
|
+
'password_authentication' => 'PasswordAuthentication',
|
397
|
+
'passwordauthentication' => 'PasswordAuthentication',
|
398
|
+
'log_level' => 'LogLevel',
|
399
|
+
'loglevel' => 'LogLevel',
|
400
|
+
'proxy_command' => 'ProxyCommand',
|
401
|
+
'proxycommand' => 'ProxyCommand',
|
402
|
+
'compression' => 'Compression',
|
403
|
+
'compression_level' => 'CompressionLevel',
|
404
|
+
'compressionlevel' => 'CompressionLevel',
|
405
|
+
'connect_timeout' => 'ConnectTimeout',
|
406
|
+
'connecttimeout' => 'ConnectTimeout',
|
407
|
+
'forward_agent' => 'ForwardAgent',
|
408
|
+
'forwardagent' => 'ForwardAgent',
|
409
|
+
'forward_x11' => 'ForwardX11',
|
410
|
+
'forwardx11' => 'ForwardX11'
|
411
|
+
}
|
412
|
+
|
413
|
+
key_str = key.to_s.downcase
|
414
|
+
key_mappings[key_str] || key.to_s
|
415
|
+
end
|
416
|
+
|
417
|
+
def normalize_config_value(key, value)
|
418
|
+
return nil if value.nil? || value.to_s.strip.empty?
|
419
|
+
|
420
|
+
case key.to_s.downcase
|
421
|
+
when 'port'
|
422
|
+
value.to_i.to_s
|
423
|
+
when 'compression', 'identitiesonly', 'stricthostkeychecking',
|
424
|
+
'passwordauthentication', 'forwardagent', 'forwardx11'
|
425
|
+
# Normalize boolean-like values
|
426
|
+
case value.to_s.downcase
|
427
|
+
when 'true', 'yes', '1'
|
428
|
+
'yes'
|
429
|
+
when 'false', 'no', '0'
|
430
|
+
'no'
|
431
|
+
else
|
432
|
+
value.to_s
|
433
|
+
end
|
434
|
+
when 'identityfile'
|
435
|
+
# Expand tilde in file paths
|
436
|
+
File.expand_path(value.to_s)
|
437
|
+
else
|
438
|
+
value.to_s.strip
|
439
|
+
end
|
440
|
+
end
|
441
|
+
end
|
442
|
+
end
|
443
|
+
end
|
@@ -0,0 +1,30 @@
|
|
1
|
+
require "vagrant-ssh-config-manager/version"
|
2
|
+
require "vagrant-ssh-config-manager/plugin"
|
3
|
+
|
4
|
+
module VagrantPlugins
|
5
|
+
module SshConfigManager
|
6
|
+
# Main plugin entry point
|
7
|
+
# This file is loaded when the plugin is activated
|
8
|
+
|
9
|
+
# Lazy load other components only when needed
|
10
|
+
def self.require_file_manager
|
11
|
+
require "vagrant-ssh-config-manager/file_manager" unless defined?(FileManager)
|
12
|
+
end
|
13
|
+
|
14
|
+
def self.require_include_manager
|
15
|
+
require "vagrant-ssh-config-manager/include_manager" unless defined?(IncludeManager)
|
16
|
+
end
|
17
|
+
|
18
|
+
def self.require_ssh_info_extractor
|
19
|
+
require "vagrant-ssh-config-manager/ssh_info_extractor" unless defined?(SshInfoExtractor)
|
20
|
+
end
|
21
|
+
|
22
|
+
def self.require_file_locker
|
23
|
+
require "vagrant-ssh-config-manager/file_locker" unless defined?(FileLocker)
|
24
|
+
end
|
25
|
+
|
26
|
+
def self.require_config
|
27
|
+
require "vagrant-ssh-config-manager/config" unless defined?(Config)
|
28
|
+
end
|
29
|
+
end
|
30
|
+
end
|
@@ -0,0 +1,35 @@
|
|
1
|
+
lib = File.expand_path("../lib", __FILE__)
|
2
|
+
$LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
|
3
|
+
require "vagrant-ssh-config-manager/version"
|
4
|
+
|
5
|
+
Gem::Specification.new do |spec|
|
6
|
+
spec.name = "vagrant-ssh-config-manager"
|
7
|
+
spec.version = VagrantPlugins::SshConfigManager::VERSION
|
8
|
+
spec.authors = ["Marek Ruzicka"]
|
9
|
+
spec.email = ["marek.ruzicka@glide.sk"]
|
10
|
+
|
11
|
+
spec.summary = "Vagrant plugin that automatically manages SSH configurations"
|
12
|
+
spec.description = "A Vagrant plugin that automatically manages SSH configurations. Creates and maintains SSH config entries when VMs are started and cleans them up when VMs are destroyed, with environment isolation and file locking support."
|
13
|
+
spec.homepage = "https://github.com/marekruzicka/vagrant-ssh-config-manager.git"
|
14
|
+
spec.license = "MIT"
|
15
|
+
|
16
|
+
# Specify which files should be added to the gem when it is released.
|
17
|
+
# The `git ls-files -z` loads the files in the RubyGems gemspec, but not Git submodules.
|
18
|
+
spec.files = Dir.chdir(File.expand_path('..', __FILE__)) do
|
19
|
+
`git ls-files -z`.split("\x0").reject { |f| f.match(%r{^(test|spec|features|tasks|.github|.ai)/}) }
|
20
|
+
end
|
21
|
+
spec.require_paths = ["lib"]
|
22
|
+
|
23
|
+
# Development dependencies
|
24
|
+
spec.add_development_dependency "bundler", "~> 2.0"
|
25
|
+
spec.add_development_dependency "rake", "~> 13.0"
|
26
|
+
spec.add_development_dependency "rspec", "~> 3.0"
|
27
|
+
spec.add_development_dependency "rspec-mocks", "~> 3.0"
|
28
|
+
spec.add_development_dependency "simplecov", "~> 0.21"
|
29
|
+
|
30
|
+
# Ensure compatibility with supported Ruby versions
|
31
|
+
spec.required_ruby_version = ">= 2.6.0"
|
32
|
+
|
33
|
+
# Plugin metadata for Vagrant
|
34
|
+
spec.metadata["vagrant_plugin"] = "true"
|
35
|
+
end
|
metadata
ADDED
@@ -0,0 +1,133 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: vagrant-ssh-config-manager
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 0.8.2
|
5
|
+
platform: ruby
|
6
|
+
authors:
|
7
|
+
- Marek Ruzicka
|
8
|
+
bindir: bin
|
9
|
+
cert_chain: []
|
10
|
+
date: 1980-01-02 00:00:00.000000000 Z
|
11
|
+
dependencies:
|
12
|
+
- !ruby/object:Gem::Dependency
|
13
|
+
name: bundler
|
14
|
+
requirement: !ruby/object:Gem::Requirement
|
15
|
+
requirements:
|
16
|
+
- - "~>"
|
17
|
+
- !ruby/object:Gem::Version
|
18
|
+
version: '2.0'
|
19
|
+
type: :development
|
20
|
+
prerelease: false
|
21
|
+
version_requirements: !ruby/object:Gem::Requirement
|
22
|
+
requirements:
|
23
|
+
- - "~>"
|
24
|
+
- !ruby/object:Gem::Version
|
25
|
+
version: '2.0'
|
26
|
+
- !ruby/object:Gem::Dependency
|
27
|
+
name: rake
|
28
|
+
requirement: !ruby/object:Gem::Requirement
|
29
|
+
requirements:
|
30
|
+
- - "~>"
|
31
|
+
- !ruby/object:Gem::Version
|
32
|
+
version: '13.0'
|
33
|
+
type: :development
|
34
|
+
prerelease: false
|
35
|
+
version_requirements: !ruby/object:Gem::Requirement
|
36
|
+
requirements:
|
37
|
+
- - "~>"
|
38
|
+
- !ruby/object:Gem::Version
|
39
|
+
version: '13.0'
|
40
|
+
- !ruby/object:Gem::Dependency
|
41
|
+
name: rspec
|
42
|
+
requirement: !ruby/object:Gem::Requirement
|
43
|
+
requirements:
|
44
|
+
- - "~>"
|
45
|
+
- !ruby/object:Gem::Version
|
46
|
+
version: '3.0'
|
47
|
+
type: :development
|
48
|
+
prerelease: false
|
49
|
+
version_requirements: !ruby/object:Gem::Requirement
|
50
|
+
requirements:
|
51
|
+
- - "~>"
|
52
|
+
- !ruby/object:Gem::Version
|
53
|
+
version: '3.0'
|
54
|
+
- !ruby/object:Gem::Dependency
|
55
|
+
name: rspec-mocks
|
56
|
+
requirement: !ruby/object:Gem::Requirement
|
57
|
+
requirements:
|
58
|
+
- - "~>"
|
59
|
+
- !ruby/object:Gem::Version
|
60
|
+
version: '3.0'
|
61
|
+
type: :development
|
62
|
+
prerelease: false
|
63
|
+
version_requirements: !ruby/object:Gem::Requirement
|
64
|
+
requirements:
|
65
|
+
- - "~>"
|
66
|
+
- !ruby/object:Gem::Version
|
67
|
+
version: '3.0'
|
68
|
+
- !ruby/object:Gem::Dependency
|
69
|
+
name: simplecov
|
70
|
+
requirement: !ruby/object:Gem::Requirement
|
71
|
+
requirements:
|
72
|
+
- - "~>"
|
73
|
+
- !ruby/object:Gem::Version
|
74
|
+
version: '0.21'
|
75
|
+
type: :development
|
76
|
+
prerelease: false
|
77
|
+
version_requirements: !ruby/object:Gem::Requirement
|
78
|
+
requirements:
|
79
|
+
- - "~>"
|
80
|
+
- !ruby/object:Gem::Version
|
81
|
+
version: '0.21'
|
82
|
+
description: A Vagrant plugin that automatically manages SSH configurations. Creates
|
83
|
+
and maintains SSH config entries when VMs are started and cleans them up when VMs
|
84
|
+
are destroyed, with environment isolation and file locking support.
|
85
|
+
email:
|
86
|
+
- marek.ruzicka@glide.sk
|
87
|
+
executables: []
|
88
|
+
extensions: []
|
89
|
+
extra_rdoc_files: []
|
90
|
+
files:
|
91
|
+
- ".bundle/config"
|
92
|
+
- ".gitignore"
|
93
|
+
- Gemfile
|
94
|
+
- README.md
|
95
|
+
- Rakefile
|
96
|
+
- lib/vagrant-ssh-config-manager.rb
|
97
|
+
- lib/vagrant-ssh-config-manager/action/destroy.rb
|
98
|
+
- lib/vagrant-ssh-config-manager/action/halt.rb
|
99
|
+
- lib/vagrant-ssh-config-manager/action/provision.rb
|
100
|
+
- lib/vagrant-ssh-config-manager/action/reload.rb
|
101
|
+
- lib/vagrant-ssh-config-manager/action/up.rb
|
102
|
+
- lib/vagrant-ssh-config-manager/config.rb
|
103
|
+
- lib/vagrant-ssh-config-manager/file_locker.rb
|
104
|
+
- lib/vagrant-ssh-config-manager/file_manager.rb
|
105
|
+
- lib/vagrant-ssh-config-manager/include_manager.rb
|
106
|
+
- lib/vagrant-ssh-config-manager/plugin.rb
|
107
|
+
- lib/vagrant-ssh-config-manager/ssh_config_manager.rb
|
108
|
+
- lib/vagrant-ssh-config-manager/ssh_info_extractor.rb
|
109
|
+
- lib/vagrant-ssh-config-manager/version.rb
|
110
|
+
- vagrant-ssh-config-manager.gemspec
|
111
|
+
homepage: https://github.com/marekruzicka/vagrant-ssh-config-manager.git
|
112
|
+
licenses:
|
113
|
+
- MIT
|
114
|
+
metadata:
|
115
|
+
vagrant_plugin: 'true'
|
116
|
+
rdoc_options: []
|
117
|
+
require_paths:
|
118
|
+
- lib
|
119
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
120
|
+
requirements:
|
121
|
+
- - ">="
|
122
|
+
- !ruby/object:Gem::Version
|
123
|
+
version: 2.6.0
|
124
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
125
|
+
requirements:
|
126
|
+
- - ">="
|
127
|
+
- !ruby/object:Gem::Version
|
128
|
+
version: '0'
|
129
|
+
requirements: []
|
130
|
+
rubygems_version: 3.6.7
|
131
|
+
specification_version: 4
|
132
|
+
summary: Vagrant plugin that automatically manages SSH configurations
|
133
|
+
test_files: []
|