Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
10 changes: 2 additions & 8 deletions types/project.go
Original file line number Diff line number Diff line change
Expand Up @@ -698,8 +698,8 @@ func (p *Project) WithServicesDisabled(names ...string) *Project {
// It returns a new Project instance with the changes and keep the original Project unchanged.
// Besides the service image, this also resolves the images services depend on:
// - pre_start hook images, which run as ephemeral init containers with their own image
// - `type: image` volume sources, unless they reference another service by name (those are
// resolved to a locally built image rather than a registry digest)
// - `type: image` volume sources — source is always a docker image reference, not
// a reference to another service (there is no such thing in the Compose Spec)
func (p *Project) WithImagesResolved(resolver func(named reference.Named) (godigest.Digest, error)) (*Project, error) {
// Deduplicate resolutions per raw image string across the whole call, on two axes:
// - cache (sync.Map) memoizes results for the whole call, so images resolved at
Expand Down Expand Up @@ -752,12 +752,6 @@ func (p *Project) WithImagesResolved(resolver func(named reference.Named) (godig
if vol.Type != VolumeTypeImage {
continue
}
if _, ok := p.Services[vol.Source]; ok {
continue
}
if _, ok := p.DisabledServices[vol.Source]; ok {
continue
}
image, err := resolve(vol.Source)
if err != nil {
return service, err
Expand Down
21 changes: 12 additions & 9 deletions types/project_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -321,7 +321,10 @@ func Test_ResolveImages_imageVolumes(t *testing.T) {
Volumes: []ServiceVolumeConfig{
// external image reference: must be resolved to a digest
{Type: VolumeTypeImage, Source: "alpine:3.19", Target: "/data"},
// reference to another service: resolved to a local image, left untouched
// a `type: image` source is always a docker image reference,
// resolved like any other one — even though a project service
// happens to share its name, it is NOT treated as a reference
// to that service (there is no such thing in the Compose Spec)
{Type: VolumeTypeImage, Source: "builder", Target: "/from-builder"},
// regular named volume: left untouched
{Type: VolumeTypeVolume, Source: "vol", Target: "/vol"},
Expand All @@ -336,7 +339,7 @@ func Test_ResolveImages_imageVolumes(t *testing.T) {

volumes := p.Services["service_1"].Volumes
assert.Equal(t, volumes[0].Source, "docker.io/library/alpine:3.19@"+digested)
assert.Equal(t, volumes[1].Source, "builder")
assert.Equal(t, volumes[1].Source, "docker.io/library/builder:latest@"+digested)
assert.Equal(t, volumes[2].Source, "vol")
}

Expand All @@ -362,10 +365,11 @@ func Test_ResolveImages_preStartHookError(t *testing.T) {
assert.Error(t, err, "registry unreachable")
}

func Test_ResolveImages_imageVolumeDisabledService(t *testing.T) {
// A `type: image` volume referencing a profile-disabled service is treated as a
// service reference: the resolver must never be called for it, and the source is
// left unchanged. The uppercase name would also trip reference.ParseDockerRef.
func Test_ResolveImages_imageVolumeInvalidSourceErrors(t *testing.T) {
// A `type: image` source is always a docker image reference: it is resolved
// like any other one, even when it happens to match a (possibly
// profile-disabled) project service name. An uppercase name is not a valid
// docker reference, so resolution fails rather than being silently skipped.
resolver := func(named reference.Named) (digest.Digest, error) {
return "", fmt.Errorf("resolver must not be called for %s", named)
}
Expand All @@ -385,9 +389,8 @@ func Test_ResolveImages_imageVolumeDisabledService(t *testing.T) {
},
}

p, err := p.WithImagesResolved(resolver)
assert.NilError(t, err)
assert.Equal(t, p.Services["service_1"].Volumes[0].Source, "Builder")
_, err := p.WithImagesResolved(resolver)
assert.ErrorContains(t, err, "invalid reference format")
}

func Test_ResolveImages_deduplicated(t *testing.T) {
Expand Down