rvagrant 0.8.7.dev

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 (251) hide show
  1. data/.gitignore +25 -0
  2. data/.yardopts +1 -0
  3. data/CHANGELOG.md +386 -0
  4. data/Gemfile +21 -0
  5. data/LICENSE +21 -0
  6. data/README.md +61 -0
  7. data/Rakefile +11 -0
  8. data/bin/vagrant +28 -0
  9. data/config/default.rb +38 -0
  10. data/contrib/README.md +12 -0
  11. data/contrib/emacs/vagrant.el +8 -0
  12. data/contrib/vim/vagrantfile.vim +9 -0
  13. data/keys/README.md +17 -0
  14. data/keys/vagrant +27 -0
  15. data/keys/vagrant.ppk +26 -0
  16. data/keys/vagrant.pub +1 -0
  17. data/lib/vagrant.rb +44 -0
  18. data/lib/vagrant/action.rb +138 -0
  19. data/lib/vagrant/action/box.rb +11 -0
  20. data/lib/vagrant/action/box/destroy.rb +21 -0
  21. data/lib/vagrant/action/box/download.rb +72 -0
  22. data/lib/vagrant/action/box/package.rb +19 -0
  23. data/lib/vagrant/action/box/unpackage.rb +55 -0
  24. data/lib/vagrant/action/box/verify.rb +23 -0
  25. data/lib/vagrant/action/builder.rb +124 -0
  26. data/lib/vagrant/action/builtin.rb +108 -0
  27. data/lib/vagrant/action/env.rb +7 -0
  28. data/lib/vagrant/action/env/set.rb +18 -0
  29. data/lib/vagrant/action/environment.rb +50 -0
  30. data/lib/vagrant/action/general.rb +8 -0
  31. data/lib/vagrant/action/general/package.rb +113 -0
  32. data/lib/vagrant/action/general/validate.rb +19 -0
  33. data/lib/vagrant/action/vm.rb +34 -0
  34. data/lib/vagrant/action/vm/boot.rb +48 -0
  35. data/lib/vagrant/action/vm/check_accessible.rb +23 -0
  36. data/lib/vagrant/action/vm/check_box.rb +28 -0
  37. data/lib/vagrant/action/vm/check_guest_additions.rb +35 -0
  38. data/lib/vagrant/action/vm/clean_machine_folder.rb +43 -0
  39. data/lib/vagrant/action/vm/clear_forwarded_ports.rb +26 -0
  40. data/lib/vagrant/action/vm/clear_nfs_exports.rb +20 -0
  41. data/lib/vagrant/action/vm/clear_shared_folders.rb +27 -0
  42. data/lib/vagrant/action/vm/customize.rb +26 -0
  43. data/lib/vagrant/action/vm/destroy.rb +19 -0
  44. data/lib/vagrant/action/vm/destroy_unused_network_interfaces.rb +30 -0
  45. data/lib/vagrant/action/vm/discard_state.rb +22 -0
  46. data/lib/vagrant/action/vm/export.rb +52 -0
  47. data/lib/vagrant/action/vm/forward_ports.rb +133 -0
  48. data/lib/vagrant/action/vm/forward_ports_helpers.rb +28 -0
  49. data/lib/vagrant/action/vm/halt.rb +29 -0
  50. data/lib/vagrant/action/vm/host_name.rb +21 -0
  51. data/lib/vagrant/action/vm/import.rb +40 -0
  52. data/lib/vagrant/action/vm/match_mac_address.rb +26 -0
  53. data/lib/vagrant/action/vm/modify.rb +37 -0
  54. data/lib/vagrant/action/vm/network.rb +146 -0
  55. data/lib/vagrant/action/vm/nfs.rb +160 -0
  56. data/lib/vagrant/action/vm/nfs_helpers.rb +11 -0
  57. data/lib/vagrant/action/vm/package.rb +23 -0
  58. data/lib/vagrant/action/vm/package_vagrantfile.rb +33 -0
  59. data/lib/vagrant/action/vm/provision.rb +34 -0
  60. data/lib/vagrant/action/vm/provisioner_cleanup.rb +26 -0
  61. data/lib/vagrant/action/vm/resume.rb +20 -0
  62. data/lib/vagrant/action/vm/share_folders.rb +78 -0
  63. data/lib/vagrant/action/vm/suspend.rb +20 -0
  64. data/lib/vagrant/action/warden.rb +85 -0
  65. data/lib/vagrant/box.rb +90 -0
  66. data/lib/vagrant/box_collection.rb +53 -0
  67. data/lib/vagrant/cli.rb +55 -0
  68. data/lib/vagrant/command.rb +25 -0
  69. data/lib/vagrant/command/base.rb +106 -0
  70. data/lib/vagrant/command/box.rb +33 -0
  71. data/lib/vagrant/command/destroy.rb +17 -0
  72. data/lib/vagrant/command/group_base.rb +107 -0
  73. data/lib/vagrant/command/halt.rb +18 -0
  74. data/lib/vagrant/command/helpers.rb +33 -0
  75. data/lib/vagrant/command/init.rb +14 -0
  76. data/lib/vagrant/command/named_base.rb +14 -0
  77. data/lib/vagrant/command/package.rb +41 -0
  78. data/lib/vagrant/command/provision.rb +23 -0
  79. data/lib/vagrant/command/reload.rb +17 -0
  80. data/lib/vagrant/command/resume.rb +17 -0
  81. data/lib/vagrant/command/ssh.rb +49 -0
  82. data/lib/vagrant/command/ssh_config.rb +29 -0
  83. data/lib/vagrant/command/status.rb +31 -0
  84. data/lib/vagrant/command/suspend.rb +17 -0
  85. data/lib/vagrant/command/up.rb +19 -0
  86. data/lib/vagrant/command/upgrade_to_060.rb +45 -0
  87. data/lib/vagrant/command/version.rb +13 -0
  88. data/lib/vagrant/config.rb +123 -0
  89. data/lib/vagrant/config/base.rb +85 -0
  90. data/lib/vagrant/config/error_recorder.rb +19 -0
  91. data/lib/vagrant/config/nfs.rb +10 -0
  92. data/lib/vagrant/config/package.rb +9 -0
  93. data/lib/vagrant/config/ssh.rb +38 -0
  94. data/lib/vagrant/config/top.rb +61 -0
  95. data/lib/vagrant/config/vagrant.rb +17 -0
  96. data/lib/vagrant/config/vm.rb +148 -0
  97. data/lib/vagrant/config/vm/provisioner.rb +56 -0
  98. data/lib/vagrant/config/vm/sub_vm.rb +17 -0
  99. data/lib/vagrant/data_store.rb +70 -0
  100. data/lib/vagrant/downloaders.rb +7 -0
  101. data/lib/vagrant/downloaders/base.rb +23 -0
  102. data/lib/vagrant/downloaders/file.rb +22 -0
  103. data/lib/vagrant/downloaders/http.rb +70 -0
  104. data/lib/vagrant/environment.rb +516 -0
  105. data/lib/vagrant/errors.rb +352 -0
  106. data/lib/vagrant/hosts.rb +9 -0
  107. data/lib/vagrant/hosts/arch.rb +27 -0
  108. data/lib/vagrant/hosts/base.rb +76 -0
  109. data/lib/vagrant/hosts/bsd.rb +58 -0
  110. data/lib/vagrant/hosts/fedora.rb +11 -0
  111. data/lib/vagrant/hosts/linux.rb +69 -0
  112. data/lib/vagrant/plugin.rb +79 -0
  113. data/lib/vagrant/provisioners.rb +9 -0
  114. data/lib/vagrant/provisioners/base.rb +67 -0
  115. data/lib/vagrant/provisioners/chef.rb +155 -0
  116. data/lib/vagrant/provisioners/chef_client.rb +120 -0
  117. data/lib/vagrant/provisioners/chef_solo.rb +135 -0
  118. data/lib/vagrant/provisioners/puppet.rb +137 -0
  119. data/lib/vagrant/provisioners/puppet_server.rb +55 -0
  120. data/lib/vagrant/provisioners/shell.rb +96 -0
  121. data/lib/vagrant/ssh.rb +224 -0
  122. data/lib/vagrant/ssh/session.rb +136 -0
  123. data/lib/vagrant/systems.rb +13 -0
  124. data/lib/vagrant/systems/arch.rb +34 -0
  125. data/lib/vagrant/systems/base.rb +87 -0
  126. data/lib/vagrant/systems/debian.rb +36 -0
  127. data/lib/vagrant/systems/freebsd.rb +84 -0
  128. data/lib/vagrant/systems/gentoo.rb +27 -0
  129. data/lib/vagrant/systems/linux.rb +82 -0
  130. data/lib/vagrant/systems/linux/config.rb +21 -0
  131. data/lib/vagrant/systems/linux/error.rb +9 -0
  132. data/lib/vagrant/systems/redhat.rb +48 -0
  133. data/lib/vagrant/systems/solaris.rb +67 -0
  134. data/lib/vagrant/systems/suse.rb +9 -0
  135. data/lib/vagrant/systems/ubuntu.rb +17 -0
  136. data/lib/vagrant/test_helpers.rb +128 -0
  137. data/lib/vagrant/ui.rb +81 -0
  138. data/lib/vagrant/util.rb +13 -0
  139. data/lib/vagrant/util/busy.rb +59 -0
  140. data/lib/vagrant/util/counter.rb +24 -0
  141. data/lib/vagrant/util/hash_with_indifferent_access.rb +63 -0
  142. data/lib/vagrant/util/platform.rb +57 -0
  143. data/lib/vagrant/util/retryable.rb +25 -0
  144. data/lib/vagrant/util/safe_exec.rb +35 -0
  145. data/lib/vagrant/util/stacked_proc_runner.rb +35 -0
  146. data/lib/vagrant/util/template_renderer.rb +83 -0
  147. data/lib/vagrant/version.rb +6 -0
  148. data/lib/vagrant/vm.rb +181 -0
  149. data/templates/chef_server_client.erb +32 -0
  150. data/templates/chef_solo_solo.erb +23 -0
  151. data/templates/commands/init/Vagrantfile.erb +86 -0
  152. data/templates/config/validation_failed.erb +7 -0
  153. data/templates/locales/en.yml +556 -0
  154. data/templates/network_entry_arch.erb +9 -0
  155. data/templates/network_entry_debian.erb +8 -0
  156. data/templates/network_entry_gentoo.erb +5 -0
  157. data/templates/network_entry_redhat.erb +9 -0
  158. data/templates/nfs/exports.erb +5 -0
  159. data/templates/nfs/exports_linux.erb +5 -0
  160. data/templates/package_Vagrantfile.erb +11 -0
  161. data/templates/ssh_config.erb +15 -0
  162. data/test/unit/locales/en.yml +8 -0
  163. data/test/unit/test_helper.rb +28 -0
  164. data/test/unit/vagrant/action/box/destroy_test.rb +18 -0
  165. data/test/unit/vagrant/action/box/download_test.rb +125 -0
  166. data/test/unit/vagrant/action/box/package_test.rb +25 -0
  167. data/test/unit/vagrant/action/box/unpackage_test.rb +84 -0
  168. data/test/unit/vagrant/action/box/verify_test.rb +30 -0
  169. data/test/unit/vagrant/action/builder_test.rb +207 -0
  170. data/test/unit/vagrant/action/env/set_test.rb +24 -0
  171. data/test/unit/vagrant/action/environment_test.rb +27 -0
  172. data/test/unit/vagrant/action/general/package_test.rb +268 -0
  173. data/test/unit/vagrant/action/general/validate_test.rb +31 -0
  174. data/test/unit/vagrant/action/vm/boot_test.rb +66 -0
  175. data/test/unit/vagrant/action/vm/check_accessible_test.rb +61 -0
  176. data/test/unit/vagrant/action/vm/check_box_test.rb +56 -0
  177. data/test/unit/vagrant/action/vm/check_guest_additions_test.rb +9 -0
  178. data/test/unit/vagrant/action/vm/clean_machine_folder_test.rb +84 -0
  179. data/test/unit/vagrant/action/vm/clear_forwarded_ports_test.rb +52 -0
  180. data/test/unit/vagrant/action/vm/clear_nfs_exports_test.rb +22 -0
  181. data/test/unit/vagrant/action/vm/clear_shared_folders_test.rb +40 -0
  182. data/test/unit/vagrant/action/vm/customize_test.rb +37 -0
  183. data/test/unit/vagrant/action/vm/destroy_test.rb +25 -0
  184. data/test/unit/vagrant/action/vm/destroy_unused_network_interfaces_test.rb +49 -0
  185. data/test/unit/vagrant/action/vm/discard_state_test.rb +45 -0
  186. data/test/unit/vagrant/action/vm/export_test.rb +107 -0
  187. data/test/unit/vagrant/action/vm/forward_ports_helpers_test.rb +77 -0
  188. data/test/unit/vagrant/action/vm/forward_ports_test.rb +213 -0
  189. data/test/unit/vagrant/action/vm/halt_test.rb +79 -0
  190. data/test/unit/vagrant/action/vm/host_name_test.rb +36 -0
  191. data/test/unit/vagrant/action/vm/import_test.rb +66 -0
  192. data/test/unit/vagrant/action/vm/match_mac_address_test.rb +40 -0
  193. data/test/unit/vagrant/action/vm/modify_test.rb +38 -0
  194. data/test/unit/vagrant/action/vm/network_test.rb +286 -0
  195. data/test/unit/vagrant/action/vm/nfs_helpers_test.rb +26 -0
  196. data/test/unit/vagrant/action/vm/nfs_test.rb +260 -0
  197. data/test/unit/vagrant/action/vm/package_test.rb +25 -0
  198. data/test/unit/vagrant/action/vm/package_vagrantfile_test.rb +46 -0
  199. data/test/unit/vagrant/action/vm/provision_test.rb +65 -0
  200. data/test/unit/vagrant/action/vm/provisioner_cleanup_test.rb +56 -0
  201. data/test/unit/vagrant/action/vm/resume_test.rb +35 -0
  202. data/test/unit/vagrant/action/vm/share_folders_test.rb +144 -0
  203. data/test/unit/vagrant/action/vm/suspend_test.rb +35 -0
  204. data/test/unit/vagrant/action/warden_test.rb +125 -0
  205. data/test/unit/vagrant/action_test.rb +89 -0
  206. data/test/unit/vagrant/box_collection_test.rb +45 -0
  207. data/test/unit/vagrant/box_test.rb +74 -0
  208. data/test/unit/vagrant/cli_test.rb +35 -0
  209. data/test/unit/vagrant/command/base_test.rb +23 -0
  210. data/test/unit/vagrant/command/group_base_test.rb +15 -0
  211. data/test/unit/vagrant/command/helpers_test.rb +88 -0
  212. data/test/unit/vagrant/command/package_test.rb +27 -0
  213. data/test/unit/vagrant/config/base_test.rb +52 -0
  214. data/test/unit/vagrant/config/error_recorder_test.rb +18 -0
  215. data/test/unit/vagrant/config/ssh_test.rb +12 -0
  216. data/test/unit/vagrant/config/vagrant_test.rb +35 -0
  217. data/test/unit/vagrant/config/vm/provisioner_test.rb +92 -0
  218. data/test/unit/vagrant/config/vm_test.rb +56 -0
  219. data/test/unit/vagrant/config_test.rb +162 -0
  220. data/test/unit/vagrant/data_store_test.rb +77 -0
  221. data/test/unit/vagrant/downloaders/base_test.rb +28 -0
  222. data/test/unit/vagrant/downloaders/file_test.rb +48 -0
  223. data/test/unit/vagrant/downloaders/http_test.rb +82 -0
  224. data/test/unit/vagrant/environment_test.rb +598 -0
  225. data/test/unit/vagrant/errors_test.rb +42 -0
  226. data/test/unit/vagrant/hosts/base_test.rb +46 -0
  227. data/test/unit/vagrant/hosts/bsd_test.rb +53 -0
  228. data/test/unit/vagrant/hosts/linux_test.rb +54 -0
  229. data/test/unit/vagrant/plugin_test.rb +9 -0
  230. data/test/unit/vagrant/provisioners/base_test.rb +63 -0
  231. data/test/unit/vagrant/provisioners/chef_client_test.rb +190 -0
  232. data/test/unit/vagrant/provisioners/chef_solo_test.rb +114 -0
  233. data/test/unit/vagrant/provisioners/chef_test.rb +202 -0
  234. data/test/unit/vagrant/provisioners/puppet_server_test.rb +68 -0
  235. data/test/unit/vagrant/provisioners/puppet_test.rb +182 -0
  236. data/test/unit/vagrant/provisioners/shell_test.rb +79 -0
  237. data/test/unit/vagrant/ssh/session_test.rb +40 -0
  238. data/test/unit/vagrant/ssh_test.rb +307 -0
  239. data/test/unit/vagrant/systems/base_test.rb +18 -0
  240. data/test/unit/vagrant/systems/linux_test.rb +104 -0
  241. data/test/unit/vagrant/ui_test.rb +29 -0
  242. data/test/unit/vagrant/util/busy_test.rb +106 -0
  243. data/test/unit/vagrant/util/counter_test.rb +29 -0
  244. data/test/unit/vagrant/util/hash_with_indifferent_access_test.rb +39 -0
  245. data/test/unit/vagrant/util/platform_test.rb +18 -0
  246. data/test/unit/vagrant/util/retryable_test.rb +50 -0
  247. data/test/unit/vagrant/util/stacked_proc_runner_test.rb +43 -0
  248. data/test/unit/vagrant/util/template_renderer_test.rb +145 -0
  249. data/test/unit/vagrant/vm_test.rb +300 -0
  250. data/vagrant.gemspec +35 -0
  251. metadata +431 -0
