vagrant-unbundled 1.9.1.1 → 1.9.5.1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (188) hide show
  1. checksums.yaml +4 -4
  2. data/.gitignore +3 -0
  3. data/CHANGELOG.md +126 -0
  4. data/LICENSE +1 -1
  5. data/README.md +1 -1
  6. data/bin/vagrant +11 -0
  7. data/contrib/bash/completion.sh +9 -9
  8. data/lib/vagrant.rb +4 -2
  9. data/lib/vagrant/action/builtin/box_add.rb +13 -8
  10. data/lib/vagrant/action/builtin/handle_forwarded_port_collisions.rb +67 -14
  11. data/lib/vagrant/action/builtin/provision.rb +10 -5
  12. data/lib/vagrant/action/general/package_setup_files.rb +51 -0
  13. data/lib/vagrant/action/general/package_setup_folders.rb +37 -0
  14. data/lib/vagrant/batch_action.rb +4 -2
  15. data/lib/vagrant/box_collection.rb +17 -5
  16. data/lib/vagrant/bundler.rb +110 -12
  17. data/lib/vagrant/cli.rb +1 -1
  18. data/lib/vagrant/environment.rb +4 -4
  19. data/lib/vagrant/errors.rb +32 -0
  20. data/lib/vagrant/shared_helpers.rb +14 -0
  21. data/lib/vagrant/ui.rb +3 -3
  22. data/lib/vagrant/util.rb +7 -1
  23. data/lib/vagrant/util/command_deprecation.rb +56 -0
  24. data/lib/vagrant/util/credential_scrubber.rb +29 -0
  25. data/lib/vagrant/util/guest_inspection.rb +47 -0
  26. data/lib/vagrant/util/platform.rb +217 -27
  27. data/lib/vagrant/util/powershell.rb +25 -0
  28. data/lib/vagrant/util/safe_exec.rb +9 -1
  29. data/lib/vagrant/util/ssh.rb +1 -1
  30. data/lib/vagrant/util/subprocess.rb +21 -2
  31. data/lib/vagrant/util/which.rb +6 -4
  32. data/plugins/commands/box/command/list.rb +1 -1
  33. data/plugins/commands/package/command.rb +3 -2
  34. data/plugins/commands/plugin/action.rb +2 -1
  35. data/plugins/commands/plugin/action/expunge_plugins.rb +20 -5
  36. data/plugins/commands/plugin/action/install_gem.rb +0 -8
  37. data/plugins/commands/plugin/command/expunge.rb +18 -5
  38. data/plugins/commands/plugin/command/mixin_install_opts.rb +1 -4
  39. data/plugins/commands/plugin/gem_helper.rb +5 -1
  40. data/plugins/commands/up/command.rb +1 -1
  41. data/plugins/commands/validate/command.rb +31 -0
  42. data/plugins/commands/validate/plugin.rb +17 -0
  43. data/plugins/communicators/ssh/communicator.rb +50 -25
  44. data/plugins/communicators/winrm/communicator.rb +8 -54
  45. data/plugins/communicators/winrm/config.rb +3 -0
  46. data/plugins/communicators/winrm/helper.rb +1 -1
  47. data/plugins/communicators/winrm/shell.rb +38 -32
  48. data/plugins/communicators/winssh/communicator.rb +161 -0
  49. data/plugins/communicators/winssh/config.rb +30 -0
  50. data/plugins/communicators/winssh/plugin.rb +21 -0
  51. data/plugins/guests/arch/cap/change_host_name.rb +0 -3
  52. data/plugins/guests/arch/cap/configure_networks.rb +1 -1
  53. data/plugins/guests/atomic/cap/change_host_name.rb +0 -3
  54. data/plugins/guests/darwin/cap/change_host_name.rb +0 -4
  55. data/plugins/guests/debian/cap/change_host_name.rb +0 -3
  56. data/plugins/guests/debian/cap/configure_networks.rb +2 -1
  57. data/plugins/guests/elementary/guest.rb +10 -0
  58. data/plugins/guests/elementary/plugin.rb +15 -0
  59. data/plugins/guests/esxi/cap/public_key.rb +63 -0
  60. data/plugins/guests/esxi/plugin.rb +11 -0
  61. data/plugins/guests/freebsd/cap/change_host_name.rb +0 -4
  62. data/plugins/guests/gentoo/cap/change_host_name.rb +7 -5
  63. data/plugins/guests/gentoo/cap/configure_networks.rb +57 -21
  64. data/plugins/guests/linux/cap/mount_smb_shared_folder.rb +20 -53
  65. data/plugins/guests/linux/cap/mount_virtualbox_shared_folder.rb +10 -58
  66. data/plugins/guests/linux/cap/network_interfaces.rb +8 -1
  67. data/plugins/guests/linux/cap/nfs.rb +5 -14
  68. data/plugins/guests/linux/guest.rb +3 -3
  69. data/plugins/guests/omnios/cap/change_host_name.rb +0 -3
  70. data/plugins/guests/openbsd/cap/change_host_name.rb +0 -4
  71. data/plugins/guests/photon/cap/change_host_name.rb +0 -3
  72. data/plugins/guests/pld/cap/change_host_name.rb +0 -3
  73. data/plugins/guests/redhat/cap/change_host_name.rb +2 -9
  74. data/plugins/guests/redhat/cap/configure_networks.rb +43 -21
  75. data/plugins/guests/slackware/cap/change_host_name.rb +0 -3
  76. data/plugins/guests/suse/cap/change_host_name.rb +0 -3
  77. data/plugins/guests/tinycore/cap/mount_nfs.rb +3 -8
  78. data/plugins/guests/windows/cap/configure_networks.rb +0 -4
  79. data/plugins/guests/windows/cap/mount_shared_folder.rb +13 -1
  80. data/plugins/guests/windows/scripts/mount_volume.ps1.erb +1 -1
  81. data/plugins/hosts/windows/cap/ps.rb +6 -1
  82. data/plugins/kernel_v2/config/vm.rb +6 -4
  83. data/plugins/providers/docker/action.rb +4 -7
  84. data/plugins/providers/docker/action/build.rb +1 -1
  85. data/plugins/providers/docker/action/create.rb +3 -0
  86. data/plugins/providers/docker/config.rb +27 -1
  87. data/plugins/providers/docker/driver.rb +15 -2
  88. data/plugins/providers/docker/driver/compose.rb +287 -0
  89. data/plugins/providers/docker/errors.rb +16 -0
  90. data/plugins/providers/docker/provider.rb +25 -10
  91. data/plugins/providers/hyperv/action.rb +33 -8
  92. data/plugins/providers/hyperv/action/export.rb +39 -0
  93. data/plugins/providers/hyperv/action/import.rb +23 -4
  94. data/plugins/providers/hyperv/action/package.rb +16 -0
  95. data/plugins/providers/hyperv/action/package_metadata_json.rb +34 -0
  96. data/plugins/providers/hyperv/action/package_setup_files.rb +16 -0
  97. data/plugins/providers/hyperv/action/package_setup_folders.rb +18 -0
  98. data/plugins/providers/hyperv/action/package_vagrantfile.rb +34 -0
  99. data/plugins/providers/hyperv/config.rb +20 -4
  100. data/plugins/providers/hyperv/driver.rb +12 -0
  101. data/plugins/providers/hyperv/scripts/export_vm.ps1 +15 -0
  102. data/plugins/providers/hyperv/scripts/get_network_mac.ps1 +28 -0
  103. data/plugins/providers/hyperv/scripts/has_vmcx_support.ps1 +11 -0
  104. data/plugins/providers/hyperv/scripts/import_vm_vmcx.ps1 +10 -4
  105. data/plugins/providers/hyperv/scripts/import_vm_xml.ps1 +9 -3
  106. data/plugins/providers/hyperv/scripts/set_vm_integration_services.ps1 +37 -0
  107. data/plugins/providers/virtualbox/action/package_setup_files.rb +7 -42
  108. data/plugins/providers/virtualbox/action/package_setup_folders.rb +6 -26
  109. data/plugins/providers/virtualbox/driver/base.rb +10 -0
  110. data/plugins/providers/virtualbox/driver/version_5_0.rb +11 -11
  111. data/plugins/providers/virtualbox/driver/version_5_1.rb +3 -721
  112. data/plugins/providers/virtualbox/provider.rb +9 -5
  113. data/plugins/providers/virtualbox/synced_folder.rb +1 -1
  114. data/plugins/providers/virtualbox/util/compile_forwarded_ports.rb +3 -1
  115. data/plugins/provisioners/ansible/cap/guest/arch/ansible_install.rb +2 -2
  116. data/plugins/provisioners/ansible/cap/guest/debian/ansible_install.rb +8 -8
  117. data/plugins/provisioners/ansible/cap/guest/fedora/ansible_install.rb +9 -5
  118. data/plugins/provisioners/ansible/cap/guest/freebsd/ansible_install.rb +1 -1
  119. data/plugins/provisioners/ansible/cap/guest/pip/pip.rb +5 -3
  120. data/plugins/provisioners/ansible/cap/guest/redhat/ansible_install.rb +7 -3
  121. data/plugins/provisioners/ansible/cap/guest/suse/ansible_install.rb +1 -1
  122. data/plugins/provisioners/ansible/cap/guest/ubuntu/ansible_install.rb +3 -3
  123. data/plugins/provisioners/ansible/config/guest.rb +7 -1
  124. data/plugins/provisioners/ansible/provisioner/guest.rb +1 -1
  125. data/plugins/provisioners/chef/cap/freebsd/chef_install.rb +18 -0
  126. data/plugins/provisioners/chef/cap/freebsd/chef_installed.rb +22 -0
  127. data/plugins/provisioners/chef/plugin.rb +10 -0
  128. data/plugins/provisioners/docker/cap/linux/docker_installed.rb +1 -0
  129. data/plugins/provisioners/salt/bootstrap-salt.ps1 +4 -4
  130. data/plugins/provisioners/shell/config.rb +2 -2
  131. data/plugins/provisioners/shell/provisioner.rb +66 -4
  132. data/plugins/pushes/atlas/push.rb +6 -0
  133. data/plugins/pushes/local-exec/config.rb +2 -2
  134. data/plugins/synced_folders/unix_mount_helpers.rb +105 -0
  135. data/templates/commands/init/Vagrantfile.erb +6 -0
  136. data/templates/commands/init/Vagrantfile.min.erb +3 -0
  137. data/templates/guests/debian/network_dhcp.erb +2 -2
  138. data/templates/guests/gentoo/network_systemd.erb +16 -0
  139. data/templates/guests/redhat/network_dhcp.erb +1 -0
  140. data/templates/guests/redhat/network_static.erb +1 -1
  141. data/templates/guests/redhat/network_static6.erb +1 -1
  142. data/templates/locales/en.yml +72 -3
  143. data/templates/locales/providers_docker.yml +15 -0
  144. data/test/acceptance/provider-virtualbox/linked_clone_spec.rb +1 -1
  145. data/test/unit/plugins/commands/init/command_test.rb +7 -0
  146. data/test/unit/plugins/commands/up/command_test.rb +65 -0
  147. data/test/unit/plugins/commands/validate/command_test.rb +52 -0
  148. data/test/unit/plugins/communicators/ssh/communicator_test.rb +58 -2
  149. data/test/unit/plugins/communicators/winrm/communicator_test.rb +14 -26
  150. data/test/unit/plugins/communicators/winrm/helper_test.rb +12 -0
  151. data/test/unit/plugins/communicators/winrm/shell_test.rb +54 -15
  152. data/test/unit/plugins/communicators/winssh/communicator_test.rb +525 -0
  153. data/test/unit/plugins/guests/arch/cap/configure_networks_test.rb +5 -0
  154. data/test/unit/plugins/guests/esxi/cap/public_key_test.rb +48 -0
  155. data/test/unit/plugins/guests/linux/cap/mount_nfs_test.rb +1 -1
  156. data/test/unit/plugins/guests/linux/cap/mount_smb_shared_folder.rb +71 -0
  157. data/test/unit/plugins/guests/linux/cap/mount_virtual_box_shared_folder_test.rb +43 -0
  158. data/test/unit/plugins/guests/linux/cap/network_interfaces_test.rb +10 -10
  159. data/test/unit/plugins/guests/redhat/cap/configure_networks_test.rb +132 -12
  160. data/test/unit/plugins/guests/windows/cap/mount_shared_folder_test.rb +23 -0
  161. data/test/unit/plugins/providers/docker/driver_compose_test.rb +268 -0
  162. data/test/unit/plugins/provisioners/ansible/config/guest_test.rb +9 -0
  163. data/test/unit/plugins/provisioners/ansible/provisioner_test.rb +2 -2
  164. data/test/unit/plugins/pushes/atlas/push_test.rb +151 -150
  165. data/test/unit/templates/guests/debian/network_dhcp_test.rb +1 -0
  166. data/test/unit/templates/guests/gentoo/systemd_network_test.rb +73 -0
  167. data/test/unit/templates/guests/redhat/network_dhcp_test.rb +18 -0
  168. data/test/unit/vagrant/action/builtin/box_add_test.rb +27 -0
  169. data/test/unit/vagrant/action/builtin/handle_forwarded_port_collisions_test.rb +170 -0
  170. data/test/unit/vagrant/action/builtin/provision_test.rb +208 -0
  171. data/test/unit/vagrant/box_collection_test.rb +98 -0
  172. data/test/unit/vagrant/environment_test.rb +17 -0
  173. data/test/unit/vagrant/shared_helpers_test.rb +12 -0
  174. data/test/unit/vagrant/util/command_deprecation_test.rb +106 -0
  175. data/test/unit/vagrant/util/env_test.rb +43 -0
  176. data/test/unit/vagrant/util/platform_test.rb +8 -0
  177. data/test/unit/vagrant/util/subprocess_test.rb +61 -0
  178. data/test/vagrant-spec/Vagrantfile.spec +78 -0
  179. data/test/vagrant-spec/boxes/.keep +0 -0
  180. data/test/vagrant-spec/configs/vagrant-spec.config.virtualbox.rb +10 -0
  181. data/test/vagrant-spec/scripts/centos-run.virtualbox.sh +8 -0
  182. data/test/vagrant-spec/scripts/centos-setup.virtualbox.sh +14 -0
  183. data/test/vagrant-spec/scripts/ubuntu-run.virtualbox.sh +8 -0
  184. data/test/vagrant-spec/scripts/ubuntu-setup.virtualbox.sh +12 -0
  185. data/vagrant.gemspec +6 -11
  186. data/version.txt +1 -1
  187. metadata +1694 -1835
  188. data/plugins/communicators/winrm/scripts/elevated_shell.ps1.erb +0 -101
