dockerapi 0.2.0 → 0.7.0

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 12366435331bd8fc215857a65d28b337f9ccb22556d8451e6f213d4fda8a5113
4
- data.tar.gz: 724f52952a3f24dc664dc1b1b5cc66a75ae920b281de61b36ae74ed5431d7159
3
+ metadata.gz: 756ada080034328ec7c5e7950d5565e6e7d3612bc92af58d9c8f89558a75f5c5
4
+ data.tar.gz: cba4ac000ef8e9d4374bc36be4e87dd3c2227b2fa59cc062f24ad7a06de11c52
5
5
  SHA512:
6
- metadata.gz: 61ac762e369e72c7b715b2ae3c19cc9046fd25425ef865bbb723a86f79df57716dfa1c1d42720830b10f441150d96def147649921e764180c194e778589071bd
7
- data.tar.gz: 9d2327454dc893fe4cf9ab5adf06619a69142dea22bad32536886552711f3f6119060f859092af4ab17285d2a740e2aa56b563d32fb367c5a55cc6183c2967dd
6
+ metadata.gz: 0c6c059fa47afc4749dac18c62d61a984c2874b41046c2926d7eb884a7761ea088fef4209deaf71fe5864e5351e8e96a975f29520760ae6559209d323dd4f4b6
7
+ data.tar.gz: 25bad2ed4c92a2b5551d78e3a12833037623e7f26045a829b587d4136c383d7431ae7a3d17bf6b9ca139c4fcc060cbf07f15a61854a4a43f601455613174f943
@@ -1,3 +1,57 @@
1
+ # 0.7.0
2
+
3
+ Major changes: Docker::API::Connection is now a regular class intead of a Singleton, allowing multiple connections to be stablished within the same program (replacing the connect_to implementation). To leverage this feature, API-related classes must be initialized and may or may not receive a Docker::API::Connection as parameter, or it'll connect to /var/run/docker.sock by default. For this reason, class methods were replaced with instance methods. Documentation will reflect this changes of implementation.
4
+
5
+ Bug fix: Image push returns a 20X status even when the push is unsucessful. To prevent false positives, it now requires the authentication parameters to be provided, generating a 403 status for invalid credentials or an error if they are absent.
6
+
7
+ # 0.6.0
8
+
9
+ Add connection parameters specifications with connect_to in Docker::API::Connection.
10
+
11
+ Add Docker::API::Exec methods:
12
+ * create
13
+ * start
14
+ * resize
15
+ * inspect
16
+
17
+ # 0.5.0
18
+
19
+ Add Docker::API::System methods:
20
+ * auth
21
+ * ping
22
+ * info
23
+ * version
24
+ * events
25
+ * df
26
+
27
+ Add new response class Docker::API::Response with the following methods:
28
+ * json
29
+ * path
30
+ * success?
31
+
32
+ Error classes output better error messages.
33
+
34
+ # 0.4.0
35
+
36
+ Add Docker::API::Network methods:
37
+ * list
38
+ * inspect
39
+ * create
40
+ * remove
41
+ * prune
42
+ * connect
43
+ * disconnect
44
+
45
+ # 0.3.0
46
+
47
+ Add Docker::API::Volume methods:
48
+ * list
49
+ * inspect
50
+ * create
51
+ * remove
52
+ * prune
53
+
54
+
1
55
  # 0.2.0
2
56
 
3
57
  Add Docker::API::Image methods:
@@ -1,7 +1,7 @@
1
1
  PATH
2
2
  remote: .
3
3
  specs:
4
- dockerapi (0.2.0)
4
+ dockerapi (0.7.0)
5
5
  excon (~> 0.74.0)
6
6
 
7
7
  GEM
data/README.md CHANGED
@@ -20,72 +20,76 @@ Or install it yourself as:
20
20
 
21
21
  ## Usage
22
22
 
23
+ New implementation details as of v0.7.0.
24
+
23
25
  ### Images
24
26
 
25
27
  ```ruby
28
+ # Connect to local image endpoints
29
+ image = Docker::API::Image.new
30
+
26
31
  # Pull from a public repository
27
- Docker::API::Image.create( fromImage: "nginx:latest" )
32
+ image.create( fromImage: "nginx:latest" )
28
33
 
29
34
  # Pull from a private repository
30
- Docker::API::Image.create( {fromImage: "private/repo:tag"}, {username: "janedoe", password: "password"} )
35
+ image.create( {fromImage: "private/repo:tag"}, {username: "janedoe", password: "password"} )
31
36
 
32
37
  # Create image from local tar file
33
- Docker::API::Image.create( fromSrc: "/path/to/file.tar", repo: "repo:tag" )
38
+ image.create( fromSrc: "/path/to/file.tar", repo: "repo:tag" )
34
39
 
35
40
  # Create image from remote tar file
36
- Docker::API::Image.create( fromSrc: "https://url.to/file.tar", repo: "repo:tag" )
41
+ image.create( fromSrc: "https://url.to/file.tar", repo: "repo:tag" )
37
42
 
38
43
  # List images
39
- Docker::API::Image.list
40
- Docker::API::Image.list( all:true )
44
+ image.list
41
45
 
42
46
  # Inspect image
43
- Docker::API::Image.inspect("image")
47
+ image.inspect("image")
44
48
 
45
49
  # History
46
- Docker::API::Image.history("image")
50
+ image.history("image")
47
51
 
48
52
  # Search image
49
- Docker::API::Image.search(term: "busybox", limit: 2)
50
- Docker::API::Image.search(term: "busybox", filters: {"is-automated": {"true": true}})
51
- Docker::API::Image.search(term: "busybox", filters: {"is-official": {"true": true}})
53
+ image.search(term: "busybox", limit: 2)
54
+ image.search(term: "busybox", filters: {"is-automated": {"true": true}})
55
+ image.search(term: "busybox", filters: {"is-official": {"true": true}})
52
56
 
53
57
  # Tag image
54
- Docker::API::Image.tag("current:tag", repo: "new:tag") # or
55
- Docker::API::Image.tag("current:tag", repo: "new", tag: "tag")
58
+ image.tag("current:tag", repo: "new:tag") # or
59
+ image.tag("current:tag", repo: "new", tag: "tag")
56
60
 
57
61
  # Push image
58
- Docker::API::Image.push("repo:tag") # to dockerhub
59
- Docker::API::Image.push("localhost:5000/repo:tag") # to local registry
60
- Docker::API::Image.push("private/repo", {tag: "tag"}, {username: "janedoe", password: "password"} # to private repository
62
+ image.push("repo:tag") # to dockerhub
63
+ image.push("localhost:5000/repo:tag") # to local registry
64
+ image.push("private/repo", {tag: "tag"}, {username: "janedoe", password: "password"} # to private repository
61
65
 
62
- # Remove container
63
- Docker::API::Image.remove("image")
64
- Docker::API::Image.remove("image", force: true)
66
+ # Remove image
67
+ image.remove("image")
68
+ image.remove("image", force: true)
65
69
 
66
70
  # Remove unsued images (prune)
67
- Docker::API::Image.prune(filters: {dangling: {"false": true}})
71
+ image.prune(filters: {dangling: {"false": true}})
68
72
 
69
73
  # Create image from a container (commit)
70
- Docker::API::Image.commit(container: container, repo: "my/image", tag: "latest", comment: "Comment from commit", author: "dockerapi", pause: false )
74
+ image.commit(container: container, repo: "my/image", tag: "latest", comment: "Comment from commit", author: "dockerapi", pause: false )
71
75
 
72
76
  # Build image from a local tar file
73
- Docker::API::Image.build("/path/to/file.tar")
77
+ image.build("/path/to/file.tar")
74
78
 
75
79
  # Build image from a remote tar file
76
- Docker::API::Image.build(nil, remote: "https://url.to/file.tar")
80
+ image.build(nil, remote: "https://url.to/file.tar")
77
81
 
78
82
  # Build image from a remote Dockerfile
79
- Docker::API::Image.build(nil, remote: "https://url.to/Dockerfile")
83
+ image.build(nil, remote: "https://url.to/Dockerfile")
80
84
 
81
85
  # Delete builder cache
82
- Docker::API::Image.delete_cache
86
+ image.delete_cache
83
87
 
84
88
  # Export repo
85
- Docker::API::Image.export("repo:tag", "~/exported_image.tar")
89
+ image.export("repo:tag", "~/exported_image.tar")
86
90
 
87
91
  # Import repo
88
- Docker::API::Image.import("/path/to/file.tar")
92
+ image.import("/path/to/file.tar")
89
93
  ```
