fiveruns-dash-ruby 0.7.0
Sign up to get free protection for your applications and to get access to all the features.
- data/README.rdoc +68 -0
- data/Rakefile +39 -0
- data/lib/fiveruns/dash.rb +144 -0
- data/lib/fiveruns/dash/configuration.rb +116 -0
- data/lib/fiveruns/dash/exception_recorder.rb +135 -0
- data/lib/fiveruns/dash/host.rb +173 -0
- data/lib/fiveruns/dash/instrument.rb +128 -0
- data/lib/fiveruns/dash/metric.rb +379 -0
- data/lib/fiveruns/dash/recipe.rb +63 -0
- data/lib/fiveruns/dash/reporter.rb +208 -0
- data/lib/fiveruns/dash/scm.rb +126 -0
- data/lib/fiveruns/dash/session.rb +81 -0
- data/lib/fiveruns/dash/store/file.rb +24 -0
- data/lib/fiveruns/dash/store/http.rb +198 -0
- data/lib/fiveruns/dash/threads.rb +24 -0
- data/lib/fiveruns/dash/trace.rb +65 -0
- data/lib/fiveruns/dash/typable.rb +29 -0
- data/lib/fiveruns/dash/update.rb +215 -0
- data/lib/fiveruns/dash/version.rb +86 -0
- data/recipes/jruby.rb +107 -0
- data/recipes/ruby.rb +34 -0
- data/test/collector_communication_test.rb +260 -0
- data/test/configuration_test.rb +97 -0
- data/test/exception_recorder_test.rb +112 -0
- data/test/file_store_test.rb +56 -0
- data/test/fixtures/http_store_test/response.json +6 -0
- data/test/http_store_test.rb +210 -0
- data/test/metric_test.rb +204 -0
- data/test/recipe_test.rb +146 -0
- data/test/reliability_test.rb +60 -0
- data/test/reporter_test.rb +46 -0
- data/test/scm_test.rb +70 -0
- data/test/session_test.rb +49 -0
- data/test/test_helper.rb +96 -0
- data/test/tracing_test.rb +68 -0
- data/test/update_test.rb +42 -0
- data/version.yml +3 -0
- metadata +112 -0
@@ -0,0 +1,97 @@
|
|
1
|
+
require File.dirname(__FILE__) << "/test_helper"
|
2
|
+
|
3
|
+
class ConfigurationTest < Test::Unit::TestCase
|
4
|
+
|
5
|
+
context "Configuration" do
|
6
|
+
|
7
|
+
setup do
|
8
|
+
no_recipe_loading!
|
9
|
+
mock_streams!
|
10
|
+
end
|
11
|
+
|
12
|
+
teardown do
|
13
|
+
restore_streams!
|
14
|
+
Fiveruns::Dash.class_eval { @configuration = nil }
|
15
|
+
end
|
16
|
+
|
17
|
+
should "update options passed by Fiveruns::Dash.configure convenience method" do
|
18
|
+
assert_nil config.options[:app]
|
19
|
+
token = 'foo-bar'
|
20
|
+
Fiveruns::Dash.configure :app => token
|
21
|
+
assert_equal token, config.options[:app]
|
22
|
+
end
|
23
|
+
|
24
|
+
context "metric definitions" do
|
25
|
+
setup do
|
26
|
+
@configuration = Configuration.new do |config|
|
27
|
+
metric_types.each do |type|
|
28
|
+
config.__send__(type, "Metric: #{type}") do
|
29
|
+
# Empty block for metric types that require it
|
30
|
+
end
|
31
|
+
end
|
32
|
+
end
|
33
|
+
end
|
34
|
+
should "assign all metrics" do
|
35
|
+
assert_equal 3, @configuration.metrics.size
|
36
|
+
types = @configuration.metrics.map { |metric| metric.class.metric_type }
|
37
|
+
assert_equal metric_types.map(&:to_s).sort, types.map(&:to_s).sort
|
38
|
+
end
|
39
|
+
should "not allow invalid types" do
|
40
|
+
assert_raises NoMethodError do
|
41
|
+
@configuration.__send__(:bad_type, 'My Horrible Metric')
|
42
|
+
end
|
43
|
+
end
|
44
|
+
end
|
45
|
+
|
46
|
+
context "setting by version" do
|
47
|
+
setup do
|
48
|
+
@version = '0.1.6'
|
49
|
+
@config = Configuration.new do |config|
|
50
|
+
config.for_version @version, '0.1.6' do
|
51
|
+
config.counter :foo do
|
52
|
+
1
|
53
|
+
end
|
54
|
+
end
|
55
|
+
config.for_version @version, ['=', '0.1.6'] do
|
56
|
+
config.counter :bar do
|
57
|
+
1
|
58
|
+
end
|
59
|
+
end
|
60
|
+
config.for_version @version, ['==', '0.1.5'] do
|
61
|
+
config.counter :spam do
|
62
|
+
1
|
63
|
+
end
|
64
|
+
end
|
65
|
+
config.for_version @version, ['>', '0.1.6'] do
|
66
|
+
config.counter :baz do
|
67
|
+
1
|
68
|
+
end
|
69
|
+
end
|
70
|
+
config.for_version nil, ['>', '0.1.6'] do
|
71
|
+
config.counter :quux do
|
72
|
+
1
|
73
|
+
end
|
74
|
+
end
|
75
|
+
end
|
76
|
+
end
|
77
|
+
should "execute if correct version" do
|
78
|
+
assert_equal 2, @config.metrics.size
|
79
|
+
assert_equal %w(bar foo), @config.metrics.map(&:name).map(&:to_s).sort
|
80
|
+
end
|
81
|
+
end
|
82
|
+
|
83
|
+
end
|
84
|
+
|
85
|
+
#######
|
86
|
+
private
|
87
|
+
#######
|
88
|
+
|
89
|
+
def metric_types
|
90
|
+
[:counter, :percentage, :absolute]
|
91
|
+
end
|
92
|
+
|
93
|
+
def config
|
94
|
+
Fiveruns::Dash.configuration
|
95
|
+
end
|
96
|
+
|
97
|
+
end
|
@@ -0,0 +1,112 @@
|
|
1
|
+
require File.dirname(__FILE__) << "/test_helper"
|
2
|
+
|
3
|
+
class ExceptionRecorderTest < Test::Unit::TestCase
|
4
|
+
|
5
|
+
attr_reader :payload
|
6
|
+
attr_reader :recorder, :exceptions
|
7
|
+
|
8
|
+
context "ExceptionRecorder" do
|
9
|
+
|
10
|
+
setup do
|
11
|
+
@recorder = Fiveruns::Dash::ExceptionRecorder.new(flexmock(:session))
|
12
|
+
flexmock(ExceptionRecorder).should_receive(:replacements).and_return({
|
13
|
+
:foo => /^foo\b/
|
14
|
+
})
|
15
|
+
end
|
16
|
+
|
17
|
+
context "when recording an exception" do
|
18
|
+
setup do
|
19
|
+
recorder.record(build("Message", "foo/bar/baz"))
|
20
|
+
end
|
21
|
+
should "record an exception" do
|
22
|
+
assert_equal 1, recorder.data.size
|
23
|
+
end
|
24
|
+
should "normalize a backtrace" do
|
25
|
+
assert(recorder.data.first[:backtrace] =~ /\[FOO\]/)
|
26
|
+
end
|
27
|
+
end
|
28
|
+
|
29
|
+
context "when recording an exception with a sample" do
|
30
|
+
setup do
|
31
|
+
recorder.record(build("Message", "foo/bar/baz"), {:key => :value})
|
32
|
+
end
|
33
|
+
should "record an exception" do
|
34
|
+
assert_equal 1, recorder.data.size
|
35
|
+
end
|
36
|
+
should "normalize a backtrace" do
|
37
|
+
assert(recorder.data.first[:backtrace] =~ /\[FOO\]/)
|
38
|
+
end
|
39
|
+
should "not serialize sample" do
|
40
|
+
assert_equal({'key' => 'value'}, recorder.data.first[:sample])
|
41
|
+
end
|
42
|
+
end
|
43
|
+
|
44
|
+
context "when recording exceptions with the same message and backtrace" do
|
45
|
+
setup do
|
46
|
+
recorder.record(build, :key1=>:value1)
|
47
|
+
recorder.record(build, :key2=>:value2)
|
48
|
+
end
|
49
|
+
should "collapse" do
|
50
|
+
assert_equal 1, recorder.data.size
|
51
|
+
end
|
52
|
+
should "count them together" do
|
53
|
+
assert_equal [2], recorder.data.map { |exc| exc[:total] }
|
54
|
+
end
|
55
|
+
should "store only the first sample" do
|
56
|
+
assert_equal({'key1' => 'value1'}, recorder.data.first[:sample])
|
57
|
+
end
|
58
|
+
end
|
59
|
+
|
60
|
+
context "when recording exceptions with different messages" do
|
61
|
+
setup do
|
62
|
+
recorder.record(build("Message1", "Line 1"))
|
63
|
+
recorder.record(build("Message2", "Line 1"))
|
64
|
+
end
|
65
|
+
should "collapse" do
|
66
|
+
assert_equal 1, recorder.data.size
|
67
|
+
end
|
68
|
+
should "count them together" do
|
69
|
+
assert_equal [2], recorder.data.map { |exc| exc[:total] }
|
70
|
+
end
|
71
|
+
end
|
72
|
+
|
73
|
+
context "when recording exceptions with different backtraces" do
|
74
|
+
setup do
|
75
|
+
recorder.record(build("Message", "Line 1"))
|
76
|
+
recorder.record(build("Message", "Line 2"))
|
77
|
+
end
|
78
|
+
should "not collapse" do
|
79
|
+
assert_equal 2, recorder.data.size
|
80
|
+
end
|
81
|
+
should "count them separately" do
|
82
|
+
assert_equal [1, 1], recorder.data.map { |exc| exc[:total] }
|
83
|
+
end
|
84
|
+
end
|
85
|
+
|
86
|
+
context "when retrieving the data" do
|
87
|
+
should "call reset when returning data hash" do
|
88
|
+
flexmock(recorder).should_receive(:reset).once
|
89
|
+
recorder.data
|
90
|
+
end
|
91
|
+
|
92
|
+
should "empty the exception list on reset" do
|
93
|
+
recorder.send(:exceptions) << "Item"
|
94
|
+
assert_equal( 1, recorder.send(:exceptions).size )
|
95
|
+
recorder.send(:reset)
|
96
|
+
assert_equal( 0, recorder.send(:exceptions).size )
|
97
|
+
end
|
98
|
+
end
|
99
|
+
|
100
|
+
end
|
101
|
+
|
102
|
+
#######
|
103
|
+
private
|
104
|
+
#######
|
105
|
+
|
106
|
+
def build(message = 'This is a message', line = 'backtrace line')
|
107
|
+
flexmock(:exception) do |mock|
|
108
|
+
mock.should_receive(:backtrace).and_return([line])
|
109
|
+
mock.should_receive(:message).and_return(message)
|
110
|
+
end
|
111
|
+
end
|
112
|
+
end
|
@@ -0,0 +1,56 @@
|
|
1
|
+
require File.dirname(__FILE__) << "/test_helper"
|
2
|
+
|
3
|
+
class FileStoreTest < Test::Unit::TestCase
|
4
|
+
|
5
|
+
context "FileStore" do
|
6
|
+
|
7
|
+
setup do
|
8
|
+
mock_storage!
|
9
|
+
directories.each do |dir|
|
10
|
+
FileUtils.mkdir_p dir
|
11
|
+
end
|
12
|
+
@update = @klass.new
|
13
|
+
@update.store_file(*uris)
|
14
|
+
end
|
15
|
+
|
16
|
+
teardown do
|
17
|
+
FileUtils.rm_rf(File.dirname(__FILE__) << "/tmp")
|
18
|
+
end
|
19
|
+
|
20
|
+
should "write to all filenames" do
|
21
|
+
assert_equal 2, files.size
|
22
|
+
end
|
23
|
+
|
24
|
+
should "name all filenames the same" do
|
25
|
+
assert_equal 1, files.map { |f| File.basename(f) }.uniq.size
|
26
|
+
end
|
27
|
+
|
28
|
+
end
|
29
|
+
|
30
|
+
#######
|
31
|
+
private
|
32
|
+
#######
|
33
|
+
|
34
|
+
def files
|
35
|
+
Dir[File.dirname(__FILE__) << "/tmp/**/*.json"]
|
36
|
+
end
|
37
|
+
|
38
|
+
def mock_storage!
|
39
|
+
@klass = Class.new { include Store::File }
|
40
|
+
flexmock(@klass).new_instances do |mock|
|
41
|
+
mock.should_receive(:payload).and_return(:foo => 1, :bar => 2)
|
42
|
+
mock.should_receive(:guid).and_return('GUID')
|
43
|
+
end
|
44
|
+
end
|
45
|
+
|
46
|
+
def directories
|
47
|
+
%w(foo bar).map do |name|
|
48
|
+
File.expand_path(File.join(File.dirname(__FILE__), 'tmp', 'file_store', name))
|
49
|
+
end
|
50
|
+
end
|
51
|
+
|
52
|
+
def uris
|
53
|
+
directories.map { |path| URI.parse("file://#{path}") }
|
54
|
+
end
|
55
|
+
|
56
|
+
end
|
@@ -0,0 +1,210 @@
|
|
1
|
+
require File.dirname(__FILE__) << "/test_helper"
|
2
|
+
|
3
|
+
class HTTPStoreTest < Test::Unit::TestCase
|
4
|
+
|
5
|
+
attr_reader :payload
|
6
|
+
|
7
|
+
context "HTTPStore" do
|
8
|
+
|
9
|
+
setup do
|
10
|
+
Thread.current[:resolved_hostnames] = nil
|
11
|
+
@urls = %w(http://metrics.foo.com http://metrics02.bar.com http://metrics03.bar.com)
|
12
|
+
@klass = Class.new { include Store::HTTP }
|
13
|
+
@params = {:this_is_a_param => 'value'}
|
14
|
+
@metric = flexmock(:metric) do |mock|
|
15
|
+
mock.should_receive(:key).and_return(:name => 'MetricTest.time_me', :recipe_name => nil, :recipe_url => nil)
|
16
|
+
mock.should_receive(:info_id=)
|
17
|
+
end
|
18
|
+
@configuration = flexmock(:config) do |mock|
|
19
|
+
mock.should_receive(:options).and_return(:app => '123')
|
20
|
+
mock.should_receive(:metrics).and_return([@metric])
|
21
|
+
end
|
22
|
+
flexmock(::Fiveruns::Dash).should_receive(:configuration).and_return(@configuration)
|
23
|
+
flexmock(@klass).new_instances do |mock|
|
24
|
+
mock.should_receive(:payload).and_return { payload }
|
25
|
+
mock.should_receive(:params).and_return(@params)
|
26
|
+
end
|
27
|
+
@update = @klass.new
|
28
|
+
no_recipe_loading!
|
29
|
+
# mock_streams!
|
30
|
+
end
|
31
|
+
|
32
|
+
teardown do
|
33
|
+
FakeWeb.clean_registry
|
34
|
+
# restore_streams!
|
35
|
+
end
|
36
|
+
|
37
|
+
#TODO
|
38
|
+
context "collector hostnames" do
|
39
|
+
should "should be resolved on the first access" do
|
40
|
+
flexmock(IPSocket).should_receive(:getaddress).times(1).returns("1.1.1.1")
|
41
|
+
assert_equal @update.resolved_hostnames.keys.size, 0
|
42
|
+
new_uri = @update.resolved_hostname(uris.first.host)
|
43
|
+
assert_equal new_uri, "1.1.1.1"
|
44
|
+
assert_equal @update.resolved_hostnames.keys.size, 1
|
45
|
+
assert @update.resolved_hostnames[uris.first.host].next_update > (Time.now + 23.hours)
|
46
|
+
assert_equal @update.resolved_hostname(uris.first.host), "1.1.1.1"
|
47
|
+
junk = @update.resolved_hostname(uris.first.host)
|
48
|
+
|
49
|
+
end
|
50
|
+
|
51
|
+
should "re-cache address if time has expired" do
|
52
|
+
flexmock(@update).should_receive(:rand).returns(1)
|
53
|
+
flexmock(IPSocket).should_receive(:getaddress).returns("1.1.1.1", "2.2.2.2")
|
54
|
+
assert_equal @update.resolved_hostnames.keys.size, 0
|
55
|
+
new_uri = @update.resolved_hostname(uris.first.host)
|
56
|
+
assert_equal new_uri, "1.1.1.1"
|
57
|
+
@update.resolved_hostnames[uris.first.host].next_update = 10.hours.ago
|
58
|
+
first_expire = @update.resolved_hostnames[uris.first.host].next_update
|
59
|
+
|
60
|
+
new_uri = @update.resolved_hostname(uris.first.host)
|
61
|
+
second_expire = @update.resolved_hostnames[uris.first.host].next_update
|
62
|
+
assert_equal new_uri, "2.2.2.2"
|
63
|
+
assert second_expire < (Time.now + 25.hours)
|
64
|
+
assert second_expire > (Time.now + 23.hours)
|
65
|
+
end
|
66
|
+
end
|
67
|
+
|
68
|
+
context "with info payload" do
|
69
|
+
setup do
|
70
|
+
@payload = InfoPayload.new({:pid => 987}, Time.now.utc)
|
71
|
+
@update#TODO
|
72
|
+
flexmock(@update).should_receive(:resolved_hostname).and_return {|c| c }
|
73
|
+
# restore_streams!
|
74
|
+
end
|
75
|
+
teardown do
|
76
|
+
# mock_streams!
|
77
|
+
end
|
78
|
+
context "on connection error" do
|
79
|
+
setup do
|
80
|
+
FakeWeb.register_uri full_urls(:processes).first, :string => 'FAIL!', :exception => Net::HTTPError
|
81
|
+
full_urls(:processes)[1..-1].each do |url|
|
82
|
+
FakeWeb.register_uri url, :status => 201, :string => File.read(File.dirname(__FILE__) << "/fixtures/http_store_test/response.json")
|
83
|
+
end
|
84
|
+
end
|
85
|
+
should "fallback to working URL" do
|
86
|
+
returning @update.store_http(*uris) do |pass_uri|
|
87
|
+
assert_equal uris[1], pass_uri
|
88
|
+
end
|
89
|
+
end
|
90
|
+
end
|
91
|
+
context "on non-201 response" do
|
92
|
+
setup do
|
93
|
+
[500, 403, 200].zip(full_urls(:processes)).each do |status, url|
|
94
|
+
FakeWeb.register_uri url, :string => 'Not what we want', :status => status
|
95
|
+
end
|
96
|
+
end
|
97
|
+
should "not succeed" do
|
98
|
+
assert !@update.store_http(*uris)
|
99
|
+
end
|
100
|
+
end
|
101
|
+
end
|
102
|
+
|
103
|
+
context "with data payload" do
|
104
|
+
|
105
|
+
setup do
|
106
|
+
@payload = DataPayload.new([{:metric_info_id => 123, :name => 'bar'}])
|
107
|
+
flexmock(@update).should_receive(:resolved_hostname).and_return {|c| c}
|
108
|
+
|
109
|
+
end
|
110
|
+
|
111
|
+
context "fallback URLs" do
|
112
|
+
context "on connection error" do
|
113
|
+
setup do
|
114
|
+
FakeWeb.register_uri full_urls(:metrics).first, :string => 'FAIL!', :exception => Net::HTTPError
|
115
|
+
full_urls(:metrics)[1..-1].each do |url|
|
116
|
+
FakeWeb.register_uri url, :string => '{"message" : "OK!"}', :status => 201
|
117
|
+
end
|
118
|
+
end
|
119
|
+
should "fallback to working URL" do
|
120
|
+
returning @update.store_http(*uris) do |pass_uri|
|
121
|
+
assert_equal uris[1], pass_uri
|
122
|
+
end
|
123
|
+
end
|
124
|
+
end
|
125
|
+
context "on non-201 response" do
|
126
|
+
setup do
|
127
|
+
[500, 403, 200].zip(full_urls(:metrics)).each do |status, url|
|
128
|
+
FakeWeb.register_uri url, :string => 'Not what we want', :status => status
|
129
|
+
end
|
130
|
+
end
|
131
|
+
should "not succeed" do
|
132
|
+
assert !@update.store_http(*uris)
|
133
|
+
end
|
134
|
+
end
|
135
|
+
end
|
136
|
+
|
137
|
+
end
|
138
|
+
|
139
|
+
context "with exceptions payload" do
|
140
|
+
|
141
|
+
setup do
|
142
|
+
@payload = ExceptionsPayload.new([
|
143
|
+
{
|
144
|
+
:name => 'FooError',
|
145
|
+
:message => 'Fake Foo Error',
|
146
|
+
:backtrace => '--- This is not real',
|
147
|
+
:total => 3
|
148
|
+
},
|
149
|
+
{
|
150
|
+
:name => 'BarError',
|
151
|
+
:message => 'Fake Bar Error',
|
152
|
+
:backtrace => '--- This is not real',
|
153
|
+
:total => 10
|
154
|
+
}
|
155
|
+
])
|
156
|
+
flexmock(@update).should_receive(:resolved_hostname).and_return {|c| c}
|
157
|
+
|
158
|
+
end
|
159
|
+
|
160
|
+
context "fallback URLs" do
|
161
|
+
context "on connection error" do
|
162
|
+
setup do
|
163
|
+
FakeWeb.register_uri full_urls(:exceptions).first, :string => 'FAIL!', :exception => Net::HTTPError
|
164
|
+
full_urls(:exceptions)[1..-1].each do |url|
|
165
|
+
FakeWeb.register_uri url, :string => '{"message" : "OK!"}', :status => 201
|
166
|
+
end
|
167
|
+
end
|
168
|
+
should "fallback to working URL" do
|
169
|
+
returning @update.store_http(*uris) do |pass_uri|
|
170
|
+
assert_equal uris[1], pass_uri
|
171
|
+
end
|
172
|
+
end
|
173
|
+
end
|
174
|
+
context "on non-201 response" do
|
175
|
+
setup do
|
176
|
+
[500, 403, 200].zip(full_urls(:exceptions)).each do |status, url|
|
177
|
+
FakeWeb.register_uri url, :string => 'Not what we want', :status => status
|
178
|
+
end
|
179
|
+
end
|
180
|
+
should "not succeed" do
|
181
|
+
assert !@update.store_http(*uris)
|
182
|
+
end
|
183
|
+
end
|
184
|
+
end
|
185
|
+
|
186
|
+
end
|
187
|
+
|
188
|
+
end
|
189
|
+
|
190
|
+
#######
|
191
|
+
private
|
192
|
+
#######
|
193
|
+
|
194
|
+
def full_urls(service)
|
195
|
+
full_uris(service).map(&:to_s)
|
196
|
+
end
|
197
|
+
|
198
|
+
def full_uris(service)
|
199
|
+
@urls.map do |url|
|
200
|
+
uri = URI.parse(url)
|
201
|
+
uri.path = "/apps/123/#{service}.json"
|
202
|
+
uri
|
203
|
+
end
|
204
|
+
end
|
205
|
+
|
206
|
+
def uris
|
207
|
+
@urls.map { |url| URI.parse(url) }
|
208
|
+
end
|
209
|
+
|
210
|
+
end
|