Skip to content

Navigation Menu

Sign in
Appearance settings

Search code, repositories, users, issues, pull requests...

Provide feedback

We read every piece of feedback, and take your input very seriously.

Saved searches

Use saved searches to filter your results more quickly

Appearance settings

introduce volume.type=image #12725

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 1 commit into from
Apr 9, 2025
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
4 changes: 1 addition & 3 deletions 4 go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ require (
github.com/Microsoft/go-winio v0.6.2
github.com/acarl005/stripansi v0.0.0-20180116102854-5a71ef0e047d
github.com/buger/goterm v1.0.4
github.com/compose-spec/compose-go/v2 v2.5.0
github.com/compose-spec/compose-go/v2 v2.5.1-0.20250409070949-8e1a035095ca
github.com/containerd/containerd/v2 v2.0.4
github.com/containerd/platforms v1.0.0-rc.1
github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc
Expand Down Expand Up @@ -206,5 +206,3 @@ require (
sigs.k8s.io/structured-merge-diff/v4 v4.4.1 // indirect
sigs.k8s.io/yaml v1.4.0 // indirect
)

replace github.com/compose-spec/compose-go/v2 => github.com/glours/compose-go/v2 v2.0.0-20250403082600-80aa75f06535
4 changes: 2 additions & 2 deletions 4 go.sum
Original file line number Diff line number Diff line change
Expand Up @@ -83,6 +83,8 @@ github.com/cloudflare/cfssl v0.0.0-20180223231731-4e2dcbde5004 h1:lkAMpLVBDaj17e
github.com/cloudflare/cfssl v0.0.0-20180223231731-4e2dcbde5004/go.mod h1:yMWuSON2oQp+43nFtAV/uvKQIFpSPerB57DCt9t8sSA=
github.com/codahale/rfc6979 v0.0.0-20141003034818-6a90f24967eb h1:EDmT6Q9Zs+SbUoc7Ik9EfrFqcylYqgPZ9ANSbTAntnE=
github.com/codahale/rfc6979 v0.0.0-20141003034818-6a90f24967eb/go.mod h1:ZjrT6AXHbDs86ZSdt/osfBi5qfexBrKUdONk989Wnk4=
github.com/compose-spec/compose-go/v2 v2.5.1-0.20250409070949-8e1a035095ca h1:4dH4DudeDunWTYetcJxQE65osreQKvaLtFLdl9CcqME=
github.com/compose-spec/compose-go/v2 v2.5.1-0.20250409070949-8e1a035095ca/go.mod h1:vPlkN0i+0LjLf9rv52lodNMUTJF5YHVfHVGLLIP67NA=
github.com/containerd/cgroups/v3 v3.0.5 h1:44na7Ud+VwyE7LIoJ8JTNQOa549a8543BmzaJHo6Bzo=
github.com/containerd/cgroups/v3 v3.0.5/go.mod h1:SA5DLYnXO8pTGYiAHXz94qvLQTKfVM5GEVisn4jpins=
github.com/containerd/console v1.0.4 h1:F2g4+oChYvBTsASRTz8NP6iIAi97J3TtSAsLbIFn4ro=
Expand Down Expand Up @@ -167,8 +169,6 @@ github.com/fvbommel/sortorder v1.1.0 h1:fUmoe+HLsBTctBDoaBwpQo5N+nrCp8g/BjKb/6ZQ
github.com/fvbommel/sortorder v1.1.0/go.mod h1:uk88iVf1ovNn1iLfgUVU2F9o5eO30ui720w+kxuqRs0=
github.com/fxamacker/cbor/v2 v2.7.0 h1:iM5WgngdRBanHcxugY4JySA0nk1wZorNOpTgCMedv5E=
github.com/fxamacker/cbor/v2 v2.7.0/go.mod h1:pxXPTn3joSm21Gbwsv0w9OSA2y1HFR9qXEeXQVeNoDQ=
github.com/glours/compose-go/v2 v2.0.0-20250403082600-80aa75f06535 h1:S/P6v3QxsMpkKn+2OSMPNkfSkadSjSHoMGAc/eBZgMU=
github.com/glours/compose-go/v2 v2.0.0-20250403082600-80aa75f06535/go.mod h1:vPlkN0i+0LjLf9rv52lodNMUTJF5YHVfHVGLLIP67NA=
github.com/go-kit/kit v0.8.0/go.mod h1:xBxKIO96dXMWWy0MnWVtmwkA9/13aqxPnvrjFYMA2as=
github.com/go-logfmt/logfmt v0.3.0/go.mod h1:Qt1PoO58o5twSAckw1HlFXLmHsOX5/0LbT9GBnD5lWE=
github.com/go-logfmt/logfmt v0.4.0/go.mod h1:3RMwSq7FuexP4Kalkev3ejPJsZTpXXBr9+V4qmtdjCk=
Expand Down
11 changes: 7 additions & 4 deletions 11 pkg/compose/build.go
Original file line number Diff line number Diff line change
Expand Up @@ -318,14 +318,17 @@ func (s *composeService) ensureImagesExists(ctx context.Context, project *types.
}

func (s *composeService) getLocalImagesDigests(ctx context.Context, project *types.Project) (map[string]api.ImageSummary, error) {
var imageNames []string
imageNames := utils.Set[string]{}
for _, s := range project.Services {
imgName := api.GetImageNameOrDefault(s, project.Name)
if !utils.StringContains(imageNames, imgName) {
imageNames = append(imageNames, imgName)
imageNames.Add(imgName)
for _, volume := range s.Volumes {
if volume.Type == types.VolumeTypeImage {
imageNames.Add(volume.Source)
}
}
}
imgs, err := s.getImageSummaries(ctx, imageNames)
imgs, err := s.getImageSummaries(ctx, imageNames.Elements())
if err != nil {
return nil, err
}
Expand Down
66 changes: 41 additions & 25 deletions 66 pkg/compose/create.go
Original file line number Diff line number Diff line change
Expand Up @@ -871,6 +871,15 @@ MOUNTS:
}
}
}
if m.Type == mount.TypeImage {
version, err := s.RuntimeVersion(ctx)
if err != nil {
return nil, nil, err
}
if versions.LessThan(version, "1.48") {
return nil, nil, fmt.Errorf("volume with type=image require Docker Engine v28 or later")
}
}
mounts = append(mounts, m)
}
return binds, mounts, nil
Expand Down Expand Up @@ -1125,7 +1134,7 @@ func buildMount(project types.Project, volume types.ServiceVolumeConfig) (mount.
}
}

bind, vol, tmpfs := buildMountOptions(volume)
bind, vol, tmpfs, img := buildMountOptions(volume)

if bind != nil {
volume.Type = types.VolumeTypeBind
Expand All @@ -1140,37 +1149,35 @@ func buildMount(project types.Project, volume types.ServiceVolumeConfig) (mount.
BindOptions: bind,
VolumeOptions: vol,
TmpfsOptions: tmpfs,
ImageOptions: img,
}, nil
}

func buildMountOptions(volume types.ServiceVolumeConfig) (*mount.BindOptions, *mount.VolumeOptions, *mount.TmpfsOptions) {
func buildMountOptions(volume types.ServiceVolumeConfig) (*mount.BindOptions, *mount.VolumeOptions, *mount.TmpfsOptions, *mount.ImageOptions) {
if volume.Type != types.VolumeTypeBind && volume.Bind != nil {
logrus.Warnf("mount of type `%s` should not define `bind` option", volume.Type)
}
if volume.Type != types.VolumeTypeVolume && volume.Volume != nil {
logrus.Warnf("mount of type `%s` should not define `volume` option", volume.Type)
}
if volume.Type != types.VolumeTypeTmpfs && volume.Tmpfs != nil {
logrus.Warnf("mount of type `%s` should not define `tmpfs` option", volume.Type)
}
if volume.Type != types.VolumeTypeImage && volume.Image != nil {
logrus.Warnf("mount of type `%s` should not define `image` option", volume.Type)
}

switch volume.Type {
case "bind":
if volume.Volume != nil {
logrus.Warnf("mount of type `bind` should not define `volume` option")
}
if volume.Tmpfs != nil {
logrus.Warnf("mount of type `bind` should not define `tmpfs` option")
}
return buildBindOption(volume.Bind), nil, nil
return buildBindOption(volume.Bind), nil, nil, nil
case "volume":
if volume.Bind != nil {
logrus.Warnf("mount of type `volume` should not define `bind` option")
}
if volume.Tmpfs != nil {
logrus.Warnf("mount of type `volume` should not define `tmpfs` option")
}
return nil, buildVolumeOptions(volume.Volume), nil
return nil, buildVolumeOptions(volume.Volume), nil, nil
case "tmpfs":
if volume.Bind != nil {
logrus.Warnf("mount of type `tmpfs` should not define `bind` option")
}
if volume.Volume != nil {
logrus.Warnf("mount of type `tmpfs` should not define `volume` option")
}
return nil, nil, buildTmpfsOptions(volume.Tmpfs)
return nil, nil, buildTmpfsOptions(volume.Tmpfs), nil
case "image":
return nil, nil, nil, buildImageOptions(volume.Image)
}
return nil, nil, nil
return nil, nil, nil, nil
}

func buildBindOption(bind *types.ServiceVolumeBind) *mount.BindOptions {
Expand Down Expand Up @@ -1199,7 +1206,7 @@ func buildVolumeOptions(vol *types.ServiceVolumeVolume) *mount.VolumeOptions {
return &mount.VolumeOptions{
NoCopy: vol.NoCopy,
Subpath: vol.Subpath,
// Labels: , // FIXME missing from model ?
Labels: vol.Labels,
// DriverConfig: , // FIXME missing from model ?
}
}
Expand All @@ -1214,6 +1221,15 @@ func buildTmpfsOptions(tmpfs *types.ServiceVolumeTmpfs) *mount.TmpfsOptions {
}
}

func buildImageOptions(image *types.ServiceVolumeImage) *mount.ImageOptions {
if image == nil {
return nil
}
return &mount.ImageOptions{
Subpath: image.SubPath,
}
}

func (s *composeService) ensureNetwork(ctx context.Context, project *types.Project, name string, n *types.NetworkConfig) (string, error) {
if n.External {
return s.resolveExternalNetwork(ctx, n)
Expand Down
25 changes: 19 additions & 6 deletions 25 pkg/compose/pull.go
Original file line number Diff line number Diff line change
Expand Up @@ -290,15 +290,28 @@ func encodedAuth(ref reference.Named, configFile driver.Auth) (string, error) {
}

func (s *composeService) pullRequiredImages(ctx context.Context, project *types.Project, images map[string]api.ImageSummary, quietPull bool) error {
var needPull []types.ServiceConfig
for _, service := range project.Services {
needPull := map[string]types.ServiceConfig{}
for name, service := range project.Services {
pull, err := mustPull(service, images)
if err != nil {
return err
}
if pull {
needPull = append(needPull, service)
needPull[name] = service
}
for i, vol := range service.Volumes {
if vol.Type == types.VolumeTypeImage {
if _, ok := images[vol.Source]; !ok {
// Hack: create a fake ServiceConfig so we pull missing volume image
n := fmt.Sprintf("%s:volume %d", name, i)
needPull[n] = types.ServiceConfig{
Name: n,
Image: vol.Source,
}
}
}
}

}
if len(needPull) == 0 {
return nil
Expand All @@ -308,11 +321,11 @@ func (s *composeService) pullRequiredImages(ctx context.Context, project *types.
w := progress.ContextWriter(ctx)
eg, ctx := errgroup.WithContext(ctx)
eg.SetLimit(s.maxConcurrency)
pulledImages := make([]api.ImageSummary, len(needPull))
for i, service := range needPull {
pulledImages := map[string]api.ImageSummary{}
for name, service := range needPull {
eg.Go(func() error {
id, err := s.pullServiceImage(ctx, service, s.configFile(), w, quietPull, project.Environment["DOCKER_DEFAULT_PLATFORM"])
pulledImages[i] = api.ImageSummary{
pulledImages[name] = api.ImageSummary{
ID: id,
Repository: service.Image,
LastTagTime: time.Now(),
Expand Down
10 changes: 10 additions & 0 deletions 10 pkg/e2e/fixtures/volumes/compose.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
services:
with_image:
image: alpine
command: "ls -al /mnt/image"
volumes:
- type: image
source: nginx:alpine
target: /mnt/image
image:
subpath: usr/share/nginx/html/
19 changes: 19 additions & 0 deletions 19 pkg/e2e/volumes_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -174,3 +174,22 @@ func TestUpRecreateVolumes_IgnoreBinds(t *testing.T) {
res := c.RunDockerComposeCmd(t, "-f", "./fixtures/recreate-volumes/bind.yaml", "--project-name", projectName, "up", "-d")
assert.Check(t, !strings.Contains(res.Combined(), "Recreated"))
}

func TestImageVolume(t *testing.T) {
c := NewCLI(t)
const projectName = "compose-e2e-image-volume"
t.Cleanup(func() {
c.cleanupWithDown(t, projectName)
})

version := c.RunDockerCmd(t, "version", "-f", "{{.Server.Version}}")
major, _, found := strings.Cut(version.Combined(), ".")
assert.Assert(t, found)
if major == "26" || major == "27" {
t.Skip("Skipping test due to docker version < 28")
}

res := c.RunDockerComposeCmd(t, "-f", "./fixtures/volumes/compose.yaml", "--project-name", projectName, "up", "with_image")
out := res.Combined()
assert.Check(t, strings.Contains(out, "index.html"))
}
Morty Proxy This is a proxified and sanitized view of the page, visit original site.