right_agent 2.0.7-x86-mingw32

Sign up to get free protection for your applications and to get access to all the features.
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