Merge pull request #5755 from LaurentGoderre/image-mount-subpath
Add image mount options
This commit is contained in:
commit
c9e7daec81
@ -40,6 +40,9 @@ func handleVolumeToMount(
|
|||||||
) (mount.Mount, error) {
|
) (mount.Mount, error) {
|
||||||
result := createMountFromVolume(volume)
|
result := createMountFromVolume(volume)
|
||||||
|
|
||||||
|
if volume.Image != nil {
|
||||||
|
return mount.Mount{}, errors.New("images options are incompatible with type volume")
|
||||||
|
}
|
||||||
if volume.Tmpfs != nil {
|
if volume.Tmpfs != nil {
|
||||||
return mount.Mount{}, errors.New("tmpfs options are incompatible with type volume")
|
return mount.Mount{}, errors.New("tmpfs options are incompatible with type volume")
|
||||||
}
|
}
|
||||||
@ -86,6 +89,32 @@ func handleVolumeToMount(
|
|||||||
return result, nil
|
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) {
|
func handleBindToMount(volume composetypes.ServiceVolumeConfig) (mount.Mount, error) {
|
||||||
result := createMountFromVolume(volume)
|
result := createMountFromVolume(volume)
|
||||||
|
|
||||||
@ -95,6 +124,9 @@ func handleBindToMount(volume composetypes.ServiceVolumeConfig) (mount.Mount, er
|
|||||||
if volume.Volume != nil {
|
if volume.Volume != nil {
|
||||||
return mount.Mount{}, errors.New("volume options are incompatible with type bind")
|
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 {
|
if volume.Tmpfs != nil {
|
||||||
return mount.Mount{}, errors.New("tmpfs options are incompatible with type bind")
|
return mount.Mount{}, errors.New("tmpfs options are incompatible with type bind")
|
||||||
}
|
}
|
||||||
@ -121,6 +153,9 @@ func handleTmpfsToMount(volume composetypes.ServiceVolumeConfig) (mount.Mount, e
|
|||||||
if volume.Volume != nil {
|
if volume.Volume != nil {
|
||||||
return mount.Mount{}, errors.New("volume options are incompatible with type tmpfs")
|
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 {
|
if volume.Cluster != nil {
|
||||||
return mount.Mount{}, errors.New("cluster options are incompatible with type tmpfs")
|
return mount.Mount{}, errors.New("cluster options are incompatible with type tmpfs")
|
||||||
}
|
}
|
||||||
@ -141,6 +176,9 @@ func handleNpipeToMount(volume composetypes.ServiceVolumeConfig) (mount.Mount, e
|
|||||||
if volume.Volume != nil {
|
if volume.Volume != nil {
|
||||||
return mount.Mount{}, errors.New("volume options are incompatible with type npipe")
|
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 {
|
if volume.Tmpfs != nil {
|
||||||
return mount.Mount{}, errors.New("tmpfs options are incompatible with type npipe")
|
return mount.Mount{}, errors.New("tmpfs options are incompatible with type npipe")
|
||||||
}
|
}
|
||||||
@ -203,6 +241,8 @@ func convertVolumeToMount(
|
|||||||
switch volume.Type {
|
switch volume.Type {
|
||||||
case "volume", "":
|
case "volume", "":
|
||||||
return handleVolumeToMount(volume, stackVolumes, namespace)
|
return handleVolumeToMount(volume, stackVolumes, namespace)
|
||||||
|
case "image":
|
||||||
|
return handleImageToMount(volume)
|
||||||
case "bind":
|
case "bind":
|
||||||
return handleBindToMount(volume)
|
return handleBindToMount(volume)
|
||||||
case "tmpfs":
|
case "tmpfs":
|
||||||
|
@ -398,6 +398,7 @@ type ServiceVolumeConfig struct {
|
|||||||
Consistency string `yaml:",omitempty" json:"consistency,omitempty"`
|
Consistency string `yaml:",omitempty" json:"consistency,omitempty"`
|
||||||
Bind *ServiceVolumeBind `yaml:",omitempty" json:"bind,omitempty"`
|
Bind *ServiceVolumeBind `yaml:",omitempty" json:"bind,omitempty"`
|
||||||
Volume *ServiceVolumeVolume `yaml:",omitempty" json:"volume,omitempty"`
|
Volume *ServiceVolumeVolume `yaml:",omitempty" json:"volume,omitempty"`
|
||||||
|
Image *ServiceVolumeImage `yaml:",omitempty" json:"image,omitempty"`
|
||||||
Tmpfs *ServiceVolumeTmpfs `yaml:",omitempty" json:"tmpfs,omitempty"`
|
Tmpfs *ServiceVolumeTmpfs `yaml:",omitempty" json:"tmpfs,omitempty"`
|
||||||
Cluster *ServiceVolumeCluster `yaml:",omitempty" json:"cluster,omitempty"`
|
Cluster *ServiceVolumeCluster `yaml:",omitempty" json:"cluster,omitempty"`
|
||||||
}
|
}
|
||||||
@ -412,6 +413,11 @@ type ServiceVolumeVolume struct {
|
|||||||
NoCopy bool `mapstructure:"nocopy" yaml:"nocopy,omitempty" json:"nocopy,omitempty"`
|
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
|
// ServiceVolumeTmpfs are options for a service volume of type tmpfs
|
||||||
type ServiceVolumeTmpfs struct {
|
type ServiceVolumeTmpfs struct {
|
||||||
Size int64 `yaml:",omitempty" json:"size,omitempty"`
|
Size int64 `yaml:",omitempty" json:"size,omitempty"`
|
||||||
|
@ -43,6 +43,13 @@ func (m *MountOpt) Set(value string) error {
|
|||||||
return mount.VolumeOptions
|
return mount.VolumeOptions
|
||||||
}
|
}
|
||||||
|
|
||||||
|
imageOptions := func() *mounttypes.ImageOptions {
|
||||||
|
if mount.ImageOptions == nil {
|
||||||
|
mount.ImageOptions = new(mounttypes.ImageOptions)
|
||||||
|
}
|
||||||
|
return mount.ImageOptions
|
||||||
|
}
|
||||||
|
|
||||||
bindOptions := func() *mounttypes.BindOptions {
|
bindOptions := func() *mounttypes.BindOptions {
|
||||||
if mount.BindOptions == nil {
|
if mount.BindOptions == nil {
|
||||||
mount.BindOptions = new(mounttypes.BindOptions)
|
mount.BindOptions = new(mounttypes.BindOptions)
|
||||||
@ -147,6 +154,8 @@ func (m *MountOpt) Set(value string) error {
|
|||||||
volumeOptions().DriverConfig.Options = make(map[string]string)
|
volumeOptions().DriverConfig.Options = make(map[string]string)
|
||||||
}
|
}
|
||||||
setValueOnMap(volumeOptions().DriverConfig.Options, val)
|
setValueOnMap(volumeOptions().DriverConfig.Options, val)
|
||||||
|
case "image-subpath":
|
||||||
|
imageOptions().Subpath = val
|
||||||
case "tmpfs-size":
|
case "tmpfs-size":
|
||||||
sizeBytes, err := units.RAMInBytes(val)
|
sizeBytes, err := units.RAMInBytes(val)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@ -175,6 +184,9 @@ func (m *MountOpt) Set(value string) error {
|
|||||||
if mount.VolumeOptions != nil && mount.Type != mounttypes.TypeVolume {
|
if mount.VolumeOptions != nil && mount.Type != mounttypes.TypeVolume {
|
||||||
return fmt.Errorf("cannot mix 'volume-*' options with mount type '%s'", mount.Type)
|
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 {
|
if mount.BindOptions != nil && mount.Type != mounttypes.TypeBind {
|
||||||
return fmt.Errorf("cannot mix 'bind-*' options with mount type '%s'", mount.Type)
|
return fmt.Errorf("cannot mix 'bind-*' options with mount type '%s'", mount.Type)
|
||||||
}
|
}
|
||||||
|
@ -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")
|
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) {
|
func TestMountOptSetTmpfsNoError(t *testing.T) {
|
||||||
for _, testcase := range []string{
|
for _, testcase := range []string{
|
||||||
// tests several aliases that should have same result.
|
// tests several aliases that should have same result.
|
||||||
|
Loading…
x
Reference in New Issue
Block a user