docker-api 2.3.0 → 2.5.0

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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 8bcdc6793c2627d187d2844a679965abf7903c58b89dc26de88f411feb8d0fd6
4
- data.tar.gz: 3cc8a8aadeee74ab3ba28b25d147b795dae8aff22e53923a2911f36f7bbc3d5d
3
+ metadata.gz: 8bd06b9a57651660c10d8e279df9ee588c3eaed258e4ced980f4090a5728a0b3
4
+ data.tar.gz: 07f5419a9f3e02af5db30e41fa91fd12afb01cfed84bae20d19fda55de27819b
5
5
  SHA512:
6
- metadata.gz: 6fb00d23f25120f8341002958a9abedbff05bc27e11d5992308063569a640b9e51c3a492a0fdd00dae15e8f6ddbb62bc8c706b8fbe25d6a7c79719178194b328
7
- data.tar.gz: f8fee2e81864164601e6cccd2e3cd3235df383217a2fd0c362a06a9da8448f0ef452306fa6a2d370c02ea95dbe9b9db1669d1ab39cdd4d1b880265fd0e57752d
6
+ metadata.gz: e778d8fbb81217c9ad405e0879b8e5754e17e79f6bdb072e717a2ac2e7ab044e3a62a8aab0c3d58fc1094a578ef4480451b478a4161e611a1ce10bbb980564cc
7
+ data.tar.gz: aa0670fd34bd8a2ee4aa16422af84a462451bde78807fe9d9ce2401bc4006f432e79b06d38a86aa0d2ee6d2c2a3cc2ca35b2c67e513565b91bd1ee351df75466
data/README.md CHANGED
@@ -1,8 +1,8 @@
1
1
  docker-api
2
2
  ==========
