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,91 @@
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
+ CONFIG_DRIVE_MOUNTPOINT = File.join(RightScale::Platform.filesystem.spool_dir, name.to_s) if ::RightScale::Platform.linux?
24
+ CONFIG_DRIVE_MOUNTPOINT = File.join(ENV['ProgramW6432'], 'RightScale', 'Mount', 'Softlayer').gsub('/', '\\') if ::RightScale::Platform.windows?
25
+
26
+ # dependencies.
27
+ metadata_source 'metadata_sources/config_drive_metadata_source'
28
+ metadata_writers 'metadata_writers/dictionary_metadata_writer',
29
+ 'metadata_writers/ruby_metadata_writer',
30
+ 'metadata_writers/shell_metadata_writer'
31
+
32
+ abbreviation :sl
33
+
34
+ # Parses softlayer user metadata into a hash.
35
+ #
36
+ # === Parameters
37
+ # tree_climber(MetadataTreeClimber):: tree climber
38
+ # data(String):: raw data
39
+ #
40
+ # === Return
41
+ # result(Hash):: Hash-like leaf value
42
+ def create_user_metadata_leaf(tree_climber, data)
43
+ result = tree_climber.create_branch
44
+ # REVIEW: This can (and will) raise an exception if the data is malformed or empty. I was putting it in a
45
+ # begin/rescue/end block, but there doesn't appear to be a logger in scope to report the problem and exit gracefully.
46
+ #
47
+ # Also, is it appropriate to be parsing JSON here? Is a specific tree_climber for json more appropriate?
48
+ #
49
+ # REVIEWER:
50
+ # (1) added an in-scope logger (it was always available as option(:logger)) for RightLink v5.8+
51
+ # (2) catching and logging an exception here is reasonable; added it.
52
+ # (3) as far as subclassing goes, the cloud definition methodology allows for overriding a few
53
+ # methods in the existing code base instead of having to create a custom class hierarchy for each
54
+ # cloud. either approach is supported, but the override philosophy used here seems simpler
55
+ # (especially for new cloud providers who haven't seen much ruby up till now).
56
+ parsed_data = nil
57
+ begin
58
+ parsed_data = JSON.parse(data.strip)
59
+ rescue Exception => e
60
+ logger.error("#{e.class}: #{e.message}")
61
+ end
62
+ ::RightScale::CloudUtilities.split_metadata(parsed_data[0], '&', result) unless !parsed_data || parsed_data.length == 0
63
+ result
64
+ end
65
+
66
+ # defaults.
67
+ default_option([:user_metadata, :metadata_tree_climber, :create_leaf_override], method(:create_user_metadata_leaf))
68
+ default_option([:metadata_source, :user_metadata_source_file_path], File.join(CONFIG_DRIVE_MOUNTPOINT, 'meta.js'))
69
+
70
+ default_option([:metadata_source, :config_drive_uuid], "681B-8C5D")
71
+ default_option([:metadata_source, :config_drive_filesystem], ::RightScale::Platform.windows? ? 'FAT' : 'vfat')
72
+ default_option([:metadata_source, :config_drive_label], 'METADATA')
73
+ default_option([:metadata_source, :config_drive_mountpoint], CONFIG_DRIVE_MOUNTPOINT)
74
+
75
+ # Updates the given node with cloud metadata details.
76
+ #
77
+ # === Return
78
+ # always true
79
+ def update_details
80
+ details = {}
81
+ if ohai = @options[:ohai_node]
82
+ if platform.windows?
83
+ details[:public_ip] = ::RightScale::CloudUtilities.ip_for_windows_interface(ohai, 'Local Area Connection 2')
84
+ details[:private_ip] = ::RightScale::CloudUtilities.ip_for_windows_interface(ohai, 'Local Area Connection')
85
+ else
86
+ details[:public_ip] = ::RightScale::CloudUtilities.ip_for_interface(ohai, :eth1)
87
+ details[:private_ip] = ::RightScale::CloudUtilities.ip_for_interface(ohai, :eth0)
88
+ end
89
+ end
90
+ return details
91
+ end
@@ -0,0 +1,108 @@
1
+ #
2
+ # Copyright (c) 2010-2011 RightScale Inc
3
+ #
4
+ # Permission is hereby granted, free of charge, to any person obtaining
5
+ # a copy of this software and associated documentation files (the
6
+ # "Software"), to deal in the Software without restriction, including
7
+ # without limitation the rights to use, copy, modify, merge, publish,
8
+ # distribute, sublicense, and/or sell copies of the Software, and to
9
+ # permit persons to whom the Software is furnished to do so, subject to
10
+ # the following conditions:
11
+ #
12
+ # The above copyright notice and this permission notice shall be
13
+ # included in all copies or substantial portions of the Software.
14
+ #
15
+ # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
16
+ # EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
17
+ # MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
18
+ # NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
19
+ # LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
20
+ # OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
21
+ # WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
22
+
23
+ module RightScale
24
+
25
+ # Abstracts a formatter which maps one kind of metadata output to another.
26
+ class MetadataFormatter
27
+
28
+ # RS_ is reserved for use by RightScale and should be avoided by users
29
+ # passing non-RightScale metadata to instances.
30
+ RS_METADATA_PREFIX = 'RS_'
31
+
32
+ attr_accessor :formatted_path_prefix, :format_metadata_override
33
+
34
+ # Initializer.
35
+ #
36
+ # === Parameters
37
+ # options[:formatted_path_prefix](String):: default prefix for formatted metadata keys
38
+ # options[:format_metadata_override](Proc(formatter, metadata):: format_metadata override or nil
39
+ def initialize(options = {})
40
+ # options
41
+ @formatted_path_prefix = options[:formatted_path_prefix] || RS_METADATA_PREFIX
42
+
43
+ # overrides
44
+ @format_metadata_override = options[:format_metadata_override]
45
+ end
46
+
47
+ # Formats metadata such that any hierarchical details flattened into simple
48
+ # key names.
49
+ #
50
+ # === Parameters
51
+ # tree_metadata(Hash):: tree of raw metadata
52
+ #
53
+ # === Returns
54
+ # flat_metadata(Hash):: flattened metadata
55
+ def format_metadata(metadata)
56
+ return @format_metadata_override.call(self, metadata) if @format_metadata_override
57
+ return recursive_flatten_metadata(metadata)
58
+ end
59
+
60
+ protected
61
+
62
+ # Recursively flattens metadata.
63
+ #
64
+ # === Parameters
65
+ # tree_metadata(Hash):: metadata to flatten
66
+ # flat_metadata(Hash):: flattened metadata or {}
67
+ # metadata_path(Array):: array of metadata path elements or []
68
+ # path_index(int):: path array index to update or 0
69
+ #
70
+ # === Returns
71
+ # flat_metadata(Hash):: flattened metadata
72
+ def recursive_flatten_metadata(tree_metadata, flat_metadata = {}, metadata_path = [], path_index = 0)
73
+ unless tree_metadata.empty?
74
+ tree_metadata.each do |key, value|
75
+ metadata_path[path_index] = key
76
+ if value.respond_to?(:has_key?)
77
+ recursive_flatten_metadata(value, flat_metadata, metadata_path, path_index + 1)
78
+ else
79
+ flat_path = flatten_metadata_path(metadata_path)
80
+ flat_metadata[flat_path] = value
81
+ end
82
+ end
83
+ metadata_path.pop
84
+ raise "Unexpected path" unless metadata_path.size == path_index
85
+ end
86
+ return flat_metadata
87
+ end
88
+
89
+ # Flattens a sequence of metadata keys into a simple key string
90
+ # distinguishing the path to a value stored at some depth in a tree of
91
+ # metadata.
92
+ #
93
+ # === Parameters
94
+ # metadata_path(Array):: array of metadata path elements
95
+ #
96
+ # === Returns
97
+ # flat_path(String):: flattened path
98
+ def flatten_metadata_path(metadata_path)
99
+ flat_path = metadata_path.join('_').gsub(/[\W,\/]/, '_').upcase
100
+ if @formatted_path_prefix && !(flat_path.start_with?(RS_METADATA_PREFIX) || flat_path.start_with?(@formatted_path_prefix))
101
+ return @formatted_path_prefix + flat_path
102
+ end
103
+ return flat_path
104
+ end
105
+
106
+ end # MetadataFormatter
107
+
108
+ end # RightScale
@@ -0,0 +1,128 @@
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.join(File.dirname(__FILE__), 'metadata_provider'))
24
+
25
+ module RightScale
26
+
27
+ # Abstracts a metadata provider which implements recursive tree building and
28
+ # relies on an external fetcher object
29
+ class MetadataProvider
30
+
31
+ attr_accessor :metadata_source, :metadata_tree_climber, :raw_metadata_writer
32
+
33
+ def initialize(options = {})
34
+ @metadata_source = options[:metadata_source]
35
+ @metadata_tree_climber = options[:metadata_tree_climber]
36
+ @raw_metadata_writer = options[:raw_metadata_writer]
37
+ @build_metadata_override = options[:build_metadata_override]
38
+ @query_override = options[:query_override]
39
+ end
40
+
41
+ # Queries cloud-specific instance metadata in an implementation-specific
42
+ # manner. The resulting tree of metadata is built using the given Hash-like
43
+ # class.
44
+ #
45
+ # === Return
46
+ # tree_metadata(Hash|String):: tree of metadata or leaf value or nil
47
+ # depending on options
48
+ #
49
+ # === Raises
50
+ # RightScale::MetadataSource::QueryFailed:: on failure to query metadata
51
+ def build_metadata
52
+ return @build_metadata_override.call(self) if @build_metadata_override
53
+ @root_path = @metadata_tree_climber.root_path
54
+ recursive_build_metadata(@root_path)
55
+ ensure
56
+ @root_path = nil
57
+ end
58
+
59
+ protected
60
+
61
+ # Queries the given path for metadata using the responder. The metadata is
62
+ # then (recursively) processed according to the tree surgeon's analysis and
63
+ # the metadata tree (or else flat data) is filled and returned.
64
+ #
65
+ # === Parameters
66
+ # path(String):: path to metadata
67
+ #
68
+ # === Return
69
+ # tree_metadata(Hash|String):: tree of metadata or raw value depending on
70
+ # options
71
+ def recursive_build_metadata(path)
72
+ # query
73
+ query_result = query(path)
74
+
75
+ # climb, if arboreal
76
+ if @metadata_tree_climber.has_children?(path, query_result)
77
+ metadata = @metadata_tree_climber.create_branch
78
+ child_names = @metadata_tree_climber.child_names(path, query_result)
79
+ child_names.each do |child_name|
80
+ if key = @metadata_tree_climber.branch_key(child_name)
81
+ branch_path = @metadata_source.append_branch_name(path, child_name)
82
+ metadata[key] = recursive_build_metadata(branch_path)
83
+ elsif key = @metadata_tree_climber.leaf_key(child_name)
84
+ leaf_path = @metadata_source.append_leaf_name(path, child_name)
85
+ query_result = @metadata_source.query(leaf_path)
86
+ write_raw_leaf_query_result(leaf_path, query_result)
87
+ metadata[key] = @metadata_tree_climber.create_leaf(leaf_path, query_result)
88
+ end
89
+ end
90
+ return metadata
91
+ end
92
+
93
+ # the only leaf.
94
+ write_raw_leaf_query_result(path, query_result)
95
+ return @metadata_tree_climber.create_leaf(path, query_result)
96
+ end
97
+
98
+ # Executes a query on the source using the given path.
99
+ #
100
+ # === Parameters
101
+ # path(String):: path to metadata
102
+ #
103
+ # === Return
104
+ # result(Object):: any kind of metadata
105
+ def query(path)
106
+ return @query_override.call(self, path) if @query_override
107
+ return @metadata_source.query(path)
108
+ end
109
+
110
+ # Writes raw responses to query writer, if given
111
+ #
112
+ # === Parameters
113
+ # path(String):: path to metadata
114
+ # query_result(String):: raw query result
115
+ #
116
+ # === Return
117
+ # always true
118
+ def write_raw_leaf_query_result(path, query_result)
119
+ if @raw_metadata_writer
120
+ subpath = (path.length > @root_path.length) ? path[@root_path.length..-1] : nil
121
+ @raw_metadata_writer.write(query_result, subpath)
122
+ end
123
+ true
124
+ end
125
+
126
+ end
127
+
128
+ end
@@ -0,0 +1,87 @@
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
+ # Interface for a metadata source.
26
+ class MetadataSource
27
+
28
+ # exceptions.
29
+ class QueryFailed < Exception; end
30
+
31
+ attr_reader :logger
32
+
33
+ def initialize(options)
34
+ raise ArgumentError, "options[:logger] is required" unless @logger = options[:logger]
35
+ end
36
+
37
+ # Appends a branch name to the given path.
38
+ #
39
+ # === Parameters
40
+ # path(String):: metadata path
41
+ # branch_name(String):: branch name to append
42
+ #
43
+ # === Return
44
+ # result(String):: updated path
45
+ def append_branch_name(path, branch_name)
46
+ # remove anything after equals.
47
+ branch_name = branch_name.gsub(/\=.*$/, '')
48
+ branch_name = "#{branch_name}/" unless '/' == branch_name[-1..-1]
49
+ return append_leaf_name(path, branch_name)
50
+ end
51
+
52
+ # Appends a leaf name to the given path.
53
+ #
54
+ # === Parameters
55
+ # path(String):: metadata path
56
+ # leaf_name(String):: leaf name to append
57
+ #
58
+ # === Return
59
+ # result(String):: updated path
60
+ def append_leaf_name(path, leaf_name)
61
+ path = "#{path}/" unless '/' == path[-1..-1]
62
+ return "#{path}#{URI.escape(leaf_name)}"
63
+ end
64
+
65
+ # Queries for metadata using the given path.
66
+ #
67
+ # === Parameters
68
+ # path(String):: metadata path
69
+ #
70
+ # === Return
71
+ # metadata(String):: query result or empty
72
+ #
73
+ # === Raises
74
+ # QueryFailed:: on any failure to query
75
+ def query(path)
76
+ raise NotImplementedError
77
+ end
78
+
79
+ # Releases any resources used to query metadata. Must be called before
80
+ # releasing source.
81
+ def finish
82
+ raise NotImplementedError
83
+ end
84
+
85
+ end
86
+
87
+ end
@@ -0,0 +1,207 @@
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
+ require 'tmpdir'
24
+ require 'openssl'
25
+ require 'base64'
26
+
27
+ module RightScale
28
+
29
+ module MetadataSources
30
+
31
+ # Provides metadata by reading a dictionary file on disk.
32
+ class CertificateMetadataSource < MetadataSource
33
+
34
+ # definitions for querying kinds of metadata by a simple path.
35
+ DEFAULT_CLOUD_METADATA_ROOT_PATH = "cloud_metadata"
36
+ DEFAULT_USER_METADATA_ROOT_PATH = "user_metadata"
37
+
38
+ attr_accessor :cloud_metadata_cert_store, :cloud_metadata_cert_issuer
39
+ attr_accessor :user_metadata_cert_store, :user_metadata_cert_issuer
40
+
41
+ def initialize(options)
42
+ super(options)
43
+ raise ArgumentError.new("options[:cloud_metadata_root_path] is required") unless @cloud_metadata_root_path = options[:cloud_metadata_root_path]
44
+ raise ArgumentError.new("options[:user_metadata_root_path] is required") unless @user_metadata_root_path = options[:user_metadata_root_path]
45
+
46
+ @cloud_metadata_cert_store = options[:cloud_metadata_cert_store]
47
+ @cloud_metadata_cert_issuer = options[:cloud_metadata_cert_issuer]
48
+
49
+ @user_metadata_cert_store = options[:user_metadata_cert_store]
50
+ @user_metadata_cert_issuer = options[:user_metadata_cert_issuer]
51
+ end
52
+
53
+ # Queries for metadata using the given path.
54
+ #
55
+ # === Parameters
56
+ # path(String):: metadata path
57
+ #
58
+ # === Return
59
+ # metadata(String):: query result or empty
60
+ #
61
+ # === Raises
62
+ # QueryFailed:: on any failure to query
63
+ def query(path)
64
+ result = ""
65
+ if path == @cloud_metadata_root_path
66
+ result = read_cert(@cloud_metadata_cert_store, @cloud_metadata_cert_issuer) if @cloud_metadata_cert_store && @cloud_metadata_cert_issuer
67
+ elsif path == @user_metadata_root_path
68
+ result = read_cert(@user_metadata_cert_store, @user_metadata_cert_issuer) if @user_metadata_cert_store && @user_metadata_cert_issuer
69
+ else
70
+ raise QueryFailed.new("Unknown path: #{path}")
71
+ end
72
+ result
73
+ rescue QueryFailed
74
+ raise
75
+ rescue Exception => e
76
+ raise QueryFailed.new(e.message)
77
+ end
78
+
79
+ # Nothing to do.
80
+ def finish
81
+ true
82
+ end
83
+
84
+ protected
85
+
86
+ def read_cert(cert_store, cert_issuer)
87
+ if ::RightScale::Platform.windows?
88
+ read_cert_windows(cert_store, cert_issuer)
89
+ else
90
+ read_cert_linux(cert_store, cert_issuer)
91
+ end
92
+ end
93
+
94
+ def read_cert_linux(cert_store, cert_issuer)
95
+ begin
96
+ data = File.read(cert_store)
97
+ cert = OpenSSL::X509::Certificate.new(data)
98
+
99
+ certificate_issuer = cert.issuer.to_s.split("/").sort
100
+ certificate_issuer.shift
101
+ raise QueryFailed.new("Certificate issuer does not match.") unless certificate_issuer == cert_issuer.split(", ").sort
102
+ raise QueryFailed.new("Unexpected certificate subject format: #{cert.subject.to_s}") unless cert.subject.to_s[1..3] == "CN="
103
+
104
+ result = Base64.decode64(cert.subject.to_s[4..-1].gsub('x0A',''))
105
+ rescue Exception => e
106
+ raise QueryFailed.new("Failed to retrieve metadata from cert given as \"#{cert_issuer}\" under \"#{cert_store}\"")
107
+ end
108
+
109
+ return result
110
+ end
111
+
112
+ READ_CERT_POWERSHELL_SCRIPT = <<EOF
113
+ # stop and fail script when a command fails.
114
+ $ErrorActionPreference = "Stop"
115
+
116
+ try
117
+ {
118
+ # requires Win2008+
119
+ if ([Int32]::Parse((Get-WmiObject Win32_OperatingSystem).Version.split('.')[0]) -lt 6)
120
+ {
121
+ throw "This version of Windows is not supported."
122
+ }
123
+
124
+ # check arguments.
125
+ if ($args.length -lt 3)
126
+ {
127
+ write-output "Usage: read_cert <cert store> <cert issuer> <output file>"
128
+ exit 101
129
+ }
130
+ $CERT_STORE = $args[0]
131
+ $CERT_ISSUER = $args[1]
132
+ $OUTPUT_FILE_PATH = $args[2]
133
+
134
+ # normalizes a Distinguished Name (DN) to ensure that parts appear in a consistent order in
135
+ # the DN string for comparison purposes. in Active Directory, DN parts are strictly ordered
136
+ # to make a full path to an object but other use cases (cert issuer, etc.) may not be as strict.
137
+ function NormalizeDN($dn)
138
+ {
139
+ [string]::join(',', ($dn.split(',') | foreach-object { $_.trim() } | sort-object))
140
+ }
141
+
142
+ # attempt to cert given by issuer (distinguished name) in the given cert store. select the most
143
+ # recently issued cert matching the given issuer by sorting certs in descending 'not before'
144
+ # order (i.e. last issued) and selecting first in the sorted array.
145
+ $compare = NormalizeDN($CERT_ISSUER)
146
+ $certs = @() + (get-item "$CERT_STORE\\*" | where-object { $compare -eq (NormalizeDN($_.issuer)) } | sort-object -Property notbefore -Descending)
147
+ $cert = $certs[0]
148
+ if ($NULL -eq $cert)
149
+ {
150
+ throw "Unable to find certificate matching ""$CERT_ISSUER"" under ""$CERT_STORE""."
151
+ }
152
+
153
+ # assumes that metadata is encoded in base-64 binary as .subject field of cert
154
+ # in form 'CN=<base-64 metadata string>'. if we don't match this pattern,
155
+ # then just bail out.
156
+ $encodedMetadata = $cert.subject
157
+ if (-not ($encodedMetadata.startsWith('CN=')))
158
+ {
159
+ throw "Unexpected cert subject format ""$encodedMetadata"""
160
+ }
161
+ # note that the base-64 string may or may not have double-quotes around it.
162
+ # not sure how double-quotes get inserted into the middle of the CN= phrase
163
+ # on Linux side (and not in Windows test), but life is a mystery.
164
+ $encodedMetadata = ($encodedMetadata.substring(3, $encodedMetadata.length - 3)).trim('"')
165
+ $decodedMetadata = ([System.Text.Encoding]::UTF8.GetString([System.Convert]::FromBase64String($encodedMetadata)))
166
+ $decodedMetadata | Out-File -Encoding ASCII $OUTPUT_FILE_PATH
167
+ }
168
+ catch
169
+ {
170
+ $ErrorActionPreference = "Continue"
171
+ write-error $_
172
+ exit 100
173
+ }
174
+
175
+ exit 0
176
+ EOF
177
+
178
+ def read_cert_windows(cert_store, cert_issuer)
179
+ result = ''
180
+ Dir.mktmpdir do |dir|
181
+ script_file_path = ::File.normalize_path(::File.join(dir, 'read_cert.ps1'))
182
+ output_file_path = ::File.normalize_path(::File.join(dir, 'output.txt'))
183
+ ::File.open(script_file_path, "w") { |f| f.write READ_CERT_POWERSHELL_SCRIPT }
184
+ cmd = ::RightScale::Platform.shell.format_shell_command(script_file_path, cert_store, cert_issuer, output_file_path)
185
+ result = `#{cmd}`
186
+ if $?.success?
187
+ if ::File.file?(output_file_path)
188
+ result = ::File.read(output_file_path)
189
+ else
190
+ result = result.to_s.strip
191
+ result = "No data was read from cert given as \"#{cert_issuer}\" under \"#{cert_store}\"." if result.empty?
192
+ raise QueryFailed.new(result)
193
+ end
194
+ else
195
+ result = result.to_s.strip
196
+ result = "Failed to retrieve metadata from cert given as \"#{cert_issuer}\" under \"#{cert_store}\"." if result.empty?
197
+ raise QueryFailed.new(result)
198
+ end
199
+ end
200
+ return result
201
+ end
202
+
203
+ end # CertificateMetadataSource
204
+
205
+ end # MetadataSources
206
+
207
+ end # RightScale