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:
parent
8f7a9b3439
commit
a1cf3984dc
11 changed files with 122 additions and 146 deletions
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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) {
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue