flapjack 1.0.0rc3 → 1.0.0rc5

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 (77) hide show
  1. checksums.yaml +4 -4
  2. data/.gitignore +1 -2
  3. data/.ruby-version +1 -0
  4. data/CHANGELOG.md +20 -0
  5. data/CONTRIBUTING.md +2 -2
  6. data/Gemfile +1 -1
  7. data/README.md +6 -16
  8. data/build.sh +13 -1
  9. data/etc/flapjack_config.yaml.example +98 -12
  10. data/features/cli.feature +8 -8
  11. data/features/cli_flapjack-nagios-receiver.feature +29 -37
  12. data/features/cli_flapper.feature +24 -12
  13. data/features/cli_simulate-failed-check.feature +2 -2
  14. data/features/notifications.feature +18 -1
  15. data/features/steps/cli_steps.rb +2 -2
  16. data/features/steps/notifications_steps.rb +71 -0
  17. data/features/support/env.rb +7 -6
  18. data/flapjack.gemspec +3 -1
  19. data/lib/flapjack/cli/flapper.rb +74 -25
  20. data/lib/flapjack/cli/import.rb +3 -4
  21. data/lib/flapjack/cli/maintenance.rb +182 -0
  22. data/lib/flapjack/cli/receiver.rb +110 -121
  23. data/lib/flapjack/cli/server.rb +30 -26
  24. data/lib/flapjack/cli/simulate.rb +2 -3
  25. data/lib/flapjack/data/contact.rb +1 -1
  26. data/lib/flapjack/data/entity.rb +425 -32
  27. data/lib/flapjack/data/entity_check.rb +212 -14
  28. data/lib/flapjack/data/event.rb +1 -1
  29. data/lib/flapjack/gateways/aws_sns.rb +134 -0
  30. data/lib/flapjack/gateways/aws_sns/alert.text.erb +5 -0
  31. data/lib/flapjack/gateways/aws_sns/rollup.text.erb +2 -0
  32. data/lib/flapjack/gateways/jabber.rb +2 -2
  33. data/lib/flapjack/gateways/jsonapi/check_methods.rb +1 -1
  34. data/lib/flapjack/gateways/jsonapi/contact_methods.rb +1 -1
  35. data/lib/flapjack/gateways/jsonapi/entity_methods.rb +15 -1
  36. data/lib/flapjack/gateways/jsonapi/metrics_methods.rb +4 -3
  37. data/lib/flapjack/gateways/jsonapi/report_methods.rb +1 -1
  38. data/lib/flapjack/gateways/web.rb +35 -16
  39. data/lib/flapjack/gateways/web/public/css/tablesort.css +0 -16
  40. data/lib/flapjack/gateways/web/public/js/backbone.jsonapi.js +1 -1
  41. data/lib/flapjack/gateways/web/public/js/jquery.tablesorter.widgets.js +0 -45
  42. data/lib/flapjack/gateways/web/public/js/modules/contact.js +2 -2
  43. data/lib/flapjack/gateways/web/public/js/modules/entity.js +2 -2
  44. data/lib/flapjack/gateways/web/public/js/modules/medium.js +4 -4
  45. data/lib/flapjack/gateways/web/public/js/self_stats.js +1 -1
  46. data/lib/flapjack/gateways/web/views/check.html.erb +10 -10
  47. data/lib/flapjack/gateways/web/views/checks.html.erb +1 -1
  48. data/lib/flapjack/gateways/web/views/contact.html.erb +5 -1
  49. data/lib/flapjack/gateways/web/views/edit_contacts.html.erb +3 -4
  50. data/lib/flapjack/gateways/web/views/entities.html.erb +1 -1
  51. data/lib/flapjack/gateways/web/views/index.html.erb +2 -2
  52. data/lib/flapjack/gateways/web/views/layout.erb +3 -3
  53. data/lib/flapjack/gateways/web/views/self_stats.html.erb +5 -6
  54. data/lib/flapjack/notifier.rb +4 -1
  55. data/lib/flapjack/patches.rb +8 -2
  56. data/lib/flapjack/pikelet.rb +3 -1
  57. data/lib/flapjack/version.rb +1 -1
  58. data/libexec/httpbroker.go +1 -1
  59. data/spec/lib/flapjack/coordinator_spec.rb +3 -3
  60. data/spec/lib/flapjack/data/contact_spec.rb +2 -2
  61. data/spec/lib/flapjack/data/entity_check_spec.rb +805 -53
  62. data/spec/lib/flapjack/data/entity_spec.rb +661 -0
  63. data/spec/lib/flapjack/gateways/aws_sns_spec.rb +123 -0
  64. data/spec/lib/flapjack/gateways/jabber_spec.rb +1 -1
  65. data/spec/lib/flapjack/gateways/jsonapi/check_methods_spec.rb +1 -1
  66. data/spec/lib/flapjack/gateways/jsonapi/entity_methods_spec.rb +2 -2
  67. data/spec/lib/flapjack/gateways/pagerduty_spec.rb +1 -1
  68. data/spec/lib/flapjack/gateways/web_spec.rb +11 -11
  69. data/spec/support/profile_all_formatter.rb +10 -10
  70. data/spec/support/uncolored_doc_formatter.rb +66 -4
  71. data/src/flapjack/event.go +1 -1
  72. data/tasks/benchmarks.rake +24 -20
  73. data/tasks/entities.rake +148 -0
  74. data/tmp/dummy_contacts.json +43 -0
  75. data/tmp/dummy_entities.json +37 -1
  76. metadata +43 -7
  77. data/tmp/test_entities.json +0 -1
