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,104 @@
|
|
1
|
+
#
|
2
|
+
# Copyright (c) 2010-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
|
+
module RightScale
|
24
|
+
|
25
|
+
# Format the payload of requests and pushes for logging
|
26
|
+
# Individual agents may extend as needed
|
27
|
+
class PayloadFormatter
|
28
|
+
|
29
|
+
# Retrieve info log message for given request type and payload
|
30
|
+
#
|
31
|
+
# === Parameters
|
32
|
+
# type(String):: Request type
|
33
|
+
# payload(Hash):: Request payload
|
34
|
+
#
|
35
|
+
# === Return
|
36
|
+
# msg(String|NilClass):: Message to be logged or nil (don't log)
|
37
|
+
def self.log(type, payload)
|
38
|
+
@formatter ||= new
|
39
|
+
parts = type.split('/')
|
40
|
+
meth = "#{parts[1]}_#{parts[2]}".to_sym
|
41
|
+
res = nil
|
42
|
+
res = @formatter.send(meth, payload) if @formatter.respond_to?(meth)
|
43
|
+
res
|
44
|
+
end
|
45
|
+
|
46
|
+
protected
|
47
|
+
|
48
|
+
# booter/declare request log message
|
49
|
+
# Payload :
|
50
|
+
# { :agent_identity => ..., :r_s_version => ..., :resource_uid => ... }
|
51
|
+
#
|
52
|
+
# === Parameters
|
53
|
+
# payload(Hash):: Request payload
|
54
|
+
#
|
55
|
+
# === Return
|
56
|
+
# true:: Always return true
|
57
|
+
def booter_declare(payload)
|
58
|
+
msg = get(payload, :resource_uid)
|
59
|
+
end
|
60
|
+
|
61
|
+
# forwarder/schedule_right_script request log message
|
62
|
+
# Payload :
|
63
|
+
# { :audit_id => ..., :token_id => ..., :agent_identity => ..., account_id => ...,
|
64
|
+
# :right_script_id => ..., :right_script => ..., :arguments => ... }
|
65
|
+
#
|
66
|
+
# === Parameters
|
67
|
+
# payload(Hash):: Request payload
|
68
|
+
#
|
69
|
+
# === Return
|
70
|
+
# true:: Always return true
|
71
|
+
def forwarder_schedule_right_script(payload)
|
72
|
+
msg = get(payload, :right_script) || "RightScript #{get(payload, :right_script_id)}"
|
73
|
+
end
|
74
|
+
|
75
|
+
# forwarder/schedule_recipe request log message
|
76
|
+
# Payload :
|
77
|
+
# { :audit_id => ..., :token_id => ..., :agent_identity => ..., account_id => ...,
|
78
|
+
# :recipe_id => ..., :recipe => ..., :arguments => ..., :json => ... }
|
79
|
+
#
|
80
|
+
# === Parameters
|
81
|
+
# payload(Hash):: Request payload
|
82
|
+
#
|
83
|
+
# === Return
|
84
|
+
# true:: Always return true
|
85
|
+
def forwarder_schedule_recipe(payload)
|
86
|
+
msg = get(payload, :recipe) || "recipe #{get(payload, :recipe_id)}"
|
87
|
+
end
|
88
|
+
|
89
|
+
# Access Hash element where key could be a symbol or a string
|
90
|
+
#
|
91
|
+
# === Parameters
|
92
|
+
# hash(Hash):: Hash containing element to be accessed
|
93
|
+
# key(Symbol):: Key of element to be accessed (symbol)
|
94
|
+
#
|
95
|
+
# === Return
|
96
|
+
# elem(Object):: Corresponding element or nil if not found
|
97
|
+
def get(hash, key)
|
98
|
+
elem = hash[key] || hash[key.to_s]
|
99
|
+
end
|
100
|
+
|
101
|
+
end
|
102
|
+
|
103
|
+
end
|
104
|
+
|
@@ -0,0 +1,128 @@
|
|
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
|
+
# Request that is waiting for a response
|
26
|
+
class PendingRequest
|
27
|
+
|
28
|
+
# (Symbol) Kind of request: :send_push or :send_request
|
29
|
+
attr_reader :kind
|
30
|
+
|
31
|
+
# (Time) Time when request message was received
|
32
|
+
attr_reader :receive_time
|
33
|
+
|
34
|
+
# (Proc) Block to be activated when response is received
|
35
|
+
attr_reader :response_handler
|
36
|
+
|
37
|
+
# (String) Token for parent request in a retry situation
|
38
|
+
attr_accessor :retry_parent_token
|
39
|
+
|
40
|
+
# (String) Non-delivery reason if any
|
41
|
+
attr_accessor :non_delivery
|
42
|
+
|
43
|
+
def initialize(kind, receive_time, response_handler)
|
44
|
+
@kind = kind
|
45
|
+
@receive_time = receive_time
|
46
|
+
@response_handler = response_handler
|
47
|
+
@retry_parent_token = nil
|
48
|
+
@non_delivery = nil
|
49
|
+
end
|
50
|
+
|
51
|
+
end # PendingRequest
|
52
|
+
|
53
|
+
# Cache for requests that are waiting for a response
|
54
|
+
# Automatically deletes push requests when get too old
|
55
|
+
# Retains non-push requests until explicitly deleted
|
56
|
+
class PendingRequests < Hash
|
57
|
+
|
58
|
+
# Maximum number of seconds to retain send pushes in cache
|
59
|
+
MAX_PUSH_AGE = 2 * 60
|
60
|
+
|
61
|
+
# Minimum number of seconds between push cleanups
|
62
|
+
MIN_CLEANUP_INTERVAL = 15
|
63
|
+
|
64
|
+
# Create cache
|
65
|
+
def initialize
|
66
|
+
@last_cleanup = Time.now
|
67
|
+
super
|
68
|
+
end
|
69
|
+
|
70
|
+
# Store pending request
|
71
|
+
#
|
72
|
+
# === Parameters
|
73
|
+
# token(String):: Generated message identifier
|
74
|
+
# pending_request(PendingRequest):: Pending request
|
75
|
+
#
|
76
|
+
# === Return
|
77
|
+
# (PendingRequest):: Stored request
|
78
|
+
def []=(token, pending_request)
|
79
|
+
now = Time.now
|
80
|
+
if (now - @last_cleanup) > MIN_CLEANUP_INTERVAL
|
81
|
+
self.reject! { |t, r| r.kind == :send_push && (now - r.receive_time) > MAX_PUSH_AGE }
|
82
|
+
@last_cleanup = now
|
83
|
+
end
|
84
|
+
super
|
85
|
+
end
|
86
|
+
|
87
|
+
# Select cache entries of the given kind
|
88
|
+
#
|
89
|
+
# === Parameters
|
90
|
+
# kind(Symbol):: Kind of request to be included: :send_push or :send_request
|
91
|
+
#
|
92
|
+
# === Return
|
93
|
+
# (Hash):: Requests of specified kind
|
94
|
+
def kind(kind)
|
95
|
+
self.reject { |t, r| r.kind != kind}
|
96
|
+
end
|
97
|
+
|
98
|
+
# Get age of youngest pending request
|
99
|
+
#
|
100
|
+
# === Return
|
101
|
+
# age(Integer):: Age of youngest request
|
102
|
+
def youngest_age
|
103
|
+
now = Time.now
|
104
|
+
age = nil
|
105
|
+
self.each_value do |r|
|
106
|
+
seconds = (now - r.receive_time).to_i
|
107
|
+
age = seconds if age.nil? || seconds < age
|
108
|
+
end
|
109
|
+
age
|
110
|
+
end
|
111
|
+
|
112
|
+
# Get age of oldest pending request
|
113
|
+
#
|
114
|
+
# === Return
|
115
|
+
# age(Integer):: Age of oldest request
|
116
|
+
def oldest_age
|
117
|
+
now = Time.now
|
118
|
+
age = nil
|
119
|
+
self.each_value do |r|
|
120
|
+
seconds = (now - r.receive_time).to_i
|
121
|
+
age = seconds if age.nil? || seconds > age
|
122
|
+
end
|
123
|
+
age
|
124
|
+
end
|
125
|
+
|
126
|
+
end # PendingRequests
|
127
|
+
|
128
|
+
end # RightScale
|
@@ -0,0 +1,159 @@
|
|
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
|
+
module RightScale
|
24
|
+
|
25
|
+
# Encapsulates an agent pid file
|
26
|
+
# A pid file contains three components:
|
27
|
+
# - the PID of the process running the agent
|
28
|
+
# - the port number that should be used to talk to the agent via the
|
29
|
+
# command protocol
|
30
|
+
# - the cookie used to authenticate a client talking to the agent via
|
31
|
+
# the command protocol
|
32
|
+
class PidFile
|
33
|
+
|
34
|
+
class AlreadyRunning < Exception; end
|
35
|
+
|
36
|
+
attr_reader :identity, :pid_file, :cookie_file
|
37
|
+
|
38
|
+
# Initialize pid file location from agent identity and pid directory
|
39
|
+
def initialize(identity, pid_dir = nil)
|
40
|
+
@identity = identity
|
41
|
+
@pid_dir = File.normalize_path(pid_dir || AgentConfig.pid_dir)
|
42
|
+
@pid_file = File.join(@pid_dir, "#{identity}.pid")
|
43
|
+
@cookie_file = File.join(@pid_dir, "#{identity}.cookie")
|
44
|
+
end
|
45
|
+
|
46
|
+
# Check whether pid file can be created
|
47
|
+
# Delete any existing pid file if process is not running anymore
|
48
|
+
#
|
49
|
+
# === Return
|
50
|
+
# true:: Always return true
|
51
|
+
#
|
52
|
+
# === Raise
|
53
|
+
# AlreadyRunning:: If pid file already exists and process is running
|
54
|
+
def check
|
55
|
+
if pid = read_pid[:pid]
|
56
|
+
if process_running?(pid)
|
57
|
+
raise AlreadyRunning.new("#{@pid_file} already exists and process is running (pid: #{pid})")
|
58
|
+
else
|
59
|
+
Log.info("Removing stale pid file: #{@pid_file}")
|
60
|
+
remove
|
61
|
+
end
|
62
|
+
end
|
63
|
+
true
|
64
|
+
end
|
65
|
+
|
66
|
+
# Write pid to pid file
|
67
|
+
#
|
68
|
+
# === Return
|
69
|
+
# true:: Always return true
|
70
|
+
def write
|
71
|
+
begin
|
72
|
+
FileUtils.mkdir_p(@pid_dir)
|
73
|
+
open(@pid_file,'w') { |f| f.write(Process.pid) }
|
74
|
+
File.chmod(0644, @pid_file)
|
75
|
+
rescue Exception => e
|
76
|
+
Log.error "Failed to create PID file: #{e.message}"
|
77
|
+
raise
|
78
|
+
end
|
79
|
+
true
|
80
|
+
end
|
81
|
+
|
82
|
+
# Update associated command protocol port
|
83
|
+
#
|
84
|
+
# === Parameters
|
85
|
+
# options[:listen_port](Integer):: Command protocol port to be used for this agent
|
86
|
+
# options[:cookie](String):: Cookie to be used together with command protocol
|
87
|
+
#
|
88
|
+
# === Return
|
89
|
+
# true:: Always return true
|
90
|
+
def set_command_options(options)
|
91
|
+
content = { :listen_port => options[:listen_port], :cookie => options[:cookie] }
|
92
|
+
open(@cookie_file,'w') { |f| f.write(YAML.dump(content)) }
|
93
|
+
File.chmod(0600, @cookie_file)
|
94
|
+
true
|
95
|
+
end
|
96
|
+
|
97
|
+
# Delete pid file
|
98
|
+
#
|
99
|
+
# === Return
|
100
|
+
# true:: Always return true
|
101
|
+
def remove
|
102
|
+
File.delete(@pid_file) if exists?
|
103
|
+
File.delete(@cookie_file) if File.exists?(@cookie_file)
|
104
|
+
true
|
105
|
+
end
|
106
|
+
|
107
|
+
# Read pid file content
|
108
|
+
# Empty hash if pid file does not exist or content cannot be loaded
|
109
|
+
#
|
110
|
+
# === Return
|
111
|
+
# content(Hash):: Hash containing 3 keys :pid, :cookie and :port
|
112
|
+
def read_pid
|
113
|
+
content = {}
|
114
|
+
if exists?
|
115
|
+
open(@pid_file,'r') { |f| content[:pid] = f.read.to_i }
|
116
|
+
open(@cookie_file,'r') do |f|
|
117
|
+
command_options = (YAML.load(f.read) rescue {}) || {}
|
118
|
+
content.merge!(command_options)
|
119
|
+
end if File.readable?(@cookie_file)
|
120
|
+
end
|
121
|
+
content
|
122
|
+
end
|
123
|
+
|
124
|
+
# Does pid file exist?
|
125
|
+
#
|
126
|
+
# === Return
|
127
|
+
# true:: If pid file exists
|
128
|
+
# false:: Otherwise
|
129
|
+
def exists?
|
130
|
+
File.exists?(@pid_file)
|
131
|
+
end
|
132
|
+
|
133
|
+
# Human representation
|
134
|
+
#
|
135
|
+
# === Return
|
136
|
+
# path(String):: Path to pid file
|
137
|
+
def to_s
|
138
|
+
path = @pid_file
|
139
|
+
end
|
140
|
+
|
141
|
+
private
|
142
|
+
|
143
|
+
# Check whether there is a process running with the given pid
|
144
|
+
#
|
145
|
+
# === Parameters
|
146
|
+
# pid(Integer):: PID to check
|
147
|
+
#
|
148
|
+
# === Return
|
149
|
+
# true:: If there is a process running with the given pid
|
150
|
+
# false: Otherwise
|
151
|
+
def process_running?(pid)
|
152
|
+
Process.getpgid(pid) != -1
|
153
|
+
rescue Errno::ESRCH
|
154
|
+
false
|
155
|
+
end
|
156
|
+
|
157
|
+
end # PidFile
|
158
|
+
|
159
|
+
end # RightScale
|
@@ -0,0 +1,770 @@
|
|
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
|
+
# This file may get required twice on Windows: once using long path and once
|
24
|
+
# using short path. Since this is where we define the File.normalize_path
|
25
|
+
# method to alleviate this issue, we have a chicken & egg problem. So detect if
|
26
|
+
# we already required this file and skip the rest if that was the case.
|
27
|
+
unless defined?(RightScale::Platform)
|
28
|
+
|
29
|
+
# Note that the platform-specific submodules will be loaded on demand to resolve
|
30
|
+
# some install-time gem dependency issues.
|
31
|
+
require 'rubygems'
|
32
|
+
require 'rbconfig'
|
33
|
+
require 'right_support'
|
34
|
+
|
35
|
+
require ::File.expand_path('../exceptions', __FILE__)
|
36
|
+
|
37
|
+
module RightScale
|
38
|
+
|
39
|
+
# A utility class that provides information about the platform on which the
|
40
|
+
# RightAgent is running.
|
41
|
+
#
|
42
|
+
# Available information includes:
|
43
|
+
# - which flavor cloud (EC2, Rackspace, Eucalyptus, ..)
|
44
|
+
# - which flavor operating system (Linux, Windows or Mac)
|
45
|
+
# - which OS release (a numeric value that is specific to the OS)
|
46
|
+
# - directories in which various bits of RightScale state may be found
|
47
|
+
# - platform-specific information such as Linux flavor or release
|
48
|
+
#
|
49
|
+
# For platform information only used in specific contexts, the dispatch
|
50
|
+
# method may be used.
|
51
|
+
class Platform
|
52
|
+
|
53
|
+
include RightSupport::Ruby::EasySingleton
|
54
|
+
|
55
|
+
# exceptions
|
56
|
+
class CommandError < RightScale::Exceptions::PlatformError
|
57
|
+
attr_accessor :command
|
58
|
+
attr_accessor :status
|
59
|
+
attr_accessor :output_text
|
60
|
+
end
|
61
|
+
|
62
|
+
attr_reader :flavor, :release, :codename
|
63
|
+
|
64
|
+
# Generic platform family
|
65
|
+
#
|
66
|
+
# @deprecated family is a legacy definition, see genus/species for a more
|
67
|
+
# granular definition.
|
68
|
+
#
|
69
|
+
# @return [Symbol] result as one of :linux, :windows or :darwin
|
70
|
+
def family
|
71
|
+
warn "#{self.class.name}#family is deprecated, use genus or species instead.\n#{caller[0,2].join("\n")}"
|
72
|
+
(genus == :unix) ? species : genus
|
73
|
+
end
|
74
|
+
|
75
|
+
# @return [Symbol] platform genus
|
76
|
+
def genus
|
77
|
+
resolve_taxonomy unless @genus
|
78
|
+
@genus
|
79
|
+
end
|
80
|
+
|
81
|
+
# @return [Symbol] platform species
|
82
|
+
def species
|
83
|
+
resolve_taxonomy unless @species
|
84
|
+
@species
|
85
|
+
end
|
86
|
+
|
87
|
+
# Is current platform in the Unix genus?
|
88
|
+
#
|
89
|
+
# @return [TrueClass|FalseClass] true if Unix
|
90
|
+
def unix?
|
91
|
+
genus == :unix
|
92
|
+
end
|
93
|
+
|
94
|
+
# Is current platform Linux?
|
95
|
+
#
|
96
|
+
# @return [TrueClass|FalseClass] true if Linux
|
97
|
+
def linux?
|
98
|
+
species == :linux
|
99
|
+
end
|
100
|
+
|
101
|
+
# Is current platform Darwin?
|
102
|
+
#
|
103
|
+
# @return [TrueClass|FalseClass] true if Darwin
|
104
|
+
def darwin?
|
105
|
+
species == :darwin
|
106
|
+
end
|
107
|
+
|
108
|
+
# Is current platform Windows?
|
109
|
+
#
|
110
|
+
# @return [TrueClass|FalseClass] true if Windows
|
111
|
+
def windows?
|
112
|
+
genus == :windows
|
113
|
+
end
|
114
|
+
|
115
|
+
# Are we on an EC2 cloud instance?
|
116
|
+
#
|
117
|
+
# @deprecated use right_link cloud libraries instead.
|
118
|
+
#
|
119
|
+
# @return [TrueClass|FalseClass] true if EC2
|
120
|
+
def ec2?
|
121
|
+
warn "#{self.class.name}#ec2? is deprecated, use right_link cloud libraries instead.\n#{caller[0,2].join("\n")}"
|
122
|
+
resolve_cloud_type unless @cloud_type
|
123
|
+
@cloud_type == 'ec2'
|
124
|
+
end
|
125
|
+
|
126
|
+
# Are we on an Rackspace cloud instance?
|
127
|
+
#
|
128
|
+
# @deprecated use right_link cloud libraries instead.
|
129
|
+
#
|
130
|
+
# @return [TrueClass|FalseClass] true if Rackspace
|
131
|
+
def rackspace?
|
132
|
+
warn "#{self.class.name}#rackspace? is deprecated, use right_link cloud libraries instead.\n#{caller[0,2].join("\n")}"
|
133
|
+
resolve_cloud_type unless @cloud_type
|
134
|
+
@cloud_type == 'rackspace'
|
135
|
+
end
|
136
|
+
|
137
|
+
# Are we on an Eucalyptus cloud instance?
|
138
|
+
#
|
139
|
+
# @deprecated use right_link cloud libraries instead.
|
140
|
+
#
|
141
|
+
# @return [TrueClass|FalseClass] true if Eucalyptus
|
142
|
+
def eucalyptus?
|
143
|
+
warn "#{self.class.name}#eucalyptus? is deprecated, use right_link cloud libraries instead.\n#{caller[0,2].join("\n")}"
|
144
|
+
resolve_cloud_type unless @cloud_type
|
145
|
+
@cloud_type == 'eucalyptus'
|
146
|
+
end
|
147
|
+
|
148
|
+
# Call platform specific implementation of method whose symbol is returned
|
149
|
+
# by the passed in block. Arguments are passed through.
|
150
|
+
# e.g.
|
151
|
+
#
|
152
|
+
# Platform.dispatch(2) { :echo }
|
153
|
+
#
|
154
|
+
# will result in 'echo_linux(2)' being executed in self if running on
|
155
|
+
# linux, 'echo_windows(2)' if running on Windows and 'echo_darwin(2)' if
|
156
|
+
# on Mac OS X. Note that the method is run in the instance of the caller.
|
157
|
+
#
|
158
|
+
# @param [Array] args as pass-through arguments
|
159
|
+
#
|
160
|
+
# @yield [] given block should not take any argument and return a symbol
|
161
|
+
# for the method that should be called.
|
162
|
+
#
|
163
|
+
# @return [Object] result of Platform-specific implementation
|
164
|
+
def dispatch(*args, &blk)
|
165
|
+
raise 'Platform.dispatch requires a block' unless blk
|
166
|
+
binding = blk.binding.eval('self')
|
167
|
+
meth = blk.call
|
168
|
+
target = dispatch_candidates(meth).detect do |candidate|
|
169
|
+
binding.respond_to?(candidate)
|
170
|
+
end
|
171
|
+
raise "No platform dispatch target found in #{binding.class} for " +
|
172
|
+
"'#{meth.inspect}', tried " + dispatch_candidates(meth).join(', ') unless target
|
173
|
+
binding.send(target, *args)
|
174
|
+
end
|
175
|
+
|
176
|
+
# @return [Controller] Platform-specific controller object
|
177
|
+
def controller
|
178
|
+
platform_service(:controller)
|
179
|
+
end
|
180
|
+
|
181
|
+
# @return [Filesystem] Platform-specific filesystem config object
|
182
|
+
def filesystem
|
183
|
+
platform_service(:filesystem)
|
184
|
+
end
|
185
|
+
|
186
|
+
# @return [VolumeManager] Platform-specific volume manager config object
|
187
|
+
def volume_manager
|
188
|
+
platform_service(:volume_manager)
|
189
|
+
end
|
190
|
+
|
191
|
+
# @return [Shell] Platform-specific shell information object
|
192
|
+
def shell
|
193
|
+
platform_service(:shell)
|
194
|
+
end
|
195
|
+
|
196
|
+
# @return [Rng] Platform-specific RNG object
|
197
|
+
def rng
|
198
|
+
platform_service(:rng)
|
199
|
+
end
|
200
|
+
|
201
|
+
# @return [Process] Platform-specific process facilities object
|
202
|
+
def process
|
203
|
+
platform_service(:process)
|
204
|
+
end
|
205
|
+
|
206
|
+
# @return [Installer] Platform-specific installer information object
|
207
|
+
def installer
|
208
|
+
platform_service(:installer)
|
209
|
+
end
|
210
|
+
|
211
|
+
# Blocking call to invoke a command line tool used to perform platform-
|
212
|
+
# specific tasks.
|
213
|
+
#
|
214
|
+
# Also provides a consistent interface for mocking command output during
|
215
|
+
# spec testing. Implementations should use this method instead of
|
216
|
+
# embedding popen/backtick calls to assist with testing.
|
217
|
+
#
|
218
|
+
# @param [String] command to run
|
219
|
+
# @param [Hash] options for execution
|
220
|
+
# @option options [TrueClass|FalseClass] :raise_on_failure true to raise on command failure (Default)
|
221
|
+
#
|
222
|
+
# @return [String] output from command or empty
|
223
|
+
#
|
224
|
+
# @raise [CommandError] on failure (by default)
|
225
|
+
def execute(command, options = {})
|
226
|
+
options = { :raise_on_failure => true }.merge(options)
|
227
|
+
raise_on_failure = options[:raise_on_failure]
|
228
|
+
output_text = ''
|
229
|
+
begin
|
230
|
+
output_text = `#{command}`
|
231
|
+
if !$?.success? && options[:raise_on_failure]
|
232
|
+
message = []
|
233
|
+
message << "Command failed with exit code = #{$?.exitstatus}:"
|
234
|
+
message << "> #{command}"
|
235
|
+
error_output_text = output_text.strip
|
236
|
+
message << error_output_text unless error_output_text.empty?
|
237
|
+
e = CommandError.new(message.join("\n"))
|
238
|
+
e.command = command
|
239
|
+
e.status = $?
|
240
|
+
e.output_text = output_text
|
241
|
+
raise e
|
242
|
+
end
|
243
|
+
rescue Errno::ENOENT => e
|
244
|
+
if raise_on_failure
|
245
|
+
raise CommandError, "Command failed: #{e.message}"
|
246
|
+
end
|
247
|
+
end
|
248
|
+
output_text
|
249
|
+
end
|
250
|
+
|
251
|
+
# Base class for platform helpers.
|
252
|
+
class PlatformHelperBase
|
253
|
+
|
254
|
+
private
|
255
|
+
|
256
|
+
# Convenience method for declaring must-be-overridden interfaces.
|
257
|
+
#
|
258
|
+
# @raise [NotImplementedError] always unless overridden
|
259
|
+
def must_be_overridden
|
260
|
+
raise ::NotImplementedError, 'Must be overridden'
|
261
|
+
end
|
262
|
+
|
263
|
+
# Convenience method for executing a command via platform.
|
264
|
+
#
|
265
|
+
# See Platform#execute
|
266
|
+
def execute(cmd, options = {})
|
267
|
+
::RightScale::Platform.execute(cmd, options)
|
268
|
+
end
|
269
|
+
end
|
270
|
+
|
271
|
+
# Declares various file system APIs.
|
272
|
+
class Filesystem < PlatformHelperBase
|
273
|
+
|
274
|
+
# Is given command available in the PATH?
|
275
|
+
#
|
276
|
+
# @param [String] command_name to be tested
|
277
|
+
#
|
278
|
+
# @return [TrueClass|FalseClass] true if command is in path
|
279
|
+
def has_executable_in_path(command_name)
|
280
|
+
return !!find_executable_in_path(command_name)
|
281
|
+
end
|
282
|
+
|
283
|
+
# Finds the given command name in the PATH. this emulates the 'which'
|
284
|
+
# command from linux (without the terminating newline).
|
285
|
+
#
|
286
|
+
# @param [String] command_name to be tested
|
287
|
+
#
|
288
|
+
# @return [String] path to first matching executable file in PATH or nil
|
289
|
+
def find_executable_in_path(command_name)
|
290
|
+
must_be_overridden
|
291
|
+
end
|
292
|
+
|
293
|
+
# @return [String] directory containing generated agent configuration files
|
294
|
+
def right_agent_cfg_dir
|
295
|
+
must_be_overridden
|
296
|
+
end
|
297
|
+
|
298
|
+
# @return [String] static (time-invariant) state that is common to all RightScale apps/agents
|
299
|
+
def right_scale_static_state_dir
|
300
|
+
must_be_overridden
|
301
|
+
end
|
302
|
+
|
303
|
+
# @return [String] static (time-invariant) state that is specific to RightLink
|
304
|
+
def right_link_static_state_dir
|
305
|
+
must_be_overridden
|
306
|
+
end
|
307
|
+
|
308
|
+
# @return [String] dynamic, persistent runtime state that is specific to RightLink
|
309
|
+
def right_link_dynamic_state_dir
|
310
|
+
must_be_overridden
|
311
|
+
end
|
312
|
+
|
313
|
+
# @return [String] data which is awaiting some kind of later processing
|
314
|
+
def spool_dir
|
315
|
+
must_be_overridden
|
316
|
+
end
|
317
|
+
|
318
|
+
# TEAL TODO description
|
319
|
+
def ssh_cfg_dir
|
320
|
+
must_be_overridden
|
321
|
+
end
|
322
|
+
|
323
|
+
# Cached data from applications. Such data is locally generated as a
|
324
|
+
# result of time-consuming I/O or calculation. The application must
|
325
|
+
# be able to regenerate or restore the data.
|
326
|
+
#
|
327
|
+
# @return [String] cache directory
|
328
|
+
def cache_dir
|
329
|
+
must_be_overridden
|
330
|
+
end
|
331
|
+
|
332
|
+
# @return [String] system logs
|
333
|
+
def log_dir
|
334
|
+
must_be_overridden
|
335
|
+
end
|
336
|
+
|
337
|
+
# For Unix compatibility; has no significance in Windows
|
338
|
+
#
|
339
|
+
# @return [String] source code directory, for reference purposes and for development
|
340
|
+
def source_code_dir
|
341
|
+
must_be_overridden
|
342
|
+
end
|
343
|
+
|
344
|
+
# @return [String] temporary files.
|
345
|
+
def temp_dir
|
346
|
+
must_be_overridden
|
347
|
+
end
|
348
|
+
|
349
|
+
# @return [String] path to place pid files
|
350
|
+
def pid_dir
|
351
|
+
must_be_overridden
|
352
|
+
end
|
353
|
+
|
354
|
+
# @return [String] installed home (parent of) right_link directory path
|
355
|
+
def right_link_home_dir
|
356
|
+
must_be_overridden
|
357
|
+
end
|
358
|
+
|
359
|
+
# @return [String] path to right link configuration and internal usage scripts
|
360
|
+
def private_bin_dir
|
361
|
+
must_be_overridden
|
362
|
+
end
|
363
|
+
|
364
|
+
# TEAL TODO description
|
365
|
+
def sandbox_dir
|
366
|
+
must_be_overridden
|
367
|
+
end
|
368
|
+
|
369
|
+
# Converts a long path (e.g. "C:/Program Files") to a short path
|
370
|
+
# (e.g. "C:/PROGRA~1") if necessary. See implementation for notes.
|
371
|
+
#
|
372
|
+
# For Windows compatibility; has no significance in Linux
|
373
|
+
#
|
374
|
+
# @param [String] long_path to convert
|
375
|
+
#
|
376
|
+
# @return [String] short path
|
377
|
+
def long_path_to_short_path(long_path)
|
378
|
+
must_be_overridden
|
379
|
+
end
|
380
|
+
|
381
|
+
# Converts slashes in a path to a consistent style.
|
382
|
+
#
|
383
|
+
# For Windows compatibility; has no significance in Linux
|
384
|
+
#
|
385
|
+
# @param [String] path to make pretty
|
386
|
+
# @param [String] native_fs_flag as true if path is pretty for native
|
387
|
+
# file system (i.e. file system calls more likely to succeed), false
|
388
|
+
# if pretty for Ruby interpreter (default).
|
389
|
+
#
|
390
|
+
# @return [String] pretty path
|
391
|
+
def pretty_path(path, native_fs_flag = false)
|
392
|
+
must_be_overridden
|
393
|
+
end
|
394
|
+
|
395
|
+
# Ensures a local drive location for the file or folder given by path
|
396
|
+
# by copying to a local temp directory given by name only if the item
|
397
|
+
# does not appear on the home drive. This method is useful because
|
398
|
+
# secure applications refuse to run scripts from network locations, etc.
|
399
|
+
# Replaces any similar files in given temp dir to ensure latest updates.
|
400
|
+
#
|
401
|
+
# For Windows compatibility; has no significance in Linux
|
402
|
+
#
|
403
|
+
# @param [String] path to file or directory to be placed locally
|
404
|
+
# @param [String] temp_dir_name relative to user temp_dir to use only if the file or folder is not on a local drive
|
405
|
+
#
|
406
|
+
# @return [String] local drive path
|
407
|
+
def ensure_local_drive_path(path, temp_dir_name)
|
408
|
+
must_be_overridden
|
409
|
+
end
|
410
|
+
|
411
|
+
# Creates a symlink (if supported by platform).
|
412
|
+
#
|
413
|
+
# @param [String] from_path the path to the real file/directory
|
414
|
+
# @param [String] to_path the path to the symlink to be created
|
415
|
+
#
|
416
|
+
# @return [Fixnum] always 0 as does File.symlink under Linux
|
417
|
+
#
|
418
|
+
# @raise [RightScale::Exceptions::PlatformError] on failure
|
419
|
+
def create_symlink(from_path, to_path)
|
420
|
+
must_be_overridden
|
421
|
+
end
|
422
|
+
end
|
423
|
+
|
424
|
+
# Provides utilities for managing volumes (disks).
|
425
|
+
class VolumeManager < PlatformHelperBase
|
426
|
+
|
427
|
+
# exceptions
|
428
|
+
class ParserError < ::RightScale::Exceptions::PlatformError; end
|
429
|
+
class VolumeError < ::RightScale::Exceptions::PlatformError; end
|
430
|
+
|
431
|
+
# Gets a list of currently visible volumes in the form:
|
432
|
+
# [{:device, :label, :uuid, :type, :filesystem}]
|
433
|
+
#
|
434
|
+
# @param [Hash] conditions to match, if any (Default = no conditions)
|
435
|
+
#
|
436
|
+
# @return [Array] volume info as an array of hashes or empty
|
437
|
+
#
|
438
|
+
# @raise [ParserError] on failure to parse volume list
|
439
|
+
# @raise [VolumeError] on failure to execute `blkid` to obtain raw output
|
440
|
+
def volumes(conditions = nil)
|
441
|
+
must_be_overridden
|
442
|
+
end
|
443
|
+
end # VolumeManager
|
444
|
+
|
445
|
+
# Declares various command shell APIs.
|
446
|
+
class Shell < PlatformHelperBase
|
447
|
+
|
448
|
+
# @return [String] name or path of file reserved for null output redirection
|
449
|
+
def null_output_name
|
450
|
+
must_be_overridden
|
451
|
+
end
|
452
|
+
|
453
|
+
# Fully qualifies a partial script file path to ensure it is executable on
|
454
|
+
# the current platform.
|
455
|
+
#
|
456
|
+
# For Windows compatibility; has no significance in Linux
|
457
|
+
#
|
458
|
+
# @param [String] partial_script_file_path to format
|
459
|
+
# @param [String] default_extension to use if no extension (Default = platform specific)
|
460
|
+
#
|
461
|
+
# @return [String] full script path
|
462
|
+
def format_script_file_name(partial_script_file_path, default_extension = nil)
|
463
|
+
must_be_overridden
|
464
|
+
end
|
465
|
+
|
466
|
+
# Formats an executable command by quoting any of the arguments as needed
|
467
|
+
# and building an executable command string.
|
468
|
+
#
|
469
|
+
# @param [String] executable_file_path full or partial
|
470
|
+
# @param [Array] arguments for executable, if any
|
471
|
+
#
|
472
|
+
# @return [String] executable command string
|
473
|
+
def format_executable_command(executable_file_path, *arguments)
|
474
|
+
must_be_overridden
|
475
|
+
end
|
476
|
+
|
477
|
+
# Formats a shell command using the given script path and arguments.
|
478
|
+
# Provides the path to the executable for the script as needed for the
|
479
|
+
# current platform.
|
480
|
+
#
|
481
|
+
# @param [String] shell_script_file_path shell script file path
|
482
|
+
# @param [Array] arguments for executable, if any
|
483
|
+
#
|
484
|
+
# @return [String] executable command string
|
485
|
+
def format_shell_command(shell_script_file_path, *arguments)
|
486
|
+
must_be_overridden
|
487
|
+
end
|
488
|
+
|
489
|
+
# Formats a ruby command using the given script path and arguments and
|
490
|
+
# the sandbox ruby path.
|
491
|
+
#
|
492
|
+
# @param [String] shell_script_file_path for formatting
|
493
|
+
# @param [Array] arguments for command or empty
|
494
|
+
#
|
495
|
+
# @return [String] executable command string
|
496
|
+
def format_ruby_command(shell_script_file_path, *arguments)
|
497
|
+
return format_executable_command(sandbox_ruby, [shell_script_file_path, arguments])
|
498
|
+
end
|
499
|
+
|
500
|
+
# Appends STDOUT redirection to the given shell command.
|
501
|
+
#
|
502
|
+
# @param [String] cmd to format
|
503
|
+
# @param [String] redirection target (Default = null output)
|
504
|
+
#
|
505
|
+
# @return [String] formatted for redirection
|
506
|
+
def format_redirect_stdout(cmd, target = nil)
|
507
|
+
target ||= null_output_name
|
508
|
+
return cmd + " 1>#{target}"
|
509
|
+
end
|
510
|
+
|
511
|
+
# Appends STDERR redirection to the given shell command.
|
512
|
+
#
|
513
|
+
# @param [String] cmd to format
|
514
|
+
# @param [String] redirection target (Default = null output)
|
515
|
+
#
|
516
|
+
# @return [String] formatted for redirection
|
517
|
+
def format_redirect_stderr(cmd, target = nil)
|
518
|
+
target ||= null_output_name
|
519
|
+
return cmd + " 2>#{target}"
|
520
|
+
end
|
521
|
+
|
522
|
+
# Appends STDERR redirection to the given shell command.
|
523
|
+
#
|
524
|
+
# @param [String] cmd to format
|
525
|
+
# @param [String] redirection target (Default = null output)
|
526
|
+
#
|
527
|
+
# @return [String] formatted for redirection
|
528
|
+
def format_redirect_both(cmd, target = nil)
|
529
|
+
target ||= null_output_name
|
530
|
+
return cmd + " 1>#{target} 2>&1"
|
531
|
+
end
|
532
|
+
|
533
|
+
# @return [String] full path to the RightScale sandboxed ruby executable
|
534
|
+
def sandbox_ruby
|
535
|
+
must_be_overridden
|
536
|
+
end
|
537
|
+
|
538
|
+
# Gets the current system uptime.
|
539
|
+
#
|
540
|
+
# @return [Float] time the machine has been up, in seconds, or 0.0
|
541
|
+
def uptime
|
542
|
+
must_be_overridden
|
543
|
+
end
|
544
|
+
|
545
|
+
# Gets the time at which the system was booted.
|
546
|
+
#
|
547
|
+
# @return [Integer] the UTC timestamp at which the system was booted or nil on failure
|
548
|
+
def booted_at
|
549
|
+
must_be_overridden
|
550
|
+
end
|
551
|
+
end
|
552
|
+
|
553
|
+
# System controller APIs.
|
554
|
+
class Controller < PlatformHelperBase
|
555
|
+
|
556
|
+
# Reboot machine now.
|
557
|
+
#
|
558
|
+
# @return [TrueClass] always true
|
559
|
+
def reboot
|
560
|
+
must_be_overridden
|
561
|
+
end
|
562
|
+
|
563
|
+
# Shutdown machine now.
|
564
|
+
#
|
565
|
+
# @return [TrueClass] always true
|
566
|
+
def shutdown
|
567
|
+
must_be_overridden
|
568
|
+
end
|
569
|
+
end
|
570
|
+
|
571
|
+
# Randomizer APIs.
|
572
|
+
class Rng < PlatformHelperBase
|
573
|
+
|
574
|
+
# Generates a pseudo-random byte string.
|
575
|
+
#
|
576
|
+
# @param [Fixnum] count of bytes
|
577
|
+
#
|
578
|
+
# @return [String] bytes
|
579
|
+
def pseudorandom_bytes(count)
|
580
|
+
must_be_overridden
|
581
|
+
end
|
582
|
+
end
|
583
|
+
|
584
|
+
# Process APIs.
|
585
|
+
class Process < PlatformHelperBase
|
586
|
+
|
587
|
+
# Queries resident/working set size (total memory used by process) for
|
588
|
+
# the process given by identifier (PID).
|
589
|
+
#
|
590
|
+
# @param [Fixnum] pid for query (Default = current process)
|
591
|
+
#
|
592
|
+
# @return [Integer] current set size in KB
|
593
|
+
def resident_set_size(pid = nil)
|
594
|
+
must_be_overridden
|
595
|
+
end
|
596
|
+
end
|
597
|
+
|
598
|
+
# Package installation APIs.
|
599
|
+
class Installer < PlatformHelperBase
|
600
|
+
|
601
|
+
# exceptions
|
602
|
+
class PackageNotFound < ::RightScale::Exceptions::PlatformError; end
|
603
|
+
class PackageManagerNotFound < ::RightScale::Exceptions::PlatformError; end
|
604
|
+
|
605
|
+
# @return [String] installer output or nil
|
606
|
+
attr_accessor :output
|
607
|
+
|
608
|
+
def initialize
|
609
|
+
@output = nil
|
610
|
+
end
|
611
|
+
|
612
|
+
# Install packages based on installed package manager.
|
613
|
+
#
|
614
|
+
# For Unix compatibility; has no significance in Windows
|
615
|
+
#
|
616
|
+
# @param [Array] packages to be installed
|
617
|
+
#
|
618
|
+
# @return [TrueClass] always true
|
619
|
+
#
|
620
|
+
# @raise [RightScale::Exceptions::PlatformError] if not supported by platform
|
621
|
+
# @raise [PackageNotFound] if package is not found
|
622
|
+
# @raise [PackageManagerNotFound] if package manager is not available
|
623
|
+
# @raise [CommandError] on any other command failure
|
624
|
+
def install(packages)
|
625
|
+
must_be_overridden
|
626
|
+
end
|
627
|
+
end
|
628
|
+
|
629
|
+
private
|
630
|
+
|
631
|
+
# Load platform specific implementation
|
632
|
+
def initialize
|
633
|
+
@genus = nil
|
634
|
+
@species = nil
|
635
|
+
@filesystem = nil
|
636
|
+
@shell = nil
|
637
|
+
@ssh = nil
|
638
|
+
@controller = nil
|
639
|
+
@installer = nil
|
640
|
+
@flavor = nil
|
641
|
+
@release = nil
|
642
|
+
@codename = nil
|
643
|
+
|
644
|
+
initialize_platform_specific
|
645
|
+
end
|
646
|
+
|
647
|
+
# First-initialization tasks. Also convenient for overriding during
|
648
|
+
# testing on platforms that differ from current platform.
|
649
|
+
def initialize_platform_specific
|
650
|
+
load_platform_specific
|
651
|
+
initialize_genus
|
652
|
+
initialize_species
|
653
|
+
end
|
654
|
+
|
655
|
+
# Load platform specific implementation
|
656
|
+
#
|
657
|
+
# @return [TrueClass|FalseClass] true if loaded first time, false if already loaded
|
658
|
+
def load_platform_specific
|
659
|
+
# TEAL NOTE the unusal thing about this singleton is that it is
|
660
|
+
# redefined incrementally by first loading the base then the genus then
|
661
|
+
# the species, all of which define parts of the whole.
|
662
|
+
result = require platform_genus_path
|
663
|
+
result = (require platform_species_path) && result
|
664
|
+
result
|
665
|
+
end
|
666
|
+
|
667
|
+
# Performs any platform genus-specific initialization. This method is
|
668
|
+
# invoked only after the current platform's specific implementation has
|
669
|
+
# been loaded.
|
670
|
+
#
|
671
|
+
# @return [TrueClass] always true
|
672
|
+
def initialize_genus
|
673
|
+
raise ::NotImplementedError, 'Must be overridden'
|
674
|
+
end
|
675
|
+
|
676
|
+
# Performs any platform species-specific initialization. This method is
|
677
|
+
# invoked only after the current platform's specific implementation has
|
678
|
+
# been loaded.
|
679
|
+
#
|
680
|
+
# @return [TrueClass] always true
|
681
|
+
def initialize_species
|
682
|
+
raise ::NotImplementedError, 'Must be overridden'
|
683
|
+
end
|
684
|
+
|
685
|
+
# Determines genus/species for current platform.
|
686
|
+
def resolve_taxonomy
|
687
|
+
case ::RbConfig::CONFIG['host_os']
|
688
|
+
when /darwin/i
|
689
|
+
@genus = :unix
|
690
|
+
@species = :darwin
|
691
|
+
when /linux/i
|
692
|
+
@genus = :unix
|
693
|
+
@species = :linux
|
694
|
+
when /mingw/i
|
695
|
+
@genus = :windows
|
696
|
+
@species = :mingw
|
697
|
+
when /mswin/i
|
698
|
+
@genus = :windows
|
699
|
+
@species = :mswin
|
700
|
+
when /windows|win32|dos|cygwin/i
|
701
|
+
raise ::RightScale::Exceptions::PlatformError,
|
702
|
+
'Unsupported Ruby-on-Windows variant'
|
703
|
+
else
|
704
|
+
raise ::RightScale::Exceptions::PlatformError, 'Unknown platform'
|
705
|
+
end
|
706
|
+
true
|
707
|
+
end
|
708
|
+
|
709
|
+
# @return [String] path to platform-independent implementation.
|
710
|
+
def platform_base_path
|
711
|
+
::File.expand_path('../platform', __FILE__)
|
712
|
+
end
|
713
|
+
|
714
|
+
# @return [String] path to platform-specific genus implementation.
|
715
|
+
def platform_genus_path
|
716
|
+
::File.expand_path("#{genus}/platform", platform_base_path)
|
717
|
+
end
|
718
|
+
|
719
|
+
# @return [String] path to platform-specific species implementation.
|
720
|
+
def platform_species_path
|
721
|
+
::File.expand_path("#{genus}/#{species}/platform", platform_base_path)
|
722
|
+
end
|
723
|
+
|
724
|
+
# Retrieve platform specific service implementation
|
725
|
+
#
|
726
|
+
# @param [Symbol] name of platform service
|
727
|
+
#
|
728
|
+
# @return [PlatformHelperBase] service instance
|
729
|
+
#
|
730
|
+
# @raise [RightScale::Exceptions::PlatformError] on unknown service
|
731
|
+
def platform_service(name)
|
732
|
+
instance_var = "@#{name.to_s}".to_sym
|
733
|
+
const_name = name.to_s.camelize
|
734
|
+
|
735
|
+
unless res = self.instance_variable_get(instance_var)
|
736
|
+
load_platform_specific
|
737
|
+
if clazz = Platform.const_get(const_name)
|
738
|
+
res = clazz.new
|
739
|
+
self.instance_variable_set(instance_var, res)
|
740
|
+
else
|
741
|
+
raise ::RightScale::Exceptions::PlatformError,
|
742
|
+
"Unknown platform service: #{name}"
|
743
|
+
end
|
744
|
+
end
|
745
|
+
return res
|
746
|
+
end
|
747
|
+
|
748
|
+
# Determines which cloud we're on by the cheap but simple expedient of
|
749
|
+
# reading the RightScale cloud file.
|
750
|
+
#
|
751
|
+
# @deprecated leverage the right_link cloud libraries for any cloud-
|
752
|
+
# specific behavior because the behavior of all possible clouds is
|
753
|
+
# beyond the scope of hard-coded case statements.
|
754
|
+
#
|
755
|
+
# @return [String] cloud type or nil
|
756
|
+
def resolve_cloud_type
|
757
|
+
cloud_file_path = ::File.join(self.filesystem.right_scale_static_state_dir, 'cloud')
|
758
|
+
@cloud_type = ::File.read(cloud_file_path) rescue nil
|
759
|
+
@cloud_type
|
760
|
+
end
|
761
|
+
|
762
|
+
end # Platform
|
763
|
+
|
764
|
+
end # RightScale
|
765
|
+
|
766
|
+
# Initialize for current platform and/or force singleton creation on current
|
767
|
+
# thread to avoid any weird threaded initialization issues.
|
768
|
+
::RightScale::Platform.instance
|
769
|
+
|
770
|
+
end # unless already defined
|