Skip to content

Commit ae43465

Browse files
authored
load: include details about included files on *Project (#444)
Add a map to `Project` that has Compose YAML filename as key and any `IncludeConfig`s loaded from it. This is populated as the project is recursively loaded. For example: ``` proj/ compose.yaml a/ compose.yaml b/ compose.yaml ``` If `project/compose.yaml` has: ``` include: ['./a/compose.yaml'] ``` And `project/a/compose.yaml` has: ``` include: ['../b/compose.yaml'] ``` The final result after load is conceptually: ``` { "proj/compose.yaml": ["proj/a/compose.yaml"], "proj/a/compose.yaml": ["proj/b/compose.yaml"], } ``` (Note: in reality, it's a list of `IncludeConfig`, which has multiple fields. Example is simplified.) Relative path resolution is based on overall loader configuration, but note that disabling it does not work properly for `include` currently due to other issues. This makes it possible for the caller to understand a bit more about the loaded project resources. We're really overdue for a bit of an overhaul/refactor of the loader - at that point, I think it might be better to have a `Loader` object type that can track stuff like this on the instance because it's weirdly both part of the project and NOT part of the project at the moment (similar to `Profiles`, project name, etc). Signed-off-by: Milas Bowman <[email protected]>
1 parent e5265c8 commit ae43465

File tree

5 files changed

+77
-17
lines changed

5 files changed

+77
-17
lines changed

loader/include.go

Lines changed: 12 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -45,14 +45,17 @@ var transformIncludeConfig TransformerFunc = func(data interface{}) (interface{}
4545
}
4646
}
4747

48-
func loadInclude(ctx context.Context, configDetails types.ConfigDetails, model *types.Config, options *Options, loaded []string) (*types.Config, error) {
48+
func loadInclude(ctx context.Context, filename string, configDetails types.ConfigDetails, model *types.Config, options *Options, loaded []string) (*types.Config, map[string][]types.IncludeConfig, error) {
49+
included := make(map[string][]types.IncludeConfig)
4950
for _, r := range model.Include {
51+
included[filename] = append(included[filename], r)
52+
5053
for i, p := range r.Path {
5154
for _, loader := range options.ResourceLoaders {
5255
if loader.Accept(p) {
5356
path, err := loader.Load(ctx, p)
5457
if err != nil {
55-
return nil, err
58+
return nil, nil, err
5659
}
5760
p = path
5861
break
@@ -72,7 +75,7 @@ func loadInclude(ctx context.Context, configDetails types.ConfigDetails, model *
7275

7376
env, err := dotenv.GetEnvFromFile(configDetails.Environment, r.ProjectDirectory, r.EnvFile)
7477
if err != nil {
75-
return nil, err
78+
return nil, nil, err
7679
}
7780

7881
config := types.ConfigDetails{
@@ -87,16 +90,19 @@ func loadInclude(ctx context.Context, configDetails types.ConfigDetails, model *
8790
}
8891
imported, err := load(ctx, config, loadOptions, loaded)
8992
if err != nil {
90-
return nil, err
93+
return nil, nil, err
94+
}
95+
for k, v := range imported.IncludeReferences {
96+
included[k] = append(included[k], v...)
9197
}
9298

9399
err = importResources(model, imported, r.Path)
94100
if err != nil {
95-
return nil, err
101+
return nil, nil, err
96102
}
97103
}
98104
model.Include = nil
99-
return model, nil
105+
return model, included, nil
100106
}
101107

102108
// importResources import into model all resources defined by imported, and report error on conflict

loader/loader.go

Lines changed: 10 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -250,6 +250,7 @@ func load(ctx context.Context, configDetails types.ConfigDetails, opts *Options,
250250
}
251251
loaded = append(loaded, mainFile)
252252

253+
includeRefs := make(map[string][]types.IncludeConfig)
253254
for i, file := range configDetails.ConfigFiles {
254255
var postProcessor PostProcessor
255256
configDict := file.Config
@@ -285,10 +286,14 @@ func load(ctx context.Context, configDetails types.ConfigDetails, opts *Options,
285286
}
286287

287288
if !opts.SkipInclude {
288-
cfg, err = loadInclude(ctx, configDetails, cfg, opts, loaded)
289+
var included map[string][]types.IncludeConfig
290+
cfg, included, err = loadInclude(ctx, file.Filename, configDetails, cfg, opts, loaded)
289291
if err != nil {
290292
return nil, err
291293
}
294+
for k, v := range included {
295+
includeRefs[k] = append(includeRefs[k], v...)
296+
}
292297
}
293298

294299
if i == 0 {
@@ -321,6 +326,10 @@ func load(ctx context.Context, configDetails types.ConfigDetails, opts *Options,
321326
Extensions: model.Extensions,
322327
}
323328

329+
if len(includeRefs) != 0 {
330+
project.IncludeReferences = includeRefs
331+
}
332+
324333
if !opts.SkipNormalization {
325334
err := Normalize(project)
326335
if err != nil {

loader/loader_test.go

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2545,6 +2545,15 @@ services:
25452545
},
25462546
},
25472547
})
2548+
assert.DeepEqual(t, p.IncludeReferences, map[string][]types.IncludeConfig{
2549+
filepath.Join(workingDir, "filename0.yml"): {
2550+
{
2551+
Path: []string{filepath.Join(workingDir, "testdata", "subdir", "compose-test-extends-imported.yaml")},
2552+
ProjectDirectory: workingDir,
2553+
EnvFile: []string{filepath.Join(workingDir, "testdata", "subdir", "extra.env")},
2554+
},
2555+
},
2556+
})
25482557
assert.NilError(t, err)
25492558
}
25502559

loader/paths.go

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -64,6 +64,25 @@ func ResolveRelativePaths(project *types.Project) error {
6464
project.Volumes[name] = config
6565
}
6666
}
67+
68+
// don't coerce a nil map to an empty map
69+
if project.IncludeReferences != nil {
70+
absIncludes := make(map[string][]types.IncludeConfig, len(project.IncludeReferences))
71+
for filename, config := range project.IncludeReferences {
72+
filename = absPath(project.WorkingDir, filename)
73+
absConfigs := make([]types.IncludeConfig, len(config))
74+
for i, c := range config {
75+
absConfigs[i] = types.IncludeConfig{
76+
Path: resolvePaths(project.WorkingDir, c.Path),
77+
ProjectDirectory: absPath(project.WorkingDir, c.ProjectDirectory),
78+
EnvFile: resolvePaths(project.WorkingDir, c.EnvFile),
79+
}
80+
}
81+
absIncludes[filename] = absConfigs
82+
}
83+
project.IncludeReferences = absIncludes
84+
}
85+
6786
return nil
6887
}
6988

@@ -133,3 +152,14 @@ func isRemoteContext(maybeURL string) bool {
133152
}
134153
return false
135154
}
155+
156+
func resolvePaths(basePath string, in types.StringList) types.StringList {
157+
if in == nil {
158+
return nil
159+
}
160+
ret := make(types.StringList, len(in))
161+
for i := range in {
162+
ret[i] = absPath(basePath, in[i])
163+
}
164+
return ret
165+
}

types/project.go

Lines changed: 16 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -36,16 +36,22 @@ import (
3636

3737
// Project is the result of loading a set of compose files
3838
type Project struct {
39-
Name string `yaml:"name,omitempty" json:"name,omitempty"`
40-
WorkingDir string `yaml:"-" json:"-"`
41-
Services Services `yaml:"services" json:"services"`
42-
Networks Networks `yaml:"networks,omitempty" json:"networks,omitempty"`
43-
Volumes Volumes `yaml:"volumes,omitempty" json:"volumes,omitempty"`
44-
Secrets Secrets `yaml:"secrets,omitempty" json:"secrets,omitempty"`
45-
Configs Configs `yaml:"configs,omitempty" json:"configs,omitempty"`
46-
Extensions Extensions `yaml:"#extensions,inline" json:"-"` // https://github.com/golang/go/issues/6213
47-
ComposeFiles []string `yaml:"-" json:"-"`
48-
Environment Mapping `yaml:"-" json:"-"`
39+
Name string `yaml:"name,omitempty" json:"name,omitempty"`
40+
WorkingDir string `yaml:"-" json:"-"`
41+
Services Services `yaml:"services" json:"services"`
42+
Networks Networks `yaml:"networks,omitempty" json:"networks,omitempty"`
43+
Volumes Volumes `yaml:"volumes,omitempty" json:"volumes,omitempty"`
44+
Secrets Secrets `yaml:"secrets,omitempty" json:"secrets,omitempty"`
45+
Configs Configs `yaml:"configs,omitempty" json:"configs,omitempty"`
46+
Extensions Extensions `yaml:"#extensions,inline" json:"-"` // https://github.com/golang/go/issues/6213
47+
48+
// IncludeReferences is keyed by Compose YAML filename and contains config for
49+
// other Compose YAML files it directly triggered a load of via `include`.
50+
//
51+
// Note: this is
52+
IncludeReferences map[string][]IncludeConfig `yaml:"-" json:"-"`
53+
ComposeFiles []string `yaml:"-" json:"-"`
54+
Environment Mapping `yaml:"-" json:"-"`
4955

5056
// DisabledServices track services which have been disable as profile is not active
5157
DisabledServices Services `yaml:"-" json:"-"`

0 commit comments

Comments
 (0)