@@ -0,0 +1,123 @@
1
+ require 'spec_helper'
2
+ require 'flapjack/gateways/aws_sns'
3
+
4
+ describe Flapjack::Gateways::AwsSns, :logger => true do
5
+
6
+ let(:lock) { double(Monitor) }
7
+
8
+ let(:time) { Time.new(2013, 10, 31, 13, 45) }
9
+
10
+ let(:time_str) { Time.at(time).strftime('%-d %b %H:%M') }
11
+
12
+ let(:config) { {'region' => 'us-east-1',
13
+ 'access_key' => 'AKIAIOSFODNN7EXAMPLE',
14
+ 'secret_key' => 'secret'
15
+ }
16
+ }
17
+
18
+ let(:message) { {'notification_type' => 'recovery',
19
+ 'contact_first_name' => 'John',
20
+ 'contact_last_name' => 'Smith',
21
+ 'state' => 'ok',
22
+ 'summary' => 'smile',
23
+ 'last_state' => 'problem',
24
+ 'last_summary' => 'frown',
25
+ 'time' => time.to_i,
26
+ 'address' => 'arn:aws:sns:us-east-1:698519295917:My-Topic',
27
+ 'event_id' => 'example.com:ping',
28
+ 'id' => '123456789',
29
+ 'duration' => 55,
30
+ 'state_duration' => 23
31
+ }
32
+ }
33
+
34
+ it "sends an SMS message" do
35
+ req = stub_request(:post, "http://sns.us-east-1.amazonaws.com/").
36
+ with(:query => hash_including({'Action' => 'Publish',
37
+ 'AWSAccessKeyId' => config['access_key'],
38
+ 'TopicArn' => message['address'],
39
+ 'SignatureVersion' => '2',
40
+ 'SignatureMethod' => 'HmacSHA256'})).
41
+ to_return(:status => 200)
42
+
43
+ EM.synchrony do
44
+ Flapjack::Gateways::AwsSns.instance_variable_set('@config', config)
45
+ Flapjack::Gateways::AwsSns.instance_variable_set('@logger', @logger)
46
+ Flapjack::Gateways::AwsSns.start
47
+ Flapjack::Gateways::AwsSns.perform(message)
48
+ EM.stop
49
+ end
50
+ expect(req).to have_been_requested
51
+ end
52
+
53
+ it "does not send an SMS message with an invalid config" do
54
+ EM.synchrony do
55
+ Flapjack::Gateways::AwsSns.instance_variable_set('@config', config.reject {|k, v| k == 'secret_key'})
56
+ Flapjack::Gateways::AwsSns.instance_variable_set('@logger', @logger)
57
+ Flapjack::Gateways::AwsSns.start
58
+ Flapjack::Gateways::AwsSns.perform(message)
59
+ EM.stop
60
+ end
61
+
62
+ expect(WebMock).not_to have_requested(:get, "http://sns.us-east-1.amazonaws.com/")
63
+ end
64
+
65
+ context "#string_to_sign" do
66
+
67
+ let(:method) { 'post' }
68
+
69
+ let(:host) { 'sns.us-east-1.AmazonAWS.com' }
70
+
71
+ let(:uri) { '/' }
72
+
73
+ let(:query) { {'TopicArn' => 'HelloWorld',
74
+ 'Action' => 'Publish'} }
75
+
76
+ let(:string_to_sign) { Flapjack::Gateways::AwsSns.string_to_sign(method, host, uri, query) }
77
+
78
+ let(:lines) { string_to_sign.split(/\n/) }
79
+
80
+ it 'should put the method on the first line' do
81
+ expect(lines[0]).to eq("POST")
82
+ end
83
+
84
+ it 'should put the downcased hostname on the second line' do
85
+ expect(lines[1]).to eq("sns.us-east-1.amazonaws.com")
86
+ end
87
+
88
+ it 'should put the URI on the third line' do
89
+ expect(lines[2]).to eq("/")
90
+ end
91
+
92
+ it 'should put the encoded, sorted query-string on the fourth line' do
93
+ expect(lines[3]).to eq("Action=Publish&TopicArn=HelloWorld")
94
+ end
95
+
96
+ end
97
+
98
+ context "#get_signature" do
99
+ let(:method) { 'GET' }
100
+
101
+ let(:host) { 'elasticmapreduce.amazonaws.com' }
102
+
103
+ let(:uri) { '/' }
104
+
105
+ let(:query) { {'AWSAccessKeyId' => 'AKIAIOSFODNN7EXAMPLE',
106
+ 'Action' => 'DescribeJobFlows',
107
+ 'SignatureMethod' => 'HmacSHA256',
108
+ 'SignatureVersion' => '2',
109
+ 'Timestamp' => '2011-10-03T15:19:30',
110
+ 'Version' => '2009-03-31'} }
111
+
112
+ let(:secret_key) { 'wJalrXUtnFEMI/K7MDENG/bPxRfiCYEXAMPLEKEY' }
113
+
114
+ let(:string_to_sign) { Flapjack::Gateways::AwsSns.string_to_sign(method, host, uri, query) }
115
+
116
+ let(:signature) { Flapjack::Gateways::AwsSns.get_signature(secret_key, string_to_sign) }
117
+
118
+ it 'should HMAC-SHA256 and base64 encode the signature' do
119
+ expect(signature).to eq("i91nKc4PWAt0JJIdXwz9HxZCJDdiy6cf/Mj6vPxyYIs=")
120
+ end
121
+ end
122
+
123
+ end
@@ -174,7 +174,7 @@ describe Flapjack::Gateways::Jabber, :logger => true do
174
174
 