3
- [![Gem Version](https://badge.fury.io/rb/docker-api.svg)](https://badge.fury.io/rb/docker-api) [![Code Climate](https://codeclimate.com/github/upserve/docker-api.svg)](https://codeclimate.com/github/upserve/docker-api)
3
+ [![Gem Version](https://badge.fury.io/rb/docker-api.svg)](https://badge.fury.io/rb/docker-api)
4
4
 
5
- This gem provides an object-oriented interface to the [Docker Engine API](https://docs.docker.com/develop/sdk/). Every method listed there is implemented. At the time of this writing, docker-api is meant to interface with Docker version 1.4.*
5
+ This gem provides an object-oriented interface to the [Docker Engine API](https://docs.docker.com/develop/sdk/). Every method listed there is implemented. The gem does not pin an API version, so requests use the daemon's default. It is tested against Docker 28 and 29.
6
6
 
7
7
  If you're interested in using Docker to package your apps, we recommend the [dockly](https://github.com/upserve/dockly) gem. Dockly provides a simple DSL for describing Docker containers that install as Debian packages and are controlled by upstart scripts.
8
8
 
@@ -41,7 +41,7 @@ At this time, basic `podman` support has been added via the podman docker-compat
41
41
  Follow the [installation instructions](https://docs.docker.com/install/), and then run:
42
42
 
43
43
  ```shell
44
- $ sudo docker -d
44
+ $ sudo dockerd
45
45
  ```
46
46
 
47
47
  This will daemonize Docker so that it can be used for the remote API calls.
@@ -138,15 +138,15 @@ Docker.authenticate!('username' => 'docker-fan-boi', 'password' => 'i<3docker',
138
138
 
139
139
  ## Images
140
140
 
141
- Just about every method here has a one-to-one mapping with the [Images](https://docs.docker.com/engine/reference/api/docker_remote_api_v1.14/#2-2-images) section of the API. If an API call accepts query parameters, these can be passed as an Hash to it's corresponding method. Also, note that `Docker::Image.new` is a private method, so you must use `.create`, `.build`, `.build_from_dir`, `build_from_tar`, or `.import` to make an instance.
141
+ Just about every method here has a one-to-one mapping with the [Images](https://docs.docker.com/reference/api/engine/) section of the API. If an API call accepts query parameters, these can be passed as an Hash to it's corresponding method. Also, note that `Docker::Image.new` is a private method, so you must use `.create`, `.build`, `.build_from_dir`, `build_from_tar`, or `.import` to make an instance.
142
142
 
143
143
  ```ruby
144
144
  require 'docker'
145
145
  # => true
146
146
 
147
147
  # Pull an Image.
148
- # docker command for reference: docker pull ubuntu:14.04
149
- image = Docker::Image.create('fromImage' => 'ubuntu:14.04')
148
+ # docker command for reference: docker pull ubuntu:24.04
149
+ image = Docker::Image.create('fromImage' => 'ubuntu:24.04')
150
150
  # => Docker::Image { :id => ae7ffbcd1, :connection => Docker::Connection { :url => tcp://localhost, :options => {:port=>2375} } }
151
151
 
152
152
  # Insert a local file into an Image.
@@ -194,12 +194,12 @@ image.run('ls -l')
194
194
  # Remove the Image from the server.
195
195
  # docker command for reference: docker rmi -f <IMAGE.ID>
196
196
  image.remove(:force => true)
197
- # => true
197
+ # => "[{\"Untagged\":\"base2:latest\"}]\n"
198
198
 
199
199
  # Export a single Docker Image to a file
200
200
  # docker command for reference: docker save <IMAGE.ID> my_export.tar
201
201
  image.save('my_export.tar')
202
- # => Docker::Image { :id => 66b712aef, :connection => Docker::Connection { :url => tcp://localhost, :options => {:port=>2375} } }
202
+ # => nil
203
203
 
204
204
  # Return the raw image binary data
205
205
  image.save
@@ -225,7 +225,7 @@ File.open('my-export.tar') do |file|
225
225
  end
226
226
 
227
227
  # Create an Image from a Dockerfile as a String.
228
- Docker::Image.build("from base\nrun touch /test")
228
+ Docker::Image.build("FROM debian:stable\nRUN touch /test")
229
229
  # => Docker::Image { :id => b750fe79269d2ec9a3c593ef05b4332b1d1a02a62b4accb2c21d589ff2f5f2dc, :connection => Docker::Connection { :url => tcp://localhost, :options => {:port=>2375} } }
230
230
 
231
231
  # Create an Image from a Dockerfile.
@@ -256,7 +256,7 @@ Docker::Image.all
256
256
  # => [Docker::Image { :id => b750fe79269d2ec9a3c593ef05b4332b1d1a02a62b4accb2c21d589ff2f5f2dc, :connection => Docker::Connection { :url => tcp://localhost, :options => {:port=>2375} } }, Docker::Image { :id => 8dbd9e392a964056420e5d58ca5cc376ef18e2de93b5cc90e868a1bbc8318c1c, :connection => Docker::Connection { :url => tcp://localhost, :options => {:port=>2375} } }]
257
257
 
258
258
  # Get Image from the server, with id
259
- # docker command for reference: docker images <IMAGE.ID>
259
+ # docker command for reference: docker inspect <IMAGE.ID>
260
260
  Docker::Image.get('df4f1bdecf40')
261
261
  # => Docker::Image { :id => eb693ec80, :connection => Docker::Connection { :url => tcp://localhost, :options => {:port=>2375} } }
262
262
 
@@ -298,13 +298,13 @@ Docker::Image.search('term' => 'sshd')
298
298
 
299
299
  ## Containers
300
300
 
301
- Much like the Images, this object also has a one-to-one mapping with the [Containers](https://docs.docker.com/engine/reference/api/docker_remote_api_v1.14/#2-1-containers) section of the API. Also like Images, `.new` is a private method, so you must use `.create` to make an instance.
301
+ Much like the Images, this object also has a one-to-one mapping with the [Containers](https://docs.docker.com/reference/api/engine/) section of the API. Also like Images, `.new` is a private method, so you must use `.create` to make an instance.
302
302
 
303
303
  ```ruby
304
304
  require 'docker'
305
305
 
306
306
  # Create a Container.
307
- container = Docker::Container.create('Cmd' => ['ls'], 'Image' => 'base')
307
+ container = Docker::Container.create('Cmd' => ['ls'], 'Image' => 'debian:stable')
308
308
  # => Docker::Container { :id => 492510dd38e4, :connection => Docker::Connection { :url => tcp://localhost, :options => {:port=>2375} } }
309
309
 
310
310
  # Get more information about the Container.
@@ -423,15 +423,15 @@ container.changes
423
423
  # => [{'Path'=>'/dev', 'Kind'=>0}, {'Path'=>'/dev/kmsg', 'Kind'=>1}]
424
424
 
425
425
  # Copy files/directories from the Container. Note that these are exported as tars.
426
- container.copy('/etc/hosts') { |chunk| puts chunk }
427
-
428
- hosts0000644000000000000000000000023412100405636007023 0ustar
429
- 127.0.0.1 localhost
430
- ::1 localhost ip6-localhost ip6-loopback
431
- fe00::0 ip6-localnet
432
- ff00::0 ip6-mcastprefix
433
- ff02::1 ip6-allnodes
434
- ff02::2 ip6-allrouters
426
+ container.archive_out('/etc/hosts') { |chunk| puts chunk }
427
+
428
+ # hosts0000644000000000000000000000023412100405636007023 0ustar
429
+ # 127.0.0.1 localhost
430
+ # ::1 localhost ip6-localhost ip6-loopback
431
+ # fe00::0 ip6-localnet
432
+ # ff00::0 ip6-mcastprefix
433
+ # ff02::1 ip6-allnodes
434
+ # ff02::2 ip6-allrouters
435
435
  # => Docker::Container { :id => a1759f3e2873, :connection => Docker::Connection { :url => tcp://localhost, :options => {:port=>2375} } }
436
436
 
437
437
  # Wait for the current command to finish executing. If an argument is given,
@@ -445,13 +445,13 @@ container.attach(:stream => true, :stdin => nil, :stdout => true, :stderr => tru
445
445
  # => [["bin\nboot\ndev\netc\nhome\nlib\nlib64\nmedia\nmnt\nopt\nproc\nroot\nrun\nsbin\nselinux\nsrv\nsys\ntmp\nusr\nvar", []]
446
446
 
447
447
  # If you wish to stream the attach method, a block may be supplied.
448
- container = Docker::Container.create('Image' => 'base', 'Cmd' => ['find / -name *'])
448
+ container = Docker::Container.create('Image' => 'debian:stable', 'Cmd' => ['find / -name *'])
449
449
  container.tap(&:start).attach { |stream, chunk| puts "#{stream}: #{chunk}" }
450
- stderr: 2013/10/30 17:16:24 Unable to locate find / -name *
450
+ # stderr: 2013/10/30 17:16:24 Unable to locate find / -name *
451
451
  # => [[], ["2013/10/30 17:16:24 Unable to locate find / -name *\n"]]
452
452
 
453
453
  # If you want to attach to stdin of the container, supply an IO-like object:
454
- container = Docker::Container.create('Image' => 'base', 'Cmd' => ['cat'], 'OpenStdin' => true, 'StdinOnce' => true)
454
+ container = Docker::Container.create('Image' => 'debian:stable', 'Cmd' => ['cat'], 'OpenStdin' => true, 'StdinOnce' => true)
455
455
  container.tap(&:start).attach(stdin: StringIO.new("foo\nbar\n"))
456
456
  # => [["foo\nbar\n"], []]
457
457
 
@@ -470,16 +470,16 @@ container.logs(stdout: true)
470
470
 
471
471
  # Streaming logs from non-TTY container removing multiplex prefix with a block printing out each line (block not possible with Container#logs)
472
472
  container.streaming_logs(stdout: true) { |stream, chunk| puts "#{stream}: #{chunk}" }
473
- stdout: 1
474
- stdout: 2
475
- stdout: 3
476
- stdout: 4
477
- stdout: 5
478
- stdout: 6
479
- stdout: 7
480
- stdout: 8
481
- stdout: 9
482
- stdout: 10
473
+ # stdout: 1
474
+ # stdout: 2
475
+ # stdout: 3
476
+ # stdout: 4
477
+ # stdout: 5
478
+ # stdout: 6
479
+ # stdout: 7
480
+ # stdout: 8
481
+ # stdout: 9
482
+ # stdout: 10
483
483
  # => "1\n\n2\n\n3\n\n4\n\n5\n\n6\n\n7\n\n8\n\n9\n\n10\n"
484
484
 
485
485
  # If the container has TTY enabled, set `tty => true` to get the raw stream:
@@ -499,7 +499,7 @@ container.commit
499
499
  # Commit the Container and run a new command. The second argument is the number
500
500
  # of seconds the Container should wait before stopping its current command.
501
501
  container.run('pwd', 10)
502
- # => Docker::Image { :id => 4427be4199ac, :connection => Docker::Connection { :url => tcp://localhost, :options => {:port=>2375} } }
502
+ # => Docker::Container { :id => 4427be4199ac, :connection => Docker::Connection { :url => tcp://localhost, :options => {:port=>2375} } }
503
503
 
504
504
  # Run an Exec instance inside the container and capture its output and exit status
505
505
  container.exec(['date'])
@@ -511,12 +511,13 @@ container.exec(['./my_service'], detach: true)
511
511
 
512
512
  # Parse the output of an Exec instance
513
513
  container.exec(['find', '/', '-name *']) { |stream, chunk| puts "#{stream}: #{chunk}" }
514
- stderr: 2013/10/30 17:16:24 Unable to locate find / -name *
514
+ # stderr: 2013/10/30 17:16:24 Unable to locate find / -name *
515
515
  # => [[], ["2013/10/30 17:16:24 Unable to locate find / -name *\n"], 1]
516
516
 
517
- # Run an Exec instance by grab only the STDOUT output
518
- container.exec(['date'], stderr: false)
519
- # => [["Wed Nov 26 11:10:30 CST 2014\n"], [], 0]
517
+ # The return value is [stdout_lines, stderr_lines, exit_code], so destructure
518
+ # it if you only care about one stream.
519
+ stdout, _stderr, _exit_code = container.exec(['date'])
520
+ # stdout => ["Wed Nov 26 11:10:30 CST 2014\n"]
520
521
 
521
522
  # Pass input to an Exec instance command via Stdin
522
523
  container.exec(['cat'], stdin: StringIO.new("foo\nbar\n"))
@@ -529,7 +530,7 @@ container.exec(command, tty: true)
529
530
 
530
531
  # Wait for the current command to finish executing. If an argument is given,
531
532
  # will timeout after that number of seconds. The default is one minute.
532
- command = ["bash", "-c", "if [ -t 1 ]; then echo -n \"Set max seconds for exec!!\"; fi"]
533
+ command = ["bash", "-c", "if [ -t 1 ]; then echo -n \"Set max seconds for exec!\"; fi"]
533
534
  container.exec(command, wait: 120)
534
535
  # => [["Set max seconds for exec!"], [], 0]
535
536
 
@@ -538,7 +539,7 @@ container.delete(:force => true)
538
539
  # => nil
539
540
 
540
541
  # Update the container.
541
- container.update("CpuShares" => 50000")
542
+ container.update("CpuShares" => 50000)
542
543
 
543
544
  # Request a Container by ID or name.
544
545
  Docker::Container.get('500f53b25e6e')
@@ -578,17 +579,37 @@ require 'docker'
578
579
 
579
580
  # Action on a stream of events as they come in
580
581
  Docker::Event.stream { |event| puts event; break }
581
- Docker::Event { :status => create, :id => aeb8b55726df63bdd69d41e1b2650131d7ce32ca0d2fa5cbc75f24d0df34c7b0, :from => base:latest, :time => 1416958554 }
582
+ # Docker::Event { :status => create, :id => aeb8b55726df63bdd69d41e1b2650131d7ce32ca0d2fa5cbc75f24d0df34c7b0, :from => debian:stable, :time => 1416958554 }
582
583
  # => nil
583
584
 
584
585
  # Action on all events after a given time (will execute the block for all events up till the current time, and wait to execute on any new events after)
585
586
  Docker::Event.since(1416958763) { |event| puts event; puts Time.now.to_i; break }
586
- Docker::Event { :status => die, :id => 663005cdeb56f50177c395a817dbc8bdcfbdfbdaef329043b409ecb97fb68d7e, :from => base:latest, :time => 1416958764 }
587
- 1416959041
587
+ # Docker::Event { :status => die, :id => 663005cdeb56f50177c395a817dbc8bdcfbdfbdaef329043b409ecb97fb68d7e, :from => debian:stable, :time => 1416958764 }
588
+ # 1416959041
588
589
  # => nil
589
590
  ```
590
591
 
591
- These methods are prone to read timeouts. `Docker.options[:read_timeout]` will need to be made higher than 60 seconds if expecting a long time between events.
592
+ These methods are prone to read timeouts. Note that the first argument to
593
+ `Docker::Event.stream` and `Docker::Event.since` is a hash of **query parameters**
594
+ for the `/events` endpoint, not Excon options. Passing `read_timeout` there sends
595
+ it to Docker as a query parameter and has no effect on the timeout. Set it on the
596
+ connection instead:
597
+
598
+ ```ruby
599
+ # Disable timeouts completely, for this call only
600
+ conn = Docker::Connection.new(Docker.url, Docker.options.merge(read_timeout: 0))
601
+ Docker::Event.stream({}, conn) { |event| puts event }
602
+
603
+ # Timeout if no events are received in 24h
604
+ conn = Docker::Connection.new(Docker.url, Docker.options.merge(read_timeout: 60 * 60 * 24))
605
+ Docker::Event.stream({}, conn) { |event| puts event }
606
+
607
+ # Or set a high timeout globally. Be warned that you probably don't want this
608
+ # for other methods. This has to happen before the default connection is first
609
+ # used, since `Docker.connection` is memoized.
610
+ Docker.options[:read_timeout] = 60 * 60 * 24
611
+ Docker::Event.stream { |event| puts event }
612
+ ```
592
613
 
593
614
  ## Connecting to Multiple Servers
594
615
 
@@ -624,8 +645,8 @@ end
624
645
 
625
646
  ## Not supported (yet)
626
647
 
627
- * Generating a tarball of images and metadata for a repository specified by a name: https://docs.docker.com/engine/reference/api/docker_remote_api_v1.14/#get-a-tarball-containing-all-images-and-tags-in-a-repository
628
- * Load a tarball generated from docker that contains all the images and metadata of a repository: https://docs.docker.com/engine/reference/api/docker_remote_api_v1.14/#load-a-tarball-with-a-set-of-images-and-tags-into-docker
648
+ * Generating a tarball of images and metadata for a repository specified by a name
649
+ * Load a tarball generated from docker that contains all the images and metadata of a repository
629
650
 
630
651
  License
631
652
  -----
data/lib/docker/base.rb CHANGED
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  # This class is a base class for Docker Container and Image.
2
4
  # It is implementing accessor methods for the models attributes.
3
5
  module Docker::Base
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  # This class represents a Connection to a Docker server. The Connection is
2
4
  # immutable in that once the url and options is set they cannot be changed.
3
5
  class Docker::Connection
@@ -127,7 +129,12 @@ class Docker::Connection
127
129
  end
128
130
 
129
131
  def rootless?
130
- @rootless ||= (info['Rootless'] == true)
132
+ # `||=` cannot cache a false result, so this has to test for the ivar
133
+ # itself. #info is not memoized, so re-evaluating means another /info
134
+ # request on every call against a non-rootless daemon.
135
+ return @rootless if defined?(@rootless)
136
+
137
+ @rootless = (info['Rootless'] == true)
131
138
  end
132
139
 
133
140
  def version
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  # This class represents a Docker Container. It's important to note that nothing
2
4
  # is cached so that the information is always up to date.
3
5
  class Docker::Container
@@ -58,8 +60,11 @@ class Docker::Container
58
60
  detach = options.delete(:detach) || false
59
61
  user = options.delete(:user)
60
62
  stdin = options.delete(:stdin)
61
- stdout = options.delete(:stdout) || !detach
62
- stderr = options.delete(:stderr) || !detach
63
+ # `options.delete(:stdout) || !detach` turns an explicit false back into
64
+ # true, so these have to be tested for presence rather than truthiness.
65
+ # They default to the inverse of :detach when the caller says nothing.
66
+ stdout = options.key?(:stdout) ? options.delete(:stdout) : !detach
67
+ stderr = options.key?(:stderr) ? options.delete(:stderr) : !detach
63
68
  wait = options.delete(:wait)
64
69
 
65
70
  opts = {
@@ -116,6 +121,16 @@ class Docker::Container
116
121
  opts[:stdin] = true
117
122
  excon_params[:hijack_block] = Docker::Util.hijack_for(stdin, block,
118
123
  msgs, tty)
124
+ # Hijacking means asking the daemon to upgrade the connection. Without
125
+ # these headers Docker answers 200 and leaves the payload on Excon's
126
+ # buffered socket, which the raw hijacked read never looks at, so the
127
+ # attach blocks forever. With them the daemon replies 101 and hands the
128
+ # socket over, so 101 has to be an accepted status.
129
+ excon_params[:headers] = {
130
+ 'Connection' => 'Upgrade',
131
+ 'Upgrade' => 'tcp'
132
+ }.merge(excon_params[:headers] || {})
133
+ excon_params[:expects] ||= (200..204).to_a + [101, 301, 304]
119
134
  else
120
135
  excon_params[:response_block] = Docker::Util.attach_for(block, msgs, tty)
121
136
  end
@@ -135,7 +150,7 @@ class Docker::Container
135
150
  # Based on the link, the config passed as run, needs to be passed as the
136
151
  # body of the post so capture it, remove from the options, and pass it via
137
152
  # the post body
138
- config = MultiJson.dump(options.delete('run'))
153
+ config = JSON.generate(options.delete('run'))
139
154
  hash = Docker::Util.parse_json(
140
155
  connection.post('/commit', options, body: config)
141
156
  )
@@ -185,7 +200,7 @@ class Docker::Container
185
200
  end
186
201
 
187
202
  def update(opts)
188
- connection.post(path_for(:update), {}, body: MultiJson.dump(opts))
203
+ connection.post(path_for(:update), {}, body: JSON.generate(opts))
189
204
  end
190
205
 
191
206
  def streaming_logs(opts = {}, &block)
@@ -199,7 +214,7 @@ class Docker::Container
199
214
  end
200
215
 
201
216
  def start!(opts = {})
202
- connection.post(path_for(:start), {}, body: MultiJson.dump(opts))
217
+ connection.post(path_for(:start), {}, body: JSON.generate(opts))
203
218
  self
204
219
  end
205
220
 
@@ -225,7 +240,7 @@ class Docker::Container
225
240
  timeout = opts.delete('timeout')
226
241
  query = {}
227
242
  request_options = {
228
- :body => MultiJson.dump(opts)
243
+ :body => JSON.generate(opts)
229
244
  }
230
245
  if timeout
231
246
  query['t'] = timeout
@@ -322,11 +337,16 @@ class Docker::Container
322
337
  archive_in_stream("/", overwrite: true) { output_io.read }
323
338
  end
324
339
 
340
+ # Keys that /containers/create takes as query parameters rather than in the
341
+ # request body. The daemon silently ignores them if they are sent in the
342
+ # body, so a misplaced key means the option has no effect at all.
343
+ CREATE_QUERY_KEYS = %w[name platform].freeze
344
+
325
345
  # Create a new Container.
326
346
  def self.create(opts = {}, conn = Docker.connection)
327
- query = opts.select {|key| ['name', :name].include?(key) }
328
- clean_opts = opts.reject {|key| ['name', :name].include?(key) }
329
- resp = conn.post('/containers/create', query, :body => MultiJson.dump(clean_opts))
347
+ query = opts.select { |key| CREATE_QUERY_KEYS.include?(key.to_s) }
348
+ clean_opts = opts.reject { |key| CREATE_QUERY_KEYS.include?(key.to_s) }
349
+ resp = conn.post('/containers/create', query, :body => JSON.generate(clean_opts))
330
350
  hash = Docker::Util.parse_json(resp) || {}
331
351
  new(conn, hash)
332
352
  end
data/lib/docker/error.rb CHANGED
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  # This module holds the Errors for the gem.
2
4
  module Docker::Error
3
5
 
data/lib/docker/event.rb CHANGED
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  # This class represents a Docker Event.
2
4
  class Docker::Event
3
5
  include Docker::Error
data/lib/docker/exec.rb CHANGED
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  # This class represents a Docker Exec Instance.
2
4
  class Docker::Exec
3
5
  include Docker::Base
@@ -27,7 +29,7 @@ class Docker::Exec
27
29
  end
28
30
 
29
31
  resp = conn.post("/containers/#{container}/exec", {},
30
- body: MultiJson.dump(options))
32
+ body: JSON.generate(options))
31
33
  hash = Docker::Util.parse_json(resp) || {}
32
34
  new(conn, hash)
33
35
  end
@@ -58,7 +60,7 @@ class Docker::Exec
58
60
  read_timeout = options[:wait]
59
61
 
60
62
  # Create API Request Body
61
- body = MultiJson.dump(
63
+ body = JSON.generate(
62
64
  'Tty' => tty,
63
65
  'Detach' => detached
64
66
  )
data/lib/docker/image.rb CHANGED
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  # This class represents a Docker Image.
2
4
  class Docker::Image
3
5
  include Docker::Base
@@ -28,7 +30,7 @@ class Docker::Image
28
30
  repo, tag = Docker::Util.parse_repo_tag(repo_tag)
29
31
  raise ArgumentError, "Image does not have a name to push." if repo.nil?
30
32
 
31
- body = ""
33
+ body = +""
32
34
  credentials = creds || Docker.creds || {}
33
35
  headers = Docker::Util.build_auth_header(credentials)
34
36
  opts = {:tag => tag}.merge(options)
@@ -117,9 +119,9 @@ class Docker::Image
117
119
 
118
120
  # Create a new Image.
119
121
  def create(opts = {}, creds = nil, conn = Docker.connection, &block)
120
- credentials = creds.nil? ? Docker.creds : MultiJson.dump(creds)
122
+ credentials = creds.nil? ? Docker.creds : JSON.generate(creds)
121
123
  headers = credentials && Docker::Util.build_auth_header(credentials) || {}
122
- body = ''
124
+ body = +''
123
125
  conn.post(
124
126
  '/images/create',
125
127
  opts,
@@ -168,7 +170,7 @@ class Docker::Image
168
170
  end
169
171
  nil
170
172
  else
171
- string = ''
173
+ string = +''
172
174
  save_stream(names, {}, conn, &response_block_for_save(string))
173
175
  string
174
176
  end
@@ -196,7 +198,7 @@ class Docker::Image
196
198
  def load(tar, opts = {}, conn = Docker.connection, creds = nil, &block)
197
199
  headers = build_headers(creds)
198
200
  io = tar.is_a?(String) ? File.open(tar, 'rb') : tar
199
- body = ""
201
+ body = +""
200
202
  conn.post(
201
203
  '/images/load',
202
204
  opts,
@@ -267,7 +269,7 @@ class Docker::Image
267
269
 
268
270
  # Given a Dockerfile as a string, builds an Image.
269
271
  def build(commands, opts = {}, connection = Docker.connection, &block)
270
- body = ""
272
+ body = +""
271
273
  connection.post(
272
274
  '/build', opts,
273
275
  :body => Docker::Util.create_tar('Dockerfile' => commands),
@@ -288,7 +290,7 @@ class Docker::Image
288
290
  headers = build_headers(creds)
289
291
 
290
292
  # The response_block passed to Excon will build up this body variable.
291
- body = ""
293
+ body = +""
292
294
  connection.post(
293
295
  '/build', opts,
294
296
  :headers => headers,
@@ -340,7 +342,7 @@ class Docker::Image
340
342
  # Convience method to get the Dockerfile for a file hash and a path to
341
343
  # output to.
342
344
  def dockerfile_for(file_hash, output_path)
343
- dockerfile = "from #{self.id}\n"
345
+ dockerfile = +"from #{self.id}\n"
344
346
 
345
347
  file_hash.keys.each do |basename|
346
348
  dockerfile << "add #{basename} #{output_path}\n"
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  # This class represents all the messages either received by chunks from attach
2
4
  class Docker::Messages
3
5
 
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  # This class represents a messages stack
2
4
  class Docker::MessagesStack
3
5
 
@@ -1,9 +1,11 @@
1
+ # frozen_string_literal: true
2
+
1
3
  # This class represents a Docker Network.
2
4
  class Docker::Network
3
5
  include Docker::Base
4
6
 
5
7
  def connect(container, opts = {}, body_opts = {})
6
- body = MultiJson.dump({ container: container }.merge(body_opts))
8
+ body = JSON.generate({ container: container }.merge(body_opts))
7
9
  Docker::Util.parse_json(
8
10
  connection.post(path_for('connect'), opts, body: body)
9
11
  )
@@ -11,7 +13,7 @@ class Docker::Network
11
13
  end
12
14
 
13
15
  def disconnect(container, opts = {})
14
- body = MultiJson.dump(container: container)
16
+ body = JSON.generate(container: container)
15
17
  Docker::Util.parse_json(
16
18
  connection.post(path_for('disconnect'), opts, body: body)
17
19
  )
@@ -41,7 +43,7 @@ class Docker::Network
41
43
 
42
44
  class << self
43
45
  def create(name, opts = {}, conn = Docker.connection)
44
- default_opts = MultiJson.dump({
46
+ default_opts = JSON.generate({
45
47
  'Name' => name,
46
48
  'CheckDuplicate' => true
47
49
  }.merge(opts))
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  # This class allows image-based tasks to be created.
2
4
  class Docker::ImageTask < Rake::Task
3
5
  def self.scope_name(_scope, task_name)
data/lib/docker/util.rb CHANGED
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  require 'set'
2
4
 
3
5
  # This module holds shared logic that doesn't really belong anywhere else in the
@@ -104,8 +106,8 @@ module Docker::Util
104
106
  end
105
107
 
106
108
  def parse_json(body)
107
- MultiJson.load(body) unless body.nil? || body.empty? || (body == 'null')
108
- rescue MultiJson::ParseError => ex
109
+ JSON.parse(body) unless body.nil? || body.empty? || (body == 'null')
110
+ rescue JSON::ParserError => ex
109
111
  raise UnexpectedResponseError, ex.message
110
112
  end
111
113
 
@@ -171,14 +173,25 @@ module Docker::Util
171
173
  end
172
174
 
173
175
  def create_relative_dir_tar(directory, output)
176
+ root = File.realpath(directory)
177
+
174
178
  Gem::Package::TarWriter.new(output) do |tar|
175
179
  files = docker_context(directory)
176
180
 
177
181
  files.each do |prefixed_file_name|
178
- stat = File.stat(prefixed_file_name)
182
+ stat = File.lstat(prefixed_file_name)
183
+ unprefixed_file_name = prefixed_file_name[directory.length..-1]
184
+
185
+ if stat.symlink?
186
+ tar.add_symlink(
187
+ unprefixed_file_name, File.readlink(prefixed_file_name), stat.mode
188
+ )
189
+ next
190
+ end
179
191
  next unless stat.file?
180
192
 
181
- unprefixed_file_name = prefixed_file_name[directory.length..-1]
193
+ next unless contained?(prefixed_file_name, root)
194
+
182
195
  add_file_to_tar(
183
196
  tar, unprefixed_file_name, stat.mode, stat.size, stat.mtime
184
197
  ) do |tar_file|
@@ -188,6 +201,14 @@ module Docker::Util
188
201
  end
189
202
  end
190
203
 
204
+ # True when path resolves, with every symlink and '..' expanded, to
205
+ # something underneath root.
206
+ def contained?(path, root)
207
+ File.realpath(path).start_with?(root + File::SEPARATOR)
208
+ rescue SystemCallError
209
+ false
210
+ end
211
+
191
212
  def add_file_to_tar(tar, name, mode, size, mtime)
192
213
  tar.check_closed
193
214
 
@@ -259,7 +280,7 @@ module Docker::Util
259
280
  end
260
281
 
261
282
  def build_auth_header(credentials)
262
- credentials = MultiJson.dump(credentials) if credentials.is_a?(Hash)
283
+ credentials = JSON.generate(credentials) if credentials.is_a?(Hash)
263
284
  encoded_creds = Base64.urlsafe_encode64(credentials)
264
285
  {
265
286
  'X-Registry-Auth' => encoded_creds
@@ -268,10 +289,10 @@ module Docker::Util
268
289
 
269
290
  def build_config_header(credentials)
270
291
  if credentials.is_a?(String)
271
- credentials = MultiJson.load(credentials, symbolize_keys: true)
292
+ credentials = JSON.parse(credentials, symbolize_names: true)
272
293
  end
273
294
 
274
- header = MultiJson.dump(
295
+ header = JSON.generate(
275
296
  credentials[:serveraddress].to_s => {
276
297
  'username' => credentials[:username].to_s,
277
298
  'password' => credentials[:password].to_s,
@@ -1,4 +1,6 @@
1
+ # frozen_string_literal: true
2
+
1
3
  module Docker
2
4
  # The version of the docker-api gem.
3
- VERSION = '2.3.0'
5
+ VERSION = '2.5.0'
4
6
  end
data/lib/docker/volume.rb CHANGED
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  # class represents a Docker Volume
2
4
  class Docker::Volume
3
5
  include Docker::Base
@@ -31,7 +33,7 @@ class Docker::Volume
31
33
  # creates a volume with an arbitrary name
32
34
  def create(name, opts = {}, conn = Docker.connection)
33
35
  opts['Name'] = name
34
- resp = conn.post('/volumes/create', {}, body: MultiJson.dump(opts))
36
+ resp = conn.post('/volumes/create', {}, body: JSON.generate(opts))
35
37
  hash = Docker::Util.parse_json(resp) || {}
36
38
  new(conn, hash)
37
39
  end
data/lib/docker-api.rb CHANGED
@@ -1 +1,3 @@
1
+ # frozen_string_literal: true
2
+
1
3
  require 'docker'
data/lib/docker.rb CHANGED
@@ -1,5 +1,7 @@
1
+ # frozen_string_literal: true
2
+
1
3
  require 'cgi'
2
- require 'multi_json'
4
+ require 'json'
3
5
  require 'excon'
4
6
  require 'tempfile'
5
7
  require 'base64'
@@ -131,7 +133,7 @@ module Docker
131
133
 
132
134
  # Login to the Docker registry.
133
135
  def authenticate!(options = {}, connection = self.connection)
134
- creds = MultiJson.dump(options)
136
+ creds = JSON.generate(options)
135
137
  connection.post('/auth', {}, body: creds)
136
138
  @creds = creds
137
139
  true
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  module Excon
2
4
  module Middleware
3
5
  # Hijack is an Excon middleware which parses response headers and then
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: docker-api
3
3
  version: !ruby/object:Gem::Version
4
- version: 2.3.0
4
+ version: 2.5.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Swipely, Inc.
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2024-07-08 00:00:00.000000000 Z
11
+ date: 2026-10-27 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: excon
@@ -25,7 +25,7 @@ dependencies:
25
25
  - !ruby/object:Gem::Version
26
26
  version: 0.64.0
27
27
  - !ruby/object:Gem::Dependency
28
- name: multi_json
28
+ name: base64
29
29
  requirement: !ruby/object:Gem::Requirement
30
30
  requirements:
31
31
  - - ">="
@@ -80,20 +80,6 @@ dependencies:
80
80
  - - ">="
81
81
  - !ruby/object:Gem::Version
82
82
  version: '0'
83
- - !ruby/object:Gem::Dependency
84
- name: cane
85
- requirement: !ruby/object:Gem::Requirement
86
- requirements:
87
- - - ">="
88
- - !ruby/object:Gem::Version
89
- version: '0'
90
- type: :development
91
- prerelease: false
92
- version_requirements: !ruby/object:Gem::Requirement
93
- requirements:
94
- - - ">="
95
- - !ruby/object:Gem::Version
96
- version: '0'
97
83
  - !ruby/object:Gem::Dependency
98
84
  name: pry
99
85
  requirement: !ruby/object:Gem::Requirement
@@ -178,7 +164,10 @@ files:
178
164
  homepage: https://github.com/upserve/docker-api
179
165
  licenses:
180
166
  - MIT
181
- metadata: {}
167
+ metadata:
168
+ bug_tracker_uri: https://github.com/upserve/docker-api/issues
169
+ source_code_uri: https://github.com/upserve/docker-api
170
+ rubygems_mfa_required: 'true'
182
171
  post_install_message:
183
172
  rdoc_options: []
184
173
  require_paths:
@@ -194,7 +183,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
194
183
  - !ruby/object:Gem::Version
195
184
  version: '0'
196
185
  requirements: []
197
- rubygems_version: 3.1.6
186
+ rubygems_version: 3.2.34
198
187
  signing_key:
199
188
  specification_version: 4
200
189
  summary: A simple REST client for the Docker Remote API