rearview 1.1.2-jruby → 1.2.0-jruby

Sign up to get free protection for your applications and to get access to all the features.
Files changed (106) hide show
  1. checksums.yaml +4 -4
  2. data/README.md +11 -3
  3. data/app/controllers/rearview/application_controller.rb +11 -0
  4. data/app/controllers/rearview/dashboard_children_controller.rb +1 -1
  5. data/app/controllers/rearview/dashboards_controller.rb +1 -1
  6. data/app/controllers/rearview/home_controller.rb +1 -1
  7. data/app/controllers/rearview/jobs_controller.rb +33 -29
  8. data/app/controllers/rearview/monitor_controller.rb +16 -2
  9. data/app/controllers/rearview/user_controller.rb +2 -2
  10. data/app/helpers/rearview/application_helper.rb +3 -3
  11. data/app/mailers/rearview/alert_mailer.rb +0 -2
  12. data/app/mailers/rearview/metrics_validation_mailer.rb +12 -0
  13. data/app/models/rearview/job.rb +17 -13
  14. data/app/views/rearview/home/show.html.erb +2 -0
  15. data/app/views/rearview/jobs/_job.json.jbuilder +1 -0
  16. data/app/views/rearview/metrics_validation_mailer/validation_failed_email.text.erb +13 -0
  17. data/app/views/rearview/monitor/create.json.jbuilder +1 -0
  18. data/db/migrate/20131106162900_base_schema.rb +2 -2
  19. data/lib/generators/templates/rearview.rb +24 -7
  20. data/lib/graphite.rb +13 -0
  21. data/lib/graphite/cacerts.pem +2184 -0
  22. data/lib/graphite/client.rb +44 -0
  23. data/lib/graphite/graph.rb +35 -0
  24. data/lib/{rearview/graphite_parser.rb → graphite/raw_parser.rb} +2 -3
  25. data/lib/graphite/target.rb +9 -0
  26. data/lib/graphite/target_grammer.rb +114 -0
  27. data/lib/graphite/target_grammer.treetop +53 -0
  28. data/lib/graphite/target_parser.rb +50 -0
  29. data/lib/graphite/treetop_ext.rb +14 -0
  30. data/lib/rearview.rb +11 -3
  31. data/lib/rearview/alerts_handler.rb +2 -6
  32. data/lib/rearview/configuration.rb +44 -19
  33. data/lib/rearview/cron_expression_validator.rb +11 -0
  34. data/lib/rearview/metrics_validator.rb +52 -0
  35. data/lib/rearview/metrics_validator_service.rb +24 -0
  36. data/lib/rearview/metrics_validator_task.rb +56 -0
  37. data/lib/rearview/monitor_runner.rb +9 -7
  38. data/lib/rearview/monitor_service.rb +2 -0
  39. data/lib/rearview/stats_service.rb +4 -2
  40. data/lib/rearview/version.rb +1 -1
  41. data/lib/tasks/rearview_tasks.rake +7 -1
  42. data/public/{help → rearview-src/help}/alert.html +0 -0
  43. data/public/{help → rearview-src/help}/quick.html +0 -0
  44. data/public/rearview-src/js/app.js +9 -0
  45. data/public/rearview-src/js/model/user.js +6 -2
  46. data/public/rearview-src/js/view/addmonitor.js +13 -8
  47. data/public/rearview-src/js/view/alert.js +11 -4
  48. data/public/rearview-src/js/view/dashboard.js +4 -2
  49. data/public/rearview-src/js/view/expandedmonitor.js +22 -9
  50. data/public/rearview-src/js/view/settings.js +84 -0
  51. data/public/rearview-src/less/login.less +4 -4
  52. data/public/rearview-src/less/rearview.less +17 -10
  53. data/public/{monitors → rearview-src/monitors}/index.json +1 -1
  54. data/public/{monitors → rearview-src/monitors}/outage.rb +0 -0
  55. data/public/rearview-src/templates/alert.hbs +10 -2
  56. data/public/rearview-src/templates/primarynav.hbs +6 -6
  57. data/public/rearview-src/templates/schedulemonitor.hbs +2 -1
  58. data/public/rearview-src/templates/settings.hbs +23 -0
  59. data/public/rearview/build.txt +1 -0
  60. data/public/rearview/help/alert.html +20 -0
  61. data/public/rearview/help/quick.html +34 -0
  62. data/public/rearview/js/app.js +1 -1
  63. data/public/rearview/js/main.js +21 -21
  64. data/public/rearview/js/model/user.js +1 -1
  65. data/public/rearview/js/view/addmonitor.js +1 -1
  66. data/public/rearview/js/view/alert.js +1 -1
  67. data/public/rearview/js/view/dashboard.js +1 -1
  68. data/public/rearview/js/view/expandedmonitor.js +1 -1
  69. data/public/rearview/js/view/settings.js +1 -0
  70. data/public/rearview/less/login.less +4 -4
  71. data/public/rearview/less/rearview.less +17 -10
  72. data/public/rearview/monitors/index.json +3 -0
  73. data/public/rearview/monitors/outage.rb +2 -0
  74. data/public/rearview/templates/alert.hbs +10 -2
  75. data/public/rearview/templates/primarynav.hbs +6 -6
  76. data/public/rearview/templates/schedulemonitor.hbs +2 -1
  77. data/public/rearview/templates/settings.hbs +23 -0
  78. data/spec/controllers/jobs_controller_spec.rb +1 -0
  79. data/spec/controllers/monitor_controller_spec.rb +3 -0
  80. data/spec/controllers/user_controller_spec.rb +5 -2
  81. data/spec/data/metrics.yml +598 -0
  82. data/spec/dummy/log/development.log +1044 -0
  83. data/spec/dummy/log/test.log +171716 -0
  84. data/spec/helpers/application_helper_spec.rb +33 -0
  85. data/spec/lib/graphite/client_spec.rb +126 -0
  86. data/spec/lib/graphite/graph_spec.rb +17 -0
  87. data/spec/lib/graphite/graphite_spec.rb +4 -0
  88. data/spec/lib/{rearview/graphite_parser_spec.rb → graphite/raw_parser.rb} +6 -5
  89. data/spec/lib/graphite/target_grammer_spec.rb +106 -0
  90. data/spec/lib/graphite/target_parser_spec.rb +124 -0
  91. data/spec/lib/graphite/target_spec.rb +5 -0
  92. data/spec/lib/rearview/configuration_spec.rb +69 -48
  93. data/spec/lib/rearview/metrics_validator_service_spec.rb +43 -0
  94. data/spec/lib/rearview/metrics_validator_spec.rb +84 -0
  95. data/spec/lib/rearview/metrics_validator_task_spec.rb +62 -0
  96. data/spec/lib/rearview/monitor_runner_spec.rb +3 -3
  97. data/spec/lib/rearview/stats_task_spec.rb +21 -0
  98. data/spec/mailers/metrics_validation_mailer_spec.rb +46 -0
  99. data/spec/models/job_spec.rb +82 -9
  100. data/spec/spec_helper.rb +15 -4
  101. data/spec/support/json_factory.rb +1 -1
  102. data/spec/views/dashboards/show.json.jbuilder_spec.rb +3 -1
  103. data/spec/views/jobs/show.json.jbuilder_spec.rb +2 -1
  104. metadata +98 -11
  105. data/public/rearview-src/templates/test.txt +0 -1
  106. data/public/rearview/templates/test.txt +0 -1
