shad-go/distbuild/pkg/client/build.go

125 lines
2.8 KiB
Go
Raw Normal View History

2020-03-10 12:08:59 +00:00
package client
import (
"context"
"fmt"
2020-03-29 16:24:18 +00:00
"io"
2020-04-04 21:21:55 +00:00
"path/filepath"
2020-03-10 12:08:59 +00:00
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"
2020-04-04 20:11:21 +00:00
"gitlab.com/slon/shad-go/distbuild/pkg/filecache"
2020-03-10 12:08:59 +00:00
)
type Client struct {
2020-03-29 16:24:18 +00:00
l *zap.Logger
2020-04-04 21:13:45 +00:00
client *api.BuildClient
2020-04-04 20:11:21 +00:00
cache *filecache.Client
2020-03-29 16:24:18 +00:00
sourceDir string
}
func NewClient(
l *zap.Logger,
apiEndpoint string,
sourceDir string,
) *Client {
return &Client{
l: l,
2020-04-04 21:13:45 +00:00
client: api.NewBuildClient(l, apiEndpoint),
2020-04-04 20:11:21 +00:00
cache: filecache.NewClient(l, apiEndpoint),
2020-03-29 16:24:18 +00:00
sourceDir: sourceDir,
}
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-04-04 20:11:21 +00:00
func (c *Client) uploadSources(ctx context.Context, graph *build.Graph, started *api.BuildStarted) error {
for _, id := range started.MissingFiles {
c.l.Debug("uploading missing file to coordinator", zap.String("id", id.String()))
path, ok := graph.SourceFiles[id]
if !ok {
return fmt.Errorf("file is missing in build graph: id=%s", id)
}
2020-04-04 21:21:55 +00:00
absPath := filepath.Join(c.sourceDir, path)
if err := c.cache.Upload(ctx, id, absPath); err != nil {
2020-04-04 20:11:21 +00:00
return err
}
}
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 {
2020-03-29 16:24:18 +00:00
started, r, err := c.client.StartBuild(ctx, &api.BuildRequest{Graph: graph})
2020-03-10 12:08:59 +00:00
if err != nil {
return err
}
2020-03-29 16:24:18 +00:00
c.l.Debug("build started", zap.String("build_id", started.ID.String()))
2020-04-04 20:11:21 +00:00
if err := c.uploadSources(ctx, &graph, started); err != nil {
2020-03-10 12:08:59 +00:00
return err
}
2020-04-04 21:13:45 +00:00
uploadDone := &api.SignalRequest{UploadDone: &api.UploadDone{}}
_, err = c.client.SignalBuild(ctx, started.ID, uploadDone)
if err != nil {
return err
}
2020-03-10 12:08:59 +00:00
for {
2020-03-29 16:24:18 +00:00
u, err := r.Next()
if err == io.EOF {
return fmt.Errorf("unexpected end of status stream")
} else if err != nil {
return err
2020-03-10 12:08:59 +00:00
}
2020-03-29 16:24:18 +00:00
c.l.Debug("received status update", zap.String("build_id", started.ID.String()), zap.Any("update", u))
2020-03-10 12:08:59 +00:00
switch {
2020-03-29 16:24:18 +00:00
case u.BuildFailed != nil:
return fmt.Errorf("build failed: %s", u.BuildFailed.Error)
2020-03-10 12:08:59 +00:00
2020-03-29 16:24:18 +00:00
case u.BuildFinished != nil:
2020-03-14 10:24:44 +00:00
return nil
2020-03-29 16:24:18 +00:00
case u.JobFinished != nil:
jf := u.JobFinished
2020-03-10 12:08:59 +00:00
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")
}
}
}