Implement routes to list, get and delete execution environments
* #9 Implement routes to list, get and delete execution environments. A refactoring was required to introduce the ExecutionEnvironment interface. * Fix MR comments, linting issues and bug that lead to e2e test failure * Add e2e tests * Add unit tests
This commit is contained in:
@@ -1,9 +1,10 @@
|
||||
package e2e
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
nomadApi "github.com/hashicorp/nomad/api"
|
||||
"github.com/openHPI/poseidon/internal/api"
|
||||
"github.com/openHPI/poseidon/internal/runner"
|
||||
"github.com/openHPI/poseidon/internal/nomad"
|
||||
"github.com/openHPI/poseidon/pkg/dto"
|
||||
"github.com/openHPI/poseidon/tests"
|
||||
"github.com/openHPI/poseidon/tests/helpers"
|
||||
@@ -65,7 +66,213 @@ func TestCreateOrUpdateEnvironment(t *testing.T) {
|
||||
validateJob(t, request)
|
||||
})
|
||||
|
||||
cleanupJobsForEnvironment(t, tests.AnotherEnvironmentIDAsString)
|
||||
deleteEnvironment(t, tests.AnotherEnvironmentIDAsString)
|
||||
}
|
||||
|
||||
func TestListEnvironments(t *testing.T) {
|
||||
path := helpers.BuildURL(api.BasePath, api.EnvironmentsPath)
|
||||
|
||||
t.Run("returns list with one element", func(t *testing.T) {
|
||||
response, err := http.Get(path) //nolint:gosec // because we build this path right above
|
||||
require.NoError(t, err)
|
||||
|
||||
assert.Equal(t, http.StatusOK, response.StatusCode)
|
||||
environmentsArray := assertEnvironmentArrayInResponse(t, response)
|
||||
assert.Equal(t, 1, len(environmentsArray))
|
||||
})
|
||||
|
||||
t.Run("returns list including the default environment", func(t *testing.T) {
|
||||
response, err := http.Get(path) //nolint:gosec // because we build this path right above
|
||||
require.NoError(t, err)
|
||||
require.Equal(t, http.StatusOK, response.StatusCode)
|
||||
|
||||
environmentsArray := assertEnvironmentArrayInResponse(t, response)
|
||||
require.Equal(t, 1, len(environmentsArray))
|
||||
|
||||
assertEnvironment(t, environmentsArray[0], tests.DefaultEnvironmentIDAsInteger)
|
||||
})
|
||||
|
||||
t.Run("Added environments can be retrieved without fetch", func(t *testing.T) {
|
||||
createEnvironment(t, tests.AnotherEnvironmentIDAsString)
|
||||
|
||||
response, err := http.Get(path) //nolint:gosec // because we build this path right above
|
||||
require.NoError(t, err)
|
||||
require.Equal(t, http.StatusOK, response.StatusCode)
|
||||
|
||||
environmentsArray := assertEnvironmentArrayInResponse(t, response)
|
||||
require.Equal(t, 2, len(environmentsArray))
|
||||
foundIDs := parseIDsFromEnvironments(t, environmentsArray)
|
||||
assert.Contains(t, foundIDs, dto.EnvironmentID(tests.AnotherEnvironmentIDAsInteger))
|
||||
})
|
||||
deleteEnvironment(t, tests.AnotherEnvironmentIDAsString)
|
||||
|
||||
t.Run("Added environments can be retrieved with fetch", func(t *testing.T) {
|
||||
// Add environment without Poseidon
|
||||
_, job := helpers.CreateTemplateJob()
|
||||
jobID := nomad.TemplateJobID(tests.AnotherEnvironmentIDAsInteger)
|
||||
job.ID = &jobID
|
||||
job.Name = &jobID
|
||||
_, _, err := nomadClient.Jobs().Register(job, nil)
|
||||
require.NoError(t, err)
|
||||
|
||||
// List without fetch should not include the added environment
|
||||
response, err := http.Get(path) //nolint:gosec // because we build this path right above
|
||||
require.NoError(t, err)
|
||||
require.Equal(t, http.StatusOK, response.StatusCode)
|
||||
environmentsArray := assertEnvironmentArrayInResponse(t, response)
|
||||
require.Equal(t, 1, len(environmentsArray))
|
||||
assertEnvironment(t, environmentsArray[0], tests.DefaultEnvironmentIDAsInteger)
|
||||
|
||||
// List with fetch should include the added environment
|
||||
response, err = http.Get(path + "?fetch=true") //nolint:gosec // because we build this path right above
|
||||
require.NoError(t, err)
|
||||
require.Equal(t, http.StatusOK, response.StatusCode)
|
||||
environmentsArray = assertEnvironmentArrayInResponse(t, response)
|
||||
require.Equal(t, 2, len(environmentsArray))
|
||||
foundIDs := parseIDsFromEnvironments(t, environmentsArray)
|
||||
assert.Contains(t, foundIDs, dto.EnvironmentID(tests.AnotherEnvironmentIDAsInteger))
|
||||
})
|
||||
deleteEnvironment(t, tests.AnotherEnvironmentIDAsString)
|
||||
}
|
||||
|
||||
func TestGetEnvironment(t *testing.T) {
|
||||
t.Run("returns the default environment", func(t *testing.T) {
|
||||
path := helpers.BuildURL(api.BasePath, api.EnvironmentsPath, tests.DefaultEnvironmentIDAsString)
|
||||
response, err := http.Get(path) //nolint:gosec // because we build this path right above
|
||||
require.NoError(t, err)
|
||||
require.Equal(t, http.StatusOK, response.StatusCode)
|
||||
|
||||
environment := getEnvironmentFromResponse(t, response)
|
||||
assertEnvironment(t, environment, tests.DefaultEnvironmentIDAsInteger)
|
||||
})
|
||||
|
||||
t.Run("Added environments can be retrieved without fetch", func(t *testing.T) {
|
||||
createEnvironment(t, tests.AnotherEnvironmentIDAsString)
|
||||
|
||||
path := helpers.BuildURL(api.BasePath, api.EnvironmentsPath, tests.AnotherEnvironmentIDAsString)
|
||||
response, err := http.Get(path) //nolint:gosec // because we build this path right above
|
||||
require.NoError(t, err)
|
||||
require.Equal(t, http.StatusOK, response.StatusCode)
|
||||
|
||||
environment := getEnvironmentFromResponse(t, response)
|
||||
assertEnvironment(t, environment, tests.AnotherEnvironmentIDAsInteger)
|
||||
})
|
||||
deleteEnvironment(t, tests.AnotherEnvironmentIDAsString)
|
||||
|
||||
t.Run("Added environments can be retrieved with fetch", func(t *testing.T) {
|
||||
// Add environment without Poseidon
|
||||
_, job := helpers.CreateTemplateJob()
|
||||
jobID := nomad.TemplateJobID(tests.AnotherEnvironmentIDAsInteger)
|
||||
job.ID = &jobID
|
||||
job.Name = &jobID
|
||||
_, _, err := nomadClient.Jobs().Register(job, nil)
|
||||
require.NoError(t, err)
|
||||
|
||||
// List without fetch should not include the added environment
|
||||
path := helpers.BuildURL(api.BasePath, api.EnvironmentsPath, tests.AnotherEnvironmentIDAsString)
|
||||
response, err := http.Get(path) //nolint:gosec // because we build this path right above
|
||||
require.NoError(t, err)
|
||||
require.Equal(t, http.StatusNotFound, response.StatusCode)
|
||||
|
||||
// List with fetch should include the added environment
|
||||
response, err = http.Get(path + "?fetch=true") //nolint:gosec // because we build this path right above
|
||||
require.NoError(t, err)
|
||||
require.Equal(t, http.StatusOK, response.StatusCode)
|
||||
environment := getEnvironmentFromResponse(t, response)
|
||||
assertEnvironment(t, environment, tests.AnotherEnvironmentIDAsInteger)
|
||||
})
|
||||
deleteEnvironment(t, tests.AnotherEnvironmentIDAsString)
|
||||
}
|
||||
|
||||
func TestDeleteEnvironment(t *testing.T) {
|
||||
t.Run("Removes added environment", func(t *testing.T) {
|
||||
createEnvironment(t, tests.AnotherEnvironmentIDAsString)
|
||||
|
||||
path := helpers.BuildURL(api.BasePath, api.EnvironmentsPath, tests.AnotherEnvironmentIDAsString)
|
||||
response, err := helpers.HTTPDelete(path, nil)
|
||||
assert.NoError(t, err)
|
||||
assert.Equal(t, http.StatusNoContent, response.StatusCode)
|
||||
})
|
||||
|
||||
t.Run("Removes Nomad Job", func(t *testing.T) {
|
||||
createEnvironment(t, tests.AnotherEnvironmentIDAsString)
|
||||
|
||||
// Expect created Nomad job
|
||||
jobID := nomad.TemplateJobID(tests.AnotherEnvironmentIDAsInteger)
|
||||
job, _, err := nomadClient.Jobs().Info(jobID, nil)
|
||||
assert.NoError(t, err)
|
||||
assert.Equal(t, jobID, *job.ID)
|
||||
|
||||
// Delete the job
|
||||
path := helpers.BuildURL(api.BasePath, api.EnvironmentsPath, tests.AnotherEnvironmentIDAsString)
|
||||
response, err := helpers.HTTPDelete(path, nil)
|
||||
assert.NoError(t, err)
|
||||
assert.Equal(t, http.StatusNoContent, response.StatusCode)
|
||||
|
||||
// Expect not to find the Nomad job
|
||||
_, _, err = nomadClient.Jobs().Info(jobID, nil)
|
||||
assert.Error(t, err)
|
||||
})
|
||||
}
|
||||
|
||||
func parseIDsFromEnvironments(t *testing.T, environments []interface{}) (ids []dto.EnvironmentID) {
|
||||
t.Helper()
|
||||
for _, environment := range environments {
|
||||
id, _ := parseEnvironment(t, environment)
|
||||
ids = append(ids, id)
|
||||
}
|
||||
return ids
|
||||
}
|
||||
|
||||
func assertEnvironment(t *testing.T, environment interface{}, expectedID dto.EnvironmentID) {
|
||||
t.Helper()
|
||||
id, defaultEnvironmentParams := parseEnvironment(t, environment)
|
||||
|
||||
assert.Equal(t, expectedID, id)
|
||||
expectedKeys := []string{"prewarmingPoolSize", "cpuLimit", "memoryLimit", "image", "networkAccess", "exposedPorts"}
|
||||
for _, key := range expectedKeys {
|
||||
_, ok := defaultEnvironmentParams[key]
|
||||
assert.True(t, ok)
|
||||
}
|
||||
}
|
||||
|
||||
func parseEnvironment(t *testing.T, environment interface{}) (id dto.EnvironmentID, params map[string]interface{}) {
|
||||
t.Helper()
|
||||
environmentParams, ok := environment.(map[string]interface{})
|
||||
require.True(t, ok)
|
||||
idInterface, ok := environmentParams["id"]
|
||||
require.True(t, ok)
|
||||
idFloat, ok := idInterface.(float64)
|
||||
require.True(t, ok)
|
||||
return dto.EnvironmentID(int(idFloat)), environmentParams
|
||||
}
|
||||
|
||||
func assertEnvironmentArrayInResponse(t *testing.T, response *http.Response) []interface{} {
|
||||
t.Helper()
|
||||
paramMap := make(map[string]interface{})
|
||||
err := json.NewDecoder(response.Body).Decode(¶mMap)
|
||||
require.NoError(t, err)
|
||||
environments, ok := paramMap["executionEnvironments"]
|
||||
assert.True(t, ok)
|
||||
environmentsArray, ok := environments.([]interface{})
|
||||
assert.True(t, ok)
|
||||
return environmentsArray
|
||||
}
|
||||
|
||||
func getEnvironmentFromResponse(t *testing.T, response *http.Response) interface{} {
|
||||
t.Helper()
|
||||
var environment interface{}
|
||||
err := json.NewDecoder(response.Body).Decode(&environment)
|
||||
require.NoError(t, err)
|
||||
return environment
|
||||
}
|
||||
|
||||
//nolint:unparam // Because its more clear if the environment id is written in the real test
|
||||
func deleteEnvironment(t *testing.T, id string) {
|
||||
t.Helper()
|
||||
path := helpers.BuildURL(api.BasePath, api.EnvironmentsPath, id)
|
||||
_, err := helpers.HTTPDelete(path, nil)
|
||||
require.NoError(t, err)
|
||||
}
|
||||
|
||||
func cleanupJobsForEnvironment(t *testing.T, environmentID string) {
|
||||
@@ -84,6 +291,21 @@ func cleanupJobsForEnvironment(t *testing.T, environmentID string) {
|
||||
}
|
||||
}
|
||||
|
||||
//nolint:unparam // Because its more clear if the environment id is written in the real test
|
||||
func createEnvironment(t *testing.T, environmentID string) {
|
||||
t.Helper()
|
||||
path := helpers.BuildURL(api.BasePath, api.EnvironmentsPath, environmentID)
|
||||
request := dto.ExecutionEnvironmentRequest{
|
||||
PrewarmingPoolSize: 1,
|
||||
CPULimit: 100,
|
||||
MemoryLimit: 100,
|
||||
Image: *testDockerImage,
|
||||
NetworkAccess: false,
|
||||
ExposedPorts: nil,
|
||||
}
|
||||
assertPutReturnsStatusAndZeroContent(t, path, request, http.StatusCreated)
|
||||
}
|
||||
|
||||
func assertPutReturnsStatusAndZeroContent(t *testing.T, path string,
|
||||
request dto.ExecutionEnvironmentRequest, status int) {
|
||||
t.Helper()
|
||||
@@ -133,9 +355,9 @@ func validateJob(t *testing.T, expected dto.ExecutionEnvironmentRequest) {
|
||||
}
|
||||
}
|
||||
|
||||
func findTemplateJob(t *testing.T, id runner.EnvironmentID) *nomadApi.Job {
|
||||
func findTemplateJob(t *testing.T, id dto.EnvironmentID) *nomadApi.Job {
|
||||
t.Helper()
|
||||
job, _, err := nomadClient.Jobs().Info(runner.TemplateJobID(id), nil)
|
||||
job, _, err := nomadClient.Jobs().Info(nomad.TemplateJobID(id), nil)
|
||||
if err != nil {
|
||||
t.Fatalf("Error retrieving Nomad job: %v", err)
|
||||
}
|
||||
|
Reference in New Issue
Block a user