@@ -0,0 +1,5 @@
1
+ require 'spec_helper'
2
+
3
+ describe Graphite::Target do
4
+ end
5
+
@@ -9,65 +9,85 @@ describe Rearview::Configuration do
9
9
  let(:config) { Rearview::Configuration.new }
10
10
 
11
11
  context 'validation' do
12
- it { should validate_presence_of(:graphite_url) }
13
- it { should validate_presence_of(:pagerduty_url) }
14
12
  it { should validate_presence_of(:default_from) }
15
- it { should validate_presence_of(:sandbox_dir) }
16
13
  it { should validate_presence_of(:sandbox_exec) }
17
14
  it { should validate_presence_of(:sandbox_timeout) }
18
15
  it { should validate_presence_of(:default_url_options) }
19
16
  it { should validate_presence_of(:authentication) }
20
17
  it { should validate_numericality_of(:sandbox_timeout).is_greater_than(4) }
21
- it "should requre sandbox_dir to be a directory" do
22
- config.sandbox_dir="/__not_likely__"
23
- config.valid?
24
- expect(config.errors[:sandbox_dir]).to include("is not a directory")
25
- config.sandbox_dir = File.dirname(__FILE__)
26
- config.valid?
27
- expect(config.errors[:sandbox_dir]).to be_empty
18
+ context 'sandbox_dir' do
19
+ it { should validate_presence_of(:sandbox_dir) }
20
+ it "should be a directory" do
21
+ config.sandbox_dir="/__not_likely__"
22
+ config.valid?
23
+ expect(config.errors[:sandbox_dir]).to include("is not a directory")
24
+ config.sandbox_dir = File.dirname(__FILE__)
25
+ config.valid?
26
+ expect(config.errors[:sandbox_dir]).to be_empty
27
+ end
28
28
  end
