dockerapi 0.1.0 → 0.6.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: 39b737a07cdf35864b8be1402ca7d504c59fc854e2092762df7a9f448bb2bf24
4
- data.tar.gz: d71d1894cab1f61a3713f0991d24c272f99373832bff140c962604a8d707621f
3
+ metadata.gz: 90acd383975c4846ca2d6bfb91aa15d9898ce0101557ca50481f20729871ef73
4
+ data.tar.gz: 03d01594692f5c2093bf3e91b4f67635d2bd8e1bf0360217747ea4aa8d2c6840
5
5
  SHA512:
6
- metadata.gz: 3622c339bae6e7a93738d110a5d6eaa38dda6288abeebda35c4b376cd354b33fa2b1595bf53fc2da04cf8a5e6f8e78a49f7cea10927afdb3fc719f119d94a8fd
7
- data.tar.gz: d40f00a1e7c913a5b005cba33182e2295da22d1631eb0fec23101d45639998391ad89829a03a51b3c197da8f69196c954fb8e70234f90175f2b7bbc410159be1
6
+ metadata.gz: 56a36a3c8b73fc0a8ae02cc68bd93b6b858be21d8c9c6d033b5f1d34d581f646dbb9e77df2e0a9d216c2a835eec3bd8e1abb62ade82aa6ec818ef081b5181d5d
7
+ data.tar.gz: 101c74b35333664dc5573d5443f2c7d75d5c8de9a43a2f70a7a54e5bb5673091eebe31dfc9ed5845c1052f47907dc6d7873f3bbe84a5015e10f5411458993cb9
@@ -0,0 +1,93 @@
1
+ # 0.6.0
2
+
3
+ Add connection parameters specifications with connect_to in Docker::API::Connection.
4
+
5
+ Add Docker::API::Exec methods:
6
+ * create
7
+ * start
8
+ * resize
9
+ * inspect
10
+
11
+ # 0.5.0
12
+
13
+ Add Docker::API::System methods:
14
+ * auth
15
+ * ping
16
+ * info
17
+ * version
18
+ * events
19
+ * df
20
+
21
+ Add new response class Docker::API::Response with the following methods:
22
+ * json
23
+ * path
24
+ * success?
25
+
26
+ Error classes output better error messages.
27
+
28
+ # 0.4.0
29
+
30
+ Add Docker::API::Network methods:
31
+ * list
32
+ * inspect
33
+ * create
34
+ * remove
35
+ * prune
36
+ * connect
37
+ * disconnect
38
+
39
+ # 0.3.0
40
+
41
+ Add Docker::API::Volume methods:
42
+ * list
43
+ * inspect
44
+ * create
45
+ * remove
46
+ * prune
47
+
48
+
49
+ # 0.2.0
50
+
51
+ Add Docker::API::Image methods:
52
+ * inspect
53
+ * history
54
+ * list
55
+ * search
56
+ * tag
57
+ * prune
58
+ * remove
59
+ * export
60
+ * import
61
+ * push
62
+ * commit
63
+ * create
64
+ * build
65
+ * delete_cache
66
+
67
+ Add Docker::API::System.auth (untested) for basic authentication
68
+
69
+ # 0.1.0
70
+
71
+ Add Docker::API::Container methods:
72
+ * list
73
+ * inspect
74
+ * top
75
+ * changes
76
+ * start
77
+ * stop
78
+ * restart
79
+ * kill
80
+ * wait
81
+ * update
82
+ * rename
83
+ * resize
84
+ * prune
85
+ * pause
86
+ * unpause
87
+ * remove
88
+ * logs
89
+ * attach
90
+ * create
91
+ * stats
92
+ * export
93
+ * archive
@@ -1,8 +1,8 @@
1
1
  PATH
2
2
  remote: .
3
3
  specs:
4
- dockerapi (0.1.0)
5
- excon
4
+ dockerapi (0.6.0)
5
+ excon (~> 0.74.0)
6
6
 
7
7
  GEM
8
8
  remote: https://rubygems.org/
data/README.md CHANGED
@@ -20,6 +20,75 @@ Or install it yourself as:
20
20
 
21
21
  ## Usage
22
22
 
