ensure that the shell provisioner only emits complete output lines to the ui

partial lines are buffered until they are complete (have a line ending)

see https://github.com/hashicorp/vagrant/issues/11047
This commit is contained in:
Rui Lopes 2021-05-31 20:20:12 +01:00 committed by Chris Roberts
parent eb1327bf7e
commit ef18b45786
2 changed files with 91 additions and 22 deletions

View File

@ -0,0 +1,35 @@
require 'stringio'
module Vagrant
module Util
class LineBuffer
def initialize
@buffer = StringIO.new
end
def lines(data, &block)
if data == nil
return
end
remaining_buffer = StringIO.new
@buffer << data
@buffer.string.each_line do |line|
if line.end_with? "\n"
block.call(line.rstrip)
else
remaining_buffer << line
break
end
end
@buffer = remaining_buffer
end
def remaining(&block)
if @buffer.length > 0
block.call(@buffer.string.rstrip)
@buffer = StringIO.new
end
end
end
end
end

View File

@ -2,6 +2,7 @@ require "pathname"
require "tempfile"
require "vagrant/util/downloader"
require "vagrant/util/line_buffer"
require "vagrant/util/retryable"
module VagrantPlugins
@ -65,15 +66,42 @@ module VagrantPlugins
protected
# This handles outputting the communication data back to the UI
def handle_comm(type, data)
if [:stderr, :stdout].include?(type)
# Output the data with the proper color based on the stream.
color = type == :stdout ? :green : :red
# This buffers a communicator output data (which can have partial
# lines) into full lines and emits them back to the UI.
def comm_line_buffer(&block)
stdout = Vagrant::Util::LineBuffer.new
stderr = Vagrant::Util::LineBuffer.new
# Clear out the newline since we add one
data = data.chomp
return if data.empty?
line_buffer_handle_comm = Proc.new do |type, data|
case type
when :stdout
stdout.lines data do |line|
handle_comm(type, line)
end
when :stderr
stderr.lines data do |line|
handle_comm(type, line)
end
end
end
# execute the block and emit complete lines to the UI.
block.call(line_buffer_handle_comm)
# emit the remaining as incomplete/partial lines to the UI.
stdout.remaining do |line|
handle_comm(:stdout, line)
end
stderr.remaining do |line|
handle_comm(:stderr, line)
end
end
# This handles outputting the communication line back to the UI
def handle_comm(type, line)
if [:stderr, :stdout].include?(type)
# Output the line with the proper color based on the stream.
color = type == :stdout ? :green : :red
options = {}
options[:color] = color if !config.keep_color
@ -121,12 +149,14 @@ module VagrantPlugins
end
# Execute it with sudo
comm.execute(
command,
sudo: config.privileged,
error_key: :ssh_bad_exit_status_muted
) do |type, data|
handle_comm(type, data)
comm_line_buffer do |handle_comm|
comm.execute(
command,
sudo: config.privileged,
error_key: :ssh_bad_exit_status_muted
) do |type, data|
handle_comm.call(type, data)
end
end
end
end
@ -176,12 +206,14 @@ module VagrantPlugins
end
# Execute it with sudo
comm.execute(
command,
shell: :powershell,
error_key: :ssh_bad_exit_status_muted
) do |type, data|
handle_comm(type, data)
comm_line_buffer do |handle_comm|
comm.execute(
command,
shell: :powershell,
error_key: :ssh_bad_exit_status_muted
) do |type, data|
handle_comm.call(type, data)
end
end
end
end
@ -245,8 +277,10 @@ module VagrantPlugins
end
# Execute it with sudo
comm.sudo(command, { elevated: config.privileged, interactive: config.powershell_elevated_interactive }) do |type, data|
handle_comm(type, data)
comm_line_buffer do |handle_comm|
comm.sudo(command, { elevated: config.privileged, interactive: config.powershell_elevated_interactive }) do |type, data|
handle_comm.call(type, data)
end
end
end
end