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.
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,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