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,129 @@
|
|
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 File.normalize_path(File.expand_path('../file_metadata_source', __FILE__))
|
24
|
+
|
25
|
+
module RightScale
|
26
|
+
module MetadataSources
|
27
|
+
class ConfigDriveMetadataSource < FileMetadataSource
|
28
|
+
|
29
|
+
class ConfigDriveError < Exception; end
|
30
|
+
|
31
|
+
DEFAULT_CONFIG_DRIVE_MOUNTPOINT = "/mnt/configdrive"
|
32
|
+
|
33
|
+
attr_accessor :config_drive_label, :config_drive_mountpoint, :config_drive_uuid, :config_drive_filesystem
|
34
|
+
|
35
|
+
def initialize(options)
|
36
|
+
super(options)
|
37
|
+
|
38
|
+
@config_drive_mountpoint = options[:config_drive_mountpoint] || DEFAULT_CONFIG_DRIVE_MOUNTPOINT
|
39
|
+
@config_drive_uuid = options[:config_drive_uuid]
|
40
|
+
@config_drive_filesystem = options[:config_drive_filesystem]
|
41
|
+
@config_drive_label = options[:config_drive_label]
|
42
|
+
|
43
|
+
if @config_drive_uuid.nil? & @config_drive_label.nil? & @config_drive_filesystem.nil?
|
44
|
+
raise ArgumentError, "at least one of the following is required [options[:config_drive_label], options[:config_drive_filesystem],options[:config_drive_uuid]]"
|
45
|
+
end
|
46
|
+
end
|
47
|
+
|
48
|
+
# Queries for metadata using the given path.
|
49
|
+
#
|
50
|
+
# === Parameters
|
51
|
+
# path(String):: metadata path
|
52
|
+
#
|
53
|
+
# === Return
|
54
|
+
# metadata(String):: query result or empty
|
55
|
+
#
|
56
|
+
# === Raises
|
57
|
+
# QueryFailed:: on any failure to query
|
58
|
+
def query(path)
|
59
|
+
mount_config_drive
|
60
|
+
|
61
|
+
super(path)
|
62
|
+
end
|
63
|
+
|
64
|
+
# Mounts the configuration drive based on the provided parameters
|
65
|
+
#
|
66
|
+
# === Parameters
|
67
|
+
#
|
68
|
+
# === Return
|
69
|
+
# always true
|
70
|
+
#
|
71
|
+
# === Raises
|
72
|
+
# ConfigDriveError:: on failure to find a config drive
|
73
|
+
# SystemCallError:: on failure to create the mountpoint
|
74
|
+
# ArgumentError:: on invalid parameters
|
75
|
+
# VolumeError:: on a failure to mount the device
|
76
|
+
# ParserError:: on failure to parse volume list
|
77
|
+
def mount_config_drive
|
78
|
+
# These two conditions are available on *nix and windows
|
79
|
+
conditions = {}
|
80
|
+
conditions[:label] = @config_drive_label if @config_drive_label
|
81
|
+
conditions[:filesystem] = @config_drive_filesystem if @config_drive_filesystem
|
82
|
+
|
83
|
+
if ::RightScale::Platform.linux? && @config_drive_uuid
|
84
|
+
conditions[:uuid] = @config_drive_uuid
|
85
|
+
end
|
86
|
+
|
87
|
+
timeout = 60 * 10
|
88
|
+
starttime = Time.now.to_i
|
89
|
+
backoff = [2,5,10]
|
90
|
+
idx = -1
|
91
|
+
|
92
|
+
begin
|
93
|
+
device_ary = []
|
94
|
+
if ::RightScale::Platform.windows?
|
95
|
+
# Check once, in case the metadata drive is onlined already
|
96
|
+
device_ary = ::RightScale::Platform.volume_manager.volumes(conditions)
|
97
|
+
::RightScale::Platform.volume_manager.disks({:status => "Offline"}).each do |disk|
|
98
|
+
::RightScale::Platform.volume_manager.online_disk(disk[:index])
|
99
|
+
# Per the interwebs, you cannot run disk part commands back to back without a wait.
|
100
|
+
sleep(15)
|
101
|
+
device_ary = ::RightScale::Platform.volume_manager.volumes(conditions)
|
102
|
+
break if device_ary.length > 0
|
103
|
+
::RightScale::Platform.volume_manager.offline_disk(disk[:index])
|
104
|
+
end unless device_ary.length > 0
|
105
|
+
else
|
106
|
+
device_ary = ::RightScale::Platform.volume_manager.volumes(conditions)
|
107
|
+
end
|
108
|
+
idx = idx + 1 unless idx == 2
|
109
|
+
break if (Time.now.to_i - starttime) > timeout || device_ary.length > 0
|
110
|
+
@logger.warn("Configuration drive device was not found. Trying again in #{backoff[idx % backoff.length]} seconds.")
|
111
|
+
Kernel.sleep(backoff[idx])
|
112
|
+
end while device_ary.length == 0
|
113
|
+
|
114
|
+
# REVIEW: Raise or log and exit?
|
115
|
+
raise ConfigDriveError.new("Configuration drive device found. Conditions: #{conditions.inspect}") if device_ary.length == 0
|
116
|
+
|
117
|
+
FileUtils.mkdir_p(@config_drive_mountpoint) unless File.directory? @config_drive_mountpoint
|
118
|
+
|
119
|
+
if ::RightScale::Platform.linux?
|
120
|
+
::RightScale::Platform.volume_manager.mount_volume(device_ary[0], @config_drive_mountpoint)
|
121
|
+
elsif ::RightScale::Platform.windows?
|
122
|
+
::RightScale::Platform.volume_manager.assign_device(device_ary[0][:index], @config_drive_mountpoint, {:idempotent => true, :clear_readonly => false, :remove_all => true})
|
123
|
+
end
|
124
|
+
return true
|
125
|
+
end
|
126
|
+
|
127
|
+
end # ConfigDriveMetadataSource
|
128
|
+
end # MetadataSources
|
129
|
+
end # RightScale
|
@@ -0,0 +1,74 @@
|
|
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
|
+
module RightScale
|
24
|
+
|
25
|
+
module MetadataSources
|
26
|
+
|
27
|
+
# Provides metadata by reading a dictionary file on disk.
|
28
|
+
class FileMetadataSource < MetadataSource
|
29
|
+
|
30
|
+
attr_accessor :cloud_metadata_source_file_path, :user_metadata_source_file_path
|
31
|
+
|
32
|
+
def initialize(options)
|
33
|
+
super(options)
|
34
|
+
raise ArgumentError.new("options[:cloud_metadata_root_path] is required") unless @cloud_metadata_root_path = options[:cloud_metadata_root_path]
|
35
|
+
raise ArgumentError.new("options[:user_metadata_root_path] is required") unless @user_metadata_root_path = options[:user_metadata_root_path]
|
36
|
+
|
37
|
+
@cloud_metadata_source_file_path = options[:cloud_metadata_source_file_path]
|
38
|
+
@user_metadata_source_file_path = options[:user_metadata_source_file_path]
|
39
|
+
end
|
40
|
+
|
41
|
+
# Queries for metadata using the given path.
|
42
|
+
#
|
43
|
+
# === Parameters
|
44
|
+
# path(String):: metadata path
|
45
|
+
#
|
46
|
+
# === Return
|
47
|
+
# metadata(String):: query result or empty
|
48
|
+
#
|
49
|
+
# === Raises
|
50
|
+
# QueryFailed:: on any failure to query
|
51
|
+
def query(path)
|
52
|
+
result = ""
|
53
|
+
if path == @cloud_metadata_root_path
|
54
|
+
result = File.read(@cloud_metadata_source_file_path) if @cloud_metadata_source_file_path
|
55
|
+
elsif path == @user_metadata_root_path
|
56
|
+
result = File.read(@user_metadata_source_file_path) if @user_metadata_source_file_path
|
57
|
+
else
|
58
|
+
raise QueryFailed.new("Unknown path: #{path}")
|
59
|
+
end
|
60
|
+
result
|
61
|
+
rescue Exception => e
|
62
|
+
raise QueryFailed.new(e.message)
|
63
|
+
end
|
64
|
+
|
65
|
+
# Nothing to do.
|
66
|
+
def finish
|
67
|
+
true
|
68
|
+
end
|
69
|
+
|
70
|
+
end # FileMetadataSource
|
71
|
+
|
72
|
+
end # MetadataSources
|
73
|
+
|
74
|
+
end # RightScale
|
@@ -0,0 +1,277 @@
|
|
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 'right_http_connection'
|
24
|
+
require 'uri'
|
25
|
+
|
26
|
+
module RightScale
|
27
|
+
|
28
|
+
module MetadataSources
|
29
|
+
|
30
|
+
# Provides metadata via a single http connection which is kept alive in the
|
31
|
+
# case of a tree of metadata.
|
32
|
+
class HttpMetadataSource < MetadataSource
|
33
|
+
|
34
|
+
attr_accessor :host, :port
|
35
|
+
|
36
|
+
def initialize(options)
|
37
|
+
super(options)
|
38
|
+
raise ArgumentError, "options[:hosts] is required" unless @hosts = options[:hosts]
|
39
|
+
@host, @port = self.class.select_metadata_server(@hosts)
|
40
|
+
@connections = {}
|
41
|
+
end
|
42
|
+
|
43
|
+
# Queries for metadata using the given path.
|
44
|
+
#
|
45
|
+
# === Parameters
|
46
|
+
# path(String):: metadata path
|
47
|
+
#
|
48
|
+
# === Return
|
49
|
+
# metadata(String):: query result or empty
|
50
|
+
#
|
51
|
+
# === Raises
|
52
|
+
# QueryFailed:: on any failure to query
|
53
|
+
def query(path)
|
54
|
+
http_path = "http://#{@host}:#{@port}/#{path}"
|
55
|
+
attempts = 1
|
56
|
+
while true
|
57
|
+
begin
|
58
|
+
logger.debug("Querying \"#{http_path}\"...")
|
59
|
+
# get.
|
60
|
+
result = http_get(http_path)
|
61
|
+
if result
|
62
|
+
logger.debug("Successfully retrieved from: \"#{http_path}\" Result: #{path} = #{result}")
|
63
|
+
return result
|
64
|
+
end
|
65
|
+
|
66
|
+
# retry, if allowed.
|
67
|
+
if snooze(attempts)
|
68
|
+
logger.info("Retrying \"#{http_path}\"...")
|
69
|
+
else
|
70
|
+
logger.error("Could not retrieve metadata from \"#{http_path}\"; retry limit exceeded.")
|
71
|
+
return ""
|
72
|
+
end
|
73
|
+
rescue Exception => e
|
74
|
+
logger.error("#{Time.now().to_s()}: Exception occurred while attempting to retrieve metadata from \"#{http_path}\"; Exception:#{e.message}")
|
75
|
+
finish # Reset the connections.
|
76
|
+
unless snooze(attempts)
|
77
|
+
logger.error("Trace:#{e.backtrace.join("\n")}")
|
78
|
+
return ""
|
79
|
+
end
|
80
|
+
end
|
81
|
+
attempts += 1
|
82
|
+
end
|
83
|
+
end
|
84
|
+
|
85
|
+
# Closes any http connections left open after fetching metadata.
|
86
|
+
def finish
|
87
|
+
@connections.each_value do |connection|
|
88
|
+
begin
|
89
|
+
connection.finish
|
90
|
+
rescue Exception => e
|
91
|
+
logger.error("Failed to close metadata http connection: #{e.backtrace.join("\n")}")
|
92
|
+
end
|
93
|
+
end
|
94
|
+
@connections = {}
|
95
|
+
end
|
96
|
+
|
97
|
+
# selects a host/port by attempting to dig one or more well-known DNS
|
98
|
+
# names or IP addresses.
|
99
|
+
#
|
100
|
+
# === Parameters
|
101
|
+
# hosts(Array):: array of hosts in the form [{:host => <dns name or ip address>, :port => <port or nil>}+]
|
102
|
+
#
|
103
|
+
# === Return
|
104
|
+
# result(Array):: pair in form of [<selected host ip address>, <selected port>]
|
105
|
+
def self.select_metadata_server(hosts)
|
106
|
+
# note that .each works for strings (by newline) and arrays.
|
107
|
+
last_exception = nil
|
108
|
+
hosts.each do |host_data|
|
109
|
+
begin
|
110
|
+
# resolve metadata server hostname.
|
111
|
+
addrs = Socket.gethostbyname(host_data[:host])[3..-1]
|
112
|
+
|
113
|
+
# select only IPv4 addresses
|
114
|
+
addrs = addrs.select { |x| x.length == 4 }
|
115
|
+
|
116
|
+
# choose a random IPv4 address
|
117
|
+
raw_ip = addrs[rand(addrs.size)]
|
118
|
+
|
119
|
+
# transform binary IP address into string representation
|
120
|
+
ip = []
|
121
|
+
raw_ip.each_byte { |x| ip << x.to_s }
|
122
|
+
return [ip.join('.'), host_data[:port] || 80]
|
123
|
+
rescue Exception => e
|
124
|
+
last_exception = e
|
125
|
+
end
|
126
|
+
end
|
127
|
+
raise last_exception
|
128
|
+
end
|
129
|
+
|
130
|
+
protected
|
131
|
+
|
132
|
+
# Some time definitions
|
133
|
+
SECOND = 1
|
134
|
+
MINUTE = 60 * SECOND
|
135
|
+
HOUR = 60 * MINUTE
|
136
|
+
|
137
|
+
# Time to yield before retries
|
138
|
+
RETRY_DELAY = 2 * SECOND
|
139
|
+
RETRY_DELAY_FACTOR = 1
|
140
|
+
|
141
|
+
# Total amount of time to retry
|
142
|
+
RETRY_MAX_TOTAL_TIME = 1 * HOUR
|
143
|
+
|
144
|
+
RETRY_MAX_ATTEMPTS = RETRY_MAX_TOTAL_TIME / RETRY_DELAY
|
145
|
+
|
146
|
+
# ensures we are not infinitely redirected.
|
147
|
+
MAX_REDIRECT_HISTORY = 16
|
148
|
+
|
149
|
+
# Simple sleep algorithm. Returns true if processing
|
150
|
+
# should continue or false if the maximum number of attempts has
|
151
|
+
# been exceeded.
|
152
|
+
#
|
153
|
+
# === Parameters
|
154
|
+
# attempts(int):: number of attempts
|
155
|
+
#
|
156
|
+
# === Return
|
157
|
+
# result(Boolean):: true to continue, false to give up
|
158
|
+
def snooze(attempts)
|
159
|
+
if attempts >= RETRY_MAX_ATTEMPTS
|
160
|
+
logger.debug("Exceeded retry limit of #{RETRY_MAX_ATTEMPTS}.")
|
161
|
+
false
|
162
|
+
else
|
163
|
+
sleep(RETRY_DELAY * RETRY_DELAY_FACTOR)
|
164
|
+
true
|
165
|
+
end
|
166
|
+
end
|
167
|
+
|
168
|
+
# Performs an HTTP get request with built-in retries and redirection based
|
169
|
+
# on HTTP responses.
|
170
|
+
#
|
171
|
+
# === Parameters
|
172
|
+
# attempts(int):: number of attempts
|
173
|
+
#
|
174
|
+
# === Return
|
175
|
+
# result(String):: body of response or nil
|
176
|
+
def http_get(path, keep_alive = true)
|
177
|
+
uri = safe_parse_http_uri(path)
|
178
|
+
history = []
|
179
|
+
loop do
|
180
|
+
logger.debug("http_get(#{uri})")
|
181
|
+
|
182
|
+
# keep history of live connections for more efficient redirection.
|
183
|
+
host = uri.host
|
184
|
+
connection = @connections[host] ||= Rightscale::HttpConnection.new(:logger => logger, :exception => QueryFailed)
|
185
|
+
|
186
|
+
# prepare request. ensure path not empty due to Net::HTTP limitation.
|
187
|
+
#
|
188
|
+
# note that the default for Net::HTTP is to close the connection after
|
189
|
+
# each request (contrary to expected conventions). we must be explicit
|
190
|
+
# about keep-alive if we want that behavior.
|
191
|
+
request = Net::HTTP::Get.new(uri.path)
|
192
|
+
request['Connection'] = keep_alive ? 'keep-alive' : 'close'
|
193
|
+
|
194
|
+
# get.
|
195
|
+
response = connection.request(:protocol => uri.scheme, :server => uri.host, :port => uri.port, :request => request)
|
196
|
+
return response.body if response.kind_of?(Net::HTTPSuccess)
|
197
|
+
if response.kind_of?(Net::HTTPServerError)
|
198
|
+
logger.debug("Request failed but can retry; #{response.class.name}")
|
199
|
+
return nil
|
200
|
+
elsif response.kind_of?(Net::HTTPRedirection)
|
201
|
+
# keep history of redirects.
|
202
|
+
history << uri.to_s
|
203
|
+
location = response['Location']
|
204
|
+
uri = safe_parse_http_uri(location)
|
205
|
+
if uri.absolute?
|
206
|
+
if history.include?(uri.to_s)
|
207
|
+
logger.error("Circular redirection to #{location.inspect} detected; giving up")
|
208
|
+
return nil
|
209
|
+
elsif history.size >= MAX_REDIRECT_HISTORY
|
210
|
+
logger.error("Unbounded redirection to #{location.inspect} detected; giving up")
|
211
|
+
return nil
|
212
|
+
else
|
213
|
+
# redirect and continue in loop.
|
214
|
+
logger.debug("Request redirected to #{location.inspect}: #{response.class.name}")
|
215
|
+
end
|
216
|
+
else
|
217
|
+
# can't redirect without an absolute location.
|
218
|
+
logger.error("Unable to redirect to metadata server location #{location.inspect}: #{response.class.name}")
|
219
|
+
return nil
|
220
|
+
end
|
221
|
+
else
|
222
|
+
# not retryable.
|
223
|
+
#
|
224
|
+
# log an error and return empty string.
|
225
|
+
#
|
226
|
+
# note that the EC2 metadata server is known to give malformed
|
227
|
+
# responses on rare occasions, but the right_http_connection will
|
228
|
+
# consider these to be 'bananas' and retry automatically (up to a
|
229
|
+
# pre-defined limit).
|
230
|
+
logger.error("Request for metadata failed: #{response.class.name}")
|
231
|
+
return ""
|
232
|
+
end
|
233
|
+
end
|
234
|
+
end
|
235
|
+
|
236
|
+
# Handles some cases which raise exceptions in the URI class.
|
237
|
+
#
|
238
|
+
# === Parameters
|
239
|
+
# path(String):: URI to parse
|
240
|
+
#
|
241
|
+
# === Return
|
242
|
+
# uri(URI):: parsed URI
|
243
|
+
#
|
244
|
+
# === Raise
|
245
|
+
# URI::InvalidURIError:: on invalid URI
|
246
|
+
def safe_parse_http_uri(path)
|
247
|
+
raise ArgumentError.new("URI path cannot be empty") if path.to_s.empty?
|
248
|
+
begin
|
249
|
+
uri = URI.parse(path)
|
250
|
+
rescue URI::InvalidURIError => e
|
251
|
+
# URI raises an exception for paths like "<IP>:<port>"
|
252
|
+
# (e.g. "127.0.0.1:123") unless they also have scheme (e.g. http)
|
253
|
+
# prefix.
|
254
|
+
raise e if path.start_with?("http://") || path.start_with?("https://")
|
255
|
+
uri = URI.parse("http://" + path)
|
256
|
+
uri = URI.parse("https://" + path) if uri.port == 443
|
257
|
+
path = uri.to_s
|
258
|
+
end
|
259
|
+
|
260
|
+
# supply any missing default values to make URI as complete as possible.
|
261
|
+
if uri.scheme.nil? || uri.host.nil?
|
262
|
+
scheme = (uri.port == 443) ? 'https' : 'http'
|
263
|
+
uri = URI.parse("#{scheme}://#{path}")
|
264
|
+
path = uri.to_s
|
265
|
+
end
|
266
|
+
if uri.path.to_s.empty?
|
267
|
+
uri = URI.parse("#{path}/")
|
268
|
+
path = uri.to_s
|
269
|
+
end
|
270
|
+
return uri
|
271
|
+
end
|
272
|
+
|
273
|
+
end # HttpMetadataSource
|
274
|
+
|
275
|
+
end # MetadataSources
|
276
|
+
|
277
|
+
end # RightScale
|
@@ -0,0 +1,122 @@
|
|
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
|
+
module RightScale
|
24
|
+
|
25
|
+
module MetadataSources
|
26
|
+
|
27
|
+
# Provides metadata by attempting to get metadata from one or more listed
|
28
|
+
# sources; first responding successfully wins.
|
29
|
+
class SelectiveMetadataSource < MetadataSource
|
30
|
+
|
31
|
+
attr_accessor :metadata_sources
|
32
|
+
|
33
|
+
def initialize(options)
|
34
|
+
raise ArgumentError, "options[:metadata_sources] is required" unless metadata_source_types = options[:metadata_source_types]
|
35
|
+
raise ArgumentError, "options[:cloud] is required" unless @cloud = options[:cloud]
|
36
|
+
@select_metadata_override = options[:select_metadata_override]
|
37
|
+
|
38
|
+
# keep types but create selective sources on demand in case not all are used.
|
39
|
+
@metadata_sources = []
|
40
|
+
metadata_source_types.each do |metadata_source_type|
|
41
|
+
@metadata_sources << { :type => metadata_source_type, :source => nil }
|
42
|
+
end
|
43
|
+
end
|
44
|
+
|
45
|
+
# Queries for metadata using the given path.
|
46
|
+
#
|
47
|
+
# === Parameters
|
48
|
+
# path(String):: metadata path
|
49
|
+
#
|
50
|
+
# === Return
|
51
|
+
# metadata(String):: query result
|
52
|
+
#
|
53
|
+
# === Raises
|
54
|
+
# QueryFailed:: on any failure to query
|
55
|
+
def query(path)
|
56
|
+
merged_metadata = ""
|
57
|
+
last_query_failed = nil
|
58
|
+
@metadata_sources.each do |metadata_source|
|
59
|
+
type = metadata_source[:type]
|
60
|
+
unless source = metadata_source[:source]
|
61
|
+
# note that sources are special in that they ignore cloud vs. user
|
62
|
+
# specialization unlike other dependency types.
|
63
|
+
kind = :cloud_metadata
|
64
|
+
source = @cloud.create_dependency_type(kind, :metadata_source, type)
|
65
|
+
metadata_source[:source] = source
|
66
|
+
end
|
67
|
+
begin
|
68
|
+
query_result = source.query(path)
|
69
|
+
selected = select_metadata(path, type, query_result, merged_metadata)
|
70
|
+
merged_metadata = selected[:merged_metadata]
|
71
|
+
last_query_failed = nil # reset last failed query
|
72
|
+
break unless selected[:query_next_metadata]
|
73
|
+
rescue QueryFailed => e
|
74
|
+
# temporarily ignore failed query in case next source query succeeds
|
75
|
+
last_query_failed = e
|
76
|
+
end
|
77
|
+
end
|
78
|
+
raise last_query_failed if last_query_failed
|
79
|
+
return merged_metadata
|
80
|
+
rescue Exception => e
|
81
|
+
raise QueryFailed.new(e.message)
|
82
|
+
end
|
83
|
+
|
84
|
+
# Selects metadata by determining if the metadata condition has been
|
85
|
+
# satisfied. Supports merging of metadata (potentially in different
|
86
|
+
# formats) from different sources.
|
87
|
+
#
|
88
|
+
# === Parameters
|
89
|
+
# path(String):: metadata path
|
90
|
+
# metadata_source_type(String):: metadata source type
|
91
|
+
# query_result(String):: raw metadata from query
|
92
|
+
# previous_metadata(String):: previously merged metadata or empty
|
93
|
+
#
|
94
|
+
# === Returns
|
95
|
+
# result[:query_next_metadata](Boolean):: when true indicates that the next metadata source should also be queried.
|
96
|
+
# result[:merged_metadata](String):: result of merging any previous metadata with current query result.
|
97
|
+
def select_metadata(path, metadata_source_type, query_result, previous_metadata)
|
98
|
+
return @select_metadata_override.call(self, path, metadata_source_type, query_result, previous_metadata) if @select_metadata_override
|
99
|
+
return {:query_next_metadata => query_result.strip.empty?, :merged_metadata => query_result}
|
100
|
+
end
|
101
|
+
|
102
|
+
# Attempts to finish all child metadata sources.
|
103
|
+
def finish
|
104
|
+
last_exception = nil
|
105
|
+
@metadata_sources.each do |metadata_source|
|
106
|
+
begin
|
107
|
+
source = metadata_source[:source]
|
108
|
+
source.finish if source
|
109
|
+
rescue Exception => e
|
110
|
+
last_exception = e
|
111
|
+
ensure
|
112
|
+
metadata_source[:source] = nil
|
113
|
+
end
|
114
|
+
end
|
115
|
+
raise last_exception if last_exception
|
116
|
+
end
|
117
|
+
|
118
|
+
end # SelectiveMetadataSource
|
119
|
+
|
120
|
+
end # MetadataSources
|
121
|
+
|
122
|
+
end # RightScale
|