fix: support large output with --enable-live-ouput (#1139)

* fix: support large output with --enable-live-ouput

This replaces Scanner with ReadString to handle large amount of data returned by helm ouptut when executing diff action

- Changing pkg/helmexec/runner_test.go TestLiveOutput test, adding test with large amount of data, reproducing the issue before applying this fix
- Changing pkg/helmexec/runner.go Scanner with Readstring

Resolves https://github.com/helmfile/helmfile/issues/893

Signed-off-by: Franck Labatut <franck.labatut@ubisoft.com>

* fix: prevent data race

Signed-off-by: Franck Labatut <franck.labatut@ubisoft.com>

---------

Signed-off-by: Franck Labatut <franck.labatut@ubisoft.com>
This commit is contained in:
flabatut 2023-11-11 23:45:08 +01:00 committed by GitHub
parent e756712e80
commit b8a729d8c4
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
3 changed files with 29709 additions and 7 deletions

View File

@ -127,13 +127,26 @@ func Output(ctx context.Context, c *exec.Cmd, stripArgsValuesOnExitError bool, l
func LiveOutput(ctx context.Context, c *exec.Cmd, stripArgsValuesOnExitError bool, stdout io.Writer) ([]byte, error) { func LiveOutput(ctx context.Context, c *exec.Cmd, stripArgsValuesOnExitError bool, stdout io.Writer) ([]byte, error) {
reader, writer := io.Pipe() reader, writer := io.Pipe()
scannerStopped := make(chan struct{}) ch := make(chan error)
doneCh := make(chan struct{})
go func() { go func() {
defer close(scannerStopped) defer close(doneCh)
scanner := bufio.NewScanner(reader) reader := bufio.NewReader(reader)
for scanner.Scan() {
fmt.Fprintln(stdout, scanner.Text()) for {
// prefer readstring over scanner to handle potential large output
// https://stackoverflow.com/a/29444042
line, err := reader.ReadString('\n')
if err != nil {
if err == io.EOF {
break
}
ch <- err
}
line = strings.TrimSuffix(line, "\n")
fmt.Fprintln(stdout, line)
} }
}() }()
@ -142,7 +155,6 @@ func LiveOutput(ctx context.Context, c *exec.Cmd, stripArgsValuesOnExitError boo
err := c.Start() err := c.Start()
if err == nil { if err == nil {
ch := make(chan error)
go func() { go func() {
ch <- c.Wait() ch <- c.Wait()
}() }()
@ -154,7 +166,7 @@ func LiveOutput(ctx context.Context, c *exec.Cmd, stripArgsValuesOnExitError boo
err = <-ch err = <-ch
} }
_ = writer.Close() _ = writer.Close()
<-scannerStopped <-doneCh
} }
if err != nil { if err != nil {

View File

@ -3,6 +3,7 @@ package helmexec
import ( import (
"bytes" "bytes"
"context" "context"
_ "embed"
"os/exec" "os/exec"
"reflect" "reflect"
"strings" "strings"
@ -48,6 +49,9 @@ func TestShellRunner_Execute(t *testing.T) {
} }
} }
//go:embed testdata/live-output-data.txt
var liveOutputData string
func TestLiveOutput(t *testing.T) { func TestLiveOutput(t *testing.T) {
tests := []struct { tests := []struct {
name string name string
@ -55,6 +59,12 @@ func TestLiveOutput(t *testing.T) {
wantW string wantW string
wantErr bool wantErr bool
}{ }{
{
name: "live_output_data",
cmd: exec.Command("cat", "testdata/live-output-data.txt"),
wantW: liveOutputData,
wantErr: false,
},
{ {
name: "echo_template", name: "echo_template",
cmd: exec.Command("echo", "template"), cmd: exec.Command("echo", "template"),

29680
pkg/helmexec/testdata/live-output-data.txt vendored Normal file

File diff suppressed because one or more lines are too long