docker_helper 0.0.1 → 0.0.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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: ac34d2dde6a799aa2856fcb5fbf115920adc60e6
4
- data.tar.gz: 0459f78e45e18567447af1684677ec8e50802413
3
+ metadata.gz: 281e717d31b942b1d9948fed9d4f652df44dac9b
4
+ data.tar.gz: e1a79e7764f8cc227f76b70543b0d09c211777c2
5
5
  SHA512:
6
- metadata.gz: 95ca42aed3756679e9ef409b870330109c94f6f8e99b64864b14dd6579722f19f010907a1d138646bdd9195cf5e7da7d8ad9504096c751160dc39d9148e8d962
7
- data.tar.gz: 4f88fd49be93a3ed3d77d20f72dc8c79e4c19e58be6870d6899915ac01b13faa3a3a52ea4b89d9cd355049a2f66282722e900ad5e9eb9eb3d33fca91a5c20fcb
6
+ metadata.gz: 0e1367bc8d01661f373f7dafbec395e1790ce1c3860d19106b6aa1fcf4900120c2ca69574d34bcfea733dc12a3152976c2ff7238a9df85116edb63d4459505c7
7
+ data.tar.gz: 86f1aabfa46d791af0462604572a9d4301681842a76dfe91b177750f96e5e445a892de409635f83376bd0693da6913d61dd6f1919307715758165774b5517443
data/ChangeLog CHANGED
@@ -2,6 +2,12 @@
2
2
 
3
3
  = Revision history for docker_helper
4
4
 
5
+ == 0.0.2 [2014-09-26]
6
+
7
+ * Added DockerHelper#docker_ready.
8
+ * Added DockerHelper#docker_port.
9
+ * Added DockerHelper::Pool.
10
+
5
11
  == 0.0.1 [2014-08-05]
6
12
 
7
13
  * First release.
data/README CHANGED
@@ -2,7 +2,7 @@
2
2
 
3
3
  == VERSION
4
4
 
5
- This documentation refers to docker_helper version 0.0.1.
5
+ This documentation refers to docker_helper version 0.0.2.
6
6
 
7
7
 
8
8
  == DESCRIPTION
@@ -24,6 +24,8 @@
24
24
  ###############################################################################
25
25
  #++
26
26
 
27
+ require 'net/http'
28
+
27
29
  # Various helper methods to control the Docker command-line client. Main
28
30
  # entrance point is the #docker helper. Use ::proxy for a self-contained
29
31
  # controller object to call these methods on.
@@ -208,17 +210,45 @@ module DockerHelper
208
210
  end
209
211
 
210
212
  # call-seq:
211
- # docker_url(port, name) -> aString
213
+ # docker_port(port, name) -> aString
212
214
  #
213
- # Returns the HTTP URL for container +name+ on port +port+. Fails
215
+ # Returns the host and port for container +name+ on port +port+. Fails
214
216
  # if container is not running or the specified port is not exposed.
215
217
  #
216
218
  # Command reference: {docker port
217
219
  # }[https://docs.docker.com/reference/commandline/cli/#port]
218
- def docker_url(port, name = nil)
220
+ def docker_port(port, name = nil)
219
221
  name ||= docker_container_name
220
222
 
221
- "http://#{docker(:port, name, port)}"
223
+ docker :port, name, port
224
+ end
225
+
226
+ # call-seq:
227
+ # docker_url(port, name) -> aString
228
+ #
229
+ # Returns the HTTP URL for container +name+ on port +port+ (see
230
+ # #docker_port).
231
+ def docker_url(port, name = nil)
232
+ "http://#{docker_port(port, name)}"
233
+ end
234
+
235
+ # call-seq:
236
+ # docker_ready(host_with_port[, path]) -> true or false
237
+ #
238
+ # Argument +host_with_port+ must be of the form <tt>host:port</tt>, as
239
+ # returned by #docker_port, or an array of host and port.
240
+ #
241
+ # Returns +true+ if and when the TCP port is available on the host and,
242
+ # if +path+ is given, a HTTP request for +path+ is successful.
243
+ #
244
+ # Returns +false+ if the port and the path haven't become available after
245
+ # 30 attempts each. Sleeps for 0.1 seconds inbetween attempts.
246
+ def docker_ready(host_with_port, path = nil, attempts = 30, sleep = 0.1)
247
+ host, port = host_with_port.is_a?(Array) ?
248
+ host_with_port : host_with_port.split(':')
249
+
250
+ docker_socket_ready(host, port, attempts, sleep) &&
251
+ (!path || docker_http_ready(host, port, path, attempts, sleep))
222
252
  end
223
253
 
224
254
  # call-seq:
@@ -355,7 +385,38 @@ module DockerHelper
355
385
  abort
356
386
  end
357
387
 
388
+ # Checks TCP connection.
389
+ def docker_socket_ready(host, port, attempts, sleep)
390
+ TCPSocket.new(host, port).close
391
+ true
392
+ rescue Errno::ECONNREFUSED
393
+ return false unless docker_ready_sleep(sleep, attempts -= 1)
394
+ retry
395
+ end
396
+
397
+ # Checks HTTP connection.
398
+ def docker_http_ready(host, port, path, attempts, sleep)
399
+ loop {
400
+ begin
401
+ break if Net::HTTP.get_response(host, path, port).is_a?(Net::HTTPSuccess)
402
+ rescue Errno::ECONNRESET, EOFError => err
403
+ return false unless docker_ready_sleep(sleep, attempts -= 1)
404
+ retry
405
+ end
406
+
407
+ return false unless docker_ready_sleep(sleep, attempts -= 1)
408
+ }
409
+
410
+ true
411
+ end
412
+
413
+ # Sleeps unless out of attempts.
414
+ def docker_ready_sleep(sleep, attempts)
415
+ sleep(sleep) unless attempts.zero?
416
+ end
417
+
358
418
  end
359
419
 
360
420
  require_relative 'docker_helper/version'
361
421
  require_relative 'docker_helper/proxy'
422
+ require_relative 'docker_helper/pool'
@@ -0,0 +1,103 @@
1
+ #--
2
+ ###############################################################################
3
+ # #
4
+ # docker_helper -- Helper methods to interact with Docker #
5
+ # #
6
+ # Copyright (C) 2014 Jens Wille #
7
+ # #
8
+ # Authors: #
9
+ # Jens Wille <jens.wille@gmail.com> #
10
+ # #
11
+ # docker_helper is free software; you can redistribute it and/or modify it #
12
+ # under the terms of the GNU Affero General Public License as published by #
13
+ # the Free Software Foundation; either version 3 of the License, or (at your #
14
+ # option) any later version. #
15
+ # #
16
+ # docker_helper is distributed in the hope that it will be useful, but #
17
+ # WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY #
18
+ # or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero General Public #
19
+ # License for more details. #
20
+ # #
21
+ # You should have received a copy of the GNU Affero General Public License #
22
+ # along with docker_helper. If not, see <http://www.gnu.org/licenses/>. #
23
+ # #
24
+ ###############################################################################
25
+ #++
26
+
27
+ module DockerHelper
28
+
29
+ class Pool
30
+
31
+ DEFAULT_SIZE = 2
32
+
33
+ DEFAULT_BASENAME = 'docker_helper'
34
+
35
+ def initialize(size = nil, basename = nil, docker = nil)
36
+ @docker, @previous_name, @basename =
37
+ docker ||= DockerHelper.proxy, nil,
38
+ basename ||= self.class::DEFAULT_BASENAME
39
+
40
+ yield self if block_given?
41
+
42
+ @pool = Array.new(size ||= self.class::DEFAULT_SIZE) { |i|
43
+ spawn_thread("#{basename}-#{$$}-#{i}")
44
+ }
45
+
46
+ extend(SinglePool) if size == 1
47
+ end
48
+
49
+ attr_accessor :image, :port, :path
50
+
51
+ def fetch_url(name = @previous_name)
52
+ docker.url(port, @previous_name = fetch(name)) + path.to_s
53
+ end
54
+
55
+ def fetch(name = @previous_name)
56
+ pool.shift.tap { reclaim(name) }.value
57
+ end
58
+
59
+ def clean(name = @previous_name)
60
+ pool.map { |t| clean_thread(t.value) }.tap { |clean|
61
+ clean << clean_thread(name) if name
62
+ }.each(&:join)
63
+ end
64
+
65
+ def inspect
66
+ '#<%s:0x%x %s@%d>' % [self.class, object_id, basename, pool.size]
67
+ end
68
+
69
+ private
70
+
71
+ attr_reader :docker, :basename, :pool
72
+
73
+ def spawn_thread(name, clean = false)
74
+ Thread.new {
75
+ docker.clean(name) if clean
76
+ docker.start(name, image) if image
77
+ docker.ready(docker.port(port, name), path) if port
78
+
79
+ name
80
+ }
81
+ end
82
+
83
+ def clean_thread(name)
84
+ Thread.new { docker.clean(name) }
85
+ end
86
+
87
+ def reclaim(name)
88
+ pool << spawn_thread(name, true) if name
89
+ end
90
+
91
+ module SinglePool
92
+
93
+ def fetch(name = @previous_name)
94
+ name || !block_given? ? super : yield(new_name = super)
95
+ ensure
96
+ reclaim(new_name)
97
+ end
98
+
99
+ end
100
+
101
+ end
102
+
103
+ end
@@ -31,8 +31,10 @@ module DockerHelper
31
31
 
32
32
  class Proxy
33
33
 
34
- def initialize(prefix = 'docker')
35
- self.proxy_prefix = prefix
34
+ DEFAULT_PREFIX = 'docker'
35
+
36
+ def initialize(prefix = nil)
37
+ self.proxy_prefix = prefix || self.class::DEFAULT_PREFIX
36
38
  end
37
39
 
38
40
  attr_accessor :proxy_prefix
@@ -46,6 +48,10 @@ module DockerHelper
46
48
  respond_to?(prefix_method(method))
47
49
  end
48
50
 
51
+ def pool(size = nil, basename = nil, &block)
52
+ Pool.new(size, basename, self, &block)
53
+ end
54
+
49
55
  private
50
56
 
51
57
  def prefix_method(method)
@@ -4,7 +4,7 @@ module DockerHelper
4
4
 
5
5
  MAJOR = 0
6
6
  MINOR = 0
7
- TINY = 1
7
+ TINY = 2
8
8
 
9
9
  class << self
10
10
 
@@ -44,6 +44,15 @@ quix 1.0 789 a week
44
44
 
45
45
  end
46
46
 
47
+ describe '#port' do
48
+
49
+ it 'should return the port' do
50
+ expect_pipe('0.0.0.0:42', 'port', 'foo', '23', {})
51
+ expect(subject.docker_port(23, 'foo')).to eq('0.0.0.0:42')
52
+ end
53
+
54
+ end
55
+
47
56
  describe '#url' do
48
57
 
49
58
  it 'should return the URL' do
@@ -53,6 +62,128 @@ quix 1.0 789 a week
53
62
 
54
63
  end
55
64
 
65
+ describe '#ready' do
66
+
67
+ def expect_socket(result)
68
+ expect(subject).to receive(:docker_socket_ready).with(
69
+ *@host_with_port_ary, a_kind_of(Integer), a_kind_of(Float)
70
+ ).and_return(result)
71
+ end
72
+
73
+ def expect_http(result)
74
+ expect(subject).to receive(:docker_http_ready).with(
75
+ *@host_with_port_ary, @path, a_kind_of(Integer), a_kind_of(Float)
76
+ ).and_return(result)
77
+ end
78
+
79
+ before do
80
+ @host_with_port_str = '0.0.0.0:42'
81
+ @host_with_port_ary = @host_with_port_str.split(':')
82
+ end
83
+
84
+ describe 'when socket ready' do
85
+
86
+ before do
87
+ expect_socket(true)
88
+ end
89
+
90
+ describe 'without path' do
91
+
92
+ before do
93
+ expect(subject).not_to receive(:docker_http_ready)
94
+ end
95
+
96
+ it 'should return true with string' do
97
+ expect(subject.docker_ready(@host_with_port_str)).to eq(true)
98
+ end
99
+
100
+ it 'should return true with array' do
101
+ expect(subject.docker_ready(@host_with_port_ary)).to eq(true)
102
+ end
103
+
104
+ end
105
+
106
+ describe 'with path' do
107
+
108
+ before do
109
+ @path = '/path'
110
+ end
111
+
112
+ describe 'when HTTP ready' do
113
+
114
+ before do
115
+ expect_http(true)
116
+ end
117
+
118
+ it 'should return true with string' do
119
+ expect(subject.docker_ready(@host_with_port_str, @path)).to eq(true)
120
+ end
121
+
122
+ it 'should return true with array' do
123
+ expect(subject.docker_ready(@host_with_port_ary, @path)).to eq(true)
124
+ end
125
+
126
+ end
127
+
128
+ describe 'when HTTP not ready' do
129
+
130
+ before do
131
+ expect_http(false)
132
+ end
133
+
134
+ it 'should return false with string' do
135
+ expect(subject.docker_ready(@host_with_port_str, @path)).to eq(false)
136
+ end
137
+
138
+ it 'should return false with array' do
139
+ expect(subject.docker_ready(@host_with_port_ary, @path)).to eq(false)
140
+ end
141
+
142
+ end
143
+
144
+ end
145
+
146
+ end
147
+
148
+ describe 'when socket ready' do
149
+
150
+ before do
151
+ expect_socket(false)
152
+ expect(subject).not_to receive(:docker_http_ready)
153
+ end
154
+
155
+ describe 'without path' do
156
+
157
+ it 'should return false with string' do
158
+ expect(subject.docker_ready(@host_with_port_str)).to eq(false)
159
+ end
160
+
161
+ it 'should return false with array' do
162
+ expect(subject.docker_ready(@host_with_port_ary)).to eq(false)
163
+ end
164
+
165
+ end
166
+
167
+ describe 'with path' do
168
+
169
+ before do
170
+ @path = '/path'
171
+ end
172
+
173
+ it 'should return false with string' do
174
+ expect(subject.docker_ready(@host_with_port_str, @path)).to eq(false)
175
+ end
176
+
177
+ it 'should return false with array' do
178
+ expect(subject.docker_ready(@host_with_port_ary, @path)).to eq(false)
179
+ end
180
+
181
+ end
182
+
183
+ end
184
+
185
+ end
186
+
56
187
  describe '#start' do
57
188
 
58
189
  it 'should start the container' do
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: docker_helper
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.0.1
4
+ version: 0.0.2
5
5
  platform: ruby
6
6
  authors:
7
7
  - Jens Wille
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2014-08-05 00:00:00.000000000 Z
11
+ date: 2014-09-26 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: hen
@@ -66,6 +66,7 @@ files:
66
66
  - README
67
67
  - Rakefile
68
68
  - lib/docker_helper.rb
69
+ - lib/docker_helper/pool.rb
69
70
  - lib/docker_helper/proxy.rb
70
71
  - lib/docker_helper/version.rb
71
72
  - spec/docker_helper_spec.rb
@@ -76,13 +77,15 @@ licenses:
76
77
  metadata: {}
77
78
  post_install_message: |2+
78
79
 
79
- docker_helper-0.0.1 [2014-08-05]:
80
+ docker_helper-0.0.2 [2014-09-26]:
80
81
 
81
- * First release.
82
+ * Added DockerHelper#docker_ready.
83
+ * Added DockerHelper#docker_port.
84
+ * Added DockerHelper::Pool.
82
85
 
83
86
  rdoc_options:
84
87
  - "--title"
85
- - docker_helper Application documentation (v0.0.1)
88
+ - docker_helper Application documentation (v0.0.2)
86
89
  - "--charset"
87
90
  - UTF-8
88
91
  - "--line-numbers"