package runner import ( "context" "encoding/base64" "github.com/gorilla/websocket" "github.com/openHPI/poseidon/internal/config" "github.com/openHPI/poseidon/pkg/dto" "github.com/openHPI/poseidon/tests" "io" "net/http" "net/http/httptest" "strings" "time" ) func (s *MainTestSuite) TestAWSExecutionRequestIsStored() { environment := &ExecutionEnvironmentMock{} environment.On("ID").Return(dto.EnvironmentID(tests.DefaultEnvironmentIDAsInteger)) r, err := NewAWSFunctionWorkload(environment, func(_ Runner) error { return nil }) s.NoError(err) executionRequest := &dto.ExecutionRequest{ Command: "command", TimeLimit: 10, Environment: nil, } r.StoreExecution(tests.DefaultEnvironmentIDAsString, executionRequest) s.True(r.ExecutionExists(tests.DefaultEnvironmentIDAsString)) storedExecutionRunner, ok := r.executions.Pop(tests.DefaultEnvironmentIDAsString) s.True(ok, "Getting an execution should not return ok false") s.Equal(executionRequest, storedExecutionRunner) err = r.Destroy(nil) s.NoError(err) } type awsEndpointMock struct { hasConnected bool ctx context.Context receivedData string } func (a *awsEndpointMock) handler(w http.ResponseWriter, r *http.Request) { upgrader := websocket.Upgrader{} c, err := upgrader.Upgrade(w, r, nil) if err != nil { return } defer c.Close() a.hasConnected = true for a.ctx.Err() == nil { _, message, err := c.ReadMessage() if err != nil { break } a.receivedData = string(message) } } func (s *MainTestSuite) TestAWSFunctionWorkload_ExecuteInteractively() { environment := &ExecutionEnvironmentMock{} environment.On("ID").Return(dto.EnvironmentID(tests.DefaultEnvironmentIDAsInteger)) environment.On("Image").Return("testImage or AWS endpoint") r, err := NewAWSFunctionWorkload(environment, func(_ Runner) error { return nil }) s.Require().NoError(err) var cancel context.CancelFunc awsMock := &awsEndpointMock{} sv := httptest.NewServer(http.HandlerFunc(awsMock.handler)) defer sv.Close() s.Run("establishes WebSocket connection to AWS endpoint", func() { // Convert http://127.0.0.1 to ws://127.0.0.1 config.Config.AWS.Endpoint = "ws" + strings.TrimPrefix(sv.URL, "http") awsMock.ctx, cancel = context.WithCancel(context.Background()) cancel() r.StoreExecution(tests.DefaultEnvironmentIDAsString, &dto.ExecutionRequest{}) exit, _, err := r.ExecuteInteractively( tests.DefaultEnvironmentIDAsString, nil, io.Discard, io.Discard, s.TestCtx) s.Require().NoError(err) <-exit s.True(awsMock.hasConnected) }) s.Run("sends execution request", func() { s.T().Skip("The AWS runner ignores its context for executions and waits infinetly for the exit message.") // ToDo awsMock.ctx, cancel = context.WithTimeout(context.Background(), tests.ShortTimeout) defer cancel() command := "sl" request := &dto.ExecutionRequest{Command: command} r.StoreExecution(tests.DefaultEnvironmentIDAsString, request) _, cancel, err := r.ExecuteInteractively( tests.DefaultEnvironmentIDAsString, nil, io.Discard, io.Discard, s.TestCtx) s.Require().NoError(err) <-time.After(tests.ShortTimeout) cancel() expectedRequestData := `{"action":"` + environment.Image() + `","cmd":["/bin/bash","-c","env CODEOCEAN=true /bin/bash -c \"unset \\\"\\${!AWS@}\\\" \u0026\u0026 ` + command + `\""],"files":{}}` s.Equal(expectedRequestData, awsMock.receivedData) }) err = r.Destroy(nil) s.NoError(err) } func (s *MainTestSuite) TestAWSFunctionWorkload_UpdateFileSystem() { s.T().Skip("The AWS runner ignores its context for executions and waits infinetly for the exit message.") // ToDo environment := &ExecutionEnvironmentMock{} environment.On("ID").Return(dto.EnvironmentID(tests.DefaultEnvironmentIDAsInteger)) environment.On("Image").Return("testImage or AWS endpoint") r, err := NewAWSFunctionWorkload(environment, nil) s.Require().NoError(err) var cancel context.CancelFunc awsMock := &awsEndpointMock{} sv := httptest.NewServer(http.HandlerFunc(awsMock.handler)) defer sv.Close() // Convert http://127.0.0.1 to ws://127.0.0.1 config.Config.AWS.Endpoint = "ws" + strings.TrimPrefix(sv.URL, "http") awsMock.ctx, cancel = context.WithTimeout(context.Background(), tests.ShortTimeout) defer cancel() command := "sl" request := &dto.ExecutionRequest{Command: command} r.StoreExecution(tests.DefaultEnvironmentIDAsString, request) myFile := dto.File{Path: "myPath", Content: []byte("myContent")} err = r.UpdateFileSystem(&dto.UpdateFileSystemRequest{Copy: []dto.File{myFile}}, s.TestCtx) s.NoError(err) _, execCancel, err := r.ExecuteInteractively( tests.DefaultEnvironmentIDAsString, nil, io.Discard, io.Discard, s.TestCtx) s.Require().NoError(err) <-time.After(tests.ShortTimeout) execCancel() expectedRequestData := `{"action":"` + environment.Image() + `","cmd":["/bin/bash","-c","env CODEOCEAN=true /bin/bash -c \"unset \\\"\\${!AWS@}\\\" \u0026\u0026 ` + command + `\""],"files":{"` + string(myFile.Path) + `":"` + base64.StdEncoding.EncodeToString(myFile.Content) + `"}}` s.Equal(expectedRequestData, awsMock.receivedData) err = r.Destroy(nil) s.NoError(err) } func (s *MainTestSuite) TestAWSFunctionWorkload_Destroy() { environment := &ExecutionEnvironmentMock{} environment.On("ID").Return(dto.EnvironmentID(tests.DefaultEnvironmentIDAsInteger)) hasDestroyBeenCalled := false r, err := NewAWSFunctionWorkload(environment, func(_ Runner) error { hasDestroyBeenCalled = true return nil }) s.Require().NoError(err) var reason error err = r.Destroy(reason) s.NoError(err) s.True(hasDestroyBeenCalled) }