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.
- checksums.yaml +7 -0
- data/lib/podman.rb +1 -0
- data/lib/vagrant/podman/action/build.rb +99 -0
- data/lib/vagrant/podman/action/compare_synced_folders.rb +65 -0
- data/lib/vagrant/podman/action/connect_networks.rb +80 -0
- data/lib/vagrant/podman/action/create.rb +165 -0
- data/lib/vagrant/podman/action/destroy.rb +34 -0
- data/lib/vagrant/podman/action/destroy_build_image.rb +51 -0
- data/lib/vagrant/podman/action/destroy_network.rb +53 -0
- data/lib/vagrant/podman/action/forwarded_ports.rb +36 -0
- data/lib/vagrant/podman/action/has_ssh.rb +21 -0
- data/lib/vagrant/podman/action/host_machine.rb +75 -0
- data/lib/vagrant/podman/action/host_machine_build_dir.rb +49 -0
- data/lib/vagrant/podman/action/host_machine_port_checker.rb +34 -0
- data/lib/vagrant/podman/action/host_machine_port_warning.rb +40 -0
- data/lib/vagrant/podman/action/host_machine_required.rb +20 -0
- data/lib/vagrant/podman/action/host_machine_sync_folders.rb +176 -0
- data/lib/vagrant/podman/action/host_machine_sync_folders_disable.rb +91 -0
- data/lib/vagrant/podman/action/init_state.rb +23 -0
- data/lib/vagrant/podman/action/is_build.rb +19 -0
- data/lib/vagrant/podman/action/is_host_machine_created.rb +32 -0
- data/lib/vagrant/podman/action/login.rb +51 -0
- data/lib/vagrant/podman/action/prepare_forwarded_port_collision_params.rb +64 -0
- data/lib/vagrant/podman/action/prepare_networks.rb +397 -0
- data/lib/vagrant/podman/action/prepare_nfs_settings.rb +60 -0
- data/lib/vagrant/podman/action/prepare_nfs_valid_ids.rb +22 -0
- data/lib/vagrant/podman/action/prepare_ssh.rb +48 -0
- data/lib/vagrant/podman/action/pull.rb +30 -0
- data/lib/vagrant/podman/action/start.rb +24 -0
- data/lib/vagrant/podman/action/stop.rb +24 -0
- data/lib/vagrant/podman/action/wait_for_running.rb +71 -0
- data/lib/vagrant/podman/action.rb +319 -0
- data/lib/vagrant/podman/cap/has_communicator.rb +14 -0
- data/lib/vagrant/podman/cap/proxy_machine.rb +15 -0
- data/lib/vagrant/podman/cap/public_address.rb +26 -0
- data/lib/vagrant/podman/command/exec.rb +112 -0
- data/lib/vagrant/podman/command/logs.rb +111 -0
- data/lib/vagrant/podman/command/run.rb +76 -0
- data/lib/vagrant/podman/communicator.rb +199 -0
- data/lib/vagrant/podman/config.rb +368 -0
- data/lib/vagrant/podman/driver/compose.rb +315 -0
- data/lib/vagrant/podman/driver.rb +417 -0
- data/lib/vagrant/podman/errors.rb +108 -0
- data/lib/vagrant/podman/executor/local.rb +48 -0
- data/lib/vagrant/podman/executor/vagrant.rb +88 -0
- data/lib/vagrant/podman/hostmachine/Vagrantfile +3 -0
- data/lib/vagrant/podman/plugin.rb +89 -0
- data/lib/vagrant/podman/provider.rb +216 -0
- data/lib/vagrant/podman/synced_folder.rb +35 -0
- data/templates/locales/providers_podman.yml +321 -0
- 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
|