vagrant 0.7.8 → 0.8.1
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.
- data/CHANGELOG.md +39 -0
- data/Gemfile +1 -7
- data/Rakefile +0 -11
- data/bin/vagrant +4 -0
- data/config/default.rb +1 -2
- data/lib/vagrant.rb +7 -5
- data/lib/vagrant/action.rb +5 -1
- data/lib/vagrant/action/builtin.rb +4 -1
- data/lib/vagrant/action/general/package.rb +6 -2
- data/lib/vagrant/action/vm.rb +2 -0
- data/lib/vagrant/action/vm/clear_forwarded_ports.rb +9 -22
- data/lib/vagrant/action/vm/clear_shared_folders.rb +9 -14
- data/lib/vagrant/action/vm/customize.rb +9 -4
- data/lib/vagrant/action/vm/forward_ports.rb +10 -11
- data/lib/vagrant/action/vm/match_mac_address.rb +8 -3
- data/lib/vagrant/action/vm/modify.rb +37 -0
- data/lib/vagrant/action/vm/network.rb +9 -2
- data/lib/vagrant/action/vm/provision.rb +10 -17
- data/lib/vagrant/action/vm/provisioner_cleanup.rb +26 -0
- data/lib/vagrant/action/vm/share_folders.rb +16 -8
- data/lib/vagrant/action/warden.rb +8 -2
- data/lib/vagrant/command/ssh.rb +4 -4
- data/lib/vagrant/command/ssh_config.rb +4 -2
- data/lib/vagrant/config/ssh.rb +3 -0
- data/lib/vagrant/config/vm.rb +16 -12
- data/lib/vagrant/downloaders/http.rb +2 -0
- data/lib/vagrant/environment.rb +136 -12
- data/lib/vagrant/errors.rb +15 -0
- data/lib/vagrant/provisioners.rb +1 -1
- data/lib/vagrant/provisioners/base.rb +4 -0
- data/lib/vagrant/provisioners/chef.rb +13 -11
- data/lib/vagrant/provisioners/{chef_server.rb → chef_client.rb} +5 -5
- data/lib/vagrant/provisioners/chef_solo.rb +48 -89
- data/lib/vagrant/provisioners/shell.rb +47 -12
- data/lib/vagrant/ssh.rb +61 -27
- data/lib/vagrant/systems.rb +1 -0
- data/lib/vagrant/systems/base.rb +1 -1
- data/lib/vagrant/systems/linux.rb +7 -9
- data/lib/vagrant/systems/redhat.rb +12 -4
- data/lib/vagrant/systems/solaris.rb +9 -4
- data/lib/vagrant/systems/suse.rb +9 -0
- data/lib/vagrant/ui.rb +12 -5
- data/lib/vagrant/util.rb +2 -2
- data/lib/vagrant/util/counter.rb +22 -0
- data/lib/vagrant/util/platform.rb +1 -2
- data/lib/vagrant/util/safe_exec.rb +28 -0
- data/lib/vagrant/version.rb +1 -1
- data/lib/vagrant/vm.rb +2 -0
- data/templates/chef_solo_solo.erb +4 -4
- data/templates/commands/init/Vagrantfile.erb +4 -0
- data/templates/locales/en.yml +31 -8
- data/templates/ssh_config.erb +6 -0
- data/test/test_helper.rb +5 -3
- data/test/vagrant/action/builder_test.rb +4 -0
- data/test/vagrant/action/vm/clear_forwarded_ports_test.rb +18 -38
- data/test/vagrant/action/vm/clear_shared_folders_test.rb +7 -16
- data/test/vagrant/action/vm/customize_test.rb +12 -5
- data/test/vagrant/action/vm/forward_ports_test.rb +12 -7
- data/test/vagrant/action/vm/match_mac_address_test.rb +5 -1
- data/test/vagrant/action/vm/modify_test.rb +38 -0
- data/test/vagrant/action/vm/provision_test.rb +13 -38
- data/test/vagrant/action/vm/provisioner_cleanup_test.rb +56 -0
- data/test/vagrant/action/vm/share_folders_test.rb +10 -5
- data/test/vagrant/action/warden_test.rb +13 -7
- data/test/vagrant/config/vm_test.rb +0 -22
- data/test/vagrant/downloaders/http_test.rb +2 -0
- data/test/vagrant/environment_test.rb +110 -20
- data/test/vagrant/provisioners/{chef_server_test.rb → chef_client_test.rb} +2 -2
- data/test/vagrant/provisioners/chef_solo_test.rb +16 -173
- data/test/vagrant/ssh_test.rb +8 -43
- data/test/vagrant/systems/linux_test.rb +9 -19
- data/test/vagrant/util/counter_test.rb +29 -0
- data/test/vagrant/util/platform_test.rb +2 -2
- data/vagrant.gemspec +1 -2
- metadata +114 -84
- data/lib/vagrant/util/plain_logger.rb +0 -25
- data/lib/vagrant/util/resource_logger.rb +0 -63
- data/test/vagrant/util/plain_logger_test.rb +0 -17
- data/test/vagrant/util/resource_logger_test.rb +0 -78
data/lib/vagrant/errors.rb
CHANGED
@@ -148,6 +148,21 @@ module Vagrant
|
|
148
148
|
error_key(:socket_error, "vagrant.downloaders.http")
|
149
149
|
end
|
150
150
|
|
151
|
+
class DownloaderHTTPStatusError < VagrantError
|
152
|
+
status_code(51)
|
153
|
+
error_key(:status_error, "vagrant.downloaders.http")
|
154
|
+
end
|
155
|
+
|
156
|
+
class EnvironmentLockedError < VagrantError
|
157
|
+
status_code(52)
|
158
|
+
error_key(:environment_locked)
|
159
|
+
end
|
160
|
+
|
161
|
+
class HomeDirectoryMigrationFailed < VagrantError
|
162
|
+
status_code(53)
|
163
|
+
error_key(:home_dir_migration_failed)
|
164
|
+
end
|
165
|
+
|
151
166
|
class ForwardPortAutolistEmpty < VagrantError
|
152
167
|
status_code(27)
|
153
168
|
error_key(:auto_empty, "vagrant.actions.vm.forward_ports")
|
data/lib/vagrant/provisioners.rb
CHANGED
@@ -2,7 +2,7 @@
|
|
2
2
|
# as configuration classes right away with Vagrant.
|
3
3
|
require 'vagrant/provisioners/base'
|
4
4
|
require 'vagrant/provisioners/chef'
|
5
|
-
require 'vagrant/provisioners/
|
5
|
+
require 'vagrant/provisioners/chef_client'
|
6
6
|
require 'vagrant/provisioners/chef_solo'
|
7
7
|
require 'vagrant/provisioners/puppet'
|
8
8
|
require 'vagrant/provisioners/puppet_server'
|
@@ -58,6 +58,10 @@ module Vagrant
|
|
58
58
|
# is expected to do whatever necessary to provision the system (create files,
|
59
59
|
# SSH, etc.)
|
60
60
|
def provision!; end
|
61
|
+
|
62
|
+
# This is the method called to when the system is being destroyed
|
63
|
+
# and allows the provisioners to engage in any cleanup tasks necessary.
|
64
|
+
def cleanup; end
|
61
65
|
end
|
62
66
|
end
|
63
67
|
end
|
@@ -59,7 +59,7 @@ module Vagrant
|
|
59
59
|
|
60
60
|
# Merge with the "extra data" which isn't put under the
|
61
61
|
# vagrant namespace by default
|
62
|
-
data.merge!(config.
|
62
|
+
data.merge!(config.merged_json)
|
63
63
|
|
64
64
|
json = data.to_json
|
65
65
|
|
@@ -76,6 +76,8 @@ module Vagrant
|
|
76
76
|
class Chef < Base
|
77
77
|
# This is the configuration which is available through `config.chef`
|
78
78
|
class Config < Vagrant::Config::Base
|
79
|
+
extend Util::Counter
|
80
|
+
|
79
81
|
# Shared config
|
80
82
|
attr_accessor :node_name
|
81
83
|
attr_accessor :provisioning_path
|
@@ -90,11 +92,12 @@ module Vagrant
|
|
90
92
|
attr_accessor :no_proxy
|
91
93
|
attr_accessor :binary_path
|
92
94
|
attr_accessor :binary_env
|
95
|
+
attr_accessor :run_list
|
93
96
|
|
94
97
|
def initialize
|
95
|
-
@provisioning_path = "/tmp/vagrant-chef"
|
98
|
+
@provisioning_path = "/tmp/vagrant-chef-#{self.class.get_and_update_counter}"
|
96
99
|
@log_level = :info
|
97
|
-
@json = {
|
100
|
+
@json = {}
|
98
101
|
@http_proxy = nil
|
99
102
|
@http_proxy_user = nil
|
100
103
|
@http_proxy_pass = nil
|
@@ -104,16 +107,15 @@ module Vagrant
|
|
104
107
|
@no_proxy = nil
|
105
108
|
@binary_path = nil
|
106
109
|
@binary_env = nil
|
110
|
+
@run_list = []
|
107
111
|
end
|
108
112
|
|
109
|
-
#
|
110
|
-
|
111
|
-
|
112
|
-
|
113
|
-
|
114
|
-
|
115
|
-
def run_list=(value)
|
116
|
-
json[:run_list] = value
|
113
|
+
# This returns the json that is merged with the defaults and the
|
114
|
+
# user set data.
|
115
|
+
def merged_json
|
116
|
+
{ :instance_role => "vagrant",
|
117
|
+
:run_list => run_list
|
118
|
+
}.merge(json || {})
|
117
119
|
end
|
118
120
|
|
119
121
|
# Adds a recipe to the run list
|
@@ -4,8 +4,8 @@ module Vagrant
|
|
4
4
|
module Provisioners
|
5
5
|
# This class implements provisioning via chef-client, allowing provisioning
|
6
6
|
# with a chef server.
|
7
|
-
class
|
8
|
-
register :
|
7
|
+
class ChefClient < Chef
|
8
|
+
register :chef_client
|
9
9
|
|
10
10
|
class Config < Chef::Config
|
11
11
|
attr_accessor :chef_server_url
|
@@ -34,7 +34,7 @@ module Vagrant
|
|
34
34
|
|
35
35
|
errors.add(I18n.t("vagrant.config.chef.server_url_empty")) if !chef_server_url || chef_server_url.strip == ""
|
36
36
|
errors.add(I18n.t("vagrant.config.chef.validation_key_path")) if !validation_key_path
|
37
|
-
errors.add(I18n.t("vagrant.config.chef.run_list_empty")) if
|
37
|
+
errors.add(I18n.t("vagrant.config.chef.run_list_empty")) if run_list && run_list.empty?
|
38
38
|
end
|
39
39
|
end
|
40
40
|
|
@@ -68,7 +68,7 @@ module Vagrant
|
|
68
68
|
env.ui.info I18n.t("vagrant.provisioners.chef.upload_validation_key")
|
69
69
|
vm.ssh.upload!(validation_key_path, guest_validation_key_path)
|
70
70
|
end
|
71
|
-
|
71
|
+
|
72
72
|
def upload_encrypted_data_bag_secret
|
73
73
|
env.ui.info I18n.t("vagrant.provisioners.chef.upload_encrypted_data_bag_secret_key")
|
74
74
|
vm.ssh.upload!(encrypted_data_bag_secret_key_path, config.encrypted_data_bag_secret)
|
@@ -107,7 +107,7 @@ module Vagrant
|
|
107
107
|
def validation_key_path
|
108
108
|
File.expand_path(config.validation_key_path, env.root_path)
|
109
109
|
end
|
110
|
-
|
110
|
+
|
111
111
|
def encrypted_data_bag_secret_key_path
|
112
112
|
File.expand_path(config.encrypted_data_bag_secret_key_path, env.root_path)
|
113
113
|
end
|
@@ -24,14 +24,22 @@ module Vagrant
|
|
24
24
|
super
|
25
25
|
|
26
26
|
errors.add(I18n.t("vagrant.config.chef.cookbooks_path_empty")) if !cookbooks_path || [cookbooks_path].flatten.empty?
|
27
|
-
errors.add(I18n.t("vagrant.config.chef.run_list_empty")) if !
|
27
|
+
errors.add(I18n.t("vagrant.config.chef.run_list_empty")) if !run_list || run_list.empty?
|
28
28
|
end
|
29
29
|
end
|
30
30
|
|
31
|
+
attr_reader :cookbook_folders
|
32
|
+
attr_reader :role_folders
|
33
|
+
attr_reader :data_bags_folders
|
34
|
+
|
31
35
|
def prepare
|
32
|
-
|
33
|
-
|
34
|
-
|
36
|
+
@cookbook_folders = expanded_folders(config.cookbooks_path)
|
37
|
+
@role_folders = expanded_folders(config.roles_path)
|
38
|
+
@data_bags_folders = expanded_folders(config.data_bags_path)
|
39
|
+
|
40
|
+
share_folders("csc", @cookbook_folders)
|
41
|
+
share_folders("csr", @role_folders)
|
42
|
+
share_folders("csdb", @data_bags_folders)
|
35
43
|
end
|
36
44
|
|
37
45
|
def provision!
|
@@ -42,25 +50,47 @@ module Vagrant
|
|
42
50
|
run_chef_solo
|
43
51
|
end
|
44
52
|
|
45
|
-
|
46
|
-
|
47
|
-
|
48
|
-
|
49
|
-
|
53
|
+
# Converts paths to a list of properly expanded paths with types.
|
54
|
+
def expanded_folders(paths)
|
55
|
+
# Convert the path to an array if it is a string or just a single
|
56
|
+
# path element which contains the folder location (:host or :vm)
|
57
|
+
paths = [paths] if paths.is_a?(String) || paths.first.is_a?(Symbol)
|
58
|
+
|
59
|
+
index = 0
|
60
|
+
paths.map do |path|
|
61
|
+
path = [:host, path] if !path.is_a?(Array)
|
62
|
+
type, path = path
|
63
|
+
|
64
|
+
# Create the local/remote path based on whether this is a host
|
65
|
+
# or VM path.
|
66
|
+
local_path = nil
|
67
|
+
local_path = File.expand_path(path, env.root_path) if type == :host
|
68
|
+
remote_path = type == :host ? "#{config.provisioning_path}/chef-solo-#{index}" : path
|
69
|
+
index += 1
|
50
70
|
|
51
|
-
|
52
|
-
|
53
|
-
env.config.vm.share_folder("v-csr-#{i}", role_path(i), role, :nfs => config.nfs)
|
71
|
+
# Return the result
|
72
|
+
[type, local_path, remote_path]
|
54
73
|
end
|
55
74
|
end
|
56
75
|
|
57
|
-
|
58
|
-
|
59
|
-
|
76
|
+
# Shares the given folders with the given prefix. The folders should
|
77
|
+
# be of the structure resulting from the `expanded_folders` function.
|
78
|
+
def share_folders(prefix, folders)
|
79
|
+
index = 0
|
80
|
+
folders.each do |type, local_path, remote_path|
|
81
|
+
if type == :host
|
82
|
+
env.config.vm.share_folder("v-#{prefix}-#{index}",
|
83
|
+
remote_path, local_path, :nfs => config.nfs)
|
84
|
+
index += 1
|
85
|
+
end
|
60
86
|
end
|
61
87
|
end
|
62
88
|
|
63
89
|
def setup_solo_config
|
90
|
+
cookbooks_path = guest_paths(@cookbook_folders)
|
91
|
+
roles_path = guest_paths(@role_folders)
|
92
|
+
data_bags_path = guest_paths(@data_bags_folders)
|
93
|
+
|
64
94
|
setup_config("chef_solo_solo", "solo.rb", {
|
65
95
|
:node_name => config.node_name,
|
66
96
|
:provisioning_path => config.provisioning_path,
|
@@ -87,80 +117,9 @@ module Vagrant
|
|
87
117
|
end
|
88
118
|
end
|
89
119
|
|
90
|
-
|
91
|
-
|
92
|
-
|
93
|
-
paths = [paths] if paths.is_a?(String) || paths.first.is_a?(Symbol)
|
94
|
-
|
95
|
-
paths.inject([]) do |acc, path|
|
96
|
-
path = [:host, path] if !path.is_a?(Array)
|
97
|
-
type, path = path
|
98
|
-
|
99
|
-
acc << File.expand_path(path, env.root_path) if type == :host
|
100
|
-
acc
|
101
|
-
end
|
102
|
-
end
|
103
|
-
|
104
|
-
def folder_path(*args)
|
105
|
-
File.join(config.provisioning_path, args.join("-"))
|
106
|
-
end
|
107
|
-
|
108
|
-
def folders_path(folders, folder)
|
109
|
-
# Convert single cookbook paths such as "cookbooks" or [:vm, "cookbooks"]
|
110
|
-
# into a proper array representation.
|
111
|
-
folders = [folders] if folders.is_a?(String) || folders.first.is_a?(Symbol)
|
112
|
-
|
113
|
-
# Convert each path to the proper absolute path depending on if the path
|
114
|
-
# is a host path or a VM path
|
115
|
-
result = []
|
116
|
-
folders.each_with_index do |path, i|
|
117
|
-
path = [:host, path] if !path.is_a?(Array)
|
118
|
-
type, path = path
|
119
|
-
|
120
|
-
result << folder_path(folder, i) if type == :host
|
121
|
-
result << folder_path(path) if type == :vm
|
122
|
-
end
|
123
|
-
|
124
|
-
# We're lucky that ruby's string and array syntax for strings is the
|
125
|
-
# same as JSON, so we can just convert to JSON here and use that
|
126
|
-
result = result[0].to_s if result.length == 1
|
127
|
-
result
|
128
|
-
end
|
129
|
-
|
130
|
-
def host_cookbook_paths
|
131
|
-
host_folder_paths(config.cookbooks_path)
|
132
|
-
end
|
133
|
-
|
134
|
-
def host_role_paths
|
135
|
-
host_folder_paths(config.roles_path)
|
136
|
-
end
|
137
|
-
|
138
|
-
def host_data_bag_paths
|
139
|
-
host_folder_paths(config.data_bags_path)
|
140
|
-
end
|
141
|
-
|
142
|
-
def cookbook_path(i)
|
143
|
-
folder_path("cookbooks", i)
|
144
|
-
end
|
145
|
-
|
146
|
-
def role_path(i)
|
147
|
-
folder_path("roles", i)
|
148
|
-
end
|
149
|
-
|
150
|
-
def data_bag_path(i)
|
151
|
-
folder_path("data_bags", i)
|
152
|
-
end
|
153
|
-
|
154
|
-
def cookbooks_path
|
155
|
-
folders_path(config.cookbooks_path, "cookbooks").to_json
|
156
|
-
end
|
157
|
-
|
158
|
-
def roles_path
|
159
|
-
folders_path(config.roles_path, "roles").to_json
|
160
|
-
end
|
161
|
-
|
162
|
-
def data_bags_path
|
163
|
-
folders_path(config.data_bags_path, "data_bags").to_json
|
120
|
+
# Extracts only the remote paths from a list of folders
|
121
|
+
def guest_paths(folders)
|
122
|
+
folders.map { |parts| parts[2] }
|
164
123
|
end
|
165
124
|
end
|
166
125
|
end
|
@@ -4,10 +4,13 @@ module Vagrant
|
|
4
4
|
register :shell
|
5
5
|
|
6
6
|
class Config < Vagrant::Config::Base
|
7
|
+
attr_accessor :inline
|
7
8
|
attr_accessor :path
|
8
9
|
attr_accessor :upload_path
|
9
10
|
|
10
11
|
def initialize
|
12
|
+
@inline = nil
|
13
|
+
@path = nil
|
11
14
|
@upload_path = "/tmp/vagrant-shell"
|
12
15
|
end
|
13
16
|
|
@@ -18,31 +21,63 @@ module Vagrant
|
|
18
21
|
def validate(errors)
|
19
22
|
super
|
20
23
|
|
21
|
-
|
22
|
-
|
23
|
-
|
24
|
+
# Validate that the parameters are properly set
|
25
|
+
if path && inline
|
26
|
+
errors.add(I18n.t("vagrant.provisioners.shell.path_and_inline_set"))
|
27
|
+
elsif !path && !inline
|
28
|
+
errors.add(I18n.t("vagrant.provisioners.shell.no_path_or_inline"))
|
29
|
+
end
|
30
|
+
|
31
|
+
# Validate the existence of a script to upload
|
32
|
+
if path && !expanded_path.file?
|
24
33
|
errors.add(I18n.t("vagrant.provisioners.shell.path_invalid", :path => expanded_path))
|
25
34
|
end
|
26
35
|
|
36
|
+
# There needs to be a path to upload the script to
|
27
37
|
if !upload_path
|
28
38
|
errors.add(I18n.t("vagrant.provisioners.shell.upload_path_not_set"))
|
29
39
|
end
|
30
40
|
end
|
31
41
|
end
|
32
42
|
|
43
|
+
# This method yields the path to a script to upload and execute
|
44
|
+
# on the remote server. This method will properly clean up the
|
45
|
+
# script file if needed.
|
46
|
+
def with_script_file
|
47
|
+
if config.path
|
48
|
+
# Just yield the path to that file...
|
49
|
+
yield config.expanded_path
|
50
|
+
return
|
51
|
+
end
|
52
|
+
|
53
|
+
# Otherwise we have an inline script, we need to Tempfile it,
|
54
|
+
# and handle it specially...
|
55
|
+
file = Tempfile.new('vagrant-shell')
|
56
|
+
begin
|
57
|
+
file.write(config.inline)
|
58
|
+
file.fsync
|
59
|
+
yield file.path
|
60
|
+
ensure
|
61
|
+
file.close
|
62
|
+
file.unlink
|
63
|
+
end
|
64
|
+
end
|
65
|
+
|
33
66
|
def provision!
|
34
67
|
commands = ["chmod +x #{config.upload_path}", config.upload_path]
|
35
68
|
|
36
|
-
|
37
|
-
|
69
|
+
with_script_file do |path|
|
70
|
+
# Upload the script to the VM
|
71
|
+
vm.ssh.upload!(path.to_s, config.upload_path)
|
38
72
|
|
39
|
-
|
40
|
-
|
41
|
-
|
42
|
-
|
43
|
-
|
44
|
-
|
45
|
-
|
73
|
+
# Execute it with sudo
|
74
|
+
vm.ssh.execute do |ssh|
|
75
|
+
ssh.sudo!(commands) do |ch, type, data|
|
76
|
+
if type == :exit_status
|
77
|
+
ssh.check_exit_status(data, commands)
|
78
|
+
else
|
79
|
+
env.ui.info(data)
|
80
|
+
end
|
46
81
|
end
|
47
82
|
end
|
48
83
|
end
|
data/lib/vagrant/ssh.rb
CHANGED
@@ -1,16 +1,17 @@
|
|
1
|
-
require 'timeout'
|
2
1
|
require 'net/ssh'
|
3
2
|
require 'net/scp'
|
4
|
-
require 'mario'
|
5
|
-
|
6
|
-
require 'vagrant/ssh/session'
|
7
3
|
|
8
4
|
module Vagrant
|
9
5
|
# Manages SSH access to a specific environment. Allows an environment to
|
10
6
|
# replace the process with SSH itself, run a specific set of commands,
|
11
7
|
# upload files, or even check if a host is up.
|
12
8
|
class SSH
|
9
|
+
# Autoload this guy because he is really only used in one location
|
10
|
+
# and not for every Vagrant command.
|
11
|
+
autoload :Session, 'vagrant/ssh/session'
|
12
|
+
|
13
13
|
include Util::Retryable
|
14
|
+
include Util::SafeExec
|
14
15
|
|
15
16
|
# Reference back up to the environment which this SSH object belongs
|
16
17
|
# to
|
@@ -18,13 +19,14 @@ module Vagrant
|
|
18
19
|
|
19
20
|
def initialize(environment)
|
20
21
|
@env = environment
|
22
|
+
@current_session = nil
|
21
23
|
end
|
22
24
|
|
23
25
|
# Connects to the environment's virtual machine, replacing the ruby
|
24
26
|
# process with an SSH process. This method optionally takes a hash
|
25
27
|
# of options which override the configuration values.
|
26
28
|
def connect(opts={})
|
27
|
-
if
|
29
|
+
if Util::Platform.windows?
|
28
30
|
raise Errors::SSHUnavailableWindows, :key_path => env.config.ssh.private_key_path,
|
29
31
|
:ssh_port => port(opts)
|
30
32
|
end
|
@@ -54,10 +56,9 @@ module Vagrant
|
|
54
56
|
# Some hackery going on here. On Mac OS X Leopard (10.5), exec fails
|
55
57
|
# (GH-51). As a workaround, we fork and wait. On all other platforms,
|
56
58
|
# we simply exec.
|
57
|
-
|
58
|
-
|
59
|
-
|
60
|
-
Process.wait(pid) if pid
|
59
|
+
command = "ssh #{command_options.join(" ")} #{options[:username]}@#{options[:host]}".strip
|
60
|
+
env.logger.info("ssh") { "Invoking SSH: #{command}" }
|
61
|
+
safe_exec(command)
|
61
62
|
end
|
62
63
|
|
63
64
|
# Opens an SSH connection to this environment's virtual machine and yields
|
@@ -71,17 +72,37 @@ module Vagrant
|
|
71
72
|
opts[:forward_agent] = true if env.config.ssh.forward_agent
|
72
73
|
opts[:port] ||= port
|
73
74
|
|
74
|
-
|
75
|
-
|
76
|
-
|
77
|
-
|
78
|
-
|
79
|
-
|
80
|
-
|
81
|
-
|
82
|
-
|
75
|
+
# Check if we have a currently open SSH session which has the
|
76
|
+
# same options, and use that if possible
|
77
|
+
session, options = @current_session
|
78
|
+
|
79
|
+
if !session || options != opts
|
80
|
+
env.logger.info("ssh") { "Connecting to SSH: #{env.config.ssh.host} #{opts[:port]}" }
|
81
|
+
|
82
|
+
# The exceptions which are acceptable to retry on during
|
83
|
+
# attempts to connect to SSH
|
84
|
+
exceptions = [Errno::ECONNREFUSED, Net::SSH::Disconnect]
|
85
|
+
|
86
|
+
# Connect to SSH and gather the session
|
87
|
+
session = retryable(:tries => 5, :on => exceptions) do
|
88
|
+
connection = Net::SSH.start(env.config.ssh.host,
|
89
|
+
env.config.ssh.username,
|
90
|
+
opts.merge( :keys => [env.config.ssh.private_key_path],
|
91
|
+
:keys_only => true,
|
92
|
+
:user_known_hosts_file => [],
|
93
|
+
:paranoid => false,
|
94
|
+
:config => false))
|
95
|
+
SSH::Session.new(connection, env)
|
83
96
|
end
|
97
|
+
|
98
|
+
# Save the new session along with the options which created it
|
99
|
+
@current_session = [session, opts]
|
100
|
+
else
|
101
|
+
env.logger.info("ssh") { "Using cached SSH session: #{session}" }
|
84
102
|
end
|
103
|
+
|
104
|
+
# Yield our session for executing
|
105
|
+
return yield session if block_given?
|
85
106
|
rescue Errno::ECONNREFUSED
|
86
107
|
raise Errors::SSHConnectionRefused
|
87
108
|
end
|
@@ -107,6 +128,7 @@ module Vagrant
|
|
107
128
|
# Windows
|
108
129
|
ssh_port = port
|
109
130
|
|
131
|
+
require 'timeout'
|
110
132
|
Timeout.timeout(env.config.ssh.timeout) do
|
111
133
|
execute(:timeout => env.config.ssh.timeout,
|
112
134
|
:port => ssh_port) { |ssh| }
|
@@ -124,13 +146,16 @@ module Vagrant
|
|
124
146
|
# if needed, or on failure erroring.
|
125
147
|
def check_key_permissions(key_path)
|
126
148
|
# Windows systems don't have this issue
|
127
|
-
return if
|
149
|
+
return if Util::Platform.windows?
|
150
|
+
|
151
|
+
env.logger.info("ssh") { "Checking key permissions: #{key_path}" }
|
128
152
|
|
129
153
|
stat = File.stat(key_path)
|
130
154
|
|
131
155
|
if stat.owned? && file_perms(key_path) != "600"
|
132
|
-
|
156
|
+
env.logger.info("ssh") { "Attempting to correct key permissions to 0600" }
|
133
157
|
|
158
|
+
File.chmod(0600, key_path)
|
134
159
|
raise Errors::SSHKeyBadPermissions, :key_path => key_path if file_perms(key_path) != "600"
|
135
160
|
end
|
136
161
|
rescue Errno::EPERM
|
@@ -151,23 +176,32 @@ module Vagrant
|
|
151
176
|
# `config.ssh.forwarded_port_key`.
|
152
177
|
def port(opts={})
|
153
178
|
# Check if port was specified in options hash
|
154
|
-
|
155
|
-
return pnum if pnum
|
179
|
+
return opts[:port] if opts[:port]
|
156
180
|
|
157
181
|
# Check if a port was specified in the config
|
158
182
|
return env.config.ssh.port if env.config.ssh.port
|
159
|
-
|
183
|
+
|
160
184
|
# Check if we have an SSH forwarded port
|
161
|
-
|
185
|
+
pnum_by_name = nil
|
186
|
+
pnum_by_destination = nil
|
162
187
|
env.vm.vm.network_adapters.each do |na|
|
163
|
-
|
188
|
+
# Look for the port number by name...
|
189
|
+
pnum_by_name = na.nat_driver.forwarded_ports.detect do |fp|
|
164
190
|
fp.name == env.config.ssh.forwarded_port_key
|
165
191
|
end
|
166
192
|
|
167
|
-
|
193
|
+
# Look for the port number by destination...
|
194
|
+
pnum_by_destination = na.nat_driver.forwarded_ports.detect do |fp|
|
195
|
+
fp.guestport == env.config.ssh.forwarded_port_destination
|
196
|
+
end
|
197
|
+
|
198
|
+
# pnum_by_name is what we're looking for here, so break early
|
199
|
+
# if we have it.
|
200
|
+
break if pnum_by_name
|
168
201
|
end
|
169
202
|
|
170
|
-
return
|
203
|
+
return pnum_by_name.hostport if pnum_by_name
|
204
|
+
return pnum_by_destination.hostport if pnum_by_destination
|
171
205
|
|
172
206
|
# This should NEVER happen.
|
173
207
|
raise Errors::SSHPortNotDetected
|