vagrant-unbundled 2.2.7.0 → 2.2.16.0

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 (282) hide show
  1. checksums.yaml +4 -4
  2. data/.hashibot.hcl +2 -0
  3. data/CHANGELOG.md +228 -0
  4. data/Gemfile +1 -1
  5. data/README.md +9 -51
  6. data/RELEASE.md +1 -1
  7. data/bin/vagrant +50 -1
  8. data/contrib/README.md +1 -0
  9. data/contrib/sudoers/linux-suse +2 -2
  10. data/contrib/zsh/_vagrant +738 -0
  11. data/contrib/zsh/generate_zsh_completion.rb +165 -0
  12. data/lib/vagrant.rb +28 -5
  13. data/lib/vagrant/action.rb +7 -0
  14. data/lib/vagrant/action/builder.rb +184 -38
  15. data/lib/vagrant/action/builtin/box_add.rb +24 -8
  16. data/lib/vagrant/action/builtin/box_check_outdated.rb +2 -1
  17. data/lib/vagrant/action/builtin/cleanup_disks.rb +56 -0
  18. data/lib/vagrant/action/builtin/cloud_init_setup.rb +122 -0
  19. data/lib/vagrant/action/builtin/cloud_init_wait.rb +30 -0
  20. data/lib/vagrant/action/builtin/delayed.rb +26 -0
  21. data/lib/vagrant/action/builtin/disk.rb +14 -1
  22. data/lib/vagrant/action/builtin/handle_box.rb +3 -1
  23. data/lib/vagrant/action/builtin/handle_forwarded_port_collisions.rb +28 -9
  24. data/lib/vagrant/action/builtin/has_provisioner.rb +36 -0
  25. data/lib/vagrant/action/builtin/mixin_provisioners.rb +1 -0
  26. data/lib/vagrant/action/builtin/mixin_synced_folders.rb +20 -21
  27. data/lib/vagrant/action/builtin/set_hostname.rb +5 -1
  28. data/lib/vagrant/action/builtin/synced_folders.rb +16 -0
  29. data/lib/vagrant/action/builtin/trigger.rb +37 -0
  30. data/lib/vagrant/action/hook.rb +76 -23
  31. data/lib/vagrant/action/runner.rb +12 -27
  32. data/lib/vagrant/action/warden.rb +28 -31
  33. data/lib/vagrant/box.rb +11 -4
  34. data/lib/vagrant/box_collection.rb +1 -1
  35. data/lib/vagrant/bundler.rb +310 -61
  36. data/lib/vagrant/cli.rb +4 -2
  37. data/lib/vagrant/environment.rb +1 -0
  38. data/lib/vagrant/errors.rb +69 -1
  39. data/lib/vagrant/machine.rb +64 -11
  40. data/lib/vagrant/machine_index.rb +28 -1
  41. data/lib/vagrant/patches/net-ssh.rb +186 -0
  42. data/lib/vagrant/plugin/manager.rb +45 -16
  43. data/lib/vagrant/plugin/v2/command.rb +7 -2
  44. data/lib/vagrant/plugin/v2/components.rb +6 -0
  45. data/lib/vagrant/plugin/v2/manager.rb +67 -0
  46. data/lib/vagrant/plugin/v2/plugin.rb +13 -0
  47. data/lib/vagrant/plugin/v2/synced_folder.rb +50 -0
  48. data/lib/vagrant/plugin/v2/trigger.rb +64 -25
  49. data/lib/vagrant/shared_helpers.rb +36 -0
  50. data/lib/vagrant/ui.rb +43 -2
  51. data/lib/vagrant/util.rb +2 -0
  52. data/lib/vagrant/util/ansi_escape_code_remover.rb +1 -1
  53. data/lib/vagrant/util/caps.rb +48 -0
  54. data/lib/vagrant/util/credential_scrubber.rb +1 -1
  55. data/lib/vagrant/util/curl_helper.rb +12 -8
  56. data/lib/vagrant/util/directory.rb +19 -0
  57. data/lib/vagrant/util/downloader.rb +10 -5
  58. data/lib/vagrant/util/guest_hosts.rb +68 -0
  59. data/lib/vagrant/util/guest_inspection.rb +9 -1
  60. data/lib/vagrant/util/install_cli_autocomplete.rb +118 -0
  61. data/lib/vagrant/util/io.rb +7 -27
  62. data/lib/vagrant/util/ipv4_interfaces.rb +15 -0
  63. data/lib/vagrant/util/is_port_open.rb +8 -19
  64. data/lib/vagrant/util/map_command_options.rb +33 -0
  65. data/lib/vagrant/util/mime.rb +92 -0
  66. data/lib/vagrant/util/network_ip.rb +11 -1
  67. data/lib/vagrant/util/numeric.rb +28 -0
  68. data/lib/vagrant/util/platform.rb +10 -2
  69. data/lib/vagrant/util/powershell.rb +31 -15
  70. data/lib/vagrant/util/subprocess.rb +9 -1
  71. data/lib/vagrant/util/template_renderer.rb +2 -2
  72. data/lib/vagrant/util/uploader.rb +7 -4
  73. data/lib/vagrant/vagrantfile.rb +2 -2
  74. data/plugins/commands/autocomplete/command/install.rb +49 -0
  75. data/plugins/commands/autocomplete/command/root.rb +64 -0
  76. data/plugins/commands/autocomplete/plugin.rb +18 -0
  77. data/plugins/commands/cap/command.rb +5 -1
  78. data/plugins/commands/cloud/auth/login.rb +20 -23
  79. data/plugins/commands/cloud/auth/logout.rb +2 -10
  80. data/plugins/commands/cloud/auth/middleware/add_authentication.rb +60 -31
  81. data/plugins/commands/cloud/auth/middleware/add_downloader_authentication.rb +64 -0
  82. data/plugins/commands/cloud/auth/whoami.rb +18 -20
  83. data/plugins/commands/cloud/box/create.rb +33 -29
  84. data/plugins/commands/cloud/box/delete.rb +30 -24
  85. data/plugins/commands/cloud/box/show.rb +41 -31
  86. data/plugins/commands/cloud/box/update.rb +34 -26
  87. data/plugins/commands/cloud/client/client.rb +55 -79
  88. data/plugins/commands/cloud/list.rb +3 -4
  89. data/plugins/commands/cloud/locales/en.yml +15 -11
  90. data/plugins/commands/cloud/plugin.rb +10 -0
  91. data/plugins/commands/cloud/provider/create.rb +38 -28
  92. data/plugins/commands/cloud/provider/delete.rb +39 -29
  93. data/plugins/commands/cloud/provider/update.rb +37 -28
  94. data/plugins/commands/cloud/provider/upload.rb +53 -33
  95. data/plugins/commands/cloud/publish.rb +193 -106
  96. data/plugins/commands/cloud/search.rb +34 -21
  97. data/plugins/commands/cloud/util.rb +273 -161
  98. data/plugins/commands/cloud/version/create.rb +33 -28
  99. data/plugins/commands/cloud/version/delete.rb +35 -28
  100. data/plugins/commands/cloud/version/release.rb +35 -29
  101. data/plugins/commands/cloud/version/revoke.rb +36 -29
  102. data/plugins/commands/cloud/version/update.rb +29 -25
  103. data/plugins/commands/destroy/command.rb +7 -7
  104. data/plugins/commands/login/plugin.rb +0 -13
  105. data/plugins/commands/ssh_config/command.rb +1 -1
  106. data/plugins/communicators/ssh/communicator.rb +25 -24
  107. data/plugins/communicators/winrm/config.rb +1 -1
  108. data/plugins/communicators/winrm/helper.rb +1 -1
  109. data/plugins/communicators/winrm/shell.rb +1 -1
  110. data/plugins/communicators/winssh/communicator.rb +126 -38
  111. data/plugins/communicators/winssh/config.rb +3 -7
  112. data/plugins/guests/alpine/cap/change_host_name.rb +10 -11
  113. data/plugins/guests/alpine/cap/configure_networks.rb +1 -1
  114. data/plugins/guests/alt/cap/change_host_name.rb +40 -53
  115. data/plugins/guests/arch/cap/change_host_name.rb +5 -14
  116. data/plugins/guests/arch/cap/configure_networks.rb +27 -10
  117. data/plugins/guests/arch/cap/smb.rb +1 -1
  118. data/plugins/guests/atomic/cap/change_host_name.rb +5 -14
  119. data/plugins/guests/centos/cap/flavor.rb +24 -0
  120. data/plugins/guests/centos/guest.rb +9 -0
  121. data/plugins/guests/centos/plugin.rb +20 -0
  122. data/plugins/guests/darwin/cap/change_host_name.rb +10 -6
  123. data/plugins/guests/darwin/cap/darwin_version.rb +40 -0
  124. data/plugins/guests/darwin/cap/mount_smb_shared_folder.rb +1 -1
  125. data/plugins/guests/darwin/cap/mount_vmware_shared_folder.rb +33 -10
  126. data/plugins/guests/darwin/plugin.rb +15 -0
  127. data/plugins/guests/debian/cap/change_host_name.rb +12 -11
  128. data/plugins/guests/debian/cap/configure_networks.rb +14 -6
  129. data/plugins/guests/esxi/cap/public_key.rb +3 -1
  130. data/plugins/guests/fedora/guest.rb +4 -4
  131. data/plugins/guests/freebsd/cap/change_host_name.rb +10 -6
  132. data/plugins/guests/gentoo/cap/change_host_name.rb +14 -22
  133. data/plugins/guests/haiku/cap/rsync.rb +19 -0
  134. data/plugins/guests/haiku/plugin.rb +15 -0
  135. data/plugins/guests/linux/cap/change_host_name.rb +46 -0
  136. data/plugins/guests/linux/cap/halt.rb +9 -1
  137. data/plugins/guests/linux/cap/mount_smb_shared_folder.rb +25 -34
  138. data/plugins/guests/linux/cap/mount_virtualbox_shared_folder.rb +10 -11
  139. data/plugins/guests/linux/cap/persist_mount_shared_folder.rb +74 -0
  140. data/plugins/guests/linux/cap/reboot.rb +36 -7
  141. data/plugins/guests/linux/plugin.rb +10 -0
  142. data/plugins/guests/omnios/cap/change_host_name.rb +10 -16
  143. data/plugins/guests/openbsd/cap/change_host_name.rb +10 -6
  144. data/plugins/guests/openwrt/cap/change_host_name.rb +19 -0
  145. data/plugins/guests/openwrt/cap/halt.rb +16 -0
  146. data/plugins/guests/openwrt/cap/insert_public_key.rb +20 -0
  147. data/plugins/guests/openwrt/cap/remove_public_key.rb +22 -0
  148. data/plugins/guests/openwrt/cap/rsync.rb +35 -0
  149. data/plugins/guests/openwrt/guest.rb +23 -0
  150. data/plugins/guests/openwrt/plugin.rb +61 -0
  151. data/plugins/guests/photon/cap/change_host_name.rb +9 -15
  152. data/plugins/guests/pld/cap/change_host_name.rb +11 -17
  153. data/plugins/guests/redhat/cap/change_host_name.rb +14 -5
  154. data/plugins/guests/redhat/cap/flavor.rb +3 -1
  155. data/plugins/guests/redhat/cap/smb.rb +20 -0
  156. data/plugins/guests/redhat/plugin.rb +5 -0
  157. data/plugins/guests/slackware/cap/change_host_name.rb +11 -17
  158. data/plugins/guests/solaris11/plugin.rb +5 -0
  159. data/plugins/guests/suse/cap/change_host_name.rb +31 -9
  160. data/plugins/guests/windows/cap/public_key.rb +3 -3
  161. data/plugins/guests/windows/cap/reboot.rb +10 -5
  162. data/plugins/hosts/darwin/cap/fs_iso.rb +49 -0
  163. data/plugins/hosts/darwin/plugin.rb +10 -0
  164. data/plugins/hosts/linux/cap/fs_iso.rb +49 -0
  165. data/plugins/hosts/linux/cap/nfs.rb +1 -0
  166. data/plugins/hosts/linux/cap/rdp.rb +1 -1
  167. data/plugins/hosts/linux/plugin.rb +10 -0
  168. data/plugins/hosts/windows/cap/fs_iso.rb +48 -0
  169. data/plugins/hosts/windows/cap/rdp.rb +1 -1
  170. data/plugins/hosts/windows/plugin.rb +15 -0
  171. data/plugins/kernel_v2/config/cloud_init.rb +133 -0
  172. data/plugins/kernel_v2/config/disk.rb +67 -14
  173. data/plugins/kernel_v2/config/ssh_connect.rb +24 -0
  174. data/plugins/kernel_v2/config/vm.rb +155 -21
  175. data/plugins/kernel_v2/config/vm_provisioner.rb +13 -2
  176. data/plugins/kernel_v2/config/vm_trigger.rb +6 -5
  177. data/plugins/providers/docker/action.rb +8 -17
  178. data/plugins/providers/docker/action/forwarded_ports.rb +2 -0
  179. data/plugins/providers/docker/action/prepare_forwarded_port_collision_params.rb +61 -0
  180. data/plugins/providers/docker/cap/has_communicator.rb +11 -0
  181. data/plugins/providers/docker/communicator.rb +1 -1
  182. data/plugins/providers/docker/driver.rb +58 -7
  183. data/plugins/providers/docker/plugin.rb +5 -0
  184. data/plugins/providers/hyperv/action.rb +3 -1
  185. data/plugins/providers/hyperv/action/configure.rb +8 -0
  186. data/plugins/providers/hyperv/action/export.rb +4 -2
  187. data/plugins/providers/hyperv/cap/cleanup_disks.rb +54 -0
  188. data/plugins/providers/hyperv/cap/configure_disks.rb +200 -0
  189. data/plugins/providers/hyperv/cap/validate_disk_ext.rb +34 -0
  190. data/plugins/providers/hyperv/config.rb +5 -0
  191. data/plugins/providers/hyperv/driver.rb +90 -9
  192. data/plugins/providers/hyperv/plugin.rb +25 -0
  193. data/plugins/providers/hyperv/scripts/attach_disk_drive.ps1 +28 -0
  194. data/plugins/providers/hyperv/scripts/dismount_vhd.ps1 +13 -0
  195. data/plugins/providers/hyperv/scripts/get_vhd.ps1 +16 -0
  196. data/plugins/providers/hyperv/scripts/get_vm_status.ps1 +1 -1
  197. data/plugins/providers/hyperv/scripts/list_hdds.ps1 +17 -0
  198. data/plugins/providers/hyperv/scripts/new_vhd.ps1 +31 -0
  199. data/plugins/providers/hyperv/scripts/remove_disk_drive.ps1 +25 -0
  200. data/plugins/providers/hyperv/scripts/resize_disk_drive.ps1 +18 -0
  201. data/plugins/providers/hyperv/scripts/set_enhanced_session_transport_type.ps1 +24 -0
  202. data/plugins/providers/hyperv/scripts/set_vm_integration_services.ps1 +3 -3
  203. data/plugins/providers/hyperv/scripts/utils/VagrantVM/VagrantVM.psm1 +14 -6
  204. data/plugins/providers/virtualbox/action.rb +13 -1
  205. data/plugins/providers/virtualbox/action/export.rb +4 -2
  206. data/plugins/providers/virtualbox/action/forward_ports.rb +2 -2
  207. data/plugins/providers/virtualbox/action/import.rb +8 -4
  208. data/plugins/providers/virtualbox/action/network.rb +12 -5
  209. data/plugins/providers/virtualbox/action/prepare_clone_snapshot.rb +4 -2
  210. data/plugins/providers/virtualbox/action/snapshot_delete.rb +4 -2
  211. data/plugins/providers/virtualbox/action/snapshot_restore.rb +4 -2
  212. data/plugins/providers/virtualbox/cap/cleanup_disks.rb +85 -0
  213. data/plugins/providers/virtualbox/cap/configure_disks.rb +440 -0
  214. data/plugins/providers/virtualbox/cap/mount_options.rb +40 -0
  215. data/plugins/providers/virtualbox/cap/validate_disk_ext.rb +34 -0
  216. data/plugins/providers/virtualbox/driver/base.rb +15 -0
  217. data/plugins/providers/virtualbox/driver/meta.rb +16 -2
  218. data/plugins/providers/virtualbox/driver/version_5_0.rb +217 -2
  219. data/plugins/providers/virtualbox/driver/version_6_1.rb +23 -0
  220. data/plugins/providers/virtualbox/model/storage_controller.rb +135 -0
  221. data/plugins/providers/virtualbox/model/storage_controller_array.rb +98 -0
  222. data/plugins/providers/virtualbox/plugin.rb +42 -0
  223. data/plugins/providers/virtualbox/provider.rb +2 -1
  224. data/plugins/providers/virtualbox/synced_folder.rb +1 -0
  225. data/plugins/provisioners/ansible/cap/guest/alpine/ansible_install.rb +44 -0
  226. data/plugins/provisioners/ansible/cap/guest/freebsd/ansible_install.rb +1 -1
  227. data/plugins/provisioners/ansible/plugin.rb +5 -0
  228. data/plugins/provisioners/ansible/provisioner/base.rb +1 -1
  229. data/plugins/provisioners/container/client.rb +203 -0
  230. data/plugins/provisioners/container/config.rb +83 -0
  231. data/plugins/provisioners/container/installer.rb +13 -0
  232. data/plugins/provisioners/container/plugin.rb +23 -0
  233. data/plugins/provisioners/container/provisioner.rb +28 -0
  234. data/plugins/provisioners/docker/cap/{redhat → centos}/docker_install.rb +10 -7
  235. data/plugins/provisioners/docker/cap/centos/docker_start_service.rb +24 -0
  236. data/plugins/provisioners/docker/client.rb +4 -175
  237. data/plugins/provisioners/docker/config.rb +2 -72
  238. data/plugins/provisioners/docker/installer.rb +3 -5
  239. data/plugins/provisioners/docker/plugin.rb +6 -6
  240. data/plugins/provisioners/docker/provisioner.rb +4 -10
  241. data/plugins/provisioners/podman/cap/centos/podman_install.rb +35 -0
  242. data/plugins/provisioners/podman/cap/linux/podman_installed.rb +13 -0
  243. data/plugins/provisioners/podman/cap/redhat/podman_install.rb +26 -0
  244. data/plugins/provisioners/podman/client.rb +12 -0
  245. data/plugins/provisioners/podman/config.rb +28 -0
  246. data/plugins/provisioners/podman/installer.rb +33 -0
  247. data/plugins/provisioners/podman/plugin.rb +38 -0
  248. data/plugins/provisioners/podman/provisioner.rb +52 -0
  249. data/plugins/provisioners/salt/bootstrap-salt.sh +7 -4
  250. data/plugins/provisioners/salt/provisioner.rb +4 -0
  251. data/plugins/provisioners/shell/config.rb +1 -6
  252. data/plugins/provisioners/shell/provisioner.rb +61 -26
  253. data/plugins/synced_folders/nfs/synced_folder.rb +3 -1
  254. data/plugins/synced_folders/smb/cap/default_fstab_modification.rb +11 -0
  255. data/plugins/synced_folders/smb/cap/mount_options.rb +56 -0
  256. data/plugins/synced_folders/smb/plugin.rb +20 -0
  257. data/plugins/synced_folders/smb/synced_folder.rb +2 -2
  258. data/plugins/synced_folders/unix_mount_helpers.rb +14 -0
  259. data/scripts/website_push_www.sh +1 -1
  260. data/templates/commands/init/Vagrantfile.min.erb +3 -0
  261. data/templates/guests/arch/{network_dhcp.erb → default_network/network_dhcp.erb} +0 -0
  262. data/templates/guests/arch/{network_static.erb → default_network/network_static.erb} +0 -0
  263. data/templates/guests/arch/{network_static6.erb → default_network/network_static6.erb} +0 -0
  264. data/templates/guests/arch/systemd_networkd/network_dhcp.erb +6 -0
  265. data/templates/guests/arch/systemd_networkd/network_static.erb +9 -0
  266. data/templates/guests/arch/systemd_networkd/network_static6.erb +9 -0
  267. data/templates/guests/linux/etc_fstab.erb +6 -0
  268. data/templates/guests/nixos/network.erb +5 -6
  269. data/templates/locales/en.yml +221 -5
  270. data/templates/locales/providers_docker.yml +4 -0
  271. data/templates/nfs/exports_darwin.erb +1 -1
  272. data/vagrant.gemspec +14 -20
  273. data/version.txt +1 -1
  274. metadata +5092 -8978
  275. data/lib/vagrant/action/builtin/after_trigger.rb +0 -31
  276. data/lib/vagrant/action/builtin/before_trigger.rb +0 -28
  277. data/plugins/commands/login/client.rb +0 -253
  278. data/plugins/commands/login/command.rb +0 -137
  279. data/plugins/commands/login/errors.rb +0 -24
  280. data/plugins/commands/login/locales/en.yml +0 -49
  281. data/plugins/provisioners/docker/cap/redhat/docker_start_service.rb +0 -16
  282. data/scripts/website_push_docs.sh +0 -40
