From 134f509892cfa605b4c3c045b28af6345361c8b4 Mon Sep 17 00:00:00 2001 From: boiledfroginthewell Date: Sat, 15 Jul 2023 22:09:14 +0900 Subject: [PATCH] fix: prevent stdout from disappearing in script templates Signed-off-by: boiledfroginthewell --- cmd/argoexec/commands/emissary_test.go | 9 ++++ workflow/executor/os-specific/command.go | 8 +++- workflow/executor/os-specific/command_test.go | 47 +++++++++++++++++++ 3 files changed, 63 insertions(+), 1 deletion(-) create mode 100644 workflow/executor/os-specific/command_test.go diff --git a/cmd/argoexec/commands/emissary_test.go b/cmd/argoexec/commands/emissary_test.go index 2f40a48ed61b..78cc0be041cc 100644 --- a/cmd/argoexec/commands/emissary_test.go +++ b/cmd/argoexec/commands/emissary_test.go @@ -40,12 +40,21 @@ func TestEmissary(t *testing.T) { assert.Equal(t, "1", string(data)) }) t.Run("Stdout", func(t *testing.T) { + _ = os.Remove(varRunArgo + "/ctr/main/stdout") err := run("echo hello") assert.NoError(t, err) data, err := os.ReadFile(varRunArgo + "/ctr/main/stdout") assert.NoError(t, err) assert.Contains(t, string(data), "hello") }) + t.Run("Sub-process", func(t *testing.T) { + _ = os.Remove(varRunArgo + "/ctr/main/stdout") + err := run("(sleep 60; echo 'should not wait for sub-process')& echo -n hello") + assert.NoError(t, err) + data, err := os.ReadFile(varRunArgo + "/ctr/main/stdout") + assert.NoError(t, err) + assert.Equal(t, "hello", string(data)) + }) t.Run("Combined", func(t *testing.T) { err := run("echo hello > /dev/stderr") assert.NoError(t, err) diff --git a/workflow/executor/os-specific/command.go b/workflow/executor/os-specific/command.go index 53ea1f2a42b0..3e636babcd77 100644 --- a/workflow/executor/os-specific/command.go +++ b/workflow/executor/os-specific/command.go @@ -4,6 +4,7 @@ import ( "io" "os" "os/exec" + "time" log "github.com/sirupsen/logrus" "golang.org/x/term" @@ -16,7 +17,12 @@ func simpleStart(cmd *exec.Cmd) (func(), error) { return nil, err } - return func() {}, nil + closer := func() { + cmd.WaitDelay = 100 * time.Millisecond + _ = cmd.Wait() + } + + return closer, nil } func isTerminal(stdin io.Reader) bool { diff --git a/workflow/executor/os-specific/command_test.go b/workflow/executor/os-specific/command_test.go new file mode 100644 index 000000000000..53b07d5da96f --- /dev/null +++ b/workflow/executor/os-specific/command_test.go @@ -0,0 +1,47 @@ +package os_specific + +import ( + "bytes" + "io" + "os/exec" + "testing" + "time" + + "github.com/stretchr/testify/assert" +) + +func TestSimpleStartCloser(t *testing.T) { + cmd := exec.Command("sh", "-c", "echo -n A123456789B123456789C123456789D123456789E123456789") + var stdoutWriter bytes.Buffer + slowWriter := SlowWriter{ + &stdoutWriter, + } + // Command outputs are asynchronously written to cmd.Stdout. + // Using SlowWriter causes the situation where the invoked command has exited but its outputs have not been written yet. + cmd.Stdout = slowWriter + + closer, err := StartCommand(cmd) + assert.NoError(t, err) + err = cmd.Process.Release() + assert.NoError(t, err) + // Wait for echo command to exit before calling closer + time.Sleep(100 * time.Millisecond) + closer() + + assert.Equal(t, "A123456789B123456789C123456789D123456789E123456789", stdoutWriter.String()) +} + +type SlowWriter struct { + Writer io.Writer +} + +func (s SlowWriter) Write(data []byte) (n int, err error) { + for i := range data { + _, err := s.Writer.Write(data[i : i+1]) + if err != nil { + return i, err + } + time.Sleep(7 * time.Millisecond) + } + return len(data), nil +}