Class: RightScale::Windows::PowershellPipeServer

Inherits:
Object
  • Object
show all
Defined in:
lib/chef/windows/powershell_pipe_server.rb

Overview

Provides a server for a named pipe connection which serves a series of commands to be executed in the powershell context. The final command is expected to be an “exit” statement.

Constant Summary collapse

LAST_EXIT_CODE_KEY =

Request hash key associated with previous execution exit code

"LastExitCode"
LAST_ERROR_MESSAGE_KEY =
"LastErrorMessage"
NEXT_ACTION_KEY =

Response hash key associated with action to run

:NextAction

Instance Method Summary collapse

Constructor Details

#initialize(options = {}, &callback) ⇒ PowershellPipeServer

Initialize pipe server

Parameters

options(String)

Name of pipe to connect to (required)

Block

Given block gets called back for each request It should take two arguments:

* First argument is either :is_ready or :respond
  calls with :is_ready should return a boolean value set to true if there is a pending command
  calls with :respond should return the pending command
* Second argument contains the request data (only set with :respond)

Raises:

  • (ArgumentError)


55
56
57
58
59
# File 'lib/chef/windows/powershell_pipe_server.rb', line 55

def initialize(options = {}, &callback)
  raise ArgumentError, "Missing required :pipe_name" unless @pipe_name = options[:pipe_name]
  @callback = callback
  @pipe_eventable = nil
end

Instance Method Details

#request_handler(request_data) ⇒ Object

Handler for next action requests. Expects complete requests and responses to appear serialized as JSON on individual lines (i.e. delimited by newlines). note that JSON text escapes newline characters within string values and normally only includes whitespace for human- readability.

Parameters

request_data(String)

Request data

Returns

response(String)

Request response



119
120
121
122
123
124
125
126
127
128
129
130
# File 'lib/chef/windows/powershell_pipe_server.rb', line 119

def request_handler(request_data)
  # assume request_data is a single line with a possible newline trailing.
  request = JSON.load(request_data.chomp)
  if 2 == request.keys.size && request.has_key?(LAST_EXIT_CODE_KEY) && request.has_key?(LAST_ERROR_MESSAGE_KEY)
    # pop the next action from the queue.
    command = @callback.call(:respond, request)
    return JSON.dump(NEXT_ACTION_KEY => command) + "\n";
  end
  raise ArgumentError, "Invalid request"
rescue Exception => e
  return JSON.dump(:Error => "#{e.class}: #{e.message}", :Detail => e.backtrace.join("\n")) + "\n"
end

#request_query(request_data) ⇒ Object

Ready to respond if the next action queue is empty, otherwise continue blocking client.

Parameters

request_data(String)

request data

Returns

result(Boolean)

true if response is ready



104
105
106
# File 'lib/chef/windows/powershell_pipe_server.rb', line 104

def request_query(request_data)
  return @callback.call(:is_ready, nil)
end

#startObject

Starts the pipe server by creating an asynchronous named pipe. Returns control to the caller after adding the pipe to the event machine.

Return

true

If server was successfully started

false

Otherwise



67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
# File 'lib/chef/windows/powershell_pipe_server.rb', line 67

def start
  flags = ::Win32::Pipe::ACCESS_DUPLEX | ::Win32::Pipe::OVERLAPPED
  pipe  = PipeServer.new(@pipe_name, 0, flags)
  res   = true
  begin
    options = {:target => self,
               :request_handler => :request_handler,
               :request_query => :request_query,
               :pipe => pipe}
    @pipe_eventable = EM.watch(pipe, PipeServerHandler, options)
    @pipe_eventable.notify_readable = true
  rescue Exception => e
    pipe.close rescue nil
    RightScale::Log.error("Failed to start pipe server", e, :trace)
    res = false
  end
  res
end

#stopObject

Stops the pipe server by detaching the eventable from the event machine.

Return

true

Always return true



90
91
92
93
94
# File 'lib/chef/windows/powershell_pipe_server.rb', line 90

def stop
  @pipe_eventable.force_detach if @pipe_eventable
  @pipe_eventable = nil
  true
end