right_link 5.9.0

Sign up to get free protection for your applications and to get access to all the features.
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