right_link 5.9.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (199) hide show
  1. data/actors/agent_manager.rb +88 -0
  2. data/actors/instance_scheduler.rb +321 -0
  3. data/actors/instance_services.rb +64 -0
  4. data/actors/instance_setup.rb +567 -0
  5. data/bin/cloud +25 -0
  6. data/bin/cook_runner +44 -0
  7. data/bin/deploy +120 -0
  8. data/bin/enroll +385 -0
  9. data/bin/rad +32 -0
  10. data/bin/rchk +29 -0
  11. data/bin/rnac +39 -0
  12. data/bin/rs_connect +33 -0
  13. data/bin/rs_log_level +31 -0
  14. data/bin/rs_ohai +28 -0
  15. data/bin/rs_reenroll +31 -0
  16. data/bin/rs_run_recipe +34 -0
  17. data/bin/rs_run_right_script +34 -0
  18. data/bin/rs_shutdown +33 -0
  19. data/bin/rs_tag +33 -0
  20. data/bin/rs_thunk +33 -0
  21. data/bin/rstat +31 -0
  22. data/bin/system +16 -0
  23. data/ext/Rakefile +18 -0
  24. data/init/config.yml +5 -0
  25. data/init/init.rb +79 -0
  26. data/lib/chef/ohai_setup.rb +51 -0
  27. data/lib/chef/plugins/cloud.rb +91 -0
  28. data/lib/chef/plugins/cloudstack.rb +23 -0
  29. data/lib/chef/plugins/ec2.rb +23 -0
  30. data/lib/chef/plugins/linux/block_device2.rb +24 -0
  31. data/lib/chef/plugins/rackspace.rb +23 -0
  32. data/lib/chef/plugins/rightscale.rb +125 -0
  33. data/lib/chef/plugins/windows/network.rb +114 -0
  34. data/lib/chef/plugins.rb +74 -0
  35. data/lib/chef/providers/dns_dnsmadeeasy_provider.rb +81 -0
  36. data/lib/chef/providers/dns_resource.rb +100 -0
  37. data/lib/chef/providers/executable_schedule_provider.rb +70 -0
  38. data/lib/chef/providers/executable_schedule_resource.rb +144 -0
  39. data/lib/chef/providers/remote_recipe_provider.rb +86 -0
  40. data/lib/chef/providers/remote_recipe_resource.rb +101 -0
  41. data/lib/chef/providers/right_link_tag_provider.rb +73 -0
  42. data/lib/chef/providers/right_link_tag_resource.rb +59 -0
  43. data/lib/chef/providers/right_script_provider.rb +190 -0
  44. data/lib/chef/providers/right_script_resource.rb +113 -0
  45. data/lib/chef/providers/rs_shutdown_provider.rb +75 -0
  46. data/lib/chef/providers/rs_shutdown_resource.rb +55 -0
  47. data/lib/chef/providers/server_collection_provider.rb +66 -0
  48. data/lib/chef/providers/server_collection_resource.rb +93 -0
  49. data/lib/chef/providers/windows/powershell_provider.rb +151 -0
  50. data/lib/chef/providers/windows/powershell_resource.rb +111 -0
  51. data/lib/chef/providers/windows/unsupported_provider.rb +51 -0
  52. data/lib/chef/right_providers.rb +55 -0
  53. data/lib/chef/windows/ChefNodeCmdlet/ChefNodeCmdlet/ChefNodeCmdlet.csproj +104 -0
  54. data/lib/chef/windows/ChefNodeCmdlet/ChefNodeCmdlet/ChefNodeCmdlet.dll-Help.xml +141 -0
  55. data/lib/chef/windows/ChefNodeCmdlet/ChefNodeCmdlet/Exceptions.cs +182 -0
  56. data/lib/chef/windows/ChefNodeCmdlet/ChefNodeCmdlet/GetChefNodeCommand.cs +58 -0
  57. data/lib/chef/windows/ChefNodeCmdlet/ChefNodeCmdlet/GetChefNodeRequest.cs +46 -0
  58. data/lib/chef/windows/ChefNodeCmdlet/ChefNodeCmdlet/GetChefNodeResponse.cs +45 -0
  59. data/lib/chef/windows/ChefNodeCmdlet/ChefNodeCmdlet/GetCurrentResourceCommand.cs +58 -0
  60. data/lib/chef/windows/ChefNodeCmdlet/ChefNodeCmdlet/GetCurrentResourceRequest.cs +46 -0
  61. data/lib/chef/windows/ChefNodeCmdlet/ChefNodeCmdlet/GetCurrentResourceResponse.cs +45 -0
  62. data/lib/chef/windows/ChefNodeCmdlet/ChefNodeCmdlet/GetNewResourceCommand.cs +58 -0
  63. data/lib/chef/windows/ChefNodeCmdlet/ChefNodeCmdlet/GetNewResourceRequest.cs +46 -0
  64. data/lib/chef/windows/ChefNodeCmdlet/ChefNodeCmdlet/GetNewResourceResponse.cs +45 -0
  65. data/lib/chef/windows/ChefNodeCmdlet/ChefNodeCmdlet/GetNextActionCommand.cs +178 -0
  66. data/lib/chef/windows/ChefNodeCmdlet/ChefNodeCmdlet/GetNextActionRequest.cs +67 -0
  67. data/lib/chef/windows/ChefNodeCmdlet/ChefNodeCmdlet/GetNextActionResponse.cs +58 -0
  68. data/lib/chef/windows/ChefNodeCmdlet/ChefNodeCmdlet/GetNodeValueCommandBase.cs +142 -0
  69. data/lib/chef/windows/ChefNodeCmdlet/ChefNodeCmdlet/GetNodeValueRequestBase.cs +64 -0
  70. data/lib/chef/windows/ChefNodeCmdlet/ChefNodeCmdlet/GetNodeValueResponseBase.cs +69 -0
  71. data/lib/chef/windows/ChefNodeCmdlet/ChefNodeCmdlet/JsonTransport.cs +110 -0
  72. data/lib/chef/windows/ChefNodeCmdlet/ChefNodeCmdlet/PipeClient.cs +158 -0
  73. data/lib/chef/windows/ChefNodeCmdlet/ChefNodeCmdlet/PipeServer.cs +142 -0
  74. data/lib/chef/windows/ChefNodeCmdlet/ChefNodeCmdlet/Properties/AssemblyInfo.cs +16 -0
  75. data/lib/chef/windows/ChefNodeCmdlet/ChefNodeCmdlet/ProtocolConstants.cs +55 -0
  76. data/lib/chef/windows/ChefNodeCmdlet/ChefNodeCmdlet/ProtocolUtilities.cs +77 -0
  77. data/lib/chef/windows/ChefNodeCmdlet/ChefNodeCmdlet/ReadMe.txt +53 -0
  78. data/lib/chef/windows/ChefNodeCmdlet/ChefNodeCmdlet/SetChefNodeCommand.cs +59 -0
  79. data/lib/chef/windows/ChefNodeCmdlet/ChefNodeCmdlet/SetChefNodeRequest.cs +46 -0
  80. data/lib/chef/windows/ChefNodeCmdlet/ChefNodeCmdlet/SetChefNodeResponse.cs +58 -0
  81. data/lib/chef/windows/ChefNodeCmdlet/ChefNodeCmdlet/SetCurrentResourceCommand.cs +59 -0
  82. data/lib/chef/windows/ChefNodeCmdlet/ChefNodeCmdlet/SetCurrentResourceRequest.cs +46 -0
  83. data/lib/chef/windows/ChefNodeCmdlet/ChefNodeCmdlet/SetCurrentResourceResponse.cs +40 -0
  84. data/lib/chef/windows/ChefNodeCmdlet/ChefNodeCmdlet/SetNewResourceCommand.cs +59 -0
  85. data/lib/chef/windows/ChefNodeCmdlet/ChefNodeCmdlet/SetNewResourceRequest.cs +46 -0
  86. data/lib/chef/windows/ChefNodeCmdlet/ChefNodeCmdlet/SetNewResourceResponse.cs +40 -0
  87. data/lib/chef/windows/ChefNodeCmdlet/ChefNodeCmdlet/SetNodeValueCommandBase.cs +293 -0
  88. data/lib/chef/windows/ChefNodeCmdlet/ChefNodeCmdlet/SetNodeValueRequestBase.cs +75 -0
  89. data/lib/chef/windows/ChefNodeCmdlet/ChefNodeCmdlet/SetNodeValueResponseBase.cs +45 -0
  90. data/lib/chef/windows/ChefNodeCmdlet/ChefNodeCmdlet/Transport.cs +91 -0
  91. data/lib/chef/windows/ChefNodeCmdlet/ChefNodeCmdlet.sln +35 -0
  92. data/lib/chef/windows/ChefNodeCmdlet/TestChefNodeCmdlet/Program.cs +374 -0
  93. data/lib/chef/windows/ChefNodeCmdlet/TestChefNodeCmdlet/Properties/AssemblyInfo.cs +16 -0
  94. data/lib/chef/windows/ChefNodeCmdlet/TestChefNodeCmdlet/TestChefNodeCmdlet.csproj +65 -0
  95. data/lib/chef/windows/ChefNodeCmdlet/TestNextActionCmdlet/Program.cs +136 -0
  96. data/lib/chef/windows/ChefNodeCmdlet/TestNextActionCmdlet/Properties/AssemblyInfo.cs +36 -0
  97. data/lib/chef/windows/ChefNodeCmdlet/TestNextActionCmdlet/ReadMe.txt +46 -0
  98. data/lib/chef/windows/ChefNodeCmdlet/TestNextActionCmdlet/TestNextActionCmdlet.csproj +68 -0
  99. data/lib/chef/windows/bin/Newtonsoft.Json.dll +0 -0
  100. data/lib/chef/windows/chef_node_server.rb +463 -0
  101. data/lib/chef/windows/dynamic_powershell_provider.rb +296 -0
  102. data/lib/chef/windows/pipe_server.rb +283 -0
  103. data/lib/chef/windows/powershell_host.rb +285 -0
  104. data/lib/chef/windows/powershell_pipe_server.rb +136 -0
  105. data/lib/chef/windows/powershell_provider_base.rb +92 -0
  106. data/lib/chef/windows/scripts/run_loop.ps1 +105 -0
  107. data/lib/clouds/cloud.rb +557 -0
  108. data/lib/clouds/cloud_factory.rb +250 -0
  109. data/lib/clouds/cloud_utilities.rb +244 -0
  110. data/lib/clouds/clouds/azure.rb +106 -0
  111. data/lib/clouds/clouds/cloudstack.rb +114 -0
  112. data/lib/clouds/clouds/ec2.rb +113 -0
  113. data/lib/clouds/clouds/eucalyptus.rb +46 -0
  114. data/lib/clouds/clouds/google.rb +102 -0
  115. data/lib/clouds/clouds/none.rb +76 -0
  116. data/lib/clouds/clouds/openstack.rb +30 -0
  117. data/lib/clouds/clouds/rackspace-ng.rb +54 -0
  118. data/lib/clouds/clouds/rackspace.rb +78 -0
  119. data/lib/clouds/clouds/softlayer.rb +91 -0
  120. data/lib/clouds/metadata_formatter.rb +108 -0
  121. data/lib/clouds/metadata_provider.rb +128 -0
  122. data/lib/clouds/metadata_source.rb +87 -0
  123. data/lib/clouds/metadata_sources/certificate_metadata_source.rb +207 -0
  124. data/lib/clouds/metadata_sources/config_drive_metadata_source.rb +129 -0
  125. data/lib/clouds/metadata_sources/file_metadata_source.rb +74 -0
  126. data/lib/clouds/metadata_sources/http_metadata_source.rb +277 -0
  127. data/lib/clouds/metadata_sources/selective_metadata_source.rb +122 -0
  128. data/lib/clouds/metadata_tree_climber.rb +144 -0
  129. data/lib/clouds/metadata_writer.rb +155 -0
  130. data/lib/clouds/metadata_writers/dictionary_metadata_writer.rb +72 -0
  131. data/lib/clouds/metadata_writers/ruby_metadata_writer.rb +76 -0
  132. data/lib/clouds/metadata_writers/shell_metadata_writer.rb +121 -0
  133. data/lib/clouds/register_clouds.rb +34 -0
  134. data/lib/clouds.rb +32 -0
  135. data/lib/gem_dependencies.rb +83 -0
  136. data/lib/git_hooks/commit-msg.rb +7 -0
  137. data/lib/instance/agent_config.rb +168 -0
  138. data/lib/instance/agent_watcher.rb +233 -0
  139. data/lib/instance/audit_cook_stub.rb +104 -0
  140. data/lib/instance/audit_proxy.rb +247 -0
  141. data/lib/instance/bundle_queue.rb +104 -0
  142. data/lib/instance/cook/agent_connection.rb +109 -0
  143. data/lib/instance/cook/audit_logger.rb +165 -0
  144. data/lib/instance/cook/audit_stub.rb +142 -0
  145. data/lib/instance/cook/ca-bundle.crt +2794 -0
  146. data/lib/instance/cook/chef_state.rb +211 -0
  147. data/lib/instance/cook/cook.rb +306 -0
  148. data/lib/instance/cook/cook_state.rb +298 -0
  149. data/lib/instance/cook/cookbook_path_mapping.rb +66 -0
  150. data/lib/instance/cook/cookbook_repo_retriever.rb +190 -0
  151. data/lib/instance/cook/executable_sequence.rb +765 -0
  152. data/lib/instance/cook/external_parameter_gatherer.rb +190 -0
  153. data/lib/instance/cook/repose_downloader.rb +349 -0
  154. data/lib/instance/cook/shutdown_request_proxy.rb +121 -0
  155. data/lib/instance/cook.rb +41 -0
  156. data/lib/instance/downloader.rb +208 -0
  157. data/lib/instance/duplicable.rb +67 -0
  158. data/lib/instance/exceptions.rb +49 -0
  159. data/lib/instance/executable_sequence_proxy.rb +278 -0
  160. data/lib/instance/instance_commands.rb +577 -0
  161. data/lib/instance/instance_state.rb +633 -0
  162. data/lib/instance/json_utilities.rb +102 -0
  163. data/lib/instance/login_manager.rb +533 -0
  164. data/lib/instance/login_user_manager.rb +522 -0
  165. data/lib/instance/message_encoder.rb +118 -0
  166. data/lib/instance/multi_thread_bundle_queue.rb +232 -0
  167. data/lib/instance/operation_context.rb +60 -0
  168. data/lib/instance/options_bag.rb +65 -0
  169. data/lib/instance/payload_formatter.rb +46 -0
  170. data/lib/instance/policy.rb +53 -0
  171. data/lib/instance/policy_audit.rb +100 -0
  172. data/lib/instance/policy_manager.rb +146 -0
  173. data/lib/instance/reenroll_manager.rb +104 -0
  174. data/lib/instance/right_scripts_cookbook.rb +181 -0
  175. data/lib/instance/shutdown_request.rb +221 -0
  176. data/lib/instance/single_thread_bundle_queue.rb +189 -0
  177. data/lib/instance/volume_management.rb +450 -0
  178. data/lib/instance.rb +50 -0
  179. data/lib/repo_conf_generators/apt_conf_generators.rb +106 -0
  180. data/lib/repo_conf_generators/gem_conf_generators.rb +80 -0
  181. data/lib/repo_conf_generators/rightscale_conf_generators.rb +254 -0
  182. data/lib/repo_conf_generators/rightscale_key.pub +17 -0
  183. data/lib/repo_conf_generators/yum_conf_generators.rb +225 -0
  184. data/lib/repo_conf_generators.rb +30 -0
  185. data/lib/run_shell.rb +28 -0
  186. data/scripts/agent_checker.rb +571 -0
  187. data/scripts/agent_controller.rb +247 -0
  188. data/scripts/agent_deployer.rb +148 -0
  189. data/scripts/bundle_runner.rb +336 -0
  190. data/scripts/cloud_controller.rb +176 -0
  191. data/scripts/log_level_manager.rb +142 -0
  192. data/scripts/ohai_runner.rb +33 -0
  193. data/scripts/reenroller.rb +193 -0
  194. data/scripts/server_importer.rb +293 -0
  195. data/scripts/shutdown_client.rb +183 -0
  196. data/scripts/system_configurator.rb +367 -0
  197. data/scripts/tagger.rb +381 -0
  198. data/scripts/thunker.rb +356 -0
  199. metadata +418 -0
@@ -0,0 +1,250 @@
1
+ #
2
+ # Copyright (c) 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
+
24
+ module RightScale
25
+
26
+ # Singleton for registering and instantiating clouds.
27
+ class CloudFactory
28
+
29
+ include RightSupport::Ruby::EasySingleton
30
+
31
+ # the unknown cloud is used to automatically detect current instance's cloud
32
+ UNKNOWN_CLOUD_NAME = :unknown
33
+
34
+ # exceptions
35
+ class UnknownCloud < Exception; end
36
+
37
+ # Registry method for a dynamic metadata type.
38
+ #
39
+ # === Parameters
40
+ # cloud_names(Array|String):: name of one or more clouds (which may include DEFAULT_CLOUD) that use the given type
41
+ # cloud_script_path(String):: path to script used to describe cloud on creation
42
+ #
43
+ # === Return
44
+ # always true
45
+ def register(cloud_names, cloud_script_path)
46
+ # relies on each to split on newlines for strings and otherwise do each for collections.
47
+ cloud_script_path = File.normalize_path(cloud_script_path)
48
+ cloud_names.each { |cloud_name| registered_type(cloud_name, cloud_script_path) }
49
+ true
50
+ end
51
+
52
+ # Resets the global cloud registry (to ensure a clean reload of cloud names).
53
+ #
54
+ # === Return
55
+ # result(Hash):: Hash of cloud names to script paths before reset
56
+ def reset_registry
57
+ result = @names_to_script_paths
58
+ @names_to_script_paths = nil
59
+ result
60
+ end
61
+
62
+ # Gets the path to the script describing a cloud.
63
+ #
64
+ # === Parameters
65
+ # cloud_name(String):: a registered_type cloud name
66
+ #
67
+ # === Return
68
+ # cloud_script_path(String):: path to script used to describe cloud on creation
69
+ #
70
+ # === Raise
71
+ # UnknownCloud:: on error
72
+ def registered_script_path(cloud_name)
73
+ cloud_script_path = registered_type(cloud_name)
74
+ raise UnknownCloud.new("Unknown cloud: #{cloud_name}") unless cloud_script_path
75
+ return cloud_script_path
76
+ end
77
+
78
+ # Factory method for dynamic metadata types.
79
+ #
80
+ # === Parameters
81
+ # cloud(String):: a registered_type cloud name
82
+ # options(Hash):: options for creation
83
+ #
84
+ # === Return
85
+ # result(Object):: new instance of registered_type metadata type
86
+ #
87
+ # === Raise
88
+ # UnknownCloud:: on error
89
+ def create(cloud_name, options)
90
+ raise ArgumentError.new("cloud_name is required") if cloud_name.to_s.empty?
91
+ raise ArgumentError.new("options[:logger] is required") unless logger = options[:logger]
92
+ raise UnknownCloud.new("No cloud definitions available.") unless @names_to_script_paths
93
+ cloud_name = cloud_name.to_sym
94
+ cloud_name = default_cloud_name if UNKNOWN_CLOUD_NAME == cloud_name
95
+ if UNKNOWN_CLOUD_NAME == cloud_name
96
+ # persist default cloud name after successful detection.
97
+ cloud = detect_cloud(options)
98
+ raise UnknownCloud.new("Unable to determine a default cloud") unless cloud
99
+ default_cloud_name(cloud.name)
100
+ return cloud
101
+ end
102
+ cloud_script_path = registered_script_path(cloud_name)
103
+ options = options.dup
104
+ options[:name] ||= cloud_name.to_s
105
+ options[:script_path] = cloud_script_path
106
+ cloud = Cloud.new(options)
107
+ text = File.read(cloud_script_path)
108
+ cloud.instance_eval(text)
109
+ cloud.abbreviation(cloud_name) unless cloud.abbreviation
110
+ extend_cloud_by_scripts(cloud, logger)
111
+
112
+ # finalize defaults only after all cloud definitions have been evaluated
113
+ # by the new cloud object.
114
+ cloud.finalize_default_options
115
+ return cloud
116
+ end
117
+
118
+ # Setter/getter for the default cloud name. This currently relies on a
119
+ # 'cloud file' which must be present in an expected RightScale location.
120
+ #
121
+ # === Parameters
122
+ # value(String|Token):: default cloud name or nil
123
+ #
124
+ # === Return
125
+ # result(String):: default cloud name or nil
126
+ def default_cloud_name(value = nil)
127
+ cloud_file_path = RightScale::AgentConfig.cloud_file_path
128
+ if value
129
+ parent_dir = File.dirname(cloud_file_path)
130
+ FileUtils.mkdir_p(parent_dir) unless File.directory?(parent_dir)
131
+ File.open(cloud_file_path, "w") { |f| f.write(value.to_s) }
132
+ else
133
+ value = File.read(cloud_file_path).strip if File.file?(cloud_file_path)
134
+ end
135
+ value.to_s.empty? ? UNKNOWN_CLOUD_NAME : value
136
+ end
137
+
138
+ # Attempts to detect the current instance's cloud by instantiating the
139
+ # various known clouds and running their detection methods.
140
+ #
141
+ # === Parameters
142
+ # options(Hash):: options for creation or empty
143
+ #
144
+ # === Return
145
+ # cloud(Cloud):: detected cloud or nil
146
+ def detect_cloud(options)
147
+ @names_to_script_paths.each_key do |cloud_name|
148
+ begin
149
+ cloud = create(cloud_name, options)
150
+ return cloud if cloud.is_current_cloud?
151
+ rescue Exception
152
+ # ignore failures and proceed to detecting next cloud, if any.
153
+ end
154
+ end
155
+ nil
156
+ end
157
+
158
+ # Normalizes a cloud name to ensure all variants are resolvable.
159
+ #
160
+ # === Parameters
161
+ # cloud_name(String):: cloud name
162
+ #
163
+ # === Return
164
+ # result(String):: normalized cloud name
165
+ def self.normalize_cloud_name(cloud_name)
166
+ return cloud_name.to_s.strip.downcase
167
+ end
168
+
169
+ protected
170
+
171
+ # Getter/setter for cloud types registered clouds.
172
+ #
173
+ # === Parameters
174
+ # name(String):: name of cloud
175
+ # script_path(String):: path to script to evaluate when creating cloud
176
+ #
177
+ # === Return
178
+ # result(Hash):: hash in form {:name => <name>, :script_path => <script_path>} or nil
179
+ def registered_type(cloud_name, cloud_script_path = nil)
180
+ raise ArgumentError.new("cloud_name is required") unless cloud_name
181
+ key = self.class.normalize_cloud_name(cloud_name).to_sym
182
+ @names_to_script_paths ||= {}
183
+ @names_to_script_paths[key] ||= cloud_script_path
184
+ end
185
+
186
+ # Supports runtime extension of the cloud object by external scripts which
187
+ # are associated with instance methods. These scripts can also override the
188
+ # predefined methods (e.g. write_metadata) to further customize a cloud's
189
+ # behavior on a given instance. It may also be better to run some complex
190
+ # operation in a child process instead of in the process which is loading
191
+ # the cloud object.
192
+ #
193
+ # === Parameters
194
+ # @param [String] cloud as a registered_type or UNKNOWN_CLOUD_NAME
195
+ # @param [Logger] logger
196
+ def extend_cloud_by_scripts(cloud, logger)
197
+ # search for script directories based first on any clouds which were
198
+ # extended by the cloud and then by the exact cloud name.
199
+ cloud_name = cloud.name.to_s
200
+ cloud_aliases = cloud.extended_clouds + [cloud_name]
201
+
202
+ search_paths = []
203
+ cloud_aliases.each do |cloud_alias|
204
+ # first add default search path for cloud name.
205
+ search_path = File.join(RightScale::Platform.filesystem.private_bin_dir, cloud_alias)
206
+ if File.directory?(search_path)
207
+ search_paths << search_path
208
+ logger.debug("Added #{search_path.inspect} to search path for extension scripts.")
209
+ else
210
+ logger.debug("Optional extension script dir #{search_path.inspect} does not exist.")
211
+ end
212
+
213
+ # custom paths are last in order to supercede any preceeding extensions.
214
+ cloud.extension_script_base_paths.each do |base_path|
215
+ search_path = File.join(base_path, cloud_alias)
216
+ if File.directory?(search_path)
217
+ search_paths << search_path
218
+ logger.debug("Added #{search_path.inspect} to search path for extension scripts.")
219
+ else
220
+ logger.debug("Optional extension script dir #{search_path.inspect} does not exist.")
221
+ end
222
+ end
223
+ end
224
+
225
+ # inject any scripts discovered in script paths as instance methods which
226
+ # return the result of calling the external script.
227
+ search_paths.each do |search_path|
228
+ search_path = File.normalize_path(search_path)
229
+ Dir.glob(File.join(search_path, "*")).each do |script_path|
230
+ script_ext = File.extname(script_path)
231
+ script_name = File.basename(script_path, script_ext)
232
+
233
+ # ignore any script names which contain strange characters (like
234
+ # semicolon) for security reasons.
235
+ if script_name =~ /^[_A-Za-z][_A-Za-z0-9]*$/
236
+ logger.debug("Extending #{cloud_name} from #{script_path.inspect}")
237
+ eval_me = <<EOF
238
+ def #{script_name}(*arguments)
239
+ return execute_script(\"#{script_path}\", *arguments)
240
+ end
241
+ EOF
242
+ cloud.instance_eval(eval_me)
243
+ end
244
+ end
245
+ end
246
+ end
247
+
248
+ end # CloudFactory
249
+
250
+ end # RightScale
@@ -0,0 +1,244 @@
1
+ #
2
+ # Copyright (c) 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
+ require 'open-uri'
24
+ require 'socket'
25
+
26
+ module RightScale
27
+
28
+ class CloudUtilities
29
+
30
+ IP_ADDRESS_REGEX = /^(?:(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\.){3}(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)$/
31
+
32
+ DEFAULT_WHATS_MY_IP_HOST_NAME = 'eip-us-east.rightscale.com'
33
+ DEFAULT_WHATS_MY_IP_TIMEOUT = 10 * 60
34
+ DEFAULT_WHATS_MY_IP_RETRY_DELAY = 5
35
+
36
+ # Does an interface have the given mac address?
37
+ #
38
+ # === Parameters
39
+ # ohai(Mash):: ohai state
40
+ # mac(String):: MAC address to find
41
+ #
42
+ # === Return
43
+ # (Boolean):: true if there is an interface with the given mac address
44
+ def self.has_mac?(ohai, mac)
45
+ !!ohai[:network][:interfaces].values.detect { |iface| !iface[:arp].nil? && iface[:arp].value?(mac) }
46
+ end
47
+
48
+ # Attempt to connect to the given address on the given port as a quick verification
49
+ # that the metadata service is available.
50
+ #
51
+ # === Parameters
52
+ # addr(String):: address of the metadata service
53
+ # port(Number):: port of the metadata service
54
+ # timeout(Number)::Optional - time to wait for a response
55
+ #
56
+ # === Return
57
+ # connected(Boolean):: true if a connection could be made, false otherwise
58
+ def self.can_contact_metadata_server?(addr, port, timeout=2)
59
+ t = Socket.new(Socket::Constants::AF_INET, Socket::Constants::SOCK_STREAM, 0)
60
+ saddr = Socket.pack_sockaddr_in(port, addr)
61
+ connected = false
62
+
63
+ begin
64
+ t.connect_nonblock(saddr)
65
+ rescue Errno::EINPROGRESS
66
+ r, w, e = IO::select(nil, [t], nil, timeout)
67
+ if !w.nil?
68
+ connected = true
69
+ else
70
+ begin
71
+ t.connect_nonblock(saddr)
72
+ rescue Errno::EISCONN
73
+ t.close
74
+ connected = true
75
+ rescue SystemCallError
76
+ end
77
+ end
78
+ rescue SystemCallError
79
+ end
80
+
81
+ connected
82
+ end
83
+
84
+ # Finds the first ip address for a given interface in the ohai mash
85
+ #
86
+ # === Parameters
87
+ # ohai(Mash):: ohai state
88
+ # interface(Symbol):: symbol of the interface (:eth0, :eth1, ...)
89
+ #
90
+ # === Return
91
+ # address(String|nil):: ip address associated with the given interface, nil if interface could not be found
92
+ def self.ip_for_interface(ohai, interface)
93
+ address = nil
94
+ if ohai[:network] != nil &&
95
+ ohai[:network][:interfaces] != nil &&
96
+ ohai[:network][:interfaces][interface] != nil &&
97
+ ohai[:network][:interfaces][interface][:addresses] != nil
98
+
99
+ addresses = ohai[:network][:interfaces][interface][:addresses].find { |key, item| item['family'] == 'inet' }
100
+ address = addresses.first unless addresses.nil?
101
+ end
102
+
103
+ address
104
+ end
105
+
106
+ # Finds the first ip address that matches a given connection type (private|public)
107
+ #
108
+ # === Parameters
109
+ # ohai(Mash):: ohai state
110
+ # connection_type(Symbol):: either 'public' or 'private'
111
+ #
112
+ # === Return
113
+ # address(String|nil):: ip address associated with the given interface, nil if interface could not be found
114
+ def self.ip_for_windows_interface(ohai, connection_type)
115
+ address = nil
116
+
117
+ # find the right interface
118
+ if ohai[:network] != nil &&
119
+ ohai[:network][:interfaces] != nil
120
+ interface = ohai[:network][:interfaces].values.find do |item|
121
+ !(item.nil? || item[:instance].nil?) && item[:instance][:net_connection_id] == connection_type
122
+ end
123
+
124
+ # grab the ip if there is one
125
+ if interface != nil &&
126
+ interface["configuration"] != nil &&
127
+ interface["configuration"]["ip_address"] != nil
128
+ address = interface["configuration"]["ip_address"].first
129
+ end
130
+ end
131
+
132
+ address
133
+ end
134
+
135
+ # Splits data on the given splitter character and merges to the given hash.
136
+ #
137
+ # === Parameters
138
+ # data(String):: raw data
139
+ # splitter(String):: splitter character
140
+ # hash(Hash):: hash to merge
141
+ # name_value_delimiter(String):: name/value delimiter (defaults to '=')
142
+ #
143
+ # === Return
144
+ # hash(Hash):: merged hash result
145
+ def self.split_metadata(data, splitter, hash, name_value_delimiter = '=')
146
+ data.split(splitter).each do |pair|
147
+ name, value = pair.split(name_value_delimiter, 2)
148
+ hash[name.strip] = value.strip if name && value
149
+ end
150
+ hash
151
+ end
152
+
153
+ # Queries a whats-my-ip service for the public IP address of this instance.
154
+ # can query either for an expected IP or for any IP which is voted by majority
155
+ # (or unanimously). this is no guarantee that an instance actually has a public
156
+ # IP individually assigned to it as a private cloud instance will still appear
157
+ # to have it's router's public IP as it's own address.
158
+ #
159
+ # === Parameters
160
+ # options[:expected_ip](String):: expected IP address or nil (no DNS names)
161
+ # options[:unanimous][TrueClass|FalseClass]:: true if vote must be unanimous, false for simple majority of all responders
162
+ # options[:host_name](String):: host name for whats-my-ip query or DEFAULT_WHATS_MY_IP_HOST_NAME
163
+ # options[:logger](Logger):: logger or defaults to null logger
164
+ # options[:timeout][Fixnum]:: timeout in seconds or DEFAULT_WHATS_MY_IP_TIMEOUT
165
+ # options[:retry_delay][Fixnum]:: retry delay in seconds or DEFAULT_WHATS_MY_IP_RETRY_DELAY
166
+ #
167
+ # === Return
168
+ # public_ip(String):: the consensus public IP for this instance or nil
169
+ def self.query_whats_my_ip(options={})
170
+ expected_ip = options[:expected_ip]
171
+ raise ArgumentError.new("expected_ip is invalid") if expected_ip && !(expected_ip =~ IP_ADDRESS_REGEX)
172
+ unanimous = options[:unanimous] || false
173
+ host_name = options[:host_name] || DEFAULT_WHATS_MY_IP_HOST_NAME
174
+ logger = options[:logger] || Logger.new(::RightScale::Platform::Shell::NULL_OUTPUT_NAME)
175
+ timeout = options[:timeout] || DEFAULT_WHATS_MY_IP_TIMEOUT
176
+ retry_delay = options[:retry_delay] || DEFAULT_WHATS_MY_IP_RETRY_DELAY
177
+
178
+ if expected_ip
179
+ logger.info("Waiting for IP=#{expected_ip}")
180
+ else
181
+ logger.info("Waiting for any IP to converge.")
182
+ end
183
+
184
+ # attempt to dig some hosts.
185
+ hosts = `dig +short #{host_name}`.strip.split
186
+ if hosts.empty?
187
+ logger.info("No hosts to poll for IP from #{host_name}.")
188
+ else
189
+ # a little randomization avoids hitting the same hosts from each
190
+ # instance since there is no guarantee that the hosts are returned in
191
+ # random order.
192
+ hosts = hosts.sort { (rand(2) * 2) - 1 }
193
+ if logger.debug?
194
+ message = ["Using these hosts to check the IP:"]
195
+ hosts.each { |host| message << " #{host}" }
196
+ message << "-------------------------"
197
+ logger.debug(message.join("\n"))
198
+ end
199
+
200
+ unanimity = hosts.count
201
+ required_votes = unanimous ? unanimity : (1 + unanimity / 2)
202
+ logger.info("Required votes = #{required_votes}/#{unanimity}")
203
+ end_time = Time.now + timeout
204
+ loop do
205
+ reported_ips_to_voters = {}
206
+ address_to_hosts = {}
207
+ hosts.each do |host|
208
+ address = `curl --max-time 1 -S -s http://#{host}/ip/mine`.strip
209
+ logger.debug("Host=#{host} reports IP=#{address}")
210
+ address_to_hosts[address] ||= []
211
+ address_to_hosts[address] << host
212
+ if expected_ip
213
+ vote = (address_to_hosts[expected_ip] || []).count
214
+ popular_address = expected_ip
215
+ else
216
+ popular_address = nil
217
+ vote = 0
218
+ address_to_hosts.each do |address, hosts|
219
+ if hosts.count > vote
220
+ vote = hosts.count
221
+ popular_address = address
222
+ end
223
+ end
224
+ end
225
+ if vote >= required_votes
226
+ logger.info("IP=#{popular_address} has the required vote count of #{required_votes}.")
227
+ return popular_address
228
+ end
229
+ end
230
+
231
+ # go around again, if possible.
232
+ now_time = Time.now
233
+ break if now_time >= end_time
234
+ retry_delay = [retry_delay, end_time - now_time].min.to_i
235
+ logger.debug("Sleeping for #{retry_delay} seconds...")
236
+ sleep retry_delay
237
+ retry_delay = [retry_delay * 2, 60].min # a little backoff helps when launching thousands
238
+ end
239
+ logger.info("Never got the required vote count of #{required_votes}/#{unanimity} after #{timeout} seconds; public IP did not converge.")
240
+ return nil
241
+ end
242
+ end
243
+ end
244
+ end
@@ -0,0 +1,106 @@
1
+ #
2
+ # Copyright (c) 2012 RightScale Inc
3
+ #
4
+ # Permission is hereby granted, free of charge, to any person obtaining
5
+ # a copy of this software and associated documentation files (the
6
+ # "Software"), to deal in the Software without restriction, including
7
+ # without limitation the rights to use, copy, modify, merge, publish,
8
+ # distribute, sublicense, and/or sell copies of the Software, and to
9
+ # permit persons to whom the Software is furnished to do so, subject to
10
+ # the following conditions:
11
+ #
12
+ # The above copyright notice and this permission notice shall be
13
+ # included in all copies or substantial portions of the Software.
14
+ #
15
+ # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
16
+ # EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
17
+ # MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
18
+ # NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
19
+ # LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
20
+ # OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
21
+ # WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
22
+
23
+ # Location for injected certificate
24
+ CERT_FILE = '/var/lib/waagent/Certificates.pem'
25
+
26
+ # Windows changes the ST=CA portion of our issuer name to S=CA at some point.
27
+ ISSUER_STATE_KEY = platform.windows? ? 'S' : 'ST'
28
+
29
+ # dependencies.
30
+ metadata_source 'metadata_sources/certificate_metadata_source'
31
+ metadata_writers 'metadata_writers/dictionary_metadata_writer',
32
+ 'metadata_writers/ruby_metadata_writer',
33
+ 'metadata_writers/shell_metadata_writer'
34
+
35
+ # set abbreviation for non-RS env var generation
36
+ abbreviation :waz
37
+
38
+ # Parses azure user metadata into a hash.
39
+ #
40
+ # === Parameters
41
+ # tree_climber(MetadataTreeClimber):: tree climber
42
+ # data(String):: raw data
43
+ #
44
+ # === Return
45
+ # result(Hash):: Hash-like leaf value
46
+ def create_user_metadata_leaf(tree_climber, data)
47
+ result = tree_climber.create_branch
48
+ ::RightScale::CloudUtilities.split_metadata(data.strip, '&', result)
49
+ result
50
+ end
51
+
52
+ # defaults.
53
+ default_option([:metadata_source, :user_metadata_cert_store], platform.windows? ? "cert:/LocalMachine/My" : CERT_FILE)
54
+ default_option([:metadata_source, :user_metadata_cert_issuer], "O=RightScale, C=US, #{ISSUER_STATE_KEY}=CA, CN=RightScale User Data")
55
+
56
+ default_option([:user_metadata, :metadata_tree_climber, :create_leaf_override], method(:create_user_metadata_leaf))
57
+
58
+ def wait_for_instance_ready
59
+ if platform.linux?
60
+ STDOUT.puts "Waiting for instance to appear ready."
61
+ until File.exist?(CERT_FILE) && File.mtime(CERT_FILE).to_f > platform.shell.booted_at.to_f do
62
+ sleep(1)
63
+ end
64
+ STDOUT.puts "Instance appears ready."
65
+ end
66
+ end
67
+
68
+ # Determines if the current instance is running on azure.
69
+ #
70
+ # === Return
71
+ # true if running on azure
72
+ def is_current_cloud?
73
+ # FIX: the presence of the user data cert isn't sufficient criteria to
74
+ # determine whether this is an azure instance. is there a mac address we can
75
+ # check against? in the meantime, just say no.
76
+ false
77
+ end
78
+
79
+ # Updates the given node with azure details.
80
+ #
81
+ # === Return
82
+ # always true
83
+ def update_details
84
+ details = {}
85
+ if ohai = @options[:ohai_node]
86
+ # FIX: there is currently no instance-facing API (i.e. an API which does not
87
+ # require management credentials) to provide the instance's public IP address
88
+ # so a workaround is required until the instance-facing API is available.
89
+ if public_ip = ::RightScale::CloudUtilities.query_whats_my_ip(:logger=>logger)
90
+ details[:public_ip] = public_ip
91
+ end
92
+ if platform.windows?
93
+ interface_names = ['Local Area Connection', # Windows Server 2008 R2
94
+ 'Ethernet'] # Windows Server 2012+ (?)
95
+ interface_names.each do |interface_name|
96
+ if ip = ::RightScale::CloudUtilities.ip_for_windows_interface(ohai, interface_name)
97
+ details[:private_ip] = ip
98
+ break
99
+ end
100
+ end
101
+ else
102
+ details[:private_ip] = ::RightScale::CloudUtilities.ip_for_interface(ohai, :eth0)
103
+ end
104
+ end
105
+ return details
106
+ end