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.
Files changed (176) hide show
  1. data/LICENSE +20 -0
  2. data/README.rdoc +82 -0
  3. data/Rakefile +113 -0
  4. data/lib/right_agent.rb +59 -0
  5. data/lib/right_agent/actor.rb +182 -0
  6. data/lib/right_agent/actor_registry.rb +76 -0
  7. data/lib/right_agent/actors/agent_manager.rb +232 -0
  8. data/lib/right_agent/agent.rb +1149 -0
  9. data/lib/right_agent/agent_config.rb +480 -0
  10. data/lib/right_agent/agent_identity.rb +210 -0
  11. data/lib/right_agent/agent_tag_manager.rb +237 -0
  12. data/lib/right_agent/audit_formatter.rb +107 -0
  13. data/lib/right_agent/clients.rb +31 -0
  14. data/lib/right_agent/clients/api_client.rb +383 -0
  15. data/lib/right_agent/clients/auth_client.rb +247 -0
  16. data/lib/right_agent/clients/balanced_http_client.rb +369 -0
  17. data/lib/right_agent/clients/base_retry_client.rb +495 -0
  18. data/lib/right_agent/clients/right_http_client.rb +279 -0
  19. data/lib/right_agent/clients/router_client.rb +493 -0
  20. data/lib/right_agent/command.rb +30 -0
  21. data/lib/right_agent/command/agent_manager_commands.rb +150 -0
  22. data/lib/right_agent/command/command_client.rb +136 -0
  23. data/lib/right_agent/command/command_constants.rb +33 -0
  24. data/lib/right_agent/command/command_io.rb +126 -0
  25. data/lib/right_agent/command/command_parser.rb +87 -0
  26. data/lib/right_agent/command/command_runner.rb +118 -0
  27. data/lib/right_agent/command/command_serializer.rb +63 -0
  28. data/lib/right_agent/connectivity_checker.rb +179 -0
  29. data/lib/right_agent/console.rb +65 -0
  30. data/lib/right_agent/core_payload_types.rb +44 -0
  31. data/lib/right_agent/core_payload_types/cookbook.rb +61 -0
  32. data/lib/right_agent/core_payload_types/cookbook_position.rb +46 -0
  33. data/lib/right_agent/core_payload_types/cookbook_repository.rb +116 -0
  34. data/lib/right_agent/core_payload_types/cookbook_sequence.rb +70 -0
  35. data/lib/right_agent/core_payload_types/dev_repositories.rb +100 -0
  36. data/lib/right_agent/core_payload_types/dev_repository.rb +76 -0
  37. data/lib/right_agent/core_payload_types/event_categories.rb +38 -0
  38. data/lib/right_agent/core_payload_types/executable_bundle.rb +130 -0
  39. data/lib/right_agent/core_payload_types/login_policy.rb +72 -0
  40. data/lib/right_agent/core_payload_types/login_user.rb +79 -0
  41. data/lib/right_agent/core_payload_types/planned_volume.rb +94 -0
  42. data/lib/right_agent/core_payload_types/recipe_instantiation.rb +73 -0
  43. data/lib/right_agent/core_payload_types/repositories_bundle.rb +50 -0
  44. data/lib/right_agent/core_payload_types/right_script_attachment.rb +95 -0
  45. data/lib/right_agent/core_payload_types/right_script_instantiation.rb +94 -0
  46. data/lib/right_agent/core_payload_types/runlist_policy.rb +44 -0
  47. data/lib/right_agent/core_payload_types/secure_document.rb +66 -0
  48. data/lib/right_agent/core_payload_types/secure_document_location.rb +63 -0
  49. data/lib/right_agent/core_payload_types/software_repository_instantiation.rb +61 -0
  50. data/lib/right_agent/daemonize.rb +35 -0
  51. data/lib/right_agent/dispatched_cache.rb +109 -0
  52. data/lib/right_agent/dispatcher.rb +272 -0
  53. data/lib/right_agent/enrollment_result.rb +221 -0
  54. data/lib/right_agent/exceptions.rb +87 -0
  55. data/lib/right_agent/history.rb +145 -0
  56. data/lib/right_agent/log.rb +460 -0
  57. data/lib/right_agent/minimal.rb +46 -0
  58. data/lib/right_agent/monkey_patches.rb +30 -0
  59. data/lib/right_agent/monkey_patches/ruby_patch.rb +55 -0
  60. data/lib/right_agent/monkey_patches/ruby_patch/array_patch.rb +29 -0
  61. data/lib/right_agent/monkey_patches/ruby_patch/darwin_patch.rb +24 -0
  62. data/lib/right_agent/monkey_patches/ruby_patch/linux_patch.rb +24 -0
  63. data/lib/right_agent/monkey_patches/ruby_patch/linux_patch/file_patch.rb +30 -0
  64. data/lib/right_agent/monkey_patches/ruby_patch/object_patch.rb +49 -0
  65. data/lib/right_agent/monkey_patches/ruby_patch/windows_patch.rb +32 -0
  66. data/lib/right_agent/monkey_patches/ruby_patch/windows_patch/file_patch.rb +60 -0
  67. data/lib/right_agent/monkey_patches/ruby_patch/windows_patch/process_patch.rb +63 -0
  68. data/lib/right_agent/monkey_patches/ruby_patch/windows_patch/stdio_patch.rb +27 -0
  69. data/lib/right_agent/monkey_patches/ruby_patch/windows_patch/time_patch.rb +55 -0
  70. data/lib/right_agent/monkey_patches/ruby_patch/windows_patch/win32ole_patch.rb +34 -0
  71. data/lib/right_agent/multiplexer.rb +102 -0
  72. data/lib/right_agent/offline_handler.rb +270 -0
  73. data/lib/right_agent/operation_result.rb +300 -0
  74. data/lib/right_agent/packets.rb +673 -0
  75. data/lib/right_agent/payload_formatter.rb +104 -0
  76. data/lib/right_agent/pending_requests.rb +128 -0
  77. data/lib/right_agent/pid_file.rb +159 -0
  78. data/lib/right_agent/platform.rb +770 -0
  79. data/lib/right_agent/platform/unix/darwin/platform.rb +102 -0
  80. data/lib/right_agent/platform/unix/linux/platform.rb +305 -0
  81. data/lib/right_agent/platform/unix/platform.rb +226 -0
  82. data/lib/right_agent/platform/windows/mingw/platform.rb +447 -0
  83. data/lib/right_agent/platform/windows/mswin/platform.rb +236 -0
  84. data/lib/right_agent/platform/windows/platform.rb +1808 -0
  85. data/lib/right_agent/protocol_version_mixin.rb +69 -0
  86. data/lib/right_agent/retryable_request.rb +195 -0
  87. data/lib/right_agent/scripts/agent_controller.rb +543 -0
  88. data/lib/right_agent/scripts/agent_deployer.rb +400 -0
  89. data/lib/right_agent/scripts/common_parser.rb +160 -0
  90. data/lib/right_agent/scripts/log_level_manager.rb +192 -0
  91. data/lib/right_agent/scripts/stats_manager.rb +268 -0
  92. data/lib/right_agent/scripts/usage.rb +58 -0
  93. data/lib/right_agent/secure_identity.rb +92 -0
  94. data/lib/right_agent/security.rb +32 -0
  95. data/lib/right_agent/security/cached_certificate_store_proxy.rb +77 -0
  96. data/lib/right_agent/security/certificate.rb +102 -0
  97. data/lib/right_agent/security/certificate_cache.rb +89 -0
  98. data/lib/right_agent/security/distinguished_name.rb +56 -0
  99. data/lib/right_agent/security/encrypted_document.rb +83 -0
  100. data/lib/right_agent/security/rsa_key_pair.rb +76 -0
  101. data/lib/right_agent/security/signature.rb +86 -0
  102. data/lib/right_agent/security/static_certificate_store.rb +85 -0
  103. data/lib/right_agent/sender.rb +792 -0
  104. data/lib/right_agent/serialize.rb +29 -0
  105. data/lib/right_agent/serialize/message_pack.rb +107 -0
  106. data/lib/right_agent/serialize/secure_serializer.rb +151 -0
  107. data/lib/right_agent/serialize/secure_serializer_initializer.rb +47 -0
  108. data/lib/right_agent/serialize/serializable.rb +151 -0
  109. data/lib/right_agent/serialize/serializer.rb +159 -0
  110. data/lib/right_agent/subprocess.rb +38 -0
  111. data/lib/right_agent/tracer.rb +124 -0
  112. data/right_agent.gemspec +101 -0
  113. data/spec/actor_registry_spec.rb +80 -0
  114. data/spec/actor_spec.rb +162 -0
  115. data/spec/agent_config_spec.rb +235 -0
  116. data/spec/agent_identity_spec.rb +78 -0
  117. data/spec/agent_spec.rb +734 -0
  118. data/spec/agent_tag_manager_spec.rb +319 -0
  119. data/spec/clients/api_client_spec.rb +423 -0
  120. data/spec/clients/auth_client_spec.rb +272 -0
  121. data/spec/clients/balanced_http_client_spec.rb +576 -0
  122. data/spec/clients/base_retry_client_spec.rb +635 -0
  123. data/spec/clients/router_client_spec.rb +594 -0
  124. data/spec/clients/spec_helper.rb +111 -0
  125. data/spec/command/agent_manager_commands_spec.rb +51 -0
  126. data/spec/command/command_io_spec.rb +93 -0
  127. data/spec/command/command_parser_spec.rb +79 -0
  128. data/spec/command/command_runner_spec.rb +107 -0
  129. data/spec/command/command_serializer_spec.rb +51 -0
  130. data/spec/connectivity_checker_spec.rb +83 -0
  131. data/spec/core_payload_types/dev_repositories_spec.rb +64 -0
  132. data/spec/core_payload_types/dev_repository_spec.rb +33 -0
  133. data/spec/core_payload_types/executable_bundle_spec.rb +67 -0
  134. data/spec/core_payload_types/login_user_spec.rb +102 -0
  135. data/spec/core_payload_types/recipe_instantiation_spec.rb +81 -0
  136. data/spec/core_payload_types/right_script_attachment_spec.rb +65 -0
  137. data/spec/core_payload_types/right_script_instantiation_spec.rb +79 -0
  138. data/spec/core_payload_types/spec_helper.rb +23 -0
  139. data/spec/dispatched_cache_spec.rb +136 -0
  140. data/spec/dispatcher_spec.rb +324 -0
  141. data/spec/enrollment_result_spec.rb +53 -0
  142. data/spec/history_spec.rb +246 -0
  143. data/spec/log_spec.rb +192 -0
  144. data/spec/monkey_patches/eventmachine_spec.rb +62 -0
  145. data/spec/multiplexer_spec.rb +48 -0
  146. data/spec/offline_handler_spec.rb +340 -0
  147. data/spec/operation_result_spec.rb +208 -0
  148. data/spec/packets_spec.rb +461 -0
  149. data/spec/pending_requests_spec.rb +136 -0
  150. data/spec/platform/spec_helper.rb +216 -0
  151. data/spec/platform/unix/darwin/platform_spec.rb +181 -0
  152. data/spec/platform/unix/linux/platform_spec.rb +540 -0
  153. data/spec/platform/unix/spec_helper.rb +149 -0
  154. data/spec/platform/windows/mingw/platform_spec.rb +222 -0
  155. data/spec/platform/windows/mswin/platform_spec.rb +259 -0
  156. data/spec/platform/windows/spec_helper.rb +720 -0
  157. data/spec/retryable_request_spec.rb +306 -0
  158. data/spec/secure_identity_spec.rb +50 -0
  159. data/spec/security/cached_certificate_store_proxy_spec.rb +62 -0
  160. data/spec/security/certificate_cache_spec.rb +71 -0
  161. data/spec/security/certificate_spec.rb +49 -0
  162. data/spec/security/distinguished_name_spec.rb +46 -0
  163. data/spec/security/encrypted_document_spec.rb +55 -0
  164. data/spec/security/rsa_key_pair_spec.rb +55 -0
  165. data/spec/security/signature_spec.rb +66 -0
  166. data/spec/security/static_certificate_store_spec.rb +58 -0
  167. data/spec/sender_spec.rb +1045 -0
  168. data/spec/serialize/message_pack_spec.rb +131 -0
  169. data/spec/serialize/secure_serializer_spec.rb +132 -0
  170. data/spec/serialize/serializable_spec.rb +90 -0
  171. data/spec/serialize/serializer_spec.rb +197 -0
  172. data/spec/spec.opts +2 -0
  173. data/spec/spec.win32.opts +1 -0
  174. data/spec/spec_helper.rb +130 -0
  175. data/spec/tracer_spec.rb +114 -0
  176. metadata +447 -0
@@ -0,0 +1,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