outliers 0.3.3 → 0.5.0.beta1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (39) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +11 -1
  3. data/README.md +21 -17
  4. data/lib/outliers/{credentials.rb → account.rb} +4 -4
  5. data/lib/outliers/cli/process.rb +53 -15
  6. data/lib/outliers/cli.rb +1 -1
  7. data/lib/outliers/collection.rb +24 -18
  8. data/lib/outliers/evaluation.rb +60 -32
  9. data/lib/outliers/exceptions.rb +10 -1
  10. data/lib/outliers/filters/aws/ec2/tags.rb +2 -2
  11. data/lib/outliers/handlers/json.rb +36 -0
  12. data/lib/outliers/handlers/outliers_api.rb +62 -0
  13. data/lib/outliers/handlers.rb +1 -0
  14. data/lib/outliers/provider.rb +7 -7
  15. data/lib/outliers/resources/aws/ec2/instance.rb +1 -1
  16. data/lib/outliers/resources/aws/elb/load_balancer.rb +1 -1
  17. data/lib/outliers/resources/aws/rds/db_instance.rb +1 -1
  18. data/lib/outliers/resources/aws/s3/bucket_collection.rb +1 -1
  19. data/lib/outliers/result.rb +32 -8
  20. data/lib/outliers/run.rb +9 -3
  21. data/lib/outliers/verifications/shared.rb +2 -2
  22. data/lib/outliers/version.rb +1 -1
  23. data/lib/outliers.rb +2 -1
  24. data/outliers.gemspec +1 -1
  25. data/reference.yaml +10 -10
  26. data/shared.yaml +1 -1
  27. data/spec/{credentials_spec.rb → account_spec.rb} +7 -7
  28. data/spec/collection_spec.rb +48 -7
  29. data/spec/evaluation_spec.rb +109 -47
  30. data/spec/fixtures/{credentials1.yml → account1.yml} +1 -1
  31. data/spec/fixtures/{credentials2.yml → account2.yml} +1 -1
  32. data/spec/handlers/outliers_api_spec.rb +61 -0
  33. data/spec/info_spec.rb +2 -2
  34. data/spec/provider_spec.rb +9 -9
  35. data/spec/results_spec.rb +65 -16
  36. data/spec/run_spec.rb +4 -4
  37. data/spec/spec_helper.rb +3 -3
  38. data/spec/verifications/shared_spec.rb +3 -3
  39. metadata +18 -13
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: 7a3abdedf8279901f986e89f931eea7027945348
4
- data.tar.gz: 343deb6efbe0455784e35acfdd0b15dce25bec44
3
+ metadata.gz: e21b9c0b8c0b80d467c4deedbd9e2f0911b44fcc
4
+ data.tar.gz: e14dbca5caad874380dc33a7e2e8edebd4884556
5
5
  SHA512:
6
- metadata.gz: 82e1db72c5424b718a5ed6e9a4550315f5c9b457b3959a58d9581495f3c8971794ac1096b4cb24ae712c2f336c83213bb8a0e439a303f5184a48f12635244699
7
- data.tar.gz: 89e55d536770ffbca617679940e3f947c07e30754837a8f86964810959cecfed97ddb675c6d40e7814c912e64b22375f2c4f0af93449c9a10b26923f50b65660
6
+ metadata.gz: 6aa6ca8c8f0bcd4a4923f9c89b61cf93c5224754fdce113b69c1f4f039d7cf97a1d9cacb68781081502d8ceaff50089368e7f174d8cc262eda6c6a3d8c5a0511
7
+ data.tar.gz: a957f1bd5eeb096f930ff27ecdaf864dbba496d2aa8732081a4921f7ddb87bfddd65f62c53045d718e641963e4dbd6436ba4558a5e6f71fb5866c8f2a25daa1e
data/CHANGELOG.md CHANGED
@@ -1,4 +1,14 @@
1
- ## HEAD
1
+ ## 0.5.0
2
+
3
+ * Added exclude filters
4
+ * Refactored and simplified DSL
5
+ * Added basic handler
6
+
7
+ ## 0.4.0
8
+
9
+ * Renamed credentials to account
10
+ * Adding JSON support to result
11
+ * Refactor results
2
12
 
