diff --git a/cmd/compose/images.go b/cmd/compose/images.go index 918c5a11e..4f305e491 100644 --- a/cmd/compose/images.go +++ b/cmd/compose/images.go @@ -103,7 +103,7 @@ func runImages(ctx context.Context, dockerCli command.Cli, backend api.Service, for ctr, i := range images { lastTagTime := i.LastTagTime if lastTagTime.IsZero() { - lastTagTime = time.Now() + lastTagTime = i.Created } imageList = append(imageList, img{ ContainerName: ctr, diff --git a/pkg/api/api.go b/pkg/api/api.go index a48b5ab5f..51140757a 100644 --- a/pkg/api/api.go +++ b/pkg/api/api.go @@ -558,6 +558,7 @@ type ImageSummary struct { Tag string Platform platforms.Platform Size int64 + Created time.Time LastTagTime time.Time } diff --git a/pkg/compose/images.go b/pkg/compose/images.go index a91d6159f..d49e92c65 100644 --- a/pkg/compose/images.go +++ b/pkg/compose/images.go @@ -22,6 +22,7 @@ import ( "slices" "strings" "sync" + "time" "github.com/containerd/errdefs" "github.com/containerd/platforms" @@ -90,6 +91,11 @@ func (s *composeService) Images(ctx context.Context, projectName string, options } } + created, err := time.Parse(time.RFC3339Nano, image.Created) + if err != nil { + return err + } + mux.Lock() defer mux.Unlock() summary[getCanonicalContainerName(c)] = api.ImageSummary{ @@ -103,6 +109,7 @@ func (s *composeService) Images(ctx context.Context, projectName string, options Variant: image.Variant, }, Size: image.Size, + Created: created, LastTagTime: image.Metadata.LastTagTime, } return nil diff --git a/pkg/compose/images_test.go b/pkg/compose/images_test.go index a7a1d5e9a..85a2b5770 100644 --- a/pkg/compose/images_test.go +++ b/pkg/compose/images_test.go @@ -20,6 +20,7 @@ import ( "context" "strings" "testing" + "time" "github.com/docker/docker/api/types" "github.com/docker/docker/api/types/container" @@ -44,8 +45,12 @@ func TestImages(t *testing.T) { args := filters.NewArgs(projectFilter(strings.ToLower(testProject))) listOpts := container.ListOptions{All: true, Filters: args} api.EXPECT().ServerVersion(gomock.Any()).Return(types.Version{APIVersion: "1.96"}, nil).AnyTimes() - image1 := imageInspect("image1", "foo:1", 12345) - image2 := imageInspect("image2", "bar:2", 67890) + timeStr1 := "2025-06-06T06:06:06.000000000Z" + created1, _ := time.Parse(time.RFC3339Nano, timeStr1) + timeStr2 := "2025-03-03T03:03:03.000000000Z" + created2, _ := time.Parse(time.RFC3339Nano, timeStr2) + image1 := imageInspect("image1", "foo:1", 12345, timeStr1) + image2 := imageInspect("image2", "bar:2", 67890, timeStr2) api.EXPECT().ImageInspect(anyCancellableContext(), "foo:1").Return(image1, nil).MaxTimes(2) api.EXPECT().ImageInspect(anyCancellableContext(), "bar:2").Return(image2, nil) c1 := containerDetail("service1", "123", "running", "foo:1") @@ -62,32 +67,36 @@ func TestImages(t *testing.T) { Repository: "foo", Tag: "1", Size: 12345, + Created: created1, }, "456": { ID: "image2", Repository: "bar", Tag: "2", Size: 67890, + Created: created2, }, "789": { ID: "image1", Repository: "foo", Tag: "1", Size: 12345, + Created: created1, }, } assert.NilError(t, err) assert.DeepEqual(t, images, expected) } -func imageInspect(id string, imageReference string, size int64) image.InspectResponse { +func imageInspect(id string, imageReference string, size int64, created string) image.InspectResponse { return image.InspectResponse{ ID: id, RepoTags: []string{ "someRepo:someTag", imageReference, }, - Size: size, + Size: size, + Created: created, } }