debian-forge-composer/internal/jobqueue/api.go
Ondřej Budai 0d4479bbcd worker: save result.json in the composer instead of the worker
In the future remote workers will be introduced. Obviously, the remote worker
cannot support the local target. Unfortunately, the current implementation of
storing the osbuild result is dependant on it.

This commit moves the responsibility of storing osbuild result to the
composer process instead of the worker process. The result is transferred from
a worker to a composer using extended HTTP API.
2020-02-05 01:35:50 +01:00

139 lines
3.6 KiB
Go

package jobqueue
import (
"encoding/json"
"log"
"net"
"net/http"
"github.com/osbuild/osbuild-composer/internal/store"
"github.com/google/uuid"
"github.com/julienschmidt/httprouter"
)
type API struct {
logger *log.Logger
store *store.Store
router *httprouter.Router
}
func New(logger *log.Logger, store *store.Store) *API {
api := &API{
logger: logger,
store: store,
}
api.router = httprouter.New()
api.router.RedirectTrailingSlash = false
api.router.RedirectFixedPath = false
api.router.MethodNotAllowed = http.HandlerFunc(methodNotAllowedHandler)
api.router.NotFound = http.HandlerFunc(notFoundHandler)
api.router.POST("/job-queue/v1/jobs", api.addJobHandler)
api.router.PATCH("/job-queue/v1/jobs/:id", api.updateJobHandler)
return api
}
func (api *API) Serve(listener net.Listener) error {
server := http.Server{Handler: api}
err := server.Serve(listener)
if err != nil && err != http.ErrServerClosed {
return err
}
return nil
}
func (api *API) ServeHTTP(writer http.ResponseWriter, request *http.Request) {
if api.logger != nil {
log.Println(request.Method, request.URL.Path)
}
writer.Header().Set("Content-Type", "application/json; charset=utf-8")
api.router.ServeHTTP(writer, request)
}
func methodNotAllowedHandler(writer http.ResponseWriter, request *http.Request) {
writer.WriteHeader(http.StatusMethodNotAllowed)
}
func notFoundHandler(writer http.ResponseWriter, request *http.Request) {
writer.WriteHeader(http.StatusNotFound)
}
func statusResponseOK(writer http.ResponseWriter) {
writer.WriteHeader(http.StatusOK)
}
func statusResponseError(writer http.ResponseWriter, code int, errors ...string) {
writer.WriteHeader(code)
}
func (api *API) addJobHandler(writer http.ResponseWriter, request *http.Request, _ httprouter.Params) {
type requestBody struct {
}
contentType := request.Header["Content-Type"]
if len(contentType) != 1 || contentType[0] != "application/json" {
statusResponseError(writer, http.StatusUnsupportedMediaType)
return
}
var body requestBody
err := json.NewDecoder(request.Body).Decode(&body)
if err != nil {
statusResponseError(writer, http.StatusBadRequest, "invalid request: "+err.Error())
return
}
nextJob := api.store.PopCompose()
writer.WriteHeader(http.StatusCreated)
json.NewEncoder(writer).Encode(Job{
ID: nextJob.ComposeID,
Distro: nextJob.Distro,
Pipeline: nextJob.Pipeline,
Targets: nextJob.Targets,
OutputType: nextJob.OutputType,
})
}
func (api *API) updateJobHandler(writer http.ResponseWriter, request *http.Request, params httprouter.Params) {
contentType := request.Header["Content-Type"]
if len(contentType) != 1 || contentType[0] != "application/json" {
statusResponseError(writer, http.StatusUnsupportedMediaType)
return
}
id, err := uuid.Parse(params.ByName("id"))
if err != nil {
statusResponseError(writer, http.StatusBadRequest, "invalid compose id: "+err.Error())
return
}
var body JobStatus
err = json.NewDecoder(request.Body).Decode(&body)
if err != nil {
statusResponseError(writer, http.StatusBadRequest, "invalid status: "+err.Error())
return
}
err = api.store.UpdateCompose(id, body.Status, body.Image, body.Result)
if err != nil {
switch err.(type) {
case *store.NotFoundError:
statusResponseError(writer, http.StatusNotFound, err.Error())
case *store.NotPendingError:
statusResponseError(writer, http.StatusNotFound, err.Error())
case *store.NotRunningError:
statusResponseError(writer, http.StatusBadRequest, err.Error())
case *store.InvalidRequestError:
statusResponseError(writer, http.StatusBadRequest, err.Error())
}
return
}
statusResponseOK(writer)
}