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
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
SHA256:
|
3
|
+
metadata.gz: e1bd8cef21dfadbeaed3204b702d00203c2b30581a6ad27c370d5679a661e594
|
4
|
+
data.tar.gz: 2ce4b4be6a99bdfc0c978c3fda2af091e37f80cebca628bb317cc52ac19266a9
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: c314ed1a2dfcfb02dee0c09e4cabb3b36f8f6423685388f34632dd707545c8d9408178db0c63617cd789dd7115cd115d37fefed57b8b619500fd55ded0e35ccf
|
7
|
+
data.tar.gz: 62de37425bece9d76e6f756ccdf71ac52c7c03a46258222412a3993151297d24ae4d8eb7929b33a1407c3b7f0fbc53aaa9608262fee4d7ea22169a39f967ac9b
|
data/.bundle/config
ADDED
data/.gitignore
ADDED
data/Gemfile
ADDED
@@ -0,0 +1,15 @@
|
|
1
|
+
source "https://rubygems.org"
|
2
|
+
|
3
|
+
# Specify your gem's dependencies in vagrant-ssh-config-manager.gemspec
|
4
|
+
gemspec
|
5
|
+
|
6
|
+
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"
|
15
|
+
end
|
data/README.md
ADDED
@@ -0,0 +1,267 @@
|
|
1
|
+
# Vagrant SSH Config Manager
|
2
|
+
|
3
|
+
A Vagrant plugin that automatically manages SSH configuration entries for Vagrant-managed VMs, providing seamless SSH access without the need to remember ports, IPs, or key locations.
|
4
|
+
|
5
|
+
## Features
|
6
|
+
|
7
|
+
- **Automatic SSH Config Management**: Creates and maintains SSH config files when VMs are started
|
8
|
+
- **Project Isolation**: Each Vagrant project gets its own SSH config namespace to avoid conflicts
|
9
|
+
- **Separate File Management**: Creates individual SSH config files for each VM in a dedicated directory
|
10
|
+
- **Include Directive Management**: Automatically manages Include directives in your main SSH config
|
11
|
+
- **Lifecycle Integration**: Hooks into all Vagrant VM lifecycle events (up, destroy, reload, halt, provision)
|
12
|
+
- **Configurable Behavior**: Extensive configuration options for different workflows
|
13
|
+
- **Robust Error Handling**: Graceful handling of file permission issues and concurrent access
|
14
|
+
- **File Locking**: Prevents SSH config corruption during concurrent operations
|
15
|
+
- **Backup Support**: Creates backups before modifying SSH config files
|
16
|
+
|
17
|
+
## Installation
|
18
|
+
|
19
|
+
### Install from RubyGems (Recommended)
|
20
|
+
|
21
|
+
```bash
|
22
|
+
vagrant plugin install vagrant-ssh-config-manager
|
23
|
+
```
|
24
|
+
|
25
|
+
### Install from Source
|
26
|
+
|
27
|
+
```bash
|
28
|
+
git clone https://github.com/your-username/vagrant-ssh-config-manager.git
|
29
|
+
cd vagrant-ssh-config-manager
|
30
|
+
gem build vagrant-ssh-config-manager.gemspec
|
31
|
+
vagrant plugin install vagrant-ssh-config-manager-*.gem
|
32
|
+
```
|
33
|
+
|
34
|
+
## Quick Start
|
35
|
+
|
36
|
+
1. Install the plugin
|
37
|
+
2. Add configuration to your `Vagrantfile` (optional)
|
38
|
+
3. Run `vagrant up`
|
39
|
+
4. Connect to your VM with: `ssh <project>-<machine-name>`
|
40
|
+
|
41
|
+
### Example
|
42
|
+
|
43
|
+
```ruby
|
44
|
+
# Vagrantfile
|
45
|
+
Vagrant.configure("2") do |config|
|
46
|
+
config.vm.box = "ubuntu/focal64"
|
47
|
+
|
48
|
+
# Optional: Configure the plugin (uses sensible defaults)
|
49
|
+
config.sshconfigmanager.enabled = true
|
50
|
+
config.sshconfigmanager.ssh_config_dir = "~/.ssh/config.d/vagrant"
|
51
|
+
config.sshconfigmanager.manage_includes = true
|
52
|
+
|
53
|
+
config.vm.define "web" do |web|
|
54
|
+
web.vm.network "private_network", ip: "192.168.33.10"
|
55
|
+
end
|
56
|
+
end
|
57
|
+
```
|
58
|
+
|
59
|
+
After `vagrant up`, you can connect with:
|
60
|
+
```bash
|
61
|
+
ssh vagrant-web # Project + machine name
|
62
|
+
```
|
63
|
+
|
64
|
+
## Configuration
|
65
|
+
|
66
|
+
All configuration options are available in your `Vagrantfile`:
|
67
|
+
|
68
|
+
```ruby
|
69
|
+
Vagrant.configure("2") do |config|
|
70
|
+
config.sshconfigmanager.enabled = true # Enable/disable plugin (default: true)
|
71
|
+
config.sshconfigmanager.ssh_config_dir = "~/.ssh/config.d/vagrant" # Directory for individual SSH config files (default)
|
72
|
+
config.sshconfigmanager.manage_includes = false # Manage Include directive in main SSH config (default: false)
|
73
|
+
config.sshconfigmanager.auto_create_dir = true # Auto-create SSH config dir if missing (default: true)
|
74
|
+
config.sshconfigmanager.cleanup_empty_dir = true # Remove empty SSH config dir when no VMs remain (default: true)
|
75
|
+
config.sshconfigmanager.auto_remove_on_destroy = true # Remove SSH entries on VM destroy (default: true)
|
76
|
+
config.sshconfigmanager.update_on_reload = true # Update SSH entries on VM reload (default: true)
|
77
|
+
config.sshconfigmanager.refresh_on_provision = true # Refresh SSH entries on VM provision (default: true)
|
78
|
+
config.sshconfigmanager.keep_config_on_halt = true # Keep SSH entries when VM is halted (default: true)
|
79
|
+
config.sshconfigmanager.project_isolation = true # Use project-specific naming (default: true)
|
80
|
+
end
|
81
|
+
```
|
82
|
+
|
83
|
+
### Configuration Options
|
84
|
+
|
85
|
+
| Option | Type | Default | Description |
|
86
|
+
|--------|------|---------|-------------|
|
87
|
+
| `enabled` | Boolean | `true` | Enable or disable the plugin globally |
|
88
|
+
| `ssh_config_dir` | String | `~/.ssh/config.d/vagrant` | Directory for individual SSH config files |
|
89
|
+
| `manage_includes` | Boolean | `false` | Automatically manage Include directive in main SSH config |
|
90
|
+
| `auto_create_dir` | Boolean | `true` | Automatically create SSH config directory if it doesn't exist |
|
91
|
+
| `cleanup_empty_dir` | Boolean | `true` | Remove empty SSH config directory when no VMs remain |
|
92
|
+
| `auto_remove_on_destroy` | Boolean | `true` | Remove SSH entries when VM is destroyed |
|
93
|
+
| `update_on_reload` | Boolean | `true` | Update SSH entries when VM is reloaded |
|
94
|
+
| `refresh_on_provision` | Boolean | `true` | Refresh SSH entries when VM is provisioned |
|
95
|
+
| `keep_config_on_halt` | Boolean | `true` | Keep SSH entries when VM is halted/suspended |
|
96
|
+
| `project_isolation` | Boolean | `true` | Use project-specific naming for SSH entries.<br>Set to `false` if using together with `vagrant-hostmanager`.<br>**Be mindful of hostname collisions!** |
|
97
|
+
|
98
|
+
## How It Works
|
99
|
+
|
100
|
+
### Separate File Architecture (Default)
|
101
|
+
|
102
|
+
The plugin creates individual SSH config files for each VM in a dedicated directory (default: `~/.ssh/config.d/vagrant/`). This approach:
|
103
|
+
|
104
|
+
- **Prevents conflicts**: Each VM has its own config file
|
105
|
+
- **Enables easier cleanup**: Destroying a VM removes only its config file
|
106
|
+
- **Supports concurrent operations**: Multiple VMs can be managed simultaneously
|
107
|
+
- **Maintains cleaner config**: Main SSH config stays uncluttered
|
108
|
+
|
109
|
+
### SSH Config Structure
|
110
|
+
|
111
|
+
```
|
112
|
+
~/.ssh/config # Your main SSH config
|
113
|
+
~/.ssh/config.d/vagrant/ # Plugin-managed directory
|
114
|
+
├── a1b2c3d4-web.conf # Individual VM config files
|
115
|
+
├── a1b2c3d4-db.conf
|
116
|
+
└── f5e6d7c8-api.conf # From different projects
|
117
|
+
```
|
118
|
+
|
119
|
+
### Include Directive Management
|
120
|
+
|
121
|
+
When `manage_includes` is enabled (default: false), the plugin automatically adds:
|
122
|
+
|
123
|
+
```
|
124
|
+
# ~/.ssh/config
|
125
|
+
# BEGIN vagrant-ssh-config-manager
|
126
|
+
Include ~/.ssh/config.d/vagrant/*.conf
|
127
|
+
# END vagrant-ssh-config-manager
|
128
|
+
|
129
|
+
# Your existing SSH configuration continues here...
|
130
|
+
```
|
131
|
+
|
132
|
+
### Project Isolation
|
133
|
+
|
134
|
+
Each Vagrant project gets a unique identifier based on the project path:
|
135
|
+
- Project path: `/home/user/my-app`
|
136
|
+
- Project hash: `a1b2c3d4` (first 8 chars of MD5)
|
137
|
+
- SSH host names: `my-app-web`, `my-app-db`
|
138
|
+
- Config files: `a1b2c3d4-web.conf`, `a1b2c3d4-db.conf`
|
139
|
+
|
140
|
+
### Individual Config File Example
|
141
|
+
|
142
|
+
```
|
143
|
+
# ~/.ssh/config.d/vagrant/a1b2c3d4-web.conf
|
144
|
+
# Managed by vagrant-ssh-config-manager plugin
|
145
|
+
# Project: my-app
|
146
|
+
# VM: web
|
147
|
+
# Generated: 2025-01-01 12:00:00
|
148
|
+
|
149
|
+
Host my-app-web
|
150
|
+
HostName 192.168.33.10
|
151
|
+
Port 22
|
152
|
+
User vagrant
|
153
|
+
IdentityFile /home/user/.vagrant.d/insecure_private_key
|
154
|
+
IdentitiesOnly yes
|
155
|
+
UserKnownHostsFile /dev/null
|
156
|
+
StrictHostKeyChecking no
|
157
|
+
PasswordAuthentication no
|
158
|
+
LogLevel FATAL
|
159
|
+
```
|
160
|
+
|
161
|
+
### Main SSH Config Integration
|
162
|
+
|
163
|
+
When `manage_includes` is enabled, the plugin automatically adds an Include directive:
|
164
|
+
|
165
|
+
```
|
166
|
+
# ~/.ssh/config
|
167
|
+
# BEGIN vagrant-ssh-config-manager
|
168
|
+
Include ~/.ssh/config.d/vagrant/*.conf
|
169
|
+
# END vagrant-ssh-config-manager
|
170
|
+
|
171
|
+
# Your existing SSH configuration...
|
172
|
+
```
|
173
|
+
|
174
|
+
## Usage Examples
|
175
|
+
|
176
|
+
### Basic Multi-Machine Setup
|
177
|
+
|
178
|
+
```ruby
|
179
|
+
Vagrant.configure("2") do |config|
|
180
|
+
config.vm.box = "generic/debian12"
|
181
|
+
|
182
|
+
config.vm.define "web" do |web|
|
183
|
+
web.vm.network "private_network", ip: "192.168.33.10"
|
184
|
+
end
|
185
|
+
|
186
|
+
config.vm.define "db" do |db|
|
187
|
+
db.vm.network "private_network", ip: "192.168.33.11"
|
188
|
+
end
|
189
|
+
end
|
190
|
+
```
|
191
|
+
|
192
|
+
Connect to machines:
|
193
|
+
```bash
|
194
|
+
vagrant up
|
195
|
+
ssh vagrant-web
|
196
|
+
ssh vagrant-db
|
197
|
+
```
|
198
|
+
|
199
|
+
### Disable for Specific Environments
|
200
|
+
|
201
|
+
```ruby
|
202
|
+
Vagrant.configure("2") do |config|
|
203
|
+
# Disable in production environment
|
204
|
+
config.sshconfigmanager.enabled = ENV['VAGRANT_ENV'] != 'production'
|
205
|
+
# ... rest of config
|
206
|
+
end
|
207
|
+
```
|
208
|
+
|
209
|
+
## Troubleshooting
|
210
|
+
|
211
|
+
### Common Issues
|
212
|
+
|
213
|
+
#### Permission Denied
|
214
|
+
```
|
215
|
+
SSH config manager: Permission denied. Check file permissions.
|
216
|
+
```
|
217
|
+
**Solution**: Ensure SSH config file and directory are writable:
|
218
|
+
```bash
|
219
|
+
chmod 600 ~/.ssh/config
|
220
|
+
chmod 700 ~/.ssh
|
221
|
+
```
|
222
|
+
|
223
|
+
#### File Not Found
|
224
|
+
```
|
225
|
+
SSH config manager: SSH config file cannot be created
|
226
|
+
```
|
227
|
+
**Solution**: Ensure SSH directory exists:
|
228
|
+
```bash
|
229
|
+
mkdir -p ~/.ssh
|
230
|
+
chmod 700 ~/.ssh
|
231
|
+
```
|
232
|
+
|
233
|
+
#### Concurrent Access Issues
|
234
|
+
The plugin uses separate ssh config file for each of the servers... there should be no concurrency issues by design.
|
235
|
+
|
236
|
+
|
237
|
+
### Debugging
|
238
|
+
|
239
|
+
Enable Vagrant debug output:
|
240
|
+
```bash
|
241
|
+
VAGRANT_LOG=debug vagrant up
|
242
|
+
```
|
243
|
+
|
244
|
+
Check plugin logs in the output for SSH config manager operations.
|
245
|
+
|
246
|
+
### Manual Cleanup
|
247
|
+
|
248
|
+
If you need to manually clean up SSH config entries:
|
249
|
+
|
250
|
+
```bash
|
251
|
+
# Remove all Vagrant-managed entries
|
252
|
+
rm ~/.ssh/config.d/vagrant/*
|
253
|
+
|
254
|
+
# Remove include directive from main config
|
255
|
+
sed -i '/# BEGIN vagrant-ssh-config-manager/,/# END vagrant-ssh-config-manager/d' ~/.ssh/config
|
256
|
+
```
|
257
|
+
|
258
|
+
## Compatibility
|
259
|
+
|
260
|
+
- **Vagrant**: 2.0+
|
261
|
+
- **Ruby**: 2.4+
|
262
|
+
- **Platforms**: Linux, macOS, Windows (with WSL)
|
263
|
+
- **Providers**: VirtualBox, VMware, Libvirt, Hyper-V, Docker, AWS, and others
|
264
|
+
|
265
|
+
## License
|
266
|
+
|
267
|
+
MIT License - see [LICENSE](LICENSE) file for details.
|
data/Rakefile
ADDED
@@ -0,0 +1,22 @@
|
|
1
|
+
require "bundler/gem_tasks"
|
2
|
+
require "rspec/core/rake_task"
|
3
|
+
|
4
|
+
RSpec::Core::RakeTask.new(:spec)
|
5
|
+
|
6
|
+
task :default => :spec
|
7
|
+
|
8
|
+
desc "Run RuboCop"
|
9
|
+
task :rubocop do
|
10
|
+
sh "bundle exec rubocop"
|
11
|
+
end
|
12
|
+
|
13
|
+
desc "Run RuboCop with auto-correct"
|
14
|
+
task :rubocop_fix do
|
15
|
+
sh "bundle exec rubocop -a"
|
16
|
+
end
|
17
|
+
|
18
|
+
desc "Run tests with coverage"
|
19
|
+
task :coverage do
|
20
|
+
ENV['COVERAGE'] = 'true'
|
21
|
+
Rake::Task[:spec].invoke
|
22
|
+
end
|
@@ -0,0 +1,84 @@
|
|
1
|
+
module VagrantPlugins
|
2
|
+
module SshConfigManager
|
3
|
+
module Action
|
4
|
+
class Destroy
|
5
|
+
def initialize(app, env)
|
6
|
+
@app = app
|
7
|
+
@env = env
|
8
|
+
@logger = Log4r::Logger.new("vagrant::plugins::ssh_config_manager::action::destroy")
|
9
|
+
end
|
10
|
+
|
11
|
+
def call(env)
|
12
|
+
machine = env[:machine]
|
13
|
+
|
14
|
+
# Handle SSH config removal before destroying the machine
|
15
|
+
if machine
|
16
|
+
config = machine.config.sshconfigmanager
|
17
|
+
if config && config.enabled && config.auto_remove_on_destroy
|
18
|
+
handle_ssh_config_removal(machine, config)
|
19
|
+
end
|
20
|
+
end
|
21
|
+
|
22
|
+
# Call the next middleware (actual destroy)
|
23
|
+
@app.call(env)
|
24
|
+
end
|
25
|
+
|
26
|
+
private
|
27
|
+
|
28
|
+
def handle_ssh_config_removal(machine, config)
|
29
|
+
begin
|
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
|
+
|
67
|
+
rescue Errno::EACCES => e
|
68
|
+
@logger.error("Permission denied accessing SSH config for #{machine.name}: #{e.message}")
|
69
|
+
machine.ui.warn("SSH config manager: Permission denied. Check file permissions.")
|
70
|
+
rescue Errno::EIO => e
|
71
|
+
@logger.error("I/O error for #{machine.name}: #{e.message}")
|
72
|
+
machine.ui.warn("SSH config manager: I/O error accessing SSH config files.")
|
73
|
+
rescue => e
|
74
|
+
@logger.error("Error removing SSH config for #{machine.name}: #{e.message}")
|
75
|
+
@logger.debug("Backtrace: #{e.backtrace.join("\n")}")
|
76
|
+
|
77
|
+
# Don't fail the vagrant destroy process, just warn
|
78
|
+
machine.ui.warn("SSH config manager encountered an error during cleanup: #{e.message}")
|
79
|
+
end
|
80
|
+
end
|
81
|
+
end
|
82
|
+
end
|
83
|
+
end
|
84
|
+
end
|
@@ -0,0 +1,68 @@
|
|
1
|
+
module VagrantPlugins
|
2
|
+
module SshConfigManager
|
3
|
+
module Action
|
4
|
+
class Halt
|
5
|
+
def initialize(app, env)
|
6
|
+
@app = app
|
7
|
+
@env = env
|
8
|
+
@logger = Log4r::Logger.new("vagrant::plugins::ssh_config_manager::action::halt")
|
9
|
+
end
|
10
|
+
|
11
|
+
def call(env)
|
12
|
+
machine = env[:machine]
|
13
|
+
|
14
|
+
# Handle SSH config for halt/suspend operations
|
15
|
+
if machine
|
16
|
+
config = machine.config.sshconfigmanager
|
17
|
+
if config && config.enabled && config.keep_config_on_halt
|
18
|
+
handle_ssh_config_for_halt(machine, config, env)
|
19
|
+
end
|
20
|
+
end
|
21
|
+
|
22
|
+
# Call the next middleware (actual halt/suspend)
|
23
|
+
@app.call(env)
|
24
|
+
end
|
25
|
+
|
26
|
+
private
|
27
|
+
|
28
|
+
def handle_ssh_config_for_halt(machine, config, env)
|
29
|
+
begin
|
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
|
+
|
44
|
+
rescue => e
|
45
|
+
@logger.error("Error handling SSH config for halt/suspend #{machine.name}: #{e.message}")
|
46
|
+
@logger.debug("Backtrace: #{e.backtrace.join("\n")}")
|
47
|
+
|
48
|
+
# Don't fail the halt/suspend process
|
49
|
+
machine.ui.warn("SSH config manager encountered an error: #{e.message}")
|
50
|
+
end
|
51
|
+
end
|
52
|
+
|
53
|
+
def determine_operation_type(env)
|
54
|
+
# Try to determine what type of halt operation this is
|
55
|
+
if env[:force_halt]
|
56
|
+
:force_halt
|
57
|
+
elsif env[:suspend]
|
58
|
+
:suspend
|
59
|
+
elsif env[:graceful_halt] == false
|
60
|
+
:poweroff
|
61
|
+
else
|
62
|
+
:halt
|
63
|
+
end
|
64
|
+
end
|
65
|
+
end
|
66
|
+
end
|
67
|
+
end
|
68
|
+
end
|
@@ -0,0 +1,82 @@
|
|
1
|
+
module VagrantPlugins
|
2
|
+
module SshConfigManager
|
3
|
+
module Action
|
4
|
+
class Provision
|
5
|
+
def initialize(app, env)
|
6
|
+
@app = app
|
7
|
+
@env = env
|
8
|
+
@logger = Log4r::Logger.new("vagrant::plugins::ssh_config_manager::action::provision")
|
9
|
+
end
|
10
|
+
|
11
|
+
def call(env)
|
12
|
+
# Call the next middleware first (actual provisioning)
|
13
|
+
@app.call(env)
|
14
|
+
|
15
|
+
# Only proceed if the machine is running and SSH is ready
|
16
|
+
machine = env[:machine]
|
17
|
+
return unless machine && machine.state.id == :running
|
18
|
+
|
19
|
+
# Check if plugin is enabled
|
20
|
+
config = machine.config.sshconfigmanager
|
21
|
+
return unless config && config.enabled && config.refresh_on_provision
|
22
|
+
|
23
|
+
# Handle SSH config file refresh after provisioning
|
24
|
+
handle_ssh_config_refresh(machine, config)
|
25
|
+
end
|
26
|
+
|
27
|
+
private
|
28
|
+
|
29
|
+
def handle_ssh_config_refresh(machine, config)
|
30
|
+
begin
|
31
|
+
@logger.info("Refreshing SSH config file after provisioning for machine: #{machine.name}")
|
32
|
+
|
33
|
+
# Lazy load required classes with error handling
|
34
|
+
begin
|
35
|
+
require 'vagrant-ssh-config-manager/ssh_info_extractor'
|
36
|
+
require 'vagrant-ssh-config-manager/file_manager'
|
37
|
+
require 'vagrant-ssh-config-manager/include_manager'
|
38
|
+
rescue LoadError => e
|
39
|
+
@logger.error("Failed to load required classes: #{e.message}")
|
40
|
+
machine.ui.warn("SSH config manager: Failed to load required components, skipping SSH config refresh")
|
41
|
+
return
|
42
|
+
end
|
43
|
+
|
44
|
+
# Extract SSH information
|
45
|
+
extractor = SshInfoExtractor.new(machine)
|
46
|
+
|
47
|
+
# Check if machine supports SSH
|
48
|
+
unless extractor.ssh_capable?
|
49
|
+
@logger.debug("Machine #{machine.name} does not support SSH, skipping")
|
50
|
+
return
|
51
|
+
end
|
52
|
+
|
53
|
+
# Create file manager and include manager
|
54
|
+
file_manager = FileManager.new(config)
|
55
|
+
include_manager = IncludeManager.new(config)
|
56
|
+
|
57
|
+
# Refresh SSH config file (regenerate it)
|
58
|
+
# Provisioning might have changed SSH configuration
|
59
|
+
if file_manager.write_ssh_config_file(machine)
|
60
|
+
host_name = file_manager.send(:generate_host_name, machine)
|
61
|
+
machine.ui.info("SSH config file refreshed for machine '#{machine.name}' after provisioning")
|
62
|
+
@logger.info("SSH config file refreshed due to provisioning")
|
63
|
+
|
64
|
+
# Ensure Include directive is managed
|
65
|
+
include_manager.manage_include_directive
|
66
|
+
else
|
67
|
+
machine.ui.warn("Failed to refresh SSH config file for machine: #{machine.name}")
|
68
|
+
@logger.warn("Failed to refresh SSH config file for #{machine.name}")
|
69
|
+
end
|
70
|
+
|
71
|
+
rescue => 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
|
82
|
+
end
|
@@ -0,0 +1,106 @@
|
|
1
|
+
module VagrantPlugins
|
2
|
+
module SshConfigManager
|
3
|
+
module Action
|
4
|
+
class Reload
|
5
|
+
def initialize(app, env)
|
6
|
+
@app = app
|
7
|
+
@env = env
|
8
|
+
@logger = Log4r::Logger.new("vagrant::plugins::ssh_config_manager::action::reload")
|
9
|
+
end
|
10
|
+
|
11
|
+
def call(env)
|
12
|
+
# Call the next middleware first
|
13
|
+
@app.call(env)
|
14
|
+
|
15
|
+
# Only proceed if the machine is running and SSH is ready
|
16
|
+
machine = env[:machine]
|
17
|
+
return unless machine && machine.state.id == :running
|
18
|
+
|
19
|
+
# Check if plugin is enabled
|
20
|
+
config = machine.config.sshconfigmanager
|
21
|
+
return unless config && config.enabled
|
22
|
+
|
23
|
+
# Handle SSH config update
|
24
|
+
handle_ssh_config_update(machine, config)
|
25
|
+
end
|
26
|
+
|
27
|
+
private
|
28
|
+
|
29
|
+
def handle_ssh_config_update(machine, config)
|
30
|
+
begin
|
31
|
+
@logger.info("Updating SSH config entry for machine: #{machine.name}")
|
32
|
+
|
33
|
+
# Lazy load required classes with error handling
|
34
|
+
begin
|
35
|
+
require 'vagrant-ssh-config-manager/ssh_info_extractor'
|
36
|
+
require 'vagrant-ssh-config-manager/file_manager'
|
37
|
+
require 'vagrant-ssh-config-manager/include_manager'
|
38
|
+
rescue LoadError => e
|
39
|
+
@logger.error("Failed to load required classes: #{e.message}")
|
40
|
+
machine.ui.warn("SSH config manager: Failed to load required components, skipping SSH config update")
|
41
|
+
return
|
42
|
+
end
|
43
|
+
|
44
|
+
# Extract SSH information
|
45
|
+
extractor = SshInfoExtractor.new(machine)
|
46
|
+
|
47
|
+
# Check if machine supports SSH
|
48
|
+
unless extractor.ssh_capable?
|
49
|
+
@logger.debug("Machine #{machine.name} does not support SSH, skipping")
|
50
|
+
machine.ui.info("Machine #{machine.name} does not support SSH, skipping SSH config update")
|
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
|
+
# Check if file exists and compare content
|
59
|
+
if file_manager.ssh_config_file_exists?(machine)
|
60
|
+
# Update SSH config file (always regenerate for simplicity)
|
61
|
+
@logger.info("SSH config file exists, updating for #{machine.name}")
|
62
|
+
if file_manager.write_ssh_config_file(machine)
|
63
|
+
host_name = file_manager.send(:generate_host_name, machine)
|
64
|
+
machine.ui.info("SSH config file updated for machine '#{machine.name}' (#{host_name})")
|
65
|
+
@logger.info("SSH config file updated due to reload")
|
66
|
+
|
67
|
+
# Ensure Include directive is managed
|
68
|
+
include_manager.manage_include_directive
|
69
|
+
else
|
70
|
+
machine.ui.warn("Failed to update SSH config file for machine: #{machine.name}")
|
71
|
+
@logger.warn("Failed to update SSH config file for #{machine.name}")
|
72
|
+
end
|
73
|
+
else
|
74
|
+
# Add new SSH config file (machine might have been added during reload)
|
75
|
+
@logger.info("No existing SSH config file found, creating new file for #{machine.name}")
|
76
|
+
if file_manager.write_ssh_config_file(machine)
|
77
|
+
host_name = file_manager.send(:generate_host_name, machine)
|
78
|
+
machine.ui.info("SSH config file created for machine '#{machine.name}' as '#{host_name}'")
|
79
|
+
@logger.info("Successfully created new SSH config file for #{machine.name}")
|
80
|
+
|
81
|
+
# Manage Include directive after file creation
|
82
|
+
include_manager.manage_include_directive
|
83
|
+
else
|
84
|
+
machine.ui.warn("Failed to create SSH config file for machine: #{machine.name}")
|
85
|
+
@logger.warn("Failed to create SSH config file for #{machine.name}")
|
86
|
+
end
|
87
|
+
end
|
88
|
+
|
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 => 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
|
106
|
+
end
|