diff --git a/cmd/compose/compose.go b/cmd/compose/compose.go index 10b53e53f..2be3ab157 100644 --- a/cmd/compose/compose.go +++ b/cmd/compose/compose.go @@ -636,6 +636,7 @@ func RootCommand(dockerCli command.Cli, backend Backend) *cobra.Command { //noli publishCommand(&opts, dockerCli, backend), alphaCommand(&opts, dockerCli, backend), bridgeCommand(&opts, dockerCli), + volumesCommand(&opts, dockerCli, backend), ) c.Flags().SetInterspersed(false) diff --git a/cmd/compose/volumes.go b/cmd/compose/volumes.go new file mode 100644 index 000000000..fb79c7a4a --- /dev/null +++ b/cmd/compose/volumes.go @@ -0,0 +1,82 @@ +/* + 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/cli/cli/command" + "github.com/docker/cli/cli/command/formatter" + "github.com/docker/cli/cli/flags" + "github.com/docker/compose/v2/pkg/api" + "github.com/spf13/cobra" +) + +type volumesOptions struct { + *ProjectOptions + Quiet bool + Format string +} + +func volumesCommand(p *ProjectOptions, dockerCli command.Cli, backend api.Service) *cobra.Command { + options := volumesOptions{ + ProjectOptions: p, + } + + cmd := &cobra.Command{ + Use: "volumes [OPTIONS]", + Short: "List volumes", + RunE: Adapt(func(ctx context.Context, args []string) error { + return runVol(ctx, dockerCli, backend, options) + }), + Args: cobra.NoArgs, + ValidArgsFunction: noCompletion(), + } + + cmd.Flags().BoolVarP(&options.Quiet, "quiet", "q", false, "Only display volume names") + cmd.Flags().StringVar(&options.Format, "format", "table", flags.FormatHelp) + + return cmd +} + +func runVol(ctx context.Context, dockerCli command.Cli, backend api.Service, options volumesOptions) error { + project, _, err := options.projectOrName(ctx, dockerCli, []string{}...) + if err != nil { + return err + } + + volumes, err := backend.Volumes(ctx, project, api.VolumesOptions{ + }) + if err != nil { + return err + } + + if options.Quiet { + for _, v := range volumes { + _, _ = fmt.Fprintln(dockerCli.Out(), v.Name) + } + return nil + } + + volumeCtx := formatter.Context{ + Output: dockerCli.Out(), + Format: formatter.NewVolumeFormat(options.Format, options.Quiet), + } + + return formatter.VolumeWrite(volumeCtx, volumes) +} diff --git a/pkg/api/api.go b/pkg/api/api.go index 1be883899..ccb44640c 100644 --- a/pkg/api/api.go +++ b/pkg/api/api.go @@ -26,6 +26,7 @@ import ( "github.com/compose-spec/compose-go/v2/types" "github.com/containerd/platforms" "github.com/docker/cli/opts" + "github.com/docker/docker/api/types/volume" ) // Service manages a compose project @@ -98,8 +99,15 @@ type Service interface { Commit(ctx context.Context, projectName string, options CommitOptions) error // Generate generates a Compose Project from existing containers Generate(ctx context.Context, options GenerateOptions) (*types.Project, error) + // Volumes executes the equivalent to a `docker volume ls` + Volumes(ctx context.Context, project *types.Project, options VolumesOptions) ([]VolumesSummary, error) } +type VolumesOptions struct { +} + +type VolumesSummary = *volume.Volume + type ScaleOptions struct { Services []string } diff --git a/pkg/compose/volumes.go b/pkg/compose/volumes.go new file mode 100644 index 000000000..0ea3c9a1a --- /dev/null +++ b/pkg/compose/volumes.go @@ -0,0 +1,42 @@ +/* + 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" + + "github.com/compose-spec/compose-go/v2/types" + "github.com/docker/compose/v2/pkg/api" + "github.com/docker/docker/api/types/filters" + "github.com/docker/docker/api/types/volume" +) + +func (s *composeService) Volumes( ctx context.Context, project *types.Project, options api.VolumesOptions) ([]api.VolumesSummary, error) { + + projectName := project.Name + + volumesResponse, err := s.apiClient().VolumeList(ctx, volume.ListOptions{ + Filters: filters.NewArgs(projectFilter(projectName)), + }) + if err != nil { + return nil, err + } + + projectVolumes := volumesResponse.Volumes + + return projectVolumes, nil +} diff --git a/pkg/mocks/mock_docker_compose_api.go b/pkg/mocks/mock_docker_compose_api.go index 811187ea7..6bb065f34 100644 --- a/pkg/mocks/mock_docker_compose_api.go +++ b/pkg/mocks/mock_docker_compose_api.go @@ -497,6 +497,21 @@ func (mr *MockServiceMockRecorder) Viz(ctx, project, options any) *gomock.Call { return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Viz", reflect.TypeOf((*MockService)(nil).Viz), ctx, project, options) } +// Volumes mocks base method. +func (m *MockService) Volumes(ctx context.Context, project *types.Project, options api.VolumesOptions) ([]api.VolumesSummary, error) { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "Volumes", ctx, project, options) + ret0, _ := ret[0].([]api.VolumesSummary) + ret1, _ := ret[1].(error) + return ret0, ret1 +} + +// Volumes indicates an expected call of Volumes. +func (mr *MockServiceMockRecorder) Volumes(ctx, project, options any) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Volumes", reflect.TypeOf((*MockService)(nil).Volumes), ctx, project, options) +} + // Wait mocks base method. func (m *MockService) Wait(ctx context.Context, projectName string, options api.WaitOptions) (int64, error) { m.ctrl.T.Helper()