cloudapi: replace chi with echo
standardize middleware by replacing chi with echo
This commit is contained in:
parent
c97420e879
commit
29af662bf8
15 changed files with 144 additions and 2497 deletions
|
|
@ -12,7 +12,7 @@ import (
|
|||
"fmt"
|
||||
"github.com/deepmap/oapi-codegen/pkg/runtime"
|
||||
"github.com/getkin/kin-openapi/openapi3"
|
||||
"github.com/go-chi/chi"
|
||||
"github.com/labstack/echo/v4"
|
||||
"io"
|
||||
"io/ioutil"
|
||||
"net/http"
|
||||
|
|
@ -943,111 +943,113 @@ func ParseGetVersionResponse(rsp *http.Response) (*GetVersionResponse, error) {
|
|||
type ServerInterface interface {
|
||||
// Create compose
|
||||
// (POST /compose)
|
||||
Compose(w http.ResponseWriter, r *http.Request)
|
||||
Compose(ctx echo.Context) error
|
||||
// The status of a compose
|
||||
// (GET /compose/{id})
|
||||
ComposeStatus(w http.ResponseWriter, r *http.Request, id string)
|
||||
ComposeStatus(ctx echo.Context, id string) error
|
||||
// Get the metadata for a compose.
|
||||
// (GET /compose/{id}/metadata)
|
||||
ComposeMetadata(w http.ResponseWriter, r *http.Request, id string)
|
||||
ComposeMetadata(ctx echo.Context, id string) error
|
||||
// get the openapi json specification
|
||||
// (GET /openapi.json)
|
||||
GetOpenapiJson(w http.ResponseWriter, r *http.Request)
|
||||
GetOpenapiJson(ctx echo.Context) error
|
||||
// get the service version
|
||||
// (GET /version)
|
||||
GetVersion(w http.ResponseWriter, r *http.Request)
|
||||
GetVersion(ctx echo.Context) error
|
||||
}
|
||||
|
||||
// ServerInterfaceWrapper converts contexts to parameters.
|
||||
// ServerInterfaceWrapper converts echo contexts to parameters.
|
||||
type ServerInterfaceWrapper struct {
|
||||
Handler ServerInterface
|
||||
}
|
||||
|
||||
// Compose operation middleware
|
||||
func (siw *ServerInterfaceWrapper) Compose(w http.ResponseWriter, r *http.Request) {
|
||||
ctx := r.Context()
|
||||
|
||||
siw.Handler.Compose(w, r.WithContext(ctx))
|
||||
}
|
||||
|
||||
// ComposeStatus operation middleware
|
||||
func (siw *ServerInterfaceWrapper) ComposeStatus(w http.ResponseWriter, r *http.Request) {
|
||||
ctx := r.Context()
|
||||
|
||||
// Compose converts echo context to params.
|
||||
func (w *ServerInterfaceWrapper) Compose(ctx echo.Context) error {
|
||||
var err error
|
||||
|
||||
// Invoke the callback with all the unmarshalled arguments
|
||||
err = w.Handler.Compose(ctx)
|
||||
return err
|
||||
}
|
||||
|
||||
// ComposeStatus converts echo context to params.
|
||||
func (w *ServerInterfaceWrapper) ComposeStatus(ctx echo.Context) error {
|
||||
var err error
|
||||
// ------------- Path parameter "id" -------------
|
||||
var id string
|
||||
|
||||
err = runtime.BindStyledParameter("simple", false, "id", chi.URLParam(r, "id"), &id)
|
||||
err = runtime.BindStyledParameter("simple", false, "id", ctx.Param("id"), &id)
|
||||
if err != nil {
|
||||
http.Error(w, fmt.Sprintf("Invalid format for parameter id: %s", err), http.StatusBadRequest)
|
||||
return
|
||||
return echo.NewHTTPError(http.StatusBadRequest, fmt.Sprintf("Invalid format for parameter id: %s", err))
|
||||
}
|
||||
|
||||
siw.Handler.ComposeStatus(w, r.WithContext(ctx), id)
|
||||
// Invoke the callback with all the unmarshalled arguments
|
||||
err = w.Handler.ComposeStatus(ctx, id)
|
||||
return err
|
||||
}
|
||||
|
||||
// ComposeMetadata operation middleware
|
||||
func (siw *ServerInterfaceWrapper) ComposeMetadata(w http.ResponseWriter, r *http.Request) {
|
||||
ctx := r.Context()
|
||||
|
||||
// ComposeMetadata converts echo context to params.
|
||||
func (w *ServerInterfaceWrapper) ComposeMetadata(ctx echo.Context) error {
|
||||
var err error
|
||||
|
||||
// ------------- Path parameter "id" -------------
|
||||
var id string
|
||||
|
||||
err = runtime.BindStyledParameter("simple", false, "id", chi.URLParam(r, "id"), &id)
|
||||
err = runtime.BindStyledParameter("simple", false, "id", ctx.Param("id"), &id)
|
||||
if err != nil {
|
||||
http.Error(w, fmt.Sprintf("Invalid format for parameter id: %s", err), http.StatusBadRequest)
|
||||
return
|
||||
return echo.NewHTTPError(http.StatusBadRequest, fmt.Sprintf("Invalid format for parameter id: %s", err))
|
||||
}
|
||||
|
||||
siw.Handler.ComposeMetadata(w, r.WithContext(ctx), id)
|
||||
// Invoke the callback with all the unmarshalled arguments
|
||||
err = w.Handler.ComposeMetadata(ctx, id)
|
||||
return err
|
||||
}
|
||||
|
||||
// GetOpenapiJson operation middleware
|
||||
func (siw *ServerInterfaceWrapper) GetOpenapiJson(w http.ResponseWriter, r *http.Request) {
|
||||
ctx := r.Context()
|
||||
// GetOpenapiJson converts echo context to params.
|
||||
func (w *ServerInterfaceWrapper) GetOpenapiJson(ctx echo.Context) error {
|
||||
var err error
|
||||
|
||||
siw.Handler.GetOpenapiJson(w, r.WithContext(ctx))
|
||||
// Invoke the callback with all the unmarshalled arguments
|
||||
err = w.Handler.GetOpenapiJson(ctx)
|
||||
return err
|
||||
}
|
||||
|
||||
// GetVersion operation middleware
|
||||
func (siw *ServerInterfaceWrapper) GetVersion(w http.ResponseWriter, r *http.Request) {
|
||||
ctx := r.Context()
|
||||
// GetVersion converts echo context to params.
|
||||
func (w *ServerInterfaceWrapper) GetVersion(ctx echo.Context) error {
|
||||
var err error
|
||||
|
||||
siw.Handler.GetVersion(w, r.WithContext(ctx))
|
||||
// Invoke the callback with all the unmarshalled arguments
|
||||
err = w.Handler.GetVersion(ctx)
|
||||
return err
|
||||
}
|
||||
|
||||
// Handler creates http.Handler with routing matching OpenAPI spec.
|
||||
func Handler(si ServerInterface) http.Handler {
|
||||
return HandlerFromMux(si, chi.NewRouter())
|
||||
// This is a simple interface which specifies echo.Route addition functions which
|
||||
// are present on both echo.Echo and echo.Group, since we want to allow using
|
||||
// either of them for path registration
|
||||
type EchoRouter interface {
|
||||
CONNECT(path string, h echo.HandlerFunc, m ...echo.MiddlewareFunc) *echo.Route
|
||||
DELETE(path string, h echo.HandlerFunc, m ...echo.MiddlewareFunc) *echo.Route
|
||||
GET(path string, h echo.HandlerFunc, m ...echo.MiddlewareFunc) *echo.Route
|
||||
HEAD(path string, h echo.HandlerFunc, m ...echo.MiddlewareFunc) *echo.Route
|
||||
OPTIONS(path string, h echo.HandlerFunc, m ...echo.MiddlewareFunc) *echo.Route
|
||||
PATCH(path string, h echo.HandlerFunc, m ...echo.MiddlewareFunc) *echo.Route
|
||||
POST(path string, h echo.HandlerFunc, m ...echo.MiddlewareFunc) *echo.Route
|
||||
PUT(path string, h echo.HandlerFunc, m ...echo.MiddlewareFunc) *echo.Route
|
||||
TRACE(path string, h echo.HandlerFunc, m ...echo.MiddlewareFunc) *echo.Route
|
||||
}
|
||||
|
||||
// HandlerFromMux creates http.Handler with routing matching OpenAPI spec based on the provided mux.
|
||||
func HandlerFromMux(si ServerInterface, r chi.Router) http.Handler {
|
||||
// RegisterHandlers adds each server route to the EchoRouter.
|
||||
func RegisterHandlers(router EchoRouter, si ServerInterface) {
|
||||
|
||||
wrapper := ServerInterfaceWrapper{
|
||||
Handler: si,
|
||||
}
|
||||
|
||||
r.Group(func(r chi.Router) {
|
||||
r.Post("/compose", wrapper.Compose)
|
||||
})
|
||||
r.Group(func(r chi.Router) {
|
||||
r.Get("/compose/{id}", wrapper.ComposeStatus)
|
||||
})
|
||||
r.Group(func(r chi.Router) {
|
||||
r.Get("/compose/{id}/metadata", wrapper.ComposeMetadata)
|
||||
})
|
||||
r.Group(func(r chi.Router) {
|
||||
r.Get("/openapi.json", wrapper.GetOpenapiJson)
|
||||
})
|
||||
r.Group(func(r chi.Router) {
|
||||
r.Get("/version", wrapper.GetVersion)
|
||||
})
|
||||
router.POST("/compose", wrapper.Compose)
|
||||
router.GET("/compose/:id", wrapper.ComposeStatus)
|
||||
router.GET("/compose/:id/metadata", wrapper.ComposeMetadata)
|
||||
router.GET("/openapi.json", wrapper.GetOpenapiJson)
|
||||
router.GET("/version", wrapper.GetVersion)
|
||||
|
||||
return r
|
||||
}
|
||||
|
||||
// Base64 encoded, gzipped, json marshaled Swagger object
|
||||
|
|
|
|||
|
|
@ -1,9 +1,8 @@
|
|||
//go:generate go run github.com/deepmap/oapi-codegen/cmd/oapi-codegen --package=cloudapi --generate types,spec,chi-server,client -o openapi.gen.go openapi.yml
|
||||
//go:generate go run github.com/deepmap/oapi-codegen/cmd/oapi-codegen --package=cloudapi --generate types,spec,client,server -o openapi.gen.go openapi.yml
|
||||
|
||||
package cloudapi
|
||||
|
||||
import (
|
||||
"context"
|
||||
"crypto/rand"
|
||||
"encoding/base64"
|
||||
"encoding/json"
|
||||
|
|
@ -13,8 +12,8 @@ import (
|
|||
"net/http"
|
||||
"strings"
|
||||
|
||||
"github.com/go-chi/chi"
|
||||
"github.com/google/uuid"
|
||||
"github.com/labstack/echo/v4"
|
||||
|
||||
"github.com/osbuild/osbuild-composer/internal/blueprint"
|
||||
"github.com/osbuild/osbuild-composer/internal/distro"
|
||||
|
|
@ -37,6 +36,10 @@ type Server struct {
|
|||
|
||||
type contextKey int
|
||||
|
||||
type apiHandlers struct {
|
||||
server *Server
|
||||
}
|
||||
|
||||
// NewServer creates a new cloud server
|
||||
func NewServer(workers *worker.Server, rpmMetadata rpmmd.RPMMD, distros *distroregistry.Registry) *Server {
|
||||
server := &Server{
|
||||
|
|
@ -50,94 +53,87 @@ func NewServer(workers *worker.Server, rpmMetadata rpmmd.RPMMD, distros *distror
|
|||
// Create an http.Handler() for this server, that provides the composer API at
|
||||
// the given path.
|
||||
func (server *Server) Handler(path string, identityFilter []string) http.Handler {
|
||||
r := chi.NewRouter()
|
||||
e := echo.New()
|
||||
|
||||
if len(identityFilter) > 0 {
|
||||
server.identityFilter = identityFilter
|
||||
r.Use(server.VerifyIdentityHeader)
|
||||
e.Use(server.VerifyIdentityHeader)
|
||||
}
|
||||
r.Use(server.IncRequests)
|
||||
r.Route(path, func(r chi.Router) {
|
||||
HandlerFromMux(server, r)
|
||||
})
|
||||
handler := apiHandlers{
|
||||
server: server,
|
||||
}
|
||||
RegisterHandlers(e.Group(path, server.IncRequests), &handler)
|
||||
|
||||
return r
|
||||
return e
|
||||
}
|
||||
|
||||
func (server *Server) VerifyIdentityHeader(next http.Handler) http.Handler {
|
||||
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
||||
func (server *Server) VerifyIdentityHeader(next echo.HandlerFunc) echo.HandlerFunc {
|
||||
return func(c echo.Context) error {
|
||||
const identityHeaderKey contextKey = iota
|
||||
type identityHeader struct {
|
||||
Identity struct {
|
||||
AccountNumber string `json:"account_number"`
|
||||
} `json:"identity"`
|
||||
}
|
||||
|
||||
idHeaderB64 := r.Header["X-Rh-Identity"]
|
||||
if len(idHeaderB64) != 1 {
|
||||
http.Error(w, "Auth header is not present", http.StatusNotFound)
|
||||
return
|
||||
idHeaderB64 := c.Request().Header.Get("X-Rh-Identity")
|
||||
if idHeaderB64 == "" {
|
||||
return echo.NewHTTPError(http.StatusNotFound, "Auth header is not present")
|
||||
}
|
||||
|
||||
b64Result, err := base64.StdEncoding.DecodeString(idHeaderB64[0])
|
||||
b64Result, err := base64.StdEncoding.DecodeString(idHeaderB64)
|
||||
if err != nil {
|
||||
http.Error(w, "Auth header has incorrect format", http.StatusNotFound)
|
||||
return
|
||||
return echo.NewHTTPError(http.StatusNotFound, "Auth header has incorrect format")
|
||||
}
|
||||
|
||||
var idHeader identityHeader
|
||||
err = json.Unmarshal([]byte(strings.TrimSuffix(fmt.Sprintf("%s", b64Result), "\n")), &idHeader)
|
||||
if err != nil {
|
||||
http.Error(w, "Auth header has incorrect format", http.StatusNotFound)
|
||||
return
|
||||
return echo.NewHTTPError(http.StatusNotFound, "Auth header has incorrect format")
|
||||
}
|
||||
|
||||
for _, i := range server.identityFilter {
|
||||
if idHeader.Identity.AccountNumber == i {
|
||||
next.ServeHTTP(w, r.WithContext(context.WithValue(r.Context(), identityHeaderKey, idHeader)))
|
||||
return
|
||||
c.Set("IdentityHeader", idHeader)
|
||||
c.Set("IdentityHeaderKey", identityHeaderKey)
|
||||
return next(c)
|
||||
}
|
||||
}
|
||||
http.Error(w, "Account not allowed", http.StatusNotFound)
|
||||
})
|
||||
return echo.NewHTTPError(http.StatusNotFound, "Account not allowed")
|
||||
}
|
||||
}
|
||||
|
||||
func (s *Server) IncRequests(next http.Handler) http.Handler {
|
||||
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
||||
func (s *Server) IncRequests(next echo.HandlerFunc) echo.HandlerFunc {
|
||||
return func(c echo.Context) error {
|
||||
prometheus.TotalRequests.Inc()
|
||||
if strings.HasSuffix(r.URL.Path, "/compose") {
|
||||
if strings.HasSuffix(c.Path(), "/compose") {
|
||||
prometheus.ComposeRequests.Inc()
|
||||
}
|
||||
next.ServeHTTP(w, r)
|
||||
})
|
||||
return next(c)
|
||||
}
|
||||
}
|
||||
|
||||
// Compose handles a new /compose POST request
|
||||
func (server *Server) Compose(w http.ResponseWriter, r *http.Request) {
|
||||
contentType := r.Header["Content-Type"]
|
||||
func (h *apiHandlers) Compose(ctx echo.Context) error {
|
||||
contentType := ctx.Request().Header["Content-Type"]
|
||||
if len(contentType) != 1 || contentType[0] != "application/json" {
|
||||
http.Error(w, "Only 'application/json' content type is supported", http.StatusUnsupportedMediaType)
|
||||
return
|
||||
return echo.NewHTTPError(http.StatusUnsupportedMediaType, "Only 'application/json' content type is supported")
|
||||
}
|
||||
|
||||
var request ComposeRequest
|
||||
err := json.NewDecoder(r.Body).Decode(&request)
|
||||
err := json.NewDecoder(ctx.Request().Body).Decode(&request)
|
||||
if err != nil {
|
||||
http.Error(w, "Could not parse JSON body", http.StatusBadRequest)
|
||||
return
|
||||
return echo.NewHTTPError(http.StatusBadRequest, "Could not parse request body")
|
||||
}
|
||||
|
||||
distribution := server.distros.GetDistro(request.Distribution)
|
||||
distribution := h.server.distros.GetDistro(request.Distribution)
|
||||
if distribution == nil {
|
||||
http.Error(w, fmt.Sprintf("Unsupported distribution: %s", request.Distribution), http.StatusBadRequest)
|
||||
return
|
||||
return echo.NewHTTPError(http.StatusBadRequest, "Unsupported distribution: %s", request.Distribution)
|
||||
}
|
||||
|
||||
var bp = blueprint.Blueprint{}
|
||||
err = bp.Initialize()
|
||||
if err != nil {
|
||||
http.Error(w, "Unable to initialize blueprint", http.StatusInternalServerError)
|
||||
return
|
||||
return echo.NewHTTPError(http.StatusInternalServerError, "Unable to initialize blueprint")
|
||||
}
|
||||
if request.Customizations != nil && request.Customizations.Packages != nil {
|
||||
for _, p := range *request.Customizations.Packages {
|
||||
|
|
@ -165,13 +161,11 @@ func (server *Server) Compose(w http.ResponseWriter, r *http.Request) {
|
|||
for i, ir := range request.ImageRequests {
|
||||
arch, err := distribution.GetArch(ir.Architecture)
|
||||
if err != nil {
|
||||
http.Error(w, fmt.Sprintf("Unsupported architecture '%s' for distribution '%s'", ir.Architecture, request.Distribution), http.StatusBadRequest)
|
||||
return
|
||||
return echo.NewHTTPError(http.StatusBadRequest, "Unsupported architecture '%s' for distribution '%s'", ir.Architecture, request.Distribution)
|
||||
}
|
||||
imageType, err := arch.GetImageType(ir.ImageType)
|
||||
if err != nil {
|
||||
http.Error(w, fmt.Sprintf("Unsupported image type '%s' for %s/%s", ir.ImageType, ir.Architecture, request.Distribution), http.StatusBadRequest)
|
||||
return
|
||||
return echo.NewHTTPError(http.StatusBadRequest, "Unsupported image type '%s' for %s/%s", ir.ImageType, ir.Architecture, request.Distribution)
|
||||
}
|
||||
repositories := make([]rpmmd.RepoConfig, len(ir.Repositories))
|
||||
for j, repo := range ir.Repositories {
|
||||
|
|
@ -184,15 +178,14 @@ func (server *Server) Compose(w http.ResponseWriter, r *http.Request) {
|
|||
} else if repo.Metalink != nil {
|
||||
repositories[j].Metalink = *repo.Metalink
|
||||
} else {
|
||||
http.Error(w, "Must specify baseurl, mirrorlist, or metalink", http.StatusBadRequest)
|
||||
return
|
||||
return echo.NewHTTPError(http.StatusBadRequest, "Must specify baseurl, mirrorlist, or metalink")
|
||||
}
|
||||
}
|
||||
|
||||
packageSets := imageType.PackageSets(bp)
|
||||
pkgSpecSets := make(map[string][]rpmmd.PackageSpec)
|
||||
for name, packages := range packageSets {
|
||||
pkgs, _, err := server.rpmMetadata.Depsolve(packages, repositories, distribution.ModulePlatformID(), arch.Name())
|
||||
pkgs, _, err := h.server.rpmMetadata.Depsolve(packages, repositories, distribution.ModulePlatformID(), arch.Name())
|
||||
if err != nil {
|
||||
var error_type int
|
||||
switch err.(type) {
|
||||
|
|
@ -204,8 +197,7 @@ func (server *Server) Compose(w http.ResponseWriter, r *http.Request) {
|
|||
case error:
|
||||
error_type = http.StatusInternalServerError
|
||||
}
|
||||
http.Error(w, fmt.Sprintf("Failed to depsolve base packages for %s/%s/%s: %s", ir.ImageType, ir.Architecture, request.Distribution, err), error_type)
|
||||
return
|
||||
return echo.NewHTTPError(error_type, "Failed to depsolve base packages for %s/%s/%s: %s", ir.ImageType, ir.Architecture, request.Distribution, err)
|
||||
}
|
||||
pkgSpecSets[name] = pkgs
|
||||
}
|
||||
|
|
@ -226,8 +218,7 @@ func (server *Server) Compose(w http.ResponseWriter, r *http.Request) {
|
|||
if ostreeOptions == nil || ostreeOptions.Ref == nil {
|
||||
imageOptions.OSTree = distro.OSTreeImageOptions{Ref: imageType.OSTreeRef()}
|
||||
} else if !ostree.VerifyRef(*ostreeOptions.Ref) {
|
||||
http.Error(w, fmt.Sprintf("Invalid OSTree ref: %s", *ostreeOptions.Ref), http.StatusBadRequest)
|
||||
return
|
||||
return echo.NewHTTPError(http.StatusBadRequest, "Invalid OSTree ref: %s", *ostreeOptions.Ref)
|
||||
} else {
|
||||
imageOptions.OSTree = distro.OSTreeImageOptions{Ref: *ostreeOptions.Ref}
|
||||
}
|
||||
|
|
@ -237,8 +228,7 @@ func (server *Server) Compose(w http.ResponseWriter, r *http.Request) {
|
|||
imageOptions.OSTree.URL = *ostreeOptions.Url
|
||||
parent, err = ostree.ResolveRef(imageOptions.OSTree.URL, imageOptions.OSTree.Ref)
|
||||
if err != nil {
|
||||
http.Error(w, fmt.Sprintf("Error resolving OSTree repo %s: %s", imageOptions.OSTree.URL, err), http.StatusBadRequest)
|
||||
return
|
||||
return echo.NewHTTPError(http.StatusBadRequest, "Error resolving OSTree repo %s: %s", imageOptions.OSTree.URL, err)
|
||||
}
|
||||
imageOptions.OSTree.Parent = parent
|
||||
}
|
||||
|
|
@ -269,8 +259,7 @@ func (server *Server) Compose(w http.ResponseWriter, r *http.Request) {
|
|||
|
||||
manifest, err := imageType.Manifest(blueprintCustoms, imageOptions, repositories, pkgSpecSets, manifestSeed)
|
||||
if err != nil {
|
||||
http.Error(w, fmt.Sprintf("Failed to get manifest for for %s/%s/%s: %s", ir.ImageType, ir.Architecture, request.Distribution, err), http.StatusBadRequest)
|
||||
return
|
||||
return echo.NewHTTPError(http.StatusBadRequest, "Failed to get manifest for for %s/%s/%s: %s", ir.ImageType, ir.Architecture, request.Distribution, err)
|
||||
}
|
||||
|
||||
imageRequests[i].manifest = manifest
|
||||
|
|
@ -283,13 +272,11 @@ func (server *Server) Compose(w http.ResponseWriter, r *http.Request) {
|
|||
var awsUploadOptions AWSUploadRequestOptions
|
||||
jsonUploadOptions, err := json.Marshal(uploadRequest.Options)
|
||||
if err != nil {
|
||||
http.Error(w, "Unable to marshal aws upload request", http.StatusInternalServerError)
|
||||
return
|
||||
return echo.NewHTTPError(http.StatusInternalServerError, "Unable to marshal aws upload request")
|
||||
}
|
||||
err = json.Unmarshal(jsonUploadOptions, &awsUploadOptions)
|
||||
if err != nil {
|
||||
http.Error(w, "Unable to unmarshal aws upload request", http.StatusInternalServerError)
|
||||
return
|
||||
return echo.NewHTTPError(http.StatusInternalServerError, "Unable to unmarshal aws upload request")
|
||||
}
|
||||
|
||||
var share []string
|
||||
|
|
@ -317,13 +304,11 @@ func (server *Server) Compose(w http.ResponseWriter, r *http.Request) {
|
|||
var awsS3UploadOptions AWSS3UploadRequestOptions
|
||||
jsonUploadOptions, err := json.Marshal(uploadRequest.Options)
|
||||
if err != nil {
|
||||
http.Error(w, "Unable to marshal aws upload request", http.StatusInternalServerError)
|
||||
return
|
||||
return echo.NewHTTPError(http.StatusInternalServerError, "Unable to unmarshal aws upload request")
|
||||
}
|
||||
err = json.Unmarshal(jsonUploadOptions, &awsS3UploadOptions)
|
||||
if err != nil {
|
||||
http.Error(w, "Unable to unmarshal aws upload request", http.StatusInternalServerError)
|
||||
return
|
||||
return echo.NewHTTPError(http.StatusInternalServerError, "Unable to unmarshal aws upload request")
|
||||
}
|
||||
|
||||
key := fmt.Sprintf("composer-api-%s", uuid.New().String())
|
||||
|
|
@ -342,13 +327,11 @@ func (server *Server) Compose(w http.ResponseWriter, r *http.Request) {
|
|||
var gcpUploadOptions GCPUploadRequestOptions
|
||||
jsonUploadOptions, err := json.Marshal(uploadRequest.Options)
|
||||
if err != nil {
|
||||
http.Error(w, "Unable to marshal gcp upload request", http.StatusInternalServerError)
|
||||
return
|
||||
return echo.NewHTTPError(http.StatusInternalServerError, "Unable to marshal gcp upload request")
|
||||
}
|
||||
err = json.Unmarshal(jsonUploadOptions, &gcpUploadOptions)
|
||||
if err != nil {
|
||||
http.Error(w, "Unable to unmarshal gcp upload request", http.StatusInternalServerError)
|
||||
return
|
||||
return echo.NewHTTPError(http.StatusInternalServerError, "Unable to unmarshal gcp upload request")
|
||||
}
|
||||
|
||||
var share []string
|
||||
|
|
@ -380,13 +363,11 @@ func (server *Server) Compose(w http.ResponseWriter, r *http.Request) {
|
|||
var azureUploadOptions AzureUploadRequestOptions
|
||||
jsonUploadOptions, err := json.Marshal(uploadRequest.Options)
|
||||
if err != nil {
|
||||
http.Error(w, "Unable to marshal azure upload request", http.StatusInternalServerError)
|
||||
return
|
||||
return echo.NewHTTPError(http.StatusInternalServerError, "Unable to marshal azure upload request")
|
||||
}
|
||||
err = json.Unmarshal(jsonUploadOptions, &azureUploadOptions)
|
||||
if err != nil {
|
||||
http.Error(w, "Unable to unmarshal azure upload request", http.StatusInternalServerError)
|
||||
return
|
||||
return echo.NewHTTPError(http.StatusInternalServerError, "Unable to unmarshal azure upload request")
|
||||
}
|
||||
t := target.NewAzureImageTarget(&target.AzureImageTargetOptions{
|
||||
Filename: imageType.Filename(),
|
||||
|
|
@ -405,8 +386,7 @@ func (server *Server) Compose(w http.ResponseWriter, r *http.Request) {
|
|||
|
||||
targets = append(targets, t)
|
||||
} else {
|
||||
http.Error(w, "Unknown upload request type, only 'aws', 'azure' and 'gcp' are supported", http.StatusBadRequest)
|
||||
return
|
||||
return echo.NewHTTPError(http.StatusBadRequest, "Unknown upload request type, only 'aws', 'azure' and 'gcp' are supported")
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -415,51 +395,42 @@ func (server *Server) Compose(w http.ResponseWriter, r *http.Request) {
|
|||
// NOTE: the store currently does not support multi-image composes
|
||||
ir = imageRequests[0]
|
||||
} else {
|
||||
http.Error(w, "Only single-image composes are currently supported", http.StatusBadRequest)
|
||||
return
|
||||
return echo.NewHTTPError(http.StatusBadRequest, "Only single-image composes are currently supported")
|
||||
}
|
||||
|
||||
id, err := server.workers.EnqueueOSBuild(ir.arch, &worker.OSBuildJob{
|
||||
id, err := h.server.workers.EnqueueOSBuild(ir.arch, &worker.OSBuildJob{
|
||||
Manifest: ir.manifest,
|
||||
Targets: targets,
|
||||
Exports: ir.exports,
|
||||
})
|
||||
if err != nil {
|
||||
http.Error(w, "Failed to enqueue manifest", http.StatusInternalServerError)
|
||||
return
|
||||
return echo.NewHTTPError(http.StatusInternalServerError, "Failed to enqueue manifest")
|
||||
}
|
||||
|
||||
var response ComposeResult
|
||||
response.Id = id.String()
|
||||
w.Header().Set("Content-Type", "application/json; charset=utf-8")
|
||||
w.WriteHeader(http.StatusCreated)
|
||||
err = json.NewEncoder(w).Encode(response)
|
||||
if err != nil {
|
||||
panic("Failed to write response")
|
||||
}
|
||||
|
||||
return ctx.JSON(http.StatusOK, response)
|
||||
}
|
||||
|
||||
// ComposeStatus handles a /compose/{id} GET request
|
||||
func (server *Server) ComposeStatus(w http.ResponseWriter, r *http.Request, id string) {
|
||||
func (h *apiHandlers) ComposeStatus(ctx echo.Context, id string) error {
|
||||
jobId, err := uuid.Parse(id)
|
||||
if err != nil {
|
||||
http.Error(w, fmt.Sprintf("Invalid format for parameter id: %s", err), http.StatusBadRequest)
|
||||
return
|
||||
return echo.NewHTTPError(http.StatusBadRequest, "Invalid format for parameter id: %s", err)
|
||||
}
|
||||
|
||||
var result worker.OSBuildJobResult
|
||||
status, _, err := server.workers.JobStatus(jobId, &result)
|
||||
status, _, err := h.server.workers.JobStatus(jobId, &result)
|
||||
if err != nil {
|
||||
http.Error(w, fmt.Sprintf("Job %s not found: %s", id, err), http.StatusNotFound)
|
||||
return
|
||||
return echo.NewHTTPError(http.StatusNotFound, "Job %s not found: %s", id, err)
|
||||
}
|
||||
|
||||
var us *UploadStatus
|
||||
if result.TargetResults != nil {
|
||||
// Only single upload target is allowed, therefore only a single upload target result is allowed as well
|
||||
if len(result.TargetResults) != 1 {
|
||||
http.Error(w, fmt.Sprintf("Job %s returned more upload target results than allowed", id), http.StatusInternalServerError)
|
||||
return
|
||||
return echo.NewHTTPError(http.StatusInternalServerError, "Job %s returned more upload target results than allowed", id)
|
||||
}
|
||||
tr := *result.TargetResults[0]
|
||||
|
||||
|
|
@ -494,8 +465,7 @@ func (server *Server) ComposeStatus(w http.ResponseWriter, r *http.Request, id s
|
|||
ImageName: gcpOptions.ImageName,
|
||||
}
|
||||
default:
|
||||
http.Error(w, fmt.Sprintf("Job %s returned unknown upload target results %s", id, tr.Name), http.StatusInternalServerError)
|
||||
return
|
||||
return echo.NewHTTPError(http.StatusInternalServerError, "Job %s returned unknown upload target results %s", id, tr.Name)
|
||||
}
|
||||
|
||||
us = &UploadStatus{
|
||||
|
|
@ -511,11 +481,7 @@ func (server *Server) ComposeStatus(w http.ResponseWriter, r *http.Request, id s
|
|||
UploadStatus: us,
|
||||
},
|
||||
}
|
||||
w.Header().Set("Content-Type", "application/json; charset=utf-8")
|
||||
err = json.NewEncoder(w).Encode(response)
|
||||
if err != nil {
|
||||
panic("Failed to write response")
|
||||
}
|
||||
return ctx.JSON(http.StatusOK, response)
|
||||
}
|
||||
|
||||
func composeStatusFromJobStatus(js *worker.JobStatus, result *worker.OSBuildJobResult) ImageStatusValue {
|
||||
|
|
@ -541,61 +507,45 @@ func composeStatusFromJobStatus(js *worker.JobStatus, result *worker.OSBuildJobR
|
|||
}
|
||||
|
||||
// GetOpenapiJson handles a /openapi.json GET request
|
||||
func (server *Server) GetOpenapiJson(w http.ResponseWriter, r *http.Request) {
|
||||
func (h *apiHandlers) GetOpenapiJson(ctx echo.Context) error {
|
||||
spec, err := GetSwagger()
|
||||
if err != nil {
|
||||
http.Error(w, "Could not load openapi spec", http.StatusInternalServerError)
|
||||
return
|
||||
}
|
||||
w.Header().Set("Content-Type", "application/json; charset=utf-8")
|
||||
err = json.NewEncoder(w).Encode(spec)
|
||||
if err != nil {
|
||||
panic("Failed to write response")
|
||||
return echo.NewHTTPError(http.StatusInternalServerError, "Could not load openapi spec")
|
||||
}
|
||||
return ctx.JSON(http.StatusOK, spec)
|
||||
}
|
||||
|
||||
// GetVersion handles a /version GET request
|
||||
func (server *Server) GetVersion(w http.ResponseWriter, r *http.Request) {
|
||||
func (h *apiHandlers) GetVersion(ctx echo.Context) error {
|
||||
spec, err := GetSwagger()
|
||||
if err != nil {
|
||||
http.Error(w, "Could not load version", http.StatusInternalServerError)
|
||||
return
|
||||
return echo.NewHTTPError(http.StatusInternalServerError, "Could not load version")
|
||||
}
|
||||
version := Version{spec.Info.Version}
|
||||
w.Header().Set("Content-Type", "application/json; charset=utf-8")
|
||||
err = json.NewEncoder(w).Encode(version)
|
||||
if err != nil {
|
||||
panic("Failed to write response")
|
||||
}
|
||||
return ctx.JSON(http.StatusOK, version)
|
||||
}
|
||||
|
||||
// ComposeMetadata handles a /compose/{id}/metadata GET request
|
||||
func (server *Server) ComposeMetadata(w http.ResponseWriter, r *http.Request, id string) {
|
||||
func (h *apiHandlers) ComposeMetadata(ctx echo.Context, id string) error {
|
||||
jobId, err := uuid.Parse(id)
|
||||
if err != nil {
|
||||
http.Error(w, fmt.Sprintf("Invalid format for parameter id: %s", err), http.StatusBadRequest)
|
||||
return
|
||||
return echo.NewHTTPError(http.StatusBadRequest, "Invalid format for parameter id: %s", err)
|
||||
}
|
||||
|
||||
var result worker.OSBuildJobResult
|
||||
status, _, err := server.workers.JobStatus(jobId, &result)
|
||||
status, _, err := h.server.workers.JobStatus(jobId, &result)
|
||||
if err != nil {
|
||||
http.Error(w, fmt.Sprintf("Job %s not found: %s", id, err), http.StatusNotFound)
|
||||
return
|
||||
return echo.NewHTTPError(http.StatusNotFound, "Job %s not found: %s", id, err)
|
||||
}
|
||||
|
||||
var job worker.OSBuildJob
|
||||
if _, _, _, err = server.workers.Job(jobId, &job); err != nil {
|
||||
http.Error(w, fmt.Sprintf("Job %s not found: %s", id, err), http.StatusNotFound)
|
||||
return
|
||||
if _, _, _, err = h.server.workers.Job(jobId, &job); err != nil {
|
||||
return echo.NewHTTPError(http.StatusNotFound, "Job %s not found: %s", id, err)
|
||||
}
|
||||
|
||||
if status.Finished.IsZero() {
|
||||
// job still running: empty response
|
||||
if err := json.NewEncoder(w).Encode(new(ComposeMetadata)); err != nil {
|
||||
panic("Failed to write response: " + err.Error())
|
||||
}
|
||||
return
|
||||
return ctx.JSON(200, ComposeMetadata{})
|
||||
}
|
||||
|
||||
manifestVer, err := job.Manifest.Version()
|
||||
|
|
@ -663,7 +613,5 @@ func (server *Server) ComposeMetadata(w http.ResponseWriter, r *http.Request, id
|
|||
resp.OstreeCommit = &commitMetadata.Compose.OSTreeCommit
|
||||
}
|
||||
|
||||
if err := json.NewEncoder(w).Encode(resp); err != nil {
|
||||
panic("Failed to write response: " + err.Error())
|
||||
}
|
||||
return ctx.JSON(200, resp)
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue