vendor: update buildkit with typed errors support

Signed-off-by: Tonis Tiigi <tonistiigi@gmail.com>
This commit is contained in:
Tonis Tiigi
2020-06-24 22:20:05 -07:00
parent 0269388aa7
commit 2d720a1e0b
619 changed files with 38296 additions and 104947 deletions

View File

@ -0,0 +1,77 @@
package progress
import (
"context"
"io"
"sync"
)
type MultiReader struct {
mu sync.Mutex
main Reader
initialized bool
done chan struct{}
writers map[*progressWriter]func()
}
func NewMultiReader(pr Reader) *MultiReader {
mr := &MultiReader{
main: pr,
writers: make(map[*progressWriter]func()),
done: make(chan struct{}),
}
return mr
}
func (mr *MultiReader) Reader(ctx context.Context) Reader {
mr.mu.Lock()
defer mr.mu.Unlock()
pr, ctx, closeWriter := NewContext(ctx)
pw, _, ctx := FromContext(ctx)
w := pw.(*progressWriter)
mr.writers[w] = closeWriter
go func() {
select {
case <-ctx.Done():
case <-mr.done:
}
mr.mu.Lock()
defer mr.mu.Unlock()
delete(mr.writers, w)
}()
if !mr.initialized {
go mr.handle()
mr.initialized = true
}
return pr
}
func (mr *MultiReader) handle() error {
for {
p, err := mr.main.Read(context.TODO())
if err != nil {
if err == io.EOF {
mr.mu.Lock()
for w, c := range mr.writers {
w.Close()
c()
}
mr.mu.Unlock()
return nil
}
return err
}
mr.mu.Lock()
for _, p := range p {
for w := range mr.writers {
w.writeRawProgress(p)
}
}
mr.mu.Unlock()
}
}

View File

@ -0,0 +1,104 @@
package progress
import (
"sort"
"sync"
"time"
)
type rawProgressWriter interface {
WriteRawProgress(*Progress) error
Close() error
}
type MultiWriter struct {
mu sync.Mutex
items []*Progress
writers map[rawProgressWriter]struct{}
meta map[string]interface{}
}
func NewMultiWriter(opts ...WriterOption) *MultiWriter {
mw := &MultiWriter{
writers: map[rawProgressWriter]struct{}{},
meta: map[string]interface{}{},
}
for _, o := range opts {
o(mw)
}
return mw
}
func (ps *MultiWriter) Add(pw Writer) {
rw, ok := pw.(rawProgressWriter)
if !ok {
return
}
ps.mu.Lock()
plist := make([]*Progress, 0, len(ps.items))
for _, p := range ps.items {
plist = append(plist, p)
}
sort.Slice(plist, func(i, j int) bool {
return plist[i].Timestamp.Before(plist[j].Timestamp)
})
for _, p := range plist {
rw.WriteRawProgress(p)
}
ps.writers[rw] = struct{}{}
ps.mu.Unlock()
}
func (ps *MultiWriter) Delete(pw Writer) {
rw, ok := pw.(rawProgressWriter)
if !ok {
return
}
ps.mu.Lock()
delete(ps.writers, rw)
ps.mu.Unlock()
}
func (ps *MultiWriter) Write(id string, v interface{}) error {
p := &Progress{
ID: id,
Timestamp: time.Now(),
Sys: v,
meta: ps.meta,
}
return ps.WriteRawProgress(p)
}
func (ps *MultiWriter) WriteRawProgress(p *Progress) error {
meta := p.meta
if len(ps.meta) > 0 {
meta = map[string]interface{}{}
for k, v := range p.meta {
meta[k] = v
}
for k, v := range ps.meta {
if _, ok := meta[k]; !ok {
meta[k] = v
}
}
}
p.meta = meta
return ps.writeRawProgress(p)
}
func (ps *MultiWriter) writeRawProgress(p *Progress) error {
ps.mu.Lock()
defer ps.mu.Unlock()
ps.items = append(ps.items, p)
for w := range ps.writers {
if err := w.WriteRawProgress(p); err != nil {
return err
}
}
return nil
}
func (ps *MultiWriter) Close() error {
return nil
}

View File

@ -0,0 +1,261 @@
package progress
import (
"context"
"io"
"sort"
"sync"
"time"
"github.com/pkg/errors"
)
// Progress package provides utility functions for using the context to capture
// progress of a running function. All progress items written contain an ID
// that is used to collapse unread messages.
type contextKeyT string
var contextKey = contextKeyT("buildkit/util/progress")
// FromContext returns a progress writer from a context.
func FromContext(ctx context.Context, opts ...WriterOption) (Writer, bool, context.Context) {
v := ctx.Value(contextKey)
pw, ok := v.(*progressWriter)
if !ok {
if pw, ok := v.(*MultiWriter); ok {
return pw, true, ctx
}
return &noOpWriter{}, false, ctx
}
pw = newWriter(pw)
for _, o := range opts {
o(pw)
}
ctx = context.WithValue(ctx, contextKey, pw)
return pw, true, ctx
}
type WriterOption func(Writer)
// NewContext returns a new context and a progress reader that captures all
// progress items writtern to this context. Last returned parameter is a closer
// function to signal that no new writes will happen to this context.
func NewContext(ctx context.Context) (Reader, context.Context, func()) {
pr, pw, cancel := pipe()
ctx = WithProgress(ctx, pw)
return pr, ctx, cancel
}
func WithProgress(ctx context.Context, pw Writer) context.Context {
return context.WithValue(ctx, contextKey, pw)
}
func WithMetadata(key string, val interface{}) WriterOption {
return func(w Writer) {
if pw, ok := w.(*progressWriter); ok {
pw.meta[key] = val
}
if pw, ok := w.(*MultiWriter); ok {
pw.meta[key] = val
}
}
}
type Controller interface {
Start(context.Context) (context.Context, func(error))
Status(id string, action string) func()
}
type Writer interface {
Write(id string, value interface{}) error
Close() error
}
type Reader interface {
Read(context.Context) ([]*Progress, error)
}
type Progress struct {
ID string
Timestamp time.Time
Sys interface{}
meta map[string]interface{}
}
type Status struct {
Action string
Current int
Total int
Started *time.Time
Completed *time.Time
}
type progressReader struct {
ctx context.Context
cond *sync.Cond
mu sync.Mutex
writers map[*progressWriter]struct{}
dirty map[string]*Progress
}
func (pr *progressReader) Read(ctx context.Context) ([]*Progress, error) {
done := make(chan struct{})
defer close(done)
go func() {
select {
case <-done:
case <-ctx.Done():
pr.mu.Lock()
pr.cond.Broadcast()
pr.mu.Unlock()
}
}()
pr.mu.Lock()
for {
select {
case <-ctx.Done():
pr.mu.Unlock()
return nil, ctx.Err()
default:
}
dmap := pr.dirty
if len(dmap) == 0 {
select {
case <-pr.ctx.Done():
if len(pr.writers) == 0 {
pr.mu.Unlock()
return nil, io.EOF
}
default:
}
pr.cond.Wait()
continue
}
pr.dirty = make(map[string]*Progress)
pr.mu.Unlock()
out := make([]*Progress, 0, len(dmap))
for _, p := range dmap {
out = append(out, p)
}
sort.Slice(out, func(i, j int) bool {
return out[i].Timestamp.Before(out[j].Timestamp)
})
return out, nil
}
}
func (pr *progressReader) append(pw *progressWriter) {
pr.mu.Lock()
defer pr.mu.Unlock()
select {
case <-pr.ctx.Done():
return
default:
pr.writers[pw] = struct{}{}
}
}
func pipe() (*progressReader, *progressWriter, func()) {
ctx, cancel := context.WithCancel(context.Background())
pr := &progressReader{
ctx: ctx,
writers: make(map[*progressWriter]struct{}),
dirty: make(map[string]*Progress),
}
pr.cond = sync.NewCond(&pr.mu)
go func() {
<-ctx.Done()
pr.mu.Lock()
pr.cond.Broadcast()
pr.mu.Unlock()
}()
pw := &progressWriter{
reader: pr,
}
return pr, pw, cancel
}
func newWriter(pw *progressWriter) *progressWriter {
meta := make(map[string]interface{})
for k, v := range pw.meta {
meta[k] = v
}
pw = &progressWriter{
reader: pw.reader,
meta: meta,
}
pw.reader.append(pw)
return pw
}
type progressWriter struct {
done bool
reader *progressReader
meta map[string]interface{}
}
func (pw *progressWriter) Write(id string, v interface{}) error {
if pw.done {
return errors.Errorf("writing %s to closed progress writer", id)
}
return pw.writeRawProgress(&Progress{
ID: id,
Timestamp: time.Now(),
Sys: v,
meta: pw.meta,
})
}
func (pw *progressWriter) WriteRawProgress(p *Progress) error {
meta := p.meta
if len(pw.meta) > 0 {
meta = map[string]interface{}{}
for k, v := range p.meta {
meta[k] = v
}
for k, v := range pw.meta {
if _, ok := meta[k]; !ok {
meta[k] = v
}
}
}
p.meta = meta
return pw.writeRawProgress(p)
}
func (pw *progressWriter) writeRawProgress(p *Progress) error {
pw.reader.mu.Lock()
pw.reader.dirty[p.ID] = p
pw.reader.cond.Broadcast()
pw.reader.mu.Unlock()
return nil
}
func (pw *progressWriter) Close() error {
pw.reader.mu.Lock()
delete(pw.reader.writers, pw)
pw.reader.mu.Unlock()
pw.reader.cond.Broadcast()
pw.done = true
return nil
}
func (p *Progress) Meta(key string) (interface{}, bool) {
v, ok := p.meta[key]
return v, ok
}
type noOpWriter struct{}
func (pw *noOpWriter) Write(_ string, _ interface{}) error {
return nil
}
func (pw *noOpWriter) Close() error {
return nil
}

