Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
14 changes: 14 additions & 0 deletions files/mcollective/agent/shell.ddl
Original file line number Diff line number Diff line change
Expand Up @@ -127,6 +127,20 @@ action "list", :description => "Get a list of all running commands" do

end

action "statuses", :description => "Get status and output of multiple managed commands" do
display :always

input :handles,
:prompt => "Handles",
:description => "Array of command handles to query",
:type => :array,
:optional => false

output :statuses,
:description => "status and output keyed by handle",
:display_as => "statuses"
end

action "kill", :description => "Kill a command by handle" do
display :always

Expand Down
21 changes: 21 additions & 0 deletions files/mcollective/agent/shell.json
Original file line number Diff line number Diff line change
Expand Up @@ -96,6 +96,27 @@
"display": "always",
"description": "Run a command"
},
{
"action": "statuses",
"input": {
"handles": {
"prompt": "Handles",
"description": "Array of command handles to query",
"type": "array",
"default": null,
"optional": false
}
},
"output": {
"statuses": {
"description": "status and output keyed by handle",
"display_as": "statuses",
"default": null
}
},
"display": "always",
"description": "Get status and output of multiple managed commands"
},
{
"action": "start",
"input": {
Expand Down
20 changes: 20 additions & 0 deletions files/mcollective/agent/shell.rb
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,26 @@ class Shell<RPC::Agent
end
end

action 'statuses' do
handles = request[:handles]
results = {}
handles.each do |handle|
begin
job = Job.new(handle)
entry = {
:status => job.status,
:stdout => job.stdout,
:stderr => job.stderr,
}
entry[:exitcode] = job.exitcode if job.status == :stopped
results[handle] = entry
rescue StandardError => error
results[handle] = { :status => :error, :error => error.message }
end
end
reply[:statuses] = results
end

action 'kill' do
handle = request[:handle]
job = Job.new(handle)
Expand Down
73 changes: 73 additions & 0 deletions spec/unit/agent/shell_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,79 @@ module Agent
end
end

describe '#statuses' do
let(:reply) { {} }

before :each do
agent.stubs(:reply).returns(reply)
@tmpdir = Dir.mktmpdir
Shell::Job.stubs(:state_path).returns(@tmpdir)
end

after :each do
FileUtils.remove_entry_secure @tmpdir
end

it 'should return stdout, stderr, and exitcode for stopped jobs' do
job = Shell::Job.new
job.start_command('echo foo')
job.wait_for_process

agent.call(:statuses, :handles => [job.handle])
statuses = reply[:statuses]
statuses.should have_key(job.handle)
statuses[job.handle][:status].should == :stopped
statuses[job.handle][:stdout].should == "foo\n"
statuses[job.handle][:stderr].should == ''
statuses[job.handle][:exitcode].should == 0
end

it 'should return stdout and stderr for running jobs' do
job = Shell::Job.new
job.start_command(%{ruby -e 'STDOUT.sync = true; puts "partial"; sleep 60'})
sleep 0.5

agent.call(:statuses, :handles => [job.handle])
statuses = reply[:statuses]
statuses[job.handle][:status].should == :running
statuses[job.handle][:stdout].should == "partial\n"
statuses[job.handle][:stderr].should == ''
statuses[job.handle].should_not have_key(:exitcode)

job.kill
job.wait_for_process
end

it 'should return error for invalid handle without affecting valid handles' do
job = Shell::Job.new
job.start_command('echo good')
job.wait_for_process

agent.call(:statuses, :handles => [job.handle, 'nonexistent-handle'])
statuses = reply[:statuses]
statuses[job.handle][:status].should == :stopped
statuses[job.handle][:stdout].should == "good\n"
statuses['nonexistent-handle'][:status].should == :error
statuses['nonexistent-handle'].should have_key(:error)
end

it 'should handle multiple handles in one call' do
job_one = Shell::Job.new
job_one.start_command('echo one')
job_one.wait_for_process

job_two = Shell::Job.new
job_two.start_command('echo two')
job_two.wait_for_process

agent.call(:statuses, :handles => [job_one.handle, job_two.handle])
statuses = reply[:statuses]
statuses.keys.size.should == 2
statuses[job_one.handle][:stdout].should == "one\n"
statuses[job_two.handle][:stdout].should == "two\n"
end
end

describe '#run_command' do
let(:reply) { {} }

Expand Down
Loading