auger 1.3.8 → 1.4.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.
- data/README.md +35 -38
- data/VERSION +1 -1
- data/bin/aug +51 -55
- data/bin/aug_simple +42 -0
- data/lib/auger/connection.rb +10 -1
- data/lib/auger/project.rb +26 -33
- data/lib/auger/request.rb +1 -1
- data/lib/auger/server.rb +4 -3
- metadata +5 -3
data/README.md
CHANGED
@@ -8,21 +8,21 @@ The primary goal of Auger is test-driven operations: unit testing for
|
|
8
8
|
application admins. The library can also be used as a framework for
|
9
9
|
implmenting automated tests.
|
10
10
|
|
11
|
-
|
11
|
+
These are the sorts of questions auger can answer:
|
12
12
|
|
13
|
-
|
13
|
+
* is port :80 on my application webservers open? does /index.html
|
14
14
|
contain a response tag that we know should be served from a given
|
15
15
|
backend data source?
|
16
16
|
|
17
|
-
|
17
|
+
* is redis running? is it configured as a master? a slave?
|
18
18
|
|
19
|
-
|
19
|
+
* is elasticsearch responding on all my hosts it should be? what's
|
20
20
|
the cluster state? do I have the number of data nodes responding
|
21
21
|
that we're supposed to have?
|
22
22
|
|
23
|
-
|
24
|
-
|
25
|
-
|
23
|
+
Clearly a lot of this information includes things you should be
|
24
|
+
graphing. What auger wants to do is give you a quick overview
|
25
|
+
of current status: green == good, red == ruh roh!
|
26
26
|
|
27
27
|
## Plugins
|
28
28
|
|
@@ -30,43 +30,38 @@ Specific protocols are implemented using plugins, which are designed
|
|
30
30
|
to be easy to write wrappers on existing gems. Auger currently includes
|
31
31
|
the following plugins:
|
32
32
|
|
33
|
-
* http - http and https requests using `net/http`
|
34
|
-
* telnet - send arbitrary commands to a port using `net/telnet`
|
35
33
|
* socket - test whether a port is open
|
34
|
+
* telnet - send arbitrary commands to a port using `net/telnet`
|
35
|
+
* http - http and https requests using `net/http`
|
36
|
+
* redis - query redis db info, keys, etc
|
37
|
+
* dns - domain and query requests for testing a DNS server
|
36
38
|
|
37
39
|
## Installation
|
38
40
|
|
39
|
-
|
41
|
+
gem install auger
|
40
42
|
|
41
43
|
### If you want to run the latest source:
|
42
44
|
|
43
|
-
|
44
|
-
|
45
|
+
git clone git@github.com/brewster/auger
|
46
|
+
cd auger; bundle install && rake install
|
45
47
|
|
46
48
|
## Command-line client usage
|
47
49
|
|
48
|
-
|
49
|
-
then run via `aug redis` etc.
|
50
|
-
|
51
|
-
* if you've installed as a gem, the examples will be located wherever your gems get installed
|
50
|
+
`aug -h` will print usage details.
|
52
51
|
|
53
|
-
|
52
|
+
Sample configs are included in `auger/cfg/examples` (if installed as a
|
53
|
+
gem, look in `$GEM_HOME/gems/auger-x.x.x/cfg/examples`). Use them as a
|
54
|
+
basis to write your own tests, which auger can find in one of three ways:
|
54
55
|
|
55
|
-
*
|
56
|
-
|
56
|
+
* in the directory `auger/cfg`
|
57
|
+
* from the config load path defined in environment variable `AUGER_CFG`.
|
58
|
+
e.g. `AUGER_CFG=~/aug_cfg/prod:~/aug_cfg/staging`
|
59
|
+
* using a relative path
|
57
60
|
|
58
|
-
|
61
|
+
Reference config files using `aug foo`, to find `foo.rb` in
|
62
|
+
`auger/cfg` or `AUGER_CFG` path, or `aug path/to/foo.rb` to find it directly.
|
59
63
|
|
60
|
-
|
61
|
-
|
62
|
-
* `aug -l` will print available tests
|
63
|
-
* `aug -h` will print usage details
|
64
|
-
|
65
|
-
## Writing tests
|
66
|
-
|
67
|
-
Tests are written as ruby code describing a test configuration. Files
|
68
|
-
containing tests should be placed in the path described by the `AUGER_CFG`
|
69
|
-
environment variable.
|
64
|
+
`aug -l` will print available config files in `AUGER_CFG` path.
|
70
65
|
|
71
66
|
### Example 1 - testing a webserver response
|
72
67
|
|
@@ -194,7 +189,7 @@ a checkmark or an 'x'.
|
|
194
189
|
require 'json'
|
195
190
|
|
196
191
|
project "Elasticsearch" do
|
197
|
-
|
192
|
+
server 'prod-es-[01-04]'
|
198
193
|
|
199
194
|
http 9200 do
|
200
195
|
get "/_cluster/health" do
|
@@ -341,13 +336,15 @@ ZSH completion:
|
|
341
336
|
|
342
337
|
## Pull Requests
|
343
338
|
|
344
|
-
|
345
|
-
|
346
|
-
|
347
|
-
|
348
|
-
|
349
|
-
|
350
|
-
|
339
|
+
Yes please.
|
340
|
+
|
341
|
+
New plugins and general bug fixes, updates, etc are all welcome.
|
342
|
+
|
343
|
+
Generally, we'd prefer you do the following to submit a pull:
|
344
|
+
* fork
|
345
|
+
* create a local topic branch
|
346
|
+
* make your changes and push
|
347
|
+
* submit your pull request
|
351
348
|
|
352
349
|
## License
|
353
350
|
|
data/VERSION
CHANGED
@@ -1 +1 @@
|
|
1
|
-
1.
|
1
|
+
1.4.0
|
data/bin/aug
CHANGED
@@ -14,7 +14,7 @@ require 'auger'
|
|
14
14
|
|
15
15
|
## set opts
|
16
16
|
options = {}
|
17
|
-
|
17
|
+
OptionParser.new do |opts|
|
18
18
|
opts.banner = "Usage: aug [-h|--help] [-l|--list] [-v|--version] cfg"
|
19
19
|
|
20
20
|
if ARGV[0] == nil
|
@@ -22,50 +22,33 @@ optparse = OptionParser.new do |opts|
|
|
22
22
|
exit
|
23
23
|
end
|
24
24
|
|
25
|
-
opts.on('-l', '--list', 'List available configs and exit.') do
|
26
|
-
list = AUGER_CFG.map do |dir|
|
27
|
-
Dir["#{dir}/*.rb"].map{ |file| File.basename(file).sub(/\.rb$/, '') }
|
28
|
-
end
|
29
|
-
puts list.flatten.sort
|
30
|
-
exit
|
31
|
-
end
|
32
|
-
|
33
25
|
opts.on('-h', '--help', 'Display help') do
|
34
26
|
puts opts
|
35
27
|
exit
|
36
28
|
end
|
37
29
|
|
38
|
-
opts.on('-s', '--server REGEX', 'Limit to server names matching regex.') do |s|
|
39
|
-
options[:server] = s
|
40
|
-
end
|
41
|
-
|
42
|
-
opts.on('-t', '--time', 'Show request times.') do |t|
|
43
|
-
options[:time] = true
|
44
|
-
end
|
45
|
-
|
46
30
|
opts.on('-v', '--version', 'Display version and exit.') do
|
47
|
-
puts Auger::VERSION
|
31
|
+
puts Auger::VERSION
|
48
32
|
exit
|
49
33
|
end
|
50
|
-
|
51
|
-
|
52
|
-
|
53
|
-
|
54
|
-
|
55
|
-
|
56
|
-
ARGV[0]
|
57
|
-
elsif path = AUGER_CFG.find { |path| File.exists?("#{path}/#{ARGV[0]}.rb") }
|
58
|
-
[path, "#{ARGV[0]}.rb"].join(File::SEPARATOR)
|
59
|
-
else
|
60
|
-
raise ArgumentError, "config #{ARGV[0]} not found"
|
34
|
+
|
35
|
+
opts.on('-l', '--list', 'List available configs and exit.') do
|
36
|
+
puts(AUGER_CFG.map do |dir|
|
37
|
+
Dir["#{dir}/*.rb"].map{ |file| File.basename(file).sub(/\.rb$/, '') }
|
38
|
+
end.flatten.sort)
|
39
|
+
exit
|
61
40
|
end
|
41
|
+
end.parse!
|
42
|
+
|
43
|
+
## load plugins
|
44
|
+
Dir["#{File.dirname(File.dirname(__FILE__))}/lib/plugins/*.rb"].each { |file| require file }
|
62
45
|
|
63
46
|
## pretty-print Result object
|
64
47
|
module Auger
|
65
48
|
class Result
|
66
49
|
|
67
50
|
def format
|
68
|
-
output =
|
51
|
+
output =
|
69
52
|
case self.outcome
|
70
53
|
when TrueClass then "\u2713"
|
71
54
|
when MatchData then outcome.captures.empty? ? "\u2713" : outcome.captures.join(' ')
|
@@ -88,48 +71,61 @@ module Auger
|
|
88
71
|
end
|
89
72
|
else :green
|
90
73
|
end
|
91
|
-
|
74
|
+
|
92
75
|
return output.color(color)
|
93
76
|
end
|
94
77
|
end
|
95
78
|
end
|
96
79
|
|
80
|
+
## find cfg in path, or just use local file
|
81
|
+
cfg = AUGER_CFG.map do |path|
|
82
|
+
File.join(path, "#{ARGV[0]}.rb")
|
83
|
+
end.find do |file|
|
84
|
+
File.exists?(file)
|
85
|
+
end || ARGV[0]
|
86
|
+
|
87
|
+
## load config file and loop projects
|
97
88
|
Auger::Config.load(cfg).projects.each do |project|
|
98
89
|
|
99
|
-
|
100
|
-
|
101
|
-
|
102
|
-
|
103
|
-
|
104
|
-
|
105
|
-
|
106
|
-
|
107
|
-
|
108
|
-
connection.requests.map do |request|
|
109
|
-
response, time = request.
|
90
|
+
servers = project.servers.map do |server|
|
91
|
+
|
92
|
+
pipes = project.connections(*server.roles).map do |connection|
|
93
|
+
read, write = IO.pipe
|
94
|
+
|
95
|
+
Process.fork do
|
96
|
+
read.close # child does not need
|
97
|
+
conn = connection.try_open(server)
|
98
|
+
|
99
|
+
responses = connection.requests.map do |request|
|
100
|
+
response, time = request.try_run(conn)
|
110
101
|
request.tests.map do |test|
|
111
|
-
|
112
|
-
result.time = time
|
113
|
-
result
|
102
|
+
[ test.name, test.run(response).format ]
|
114
103
|
end
|
115
104
|
end
|
105
|
+
|
106
|
+
Marshal.dump(responses, write)
|
107
|
+
connection.try_close(conn)
|
116
108
|
end
|
109
|
+
|
110
|
+
write.close # parent does not need
|
111
|
+
read # return the pipe
|
117
112
|
end
|
113
|
+
|
114
|
+
[ server, pipes ] # return server and its read pipes
|
118
115
|
end
|
119
116
|
|
117
|
+
Process.waitall
|
118
|
+
|
120
119
|
## width of test name column
|
121
|
-
max_test_length =
|
122
|
-
project.connections.map{|c| c.requests.map{|r| r.tests.map{|t| t.name.length}}}.flatten.max
|
120
|
+
max_test_length = project.tests.map { |test| test.name.length }.max
|
123
121
|
|
124
122
|
## print results
|
125
|
-
|
126
|
-
puts "[#{server.color(:cyan)}]"
|
127
|
-
|
128
|
-
|
129
|
-
|
130
|
-
|
131
|
-
output << "[#{result.time}]".color(:blue) if options[:time]
|
132
|
-
puts output
|
123
|
+
servers.each do |server, pipes|
|
124
|
+
puts "[#{server.name.color(:cyan)}]"
|
125
|
+
|
126
|
+
pipes.each do |pipe|
|
127
|
+
Marshal.load(pipe).flatten(1).each do |test, result|
|
128
|
+
puts " %+#{max_test_length}s %-30s" % [ test, result ]
|
133
129
|
end
|
134
130
|
end
|
135
131
|
end
|
data/bin/aug_simple
ADDED
@@ -0,0 +1,42 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
# -*- coding: utf-8 -*-
|
3
|
+
|
4
|
+
## this is the simplest auger client, written for educational purposes;
|
5
|
+
## not intended for actual use
|
6
|
+
|
7
|
+
AUGER_DIR = File.dirname(File.dirname(__FILE__))
|
8
|
+
AUGER_LIB = File.join(AUGER_DIR, 'lib')
|
9
|
+
|
10
|
+
## relative path to libs (in case not installed as a gem)
|
11
|
+
$LOAD_PATH.unshift(AUGER_LIB) unless $LOAD_PATH.include?(AUGER_LIB)
|
12
|
+
require "auger"
|
13
|
+
|
14
|
+
## load plugins
|
15
|
+
Dir["#{File.dirname(File.dirname(__FILE__))}/lib/plugins/*.rb"].each { |file| require file }
|
16
|
+
|
17
|
+
## load config file and loop projects
|
18
|
+
Auger::Config.load(ARGV[0]).projects.each do |project|
|
19
|
+
|
20
|
+
project.servers.each do |server|
|
21
|
+
puts "[#{server.name}]"
|
22
|
+
|
23
|
+
project.connections(*server.roles).each do |connection|
|
24
|
+
conn = connection.try_open(server)
|
25
|
+
|
26
|
+
connection.requests.each do |request|
|
27
|
+
response, time = request.try_run(conn)
|
28
|
+
|
29
|
+
request.tests.each do |test|
|
30
|
+
result = test.run(response)
|
31
|
+
puts " #{test.name}: #{result.outcome}"
|
32
|
+
end
|
33
|
+
|
34
|
+
end
|
35
|
+
|
36
|
+
connection.try_close(conn)
|
37
|
+
|
38
|
+
end
|
39
|
+
|
40
|
+
end
|
41
|
+
|
42
|
+
end
|
data/lib/auger/connection.rb
CHANGED
@@ -30,7 +30,7 @@ module Auger
|
|
30
30
|
end
|
31
31
|
|
32
32
|
## call plugin open() and return plugin-specific connection object, or exception
|
33
|
-
def
|
33
|
+
def try_open(server)
|
34
34
|
options = @options.merge(server.options)
|
35
35
|
begin
|
36
36
|
self.open(server.name, options)
|
@@ -39,6 +39,15 @@ module Auger
|
|
39
39
|
end
|
40
40
|
end
|
41
41
|
|
42
|
+
## safe way to call plugin close() (rescue if the connection did not exist)
|
43
|
+
def try_close(conn)
|
44
|
+
begin
|
45
|
+
self.close(conn)
|
46
|
+
rescue => e
|
47
|
+
e
|
48
|
+
end
|
49
|
+
end
|
50
|
+
|
42
51
|
end
|
43
52
|
|
44
53
|
end
|
data/lib/auger/project.rb
CHANGED
@@ -3,7 +3,7 @@ require 'host_range'
|
|
3
3
|
module Auger
|
4
4
|
|
5
5
|
class Project
|
6
|
-
attr_accessor :name, :
|
6
|
+
attr_accessor :name, :connections, :servers
|
7
7
|
|
8
8
|
def self.load(name, &block)
|
9
9
|
project = new(name)
|
@@ -13,51 +13,44 @@ module Auger
|
|
13
13
|
|
14
14
|
def initialize(name)
|
15
15
|
@name = name
|
16
|
-
@
|
17
|
-
@fqdns = []
|
16
|
+
@servers = []
|
18
17
|
@connections = []
|
19
|
-
@roles = Hash.new { |h,k| h[k] = [] }
|
20
18
|
self
|
21
19
|
end
|
22
20
|
|
23
|
-
|
24
|
-
|
25
|
-
servers = args.map { |arg| HostRange.parse(arg) }.flatten
|
26
|
-
servers.each { |server| roles[name] << Auger::Server.new(server, options) }
|
27
|
-
end
|
28
|
-
|
21
|
+
## set server, or list of server names, with optional roles and options
|
22
|
+
## e.g. server server1, server2, :roleA, :roleB, options => values
|
29
23
|
def server(*args)
|
30
24
|
options = args.last.is_a?(Hash) ? args.pop : {}
|
31
|
-
roles =
|
32
|
-
servers =
|
33
|
-
|
34
|
-
|
35
|
-
when Symbol then roles << arg
|
36
|
-
when String then servers << arg
|
37
|
-
else raise ArgumentError, "illegal argument to server: #{arg}"
|
38
|
-
end
|
25
|
+
roles = args.select { |arg| arg.class == Symbol }
|
26
|
+
servers = args.select { |arg| arg.class == String }.map { |arg| HostRange.parse(arg) }
|
27
|
+
@servers += servers.flatten.map do |name|
|
28
|
+
Auger::Server.new(name, *roles, options)
|
39
29
|
end
|
40
|
-
roles = [nil] if roles.empty? # default role
|
41
|
-
roles.each { |name| role(name, *servers, options) }
|
42
30
|
end
|
43
31
|
|
44
|
-
|
45
|
-
|
46
|
-
|
47
|
-
|
48
|
-
|
49
|
-
.
|
32
|
+
## get list of server objects (optionally matching list of roles)
|
33
|
+
def servers(*roles)
|
34
|
+
if roles.empty?
|
35
|
+
@servers
|
36
|
+
else
|
37
|
+
roles.map do |role|
|
38
|
+
@servers.select { |server| server.roles.include?(role) }
|
39
|
+
end.flatten.uniq
|
40
|
+
end
|
50
41
|
end
|
51
42
|
|
52
|
-
|
53
|
-
|
54
|
-
|
55
|
-
|
56
|
-
|
43
|
+
## return all connections, or those matching list of roles;
|
44
|
+
## connections with no roles match all, or find intersection with roles list
|
45
|
+
def connections(*roles)
|
46
|
+
if roles.empty?
|
47
|
+
@connections
|
48
|
+
else
|
49
|
+
@connections.select { |c| c.roles.empty? or !(c.roles & roles).empty? }
|
50
|
+
end
|
57
51
|
end
|
58
52
|
|
59
|
-
|
60
|
-
|
53
|
+
## return list of all test objects for this project
|
61
54
|
def tests
|
62
55
|
@connections.map do |connection|
|
63
56
|
connection.requests.map do |request|
|
data/lib/auger/request.rb
CHANGED
data/lib/auger/server.rb
CHANGED
@@ -1,11 +1,12 @@
|
|
1
1
|
module Auger
|
2
2
|
|
3
3
|
class Server
|
4
|
-
attr_accessor :name, :options
|
4
|
+
attr_accessor :name, :options, :roles
|
5
5
|
|
6
|
-
def initialize(name,
|
6
|
+
def initialize(name, *args)
|
7
7
|
@name = name
|
8
|
-
@options =
|
8
|
+
@options = args.last.is_a?(Hash) ? args.pop : {}
|
9
|
+
@roles = args
|
9
10
|
end
|
10
11
|
|
11
12
|
end
|
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: auger
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 1.
|
4
|
+
version: 1.4.0
|
5
5
|
prerelease:
|
6
6
|
platform: ruby
|
7
7
|
authors:
|
@@ -10,7 +10,7 @@ authors:
|
|
10
10
|
autorequire:
|
11
11
|
bindir: bin
|
12
12
|
cert_chain: []
|
13
|
-
date: 2012-10-
|
13
|
+
date: 2012-10-24 00:00:00.000000000 Z
|
14
14
|
dependencies:
|
15
15
|
- !ruby/object:Gem::Dependency
|
16
16
|
name: json
|
@@ -98,6 +98,7 @@ email:
|
|
98
98
|
- heffergm@gmail.com
|
99
99
|
executables:
|
100
100
|
- aug
|
101
|
+
- aug_simple
|
101
102
|
extensions: []
|
102
103
|
extra_rdoc_files: []
|
103
104
|
files:
|
@@ -109,6 +110,7 @@ files:
|
|
109
110
|
- VERSION
|
110
111
|
- auger.gemspec
|
111
112
|
- bin/aug
|
113
|
+
- bin/aug_simple
|
112
114
|
- cfg/examples/elasticsearch.rb
|
113
115
|
- cfg/examples/redis.rb
|
114
116
|
- cfg/examples/riak.rb
|
@@ -148,7 +150,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
148
150
|
version: '0'
|
149
151
|
requirements: []
|
150
152
|
rubyforge_project:
|
151
|
-
rubygems_version: 1.8.
|
153
|
+
rubygems_version: 1.8.24
|
152
154
|
signing_key:
|
153
155
|
specification_version: 3
|
154
156
|
summary: App && infrastructure testing DSL
|