Repository: docker/compose-cli
Pull request #1760 (2021-06-08 14:20:20)

code cleanup: splitWriter does not use service/container name
**What I did**
moved (and renamed) splitWriter into it's own go file
removed reference to container and service names which aren't used anymore.



Patch:
diff --git a/aci/compose.go b/aci/compose.go
index 83c74c5f..7712c818 100644
--- a/aci/compose.go
+++ b/aci/compose.go
@@ -22,7 +22,6 @@ import (
 	"net/http"
 
 	"github.com/compose-spec/compose-go/types"
-	"github.com/pkg/errors"
 	"github.com/sirupsen/logrus"
 
 	"github.com/docker/compose-cli/aci/convert"
@@ -30,7 +29,6 @@ import (
 	"github.com/docker/compose-cli/api/compose"
 	"github.com/docker/compose-cli/api/context/store"
 	"github.com/docker/compose-cli/api/errdefs"
-	"github.com/docker/compose-cli/api/progress"
 	"github.com/docker/compose-cli/utils/formatter"
 )
 
@@ -125,29 +123,21 @@ func (cs aciComposeService) warnKeepVolumeOnDown(ctx context.Context, projectNam
 }
 
 func (cs *aciComposeService) Down(ctx context.Context, projectName string, options compose.DownOptions) error {
-	if options.Volumes {
-		return errors.Wrap(errdefs.ErrNotImplemented, "--volumes option is not supported on ACI")
-	}
-	if options.Images != "" {
-		return errors.Wrap(errdefs.ErrNotImplemented, "--rmi option is not supported on ACI")
-	}
-	return progress.Run(ctx, func(ctx context.Context) error {
-		logrus.Debugf("Down on project with name %q", projectName)
-
-		if err := cs.warnKeepVolumeOnDown(ctx, projectName); err != nil {
-			return err
-		}
+	logrus.Debugf("Down on projectName with name %q", projectName)
 
-		cg, err := deleteACIContainerGroup(ctx, cs.ctx, projectName)
-		if err != nil {
-			return err
-		}
-		if cg.IsHTTPStatus(http.StatusNoContent) {
-			return errdefs.ErrNotFound
-		}
+	if err := cs.warnKeepVolumeOnDown(ctx, projectName); err != nil {
+		return err
+	}
 
+	cg, err := deleteACIContainerGroup(ctx, cs.ctx, projectName)
+	if err != nil {
 		return err
-	})
+	}
+	if cg.IsHTTPStatus(http.StatusNoContent) {
+		return errdefs.ErrNotFound
+	}
+
+	return err
 }
 
 func (cs *aciComposeService) Ps(ctx context.Context, projectName string, options compose.PsOptions) ([]compose.ContainerSummary, error) {
diff --git a/cli/cmd/compose/compose.go b/cli/cmd/compose/compose.go
index c3bb0559..4aecd3b0 100644
--- a/cli/cmd/compose/compose.go
+++ b/cli/cmd/compose/compose.go
@@ -218,7 +218,7 @@ func RootCommand(contextType string, backend compose.Service) *cobra.Command {
 
 	command.AddCommand(
 		upCommand(&opts, contextType, backend),
-		downCommand(&opts, backend),
+		downCommand(&opts, contextType, backend),
 		startCommand(&opts, backend),
 		restartCommand(&opts, backend),
 		stopCommand(&opts, backend),
diff --git a/cli/cmd/compose/down.go b/cli/cmd/compose/down.go
index c2191c2c..af42cfbd 100644
--- a/cli/cmd/compose/down.go
+++ b/cli/cmd/compose/down.go
@@ -25,6 +25,8 @@ import (
 	"github.com/spf13/cobra"
 
 	"github.com/docker/compose-cli/api/compose"
+	"github.com/docker/compose-cli/api/context/store"
+	"github.com/docker/compose-cli/api/progress"
 )
 
 type downOptions struct {
@@ -36,7 +38,7 @@ type downOptions struct {
 	images        string
 }
 
-func downCommand(p *projectOptions, backend compose.Service) *cobra.Command {
+func downCommand(p *projectOptions, contextType string, backend compose.Service) *cobra.Command {
 	opts := downOptions{
 		projectOptions: p,
 	}
@@ -61,33 +63,39 @@ func downCommand(p *projectOptions, backend compose.Service) *cobra.Command {
 	flags := downCmd.Flags()
 	flags.BoolVar(&opts.removeOrphans, "remove-orphans", false, "Remove containers for services not defined in the Compose file.")
 	flags.IntVarP(&opts.timeout, "timeout", "t", 10, "Specify a shutdown timeout in seconds")
-	flags.BoolVarP(&opts.volumes, "volumes", "v", false, " Remove named volumes declared in the `volumes` section of the Compose file and anonymous volumes attached to containers.")
-	flags.StringVar(&opts.images, "rmi", "", `Remove images used by services. "local" remove only images that don't have a custom tag ("local"|"all")`)
+
+	switch contextType {
+	case store.LocalContextType, store.DefaultContextType, store.EcsLocalSimulationContextType:
+		flags.BoolVarP(&opts.volumes, "volumes", "v", false, " Remove named volumes declared in the `volumes` section of the Compose file and anonymous volumes attached to containers.")
+		flags.StringVar(&opts.images, "rmi", "", `Remove images used by services. "local" remove only images that don't have a custom tag ("local"|"all")`)
+	}
 	return downCmd
 }
 
 func runDown(ctx context.Context, backend compose.Service, opts downOptions) error {
-	name := opts.ProjectName
-	var project *types.Project
-	if opts.ProjectName == "" {
-		p, err := opts.toProject(nil)
-		if err != nil {
-			return err
+	return progress.Run(ctx, func(ctx context.Context) error {
+		name := opts.ProjectName
+		var project *types.Project
+		if opts.ProjectName == "" {
+			p, err := opts.toProject(nil)
+			if err != nil {
+				return err
+			}
+			project = p
+			name = p.Name
 		}
-		project = p
-		name = p.Name
-	}
 
-	var timeout *time.Duration
-	if opts.timeChanged {
-		timeoutValue := time.Duration(opts.timeout) * time.Second
-		timeout = &timeoutValue
-	}
-	return backend.Down(ctx, name, compose.DownOptions{
-		RemoveOrphans: opts.removeOrphans,
-		Project:       project,
-		Timeout:       timeout,
-		Images:        opts.images,
-		Volumes:       opts.volumes,
+		var timeout *time.Duration
+		if opts.timeChanged {
+			timeoutValue := time.Duration(opts.timeout) * time.Second
+			timeout = &timeoutValue
+		}
+		return backend.Down(ctx, name, compose.DownOptions{
+			RemoveOrphans: opts.removeOrphans,
+			Project:       project,
+			Timeout:       timeout,
+			Images:        opts.images,
+			Volumes:       opts.volumes,
+		})
 	})
 }
diff --git a/cli/mobycli/exec.go b/cli/mobycli/exec.go
index f6f2cdc6..de01215c 100644
--- a/cli/mobycli/exec.go
+++ b/cli/mobycli/exec.go
@@ -106,13 +106,8 @@ func RunDocker(childExit chan bool, args ...string) error {
 				if cmd.Process == nil {
 					continue // can happen if receiving signal before the process is actually started
 				}
-				// In go1.14+, the go runtime issues SIGURG as an interrupt to
-				// support preemptable system calls on Linux. Since we can't
-				// forward that along we'll check that here.
-				if isRuntimeSig(sig) {
-					continue
-				}
-				_ = cmd.Process.Signal(sig)
+				// nolint errcheck
+				cmd.Process.Signal(sig)
 			case <-childExit:
 				return
 			}
diff --git a/cli/mobycli/exec_unix.go b/cli/mobycli/exec_unix.go
deleted file mode 100644
index 5d3f447b..00000000
--- a/cli/mobycli/exec_unix.go
+++ /dev/null
@@ -1,29 +0,0 @@
-// +build !windows
-
-/*
-   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 mobycli
-
-import (
-	"os"
-
-	"golang.org/x/sys/unix"
-)
-
-func isRuntimeSig(s os.Signal) bool {
-	return s == unix.SIGURG
-}
diff --git a/cli/mobycli/exec_windows.go b/cli/mobycli/exec_windows.go
deleted file mode 100644
index 9111ec57..00000000
--- a/cli/mobycli/exec_windows.go
+++ /dev/null
@@ -1,23 +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 mobycli
-
-import "os"
-
-func isRuntimeSig(s os.Signal) bool {
-	return false
-}
diff --git a/ecs/down.go b/ecs/down.go
index d7acecdd..95982989 100644
--- a/ecs/down.go
+++ b/ecs/down.go
@@ -19,26 +19,12 @@ package ecs
 import (
 	"context"
 
-	"github.com/pkg/errors"
-
 	"github.com/docker/compose-cli/api/compose"
-	"github.com/docker/compose-cli/api/errdefs"
+
 	"github.com/docker/compose-cli/api/progress"
 )
 
 func (b *ecsAPIService) Down(ctx context.Context, projectName string, options compose.DownOptions) error {
-	if options.Volumes {
-		return errors.Wrap(errdefs.ErrNotImplemented, "--volumes option is not supported on ECS")
-	}
-	if options.Images != "" {
-		return errors.Wrap(errdefs.ErrNotImplemented, "--rmi option is not supported on ECS")
-	}
-	return progress.Run(ctx, func(ctx context.Context) error {
-		return b.down(ctx, projectName)
-	})
-}
-
-func (b *ecsAPIService) down(ctx context.Context, projectName string) error {
 	resources, err := b.aws.ListStackResources(ctx, projectName)
 	if err != nil {
 		return err
diff --git a/go.mod b/go.mod
index 60889579..c44073b5 100644
--- a/go.mod
+++ b/go.mod
@@ -19,13 +19,13 @@ require (
 	github.com/awslabs/goformation/v4 v4.15.6
 	github.com/buger/goterm v1.0.0
 	github.com/cnabio/cnab-to-oci v0.3.1-beta1
-	github.com/compose-spec/compose-go v0.0.0-20210607122939-5d8ae5f41be5
+	github.com/compose-spec/compose-go v0.0.0-20210525111037-1dc9cd17f504
 	github.com/containerd/console v1.0.2
 	github.com/containerd/containerd v1.5.0
 	github.com/distribution/distribution/v3 v3.0.0-20210316161203-a01c71e2477e
 	github.com/docker/buildx v0.5.2-0.20210422185057-908a856079fc
-	github.com/docker/cli v20.10.7+incompatible
-	github.com/docker/docker v20.10.7+incompatible
+	github.com/docker/cli v20.10.6+incompatible
+	github.com/docker/docker v20.10.5+incompatible
 	github.com/docker/go-connections v0.4.0
 	github.com/docker/go-units v0.4.0
 	github.com/fatih/color v1.9.0 // indirect
@@ -45,7 +45,7 @@ require (
 	github.com/mattn/go-colorable v0.1.6 // indirect
 	github.com/mattn/go-ieproxy v0.0.1 // indirect
 	github.com/mattn/go-isatty v0.0.12
-	github.com/mattn/go-shellwords v1.0.12
+	github.com/mattn/go-shellwords v1.0.11
 	github.com/moby/buildkit v0.8.2-0.20210401015549-df49b648c8bf
 	github.com/moby/term v0.0.0-20201110203204-bea5bbe245bf
 	github.com/morikuni/aec v1.0.0
diff --git a/go.sum b/go.sum
index 02727a32..124684a4 100644
--- a/go.sum
+++ b/go.sum
@@ -247,8 +247,8 @@ github.com/cncf/udpa/go v0.0.0-20201120205902-5459f2c99403/go.mod h1:WmhPx2Nbnht
 github.com/cockroachdb/datadriven v0.0.0-20190809214429-80d97fb3cbaa/go.mod h1:zn76sxSg3SzpJ0PPJaLDCu+Bu0Lg3sKTORVIj19EIF8=
 github.com/codahale/hdrhistogram v0.0.0-20160425231609-f8ad88b59a58/go.mod h1:sE/e/2PUdi/liOCUjSTXgM1o87ZssimdTWN964YiIeI=
 github.com/codahale/hdrhistogram v0.0.0-20161010025455-3a0bb77429bd/go.mod h1:sE/e/2PUdi/liOCUjSTXgM1o87ZssimdTWN964YiIeI=
-github.com/compose-spec/compose-go v0.0.0-20210607122939-5d8ae5f41be5 h1:rpgjmlMCtn4UHWBUkOFzOIG4XBP28ZXbqG39/KjTVXU=
-github.com/compose-spec/compose-go v0.0.0-20210607122939-5d8ae5f41be5/go.mod h1:5V65rPnTvvQagtoMxTneJ2QicLq6ZRQQ7fOgPN226fo=
+github.com/compose-spec/compose-go v0.0.0-20210525111037-1dc9cd17f504 h1:K864sZH7r/11JlWEY7EsyX8MtVNo6C6My/3wS2X7D4w=
+github.com/compose-spec/compose-go v0.0.0-20210525111037-1dc9cd17f504/go.mod h1:6eIT9U2OgdHmkRD6szmqatCrWWEEUSwl/j2iJYH4jLo=
 github.com/containerd/aufs v0.0.0-20200908144142-dab0cbea06f4/go.mod h1:nukgQABAEopAHvB6j7cnP5zJ+/3aVcE7hCYqvIwAHyE=
 github.com/containerd/aufs v0.0.0-20201003224125-76a6863f2989/go.mod h1:AkGGQs9NM2vtYHaUen+NljV0/baGCAPELGm2q9ZXpWU=
 github.com/containerd/aufs v0.0.0-20210316121734-20793ff83c97/go.mod h1:kL5kd6KM5TzQjR79jljyi4olc1Vrx6XBlcyj3gNv2PU=
@@ -399,9 +399,8 @@ github.com/docker/buildx v0.5.2-0.20210422185057-908a856079fc/go.mod h1:T5sa7xGu
 github.com/docker/cli v0.0.0-20190925022749-754388324470/go.mod h1:JLrzqnKDaYBop7H2jaqPtU4hHvMKP+vjCwu2uszcLI8=
 github.com/docker/cli v0.0.0-20191017083524-a8ff7f821017/go.mod h1:JLrzqnKDaYBop7H2jaqPtU4hHvMKP+vjCwu2uszcLI8=
 github.com/docker/cli v20.10.5+incompatible/go.mod h1:JLrzqnKDaYBop7H2jaqPtU4hHvMKP+vjCwu2uszcLI8=
+github.com/docker/cli v20.10.6+incompatible h1:LAyI6Lnwv+AUjtp2ZyN1lxqXBtkeFUqm4H7CZMWZuP8=
 github.com/docker/cli v20.10.6+incompatible/go.mod h1:JLrzqnKDaYBop7H2jaqPtU4hHvMKP+vjCwu2uszcLI8=
-github.com/docker/cli v20.10.7+incompatible h1:pv/3NqibQKphWZiAskMzdz8w0PRbtTaEB+f6NwdU7Is=
-github.com/docker/cli v20.10.7+incompatible/go.mod h1:JLrzqnKDaYBop7H2jaqPtU4hHvMKP+vjCwu2uszcLI8=
 github.com/docker/compose-on-kubernetes v0.4.19-0.20190128150448-356b2919c496/go.mod h1:iT2pYfi580XlpaV4KmK0T6+4/9+XoKmk/fhoDod1emE=
 github.com/docker/distribution v0.0.0-20190905152932-14b96e55d84c/go.mod h1:0+TTO4EOBfRPhZXAeF1Vu+W3hHZ8eLp8PgKVZlcvtFY=
 github.com/docker/distribution v2.6.0-rc.1.0.20180327202408-83389a148052+incompatible/go.mod h1:J2gT2udsDAN96Uj4KfcMRqY0/ypR+oyYUYmja8H+y+w=
@@ -414,9 +413,8 @@ github.com/docker/docker v1.4.2-0.20180531152204-71cd53e4a197/go.mod h1:eEKB0N0r
 github.com/docker/docker v1.4.2-0.20181229214054-f76d6a078d88/go.mod h1:eEKB0N0r5NX/I1kEveEz05bcu8tLC/8azJZsviup8Sk=
 github.com/docker/docker v17.12.0-ce-rc1.0.20200618181300-9dc6525e6118+incompatible/go.mod h1:eEKB0N0r5NX/I1kEveEz05bcu8tLC/8azJZsviup8Sk=
 github.com/docker/docker v17.12.0-ce-rc1.0.20200730172259-9f28837c1d93+incompatible/go.mod h1:eEKB0N0r5NX/I1kEveEz05bcu8tLC/8azJZsviup8Sk=
+github.com/docker/docker v20.10.5+incompatible h1:o5WL5onN4awYGwrW7+oTn5x9AF2prw7V0Ox8ZEkoCdg=
 github.com/docker/docker v20.10.5+incompatible/go.mod h1:eEKB0N0r5NX/I1kEveEz05bcu8tLC/8azJZsviup8Sk=
-github.com/docker/docker v20.10.7+incompatible h1:Z6O9Nhsjv+ayUEeI1IojKbYcsGdgYSNqxe1s2MYzUhQ=
-github.com/docker/docker v20.10.7+incompatible/go.mod h1:eEKB0N0r5NX/I1kEveEz05bcu8tLC/8azJZsviup8Sk=
 github.com/docker/docker-credential-helpers v0.6.3/go.mod h1:WRaJzqw3CTB9bk10avuGsjVBZsD05qeibJ1/TYlvc0Y=
 github.com/docker/docker-credential-helpers v0.6.4-0.20210125172408-38bea2ce277a h1:1DndKi+f9qs3AexOfI6di7RkWcWlNHzdqHu1cX19/ac=
 github.com/docker/docker-credential-helpers v0.6.4-0.20210125172408-38bea2ce277a/go.mod h1:ofX3UI0Gz1TteYBjtgs07O36Pyasyp66D2uKT7H8W1c=
@@ -910,9 +908,8 @@ github.com/mattn/go-runewidth v0.0.2/go.mod h1:LwmH8dsx7+W8Uxz3IHJYH5QSwggIsqBzp
 github.com/mattn/go-runewidth v0.0.4 h1:2BvfKmzob6Bmd4YsL0zygOqfdFnK7GR4QL06Do4/p7Y=
 github.com/mattn/go-runewidth v0.0.4/go.mod h1:LwmH8dsx7+W8Uxz3IHJYH5QSwggIsqBzpuz5H//U1FU=
 github.com/mattn/go-shellwords v1.0.3/go.mod h1:3xCvwCdWdlDJUrvuMn7Wuy9eWs4pE8vqg+NOMyg4B2o=
+github.com/mattn/go-shellwords v1.0.11 h1:vCoR9VPpsk/TZFW2JwK5I9S0xdrtUq2bph6/YjEPnaw=
 github.com/mattn/go-shellwords v1.0.11/go.mod h1:EZzvwXDESEeg03EKmM+RmDnNOPKG4lLtQsUlTZDWQ8Y=
-github.com/mattn/go-shellwords v1.0.12 h1:M2zGm7EW6UQJvDeQxo4T51eKPurbeFbe8WtebGE2xrk=
-github.com/mattn/go-shellwords v1.0.12/go.mod h1:EZzvwXDESEeg03EKmM+RmDnNOPKG4lLtQsUlTZDWQ8Y=
 github.com/mattn/go-sqlite3 v1.10.0/go.mod h1:FPy6KqzDD04eiIsT53CuJW3U88zkxoIYsOqkbpncsNc=
 github.com/mattn/go-sqlite3 v1.11.0/go.mod h1:FPy6KqzDD04eiIsT53CuJW3U88zkxoIYsOqkbpncsNc=
 github.com/mattn/go-sqlite3 v1.12.0/go.mod h1:FPy6KqzDD04eiIsT53CuJW3U88zkxoIYsOqkbpncsNc=
diff --git a/kube/client/client.go b/kube/client/client.go
index 6c4d6eb0..a99e8e3b 100644
--- a/kube/client/client.go
+++ b/kube/client/client.go
@@ -96,8 +96,8 @@ func (kc *KubeClient) GetLogs(ctx context.Context, projectName string, consumer
 	for _, pod := range pods.Items {
 		request := kc.client.CoreV1().Pods(kc.namespace).GetLogs(pod.Name, &corev1.PodLogOptions{Follow: follow})
 		service := pod.Labels[compose.ServiceTag]
-		w := utils.GetWriter(pod.Name, service, func(event compose.ContainerEvent) {
-			consumer.Log(event.Container, event.Service, event.Line)
+		w := utils.GetWriter(func(line string) {
+			consumer.Log(pod.Name, service, line)
 		})
 
 		eg.Go(func() error {
diff --git a/kube/compose.go b/kube/compose.go
index 389c7eae..ac9a46b5 100644
--- a/kube/compose.go
+++ b/kube/compose.go
@@ -24,7 +24,6 @@ import (
 	"strings"
 
 	"github.com/compose-spec/compose-go/types"
-	"github.com/pkg/errors"
 
 	"github.com/docker/compose-cli/api/compose"
 	apicontext "github.com/docker/compose-cli/api/context"
@@ -111,19 +110,8 @@ func (s *composeService) Up(ctx context.Context, project *types.Project, options
 
 // Down executes the equivalent to a `compose down`
 func (s *composeService) Down(ctx context.Context, projectName string, options compose.DownOptions) error {
-	if options.Volumes {
-		return errors.Wrap(errdefs.ErrNotImplemented, "--volumes option is not supported on Kubernetes")
-	}
-	if options.Images != "" {
-		return errors.Wrap(errdefs.ErrNotImplemented, "--rmi option is not supported on Kubernetes")
-	}
-	return progress.Run(ctx, func(ctx context.Context) error {
-		return s.down(ctx, projectName, options)
-	})
-}
-
-func (s *composeService) down(ctx context.Context, projectName string, options compose.DownOptions) error {
 	w := progress.ContextWriter(ctx)
+
 	eventName := fmt.Sprintf("Remove %s", projectName)
 	w.Event(progress.CreatingEvent(eventName))
 
diff --git a/local/compose/attach.go b/local/compose/attach.go
index fc8f6739..1114be9b 100644
--- a/local/compose/attach.go
+++ b/local/compose/attach.go
@@ -58,8 +58,7 @@ func (s *composeService) attach(ctx context.Context, project *types.Project, lis
 
 func (s *composeService) attachContainer(ctx context.Context, container moby.Container, listener compose.ContainerEventListener, project *types.Project) error {
 	serviceName := container.Labels[serviceLabel]
-	w := utils.GetWriter(getContainerNameWithoutProject(container), serviceName, listener)
-
+	containerName := getContainerNameWithoutProject(container)
 	service, err := project.GetService(serviceName)
 	if err != nil {
 		return err
@@ -67,10 +66,18 @@ func (s *composeService) attachContainer(ctx context.Context, container moby.Con
 
 	listener(compose.ContainerEvent{
 		Type:      compose.ContainerEventAttach,
-		Container: getContainerNameWithoutProject(container),
-		Service:   container.Labels[serviceLabel],
+		Container: containerName,
+		Service:   serviceName,
 	})
 
+	w := utils.GetWriter(func(line string) {
+		listener(compose.ContainerEvent{
+			Type:      compose.ContainerEventLog,
+			Container: containerName,
+			Service:   serviceName,
+			Line:      line,
+		})
+	})
 	_, err = s.attachContainerStreams(ctx, container.ID, service.Tty, nil, w)
 	return err
 }
diff --git a/local/compose/down.go b/local/compose/down.go
index 1464ec35..0d4a9220 100644
--- a/local/compose/down.go
+++ b/local/compose/down.go
@@ -36,12 +36,6 @@ import (
 type downOp func() error
 
 func (s *composeService) Down(ctx context.Context, projectName string, options compose.DownOptions) error {
-	return progress.Run(ctx, func(ctx context.Context) error {
-		return s.down(ctx, projectName, options)
-	})
-}
-
-func (s *composeService) down(ctx context.Context, projectName string, options compose.DownOptions) error {
 	w := progress.ContextWriter(ctx)
 	resourceToRemove := false
 
diff --git a/local/compose/logs.go b/local/compose/logs.go
index c708280e..5cb55336 100644
--- a/local/compose/logs.go
+++ b/local/compose/logs.go
@@ -57,8 +57,8 @@ func (s *composeService) Logs(ctx context.Context, projectName string, consumer
 			defer r.Close() // nolint errcheck
 
 			name := getContainerNameWithoutProject(c)
-			w := utils.GetWriter(name, service, func(event compose.ContainerEvent) {
-				consumer.Log(name, service, event.Line)
+			w := utils.GetWriter(func(line string) {
+				consumer.Log(name, service, line)
 			})
 			if container.Config.Tty {
 				_, err = io.Copy(w, r)
diff --git a/local/compose/pull.go b/local/compose/pull.go
index cc8d16e5..bdb573cc 100644
--- a/local/compose/pull.go
+++ b/local/compose/pull.go
@@ -162,31 +162,23 @@ func (s *composeService) pullRequiredImages(ctx context.Context, project *types.
 		info.IndexServerAddress = registry.IndexServer
 	}
 
-	var needPull []types.ServiceConfig
-	for _, service := range project.Services {
-		if service.Image == "" {
-			continue
-		}
-		switch service.PullPolicy {
-		case "", types.PullPolicyMissing, types.PullPolicyIfNotPresent:
-			if _, ok := images[service.Image]; ok {
-				continue
-			}
-		case types.PullPolicyNever, types.PullPolicyBuild:
-			continue
-		case types.PullPolicyAlways:
-			// force pull
-		}
-		needPull = append(needPull, service)
-	}
-	if len(needPull) == 0 {
-		return nil
-	}
-
 	return progress.Run(ctx, func(ctx context.Context) error {
 		w := progress.ContextWriter(ctx)
 		eg, ctx := errgroup.WithContext(ctx)
-		for _, service := range needPull {
+		for _, service := range project.Services {
+			if service.Image == "" {
+				continue
+			}
+			switch service.PullPolicy {
+			case types.PullPolicyMissing, types.PullPolicyIfNotPresent:
+				if _, ok := images[service.Image]; ok {
+					continue
+				}
+			case types.PullPolicyNever, types.PullPolicyBuild:
+				continue
+			case types.PullPolicyAlways:
+				// force pull
+			}
 			service := service
 			eg.Go(func() error {
 				err := s.pullServiceImage(ctx, service, info, s.configFile, w, quietPull)
diff --git a/utils/logs.go b/utils/logs.go
index 1c918d75..793ece9d 100644
--- a/utils/logs.go
+++ b/utils/logs.go
@@ -17,9 +17,6 @@
 package utils
 
 import (
-	"bytes"
-	"io"
-
 	"github.com/docker/compose-cli/api/compose"
 )
 
@@ -60,57 +57,3 @@ func (a *allowListLogConsumer) Register(name string) {
 		a.delegate.Register(name)
 	}
 }
-
-// GetWriter creates a io.Writer that will actually split by line and format by LogConsumer
-func GetWriter(container, service string, events compose.ContainerEventListener) io.WriteCloser {
-	return &splitBuffer{
-		buffer:    bytes.Buffer{},
-		service:   service,
-		container: container,
-		consumer:  events,
-	}
-}
-
-type splitBuffer struct {
-	buffer    bytes.Buffer
-	service   string
-	container string
-	consumer  compose.ContainerEventListener
-}
-
-// Write implements io.Writer. joins all input, splits on the separator and yields each chunk
-func (s *splitBuffer) Write(b []byte) (int, error) {
-	n, err := s.buffer.Write(b)
-	if err != nil {
-		return n, err
-	}
-	for {
-		b = s.buffer.Bytes()
-		index := bytes.Index(b, []byte{'\n'})
-		if index < 0 {
-			break
-		}
-		line := s.buffer.Next(index + 1)
-		s.consumer(compose.ContainerEvent{
-			Type:      compose.ContainerEventLog,
-			Service:   s.service,
-			Container: s.container,
-			Line:      string(line[:len(line)-1]),
-		})
-	}
-	return n, nil
-}
-
-func (s *splitBuffer) Close() error {
-	b := s.buffer.Bytes()
-	if len(b) == 0 {
-		return nil
-	}
-	s.consumer(compose.ContainerEvent{
-		Type:      compose.ContainerEventLog,
-		Service:   s.service,
-		Container: s.container,
-		Line:      string(b),
-	})
-	return nil
-}
diff --git a/utils/writer.go b/utils/writer.go
new file mode 100644
index 00000000..83f0bf5c
--- /dev/null
+++ b/utils/writer.go
@@ -0,0 +1,62 @@
+/*
+   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 utils
+
+import (
+	"bytes"
+	"io"
+)
+
+// GetWriter creates a io.Writer that will actually split by line and format by LogConsumer
+func GetWriter(consumer func(string)) io.WriteCloser {
+	return &splitWriter{
+		buffer:   bytes.Buffer{},
+		consumer: consumer,
+	}
+}
+
+type splitWriter struct {
+	buffer   bytes.Buffer
+	consumer func(string)
+}
+
+// Write implements io.Writer. joins all input, splits on the separator and yields each chunk
+func (s *splitWriter) Write(b []byte) (int, error) {
+	n, err := s.buffer.Write(b)
+	if err != nil {
+		return n, err
+	}
+	for {
+		b = s.buffer.Bytes()
+		index := bytes.Index(b, []byte{'\n'})
+		if index < 0 {
+			break
+		}
+		line := s.buffer.Next(index + 1)
+		s.consumer(string(line[:len(line)-1]))
+	}
+	return n, nil
+}
+
+func (s *splitWriter) Close() error {
+	b := s.buffer.Bytes()
+	if len(b) == 0 {
+		return nil
+	}
+	s.consumer(string(b))
+	return nil
+}


Test patch:
diff --git a/utils/logs_test.go b/utils/writer_test.go
similarity index 87%
rename from utils/logs_test.go
rename to utils/writer_test.go
index f0eaf835..7eedb844 100644
--- a/utils/logs_test.go
+++ b/utils/writer_test.go
@@ -20,14 +20,12 @@ import (
 	"testing"
 
 	"gotest.tools/v3/assert"
-
-	"github.com/docker/compose-cli/api/compose"
 )
 
 func TestSplitWriter(t *testing.T) {
 	var lines []string
-	w := GetWriter("container", "service", func(event compose.ContainerEvent) {
-		lines = append(lines, event.Line)
+	w := GetWriter(func(line string) {
+		lines = append(lines, line)
 	})
 	w.Write([]byte("h"))        //nolint: errcheck
 	w.Write([]byte("e"))        //nolint: errcheck