worker: introduce job artifact directory

The `jobs/:job_id/builds/:build_id/image` route was awkward: the
`:jobid` was actually weldr's compose id and `:build_id` was always `0`.

Change it to `jobs/:job_id/artifacts/:name`, where `:job_id` is now a
job id, and `:name` is the name of the artifact to upload. In the
future, it could support uploading more than one artifact.

This allows removing outputs from `store`, which is now back to being a
pure JSON-store. Take care that `weldr` returns (and deletes) images
from the new (or for backwards compatibility, the old) location.

The `org.osbuild.local` target continues to exist as a marker for the
worker to know whether it should upload artifacts.
This commit is contained in:
Lars Karlitski 2020-05-26 00:55:47 +02:00 committed by Tom Gundersen
parent 8f7a9b3439
commit a1cf3984dc
11 changed files with 122 additions and 146 deletions

View file

@ -118,9 +118,8 @@ func (c *Client) UpdateJob(job *Job, status common.ImageBuildState, result *comm
return nil
}
func (c *Client) UploadImage(composeId uuid.UUID, imageBuildId int, reader io.Reader) error {
// content type doesn't really matter
url := c.createURL(fmt.Sprintf("/job-queue/v1/jobs/%s/builds/%d/image", composeId, imageBuildId))
func (c *Client) UploadImage(job uuid.UUID, name string, reader io.Reader) error {
url := c.createURL(fmt.Sprintf("/job-queue/v1/jobs/%s/artifacts/%s", job, name))
_, err := c.client.Post(url, "application/octet-stream", reader)
return err

View file

@ -8,7 +8,8 @@ import (
"log"
"net"
"net/http"
"strconv"
"os"
"path"
"time"
"github.com/google/uuid"
@ -21,10 +22,10 @@ import (
)
type Server struct {
logger *log.Logger
jobs jobqueue.JobQueue
router *httprouter.Router
imageWriter WriteImageFunc
logger *log.Logger
jobs jobqueue.JobQueue
router *httprouter.Router
artifactsDir string
}
type JobStatus struct {
@ -35,13 +36,11 @@ type JobStatus struct {
Result OSBuildJobResult
}
type WriteImageFunc func(composeID uuid.UUID, imageBuildID int, reader io.Reader) error
func NewServer(logger *log.Logger, jobs jobqueue.JobQueue, imageWriter WriteImageFunc) *Server {
func NewServer(logger *log.Logger, jobs jobqueue.JobQueue, artifactsDir string) *Server {
s := &Server{
logger: logger,
jobs: jobs,
imageWriter: imageWriter,
logger: logger,
jobs: jobs,
artifactsDir: artifactsDir,
}
s.router = httprouter.New()
@ -52,7 +51,7 @@ func NewServer(logger *log.Logger, jobs jobqueue.JobQueue, imageWriter WriteImag
s.router.POST("/job-queue/v1/jobs", s.addJobHandler)
s.router.PATCH("/job-queue/v1/jobs/:job_id", s.updateJobHandler)
s.router.POST("/job-queue/v1/jobs/:job_id/builds/:build_id/image", s.addJobImageHandler)
s.router.POST("/job-queue/v1/jobs/:job_id/artifacts/:name", s.addJobImageHandler)
return s
}
@ -216,18 +215,35 @@ func (s *Server) addJobImageHandler(writer http.ResponseWriter, request *http.Re
return
}
imageBuildId, err := strconv.Atoi(params.ByName("build_id"))
if err != nil {
jsonErrorf(writer, http.StatusBadRequest, "cannot parse image build id: %v", err)
name := params.ByName("name")
if name == "" {
jsonErrorf(writer, http.StatusBadRequest, "invalid artifact name")
return
}
if s.imageWriter == nil {
_, err = io.Copy(ioutil.Discard, request.Body)
} else {
err = s.imageWriter(id, imageBuildId, request.Body)
if s.artifactsDir == "" {
_, err := io.Copy(ioutil.Discard, request.Body)
if err != nil {
jsonErrorf(writer, http.StatusInternalServerError, "error discarding artifact: %v", err)
}
return
}
err = os.Mkdir(path.Join(s.artifactsDir, id.String()), 0700)
if err != nil {
jsonErrorf(writer, http.StatusInternalServerError, "%v", err)
jsonErrorf(writer, http.StatusInternalServerError, "cannot create artifact directory: %v", err)
return
}
f, err := os.Create(path.Join(s.artifactsDir, id.String(), name))
if err != nil {
jsonErrorf(writer, http.StatusInternalServerError, "cannot create artifact file: %v", err)
return
}
_, err = io.Copy(f, request.Body)
if err != nil {
jsonErrorf(writer, http.StatusInternalServerError, "error writing artifact file: %v", err)
return
}
}

View file

@ -36,7 +36,7 @@ func TestErrors(t *testing.T) {
}
for _, c := range cases {
server := worker.NewServer(nil, testjobqueue.New(), nil)
server := worker.NewServer(nil, testjobqueue.New(), "")
test.TestRoute(t, server, false, c.Method, c.Path, c.Body, c.ExpectedStatus, "{}", "message")
}
}
@ -51,7 +51,7 @@ func TestCreate(t *testing.T) {
if err != nil {
t.Fatalf("error getting image type from arch")
}
server := worker.NewServer(nil, testjobqueue.New(), nil)
server := worker.NewServer(nil, testjobqueue.New(), "")
manifest, err := imageType.Manifest(nil, distro.ImageOptions{Size: imageType.Size(0)}, nil, nil, nil)
if err != nil {
@ -75,7 +75,7 @@ func testUpdateTransition(t *testing.T, from, to string, expectedStatus int) {
if err != nil {
t.Fatalf("error getting image type from arch")
}
server := worker.NewServer(nil, testjobqueue.New(), nil)
server := worker.NewServer(nil, testjobqueue.New(), "")
id := uuid.Nil
if from != "VOID" {