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,567 @@
|
|
1
|
+
#
|
2
|
+
# Copyright (c) 2009-2012 RightScale Inc
|
3
|
+
#
|
4
|
+
# Permission is hereby granted, free of charge, to any person obtaining
|
5
|
+
# a copy of this software and associated documentation files (the
|
6
|
+
# "Software"), to deal in the Software without restriction, including
|
7
|
+
# without limitation the rights to use, copy, modify, merge, publish,
|
8
|
+
# distribute, sublicense, and/or sell copies of the Software, and to
|
9
|
+
# permit persons to whom the Software is furnished to do so, subject to
|
10
|
+
# the following conditions:
|
11
|
+
#
|
12
|
+
# The above copyright notice and this permission notice shall be
|
13
|
+
# included in all copies or substantial portions of the Software.
|
14
|
+
#
|
15
|
+
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
16
|
+
# EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
17
|
+
# MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
18
|
+
# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
19
|
+
# LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
20
|
+
# OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
21
|
+
# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
22
|
+
|
23
|
+
require File.normalize_path(File.join(File.dirname(__FILE__), '..', 'lib', 'instance', 'volume_management'))
|
24
|
+
|
25
|
+
class InstanceSetup
|
26
|
+
|
27
|
+
include RightScale::Actor
|
28
|
+
include RightScale::OperationResultHelper
|
29
|
+
include RightScale::VolumeManagementHelper
|
30
|
+
|
31
|
+
CONFIG_YAML_FILE = File.normalize_path(File.join(RightScale::Platform.filesystem.right_link_static_state_dir, 'features.yml'))
|
32
|
+
|
33
|
+
CONFIG=\
|
34
|
+
if File.exists?(CONFIG_YAML_FILE)
|
35
|
+
RightSupport::Config.features(CONFIG_YAML_FILE)
|
36
|
+
else
|
37
|
+
RightSupport::Config.features({})
|
38
|
+
end
|
39
|
+
|
40
|
+
expose :report_state
|
41
|
+
|
42
|
+
# Amount of seconds to wait between set_r_s_version calls attempts
|
43
|
+
RECONNECT_DELAY = 5
|
44
|
+
|
45
|
+
# Amount of seconds to wait before shutting down if boot hasn't completed
|
46
|
+
SUICIDE_DELAY = 45 * 60
|
47
|
+
|
48
|
+
# Time between attempts to get missing inputs.
|
49
|
+
MISSING_INPUT_RETRY_DELAY_SECS = 20
|
50
|
+
|
51
|
+
# Maximum time between nag audits for missing inputs.
|
52
|
+
MISSING_INPUT_AUDIT_DELAY_SECS = 2 * 60
|
53
|
+
|
54
|
+
# Tag set on instances that are part of an array
|
55
|
+
AUTO_LAUNCH_TAG ='rs_launch:type=auto'
|
56
|
+
|
57
|
+
# How long to wait for tag query before proceeding to boot sequence
|
58
|
+
INITIAL_TAG_QUERY_TIMEOUT = 20
|
59
|
+
|
60
|
+
# Boot if and only if instance state is 'booting'
|
61
|
+
# Prime timer for shutdown on unsuccessful boot ('suicide' functionality)
|
62
|
+
#
|
63
|
+
# === Parameters
|
64
|
+
# agent(RightScale::Agent):: Host agent
|
65
|
+
def initialize(agent)
|
66
|
+
@agent_identity = agent.identity
|
67
|
+
@got_boot_bundle = false
|
68
|
+
EM.threadpool_size = 1
|
69
|
+
RightScale::InstanceState.init(@agent_identity)
|
70
|
+
|
71
|
+
# Reset log level here even though already initialized in the agent
|
72
|
+
# so that InstanceState gets notified of the level in use
|
73
|
+
RightScale::Log.level = agent.options[:log_level] if agent.options[:log_level]
|
74
|
+
|
75
|
+
# Schedule boot sequence, don't run it now so agent is registered first
|
76
|
+
if RightScale::InstanceState.value == 'booting'
|
77
|
+
EM.next_tick { init_boot }
|
78
|
+
else
|
79
|
+
RightScale::Sender.instance.initialize_offline_queue
|
80
|
+
RightScale::Sender.instance.start_offline_queue
|
81
|
+
|
82
|
+
# handle case of a decommission which was abruptly interrupted and never
|
83
|
+
# shutdown the instance (likely due to a decommission script which induced
|
84
|
+
# an unexpected fault in the agent).
|
85
|
+
#
|
86
|
+
# note that upon successfuly reboot (or start of a stopped instance) the
|
87
|
+
# instance state file is externally reset to a rebooting state (thus
|
88
|
+
# avoiding the dreaded infinite reboot/stop scenario).
|
89
|
+
if RightScale::InstanceState.value == 'decommissioning' && (kind = RightScale::InstanceState.decommission_type)
|
90
|
+
EM.next_tick { recover_decommission(user_id = nil, skip_db_update = false, kind) }
|
91
|
+
end
|
92
|
+
end
|
93
|
+
end
|
94
|
+
|
95
|
+
# Retrieve current instance state
|
96
|
+
#
|
97
|
+
# === Return
|
98
|
+
# (RightScale::OperationResult):: Success operation result containing instance state
|
99
|
+
def report_state
|
100
|
+
success_result(RightScale::InstanceState.value)
|
101
|
+
end
|
102
|
+
|
103
|
+
# Handle disconnected notification from broker, enter offline mode
|
104
|
+
#
|
105
|
+
# === Parameters
|
106
|
+
# status(Symbol):: Connection status, one of :connected or :disconnected
|
107
|
+
#
|
108
|
+
# === Return
|
109
|
+
# true:: Always return true
|
110
|
+
def connection_status(status)
|
111
|
+
if status == :disconnected
|
112
|
+
RightScale::Sender.instance.enable_offline_mode
|
113
|
+
else
|
114
|
+
RightScale::Sender.instance.disable_offline_mode
|
115
|
+
end
|
116
|
+
true
|
117
|
+
end
|
118
|
+
|
119
|
+
protected
|
120
|
+
|
121
|
+
# We start off by setting the instance 'r_s_version' in the core site,
|
122
|
+
# then perform an initial tag query before proceeding with the boot
|
123
|
+
# sequence.
|
124
|
+
#
|
125
|
+
# === Return
|
126
|
+
# true:: Always return true
|
127
|
+
def init_boot
|
128
|
+
RightScale::Sender.instance.initialize_offline_queue
|
129
|
+
payload = {:agent_identity => @agent_identity,
|
130
|
+
:r_s_version => RightScale::AgentConfig.protocol_version,
|
131
|
+
:resource_uid => RightScale::InstanceState.resource_uid}
|
132
|
+
req = RightScale::IdempotentRequest.new('/booter/declare', payload, :retry_on_error => true)
|
133
|
+
req.callback do |res|
|
134
|
+
RightScale::Sender.instance.start_offline_queue
|
135
|
+
init_fetch_tags
|
136
|
+
end
|
137
|
+
req.run
|
138
|
+
true
|
139
|
+
end
|
140
|
+
|
141
|
+
# Perform a one=time initial tag query in order to ensure that InstanceState
|
142
|
+
# and CookState have the fresh possible list of tags. This is advantageous
|
143
|
+
# because the agent's tags can affect the agent and the cook subprocess
|
144
|
+
# in many ways.
|
145
|
+
#
|
146
|
+
# === Return
|
147
|
+
# true:: Always return true
|
148
|
+
def init_fetch_tags
|
149
|
+
RightScale::Log.info "Performing initial-startup tag query"
|
150
|
+
RightScale::AgentTagManager.instance.tags(:timeout=>INITIAL_TAG_QUERY_TIMEOUT) do |tags|
|
151
|
+
if tags.is_a?(String)
|
152
|
+
# AgentTagManager could give us a String (error message)
|
153
|
+
RightScale::Log.error("Failed to query tags during initial startup: #{tags}")
|
154
|
+
else
|
155
|
+
#CookState is also updated in ExecutableSequenceProxy#run, but doing
|
156
|
+
#it here ensures that CookState is initially convergent with InstanceState.
|
157
|
+
RightScale::InstanceState.startup_tags = tags
|
158
|
+
RightScale::CookState.update(RightScale::InstanceState)
|
159
|
+
|
160
|
+
# we are no longer freezing log level for v5.8+
|
161
|
+
tagged_log_level = ::RightScale::CookState.dev_log_level
|
162
|
+
RightScale::Log.level = tagged_log_level if tagged_log_level
|
163
|
+
RightScale::Log.info("Tags discovered at initial startup: #{tags.inspect} (dev mode = #{::RightScale::CookState.dev_mode_enabled?})")
|
164
|
+
end
|
165
|
+
|
166
|
+
# Setup suicide timer which will cause instance to shutdown if the rs_launch:type=auto tag
|
167
|
+
# is set and the instance has not gotten its boot bundle after SUICIDE_DELAY seconds and this is
|
168
|
+
# the first time this instance boots
|
169
|
+
if RightScale::InstanceState.initial_boot?
|
170
|
+
@suicide_timer = EM::Timer.new(SUICIDE_DELAY) do
|
171
|
+
if RightScale::InstanceState.startup_tags.include?(AUTO_LAUNCH_TAG) && !@got_boot_bundle
|
172
|
+
msg = "Shutting down after having tried to boot for #{SUICIDE_DELAY / 60} minutes"
|
173
|
+
RightScale::Log.error(msg)
|
174
|
+
@audit.append_error(msg, :category => RightScale::EventCategories::CATEGORY_ERROR) if @audit
|
175
|
+
RightScale::Platform.controller.shutdown
|
176
|
+
end
|
177
|
+
end
|
178
|
+
end
|
179
|
+
|
180
|
+
enable_managed_login
|
181
|
+
end
|
182
|
+
|
183
|
+
true
|
184
|
+
end
|
185
|
+
|
186
|
+
# Enable managed SSH for this instance, then continue with boot. Ensures that
|
187
|
+
# managed SSH users can login to troubleshoot stranded and other 'interesting' events
|
188
|
+
#
|
189
|
+
# === Return
|
190
|
+
# true:: Always return true
|
191
|
+
def enable_managed_login
|
192
|
+
if !RightScale::LoginManager.instance.supported_by_platform?
|
193
|
+
setup_volumes
|
194
|
+
else
|
195
|
+
ssh_host_keys = RightScale::LoginManager.instance.get_ssh_host_keys
|
196
|
+
req = RightScale::IdempotentRequest.new('/booter/get_login_policy',
|
197
|
+
{ :agent_identity => @agent_identity,
|
198
|
+
:ssh_host_keys=>ssh_host_keys })
|
199
|
+
|
200
|
+
req.callback do |policy|
|
201
|
+
audit = RightScale::AuditProxy.new(policy.audit_id)
|
202
|
+
begin
|
203
|
+
RightScale::LoginManager.instance.update_policy(policy, @agent_identity) do |audit_content|
|
204
|
+
if audit_content
|
205
|
+
audit.create_new_section('Managed login enabled')
|
206
|
+
audit.append_info(audit_content)
|
207
|
+
end
|
208
|
+
end
|
209
|
+
rescue Exception => e
|
210
|
+
audit.create_new_section('Failed to enable managed login')
|
211
|
+
audit.append_error("Error applying login policy: #{e}", :category => RightScale::EventCategories::CATEGORY_ERROR)
|
212
|
+
RightScale::Log.error('Failed to enable managed login', e, :trace)
|
213
|
+
end
|
214
|
+
|
215
|
+
setup_volumes
|
216
|
+
end
|
217
|
+
|
218
|
+
req.errback do |res|
|
219
|
+
RightScale::Log.error('Could not get login policy', res)
|
220
|
+
boot
|
221
|
+
end
|
222
|
+
|
223
|
+
req.run
|
224
|
+
end
|
225
|
+
end
|
226
|
+
|
227
|
+
# Attach EBS volumes to drive letters on Windows
|
228
|
+
#
|
229
|
+
# === Return
|
230
|
+
# true:: Always return true
|
231
|
+
def setup_volumes
|
232
|
+
# managing planned volumes is currently only needed in Windows and only if
|
233
|
+
# this is not a reboot scenario.
|
234
|
+
if !RightScale::Platform.windows? || RightScale::InstanceState.reboot?
|
235
|
+
boot
|
236
|
+
else
|
237
|
+
RightScale::AuditProxy.create(@agent_identity, 'Planned volume management') do |audit|
|
238
|
+
@audit = audit
|
239
|
+
manage_planned_volumes do
|
240
|
+
@audit = nil
|
241
|
+
boot
|
242
|
+
end
|
243
|
+
end
|
244
|
+
end
|
245
|
+
true
|
246
|
+
end
|
247
|
+
|
248
|
+
# Retrieve software repositories and configure mirrors accordingly then proceed to
|
249
|
+
# retrieving and running boot bundle.
|
250
|
+
#
|
251
|
+
# === Return
|
252
|
+
# true:: Always return true
|
253
|
+
def boot
|
254
|
+
req = RightScale::IdempotentRequest.new('/booter/get_repositories', { :agent_identity => @agent_identity })
|
255
|
+
|
256
|
+
req.callback do |res|
|
257
|
+
@audit = RightScale::AuditProxy.new(res.audit_id)
|
258
|
+
if CONFIG['package_repositories']['freeze'] && !RightScale::Platform.windows? && !RightScale::Platform.darwin?
|
259
|
+
reps = res.repositories
|
260
|
+
audit_content = "Using the following software repositories:\n"
|
261
|
+
reps.each { |rep| audit_content += " - #{rep.to_s}\n" }
|
262
|
+
@audit.create_new_section('Configuring software repositories')
|
263
|
+
@audit.append_info(audit_content)
|
264
|
+
configure_repositories(reps)
|
265
|
+
@audit.update_status('Software repositories configured')
|
266
|
+
end
|
267
|
+
@audit.create_new_section('Preparing boot bundle')
|
268
|
+
prepare_boot_bundle do |prep_res|
|
269
|
+
if prep_res.success?
|
270
|
+
@audit.update_status('Boot bundle ready')
|
271
|
+
run_boot_bundle(prep_res.content) do |boot_res|
|
272
|
+
if boot_res.success?
|
273
|
+
# want to go operational only if no immediate shutdown request.
|
274
|
+
# immediate shutdown requires we stay in the current (booting)
|
275
|
+
# state pending full reboot/restart of instance so that we don't
|
276
|
+
# bounce between operational and booting in a multi-reboot case.
|
277
|
+
# if shutdown is deferred, then go operational before shutdown.
|
278
|
+
shutdown_request = RightScale::ShutdownRequest.instance
|
279
|
+
if shutdown_request.immediately?
|
280
|
+
# process the shutdown request immediately since the
|
281
|
+
# operational bundles queue will not start in this case.
|
282
|
+
errback = lambda { strand("Failed to #{shutdown_request} while running boot sequence") }
|
283
|
+
shutdown_request.process(errback, @audit)
|
284
|
+
else
|
285
|
+
# any deferred shutdown request was submitted to the
|
286
|
+
# operational bundles queue and will execute later.
|
287
|
+
RightScale::InstanceState.value = 'operational'
|
288
|
+
end
|
289
|
+
else
|
290
|
+
strand('Failed to run boot sequence', boot_res)
|
291
|
+
end
|
292
|
+
end
|
293
|
+
else
|
294
|
+
strand('Failed to prepare boot bundle', prep_res)
|
295
|
+
end
|
296
|
+
end
|
297
|
+
end
|
298
|
+
|
299
|
+
req.errback do|res|
|
300
|
+
strand('Failed to retrieve software repositories', res)
|
301
|
+
end
|
302
|
+
|
303
|
+
req.run
|
304
|
+
true
|
305
|
+
end
|
306
|
+
|
307
|
+
# Log error to local log file and set instance state to stranded
|
308
|
+
#
|
309
|
+
# === Parameters
|
310
|
+
# msg(String):: Error message that will be audited and logged
|
311
|
+
# res(RightScale::OperationResult):: Operation result with additional information
|
312
|
+
#
|
313
|
+
# === Return
|
314
|
+
# true:: Always return true
|
315
|
+
def strand(msg, res = nil)
|
316
|
+
# attempt to provide details of exception or result which caused stranding.
|
317
|
+
detailed = nil
|
318
|
+
if msg.kind_of? Exception
|
319
|
+
e = msg
|
320
|
+
detailed = RightScale::Log.format("Instance stranded", e, :trace)
|
321
|
+
msg = e.message
|
322
|
+
end
|
323
|
+
res = res.content if res.respond_to?(:content)
|
324
|
+
msg += ": #{res}" if res
|
325
|
+
|
326
|
+
@audit.append_error(msg, :category => RightScale::EventCategories::CATEGORY_ERROR) if @audit
|
327
|
+
RightScale::Log.error(detailed) if detailed
|
328
|
+
|
329
|
+
# set stranded state last in case this would prevent final audits from being
|
330
|
+
# sent (as it does in testing).
|
331
|
+
RightScale::InstanceState.value = 'stranded'
|
332
|
+
true
|
333
|
+
end
|
334
|
+
|
335
|
+
# Overrides default shutdown management failure handler in order to strand.
|
336
|
+
def handle_failed_shutdown_request(audit, msg, res = nil)
|
337
|
+
@audit = audit
|
338
|
+
strand(msg, res)
|
339
|
+
end
|
340
|
+
|
341
|
+
# Configure software repositories
|
342
|
+
# Note: the configurators may return errors when the platform is not what they expect,
|
343
|
+
# for now log error and keep going (to replicate legacy behavior).
|
344
|
+
#
|
345
|
+
# === Parameters
|
346
|
+
# repositories(Array[(RepositoryInstantiation)]):: repositories to be configured
|
347
|
+
#
|
348
|
+
# === Return
|
349
|
+
# true:: Always return true
|
350
|
+
def configure_repositories(repositories)
|
351
|
+
repositories.each do |repo|
|
352
|
+
begin
|
353
|
+
klass = repo.name.to_const
|
354
|
+
unless klass.nil?
|
355
|
+
fz = nil
|
356
|
+
if repo.frozen_date
|
357
|
+
# gives us date for yesterday since the mirror for today may not have been generated yet
|
358
|
+
fz = (Date.parse(repo.frozen_date) - 1).to_s
|
359
|
+
fz.gsub!(/-/,"")
|
360
|
+
end
|
361
|
+
klass.generate("none", repo.base_urls, fz)
|
362
|
+
end
|
363
|
+
rescue RightScale::Exceptions::PlatformError => e
|
364
|
+
# we don't want to use words like error or failed here just because we
|
365
|
+
# rely on exceptions for flow-of-control in repo conf generators because
|
366
|
+
# that leads to tickets being filed. just say "hey buddy, it didn't work
|
367
|
+
# out but it's all okay"
|
368
|
+
RightScale::Log.info("Repository configurator #{repo.name} is not suitable for host OS: #{e.message}")
|
369
|
+
rescue Exception => e
|
370
|
+
RightScale::Log.error("Repository configurator #{repo.name} failed", e)
|
371
|
+
end
|
372
|
+
end
|
373
|
+
if system('which apt-get')
|
374
|
+
ENV['DEBIAN_FRONTEND'] = 'noninteractive' # this prevents prompts
|
375
|
+
@audit.append_output(`apt-get update 2>&1`)
|
376
|
+
elsif system('which yum')
|
377
|
+
@audit.append_output(`yum clean metadata`)
|
378
|
+
end
|
379
|
+
true
|
380
|
+
end
|
381
|
+
|
382
|
+
# Retrieve missing inputs if any
|
383
|
+
#
|
384
|
+
# === Block
|
385
|
+
# Calls given block passing in one argument of type RightScale::OperationResult
|
386
|
+
# The argument contains either a failure with associated message or success
|
387
|
+
# with the corresponding boot bundle.
|
388
|
+
#
|
389
|
+
# === Return
|
390
|
+
# true:: Always return true
|
391
|
+
def prepare_boot_bundle(&cb)
|
392
|
+
payload = {:agent_identity => @agent_identity, :audit_id => @audit.audit_id}
|
393
|
+
req = RightScale::IdempotentRequest.new('/booter/get_boot_bundle', payload)
|
394
|
+
|
395
|
+
req.callback do |bundle|
|
396
|
+
if bundle.executables.any? { |e| !e.ready }
|
397
|
+
retrieve_missing_inputs(bundle) { cb.call(success_result(bundle)) }
|
398
|
+
else
|
399
|
+
yield success_result(bundle)
|
400
|
+
end
|
401
|
+
end
|
402
|
+
|
403
|
+
req.errback do |res|
|
404
|
+
yield error_result(RightScale::Log.format('Failed to retrieve boot scripts', res))
|
405
|
+
end
|
406
|
+
|
407
|
+
req.run
|
408
|
+
end
|
409
|
+
|
410
|
+
# Retrieve missing inputs for recipes and RightScripts stored in
|
411
|
+
# @recipes and @scripts respectively, update recipe attributes, RightScript
|
412
|
+
# parameters and ready fields (set ready field to true if attempt was successful,
|
413
|
+
# false otherwise).
|
414
|
+
# This is for environment variables that we are waiting on.
|
415
|
+
# Retries forever.
|
416
|
+
#
|
417
|
+
# === Parameters
|
418
|
+
# bundle(ExecutableBundle):: bundle containing at least one script/recipe with missing inputs.
|
419
|
+
# last_missing_inputs(Hash):: state of missing inputs for refreshing audit message as needed or nil.
|
420
|
+
#
|
421
|
+
# === Block
|
422
|
+
# Continuation block, will be called once attempt to retrieve attributes is completed
|
423
|
+
#
|
424
|
+
# === Return
|
425
|
+
# true:: Always return true
|
426
|
+
def retrieve_missing_inputs(bundle, last_missing_inputs = nil, &cb)
|
427
|
+
scripts = bundle.executables.select { |e| e.is_a?(RightScale::RightScriptInstantiation) }
|
428
|
+
recipes = bundle.executables.select { |e| e.is_a?(RightScale::RecipeInstantiation) }
|
429
|
+
scripts_ids = scripts.select { |s| !s.ready }.map { |s| s.id }
|
430
|
+
recipes_ids = recipes.select { |r| !r.ready }.map { |r| r.id }
|
431
|
+
payload = {:agent_identity => @agent_identity,
|
432
|
+
:scripts_ids => scripts_ids,
|
433
|
+
:recipes_ids => recipes_ids}
|
434
|
+
req = RightScale::IdempotentRequest.new('/booter/get_missing_attributes', payload)
|
435
|
+
|
436
|
+
req.callback do |res|
|
437
|
+
res.each do |e|
|
438
|
+
if e.is_a?(RightScale::RightScriptInstantiation)
|
439
|
+
if script = scripts.detect { |s| s.id == e.id }
|
440
|
+
script.ready = true
|
441
|
+
script.parameters = e.parameters
|
442
|
+
end
|
443
|
+
else
|
444
|
+
if recipe = recipes.detect { |s| s.id == e.id }
|
445
|
+
recipe.ready = true
|
446
|
+
recipe.attributes = e.attributes
|
447
|
+
end
|
448
|
+
end
|
449
|
+
end
|
450
|
+
pending_executables = bundle.executables.select { |e| !e.ready }
|
451
|
+
if pending_executables.empty?
|
452
|
+
yield
|
453
|
+
else
|
454
|
+
# keep state to provide fewer but more meaningful audits.
|
455
|
+
last_missing_inputs ||= {}
|
456
|
+
last_missing_inputs[:executables] ||= {}
|
457
|
+
|
458
|
+
# don't need to audit on each attempt to resolve missing inputs, but nag
|
459
|
+
# every so often to let the user know this server is still waiting.
|
460
|
+
last_audit_time = last_missing_inputs[:last_audit_time]
|
461
|
+
audit_missing_inputs = last_audit_time.nil? || (last_audit_time + MISSING_INPUT_AUDIT_DELAY_SECS < Time.now)
|
462
|
+
sent_audit = false
|
463
|
+
|
464
|
+
# audit missing inputs, if necessary.
|
465
|
+
missing_inputs_executables = {}
|
466
|
+
pending_executables.each do |e|
|
467
|
+
# names of missing inputs are available from RightScripts.
|
468
|
+
missing_input_names = []
|
469
|
+
|
470
|
+
e.input_flags.each {|k,v| missing_input_names << k if v.member?("unready")}
|
471
|
+
@audit.append_info("Waiting for the following missing inputs which are used by '#{e.nickname}': #{missing_input_names.join(", ")}")
|
472
|
+
|
473
|
+
sent_audit = true
|
474
|
+
missing_inputs_executables[e.nickname] = missing_input_names
|
475
|
+
end
|
476
|
+
|
477
|
+
# audit any executables which now have all inputs.
|
478
|
+
last_missing_inputs[:executables].each_key do |nickname|
|
479
|
+
unless missing_inputs_executables[nickname]
|
480
|
+
title = RightScale::RightScriptsCookbook.recipe_title(nickname)
|
481
|
+
@audit.append_info("The inputs used by #{title} which had been missing have now been resolved.")
|
482
|
+
sent_audit = true
|
483
|
+
end
|
484
|
+
end
|
485
|
+
last_missing_inputs[:executables] = missing_inputs_executables
|
486
|
+
last_missing_inputs[:last_audit_time] = Time.now if sent_audit
|
487
|
+
|
488
|
+
# schedule retry to retrieve missing inputs.
|
489
|
+
EM.add_timer(MISSING_INPUT_RETRY_DELAY_SECS) { retrieve_missing_inputs(bundle, last_missing_inputs, &cb) }
|
490
|
+
end
|
491
|
+
end
|
492
|
+
|
493
|
+
req.errback do |res|
|
494
|
+
strand('Failed to retrieve missing inputs', res)
|
495
|
+
end
|
496
|
+
|
497
|
+
req.run
|
498
|
+
end
|
499
|
+
|
500
|
+
# Creates a new sequence for the given context.
|
501
|
+
#
|
502
|
+
# === Parameters
|
503
|
+
# context(RightScale::OperationContext):: context
|
504
|
+
#
|
505
|
+
# === Return
|
506
|
+
# sequence(RightScale::ExecutableSequenceProxy):: new sequence
|
507
|
+
def create_sequence(context)
|
508
|
+
return RightScale::ExecutableSequenceProxy.new(context)
|
509
|
+
end
|
510
|
+
|
511
|
+
# Retrieve and run boot scripts
|
512
|
+
#
|
513
|
+
# === Return
|
514
|
+
# true:: Always return true
|
515
|
+
def run_boot_bundle(bundle)
|
516
|
+
@got_boot_bundle = true
|
517
|
+
|
518
|
+
# Force full converge on boot so that Chef state gets persisted
|
519
|
+
context = RightScale::OperationContext.new(bundle, @audit)
|
520
|
+
sequence = create_sequence(context)
|
521
|
+
sequence.callback do
|
522
|
+
if patch = sequence.inputs_patch && !patch.empty?
|
523
|
+
payload = {:agent_identity => @agent_identity, :patch => patch}
|
524
|
+
send_persistent_push('/updater/update_inputs', payload)
|
525
|
+
end
|
526
|
+
@audit.update_status("boot completed: #{bundle}")
|
527
|
+
yield success_result
|
528
|
+
end
|
529
|
+
sequence.errback do
|
530
|
+
@audit.update_status("boot failed: #{bundle}")
|
531
|
+
yield error_result('Failed to run boot bundle')
|
532
|
+
end
|
533
|
+
|
534
|
+
begin
|
535
|
+
sequence.run
|
536
|
+
rescue Exception => e
|
537
|
+
msg = 'Execution of boot sequence failed'
|
538
|
+
RightScale::Log.error(msg, e, :trace)
|
539
|
+
strand(RightScale::Log.format(msg, e))
|
540
|
+
end
|
541
|
+
|
542
|
+
true
|
543
|
+
end
|
544
|
+
|
545
|
+
# Recovers from an aborted decommission.
|
546
|
+
#
|
547
|
+
# === Parameters
|
548
|
+
# user_id(int):: user id or zero or nil
|
549
|
+
# skip_db_update(Boolean):: true to skip db update after shutdown
|
550
|
+
# kind(String):: 'reboot', 'stop' or 'terminate'
|
551
|
+
#
|
552
|
+
# === Return
|
553
|
+
# always true
|
554
|
+
def recover_decommission(user_id, skip_db_update, kind)
|
555
|
+
# skip running decommission bundle again to avoid repeating the failure
|
556
|
+
# which caused the previous decommission to kill the agent. log this
|
557
|
+
# strange situation and go directly to instance shutdown.
|
558
|
+
RightScale::Log.warning("Instance has recovered from an aborted decommission and will perform " +
|
559
|
+
"the last requested shutdown: #{kind}")
|
560
|
+
RightScale::InstanceState.shutdown(user_id, skip_db_update, kind)
|
561
|
+
true
|
562
|
+
rescue Exception => e
|
563
|
+
RightScale::Log.error("Failed recovering from aborted decommission", e, :trace)
|
564
|
+
true
|
565
|
+
end
|
566
|
+
|
567
|
+
end
|
data/bin/cloud
ADDED
@@ -0,0 +1,25 @@
|
|
1
|
+
# Copyright (c) 2011 RightScale Inc
|
2
|
+
#
|
3
|
+
# Permission is hereby granted, free of charge, to any person obtaining
|
4
|
+
# a copy of this software and associated documentation files (the
|
5
|
+
# "Software"), to deal in the Software without restriction, including
|
6
|
+
# without limitation the rights to use, copy, modify, merge, publish,
|
7
|
+
# distribute, sublicense, and/or sell copies of the Software, and to
|
8
|
+
# permit persons to whom the Software is furnished to do so, subject to
|
9
|
+
# the following conditions:
|
10
|
+
#
|
11
|
+
# The above copyright notice and this permission notice shall be
|
12
|
+
# included in all copies or substantial portions of the Software.
|
13
|
+
#
|
14
|
+
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
15
|
+
# EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
16
|
+
# MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
17
|
+
# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
18
|
+
# LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
19
|
+
# OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
20
|
+
# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
21
|
+
|
22
|
+
require File.expand_path(File.join(File.dirname(__FILE__), '..', 'lib', 'gem_dependencies'))
|
23
|
+
require File.expand_path(File.join(File.dirname(__FILE__), '..', 'scripts', 'cloud_controller'))
|
24
|
+
|
25
|
+
RightScale::CloudController.run
|
data/bin/cook_runner
ADDED
@@ -0,0 +1,44 @@
|
|
1
|
+
# === Synopsis:
|
2
|
+
# RightScale Chef Cook (cook) - (c) 2010-11 RightScale Inc
|
3
|
+
#
|
4
|
+
# This utility is meant to be used internally by RightLink, use
|
5
|
+
# rs_run_right_script and rs_run_recipe instead.
|
6
|
+
#
|
7
|
+
|
8
|
+
require File.expand_path(File.join(File.dirname(__FILE__), '..', 'lib', 'gem_dependencies'))
|
9
|
+
|
10
|
+
require 'right_agent'
|
11
|
+
require 'eventmachine'
|
12
|
+
require 'chef'
|
13
|
+
require 'fileutils'
|
14
|
+
require 'right_scraper'
|
15
|
+
|
16
|
+
BASE_DIR = File.join(File.dirname(__FILE__), '..')
|
17
|
+
|
18
|
+
require File.normalize_path(File.join(BASE_DIR, 'lib', 'instance'))
|
19
|
+
require File.normalize_path(File.join(BASE_DIR, 'lib', 'instance', 'cook'))
|
20
|
+
|
21
|
+
# Launch it!
|
22
|
+
RightScale::Cook.new.run
|
23
|
+
|
24
|
+
#
|
25
|
+
# Copyright (c) 2009-2011 RightScale Inc
|
26
|
+
#
|
27
|
+
# Permission is hereby granted, free of charge, to any person obtaining
|
28
|
+
# a copy of this software and associated documentation files (the
|
29
|
+
# "Software"), to deal in the Software without restriction, including
|
30
|
+
# without limitation the rights to use, copy, modify, merge, publish,
|
31
|
+
# distribute, sublicense, and/or sell copies of the Software, and to
|
32
|
+
# permit persons to whom the Software is furnished to do so, subject to
|
33
|
+
# the following conditions:
|
34
|
+
#
|
35
|
+
# The above copyright notice and this permission notice shall be
|
36
|
+
# included in all copies or substantial portions of the Software.
|
37
|
+
#
|
38
|
+
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
39
|
+
# EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
40
|
+
# MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
41
|
+
# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
42
|
+
# LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
43
|
+
# OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
44
|
+
# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|