mirror of https://github.com/containers/podman.git
Hit a number of to-do comments in unified volumes code
As part of this, move bind mount option validity parsing and modification (adding e.g. rbind on bind mounts that are missing it), which requires test changes (expected values have changed). Signed-off-by: Matthew Heon <matthew.heon@pm.me>
This commit is contained in:
parent
70beb57faa
commit
13451cab5c
|
@ -79,6 +79,7 @@ func (config *CreateConfig) parseVolumes(runtime *libpod.Runtime) ([]spec.Mount,
|
||||||
}
|
}
|
||||||
|
|
||||||
// Unify mounts from --mount, --volume, --tmpfs.
|
// Unify mounts from --mount, --volume, --tmpfs.
|
||||||
|
// Also add mounts + volumes directly from createconfig.
|
||||||
// Start with --volume.
|
// Start with --volume.
|
||||||
for dest, mount := range volumeMounts {
|
for dest, mount := range volumeMounts {
|
||||||
if _, ok := unifiedMounts[dest]; ok {
|
if _, ok := unifiedMounts[dest]; ok {
|
||||||
|
@ -99,8 +100,21 @@ func (config *CreateConfig) parseVolumes(runtime *libpod.Runtime) ([]spec.Mount,
|
||||||
}
|
}
|
||||||
unifiedMounts[dest] = tmpfs
|
unifiedMounts[dest] = tmpfs
|
||||||
}
|
}
|
||||||
|
// Now spec mounts and volumes
|
||||||
// TODO: Check for conflicts between volumes and named volumes.
|
for _, mount := range config.Mounts {
|
||||||
|
dest := mount.Destination
|
||||||
|
if _, ok := unifiedMounts[dest]; ok {
|
||||||
|
return nil, nil, errors.Wrapf(errDuplicateDest, dest)
|
||||||
|
}
|
||||||
|
unifiedMounts[dest] = mount
|
||||||
|
}
|
||||||
|
for _, volume := range config.NamedVolumes {
|
||||||
|
dest := volume.Dest
|
||||||
|
if _, ok := unifiedVolumes[dest]; ok {
|
||||||
|
return nil, nil, errors.Wrapf(errDuplicateDest, dest)
|
||||||
|
}
|
||||||
|
unifiedVolumes[dest] = volume
|
||||||
|
}
|
||||||
|
|
||||||
// If requested, add container init binary
|
// If requested, add container init binary
|
||||||
if config.Init {
|
if config.Init {
|
||||||
|
@ -131,9 +145,17 @@ func (config *CreateConfig) parseVolumes(runtime *libpod.Runtime) ([]spec.Mount,
|
||||||
baseVolumes[dest] = volume
|
baseVolumes[dest] = volume
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO: Allow an explicit list of named volumes and spec mounts in the
|
// Check for conflicts between named volumes and mounts
|
||||||
// createconfig. Podman doesn't need them, but other libpod consumers
|
for dest := range unifiedMounts {
|
||||||
// might want them.
|
if _, ok := unifiedVolumes[dest]; ok {
|
||||||
|
return nil, nil, errors.Wrapf(errDuplicateDest, "conflict at mount destination %v", dest)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
for dest := range unifiedVolumes {
|
||||||
|
if _, ok := unifiedMounts[dest]; ok {
|
||||||
|
return nil, nil, errors.Wrapf(errDuplicateDest, "conflict at mount destination %v", dest)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// Final step: maps to arrays
|
// Final step: maps to arrays
|
||||||
finalMounts := make([]spec.Mount, 0, len(baseMounts))
|
finalMounts := make([]spec.Mount, 0, len(baseMounts))
|
||||||
|
@ -164,19 +186,11 @@ func (config *CreateConfig) getVolumesFrom(runtime *libpod.Runtime) (map[string]
|
||||||
|
|
||||||
// Both of these are maps of mount destination to mount type.
|
// Both of these are maps of mount destination to mount type.
|
||||||
// We ensure that each destination is only mounted to once in this way.
|
// We ensure that each destination is only mounted to once in this way.
|
||||||
// We tiebreak only within each category; we'll let libpod tiebreak
|
|
||||||
// between mounts and named volumes.
|
|
||||||
// TODO: Should we unmount subtree mounts? E.g., if /tmp/ is mounted by
|
|
||||||
// one mount, and we already have /tmp/a and /tmp/b, should we remove
|
|
||||||
// the /tmp/a and /tmp/b mounts in favor of the more general /tmp?
|
|
||||||
finalMounts := make(map[string]spec.Mount)
|
finalMounts := make(map[string]spec.Mount)
|
||||||
finalNamedVolumes := make(map[string]*libpod.ContainerNamedVolume)
|
finalNamedVolumes := make(map[string]*libpod.ContainerNamedVolume)
|
||||||
|
|
||||||
for _, vol := range config.VolumesFrom {
|
for _, vol := range config.VolumesFrom {
|
||||||
options := []string{}
|
options := []string{}
|
||||||
// TODO: now that container names may contain colons this is no
|
|
||||||
// longer safe.
|
|
||||||
// I don't know if there's much we can do here...
|
|
||||||
splitVol := strings.SplitN(vol, ":", 2)
|
splitVol := strings.SplitN(vol, ":", 2)
|
||||||
if len(splitVol) == 2 {
|
if len(splitVol) == 2 {
|
||||||
if strings.Contains(splitVol[1], "Z") ||
|
if strings.Contains(splitVol[1], "Z") ||
|
||||||
|
@ -185,10 +199,10 @@ func (config *CreateConfig) getVolumesFrom(runtime *libpod.Runtime) (map[string]
|
||||||
strings.Contains(splitVol[1], "shared") {
|
strings.Contains(splitVol[1], "shared") {
|
||||||
return nil, nil, errors.Errorf("invalid options %q, can only specify 'ro', 'rw', and 'z", splitVol[1])
|
return nil, nil, errors.Errorf("invalid options %q, can only specify 'ro', 'rw', and 'z", splitVol[1])
|
||||||
}
|
}
|
||||||
if err := ValidateVolumeOpts(splitVol[1]); err != nil {
|
options = strings.Split(splitVol[1], ",")
|
||||||
|
if err := ValidateVolumeOpts(options); err != nil {
|
||||||
return nil, nil, err
|
return nil, nil, err
|
||||||
}
|
}
|
||||||
options = strings.Split(splitVol[1], ",")
|
|
||||||
}
|
}
|
||||||
ctr, err := runtime.LookupContainer(splitVol[0])
|
ctr, err := runtime.LookupContainer(splitVol[0])
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
@ -339,14 +353,12 @@ func getBindMount(args []string) (spec.Mount, error) {
|
||||||
kv := strings.Split(val, "=")
|
kv := strings.Split(val, "=")
|
||||||
switch kv[0] {
|
switch kv[0] {
|
||||||
case "ro", "nosuid", "nodev", "noexec":
|
case "ro", "nosuid", "nodev", "noexec":
|
||||||
// TODO: detect duplication of these options
|
// TODO: detect duplication of these options.
|
||||||
|
// (Is this necessary?)
|
||||||
newMount.Options = append(newMount.Options, kv[0])
|
newMount.Options = append(newMount.Options, kv[0])
|
||||||
case "shared", "rshared", "private", "rprivate", "slave", "rslave", "Z", "z":
|
case "shared", "rshared", "private", "rprivate", "slave", "rslave", "Z", "z":
|
||||||
// TODO: detect duplicate mount propagation options
|
|
||||||
newMount.Options = append(newMount.Options, kv[0])
|
newMount.Options = append(newMount.Options, kv[0])
|
||||||
case "bind-propagation":
|
case "bind-propagation":
|
||||||
// TODO: this should conflict with the mount propagation
|
|
||||||
// options above.
|
|
||||||
if len(kv) == 1 {
|
if len(kv) == 1 {
|
||||||
return newMount, errors.Wrapf(optionArgError, kv[0])
|
return newMount, errors.Wrapf(optionArgError, kv[0])
|
||||||
}
|
}
|
||||||
|
@ -382,6 +394,10 @@ func getBindMount(args []string) (spec.Mount, error) {
|
||||||
newMount.Source = newMount.Destination
|
newMount.Source = newMount.Destination
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if err := ValidateVolumeOpts(newMount.Options); err != nil {
|
||||||
|
return newMount, err
|
||||||
|
}
|
||||||
|
|
||||||
return newMount, nil
|
return newMount, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -509,28 +525,27 @@ func ValidateVolumeCtrDir(ctrDir string) error {
|
||||||
}
|
}
|
||||||
|
|
||||||
// ValidateVolumeOpts validates a volume's options
|
// ValidateVolumeOpts validates a volume's options
|
||||||
func ValidateVolumeOpts(option string) error {
|
func ValidateVolumeOpts(options []string) error {
|
||||||
var foundRootPropagation, foundRWRO, foundLabelChange int
|
var foundRootPropagation, foundRWRO, foundLabelChange int
|
||||||
options := strings.Split(option, ",")
|
|
||||||
for _, opt := range options {
|
for _, opt := range options {
|
||||||
switch opt {
|
switch opt {
|
||||||
case "rw", "ro":
|
case "rw", "ro":
|
||||||
foundRWRO++
|
foundRWRO++
|
||||||
if foundRWRO > 1 {
|
if foundRWRO > 1 {
|
||||||
return errors.Errorf("invalid options %q, can only specify 1 'rw' or 'ro' option", option)
|
return errors.Errorf("invalid options %q, can only specify 1 'rw' or 'ro' option", strings.Join(options, ", "))
|
||||||
}
|
}
|
||||||
case "z", "Z":
|
case "z", "Z":
|
||||||
foundLabelChange++
|
foundLabelChange++
|
||||||
if foundLabelChange > 1 {
|
if foundLabelChange > 1 {
|
||||||
return errors.Errorf("invalid options %q, can only specify 1 'z' or 'Z' option", option)
|
return errors.Errorf("invalid options %q, can only specify 1 'z' or 'Z' option", strings.Join(options, ", "))
|
||||||
}
|
}
|
||||||
case "private", "rprivate", "shared", "rshared", "slave", "rslave":
|
case "private", "rprivate", "shared", "rshared", "slave", "rslave":
|
||||||
foundRootPropagation++
|
foundRootPropagation++
|
||||||
if foundRootPropagation > 1 {
|
if foundRootPropagation > 1 {
|
||||||
return errors.Errorf("invalid options %q, can only specify 1 '[r]shared', '[r]private' or '[r]slave' option", option)
|
return errors.Errorf("invalid options %q, can only specify 1 '[r]shared', '[r]private' or '[r]slave' option", strings.Join(options, ", "))
|
||||||
}
|
}
|
||||||
default:
|
default:
|
||||||
return errors.Errorf("invalid option type %q", option)
|
return errors.Errorf("invalid option type %q", opt)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return nil
|
return nil
|
||||||
|
@ -562,10 +577,10 @@ func (config *CreateConfig) getVolumeMounts() (map[string]spec.Mount, map[string
|
||||||
dest = splitVol[1]
|
dest = splitVol[1]
|
||||||
}
|
}
|
||||||
if len(splitVol) > 2 {
|
if len(splitVol) > 2 {
|
||||||
if err := ValidateVolumeOpts(splitVol[2]); err != nil {
|
options = strings.Split(splitVol[2], ",")
|
||||||
|
if err := ValidateVolumeOpts(options); err != nil {
|
||||||
return nil, nil, err
|
return nil, nil, err
|
||||||
}
|
}
|
||||||
options = strings.Split(splitVol[2], ",")
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if err := ValidateVolumeHostDir(src); err != nil {
|
if err := ValidateVolumeHostDir(src); err != nil {
|
||||||
|
@ -724,6 +739,10 @@ func processOptions(options []string) []string {
|
||||||
return options
|
return options
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Supercede existing mounts in the spec with new, user-specified mounts.
|
||||||
|
// TODO: Should we unmount subtree mounts? E.g., if /tmp/ is mounted by
|
||||||
|
// one mount, and we already have /tmp/a and /tmp/b, should we remove
|
||||||
|
// the /tmp/a and /tmp/b mounts in favor of the more general /tmp?
|
||||||
func supercedeUserMounts(mounts []spec.Mount, configMount []spec.Mount) []spec.Mount {
|
func supercedeUserMounts(mounts []spec.Mount, configMount []spec.Mount) []spec.Mount {
|
||||||
if len(mounts) > 0 {
|
if len(mounts) > 0 {
|
||||||
// If we have overlappings mounts, remove them from the spec in favor of
|
// If we have overlappings mounts, remove them from the spec in favor of
|
||||||
|
|
|
@ -13,7 +13,7 @@ func TestGetVolumeMountsOneVolume(t *testing.T) {
|
||||||
Destination: "/foobar",
|
Destination: "/foobar",
|
||||||
Type: "bind",
|
Type: "bind",
|
||||||
Source: "/tmp",
|
Source: "/tmp",
|
||||||
Options: []string{"ro", "rbind", "rprivate"},
|
Options: []string{"ro"},
|
||||||
}
|
}
|
||||||
config := CreateConfig{
|
config := CreateConfig{
|
||||||
Volumes: []string{"/tmp:/foobar:ro"},
|
Volumes: []string{"/tmp:/foobar:ro"},
|
||||||
|
|
Loading…
Reference in New Issue