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,315 @@
1
+ # Copyright (c) HashiCorp, Inc.
2
+ # SPDX-License-Identifier: BUSL-1.1
3
+
4
+ require "json"
5
+ require "log4r"
6
+
7
+ module VagrantPlugins
8
+ module PodmanProvider
9
+ class Driver
10
+ class Compose < Driver
11
+
12
+ # @return [Integer] Maximum number of seconds to wait for lock
13
+ LOCK_TIMEOUT = 60
14
+ # @return [String] Compose file format version
15
+ COMPOSE_VERSION = "2".freeze
16
+
17
+ # @return [Pathname] data directory to store composition
18
+ attr_reader :data_directory
19
+ # @return [Vagrant::Machine]
20
+ attr_reader :machine
21
+
22
+ # Create a new driver instance
23
+ #
24
+ # @param [Vagrant::Machine] machine Machine instance for this driver
25
+ def initialize(machine)
26
+ if !Vagrant::Util::Which.which("podman-compose")
27
+ raise Errors::PodmanComposeNotInstalledError
28
+ end
29
+ super()
30
+ @machine = machine
31
+ @data_directory = Pathname.new(machine.env.local_data_path).
32
+ join("podman-compose")
33
+ @data_directory.mkpath
34
+ @logger = Log4r::Logger.new("vagrant::podman::driver::compose")
35
+ @compose_lock = Mutex.new
36
+ @logger.debug("Podman compose driver initialize for machine `#{@machine.name}` (`#{@machine.id}`)")
37
+ @logger.debug("Data directory for composition file `#{@data_directory}`")
38
+ end
39
+
40
+ # Updates the podman compose config file with the given arguments
41
+ #
42
+ # @param [String] dir - local directory or git repo URL
43
+ # @param [Hash] opts - valid key: extra_args
44
+ # @param [Block] block
45
+ # @return [Nil]
46
+ def build(dir, **opts, &block)
47
+ name = machine.name.to_s
48
+ @logger.debug("Applying build for `#{name}` using `#{dir}` directory.")
49
+ begin
50
+ update_composition do |composition|
51
+ services = composition["services"] ||= {}
52
+ services[name] ||= {}
53
+ services[name]["build"] = {"context" => dir}
54
+ # Extract custom podmanfile location if set
55
+ if opts[:extra_args] && opts[:extra_args].include?("--file")
56
+ services[name]["build"]["podmanfile"] = opts[:extra_args][opts[:extra_args].index("--file") + 1]
57
+ end
58
+ # Extract any build args that can be found
59
+ case opts[:extra_args]
60
+ when Array
61
+ if opts[:extra_args].include?("--build-arg")
62
+ idx = 0
63
+ extra_args = {}
64
+ while(idx < opts[:extra_args].size)
65
+ arg_value = opts[:extra_args][idx]
66
+ idx += 1
67
+ if arg_value.start_with?("--build-arg")
68
+ if !arg_value.include?("=")
69
+ arg_value = opts[:extra_args][idx]
70
+ idx += 1
71
+ end
72
+ key, val = arg_value.to_s.split("=", 2).to_s.split("=")
73
+ extra_args[key] = val
74
+ end
75
+ end
76
+ end
77
+ when Hash
78
+ services[name]["build"]["args"] = opts[:extra_args]
79
+ end
80
+ end
81
+ rescue => error
82
+ @logger.error("Failed to apply build using `#{dir}` directory: #{error.class} - #{error}")
83
+ update_composition do |composition|
84
+ composition["services"].delete(name)
85
+ end
86
+ raise
87
+ end
88
+ end
89
+
90
+ def create(params, **opts, &block)
91
+ # NOTE: Use the direct machine name as we don't
92
+ # need to worry about uniqueness with compose
93
+ name = machine.name.to_s
94
+ image = params.fetch(:image)
95
+ links = Array(params.fetch(:links, [])).map do |link|
96
+ case link
97
+ when Array
98
+ link
99
+ else
100
+ link.to_s.split(":")
101
+ end
102
+ end
103
+ ports = Array(params[:ports])
104
+ volumes = Array(params[:volumes]).map do |v|
105
+ v = v.to_s
106
+ host, guest = v.split(":", 2)
107
+ if v.include?(":") && (Vagrant::Util::Platform.windows? || Vagrant::Util::Platform.wsl?)
108
+ host = Vagrant::Util::Platform.windows_path(host)
109
+ # NOTE: Podman does not support UNC style paths (which also
110
+ # means that there's no long path support). Hopefully this
111
+ # will be fixed someday and the gsub below can be removed.
112
+ host.gsub!(/^[^A-Za-z]+/, "")
113
+ end
114
+ # if host path is a volume key, don't expand it.
115
+ # if both exist (a path and a key) show warning and move on
116
+ # otherwise assume it's a realative path and expand the host path
117
+ compose_config = get_composition
118
+ if compose_config["volumes"] && compose_config["volumes"].keys.include?(host)
119
+ if File.directory?(@machine.env.cwd.join(host).to_s)
120
+ @machine.env.ui.warn(I18n.t("podman_provider.volume_path_not_expanded",
121
+ host: host))
122
+ end
123
+ else
124
+ @logger.debug("Path expanding #{host} to current Vagrant working dir instead of podman-compose config file directory")
125
+ host = @machine.env.cwd.join(host).to_s
126
+ end
127
+ "#{host}:#{guest}"
128
+ end
129
+ cmd = Array(params.fetch(:cmd))
130
+ env = Hash[*params.fetch(:env).flatten.map(&:to_s)]
131
+ expose = Array(params[:expose])
132
+ @logger.debug("Creating container `#{name}`")
133
+ begin
134
+ update_args = [:apply]
135
+ update_args.push(:detach) if params[:detach]
136
+ update_args << block
137
+ update_composition(*update_args) do |composition|
138
+ services = composition["services"] ||= {}
139
+ services[name] ||= {}
140
+ if params[:extra_args].is_a?(Hash)
141
+ services[name].merge!(
142
+ Hash[
143
+ params[:extra_args].map{ |k, v|
144
+ [k.to_s, v]
145
+ }
146
+ ]
147
+ )
148
+ end
149
+ services[name].merge!(
150
+ "environment" => env,
151
+ "expose" => expose,
152
+ "ports" => ports,
153
+ "volumes" => volumes,
154
+ "links" => links,
155
+ "command" => cmd
156
+ )
157
+ services[name]["image"] = image if image
158
+ services[name]["hostname"] = params[:hostname] if params[:hostname]
159
+ services[name]["privileged"] = true if params[:privileged]
160
+ services[name]["pty"] = true if params[:pty]
161
+ end
162
+ rescue => error
163
+ @logger.error("Failed to create container `#{name}`: #{error.class} - #{error}")
164
+ update_composition do |composition|
165
+ composition["services"].delete(name)
166
+ end
167
+ raise
168
+ end
169
+ get_container_id(name)
170
+ end
171
+
172
+ def rm(cid)
173
+ if created?(cid)
174
+ destroy = false
175
+ synchronized do
176
+ compose_execute("rm", "-f", machine.name.to_s)
177
+ update_composition do |composition|
178
+ if composition["services"] && composition["services"].key?(machine.name.to_s)
179
+ @logger.info("Removing container `#{machine.name}`")
180
+ if composition["services"].size > 1
181
+ composition["services"].delete(machine.name.to_s)
182
+ else
183
+ destroy = true
184
+ end
185
+ end
186
+ end
187
+ if destroy
188
+ @logger.info("No containers remain. Destroying full environment.")
189
+ compose_execute("down", "--volumes", "--rmi", "local")
190
+ @logger.info("Deleting composition path `#{composition_path}`")
191
+ composition_path.delete
192
+ end
193
+ end
194
+ end
195
+ end
196
+
197
+ def rmi(*_)
198
+ true
199
+ end
200
+
201
+ def created?(cid)
202
+ result = super
203
+ if !result
204
+ composition = get_composition
205
+ if composition["services"] && composition["services"].has_key?(machine.name.to_s)
206
+ result = true
207
+ end
208
+ end
209
+ result
210
+ end
211
+
212
+ private
213
+
214
+ # Lookup the ID for the container with the given name
215
+ #
216
+ # @param [String] name Name of container
217
+ # @return [String] Container ID
218
+ def get_container_id(name)
219
+ compose_execute("ps", "-q", name).chomp
220
+ end
221
+
222
+ # Execute a `podman-compose` command
223
+ def compose_execute(*cmd, **opts, &block)
224
+ synchronized do
225
+ execute("podman-compose", "-f", composition_path.to_s,
226
+ "-p", machine.env.cwd.basename.to_s, *cmd, **opts, &block)
227
+ end
228
+ end
229
+
230
+ # Apply any changes made to the composition
231
+ def apply_composition!(*args)
232
+ block = args.detect{|arg| arg.is_a?(Proc) }
233
+ execute_args = ["up", "--remove-orphans"]
234
+ if args.include?(:detach)
235
+ execute_args << "-d"
236
+ end
237
+ machine.env.lock("compose", retry: true) do
238
+ if block
239
+ compose_execute(*execute_args, &block)
240
+ else
241
+ compose_execute(*execute_args)
242
+ end
243
+ end
244
+ end
245
+
246
+ # Update the composition and apply changes if requested
247
+ #
248
+ # @param [Boolean] apply Apply composition changes
249
+ def update_composition(*args)
250
+ synchronized do
251
+ machine.env.lock("compose", retry: true) do
252
+ composition = get_composition
253
+ result = yield composition
254
+ write_composition(composition)
255
+ if args.include?(:apply) || (args.include?(:conditional) && result)
256
+ apply_composition!(*args)
257
+ end
258
+ end
259
+ end
260
+ end
261
+
262
+ # @return [Hash] current composition contents
263
+ def get_composition
264
+ composition = {"version" => COMPOSE_VERSION.dup}
265
+ if composition_path.exist?
266
+ composition = Vagrant::Util::DeepMerge.deep_merge(composition, YAML.load(composition_path.read))
267
+ end
268
+ composition = Vagrant::Util::DeepMerge.deep_merge(composition, machine.provider_config.compose_configuration.dup)
269
+ @logger.debug("Fetched composition with provider configuration applied: #{composition}")
270
+ composition
271
+ end
272
+
273
+ # Save the composition
274
+ #
275
+ # @param [Hash] composition New composition
276
+ def write_composition(composition)
277
+ @logger.debug("Saving composition to `#{composition_path}`: #{composition}")
278
+ tmp_file = Tempfile.new("vagrant-podman-compose")
279
+ tmp_file.write(composition.to_yaml)
280
+ tmp_file.close
281
+ synchronized do
282
+ FileUtils.mv(tmp_file.path, composition_path.to_s)
283
+ end
284
+ end
285
+
286
+ # @return [Pathname] path to the podman-compose.yml file
287
+ def composition_path
288
+ data_directory.join("podman-compose.yml")
289
+ end
290
+
291
+ def synchronized
292
+ if !@compose_lock.owned?
293
+ timeout = LOCK_TIMEOUT.to_f
294
+ until @compose_lock.owned?
295
+ if @compose_lock.try_lock
296
+ if timeout > 0
297
+ timeout -= sleep(1)
298
+ else
299
+ raise Errors::ComposeLockTimeoutError
300
+ end
301
+ end
302
+ end
303
+ got_lock = true
304
+ end
305
+ begin
306
+ result = yield
307
+ ensure
308
+ @compose_lock.unlock if got_lock
309
+ end
310
+ result
311
+ end
312
+ end
313
+ end
314
+ end
315
+ end