hyperdock 0.10.2
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 +7 -0
- data/LICENSE.txt +21 -0
- data/README.org +472 -0
- data/bin/hyperdock-api +17 -0
- data/hyperdock.gemspec +18 -0
- data/lib/hyperdock.rb +1 -0
- data/lib/hyperdock/resource/container.rb +35 -0
- data/lib/hyperdock/resource/container_ports.rb +48 -0
- data/lib/hyperdock/resource/containers.rb +22 -0
- data/lib/hyperdock/resource/core.rb +37 -0
- data/lib/hyperdock/resource/project.rb +26 -0
- data/lib/hyperdock/resource/project_service.rb +41 -0
- data/lib/hyperdock/resource/project_services.rb +47 -0
- data/lib/hyperdock/resource/projects.rb +26 -0
- data/lib/hyperdock/resource/root.rb +24 -0
- data/lib/hyperdock/resources.rb +8 -0
- metadata +119 -0
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: []
|