29
- it "should require graphite_url to be a url" do
30
- response = stub(code: 200)
31
- HTTParty.stubs(:get).returns(response)
32
- config.graphite_url="ssh://fooblah"
33
- config.valid?
34
- expect(config.errors[:graphite_url]).to include("is not a valid URL")
35
- config.graphite_url="fooblah"
36
- config.valid?
37
- expect(config.errors[:graphite_url]).to include("is not a valid URL")
38
- config.graphite_url="http://fooblah.com"
39
- config.valid?
40
- expect(config.errors[:graphite_url]).to be_empty
29
+ context 'graphite_connection' do
30
+ it { should validate_presence_of(:graphite_connection) }
31
+ it "should require url option to be a valid url" do
32
+ config.graphite_connection = { url: 'ssh://fooblah' }
33
+ config.valid?
34
+ expect(config.errors[:graphite_connection]).to include("does not contain a valid URL")
35
+ end
36
+ it "should require url option to be present" do
37
+ config.graphite_connection = { url: nil }
38
+ config.valid?
39
+ expect(config.errors[:graphite_connection]).to include("graphite URL can't be blank")
40
+ end
41
+ pending do
42
+ it "should require url option to be reachable" do
43
+ config.graphite_connection = { url: 'http://graphite6.graphitehosting.com' }
44
+ config.valid?
45
+ mock_client = mock(:reachable? => false)
46
+ Graphite::Client.expects(:new).returns(mock_client)
47
+ expect(config.errors[:graphite_connection]).to include("graphite cannot be reached")
48
+ end
49
+ end
41
50
  end
42
- it "should require pagerduty_url to be a url" do
43
- config.pagerduty_url="ftp://fooblah"
44
- config.valid?
45
- expect(config.errors[:pagerduty_url]).to include("is not a valid URL")
46
- config.pagerduty_url="HTTPS://fooblah"
47
- config.valid?
48
- expect(config.errors[:pagerduty_url]).to be_empty
51
+ context 'pagerduty_url' do
52
+ it { should validate_presence_of(:pagerduty_url) }
53
+ it "should require pagerduty_url to be a url" do
54
+ config.pagerduty_url="ftp://fooblah"
55
+ config.valid?
56
+ expect(config.errors[:pagerduty_url]).to include("is not a valid URL")
57
+ config.pagerduty_url="HTTPS://fooblah"
58
+ config.valid?
59
+ expect(config.errors[:pagerduty_url]).to be_empty
60
+ end
49
61
  end
50
- it "should require graphite_url to be reachable" do
51
- response = stub(code: 400)
52
- HTTParty.expects(:get).with("http://graphite.mycompany-unreachable.com").returns(response)
53
- config.graphite_url="http://graphite.mycompany-unreachable.com"
54
- config.valid?
55
- expect(config.errors[:graphite_url]).to include("is not a reachable URL")
56
- response = stub(code: 200)
57
- HTTParty.expects(:get).with("http://graphite.mycompany.com").returns(response)
58
- config.graphite_url="http://graphite.mycompany.com"
59
- config.valid?
60
- expect(config.errors[:graphite_url]).to be_empty
62
+ context 'statsd_connection' do
63
+ it "should be present if stats are enabled" do
64
+ config.enable_stats = false
65
+ config.valid?
66
+ expect(config.errors[:statsd_connection]).to be_empty
67
+ config.enable_stats = true
68
+ config.valid?
69
+ expect(config.errors[:statsd_connection]).to include("can't be blank")
70
+ end
61
71
  end
