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,104 @@
|
|
1
|
+
#
|
2
|
+
# Copyright (c) 2009-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
|
+
# Tracks reenroll votes and trigger reenroll as necessary
|
26
|
+
class ReenrollManager
|
27
|
+
|
28
|
+
# Number of votes required to trigger re-enroll
|
29
|
+
REENROLL_THRESHOLD = 3
|
30
|
+
|
31
|
+
# Additional number of seconds that should be spent in offline mode before
|
32
|
+
# triggering a re-enroll after vote threshold has been reached
|
33
|
+
MAXIMUM_REENROLL_DELAY = 900 # 15 minutes
|
34
|
+
|
35
|
+
# Delay in seconds until votes count is reset if no more votes occur
|
36
|
+
# This value should be more than two hours as this is the period at which
|
37
|
+
# votes will get generated in offline mode
|
38
|
+
RESET_DELAY = 7200 # 2 hours
|
39
|
+
|
40
|
+
# Are we currently reenrolling?
|
41
|
+
#
|
42
|
+
# === Return
|
43
|
+
# true:: If we are currently re-enrolling (note: process is going away)
|
44
|
+
# false:: Otherwise
|
45
|
+
def self.reenrolling?
|
46
|
+
@reenrolling
|
47
|
+
end
|
48
|
+
|
49
|
+
# Set reenrolling flag
|
50
|
+
#
|
51
|
+
# === Return
|
52
|
+
# true:: Always return true
|
53
|
+
def self.set_reenrolling
|
54
|
+
@reenrolling = true
|
55
|
+
end
|
56
|
+
|
57
|
+
# Vote for re-enrolling, if threshold is reached re-enroll
|
58
|
+
# If no vote occurs in the next two hours, then reset counter
|
59
|
+
#
|
60
|
+
# === Return
|
61
|
+
# true:: Always return true
|
62
|
+
def self.vote
|
63
|
+
@total_votes ||= 0
|
64
|
+
@reenrolling ||= false
|
65
|
+
@total_votes += 1
|
66
|
+
@reset_timer.cancel if @reset_timer
|
67
|
+
@reset_timer = EM::Timer.new(RESET_DELAY) { reset_votes }
|
68
|
+
if @total_votes >= REENROLL_THRESHOLD && !@reenrolling
|
69
|
+
delay = rand(MAXIMUM_REENROLL_DELAY)
|
70
|
+
Log.info("[re-enroll] Re-enroll threshold reached, shutting down and re-enrolling in #{delay} seconds")
|
71
|
+
set_reenrolling
|
72
|
+
EM::Timer.new(delay) { reenroll! }
|
73
|
+
end
|
74
|
+
true
|
75
|
+
end
|
76
|
+
|
77
|
+
# Reset votes count
|
78
|
+
#
|
79
|
+
# === Return
|
80
|
+
# true:: Always return true
|
81
|
+
def self.reset_votes
|
82
|
+
@total_votes = 0
|
83
|
+
@reset_timer = nil
|
84
|
+
true
|
85
|
+
end
|
86
|
+
|
87
|
+
# Trigger reenroll
|
88
|
+
#
|
89
|
+
# === Parameters
|
90
|
+
# args(String):: Arguments to be passed through to rs_reenroll
|
91
|
+
#
|
92
|
+
# === Return
|
93
|
+
# true:: If command ran successfully
|
94
|
+
# false:: Otherwise
|
95
|
+
def self.reenroll!(args=nil)
|
96
|
+
cmd = "rs_reenroll #{args}"
|
97
|
+
cmd += '&' unless RightScale::Platform.windows?
|
98
|
+
AMQP.stop { EM.stop } rescue nil
|
99
|
+
system(cmd)
|
100
|
+
end
|
101
|
+
|
102
|
+
end
|
103
|
+
|
104
|
+
end
|
@@ -0,0 +1,181 @@
|
|
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 'fileutils'
|
24
|
+
|
25
|
+
module RightScale
|
26
|
+
|
27
|
+
# Generate recipes dynamically for RightScripts
|
28
|
+
# Usage is:
|
29
|
+
# 1. Call 'recipe_from_right_script' for each RightScript that should be converted to a recipe
|
30
|
+
# 2. Call 'save' before running Chef, 'recipe_from_right_script' cannot be called after 'save'
|
31
|
+
# 3. Use 'repo_dir' to retrieve the Chef cookbook repo path (can be called at any time)
|
32
|
+
class RightScriptsCookbook
|
33
|
+
|
34
|
+
# Name of cookbook containing RightScript recipes
|
35
|
+
COOKBOOK_NAME = 'right_scripts_cookbook'
|
36
|
+
|
37
|
+
# Path to generated cookbook repo
|
38
|
+
attr_reader :repo_dir
|
39
|
+
|
40
|
+
# Wheter 'save' has been called
|
41
|
+
attr_reader :saved
|
42
|
+
|
43
|
+
# Setup temporary directory for cookbook repo containing
|
44
|
+
# recipes generated from RightScripts
|
45
|
+
def initialize(thread_name)
|
46
|
+
@saved = false
|
47
|
+
@recipes = {}
|
48
|
+
@repo_dir = File.join(AgentConfig.right_scripts_repo_dir, thread_name)
|
49
|
+
@cookbook_dir = File.join(@repo_dir, COOKBOOK_NAME)
|
50
|
+
@recipes_dir = File.join(@cookbook_dir, 'recipes')
|
51
|
+
FileUtils.rm_rf(@cookbook_dir)
|
52
|
+
FileUtils.mkdir_p(@recipes_dir)
|
53
|
+
end
|
54
|
+
|
55
|
+
# Add RightScript instantiation to cookbook
|
56
|
+
#
|
57
|
+
# === Parameters
|
58
|
+
# script(RightScale::RightScriptInstantiation):: RightScript to be added
|
59
|
+
#
|
60
|
+
# === Return
|
61
|
+
# recipe(RightScale::RecipeInstantiation):: Recipe that wraps RightScript
|
62
|
+
#
|
63
|
+
# === Raise
|
64
|
+
# (RightScale::Exceptions::Application):: If 'save' has been called
|
65
|
+
def recipe_from_right_script(script)
|
66
|
+
raise RightScale::Exceptions::Application, 'cannot create recipe after cookbook repo has been saved' if @saved
|
67
|
+
path = script_path(script.nickname)
|
68
|
+
recipe_name = File.basename(path)
|
69
|
+
@recipes[recipe_name] = script.nickname
|
70
|
+
recipe_content = <<-EOS
|
71
|
+
right_script '#{script.nickname.gsub("'", "\\\\'")}' do
|
72
|
+
parameters(node["#{script.nickname}"]["parameters"])
|
73
|
+
cache_dir '#{cache_dir(script)}'
|
74
|
+
source_file '#{path}'
|
75
|
+
display_version '#{script.display_version.to_s.strip.gsub("'", "\\\\'")}'
|
76
|
+
end
|
77
|
+
EOS
|
78
|
+
File.open(path, 'w') { |f| f.puts script.source }
|
79
|
+
File.chmod(0744, path)
|
80
|
+
recipe_path = "#{path}.rb"
|
81
|
+
File.open(recipe_path, 'w') { |f| f.puts recipe_content }
|
82
|
+
|
83
|
+
recipe = RecipeInstantiation.new("#{COOKBOOK_NAME}::#{recipe_name}",
|
84
|
+
{ script.nickname => { "parameters" => script.parameters } },
|
85
|
+
script.id, script.ready)
|
86
|
+
|
87
|
+
end
|
88
|
+
|
89
|
+
# Produce file name for given script nickname
|
90
|
+
#
|
91
|
+
# === Parameters
|
92
|
+
# nickname(String):: Script nick name
|
93
|
+
#
|
94
|
+
# === Return
|
95
|
+
# path(String):: Path to corresponding recipe
|
96
|
+
def script_path(nickname)
|
97
|
+
base_path = nickname.gsub(/[^0-9a-zA-Z_]/,'_')
|
98
|
+
base_path = File.join(@recipes_dir, base_path)
|
99
|
+
candidate_path = RightScale::Platform.shell.format_script_file_name(base_path)
|
100
|
+
i = 1
|
101
|
+
path = candidate_path
|
102
|
+
path = candidate_path + (i += 1).to_s while File.exists?(path)
|
103
|
+
path
|
104
|
+
end
|
105
|
+
|
106
|
+
# Save cookbook repo
|
107
|
+
#
|
108
|
+
# === Return
|
109
|
+
# true:: Always return true
|
110
|
+
def save
|
111
|
+
unless empty?
|
112
|
+
recipes = @recipes.keys.sort
|
113
|
+
metadata_content = <<-EOS
|
114
|
+
description "Automatically generated repo, do not modify"
|
115
|
+
#{recipes.map { |r| "recipe \"#{COOKBOOK_NAME}::#{r}\", \"RightScript < #{@recipes[r]} >\"" }.join("\n")}
|
116
|
+
EOS
|
117
|
+
metadata_path = File.join(@cookbook_dir, 'metadata.rb')
|
118
|
+
File.open(metadata_path, 'w') { |f| f.puts metadata_content }
|
119
|
+
end
|
120
|
+
@saved = true
|
121
|
+
end
|
122
|
+
|
123
|
+
# Whether given recipe name corresponds to a converted RightScript
|
124
|
+
#
|
125
|
+
# === Parameters
|
126
|
+
# nickname(String):: Recipe nickname
|
127
|
+
#
|
128
|
+
# === Return
|
129
|
+
# true:: If recipe was created from converting a RightScript
|
130
|
+
# false:: Otherwise
|
131
|
+
def self.right_script?(nickname)
|
132
|
+
# the RightScripts deserialized from core appear to not have the
|
133
|
+
# RightScript cookbook in their nickname. actual Chef recipes will have
|
134
|
+
# their cookbook name, so assume missing cookbook name means RightScript.
|
135
|
+
regex = /^(.*)::/
|
136
|
+
match = regex.match(nickname)
|
137
|
+
return match.nil? || COOKBOOK_NAME == match[1]
|
138
|
+
end
|
139
|
+
|
140
|
+
# Human friendly title for given recipe instantiation
|
141
|
+
#
|
142
|
+
# === Parameters
|
143
|
+
# recipe(String):: Recipe nickname
|
144
|
+
#
|
145
|
+
# === Return
|
146
|
+
# title(String):: Recipe title to be used in audits
|
147
|
+
def self.recipe_title(recipe)
|
148
|
+
title = right_script?(recipe) ? 'RightScript' : 'recipe'
|
149
|
+
title = "#{title} < #{recipe} >"
|
150
|
+
end
|
151
|
+
|
152
|
+
# Path to cache directory for given script
|
153
|
+
#
|
154
|
+
# === Parameters
|
155
|
+
# script(Object):: script object of some kind (e.g. RightScale::RightScriptInstantiation)
|
156
|
+
#
|
157
|
+
# === Return
|
158
|
+
# path(String):: Path to directory used for attachments and source
|
159
|
+
def cache_dir(script)
|
160
|
+
# prefix object ID with a text constant to make a legal directory name
|
161
|
+
# in case object id is negative (Ubuntu, etc.). this method will be called
|
162
|
+
# more than once and must return the same directory each time for a given
|
163
|
+
# script instantiation.
|
164
|
+
path = File.normalize_path(File.join(AgentConfig.cache_dir, 'right_scripts_content', "rs_attach" + script.object_id.to_s))
|
165
|
+
|
166
|
+
# convert to native format for ease of scripting in Windows, etc. the
|
167
|
+
# normalized path is normal for Ruby but not necessarily for native FS.
|
168
|
+
return RightScale::Platform.filesystem.pretty_path(path, true)
|
169
|
+
end
|
170
|
+
|
171
|
+
# Is there no RightScript recipe in repo?
|
172
|
+
#
|
173
|
+
# === Return
|
174
|
+
# true:: If +recipe_from_right_script+ was never called
|
175
|
+
# false:: Otherwise
|
176
|
+
def empty?
|
177
|
+
@recipes.empty?
|
178
|
+
end
|
179
|
+
|
180
|
+
end
|
181
|
+
end
|
@@ -0,0 +1,221 @@
|
|
1
|
+
#
|
2
|
+
# Copyright (c) 2011 RightScale Inc
|
3
|
+
#
|
4
|
+
# Permission is hereby granted, free of charge, to any person obtaining
|
5
|
+
# a copy of this software and associated documentation files (the
|
6
|
+
# "Software"), to deal in the Software without restriction, including
|
7
|
+
# without limitation the rights to use, copy, modify, merge, publish,
|
8
|
+
# distribute, sublicense, and/or sell copies of the Software, and to
|
9
|
+
# permit persons to whom the Software is furnished to do so, subject to
|
10
|
+
# the following conditions:
|
11
|
+
#
|
12
|
+
# The above copyright notice and this permission notice shall be
|
13
|
+
# included in all copies or substantial portions of the Software.
|
14
|
+
#
|
15
|
+
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
16
|
+
# EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
17
|
+
# MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
18
|
+
# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
19
|
+
# LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
20
|
+
# OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
21
|
+
# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
22
|
+
|
23
|
+
module RightScale
|
24
|
+
|
25
|
+
# Mixin for defining a common interface for a shutdown request class and its
|
26
|
+
# proxy class.
|
27
|
+
module ShutdownRequestMixin
|
28
|
+
|
29
|
+
# initial kind; no shutdown requested.
|
30
|
+
CONTINUE = 'continue'
|
31
|
+
|
32
|
+
# requested a reboot.
|
33
|
+
REBOOT = 'reboot'
|
34
|
+
|
35
|
+
# requested a stop (keep boot volume after shutdown).
|
36
|
+
STOP = 'stop'
|
37
|
+
|
38
|
+
# requested a terminate (discard boot volume after shutdown).
|
39
|
+
TERMINATE = 'terminate'
|
40
|
+
|
41
|
+
# levels.
|
42
|
+
LEVELS = [CONTINUE, REBOOT, STOP, TERMINATE]
|
43
|
+
|
44
|
+
# exceptions.
|
45
|
+
class NotInitialized < Exception; end
|
46
|
+
class InvalidLevel < Exception; end
|
47
|
+
|
48
|
+
|
49
|
+
# true if no shutdown was requested, false if shutdown was requested.
|
50
|
+
def continue?; CONTINUE == @level; end
|
51
|
+
|
52
|
+
# true if any requested shutdown will interrupt sequence of running scripts
|
53
|
+
# immediately (current script is allowed to complete).
|
54
|
+
# false to defer shutdown until all outstanding scripts have run.
|
55
|
+
def immediately?; @immediately; end
|
56
|
+
def immediately!
|
57
|
+
raise InvalidLevel.new("Immediately is unexpected for current shutdown state") if continue?
|
58
|
+
@immediately = true
|
59
|
+
end
|
60
|
+
|
61
|
+
# Shutdown request level.
|
62
|
+
def level; @level; end
|
63
|
+
def level=(value)
|
64
|
+
value = value.to_s
|
65
|
+
raise InvalidLevel.new("Invalid shutdown level: #{value.inspect}") unless LEVELS.include?(value)
|
66
|
+
|
67
|
+
# strictly escalate to higher level and ignore lower level requests.
|
68
|
+
@level = value if LEVELS.index(value) > LEVELS.index(@level)
|
69
|
+
end
|
70
|
+
|
71
|
+
# Stringizer.
|
72
|
+
def to_s
|
73
|
+
# note that printing 'deferred' would seem strange at the time when the
|
74
|
+
# deferred shutdown is actually being processed, so only say immediately.
|
75
|
+
immediacy = if @immediately; ' immediately'; else; ''; end
|
76
|
+
return "#{@level}#{immediacy}"
|
77
|
+
end
|
78
|
+
|
79
|
+
protected
|
80
|
+
|
81
|
+
def initialize
|
82
|
+
@level = CONTINUE
|
83
|
+
@immediately = false
|
84
|
+
end
|
85
|
+
|
86
|
+
end
|
87
|
+
|
88
|
+
# Represents outstanding request(s) for reboot, stop or terminate instance.
|
89
|
+
# Requests are cumulative and implicitly non-decreasing in level (e.g. reboot
|
90
|
+
# never superceeds terminate).
|
91
|
+
class ShutdownRequest
|
92
|
+
|
93
|
+
include ShutdownRequestMixin
|
94
|
+
|
95
|
+
# Class initializer.
|
96
|
+
#
|
97
|
+
# === Parameters
|
98
|
+
# scheduler(InstanceScheduler):: scheduler for shutdown requests
|
99
|
+
#
|
100
|
+
# === Return
|
101
|
+
# always true
|
102
|
+
def self.init(scheduler)
|
103
|
+
@@instance = ShutdownRequest.new
|
104
|
+
@@scheduler = scheduler
|
105
|
+
true
|
106
|
+
end
|
107
|
+
|
108
|
+
# Factory method
|
109
|
+
#
|
110
|
+
# === Return
|
111
|
+
# (ShutdownRequest):: the singleton for this class
|
112
|
+
def self.instance
|
113
|
+
raise NotInitialized.new("ShutdownRequest.init has not been called") unless defined?(@@instance)
|
114
|
+
return @@instance
|
115
|
+
end
|
116
|
+
|
117
|
+
# Submits a new shutdown request state which may be superceded by a
|
118
|
+
# previous, higher-priority shutdown level.
|
119
|
+
#
|
120
|
+
# === Parameters
|
121
|
+
# request[:level](String):: shutdown level
|
122
|
+
# request[:immediately](Boolean):: shutdown immediacy or nil
|
123
|
+
#
|
124
|
+
# === Returns
|
125
|
+
# result(ShutdownRequest):: the updated instance
|
126
|
+
def self.submit(request)
|
127
|
+
# RightNet protocols use kind instead of level, so be a little flexible.
|
128
|
+
result = instance
|
129
|
+
result.level = request[:kind] || request[:level]
|
130
|
+
result.immediately! if request[:immediately]
|
131
|
+
@@scheduler.schedule_shutdown unless result.continue?
|
132
|
+
return result
|
133
|
+
end
|
134
|
+
|
135
|
+
# Processes shutdown requests by communicating the need to shutdown an
|
136
|
+
# instance with the core agent, if necessary.
|
137
|
+
#
|
138
|
+
# === Parameters
|
139
|
+
# errback(Proc):: error handler or nil
|
140
|
+
# audit(Audit):: audit for shutdown action, if needed, or nil.
|
141
|
+
#
|
142
|
+
# === Block
|
143
|
+
# block(Proc):: continuation block for successful handling of shutdown or nil
|
144
|
+
#
|
145
|
+
# === Return
|
146
|
+
# always true
|
147
|
+
def process(errback = nil, audit = nil, &block)
|
148
|
+
# yield if not shutting down (continuing) or if already requested shutdown.
|
149
|
+
if continue? || @shutdown_scheduled
|
150
|
+
block.call if block
|
151
|
+
return true
|
152
|
+
end
|
153
|
+
|
154
|
+
# ensure we have an audit, creating a temporary audit if necessary.
|
155
|
+
sender = Sender.instance
|
156
|
+
agent_identity = sender.identity
|
157
|
+
if audit
|
158
|
+
case @level
|
159
|
+
when REBOOT, STOP, TERMINATE
|
160
|
+
operation = "/forwarder/shutdown"
|
161
|
+
payload = {:agent_identity => agent_identity, :kind => @level}
|
162
|
+
else
|
163
|
+
raise InvalidLevel.new("Unexpected shutdown level: #{@level.inspect}")
|
164
|
+
end
|
165
|
+
|
166
|
+
# request shutdown (kind indicated by operation and/or payload).
|
167
|
+
audit.append_info("Shutdown requested: #{self}")
|
168
|
+
sender.send_persistent_request(operation, payload) do |r|
|
169
|
+
res = OperationResult.from_results(r)
|
170
|
+
if res.success?
|
171
|
+
@shutdown_scheduled = true
|
172
|
+
block.call if block
|
173
|
+
else
|
174
|
+
fail(errback, audit, "Failed to shutdown instance", res)
|
175
|
+
end
|
176
|
+
end
|
177
|
+
else
|
178
|
+
AuditProxy.create(agent_identity, "Shutdown requested: #{self}") do |new_audit|
|
179
|
+
process(errback, new_audit, &block)
|
180
|
+
end
|
181
|
+
end
|
182
|
+
true
|
183
|
+
rescue Exception => e
|
184
|
+
fail(errback, audit, e)
|
185
|
+
end
|
186
|
+
|
187
|
+
protected
|
188
|
+
|
189
|
+
def initialize
|
190
|
+
super
|
191
|
+
@shutdown_scheduled = false
|
192
|
+
end
|
193
|
+
|
194
|
+
# Handles any shutdown failure.
|
195
|
+
#
|
196
|
+
# === Parameters
|
197
|
+
# audit(Audit):: Audit or nil
|
198
|
+
# errback(Proc):: error handler or nil
|
199
|
+
# msg(String):: Error message that will be audited and logged
|
200
|
+
# res(RightScale::OperationResult):: Operation result with additional information
|
201
|
+
#
|
202
|
+
# === Return
|
203
|
+
# always true
|
204
|
+
def fail(errback, audit, msg, res = nil)
|
205
|
+
if msg.kind_of?(Exception)
|
206
|
+
e = msg
|
207
|
+
detailed = Log.format("Could not process shutdown state #{self}", e, :trace)
|
208
|
+
msg = e.message
|
209
|
+
else
|
210
|
+
detailed = nil
|
211
|
+
end
|
212
|
+
msg += ": #{res.content}" if res && res.content
|
213
|
+
audit.append_error(msg, :category => RightScale::EventCategories::CATEGORY_ERROR) if audit
|
214
|
+
Log.error(detailed) if detailed
|
215
|
+
errback.call if errback
|
216
|
+
true
|
217
|
+
end
|
218
|
+
|
219
|
+
end # ShutdownRequest
|
220
|
+
|
221
|
+
end # RightScale
|
@@ -0,0 +1,189 @@
|
|
1
|
+
#
|
2
|
+
# Copyright (c) 2011 RightScale Inc
|
3
|
+
#
|
4
|
+
# Permission is hereby granted, free of charge, to any person obtaining
|
5
|
+
# a copy of this software and associated documentation files (the
|
6
|
+
# "Software"), to deal in the Software without restriction, including
|
7
|
+
# without limitation the rights to use, copy, modify, merge, publish,
|
8
|
+
# distribute, sublicense, and/or sell copies of the Software, and to
|
9
|
+
# permit persons to whom the Software is furnished to do so, subject to
|
10
|
+
# the following conditions:
|
11
|
+
#
|
12
|
+
# The above copyright notice and this permission notice shall be
|
13
|
+
# included in all copies or substantial portions of the Software.
|
14
|
+
#
|
15
|
+
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
16
|
+
# EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
17
|
+
# MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
18
|
+
# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
19
|
+
# LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
20
|
+
# OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
21
|
+
# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
22
|
+
|
23
|
+
module RightScale
|
24
|
+
|
25
|
+
class SingleThreadBundleQueue < BundleQueue
|
26
|
+
|
27
|
+
attr_reader :thread_name
|
28
|
+
|
29
|
+
# Set continuation block to be called after 'close' is called
|
30
|
+
#
|
31
|
+
# === Block
|
32
|
+
# continuation block
|
33
|
+
def initialize(thread_name = ::RightScale::AgentConfig.default_thread_name, &continuation)
|
34
|
+
super(&continuation)
|
35
|
+
@active = false
|
36
|
+
@thread = nil
|
37
|
+
@thread_name = thread_name
|
38
|
+
@pid = nil
|
39
|
+
@mutex = Mutex.new
|
40
|
+
@queue = Queue.new
|
41
|
+
@sequence_finished = ConditionVariable.new
|
42
|
+
end
|
43
|
+
|
44
|
+
# Determines if queue is active
|
45
|
+
#
|
46
|
+
# === Return
|
47
|
+
# active(Boolean):: true if queue is active
|
48
|
+
def active?
|
49
|
+
active = false
|
50
|
+
@mutex.synchronize { active = @active }
|
51
|
+
return active
|
52
|
+
end
|
53
|
+
|
54
|
+
# Activate queue for execution, idempotent
|
55
|
+
# Any pending bundle will be run sequentially in order
|
56
|
+
#
|
57
|
+
# === Return
|
58
|
+
# true:: Always return true
|
59
|
+
def activate
|
60
|
+
@mutex.synchronize do
|
61
|
+
unless @active
|
62
|
+
@thread = Thread.new { run }
|
63
|
+
@active = true
|
64
|
+
end
|
65
|
+
end
|
66
|
+
true
|
67
|
+
end
|
68
|
+
|
69
|
+
# Determines if queue is busy
|
70
|
+
#
|
71
|
+
# === Return
|
72
|
+
# active(Boolean):: true if queue is busy
|
73
|
+
def busy?
|
74
|
+
busy = false
|
75
|
+
@mutex.synchronize { busy = !!@busy }
|
76
|
+
busy
|
77
|
+
end
|
78
|
+
|
79
|
+
# Push new context to bundle queue and run next bundle
|
80
|
+
#
|
81
|
+
# === Return
|
82
|
+
# true:: Always return true
|
83
|
+
def push(context)
|
84
|
+
@queue << context
|
85
|
+
true
|
86
|
+
end
|
87
|
+
|
88
|
+
# Clear queue content
|
89
|
+
#
|
90
|
+
# === Return
|
91
|
+
# true:: Always return true
|
92
|
+
def clear
|
93
|
+
@queue.clear
|
94
|
+
true
|
95
|
+
end
|
96
|
+
|
97
|
+
# Close queue so that further call to 'push' will be ignored
|
98
|
+
#
|
99
|
+
# === Return
|
100
|
+
# true:: Always return true
|
101
|
+
def close
|
102
|
+
push(FINAL_BUNDLE)
|
103
|
+
end
|
104
|
+
|
105
|
+
protected
|
106
|
+
|
107
|
+
# Run next bundle in the queue if active
|
108
|
+
# If bundle is FINAL_BUNDLE then call continuation block and deactivate
|
109
|
+
#
|
110
|
+
# === Return
|
111
|
+
# true:: Always return true
|
112
|
+
def run
|
113
|
+
loop do
|
114
|
+
context = @queue.shift
|
115
|
+
if context == FINAL_BUNDLE
|
116
|
+
break
|
117
|
+
elsif context == SHUTDOWN_BUNDLE
|
118
|
+
# process shutdown request.
|
119
|
+
ShutdownRequest.instance.process
|
120
|
+
# continue in queue in the expectation that the decommission bundle will
|
121
|
+
# shutdown the instance and its agent normally.
|
122
|
+
elsif !context.decommission? && ShutdownRequest.instance.immediately?
|
123
|
+
# immediate shutdown pre-empts any futher attempts to run operational
|
124
|
+
# scripts but still allows the decommission bundle to run.
|
125
|
+
context.audit.update_status("Skipped bundle due to immediate shutdown of #{@thread_name} thread: #{context.payload}")
|
126
|
+
# proceed ignoring bundles until final or shutdown are encountered.
|
127
|
+
else
|
128
|
+
sequence = create_sequence(context)
|
129
|
+
sequence.callback { audit_status(sequence) }
|
130
|
+
sequence.errback { audit_status(sequence) }
|
131
|
+
|
132
|
+
# wait until sequence is finished using a ruby mutex conditional.
|
133
|
+
# need to synchronize before run to ensure we are waiting before any
|
134
|
+
# immediate signalling occurs (under test conditions, etc.).
|
135
|
+
@mutex.synchronize do
|
136
|
+
@busy = true
|
137
|
+
sequence.run
|
138
|
+
@sequence_finished.wait(@mutex)
|
139
|
+
@pid = nil
|
140
|
+
@busy = false
|
141
|
+
end
|
142
|
+
end
|
143
|
+
end
|
144
|
+
true
|
145
|
+
rescue Exception => e
|
146
|
+
Log.error(Log.format("SingleThreadBundleQueue.run failed for #{@thread_name} thread", e, :trace))
|
147
|
+
ensure
|
148
|
+
# invoke continuation (off of this thread which is going away).
|
149
|
+
@mutex.synchronize { @active = false }
|
150
|
+
run_continuation
|
151
|
+
@thread = nil
|
152
|
+
end
|
153
|
+
|
154
|
+
# Factory method for a new sequence.
|
155
|
+
#
|
156
|
+
# context(RightScale::OperationContext)
|
157
|
+
def create_sequence(context)
|
158
|
+
pid_callback = lambda do |sequence|
|
159
|
+
# TODO preserve cook PIDs per thread in InstanceState and recover
|
160
|
+
# orphaned cook in case of agent crash.
|
161
|
+
@mutex.synchronize { @pid = sequence.pid }
|
162
|
+
end
|
163
|
+
return RightScale::ExecutableSequenceProxy.new(context, :pid_callback => pid_callback )
|
164
|
+
end
|
165
|
+
|
166
|
+
# Audit executable sequence status after it ran
|
167
|
+
#
|
168
|
+
# === Parameters
|
169
|
+
# sequence(RightScale::ExecutableSequence):: finished sequence being audited
|
170
|
+
#
|
171
|
+
# === Return
|
172
|
+
# true:: Always return true
|
173
|
+
def audit_status(sequence)
|
174
|
+
context = sequence.context
|
175
|
+
title = context.decommission? ? 'decommission ' : ''
|
176
|
+
title += context.succeeded ? 'completed' : 'failed'
|
177
|
+
context.audit.update_status("#{title}: #{context.payload}")
|
178
|
+
true
|
179
|
+
rescue Exception => e
|
180
|
+
Log.error(Log.format("SingleThreadBundleQueue.audit_status failed for #{@thread_name} thread", e, :trace))
|
181
|
+
ensure
|
182
|
+
# release queue thread to wait on next bundle in queue. we must ensure
|
183
|
+
# that we are not currently on the queue thread so next-tick the signal.
|
184
|
+
EM.next_tick { @mutex.synchronize { @sequence_finished.signal } }
|
185
|
+
end
|
186
|
+
|
187
|
+
end
|
188
|
+
|
189
|
+
end
|