175
175
  expect(EventMachine::Synchrony).to receive(:sleep).with(5).exactly(1).times
176
176
  expect(EventMachine::Synchrony).to receive(:sleep).with(2).exactly(3).times
177
- expect(fj).to receive(:connect).exactly(4).times.and_return {
177
+ expect(fj).to receive(:connect).exactly(4).times {
178
178
  attempts +=1
179
179
  raise StandardError.new unless attempts > 3
180
180
  }
@@ -22,7 +22,7 @@ describe 'Flapjack::Gateways::JSONAPI::CheckMethods', :sinatra => true, :logger
22
22
  expect(entity_check).to receive(:entity).and_return(entity)
23
23
  expect(entity_check).to receive(:key).twice.and_return('PING')
24
24
  expect(entity_check).to receive(:to_jsonapi).and_return(check_data.to_json)
25
- expect(Flapjack::Data::EntityCheck).to receive(:find_all).with(:redis => redis).
25
+ expect(Flapjack::Data::EntityCheck).to receive(:find_current).with(:redis => redis).
26
26
  and_return([entity_check])
27
27
 
28
28
  aget '/checks'
@@ -22,7 +22,7 @@ describe 'Flapjack::Gateways::JSONAPI::EntityMethods', :sinatra => true, :logger
22
22
  expect(Flapjack::Data::Entity).to receive(:contact_ids_for).
23
23
  with([entity_core['id']], :redis => redis).and_return({})
24
24
  expect(entity).to receive(:to_jsonapi).and_return(entity_core.to_json)
25
- expect(Flapjack::Data::Entity).to receive(:all).with(:redis => redis).
25
+ expect(Flapjack::Data::Entity).to receive(:all).with(:enabled => nil, :redis => redis).
26
26
  and_return([entity])
27
27
 
28
28
  aget '/entities'
@@ -38,7 +38,7 @@ describe 'Flapjack::Gateways::JSONAPI::EntityMethods', :sinatra => true, :logger
38
38
  with([entity_core['id']], :redis => redis).and_return({})
39
39
  expect(entity).to receive(:to_jsonapi).and_return(entity_core.to_json)
40
40
  expect(idless_entity).not_to receive(:to_jsonapi)
41
- expect(Flapjack::Data::Entity).to receive(:all).with(:redis => redis).
41
+ expect(Flapjack::Data::Entity).to receive(:all).with(:enabled => nil, :redis => redis).
42
42
  and_return([entity, idless_entity])
43
43
 
44
44
  aget '/entities'
@@ -182,7 +182,7 @@ describe Flapjack::Gateways::Pagerduty, :logger => true do
182
182
 
183
183
  EM.synchrony do
184
184
  ret = fp.send(:send_pagerduty_event, evt)
185
- expect { ret }.not_to be_nil
185
+ expect(ret).not_to be_nil
186
186
  expect(ret).to eq([200, nil])
187
187
  EM.stop
188
188
  end
@@ -7,7 +7,7 @@ describe Flapjack::Gateways::Web, :sinatra => true, :logger => true do
7
7
  Flapjack::Gateways::Web
8
8
  end
9
9
 
10
- let(:entity_name) { 'example.com'}
10
+ let(:entity_name) { 'foo-app-01.example.com'}
11
11
  let(:entity_name_esc) { CGI.escape(entity_name) }
12
12
  let(:check) { 'ping' }
13
13
 
@@ -30,20 +30,20 @@ describe Flapjack::Gateways::Web, :sinatra => true, :logger => true do
30
30
  expect(redis).to receive(:hgetall).twice.and_return({'all' => '8001', 'ok' => '8002'},
31
31
  {'all' => '9001', 'ok' => '9002'})
32
32
  expect(redis).to receive(:llen).with('events')
33
- expect(redis).to receive(:zrange).with('current_entities', 0, -1).and_return(['foo-app-01.example.com'])
34
- expect(redis).to receive(:zrange).with('current_checks:foo-app-01.example.com', 0, -1, {:withscores => true}).and_return([['ping', 1382329923.0]])
33
+ expect(Flapjack::Data::EntityCheck).to receive(:find_all_split_by_freshness).
34
+ and_return(30 => 3)
35
35
  end
36
36
 
37
37
  def expect_check_stats
38
- expect(Flapjack::Data::EntityCheck).to receive(:count_all).
38
+ expect(Flapjack::Data::EntityCheck).to receive(:count_current).
39
39
  with(:redis => redis).and_return(1)
40
- expect(Flapjack::Data::EntityCheck).to receive(:count_all_failing).
40
+ expect(Flapjack::Data::EntityCheck).to receive(:count_current_failing).
41
41
  with(:redis => redis).and_return(1)
42
42
  end
43
43
 
44
44
  def expect_entity_stats
45
- expect(Flapjack::Data::Entity).to receive(:find_all_with_checks).
46
- with(:redis => redis).and_return([entity_name])
45
+ expect(Flapjack::Data::Entity).to receive(:all).
46
+ with(:enabled => true, :redis => redis).and_return([entity_name])
47
47
  expect(Flapjack::Data::Entity).to receive(:find_all_with_failing_checks).
48
48
  with(:redis => redis).and_return([entity_name])
49
49
  end
@@ -87,7 +87,6 @@ describe Flapjack::Gateways::Web, :sinatra => true, :logger => true do
87
87
  logo_image_tag = '<img alt="Flapjack" class="logo" src="/img/branding.png">'
88
88
 
89
89
  aget '/self_stats'
90
-
91
90
  expect( last_response.body ).to include(logo_image_tag)
92
91
  end
93
92
 
@@ -121,14 +120,14 @@ describe Flapjack::Gateways::Web, :sinatra => true, :logger => true do
121
120
 
122
121
  it "shows a page listing all checks" do
123
122
  #redis.should_receive(:keys).with('*:*:states').and_return(["#{entity_name}:#{check}"])
124
- expect(Flapjack::Data::EntityCheck).to receive(:find_all_by_entity).
123
+ expect(Flapjack::Data::EntityCheck).to receive(:find_current_by_entity).
125
124
  with(:redis => redis).and_return({entity_name => [check]})
126
125
  expect_check_stats
127
126
 
128
127
  expect_entity_check_status(entity_check)
129
128
 
130
129
  expect(Flapjack::Data::Entity).to receive(:find_by_name).
131
- with(entity_name, :redis => redis).and_return(entity)
130
+ with(entity_name, hash_including(:redis => redis)).twice.and_return(entity)
132
131
 
133
132
  expect(Flapjack::Data::EntityCheck).to receive(:for_entity).
134
133
  with(entity, 'ping', :redis => redis).and_return(entity_check)
@@ -147,7 +146,7 @@ describe Flapjack::Gateways::Web, :sinatra => true, :logger => true do
147
146
  expect(Flapjack::Data::Entity).to receive(:find_by_name).
148
147
  with(entity_name, :redis => redis).and_return(entity)
149
148
 
150
- expect(Flapjack::Data::EntityCheck).to receive(:find_all_failing_by_entity).
149
+ expect(Flapjack::Data::EntityCheck).to receive(:find_current_failing_by_entity).
151
150
  with(:redis => redis).and_return({entity_name => [check]})
152
151
 
153
152
  expect(Flapjack::Data::EntityCheck).to receive(:for_entity).
@@ -164,6 +163,7 @@ describe Flapjack::Gateways::Web, :sinatra => true, :logger => true do
164
163
  expect_entity_stats
165
164
 
166
165
  aget '/self_stats'
166
+ # p @logger.messages
167
167
  expect(last_response).to be_ok
168
168
  end
169
169
 
@@ -2,32 +2,32 @@ require 'rspec/core/formatters/base_formatter'
2
2
 
3
3
  class ProfileAllFormatter < RSpec::Core::Formatters::BaseFormatter
4
4
 
5
+ RSpec::Core::Formatters.register self,
6
+ :example_started, :example_passed, :start_dump
7
+
5
8
  def initialize(output)
6
9
  super(output)
7
10
  @example_times = []
8
11
  end
9
12
 
10
- def start(count)
11
- super(count)
13
+ def start(notification)
14
+ super(notification)
12
15
  @output.puts "Profiling enabled."
13
16
  end
14
17
 
15
- def example_started(example)
16
- super(example)
18
+ def example_started(notification)
17
19
  @time = ((Time.respond_to?(:zone) && Time.zone) ? Time.zone.now : Time.now)
18
20
  end
19
21
 
20
- def example_passed(example)
22
+ def example_passed(notification)
21
23
  @example_times << [
22
- example_group.description,
23
- example.description,
24
+ notification.example.example_group.description,
25
+ notification.example.description,
24
26
  ((Time.respond_to?(:zone) && Time.zone) ? Time.zone.now : Time.now) - @time
25
27
  ]
26
- super(example)
27
28
  end
28
29
 
29
- def start_dump
30
- super
30
+ def start_dump(notification)
31
31
  @output.puts "\n\nExample times:\n"
32
32
 
33
33
  @example_times = @example_times.sort_by do |description, example, time|
@@ -1,9 +1,71 @@
1
- require 'rspec/core/formatters/documentation_formatter'
1
+ require 'rspec/core/formatters/base_text_formatter'
2
2
 
3
- class UncoloredDocFormatter < RSpec::Core::Formatters::DocumentationFormatter
4
-
5
- def color(text, color_code)
3
+ module NoColorizer
4
+ def self.wrap(text, color)
6
5
  text
7
6
  end
7
+ end
8
+
9
+ class UncoloredDocFormatter < RSpec::Core::Formatters::BaseTextFormatter
10
+ RSpec::Core::Formatters.register self, :example_group_started, :example_group_finished,
11
+ :example_passed, :example_pending, :example_failed,
12
+ :dump_failures, :dump_pending, :dump_summary
13
+
14
+ def initialize(output)
15
+ super
16
+ @group_level = 0
17
+ end
18
+
19
+ def example_group_started(notification)
20
+ output.puts if @group_level == 0
21
+ output.puts "#{current_indentation}#{notification.group.description.strip}"
22
+
23
+ @group_level += 1
24
+ end
25
+
26
+ def example_group_finished(notification)
27
+ @group_level -= 1
28
+ end
29
+
30
+ def example_passed(passed)
31
+ output.puts "#{current_indentation}#{passed.example.description.strip}"
32
+ end
33
+
34
+ def example_pending(pending)
35
+ output.puts "#{current_indentation}#{pending.example.description.strip} (PENDING: #{pending.example.execution_result.pending_message})"
36
+ end
37
+
38
+ def example_failed(failure)
39
+ output.puts "#{current_indentation}#{failure.example.description.strip} (FAILED - #{next_failure_index})"
40
+ end
41
+
42
+ def dump_failures(notification)
43
+ return if notification.failure_notifications.empty?
44
+ output.puts notification.fully_formatted_failed_examples(NoColorizer)
45
+ end
46
+
47
+ def dump_pending(notification)
48
+ return if notification.pending_examples.empty?
49
+ output.puts notification.fully_formatted_pending_examples(NoColorizer)
50
+ end
51
+
52
+ def dump_summary(notification)
53
+ output.puts notification.fully_formatted(NoColorizer)
54
+ end
55
+
56
+ private
57
+
58
+ def next_failure_index
59
+ @next_failure_index ||= 0
60
+ @next_failure_index += 1
61
+ end
62
+
63
+ def current_indentation
64
+ ' ' * @group_level
65
+ end
66
+
67
+ def example_group_chain
68
+ example_group.parent_groups.reverse
69
+ end
8
70
 
9
71
  end
@@ -3,7 +3,7 @@ package flapjack
3
3
  import "errors"
4
4
 
5
5
  // Event is a basic representation of a Flapjack event.
6
- // Find more at https://github.com/flapjack/flapjack/wiki/DATA_STRUCTURES
6
+ // Find more at http://flapjack.io/docs/1.0/development/DATA_STRUCTURES
7
7
  type Event struct {
8
8
  Entity string `json:"entity"`
9
9
  Check string `json:"check"`
@@ -2,36 +2,40 @@ require 'redis'
2
2
  require 'oj'
3
3
  require 'time'
4
4
 
5
+ # add lib to the default include path
6
+ unless $:.include?(File.dirname(__FILE__) + '/../lib/')
7
+ $: << File.dirname(__FILE__) + '/../lib'
8
+ end
9
+
10
+ require 'flapjack/configuration'
11
+ require 'flapjack/data/event'
12
+ require 'flapjack/data/entity_check'
13
+ require 'flapjack/version'
14
+
5
15
  namespace :benchmarks do
6
16
 
7
- # add lib to the default include path
8
- unless $:.include?(File.dirname(__FILE__) + '/../lib/')
9
- $: << File.dirname(__FILE__) + '/../lib'
17
+ def redis
18
+ @redis ||= Redis.new(@redis_config)
10
19
  end
11
20
 
12
- require 'flapjack/configuration'
13
- require 'flapjack/data/event'
14
- require 'flapjack/data/entity_check'
15
- require 'flapjack/version'
21
+ task :setup do
22
+ FLAPJACK_ENV = 'test'
23
+ config_file = File.join('tasks', 'support', 'flapjack_config_benchmark.yaml')
16
24
 
17
- FLAPJACK_ENV = 'test'
18
- config_file = File.join('tasks', 'support', 'flapjack_config_benchmark.yaml')
25
+ config = Flapjack::Configuration.new
26
+ config.load( config_file )
19
27
 
20
- config = Flapjack::Configuration.new
21
- config.load( config_file )
28
+ @config_env = config.all
29
+ @redis_config = config.for_redis
22
30
 
23
- @config_env = config.all
24
- @redis_config = config.for_redis
25
-
26
- if @config_env.nil? || @config_env.empty?
27
- puts "No config data for environment '#{FLAPJACK_ENV}' found in '#{config_file}'"
28
- exit(false)
31
+ if @config_env.nil? || @config_env.empty?
32
+ puts "No config data for environment '#{FLAPJACK_ENV}' found in '#{config_file}'"
33
+ exit(false)
34
+ end
29
35
  end
30
36
 
31
- redis = Redis.new(@redis_config)
32
-
33
37
  desc "nukes the redis db, generates the events, runs and shuts down flapjack, generates perftools reports"
34
- task :run => [:reset_redis, :benchmark, :run_flapjack, :reports] do
38
+ task :run => [:setup, :reset_redis, :benchmark, :run_flapjack, :reports] do
35
39
  puts Oj.dump(@benchmark_data, :indent => 2)
36
40
  end
37
41
 
@@ -0,0 +1,148 @@
1
+ require 'csv'
2
+ require 'securerandom'
3
+
4
+ require 'redis'
5
+
6
+ require 'flapjack/configuration'
7
+ require 'flapjack/data/entity'
8
+
9
+ FLAPJACK_ENV = ENV['FLAPJACK_ENV'] || 'production'
10
+
11
+ namespace :entities do
12
+
13
+ def redis
14
+ @redis ||= Redis.new(@redis_config)
15
+ end
16
+
17
+ def orphaned_entity_names
18
+ current_names = Flapjack::Data::Entity.all(:redis => redis).map(&:name)
19
+
20
+ all_entity_names = redis.keys("check:*:*").inject([]) do |memo, ck|
21
+ if ck =~ /^check:([^:]+):.+$/
22
+ memo << $1
23
+ end
24
+
25
+ memo
26
+ end
27
+
28
+ all_entity_names - current_names
29
+ end
30
+
31
+ task :setup do
32
+ config_file = File.join('etc', 'flapjack_config.yaml')
33
+
34
+ config = Flapjack::Configuration.new
35
+ config.load( config_file )
36
+
37
+ @config_env = config.all
38
+ @redis_config = config.for_redis
39
+
40
+ if @config_env.nil? || @config_env.empty?
41
+ puts "No config data for environment '#{FLAPJACK_ENV}' found in '#{config_file}'"
42
+ exit(false)
43
+ end
44
+ end
45
+
46
+ desc "lists current entity names and their ids"
47
+ task :current => [:setup] do
48
+ puts CSV.generate(:headers => :first_row, :write_headers => true, :row_sep => "\r\n") {|csv|
49
+ csv << ['id', 'name']
50
+ Flapjack::Data::Entity.all(:redis => redis).each do |entity|
51
+ csv << [entity.id, entity.name]
52
+ end
53
+ }
54
+ end
55
+
56
+ desc "lists entities with data orphaned by entity renames, or created without ids, prior to v1.0"
57
+ task :orphaned => [:setup] do
58
+ orph = orphaned_entity_names
59
+
60
+ puts CSV.generate(:headers => :first_row, :write_headers => true, :row_sep => "\r\n") {|csv|
61
+ csv << ['name']
62
+ orph.each {|orp| csv << [orp]}
63
+ }
64
+ end
65
+
66
+ desc "reparent entity names from a CSV file on input"
67
+ task :reparent => [:setup] do
68
+
69
+ # The CSV should be in the form 'id', 'name', 'old_name'
70
+ # If more than one old name exists, another row should be used with
71
+ # the first two values repeated. The id is there as a sanity check
72
+ # against current data.
73
+
74
+ entities = {}
75
+ old_entities = {}
76
+
77
+ CSV.new($stdin, :headers => :first_row, :return_headers => false) {|csv|
78
+ csv.each {|row|
79
+
80
+ id = row['id']
81
+ name = row['name']
82
+ old_name = row['old_name']
83
+
84
+ if entities.has_key?(id)
85
+ unless entities[id].eql?(name)
86
+ raise "Different names for entity id 'id': '#{name}', '#{old_name}'"
87
+ end
88
+ else
89
+ entities[id] = name
90
+ end
91
+
92
+ old_entities[id] ||= []
93
+ old_entities[id] << old_name
94
+ }
95
+
96
+ old_entities.each_pair do |id, old_entities_for_id|
97
+ old_entities_for_id.each do |old_entity|
98
+ puts "Re-parenting '#{old_entity}' data to '#{entities[id]}'"
99
+ Flapjack::Data::Entity.merge(old_entity, entities[id], :redis => redis)
100
+ end
101
+ end
102
+ }
103
+
104
+ end
105
+
106
+ # Checks sent by monitoring data before the related entity had been imported,
107
+ # before 1.0, had autogenerated entities saved without ids, and these entities
108
+ # were not re-integrated later. This task will detect and fix any such data.
109
+ desc "give an identity to entities that never had one"
110
+ task :repatriate => [:setup] do
111
+
112
+ orph = orphaned_entity_names
113
+
114
+ CSV.new($stdin, :headers => :first_row, :return_headers => false) {|csv|
115
+ csv.each {|row|
116
+
117
+ name = row['name']
118
+ id = row['id']
119
+
120
+ if !orph.include?(name)
121
+ puts "'#{name}' is not an orphaned entity."
122
+ next
123
+ end
124
+
125
+ entity = id.nil? ? nil : Flapjack::Data::Entity.find_by_id(id)
126
+
127
+ if id.nil? || entity.nil?
128
+ id ||= SecureRandom.uuid
129
+ redis.multi
130
+ redis.set("entity_id:#{name}", id)
131
+ redis.hset("entity:#{id}", 'name', name)
132
+ redis.exec
133
+ puts "Set id '#{id}' for entity #{name}'"
134
+ elsif entity.name.eql?(name)
135
+ puts "'#{name}' entity already exists with the provided id"
136
+ else
137
+ # entity exists, name doesn't match -- merge old data
138
+ Flapjack::Data::Entity.merge(name, entity.name, :redis => redis)
139
+ puts "Merged data for entity '#{name}' into entity #{entity.name}' with id '#{id}'"
140
+ end
141
+
142
+ }
143
+
144
+ }
145
+
146
+ end
147
+
148
+ end