vagrant-ssh-config-manager 0.8.3 → 1.0.0.alpha

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.
Files changed (36) hide show
  1. checksums.yaml +4 -4
  2. data/.bundle/config +1 -1
  3. data/.gitignore +2 -1
  4. data/.rspec +2 -0
  5. data/.rubocop.yml +62 -0
  6. data/Gemfile +11 -9
  7. data/README.md +2 -2
  8. data/Rakefile +10 -8
  9. data/TESTING.md +82 -0
  10. data/lib/vagrant/ssh/config/manager.rb +5 -0
  11. data/lib/vagrant_ssh_config_manager/action/destroy.rb +82 -0
  12. data/lib/vagrant_ssh_config_manager/action/halt.rb +66 -0
  13. data/lib/vagrant_ssh_config_manager/action/provision.rb +81 -0
  14. data/lib/vagrant_ssh_config_manager/action/reload.rb +105 -0
  15. data/lib/vagrant_ssh_config_manager/action/up.rb +98 -0
  16. data/lib/{vagrant-ssh-config-manager → vagrant_ssh_config_manager}/config.rb +45 -49
  17. data/lib/{vagrant-ssh-config-manager → vagrant_ssh_config_manager}/file_locker.rb +35 -37
  18. data/lib/{vagrant-ssh-config-manager → vagrant_ssh_config_manager}/file_manager.rb +90 -80
  19. data/lib/{vagrant-ssh-config-manager → vagrant_ssh_config_manager}/include_manager.rb +54 -53
  20. data/lib/{vagrant-ssh-config-manager → vagrant_ssh_config_manager}/plugin.rb +15 -13
  21. data/lib/vagrant_ssh_config_manager/ssh_config_manager.rb +1152 -0
  22. data/lib/{vagrant-ssh-config-manager → vagrant_ssh_config_manager}/ssh_info_extractor.rb +129 -141
  23. data/lib/vagrant_ssh_config_manager/version.rb +7 -0
  24. data/lib/{vagrant-ssh-config-manager.rb → vagrant_ssh_config_manager.rb} +15 -12
  25. data/test-all.sh +11 -0
  26. data/test-integration.sh +4 -0
  27. data/test-unit.sh +4 -0
  28. data/vagrant-ssh-config-manager.gemspec +25 -21
  29. metadata +28 -18
  30. data/lib/vagrant-ssh-config-manager/action/destroy.rb +0 -84
  31. data/lib/vagrant-ssh-config-manager/action/halt.rb +0 -68
  32. data/lib/vagrant-ssh-config-manager/action/provision.rb +0 -82
  33. data/lib/vagrant-ssh-config-manager/action/reload.rb +0 -106
  34. data/lib/vagrant-ssh-config-manager/action/up.rb +0 -99
  35. data/lib/vagrant-ssh-config-manager/ssh_config_manager.rb +0 -2150
  36. data/lib/vagrant-ssh-config-manager/version.rb +0 -5
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 9054395c57d03b3fe49dbdc01f03067acd35bdb3c80ca95061d69934f9f1a3d8
4
- data.tar.gz: c317e4cfe81e2b7855df4dd07bcf059f3ceeec715f1ec38ae719d54072b41bff
3
+ metadata.gz: 47f9a813e321f5ad9c85a343e9caf8d1fff7c220e91e5276f54ce306677d0264
4
+ data.tar.gz: 92261bf77ca112add8fe96b3c35c613ea7bc461022008b75a9aff879c5a146a8
5
5
  SHA512:
6
- metadata.gz: 3c69529ffed4b61b365575ea91e0d22b178b1231b2b199cb59964c0324f2ba1c6391bfcd78335ebc78c16a9e0578f11a8ca2479295d2555812eeedbd6b25b946
7
- data.tar.gz: d042e9352db40cdc22d683b3b52a6c46b12f1adea06469f5b9fff97fc24276787554fa73c0e4ae48ed5d60d764a74dbdf6becfe8a7c999c3abbf35921926fe1c
6
+ metadata.gz: 6807f6309de0a625b8773a98c696a9e40e6b74465a009f1b2fa57d355cfc6007cab440ff468e98590fa84e6aaca5bc3bce3dc65ad9219e432620370640dfa60a
7
+ data.tar.gz: bb1dc4515944ae88da5a621a8e48d87ca16c9ef60c5a2ee717f3c6eaef64bbc6dded204d90d006d79808473a02d4971cfbe380e67d2d81e7fbbcbc6d5d441295
data/.bundle/config CHANGED
@@ -1,2 +1,2 @@
1
1
  ---
