zkexec 0.0.1
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +7 -0
- data/.gitignore +17 -0
- data/Gemfile +4 -0
- data/LICENSE.txt +22 -0
- data/README.md +160 -0
- data/Rakefile +1 -0
- data/bin/zkexec +7 -0
- data/lib/zkexec.rb +15 -0
- data/lib/zkexec/executor.rb +182 -0
- data/lib/zkexec/runner.rb +82 -0
- data/lib/zkexec/version.rb +3 -0
- data/test/libexec/prefixed-cat +7 -0
- data/test/libexec/slowcheck +11 -0
- data/test/zk_exec_test.rb +27 -0
- data/zkexec.gemspec +25 -0
- metadata +104 -0
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
SHA1:
|
3
|
+
metadata.gz: a532a346ae00de20b532490448e745ae1132a57f
|
4
|
+
data.tar.gz: 08435fec85406659ec1f6266b598d9af96612c9c
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: 84d232792462ce12690fb431f57240402e6570e58a5e4cf9c082f329f4211e79c25c9d2c6624d72d0e431db6361c89337fc00f4708bae92a2f3801a73fca6d49
|
7
|
+
data.tar.gz: 1067682fb483530f67a73fd071fd0fa9a57e0eb1ab55dca74253c14b2a57b45dff270f91fc9f3c9fa42555c7f60d77a0b0e2bd9bedb9b947bce6b64a799ddcb3
|
data/.gitignore
ADDED
data/Gemfile
ADDED
data/LICENSE.txt
ADDED
@@ -0,0 +1,22 @@
|
|
1
|
+
Copyright (c) 2014 Kyle Maxwell
|
2
|
+
|
3
|
+
MIT License
|
4
|
+
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining
|
6
|
+
a copy of this software and associated documentation files (the
|
7
|
+
"Software"), to deal in the Software without restriction, including
|
8
|
+
without limitation the rights to use, copy, modify, merge, publish,
|
9
|
+
distribute, sublicense, and/or sell copies of the Software, and to
|
10
|
+
permit persons to whom the Software is furnished to do so, subject to
|
11
|
+
the following conditions:
|
12
|
+
|
13
|
+
The above copyright notice and this permission notice shall be
|
14
|
+
included in all copies or substantial portions of the Software.
|
15
|
+
|
16
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
17
|
+
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
18
|
+
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
19
|
+
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
20
|
+
LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
21
|
+
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
22
|
+
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
data/README.md
ADDED
@@ -0,0 +1,160 @@
|
|
1
|
+
# zkexec
|
2
|
+
|
3
|
+
zkexec is a wrapper around an executable that imports configuration from a zookeeper cluster. When zookeeper configuration changes, zkexec syncs the configuration, then restarts the executable. zkexec supports rolling restarts, health checks, and alerts.
|
4
|
+
|
5
|
+
Warning: this code is not yet being used in production.
|
6
|
+
|
7
|
+
## Tutorial
|
8
|
+
|
9
|
+
### Ante
|
10
|
+
|
11
|
+
1. Get a local zookeeper running on :2181 (the default port).
|
12
|
+
2. Checkout this git repo and cd into it.
|
13
|
+
3. Either `gem install zkexec`, or `bundle; export PATH=$PATH:bin`
|
14
|
+
|
15
|
+
### Calling zkexec
|
16
|
+
|
17
|
+
We'll start with the contrived example of running a script that acts just like cat, but it prefixes each line with the contents of a file:
|
18
|
+
|
19
|
+
:$ echo -n hello > /tmp/prefix
|
20
|
+
:$ echo world | ./test/libexec/prefixed-cat /tmp/prefix
|
21
|
+
hello world
|
22
|
+
|
23
|
+
We want to run this in the console, such that we can type input in stdin, and get our prefixed output. Here's the invokation (feel free to remove the --silent):
|
24
|
+
|
25
|
+
:$ zkexec --silent --exec "./test/libexec/prefixed-cat /tmp/prefix"
|
26
|
+
|
27
|
+
This doesn't do anything special--it just wraps the execution. Go ahead and run that, then type a few lines. Ctrl-c away, check the return code. It should behave just like `prefixed-cat`.
|
28
|
+
|
29
|
+
### Updating the config files
|
30
|
+
|
31
|
+
Let's put the prefix in zookeeper. We'll throw it in the root, and use the --mirror flag to let zkexec know about it.
|
32
|
+
|
33
|
+
:$ zookeeper/bin/zkCli.sh -cmd create /prefix hello
|
34
|
+
:$ zkexec --silent \
|
35
|
+
--exec "./test/libexec/prefixed-cat /tmp/prefix" \
|
36
|
+
--mirror /tmp/prefix=/prefix
|
37
|
+
world # typed into stdin
|
38
|
+
hello world # stdout
|
39
|
+
|
40
|
+
So, now that we're tracking the config file, lets change it in another console. Leave the existing terminal running.
|
41
|
+
|
42
|
+
:$ zookeeper/bin/zkCli.sh -cmd set /prefix goodbye
|
43
|
+
|
44
|
+
Now, flip back to the original terminal and start typing. You'll note that now the prefix is `goodbye`!
|
45
|
+
|
46
|
+
### Edge cases
|
47
|
+
|
48
|
+
#### What if you run a command that terminates?
|
49
|
+
|
50
|
+
zkexec will exit with the same error code. Try it with ``/usr/bin/false`! (oh and I'll take this opportunity to show the default logs that show up on stderr when you don't pass the --silent flag)
|
51
|
+
|
52
|
+
:$ zkexec --exec false # $ zkexec --exec false
|
53
|
+
[2014-04-28 16:41:38 -0700] connecting to localhost:2181
|
54
|
+
[2014-04-28 16:41:38 -0700] connected
|
55
|
+
[2014-04-28 16:41:38 -0700] forking false
|
56
|
+
[2014-04-28 16:41:38 -0700] forked 41804
|
57
|
+
[2014-04-28 16:41:38 -0700] command failed
|
58
|
+
:$ echo $?
|
59
|
+
1
|
60
|
+
|
61
|
+
This means that you should have something (monit, etc) watching zkexec. zkexec is not trying to be an all purpose monitoring solution.
|
62
|
+
|
63
|
+
### Adding health checks
|
64
|
+
|
65
|
+
A health check is simply an executable that returns 0 when the system is healthy, and non-zero otherwise. For this tutorial, we'll use `./test/libexec/slowcheck`. slowcheck takes two arguments, (1) the tcp port to check, and (2) the number of seconds to sleep before checking it. If and only if the port is open, slowcheck will succeed. The sleep will be useful later.
|
66
|
+
|
67
|
+
Let's append a health check to the previous command
|
68
|
+
|
69
|
+
:$ zkexec \
|
70
|
+
--exec "./test/libexec/prefixed-cat /tmp/prefix" \
|
71
|
+
--mirror /tmp/prefix=/prefix \
|
72
|
+
--health "./test/libexec/slowcheck 2181 1" \
|
73
|
+
--health-delay 1 \
|
74
|
+
--health-interval 1
|
75
|
+
|
76
|
+
The health-delay is the interval before the first health check (useful if your script is slow to start), and after the service starts, the health check will run every health-interval seconds. It's up to you to make the health check script reasonable. zkexec will happily run absurd scripts without timeouts, etc.
|
77
|
+
|
78
|
+
In this case, we're actually checking zookeeper's port. There's nothing special or required about that, it just happened to be convenient filler.
|
79
|
+
|
80
|
+
If you aren't running in silent mode, you'll see output like:
|
81
|
+
|
82
|
+
[2014-04-28 16:51:07 -0700] connecting to localhost:2181
|
83
|
+
[2014-04-28 16:51:07 -0700] connected
|
84
|
+
[2014-04-28 16:51:07 -0700] registering callback on /prefix
|
85
|
+
[2014-04-28 16:51:07 -0700] forking ./test/libexec/prefixed-cat /tmp/prefix
|
86
|
+
[2014-04-28 16:51:07 -0700] forked 42333
|
87
|
+
[2014-04-28 16:51:08 -0700] health checking via: ./test/libexec/slowcheck 2181 1
|
88
|
+
[2014-04-28 16:51:09 -0700] successful health check
|
89
|
+
[2014-04-28 16:51:10 -0700] health checking via: ./test/libexec/slowcheck 2181 1
|
90
|
+
[2014-04-28 16:51:12 -0700] successful health check
|
91
|
+
|
92
|
+
|
93
|
+
### Adding alerts
|
94
|
+
|
95
|
+
An alert is simply an executable that gets called when a health check fails unexpectedly. Alerts won't trigger within `health-delay` seconds of a restart.
|
96
|
+
|
97
|
+
Alerts are provided for your convenience. In most cases, it's better to just add a dead man's switch to your health check.
|
98
|
+
|
99
|
+
#### A better alert scheme
|
100
|
+
|
101
|
+
Install a dead man's switch via monit via something similar to this sample code:
|
102
|
+
|
103
|
+
:$ mkdir -p /var/run/foo
|
104
|
+
:$ echo "check file succeeded\_at path /var/run/foo/succeeded\_at
|
105
|
+
if timestamp < 5 minutes then alert" > /etc/monit/conf.d/foo.conf
|
106
|
+
:$ sudo monit reload
|
107
|
+
:$
|
108
|
+
:$ zkexec \
|
109
|
+
...
|
110
|
+
--health "./test/libexec/slowcheck 2181 1 && touch /tmp/succeeded_at"
|
111
|
+
|
112
|
+
### Rolling restarts
|
113
|
+
|
114
|
+
Use `--lock NAME` to establish a rolling restart group. When a config file changes, all wrapped processes sharing the lock name and the zookeeper cluster must acquire the lock before killing the child process. A zkexec only releases the lock when health checks succeed or the zkexec parent process is killed.
|
115
|
+
|
116
|
+
Give it a try locally. In this case, we'll use the slowness of the health check script to our advantage, because we can watch the restart execute serially via the timestamps of the logs.
|
117
|
+
|
118
|
+
Run two or more of the following command in separate terminals:
|
119
|
+
|
120
|
+
:$ zkexec \
|
121
|
+
--exec "./test/libexec/prefixed-cat /tmp/prefix" \
|
122
|
+
--lock foo \
|
123
|
+
--health "test/libexec/slowcheck 2181 10" \
|
124
|
+
--mirror /tmp/prefix=/prefix \
|
125
|
+
--health-delay 20 \
|
126
|
+
--health-interval 1
|
127
|
+
|
128
|
+
Then, run the following:
|
129
|
+
|
130
|
+
:$ zookeeper/bin/zkCli.sh -cmd set /prefix woot
|
131
|
+
|
132
|
+
You can watch the timestamps of the zkexec processes as they log on stdout, noting that they take turns restarting.
|
133
|
+
|
134
|
+
#### Config failures in rolling restart
|
135
|
+
|
136
|
+
A config failure during a restart is defined as either (1) the restarted process exiting non-zero, or (2) the restarted process failing health checks for `health-delay` seconds.
|
137
|
+
|
138
|
+
A config failure pauses the restart. If this happens, you should push new config, and if it doesn't fail, the restart will pick up where it left off.
|
139
|
+
|
140
|
+
### All options
|
141
|
+
|
142
|
+
zkexec doesn't use config files. All options are documented on the command line `--help`:
|
143
|
+
|
144
|
+
:$ zkexec -h
|
145
|
+
Usage: zkexec [options]
|
146
|
+
|
147
|
+
Run a command, and restart if the config files change on the remote zookeeper.
|
148
|
+
|
149
|
+
-e, --exec COMMAND Run this command
|
150
|
+
-c, --cluster HOST:PORT,... Comma-delimited list of zookeeper hosts
|
151
|
+
-H, --health COMMAND Run this command to health-check
|
152
|
+
-i, --health-interval INTERVAL Health-check every INTERVAL seconds
|
153
|
+
-d, --health-delay INTERVAL Wait before starting health checks
|
154
|
+
-m, --mirror LOCAL_PATH=ZK_PATH Mirror a config file from zookeeper to localhost
|
155
|
+
-l, --lock NAME Name of a zk lockfile, used to enforce rolling restarts
|
156
|
+
-a, --alert COMMAND Run this command if the primary command returns
|
157
|
+
falsey or health checks fail for too long
|
158
|
+
-v, --verbose
|
159
|
+
-s, --silent
|
160
|
+
-h, --help Show this message
|
data/Rakefile
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
require "bundler/gem_tasks"
|
data/bin/zkexec
ADDED
data/lib/zkexec.rb
ADDED
@@ -0,0 +1,182 @@
|
|
1
|
+
require "zk"
|
2
|
+
require "thread"
|
3
|
+
|
4
|
+
module ZkExec
|
5
|
+
class Executor
|
6
|
+
include ZkExec
|
7
|
+
include Process
|
8
|
+
|
9
|
+
def initialize(options)
|
10
|
+
@cmd = options[:exec]
|
11
|
+
@cluster = options[:cluster]
|
12
|
+
@health = options[:health]
|
13
|
+
@health_interval = options[:health_interval]
|
14
|
+
@health_delay = options[:health_delay]
|
15
|
+
@mirrors = options[:mirrors]
|
16
|
+
@alert = options[:alert]
|
17
|
+
@lock_name = options[:lock]
|
18
|
+
|
19
|
+
log "connecting to #{@cluster}"
|
20
|
+
@zk = ZK.new(@cluster, :thread => :per_callback)
|
21
|
+
raise "timeout connecting to #{@cluster}" unless @zk.connected?
|
22
|
+
log "connected"
|
23
|
+
|
24
|
+
# re-establish watches
|
25
|
+
@on_connected ||= @zk.on_connected do
|
26
|
+
@mirrors.each do |(local, remote)|
|
27
|
+
watch(remote)
|
28
|
+
end
|
29
|
+
end
|
30
|
+
|
31
|
+
@restart_lock = @lock_name && @zk.exclusive_locker(@lock_name)
|
32
|
+
@local_lock = Mutex.new
|
33
|
+
|
34
|
+
@mirrors.each do |(local, remote)|
|
35
|
+
log "registering callback on #{remote}"
|
36
|
+
@zk.register(remote) do |event|
|
37
|
+
if event.changed?
|
38
|
+
log "#{remote} changed"
|
39
|
+
copy(local, remote)
|
40
|
+
kill_to_refork
|
41
|
+
else
|
42
|
+
watch(remote)
|
43
|
+
end
|
44
|
+
end
|
45
|
+
watch(remote)
|
46
|
+
end
|
47
|
+
end
|
48
|
+
|
49
|
+
private
|
50
|
+
def copy(local, remote)
|
51
|
+
data = watch(remote)
|
52
|
+
File.open(local, "w") {|f| f.print(data) }
|
53
|
+
rescue ZK::Exceptions::NoNode => e
|
54
|
+
raise "node not found in #{e.message}"
|
55
|
+
end
|
56
|
+
|
57
|
+
private
|
58
|
+
def watch(remote)
|
59
|
+
data, stat = *@zk.get(remote, :watch => true)
|
60
|
+
return data
|
61
|
+
rescue ZK::Exceptions::NoNode => e
|
62
|
+
raise "node not found in #{e.message}"
|
63
|
+
end
|
64
|
+
|
65
|
+
private
|
66
|
+
def with_restart_lock
|
67
|
+
if @restart_lock
|
68
|
+
begin
|
69
|
+
log "waiting on lock: #{@lock_name}"
|
70
|
+
@restart_lock.lock(:wait => true)
|
71
|
+
log "acquired lock: #{@lock_name}"
|
72
|
+
yield
|
73
|
+
ensure
|
74
|
+
@restart_lock.unlock
|
75
|
+
log "released lock: #{@lock_name}"
|
76
|
+
end
|
77
|
+
else
|
78
|
+
yield
|
79
|
+
end
|
80
|
+
end
|
81
|
+
|
82
|
+
private
|
83
|
+
def pid_exists?(pid)
|
84
|
+
begin
|
85
|
+
Process.getpgid(pid)
|
86
|
+
true
|
87
|
+
rescue Errno::ESRCH
|
88
|
+
false
|
89
|
+
end
|
90
|
+
end
|
91
|
+
|
92
|
+
private
|
93
|
+
def kill_to_refork
|
94
|
+
if @child
|
95
|
+
with_restart_lock do
|
96
|
+
if @health_checks
|
97
|
+
Thread.kill(@health_checks)
|
98
|
+
@health_checks = nil
|
99
|
+
end
|
100
|
+
@should_refork = true
|
101
|
+
child = @child
|
102
|
+
@child = nil
|
103
|
+
|
104
|
+
log "killing #{child}"
|
105
|
+
Process.kill("TERM", child)
|
106
|
+
checks_started = Time.now
|
107
|
+
while pid_exists?(child) && Time.now - checks_started < 30
|
108
|
+
sleep 1
|
109
|
+
end
|
110
|
+
if pid_exists?(child)
|
111
|
+
log "force killing #{child}"
|
112
|
+
Process.kill("KILL", child)
|
113
|
+
end
|
114
|
+
|
115
|
+
log "#{child} terminated"
|
116
|
+
|
117
|
+
# This intentionally infinite loops on failure, so that we don't propagate bad config
|
118
|
+
health_checks_started = Time.now
|
119
|
+
loop do
|
120
|
+
log "waiting for health check success"
|
121
|
+
if system(@health)
|
122
|
+
log "health checks succeeding"
|
123
|
+
return
|
124
|
+
end
|
125
|
+
if Time.now - health_checks_started > @health_delay
|
126
|
+
log "health checks failing"
|
127
|
+
alert
|
128
|
+
end
|
129
|
+
sleep @health_interval
|
130
|
+
end
|
131
|
+
end
|
132
|
+
end
|
133
|
+
end
|
134
|
+
|
135
|
+
def alert
|
136
|
+
if @alert
|
137
|
+
fork { exec(@alert) }
|
138
|
+
end
|
139
|
+
end
|
140
|
+
|
141
|
+
def start_health_thread
|
142
|
+
@health_checks ||= @health && Thread.new {
|
143
|
+
sleep @health_delay
|
144
|
+
loop do
|
145
|
+
log "health checking via: #{@health}"
|
146
|
+
pid = fork { exec(@health) }
|
147
|
+
wait pid
|
148
|
+
if $?.exitstatus == 0
|
149
|
+
log "successful health check"
|
150
|
+
else
|
151
|
+
log "failed health check, alerting"
|
152
|
+
alert
|
153
|
+
end
|
154
|
+
sleep @health_interval
|
155
|
+
end
|
156
|
+
}
|
157
|
+
end
|
158
|
+
|
159
|
+
public
|
160
|
+
def run
|
161
|
+
Thread.new { execute }
|
162
|
+
end
|
163
|
+
|
164
|
+
def execute
|
165
|
+
@should_refork = true
|
166
|
+
|
167
|
+
while @should_refork
|
168
|
+
start_health_thread
|
169
|
+
log "forking #{@cmd}"
|
170
|
+
@child = fork { exec @cmd }
|
171
|
+
log "forked #{@child}"
|
172
|
+
@should_refork = false
|
173
|
+
wait @child
|
174
|
+
end
|
175
|
+
|
176
|
+
if $?.exitstatus != 0
|
177
|
+
alert
|
178
|
+
raise "command failed"
|
179
|
+
end
|
180
|
+
end
|
181
|
+
end
|
182
|
+
end
|
@@ -0,0 +1,82 @@
|
|
1
|
+
require "optparse"
|
2
|
+
|
3
|
+
module ZkExec
|
4
|
+
class Runner
|
5
|
+
include ZkExec
|
6
|
+
|
7
|
+
def initialize
|
8
|
+
end
|
9
|
+
|
10
|
+
def run(args)
|
11
|
+
options = { :mirrors => [], :cluster => "localhost:2181", :health_interval => 30, :health_delay => 30 }
|
12
|
+
opts = OptionParser.new do |opts|
|
13
|
+
opts.banner = "Usage: zkexec [options]\n\nRun a command, and restart if the config files change on the remote zookeeper.\n\n"
|
14
|
+
|
15
|
+
opts.on("-e", "--exec COMMAND", "Run this command") do |s|
|
16
|
+
options[:exec] = s
|
17
|
+
end
|
18
|
+
|
19
|
+
opts.on("-c", "--cluster HOST:PORT,...", "Comma-delimited list of zookeeper hosts") do |s|
|
20
|
+
options[:cluster] = s
|
21
|
+
end
|
22
|
+
|
23
|
+
opts.on("-H", "--health COMMAND", "Run this command to health-check") do |s|
|
24
|
+
options[:health] = s
|
25
|
+
end
|
26
|
+
|
27
|
+
opts.on("-i", "--health-interval INTERVAL", "Health-check every INTERVAL seconds") do |s|
|
28
|
+
options[:health_interval] = s.to_f
|
29
|
+
end
|
30
|
+
|
31
|
+
opts.on("-d", "--health-delay INTERVAL", "Wait before starting health checks") do |s|
|
32
|
+
options[:health_delay] = s.to_f
|
33
|
+
end
|
34
|
+
|
35
|
+
opts.on("-m", "--mirror LOCAL_PATH=ZK_PATH", "Mirror a config file from zookeeper to localhost") do |s|
|
36
|
+
options[:mirrors] << s.split("=")
|
37
|
+
end
|
38
|
+
|
39
|
+
opts.on("-l", "--lock NAME", "Name of a zk lockfile, used to enforce rolling restarts") do |s|
|
40
|
+
options[:lock] = s
|
41
|
+
end
|
42
|
+
|
43
|
+
opts.on("-a", "--alert COMMAND", "Run this command if the primary command returns","falsey or health checks fail for too long") do |s|
|
44
|
+
options[:alert] = s
|
45
|
+
end
|
46
|
+
|
47
|
+
opts.on("-v", "--verbose") do
|
48
|
+
options[:verbose] = true
|
49
|
+
end
|
50
|
+
|
51
|
+
opts.on("-s", "--silent") do
|
52
|
+
options[:silent] = true
|
53
|
+
end
|
54
|
+
|
55
|
+
opts.on_tail("-h", "--help", "Show this message") do
|
56
|
+
puts opts
|
57
|
+
exit
|
58
|
+
end
|
59
|
+
end
|
60
|
+
|
61
|
+
opts.parse!
|
62
|
+
|
63
|
+
unless options[:exec]
|
64
|
+
puts "Missing required option --exec. See --help for usage."
|
65
|
+
exit 1
|
66
|
+
end
|
67
|
+
|
68
|
+
silence! if options[:silent]
|
69
|
+
|
70
|
+
begin
|
71
|
+
Executor.new(options).execute
|
72
|
+
rescue => e
|
73
|
+
if options[:verbose]
|
74
|
+
raise
|
75
|
+
else
|
76
|
+
log(e.message)
|
77
|
+
exit 1
|
78
|
+
end
|
79
|
+
end
|
80
|
+
end
|
81
|
+
end
|
82
|
+
end
|
@@ -0,0 +1,27 @@
|
|
1
|
+
gem "minitest"
|
2
|
+
require "minitest/autorun"
|
3
|
+
require "minitest/spec"
|
4
|
+
require "socket"
|
5
|
+
|
6
|
+
TCPSocket.new("localhost", 2181).close \
|
7
|
+
rescue abort "No zookeeper running, please start a local one!"
|
8
|
+
|
9
|
+
ZKEXEC = File.expand_path(File.join(File.dirname(__FILE__), "..", "bin", "zkexec"))
|
10
|
+
|
11
|
+
describe "zkexec" do
|
12
|
+
it "should fail when run without args" do
|
13
|
+
system("#{ZKEXEC}").must_equal(false)
|
14
|
+
end
|
15
|
+
|
16
|
+
it "should run with exec" do # note this is /usr/bin/true or equivalent
|
17
|
+
system("#{ZKEXEC} --exec true").must_equal(true)
|
18
|
+
end
|
19
|
+
|
20
|
+
it "should keep the inner response code" do # note this is /usr/bin/false or equivalent
|
21
|
+
system("#{ZKEXEC} --exec false").must_equal(false)
|
22
|
+
end
|
23
|
+
|
24
|
+
it "should alert on failure" do
|
25
|
+
`#{ZKEXEC} --exec false --alert 'echo FOO'`.must_match(/FOO/)
|
26
|
+
end
|
27
|
+
end
|
data/zkexec.gemspec
ADDED
@@ -0,0 +1,25 @@
|
|
1
|
+
# coding: utf-8
|
2
|
+
lib = File.expand_path('../lib', __FILE__)
|
3
|
+
$LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
|
4
|
+
require 'zkexec/version'
|
5
|
+
|
6
|
+
Gem::Specification.new do |spec|
|
7
|
+
spec.name = "zkexec"
|
8
|
+
spec.version = ZkExec::VERSION
|
9
|
+
spec.authors = ["Kyle Maxwell"]
|
10
|
+
spec.email = ["kyle@kylemaxwell.com"]
|
11
|
+
spec.summary = %q{Run a process in a wrapper that manages config files from zookeeper}
|
12
|
+
spec.description = %q{Run a process in a wrapper that manages config files from zookeeper}
|
13
|
+
spec.homepage = ""
|
14
|
+
spec.license = "MIT"
|
15
|
+
|
16
|
+
spec.files = `git ls-files -z`.split("\x0")
|
17
|
+
spec.executables = spec.files.grep(%r{^bin/}) { |f| File.basename(f) }
|
18
|
+
spec.test_files = spec.files.grep(%r{^(test|spec|features)/})
|
19
|
+
spec.require_paths = ["lib"]
|
20
|
+
|
21
|
+
spec.add_runtime_dependency "zk", "1.9.4"
|
22
|
+
|
23
|
+
spec.add_development_dependency "bundler", "~> 1.5"
|
24
|
+
spec.add_development_dependency "rake"
|
25
|
+
end
|
metadata
ADDED
@@ -0,0 +1,104 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: zkexec
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 0.0.1
|
5
|
+
platform: ruby
|
6
|
+
authors:
|
7
|
+
- Kyle Maxwell
|
8
|
+
autorequire:
|
9
|
+
bindir: bin
|
10
|
+
cert_chain: []
|
11
|
+
date: 2014-04-29 00:00:00.000000000 Z
|
12
|
+
dependencies:
|
13
|
+
- !ruby/object:Gem::Dependency
|
14
|
+
name: zk
|
15
|
+
requirement: !ruby/object:Gem::Requirement
|
16
|
+
requirements:
|
17
|
+
- - '='
|
18
|
+
- !ruby/object:Gem::Version
|
19
|
+
version: 1.9.4
|
20
|
+
type: :runtime
|
21
|
+
prerelease: false
|
22
|
+
version_requirements: !ruby/object:Gem::Requirement
|
23
|
+
requirements:
|
24
|
+
- - '='
|
25
|
+
- !ruby/object:Gem::Version
|
26
|
+
version: 1.9.4
|
27
|
+
- !ruby/object:Gem::Dependency
|
28
|
+
name: bundler
|
29
|
+
requirement: !ruby/object:Gem::Requirement
|
30
|
+
requirements:
|
31
|
+
- - "~>"
|
32
|
+
- !ruby/object:Gem::Version
|
33
|
+
version: '1.5'
|
34
|
+
type: :development
|
35
|
+
prerelease: false
|
36
|
+
version_requirements: !ruby/object:Gem::Requirement
|
37
|
+
requirements:
|
38
|
+
- - "~>"
|
39
|
+
- !ruby/object:Gem::Version
|
40
|
+
version: '1.5'
|
41
|
+
- !ruby/object:Gem::Dependency
|
42
|
+
name: rake
|
43
|
+
requirement: !ruby/object:Gem::Requirement
|
44
|
+
requirements:
|
45
|
+
- - ">="
|
46
|
+
- !ruby/object:Gem::Version
|
47
|
+
version: '0'
|
48
|
+
type: :development
|
49
|
+
prerelease: false
|
50
|
+
version_requirements: !ruby/object:Gem::Requirement
|
51
|
+
requirements:
|
52
|
+
- - ">="
|
53
|
+
- !ruby/object:Gem::Version
|
54
|
+
version: '0'
|
55
|
+
description: Run a process in a wrapper that manages config files from zookeeper
|
56
|
+
email:
|
57
|
+
- kyle@kylemaxwell.com
|
58
|
+
executables:
|
59
|
+
- zkexec
|
60
|
+
extensions: []
|
61
|
+
extra_rdoc_files: []
|
62
|
+
files:
|
63
|
+
- ".gitignore"
|
64
|
+
- Gemfile
|
65
|
+
- LICENSE.txt
|
66
|
+
- README.md
|
67
|
+
- Rakefile
|
68
|
+
- bin/zkexec
|
69
|
+
- lib/zkexec.rb
|
70
|
+
- lib/zkexec/executor.rb
|
71
|
+
- lib/zkexec/runner.rb
|
72
|
+
- lib/zkexec/version.rb
|
73
|
+
- test/libexec/prefixed-cat
|
74
|
+
- test/libexec/slowcheck
|
75
|
+
- test/zk_exec_test.rb
|
76
|
+
- zkexec.gemspec
|
77
|
+
homepage: ''
|
78
|
+
licenses:
|
79
|
+
- MIT
|
80
|
+
metadata: {}
|
81
|
+
post_install_message:
|
82
|
+
rdoc_options: []
|
83
|
+
require_paths:
|
84
|
+
- lib
|
85
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
86
|
+
requirements:
|
87
|
+
- - ">="
|
88
|
+
- !ruby/object:Gem::Version
|
89
|
+
version: '0'
|
90
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
91
|
+
requirements:
|
92
|
+
- - ">="
|
93
|
+
- !ruby/object:Gem::Version
|
94
|
+
version: '0'
|
95
|
+
requirements: []
|
96
|
+
rubyforge_project:
|
97
|
+
rubygems_version: 2.2.2
|
98
|
+
signing_key:
|
99
|
+
specification_version: 4
|
100
|
+
summary: Run a process in a wrapper that manages config files from zookeeper
|
101
|
+
test_files:
|
102
|
+
- test/libexec/prefixed-cat
|
103
|
+
- test/libexec/slowcheck
|
104
|
+
- test/zk_exec_test.rb
|