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

@ -11,6 +11,8 @@ import (
"net"
"net/http"
"net/url"
"os"
"path"
"regexp"
"sort"
"strconv"
@ -41,19 +43,24 @@ type API struct {
logger *log.Logger
router *httprouter.Router
artifactsDir string
compatOutputDir string
}
var ValidBlueprintName = regexp.MustCompile(`^[a-zA-Z0-9._-]+$`)
func New(rpmmd rpmmd.RPMMD, arch distro.Arch, distro distro.Distro, repos []rpmmd.RepoConfig, logger *log.Logger, store *store.Store, workers *worker.Server) *API {
func New(rpmmd rpmmd.RPMMD, arch distro.Arch, distro distro.Distro, repos []rpmmd.RepoConfig, logger *log.Logger, store *store.Store, workers *worker.Server, artifactsDir, compatOutputDir string) *API {
api := &API{
store: store,
workers: workers,
rpmmd: rpmmd,
arch: arch,
distro: distro,
repos: repos,
logger: logger,
store: store,
workers: workers,
rpmmd: rpmmd,
arch: arch,
distro: distro,
repos: repos,
logger: logger,
artifactsDir: artifactsDir,
compatOutputDir: compatOutputDir,
}
api.router = httprouter.New()
@ -191,6 +198,32 @@ func (api *API) getComposeStatus(compose store.Compose) *composeStatus {
}
}
// Opens the image file for `compose`. This looks under `{artifacts}/{jobId}`
// first, and then under `{outputs}/{composeId}/{imageBuildId}` for backwards
// compatibility.
func (api *API) openImageFile(composeId uuid.UUID, compose store.Compose) (io.Reader, int64, error) {
p := path.Join(api.artifactsDir, compose.ImageBuild.JobID.String(), compose.ImageBuild.ImageType.Filename())
f, err := os.Open(p)
if err != nil {
if api.compatOutputDir == "" || !os.IsNotExist(err) {
return nil, 0, err
}
p = path.Join(api.compatOutputDir, composeId.String(), "0")
f, err = os.Open(p)
if err != nil {
return nil, 0, err
}
}
info, err := f.Stat()
if err != nil {
return nil, 0, err
}
return f, info.Size(), nil
}
func verifyRequestVersion(writer http.ResponseWriter, params httprouter.Params, minVersion uint) bool {
versionString := params.ByName("version")
@ -1721,6 +1754,14 @@ func (api *API) composeDeleteHandler(writer http.ResponseWriter, request *http.R
continue
}
// Delete artifacts from jobs and the compat output dir. Ignore
// errors, because there's no point of reporting them to the
// client after the compose itself has already been deleted.
_ = os.RemoveAll(path.Join(api.artifactsDir, compose.ImageBuild.JobID.String()))
if api.compatOutputDir != "" {
_ = os.RemoveAll(path.Join(api.compatOutputDir, id.String()))
}
results = append(results, composeDeleteStatus{id, true})
}
@ -1966,13 +2007,11 @@ func (api *API) composeImageHandler(writer http.ResponseWriter, request *http.Re
imageName := compose.ImageBuild.ImageType.Filename()
imageMime := compose.ImageBuild.ImageType.MIMEType()
reader, fileSize, err := api.store.GetImageBuildImage(uuid)
// TODO: this might return misleading error
reader, fileSize, err := api.openImageFile(uuid, compose)
if err != nil {
errors := responseError{
ID: "BuildMissingFile",
Msg: fmt.Sprintf("Build %s is missing file %s!", uuidString, imageName),
ID: "InternalServerError",
Msg: fmt.Sprintf("Error accessing image file for compose %s: %v", uuid, err),
}
statusResponseError(writer, http.StatusBadRequest, errors)
return

View file

@ -4,6 +4,7 @@ import (
"archive/tar"
"bytes"
"io"
"io/ioutil"
"math/rand"
"net/http"
"net/http/httptest"
@ -37,7 +38,13 @@ func createWeldrAPI(fixtureGenerator rpmmd_mock.FixtureGenerator) (*API, *store.
panic(err)
}
return New(rpm, arch, d, repos, nil, fixture.Store, fixture.Workers), fixture.Store
artifactsDir, err := ioutil.TempDir("", "client_test-")
if err != nil {
panic(err)
}
defer os.RemoveAll(artifactsDir)
return New(rpm, arch, d, repos, nil, fixture.Store, fixture.Workers, artifactsDir, ""), fixture.Store
}
func TestBasic(t *testing.T) {