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.
Files changed (147) hide show
  1. data/LICENSE +20 -0
  2. data/README.rdoc +78 -0
  3. data/Rakefile +86 -0
  4. data/lib/right_agent.rb +66 -0
  5. data/lib/right_agent/actor.rb +163 -0
  6. data/lib/right_agent/actor_registry.rb +76 -0
  7. data/lib/right_agent/actors/agent_manager.rb +189 -0
  8. data/lib/right_agent/agent.rb +735 -0
  9. data/lib/right_agent/agent_config.rb +403 -0
  10. data/lib/right_agent/agent_identity.rb +209 -0
  11. data/lib/right_agent/agent_tags_manager.rb +213 -0
  12. data/lib/right_agent/audit_formatter.rb +107 -0
  13. data/lib/right_agent/broker_client.rb +683 -0
  14. data/lib/right_agent/command.rb +30 -0
  15. data/lib/right_agent/command/agent_manager_commands.rb +134 -0
  16. data/lib/right_agent/command/command_client.rb +136 -0
  17. data/lib/right_agent/command/command_constants.rb +42 -0
  18. data/lib/right_agent/command/command_io.rb +128 -0
  19. data/lib/right_agent/command/command_parser.rb +87 -0
  20. data/lib/right_agent/command/command_runner.rb +105 -0
  21. data/lib/right_agent/command/command_serializer.rb +63 -0
  22. data/lib/right_agent/console.rb +65 -0
  23. data/lib/right_agent/core_payload_types.rb +42 -0
  24. data/lib/right_agent/core_payload_types/cookbook.rb +61 -0
  25. data/lib/right_agent/core_payload_types/cookbook_position.rb +46 -0
  26. data/lib/right_agent/core_payload_types/cookbook_repository.rb +116 -0
  27. data/lib/right_agent/core_payload_types/cookbook_sequence.rb +70 -0
  28. data/lib/right_agent/core_payload_types/dev_repositories.rb +90 -0
  29. data/lib/right_agent/core_payload_types/event_categories.rb +38 -0
  30. data/lib/right_agent/core_payload_types/executable_bundle.rb +138 -0
  31. data/lib/right_agent/core_payload_types/login_policy.rb +72 -0
  32. data/lib/right_agent/core_payload_types/login_user.rb +62 -0
  33. data/lib/right_agent/core_payload_types/planned_volume.rb +94 -0
  34. data/lib/right_agent/core_payload_types/recipe_instantiation.rb +60 -0
  35. data/lib/right_agent/core_payload_types/repositories_bundle.rb +50 -0
  36. data/lib/right_agent/core_payload_types/right_script_attachment.rb +95 -0
  37. data/lib/right_agent/core_payload_types/right_script_instantiation.rb +73 -0
  38. data/lib/right_agent/core_payload_types/secure_document.rb +66 -0
  39. data/lib/right_agent/core_payload_types/secure_document_location.rb +63 -0
  40. data/lib/right_agent/core_payload_types/software_repository_instantiation.rb +61 -0
  41. data/lib/right_agent/daemonize.rb +35 -0
  42. data/lib/right_agent/dispatcher.rb +348 -0
  43. data/lib/right_agent/enrollment_result.rb +217 -0
  44. data/lib/right_agent/exceptions.rb +30 -0
  45. data/lib/right_agent/ha_broker_client.rb +1278 -0
  46. data/lib/right_agent/idempotent_request.rb +140 -0
  47. data/lib/right_agent/log.rb +418 -0
  48. data/lib/right_agent/monkey_patches.rb +29 -0
  49. data/lib/right_agent/monkey_patches/amqp_patch.rb +274 -0
  50. data/lib/right_agent/monkey_patches/ruby_patch.rb +49 -0
  51. data/lib/right_agent/monkey_patches/ruby_patch/array_patch.rb +29 -0
  52. data/lib/right_agent/monkey_patches/ruby_patch/darwin_patch.rb +24 -0
  53. data/lib/right_agent/monkey_patches/ruby_patch/linux_patch.rb +24 -0
  54. data/lib/right_agent/monkey_patches/ruby_patch/linux_patch/file_patch.rb +30 -0
  55. data/lib/right_agent/monkey_patches/ruby_patch/object_patch.rb +49 -0
  56. data/lib/right_agent/monkey_patches/ruby_patch/singleton_patch.rb +46 -0
  57. data/lib/right_agent/monkey_patches/ruby_patch/string_patch.rb +107 -0
  58. data/lib/right_agent/monkey_patches/ruby_patch/windows_patch.rb +32 -0
  59. data/lib/right_agent/monkey_patches/ruby_patch/windows_patch/file_patch.rb +90 -0
  60. data/lib/right_agent/monkey_patches/ruby_patch/windows_patch/process_patch.rb +63 -0
  61. data/lib/right_agent/monkey_patches/ruby_patch/windows_patch/stdio_patch.rb +27 -0
  62. data/lib/right_agent/monkey_patches/ruby_patch/windows_patch/time_patch.rb +55 -0
  63. data/lib/right_agent/monkey_patches/ruby_patch/windows_patch/win32ole_patch.rb +34 -0
  64. data/lib/right_agent/multiplexer.rb +91 -0
  65. data/lib/right_agent/operation_result.rb +270 -0
  66. data/lib/right_agent/packets.rb +637 -0
  67. data/lib/right_agent/payload_formatter.rb +104 -0
  68. data/lib/right_agent/pid_file.rb +159 -0
  69. data/lib/right_agent/platform.rb +319 -0
  70. data/lib/right_agent/platform/darwin.rb +227 -0
  71. data/lib/right_agent/platform/linux.rb +268 -0
  72. data/lib/right_agent/platform/windows.rb +1204 -0
  73. data/lib/right_agent/scripts/agent_controller.rb +522 -0
  74. data/lib/right_agent/scripts/agent_deployer.rb +379 -0
  75. data/lib/right_agent/scripts/common_parser.rb +153 -0
  76. data/lib/right_agent/scripts/log_level_manager.rb +193 -0
  77. data/lib/right_agent/scripts/stats_manager.rb +256 -0
  78. data/lib/right_agent/scripts/usage.rb +58 -0
  79. data/lib/right_agent/secure_identity.rb +92 -0
  80. data/lib/right_agent/security.rb +32 -0
  81. data/lib/right_agent/security/cached_certificate_store_proxy.rb +63 -0
  82. data/lib/right_agent/security/certificate.rb +102 -0
  83. data/lib/right_agent/security/certificate_cache.rb +89 -0
  84. data/lib/right_agent/security/distinguished_name.rb +56 -0
  85. data/lib/right_agent/security/encrypted_document.rb +84 -0
  86. data/lib/right_agent/security/rsa_key_pair.rb +76 -0
  87. data/lib/right_agent/security/signature.rb +86 -0
  88. data/lib/right_agent/security/static_certificate_store.rb +69 -0
  89. data/lib/right_agent/sender.rb +937 -0
  90. data/lib/right_agent/serialize.rb +29 -0
  91. data/lib/right_agent/serialize/message_pack.rb +102 -0
  92. data/lib/right_agent/serialize/secure_serializer.rb +131 -0
  93. data/lib/right_agent/serialize/secure_serializer_initializer.rb +47 -0
  94. data/lib/right_agent/serialize/serializable.rb +135 -0
  95. data/lib/right_agent/serialize/serializer.rb +149 -0
  96. data/lib/right_agent/stats_helper.rb +731 -0
  97. data/lib/right_agent/subprocess.rb +38 -0
  98. data/lib/right_agent/tracer.rb +124 -0
  99. data/right_agent.gemspec +60 -0
  100. data/spec/actor_registry_spec.rb +81 -0
  101. data/spec/actor_spec.rb +99 -0
  102. data/spec/agent_config_spec.rb +226 -0
  103. data/spec/agent_identity_spec.rb +75 -0
  104. data/spec/agent_spec.rb +571 -0
  105. data/spec/broker_client_spec.rb +961 -0
  106. data/spec/command/agent_manager_commands_spec.rb +51 -0
  107. data/spec/command/command_io_spec.rb +93 -0
  108. data/spec/command/command_parser_spec.rb +79 -0
  109. data/spec/command/command_runner_spec.rb +72 -0
  110. data/spec/command/command_serializer_spec.rb +51 -0
  111. data/spec/core_payload_types/dev_repositories_spec.rb +64 -0
  112. data/spec/core_payload_types/executable_bundle_spec.rb +59 -0
  113. data/spec/core_payload_types/login_user_spec.rb +98 -0
  114. data/spec/core_payload_types/right_script_attachment_spec.rb +65 -0
  115. data/spec/core_payload_types/spec_helper.rb +23 -0
  116. data/spec/dispatcher_spec.rb +372 -0
  117. data/spec/enrollment_result_spec.rb +53 -0
  118. data/spec/ha_broker_client_spec.rb +1673 -0
  119. data/spec/idempotent_request_spec.rb +136 -0
  120. data/spec/log_spec.rb +177 -0
  121. data/spec/monkey_patches/amqp_patch_spec.rb +100 -0
  122. data/spec/monkey_patches/eventmachine_spec.rb +62 -0
  123. data/spec/monkey_patches/string_patch_spec.rb +99 -0
  124. data/spec/multiplexer_spec.rb +48 -0
  125. data/spec/operation_result_spec.rb +171 -0
  126. data/spec/packets_spec.rb +418 -0
  127. data/spec/platform/platform_spec.rb +60 -0
  128. data/spec/results_mock.rb +45 -0
  129. data/spec/secure_identity_spec.rb +50 -0
  130. data/spec/security/cached_certificate_store_proxy_spec.rb +56 -0
  131. data/spec/security/certificate_cache_spec.rb +71 -0
  132. data/spec/security/certificate_spec.rb +49 -0
  133. data/spec/security/distinguished_name_spec.rb +46 -0
  134. data/spec/security/encrypted_document_spec.rb +55 -0
  135. data/spec/security/rsa_key_pair_spec.rb +55 -0
  136. data/spec/security/signature_spec.rb +66 -0
  137. data/spec/security/static_certificate_store_spec.rb +52 -0
  138. data/spec/sender_spec.rb +887 -0
  139. data/spec/serialize/message_pack_spec.rb +131 -0
  140. data/spec/serialize/secure_serializer_spec.rb +102 -0
  141. data/spec/serialize/serializable_spec.rb +90 -0
  142. data/spec/serialize/serializer_spec.rb +174 -0
  143. data/spec/spec.opts +2 -0
  144. data/spec/spec_helper.rb +77 -0
  145. data/spec/stats_helper_spec.rb +681 -0
  146. data/spec/tracer_spec.rb +114 -0
  147. 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