2017-06-09 01:16:19 +00:00
|
|
|
package client
|
|
|
|
|
|
|
|
import (
|
|
|
|
"context"
|
|
|
|
"crypto/rand"
|
|
|
|
"encoding/hex"
|
|
|
|
"io"
|
2017-06-29 22:31:08 +00:00
|
|
|
"time"
|
2017-06-09 01:16:19 +00:00
|
|
|
|
2017-06-22 20:15:46 +00:00
|
|
|
controlapi "github.com/moby/buildkit/api/services/control"
|
|
|
|
"github.com/moby/buildkit/client/llb"
|
2017-06-09 01:16:19 +00:00
|
|
|
"github.com/pkg/errors"
|
2017-06-14 00:15:55 +00:00
|
|
|
"golang.org/x/sync/errgroup"
|
2017-06-09 01:16:19 +00:00
|
|
|
)
|
|
|
|
|
2017-06-15 23:34:42 +00:00
|
|
|
func (c *Client) Solve(ctx context.Context, r io.Reader, statusChan chan *SolveStatus) error {
|
2017-06-09 01:16:19 +00:00
|
|
|
def, err := llb.ReadFrom(r)
|
|
|
|
if err != nil {
|
|
|
|
return errors.Wrap(err, "failed to parse input")
|
|
|
|
}
|
|
|
|
|
|
|
|
if len(def) == 0 {
|
|
|
|
return errors.New("invalid empty definition")
|
|
|
|
}
|
|
|
|
|
2017-06-14 00:15:55 +00:00
|
|
|
ref := generateID()
|
|
|
|
eg, ctx := errgroup.WithContext(ctx)
|
|
|
|
|
2017-06-29 22:31:08 +00:00
|
|
|
statusContext, cancelStatus := context.WithCancel(context.Background())
|
|
|
|
|
2017-06-14 00:15:55 +00:00
|
|
|
eg.Go(func() error {
|
2017-06-29 22:31:08 +00:00
|
|
|
defer func() { // make sure the Status ends cleanly on build errors
|
|
|
|
go func() {
|
|
|
|
<-time.After(3 * time.Second)
|
|
|
|
cancelStatus()
|
|
|
|
}()
|
|
|
|
}()
|
2017-06-14 00:15:55 +00:00
|
|
|
_, err = c.controlClient().Solve(ctx, &controlapi.SolveRequest{
|
|
|
|
Ref: ref,
|
|
|
|
Definition: def,
|
|
|
|
})
|
|
|
|
if err != nil {
|
|
|
|
return errors.Wrap(err, "failed to solve")
|
|
|
|
}
|
|
|
|
return nil
|
2017-06-09 01:16:19 +00:00
|
|
|
})
|
2017-06-14 00:15:55 +00:00
|
|
|
|
|
|
|
eg.Go(func() error {
|
2017-06-29 22:31:08 +00:00
|
|
|
stream, err := c.controlClient().Status(statusContext, &controlapi.StatusRequest{
|
2017-06-14 00:15:55 +00:00
|
|
|
Ref: ref,
|
|
|
|
})
|
|
|
|
if err != nil {
|
|
|
|
return errors.Wrap(err, "failed to get status")
|
|
|
|
}
|
|
|
|
for {
|
|
|
|
resp, err := stream.Recv()
|
|
|
|
if err != nil {
|
|
|
|
if err == io.EOF {
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
return errors.Wrap(err, "failed to receive status")
|
|
|
|
}
|
2017-06-15 23:34:42 +00:00
|
|
|
s := SolveStatus{}
|
|
|
|
for _, v := range resp.Vertexes {
|
|
|
|
s.Vertexes = append(s.Vertexes, &Vertex{
|
|
|
|
Digest: v.Digest,
|
|
|
|
Inputs: v.Inputs,
|
|
|
|
Name: v.Name,
|
|
|
|
Started: v.Started,
|
|
|
|
Completed: v.Completed,
|
2017-06-29 22:31:08 +00:00
|
|
|
Error: v.Error,
|
2017-06-15 23:34:42 +00:00
|
|
|
})
|
|
|
|
}
|
|
|
|
for _, v := range resp.Statuses {
|
|
|
|
s.Statuses = append(s.Statuses, &VertexStatus{
|
|
|
|
ID: v.ID,
|
|
|
|
Vertex: v.Vertex,
|
|
|
|
Name: v.Name,
|
|
|
|
Total: v.Total,
|
|
|
|
Current: v.Current,
|
|
|
|
Timestamp: v.Timestamp,
|
|
|
|
Started: v.Started,
|
|
|
|
Completed: v.Completed,
|
|
|
|
})
|
|
|
|
}
|
2017-06-19 05:24:41 +00:00
|
|
|
for _, v := range resp.Logs {
|
|
|
|
s.Logs = append(s.Logs, &VertexLog{
|
|
|
|
Vertex: v.Vertex,
|
|
|
|
Stream: int(v.Stream),
|
|
|
|
Data: v.Msg,
|
|
|
|
Timestamp: v.Timestamp,
|
|
|
|
})
|
|
|
|
}
|
2017-06-15 23:34:42 +00:00
|
|
|
if statusChan != nil {
|
|
|
|
statusChan <- &s
|
|
|
|
}
|
2017-06-14 00:15:55 +00:00
|
|
|
}
|
|
|
|
})
|
|
|
|
|
2017-06-15 23:34:42 +00:00
|
|
|
defer func() {
|
|
|
|
if statusChan != nil {
|
|
|
|
close(statusChan)
|
|
|
|
}
|
|
|
|
}()
|
|
|
|
|
2017-06-14 00:15:55 +00:00
|
|
|
return eg.Wait()
|
2017-06-09 01:16:19 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
func generateID() string {
|
|
|
|
b := make([]byte, 32)
|
|
|
|
if _, err := rand.Read(b); err != nil {
|
|
|
|
panic(err)
|
|
|
|
}
|
|
|
|
return hex.EncodeToString(b)
|
|
|
|
}
|