23
+ ### Images
24
+
25
+ ```ruby
26
+ # Pull from a public repository
27
+ Docker::API::Image.create( fromImage: "nginx:latest" )
28
+
29
+ # Pull from a private repository
30
+ Docker::API::Image.create( {fromImage: "private/repo:tag"}, {username: "janedoe", password: "password"} )
31
+
32
+ # Create image from local tar file
33
+ Docker::API::Image.create( fromSrc: "/path/to/file.tar", repo: "repo:tag" )
34
+
35
+ # Create image from remote tar file
36
+ Docker::API::Image.create( fromSrc: "https://url.to/file.tar", repo: "repo:tag" )
37
+
38
+ # List images
39
+ Docker::API::Image.list
40
+
41
+ # Inspect image
42
+ Docker::API::Image.inspect("image")
43
+
44
+ # History
45
+ Docker::API::Image.history("image")
46
+
47
+ # Search image
48
+ Docker::API::Image.search(term: "busybox", limit: 2)
49
+ Docker::API::Image.search(term: "busybox", filters: {"is-automated": {"true": true}})
50
+ Docker::API::Image.search(term: "busybox", filters: {"is-official": {"true": true}})
51
+
52
+ # Tag image
53
+ Docker::API::Image.tag("current:tag", repo: "new:tag") # or
54
+ Docker::API::Image.tag("current:tag", repo: "new", tag: "tag")
55
+
56
+ # Push image
57
+ Docker::API::Image.push("repo:tag") # to dockerhub
58
+ Docker::API::Image.push("localhost:5000/repo:tag") # to local registry
59
+ Docker::API::Image.push("private/repo", {tag: "tag"}, {username: "janedoe", password: "password"} # to private repository
60
+
61
+ # Remove image
62
+ Docker::API::Image.remove("image")
63
+ Docker::API::Image.remove("image", force: true)
64
+
65
+ # Remove unsued images (prune)
66
+ Docker::API::Image.prune(filters: {dangling: {"false": true}})
67
+
68
+ # Create image from a container (commit)
69
+ Docker::API::Image.commit(container: container, repo: "my/image", tag: "latest", comment: "Comment from commit", author: "dockerapi", pause: false )
70
+
71
+ # Build image from a local tar file
72
+ Docker::API::Image.build("/path/to/file.tar")
73
+
74
+ # Build image from a remote tar file
75
+ Docker::API::Image.build(nil, remote: "https://url.to/file.tar")
76
+
77
+ # Build image from a remote Dockerfile
78
+ Docker::API::Image.build(nil, remote: "https://url.to/Dockerfile")
79
+
80
+ # Delete builder cache
81
+ Docker::API::Image.delete_cache
82
+
83
+ # Export repo
84
+ Docker::API::Image.export("repo:tag", "~/exported_image.tar")
85
+
86
+ # Import repo
87
+ Docker::API::Image.import("/path/to/file.tar")
88
+ ```
89
+
90
+ ### Containers
91
+
23
92
  Let's test a Nginx container
24
93
 
25
94
  ```ruby
@@ -82,13 +151,123 @@ Docker::API::Container.remove("nginx")
82
151
  Docker::API::Container.prune
83
152
  ```
84
153
 
154
+ ### Volumes
155
+
156
+ ```ruby
157
+ # Create volume
158
+ Docker::API::Volume.create( Name:"my-volume" )
159
+
160
+ # List volumes
161
+ Docker::API::Volume.list
162
+
163
+ # Inspect volume
164
+ Docker::API::Volume.inspect("my-volume")
165
+
166
+ # Remove volume
167
+ Docker::API::Volume.remove("my-volume")
168
+
169
+ # Remove unused volumes (prune)
170
+ Docker::API::Volume.prune
171
+ ```
172
+
173
+ ### Network
174
+
175
+ ```ruby
176
+ # List networks
177
+ Docker::API::Network.list
178
+
179
+ # Inspect network
180
+ Docker::API::Network.inspect("bridge")
181
+
182
+ # Create network
183
+ Docker::API::Network.create( Name:"my-network" )
184
+
185
+ # Remove network
186
+ Docker::API::Network.remove("my-network")
187
+
188
+ # Remove unused network (prune)
189
+ Docker::API::Network.prune
190
+
191
+ # Connect container to a network
192
+ Docker::API::Network.connect( "my-network", Container: "my-container" )
193
+
194
+ # Disconnect container to a network
195
+ Docker::API::Network.disconnect( "my-network", Container: "my-container" )
196
+ ```
197
+
198
+ ### System
199
+
200
+ ```ruby
201
+ # Ping docker api
202
+ Docker::API::System.ping
203
+
204
+ # Docker components versions
205
+ Docker::API::System.version
206
+
207
+ # System info
208
+ Docker::API::System.info
209
+
210
+ # System events (stream)
211
+ Docker::API::System.events(until: Time.now.to_i)
212
+
213
+ # Data usage information
214
+ Docker::API::System.df
215
+ ```
216
+
217
+ ### Exec
218
+
219
+ ```ruby
220
+ # Create exec instance, get generated exec ID
221
+ response = Docker::API::Exec.create(container, AttachStdout:true, Cmd: ["ls", "-l"])
222
+ id = response.json["Id"]
223
+
224
+ # Execute the command, stream from Stdout is stored in response data
225
+ response = Docker::API::Exec.start(id)
226
+ print response.data[:stream]
227
+
228
+ # Inspect exec instance
229
+ Docker::API::Exec.inspect(id)
230
+ ```
231
+
85
232
  ### Requests
86
233
 
