Skip to content

Commit 799ab84

Browse files
kooogendeloof
authored andcommitted
Fix compose images that reutn a different image with the same ID
Signed-off-by: koooge <[email protected]>
1 parent 2f65ace commit 799ab84

File tree

3 files changed

+122
-22
lines changed

3 files changed

+122
-22
lines changed

pkg/compose/build.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -274,7 +274,7 @@ func (s *composeService) getLocalImagesDigests(ctx context.Context, project *typ
274274
imageNames = append(imageNames, imgName)
275275
}
276276
}
277-
imgs, err := s.getImages(ctx, imageNames)
277+
imgs, err := s.getImageSummaries(ctx, imageNames)
278278
if err != nil {
279279
return nil, err
280280
}

pkg/compose/images.go

Lines changed: 18 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -55,20 +55,19 @@ func (s *composeService) Images(ctx context.Context, projectName string, options
5555
containers = allContainers
5656
}
5757

58-
imageIDs := []string{}
59-
// aggregate image IDs
58+
images := []string{}
6059
for _, c := range containers {
61-
if !utils.StringContains(imageIDs, c.ImageID) {
62-
imageIDs = append(imageIDs, c.ImageID)
60+
if !utils.StringContains(images, c.Image) {
61+
images = append(images, c.Image)
6362
}
6463
}
65-
images, err := s.getImages(ctx, imageIDs)
64+
imageSummaries, err := s.getImageSummaries(ctx, images)
6665
if err != nil {
6766
return nil, err
6867
}
6968
summary := make([]api.ImageSummary, len(containers))
7069
for i, container := range containers {
71-
img, ok := images[container.ImageID]
70+
img, ok := imageSummaries[container.Image]
7271
if !ok {
7372
return nil, fmt.Errorf("failed to retrieve image for container %s", getCanonicalContainerName(container))
7473
}
@@ -79,34 +78,32 @@ func (s *composeService) Images(ctx context.Context, projectName string, options
7978
return summary, nil
8079
}
8180

82-
func (s *composeService) getImages(ctx context.Context, images []string) (map[string]api.ImageSummary, error) {
81+
func (s *composeService) getImageSummaries(ctx context.Context, repoTags []string) (map[string]api.ImageSummary, error) {
8382
summary := map[string]api.ImageSummary{}
8483
l := sync.Mutex{}
8584
eg, ctx := errgroup.WithContext(ctx)
86-
for _, img := range images {
87-
img := img
85+
for _, repoTag := range repoTags {
86+
repoTag := repoTag
8887
eg.Go(func() error {
89-
inspect, _, err := s.apiClient().ImageInspectWithRaw(ctx, img)
88+
inspect, _, err := s.apiClient().ImageInspectWithRaw(ctx, repoTag)
9089
if err != nil {
9190
if errdefs.IsNotFound(err) {
9291
return nil
9392
}
94-
return fmt.Errorf("unable to get image '%s': %w", img, err)
93+
return fmt.Errorf("unable to get image '%s': %w", repoTag, err)
9594
}
9695
tag := ""
9796
repository := ""
98-
if len(inspect.RepoTags) > 0 {
99-
ref, err := reference.ParseDockerRef(inspect.RepoTags[0])
100-
if err != nil {
101-
return err
102-
}
103-
repository = reference.FamiliarName(ref)
104-
if tagged, ok := ref.(reference.Tagged); ok {
105-
tag = tagged.Tag()
106-
}
97+
ref, err := reference.ParseDockerRef(repoTag)
98+
if err != nil {
99+
return err
100+
}
101+
repository = reference.FamiliarName(ref)
102+
if tagged, ok := ref.(reference.Tagged); ok {
103+
tag = tagged.Tag()
107104
}
108105
l.Lock()
109-
summary[img] = api.ImageSummary{
106+
summary[repoTag] = api.ImageSummary{
110107
ID: inspect.ID,
111108
Repository: repository,
112109
Tag: tag,

pkg/compose/images_test.go

Lines changed: 103 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,103 @@
1+
/*
2+
Copyright 2024 Docker Compose CLI authors
3+
4+
Licensed under the Apache License, Version 2.0 (the "License");
5+
you may not use this file except in compliance with the License.
6+
You may obtain a copy of the License at
7+
8+
http://www.apache.org/licenses/LICENSE-2.0
9+
10+
Unless required by applicable law or agreed to in writing, software
11+
distributed under the License is distributed on an "AS IS" BASIS,
12+
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
See the License for the specific language governing permissions and
14+
limitations under the License.
15+
*/
16+
17+
package compose
18+
19+
import (
20+
"context"
21+
"strings"
22+
"testing"
23+
24+
containerType "github.com/docker/docker/api/types/container"
25+
"go.uber.org/mock/gomock"
26+
"gotest.tools/v3/assert"
27+
28+
compose "github.com/docker/compose/v2/pkg/api"
29+
moby "github.com/docker/docker/api/types"
30+
"github.com/docker/docker/api/types/filters"
31+
)
32+
33+
func TestImages(t *testing.T) {
34+
mockCtrl := gomock.NewController(t)
35+
defer mockCtrl.Finish()
36+
37+
api, cli := prepareMocks(mockCtrl)
38+
tested := composeService{
39+
dockerCli: cli,
40+
}
41+
42+
ctx := context.Background()
43+
args := filters.NewArgs(projectFilter(strings.ToLower(testProject)))
44+
listOpts := containerType.ListOptions{All: true, Filters: args}
45+
image1 := imageInspect("image1", "foo:1", 12345)
46+
image2 := imageInspect("image2", "bar:2", 67890)
47+
api.EXPECT().ImageInspectWithRaw(anyCancellableContext(), "foo:1").Return(image1, nil, nil)
48+
api.EXPECT().ImageInspectWithRaw(anyCancellableContext(), "bar:2").Return(image2, nil, nil)
49+
c1 := containerDetail("service1", "123", "running", "foo:1")
50+
c2 := containerDetail("service1", "456", "running", "bar:2")
51+
c2.Ports = []moby.Port{{PublicPort: 80, PrivatePort: 90, IP: "localhost"}}
52+
c3 := containerDetail("service2", "789", "exited", "foo:1")
53+
api.EXPECT().ContainerList(ctx, listOpts).Return([]moby.Container{c1, c2, c3}, nil)
54+
55+
images, err := tested.Images(ctx, strings.ToLower(testProject), compose.ImagesOptions{})
56+
57+
expected := []compose.ImageSummary{
58+
{
59+
ID: "image1",
60+
ContainerName: "123",
61+
Repository: "foo",
62+
Tag: "1",
63+
Size: 12345,
64+
},
65+
{
66+
ID: "image2",
67+
ContainerName: "456",
68+
Repository: "bar",
69+
Tag: "2",
70+
Size: 67890,
71+
},
72+
{
73+
ID: "image1",
74+
ContainerName: "789",
75+
Repository: "foo",
76+
Tag: "1",
77+
Size: 12345,
78+
},
79+
}
80+
assert.NilError(t, err)
81+
assert.DeepEqual(t, images, expected)
82+
}
83+
84+
func imageInspect(id string, image string, size int64) moby.ImageInspect {
85+
return moby.ImageInspect{
86+
ID: id,
87+
RepoTags: []string{
88+
"someRepo:someTag",
89+
image,
90+
},
91+
Size: size,
92+
}
93+
}
94+
95+
func containerDetail(service string, id string, status string, image string) moby.Container {
96+
return moby.Container{
97+
ID: id,
98+
Names: []string{"/" + id},
99+
Image: image,
100+
Labels: containerLabels(service, false),
101+
State: status,
102+
}
103+
}

0 commit comments

Comments
 (0)