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,296 @@
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
+ # Dynamically create Chef providers from Powershell scripts.
26
+ # All the Powershell scripts defining a Chef provider should be contained in
27
+ # a folder under the cookbook 'powershell_providers' directory. For
28
+ # example creating an IIS Chef provider exposing a start web site and a stop
29
+ # web site action in Powershell would involve creating the following file
30
+ # hierarchy:
31
+ #
32
+ # `--iis_cookbook
33
+ # |-- metadata.json
34
+ # |-- metadata.rb
35
+ # |-- powershell_providers
36
+ # | `-- iis
37
+ # | |-- _init.ps1
38
+ # | |-- _load_current_resource.ps1
39
+ # | |-- start.ps1
40
+ # | |-- stop.ps1
41
+ # | `-- _term.ps1
42
+ # |-- recipes
43
+ # | |-- default.rb
44
+ # | |-- install.rb
45
+ # | |-- restart.rb
46
+ # | |-- start.rb
47
+ # | `-- stop.rb
48
+ # `-- resources
49
+ # `-- powershell_iis.rb
50
+ #
51
+ # In this example, the 'start.rb', 'stop.rb' and 'restart.rb' recipes would
52
+ # use the 'start' and/or 'stop' actions implemented by the corresponding
53
+ # Powershell scripts.
54
+ #
55
+ # The '_init.ps1' and '_term.ps1' are optional scripts that can contain
56
+ # initialization and cleanup code respectively. These two scripts are called
57
+ # once during a single Chef converge and can be used e.g. to load required
58
+ # .NET assemblies in the Powershell environment used to run the action
59
+ # scripts.
60
+ # The '_load_current_resource.ps1' script is also optional. Chef calls this
61
+ # script right before executing an action if it exists. The script should
62
+ # load any state from the system that the provider needs in order to run its
63
+ # actions (in this example this script could check whether the website is
64
+ # currently running so that the start and stop scripts would know whether any
65
+ # action is required on their part).
66
+ #
67
+ # Note that there should be a light weight resource defined for each
68
+ # Powershell provider. By default the resource name should match the name of
69
+ # the provider (that is the name of the folder containing the Powershell
70
+ # scripts). A lightweight resource can specify a different name for its
71
+ # corresponding provider though.
72
+ #
73
+ # Typical usage for this class involved calling 'generate_providers' multiple
74
+ # times then inspecting 'validation_errors'
75
+ class DynamicPowershellProvider
76
+
77
+ # Name of directory under cookbook that contains Powershell providers
78
+ POWERSHELL_PROVIDERS_DIR_NAME = 'powershell_providers'
79
+
80
+ # List of files with built-in behavior
81
+ INIT_SCRIPT = '_init'
82
+ TERM_SCRIPT = '_term'
83
+ LOAD_SCRIPT = '_load_current_resource'
84
+ BUILT_IN_SCRIPTS = [INIT_SCRIPT, TERM_SCRIPT, LOAD_SCRIPT]
85
+
86
+ # Hash of Powershell Chef providers validation errors keyed by provider path and
87
+ # initialized by 'generate_providers'
88
+ attr_reader :validation_errors
89
+
90
+ # Generated providers classes
91
+ # initialized by 'generate_providers'
92
+ attr_reader :providers
93
+
94
+ # chef class resource class naming
95
+ include Chef::Mixin::ConvertToClassName
96
+
97
+ # Initialize instance
98
+ def initialize
99
+ @validation_errors = {}
100
+ @providers = []
101
+ @providers_names = []
102
+ end
103
+
104
+ # Generate Chef providers from cookbooks in given path
105
+ # Initializes 'validation_errors' accordingly
106
+ # Skip providers that have already been created by this instance
107
+ #
108
+ # === Parameters
109
+ # cookbooks_path(String|Array):: Path(s) to cookbooks directories
110
+ #
111
+ # === Return
112
+ # providers(Array):: List of generated providers names
113
+ def generate_providers(cookbooks_paths)
114
+ providers = []
115
+ cookbooks_paths = [cookbooks_paths] unless cookbooks_paths.is_a?(Array)
116
+ cookbooks_paths.each do |cookbooks_path|
117
+ return [] unless File.directory?(cookbooks_path)
118
+ Dir[File.normalize_path(File.join(cookbooks_path, '*/'))].each do |cookbook_path|
119
+ cookbook_name = File.basename(cookbook_path)
120
+ Dir[File.normalize_path(File.join(cookbook_path, POWERSHELL_PROVIDERS_DIR_NAME, '*/'))].each do |provider_file_path|
121
+ provider_name = filename_to_qualified_string(cookbook_name, provider_file_path)
122
+ provider_class_name = convert_to_class_name(provider_name)
123
+ next if @providers_names.include?(provider_class_name)
124
+ generate_single_provider(provider_class_name, provider_file_path)
125
+ providers << provider_name
126
+ end
127
+ end
128
+ end
129
+ @providers_names += providers
130
+ true
131
+ end
132
+
133
+ protected
134
+ # Dynamically create provider class
135
+ #
136
+ # === Parameters
137
+ # name(String):: Powershell Chef provider class name
138
+ # path(String):: Path to directory containing Powershell scripts
139
+ #
140
+ # === Return
141
+ # true:: Always return true
142
+ def generate_single_provider(name, path)
143
+ Log.info("[chef] Creating Powershell provider #{name}")
144
+ all_scripts = Dir[File.join(path, "*#{::RightScale::Platform::Shell::POWERSHELL_V1x0_SCRIPT_EXTENSION}")]
145
+ action_scripts = all_scripts.select { |s| is_action_script?(s) }
146
+
147
+ new_provider = create_provider_class(name) do |provider|
148
+ action_script_names = []
149
+ action_scripts.each do |script|
150
+ action_script_name = File.basename(script, '.*').snake_case
151
+ action_script_names << action_script_name
152
+ action_name = "action_#{action_script_name}"
153
+ Log.info("[chef] Defining #{name}##{action_name} to run '#{script}'")
154
+ provider.class_eval("def #{action_name}; #{name}.run_script('#{script}'); end")
155
+ end
156
+
157
+ validate_resource_actions(File.join(path, "..", "..", "resources", "#{File.basename(path)}.rb"), action_script_names)
158
+
159
+ if load_script = all_scripts.detect { |s| File.basename(s, '.*').downcase == LOAD_SCRIPT }
160
+ Log.info("[chef] Defining #{name}#load_current_resource to run '#{load_script}'")
161
+ provider.class_eval(<<-EOF
162
+ def load_current_resource;
163
+ @current_resoure = #{resource_class_name(name)}.new(@new_resource.name)
164
+ RightScale::Windows::ChefNodeServer.instance.current_resource = @current_resource
165
+ #{name}.run_script('#{load_script}')
166
+ end
167
+ EOF
168
+ )
169
+ end
170
+ if init_script = all_scripts.detect { |s| File.basename(s, '.*').downcase == INIT_SCRIPT }
171
+ Log.info("[chef] Defining #{name}.init to run '#{init_script}'")
172
+ provider.instance_eval("def init(node); run_script('#{init_script}') if super(node); end")
173
+ end
174
+ if term_script = all_scripts.detect { |s| File.basename(s, '.*').downcase == TERM_SCRIPT }
175
+ Log.info("[chef] Defining #{name}.terminate to run '#{term_script}'")
176
+ provider.instance_eval("def terminate; begin; run_script('#{term_script}'); ensure; super; end; end")
177
+ end
178
+ Log.info("[chef] Done creating #{name}")
179
+ end
180
+
181
+ # register the provider with the default windows platform
182
+ Chef::Platform.platforms[:windows][:default].merge!(name.snake_case.gsub("::","_").to_sym => new_provider)
183
+
184
+ @providers << new_provider
185
+ true
186
+ end
187
+
188
+ # Given a fully qualified provider class name, generate the fully qualified class name of the associated
189
+ # resource (for lightweight resources).
190
+ #
191
+ # Note: Uses Chef::Mixin::ConvertToClassName to create the resource name in the same manner Chef does when
192
+ # creating lightweight resources
193
+ #
194
+ # === Parameters
195
+ # provider_class_name(String):: unscoped provider class name. Assumes the following <cookbook name><provider name>
196
+ #
197
+ # === Return
198
+ # (String):: Fully qualified resource class name
199
+ def resource_class_name(provider_class_name)
200
+ # Chef lwr/p resource and provider base names are the same
201
+ "Chef::Resource::#{provider_class_name}"
202
+ end
203
+
204
+ # Creates/overrides class with given name
205
+ # Also create modules if class name includes '::' and corresponding
206
+ # modules don't exist yet.
207
+ #
208
+ # === Parameters
209
+ # name(String):: Class name, may include module names as well (e.g. 'Foo::Bar')
210
+ # mod(Constant):: Module in which class should be created, Object by default
211
+ #
212
+ # === Block
213
+ # Given block should take one argument which corresponds to the class instance
214
+ def create_provider_class(name, mod=Object, &init)
215
+ parts = name.split('::', 2)
216
+ cls = nil
217
+ if parts.size == 1
218
+ if mod.const_defined?(name)
219
+ # Was already previously defined, undef all the known *instance* methods
220
+ # (class methods are inherited and should not be undefined)
221
+ cls = mod.const_get(name)
222
+ (cls.instance_methods - RightScale::PowershellProviderBase.instance_methods).each { |m| cls.class_eval("undef #{m}") }
223
+ init.call(cls)
224
+ else
225
+ # New class
226
+ cls = Class.new(RightScale::PowershellProviderBase) { |c| init.call(c) }
227
+ mod.const_set(name, cls)
228
+ end
229
+ else
230
+ m = parts[0]
231
+ mod = if mod.const_defined?(m)
232
+ # Recurse into existing module
233
+ mod.const_get(m)
234
+ else
235
+ # Create new module and recurse
236
+ mod.const_set(m, Module.new)
237
+ end
238
+ cls = create_provider_class(parts[1], mod, &init)
239
+ end
240
+ cls
241
+ end
242
+
243
+ # Is given filename a valid Chef Powershell provider action script?
244
+ #
245
+ # === Parameters
246
+ # filename(String):: File name to be tested
247
+ #
248
+ # === Return
249
+ # true:: If given filename is a valid Powershell provider action script
250
+ # false:: Otherwise
251
+ def is_action_script?(filename)
252
+ basename = File.basename(filename, '.*').downcase
253
+ valid_identifier = !!(basename =~ /^[_|a-z]+[a-z|0-9|_]*$/)
254
+ valid_identifier && !BUILT_IN_SCRIPTS.include?(basename)
255
+ end
256
+
257
+ # extract the list of actions from the given chef lightweight resource file
258
+ #
259
+ # === Parameters
260
+ # path(String):: Path to the resource file to be parsed
261
+ #
262
+ # === Return
263
+ # actions(String|Array):: actions defined for the given resource
264
+ def load_resource_actions(path)
265
+ # HACK: the resource file is the only known location of the actions defined for a
266
+ # given light weight resource. Do a quick and dirty parse of the resource file
267
+ #looking for the list of actions.
268
+ resource_content = File.read(path)
269
+ actions = /^\s*actions\s*(.*)$/.match(resource_content.gsub(/\s*,\s*\n/, ", "))[1].split(',').map { |action| action.strip.gsub(":", "") }
270
+ actions
271
+ end
272
+
273
+ # warn if resource action is defined, but corresponding powershell script does not exist
274
+ #
275
+ # === Parameters
276
+ # resource_file_path(String):: Path to the resource file to be parsed
277
+ # action_script_names(String|Array):: names of discovered powershell action scripts for this provider
278
+ #
279
+ # === Return
280
+ # true:: always
281
+ def validate_resource_actions(resource_file_path, action_script_names)
282
+ defined_actions = load_resource_actions(resource_file_path) if File.exists?(resource_file_path)
283
+ unless defined_actions.nil? || defined_actions.empty?
284
+ missing_action_definitions = []
285
+ defined_actions.each { |action_name| missing_action_definitions << action_name unless action_script_names.include?(action_name) }
286
+ if missing_action_definitions.size == 1
287
+ Log.info("[chef] Warning! no powershell script exists for the action: #{missing_action_definitions.first}")
288
+ elsif missing_action_definitions.size > 1
289
+ Log.info("[chef] Warning! no powershell scripts exist for the following actions: #{missing_action_definitions.join(", ")}")
290
+ end
291
+ end
292
+
293
+ true
294
+ end
295
+ end
296
+ end
@@ -0,0 +1,283 @@
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
+ require 'rubygems'
23
+ require 'eventmachine'
24
+ require 'win32/pipe'
25
+ require 'tempfile'
26
+
27
+ module RightScale
28
+
29
+ module Windows
30
+
31
+ # Provides an eventmachine callback handler for the server pipe.
32
+ module PipeServerHandler
33
+
34
+ CONNECTING_STATE = 0 # state between client connections
35
+ READING_STATE = 1 # after connection, receiving request
36
+ RESPONDING_STATE = 2 # received request, calculating response
37
+ WRITING_STATE = 3 # calculated response, respond before disconnecting
38
+
39
+ WAIT_SLEEP_DELAY_MSECS = 0.001 # yield to avoid busy looping
40
+ ASYNC_IO_SLEEP_DELAY_MSECS = 0.01 # yield to allow async I/O time to process
41
+
42
+ # === Parameters
43
+ # options(Hash):: A hash containing the following options by token name:
44
+ #
45
+ # target(Object):: Object defining handler methods to be called (required).
46
+ #
47
+ # request_handler(Token):: Token for request handler method name (required).
48
+ #
49
+ # request_query(Token):: Token for request query method name if server
50
+ # needs time to calculate a response (allows event loop to continue until
51
+ # such time as request_query returns true).
52
+ #
53
+ # pipe(IO):: pipe object (required).
54
+ def initialize(options)
55
+ raise "Missing required :target" unless @target = options[:target]
56
+ raise "Missing required :request_handler" unless @request_handler = options[:request_handler]
57
+ raise "Missing require :pipe" unless @pipe = options[:pipe]
58
+ @request_query = options[:request_query]
59
+ @unbound = false
60
+ @state = CONNECTING_STATE
61
+ @data = nil
62
+ end
63
+
64
+ # Callback from EM to asynchronously read the pipe stream. Note that this
65
+ # callback mechanism is deprecated after EM v0.12.8
66
+ def notify_readable
67
+ if @state == RESPONDING_STATE || @pipe.wait(WAIT_SLEEP_DELAY_MSECS)
68
+ if @pipe.pending?
69
+ handle_pending
70
+ else
71
+ handle_non_pending
72
+ end
73
+
74
+ # sleep a little to allow asynchronous I/O time to complete and
75
+ # avoid busy looping.
76
+ sleep ASYNC_IO_SLEEP_DELAY_MSECS
77
+ end
78
+ rescue Exception => e
79
+ RightScale::Log.error("Failed to send data to Powershell", e, :trace)
80
+ (disconnect rescue nil) if @state != CONNECTING_STATE
81
+ end
82
+
83
+ # Callback from EM to receive data, which we also use to handle the
84
+ # asynchronous data we read ourselves.
85
+ def receive_data(data)
86
+ # automagically append a newlineto make it easier to parse response.
87
+ result = @target.method(@request_handler).call(data)
88
+ result += "\n" unless result[-1] == "\n"[0]
89
+ return result
90
+ end
91
+
92
+ # Callback from EM to unbind.
93
+ def unbind
94
+ Log.debug("unbound")
95
+ @pipe.close rescue nil
96
+ @connected = false
97
+ @pipe = nil
98
+ @unbound = true
99
+ end
100
+
101
+ # Forces detachment of the handler unless already unbound.
102
+ def force_detach
103
+ # No need to use next tick to prevent issue in EM where
104
+ # descriptors list gets out-of-sync when calling detach
105
+ # in an unbind callback
106
+ detach unless @unbound
107
+ end
108
+
109
+ protected
110
+
111
+ # Handles any pending I/O from asynchronous pipe server.
112
+ def handle_pending
113
+ case @state
114
+ when CONNECTING_STATE
115
+ Log.debug("connection pending")
116
+ connected
117
+ if @pipe.read
118
+ consume_pipe_buffer if not @pipe.pending?
119
+ else
120
+ disconnect
121
+ end
122
+ when READING_STATE
123
+ Log.debug("read pending")
124
+ if @pipe.transferred == 0
125
+ disconnect
126
+ else
127
+ consume_pipe_buffer
128
+ end
129
+ when RESPONDING_STATE
130
+ responding
131
+ when WRITING_STATE
132
+ Log.debug("write pending")
133
+ if @pipe.transferred >= @pipe.size
134
+ write_complete
135
+ end
136
+ end
137
+ end
138
+
139
+ # Handles state progression when there is no pending I/O.
140
+ def handle_non_pending
141
+ case @state
142
+ when CONNECTING_STATE
143
+ Log.debug("waiting for connection")
144
+ if @pipe.connect
145
+ connected
146
+ end
147
+ when READING_STATE
148
+ Log.debug("still reading request")
149
+ if @pipe.read
150
+ consume_pipe_buffer if not @pipe.pending?
151
+ else
152
+ disconnect
153
+ end
154
+ when RESPONDING_STATE
155
+ responding
156
+ when WRITING_STATE
157
+ Log.debug("done writing request")
158
+ write_complete
159
+ end
160
+ end
161
+
162
+ # Acknowledges client connected by changing to reading state.
163
+ def connected
164
+ Log.debug("connected")
165
+
166
+ # sleep a little to allow asynchronous I/O time to complete and
167
+ # avoid busy looping before reading from pipe.
168
+ sleep ASYNC_IO_SLEEP_DELAY_MSECS
169
+ @state = READING_STATE
170
+ end
171
+
172
+ # Acknowledges request received by invoking EM receive_data method.
173
+ def read_complete
174
+ Log.debug("read_complete")
175
+ if @data && @data.length > 0
176
+ Log.debug("received: #{@data}")
177
+ @state = RESPONDING_STATE
178
+ else
179
+ disconnect
180
+ end
181
+ end
182
+
183
+ # Determines if the server implementation requires a callback to see if
184
+ # a response is available of if the response must be delayed. In the latter
185
+ # case, the event machine is allowed to continue until the request_query
186
+ # method returns true.
187
+ def responding
188
+ respond if (@request_query.nil? || @target.method(@request_query).call(@data))
189
+ end
190
+
191
+ # Asks the server implementation for the calculated response and puts it
192
+ # on the wire.
193
+ def respond
194
+ response = receive_data(@data)
195
+ @data = nil
196
+ if response && response.length > 0
197
+ Log.debug("writing response = #{response}")
198
+ if @pipe.write(response)
199
+ if @pipe.pending?
200
+ @state = WRITING_STATE
201
+ else
202
+ write_complete
203
+ end
204
+ else
205
+ disconnect
206
+ end
207
+ else
208
+ disconnect
209
+ end
210
+ end
211
+
212
+ # Acknowledges response sent by disconnecting and waiting for next client.
213
+ def write_complete
214
+ Log.debug("write_complete")
215
+ disconnect
216
+ end
217
+
218
+ # Disconnects from client and resumes waiting for next client.
219
+ def disconnect
220
+ Log.debug("disconnect")
221
+ @pipe.disconnect
222
+ @state = CONNECTING_STATE
223
+ @data = nil
224
+ end
225
+
226
+ # Consumes the current contents of the pipe buffer.
227
+ def consume_pipe_buffer
228
+ buffer = @pipe.buffer.clone
229
+ Log.debug("before consume @data = #{@data}")
230
+ if @data
231
+ @data += buffer
232
+ else
233
+ @data = buffer
234
+ end
235
+ Log.debug("after consume @data = #{@data}")
236
+
237
+ # newline delimits each complete request. the pending flag is not an
238
+ # indication that the complete request has been received because the
239
+ # complete request can be buffered in memory and not be "pending".
240
+ if buffer.index("\n")
241
+ read_complete
242
+ end
243
+ end
244
+ end
245
+
246
+ # Provides a generic Windows named pipe server based on eventmachine.
247
+ class PipeServer < ::Win32::Pipe::Server
248
+
249
+ # Hack which allows eventmachine to schedule I/O for this non-Ruby IO
250
+ # object. EM needs a real file number, so this method uses a temporary
251
+ # file.
252
+ #
253
+ # === Returns
254
+ # fileno(Integer):: temporary file's file number.
255
+ def fileno
256
+ @temp_file = Tempfile.new("RS-pipe-server") unless @temp_file
257
+ return @temp_file.fileno
258
+ end
259
+
260
+ # Hack which fixes a bug with pending write never resetting pending_io
261
+ # flag, causing state machine never to reenter waiting state. also resets
262
+ # buffer which can be set to a small size by a tiny overlapped I/O
263
+ # operation.
264
+ def disconnect
265
+ @pending_io = false
266
+ @buffer = 0.chr * PIPE_BUFFER_SIZE
267
+ super
268
+ end
269
+
270
+ # Closes temporary file before closing pipe.
271
+ def close
272
+ if @temp_file
273
+ @temp_file.close
274
+ @temp_file = nil
275
+ end
276
+ super.close
277
+ end
278
+
279
+ end
280
+
281
+ end
282
+
283
+ end