waitutil 0.1.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +15 -0
- data/.gitignore +28 -0
- data/.travis.yml +7 -0
- data/.yardopts +2 -0
- data/Gemfile +3 -0
- data/README.md +35 -0
- data/Rakefile +14 -0
- data/VERSION +1 -0
- data/lib/waitutil/version.rb +3 -0
- data/lib/waitutil.rb +78 -0
- data/spec/Rakefile +0 -0
- data/spec/waitutil_spec.rb +135 -0
- data/waitutil.gemspec +25 -0
- metadata +113 -0
checksums.yaml
ADDED
@@ -0,0 +1,15 @@
|
|
1
|
+
---
|
2
|
+
!binary "U0hBMQ==":
|
3
|
+
metadata.gz: !binary |-
|
4
|
+
ZjhlODkyZTdjOTQwNjVjNTFmZGMzYjkwYjdkNWRjOWZlZWM1NzQ3Mw==
|
5
|
+
data.tar.gz: !binary |-
|
6
|
+
ZTc0Y2RlYTM5MjZmZjI2YjIxMmYzNDZmY2U4OGJmYzljOTljNzljZQ==
|
7
|
+
SHA512:
|
8
|
+
metadata.gz: !binary |-
|
9
|
+
NDhkZWMzZWE1MDEyMDYxYmNjNTZhMDBlNDIxYjkwOWU4ZWM4NjBiOWRiMWVj
|
10
|
+
MjU2YjNmZWRlMjFjZGIxY2Y5YWRmMDhlZDQyY2ZmNmU1MDJlZmQxNDRkMDBk
|
11
|
+
N2IyM2Y0N2JiNmYyYTBjYmNmNGUzYmJlZDY5OGEyNmE0ZTA5ZWE=
|
12
|
+
data.tar.gz: !binary |-
|
13
|
+
NDM4N2Q3YTMxZGEwNmIwMzdlZDViYWZkOWQ3ODM3ODk2NDM1ODIwNjRkYzRh
|
14
|
+
NmFkMjIxODkyODQwNDlkOTYxNDgwZmI1MmM3YThhYjdhN2M3NzhhYTIxNDAw
|
15
|
+
NjYyYjkzOGUyOWFkMWU2NmViMjI4ODQ2MjcyODc5ZGJmOGMyMzE=
|
data/.gitignore
ADDED
@@ -0,0 +1,28 @@
|
|
1
|
+
*.gem
|
2
|
+
*.rbc
|
3
|
+
/.config
|
4
|
+
/coverage/
|
5
|
+
/InstalledFiles
|
6
|
+
/pkg/
|
7
|
+
/spec/reports/
|
8
|
+
/test/tmp/
|
9
|
+
/test/version_tmp/
|
10
|
+
/tmp/
|
11
|
+
|
12
|
+
## Documentation cache and generated files:
|
13
|
+
/.yardoc/
|
14
|
+
/_yardoc/
|
15
|
+
/doc/
|
16
|
+
/rdoc/
|
17
|
+
|
18
|
+
## Environment normalisation:
|
19
|
+
/.bundle/
|
20
|
+
/lib/bundler/man/
|
21
|
+
|
22
|
+
Gemfile.lock
|
23
|
+
.ruby-version
|
24
|
+
.ruby-gemset
|
25
|
+
|
26
|
+
# unless supporting rvm < 1.11.0 or doing something fancy, ignore this:
|
27
|
+
.rvmrc
|
28
|
+
|
data/.travis.yml
ADDED
data/.yardopts
ADDED
data/Gemfile
ADDED
data/README.md
ADDED
@@ -0,0 +1,35 @@
|
|
1
|
+
## waitutil
|
2
|
+
|
3
|
+
[![Build Status](https://travis-ci.org/mbautin/waitutil.png?branch=master)](https://travis-ci.org/mbautin/waitutil)
|
4
|
+
|
5
|
+
`waitutil` provides tools for waiting for various conditions to occur, with a configurable
|
6
|
+
delay time, timeout, and logging.
|
7
|
+
|
8
|
+
### Examples
|
9
|
+
|
10
|
+
#### Waiting for conditions
|
11
|
+
|
12
|
+
Maximum wait time is one minute by default, and the delay time is one second.
|
13
|
+
```ruby
|
14
|
+
WaitUtil.wait_for_condition("my_event to happen") do
|
15
|
+
check_if_my_event_happened
|
16
|
+
end
|
17
|
+
```
|
18
|
+
|
19
|
+
Customized wait time and delay time:
|
20
|
+
```ruby
|
21
|
+
WaitUtil.wait_for_condition("my_event to happen", :timeout_sec => 30, :delay_sec => 0.5) do
|
22
|
+
check_if_my_event_happened
|
23
|
+
end
|
24
|
+
```
|
25
|
+
|
26
|
+
#### Waiting for service availability
|
27
|
+
|
28
|
+
Wait for a TCP server to be available:
|
29
|
+
```ruby
|
30
|
+
WaitUtil.wait_for_service('example.com', 8080)
|
31
|
+
```
|
32
|
+
|
33
|
+
### License
|
34
|
+
|
35
|
+
[Apache License, Version 2.0](http://www.apache.org/licenses/LICENSE-2.0.html)
|
data/Rakefile
ADDED
@@ -0,0 +1,14 @@
|
|
1
|
+
$LOAD_PATH.unshift File.expand_path("../lib", __FILE__)
|
2
|
+
|
3
|
+
require 'bundler'
|
4
|
+
require 'rspec/core/rake_task'
|
5
|
+
require 'rake/clean'
|
6
|
+
require 'rubygems/tasks'
|
7
|
+
|
8
|
+
CLEAN.include("**/*.gem")
|
9
|
+
|
10
|
+
Bundler::GemHelper.install_tasks
|
11
|
+
|
12
|
+
RSpec::Core::RakeTask.new(:spec)
|
13
|
+
|
14
|
+
task :default => :spec
|
data/VERSION
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
0.1.0
|
data/lib/waitutil.rb
ADDED
@@ -0,0 +1,78 @@
|
|
1
|
+
require 'logger'
|
2
|
+
|
3
|
+
module WaitUtil
|
4
|
+
|
5
|
+
extend self
|
6
|
+
|
7
|
+
class TimeoutError < StandardError
|
8
|
+
end
|
9
|
+
|
10
|
+
DEFAULT_TIMEOUT_SEC = 60
|
11
|
+
DEFAULT_DELAY_SEC = 1
|
12
|
+
|
13
|
+
@@logger = Logger.new(STDOUT)
|
14
|
+
@@logger.level = Logger::INFO
|
15
|
+
|
16
|
+
def self.logger
|
17
|
+
@@logger
|
18
|
+
end
|
19
|
+
|
20
|
+
# Wait until the condition computed by the given block is met. The supplied block may return a
|
21
|
+
# boolean or an array of two elements: whether the condition has been met and an additional
|
22
|
+
# message to display in case of timeout.
|
23
|
+
def wait_for_condition(description, options = {}, &block)
|
24
|
+
delay_sec = options.delete(:delay_sec) || DEFAULT_DELAY_SEC
|
25
|
+
timeout_sec = options.delete(:timeout_sec) || DEFAULT_TIMEOUT_SEC
|
26
|
+
verbose = options.delete(:verbose)
|
27
|
+
unless options.empty?
|
28
|
+
raise "Invalid options: #{options}"
|
29
|
+
end
|
30
|
+
|
31
|
+
if verbose
|
32
|
+
@@logger.info("Waiting for #{description} for up to #{timeout_sec} seconds")
|
33
|
+
end
|
34
|
+
|
35
|
+
start_time = Time.now
|
36
|
+
iteration = 0
|
37
|
+
until is_condition_met(condition_result = yield(iteration))
|
38
|
+
if Time.now - start_time >= timeout_sec
|
39
|
+
raise TimeoutError.new(
|
40
|
+
"Timed out waiting for #{description} (#{timeout_sec} seconds elapsed)" +
|
41
|
+
get_additional_message(condition_result)
|
42
|
+
)
|
43
|
+
end
|
44
|
+
sleep(delay_sec)
|
45
|
+
iteration += 1
|
46
|
+
end
|
47
|
+
if verbose
|
48
|
+
@@logger.info("Success waiting for #{description} (#{Time.now - start_time} seconds)")
|
49
|
+
end
|
50
|
+
true
|
51
|
+
end
|
52
|
+
|
53
|
+
# Wait until a service is available at the given host/port.
|
54
|
+
def wait_for_service(description, host, port, options = {})
|
55
|
+
wait_for_condition("#{description} port #{port} to become available on #{host}",
|
56
|
+
options) do
|
57
|
+
begin
|
58
|
+
s = TCPSocket.new(host, port)
|
59
|
+
s.close
|
60
|
+
true
|
61
|
+
rescue Errno::ECONNREFUSED, Errno::EHOSTUNREACH
|
62
|
+
false
|
63
|
+
end
|
64
|
+
end
|
65
|
+
end
|
66
|
+
|
67
|
+
private
|
68
|
+
|
69
|
+
def is_condition_met(condition_result)
|
70
|
+
condition_result.kind_of?(Array) ? condition_result[0] : condition_result
|
71
|
+
end
|
72
|
+
|
73
|
+
def get_additional_message(condition_result)
|
74
|
+
condition_result.kind_of?(Array) ? ': ' + condition_result[1] : ''
|
75
|
+
end
|
76
|
+
|
77
|
+
extend WaitUtil
|
78
|
+
end
|
data/spec/Rakefile
ADDED
File without changes
|
@@ -0,0 +1,135 @@
|
|
1
|
+
require 'waitutil'
|
2
|
+
require 'socket'
|
3
|
+
|
4
|
+
RSpec.configure do |configuration|
|
5
|
+
configuration.include WaitUtil
|
6
|
+
end
|
7
|
+
|
8
|
+
describe WaitUtil do
|
9
|
+
describe '.wait_for_condition' do
|
10
|
+
it 'logs if the verbose option is specified' do
|
11
|
+
iterations = []
|
12
|
+
WaitUtil.logger.should_receive(:info).with('Waiting for true for up to 60 seconds')
|
13
|
+
WaitUtil.logger.should_receive(:info) do |msg|
|
14
|
+
msg =~ /^Success waiting for true \(.*\)$/
|
15
|
+
end
|
16
|
+
|
17
|
+
ret = wait_for_condition('true', :verbose => true) do |iteration|
|
18
|
+
iterations << iteration
|
19
|
+
true
|
20
|
+
end
|
21
|
+
expect(ret).to be_true
|
22
|
+
expect(iterations).to eq([0])
|
23
|
+
end
|
24
|
+
|
25
|
+
it 'returns immediately if the condition is true' do
|
26
|
+
iterations = []
|
27
|
+
ret = wait_for_condition('true') {|iteration| iterations << iteration; true }
|
28
|
+
expect(ret).to be_true
|
29
|
+
expect(iterations).to eq([0])
|
30
|
+
end
|
31
|
+
|
32
|
+
it 'should time out if the condition is always false' do
|
33
|
+
iterations = []
|
34
|
+
start_time = Time.now
|
35
|
+
begin
|
36
|
+
wait_for_condition('false', :timeout_sec => 0.1, :delay_sec => 0.01) do |iteration|
|
37
|
+
iterations << iteration
|
38
|
+
false
|
39
|
+
end
|
40
|
+
fail 'Expected an exception'
|
41
|
+
rescue WaitUtil::TimeoutError => ex
|
42
|
+
expect(ex.to_s).to match(/^Timed out waiting for false /)
|
43
|
+
end
|
44
|
+
elapsed_sec = Time.now - start_time
|
45
|
+
expect(elapsed_sec).to be >= 0.1
|
46
|
+
expect(iterations.length).to be >= 9
|
47
|
+
expect(iterations.length).to be <= 11
|
48
|
+
expect(iterations).to eq((0..iterations.length - 1).to_a)
|
49
|
+
end
|
50
|
+
|
51
|
+
it 'should handle additional messages from the block' do
|
52
|
+
begin
|
53
|
+
wait_for_condition('false', :timeout_sec => 0.01, :delay_sec => 0.05) do |iteration|
|
54
|
+
[false, 'Some error']
|
55
|
+
end
|
56
|
+
fail 'Expected an exception'
|
57
|
+
rescue WaitUtil::TimeoutError => ex
|
58
|
+
expect(ex.to_s).to match(/^Timed out waiting for false (.*): Some error$/)
|
59
|
+
end
|
60
|
+
end
|
61
|
+
|
62
|
+
it 'should treat the first element of returned tuple as condition status' do
|
63
|
+
iterations = []
|
64
|
+
ret = wait_for_condition('some condition', :timeout_sec => 1, :delay_sec => 0) do |iteration|
|
65
|
+
iterations << iteration
|
66
|
+
[iteration >= 3, 'some message']
|
67
|
+
end
|
68
|
+
expect(ret).to be_true
|
69
|
+
expect(iterations).to eq([0, 1, 2, 3])
|
70
|
+
end
|
71
|
+
|
72
|
+
it 'should evaluate the block return value as a boolean if it is not an array' do
|
73
|
+
iterations = []
|
74
|
+
ret = wait_for_condition('some condition', :timeout_sec => 1, :delay_sec => 0) do |iteration|
|
75
|
+
iterations << iteration
|
76
|
+
iteration >= 3
|
77
|
+
end
|
78
|
+
expect(ret).to be_true
|
79
|
+
expect(iterations).to eq([0, 1, 2, 3])
|
80
|
+
end
|
81
|
+
end
|
82
|
+
|
83
|
+
describe '.wait_for_service' do
|
84
|
+
BIND_IP = '127.0.0.1'
|
85
|
+
|
86
|
+
it 'should succeed immediately when there is a TCP server listening' do
|
87
|
+
# Find an unused port.
|
88
|
+
socket = Socket.new(:INET, :STREAM, 0)
|
89
|
+
sockaddr = if RUBY_ENGINE == 'jruby'
|
90
|
+
ServerSocket.pack_sockaddr_in(12345, "127.0.0.1")
|
91
|
+
else
|
92
|
+
Addrinfo.tcp(BIND_IP, 0)
|
93
|
+
end
|
94
|
+
socket.bind(sockaddr)
|
95
|
+
port = socket.local_address.ip_port
|
96
|
+
socket.close
|
97
|
+
|
98
|
+
server_thread = Thread.new do
|
99
|
+
server = TCPServer.new(port)
|
100
|
+
loop do
|
101
|
+
client = server.accept # Wait for a client to connect
|
102
|
+
client.puts "Hello !"
|
103
|
+
client.close
|
104
|
+
break
|
105
|
+
end
|
106
|
+
end
|
107
|
+
|
108
|
+
wait_for_service('wait for my service', BIND_IP, port, :delay_sec => 0.1, :timeout_sec => 0.3)
|
109
|
+
end
|
110
|
+
|
111
|
+
it 'should fail when there is no TCP server listening' do
|
112
|
+
port = nil
|
113
|
+
# Find a port that no one is listening on.
|
114
|
+
attempts = 0
|
115
|
+
while attempts < 100
|
116
|
+
port = 32768 + rand(61000 - 32768)
|
117
|
+
begin
|
118
|
+
TCPSocket.new(BIND_IP, port)
|
119
|
+
port = nil
|
120
|
+
rescue Errno::ECONNREFUSED
|
121
|
+
break
|
122
|
+
end
|
123
|
+
attempts += 1
|
124
|
+
end
|
125
|
+
fail 'Could not find a port no one is listening on' unless port
|
126
|
+
|
127
|
+
expect {
|
128
|
+
wait_for_service(
|
129
|
+
'wait for non-existent service', BIND_IP, port, :delay_sec => 0.1, :timeout_sec => 0.3
|
130
|
+
)
|
131
|
+
}.to raise_error(WaitUtil::TimeoutError)
|
132
|
+
end
|
133
|
+
|
134
|
+
end
|
135
|
+
end
|
data/waitutil.gemspec
ADDED
@@ -0,0 +1,25 @@
|
|
1
|
+
# -*- encoding: utf-8 -*-
|
2
|
+
GEM_NAME = 'waitutil'
|
3
|
+
|
4
|
+
require File.expand_path("../lib/#{GEM_NAME}/version", __FILE__)
|
5
|
+
|
6
|
+
Gem::Specification.new do |gem|
|
7
|
+
gem.authors = ['Mikhail Bautin']
|
8
|
+
gem.email = ['mbautin@gmail.com']
|
9
|
+
gem.description = 'Utilities for waiting for various conditions'
|
10
|
+
gem.summary = 'Utilities for waiting for various conditions'
|
11
|
+
gem.homepage = "http://github.com/mbautin/#{GEM_NAME}"
|
12
|
+
|
13
|
+
gem.executables = `git ls-files -- bin/*`.split("\n").map{ |f| File.basename(f) }
|
14
|
+
gem.files = `git ls-files`.split("\n").map(&:strip)
|
15
|
+
gem.test_files = `git ls-files -- {test,spec,features}/*`.split("\n")
|
16
|
+
gem.name = GEM_NAME
|
17
|
+
gem.require_paths = ['lib']
|
18
|
+
gem.version = WaitUtil::VERSION
|
19
|
+
|
20
|
+
gem.add_development_dependency 'rake', '~> 10.1'
|
21
|
+
gem.add_development_dependency 'rspec', '~> 2.14'
|
22
|
+
gem.add_development_dependency 'rubygems-tasks', '~> 0.2'
|
23
|
+
|
24
|
+
gem.add_development_dependency 'webrick'
|
25
|
+
end
|
metadata
ADDED
@@ -0,0 +1,113 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: waitutil
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 0.1.0
|
5
|
+
platform: ruby
|
6
|
+
authors:
|
7
|
+
- Mikhail Bautin
|
8
|
+
autorequire:
|
9
|
+
bindir: bin
|
10
|
+
cert_chain: []
|
11
|
+
date: 2014-02-15 00:00:00.000000000 Z
|
12
|
+
dependencies:
|
13
|
+
- !ruby/object:Gem::Dependency
|
14
|
+
name: rake
|
15
|
+
requirement: !ruby/object:Gem::Requirement
|
16
|
+
requirements:
|
17
|
+
- - ~>
|
18
|
+
- !ruby/object:Gem::Version
|
19
|
+
version: '10.1'
|
20
|
+
type: :development
|
21
|
+
prerelease: false
|
22
|
+
version_requirements: !ruby/object:Gem::Requirement
|
23
|
+
requirements:
|
24
|
+
- - ~>
|
25
|
+
- !ruby/object:Gem::Version
|
26
|
+
version: '10.1'
|
27
|
+
- !ruby/object:Gem::Dependency
|
28
|
+
name: rspec
|
29
|
+
requirement: !ruby/object:Gem::Requirement
|
30
|
+
requirements:
|
31
|
+
- - ~>
|
32
|
+
- !ruby/object:Gem::Version
|
33
|
+
version: '2.14'
|
34
|
+
type: :development
|
35
|
+
prerelease: false
|
36
|
+
version_requirements: !ruby/object:Gem::Requirement
|
37
|
+
requirements:
|
38
|
+
- - ~>
|
39
|
+
- !ruby/object:Gem::Version
|
40
|
+
version: '2.14'
|
41
|
+
- !ruby/object:Gem::Dependency
|
42
|
+
name: rubygems-tasks
|
43
|
+
requirement: !ruby/object:Gem::Requirement
|
44
|
+
requirements:
|
45
|
+
- - ~>
|
46
|
+
- !ruby/object:Gem::Version
|
47
|
+
version: '0.2'
|
48
|
+
type: :development
|
49
|
+
prerelease: false
|
50
|
+
version_requirements: !ruby/object:Gem::Requirement
|
51
|
+
requirements:
|
52
|
+
- - ~>
|
53
|
+
- !ruby/object:Gem::Version
|
54
|
+
version: '0.2'
|
55
|
+
- !ruby/object:Gem::Dependency
|
56
|
+
name: webrick
|
57
|
+
requirement: !ruby/object:Gem::Requirement
|
58
|
+
requirements:
|
59
|
+
- - ! '>='
|
60
|
+
- !ruby/object:Gem::Version
|
61
|
+
version: '0'
|
62
|
+
type: :development
|
63
|
+
prerelease: false
|
64
|
+
version_requirements: !ruby/object:Gem::Requirement
|
65
|
+
requirements:
|
66
|
+
- - ! '>='
|
67
|
+
- !ruby/object:Gem::Version
|
68
|
+
version: '0'
|
69
|
+
description: Utilities for waiting for various conditions
|
70
|
+
email:
|
71
|
+
- mbautin@gmail.com
|
72
|
+
executables: []
|
73
|
+
extensions: []
|
74
|
+
extra_rdoc_files: []
|
75
|
+
files:
|
76
|
+
- .gitignore
|
77
|
+
- .travis.yml
|
78
|
+
- .yardopts
|
79
|
+
- Gemfile
|
80
|
+
- README.md
|
81
|
+
- Rakefile
|
82
|
+
- VERSION
|
83
|
+
- lib/waitutil.rb
|
84
|
+
- lib/waitutil/version.rb
|
85
|
+
- spec/Rakefile
|
86
|
+
- spec/waitutil_spec.rb
|
87
|
+
- waitutil.gemspec
|
88
|
+
homepage: http://github.com/mbautin/waitutil
|
89
|
+
licenses: []
|
90
|
+
metadata: {}
|
91
|
+
post_install_message:
|
92
|
+
rdoc_options: []
|
93
|
+
require_paths:
|
94
|
+
- lib
|
95
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
96
|
+
requirements:
|
97
|
+
- - ! '>='
|
98
|
+
- !ruby/object:Gem::Version
|
99
|
+
version: '0'
|
100
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
101
|
+
requirements:
|
102
|
+
- - ! '>='
|
103
|
+
- !ruby/object:Gem::Version
|
104
|
+
version: '0'
|
105
|
+
requirements: []
|
106
|
+
rubyforge_project:
|
107
|
+
rubygems_version: 2.1.11
|
108
|
+
signing_key:
|
109
|
+
specification_version: 4
|
110
|
+
summary: Utilities for waiting for various conditions
|
111
|
+
test_files:
|
112
|
+
- spec/Rakefile
|
113
|
+
- spec/waitutil_spec.rb
|