data/lib/vagrant/box.rb CHANGED
@@ -57,12 +57,15 @@ module Vagrant
57
57
  # @param [Symbol] provider The provider that this box implements.
58
58
  # @param [Pathname] directory The directory where this box exists on
59
59
  # disk.
60
- def initialize(name, provider, version, directory, **opts)
60
+ # @param [String] metadata_url Metadata URL for box
61
+ # @param [Hook] hook A hook to apply to the box downloader, for example, for authentication
62
+ def initialize(name, provider, version, directory, metadata_url: nil, hook: nil)
61
63
  @name = name
62
64
  @version = version
63
65
  @provider = provider
64
66
  @directory = directory
65
- @metadata_url = opts[:metadata_url]
67
+ @metadata_url = metadata_url
68
+ @hook = hook
66
69
 
67
70
  metadata_file = directory.join("metadata.json")
68
71
  raise Errors::BoxMetadataFileNotFound, name: @name if !metadata_file.file?
@@ -120,7 +123,7 @@ module Vagrant
120
123
  #
121
124
  # @param [Hash] download_options Options to pass to the downloader.
122
125
  # @return [BoxMetadata]
123
- def load_metadata(**download_options)
126
+ def load_metadata(download_options={})
124
127
  tf = Tempfile.new("vagrant-load-metadata")
125
128
  tf.close
126
129
 
@@ -132,7 +135,11 @@ module Vagrant
132
135
  end
133
136
 
134
137
  opts = { headers: ["Accept: application/json"] }.merge(download_options)
135
- Util::Downloader.new(url, tf.path, **opts).download!
138
+ d = Util::Downloader.new(url, tf.path, opts)
139
+ if @hook
140
+ @hook.call(:authenticate_box_downloader, downloader: d)
141
+ end
142
+ d.download!
136
143
  BoxMetadata.new(File.open(tf.path, "r"))
137
144
  rescue Errors::DownloaderError => e
138
145
  raise Errors::BoxMetadataDownloadError,
@@ -325,7 +325,7 @@ module Vagrant
325
325
 
326
326
  return Box.new(
327
327
  name, provider, version_dir_map[v.to_s], provider_dir,
328
- metadata_url: metadata_url,
328
+ metadata_url: metadata_url, hook: @hook
329
329
  )
330
330
  end
331
331
  end
@@ -18,6 +18,155 @@ module Vagrant
18
18
  # Bundler as a way to properly resolve all dependencies of Vagrant and
19
19
  # all Vagrant-installed plugins.
20
20
  class Bundler
21
+ class SolutionFile
22
+ # @return [Pathname] path to plugin file
23
+ attr_reader :plugin_file
24
+ # @return [Pathname] path to solution file
25
+ attr_reader :solution_file
26
+ # @return [Array<Gem::Resolver::DependencyRequest>] list of required dependencies
27
+ attr_reader :dependency_list
28
+
29
+ # @param [Pathname] plugin_file Path to plugin file
30
+ # @param [Pathname] solution_file Custom path to solution file
31
+ def initialize(plugin_file:, solution_file: nil)
32
+ @logger = Log4r::Logger.new("vagrant::bundler::solution_file")
33
+ @plugin_file = Pathname.new(plugin_file.to_s)
34
+ if solution_file
35
+ @solution_file = Pathname.new(solution_file.to_s)
36
+ else
37
+ @solution_file = Pathname.new(@plugin_file.to_s + ".sol")
38
+ end
39
+ @valid = false
40
+ @dependency_list = [].freeze
41
+ @logger.debug("new solution file instance plugin_file=#{plugin_file} " \
42
+ "solution_file=#{solution_file}")
43
+ load
44
+ end
45
+
46
+ # Set the list of dependencies for this solution
47
+ #
48
+ # @param [Array<Gem::Dependency>] dependency_list List of dependencies for the solution
49
+ # @return [Array<Gem::Resolver::DependencyRequest>]
50
+ def dependency_list=(dependency_list)
51
+ Array(dependency_list).each do |d|
52
+ if !d.is_a?(Gem::Dependency)
53
+ raise TypeError, "Expected `Gem::Dependency` but received `#{d.class}`"
54
+ end
55
+ end
56
+ @dependency_list = dependency_list.map do |d|
57
+ Gem::Resolver::DependencyRequest.new(d, nil).freeze
58
+ end.freeze
59
+ end
60
+
61
+ # @return [Boolean] contained solution is valid
62
+ def valid?
63
+ @valid
64
+ end
65
+
66
+ # @return [FalseClass] invalidate this solution file
67
+ def invalidate!
68
+ @valid = false
69
+ @logger.debug("manually invalidating solution file #{self}")
70
+ @valid
71
+ end
72
+
73
+ # Delete the solution file
74
+ #
75
+ # @return [Boolean] true if file was deleted
76
+ def delete!
77
+ if !solution_file.exist?
78
+ @logger.debug("solution file does not exist. nothing to delete.")
79
+ return false
80
+ end
81
+ @logger.debug("deleting solution file - #{solution_file}")
82
+ solution_file.delete
83
+ true
84
+ end
85
+
86
+ # Store the solution file
87
+ def store!
88
+ if !plugin_file.exist?
89
+ @logger.debug("plugin file does not exist, not storing solution")
90
+ return
91
+ end
92
+ if !solution_file.dirname.exist?
93
+ @logger.debug("creating directory for solution file: #{solution_file.dirname}")
94
+ solution_file.dirname.mkpath
95
+ end
96
+ @logger.debug("writing solution file contents to disk")
97
+ solution_file.write({
98
+ dependencies: dependency_list.map { |d|
99
+ [d.dependency.name, d.dependency.requirements_list]
100
+ },
101
+ checksum: plugin_file_checksum,
102
+ vagrant_version: Vagrant::VERSION
103
+ }.to_json)
104
+ @valid = true
105
+ end
106
+
107
+ def to_s # :nodoc:
108
+ "<Vagrant::Bundler::SolutionFile:#{plugin_file}:" \
109
+ "#{solution_file}:#{valid? ? "valid" : "invalid"}>"
110
+ end
111
+
112
+ protected
113
+
114
+ # Load the solution file for the plugin path provided
115
+ # if it exists. Validate solution is still applicable
116
+ # before injecting dependencies.
117
+ def load
118
+ if !plugin_file.exist? || !solution_file.exist?
119
+ @logger.debug("missing file so skipping loading")
120
+ return
121
+ end
122
+ solution = read_solution || return
123
+ return if !valid_solution?(
124
+ checksum: solution[:checksum],
125
+ version: solution[:vagrant_version]
126
+ )
127
+ @logger.debug("loading solution dependency list")
128
+ @dependency_list = Array(solution[:dependencies]).map do |name, requirements|
129
+ gd = Gem::Dependency.new(name, requirements)
130
+ Gem::Resolver::DependencyRequest.new(gd, nil).freeze
131
+ end.freeze
132
+ @logger.debug("solution dependency list: #{dependency_list}")
133
+ @valid = true
134
+ end
135
+
136
+ # Validate the given checksum matches the plugin file
137
+ # checksum
138
+ #
139
+ # @param [String] checksum Checksum value to validate
140
+ # @return [Boolean]
141
+ def valid_solution?(checksum:, version:)
142
+ file_checksum = plugin_file_checksum
143
+ @logger.debug("solution validation check CHECKSUM #{file_checksum} <-> #{checksum}" \
144
+ " VERSION #{Vagrant::VERSION} <-> #{version}")
145
+ plugin_file_checksum == checksum &&
146
+ Vagrant::VERSION == version
147
+ end
148
+
149
+ # @return [String] checksum of plugin file
150
+ def plugin_file_checksum
151
+ digest = Digest::SHA256.new
152
+ digest.file(plugin_file.to_s)
153
+ digest.hexdigest
154
+ end
155
+
156
+ # Read contents of solution file and parse
157
+ #
158
+ # @return [Hash]
159
+ def read_solution
160
+ @logger.debug("reading solution file - #{solution_file}")
161
+ begin
162
+ hash = JSON.load(solution_file.read)
163
+ Vagrant::Util::HashWithIndifferentAccess.new(hash)
164
+ rescue => err
165
+ @logger.warn("failed to load solution file, ignoring (error: #{err})")
166
+ nil
167
+ end
168
+ end
169
+ end
21
170
 
