fixed-vagrant 0.7.4.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 (242) hide show
  1. data/.gitignore +15 -0
  2. data/.yardopts +1 -0
  3. data/CHANGELOG.md +272 -0
  4. data/Gemfile +21 -0
  5. data/LICENSE +21 -0
  6. data/README.md +75 -0
  7. data/Rakefile +22 -0
  8. data/bin/vagrant +22 -0
  9. data/config/default.rb +39 -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 +42 -0
  18. data/lib/vagrant/action.rb +134 -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 +104 -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 +109 -0
  32. data/lib/vagrant/action/general/validate.rb +19 -0
  33. data/lib/vagrant/action/vm.rb +31 -0
  34. data/lib/vagrant/action/vm/boot.rb +48 -0
  35. data/lib/vagrant/action/vm/check_box.rb +28 -0
  36. data/lib/vagrant/action/vm/check_guest_additions.rb +30 -0
  37. data/lib/vagrant/action/vm/clean_machine_folder.rb +43 -0
  38. data/lib/vagrant/action/vm/clear_forwarded_ports.rb +39 -0
  39. data/lib/vagrant/action/vm/clear_nfs_exports.rb +20 -0
  40. data/lib/vagrant/action/vm/clear_shared_folders.rb +32 -0
  41. data/lib/vagrant/action/vm/customize.rb +21 -0
  42. data/lib/vagrant/action/vm/destroy.rb +19 -0
  43. data/lib/vagrant/action/vm/destroy_unused_network_interfaces.rb +30 -0
  44. data/lib/vagrant/action/vm/discard_state.rb +22 -0
  45. data/lib/vagrant/action/vm/export.rb +52 -0
  46. data/lib/vagrant/action/vm/forward_ports.rb +134 -0
  47. data/lib/vagrant/action/vm/forward_ports_helpers.rb +28 -0
  48. data/lib/vagrant/action/vm/halt.rb +29 -0
  49. data/lib/vagrant/action/vm/host_name.rb +21 -0
  50. data/lib/vagrant/action/vm/import.rb +35 -0
  51. data/lib/vagrant/action/vm/match_mac_address.rb +21 -0
  52. data/lib/vagrant/action/vm/network.rb +139 -0
  53. data/lib/vagrant/action/vm/nfs.rb +159 -0
  54. data/lib/vagrant/action/vm/nfs_helpers.rb +11 -0
  55. data/lib/vagrant/action/vm/package.rb +23 -0
  56. data/lib/vagrant/action/vm/package_vagrantfile.rb +33 -0
  57. data/lib/vagrant/action/vm/provision.rb +41 -0
  58. data/lib/vagrant/action/vm/resume.rb +20 -0
  59. data/lib/vagrant/action/vm/share_folders.rb +70 -0
  60. data/lib/vagrant/action/vm/suspend.rb +20 -0
  61. data/lib/vagrant/action/warden.rb +79 -0
  62. data/lib/vagrant/box.rb +90 -0
  63. data/lib/vagrant/box_collection.rb +53 -0
  64. data/lib/vagrant/cli.rb +55 -0
  65. data/lib/vagrant/command.rb +25 -0
  66. data/lib/vagrant/command/base.rb +106 -0
  67. data/lib/vagrant/command/box.rb +33 -0
  68. data/lib/vagrant/command/destroy.rb +17 -0
  69. data/lib/vagrant/command/group_base.rb +107 -0
  70. data/lib/vagrant/command/halt.rb +18 -0
  71. data/lib/vagrant/command/helpers.rb +33 -0
  72. data/lib/vagrant/command/init.rb +14 -0
  73. data/lib/vagrant/command/named_base.rb +14 -0
  74. data/lib/vagrant/command/package.rb +41 -0
  75. data/lib/vagrant/command/provision.rb +17 -0
  76. data/lib/vagrant/command/reload.rb +17 -0
  77. data/lib/vagrant/command/resume.rb +17 -0
  78. data/lib/vagrant/command/ssh.rb +42 -0
  79. data/lib/vagrant/command/ssh_config.rb +26 -0
  80. data/lib/vagrant/command/status.rb +22 -0
  81. data/lib/vagrant/command/suspend.rb +17 -0
  82. data/lib/vagrant/command/up.rb +19 -0
  83. data/lib/vagrant/command/upgrade_to_060.rb +45 -0
  84. data/lib/vagrant/command/version.rb +13 -0
  85. data/lib/vagrant/config.rb +123 -0
  86. data/lib/vagrant/config/base.rb +85 -0
  87. data/lib/vagrant/config/error_recorder.rb +19 -0
  88. data/lib/vagrant/config/nfs.rb +10 -0
  89. data/lib/vagrant/config/package.rb +9 -0
  90. data/lib/vagrant/config/ssh.rb +33 -0
  91. data/lib/vagrant/config/top.rb +61 -0
  92. data/lib/vagrant/config/vagrant.rb +16 -0
  93. data/lib/vagrant/config/vm.rb +137 -0
  94. data/lib/vagrant/config/vm/provisioner.rb +56 -0
  95. data/lib/vagrant/config/vm/sub_vm.rb +17 -0
  96. data/lib/vagrant/data_store.rb +70 -0
  97. data/lib/vagrant/downloaders.rb +7 -0
  98. data/lib/vagrant/downloaders/base.rb +23 -0
  99. data/lib/vagrant/downloaders/file.rb +22 -0
  100. data/lib/vagrant/downloaders/http.rb +64 -0
  101. data/lib/vagrant/environment.rb +392 -0
  102. data/lib/vagrant/errors.rb +332 -0
  103. data/lib/vagrant/hosts.rb +8 -0
  104. data/lib/vagrant/hosts/arch.rb +27 -0
  105. data/lib/vagrant/hosts/base.rb +78 -0
  106. data/lib/vagrant/hosts/bsd.rb +52 -0
  107. data/lib/vagrant/hosts/linux.rb +47 -0
  108. data/lib/vagrant/plugin.rb +57 -0
  109. data/lib/vagrant/provisioners.rb +9 -0
  110. data/lib/vagrant/provisioners/base.rb +63 -0
  111. data/lib/vagrant/provisioners/chef.rb +130 -0
  112. data/lib/vagrant/provisioners/chef_server.rb +103 -0
  113. data/lib/vagrant/provisioners/chef_solo.rb +142 -0
  114. data/lib/vagrant/provisioners/puppet.rb +137 -0
  115. data/lib/vagrant/provisioners/puppet_server.rb +55 -0
  116. data/lib/vagrant/provisioners/shell.rb +52 -0
  117. data/lib/vagrant/ssh.rb +173 -0
  118. data/lib/vagrant/ssh/session.rb +125 -0
  119. data/lib/vagrant/systems.rb +11 -0
  120. data/lib/vagrant/systems/base.rb +87 -0
  121. data/lib/vagrant/systems/debian.rb +36 -0
  122. data/lib/vagrant/systems/freebsd.rb +84 -0
  123. data/lib/vagrant/systems/gentoo.rb +27 -0
  124. data/lib/vagrant/systems/linux.rb +83 -0
  125. data/lib/vagrant/systems/linux/config.rb +21 -0
  126. data/lib/vagrant/systems/linux/error.rb +9 -0
  127. data/lib/vagrant/systems/redhat.rb +39 -0
  128. data/lib/vagrant/systems/solaris.rb +62 -0
  129. data/lib/vagrant/systems/ubuntu.rb +17 -0
  130. data/lib/vagrant/test_helpers.rb +128 -0
  131. data/lib/vagrant/ui.rb +69 -0
  132. data/lib/vagrant/util.rb +13 -0
  133. data/lib/vagrant/util/busy.rb +59 -0
  134. data/lib/vagrant/util/hash_with_indifferent_access.rb +63 -0
  135. data/lib/vagrant/util/plain_logger.rb +25 -0
  136. data/lib/vagrant/util/platform.rb +65 -0
  137. data/lib/vagrant/util/resource_logger.rb +63 -0
  138. data/lib/vagrant/util/retryable.rb +25 -0
  139. data/lib/vagrant/util/stacked_proc_runner.rb +35 -0
  140. data/lib/vagrant/util/template_renderer.rb +83 -0
  141. data/lib/vagrant/version.rb +6 -0
  142. data/lib/vagrant/vm.rb +177 -0
  143. data/templates/chef_server_client.erb +18 -0
  144. data/templates/chef_solo_solo.erb +19 -0
  145. data/templates/commands/init/Vagrantfile.erb +82 -0
  146. data/templates/config/validation_failed.erb +7 -0
  147. data/templates/locales/en.yml +535 -0
  148. data/templates/network_entry_debian.erb +8 -0
  149. data/templates/network_entry_gentoo.erb +5 -0
  150. data/templates/network_entry_redhat.erb +8 -0
  151. data/templates/nfs/exports.erb +5 -0
  152. data/templates/nfs/exports_linux.erb +5 -0
  153. data/templates/package_Vagrantfile.erb +11 -0
  154. data/templates/ssh_config.erb +9 -0
  155. data/test/locales/en.yml +8 -0
  156. data/test/test_helper.rb +26 -0
  157. data/test/vagrant/action/box/destroy_test.rb +18 -0
  158. data/test/vagrant/action/box/download_test.rb +125 -0
  159. data/test/vagrant/action/box/package_test.rb +25 -0
  160. data/test/vagrant/action/box/unpackage_test.rb +84 -0
  161. data/test/vagrant/action/box/verify_test.rb +30 -0
  162. data/test/vagrant/action/builder_test.rb +203 -0
  163. data/test/vagrant/action/env/set_test.rb +24 -0
  164. data/test/vagrant/action/environment_test.rb +27 -0
  165. data/test/vagrant/action/general/package_test.rb +268 -0
  166. data/test/vagrant/action/general/validate_test.rb +31 -0
  167. data/test/vagrant/action/vm/boot_test.rb +66 -0
  168. data/test/vagrant/action/vm/check_box_test.rb +56 -0
  169. data/test/vagrant/action/vm/check_guest_additions_test.rb +9 -0
  170. data/test/vagrant/action/vm/clean_machine_folder_test.rb +84 -0
  171. data/test/vagrant/action/vm/clear_forwarded_ports_test.rb +72 -0
  172. data/test/vagrant/action/vm/clear_nfs_exports_test.rb +22 -0
  173. data/test/vagrant/action/vm/clear_shared_folders_test.rb +49 -0
  174. data/test/vagrant/action/vm/customize_test.rb +30 -0
  175. data/test/vagrant/action/vm/destroy_test.rb +25 -0
  176. data/test/vagrant/action/vm/destroy_unused_network_interfaces_test.rb +49 -0
  177. data/test/vagrant/action/vm/discard_state_test.rb +45 -0
  178. data/test/vagrant/action/vm/export_test.rb +107 -0
  179. data/test/vagrant/action/vm/forward_ports_helpers_test.rb +70 -0
  180. data/test/vagrant/action/vm/forward_ports_test.rb +194 -0
  181. data/test/vagrant/action/vm/halt_test.rb +79 -0
  182. data/test/vagrant/action/vm/host_name_test.rb +36 -0
  183. data/test/vagrant/action/vm/import_test.rb +66 -0
  184. data/test/vagrant/action/vm/match_mac_address_test.rb +36 -0
  185. data/test/vagrant/action/vm/network_test.rb +286 -0
  186. data/test/vagrant/action/vm/nfs_helpers_test.rb +26 -0
  187. data/test/vagrant/action/vm/nfs_test.rb +260 -0
  188. data/test/vagrant/action/vm/package_test.rb +25 -0
  189. data/test/vagrant/action/vm/package_vagrantfile_test.rb +46 -0
  190. data/test/vagrant/action/vm/provision_test.rb +90 -0
  191. data/test/vagrant/action/vm/resume_test.rb +35 -0
  192. data/test/vagrant/action/vm/share_folders_test.rb +139 -0
  193. data/test/vagrant/action/vm/suspend_test.rb +35 -0
  194. data/test/vagrant/action/warden_test.rb +119 -0
  195. data/test/vagrant/action_test.rb +89 -0
  196. data/test/vagrant/box_collection_test.rb +45 -0
  197. data/test/vagrant/box_test.rb +74 -0
  198. data/test/vagrant/cli_test.rb +35 -0
  199. data/test/vagrant/command/base_test.rb +23 -0
  200. data/test/vagrant/command/group_base_test.rb +15 -0
  201. data/test/vagrant/command/helpers_test.rb +88 -0
  202. data/test/vagrant/command/package_test.rb +27 -0
  203. data/test/vagrant/config/base_test.rb +52 -0
  204. data/test/vagrant/config/error_recorder_test.rb +18 -0
  205. data/test/vagrant/config/ssh_test.rb +12 -0
  206. data/test/vagrant/config/vagrant_test.rb +35 -0
  207. data/test/vagrant/config/vm/provisioner_test.rb +92 -0
  208. data/test/vagrant/config/vm_test.rb +86 -0
  209. data/test/vagrant/config_test.rb +162 -0
  210. data/test/vagrant/data_store_test.rb +77 -0
  211. data/test/vagrant/downloaders/base_test.rb +28 -0
  212. data/test/vagrant/downloaders/file_test.rb +48 -0
  213. data/test/vagrant/downloaders/http_test.rb +80 -0
  214. data/test/vagrant/environment_test.rb +508 -0
  215. data/test/vagrant/errors_test.rb +42 -0
  216. data/test/vagrant/hosts/base_test.rb +46 -0
  217. data/test/vagrant/hosts/bsd_test.rb +53 -0
  218. data/test/vagrant/hosts/linux_test.rb +54 -0
  219. data/test/vagrant/plugin_test.rb +9 -0
  220. data/test/vagrant/provisioners/base_test.rb +63 -0
  221. data/test/vagrant/provisioners/chef_server_test.rb +188 -0
  222. data/test/vagrant/provisioners/chef_solo_test.rb +219 -0
  223. data/test/vagrant/provisioners/chef_test.rb +179 -0
  224. data/test/vagrant/provisioners/puppet_server_test.rb +68 -0
  225. data/test/vagrant/provisioners/puppet_test.rb +182 -0
  226. data/test/vagrant/provisioners/shell_test.rb +68 -0
  227. data/test/vagrant/ssh/session_test.rb +40 -0
  228. data/test/vagrant/ssh_test.rb +342 -0
  229. data/test/vagrant/systems/base_test.rb +18 -0
  230. data/test/vagrant/systems/linux_test.rb +114 -0
  231. data/test/vagrant/ui_test.rb +29 -0
  232. data/test/vagrant/util/busy_test.rb +106 -0
  233. data/test/vagrant/util/hash_with_indifferent_access_test.rb +39 -0
  234. data/test/vagrant/util/plain_logger_test.rb +17 -0
  235. data/test/vagrant/util/platform_test.rb +18 -0
  236. data/test/vagrant/util/resource_logger_test.rb +78 -0
  237. data/test/vagrant/util/retryable_test.rb +50 -0
  238. data/test/vagrant/util/stacked_proc_runner_test.rb +43 -0
  239. data/test/vagrant/util/template_renderer_test.rb +145 -0
  240. data/test/vagrant/vm_test.rb +291 -0
  241. data/vagrant.gemspec +36 -0
  242. metadata +514 -0
