hyperdock 0.10.2

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA1:
3
+ metadata.gz: ca01f647778c5873550eb4c9cb7746a34951e5ed
4
+ data.tar.gz: 9b88ea674ab4bee1d382181b54cdc472ade78f05
5
+ SHA512:
6
+ metadata.gz: b88b28f88f7a8528a6a6c84885419757cade0ae4d98c6c58f38d6606f6aaf589bc970eccab2fcd86cb4d98d428560b6410a6659377a983a56265522bb83ff963
7
+ data.tar.gz: b6321e08db59681b4448530bab911422c70317d993bf7bb1308248deaf47f5a4247782d9cb39bc249eecf3f4f57d51dc5cddcae5367bbaf6c8c3365d7808a72f
data/LICENSE.txt ADDED
@@ -0,0 +1,21 @@
1
+
2
+ The MIT License (MIT)
3
+ Copyright © 2016 Chris Olstrom <chris@olstrom.com>
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the “Software”), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in
13
+ all copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED “AS IS”, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
21
+ THE SOFTWARE.
data/README.org ADDED
@@ -0,0 +1,472 @@
1
+ #+TITLE: HyperDock: A Hypermedia API for Docker
2
+ #+LATEX: \pagebreak
3
+
4
+ * Overview
5
+
6
+ ~HyperDock~ is a Hypermedia API for Docker. It augments the standard Docker
7
+ API with a discoverable, link-driven API conforming to the [[http://stateless.co/hal_specification.html][HAL Specification]].
8
+
9
+ * Why does this exist?
10
+
11
+ Docker's internal structure is very graph-oriented, but the API is less so. It
12
+ should be possible for most clients to "write themselves" (or at least
13
+ configure themselves) using the data available through the API. At present,
14
+ this is cumbersome.
15
+
16
+ * Status
17
+
18
+ ~HyperDock~ is probably not suitable for production use yet.
19
+
20
+ * Installation
21
+
22
+ #+BEGIN_SRC shell
23
+ gem install hyperdock
24
+ #+END_SRC
25
+
26
+ * Usage
27
+
28
+ To launch it, just run:
29
+
30
+ #+BEGIN_SRC shell
31
+ hyperdock-api
32
+ #+END_SRC
33
+
34
+ The API will be available on port =8080=.
35
+
36
+ ** Launch something with ~docker-compose~ (example)
37
+
38
+ For the purpose of documentation, we'll use ~cloud-surveyor~.
39
+
40
+ #+BEGIN_SRC shell
41
+ git clone https://github.com/colstrom/cloud-surveyor
42
+ cd cloud-surveyor
43
+ docker-compose up -d
44
+ #+END_SRC
45
+
46
+ This should run two service containers for ~cloud-surveyor~:
47
+ - ~redis~
48
+ - ~elasticmq~
49
+
50
+ ** Explore!
51
+
52
+ *** The Root Resource
53
+
54
+ Let's see what we have at the root resource (this should be a
55
+ self-describing gateway to the rest of the API:
56
+
57
+ #+BEGIN_SRC shell
58
+ curl -s http://localhost:8080/ | jq .
59
+ #+END_SRC
60
+
61
+ #+BEGIN_SRC json
62
+ {
63
+ "_links": {
64
+ "self": {
65
+ "href": "/"
66
+ },
67
+ "projects": {
68
+ "href": "/projects"
69
+ },
70
+ "project": {
71
+ "href": "/project/{project}",
72
+ "templated": true
73
+ },
74
+ "project:services": {
75
+ "href": "/project/{project}/services",
76
+ "templated": true
77
+ },
78
+ "containers": {
79
+ "href": "/containers"
80
+ },
81
+ "container": {
82
+ "href": "/container/{container}",
83
+ "templated": true
84
+ },
85
+ "container:ports": {
86
+ "href": "/container/{container}/ports",
87
+ "templated": true
88
+ }
89
+ },
90
+ "version": 1
91
+ }
92
+ #+END_SRC
93
+
94
+ What we see here (and in all the responses) is a list of resources reachable
95
+ from this resource. Since this is the root level, we have a rough map of the
96
+ main API.
97
+
98
+ =/containers= seems pretty standard for Docker, so let's check out
99
+ =projects= instead.
100
+
101
+ *** Projects
102
+
103
+ =/projects= lists all ~docker-compose~ projects running on this docker host.
104
+
105
+ #+BEGIN_SRC shell
106
+ curl -s localhost:8080/projects | jq .
107
+ #+END_SRC
108
+
109
+ #+BEGIN_SRC json
110
+ {
111
+ "_links": {
112
+ "self": {
113
+ "href": "/projects"
114
+ },
115
+ "project:cloudsurveyor": {
116
+ "href": "/project/cloudsurveyor"
117
+ }
118
+ },
119
+ "projects": [
120
+ "cloudsurveyor"
121
+ ]
122
+ }
123
+ #+END_SRC
124
+
125
+ Let's follow the links, and see where we end up!
126
+
127
+ *** Project
128
+
129
+ =/project/{project}= returns a project. Projects have things like
130
+ =services=, so we should expect a =link= for those.
131
+
132
+ #+BEGIN_SRC shell
133
+ curl -s localhost:8080/projects/cloudsurveyor | jq .
134
+ #+END_SRC
135
+
136
+ #+BEGIN_SRC json
137
+ {
138
+ "_links": {
139
+ "self": {
140
+ "href": "/project/cloudsurveyor"
141
+ },
142
+ "services": {
143
+ "href": "/project/cloudsurveyor/services"
144
+ }
145
+ }
146
+ }
147
+ #+END_SRC
148
+
149
+ *** Services
150
+
151
+ =/project/{project}/services= returns a list of services for a project.
152
+ These should correspond to the services described in your
153
+ =docker-compose.yaml=.
154
+
155
+ #+BEGIN_SRC shell
156
+ curl -s localhost:8080/projects/cloudsurveyor/services | jq .
157
+ #+END_SRC
158
+
159
+ #+BEGIN_SRC json
160
+ {
161
+ "_links": {
162
+ "self": {
163
+ "href": "/project/cloudsurveyor/services"
164
+ },
165
+ "service:redis": {
166
+ "href": "/project/cloudsurveyor/service/redis"
167
+ },
168
+ "service:elasticmq": {
169
+ "href": "/project/cloudsurveyor/service/elasticmq"
170
+ }
171
+ },
172
+ "services": [
173
+ "redis",
174
+ "elasticmq"
175
+ ]
176
+ }
177
+ #+END_SRC
178
+
179
+ We can see two services here: =redis= and =elasticmq=. Compare this to the
180
+ =docker-compose.yaml= in the ~cloud-surveyor~ repository:
181
+
182
+ #+BEGIN_SRC yaml
183
+ ---
184
+ version: "2"
185
+ services:
186
+ elasticmq:
187
+ image: colstrom/elasticmq
188
+ ports:
189
+ - "9324:9324"
190
+ redis:
191
+ image: redis:alpine
192
+ ports:
193
+ - "6379"
194
+ #+END_SRC
195
+
196
+ Let's have a look at that =redis= service, shall we?
197
+
198
+ *** Service
199
+
200
+ =/project/{project}/service/{service}= returns a list of containers for the
201
+ specified service.
202
+
203
+ #+BEGIN_SRC shell
204
+ curl -s localhost:8080/projects/cloudsurveyor/service/redis | jq .
205
+ #+END_SRC
206
+
207
+ #+BEGIN_SRC json
208
+ {
209
+ "_links": {
210
+ "self": {
211
+ "href": "/project/cloudsurveyor/service/redis"
212
+ },
213
+ "containers": [
214
+ {
215
+ "href": "/container/27d6320f12e28b57ea7b2cbf423e647ab7f56793d7622069c9dc1d2f7a8d362b"
216
+ }
217
+ ]
218
+ }
219
+ }
220
+ #+END_SRC
221
+
222
+ Well, look at that: something that isn't nested deeper under the same path!
223
+
224
+ What happens if we scale up the redis containers a bit?
225
+
226
+ #+BEGIN_SRC shell
227
+ docker-compose scale redis=3
228
+ #+END_SRC
229
+
230
+ And then check the =service= links again?
231
+
232
+ #+BEGIN_SRC shell
233
+ curl -s localhost:8080/projects/cloudsurveyor/service/redis | jq .
234
+ #+END_SRC
235
+
236
+ #+BEGIN_SRC json
237
+ {
238
+ "_links": {
239
+ "self": {
240
+ "href": "/project/cloudsurveyor/service/redis"
241
+ },
242
+ "containers": [
243
+ {
244
+ "href": "/container/a59c2bdc3691df1cd895c3705c1d260d1ef74323d5d032535e4c34f2d9b29351"
245
+ },
246
+ {
247
+ "href": "/container/5dd8c8db1b7e767ca5529165bf337e70096e32e635346208eda173a0a1eb6c3c"
248
+ },
249
+ {
250
+ "href": "/container/27e0bc50a08b7f54f375ba098a64733720e1287e0f18763c56150feb4869bd1b"
251
+ }
252
+ ]
253
+ }
254
+ }
255
+ #+END_SRC
256
+
257
+ But what do those =containers= look like?
258
+
259
+ *** Container
260
+
261
+ =/container/{container}= returns information about a specific container, as
262
+ you might expect.
263
+
264
+ #+BEGIN_SRC shell
265
+ curl -s localhost:8080/container/a59c2bdc3691df1cd895c3705c1d260d1ef74323d5d032535e4c34f2d9b29351 | jq .
266
+ #+END_SRC
267
+
268
+ #+BEGIN_SRC json
269
+ {
270
+ "_links": {
271
+ "self": {
272
+ "href": "/container/a59c2bdc3691df1cd895c3705c1d260d1ef74323d5d032535e4c34f2d9b29351"
273
+ },
274
+ "mounts": {
275
+ "href": "/container/a59c2bdc3691df1cd895c3705c1d260d1ef74323d5d032535e4c34f2d9b29351/mounts"
276
+ },
277
+ "networks": {
278
+ "href": "/container/a59c2bdc3691df1cd895c3705c1d260d1ef74323d5d032535e4c34f2d9b29351/networks"
279
+ },
280
+ "ports": {
281
+ "href": "/container/a59c2bdc3691df1cd895c3705c1d260d1ef74323d5d032535e4c34f2d9b29351/ports"
282
+ }
283
+ },
284
+ "info": {
285
+ "Names": [
286
+ "/cloudsurveyor_redis_2"
287
+ ],
288
+ "Image": "redis:alpine",
289
+ "ImageID": "sha256:2aabafe89cbffe63a812e3965137f36df73488488a6ad4ba641272a3cf384cd1",
290
+ "Command": "docker-entrypoint.sh redis-server",
291
+ "Created": 1471154629,
292
+ "Ports": [
293
+ {
294
+ "IP": "0.0.0.0",
295
+ "PrivatePort": 6379,
296
+ "PublicPort": 32770,
297
+ "Type": "tcp"
298
+ }
299
+ ],
300
+ "Labels": {
301
+ "com.docker.compose.config-hash": "7823e6dcfbb9d488dc1be2e26cff00c9019451db8fcc5187f711f17e4a161a4f",
302
+ "com.docker.compose.container-number": "2",
303
+ "com.docker.compose.oneoff": "False",
304
+ "com.docker.compose.project": "cloudsurveyor",
305
+ "com.docker.compose.service": "redis",
306
+ "com.docker.compose.version": "1.8.0"
307
+ },
308
+ "State": "running",
309
+ "Status": "Up 4 minutes",
310
+ "HostConfig": {
311
+ "NetworkMode": "cloudsurveyor_default"
312
+ },
313
+ "NetworkSettings": {
314
+ "Networks": {
315
+ "cloudsurveyor_default": {
316
+ "IPAMConfig": null,
317
+ "Links": null,
318
+ "Aliases": null,
319
+ "NetworkID": "b6665ba6532fb26f8595c2155c1a5ca6fd9397b69c1033dc39ddfecc7abe470b",
320
+ "EndpointID": "f416509bd5386316bfb84197d01ee173e41b741a6ef4b3a3545e7c03ead48a11",
321
+ "Gateway": "172.19.0.1",
322
+ "IPAddress": "172.19.0.5",
323
+ "IPPrefixLen": 16,
324
+ "IPv6Gateway": "",
325
+ "GlobalIPv6Address": "",
326
+ "GlobalIPv6PrefixLen": 0,
327
+ "MacAddress": "02:42:ac:13:00:05"
328
+ }
329
+ }
330
+ },
331
+ "Mounts": [
332
+ {
333
+ "Name": "eba94001326155110ed4901f12936c5a0c05c79ca550fd023fa55e12df965a13",
334
+ "Source": "/var/lib/docker/volumes/eba94001326155110ed4901f12936c5a0c05c79ca550fd023fa55e12df965a13/_data",
335
+ "Destination": "/data",
336
+ "Driver": "local",
337
+ "Mode": "",
338
+ "RW": true,
339
+ "Propagation": ""
340
+ }
341
+ ],
342
+ "id": "a59c2bdc3691df1cd895c3705c1d260d1ef74323d5d032535e4c34f2d9b29351"
343
+ }
344
+ }
345
+ #+END_SRC
346
+
347
+
348
+ *** Ports
349
+
350
+ =/container/{container}/ports= returns port information in various
351
+ configurations, for convenient discovery.
352
+
353
+ #+BEGIN_SRC shell
354
+ curl -s localhost:8080/container/a59c2bdc3691df1cd895c3705c1d260d1ef74323d5d032535e4c34f2d9b29351/ports | jq .
355
+ #+END_SRC
356
+
357
+ #+BEGIN_SRC json
358
+ {
359
+ "_links": {
360
+ "self": {
361
+ "href": "/container/a59c2bdc3691df1cd895c3705c1d260d1ef74323d5d032535e4c34f2d9b29351/ports"
362
+ }
363
+ },
364
+ "all": [
365
+ {
366
+ "IP": "0.0.0.0",
367
+ "PrivatePort": 6379,
368
+ "PublicPort": 32770,
369
+ "Type": "tcp"
370
+ }
371
+ ],
372
+ "PublicPort": {
373
+ "32770": [
374
+ {
375
+ "IP": "0.0.0.0",
376
+ "PrivatePort": 6379,
377
+ "PublicPort": 32770,
378
+ "Type": "tcp"
379
+ }
380
+ ]
381
+ },
382
+ "PrivatePort": {
383
+ "6379": [
384
+ {
385
+ "IP": "0.0.0.0",
386
+ "PrivatePort": 6379,
387
+ "PublicPort": 32770,
388
+ "Type": "tcp"
389
+ }
390
+ ]
391
+ },
392
+ "IP": {
393
+ "0.0.0.0": [
394
+ {
395
+ "IP": "0.0.0.0",
396
+ "PrivatePort": 6379,
397
+ "PublicPort": 32770,
398
+ "Type": "tcp"
399
+ }
400
+ ]
401
+ },
402
+ "Type": {
403
+ "tcp": [
404
+ {
405
+ "IP": "0.0.0.0",
406
+ "PrivatePort": 6379,
407
+ "PublicPort": 32770,
408
+ "Type": "tcp"
409
+ }
410
+ ]
411
+ },
412
+ "tcp": [
413
+ {
414
+ "IP": "0.0.0.0",
415
+ "PrivatePort": 6379,
416
+ "PublicPort": 32770,
417
+ "Type": "tcp"
418
+ }
419
+ ],
420
+ "udp": []
421
+ }
422
+ #+END_SRC
423
+
424
+ So, from the top, we've found the ports bound on the host for a service
425
+ container associated with a project. In the simple example here, that's easy
426
+ enough to dig out of the Docker API, but navigating by relationships gives
427
+ us more flexibility and helps keep things manageable at scale.
428
+
429
+ *** Containers
430
+
431
+ =/containers/= returns a list of all containers running on the host, with no
432
+ particular ordering or filters.
433
+
434
+ #+BEGIN_SRC shell
435
+ curl -s localhost:8080/containers | jq .
436
+ #+END_SRC
437
+
438
+ #+BEGIN_SRC json
439
+ {
440
+ "_links": {
441
+ "self": {
442
+ "href": "/containers"
443
+ },
444
+ "container:a59c2bdc3691df1cd895c3705c1d260d1ef74323d5d032535e4c34f2d9b29351": {
445
+ "href": "/container/a59c2bdc3691df1cd895c3705c1d260d1ef74323d5d032535e4c34f2d9b29351"
446
+ },
447
+ "container:5dd8c8db1b7e767ca5529165bf337e70096e32e635346208eda173a0a1eb6c3c": {
448
+ "href": "/container/5dd8c8db1b7e767ca5529165bf337e70096e32e635346208eda173a0a1eb6c3c"
449
+ },
450
+ "container:27e0bc50a08b7f54f375ba098a64733720e1287e0f18763c56150feb4869bd1b": {
451
+ "href": "/container/27e0bc50a08b7f54f375ba098a64733720e1287e0f18763c56150feb4869bd1b"
452
+ },
453
+ "container:47c9f36e225e722380ce0bf3a7a41365b754fad8df8c1a3c64b2223a6d6e3548": {
454
+ "href": "/container/47c9f36e225e722380ce0bf3a7a41365b754fad8df8c1a3c64b2223a6d6e3548"
455
+ }
456
+ },
457
+ "containers": [
458
+ "a59c2bdc3691df1cd895c3705c1d260d1ef74323d5d032535e4c34f2d9b29351",
459
+ "5dd8c8db1b7e767ca5529165bf337e70096e32e635346208eda173a0a1eb6c3c",
460
+ "27e0bc50a08b7f54f375ba098a64733720e1287e0f18763c56150feb4869bd1b",
461
+ "47c9f36e225e722380ce0bf3a7a41365b754fad8df8c1a3c64b2223a6d6e3548"
462
+ ]
463
+ }
464
+ #+END_SRC
465
+
466
+ * License
467
+
468
+ ~hyperdock~ is available under the [[https://tldrlegal.com/license/mit-license][MIT License]]. See ~LICENSE.txt~ for the full text.
469
+
470
+ * Contributors
471
+
472
+ - [[https://colstrom.github.io/][Chris Olstrom]] | [[mailto:chris@olstrom.com][e-mail]] | [[https://twitter.com/ChrisOlstrom][Twitter]]
data/bin/hyperdock-api ADDED
@@ -0,0 +1,17 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ require 'rakuna'
4
+ require_relative '../lib/hyperdock'
5
+
6
+ Webmachine.application.routes do
7
+ add [], HyperDock::Resource::Root
8
+ add ['projects'], HyperDock::Resource::Projects
9
+ add ['project', :project], HyperDock::Resource::Project
10
+ add ['project', :project, 'services'], HyperDock::Resource::ProjectServices
11
+ add ['project', :project, 'service', :service], HyperDock::Resource::ProjectService
12
+ add ['containers'], HyperDock::Resource::Containers
13
+ add ['container', :container], HyperDock::Resource::Container
14
+ add ['container', :container, 'ports'], HyperDock::Resource::ContainerPorts
15
+ end
16
+
17
+ Webmachine.application.run
data/hyperdock.gemspec ADDED
@@ -0,0 +1,18 @@
1
+ Gem::Specification.new do |gem|
2
+ gem.name = 'hyperdock'
3
+ gem.version = `git describe --tags --abbrev=0`.chomp
4
+ gem.licenses = 'MIT'
5
+ gem.authors = ['Chris Olstrom']
6
+ gem.email = 'chris@olstrom.com'
7
+ gem.homepage = 'https://github.com/colstrom/hyperdock'
8
+ gem.summary = 'Hypermedia API for Docker'
9
+
10
+ gem.files = `git ls-files`.split("\n")
11
+ gem.test_files = `git ls-files -- {test,spec,features}/*`.split("\n")
12
+ gem.executables = `git ls-files -- bin/*`.split("\n").map { |f| File.basename(f) }
13
+ gem.require_paths = ['lib']
14
+
15
+ gem.add_runtime_dependency 'rakuna', '~> 1.0', '>= 1.0.1'
16
+ gem.add_runtime_dependency 'docker-api', '~> 1.31', '>= 1.31.0'
17
+ gem.add_runtime_dependency 'contracts', '~> 0.14', '>= 0.14.0'
18
+ end
data/lib/hyperdock.rb ADDED
@@ -0,0 +1 @@
1
+ require_relative 'hyperdock/resources'
@@ -0,0 +1,35 @@
1
+ require 'docker-api'
2
+ require_relative 'core'
3
+
4
+ module HyperDock
5
+ module Resource
6
+ class Container < Core
7
+ Contract None => Maybe[::Docker::Container]
8
+ def container
9
+ @container ||= ::Docker::Container.all.select do |container|
10
+ container.id == request.path_info[:container]
11
+ end.first
12
+ #::Docker::Container.get id
13
+ end
14
+
15
+ Contract None => Bool
16
+ def resource_exists?
17
+ !container.nil?
18
+ rescue ::Docker::Error::NotFoundError
19
+ false
20
+ end
21
+
22
+ def links
23
+ @links ||= {
24
+ mounts: { href: "/#{request.disp_path}/mounts" },
25
+ networks: { href: "/#{request.disp_path}/networks" },
26
+ ports: { href: "/#{request.disp_path}/ports" }
27
+ }
28
+ end
29
+
30
+ def attributes
31
+ { info: container.info }
32
+ end
33
+ end
34
+ end
35
+ end
@@ -0,0 +1,48 @@
1
+ require_relative 'container'
2
+
3
+ module HyperDock
4
+ module Resource
5
+ class ContainerPorts < Container
6
+ Contract None => ArrayOf[Hash]
7
+ def ports
8
+ @ports ||= container.info.fetch('Ports') { [] }
9
+ end
10
+
11
+ def public_port
12
+ @public_port ||= ports.group_by { |port| port['PublicPort'] }
13
+ end
14
+
15
+ def private_port
16
+ @private_port ||= ports.group_by { |port| port['PrivatePort'] }
17
+ end
18
+
19
+ def ip
20
+ @ip ||= ports.group_by { |port| port['IP'] }
21
+ end
22
+
23
+ def type
24
+ @type ||= ports.group_by { |port| port['Type'] }
25
+ end
26
+
27
+ def tcp
28
+ @tcp ||= ports.select { |port| port['Type'] == 'tcp' }
29
+ end
30
+
31
+ def udp
32
+ @udp ||= ports.select { |port| port['Type'] == 'udp' }
33
+ end
34
+
35
+ def attributes
36
+ {
37
+ all: ports,
38
+ PublicPort: public_port,
39
+ PrivatePort: private_port,
40
+ IP: ip,
41
+ Type: type,
42
+ tcp: tcp,
43
+ udp: udp
44
+ }
45
+ end
46
+ end
47
+ end
48
+ end
@@ -0,0 +1,22 @@
1
+ require 'docker-api'
2
+ require_relative 'core'
3
+
4
+ module HyperDock
5
+ module Resource
6
+ class Containers < Core
7
+ def containers
8
+ @containers ||= ::Docker::Container.all.map(&:id)
9
+ end
10
+
11
+ def links
12
+ @links ||= containers.map do |container|
13
+ { "container:#{container}" => { href: "/container/#{container}" } }
14
+ end.reduce(&:merge)
15
+ end
16
+
17
+ def attributes
18
+ { containers: containers }
19
+ end
20
+ end
21
+ end
22
+ end
@@ -0,0 +1,37 @@
1
+ require 'contracts'
2
+ require 'rakuna'
3
+
4
+ module HyperDock
5
+ module Resource
6
+ class Core < ::Rakuna::Resource::Basic
7
+ include ::Contracts::Core
8
+ include ::Contracts::Builtin
9
+ include ::Rakuna::Provides::JSON
10
+
11
+ Contract None => ArrayOf[String]
12
+ def allowed_methods
13
+ ['GET', 'HEAD']
14
+ end
15
+
16
+ Contract None => HashOf[RespondTo[:to_s], HashOf[RespondTo[:to_s], Any]]
17
+ def _links
18
+ { self: { href: "/#{request.disp_path}" } }
19
+ end
20
+
21
+ Contract None => HashOf[RespondTo[:to_s], HashOf[RespondTo[:to_s], Hash]]
22
+ def links
23
+ {}
24
+ end
25
+
26
+ Contract None => HashOf[RespondTo[:to_s], Any]
27
+ def attributes
28
+ {}
29
+ end
30
+
31
+ Contract None => RespondTo[:to_json]
32
+ def output
33
+ { _links: _links.merge(links) }.merge(attributes)
34
+ end
35
+ end
36
+ end
37
+ end
@@ -0,0 +1,26 @@
1
+ require 'docker-api'
2
+ require_relative 'core'
3
+
4
+ module HyperDock
5
+ module Resource
6
+ class Project < Core
7
+ Contract None => ArrayOf[String]
8
+ def projects
9
+ @projects ||= ::Docker::Container.all.map do |container|
10
+ container.info.dig 'Labels', 'com.docker.compose.project'
11
+ end
12
+ end
13
+
14
+ Contract None => Bool
15
+ def resource_exists?
16
+ projects.include? request.path_info[:project]
17
+ end
18
+
19
+ def links
20
+ @links ||= {
21
+ services: { href: "/#{request.disp_path}/services" }
22
+ }
23
+ end
24
+ end
25
+ end
26
+ end
@@ -0,0 +1,41 @@
1
+ require 'docker-api'
2
+ require_relative 'core'
3
+
4
+ module HyperDock
5
+ module Resource
6
+ class ProjectService < Core
7
+ PROJECT_LABEL = 'com.docker.compose.project'.freeze
8
+ SERVICE_LABEL = 'com.docker.compose.service'.freeze
9
+
10
+ def project
11
+ @project ||= request.path_info[:project]
12
+ end
13
+
14
+ def service
15
+ @service ||= request.path_info[:service]
16
+ end
17
+
18
+ def matches_project?(container)
19
+ container.info.dig('Labels', PROJECT_LABEL) == project
20
+ end
21
+
22
+ def matches_service?(container)
23
+ container.info.dig('Labels', SERVICE_LABEL) == service
24
+ end
25
+
26
+ def containers
27
+ @containers ||= ::Docker::Container.all.select do |container|
28
+ matches_project?(container) && matches_service?(container)
29
+ end.map(&:id)
30
+ end
31
+
32
+ def links
33
+ @links ||= {
34
+ 'containers' => containers.map do |container|
35
+ { href: "/container/#{container}" }
36
+ end
37
+ }
38
+ end
39
+ end
40
+ end
41
+ end
@@ -0,0 +1,47 @@
1
+ require 'docker-api'
2
+ require_relative 'core'
3
+
4
+ module HyperDock
5
+ module Resource
6
+ class ProjectServices < Core
7
+ Contract None => String
8
+ def project
9
+ @project ||= request.path_info[:project]
10
+ end
11
+
12
+ Contract None => ArrayOf[::Docker::Container]
13
+ def containers
14
+ @containers ||= ::Docker::Container.all.select do |container|
15
+ container.info.dig('Labels', 'com.docker.compose.project') == project
16
+ end
17
+ end
18
+
19
+ Contract None => Bool
20
+ def resource_exists?
21
+ !containers.empty?
22
+ end
23
+
24
+ Contract None => ArrayOf[String]
25
+ def services
26
+ @services ||= containers.map do |container|
27
+ container.info.dig('Labels', 'com.docker.compose.service')
28
+ end.uniq
29
+ end
30
+
31
+ Contract None => HashOf[RespondTo[:to_s], Hash]
32
+ def links
33
+ @links ||= services.map do |service|
34
+ {
35
+ "service:#{service}" => {
36
+ href: "/project/#{project}/service/#{service}"
37
+ }
38
+ }
39
+ end.reduce(&:merge)
40
+ end
41
+
42
+ def attributes
43
+ { services: services }
44
+ end
45
+ end
46
+ end
47
+ end
@@ -0,0 +1,26 @@
1
+ require 'docker-api'
2
+ require_relative 'core'
3
+
4
+ module HyperDock
5
+ module Resource
6
+ class Projects < Core
7
+ Contract None => ArrayOf[String]
8
+ def projects
9
+ @projects ||= ::Docker::Container.all.map do |container|
10
+ container.info.dig 'Labels', 'com.docker.compose.project'
11
+ end.uniq
12
+ end
13
+
14
+ Contract None => HashOf[RespondTo[:to_s], Hash]
15
+ def links
16
+ @links ||= projects.map do |project|
17
+ { "project:#{project}" => { href: "/project/#{project}" } }
18
+ end.reduce(&:merge)
19
+ end
20
+
21
+ def attributes
22
+ { projects: projects }
23
+ end
24
+ end
25
+ end
26
+ end
@@ -0,0 +1,24 @@
1
+ require_relative 'core'
2
+
3
+ module HyperDock
4
+ module Resource
5
+ class Root < Core
6
+ VERSION = 1
7
+
8
+ def attributes
9
+ { version: VERSION }
10
+ end
11
+
12
+ def links
13
+ @links ||= {
14
+ projects: { href: '/projects' },
15
+ project: { href: '/project/{project}', templated: true },
16
+ 'project:services' => { href: '/project/{project}/services', templated: true },
17
+ containers: { href: '/containers' },
18
+ container: { href: '/container/{container}', templated: true },
19
+ 'container:ports' => { href: '/container/{container}/ports', templated: true }
20
+ }
21
+ end
22
+ end
23
+ end
24
+ end
@@ -0,0 +1,8 @@
1
+ require_relative 'resource/container'
2
+ require_relative 'resource/container_ports'
3
+ require_relative 'resource/containers'
4
+ require_relative 'resource/project'
5
+ require_relative 'resource/project_service'
6
+ require_relative 'resource/project_services'
7
+ require_relative 'resource/projects'
8
+ require_relative 'resource/root'
metadata ADDED
@@ -0,0 +1,119 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: hyperdock
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.10.2
5
+ platform: ruby
6
+ authors:
7
+ - Chris Olstrom
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain: []
11
+ date: 2016-08-14 00:00:00.000000000 Z
12
+ dependencies:
13
+ - !ruby/object:Gem::Dependency
14
+ name: rakuna
15
+ requirement: !ruby/object:Gem::Requirement
16
+ requirements:
17
+ - - "~>"
18
+ - !ruby/object:Gem::Version
19
+ version: '1.0'
20
+ - - ">="
21
+ - !ruby/object:Gem::Version
22
+ version: 1.0.1
23
+ type: :runtime
24
+ prerelease: false
25
+ version_requirements: !ruby/object:Gem::Requirement
26
+ requirements:
27
+ - - "~>"
28
+ - !ruby/object:Gem::Version
29
+ version: '1.0'
30
+ - - ">="
31
+ - !ruby/object:Gem::Version
32
+ version: 1.0.1
33
+ - !ruby/object:Gem::Dependency
34
+ name: docker-api
35
+ requirement: !ruby/object:Gem::Requirement
36
+ requirements:
37
+ - - "~>"
38
+ - !ruby/object:Gem::Version
39
+ version: '1.31'
40
+ - - ">="
41
+ - !ruby/object:Gem::Version
42
+ version: 1.31.0
43
+ type: :runtime
44
+ prerelease: false
45
+ version_requirements: !ruby/object:Gem::Requirement
46
+ requirements:
47
+ - - "~>"
48
+ - !ruby/object:Gem::Version
49
+ version: '1.31'
50
+ - - ">="
51
+ - !ruby/object:Gem::Version
52
+ version: 1.31.0
53
+ - !ruby/object:Gem::Dependency
54
+ name: contracts
55
+ requirement: !ruby/object:Gem::Requirement
56
+ requirements:
57
+ - - "~>"
58
+ - !ruby/object:Gem::Version
59
+ version: '0.14'
60
+ - - ">="
61
+ - !ruby/object:Gem::Version
62
+ version: 0.14.0
63
+ type: :runtime
64
+ prerelease: false
65
+ version_requirements: !ruby/object:Gem::Requirement
66
+ requirements:
67
+ - - "~>"
68
+ - !ruby/object:Gem::Version
69
+ version: '0.14'
70
+ - - ">="
71
+ - !ruby/object:Gem::Version
72
+ version: 0.14.0
73
+ description:
74
+ email: chris@olstrom.com
75
+ executables:
76
+ - hyperdock-api
77
+ extensions: []
78
+ extra_rdoc_files: []
79
+ files:
80
+ - LICENSE.txt
81
+ - README.org
82
+ - bin/hyperdock-api
83
+ - hyperdock.gemspec
84
+ - lib/hyperdock.rb
85
+ - lib/hyperdock/resource/container.rb
86
+ - lib/hyperdock/resource/container_ports.rb
87
+ - lib/hyperdock/resource/containers.rb
88
+ - lib/hyperdock/resource/core.rb
89
+ - lib/hyperdock/resource/project.rb
90
+ - lib/hyperdock/resource/project_service.rb
91
+ - lib/hyperdock/resource/project_services.rb
92
+ - lib/hyperdock/resource/projects.rb
93
+ - lib/hyperdock/resource/root.rb
94
+ - lib/hyperdock/resources.rb
95
+ homepage: https://github.com/colstrom/hyperdock
96
+ licenses:
97
+ - MIT
98
+ metadata: {}
99
+ post_install_message:
100
+ rdoc_options: []
101
+ require_paths:
102
+ - lib
103
+ required_ruby_version: !ruby/object:Gem::Requirement
104
+ requirements:
105
+ - - ">="
106
+ - !ruby/object:Gem::Version
107
+ version: '0'
108
+ required_rubygems_version: !ruby/object:Gem::Requirement
109
+ requirements:
110
+ - - ">="
111
+ - !ruby/object:Gem::Version
112
+ version: '0'
113
+ requirements: []
114
+ rubyforge_project:
115
+ rubygems_version: 2.5.1
116
+ signing_key:
117
+ specification_version: 4
118
+ summary: Hypermedia API for Docker
119
+ test_files: []