90
94
 
91
95
  ### Containers
@@ -94,62 +98,177 @@ Let's test a Nginx container
94
98
 
95
99
  ```ruby
96
100
  # Pull nginx image
97
- Docker::API::Image.create( fromImage: "nginx:latest" )
101
+ Docker::API::Image.new.create( fromImage: "nginx:latest" )
102
+
103
+ # Connect to local container endpoints
104
+ container = Docker::API::Container.new
98
105
 
99
106
  # Create container
100
- Docker::API::Container.create( {name: "nginx"}, {Image: "nginx:latest", HostConfig: {PortBindings: {"80/tcp": [ {HostIp: "0.0.0.0", HostPort: "80"} ]}}})
107
+ container.create( {name: "nginx"}, {Image: "nginx:latest", HostConfig: {PortBindings: {"80/tcp": [ {HostIp: "0.0.0.0", HostPort: "80"} ]}}})
101
108
 
102
109
  # Start container
103
- Docker::API::Container.start("nginx")
110
+ container.start("nginx")
104
111
 
105
112
  # Open localhost or machine IP to check the container running
106
113
 
107
114
  # Restart container
108
- Docker::API::Container.restart("nginx")
115
+ container.restart("nginx")
109
116
 
110
117
  # Pause/unpause container
111
- Docker::API::Container.pause("nginx")
112
- Docker::API::Container.unpause("nginx")
118
+ container.pause("nginx")
119
+ container.unpause("nginx")
113
120
 
114
121
  # List containers
115
- Docker::API::Container::list
122
+ container.list
116
123
 
117
124
  # List containers (including stopped ones)
118
- Docker::API::Container::list(all: true)
125
+ container.list(all: true)
119
126
 
120
127
  # Inspect container
121
- Docker::API::Container.inspect("nginx")
128
+ container.inspect("nginx")
122
129
 
123
130
  # View container's processes
124
- Docker::API::Container.top("nginx")
131
+ container.top("nginx")
125
132
 
126
- # Let's enhance the output
127
- JSON.parse(Docker::API::Container.top("nginx").body)
133
+ # Using json output
134
+ container.top("nginx").json
128
135
 
129
136
  # View filesystem changes
130
- Docker::API::Container.changes("nginx")
137
+ container.changes("nginx")
131
138
 
132
139
  # View filesystem logs
133
- Docker::API::Container.logs("nginx", stdout: true)
134
- Docker::API::Container.logs("nginx", stdout: true, follow: true)
140
+ container.logs("nginx", stdout: true)
141
+ container.logs("nginx", stdout: true, follow: true)
135
142
 
136
143
  # View filesystem stats
137
- Docker::API::Container.stats("nginx", stream: true)
144
+ container.stats("nginx", stream: true)
138
145
 
139
146
  # Export container
140
- Docker::API::Container.export("nginx", "~/exported_container")
147
+ container.export("nginx", "~/exported_container")
141
148
 
142
149
  # Get files from container
143
- Docker::API::Container.archive("nginx", "~/html.tar", path: "/usr/share/nginx/html/")
150
+ container.archive("nginx", "~/html.tar", path: "/usr/share/nginx/html/")
144
151
 
145
152
  # Stop container
146
- Docker::API::Container.stop("nginx")
153
+ container.stop("nginx")
147
154
 
148
155
  # Remove container
149
- Docker::API::Container.remove("nginx")
156
+ container.remove("nginx")
150
157
 
151
158
  # Remove stopped containers (prune)
152
- Docker::API::Container.prune
159
+ container.prune
160
+ ```
161
+
162
+ ### Volumes
163
+
164
+ ```ruby
165
+ # Connect to local volume endpoints
166
+ volume = Docker::API::Volume.new
167
+
168
+ # Create volume
169
+ volume.create( Name:"my-volume" )
170
+
171
+ # List volumes
172
+ volume.list
173
+
174
+ # Inspect volume
175
+ volume.inspect("my-volume")
176
+
177
+ # Remove volume
178
+ volume.remove("my-volume")
179
+
180
+ # Remove unused volumes (prune)
181
+ volume.prune
182
+ ```
183
+
184
+ ### Network
185
+
186
+ ```ruby
187
+ # Connect to local network endpoints
188
+ network = Docker::API::Network.new
189
+
190
+ # List networks
191
+ network.list
192
+
193
+ # Inspect network
194
+ network.inspect("bridge")
195
+
196
+ # Create network
197
+ network.create( Name:"my-network" )
198
+
199
+ # Remove network
200
+ network.remove("my-network")
201
+
202
+ # Remove unused network (prune)
203
+ network.prune
204
+
205
+ # Connect container to a network
206
+ network.connect( "my-network", Container: "my-container" )
207
+
208
+ # Disconnect container to a network
209
+ network.disconnect( "my-network", Container: "my-container" )
210
+ ```
211
+
212
+ ### System
213
+
214
+ ```ruby
215
+ # Connect to local system endpoints
216
+ sys = Docker::API::System.new
217
+
218
+ # Ping docker api
219
+ sys.ping
220
+
221
+ # Docker components versions
222
+ sys.version
223
+
224
+ # System info
225
+ sys.info
226
+
227
+ # System events (stream)
228
+ sys.events(until: Time.now.to_i)
229
+
230
+ # Data usage information
231
+ sys.df
232
+ ```
233
+
234
+ ### Exec
235
+
236
+ ```ruby
237
+ # Connect to local exec endpoints
238
+ exe = Docker::API::Exec.new
239
+
240
+ # Create exec instance, get generated exec ID
241
+ response = exe.create(container, AttachStdout:true, Cmd: ["ls", "-l"])
242
+ id = response.json["Id"]
243
+
244
+ # Execute the command, stream from Stdout is stored in response data
245
+ response = exe.start(id)
246
+ print response.data[:stream]
247
+
248
+ # Inspect exec instance
249
+ exe.inspect(id)
250
+ ```
251
+
252
+ ### Connection
253
+
254
+ By default Docker::API::Connection will connect to local Docker socket at `/var/run/docker.sock`. See examples below to use a different path or connect to a remote address.
255
+
256
+ ```ruby
257
+ # Setting different connections
258
+ local = Docker::API::Connection.new('unix:///', socket: "/path/to/docker.sock")
259
+ remote = Docker::API::Connection.new("http://127.0.0.1:2375") # change the IP address accordingly
260
+
261
+ # Using default /var/run/docker.sock
262
+ image_default = Docker::API::Image.new
263
+ image_default.list
264
+
265
+ # Using custom socket path
266
+ image_custom = Docker::API::Image.new(local)
267
+ image_custom.list
268
+
269
+ # Using remote address
270
+ image_remote = Docker::API::Image.new(remote)
271
+ image_remote.list
153
272
  ```
