docker_helper 0.0.1 → 0.0.2

Sign up to get free protection for your applications and to get access to all the features.
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"