Skip to content

Commit 26a5dd3

Browse files
jhrotkondeloof
authored andcommitted
Add listeners to trace file metadata
Signed-off-by: jhrotko <[email protected]>
1 parent 2b6b594 commit 26a5dd3

File tree

4 files changed

+136
-1
lines changed

4 files changed

+136
-1
lines changed

cli/options.go

Lines changed: 19 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -80,6 +80,10 @@ type ProjectOptions struct {
8080
EnvFiles []string
8181

8282
loadOptions []func(*loader.Options)
83+
84+
// Callbacks to retrieve metadata information during parse defined before
85+
// creating the project
86+
Listeners []loader.Listener
8387
}
8488

8589
type ProjectOptionsFn func(*ProjectOptions) error
@@ -89,6 +93,7 @@ func NewProjectOptions(configs []string, opts ...ProjectOptionsFn) (*ProjectOpti
8993
options := &ProjectOptions{
9094
ConfigPaths: configs,
9195
Environment: map[string]string{},
96+
Listeners: []loader.Listener{},
9297
}
9398
for _, o := range opts {
9499
err := o(options)
@@ -365,6 +370,11 @@ func WithExtension(name string, typ any) ProjectOptionsFn {
365370
}
366371
}
367372

373+
// Append listener to event
374+
func (o *ProjectOptions) WithListeners(listeners ...loader.Listener) {
375+
o.Listeners = append(o.Listeners, listeners...)
376+
}
377+
368378
// WithoutEnvironmentResolution disable environment resolution
369379
func WithoutEnvironmentResolution(o *ProjectOptions) error {
370380
o.loadOptions = append(o.loadOptions, func(options *loader.Options) {
@@ -437,7 +447,8 @@ func ProjectFromOptions(options *ProjectOptions) (*types.Project, error) {
437447

438448
options.loadOptions = append(options.loadOptions,
439449
withNamePrecedenceLoad(absWorkingDir, options),
440-
withConvertWindowsPaths(options))
450+
withConvertWindowsPaths(options),
451+
withListener(options))
441452

442453
ctx := options.ctx
443454
if ctx == nil {
@@ -480,6 +491,13 @@ func withConvertWindowsPaths(options *ProjectOptions) func(*loader.Options) {
480491
}
481492
}
482493

494+
// save listeners from ProjectOptions (compose) to loader.Options
495+
func withListener(options *ProjectOptions) func(*loader.Options) {
496+
return func(opts *loader.Options) {
497+
opts.Listeners = options.Listeners
498+
}
499+
}
500+
483501
// getConfigPathsFromOptions retrieves the config files for project based on project options
484502
func getConfigPathsFromOptions(options *ProjectOptions) ([]string, error) {
485503
if len(options.ConfigPaths) != 0 {

loader/extends.go

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -70,8 +70,10 @@ func applyServiceExtends(ctx context.Context, name string, services map[string]a
7070
case map[string]any:
7171
ref = v["service"].(string)
7272
file = v["file"]
73+
opts.ProcessEvent("extends", v)
7374
case string:
7475
ref = v
76+
opts.ProcessEvent("extends", map[string]any{"service": ref})
7577
}
7678

7779
var base any
@@ -121,6 +123,7 @@ func applyServiceExtends(ctx context.Context, name string, services map[string]a
121123
return nil, err
122124
}
123125
delete(merged, "extends")
126+
services[name] = merged
124127
return merged, nil
125128
}
126129

loader/extends_test.go

Lines changed: 102 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -52,6 +52,7 @@ services:
5252
abs, err := filepath.Abs(".")
5353
assert.NilError(t, err)
5454

55+
extendsCount := 0
5556
p, err := LoadWithContext(context.Background(), types.ConfigDetails{
5657
ConfigFiles: []types.ConfigFile{
5758
{
@@ -60,11 +61,21 @@ services:
6061
},
6162
},
6263
WorkingDir: abs,
64+
}, func(options *Options) {
65+
options.ResolvePaths = false
66+
options.Listeners = []Listener{
67+
func(event string, metadata map[string]any) {
68+
if event == "extends" {
69+
extendsCount++
70+
}
71+
},
72+
}
6373
})
6474
assert.NilError(t, err)
6575
assert.DeepEqual(t, p.Services["test1"].Hostname, "test1")
6676
assert.Equal(t, p.Services["test2"].Hostname, "test2")
6777
assert.Equal(t, p.Services["test3"].Hostname, "test3")
78+
assert.Equal(t, extendsCount, 4)
6879
}
6980

7081
func TestExtendsPort(t *testing.T) {
@@ -262,6 +273,7 @@ services:
262273

263274
assert.NilError(t, os.WriteFile(filepath.Join(tmpdir, "compose.yaml"), []byte(rootYAML), 0o600))
264275

276+
extendsCount := 0
265277
actual, err := Load(types.ConfigDetails{
266278
WorkingDir: tmpdir,
267279
ConfigFiles: []types.ConfigFile{{
@@ -272,6 +284,13 @@ services:
272284
options.SkipNormalization = true
273285
options.SkipConsistencyCheck = true
274286
options.SetProjectName("project", true)
287+
options.Listeners = []Listener{
288+
func(event string, metadata map[string]any) {
289+
if event == "extends" {
290+
extendsCount++
291+
}
292+
},
293+
}
275294
})
276295
assert.NilError(t, err)
277296
assert.Assert(t, is.Len(actual.Services, 2))
@@ -283,6 +302,8 @@ services:
283302
svcB, err := actual.GetService("out-service")
284303
assert.NilError(t, err)
285304
assert.Equal(t, svcB.Build.Context, tmpdir)
305+
306+
assert.Equal(t, extendsCount, 3)
286307
}
287308

288309
func TestRejectExtendsWithServiceRef(t *testing.T) {
@@ -358,3 +379,84 @@ services:
358379
})
359380
}
360381
}
382+
383+
func TestLoadExtendsListener(t *testing.T) {
384+
yaml := `
385+
name: listener-extends
386+
services:
387+
foo:
388+
image: busybox
389+
extends: bar
390+
bar:
391+
image: alpine
392+
command: echo
393+
extends: wee
394+
wee:
395+
extends: last
396+
command: echo
397+
last:
398+
image: python`
399+
extendsCount := 0
400+
_, err := Load(buildConfigDetails(yaml, nil), func(options *Options) {
401+
options.SkipConsistencyCheck = true
402+
options.SkipNormalization = true
403+
options.ResolvePaths = true
404+
options.Listeners = []Listener{
405+
func(event string, metadata map[string]any) {
406+
if event == "extends" {
407+
extendsCount++
408+
}
409+
},
410+
}
411+
})
412+
413+
assert.NilError(t, err)
414+
assert.Equal(t, extendsCount, 3)
415+
}
416+
417+
func TestLoadExtendsListenerMultipleFiles(t *testing.T) {
418+
tmpdir := t.TempDir()
419+
subDir := filepath.Join(tmpdir, "sub")
420+
assert.NilError(t, os.Mkdir(subDir, 0o700))
421+
subYAML := `
422+
services:
423+
b:
424+
extends: c
425+
build:
426+
target: fake
427+
c:
428+
command: echo
429+
`
430+
assert.NilError(t, os.WriteFile(filepath.Join(tmpdir, "sub", "compose.yaml"), []byte(subYAML), 0o600))
431+
432+
rootYAML := `
433+
services:
434+
a:
435+
extends:
436+
file: ./sub/compose.yaml
437+
service: b
438+
`
439+
assert.NilError(t, os.WriteFile(filepath.Join(tmpdir, "compose.yaml"), []byte(rootYAML), 0o600))
440+
441+
extendsCount := 0
442+
_, err := Load(types.ConfigDetails{
443+
WorkingDir: tmpdir,
444+
ConfigFiles: []types.ConfigFile{{
445+
Filename: filepath.Join(tmpdir, "compose.yaml"),
446+
}},
447+
Environment: nil,
448+
}, func(options *Options) {
449+
options.SkipNormalization = true
450+
options.SkipConsistencyCheck = true
451+
options.SetProjectName("project", true)
452+
options.Listeners = []Listener{
453+
func(event string, metadata map[string]any) {
454+
if event == "extends" {
455+
extendsCount++
456+
}
457+
},
458+
}
459+
})
460+
assert.NilError(t, err)
461+
assert.Equal(t, extendsCount, 2)
462+
}

loader/loader.go

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -80,6 +80,17 @@ type Options struct {
8080
ResourceLoaders []ResourceLoader
8181
// KnownExtensions manages x-* attribute we know and the corresponding go structs
8282
KnownExtensions map[string]any
83+
// Metada for telemetry
84+
Listeners []Listener
85+
}
86+
87+
type Listener = func(event string, metadata map[string]any)
88+
89+
// Invoke all listeners for an event
90+
func (o *Options) ProcessEvent(event string, metadata map[string]any) {
91+
for _, l := range o.Listeners {
92+
l(event, metadata)
93+
}
8394
}
8495

8596
// ResourceLoader is a plugable remote resource resolver
@@ -153,6 +164,7 @@ func (o *Options) clone() *Options {
153164
Profiles: o.Profiles,
154165
ResourceLoaders: o.ResourceLoaders,
155166
KnownExtensions: o.KnownExtensions,
167+
Listeners: o.Listeners,
156168
}
157169
}
158170

0 commit comments

Comments
 (0)