shad-go/distbuild/pkg/dist/build.go
2020-04-05 15:00:33 +03:00

119 lines
2.3 KiB
Go

package dist
import (
"context"
"fmt"
"go.uber.org/zap"
"gitlab.com/slon/shad-go/distbuild/pkg/api"
"gitlab.com/slon/shad-go/distbuild/pkg/build"
)
type Build struct {
ID build.ID
Graph *build.Graph
reverseFiles map[string]build.ID
l *zap.Logger
c *Coordinator
uploadDone chan struct{}
}
func NewBuild(graph *build.Graph, c *Coordinator) *Build {
id := build.NewID()
return &Build{
ID: id,
Graph: graph,
reverseFiles: make(map[string]build.ID),
l: c.log.With(zap.String("build_id", id.String())),
c: c,
uploadDone: make(chan struct{}),
}
}
func (b *Build) missingFiles() []build.ID {
var files []build.ID
for id, path := range b.Graph.SourceFiles {
files = append(files, id)
b.reverseFiles[path] = id
}
return files
}
func (b *Build) Run(ctx context.Context, w api.StatusWriter) error {
if err := w.Started(&api.BuildStarted{ID: b.ID, MissingFiles: b.missingFiles()}); err != nil {
return err
}
b.l.Debug("waiting for file upload")
select {
case <-ctx.Done():
return ctx.Err()
case <-b.uploadDone:
}
b.l.Debug("file upload completed")
for _, job := range b.Graph.Jobs {
spec := api.JobSpec{
Job: job,
SourceFiles: make(map[build.ID]string),
Artifacts: make(map[build.ID]api.WorkerID),
}
for _, file := range job.Inputs {
spec.SourceFiles[b.reverseFiles[file]] = file
}
for _, id := range job.Deps {
workerID, ok := b.c.scheduler.LocateArtifact(id)
if !ok {
return fmt.Errorf("artifact %q is missing in cache", id)
}
spec.Artifacts[id] = workerID
}
s := b.c.scheduler.ScheduleJob(&spec)
select {
case <-ctx.Done():
return ctx.Err()
case <-s.Finished:
}
b.l.Debug("job finished", zap.String("job_id", job.ID.String()))
jobFinished := api.StatusUpdate{JobFinished: s.Result}
if err := w.Updated(&jobFinished); err != nil {
return err
}
}
finished := api.StatusUpdate{BuildFinished: &api.BuildFinished{}}
return w.Updated(&finished)
}
func (b *Build) Signal(ctx context.Context, req *api.SignalRequest) (*api.SignalResponse, error) {
switch {
case req.UploadDone != nil:
select {
case <-b.uploadDone:
return nil, fmt.Errorf("upload already done")
default:
close(b.uploadDone)
}
default:
return nil, fmt.Errorf("unexpected signal kind")
}
return &api.SignalResponse{}, nil
}