@@ -33,66 +33,32 @@ func (m *MountOpt) Set(value string) error {
3333 return err
3434 }
3535
36- mount := mounttypes.Mount {}
37-
38- volumeOptions := func () * mounttypes.VolumeOptions {
39- if mount .VolumeOptions == nil {
40- mount .VolumeOptions = & mounttypes.VolumeOptions {
41- Labels : make (map [string ]string ),
42- }
43- }
44- if mount .VolumeOptions .DriverConfig == nil {
45- mount .VolumeOptions .DriverConfig = & mounttypes.Driver {}
46- }
47- return mount .VolumeOptions
36+ mount := mounttypes.Mount {
37+ Type : mounttypes .TypeVolume , // default to volume mounts
4838 }
4939
50- imageOptions := func () * mounttypes.ImageOptions {
51- if mount .ImageOptions == nil {
52- mount .ImageOptions = new (mounttypes.ImageOptions )
53- }
54- return mount .ImageOptions
55- }
56-
57- bindOptions := func () * mounttypes.BindOptions {
58- if mount .BindOptions == nil {
59- mount .BindOptions = new (mounttypes.BindOptions )
60- }
61- return mount .BindOptions
62- }
63-
64- tmpfsOptions := func () * mounttypes.TmpfsOptions {
65- if mount .TmpfsOptions == nil {
66- mount .TmpfsOptions = new (mounttypes.TmpfsOptions )
40+ for _ , field := range fields {
41+ key , val , hasValue := strings .Cut (field , "=" )
42+ if k := strings .TrimSpace (key ); k != key {
43+ return fmt .Errorf ("invalid option '%s' in '%s': option should not have whitespace" , k , field )
6744 }
68- return mount .TmpfsOptions
69- }
70-
71- setValueOnMap := func (target map [string ]string , value string ) {
72- k , v , _ := strings .Cut (value , "=" )
73- if k != "" {
74- target [k ] = v
45+ if hasValue {
46+ v := strings .TrimSpace (val )
47+ if v == "" {
48+ return fmt .Errorf ("invalid value for '%s': value is empty" , key )
49+ }
50+ if v != val {
51+ return fmt .Errorf ("invalid value for '%s' in '%s': value should not have whitespace" , key , field )
52+ }
7553 }
76- }
77-
78- mount .Type = mounttypes .TypeVolume // default to volume mounts
79- // Set writable as the default
80- for _ , field := range fields {
81- key , val , ok := strings .Cut (field , "=" )
8254
8355 // TODO(thaJeztah): these options should not be case-insensitive.
8456 key = strings .ToLower (key )
8557
86- if ! ok {
58+ if ! hasValue {
8759 switch key {
88- case "readonly" , "ro" :
89- mount .ReadOnly = true
90- continue
91- case "volume-nocopy" :
92- volumeOptions ().NoCopy = true
93- continue
94- case "bind-nonrecursive" :
95- return errors .New ("bind-nonrecursive is deprecated, use bind-recursive=disabled instead" )
60+ case "readonly" , "ro" , "volume-nocopy" , "bind-nonrecursive" :
61+ // boolean values
9662 default :
9763 return fmt .Errorf ("invalid field '%s' must be a key=value pair" , field )
9864 }
@@ -111,97 +77,67 @@ func (m *MountOpt) Set(value string) error {
11177 case "target" , "dst" , "destination" :
11278 mount .Target = val
11379 case "readonly" , "ro" :
114- mount .ReadOnly , err = strconv . ParseBool ( val )
80+ mount .ReadOnly , err = parseBoolValue ( key , val , hasValue )
11581 if err != nil {
116- return fmt . Errorf ( "invalid value for %s: %s" , key , val )
82+ return err
11783 }
11884 case "consistency" :
11985 mount .Consistency = mounttypes .Consistency (strings .ToLower (val ))
12086 case "bind-propagation" :
121- bindOptions ( ).Propagation = mounttypes .Propagation (strings .ToLower (val ))
87+ ensureBindOptions ( & mount ).Propagation = mounttypes .Propagation (strings .ToLower (val ))
12288 case "bind-nonrecursive" :
12389 return errors .New ("bind-nonrecursive is deprecated, use bind-recursive=disabled instead" )
12490 case "bind-recursive" :
12591 switch val {
12692 case "enabled" : // read-only mounts are recursively read-only if Engine >= v25 && kernel >= v5.12, otherwise writable
12793 // NOP
12894 case "disabled" : // previously "bind-nonrecursive=true"
129- bindOptions ( ).NonRecursive = true
95+ ensureBindOptions ( & mount ).NonRecursive = true
13096 case "writable" : // conforms to the default read-only bind-mount of Docker v24; read-only mounts are recursively mounted but not recursively read-only
131- bindOptions ( ).ReadOnlyNonRecursive = true
97+ ensureBindOptions ( & mount ).ReadOnlyNonRecursive = true
13298 case "readonly" : // force recursively read-only, or raise an error
133- bindOptions ( ).ReadOnlyForceRecursive = true
99+ ensureBindOptions ( & mount ).ReadOnlyForceRecursive = true
134100 // TODO: implicitly set propagation and error if the user specifies a propagation in a future refactor/UX polish pass
135101 // https://github.com/docker/cli/pull/4316#discussion_r1341974730
136102 default :
137103 return fmt .Errorf (`invalid value for %s: %s (must be "enabled", "disabled", "writable", or "readonly")` , key , val )
138104 }
139105 case "volume-subpath" :
140- volumeOptions ( ).Subpath = val
106+ ensureVolumeOptions ( & mount ).Subpath = val
141107 case "volume-nocopy" :
142- volumeOptions ( ).NoCopy , err = strconv . ParseBool ( val )
108+ ensureVolumeOptions ( & mount ).NoCopy , err = parseBoolValue ( key , val , hasValue )
143109 if err != nil {
144- return fmt . Errorf ( "invalid value for volume-nocopy: %s" , val )
110+ return err
145111 }
146112 case "volume-label" :
147- setValueOnMap (volumeOptions ().Labels , val )
113+ volumeOpts := ensureVolumeOptions (& mount )
114+ volumeOpts .Labels = setValueOnMap (volumeOpts .Labels , val )
148115 case "volume-driver" :
149- volumeOptions (). DriverConfig .Name = val
116+ ensureVolumeDriver ( & mount ) .Name = val
150117 case "volume-opt" :
151- if volumeOptions ().DriverConfig .Options == nil {
152- volumeOptions ().DriverConfig .Options = make (map [string ]string )
153- }
154- setValueOnMap (volumeOptions ().DriverConfig .Options , val )
118+ volumeDriver := ensureVolumeDriver (& mount )
119+ volumeDriver .Options = setValueOnMap (volumeDriver .Options , val )
155120 case "image-subpath" :
156- imageOptions ( ).Subpath = val
121+ ensureImageOptions ( & mount ).Subpath = val
157122 case "tmpfs-size" :
158123 sizeBytes , err := units .RAMInBytes (val )
159124 if err != nil {
160125 return fmt .Errorf ("invalid value for %s: %s" , key , val )
161126 }
162- tmpfsOptions ( ).SizeBytes = sizeBytes
127+ ensureTmpfsOptions ( & mount ).SizeBytes = sizeBytes
163128 case "tmpfs-mode" :
164129 ui64 , err := strconv .ParseUint (val , 8 , 32 )
165130 if err != nil {
166131 return fmt .Errorf ("invalid value for %s: %s" , key , val )
167132 }
168- tmpfsOptions ( ).Mode = os .FileMode (ui64 )
133+ ensureTmpfsOptions ( & mount ).Mode = os .FileMode (ui64 )
169134 default :
170- return fmt .Errorf ("unexpected key '%s' in '%s'" , key , field )
135+ return fmt .Errorf ("unknown option '%s' in '%s'" , key , field )
171136 }
172137 }
173138
174- if mount .Type == "" {
175- return errors .New ("type is required" )
176- }
177-
178- if mount .VolumeOptions != nil && mount .Type != mounttypes .TypeVolume {
179- return fmt .Errorf ("cannot mix 'volume-*' options with mount type '%s'" , mount .Type )
180- }
181- if mount .ImageOptions != nil && mount .Type != mounttypes .TypeImage {
182- return fmt .Errorf ("cannot mix 'image-*' options with mount type '%s'" , mount .Type )
183- }
184- if mount .BindOptions != nil && mount .Type != mounttypes .TypeBind {
185- return fmt .Errorf ("cannot mix 'bind-*' options with mount type '%s'" , mount .Type )
186- }
187- if mount .TmpfsOptions != nil && mount .Type != mounttypes .TypeTmpfs {
188- return fmt .Errorf ("cannot mix 'tmpfs-*' options with mount type '%s'" , mount .Type )
189- }
190-
191- if mount .BindOptions != nil {
192- if mount .BindOptions .ReadOnlyNonRecursive {
193- if ! mount .ReadOnly {
194- return errors .New ("option 'bind-recursive=writable' requires 'readonly' to be specified in conjunction" )
195- }
196- }
197- if mount .BindOptions .ReadOnlyForceRecursive {
198- if ! mount .ReadOnly {
199- return errors .New ("option 'bind-recursive=readonly' requires 'readonly' to be specified in conjunction" )
200- }
201- if mount .BindOptions .Propagation != mounttypes .PropagationRPrivate {
202- return errors .New ("option 'bind-recursive=readonly' requires 'bind-propagation=rprivate' to be specified in conjunction" )
203- }
204- }
139+ if err := validateMountOptions (& mount ); err != nil {
140+ return err
205141 }
206142
207143 m .values = append (m .values , mount )
0 commit comments