forked from ManageIQ/manageiq
-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Avoid calling $evm.backtrace over DRb to prevent DRb-level mutex locks
Moves dynamic method building into DrbRemoteInvoker since it isn't needed at all in MiqAeService. Once in DrbRemoteInvoker, we can use the dynamic preamble to inform the generated ruby method body of things like the number of preamble lines and the name of the method for backtrace filtering purposes.
- Loading branch information
Showing
5 changed files
with
136 additions
and
128 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
150 changes: 96 additions & 54 deletions
150
spec/lib/miq_automation_engine/engine/miq_ae_method_spec.rb
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,69 +1,111 @@ | ||
describe MiqAeEngine::MiqAeMethod do | ||
context ".invoke_inline_ruby(private)" do | ||
it "writes to the logger immediately" do | ||
script = <<-EOS | ||
puts 'Hi from puts' | ||
STDOUT.puts 'Hi from STDOUT.puts' | ||
$stdout.puts 'Hi from $stdout.puts' | ||
STDERR.puts 'Hi from STDERR.puts' | ||
$stderr.puts 'Hi from $stderr.puts' | ||
$evm.logger.sleep | ||
EOS | ||
|
||
workspace = Class.new do | ||
describe ".invoke_inline_ruby (private)" do | ||
let(:workspace) do | ||
Class.new do | ||
attr_accessor :invoker | ||
# rubocop:disable Style/SingleLineMethods, Style/EmptyLineBetweenDefs | ||
def persist_state_hash; end | ||
def disable_rbac; end | ||
def current_method; "/my/automate/method"; end | ||
# rubocop:enable Style/SingleLineMethods, Style/EmptyLineBetweenDefs | ||
end.new | ||
end | ||
|
||
def persist_state_hash | ||
end | ||
let(:aem) { double("AEM", :data => script, :fqname => "/my/automate/method") } | ||
let(:obj) { double("OBJ", :workspace => workspace) } | ||
let(:inputs) { [] } | ||
|
||
def disable_rbac | ||
end | ||
end.new | ||
subject { described_class.send(:invoke_inline_ruby, aem, obj, inputs) } | ||
|
||
logger_klass = Class.new do | ||
attr_reader :expected_messages | ||
|
||
def initialize | ||
@expected_messages = [ | ||
"Method STDOUT: Hi from puts", | ||
"Method STDOUT: Hi from STDOUT.puts", | ||
"Method STDOUT: Hi from $stdout.puts", | ||
"Method STDERR: Hi from STDERR.puts", | ||
"Method STDERR: Hi from $stderr.puts", | ||
] | ||
end | ||
|
||
def sleep | ||
# Raise if all messages have not already been written before a method like sleep runs. | ||
raise unless expected_messages == [] | ||
end | ||
|
||
def verify_next_message(message) | ||
expected = @expected_messages.shift | ||
return if message == expected | ||
puts "Expected: #{expected.inspect}, Got: #{message.inspect}" | ||
context "with a script that raises" do | ||
let(:script) do | ||
<<-RUBY | ||
puts 'Hi from puts' | ||
raise | ||
end | ||
alias_method :error, :verify_next_message | ||
alias_method :info, :verify_next_message | ||
RUBY | ||
end | ||
|
||
it "logs the error with file and line numbers changed in the stacktrace, and raises an exception" do | ||
allow($miq_ae_logger).to receive(:error).and_call_original | ||
expect($miq_ae_logger).to receive(:error).with("Method STDERR: /my/automate/method:2:in `<main>': unhandled exception").at_least(:once) | ||
|
||
expect { subject }.to raise_error(MiqAeException::UnknownMethodRc) | ||
end | ||
end | ||
|
||
aem = double("AEM", :data => script, :fqname => "fqname") | ||
inputs = [] | ||
logger_stub = logger_klass.new | ||
obj = double("OBJ", :workspace => workspace) | ||
svc = MiqAeMethodService::MiqAeService.new(workspace, inputs, aem.data, logger_stub) | ||
context "with a script that raises in a nested method" do | ||
let(:script) do | ||
<<-RUBY | ||
def my_method | ||
raise | ||
end | ||
expect(MiqAeMethodService::MiqAeService).to receive(:new).with(workspace, inputs, aem.data).and_return(svc) | ||
puts 'Hi from puts' | ||
my_method | ||
RUBY | ||
end | ||
|
||
expect($miq_ae_logger).to receive(:info).with("<AEMethod [fqname]> Starting ").ordered | ||
expect(logger_stub).to receive(:sleep).and_call_original.ordered | ||
expect($miq_ae_logger).to receive(:info).with("<AEMethod [fqname]> Ending").ordered | ||
expect($miq_ae_logger).to receive(:info).with("Method exited with rc=MIQ_OK").ordered | ||
it "logs the error with file and line numbers changed in the stacktrace, and raises an exception" do | ||
allow($miq_ae_logger).to receive(:error).and_call_original | ||
expect($miq_ae_logger).to receive(:error).with("Method STDERR: /my/automate/method:2:in `my_method': unhandled exception").at_least(:once) | ||
expect($miq_ae_logger).to receive(:error).with("Method STDERR: \tfrom /my/automate/method:6:in `<main>'").at_least(:once) | ||
|
||
expect(described_class.send(:invoke_inline_ruby, aem, obj, inputs)).to eq(0) | ||
expect { subject }.to raise_error(MiqAeException::UnknownMethodRc) | ||
end | ||
end | ||
|
||
expect(logger_stub.expected_messages).to eq([]) | ||
context "with a script that does I/O" do | ||
let(:script) do | ||
<<-RUBY | ||
puts 'Hi from puts' | ||
STDOUT.puts 'Hi from STDOUT.puts' | ||
$stdout.puts 'Hi from $stdout.puts' | ||
STDERR.puts 'Hi from STDERR.puts' | ||
$stderr.puts 'Hi from $stderr.puts' | ||
$evm.logger.sleep | ||
RUBY | ||
end | ||
|
||
it "writes to the logger synchronously" do | ||
logger_stub = Class.new do | ||
attr_reader :expected_messages | ||
|
||
def initialize | ||
@expected_messages = [ | ||
"Method STDOUT: Hi from puts", | ||
"Method STDOUT: Hi from STDOUT.puts", | ||
"Method STDOUT: Hi from $stdout.puts", | ||
"Method STDERR: Hi from STDERR.puts", | ||
"Method STDERR: Hi from $stderr.puts", | ||
] | ||
end | ||
|
||
def sleep | ||
# Raise if all messages have not already been written before a method like sleep runs. | ||
raise unless expected_messages == [] | ||
end | ||
|
||
def verify_next_message(message) | ||
expected = expected_messages.shift | ||
return if message == expected | ||
puts "Expected: #{expected.inspect}, Got: #{message.inspect}" | ||
raise | ||
end | ||
alias_method :error, :verify_next_message | ||
alias_method :info, :verify_next_message | ||
end.new | ||
|
||
svc = MiqAeMethodService::MiqAeService.new(workspace, [], nil, logger_stub) | ||
expect(MiqAeMethodService::MiqAeService).to receive(:new).with(workspace, []).and_return(svc) | ||
|
||
expect($miq_ae_logger).to receive(:info).with("<AEMethod [/my/automate/method]> Starting ").ordered | ||
expect(logger_stub).to receive(:sleep).and_call_original.ordered | ||
expect($miq_ae_logger).to receive(:info).with("<AEMethod [/my/automate/method]> Ending").ordered | ||
expect($miq_ae_logger).to receive(:info).with("Method exited with rc=MIQ_OK").ordered | ||
|
||
expect(subject).to eq(0) | ||
expect(logger_stub.expected_messages).to eq([]) | ||
end | ||
end | ||
end | ||
end |