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:
Ondřej Budai 2020-02-12 14:54:01 +01:00 committed by Tom Gundersen
parent a9633d29e9
commit cc00e0cdc9
6 changed files with 68 additions and 66 deletions

View file

@ -6,7 +6,6 @@ import (
"encoding/json"
"errors"
"fmt"
"github.com/osbuild/osbuild-composer/internal/compose"
"log"
"net"
"net/http"
@ -55,9 +54,9 @@ func (c *ComposerClient) AddJob() (*jobqueue.Job, error) {
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
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)
if err != nil {
return err
@ -84,19 +83,19 @@ func handleJob(client *ComposerClient) error {
return err
}
err = client.UpdateJob(job, common.IBRunning, nil, nil)
err = client.UpdateJob(job, common.IBRunning, nil)
if err != nil {
return err
}
fmt.Printf("Running job %s\n", job.ID.String())
image, result, err := job.Run()
result, err := job.Run()
if err != nil {
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() {

View file

@ -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.
// It contains all the information necessary to generate the inputs for the job, as
// well as the job's state.

View file

@ -94,12 +94,12 @@ func (api *API) addJobHandler(writer http.ResponseWriter, request *http.Request,
writer.WriteHeader(http.StatusCreated)
// FIXME: handle or comment this possible error
_ = json.NewEncoder(writer).Encode(Job{
ID: nextJob.ComposeID,
ID: nextJob.ComposeID,
ImageBuildID: nextJob.ImageBuildID,
Distro: nextJob.Distro,
Pipeline: nextJob.Pipeline,
Targets: nextJob.Targets,
OutputType: nextJob.ImageType,
Distro: nextJob.Distro,
Pipeline: nextJob.Pipeline,
Targets: nextJob.Targets,
OutputType: nextJob.ImageType,
})
}
@ -123,7 +123,7 @@ func (api *API) updateJobHandler(writer http.ResponseWriter, request *http.Reque
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 {
switch err.(type) {
case *store.NotFoundError:

View file

@ -3,7 +3,6 @@ package jobqueue
import (
"encoding/json"
"fmt"
"github.com/osbuild/osbuild-composer/internal/compose"
"io/ioutil"
"os"
"os/exec"
@ -29,7 +28,6 @@ type Job struct {
type JobStatus struct {
Status common.ImageBuildState `json:"status"`
ImageBuildID int `json:"image_build_id"`
Image *compose.Image `json:"image"`
Result *common.ComposeResult `json:"result"`
}
@ -47,11 +45,11 @@ func (e *TargetsError) Error() string {
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"})
d := distros.GetDistro(job.Distro)
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{
@ -60,19 +58,19 @@ func (job *Job) Run() (*compose.Image, *common.ComposeResult, error) {
buildFile, err := ioutil.TempFile("", "osbuild-worker-build-env-*")
if err != nil {
return nil, nil, err
return nil, err
}
// FIXME: how to handle errors in defer?
defer os.Remove(buildFile.Name())
err = json.NewEncoder(buildFile).Encode(build)
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")
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?
defer os.RemoveAll(tmpStore)
@ -87,22 +85,22 @@ func (job *Job) Run() (*compose.Image, *common.ComposeResult, error) {
stdin, err := cmd.StdinPipe()
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()
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()
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)
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
_ = stdin.Close()
@ -110,21 +108,14 @@ func (job *Job) Run() (*compose.Image, *common.ComposeResult, error) {
var result common.ComposeResult
err = json.NewDecoder(stdout).Decode(&result)
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()
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
for _, t := range job.Targets {
@ -143,24 +134,6 @@ func (job *Job) Run() (*compose.Image, *common.ComposeResult, error) {
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:
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 {
return nil, &result, &TargetsError{r}
return &result, &TargetsError{r}
}
return &image, &result, nil
return &result, nil
}
func runCommand(command string, params ...string) error {

View file

@ -683,7 +683,7 @@ func (s *Store) PopJob() Job {
}
// 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 {
// Check that the compose exists
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
if status == common.IBFinished || status == common.IBFailed {
currentCompose.ImageBuilds[imageBuildID].JobFinished = time.Now()
if status == common.IBFinished {
currentCompose.ImageBuilds[imageBuildID].Image = image
}
}
s.Composes[composeID] = currentCompose

View file

@ -12,7 +12,6 @@ import (
"net/http"
"net/url"
"os"
"path/filepath"
"sort"
"strconv"
"strings"
@ -1566,18 +1565,31 @@ func (api *API) composeImageHandler(writer http.ResponseWriter, request *http.Re
return
}
if compose.ImageBuilds[0].Image == nil {
imageBuild := compose.ImageBuilds[0]
localTarget := imageBuild.GetLocalTarget()
if localTarget == nil {
errors := responseError{
ID: "BuildMissingFile",
Msg: fmt.Sprintf("Compose %s doesn't have an image assigned", uuidString),
ID: "BadCompose",
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
}
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 {
errors := responseError{
ID: "BuildMissingFile",
@ -1587,9 +1599,20 @@ func (api *API) composeImageHandler(writer http.ResponseWriter, request *http.Re
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-Type", compose.ImageBuilds[0].Image.Mime)
writer.Header().Set("Content-Length", fmt.Sprintf("%d", compose.ImageBuilds[0].Image.Size))
writer.Header().Set("Content-Type", imageMime)
writer.Header().Set("Content-Length", fmt.Sprintf("%d", stat.Size()))
io.Copy(writer, file)
}