2020-03-10 12:08:59 +00:00
|
|
|
package client
|
|
|
|
|
|
|
|
import (
|
|
|
|
"bytes"
|
|
|
|
"context"
|
|
|
|
"encoding/json"
|
|
|
|
"fmt"
|
|
|
|
"io/ioutil"
|
|
|
|
"net/http"
|
|
|
|
|
2020-03-11 22:46:45 +00:00
|
|
|
"go.uber.org/zap"
|
|
|
|
|
2020-03-29 16:03:07 +00:00
|
|
|
"gitlab.com/slon/shad-go/distbuild/pkg/api"
|
2020-03-10 12:08:59 +00:00
|
|
|
"gitlab.com/slon/shad-go/distbuild/pkg/build"
|
|
|
|
)
|
|
|
|
|
|
|
|
type Client struct {
|
|
|
|
CoordinatorEndpoint string
|
2020-03-11 22:46:45 +00:00
|
|
|
SourceDir string
|
|
|
|
Log *zap.Logger
|
2020-03-10 12:08:59 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
type BuildListener interface {
|
|
|
|
OnJobStdout(jobID build.ID, stdout []byte) error
|
2020-03-11 22:46:45 +00:00
|
|
|
OnJobStderr(jobID build.ID, stderr []byte) error
|
2020-03-10 12:08:59 +00:00
|
|
|
|
|
|
|
OnJobFinished(jobID build.ID) error
|
|
|
|
OnJobFailed(jobID build.ID, code int, error string) error
|
|
|
|
}
|
|
|
|
|
2020-03-29 16:03:07 +00:00
|
|
|
func (c *Client) uploadSources(ctx context.Context, src api.BuildStarted) error {
|
2020-03-11 22:46:45 +00:00
|
|
|
return nil
|
2020-03-10 12:08:59 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
func (c *Client) Build(ctx context.Context, graph build.Graph, lsn BuildListener) error {
|
|
|
|
graphJS, err := json.Marshal(graph)
|
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
|
|
|
|
req, err := http.NewRequest("POST", c.CoordinatorEndpoint+"/build", bytes.NewBuffer(graphJS))
|
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
req.Header.Add("Content-Type", "application/json")
|
|
|
|
req = req.WithContext(ctx)
|
|
|
|
|
2020-03-11 22:46:45 +00:00
|
|
|
c.Log.Debug("sending build request", zap.String("url", req.URL.String()))
|
|
|
|
|
2020-03-10 12:08:59 +00:00
|
|
|
rsp, err := http.DefaultClient.Do(req)
|
|
|
|
if err != nil {
|
|
|
|
return fmt.Errorf("build failed: %w", err)
|
|
|
|
}
|
|
|
|
defer rsp.Body.Close()
|
|
|
|
|
|
|
|
if rsp.StatusCode != 200 {
|
|
|
|
errorMsg, _ := ioutil.ReadAll(rsp.Body)
|
|
|
|
return fmt.Errorf("build failed: %s", errorMsg)
|
|
|
|
}
|
|
|
|
|
|
|
|
d := json.NewDecoder(rsp.Body)
|
|
|
|
|
2020-03-29 16:03:07 +00:00
|
|
|
var missing api.BuildStarted
|
2020-03-10 12:08:59 +00:00
|
|
|
if err := d.Decode(&missing); err != nil {
|
2020-03-11 22:46:45 +00:00
|
|
|
return fmt.Errorf("error receiving source list: %w", err)
|
2020-03-10 12:08:59 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
if err := c.uploadSources(ctx, missing); err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
|
|
|
|
for {
|
2020-03-29 16:03:07 +00:00
|
|
|
var update api.StatusUpdate
|
2020-03-10 12:08:59 +00:00
|
|
|
if err := d.Decode(&update); err != nil {
|
2020-03-11 22:46:45 +00:00
|
|
|
return fmt.Errorf("error receiving status update: %w", err)
|
2020-03-10 12:08:59 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
switch {
|
|
|
|
case update.BuildFailed != nil:
|
|
|
|
return fmt.Errorf("build failed: %s", update.BuildFailed.Error)
|
|
|
|
|
2020-03-14 10:24:44 +00:00
|
|
|
case update.BuildFinished != nil:
|
|
|
|
return nil
|
|
|
|
|
2020-03-10 12:08:59 +00:00
|
|
|
case update.JobFinished != nil:
|
|
|
|
jf := update.JobFinished
|
|
|
|
|
|
|
|
if jf.Stdout != nil {
|
|
|
|
if err := lsn.OnJobStdout(jf.ID, jf.Stdout); err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if jf.Stderr != nil {
|
|
|
|
if err := lsn.OnJobStderr(jf.ID, jf.Stderr); err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if jf.Error != nil {
|
|
|
|
if err := lsn.OnJobFailed(jf.ID, jf.ExitCode, *jf.Error); err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
if err := lsn.OnJobFinished(jf.ID); err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
default:
|
|
|
|
return fmt.Errorf("build failed: unexpected status update")
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|