right_agent 0.5.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (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