|
Packit |
63bb0d |
package worker
|
|
Packit |
63bb0d |
|
|
Packit |
63bb0d |
import (
|
|
Packit Service |
509fd4 |
"context"
|
|
Packit |
63bb0d |
"encoding/json"
|
|
Packit Service |
509fd4 |
"errors"
|
|
Packit |
63bb0d |
"fmt"
|
|
Packit |
63bb0d |
"io"
|
|
Packit |
63bb0d |
"io/ioutil"
|
|
Packit |
63bb0d |
"log"
|
|
Packit |
63bb0d |
"net/http"
|
|
Packit |
63bb0d |
"os"
|
|
Packit |
63bb0d |
"path"
|
|
Packit Service |
509fd4 |
"sync"
|
|
Packit |
63bb0d |
"time"
|
|
Packit |
63bb0d |
|
|
Packit |
63bb0d |
"github.com/google/uuid"
|
|
Packit Service |
509fd4 |
"github.com/labstack/echo/v4"
|
|
Packit |
63bb0d |
|
|
Packit |
63bb0d |
"github.com/osbuild/osbuild-composer/internal/jobqueue"
|
|
Packit Service |
509fd4 |
"github.com/osbuild/osbuild-composer/internal/worker/api"
|
|
Packit |
63bb0d |
)
|
|
Packit |
63bb0d |
|
|
Packit |
63bb0d |
type Server struct {
|
|
Packit |
63bb0d |
jobs jobqueue.JobQueue
|
|
Packit Service |
509fd4 |
logger *log.Logger
|
|
Packit |
63bb0d |
artifactsDir string
|
|
Packit Service |
509fd4 |
|
|
Packit Service |
509fd4 |
// Currently running jobs. Workers are not handed job ids, but
|
|
Packit Service |
509fd4 |
// independent tokens which serve as an indirection. This enables
|
|
Packit Service |
509fd4 |
// race-free uploading of artifacts and makes restarting composer more
|
|
Packit Service |
509fd4 |
// robust (workers from an old run cannot report results for jobs
|
|
Packit Service |
509fd4 |
// composer thinks are not running).
|
|
Packit Service |
509fd4 |
// This map maps these tokens to job ids. Artifacts are stored in
|
|
Packit Service |
509fd4 |
// `$STATE_DIRECTORY/artifacts/tmp/$TOKEN` while the worker is running,
|
|
Packit Service |
509fd4 |
// and renamed to `$STATE_DIRECTORY/artifacts/$JOB_ID` once the job is
|
|
Packit Service |
509fd4 |
// reported as done.
|
|
Packit Service |
509fd4 |
running map[uuid.UUID]uuid.UUID
|
|
Packit Service |
509fd4 |
runningMutex sync.Mutex
|
|
Packit |
63bb0d |
}
|
|
Packit |
63bb0d |
|
|
Packit |
63bb0d |
type JobStatus struct {
|
|
Packit |
63bb0d |
Queued time.Time
|
|
Packit |
63bb0d |
Started time.Time
|
|
Packit |
63bb0d |
Finished time.Time
|
|
Packit |
63bb0d |
Canceled bool
|
|
Packit |
63bb0d |
}
|
|
Packit |
63bb0d |
|
|
Packit Service |
509fd4 |
var ErrTokenNotExist = errors.New("worker token does not exist")
|
|
Packit Service |
509fd4 |
|
|
Packit |
63bb0d |
func NewServer(logger *log.Logger, jobs jobqueue.JobQueue, artifactsDir string) *Server {
|
|
Packit Service |
509fd4 |
return &Server{
|
|
Packit |
63bb0d |
jobs: jobs,
|
|
Packit Service |
509fd4 |
logger: logger,
|
|
Packit |
63bb0d |
artifactsDir: artifactsDir,
|
|
Packit Service |
509fd4 |
running: make(map[uuid.UUID]uuid.UUID),
|
|
Packit |
63bb0d |
}
|
|
Packit Service |
509fd4 |
}
|
|
Packit |
63bb0d |
|
|
Packit Service |
509fd4 |
func (s *Server) Handler() http.Handler {
|
|
Packit Service |
509fd4 |
e := echo.New()
|
|
Packit Service |
509fd4 |
e.Binder = binder{}
|
|
Packit Service |
509fd4 |
e.StdLogger = s.logger
|
|
Packit |
63bb0d |
|
|
Packit Service |
509fd4 |
// log errors returned from handlers
|
|
Packit Service |
509fd4 |
e.HTTPErrorHandler = func(err error, c echo.Context) {
|
|
Packit Service |
509fd4 |
log.Println(c.Path(), c.QueryParams().Encode(), err.Error())
|
|
Packit Service |
509fd4 |
e.DefaultHTTPErrorHandler(err, c)
|
|
Packit Service |
509fd4 |
}
|
|
Packit |
63bb0d |
|
|
Packit Service |
509fd4 |
handler := apiHandlers{
|
|
Packit Service |
509fd4 |
server: s,
|
|
Packit Service |
509fd4 |
}
|
|
Packit Service |
509fd4 |
api.RegisterHandlers(e.Group(api.BasePath), &handler)
|
|
Packit |
63bb0d |
|
|
Packit Service |
509fd4 |
return e
|
|
Packit |
63bb0d |
}
|
|
Packit |
63bb0d |
|
|
Packit Service |
509fd4 |
func (s *Server) EnqueueOSBuild(arch string, job *OSBuildJob) (uuid.UUID, error) {
|
|
Packit Service |
509fd4 |
return s.jobs.Enqueue("osbuild:"+arch, job, nil)
|
|
Packit Service |
509fd4 |
}
|
|
Packit |
63bb0d |
|
|
Packit Service |
509fd4 |
func (s *Server) EnqueueOSBuildKoji(arch string, job *OSBuildKojiJob, initID uuid.UUID) (uuid.UUID, error) {
|
|
Packit Service |
509fd4 |
return s.jobs.Enqueue("osbuild-koji:"+arch, job, []uuid.UUID{initID})
|
|
Packit |
63bb0d |
}
|
|
Packit |
63bb0d |
|
|
Packit Service |
509fd4 |
func (s *Server) EnqueueKojiInit(job *KojiInitJob) (uuid.UUID, error) {
|
|
Packit Service |
509fd4 |
return s.jobs.Enqueue("koji-init", job, nil)
|
|
Packit Service |
509fd4 |
}
|
|
Packit |
63bb0d |
|
|
Packit Service |
509fd4 |
func (s *Server) EnqueueKojiFinalize(job *KojiFinalizeJob, initID uuid.UUID, buildIDs []uuid.UUID) (uuid.UUID, error) {
|
|
Packit Service |
509fd4 |
return s.jobs.Enqueue("koji-finalize", job, append([]uuid.UUID{initID}, buildIDs...))
|
|
Packit |
63bb0d |
}
|
|
Packit |
63bb0d |
|
|
Packit Service |
509fd4 |
func (s *Server) JobStatus(id uuid.UUID, result interface{}) (*JobStatus, []uuid.UUID, error) {
|
|
Packit Service |
509fd4 |
rawResult, queued, started, finished, canceled, deps, err := s.jobs.JobStatus(id)
|
|
Packit Service |
509fd4 |
if err != nil {
|
|
Packit Service |
509fd4 |
return nil, nil, err
|
|
Packit |
63bb0d |
}
|
|
Packit |
63bb0d |
|
|
Packit Service |
509fd4 |
if !finished.IsZero() && !canceled {
|
|
Packit Service |
509fd4 |
err = json.Unmarshal(rawResult, result)
|
|
Packit Service |
509fd4 |
if err != nil {
|
|
Packit Service |
509fd4 |
return nil, nil, fmt.Errorf("error unmarshaling result for job '%s': %v", id, err)
|
|
Packit Service |
509fd4 |
}
|
|
Packit Service |
509fd4 |
}
|
|
Packit |
63bb0d |
|
|
Packit Service |
509fd4 |
// For backwards compatibility: OSBuildJobResult didn't use to have a
|
|
Packit Service |
509fd4 |
// top-level `Success` flag. Override it here by looking into the job.
|
|
Packit Service |
509fd4 |
if r, ok := result.(*OSBuildJobResult); ok {
|
|
Packit Service |
509fd4 |
if !r.Success && r.OSBuildOutput != nil {
|
|
Packit Service |
509fd4 |
r.Success = r.OSBuildOutput.Success && len(r.TargetErrors) == 0
|
|
Packit |
63bb0d |
}
|
|
Packit |
63bb0d |
}
|
|
Packit |
63bb0d |
|
|
Packit |
63bb0d |
return &JobStatus{
|
|
Packit |
63bb0d |
Queued: queued,
|
|
Packit |
63bb0d |
Started: started,
|
|
Packit |
63bb0d |
Finished: finished,
|
|
Packit |
63bb0d |
Canceled: canceled,
|
|
Packit Service |
509fd4 |
}, deps, nil
|
|
Packit |
63bb0d |
}
|
|
Packit |
63bb0d |
|
|
Packit Service |
3a6627 |
// Job provides access to all the parameters of a job.
|
|
Packit Service |
3a6627 |
func (s *Server) Job(id uuid.UUID, job interface{}) (string, json.RawMessage, []uuid.UUID, error) {
|
|
Packit Service |
3a6627 |
jobType, rawArgs, deps, err := s.jobs.Job(id)
|
|
Packit Service |
3a6627 |
if err != nil {
|
|
Packit Service |
3a6627 |
return "", nil, nil, err
|
|
Packit Service |
3a6627 |
}
|
|
Packit Service |
3a6627 |
|
|
Packit Service |
3a6627 |
if err := json.Unmarshal(rawArgs, job); err != nil {
|
|
Packit Service |
3a6627 |
return "", nil, nil, fmt.Errorf("error unmarshaling arguments for job '%s': %v", id, err)
|
|
Packit Service |
3a6627 |
}
|
|
Packit Service |
3a6627 |
|
|
Packit Service |
3a6627 |
return jobType, rawArgs, deps, nil
|
|
Packit Service |
3a6627 |
}
|
|
Packit Service |
3a6627 |
|
|
Packit |
63bb0d |
func (s *Server) Cancel(id uuid.UUID) error {
|
|
Packit |
63bb0d |
return s.jobs.CancelJob(id)
|
|
Packit |
63bb0d |
}
|
|
Packit |
63bb0d |
|
|
Packit |
63bb0d |
// Provides access to artifacts of a job. Returns an io.Reader for the artifact
|
|
Packit |
63bb0d |
// and the artifact's size.
|
|
Packit |
63bb0d |
func (s *Server) JobArtifact(id uuid.UUID, name string) (io.Reader, int64, error) {
|
|
Packit Service |
509fd4 |
if s.artifactsDir == "" {
|
|
Packit Service |
509fd4 |
return nil, 0, errors.New("Artifacts not enabled")
|
|
Packit Service |
509fd4 |
}
|
|
Packit Service |
509fd4 |
|
|
Packit Service |
509fd4 |
status, _, err := s.JobStatus(id, &json.RawMessage{})
|
|
Packit |
63bb0d |
if err != nil {
|
|
Packit |
63bb0d |
return nil, 0, err
|
|
Packit |
63bb0d |
}
|
|
Packit |
63bb0d |
|
|
Packit |
63bb0d |
if status.Finished.IsZero() {
|
|
Packit |
63bb0d |
return nil, 0, fmt.Errorf("Cannot access artifacts before job is finished: %s", id)
|
|
Packit |
63bb0d |
}
|
|
Packit |
63bb0d |
|
|
Packit |
63bb0d |
p := path.Join(s.artifactsDir, id.String(), name)
|
|
Packit |
63bb0d |
f, err := os.Open(p)
|
|
Packit |
63bb0d |
if err != nil {
|
|
Packit |
63bb0d |
return nil, 0, fmt.Errorf("Error accessing artifact %s for job %s: %v", name, id, err)
|
|
Packit |
63bb0d |
}
|
|
Packit |
63bb0d |
|
|
Packit |
63bb0d |
info, err := f.Stat()
|
|
Packit |
63bb0d |
if err != nil {
|
|
Packit |
63bb0d |
return nil, 0, fmt.Errorf("Error getting size of artifact %s for job %s: %v", name, id, err)
|
|
Packit |
63bb0d |
}
|
|
Packit |
63bb0d |
|
|
Packit |
63bb0d |
return f, info.Size(), nil
|
|
Packit |
63bb0d |
}
|
|
Packit |
63bb0d |
|
|
Packit |
63bb0d |
// Deletes all artifacts for job `id`.
|
|
Packit |
63bb0d |
func (s *Server) DeleteArtifacts(id uuid.UUID) error {
|
|
Packit Service |
509fd4 |
if s.artifactsDir == "" {
|
|
Packit Service |
509fd4 |
return errors.New("Artifacts not enabled")
|
|
Packit Service |
509fd4 |
}
|
|
Packit Service |
509fd4 |
|
|
Packit Service |
509fd4 |
status, _, err := s.JobStatus(id, &json.RawMessage{})
|
|
Packit |
63bb0d |
if err != nil {
|
|
Packit |
63bb0d |
return err
|
|
Packit |
63bb0d |
}
|
|
Packit |
63bb0d |
|
|
Packit |
63bb0d |
if status.Finished.IsZero() {
|
|
Packit |
63bb0d |
return fmt.Errorf("Cannot delete artifacts before job is finished: %s", id)
|
|
Packit |
63bb0d |
}
|
|
Packit |
63bb0d |
|
|
Packit |
63bb0d |
return os.RemoveAll(path.Join(s.artifactsDir, id.String()))
|
|
Packit |
63bb0d |
}
|
|
Packit |
63bb0d |
|
|
Packit Service |
509fd4 |
func (s *Server) RequestJob(ctx context.Context, arch string, jobTypes []string) (uuid.UUID, uuid.UUID, string, json.RawMessage, []json.RawMessage, error) {
|
|
Packit Service |
509fd4 |
token := uuid.New()
|
|
Packit |
63bb0d |
|
|
Packit Service |
509fd4 |
// treat osbuild jobs specially until we have found a generic way to
|
|
Packit Service |
509fd4 |
// specify dequeuing restrictions. For now, we only have one
|
|
Packit Service |
509fd4 |
// restriction: arch for osbuild jobs.
|
|
Packit Service |
509fd4 |
jts := []string{}
|
|
Packit Service |
509fd4 |
for _, t := range jobTypes {
|
|
Packit Service |
509fd4 |
if t == "osbuild" || t == "osbuild-koji" {
|
|
Packit Service |
509fd4 |
t = t + ":" + arch
|
|
Packit Service |
509fd4 |
}
|
|
Packit Service |
509fd4 |
jts = append(jts, t)
|
|
Packit Service |
509fd4 |
}
|
|
Packit Service |
509fd4 |
|
|
Packit Service |
509fd4 |
jobId, depIDs, jobType, args, err := s.jobs.Dequeue(ctx, jts)
|
|
Packit Service |
509fd4 |
if err != nil {
|
|
Packit Service |
509fd4 |
return uuid.Nil, uuid.Nil, "", nil, nil, err
|
|
Packit Service |
509fd4 |
}
|
|
Packit Service |
509fd4 |
|
|
Packit Service |
509fd4 |
var dynamicArgs []json.RawMessage
|
|
Packit Service |
509fd4 |
for _, depID := range depIDs {
|
|
Packit Service |
509fd4 |
result, _, _, _, _, _, _ := s.jobs.JobStatus(depID)
|
|
Packit Service |
509fd4 |
dynamicArgs = append(dynamicArgs, result)
|
|
Packit Service |
509fd4 |
}
|
|
Packit Service |
509fd4 |
|
|
Packit Service |
509fd4 |
if s.artifactsDir != "" {
|
|
Packit Service |
509fd4 |
err := os.MkdirAll(path.Join(s.artifactsDir, "tmp", token.String()), 0700)
|
|
Packit Service |
509fd4 |
if err != nil {
|
|
Packit Service |
509fd4 |
return uuid.Nil, uuid.Nil, "", nil, nil, fmt.Errorf("cannot create artifact directory: %v", err)
|
|
Packit Service |
509fd4 |
}
|
|
Packit Service |
509fd4 |
}
|
|
Packit Service |
509fd4 |
|
|
Packit Service |
509fd4 |
s.runningMutex.Lock()
|
|
Packit Service |
509fd4 |
defer s.runningMutex.Unlock()
|
|
Packit Service |
509fd4 |
s.running[token] = jobId
|
|
Packit Service |
509fd4 |
|
|
Packit Service |
509fd4 |
if jobType == "osbuild:"+arch {
|
|
Packit Service |
509fd4 |
jobType = "osbuild"
|
|
Packit Service |
509fd4 |
} else if jobType == "osbuild-koji:"+arch {
|
|
Packit Service |
509fd4 |
jobType = "osbuild-koji"
|
|
Packit Service |
509fd4 |
}
|
|
Packit Service |
509fd4 |
|
|
Packit Service |
509fd4 |
return token, jobId, jobType, args, dynamicArgs, nil
|
|
Packit |
63bb0d |
}
|
|
Packit |
63bb0d |
|
|
Packit Service |
509fd4 |
func (s *Server) RunningJob(token uuid.UUID) (uuid.UUID, error) {
|
|
Packit Service |
509fd4 |
s.runningMutex.Lock()
|
|
Packit Service |
509fd4 |
defer s.runningMutex.Unlock()
|
|
Packit Service |
509fd4 |
|
|
Packit Service |
509fd4 |
jobId, ok := s.running[token]
|
|
Packit Service |
509fd4 |
if !ok {
|
|
Packit Service |
509fd4 |
return uuid.Nil, ErrTokenNotExist
|
|
Packit Service |
509fd4 |
}
|
|
Packit Service |
509fd4 |
|
|
Packit Service |
509fd4 |
return jobId, nil
|
|
Packit |
63bb0d |
}
|
|
Packit |
63bb0d |
|
|
Packit Service |
509fd4 |
func (s *Server) FinishJob(token uuid.UUID, result json.RawMessage) error {
|
|
Packit Service |
509fd4 |
s.runningMutex.Lock()
|
|
Packit Service |
509fd4 |
defer s.runningMutex.Unlock()
|
|
Packit Service |
509fd4 |
|
|
Packit Service |
509fd4 |
jobId, ok := s.running[token]
|
|
Packit Service |
509fd4 |
if !ok {
|
|
Packit Service |
509fd4 |
return ErrTokenNotExist
|
|
Packit Service |
509fd4 |
}
|
|
Packit Service |
509fd4 |
|
|
Packit Service |
509fd4 |
// Always delete the running job, even if there are errors finishing
|
|
Packit Service |
509fd4 |
// the job, because callers won't call this a second time on error.
|
|
Packit Service |
509fd4 |
delete(s.running, token)
|
|
Packit Service |
509fd4 |
|
|
Packit Service |
509fd4 |
err := s.jobs.FinishJob(jobId, result)
|
|
Packit Service |
509fd4 |
if err != nil {
|
|
Packit Service |
509fd4 |
return fmt.Errorf("error finishing job: %v", err)
|
|
Packit Service |
509fd4 |
}
|
|
Packit Service |
509fd4 |
|
|
Packit Service |
509fd4 |
// Move artifacts from the temporary location to the final job
|
|
Packit Service |
509fd4 |
// location. Log any errors, but do not treat them as fatal. The job is
|
|
Packit Service |
509fd4 |
// already finished.
|
|
Packit Service |
509fd4 |
if s.artifactsDir != "" {
|
|
Packit Service |
509fd4 |
err := os.Rename(path.Join(s.artifactsDir, "tmp", token.String()), path.Join(s.artifactsDir, jobId.String()))
|
|
Packit Service |
509fd4 |
if err != nil {
|
|
Packit Service |
509fd4 |
log.Printf("Error moving artifacts for job%s: %v", jobId, err)
|
|
Packit Service |
509fd4 |
}
|
|
Packit Service |
509fd4 |
}
|
|
Packit Service |
509fd4 |
|
|
Packit Service |
509fd4 |
return nil
|
|
Packit |
63bb0d |
}
|
|
Packit |
63bb0d |
|
|
Packit Service |
509fd4 |
// apiHandlers implements api.ServerInterface - the http api route handlers
|
|
Packit Service |
509fd4 |
// generated from api/openapi.yml. This is a separate object, because these
|
|
Packit Service |
509fd4 |
// handlers should not be exposed on the `Server` object.
|
|
Packit Service |
509fd4 |
type apiHandlers struct {
|
|
Packit Service |
509fd4 |
server *Server
|
|
Packit Service |
509fd4 |
}
|
|
Packit |
63bb0d |
|
|
Packit Service |
509fd4 |
func (h *apiHandlers) GetStatus(ctx echo.Context) error {
|
|
Packit Service |
509fd4 |
return ctx.JSON(http.StatusOK, &statusResponse{
|
|
Packit |
63bb0d |
Status: "OK",
|
|
Packit |
63bb0d |
})
|
|
Packit |
63bb0d |
}
|
|
Packit |
63bb0d |
|
|
Packit Service |
509fd4 |
func (h *apiHandlers) RequestJob(ctx echo.Context) error {
|
|
Packit Service |
509fd4 |
var body api.RequestJobJSONRequestBody
|
|
Packit Service |
509fd4 |
err := ctx.Bind(&body)
|
|
Packit |
63bb0d |
if err != nil {
|
|
Packit Service |
509fd4 |
return err
|
|
Packit |
63bb0d |
}
|
|
Packit |
63bb0d |
|
|
Packit Service |
509fd4 |
token, jobId, jobType, jobArgs, dynamicJobArgs, err := h.server.RequestJob(ctx.Request().Context(), body.Arch, body.Types)
|
|
Packit |
63bb0d |
if err != nil {
|
|
Packit Service |
509fd4 |
return err
|
|
Packit |
63bb0d |
}
|
|
Packit |
63bb0d |
|
|
Packit Service |
509fd4 |
return ctx.JSON(http.StatusCreated, requestJobResponse{
|
|
Packit Service |
509fd4 |
Id: jobId,
|
|
Packit Service |
509fd4 |
Location: fmt.Sprintf("%s/jobs/%v", api.BasePath, token),
|
|
Packit Service |
509fd4 |
ArtifactLocation: fmt.Sprintf("%s/jobs/%v/artifacts/", api.BasePath, token),
|
|
Packit Service |
509fd4 |
Type: jobType,
|
|
Packit Service |
509fd4 |
Args: jobArgs,
|
|
Packit Service |
509fd4 |
DynamicArgs: dynamicJobArgs,
|
|
Packit |
63bb0d |
})
|
|
Packit |
63bb0d |
}
|
|
Packit |
63bb0d |
|
|
Packit Service |
509fd4 |
func (h *apiHandlers) GetJob(ctx echo.Context, tokenstr string) error {
|
|
Packit Service |
509fd4 |
token, err := uuid.Parse(tokenstr)
|
|
Packit Service |
509fd4 |
if err != nil {
|
|
Packit Service |
509fd4 |
return echo.NewHTTPError(http.StatusBadRequest, "cannot parse job token")
|
|
Packit |
63bb0d |
}
|
|
Packit |
63bb0d |
|
|
Packit Service |
509fd4 |
jobId, err := h.server.RunningJob(token)
|
|
Packit |
63bb0d |
if err != nil {
|
|
Packit Service |
509fd4 |
switch err {
|
|
Packit Service |
509fd4 |
case ErrTokenNotExist:
|
|
Packit Service |
509fd4 |
return echo.NewHTTPError(http.StatusNotFound, "not found")
|
|
Packit Service |
509fd4 |
default:
|
|
Packit Service |
509fd4 |
return err
|
|
Packit Service |
509fd4 |
}
|
|
Packit |
63bb0d |
}
|
|
Packit |
63bb0d |
|
|
Packit Service |
509fd4 |
if jobId == uuid.Nil {
|
|
Packit Service |
509fd4 |
return ctx.JSON(http.StatusOK, getJobResponse{})
|
|
Packit Service |
509fd4 |
}
|
|
Packit Service |
509fd4 |
|
|
Packit Service |
509fd4 |
status, _, err := h.server.JobStatus(jobId, &json.RawMessage{})
|
|
Packit |
63bb0d |
if err != nil {
|
|
Packit Service |
509fd4 |
return err
|
|
Packit |
63bb0d |
}
|
|
Packit |
63bb0d |
|
|
Packit Service |
509fd4 |
return ctx.JSON(http.StatusOK, getJobResponse{
|
|
Packit Service |
509fd4 |
Canceled: status.Canceled,
|
|
Packit |
63bb0d |
})
|
|
Packit |
63bb0d |
}
|
|
Packit |
63bb0d |
|
|
Packit Service |
509fd4 |
func (h *apiHandlers) UpdateJob(ctx echo.Context, idstr string) error {
|
|
Packit Service |
509fd4 |
token, err := uuid.Parse(idstr)
|
|
Packit |
63bb0d |
if err != nil {
|
|
Packit Service |
509fd4 |
return echo.NewHTTPError(http.StatusBadRequest, "cannot parse job token")
|
|
Packit |
63bb0d |
}
|
|
Packit |
63bb0d |
|
|
Packit |
63bb0d |
var body updateJobRequest
|
|
Packit Service |
509fd4 |
err = ctx.Bind(&body)
|
|
Packit |
63bb0d |
if err != nil {
|
|
Packit Service |
509fd4 |
return err
|
|
Packit |
63bb0d |
}
|
|
Packit |
63bb0d |
|
|
Packit Service |
509fd4 |
err = h.server.FinishJob(token, body.Result)
|
|
Packit |
63bb0d |
if err != nil {
|
|
Packit |
63bb0d |
switch err {
|
|
Packit Service |
509fd4 |
case ErrTokenNotExist:
|
|
Packit Service |
509fd4 |
return echo.NewHTTPError(http.StatusNotFound, "not found")
|
|
Packit |
63bb0d |
default:
|
|
Packit Service |
509fd4 |
return err
|
|
Packit |
63bb0d |
}
|
|
Packit |
63bb0d |
}
|
|
Packit |
63bb0d |
|
|
Packit Service |
509fd4 |
return ctx.JSON(http.StatusOK, updateJobResponse{})
|
|
Packit |
63bb0d |
}
|
|
Packit |
63bb0d |
|
|
Packit Service |
509fd4 |
func (h *apiHandlers) UploadJobArtifact(ctx echo.Context, tokenstr string, name string) error {
|
|
Packit Service |
509fd4 |
token, err := uuid.Parse(tokenstr)
|
|
Packit |
63bb0d |
if err != nil {
|
|
Packit Service |
509fd4 |
return echo.NewHTTPError(http.StatusBadRequest, "cannot parse job token")
|
|
Packit |
63bb0d |
}
|
|
Packit |
63bb0d |
|
|
Packit Service |
509fd4 |
request := ctx.Request()
|
|
Packit |
63bb0d |
|
|
Packit Service |
509fd4 |
if h.server.artifactsDir == "" {
|
|
Packit |
63bb0d |
_, err := io.Copy(ioutil.Discard, request.Body)
|
|
Packit |
63bb0d |
if err != nil {
|
|
Packit Service |
509fd4 |
return fmt.Errorf("error discarding artifact: %v", err)
|
|
Packit |
63bb0d |
}
|
|
Packit Service |
509fd4 |
return ctx.NoContent(http.StatusOK)
|
|
Packit |
63bb0d |
}
|
|
Packit |
63bb0d |
|
|
Packit Service |
509fd4 |
f, err := os.Create(path.Join(h.server.artifactsDir, "tmp", token.String(), name))
|
|
Packit |
63bb0d |
if err != nil {
|
|
Packit Service |
509fd4 |
return fmt.Errorf("cannot create artifact file: %v", err)
|
|
Packit |
63bb0d |
}
|
|
Packit |
63bb0d |
|
|
Packit Service |
509fd4 |
_, err = io.Copy(f, request.Body)
|
|
Packit |
63bb0d |
if err != nil {
|
|
Packit Service |
509fd4 |
return fmt.Errorf("error writing artifact file: %v", err)
|
|
Packit |
63bb0d |
}
|
|
Packit |
63bb0d |
|
|
Packit Service |
509fd4 |
return ctx.NoContent(http.StatusOK)
|
|
Packit Service |
509fd4 |
}
|
|
Packit Service |
509fd4 |
|
|
Packit Service |
509fd4 |
// A simple echo.Binder(), which only accepts application/json, but is more
|
|
Packit Service |
509fd4 |
// strict than echo's DefaultBinder. It does not handle binding query
|
|
Packit Service |
509fd4 |
// parameters either.
|
|
Packit Service |
509fd4 |
type binder struct{}
|
|
Packit Service |
509fd4 |
|
|
Packit Service |
509fd4 |
func (b binder) Bind(i interface{}, ctx echo.Context) error {
|
|
Packit Service |
509fd4 |
request := ctx.Request()
|
|
Packit Service |
509fd4 |
|
|
Packit Service |
509fd4 |
contentType := request.Header["Content-Type"]
|
|
Packit Service |
509fd4 |
if len(contentType) != 1 || contentType[0] != "application/json" {
|
|
Packit Service |
509fd4 |
return echo.NewHTTPError(http.StatusUnsupportedMediaType, "request must be json-encoded")
|
|
Packit Service |
509fd4 |
}
|
|
Packit Service |
509fd4 |
|
|
Packit Service |
509fd4 |
err := json.NewDecoder(request.Body).Decode(i)
|
|
Packit |
63bb0d |
if err != nil {
|
|
Packit Service |
509fd4 |
return echo.NewHTTPError(http.StatusBadRequest, "cannot parse request body: "+err.Error())
|
|
Packit |
63bb0d |
}
|
|
Packit Service |
509fd4 |
|
|
Packit Service |
509fd4 |
return nil
|
|
Packit |
63bb0d |
}
|