WinRb / WinRM

Ruby library for Windows Remote Management
Apache License 2.0
412 stars 117 forks source link

shell.run returns partial data/lines #329

Open rgl opened 3 years ago

rgl commented 3 years ago

I'm trying to troubleshoot why shell.run is returning partial data/lines. Which is something that originally came from vagrant#11047, but it seems that what I've found has its root in this gem.

When we agree that this is rooted here, I will prepare a PR.

To troubleshoot this, I've created an example at https://github.com/rgl/ruby-winrm-shell-partial-output (please look here for the whole source).

The example emit-partial-output.py application is executed by main.rb and its output is displayed.

The example application will write a single byte at a time to stdout/stderr to test how the WinRM gem handles partial output.

The example application is executed as:

# shell_type will be :cmd or :powershell
conn.shell(shell_type) do |shell|
  output = shell.run command do |stdout, stderr|
    puts "stdout: #{stdout}" if stdout
    puts "stderr: #{stderr}" if stderr
  end
end

We can see unexpected differences in how the gem reports the output data.

When using the cmd shell we can see partial lines being reported. Compare the actual output:

================================================================================================================================
cmd original_shell_run
exitcode: 0
--------------------------------------------------------------------------------------------------------------------------------
stdout: #
stdout:  line 0001 ###################################################
stdout: #########################################################
# line 0002 ############################################################################################################
# line 0003 ############################################################################################################

--------------------------------------------------------------------------------------------------------------------------------
stderr: #
stderr:  line 
stderr: 0001 ############################################################################################################
# line 0002 ############################################################################################################
# line 0003 ############################################################################################################

================================================================================================================================

With the expected result:

cmd line_buffered_shell_run
exitcode: 0
--------------------------------------------------------------------------------------------------------------------------------
stdout: # line 0001 ############################################################################################################
stdout: # line 0002 ############################################################################################################
stdout: # line 0003 ############################################################################################################
--------------------------------------------------------------------------------------------------------------------------------
stderr: # line 0001 ############################################################################################################
stderr: # line 0002 ############################################################################################################
stderr: # line 0003 ############################################################################################################
================================================================================================================================

While using the powershell shell, we can see extra new lines being reported. Compare the actual output:

powershell original_shell_run
exitcode: 0
--------------------------------------------------------------------------------------------------------------------------------
stdout: # line 0001 ############################################################################################################

stdout: # line 0002 ############################################################################################################

stdout: # line 0003 ############################################################################################################

================================================================================================================================

With the expected result:

powershell line_buffered_shell_run
exitcode: 0
--------------------------------------------------------------------------------------------------------------------------------
stdout: # line 0001 ############################################################################################################
stdout: # line 0002 ############################################################################################################
stdout: # line 0003 ############################################################################################################

The expected results we normalized by line_buffered_shell_run function, which implements my expected results.

That function essentially buffers the command output and only emit complete lines to its caller.

To execute the whole example:

  1. In the target machine:
    1. Install Python 3.6 at C:\Python36.
    2. Copy the emit-partial-output.py file to the target machine C:\ directory.
  2. In the client machine:
    1. Install Ruby 2.7.0 and the WinRM gem 2.3.6.
    2. Set the target machine credentials in main.rb.
    3. Execute main.rb.

Please note that you might need to fiddle with the main.rb to make it misbehave in your machine. This is the line you want to modify:

command = 'C:/Python36/python.exe c:/emit-partial-output.py --lines 3 --length 120 --flush'