Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

detect include cycle using compose-file stored in context #477

Draft
wants to merge 1 commit into
base: main
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion loader/extends.go
Original file line number Diff line number Diff line change
Expand Up @@ -71,7 +71,7 @@ func ApplyExtends(ctx context.Context, dict map[string]any, workingdir string, o
ConfigFiles: []types.ConfigFile{
{Filename: local},
},
}, extendsOpts, ct, nil)
}, extendsOpts, ct)
if err != nil {
return err
}
Expand Down
23 changes: 16 additions & 7 deletions loader/include.go
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@ import (
"reflect"
"strings"

"github.com/compose-spec/compose-go/v2/consts"
"github.com/compose-spec/compose-go/v2/dotenv"
interp "github.com/compose-spec/compose-go/v2/interpolation"
"github.com/compose-spec/compose-go/v2/types"
Expand All @@ -39,7 +40,7 @@ func loadIncludeConfig(source any) ([]types.IncludeConfig, error) {
return requires, err
}

func ApplyInclude(ctx context.Context, configDetails types.ConfigDetails, model map[string]any, options *Options, included []string) error {
func ApplyInclude(ctx context.Context, configDetails types.ConfigDetails, model map[string]any, options *Options) error {
includeConfig, err := loadIncludeConfig(model["include"])
if err != nil {
return err
Expand All @@ -60,11 +61,9 @@ func ApplyInclude(ctx context.Context, configDetails types.ConfigDetails, model
}

mainFile := r.Path[0]
for _, f := range included {
if f == mainFile {
included = append(included, mainFile)
return errors.Errorf("include cycle detected:\n%s\n include %s", included[0], strings.Join(included[1:], "\n include "))
}
err := checkIncludeCycle(ctx, mainFile)
if err != nil {
return err
}

if r.ProjectDirectory == "" {
Expand All @@ -91,7 +90,7 @@ func ApplyInclude(ctx context.Context, configDetails types.ConfigDetails, model
LookupValue: config.LookupEnv,
TypeCastMapping: options.Interpolate.TypeCastMapping,
}
imported, err := loadYamlModel(ctx, config, loadOptions, &cycleTracker{}, included)
imported, err := loadYamlModel(ctx, config, loadOptions, &cycleTracker{})
if err != nil {
return err
}
Expand All @@ -104,6 +103,16 @@ func ApplyInclude(ctx context.Context, configDetails types.ConfigDetails, model
return nil
}

func checkIncludeCycle(ctx context.Context, mainFile string) error {
files, _ := ctx.Value(consts.ComposeFileKey{}).([]string)
for _, f := range files {
if f == mainFile {
return errors.Errorf("include cycle detected:\n%s\n include %s", strings.Join(files, "\n include "), mainFile)
}
}
return nil
}

// importResources import into model all resources defined by imported, and report error on conflict
func importResources(source map[string]any, target map[string]any) error {
if err := importResource(source, target, "services"); err != nil {
Expand Down
14 changes: 8 additions & 6 deletions loader/loader.go
Original file line number Diff line number Diff line change
Expand Up @@ -274,13 +274,16 @@ func LoadWithContext(ctx context.Context, configDetails types.ConfigDetails, opt
return load(ctx, configDetails, opts, nil)
}

func loadYamlModel(ctx context.Context, config types.ConfigDetails, opts *Options, ct *cycleTracker, included []string) (map[string]interface{}, error) {
func loadYamlModel(ctx context.Context, config types.ConfigDetails, opts *Options, ct *cycleTracker) (map[string]interface{}, error) {
var (
dict = map[string]interface{}{}
err error
)

f, _ := ctx.Value(consts.ComposeFileKey{}).([]string)
ctx = context.WithValue(ctx, consts.ComposeFileKey{}, append(f, config.ConfigFiles[0].Filename))

for _, file := range config.ConfigFiles {
fctx := context.WithValue(ctx, consts.ComposeFileKey{}, file.Filename)
if len(file.Content) == 0 && file.Config == nil {
content, err := os.ReadFile(file.Filename)
if err != nil {
Expand Down Expand Up @@ -315,7 +318,7 @@ func loadYamlModel(ctx context.Context, config types.ConfigDetails, opts *Option
}

if !opts.SkipExtends {
err = ApplyExtends(fctx, cfg, config.WorkingDir, opts, ct, processors...)
err = ApplyExtends(ctx, cfg, config.WorkingDir, opts, ct, processors...)
if err != nil {
return err
}
Expand Down Expand Up @@ -364,8 +367,7 @@ func loadYamlModel(ctx context.Context, config types.ConfigDetails, opts *Option
}

if !opts.SkipInclude {
included = append(included, config.ConfigFiles[0].Filename)
err = ApplyInclude(ctx, config, dict, opts, included)
err = ApplyInclude(ctx, config, dict, opts)
if err != nil {
return nil, err
}
Expand Down Expand Up @@ -400,7 +402,7 @@ func load(ctx context.Context, configDetails types.ConfigDetails, opts *Options,

includeRefs := make(map[string][]types.IncludeConfig)

dict, err := loadYamlModel(ctx, configDetails, opts, &cycleTracker{}, nil)
dict, err := loadYamlModel(ctx, configDetails, opts, &cycleTracker{})
if err != nil {
return nil, err
}
Expand Down
2 changes: 1 addition & 1 deletion loader/loader_yaml_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -43,7 +43,7 @@ services:
image: bar
command: echo world
init: false
`)}}}, &Options{}, &cycleTracker{}, nil)
`)}}}, &Options{}, &cycleTracker{})
assert.NilError(t, err)
assert.DeepEqual(t, model, map[string]interface{}{
"services": map[string]interface{}{
Expand Down
Loading