22
171
  # Location of HashiCorp gem repository
23
172
  HASHICORP_GEMSTORE = "https://gems.hashicorp.com/".freeze
@@ -34,26 +183,19 @@ module Vagrant
34
183
 
35
184
  # @return [Pathname] Global plugin path
36
185
  attr_reader :plugin_gem_path
186
+ # @return [Pathname] Global plugin solution set path
187
+ attr_reader :plugin_solution_path
37
188
  # @return [Pathname] Vagrant environment specific plugin path
38
189
  attr_reader :env_plugin_gem_path
190
+ # @return [Pathname] Vagrant environment data path
191
+ attr_reader :environment_data_path
192
+ # @return [Array<Gem::Specification>, nil] List of builtin specs
193
+ attr_accessor :builtin_specs
39
194
 
40
195
  def initialize
196
+ @builtin_specs = []
41
197
  @plugin_gem_path = Vagrant.user_data_path.join("gems", RUBY_VERSION).freeze
42
198
  @logger = Log4r::Logger.new("vagrant::bundler")
43
-
44
- # TODO: Remove fix when https://github.com/rubygems/rubygems/pull/2735
45
- # gets merged and released
46
- #
47
- # Because of a rubygems bug, we need to set the gemrc file path
48
- # through this method rather than relying on the environment varible
49
- # GEMRC. On windows, that path gets split on `:`: and `;`, which means
50
- # the drive letter gets treated as its own path. If that path exists locally,
51
- # (like having a random folder called `c` where the library was invoked),
52
- # it fails thinking the folder `c` is a gemrc file.
53
- gemrc_val = ENV["GEMRC"]
54
- ENV["GEMRC"] = ""
55
- Gem.configuration = Gem::ConfigFile.new(["--config-file", gemrc_val])
56
- ENV["GEMRC"] = gemrc_val
57
199
  end
58
200
 
59
201
  # Enable Vagrant environment specific plugins at given data path
@@ -61,12 +203,39 @@ module Vagrant
61
203
  # @param [Pathname] Path to Vagrant::Environment data directory
62
204
  # @return [Pathname] Path to environment specific gem directory
63
205
  def environment_path=(env_data_path)
206
+ if !env_data_path.is_a?(Pathname)
207
+ raise TypeError, "Expected `Pathname` but received `#{env_data_path.class}`"
208
+ end
64
209
  @env_plugin_gem_path = env_data_path.join("plugins", "gems", RUBY_VERSION).freeze
210
+ @environment_data_path = env_data_path
211
+ end
212
+
213
+ # Use the given options to create a solution file instance
214
+ # for use during initialization. When a Vagrant environment
215
+ # is in use, solution files will be stored within the environment's
216
+ # data directory. This is because the solution for loading global
217
+ # plugins is dependent on any solution generated for local plugins.
218
+ # When no Vagrant environment is in use (running Vagrant without a
219
+ # Vagrantfile), the Vagrant user data path will be used for solution
220
+ # storage since only the global plugins will be used.
221
+ #
222
+ # @param [Hash] opts Options passed to #init!
223
+ # @return [SolutionFile]
224
+ def load_solution_file(opts={})
225
+ return if !opts[:local] && !opts[:global]
226
+ return if opts[:local] && opts[:global]
227
+ return if opts[:local] && environment_data_path.nil?
228
+ solution_path = (environment_data_path || Vagrant.user_data_path) + "bundler"
229
+ solution_path += opts[:local] ? "local.sol" : "global.sol"
230
+ SolutionFile.new(
231
+ plugin_file: opts[:local] || opts[:global],
232
+ solution_file: solution_path
233
+ )
65
234
  end
