2020-06-18 16:13:24 +02:00
|
|
|
/*
|
2020-09-22 12:13:00 +02:00
|
|
|
Copyright 2020 Docker Compose CLI authors
|
2020-06-18 16:13:24 +02:00
|
|
|
|
|
|
|
Licensed under the Apache License, Version 2.0 (the "License");
|
|
|
|
you may not use this file except in compliance with the License.
|
|
|
|
You may obtain a copy of the License at
|
|
|
|
|
|
|
|
http://www.apache.org/licenses/LICENSE-2.0
|
|
|
|
|
|
|
|
Unless required by applicable law or agreed to in writing, software
|
|
|
|
distributed under the License is distributed on an "AS IS" BASIS,
|
|
|
|
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
|
|
See the License for the specific language governing permissions and
|
|
|
|
limitations under the License.
|
|
|
|
*/
|
|
|
|
|
2020-06-16 09:42:07 +02:00
|
|
|
package progress
|
|
|
|
|
|
|
|
import (
|
|
|
|
"context"
|
2020-06-18 12:10:36 +02:00
|
|
|
"os"
|
2020-06-16 09:42:07 +02:00
|
|
|
"sync"
|
|
|
|
"time"
|
|
|
|
|
2020-06-17 22:19:08 +02:00
|
|
|
"github.com/containerd/console"
|
|
|
|
"github.com/moby/term"
|
2020-06-18 12:10:36 +02:00
|
|
|
"golang.org/x/sync/errgroup"
|
2020-06-16 09:42:07 +02:00
|
|
|
)
|
|
|
|
|
|
|
|
// EventStatus indicates the status of an action
|
|
|
|
type EventStatus int
|
|
|
|
|
|
|
|
const (
|
|
|
|
// Working means that the current task is working
|
|
|
|
Working EventStatus = iota
|
|
|
|
// Done means that the current task is done
|
|
|
|
Done
|
|
|
|
// Error means that the current task has errored
|
|
|
|
Error
|
|
|
|
)
|
|
|
|
|
|
|
|
// Event reprensents a progress event
|
|
|
|
type Event struct {
|
|
|
|
ID string
|
|
|
|
Text string
|
|
|
|
Status EventStatus
|
|
|
|
StatusText string
|
|
|
|
Done bool
|
|
|
|
|
|
|
|
startTime time.Time
|
|
|
|
endTime time.Time
|
|
|
|
spinner *spinner
|
|
|
|
}
|
|
|
|
|
|
|
|
func (e *Event) stop() {
|
|
|
|
e.endTime = time.Now()
|
|
|
|
e.spinner.Stop()
|
|
|
|
}
|
|
|
|
|
|
|
|
// Writer can write multiple progress events
|
|
|
|
type Writer interface {
|
|
|
|
Start(context.Context) error
|
|
|
|
Stop()
|
|
|
|
Event(Event)
|
|
|
|
}
|
|
|
|
|
2020-06-17 17:24:50 +02:00
|
|
|
type writerKey struct{}
|
|
|
|
|
|
|
|
// WithContextWriter adds the writer to the context
|
|
|
|
func WithContextWriter(ctx context.Context, writer Writer) context.Context {
|
|
|
|
return context.WithValue(ctx, writerKey{}, writer)
|
|
|
|
}
|
|
|
|
|
|
|
|
// ContextWriter returns the writer from the context
|
|
|
|
func ContextWriter(ctx context.Context) Writer {
|
|
|
|
s, _ := ctx.Value(writerKey{}).(Writer)
|
|
|
|
return s
|
|
|
|
}
|
|
|
|
|
2020-09-14 10:01:59 +02:00
|
|
|
type progressFunc func(context.Context) (string, error)
|
2020-06-18 12:10:36 +02:00
|
|
|
|
|
|
|
// Run will run a writer and the progress function
|
|
|
|
// in parallel
|
2020-09-14 10:01:59 +02:00
|
|
|
func Run(ctx context.Context, pf progressFunc) (string, error) {
|
2020-06-18 12:10:36 +02:00
|
|
|
eg, _ := errgroup.WithContext(ctx)
|
|
|
|
w, err := NewWriter(os.Stderr)
|
2020-09-14 10:01:59 +02:00
|
|
|
var result string
|
2020-06-18 12:10:36 +02:00
|
|
|
if err != nil {
|
2020-09-14 10:01:59 +02:00
|
|
|
return "", err
|
2020-06-18 12:10:36 +02:00
|
|
|
}
|
|
|
|
eg.Go(func() error {
|
|
|
|
return w.Start(context.Background())
|
|
|
|
})
|
|
|
|
|
|
|
|
ctx = WithContextWriter(ctx, w)
|
|
|
|
|
|
|
|
eg.Go(func() error {
|
|
|
|
defer w.Stop()
|
2020-09-14 10:01:59 +02:00
|
|
|
s, err := pf(ctx)
|
|
|
|
if err == nil {
|
|
|
|
result = s
|
|
|
|
}
|
|
|
|
return err
|
2020-06-18 12:10:36 +02:00
|
|
|
})
|
|
|
|
|
2020-09-14 10:01:59 +02:00
|
|
|
err = eg.Wait()
|
|
|
|
return result, err
|
2020-06-18 12:10:36 +02:00
|
|
|
}
|
|
|
|
|
2020-06-16 09:42:07 +02:00
|
|
|
// NewWriter returns a new multi-progress writer
|
2020-06-17 22:19:08 +02:00
|
|
|
func NewWriter(out console.File) (Writer, error) {
|
|
|
|
_, isTerminal := term.GetFdInfo(out)
|
2020-06-16 09:42:07 +02:00
|
|
|
|
2020-06-17 22:19:08 +02:00
|
|
|
if isTerminal {
|
|
|
|
con, err := console.ConsoleFromFile(out)
|
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
2020-06-16 09:42:07 +02:00
|
|
|
}
|
|
|
|
|
2020-06-17 22:19:08 +02:00
|
|
|
return &ttyWriter{
|
|
|
|
out: con,
|
|
|
|
eventIDs: []string{},
|
|
|
|
events: map[string]Event{},
|
|
|
|
repeated: false,
|
|
|
|
done: make(chan bool),
|
|
|
|
mtx: &sync.RWMutex{},
|
|
|
|
}, nil
|
2020-06-16 09:42:07 +02:00
|
|
|
}
|
|
|
|
|
2020-06-17 22:19:08 +02:00
|
|
|
return &plainWriter{
|
|
|
|
out: out,
|
|
|
|
done: make(chan bool),
|
|
|
|
}, nil
|
2020-06-16 09:42:07 +02:00
|
|
|
}
|