@@ -0,0 +1,96 @@
1
+ module Vagrant
2
+ module Provisioners
3
+ class Shell < Base
4
+ register :shell
5
+
6
+ class Config < Vagrant::Config::Base
7
+ attr_accessor :inline
8
+ attr_accessor :path
9
+ attr_accessor :upload_path
10
+ attr_accessor :args
11
+
12
+ def initialize
13
+ @inline = nil
14
+ @path = nil
15
+ @upload_path = "/tmp/vagrant-shell"
16
+ @args = nil
17
+ end
18
+
19
+ def expanded_path
20
+ Pathname.new(path).expand_path(env.root_path) if path
21
+ end
22
+
23
+ def validate(errors)
24
+ super
25
+
26
+ # Validate that the parameters are properly set
27
+ if path && inline
28
+ errors.add(I18n.t("vagrant.provisioners.shell.path_and_inline_set"))
29
+ elsif !path && !inline
30
+ errors.add(I18n.t("vagrant.provisioners.shell.no_path_or_inline"))
31
+ end
32
+
33
+ # Validate the existence of a script to upload
34
+ if path && !expanded_path.file?
35
+ errors.add(I18n.t("vagrant.provisioners.shell.path_invalid", :path => expanded_path))
36
+ end
37
+
38
+ # There needs to be a path to upload the script to
39
+ if !upload_path
40
+ errors.add(I18n.t("vagrant.provisioners.shell.upload_path_not_set"))
41
+ end
42
+
43
+ # If there are args and its not a string, that is a problem
44
+ if args && !args.is_a?(String)
45
+ errors.add(I18n.t("vagrant.provisioners.shell.args_not_string"))
46
+ end
47
+ end
48
+ end
49
+
50
+ # This method yields the path to a script to upload and execute
51
+ # on the remote server. This method will properly clean up the
52
+ # script file if needed.
53
+ def with_script_file
54
+ if config.path
55
+ # Just yield the path to that file...
56
+ yield config.expanded_path
57
+ return
58
+ end
59
+
60
+ # Otherwise we have an inline script, we need to Tempfile it,
61
+ # and handle it specially...
62
+ file = Tempfile.new('vagrant-shell')
63
+ begin
64
+ file.write(config.inline)
65
+ file.fsync
66
+ yield file.path
67
+ ensure
68
+ file.close
69
+ file.unlink
70
+ end
71
+ end
72
+
73
+ def provision!
74
+ args = ""
75
+ args = " #{config.args}" if config.args
76
+ commands = ["chmod +x #{config.upload_path}", "#{config.upload_path}#{args}"]
77
+
78
+ with_script_file do |path|
79
+ # Upload the script to the VM
80
+ vm.ssh.upload!(path.to_s, config.upload_path)
81
+
82
+ # Execute it with sudo
83
+ vm.ssh.execute do |ssh|
84
+ ssh.sudo!(commands) do |ch, type, data|
85
+ if type == :exit_status
86
+ ssh.check_exit_status(data, commands)
87
+ else
88
+ env.ui.info(data)
89
+ end
90
+ end
91
+ end
92
+ end
93
+ end
94
+ end
95
+ end
96
+ end
@@ -0,0 +1,224 @@
1
+ require 'net/ssh'
2
+ require 'net/scp'
3
+
4
+ module Vagrant
5
+ # Manages SSH access to a specific environment. Allows an environment to
6
+ # replace the process with SSH itself, run a specific set of commands,
7
+ # upload files, or even check if a host is up.
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
+ include Util::Retryable
14
+ include Util::SafeExec
15
+
16
+ # Reference back up to the environment which this SSH object belongs
17
+ # to
18
+ attr_accessor :env
19
+
20
+ def initialize(environment)
21
+ @env = environment
22
+ @current_session = nil
23
+ end
24
+
25
+ # Connects to the environment's virtual machine, replacing the ruby
26
+ # process with an SSH process. This method optionally takes a hash
27
+ # of options which override the configuration values.
28
+ def connect(opts={})
29
+ if Util::Platform.windows?
30
+ raise Errors::SSHUnavailableWindows, :key_path => env.config.ssh.private_key_path,
31
+ :ssh_port => port(opts)
32
+ end
33
+
34
+ raise Errors::SSHUnavailable if !Kernel.system("which ssh > /dev/null 2>&1")
35
+
36
+ options = {}
37
+ options[:port] = port(opts)
38
+ [:host, :username, :private_key_path].each do |param|
39
+ options[param] = opts[param] || env.config.ssh.send(param)
40
+ end
41
+
42
+ check_key_permissions(options[:private_key_path])
43
+
44
+ # Command line options
45
+ command_options = ["-p #{options[:port]}", "-o UserKnownHostsFile=/dev/null",
46
+ "-o StrictHostKeyChecking=no", "-o IdentitiesOnly=yes",
47
+ "-i #{options[:private_key_path]}", "-o LogLevel=ERROR"]
48
+ command_options << "-o ForwardAgent=yes" if env.config.ssh.forward_agent
49
+
50
+ if env.config.ssh.forward_x11
51
+ # Both are required so that no warnings are shown regarding X11
52
+ command_options << "-o ForwardX11=yes"
53
+ command_options << "-o ForwardX11Trusted=yes"
54
+ end
55
+
56
+ # Some hackery going on here. On Mac OS X Leopard (10.5), exec fails
57
+ # (GH-51). As a workaround, we fork and wait. On all other platforms,
58
+ # we simply exec.
59
+ command = "ssh #{command_options.join(" ")} #{options[:username]}@#{options[:host]}".strip
60
+ env.logger.info("ssh") { "Invoking SSH: #{command}" }
61
+ safe_exec(command)
62
+ end
63
+
64
+ # Opens an SSH connection to this environment's virtual machine and yields
65
+ # a Net::SSH object which can be used to execute remote commands.
66
+ def execute(opts={})
67
+ # Check the key permissions to avoid SSH hangs
68
+ check_key_permissions(env.config.ssh.private_key_path)
69
+
70
+ # Merge in any additional options
71
+ opts = opts.dup
72
+ opts[:forward_agent] = true if env.config.ssh.forward_agent
73
+ opts[:port] ||= port
74
+
75
+ # Check if we have a currently open SSH session which has the
76
+ # same options, and use that if possible.
77
+ #
78
+ # NOTE: This is experimental and unstable. Therefore it is disabled
79
+ # by default.
80
+ session, options = nil
81
+ session, options = @current_session if env.config.vagrant.ssh_session_cache
82
+
83
+ if session && options == opts
84
+ # Verify that the SSH session is still valid
85
+ begin
86
+ session.exec!("echo foo")
87
+ rescue IOError
88
+ # Reset the session, we need to reconnect
89
+ session = nil
90
+ end
91
+ end
92
+
93
+ if !session || options != opts
94
+ env.logger.info("ssh") { "Connecting to SSH: #{env.config.ssh.host} #{opts[:port]}" }
95
+
96
+ # The exceptions which are acceptable to retry on during
97
+ # attempts to connect to SSH
98
+ exceptions = [Errno::ECONNREFUSED, Net::SSH::Disconnect]
99
+
100
+ # Connect to SSH and gather the session
101
+ session = retryable(:tries => 5, :on => exceptions) do
102
+ connection = Net::SSH.start(env.config.ssh.host,
103
+ env.config.ssh.username,
104
+ opts.merge( :keys => [env.config.ssh.private_key_path],
105
+ :keys_only => true,
106
+ :user_known_hosts_file => [],
107
+ :paranoid => false,
108
+ :config => false))
109
+ SSH::Session.new(connection, env)
110
+ end
111
+
112
+ # Save the new session along with the options which created it
113
+ @current_session = [session, opts]
114
+ else
115
+ env.logger.info("ssh") { "Using cached SSH session: #{session}" }
116
+ end
117
+
118
+ # Yield our session for executing
119
+ return yield session if block_given?
120
+ rescue Errno::ECONNREFUSED
121
+ raise Errors::SSHConnectionRefused
122
+ end
123
+
124
+ # Uploads a file from `from` to `to`. `from` is expected to be a filename
125
+ # or StringIO, and `to` is expected to be a path. This method simply forwards
126
+ # the arguments to `Net::SCP#upload!` so view that for more information.
127
+ def upload!(from, to)
128
+ retryable(:tries => 5, :on => IOError) do
129
+ execute do |ssh|
130
+ scp = Net::SCP.new(ssh.session)
131
+ scp.upload!(from, to)
132
+ end
133
+ end
134
+ end
135
+
136
+ # Checks if this environment's machine is up (i.e. responding to SSH).
137
+ #
138
+ # @return [Boolean]
139
+ def up?
140
+ # We have to determine the port outside of the block since it uses
141
+ # API calls which can only be used from the main thread in JRuby on
142
+ # Windows
143
+ ssh_port = port
144
+
145
+ require 'timeout'
146
+ Timeout.timeout(env.config.ssh.timeout) do
147
+ execute(:timeout => env.config.ssh.timeout,
148
+ :port => ssh_port) { |ssh| }
149
+ end
150
+
151
+ true
152
+ rescue Net::SSH::AuthenticationFailed
153
+ raise Errors::SSHAuthenticationFailed
154
+ rescue Timeout::Error, Errno::ECONNREFUSED, Net::SSH::Disconnect,
155
+ Errors::SSHConnectionRefused, Net::SSH::AuthenticationFailed
156
+ return false
157
+ end
158
+
159
+ # Checks the file permissions for the private key, resetting them
160
+ # if needed, or on failure erroring.
161
+ def check_key_permissions(key_path)
162
+ # Windows systems don't have this issue
163
+ return if Util::Platform.windows?
164
+
165
+ env.logger.info("ssh") { "Checking key permissions: #{key_path}" }
166
+
167
+ stat = File.stat(key_path)
168
+
169
+ if stat.owned? && file_perms(key_path) != "600"
170
+ env.logger.info("ssh") { "Attempting to correct key permissions to 0600" }
171
+
172
+ File.chmod(0600, key_path)
173
+ raise Errors::SSHKeyBadPermissions, :key_path => key_path if file_perms(key_path) != "600"
174
+ end
175
+ rescue Errno::EPERM
176
+ # This shouldn't happen since we verify we own the file, but just
177
+ # in case.
178
+ raise Errors::SSHKeyBadPermissions, :key_path => key_path
179
+ end
180
+
181
+ # Returns the file permissions of a given file. This is fairly unix specific
182
+ # and probably doesn't belong in this class. Will be refactored out later.
183
+ def file_perms(path)
184
+ perms = sprintf("%o", File.stat(path).mode)
185
+ perms.reverse[0..2].reverse
186
+ end
187
+
188
+ # Returns the port which is either given in the options hash or taken from
189
+ # the config by finding it in the forwarded ports hash based on the
190
+ # `config.ssh.forwarded_port_key`.
191
+ def port(opts={})
192
+ # Check if port was specified in options hash
193
+ return opts[:port] if opts[:port]
194
+
195
+ # Check if a port was specified in the config
196
+ return env.config.ssh.port if env.config.ssh.port
197
+
198
+ # Check if we have an SSH forwarded port
199
+ pnum_by_name = nil
200
+ pnum_by_destination = nil
201
+ env.vm.vm.network_adapters.each do |na|
202
+ # Look for the port number by name...
203
+ pnum_by_name = na.nat_driver.forwarded_ports.detect do |fp|
204
+ fp.name == env.config.ssh.forwarded_port_key
205
+ end
206
+
207
+ # Look for the port number by destination...
208
+ pnum_by_destination = na.nat_driver.forwarded_ports.detect do |fp|
209
+ fp.guestport == env.config.ssh.forwarded_port_destination
210
+ end
211
+
212
+ # pnum_by_name is what we're looking for here, so break early
213
+ # if we have it.
214
+ break if pnum_by_name
215
+ end
216
+
217
+ return pnum_by_name.hostport if pnum_by_name
218
+ return pnum_by_destination.hostport if pnum_by_destination
219
+
220
+ # This should NEVER happen.
221
+ raise Errors::SSHPortNotDetected
222
+ end
223
+ end
224
+ end
@@ -0,0 +1,136 @@
1
+ module Vagrant
2
+ class SSH
3
+ # A helper class which wraps around `Net::SSH::Connection::Session`
4
+ # in order to provide basic command error checking while still
5
+ # providing access to the actual session object.
6
+ class Session
7
+ include Util::Retryable
8
+
9
+ attr_reader :session
10
+ attr_reader :env
11
+
12
+ def initialize(session, env)
13
+ @session = session
14
+ @env = env
15
+ end
16
+
17
+ # Executes a given command and simply returns true/false if the
18
+ # command succeeded or not.
19
+ def test?(command)
20
+ exec!(command) do |ch, type, data|
21
+ return true if type == :exit_status && data == 0
22
+ end
23
+
24
+ false
25
+ end
26
+
27
+ # Executes a given command on the SSH session using `sudo` and
28
+ # blocks until the command completes. This takes the same parameters
29
+ # as {#exec!}. The only difference is that the command can be an
30
+ # array of commands, which will be placed into the same script.
31
+ #
32
+ # This is different than just calling {#exec!} with `sudo`, since
33
+ # this command is tailor-made to be compliant with older versions
34
+ # of `sudo`.
35
+ def sudo!(commands, options=nil, &block)
36
+ channel = session.open_channel do |ch|
37
+ ch.exec("sudo -H #{env.config.ssh.shell} -l") do |ch2, success|
38
+ # Set the terminal
39
+ ch2.send_data "export TERM=vt100\n"
40
+
41
+ # Output each command as if they were entered on the command line
42
+ [commands].flatten.each do |command|
43
+ ch2.send_data "#{command}\n"
44
+ end
45
+
46
+ # Remember to exit or we'll hang!
47
+ ch2.send_data "exit\n"
48
+
49
+ # Setup the callbacks with our options so we get all the
50
+ # stdout/stderr and error checking goodies
51
+ setup_channel_callbacks(ch2, commands, options, block)
52
+ end
53
+ end
54
+
55
+ channel.wait
56
+ channel[:result]
57
+ end
58
+
59
+ # Executes a given command on the SSH session and blocks until
60
+ # the command completes. This is an almost line for line copy of
61
+ # the actual `exec!` implementation, except that this
62
+ # implementation also reports `:exit_status` to the block if given.
63
+ def exec!(commands, options=nil, &block)
64
+ retryable(:tries => 5, :on => [IOError, Net::SSH::Disconnect], :sleep => 1.0) do
65
+ metach = session.open_channel do |ch|
66
+ ch.exec("#{env.config.ssh.shell} -l") do |ch2, success|
67
+ # Set the terminal
68
+ ch2.send_data "export TERM=vt100\n"
69
+
70
+ # Output the commands as if they were entered on the command line
71
+ [commands].flatten.each do |command|
72
+ ch2.send_data "#{command}\n"
73
+ end
74
+
75
+ # Remember to exit
76
+ ch2.send_data "exit\n"
77
+
78
+ # Setup the callbacks
79
+ setup_channel_callbacks(ch2, commands, options, block)
80
+ end
81
+ end
82
+
83
+ metach.wait
84
+ metach[:result]
85
+ end
86
+ end
87
+
88
+ # Sets up the channel callbacks to properly check exit statuses and
89
+ # callback on stdout/stderr.
90
+ def setup_channel_callbacks(channel, command, options, block)
91
+ options = { :error_check => true }.merge(options || {})
92
+
93
+ block ||= Proc.new do |ch, type, data|
94
+ check_exit_status(data, command, options, ch[:result]) if type == :exit_status && options[:error_check]
95
+
96
+ ch[:result] ||= ""
97
+ ch[:result] << data if [:stdout, :stderr].include?(type)
98
+ end
99
+
100
+ # Output stdout data to the block
101
+ channel.on_data do |ch2, data|
102
+ # This clears the screen, we want to filter it out.
103
+ data.gsub!("\e[H", "")
104
+
105
+ block.call(ch2, :stdout, data)
106
+ end
107
+
108
+ # Output stderr data to the block
109
+ channel.on_extended_data do |ch2, type, data|
110
+ block.call(ch2, :stderr, data)
111
+ end
112
+
113
+ # Output exit status information to the block
114
+ channel.on_request("exit-status") do |ch2, data|
115
+ block.call(ch2, :exit_status, data.read_long)
116
+ end
117
+ end
118
+
119
+ # Checks for an erroroneous exit status and raises an exception
120
+ # if so.
121
+ def check_exit_status(exit_status, commands, options=nil, output=nil)
122
+ if exit_status != 0
123
+ output ||= '[no output]'
124
+ options = {
125
+ :_error_class => Errors::VagrantError,
126
+ :_key => :ssh_bad_exit_status,
127
+ :command => [commands].flatten.join("\n"),
128
+ :output => output
129
+ }.merge(options || {})
130
+
131
+ raise options[:_error_class], options
132
+ end
133
+ end
134
+ end
135
+ end
136
+ end