66
235
 
67
236
  # Initializes Bundler and the various gem paths so that we can begin
68
237
  # loading gems.
69
- def init!(plugins, repair=false)
238
+ def init!(plugins, repair=false, **opts)
70
239
  if !@initial_specifications
71
240
  @initial_specifications = Gem::Specification.find_all{true}
72
241
  else
@@ -74,45 +243,83 @@ module Vagrant
74
243
  Gem::Specification.reset
75
244
  end
76
245
 
77
- # Add HashiCorp RubyGems source
78
- if !Gem.sources.include?(HASHICORP_GEMSTORE)
79
- sources = [HASHICORP_GEMSTORE] + Gem.sources.sources
80
- Gem.sources.replace(sources)
246
+ solution_file = load_solution_file(opts)
247
+ @logger.debug("solution file in use for init: #{solution_file}")
248
+
249
+ solution = nil
250
+ composed_set = generate_vagrant_set
251
+
252
+ # Force the composed set to allow prereleases
253
+ if Vagrant.allow_prerelease_dependencies?
254
+ @logger.debug("enabling prerelease dependency matching due to user request")
255
+ composed_set.prerelease = true
81
256
  end
82
257
 
83
- # Generate dependencies for all registered plugins
84
- plugin_deps = plugins.map do |name, info|
85
- Gem::Dependency.new(name, info['installed_gem_version'].to_s.empty? ? '> 0' : info['installed_gem_version'])
258
+ if solution_file&.valid?
259
+ @logger.debug("loading cached solution set")
260
+ solution = solution_file.dependency_list.map do |dep|
261
+ spec = composed_set.find_all(dep).first
262
+ if !spec
263
+ @logger.warn("failed to locate specification for dependency - #{dep}")
264
+ @logger.warn("invalidating solution file - #{solution_file}")
265
+ solution_file.invalidate!
266
+ break
267
+ end
268
+ dep_r = Gem::Resolver::DependencyRequest.new(dep, nil)
269
+ Gem::Resolver::ActivationRequest.new(spec, dep_r)
270
+ end
86
271
  end
