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,417 @@
1
+ # Copyright (c) HashiCorp, Inc.
2
+ # SPDX-License-Identifier: BUSL-1.1
3
+
4
+ require "json"
5
+ require "log4r"
6
+
7
+ require_relative "./driver/compose"
8
+
9
+ module VagrantPlugins
10
+ module PodmanProvider
11
+ class Driver
12
+ # The executor is responsible for actually executing Podman commands.
13
+ # This is set by the provider, but defaults to local execution.
14
+ attr_accessor :executor
15
+
16
+ def initialize
17
+ @logger = Log4r::Logger.new("vagrant::podman::driver")
18
+ @executor = Executor::Local.new
19
+ end
20
+
21
+ # Returns the id for a new container built from `podman build`. Raises
22
+ # an exception if the id was unable to be captured from the output
23
+ #
24
+ # @return [String] id - ID matched from the podman build output.
25
+ def build(dir, **opts, &block)
26
+ args = Array(opts[:extra_args])
27
+ args << dir
28
+ opts = {with_stderr: true}
29
+ result = execute('podman', 'build', *args, **opts, &block)
30
+ # Check for the new output format 'writing image sha256...'
31
+ # In this case, podman buildkit is enabled. Its format is different
32
+ # from standard podman
33
+ matches = result.scan(/writing image .+:([^\s]+)/i).last
34
+ if !matches
35
+ if podman?
36
+ # Check for podman format when it is emulating podman CLI.
37
+ # Podman outputs the full hash of the container on
38
+ # the last line after a successful build.
39
+ match = result.split.select { |str| str.match?(/[0-9a-z]{64}/) }.last
40
+ return match[0..7] unless match.nil?
41
+ else
42
+ matches = result.scan(/Successfully built (.+)$/i).last
43
+ end
44
+
45
+ if !matches
46
+ # This will cause a stack trace in Vagrant, but it is a bug
47
+ # if this happens anyways.
48
+ raise Errors::BuildError, result: result
49
+ end
50
+ end
51
+
52
+ # Return the matched group `id`
53
+ matches[0].strip
54
+ end
55
+
56
+ # Check if podman emulating podman CLI is enabled.
57
+ #
58
+ # @return [Bool]
59
+ def podman?
60
+ execute('podman', '--version').include?("podman")
61
+ end
62
+
63
+ def create(params, **opts, &block)
64
+ image = params.fetch(:image)
65
+ network = params.fetch(:network)
66
+ user = params.fetch(:user)
67
+ userns = params.fetch(:userns)
68
+ links = params.fetch(:links)
69
+ ports = Array(params[:ports])
70
+ volumes = Array(params[:volumes])
71
+ name = params.fetch(:name)
72
+ cmd = Array(params.fetch(:cmd))
73
+ env = params.fetch(:env)
74
+ expose = Array(params[:expose])
75
+
76
+ run_cmd = %W(podman run --name #{name})
77
+ run_cmd << "-d" if params[:detach]
78
+ run_cmd += env.map { |k,v| ['-e', "#{k}=#{v}"] }
79
+ run_cmd += expose.map { |p| ['--expose', "#{p}"] }
80
+ run_cmd += links.map { |k, v| ['--link', "#{k}:#{v}"] }
81
+ run_cmd += ports.map { |p| ['-p', p.to_s] }
82
+ run_cmd += volumes.map { |v|
83
+ v = v.to_s
84
+ if v.include?(":") && @executor.windows?
85
+ if v.index(":") != v.rindex(":")
86
+ # If we have 2 colons, the host path is an absolute Windows URL
87
+ # and we need to remove the colon from it
88
+ host, _, guest = v.rpartition(":")
89
+ host = "//" + host[0].downcase + host[2..-1]
90
+ v = [host, guest].join(":")
91
+ else
92
+ host, guest = v.split(":", 2)
93
+ host = Vagrant::Util::Platform.windows_path(host)
94
+ # NOTE: Podman does not support UNC style paths (which also
95
+ # means that there's no long path support). Hopefully this
96
+ # will be fixed someday and the gsub below can be removed.
97
+ host.gsub!(/^[^A-Za-z]+/, "")
98
+ v = [host, guest].join(":")
99
+ end
100
+ end
101
+
102
+ ['-v', v.to_s]
103
+ }
104
+ run_cmd += %W(--privileged) if params[:privileged]
105
+ run_cmd += %W(-h #{params[:hostname]}) if params[:hostname]
106
+ run_cmd += %W(--network #{params[:network]}) if params[:network]
107
+ run_cmd += %W(--user #{params[:user]}) if params[:user]
108
+ run_cmd += %W(--userns #{params[:userns]}) if params[:userns]
109
+ run_cmd << "-t" if params[:pty]
110
+ run_cmd << "--rm=true" if params[:rm]
111
+ run_cmd += params[:extra_args] if params[:extra_args]
112
+ run_cmd += [image, cmd]
113
+
114
+ execute(*run_cmd.flatten, **opts, &block).chomp.lines.last
115
+ end
116
+
117
+ def state(cid)
118
+ case
119
+ when running?(cid)
120
+ :running
121
+ when created?(cid)
122
+ :stopped
123
+ else
124
+ :not_created
125
+ end
126
+ end
127
+
128
+ def created?(cid)
129
+ result = execute('podman', 'ps', '-a', '-q', '--no-trunc').to_s
130
+ result =~ /^#{Regexp.escape cid}$/
131
+ end
132
+
133
+ def image?(id)
134
+ result = execute('podman', 'images', '-q').to_s
135
+ result =~ /^#{Regexp.escape(id)}$/
136
+ end
137
+
138
+ # Reads all current podman containers and determines what ports
139
+ # are currently registered to be forwarded
140
+ # {2222=>#<Set: {"127.0.0.1"}>, 8080=>#<Set: {"*"}>, 9090=>#<Set: {"*"}>}
141
+ #
142
+ # Note: This is this format because of what the builtin action for resolving colliding
143
+ # port forwards expects.
144
+ #
145
+ # @return [Hash[Set]] used_ports - {forward_port: #<Set: {"host ip address"}>}
146
+ def read_used_ports
147
+ used_ports = Hash.new{|hash,key| hash[key] = Set.new}
148
+
149
+ all_containers.each do |c|
150
+ container_info = inspect_container(c)
151
+
152
+ active = container_info["State"]["Running"]
153
+ next unless active # Ignore used ports on inactive containers
154
+
155
+ if container_info["HostConfig"]["PortBindings"]
156
+ port_bindings = container_info["HostConfig"]["PortBindings"]
157
+ next if port_bindings.empty? # Nothing defined, but not nil either
158
+
159
+ port_bindings.each do |guest_port,host_mapping|
160
+ host_mapping.each do |h|
161
+ if h["HostIp"] == ""
162
+ hostip = "*"
163
+ else
164
+ hostip = h["HostIp"]
165
+ end
166
+ hostport = h["HostPort"]
167
+ used_ports[hostport].add(hostip)
168
+ end
169
+ end
170
+ end
171
+ end
172
+
173
+ used_ports
174
+ end
175
+
176
+ def running?(cid)
177
+ result = execute('podman', 'ps', '-q', '--no-trunc')
178
+ result =~ /^#{Regexp.escape cid}$/m
179
+ end
180
+
181
+ def privileged?(cid)
182
+ inspect_container(cid)['HostConfig']['Privileged']
183
+ end
184
+
185
+ def login(email, username, password, server)
186
+ cmd = %W(podman login)
187
+ cmd += ["-e", email] if email != ""
188
+ cmd += ["-u", username] if username != ""
189
+ cmd += ["-p", password] if password != ""
190
+ cmd << server if server && server != ""
191
+
192
+ execute(*cmd.flatten)
193
+ end
194
+
195
+ def logout(server)
196
+ cmd = %W(podman logout)
197
+ cmd << server if server && server != ""
198
+ execute(*cmd.flatten)
199
+ end
200
+
201
+ def pull(image)
202
+ execute('podman', 'pull', image)
203
+ end
204
+
205
+ def start(cid)
206
+ if !running?(cid)
207
+ execute('podman', 'start', cid)
208
+ # This resets the cached information we have around, allowing `vagrant reload`s
209
+ # to work properly
210
+ @data = nil
211
+ end
212
+ end
213
+
214
+ def stop(cid, timeout)
215
+ if running?(cid)
216
+ execute('podman', 'stop', '-t', timeout.to_s, cid)
217
+ end
218
+ end
219
+
220
+ def rm(cid)
221
+ if created?(cid)
222
+ execute('podman', 'rm', '-f', '-v', cid)
223
+ end
224
+ end
225
+
226
+ def rmi(id)
227
+ execute('podman', 'rmi', id)
228
+ return true
229
+ rescue => e
230
+ return false if e.to_s.include?("is using it")
231
+ return false if e.to_s.include?("is being used")
232
+ raise if !e.to_s.include?("No such image")
233
+ end
234
+
235
+ # Inspect the provided container
236
+ #
237
+ # @param [String] cid ID or name of container
238
+ # @return [Hash]
239
+ def inspect_container(cid)
240
+ JSON.parse(execute('podman', 'inspect', cid)).first
241
+ end
242
+
243
+ # @return [Array<String>] list of all container IDs
244
+ def all_containers
245
+ execute('podman', 'ps', '-a', '-q', '--no-trunc').to_s.split
246
+ end
247
+
248
+ # Attempts to first use the podman-cli tool to inspect the default bridge subnet
249
+ # Falls back to using /sbin/ip if that fails
250
+ #
251
+ # @return [String] IP address of the podman bridge
252
+ def podman_bridge_ip
253
+ bridge = inspect_network("bridge")&.first
254
+ if bridge
255
+ bridge_ip = bridge.dig("subnets", 0, "gateway")
256
+ end
257
+ return bridge_ip if bridge_ip
258
+ @logger.debug("Failed to get bridge ip from podman, falling back to `ip`")
259
+ podman_bridge_ip_fallback
260
+ end
261
+
262
+ def podman_bridge_ip_fallback
263
+ output = execute('ip', '-4', 'addr', 'show', 'scope', 'global', 'podman0')
264
+ if output =~ /^\s+inet ([0-9.]+)\/[0-9]+\s+/
265
+ return $1.to_s
266
+ else
267
+ # TODO: Raise an user friendly message
268
+ raise 'Unable to fetch podman bridge IP!'
269
+ end
270
+ end
271
+
272
+ # @param [String] network - name of network to connect conatiner to
273
+ # @param [String] cid - container id
274
+ # @param [Array] opts - An array of flags used for listing networks
275
+ def connect_network(network, cid, opts=nil)
276
+ command = ['podman', 'network', 'connect', network, cid].push(*opts)
277
+ output = execute(*command)
278
+ output
279
+ end
280
+
281
+ # @param [String] network - name of network to create
282
+ # @param [Array] opts - An array of flags used for listing networks
283
+ def create_network(network, opts=nil)
284
+ command = ['podman', 'network', 'create', network].push(*opts)
285
+ output = execute(*command)
286
+ output
287
+ end
288
+
289
+ # @param [String] network - name of network to disconnect container from
290
+ # @param [String] cid - container id
291
+ def disconnect_network(network, cid)
292
+ command = ['podman', 'network', 'disconnect', network, cid, "--force"]
293
+ output = execute(*command)
294
+ output
295
+ end
296
+
297
+ # @param [Array] networks - list of networks to inspect
298
+ # @param [Array] opts - An array of flags used for listing networks
299
+ def inspect_network(network, opts=nil)
300
+ command = ['podman', 'network', 'inspect'] + Array(network)
301
+ command = command.push(*opts)
302
+ output = execute(*command)
303
+ begin
304
+ JSON.load(output)
305
+ rescue JSON::ParserError
306
+ @logger.warn("Failed to parse network inspection of network: #{network}")
307
+ @logger.debug("Failed network output content: `#{output.inspect}`")
308
+ nil
309
+ end
310
+ end
311
+
312
+ # @param [String] opts - Flags used for listing networks
313
+ def list_network(*opts)
314
+ command = ['podman', 'network', 'ls', *opts]
315
+ output = execute(*command)
316
+ output
317
+ end
318
+
319
+ # Will delete _all_ defined but unused networks in the podman engine. Even
320
+ # networks not created by Vagrant.
321
+ #
322
+ # @param [Array] opts - An array of flags used for listing networks
323
+ def prune_network(opts=nil)
324
+ command = ['podman', 'network', 'prune', '--force'].push(*opts)
325
+ output = execute(*command)
326
+ output
327
+ end
328
+
329
+ # Delete network(s)
330
+ #
331
+ # @param [String] network - name of network to remove
332
+ def rm_network(*network)
333
+ command = ['podman', 'network', 'rm', *network]
334
+ output = execute(*command)
335
+ output
336
+ end
337
+
338
+ # @param [Array] opts - An array of flags used for listing networks
339
+ def execute(*cmd, **opts, &block)
340
+ @executor.execute(*cmd, **opts, &block)
341
+ end
342
+
343
+ # ######################
344
+ # Podman network helpers
345
+ # ######################
346
+
347
+ # Determines if a given network has been defined through vagrant with a given
348
+ # subnet string
349
+ #
350
+ # @param [String] subnet_string - Subnet to look for
351
+ # @return [String] network name - Name of network with requested subnet.`nil` if not found
352
+ def network_defined?(subnet_string)
353
+ all_networks = list_network_names
354
+
355
+ network_info = inspect_network(all_networks)
356
+ network_info.each do |network|
357
+ config = Array(network["subnets"])
358
+ if (config.size > 0 &&
359
+ config.first["subnet"] == subnet_string)
360
+ @logger.debug("Found existing network #{network["name"]} already configured with #{subnet_string}")
361
+ return network["name"]
362
+ end
363
+ end
364
+ return nil
365
+ end
366
+
367
+ # Locate network which contains given address
368
+ #
369
+ # @param [String] address IP address
370
+ # @return [String] network name
371
+ def network_containing_address(address)
372
+ names = list_network_names
373
+ networks = inspect_network(names)
374
+ return if !networks
375
+ networks.each do |net|
376
+ next if !net["subnets"]
377
+ config = net["subnets"]
378
+ next if !config || config.size < 1
379
+ config.each do |opts|
380
+ subnet = IPAddr.new(opts["subnet"])
381
+ if subnet.include?(address)
382
+ return net["name"]
383
+ end
384
+ end
385
+ end
386
+ nil
387
+ end
388
+
389
+ # Looks to see if a podman network has already been defined
390
+ # with the given name
391
+ #
392
+ # @param [String] network_name - name of network to look for
393
+ # @return [Bool]
394
+ def existing_named_network?(network_name)
395
+ result = list_network_names
396
+ result.any?{|net_name| net_name == network_name}
397
+ end
398
+
399
+ # @return [Array<String>] list of all podman networks
400
+ def list_network_names
401
+ list_network("--format={{.Name}}").split("\n").map(&:strip)
402
+ end
403
+
404
+ # Returns true or false if network is in use or not.
405
+ # Nil if Vagrant fails to receive proper JSON from `podman network inspect`
406
+ #
407
+ # @param [String] network - name of network to look for
408
+ # @return [Bool,nil]
409
+ def network_used?(network)
410
+ result = inspect_network(network)
411
+ return nil if !result
412
+ return result.first["Containers"].size > 0
413
+ end
414
+
415
+ end
416
+ end
417
+ end
@@ -0,0 +1,108 @@
1
+ # Copyright (c) HashiCorp, Inc.
2
+ # SPDX-License-Identifier: BUSL-1.1
3
+
4
+ module VagrantPlugins
5
+ module PodmanProvider
6
+ module Errors
7
+ class PodmanError < Vagrant::Errors::VagrantError
8
+ error_namespace("podman_provider.errors")
9
+ end
10
+
11
+ class BuildError < PodmanError
12
+ error_key(:build_error)
13
+ end
14
+
15
+ class CommunicatorNonPodman < PodmanError
16
+ error_key(:communicator_non_podman)
17
+ end
18
+
19
+ class ComposeLockTimeoutError < PodmanError
20
+ error_key(:compose_lock_timeout)
21
+ end
22
+
23
+ class ContainerNotRunningError < PodmanError
24
+ error_key(:not_running)
25
+ end
26
+
27
+ class ContainerNotCreatedError < PodmanError
28
+ error_key(:not_created)
29
+ end
30
+
31
+ class PodmanComposeNotInstalledError < PodmanError
32
+ error_key(:podman_compose_not_installed)
33
+ end
34
+
35
+ class ExecuteError < PodmanError
36
+ error_key(:execute_error)
37
+ end
38
+
39
+ class ExecCommandRequired < PodmanError
40
+ error_key(:exec_command_required)
41
+ end
42
+
43
+ class HostVMCommunicatorNotReady < PodmanError
44
+ error_key(:host_vm_communicator_not_ready)
45
+ end
46
+
47
+ class ImageNotConfiguredError < PodmanError
48
+ error_key(:podman_provider_image_not_configured)
49
+ end
50
+
51
+ class NfsWithoutPrivilegedError < PodmanError
52
+ error_key(:podman_provider_nfs_without_privileged)
53
+ end
54
+
55
+ class NetworkAddressInvalid < PodmanError
56
+ error_key(:network_address_invalid)
57
+ end
58
+
59
+ class NetworkIPAddressRequired < PodmanError
60
+ error_key(:network_address_required)
61
+ end
62
+
63
+ class NetworkSubnetInvalid < PodmanError
64
+ error_key(:network_subnet_invalid)
65
+ end
66
+
67
+ class NetworkInvalidOption < PodmanError
68
+ error_key(:network_invalid_option)
69
+ end
70
+
71
+ class NetworkNameMissing < PodmanError
72
+ error_key(:network_name_missing)
73
+ end
74
+
75
+ class NetworkNameUndefined < PodmanError
76
+ error_key(:network_name_undefined)
77
+ end
78
+
79
+ class NetworkNoInterfaces < PodmanError
80
+ error_key(:network_no_interfaces)
81
+ end
82
+
83
+ class PackageNotSupported < PodmanError
84
+ error_key(:package_not_supported)
85
+ end
86
+
87
+ class StateNotRunning < PodmanError
88
+ error_key(:state_not_running)
89
+ end
90
+
91
+ class StateStopped < PodmanError
92
+ error_key(:state_stopped)
93
+ end
94
+
95
+ class SuspendNotSupported < PodmanError
96
+ error_key(:suspend_not_supported)
97
+ end
98
+
99
+ class SyncedFolderNonPodman < PodmanError
100
+ error_key(:synced_folder_non_podman)
101
+ end
102
+
103
+ class VagrantfileNotFound < PodmanError
104
+ error_key(:vagrantfile_not_found)
105
+ end
106
+ end
107
+ end
108
+ end
@@ -0,0 +1,48 @@
1
+ # Copyright (c) HashiCorp, Inc.
2
+ # SPDX-License-Identifier: BUSL-1.1
3
+
4
+ require "vagrant/util/busy"
5
+ require "vagrant/util/subprocess"
6
+
7
+ module VagrantPlugins
8
+ module PodmanProvider
9
+ module Executor
10
+ # The Local executor executes a Podman client that is running
11
+ # locally.
12
+ class Local
13
+ def execute(*cmd, **opts, &block)
14
+ # Append in the options for subprocess
15
+ cmd << { notify: [:stdout, :stderr] }
16
+
17
+ interrupted = false
18
+ int_callback = ->{ interrupted = true }
19
+ result = ::Vagrant::Util::Busy.busy(int_callback) do
20
+ ::Vagrant::Util::Subprocess.execute(*cmd, &block)
21
+ end
22
+
23
+ result.stderr.gsub!("\r\n", "\n")
24
+ result.stdout.gsub!("\r\n", "\n")
25
+
26
+ if result.exit_code != 0 && !interrupted
27
+ raise Errors::ExecuteError,
28
+ command: cmd.inspect,
29
+ stderr: result.stderr,
30
+ stdout: result.stdout
31
+ end
32
+
33
+ if opts
34
+ if opts[:with_stderr]
35
+ return result.stdout + " " + result.stderr
36
+ else
37
+ return result.stdout
38
+ end
39
+ end
40
+ end
41
+
42
+ def windows?
43
+ ::Vagrant::Util::Platform.windows? || ::Vagrant::Util::Platform.wsl?
44
+ end
45
+ end
46
+ end
47
+ end
48
+ end
@@ -0,0 +1,88 @@
1
+ # Copyright (c) HashiCorp, Inc.
2
+ # SPDX-License-Identifier: BUSL-1.1
3
+
4
+ require "vagrant/util/shell_quote"
5
+
6
+ module VagrantPlugins
7
+ module PodmanProvider
8
+ module Executor
9
+ # The Vagrant executor runs Podman over SSH against the given
10
+ # Vagrant-managed machine.
11
+ class Vagrant
12
+ def initialize(host_machine)
13
+ @host_machine = host_machine
14
+ end
15
+
16
+ def execute(*cmd, **opts, &block)
17
+ quote = '"'
18
+ cmd = cmd.map do |a|
19
+ "#{quote}#{::Vagrant::Util::ShellQuote.escape(a, quote)}#{quote}"
20
+ end.join(" ")
21
+
22
+ # If we want stdin, we just run in a full subprocess
23
+ return ssh_run(cmd) if opts[:stdin]
24
+
25
+ # Add a start fence so we know when to start reading output.
26
+ # We have to do this because boot2podman outputs a login shell
27
+ # boot2podman version that we get otherwise and messes up output.
28
+ start_fence = "========== VAGRANT PODMAN BEGIN =========="
29
+ ssh_cmd = "echo -n \"#{start_fence}\"; #{cmd}"
30
+
31
+ stderr = ""
32
+ stdout = ""
33
+ fenced = false
34
+ comm = @host_machine.communicate
35
+ code = comm.execute(ssh_cmd, error_check: false) do |type, data|
36
+ next if ![:stdout, :stderr].include?(type)
37
+ stderr << data if type == :stderr
38
+ stdout << data if type == :stdout
39
+
40
+ if !fenced
41
+ index = stdout.index(start_fence)
42
+ if index
43
+ fenced = true
44
+
45
+ index += start_fence.length
46
+ stdout = stdout[index..-1]
47
+ stdout.chomp!
48
+
49
+ # We're now fenced, send all the data through
50
+ if block
51
+ block.call(:stdout, stdout) if stdout != ""
52
+ block.call(:stderr, stderr) if stderr != ""
53
+ end
54
+ end
55
+ else
56
+ # If we're already fenced, just send the data through.
57
+ block.call(type, data) if block && fenced
58
+ end
59
+ end
60
+
61
+ if code != 0
62
+ raise Errors::ExecuteError,
63
+ command: cmd,
64
+ stderr: stderr.chomp,
65
+ stdout: stdout.chomp
66
+ end
67
+
68
+ stdout.chomp
69
+ end
70
+
71
+ def windows?
72
+ false
73
+ end
74
+
75
+ protected
76
+
77
+ def ssh_run(cmd)
78
+ @host_machine.action(
79
+ :ssh_run,
80
+ ssh_run_command: cmd,
81
+ )
82
+
83
+ ""
84
+ end
85
+ end
86
+ end
87
+ end
88
+ end
@@ -0,0 +1,3 @@
1
+ Vagrant.configure("2") do |config|
2
+ config.vm.box = "hashicorp/boot2podman"
3
+ end