hyperdock 0.10.2
Sign up to get free protection for your applications and to get access to all the features.
- 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: []
|