lib/symphony/tasks/ssh.rb
changeset 17 5db18679edcf
parent 11 ffa70066522c
equal deleted inserted replaced
16:420037982f78 17:5db18679edcf
    17 ###
    17 ###
    18 ###    host:    (required) The hostname to connect to
    18 ###    host:    (required) The hostname to connect to
    19 ###    command: (required) The command to run on the remote host
    19 ###    command: (required) The command to run on the remote host
    20 ###    port:    (optional) The port to connect to (defaults to 22)
    20 ###    port:    (optional) The port to connect to (defaults to 22)
    21 ###    opts:    (optional) Explicit SSH client options
    21 ###    opts:    (optional) Explicit SSH client options
       
    22 ###    env:     (optional) A hash of environment vars to set for the connection.
    22 ###    user:    (optional) The user to connect as (defaults to root)
    23 ###    user:    (optional) The user to connect as (defaults to root)
    23 ###    key:     (optional) The path to an SSH private key
    24 ###    key:     (optional) The path to an SSH private key
    24 ###
    25 ###
    25 ###
    26 ###
    26 ### Additionally, this class responds to the 'symphony.ssh' configurability
    27 ### Additionally, this class responds to the 'symphony.ssh' configurability
    84 		# An absolute path to a password-free ssh private key.
    85 		# An absolute path to a password-free ssh private key.
    85 		setting :key
    86 		setting :key
    86 	end
    87 	end
    87 
    88 
    88 
    89 
    89 	### Perform the ssh connection, passing the command to the pipe
    90 	### Perform the ssh connection in 'exec' mode, and retrieve any
    90 	### and retreiving any output from the remote end.
    91 	### output from the remote end.
    91 	###
    92 	###
    92 	def work( payload, metadata )
    93 	def work( payload, metadata )
    93 		command = payload[ 'command' ]
    94 		raise ArgumentError, "Missing required option 'command'" unless payload[ 'command' ]
    94 		raise ArgumentError, "Missing required option 'command'" unless command
       
    95 		raise ArgumentError, "Missing required option 'host'"    unless payload[ 'host' ]
    95 		raise ArgumentError, "Missing required option 'host'"    unless payload[ 'host' ]
    96 
    96 
    97 		exitcode = self.open_connection( payload, metadata ) do |reader, writer|
    97 		exitcode = self.open_connection( payload, metadata ) do |reader, writer|
    98 			self.log.debug "Writing command #{command}..."
    98 			#self.log.debug "Writing command #{command}..."
    99 			writer.puts( command )
    99 			#writer.puts( command )
   100 			self.log.debug "  closing child's writer."
   100 			self.log.debug "  closing child's writer."
   101 			writer.close
   101 			writer.close
   102 			self.log.debug "  reading from child."
   102 			self.log.debug "  reading from child."
   103 			reader.read
   103 			reader.read
   104 		end
   104 		end
   121 
   121 
   122 		port = payload[ 'port' ] || 22
   122 		port = payload[ 'port' ] || 22
   123 		opts = payload[ 'opts' ] || Symphony::Task::SSH.opts
   123 		opts = payload[ 'opts' ] || Symphony::Task::SSH.opts
   124 		user = payload[ 'user' ] || Symphony::Task::SSH.user
   124 		user = payload[ 'user' ] || Symphony::Task::SSH.user
   125 		key  = payload[ 'key'  ] || Symphony::Task::SSH.key
   125 		key  = payload[ 'key'  ] || Symphony::Task::SSH.key
       
   126 		env  = payload[ 'env'  ] || {}
   126 
   127 
   127 		cmd = []
   128 		cmd = []
   128 		cmd << Symphony::Task::SSH.path
   129 		cmd << Symphony::Task::SSH.path
   129 		cmd += opts
   130 		cmd += opts
   130 
   131 
   131 		cmd << '-p' << port.to_s
   132 		cmd << '-p' << port.to_s
   132 		cmd << '-i' << key if key
   133 		cmd << '-i' << key if key
   133 		cmd << '-l' << user
   134 		cmd << '-l' << user
   134 		cmd << payload[ 'host' ]
   135 		cmd << payload[ 'host' ]
       
   136 		cmd << payload[ 'command' ]
   135 		cmd.flatten!
   137 		cmd.flatten!
   136 		self.log.debug "Running SSH command with: %p" % [ Shellwords.shelljoin(cmd) ]
   138 		self.log.debug "Running SSH command with: %p" % [ Shellwords.shelljoin(cmd) ]
   137 
   139 
   138 		parent_reader, child_writer = IO.pipe
   140 		parent_reader, child_writer = IO.pipe
   139 		child_reader, parent_writer = IO.pipe
   141 		child_reader, parent_writer = IO.pipe
   140 
   142 
   141 		pid = Process.spawn( *cmd, :out => child_writer, :in => child_reader, :close_others => true )
   143 		pid = Process.spawn( env, *cmd,
       
   144 			out:             child_writer,
       
   145 			in:              child_reader,
       
   146 			close_others:    true,
       
   147 			unsetenv_others: true
       
   148 		)
       
   149 
   142 		child_writer.close
   150 		child_writer.close
   143 		child_reader.close
   151 		child_reader.close
   144 
   152 
   145 		self.log.debug "Yielding back to the run block."
   153 		self.log.debug "Yielding back to the run block."
   146 		@output = yield( parent_reader, parent_writer )
   154 		@output = yield( parent_reader, parent_writer )
   147 		@output = @output.split("\n").reject{|l| l =~ SSH_CLEANUP }.join
   155 		@output = @output.split( /\r?\n/ ).reject{|l| l =~ SSH_CLEANUP }.join
   148 		self.log.debug "  run block done."
   156 		self.log.debug "  run block done."
   149 
   157 
   150 		status = nil
   158 	rescue => err
   151 
   159 		self.log.error( err.message )
   152 	ensure
   160 	ensure
   153 		if pid
   161 		if pid
   154 			active = Process.kill( 0, pid ) rescue false
   162 			active = Process.kill( 0, pid ) rescue false
   155 			Process.kill( :TERM, pid ) if active
   163 			Process.kill( :TERM, pid ) if active
   156 			pid, status = Process.waitpid2( pid )
   164 			pid, status = Process.waitpid2( pid )