Просмотр исходного кода

Merge pull request #11492 from jhrotko/use-listeners-for-file-metadata

Use listener for file metadata
Guillaume Lours 1 год назад
Родитель
Сommit
aa56ab3e71

+ 1 - 1
cmd/compose/build.go

@@ -136,7 +136,7 @@ func buildCommand(p *ProjectOptions, dockerCli command.Cli, backend api.Service)
 }
 
 func runBuild(ctx context.Context, dockerCli command.Cli, backend api.Service, opts buildOptions, services []string) error {
-	project, err := opts.ToProject(ctx, dockerCli, services, cli.WithResolvedPaths(true), cli.WithoutEnvironmentResolution)
+	project, _, err := opts.ToProject(ctx, dockerCli, services, cli.WithResolvedPaths(true), cli.WithoutEnvironmentResolution)
 	if err != nil {
 		return err
 	}

+ 2 - 2
cmd/compose/completion.go

@@ -37,7 +37,7 @@ func noCompletion() validArgsFn {
 func completeServiceNames(dockerCli command.Cli, p *ProjectOptions) validArgsFn {
 	return func(cmd *cobra.Command, args []string, toComplete string) ([]string, cobra.ShellCompDirective) {
 		p.Offline = true
-		project, err := p.ToProject(cmd.Context(), dockerCli, nil)
+		project, _, err := p.ToProject(cmd.Context(), dockerCli, nil)
 		if err != nil {
 			return nil, cobra.ShellCompDirectiveNoFileComp
 		}
@@ -72,7 +72,7 @@ func completeProjectNames(backend api.Service) func(cmd *cobra.Command, args []s
 func completeProfileNames(dockerCli command.Cli, p *ProjectOptions) validArgsFn {
 	return func(cmd *cobra.Command, args []string, toComplete string) ([]string, cobra.ShellCompDirective) {
 		p.Offline = true
-		project, err := p.ToProject(cmd.Context(), dockerCli, nil)
+		project, _, err := p.ToProject(cmd.Context(), dockerCli, nil)
 		if err != nil {
 			return nil, cobra.ShellCompDirectiveNoFileComp
 		}

+ 18 - 9
cmd/compose/compose.go

@@ -36,6 +36,7 @@ import (
 	"github.com/docker/cli/cli-plugins/manager"
 	"github.com/docker/cli/cli/command"
 	"github.com/docker/compose/v2/cmd/formatter"
+	"github.com/docker/compose/v2/internal/tracing"
 	"github.com/docker/compose/v2/pkg/api"
 	"github.com/docker/compose/v2/pkg/compose"
 	ui "github.com/docker/compose/v2/pkg/progress"
@@ -141,11 +142,13 @@ func (o *ProjectOptions) WithServices(dockerCli command.Cli, fn ProjectServicesF
 			cli.WithDiscardEnvFile,
 		}
 
-		project, err := o.ToProject(ctx, dockerCli, args, options...)
+		project, metrics, err := o.ToProject(ctx, dockerCli, args, options...)
 		if err != nil {
 			return err
 		}
 
+		ctx = context.WithValue(ctx, tracing.Metrics{}, metrics)
+
 		return fn(ctx, project, args)
 	})
 }
@@ -166,7 +169,7 @@ func (o *ProjectOptions) projectOrName(ctx context.Context, dockerCli command.Cl
 	name := o.ProjectName
 	var project *types.Project
 	if len(o.ConfigPaths) > 0 || o.ProjectName == "" {
-		p, err := o.ToProject(ctx, dockerCli, services, cli.WithDiscardEnvFile)
+		p, _, err := o.ToProject(ctx, dockerCli, services, cli.WithDiscardEnvFile)
 		if err != nil {
 			envProjectName := os.Getenv(ComposeProjectName)
 			if envProjectName != "" {
@@ -190,14 +193,15 @@ func (o *ProjectOptions) toProjectName(ctx context.Context, dockerCli command.Cl
 		return envProjectName, nil
 	}
 
-	project, err := o.ToProject(ctx, dockerCli, nil)
+	project, _, err := o.ToProject(ctx, dockerCli, nil)
 	if err != nil {
 		return "", err
 	}
 	return project.Name, nil
 }
 
-func (o *ProjectOptions) ToProject(ctx context.Context, dockerCli command.Cli, services []string, po ...cli.ProjectOptionsFn) (*types.Project, error) {
+func (o *ProjectOptions) ToProject(ctx context.Context, dockerCli command.Cli, services []string, po ...cli.ProjectOptionsFn) (*types.Project, tracing.Metrics, error) {
+	var metrics tracing.Metrics
 	if !o.Offline {
 		po = append(po, o.remoteLoaders(dockerCli)...)
 	}
@@ -206,25 +210,30 @@ func (o *ProjectOptions) ToProject(ctx context.Context, dockerCli command.Cli, s
 
 	options, err := o.toProjectOptions(po...)
 	if err != nil {
-		return nil, compose.WrapComposeError(err)
+		return nil, metrics, compose.WrapComposeError(err)
 	}
 
+	options.WithListeners(func(event string, metadata map[string]any) {
+		if event == "extends" {
+			metrics.CountExtends++
+		}
+	})
 	if o.Compatibility || utils.StringToBool(options.Environment[ComposeCompatibility]) {
 		api.Separator = "_"
 	}
 
 	project, err := cli.ProjectFromOptions(options)
 	if err != nil {
-		return nil, compose.WrapComposeError(err)
+		return nil, metrics, compose.WrapComposeError(err)
 	}
 
 	if project.Name == "" {
-		return nil, errors.New("project name can't be empty. Use `--project-name` to set a valid name")
+		return nil, metrics, errors.New("project name can't be empty. Use `--project-name` to set a valid name")
 	}
 
 	project, err = project.WithServicesEnabled(services...)
 	if err != nil {
-		return nil, err
+		return nil, metrics, err
 	}
 
 	for name, s := range project.Services {
@@ -245,7 +254,7 @@ func (o *ProjectOptions) ToProject(ctx context.Context, dockerCli command.Cli, s
 	project = project.WithoutUnnecessaryResources()
 
 	project, err = project.WithSelectedServices(services)
-	return project, err
+	return project, metrics, err
 }
 
 func (o *ProjectOptions) remoteLoaders(dockerCli command.Cli) []cli.ProjectOptionsFn {

+ 8 - 7
cmd/compose/config.go

@@ -29,6 +29,7 @@ import (
 	"github.com/docker/cli/cli/command"
 	"github.com/spf13/cobra"
 
+	"github.com/docker/compose/v2/internal/tracing"
 	"github.com/docker/compose/v2/pkg/api"
 	"github.com/docker/compose/v2/pkg/compose"
 )
@@ -50,7 +51,7 @@ type configOptions struct {
 	noConsistency       bool
 }
 
-func (o *configOptions) ToProject(ctx context.Context, dockerCli command.Cli, services []string, po ...cli.ProjectOptionsFn) (*types.Project, error) {
+func (o *configOptions) ToProject(ctx context.Context, dockerCli command.Cli, services []string, po ...cli.ProjectOptionsFn) (*types.Project, tracing.Metrics, error) {
 	po = append(po,
 		cli.WithInterpolation(!o.noInterpolate),
 		cli.WithResolvedPaths(!o.noResolvePath),
@@ -124,7 +125,7 @@ func configCommand(p *ProjectOptions, dockerCli command.Cli, backend api.Service
 
 func runConfig(ctx context.Context, dockerCli command.Cli, backend api.Service, opts configOptions, services []string) error {
 	var content []byte
-	project, err := opts.ToProject(ctx, dockerCli, services)
+	project, _, err := opts.ToProject(ctx, dockerCli, services)
 	if err != nil {
 		return err
 	}
@@ -154,7 +155,7 @@ func runConfig(ctx context.Context, dockerCli command.Cli, backend api.Service,
 }
 
 func runServices(ctx context.Context, dockerCli command.Cli, opts configOptions) error {
-	project, err := opts.ToProject(ctx, dockerCli, nil, cli.WithoutEnvironmentResolution)
+	project, _, err := opts.ToProject(ctx, dockerCli, nil, cli.WithoutEnvironmentResolution)
 	if err != nil {
 		return err
 	}
@@ -166,7 +167,7 @@ func runServices(ctx context.Context, dockerCli command.Cli, opts configOptions)
 }
 
 func runVolumes(ctx context.Context, dockerCli command.Cli, opts configOptions) error {
-	project, err := opts.ToProject(ctx, dockerCli, nil, cli.WithoutEnvironmentResolution)
+	project, _, err := opts.ToProject(ctx, dockerCli, nil, cli.WithoutEnvironmentResolution)
 	if err != nil {
 		return err
 	}
@@ -181,7 +182,7 @@ func runHash(ctx context.Context, dockerCli command.Cli, opts configOptions) err
 	if opts.hash != "*" {
 		services = append(services, strings.Split(opts.hash, ",")...)
 	}
-	project, err := opts.ToProject(ctx, dockerCli, nil, cli.WithoutEnvironmentResolution)
+	project, _, err := opts.ToProject(ctx, dockerCli, nil, cli.WithoutEnvironmentResolution)
 	if err != nil {
 		return err
 	}
@@ -217,7 +218,7 @@ func runHash(ctx context.Context, dockerCli command.Cli, opts configOptions) err
 
 func runProfiles(ctx context.Context, dockerCli command.Cli, opts configOptions, services []string) error {
 	set := map[string]struct{}{}
-	project, err := opts.ToProject(ctx, dockerCli, services, cli.WithoutEnvironmentResolution)
+	project, _, err := opts.ToProject(ctx, dockerCli, services, cli.WithoutEnvironmentResolution)
 	if err != nil {
 		return err
 	}
@@ -238,7 +239,7 @@ func runProfiles(ctx context.Context, dockerCli command.Cli, opts configOptions,
 }
 
 func runConfigImages(ctx context.Context, dockerCli command.Cli, opts configOptions, services []string) error {
-	project, err := opts.ToProject(ctx, dockerCli, services, cli.WithoutEnvironmentResolution)
+	project, _, err := opts.ToProject(ctx, dockerCli, services, cli.WithoutEnvironmentResolution)
 	if err != nil {
 		return err
 	}

+ 1 - 1
cmd/compose/publish.go

@@ -50,7 +50,7 @@ func publishCommand(p *ProjectOptions, dockerCli command.Cli, backend api.Servic
 }
 
 func runPublish(ctx context.Context, dockerCli command.Cli, backend api.Service, opts publishOptions, repository string) error {
-	project, err := opts.ToProject(ctx, dockerCli, nil)
+	project, _, err := opts.ToProject(ctx, dockerCli, nil)
 	if err != nil {
 		return err
 	}

+ 1 - 1
cmd/compose/pull.go

@@ -94,7 +94,7 @@ func (opts pullOptions) apply(project *types.Project, services []string) (*types
 }
 
 func runPull(ctx context.Context, dockerCli command.Cli, backend api.Service, opts pullOptions, services []string) error {
-	project, err := opts.ToProject(ctx, dockerCli, services)
+	project, _, err := opts.ToProject(ctx, dockerCli, services)
 	if err != nil {
 		return err
 	}

+ 1 - 1
cmd/compose/push.go

@@ -54,7 +54,7 @@ func pushCommand(p *ProjectOptions, dockerCli command.Cli, backend api.Service)
 }
 
 func runPush(ctx context.Context, dockerCli command.Cli, backend api.Service, opts pushOptions, services []string) error {
-	project, err := opts.ToProject(ctx, dockerCli, services)
+	project, _, err := opts.ToProject(ctx, dockerCli, services)
 	if err != nil {
 		return err
 	}

+ 1 - 1
cmd/compose/run.go

@@ -156,7 +156,7 @@ func runCommand(p *ProjectOptions, dockerCli command.Cli, backend api.Service) *
 			return nil
 		}),
 		RunE: Adapt(func(ctx context.Context, args []string) error {
-			project, err := p.ToProject(ctx, dockerCli, []string{options.Service}, cgo.WithResolvedPaths(true), cgo.WithDiscardEnvFile)
+			project, _, err := p.ToProject(ctx, dockerCli, []string{options.Service}, cgo.WithResolvedPaths(true), cgo.WithDiscardEnvFile)
 			if err != nil {
 				return err
 			}

+ 1 - 1
cmd/compose/scale.go

@@ -61,7 +61,7 @@ func scaleCommand(p *ProjectOptions, dockerCli command.Cli, backend api.Service)
 
 func runScale(ctx context.Context, dockerCli command.Cli, backend api.Service, opts scaleOptions, serviceReplicaTuples map[string]int) error {
 	services := maps.Keys(serviceReplicaTuples)
-	project, err := opts.ToProject(ctx, dockerCli, services)
+	project, _, err := opts.ToProject(ctx, dockerCli, services)
 	if err != nil {
 		return err
 	}

+ 1 - 1
cmd/compose/viz.go

@@ -65,7 +65,7 @@ func vizCommand(p *ProjectOptions, dockerCli command.Cli, backend api.Service) *
 
 func runViz(ctx context.Context, dockerCli command.Cli, backend api.Service, opts *vizOptions) error {
 	_, _ = fmt.Fprintln(os.Stderr, "viz command is EXPERIMENTAL")
-	project, err := opts.ToProject(ctx, dockerCli, nil)
+	project, _, err := opts.ToProject(ctx, dockerCli, nil)
 	if err != nil {
 		return err
 	}

+ 1 - 1
cmd/compose/watch.go

@@ -63,7 +63,7 @@ func watchCommand(p *ProjectOptions, dockerCli command.Cli, backend api.Service)
 }
 
 func runWatch(ctx context.Context, dockerCli command.Cli, backend api.Service, watchOpts watchOptions, buildOpts buildOptions, services []string) error {
-	project, err := watchOpts.ToProject(ctx, dockerCli, nil)
+	project, _, err := watchOpts.ToProject(ctx, dockerCli, nil)
 	if err != nil {
 		return err
 	}

+ 1 - 1
go.mod

@@ -6,7 +6,7 @@ require (
 	github.com/AlecAivazis/survey/v2 v2.3.7
 	github.com/Microsoft/go-winio v0.6.1
 	github.com/buger/goterm v1.0.4
-	github.com/compose-spec/compose-go/v2 v2.0.0-rc.5
+	github.com/compose-spec/compose-go/v2 v2.0.0-rc.6
 	github.com/containerd/console v1.0.3
 	github.com/containerd/containerd v1.7.12
 	github.com/davecgh/go-spew v1.1.1

+ 2 - 2
go.sum

@@ -86,8 +86,8 @@ github.com/cncf/xds/go v0.0.0-20230607035331-e9ce68804cb4 h1:/inchEIKaYC1Akx+H+g
 github.com/cncf/xds/go v0.0.0-20230607035331-e9ce68804cb4/go.mod h1:eXthEFrGJvWHgFFCl3hGmgk+/aYT6PnTQLykKQRLhEs=
 github.com/codahale/rfc6979 v0.0.0-20141003034818-6a90f24967eb h1:EDmT6Q9Zs+SbUoc7Ik9EfrFqcylYqgPZ9ANSbTAntnE=
 github.com/codahale/rfc6979 v0.0.0-20141003034818-6a90f24967eb/go.mod h1:ZjrT6AXHbDs86ZSdt/osfBi5qfexBrKUdONk989Wnk4=
-github.com/compose-spec/compose-go/v2 v2.0.0-rc.5 h1:YoGsuVzxve1m5SdCfZqI8wJoMVZWu7SelHoqiCqb+iQ=
-github.com/compose-spec/compose-go/v2 v2.0.0-rc.5/go.mod h1:bEPizBkIojlQ20pi2vNluBa58tevvj0Y18oUSHPyfdc=
+github.com/compose-spec/compose-go/v2 v2.0.0-rc.6 h1:sv9W3U0IEYqgGqTbSDpU2c8cttWQmlbJ0D6jdt//Dv8=
+github.com/compose-spec/compose-go/v2 v2.0.0-rc.6/go.mod h1:bEPizBkIojlQ20pi2vNluBa58tevvj0Y18oUSHPyfdc=
 github.com/containerd/cgroups v1.1.0 h1:v8rEWFl6EoqHB+swVNjVoCJE8o3jX7e8nqBGPLaDFBM=
 github.com/containerd/cgroups v1.1.0/go.mod h1:6ppBcbh/NOOUU+dMKrykgaBnK9lCIBxHqJDGwsa1mIw=
 github.com/containerd/console v1.0.3 h1:lIr7SlA5PxZyMV30bDW0MGbiOPXwc63yRuCP0ARubLw=

+ 11 - 4
internal/tracing/attributes.go

@@ -17,6 +17,7 @@
 package tracing
 
 import (
+	"context"
 	"crypto/sha256"
 	"encoding/json"
 	"fmt"
@@ -34,6 +35,9 @@ import (
 // SpanOptions is a small helper type to make it easy to share the options helpers between
 // downstream functions that accept slices of trace.SpanStartOption and trace.EventOption.
 type SpanOptions []trace.SpanStartEventOption
+type Metrics struct {
+	CountExtends int
+}
 
 func (s SpanOptions) SpanStartOptions() []trace.SpanStartOption {
 	out := make([]trace.SpanStartOption, len(s))
@@ -56,18 +60,15 @@ func (s SpanOptions) EventOptions() []trace.EventOption {
 // For convenience, it's returned as a SpanOptions object to allow it to be
 // passed directly to the wrapping helper methods in this package such as
 // SpanWrapFunc.
-func ProjectOptions(proj *types.Project) SpanOptions {
+func ProjectOptions(ctx context.Context, proj *types.Project) SpanOptions {
 	if proj == nil {
 		return nil
 	}
-
 	capabilities, gpu, tpu := proj.ServicesWithCapabilities()
 	attrs := []attribute.KeyValue{
 		attribute.String("project.name", proj.Name),
 		attribute.String("project.dir", proj.WorkingDir),
 		attribute.StringSlice("project.compose_files", proj.ComposeFiles),
-		attribute.StringSlice("project.services.active", proj.ServiceNames()),
-		attribute.StringSlice("project.services.disabled", proj.DisabledServiceNames()),
 		attribute.StringSlice("project.profiles", proj.Profiles),
 		attribute.StringSlice("project.volumes", proj.VolumeNames()),
 		attribute.StringSlice("project.networks", proj.NetworkNames()),
@@ -75,12 +76,18 @@ func ProjectOptions(proj *types.Project) SpanOptions {
 		attribute.StringSlice("project.configs", proj.ConfigNames()),
 		attribute.StringSlice("project.extensions", keys(proj.Extensions)),
 		attribute.StringSlice("project.includes", flattenIncludeReferences(proj.IncludeReferences)),
+		attribute.StringSlice("project.services.active", proj.ServiceNames()),
+		attribute.StringSlice("project.services.disabled", proj.DisabledServiceNames()),
 		attribute.StringSlice("project.services.build", proj.ServicesWithBuild()),
 		attribute.StringSlice("project.services.depends_on", proj.ServicesWithDependsOn()),
 		attribute.StringSlice("project.services.capabilities", capabilities),
 		attribute.StringSlice("project.services.capabilities.gpu", gpu),
 		attribute.StringSlice("project.services.capabilities.tpu", tpu),
 	}
+	if metrics, ok := ctx.Value(Metrics{}).(Metrics); ok {
+		attrs = append(attrs, attribute.Int("project.services.extends", metrics.CountExtends))
+	}
+
 	if projHash, ok := projectHash(proj); ok {
 		attrs = append(attrs, attribute.String("project.hash", projHash))
 	}

+ 2 - 2
pkg/compose/build.go

@@ -221,7 +221,7 @@ func (s *composeService) ensureImagesExists(ctx context.Context, project *types.
 		return err
 	}
 
-	err = tracing.SpanWrapFunc("project/pull", tracing.ProjectOptions(project),
+	err = tracing.SpanWrapFunc("project/pull", tracing.ProjectOptions(ctx, project),
 		func(ctx context.Context) error {
 			return s.pullRequiredImages(ctx, project, images, quietPull)
 		},
@@ -231,7 +231,7 @@ func (s *composeService) ensureImagesExists(ctx context.Context, project *types.
 	}
 
 	if buildOpts != nil {
-		err = tracing.SpanWrapFunc("project/build", tracing.ProjectOptions(project),
+		err = tracing.SpanWrapFunc("project/build", tracing.ProjectOptions(ctx, project),
 			func(ctx context.Context) error {
 				builtImages, err := s.build(ctx, project, *buildOpts, images)
 				if err != nil {

+ 1 - 1
pkg/compose/scale.go

@@ -25,7 +25,7 @@ import (
 )
 
 func (s *composeService) Scale(ctx context.Context, project *types.Project, options api.ScaleOptions) error {
-	return progress.Run(ctx, tracing.SpanWrapFunc("project/scale", tracing.ProjectOptions(project), func(ctx context.Context) error {
+	return progress.Run(ctx, tracing.SpanWrapFunc("project/scale", tracing.ProjectOptions(ctx, project), func(ctx context.Context) error {
 		err := s.create(ctx, project, api.CreateOptions{Services: options.Services})
 		if err != nil {
 			return err

+ 1 - 1
pkg/compose/up.go

@@ -32,7 +32,7 @@ import (
 )
 
 func (s *composeService) Up(ctx context.Context, project *types.Project, options api.UpOptions) error { //nolint:gocyclo
-	err := progress.Run(ctx, tracing.SpanWrapFunc("project/up", tracing.ProjectOptions(project), func(ctx context.Context) error {
+	err := progress.Run(ctx, tracing.SpanWrapFunc("project/up", tracing.ProjectOptions(ctx, project), func(ctx context.Context) error {
 		w := progress.ContextWriter(ctx)
 		w.HasMore(options.Start.Attach == nil)
 		err := s.create(ctx, project, options.Create)