diff --git a/pkg/api/api.go b/pkg/api/api.go index 9a1430df3c..46d990b0df 100644 --- a/pkg/api/api.go +++ b/pkg/api/api.go @@ -762,10 +762,9 @@ func GetImageNameOrDefault(service types.ServiceConfig, projectName string) stri } // GetDependentImages returns the additional images a service depends on beyond -// its main image. Currently this is the set of pre_start hook images, which run -// as ephemeral init containers with their own image. A hook without an explicit -// image, or one reusing the service image, is skipped as that image is already -// accounted for as the service image. +// its main image. This includes pre_start hook images and type=image volume +// sources. Images that match the service image are skipped as they are already +// accounted for. func GetDependentImages(service types.ServiceConfig, projectName string) []string { serviceImage := GetImageNameOrDefault(service, projectName) var images []string @@ -774,5 +773,10 @@ func GetDependentImages(service types.ServiceConfig, projectName string) []strin images = append(images, hook.Image) } } + for _, vol := range service.Volumes { + if vol.Type == types.VolumeTypeImage && vol.Source != serviceImage { + images = append(images, vol.Source) + } + } return images } diff --git a/pkg/api/api_test.go b/pkg/api/api_test.go index 4fb5890f76..c70e956603 100644 --- a/pkg/api/api_test.go +++ b/pkg/api/api_test.go @@ -101,6 +101,50 @@ func TestGetDependentImages(t *testing.T) { }, expected: nil, }, + { + name: "type=image volume source is collected", + service: types.ServiceConfig{ + Image: "nginx:alpine", + Volumes: []types.ServiceVolumeConfig{ + {Type: types.VolumeTypeImage, Source: "myorg/assets:latest", Target: "/srv/static"}, + }, + }, + expected: []string{"myorg/assets:latest"}, + }, + { + name: "type=image volume reusing the service image is ignored", + service: types.ServiceConfig{ + Image: "myorg/shared:latest", + Volumes: []types.ServiceVolumeConfig{ + {Type: types.VolumeTypeImage, Source: "myorg/shared:latest", Target: "/data"}, + }, + }, + expected: nil, + }, + { + name: "non-image volume types are ignored", + service: types.ServiceConfig{ + Image: "alpine:3.20", + Volumes: []types.ServiceVolumeConfig{ + {Type: types.VolumeTypeBind, Source: "/host/path", Target: "/container"}, + {Type: types.VolumeTypeVolume, Source: "myvolume", Target: "/data"}, + }, + }, + expected: nil, + }, + { + name: "hook and volume images are both collected", + service: types.ServiceConfig{ + Image: "alpine:3.20", + PreStart: []types.ServiceHook{ + {Image: "init:latest", Command: types.ShellCommand{"echo", "init"}}, + }, + Volumes: []types.ServiceVolumeConfig{ + {Type: types.VolumeTypeImage, Source: "myorg/data:1.0", Target: "/data"}, + }, + }, + expected: []string{"init:latest", "myorg/data:1.0"}, + }, } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) {