init CommandAgent

ZirconCode 11 ans auparavant
Parent
Commettre
e65bc11f02
2 fichiers modifiés avec 158 ajouts et 0 suppressions
  1. 88 0
      app/models/agents/command_agent.rb
  2. 70 0
      spec/models/agents/command_agent_spec.rb

+ 88 - 0
app/models/agents/command_agent.rb

@@ -0,0 +1,88 @@
1
+module Agents
2
+  class CommandAgent < Agent
3
+    
4
+
5
+    default_schedule "midnight"
6
+
7
+
8
+    description <<-MD
9
+      The CommandAgent can execute commands on your local system, returning the output.
10
+
11
+      `command` specifies the command to be executed, and `path` will tell CommandAgent in what directory to run this command.
12
+
13
+      `expected_update_period_in_days` is used to determine if the Agent is working.
14
+
15
+      CommandAgent can also act upon recieveing events. These events may contain their own path and command arguments. If they do not, CommandAgent will use the configured options. For this reason, please specify defaults even if you are planning to have this Agent respond to events.
16
+
17
+      The resulting event will contain the `command` which was executed, the `path` it was executed under, the `exit_status` of the command, and the actual `output`. CommandAgent will not log an error if the `exit_status` implies that something went wrong.
18
+
19
+    MD
20
+
21
+    event_description <<-MD
22
+    Events look like this:
23
+
24
+      {
25
+        'command' => 'pwd',
26
+        'path' => '/home/Huginn',
27
+        'exit_status' => '0',
28
+        'output' => '/home' 
29
+      }
30
+    MD
31
+
32
+    def default_options
33
+      {
34
+          'path' => "/home",
35
+          'command' => "pwd",
36
+          'expected_update_period_in_days' => 1
37
+      }
38
+    end
39
+
40
+    def validate_options
41
+      unless options['path'].present? && options['command'].present? && options['expected_update_period_in_days'].present?
42
+        errors.add(:base, "The path, command, and expected_update_period_in_days fields are all required.")
43
+      end   
44
+      unless File.directory?(options['path'])
45
+        errors.add(:base, "#{options['path']} is not a real directory.")
46
+      end
47
+    end
48
+
49
+    def working?
50
+      event_created_within?(options['expected_update_period_in_days']) && !recent_error_logs?
51
+    end
52
+
53
+    def exec_command(opts = options)
54
+      command = opts['command'] || options['command']
55
+      path = opts['path'] || options['path']
56
+
57
+      result = nil
58
+      proc_stat = nil
59
+      Dir.chdir(path){
60
+        result = `#{command}`
61
+        proc_stat = $?
62
+      }
63
+
64
+      exit_status = -404 # should never happen, but $? is global
65
+      exit_status = proc_stat.to_i if(proc_stat.is_a?(Process::Status))
66
+
67
+      result.chomp! if !result.nil?
68
+
69
+      vals = {"command" => command, "path" => path, "exit_status" => exit_status, "output" => result}
70
+      evnt = create_event :payload => vals
71
+
72
+      log("Ran '#{command}' under '#{path}'", :outbound_event => evnt)
73
+    end
74
+
75
+
76
+    def receive(incoming_events)
77
+      incoming_events.each do |event|
78
+        exec_command(event.payload)
79
+      end
80
+    end
81
+
82
+    def check
83
+      exec_command(options)
84
+    end
85
+
86
+
87
+  end
88
+end

+ 70 - 0
spec/models/agents/command_agent_spec.rb

@@ -0,0 +1,70 @@
1
+require 'spec_helper'
2
+
3
+describe Agents::CommandAgent do
4
+
5
+	before do
6
+		@valid_path = Dir.pwd
7
+		@valid_params = {
8
+							:path 							=> @valid_path,
9
+							:command						=> "pwd",
10
+							:expected_update_period_in_days => "1",
11
+						}
12
+
13
+		@checker = Agents::CommandAgent.new(:name => "somename", :options => @valid_params)
14
+		@checker.user = users(:jane)
15
+		@checker.save!
16
+
17
+		@event = Event.new
18
+        @event.agent = agents(:jane_weather_agent)
19
+        @event.payload = {
20
+            :command => "pwd"
21
+        }
22
+        @event.save!
23
+	end
24
+
25
+	describe "validation" do
26
+	    before do
27
+	        @checker.should be_valid
28
+	    end
29
+
30
+	    it "should validate presence of necessary fields" do
31
+	        @checker.options[:command] = nil
32
+	        @checker.should_not be_valid
33
+	    end
34
+
35
+	    it "should validate path" do
36
+	        @checker.options[:path] = 'notarealpath/itreallyisnt'
37
+	        @checker.should_not be_valid
38
+	    end
39
+	end
40
+
41
+	describe "#working?" do
42
+		it "checks if its generating events as scheduled" do
43
+			@checker.should_not be_working
44
+			@checker.check
45
+			@checker.reload.should be_working
46
+			three_days_from_now = 3.days.from_now
47
+			stub(Time).now { three_days_from_now }
48
+			@checker.should_not be_working
49
+		end
50
+	end
51
+
52
+	describe "#check" do
53
+		it "should check that initial run creates an event" do
54
+			expect { @checker.check }.to change { Event.count }.by(1)
55
+		end
56
+	end
57
+
58
+	describe "#receive" do
59
+	    it "checks if creates events" do
60
+	        @checker.receive([@event])
61
+	        Event.last.payload[:path].should == @valid_path
62
+	    end
63
+	    it "checks if options are taken from event" do
64
+	    	@event.payload[:command] = 'notarealcommand'
65
+	        @checker.receive([@event])
66
+	        Event.last.payload[:command].should == 'notarealcommand'
67
+	    end
68
+	end
69
+
70
+end