einhorn 0.7.4 → 1.0.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +7 -0
- data/Changes.md +10 -0
- data/README.md +36 -30
- data/bin/einhorn +17 -2
- data/einhorn.gemspec +23 -21
- data/example/pool_worker.rb +1 -1
- data/example/thin_example +8 -8
- data/example/time_server +5 -5
- data/lib/einhorn/client.rb +8 -9
- data/lib/einhorn/command/interface.rb +100 -95
- data/lib/einhorn/command.rb +167 -88
- data/lib/einhorn/compat.rb +7 -7
- data/lib/einhorn/event/abstract_text_descriptor.rb +31 -35
- data/lib/einhorn/event/ack_timer.rb +2 -2
- data/lib/einhorn/event/command_server.rb +7 -9
- data/lib/einhorn/event/connection.rb +1 -3
- data/lib/einhorn/event/loop_breaker.rb +2 -1
- data/lib/einhorn/event/persistent.rb +2 -2
- data/lib/einhorn/event/timer.rb +4 -4
- data/lib/einhorn/event.rb +29 -20
- data/lib/einhorn/prctl.rb +26 -0
- data/lib/einhorn/prctl_linux.rb +48 -0
- data/lib/einhorn/safe_yaml.rb +17 -0
- data/lib/einhorn/version.rb +1 -1
- data/lib/einhorn/worker.rb +67 -49
- data/lib/einhorn/worker_pool.rb +9 -9
- data/lib/einhorn.rb +155 -126
- metadata +42 -137
- data/.gitignore +0 -17
- data/.travis.yml +0 -10
- data/CONTRIBUTORS +0 -6
- data/Gemfile +0 -11
- data/History.txt +0 -4
- data/README.md.in +0 -76
- data/Rakefile +0 -27
- data/test/_lib.rb +0 -12
- data/test/integration/_lib/fixtures/env_printer/env_printer.rb +0 -26
- data/test/integration/_lib/fixtures/exit_during_upgrade/exiting_server.rb +0 -22
- data/test/integration/_lib/fixtures/exit_during_upgrade/upgrade_reexec.rb +0 -6
- data/test/integration/_lib/fixtures/upgrade_project/upgrading_server.rb +0 -22
- data/test/integration/_lib/helpers/einhorn_helpers.rb +0 -143
- data/test/integration/_lib/helpers.rb +0 -4
- data/test/integration/_lib.rb +0 -6
- data/test/integration/startup.rb +0 -31
- data/test/integration/upgrading.rb +0 -157
- data/test/unit/einhorn/client.rb +0 -88
- data/test/unit/einhorn/command/interface.rb +0 -49
- data/test/unit/einhorn/command.rb +0 -21
- data/test/unit/einhorn/event.rb +0 -89
- data/test/unit/einhorn/worker_pool.rb +0 -39
- data/test/unit/einhorn.rb +0 -58
- /data/{LICENSE → LICENSE.txt} +0 -0
metadata
CHANGED
@@ -1,149 +1,88 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: einhorn
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.
|
5
|
-
prerelease:
|
4
|
+
version: 1.0.0
|
6
5
|
platform: ruby
|
7
6
|
authors:
|
8
|
-
-
|
9
|
-
|
7
|
+
- Stripe
|
8
|
+
- Mike Perham
|
9
|
+
autorequire:
|
10
10
|
bindir: bin
|
11
11
|
cert_chain: []
|
12
|
-
date:
|
12
|
+
date: 2022-07-14 00:00:00.000000000 Z
|
13
13
|
dependencies:
|
14
|
-
- !ruby/object:Gem::Dependency
|
15
|
-
name: rack
|
16
|
-
requirement: !ruby/object:Gem::Requirement
|
17
|
-
none: false
|
18
|
-
requirements:
|
19
|
-
- - ~>
|
20
|
-
- !ruby/object:Gem::Version
|
21
|
-
version: '1.6'
|
22
|
-
type: :development
|
23
|
-
prerelease: false
|
24
|
-
version_requirements: !ruby/object:Gem::Requirement
|
25
|
-
none: false
|
26
|
-
requirements:
|
27
|
-
- - ~>
|
28
|
-
- !ruby/object:Gem::Version
|
29
|
-
version: '1.6'
|
30
14
|
- !ruby/object:Gem::Dependency
|
31
15
|
name: rake
|
32
16
|
requirement: !ruby/object:Gem::Requirement
|
33
|
-
none: false
|
34
|
-
requirements:
|
35
|
-
- - ! '>='
|
36
|
-
- !ruby/object:Gem::Version
|
37
|
-
version: '0'
|
38
|
-
type: :development
|
39
|
-
prerelease: false
|
40
|
-
version_requirements: !ruby/object:Gem::Requirement
|
41
|
-
none: false
|
42
|
-
requirements:
|
43
|
-
- - ! '>='
|
44
|
-
- !ruby/object:Gem::Version
|
45
|
-
version: '0'
|
46
|
-
- !ruby/object:Gem::Dependency
|
47
|
-
name: pry
|
48
|
-
requirement: !ruby/object:Gem::Requirement
|
49
|
-
none: false
|
50
17
|
requirements:
|
51
|
-
- -
|
18
|
+
- - "~>"
|
52
19
|
- !ruby/object:Gem::Version
|
53
|
-
version: '
|
20
|
+
version: '13'
|
54
21
|
type: :development
|
55
22
|
prerelease: false
|
56
23
|
version_requirements: !ruby/object:Gem::Requirement
|
57
|
-
none: false
|
58
24
|
requirements:
|
59
|
-
- -
|
25
|
+
- - "~>"
|
60
26
|
- !ruby/object:Gem::Version
|
61
|
-
version: '
|
27
|
+
version: '13'
|
62
28
|
- !ruby/object:Gem::Dependency
|
63
29
|
name: minitest
|
64
30
|
requirement: !ruby/object:Gem::Requirement
|
65
|
-
none: false
|
66
31
|
requirements:
|
67
|
-
- -
|
32
|
+
- - "~>"
|
68
33
|
- !ruby/object:Gem::Version
|
69
|
-
version: '5
|
34
|
+
version: '5'
|
70
35
|
type: :development
|
71
36
|
prerelease: false
|
72
37
|
version_requirements: !ruby/object:Gem::Requirement
|
73
|
-
none: false
|
74
38
|
requirements:
|
75
|
-
- -
|
39
|
+
- - "~>"
|
76
40
|
- !ruby/object:Gem::Version
|
77
|
-
version: '5
|
41
|
+
version: '5'
|
78
42
|
- !ruby/object:Gem::Dependency
|
79
43
|
name: mocha
|
80
44
|
requirement: !ruby/object:Gem::Requirement
|
81
|
-
none: false
|
82
|
-
requirements:
|
83
|
-
- - ~>
|
84
|
-
- !ruby/object:Gem::Version
|
85
|
-
version: '0.13'
|
86
|
-
type: :development
|
87
|
-
prerelease: false
|
88
|
-
version_requirements: !ruby/object:Gem::Requirement
|
89
|
-
none: false
|
90
|
-
requirements:
|
91
|
-
- - ~>
|
92
|
-
- !ruby/object:Gem::Version
|
93
|
-
version: '0.13'
|
94
|
-
- !ruby/object:Gem::Dependency
|
95
|
-
name: chalk-rake
|
96
|
-
requirement: !ruby/object:Gem::Requirement
|
97
|
-
none: false
|
98
45
|
requirements:
|
99
|
-
- -
|
46
|
+
- - "~>"
|
100
47
|
- !ruby/object:Gem::Version
|
101
|
-
version: '
|
48
|
+
version: '1'
|
102
49
|
type: :development
|
103
50
|
prerelease: false
|
104
51
|
version_requirements: !ruby/object:Gem::Requirement
|
105
|
-
none: false
|
106
52
|
requirements:
|
107
|
-
- -
|
53
|
+
- - "~>"
|
108
54
|
- !ruby/object:Gem::Version
|
109
|
-
version: '
|
55
|
+
version: '1'
|
110
56
|
- !ruby/object:Gem::Dependency
|
111
57
|
name: subprocess
|
112
58
|
requirement: !ruby/object:Gem::Requirement
|
113
|
-
none: false
|
114
59
|
requirements:
|
115
|
-
- -
|
60
|
+
- - "~>"
|
116
61
|
- !ruby/object:Gem::Version
|
117
|
-
version: '
|
62
|
+
version: '1'
|
118
63
|
type: :development
|
119
64
|
prerelease: false
|
120
65
|
version_requirements: !ruby/object:Gem::Requirement
|
121
|
-
none: false
|
122
66
|
requirements:
|
123
|
-
- -
|
67
|
+
- - "~>"
|
124
68
|
- !ruby/object:Gem::Version
|
125
|
-
version: '
|
69
|
+
version: '1'
|
126
70
|
description: Einhorn makes it easy to run multiple instances of an application server,
|
127
71
|
all listening on the same port. You can also seamlessly restart your workers without
|
128
72
|
dropping any requests. Einhorn requires minimal application-level support, making
|
129
73
|
it easy to use with an existing project.
|
130
74
|
email:
|
131
|
-
-
|
75
|
+
- support+github@stripe.com
|
76
|
+
- mperham@gmail.com
|
132
77
|
executables:
|
133
78
|
- einhorn
|
134
79
|
- einhornsh
|
135
80
|
extensions: []
|
136
81
|
extra_rdoc_files: []
|
137
82
|
files:
|
138
|
-
- .
|
139
|
-
- .
|
140
|
-
- CONTRIBUTORS
|
141
|
-
- Gemfile
|
142
|
-
- History.txt
|
143
|
-
- LICENSE
|
83
|
+
- Changes.md
|
84
|
+
- LICENSE.txt
|
144
85
|
- README.md
|
145
|
-
- README.md.in
|
146
|
-
- Rakefile
|
147
86
|
- bin/einhorn
|
148
87
|
- bin/einhornsh
|
149
88
|
- einhorn.gemspec
|
@@ -163,71 +102,37 @@ files:
|
|
163
102
|
- lib/einhorn/event/loop_breaker.rb
|
164
103
|
- lib/einhorn/event/persistent.rb
|
165
104
|
- lib/einhorn/event/timer.rb
|
105
|
+
- lib/einhorn/prctl.rb
|
106
|
+
- lib/einhorn/prctl_linux.rb
|
107
|
+
- lib/einhorn/safe_yaml.rb
|
166
108
|
- lib/einhorn/third.rb
|
167
109
|
- lib/einhorn/version.rb
|
168
110
|
- lib/einhorn/worker.rb
|
169
111
|
- lib/einhorn/worker_pool.rb
|
170
|
-
|
171
|
-
- test/integration/_lib.rb
|
172
|
-
- test/integration/_lib/fixtures/env_printer/env_printer.rb
|
173
|
-
- test/integration/_lib/fixtures/exit_during_upgrade/exiting_server.rb
|
174
|
-
- test/integration/_lib/fixtures/exit_during_upgrade/upgrade_reexec.rb
|
175
|
-
- test/integration/_lib/fixtures/upgrade_project/upgrading_server.rb
|
176
|
-
- test/integration/_lib/helpers.rb
|
177
|
-
- test/integration/_lib/helpers/einhorn_helpers.rb
|
178
|
-
- test/integration/startup.rb
|
179
|
-
- test/integration/upgrading.rb
|
180
|
-
- test/unit/einhorn.rb
|
181
|
-
- test/unit/einhorn/client.rb
|
182
|
-
- test/unit/einhorn/command.rb
|
183
|
-
- test/unit/einhorn/command/interface.rb
|
184
|
-
- test/unit/einhorn/event.rb
|
185
|
-
- test/unit/einhorn/worker_pool.rb
|
186
|
-
homepage: https://github.com/stripe/einhorn
|
112
|
+
homepage: https://github.com/contribsys/einhorn
|
187
113
|
licenses:
|
188
114
|
- MIT
|
189
|
-
|
115
|
+
metadata:
|
116
|
+
bug_tracker_uri: https://github.com/contribsys/einhorn/issues
|
117
|
+
documentation_uri: https://github.com/contribsys/einhorn/wiki
|
118
|
+
changelog_uri: https://github.com/contribsys/einhorn/blob/main/Changes.md
|
119
|
+
post_install_message:
|
190
120
|
rdoc_options: []
|
191
121
|
require_paths:
|
192
122
|
- lib
|
193
123
|
required_ruby_version: !ruby/object:Gem::Requirement
|
194
|
-
none: false
|
195
124
|
requirements:
|
196
|
-
- -
|
125
|
+
- - ">="
|
197
126
|
- !ruby/object:Gem::Version
|
198
|
-
version:
|
199
|
-
segments:
|
200
|
-
- 0
|
201
|
-
hash: 4291999791412545660
|
127
|
+
version: 2.5.0
|
202
128
|
required_rubygems_version: !ruby/object:Gem::Requirement
|
203
|
-
none: false
|
204
129
|
requirements:
|
205
|
-
- -
|
130
|
+
- - ">="
|
206
131
|
- !ruby/object:Gem::Version
|
207
132
|
version: '0'
|
208
|
-
segments:
|
209
|
-
- 0
|
210
|
-
hash: 4291999791412545660
|
211
133
|
requirements: []
|
212
|
-
|
213
|
-
|
214
|
-
|
215
|
-
|
216
|
-
|
217
|
-
test_files:
|
218
|
-
- test/_lib.rb
|
219
|
-
- test/integration/_lib.rb
|
220
|
-
- test/integration/_lib/fixtures/env_printer/env_printer.rb
|
221
|
-
- test/integration/_lib/fixtures/exit_during_upgrade/exiting_server.rb
|
222
|
-
- test/integration/_lib/fixtures/exit_during_upgrade/upgrade_reexec.rb
|
223
|
-
- test/integration/_lib/fixtures/upgrade_project/upgrading_server.rb
|
224
|
-
- test/integration/_lib/helpers.rb
|
225
|
-
- test/integration/_lib/helpers/einhorn_helpers.rb
|
226
|
-
- test/integration/startup.rb
|
227
|
-
- test/integration/upgrading.rb
|
228
|
-
- test/unit/einhorn.rb
|
229
|
-
- test/unit/einhorn/client.rb
|
230
|
-
- test/unit/einhorn/command.rb
|
231
|
-
- test/unit/einhorn/command/interface.rb
|
232
|
-
- test/unit/einhorn/event.rb
|
233
|
-
- test/unit/einhorn/worker_pool.rb
|
134
|
+
rubygems_version: 3.2.32
|
135
|
+
signing_key:
|
136
|
+
specification_version: 4
|
137
|
+
summary: 'Einhorn: the language-independent shared socket manager'
|
138
|
+
test_files: []
|
data/.gitignore
DELETED
data/.travis.yml
DELETED
data/CONTRIBUTORS
DELETED
data/Gemfile
DELETED
@@ -1,11 +0,0 @@
|
|
1
|
-
# Execute bundler hook if present
|
2
|
-
['~/.', '/etc/'].any? do |file|
|
3
|
-
File.lstat(path = File.expand_path(file + 'bundle-gemfile-hook')) rescue next
|
4
|
-
eval(File.read(path), binding, path); break true
|
5
|
-
end || source('https://rubygems.org/')
|
6
|
-
|
7
|
-
# Only needed for examples
|
8
|
-
gem 'thin-attach_socket'
|
9
|
-
|
10
|
-
# Specify your gem's dependencies in einhorn.gemspec
|
11
|
-
gemspec
|
data/History.txt
DELETED
data/README.md.in
DELETED
@@ -1,76 +0,0 @@
|
|
1
|
-
# Einhorn: the language-independent shared socket manager
|
2
|
-
|
3
|
-
![Einhorn](https://stripe.com/img/blog/posts/meet-einhorn/einhorn.png)
|
4
|
-
|
5
|
-
Let's say you have a server process which processes one request at a
|
6
|
-
time. Your site is becoming increasingly popular, and this one process
|
7
|
-
is no longer able to handle all of your inbound connections. However,
|
8
|
-
you notice that your box's load number is low.
|
9
|
-
|
10
|
-
So you start thinking about how to handle more requests. You could
|
11
|
-
rewrite your server to use threads, but threads are a pain to program
|
12
|
-
against (and maybe you're writing in Python or Ruby where you don't
|
13
|
-
have true threads anyway). You could rewrite your server to be
|
14
|
-
event-driven, but that'd require a ton of effort, and it wouldn't help
|
15
|
-
you go beyond one core. So instead, you decide to just run multiple
|
16
|
-
copies of your server process.
|
17
|
-
|
18
|
-
Enter Einhorn. Einhorn makes it easy to run (and keep alive) multiple
|
19
|
-
copies of a single long-lived process. If that process is a server
|
20
|
-
listening on some socket, Einhorn will open the socket in the master
|
21
|
-
process so that it's shared among the workers.
|
22
|
-
|
23
|
-
Einhorn is designed to be compatible with arbitrary languages and
|
24
|
-
frameworks, requiring minimal modification of your
|
25
|
-
application. Einhorn is simple to configure and run.
|
26
|
-
|
27
|
-
## Installation
|
28
|
-
|
29
|
-
Install from Rubygems as:
|
30
|
-
|
31
|
-
$ gem install einhorn
|
32
|
-
|
33
|
-
Or build from source by:
|
34
|
-
|
35
|
-
$ gem build einhorn.gemspec
|
36
|
-
|
37
|
-
And then install the built gem.
|
38
|
-
|
39
|
-
[[usage]]
|
40
|
-
|
41
|
-
## Contributing
|
42
|
-
|
43
|
-
Contributions are definitely welcome. To contribute, just follow the
|
44
|
-
usual workflow:
|
45
|
-
|
46
|
-
1. Fork Einhorn
|
47
|
-
2. Create your feature branch (`git checkout -b my-new-feature`)
|
48
|
-
3. Commit your changes (`git commit -am 'Added some feature'`)
|
49
|
-
4. Push to the branch (`git push origin my-new-feature`)
|
50
|
-
5. Create new Github pull request
|
51
|
-
|
52
|
-
## History
|
53
|
-
|
54
|
-
Einhorn came about when Stripe was investigating seamless code
|
55
|
-
upgrading solutions for our API worker processes. We really liked the
|
56
|
-
process model of [Unicorn](http://unicorn.bogomips.org/), but didn't
|
57
|
-
want to use its HTTP functionality. So Einhorn was born, providing the
|
58
|
-
master process functionality of Unicorn (and similar preforking
|
59
|
-
servers) to a wider array of applications.
|
60
|
-
|
61
|
-
See https://stripe.com/blog/meet-einhorn for more background.
|
62
|
-
|
63
|
-
Stripe currently uses Einhorn in production for a number of
|
64
|
-
services. You can use Conrad Irwin's thin-attach_socket gem along with
|
65
|
-
EventMachine-LE to support file-descriptor passing. Check out
|
66
|
-
`example/thin_example` for an example of running Thin under Einhorn.
|
67
|
-
|
68
|
-
## Compatibility
|
69
|
-
|
70
|
-
Einhorn was developed and tested under Ruby 1.8.7.
|
71
|
-
|
72
|
-
## About
|
73
|
-
|
74
|
-
Einhorn is a project of [Stripe](https://stripe.com), led by [Greg
|
75
|
-
Brockman](https://twitter.com/thegdb). Feel free to get in touch at
|
76
|
-
info@stripe.com.
|
data/Rakefile
DELETED
@@ -1,27 +0,0 @@
|
|
1
|
-
#!/usr/bin/env rake
|
2
|
-
require 'bundler/gem_tasks'
|
3
|
-
require 'rake/testtask'
|
4
|
-
|
5
|
-
desc 'Rebuild the README with the latest usage from einhorn'
|
6
|
-
task :readme do
|
7
|
-
Dir.chdir(File.dirname(__FILE__))
|
8
|
-
readme = File.read('README.md.in')
|
9
|
-
usage = `bin/einhorn -h`
|
10
|
-
readme.gsub!('[[usage]]', usage)
|
11
|
-
File.open('README.md', 'w') {|f| f.write(readme)}
|
12
|
-
end
|
13
|
-
|
14
|
-
task :default => :test do
|
15
|
-
end
|
16
|
-
require 'bundler/setup'
|
17
|
-
require 'chalk-rake/gem_tasks'
|
18
|
-
require 'rake/testtask'
|
19
|
-
|
20
|
-
Rake::TestTask.new do |t|
|
21
|
-
t.libs = ['lib']
|
22
|
-
# t.warning = true
|
23
|
-
t.verbose = true
|
24
|
-
t.test_files = FileList['test/**/*.rb'].reject do |file|
|
25
|
-
file.end_with?('_lib.rb') || file.include?('/_lib/')
|
26
|
-
end
|
27
|
-
end
|
data/test/_lib.rb
DELETED
@@ -1,26 +0,0 @@
|
|
1
|
-
require 'bundler/setup'
|
2
|
-
require 'socket'
|
3
|
-
require 'einhorn/worker'
|
4
|
-
|
5
|
-
def einhorn_main
|
6
|
-
$stderr.puts "Worker starting up!"
|
7
|
-
serv = Socket.for_fd(ENV['EINHORN_FD_0'].to_i)
|
8
|
-
$stderr.puts "Worker has a socket"
|
9
|
-
Einhorn::Worker.ack!
|
10
|
-
$stderr.puts "Worker sent ack to einhorn"
|
11
|
-
$stdout.puts "Environment from #{Process.pid} is: #{ENV.inspect}"
|
12
|
-
while true
|
13
|
-
s, addrinfo = serv.accept
|
14
|
-
$stderr.puts "Worker got a socket!"
|
15
|
-
output = ""
|
16
|
-
ARGV.each do |variable_to_write|
|
17
|
-
output += ENV[variable_to_write].to_s
|
18
|
-
end
|
19
|
-
s.write(output)
|
20
|
-
s.flush
|
21
|
-
s.close
|
22
|
-
$stderr.puts "Worker closed its socket"
|
23
|
-
end
|
24
|
-
end
|
25
|
-
|
26
|
-
einhorn_main if $0 == __FILE__
|
@@ -1,22 +0,0 @@
|
|
1
|
-
require 'bundler/setup'
|
2
|
-
require 'socket'
|
3
|
-
require 'einhorn/worker'
|
4
|
-
|
5
|
-
def einhorn_main
|
6
|
-
serv = Socket.for_fd(Einhorn::Worker.socket!)
|
7
|
-
Einhorn::Worker.ack!
|
8
|
-
|
9
|
-
Signal.trap('USR2') do
|
10
|
-
sleep 3
|
11
|
-
exit!
|
12
|
-
end
|
13
|
-
|
14
|
-
while true
|
15
|
-
s, _ = serv.accept
|
16
|
-
s.write($$)
|
17
|
-
s.flush
|
18
|
-
s.close
|
19
|
-
end
|
20
|
-
end
|
21
|
-
|
22
|
-
einhorn_main if $0 == __FILE__
|
@@ -1,22 +0,0 @@
|
|
1
|
-
require 'bundler/setup'
|
2
|
-
require 'socket'
|
3
|
-
require 'einhorn/worker'
|
4
|
-
|
5
|
-
def einhorn_main
|
6
|
-
version = File.read(File.join(File.dirname(__FILE__), "version"))
|
7
|
-
$stderr.puts "Worker starting up!"
|
8
|
-
serv = Socket.for_fd(ENV['EINHORN_FD_0'].to_i)
|
9
|
-
$stderr.puts "Worker has a socket"
|
10
|
-
Einhorn::Worker.ack!
|
11
|
-
$stderr.puts "Worker sent ack to einhorn"
|
12
|
-
while true
|
13
|
-
s, addrinfo = serv.accept
|
14
|
-
$stderr.puts "Worker got a socket!"
|
15
|
-
s.write(version)
|
16
|
-
s.flush
|
17
|
-
s.close
|
18
|
-
$stderr.puts "Worker closed its socket"
|
19
|
-
end
|
20
|
-
end
|
21
|
-
|
22
|
-
einhorn_main if $0 == __FILE__
|
@@ -1,143 +0,0 @@
|
|
1
|
-
require 'subprocess'
|
2
|
-
require 'timeout'
|
3
|
-
require 'tmpdir'
|
4
|
-
|
5
|
-
module Helpers
|
6
|
-
module EinhornHelpers
|
7
|
-
def einhorn_code_dir
|
8
|
-
File.expand_path('../../../../', File.dirname(__FILE__))
|
9
|
-
end
|
10
|
-
|
11
|
-
def default_einhorn_command
|
12
|
-
cmd = ['bundle', 'exec']
|
13
|
-
cmd << '--keep-file-descriptors' if RUBY_VERSION >= '2.0'
|
14
|
-
cmd << File.expand_path('bin/einhorn', einhorn_code_dir)
|
15
|
-
|
16
|
-
cmd
|
17
|
-
end
|
18
|
-
|
19
|
-
def with_running_einhorn(cmdline, options = {})
|
20
|
-
options = options.dup
|
21
|
-
einhorn_command = options.delete(:einhorn_command) { default_einhorn_command }
|
22
|
-
expected_exit_code = options.delete(:expected_exit_code) { nil }
|
23
|
-
output_callback = options.delete(:output_callback) { nil }
|
24
|
-
|
25
|
-
stdout, stderr = "", ""
|
26
|
-
communicator = nil
|
27
|
-
process = Bundler.with_clean_env do
|
28
|
-
default_options = {
|
29
|
-
:stdout => Subprocess::PIPE,
|
30
|
-
:stderr => Subprocess::PIPE,
|
31
|
-
:stdin => '/dev/null',
|
32
|
-
:cwd => einhorn_code_dir
|
33
|
-
}
|
34
|
-
Subprocess::Process.new(Array(einhorn_command) + cmdline, default_options.merge(options))
|
35
|
-
end
|
36
|
-
|
37
|
-
status = nil
|
38
|
-
begin
|
39
|
-
communicator = Thread.new do
|
40
|
-
begin
|
41
|
-
stdout, stderr = process.communicate
|
42
|
-
rescue Errno::ECHILD
|
43
|
-
# It's dead, and we're not getting anything. This is peaceful.
|
44
|
-
end
|
45
|
-
end
|
46
|
-
yield(process) if block_given?
|
47
|
-
rescue
|
48
|
-
unless (status = process.poll) && status.exited?
|
49
|
-
process.terminate
|
50
|
-
end
|
51
|
-
raise
|
52
|
-
ensure
|
53
|
-
unless (status = process.poll) && status.exited?
|
54
|
-
for i in 1..10 do
|
55
|
-
status = process.poll
|
56
|
-
if status && status.exited?
|
57
|
-
break
|
58
|
-
end
|
59
|
-
sleep(1)
|
60
|
-
end
|
61
|
-
unless status && status.exited?
|
62
|
-
$stderr.puts "Could not get Einhorn to quit within 10 seconds, killing it forcefully..."
|
63
|
-
process.send_signal("KILL")
|
64
|
-
status = process.wait
|
65
|
-
end
|
66
|
-
end
|
67
|
-
communicator.join
|
68
|
-
output_callback.call(stdout, stderr) if output_callback
|
69
|
-
end
|
70
|
-
end
|
71
|
-
|
72
|
-
def einhornsh(commandline, options = {})
|
73
|
-
Subprocess.check_call(%W{bundle exec #{File.expand_path('bin/einhornsh')}} + commandline,
|
74
|
-
{
|
75
|
-
:stdin => '/dev/null',
|
76
|
-
:stdout => '/dev/null',
|
77
|
-
:stderr => '/dev/null'
|
78
|
-
}.merge(options))
|
79
|
-
end
|
80
|
-
|
81
|
-
def fixture_path(name)
|
82
|
-
File.expand_path(File.join('../fixtures', name), File.dirname(__FILE__))
|
83
|
-
end
|
84
|
-
|
85
|
-
# Creates a new temporary directory with the initial contents from
|
86
|
-
# test/integration/_lib/fixtures/{name} and returns the path to
|
87
|
-
# it. The contents of this directory are temporary and can be
|
88
|
-
# safely overwritten.
|
89
|
-
def prepare_fixture_directory(name)
|
90
|
-
@fixtured_dirs ||= Set.new
|
91
|
-
new_dir = Dir.mktmpdir(name)
|
92
|
-
@fixtured_dirs << new_dir
|
93
|
-
FileUtils.cp_r(File.join(fixture_path(name), '.'), new_dir, :preserve => true)
|
94
|
-
|
95
|
-
new_dir
|
96
|
-
end
|
97
|
-
|
98
|
-
def cleanup_fixtured_directories
|
99
|
-
(@fixtured_dirs || []).each { |dir| FileUtils.rm_rf(dir) }
|
100
|
-
end
|
101
|
-
|
102
|
-
def find_free_port(host='127.0.0.1')
|
103
|
-
open_port = TCPServer.new(host, 0)
|
104
|
-
open_port.addr[1]
|
105
|
-
ensure
|
106
|
-
open_port.close
|
107
|
-
end
|
108
|
-
|
109
|
-
def wait_for_open_port
|
110
|
-
max_retries = 50
|
111
|
-
begin
|
112
|
-
read_from_port
|
113
|
-
rescue Errno::ECONNREFUSED
|
114
|
-
max_retries -= 1
|
115
|
-
if max_retries <= 0
|
116
|
-
raise
|
117
|
-
else
|
118
|
-
sleep 0.1
|
119
|
-
retry
|
120
|
-
end
|
121
|
-
end
|
122
|
-
end
|
123
|
-
|
124
|
-
|
125
|
-
def read_from_port
|
126
|
-
ewouldblock = RUBY_VERSION >= '1.9.0' ? IO::WaitWritable : Errno::EINPROGRESS
|
127
|
-
socket = Socket.new(Socket::PF_INET, Socket::SOCK_STREAM, 0)
|
128
|
-
sockaddr = Socket.pack_sockaddr_in(@port, '127.0.0.1')
|
129
|
-
begin
|
130
|
-
socket.connect_nonblock(sockaddr)
|
131
|
-
rescue ewouldblock
|
132
|
-
IO.select(nil, [socket], [], 5)
|
133
|
-
begin
|
134
|
-
socket.connect_nonblock(sockaddr)
|
135
|
-
rescue Errno::EISCONN
|
136
|
-
end
|
137
|
-
end
|
138
|
-
socket.read.chomp
|
139
|
-
ensure
|
140
|
-
socket.close if socket
|
141
|
-
end
|
142
|
-
end
|
143
|
-
end
|