87
272
 
88
- @logger.debug("Current generated plugin dependency list: #{plugin_deps}")
273
+ if !solution_file&.valid?
274
+ @logger.debug("generating solution set for configured plugins")
275
+ # Add HashiCorp RubyGems source
276
+ if !Gem.sources.include?(HASHICORP_GEMSTORE)
277
+ sources = [HASHICORP_GEMSTORE] + Gem.sources.sources
278
+ Gem.sources.replace(sources)
279
+ end
89
280
 
90
- # Load dependencies into a request set for resolution
91
- request_set = Gem::RequestSet.new(*plugin_deps)
92
- # Never allow dependencies to be remotely satisfied during init
93
- request_set.remote = false
281
+ # Generate dependencies for all registered plugins
282
+ plugin_deps = plugins.map do |name, info|
283
+ Gem::Dependency.new(name, info['installed_gem_version'].to_s.empty? ? '> 0' : info['installed_gem_version'])
284
+ end
94
285
 
95
- repair_result = nil
96
- begin
97
- # Compose set for resolution
98
- composed_set = generate_vagrant_set
99
- # Resolve the request set to ensure proper activation order
100
- solution = request_set.resolve(composed_set)
101
- rescue Gem::UnsatisfiableDependencyError => failure
102
- if repair
103
- raise failure if @init_retried
104
- @logger.debug("Resolution failed but attempting to repair. Failure: #{failure}")
105
- install(plugins)
106
- @init_retried = true
107
- retry
108
- else
109
- raise
286
+ @logger.debug("Current generated plugin dependency list: #{plugin_deps}")
287
+
288
+ # Load dependencies into a request set for resolution
289
+ request_set = Gem::RequestSet.new(*plugin_deps)
290
+ # Never allow dependencies to be remotely satisfied during init
291
+ request_set.remote = false
292
+
293
+ begin
294
+ @logger.debug("resolving solution from available specification set")
295
+ # Resolve the request set to ensure proper activation order
296
+ solution = request_set.resolve(composed_set)
297
+ @logger.debug("solution set for configured plugins has been resolved")
298
+ rescue Gem::UnsatisfiableDependencyError => failure
299
+ if repair
300
+ raise failure if @init_retried
301
+ @logger.debug("Resolution failed but attempting to repair. Failure: #{failure}")
302
+ install(plugins)
303
+ @init_retried = true
304
+ retry
305
+ else
306
+ raise
307
+ end
110
308
  end
111
309
  end
112
310
 
113
311
  # Activate the gems
312
+ @logger.debug("activating solution set")
114
313
  activate_solution(solution)
115
314
 
315
+ if solution_file && !solution_file.valid?
316
+ solution_file.dependency_list = solution.map do |activation|
317
+ activation.request.dependency
318
+ end
319
+ solution_file.store!
320
+ @logger.debug("solution set stored to - #{solution_file}")
321
+ end
322
+
116
323
  full_vagrant_spec_list = @initial_specifications +
117
324
  solution.map(&:full_spec)
118
325
 
@@ -287,7 +494,7 @@ module Vagrant
287
494
  if update[:gems] == true || (update[:gems].respond_to?(:include?) && update[:gems].include?(name))
288
495
  if Gem::Requirement.new(gem_version).exact?
289
496
  gem_version = "> 0"
290
- @logger.debug("Detected exact version match for `#{name}` plugin update. Reset to loose constraint #{gem_version.inspect}.")
497
+ @logger.debug("Detected exact version match for `#{name}` plugin update. Reset to loosen constraint #{gem_version.inspect}.")
291
498
  end
292
499
  skips << name
293
500
  end
@@ -309,6 +516,9 @@ module Vagrant
309
516
  @logger.debug("Enabling strict dependency enforcement")
310
517
  plugin_deps += vagrant_internal_specs.map do |spec|
311
518
  next if system_plugins.include?(spec.name)
519
+ # If this spec is for a default plugin included in
520
+ # the ruby stdlib, ignore it
521
+ next if spec.default_gem?
312
522
  # If we are not running within the installer and
313
523
  # we are not within a bundler environment then we
314
524
  # only want activated specs
@@ -363,17 +573,25 @@ module Vagrant
363
573
  generate_builtin_set(system_plugins),
364
574
  generate_plugin_set(skips)
365
575
  )
576
+
577
+ if Vagrant.allow_prerelease_dependencies?
578
+ @logger.debug("enabling prerelease dependency matching based on user request")
579
+ request_set.prerelease = true
580
+ installer_set.prerelease = true
581
+ end
582
+
366
583
  @logger.debug("Generating solution set for installation.")
367
584
 
368
585
  # Generate the required solution set for new plugins
369
586
  solution = request_set.resolve(installer_set)
587
+
370
588
  activate_solution(solution)
371
589
 
372
590
  # Remove gems which are already installed
