@@ -49,6 +49,8 @@ type Options struct {
4949 SkipInterpolation bool
5050 // Skip normalization
5151 SkipNormalization bool
52+ // Resolve paths
53+ ResolvePaths bool
5254 // Skip consistency check
5355 SkipConsistencyCheck bool
5456 // Skip extends
@@ -199,7 +201,7 @@ func Load(configDetails types.ConfigDetails, options ...func(*Options)) (*types.
199201 }
200202
201203 if ! opts .SkipNormalization {
202- err = normalize (project )
204+ err = normalize (project , opts . ResolvePaths )
203205 if err != nil {
204206 return nil , err
205207 }
@@ -269,11 +271,11 @@ func loadSections(filename string, config map[string]interface{}, configDetails
269271 if err != nil {
270272 return nil , err
271273 }
272- cfg .Secrets , err = LoadSecrets (getSection (config , "secrets" ), configDetails )
274+ cfg .Secrets , err = LoadSecrets (getSection (config , "secrets" ), configDetails , opts . ResolvePaths )
273275 if err != nil {
274276 return nil , err
275277 }
276- cfg .Configs , err = LoadConfigObjs (getSection (config , "configs" ), configDetails )
278+ cfg .Configs , err = LoadConfigObjs (getSection (config , "configs" ), configDetails , opts . ResolvePaths )
277279 if err != nil {
278280 return nil , err
279281 }
@@ -443,7 +445,7 @@ func loadServiceWithExtends(filename, name string, servicesDict map[string]inter
443445 return nil , err
444446 }
445447
446- serviceConfig , err := LoadService (name , servicesDict [name ].(map [string ]interface {}), workingDir , lookupEnv )
448+ serviceConfig , err := LoadService (name , servicesDict [name ].(map [string ]interface {}), workingDir , lookupEnv , opts . ResolvePaths )
447449 if err != nil {
448450 return nil , err
449451 }
@@ -514,7 +516,7 @@ func loadServiceWithExtends(filename, name string, servicesDict map[string]inter
514516
515517// LoadService produces a single ServiceConfig from a compose file Dict
516518// the serviceDict is not validated if directly used. Use Load() to enable validation
517- func LoadService (name string , serviceDict map [string ]interface {}, workingDir string , lookupEnv template.Mapping ) (* types.ServiceConfig , error ) {
519+ func LoadService (name string , serviceDict map [string ]interface {}, workingDir string , lookupEnv template.Mapping , resolvePaths bool ) (* types.ServiceConfig , error ) {
518520 serviceConfig := & types.ServiceConfig {}
519521 if err := Transform (serviceDict , serviceConfig ); err != nil {
520522 return nil , err
@@ -525,8 +527,18 @@ func LoadService(name string, serviceDict map[string]interface{}, workingDir str
525527 return nil , err
526528 }
527529
528- if err := resolveVolumePaths (serviceConfig .Volumes , workingDir , lookupEnv ); err != nil {
529- return nil , err
530+ for i , volume := range serviceConfig .Volumes {
531+ if volume .Type != "bind" {
532+ continue
533+ }
534+
535+ if volume .Source == "" {
536+ return nil , errors .New (`invalid mount config for type "bind": field Source must not be empty` )
537+ }
538+
539+ if resolvePaths {
540+ serviceConfig .Volumes [i ] = resolveVolumePath (volume , workingDir , lookupEnv )
541+ }
530542 }
531543
532544 return serviceConfig , nil
@@ -561,30 +573,19 @@ func resolveEnvironment(serviceConfig *types.ServiceConfig, workingDir string, l
561573 return nil
562574}
563575
564- func resolveVolumePaths (volumes []types.ServiceVolumeConfig , workingDir string , lookupEnv template.Mapping ) error {
565- for i , volume := range volumes {
566- if volume .Type != "bind" {
567- continue
568- }
569-
570- if volume .Source == "" {
571- return errors .New (`invalid mount config for type "bind": field Source must not be empty` )
572- }
573-
574- filePath := expandUser (volume .Source , lookupEnv )
575- // Check if source is an absolute path (either Unix or Windows), to
576- // handle a Windows client with a Unix daemon or vice-versa.
577- //
578- // Note that this is not required for Docker for Windows when specifying
579- // a local Windows path, because Docker for Windows translates the Windows
580- // path into a valid path within the VM.
581- if ! path .IsAbs (filePath ) && ! isAbs (filePath ) {
582- filePath = absPath (workingDir , filePath )
583- }
584- volume .Source = filePath
585- volumes [i ] = volume
586- }
587- return nil
576+ func resolveVolumePath (volume types.ServiceVolumeConfig , workingDir string , lookupEnv template.Mapping ) types.ServiceVolumeConfig {
577+ filePath := expandUser (volume .Source , lookupEnv )
578+ // Check if source is an absolute path (either Unix or Windows), to
579+ // handle a Windows client with a Unix daemon or vice-versa.
580+ //
581+ // Note that this is not required for Docker for Windows when specifying
582+ // a local Windows path, because Docker for Windows translates the Windows
583+ // path into a valid path within the VM.
584+ if ! path .IsAbs (filePath ) && ! isAbs (filePath ) {
585+ filePath = absPath (workingDir , filePath )
586+ }
587+ volume .Source = filePath
588+ return volume
588589}
589590
590591// TODO: make this more robust
@@ -688,13 +689,13 @@ func LoadVolumes(source map[string]interface{}) (map[string]types.VolumeConfig,
688689
689690// LoadSecrets produces a SecretConfig map from a compose file Dict
690691// the source Dict is not validated if directly used. Use Load() to enable validation
691- func LoadSecrets (source map [string ]interface {}, details types.ConfigDetails ) (map [string ]types.SecretConfig , error ) {
692+ func LoadSecrets (source map [string ]interface {}, details types.ConfigDetails , resolvePaths bool ) (map [string ]types.SecretConfig , error ) {
692693 secrets := make (map [string ]types.SecretConfig )
693694 if err := Transform (source , & secrets ); err != nil {
694695 return secrets , err
695696 }
696697 for name , secret := range secrets {
697- obj , err := loadFileObjectConfig (name , "secret" , types .FileObjectConfig (secret ), details )
698+ obj , err := loadFileObjectConfig (name , "secret" , types .FileObjectConfig (secret ), details , resolvePaths )
698699 if err != nil {
699700 return nil , err
700701 }
@@ -706,13 +707,13 @@ func LoadSecrets(source map[string]interface{}, details types.ConfigDetails) (ma
706707
707708// LoadConfigObjs produces a ConfigObjConfig map from a compose file Dict
708709// the source Dict is not validated if directly used. Use Load() to enable validation
709- func LoadConfigObjs (source map [string ]interface {}, details types.ConfigDetails ) (map [string ]types.ConfigObjConfig , error ) {
710+ func LoadConfigObjs (source map [string ]interface {}, details types.ConfigDetails , resolvePaths bool ) (map [string ]types.ConfigObjConfig , error ) {
710711 configs := make (map [string ]types.ConfigObjConfig )
711712 if err := Transform (source , & configs ); err != nil {
712713 return configs , err
713714 }
714715 for name , config := range configs {
715- obj , err := loadFileObjectConfig (name , "config" , types .FileObjectConfig (config ), details )
716+ obj , err := loadFileObjectConfig (name , "config" , types .FileObjectConfig (config ), details , resolvePaths )
716717 if err != nil {
717718 return nil , err
718719 }
@@ -722,7 +723,7 @@ func LoadConfigObjs(source map[string]interface{}, details types.ConfigDetails)
722723 return configs , nil
723724}
724725
725- func loadFileObjectConfig (name string , objType string , obj types.FileObjectConfig , details types.ConfigDetails ) (types.FileObjectConfig , error ) {
726+ func loadFileObjectConfig (name string , objType string , obj types.FileObjectConfig , details types.ConfigDetails , resolvePaths bool ) (types.FileObjectConfig , error ) {
726727 // if "external: true"
727728 switch {
728729 case obj .External .External :
@@ -745,7 +746,9 @@ func loadFileObjectConfig(name string, objType string, obj types.FileObjectConfi
745746 return obj , errors .Errorf ("%[1]s %[2]s: %[1]s.driver and %[1]s.file conflict; only use %[1]s.driver" , objType , name )
746747 }
747748 default :
748- obj .File = absPath (details .WorkingDir , obj .File )
749+ if resolvePaths {
750+ obj .File = absPath (details .WorkingDir , obj .File )
751+ }
749752 }
750753
751754 return obj , nil
0 commit comments