debian-forge-composer/cmd/osbuild-worker/main.go
Ondřej Budai 6902f730cb worker: upload local target image using jobqueue api
Prior this commit local target copied the image from a worker to a composer
using cp(1) command. This prevented the local target to work on remote
workers.

This commit switches the local target implementation to using the jobqueue
API introduced in the previous commit. I had some concerns about speed
of this solution (imho nothing can beat pure cp(1) implementation) but
ad hoc sanity tests showed the copying of the image using the jobqueue API
when running the worker on the same machine as the composer is still
more or less instant.
2020-02-14 11:53:38 +01:00

118 lines
2.7 KiB
Go

package main
import (
"bytes"
"context"
"encoding/json"
"errors"
"fmt"
"io"
"log"
"net"
"net/http"
"github.com/osbuild/osbuild-composer/internal/common"
"github.com/osbuild/osbuild-composer/internal/jobqueue"
)
type ComposerClient struct {
client *http.Client
}
func NewClient() *ComposerClient {
client := &http.Client{
Transport: &http.Transport{
DialContext: func(context context.Context, network, addr string) (net.Conn, error) {
return net.Dial("unix", "/run/osbuild-composer/job.socket")
},
},
}
return &ComposerClient{client}
}
func (c *ComposerClient) AddJob() (*jobqueue.Job, error) {
type request struct {
}
var b bytes.Buffer
json.NewEncoder(&b).Encode(request{})
response, err := c.client.Post("http://localhost/job-queue/v1/jobs", "application/json", &b)
if err != nil {
return nil, err
}
defer response.Body.Close()
if response.StatusCode != http.StatusCreated {
return nil, errors.New("couldn't create job")
}
job := &jobqueue.Job{}
err = json.NewDecoder(response.Body).Decode(job)
if err != nil {
return nil, err
}
return job, nil
}
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, result})
url := fmt.Sprintf("http://localhost/job-queue/v1/jobs/%s/builds/%d", job.ID.String(), job.ImageBuildID)
req, err := http.NewRequest("PATCH", url, &b)
if err != nil {
return err
}
req.Header.Set("Content-Type", "application/json")
response, err := c.client.Do(req)
if err != nil {
return err
}
defer response.Body.Close()
if response.StatusCode != http.StatusOK {
return errors.New("error setting job status")
}
return nil
}
func (c *ComposerClient) UploadImage(job *jobqueue.Job, reader io.Reader) error {
// content type doesn't really matter
url := fmt.Sprintf("http://localhost/job-queue/v1/jobs/%s/builds/%d/image", job.ID.String(), job.ImageBuildID)
_, err := c.client.Post(url, "application/octet-stream", reader)
return err
}
func handleJob(client *ComposerClient) error {
fmt.Println("Waiting for a new job...")
job, err := client.AddJob()
if err != nil {
return err
}
err = client.UpdateJob(job, common.IBRunning, nil)
if err != nil {
return err
}
fmt.Printf("Running job %s\n", job.ID.String())
result, err := job.Run(client)
if err != nil {
log.Printf(" Job failed: %v", err)
return client.UpdateJob(job, common.IBFailed, result)
}
return client.UpdateJob(job, common.IBFinished, result)
}
func main() {
client := NewClient()
for {
if err := handleJob(client); err != nil {
log.Fatalf("Failed to handle job: " + err.Error())
}
}
}