373
- request_set.sorted_requests.delete_if do |activation_req|
374
- rs_spec = activation_req.spec
375
- if vagrant_internal_specs.detect{|ispec| ispec.name == rs_spec.name && ispec.version == rs_spec.version }
376
- @logger.debug("Removing activation request from install. Already installed. (#{rs_spec.spec.full_name})")
591
+ request_set.sorted_requests.delete_if do |act_req|
592
+ rs = act_req.spec
593
+ if vagrant_internal_specs.detect{ |i| i.name == rs.name && i.version == rs.version }
594
+ @logger.debug("Removing activation request from install. Already installed. (#{rs.spec.full_name})")
377
595
  true
378
596
  end
379
597
  end
@@ -387,9 +605,11 @@ module Vagrant
387
605
  install_path = extra[:env_local] ? env_plugin_gem_path : plugin_gem_path
388
606
  result = request_set.install_into(install_path.to_s, true,
389
607
  ignore_dependencies: true,
390
- prerelease: Vagrant.prerelease?,
391
- wrappers: true
608
+ prerelease: Vagrant.prerelease? || Vagrant.allow_prerelease_dependencies?,
609
+ wrappers: true,
610
+ document: []
392
611
  )
612
+
393
613
  result = result.map(&:full_spec)
394
614
  result.each do |spec|
395
615
  existing_paths = $LOAD_PATH.find_all{|s| s.include?(spec.full_name) }
@@ -421,15 +641,35 @@ module Vagrant
421
641
  def vagrant_internal_specs
422
642
  # activate any dependencies up front so we can always
423
643
  # pin them when resolving
424
- Gem::Specification.find { |s| s.name == "vagrant-unbundled" && s.activated? }.
425
- runtime_dependencies.each { |d| gem d.name, *d.requirement.as_list }
644
+ self_spec = Gem::Specification.find { |s| s.name == "vagrant-unbundled" && s.activated? }
645
+ if !self_spec
646
+ @logger.warn("Failed to locate activated vagrant specification. Activating...")
647
+ self_spec = Gem::Specification.find { |s| s.name == "vagrant-unbundled" }
648
+ if !self_spec
649
+ @logger.error("Failed to locate Vagrant RubyGem specification")
650
+ raise Vagrant::Errors::SourceSpecNotFound
651
+ end
652
+ self_spec.activate
653
+ @logger.info("Activated vagrant specification version - #{self_spec.version}")
654
+ end
426
655
  # discover all the gems we have available
427
656
  list = {}
428
- directories = [Gem::Specification.default_specifications_dir]
429
- Gem::Specification.find_all{true}.each do |spec|
430
- list[spec.full_name] = spec
657
+ if Gem.respond_to?(:default_specifications_dir)
658
+ spec_dir = Gem.default_specifications_dir
659
+ else
660
+ spec_dir = Gem::Specification.default_specifications_dir
431
661
  end
432
- if(!defined?(::Bundler))
662
+ directories = [spec_dir]
663
+ if Vagrant.in_bundler?
664
+ Gem::Specification.find_all{true}.each do |spec|
665
+ list[spec.full_name] = spec
666
+ end
667
+ else
668
+ builtin_specs.each do |spec|
669
+ list[spec.full_name] = spec
670
+ end
671
+ end
672
+ if Vagrant.in_installer?
433
673
  directories += Gem::Specification.dirs.find_all do |path|
434
674
  !path.start_with?(Gem.user_dir)
435
675
  end
@@ -528,7 +768,6 @@ module Vagrant
528
768
  request.name == matcher["gem_name"]
529
769
  end
530
770
  if desired_activation_request && !desired_activation_request.full_spec.activated?
531
- activation_request = desired_activation_request
532
771
  @logger.warn("Found misordered activation request for #{desired_activation_request.full_name}. Moving to solution HEAD.")
533
772
  solution.delete(desired_activation_request)
534
773
  solution.unshift(desired_activation_request)
@@ -600,12 +839,22 @@ module Vagrant
600
839
  end
601
840
 
602
841
  def find_all(req)
603
- @specs.select do |spec|
604
- allow_prerelease = spec.name == "vagrant-unbundled" && Vagrant.prerelease?
605
- req.match?(spec, allow_prerelease)
842
+ r = @specs.select do |spec|
843
+ # When matching requests against builtin specs, we _always_ enable
844
+ # prerelease matching since any prerelease that's found in this
845
+ # set has been added explicitly and should be available for all
846
+ # plugins to resolve against. This includes Vagrant itself since
847
+ # it is considered a prerelease when in development mode
848
+ req.match?(spec, true)
606
849
  end.map do |spec|
607
850
  Gem::Resolver::InstalledSpecification.new(self, spec)
608
851
  end
852
+ # If any of the results are a prerelease, we need to mark the request
853
+ # to allow prereleases so the solution can be properly fulfilled
854
+ if r.any? { |x| x.version.prerelease? }
855
+ req.dependency.prerelease = true
856
+ end
857
+ r
609
858
  end
610
859
  end
611
860
 
@@ -639,7 +888,7 @@ module Vagrant
639
888
  # DependencyRequest +req+.
640
889
  def find_all(req)
641
890
  @specs.values.flatten.select do |spec|
642
- req.match?(spec)
891
+ req.match?(spec, prerelease)
643
892
  end.map do |spec|
644
893
  source = Gem::Source::Vendor.new(@directories[spec])
645
894
  Gem::Resolver::VendorSpecification.new(self, spec, source)
@@ -649,7 +898,7 @@ module Vagrant
649
898
  ##
650
899
  # Loads a spec with the given +name+. +version+, +platform+ and +source+ are
651
900
  # ignored.
652
- def load_spec (name, version, platform, source)
901
+ def load_spec(name, version, platform, source)
653
902
  version = Gem::Version.new(version) if !version.is_a?(Gem::Version)
654
903
  @specs.fetch(name, []).detect{|s| s.name == name && s.version == version}
655
904
  end