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.
- data/LICENSE +20 -0
- data/README.rdoc +82 -0
- data/Rakefile +113 -0
- data/lib/right_agent.rb +59 -0
- data/lib/right_agent/actor.rb +182 -0
- data/lib/right_agent/actor_registry.rb +76 -0
- data/lib/right_agent/actors/agent_manager.rb +232 -0
- data/lib/right_agent/agent.rb +1149 -0
- data/lib/right_agent/agent_config.rb +480 -0
- data/lib/right_agent/agent_identity.rb +210 -0
- data/lib/right_agent/agent_tag_manager.rb +237 -0
- data/lib/right_agent/audit_formatter.rb +107 -0
- data/lib/right_agent/clients.rb +31 -0
- data/lib/right_agent/clients/api_client.rb +383 -0
- data/lib/right_agent/clients/auth_client.rb +247 -0
- data/lib/right_agent/clients/balanced_http_client.rb +369 -0
- data/lib/right_agent/clients/base_retry_client.rb +495 -0
- data/lib/right_agent/clients/right_http_client.rb +279 -0
- data/lib/right_agent/clients/router_client.rb +493 -0
- data/lib/right_agent/command.rb +30 -0
- data/lib/right_agent/command/agent_manager_commands.rb +150 -0
- data/lib/right_agent/command/command_client.rb +136 -0
- data/lib/right_agent/command/command_constants.rb +33 -0
- data/lib/right_agent/command/command_io.rb +126 -0
- data/lib/right_agent/command/command_parser.rb +87 -0
- data/lib/right_agent/command/command_runner.rb +118 -0
- data/lib/right_agent/command/command_serializer.rb +63 -0
- data/lib/right_agent/connectivity_checker.rb +179 -0
- data/lib/right_agent/console.rb +65 -0
- data/lib/right_agent/core_payload_types.rb +44 -0
- data/lib/right_agent/core_payload_types/cookbook.rb +61 -0
- data/lib/right_agent/core_payload_types/cookbook_position.rb +46 -0
- data/lib/right_agent/core_payload_types/cookbook_repository.rb +116 -0
- data/lib/right_agent/core_payload_types/cookbook_sequence.rb +70 -0
- data/lib/right_agent/core_payload_types/dev_repositories.rb +100 -0
- data/lib/right_agent/core_payload_types/dev_repository.rb +76 -0
- data/lib/right_agent/core_payload_types/event_categories.rb +38 -0
- data/lib/right_agent/core_payload_types/executable_bundle.rb +130 -0
- data/lib/right_agent/core_payload_types/login_policy.rb +72 -0
- data/lib/right_agent/core_payload_types/login_user.rb +79 -0
- data/lib/right_agent/core_payload_types/planned_volume.rb +94 -0
- data/lib/right_agent/core_payload_types/recipe_instantiation.rb +73 -0
- data/lib/right_agent/core_payload_types/repositories_bundle.rb +50 -0
- data/lib/right_agent/core_payload_types/right_script_attachment.rb +95 -0
- data/lib/right_agent/core_payload_types/right_script_instantiation.rb +94 -0
- data/lib/right_agent/core_payload_types/runlist_policy.rb +44 -0
- data/lib/right_agent/core_payload_types/secure_document.rb +66 -0
- data/lib/right_agent/core_payload_types/secure_document_location.rb +63 -0
- data/lib/right_agent/core_payload_types/software_repository_instantiation.rb +61 -0
- data/lib/right_agent/daemonize.rb +35 -0
- data/lib/right_agent/dispatched_cache.rb +109 -0
- data/lib/right_agent/dispatcher.rb +272 -0
- data/lib/right_agent/enrollment_result.rb +221 -0
- data/lib/right_agent/exceptions.rb +87 -0
- data/lib/right_agent/history.rb +145 -0
- data/lib/right_agent/log.rb +460 -0
- data/lib/right_agent/minimal.rb +46 -0
- data/lib/right_agent/monkey_patches.rb +30 -0
- data/lib/right_agent/monkey_patches/ruby_patch.rb +55 -0
- data/lib/right_agent/monkey_patches/ruby_patch/array_patch.rb +29 -0
- data/lib/right_agent/monkey_patches/ruby_patch/darwin_patch.rb +24 -0
- data/lib/right_agent/monkey_patches/ruby_patch/linux_patch.rb +24 -0
- data/lib/right_agent/monkey_patches/ruby_patch/linux_patch/file_patch.rb +30 -0
- data/lib/right_agent/monkey_patches/ruby_patch/object_patch.rb +49 -0
- data/lib/right_agent/monkey_patches/ruby_patch/windows_patch.rb +32 -0
- data/lib/right_agent/monkey_patches/ruby_patch/windows_patch/file_patch.rb +60 -0
- data/lib/right_agent/monkey_patches/ruby_patch/windows_patch/process_patch.rb +63 -0
- data/lib/right_agent/monkey_patches/ruby_patch/windows_patch/stdio_patch.rb +27 -0
- data/lib/right_agent/monkey_patches/ruby_patch/windows_patch/time_patch.rb +55 -0
- data/lib/right_agent/monkey_patches/ruby_patch/windows_patch/win32ole_patch.rb +34 -0
- data/lib/right_agent/multiplexer.rb +102 -0
- data/lib/right_agent/offline_handler.rb +270 -0
- data/lib/right_agent/operation_result.rb +300 -0
- data/lib/right_agent/packets.rb +673 -0
- data/lib/right_agent/payload_formatter.rb +104 -0
- data/lib/right_agent/pending_requests.rb +128 -0
- data/lib/right_agent/pid_file.rb +159 -0
- data/lib/right_agent/platform.rb +770 -0
- data/lib/right_agent/platform/unix/darwin/platform.rb +102 -0
- data/lib/right_agent/platform/unix/linux/platform.rb +305 -0
- data/lib/right_agent/platform/unix/platform.rb +226 -0
- data/lib/right_agent/platform/windows/mingw/platform.rb +447 -0
- data/lib/right_agent/platform/windows/mswin/platform.rb +236 -0
- data/lib/right_agent/platform/windows/platform.rb +1808 -0
- data/lib/right_agent/protocol_version_mixin.rb +69 -0
- data/lib/right_agent/retryable_request.rb +195 -0
- data/lib/right_agent/scripts/agent_controller.rb +543 -0
- data/lib/right_agent/scripts/agent_deployer.rb +400 -0
- data/lib/right_agent/scripts/common_parser.rb +160 -0
- data/lib/right_agent/scripts/log_level_manager.rb +192 -0
- data/lib/right_agent/scripts/stats_manager.rb +268 -0
- data/lib/right_agent/scripts/usage.rb +58 -0
- data/lib/right_agent/secure_identity.rb +92 -0
- data/lib/right_agent/security.rb +32 -0
- data/lib/right_agent/security/cached_certificate_store_proxy.rb +77 -0
- data/lib/right_agent/security/certificate.rb +102 -0
- data/lib/right_agent/security/certificate_cache.rb +89 -0
- data/lib/right_agent/security/distinguished_name.rb +56 -0
- data/lib/right_agent/security/encrypted_document.rb +83 -0
- data/lib/right_agent/security/rsa_key_pair.rb +76 -0
- data/lib/right_agent/security/signature.rb +86 -0
- data/lib/right_agent/security/static_certificate_store.rb +85 -0
- data/lib/right_agent/sender.rb +792 -0
- data/lib/right_agent/serialize.rb +29 -0
- data/lib/right_agent/serialize/message_pack.rb +107 -0
- data/lib/right_agent/serialize/secure_serializer.rb +151 -0
- data/lib/right_agent/serialize/secure_serializer_initializer.rb +47 -0
- data/lib/right_agent/serialize/serializable.rb +151 -0
- data/lib/right_agent/serialize/serializer.rb +159 -0
- data/lib/right_agent/subprocess.rb +38 -0
- data/lib/right_agent/tracer.rb +124 -0
- data/right_agent.gemspec +101 -0
- data/spec/actor_registry_spec.rb +80 -0
- data/spec/actor_spec.rb +162 -0
- data/spec/agent_config_spec.rb +235 -0
- data/spec/agent_identity_spec.rb +78 -0
- data/spec/agent_spec.rb +734 -0
- data/spec/agent_tag_manager_spec.rb +319 -0
- data/spec/clients/api_client_spec.rb +423 -0
- data/spec/clients/auth_client_spec.rb +272 -0
- data/spec/clients/balanced_http_client_spec.rb +576 -0
- data/spec/clients/base_retry_client_spec.rb +635 -0
- data/spec/clients/router_client_spec.rb +594 -0
- data/spec/clients/spec_helper.rb +111 -0
- data/spec/command/agent_manager_commands_spec.rb +51 -0
- data/spec/command/command_io_spec.rb +93 -0
- data/spec/command/command_parser_spec.rb +79 -0
- data/spec/command/command_runner_spec.rb +107 -0
- data/spec/command/command_serializer_spec.rb +51 -0
- data/spec/connectivity_checker_spec.rb +83 -0
- data/spec/core_payload_types/dev_repositories_spec.rb +64 -0
- data/spec/core_payload_types/dev_repository_spec.rb +33 -0
- data/spec/core_payload_types/executable_bundle_spec.rb +67 -0
- data/spec/core_payload_types/login_user_spec.rb +102 -0
- data/spec/core_payload_types/recipe_instantiation_spec.rb +81 -0
- data/spec/core_payload_types/right_script_attachment_spec.rb +65 -0
- data/spec/core_payload_types/right_script_instantiation_spec.rb +79 -0
- data/spec/core_payload_types/spec_helper.rb +23 -0
- data/spec/dispatched_cache_spec.rb +136 -0
- data/spec/dispatcher_spec.rb +324 -0
- data/spec/enrollment_result_spec.rb +53 -0
- data/spec/history_spec.rb +246 -0
- data/spec/log_spec.rb +192 -0
- data/spec/monkey_patches/eventmachine_spec.rb +62 -0
- data/spec/multiplexer_spec.rb +48 -0
- data/spec/offline_handler_spec.rb +340 -0
- data/spec/operation_result_spec.rb +208 -0
- data/spec/packets_spec.rb +461 -0
- data/spec/pending_requests_spec.rb +136 -0
- data/spec/platform/spec_helper.rb +216 -0
- data/spec/platform/unix/darwin/platform_spec.rb +181 -0
- data/spec/platform/unix/linux/platform_spec.rb +540 -0
- data/spec/platform/unix/spec_helper.rb +149 -0
- data/spec/platform/windows/mingw/platform_spec.rb +222 -0
- data/spec/platform/windows/mswin/platform_spec.rb +259 -0
- data/spec/platform/windows/spec_helper.rb +720 -0
- data/spec/retryable_request_spec.rb +306 -0
- data/spec/secure_identity_spec.rb +50 -0
- data/spec/security/cached_certificate_store_proxy_spec.rb +62 -0
- data/spec/security/certificate_cache_spec.rb +71 -0
- data/spec/security/certificate_spec.rb +49 -0
- data/spec/security/distinguished_name_spec.rb +46 -0
- data/spec/security/encrypted_document_spec.rb +55 -0
- data/spec/security/rsa_key_pair_spec.rb +55 -0
- data/spec/security/signature_spec.rb +66 -0
- data/spec/security/static_certificate_store_spec.rb +58 -0
- data/spec/sender_spec.rb +1045 -0
- data/spec/serialize/message_pack_spec.rb +131 -0
- data/spec/serialize/secure_serializer_spec.rb +132 -0
- data/spec/serialize/serializable_spec.rb +90 -0
- data/spec/serialize/serializer_spec.rb +197 -0
- data/spec/spec.opts +2 -0
- data/spec/spec.win32.opts +1 -0
- data/spec/spec_helper.rb +130 -0
- data/spec/tracer_spec.rb +114 -0
- 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
|