1717package loader
1818
1919import (
20+ "context"
2021 "fmt"
2122 "os"
2223 paths "path"
@@ -68,6 +69,16 @@ type Options struct {
6869 projectNameImperativelySet bool
6970 // Profiles set profiles to enable
7071 Profiles []string
72+ // ResourceLoaders manages support for remote resources
73+ ResourceLoaders []ResourceLoader
74+ }
75+
76+ // ResourceLoader is a plugable remote resource resolver
77+ type ResourceLoader interface {
78+ // Accept returns `true` is the resource reference matches ResourceLoader supported protocol(s)
79+ Accept (path string ) bool
80+ // Load returns the path to a local copy of remote resource identified by `path`.
81+ Load (ctx context.Context , path string ) (string , error )
7182}
7283
7384func (o * Options ) clone () * Options {
@@ -85,6 +96,7 @@ func (o *Options) clone() *Options {
8596 projectName : o .projectName ,
8697 projectNameImperativelySet : o .projectNameImperativelySet ,
8798 Profiles : o .Profiles ,
99+ ResourceLoaders : o .ResourceLoaders ,
88100 }
89101}
90102
@@ -193,8 +205,14 @@ func parseYAML(source []byte) (map[string]interface{}, PostProcessor, error) {
193205 return converted .(map [string ]interface {}), & processor , nil
194206}
195207
196- // Load reads a ConfigDetails and returns a fully loaded configuration
208+ // Load reads a ConfigDetails and returns a fully loaded configuration.
209+ // Deprecated: use LoadWithContext.
197210func Load (configDetails types.ConfigDetails , options ... func (* Options )) (* types.Project , error ) {
211+ return LoadWithContext (context .Background (), configDetails , options ... )
212+ }
213+
214+ // LoadWithContext reads a ConfigDetails and returns a fully loaded configuration
215+ func LoadWithContext (ctx context.Context , configDetails types.ConfigDetails , options ... func (* Options )) (* types.Project , error ) {
198216 if len (configDetails .ConfigFiles ) < 1 {
199217 return nil , errors .Errorf ("No files specified" )
200218 }
@@ -217,10 +235,10 @@ func Load(configDetails types.ConfigDetails, options ...func(*Options)) (*types.
217235 return nil , err
218236 }
219237 opts .projectName = projectName
220- return load (configDetails , opts , nil )
238+ return load (ctx , configDetails , opts , nil )
221239}
222240
223- func load (configDetails types.ConfigDetails , opts * Options , loaded []string ) (* types.Project , error ) {
241+ func load (ctx context. Context , configDetails types.ConfigDetails , opts * Options , loaded []string ) (* types.Project , error ) {
224242 var model * types.Config
225243
226244 mainFile := configDetails .ConfigFiles [0 ].Filename
@@ -261,13 +279,13 @@ func load(configDetails types.ConfigDetails, opts *Options, loaded []string) (*t
261279
262280 configDict = groupXFieldsIntoExtensions (configDict )
263281
264- cfg , err := loadSections (file .Filename , configDict , configDetails , opts )
282+ cfg , err := loadSections (ctx , file .Filename , configDict , configDetails , opts )
265283 if err != nil {
266284 return nil , err
267285 }
268286
269287 if ! opts .SkipInclude {
270- cfg , err = loadInclude (configDetails , cfg , opts , loaded )
288+ cfg , err = loadInclude (ctx , configDetails , cfg , opts , loaded )
271289 if err != nil {
272290 return nil , err
273291 }
@@ -453,7 +471,7 @@ func groupXFieldsIntoExtensions(dict map[string]interface{}) map[string]interfac
453471 return dict
454472}
455473
456- func loadSections (filename string , config map [string ]interface {}, configDetails types.ConfigDetails , opts * Options ) (* types.Config , error ) {
474+ func loadSections (ctx context. Context , filename string , config map [string ]interface {}, configDetails types.ConfigDetails , opts * Options ) (* types.Config , error ) {
457475 var err error
458476 cfg := types.Config {
459477 Filename : filename ,
@@ -466,7 +484,7 @@ func loadSections(filename string, config map[string]interface{}, configDetails
466484 }
467485 }
468486 cfg .Name = name
469- cfg .Services , err = LoadServices (filename , getSection (config , "services" ), configDetails .WorkingDir , configDetails .LookupEnv , opts )
487+ cfg .Services , err = LoadServices (ctx , filename , getSection (config , "services" ), configDetails .WorkingDir , configDetails .LookupEnv , opts )
470488 if err != nil {
471489 return nil , err
472490 }
@@ -659,7 +677,7 @@ func formatInvalidKeyError(keyPrefix string, key interface{}) error {
659677
660678// LoadServices produces a ServiceConfig map from a compose file Dict
661679// the servicesDict is not validated if directly used. Use Load() to enable validation
662- func LoadServices (filename string , servicesDict map [string ]interface {}, workingDir string , lookupEnv template.Mapping , opts * Options ) ([]types.ServiceConfig , error ) {
680+ func LoadServices (ctx context. Context , filename string , servicesDict map [string ]interface {}, workingDir string , lookupEnv template.Mapping , opts * Options ) ([]types.ServiceConfig , error ) {
663681 var services []types.ServiceConfig
664682
665683 x , ok := servicesDict [extensions ]
@@ -672,7 +690,7 @@ func LoadServices(filename string, servicesDict map[string]interface{}, workingD
672690 }
673691
674692 for name := range servicesDict {
675- serviceConfig , err := loadServiceWithExtends (filename , name , servicesDict , workingDir , lookupEnv , opts , & cycleTracker {})
693+ serviceConfig , err := loadServiceWithExtends (ctx , filename , name , servicesDict , workingDir , lookupEnv , opts , & cycleTracker {})
676694 if err != nil {
677695 return nil , err
678696 }
@@ -683,7 +701,7 @@ func LoadServices(filename string, servicesDict map[string]interface{}, workingD
683701 return services , nil
684702}
685703
686- func loadServiceWithExtends (filename , name string , servicesDict map [string ]interface {}, workingDir string , lookupEnv template.Mapping , opts * Options , ct * cycleTracker ) (* types.ServiceConfig , error ) {
704+ func loadServiceWithExtends (ctx context. Context , filename , name string , servicesDict map [string ]interface {}, workingDir string , lookupEnv template.Mapping , opts * Options , ct * cycleTracker ) (* types.ServiceConfig , error ) {
687705 if err := ct .Add (filename , name ); err != nil {
688706 return nil , err
689707 }
@@ -707,11 +725,21 @@ func loadServiceWithExtends(filename, name string, servicesDict map[string]inter
707725 var baseService * types.ServiceConfig
708726 file := serviceConfig .Extends .File
709727 if file == "" {
710- baseService , err = loadServiceWithExtends (filename , baseServiceName , servicesDict , workingDir , lookupEnv , opts , ct )
728+ baseService , err = loadServiceWithExtends (ctx , filename , baseServiceName , servicesDict , workingDir , lookupEnv , opts , ct )
711729 if err != nil {
712730 return nil , err
713731 }
714732 } else {
733+ for _ , loader := range opts .ResourceLoaders {
734+ if loader .Accept (file ) {
735+ path , err := loader .Load (ctx , file )
736+ if err != nil {
737+ return nil , err
738+ }
739+ file = path
740+ break
741+ }
742+ }
715743 // Resolve the path to the imported file, and load it.
716744 baseFilePath := absPath (workingDir , file )
717745
@@ -726,7 +754,7 @@ func loadServiceWithExtends(filename, name string, servicesDict map[string]inter
726754 }
727755
728756 baseFileServices := getSection (baseFile , "services" )
729- baseService , err = loadServiceWithExtends (baseFilePath , baseServiceName , baseFileServices , filepath .Dir (baseFilePath ), lookupEnv , opts , ct )
757+ baseService , err = loadServiceWithExtends (ctx , baseFilePath , baseServiceName , baseFileServices , filepath .Dir (baseFilePath ), lookupEnv , opts , ct )
730758 if err != nil {
731759 return nil , err
732760 }
0 commit comments