drop the Compose.Image field
Everything that this field contained can be computed in another way: - path: just lookup the local target and read the path from there - mime: can be derived from distribution and compose output type - size: can be derived from the path Therefore it imho doesn't make much sense to store these information multiple times.
This commit is contained in:
parent
a9633d29e9
commit
cc00e0cdc9
6 changed files with 68 additions and 66 deletions
|
|
@ -6,7 +6,6 @@ import (
|
||||||
"encoding/json"
|
"encoding/json"
|
||||||
"errors"
|
"errors"
|
||||||
"fmt"
|
"fmt"
|
||||||
"github.com/osbuild/osbuild-composer/internal/compose"
|
|
||||||
"log"
|
"log"
|
||||||
"net"
|
"net"
|
||||||
"net/http"
|
"net/http"
|
||||||
|
|
@ -55,9 +54,9 @@ func (c *ComposerClient) AddJob() (*jobqueue.Job, error) {
|
||||||
return job, nil
|
return job, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *ComposerClient) UpdateJob(job *jobqueue.Job, status common.ImageBuildState, image *compose.Image, result *common.ComposeResult) error {
|
func (c *ComposerClient) UpdateJob(job *jobqueue.Job, status common.ImageBuildState, result *common.ComposeResult) error {
|
||||||
var b bytes.Buffer
|
var b bytes.Buffer
|
||||||
json.NewEncoder(&b).Encode(&jobqueue.JobStatus{status, job.ImageBuildID, image, result})
|
json.NewEncoder(&b).Encode(&jobqueue.JobStatus{status, job.ImageBuildID, result})
|
||||||
req, err := http.NewRequest("PATCH", "http://localhost/job-queue/v1/jobs/"+job.ID.String(), &b)
|
req, err := http.NewRequest("PATCH", "http://localhost/job-queue/v1/jobs/"+job.ID.String(), &b)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
|
|
@ -84,19 +83,19 @@ func handleJob(client *ComposerClient) error {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
err = client.UpdateJob(job, common.IBRunning, nil, nil)
|
err = client.UpdateJob(job, common.IBRunning, nil)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
fmt.Printf("Running job %s\n", job.ID.String())
|
fmt.Printf("Running job %s\n", job.ID.String())
|
||||||
image, result, err := job.Run()
|
result, err := job.Run()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Printf(" Job failed: %v", err)
|
log.Printf(" Job failed: %v", err)
|
||||||
return client.UpdateJob(job, common.IBFailed, nil, result)
|
return client.UpdateJob(job, common.IBFailed, result)
|
||||||
}
|
}
|
||||||
|
|
||||||
return client.UpdateJob(job, common.IBFinished, image, result)
|
return client.UpdateJob(job, common.IBFinished, result)
|
||||||
}
|
}
|
||||||
|
|
||||||
func main() {
|
func main() {
|
||||||
|
|
|
||||||
|
|
@ -71,6 +71,16 @@ func (ib *ImageBuild) DeepCopy() ImageBuild {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (ib *ImageBuild) GetLocalTarget() *target.LocalTargetOptions {
|
||||||
|
for _, t := range ib.Targets {
|
||||||
|
if localTarget, ok := t.Options.(*target.LocalTargetOptions); ok {
|
||||||
|
return localTarget
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
// A Compose represent the task of building a set of images from a single blueprint.
|
// A Compose represent the task of building a set of images from a single blueprint.
|
||||||
// It contains all the information necessary to generate the inputs for the job, as
|
// It contains all the information necessary to generate the inputs for the job, as
|
||||||
// well as the job's state.
|
// well as the job's state.
|
||||||
|
|
|
||||||
|
|
@ -94,12 +94,12 @@ func (api *API) addJobHandler(writer http.ResponseWriter, request *http.Request,
|
||||||
writer.WriteHeader(http.StatusCreated)
|
writer.WriteHeader(http.StatusCreated)
|
||||||
// FIXME: handle or comment this possible error
|
// FIXME: handle or comment this possible error
|
||||||
_ = json.NewEncoder(writer).Encode(Job{
|
_ = json.NewEncoder(writer).Encode(Job{
|
||||||
ID: nextJob.ComposeID,
|
ID: nextJob.ComposeID,
|
||||||
ImageBuildID: nextJob.ImageBuildID,
|
ImageBuildID: nextJob.ImageBuildID,
|
||||||
Distro: nextJob.Distro,
|
Distro: nextJob.Distro,
|
||||||
Pipeline: nextJob.Pipeline,
|
Pipeline: nextJob.Pipeline,
|
||||||
Targets: nextJob.Targets,
|
Targets: nextJob.Targets,
|
||||||
OutputType: nextJob.ImageType,
|
OutputType: nextJob.ImageType,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -123,7 +123,7 @@ func (api *API) updateJobHandler(writer http.ResponseWriter, request *http.Reque
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
err = api.store.UpdateImageBuildInCompose(id, body.ImageBuildID, body.Status, body.Image, body.Result)
|
err = api.store.UpdateImageBuildInCompose(id, body.ImageBuildID, body.Status, body.Result)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
switch err.(type) {
|
switch err.(type) {
|
||||||
case *store.NotFoundError:
|
case *store.NotFoundError:
|
||||||
|
|
|
||||||
|
|
@ -3,7 +3,6 @@ package jobqueue
|
||||||
import (
|
import (
|
||||||
"encoding/json"
|
"encoding/json"
|
||||||
"fmt"
|
"fmt"
|
||||||
"github.com/osbuild/osbuild-composer/internal/compose"
|
|
||||||
"io/ioutil"
|
"io/ioutil"
|
||||||
"os"
|
"os"
|
||||||
"os/exec"
|
"os/exec"
|
||||||
|
|
@ -29,7 +28,6 @@ type Job struct {
|
||||||
type JobStatus struct {
|
type JobStatus struct {
|
||||||
Status common.ImageBuildState `json:"status"`
|
Status common.ImageBuildState `json:"status"`
|
||||||
ImageBuildID int `json:"image_build_id"`
|
ImageBuildID int `json:"image_build_id"`
|
||||||
Image *compose.Image `json:"image"`
|
|
||||||
Result *common.ComposeResult `json:"result"`
|
Result *common.ComposeResult `json:"result"`
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -47,11 +45,11 @@ func (e *TargetsError) Error() string {
|
||||||
return errString
|
return errString
|
||||||
}
|
}
|
||||||
|
|
||||||
func (job *Job) Run() (*compose.Image, *common.ComposeResult, error) {
|
func (job *Job) Run() (*common.ComposeResult, error) {
|
||||||
distros := distro.NewRegistry([]string{"/etc/osbuild-composer", "/usr/share/osbuild-composer"})
|
distros := distro.NewRegistry([]string{"/etc/osbuild-composer", "/usr/share/osbuild-composer"})
|
||||||
d := distros.GetDistro(job.Distro)
|
d := distros.GetDistro(job.Distro)
|
||||||
if d == nil {
|
if d == nil {
|
||||||
return nil, nil, fmt.Errorf("unknown distro: %s", job.Distro)
|
return nil, fmt.Errorf("unknown distro: %s", job.Distro)
|
||||||
}
|
}
|
||||||
|
|
||||||
build := pipeline.Build{
|
build := pipeline.Build{
|
||||||
|
|
@ -60,19 +58,19 @@ func (job *Job) Run() (*compose.Image, *common.ComposeResult, error) {
|
||||||
|
|
||||||
buildFile, err := ioutil.TempFile("", "osbuild-worker-build-env-*")
|
buildFile, err := ioutil.TempFile("", "osbuild-worker-build-env-*")
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
// FIXME: how to handle errors in defer?
|
// FIXME: how to handle errors in defer?
|
||||||
defer os.Remove(buildFile.Name())
|
defer os.Remove(buildFile.Name())
|
||||||
|
|
||||||
err = json.NewEncoder(buildFile).Encode(build)
|
err = json.NewEncoder(buildFile).Encode(build)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, nil, fmt.Errorf("error encoding build environment: %v", err)
|
return nil, fmt.Errorf("error encoding build environment: %v", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
tmpStore, err := ioutil.TempDir("/var/tmp", "osbuild-store")
|
tmpStore, err := ioutil.TempDir("/var/tmp", "osbuild-store")
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, nil, fmt.Errorf("error setting up osbuild store: %v", err)
|
return nil, fmt.Errorf("error setting up osbuild store: %v", err)
|
||||||
}
|
}
|
||||||
// FIXME: how to handle errors in defer?
|
// FIXME: how to handle errors in defer?
|
||||||
defer os.RemoveAll(tmpStore)
|
defer os.RemoveAll(tmpStore)
|
||||||
|
|
@ -87,22 +85,22 @@ func (job *Job) Run() (*compose.Image, *common.ComposeResult, error) {
|
||||||
|
|
||||||
stdin, err := cmd.StdinPipe()
|
stdin, err := cmd.StdinPipe()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, nil, fmt.Errorf("error setting up stdin for osbuild: %v", err)
|
return nil, fmt.Errorf("error setting up stdin for osbuild: %v", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
stdout, err := cmd.StdoutPipe()
|
stdout, err := cmd.StdoutPipe()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, nil, fmt.Errorf("error setting up stdout for osbuild: %v", err)
|
return nil, fmt.Errorf("error setting up stdout for osbuild: %v", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
err = cmd.Start()
|
err = cmd.Start()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, nil, fmt.Errorf("error starting osbuild: %v", err)
|
return nil, fmt.Errorf("error starting osbuild: %v", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
err = json.NewEncoder(stdin).Encode(job.Pipeline)
|
err = json.NewEncoder(stdin).Encode(job.Pipeline)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, nil, fmt.Errorf("error encoding osbuild pipeline: %v", err)
|
return nil, fmt.Errorf("error encoding osbuild pipeline: %v", err)
|
||||||
}
|
}
|
||||||
// FIXME: handle or comment this possible error
|
// FIXME: handle or comment this possible error
|
||||||
_ = stdin.Close()
|
_ = stdin.Close()
|
||||||
|
|
@ -110,21 +108,14 @@ func (job *Job) Run() (*compose.Image, *common.ComposeResult, error) {
|
||||||
var result common.ComposeResult
|
var result common.ComposeResult
|
||||||
err = json.NewDecoder(stdout).Decode(&result)
|
err = json.NewDecoder(stdout).Decode(&result)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, nil, fmt.Errorf("error decoding osbuild output: %#v", err)
|
return nil, fmt.Errorf("error decoding osbuild output: %#v", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
err = cmd.Wait()
|
err = cmd.Wait()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, &result, err
|
return &result, err
|
||||||
}
|
}
|
||||||
|
|
||||||
filename, mimeType, err := d.FilenameFromType(job.OutputType)
|
|
||||||
if err != nil {
|
|
||||||
return nil, &result, fmt.Errorf("cannot fetch information about output type %s: %v", job.OutputType, err)
|
|
||||||
}
|
|
||||||
|
|
||||||
var image compose.Image
|
|
||||||
|
|
||||||
var r []error
|
var r []error
|
||||||
|
|
||||||
for _, t := range job.Targets {
|
for _, t := range job.Targets {
|
||||||
|
|
@ -143,24 +134,6 @@ func (job *Job) Run() (*compose.Image, *common.ComposeResult, error) {
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
|
||||||
imagePath := options.Location + "/" + filename
|
|
||||||
file, err := os.Open(imagePath)
|
|
||||||
if err != nil {
|
|
||||||
r = append(r, err)
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
|
|
||||||
fileStat, err := file.Stat()
|
|
||||||
if err != nil {
|
|
||||||
return nil, &result, err
|
|
||||||
}
|
|
||||||
|
|
||||||
image = compose.Image{
|
|
||||||
Path: imagePath,
|
|
||||||
Mime: mimeType,
|
|
||||||
Size: uint64(fileStat.Size()),
|
|
||||||
}
|
|
||||||
|
|
||||||
case *target.AWSTargetOptions:
|
case *target.AWSTargetOptions:
|
||||||
|
|
||||||
a, err := awsupload.New(options.Region, options.AccessKeyID, options.SecretAccessKey)
|
a, err := awsupload.New(options.Region, options.AccessKeyID, options.SecretAccessKey)
|
||||||
|
|
@ -192,10 +165,10 @@ func (job *Job) Run() (*compose.Image, *common.ComposeResult, error) {
|
||||||
}
|
}
|
||||||
|
|
||||||
if len(r) > 0 {
|
if len(r) > 0 {
|
||||||
return nil, &result, &TargetsError{r}
|
return &result, &TargetsError{r}
|
||||||
}
|
}
|
||||||
|
|
||||||
return &image, &result, nil
|
return &result, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func runCommand(command string, params ...string) error {
|
func runCommand(command string, params ...string) error {
|
||||||
|
|
|
||||||
|
|
@ -683,7 +683,7 @@ func (s *Store) PopJob() Job {
|
||||||
}
|
}
|
||||||
|
|
||||||
// UpdateImageBuildInCompose sets the status and optionally also the final image.
|
// UpdateImageBuildInCompose sets the status and optionally also the final image.
|
||||||
func (s *Store) UpdateImageBuildInCompose(composeID uuid.UUID, imageBuildID int, status common.ImageBuildState, image *compose.Image, result *common.ComposeResult) error {
|
func (s *Store) UpdateImageBuildInCompose(composeID uuid.UUID, imageBuildID int, status common.ImageBuildState, result *common.ComposeResult) error {
|
||||||
return s.change(func() error {
|
return s.change(func() error {
|
||||||
// Check that the compose exists
|
// Check that the compose exists
|
||||||
currentCompose, exists := s.Composes[composeID]
|
currentCompose, exists := s.Composes[composeID]
|
||||||
|
|
@ -717,9 +717,6 @@ func (s *Store) UpdateImageBuildInCompose(composeID uuid.UUID, imageBuildID int,
|
||||||
// In case the image build is done, store the time and possibly also the image
|
// In case the image build is done, store the time and possibly also the image
|
||||||
if status == common.IBFinished || status == common.IBFailed {
|
if status == common.IBFinished || status == common.IBFailed {
|
||||||
currentCompose.ImageBuilds[imageBuildID].JobFinished = time.Now()
|
currentCompose.ImageBuilds[imageBuildID].JobFinished = time.Now()
|
||||||
if status == common.IBFinished {
|
|
||||||
currentCompose.ImageBuilds[imageBuildID].Image = image
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
s.Composes[composeID] = currentCompose
|
s.Composes[composeID] = currentCompose
|
||||||
|
|
|
||||||
|
|
@ -12,7 +12,6 @@ import (
|
||||||
"net/http"
|
"net/http"
|
||||||
"net/url"
|
"net/url"
|
||||||
"os"
|
"os"
|
||||||
"path/filepath"
|
|
||||||
"sort"
|
"sort"
|
||||||
"strconv"
|
"strconv"
|
||||||
"strings"
|
"strings"
|
||||||
|
|
@ -1566,18 +1565,31 @@ func (api *API) composeImageHandler(writer http.ResponseWriter, request *http.Re
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
if compose.ImageBuilds[0].Image == nil {
|
imageBuild := compose.ImageBuilds[0]
|
||||||
|
localTarget := imageBuild.GetLocalTarget()
|
||||||
|
|
||||||
|
if localTarget == nil {
|
||||||
errors := responseError{
|
errors := responseError{
|
||||||
ID: "BuildMissingFile",
|
ID: "BadCompose",
|
||||||
Msg: fmt.Sprintf("Compose %s doesn't have an image assigned", uuidString),
|
Msg: fmt.Sprintf("Compose %s is ill-formed: it doesn't contain LocalTarget", uuidString),
|
||||||
}
|
}
|
||||||
statusResponseError(writer, http.StatusBadRequest, errors)
|
statusResponseError(writer, http.StatusInternalServerError, errors)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
imageName := filepath.Base(compose.ImageBuilds[0].Image.Path)
|
imageType, _ := imageBuild.ImageType.ToCompatString()
|
||||||
|
imageName, imageMime, err := api.distro.FilenameFromType(imageType)
|
||||||
|
|
||||||
file, err := os.Open(compose.ImageBuilds[0].Image.Path)
|
if err != nil {
|
||||||
|
errors := responseError{
|
||||||
|
ID: "BadCompose",
|
||||||
|
Msg: fmt.Sprintf("Compose %s is ill-formed: output type %v is invalid for distro %s", uuidString, imageBuild.ImageType, api.distro.Name()),
|
||||||
|
}
|
||||||
|
statusResponseError(writer, http.StatusInternalServerError, errors)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
file, err := os.Open(localTarget.Location + "/" + imageName)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
errors := responseError{
|
errors := responseError{
|
||||||
ID: "BuildMissingFile",
|
ID: "BuildMissingFile",
|
||||||
|
|
@ -1587,9 +1599,20 @@ func (api *API) composeImageHandler(writer http.ResponseWriter, request *http.Re
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
stat, err := file.Stat()
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
errors := responseError{
|
||||||
|
ID: "BuildStatFailed",
|
||||||
|
Msg: fmt.Sprintf("Cannot stat image for build %s!", uuidString),
|
||||||
|
}
|
||||||
|
statusResponseError(writer, http.StatusBadRequest, errors)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
writer.Header().Set("Content-Disposition", "attachment; filename="+uuid.String()+"-"+imageName)
|
writer.Header().Set("Content-Disposition", "attachment; filename="+uuid.String()+"-"+imageName)
|
||||||
writer.Header().Set("Content-Type", compose.ImageBuilds[0].Image.Mime)
|
writer.Header().Set("Content-Type", imageMime)
|
||||||
writer.Header().Set("Content-Length", fmt.Sprintf("%d", compose.ImageBuilds[0].Image.Size))
|
writer.Header().Set("Content-Length", fmt.Sprintf("%d", stat.Size()))
|
||||||
|
|
||||||
io.Copy(writer, file)
|
io.Copy(writer, file)
|
||||||
}
|
}
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue