@@ -828,6 +828,7 @@ func getDependentServiceFromMode(mode string) string {
828828 return ""
829829}
830830
831+ //nolint:gocyclo
831832func (s * composeService ) buildContainerVolumes (
832833 ctx context.Context ,
833834 p types.Project ,
@@ -848,30 +849,37 @@ func (s *composeService) buildContainerVolumes(
848849 return nil , nil , err
849850 }
850851
851- MOUNTS:
852852 for _ , m := range mountOptions {
853- if m .Type == mount .TypeNamedPipe {
854- mounts = append (mounts , m )
855- continue
856- }
857- if m .Type == mount .TypeBind {
853+ switch m .Type {
854+ case mount .TypeBind :
858855 // `Mount` is preferred but does not offer option to created host path if missing
859856 // so `Bind` API is used here with raw volume string
860857 // see https://github.com/moby/moby/issues/43483
861- for _ , v := range service .Volumes {
862- if v .Target == m .Target {
863- switch {
864- case string (m .Type ) != v .Type :
865- v .Source = m .Source
866- fallthrough
867- case ! requireMountAPI (v .Bind ):
868- binds = append (binds , v .String ())
869- continue MOUNTS
858+ v := findVolumeByTarget (service .Volumes , m .Target )
859+ if v != nil {
860+ switch {
861+ case v .Type != types .VolumeTypeBind :
862+ v .Source = m .Source
863+ fallthrough
864+ case ! requireMountAPI (v .Bind ):
865+ vol := findVolumeByName (p .Volumes , m .Source )
866+ if vol != nil {
867+ binds = append (binds , toBindString (vol .Name , v ))
868+ continue
870869 }
871870 }
872871 }
873- }
874- if m .Type == mount .TypeImage {
872+ case mount .TypeVolume :
873+ v := findVolumeByTarget (service .Volumes , m .Target )
874+ vol := findVolumeByName (p .Volumes , m .Source )
875+ if v != nil && vol != nil {
876+ if _ , ok := vol .DriverOpts ["device" ]; ok && vol .Driver == "local" && vol .DriverOpts ["o" ] == "bind" {
877+ // Looks like a volume, but actually a bind mount which requires the bind API
878+ binds = append (binds , toBindString (vol .Name , v ))
879+ continue
880+ }
881+ }
882+ case mount .TypeImage :
875883 version , err := s .RuntimeVersion (ctx )
876884 if err != nil {
877885 return nil , nil , err
@@ -885,6 +893,42 @@ MOUNTS:
885893 return binds , mounts , nil
886894}
887895
896+ func toBindString (name string , v * types.ServiceVolumeConfig ) string {
897+ access := "rw"
898+ if v .ReadOnly {
899+ access = "ro"
900+ }
901+ options := []string {access }
902+ if v .Bind != nil && v .Bind .SELinux != "" {
903+ options = append (options , v .Bind .SELinux )
904+ }
905+ if v .Bind != nil && v .Bind .Propagation != "" {
906+ options = append (options , v .Bind .Propagation )
907+ }
908+ if v .Volume != nil && v .Volume .NoCopy {
909+ options = append (options , "nocopy" )
910+ }
911+ return fmt .Sprintf ("%s:%s:%s" , name , v .Target , strings .Join (options , "," ))
912+ }
913+
914+ func findVolumeByName (volumes types.Volumes , name string ) * types.VolumeConfig {
915+ for _ , vol := range volumes {
916+ if vol .Name == name {
917+ return & vol
918+ }
919+ }
920+ return nil
921+ }
922+
923+ func findVolumeByTarget (volumes []types.ServiceVolumeConfig , target string ) * types.ServiceVolumeConfig {
924+ for _ , v := range volumes {
925+ if v .Target == target {
926+ return & v
927+ }
928+ }
929+ return nil
930+ }
931+
888932// requireMountAPI check if Bind declaration can be implemented by the plain old Bind API or uses any of the advanced
889933// options which require use of Mount API
890934func requireMountAPI (bind * types.ServiceVolumeBind ) bool {
0 commit comments