Class: RightScale::CommandRunner

Inherits:
Object
  • Object
show all
Defined in:
lib/right_agent/command/command_runner.rb

Overview

Run commands exposed by an agent. External processes can send commands through a socket with the specified port. Command runner accepts connections and unserializes commands using YAML. Each command is expected to be a hash containing the :name and :options keys.

Class Attribute Summary collapse

Class Method Summary collapse

Class Attribute Details

(String) Cookie used by command protocol



35
36
37
# File 'lib/right_agent/command/command_runner.rb', line 35

def cookie
  @cookie
end

.listen_portObject (readonly)

(Integer) Port command runner is listening on



32
33
34
# File 'lib/right_agent/command/command_runner.rb', line 32

def listen_port
  @listen_port
end

Class Method Details

.start(socket_port, identity, commands, fiber_pool = nil) ⇒ Object

Start a command runner listening on a local TCP port.

Parameters

socket_port(Integer)

Base socket port on which to listen for connection, increment and retry if port already taken

identity(String)

Agent identity

commands(Hash)

Commands exposed by agent

fiber_pool(NB::FiberPool)

Pool of initialized fibers to be used for executing

received commands in non-blocking fashion

Block

If a block is provided, this method will yield after all setup has been completed, passing its PidFile to the block. This provides a customization hook, e.g. for changing the pid file’s access mode or ownership.

Return

cmd_options(String)

Command protocol cookie

cmd_options(Integer)

Command server listen port

Raise

(Exceptions::Application)

If start has already been called and stop hasn’t since



59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
# File 'lib/right_agent/command/command_runner.rb', line 59

def self.start(socket_port, identity, commands, fiber_pool = nil)
  cmd_options = nil
  @listen_port = socket_port

  begin
    CommandIO.instance.listen(socket_port) do |c, conn|
      begin
        cmd_cookie = c[:cookie]
        if cmd_cookie == @cookie
          cmd_name = c[:name].to_sym
          if commands.include?(cmd_name)
            if fiber_pool
              fiber_pool.spawn { commands[cmd_name].call(c, conn) }
            else
              commands[cmd_name].call(c, conn)
            end
          else
            Log.warning("Unknown command '#{cmd_name}', known commands: #{commands.keys.join(', ')}")
          end
        else
          Log.error("Invalid cookie used by command protocol client (#{cmd_cookie})")
        end
      rescue Exception => e
        Log.error("Command failed", e, :trace)
      end
    end

    @cookie = AgentIdentity.generate
    cmd_options = { :listen_port => @listen_port, :cookie => @cookie }
    # Now update pid file with command port and cookie
    pid_file = PidFile.new(identity)
    if pid_file.exists?
      pid_file.set_command_options(cmd_options)
      yield(pid_file) if block_given?
    else
      Log.warning("Failed to update listen port in PID file - no pid file found for agent with identity #{identity}")
    end

    Log.info("[setup] Command server started listening on port #{@listen_port}")
  rescue Exceptions::IO
    # Port already taken, increment and retry
    cmd_options = start(socket_port + 1, identity, commands)
  end

  cmd_options
end

.stopObject

Stop command runner, cleanup all opened file descriptors and delete pipe

Return

true

If command listener was listening

false

Otherwise



111
112
113
114
# File 'lib/right_agent/command/command_runner.rb', line 111

def self.stop
  CommandIO.instance.stop_listening
  Log.info("[stop] Command server stopped listening")
end