62
- it "should require statsd_connection to be present if stats are enabled" do
63
- config.enable_stats = false
64
- config.valid?
65
- expect(config.errors[:statsd_connection]).to be_empty
66
- config.enable_stats = true
67
- config.valid?
68
- expect(config.errors[:statsd_connection]).to include("can't be blank")
72
+ context 'metrics_validator_schedule' do
73
+ it {
74
+ Rearview::Configuration.any_instance.stubs(:metrics_validator_enabled?).returns(true)
75
+ should_not allow_value('abc').for(:metrics_validator_schedule)
76
+ }
77
+ it {
78
+ Rearview::Configuration.any_instance.stubs(:metrics_validator_enabled?).returns(true)
79
+ should allow_value('0 * * * * ?').for(:metrics_validator_schedule)
80
+ }
81
+ it "should be present if metrics_validator is enabled" do
82
+ config.enable_metrics_validator = false
83
+ config.valid?
84
+ expect(config.errors[:metrics_validator_schedule]).to be_empty
85
+ config.enable_metrics_validator = true
86
+ config.metrics_validator_schedule = nil
87
+ config.valid?
88
+ expect(config.errors[:metrics_validator_schedule]).to include("can't be blank")
89
+ end
69
90
  end
70
- pending "should require sanbox_exec to be executable"
71
91
  end
72
92
 
73
93
  context '.new' do
@@ -80,6 +100,7 @@ describe Rearview::Configuration do
80
100
  expect(config.enable_monitor).to be_true
81
101
  expect(config.enable_stats).to be_false
82
102
  expect(config.verify).to be_false
103
+ expect(config.graphite_connection).to eq({})
83
104
  expect(config.authentication).to eq({strategy: :database})
84
105
  expect(config.default_url_options).to eq({:host=>"localhost", :port=>"3000"})
85
106
  end
