Skip to content
This repository was archived by the owner on Nov 27, 2023. It is now read-only.

Commit 64329c2

Browse files
authored
Merge pull request #1618 from ndeloof/remove_volume
2 parents 94cf3ea + deecf76 commit 64329c2

File tree

4 files changed

+101
-22
lines changed

4 files changed

+101
-22
lines changed

local/compose/create.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -938,7 +938,7 @@ func (s *composeService) ensureNetwork(ctx context.Context, n types.NetworkConfi
938938
return nil
939939
}
940940

941-
func (s *composeService) ensureNetworkDown(ctx context.Context, networkID string, networkName string) error {
941+
func (s *composeService) removeNetwork(ctx context.Context, networkID string, networkName string) error {
942942
w := progress.ContextWriter(ctx)
943943
eventName := fmt.Sprintf("Network %s", networkName)
944944
w.Event(progress.RemovingEvent(eventName))

local/compose/down.go

Lines changed: 73 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,8 @@ import (
3333
"github.com/docker/compose-cli/api/progress"
3434
)
3535

36+
type downOp func() error
37+
3638
func (s *composeService) Down(ctx context.Context, projectName string, options compose.DownOptions) error {
3739
w := progress.ContextWriter(ctx)
3840
resourceToRemove := false
@@ -73,37 +75,76 @@ func (s *composeService) Down(ctx context.Context, projectName string, options c
7375
}
7476
}
7577

76-
networks, err := s.apiClient.NetworkList(ctx, moby.NetworkListOptions{Filters: filters.NewArgs(projectFilter(projectName))})
78+
ops, err := s.ensureNetwoksDown(ctx, projectName)
7779
if err != nil {
7880
return err
7981
}
8082

81-
eg, _ := errgroup.WithContext(ctx)
82-
for _, n := range networks {
83-
resourceToRemove = true
84-
networkID := n.ID
85-
networkName := n.Name
86-
eg.Go(func() error {
87-
return s.ensureNetworkDown(ctx, networkID, networkName)
88-
})
83+
if options.Images != "" {
84+
ops = append(ops, s.ensureImagesDown(ctx, projectName, options, w)...)
8985
}
9086

91-
if options.Images != "" {
92-
for image := range s.getServiceImages(options, projectName) {
93-
image := image
94-
eg.Go(func() error {
95-
resourceToRemove = true
96-
return s.removeImage(ctx, image, w)
97-
})
87+
if options.Volumes {
88+
rm, err := s.ensureVolumesDown(ctx, projectName, w)
89+
if err != nil {
90+
return err
9891
}
92+
ops = append(ops, rm...)
9993
}
10094

101-
if !resourceToRemove {
95+
if !resourceToRemove && len(ops) == 0 {
10296
w.Event(progress.NewEvent(projectName, progress.Done, "Warning: No resource found to remove"))
10397
}
98+
99+
eg, _ := errgroup.WithContext(ctx)
100+
for _, op := range ops {
101+
eg.Go(op)
102+
}
104103
return eg.Wait()
105104
}
106105

106+
func (s *composeService) ensureVolumesDown(ctx context.Context, projectName string, w progress.Writer) ([]downOp, error) {
107+
var ops []downOp
108+
volumes, err := s.apiClient.VolumeList(ctx, filters.NewArgs(projectFilter(projectName)))
109+
if err != nil {
110+
return ops, err
111+
}
112+
for _, vol := range volumes.Volumes {
113+
id := vol.Name
114+
ops = append(ops, func() error {
115+
return s.removeVolume(ctx, id, w)
116+
})
117+
}
118+
return ops, nil
119+
}
120+
121+
func (s *composeService) ensureImagesDown(ctx context.Context, projectName string, options compose.DownOptions, w progress.Writer) []downOp {
122+
var ops []downOp
123+
for image := range s.getServiceImages(options, projectName) {
124+
image := image
125+
ops = append(ops, func() error {
126+
return s.removeImage(ctx, image, w)
127+
})
128+
}
129+
return ops
130+
}
131+
132+
func (s *composeService) ensureNetwoksDown(ctx context.Context, projectName string) ([]downOp, error) {
133+
var ops []downOp
134+
networks, err := s.apiClient.NetworkList(ctx, moby.NetworkListOptions{Filters: filters.NewArgs(projectFilter(projectName))})
135+
if err != nil {
136+
return ops, err
137+
}
138+
for _, n := range networks {
139+
networkID := n.ID
140+
networkName := n.Name
141+
ops = append(ops, func() error {
142+
return s.removeNetwork(ctx, networkID, networkName)
143+
})
144+
}
145+
return ops, nil
146+
}
147+
107148
func (s *composeService) getServiceImages(options compose.DownOptions, projectName string) map[string]struct{} {
108149
images := map[string]struct{}{}
109150
for _, service := range options.Project.Services {
@@ -134,6 +175,21 @@ func (s *composeService) removeImage(ctx context.Context, image string, w progre
134175
return err
135176
}
136177

178+
func (s *composeService) removeVolume(ctx context.Context, id string, w progress.Writer) error {
179+
resource := fmt.Sprintf("Volume %s", id)
180+
w.Event(progress.NewEvent(resource, progress.Working, "Removing"))
181+
err := s.apiClient.VolumeRemove(ctx, id, true)
182+
if err == nil {
183+
w.Event(progress.NewEvent(resource, progress.Done, "Removed"))
184+
return nil
185+
}
186+
if errdefs.IsNotFound(err) {
187+
w.Event(progress.NewEvent(resource, progress.Done, "Warning: No resource found to remove"))
188+
return nil
189+
}
190+
return err
191+
}
192+
137193
func (s *composeService) stopContainers(ctx context.Context, w progress.Writer, containers []moby.Container, timeout *time.Duration) error {
138194
for _, container := range containers {
139195
toStop := container

local/compose/down_test.go

Lines changed: 23 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -20,12 +20,12 @@ import (
2020
"context"
2121
"testing"
2222

23-
"github.com/docker/docker/api/types/filters"
24-
2523
"github.com/docker/compose-cli/api/compose"
2624
"github.com/docker/compose-cli/local/mocks"
2725

2826
apitypes "github.com/docker/docker/api/types"
27+
"github.com/docker/docker/api/types/filters"
28+
"github.com/docker/docker/api/types/volume"
2929
"github.com/golang/mock/gomock"
3030
"gotest.tools/v3/assert"
3131
)
@@ -79,3 +79,24 @@ func TestDownRemoveOrphans(t *testing.T) {
7979
err := tested.Down(context.Background(), testProject, compose.DownOptions{RemoveOrphans: true})
8080
assert.NilError(t, err)
8181
}
82+
83+
func TestDownRemoveVolumes(t *testing.T) {
84+
mockCtrl := gomock.NewController(t)
85+
defer mockCtrl.Finish()
86+
api := mocks.NewMockAPIClient(mockCtrl)
87+
tested.apiClient = api
88+
89+
api.EXPECT().ContainerList(gomock.Any(), projectFilterListOpt()).Return(
90+
[]apitypes.Container{testContainer("service1", "123")}, nil)
91+
92+
api.EXPECT().ContainerStop(gomock.Any(), "123", nil).Return(nil)
93+
api.EXPECT().ContainerRemove(gomock.Any(), "123", apitypes.ContainerRemoveOptions{Force: true}).Return(nil)
94+
95+
api.EXPECT().NetworkList(gomock.Any(), apitypes.NetworkListOptions{Filters: filters.NewArgs(projectFilter(testProject))}).Return(nil, nil)
96+
97+
api.EXPECT().VolumeList(gomock.Any(), filters.NewArgs(projectFilter(testProject))).Return(volume.VolumeListOKBody{Volumes: []*apitypes.Volume{{Name: "myProject_volume"}}}, nil)
98+
api.EXPECT().VolumeRemove(gomock.Any(), "myProject_volume", true).Return(nil)
99+
100+
err := tested.Down(context.Background(), testProject, compose.DownOptions{Volumes: true})
101+
assert.NilError(t, err)
102+
}

local/e2e/compose/volumes_test.go

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -72,7 +72,9 @@ func TestLocalComposeVolume(t *testing.T) {
7272
})
7373

7474
t.Run("cleanup volume project", func(t *testing.T) {
75-
c.RunDockerCmd("compose", "--project-name", projectName, "down")
76-
c.RunDockerCmd("volume", "rm", projectName+"_staticVol")
75+
c.RunDockerCmd("compose", "--project-name", projectName, "down", "--volumes")
76+
res := c.RunDockerCmd("volume", "ls")
77+
assert.Assert(t, !strings.Contains(res.Stdout(), projectName+"_staticVol"))
78+
assert.Assert(t, !strings.Contains(res.Stdout(), "myvolume"))
7779
})
7880
}

0 commit comments

Comments
 (0)