3
13
  ## 0.3.3
4
14
 
data/README.md CHANGED
@@ -1,4 +1,5 @@
1
1
  [![Build Status](https://secure.travis-ci.org/brettweavnet/outliers.png)](http://travis-ci.org/brettweavnet/outliers)
2
+ [![Code Climate](https://codeclimate.com/github/brettweavnet/outliers.png)](https://codeclimate.com/github/brettweavnet/outliers)
2
3
 
3
4
  # Outliers
4
5
 
@@ -6,32 +7,26 @@ A framework to detect misconfigurations (Outliers).
6
7
 
7
8
  ## Overview
8
9
 
9
- To detect misconfigurations at scale, Outliers provides a framework for performing complex evaluations:
10
+ To detect misconfigurations at scale, Outliers provides a framework for performing complex evaluations of cloud resources based on the following:
10
11
 
11
12
  * Applications rely on **resources** delivered from multiple **providers** (EC2, S3, etc).
12
13
  * Resource configuration can be evaluated against specific **verifications** (Instance launched from given AMI, S3 bucket contains no public objects, etc).
13
14
  * Verifications can be performed against a subset of resources based on a **filter**.
14
15
  * Those not passing verification, are flagged as Outliers.
15
16
 
16
- Evalutions are read from from files ending with **.rb** within a target directory.
17
-
18
- Multiple evaluations can be specified in a file, with multiple files in directory.
19
-
20
17
  ## Requirements
21
18
 
22
- * Ruby 1.9.3 or greater.
19
+ * Ruby 1.9.3 or greater
23
20
 
24
21
  ## Installation
25
22
 
26
- Install the gem:
27
-
28
23
  gem install outliers
29
24
 
30
25
  ## Getting Started
31
26
 
32
- Create **~/outliers.yml** with a list of credentials in the following format:
27
+ Create **~/outliers.yml** with a list of accounts in the following format:
33
28
 
34
- credential_name:
29
+ account_name:
35
30
  region: AWS_REGION
36
31
  access_key_id: AWS_ACCESS_ID
37
32
  secret_access_key: AWS_SECRET_KEY
@@ -43,11 +38,11 @@ For example:
43
38
  access_key_id: abcd1234abcd1234abcd
44
39
  secret_access_key: abcd1234abcd1234abcdabcd1234abcd1234abcd
45
40
 
46
- Outliers provides a DSL which can be used to build up a comprehensive list of evaluations. Create a directory to store your evaluations.
41
+ Create a directory to store your evaluations.
47
42
 
48
43
  mkdir ~/outliers
49
44
 
50
- To verify all instances are in a VPC, create a file **ec2.rb** in **~/outliers** containing:
45
+ To verify all instances in aws_prod are in a VPC, create ec2.rb in ~/outliers containing:
51
46
 
52
47
  evaluate do
53
48
  connect 'aws_prod', provider: 'aws_ec2'
@@ -76,13 +71,22 @@ Sample Output:
76
71
  I, [2013-09-24T09:42:44.804147 #4940] INFO -- : Evaluations completed.
77
72
  I, [2013-09-24T09:42:44.804211 #4940] INFO -- : (0 evaluations failed, 1 evaluations passed.)
78
73
 
79
- * Resources can be targeted or excluded by their ID (EC2 Instance ID, S3 Object Key, etc).
80
- * Resources can be targeted or excluded by matching a filter (Instance has tag 'x' with value 'y').
74
+ ## Results
75
+
76
+ To return outliers results, you must set the **OUTLIERS_KEY** environment variable.
77
+
78
+ export OUTLIERS_KEY=abcd1234
79
+
80
+ To modify the URL where reuslts are sent, set **OUTLIERS_URL** environment variable.
81
+
82
+ By default, results are sent to **https://api.getoutliers.com**
83
+
84
+ export OUTLIERS_URL=http://localhost:3000
81
85
 
82
86
  ## Examples
83
87
 
84
- See [examples](http://brettweavnet.github.io/outliers/examples) for a list of more advanced evaluations.
88
+ See [examples](http://www.getoutliers.com/documentation/examples) for a list of more advanced evaluations.
85
89
 
86
- ## References
90
+ ## Documentation
87
91
 
88
- See the [providers](http://brettweavnet.github.io/outliers/providers), [resources](http://brettweavnet.github.io/outliers/resources) and [filters](http://brettweavnet.github.io/outliers/filters) pages for additional documentation.
92
+ See [providers](http://www.getoutliers.com/documentation/providers), [resources](http://www.getoutliers.com/documentation/resources) and [filters](http://www.getoutliers.com/documentation/filters) for additional documentation.
@@ -1,14 +1,14 @@
1
1
  module Outliers
2
- module Credentials
2
+ module Account
3
3
  module_function
4
4
 
5
5
  def load_from_file(file)
6
- credentials = {}
6
+ account = {}
7
7
  contents = File.read file
8
8
  YAML.load(contents).each_pair do |k,v|
9
- credentials[k] = v
9
+ account[k] = v
10
10
  end
11
- credentials
11
+ account
12
12
  end
13
13
 
14
14
  end
@@ -2,7 +2,7 @@ module Outliers
2
2
  module CLI
3
3
  class Process
4
4
  def process
5
- @options = { threads: 1 }
5
+ @options = { threads: 1, log_level: 'info' }
6
6
 
7
7
  option_parser.parse!
8
8
 
@@ -16,31 +16,53 @@ module Outliers
16
16
  @run.thread_count = @options[:threads]
17
17
  end
18
18
 
19
+ log_level = @options.fetch(:log_level).upcase
20
+
21
+ unless ["DEBUG", "INFO", "WARN", "ERROR"].include? log_level
22
+ @logger.error "Invalid log level. Valid levels are debug, info, warn, error."
23
+ exit 1
24
+ end
25
+
26
+ @logger.level = Logger.const_get log_level
27
+
19
28
  begin
20
- @run.credentials = Credentials.load_from_file "#{ENV['HOME']}/.outliers.yml"
29
+ @run.account = Account.load_from_file "#{ENV['HOME']}/.outliers.yml"
21
30
  @run.process_evaluations_in_dir
22
31
  rescue Outliers::Exceptions::Base => e
23
32
  @logger.error e.message
24
33
  exit 1
25
34
  end
26
35
 
27
- passed = @run.passed.count
28
- failed = @run.failed.count
36
+ passing_count = @run.passing_results.count
37
+ failing_count = @run.failing_results.count
29
38
 
30
39
  @logger.info "Evaluations completed."
31
40
 
32
- @run.failed.each do |f|
33
- if f.evaluation
34
- @logger.info "Evaluation '#{f.evaluation}' verification '#{f.verification}' of '#{f.resource}' failed."
41
+ if key
42
+ @logger.info "Running report handlers."
43
+ @run.results.each do |result|
44
+ unless Outliers::Handlers::OutliersApi.new.post result, key, results_url
45
+ @logger.error "Report handler failed."
46
+ exit 1
47
+ end
48
+ end
49
+ @logger.info "Report handlers completed."
50
+ else
51
+ @logger.info "OUTLIERS_KEY not set, not sending results."
52
+ end
53
+
54
+ @run.failing_results.each do |r|
55
+ if r.name
56
+ @logger.info "Results of '#{r.name}', verifying '#{r.verification_name}' of '#{r.provider_name}:#{r.resource_name}' via '#{r.account_name}' failed."
35
57
  else
36
- @logger.info "Verification '#{f.verification}' of '#{f.resource}' failed."
58
+ @logger.info "Verification '#{r.verification_name}' of '#{r.provider_name}:#{r.resource_name}' via '#{r.account_name}' failed."
37
59
  end
38
- @logger.info "Failing resource IDs '#{f.failing_resources.map{|r| r.id}.join(', ')}'"
60
+ @logger.info "Failing resource IDs '#{r.failing_resources.map{|r| r.id}.join(', ')}'"
39
61
  end
40
62
 
41
- @logger.info "(#{failed} evaluations failed, #{passed} evaluations passed.)"
63
+ @logger.info "(#{failing_count} evaluations failed, #{passing_count} evaluations passed.)"
42
64
 
43
- exit 1 unless failed.zero?
65
+ exit 1 unless failing_count.zero?
44
66
  end
45
67
 
46
68
  def command_name
@@ -53,17 +75,33 @@ module Outliers
53
75
 
54
76
  private
55
77
 
78
+ def key
79
+ ENV['OUTLIERS_KEY']
80
+ end
81
+
82
+ def url
83
+ ENV['OUTLIERS_URL'] ||= 'https://api.getoutliers.com'
84
+ end
85
+
86
+ def results_url
87
+ "#{url}/results"
88
+ end
89
+
56
90
  def option_parser
57
91
  OptionParser.new do |opts|
58
92
  opts.banner = "Usage: outliers process [options]"
59
93
 
60
- opts.on("-t", "--threads [THREADS]", "Maximum number of evaluations threads to run concurrently (Default: 1).") do |o|
61
- @options[:threads] = o.to_i
62
- end
63
-
64
94
  opts.on("-d", "--directory [DIRECTORY]", "Directory containing evaluations to load.") do |o|
65
95
  @options[:directory] = o
66
96
  end
97
+
98
+ opts.on("-l", "--log_level [LOG_LEVEL]", "Log level (Default: info).") do |o|
99
+ @options[:log_level] = o
100
+ end
101
+
102
+ opts.on("-t", "--threads [THREADS]", "Maximum number of evaluations threads to run concurrently (Default: 1).") do |o|
103
+ @options[:threads] = o.to_i
104
+ end
67
105
  end
68
106
  end
69
107
 
data/lib/outliers/cli.rb CHANGED
@@ -31,7 +31,7 @@ module Outliers
31
31
  puts ''
32
32
  puts 'Append -h for help on specific subcommand.'
33
33
  puts ''
34
- puts 'See http://brettweavnet.github.io/outliers for documentation.'
34
+ puts 'See http://www.getoutliers.com/documentation for documentation.'
35
35
  puts ''
36
36
 
37
37
  puts 'Commands:'
@@ -43,36 +43,44 @@ module Outliers
43
43
  end
44
44
 
45
45
  def exclude_by_key(exclusions)
46
- @logger.info "Excluding the following resources: '#{exclusions.join(',')}'."
47
46
  save = list.reject {|u| exclusions.include? u.public_send key}
48
47
  @list = save
49
48
  end
50
49
 
51
- def filter(args)
50
+ def filter(action, args)
52
51
  name = args.keys.first
53
52
  value = args.fetch name
54
53
 
55
- logger.info "Applying filter '#{name}' with value '#{value}'."
56
-
57
54
  unless self.public_methods.include? "filter_#{name}".to_sym
58
55
  raise Exceptions::UnknownFilter.new "Unknown filter '#{name}'."
59
56
  end
60
57
 
61
58
  filtered_list = self.public_send "filter_#{name}", value
62
59
 
63
- logger.warn "No resources match filter." unless filtered_list.any?
64
-
65
- @list = filtered_list
60
+ case action
61
+ when 'include'
62
+ logger.info "Including resources filtered by '#{name}' with value '#{value}'."
63
+ logger.warn "No resources match filter." unless filtered_list.any?
64
+ @list = filtered_list & @list
65
+ when 'exclude'
66
+ logger.info "Excluding resources filtered by '#{name}' with value '#{value}'."
67
+ @list -= filtered_list
68
+ else
69
+ raise Exceptions::UnknownFilterAction.new "Filters must be either 'include' or 'exclude'."
70
+ end
66
71
  end
67
72
 
68
- def verify(name, arguments={})
69
- name << "?" unless name =~ /^.*\?$/
73
+ def verify(name, arguments=nil)
74
+ logger.debug "Verifying '#{name}'."
75
+
76
+ name += "?" unless name =~ /^.*\?$/
70
77
 
71
78
  unless list.any?
72
79
  return { failing_resources: [], passing_resources: [] }
73
80
  end
74
81
 
75
- logger.info "Verifying '#{name}'."
82
+ set_target_resources name if targets.any?
83
+
76
84
  logger.debug "Target resources '#{list_by_key.join(', ')}'."
77
85
 
78
86
  unless verification_exists? name
@@ -124,7 +132,7 @@ module Outliers
124
132
  end
125
133
 
126
134
  def set_target_resources(verification)
127
- logger.info "Verifying target '#{targets.join(', ')}'."
135
+ logger.debug "Setting target resource(s) to '#{targets.join(', ')}'."
128
136
 
129
137
  @list = list.select {|r| targets.include? r.id }
130
138
 
@@ -136,12 +144,10 @@ module Outliers
136
144
  end
137
145
 
138
146
  def send_resources_verification(verification, arguments)
139
- set_target_resources verification if targets.any?
140
-
141
147
  failing_resources = reject do |resource|
142
- result = send_verification resource, verification, arguments
143
- logger.debug "Verification of resource '#{resource.id}' #{result ? 'passed' : 'failed'}."
144
- result
148
+ r = send_verification resource, verification, arguments
149
+ logger.debug "Verification of resource '#{resource.id}' #{r ? 'passed' : 'failed'}."
150
+ r
145
151
  end
146
152
  { failing_resources: failing_resources, passing_resources: list - failing_resources }
147
153
  end
@@ -153,12 +159,12 @@ module Outliers
153
159
 
154
160
  def send_verification(object, verification, arguments)
155
161
  if object.method(verification).arity.zero?
156
- if arguments.any?
162
+ unless arguments.nil?
157
163
  raise Outliers::Exceptions::NoArgumentRequired.new "Verification '#{verification}' does not require an arguments."
158
164
  end
159
165
  object.public_send verification
160
166
  else
161
- if arguments.none?
167
+ if arguments.nil?
162
168
  raise Outliers::Exceptions::ArgumentRequired.new "Verification '#{verification}' requires arguments."
163
169
  end
164
170
  object.public_send verification, arguments
@@ -1,63 +1,92 @@
1
1
  module Outliers
2
2
  class Evaluation
3
3
 
4
- attr_reader :collection, :provider_name, :provider_name_array
4
+ attr_reader :resource_collection, :provider_name, :provider_name_array
5
5
 
6
6
  def initialize(args)
7
7
  @run = args[:run]
8
8
  @name = args[:name]
9
9
  end
10
10
 
11
- def connect(name, options={})
12
- @provider_name = merged_credentials(name, options).fetch 'provider'
11
+ def connect(account_name, options={})
12
+ @account_name = account_name
13
+ @provider_name = merged_account(account_name, options).fetch 'provider'
13
14
 
14
- logger.info "Connecting via '#{name}' to '#{@provider_name}'."
15
+ logger.info "Connecting via '#{account_name}' to '#{@provider_name}'."
15
16
  logger.info "Including connection options '#{options.map {|k,v| "#{k}=#{v}"}.join(',')}'." if options.any?
16
17
 
17
18
  set_provider_name_array
18
19
 
19
- @provider = Outliers::Provider.connect_to merged_credentials(name, options)
20
+ @provider = Outliers::Provider.connect_to merged_account(account_name, options)
20
21
  end
21
22
 
22
- def resources(name, targets=[])
23
- logger.info "Loading '#{name}' resource collection."
24
- @collection = collection_object name
23
+ def resources(name, targets=nil)
24
+ logger.debug "Loading '#{name}' resource collection."
25
25
 
26
- targets_array = Array(targets)
26
+ @resource_name = name
27
+ @resource_collection = collection_object name
27
28
 
28
- if targets_array.any?
29
- logger.info "Verifying '#{targets_array.join(', ')}' from '#{name}' collection."
30
- collection.targets = targets_array
31
- end
32
- collection
29
+ load_targets targets
30
+ resource_collection
33
31
  end
34
32
 
35
- def exclude(exclusions)
36
- collection.exclude_by_key Array(exclusions)
33
+ def filter(action, args)
34
+ resource_collection.filter action, args.keys_to_s
37
35
  end
38
36
 
39
- def filter(args)
40
- collection.filter args.keys_to_s
41
- end
37
+ def verify(verification_name, arguments=nil)
38
+ @resources_loaded ||= resource_collection.load_all
42
39
 
43
- def verify(verification, arguments={})
44
- @resources_loaded ||= collection.load_all
40
+ args_to_send = convert_verification_arguments arguments
45
41
 
46
- verification_result = collection.verify verification, arguments.keys_to_sym
42
+ verification_result = resource_collection.verify verification_name, args_to_send
47
43
 
48
- result = Outliers::Result.new evaluation: @name,
44
+ result = Outliers::Result.new account_name: @account_name,
49
45
  failing_resources: verification_result.fetch(:failing_resources),
46
+ name: @name,
50
47
  passing_resources: verification_result.fetch(:passing_resources),
51
- resource: @collection,
52
- verification: verification
48
+ arguments: Array(args_to_send),
49
+ provider_name: @provider_name,
50
+ resource_name: @resource_name,
51
+ verification_name: verification_name
53
52
 
54
- logger.info "Verification '#{verification}' #{result}."
53
+ logger.info "Verification '#{verification_name}' #{result.passed? ? 'passed' : 'failed'}."
55
54
 
56
55
  @run.results << result
57
56
  end
58
57
 
59
58
  private
60
59
 
60
+ def load_targets(targets)
61
+ case targets.class.to_s
62
+ when "Hash"
63
+ t = targets.keys_to_sym
64
+ if t.has_key? :include
65
+ list = Array(t.fetch :include)
66
+ logger.info "Targeting '#{list.join(', ')}' from '#{@resource_name}' collection."
67
+ resource_collection.targets = list
68
+ elsif t.has_key? :exclude
69
+ list = Array(t.fetch :exclude)
70
+ logger.info "Excluding '#{list.join(', ')}' from '#{@resource_name}' collection."
71
+ resource_collection.exclude_by_key list
72
+ else
73
+ logger.info "Targeting all resources in '#{@resource_name}' collection."
74
+ end
75
+ when "String", "Array"
76
+ list = Array(targets)
77
+ logger.info "Targeting '#{list.join(', ')}' from '#{@resource_name}' collection."
78
+ resource_collection.targets = list
79
+ when "Nil"
80
+ logger.info "Targeting all resources in '#{@resource_name}' collection."
81
+ end
82
+ end
83
+
84
+ def convert_verification_arguments(arguments)
85
+ return Array(arguments) if arguments.is_a?(Array) || arguments.is_a?(String)
86
+ return nil if arguments.is_a?(NilClass)
87
+ raise Outliers::Exceptions::InvalidArguments.new "Verification arguments '#{arguments}' invalid. Must be a string or array."
88
+ end
89
+
61
90
  def set_provider_name_array
62
91
  begin
63
92
  array = Outliers::Providers.name_map.fetch(provider_name).to_s.split('::')
@@ -75,13 +104,13 @@ module Outliers
75
104
  raise Outliers::Exceptions::UnknownCollection.new "Unknown collection '#{name}'."
76
105
  end
77
106
 
78
- def credentials(name)
79
- @run.credentials.fetch name
107
+ def account(name)
108
+ @run.account.fetch name
80
109
  end
81
110
 
82
- def merged_credentials(name, options)
83
- credentials(name).merge! options.keys_to_s
84
- credentials(name).merge :name => name
111
+ def merged_account(name, options)
112
+ account(name).merge! options.keys_to_s
113
+ account(name).merge :name => name
85
114
  end
86
115
 
87
116
  def logger
@@ -90,4 +119,3 @@ module Outliers
90
119
 
91
120
  end
92
121
  end
93
-
@@ -12,16 +12,22 @@ module Outliers
12
12
  class ArgumentRequired < Base
13
13
  end
14
14
 
15
+ class HandlerError < Base
16
+ end
17
+
15
18
  class InvalidBucket < Base
16
19
  end
17
20
 
21
+ class InvalidArguments < Base
22
+ end
23
+
18
24
  class NoArgumentRequired < Base
19
25
  end
20
26
 
21
27
  class UnknownCollection < Base
22
28
  end
23
29
 
24
- class UnknownCredentials < Base
30
+ class UnknownAccount < Base
25
31
  end
26
32
 
27
33
  class UnknownVerification < Base
@@ -30,6 +36,9 @@ module Outliers
30
36
  class UnknownFilter < Base
31
37
  end
32
38
 
39
+ class UnknownFilterAction < Base
40
+ end
41
+
33
42
  class UnknownProvider < Base
34
43
  end
35
44
 
@@ -7,12 +7,12 @@ module Outliers
7
7
  def filter_tag(value)
8
8
  tag_name = value.split(':').first
9
9
  tag_value = value.split(':').last
10
- logger.info "Filtering by tag '#{tag_name}' equals '#{tag_value}'."
10
+ logger.info "Loading filter by tag '#{tag_name}' equals '#{tag_value}'."
11
11
  list.select do |r|
12
12
  if r.tags.has_key? tag_name
13
13
  value = r.tags[tag_name]
14
14
  result = value == tag_value
15
- logger.debug "'#{r.id}' has tag with value '#{value}'. #{result ? 'Matches' : 'Does not match'} filter."
15
+ logger.debug "'#{r.id}' has tag '#{tag_name}' with value '#{value}'. #{result ? 'Matches' : 'Does not match'} filter."
16
16
  result
17
17
  else
18
18
  logger.debug "'#{r.id}' does not have tag '#{tag_name}'"
@@ -0,0 +1,36 @@
1
+ require 'net/http'
2
+
3
+ module Outliers
4
+ module Handlers
5
+ class JSON
6
+ def post(result)
7
+ host = 'localhost'
8
+ port = '3000'
9
+ path = '/results'
10
+
11
+ req = Net::HTTP::Post.new(path, initheader = { 'Content-Type' => 'application/json',
12
+ 'Accept' => 'application/vnd.outliers-v1+json' })
13
+ req.body = result.to_json
14
+
15
+ response = Net::HTTP.new(host, port).start {|http| http.request(req) }
16
+
17
+ logger.debug response.body
18
+
19
+ if response
20
+ logger.debug "Handler completed succesfully."
21
+ true
22
+ else
23
+ logger.debug "Handler failed."
24
+ false
25
+ end
26
+ end
27
+
28
+ private
29
+
30
+ def logger
31
+ @logger ||= Outliers.logger
32
+ end
33
+
34
+ end
35
+ end
36
+ end
@@ -0,0 +1,62 @@
1
+ require 'json'
2
+ require 'net/http'
3
+ require 'uri'
4
+
5
+ module Outliers
6
+ module Handlers
7
+ class OutliersApi
8
+ def post(result, key, url)
9
+ uri = URI.parse url
10
+
11
+ host = uri.host
12
+ port = uri.port
13
+ path = uri.path
14
+ use_ssl = uri.scheme == 'https'
15
+
16
+ req = Net::HTTP::Post.new path, header
17
+
18
+ req.body = body(key, result)
19
+
20
+ logger.debug "Hanlder URL: #{url}"
21
+ logger.debug "Posting: #{body("XXX", result)}"
22
+
23
+ session = Net::HTTP.new(host, port)
24
+ session.use_ssl = use_ssl
25
+
26
+ begin
27
+ response = session.start {|http| http.request(req) }
28
+ rescue Errno::ECONNREFUSED
29
+ logger.error "Connection to '#{url}' refused."
30
+ return false
31
+ end
32
+
33
+ if response.code == "200"
34
+ logger.debug "Received: #{response.body}"
35
+ logger.debug "Handler completed succesfully."
36
+ true
37
+ else
38
+ logger.error "Received: #{response.body}"
39
+ logger.error "Handler failed with code #{response.code}."
40
+ false
41
+ end
42
+ end
43
+
44
+ private
45
+
46
+ def logger
47
+ @logger ||= Outliers.logger
48
+ end
49
+
50
+ def header
51
+ { 'Content-Type' => 'application/json',
52
+ 'Accept' => 'application/vnd.outliers-v1+json' }
53
+ end
54
+
55
+ def body(key, result)
56
+ { 'key' => key,
57
+ 'result' => result.to_hash }.to_json
58
+ end
59
+
60
+ end
61
+ end
62
+ end
@@ -0,0 +1 @@
1
+ require 'outliers/handlers/outliers_api'