@@ -0,0 +1,43 @@
1
+ require 'spec_helper'
2
+
3
+ describe Rearview::MetricsValidatorService do
4
+ before do
5
+ Celluloid.shutdown
6
+ Celluloid.boot
7
+ @service = Rearview::MetricsValidatorService.new
8
+ end
9
+ context '#startup' do
10
+ it "can only be started if stopped" do
11
+ Rearview::MetricsValidatorTask.stubs(:supervise).returns(mock)
12
+ @service.startup
13
+ expect { @service.startup }.to raise_error("service already started")
14
+ end
15
+ it "should create the supervised task" do
16
+ Rearview::MetricsValidatorTask.expects(:supervise)
17
+ @service.startup
18
+ end
19
+ end
20
+ context '#shutdown' do
21
+ it "can only be shutdown if started" do
22
+ expect { @service.shutdown }.to raise_error("service not started")
23
+ end
24
+ it "should terminate the supervised task" do
25
+ mock_task = mock
26
+ mock_task.expects(:terminate).once
27
+ Rearview::MetricsValidatorTask.stubs(:supervise).returns(stub(:actors => stub(:first => mock_task)))
28
+ @service.startup
29
+ @service.shutdown
30
+ end
31
+ end
32
+ context '#started?' do
33
+ it "should be true if the service is started" do
34
+ Rearview::MetricsValidatorTask.stubs(:supervise).returns(mock)
35
+ @service.startup
36
+ expect(@service.started?).to be_true
37
+ end
38
+ it "should be false if the service is not started" do
39
+ expect(@service.started?).to be_false
40
+ end
41
+ end
42
+ end
43
+
@@ -0,0 +1,84 @@
1
+ require 'spec_helper'
2
+
3
+ describe Rearview::MetricsValidator do
4
+
5
+ context '#metric_valid?' do
6
+ let(:metrics_validator) { Rearview::MetricsValidator.new({ attributes: [:metrics] })}
7
+ context 'true' do
8
+ it 'when the metric is commented out' do
9
+ expect(metrics_validator.metric_valid?('#stats_count.foo')).to be_true
10
+ end
11
+ it 'when the metric is complex' do
12
+ mock_client = mock(:metric_exists? => true)
13
+ metrics_validator.stubs(:client).returns(mock_client)
14
+ expect(metrics_validator.metric_valid?(%q[alias(summarize(stats_counts.watson.summary_tables.run,"5min"),"Daily Watson Tables so far")])).to be_true
15
+ end
16
+ end
17
+ context 'false' do
18
+ it 'when the metric is not parseable' do
19
+ expect(metrics_validator.metric_valid?(%q[alias@(stats.x.y,"processed")])).to be_false
20
+ end
21
+ it 'when the metric cannot be extracted' do
22
+ mock_parser = mock
23
+ mock_parser.stubs(:parse)
24
+ mock_parser.stubs(:error?).returns(false)
25
+ mock_parser.stubs(:tree).returns(mock(:comment? => false,:metric => nil))
26
+ metrics_validator.stubs(:target_parser).returns(mock_parser)
27
+ expect(metrics_validator.metric_valid?('stats.garbage.ignored')).to be_false
28
+ end
29
+ end
30
+ end
31
+ context '#validate_each' do
32
+ let(:job) { FactoryGirl.create(:job) }
33
+ let(:metrics_validator) { Rearview::MetricsValidator.new({ attributes: [:metrics] })}
34
+ context 'valid' do
35
+ it 'parses the metric' do
36
+ mock_client = mock(:metric_exists? => true)
37
+ metrics_validator.stubs(:client).returns(mock_client)
38
+ expect(metrics_validator.metric_valid?(%q[alias(summarize(stats_counts.watson.summary_tables.run,"5min"),"Daily Watson Tables so far")])).to be_true
39
+ end
40
+ it 'when all metrics exists' do
41
+ mock_client = mock(:metric_exists? => true)
42
+ metrics_validator.stubs(:client).returns(mock_client)
43
+ metrics_validator.validate_each(job,:metrics,job.metrics)
44
+ expect(job.errors[:metrics]).to be_empty
45
+ end
46
+ end
47
+ context 'invalid' do
48
+ it 'when one metric does not exist' do
49
+ job.metrics << "metric.fooey"
50
+ mock_client = mock
51
+ mock_client.expects(:metric_exists?).with("metric.fooey").returns(false)
52
+ mock_client.expects(:metric_exists?).with(job.metrics.first).returns(true)
53
+ metrics_validator.stubs(:client).returns(mock_client)
54
+ metrics_validator.validate_each(job,:metrics,job.metrics)
55
+ expect(job.errors[:metrics]).to include("contains an invalid metric: metric.fooey")
56
+ expect(job.errors[:metrics]).not_to include("contains an invalid metric: #{job.metrics.first}")
57
+ end
58
+ end
59
+ context 'cache' do
60
+ context 'enabled' do
61
+ let(:caching_validator) { Rearview::MetricsValidator.new({ attributes: [:metrics], cache: true })}
62
+ it 'caches responses for metrics' do
63
+ job.metrics << job.metrics.first
64
+ mock_client = mock
65
+ mock_client.expects(:metric_exists?).with(job.metrics.first).once.returns(true)
66
+ caching_validator.stubs(:client).returns(mock_client)
67
+ caching_validator.validate_each(job,:metrics,job.metrics)
68
+ expect(job.errors[:metrics]).to be_empty
69
+ end
70
+ end
71
+ context 'disabled' do
72
+ it 'does not cache responses for metrics' do
73
+ job.metrics << job.metrics.first
74
+ mock_client = mock
75
+ mock_client.expects(:metric_exists?).with(job.metrics.first).twice.returns(true)
76
+ metrics_validator.stubs(:client).returns(mock_client)
77
+ metrics_validator.validate_each(job,:metrics,job.metrics)
78
+ expect(job.errors[:metrics]).to be_empty
79
+ end
80
+ end
81
+ end
82
+ end
83
+
84
+ end
@@ -0,0 +1,62 @@
1
+ require 'spec_helper'
2
+
3
+ describe Rearview::MetricsValidatorTask do
4
+
5
+ before do
6
+ now = Time.now
7
+ Timecop.freeze(Time.local(now.year,now.mon,now.day))
8
+ end
9
+
10
+ context '.initialize' do
11
+ it 'schedules itself by default' do
12
+ Rearview::MetricsValidatorTask.any_instance.expects(:schedule)
13
+ Rearview::MetricsValidatorTask.new('0 * * * * ?')
14
+ end
15
+ end
16
+
17
+ context '#schedule' do
18
+ it 'sets the delay to 60s if the cron expression is \'0 * * * * ?\'' do
19
+ task = Rearview::MetricsValidatorTask.new('0 * * * * ?',false)
20
+ task.expects(:after).with(60.0).once
21
+ task.schedule
22
+ end
23
+ it 'sets the delay to 300s if the cron expression is \'0 5 * * * ?\'' do
24
+ task = Rearview::MetricsValidatorTask.new('0 5 * * * ?',false)
25
+ task.expects(:after).with(300.0).once
26
+ task.schedule
27
+ end
28
+ end
29
+
30
+ context '.run' do
31
+ let(:job1_invalid) {
32
+ job = FactoryGirl.create(:job)
33
+ job.errors.add(:metrics,"contains an invalid metric: #{job.metrics.first}")
34
+ job
35
+ }
36
+ let(:job2_valid) {
37
+ FactoryGirl.create(:job)
38
+ }
39
+ let(:job3_invalid) {
40
+ job = FactoryGirl.create(:job)
41
+ job.errors.add(:metrics,"contains an invalid metric: #{job.metrics.first}")
42
+ job
43
+ }
44
+ it 'still schedules if there is an exception' do
45
+ Rearview::Job.stubs(:schedulable).raises(StandardError,"oops")
46
+ task = Rearview::MetricsValidatorTask.new('0 5 * * * ?',false)
47
+ task.expects(:schedule).once
48
+ task.run
49
+ end
50
+ it 'mails alerts only for jobs with invalid metrics' do
51
+ Rearview::Job.stubs(:schedulable).returns(mock(:load => [job1_invalid,job2_valid,job3_invalid]))
52
+ Rearview::MetricsValidator.any_instance.stubs(:validate_each)
53
+ task = Rearview::MetricsValidatorTask.new('0 5 * * * ?',false)
54
+ task.expects(:schedule).once
55
+ task.expects(:alert_validation_failed).with(job1_invalid).once
56
+ task.expects(:alert_validation_failed).with(job2_valid).never
57
+ task.expects(:alert_validation_failed).with(job3_invalid).once
58
+ task.run
59
+ end
60
+ end
61
+
62
+ end
@@ -2,9 +2,9 @@ require 'spec_helper'
2
2
 
