einhorn 0.7.4 → 1.0.0
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 +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
|
-

|
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
|