Skip to content
Merged
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
5 changes: 2 additions & 3 deletions loader/extends.go
Original file line number Diff line number Diff line change
Expand Up @@ -81,13 +81,12 @@ func applyServiceExtends(ctx context.Context, name string, services map[string]a

var (
base any
processor PostProcessor
processor PostProcessor = NoopPostProcessor{}
)

if file != nil {
refFilename := file.(string)
services, processor, err = getExtendsBaseFromFile(ctx, name, ref, filename, refFilename, opts, tracker)
post = append(post, processor)
if err != nil {
return nil, err
}
Expand All @@ -105,7 +104,7 @@ func applyServiceExtends(ctx context.Context, name string, services map[string]a
}

// recursively apply `extends`
base, err = applyServiceExtends(ctx, ref, services, opts, tracker, post...)
base, err = applyServiceExtends(ctx, ref, services, opts, tracker, processor)
if err != nil {
return nil, err
}
Expand Down
6 changes: 4 additions & 2 deletions loader/loader.go
Original file line number Diff line number Diff line change
Expand Up @@ -260,12 +260,14 @@ func WithProfiles(profiles []string) func(*Options) {
// PostProcessor is used to tweak compose model based on metadata extracted during yaml Unmarshal phase
// that hardly can be implemented using go-yaml and mapstructure
type PostProcessor interface {
yaml.Unmarshaler

// Apply changes to compose model based on recorder metadata
Apply(interface{}) error
}

type NoopPostProcessor struct{}

func (NoopPostProcessor) Apply(interface{}) error { return nil }

// LoadConfigFiles ingests config files with ResourceLoader and returns config details with paths to local copies
func LoadConfigFiles(ctx context.Context, configFiles []string, workingDir string, options ...func(*Options)) (*types.ConfigDetails, error) {
if len(configFiles) < 1 {
Expand Down
62 changes: 62 additions & 0 deletions loader/loader_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -3917,3 +3917,65 @@ services:
assert.Equal(t, build.Provenance, "mode=max")
assert.Equal(t, build.SBOM, "true")
}

func TestOverrideMiddle(t *testing.T) {
pwd := t.TempDir()
base := filepath.Join(pwd, "base.yaml")
err := os.WriteFile(base, []byte(`
services:
base:
volumes:
- /foo:/foo
`), 0o700)
assert.NilError(t, err)

override := filepath.Join(pwd, "override.yaml")
err = os.WriteFile(override, []byte(`
services:
override:
extends:
file: ./base.yaml
service: base
volumes: !override
- /bar:/bar
`), 0o700)
assert.NilError(t, err)

compose := filepath.Join(pwd, "compose.yaml")
err = os.WriteFile(compose, []byte(`
name: test
services:
test:
image: test
extends:
file: ./override.yaml
service: override
volumes:
- /zot:/zot
`), 0o700)
assert.NilError(t, err)

project, err := LoadWithContext(context.TODO(), types.ConfigDetails{
WorkingDir: pwd,
ConfigFiles: []types.ConfigFile{
{Filename: compose},
},
})
assert.NilError(t, err)
test := project.Services["test"]
assert.Equal(t, len(test.Volumes), 2)
assert.DeepEqual(t, test.Volumes, []types.ServiceVolumeConfig{
{
Type: "bind",
Source: "/bar",
Target: "/bar",
Bind: &types.ServiceVolumeBind{CreateHostPath: true},
},
{
Type: "bind",
Source: "/zot",
Target: "/zot",
Bind: &types.ServiceVolumeBind{CreateHostPath: true},
},
})
}