3
3
  describe Rearview::MonitorRunner, :travis=>true do
4
4
 
5
- artifact1 = Rearview::GraphiteParser.parse open("spec/data/monitor.dat").read
6
- artifact2 = Rearview::GraphiteParser.parse open("spec/data/test.dat").read
7
- artifact3 = Rearview::GraphiteParser.parse open("spec/data/large_set.dat").read
5
+ artifact1 = Graphite::RawParser.parse open("spec/data/monitor.dat").read
6
+ artifact2 = Graphite::RawParser.parse open("spec/data/test.dat").read
7
+ artifact3 = Graphite::RawParser.parse open("spec/data/large_set.dat").read
8
8
 
9
9
  context 'create_from_to_dates' do
10
10
  before do
@@ -39,6 +39,27 @@ describe Rearview::StatsTask do
39
39
  stats_task.statsd.expects(:batch)
40
40
  stats_task.run
41
41
  end
42
+ it "sends the correct stats" do
43
+ statsd = mock
44
+ batch = mock
45
+ statsd.expects(:batch_size=)
46
+ Rearview::Statsd.stubs(:new).returns(statsd)
47
+ stats_task = Rearview::StatsTask.new(120,false)
48
+ statsd.expects(:batch).yields(batch)
49
+ batch.expects(:gauge).with('vm.total_memory',any_parameters)
50
+ batch.expects(:gauge).with('vm.free_memory',any_parameters)
51
+ batch.expects(:gauge).with('vm.max_memory',any_parameters)
52
+ batch.expects(:gauge).with('vm.heap.committed',any_parameters)
53
+ batch.expects(:gauge).with('vm.heap.init',any_parameters)
54
+ batch.expects(:gauge).with('vm.heap.max',any_parameters)
55
+ batch.expects(:gauge).with('vm.heap.used',any_parameters)
56
+ batch.expects(:gauge).with('vm.non_heap.committed',any_parameters)
57
+ batch.expects(:gauge).with('vm.non_heap.init',any_parameters)
58
+ batch.expects(:gauge).with('vm.non_heap.max',any_parameters)
59
+ batch.expects(:gauge).with('vm.non_heap.used',any_parameters)
60
+ batch.expects(:gauge).with('monitor.total',any_parameters)
61
+ stats_task.run
62
+ end
42
63
  end
