right_agent 0.5.1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (147) hide show
  1. data/LICENSE +20 -0
  2. data/README.rdoc +78 -0
  3. data/Rakefile +86 -0
  4. data/lib/right_agent.rb +66 -0
  5. data/lib/right_agent/actor.rb +163 -0
  6. data/lib/right_agent/actor_registry.rb +76 -0
  7. data/lib/right_agent/actors/agent_manager.rb +189 -0
  8. data/lib/right_agent/agent.rb +735 -0
  9. data/lib/right_agent/agent_config.rb +403 -0
  10. data/lib/right_agent/agent_identity.rb +209 -0
  11. data/lib/right_agent/agent_tags_manager.rb +213 -0
  12. data/lib/right_agent/audit_formatter.rb +107 -0
  13. data/lib/right_agent/broker_client.rb +683 -0
  14. data/lib/right_agent/command.rb +30 -0
  15. data/lib/right_agent/command/agent_manager_commands.rb +134 -0
  16. data/lib/right_agent/command/command_client.rb +136 -0
  17. data/lib/right_agent/command/command_constants.rb +42 -0
  18. data/lib/right_agent/command/command_io.rb +128 -0
  19. data/lib/right_agent/command/command_parser.rb +87 -0
  20. data/lib/right_agent/command/command_runner.rb +105 -0
  21. data/lib/right_agent/command/command_serializer.rb +63 -0
  22. data/lib/right_agent/console.rb +65 -0
  23. data/lib/right_agent/core_payload_types.rb +42 -0
  24. data/lib/right_agent/core_payload_types/cookbook.rb +61 -0
  25. data/lib/right_agent/core_payload_types/cookbook_position.rb +46 -0
  26. data/lib/right_agent/core_payload_types/cookbook_repository.rb +116 -0
  27. data/lib/right_agent/core_payload_types/cookbook_sequence.rb +70 -0
  28. data/lib/right_agent/core_payload_types/dev_repositories.rb +90 -0
  29. data/lib/right_agent/core_payload_types/event_categories.rb +38 -0
  30. data/lib/right_agent/core_payload_types/executable_bundle.rb +138 -0
  31. data/lib/right_agent/core_payload_types/login_policy.rb +72 -0
  32. data/lib/right_agent/core_payload_types/login_user.rb +62 -0
  33. data/lib/right_agent/core_payload_types/planned_volume.rb +94 -0
  34. data/lib/right_agent/core_payload_types/recipe_instantiation.rb +60 -0
  35. data/lib/right_agent/core_payload_types/repositories_bundle.rb +50 -0
  36. data/lib/right_agent/core_payload_types/right_script_attachment.rb +95 -0
  37. data/lib/right_agent/core_payload_types/right_script_instantiation.rb +73 -0
  38. data/lib/right_agent/core_payload_types/secure_document.rb +66 -0
  39. data/lib/right_agent/core_payload_types/secure_document_location.rb +63 -0
  40. data/lib/right_agent/core_payload_types/software_repository_instantiation.rb +61 -0
  41. data/lib/right_agent/daemonize.rb +35 -0
  42. data/lib/right_agent/dispatcher.rb +348 -0
  43. data/lib/right_agent/enrollment_result.rb +217 -0
  44. data/lib/right_agent/exceptions.rb +30 -0
  45. data/lib/right_agent/ha_broker_client.rb +1278 -0
  46. data/lib/right_agent/idempotent_request.rb +140 -0
  47. data/lib/right_agent/log.rb +418 -0
  48. data/lib/right_agent/monkey_patches.rb +29 -0
  49. data/lib/right_agent/monkey_patches/amqp_patch.rb +274 -0
  50. data/lib/right_agent/monkey_patches/ruby_patch.rb +49 -0
  51. data/lib/right_agent/monkey_patches/ruby_patch/array_patch.rb +29 -0
  52. data/lib/right_agent/monkey_patches/ruby_patch/darwin_patch.rb +24 -0
  53. data/lib/right_agent/monkey_patches/ruby_patch/linux_patch.rb +24 -0
  54. data/lib/right_agent/monkey_patches/ruby_patch/linux_patch/file_patch.rb +30 -0
  55. data/lib/right_agent/monkey_patches/ruby_patch/object_patch.rb +49 -0
  56. data/lib/right_agent/monkey_patches/ruby_patch/singleton_patch.rb +46 -0
  57. data/lib/right_agent/monkey_patches/ruby_patch/string_patch.rb +107 -0
  58. data/lib/right_agent/monkey_patches/ruby_patch/windows_patch.rb +32 -0
  59. data/lib/right_agent/monkey_patches/ruby_patch/windows_patch/file_patch.rb +90 -0
  60. data/lib/right_agent/monkey_patches/ruby_patch/windows_patch/process_patch.rb +63 -0
  61. data/lib/right_agent/monkey_patches/ruby_patch/windows_patch/stdio_patch.rb +27 -0
  62. data/lib/right_agent/monkey_patches/ruby_patch/windows_patch/time_patch.rb +55 -0
  63. data/lib/right_agent/monkey_patches/ruby_patch/windows_patch/win32ole_patch.rb +34 -0
  64. data/lib/right_agent/multiplexer.rb +91 -0
  65. data/lib/right_agent/operation_result.rb +270 -0
  66. data/lib/right_agent/packets.rb +637 -0
  67. data/lib/right_agent/payload_formatter.rb +104 -0
  68. data/lib/right_agent/pid_file.rb +159 -0
  69. data/lib/right_agent/platform.rb +319 -0
  70. data/lib/right_agent/platform/darwin.rb +227 -0
  71. data/lib/right_agent/platform/linux.rb +268 -0
  72. data/lib/right_agent/platform/windows.rb +1204 -0
  73. data/lib/right_agent/scripts/agent_controller.rb +522 -0
  74. data/lib/right_agent/scripts/agent_deployer.rb +379 -0
  75. data/lib/right_agent/scripts/common_parser.rb +153 -0
  76. data/lib/right_agent/scripts/log_level_manager.rb +193 -0
  77. data/lib/right_agent/scripts/stats_manager.rb +256 -0
  78. data/lib/right_agent/scripts/usage.rb +58 -0
  79. data/lib/right_agent/secure_identity.rb +92 -0
  80. data/lib/right_agent/security.rb +32 -0
  81. data/lib/right_agent/security/cached_certificate_store_proxy.rb +63 -0
  82. data/lib/right_agent/security/certificate.rb +102 -0
  83. data/lib/right_agent/security/certificate_cache.rb +89 -0
  84. data/lib/right_agent/security/distinguished_name.rb +56 -0
  85. data/lib/right_agent/security/encrypted_document.rb +84 -0
  86. data/lib/right_agent/security/rsa_key_pair.rb +76 -0
  87. data/lib/right_agent/security/signature.rb +86 -0
  88. data/lib/right_agent/security/static_certificate_store.rb +69 -0
  89. data/lib/right_agent/sender.rb +937 -0
  90. data/lib/right_agent/serialize.rb +29 -0
  91. data/lib/right_agent/serialize/message_pack.rb +102 -0
  92. data/lib/right_agent/serialize/secure_serializer.rb +131 -0
  93. data/lib/right_agent/serialize/secure_serializer_initializer.rb +47 -0
  94. data/lib/right_agent/serialize/serializable.rb +135 -0
  95. data/lib/right_agent/serialize/serializer.rb +149 -0
  96. data/lib/right_agent/stats_helper.rb +731 -0
  97. data/lib/right_agent/subprocess.rb +38 -0
  98. data/lib/right_agent/tracer.rb +124 -0
  99. data/right_agent.gemspec +60 -0
  100. data/spec/actor_registry_spec.rb +81 -0
  101. data/spec/actor_spec.rb +99 -0
  102. data/spec/agent_config_spec.rb +226 -0
  103. data/spec/agent_identity_spec.rb +75 -0
  104. data/spec/agent_spec.rb +571 -0
  105. data/spec/broker_client_spec.rb +961 -0
  106. data/spec/command/agent_manager_commands_spec.rb +51 -0
  107. data/spec/command/command_io_spec.rb +93 -0
  108. data/spec/command/command_parser_spec.rb +79 -0
  109. data/spec/command/command_runner_spec.rb +72 -0
  110. data/spec/command/command_serializer_spec.rb +51 -0
  111. data/spec/core_payload_types/dev_repositories_spec.rb +64 -0
  112. data/spec/core_payload_types/executable_bundle_spec.rb +59 -0
  113. data/spec/core_payload_types/login_user_spec.rb +98 -0
  114. data/spec/core_payload_types/right_script_attachment_spec.rb +65 -0
  115. data/spec/core_payload_types/spec_helper.rb +23 -0
  116. data/spec/dispatcher_spec.rb +372 -0
  117. data/spec/enrollment_result_spec.rb +53 -0
  118. data/spec/ha_broker_client_spec.rb +1673 -0
  119. data/spec/idempotent_request_spec.rb +136 -0
  120. data/spec/log_spec.rb +177 -0
  121. data/spec/monkey_patches/amqp_patch_spec.rb +100 -0
  122. data/spec/monkey_patches/eventmachine_spec.rb +62 -0
  123. data/spec/monkey_patches/string_patch_spec.rb +99 -0
  124. data/spec/multiplexer_spec.rb +48 -0
  125. data/spec/operation_result_spec.rb +171 -0
  126. data/spec/packets_spec.rb +418 -0
  127. data/spec/platform/platform_spec.rb +60 -0
  128. data/spec/results_mock.rb +45 -0
  129. data/spec/secure_identity_spec.rb +50 -0
  130. data/spec/security/cached_certificate_store_proxy_spec.rb +56 -0
  131. data/spec/security/certificate_cache_spec.rb +71 -0
  132. data/spec/security/certificate_spec.rb +49 -0
  133. data/spec/security/distinguished_name_spec.rb +46 -0
  134. data/spec/security/encrypted_document_spec.rb +55 -0
  135. data/spec/security/rsa_key_pair_spec.rb +55 -0
  136. data/spec/security/signature_spec.rb +66 -0
  137. data/spec/security/static_certificate_store_spec.rb +52 -0
  138. data/spec/sender_spec.rb +887 -0
  139. data/spec/serialize/message_pack_spec.rb +131 -0
  140. data/spec/serialize/secure_serializer_spec.rb +102 -0
  141. data/spec/serialize/serializable_spec.rb +90 -0
  142. data/spec/serialize/serializer_spec.rb +174 -0
  143. data/spec/spec.opts +2 -0
  144. data/spec/spec_helper.rb +77 -0
  145. data/spec/stats_helper_spec.rb +681 -0
  146. data/spec/tracer_spec.rb +114 -0
  147. metadata +320 -0
