|
|
@@ -18,12 +18,15 @@ package compose
|
|
|
|
|
|
import (
|
|
|
"context"
|
|
|
+ "fmt"
|
|
|
"strings"
|
|
|
"testing"
|
|
|
|
|
|
+ "github.com/compose-spec/compose-go/types"
|
|
|
moby "github.com/docker/docker/api/types"
|
|
|
"github.com/docker/docker/api/types/filters"
|
|
|
"github.com/docker/docker/api/types/volume"
|
|
|
+ "github.com/docker/docker/errdefs"
|
|
|
"github.com/golang/mock/gomock"
|
|
|
"gotest.tools/v3/assert"
|
|
|
|
|
|
@@ -143,38 +146,109 @@ func TestDownRemoveVolumes(t *testing.T) {
|
|
|
assert.NilError(t, err)
|
|
|
}
|
|
|
|
|
|
-func TestDownRemoveImageLocal(t *testing.T) {
|
|
|
+func TestDownRemoveImages(t *testing.T) {
|
|
|
mockCtrl := gomock.NewController(t)
|
|
|
defer mockCtrl.Finish()
|
|
|
|
|
|
+ opts := compose.DownOptions{
|
|
|
+ Project: &types.Project{
|
|
|
+ Name: strings.ToLower(testProject),
|
|
|
+ Services: types.Services{
|
|
|
+ {Name: "local-anonymous"},
|
|
|
+ {Name: "local-named", Image: "local-named-image"},
|
|
|
+ {Name: "remote", Image: "remote-image"},
|
|
|
+ {Name: "remote-tagged", Image: "registry.example.com/remote-image-tagged:v1.0"},
|
|
|
+ {Name: "no-images-anonymous"},
|
|
|
+ {Name: "no-images-named", Image: "missing-named-image"},
|
|
|
+ },
|
|
|
+ },
|
|
|
+ }
|
|
|
+
|
|
|
api := mocks.NewMockAPIClient(mockCtrl)
|
|
|
cli := mocks.NewMockCli(mockCtrl)
|
|
|
tested.dockerCli = cli
|
|
|
cli.EXPECT().Client().Return(api).AnyTimes()
|
|
|
|
|
|
- container := testContainer("service1", "123", false)
|
|
|
- container.Labels[compose.ImageNameLabel] = ""
|
|
|
-
|
|
|
- api.EXPECT().ContainerList(gomock.Any(), projectFilterListOpt(false)).Return(
|
|
|
- []moby.Container{container}, nil)
|
|
|
-
|
|
|
- api.EXPECT().VolumeList(gomock.Any(), filters.NewArgs(projectFilter(strings.ToLower(testProject)))).
|
|
|
- Return(volume.VolumeListOKBody{
|
|
|
- Volumes: []*moby.Volume{{Name: "myProject_volume"}},
|
|
|
- }, nil)
|
|
|
- api.EXPECT().NetworkList(gomock.Any(), moby.NetworkListOptions{Filters: filters.NewArgs(projectFilter(strings.ToLower(testProject)))}).
|
|
|
- Return(nil, nil)
|
|
|
-
|
|
|
- api.EXPECT().ContainerStop(gomock.Any(), "123", nil).Return(nil)
|
|
|
- api.EXPECT().ContainerRemove(gomock.Any(), "123", moby.ContainerRemoveOptions{Force: true}).Return(nil)
|
|
|
-
|
|
|
- api.EXPECT().ImageRemove(gomock.Any(), "testproject-service1", moby.ImageRemoveOptions{}).Return(nil, nil)
|
|
|
+ api.EXPECT().ContainerList(gomock.Any(), projectFilterListOpt(false)).
|
|
|
+ Return([]moby.Container{
|
|
|
+ testContainer("service1", "123", false),
|
|
|
+ }, nil).
|
|
|
+ AnyTimes()
|
|
|
+
|
|
|
+ api.EXPECT().ImageList(gomock.Any(), moby.ImageListOptions{
|
|
|
+ Filters: filters.NewArgs(
|
|
|
+ projectFilter(strings.ToLower(testProject)),
|
|
|
+ filters.Arg("dangling", "false"),
|
|
|
+ ),
|
|
|
+ }).Return([]moby.ImageSummary{
|
|
|
+ {
|
|
|
+ Labels: types.Labels{compose.ServiceLabel: "local-anonymous"},
|
|
|
+ RepoTags: []string{"testproject-local-anonymous:latest"},
|
|
|
+ },
|
|
|
+ {
|
|
|
+ Labels: types.Labels{compose.ServiceLabel: "local-named"},
|
|
|
+ RepoTags: []string{"local-named-image:latest"},
|
|
|
+ },
|
|
|
+ }, nil).AnyTimes()
|
|
|
+
|
|
|
+ imagesToBeInspected := map[string]bool{
|
|
|
+ "local-named-image:latest": true,
|
|
|
+ "remote-image:latest": true,
|
|
|
+ "testproject-no-images-anonymous:latest": false,
|
|
|
+ "missing-named-image:latest": false,
|
|
|
+ }
|
|
|
+ for img, exists := range imagesToBeInspected {
|
|
|
+ var resp moby.ImageInspect
|
|
|
+ var err error
|
|
|
+ if exists {
|
|
|
+ resp.RepoTags = []string{img}
|
|
|
+ } else {
|
|
|
+ err = errdefs.NotFound(fmt.Errorf("test specified that image %q should not exist", img))
|
|
|
+ }
|
|
|
+
|
|
|
+ api.EXPECT().ImageInspectWithRaw(gomock.Any(), img).
|
|
|
+ Return(resp, nil, err).
|
|
|
+ AnyTimes()
|
|
|
+ }
|
|
|
+
|
|
|
+ api.EXPECT().ImageInspectWithRaw(gomock.Any(), "registry.example.com/remote-image-tagged:v1.0").
|
|
|
+ Return(moby.ImageInspect{RepoTags: []string{"registry.example.com/remote-image-tagged:v1.0"}}, nil, nil).
|
|
|
+ AnyTimes()
|
|
|
+
|
|
|
+ localImagesToBeRemoved := []string{
|
|
|
+ "testproject-local-anonymous:latest",
|
|
|
+ }
|
|
|
+ for _, img := range localImagesToBeRemoved {
|
|
|
+ // test calls down --rmi=local then down --rmi=all, so local images
|
|
|
+ // get "removed" 2x, while other images are only 1x
|
|
|
+ api.EXPECT().ImageRemove(gomock.Any(), img, moby.ImageRemoveOptions{}).
|
|
|
+ Return(nil, nil).
|
|
|
+ Times(2)
|
|
|
+ }
|
|
|
+
|
|
|
+ t.Log("-> docker compose down --rmi=local")
|
|
|
+ opts.Images = "local"
|
|
|
+ err := tested.Down(context.Background(), strings.ToLower(testProject), opts)
|
|
|
+ assert.NilError(t, err)
|
|
|
|
|
|
- err := tested.Down(context.Background(), strings.ToLower(testProject), compose.DownOptions{Images: "local"})
|
|
|
+ otherImagesToBeRemoved := []string{
|
|
|
+ "local-named-image:latest",
|
|
|
+ "remote-image:latest",
|
|
|
+ "registry.example.com/remote-image-tagged:v1.0",
|
|
|
+ }
|
|
|
+ for _, img := range otherImagesToBeRemoved {
|
|
|
+ api.EXPECT().ImageRemove(gomock.Any(), img, moby.ImageRemoveOptions{}).
|
|
|
+ Return(nil, nil).
|
|
|
+ Times(1)
|
|
|
+ }
|
|
|
+
|
|
|
+ t.Log("-> docker compose down --rmi=all")
|
|
|
+ opts.Images = "all"
|
|
|
+ err = tested.Down(context.Background(), strings.ToLower(testProject), opts)
|
|
|
assert.NilError(t, err)
|
|
|
}
|
|
|
|
|
|
-func TestDownRemoveImageLocalNoLabel(t *testing.T) {
|
|
|
+func TestDownRemoveImages_NoLabel(t *testing.T) {
|
|
|
mockCtrl := gomock.NewController(t)
|
|
|
defer mockCtrl.Finish()
|
|
|
|
|
|
@@ -195,37 +269,23 @@ func TestDownRemoveImageLocalNoLabel(t *testing.T) {
|
|
|
api.EXPECT().NetworkList(gomock.Any(), moby.NetworkListOptions{Filters: filters.NewArgs(projectFilter(strings.ToLower(testProject)))}).
|
|
|
Return(nil, nil)
|
|
|
|
|
|
- api.EXPECT().ContainerStop(gomock.Any(), "123", nil).Return(nil)
|
|
|
- api.EXPECT().ContainerRemove(gomock.Any(), "123", moby.ContainerRemoveOptions{Force: true}).Return(nil)
|
|
|
+ // ImageList returns no images for the project since they were unlabeled
|
|
|
+ // (created by an older version of Compose)
|
|
|
+ api.EXPECT().ImageList(gomock.Any(), moby.ImageListOptions{
|
|
|
+ Filters: filters.NewArgs(
|
|
|
+ projectFilter(strings.ToLower(testProject)),
|
|
|
+ filters.Arg("dangling", "false"),
|
|
|
+ ),
|
|
|
+ }).Return(nil, nil)
|
|
|
|
|
|
- err := tested.Down(context.Background(), strings.ToLower(testProject), compose.DownOptions{Images: "local"})
|
|
|
- assert.NilError(t, err)
|
|
|
-}
|
|
|
-
|
|
|
-func TestDownRemoveImageAll(t *testing.T) {
|
|
|
- mockCtrl := gomock.NewController(t)
|
|
|
- defer mockCtrl.Finish()
|
|
|
-
|
|
|
- api := mocks.NewMockAPIClient(mockCtrl)
|
|
|
- cli := mocks.NewMockCli(mockCtrl)
|
|
|
- tested.dockerCli = cli
|
|
|
- cli.EXPECT().Client().Return(api).AnyTimes()
|
|
|
-
|
|
|
- api.EXPECT().ContainerList(gomock.Any(), projectFilterListOpt(false)).Return(
|
|
|
- []moby.Container{testContainer("service1", "123", false)}, nil)
|
|
|
-
|
|
|
- api.EXPECT().VolumeList(gomock.Any(), filters.NewArgs(projectFilter(strings.ToLower(testProject)))).
|
|
|
- Return(volume.VolumeListOKBody{
|
|
|
- Volumes: []*moby.Volume{{Name: "myProject_volume"}},
|
|
|
- }, nil)
|
|
|
- api.EXPECT().NetworkList(gomock.Any(), moby.NetworkListOptions{Filters: filters.NewArgs(projectFilter(strings.ToLower(testProject)))}).
|
|
|
- Return(nil, nil)
|
|
|
+ api.EXPECT().ImageInspectWithRaw(gomock.Any(), "testproject-service1:latest").
|
|
|
+ Return(moby.ImageInspect{}, nil, nil)
|
|
|
|
|
|
api.EXPECT().ContainerStop(gomock.Any(), "123", nil).Return(nil)
|
|
|
api.EXPECT().ContainerRemove(gomock.Any(), "123", moby.ContainerRemoveOptions{Force: true}).Return(nil)
|
|
|
|
|
|
- api.EXPECT().ImageRemove(gomock.Any(), "service1-img", moby.ImageRemoveOptions{}).Return(nil, nil)
|
|
|
+ api.EXPECT().ImageRemove(gomock.Any(), "testproject-service1:latest", moby.ImageRemoveOptions{}).Return(nil, nil)
|
|
|
|
|
|
- err := tested.Down(context.Background(), strings.ToLower(testProject), compose.DownOptions{Images: "all"})
|
|
|
+ err := tested.Down(context.Background(), strings.ToLower(testProject), compose.DownOptions{Images: "local"})
|
|
|
assert.NilError(t, err)
|
|
|
}
|