right_agent 0.5.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- data/LICENSE +20 -0
- data/README.rdoc +78 -0
- data/Rakefile +86 -0
- data/lib/right_agent.rb +66 -0
- data/lib/right_agent/actor.rb +163 -0
- data/lib/right_agent/actor_registry.rb +76 -0
- data/lib/right_agent/actors/agent_manager.rb +189 -0
- data/lib/right_agent/agent.rb +735 -0
- data/lib/right_agent/agent_config.rb +403 -0
- data/lib/right_agent/agent_identity.rb +209 -0
- data/lib/right_agent/agent_tags_manager.rb +213 -0
- data/lib/right_agent/audit_formatter.rb +107 -0
- data/lib/right_agent/broker_client.rb +683 -0
- data/lib/right_agent/command.rb +30 -0
- data/lib/right_agent/command/agent_manager_commands.rb +134 -0
- data/lib/right_agent/command/command_client.rb +136 -0
- data/lib/right_agent/command/command_constants.rb +42 -0
- data/lib/right_agent/command/command_io.rb +128 -0
- data/lib/right_agent/command/command_parser.rb +87 -0
- data/lib/right_agent/command/command_runner.rb +105 -0
- data/lib/right_agent/command/command_serializer.rb +63 -0
- data/lib/right_agent/console.rb +65 -0
- data/lib/right_agent/core_payload_types.rb +42 -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 +90 -0
- data/lib/right_agent/core_payload_types/event_categories.rb +38 -0
- data/lib/right_agent/core_payload_types/executable_bundle.rb +138 -0
- data/lib/right_agent/core_payload_types/login_policy.rb +72 -0
- data/lib/right_agent/core_payload_types/login_user.rb +62 -0
- data/lib/right_agent/core_payload_types/planned_volume.rb +94 -0
- data/lib/right_agent/core_payload_types/recipe_instantiation.rb +60 -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 +73 -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/dispatcher.rb +348 -0
- data/lib/right_agent/enrollment_result.rb +217 -0
- data/lib/right_agent/exceptions.rb +30 -0
- data/lib/right_agent/ha_broker_client.rb +1278 -0
- data/lib/right_agent/idempotent_request.rb +140 -0
- data/lib/right_agent/log.rb +418 -0
- data/lib/right_agent/monkey_patches.rb +29 -0
- data/lib/right_agent/monkey_patches/amqp_patch.rb +274 -0
- data/lib/right_agent/monkey_patches/ruby_patch.rb +49 -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/singleton_patch.rb +46 -0
- data/lib/right_agent/monkey_patches/ruby_patch/string_patch.rb +107 -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 +90 -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 +91 -0
- data/lib/right_agent/operation_result.rb +270 -0
- data/lib/right_agent/packets.rb +637 -0
- data/lib/right_agent/payload_formatter.rb +104 -0
- data/lib/right_agent/pid_file.rb +159 -0
- data/lib/right_agent/platform.rb +319 -0
- data/lib/right_agent/platform/darwin.rb +227 -0
- data/lib/right_agent/platform/linux.rb +268 -0
- data/lib/right_agent/platform/windows.rb +1204 -0
- data/lib/right_agent/scripts/agent_controller.rb +522 -0
- data/lib/right_agent/scripts/agent_deployer.rb +379 -0
- data/lib/right_agent/scripts/common_parser.rb +153 -0
- data/lib/right_agent/scripts/log_level_manager.rb +193 -0
- data/lib/right_agent/scripts/stats_manager.rb +256 -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 +63 -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 +84 -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 +69 -0
- data/lib/right_agent/sender.rb +937 -0
- data/lib/right_agent/serialize.rb +29 -0
- data/lib/right_agent/serialize/message_pack.rb +102 -0
- data/lib/right_agent/serialize/secure_serializer.rb +131 -0
- data/lib/right_agent/serialize/secure_serializer_initializer.rb +47 -0
- data/lib/right_agent/serialize/serializable.rb +135 -0
- data/lib/right_agent/serialize/serializer.rb +149 -0
- data/lib/right_agent/stats_helper.rb +731 -0
- data/lib/right_agent/subprocess.rb +38 -0
- data/lib/right_agent/tracer.rb +124 -0
- data/right_agent.gemspec +60 -0
- data/spec/actor_registry_spec.rb +81 -0
- data/spec/actor_spec.rb +99 -0
- data/spec/agent_config_spec.rb +226 -0
- data/spec/agent_identity_spec.rb +75 -0
- data/spec/agent_spec.rb +571 -0
- data/spec/broker_client_spec.rb +961 -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 +72 -0
- data/spec/command/command_serializer_spec.rb +51 -0
- data/spec/core_payload_types/dev_repositories_spec.rb +64 -0
- data/spec/core_payload_types/executable_bundle_spec.rb +59 -0
- data/spec/core_payload_types/login_user_spec.rb +98 -0
- data/spec/core_payload_types/right_script_attachment_spec.rb +65 -0
- data/spec/core_payload_types/spec_helper.rb +23 -0
- data/spec/dispatcher_spec.rb +372 -0
- data/spec/enrollment_result_spec.rb +53 -0
- data/spec/ha_broker_client_spec.rb +1673 -0
- data/spec/idempotent_request_spec.rb +136 -0
- data/spec/log_spec.rb +177 -0
- data/spec/monkey_patches/amqp_patch_spec.rb +100 -0
- data/spec/monkey_patches/eventmachine_spec.rb +62 -0
- data/spec/monkey_patches/string_patch_spec.rb +99 -0
- data/spec/multiplexer_spec.rb +48 -0
- data/spec/operation_result_spec.rb +171 -0
- data/spec/packets_spec.rb +418 -0
- data/spec/platform/platform_spec.rb +60 -0
- data/spec/results_mock.rb +45 -0
- data/spec/secure_identity_spec.rb +50 -0
- data/spec/security/cached_certificate_store_proxy_spec.rb +56 -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 +52 -0
- data/spec/sender_spec.rb +887 -0
- data/spec/serialize/message_pack_spec.rb +131 -0
- data/spec/serialize/secure_serializer_spec.rb +102 -0
- data/spec/serialize/serializable_spec.rb +90 -0
- data/spec/serialize/serializer_spec.rb +174 -0
- data/spec/spec.opts +2 -0
- data/spec/spec_helper.rb +77 -0
- data/spec/stats_helper_spec.rb +681 -0
- data/spec/tracer_spec.rb +114 -0
- metadata +320 -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,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
|
|
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.exists?(@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,319 @@
|
|
|
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
|
+
# 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
|
+
|
|
32
|
+
require 'rbconfig'
|
|
33
|
+
|
|
34
|
+
|
|
35
|
+
|
|
36
|
+
# Load ruby interpreter monkey-patches first (to ensure File.normalize_path is defined, etc.).
|
|
37
|
+
require File.expand_path(File.join(File.dirname(__FILE__), 'monkey_patches', 'ruby_patch'))
|
|
38
|
+
|
|
39
|
+
module RightScale
|
|
40
|
+
# Throw when the current platform is not supported for some reason
|
|
41
|
+
class PlatformNotSupported < Exception; end
|
|
42
|
+
|
|
43
|
+
# A utility class that provides information about the platform on which the RightAgent is running
|
|
44
|
+
# Available information includes:
|
|
45
|
+
# - which flavor cloud (EC2, Rackspace, Eucalyptus, ..)
|
|
46
|
+
# - which flavor operating system (Linux, Windows or Mac)
|
|
47
|
+
# - which OS release (a numeric value that is specific to the OS)
|
|
48
|
+
# - directories in which various bits of RightScale state may be found
|
|
49
|
+
# - platform-specific information such as Linux flavor or release
|
|
50
|
+
#
|
|
51
|
+
# For platform information only used in specific contexts, the dispatch method may be used
|
|
52
|
+
#
|
|
53
|
+
# This is a summary of the information you can query by calling Platform's instance methods:
|
|
54
|
+
# - .flavor
|
|
55
|
+
# - .release
|
|
56
|
+
# - .linux?
|
|
57
|
+
# - .mac?
|
|
58
|
+
# - .windows?
|
|
59
|
+
# - .ec2?
|
|
60
|
+
# - .rackspace?
|
|
61
|
+
# - .eucalyptus?
|
|
62
|
+
# - .filesystem
|
|
63
|
+
# - right_scale_state_dir
|
|
64
|
+
# - spool_dir
|
|
65
|
+
# - cache_dir
|
|
66
|
+
# - .linux (only available under Linux)
|
|
67
|
+
# - ubuntu?
|
|
68
|
+
# - centos?
|
|
69
|
+
# - suse?
|
|
70
|
+
class Platform
|
|
71
|
+
|
|
72
|
+
include Singleton
|
|
73
|
+
|
|
74
|
+
# Generic platform family
|
|
75
|
+
#
|
|
76
|
+
# === Return
|
|
77
|
+
# family(Symbol):: One of :linux, :windows or :darwin
|
|
78
|
+
def family
|
|
79
|
+
@family ||= case RbConfig::CONFIG['host_os']
|
|
80
|
+
when /mswin|win32|dos|mingw|cygwin/i then :windows
|
|
81
|
+
when /darwin/i then :darwin
|
|
82
|
+
when /linux/i then :linux
|
|
83
|
+
end
|
|
84
|
+
end
|
|
85
|
+
|
|
86
|
+
# Is current platform linux?
|
|
87
|
+
#
|
|
88
|
+
# === Return
|
|
89
|
+
# true:: If current platform is linux
|
|
90
|
+
# false:: Otherwise
|
|
91
|
+
def linux?
|
|
92
|
+
return family == :linux
|
|
93
|
+
end
|
|
94
|
+
|
|
95
|
+
# Is current platform darwin?
|
|
96
|
+
#
|
|
97
|
+
# === Return
|
|
98
|
+
# true:: If current platform is darwin
|
|
99
|
+
# false:: Otherwise
|
|
100
|
+
def darwin?
|
|
101
|
+
return family == :darwin
|
|
102
|
+
end
|
|
103
|
+
# Is current platform windows?
|
|
104
|
+
#
|
|
105
|
+
# === Return
|
|
106
|
+
# true:: If current platform is Windows
|
|
107
|
+
# false:: Otherwise
|
|
108
|
+
def windows?
|
|
109
|
+
return family == :windows
|
|
110
|
+
end
|
|
111
|
+
|
|
112
|
+
# Call platform specific implementation of method whose symbol is returned
|
|
113
|
+
# by the passed in block. Arguments are passed through.
|
|
114
|
+
# e.g.
|
|
115
|
+
#
|
|
116
|
+
# Platform.dispatch(2) { :echo }
|
|
117
|
+
#
|
|
118
|
+
# will result in 'echo_linux(2)' being executed in self if running on linux,
|
|
119
|
+
# 'echo_windows(2)' if running on Windows and 'echo_darwin(2)' if on Mac OS X.
|
|
120
|
+
# Note that the method is run in the instance of the caller.
|
|
121
|
+
#
|
|
122
|
+
# === Parameters
|
|
123
|
+
# args:: Pass-through arguments
|
|
124
|
+
#
|
|
125
|
+
# === Block
|
|
126
|
+
# Given block should not take any argument and return a symbol for the
|
|
127
|
+
# method that should be called
|
|
128
|
+
#
|
|
129
|
+
# === Return
|
|
130
|
+
# res(Object):: Result returned by platform specific implementation
|
|
131
|
+
def dispatch(*args, &blk)
|
|
132
|
+
raise "Platform.dispatch requires a block" unless blk
|
|
133
|
+
binding = blk.binding.eval('self')
|
|
134
|
+
meth = blk.call
|
|
135
|
+
target = dispatch_candidates(meth).detect do |candidate|
|
|
136
|
+
binding.respond_to?(candidate)
|
|
137
|
+
end
|
|
138
|
+
raise "No platform dispatch target found in #{binding.class} for " +
|
|
139
|
+
"'#{meth.inspect}', tried " + dispatch_candidates(meth).join(', ') unless target
|
|
140
|
+
binding.__send__(target, *args)
|
|
141
|
+
end
|
|
142
|
+
|
|
143
|
+
# Load platform specific implementation
|
|
144
|
+
#
|
|
145
|
+
# === Return
|
|
146
|
+
# true:: Always return true
|
|
147
|
+
def load_platform_specific
|
|
148
|
+
if linux?
|
|
149
|
+
require_linux
|
|
150
|
+
elsif darwin?
|
|
151
|
+
require_darwin
|
|
152
|
+
elsif windows?
|
|
153
|
+
require_windows
|
|
154
|
+
else
|
|
155
|
+
raise PlatformError.new('Unknown platform')
|
|
156
|
+
end
|
|
157
|
+
end
|
|
158
|
+
|
|
159
|
+
# Are we in an EC2 cloud?
|
|
160
|
+
#
|
|
161
|
+
# === Return
|
|
162
|
+
# true:: If machine is located in an Ec2 cloud
|
|
163
|
+
# false:: Otherwise
|
|
164
|
+
def ec2?
|
|
165
|
+
resolve_cloud_type if @ec2.nil?
|
|
166
|
+
@ec2
|
|
167
|
+
end
|
|
168
|
+
|
|
169
|
+
# Are we in a Rackspace cloud?
|
|
170
|
+
#
|
|
171
|
+
# === Return
|
|
172
|
+
# true:: If machine is located in an Rackspace cloud
|
|
173
|
+
# false:: Otherwise
|
|
174
|
+
def rackspace?
|
|
175
|
+
resolve_cloud_type if @rackspace.nil?
|
|
176
|
+
@rackspace
|
|
177
|
+
end
|
|
178
|
+
|
|
179
|
+
# Are we in a Eucalyptus cloud?
|
|
180
|
+
#
|
|
181
|
+
# === Return
|
|
182
|
+
# true:: If machine is located in an Eucalyptus cloud
|
|
183
|
+
# false:: Otherwise
|
|
184
|
+
def eucalyptus?
|
|
185
|
+
resolve_cloud_type if @eucalyptus.nil?
|
|
186
|
+
@eucalyptus
|
|
187
|
+
end
|
|
188
|
+
|
|
189
|
+
# Controller object
|
|
190
|
+
#
|
|
191
|
+
# === Return
|
|
192
|
+
# (Controller):: Platform-specific controller object
|
|
193
|
+
def controller
|
|
194
|
+
platform_service(:controller)
|
|
195
|
+
end
|
|
196
|
+
|
|
197
|
+
# Filesystem config object
|
|
198
|
+
#
|
|
199
|
+
# === Return
|
|
200
|
+
# (Filesystem):: Platform-specific filesystem config object
|
|
201
|
+
def filesystem
|
|
202
|
+
platform_service(:filesystem)
|
|
203
|
+
end
|
|
204
|
+
|
|
205
|
+
# VolumeManager config object
|
|
206
|
+
#
|
|
207
|
+
# === Return
|
|
208
|
+
# (VolumeManager):: Platform-specific volume manager config object
|
|
209
|
+
def volume_manager
|
|
210
|
+
platform_service(:volume_manager)
|
|
211
|
+
end
|
|
212
|
+
|
|
213
|
+
# Shell information object
|
|
214
|
+
#
|
|
215
|
+
# === Return
|
|
216
|
+
# (Object):: Platform-specific shell information object
|
|
217
|
+
def shell
|
|
218
|
+
platform_service(:shell)
|
|
219
|
+
end
|
|
220
|
+
|
|
221
|
+
# SSH information object
|
|
222
|
+
#
|
|
223
|
+
# === Return
|
|
224
|
+
# (Object):: Platform-specific ssh object
|
|
225
|
+
def ssh
|
|
226
|
+
platform_service(:ssh)
|
|
227
|
+
end
|
|
228
|
+
|
|
229
|
+
# Platform random number generator (RNG) facilities.
|
|
230
|
+
#
|
|
231
|
+
# === Return
|
|
232
|
+
# (Object):: Platform-specific RNG object
|
|
233
|
+
def rng
|
|
234
|
+
platform_service(:rng)
|
|
235
|
+
end
|
|
236
|
+
|
|
237
|
+
private
|
|
238
|
+
|
|
239
|
+
# Load platform specific implementation
|
|
240
|
+
def initialize
|
|
241
|
+
require File.expand_path(File.join(File.dirname(__FILE__), 'platform', family.to_s))
|
|
242
|
+
@filesystem = nil
|
|
243
|
+
@shell = nil
|
|
244
|
+
@ssh = nil
|
|
245
|
+
@controller = nil
|
|
246
|
+
|
|
247
|
+
@ec2 = nil
|
|
248
|
+
@rackspace = nil
|
|
249
|
+
@eucalyptus = nil
|
|
250
|
+
|
|
251
|
+
init
|
|
252
|
+
|
|
253
|
+
# Note that we must defer any use of filesystem until requested because
|
|
254
|
+
# Windows setup scripts attempt to use Platform before installing some
|
|
255
|
+
# of the required gems. Don't attempt to call code that requires gems in
|
|
256
|
+
# initialize().
|
|
257
|
+
end
|
|
258
|
+
|
|
259
|
+
def require_linux
|
|
260
|
+
require File.expand_path(File.join(File.dirname(__FILE__), 'platform', 'linux'))
|
|
261
|
+
end
|
|
262
|
+
|
|
263
|
+
def require_darwin
|
|
264
|
+
require File.expand_path(File.join(File.dirname(__FILE__), 'platform', 'darwin'))
|
|
265
|
+
end
|
|
266
|
+
|
|
267
|
+
def require_windows
|
|
268
|
+
require File.expand_path(File.join(File.dirname(__FILE__), 'platform', 'windows'))
|
|
269
|
+
end
|
|
270
|
+
|
|
271
|
+
# Determines which cloud we're on by the cheap but simple expedient of
|
|
272
|
+
# reading the RightScale cloud file
|
|
273
|
+
def resolve_cloud_type
|
|
274
|
+
cloud_type = File.read(File.join(self.filesystem.right_scale_state_dir, 'cloud')) rescue nil
|
|
275
|
+
@ec2 = false
|
|
276
|
+
@rackspace = false
|
|
277
|
+
@eucalyptus = false
|
|
278
|
+
case cloud_type
|
|
279
|
+
when 'ec2' then ec2 = true
|
|
280
|
+
when 'rackspace' then @rackspace = true
|
|
281
|
+
when 'eucalyptus' then @eucalyptus = true
|
|
282
|
+
end
|
|
283
|
+
end
|
|
284
|
+
|
|
285
|
+
# Retrieve platform specific service implementation
|
|
286
|
+
#
|
|
287
|
+
# === Parameters
|
|
288
|
+
# name(Symbol):: Service name, one of :filesystem, :shell, :ssh, :controller
|
|
289
|
+
#
|
|
290
|
+
# === Return
|
|
291
|
+
# res(Object):: Service instance
|
|
292
|
+
#
|
|
293
|
+
# === Raise
|
|
294
|
+
# RightScale::Exceptions::PlatformError:: If the service is not known
|
|
295
|
+
def platform_service(name)
|
|
296
|
+
instance_var = "@#{name.to_s}".to_sym
|
|
297
|
+
const_name = name.to_s.camelize
|
|
298
|
+
|
|
299
|
+
unless res = self.instance_variable_get(instance_var)
|
|
300
|
+
load_platform_specific
|
|
301
|
+
if linux?
|
|
302
|
+
res = Platform.const_get(const_name).new
|
|
303
|
+
elsif darwin?
|
|
304
|
+
res = Platform.const_get(const_name).new
|
|
305
|
+
elsif windows?
|
|
306
|
+
res = Platform.const_get(const_name).new
|
|
307
|
+
end
|
|
308
|
+
end
|
|
309
|
+
return res
|
|
310
|
+
end
|
|
311
|
+
|
|
312
|
+
end # Platform
|
|
313
|
+
|
|
314
|
+
end # RightScale
|
|
315
|
+
|
|
316
|
+
# Initialize for current platform
|
|
317
|
+
RightScale::Platform.load_platform_specific
|
|
318
|
+
|
|
319
|
+
end # Unless already defined
|