Skip to content

Commit

Permalink
Add a stdin option to ShellCommandAgent
Browse files Browse the repository at this point in the history
  • Loading branch information
knu committed Oct 21, 2015
1 parent d9d5fbf commit c3fb669
Show file tree
Hide file tree
Showing 2 changed files with 33 additions and 10 deletions.
22 changes: 18 additions & 4 deletions app/models/agents/shell_command_agent.rb
Expand Up @@ -11,7 +11,7 @@ def self.should_run?
description <<-MD
The Shell Command Agent will execute commands on your local system, returning the output.
`command` specifies the command (either a shell command line string or an array of command line arguments) to be executed, and `path` will tell ShellCommandAgent in what directory to run this command.
`command` specifies the command (either a shell command line string or an array of command line arguments) to be executed, and `path` will tell ShellCommandAgent in what directory to run this command. The content of `stdin` will be fed to the command via the standard input.
`expected_update_period_in_days` is used to determine if the Agent is working.
Expand Down Expand Up @@ -50,6 +50,12 @@ def validate_options
errors.add(:base, "The path, command, and expected_update_period_in_days fields are all required.")
end

case options['stdin']
when String, nil
else
errors.add(:base, "stdin must be a string.")
end

unless Array(options['command']).all? { |o| o.is_a?(String) }
errors.add(:base, "command must be a shell command line string or an array of command line arguments.")
end
Expand Down Expand Up @@ -79,8 +85,9 @@ def handle(opts, event = nil)
if Agents::ShellCommandAgent.should_run?
command = opts['command']
path = opts['path']
stdin = opts['stdin']

result, errors, exit_status = run_command(path, command)
result, errors, exit_status = run_command(path, command, stdin)

vals = {"command" => command, "path" => path, "exit_status" => exit_status, "errors" => errors, "output" => result}
created_event = create_event :payload => vals
Expand All @@ -91,15 +98,22 @@ def handle(opts, event = nil)
end
end

def run_command(path, command)
def run_command(path, command, stdin)
begin
rout, wout = IO.pipe
rerr, werr = IO.pipe
rin, win = IO.pipe

pid = spawn(*command, chdir: path, out: wout, err: werr)
pid = spawn(*command, chdir: path, out: wout, err: werr, in: rin)

wout.close
werr.close
rin.close

if stdin
win.write stdin
win.close
end

(result = rout.read).strip!
(errors = rerr.read).strip!
Expand Down
21 changes: 15 additions & 6 deletions spec/models/agents/shell_command_agent_spec.rb
Expand Up @@ -12,7 +12,8 @@

@valid_params2 = {
path: @valid_path,
command: [RbConfig.ruby, '-e', 'puts "hello, world."; STDERR.puts "warning!"'],
command: [RbConfig.ruby, '-e', 'puts "hello, #{STDIN.eof? ? "world" : STDIN.read.strip}."; STDERR.puts "warning!"'],
stdin: "{{name}}",
expected_update_period_in_days: '1',
}

Expand All @@ -27,7 +28,8 @@
@event = Event.new
@event.agent = agents(:jane_weather_agent)
@event.payload = {
:cmd => "ls"
'name' => 'Huginn',
'cmd' => 'ls',
}
@event.save!

Expand Down Expand Up @@ -58,7 +60,7 @@

describe "#working?" do
it "generating events as scheduled" do
stub(@checker).run_command(@valid_path, 'pwd') { ["fake pwd output", "", 0] }
stub(@checker).run_command(@valid_path, 'pwd', nil) { ["fake pwd output", "", 0] }

expect(@checker).not_to be_working
@checker.check
Expand All @@ -71,7 +73,7 @@

describe "#check" do
before do
stub(@checker).run_command(@valid_path, 'pwd') { ["fake pwd output", "", 0] }
stub(@checker).run_command(@valid_path, 'pwd', nil) { ["fake pwd output", "", 0] }
end

it "should create an event when checking" do
Expand All @@ -84,7 +86,7 @@
it "should create an event when checking (unstubbed)" do
expect { @checker2.check }.to change { Event.count }.by(1)
expect(Event.last.payload[:path]).to eq(@valid_path)
expect(Event.last.payload[:command]).to eq([RbConfig.ruby, '-e', 'puts "hello, world."; STDERR.puts "warning!"'])
expect(Event.last.payload[:command]).to eq([RbConfig.ruby, '-e', 'puts "hello, #{STDIN.eof? ? "world" : STDIN.read.strip}."; STDERR.puts "warning!"'])
expect(Event.last.payload[:output]).to eq('hello, world.')
expect(Event.last.payload[:errors]).to eq('warning!')
end
Expand All @@ -97,7 +99,7 @@

describe "#receive" do
before do
stub(@checker).run_command(@valid_path, @event.payload[:cmd]) { ["fake ls output", "", 0] }
stub(@checker).run_command(@valid_path, @event.payload[:cmd], nil) { ["fake ls output", "", 0] }
end

it "creates events" do
Expand All @@ -108,6 +110,13 @@
expect(Event.last.payload[:output]).to eq("fake ls output")
end

it "creates events (unstubbed)" do
@checker2.receive([@event])
expect(Event.last.payload[:path]).to eq(@valid_path)
expect(Event.last.payload[:output]).to eq('hello, Huginn.')
expect(Event.last.payload[:errors]).to eq('warning!')
end

it "does not run when should_run? is false" do
stub(Agents::ShellCommandAgent).should_run? { false }

Expand Down

0 comments on commit c3fb669

Please sign in to comment.