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 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
- * these are the sorts of questions auger can answer:
11
+ These are the sorts of questions auger can answer:
12
12
 
13
- * is port :80 on my application webservers open? does /index.html
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
- * is redis running? is it configured as a master? a slave?
17
+ * is redis running? is it configured as a master? a slave?
18
18
 
19
- * is elasticsearch responding on all my hosts it should be? what's
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
- * 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!
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
- * gem install auger
41
+ gem install auger
40
42
 
41
43
  ### If you want to run the latest source:
42
44
 
43
- * `git clone git@github.com/blah/auger` TODO => fix github url
44
- * `cd auger; bundle install && rake install`
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
- * sample configs included in cfg/examples/ can be moved into cfg/ and
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
- * one quick way to find them should be `cd $GEM_HOME/gems/auger-x.x.x/cfg/examples`
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
- * alternatively, you can place your configs anywhere you'd like and
56
- set the env_var: `AUGER_CFG=/path/to/your/configs/prod:/path/to/your/configs/stage`
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
- * now call your tests via `aug name_of_my_config`, e.g. `aug redis`
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
- * configs should take the format `name.rb`
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
- servers 'prod-es-[01-04]'
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
- * yes please
345
- * new plugins and genereal bug fixes, updates, etc are all welcome
346
- * generally, we'd prefer you do the following to submit a pull:
347
- * fork
348
- * create a local topic branch
349
- * make your changes and push
350
- * submit your pull request
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.3.8
1
+ 1.4.0
data/bin/aug CHANGED
@@ -14,7 +14,7 @@ require 'auger'
14
14
 
15
15
  ## set opts
16
16
  options = {}
17
- optparse = OptionParser.new do |opts|
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.color(:green)
31
+ puts Auger::VERSION
48
32
  exit
49
33
  end
50
- end
51
- optparse.parse!
52
-
53
- ## cfg file can be e.g. 'imagine' or relative path
54
- cfg =
55
- if File.exists?(ARGV[0])
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
- threads = Hash.new { |h,k| h[k] = [] }
100
-
101
- ## run tests
102
- project.connections.each do |connection|
103
- servers = project.servers(connection.roles)
104
- servers.select! { |server| server.name.match(options[:server]) } if options[:server]
105
- servers.map do |server|
106
- threads[server.name] << Thread.new do
107
- conn = connection.do_open(server)
108
- connection.requests.map do |request|
109
- response, time = request.do_run(conn)
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
- result = test.run(response)
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
- threads.keys.each do |server|
126
- puts "[#{server.color(:cyan)}]"
127
- threads[server].each do |thread|
128
- results = thread.value # this waits on thread
129
- results.flatten.each do |result|
130
- output = " %+#{max_test_length}s %-30s" % [result.test.name, result.format]
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
@@ -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
@@ -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 do_open(server)
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
@@ -3,7 +3,7 @@ require 'host_range'
3
3
  module Auger
4
4
 
5
5
  class Project
6
- attr_accessor :name, :fqdns, :hosts, :connections, :roles
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
- @hosts = []
17
- @fqdns = []
16
+ @servers = []
18
17
  @connections = []
19
- @roles = Hash.new { |h,k| h[k] = [] }
20
18
  self
21
19
  end
22
20
 
23
- def role(name, *args)
24
- options = args.last.is_a?(Hash) ? args.pop : {}
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
- args.each do |arg|
34
- case arg
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
- alias :hosts :server
45
-
46
- ## return array of servers for given array of roles (default to all)
47
- def servers(roles = [])
48
- (roles.empty? ? @roles.values : @roles.values_at(*roles))
49
- .flatten
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
- alias :host :hosts
53
-
54
- ## add fqdn or return list of fqdns
55
- def fqdns(*ranges)
56
- ranges.empty? ? @fqdns.flatten : @fqdns << [*ranges].map {|r| HostRange.parse(r)}
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
- alias :fqdn :fqdns
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|
@@ -38,7 +38,7 @@ module Auger
38
38
  end
39
39
 
40
40
  ## call plugin run() and return plugin-specfic response object or exception
41
- def do_run(conn)
41
+ def try_run(conn)
42
42
  return conn if conn.is_a? Exception
43
43
  begin
44
44
  arg = @arg
@@ -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, options)
6
+ def initialize(name, *args)
7
7
  @name = name
8
- @options = 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.3.8
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-05 00:00:00.000000000 Z
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.23
153
+ rubygems_version: 1.8.24
152
154
  signing_key:
153
155
  specification_version: 3
154
156
  summary: App && infrastructure testing DSL