podman 1.0.3

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 (51) hide show
  1. checksums.yaml +7 -0
  2. data/lib/podman.rb +1 -0
  3. data/lib/vagrant/podman/action/build.rb +99 -0
  4. data/lib/vagrant/podman/action/compare_synced_folders.rb +65 -0
  5. data/lib/vagrant/podman/action/connect_networks.rb +80 -0
  6. data/lib/vagrant/podman/action/create.rb +165 -0
  7. data/lib/vagrant/podman/action/destroy.rb +34 -0
  8. data/lib/vagrant/podman/action/destroy_build_image.rb +51 -0
  9. data/lib/vagrant/podman/action/destroy_network.rb +53 -0
  10. data/lib/vagrant/podman/action/forwarded_ports.rb +36 -0
  11. data/lib/vagrant/podman/action/has_ssh.rb +21 -0
  12. data/lib/vagrant/podman/action/host_machine.rb +75 -0
  13. data/lib/vagrant/podman/action/host_machine_build_dir.rb +49 -0
  14. data/lib/vagrant/podman/action/host_machine_port_checker.rb +34 -0
  15. data/lib/vagrant/podman/action/host_machine_port_warning.rb +40 -0
  16. data/lib/vagrant/podman/action/host_machine_required.rb +20 -0
  17. data/lib/vagrant/podman/action/host_machine_sync_folders.rb +176 -0
  18. data/lib/vagrant/podman/action/host_machine_sync_folders_disable.rb +91 -0
  19. data/lib/vagrant/podman/action/init_state.rb +23 -0
  20. data/lib/vagrant/podman/action/is_build.rb +19 -0
  21. data/lib/vagrant/podman/action/is_host_machine_created.rb +32 -0
  22. data/lib/vagrant/podman/action/login.rb +51 -0
  23. data/lib/vagrant/podman/action/prepare_forwarded_port_collision_params.rb +64 -0
  24. data/lib/vagrant/podman/action/prepare_networks.rb +397 -0
  25. data/lib/vagrant/podman/action/prepare_nfs_settings.rb +60 -0
  26. data/lib/vagrant/podman/action/prepare_nfs_valid_ids.rb +22 -0
  27. data/lib/vagrant/podman/action/prepare_ssh.rb +48 -0
  28. data/lib/vagrant/podman/action/pull.rb +30 -0
  29. data/lib/vagrant/podman/action/start.rb +24 -0
  30. data/lib/vagrant/podman/action/stop.rb +24 -0
  31. data/lib/vagrant/podman/action/wait_for_running.rb +71 -0
  32. data/lib/vagrant/podman/action.rb +319 -0
  33. data/lib/vagrant/podman/cap/has_communicator.rb +14 -0
  34. data/lib/vagrant/podman/cap/proxy_machine.rb +15 -0
  35. data/lib/vagrant/podman/cap/public_address.rb +26 -0
  36. data/lib/vagrant/podman/command/exec.rb +112 -0
  37. data/lib/vagrant/podman/command/logs.rb +111 -0
  38. data/lib/vagrant/podman/command/run.rb +76 -0
  39. data/lib/vagrant/podman/communicator.rb +199 -0
  40. data/lib/vagrant/podman/config.rb +368 -0
  41. data/lib/vagrant/podman/driver/compose.rb +315 -0
  42. data/lib/vagrant/podman/driver.rb +417 -0
  43. data/lib/vagrant/podman/errors.rb +108 -0
  44. data/lib/vagrant/podman/executor/local.rb +48 -0
  45. data/lib/vagrant/podman/executor/vagrant.rb +88 -0
  46. data/lib/vagrant/podman/hostmachine/Vagrantfile +3 -0
  47. data/lib/vagrant/podman/plugin.rb +89 -0
  48. data/lib/vagrant/podman/provider.rb +216 -0
  49. data/lib/vagrant/podman/synced_folder.rb +35 -0
  50. data/templates/locales/providers_podman.yml +321 -0
  51. metadata +103 -0