@@ -259,9 +259,11 @@ module VagrantPlugins
259
259
  default_id = nil
260
260
 
261
261
  if type == :forwarded_port
262
- # For forwarded ports, set the default ID to the
263
- # host port so that host ports overwrite each other.
264
- default_id = "#{options[:protocol]}#{options[:host]}"
262
+ # For forwarded ports, set the default ID to be the
263
+ # concat of host_ip, proto and host_port. This would ensure Vagrant
264
+ # caters for port forwarding in an IP aliased environment where
265
+ # different host IP addresses are to be listened on the same port.
266
+ default_id = "#{options[:host_ip]}#{options[:protocol]}#{options[:host]}"
265
267
  end
266
268
 
267
269
  options[:id] = default_id || SecureRandom.uuid
@@ -697,7 +699,7 @@ module VagrantPlugins
697
699
  end
698
700
 
699
701
  if options[:host]
700
- key = "#{options[:protocol]}#{options[:host]}"
702
+ key = "#{options[:host_ip]}#{options[:protocol]}#{options[:host]}"
701
703
  if fp_used.include?(key)
702
704
  errors << I18n.t("vagrant.config.vm.network_fp_host_not_unique",
703
705
  host: options[:host].to_s,
@@ -177,14 +177,12 @@ module VagrantPlugins
177
177
  Vagrant::Action::Builder.new.tap do |b|
178
178
  b.use Call, IsState, :not_created do |env, b2|
179
179
  if env[:result]
180
- b2.use Message, I18n.t("docker_provider.messages.not_created")
181
- next
180
+ raise Errors::ContainerNotCreatedError
182
181
  end
183
182
 
184
183
  b2.use Call, IsState, :running do |env2, b3|
185
184
  if !env2[:result]
186
- b3.use Message, I18n.t("docker_provider.messages.not_running")
187
- next
185
+ raise Errors::ContainerNotRunningError
188
186
  end
189
187
 
190
188
  b3.use PrepareSSH
@@ -199,13 +197,12 @@ module VagrantPlugins
199
197
  Vagrant::Action::Builder.new.tap do |b|
200
198
  b.use Call, IsState, :not_created do |env, b2|
201
199
  if env[:result]
202
- b2.use Message, I18n.t("docker_provider.messages.not_created")
203
- next
200
+ raise Errors::ContainerNotCreatedError
204
201
  end
205
202
 
206
203
  b2.use Call, IsState, :running do |env2, b3|
207
204
  if !env2[:result]
208
- raise Vagrant::Errors::VMNotRunningError
205
+ raise Errors::ContainerNotRunningError
209
206
  end
210
207
 
211
208
  b3.use SSHRun
@@ -43,7 +43,7 @@ module VagrantPlugins
43
43
  dockerfile = machine.provider_config.dockerfile
44
44
  dockerfile_path = File.join(build_dir, dockerfile)
45
45
 
46
- args.push("--file=\"#{dockerfile_path}\"")
46
+ args.push("--file").push(dockerfile_path)
47
47
  machine.ui.output(
48
48
  I18n.t("docker_provider.building_named_dockerfile",
49
49
  file: machine.provider_config.dockerfile))
@@ -129,6 +129,9 @@ module VagrantPlugins
129
129
  # Don't include SSH if we've explicitly asked not to
130
130
  next if options[:id] == "ssh" && !include_ssh
131
131
 
132
+ # Skip port if it is disabled
133
+ next if options[:disabled]
134
+
132
135
  # If the guest port is 0, put it in the random group
133
136
  if options[:guest] == 0
134
137
  random << options[:host]
@@ -19,6 +19,18 @@ module VagrantPlugins
19
19
  # @return [String]
20
20
  attr_accessor :build_dir
21
21
 
22
+ # Use docker-compose to manage the lifecycle and environment for
23
+ # containers instead of using docker directly.
24
+ #
25
+ # @return [Boolean]
26
+ attr_accessor :compose
27
+
28
+ # Configuration Hash used for build the docker-compose composition
29
+ # file. This can be used for adding networks or volumes.
30
+ #
31
+ # @return [Hash]
32
+ attr_accessor :compose_configuration
33
+
22
34
  # An optional file name of a Dockerfile to be used when building
23
35
  # the image. This requires Docker >1.5.0.
24
36
  #
@@ -138,6 +150,8 @@ module VagrantPlugins
138
150
  @build_args = []
139
151
  @build_dir = UNSET_VALUE
140
152
  @cmd = UNSET_VALUE
153
+ @compose = UNSET_VALUE
154
+ @compose_configuration = {}
141
155
  @create_args = UNSET_VALUE
142
156
  @dockerfile = UNSET_VALUE
143
157
  @env = {}
@@ -201,6 +215,7 @@ module VagrantPlugins
201
215
  @build_args = [] if @build_args == UNSET_VALUE
202
216
  @build_dir = nil if @build_dir == UNSET_VALUE
203
217
  @cmd = [] if @cmd == UNSET_VALUE
218
+ @compose = false if @compose == UNSET_VALUE
204
219
  @create_args = [] if @create_args == UNSET_VALUE
205
220
  @dockerfile = nil if @dockerfile == UNSET_VALUE
206
221
  @env ||= {}
@@ -228,13 +243,20 @@ module VagrantPlugins
228
243
  # host VM. Other users can optionally disable this by setting the
229
244
  # value explicitly to false in their Vagrantfile.
230
245
  if @force_host_vm == UNSET_VALUE
231
- @force_host_vm = !Vagrant::Util::Platform.linux?
246
+ @force_host_vm = !Vagrant::Util::Platform.linux? &&
247
+ !Vagrant::Util::Platform.darwin? &&
248
+ !Vagrant::Util::Platform.windows?
232
249
  end
233
250
 
234
251
  # The machine name must be a symbol
235
252
  @vagrant_machine = @vagrant_machine.to_sym if @vagrant_machine
236
253
 
237
254
  @expose.uniq!
255
+
256
+ if @compose_configuration.is_a?(Hash)
257
+ # Ensures configuration is using basic types
258
+ @compose_configuration = JSON.parse(@compose_configuration.to_json)
259
+ end
238
260
  end
239
261
 
240
262
  def validate(machine)
@@ -255,6 +277,10 @@ module VagrantPlugins
255
277
  end
256
278
  end
257
279
 
280
+ if !@compose_configuration.is_a?(Hash)
281
+ errors << I18n.t("docker_provider.errors.config.compose_configuration_hash")
282
+ end
283
+
258
284
  if !@create_args.is_a?(Array)
259
285
  errors << I18n.t("docker_provider.errors.config.create_args_array")
260
286
  end
@@ -1,7 +1,8 @@
1
1
  require "json"
2
-
3
2
  require "log4r"
4
3
 
4
+ require_relative "./driver/compose"
5
+
5
6
  module VagrantPlugins
6
7
  module DockerProvider
7
8
  class Driver
@@ -45,7 +46,19 @@ module VagrantPlugins
45
46
  run_cmd += expose.map { |p| ['--expose', "#{p}"] }
46
47
  run_cmd += links.map { |k, v| ['--link', "#{k}:#{v}"] }
47
48
  run_cmd += ports.map { |p| ['-p', p.to_s] }
48
- run_cmd += volumes.map { |v| ['-v', v.to_s] }
49
+ run_cmd += volumes.map { |v|
50
+ v = v.to_s
51
+ if v.include?(":") && (Vagrant::Util::Platform.windows? || Vagrant::Util::Platform.wsl?)
52
+ host, guest = v.split(":", 2)
53
+ host = Vagrant::Util::Platform.windows_path(host)
54
+ # NOTE: Docker does not support UNC style paths (which also
55
+ # means that there's no long path support). Hopefully this
56
+ # will be fixed someday and the gsub below can be removed.
57
+ host.gsub!(/^[^A-Za-z]+/, "")
58
+ v = [host, guest].join(":")
59
+ end
60
+ ['-v', v.to_s]
61
+ }
49
62
  run_cmd += %W(--privileged) if params[:privileged]
50
63
  run_cmd += %W(-h #{params[:hostname]}) if params[:hostname]
51
64
  run_cmd << "-t" if params[:pty]
@@ -0,0 +1,287 @@
1
+ require "json"
2
+ require "log4r"
3
+
4
+ module VagrantPlugins
5
+ module DockerProvider
6
+ class Driver
7
+ class Compose < Driver
8
+
9
+ # @return [Integer] Maximum number of seconds to wait for lock
10
+ LOCK_TIMEOUT = 60
11
+ # @return [String] Compose file format version
12
+ COMPOSE_VERSION = "2".freeze
13
+
14
+ # @return [Pathname] data directory to store composition
15
+ attr_reader :data_directory
16
+ # @return [Vagrant::Machine]
17
+ attr_reader :machine
18
+
19
+ # Create a new driver instance
20
+ #
21
+ # @param [Vagrant::Machine] machine Machine instance for this driver
22
+ def initialize(machine)
23
+ if !Vagrant::Util::Which.which("vagrant-compose")
24
+ raise Errors::DockerComposeNotInstalledError
25
+ end
26
+ super()
27
+ @machine = machine
28
+ @data_directory = Pathname.new(machine.env.local_data_path).
29
+ join("docker-compose")
30
+ @data_directory.mkpath
31
+ @logger = Log4r::Logger.new("vagrant::docker::driver::compose")
32
+ @compose_lock = Mutex.new
33
+ @logger.debug("Docker compose driver initialize for machine `#{@machine.name}` (`#{@machine.id}`)")
34
+ @logger.debug("Data directory for composition file `#{@data_directory}`")
35
+ end
36
+
37
+ def build(dir, **opts, &block)
38
+ name = machine.name.to_s
39
+ @logger.debug("Applying build for `#{name}` using `#{dir}` directory.")
40
+ begin
41
+ update_composition do |composition|
42
+ services = composition["services"] ||= {}
43
+ services[name] ||= {}
44
+ services[name]["build"] = {"context" => dir}
45
+ # Extract custom dockerfile location if set
46
+ if opts[:extra_args] && opts[:extra_args].include?("--file")
47
+ services[name]["build"]["dockerfile"] = opts[:extra_args][opts[:extra_args].index("--file") + 1]
48
+ end
49
+ # Extract any build args that can be found
50
+ case opts[:build_args]
51
+ when Array
52
+ if opts[:build_args].include?("--build-arg")
53
+ idx = 0
54
+ build_args = {}
55
+ while(idx < opts[:build_args].size)
56
+ arg_value = opts[:build_args][idx]
57
+ idx += 1
58
+ if arg_value.start_with?("--build-arg")
59
+ if !arg_value.include?("=")
60
+ arg_value = opts[:build_args][idx]
61
+ idx += 1
62
+ end
63
+ key, val = arg_value.to_s.split("=", 2).to_s.split("=")
64
+ build_args[key] = val
65
+ end
66
+ end
67
+ end
68
+ when Hash
69
+ services[name]["build"]["args"] = opts[:build_args]
70
+ end
71
+ end
72
+ rescue => error
73
+ @logger.error("Failed to apply build using `#{dir}` directory: #{error.class} - #{error}")
74
+ update_composition do |composition|
75
+ composition["services"].delete(name)
76
+ end
77
+ raise
78
+ end
79
+ end
80
+
81
+ def create(params, **opts, &block)
82
+ # NOTE: Use the direct machine name as we don't
83
+ # need to worry about uniqueness with compose
84
+ name = machine.name.to_s
85
+ image = params.fetch(:image)
86
+ links = params.fetch(:links)
87
+ ports = Array(params[:ports])
88
+ volumes = Array(params[:volumes]).map do |v|
89
+ v = v.to_s
90
+ if v.include?(":") && (Vagrant::Util::Platform.windows? || Vagrant::Util::Platform.wsl?)
91
+ host, guest = v.split(":", 2)
92
+ host = Vagrant::Util::Platform.windows_path(host)
93
+ # NOTE: Docker does not support UNC style paths (which also
94
+ # means that there's no long path support). Hopefully this
95
+ # will be fixed someday and the gsub below can be removed.
96
+ host.gsub!(/^[^A-Za-z]+/, "")
97
+ v = [host, guest].join(":")
98
+ end
99
+ v
100
+ end
101
+ cmd = Array(params.fetch(:cmd))
102
+ env = Hash[*params.fetch(:env).flatten.map(&:to_s)]
103
+ expose = Array(params[:expose])
104
+ @logger.debug("Creating container `#{name}`")
105
+ begin
106
+ update_args = [:apply]
107
+ update_args.push(:detach) if params[:detach]
108
+ update_args << block
109
+ update_composition(*update_args) do |composition|
110
+ services = composition["services"] ||= {}
111
+ services[name] ||= {}
112
+ if params[:extra_args].is_a?(Hash)
113
+ services[name].merge!(
114
+ Hash[
115
+ params[:extra_args].map{ |k, v|
116
+ [k.to_s, v]
117
+ }
118
+ ]
119
+ )
120
+ end
121
+ services[name].merge!(
122
+ "environment" => env,
123
+ "expose" => expose,
124
+ "ports" => ports,
125
+ "volumes" => volumes,
126
+ "links" => links,
127
+ "command" => cmd
128
+ )
129
+ services[name]["image"] = image if image
130
+ services[name]["hostname"] = params[:hostname] if params[:hostname]
131
+ services[name]["privileged"] = true if params[:privileged]
132
+ services[name]["pty"] = true if params[:pty]
133
+ end
134
+ rescue => error
135
+ @logger.error("Failed to create container `#{name}`: #{error.class} - #{error}")
136
+ update_composition do |composition|
137
+ composition["services"].delete(name)
138
+ end
139
+ raise
140
+ end
141
+ get_container_id(name)
142
+ end
143
+
144
+ def rm(cid)
145
+ if created?(cid)
146
+ destroy = false
147
+ synchronized do
148
+ compose_execute("rm", "-f", machine.name.to_s)
149
+ update_composition do |composition|
150
+ if composition["services"] && composition["services"].key?(machine.name.to_s)
151
+ @logger.info("Removing container `#{machine.name}`")
152
+ if composition["services"].size > 1
153
+ composition["services"].delete(machine.name.to_s)
154
+ else
155
+ destroy = true
156
+ end
157
+ end
158
+ end
159
+ if destroy
160
+ @logger.info("No containers remain. Destroying full environment.")
161
+ compose_execute("down", "--volumes", "--rmi", "local")
162
+ @logger.info("Deleting composition path `#{composition_path}`")
163
+ composition_path.delete
164
+ end
165
+ end
166
+ end
167
+ end
168
+
169
+ def rmi(*_)
170
+ true
171
+ end
172
+
173
+ def created?(cid)
174
+ result = super
175
+ if !result
176
+ composition = get_composition
177
+ if composition["services"] && composition["services"].has_key?(machine.name.to_s)
178
+ result = true
179
+ end
180
+ end
181
+ result
182
+ end
183
+
184
+ private
185
+
186
+ # Lookup the ID for the container with the given name
187
+ #
188
+ # @param [String] name Name of container
189
+ # @return [String] Container ID
190
+ def get_container_id(name)
191
+ compose_execute("ps", "-q", name).chomp
192
+ end
193
+
194
+ # Execute a `docker-compose` command
195
+ def compose_execute(*cmd, **opts, &block)
196
+ synchronized do
197
+ execute("docker-compose", "-f", composition_path.to_s,
198
+ "-p", machine.env.cwd.basename.to_s, *cmd, **opts, &block)
199
+ end
200
+ end
201
+
202
+ # Apply any changes made to the composition
203
+ def apply_composition!(*args)
204
+ block = args.detect{|arg| arg.is_a?(Proc) }
205
+ execute_args = ["up", "--remove-orphans"]
206
+ if args.include?(:detach)
207
+ execute_args << "-d"
208
+ end
209
+ machine.env.lock("compose", retry: true) do
210
+ if block
211
+ compose_execute(*execute_args, &block)
212
+ else
213
+ compose_execute(*execute_args)
214
+ end
215
+ end
216
+ end
217
+
218
+ # Update the composition and apply changes if requested
219
+ #
220
+ # @param [Boolean] apply Apply composition changes
221
+ def update_composition(*args)
222
+ synchronized do
223
+ machine.env.lock("compose", retry: true) do
224
+ composition = get_composition
225
+ result = yield composition
226
+ write_composition(composition)
227
+ if args.include?(:apply) || (args.include?(:conditional) && result)
228
+ apply_composition!(*args)
229
+ end
230
+ end
231
+ end
232
+ end
233
+
234
+ # @return [Hash] current composition contents
235
+ def get_composition
236
+ composition = {"version" => COMPOSE_VERSION.dup}
237
+ if composition_path.exist?
238
+ composition = Vagrant::Util::DeepMerge.deep_merge(composition, YAML.load(composition_path.read))
239
+ end
240
+ composition = Vagrant::Util::DeepMerge.deep_merge(composition, machine.provider_config.compose_configuration.dup)
241
+ @logger.debug("Fetched composition with provider configuration applied: #{composition}")
242
+ composition
243
+ end
244
+
245
+ # Save the composition
246
+ #
247
+ # @param [Hash] composition New composition
248
+ def write_composition(composition)
249
+ @logger.debug("Saving composition to `#{composition_path}`: #{composition}")
250
+ tmp_file = Tempfile.new("vagrant-docker-compose")
251
+ tmp_file.write(composition.to_yaml)
252
+ tmp_file.close
253
+ synchronized do
254
+ FileUtils.mv(tmp_file.path, composition_path.to_s)
255
+ end
256
+ end
257
+
258
+ # @return [Pathname] path to the docker-compose.yml file
259
+ def composition_path
260
+ data_directory.join("docker-compose.yml")
261
+ end
262
+
263
+ def synchronized
264
+ if !@compose_lock.owned?
265
+ timeout = LOCK_TIMEOUT.to_f
266
+ until @compose_lock.owned?
267
+ if @compose_lock.try_lock
268
+ if timeout > 0
269
+ timeout -= sleep(1)
270
+ else
271
+ raise Errors::ComposeLockTimeoutError
272
+ end
273
+ end
274
+ end
275
+ got_lock = true
276
+ end
277
+ begin
278
+ result = yield
279
+ ensure
280
+ @compose_lock.unlock if got_lock
281
+ end
282
+ result
283
+ end
284
+ end
285
+ end
286
+ end
287
+ end