data/spec/spec.opts ADDED
@@ -0,0 +1,2 @@
1
+ --colour
2
+ --format=nested
@@ -0,0 +1,77 @@
1
+ #
2
+ # Copyright (c) 2009-2011 RightScale Inc
3
+ #
4
+ # Permission is hereby granted, free of charge, to any person obtaining
5
+ # a copy of this software and associated documentation files (the
6
+ # "Software"), to deal in the Software without restriction, including
7
+ # without limitation the rights to use, copy, modify, merge, publish,
8
+ # distribute, sublicense, and/or sell copies of the Software, and to
9
+ # permit persons to whom the Software is furnished to do so, subject to
10
+ # the following conditions:
11
+ #
12
+ # The above copyright notice and this permission notice shall be
13
+ # included in all copies or substantial portions of the Software.
14
+ #
15
+ # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
16
+ # EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
17
+ # MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
18
+ # NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
19
+ # LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
20
+ # OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
21
+ # WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
22
+
23
+ require 'rubygems'
24
+ require 'bundler/setup'
25
+
26
+ require 'flexmock'
27
+ require 'rspec'
28
+ require 'eventmachine'
29
+ require 'fileutils'
30
+
31
+ require File.expand_path(File.join(File.dirname(__FILE__), '..', 'lib', 'right_agent'))
32
+ require File.expand_path(File.join(File.dirname(__FILE__), 'results_mock'))
33
+
34
+ RSpec.configure do |c|
35
+ c.mock_with(:flexmock)
36
+ end
37
+
38
+ RightScale::Log.init
39
+
40
+ $TESTING = true
41
+ $VERBOSE = nil # Disable constant redefined warning
42
+ TEST_SOCKET_PORT = 80000
43
+
44
+ module RightScale
45
+
46
+ module SpecHelper
47
+
48
+ # Create test certificate
49
+ def issue_cert
50
+ test_dn = { 'C' => 'US',
51
+ 'ST' => 'California',
52
+ 'L' => 'Santa Barbara',
53
+ 'O' => 'Agent',
54
+ 'OU' => 'Certification Services',
55
+ 'CN' => 'Agent test' }
56
+ dn = DistinguishedName.new(test_dn)
57
+ key = RsaKeyPair.new
58
+ [ Certificate.new(key, dn, dn), key ]
59
+ end
60
+
61
+ end # SpecHelper
62
+
63
+ class Log
64
+
65
+ # Monkey path RightAgent logger to not log by default
66
+ # Define env var RS_LOG to override this behavior and have the logger log normally
67
+ class << self
68
+ alias :original_method_missing :method_missing
69
+ end
70
+
71
+ def self.method_missing(m, *args)
72
+ original_method_missing(m, *args) unless [:debug, :info, :warn, :warning, :error, :fatal].include?(m) && ENV['RS_LOG'].nil?
73
+ end
74
+
75
+ end # Log
76
+
77
+ end
@@ -0,0 +1,681 @@
1
+ #
2
+ # Copyright (c) 2009-2011 RightScale Inc
3
+ #
4
+ # Permission is hereby granted, free of charge, to any person obtaining
5
+ # a copy of this software and associated documentation files (the
6
+ # "Software"), to deal in the Software without restriction, including
7
+ # without limitation the rights to use, copy, modify, merge, publish,
8
+ # distribute, sublicense, and/or sell copies of the Software, and to
9
+ # permit persons to whom the Software is furnished to do so, subject to
10
+ # the following conditions:
11
+ #
12
+ # The above copyright notice and this permission notice shall be
13
+ # included in all copies or substantial portions of the Software.
14
+ #
15
+ # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
16
+ # EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
17
+ # MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
18
+ # NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
19
+ # LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
20
+ # OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
21
+ # WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
22
+
23
+ require File.expand_path(File.join(File.dirname(__FILE__), 'spec_helper'))
24
+
25
+ describe RightScale::StatsHelper do
26
+
27
+ before(:all) do
28
+ @original_recent_size = RightScale::StatsHelper::ActivityStats::RECENT_SIZE
29
+ RightScale::StatsHelper::ActivityStats.const_set(:RECENT_SIZE, 10)
30
+ end
31
+
32
+ after(:all) do
33
+ RightScale::StatsHelper::ActivityStats.const_set(:RECENT_SIZE, @original_recent_size)
34
+ end
35
+
36
+ include FlexMock::ArgumentTypes
37
+
38
+ describe "ActivityStats" do
39
+
40
+ before(:each) do
41
+ @now = 1000000
42
+ flexmock(Time).should_receive(:now).and_return(@now).by_default
43
+ @stats = RightScale::StatsHelper::ActivityStats.new
44
+ end
45
+
46
+ it "should initialize stats data" do
47
+ @stats.instance_variable_get(:@interval).should == 0.0
48
+ @stats.instance_variable_get(:@last_start_time).should == @now
49
+ @stats.instance_variable_get(:@avg_duration).should be_nil
50
+ @stats.instance_variable_get(:@total).should == 0
51
+ @stats.instance_variable_get(:@count_per_type).should == {}
52
+ end
53
+
54
+ it "should update count and interval information" do
55
+ flexmock(Time).should_receive(:now).and_return(1000010)
56
+ @stats.update
57
+ @stats.instance_variable_get(:@interval).should == 1.0
58
+ @stats.instance_variable_get(:@last_start_time).should == @now + 10
59
+ @stats.instance_variable_get(:@avg_duration).should be_nil
60
+ @stats.instance_variable_get(:@total).should == 1
61
+ @stats.instance_variable_get(:@count_per_type).should == {}
62
+ end
63
+
64
+ it "should update weight the average interval toward recent activity" do
65
+ end
66
+
67
+ it "should update counts per type when type provided" do
68
+ flexmock(Time).should_receive(:now).and_return(1000010)
69
+ @stats.update("test")
70
+ @stats.instance_variable_get(:@interval).should == 1.0
71
+ @stats.instance_variable_get(:@last_start_time).should == @now + 10
72
+ @stats.instance_variable_get(:@avg_duration).should be_nil
73
+ @stats.instance_variable_get(:@total).should == 1
74
+ @stats.instance_variable_get(:@count_per_type).should == {"test" => 1}
75
+ end
76
+
77
+ it "should not update counts when type contains 'stats'" do
78
+ flexmock(Time).should_receive(:now).and_return(1000010)
79
+ @stats.update("my stats")
80
+ @stats.instance_variable_get(:@interval).should == 0.0
81
+ @stats.instance_variable_get(:@last_start_time).should == @now
82
+ @stats.instance_variable_get(:@avg_duration).should be_nil
83
+ @stats.instance_variable_get(:@total).should == 0
84
+ @stats.instance_variable_get(:@count_per_type).should == {}
85
+ end
86
+
87
+ it "should limit length of type string when submitting update" do
88
+ flexmock(Time).should_receive(:now).and_return(1000010)
89
+ @stats.update("test 12345678901234567890123456789012345678901234567890123456789")
90
+ @stats.instance_variable_get(:@total).should == 1
91
+ @stats.instance_variable_get(:@count_per_type).should ==
92
+ {"test 1234567890123456789012345678901234567890123456789012..." => 1}
93
+ end
94
+
95
+ it "should not convert symbol or boolean to string when submitting update" do
96
+ flexmock(Time).should_receive(:now).and_return(1000010)
97
+ @stats.update(:test)
98
+ @stats.update(true)
99
+ @stats.update(false)
100
+ @stats.instance_variable_get(:@total).should == 3
101
+ @stats.instance_variable_get(:@count_per_type).should == {:test => 1, true => 1, false => 1}
102
+ end
103
+
104
+ it "should convert arbitrary type value to limited-length string when submitting update" do
105
+ flexmock(Time).should_receive(:now).and_return(1000010)
106
+ @stats.update({1 => 11, 2 => 22})
107
+ @stats.update({1 => 11, 2 => 22, 3 => 12345678901234567890123456789012345678901234567890123456789})
108
+ @stats.instance_variable_get(:@total).should == 2
109
+ @stats.instance_variable_get(:@count_per_type).should == {"{1=>11, 2=>22}" => 1,
110
+ "{1=>11, 2=>22, 3=>123456789012345678901234567890123456789..." => 1}
111
+ end
112
+
113
+ it "should not measure rate if disabled" do
114
+ @stats = RightScale::StatsHelper::ActivityStats.new(false)
115
+ flexmock(Time).should_receive(:now).and_return(1000010)
116
+ @stats.update
117
+ @stats.instance_variable_get(:@interval).should == 0.0
118
+ @stats.instance_variable_get(:@last_start_time).should == @now + 10
119
+ @stats.instance_variable_get(:@avg_duration).should be_nil
120
+ @stats.instance_variable_get(:@total).should == 1
121
+ @stats.instance_variable_get(:@count_per_type).should == {}
122
+ @stats.all.should == {"last" => {"elapsed"=>0}, "total" => 1}
123
+ end
124
+
125
+ it "should update duration when finish using internal start time by default" do
126
+ flexmock(Time).should_receive(:now).and_return(1000010)
127
+ @stats.finish
128
+ @stats.instance_variable_get(:@interval).should == 0.0
129
+ @stats.instance_variable_get(:@last_start_time).should == @now
130
+ @stats.instance_variable_get(:@avg_duration).should == 1.0
131
+ @stats.instance_variable_get(:@total).should == 0
132
+ @stats.instance_variable_get(:@count_per_type).should == {}
133
+ end
134
+
135
+ it "should update duration when finish using specified start time" do
136
+ flexmock(Time).should_receive(:now).and_return(1000030)
137
+ @stats.avg_duration.should be_nil
138
+ @stats.finish(1000010)
139
+ @stats.instance_variable_get(:@interval).should == 0.0
140
+ @stats.instance_variable_get(:@last_start_time).should == @now
141
+ @stats.instance_variable_get(:@avg_duration).should == 2.0
142
+ @stats.instance_variable_get(:@total).should == 0
143
+ @stats.instance_variable_get(:@count_per_type).should == {}
144
+ end
145
+
146
+ it "should convert interval to rate" do
147
+ flexmock(Time).should_receive(:now).and_return(1000020)
148
+ @stats.avg_rate.should be_nil
149
+ @stats.update
150
+ @stats.instance_variable_get(:@interval).should == 2.0
151
+ @stats.avg_rate.should == 0.5
152
+ end
153
+
154
+ it "should report number of seconds since last update or nil if no updates" do
155
+ flexmock(Time).should_receive(:now).and_return(1000010)
156
+ @stats.last.should be_nil
157
+ @stats.update
158
+ @stats.last.should == {"elapsed" => 0}
159
+ end
160
+
161
+ it "should report number of seconds since last update and last type" do
162
+ @stats.update("test")
163
+ flexmock(Time).should_receive(:now).and_return(1000010)
164
+ @stats.last.should == {"elapsed" => 10, "type" => "test"}
165
+ end
166
+
167
+ it "should report whether last activity is still active" do
168
+ @stats.update("test", "token")
169
+ flexmock(Time).should_receive(:now).and_return(1000010)
170
+ @stats.last.should == {"elapsed" => 10, "type" => "test", "active" => true}
171
+ @stats.finish(@now - 10, "token")
172
+ @stats.last.should == {"elapsed" => 10, "type" => "test", "active" => false}
173
+ @stats.instance_variable_get(:@avg_duration).should == 2.0
174
+ end
175
+
176
+ it "should convert count per type to percentages" do
177
+ flexmock(Time).should_receive(:now).and_return(1000010)
178
+ @stats.update("foo")
179
+ @stats.instance_variable_get(:@total).should == 1
180
+ @stats.instance_variable_get(:@count_per_type).should == {"foo" => 1}
181
+ @stats.percentage.should == {"total" => 1, "percent" => {"foo" => 100.0}}
182
+ @stats.update("bar")
183
+ @stats.instance_variable_get(:@total).should == 2
184
+ @stats.instance_variable_get(:@count_per_type).should == {"foo" => 1, "bar" => 1}
185
+ @stats.percentage.should == {"total" => 2, "percent" => {"foo" => 50.0, "bar" => 50.0}}
186
+ @stats.update("foo")
187
+ @stats.update("foo")
188
+ @stats.instance_variable_get(:@total).should == 4
189
+ @stats.instance_variable_get(:@count_per_type).should == {"foo" => 3, "bar" => 1}
190
+ @stats.percentage.should == {"total" => 4, "percent" => {"foo" => 75.0, "bar" => 25.0}}
191
+ end
192
+
193
+ end # ActivityStats
194
+
195
+ describe "ExceptionStats" do
196
+
197
+ before(:each) do
198
+ @now = 1000000
199
+ flexmock(Time).should_receive(:now).and_return(@now).by_default
200
+ @stats = RightScale::StatsHelper::ExceptionStats.new
201
+ @exception = Exception.new("Test error")
202
+ end
203
+
204
+ it "should initialize stats data" do
205
+ @stats.stats.should be_nil
206
+ @stats.instance_variable_get(:@callback).should be_nil
207
+ end
208
+
209
+ it "should track submitted exception information by category" do
210
+ @stats.track("testing", @exception)
211
+ @stats.stats.should == {"testing" => {"total" => 1,
212
+ "recent" => [{"count" => 1, "type" => "Exception", "message" => "Test error",
213
+ "when" => @now, "where" => nil}]}}
214
+ end
215
+
216
+ it "should recognize and count repeated exceptions" do
217
+ @stats.track("testing", @exception)
218
+ @stats.stats.should == {"testing" => {"total" => 1,
219
+ "recent" => [{"count" => 1, "type" => "Exception", "message" => "Test error",
220
+ "when" => @now, "where" => nil}]}}
221
+ flexmock(Time).should_receive(:now).and_return(1000010)
222
+ category = "another"
223
+ backtrace = ["here", "and", "there"]
224
+ 4.times do |i|
225
+ begin
226
+ raise ArgumentError, "badarg"
227
+ rescue Exception => e
228
+ flexmock(e).should_receive(:backtrace).and_return(backtrace)
229
+ @stats.track(category, e)
230
+ backtrace.shift(2) if i == 1
231
+ category = "testing" if i == 2
232
+ end
233
+ end
234
+ @stats.stats.should == {"testing" => {"total" => 2,
235
+ "recent" => [{"count" => 1, "type" => "Exception", "message" => "Test error",
236
+ "when" => @now, "where" => nil},
237
+ {"count" => 1, "type" => "ArgumentError", "message" => "badarg",
238
+ "when" => @now + 10, "where" => "there"}]},
239
+ "another" => {"total" => 3,
240
+ "recent" => [{"count" => 2, "type" => "ArgumentError", "message" => "badarg",
241
+ "when" => @now + 10, "where" => "here"},
242
+ {"count" => 1, "type" => "ArgumentError", "message" => "badarg",
243
+ "when" => @now + 10, "where" => "there"}]}}
244
+ end
245
+
246
+ it "should limit the number of exceptions stored by eliminating older exceptions" do
247
+ (RightScale::StatsHelper::ExceptionStats::MAX_RECENT_EXCEPTIONS + 1).times do |i|
248
+ begin
249
+ raise ArgumentError, "badarg"
250
+ rescue Exception => e
251
+ flexmock(e).should_receive(:backtrace).and_return([i.to_s])
252
+ @stats.track("testing", e)
253
+ end
254
+ end
255
+ stats = @stats.stats
256
+ stats["testing"]["total"].should == RightScale::StatsHelper::ExceptionStats::MAX_RECENT_EXCEPTIONS + 1
257
+ stats["testing"]["recent"].size.should == RightScale::StatsHelper::ExceptionStats::MAX_RECENT_EXCEPTIONS
258
+ stats["testing"]["recent"][0]["where"].should == "1"
259
+ end
260
+
261
+ it "should make callback if callback and message defined" do
262
+ called = 0
263
+ callback = lambda do |exception, message, server|
264
+ called += 1
265
+ exception.should == @exception
266
+ message.should == "message"
267
+ server.should == "server"
268
+ end
269
+ @stats = RightScale::StatsHelper::ExceptionStats.new("server", callback)
270
+ @stats.track("testing", @exception, "message")
271
+ @stats.stats.should == {"testing" => {"total" => 1,
272
+ "recent" => [{"count" => 1, "type" => "Exception", "message" => "Test error",
273
+ "when" => @now, "where" => nil}]}}
274
+ called.should == 1
275
+ end
276
+
277
+ it "should catch any exceptions raised internally" do
278
+ flexmock(RightScale::Log).should_receive(:error).with(/Failed to track exception/).once
279
+ flexmock(@exception).should_receive(:backtrace).and_raise(Exception)
280
+ @stats = RightScale::StatsHelper::ExceptionStats.new
281
+ @stats.track("testing", @exception, "message")
282
+ @stats.stats["testing"]["total"].should == 1
283
+ end
284
+
285
+ end # ExceptionStats
286
+
287
+ describe "Formatting" do
288
+
289
+ include RightScale::StatsHelper
290
+
291
+ before(:each) do
292
+ @now = 1000000
293
+ flexmock(Time).should_receive(:now).and_return(@now).by_default
294
+ @exceptions = RightScale::StatsHelper::ExceptionStats.new
295
+ @brokers = {"brokers"=> [{"alias" => "b0", "identity" => "rs-broker-localhost-5672", "status" => "connected",
296
+ "disconnect last" => nil,"disconnects" => nil, "failure last" => nil, "failures" => nil,
297
+ "retries" => nil},
298
+ {"alias" => "b1", "identity" => "rs-broker-localhost-5673", "status" => "disconnected",
299
+ "disconnect last" => {"elapsed" => 1000}, "disconnects" => 2,
300
+ "failure last" => nil, "failures" => nil, "retries" => nil},
301
+ {"alias" => "b2", "identity" => "rs-broker-localhost-5674", "status" => "failed",
302
+ "disconnect last" => nil, "disconnects" => nil,
303
+ "failure last" => {"elapsed" => 1000}, "failures" => 3, "retries" => 2}],
304
+ "exceptions" => {}}
305
+ end
306
+
307
+ it "should convert values to percentages" do
308
+ stats = {"first" => 1, "second" => 4, "third" => 3}
309
+ result = percentage(stats)
310
+ result.should == {"total" => 8, "percent" => {"first" => 12.5, "second" => 50.0, "third" => 37.5}}
311
+ end
312
+
313
+ it "should convert 0 to nil" do
314
+ nil_if_zero(0).should be_nil
315
+ nil_if_zero(0.0).should be_nil
316
+ nil_if_zero(1).should == 1
317
+ nil_if_zero(1.0).should == 1.0
318
+ end
319
+
320
+ it "should sort hash by key into array with integer conversion of keys if possible" do
321
+ sort_key({"c" => 3, "a" => 1, "b" => 2}).should == [["a", 1], ["b", 2], ["c", 3]]
322
+ sort_key({3 => "c", 1 => "a", 2 => "b"}).should == [[1, "a"], [2, "b"], [3, "c"]]
323
+ sort_key({11 => "c", 9 => "a", 10 => "b"}).should == [[9, "a"], [10, "b"], [11, "c"]]
324
+ sort_key({"append_info" => 9.6, "create_new_section" => 8.5, "append_output" => 7.3, "record" => 4.7,
325
+ "update_status" => 4.4,
326
+ "declare" => 39.2, "list_agents" => 3.7, "update_tags" => 3.2, "append_error" => 3.0,
327
+ "add_user" => 2.4, "get_boot_bundle" => 1.4, "get_repositories" => 1.4,
328
+ "update_login_policy" => 1.3, "schedule_decommission" => 0.91, "update_inputs" => 0.75,
329
+ "delete_queues" => 0.75, "soft_decommission" => 0.75, "remove" => 0.66,
330
+ "get_login_policy" => 0.58, "ping" => 0.50, "update_entry" => 0.25, "query_tags" => 0.083,
331
+ "get_decommission_bundle" => 0.083, "list_queues" => 0.083}).should ==
332
+ [["add_user", 2.4], ["append_error", 3.0], ["append_info", 9.6], ["append_output", 7.3],
333
+ ["create_new_section", 8.5], ["declare", 39.2], ["delete_queues", 0.75], ["get_boot_bundle", 1.4],
334
+ ["get_decommission_bundle", 0.083], ["get_login_policy", 0.58], ["get_repositories", 1.4],
335
+ ["list_agents", 3.7], ["list_queues", 0.083], ["ping", 0.5], ["query_tags", 0.083],
336
+ ["record", 4.7], ["remove", 0.66], ["schedule_decommission", 0.91], ["soft_decommission", 0.75],
337
+ ["update_entry", 0.25], ["update_inputs", 0.75],
338
+ ["update_login_policy", 1.3], ["update_status", 4.4], ["update_tags", 3.2]]
339
+ end
340
+
341
+ it "should sort hash by value into array" do
342
+ sort_value({"c" => 3, "a" => 2, "b" => 1}).should == [["b", 1], ["a", 2], ["c", 3]]
343
+ sort_value({"c" => 3.0, "a" => 2, "b" => 1.0}).should == [["b", 1.0], ["a", 2], ["c", 3.0]]
344
+ sort_value({"append_info" => 9.6, "create_new_section" => 8.5, "append_output" => 7.3, "record" => 4.7,
345
+ "update_status" => 4.4,
346
+ "declare" => 39.2, "list_agents" => 3.7, "update_tags" => 3.2, "append_error" => 3.0,
347
+ "add_user" => 2.4, "get_boot_bundle" => 1.4, "get_repositories" => 1.4,
348
+ "update_login_policy" => 1.3, "schedule_decommission" => 0.91, "update_inputs" => 0.75,
349
+ "delete_queues" => 0.75, "soft_decommission" => 0.75, "remove" => 0.66,
350
+ "get_login_policy" => 0.58, "ping" => 0.50, "update_entry" => 0.25, "query_tags" => 0.083,
351
+ "get_decommission_bundle" => 0.083, "list_queues" => 0.083}).should ==
352
+ [["list_queues", 0.083], ["query_tags", 0.083], ["get_decommission_bundle", 0.083],
353
+ ["update_entry", 0.25], ["ping", 0.5], ["get_login_policy", 0.58], ["remove", 0.66],
354
+ ["delete_queues", 0.75], ["soft_decommission", 0.75], ["update_inputs", 0.75],
355
+ ["schedule_decommission", 0.91], ["update_login_policy", 1.3], ["get_repositories", 1.4],
356
+ ["get_boot_bundle", 1.4], ["add_user", 2.4], ["append_error", 3.0], ["update_tags", 3.2],
357
+ ["list_agents", 3.7], ["update_status", 4.4],
358
+ ["record", 4.7], ["append_output", 7.3], ["create_new_section", 8.5], ["append_info", 9.6],
359
+ ["declare", 39.2]]
360
+ end
361
+
362
+ it "should wrap string by breaking it into lines at the specified separator" do
363
+ string = "Now is the time for all good men to come to the aid of their people."
364
+ result = wrap(string, 20, " ", " ")
365
+ result.should == "Now is the time for \n" +
366
+ " all good men to come \n" +
367
+ " to the aid of their \n" +
368
+ " people."
369
+ string = "dogs: 2, cats: 10, hippopotami: 99, bears: 1, ants: 100000"
370
+ result = wrap(string, 22, "--", ", ")
371
+ result.should == "dogs: 2, cats: 10, \n" +
372
+ "--hippopotami: 99, \n" +
373
+ "--bears: 1, ants: 100000"
374
+ end
375
+
376
+ it "should convert elapsed time to displayable format" do
377
+ elapsed(0).should == "0 sec"
378
+ elapsed(1).should == "1 sec"
379
+ elapsed(60).should == "60 sec"
380
+ elapsed(61).should == "1 min 1 sec"
381
+ elapsed(62).should == "1 min 2 sec"
382
+ elapsed(120).should == "2 min 0 sec"
383
+ elapsed(3600).should == "60 min 0 sec"
384
+ elapsed(3601).should == "1 hr 0 min"
385
+ elapsed(3659).should == "1 hr 0 min"
386
+ elapsed(3660).should == "1 hr 1 min"
387
+ elapsed(3720).should == "1 hr 2 min"
388
+ elapsed(7200).should == "2 hr 0 min"
389
+ elapsed(7260).should == "2 hr 1 min"
390
+ elapsed(86400).should == "24 hr 0 min"
391
+ elapsed(86401).should == "1 day 0 hr 0 min"
392
+ elapsed(86459).should == "1 day 0 hr 0 min"
393
+ elapsed(86460).should == "1 day 0 hr 1 min"
394
+ elapsed(90000).should == "1 day 1 hr 0 min"
395
+ elapsed(183546).should == "2 days 2 hr 59 min"
396
+ elapsed(125.5).should == "2 min 5 sec"
397
+ end
398
+
399
+ it "should convert floating point values to decimal digit string with at least two digit precision" do
400
+ enough_precision(100.5).should == "101"
401
+ enough_precision(100.4).should == "100"
402
+ enough_precision(99.0).should == "99"
403
+ enough_precision(10.5).should == "11"
404
+ enough_precision(10.4).should == "10"
405
+ enough_precision(9.15).should == "9.2"
406
+ enough_precision(9.1).should == "9.1"
407
+ enough_precision(1.05).should == "1.1"
408
+ enough_precision(1.01).should == "1.0"
409
+ enough_precision(1.0).should == "1.0"
410
+ enough_precision(0.995).should == "1.00"
411
+ enough_precision(0.991).should == "0.99"
412
+ enough_precision(0.0995).should == "0.100"
413
+ enough_precision(0.0991).should == "0.099"
414
+ enough_precision(0.00995).should == "0.0100"
415
+ enough_precision(0.00991).should == "0.0099"
416
+ enough_precision(0.000995).should == "0.00100"
417
+ enough_precision(0.000991).should == "0.00099"
418
+ enough_precision(0.000005).should == "0.00001"
419
+ enough_precision(0.000001).should == "0.00000"
420
+ enough_precision(0.0).should == "0"
421
+ enough_precision(55).should == "55"
422
+ enough_precision({"a" => 65.0, "b" => 23.0, "c" => 12.0}).should == {"a" => "65", "b" => "23", "c" => "12"}
423
+ enough_precision({"a" => 65.0, "b" => 33.0, "c" => 2.0}).should == {"a" => "65.0", "b" => "33.0", "c" => "2.0"}
424
+ enough_precision({"a" => 10.45, "b" => 1.0, "c" => 0.011}).should == {"a" => "10.5", "b" => "1.0", "c" => "0.011"}
425
+ enough_precision({"a" => 1000.0, "b" => 0.1, "c" => 0.0, "d" => 0.0001, "e" => 0.00001, "f" => 0.000001}).should ==
426
+ {"a" => "1000.0", "b" => "0.10", "c" => "0.0", "d" => "0.00010", "e" => "0.00001", "f" => "0.00000"}
427
+ enough_precision([["a", 65.0], ["b", 23.0], ["c", 12.0]]).should == [["a", "65"], ["b", "23"], ["c", "12"]]
428
+ enough_precision([["a", 65.0], ["b", 33.0], ["c", 2.0]]).should == [["a", "65.0"], ["b", "33.0"], ["c", "2.0"]]
429
+ enough_precision([["a", 10.45], ["b", 1.0], ["c", 0.011]]).should == [["a", "10.5"], ["b", "1.0"], ["c", "0.011"]]
430
+ enough_precision([["a", 1000.0], ["b", 0.1], ["c", 0.0], ["d", 0.0001], ["e", 0.00001], ["f", 0.000001]]).should ==
431
+ [["a", "1000.0"], ["b", "0.10"], ["c", "0.0"], ["d", "0.00010"], ["e", "0.00001"], ["f", "0.00000"]]
432
+ end
433
+
434
+ it "should convert broker status to multi-line display string" do
435
+ result = brokers_str(@brokers, 10)
436
+ result.should == "brokers : b0: rs-broker-localhost-5672 connected, disconnects: none, failures: none\n" +
437
+ " b1: rs-broker-localhost-5673 disconnected, disconnects: 2 (16 min 40 sec ago), failures: none\n" +
438
+ " b2: rs-broker-localhost-5674 failed, disconnects: none, failures: 3 (16 min 40 sec ago w/ 2 retries)\n" +
439
+ " exceptions : none\n" +
440
+ " returns : none\n"
441
+ end
442
+
443
+ it "should display broker exceptions and returns" do
444
+ @exceptions.track("testing", Exception.new("Test error"))
445
+ @brokers["exceptions"] = @exceptions.stats
446
+ activity = RightScale::StatsHelper::ActivityStats.new
447
+ activity.update("no queue")
448
+ activity.finish(@now - 10)
449
+ activity.update("no queue consumers")
450
+ activity.update("no queue consumers")
451
+ flexmock(Time).should_receive(:now).and_return(1000010)
452
+ @brokers["returns"] = activity.all
453
+ result = brokers_str(@brokers, 10)
454
+ result.should == "brokers : b0: rs-broker-localhost-5672 connected, disconnects: none, failures: none\n" +
455
+ " b1: rs-broker-localhost-5673 disconnected, disconnects: 2 (16 min 40 sec ago), failures: none\n" +
456
+ " b2: rs-broker-localhost-5674 failed, disconnects: none, failures: 3 (16 min 40 sec ago w/ 2 retries)\n" +
457
+ " exceptions : testing total: 1, most recent:\n" +
458
+ " (1) Mon Jan 12 05:46:40 Exception: Test error\n" +
459
+ " \n" +
460
+ " returns : no queue consumers: 67%, no queue: 33%, total: 3, \n" +
461
+ " last: no queue consumers (10 sec ago), rate: 0/sec\n"
462
+ end
463
+
464
+ it 'should convert activity stats to string' do
465
+ activity = RightScale::StatsHelper::ActivityStats.new
466
+ activity.update("testing")
467
+ activity.finish(@now - 10)
468
+ activity.update("more testing")
469
+ activity.update("more testing")
470
+ activity.update("more testing")
471
+ flexmock(Time).should_receive(:now).and_return(1000010)
472
+ activity_str(activity.all).should == "more testing: 75%, testing: 25%, total: 4, last: more testing (10 sec ago), " +
473
+ "rate: 0/sec"
474
+ end
475
+
476
+ it 'should convert last activity stats to string' do
477
+ activity = RightScale::StatsHelper::ActivityStats.new
478
+ activity.update("testing")
479
+ activity.finish(@now - 10)
480
+ activity.update("more testing")
481
+ flexmock(Time).should_receive(:now).and_return(1000010)
482
+ last_activity_str(activity.last).should == "more testing: 10 sec ago"
483
+ last_activity_str(activity.last, single_item = true).should == "more testing (10 sec ago)"
484
+ end
485
+
486
+ it "should convert exception stats to multi-line string" do
487
+ @exceptions.track("testing", Exception.new("This is a very long exception message that should be truncated " +
488
+ "to a reasonable length"))
489
+ flexmock(Time).should_receive(:now).and_return(1000010)
490
+ category = "another"
491
+ backtrace = ["It happened here", "Over there"]
492
+ 4.times do |i|
493
+ begin
494
+ raise ArgumentError, "badarg"
495
+ rescue Exception => e
496
+ flexmock(e).should_receive(:backtrace).and_return(backtrace)
497
+ @exceptions.track(category, e)
498
+ backtrace.shift(1) if i == 1
499
+ category = "testing" if i == 2
500
+ end
501
+ end
502
+
503
+ result = exceptions_str(@exceptions.stats, "----")
504
+ result.should == "another total: 3, most recent:\n" +
505
+ "----(1) Mon Jan 12 05:46:50 ArgumentError: badarg\n" +
506
+ "---- Over there\n" +
507
+ "----(2) Mon Jan 12 05:46:50 ArgumentError: badarg\n" +
508
+ "---- It happened here\n" +
509
+ "----testing total: 2, most recent:\n" +
510
+ "----(1) Mon Jan 12 05:46:50 ArgumentError: badarg\n" +
511
+ "---- Over there\n" +
512
+ "----(1) Mon Jan 12 05:46:40 Exception: This is a very long exception message that should be trun...\n" +
513
+ "---- "
514
+ end
515
+
516
+ it "should convert nested hash into string with keys sorted numerically if possible, else alphabetically" do
517
+ hash = {"dogs" => 2, "cats" => 3, "hippopotami" => 99, "bears" => 1, "ants" => 100000000, "dragons" => nil,
518
+ "food" => {"apples" => "bushels", "berries" => "lots", "meat" => {"fish" => 10.54, "beef" => nil}},
519
+ "versions" => { "1" => 10, "5" => 50, "10" => 100} }
520
+ result = hash_str(hash)
521
+ result.should == "ants: 100000000, bears: 1, cats: 3, dogs: 2, dragons: none, " +
522
+ "food: [ apples: bushels, berries: lots, meat: [ beef: none, fish: 11 ] ], " +
523
+ "hippopotami: 99, versions: [ 1: 10, 5: 50, 10: 100 ]"
524
+ result = wrap(result, 20, "----", ", ")
525
+ result.should == "ants: 100000000, \n" +
526
+ "----bears: 1, cats: 3, \n" +
527
+ "----dogs: 2, \n" +
528
+ "----dragons: none, \n" +
529
+ "----food: [ apples: bushels, \n" +
530
+ "----berries: lots, \n" +
531
+ "----meat: [ beef: none, \n" +
532
+ "----fish: 11 ] ], \n" +
533
+ "----hippopotami: 99, \n" +
534
+ "----versions: [ 1: 10, \n" +
535
+ "----5: 50, 10: 100 ]"
536
+ end
537
+
538
+ it "should convert sub-stats to a display string" do
539
+ @exceptions.track("testing", Exception.new("Test error"))
540
+ activity1 = RightScale::StatsHelper::ActivityStats.new
541
+ activity2 = RightScale::StatsHelper::ActivityStats.new
542
+ activity3 = RightScale::StatsHelper::ActivityStats.new
543
+ activity2.update("stats")
544
+ activity2.update("testing")
545
+ activity2.update("more testing")
546
+ activity2.update("more testing")
547
+ activity2.update("more testing")
548
+ activity3.update("testing forever", "id")
549
+ flexmock(Time).should_receive(:now).and_return(1002800)
550
+
551
+ stats = {"exceptions" => @exceptions.stats,
552
+ "empty_hash" => {},
553
+ "float_value" => 3.15,
554
+ "some % percent" => 3.54,
555
+ "some time" => 0.675,
556
+ "some rate" => 4.72,
557
+ "some age" => 125,
558
+ "activity1 %" => activity1.percentage,
559
+ "activity1 last" => activity1.last,
560
+ "activity2 %" => activity2.percentage,
561
+ "activity2 last" => activity2.last,
562
+ "activity3 last" => activity3.last,
563
+ "some hash" => {"dogs" => 2, "cats" => 3, "hippopotami" => 99, "bears" => 1,
564
+ "ants" => 100000000, "dragons" => nil, "leopards" => 25}}
565
+
566
+ result = sub_stats_str("my sub-stats", stats, 13)
567
+ result.should == "my sub-stats : activity1 % : none\n" +
568
+ " activity1 last : none\n" +
569
+ " activity2 % : more testing: 75%, testing: 25%, total: 4\n" +
570
+ " activity2 last : more testing: 46 min 40 sec ago\n" +
571
+ " activity3 last : testing forever: 46 min 40 sec ago and still active\n" +
572
+ " empty_hash : none\n" +
573
+ " exceptions : testing total: 1, most recent:\n" +
574
+ " (1) Mon Jan 12 05:46:40 Exception: Test error\n" +
575
+ " \n" +
576
+ " float_value : 3.2\n" +
577
+ " some % : 3.5%\n" +
578
+ " some age : 2 min 5 sec\n" +
579
+ " some hash : ants: 100000000, bears: 1, cats: 3, dogs: 2, dragons: none, hippopotami: 99, \n" +
580
+ " leopards: 25\n" +
581
+ " some rate : 4.7/sec\n" +
582
+ " some time : 0.68 sec\n"
583
+ end
584
+
585
+ it "should convert stats to a display string with special formatting for generic keys" do
586
+ @exceptions.track("testing", Exception.new("Test error"))
587
+ activity = RightScale::StatsHelper::ActivityStats.new
588
+ activity.update("testing")
589
+ flexmock(Time).should_receive(:now).and_return(1000010)
590
+ sub_stats = {"exceptions" => @exceptions.stats,
591
+ "empty_hash" => {},
592
+ "float_value" => 3.15,
593
+ "activity %" => activity.percentage,
594
+ "activity last" => activity.last,
595
+ "some hash" => {"dogs" => 2, "cats" => 3, "hippopotami" => 99, "bears" => 1,
596
+ "ants" => 100000000, "dragons" => nil, "leopards" => 25}}
597
+ stats = {"stat time" => @now,
598
+ "last reset time" => @now,
599
+ "service uptime" => 3720,
600
+ "machine uptime" => 183546,
601
+ "version" => 10,
602
+ "brokers" => @brokers,
603
+ "hostname" => "localhost",
604
+ "identity" => "unit tester",
605
+ "stuff stats" => sub_stats}
606
+
607
+ result = stats_str(stats)
608
+ result.should == "identity : unit tester\n" +
609
+ "hostname : localhost\n" +
610
+ "stat time : Mon Jan 12 05:46:40\n" +
611
+ "last reset : Mon Jan 12 05:46:40\n" +
612
+ "service up : 1 hr 2 min\n" +
613
+ "machine up : 2 days 2 hr 59 min\n" +
614
+ "version : 10\n" +
615
+ "brokers : b0: rs-broker-localhost-5672 connected, disconnects: none, failures: none\n" +
616
+ " b1: rs-broker-localhost-5673 disconnected, disconnects: 2 (16 min 40 sec ago), failures: none\n" +
617
+ " b2: rs-broker-localhost-5674 failed, disconnects: none, failures: 3 (16 min 40 sec ago w/ 2 retries)\n" +
618
+ " exceptions : none\n" +
619
+ " returns : none\n" +
620
+ "stuff : activity % : testing: 100%, total: 1\n" +
621
+ " activity last : testing: 10 sec ago\n" +
622
+ " empty_hash : none\n" +
623
+ " exceptions : testing total: 1, most recent:\n" +
624
+ " (1) Mon Jan 12 05:46:40 Exception: Test error\n" +
625
+ " \n" +
626
+ " float_value : 3.2\n" +
627
+ " some hash : ants: 100000000, bears: 1, cats: 3, dogs: 2, dragons: none, hippopotami: 99, \n" +
628
+ " leopards: 25\n"
629
+ end
630
+
631
+ it "should treat broker status, version, and machine uptime as optional" do
632
+ sub_stats = {"exceptions" => @exceptions.stats,
633
+ "empty_hash" => {},
634
+ "float_value" => 3.15}
635
+
636
+ stats = {"stat time" => @now,
637
+ "last reset time" => @now,
638
+ "service uptime" => 1000,
639
+ "hostname" => "localhost",
640
+ "identity" => "unit tester",
641
+ "stuff stats" => sub_stats}
642
+
643
+ result = stats_str(stats)
644
+ result.should == "identity : unit tester\n" +
645
+ "hostname : localhost\n" +
646
+ "stat time : Mon Jan 12 05:46:40\n" +
647
+ "last reset : Mon Jan 12 05:46:40\n" +
648
+ "service up : 16 min 40 sec\n" +
649
+ "stuff : empty_hash : none\n" +
650
+ " exceptions : none\n" +
651
+ " float_value : 3.2\n"
652
+ end
653
+
654
+ it "should display name if provided" do
655
+ sub_stats = {"exceptions" => @exceptions.stats,
656
+ "empty_hash" => {},
657
+ "float_value" => 3.15}
658
+
659
+ stats = {"stat time" => @now,
660
+ "last reset time" => @now,
661
+ "service uptime" => 1000,
662
+ "hostname" => "localhost",
663
+ "identity" => "unit tester",
664
+ "name" => "tester_1",
665
+ "stuff stats" => sub_stats}
666
+
667
+ result = stats_str(stats)
668
+ result.should == "name : tester_1\n" +
669
+ "identity : unit tester\n" +
670
+ "hostname : localhost\n" +
671
+ "stat time : Mon Jan 12 05:46:40\n" +
672
+ "last reset : Mon Jan 12 05:46:40\n" +
673
+ "service up : 16 min 40 sec\n" +
674
+ "stuff : empty_hash : none\n" +
675
+ " exceptions : none\n" +
676
+ " float_value : 3.2\n"
677
+ end
678
+
679
+ end # Formatting
680
+
681
+ end # RightScale::StatsHelper