traut 0.1.2 → 0.2.0
Sign up to get free protection for your applications and to get access to all the features.
- data/.gitignore +1 -0
- data/README.md +38 -30
- data/bin/traut +30 -3
- data/etc/events.d/summer.yml +7 -0
- data/etc/traut.conf +16 -0
- data/lib/traut/application.rb +65 -0
- data/lib/traut/server.rb +43 -30
- data/lib/traut/spawn.rb +55 -0
- data/lib/traut/version.rb +1 -1
- data/lib/traut.rb +16 -4
- data/traut.gemspec +11 -5
- metadata +26 -27
- data/etc/actions/app.action +0 -1
- data/etc/traut.conf.sample +0 -7
- data/lib/traut/runner.rb +0 -124
data/.gitignore
CHANGED
data/README.md
CHANGED
@@ -1,28 +1,14 @@
|
|
1
|
-
Traut -- a
|
2
|
-
|
3
|
-
|
4
|
-
|
5
|
-
|
6
|
-
|
7
|
-
|
8
|
-
|
9
|
-
|
10
|
-
|
11
|
-
|
12
|
-
|
13
|
-
Traut is a program which listens to an AMQP queue and executes scripts
|
14
|
-
found in local `/usr/local/bin/traut/` based on the message route. It
|
15
|
-
is presumed that traut will be supported by a small legion of log
|
16
|
-
watchers, daemon prodders and otherwise. Here at CarePilot, we have a
|
17
|
-
daemon hooked up to Gerrit's ssh stream-events so that we might turn
|
18
|
-
'change-merged' events into immediate project deployments, for
|
19
|
-
example. See `samples/kili`. All payloads are delivered to the
|
20
|
-
scripts' stdin.
|
21
|
-
|
22
|
-
Traut cannot daemonize itself. We use
|
23
|
-
[supervisord](http://supervisord.org/) to daemonize Traut; the code
|
24
|
-
needed to achieve self-daemonization is outside of the core focus of
|
25
|
-
this program.
|
1
|
+
Traut -- a cron-like for AMQP
|
2
|
+
=============================
|
3
|
+
|
4
|
+
Unix cron's an ancient program that runs a script dependent on the passage
|
5
|
+
time. Traut's the same way, except it keys off AMQP events instead. Use it to
|
6
|
+
update your Puppet machines or log-rotate if nagios sends a disk-full
|
7
|
+
warning. Significant number of possibilities.
|
8
|
+
|
9
|
+
Traut cannot daemonize itself. Use [supervisord](http://supervisord.org/) or
|
10
|
+
similar to daemonize Traut; the code needed to achieve self-daemonization is
|
11
|
+
outside of the core focus of this program.
|
26
12
|
|
27
13
|
Installation
|
28
14
|
------------
|
@@ -37,9 +23,31 @@ files. See the contents of etc/ for an example traut.conf. Note that
|
|
37
23
|
response to `com.carepilot.event.code.review.app` events, making its
|
38
24
|
full path `/tmp/traut/scripts/deploy/app`.
|
39
25
|
|
40
|
-
|
41
|
-
|
26
|
+
Configuration
|
27
|
+
-------------
|
28
|
+
|
29
|
+
The source distribution has an [etc/](etc/) directory with example
|
30
|
+
configuration.
|
31
|
+
|
32
|
+
Use
|
33
|
+
---
|
34
|
+
|
35
|
+
Run traut from the root of the project like this:
|
36
|
+
|
37
|
+
bundle exec bin/traut -C etc/traut.conf
|
38
|
+
|
39
|
+
Now, using [hare](https://github.com/blt/hare):
|
40
|
+
|
41
|
+
$ hare --exchange_name traut --exchange_type topic --route_key whatthesum
|
42
|
+
--producer "that wasn't so bad"
|
43
|
+
|
44
|
+
You should see nothing. Open up another terminal and
|
45
|
+
|
46
|
+
$ hare --exchange_name traut --exchange_type topic --route_key 'whatthesum.exited'
|
47
|
+
|
48
|
+
Note that the route key is the same as before, save that '.exited' has been
|
49
|
+
appended to it. Listen to this channel if you need notification of
|
50
|
+
success. Run both hare commands in the order presented, notice the second print
|
51
|
+
a json hash.
|
42
52
|
|
43
|
-
|
44
|
-
be killed and started.
|
45
|
-
* traut doesn't do log rotation. Run logrotate on your system.
|
53
|
+
See the sample configuration file for more details.
|
data/bin/traut
CHANGED
@@ -1,6 +1,33 @@
|
|
1
1
|
#!/usr/bin/env ruby
|
2
|
-
|
3
|
-
|
2
|
+
|
3
|
+
require 'optparse'
|
4
|
+
require 'logger'
|
4
5
|
|
5
6
|
require 'traut'
|
6
|
-
|
7
|
+
|
8
|
+
# Default options
|
9
|
+
options = Traut.defaults
|
10
|
+
|
11
|
+
p = OptionParser.new do |opts|
|
12
|
+
opts.banner = "Usage: traut [options]"
|
13
|
+
opts.on("-C", "--config FILE", "Load options from config file") {
|
14
|
+
|file| options['config'] = file
|
15
|
+
}
|
16
|
+
opts.on_tail("-h", "--help", "Show this message.") do
|
17
|
+
puts opts; exit
|
18
|
+
end
|
19
|
+
opts.on_tail("-v", "--version", "Show version") {
|
20
|
+
puts Traut::VERSION; exit
|
21
|
+
}
|
22
|
+
end
|
23
|
+
p.parse! ARGV
|
24
|
+
|
25
|
+
@immortal = true
|
26
|
+
trap('INT') { @immortal = false; EM.stop }
|
27
|
+
trap('HUP') { EM.stop }
|
28
|
+
|
29
|
+
while @immortal do
|
30
|
+
EventMachine.run do
|
31
|
+
Traut::Application.new(:options => options).run
|
32
|
+
end
|
33
|
+
end
|
data/etc/traut.conf
ADDED
@@ -0,0 +1,65 @@
|
|
1
|
+
require 'yaml'
|
2
|
+
require 'amqp'
|
3
|
+
|
4
|
+
module Traut
|
5
|
+
|
6
|
+
class Application
|
7
|
+
def initialize(params)
|
8
|
+
@options = params[:options] || raise('parameter :options required')
|
9
|
+
end
|
10
|
+
|
11
|
+
# Parse the arguments and run the program. Exit on error.
|
12
|
+
def run
|
13
|
+
load_options
|
14
|
+
|
15
|
+
@logger = Logger.new File.join( File.absolute_path(@options['logdir']), 'traut.log')
|
16
|
+
@logger.level = boolean(@options['debug']) ? Logger::DEBUG : Logger::INFO
|
17
|
+
|
18
|
+
## NOTE: Have to start AMQP connection out here.
|
19
|
+
amqp = @options['amqp']
|
20
|
+
|
21
|
+
AMQP.connect(:host => amqp['host'], :port => amqp['port'], :vhost => amqp['vhost'],
|
22
|
+
:username => amqp['username'], :password => amqp['password']) do |connection|
|
23
|
+
@logger.info "Traut #{Traut::VERSION} started"
|
24
|
+
channel = AMQP::Channel.new(connection)
|
25
|
+
exchange = channel.topic(amqp['exchange'] || 'traut')
|
26
|
+
|
27
|
+
Traut::Server.new(:channel => channel, :exchange => exchange,
|
28
|
+
:events => @options['events'], :log => @logger).run
|
29
|
+
end
|
30
|
+
end
|
31
|
+
|
32
|
+
private
|
33
|
+
def boolean(string)
|
34
|
+
return true if string== true || string =~ (/(true|t|yes|y|1)$/i)
|
35
|
+
return false
|
36
|
+
end
|
37
|
+
|
38
|
+
def abs(p)
|
39
|
+
File.absolute_path(p)
|
40
|
+
end
|
41
|
+
|
42
|
+
def abs?(p)
|
43
|
+
abs(p) == p
|
44
|
+
end
|
45
|
+
|
46
|
+
def mung_config_path(includedir, config)
|
47
|
+
# if includedir is absolute do nothing else
|
48
|
+
includedir if abs?(includedir)
|
49
|
+
# else take abs-dirname of config and append includedir
|
50
|
+
File.join( abs(File.dirname(config)), includedir )
|
51
|
+
end
|
52
|
+
|
53
|
+
def load_options
|
54
|
+
YAML.load_file(@options['config']).each {
|
55
|
+
|key, value| @options[key] = value
|
56
|
+
}
|
57
|
+
includedir = mung_config_path(@options['include'], @options['config'])
|
58
|
+
Dir.open(includedir).each do |f|
|
59
|
+
ff = File.join(includedir, f)
|
60
|
+
@options['events'] += YAML.load_file(ff) if File.file?(ff)
|
61
|
+
end
|
62
|
+
end
|
63
|
+
|
64
|
+
end
|
65
|
+
end
|
data/lib/traut/server.rb
CHANGED
@@ -1,41 +1,54 @@
|
|
1
|
-
require '
|
1
|
+
require 'json'
|
2
2
|
require 'systemu'
|
3
3
|
|
4
4
|
module Traut
|
5
5
|
|
6
6
|
class Server
|
7
|
-
def initialize(
|
8
|
-
@
|
9
|
-
@
|
10
|
-
@
|
7
|
+
def initialize(params)
|
8
|
+
@channel = params[:channel] || raise('parameter :channel required')
|
9
|
+
@exchange = params[:exchange] || raise('parameter :exchange required')
|
10
|
+
@events = params[:events] || raise('parameter :events required')
|
11
|
+
@log = params[:log] || raise('parameter :log required')
|
11
12
|
end
|
12
13
|
|
13
|
-
|
14
|
-
|
15
|
-
|
16
|
-
|
17
|
-
channel = AMQP::Channel.new(connection)
|
18
|
-
exchange = channel.topic('traut')
|
19
|
-
|
20
|
-
@actions.each { |route, script|
|
21
|
-
@log.info("Registering #{script} for route #{route}")
|
22
|
-
channel.queue("").bind(exchange, :routing_key => route).subscribe do |headers, payload|
|
23
|
-
status, stdout, stderr = systemu script, 'stdin' => payload
|
24
|
-
if status.exitstatus != 0
|
25
|
-
@log.error("[#{script}] exit status: #{status.exitstatus}")
|
26
|
-
@log.error("[#{script}] stdout: #{stdout.strip}")
|
27
|
-
@log.error("[#{script}] stderr: #{stderr.strip}")
|
28
|
-
else
|
29
|
-
@log.info("[#{script}] exit status: #{status.exitstatus}")
|
30
|
-
@log.info("[#{script}] stdout: #{stdout.strip}")
|
31
|
-
@log.info("[#{script}] stderr: #{stderr.strip}")
|
32
|
-
end
|
33
|
-
end
|
34
|
-
}
|
35
|
-
|
36
|
-
end
|
14
|
+
# :: () -> ()
|
15
|
+
def run
|
16
|
+
subscribe('#') do |headers, payload|
|
17
|
+
@log.debug("Noted the reception of message with route '#{headers.routing_key}'.")
|
37
18
|
end
|
19
|
+
|
20
|
+
@events.each do |event|
|
21
|
+
route, script, user, group = event['event'], event['command'], event['user'], event['group']
|
22
|
+
@log.debug("Registering #{script} to run as #{user}:#{group} for event #{route}")
|
23
|
+
|
24
|
+
subscribe(route) do |headers, payload|
|
25
|
+
Traut.spawn(:user => user, :group => group, :command => script,
|
26
|
+
:payload => payload, :logger => @log) do |status, stdout, stderr|
|
27
|
+
condition = 0 == status.exitstatus ? :debug : :error
|
28
|
+
result = {:exitstatus => status.exitstatus, :stdout => stdout.strip, :stderr => stderr.strip}
|
29
|
+
@log.send(condition, "[#{script}] #{result}")
|
30
|
+
publish(result.to_json, headers.routing_key)
|
31
|
+
end
|
32
|
+
end # channel.queue
|
33
|
+
|
34
|
+
end # eventmap.each
|
35
|
+
end # run
|
36
|
+
|
37
|
+
private
|
38
|
+
|
39
|
+
# :: string -> string
|
40
|
+
def finished_route(key)
|
41
|
+
[key, 'exited'].join('.')
|
38
42
|
end
|
39
43
|
|
40
|
-
|
44
|
+
def publish(msg, route)
|
45
|
+
@exchange.publish msg, :routing_key => finished_route(route)
|
46
|
+
end
|
47
|
+
|
48
|
+
def subscribe(route, &block)
|
49
|
+
@channel.queue('').bind(@exchange, :routing_key => route).subscribe(&block)
|
50
|
+
end
|
51
|
+
|
52
|
+
end # Server
|
53
|
+
|
41
54
|
end
|
data/lib/traut/spawn.rb
ADDED
@@ -0,0 +1,55 @@
|
|
1
|
+
require 'systemu'
|
2
|
+
|
3
|
+
module Traut
|
4
|
+
def self.spawn(params, &block)
|
5
|
+
uid = params[:user].nil? ? Process::UID.eid : Etc::getpwnam(params[:user])[:uid]
|
6
|
+
gid = params[:group].nil? ? Process::GID.eid : Etc::getgrnam(params[:group])[:gid]
|
7
|
+
command = params[:command] || require('parameter :command is required')
|
8
|
+
payload = params[:payload]
|
9
|
+
|
10
|
+
s = Spawn.new(params[:logger])
|
11
|
+
s.spawn(uid, gid, command, payload, block)
|
12
|
+
end
|
13
|
+
|
14
|
+
class Spawn
|
15
|
+
def initialize(log)
|
16
|
+
@log = log
|
17
|
+
end
|
18
|
+
|
19
|
+
def spawn(uid, gid, command, payload, block)
|
20
|
+
runas(uid, gid) do
|
21
|
+
# Why do I use systemu? Have a look at this:
|
22
|
+
## http://stackoverflow.com/questions/8998097/how-do-i-close-eventmachine-systems-side-of-an-stdin-pipe
|
23
|
+
status, stdout, stderr = systemu command, 0=>payload
|
24
|
+
block.call(status, stdout, stderr)
|
25
|
+
|
26
|
+
# If you have an answer for that, consider enabling the following code,
|
27
|
+
# keeping in mind that you need to figure out a way to get stderr back
|
28
|
+
# as well.
|
29
|
+
|
30
|
+
# EM.system command, proc{ |p| msg(p, payload) } do |stdout,status|
|
31
|
+
# @log.debug("#{stdout} :: #{status}")
|
32
|
+
# end
|
33
|
+
end
|
34
|
+
end
|
35
|
+
|
36
|
+
private
|
37
|
+
def msg(process, m)
|
38
|
+
process.send_data(m + "\n")
|
39
|
+
end
|
40
|
+
|
41
|
+
def runas(uid, gid, &block)
|
42
|
+
cur_uid = Process::UID.eid
|
43
|
+
cur_gid = Process::GID.eid
|
44
|
+
|
45
|
+
begin
|
46
|
+
Process::UID.change_privilege(uid)
|
47
|
+
Process::GID.change_privilege(gid)
|
48
|
+
block.call
|
49
|
+
ensure
|
50
|
+
Process::UID.change_privilege(cur_uid)
|
51
|
+
Process::GID.change_privilege(cur_gid)
|
52
|
+
end
|
53
|
+
end
|
54
|
+
end
|
55
|
+
end
|
data/lib/traut/version.rb
CHANGED
data/lib/traut.rb
CHANGED
@@ -1,11 +1,23 @@
|
|
1
|
-
require
|
1
|
+
require 'traut/version'
|
2
|
+
require 'ostruct'
|
3
|
+
require 'eventmachine'
|
2
4
|
|
3
5
|
module Traut
|
4
6
|
ROOT = File.expand_path(File.dirname(__FILE__))
|
5
7
|
|
6
|
-
autoload :
|
7
|
-
autoload :
|
8
|
-
|
8
|
+
autoload :Server, "#{ROOT}/traut/server"
|
9
|
+
autoload :Application, "#{ROOT}/traut/application"
|
10
|
+
|
11
|
+
# Provide the base option sets for all Textme daemons and their
|
12
|
+
# defaults.
|
13
|
+
def self.defaults
|
14
|
+
{
|
15
|
+
'config' => './traut.conf',
|
16
|
+
'logdir' => './logs/',
|
17
|
+
'debug' => true
|
18
|
+
}
|
19
|
+
end
|
9
20
|
end
|
10
21
|
|
11
22
|
require "#{Traut::ROOT}/traut/version"
|
23
|
+
require "#{Traut::ROOT}/traut/spawn"
|
data/traut.gemspec
CHANGED
@@ -6,10 +6,10 @@ Gem::Specification.new do |s|
|
|
6
6
|
s.name = "traut"
|
7
7
|
s.version = Traut::VERSION
|
8
8
|
s.authors = ["Brian L. Troutwine"]
|
9
|
-
s.email = ["brian
|
10
|
-
s.homepage = "https://github.com/
|
11
|
-
s.summary = %q{
|
12
|
-
s.description = %q{
|
9
|
+
s.email = ["brian@troutwine.us"]
|
10
|
+
s.homepage = "https://github.com/blt/traut"
|
11
|
+
s.summary = %q{Traut is like cron for AMQP events.}
|
12
|
+
s.description = %q{Unix cron is a venerable program that turns the passage of time into program invokation. Traut does the same, but using AMQP events to trigger execution. AMQP message payloads are written to the stdin of invoked commands.}
|
13
13
|
|
14
14
|
s.rubyforge_project = "traut"
|
15
15
|
|
@@ -18,8 +18,14 @@ Gem::Specification.new do |s|
|
|
18
18
|
s.executables = `git ls-files -- bin/*`.split("\n").map{ |f| File.basename(f) }
|
19
19
|
s.require_paths = ["lib"]
|
20
20
|
|
21
|
-
|
21
|
+
## Someday...
|
22
|
+
# s.add_development_dependency "rspec", '~> 2.8.0'
|
23
|
+
# s.add_development_dependency 'guard'
|
24
|
+
# s.add_development_dependency 'guard-rspec'
|
25
|
+
# s.add_development_dependency 'simplecov'
|
26
|
+
|
22
27
|
s.add_runtime_dependency "amqp", '>= 0.8.0'
|
23
28
|
s.add_runtime_dependency "systemu", '~> 2.4'
|
29
|
+
s.add_runtime_dependency 'json', '~> 1.6.5'
|
24
30
|
|
25
31
|
end
|
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: traut
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.
|
4
|
+
version: 0.2.0
|
5
5
|
prerelease:
|
6
6
|
platform: ruby
|
7
7
|
authors:
|
@@ -9,48 +9,46 @@ authors:
|
|
9
9
|
autorequire:
|
10
10
|
bindir: bin
|
11
11
|
cert_chain: []
|
12
|
-
date:
|
12
|
+
date: 2012-01-25 00:00:00.000000000 Z
|
13
13
|
dependencies:
|
14
14
|
- !ruby/object:Gem::Dependency
|
15
|
-
name:
|
16
|
-
requirement: &
|
15
|
+
name: amqp
|
16
|
+
requirement: &75597390 !ruby/object:Gem::Requirement
|
17
17
|
none: false
|
18
18
|
requirements:
|
19
|
-
- -
|
19
|
+
- - ! '>='
|
20
20
|
- !ruby/object:Gem::Version
|
21
|
-
version:
|
21
|
+
version: 0.8.0
|
22
22
|
type: :runtime
|
23
23
|
prerelease: false
|
24
|
-
version_requirements: *
|
24
|
+
version_requirements: *75597390
|
25
25
|
- !ruby/object:Gem::Dependency
|
26
|
-
name:
|
27
|
-
requirement: &
|
26
|
+
name: systemu
|
27
|
+
requirement: &75597140 !ruby/object:Gem::Requirement
|
28
28
|
none: false
|
29
29
|
requirements:
|
30
|
-
- -
|
30
|
+
- - ~>
|
31
31
|
- !ruby/object:Gem::Version
|
32
|
-
version:
|
32
|
+
version: '2.4'
|
33
33
|
type: :runtime
|
34
34
|
prerelease: false
|
35
|
-
version_requirements: *
|
35
|
+
version_requirements: *75597140
|
36
36
|
- !ruby/object:Gem::Dependency
|
37
|
-
name:
|
38
|
-
requirement: &
|
37
|
+
name: json
|
38
|
+
requirement: &75596790 !ruby/object:Gem::Requirement
|
39
39
|
none: false
|
40
40
|
requirements:
|
41
41
|
- - ~>
|
42
42
|
- !ruby/object:Gem::Version
|
43
|
-
version:
|
43
|
+
version: 1.6.5
|
44
44
|
type: :runtime
|
45
45
|
prerelease: false
|
46
|
-
version_requirements: *
|
47
|
-
description:
|
48
|
-
|
49
|
-
|
50
|
-
else that can be scripted. It needs only companions to pump events through the 'traut'
|
51
|
-
exchange.
|
46
|
+
version_requirements: *75596790
|
47
|
+
description: Unix cron is a venerable program that turns the passage of time into
|
48
|
+
program invokation. Traut does the same, but using AMQP events to trigger execution.
|
49
|
+
AMQP message payloads are written to the stdin of invoked commands.
|
52
50
|
email:
|
53
|
-
- brian
|
51
|
+
- brian@troutwine.us
|
54
52
|
executables:
|
55
53
|
- traut
|
56
54
|
extensions: []
|
@@ -62,15 +60,16 @@ files:
|
|
62
60
|
- README.md
|
63
61
|
- Rakefile
|
64
62
|
- bin/traut
|
65
|
-
- etc/
|
66
|
-
- etc/traut.conf
|
63
|
+
- etc/events.d/summer.yml
|
64
|
+
- etc/traut.conf
|
67
65
|
- lib/traut.rb
|
68
|
-
- lib/traut/
|
66
|
+
- lib/traut/application.rb
|
69
67
|
- lib/traut/server.rb
|
68
|
+
- lib/traut/spawn.rb
|
70
69
|
- lib/traut/version.rb
|
71
70
|
- samples/kili
|
72
71
|
- traut.gemspec
|
73
|
-
homepage: https://github.com/
|
72
|
+
homepage: https://github.com/blt/traut
|
74
73
|
licenses: []
|
75
74
|
post_install_message:
|
76
75
|
rdoc_options: []
|
@@ -93,5 +92,5 @@ rubyforge_project: traut
|
|
93
92
|
rubygems_version: 1.8.10
|
94
93
|
signing_key:
|
95
94
|
specification_version: 3
|
96
|
-
summary:
|
95
|
+
summary: Traut is like cron for AMQP events.
|
97
96
|
test_files: []
|
data/etc/actions/app.action
DELETED
@@ -1 +0,0 @@
|
|
1
|
-
com.carepilot.event.code.review.app: deploy/app
|
data/etc/traut.conf.sample
DELETED
data/lib/traut/runner.rb
DELETED
@@ -1,124 +0,0 @@
|
|
1
|
-
require 'optparse'
|
2
|
-
require 'yaml'
|
3
|
-
require 'logger'
|
4
|
-
|
5
|
-
module Traut
|
6
|
-
trap(:INT) { puts; exit }
|
7
|
-
|
8
|
-
# CLI runner. Parse options, run program.
|
9
|
-
class Runner
|
10
|
-
COMMANDS = %w(start stop restart status)
|
11
|
-
|
12
|
-
attr_accessor :options # parsed options
|
13
|
-
|
14
|
-
def initialize(argv)
|
15
|
-
@argv = argv
|
16
|
-
|
17
|
-
# Default options
|
18
|
-
@options = {
|
19
|
-
:amqp => {
|
20
|
-
:host => 'localhost',
|
21
|
-
:port => '5672'
|
22
|
-
},
|
23
|
-
:logs => '/var/log/traut.log',
|
24
|
-
:config => '/etc/traut/traut.conf',
|
25
|
-
:action_dir => '/etc/traut/actions/',
|
26
|
-
:scripts => '/usr/local/bin/traut',
|
27
|
-
:debug => false,
|
28
|
-
:actions => nil,
|
29
|
-
}
|
30
|
-
|
31
|
-
parse!
|
32
|
-
end
|
33
|
-
|
34
|
-
def parser
|
35
|
-
@parser ||= OptionParser.new do |opts|
|
36
|
-
opts.banner = "Usage: traut [options]"
|
37
|
-
opts.separator ""
|
38
|
-
opts.on("-A", "--amqp HOST", "The AMQP server host") {
|
39
|
-
|host| @options[:amqp][:host] = host
|
40
|
-
}
|
41
|
-
opts.on("-P", "--amqp_port PORT", "The AMQP server host port") {
|
42
|
-
|port| @options[:amqp][:port] = port
|
43
|
-
}
|
44
|
-
opts.on("-C", "--config FILE", "Load options from config file") {
|
45
|
-
|file| @options[:config] = file
|
46
|
-
}
|
47
|
-
opts.on("-s", "--scripts DIR", "Location of traut scripts directory"){
|
48
|
-
|scripts| @options[:scripts] = scripts
|
49
|
-
}
|
50
|
-
opts.on("-a", "--actions DIR", "Location of traut actions directory"){
|
51
|
-
|acts| @options[:action_dir] = acts
|
52
|
-
}
|
53
|
-
opts.on('-l', '--logs LOG', "Location of log directory location") {
|
54
|
-
|log| @options[:logs] = log
|
55
|
-
}
|
56
|
-
opts.on('--debug', 'Enable debug logging') {
|
57
|
-
|debug| @options[:debug] = true
|
58
|
-
}
|
59
|
-
opts.on_tail("-h", "--help", "Show this message.") do
|
60
|
-
puts opts
|
61
|
-
exit
|
62
|
-
end
|
63
|
-
opts.on_tail("-v", "--version", "Show version") {
|
64
|
-
puts Traut::VERSION; exit
|
65
|
-
}
|
66
|
-
end
|
67
|
-
end
|
68
|
-
|
69
|
-
# Parse command options out of @argv
|
70
|
-
def parse!
|
71
|
-
parser.parse! @argv
|
72
|
-
@command = @argv.shift
|
73
|
-
@arguments = @argv
|
74
|
-
end
|
75
|
-
|
76
|
-
# Parse the arguments and run the program. Exit on error.
|
77
|
-
def run!
|
78
|
-
load_options_from_config_file!
|
79
|
-
|
80
|
-
log = Logger.new(@options[:logs])
|
81
|
-
log.level = @options[:debug] ? Logger::DEBUG : Logger::INFO
|
82
|
-
|
83
|
-
actions = @options[:actions]
|
84
|
-
actions = actions.merge(actions) { |k,v|
|
85
|
-
File.join(@options[:scripts], v)
|
86
|
-
}
|
87
|
-
|
88
|
-
actions.each { |route, script|
|
89
|
-
if ! File.exists?(script)
|
90
|
-
log.error("#{script} does not exist on disk")
|
91
|
-
exit 1
|
92
|
-
elsif ! File.executable?(script)
|
93
|
-
log.error("#{script} exists on disk but is not executable.")
|
94
|
-
exit 1
|
95
|
-
else
|
96
|
-
log.info("#{script} recognized on disk and executable.")
|
97
|
-
end
|
98
|
-
}
|
99
|
-
|
100
|
-
server = Server.new(@options[:amqp], actions, log)
|
101
|
-
|
102
|
-
server.loop
|
103
|
-
end
|
104
|
-
|
105
|
-
private
|
106
|
-
|
107
|
-
def load_options_from_config_file!
|
108
|
-
if file = @options.delete(:config)
|
109
|
-
YAML.load_file(file).each {
|
110
|
-
|key, value| @options[key.to_sym] = value
|
111
|
-
}
|
112
|
-
dir = Dir.open(@options[:action_dir])
|
113
|
-
@options[:actions] = {}
|
114
|
-
dir.each do |f|
|
115
|
-
af = File.join(@options[:action_dir], f)
|
116
|
-
if ! File.directory? af
|
117
|
-
YAML.load_file(af).each { |k,v| @options[:actions][k] = v }
|
118
|
-
end
|
119
|
-
end
|
120
|
-
end
|
121
|
-
end
|
122
|
-
|
123
|
-
end
|
124
|
-
end
|