mirror of
https://github.com/docker/compose.git
synced 2025-07-25 22:54:54 +02:00
Fix compose images that reutn a different image with the same ID
Signed-off-by: koooge <koooooge@gmail.com>
This commit is contained in:
parent
2f65ace2aa
commit
799ab842a0
@ -274,7 +274,7 @@ func (s *composeService) getLocalImagesDigests(ctx context.Context, project *typ
|
|||||||
imageNames = append(imageNames, imgName)
|
imageNames = append(imageNames, imgName)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
imgs, err := s.getImages(ctx, imageNames)
|
imgs, err := s.getImageSummaries(ctx, imageNames)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
@ -55,20 +55,19 @@ func (s *composeService) Images(ctx context.Context, projectName string, options
|
|||||||
containers = allContainers
|
containers = allContainers
|
||||||
}
|
}
|
||||||
|
|
||||||
imageIDs := []string{}
|
images := []string{}
|
||||||
// aggregate image IDs
|
|
||||||
for _, c := range containers {
|
for _, c := range containers {
|
||||||
if !utils.StringContains(imageIDs, c.ImageID) {
|
if !utils.StringContains(images, c.Image) {
|
||||||
imageIDs = append(imageIDs, c.ImageID)
|
images = append(images, c.Image)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
images, err := s.getImages(ctx, imageIDs)
|
imageSummaries, err := s.getImageSummaries(ctx, images)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
summary := make([]api.ImageSummary, len(containers))
|
summary := make([]api.ImageSummary, len(containers))
|
||||||
for i, container := range containers {
|
for i, container := range containers {
|
||||||
img, ok := images[container.ImageID]
|
img, ok := imageSummaries[container.Image]
|
||||||
if !ok {
|
if !ok {
|
||||||
return nil, fmt.Errorf("failed to retrieve image for container %s", getCanonicalContainerName(container))
|
return nil, fmt.Errorf("failed to retrieve image for container %s", getCanonicalContainerName(container))
|
||||||
}
|
}
|
||||||
@ -79,24 +78,23 @@ func (s *composeService) Images(ctx context.Context, projectName string, options
|
|||||||
return summary, nil
|
return summary, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *composeService) getImages(ctx context.Context, images []string) (map[string]api.ImageSummary, error) {
|
func (s *composeService) getImageSummaries(ctx context.Context, repoTags []string) (map[string]api.ImageSummary, error) {
|
||||||
summary := map[string]api.ImageSummary{}
|
summary := map[string]api.ImageSummary{}
|
||||||
l := sync.Mutex{}
|
l := sync.Mutex{}
|
||||||
eg, ctx := errgroup.WithContext(ctx)
|
eg, ctx := errgroup.WithContext(ctx)
|
||||||
for _, img := range images {
|
for _, repoTag := range repoTags {
|
||||||
img := img
|
repoTag := repoTag
|
||||||
eg.Go(func() error {
|
eg.Go(func() error {
|
||||||
inspect, _, err := s.apiClient().ImageInspectWithRaw(ctx, img)
|
inspect, _, err := s.apiClient().ImageInspectWithRaw(ctx, repoTag)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
if errdefs.IsNotFound(err) {
|
if errdefs.IsNotFound(err) {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
return fmt.Errorf("unable to get image '%s': %w", img, err)
|
return fmt.Errorf("unable to get image '%s': %w", repoTag, err)
|
||||||
}
|
}
|
||||||
tag := ""
|
tag := ""
|
||||||
repository := ""
|
repository := ""
|
||||||
if len(inspect.RepoTags) > 0 {
|
ref, err := reference.ParseDockerRef(repoTag)
|
||||||
ref, err := reference.ParseDockerRef(inspect.RepoTags[0])
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
@ -104,9 +102,8 @@ func (s *composeService) getImages(ctx context.Context, images []string) (map[st
|
|||||||
if tagged, ok := ref.(reference.Tagged); ok {
|
if tagged, ok := ref.(reference.Tagged); ok {
|
||||||
tag = tagged.Tag()
|
tag = tagged.Tag()
|
||||||
}
|
}
|
||||||
}
|
|
||||||
l.Lock()
|
l.Lock()
|
||||||
summary[img] = api.ImageSummary{
|
summary[repoTag] = api.ImageSummary{
|
||||||
ID: inspect.ID,
|
ID: inspect.ID,
|
||||||
Repository: repository,
|
Repository: repository,
|
||||||
Tag: tag,
|
Tag: tag,
|
||||||
|
103
pkg/compose/images_test.go
Normal file
103
pkg/compose/images_test.go
Normal file
@ -0,0 +1,103 @@
|
|||||||
|
/*
|
||||||
|
Copyright 2024 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"
|
||||||
|
"strings"
|
||||||
|
"testing"
|
||||||
|
|
||||||
|
containerType "github.com/docker/docker/api/types/container"
|
||||||
|
"go.uber.org/mock/gomock"
|
||||||
|
"gotest.tools/v3/assert"
|
||||||
|
|
||||||
|
compose "github.com/docker/compose/v2/pkg/api"
|
||||||
|
moby "github.com/docker/docker/api/types"
|
||||||
|
"github.com/docker/docker/api/types/filters"
|
||||||
|
)
|
||||||
|
|
||||||
|
func TestImages(t *testing.T) {
|
||||||
|
mockCtrl := gomock.NewController(t)
|
||||||
|
defer mockCtrl.Finish()
|
||||||
|
|
||||||
|
api, cli := prepareMocks(mockCtrl)
|
||||||
|
tested := composeService{
|
||||||
|
dockerCli: cli,
|
||||||
|
}
|
||||||
|
|
||||||
|
ctx := context.Background()
|
||||||
|
args := filters.NewArgs(projectFilter(strings.ToLower(testProject)))
|
||||||
|
listOpts := containerType.ListOptions{All: true, Filters: args}
|
||||||
|
image1 := imageInspect("image1", "foo:1", 12345)
|
||||||
|
image2 := imageInspect("image2", "bar:2", 67890)
|
||||||
|
api.EXPECT().ImageInspectWithRaw(anyCancellableContext(), "foo:1").Return(image1, nil, nil)
|
||||||
|
api.EXPECT().ImageInspectWithRaw(anyCancellableContext(), "bar:2").Return(image2, nil, nil)
|
||||||
|
c1 := containerDetail("service1", "123", "running", "foo:1")
|
||||||
|
c2 := containerDetail("service1", "456", "running", "bar:2")
|
||||||
|
c2.Ports = []moby.Port{{PublicPort: 80, PrivatePort: 90, IP: "localhost"}}
|
||||||
|
c3 := containerDetail("service2", "789", "exited", "foo:1")
|
||||||
|
api.EXPECT().ContainerList(ctx, listOpts).Return([]moby.Container{c1, c2, c3}, nil)
|
||||||
|
|
||||||
|
images, err := tested.Images(ctx, strings.ToLower(testProject), compose.ImagesOptions{})
|
||||||
|
|
||||||
|
expected := []compose.ImageSummary{
|
||||||
|
{
|
||||||
|
ID: "image1",
|
||||||
|
ContainerName: "123",
|
||||||
|
Repository: "foo",
|
||||||
|
Tag: "1",
|
||||||
|
Size: 12345,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
ID: "image2",
|
||||||
|
ContainerName: "456",
|
||||||
|
Repository: "bar",
|
||||||
|
Tag: "2",
|
||||||
|
Size: 67890,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
ID: "image1",
|
||||||
|
ContainerName: "789",
|
||||||
|
Repository: "foo",
|
||||||
|
Tag: "1",
|
||||||
|
Size: 12345,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
assert.NilError(t, err)
|
||||||
|
assert.DeepEqual(t, images, expected)
|
||||||
|
}
|
||||||
|
|
||||||
|
func imageInspect(id string, image string, size int64) moby.ImageInspect {
|
||||||
|
return moby.ImageInspect{
|
||||||
|
ID: id,
|
||||||
|
RepoTags: []string{
|
||||||
|
"someRepo:someTag",
|
||||||
|
image,
|
||||||
|
},
|
||||||
|
Size: size,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func containerDetail(service string, id string, status string, image string) moby.Container {
|
||||||
|
return moby.Container{
|
||||||
|
ID: id,
|
||||||
|
Names: []string{"/" + id},
|
||||||
|
Image: image,
|
||||||
|
Labels: containerLabels(service, false),
|
||||||
|
State: status,
|
||||||
|
}
|
||||||
|
}
|
Loading…
x
Reference in New Issue
Block a user