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.
Files changed (79) hide show
  1. data/CHANGELOG.md +39 -0
  2. data/Gemfile +1 -7
  3. data/Rakefile +0 -11
  4. data/bin/vagrant +4 -0
  5. data/config/default.rb +1 -2
  6. data/lib/vagrant.rb +7 -5
  7. data/lib/vagrant/action.rb +5 -1
  8. data/lib/vagrant/action/builtin.rb +4 -1
  9. data/lib/vagrant/action/general/package.rb +6 -2
  10. data/lib/vagrant/action/vm.rb +2 -0
  11. data/lib/vagrant/action/vm/clear_forwarded_ports.rb +9 -22
  12. data/lib/vagrant/action/vm/clear_shared_folders.rb +9 -14
  13. data/lib/vagrant/action/vm/customize.rb +9 -4
  14. data/lib/vagrant/action/vm/forward_ports.rb +10 -11
  15. data/lib/vagrant/action/vm/match_mac_address.rb +8 -3
  16. data/lib/vagrant/action/vm/modify.rb +37 -0
  17. data/lib/vagrant/action/vm/network.rb +9 -2
  18. data/lib/vagrant/action/vm/provision.rb +10 -17
  19. data/lib/vagrant/action/vm/provisioner_cleanup.rb +26 -0
  20. data/lib/vagrant/action/vm/share_folders.rb +16 -8
  21. data/lib/vagrant/action/warden.rb +8 -2
  22. data/lib/vagrant/command/ssh.rb +4 -4
  23. data/lib/vagrant/command/ssh_config.rb +4 -2
  24. data/lib/vagrant/config/ssh.rb +3 -0
  25. data/lib/vagrant/config/vm.rb +16 -12
  26. data/lib/vagrant/downloaders/http.rb +2 -0
  27. data/lib/vagrant/environment.rb +136 -12
  28. data/lib/vagrant/errors.rb +15 -0
  29. data/lib/vagrant/provisioners.rb +1 -1
  30. data/lib/vagrant/provisioners/base.rb +4 -0
  31. data/lib/vagrant/provisioners/chef.rb +13 -11
  32. data/lib/vagrant/provisioners/{chef_server.rb → chef_client.rb} +5 -5
  33. data/lib/vagrant/provisioners/chef_solo.rb +48 -89
  34. data/lib/vagrant/provisioners/shell.rb +47 -12
  35. data/lib/vagrant/ssh.rb +61 -27
  36. data/lib/vagrant/systems.rb +1 -0
  37. data/lib/vagrant/systems/base.rb +1 -1
  38. data/lib/vagrant/systems/linux.rb +7 -9
  39. data/lib/vagrant/systems/redhat.rb +12 -4
  40. data/lib/vagrant/systems/solaris.rb +9 -4
  41. data/lib/vagrant/systems/suse.rb +9 -0
  42. data/lib/vagrant/ui.rb +12 -5
  43. data/lib/vagrant/util.rb +2 -2
  44. data/lib/vagrant/util/counter.rb +22 -0
  45. data/lib/vagrant/util/platform.rb +1 -2
  46. data/lib/vagrant/util/safe_exec.rb +28 -0
  47. data/lib/vagrant/version.rb +1 -1
  48. data/lib/vagrant/vm.rb +2 -0
  49. data/templates/chef_solo_solo.erb +4 -4
  50. data/templates/commands/init/Vagrantfile.erb +4 -0
  51. data/templates/locales/en.yml +31 -8
  52. data/templates/ssh_config.erb +6 -0
  53. data/test/test_helper.rb +5 -3
  54. data/test/vagrant/action/builder_test.rb +4 -0
  55. data/test/vagrant/action/vm/clear_forwarded_ports_test.rb +18 -38
  56. data/test/vagrant/action/vm/clear_shared_folders_test.rb +7 -16
  57. data/test/vagrant/action/vm/customize_test.rb +12 -5
  58. data/test/vagrant/action/vm/forward_ports_test.rb +12 -7
  59. data/test/vagrant/action/vm/match_mac_address_test.rb +5 -1
  60. data/test/vagrant/action/vm/modify_test.rb +38 -0
  61. data/test/vagrant/action/vm/provision_test.rb +13 -38
  62. data/test/vagrant/action/vm/provisioner_cleanup_test.rb +56 -0
  63. data/test/vagrant/action/vm/share_folders_test.rb +10 -5
  64. data/test/vagrant/action/warden_test.rb +13 -7
  65. data/test/vagrant/config/vm_test.rb +0 -22
  66. data/test/vagrant/downloaders/http_test.rb +2 -0
  67. data/test/vagrant/environment_test.rb +110 -20
  68. data/test/vagrant/provisioners/{chef_server_test.rb → chef_client_test.rb} +2 -2
  69. data/test/vagrant/provisioners/chef_solo_test.rb +16 -173
  70. data/test/vagrant/ssh_test.rb +8 -43
  71. data/test/vagrant/systems/linux_test.rb +9 -19
  72. data/test/vagrant/util/counter_test.rb +29 -0
  73. data/test/vagrant/util/platform_test.rb +2 -2
  74. data/vagrant.gemspec +1 -2
  75. metadata +114 -84
  76. data/lib/vagrant/util/plain_logger.rb +0 -25
  77. data/lib/vagrant/util/resource_logger.rb +0 -63
  78. data/test/vagrant/util/plain_logger_test.rb +0 -17
  79. data/test/vagrant/util/resource_logger_test.rb +0 -78
@@ -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")
@@ -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/chef_server'
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.json)
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 = { :instance_role => "vagrant" }
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
- # Returns the run list for the provisioning
110
- def run_list
111
- json[:run_list] ||= []
112
- end
113
-
114
- # Sets the run list to the specified value
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 ChefServer < Chef
8
- register :chef_server
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 json[:run_list] && run_list.empty?
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 !json[:run_list] || run_list.empty?
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
- share_cookbook_folders
33
- share_role_folders
34
- share_data_bags_folders
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
- def share_cookbook_folders
46
- host_cookbook_paths.each_with_index do |cookbook, i|
47
- env.config.vm.share_folder("v-csc-#{i}", cookbook_path(i), cookbook, :nfs => config.nfs)
48
- end
49
- end
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
- def share_role_folders
52
- host_role_paths.each_with_index do |role, i|
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
- def share_data_bags_folders
58
- host_data_bag_paths.each_with_index do |data_bag, i|
59
- env.config.vm.share_folder("v-csdb-#{i}", data_bag_path(i), data_bag, :nfs => config.nfs)
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
- def host_folder_paths(paths)
91
- # Convert single cookbook paths such as "cookbooks" or [:vm, "cookbooks"]
92
- # into a proper array representation.
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
- if !path
22
- errors.add(I18n.t("vagrant.provisioners.shell.path_not_set"))
23
- elsif !expanded_path.file?
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
- # Upload the script to the VM
37
- vm.ssh.upload!(config.expanded_path.to_s, config.upload_path)
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
- # Execute it with sudo
40
- vm.ssh.execute do |ssh|
41
- ssh.sudo!(commands) do |ch, type, data|
42
- if type == :exit_status
43
- ssh.check_exit_status(data, commands)
44
- else
45
- env.ui.info(data)
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
@@ -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 Mario::Platform.windows?
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
- pid = nil
58
- pid = fork if Util::Platform.leopard? || Util::Platform.tiger?
59
- Kernel.exec "ssh #{command_options.join(" ")} #{options[:username]}@#{options[:host]}".strip if pid.nil?
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
- retryable(:tries => 5, :on => Errno::ECONNREFUSED) do
75
- Net::SSH.start(env.config.ssh.host,
76
- env.config.ssh.username,
77
- opts.merge( :keys => [env.config.ssh.private_key_path],
78
- :keys_only => true,
79
- :user_known_hosts_file => [],
80
- :paranoid => false,
81
- :config => false)) do |ssh|
82
- yield SSH::Session.new(ssh, env)
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 Mario::Platform.windows?
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
- File.chmod(0600, key_path)
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
- pnum = opts[:port]
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
- pnum = nil
185
+ pnum_by_name = nil
186
+ pnum_by_destination = nil
162
187
  env.vm.vm.network_adapters.each do |na|
163
- pnum = na.nat_driver.forwarded_ports.detect do |fp|
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
- break if pnum
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 pnum.hostport if pnum
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