Skip to content

Commit c3fb669

Browse files
committed
Add a stdin option to ShellCommandAgent
1 parent d9d5fbf commit c3fb669

File tree

2 files changed

+33
-10
lines changed

2 files changed

+33
-10
lines changed

app/models/agents/shell_command_agent.rb

Lines changed: 18 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,7 @@ def self.should_run?
1111
description <<-MD
1212
The Shell Command Agent will execute commands on your local system, returning the output.
1313
14-
`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.
14+
`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.
1515
1616
`expected_update_period_in_days` is used to determine if the Agent is working.
1717
@@ -50,6 +50,12 @@ def validate_options
5050
errors.add(:base, "The path, command, and expected_update_period_in_days fields are all required.")
5151
end
5252

53+
case options['stdin']
54+
when String, nil
55+
else
56+
errors.add(:base, "stdin must be a string.")
57+
end
58+
5359
unless Array(options['command']).all? { |o| o.is_a?(String) }
5460
errors.add(:base, "command must be a shell command line string or an array of command line arguments.")
5561
end
@@ -79,8 +85,9 @@ def handle(opts, event = nil)
7985
if Agents::ShellCommandAgent.should_run?
8086
command = opts['command']
8187
path = opts['path']
88+
stdin = opts['stdin']
8289

83-
result, errors, exit_status = run_command(path, command)
90+
result, errors, exit_status = run_command(path, command, stdin)
8491

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

94-
def run_command(path, command)
101+
def run_command(path, command, stdin)
95102
begin
96103
rout, wout = IO.pipe
97104
rerr, werr = IO.pipe
105+
rin, win = IO.pipe
98106

99-
pid = spawn(*command, chdir: path, out: wout, err: werr)
107+
pid = spawn(*command, chdir: path, out: wout, err: werr, in: rin)
100108

101109
wout.close
102110
werr.close
111+
rin.close
112+
113+
if stdin
114+
win.write stdin
115+
win.close
116+
end
103117

104118
(result = rout.read).strip!
105119
(errors = rerr.read).strip!

spec/models/agents/shell_command_agent_spec.rb

Lines changed: 15 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,8 @@
1212

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

@@ -27,7 +28,8 @@
2728
@event = Event.new
2829
@event.agent = agents(:jane_weather_agent)
2930
@event.payload = {
30-
:cmd => "ls"
31+
'name' => 'Huginn',
32+
'cmd' => 'ls',
3133
}
3234
@event.save!
3335

@@ -58,7 +60,7 @@
5860

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

6365
expect(@checker).not_to be_working
6466
@checker.check
@@ -71,7 +73,7 @@
7173

7274
describe "#check" do
7375
before do
74-
stub(@checker).run_command(@valid_path, 'pwd') { ["fake pwd output", "", 0] }
76+
stub(@checker).run_command(@valid_path, 'pwd', nil) { ["fake pwd output", "", 0] }
7577
end
7678

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

98100
describe "#receive" do
99101
before do
100-
stub(@checker).run_command(@valid_path, @event.payload[:cmd]) { ["fake ls output", "", 0] }
102+
stub(@checker).run_command(@valid_path, @event.payload[:cmd], nil) { ["fake ls output", "", 0] }
101103
end
102104

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

113+
it "creates events (unstubbed)" do
114+
@checker2.receive([@event])
115+
expect(Event.last.payload[:path]).to eq(@valid_path)
116+
expect(Event.last.payload[:output]).to eq('hello, Huginn.')
117+
expect(Event.last.payload[:errors]).to eq('warning!')
118+
end
119+
111120
it "does not run when should_run? is false" do
112121
stub(Agents::ShellCommandAgent).should_run? { false }
113122

0 commit comments

Comments
 (0)