@@ -0,0 +1,137 @@
1
+ module Vagrant
2
+ module Provisioners
3
+ class PuppetError < Vagrant::Errors::VagrantError
4
+ error_namespace("vagrant.provisioners.puppet")
5
+ end
6
+
7
+ class Puppet < Base
8
+ register :puppet
9
+
10
+ class Config < Vagrant::Config::Base
11
+ attr_accessor :manifest_file
12
+ attr_accessor :manifests_path
13
+ attr_accessor :module_path
14
+ attr_accessor :pp_path
15
+ attr_accessor :options
16
+
17
+ def initialize
18
+ @manifest_file = nil
19
+ @manifests_path = "manifests"
20
+ @module_path = nil
21
+ @pp_path = "/tmp/vagrant-puppet"
22
+ @options = []
23
+ end
24
+
25
+ # Returns the manifests path expanded relative to the root path of the
26
+ # environment.
27
+ def expanded_manifests_path
28
+ Pathname.new(manifests_path).expand_path(env.root_path)
29
+ end
30
+
31
+ # Returns the manifest file if set otherwise returns the box name pp file
32
+ # which may or may not exist.
33
+ def computed_manifest_file
34
+ manifest_file || "#{top.vm.box}.pp"
35
+ end
36
+
37
+ # Returns the module paths as an array of paths expanded relative to the
38
+ # root path.
39
+ def expanded_module_paths
40
+ return [] if !module_path
41
+
42
+ # Get all the paths and expand them relative to the root path, returning
43
+ # the array of expanded paths
44
+ paths = module_path
45
+ paths = [paths] if !paths.is_a?(Array)
46
+ paths.map do |path|
47
+ Pathname.new(path).expand_path(env.root_path)
48
+ end
49
+ end
50
+
51
+ def validate(errors)
52
+ super
53
+
54
+ # Manifests path/file validation
55
+ if !expanded_manifests_path.directory?
56
+ errors.add(I18n.t("vagrant.provisioners.puppet.manifests_path_missing", :path => expanded_manifests_path))
57
+ else
58
+ if !expanded_manifests_path.join(computed_manifest_file).file?
59
+ errors.add(I18n.t("vagrant.provisioners.puppet.manifest_missing", :manifest => computed_manifest_file))
60
+ end
61
+ end
62
+
63
+ # Module paths validation
64
+ expanded_module_paths.each do |path|
65
+ if !path.directory?
66
+ errors.add(I18n.t("vagrant.provisioners.puppet.module_path_missing", :path => path))
67
+ end
68
+ end
69
+ end
70
+ end
71
+
72
+ def prepare
73
+ set_module_paths
74
+ share_manifests
75
+ share_module_paths
76
+ end
77
+
78
+ def provision!
79
+ verify_binary("puppet")
80
+ run_puppet_client
81
+ end
82
+
83
+ def share_manifests
84
+ env.config.vm.share_folder("manifests", manifests_guest_path, config.expanded_manifests_path)
85
+ end
86
+
87
+ def share_module_paths
88
+ count = 0
89
+ @module_paths.each do |from, to|
90
+ # Sorry for the cryptic key here, but VirtualBox has a strange limit on
91
+ # maximum size for it and its something small (around 10)
92
+ env.config.vm.share_folder("v-pp-m#{count}", to, from)
93
+ count += 1
94
+ end
95
+ end
96
+
97
+ def set_module_paths
98
+ @module_paths = {}
99
+ config.expanded_module_paths.each_with_index do |path, i|
100
+ @module_paths[path] = File.join(config.pp_path, "modules-#{i}")
101
+ end
102
+ end
103
+
104
+ def manifests_guest_path
105
+ File.join(config.pp_path, "manifests")
106
+ end
107
+
108
+ def verify_binary(binary)
109
+ vm.ssh.execute do |ssh|
110
+ ssh.sudo!("which #{binary}", :error_class => PuppetError, :_key => :puppet_not_detected, :binary => binary)
111
+ end
112
+ end
113
+
114
+ def run_puppet_client
115
+ options = [config.options].flatten
116
+ options << "--modulepath '#{@module_paths.values.join(':')}'" if !@module_paths.empty?
117
+ options << config.computed_manifest_file
118
+ options = options.join(" ")
119
+
120
+ commands = ["cd #{manifests_guest_path}",
121
+ "puppet #{options}"]
122
+
123
+ env.ui.info I18n.t("vagrant.provisioners.puppet.running_puppet", :manifest => config.computed_manifest_file)
124
+
125
+ vm.ssh.execute do |ssh|
126
+ ssh.sudo! commands do |ch, type, data|
127
+ if type == :exit_status
128
+ ssh.check_exit_status(data, commands)
129
+ else
130
+ env.ui.info(data)
131
+ end
132
+ end
133
+ end
134
+ end
135
+ end
136
+ end
137
+ end
@@ -0,0 +1,55 @@
1
+ module Vagrant
2
+ module Provisioners
3
+ class PuppetServerError < Vagrant::Errors::VagrantError
4
+ error_namespace("vagrant.provisioners.puppet_server")
5
+ end
6
+
7
+ class PuppetServer < Base
8
+ register :puppet_server
9
+
10
+ class Config < Vagrant::Config::Base
11
+ attr_accessor :puppet_server
12
+ attr_accessor :puppet_node
13
+ attr_accessor :options
14
+
15
+ def initialize
16
+ @puppet_server = "puppet"
17
+ @puppet_node = "puppet_node"
18
+ @options = []
19
+ end
20
+ end
21
+
22
+ def provision!
23
+ verify_binary("puppetd")
24
+ run_puppetd_client
25
+ end
26
+
27
+ def verify_binary(binary)
28
+ vm.ssh.execute do |ssh|
29
+ ssh.sudo!("which #{binary}", :error_class => PuppetServerError, :_key => :puppetd_not_detected, :binary => binary)
30
+ end
31
+ end
32
+
33
+ def run_puppetd_client
34
+ options = config.options
35
+ options = options.join(" ") if options.is_a?(Array)
36
+ if config.puppet_node
37
+ cn = config.puppet_node
38
+ else
39
+ cn = env.config.vm.box
40
+ end
41
+
42
+ commands = "puppetd #{options} --server #{config.puppet_server} --certname #{cn}"
43
+
44
+ env.ui.info I18n.t("vagrant.provisioners.puppet_server.running_puppetd")
45
+
46
+ vm.ssh.execute do |ssh|
47
+ ssh.sudo!(commands) do |channel, type, data|
48
+ ssh.check_exit_status(data, commands) if type == :exit_status
49
+ env.ui.info(data) if type != :exit_status
50
+ end
51
+ end
52
+ end
53
+ end
54
+ end
55
+ end
@@ -0,0 +1,52 @@
1
+ module Vagrant
2
+ module Provisioners
3
+ class Shell < Base
4
+ register :shell
5
+
6
+ class Config < Vagrant::Config::Base
7
+ attr_accessor :path
8
+ attr_accessor :upload_path
9
+
10
+ def initialize
11
+ @upload_path = "/tmp/vagrant-shell"
12
+ end
13
+
14
+ def expanded_path
15
+ Pathname.new(path).expand_path(env.root_path) if path
16
+ end
17
+
18
+ def validate(errors)
19
+ super
20
+
21
+ if !path
22
+ errors.add(I18n.t("vagrant.provisioners.shell.path_not_set"))
23
+ elsif !expanded_path.file?
24
+ errors.add(I18n.t("vagrant.provisioners.shell.path_invalid", :path => expanded_path))
25
+ end
26
+
27
+ if !upload_path
28
+ errors.add(I18n.t("vagrant.provisioners.shell.upload_path_not_set"))
29
+ end
30
+ end
31
+ end
32
+
33
+ def provision!
34
+ commands = ["chmod +x #{config.upload_path}", config.upload_path]
35
+
36
+ # Upload the script to the VM
37
+ vm.ssh.upload!(config.expanded_path.to_s, config.upload_path)
38
+
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)
46
+ end
47
+ end
48
+ end
49
+ end
50
+ end
51
+ end
52
+ end
@@ -0,0 +1,173 @@
1
+ require 'timeout'
2
+ require 'net/ssh'
3
+ require 'net/scp'
4
+ require 'mario'
5
+
6
+ require 'vagrant/ssh/session'
7
+
8
+ module Vagrant
9
+ # Manages SSH access to a specific environment. Allows an environment to
10
+ # replace the process with SSH itself, run a specific set of commands,
11
+ # upload files, or even check if a host is up.
12
+ class SSH
13
+ include Util::Retryable
14
+
15
+ # Reference back up to the environment which this SSH object belongs
16
+ # to
17
+ attr_accessor :env
18
+
19
+ def initialize(environment)
20
+ @env = environment
21
+ end
22
+
23
+ # Connects to the environment's virtual machine, replacing the ruby
24
+ # process with an SSH process. This method optionally takes a hash
25
+ # of options which override the configuration values.
26
+ def connect(opts={})
27
+ if Mario::Platform.windows?
28
+ raise Errors::SSHUnavailableWindows, :key_path => env.config.ssh.private_key_path,
29
+ :ssh_port => port(opts)
30
+ end
31
+
32
+ raise Errors::SSHUnavailable if !Kernel.system("which ssh > /dev/null 2>&1")
33
+
34
+ options = {}
35
+ options[:port] = port(opts)
36
+ [:host, :username, :private_key_path].each do |param|
37
+ options[param] = opts[param] || env.config.ssh.send(param)
38
+ end
39
+
40
+ check_key_permissions(options[:private_key_path])
41
+
42
+ # Command line options
43
+ command_options = ["-p #{options[:port]}", "-o UserKnownHostsFile=/dev/null",
44
+ "-o StrictHostKeyChecking=no", "-o IdentitiesOnly=yes",
45
+ "-i #{options[:private_key_path]}"]
46
+ command_options << "-o ForwardAgent=yes" if env.config.ssh.forward_agent
47
+
48
+ if env.config.ssh.forward_x11
49
+ # Both are required so that no warnings are shown regarding X11
50
+ command_options << "-o ForwardX11=yes"
51
+ command_options << "-o ForwardX11Trusted=yes"
52
+ end
53
+
54
+ # Some hackery going on here. On Mac OS X Leopard (10.5), exec fails
55
+ # (GH-51). As a workaround, we fork and wait. On all other platforms,
56
+ # 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
61
+ end
62
+
63
+ # Opens an SSH connection to this environment's virtual machine and yields
64
+ # a Net::SSH object which can be used to execute remote commands.
65
+ def execute(opts={})
66
+ # Check the key permissions to avoid SSH hangs
67
+ check_key_permissions(env.config.ssh.private_key_path)
68
+
69
+ # Merge in any additional options
70
+ opts = opts.dup
71
+ opts[:forward_agent] = true if env.config.ssh.forward_agent
72
+ opts[:port] ||= port
73
+
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)
83
+ end
84
+ end
85
+ rescue Errno::ECONNREFUSED
86
+ raise Errors::SSHConnectionRefused
87
+ end
88
+
89
+ # Uploads a file from `from` to `to`. `from` is expected to be a filename
90
+ # or StringIO, and `to` is expected to be a path. This method simply forwards
91
+ # the arguments to `Net::SCP#upload!` so view that for more information.
92
+ def upload!(from, to)
93
+ retryable(:tries => 5, :on => IOError) do
94
+ execute do |ssh|
95
+ scp = Net::SCP.new(ssh.session)
96
+ scp.upload!(from, to)
97
+ end
98
+ end
99
+ end
100
+
101
+ # Checks if this environment's machine is up (i.e. responding to SSH).
102
+ #
103
+ # @return [Boolean]
104
+ def up?
105
+ # We have to determine the port outside of the block since it uses
106
+ # API calls which can only be used from the main thread in JRuby on
107
+ # Windows
108
+ ssh_port = port
109
+
110
+ Timeout.timeout(env.config.ssh.timeout) do
111
+ execute(:timeout => env.config.ssh.timeout,
112
+ :port => ssh_port) { |ssh| }
113
+ end
114
+
115
+ true
116
+ rescue Net::SSH::AuthenticationFailed
117
+ raise Errors::SSHAuthenticationFailed
118
+ rescue Timeout::Error, Errno::ECONNREFUSED, Net::SSH::Disconnect,
119
+ Errors::SSHConnectionRefused, Net::SSH::AuthenticationFailed
120
+ return false
121
+ end
122
+
123
+ # Checks the file permissions for the private key, resetting them
124
+ # if needed, or on failure erroring.
125
+ def check_key_permissions(key_path)
126
+ # Windows systems don't have this issue
127
+ return if Mario::Platform.windows?
128
+
129
+ stat = File.stat(key_path)
130
+
131
+ if stat.owned? && file_perms(key_path) != "600"
132
+ File.chmod(0600, key_path)
133
+
134
+ raise Errors::SSHKeyBadPermissions, :key_path => key_path if file_perms(key_path) != "600"
135
+ end
136
+ rescue Errno::EPERM
137
+ # This shouldn't happen since we verify we own the file, but just
138
+ # in case.
139
+ raise Errors::SSHKeyBadPermissions, :key_path => key_path
140
+ end
141
+
142
+ # Returns the file permissions of a given file. This is fairly unix specific
143
+ # and probably doesn't belong in this class. Will be refactored out later.
144
+ def file_perms(path)
145
+ perms = sprintf("%o", File.stat(path).mode)
146
+ perms.reverse[0..2].reverse
147
+ end
148
+
149
+ # Returns the port which is either given in the options hash or taken from
150
+ # the config by finding it in the forwarded ports hash based on the
151
+ # `config.ssh.forwarded_port_key`.
152
+ def port(opts={})
153
+ # Check if port was specified in options hash
154
+ pnum = opts[:port]
155
+ return pnum if pnum
156
+
157
+ # Check if we have an SSH forwarded port
158
+ pnum = nil
159
+ env.vm.vm.network_adapters.each do |na|
160
+ pnum = na.nat_driver.forwarded_ports.detect do |fp|
161
+ fp.name == env.config.ssh.forwarded_port_key
162
+ end
163
+
164
+ break if pnum
165
+ end
166
+
167
+ return pnum.hostport if pnum
168
+
169
+ # This should NEVER happen.
170
+ raise Errors::SSHPortNotDetected
171
+ end
172
+ end
173
+ end
@@ -0,0 +1,125 @@
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 #{env.config.ssh.sudo_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!(command, options=nil, &block)
64
+ retryable(:tries => 5, :on => [IOError, Net::SSH::Disconnect], :sleep => 1.0) do
65
+ metach = session.open_channel do |channel|
66
+ channel.exec(command) do |ch, success|
67
+ raise "could not execute command: #{command.inspect}" unless success
68
+ setup_channel_callbacks(ch, command, options, block)
69
+ end
70
+ end
71
+
72
+ metach.wait
73
+ metach[:result]
74
+ end
75
+ end
76
+
77
+ # Sets up the channel callbacks to properly check exit statuses and
78
+ # callback on stdout/stderr.
79
+ def setup_channel_callbacks(channel, command, options, block)
80
+ options = { :error_check => true }.merge(options || {})
81
+
82
+ block ||= Proc.new do |ch, type, data|
83
+ check_exit_status(data, command, options, ch[:result]) if type == :exit_status && options[:error_check]
84
+
85
+ ch[:result] ||= ""
86
+ ch[:result] << data if [:stdout, :stderr].include?(type)
87
+ end
88
+
89
+ # Output stdout data to the block
90
+ channel.on_data do |ch2, data|
91
+ # This clears the screen, we want to filter it out.
92
+ data.gsub!("\e[H", "")
93
+
94
+ block.call(ch2, :stdout, data)
95
+ end
96
+
97
+ # Output stderr data to the block
98
+ channel.on_extended_data do |ch2, type, data|
99
+ block.call(ch2, :stderr, data)
100
+ end
101
+
102
+ # Output exit status information to the block
103
+ channel.on_request("exit-status") do |ch2, data|
104
+ block.call(ch2, :exit_status, data.read_long)
105
+ end
106
+ end
107
+
108
+ # Checks for an erroroneous exit status and raises an exception
109
+ # if so.
110
+ def check_exit_status(exit_status, commands, options=nil, output=nil)
111
+ if exit_status != 0
112
+ output ||= '[no output]'
113
+ options = {
114
+ :_error_class => Errors::VagrantError,
115
+ :_key => :ssh_bad_exit_status,
116
+ :command => [commands].flatten.join("\n"),
117
+ :output => output
118
+ }.merge(options || {})
119
+
120
+ raise options[:_error_class], options
121
+ end
122
+ end
123
+ end
124
+ end
125
+ end