View File

@ -273,7 +273,14 @@ func (t *trace) update(s *client.SolveStatus, termWidth int) {
if v.Started != nil {
ts = l.Timestamp.Sub(*v.Started)
}
v.logs = append(v.logs, []byte(fmt.Sprintf("#%d %s %s", v.index, fmt.Sprintf("%#.4g", ts.Seconds())[:5], dt)))
prec := 1
sec := ts.Seconds()
if sec < 10 {
prec = 3
} else if sec < 100 {
prec = 2
}
v.logs = append(v.logs, []byte(fmt.Sprintf("#%d %s %s", v.index, fmt.Sprintf("%.[2]*[1]f", sec, prec), dt)))
}
i++
})
@ -548,6 +555,9 @@ func align(l, r string, w int) string {
}
func wrapHeight(j []*job, limit int) []*job {
if limit < 0 {
return nil
}
var wrapped []*job
wrapped = append(wrapped, j...)
if len(j) > limit {

View File

@ -57,11 +57,11 @@ func (p *textMux) printVtx(t *trace, dgst digest.Digest) {
p.notFirst = true
}
if os.Getenv("PROGRESS_NO_TRUNC") == "1" {
if os.Getenv("PROGRESS_NO_TRUNC") == "0" {
fmt.Fprintf(p.w, "#%d %s\n", v.index, limitString(v.Name, 72))
} else {
fmt.Fprintf(p.w, "#%d %s\n", v.index, v.Name)
fmt.Fprintf(p.w, "#%d %s\n", v.index, v.Digest)
} else {
fmt.Fprintf(p.w, "#%d %s\n", v.index, limitString(v.Name, 72))
}
}