mirror of
https://github.com/docker/compose.git
synced 2025-11-17 20:30:25 +01:00
move progress UI components into cmd
Signed-off-by: Nicolas De Loof <nicolas.deloof@gmail.com>
This commit is contained in:
parent
5ef495c898
commit
aff5c115d6
@ -26,8 +26,8 @@ import (
|
||||
"github.com/compose-spec/compose-go/v2/types"
|
||||
"github.com/docker/cli/cli/command"
|
||||
cliopts "github.com/docker/cli/opts"
|
||||
"github.com/docker/compose/v2/cmd/display"
|
||||
"github.com/docker/compose/v2/pkg/compose"
|
||||
ui "github.com/docker/compose/v2/pkg/progress"
|
||||
"github.com/spf13/cobra"
|
||||
|
||||
"github.com/docker/compose/v2/pkg/api"
|
||||
@ -67,8 +67,8 @@ func (opts buildOptions) toAPIBuildOptions(services []string) (api.BuildOptions,
|
||||
builderName = os.Getenv("BUILDX_BUILDER")
|
||||
}
|
||||
|
||||
uiMode := ui.Mode
|
||||
if uiMode == ui.ModeJSON {
|
||||
uiMode := display.Mode
|
||||
if uiMode == display.ModeJSON {
|
||||
uiMode = "rawjson"
|
||||
}
|
||||
|
||||
@ -100,7 +100,7 @@ func buildCommand(p *ProjectOptions, dockerCli command.Cli, backendOptions *Back
|
||||
Short: "Build or rebuild services",
|
||||
PreRunE: Adapt(func(ctx context.Context, args []string) error {
|
||||
if opts.quiet {
|
||||
ui.Mode = ui.ModeQuiet
|
||||
display.Mode = display.ModeQuiet
|
||||
devnull, err := os.Open(os.DevNull)
|
||||
if err != nil {
|
||||
return err
|
||||
@ -151,7 +151,7 @@ func buildCommand(p *ProjectOptions, dockerCli command.Cli, backendOptions *Back
|
||||
|
||||
func runBuild(ctx context.Context, dockerCli command.Cli, backendOptions *BackendOptions, opts buildOptions, services []string) error {
|
||||
if opts.print {
|
||||
backendOptions.Add(compose.WithEventProcessor(ui.NewQuietWriter()))
|
||||
backendOptions.Add(compose.WithEventProcessor(display.Quiet()))
|
||||
}
|
||||
backend, err := compose.NewComposeService(dockerCli, backendOptions.Options...)
|
||||
if err != nil {
|
||||
|
||||
@ -39,11 +39,11 @@ import (
|
||||
"github.com/docker/cli/cli-plugins/metadata"
|
||||
"github.com/docker/cli/cli/command"
|
||||
"github.com/docker/cli/pkg/kvfile"
|
||||
"github.com/docker/compose/v2/cmd/display"
|
||||
"github.com/docker/compose/v2/cmd/formatter"
|
||||
"github.com/docker/compose/v2/internal/tracing"
|
||||
"github.com/docker/compose/v2/pkg/api"
|
||||
"github.com/docker/compose/v2/pkg/compose"
|
||||
ui "github.com/docker/compose/v2/pkg/progress"
|
||||
"github.com/docker/compose/v2/pkg/remote"
|
||||
"github.com/docker/compose/v2/pkg/utils"
|
||||
"github.com/morikuni/aec"
|
||||
@ -84,10 +84,16 @@ func rawEnv(r io.Reader, filename string, vars map[string]string, lookup func(ke
|
||||
return nil
|
||||
}
|
||||
|
||||
var stdioToStdout bool
|
||||
|
||||
func init() {
|
||||
// compose evaluates env file values for interpolation
|
||||
// `raw` format allows to load env_file with the same parser used by docker run --env-file
|
||||
dotenv.RegisterFormat("raw", rawEnv)
|
||||
|
||||
if v, ok := os.LookupEnv("COMPOSE_STATUS_STDOUT"); ok {
|
||||
stdioToStdout, _ = strconv.ParseBool(v)
|
||||
}
|
||||
}
|
||||
|
||||
// Command defines a compose CLI command as a func with args
|
||||
@ -116,7 +122,7 @@ func AdaptCmd(fn CobraCommand) func(cmd *cobra.Command, args []string) error {
|
||||
StatusCode: 130,
|
||||
}
|
||||
}
|
||||
if ui.Mode == ui.ModeJSON {
|
||||
if display.Mode == display.ModeJSON {
|
||||
err = makeJSONError(err)
|
||||
}
|
||||
return err
|
||||
@ -486,49 +492,49 @@ func RootCommand(dockerCli command.Cli, backendOptions *BackendOptions) *cobra.C
|
||||
formatter.SetANSIMode(dockerCli, ansi)
|
||||
|
||||
if noColor, ok := os.LookupEnv("NO_COLOR"); ok && noColor != "" {
|
||||
ui.NoColor()
|
||||
display.NoColor()
|
||||
formatter.SetANSIMode(dockerCli, formatter.Never)
|
||||
}
|
||||
|
||||
switch ansi {
|
||||
case "never":
|
||||
ui.Mode = ui.ModePlain
|
||||
display.Mode = display.ModePlain
|
||||
case "always":
|
||||
ui.Mode = ui.ModeTTY
|
||||
display.Mode = display.ModeTTY
|
||||
}
|
||||
|
||||
var ep ui.EventProcessor
|
||||
var ep api.EventProcessor
|
||||
switch opts.Progress {
|
||||
case "", ui.ModeAuto:
|
||||
case "", display.ModeAuto:
|
||||
switch {
|
||||
case ansi == "never":
|
||||
ui.Mode = ui.ModePlain
|
||||
ep = ui.NewPlainWriter(dockerCli.Err())
|
||||
display.Mode = display.ModePlain
|
||||
ep = display.Plain(dockerCli.Err())
|
||||
case dockerCli.Out().IsTerminal():
|
||||
ep = ui.NewTTYWriter(dockerCli.Err())
|
||||
ep = display.Full(dockerCli.Err(), stdinfo(dockerCli))
|
||||
default:
|
||||
ep = ui.NewPlainWriter(dockerCli.Err())
|
||||
ep = display.Plain(dockerCli.Err())
|
||||
}
|
||||
case ui.ModeTTY:
|
||||
case display.ModeTTY:
|
||||
if ansi == "never" {
|
||||
return fmt.Errorf("can't use --progress tty while ANSI support is disabled")
|
||||
}
|
||||
ui.Mode = ui.ModeTTY
|
||||
ep = ui.NewTTYWriter(dockerCli.Err())
|
||||
display.Mode = display.ModeTTY
|
||||
ep = display.Full(dockerCli.Err(), stdinfo(dockerCli))
|
||||
|
||||
case ui.ModePlain:
|
||||
case display.ModePlain:
|
||||
if ansi == "always" {
|
||||
return fmt.Errorf("can't use --progress plain while ANSI support is forced")
|
||||
}
|
||||
ui.Mode = ui.ModePlain
|
||||
ep = ui.NewPlainWriter(dockerCli.Err())
|
||||
case ui.ModeQuiet, "none":
|
||||
ui.Mode = ui.ModeQuiet
|
||||
ep = ui.NewQuietWriter()
|
||||
case ui.ModeJSON:
|
||||
ui.Mode = ui.ModeJSON
|
||||
display.Mode = display.ModePlain
|
||||
ep = display.Plain(dockerCli.Err())
|
||||
case display.ModeQuiet, "none":
|
||||
display.Mode = display.ModeQuiet
|
||||
ep = display.Quiet()
|
||||
case display.ModeJSON:
|
||||
display.Mode = display.ModeJSON
|
||||
logrus.SetFormatter(&logrus.JSONFormatter{})
|
||||
ep = ui.NewJSONWriter(dockerCli.Err())
|
||||
ep = display.JSON(dockerCli.Err())
|
||||
default:
|
||||
return fmt.Errorf("unsupported --progress value %q", opts.Progress)
|
||||
}
|
||||
@ -658,6 +664,13 @@ func RootCommand(dockerCli command.Cli, backendOptions *BackendOptions) *cobra.C
|
||||
return c
|
||||
}
|
||||
|
||||
func stdinfo(dockerCli command.Cli) io.Writer {
|
||||
if stdioToStdout {
|
||||
return dockerCli.Out()
|
||||
}
|
||||
return dockerCli.Err()
|
||||
}
|
||||
|
||||
func setEnvWithDotEnv(opts ProjectOptions) error {
|
||||
options, err := cli.NewProjectOptions(opts.ConfigPaths,
|
||||
cli.WithWorkingDirectory(opts.ProjectDir),
|
||||
@ -683,9 +696,9 @@ func setEnvWithDotEnv(opts ProjectOptions) error {
|
||||
}
|
||||
|
||||
var printerModes = []string{
|
||||
ui.ModeAuto,
|
||||
ui.ModeTTY,
|
||||
ui.ModePlain,
|
||||
ui.ModeJSON,
|
||||
ui.ModeQuiet,
|
||||
display.ModeAuto,
|
||||
display.ModeTTY,
|
||||
display.ModePlain,
|
||||
display.ModeJSON,
|
||||
display.ModeQuiet,
|
||||
}
|
||||
|
||||
@ -18,6 +18,8 @@ package compose
|
||||
|
||||
import (
|
||||
"context"
|
||||
"errors"
|
||||
"fmt"
|
||||
"os"
|
||||
|
||||
"github.com/docker/cli/cli/command"
|
||||
@ -65,10 +67,15 @@ func runKill(ctx context.Context, dockerCli command.Cli, backendOptions *Backend
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return backend.Kill(ctx, name, api.KillOptions{
|
||||
err = backend.Kill(ctx, name, api.KillOptions{
|
||||
RemoveOrphans: opts.removeOrphans,
|
||||
Project: project,
|
||||
Services: services,
|
||||
Signal: opts.signal,
|
||||
})
|
||||
if errors.Is(err, api.ErrNoResources) {
|
||||
_, _ = fmt.Fprintln(stdinfo(dockerCli), "No container to kill")
|
||||
return nil
|
||||
}
|
||||
return err
|
||||
}
|
||||
|
||||
@ -22,7 +22,6 @@ import (
|
||||
|
||||
"github.com/docker/cli/cli/command"
|
||||
"github.com/docker/compose/v2/pkg/compose"
|
||||
"github.com/docker/compose/v2/pkg/progress"
|
||||
"github.com/spf13/cobra"
|
||||
|
||||
"github.com/docker/compose/v2/cmd/formatter"
|
||||
@ -107,28 +106,28 @@ func runLogs(ctx context.Context, dockerCli command.Cli, backendOptions *Backend
|
||||
var _ api.LogConsumer = &logConsumer{}
|
||||
|
||||
type logConsumer struct {
|
||||
events progress.EventProcessor
|
||||
events api.EventProcessor
|
||||
}
|
||||
|
||||
func (l logConsumer) Log(containerName, message string) {
|
||||
l.events.On(progress.Event{
|
||||
l.events.On(api.Resource{
|
||||
ID: containerName,
|
||||
Text: message,
|
||||
})
|
||||
}
|
||||
|
||||
func (l logConsumer) Err(containerName, message string) {
|
||||
l.events.On(progress.Event{
|
||||
l.events.On(api.Resource{
|
||||
ID: containerName,
|
||||
Status: progress.Error,
|
||||
Status: api.Error,
|
||||
Text: message,
|
||||
})
|
||||
}
|
||||
|
||||
func (l logConsumer) Status(containerName, message string) {
|
||||
l.events.On(progress.Event{
|
||||
l.events.On(api.Resource{
|
||||
ID: containerName,
|
||||
Status: progress.Error,
|
||||
Status: api.Error,
|
||||
Text: message,
|
||||
})
|
||||
}
|
||||
|
||||
@ -30,9 +30,9 @@ import (
|
||||
"github.com/compose-spec/compose-go/v2/template"
|
||||
"github.com/compose-spec/compose-go/v2/types"
|
||||
"github.com/docker/cli/cli/command"
|
||||
"github.com/docker/compose/v2/cmd/display"
|
||||
"github.com/docker/compose/v2/cmd/prompt"
|
||||
"github.com/docker/compose/v2/internal/tracing"
|
||||
ui "github.com/docker/compose/v2/pkg/progress"
|
||||
)
|
||||
|
||||
func applyPlatforms(project *types.Project, buildForSinglePlatform bool) error {
|
||||
@ -247,7 +247,7 @@ func displayInterpolationVariables(writer io.Writer, varsInfo []varInfo) {
|
||||
|
||||
func displayLocationRemoteStack(dockerCli command.Cli, project *types.Project, options buildOptions) {
|
||||
mainComposeFile := options.ProjectOptions.ConfigPaths[0] //nolint:staticcheck
|
||||
if ui.Mode != ui.ModeQuiet && ui.Mode != ui.ModeJSON {
|
||||
if display.Mode != display.ModeQuiet && display.Mode != display.ModeJSON {
|
||||
_, _ = fmt.Fprintf(dockerCli.Out(), "Your compose stack %q is stored in %q\n", mainComposeFile, project.WorkingDir)
|
||||
}
|
||||
}
|
||||
|
||||
@ -18,6 +18,8 @@ package compose
|
||||
|
||||
import (
|
||||
"context"
|
||||
"errors"
|
||||
"fmt"
|
||||
|
||||
"github.com/docker/cli/cli/command"
|
||||
"github.com/docker/compose/v2/pkg/api"
|
||||
@ -70,11 +72,16 @@ func runRemove(ctx context.Context, dockerCli command.Cli, backendOptions *Backe
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return backend.Remove(ctx, name, api.RemoveOptions{
|
||||
err = backend.Remove(ctx, name, api.RemoveOptions{
|
||||
Services: services,
|
||||
Force: opts.force,
|
||||
Volumes: opts.volumes,
|
||||
Project: project,
|
||||
Stop: opts.stop,
|
||||
})
|
||||
if errors.Is(err, api.ErrNoResources) {
|
||||
_, _ = fmt.Fprintln(stdinfo(dockerCli), "No stopped containers")
|
||||
return nil
|
||||
}
|
||||
return err
|
||||
}
|
||||
|
||||
@ -25,8 +25,8 @@ import (
|
||||
composecli "github.com/compose-spec/compose-go/v2/cli"
|
||||
"github.com/compose-spec/compose-go/v2/dotenv"
|
||||
"github.com/compose-spec/compose-go/v2/format"
|
||||
"github.com/docker/compose/v2/cmd/display"
|
||||
"github.com/docker/compose/v2/pkg/compose"
|
||||
"github.com/docker/compose/v2/pkg/progress"
|
||||
xprogress "github.com/moby/buildkit/util/progress/progressui"
|
||||
"github.com/sirupsen/logrus"
|
||||
|
||||
@ -193,7 +193,7 @@ func runCommand(p *ProjectOptions, dockerCli command.Cli, backendOptions *Backen
|
||||
}
|
||||
|
||||
if options.quiet {
|
||||
progress.Mode = progress.ModeQuiet
|
||||
display.Mode = display.ModeQuiet
|
||||
devnull, err := os.Open(os.DevNull)
|
||||
if err != nil {
|
||||
return err
|
||||
|
||||
@ -26,8 +26,8 @@ import (
|
||||
|
||||
"github.com/compose-spec/compose-go/v2/types"
|
||||
"github.com/docker/cli/cli/command"
|
||||
"github.com/docker/compose/v2/cmd/display"
|
||||
"github.com/docker/compose/v2/pkg/compose"
|
||||
ui "github.com/docker/compose/v2/pkg/progress"
|
||||
xprogress "github.com/moby/buildkit/util/progress/progressui"
|
||||
"github.com/sirupsen/logrus"
|
||||
"github.com/spf13/cobra"
|
||||
@ -341,7 +341,7 @@ func runUp(
|
||||
WaitTimeout: timeout,
|
||||
Watch: upOptions.watch,
|
||||
Services: services,
|
||||
NavigationMenu: upOptions.navigationMenu && ui.Mode != "plain" && dockerCli.In().IsTerminal(),
|
||||
NavigationMenu: upOptions.navigationMenu && display.Mode != "plain" && dockerCli.In().IsTerminal(),
|
||||
},
|
||||
})
|
||||
}
|
||||
|
||||
@ -14,7 +14,7 @@
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
package progress
|
||||
package display
|
||||
|
||||
import (
|
||||
"github.com/morikuni/aec"
|
||||
@ -14,16 +14,18 @@
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
package progress
|
||||
package display
|
||||
|
||||
import (
|
||||
"context"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"io"
|
||||
|
||||
"github.com/docker/compose/v2/pkg/api"
|
||||
)
|
||||
|
||||
func NewJSONWriter(out io.Writer) EventProcessor {
|
||||
func JSON(out io.Writer) api.EventProcessor {
|
||||
return &jsonWriter{
|
||||
out: out,
|
||||
}
|
||||
@ -50,7 +52,7 @@ type jsonMessage struct {
|
||||
func (p *jsonWriter) Start(ctx context.Context, operation string) {
|
||||
}
|
||||
|
||||
func (p *jsonWriter) Event(e Event) {
|
||||
func (p *jsonWriter) Event(e api.Resource) {
|
||||
message := &jsonMessage{
|
||||
DryRun: p.dryRun,
|
||||
Tail: false,
|
||||
@ -69,7 +71,7 @@ func (p *jsonWriter) Event(e Event) {
|
||||
}
|
||||
}
|
||||
|
||||
func (p *jsonWriter) On(events ...Event) {
|
||||
func (p *jsonWriter) On(events ...api.Resource) {
|
||||
for _, e := range events {
|
||||
p.Event(e)
|
||||
}
|
||||
@ -14,13 +14,14 @@
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
package progress
|
||||
package display
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"encoding/json"
|
||||
"testing"
|
||||
|
||||
"github.com/docker/compose/v2/pkg/api"
|
||||
"gotest.tools/v3/assert"
|
||||
)
|
||||
|
||||
@ -31,11 +32,11 @@ func TestJsonWriter_Event(t *testing.T) {
|
||||
dryRun: true,
|
||||
}
|
||||
|
||||
event := Event{
|
||||
event := api.Resource{
|
||||
ID: "service1",
|
||||
ParentID: "project",
|
||||
Status: Working,
|
||||
Text: StatusCreating,
|
||||
Status: api.Working,
|
||||
Text: api.StatusCreating,
|
||||
Current: 50,
|
||||
Total: 100,
|
||||
Percent: 50,
|
||||
@ -50,7 +51,7 @@ func TestJsonWriter_Event(t *testing.T) {
|
||||
DryRun: true,
|
||||
ID: event.ID,
|
||||
ParentID: event.ParentID,
|
||||
Text: StatusCreating,
|
||||
Text: api.StatusCreating,
|
||||
Status: "Working",
|
||||
Current: event.Current,
|
||||
Total: event.Total,
|
||||
@ -1,5 +1,5 @@
|
||||
/*
|
||||
Copyright 2020 Docker Compose CLI authors
|
||||
Copyright 2024 Docker Compose CLI authors
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
@ -14,20 +14,10 @@
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
package progress
|
||||
package display
|
||||
|
||||
import (
|
||||
"context"
|
||||
)
|
||||
|
||||
type progressFunc func(context.Context) error
|
||||
|
||||
func Run(ctx context.Context, pf progressFunc, operation string, bus EventProcessor) error {
|
||||
bus.Start(ctx, operation)
|
||||
err := pf(ctx)
|
||||
bus.Done(operation, err != nil)
|
||||
return err
|
||||
}
|
||||
// Mode define how progress should be rendered, either as ModePlain or ModeTTY
|
||||
var Mode = ModeAuto
|
||||
|
||||
const (
|
||||
// ModeAuto detect console capabilities
|
||||
@ -41,6 +31,3 @@ const (
|
||||
// ModeJSON outputs a machine-readable JSON stream
|
||||
ModeJSON = "json"
|
||||
)
|
||||
|
||||
// Mode define how progress should be rendered, either as ModePlain or ModeTTY
|
||||
var Mode = ModeAuto
|
||||
@ -14,7 +14,7 @@
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
package progress
|
||||
package display
|
||||
|
||||
import (
|
||||
"context"
|
||||
@ -24,7 +24,7 @@ import (
|
||||
"github.com/docker/compose/v2/pkg/api"
|
||||
)
|
||||
|
||||
func NewPlainWriter(out io.Writer) EventProcessor {
|
||||
func Plain(out io.Writer) api.EventProcessor {
|
||||
return &plainWriter{
|
||||
out: out,
|
||||
}
|
||||
@ -38,7 +38,7 @@ type plainWriter struct {
|
||||
func (p *plainWriter) Start(ctx context.Context, operation string) {
|
||||
}
|
||||
|
||||
func (p *plainWriter) Event(e Event) {
|
||||
func (p *plainWriter) Event(e api.Resource) {
|
||||
prefix := ""
|
||||
if p.dryRun {
|
||||
prefix = api.DRYRUN_PREFIX
|
||||
@ -46,7 +46,7 @@ func (p *plainWriter) Event(e Event) {
|
||||
_, _ = fmt.Fprintln(p.out, prefix, e.ID, e.Text, e.Details)
|
||||
}
|
||||
|
||||
func (p *plainWriter) On(events ...Event) {
|
||||
func (p *plainWriter) On(events ...api.Resource) {
|
||||
for _, e := range events {
|
||||
p.Event(e)
|
||||
}
|
||||
@ -14,11 +14,15 @@
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
package progress
|
||||
package display
|
||||
|
||||
import "context"
|
||||
import (
|
||||
"context"
|
||||
|
||||
func NewQuietWriter() EventProcessor {
|
||||
"github.com/docker/compose/v2/pkg/api"
|
||||
)
|
||||
|
||||
func Quiet() api.EventProcessor {
|
||||
return &quiet{}
|
||||
}
|
||||
|
||||
@ -30,5 +34,5 @@ func (q *quiet) Start(_ context.Context, _ string) {
|
||||
func (q *quiet) Done(_ string, _ bool) {
|
||||
}
|
||||
|
||||
func (q *quiet) On(_ ...Event) {
|
||||
func (q *quiet) On(_ ...api.Resource) {
|
||||
}
|
||||
@ -14,7 +14,7 @@
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
package progress
|
||||
package display
|
||||
|
||||
import (
|
||||
"runtime"
|
||||
@ -14,7 +14,7 @@
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
package progress
|
||||
package display
|
||||
|
||||
import (
|
||||
"context"
|
||||
@ -31,11 +31,12 @@ import (
|
||||
"github.com/morikuni/aec"
|
||||
)
|
||||
|
||||
// NewTTYWriter creates an EventProcessor that render advanced UI within a terminal.
|
||||
// Full creates an EventProcessor that render advanced UI within a terminal.
|
||||
// On Start, TUI lists task with a progress timer
|
||||
func NewTTYWriter(out io.Writer) EventProcessor {
|
||||
func Full(out io.Writer, info io.Writer) api.EventProcessor {
|
||||
return &ttyWriter{
|
||||
out: out,
|
||||
info: info,
|
||||
tasks: map[string]task{},
|
||||
done: make(chan bool),
|
||||
mtx: &sync.Mutex{},
|
||||
@ -55,6 +56,7 @@ type ttyWriter struct {
|
||||
operation string
|
||||
ticker *time.Ticker
|
||||
suspended bool
|
||||
info io.Writer
|
||||
}
|
||||
|
||||
type task struct {
|
||||
@ -64,7 +66,7 @@ type task struct {
|
||||
endTime time.Time
|
||||
text string
|
||||
details string
|
||||
status EventStatus
|
||||
status api.EventStatus
|
||||
current int64
|
||||
percent int
|
||||
total int64
|
||||
@ -108,11 +110,16 @@ func (w *ttyWriter) Done(operation string, success bool) {
|
||||
w.done <- true
|
||||
}
|
||||
|
||||
func (w *ttyWriter) On(events ...Event) {
|
||||
func (w *ttyWriter) On(events ...api.Resource) {
|
||||
w.mtx.Lock()
|
||||
defer w.mtx.Unlock()
|
||||
for _, e := range events {
|
||||
if w.operation != "start" && (e.Text == StatusStarted || e.Text == StatusStarting) {
|
||||
if e.ID == "Compose" {
|
||||
_, _ = fmt.Fprintln(w.info, ErrorColor(e.Details))
|
||||
continue
|
||||
}
|
||||
|
||||
if w.operation != "start" && (e.Text == api.StatusStarted || e.Text == api.StatusStarting) {
|
||||
// skip those events to avoid mix with container logs
|
||||
continue
|
||||
}
|
||||
@ -120,9 +127,9 @@ func (w *ttyWriter) On(events ...Event) {
|
||||
}
|
||||
}
|
||||
|
||||
func (w *ttyWriter) event(e Event) {
|
||||
func (w *ttyWriter) event(e api.Resource) {
|
||||
// Suspend print while a build is in progress, to avoid collision with buildkit Display
|
||||
if e.Text == StatusBuilding {
|
||||
if e.Text == api.StatusBuilding {
|
||||
w.ticker.Stop()
|
||||
w.suspended = true
|
||||
} else if w.suspended {
|
||||
@ -132,11 +139,11 @@ func (w *ttyWriter) event(e Event) {
|
||||
|
||||
if last, ok := w.tasks[e.ID]; ok {
|
||||
switch e.Status {
|
||||
case Done, Error, Warning:
|
||||
case api.Done, api.Error, api.Warning:
|
||||
if last.status != e.Status {
|
||||
last.stop()
|
||||
}
|
||||
case Working:
|
||||
case api.Working:
|
||||
last.hasMore()
|
||||
}
|
||||
last.status = e.Status
|
||||
@ -170,7 +177,7 @@ func (w *ttyWriter) event(e Event) {
|
||||
total: e.Total,
|
||||
spinner: NewSpinner(),
|
||||
}
|
||||
if e.Status == Done || e.Status == Error {
|
||||
if e.Status == api.Done || e.Status == api.Error {
|
||||
t.stop()
|
||||
}
|
||||
w.tasks[e.ID] = t
|
||||
@ -179,7 +186,7 @@ func (w *ttyWriter) event(e Event) {
|
||||
w.printEvent(e)
|
||||
}
|
||||
|
||||
func (w *ttyWriter) printEvent(e Event) {
|
||||
func (w *ttyWriter) printEvent(e api.Resource) {
|
||||
if w.operation != "" {
|
||||
// event will be displayed by progress UI on ticker's ticks
|
||||
return
|
||||
@ -187,13 +194,13 @@ func (w *ttyWriter) printEvent(e Event) {
|
||||
|
||||
var color colorFunc
|
||||
switch e.Status {
|
||||
case Working:
|
||||
case api.Working:
|
||||
color = SuccessColor
|
||||
case Done:
|
||||
case api.Done:
|
||||
color = SuccessColor
|
||||
case Warning:
|
||||
case api.Warning:
|
||||
color = WarningColor
|
||||
case Error:
|
||||
case api.Error:
|
||||
color = ErrorColor
|
||||
}
|
||||
_, _ = fmt.Fprintf(w.out, "%s %s %s\n", e.ID, color(e.Text), e.Details)
|
||||
@ -271,7 +278,7 @@ func (w *ttyWriter) print() {
|
||||
|
||||
func (w *ttyWriter) lineText(t task, pad string, terminalWidth, statusPadding int, dryRun bool) string {
|
||||
endTime := time.Now()
|
||||
if t.status != Working {
|
||||
if t.status != api.Working {
|
||||
endTime = t.startTime
|
||||
if (t.endTime != time.Time{}) {
|
||||
endTime = t.endTime
|
||||
@ -292,11 +299,11 @@ func (w *ttyWriter) lineText(t task, pad string, terminalWidth, statusPadding in
|
||||
)
|
||||
|
||||
// only show the aggregated progress while the root operation is in-progress
|
||||
if parent := t; parent.status == Working {
|
||||
if parent := t; parent.status == api.Working {
|
||||
for _, id := range w.ids {
|
||||
child := w.tasks[id]
|
||||
if child.parentID == parent.ID {
|
||||
if child.status == Working && child.total == 0 {
|
||||
if child.status == api.Working && child.total == 0 {
|
||||
// we don't have totals available for all the child events
|
||||
// so don't show the total progress yet
|
||||
hideDetails = true
|
||||
@ -361,24 +368,24 @@ var (
|
||||
|
||||
func spinner(t task) string {
|
||||
switch t.status {
|
||||
case Done:
|
||||
case api.Done:
|
||||
return SuccessColor(spinnerDone)
|
||||
case Warning:
|
||||
case api.Warning:
|
||||
return WarningColor(spinnerWarning)
|
||||
case Error:
|
||||
case api.Error:
|
||||
return ErrorColor(spinnerError)
|
||||
default:
|
||||
return CountColor(t.spinner.String())
|
||||
}
|
||||
}
|
||||
|
||||
func colorFn(s EventStatus) colorFunc {
|
||||
func colorFn(s api.EventStatus) colorFunc {
|
||||
switch s {
|
||||
case Done:
|
||||
case api.Done:
|
||||
return SuccessColor
|
||||
case Warning:
|
||||
case api.Warning:
|
||||
return WarningColor
|
||||
case Error:
|
||||
case api.Error:
|
||||
return ErrorColor
|
||||
default:
|
||||
return nocolor
|
||||
@ -388,7 +395,7 @@ func colorFn(s EventStatus) colorFunc {
|
||||
func numDone(tasks map[string]task) int {
|
||||
i := 0
|
||||
for _, t := range tasks {
|
||||
if t.status != Working {
|
||||
if t.status != api.Working {
|
||||
i++
|
||||
}
|
||||
}
|
||||
@ -35,12 +35,7 @@ var (
|
||||
ErrForbidden = errors.New("forbidden")
|
||||
// ErrUnknown is returned when the error type is unmapped
|
||||
ErrUnknown = errors.New("unknown")
|
||||
// ErrLoginFailed is returned when login failed
|
||||
ErrLoginFailed = errors.New("login failed")
|
||||
// ErrLoginRequired is returned when login is required for a specific action
|
||||
ErrLoginRequired = errors.New("login required")
|
||||
// ErrNotImplemented is returned when a backend doesn't implement
|
||||
// an action
|
||||
// ErrNotImplemented is returned when a backend doesn't implement an action
|
||||
ErrNotImplemented = errors.New("not implemented")
|
||||
// ErrUnsupportedFlag is returned when a backend doesn't support a flag
|
||||
ErrUnsupportedFlag = errors.New("unsupported flag")
|
||||
@ -48,9 +43,8 @@ var (
|
||||
ErrCanceled = errors.New("canceled")
|
||||
// ErrParsingFailed is returned when a string cannot be parsed
|
||||
ErrParsingFailed = errors.New("parsing failed")
|
||||
// ErrWrongContextType is returned when the caller tries to get a context
|
||||
// with the wrong type
|
||||
ErrWrongContextType = errors.New("wrong context type")
|
||||
// ErrNoResources is returned when operation didn't selected any resource
|
||||
ErrNoResources = errors.New("no resources")
|
||||
)
|
||||
|
||||
// IsNotFoundError returns true if the unwrapped error is ErrNotFound
|
||||
|
||||
103
pkg/api/event.go
Normal file
103
pkg/api/event.go
Normal file
@ -0,0 +1,103 @@
|
||||
/*
|
||||
Copyright 2020 Docker Compose CLI authors
|
||||
|
||||
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.
|
||||
*/
|
||||
|
||||
package api
|
||||
|
||||
import (
|
||||
"context"
|
||||
)
|
||||
|
||||
// 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
|
||||
// Warning means that the current task has warning
|
||||
Warning
|
||||
// Error means that the current task has errored
|
||||
Error
|
||||
)
|
||||
|
||||
// ResourceCompose is a special resource ID used when event applies to all resources in the application
|
||||
const ResourceCompose = "Compose"
|
||||
|
||||
const (
|
||||
StatusError = "Error"
|
||||
StatusCreating = "Creating"
|
||||
StatusStarting = "Starting"
|
||||
StatusStarted = "Started"
|
||||
StatusWaiting = "Waiting"
|
||||
StatusHealthy = "Healthy"
|
||||
StatusExited = "Exited"
|
||||
StatusRestarting = "Restarting"
|
||||
StatusRestarted = "Restarted"
|
||||
StatusRunning = "Running"
|
||||
StatusCreated = "Created"
|
||||
StatusStopping = "Stopping"
|
||||
StatusStopped = "Stopped"
|
||||
StatusKilling = "Killing"
|
||||
StatusKilled = "Killed"
|
||||
StatusRemoving = "Removing"
|
||||
StatusRemoved = "Removed"
|
||||
StatusBuilding = "Building"
|
||||
StatusBuilt = "Built"
|
||||
StatusPulling = "Pulling"
|
||||
StatusPulled = "Pulled"
|
||||
StatusCommitting = "Committing"
|
||||
StatusCommitted = "Committed"
|
||||
StatusCopying = "Copying"
|
||||
StatusCopied = "Copied"
|
||||
StatusExporting = "Exporting"
|
||||
StatusExported = "Exported"
|
||||
)
|
||||
|
||||
// Resource represents status change and progress for a compose resource.
|
||||
type Resource struct {
|
||||
ID string
|
||||
ParentID string
|
||||
Text string
|
||||
Details string
|
||||
Status EventStatus
|
||||
Current int64
|
||||
Percent int
|
||||
Total int64
|
||||
}
|
||||
|
||||
func (e *Resource) StatusText() string {
|
||||
switch e.Status {
|
||||
case Working:
|
||||
return "Working"
|
||||
case Warning:
|
||||
return "Warning"
|
||||
case Done:
|
||||
return "Done"
|
||||
default:
|
||||
return "Error"
|
||||
}
|
||||
}
|
||||
|
||||
// EventProcessor is notified about Compose operations and tasks
|
||||
type EventProcessor interface {
|
||||
// Start is triggered as a Compose operation is starting with context
|
||||
Start(ctx context.Context, operation string)
|
||||
// On notify about (sub)task and progress processing operation
|
||||
On(events ...Resource)
|
||||
// Done is triggered as a Compose operation completed
|
||||
Done(operation string, success bool)
|
||||
}
|
||||
@ -26,7 +26,6 @@ import (
|
||||
"github.com/containerd/platforms"
|
||||
"github.com/docker/compose/v2/internal/tracing"
|
||||
"github.com/docker/compose/v2/pkg/api"
|
||||
"github.com/docker/compose/v2/pkg/progress"
|
||||
"github.com/docker/compose/v2/pkg/utils"
|
||||
specs "github.com/opencontainers/image-spec/specs-go/v1"
|
||||
"github.com/sirupsen/logrus"
|
||||
@ -37,7 +36,7 @@ func (s *composeService) Build(ctx context.Context, project *types.Project, opti
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return progress.Run(ctx, func(ctx context.Context) error {
|
||||
return Run(ctx, func(ctx context.Context) error {
|
||||
return tracing.SpanWrapFunc("project/build", tracing.ProjectOptions(ctx, project),
|
||||
func(ctx context.Context) error {
|
||||
_, err := s.build(ctx, project, options, nil)
|
||||
|
||||
@ -40,7 +40,6 @@ import (
|
||||
"github.com/docker/cli/cli/command/image/build"
|
||||
"github.com/docker/cli/cli/streams"
|
||||
"github.com/docker/compose/v2/pkg/api"
|
||||
"github.com/docker/compose/v2/pkg/progress"
|
||||
"github.com/docker/docker/api/types/versions"
|
||||
"github.com/google/uuid"
|
||||
"github.com/moby/buildkit/client"
|
||||
@ -118,10 +117,10 @@ type buildStatus struct {
|
||||
func (s *composeService) doBuildBake(ctx context.Context, project *types.Project, serviceToBeBuild types.Services, options api.BuildOptions) (map[string]string, error) { //nolint:gocyclo
|
||||
eg := errgroup.Group{}
|
||||
ch := make(chan *client.SolveStatus)
|
||||
if options.Progress == progress.ModeAuto {
|
||||
displayMode := progressui.DisplayMode(options.Progress)
|
||||
if displayMode == progressui.AutoMode {
|
||||
options.Progress = os.Getenv("BUILDKIT_PROGRESS")
|
||||
}
|
||||
displayMode := progressui.DisplayMode(options.Progress)
|
||||
out := options.Out
|
||||
if out == nil {
|
||||
out = s.stdout()
|
||||
@ -206,7 +205,7 @@ func (s *composeService) doBuildBake(ctx context.Context, project *types.Project
|
||||
}
|
||||
|
||||
image := api.GetImageNameOrDefault(service, project.Name)
|
||||
s.events.On(progress.BuildingEvent(image))
|
||||
s.events.On(buildingEvent(image))
|
||||
|
||||
expectedImages[serviceName] = image
|
||||
|
||||
@ -408,7 +407,7 @@ func (s *composeService) doBuildBake(ctx context.Context, project *types.Project
|
||||
return nil, fmt.Errorf("build result not found in Bake metadata for service %s", name)
|
||||
}
|
||||
results[image] = built.Digest
|
||||
s.events.On(progress.BuiltEvent(image))
|
||||
s.events.On(builtEvent(image))
|
||||
}
|
||||
return results, nil
|
||||
}
|
||||
@ -554,20 +553,20 @@ func (s composeService) dryRunBake(cfg bakeConfig) map[string]string {
|
||||
bakeResponse[name] = dryRunUUID
|
||||
}
|
||||
for name := range bakeResponse {
|
||||
s.events.On(progress.BuiltEvent(name))
|
||||
s.events.On(builtEvent(name))
|
||||
}
|
||||
return bakeResponse
|
||||
}
|
||||
|
||||
func (s composeService) displayDryRunBuildEvent(name, dryRunUUID, tag string) {
|
||||
s.events.On(progress.Event{
|
||||
s.events.On(api.Resource{
|
||||
ID: name + " ==>",
|
||||
Status: progress.Done,
|
||||
Status: api.Done,
|
||||
Text: fmt.Sprintf("==> writing image %s", dryRunUUID),
|
||||
})
|
||||
s.events.On(progress.Event{
|
||||
s.events.On(api.Resource{
|
||||
ID: name + " ==> ==>",
|
||||
Status: progress.Done,
|
||||
Status: api.Done,
|
||||
Text: fmt.Sprintf(`naming to %s`, tag),
|
||||
})
|
||||
}
|
||||
|
||||
@ -30,7 +30,6 @@ import (
|
||||
"github.com/docker/cli/cli"
|
||||
"github.com/docker/cli/cli/command/image/build"
|
||||
"github.com/docker/compose/v2/pkg/api"
|
||||
progress2 "github.com/docker/compose/v2/pkg/progress"
|
||||
buildtypes "github.com/docker/docker/api/types/build"
|
||||
"github.com/docker/docker/api/types/container"
|
||||
"github.com/docker/docker/api/types/registry"
|
||||
@ -86,12 +85,12 @@ func (s *composeService) doBuildClassic(ctx context.Context, project *types.Proj
|
||||
}
|
||||
|
||||
image := api.GetImageNameOrDefault(service, project.Name)
|
||||
s.events.On(progress2.BuildingEvent(image))
|
||||
s.events.On(buildingEvent(image))
|
||||
id, err := s.doBuildImage(ctx, project, service, options)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
s.events.On(progress2.BuiltEvent(image))
|
||||
s.events.On(builtEvent(image))
|
||||
builtDigests[getServiceIndex(name)] = id
|
||||
|
||||
if options.Push {
|
||||
@ -258,7 +257,7 @@ func (s *composeService) doBuildImage(ctx context.Context, project *types.Projec
|
||||
|
||||
ctx, cancel := context.WithCancel(ctx)
|
||||
defer cancel()
|
||||
s.events.On(progress2.BuildingEvent(imageName))
|
||||
s.events.On(buildingEvent(imageName))
|
||||
response, err := s.apiClient().ImageBuild(ctx, body, buildOpts)
|
||||
if err != nil {
|
||||
return "", err
|
||||
@ -287,7 +286,7 @@ func (s *composeService) doBuildImage(ctx context.Context, project *types.Projec
|
||||
}
|
||||
return "", err
|
||||
}
|
||||
s.events.On(progress2.BuiltEvent(imageName))
|
||||
s.events.On(builtEvent(imageName))
|
||||
return imageID, nil
|
||||
}
|
||||
|
||||
|
||||
@ -22,12 +22,11 @@ import (
|
||||
"strings"
|
||||
|
||||
"github.com/docker/compose/v2/pkg/api"
|
||||
"github.com/docker/compose/v2/pkg/progress"
|
||||
"github.com/docker/docker/api/types/container"
|
||||
)
|
||||
|
||||
func (s *composeService) Commit(ctx context.Context, projectName string, options api.CommitOptions) error {
|
||||
return progress.Run(ctx, func(ctx context.Context) error {
|
||||
return Run(ctx, func(ctx context.Context) error {
|
||||
return s.commit(ctx, projectName, options)
|
||||
}, "commit", s.events)
|
||||
}
|
||||
@ -42,17 +41,17 @@ func (s *composeService) commit(ctx context.Context, projectName string, options
|
||||
|
||||
name := getCanonicalContainerName(ctr)
|
||||
|
||||
s.events.On(progress.Event{
|
||||
s.events.On(api.Resource{
|
||||
ID: name,
|
||||
Status: progress.Working,
|
||||
Text: progress.StatusCommitting,
|
||||
Status: api.Working,
|
||||
Text: api.StatusCommitting,
|
||||
})
|
||||
|
||||
if s.dryRun {
|
||||
s.events.On(progress.Event{
|
||||
s.events.On(api.Resource{
|
||||
ID: name,
|
||||
Status: progress.Done,
|
||||
Text: progress.StatusCommitted,
|
||||
Status: api.Done,
|
||||
Text: api.StatusCommitted,
|
||||
})
|
||||
|
||||
return nil
|
||||
@ -69,10 +68,10 @@ func (s *composeService) commit(ctx context.Context, projectName string, options
|
||||
return err
|
||||
}
|
||||
|
||||
s.events.On(progress.Event{
|
||||
s.events.On(api.Resource{
|
||||
ID: name,
|
||||
Text: fmt.Sprintf("Committed as %s", response.ID),
|
||||
Status: progress.Done,
|
||||
Status: api.Done,
|
||||
})
|
||||
|
||||
return nil
|
||||
|
||||
@ -21,7 +21,6 @@ import (
|
||||
"errors"
|
||||
"fmt"
|
||||
"io"
|
||||
"os"
|
||||
"strconv"
|
||||
"strings"
|
||||
"sync"
|
||||
@ -32,7 +31,6 @@ import (
|
||||
"github.com/docker/cli/cli/config/configfile"
|
||||
"github.com/docker/cli/cli/flags"
|
||||
"github.com/docker/cli/cli/streams"
|
||||
"github.com/docker/compose/v2/pkg/progress"
|
||||
"github.com/docker/docker/api/types/container"
|
||||
"github.com/docker/docker/api/types/filters"
|
||||
"github.com/docker/docker/api/types/network"
|
||||
@ -45,15 +43,6 @@ import (
|
||||
"github.com/docker/compose/v2/pkg/api"
|
||||
)
|
||||
|
||||
var stdioToStdout bool
|
||||
|
||||
func init() {
|
||||
out, ok := os.LookupEnv("COMPOSE_STATUS_STDOUT")
|
||||
if ok {
|
||||
stdioToStdout, _ = strconv.ParseBool(out)
|
||||
}
|
||||
}
|
||||
|
||||
type Option func(service *composeService) error
|
||||
|
||||
// NewComposeService creates a Compose service using Docker CLI.
|
||||
@ -96,7 +85,7 @@ func NewComposeService(dockerCli command.Cli, options ...Option) (api.Compose, e
|
||||
}
|
||||
}
|
||||
if s.events == nil {
|
||||
s.events = progress.NewQuietWriter()
|
||||
s.events = &ignore{}
|
||||
}
|
||||
|
||||
// If custom streams were provided, wrap the Docker CLI to use them
|
||||
@ -204,7 +193,7 @@ func AlwaysOkPrompt() Prompt {
|
||||
|
||||
// WithEventProcessor configure component to get notified on Compose operation and progress events.
|
||||
// Typically used to configure a progress UI
|
||||
func WithEventProcessor(bus progress.EventProcessor) Option {
|
||||
func WithEventProcessor(bus api.EventProcessor) Option {
|
||||
return func(s *composeService) error {
|
||||
s.events = bus
|
||||
return nil
|
||||
@ -216,7 +205,7 @@ type composeService struct {
|
||||
// prompt is used to interact with user and confirm actions
|
||||
prompt Prompt
|
||||
// eventBus collects tasks execution events
|
||||
events progress.EventProcessor
|
||||
events api.EventProcessor
|
||||
|
||||
// Optional overrides for specific components (for SDK users)
|
||||
outStream io.Writer
|
||||
@ -278,13 +267,6 @@ func (s *composeService) stderr() *streams.Out {
|
||||
return s.dockerCli.Err()
|
||||
}
|
||||
|
||||
func (s *composeService) stdinfo() *streams.Out {
|
||||
if stdioToStdout {
|
||||
return s.stdout()
|
||||
}
|
||||
return s.stderr()
|
||||
}
|
||||
|
||||
// readCloserAdapter adapts io.Reader to io.ReadCloser
|
||||
type readCloserAdapter struct {
|
||||
r io.Reader
|
||||
|
||||
@ -41,7 +41,6 @@ import (
|
||||
|
||||
"github.com/docker/compose/v2/internal/tracing"
|
||||
"github.com/docker/compose/v2/pkg/api"
|
||||
"github.com/docker/compose/v2/pkg/progress"
|
||||
"github.com/docker/compose/v2/pkg/utils"
|
||||
)
|
||||
|
||||
@ -187,7 +186,7 @@ func (c *convergence) ensureService(ctx context.Context, project *types.Project,
|
||||
name := getContainerProgressName(ctr)
|
||||
switch ctr.State {
|
||||
case container.StateRunning:
|
||||
c.compose.events.On(progress.RunningEvent(name))
|
||||
c.compose.events.On(runningEvent(name))
|
||||
case container.StateCreated:
|
||||
case container.StateRestarting:
|
||||
case container.StateExited:
|
||||
@ -426,16 +425,16 @@ func getContainerProgressName(ctr container.Summary) string {
|
||||
return "Container " + getCanonicalContainerName(ctr)
|
||||
}
|
||||
|
||||
func containerEvents(containers Containers, eventFunc func(string) progress.Event) []progress.Event {
|
||||
events := []progress.Event{}
|
||||
func containerEvents(containers Containers, eventFunc func(string) api.Resource) []api.Resource {
|
||||
events := []api.Resource{}
|
||||
for _, ctr := range containers {
|
||||
events = append(events, eventFunc(getContainerProgressName(ctr)))
|
||||
}
|
||||
return events
|
||||
}
|
||||
|
||||
func containerReasonEvents(containers Containers, eventFunc func(string, string) progress.Event, reason string) []progress.Event {
|
||||
events := []progress.Event{}
|
||||
func containerReasonEvents(containers Containers, eventFunc func(string, string) api.Resource, reason string) []api.Resource {
|
||||
events := []api.Resource{}
|
||||
for _, ctr := range containers {
|
||||
events = append(events, eventFunc(getContainerProgressName(ctr), reason))
|
||||
}
|
||||
@ -461,7 +460,7 @@ func (s *composeService) waitDependencies(ctx context.Context, project *types.Pr
|
||||
}
|
||||
|
||||
waitingFor := containers.filter(isService(dep), isNotOneOff)
|
||||
s.events.On(containerEvents(waitingFor, progress.Waiting)...)
|
||||
s.events.On(containerEvents(waitingFor, waiting)...)
|
||||
if len(waitingFor) == 0 {
|
||||
if config.Required {
|
||||
return fmt.Errorf("%s is missing dependency %s", dependant, dep)
|
||||
@ -481,61 +480,61 @@ func (s *composeService) waitDependencies(ctx context.Context, project *types.Pr
|
||||
}
|
||||
switch config.Condition {
|
||||
case ServiceConditionRunningOrHealthy:
|
||||
healthy, err := s.isServiceHealthy(ctx, waitingFor, true)
|
||||
isHealthy, err := s.isServiceHealthy(ctx, waitingFor, true)
|
||||
if err != nil {
|
||||
if !config.Required {
|
||||
s.events.On(containerReasonEvents(waitingFor, progress.SkippedEvent,
|
||||
s.events.On(containerReasonEvents(waitingFor, skippedEvent,
|
||||
fmt.Sprintf("optional dependency %q is not running or is unhealthy", dep))...)
|
||||
logrus.Warnf("optional dependency %q is not running or is unhealthy: %s", dep, err.Error())
|
||||
return nil
|
||||
}
|
||||
return err
|
||||
}
|
||||
if healthy {
|
||||
s.events.On(containerEvents(waitingFor, progress.Healthy)...)
|
||||
if isHealthy {
|
||||
s.events.On(containerEvents(waitingFor, healthy)...)
|
||||
return nil
|
||||
}
|
||||
case types.ServiceConditionHealthy:
|
||||
healthy, err := s.isServiceHealthy(ctx, waitingFor, false)
|
||||
isHealthy, err := s.isServiceHealthy(ctx, waitingFor, false)
|
||||
if err != nil {
|
||||
if !config.Required {
|
||||
s.events.On(containerReasonEvents(waitingFor, progress.SkippedEvent,
|
||||
s.events.On(containerReasonEvents(waitingFor, skippedEvent,
|
||||
fmt.Sprintf("optional dependency %q failed to start", dep))...)
|
||||
logrus.Warnf("optional dependency %q failed to start: %s", dep, err.Error())
|
||||
return nil
|
||||
}
|
||||
s.events.On(containerEvents(waitingFor, func(s string) progress.Event {
|
||||
return progress.ErrorEventf(s, "dependency %s failed to start", dep)
|
||||
s.events.On(containerEvents(waitingFor, func(s string) api.Resource {
|
||||
return errorEventf(s, "dependency %s failed to start", dep)
|
||||
})...)
|
||||
return fmt.Errorf("dependency failed to start: %w", err)
|
||||
}
|
||||
if healthy {
|
||||
s.events.On(containerEvents(waitingFor, progress.Healthy)...)
|
||||
if isHealthy {
|
||||
s.events.On(containerEvents(waitingFor, healthy)...)
|
||||
return nil
|
||||
}
|
||||
case types.ServiceConditionCompletedSuccessfully:
|
||||
exited, code, err := s.isServiceCompleted(ctx, waitingFor)
|
||||
isExited, code, err := s.isServiceCompleted(ctx, waitingFor)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if exited {
|
||||
if isExited {
|
||||
if code == 0 {
|
||||
s.events.On(containerEvents(waitingFor, progress.Exited)...)
|
||||
s.events.On(containerEvents(waitingFor, exited)...)
|
||||
return nil
|
||||
}
|
||||
|
||||
messageSuffix := fmt.Sprintf("%q didn't complete successfully: exit %d", dep, code)
|
||||
if !config.Required {
|
||||
// optional -> mark as skipped & don't propagate error
|
||||
s.events.On(containerReasonEvents(waitingFor, progress.SkippedEvent,
|
||||
s.events.On(containerReasonEvents(waitingFor, skippedEvent,
|
||||
fmt.Sprintf("optional dependency %s", messageSuffix))...)
|
||||
logrus.Warnf("optional dependency %s", messageSuffix)
|
||||
return nil
|
||||
}
|
||||
|
||||
msg := fmt.Sprintf("service %s", messageSuffix)
|
||||
s.events.On(containerEvents(waitingFor, func(s string) progress.Event {
|
||||
return progress.ErrorEventf(s, "service %s", messageSuffix)
|
||||
s.events.On(containerEvents(waitingFor, func(s string) api.Resource {
|
||||
return errorEventf(s, "service %s", messageSuffix)
|
||||
})...)
|
||||
return errors.New(msg)
|
||||
}
|
||||
@ -599,19 +598,19 @@ func (s *composeService) createContainer(ctx context.Context, project *types.Pro
|
||||
name string, number int, opts createOptions,
|
||||
) (ctr container.Summary, err error) {
|
||||
eventName := "Container " + name
|
||||
s.events.On(progress.CreatingEvent(eventName))
|
||||
s.events.On(creatingEvent(eventName))
|
||||
ctr, err = s.createMobyContainer(ctx, project, service, name, number, nil, opts)
|
||||
if err != nil {
|
||||
if ctx.Err() == nil {
|
||||
s.events.On(progress.Event{
|
||||
s.events.On(api.Resource{
|
||||
ID: eventName,
|
||||
Status: progress.Error,
|
||||
Status: api.Error,
|
||||
Text: err.Error(),
|
||||
})
|
||||
}
|
||||
return ctr, err
|
||||
}
|
||||
s.events.On(progress.CreatedEvent(eventName))
|
||||
s.events.On(createdEvent(eventName))
|
||||
return ctr, nil
|
||||
}
|
||||
|
||||
@ -619,12 +618,12 @@ func (s *composeService) recreateContainer(ctx context.Context, project *types.P
|
||||
replaced container.Summary, inherit bool, timeout *time.Duration,
|
||||
) (created container.Summary, err error) {
|
||||
eventName := getContainerProgressName(replaced)
|
||||
s.events.On(progress.NewEvent(eventName, progress.Working, "Recreate"))
|
||||
s.events.On(newEvent(eventName, api.Working, "Recreate"))
|
||||
defer func() {
|
||||
if err != nil && ctx.Err() == nil {
|
||||
s.events.On(progress.Event{
|
||||
s.events.On(api.Resource{
|
||||
ID: eventName,
|
||||
Status: progress.Error,
|
||||
Status: api.Error,
|
||||
Text: err.Error(),
|
||||
})
|
||||
}
|
||||
@ -673,7 +672,7 @@ func (s *composeService) recreateContainer(ctx context.Context, project *types.P
|
||||
return created, err
|
||||
}
|
||||
|
||||
s.events.On(progress.NewEvent(eventName, progress.Done, "Recreated"))
|
||||
s.events.On(newEvent(eventName, api.Done, "Recreated"))
|
||||
return created, err
|
||||
}
|
||||
|
||||
@ -681,14 +680,14 @@ func (s *composeService) recreateContainer(ctx context.Context, project *types.P
|
||||
var startMx sync.Mutex
|
||||
|
||||
func (s *composeService) startContainer(ctx context.Context, ctr container.Summary) error {
|
||||
s.events.On(progress.NewEvent(getContainerProgressName(ctr), progress.Working, "Restart"))
|
||||
s.events.On(newEvent(getContainerProgressName(ctr), api.Working, "Restart"))
|
||||
startMx.Lock()
|
||||
defer startMx.Unlock()
|
||||
err := s.apiClient().ContainerStart(ctx, ctr.ID, container.StartOptions{})
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
s.events.On(progress.NewEvent(getContainerProgressName(ctr), progress.Done, "Restarted"))
|
||||
s.events.On(newEvent(getContainerProgressName(ctr), api.Done, "Restarted"))
|
||||
return nil
|
||||
}
|
||||
|
||||
@ -719,9 +718,9 @@ func (s *composeService) createMobyContainer(ctx context.Context, project *types
|
||||
return created, err
|
||||
}
|
||||
for _, warning := range response.Warnings {
|
||||
s.events.On(progress.Event{
|
||||
s.events.On(api.Resource{
|
||||
ID: service.Name,
|
||||
Status: progress.Warning,
|
||||
Status: api.Warning,
|
||||
Text: warning,
|
||||
})
|
||||
}
|
||||
@ -906,7 +905,7 @@ func (s *composeService) startService(ctx context.Context,
|
||||
}
|
||||
|
||||
eventName := getContainerProgressName(ctr)
|
||||
s.events.On(progress.StartingEvent(eventName))
|
||||
s.events.On(startingEvent(eventName))
|
||||
err = s.apiClient().ContainerStart(ctx, ctr.ID, container.StartOptions{})
|
||||
if err != nil {
|
||||
return err
|
||||
@ -919,7 +918,7 @@ func (s *composeService) startService(ctx context.Context,
|
||||
}
|
||||
}
|
||||
|
||||
s.events.On(progress.StartedEvent(eventName))
|
||||
s.events.On(startedEvent(eventName))
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
@ -25,7 +25,6 @@ import (
|
||||
"path/filepath"
|
||||
"strings"
|
||||
|
||||
"github.com/docker/compose/v2/pkg/progress"
|
||||
"golang.org/x/sync/errgroup"
|
||||
|
||||
"github.com/docker/cli/cli/command"
|
||||
@ -43,7 +42,7 @@ const (
|
||||
)
|
||||
|
||||
func (s *composeService) Copy(ctx context.Context, projectName string, options api.CopyOptions) error {
|
||||
return progress.Run(ctx, func(ctx context.Context) error {
|
||||
return Run(ctx, func(ctx context.Context) error {
|
||||
return s.copy(ctx, projectName, options)
|
||||
}, "copy", s.events)
|
||||
}
|
||||
@ -90,20 +89,20 @@ func (s *composeService) copy(ctx context.Context, projectName string, options a
|
||||
} else {
|
||||
msg = fmt.Sprintf("%s to %s:%s", srcPath, name, dstPath)
|
||||
}
|
||||
s.events.On(progress.Event{
|
||||
s.events.On(api.Resource{
|
||||
ID: name,
|
||||
Text: progress.StatusCopying,
|
||||
Text: api.StatusCopying,
|
||||
Details: msg,
|
||||
Status: progress.Working,
|
||||
Status: api.Working,
|
||||
})
|
||||
if err := copyFunc(ctx, ctr.ID, srcPath, dstPath, options); err != nil {
|
||||
return err
|
||||
}
|
||||
s.events.On(progress.Event{
|
||||
s.events.On(api.Resource{
|
||||
ID: name,
|
||||
Text: progress.StatusCopied,
|
||||
Text: api.StatusCopied,
|
||||
Details: msg,
|
||||
Status: progress.Done,
|
||||
Status: api.Done,
|
||||
})
|
||||
return nil
|
||||
})
|
||||
|
||||
@ -43,7 +43,6 @@ import (
|
||||
cdi "tags.cncf.io/container-device-interface/pkg/parser"
|
||||
|
||||
"github.com/docker/compose/v2/pkg/api"
|
||||
"github.com/docker/compose/v2/pkg/progress"
|
||||
)
|
||||
|
||||
type createOptions struct {
|
||||
@ -61,7 +60,7 @@ type createConfigs struct {
|
||||
}
|
||||
|
||||
func (s *composeService) Create(ctx context.Context, project *types.Project, createOpts api.CreateOptions) error {
|
||||
return progress.Run(ctx, func(ctx context.Context) error {
|
||||
return Run(ctx, func(ctx context.Context) error {
|
||||
return s.create(ctx, project, createOpts)
|
||||
}, "create", s.events)
|
||||
}
|
||||
@ -1394,14 +1393,14 @@ func (s *composeService) resolveOrCreateNetwork(ctx context.Context, project *ty
|
||||
}
|
||||
|
||||
networkEventName := fmt.Sprintf("Network %s", n.Name)
|
||||
s.events.On(progress.CreatingEvent(networkEventName))
|
||||
s.events.On(creatingEvent(networkEventName))
|
||||
|
||||
resp, err := s.apiClient().NetworkCreate(ctx, n.Name, createOpts)
|
||||
if err != nil {
|
||||
s.events.On(progress.ErrorEvent(networkEventName, err.Error()))
|
||||
s.events.On(errorEvent(networkEventName, err.Error()))
|
||||
return "", fmt.Errorf("failed to create network %s: %w", n.Name, err)
|
||||
}
|
||||
s.events.On(progress.CreatedEvent(networkEventName))
|
||||
s.events.On(createdEvent(networkEventName))
|
||||
|
||||
err = s.connectNetwork(ctx, n.Name, dangledContainers, nil)
|
||||
if err != nil {
|
||||
@ -1443,7 +1442,7 @@ func (s *composeService) removeDivergedNetwork(ctx context.Context, project *typ
|
||||
|
||||
err = s.apiClient().NetworkRemove(ctx, n.Name)
|
||||
eventName := fmt.Sprintf("Network %s", n.Name)
|
||||
s.events.On(progress.RemovedEvent(eventName))
|
||||
s.events.On(removedEvent(eventName))
|
||||
return containers, err
|
||||
}
|
||||
|
||||
@ -1619,7 +1618,7 @@ func (s *composeService) removeDivergedVolume(ctx context.Context, name string,
|
||||
|
||||
func (s *composeService) createVolume(ctx context.Context, volume types.VolumeConfig) error {
|
||||
eventName := fmt.Sprintf("Volume %s", volume.Name)
|
||||
s.events.On(progress.CreatingEvent(eventName))
|
||||
s.events.On(creatingEvent(eventName))
|
||||
hash, err := VolumeHash(volume)
|
||||
if err != nil {
|
||||
return err
|
||||
@ -1632,9 +1631,9 @@ func (s *composeService) createVolume(ctx context.Context, volume types.VolumeCo
|
||||
DriverOpts: volume.DriverOpts,
|
||||
})
|
||||
if err != nil {
|
||||
s.events.On(progress.ErrorEvent(eventName, err.Error()))
|
||||
s.events.On(errorEvent(eventName, err.Error()))
|
||||
return err
|
||||
}
|
||||
s.events.On(progress.CreatedEvent(eventName))
|
||||
s.events.On(createdEvent(eventName))
|
||||
return nil
|
||||
}
|
||||
|
||||
@ -25,7 +25,6 @@ import (
|
||||
"github.com/compose-spec/compose-go/v2/types"
|
||||
"github.com/containerd/errdefs"
|
||||
"github.com/docker/compose/v2/pkg/api"
|
||||
"github.com/docker/compose/v2/pkg/progress"
|
||||
"github.com/docker/compose/v2/pkg/utils"
|
||||
containerType "github.com/docker/docker/api/types/container"
|
||||
"github.com/docker/docker/api/types/filters"
|
||||
@ -38,7 +37,7 @@ import (
|
||||
type downOp func() error
|
||||
|
||||
func (s *composeService) Down(ctx context.Context, projectName string, options api.DownOptions) error {
|
||||
return progress.Run(ctx, func(ctx context.Context) error {
|
||||
return Run(ctx, func(ctx context.Context) error {
|
||||
return s.down(ctx, strings.ToLower(projectName), options)
|
||||
}, "down", s.events)
|
||||
}
|
||||
@ -210,7 +209,7 @@ func (s *composeService) removeNetwork(ctx context.Context, composeNetworkName s
|
||||
}
|
||||
|
||||
eventName := fmt.Sprintf("Network %s", name)
|
||||
s.events.On(progress.RemovingEvent(eventName))
|
||||
s.events.On(removingEvent(eventName))
|
||||
|
||||
var found int
|
||||
for _, net := range networks {
|
||||
@ -219,14 +218,14 @@ func (s *composeService) removeNetwork(ctx context.Context, composeNetworkName s
|
||||
}
|
||||
nw, err := s.apiClient().NetworkInspect(ctx, net.ID, network.InspectOptions{})
|
||||
if errdefs.IsNotFound(err) {
|
||||
s.events.On(progress.NewEvent(eventName, progress.Warning, "No resource found to remove"))
|
||||
s.events.On(newEvent(eventName, api.Warning, "No resource found to remove"))
|
||||
return nil
|
||||
}
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if len(nw.Containers) > 0 {
|
||||
s.events.On(progress.NewEvent(eventName, progress.Warning, "Resource is still in use"))
|
||||
s.events.On(newEvent(eventName, api.Warning, "Resource is still in use"))
|
||||
found++
|
||||
continue
|
||||
}
|
||||
@ -235,10 +234,10 @@ func (s *composeService) removeNetwork(ctx context.Context, composeNetworkName s
|
||||
if errdefs.IsNotFound(err) {
|
||||
continue
|
||||
}
|
||||
s.events.On(progress.ErrorEvent(eventName, err.Error()))
|
||||
s.events.On(errorEvent(eventName, err.Error()))
|
||||
return fmt.Errorf("failed to remove network %s: %w", name, err)
|
||||
}
|
||||
s.events.On(progress.RemovedEvent(eventName))
|
||||
s.events.On(removedEvent(eventName))
|
||||
found++
|
||||
}
|
||||
|
||||
@ -246,7 +245,7 @@ func (s *composeService) removeNetwork(ctx context.Context, composeNetworkName s
|
||||
// in practice, it's extremely unlikely for this to ever occur, as it'd
|
||||
// mean the network was present when we queried at the start of this
|
||||
// method but was then deleted by something else in the interim
|
||||
s.events.On(progress.NewEvent(eventName, progress.Warning, "No resource found to remove"))
|
||||
s.events.On(newEvent(eventName, api.Warning, "No resource found to remove"))
|
||||
return nil
|
||||
}
|
||||
return nil
|
||||
@ -254,18 +253,18 @@ func (s *composeService) removeNetwork(ctx context.Context, composeNetworkName s
|
||||
|
||||
func (s *composeService) removeImage(ctx context.Context, image string) error {
|
||||
id := fmt.Sprintf("Image %s", image)
|
||||
s.events.On(progress.NewEvent(id, progress.Working, "Removing"))
|
||||
s.events.On(newEvent(id, api.Working, "Removing"))
|
||||
_, err := s.apiClient().ImageRemove(ctx, image, imageapi.RemoveOptions{})
|
||||
if err == nil {
|
||||
s.events.On(progress.NewEvent(id, progress.Done, "Removed"))
|
||||
s.events.On(newEvent(id, api.Done, "Removed"))
|
||||
return nil
|
||||
}
|
||||
if errdefs.IsConflict(err) {
|
||||
s.events.On(progress.NewEvent(id, progress.Warning, "Resource is still in use"))
|
||||
s.events.On(newEvent(id, api.Warning, "Resource is still in use"))
|
||||
return nil
|
||||
}
|
||||
if errdefs.IsNotFound(err) {
|
||||
s.events.On(progress.NewEvent(id, progress.Done, "Warning: No resource found to remove"))
|
||||
s.events.On(newEvent(id, api.Done, "Warning: No resource found to remove"))
|
||||
return nil
|
||||
}
|
||||
return err
|
||||
@ -280,18 +279,18 @@ func (s *composeService) removeVolume(ctx context.Context, id string) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
s.events.On(progress.NewEvent(resource, progress.Working, "Removing"))
|
||||
s.events.On(newEvent(resource, api.Working, "Removing"))
|
||||
err = s.apiClient().VolumeRemove(ctx, id, true)
|
||||
if err == nil {
|
||||
s.events.On(progress.NewEvent(resource, progress.Done, "Removed"))
|
||||
s.events.On(newEvent(resource, api.Done, "Removed"))
|
||||
return nil
|
||||
}
|
||||
if errdefs.IsConflict(err) {
|
||||
s.events.On(progress.NewEvent(resource, progress.Warning, "Resource is still in use"))
|
||||
s.events.On(newEvent(resource, api.Warning, "Resource is still in use"))
|
||||
return nil
|
||||
}
|
||||
if errdefs.IsNotFound(err) {
|
||||
s.events.On(progress.NewEvent(resource, progress.Done, "Warning: No resource found to remove"))
|
||||
s.events.On(newEvent(resource, api.Done, "Warning: No resource found to remove"))
|
||||
return nil
|
||||
}
|
||||
return err
|
||||
@ -299,7 +298,7 @@ func (s *composeService) removeVolume(ctx context.Context, id string) error {
|
||||
|
||||
func (s *composeService) stopContainer(ctx context.Context, service *types.ServiceConfig, ctr containerType.Summary, timeout *time.Duration, listener api.ContainerEventListener) error {
|
||||
eventName := getContainerProgressName(ctr)
|
||||
s.events.On(progress.StoppingEvent(eventName))
|
||||
s.events.On(stoppingEvent(eventName))
|
||||
|
||||
if service != nil {
|
||||
for _, hook := range service.PreStop {
|
||||
@ -317,10 +316,10 @@ func (s *composeService) stopContainer(ctx context.Context, service *types.Servi
|
||||
timeoutInSecond := utils.DurationSecondToInt(timeout)
|
||||
err := s.apiClient().ContainerStop(ctx, ctr.ID, containerType.StopOptions{Timeout: timeoutInSecond})
|
||||
if err != nil {
|
||||
s.events.On(progress.ErrorEvent(eventName, "Error while Stopping"))
|
||||
s.events.On(errorEvent(eventName, "Error while Stopping"))
|
||||
return err
|
||||
}
|
||||
s.events.On(progress.StoppedEvent(eventName))
|
||||
s.events.On(stoppedEvent(eventName))
|
||||
return nil
|
||||
}
|
||||
|
||||
@ -348,22 +347,22 @@ func (s *composeService) stopAndRemoveContainer(ctx context.Context, ctr contain
|
||||
eventName := getContainerProgressName(ctr)
|
||||
err := s.stopContainer(ctx, service, ctr, timeout, nil)
|
||||
if errdefs.IsNotFound(err) {
|
||||
s.events.On(progress.RemovedEvent(eventName))
|
||||
s.events.On(removedEvent(eventName))
|
||||
return nil
|
||||
}
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
s.events.On(progress.RemovingEvent(eventName))
|
||||
s.events.On(removingEvent(eventName))
|
||||
err = s.apiClient().ContainerRemove(ctx, ctr.ID, containerType.RemoveOptions{
|
||||
Force: true,
|
||||
RemoveVolumes: volumes,
|
||||
})
|
||||
if err != nil && !errdefs.IsNotFound(err) && !errdefs.IsConflict(err) {
|
||||
s.events.On(progress.ErrorEvent(eventName, "Error while Removing"))
|
||||
s.events.On(errorEvent(eventName, "Error while Removing"))
|
||||
return err
|
||||
}
|
||||
s.events.On(progress.RemovedEvent(eventName))
|
||||
s.events.On(removedEvent(eventName))
|
||||
return nil
|
||||
}
|
||||
|
||||
|
||||
@ -24,12 +24,11 @@ import (
|
||||
|
||||
"github.com/docker/cli/cli/command"
|
||||
"github.com/docker/compose/v2/pkg/api"
|
||||
"github.com/docker/compose/v2/pkg/progress"
|
||||
"github.com/moby/sys/atomicwriter"
|
||||
)
|
||||
|
||||
func (s *composeService) Export(ctx context.Context, projectName string, options api.ExportOptions) error {
|
||||
return progress.Run(ctx, func(ctx context.Context) error {
|
||||
return Run(ctx, func(ctx context.Context) error {
|
||||
return s.export(ctx, projectName, options)
|
||||
}, "export", s.events)
|
||||
}
|
||||
@ -51,10 +50,10 @@ func (s *composeService) export(ctx context.Context, projectName string, options
|
||||
}
|
||||
|
||||
name := getCanonicalContainerName(container)
|
||||
s.events.On(progress.Event{
|
||||
s.events.On(api.Resource{
|
||||
ID: name,
|
||||
Text: progress.StatusExporting,
|
||||
Status: progress.Working,
|
||||
Text: api.StatusExporting,
|
||||
Status: api.Working,
|
||||
})
|
||||
|
||||
responseBody, err := s.apiClient().ContainerExport(ctx, container.ID)
|
||||
@ -64,7 +63,7 @@ func (s *composeService) export(ctx context.Context, projectName string, options
|
||||
|
||||
defer func() {
|
||||
if err := responseBody.Close(); err != nil {
|
||||
s.events.On(progress.ErrorEventf(name, "Failed to close response body: %s", err.Error()))
|
||||
s.events.On(errorEventf(name, "Failed to close response body: %s", err.Error()))
|
||||
}
|
||||
}()
|
||||
|
||||
@ -84,10 +83,10 @@ func (s *composeService) export(ctx context.Context, projectName string, options
|
||||
}
|
||||
}
|
||||
|
||||
s.events.On(progress.Event{
|
||||
s.events.On(api.Resource{
|
||||
ID: name,
|
||||
Text: progress.StatusExported,
|
||||
Status: progress.Done,
|
||||
Text: api.StatusExported,
|
||||
Status: api.Done,
|
||||
})
|
||||
|
||||
return nil
|
||||
|
||||
@ -18,18 +18,16 @@ package compose
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"strings"
|
||||
|
||||
"github.com/docker/docker/api/types/container"
|
||||
"golang.org/x/sync/errgroup"
|
||||
|
||||
"github.com/docker/compose/v2/pkg/api"
|
||||
"github.com/docker/compose/v2/pkg/progress"
|
||||
)
|
||||
|
||||
func (s *composeService) Kill(ctx context.Context, projectName string, options api.KillOptions) error {
|
||||
return progress.Run(ctx, func(ctx context.Context) error {
|
||||
return Run(ctx, func(ctx context.Context) error {
|
||||
return s.kill(ctx, strings.ToLower(projectName), options)
|
||||
}, "kill", s.events)
|
||||
}
|
||||
@ -55,21 +53,20 @@ func (s *composeService) kill(ctx context.Context, projectName string, options a
|
||||
containers = containers.filter(isService(project.ServiceNames()...))
|
||||
}
|
||||
if len(containers) == 0 {
|
||||
_, _ = fmt.Fprintf(s.stdinfo(), "no container to kill")
|
||||
return nil
|
||||
return api.ErrNoResources
|
||||
}
|
||||
|
||||
eg, ctx := errgroup.WithContext(ctx)
|
||||
containers.forEach(func(ctr container.Summary) {
|
||||
eg.Go(func() error {
|
||||
eventName := getContainerProgressName(ctr)
|
||||
s.events.On(progress.KillingEvent(eventName))
|
||||
s.events.On(killingEvent(eventName))
|
||||
err := s.apiClient().ContainerKill(ctx, ctr.ID, options.Signal)
|
||||
if err != nil {
|
||||
s.events.On(progress.ErrorEvent(eventName, "Error while Killing"))
|
||||
s.events.On(errorEvent(eventName, "Error while Killing"))
|
||||
return err
|
||||
}
|
||||
s.events.On(progress.KilledEvent(eventName))
|
||||
s.events.On(killedEvent(eventName))
|
||||
return nil
|
||||
})
|
||||
})
|
||||
|
||||
@ -29,7 +29,7 @@ import (
|
||||
"github.com/compose-spec/compose-go/v2/types"
|
||||
"github.com/containerd/errdefs"
|
||||
"github.com/docker/cli/cli-plugins/manager"
|
||||
"github.com/docker/compose/v2/pkg/progress"
|
||||
"github.com/docker/compose/v2/pkg/api"
|
||||
"github.com/spf13/cobra"
|
||||
"golang.org/x/sync/errgroup"
|
||||
)
|
||||
@ -101,10 +101,10 @@ func (m *modelAPI) Close() {
|
||||
m.cleanup()
|
||||
}
|
||||
|
||||
func (m *modelAPI) PullModel(ctx context.Context, model types.ModelConfig, quietPull bool, events progress.EventProcessor) error {
|
||||
events.On(progress.Event{
|
||||
func (m *modelAPI) PullModel(ctx context.Context, model types.ModelConfig, quietPull bool, events api.EventProcessor) error {
|
||||
events.On(api.Resource{
|
||||
ID: model.Name,
|
||||
Status: progress.Working,
|
||||
Status: api.Working,
|
||||
Text: "Pulling",
|
||||
})
|
||||
|
||||
@ -131,30 +131,30 @@ func (m *modelAPI) PullModel(ctx context.Context, model types.ModelConfig, quiet
|
||||
}
|
||||
|
||||
if !quietPull {
|
||||
events.On(progress.Event{
|
||||
events.On(api.Resource{
|
||||
ID: model.Name,
|
||||
Status: progress.Working,
|
||||
Text: progress.StatusPulling,
|
||||
Status: api.Working,
|
||||
Text: api.StatusPulling,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
err = cmd.Wait()
|
||||
if err != nil {
|
||||
events.On(progress.ErrorEvent(model.Name, err.Error()))
|
||||
events.On(errorEvent(model.Name, err.Error()))
|
||||
}
|
||||
events.On(progress.Event{
|
||||
events.On(api.Resource{
|
||||
ID: model.Name,
|
||||
Status: progress.Working,
|
||||
Text: progress.StatusPulled,
|
||||
Status: api.Working,
|
||||
Text: api.StatusPulled,
|
||||
})
|
||||
return err
|
||||
}
|
||||
|
||||
func (m *modelAPI) ConfigureModel(ctx context.Context, config types.ModelConfig, events progress.EventProcessor) error {
|
||||
events.On(progress.Event{
|
||||
func (m *modelAPI) ConfigureModel(ctx context.Context, config types.ModelConfig, events api.EventProcessor) error {
|
||||
events.On(api.Resource{
|
||||
ID: config.Name,
|
||||
Status: progress.Working,
|
||||
Status: api.Working,
|
||||
Text: "Configuring",
|
||||
})
|
||||
// configure [--context-size=<n>] MODEL [-- <runtime-flags...>]
|
||||
|
||||
@ -24,11 +24,10 @@ import (
|
||||
"golang.org/x/sync/errgroup"
|
||||
|
||||
"github.com/docker/compose/v2/pkg/api"
|
||||
"github.com/docker/compose/v2/pkg/progress"
|
||||
)
|
||||
|
||||
func (s *composeService) Pause(ctx context.Context, projectName string, options api.PauseOptions) error {
|
||||
return progress.Run(ctx, func(ctx context.Context) error {
|
||||
return Run(ctx, func(ctx context.Context) error {
|
||||
return s.pause(ctx, strings.ToLower(projectName), options)
|
||||
}, "pause", s.events)
|
||||
}
|
||||
@ -49,7 +48,7 @@ func (s *composeService) pause(ctx context.Context, projectName string, options
|
||||
err := s.apiClient().ContainerPause(ctx, container.ID)
|
||||
if err == nil {
|
||||
eventName := getContainerProgressName(container)
|
||||
s.events.On(progress.NewEvent(eventName, progress.Done, "Paused"))
|
||||
s.events.On(newEvent(eventName, api.Done, "Paused"))
|
||||
}
|
||||
return err
|
||||
})
|
||||
@ -58,7 +57,7 @@ func (s *composeService) pause(ctx context.Context, projectName string, options
|
||||
}
|
||||
|
||||
func (s *composeService) UnPause(ctx context.Context, projectName string, options api.PauseOptions) error {
|
||||
return progress.Run(ctx, func(ctx context.Context) error {
|
||||
return Run(ctx, func(ctx context.Context) error {
|
||||
return s.unPause(ctx, strings.ToLower(projectName), options)
|
||||
}, "unpause", s.events)
|
||||
}
|
||||
@ -79,7 +78,7 @@ func (s *composeService) unPause(ctx context.Context, projectName string, option
|
||||
err = s.apiClient().ContainerUnpause(ctx, ctr.ID)
|
||||
if err == nil {
|
||||
eventName := getContainerProgressName(ctr)
|
||||
s.events.On(progress.NewEvent(eventName, progress.Done, "Unpaused"))
|
||||
s.events.On(newEvent(eventName, api.Done, "Unpaused"))
|
||||
}
|
||||
return err
|
||||
})
|
||||
|
||||
@ -33,7 +33,7 @@ import (
|
||||
"github.com/containerd/errdefs"
|
||||
"github.com/docker/cli/cli-plugins/manager"
|
||||
"github.com/docker/cli/cli/config"
|
||||
"github.com/docker/compose/v2/pkg/progress"
|
||||
"github.com/docker/compose/v2/pkg/api"
|
||||
"github.com/sirupsen/logrus"
|
||||
"github.com/spf13/cobra"
|
||||
)
|
||||
@ -89,10 +89,10 @@ func (s *composeService) executePlugin(cmd *exec.Cmd, command string, service ty
|
||||
var action string
|
||||
switch command {
|
||||
case "up":
|
||||
s.events.On(progress.CreatingEvent(service.Name))
|
||||
s.events.On(creatingEvent(service.Name))
|
||||
action = "create"
|
||||
case "down":
|
||||
s.events.On(progress.RemovingEvent(service.Name))
|
||||
s.events.On(removingEvent(service.Name))
|
||||
action = "remove"
|
||||
default:
|
||||
return nil, fmt.Errorf("unsupported plugin command: %s", command)
|
||||
@ -124,10 +124,10 @@ func (s *composeService) executePlugin(cmd *exec.Cmd, command string, service ty
|
||||
}
|
||||
switch msg.Type {
|
||||
case ErrorType:
|
||||
s.events.On(progress.NewEvent(service.Name, progress.Error, msg.Message))
|
||||
s.events.On(newEvent(service.Name, api.Error, msg.Message))
|
||||
return nil, errors.New(msg.Message)
|
||||
case InfoType:
|
||||
s.events.On(progress.NewEvent(service.Name, progress.Working, msg.Message))
|
||||
s.events.On(newEvent(service.Name, api.Working, msg.Message))
|
||||
case SetEnvType:
|
||||
key, val, found := strings.Cut(msg.Message, "=")
|
||||
if !found {
|
||||
@ -143,14 +143,14 @@ func (s *composeService) executePlugin(cmd *exec.Cmd, command string, service ty
|
||||
|
||||
err = cmd.Wait()
|
||||
if err != nil {
|
||||
s.events.On(progress.ErrorEvent(service.Name, err.Error()))
|
||||
s.events.On(errorEvent(service.Name, err.Error()))
|
||||
return nil, fmt.Errorf("failed to %s service provider: %s", action, err.Error())
|
||||
}
|
||||
switch command {
|
||||
case "up":
|
||||
s.events.On(progress.CreatedEvent(service.Name))
|
||||
s.events.On(createdEvent(service.Name))
|
||||
case "down":
|
||||
s.events.On(progress.RemovedEvent(service.Name))
|
||||
s.events.On(removedEvent(service.Name))
|
||||
}
|
||||
return variables, nil
|
||||
}
|
||||
|
||||
176
pkg/compose/progress.go
Normal file
176
pkg/compose/progress.go
Normal file
@ -0,0 +1,176 @@
|
||||
/*
|
||||
Copyright 2020 Docker Compose CLI authors
|
||||
|
||||
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.
|
||||
*/
|
||||
|
||||
package compose
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
|
||||
"github.com/docker/compose/v2/pkg/api"
|
||||
)
|
||||
|
||||
type progressFunc func(context.Context) error
|
||||
|
||||
func Run(ctx context.Context, pf progressFunc, operation string, bus api.EventProcessor) error {
|
||||
bus.Start(ctx, operation)
|
||||
err := pf(ctx)
|
||||
bus.Done(operation, err != nil)
|
||||
return err
|
||||
}
|
||||
|
||||
// errorEvent creates a new Error Resource with message
|
||||
func errorEvent(id string, msg string) api.Resource {
|
||||
return api.Resource{
|
||||
ID: id,
|
||||
Status: api.Error,
|
||||
Text: api.StatusError,
|
||||
Details: msg,
|
||||
}
|
||||
}
|
||||
|
||||
// errorEventf creates a new Error Resource with format message
|
||||
func errorEventf(id string, msg string, args ...any) api.Resource {
|
||||
return errorEvent(id, fmt.Sprintf(msg, args...))
|
||||
}
|
||||
|
||||
// creatingEvent creates a new Create in progress Resource
|
||||
func creatingEvent(id string) api.Resource {
|
||||
return newEvent(id, api.Working, api.StatusCreating)
|
||||
}
|
||||
|
||||
// startingEvent creates a new Starting in progress Resource
|
||||
func startingEvent(id string) api.Resource {
|
||||
return newEvent(id, api.Working, api.StatusStarting)
|
||||
}
|
||||
|
||||
// startedEvent creates a new Started in progress Resource
|
||||
func startedEvent(id string) api.Resource {
|
||||
return newEvent(id, api.Done, api.StatusStarted)
|
||||
}
|
||||
|
||||
// waiting creates a new waiting event
|
||||
func waiting(id string) api.Resource {
|
||||
return newEvent(id, api.Working, api.StatusWaiting)
|
||||
}
|
||||
|
||||
// healthy creates a new healthy event
|
||||
func healthy(id string) api.Resource {
|
||||
return newEvent(id, api.Done, api.StatusHealthy)
|
||||
}
|
||||
|
||||
// exited creates a new exited event
|
||||
func exited(id string) api.Resource {
|
||||
return newEvent(id, api.Done, api.StatusExited)
|
||||
}
|
||||
|
||||
// restartingEvent creates a new Restarting in progress Resource
|
||||
func restartingEvent(id string) api.Resource {
|
||||
return newEvent(id, api.Working, api.StatusRestarting)
|
||||
}
|
||||
|
||||
// runningEvent creates a new Running in progress Resource
|
||||
func runningEvent(id string) api.Resource {
|
||||
return newEvent(id, api.Done, api.StatusRunning)
|
||||
}
|
||||
|
||||
// createdEvent creates a new Created (done) Resource
|
||||
func createdEvent(id string) api.Resource {
|
||||
return newEvent(id, api.Done, api.StatusCreated)
|
||||
}
|
||||
|
||||
// stoppingEvent creates a new Stopping in progress Resource
|
||||
func stoppingEvent(id string) api.Resource {
|
||||
return newEvent(id, api.Working, api.StatusStopping)
|
||||
}
|
||||
|
||||
// stoppedEvent creates a new Stopping in progress Resource
|
||||
func stoppedEvent(id string) api.Resource {
|
||||
return newEvent(id, api.Done, api.StatusStopped)
|
||||
}
|
||||
|
||||
// killingEvent creates a new Killing in progress Resource
|
||||
func killingEvent(id string) api.Resource {
|
||||
return newEvent(id, api.Working, api.StatusKilling)
|
||||
}
|
||||
|
||||
// killedEvent creates a new Killed in progress Resource
|
||||
func killedEvent(id string) api.Resource {
|
||||
return newEvent(id, api.Done, api.StatusKilled)
|
||||
}
|
||||
|
||||
// removingEvent creates a new Removing in progress Resource
|
||||
func removingEvent(id string) api.Resource {
|
||||
return newEvent(id, api.Working, api.StatusRemoving)
|
||||
}
|
||||
|
||||
// removedEvent creates a new removed (done) Resource
|
||||
func removedEvent(id string) api.Resource {
|
||||
return newEvent(id, api.Done, api.StatusRemoved)
|
||||
}
|
||||
|
||||
// buildingEvent creates a new Building in progress Resource
|
||||
func buildingEvent(id string) api.Resource {
|
||||
return newEvent("Image "+id, api.Working, api.StatusBuilding)
|
||||
}
|
||||
|
||||
// builtEvent creates a new built (done) Resource
|
||||
func builtEvent(id string) api.Resource {
|
||||
return newEvent("Image "+id, api.Done, api.StatusBuilt)
|
||||
}
|
||||
|
||||
// pullingEvent creates a new pulling (in progress) Resource
|
||||
func pullingEvent(id string) api.Resource {
|
||||
return newEvent("Image "+id, api.Working, api.StatusPulling)
|
||||
}
|
||||
|
||||
// pulledEvent creates a new pulled (done) Resource
|
||||
func pulledEvent(id string) api.Resource {
|
||||
return newEvent("Image "+id, api.Done, api.StatusPulled)
|
||||
}
|
||||
|
||||
// skippedEvent creates a new Skipped Resource
|
||||
func skippedEvent(id string, reason string) api.Resource {
|
||||
return api.Resource{
|
||||
ID: id,
|
||||
Status: api.Warning,
|
||||
Text: "Skipped: " + reason,
|
||||
}
|
||||
}
|
||||
|
||||
// newEvent new event
|
||||
func newEvent(id string, status api.EventStatus, text string, reason ...string) api.Resource {
|
||||
r := api.Resource{
|
||||
ID: id,
|
||||
Status: status,
|
||||
Text: text,
|
||||
}
|
||||
if len(reason) > 0 {
|
||||
r.Details = reason[0]
|
||||
}
|
||||
return r
|
||||
}
|
||||
|
||||
type ignore struct{}
|
||||
|
||||
func (q *ignore) Start(_ context.Context, _ string) {
|
||||
}
|
||||
|
||||
func (q *ignore) Done(_ string, _ bool) {
|
||||
}
|
||||
|
||||
func (q *ignore) On(_ ...api.Resource) {
|
||||
}
|
||||
@ -35,7 +35,6 @@ import (
|
||||
"github.com/docker/compose/v2/internal/oci"
|
||||
"github.com/docker/compose/v2/pkg/api"
|
||||
"github.com/docker/compose/v2/pkg/compose/transform"
|
||||
"github.com/docker/compose/v2/pkg/progress"
|
||||
"github.com/opencontainers/go-digest"
|
||||
"github.com/opencontainers/image-spec/specs-go"
|
||||
v1 "github.com/opencontainers/image-spec/specs-go/v1"
|
||||
@ -43,7 +42,7 @@ import (
|
||||
)
|
||||
|
||||
func (s *composeService) Publish(ctx context.Context, project *types.Project, repository string, options api.PublishOptions) error {
|
||||
return progress.Run(ctx, func(ctx context.Context) error {
|
||||
return Run(ctx, func(ctx context.Context) error {
|
||||
return s.publish(ctx, project, repository, options)
|
||||
}, "publish", s.events)
|
||||
}
|
||||
@ -71,10 +70,10 @@ func (s *composeService) publish(ctx context.Context, project *types.Project, re
|
||||
return err
|
||||
}
|
||||
|
||||
s.events.On(progress.Event{
|
||||
s.events.On(api.Resource{
|
||||
ID: repository,
|
||||
Text: "publishing",
|
||||
Status: progress.Working,
|
||||
Status: api.Working,
|
||||
})
|
||||
if logrus.IsLevelEnabled(logrus.DebugLevel) {
|
||||
logrus.Debug("publishing layers")
|
||||
@ -98,10 +97,10 @@ func (s *composeService) publish(ctx context.Context, project *types.Project, re
|
||||
|
||||
descriptor, err := oci.PushManifest(ctx, resolver, named, layers, options.OCIVersion)
|
||||
if err != nil {
|
||||
s.events.On(progress.Event{
|
||||
s.events.On(api.Resource{
|
||||
ID: repository,
|
||||
Text: "publishing",
|
||||
Status: progress.Error,
|
||||
Status: api.Error,
|
||||
})
|
||||
return err
|
||||
}
|
||||
@ -150,10 +149,10 @@ func (s *composeService) publish(ctx context.Context, project *types.Project, re
|
||||
}
|
||||
}
|
||||
}
|
||||
s.events.On(progress.Event{
|
||||
s.events.On(api.Resource{
|
||||
ID: repository,
|
||||
Text: "published",
|
||||
Status: progress.Done,
|
||||
Status: api.Done,
|
||||
})
|
||||
return nil
|
||||
}
|
||||
|
||||
@ -40,11 +40,10 @@ import (
|
||||
|
||||
"github.com/docker/compose/v2/internal/registry"
|
||||
"github.com/docker/compose/v2/pkg/api"
|
||||
"github.com/docker/compose/v2/pkg/progress"
|
||||
)
|
||||
|
||||
func (s *composeService) Pull(ctx context.Context, project *types.Project, options api.PullOptions) error {
|
||||
return progress.Run(ctx, func(ctx context.Context) error {
|
||||
return Run(ctx, func(ctx context.Context) error {
|
||||
return s.pull(ctx, project, options)
|
||||
}, "pull", s.events)
|
||||
}
|
||||
@ -67,9 +66,9 @@ func (s *composeService) pull(ctx context.Context, project *types.Project, opts
|
||||
i := 0
|
||||
for name, service := range project.Services {
|
||||
if service.Image == "" {
|
||||
s.events.On(progress.Event{
|
||||
s.events.On(api.Resource{
|
||||
ID: name,
|
||||
Status: progress.Done,
|
||||
Status: api.Done,
|
||||
Text: "Skipped",
|
||||
Details: "No image to be pulled",
|
||||
})
|
||||
@ -78,17 +77,17 @@ func (s *composeService) pull(ctx context.Context, project *types.Project, opts
|
||||
|
||||
switch service.PullPolicy {
|
||||
case types.PullPolicyNever, types.PullPolicyBuild:
|
||||
s.events.On(progress.Event{
|
||||
s.events.On(api.Resource{
|
||||
ID: "Image " + service.Image,
|
||||
Status: progress.Done,
|
||||
Status: api.Done,
|
||||
Text: "Skipped",
|
||||
})
|
||||
continue
|
||||
case types.PullPolicyMissing, types.PullPolicyIfNotPresent:
|
||||
if imageAlreadyPresent(service.Image, images) {
|
||||
s.events.On(progress.Event{
|
||||
s.events.On(api.Resource{
|
||||
ID: "Image " + service.Image,
|
||||
Status: progress.Done,
|
||||
Status: api.Done,
|
||||
Text: "Skipped",
|
||||
Details: "Image is already present locally",
|
||||
})
|
||||
@ -97,9 +96,9 @@ func (s *composeService) pull(ctx context.Context, project *types.Project, opts
|
||||
}
|
||||
|
||||
if service.Build != nil && opts.IgnoreBuildable {
|
||||
s.events.On(progress.Event{
|
||||
s.events.On(api.Resource{
|
||||
ID: "Image " + service.Image,
|
||||
Status: progress.Done,
|
||||
Status: api.Done,
|
||||
Text: "Skipped",
|
||||
Details: "Image can be built",
|
||||
})
|
||||
@ -122,7 +121,7 @@ func (s *composeService) pull(ctx context.Context, project *types.Project, opts
|
||||
}
|
||||
if !opts.IgnoreFailures && service.Build == nil {
|
||||
if s.dryRun {
|
||||
s.events.On(progress.ErrorEventf("Image "+service.Image,
|
||||
s.events.On(errorEventf("Image "+service.Image,
|
||||
"error pulling image: %s", service.Image))
|
||||
}
|
||||
// fail fast if image can't be pulled nor built
|
||||
@ -174,7 +173,7 @@ func getUnwrappedErrorMessage(err error) string {
|
||||
|
||||
func (s *composeService) pullServiceImage(ctx context.Context, service types.ServiceConfig, quietPull bool, defaultPlatform string) (string, error) {
|
||||
resource := "Image " + service.Image
|
||||
s.events.On(progress.PullingEvent(service.Image))
|
||||
s.events.On(pullingEvent(service.Image))
|
||||
ref, err := reference.ParseNormalizedNamed(service.Image)
|
||||
if err != nil {
|
||||
return "", err
|
||||
@ -196,9 +195,9 @@ func (s *composeService) pullServiceImage(ctx context.Context, service types.Ser
|
||||
})
|
||||
|
||||
if ctx.Err() != nil {
|
||||
s.events.On(progress.Event{
|
||||
s.events.On(api.Resource{
|
||||
ID: resource,
|
||||
Status: progress.Warning,
|
||||
Status: api.Warning,
|
||||
Text: "Interrupted",
|
||||
})
|
||||
return "", nil
|
||||
@ -207,16 +206,16 @@ func (s *composeService) pullServiceImage(ctx context.Context, service types.Ser
|
||||
// check if has error and the service has a build section
|
||||
// then the status should be warning instead of error
|
||||
if err != nil && service.Build != nil {
|
||||
s.events.On(progress.Event{
|
||||
s.events.On(api.Resource{
|
||||
ID: resource,
|
||||
Status: progress.Warning,
|
||||
Status: api.Warning,
|
||||
Text: getUnwrappedErrorMessage(err),
|
||||
})
|
||||
return "", err
|
||||
}
|
||||
|
||||
if err != nil {
|
||||
s.events.On(progress.ErrorEvent(resource, getUnwrappedErrorMessage(err)))
|
||||
s.events.On(errorEvent(resource, getUnwrappedErrorMessage(err)))
|
||||
return "", err
|
||||
}
|
||||
|
||||
@ -236,7 +235,7 @@ func (s *composeService) pullServiceImage(ctx context.Context, service types.Ser
|
||||
toPullProgressEvent(resource, jm, s.events)
|
||||
}
|
||||
}
|
||||
s.events.On(progress.PulledEvent(service.Image))
|
||||
s.events.On(pulledEvent(service.Image))
|
||||
|
||||
inspected, err := s.apiClient().ImageInspect(ctx, service.Image)
|
||||
if err != nil {
|
||||
@ -383,7 +382,7 @@ func isServiceImageToBuild(service types.ServiceConfig, services types.Services)
|
||||
|
||||
const (
|
||||
PreparingPhase = "Preparing"
|
||||
WaitingPhase = "Waiting"
|
||||
WaitingPhase = "waiting"
|
||||
PullingFsPhase = "Pulling fs layer"
|
||||
DownloadingPhase = "Downloading"
|
||||
DownloadCompletePhase = "Download complete"
|
||||
@ -393,7 +392,7 @@ const (
|
||||
PullCompletePhase = "Pull complete"
|
||||
)
|
||||
|
||||
func toPullProgressEvent(parent string, jm jsonmessage.JSONMessage, events progress.EventProcessor) {
|
||||
func toPullProgressEvent(parent string, jm jsonmessage.JSONMessage, events api.EventProcessor) {
|
||||
if jm.ID == "" || jm.Progress == nil {
|
||||
return
|
||||
}
|
||||
@ -403,7 +402,7 @@ func toPullProgressEvent(parent string, jm jsonmessage.JSONMessage, events progr
|
||||
total int64
|
||||
percent int
|
||||
current int64
|
||||
status = progress.Working
|
||||
status = api.Working
|
||||
)
|
||||
|
||||
text = jm.Progress.String()
|
||||
@ -420,22 +419,22 @@ func toPullProgressEvent(parent string, jm jsonmessage.JSONMessage, events progr
|
||||
}
|
||||
}
|
||||
case DownloadCompletePhase, AlreadyExistsPhase, PullCompletePhase:
|
||||
status = progress.Done
|
||||
status = api.Done
|
||||
percent = 100
|
||||
}
|
||||
|
||||
if strings.Contains(jm.Status, "Image is up to date") ||
|
||||
strings.Contains(jm.Status, "Downloaded newer image") {
|
||||
status = progress.Done
|
||||
status = api.Done
|
||||
percent = 100
|
||||
}
|
||||
|
||||
if jm.Error != nil {
|
||||
status = progress.Error
|
||||
status = api.Error
|
||||
text = jm.Error.Message
|
||||
}
|
||||
|
||||
events.On(progress.Event{
|
||||
events.On(api.Resource{
|
||||
ID: jm.ID,
|
||||
ParentID: parent,
|
||||
Current: current,
|
||||
|
||||
@ -33,14 +33,13 @@ import (
|
||||
|
||||
"github.com/docker/compose/v2/internal/registry"
|
||||
"github.com/docker/compose/v2/pkg/api"
|
||||
"github.com/docker/compose/v2/pkg/progress"
|
||||
)
|
||||
|
||||
func (s *composeService) Push(ctx context.Context, project *types.Project, options api.PushOptions) error {
|
||||
if options.Quiet {
|
||||
return s.push(ctx, project, options)
|
||||
}
|
||||
return progress.Run(ctx, func(ctx context.Context) error {
|
||||
return Run(ctx, func(ctx context.Context) error {
|
||||
return s.push(ctx, project, options)
|
||||
}, "push", s.events)
|
||||
}
|
||||
@ -54,9 +53,9 @@ func (s *composeService) push(ctx context.Context, project *types.Project, optio
|
||||
if options.ImageMandatory && service.Image == "" && service.Provider == nil {
|
||||
return fmt.Errorf("%q attribute is mandatory to push an image for service %q", "service.image", service.Name)
|
||||
}
|
||||
s.events.On(progress.Event{
|
||||
s.events.On(api.Resource{
|
||||
ID: service.Name,
|
||||
Status: progress.Done,
|
||||
Status: api.Done,
|
||||
Text: "Skipped",
|
||||
})
|
||||
continue
|
||||
@ -68,16 +67,16 @@ func (s *composeService) push(ctx context.Context, project *types.Project, optio
|
||||
|
||||
for _, tag := range tags {
|
||||
eg.Go(func() error {
|
||||
s.events.On(progress.NewEvent(tag, progress.Working, "Pushing"))
|
||||
s.events.On(newEvent(tag, api.Working, "Pushing"))
|
||||
err := s.pushServiceImage(ctx, tag, options.Quiet)
|
||||
if err != nil {
|
||||
if !options.IgnoreFailures {
|
||||
s.events.On(progress.NewEvent(tag, progress.Error, err.Error()))
|
||||
s.events.On(newEvent(tag, api.Error, err.Error()))
|
||||
return err
|
||||
}
|
||||
s.events.On(progress.NewEvent(tag, progress.Warning, err.Error()))
|
||||
s.events.On(newEvent(tag, api.Warning, err.Error()))
|
||||
} else {
|
||||
s.events.On(progress.NewEvent(tag, progress.Done, "Pushed"))
|
||||
s.events.On(newEvent(tag, api.Done, "Pushed"))
|
||||
}
|
||||
return nil
|
||||
})
|
||||
@ -129,24 +128,24 @@ func (s *composeService) pushServiceImage(ctx context.Context, tag string, quiet
|
||||
return nil
|
||||
}
|
||||
|
||||
func toPushProgressEvent(prefix string, jm jsonmessage.JSONMessage, events progress.EventProcessor) {
|
||||
func toPushProgressEvent(prefix string, jm jsonmessage.JSONMessage, events api.EventProcessor) {
|
||||
if jm.ID == "" {
|
||||
// skipped
|
||||
return
|
||||
}
|
||||
var (
|
||||
text string
|
||||
status = progress.Working
|
||||
status = api.Working
|
||||
total int64
|
||||
current int64
|
||||
percent int
|
||||
)
|
||||
if isDone(jm) {
|
||||
status = progress.Done
|
||||
status = api.Done
|
||||
percent = 100
|
||||
}
|
||||
if jm.Error != nil {
|
||||
status = progress.Error
|
||||
status = api.Error
|
||||
text = jm.Error.Message
|
||||
}
|
||||
if jm.Progress != nil {
|
||||
@ -160,7 +159,7 @@ func toPushProgressEvent(prefix string, jm jsonmessage.JSONMessage, events progr
|
||||
}
|
||||
}
|
||||
|
||||
events.On(progress.Event{
|
||||
events.On(api.Resource{
|
||||
ParentID: prefix,
|
||||
ID: jm.ID,
|
||||
Text: text,
|
||||
|
||||
@ -24,8 +24,6 @@ import (
|
||||
"github.com/docker/compose/v2/pkg/api"
|
||||
"github.com/docker/docker/api/types/container"
|
||||
"golang.org/x/sync/errgroup"
|
||||
|
||||
"github.com/docker/compose/v2/pkg/progress"
|
||||
)
|
||||
|
||||
func (s *composeService) Remove(ctx context.Context, projectName string, options api.RemoveOptions) error {
|
||||
@ -76,8 +74,7 @@ func (s *composeService) Remove(ctx context.Context, projectName string, options
|
||||
})
|
||||
|
||||
if len(names) == 0 {
|
||||
_, _ = fmt.Fprintln(s.stdinfo(), "No stopped containers")
|
||||
return nil
|
||||
return api.ErrNoResources
|
||||
}
|
||||
|
||||
msg := fmt.Sprintf("Going to remove %s", strings.Join(names, ", "))
|
||||
@ -92,7 +89,7 @@ func (s *composeService) Remove(ctx context.Context, projectName string, options
|
||||
return nil
|
||||
}
|
||||
}
|
||||
return progress.Run(ctx, func(ctx context.Context) error {
|
||||
return Run(ctx, func(ctx context.Context) error {
|
||||
return s.remove(ctx, stoppedContainers, options)
|
||||
}, "remove", s.events)
|
||||
}
|
||||
@ -102,13 +99,13 @@ func (s *composeService) remove(ctx context.Context, containers Containers, opti
|
||||
for _, ctr := range containers {
|
||||
eg.Go(func() error {
|
||||
eventName := getContainerProgressName(ctr)
|
||||
s.events.On(progress.RemovingEvent(eventName))
|
||||
s.events.On(removingEvent(eventName))
|
||||
err := s.apiClient().ContainerRemove(ctx, ctr.ID, container.RemoveOptions{
|
||||
RemoveVolumes: options.Volumes,
|
||||
Force: options.Force,
|
||||
})
|
||||
if err == nil {
|
||||
s.events.On(progress.RemovedEvent(eventName))
|
||||
s.events.On(removedEvent(eventName))
|
||||
}
|
||||
return err
|
||||
})
|
||||
|
||||
@ -22,14 +22,13 @@ import (
|
||||
|
||||
"github.com/compose-spec/compose-go/v2/types"
|
||||
"github.com/docker/compose/v2/pkg/api"
|
||||
"github.com/docker/compose/v2/pkg/progress"
|
||||
"github.com/docker/compose/v2/pkg/utils"
|
||||
"github.com/docker/docker/api/types/container"
|
||||
"golang.org/x/sync/errgroup"
|
||||
)
|
||||
|
||||
func (s *composeService) Restart(ctx context.Context, projectName string, options api.RestartOptions) error {
|
||||
return progress.Run(ctx, func(ctx context.Context) error {
|
||||
return Run(ctx, func(ctx context.Context) error {
|
||||
return s.restart(ctx, strings.ToLower(projectName), options)
|
||||
}, "restart", s.events)
|
||||
}
|
||||
@ -93,13 +92,13 @@ func (s *composeService) restart(ctx context.Context, projectName string, option
|
||||
}
|
||||
}
|
||||
eventName := getContainerProgressName(ctr)
|
||||
s.events.On(progress.RestartingEvent(eventName))
|
||||
s.events.On(restartingEvent(eventName))
|
||||
timeout := utils.DurationSecondToInt(options.Timeout)
|
||||
err = s.apiClient().ContainerRestart(ctx, ctr.ID, container.StopOptions{Timeout: timeout})
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
s.events.On(progress.StartedEvent(eventName))
|
||||
s.events.On(startedEvent(eventName))
|
||||
for _, hook := range def.PostStart {
|
||||
err = s.runHook(ctx, ctr, def, hook, nil)
|
||||
if err != nil {
|
||||
|
||||
@ -28,7 +28,6 @@ import (
|
||||
"github.com/docker/cli/cli"
|
||||
cmd "github.com/docker/cli/cli/command/container"
|
||||
"github.com/docker/compose/v2/pkg/api"
|
||||
"github.com/docker/compose/v2/pkg/progress"
|
||||
"github.com/docker/docker/pkg/stringid"
|
||||
)
|
||||
|
||||
@ -65,7 +64,7 @@ func (s *composeService) prepareRun(ctx context.Context, project *types.Project,
|
||||
return "", err
|
||||
}
|
||||
|
||||
err = progress.Run(ctx, func(ctx context.Context) error {
|
||||
err = Run(ctx, func(ctx context.Context) error {
|
||||
return s.startDependencies(ctx, project, opts)
|
||||
}, "run", s.events)
|
||||
if err != nil {
|
||||
|
||||
@ -21,11 +21,10 @@ import (
|
||||
"github.com/compose-spec/compose-go/v2/types"
|
||||
"github.com/docker/compose/v2/internal/tracing"
|
||||
"github.com/docker/compose/v2/pkg/api"
|
||||
"github.com/docker/compose/v2/pkg/progress"
|
||||
)
|
||||
|
||||
func (s *composeService) Scale(ctx context.Context, project *types.Project, options api.ScaleOptions) error {
|
||||
return progress.Run(ctx, tracing.SpanWrapFunc("project/scale", tracing.ProjectOptions(ctx, project), func(ctx context.Context) error {
|
||||
return Run(ctx, tracing.SpanWrapFunc("project/scale", tracing.ProjectOptions(ctx, project), func(ctx context.Context) error {
|
||||
err := s.create(ctx, project, api.CreateOptions{Services: options.Services})
|
||||
if err != nil {
|
||||
return err
|
||||
|
||||
@ -23,7 +23,6 @@ import (
|
||||
"strings"
|
||||
|
||||
"github.com/docker/compose/v2/pkg/api"
|
||||
"github.com/docker/compose/v2/pkg/progress"
|
||||
containerType "github.com/docker/docker/api/types/container"
|
||||
|
||||
"github.com/compose-spec/compose-go/v2/types"
|
||||
@ -31,7 +30,7 @@ import (
|
||||
)
|
||||
|
||||
func (s *composeService) Start(ctx context.Context, projectName string, options api.StartOptions) error {
|
||||
return progress.Run(ctx, func(ctx context.Context) error {
|
||||
return Run(ctx, func(ctx context.Context) error {
|
||||
return s.start(ctx, strings.ToLower(projectName), options, nil)
|
||||
}, "start", s.events)
|
||||
}
|
||||
|
||||
@ -22,11 +22,10 @@ import (
|
||||
"strings"
|
||||
|
||||
"github.com/docker/compose/v2/pkg/api"
|
||||
"github.com/docker/compose/v2/pkg/progress"
|
||||
)
|
||||
|
||||
func (s *composeService) Stop(ctx context.Context, projectName string, options api.StopOptions) error {
|
||||
return progress.Run(ctx, func(ctx context.Context) error {
|
||||
return Run(ctx, func(ctx context.Context) error {
|
||||
return s.stop(ctx, strings.ToLower(projectName), options, nil)
|
||||
}, "stop", s.events)
|
||||
}
|
||||
|
||||
@ -33,14 +33,13 @@ import (
|
||||
"github.com/docker/compose/v2/cmd/formatter"
|
||||
"github.com/docker/compose/v2/internal/tracing"
|
||||
"github.com/docker/compose/v2/pkg/api"
|
||||
"github.com/docker/compose/v2/pkg/progress"
|
||||
"github.com/eiannone/keyboard"
|
||||
"github.com/sirupsen/logrus"
|
||||
"golang.org/x/sync/errgroup"
|
||||
)
|
||||
|
||||
func (s *composeService) Up(ctx context.Context, project *types.Project, options api.UpOptions) error { //nolint:gocyclo
|
||||
err := progress.Run(ctx, tracing.SpanWrapFunc("project/up", tracing.ProjectOptions(ctx, project), func(ctx context.Context) error {
|
||||
err := Run(ctx, tracing.SpanWrapFunc("project/up", tracing.ProjectOptions(ctx, project), func(ctx context.Context) error {
|
||||
err := s.create(ctx, project, options.Create)
|
||||
if err != nil {
|
||||
return err
|
||||
@ -126,7 +125,7 @@ func (s *composeService) Up(ctx context.Context, project *types.Project, options
|
||||
first := true
|
||||
gracefulTeardown := func() {
|
||||
first = false
|
||||
fmt.Println("Gracefully Stopping... press Ctrl+C again to force")
|
||||
s.events.On(newEvent(api.ResourceCompose, api.Working, api.StatusStopping, "Gracefully Stopping... press Ctrl+C again to force"))
|
||||
eg.Go(func() error {
|
||||
err = s.stop(context.WithoutCancel(globalCtx), project.Name, api.StopOptions{
|
||||
Services: options.Create.Services,
|
||||
@ -162,7 +161,7 @@ func (s *composeService) Up(ctx context.Context, project *types.Project, options
|
||||
All: true,
|
||||
})
|
||||
// Ignore errors indicating that some of the containers were already stopped or removed.
|
||||
if errdefs.IsNotFound(err) || errdefs.IsConflict(err) {
|
||||
if errdefs.IsNotFound(err) || errdefs.IsConflict(err) || errors.Is(err, api.ErrNoResources) {
|
||||
return nil
|
||||
}
|
||||
|
||||
@ -205,7 +204,7 @@ func (s *composeService) Up(ctx context.Context, project *types.Project, options
|
||||
}
|
||||
once = false
|
||||
exitCode = event.ExitCode
|
||||
_, _ = fmt.Fprintln(s.stdinfo(), progress.ErrorColor("Aborting on container exit..."))
|
||||
s.events.On(newEvent(api.ResourceCompose, api.Working, api.StatusStopping, "Aborting on container exit..."))
|
||||
eg.Go(func() error {
|
||||
err = s.stop(context.WithoutCancel(globalCtx), project.Name, api.StopOptions{
|
||||
Services: options.Create.Services,
|
||||
|
||||
@ -33,9 +33,9 @@ import (
|
||||
"github.com/docker/compose/v2/internal/sync"
|
||||
"github.com/docker/compose/v2/internal/tracing"
|
||||
"github.com/docker/compose/v2/pkg/api"
|
||||
"github.com/docker/compose/v2/pkg/progress"
|
||||
cutils "github.com/docker/compose/v2/pkg/utils"
|
||||
"github.com/docker/compose/v2/pkg/watch"
|
||||
"github.com/moby/buildkit/util/progress/progressui"
|
||||
|
||||
"github.com/compose-spec/compose-go/v2/types"
|
||||
"github.com/compose-spec/compose-go/v2/utils"
|
||||
@ -472,7 +472,7 @@ func (t tarDockerClient) Exec(ctx context.Context, containerID string, cmd []str
|
||||
})
|
||||
}
|
||||
eg.Go(func() error {
|
||||
_, err := io.Copy(t.s.stdinfo(), conn.Reader)
|
||||
_, err := io.Copy(t.s.stdout(), conn.Reader)
|
||||
return err
|
||||
})
|
||||
|
||||
@ -613,7 +613,7 @@ func (s *composeService) rebuild(ctx context.Context, project *types.Project, se
|
||||
options.LogTo.Log(api.WatchLogger, fmt.Sprintf("Rebuilding service(s) %q after changes were detected...", services))
|
||||
// restrict the build to ONLY this service, not any of its dependencies
|
||||
options.Build.Services = services
|
||||
options.Build.Progress = progress.ModePlain
|
||||
options.Build.Progress = string(progressui.PlainMode)
|
||||
options.Build.Out = cutils.GetWriter(func(line string) {
|
||||
options.LogTo.Log(api.WatchLogger, line)
|
||||
})
|
||||
|
||||
@ -1,234 +0,0 @@
|
||||
/*
|
||||
Copyright 2020 Docker Compose CLI authors
|
||||
|
||||
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.
|
||||
*/
|
||||
|
||||
package progress
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
)
|
||||
|
||||
// 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
|
||||
// Warning means that the current task has warning
|
||||
Warning
|
||||
// Error means that the current task has errored
|
||||
Error
|
||||
)
|
||||
|
||||
const (
|
||||
StatusError = "Error"
|
||||
StatusCreating = "Creating"
|
||||
StatusStarting = "Starting"
|
||||
StatusStarted = "Started"
|
||||
StatusWaiting = "Waiting"
|
||||
StatusHealthy = "Healthy"
|
||||
StatusExited = "Exited"
|
||||
StatusRestarting = "Restarting"
|
||||
StatusRestarted = "Restarted"
|
||||
StatusRunning = "Running"
|
||||
StatusCreated = "Created"
|
||||
StatusStopping = "Stopping"
|
||||
StatusStopped = "Stopped"
|
||||
StatusKilling = "Killing"
|
||||
StatusKilled = "Killed"
|
||||
StatusRemoving = "Removing"
|
||||
StatusRemoved = "Removed"
|
||||
StatusBuilding = "Building"
|
||||
StatusBuilt = "Built"
|
||||
StatusPulling = "Pulling"
|
||||
StatusPulled = "Pulled"
|
||||
StatusCommitting = "Committing"
|
||||
StatusCommitted = "Committed"
|
||||
StatusCopying = "Copying"
|
||||
StatusCopied = "Copied"
|
||||
StatusExporting = "Exporting"
|
||||
StatusExported = "Exported"
|
||||
)
|
||||
|
||||
// Event represents a progress event.
|
||||
type Event struct {
|
||||
ID string
|
||||
ParentID string
|
||||
Text string
|
||||
Details string
|
||||
Status EventStatus
|
||||
Current int64
|
||||
Percent int
|
||||
Total int64
|
||||
}
|
||||
|
||||
func (e *Event) StatusText() string {
|
||||
switch e.Status {
|
||||
case Working:
|
||||
return "Working"
|
||||
case Warning:
|
||||
return "Warning"
|
||||
case Done:
|
||||
return "Done"
|
||||
default:
|
||||
return "Error"
|
||||
}
|
||||
}
|
||||
|
||||
// ErrorEvent creates a new Error Event with message
|
||||
func ErrorEvent(id string, msg string) Event {
|
||||
return Event{
|
||||
ID: id,
|
||||
Status: Error,
|
||||
Text: StatusError,
|
||||
Details: msg,
|
||||
}
|
||||
}
|
||||
|
||||
// ErrorEventf creates a new Error Event with format message
|
||||
func ErrorEventf(id string, msg string, args ...any) Event {
|
||||
return ErrorEvent(id, fmt.Sprintf(msg, args...))
|
||||
}
|
||||
|
||||
// CreatingEvent creates a new Create in progress Event
|
||||
func CreatingEvent(id string) Event {
|
||||
return NewEvent(id, Working, StatusCreating)
|
||||
}
|
||||
|
||||
// StartingEvent creates a new Starting in progress Event
|
||||
func StartingEvent(id string) Event {
|
||||
return NewEvent(id, Working, StatusStarting)
|
||||
}
|
||||
|
||||
// StartedEvent creates a new Started in progress Event
|
||||
func StartedEvent(id string) Event {
|
||||
return NewEvent(id, Done, StatusStarted)
|
||||
}
|
||||
|
||||
// Waiting creates a new waiting event
|
||||
func Waiting(id string) Event {
|
||||
return NewEvent(id, Working, StatusWaiting)
|
||||
}
|
||||
|
||||
// Healthy creates a new healthy event
|
||||
func Healthy(id string) Event {
|
||||
return NewEvent(id, Done, StatusHealthy)
|
||||
}
|
||||
|
||||
// Exited creates a new exited event
|
||||
func Exited(id string) Event {
|
||||
return NewEvent(id, Done, StatusExited)
|
||||
}
|
||||
|
||||
// RestartingEvent creates a new Restarting in progress Event
|
||||
func RestartingEvent(id string) Event {
|
||||
return NewEvent(id, Working, StatusRestarting)
|
||||
}
|
||||
|
||||
// RestartedEvent creates a new Restarted in progress Event
|
||||
func RestartedEvent(id string) Event {
|
||||
return NewEvent(id, Done, StatusRestarted)
|
||||
}
|
||||
|
||||
// RunningEvent creates a new Running in progress Event
|
||||
func RunningEvent(id string) Event {
|
||||
return NewEvent(id, Done, StatusRunning)
|
||||
}
|
||||
|
||||
// CreatedEvent creates a new Created (done) Event
|
||||
func CreatedEvent(id string) Event {
|
||||
return NewEvent(id, Done, StatusCreated)
|
||||
}
|
||||
|
||||
// StoppingEvent creates a new Stopping in progress Event
|
||||
func StoppingEvent(id string) Event {
|
||||
return NewEvent(id, Working, StatusStopping)
|
||||
}
|
||||
|
||||
// StoppedEvent creates a new Stopping in progress Event
|
||||
func StoppedEvent(id string) Event {
|
||||
return NewEvent(id, Done, StatusStopped)
|
||||
}
|
||||
|
||||
// KillingEvent creates a new Killing in progress Event
|
||||
func KillingEvent(id string) Event {
|
||||
return NewEvent(id, Working, StatusKilling)
|
||||
}
|
||||
|
||||
// KilledEvent creates a new Killed in progress Event
|
||||
func KilledEvent(id string) Event {
|
||||
return NewEvent(id, Done, StatusKilled)
|
||||
}
|
||||
|
||||
// RemovingEvent creates a new Removing in progress Event
|
||||
func RemovingEvent(id string) Event {
|
||||
return NewEvent(id, Working, StatusRemoving)
|
||||
}
|
||||
|
||||
// RemovedEvent creates a new removed (done) Event
|
||||
func RemovedEvent(id string) Event {
|
||||
return NewEvent(id, Done, StatusRemoved)
|
||||
}
|
||||
|
||||
// BuildingEvent creates a new Building in progress Event
|
||||
func BuildingEvent(id string) Event {
|
||||
return NewEvent("Image "+id, Working, StatusBuilding)
|
||||
}
|
||||
|
||||
// BuiltEvent creates a new built (done) Event
|
||||
func BuiltEvent(id string) Event {
|
||||
return NewEvent("Image "+id, Done, StatusBuilt)
|
||||
}
|
||||
|
||||
// PullingEvent creates a new pulling (in progress) Event
|
||||
func PullingEvent(id string) Event {
|
||||
return NewEvent("Image "+id, Working, StatusPulling)
|
||||
}
|
||||
|
||||
// PulledEvent creates a new pulled (done) Event
|
||||
func PulledEvent(id string) Event {
|
||||
return NewEvent("Image "+id, Done, StatusPulled)
|
||||
}
|
||||
|
||||
// SkippedEvent creates a new Skipped Event
|
||||
func SkippedEvent(id string, reason string) Event {
|
||||
return Event{
|
||||
ID: id,
|
||||
Status: Warning,
|
||||
Text: "Skipped: " + reason,
|
||||
}
|
||||
}
|
||||
|
||||
// NewEvent new event
|
||||
func NewEvent(id string, status EventStatus, text string) Event {
|
||||
return Event{
|
||||
ID: id,
|
||||
Status: status,
|
||||
Text: text,
|
||||
}
|
||||
}
|
||||
|
||||
// EventProcessor is notified about Compose operations and tasks
|
||||
type EventProcessor interface {
|
||||
// Start is triggered as a Compose operation is starting with context
|
||||
Start(ctx context.Context, operation string)
|
||||
// On notify about (sub)task and progress processing operation
|
||||
On(events ...Event)
|
||||
// Done is triggered as a Compose operation completed
|
||||
Done(operation string, success bool)
|
||||
}
|
||||
Loading…
x
Reference in New Issue
Block a user