Skip to content

Commit 04b1b4e

Browse files
Add image mount options
Signed-off-by: Laurent Goderre <[email protected]>
1 parent 1e3e1bd commit 04b1b4e

File tree

4 files changed

+79
-0
lines changed

4 files changed

+79
-0
lines changed

cli/compose/convert/volume.go

Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -40,6 +40,9 @@ func handleVolumeToMount(
4040
) (mount.Mount, error) {
4141
result := createMountFromVolume(volume)
4242

43+
if volume.Image != nil {
44+
return mount.Mount{}, errors.New("images options are incompatible with type volume")
45+
}
4346
if volume.Tmpfs != nil {
4447
return mount.Mount{}, errors.New("tmpfs options are incompatible with type volume")
4548
}
@@ -86,6 +89,32 @@ func handleVolumeToMount(
8689
return result, nil
8790
}
8891

92+
func handleImageToMount(volume composetypes.ServiceVolumeConfig) (mount.Mount, error) {
93+
result := createMountFromVolume(volume)
94+
95+
if volume.Source == "" {
96+
return mount.Mount{}, errors.New("invalid image source, source cannot be empty")
97+
}
98+
if volume.Volume != nil {
99+
return mount.Mount{}, errors.New("volume options are incompatible with type image")
100+
}
101+
if volume.Bind != nil {
102+
return mount.Mount{}, errors.New("bind options are incompatible with type image")
103+
}
104+
if volume.Tmpfs != nil {
105+
return mount.Mount{}, errors.New("tmpfs options are incompatible with type image")
106+
}
107+
if volume.Cluster != nil {
108+
return mount.Mount{}, errors.New("cluster options are incompatible with type image")
109+
}
110+
if volume.Bind != nil {
111+
result.BindOptions = &mount.BindOptions{
112+
Propagation: mount.Propagation(volume.Bind.Propagation),
113+
}
114+
}
115+
return result, nil
116+
}
117+
89118
func handleBindToMount(volume composetypes.ServiceVolumeConfig) (mount.Mount, error) {
90119
result := createMountFromVolume(volume)
91120

@@ -95,6 +124,9 @@ func handleBindToMount(volume composetypes.ServiceVolumeConfig) (mount.Mount, er
95124
if volume.Volume != nil {
96125
return mount.Mount{}, errors.New("volume options are incompatible with type bind")
97126
}
127+
if volume.Image != nil {
128+
return mount.Mount{}, errors.New("image options are incompatible with type bind")
129+
}
98130
if volume.Tmpfs != nil {
99131
return mount.Mount{}, errors.New("tmpfs options are incompatible with type bind")
100132
}
@@ -121,6 +153,9 @@ func handleTmpfsToMount(volume composetypes.ServiceVolumeConfig) (mount.Mount, e
121153
if volume.Volume != nil {
122154
return mount.Mount{}, errors.New("volume options are incompatible with type tmpfs")
123155
}
156+
if volume.Image != nil {
157+
return mount.Mount{}, errors.New("image options are incompatible with type tmpfs")
158+
}
124159
if volume.Cluster != nil {
125160
return mount.Mount{}, errors.New("cluster options are incompatible with type tmpfs")
126161
}
@@ -141,6 +176,9 @@ func handleNpipeToMount(volume composetypes.ServiceVolumeConfig) (mount.Mount, e
141176
if volume.Volume != nil {
142177
return mount.Mount{}, errors.New("volume options are incompatible with type npipe")
143178
}
179+
if volume.Image != nil {
180+
return mount.Mount{}, errors.New("image options are incompatible with type npipe")
181+
}
144182
if volume.Tmpfs != nil {
145183
return mount.Mount{}, errors.New("tmpfs options are incompatible with type npipe")
146184
}
@@ -203,6 +241,8 @@ func convertVolumeToMount(
203241
switch volume.Type {
204242
case "volume", "":
205243
return handleVolumeToMount(volume, stackVolumes, namespace)
244+
case "image":
245+
return handleImageToMount(volume)
206246
case "bind":
207247
return handleBindToMount(volume)
208248
case "tmpfs":

cli/compose/types/types.go

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -398,6 +398,7 @@ type ServiceVolumeConfig struct {
398398
Consistency string `yaml:",omitempty" json:"consistency,omitempty"`
399399
Bind *ServiceVolumeBind `yaml:",omitempty" json:"bind,omitempty"`
400400
Volume *ServiceVolumeVolume `yaml:",omitempty" json:"volume,omitempty"`
401+
Image *ServiceVolumeImage `yaml:",omitempty" json:"image,omitempty"`
401402
Tmpfs *ServiceVolumeTmpfs `yaml:",omitempty" json:"tmpfs,omitempty"`
402403
Cluster *ServiceVolumeCluster `yaml:",omitempty" json:"cluster,omitempty"`
403404
}
@@ -412,6 +413,11 @@ type ServiceVolumeVolume struct {
412413
NoCopy bool `mapstructure:"nocopy" yaml:"nocopy,omitempty" json:"nocopy,omitempty"`
413414
}
414415

416+
// ServiceVolumeImage are options for a service volume of type image
417+
type ServiceVolumeImage struct {
418+
Subpath bool `mapstructure:"subpath" yaml:"subpath,omitempty" json:"subpath,omitempty"`
419+
}
420+
415421
// ServiceVolumeTmpfs are options for a service volume of type tmpfs
416422
type ServiceVolumeTmpfs struct {
417423
Size int64 `yaml:",omitempty" json:"size,omitempty"`

opts/mount.go

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -43,6 +43,13 @@ func (m *MountOpt) Set(value string) error {
4343
return mount.VolumeOptions
4444
}
4545

46+
imageOptions := func() *mounttypes.ImageOptions {
47+
if mount.ImageOptions == nil {
48+
mount.ImageOptions = new(mounttypes.ImageOptions)
49+
}
50+
return mount.ImageOptions
51+
}
52+
4653
bindOptions := func() *mounttypes.BindOptions {
4754
if mount.BindOptions == nil {
4855
mount.BindOptions = new(mounttypes.BindOptions)
@@ -147,6 +154,8 @@ func (m *MountOpt) Set(value string) error {
147154
volumeOptions().DriverConfig.Options = make(map[string]string)
148155
}
149156
setValueOnMap(volumeOptions().DriverConfig.Options, val)
157+
case "image-subpath":
158+
imageOptions().Subpath = val
150159
case "tmpfs-size":
151160
sizeBytes, err := units.RAMInBytes(val)
152161
if err != nil {
@@ -175,6 +184,9 @@ func (m *MountOpt) Set(value string) error {
175184
if mount.VolumeOptions != nil && mount.Type != mounttypes.TypeVolume {
176185
return fmt.Errorf("cannot mix 'volume-*' options with mount type '%s'", mount.Type)
177186
}
187+
if mount.ImageOptions != nil && mount.Type != mounttypes.TypeImage {
188+
return fmt.Errorf("cannot mix 'image-*' options with mount type '%s'", mount.Type)
189+
}
178190
if mount.BindOptions != nil && mount.Type != mounttypes.TypeBind {
179191
return fmt.Errorf("cannot mix 'bind-*' options with mount type '%s'", mount.Type)
180192
}

opts/mount_test.go

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -188,6 +188,27 @@ func TestMountOptTypeConflict(t *testing.T) {
188188
assert.ErrorContains(t, m.Set("type=volume,target=/foo,source=/foo,bind-propagation=rprivate"), "cannot mix")
189189
}
190190

191+
func TestMountOptSetImageNoError(t *testing.T) {
192+
for _, testcase := range []string{
193+
"type=image,source=foo,target=/target,image-subpath=/bar",
194+
} {
195+
var mount MountOpt
196+
197+
assert.NilError(t, mount.Set(testcase))
198+
199+
mounts := mount.Value()
200+
assert.Assert(t, is.Len(mounts, 1))
201+
assert.Check(t, is.DeepEqual(mounttypes.Mount{
202+
Type: mounttypes.TypeImage,
203+
Source: "foo",
204+
Target: "/target",
205+
ImageOptions: &mounttypes.ImageOptions{
206+
Subpath: "/bar",
207+
},
208+
}, mounts[0]))
209+
}
210+
}
211+
191212
func TestMountOptSetTmpfsNoError(t *testing.T) {
192213
for _, testcase := range []string{
193214
// tests several aliases that should have same result.

0 commit comments

Comments
 (0)