Files
polyphem/internal/api/environments.go
2024-08-12 10:02:36 +02:00

160 lines
5.1 KiB
Go

package api
import (
"context"
"encoding/json"
"errors"
"fmt"
"github.com/gorilla/mux"
"github.com/openHPI/poseidon/internal/environment"
"github.com/openHPI/poseidon/internal/runner"
"github.com/openHPI/poseidon/pkg/dto"
"github.com/openHPI/poseidon/pkg/logging"
"net/http"
"strconv"
)
const (
executionEnvironmentIDKey = "executionEnvironmentId"
fetchEnvironmentKey = "fetch"
listRouteName = "list"
getRouteName = "get"
createOrUpdateRouteName = "createOrUpdate"
deleteRouteName = "delete"
)
var ErrMissingURLParameter = errors.New("url parameter missing")
type EnvironmentController struct {
manager environment.ManagerHandler
}
type ExecutionEnvironmentsResponse struct {
ExecutionEnvironments []runner.ExecutionEnvironment `json:"executionEnvironments"`
}
func (e *EnvironmentController) ConfigureRoutes(router *mux.Router) {
environmentRouter := router.PathPrefix(EnvironmentsPath).Subrouter()
environmentRouter.HandleFunc("", e.list).Methods(http.MethodGet).Name(listRouteName)
specificEnvironmentRouter := environmentRouter.Path(fmt.Sprintf("/{%s:[0-9]+}", executionEnvironmentIDKey)).Subrouter()
specificEnvironmentRouter.HandleFunc("", e.get).Methods(http.MethodGet).Name(getRouteName)
specificEnvironmentRouter.HandleFunc("", e.createOrUpdate).Methods(http.MethodPut).Name(createOrUpdateRouteName)
specificEnvironmentRouter.HandleFunc("", e.delete).Methods(http.MethodDelete).Name(deleteRouteName)
}
// list returns all information about available execution environments.
func (e *EnvironmentController) list(writer http.ResponseWriter, request *http.Request) {
fetch, err := parseFetchParameter(request)
if err != nil {
writeClientError(writer, err, http.StatusBadRequest, request.Context())
return
}
environments, err := e.manager.List(fetch)
if err != nil {
writeInternalServerError(writer, err, dto.ErrorUnknown, request.Context())
return
}
sendJSON(writer, ExecutionEnvironmentsResponse{environments}, http.StatusOK, request.Context())
}
// get returns all information about the requested execution environment.
func (e *EnvironmentController) get(writer http.ResponseWriter, request *http.Request) {
environmentID, err := parseEnvironmentID(request)
if err != nil {
// This case is never used as the router validates the id format
writeClientError(writer, err, http.StatusBadRequest, request.Context())
return
}
fetch, err := parseFetchParameter(request)
if err != nil {
writeClientError(writer, err, http.StatusBadRequest, request.Context())
return
}
executionEnvironment, err := e.manager.Get(environmentID, fetch)
if errors.Is(err, runner.ErrUnknownExecutionEnvironment) {
writer.WriteHeader(http.StatusNotFound)
return
} else if err != nil {
writeInternalServerError(writer, err, dto.ErrorUnknown, request.Context())
return
}
sendJSON(writer, executionEnvironment, http.StatusOK, request.Context())
}
// delete removes the specified execution environment.
func (e *EnvironmentController) delete(writer http.ResponseWriter, request *http.Request) {
environmentID, err := parseEnvironmentID(request)
if err != nil {
// This case is never used as the router validates the id format
writeClientError(writer, err, http.StatusBadRequest, request.Context())
return
}
found, err := e.manager.Delete(environmentID)
if err != nil {
writeInternalServerError(writer, err, dto.ErrorUnknown, request.Context())
return
} else if !found {
writer.WriteHeader(http.StatusNotFound)
return
}
writer.WriteHeader(http.StatusNoContent)
}
// createOrUpdate creates/updates an execution environment on the executor.
func (e *EnvironmentController) createOrUpdate(writer http.ResponseWriter, request *http.Request) {
req := new(dto.ExecutionEnvironmentRequest)
if err := json.NewDecoder(request.Body).Decode(req); err != nil {
writeClientError(writer, err, http.StatusBadRequest, request.Context())
return
}
environmentID, err := parseEnvironmentID(request)
if err != nil {
writeClientError(writer, err, http.StatusBadRequest, request.Context())
return
}
var created bool
logging.StartSpan("api.env.update", "Create Environment", request.Context(), func(ctx context.Context) {
created, err = e.manager.CreateOrUpdate(environmentID, *req, ctx)
})
if err != nil {
writeInternalServerError(writer, err, dto.ErrorUnknown, request.Context())
}
if created {
writer.WriteHeader(http.StatusCreated)
} else {
writer.WriteHeader(http.StatusNoContent)
}
}
func parseEnvironmentID(request *http.Request) (dto.EnvironmentID, error) {
id, ok := mux.Vars(request)[executionEnvironmentIDKey]
if !ok {
return 0, fmt.Errorf("could not find %s: %w", executionEnvironmentIDKey, ErrMissingURLParameter)
}
environmentID, err := dto.NewEnvironmentID(id)
if err != nil {
return 0, fmt.Errorf("could not update environment: %w", err)
}
return environmentID, nil
}
func parseFetchParameter(request *http.Request) (fetch bool, err error) {
fetchString := request.FormValue(fetchEnvironmentKey)
if fetchString != "" {
fetch, err = strconv.ParseBool(fetchString)
if err != nil {
return false, fmt.Errorf("could not parse fetch parameter: %w", err)
}
}
return fetch, nil
}