outliers 0.3.3 → 0.5.0.beta1

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.
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'