diff --git a/cmd/compose/compose_test.go b/cmd/compose/compose_test.go index 708929ff8c..de5bc02427 100644 --- a/cmd/compose/compose_test.go +++ b/cmd/compose/compose_test.go @@ -17,10 +17,16 @@ package compose import ( + "fmt" + "strings" "testing" "github.com/compose-spec/compose-go/v2/types" + "github.com/moby/moby/client" + "go.uber.org/mock/gomock" "gotest.tools/v3/assert" + + "github.com/docker/compose/v5/pkg/mocks" ) func TestFilterServices(t *testing.T) { @@ -53,3 +59,44 @@ func TestFilterServices(t *testing.T) { _, err = p.GetService("zot") assert.NilError(t, err) } + +func TestUpReturnsDockerConnectionErrorBeforeConfigPathFallback(t *testing.T) { + ctrl := gomock.NewController(t) + defer ctrl.Finish() + + wd := t.TempDir() + t.Chdir(wd) + t.Setenv("COMPOSE_FILE", "") + + apiClient := mocks.NewMockAPIClient(ctrl) + apiClient.EXPECT(). + Ping(gomock.Any(), client.PingOptions{NegotiateAPIVersion: true}). + Return(client.PingResult{}, fmt.Errorf("permission denied while trying to connect to the docker API at unix:///var/run/docker.sock")) + + cli := mocks.NewMockCli(ctrl) + cli.EXPECT().Client().Return(apiClient).AnyTimes() + + cmd := upCommand(&ProjectOptions{}, cli, &BackendOptions{}) + cmd.SetContext(t.Context()) + cmd.SetArgs([]string{"-d"}) + + err := cmd.Execute() + + assert.ErrorContains(t, err, "permission denied while trying to connect to the docker API") + assert.Assert(t, !strings.Contains(err.Error(), "is a directory"), err.Error()) +} + +func TestUpValidatesFlagConflictsBeforeDockerConnection(t *testing.T) { + ctrl := gomock.NewController(t) + defer ctrl.Finish() + + cli := mocks.NewMockCli(ctrl) + + cmd := upCommand(&ProjectOptions{}, cli, &BackendOptions{}) + cmd.SetContext(t.Context()) + cmd.SetArgs([]string{"--attach", "web", "--attach-dependencies"}) + + err := cmd.Execute() + + assert.Error(t, err, "cannot combine --attach and --attach-dependencies") +} diff --git a/cmd/compose/up.go b/cmd/compose/up.go index cda2678bbb..2046bd3bb4 100644 --- a/cmd/compose/up.go +++ b/cmd/compose/up.go @@ -27,6 +27,7 @@ import ( "github.com/compose-spec/compose-go/v2/types" "github.com/docker/cli/cli/command" xprogress "github.com/moby/buildkit/util/progress/progressui" + "github.com/moby/moby/client" "github.com/sirupsen/logrus" "github.com/spf13/cobra" "github.com/spf13/pflag" @@ -124,16 +125,16 @@ func upCommand(p *ProjectOptions, dockerCli command.Cli, backendOptions *Backend if !cmd.Flags().Changed("remove-orphans") { create.removeOrphans = utils.StringToBool(os.Getenv(ComposeRemoveOrphans)) } - return validateFlags(&up, &create) + if err := validateFlags(&up, &create); err != nil { + return err + } + return checkDockerConnection(ctx, dockerCli) }), RunE: p.WithServices(dockerCli, func(ctx context.Context, project *types.Project, services []string) error { create.ignoreOrphans = utils.StringToBool(project.Environment[ComposeIgnoreOrphans]) if create.ignoreOrphans && create.removeOrphans { return fmt.Errorf("cannot combine %s and --remove-orphans", ComposeIgnoreOrphans) } - if len(up.attach) != 0 && up.attachDependencies { - return errors.New("cannot combine --attach and --attach-dependencies") - } up.validateNavigationMenu(dockerCli) @@ -186,6 +187,11 @@ func upCommand(p *ProjectOptions, dockerCli command.Cli, backendOptions *Backend return upCmd } +func checkDockerConnection(ctx context.Context, dockerCli command.Cli) error { + _, err := dockerCli.Client().Ping(ctx, client.PingOptions{NegotiateAPIVersion: true}) + return err +} + //nolint:gocyclo func validateFlags(up *upOptions, create *createOptions) error { if up.waitTimeout < 0 { @@ -203,6 +209,9 @@ func validateFlags(up *upOptions, create *createOptions) error { } up.Detach = true } + if len(up.attach) != 0 && up.attachDependencies { + return errors.New("cannot combine --attach and --attach-dependencies") + } if create.Build && create.noBuild { return fmt.Errorf("--build and --no-build are incompatible") }