2
- BUNDLE_PATH: "vendor/bundle"
2
+ BUNDLE_PATH: "/home/runner/work/vagrant-ssh-config-manager/vagrant-ssh-config-manager/vendor/bundle"
data/.gitignore CHANGED
@@ -2,4 +2,5 @@ vendor
2
2
  .github/instructions
3
3
  test_file
4
4
  vagrant-ssh-config-manager-*.gem
5
- Gemfile.lock
5
+ Gemfile.lock
6
+ .vagrant
data/.rspec ADDED
@@ -0,0 +1,2 @@
1
+ --format progress
2
+ --color
data/.rubocop.yml ADDED
@@ -0,0 +1,62 @@
1
+ # Custom RuboCop configuration for vagrant-ssh-config-manager
2
+
3
+ AllCops:
4
+ NewCops: enable
5
+
6
+ # Disable metrics and complexity cops for large methods/classes
7
+ Metrics/BlockLength:
8
+ Enabled: false
9
+ Metrics/MethodLength:
10
+ Enabled: false
11
+ Metrics/AbcSize:
12
+ Enabled: false
13
+ Metrics/CyclomaticComplexity:
14
+ Enabled: false
15
+ Metrics/PerceivedComplexity:
16
+ Enabled: false
17
+ Metrics/ClassLength:
18
+ Enabled: false
19
+ Metrics/BlockNesting:
20
+ Enabled: false
21
+ Layout/LineLength:
22
+ Enabled: false
23
+ Max: 120
24
+
25
+ # Disable accessor naming cop for spec files (get_ prefix methods allowed in tests)
26
+ Naming/AccessorMethodName:
27
+ Enabled: true
28
+ Exclude:
29
+ - 'spec/**/*'
30
+
31
+ # Require documentation comments on public classes/modules
32
+ Style/Documentation:
33
+ Enabled: false
34
+
35
+ # Ensure super is called in initializers
36
+ Lint/MissingSuper:
37
+ Enabled: true
38
+
39
+ # Disable single-line modifier offenses for now
40
+ Style/IfUnlessModifier:
41
+ Enabled: false
42
+
43
+ # Disable constant scoping and access modifier false positives
44
+ Lint/UselessConstantScoping:
45
+ Enabled: false
46
+ Lint/UselessAccessModifier:
47
+ Enabled: false
48
+
49
+ # Disable staffing of gemspec checks
50
+ Gemspec/RequireMFA:
51
+ Enabled: false
52
+ Gemspec/DevelopmentDependencies:
53
+ Enabled: false
54
+
55
+ # Spec-specific configurations - allow more flexible patterns in tests
56
+ Lint/NoReturnInBeginEndBlocks:
57
+ Exclude:
58
+ - 'spec/**/*'
59
+
60
+ Naming/PredicateMethod:
61
+ Exclude:
62
+ - 'spec/**/*'
data/Gemfile CHANGED
@@ -1,15 +1,17 @@
1
- source "https://rubygems.org"
1
+ # frozen_string_literal: true
2
+
3
+ source 'https://rubygems.org'
2
4
 
3
5
  # Specify your gem's dependencies in vagrant-ssh-config-manager.gemspec
4
6
  gemspec
5
7
 
6
8
  group :development, :test do
7
- gem "bundler", "~> 2.0"
8
- gem "rake", "~> 13.0"
9
- gem "rspec", "~> 3.0"
10
- gem "rspec-mocks", "~> 3.0"
11
- gem "simplecov", "~> 0.21"
12
- gem "rubocop", "~> 1.0"
13
- gem "rubocop-rspec", "~> 2.0"
14
- gem "vagrant", git: "https://github.com/hashicorp/vagrant.git", tag: "v2.4.1"
9
+ gem 'bundler', '~> 2.0'
10
+ gem 'rake', '~> 13.0'
11
+ gem 'rspec', '~> 3.0'
12
+ gem 'rspec-mocks', '~> 3.0'
13
+ gem 'rubocop', '~> 1.0'
14
+ gem 'rubocop-rspec', '~> 2.0'
15
+ gem 'simplecov', '~> 0.21'
16
+ gem 'vagrant', git: 'https://github.com/hashicorp/vagrant.git', tag: 'v2.4.1'
15
17
  end
