diff --git a/nomad/nomad.go b/nomad/nomad.go index 697aae2..460e162 100644 --- a/nomad/nomad.go +++ b/nomad/nomad.go @@ -8,6 +8,7 @@ import ( "github.com/hashicorp/nomad/nomad/structs" "gitlab.hpi.de/codeocean/codemoon/poseidon/config" "gitlab.hpi.de/codeocean/codemoon/poseidon/logging" + "gitlab.hpi.de/codeocean/codemoon/poseidon/util" "io" "net/url" "time" @@ -287,15 +288,6 @@ func (a *APIClient) LoadEnvironmentJobs() ([]*nomadApi.Job, error) { return jobs, nil } -// nullReader is a struct that implements the io.Reader interface and returns nothing when reading -// from it. -type nullReader struct{} - -func (r nullReader) Read(_ []byte) (int, error) { - // An empty select blocks forever. - select {} -} - // ExecuteCommand executes the given command in the given allocation. // If tty is true, Nomad would normally write stdout and stderr of the command // both on the stdout stream. However, if the InteractiveStderr server config option is true, @@ -328,7 +320,7 @@ func (a *APIClient) executeCommandInteractivelyWithStderr(allocationID string, c stderrExitChan := make(chan int) go func() { // Catch stderr in separate execution. - exit, err := a.Execute(allocationID, ctx, stderrFifoCommand(currentNanoTime), true, nullReader{}, stderr, io.Discard) + exit, err := a.Execute(allocationID, ctx, stderrFifoCommand(currentNanoTime), true, util.NullReader{}, stderr, io.Discard) if err != nil { log.WithError(err).WithField("runner", allocationID).Warn("Stderr task finished with error") } diff --git a/nomad/nomad_test.go b/nomad/nomad_test.go index bf9c30e..e583034 100644 --- a/nomad/nomad_test.go +++ b/nomad/nomad_test.go @@ -14,6 +14,7 @@ import ( "github.com/stretchr/testify/suite" "gitlab.hpi.de/codeocean/codemoon/poseidon/config" "gitlab.hpi.de/codeocean/codemoon/poseidon/tests" + "gitlab.hpi.de/codeocean/codemoon/poseidon/util" "io" "net/url" "regexp" @@ -673,7 +674,7 @@ func (s *ExecuteCommandTestSuite) TestWithSeparateStderr() { }) exitCode, err := s.nomadAPIClient. - ExecuteCommand(s.allocationID, s.ctx, s.testCommandArray, withTTY, nullReader{}, &stdout, &stderr) + ExecuteCommand(s.allocationID, s.ctx, s.testCommandArray, withTTY, util.NullReader{}, &stdout, &stderr) s.Require().NoError(err) s.apiMock.AssertNumberOfCalls(s.T(), "Execute", 2) @@ -704,7 +705,7 @@ func (s *ExecuteCommandTestSuite) TestWithSeparateStderrReturnsCommandError() { s.mockExecute(s.testCommandArray, 1, tests.ErrDefault, func(args mock.Arguments) {}) s.mockExecute(mock.AnythingOfType("[]string"), 1, nil, func(args mock.Arguments) {}) _, err := s.nomadAPIClient. - ExecuteCommand(s.allocationID, s.ctx, s.testCommandArray, withTTY, nullReader{}, io.Discard, io.Discard) + ExecuteCommand(s.allocationID, s.ctx, s.testCommandArray, withTTY, util.NullReader{}, io.Discard, io.Discard) s.Equal(tests.ErrDefault, err) } @@ -726,7 +727,7 @@ func (s *ExecuteCommandTestSuite) TestWithoutSeparateStderr() { }) exitCode, err := s.nomadAPIClient. - ExecuteCommand(s.allocationID, s.ctx, s.testCommandArray, withTTY, nullReader{}, &stdout, &stderr) + ExecuteCommand(s.allocationID, s.ctx, s.testCommandArray, withTTY, util.NullReader{}, &stdout, &stderr) s.Require().NoError(err) s.apiMock.AssertNumberOfCalls(s.T(), "Execute", 1) @@ -739,7 +740,7 @@ func (s *ExecuteCommandTestSuite) TestWithoutSeparateStderrReturnsCommandError() config.Config.Server.InteractiveStderr = false s.mockExecute(s.testCommandArray, 1, tests.ErrDefault, func(args mock.Arguments) {}) _, err := s.nomadAPIClient. - ExecuteCommand(s.allocationID, s.ctx, s.testCommandArray, withTTY, nullReader{}, io.Discard, io.Discard) + ExecuteCommand(s.allocationID, s.ctx, s.testCommandArray, withTTY, util.NullReader{}, io.Discard, io.Discard) s.Equal(tests.ErrDefault, err) } @@ -750,14 +751,3 @@ func (s *ExecuteCommandTestSuite) mockExecute(command interface{}, exitCode int, Run(runFunc). Return(exitCode, err) } - -func TestNullReaderDoesNotReturnImmediately(t *testing.T) { - reader := &nullReader{} - readerReturned := make(chan bool) - go func() { - p := make([]byte, 5) - _, _ = reader.Read(p) - close(readerReturned) - }() - assert.False(t, tests.ChannelReceivesSomething(readerReturned, tests.ShortTimeout)) -} diff --git a/util/util.go b/util/util.go new file mode 100644 index 0000000..300e829 --- /dev/null +++ b/util/util.go @@ -0,0 +1,10 @@ +package util + +// NullReader is a struct that implements the io.Reader interface and returns nothing when reading +// from it. +type NullReader struct{} + +func (r NullReader) Read(_ []byte) (int, error) { + // An empty select blocks forever. + select {} +} diff --git a/util/util_test.go b/util/util_test.go new file mode 100644 index 0000000..fb7fce8 --- /dev/null +++ b/util/util_test.go @@ -0,0 +1,18 @@ +package util + +import ( + "github.com/stretchr/testify/assert" + "gitlab.hpi.de/codeocean/codemoon/poseidon/tests" + "testing" +) + +func TestNullReaderDoesNotReturnImmediately(t *testing.T) { + reader := &NullReader{} + readerReturned := make(chan bool) + go func() { + p := make([]byte, 5) + _, _ = reader.Read(p) + close(readerReturned) + }() + assert.False(t, tests.ChannelReceivesSomething(readerReturned, tests.ShortTimeout)) +}