right_link 5.9.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- data/actors/agent_manager.rb +88 -0
- data/actors/instance_scheduler.rb +321 -0
- data/actors/instance_services.rb +64 -0
- data/actors/instance_setup.rb +567 -0
- data/bin/cloud +25 -0
- data/bin/cook_runner +44 -0
- data/bin/deploy +120 -0
- data/bin/enroll +385 -0
- data/bin/rad +32 -0
- data/bin/rchk +29 -0
- data/bin/rnac +39 -0
- data/bin/rs_connect +33 -0
- data/bin/rs_log_level +31 -0
- data/bin/rs_ohai +28 -0
- data/bin/rs_reenroll +31 -0
- data/bin/rs_run_recipe +34 -0
- data/bin/rs_run_right_script +34 -0
- data/bin/rs_shutdown +33 -0
- data/bin/rs_tag +33 -0
- data/bin/rs_thunk +33 -0
- data/bin/rstat +31 -0
- data/bin/system +16 -0
- data/ext/Rakefile +18 -0
- data/init/config.yml +5 -0
- data/init/init.rb +79 -0
- data/lib/chef/ohai_setup.rb +51 -0
- data/lib/chef/plugins/cloud.rb +91 -0
- data/lib/chef/plugins/cloudstack.rb +23 -0
- data/lib/chef/plugins/ec2.rb +23 -0
- data/lib/chef/plugins/linux/block_device2.rb +24 -0
- data/lib/chef/plugins/rackspace.rb +23 -0
- data/lib/chef/plugins/rightscale.rb +125 -0
- data/lib/chef/plugins/windows/network.rb +114 -0
- data/lib/chef/plugins.rb +74 -0
- data/lib/chef/providers/dns_dnsmadeeasy_provider.rb +81 -0
- data/lib/chef/providers/dns_resource.rb +100 -0
- data/lib/chef/providers/executable_schedule_provider.rb +70 -0
- data/lib/chef/providers/executable_schedule_resource.rb +144 -0
- data/lib/chef/providers/remote_recipe_provider.rb +86 -0
- data/lib/chef/providers/remote_recipe_resource.rb +101 -0
- data/lib/chef/providers/right_link_tag_provider.rb +73 -0
- data/lib/chef/providers/right_link_tag_resource.rb +59 -0
- data/lib/chef/providers/right_script_provider.rb +190 -0
- data/lib/chef/providers/right_script_resource.rb +113 -0
- data/lib/chef/providers/rs_shutdown_provider.rb +75 -0
- data/lib/chef/providers/rs_shutdown_resource.rb +55 -0
- data/lib/chef/providers/server_collection_provider.rb +66 -0
- data/lib/chef/providers/server_collection_resource.rb +93 -0
- data/lib/chef/providers/windows/powershell_provider.rb +151 -0
- data/lib/chef/providers/windows/powershell_resource.rb +111 -0
- data/lib/chef/providers/windows/unsupported_provider.rb +51 -0
- data/lib/chef/right_providers.rb +55 -0
- data/lib/chef/windows/ChefNodeCmdlet/ChefNodeCmdlet/ChefNodeCmdlet.csproj +104 -0
- data/lib/chef/windows/ChefNodeCmdlet/ChefNodeCmdlet/ChefNodeCmdlet.dll-Help.xml +141 -0
- data/lib/chef/windows/ChefNodeCmdlet/ChefNodeCmdlet/Exceptions.cs +182 -0
- data/lib/chef/windows/ChefNodeCmdlet/ChefNodeCmdlet/GetChefNodeCommand.cs +58 -0
- data/lib/chef/windows/ChefNodeCmdlet/ChefNodeCmdlet/GetChefNodeRequest.cs +46 -0
- data/lib/chef/windows/ChefNodeCmdlet/ChefNodeCmdlet/GetChefNodeResponse.cs +45 -0
- data/lib/chef/windows/ChefNodeCmdlet/ChefNodeCmdlet/GetCurrentResourceCommand.cs +58 -0
- data/lib/chef/windows/ChefNodeCmdlet/ChefNodeCmdlet/GetCurrentResourceRequest.cs +46 -0
- data/lib/chef/windows/ChefNodeCmdlet/ChefNodeCmdlet/GetCurrentResourceResponse.cs +45 -0
- data/lib/chef/windows/ChefNodeCmdlet/ChefNodeCmdlet/GetNewResourceCommand.cs +58 -0
- data/lib/chef/windows/ChefNodeCmdlet/ChefNodeCmdlet/GetNewResourceRequest.cs +46 -0
- data/lib/chef/windows/ChefNodeCmdlet/ChefNodeCmdlet/GetNewResourceResponse.cs +45 -0
- data/lib/chef/windows/ChefNodeCmdlet/ChefNodeCmdlet/GetNextActionCommand.cs +178 -0
- data/lib/chef/windows/ChefNodeCmdlet/ChefNodeCmdlet/GetNextActionRequest.cs +67 -0
- data/lib/chef/windows/ChefNodeCmdlet/ChefNodeCmdlet/GetNextActionResponse.cs +58 -0
- data/lib/chef/windows/ChefNodeCmdlet/ChefNodeCmdlet/GetNodeValueCommandBase.cs +142 -0
- data/lib/chef/windows/ChefNodeCmdlet/ChefNodeCmdlet/GetNodeValueRequestBase.cs +64 -0
- data/lib/chef/windows/ChefNodeCmdlet/ChefNodeCmdlet/GetNodeValueResponseBase.cs +69 -0
- data/lib/chef/windows/ChefNodeCmdlet/ChefNodeCmdlet/JsonTransport.cs +110 -0
- data/lib/chef/windows/ChefNodeCmdlet/ChefNodeCmdlet/PipeClient.cs +158 -0
- data/lib/chef/windows/ChefNodeCmdlet/ChefNodeCmdlet/PipeServer.cs +142 -0
- data/lib/chef/windows/ChefNodeCmdlet/ChefNodeCmdlet/Properties/AssemblyInfo.cs +16 -0
- data/lib/chef/windows/ChefNodeCmdlet/ChefNodeCmdlet/ProtocolConstants.cs +55 -0
- data/lib/chef/windows/ChefNodeCmdlet/ChefNodeCmdlet/ProtocolUtilities.cs +77 -0
- data/lib/chef/windows/ChefNodeCmdlet/ChefNodeCmdlet/ReadMe.txt +53 -0
- data/lib/chef/windows/ChefNodeCmdlet/ChefNodeCmdlet/SetChefNodeCommand.cs +59 -0
- data/lib/chef/windows/ChefNodeCmdlet/ChefNodeCmdlet/SetChefNodeRequest.cs +46 -0
- data/lib/chef/windows/ChefNodeCmdlet/ChefNodeCmdlet/SetChefNodeResponse.cs +58 -0
- data/lib/chef/windows/ChefNodeCmdlet/ChefNodeCmdlet/SetCurrentResourceCommand.cs +59 -0
- data/lib/chef/windows/ChefNodeCmdlet/ChefNodeCmdlet/SetCurrentResourceRequest.cs +46 -0
- data/lib/chef/windows/ChefNodeCmdlet/ChefNodeCmdlet/SetCurrentResourceResponse.cs +40 -0
- data/lib/chef/windows/ChefNodeCmdlet/ChefNodeCmdlet/SetNewResourceCommand.cs +59 -0
- data/lib/chef/windows/ChefNodeCmdlet/ChefNodeCmdlet/SetNewResourceRequest.cs +46 -0
- data/lib/chef/windows/ChefNodeCmdlet/ChefNodeCmdlet/SetNewResourceResponse.cs +40 -0
- data/lib/chef/windows/ChefNodeCmdlet/ChefNodeCmdlet/SetNodeValueCommandBase.cs +293 -0
- data/lib/chef/windows/ChefNodeCmdlet/ChefNodeCmdlet/SetNodeValueRequestBase.cs +75 -0
- data/lib/chef/windows/ChefNodeCmdlet/ChefNodeCmdlet/SetNodeValueResponseBase.cs +45 -0
- data/lib/chef/windows/ChefNodeCmdlet/ChefNodeCmdlet/Transport.cs +91 -0
- data/lib/chef/windows/ChefNodeCmdlet/ChefNodeCmdlet.sln +35 -0
- data/lib/chef/windows/ChefNodeCmdlet/TestChefNodeCmdlet/Program.cs +374 -0
- data/lib/chef/windows/ChefNodeCmdlet/TestChefNodeCmdlet/Properties/AssemblyInfo.cs +16 -0
- data/lib/chef/windows/ChefNodeCmdlet/TestChefNodeCmdlet/TestChefNodeCmdlet.csproj +65 -0
- data/lib/chef/windows/ChefNodeCmdlet/TestNextActionCmdlet/Program.cs +136 -0
- data/lib/chef/windows/ChefNodeCmdlet/TestNextActionCmdlet/Properties/AssemblyInfo.cs +36 -0
- data/lib/chef/windows/ChefNodeCmdlet/TestNextActionCmdlet/ReadMe.txt +46 -0
- data/lib/chef/windows/ChefNodeCmdlet/TestNextActionCmdlet/TestNextActionCmdlet.csproj +68 -0
- data/lib/chef/windows/bin/Newtonsoft.Json.dll +0 -0
- data/lib/chef/windows/chef_node_server.rb +463 -0
- data/lib/chef/windows/dynamic_powershell_provider.rb +296 -0
- data/lib/chef/windows/pipe_server.rb +283 -0
- data/lib/chef/windows/powershell_host.rb +285 -0
- data/lib/chef/windows/powershell_pipe_server.rb +136 -0
- data/lib/chef/windows/powershell_provider_base.rb +92 -0
- data/lib/chef/windows/scripts/run_loop.ps1 +105 -0
- data/lib/clouds/cloud.rb +557 -0
- data/lib/clouds/cloud_factory.rb +250 -0
- data/lib/clouds/cloud_utilities.rb +244 -0
- data/lib/clouds/clouds/azure.rb +106 -0
- data/lib/clouds/clouds/cloudstack.rb +114 -0
- data/lib/clouds/clouds/ec2.rb +113 -0
- data/lib/clouds/clouds/eucalyptus.rb +46 -0
- data/lib/clouds/clouds/google.rb +102 -0
- data/lib/clouds/clouds/none.rb +76 -0
- data/lib/clouds/clouds/openstack.rb +30 -0
- data/lib/clouds/clouds/rackspace-ng.rb +54 -0
- data/lib/clouds/clouds/rackspace.rb +78 -0
- data/lib/clouds/clouds/softlayer.rb +91 -0
- data/lib/clouds/metadata_formatter.rb +108 -0
- data/lib/clouds/metadata_provider.rb +128 -0
- data/lib/clouds/metadata_source.rb +87 -0
- data/lib/clouds/metadata_sources/certificate_metadata_source.rb +207 -0
- data/lib/clouds/metadata_sources/config_drive_metadata_source.rb +129 -0
- data/lib/clouds/metadata_sources/file_metadata_source.rb +74 -0
- data/lib/clouds/metadata_sources/http_metadata_source.rb +277 -0
- data/lib/clouds/metadata_sources/selective_metadata_source.rb +122 -0
- data/lib/clouds/metadata_tree_climber.rb +144 -0
- data/lib/clouds/metadata_writer.rb +155 -0
- data/lib/clouds/metadata_writers/dictionary_metadata_writer.rb +72 -0
- data/lib/clouds/metadata_writers/ruby_metadata_writer.rb +76 -0
- data/lib/clouds/metadata_writers/shell_metadata_writer.rb +121 -0
- data/lib/clouds/register_clouds.rb +34 -0
- data/lib/clouds.rb +32 -0
- data/lib/gem_dependencies.rb +83 -0
- data/lib/git_hooks/commit-msg.rb +7 -0
- data/lib/instance/agent_config.rb +168 -0
- data/lib/instance/agent_watcher.rb +233 -0
- data/lib/instance/audit_cook_stub.rb +104 -0
- data/lib/instance/audit_proxy.rb +247 -0
- data/lib/instance/bundle_queue.rb +104 -0
- data/lib/instance/cook/agent_connection.rb +109 -0
- data/lib/instance/cook/audit_logger.rb +165 -0
- data/lib/instance/cook/audit_stub.rb +142 -0
- data/lib/instance/cook/ca-bundle.crt +2794 -0
- data/lib/instance/cook/chef_state.rb +211 -0
- data/lib/instance/cook/cook.rb +306 -0
- data/lib/instance/cook/cook_state.rb +298 -0
- data/lib/instance/cook/cookbook_path_mapping.rb +66 -0
- data/lib/instance/cook/cookbook_repo_retriever.rb +190 -0
- data/lib/instance/cook/executable_sequence.rb +765 -0
- data/lib/instance/cook/external_parameter_gatherer.rb +190 -0
- data/lib/instance/cook/repose_downloader.rb +349 -0
- data/lib/instance/cook/shutdown_request_proxy.rb +121 -0
- data/lib/instance/cook.rb +41 -0
- data/lib/instance/downloader.rb +208 -0
- data/lib/instance/duplicable.rb +67 -0
- data/lib/instance/exceptions.rb +49 -0
- data/lib/instance/executable_sequence_proxy.rb +278 -0
- data/lib/instance/instance_commands.rb +577 -0
- data/lib/instance/instance_state.rb +633 -0
- data/lib/instance/json_utilities.rb +102 -0
- data/lib/instance/login_manager.rb +533 -0
- data/lib/instance/login_user_manager.rb +522 -0
- data/lib/instance/message_encoder.rb +118 -0
- data/lib/instance/multi_thread_bundle_queue.rb +232 -0
- data/lib/instance/operation_context.rb +60 -0
- data/lib/instance/options_bag.rb +65 -0
- data/lib/instance/payload_formatter.rb +46 -0
- data/lib/instance/policy.rb +53 -0
- data/lib/instance/policy_audit.rb +100 -0
- data/lib/instance/policy_manager.rb +146 -0
- data/lib/instance/reenroll_manager.rb +104 -0
- data/lib/instance/right_scripts_cookbook.rb +181 -0
- data/lib/instance/shutdown_request.rb +221 -0
- data/lib/instance/single_thread_bundle_queue.rb +189 -0
- data/lib/instance/volume_management.rb +450 -0
- data/lib/instance.rb +50 -0
- data/lib/repo_conf_generators/apt_conf_generators.rb +106 -0
- data/lib/repo_conf_generators/gem_conf_generators.rb +80 -0
- data/lib/repo_conf_generators/rightscale_conf_generators.rb +254 -0
- data/lib/repo_conf_generators/rightscale_key.pub +17 -0
- data/lib/repo_conf_generators/yum_conf_generators.rb +225 -0
- data/lib/repo_conf_generators.rb +30 -0
- data/lib/run_shell.rb +28 -0
- data/scripts/agent_checker.rb +571 -0
- data/scripts/agent_controller.rb +247 -0
- data/scripts/agent_deployer.rb +148 -0
- data/scripts/bundle_runner.rb +336 -0
- data/scripts/cloud_controller.rb +176 -0
- data/scripts/log_level_manager.rb +142 -0
- data/scripts/ohai_runner.rb +33 -0
- data/scripts/reenroller.rb +193 -0
- data/scripts/server_importer.rb +293 -0
- data/scripts/shutdown_client.rb +183 -0
- data/scripts/system_configurator.rb +367 -0
- data/scripts/tagger.rb +381 -0
- data/scripts/thunker.rb +356 -0
- metadata +418 -0
@@ -0,0 +1,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
|