data/README.md CHANGED
@@ -25,7 +25,7 @@ vagrant plugin install vagrant-ssh-config-manager
25
25
  ### Install from Source
26
26
 
27
27
  ```bash
28
- git clone https://github.com/your-username/vagrant-ssh-config-manager.git
28
+ git clone https://github.com/marekruzicka/vagrant-ssh-config-manager.git
29
29
  cd vagrant-ssh-config-manager
30
30
  gem build vagrant-ssh-config-manager.gemspec
31
31
  vagrant plugin install vagrant-ssh-config-manager-*.gem
@@ -258,7 +258,7 @@ sed -i '/# BEGIN vagrant-ssh-config-manager/,/# END vagrant-ssh-config-manager/d
258
258
  ## Compatibility
259
259
 
260
260
  - **Vagrant**: 2.0+
261
- - **Ruby**: 2.4+
261
+ - **Ruby**: 3.0+
262
262
  - **Platforms**: Linux, macOS, Windows (with WSL)
263
263
  - **Providers**: VirtualBox, VMware, Libvirt, Hyper-V, Docker, AWS, and others
264
264
 
data/Rakefile CHANGED
@@ -1,21 +1,23 @@
1
- require "bundler/gem_tasks"
2
- require "rspec/core/rake_task"
1
+ # frozen_string_literal: true
2
+
3
+ require 'bundler/gem_tasks'
4
+ require 'rspec/core/rake_task'
3
5
 
4
6
  RSpec::Core::RakeTask.new(:spec)
5
7
 
6
- task :default => :spec
8
+ task default: :spec
7
9
 
8
- desc "Run RuboCop"
10
+ desc 'Run RuboCop'
9
11
  task :rubocop do
10
- sh "bundle exec rubocop"
12
+ sh 'bundle exec rubocop'
11
13
  end
12
14
 
13
- desc "Run RuboCop with auto-correct"
15
+ desc 'Run RuboCop with auto-correct'
14
16
  task :rubocop_fix do
15
- sh "bundle exec rubocop -a"
17
+ sh 'bundle exec rubocop -a'
16
18
  end
17
19
 
18
- desc "Run tests with coverage"
20
+ desc 'Run tests with coverage'
19
21
  task :coverage do
20
22
  ENV['COVERAGE'] = 'true'
21
23
  Rake::Task[:spec].invoke
data/TESTING.md ADDED
@@ -0,0 +1,82 @@
1
+ # Hybrid Testing for vagrant-ssh-config-manager
2
+
3
+ This project uses a **hybrid testing approach** that combines fast unit tests with comprehensive integration tests.
4
+
5
+ ## 🚀 Quick Start
6
+
7
+ ```bash
8
+ # Run unit tests (fast, recommended for development)
9
+ bundle exec rspec spec/unit/
10
+
11
+ # Run integration tests (real APIs)
12
+ bundle exec rspec spec/integration/
13
+
14
+ # Run both test suites
15
+ ./test-all.sh
16
+
17
+ # Helper scripts for detailed output:
18
+ ./test-unit.sh # Unit tests with documentation format
19
+ ./test-integration.sh # Integration tests with documentation format
20
+ ./test-all.sh # Both suites with progress format
21
+ ```
22
+
23
+ ## 📁 Test Structure
24
+
25
+ ```
26
+ spec/
27
+ ├── unit_helper.rb # Minimal setup for unit tests
28
+ ├── integration_helper.rb # Real Vagrant setup for integration tests
29
+ ├── unit/ # Fast, isolated unit tests
30
+ │ ├── config_spec.rb # ✅ Config class (32 examples)
31
+ │ ├── file_locker_spec.rb # ✅ FileLocker class (27 examples)
32
+ │ ├── file_manager_spec.rb # ✅ FileManager class (39 examples)
33
+ │ └── ssh_config_manager_spec.rb # ✅ SshConfigManager class (43 examples)
34
+ └── integration/ # Real API integration tests
35
+ └── include_manager_spec.rb # ✅ IncludeManager (14 examples)
36
+ ```
37
+
38
+ ## ✅ Current Test Status
39
+
40
+ - **Unit Tests**: 141 examples, 0 failures (~0.23s)
41
+ - **Integration Tests**: 14 examples, 0 failures (~0.02s)
42
+ - **Total**: **155 examples, 0 failures** (100% pass rate)
43
+ - **Combined Runtime**: ~0.25 seconds (extremely fast!)
44
+
45
+ ## 🚀 Performance Metrics
46
+
47
+ ```bash
48
+ $ ./test-all.sh
49
+ 📦 Unit Tests: 141 examples, 0 failures (0.23s)
50
+ 🔗 Integration Tests: 14 examples, 0 failures (0.02s)
51
+ ✅ Total: 155 examples, 0 failures
52
+ ```
53
+
54
+ ## 🔧 Test Types
55
+
56
+ ### Unit Tests (spec/unit/)
57
+ - **Purpose**: Fast feedback, isolated component testing
58
+ - **Setup**: Mocked dependencies, no real Vagrant loading
59
+ - **Speed**: ~0.23 seconds for 141 tests
60
+ - **Coverage**:
61
+ - Config validation and setup (32 examples)
62
+ - FileLocker concurrency and thread safety (27 examples)
63
+ - FileManager SSH file operations (39 examples)
64
+ - SshConfigManager entry management (43 examples)
65
+
66
+ ### Integration Tests (spec/integration/)
67
+ - **Purpose**: Real API verification, end-to-end testing
68
+ - **Setup**: Real Vagrant APIs, isolated file system environments
69
+ - **Speed**: ~0.02 seconds for 14 tests
70
+ - **Coverage**: IncludeManager SSH config manipulation, plugin markers
71
+
72
+ ## 🛠 Helper Scripts
73
+
74
+ - `./test-unit.sh` - Run only unit tests with detailed output
75
+ - `./test-integration.sh` - Run only integration tests with detailed output
76
+ - `./test-all.sh` - Run both test suites with summary
77
+
78
+ ### 🎯 Test Strategy
79
+ - **Unit Tests**: Mock all external dependencies for speed and isolation
80
+ - **Integration Tests**: Use real Vagrant APIs with controlled environments
81
+ - **Comprehensive Coverage**: Every major component and method tested
82
+ - **Error Scenarios**: Both success and failure paths covered
@@ -0,0 +1,5 @@
1
+ # frozen_string_literal: true
2
+
3
+ # Vagrant SSH Config Manager plugin entrypoint
4
+ # This file is required by Vagrant when loading the vagrant-ssh-config-manager plugin
5
+ require 'vagrant_ssh_config_manager'
@@ -0,0 +1,82 @@
1
+ # frozen_string_literal: true
2
+
3
+ module VagrantPlugins
4
+ module SshConfigManager
5
+ module Action
6
+ # Handles SSH config cleanup when a Vagrant machine is destroyed
7
+ class Destroy
8
+ def initialize(app, env)
9
+ @app = app
10
+ @env = env
11
+ @logger = Log4r::Logger.new('vagrant::plugins::ssh_config_manager::action::destroy')
12
+ end
13
+
14
+ def call(env)
15
+ machine = env[:machine]
16
+
17
+ # Handle SSH config removal before destroying the machine
18
+ if machine
19
+ config = machine.config.sshconfigmanager
20
+ handle_ssh_config_removal(machine, config) if config&.enabled && config.auto_remove_on_destroy
21
+ end
22
+
23
+ # Call the next middleware (actual destroy)
24
+ @app.call(env)
25
+ end
26
+
27
+ private
28
+
29
+ def handle_ssh_config_removal(machine, config)
30
+ @logger.info("Removing SSH config file for machine: #{machine.name}")
31
+
32
+ # Lazy load required classes with error handling
33
+ begin
34
+ require 'vagrant_ssh_config_manager/file_manager'
35
+ require 'vagrant_ssh_config_manager/include_manager'
36
+ rescue LoadError => e
37
+ @logger.error("Failed to load required classes: #{e.message}")
38
+ machine.ui.warn('SSH config manager: Failed to load required components, skipping SSH config removal')
39
+ return
40
+ end
41
+
42
+ # Create file manager and include manager
43
+ file_manager = FileManager.new(config)
44
+ include_manager = IncludeManager.new(config)
45
+
46
+ # Check if SSH config file exists
47
+ unless file_manager.ssh_config_file_exists?(machine)
48
+ @logger.debug("No SSH config file found for machine: #{machine.name}")
49
+ machine.ui.info("No SSH config file found to remove for machine: #{machine.name}")
50
+ return
51
+ end
52
+
53
+ # Remove SSH config file with logging
54
+ @logger.info("Attempting to remove SSH config file for #{machine.name}")
55
+
56
+ if file_manager.remove_ssh_config_file(machine)
57
+ machine.ui.info("SSH config file removed for machine '#{machine.name}'")
58
+ @logger.info("Successfully removed SSH config file for #{machine.name}")
59
+
60
+ # Manage Include directive after file removal
61
+ include_manager.manage_include_directive
62
+ else
63
+ machine.ui.warn("Failed to remove SSH config file for machine: #{machine.name}")
64
+ @logger.warn("Failed to remove SSH config file for #{machine.name}")
65
+ end
66
+ rescue Errno::EACCES => e
67
+ @logger.error("Permission denied accessing SSH config for #{machine.name}: #{e.message}")
68
+ machine.ui.warn('SSH config manager: Permission denied. Check file permissions.')
69
+ rescue Errno::EIO => e
70
+ @logger.error("I/O error for #{machine.name}: #{e.message}")
71
+ machine.ui.warn('SSH config manager: I/O error accessing SSH config files.')
72
+ rescue StandardError => e
73
+ @logger.error("Error removing SSH config for #{machine.name}: #{e.message}")
74
+ @logger.debug("Backtrace: #{e.backtrace.join("\n")}")
75
+
76
+ # Don't fail the vagrant destroy process, just warn
77
+ machine.ui.warn("SSH config manager encountered an error during cleanup: #{e.message}")
78
+ end
79
+ end
80
+ end
81
+ end
82
+ end
@@ -0,0 +1,66 @@
1
+ # frozen_string_literal: true
2
+
3
+ module VagrantPlugins
4
+ module SshConfigManager
5
+ module Action
6
+ # Handles SSH config update when a Vagrant machine is halted or suspended
7
+ class Halt
8
+ def initialize(app, env)
9
+ @app = app
10
+ @env = env
11
+ @logger = Log4r::Logger.new('vagrant::plugins::ssh_config_manager::action::halt')
12
+ end
13
+
14
+ def call(env)
15
+ machine = env[:machine]
16
+
17
+ # Handle SSH config for halt/suspend operations
18
+ if machine
19
+ config = machine.config.sshconfigmanager
20
+ handle_ssh_config_for_halt(machine, config, env) if config&.enabled && config.keep_config_on_halt
21
+ end
22
+
23
+ # Call the next middleware (actual halt/suspend)
24
+ @app.call(env)
25
+ end
26
+
27
+ private
28
+
29
+ def handle_ssh_config_for_halt(machine, _config, env)
30
+ # Determine the type of operation
31
+ operation = determine_operation_type(env)
32
+ @logger.info("Handling SSH config for #{operation} operation on machine: #{machine.name}")
33
+
34
+ # With separate files, we keep the SSH config file during halt/suspend
35
+ # This allows users to quickly resume and reconnect
36
+ case operation
37
+ when :halt, :suspend, :poweroff, :force_halt
38
+ machine.ui.info("Machine #{operation}ed - SSH config file retained")
39
+ @logger.debug("Keeping SSH config file for #{operation}ed machine: #{machine.name}")
40
+ else
41
+ @logger.debug("No SSH config action needed for operation: #{operation}")
42
+ end
43
+ rescue StandardError => e
44
+ @logger.error("Error handling SSH config for halt/suspend #{machine.name}: #{e.message}")
45
+ @logger.debug("Backtrace: #{e.backtrace.join("\n")}")
46
+
47
+ # Don't fail the halt/suspend process
48
+ machine.ui.warn("SSH config manager encountered an error: #{e.message}")
49
+ end
50
+
51
+ def determine_operation_type(env)
52
+ # Try to determine what type of halt operation this is
53
+ if env[:force_halt]
54
+ :force_halt
55
+ elsif env[:suspend]
56
+ :suspend
57
+ elsif env[:graceful_halt] == false
58
+ :poweroff
59
+ else
60
+ :halt
61
+ end
62
+ end
63
+ end
64
+ end
65
+ end
66
+ end
@@ -0,0 +1,81 @@
1
+ # frozen_string_literal: true
2
+
3
+ module VagrantPlugins
4
+ module SshConfigManager
5
+ module Action
6
+ class Provision
7
+ def initialize(app, env)
8
+ @app = app
9
+ @env = env
10
+ @logger = Log4r::Logger.new('vagrant::plugins::ssh_config_manager::action::provision')
11
+ end
12
+
13
+ def call(env)
14
+ # Call the next middleware first (actual provisioning)
15
+ @app.call(env)
16
+
17
+ # Only proceed if the machine is running and SSH is ready
18
+ machine = env[:machine]
19
+ return unless machine && machine.state.id == :running
20
+
21
+ # Check if plugin is enabled
22
+ config = machine.config.sshconfigmanager
23
+ return unless config&.enabled && config.refresh_on_provision
24
+
25
+ # Handle SSH config file refresh after provisioning
26
+ handle_ssh_config_refresh(machine, config)
27
+ end
28
+
29
+ private
30
+
31
+ def handle_ssh_config_refresh(machine, config)
32
+ @logger.info("Refreshing SSH config file after provisioning for machine: #{machine.name}")
33
+
34
+ # Lazy load required classes with error handling
35
+ begin
36
+ require 'vagrant_ssh_config_manager/ssh_info_extractor'
37
+ require 'vagrant_ssh_config_manager/file_manager'
38
+ require 'vagrant_ssh_config_manager/include_manager'
39
+ rescue LoadError => e
40
+ @logger.error("Failed to load required classes: #{e.message}")
41
+ machine.ui.warn('SSH config manager: Failed to load required components, skipping SSH config refresh')
42
+ return
43
+ end
44
+
45
+ # Extract SSH information
46
+ extractor = SshInfoExtractor.new(machine)
47
+
48
+ # Check if machine supports SSH
49
+ unless extractor.ssh_capable?
50
+ @logger.debug("Machine #{machine.name} does not support SSH, skipping")
51
+ return
52
+ end
53
+
54
+ # Create file manager and include manager
55
+ file_manager = FileManager.new(config)
56
+ include_manager = IncludeManager.new(config)
57
+
58
+ # Refresh SSH config file (regenerate it)
59
+ # Provisioning might have changed SSH configuration
60
+ if file_manager.write_ssh_config_file(machine)
61
+ file_manager.send(:generate_host_name, machine)
62
+ machine.ui.info("SSH config file refreshed for machine '#{machine.name}' after provisioning")
63
+ @logger.info('SSH config file refreshed due to provisioning')
64
+
65
+ # Ensure Include directive is managed
66
+ include_manager.manage_include_directive
67
+ else
68
+ machine.ui.warn("Failed to refresh SSH config file for machine: #{machine.name}")
69
+ @logger.warn("Failed to refresh SSH config file for #{machine.name}")
70
+ end
71
+ rescue StandardError => e
72
+ @logger.error("Error refreshing SSH config for #{machine.name}: #{e.message}")
73
+ @logger.debug("Backtrace: #{e.backtrace.join("\n")}")
74
+
75
+ # Don't fail the vagrant provision process, just warn
76
+ machine.ui.warn("SSH config manager encountered an error during refresh: #{e.message}")
77
+ end
78
+ end
79
+ end
80
+ end
81
+ end
@@ -0,0 +1,105 @@
1
+ # frozen_string_literal: true
2
+
3
+ module VagrantPlugins
4
+ module SshConfigManager
5
+ module Action
6
+ class Reload
7
+ def initialize(app, env)
8
+ @app = app
9
+ @env = env
10
+ @logger = Log4r::Logger.new('vagrant::plugins::ssh_config_manager::action::reload')
11
+ end
12
+
13
+ def call(env)
14
+ # Call the next middleware first
15
+ @app.call(env)
16
+
17
+ # Only proceed if the machine is running and SSH is ready
18
+ machine = env[:machine]
19
+ return unless machine && machine.state.id == :running
20
+
21
+ # Check if plugin is enabled
22
+ config = machine.config.sshconfigmanager
23
+ return unless config&.enabled
24
+
25
+ # Handle SSH config update
26
+ handle_ssh_config_update(machine, config)
27
+ end
28
+
29
+ private
30
+
31
+ def handle_ssh_config_update(machine, config)
32
+ @logger.info("Updating SSH config entry for machine: #{machine.name}")
33
+
34
+ # Lazy load required classes with error handling
35
+ begin
36
+ require 'vagrant_ssh_config_manager/ssh_info_extractor'
37
+ require 'vagrant_ssh_config_manager/file_manager'
38
+ require 'vagrant_ssh_config_manager/include_manager'
39
+ rescue LoadError => e
40
+ @logger.error("Failed to load required classes: #{e.message}")
41
+ machine.ui.warn('SSH config manager: Failed to load required components, skipping SSH config update')
42
+ return
43
+ end
44
+
45
+ # Extract SSH information
46
+ extractor = SshInfoExtractor.new(machine)
47
+
48
+ # Check if machine supports SSH
49
+ unless extractor.ssh_capable?
50
+ @logger.debug("Machine #{machine.name} does not support SSH, skipping")
51
+ machine.ui.info("Machine #{machine.name} does not support SSH, skipping SSH config update")
52
+ return
53
+ end
54
+
55
+ # Create file manager and include manager
56
+ file_manager = FileManager.new(config)
57
+ include_manager = IncludeManager.new(config)
58
+
59
+ # Check if file exists and compare content
60
+ if file_manager.ssh_config_file_exists?(machine)
61
+ # Update SSH config file (always regenerate for simplicity)
62
+ @logger.info("SSH config file exists, updating for #{machine.name}")
63
+ if file_manager.write_ssh_config_file(machine)
64
+ host_name = file_manager.send(:generate_host_name, machine)
65
+ machine.ui.info("SSH config file updated for machine '#{machine.name}' (#{host_name})")
66
+ @logger.info('SSH config file updated due to reload')
67
+
68
+ # Ensure Include directive is managed
69
+ include_manager.manage_include_directive
70
+ else
71
+ machine.ui.warn("Failed to update SSH config file for machine: #{machine.name}")
72
+ @logger.warn("Failed to update SSH config file for #{machine.name}")
73
+ end
74
+ else
75
+ # Add new SSH config file (machine might have been added during reload)
76
+ @logger.info("No existing SSH config file found, creating new file for #{machine.name}")
77
+ if file_manager.write_ssh_config_file(machine)
78
+ host_name = file_manager.send(:generate_host_name, machine)
79
+ machine.ui.info("SSH config file created for machine '#{machine.name}' as '#{host_name}'")
80
+ @logger.info("Successfully created new SSH config file for #{machine.name}")
81
+
82
+ # Manage Include directive after file creation
83
+ include_manager.manage_include_directive
84
+ else
85
+ machine.ui.warn("Failed to create SSH config file for machine: #{machine.name}")
86
+ @logger.warn("Failed to create SSH config file for #{machine.name}")
87
+ end
88
+ end
89
+ rescue Errno::EACCES => e
90
+ @logger.error("Permission denied accessing SSH config for #{machine.name}: #{e.message}")
91
+ machine.ui.warn('SSH config manager: Permission denied. Check file permissions.')
92
+ rescue Errno::EIO => e
93
+ @logger.error("I/O error for #{machine.name}: #{e.message}")
94
+ machine.ui.warn('SSH config manager: I/O error accessing SSH config files.')
95
+ rescue StandardError => e
96
+ @logger.error("Error updating SSH config for #{machine.name}: #{e.message}")
97
+ @logger.debug("Backtrace: #{e.backtrace.join("\n")}")
98
+
99
+ # Don't fail the vagrant reload process, just warn
100
+ machine.ui.warn("SSH config manager encountered an error: #{e.message}")
101
+ end
102
+ end
103
+ end
104
+ end
105
+ end