154
273
 
155
274
  ### Requests
@@ -158,7 +277,39 @@ Requests should work as described in [Docker API documentation](https://docs.doc
158
277
 
159
278
  ### Response
160
279
 
161
- All requests return a Excon::Response object.
280
+ All requests return a response class that inherits from Excon::Response. Available attribute readers and methods include: `status`, `data`, `body`, `headers`, `json`, `path`, `success?`.
281
+
282
+ ```ruby
283
+ response = Docker::API::Image.new.create(fromImage: "busybox:latest")
284
+
285
+ response
286
+ => #<Docker::API::Response:0x000055bb390b35c0 ... >
287
+
288
+ response.status
289
+ => 200
290
+
291
+ response.data
292
+ => {:body=>"...", :cookies=>[], :host=>nil, :headers=>{ ... }, :path=>"/images/create?fromImage=busybox:latest", :port=>nil, :status=>200, :status_line=>"HTTP/1.1 200 OK\r\n", :reason_phrase=>"OK"}
293
+
294
+ response.headers
295
+ => {"Api-Version"=>"1.40", "Content-Type"=>"application/json", "Docker-Experimental"=>"false", "Ostype"=>"linux", "Server"=>"Docker/19.03.11 (linux)", "Date"=>"Mon, 29 Jun 2020 16:10:06 GMT"}
296
+
297
+ response.body
298
+ => "{\"status\":\"Pulling from library/busybox\" ... "
299
+
300
+ response.json
301
+ => [{:status=>"Pulling from library/busybox", :id=>"latest"}, {:status=>"Pulling fs layer", :progressDetail=>{}, :id=>"76df9210b28c"}, ... , {:status=>"Status: Downloaded newer image for busybox:latest"}]
302
+
303
+ response.path
304
+ => "/images/create?fromImage=busybox:latest"
305
+
306
+ response.success?
307
+ => true
308
+ ```
309
+
310
+ ### Error handling
311
+
312
+ `Docker::API::InvalidParameter` and `Docker::API::InvalidRequestBody` may be raised when an invalid option is passed as argument (ie: an option not described in Docker API documentation for request query parameters nor request body (json) parameters). Even if no errors were raised, consider validating the status code and/or message of the response to check if the Docker daemon has fulfilled the operation properly.
162
313
 
163
314
  ## Development
164
315
 
@@ -177,10 +328,10 @@ WIP: Work In Progress
177
328
  |---|---|---|---|
178
329
  | Container | Ok | Ok | NS |
179
330
  | Image | Ok | Ok | NS |
180
- | Volume | NS | NS | NS |
181
- | Network | NS | NS | NS |
182
- | System | NS | NS | NS |
183
- | Exec | NS | NS | NS |
331
+ | Volume | Ok | Ok | NS |
332
+ | Network | Ok | Ok | NS |
333
+ | System | Ok | Ok | NS |
334
+ | Exec | Ok | Ok | NS |
184
335
  | Swarm | NS | NS | NS |
185
336
  | Node | NS | NS | NS |
186
337
  | Service | NS | NS | NS |
@@ -188,8 +339,8 @@ WIP: Work In Progress
188
339
  | Secret | NS | NS | NS |
189
340
 
190
341
  Misc:
191
- * Improve response object
192
- * Improve error objects
342
+ * ~~Improve response object~~
343
+ * ~~Improve error objects~~
193
344
 
194
345
  ## Contributing
195
346
 
data/bin/setup CHANGED
@@ -1,8 +1,22 @@
1
1
  #!/usr/bin/env bash
2
2
  set -euo pipefail
3
3
  IFS=$'\n\t'
4
- set -vx
5
-
6
- bundle install
4
+ #set -vx
7
5
 
8
6
  # Do any other automated setup that you need to do here
7
+ if [ $USER == 'root' ]
8
+ then
9
+ echo "Enabling TCP port 2375 for external connection to Docker"
10
+ echo '{"hosts": ["tcp://0.0.0.0:2375", "unix:///var/run/docker.sock"]}' > /etc/docker/daemon.json
11
+ mkdir -p /etc/systemd/system/docker.service.d
12
+ echo '[Service]' > /etc/systemd/system/docker.service.d/override.conf
13
+ echo 'ExecStart=' >> /etc/systemd/system/docker.service.d/override.conf
14
+ echo 'ExecStart=/usr/bin/dockerd' >> /etc/systemd/system/docker.service.d/override.conf
15
+ systemctl daemon-reload
16
+ systemctl restart docker.service
17
+ echo "Done!"
18
+ else
19
+ echo "Running bundle install"
20
+ bundle install
21
+ echo "Run this script as root for further configurations"
22
+ fi
@@ -1,32 +1,32 @@
1
1
  module Docker
2
2
  module API
3
3
  class Base
4
+
5
+ def initialize connection = nil
6
+ @connection = connection || Docker::API::Connection.new
7
+ end
4
8
 
5
9
  private
6
10
 
7
- def self.base_path
11
+ def base_path
8
12
  "/"
9
13
  end
10
14
 
11
- def self.connection
12
- Docker::API::Connection.instance
13
- end
14
-
15
- def self.validate error, permitted_keys, params
16
- not_permitted = params.keys.map(&:to_s) - permitted_keys.map(&:to_s)
17
- raise error if not_permitted.size > 0
15
+ def validate error, permitted, params
16
+ unpermitted = params.keys.map(&:to_s) - permitted.map(&:to_s)
17
+ raise error.new(permitted, unpermitted) if unpermitted.size > 0
18
18
  end
19
19
 
20
20
  ## Converts Ruby Hash into query parameters
21
21
  ## In general, the format is key=value
22
22
  ## If value is another Hash, it should keep a json syntax {key:value}
23
- def self.hash_to_params h
23
+ def hash_to_params h
24
24
  p = []
25
25
  h.each { |k,v| p.push( v.is_a?(Hash) ? "#{k}=#{v.to_json}" : "#{k}=#{v}") }
26
26
  p.join("&").gsub(" ","")
27
27
  end
28
28
 
29
- def self.build_path path, params = {}
29
+ def build_path path, params = {}
30
30
  p = path.is_a?(Array) ? ([base_path] << path).join("/") : path
31
31
  params.size > 0 ? [p, hash_to_params(params)].join("?") : p
32
32
  end
@@ -1,26 +1,18 @@
1
- require "excon"
2
- require "singleton"
3
- require "json"
4
1
  module Docker
5
2
  module API
6
3
  class Connection
7
- include Singleton
8
-
9
4
  [:get, :post, :head, :delete, :put].each do | method |
10
5
  define_method(method) { | path | self.request(method: method, path: path) }
11
6
  end
12
7
 
13
- def post(path, body = nil)
14
- self.request(method: :post, path: path, headers: { "Content-Type" => "application/json" }, body: body.to_json)
15
- end
16
-
17
8
  def request params
18
- @connection.request(params)
9
+ Docker::API::Response.new(@connection.request(params).data)
19
10
  end
20
11
 
21
- private
22
- def initialize
23
- @connection = Excon.new('unix:///', :socket => '/var/run/docker.sock')
12
+ def initialize url = nil, params = nil
13
+ url ||= 'unix:///'
14
+ params ||= url == 'unix:///' ? {socket: '/var/run/docker.sock'} : {}
15
+ @connection = Excon.new(url, params)
24
16
  end
25
17
 
26
18
  end
@@ -5,111 +5,111 @@ module Docker
5
5
 
6
6
  class Container < Docker::API::Base
7
7
 
8
- def self.base_path
8
+ def base_path
9
9
  "/containers"
10
10
  end
11
11
 
12
- def self.list params = {}
12
+ def list params = {}
13
13
  validate Docker::API::InvalidParameter, [:all, :limit, :size, :filters], params
14
- connection.get(build_path(["json"], params))
14
+ @connection.get(build_path(["json"], params))
15
15
  end
16
16
 
17
- def self.inspect name, params = {}
17
+ def inspect name, params = {}
18
18
  validate Docker::API::InvalidParameter, [:size], params
19
- connection.get(build_path([name, "json"], params))
19
+ @connection.get(build_path([name, "json"], params))
20
20
  end
21
21
 
22
- def self.top name, params = {}
22
+ def top name, params = {}
23
23
  validate Docker::API::InvalidParameter, [:ps_args], params
24
- connection.get(build_path([name, "top"], params))
24
+ @connection.get(build_path([name, "top"], params))
25
25
  end
26
26
 
27
- def self.changes name
28
- connection.get(build_path([name, "changes"]))
27
+ def changes name
28
+ @connection.get(build_path([name, "changes"]))
29
29
  end
30
30
 
31
- def self.start name, params = {}
31
+ def start name, params = {}
32
32
  validate Docker::API::InvalidParameter, [:detachKeys], params
33
- connection.post(build_path([name, "start"], params))
33
+ @connection.post(build_path([name, "start"], params))
34
34
  end
35
35
 
36
- def self.stop name, params = {}
36
+ def stop name, params = {}
37
37
  validate Docker::API::InvalidParameter, [:t], params
38
- connection.post(build_path([name, "stop"], params))
38
+ @connection.post(build_path([name, "stop"], params))
39
39
  end
40
40
 
41
- def self.restart name, params = {}
41
+ def restart name, params = {}
42
42
  validate Docker::API::InvalidParameter, [:t], params
43
- connection.post(build_path([name, "restart"], params))
43
+ @connection.post(build_path([name, "restart"], params))
44
44
  end
45
45
 
46
- def self.kill name, params = {}
46
+ def kill name, params = {}
47
47
  validate Docker::API::InvalidParameter, [:signal], params
48
- connection.post(build_path([name, "kill"], params))
48
+ @connection.post(build_path([name, "kill"], params))
49
49
  end
50
50
 
51
- def self.wait name, params = {}
51
+ def wait name, params = {}
52
52
  validate Docker::API::InvalidParameter, [:condition], params
53
- connection.post(build_path([name, "wait"], params))
53
+ @connection.post(build_path([name, "wait"], params))
54
54
  end
55
55
 
56
- def self.update name, body = {}
56
+ def update name, body = {}
57
57
  validate Docker::API::InvalidRequestBody, Docker::API::UpdateBody, body
58
- connection.post(build_path([name, "update"]), body)
58
+ @connection.request(method: :post, path: build_path([name, "update"]), headers: {"Content-Type": "application/json"}, body: body.to_json)
59
59
  end
60
60
 
61
- def self.rename name, params = {}
61
+ def rename name, params = {}
62
62
  validate Docker::API::InvalidParameter, [:name], params
63
- connection.post(build_path([name, "rename"], params))
63
+ @connection.post(build_path([name, "rename"], params))
64
64
  end
65
65
 
66
- def self.resize name, params = {}
66
+ def resize name, params = {}
67
67
  validate Docker::API::InvalidParameter, [:w, :h], params
68
- connection.post(build_path([name, "resize"], params))
68
+ @connection.post(build_path([name, "resize"], params))
69
69
  end
70
70
 
71
- def self.prune params = {}
71
+ def prune params = {}
72
72
  validate Docker::API::InvalidParameter, [:filters], params
73
- connection.post(build_path(["prune"], params))
73
+ @connection.post(build_path(["prune"], params))
74
74
  end
75
75
 
76
- def self.pause name
77
- connection.post(build_path([name, "pause"]))
76
+ def pause name
77
+ @connection.post(build_path([name, "pause"]))
78
78
  end
79
79
 
80
- def self.unpause name
81
- connection.post(build_path([name, "unpause"]))
80
+ def unpause name
81
+ @connection.post(build_path([name, "unpause"]))
82
82
  end
83
83
 
84
- def self.remove name, params = {}
84
+ def remove name, params = {}
85
85
  validate Docker::API::InvalidParameter, [:v, :force, :link], params
86
- connection.delete(build_path([name]))
86
+ @connection.delete(build_path([name]))
87
87
  end
88
88
 
89
- def self.logs name, params = {}
89
+ def logs name, params = {}
90
90
  validate Docker::API::InvalidParameter, [:follow, :stdout, :stderr, :since, :until, :timestamps, :tail], params
91
91
 
92
92
  path = build_path([name, "logs"], params)
93
93
 
94
94
  if params[:follow] == true || params[:follow] == 1
95
- connection.request(method: :get, path: path , response_block: lambda { |chunk, remaining_bytes, total_bytes| puts chunk.inspect })
95
+ @connection.request(method: :get, path: path , response_block: lambda { |chunk, remaining_bytes, total_bytes| puts chunk.inspect })
96
96
  else
97
- connection.get(path)
97
+ @connection.get(path)
98
98
  end
99
99
  end
100
100
 
101
- def self.attach name, params = {}
101
+ def attach name, params = {}
102
102
  validate Docker::API::InvalidParameter, [:detachKeys, :logs, :stream, :stdin, :stdout, :stderr], params
103
- connection.request(method: :post, path: build_path([name, "attach"], params) , response_block: lambda { |chunk, remaining_bytes, total_bytes| puts chunk.inspect })
103
+ @connection.request(method: :post, path: build_path([name, "attach"], params) , response_block: lambda { |chunk, remaining_bytes, total_bytes| puts chunk.inspect })
104
104
  end
105
105
 
106
- def self.create params = {}, body = {}
106
+ def create params = {}, body = {}
107
107
  validate Docker::API::InvalidParameter, [:name], params
108
108
  validate Docker::API::InvalidRequestBody, Docker::API::CreateBody, body
109
- connection.post(build_path(["create"], params), body)
109
+ @connection.request(method: :post, path: build_path(["create"], params), headers: {"Content-Type": "application/json"}, body: body.to_json)
110
110
  end
111
111
 
112
- def self.stats name, params = {}
112
+ def stats name, params = {}
113
113
  validate Docker::API::InvalidParameter, [:stream], params
114
114
  path = build_path([name, "stats"], params)
115
115
 
@@ -117,37 +117,37 @@ module Docker
117
117
  streamer = lambda do |chunk, remaining_bytes, total_bytes|
118
118
  puts chunk
119
119
  end
120
- connection.request(method: :get, path: path , response_block: streamer)
120
+ @connection.request(method: :get, path: path , response_block: streamer)
121
121
  else
122
- connection.get(path)
122
+ @connection.get(path)
123
123
  end
124
124
  end
125
125
 
126
- def self.export name, path = "exported_container"
127
- response = Docker::API::Container.inspect(name)
126
+ def export name, path = "exported_container"
127
+ response = self.inspect(name)
128
128
  if response.status == 200
129
129
  file = File.open(File.expand_path(path), "wb")
130
130
  streamer = lambda do |chunk, remaining_bytes, total_bytes|
131
131
  file.write(chunk)
132
132
  end
133
- response = connection.request(method: :get, path: build_path([name, "export"]) , response_block: streamer)
133
+ response = @connection.request(method: :get, path: build_path([name, "export"]) , response_block: streamer)
134
134
  file.close
135
135
  end
136
136
  response
137
137
  end
138
138
 
139
- def self.archive name, file, params = {}
139
+ def archive name, file, params = {}
140
140
  validate Docker::API::InvalidParameter, [:path, :noOverwriteDirNonDir, :copyUIDGID], params
141
141
 
142
142
  begin # File exists on disk, send it to container
143
143
  file = File.open( File.expand_path( file ) , "r")
144
- response = connection.request(method: :put, path: build_path([name, "archive"], params) , request_block: lambda { file.read(Excon.defaults[:chunk_size]).to_s} )
144
+ response = @connection.request(method: :put, path: build_path([name, "archive"], params) , request_block: lambda { file.read(Excon.defaults[:chunk_size]).to_s} )
145
145
  file.close
146
146
  rescue Errno::ENOENT # File doesnt exist, get it from container
147
- response = connection.head(build_path([name, "archive"], params))
147
+ response = @connection.head(build_path([name, "archive"], params))
148
148
  if response.status == 200 # file exists in container
149
149
  file = File.open( File.expand_path( file ) , "wb")
150
- response = connection.request(method: :get, path: build_path([name, "archive"], params) , response_block: lambda { |chunk, remaining_bytes, total_bytes| file.write(chunk) })
150
+ response = @connection.request(method: :get, path: build_path([name, "archive"], params) , response_block: lambda { |chunk, remaining_bytes, total_bytes| file.write(chunk) })
151
151
  file.close
152
152
  end
153
153
  end
@@ -1,6 +1,17 @@
1
1
  module Docker
2
2
  module API
3
- class InvalidParameter < StandardError; end
4
- class InvalidRequestBody < StandardError; end
3
+ class ValidationError < StandardError
4
+ def initialize permitted, unpermitted
5
+ super("Unpermitted options found: #{unpermitted.to_s}. Permitted are #{permitted.to_s}")
6
+ end
7
+ end
8
+ class Error < StandardError
9
+ def initialize msg = "Error without specific message"
10
+ super(msg)
11
+ end
12
+ end
13
+
14
+ class InvalidParameter < Docker::API::ValidationError; end
15
+ class InvalidRequestBody < Docker::API::ValidationError; end
5
16
  end
6
17
  end
@@ -0,0 +1,39 @@
1
+ module Docker
2
+ module API
3
+ class Exec < Docker::API::Base
4
+
5
+ def base_path
6
+ "/exec"
7
+ end
8
+
9
+ def create name, body = {}
10
+ validate Docker::API::InvalidRequestBody, [:AttachStdin, :AttachStdout, :AttachStderr, :DetachKeys, :Tty, :Env, :Cmd, :Privileged, :User, :WorkingDir], body
11
+ @connection.request(method: :post, path: "/containers/#{name}/exec", headers: {"Content-Type": "application/json"}, body: body.to_json )
12
+ end
13
+
14
+ def start name, body = {}
15
+ validate Docker::API::InvalidRequestBody, [:Detach, :Tty], body
16
+
17
+ stream = ""
18
+ response = @connection.request(method: :post,
19
+ path: "/exec/#{name}/start",
20
+ headers: {"Content-Type": "application/json"},
21
+ body: body.to_json,
22
+ response_block: lambda { |chunk, remaining_bytes, total_bytes| stream += chunk.to_s.encode('UTF-8', invalid: :replace, undef: :replace, replace: '?') }
23
+ )
24
+ response.data.merge!({stream: stream})
25
+ response
26
+ end
27
+
28
+ def resize name, params = {}
29
+ validate Docker::API::InvalidParameter, [:w, :h], params
30
+ @connection.post(build_path([name, "resize"], params))
31
+ end
32
+
33
+ def inspect name
34
+ @connection.get(build_path([name, "json"]))
35
+ end
36
+
37
+ end
38
+ end
39
+ end
@@ -7,113 +7,111 @@ module Docker
7
7
  BuildParams = [:dockerfile, :t, :extrahosts, :remote, :q, :nocache, :cachefrom, :pull, :rm, :forcerm, :memory, :memswap, :cpushares, :cpusetcpus, :cpuperiod, :cpuquota, :buildargs, :shmsize, :squash, :labels, :networkmode, :platform, :target, :outputs]
8
8
  class Image < Docker::API::Base
9
9
 
10
- def self.base_path
10
+ def base_path
11
11
  "/images"
12
12
  end
13
13
 
14
- def self.inspect name
15
- connection.get(build_path([name, "json"]))
14
+ def inspect name
15
+ @connection.get(build_path([name, "json"]))
16
16
  end
17
17
 
18
- def self.history name
19
- connection.get(build_path([name, "history"]))
18
+ def history name
19
+ @connection.get(build_path([name, "history"]))
20
20
  end
21
21
 
22
- def self.list params = {}
22
+ def list params = {}
23
23
  validate Docker::API::InvalidParameter, [:all, :filters, :digests], params
24
- connection.get(build_path(["json"], params))
24
+ @connection.get(build_path(["json"], params))
25
25
  end
26
26
 
27
- def self.search params = {}
27
+ def search params = {}
28
28
  validate Docker::API::InvalidParameter, [:term, :limit, :filters], params
29
- connection.get(build_path(["search"], params))
29
+ @connection.get(build_path(["search"], params))
30
30
  end
31
31
 
32
- def self.tag name, params = {}
32
+ def tag name, params = {}
33
33
  validate Docker::API::InvalidParameter, [:repo, :tag], params
34
- connection.post(build_path([name, "tag"], params))
34
+ @connection.post(build_path([name, "tag"], params))
35
35
  end
36
36
 
37
- def self.prune params = {}
37
+ def prune params = {}
38
38
  validate Docker::API::InvalidParameter, [:filters], params
39
- connection.post(build_path(["prune"], params))
39
+ @connection.post(build_path(["prune"], params))
40
40
  end
41
41
 
42
- def self.remove name, params = {}
42
+ def remove name, params = {}
43
43
  validate Docker::API::InvalidParameter, [:force, :noprune], params
44
- connection.delete(build_path([name], params))
44
+ @connection.delete(build_path([name], params))
45
45
  end
46
46
 
47
- def self.export name, path = "exported_image"
47
+ def export name, path = "exported_image"
48
48
  file = File.open("/tmp/exported-image", "wb")
49
49
  streamer = lambda do |chunk, remaining_bytes, total_bytes|
50
50
  file.write(chunk)
51
51
  end
52
- response = connection.request(method: :get, path: build_path([name, "get"]) , response_block: streamer)
52
+ response = @connection.request(method: :get, path: build_path([name, "get"]) , response_block: streamer)
53
53
  file.close
54
54
  response.status == 200 ? FileUtils.mv("/tmp/exported-image", File.expand_path(path)) : FileUtils.rm("/tmp/exported-image")
55
55
  response
56
56
  end
57
57
 
58
- def self.import path, params = {}
58
+ def import path, params = {}
59
59
  validate Docker::API::InvalidParameter, [:quiet], params
60
60
  file = File.open(File.expand_path(path), "r")
61
- response = connection.request(method: :post, path: build_path(["load"], params) , headers: {"Content-Type" => "application/x-tar"}, request_block: lambda { file.read(Excon.defaults[:chunk_size]).to_s} )
61
+ response = @connection.request(method: :post, path: build_path(["load"], params) , headers: {"Content-Type" => "application/x-tar"}, request_block: lambda { file.read(Excon.defaults[:chunk_size]).to_s} )
62
62
  file.close
63
63
  response
64
64
  end
65
65
 
66
- def self.push name, params = {}, authentication = {}
66
+ def push name, params = {}, authentication = {}
67
67
  validate Docker::API::InvalidParameter, [:tag], params
68
68
 
69
69
  if authentication.keys.size > 0
70
- auth = Docker::API::System.auth(authentication)
71
- return auth unless [200, 204].include? auth.status
72
- connection.request(method: :post, path: build_path([name, "push"], params), headers: { "X-Registry-Auth" => Base64.encode64(authentication.to_json.to_s).chomp } )
70
+ @connection.request(method: :post, path: build_path([name, "push"], params), headers: { "X-Registry-Auth" => Base64.urlsafe_encode64(authentication.to_json.to_s).chomp } )
73
71
  else
74
- connection.post(build_path([name, "push"], params))
72
+ raise Docker::API::Error.new("Provide authentication parameters to push an image")
75
73
  end
76
74
  end
77
75
 
78
- def self.commit params = {}, body = {}
76
+ def commit params = {}, body = {}
79
77
  validate Docker::API::InvalidParameter, [:container, :repo, :tag, :comment, :author, :pause, :changes], params
80
78
  validate Docker::API::InvalidRequestBody, Docker::API::CommitBody, body
81
- container = Docker::API::Container.inspect(params[:container])
79
+ container = Docker::API::Container.new.inspect(params[:container])
82
80
  return container if [404, 301].include? container.status
83
81
  body = JSON.parse(container.body)["Config"].merge(body)
84
- connection.request(method: :post, path: build_path("/commit", params), headers: {"Content-Type": "application/json"}, body: body.to_json)
82
+ @connection.request(method: :post, path: build_path("/commit", params), headers: {"Content-Type": "application/json"}, body: body.to_json)
85
83
  end
86
84
 
87
- def self.create params = {}, authentication = {}
85
+ def create params = {}, authentication = {}
88
86
  validate Docker::API::InvalidParameter, [:fromImage, :fromSrc, :repo, :tag, :message, :platform], params
89
87
 
90
88
  if authentication.keys.size > 0
91
- auth = Docker::API::System.auth(authentication)
89
+ auth = Docker::API::System.new.auth(authentication)
92
90
  return auth unless [200, 204].include? auth.status
93
- connection.request(method: :post, path: build_path(["create"], params), headers: { "X-Registry-Auth" => Base64.encode64(authentication.to_json.to_s).chomp } )
91
+ @connection.request(method: :post, path: build_path(["create"], params), headers: { "X-Registry-Auth" => Base64.encode64(authentication.to_json.to_s).chomp } )
94
92
  elsif params.has_key? :fromSrc
95
93
  if params[:fromSrc].match(/^(http|https)/)
96
- connection.request(method: :post, path: build_path(["create"], params))
94
+ @connection.request(method: :post, path: build_path(["create"], params))
97
95
  else
98
96
  file = File.open(File.expand_path(params[:fromSrc]), "r")
99
97
  params[:fromSrc] = "-"
100
- response = connection.request(method: :post, path: build_path(["create"], params) , headers: {"Content-Type" => "application/x-tar"}, request_block: lambda { file.read(Excon.defaults[:chunk_size]).to_s} )
98
+ response = @connection.request(method: :post, path: build_path(["create"], params) , headers: {"Content-Type" => "application/x-tar"}, request_block: lambda { file.read(Excon.defaults[:chunk_size]).to_s} )
101
99
  file.close
102
100
  response
103
101
  end
104
102
  else
105
- connection.post(build_path(["create"], params))
103
+ @connection.post(build_path(["create"], params))
106
104
  end
107
105
  end
108
106
 
109
- def self.build path, params = {}, authentication = {}
110
- raise Docker::API::InvalidRequestBody unless path || params[:remote]
107
+ def build path, params = {}, authentication = {}
108
+ raise Docker::API::Error.new("Expected path or params[:remote]") unless path || params[:remote]
111
109
  validate Docker::API::InvalidParameter, Docker::API::BuildParams, params
112
110
 
113
111
  header = {"Content-type": "application/x-tar"}
114
112
  if authentication.keys.size > 0
115
113
  authentication.each_key do |server|
116
- auth = Docker::API::System.auth({username: authentication[server][:username] ,password:authentication[server][:password], serveraddress: server})
114
+ auth = Docker::API::System.new.auth({username: authentication[server][:username] ,password:authentication[server][:password], serveraddress: server})
117
115
  return auth unless [200, 204].include? auth.status
118
116
  end
119
117
  header.merge!({"X-Registry-Config": Base64.urlsafe_encode64(authentication.to_json.to_s).chomp})
@@ -121,17 +119,17 @@ module Docker
121
119
 
122
120
  begin
123
121
  file = File.open( File.expand_path( path ) , "r")
124
- response = connection.request(method: :post, path: build_path("/build", params), headers: header, request_block: lambda { file.read(Excon.defaults[:chunk_size]).to_s})
122
+ response = @connection.request(method: :post, path: build_path("/build", params), headers: header, request_block: lambda { file.read(Excon.defaults[:chunk_size]).to_s})
125
123
  file.close
126
124
  rescue
127
- response = connection.request(method: :post, path: build_path("/build", params), headers: header)
125
+ response = @connection.request(method: :post, path: build_path("/build", params), headers: header)
128
126
  end
129
127
  response
130
128
  end
131
129
 
132
- def self.delete_cache params = {}
130
+ def delete_cache params = {}
133
131
  validate Docker::API::InvalidParameter, [:all, "keep-storage", :filters], params
134
- connection.post(build_path("/build/prune", params))
132
+ @connection.post(build_path("/build/prune", params))
135
133
  end
136
134
 
137
135
  end
@@ -0,0 +1,45 @@
1
+ module Docker
2
+ module API
3
+ class Network < Docker::API::Base
4
+
5
+ def base_path
6
+ "/networks"
7
+ end
8
+
9
+ def list params = {}
10
+ validate Docker::API::InvalidParameter, [:filters], params
11
+ @connection.get(build_path("/networks", params))
12
+ end
13
+
14
+ def inspect name, params = {}
15
+ validate Docker::API::InvalidParameter, [:verbose, :scope], params
16
+ @connection.get(build_path([name], params))
17
+ end
18
+
19
+ def create body = {}
20
+ validate Docker::API::InvalidRequestBody, [:Name, :CheckDuplicate, :Driver, :Internal, :Attachable, :Ingress, :IPAM, :EnableIPv6, :Options, :Labels], body
21
+ @connection.request(method: :post, path: build_path(["create"]), headers: {"Content-Type": "application/json"}, body: body.to_json)
22
+ end
23
+
24
+ def remove name
25
+ @connection.delete(build_path([name]))
26
+ end
27
+
28
+ def prune params = {}
29
+ validate Docker::API::InvalidParameter, [:filters], params
30
+ @connection.post(build_path(["prune"], params))
31
+ end
32
+
33
+ def connect name, body = {}
34
+ validate Docker::API::InvalidRequestBody, [:Container, :EndpointConfig], body
35
+ @connection.request(method: :post, path: build_path([name, "connect"]), headers: {"Content-Type": "application/json"}, body: body.to_json)
36
+ end
37
+
38
+ def disconnect name, body = {}
39
+ validate Docker::API::InvalidRequestBody, [:Container, :Force], body
40
+ @connection.request(method: :post, path: build_path([name, "disconnect"]), headers: {"Content-Type": "application/json"}, body: body.to_json)
41
+ end
42
+
43
+ end
44
+ end
45
+ end
@@ -0,0 +1,25 @@
1
+ module Docker
2
+ module API
3
+ class Response < Excon::Response
4
+ attr_reader(:json, :path)
5
+
6
+ def initialize data
7
+ super data
8
+ @json = parse_json @body
9
+ @path = @data[:path]
10
+ end
11
+
12
+ def success?
13
+ (200..204).include? @status
14
+ end
15
+
16
+ private
17
+
18
+ def parse_json data
19
+ return nil unless headers["Content-Type"] == "application/json"
20
+ return nil if data == ""
21
+ data.split("\r\n").size > 1 ? data.split("\r\n").map{ |e| eval(e) } : JSON.parse(data)
22
+ end
23
+ end
24
+ end
25
+ end
@@ -2,11 +2,33 @@ require "json"
2
2
  module Docker
3
3
  module API
4
4
  class System < Docker::API::Base
5
-
6
- def self.auth body = {}
5
+
6
+ def auth body = {}
7
7
  validate Docker::API::InvalidRequestBody, [:username, :password, :email, :serveraddress, :identitytoken], body
8
- connection.request(method: :post, path: "/auth", headers: { "Content-Type" => "application/json" }, body: body.to_json)
8
+ @connection.request(method: :post, path: "/auth", headers: { "Content-Type" => "application/json" }, body: body.to_json)
9
9
  end
10
+
11
+ def events params = {}
12
+ validate Docker::API::InvalidParameter, [:since, :until, :filters], params
13
+ @connection.request(method: :get, path: build_path("/events", params), response_block: lambda { |chunk, remaining_bytes, total_bytes| puts chunk.inspect } )
14
+ end
15
+
16
+ def ping
17
+ @connection.get("/_ping")
18
+ end
19
+
20
+ def info
21
+ @connection.get("/info")
22
+ end
23
+
24
+ def version
25
+ @connection.get("/version")
26
+ end
27
+
28
+ def df
29
+ @connection.get("/system/df")
30
+ end
31
+
10
32
  end
11
33
  end
12
34
  end
@@ -1,6 +1,6 @@
1
1
  module Docker
2
2
  module API
3
- GEM_VERSION = "0.2.0"
3
+ GEM_VERSION = "0.7.0"
4
4
 
5
5
  API_VERSION = "1.40"
6
6
 
@@ -0,0 +1,34 @@
1
+ module Docker
2
+ module API
3
+ class Volume < Docker::API::Base
4
+ def base_path
5
+ "/volumes"
6
+ end
7
+
8
+ def list params = {}
9
+ validate Docker::API::InvalidParameter, [:filters], params
10
+ @connection.get(build_path("/volumes", params))
11
+ end
12
+
13
+ def create body = {}
14
+ validate Docker::API::InvalidRequestBody, [:Name, :Driver, :DriverOpts, :Labels], body
15
+ @connection.request(method: :post, path: build_path(["create"]), headers: {"Content-Type": "application/json"}, body: body.to_json)
16
+ end
17
+
18
+ def inspect name
19
+ @connection.get(build_path([name]))
20
+ end
21
+
22
+ def remove name, params = {}
23
+ validate Docker::API::InvalidParameter, [:force], params
24
+ @connection.delete(build_path([name]))
25
+ end
26
+
27
+ def prune params = {}
28
+ validate Docker::API::InvalidParameter, [:filters], params
29
+ @connection.post(build_path(["prune"], params))
30
+ end
31
+
32
+ end
33
+ end
34
+ end
@@ -1,10 +1,16 @@
1
- require "docker/api/version"
1
+ require "excon"
2
+ require "json"
2
3
 
4
+ require "docker/api/version"
3
5
  require "docker/api/error"
4
6
  require "docker/api/connection"
7
+ require "docker/api/response"
5
8
  require "docker/api/base"
6
9
  require "docker/api/container"
7
10
  require "docker/api/image"
11
+ require "docker/api/volume"
12
+ require "docker/api/network"
13
+ require "docker/api/exec"
8
14
  require "docker/api/system"
9
15
 
10
16
  module Docker
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: dockerapi
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.2.0
4
+ version: 0.7.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Alysson A. Costa
8
8
  autorequire:
9
9
  bindir: exe
10
10
  cert_chain: []
11
- date: 2020-06-25 00:00:00.000000000 Z
11
+ date: 2020-07-05 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: excon
@@ -47,9 +47,13 @@ files:
47
47
  - lib/docker/api/connection.rb
48
48
  - lib/docker/api/container.rb
49
49
  - lib/docker/api/error.rb
50
+ - lib/docker/api/exec.rb
50
51
  - lib/docker/api/image.rb
52
+ - lib/docker/api/network.rb
53
+ - lib/docker/api/response.rb
51
54
  - lib/docker/api/system.rb
52
55
  - lib/docker/api/version.rb
56
+ - lib/docker/api/volume.rb
53
57
  - lib/dockerapi.rb
54
58
  homepage: https://github.com/nu12/dockerapi
55
59
  licenses: