right_link 5.9.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- data/actors/agent_manager.rb +88 -0
- data/actors/instance_scheduler.rb +321 -0
- data/actors/instance_services.rb +64 -0
- data/actors/instance_setup.rb +567 -0
- data/bin/cloud +25 -0
- data/bin/cook_runner +44 -0
- data/bin/deploy +120 -0
- data/bin/enroll +385 -0
- data/bin/rad +32 -0
- data/bin/rchk +29 -0
- data/bin/rnac +39 -0
- data/bin/rs_connect +33 -0
- data/bin/rs_log_level +31 -0
- data/bin/rs_ohai +28 -0
- data/bin/rs_reenroll +31 -0
- data/bin/rs_run_recipe +34 -0
- data/bin/rs_run_right_script +34 -0
- data/bin/rs_shutdown +33 -0
- data/bin/rs_tag +33 -0
- data/bin/rs_thunk +33 -0
- data/bin/rstat +31 -0
- data/bin/system +16 -0
- data/ext/Rakefile +18 -0
- data/init/config.yml +5 -0
- data/init/init.rb +79 -0
- data/lib/chef/ohai_setup.rb +51 -0
- data/lib/chef/plugins/cloud.rb +91 -0
- data/lib/chef/plugins/cloudstack.rb +23 -0
- data/lib/chef/plugins/ec2.rb +23 -0
- data/lib/chef/plugins/linux/block_device2.rb +24 -0
- data/lib/chef/plugins/rackspace.rb +23 -0
- data/lib/chef/plugins/rightscale.rb +125 -0
- data/lib/chef/plugins/windows/network.rb +114 -0
- data/lib/chef/plugins.rb +74 -0
- data/lib/chef/providers/dns_dnsmadeeasy_provider.rb +81 -0
- data/lib/chef/providers/dns_resource.rb +100 -0
- data/lib/chef/providers/executable_schedule_provider.rb +70 -0
- data/lib/chef/providers/executable_schedule_resource.rb +144 -0
- data/lib/chef/providers/remote_recipe_provider.rb +86 -0
- data/lib/chef/providers/remote_recipe_resource.rb +101 -0
- data/lib/chef/providers/right_link_tag_provider.rb +73 -0
- data/lib/chef/providers/right_link_tag_resource.rb +59 -0
- data/lib/chef/providers/right_script_provider.rb +190 -0
- data/lib/chef/providers/right_script_resource.rb +113 -0
- data/lib/chef/providers/rs_shutdown_provider.rb +75 -0
- data/lib/chef/providers/rs_shutdown_resource.rb +55 -0
- data/lib/chef/providers/server_collection_provider.rb +66 -0
- data/lib/chef/providers/server_collection_resource.rb +93 -0
- data/lib/chef/providers/windows/powershell_provider.rb +151 -0
- data/lib/chef/providers/windows/powershell_resource.rb +111 -0
- data/lib/chef/providers/windows/unsupported_provider.rb +51 -0
- data/lib/chef/right_providers.rb +55 -0
- data/lib/chef/windows/ChefNodeCmdlet/ChefNodeCmdlet/ChefNodeCmdlet.csproj +104 -0
- data/lib/chef/windows/ChefNodeCmdlet/ChefNodeCmdlet/ChefNodeCmdlet.dll-Help.xml +141 -0
- data/lib/chef/windows/ChefNodeCmdlet/ChefNodeCmdlet/Exceptions.cs +182 -0
- data/lib/chef/windows/ChefNodeCmdlet/ChefNodeCmdlet/GetChefNodeCommand.cs +58 -0
- data/lib/chef/windows/ChefNodeCmdlet/ChefNodeCmdlet/GetChefNodeRequest.cs +46 -0
- data/lib/chef/windows/ChefNodeCmdlet/ChefNodeCmdlet/GetChefNodeResponse.cs +45 -0
- data/lib/chef/windows/ChefNodeCmdlet/ChefNodeCmdlet/GetCurrentResourceCommand.cs +58 -0
- data/lib/chef/windows/ChefNodeCmdlet/ChefNodeCmdlet/GetCurrentResourceRequest.cs +46 -0
- data/lib/chef/windows/ChefNodeCmdlet/ChefNodeCmdlet/GetCurrentResourceResponse.cs +45 -0
- data/lib/chef/windows/ChefNodeCmdlet/ChefNodeCmdlet/GetNewResourceCommand.cs +58 -0
- data/lib/chef/windows/ChefNodeCmdlet/ChefNodeCmdlet/GetNewResourceRequest.cs +46 -0
- data/lib/chef/windows/ChefNodeCmdlet/ChefNodeCmdlet/GetNewResourceResponse.cs +45 -0
- data/lib/chef/windows/ChefNodeCmdlet/ChefNodeCmdlet/GetNextActionCommand.cs +178 -0
- data/lib/chef/windows/ChefNodeCmdlet/ChefNodeCmdlet/GetNextActionRequest.cs +67 -0
- data/lib/chef/windows/ChefNodeCmdlet/ChefNodeCmdlet/GetNextActionResponse.cs +58 -0
- data/lib/chef/windows/ChefNodeCmdlet/ChefNodeCmdlet/GetNodeValueCommandBase.cs +142 -0
- data/lib/chef/windows/ChefNodeCmdlet/ChefNodeCmdlet/GetNodeValueRequestBase.cs +64 -0
- data/lib/chef/windows/ChefNodeCmdlet/ChefNodeCmdlet/GetNodeValueResponseBase.cs +69 -0
- data/lib/chef/windows/ChefNodeCmdlet/ChefNodeCmdlet/JsonTransport.cs +110 -0
- data/lib/chef/windows/ChefNodeCmdlet/ChefNodeCmdlet/PipeClient.cs +158 -0
- data/lib/chef/windows/ChefNodeCmdlet/ChefNodeCmdlet/PipeServer.cs +142 -0
- data/lib/chef/windows/ChefNodeCmdlet/ChefNodeCmdlet/Properties/AssemblyInfo.cs +16 -0
- data/lib/chef/windows/ChefNodeCmdlet/ChefNodeCmdlet/ProtocolConstants.cs +55 -0
- data/lib/chef/windows/ChefNodeCmdlet/ChefNodeCmdlet/ProtocolUtilities.cs +77 -0
- data/lib/chef/windows/ChefNodeCmdlet/ChefNodeCmdlet/ReadMe.txt +53 -0
- data/lib/chef/windows/ChefNodeCmdlet/ChefNodeCmdlet/SetChefNodeCommand.cs +59 -0
- data/lib/chef/windows/ChefNodeCmdlet/ChefNodeCmdlet/SetChefNodeRequest.cs +46 -0
- data/lib/chef/windows/ChefNodeCmdlet/ChefNodeCmdlet/SetChefNodeResponse.cs +58 -0
- data/lib/chef/windows/ChefNodeCmdlet/ChefNodeCmdlet/SetCurrentResourceCommand.cs +59 -0
- data/lib/chef/windows/ChefNodeCmdlet/ChefNodeCmdlet/SetCurrentResourceRequest.cs +46 -0
- data/lib/chef/windows/ChefNodeCmdlet/ChefNodeCmdlet/SetCurrentResourceResponse.cs +40 -0
- data/lib/chef/windows/ChefNodeCmdlet/ChefNodeCmdlet/SetNewResourceCommand.cs +59 -0
- data/lib/chef/windows/ChefNodeCmdlet/ChefNodeCmdlet/SetNewResourceRequest.cs +46 -0
- data/lib/chef/windows/ChefNodeCmdlet/ChefNodeCmdlet/SetNewResourceResponse.cs +40 -0
- data/lib/chef/windows/ChefNodeCmdlet/ChefNodeCmdlet/SetNodeValueCommandBase.cs +293 -0
- data/lib/chef/windows/ChefNodeCmdlet/ChefNodeCmdlet/SetNodeValueRequestBase.cs +75 -0
- data/lib/chef/windows/ChefNodeCmdlet/ChefNodeCmdlet/SetNodeValueResponseBase.cs +45 -0
- data/lib/chef/windows/ChefNodeCmdlet/ChefNodeCmdlet/Transport.cs +91 -0
- data/lib/chef/windows/ChefNodeCmdlet/ChefNodeCmdlet.sln +35 -0
- data/lib/chef/windows/ChefNodeCmdlet/TestChefNodeCmdlet/Program.cs +374 -0
- data/lib/chef/windows/ChefNodeCmdlet/TestChefNodeCmdlet/Properties/AssemblyInfo.cs +16 -0
- data/lib/chef/windows/ChefNodeCmdlet/TestChefNodeCmdlet/TestChefNodeCmdlet.csproj +65 -0
- data/lib/chef/windows/ChefNodeCmdlet/TestNextActionCmdlet/Program.cs +136 -0
- data/lib/chef/windows/ChefNodeCmdlet/TestNextActionCmdlet/Properties/AssemblyInfo.cs +36 -0
- data/lib/chef/windows/ChefNodeCmdlet/TestNextActionCmdlet/ReadMe.txt +46 -0
- data/lib/chef/windows/ChefNodeCmdlet/TestNextActionCmdlet/TestNextActionCmdlet.csproj +68 -0
- data/lib/chef/windows/bin/Newtonsoft.Json.dll +0 -0
- data/lib/chef/windows/chef_node_server.rb +463 -0
- data/lib/chef/windows/dynamic_powershell_provider.rb +296 -0
- data/lib/chef/windows/pipe_server.rb +283 -0
- data/lib/chef/windows/powershell_host.rb +285 -0
- data/lib/chef/windows/powershell_pipe_server.rb +136 -0
- data/lib/chef/windows/powershell_provider_base.rb +92 -0
- data/lib/chef/windows/scripts/run_loop.ps1 +105 -0
- data/lib/clouds/cloud.rb +557 -0
- data/lib/clouds/cloud_factory.rb +250 -0
- data/lib/clouds/cloud_utilities.rb +244 -0
- data/lib/clouds/clouds/azure.rb +106 -0
- data/lib/clouds/clouds/cloudstack.rb +114 -0
- data/lib/clouds/clouds/ec2.rb +113 -0
- data/lib/clouds/clouds/eucalyptus.rb +46 -0
- data/lib/clouds/clouds/google.rb +102 -0
- data/lib/clouds/clouds/none.rb +76 -0
- data/lib/clouds/clouds/openstack.rb +30 -0
- data/lib/clouds/clouds/rackspace-ng.rb +54 -0
- data/lib/clouds/clouds/rackspace.rb +78 -0
- data/lib/clouds/clouds/softlayer.rb +91 -0
- data/lib/clouds/metadata_formatter.rb +108 -0
- data/lib/clouds/metadata_provider.rb +128 -0
- data/lib/clouds/metadata_source.rb +87 -0
- data/lib/clouds/metadata_sources/certificate_metadata_source.rb +207 -0
- data/lib/clouds/metadata_sources/config_drive_metadata_source.rb +129 -0
- data/lib/clouds/metadata_sources/file_metadata_source.rb +74 -0
- data/lib/clouds/metadata_sources/http_metadata_source.rb +277 -0
- data/lib/clouds/metadata_sources/selective_metadata_source.rb +122 -0
- data/lib/clouds/metadata_tree_climber.rb +144 -0
- data/lib/clouds/metadata_writer.rb +155 -0
- data/lib/clouds/metadata_writers/dictionary_metadata_writer.rb +72 -0
- data/lib/clouds/metadata_writers/ruby_metadata_writer.rb +76 -0
- data/lib/clouds/metadata_writers/shell_metadata_writer.rb +121 -0
- data/lib/clouds/register_clouds.rb +34 -0
- data/lib/clouds.rb +32 -0
- data/lib/gem_dependencies.rb +83 -0
- data/lib/git_hooks/commit-msg.rb +7 -0
- data/lib/instance/agent_config.rb +168 -0
- data/lib/instance/agent_watcher.rb +233 -0
- data/lib/instance/audit_cook_stub.rb +104 -0
- data/lib/instance/audit_proxy.rb +247 -0
- data/lib/instance/bundle_queue.rb +104 -0
- data/lib/instance/cook/agent_connection.rb +109 -0
- data/lib/instance/cook/audit_logger.rb +165 -0
- data/lib/instance/cook/audit_stub.rb +142 -0
- data/lib/instance/cook/ca-bundle.crt +2794 -0
- data/lib/instance/cook/chef_state.rb +211 -0
- data/lib/instance/cook/cook.rb +306 -0
- data/lib/instance/cook/cook_state.rb +298 -0
- data/lib/instance/cook/cookbook_path_mapping.rb +66 -0
- data/lib/instance/cook/cookbook_repo_retriever.rb +190 -0
- data/lib/instance/cook/executable_sequence.rb +765 -0
- data/lib/instance/cook/external_parameter_gatherer.rb +190 -0
- data/lib/instance/cook/repose_downloader.rb +349 -0
- data/lib/instance/cook/shutdown_request_proxy.rb +121 -0
- data/lib/instance/cook.rb +41 -0
- data/lib/instance/downloader.rb +208 -0
- data/lib/instance/duplicable.rb +67 -0
- data/lib/instance/exceptions.rb +49 -0
- data/lib/instance/executable_sequence_proxy.rb +278 -0
- data/lib/instance/instance_commands.rb +577 -0
- data/lib/instance/instance_state.rb +633 -0
- data/lib/instance/json_utilities.rb +102 -0
- data/lib/instance/login_manager.rb +533 -0
- data/lib/instance/login_user_manager.rb +522 -0
- data/lib/instance/message_encoder.rb +118 -0
- data/lib/instance/multi_thread_bundle_queue.rb +232 -0
- data/lib/instance/operation_context.rb +60 -0
- data/lib/instance/options_bag.rb +65 -0
- data/lib/instance/payload_formatter.rb +46 -0
- data/lib/instance/policy.rb +53 -0
- data/lib/instance/policy_audit.rb +100 -0
- data/lib/instance/policy_manager.rb +146 -0
- data/lib/instance/reenroll_manager.rb +104 -0
- data/lib/instance/right_scripts_cookbook.rb +181 -0
- data/lib/instance/shutdown_request.rb +221 -0
- data/lib/instance/single_thread_bundle_queue.rb +189 -0
- data/lib/instance/volume_management.rb +450 -0
- data/lib/instance.rb +50 -0
- data/lib/repo_conf_generators/apt_conf_generators.rb +106 -0
- data/lib/repo_conf_generators/gem_conf_generators.rb +80 -0
- data/lib/repo_conf_generators/rightscale_conf_generators.rb +254 -0
- data/lib/repo_conf_generators/rightscale_key.pub +17 -0
- data/lib/repo_conf_generators/yum_conf_generators.rb +225 -0
- data/lib/repo_conf_generators.rb +30 -0
- data/lib/run_shell.rb +28 -0
- data/scripts/agent_checker.rb +571 -0
- data/scripts/agent_controller.rb +247 -0
- data/scripts/agent_deployer.rb +148 -0
- data/scripts/bundle_runner.rb +336 -0
- data/scripts/cloud_controller.rb +176 -0
- data/scripts/log_level_manager.rb +142 -0
- data/scripts/ohai_runner.rb +33 -0
- data/scripts/reenroller.rb +193 -0
- data/scripts/server_importer.rb +293 -0
- data/scripts/shutdown_client.rb +183 -0
- data/scripts/system_configurator.rb +367 -0
- data/scripts/tagger.rb +381 -0
- data/scripts/thunker.rb +356 -0
- 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
|