Skip to content

Commit

Permalink
Add image mount options
Browse files Browse the repository at this point in the history
Signed-off-by: Laurent Goderre <[email protected]>
  • Loading branch information
LaurentGoderre committed Feb 11, 2025
1 parent 1e3e1bd commit 04b1b4e
Show file tree
Hide file tree
Showing 4 changed files with 79 additions and 0 deletions.
40 changes: 40 additions & 0 deletions cli/compose/convert/volume.go
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,9 @@ func handleVolumeToMount(
) (mount.Mount, error) {
result := createMountFromVolume(volume)

if volume.Image != nil {
return mount.Mount{}, errors.New("images options are incompatible with type volume")
}
if volume.Tmpfs != nil {
return mount.Mount{}, errors.New("tmpfs options are incompatible with type volume")
}
Expand Down Expand Up @@ -86,6 +89,32 @@ func handleVolumeToMount(
return result, nil
}

func handleImageToMount(volume composetypes.ServiceVolumeConfig) (mount.Mount, error) {
result := createMountFromVolume(volume)

if volume.Source == "" {
return mount.Mount{}, errors.New("invalid image source, source cannot be empty")
}
if volume.Volume != nil {
return mount.Mount{}, errors.New("volume options are incompatible with type image")
}
if volume.Bind != nil {
return mount.Mount{}, errors.New("bind options are incompatible with type image")
}
if volume.Tmpfs != nil {
return mount.Mount{}, errors.New("tmpfs options are incompatible with type image")
}
if volume.Cluster != nil {
return mount.Mount{}, errors.New("cluster options are incompatible with type image")
}
if volume.Bind != nil {
result.BindOptions = &mount.BindOptions{
Propagation: mount.Propagation(volume.Bind.Propagation),
}
}
return result, nil
}

func handleBindToMount(volume composetypes.ServiceVolumeConfig) (mount.Mount, error) {
result := createMountFromVolume(volume)

Expand All @@ -95,6 +124,9 @@ func handleBindToMount(volume composetypes.ServiceVolumeConfig) (mount.Mount, er
if volume.Volume != nil {
return mount.Mount{}, errors.New("volume options are incompatible with type bind")
}
if volume.Image != nil {
return mount.Mount{}, errors.New("image options are incompatible with type bind")
}
if volume.Tmpfs != nil {
return mount.Mount{}, errors.New("tmpfs options are incompatible with type bind")
}
Expand All @@ -121,6 +153,9 @@ func handleTmpfsToMount(volume composetypes.ServiceVolumeConfig) (mount.Mount, e
if volume.Volume != nil {
return mount.Mount{}, errors.New("volume options are incompatible with type tmpfs")
}
if volume.Image != nil {
return mount.Mount{}, errors.New("image options are incompatible with type tmpfs")
}
if volume.Cluster != nil {
return mount.Mount{}, errors.New("cluster options are incompatible with type tmpfs")
}
Expand All @@ -141,6 +176,9 @@ func handleNpipeToMount(volume composetypes.ServiceVolumeConfig) (mount.Mount, e
if volume.Volume != nil {
return mount.Mount{}, errors.New("volume options are incompatible with type npipe")
}
if volume.Image != nil {
return mount.Mount{}, errors.New("image options are incompatible with type npipe")
}
if volume.Tmpfs != nil {
return mount.Mount{}, errors.New("tmpfs options are incompatible with type npipe")
}
Expand Down Expand Up @@ -203,6 +241,8 @@ func convertVolumeToMount(
switch volume.Type {
case "volume", "":
return handleVolumeToMount(volume, stackVolumes, namespace)
case "image":
return handleImageToMount(volume)
case "bind":
return handleBindToMount(volume)
case "tmpfs":
Expand Down
6 changes: 6 additions & 0 deletions cli/compose/types/types.go
Original file line number Diff line number Diff line change
Expand Up @@ -398,6 +398,7 @@ type ServiceVolumeConfig struct {
Consistency string `yaml:",omitempty" json:"consistency,omitempty"`
Bind *ServiceVolumeBind `yaml:",omitempty" json:"bind,omitempty"`
Volume *ServiceVolumeVolume `yaml:",omitempty" json:"volume,omitempty"`
Image *ServiceVolumeImage `yaml:",omitempty" json:"image,omitempty"`
Tmpfs *ServiceVolumeTmpfs `yaml:",omitempty" json:"tmpfs,omitempty"`
Cluster *ServiceVolumeCluster `yaml:",omitempty" json:"cluster,omitempty"`
}
Expand All @@ -412,6 +413,11 @@ type ServiceVolumeVolume struct {
NoCopy bool `mapstructure:"nocopy" yaml:"nocopy,omitempty" json:"nocopy,omitempty"`
}

// ServiceVolumeImage are options for a service volume of type image
type ServiceVolumeImage struct {
Subpath bool `mapstructure:"subpath" yaml:"subpath,omitempty" json:"subpath,omitempty"`
}

// ServiceVolumeTmpfs are options for a service volume of type tmpfs
type ServiceVolumeTmpfs struct {
Size int64 `yaml:",omitempty" json:"size,omitempty"`
Expand Down
12 changes: 12 additions & 0 deletions opts/mount.go
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,13 @@ func (m *MountOpt) Set(value string) error {
return mount.VolumeOptions
}

imageOptions := func() *mounttypes.ImageOptions {
if mount.ImageOptions == nil {
mount.ImageOptions = new(mounttypes.ImageOptions)
}
return mount.ImageOptions
}

bindOptions := func() *mounttypes.BindOptions {
if mount.BindOptions == nil {
mount.BindOptions = new(mounttypes.BindOptions)
Expand Down Expand Up @@ -147,6 +154,8 @@ func (m *MountOpt) Set(value string) error {
volumeOptions().DriverConfig.Options = make(map[string]string)
}
setValueOnMap(volumeOptions().DriverConfig.Options, val)
case "image-subpath":
imageOptions().Subpath = val
case "tmpfs-size":
sizeBytes, err := units.RAMInBytes(val)
if err != nil {
Expand Down Expand Up @@ -175,6 +184,9 @@ func (m *MountOpt) Set(value string) error {
if mount.VolumeOptions != nil && mount.Type != mounttypes.TypeVolume {
return fmt.Errorf("cannot mix 'volume-*' options with mount type '%s'", mount.Type)
}
if mount.ImageOptions != nil && mount.Type != mounttypes.TypeImage {
return fmt.Errorf("cannot mix 'image-*' options with mount type '%s'", mount.Type)
}
if mount.BindOptions != nil && mount.Type != mounttypes.TypeBind {
return fmt.Errorf("cannot mix 'bind-*' options with mount type '%s'", mount.Type)
}
Expand Down
21 changes: 21 additions & 0 deletions opts/mount_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -188,6 +188,27 @@ func TestMountOptTypeConflict(t *testing.T) {
assert.ErrorContains(t, m.Set("type=volume,target=/foo,source=/foo,bind-propagation=rprivate"), "cannot mix")
}

func TestMountOptSetImageNoError(t *testing.T) {
for _, testcase := range []string{
"type=image,source=foo,target=/target,image-subpath=/bar",
} {
var mount MountOpt

assert.NilError(t, mount.Set(testcase))

mounts := mount.Value()
assert.Assert(t, is.Len(mounts, 1))
assert.Check(t, is.DeepEqual(mounttypes.Mount{
Type: mounttypes.TypeImage,
Source: "foo",
Target: "/target",
ImageOptions: &mounttypes.ImageOptions{
Subpath: "/bar",
},
}, mounts[0]))
}
}

func TestMountOptSetTmpfsNoError(t *testing.T) {
for _, testcase := range []string{
// tests several aliases that should have same result.
Expand Down

0 comments on commit 04b1b4e

Please sign in to comment.