right_agent 2.0.7-x86-mingw32

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 (176) hide show
  1. data/LICENSE +20 -0
  2. data/README.rdoc +82 -0
  3. data/Rakefile +113 -0
  4. data/lib/right_agent.rb +59 -0
  5. data/lib/right_agent/actor.rb +182 -0
  6. data/lib/right_agent/actor_registry.rb +76 -0
  7. data/lib/right_agent/actors/agent_manager.rb +232 -0
  8. data/lib/right_agent/agent.rb +1149 -0
  9. data/lib/right_agent/agent_config.rb +480 -0
  10. data/lib/right_agent/agent_identity.rb +210 -0
  11. data/lib/right_agent/agent_tag_manager.rb +237 -0
  12. data/lib/right_agent/audit_formatter.rb +107 -0
  13. data/lib/right_agent/clients.rb +31 -0
  14. data/lib/right_agent/clients/api_client.rb +383 -0
  15. data/lib/right_agent/clients/auth_client.rb +247 -0
  16. data/lib/right_agent/clients/balanced_http_client.rb +369 -0
  17. data/lib/right_agent/clients/base_retry_client.rb +495 -0
  18. data/lib/right_agent/clients/right_http_client.rb +279 -0
  19. data/lib/right_agent/clients/router_client.rb +493 -0
  20. data/lib/right_agent/command.rb +30 -0
  21. data/lib/right_agent/command/agent_manager_commands.rb +150 -0
  22. data/lib/right_agent/command/command_client.rb +136 -0
  23. data/lib/right_agent/command/command_constants.rb +33 -0
  24. data/lib/right_agent/command/command_io.rb +126 -0
  25. data/lib/right_agent/command/command_parser.rb +87 -0
  26. data/lib/right_agent/command/command_runner.rb +118 -0
  27. data/lib/right_agent/command/command_serializer.rb +63 -0
  28. data/lib/right_agent/connectivity_checker.rb +179 -0
  29. data/lib/right_agent/console.rb +65 -0
  30. data/lib/right_agent/core_payload_types.rb +44 -0
  31. data/lib/right_agent/core_payload_types/cookbook.rb +61 -0
  32. data/lib/right_agent/core_payload_types/cookbook_position.rb +46 -0
  33. data/lib/right_agent/core_payload_types/cookbook_repository.rb +116 -0
  34. data/lib/right_agent/core_payload_types/cookbook_sequence.rb +70 -0
  35. data/lib/right_agent/core_payload_types/dev_repositories.rb +100 -0
  36. data/lib/right_agent/core_payload_types/dev_repository.rb +76 -0
  37. data/lib/right_agent/core_payload_types/event_categories.rb +38 -0
  38. data/lib/right_agent/core_payload_types/executable_bundle.rb +130 -0
  39. data/lib/right_agent/core_payload_types/login_policy.rb +72 -0
  40. data/lib/right_agent/core_payload_types/login_user.rb +79 -0
  41. data/lib/right_agent/core_payload_types/planned_volume.rb +94 -0
  42. data/lib/right_agent/core_payload_types/recipe_instantiation.rb +73 -0
  43. data/lib/right_agent/core_payload_types/repositories_bundle.rb +50 -0
  44. data/lib/right_agent/core_payload_types/right_script_attachment.rb +95 -0
  45. data/lib/right_agent/core_payload_types/right_script_instantiation.rb +94 -0
  46. data/lib/right_agent/core_payload_types/runlist_policy.rb +44 -0
  47. data/lib/right_agent/core_payload_types/secure_document.rb +66 -0
  48. data/lib/right_agent/core_payload_types/secure_document_location.rb +63 -0
  49. data/lib/right_agent/core_payload_types/software_repository_instantiation.rb +61 -0
  50. data/lib/right_agent/daemonize.rb +35 -0
  51. data/lib/right_agent/dispatched_cache.rb +109 -0
  52. data/lib/right_agent/dispatcher.rb +272 -0
  53. data/lib/right_agent/enrollment_result.rb +221 -0
  54. data/lib/right_agent/exceptions.rb +87 -0
  55. data/lib/right_agent/history.rb +145 -0
  56. data/lib/right_agent/log.rb +460 -0
  57. data/lib/right_agent/minimal.rb +46 -0
  58. data/lib/right_agent/monkey_patches.rb +30 -0
  59. data/lib/right_agent/monkey_patches/ruby_patch.rb +55 -0
  60. data/lib/right_agent/monkey_patches/ruby_patch/array_patch.rb +29 -0
  61. data/lib/right_agent/monkey_patches/ruby_patch/darwin_patch.rb +24 -0
  62. data/lib/right_agent/monkey_patches/ruby_patch/linux_patch.rb +24 -0
  63. data/lib/right_agent/monkey_patches/ruby_patch/linux_patch/file_patch.rb +30 -0
  64. data/lib/right_agent/monkey_patches/ruby_patch/object_patch.rb +49 -0
  65. data/lib/right_agent/monkey_patches/ruby_patch/windows_patch.rb +32 -0
  66. data/lib/right_agent/monkey_patches/ruby_patch/windows_patch/file_patch.rb +60 -0
  67. data/lib/right_agent/monkey_patches/ruby_patch/windows_patch/process_patch.rb +63 -0
  68. data/lib/right_agent/monkey_patches/ruby_patch/windows_patch/stdio_patch.rb +27 -0
  69. data/lib/right_agent/monkey_patches/ruby_patch/windows_patch/time_patch.rb +55 -0
  70. data/lib/right_agent/monkey_patches/ruby_patch/windows_patch/win32ole_patch.rb +34 -0
  71. data/lib/right_agent/multiplexer.rb +102 -0
  72. data/lib/right_agent/offline_handler.rb +270 -0
  73. data/lib/right_agent/operation_result.rb +300 -0
  74. data/lib/right_agent/packets.rb +673 -0
  75. data/lib/right_agent/payload_formatter.rb +104 -0
  76. data/lib/right_agent/pending_requests.rb +128 -0
  77. data/lib/right_agent/pid_file.rb +159 -0
  78. data/lib/right_agent/platform.rb +770 -0
  79. data/lib/right_agent/platform/unix/darwin/platform.rb +102 -0
  80. data/lib/right_agent/platform/unix/linux/platform.rb +305 -0
  81. data/lib/right_agent/platform/unix/platform.rb +226 -0
  82. data/lib/right_agent/platform/windows/mingw/platform.rb +447 -0
  83. data/lib/right_agent/platform/windows/mswin/platform.rb +236 -0
  84. data/lib/right_agent/platform/windows/platform.rb +1808 -0
  85. data/lib/right_agent/protocol_version_mixin.rb +69 -0
  86. data/lib/right_agent/retryable_request.rb +195 -0
  87. data/lib/right_agent/scripts/agent_controller.rb +543 -0
  88. data/lib/right_agent/scripts/agent_deployer.rb +400 -0
  89. data/lib/right_agent/scripts/common_parser.rb +160 -0
  90. data/lib/right_agent/scripts/log_level_manager.rb +192 -0
  91. data/lib/right_agent/scripts/stats_manager.rb +268 -0
  92. data/lib/right_agent/scripts/usage.rb +58 -0
  93. data/lib/right_agent/secure_identity.rb +92 -0
  94. data/lib/right_agent/security.rb +32 -0
  95. data/lib/right_agent/security/cached_certificate_store_proxy.rb +77 -0
  96. data/lib/right_agent/security/certificate.rb +102 -0
  97. data/lib/right_agent/security/certificate_cache.rb +89 -0
  98. data/lib/right_agent/security/distinguished_name.rb +56 -0
  99. data/lib/right_agent/security/encrypted_document.rb +83 -0
  100. data/lib/right_agent/security/rsa_key_pair.rb +76 -0
  101. data/lib/right_agent/security/signature.rb +86 -0
  102. data/lib/right_agent/security/static_certificate_store.rb +85 -0
  103. data/lib/right_agent/sender.rb +792 -0
  104. data/lib/right_agent/serialize.rb +29 -0
  105. data/lib/right_agent/serialize/message_pack.rb +107 -0
  106. data/lib/right_agent/serialize/secure_serializer.rb +151 -0
  107. data/lib/right_agent/serialize/secure_serializer_initializer.rb +47 -0
  108. data/lib/right_agent/serialize/serializable.rb +151 -0
  109. data/lib/right_agent/serialize/serializer.rb +159 -0
  110. data/lib/right_agent/subprocess.rb +38 -0
  111. data/lib/right_agent/tracer.rb +124 -0
  112. data/right_agent.gemspec +101 -0
  113. data/spec/actor_registry_spec.rb +80 -0
  114. data/spec/actor_spec.rb +162 -0
  115. data/spec/agent_config_spec.rb +235 -0
  116. data/spec/agent_identity_spec.rb +78 -0
  117. data/spec/agent_spec.rb +734 -0
  118. data/spec/agent_tag_manager_spec.rb +319 -0
  119. data/spec/clients/api_client_spec.rb +423 -0
  120. data/spec/clients/auth_client_spec.rb +272 -0
  121. data/spec/clients/balanced_http_client_spec.rb +576 -0
  122. data/spec/clients/base_retry_client_spec.rb +635 -0
  123. data/spec/clients/router_client_spec.rb +594 -0
  124. data/spec/clients/spec_helper.rb +111 -0
  125. data/spec/command/agent_manager_commands_spec.rb +51 -0
  126. data/spec/command/command_io_spec.rb +93 -0
  127. data/spec/command/command_parser_spec.rb +79 -0
  128. data/spec/command/command_runner_spec.rb +107 -0
  129. data/spec/command/command_serializer_spec.rb +51 -0
  130. data/spec/connectivity_checker_spec.rb +83 -0
  131. data/spec/core_payload_types/dev_repositories_spec.rb +64 -0
  132. data/spec/core_payload_types/dev_repository_spec.rb +33 -0
  133. data/spec/core_payload_types/executable_bundle_spec.rb +67 -0
  134. data/spec/core_payload_types/login_user_spec.rb +102 -0
  135. data/spec/core_payload_types/recipe_instantiation_spec.rb +81 -0
  136. data/spec/core_payload_types/right_script_attachment_spec.rb +65 -0
  137. data/spec/core_payload_types/right_script_instantiation_spec.rb +79 -0
  138. data/spec/core_payload_types/spec_helper.rb +23 -0
  139. data/spec/dispatched_cache_spec.rb +136 -0
  140. data/spec/dispatcher_spec.rb +324 -0
  141. data/spec/enrollment_result_spec.rb +53 -0
  142. data/spec/history_spec.rb +246 -0
  143. data/spec/log_spec.rb +192 -0
  144. data/spec/monkey_patches/eventmachine_spec.rb +62 -0
  145. data/spec/multiplexer_spec.rb +48 -0
  146. data/spec/offline_handler_spec.rb +340 -0
  147. data/spec/operation_result_spec.rb +208 -0
  148. data/spec/packets_spec.rb +461 -0
  149. data/spec/pending_requests_spec.rb +136 -0
  150. data/spec/platform/spec_helper.rb +216 -0
  151. data/spec/platform/unix/darwin/platform_spec.rb +181 -0
  152. data/spec/platform/unix/linux/platform_spec.rb +540 -0
  153. data/spec/platform/unix/spec_helper.rb +149 -0
  154. data/spec/platform/windows/mingw/platform_spec.rb +222 -0
  155. data/spec/platform/windows/mswin/platform_spec.rb +259 -0
  156. data/spec/platform/windows/spec_helper.rb +720 -0
  157. data/spec/retryable_request_spec.rb +306 -0
  158. data/spec/secure_identity_spec.rb +50 -0
  159. data/spec/security/cached_certificate_store_proxy_spec.rb +62 -0
  160. data/spec/security/certificate_cache_spec.rb +71 -0
  161. data/spec/security/certificate_spec.rb +49 -0
  162. data/spec/security/distinguished_name_spec.rb +46 -0
  163. data/spec/security/encrypted_document_spec.rb +55 -0
  164. data/spec/security/rsa_key_pair_spec.rb +55 -0
  165. data/spec/security/signature_spec.rb +66 -0
  166. data/spec/security/static_certificate_store_spec.rb +58 -0
  167. data/spec/sender_spec.rb +1045 -0
  168. data/spec/serialize/message_pack_spec.rb +131 -0
  169. data/spec/serialize/secure_serializer_spec.rb +132 -0
  170. data/spec/serialize/serializable_spec.rb +90 -0
  171. data/spec/serialize/serializer_spec.rb +197 -0
  172. data/spec/spec.opts +2 -0
  173. data/spec/spec.win32.opts +1 -0
  174. data/spec/spec_helper.rb +130 -0
  175. data/spec/tracer_spec.rb +114 -0
  176. metadata +447 -0
@@ -0,0 +1,221 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ require 'rubygems'
4
+
5
+ require 'openssl'
6
+ require 'base64'
7
+ require 'json'
8
+
9
+ # A response to a RightNet enrollment request containing the router's X509 cert,
10
+ # the instance (or other) agent's identity cert, and the agent's private key.
11
+ # Responses are encrypted using a secret key shared between the instance and
12
+ # the Certifying Authority (aka the RightScale core site) and integrity-protected
13
+ # using a similar key.
14
+ module RightScale
15
+
16
+ class EnrollmentResult
17
+
18
+ include ProtocolVersionMixin
19
+
20
+ # Versions 5 and above use an identical format for the enrollment result
21
+ SUPPORTED_VERSIONS = 5..AgentConfig.protocol_version
22
+
23
+ class IntegrityFailure < Exception; end
24
+ class VersionError < Exception; end
25
+
26
+ attr_reader :r_s_version, :timestamp, :router_cert, :id_cert, :id_key
27
+
28
+ # Create a new instance of this class
29
+ #
30
+ # === Parameters
31
+ # timestamp(Time):: Timestamp associated with this result
32
+ # router_cert(String):: Arbitrary string
33
+ # id_cert(String):: Arbitrary string
34
+ # id_key(String):: Arbitrary string
35
+ # secret(String):: Shared secret with which the result is encrypted
36
+ #
37
+ def initialize(r_s_version, timestamp, router_cert, id_cert, id_key, secret)
38
+ @r_s_version = r_s_version
39
+ @timestamp = timestamp.utc
40
+ @router_cert = router_cert
41
+ @id_cert = id_cert
42
+ @id_key = id_key
43
+ @serializer = Serializer.new(can_handle_msgpack_result?(r_s_version) ? :msgpack : :json)
44
+
45
+ cert_name = can_handle_http?(r_s_version) ? 'router_cert' : 'mapper_cert'
46
+ msg = @serializer.dump({
47
+ cert_name => @router_cert.to_s,
48
+ 'id_cert' => @id_cert,
49
+ 'id_key' => @id_key
50
+ })
51
+
52
+ key = EnrollmentResult.derive_key(secret, @timestamp.to_i.to_s)
53
+ #TODO switch to new OpenSSL API once we move to Ruby 1.8.7
54
+ #cipher = OpenSSL::Cipher.new("aes-256-cbc")
55
+ cipher = OpenSSL::Cipher::Cipher.new("aes-256-cbc")
56
+ cipher.encrypt
57
+ cipher.key = key
58
+ cipher.iv = @iv = cipher.random_iv
59
+
60
+ @ciphertext = cipher.update(msg) + cipher.final
61
+
62
+ key = EnrollmentResult.derive_key(secret.to_s.reverse, @timestamp.to_i.to_s)
63
+ hmac = OpenSSL::HMAC.new(key, OpenSSL::Digest::SHA1.new)
64
+ hmac.update(@ciphertext)
65
+ @mac = hmac.digest
66
+ end
67
+
68
+ # Serialize an enrollment result
69
+ #
70
+ # === Parameters
71
+ # obj(EnrollmentResult):: Object to serialize
72
+ #
73
+ # === Return
74
+ # (String):: Serialized object
75
+ #
76
+ def to_s
77
+ @serializer.dump({
78
+ 'r_s_version' => @r_s_version.to_s,
79
+ 'timestamp' => @timestamp.to_i.to_s,
80
+ 'iv' => Base64::encode64(@iv).chop,
81
+ 'ciphertext' => Base64::encode64(@ciphertext).chop,
82
+ 'mac' => Base64::encode64(@mac).chop
83
+ })
84
+ end
85
+
86
+ # Compare this object to another one. Two results are equal if they have the same
87
+ # payload (certs and keys) and timestamp. The crypto fields are not included in
88
+ # the comparison because they are mutable.
89
+ #
90
+ # === Parameters
91
+ # o(EnrollmentResult):: Object to compare against
92
+ #
93
+ # === Return
94
+ # true|false:: Whether the objects' pertinent fields are identical
95
+ #
96
+ def ==(o)
97
+ self.router_cert == o.router_cert &&
98
+ self.id_cert == o.id_cert &&
99
+ self.id_key == o.id_key &&
100
+ self.timestamp.to_i == o.timestamp.to_i
101
+ end
102
+
103
+ # Serialize an enrollment result
104
+ #
105
+ # === Parameters
106
+ # obj(EnrollmentResult):: Object to serialize
107
+ #
108
+ # === Return
109
+ # (String):: Serialized object
110
+ #
111
+ def self.dump(obj)
112
+ raise VersionError.new("Unsupported version #{obj.r_s_version}") unless SUPPORTED_VERSIONS.include?(obj.r_s_version)
113
+ obj.to_s
114
+ end
115
+
116
+ # Unserialize the MessagePack encoded enrollment result
117
+ #
118
+ # === Parameters
119
+ # string(String):: MessagePack representation of the result
120
+ # secret(String):: Shared secret with which the result is encrypted
121
+ #
122
+ # === Return
123
+ # result:: An instance of EnrollmentResult
124
+ #
125
+ # === Raise
126
+ # IntegrityFailure:: if the message has been tampered with
127
+ # VersionError:: if the specified protocol version is not locally supported
128
+ #
129
+ def self.load(string, secret)
130
+ serializer = Serializer.new
131
+ envelope = serializer.load(string)
132
+
133
+ r_s_version = envelope['r_s_version'].to_i
134
+ raise VersionError.new("Unsupported version #{r_s_version}") unless SUPPORTED_VERSIONS.include?(r_s_version)
135
+
136
+ timestamp = Time.at(envelope['timestamp'].to_i)
137
+ iv = Base64::decode64(envelope['iv'])
138
+ ciphertext = Base64::decode64(envelope['ciphertext'])
139
+ mac = Base64::decode64(envelope['mac'])
140
+
141
+ key = self.derive_key(secret, timestamp.to_i.to_s)
142
+ #TODO switch to new OpenSSL API once we move to Ruby 1.8.7
143
+ #cipher = OpenSSL::Cipher.new("aes-256-cbc")
144
+ cipher = OpenSSL::Cipher::Cipher.new("aes-256-cbc")
145
+ cipher.decrypt
146
+ cipher.key = key
147
+ cipher.iv = iv
148
+
149
+ #TODO exclusively use new OpenSSL API (OpenSSL::CipherError) once we move to Ruby 1.8.7
150
+ if defined?(OpenSSL::Cipher::CipherError)
151
+ begin
152
+ plaintext = cipher.update(ciphertext) + cipher.final
153
+ rescue OpenSSL::Cipher::CipherError => e
154
+ raise IntegrityFailure.new(e.message)
155
+ end
156
+ else
157
+ begin
158
+ plaintext = cipher.update(ciphertext) + cipher.final
159
+ rescue OpenSSL::CipherError => e
160
+ raise IntegrityFailure.new(e.message)
161
+ end
162
+ end
163
+
164
+ key = self.derive_key(secret.to_s.reverse, timestamp.to_i.to_s)
165
+ hmac = OpenSSL::HMAC.new(key, OpenSSL::Digest::SHA1.new)
166
+ hmac.update(ciphertext)
167
+ my_mac = hmac.digest
168
+ raise IntegrityFailure.new("MAC mismatch: expected #{my_mac}, got #{mac}") unless (mac == my_mac)
169
+
170
+ msg = serializer.load(plaintext)
171
+ router_cert = msg['router_cert'] || msg['mapper_cert']
172
+ id_cert = msg['id_cert']
173
+ id_key = msg['id_key']
174
+
175
+ self.new(r_s_version, timestamp, router_cert, id_cert, id_key, secret)
176
+ end
177
+
178
+ protected
179
+
180
+ def self.derive_key(secret, salt)
181
+ begin
182
+ return OpenSSL::PKCS5.pbkdf2_hmac_sha1(secret, salt, 2048, 32)
183
+ rescue NameError => e
184
+ return pkcs5_pbkdf2_hmac_sha1(secret, salt, 2048, 32)
185
+ end
186
+ end
187
+
188
+ def self.pkcs5_pbkdf2_hmac_sha1(pass, salt, iter, len)
189
+ ret = ''
190
+ i = 0
191
+
192
+ while len > 0
193
+ i += 1
194
+ hmac = OpenSSL::HMAC.new(pass, OpenSSL::Digest::SHA1.new)
195
+ hmac.update(salt)
196
+ hmac.update([i].pack('N'))
197
+ digtmp = hmac.digest
198
+
199
+ cplen = len > digtmp.length ? digtmp.length : len
200
+
201
+ tmp = digtmp.dup
202
+
203
+ 1.upto(iter - 1) do |j|
204
+ hmac = OpenSSL::HMAC.new(pass, OpenSSL::Digest::SHA1.new)
205
+ hmac.update(digtmp)
206
+ digtmp = hmac.digest
207
+ 0.upto(cplen - 1) do |k|
208
+ tmp[k] = (tmp[k] ^ digtmp[k]).chr
209
+ end
210
+ end
211
+
212
+ tmp.slice!((cplen)..-1) if (tmp.length > cplen)
213
+ ret << tmp
214
+ len -= tmp.length
215
+ end
216
+
217
+ ret
218
+ end
219
+
220
+ end
221
+ end
@@ -0,0 +1,87 @@
1
+ #
2
+ # Copyright (c) 2009-2013 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
+ module RightScale
24
+
25
+ class Exceptions
26
+
27
+ # Base exception for use in nesting exceptions
28
+ class NestedException < StandardError
29
+ attr_reader :nested_exception
30
+
31
+ # Exception message and optional nested exception or string
32
+ def initialize(message, nested_exception = nil)
33
+ @nested_exception = nested_exception
34
+ super(message)
35
+ end
36
+ end
37
+
38
+ # Internal application error
39
+ class Application < StandardError; end
40
+
41
+ # Agent command IO error
42
+ class IO < RuntimeError; end
43
+
44
+ # Agent compute platform error
45
+ class PlatformError < StandardError; end
46
+
47
+ # Terminating service
48
+ class Terminating < RuntimeError; end
49
+
50
+ # Not authorized to make request
51
+ class Unauthorized < NestedException
52
+ def initialize(message, nested_exception = nil)
53
+ super(message, nested_exception)
54
+ end
55
+ end
56
+
57
+ # Cannot connect to service, lost connection to it, or it is out of service or too busy to respond
58
+ class ConnectivityFailure < NestedException
59
+ def initialize(message, nested_exception = nil)
60
+ super(message, nested_exception)
61
+ end
62
+ end
63
+
64
+ # Request failed but potentially will succeed if retried
65
+ class RetryableError < NestedException
66
+ def initialize(message, nested_exception = nil)
67
+ super(message, nested_exception)
68
+ end
69
+ end
70
+
71
+ # Database query failed
72
+ class QueryFailure < NestedException
73
+ def initialize(message, nested_exception = nil)
74
+ super(message, nested_exception)
75
+ end
76
+ end
77
+
78
+ # Error internal to specified server
79
+ class InternalServerError < NestedException
80
+ attr_reader :server
81
+ def initialize(message, server, nested_exception = nil)
82
+ @server = server
83
+ super(message, nested_exception)
84
+ end
85
+ end
86
+ end
87
+ end
@@ -0,0 +1,145 @@
1
+ #
2
+ # Copyright (c) 2009-2012 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 'fileutils'
24
+
25
+ module RightScale
26
+
27
+ # Agent history manager
28
+ class History
29
+
30
+ # Initialize history
31
+ #
32
+ # === Parameters
33
+ # identity(String):: Serialized agent identity
34
+ # pid(Integer):: Process ID of agent, defaults to ID if current process
35
+ def initialize(identity, pid = nil)
36
+ @pid = pid || Process.pid
37
+ @history = File.join(AgentConfig.pid_dir, identity + ".history")
38
+ end
39
+
40
+ # Append event to history file
41
+ #
42
+ # === Parameters
43
+ # event(Object):: Event to be stored in the form String or {String => Object},
44
+ # where String is the event name and Object is any associated JSON-encodable data
45
+ #
46
+ # === Return
47
+ # true:: Always return true
48
+ def update(event)
49
+ @last_update = {:time => Time.now.to_i, :pid => @pid, :event => event}
50
+ FileUtils.mkdir_p(File.dirname(@history)) unless File.exists?(File.dirname(@history))
51
+ File.open(@history, "a") { |f| f.puts @last_update.to_json }
52
+ true
53
+ end
54
+
55
+ # Load events from history file
56
+ #
57
+ # === Return
58
+ # events(Array):: List of historical events with each being a hash of
59
+ # :time(Integer):: Time in seconds in Unix-epoch when event occurred
60
+ # :pid(Integer):: Process id of agent recording the event
61
+ # :event(Object):: Event object in the form String or {String => Object},
62
+ # where String is the event name and Object is any associated JSON-encodable data
63
+ def load
64
+ events = []
65
+ File.open(@history, "r") { |f| events = f.readlines.map { |l| JSON.load(l) } } if File.readable?(@history)
66
+ events
67
+ end
68
+
69
+ # Analyze history to determine service attributes like uptime and restart/crash counts
70
+ #
71
+ # === Return
72
+ # (Hash):: Results of analysis
73
+ # :uptime(Integer):: Current time in service
74
+ # :total_uptime(Integer):: Total time in service (but if there were crashes
75
+ # this total includes recovery time, which makes it inaccurate)
76
+ # :restarts(Integer|nil):: Number of restarts, if any
77
+ # :graceful_exits(Integer|nil):: Number of graceful terminations, if any
78
+ # :crashes(Integer|nil):: Number of crashes, if any
79
+ # :last_crash_time(Integer|nil):: Time in seconds in Unix-epoch when last crash occurred, if any
80
+ # :crashed_last(Boolean):: Whether crashed last time it was started
81
+ def analyze_service
82
+ now = Time.now.to_i
83
+ if @last_analysis && @last_event == @last_update
84
+ delta = now - @last_analysis_time
85
+ @last_analysis[:uptime] += delta
86
+ @last_analysis[:total_uptime] += delta
87
+ else
88
+ last_run = last_crash = @last_event = {:time => 0, :pid => 0, :event => nil}
89
+ restarts = graceful_exits = crashes = accumulated_uptime = 0
90
+ crashed_last = false
91
+ load.each do |event|
92
+ event = SerializationHelper.symbolize_keys(event)
93
+ case event[:event]
94
+ when "start"
95
+ case @last_event[:event]
96
+ when "stop", "graceful exit"
97
+ restarts += 1
98
+ when "start"
99
+ crashes += 1
100
+ last_crash = event
101
+ crashed_last = true
102
+ when "run"
103
+ crashes += 1
104
+ last_crash = event
105
+ crashed_last = true
106
+ # Accumulating uptime here although this will wrongly include recovery time
107
+ accumulated_uptime += (event[:time] - @last_event[:time])
108
+ end
109
+ when "run"
110
+ last_run = event
111
+ when "stop"
112
+ crashed_last = false
113
+ if @last_event[:event] == "run" && @last_event[:pid] == event[:pid]
114
+ accumulated_uptime += (event[:time] - @last_event[:time])
115
+ end
116
+ when "graceful exit"
117
+ crashed_last = false
118
+ graceful_exits += 1
119
+ else
120
+ next
121
+ end
122
+ @last_event = event
123
+ end
124
+ current_uptime = last_run[:pid] == @pid ? (now - last_run[:time]) : 0
125
+ @last_analysis = {
126
+ :uptime => current_uptime,
127
+ :total_uptime => accumulated_uptime + current_uptime
128
+ }
129
+ if restarts > 0
130
+ @last_analysis[:restarts] = restarts
131
+ @last_analysis[:graceful_exits] = graceful_exits
132
+ end
133
+ if crashes > 0
134
+ @last_analysis[:crashes] = crashes
135
+ @last_analysis[:last_crash_time] = last_crash[:time]
136
+ @last_analysis[:crashed_last] = crashed_last
137
+ end
138
+ end
139
+ @last_analysis_time = now
140
+ @last_analysis
141
+ end
142
+
143
+ end # History
144
+
145
+ end # RightScale