docker-api 1.34.2 → 2.4.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 +5 -5
- data/README.md +7 -7
- data/lib/docker/base.rb +2 -0
- data/lib/docker/connection.rb +83 -16
- data/lib/docker/container.rb +4 -12
- data/lib/docker/error.rb +2 -0
- data/lib/docker/event.rb +5 -1
- data/lib/docker/exec.rb +9 -0
- data/lib/docker/image.rb +31 -11
- data/lib/docker/messages.rb +2 -0
- data/lib/docker/messages_stack.rb +2 -0
- data/lib/docker/network.rb +5 -3
- data/lib/docker/rake_task.rb +2 -0
- data/lib/docker/util.rb +47 -16
- data/lib/docker/version.rb +3 -4
- data/lib/docker/volume.rb +2 -0
- data/lib/docker-api.rb +2 -0
- data/lib/docker.rb +16 -15
- data/lib/excon/middlewares/hijack.rb +6 -2
- metadata +6 -21
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
|
-
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
2
|
+
SHA256:
|
|
3
|
+
metadata.gz: c8c844030bda8d245234ddd5d977ad5202b9a2f0a2e34175579790d79e6f584b
|
|
4
|
+
data.tar.gz: 5b793f7689d091a8a765853511e4f0f8f495338250cdec62bce30fc7a83d5117
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: d5ce08a8dfbe1e5377ceb62d8186e4973ca6f7fa8d166bd9177f8f4a3338dc2dfeb3d735d3c54e31b15373f4124cc87d509d369a34e0f1598291105a4593947f
|
|
7
|
+
data.tar.gz: d27a38af366bbb20febcc3699238a56f5c52d24394780c25d3caf72bc6f9314fb8bae6dd9c7f53cd5ff30558f5bbc353d22fc9f236913a2d8786488c9a702aea
|
data/README.md
CHANGED
|
@@ -1,10 +1,10 @@
|
|
|
1
1
|
docker-api
|
|
2
2
|
==========
|
|
3
|
-
[](https://badge.fury.io/rb/docker-api) [](https://badge.fury.io/rb/docker-api) [](https://codeclimate.com/github/upserve/docker-api)
|
|
4
4
|
|
|
5
|
-
This gem provides an object-oriented interface to the [Docker
|
|
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.*
|
|
6
6
|
|
|
7
|
-
If you're interested in using Docker to package your apps, we recommend the [dockly](https://github.com/
|
|
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
|
|
|
9
9
|
Installation
|
|
10
10
|
------------
|
|
@@ -34,9 +34,11 @@ Usage
|
|
|
34
34
|
|
|
35
35
|
docker-api is designed to be very lightweight. Almost no state is cached (aside from id's which are immutable) to ensure that each method call's information is up to date. As such, just about every external method represents an API call.
|
|
36
36
|
|
|
37
|
+
At this time, basic `podman` support has been added via the podman docker-compatible API socket.
|
|
38
|
+
|
|
37
39
|
## Starting up
|
|
38
40
|
|
|
39
|
-
Follow the [installation instructions](https://docs.docker.com/
|
|
41
|
+
Follow the [installation instructions](https://docs.docker.com/install/), and then run:
|
|
40
42
|
|
|
41
43
|
```shell
|
|
42
44
|
$ sudo docker -d
|
|
@@ -52,7 +54,7 @@ If you're running Docker locally as a socket, there is no setup to do in Ruby. I
|
|
|
52
54
|
Docker.url = 'tcp://example.com:5422'
|
|
53
55
|
```
|
|
54
56
|
|
|
55
|
-
Two things to note here. The first is that this gem uses [excon](
|
|
57
|
+
Two things to note here. The first is that this gem uses [excon](https://github.com/excon/excon), so any of the options that are valid for `Excon.new` are also valid for `Docker.options`. Second, by default Docker runs on a socket. The gem will assume you want to connect to the socket unless you specify otherwise.
|
|
56
58
|
|
|
57
59
|
Also, you may set the above variables via `ENV` variables. For example:
|
|
58
60
|
|
|
@@ -76,8 +78,6 @@ irb(main):004:0> Docker.options
|
|
|
76
78
|
=> {}
|
|
77
79
|
```
|
|
78
80
|
|
|
79
|
-
Before doing anything else, ensure you have the correct version of the Docker API. To do this, run `Docker.validate_version!`. If your installed version is not supported, a `Docker::Error::VersionError` is raised.
|
|
80
|
-
|
|
81
81
|
### SSL
|
|
82
82
|
|
|
83
83
|
When running docker using SSL, setting the DOCKER_CERT_PATH will configure docker-api to use SSL.
|
data/lib/docker/base.rb
CHANGED
data/lib/docker/connection.rb
CHANGED
|
@@ -1,6 +1,11 @@
|
|
|
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
|
|
6
|
+
require 'docker/util'
|
|
7
|
+
require 'docker/error'
|
|
8
|
+
|
|
4
9
|
include Docker::Error
|
|
5
10
|
|
|
6
11
|
attr_reader :url, :options
|
|
@@ -35,21 +40,58 @@ class Docker::Connection
|
|
|
35
40
|
|
|
36
41
|
# Send a request to the server with the `
|
|
37
42
|
def request(*args, &block)
|
|
43
|
+
retries ||= 0
|
|
38
44
|
request = compile_request_params(*args, &block)
|
|
39
45
|
log_request(request)
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
46
|
+
begin
|
|
47
|
+
resource.request(request).body
|
|
48
|
+
rescue Excon::Errors::BadRequest => ex
|
|
49
|
+
if retries < 2
|
|
50
|
+
response_cause = ''
|
|
51
|
+
begin
|
|
52
|
+
response_cause = JSON.parse(ex.response.body)['cause']
|
|
53
|
+
rescue JSON::ParserError
|
|
54
|
+
#noop
|
|
55
|
+
end
|
|
56
|
+
|
|
57
|
+
if response_cause.is_a?(String)
|
|
58
|
+
# The error message will tell the application type given and then the
|
|
59
|
+
# application type that the message should be
|
|
60
|
+
#
|
|
61
|
+
# This is not perfect since it relies on processing a message that
|
|
62
|
+
# could change in the future. However, it should be a good stop-gap
|
|
63
|
+
# until all methods are updated to pass in the appropriate content
|
|
64
|
+
# type.
|
|
65
|
+
#
|
|
66
|
+
# A current example message is:
|
|
67
|
+
# * 'Content-Type: application/json is not supported. Should be "application/x-tar"'
|
|
68
|
+
matches = response_cause.delete('"\'').scan(%r{(application/\S+)})
|
|
69
|
+
unless matches.count < 2
|
|
70
|
+
Docker.logger.warn(
|
|
71
|
+
<<~RETRY_WARNING
|
|
72
|
+
Automatically retrying with content type '#{response_cause}'
|
|
73
|
+
Original Error: #{ex}
|
|
74
|
+
RETRY_WARNING
|
|
75
|
+
) if Docker.logger
|
|
76
|
+
|
|
77
|
+
request[:headers]['Content-Type'] = matches.last.first
|
|
78
|
+
retries += 1
|
|
79
|
+
retry
|
|
80
|
+
end
|
|
81
|
+
end
|
|
82
|
+
end
|
|
83
|
+
raise ClientError, ex.response.body
|
|
84
|
+
rescue Excon::Errors::Unauthorized => ex
|
|
85
|
+
raise UnauthorizedError, ex.response.body
|
|
86
|
+
rescue Excon::Errors::NotFound => ex
|
|
87
|
+
raise NotFoundError, ex.response.body
|
|
88
|
+
rescue Excon::Errors::Conflict => ex
|
|
89
|
+
raise ConflictError, ex.response.body
|
|
90
|
+
rescue Excon::Errors::InternalServerError => ex
|
|
91
|
+
raise ServerError, ex.response.body
|
|
92
|
+
rescue Excon::Errors::Timeout => ex
|
|
93
|
+
raise TimeoutError, ex.message
|
|
94
|
+
end
|
|
53
95
|
end
|
|
54
96
|
|
|
55
97
|
def log_request(request)
|
|
@@ -60,13 +102,38 @@ class Docker::Connection
|
|
|
60
102
|
end
|
|
61
103
|
end
|
|
62
104
|
|
|
105
|
+
def to_s
|
|
106
|
+
"Docker::Connection { :url => #{url}, :options => #{options} }"
|
|
107
|
+
end
|
|
108
|
+
|
|
63
109
|
# Delegate all HTTP methods to the #request.
|
|
64
110
|
[:get, :put, :post, :delete].each do |method|
|
|
65
111
|
define_method(method) { |*args, &block| request(method, *args, &block) }
|
|
66
112
|
end
|
|
67
113
|
|
|
68
|
-
|
|
69
|
-
|
|
114
|
+
# Common attribute requests
|
|
115
|
+
def info
|
|
116
|
+
Docker::Util.parse_json(get('/info'))
|
|
117
|
+
end
|
|
118
|
+
|
|
119
|
+
def ping
|
|
120
|
+
get('/_ping')
|
|
121
|
+
end
|
|
122
|
+
|
|
123
|
+
def podman?
|
|
124
|
+
@podman ||= !(
|
|
125
|
+
Array(version['Components']).find do |component|
|
|
126
|
+
component['Name'].include?('Podman')
|
|
127
|
+
end
|
|
128
|
+
).nil?
|
|
129
|
+
end
|
|
130
|
+
|
|
131
|
+
def rootless?
|
|
132
|
+
@rootless ||= (info['Rootless'] == true)
|
|
133
|
+
end
|
|
134
|
+
|
|
135
|
+
def version
|
|
136
|
+
@version ||= Docker::Util.parse_json(get('/version'))
|
|
70
137
|
end
|
|
71
138
|
|
|
72
139
|
private
|
|
@@ -80,7 +147,7 @@ private
|
|
|
80
147
|
user_agent = "Swipely/Docker-API #{Docker::VERSION}"
|
|
81
148
|
{
|
|
82
149
|
:method => http_method,
|
|
83
|
-
:path =>
|
|
150
|
+
:path => path,
|
|
84
151
|
:query => query,
|
|
85
152
|
:headers => { 'Content-Type' => content_type,
|
|
86
153
|
'User-Agent' => user_agent,
|
data/lib/docker/container.rb
CHANGED
|
@@ -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
|
|
@@ -189,7 +191,7 @@ class Docker::Container
|
|
|
189
191
|
end
|
|
190
192
|
|
|
191
193
|
def streaming_logs(opts = {}, &block)
|
|
192
|
-
stack_size = opts.delete('stack_size') || -1
|
|
194
|
+
stack_size = opts.delete('stack_size') || opts.delete(:stack_size) || -1
|
|
193
195
|
tty = opts.delete('tty') || opts.delete(:tty) || false
|
|
194
196
|
msgs = Docker::MessagesStack.new(stack_size)
|
|
195
197
|
excon_params = {response_block: Docker::Util.attach_for(block, msgs, tty), idempotent: false}
|
|
@@ -266,16 +268,6 @@ class Docker::Container
|
|
|
266
268
|
end
|
|
267
269
|
end
|
|
268
270
|
|
|
269
|
-
def copy(path, &block)
|
|
270
|
-
connection.post(
|
|
271
|
-
path_for(:copy),
|
|
272
|
-
{},
|
|
273
|
-
body: MultiJson.dump('Resource' => path),
|
|
274
|
-
response_block: block
|
|
275
|
-
)
|
|
276
|
-
self
|
|
277
|
-
end
|
|
278
|
-
|
|
279
271
|
def archive_out(path, &block)
|
|
280
272
|
connection.get(
|
|
281
273
|
path_for(:archive),
|
|
@@ -343,7 +335,7 @@ class Docker::Container
|
|
|
343
335
|
|
|
344
336
|
# Return the container with specified ID
|
|
345
337
|
def self.get(id, opts = {}, conn = Docker.connection)
|
|
346
|
-
container_json = conn.get("/containers/#{
|
|
338
|
+
container_json = conn.get("/containers/#{id}/json", opts)
|
|
347
339
|
hash = Docker::Util.parse_json(container_json) || {}
|
|
348
340
|
new(conn, hash)
|
|
349
341
|
end
|
data/lib/docker/error.rb
CHANGED
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
|
|
@@ -29,7 +31,9 @@ class Docker::Event
|
|
|
29
31
|
|
|
30
32
|
def stream(opts = {}, conn = Docker.connection, &block)
|
|
31
33
|
conn.get('/events', opts, :response_block => lambda { |b, r, t|
|
|
32
|
-
|
|
34
|
+
b.each_line do |line|
|
|
35
|
+
block.call(new_event(line, r, t))
|
|
36
|
+
end
|
|
33
37
|
})
|
|
34
38
|
end
|
|
35
39
|
|
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
|
|
@@ -19,6 +21,13 @@ class Docker::Exec
|
|
|
19
21
|
# @return [Docker::Exec] self
|
|
20
22
|
def self.create(options = {}, conn = Docker.connection)
|
|
21
23
|
container = options.delete('Container')
|
|
24
|
+
|
|
25
|
+
# Podman does not attach these by default but does require them to be attached
|
|
26
|
+
if ::Docker.podman?(conn)
|
|
27
|
+
options['AttachStderr'] = true if options['AttachStderr'].nil?
|
|
28
|
+
options['AttachStdout'] = true if options['AttachStdout'].nil?
|
|
29
|
+
end
|
|
30
|
+
|
|
22
31
|
resp = conn.post("/containers/#{container}/exec", {},
|
|
23
32
|
body: MultiJson.dump(options))
|
|
24
33
|
hash = Docker::Util.parse_json(resp) || {}
|
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)
|
|
@@ -65,7 +67,16 @@ class Docker::Image
|
|
|
65
67
|
|
|
66
68
|
# Remove the Image from the server.
|
|
67
69
|
def remove(opts = {})
|
|
68
|
-
name = opts.delete(:name)
|
|
70
|
+
name = opts.delete(:name)
|
|
71
|
+
|
|
72
|
+
unless name
|
|
73
|
+
if ::Docker.podman?(connection)
|
|
74
|
+
name = self.id.split(':').last
|
|
75
|
+
else
|
|
76
|
+
name = self.id
|
|
77
|
+
end
|
|
78
|
+
end
|
|
79
|
+
|
|
69
80
|
connection.delete("/images/#{name}", opts)
|
|
70
81
|
end
|
|
71
82
|
alias_method :delete, :remove
|
|
@@ -110,7 +121,7 @@ class Docker::Image
|
|
|
110
121
|
def create(opts = {}, creds = nil, conn = Docker.connection, &block)
|
|
111
122
|
credentials = creds.nil? ? Docker.creds : MultiJson.dump(creds)
|
|
112
123
|
headers = credentials && Docker::Util.build_auth_header(credentials) || {}
|
|
113
|
-
body = ''
|
|
124
|
+
body = +''
|
|
114
125
|
conn.post(
|
|
115
126
|
'/images/create',
|
|
116
127
|
opts,
|
|
@@ -126,7 +137,7 @@ class Docker::Image
|
|
|
126
137
|
|
|
127
138
|
# Return a specific image.
|
|
128
139
|
def get(id, opts = {}, conn = Docker.connection)
|
|
129
|
-
image_json = conn.get("/images/#{
|
|
140
|
+
image_json = conn.get("/images/#{id}/json", opts)
|
|
130
141
|
hash = Docker::Util.parse_json(image_json) || {}
|
|
131
142
|
new(conn, hash)
|
|
132
143
|
end
|
|
@@ -159,7 +170,7 @@ class Docker::Image
|
|
|
159
170
|
end
|
|
160
171
|
nil
|
|
161
172
|
else
|
|
162
|
-
string = ''
|
|
173
|
+
string = +''
|
|
163
174
|
save_stream(names, {}, conn, &response_block_for_save(string))
|
|
164
175
|
string
|
|
165
176
|
end
|
|
@@ -174,7 +185,7 @@ class Docker::Image
|
|
|
174
185
|
# By using compare_by_identity we can create a Hash that has
|
|
175
186
|
# the same key multiple times.
|
|
176
187
|
query = {}.tap(&:compare_by_identity)
|
|
177
|
-
Array(names).each { |name| query['names'.dup] =
|
|
188
|
+
Array(names).each { |name| query['names'.dup] = name }
|
|
178
189
|
conn.get(
|
|
179
190
|
'/images/get',
|
|
180
191
|
query,
|
|
@@ -187,7 +198,7 @@ class Docker::Image
|
|
|
187
198
|
def load(tar, opts = {}, conn = Docker.connection, creds = nil, &block)
|
|
188
199
|
headers = build_headers(creds)
|
|
189
200
|
io = tar.is_a?(String) ? File.open(tar, 'rb') : tar
|
|
190
|
-
body = ""
|
|
201
|
+
body = +""
|
|
191
202
|
conn.post(
|
|
192
203
|
'/images/load',
|
|
193
204
|
opts,
|
|
@@ -227,7 +238,16 @@ class Docker::Image
|
|
|
227
238
|
# Import an Image from the output of Docker::Container#export. The first
|
|
228
239
|
# argument may either be a File or URI.
|
|
229
240
|
def import(imp, opts = {}, conn = Docker.connection)
|
|
230
|
-
open
|
|
241
|
+
require 'open-uri'
|
|
242
|
+
|
|
243
|
+
# This differs after Ruby 2.4
|
|
244
|
+
if URI.public_methods.include?(:open)
|
|
245
|
+
munged_open = URI.method(:open)
|
|
246
|
+
else
|
|
247
|
+
munged_open = self.method(:open)
|
|
248
|
+
end
|
|
249
|
+
|
|
250
|
+
munged_open.call(imp) do |io|
|
|
231
251
|
import_stream(opts, conn) do
|
|
232
252
|
io.read(Excon.defaults[:chunk_size]).to_s
|
|
233
253
|
end
|
|
@@ -249,7 +269,7 @@ class Docker::Image
|
|
|
249
269
|
|
|
250
270
|
# Given a Dockerfile as a string, builds an Image.
|
|
251
271
|
def build(commands, opts = {}, connection = Docker.connection, &block)
|
|
252
|
-
body = ""
|
|
272
|
+
body = +""
|
|
253
273
|
connection.post(
|
|
254
274
|
'/build', opts,
|
|
255
275
|
:body => Docker::Util.create_tar('Dockerfile' => commands),
|
|
@@ -270,7 +290,7 @@ class Docker::Image
|
|
|
270
290
|
headers = build_headers(creds)
|
|
271
291
|
|
|
272
292
|
# The response_block passed to Excon will build up this body variable.
|
|
273
|
-
body = ""
|
|
293
|
+
body = +""
|
|
274
294
|
connection.post(
|
|
275
295
|
'/build', opts,
|
|
276
296
|
:headers => headers,
|
|
@@ -322,7 +342,7 @@ class Docker::Image
|
|
|
322
342
|
# Convience method to get the Dockerfile for a file hash and a path to
|
|
323
343
|
# output to.
|
|
324
344
|
def dockerfile_for(file_hash, output_path)
|
|
325
|
-
dockerfile = "from #{self.id}\n"
|
|
345
|
+
dockerfile = +"from #{self.id}\n"
|
|
326
346
|
|
|
327
347
|
file_hash.keys.each do |basename|
|
|
328
348
|
dockerfile << "add #{basename} #{output_path}\n"
|
data/lib/docker/messages.rb
CHANGED
data/lib/docker/network.rb
CHANGED
|
@@ -1,3 +1,5 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
1
3
|
# This class represents a Docker Network.
|
|
2
4
|
class Docker::Network
|
|
3
5
|
include Docker::Base
|
|
@@ -34,7 +36,7 @@ class Docker::Network
|
|
|
34
36
|
end
|
|
35
37
|
|
|
36
38
|
def reload
|
|
37
|
-
network_json = @connection.get("/networks/#{
|
|
39
|
+
network_json = @connection.get("/networks/#{@id}")
|
|
38
40
|
hash = Docker::Util.parse_json(network_json) || {}
|
|
39
41
|
@info = hash
|
|
40
42
|
end
|
|
@@ -51,7 +53,7 @@ class Docker::Network
|
|
|
51
53
|
end
|
|
52
54
|
|
|
53
55
|
def get(id, opts = {}, conn = Docker.connection)
|
|
54
|
-
network_json = conn.get("/networks/#{
|
|
56
|
+
network_json = conn.get("/networks/#{id}", opts)
|
|
55
57
|
hash = Docker::Util.parse_json(network_json) || {}
|
|
56
58
|
new(conn, hash)
|
|
57
59
|
end
|
|
@@ -62,7 +64,7 @@ class Docker::Network
|
|
|
62
64
|
end
|
|
63
65
|
|
|
64
66
|
def remove(id, opts = {}, conn = Docker.connection)
|
|
65
|
-
conn.delete("/networks/#{
|
|
67
|
+
conn.delete("/networks/#{id}", opts)
|
|
66
68
|
nil
|
|
67
69
|
end
|
|
68
70
|
alias_method :delete, :remove
|
data/lib/docker/rake_task.rb
CHANGED
data/lib/docker/util.rb
CHANGED
|
@@ -1,8 +1,12 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require 'set'
|
|
4
|
+
|
|
1
5
|
# This module holds shared logic that doesn't really belong anywhere else in the
|
|
2
6
|
# gem.
|
|
3
7
|
module Docker::Util
|
|
4
8
|
# http://www.tldp.org/LDP/GNU-Linux-Tools-Summary/html/x11655.htm#STANDARD-WILDCARDS
|
|
5
|
-
GLOB_WILDCARDS = /[\?\*\[\{]/
|
|
9
|
+
GLOB_WILDCARDS = /[\?\*\[\{\]\}]/
|
|
6
10
|
|
|
7
11
|
include Docker::Error
|
|
8
12
|
|
|
@@ -142,10 +146,35 @@ module Docker::Util
|
|
|
142
146
|
File.new(tempfile.path, 'r')
|
|
143
147
|
end
|
|
144
148
|
|
|
149
|
+
|
|
150
|
+
# return the set of files that form the docker context
|
|
151
|
+
# implement this logic https://docs.docker.com/engine/reference/builder/#dockerignore-file
|
|
152
|
+
def docker_context(directory)
|
|
153
|
+
all_files = glob_all_files(File.join(directory, "**/*"))
|
|
154
|
+
dockerignore = File.join(directory, '.dockerignore')
|
|
155
|
+
return all_files unless all_files.include?(dockerignore)
|
|
156
|
+
|
|
157
|
+
# Iterate over valid lines, starting with the initial glob as working set
|
|
158
|
+
File
|
|
159
|
+
.read(dockerignore) # https://docs.docker.com/engine/reference/builder/#dockerignore-file
|
|
160
|
+
.each_line # "a newline-separated list of patterns"
|
|
161
|
+
.map(&:strip) # "A preprocessing step removes leading and trailing whitespace"
|
|
162
|
+
.reject(&:empty?) # "Lines that are blank after preprocessing are ignored"
|
|
163
|
+
.reject { |p| p.start_with?('#') } # "if [a line starts with `#`], then this line is considered as a comment"
|
|
164
|
+
.each_with_object(Set.new(all_files)) do |p, working_set|
|
|
165
|
+
# determine the pattern (p) and whether it is to be added or removed from context
|
|
166
|
+
add = p.start_with?("!")
|
|
167
|
+
# strip leading "!" from pattern p, then prepend the base directory
|
|
168
|
+
matches = dockerignore_compatible_glob(File.join(directory, add ? p[1..-1] : p))
|
|
169
|
+
# add or remove the matched items as indicated in the ignore file
|
|
170
|
+
add ? working_set.merge(matches) : working_set.replace(working_set.difference(matches))
|
|
171
|
+
end
|
|
172
|
+
.to_a
|
|
173
|
+
end
|
|
174
|
+
|
|
145
175
|
def create_relative_dir_tar(directory, output)
|
|
146
176
|
Gem::Package::TarWriter.new(output) do |tar|
|
|
147
|
-
files =
|
|
148
|
-
remove_ignored_files!(directory, files)
|
|
177
|
+
files = docker_context(directory)
|
|
149
178
|
|
|
150
179
|
files.each do |prefixed_file_name|
|
|
151
180
|
stat = File.stat(prefixed_file_name)
|
|
@@ -259,21 +288,23 @@ module Docker::Util
|
|
|
259
288
|
}
|
|
260
289
|
end
|
|
261
290
|
|
|
262
|
-
|
|
263
|
-
|
|
291
|
+
# do a directory glob that matches .dockerignore behavior
|
|
292
|
+
# specifically: matched directories are considered a recursive match
|
|
293
|
+
def dockerignore_compatible_glob(pattern)
|
|
294
|
+
begin
|
|
295
|
+
some_dirs, some_files = glob_all_files(pattern).partition { |f| File.directory?(f) }
|
|
296
|
+
# since all directories will be re-processed with a /**/* glob, we can preemptively
|
|
297
|
+
# eliminate any whose parent directory is already in this set. This saves significant time.
|
|
298
|
+
some_files + some_dirs.reject { |d| some_dirs.any? { |pd| d.start_with?(pd) && d != pd } }
|
|
299
|
+
end.each_with_object(Set.new) do |f, acc|
|
|
300
|
+
# expand any directories by globbing; flatten results
|
|
301
|
+
acc.merge(File.directory?(f) ? glob_all_files("#{f}/**/*") : [f])
|
|
302
|
+
end
|
|
264
303
|
end
|
|
265
304
|
|
|
266
|
-
def
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
ignored_files(directory, ignore).each { |f| files.delete(f) }
|
|
305
|
+
def glob_all_files(pattern)
|
|
306
|
+
# globs of "a_dir/**/*" can return "a_dir/.", so explicitly reject those
|
|
307
|
+
(Dir.glob(pattern, File::FNM_DOTMATCH) - ['..', '.']).reject { |p| p.end_with?("/.") }
|
|
270
308
|
end
|
|
271
309
|
|
|
272
|
-
def ignored_files(directory, ignore_file)
|
|
273
|
-
patterns = File.read(ignore_file).split("\n").each(&:strip!)
|
|
274
|
-
patterns.reject! { |p| p.empty? || p.start_with?('#') }
|
|
275
|
-
patterns.map! { |p| File.join(directory, p) }
|
|
276
|
-
patterns.map! { |p| File.directory?(p) ? "#{p}/**/*" : p }
|
|
277
|
-
patterns.flat_map { |p| p =~ GLOB_WILDCARDS ? glob_all_files(p) : p }
|
|
278
|
-
end
|
|
279
310
|
end
|
data/lib/docker/version.rb
CHANGED
data/lib/docker/volume.rb
CHANGED
data/lib/docker-api.rb
CHANGED
data/lib/docker.rb
CHANGED
|
@@ -1,3 +1,5 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
1
3
|
require 'cgi'
|
|
2
4
|
require 'multi_json'
|
|
3
5
|
require 'excon'
|
|
@@ -106,17 +108,27 @@ module Docker
|
|
|
106
108
|
|
|
107
109
|
# Get the version of Go, Docker, and optionally the Git commit.
|
|
108
110
|
def version(connection = self.connection)
|
|
109
|
-
|
|
111
|
+
connection.version
|
|
110
112
|
end
|
|
111
113
|
|
|
112
114
|
# Get more information about the Docker server.
|
|
113
115
|
def info(connection = self.connection)
|
|
114
|
-
|
|
116
|
+
connection.info
|
|
115
117
|
end
|
|
116
118
|
|
|
117
119
|
# Ping the Docker server.
|
|
118
120
|
def ping(connection = self.connection)
|
|
119
|
-
connection.
|
|
121
|
+
connection.ping
|
|
122
|
+
end
|
|
123
|
+
|
|
124
|
+
# Determine if the server is podman or docker.
|
|
125
|
+
def podman?(connection = self.connection)
|
|
126
|
+
connection.podman?
|
|
127
|
+
end
|
|
128
|
+
|
|
129
|
+
# Determine if the session is rootless.
|
|
130
|
+
def rootless?(connection = self.connection)
|
|
131
|
+
connection.rootless?
|
|
120
132
|
end
|
|
121
133
|
|
|
122
134
|
# Login to the Docker registry.
|
|
@@ -129,19 +141,8 @@ module Docker
|
|
|
129
141
|
raise Docker::Error::AuthenticationError
|
|
130
142
|
end
|
|
131
143
|
|
|
132
|
-
# When the correct version of Docker is installed, returns true. Otherwise,
|
|
133
|
-
# raises a VersionError.
|
|
134
|
-
def validate_version!
|
|
135
|
-
Docker.info
|
|
136
|
-
true
|
|
137
|
-
rescue Docker::Error::TimeoutError
|
|
138
|
-
raise
|
|
139
|
-
rescue Docker::Error::DockerError
|
|
140
|
-
raise Docker::Error::VersionError, "Expected API Version: #{API_VERSION}"
|
|
141
|
-
end
|
|
142
|
-
|
|
143
144
|
module_function :default_socket_url, :env_url, :url, :url=, :env_options,
|
|
144
145
|
:options, :options=, :creds, :creds=, :logger, :logger=,
|
|
145
146
|
:connection, :reset!, :reset_connection!, :version, :info,
|
|
146
|
-
:ping, :
|
|
147
|
+
:ping, :podman?, :rootless?, :authenticate!, :ssl_options
|
|
147
148
|
end
|
|
@@ -1,11 +1,15 @@
|
|
|
1
|
-
|
|
2
|
-
VALID_REQUEST_KEYS << :hijack_block
|
|
1
|
+
# frozen_string_literal: true
|
|
3
2
|
|
|
3
|
+
module Excon
|
|
4
4
|
module Middleware
|
|
5
5
|
# Hijack is an Excon middleware which parses response headers and then
|
|
6
6
|
# yields the underlying TCP socket for raw TCP communication (used to
|
|
7
7
|
# attach to STDIN of containers).
|
|
8
8
|
class Hijack < Base
|
|
9
|
+
def self.valid_parameter_keys
|
|
10
|
+
[:hijack_block].freeze
|
|
11
|
+
end
|
|
12
|
+
|
|
9
13
|
def build_response(status, socket)
|
|
10
14
|
response = {
|
|
11
15
|
:body => '',
|
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:
|
|
4
|
+
version: 2.4.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:
|
|
11
|
+
date: 2024-10-30 00:00:00.000000000 Z
|
|
12
12
|
dependencies:
|
|
13
13
|
- !ruby/object:Gem::Dependency
|
|
14
14
|
name: excon
|
|
@@ -16,14 +16,14 @@ dependencies:
|
|
|
16
16
|
requirements:
|
|
17
17
|
- - ">="
|
|
18
18
|
- !ruby/object:Gem::Version
|
|
19
|
-
version: 0.
|
|
19
|
+
version: 0.64.0
|
|
20
20
|
type: :runtime
|
|
21
21
|
prerelease: false
|
|
22
22
|
version_requirements: !ruby/object:Gem::Requirement
|
|
23
23
|
requirements:
|
|
24
24
|
- - ">="
|
|
25
25
|
- !ruby/object:Gem::Version
|
|
26
|
-
version: 0.
|
|
26
|
+
version: 0.64.0
|
|
27
27
|
- !ruby/object:Gem::Dependency
|
|
28
28
|
name: multi_json
|
|
29
29
|
requirement: !ruby/object:Gem::Requirement
|
|
@@ -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
|
|
@@ -175,7 +161,7 @@ files:
|
|
|
175
161
|
- lib/docker/version.rb
|
|
176
162
|
- lib/docker/volume.rb
|
|
177
163
|
- lib/excon/middlewares/hijack.rb
|
|
178
|
-
homepage: https://github.com/
|
|
164
|
+
homepage: https://github.com/upserve/docker-api
|
|
179
165
|
licenses:
|
|
180
166
|
- MIT
|
|
181
167
|
metadata: {}
|
|
@@ -194,8 +180,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
|
194
180
|
- !ruby/object:Gem::Version
|
|
195
181
|
version: '0'
|
|
196
182
|
requirements: []
|
|
197
|
-
|
|
198
|
-
rubygems_version: 2.5.1
|
|
183
|
+
rubygems_version: 3.1.6
|
|
199
184
|
signing_key:
|
|
200
185
|
specification_version: 4
|
|
201
186
|
summary: A simple REST client for the Docker Remote API
|