Files
poseidon/internal/runner/aws_runner_test.go
Maximilian Paß bfb5977d24 Destroy runner on allocation stopped
Destroying the runner when Nomad informs us about its allocation being stopped, fixes the error of executions running into their timeout even if the allocation was stopped long ago.
2023-07-21 15:30:21 +02:00

154 lines
5.2 KiB
Go

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"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
"io"
"net/http"
"net/http/httptest"
"strings"
"testing"
"time"
)
func TestAWSExecutionRequestIsStored(t *testing.T) {
environment := &ExecutionEnvironmentMock{}
environment.On("ID").Return(dto.EnvironmentID(tests.DefaultEnvironmentIDAsInteger))
r, err := NewAWSFunctionWorkload(environment, nil)
assert.NoError(t, err)
executionRequest := &dto.ExecutionRequest{
Command: "command",
TimeLimit: 10,
Environment: nil,
}
r.StoreExecution(tests.DefaultEnvironmentIDAsString, executionRequest)
assert.True(t, r.ExecutionExists(tests.DefaultEnvironmentIDAsString))
storedExecutionRunner, ok := r.executions.Pop(tests.DefaultEnvironmentIDAsString)
assert.True(t, ok, "Getting an execution should not return ok false")
assert.Equal(t, executionRequest, storedExecutionRunner)
}
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 TestAWSFunctionWorkload_ExecuteInteractively(t *testing.T) {
environment := &ExecutionEnvironmentMock{}
environment.On("ID").Return(dto.EnvironmentID(tests.DefaultEnvironmentIDAsInteger))
environment.On("Image").Return("testImage or AWS endpoint")
r, err := NewAWSFunctionWorkload(environment, nil)
require.NoError(t, err)
var cancel context.CancelFunc
awsMock := &awsEndpointMock{}
s := httptest.NewServer(http.HandlerFunc(awsMock.handler))
t.Run("establishes WebSocket connection to AWS endpoint", func(t *testing.T) {
// Convert http://127.0.0.1 to ws://127.0.0.1
config.Config.AWS.Endpoint = "ws" + strings.TrimPrefix(s.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, context.Background())
require.NoError(t, err)
<-exit
assert.True(t, awsMock.hasConnected)
})
t.Run("sends execution request", func(t *testing.T) {
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, context.Background())
require.NoError(t, 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":{}}`
assert.Equal(t, expectedRequestData, awsMock.receivedData)
})
}
func TestAWSFunctionWorkload_UpdateFileSystem(t *testing.T) {
environment := &ExecutionEnvironmentMock{}
environment.On("ID").Return(dto.EnvironmentID(tests.DefaultEnvironmentIDAsInteger))
environment.On("Image").Return("testImage or AWS endpoint")
r, err := NewAWSFunctionWorkload(environment, nil)
require.NoError(t, err)
var cancel context.CancelFunc
awsMock := &awsEndpointMock{}
s := httptest.NewServer(http.HandlerFunc(awsMock.handler))
// Convert http://127.0.0.1 to ws://127.0.0.1
config.Config.AWS.Endpoint = "ws" + strings.TrimPrefix(s.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}}, context.Background())
assert.NoError(t, err)
_, execCancel, err := r.ExecuteInteractively(
tests.DefaultEnvironmentIDAsString, nil, io.Discard, io.Discard, context.Background())
require.NoError(t, 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) + `"}}`
assert.Equal(t, expectedRequestData, awsMock.receivedData)
}
func TestAWSFunctionWorkload_Destroy(t *testing.T) {
environment := &ExecutionEnvironmentMock{}
environment.On("ID").Return(dto.EnvironmentID(tests.DefaultEnvironmentIDAsInteger))
hasDestroyBeenCalled := false
r, err := NewAWSFunctionWorkload(environment, func(_ Runner) error {
hasDestroyBeenCalled = true
return nil
})
require.NoError(t, err)
err = r.Destroy(false)
assert.NoError(t, err)
assert.True(t, hasDestroyBeenCalled)
}