-
Notifications
You must be signed in to change notification settings - Fork 5.6k
Description
Description
Creating this bug report on request of @thaJeztah.
In this comment on #2781, the following behaviour is described for the creation of host paths for volumes/mounts:
So the short syntax does have the "create host path" behaviour:
...
whereas the long syntax does not:services: foo: volumes: - type: bind source: ./path/on/host target: /path/in/containerunless you explicitly add the following:
... bind: create_host_path: true
Unfortunately, the conclusion that the long syntax does not create the host path automatically is incorrect. Host paths are always created, unless create_host_path: false is explicitly set. This issue is not caused by the spec implementation used by the CLI, but by the CLI itself. This may be a regression introduced in #12734.
In create.go there is this piece of logic when creating the mounts/volumes:
switch m.Type {
case mount.TypeBind:
// `Mount` is preferred but does not offer option to created host path if missing
// so `Bind` API is used here with raw volume string
// see https://github.com/moby/moby/issues/43483
v := findVolumeByTarget(service.Volumes, m.Target)
if v != nil {
if v.Type != types.VolumeTypeBind {
v.Source = m.Source
}
if !bindRequiresMountAPI(v.Bind) {
source := m.Source
if vol := findVolumeByName(p.Volumes, m.Source); vol != nil {
source = m.Source
}
binds = append(binds, toBindString(source, v))
continue
}
}
case mount.TypeVolume:
v := findVolumeByTarget(service.Volumes, m.Target)
vol := findVolumeByName(p.Volumes, m.Source)
if v != nil && vol != nil {
// Prefer the bind API if no advanced option is used, to preserve backward compatibility
if !volumeRequiresMountAPI(v.Volume) {
binds = append(binds, toBindString(vol.Name, v))
continue
}
}Here we can see that both bind mounts and volume mounts are converted to a bind string if the mount does not use any features which require the mount API. This is checked in bindRequiresMountAPI. Let's have a look at that check:
func bindRequiresMountAPI(bind *types.ServiceVolumeBind) bool {
switch {
case bind == nil:
return false
case !bind.CreateHostPath:
return true
case bind.Propagation != "":
return true
case bind.Recursive != "":
return true
default:
return false
}
}Note that if CreateHostPath is not explicitly set to false, and no other bind options are set in the yaml, the case bind == nil will match. This is because the whole ServiceVolumeBindpointer is unmarshaled to nil if it is not in the yaml, since it is tagged omitempty. It was probably expected to be present with all its values set to zero, to match the case !bind.CreateHostPath
This implementation results in it being treated as a normal volume, and therefore the host path will be created automatically later on, even though the value of CreateHostPath, i.e. create_host_path is not set to to true.
Steps To Reproduce
- Remove lines 371-372 from
pkg/compose/create_test.go. - Run
Test_buildContainerVolumes. It now fails.
This testcase should still be successful if bind mounts only create host paths if explicitly asked to with:
bind:
create_host_path: trueCompose Version
v2.34.0-desktop.1 and in main
Docker Environment
Anything else?
No response