87
- Requests should work as described in Docker API documentation.
234
+ Requests should work as described in [Docker API documentation](https://docs.docker.com/engine/api/v1.40). Check it out to customize your requests.
88
235
 
89
236
  ### Response
90
237
 
91
- All requests return a Excon::Response object.
238
+ All requests return a response class that inherits from Excon::Response. Available attribute readers and methods include: `status`, `data`, `body`, `headers`, `json`, `path`, `success?`.
239
+
240
+ ```ruby
241
+ response = Docker::API::Image.create(fromImage: "busybox:latest")
242
+
243
+ response
244
+ => #<Docker::API::Response:0x000055bb390b35c0 ... >
245
+
246
+ response.status
247
+ => 200
248
+
249
+ response.data
250
+ => {: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"}
251
+
252
+ response.headers
253
+ => {"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"}
254
+
255
+ response.body
256
+ => "{\"status\":\"Pulling from library/busybox\" ... "
257
+
258
+ response.json
259
+ => [{:status=>"Pulling from library/busybox", :id=>"latest"}, {:status=>"Pulling fs layer", :progressDetail=>{}, :id=>"76df9210b28c"}, ... , {:status=>"Status: Downloaded newer image for busybox:latest"}]
260
+
261
+ response.path
262
+ => "/images/create?fromImage=busybox:latest"
263
+
264
+ response.success?
265
+ => true
266
+ ```
267
+
268
+ ### Error handling
269
+
270
+ `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.
92
271
 
93
272
  ## Development
94
273
 
@@ -105,12 +284,12 @@ WIP: Work In Progress
105
284
 
106
285
  | Class | Tests | Implementation | Refactoring |
107
286
  |---|---|---|---|
108
- | Container | WIP | WIP | NS |
109
- | Image | NS | NS | NS |
110
- | Volume | NS | NS | NS |
111
- | Network | NS | NS | NS |
112
- | System | NS | NS | NS |
113
- | Exec | NS | NS | NS |
287
+ | Container | Ok | Ok | NS |
288
+ | Image | Ok | Ok | NS |
289
+ | Volume | Ok | Ok | NS |
290
+ | Network | Ok | Ok | NS |
291
+ | System | Ok | Ok | NS |
292
+ | Exec | Ok | Ok | NS |
114
293
  | Swarm | NS | NS | NS |
115
294
  | Node | NS | NS | NS |
116
295
  | Service | NS | NS | NS |
@@ -118,8 +297,8 @@ WIP: Work In Progress
118
297
  | Secret | NS | NS | NS |
119
298
 
120
299
  Misc:
121
- * Improve response object
122
- * Improve error objects
300
+ * ~~Improve response object~~
301
+ * ~~Improve error objects~~
123
302
 
124
303
  ## Contributing
125
304
 
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
@@ -14,16 +14,16 @@ Gem::Specification.new do |spec|
14
14
 
15
15
  spec.metadata["homepage_uri"] = spec.homepage
16
16
  spec.metadata["source_code_uri"] = "https://github.com/nu12/dockerapi.git"
17
- #spec.metadata["changelog_uri"] = "TODO: Put your gem's CHANGELOG.md URL here."
17
+ spec.metadata["changelog_uri"] = "https://github.com/nu12/dockerapi/blob/master/CHANGELOG.md"
18
18
 
19
19
  # Specify which files should be added to the gem when it is released.
20
20
  # The `git ls-files -z` loads the files in the RubyGem that have been added into git.
21
21
  spec.files = Dir.chdir(File.expand_path('..', __FILE__)) do
22
- `git ls-files -z`.split("\x0").reject { |f| f.match(%r{^(test|spec|features)/}) }
22
+ `git ls-files -z`.split("\x0").reject { |f| f.match(%r{^(test|spec|features|resources)/}) }
23
23
  end
24
24
  spec.bindir = "exe"
25
25
  spec.executables = spec.files.grep(%r{^exe/}) { |f| File.basename(f) }
26
26
  spec.require_paths = ["lib"]
27
27
 
28
- spec.add_dependency "excon"
28
+ spec.add_dependency("excon", "~> 0.74.0")
29
29
  end
@@ -0,0 +1,36 @@
1
+ module Docker
2
+ module API
3
+ class Base
4
+
5
+ private
6
+
7
+ def self.base_path
8
+ "/"
9
+ end
10
+
11
+ def self.connection
12
+ Docker::API::Connection.instance
13
+ end
14
+
15
+ def self.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
+ end
19
+
20
+ ## Converts Ruby Hash into query parameters
21
+ ## In general, the format is key=value
22
+ ## If value is another Hash, it should keep a json syntax {key:value}
23
+ def self.hash_to_params h
24
+ p = []
25
+ h.each { |k,v| p.push( v.is_a?(Hash) ? "#{k}=#{v.to_json}" : "#{k}=#{v}") }
26
+ p.join("&").gsub(" ","")
27
+ end
28
+
29
+ def self.build_path path, params = {}
30
+ p = path.is_a?(Array) ? ([base_path] << path).join("/") : path
31
+ params.size > 0 ? [p, hash_to_params(params)].join("?") : p
32
+ end
33
+
34
+ end
35
+ end
36
+ end
@@ -1,35 +1,23 @@
1
- require "excon"
2
1
  require "singleton"
3
- require "json"
4
2
  module Docker
5
3
  module API
6
4
  class Connection
7
5
  include Singleton
8
6
 
9
- attr_reader(:connection)
10
-
11
- def get path
12
- r = @connection.get(path: path)
13
- #p r
14
- r
7
+ [:get, :post, :head, :delete, :put].each do | method |
8
+ define_method(method) { | path | self.request(method: method, path: path) }
15
9
  end
16
10
 
17
11
  def post(path, body = nil)
18
- if body
19
- r = @connection.post(path: path, headers: { "Content-Type" => "application/json" }, body: body.to_json)
20
- else
21
- r = @connection.post(path: path)
22
- end
23
- #p r
24
- r
12
+ self.request(method: :post, path: path, headers: { "Content-Type" => "application/json" }, body: body.to_json)
25
13
  end
26
14
 
27
- def head(path)
28
- @connection.head(path: path)
15
+ def request params
16
+ Docker::API::Response.new(@connection.request(params).data)
29
17
  end
30
18
 
31
- def delete path
32
- @connection.delete(path: path)
19
+ def connect_to params
20
+ @connection = Excon.new(params)
33
21
  end
34
22
 
35
23
  private
@@ -3,121 +3,123 @@ module Docker
3
3
  CreateBody = [:Hostname,:Domainname,:User,:AttachStdin,:AttachStdout,:AttachStderr,:ExposedPorts,:Tty,:OpenStdin,:StdinOnce,:Env,:Cmd,:HealthCheck,:ArgsEscaped,:Image,:Volumes,:WorkingDir,:Entrypoint,:NetworkDisabled,:MacAddress,:OnBuild,:Labels,:StopSignal,:StopTimeout,:Shell,:HostConfig,:NetworkingConfig]
4
4
  UpdateBody = [:CpuShares, :Memory, :CgroupParent, :BlkioWeight, :BlkioWeightDevice, :BlkioWeightReadBps, :BlkioWeightWriteBps, :BlkioWeightReadOps, :BlkioWeightWriteOps, :CpuPeriod, :CpuQuota, :CpuRealtimePeriod, :CpuRealtimeRuntime, :CpusetCpus, :CpusetMems, :Devices, :DeviceCgroupRules, :DeviceRequest, :Kernel, :Memory, :KernelMemoryTCP, :MemoryReservation, :MemorySwap, :MemorySwappiness, :NanoCPUs, :OomKillDisable, :Init, :PidsLimit, :ULimits, :CpuCount, :CpuPercent, :IOMaximumIOps, :IOMaximumBandwidth, :RestartPolicy]
5
5
 
6
- class Container
6
+ class Container < Docker::API::Base
7
7
 
8
- @@base_path = "/containers"
8
+ def self.base_path
9
+ "/containers"
10
+ end
9
11
 
10
12
  def self.list params = {}
11
- self.validate Docker::API::InvalidParameter, [:all, :limit, :size, :filters], params
12
- Docker::API::Connection.instance.get(self.build_path(["json"], params))
13
+ validate Docker::API::InvalidParameter, [:all, :limit, :size, :filters], params
14
+ connection.get(build_path(["json"], params))
13
15
  end
14
16
 
15
17
  def self.inspect name, params = {}
16
- self.validate Docker::API::InvalidParameter, [:size], params
17
- Docker::API::Connection.instance.get(self.build_path([name, "json"], params))
18
+ validate Docker::API::InvalidParameter, [:size], params
19
+ connection.get(build_path([name, "json"], params))
18
20
  end
19
21
 
20
22
  def self.top name, params = {}
21
- self.validate Docker::API::InvalidParameter, [:ps_args], params
22
- Docker::API::Connection.instance.get(self.build_path([name, "top"], params))
23
+ validate Docker::API::InvalidParameter, [:ps_args], params
24
+ connection.get(build_path([name, "top"], params))
23
25
  end
24
26
 
25
27
  def self.changes name
26
- Docker::API::Connection.instance.get(self.build_path([name, "changes"]))
28
+ connection.get(build_path([name, "changes"]))
27
29
  end
28
30
 
29
31
  def self.start name, params = {}
30
- self.validate Docker::API::InvalidParameter, [:detachKeys], params
31
- Docker::API::Connection.instance.post(self.build_path([name, "start"], params))
32
+ validate Docker::API::InvalidParameter, [:detachKeys], params
33
+ connection.post(build_path([name, "start"], params))
32
34
  end
33
35
 
34
36
  def self.stop name, params = {}
35
- self.validate Docker::API::InvalidParameter, [:t], params
36
- Docker::API::Connection.instance.post(self.build_path([name, "stop"], params))
37
+ validate Docker::API::InvalidParameter, [:t], params
38
+ connection.post(build_path([name, "stop"], params))
37
39
  end
38
40
 
39
41
  def self.restart name, params = {}
40
- self.validate Docker::API::InvalidParameter, [:t], params
41
- Docker::API::Connection.instance.post(self.build_path([name, "restart"], params))
42
+ validate Docker::API::InvalidParameter, [:t], params
43
+ connection.post(build_path([name, "restart"], params))
42
44
  end
43
45
 
44
46
  def self.kill name, params = {}
45
- self.validate Docker::API::InvalidParameter, [:signal], params
46
- Docker::API::Connection.instance.post(self.build_path([name, "kill"], params))
47
+ validate Docker::API::InvalidParameter, [:signal], params
48
+ connection.post(build_path([name, "kill"], params))
47
49
  end
48
50
 
49
51
  def self.wait name, params = {}
50
- self.validate Docker::API::InvalidParameter, [:condition], params
51
- Docker::API::Connection.instance.post(self.build_path([name, "wait"], params))
52
+ validate Docker::API::InvalidParameter, [:condition], params
53
+ connection.post(build_path([name, "wait"], params))
52
54
  end
53
55
 
54
56
  def self.update name, body = {}
55
- self.validate Docker::API::InvalidRequestBody, Docker::API::UpdateBody, body
56
- Docker::API::Connection.instance.post(self.build_path([name, "update"]), body)
57
+ validate Docker::API::InvalidRequestBody, Docker::API::UpdateBody, body
58
+ connection.post(build_path([name, "update"]), body)
57
59
  end
58
60
 
59
61
  def self.rename name, params = {}
60
- self.validate Docker::API::InvalidParameter, [:name], params
61
- Docker::API::Connection.instance.post(self.build_path([name, "rename"], params))
62
+ validate Docker::API::InvalidParameter, [:name], params
63
+ connection.post(build_path([name, "rename"], params))
62
64
  end
63
65
 
64
66
  def self.resize name, params = {}
65
- self.validate Docker::API::InvalidParameter, [:w, :h], params
66
- Docker::API::Connection.instance.post(self.build_path([name, "resize"], params))
67
+ validate Docker::API::InvalidParameter, [:w, :h], params
68
+ connection.post(build_path([name, "resize"], params))
67
69
  end
68
70
 
69
71
  def self.prune params = {}
70
- self.validate Docker::API::InvalidParameter, [:filters], params
71
- Docker::API::Connection.instance.post(self.build_path(["prune"], params))
72
+ validate Docker::API::InvalidParameter, [:filters], params
73
+ connection.post(build_path(["prune"], params))
72
74
  end
73
75
 
74
76
  def self.pause name
75
- Docker::API::Connection.instance.post(self.build_path([name, "pause"]))
77
+ connection.post(build_path([name, "pause"]))
76
78
  end
77
79
 
78
80
  def self.unpause name
79
- Docker::API::Connection.instance.post(self.build_path([name, "unpause"]))
81
+ connection.post(build_path([name, "unpause"]))
80
82
  end
81
83
 
82
84
  def self.remove name, params = {}
83
- self.validate Docker::API::InvalidParameter, [:v, :force, :link], params
84
- Docker::API::Connection.instance.delete(self.build_path([name]))
85
+ validate Docker::API::InvalidParameter, [:v, :force, :link], params
86
+ connection.delete(build_path([name]))
85
87
  end
86
88
 
87
89
  def self.logs name, params = {}
88
- self.validate Docker::API::InvalidParameter, [:follow, :stdout, :stderr, :since, :until, :timestamps, :tail], params
90
+ validate Docker::API::InvalidParameter, [:follow, :stdout, :stderr, :since, :until, :timestamps, :tail], params
89
91
 
90
- path = self.build_path([name, "logs"], params)
92
+ path = build_path([name, "logs"], params)
91
93
 
92
94
  if params[:follow] == true || params[:follow] == 1
93
- Docker::API::Connection.instance.connection.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 })
94
96
  else
95
- Docker::API::Connection.instance.get(path)
97
+ connection.get(path)
96
98
  end
97
99
  end
98
100
 
99
101
  def self.attach name, params = {}
100
- self.validate Docker::API::InvalidParameter, [:detachKeys, :logs, :stream, :stdin, :stdout, :stderr], params
101
- Docker::API::Connection.instance.connection.request(method: :post, path: self.build_path([name, "attach"], params) , response_block: lambda { |chunk, remaining_bytes, total_bytes| puts chunk.inspect })
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 })
102
104
  end
103
105
 
104
106
  def self.create params = {}, body = {}
105
- self.validate Docker::API::InvalidParameter, [:name], params
106
- self.validate Docker::API::InvalidRequestBody, Docker::API::CreateBody, body
107
- Docker::API::Connection.instance.post(self.build_path(["create"], params), body)
107
+ validate Docker::API::InvalidParameter, [:name], params
108
+ validate Docker::API::InvalidRequestBody, Docker::API::CreateBody, body
109
+ connection.post(build_path(["create"], params), body)
108
110
  end
109
111
 
110
112
  def self.stats name, params = {}
111
- self.validate Docker::API::InvalidParameter, [:stream], params
112
- path = self.build_path([name, "stats"], params)
113
+ validate Docker::API::InvalidParameter, [:stream], params
114
+ path = build_path([name, "stats"], params)
113
115
 
114
116
  if params[:stream] == true || params[:stream] == 1
115
117
  streamer = lambda do |chunk, remaining_bytes, total_bytes|
116
118
  puts chunk
117
119
  end
118
- Docker::API::Connection.instance.connection.get(path: path , response_block: streamer)
120
+ connection.request(method: :get, path: path , response_block: streamer)
119
121
  else
120
- Docker::API::Connection.instance.get(path)
122
+ connection.get(path)
121
123
  end
122
124
  end
123
125
 
@@ -128,56 +130,30 @@ module Docker
128
130
  streamer = lambda do |chunk, remaining_bytes, total_bytes|
129
131
  file.write(chunk)
130
132
  end
131
- response = Docker::API::Connection.instance.connection.get(path: self.build_path([name, "export"]) , response_block: streamer)
133
+ response = connection.request(method: :get, path: build_path([name, "export"]) , response_block: streamer)
132
134
  file.close
133
135
  end
134
136
  response
135
137
  end
136
138
 
137
139
  def self.archive name, file, params = {}
138
- self.validate Docker::API::InvalidParameter, [:path, :noOverwriteDirNonDir, :copyUIDGID], params
140
+ validate Docker::API::InvalidParameter, [:path, :noOverwriteDirNonDir, :copyUIDGID], params
139
141
 
140
142
  begin # File exists on disk, send it to container
141
143
  file = File.open( File.expand_path( file ) , "r")
142
- response = Docker::API::Connection.instance.connection.put(path: self.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} )
143
145
  file.close
144
146
  rescue Errno::ENOENT # File doesnt exist, get it from container
145
- response = Docker::API::Connection.instance.head(self.build_path([name, "archive"], params))
147
+ response = connection.head(build_path([name, "archive"], params))
146
148
  if response.status == 200 # file exists in container
147
149
  file = File.open( File.expand_path( file ) , "wb")
148
- response = Docker::API::Connection.instance.connection.get(path: self.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) })
149
151
  file.close
150
152
  end
151
153
  end
152
154
  response
153
155
  end
154
-
155
- private
156
-
157
- def self.validate error, permitted_keys, params
158
- not_permitted = params.keys - permitted_keys
159
- raise error if not_permitted.size > 0
160
- end
161
-
162
- ## Converts Ruby Hash into query parameters
163
- ## In general, the format is key=value
164
- ## If value is another Hash, it should keep a json syntax {key:value}
165
- def self.hash_to_params h
166
- p = []
167
- h.each do |k,v|
168
- if v.is_a?(Hash)
169
- p << "#{k}=#{v.to_json}"
170
- else
171
- p << "#{k}=#{v}"
172
- end
173
- end
174
- p.join("&").gsub(" ","")
175
- end
176
156
 
177
- def self.build_path path, params = {}
178
- p = ([@@base_path] << path).join("/")
179
- params.size > 0 ? [p, self.hash_to_params(params)].join("?") : p
180
- end
181
157
  end
182
158
  end
183
159
  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 self.base_path
6
+ "/exec"
7
+ end
8
+
9
+ def self.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 self.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 self.resize name, params = {}
29
+ validate Docker::API::InvalidParameter, [:w, :h], params
30
+ connection.post(build_path([name, "resize"], params))
31
+ end
32
+
33
+ def self.inspect name
34
+ connection.get(build_path([name, "json"]))
35
+ end
36
+
37
+ end
38
+ end
39
+ end
@@ -1,23 +1,139 @@
1
+ require "base64"
2
+ require "json"
3
+ require 'fileutils'
1
4
  module Docker
2
5
  module API
3
- class Image
6
+ CommitBody = [:Hostname, :Domainname, :User, :AttachStdin, :AttachStdout, :AttachStderr, :ExposedPorts, :Tty, :OpenStdin, :StdinOnce, :Env, :Cmd, :HealthCheck, :ArgsEscaped, :Image, :Volumes, :WorkingDir, :Entrypoint, :NetworkDisabled, :MacAddress, :OnBuild, :Labels, :StopSignal, :StopTimeout, :Shell]
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
+ class Image < Docker::API::Base
4
9
 
5
- @@base_path = "/images"
10
+ def self.base_path
11
+ "/images"
12
+ end
13
+
14
+ def self.inspect name
15
+ connection.get(build_path([name, "json"]))
16
+ end
17
+
18
+ def self.history name
19
+ connection.get(build_path([name, "history"]))
20
+ end
21
+
22
+ def self.list params = {}
23
+ validate Docker::API::InvalidParameter, [:all, :filters, :digests], params
24
+ connection.get(build_path(["json"], params))
25
+ end
26
+
27
+ def self.search params = {}
28
+ validate Docker::API::InvalidParameter, [:term, :limit, :filters], params
29
+ connection.get(build_path(["search"], params))
30
+ end
31
+
32
+ def self.tag name, params = {}
33
+ validate Docker::API::InvalidParameter, [:repo, :tag], params
34
+ connection.post(build_path([name, "tag"], params))
35
+ end
36
+
37
+ def self.prune params = {}
38
+ validate Docker::API::InvalidParameter, [:filters], params
39
+ connection.post(build_path(["prune"], params))
40
+ end
6
41
 
7
- def self.create query_params = {}
8
- Docker::API::Connection.instance.post(self.build_path(["create"], query_params))
42
+ def self.remove name, params = {}
43
+ validate Docker::API::InvalidParameter, [:force, :noprune], params
44
+ connection.delete(build_path([name], params))
9
45
  end
10
46
 
11
- def self.remove name, query_params = {}
12
- Docker::API::Connection.instance.delete(self.build_path([name], query_params))
47
+ def self.export name, path = "exported_image"
48
+ file = File.open("/tmp/exported-image", "wb")
49
+ streamer = lambda do |chunk, remaining_bytes, total_bytes|
50
+ file.write(chunk)
51
+ end
52
+ response = connection.request(method: :get, path: build_path([name, "get"]) , response_block: streamer)
53
+ file.close
54
+ response.status == 200 ? FileUtils.mv("/tmp/exported-image", File.expand_path(path)) : FileUtils.rm("/tmp/exported-image")
55
+ response
13
56
  end
14
57
 
15
- private
58
+ def self.import path, params = {}
59
+ validate Docker::API::InvalidParameter, [:quiet], params
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} )
62
+ file.close
63
+ response
64
+ end
65
+
66
+ def self.push name, params = {}, authentication = {}
67
+ validate Docker::API::InvalidParameter, [:tag], params
68
+
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 } )
73
+ else
74
+ connection.post(build_path([name, "push"], params))
75
+ end
76
+ end
16
77
 
17
- def self.build_path path, params = {}
18
- p = ([@@base_path] << path).join("/")
19
- params.size > 0 ? [p, params.map {|k,v| "#{k}=#{v}"}.join("&")].join("?") : p
78
+ def self.commit params = {}, body = {}
79
+ validate Docker::API::InvalidParameter, [:container, :repo, :tag, :comment, :author, :pause, :changes], params
80
+ validate Docker::API::InvalidRequestBody, Docker::API::CommitBody, body
81
+ container = Docker::API::Container.inspect(params[:container])
82
+ return container if [404, 301].include? container.status
83
+ 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)
20
85
  end
86
+
87
+ def self.create params = {}, authentication = {}
88
+ validate Docker::API::InvalidParameter, [:fromImage, :fromSrc, :repo, :tag, :message, :platform], params
89
+
90
+ if authentication.keys.size > 0
91
+ auth = Docker::API::System.auth(authentication)
92
+ 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 } )
94
+ elsif params.has_key? :fromSrc
95
+ if params[:fromSrc].match(/^(http|https)/)
96
+ connection.request(method: :post, path: build_path(["create"], params))
97
+ else
98
+ file = File.open(File.expand_path(params[:fromSrc]), "r")
99
+ 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} )
101
+ file.close
102
+ response
103
+ end
104
+ else
105
+ connection.post(build_path(["create"], params))
106
+ end
107
+ end
108
+
109
+ def self.build path, params = {}, authentication = {}
110
+ raise Docker::API::Error.new("Expected path or params[:remote]") unless path || params[:remote]
111
+ validate Docker::API::InvalidParameter, Docker::API::BuildParams, params
112
+
113
+ header = {"Content-type": "application/x-tar"}
114
+ if authentication.keys.size > 0
115
+ authentication.each_key do |server|
116
+ auth = Docker::API::System.auth({username: authentication[server][:username] ,password:authentication[server][:password], serveraddress: server})
117
+ return auth unless [200, 204].include? auth.status
118
+ end
119
+ header.merge!({"X-Registry-Config": Base64.urlsafe_encode64(authentication.to_json.to_s).chomp})
120
+ end
121
+
122
+ begin
123
+ 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})
125
+ file.close
126
+ rescue
127
+ response = connection.request(method: :post, path: build_path("/build", params), headers: header)
128
+ end
129
+ response
130
+ end
131
+
132
+ def self.delete_cache params = {}
133
+ validate Docker::API::InvalidParameter, [:all, "keep-storage", :filters], params
134
+ connection.post(build_path("/build/prune", params))
135
+ end
136
+
21
137
  end
22
138
 
23
139
  end
@@ -0,0 +1,45 @@
1
+ module Docker
2
+ module API
3
+ class Network < Docker::API::Base
4
+
5
+ def self.base_path
6
+ "/networks"
7
+ end
8
+
9
+ def self.list params = {}
10
+ validate Docker::API::InvalidParameter, [:filters], params
11
+ connection.get(build_path("/networks", params))
12
+ end
13
+
14
+ def self.inspect name, params = {}
15
+ validate Docker::API::InvalidParameter, [:verbose, :scope], params
16
+ connection.get(build_path([name], params))
17
+ end
18
+
19
+ def self.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 self.remove name
25
+ connection.delete(build_path([name]))
26
+ end
27
+
28
+ def self.prune params = {}
29
+ validate Docker::API::InvalidParameter, [:filters], params
30
+ connection.post(build_path(["prune"], params))
31
+ end
32
+
33
+ def self.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 self.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
@@ -0,0 +1,33 @@
1
+ require "json"
2
+ module Docker
3
+ module API
4
+ class System < Docker::API::Base
5
+
6
+ def self.auth body = {}
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)
9
+ end
10
+
11
+ def self.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 self.ping
17
+ connection.get("/_ping")
18
+ end
19
+
20
+ def self.info
21
+ connection.get("/info")
22
+ end
23
+
24
+ def self.version
25
+ connection.get("/version")
26
+ end
27
+
28
+ def self.df
29
+ connection.get("/system/df")
30
+ end
31
+ end
32
+ end
33
+ end
@@ -1,6 +1,6 @@
1
1
  module Docker
2
2
  module API
3
- GEM_VERSION = "0.1.0"
3
+ GEM_VERSION = "0.6.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 self.base_path
5
+ "/volumes"
6
+ end
7
+
8
+ def self.list params = {}
9
+ validate Docker::API::InvalidParameter, [:filters], params
10
+ connection.get(build_path("/volumes", params))
11
+ end
12
+
13
+ def self.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 self.inspect name
19
+ connection.get(build_path([name]))
20
+ end
21
+
22
+ def self.remove name, params = {}
23
+ validate Docker::API::InvalidParameter, [:force], params
24
+ connection.delete(build_path([name]))
25
+ end
26
+
27
+ def self.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,9 +1,17 @@
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"
8
+ require "docker/api/base"
5
9
  require "docker/api/container"
6
10
  require "docker/api/image"
11
+ require "docker/api/volume"
12
+ require "docker/api/network"
13
+ require "docker/api/exec"
14
+ require "docker/api/system"
7
15
 
8
16
  module Docker
9
17
  module API
metadata CHANGED
@@ -1,29 +1,29 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: dockerapi
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.1.0
4
+ version: 0.6.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-22 00:00:00.000000000 Z
11
+ date: 2020-07-03 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: excon
15
15
  requirement: !ruby/object:Gem::Requirement
16
16
  requirements:
17
- - - ">="
17
+ - - "~>"
18
18
  - !ruby/object:Gem::Version
19
- version: '0'
19
+ version: 0.74.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.74.0
27
27
  description: Interact directly with Docker API from Ruby code.
28
28
  email:
29
29
  - alysson.avila.costa@gmail.com
@@ -34,6 +34,7 @@ files:
34
34
  - ".gitignore"
35
35
  - ".rspec"
36
36
  - ".travis.yml"
37
+ - CHANGELOG.md
37
38
  - Gemfile
38
39
  - Gemfile.lock
39
40
  - LICENSE.txt
@@ -42,11 +43,17 @@ files:
42
43
  - bin/console
43
44
  - bin/setup
44
45
  - dockerapi.gemspec
46
+ - lib/docker/api/base.rb
45
47
  - lib/docker/api/connection.rb
46
48
  - lib/docker/api/container.rb
47
49
  - lib/docker/api/error.rb
50
+ - lib/docker/api/exec.rb
48
51
  - lib/docker/api/image.rb
52
+ - lib/docker/api/network.rb
53
+ - lib/docker/api/response.rb
54
+ - lib/docker/api/system.rb
49
55
  - lib/docker/api/version.rb
56
+ - lib/docker/api/volume.rb
50
57
  - lib/dockerapi.rb
51
58
  homepage: https://github.com/nu12/dockerapi
52
59
  licenses:
@@ -54,6 +61,7 @@ licenses:
54
61
  metadata:
55
62
  homepage_uri: https://github.com/nu12/dockerapi
56
63
  source_code_uri: https://github.com/nu12/dockerapi.git
64
+ changelog_uri: https://github.com/nu12/dockerapi/blob/master/CHANGELOG.md
57
65
  post_install_message:
58
66
  rdoc_options: []
59
67
  require_paths: