@@ -48,6 +48,8 @@ type Options struct {
4848 SkipInterpolation bool
4949 // Skip normalization
5050 SkipNormalization bool
51+ // Resolve paths
52+ ResolvePaths bool
5153 // Skip consistency check
5254 SkipConsistencyCheck bool
5355 // Skip extends
@@ -203,7 +205,7 @@ func Load(configDetails types.ConfigDetails, options ...func(*Options)) (*types.
203205 }
204206
205207 if ! opts .SkipNormalization {
206- err = normalize (project )
208+ err = normalize (project , opts . ResolvePaths )
207209 if err != nil {
208210 return nil , err
209211 }
@@ -266,11 +268,11 @@ func loadSections(filename string, config map[string]interface{}, configDetails
266268 if err != nil {
267269 return nil , err
268270 }
269- cfg .Secrets , err = LoadSecrets (getSection (config , "secrets" ), configDetails )
271+ cfg .Secrets , err = LoadSecrets (getSection (config , "secrets" ), configDetails , opts . ResolvePaths )
270272 if err != nil {
271273 return nil , err
272274 }
273- cfg .Configs , err = LoadConfigObjs (getSection (config , "configs" ), configDetails )
275+ cfg .Configs , err = LoadConfigObjs (getSection (config , "configs" ), configDetails , opts . ResolvePaths )
274276 if err != nil {
275277 return nil , err
276278 }
@@ -448,7 +450,7 @@ func loadServiceWithExtends(filename, name string, servicesDict map[string]inter
448450 return nil , err
449451 }
450452
451- serviceConfig , err := LoadService (name , servicesDict [name ].(map [string ]interface {}), workingDir , lookupEnv )
453+ serviceConfig , err := LoadService (name , servicesDict [name ].(map [string ]interface {}), workingDir , lookupEnv , opts . ResolvePaths )
452454 if err != nil {
453455 return nil , err
454456 }
@@ -519,7 +521,7 @@ func loadServiceWithExtends(filename, name string, servicesDict map[string]inter
519521
520522// LoadService produces a single ServiceConfig from a compose file Dict
521523// the serviceDict is not validated if directly used. Use Load() to enable validation
522- func LoadService (name string , serviceDict map [string ]interface {}, workingDir string , lookupEnv template.Mapping ) (* types.ServiceConfig , error ) {
524+ func LoadService (name string , serviceDict map [string ]interface {}, workingDir string , lookupEnv template.Mapping , resolvePaths bool ) (* types.ServiceConfig , error ) {
523525 serviceConfig := & types.ServiceConfig {}
524526 if err := Transform (serviceDict , serviceConfig ); err != nil {
525527 return nil , err
@@ -530,8 +532,18 @@ func LoadService(name string, serviceDict map[string]interface{}, workingDir str
530532 return nil , err
531533 }
532534
533- if err := resolveVolumePaths (serviceConfig .Volumes , workingDir , lookupEnv ); err != nil {
534- return nil , err
535+ for i , volume := range serviceConfig .Volumes {
536+ if volume .Type != "bind" {
537+ continue
538+ }
539+
540+ if volume .Source == "" {
541+ return nil , errors .New (`invalid mount config for type "bind": field Source must not be empty` )
542+ }
543+
544+ if resolvePaths {
545+ serviceConfig .Volumes [i ] = resolveVolumePath (volume , workingDir , lookupEnv )
546+ }
535547 }
536548
537549 return serviceConfig , nil
@@ -566,30 +578,19 @@ func resolveEnvironment(serviceConfig *types.ServiceConfig, workingDir string, l
566578 return nil
567579}
568580
569- func resolveVolumePaths (volumes []types.ServiceVolumeConfig , workingDir string , lookupEnv template.Mapping ) error {
570- for i , volume := range volumes {
571- if volume .Type != "bind" {
572- continue
573- }
574-
575- if volume .Source == "" {
576- return errors .New (`invalid mount config for type "bind": field Source must not be empty` )
577- }
578-
579- filePath := expandUser (volume .Source , lookupEnv )
580- // Check if source is an absolute path (either Unix or Windows), to
581- // handle a Windows client with a Unix daemon or vice-versa.
582- //
583- // Note that this is not required for Docker for Windows when specifying
584- // a local Windows path, because Docker for Windows translates the Windows
585- // path into a valid path within the VM.
586- if ! path .IsAbs (filePath ) && ! isAbs (filePath ) {
587- filePath = absPath (workingDir , filePath )
588- }
589- volume .Source = filePath
590- volumes [i ] = volume
591- }
592- return nil
581+ func resolveVolumePath (volume types.ServiceVolumeConfig , workingDir string , lookupEnv template.Mapping ) types.ServiceVolumeConfig {
582+ filePath := expandUser (volume .Source , lookupEnv )
583+ // Check if source is an absolute path (either Unix or Windows), to
584+ // handle a Windows client with a Unix daemon or vice-versa.
585+ //
586+ // Note that this is not required for Docker for Windows when specifying
587+ // a local Windows path, because Docker for Windows translates the Windows
588+ // path into a valid path within the VM.
589+ if ! path .IsAbs (filePath ) && ! isAbs (filePath ) {
590+ filePath = absPath (workingDir , filePath )
591+ }
592+ volume .Source = filePath
593+ return volume
593594}
594595
595596// TODO: make this more robust
@@ -693,13 +694,13 @@ func LoadVolumes(source map[string]interface{}) (map[string]types.VolumeConfig,
693694
694695// LoadSecrets produces a SecretConfig map from a compose file Dict
695696// the source Dict is not validated if directly used. Use Load() to enable validation
696- func LoadSecrets (source map [string ]interface {}, details types.ConfigDetails ) (map [string ]types.SecretConfig , error ) {
697+ func LoadSecrets (source map [string ]interface {}, details types.ConfigDetails , resolvePaths bool ) (map [string ]types.SecretConfig , error ) {
697698 secrets := make (map [string ]types.SecretConfig )
698699 if err := Transform (source , & secrets ); err != nil {
699700 return secrets , err
700701 }
701702 for name , secret := range secrets {
702- obj , err := loadFileObjectConfig (name , "secret" , types .FileObjectConfig (secret ), details )
703+ obj , err := loadFileObjectConfig (name , "secret" , types .FileObjectConfig (secret ), details , resolvePaths )
703704 if err != nil {
704705 return nil , err
705706 }
@@ -711,13 +712,13 @@ func LoadSecrets(source map[string]interface{}, details types.ConfigDetails) (ma
711712
712713// LoadConfigObjs produces a ConfigObjConfig map from a compose file Dict
713714// the source Dict is not validated if directly used. Use Load() to enable validation
714- func LoadConfigObjs (source map [string ]interface {}, details types.ConfigDetails ) (map [string ]types.ConfigObjConfig , error ) {
715+ func LoadConfigObjs (source map [string ]interface {}, details types.ConfigDetails , resolvePaths bool ) (map [string ]types.ConfigObjConfig , error ) {
715716 configs := make (map [string ]types.ConfigObjConfig )
716717 if err := Transform (source , & configs ); err != nil {
717718 return configs , err
718719 }
719720 for name , config := range configs {
720- obj , err := loadFileObjectConfig (name , "config" , types .FileObjectConfig (config ), details )
721+ obj , err := loadFileObjectConfig (name , "config" , types .FileObjectConfig (config ), details , resolvePaths )
721722 if err != nil {
722723 return nil , err
723724 }
@@ -727,7 +728,7 @@ func LoadConfigObjs(source map[string]interface{}, details types.ConfigDetails)
727728 return configs , nil
728729}
729730
730- func loadFileObjectConfig (name string , objType string , obj types.FileObjectConfig , details types.ConfigDetails ) (types.FileObjectConfig , error ) {
731+ func loadFileObjectConfig (name string , objType string , obj types.FileObjectConfig , details types.ConfigDetails , resolvePaths bool ) (types.FileObjectConfig , error ) {
731732 // if "external: true"
732733 switch {
733734 case obj .External .External :
@@ -750,7 +751,9 @@ func loadFileObjectConfig(name string, objType string, obj types.FileObjectConfi
750751 return obj , errors .Errorf ("%[1]s %[2]s: %[1]s.driver and %[1]s.file conflict; only use %[1]s.driver" , objType , name )
751752 }
752753 default :
753- obj .File = absPath (details .WorkingDir , obj .File )
754+ if resolvePaths {
755+ obj .File = absPath (details .WorkingDir , obj .File )
756+ }
754757 }
755758
756759 return obj , nil
0 commit comments