fiveruns-dash-ruby 0.7.0
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.
- 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
|