43
64
 
44
65
  end
@@ -0,0 +1,46 @@
1
+ require 'spec_helper'
2
+
3
+ describe Rearview::MetricsValidationMailer do
4
+
5
+ let(:job) {
6
+ job = FactoryGirl.build(:job)
7
+ job.errors.add(:metrics,"contains an invalid metric: stats.my_count")
8
+ job
9
+ }
10
+
11
+ let(:mail) {
12
+ Rearview::MetricsValidationMailer.validation_failed_email("foo@foo.com",job)
13
+ }
14
+
15
+ context 'subject' do
16
+ it 'contains the job name' do
17
+ expect(mail.subject).to match(/#{job.name}/)
18
+ end
19
+ it 'contains subject tag' do
20
+ expect(mail.subject).to match(/\[Rearview ALERT\]/)
21
+ end
22
+ end
23
+
24
+ context 'body' do
25
+ it 'has the alert message' do
26
+ expect(mail.body.encoded).to match(/ALERT: Job has \(1\) invalid metric/)
27
+ end
28
+ it 'has the monitor name' do
29
+ expect(mail.body.encoded).to match(/Monitor: #{job.name}/)
30
+ end
31
+ it 'has the monitor description' do
32
+ expect(mail.body.encoded).to match(/Description: #{job.description}/)
33
+ end
34
+ it 'has the alerted on date' do
35
+ expect(mail.body.encoded).to match(/Alerted On: .*/)
36
+ end
37
+ it 'has a direct link' do
38
+ expect(mail.body.encoded).to match(%r{Direct Link: http://localhost:3000/rearview/#dash/#{job.app_id}/expand/#{job.id}})
39
+ end
40
+ it 'has the invalid metric name' do
41
+ expect(mail.body.encoded).to match(/^stats.my_count/)
42
+ end
43
+ end
44
+
45
+ end
46
+
@@ -10,26 +10,39 @@ describe Rearview::Job do
10
10
  end
11
11
 
12
12
  describe 'validations' do
13
- it { should validate_presence_of(:cron_expr) }
14
13
  it { should validate_presence_of(:name) }
15
14
  it { should validate_presence_of(:metrics) }
16
15
  it { should validate_presence_of(:app_id) }
17
- it { should_not allow_value('abc').for(:cron_expr) }
18
- it { should allow_value('0 * * * * ?').for(:cron_expr) }
19
16
  it { should ensure_inclusion_of(:status).in_array(Rearview::Job::Status.values) }
17
+ describe 'cron_expr' do
18
+ before do
19
+ Rearview::MetricsValidator.any_instance.stubs(:validate_each)
20
+ Rearview::Job.any_instance.stubs(:deep_validation?).returns(true)
21
+ end
22
+ it { should validate_presence_of(:cron_expr) }
23
+ it { should_not allow_value('abc').for(:cron_expr) }
24
+ it { should allow_value('0 * * * * ?').for(:cron_expr) }
25
+ end
20
26
  describe 'alert_keys' do
21
- let(:job) { create(:job) }
27
+ before do
28
+ Rearview::MetricsValidator.any_instance.stubs(:validate_each)
29
+ end
30
+ let(:job) {
31
+ job = create(:job)
32
+ job.deep_validation = true
33
+ job
34
+ }
22
35
  it "should require a valid URI" do
23
36
  keys = ["htptptptpt://not_a_uri"]
24
37
  job.alert_keys=keys
25
38
  expect(job.valid?).to be_false
26
- expect(job.errors[:alert_keys]).to include("invalid URI")
39
+ expect(job.errors[:alert_keys]).to include("#{keys.first} is an invalid URI")
27
40
  end
28
41
  it "should require it to be one of the supported schemes" do
29
42
  keys = ["http://www.google.com"]
30
43
  job.alert_keys=keys
31
44
  expect(job.valid?).to be_false
32
- expect(job.errors[:alert_keys]).to include("unsupported scheme")
45
+ expect(job.errors[:alert_keys]).to include("#{keys.first} is not a supported alert type")
33
46
  end
34
47
  it "should validate the scheme against the corresponding alert" do
35
48
  keys = ["pagerduty:10101010101010101010101010101010","mailto:first.last@hungrymachine.com","campfire://first.last@hungrymachine.com?token=123&room=text"]
@@ -40,19 +53,19 @@ describe Rearview::Job do
40
53
  keys = ["pagerduty:abcdefg"]
41
54
  job.alert_keys=keys
42
55
  expect(job.valid?).to be_false
43
- expect(job.errors[:alert_keys]).to include("pagerduty URI is invalid")
56
+ expect(job.errors[:alert_keys]).to include("#{keys.first} is invalid for supported alert type")
44
57
  end
45
58
  it "should detect an invalid mail URI" do
46
59
  keys = ["mailto:abcdefg"]
47
60
  job.alert_keys=keys
48
61
  expect(job.valid?).to be_false
49
- expect(job.errors[:alert_keys]).to include("mailto URI is invalid")
62
+ expect(job.errors[:alert_keys]).to include("#{keys.first} is invalid for supported alert type")
50
63
  end
51
64
  it "should detect an invalid campfire URI" do
52
65
  keys = ["campfire://first.last@hungrymachine.com"]
53
66
  job.alert_keys=keys
54
67
  expect(job.valid?).to be_false
55
- expect(job.errors[:alert_keys]).to include("campfire URI is invalid")
68
+ expect(job.errors[:alert_keys]).to include("#{keys.first} is invalid for supported alert type")
56
69
  end
57
70
  end
58
71
  end
@@ -133,13 +146,73 @@ describe Rearview::Job do
133
146
  inactive_job.sync_monitor_service
134
147
  end
135
148
  end
149
+ describe '#set_defaults' do
150
+ context 'alert_keys' do
151
+ it "should set it to an empty array if not present" do
152
+ job = create(:job, { alert_keys: nil})
153
+ job.save!
154
+ expect(job.alert_keys).to eq([])
155
+ job = create(:job)
156
+ expect(job.alert_keys).not_to eq([])
157
+ end
158
+ end
159
+ end
160
+ describe '#delay' do
161
+ before do
162
+ now = Time.now
163
+ Timecop.freeze(Time.local(now.year,now.mon,now.day))
164
+ end
165
+ after do
166
+ Timecop.return
167
+ end
168
+ it "cron expression '0 * * * * ?' to be 60.0" do
169
+ job = build(:job,cron_expr: '0 * * * * ?')
170
+ expect(job.delay).to eq(60.0)
171
+ end
172
+ it "any cron expression other than '0 * * * * ?' to be calculated by Rearview::CronHelper" do
173
+ job = build(:job,cron_expr: '0 30 * * * ?')
174
+ Rearview::CronHelper.expects(:next_valid_time_after).with('0 30 * * * ?')
175
+ job.delay
176
+ end
177
+ end
178
+ describe '#translate_associated_event' do
179
+ let(:transition) { mock }
180
+ let(:job) { create(:job) }
181
+ it 'echos back any non-security error' do
182
+ transition.stubs(:event).returns(:foo)
183
+ expect(job.send(:translate_associated_event,transition)).to eq(:foo)
184
+ end
185
+ it 'translates it to an :error' do
186
+ transition.stubs(:event).returns(:security_error)
187
+ expect(job.send(:translate_associated_event,transition)).to eq(:error)
188
+ end
189
+ end
136
190
  describe '#report_transition' do
191
+ let(:transition) { mock }
192
+ let(:job) { create(:job) }
193
+ before do
194
+ @statsd = mock
195
+ Rearview.config.stubs(:stats_enabled?).returns(true)
196
+ Rearview::Statsd.stubs(:statsd).returns(@statsd)
197
+ end
137
198
  context 'success event' do
138
199
  it "should increment monitor.success" do
200
+ transition.stubs(:event).returns(:success)
201
+ @statsd.expects(:increment).with("monitor.success")
202
+ job.send(:report_transition,transition)
139
203
  end
140
204
  end
141
205
  context 'failure event' do
142
206
  it "should increment monitor.failure" do
207
+ transition.stubs(:event).returns(:error)
208
+ @statsd.expects(:increment).with("monitor.failure")
209
+ job.send(:report_transition,transition)
210
+ end
211
+ end
212
+ context 'exception' do
213
+ it "should not raise" do
214
+ transition.stubs(:event).raises
215
+ expect{job.send(:report_transition,transition)}.not_to raise_error
143
216
  end
144
217
  end
145
218
  end