@@ -0,0 +1,397 @@
1
+ # Copyright (c) HashiCorp, Inc.
2
+ # SPDX-License-Identifier: BUSL-1.1
3
+
4
+ require 'ipaddr'
5
+ require 'log4r'
6
+
7
+ require 'vagrant/util/scoped_hash_override'
8
+
9
+ module VagrantPlugins
10
+ module PodmanProvider
11
+ module Action
12
+ class PrepareNetworks
13
+
14
+ include Vagrant::Util::ScopedHashOverride
15
+
16
+ @@lock = Mutex.new
17
+
18
+ def initialize(app, env)
19
+ @app = app
20
+ @logger = Log4r::Logger.new('vagrant::plugins::podman::preparenetworks')
21
+ end
22
+
23
+ # Generate CLI arguments for creating the podman network.
24
+ #
25
+ # @param [Hash] options Options from the network config
26
+ # @returns[Array<String>] Network create arguments
27
+ def generate_create_cli_arguments(options)
28
+ options.map do |key, value|
29
+ # If value is false, option is not set
30
+ next if value.to_s == "false"
31
+ # If value is true, consider feature flag with no value
32
+ opt = value.to_s == "true" ? [] : [value]
33
+ opt.unshift("--#{key.to_s.tr("_", "-")}")
34
+ end.flatten.compact
35
+ end
36
+
37
+ # @return [Array<Socket::Ifaddr>] interface list
38
+ def list_interfaces
39
+ Socket.getifaddrs.find_all do |i|
40
+ !i.addr.nil? && i.addr.ip? && !i.addr.ipv4_loopback? &&
41
+ !i.addr.ipv6_loopback? && !i.addr.ipv6_linklocal?
42
+ end
43
+ end
44
+
45
+ # Validates that a network name exists. If it does not
46
+ # exist, an exception is raised.
47
+ #
48
+ # @param [String] network_name Name of existing network
49
+ # @param [Hash] env Local call env
50
+ # @return [Boolean]
51
+ def validate_network_name!(network_name, env)
52
+ if !env[:machine].provider.driver.existing_named_network?(network_name)
53
+ raise Errors::NetworkNameUndefined,
54
+ network_name: network_name
55
+ end
56
+ true
57
+ end
58
+
59
+ # Validates that the provided options are compatible with a
60
+ # pre-existing network. Raises exceptions on invalid configurations
61
+ #
62
+ # @param [String] network_name Name of the network
63
+ # @param [Hash] root_options Root networking options
64
+ # @param [Hash] network_options Podman scoped networking options
65
+ # @param [Driver] driver Podman driver
66
+ # @return [Boolean]
67
+ def validate_network_configuration!(network_name, root_options, network_options, driver)
68
+ if root_options[:ip] &&
69
+ driver.network_containing_address(root_options[:ip]) != network_name
70
+ raise Errors::NetworkAddressInvalid,
71
+ address: root_options[:ip],
72
+ network_name: network_name
73
+ end
74
+ if network_options[:subnet] &&
75
+ driver.network_containing_address(network_options[:subnet]) != network_name
76
+ raise Errors::NetworkSubnetInvalid,
77
+ subnet: network_options[:subnet],
78
+ network_name: network_name
79
+ end
80
+ true
81
+ end
82
+
83
+ # Generate configuration for private network
84
+ #
85
+ # @param [Hash] root_options Root networking options
86
+ # @param [Hash] net_options Podman scoped networking options
87
+ # @param [Hash] env Local call env
88
+ # @return [String, Hash] Network name and updated network_options
89
+ def process_private_network(root_options, network_options, env)
90
+ if root_options[:ip]
91
+ addr = IPAddr.new(root_options[:ip])
92
+ elsif addr.nil?
93
+ raise Errors::NetworkIPAddressRequired
94
+ end
95
+
96
+ if root_options[:name]
97
+ #if root_options[:name] && validate_network_name!(root_options[:name], env)
98
+ network_name = root_options[:name]
99
+ if !root_options[:subnet]
100
+ # Only generate a subnet if not given one
101
+ subnet = IPAddr.new("#{addr}/#{root_options[:netmask]}")
102
+ network = "#{subnet}/#{root_options[:netmask]}"
103
+ else
104
+ network = root_options[:subnet]
105
+ end
106
+ network_options[:subnet] = network
107
+ end
108
+
109
+ @logger.debug("network process options #{root_options[:name]}, #{root_options[:subnet]}, #{root_options[:ip]}")
110
+ if root_options[:type].to_s == "dhcp"
111
+ if !root_options[:ip] && !root_options[:subnet]
112
+ network_name = "vagrant_network" if !network_name
113
+ return [network_name, network_options]
114
+ end
115
+ if root_options[:subnet]
116
+ addr = IPAddr.new(root_options[:subnet])
117
+ root_options[:netmask] = addr.prefix
118
+ end
119
+ end
120
+
121
+ # If address is ipv6, enable ipv6 support
122
+ network_options[:ipv6] = addr.ipv6?
123
+
124
+ # If no mask is provided, attempt to locate any existing
125
+ # network which contains the assigned IP address
126
+ if !root_options[:netmask] && !network_name
127
+ network_name = env[:machine].provider.driver.
128
+ network_containing_address(root_options[:ip])
129
+ # When no existing network is found, we are creating
130
+ # a new network. Since no mask was provided, default
131
+ # to /24 for ipv4 and /64 for ipv6
132
+ if !network_name
133
+ root_options[:netmask] = addr.ipv4? ? 24 : 64
134
+ end
135
+ end
136
+
137
+ # With no network name, process options to find or determine
138
+ # name for new network
139
+ if !network_name
140
+ if !root_options[:subnet]
141
+ # Only generate a subnet if not given one
142
+ subnet = IPAddr.new("#{addr}/#{root_options[:netmask]}")
143
+ network = "#{subnet}/#{root_options[:netmask]}"
144
+ else
145
+ network = root_options[:subnet]
146
+ end
147
+
148
+ network_options[:subnet] = network
149
+ existing_network = env[:machine].provider.driver.
150
+ network_defined?(network)
151
+
152
+ if !existing_network
153
+ network_name = "vagrant_network_#{network}"
154
+ else
155
+ if !existing_network.to_s.start_with?("vagrant_network")
156
+ env[:ui].warn(I18n.t("podman_provider.subnet_exists",
157
+ network_name: existing_network,
158
+ subnet: network))
159
+ end
160
+ network_name = existing_network
161
+ end
162
+ end
163
+ @logger.debug("network process options #{network_name}, #{network_options[:subnet]}, #{network_options[:ip]}")
164
+ [network_name, network_options]
165
+ end
166
+
167
+ # Generate configuration for public network
168
+ #
169
+ # TODO: When the Vagrant installer upgrades to Ruby 2.5.x,
170
+ # remove all instances of the roundabout way of determining a prefix
171
+ # and instead just use the built-in `.prefix` method
172
+ #
173
+ # @param [Hash] root_options Root networking options
174
+ # @param [Hash] net_options Podman scoped networking options
175
+ # @param [Hash] env Local call env
176
+ # @return [String, Hash] Network name and updated network_options
177
+ def process_public_network(root_options, net_options, env)
178
+ if root_options[:ip]
179
+ addr = IPAddr.new(root_options[:ip])
180
+ elsif addr.nil?
181
+ raise Errors::NetworkIPAddressRequired
182
+ end
183
+
184
+ if root_options[:name]
185
+ #if root_options[:name] && validate_network_name!(root_options[:name], env)
186
+ network_name = root_options[:name]
187
+ if !root_options[:subnet]
188
+ # Only generate a subnet if not given one
189
+ subnet = IPAddr.new("#{addr}/#{root_options[:netmask]}")
190
+ network = "#{subnet}/#{root_options[:netmask]}"
191
+ else
192
+ network = root_options[:subnet]
193
+ end
194
+ network_options[:subnet] = network
195
+ end
196
+ if !network_name
197
+ valid_interfaces = list_interfaces
198
+ if valid_interfaces.empty?
199
+ raise Errors::NetworkNoInterfaces
200
+ elsif valid_interfaces.size == 1
201
+ bridge_interface = valid_interfaces.first
202
+ elsif idx = valid_interfaces.detect{|i| Array(root_options[:bridge]).include?(i.name) }
203
+ bridge_interface = idx
204
+ end
205
+ if !bridge_interface
206
+ env[:ui].info(I18n.t("vagrant.actions.vm.bridged_networking.available"),
207
+ prefix: false)
208
+ valid_interfaces.each_with_index do |int, i|
209
+ env[:ui].info("#{i + 1}) #{int.name}", prefix: false)
210
+ end
211
+ env[:ui].info(I18n.t(
212
+ "vagrant.actions.vm.bridged_networking.choice_help") + "\n",
213
+ prefix: false
214
+ )
215
+ end
216
+ while !bridge_interface
217
+ choice = env[:ui].ask(
218
+ I18n.t("vagrant.actions.vm.bridged_networking.select_interface") + " ",
219
+ prefix: false)
220
+ bridge_interface = valid_interfaces[choice.to_i - 1]
221
+ end
222
+ base_opts = Vagrant::Util::HashWithIndifferentAccess.new
223
+ base_opts[:opt] = "parent=#{bridge_interface.name}"
224
+ subnet = IPAddr.new(bridge_interface.addr.ip_address <<
225
+ "/" << bridge_interface.netmask.ip_unpack.first)
226
+ netmask = bridge_interface.netmask.ip_unpack.first
227
+ prefix = IPAddr.new("255.255.255.255/#{netmask}").to_i.to_s(2).count("1")
228
+ base_opts[:subnet] = "#{subnet}/#{prefix}"
229
+ subnet_addr = IPAddr.new(base_opts[:subnet])
230
+ base_opts[:driver] = "macvlan"
231
+ base_opts[:gateway] = subnet_addr.succ.to_s
232
+ base_opts[:ipv6] = subnet_addr.ipv6?
233
+ network_options = base_opts.merge(net_options)
234
+
235
+ # Check if network already exists for this subnet
236
+ network_name = env[:machine].provider.driver.
237
+ network_containing_address(network_options[:gateway])
238
+ if !network_name
239
+ network_name = "vagrant_network_public_#{bridge_interface.name}"
240
+ end
241
+
242
+ # If the network doesn't already exist, gather available address range
243
+ # within subnet which podman can provide addressing
244
+ if !env[:machine].provider.driver.existing_named_network?(network_name)
245
+ if !net_options[:gateway]
246
+ network_options[:gateway] = request_public_gateway(
247
+ network_options, bridge_interface.name, env)
248
+ end
249
+ network_options[:ip_range] = request_public_iprange(
250
+ network_options, bridge_interface, env)
251
+ end
252
+ end
253
+ [network_name, network_options]
254
+ end
255
+
256
+ # Request the gateway address for the public network
257
+ #
258
+ # @param [Hash] network_options Podman scoped networking options
259
+ # @param [String] interface The bridge interface used
260
+ # @param [Hash] env Local call env
261
+ # @return [String] Gateway address
262
+ def request_public_gateway(network_options, interface, env)
263
+ subnet = IPAddr.new(network_options[:subnet])
264
+ gateway = nil
265
+ while !gateway
266
+ gateway = env[:ui].ask(I18n.t(
267
+ "podman_provider.network_bridge_gateway_request",
268
+ interface: interface,
269
+ default_gateway: network_options[:gateway]) + " ",
270
+ prefix: false
271
+ ).strip
272
+ if gateway.empty?
273
+ gateway = network_options[:gateway]
274
+ end
275
+ begin
276
+ gateway = IPAddr.new(gateway)
277
+ if !subnet.include?(gateway)
278
+ env[:ui].warn(I18n.t("podman_provider.network_bridge_gateway_outofbounds",
279
+ gateway: gateway,
280
+ subnet: network_options[:subnet]) + "\n", prefix: false)
281
+ end
282
+ rescue IPAddr::InvalidAddressError
283
+ env[:ui].warn(I18n.t("podman_provider.network_bridge_gateway_invalid",
284
+ gateway: gateway) + "\n", prefix: false)
285
+ gateway = nil
286
+ end
287
+ end
288
+ gateway.to_s
289
+ end
290
+
291
+ # Request the IP range allowed for use by podman when creating a new
292
+ # public network
293
+ #
294
+ # TODO: When the Vagrant installer upgrades to Ruby 2.5.x,
295
+ # remove all instances of the roundabout way of determining a prefix
296
+ # and instead just use the built-in `.prefix` method
297
+ #
298
+ # @param [Hash] network_options Podman scoped networking options
299
+ # @param [Socket::Ifaddr] interface The bridge interface used
300
+ # @param [Hash] env Local call env
301
+ # @return [String] Address range
302
+ def request_public_iprange(network_options, interface, env)
303
+ return network_options[:ip_range] if network_options[:ip_range]
304
+ subnet = IPAddr.new(network_options[:subnet])
305
+ env[:ui].info(I18n.t(
306
+ "podman_provider.network_bridge_iprange_info") + "\n",
307
+ prefix: false
308
+ )
309
+ range = nil
310
+ while !range
311
+ range = env[:ui].ask(I18n.t(
312
+ "podman_provider.network_bridge_iprange_request",
313
+ interface: interface.name,
314
+ default_range: network_options[:subnet]) + " ",
315
+ prefix: false
316
+ ).strip
317
+ if range.empty?
318
+ range = network_options[:subnet]
319
+ end
320
+ begin
321
+ range = IPAddr.new(range)
322
+ if !subnet.include?(range)
323
+ netmask = interface.netmask.ip_unpack.first
324
+ prefix = IPAddr.new("255.255.255.255/#{netmask}").to_i.to_s(2).count("1")
325
+ env[:ui].warn(I18n.t(
326
+ "podman_provider.network_bridge_iprange_outofbounds",
327
+ subnet: network_options[:subnet],
328
+ range: "#{range}/#{prefix}"
329
+ ) + "\n", prefix: false)
330
+ range = nil
331
+ end
332
+ rescue IPAddr::InvalidAddressError
333
+ env[:ui].warn(I18n.t(
334
+ "podman_provider.network_bridge_iprange_invalid",
335
+ range: range) + "\n", prefix: false)
336
+ range = nil
337
+ end
338
+ end
339
+
340
+ netmask = interface.netmask.ip_unpack.first
341
+ prefix = IPAddr.new("255.255.255.255/#{netmask}").to_i.to_s(2).count("1")
342
+ "#{range}/#{prefix}"
343
+ end
344
+
345
+ # Execute the action
346
+ def call(env)
347
+ # If we are using a host VM, then don't worry about it
348
+ machine = env[:machine]
349
+ if machine.provider.host_vm?
350
+ @logger.debug("Not setting up networks because podman host_vm is in use")
351
+ return @app.call(env)
352
+ end
353
+
354
+ connections = {}
355
+ @@lock.synchronize do
356
+ machine.env.lock("podman-network-create", retry: true) do
357
+ env[:ui].info(I18n.t("podman_provider.network_create"))
358
+ machine.config.vm.networks.each_with_index do |net_info, net_idx|
359
+ type, options = net_info
360
+ network_options = scoped_hash_override(options, :podman_network)
361
+ network_options.delete_if{|k,_| options.key?(k)}
362
+
363
+ case type
364
+ when :public_network
365
+ network_name, network_options = process_public_network(
366
+ options, network_options, env)
367
+ when :private_network
368
+ network_name, network_options = process_private_network(
369
+ options, network_options, env)
370
+ else
371
+ next # unsupported type so ignore
372
+ end
373
+
374
+ if !network_name
375
+ raise Errors::NetworkInvalidOption, container: machine.name
376
+ end
377
+
378
+ if !machine.provider.driver.existing_named_network?(network_name)
379
+ @logger.debug("Creating network #{network_name}")
380
+ cli_opts = generate_create_cli_arguments(network_options)
381
+ machine.provider.driver.create_network(network_name, cli_opts)
382
+ else
383
+ @logger.debug("Network #{network_name} already created")
384
+ validate_network_configuration!(network_name, options, network_options, machine.provider.driver)
385
+ end
386
+ connections[net_idx] = network_name
387
+ end
388
+ end
389
+ end
390
+
391
+ env[:podman_connects] = connections
392
+ @app.call(env)
393
+ end
394
+ end
395
+ end
396
+ end
397
+ end
@@ -0,0 +1,60 @@
1
+ # Copyright (c) HashiCorp, Inc.
2
+ # SPDX-License-Identifier: BUSL-1.1
3
+
4
+ module VagrantPlugins
5
+ module PodmanProvider
6
+ module Action
7
+ class PrepareNFSSettings
8
+ include Vagrant::Util::Retryable
9
+
10
+ def initialize(app, env)
11
+ @app = app
12
+ @logger = Log4r::Logger.new("vagrant::action::vm::nfs")
13
+ end
14
+
15
+ def call(env)
16
+ @machine = env[:machine]
17
+
18
+ @app.call(env)
19
+
20
+ if using_nfs? && !privileged_container?
21
+ raise Errors::NfsWithoutPrivilegedError
22
+ end
23
+
24
+ if using_nfs?
25
+ @logger.info("Using NFS, preparing NFS settings by reading host IP and machine IP")
26
+ add_ips_to_env!(env)
27
+ end
28
+ end
29
+
30
+ # We're using NFS if we have any synced folder with NFS configured. If
31
+ # we are not using NFS we don't need to do the extra work to
32
+ # populate these fields in the environment.
33
+ def using_nfs?
34
+ @machine.config.vm.synced_folders.any? { |_, opts| opts[:type] == :nfs }
35
+ end
36
+
37
+ def privileged_container?
38
+ @machine.provider.driver.privileged?(@machine.id)
39
+ end
40
+
41
+ # Extracts the proper host and guest IPs for NFS mounts and stores them
42
+ # in the environment for the SyncedFolder action to use them in
43
+ # mounting.
44
+ #
45
+ # The ! indicates that this method modifies its argument.
46
+ def add_ips_to_env!(env)
47
+ provider = env[:machine].provider
48
+
49
+ host_ip = provider.driver.podman_bridge_ip
50
+ machine_ip = provider.ssh_info[:host]
51
+
52
+ raise Vagrant::Errors::NFSNoHostonlyNetwork if !host_ip || !machine_ip
53
+
54
+ env[:nfs_host_ip] = host_ip
55
+ env[:nfs_machine_ip] = machine_ip
56
+ end
57
+ end
58
+ end
59
+ end
60
+ end
@@ -0,0 +1,22 @@
1
+ # Copyright (c) HashiCorp, Inc.
2
+ # SPDX-License-Identifier: BUSL-1.1
3
+
4
+ module VagrantPlugins
5
+ module PodmanProvider
6
+ module Action
7
+ class PrepareNFSValidIds
8
+ def initialize(app, env)
9
+ @app = app
10
+ @logger = Log4r::Logger.new("vagrant::action::vm::nfs")
11
+ end
12
+
13
+ def call(env)
14
+ machine = env[:machine]
15
+ env[:nfs_valid_ids] = machine.provider.driver.all_containers
16
+
17
+ @app.call(env)
18
+ end
19
+ end
20
+ end
21
+ end
22
+ end
@@ -0,0 +1,48 @@
1
+ # Copyright (c) HashiCorp, Inc.
2
+ # SPDX-License-Identifier: BUSL-1.1
3
+
4
+ module VagrantPlugins
5
+ module PodmanProvider
6
+ module Action
7
+ class PrepareSSH
8
+ def initialize(app, env)
9
+ @app = app
10
+ end
11
+
12
+ def call(env)
13
+ # If we aren't using a host VM, then don't worry about it
14
+ return @app.call(env) if !env[:machine].provider.host_vm?
15
+
16
+ env[:machine].ui.output(I18n.t(
17
+ "podman_provider.ssh_through_host_vm"))
18
+
19
+ # Modify the SSH info to be the host VM's info
20
+ env[:ssh_info] = env[:machine].provider.host_vm.ssh_info
21
+
22
+ # Modify the SSH options for when we `vagrant ssh`...
23
+ ssh_opts = env[:ssh_opts] || {}
24
+
25
+ # Build the command we'll execute within the Podman host machine:
26
+ ssh_command = env[:machine].communicate.container_ssh_command
27
+ if !Array(ssh_opts[:extra_args]).empty?
28
+ ssh_command << " #{Array(ssh_opts[:extra_args]).join(" ")}"
29
+ end
30
+
31
+ # Modify the SSH options for the original command:
32
+ # Append "-t" to force a TTY allocation
33
+ ssh_opts[:extra_args] = ["-t"]
34
+ # Enable Agent forwarding when requested for the target VM
35
+ if env[:machine].ssh_info[:forward_agent]
36
+ ssh_opts[:extra_args] << "-o ForwardAgent=yes"
37
+ end
38
+ ssh_opts[:extra_args] << ssh_command
39
+
40
+ # Set the opts
41
+ env[:ssh_opts] = ssh_opts
42
+
43
+ @app.call(env)
44
+ end
45
+ end
46
+ end
47
+ end
48
+ end
@@ -0,0 +1,30 @@
1
+ # Copyright (c) HashiCorp, Inc.
2
+ # SPDX-License-Identifier: BUSL-1.1
3
+
4
+ module VagrantPlugins
5
+ module PodmanProvider
6
+ module Action
7
+ class Pull
8
+ def initialize(app, env)
9
+ @app = app
10
+ end
11
+
12
+ def call(env)
13
+ @env = env
14
+ @machine = env[:machine]
15
+ @provider_config = @machine.provider_config
16
+ @driver = @machine.provider.driver
17
+
18
+ # Skip pulling if the image is built
19
+ return @app.call(env) if @env[:create_image] || !@provider_config.pull
20
+
21
+ image = @provider_config.image
22
+ env[:ui].output(I18n.t("podman_provider.pull", image: image))
23
+ @driver.pull(image)
24
+
25
+ @app.call(env)
26
+ end
27
+ end
28
+ end
29
+ end
30
+ end
@@ -0,0 +1,24 @@
1
+ # Copyright (c) HashiCorp, Inc.
2
+ # SPDX-License-Identifier: BUSL-1.1
3
+
4
+ module VagrantPlugins
5
+ module PodmanProvider
6
+ module Action
7
+ class Start
8
+ def initialize(app, env)
9
+ @app = app
10
+ end
11
+
12
+ def call(env)
13
+ machine = env[:machine]
14
+ driver = machine.provider.driver
15
+
16
+ machine.ui.output(I18n.t("podman_provider.messages.starting"))
17
+ driver.start(machine.id)
18
+
19
+ @app.call(env)
20
+ end
21
+ end
22
+ end
23
+ end
24
+ end
@@ -0,0 +1,24 @@
1
+ # Copyright (c) HashiCorp, Inc.
2
+ # SPDX-License-Identifier: BUSL-1.1
3
+
4
+ module VagrantPlugins
5
+ module PodmanProvider
6
+ module Action
7
+ class Stop
8
+ def initialize(app, env)
9
+ @app = app
10
+ end
11
+
12
+ def call(env)
13
+ machine = env[:machine]
14
+ driver = machine.provider.driver
15
+ if driver.running?(machine.id)
16
+ env[:ui].info I18n.t("podman_provider.messages.stopping")
17
+ driver.stop(machine.id, machine.provider_config.stop_timeout)
18
+ end
19
+ @app.call(env)
20
+ end
21
+ end
22
+ end
23
+ end
24
+ end
@@ -0,0 +1,71 @@
1
+ # Copyright (c) HashiCorp, Inc.
2
+ # SPDX-License-Identifier: BUSL-1.1
3
+
4
+ require "thread"
5
+
6
+ require "log4r"
7
+
8
+ module VagrantPlugins
9
+ module PodmanProvider
10
+ module Action
11
+ class WaitForRunning
12
+ def initialize(app, env)
13
+ @app = app
14
+ @logger = Log4r::Logger.new("vagrant::podman::waitforrunning")
15
+ end
16
+
17
+ def call(env)
18
+ machine = env[:machine]
19
+
20
+ wait = true
21
+ if !machine.provider_config.remains_running
22
+ @logger.debug("remains_running is false")
23
+ wait = false
24
+ elsif machine.state.id == :running
25
+ @logger.debug("container is already running")
26
+ wait = false
27
+ end
28
+
29
+ # If we're not waiting, just return
30
+ return @app.call(env) if !wait
31
+
32
+ machine.ui.output(I18n.t("podman_provider.waiting_for_running"))
33
+
34
+ # First, make sure it leaves the stopped state if its supposed to.
35
+ after = sleeper(5)
36
+ while machine.state.id == :stopped
37
+ if after[:done]
38
+ raise Errors::StateStopped
39
+ end
40
+ sleep 0.2
41
+ end
42
+
43
+ # Then, wait for it to become running
44
+ after = sleeper(30)
45
+ while true
46
+ state = machine.state
47
+ break if state.id == :running
48
+ @logger.info("Waiting for container to run. State: #{state.id}")
49
+
50
+ if after[:done]
51
+ raise Errors::StateNotRunning
52
+ end
53
+
54
+ sleep 0.2
55
+ end
56
+
57
+ @app.call(env)
58
+ end
59
+
60
+ protected
61
+
62
+ def sleeper(duration)
63
+ Thread.new(duration) do |d|
64
+ sleep(d)
65
+ Thread.current[:done] = true
66
+ end
67
+ end
68
+ end
69
+ end
70
+ end
71
+ end