From e0da3c9c93c724ab7531f88b289169678aabe798 Mon Sep 17 00:00:00 2001 From: Yuri Shkuro Date: Sun, 15 Oct 2023 11:31:22 -0400 Subject: [PATCH] feat(jaeger-v2): Create default config for all-in-one (#4842) ## Which problem is this PR solving? In Jaeger V1 the all-in-one binary can be run without any arguments / configuration, but since `jaeger-v2` is built on OTel Collector it requires a config file. This is unnecessary friction for local / development use of all-in-one. ## Description of the changes - Allows all-in-one with memory storage to be run without any additional config file - Hardcodes a configuration in the code, relying on default configuration from each component. ## How was this change tested? - Ran locally ``` $ go run -tags=ui ./cmd/jaeger-v2 ``` --------- Signed-off-by: Yuri Shkuro --- cmd/jaeger-v2/internal/all-in-one/.nocover | 1 + cmd/jaeger-v2/internal/all-in-one/config.go | 133 ++++++++++++++++++ cmd/jaeger-v2/internal/command.go | 47 +++++-- .../exporters/storageexporter/factory.go | 9 +- .../internal/extension/jaegerquery/factory.go | 5 + .../extension/jaegerstorage/factory.go | 22 ++- pkg/version/build.go | 2 +- pkg/version/command.go | 2 +- 8 files changed, 206 insertions(+), 15 deletions(-) create mode 100644 cmd/jaeger-v2/internal/all-in-one/.nocover create mode 100644 cmd/jaeger-v2/internal/all-in-one/config.go diff --git a/cmd/jaeger-v2/internal/all-in-one/.nocover b/cmd/jaeger-v2/internal/all-in-one/.nocover new file mode 100644 index 00000000000..9d6cf4b7fb6 --- /dev/null +++ b/cmd/jaeger-v2/internal/all-in-one/.nocover @@ -0,0 +1 @@ +FIXME diff --git a/cmd/jaeger-v2/internal/all-in-one/config.go b/cmd/jaeger-v2/internal/all-in-one/config.go new file mode 100644 index 00000000000..e9e263e0d77 --- /dev/null +++ b/cmd/jaeger-v2/internal/all-in-one/config.go @@ -0,0 +1,133 @@ +// Copyright (c) 2023 The Jaeger Authors. +// SPDX-License-Identifier: Apache-2.0 + +package allinone + +import ( + "context" + "fmt" + "time" + + "go.opentelemetry.io/collector/component" + "go.opentelemetry.io/collector/config/configtelemetry" + "go.opentelemetry.io/collector/otelcol" + "go.opentelemetry.io/collector/service" + "go.opentelemetry.io/collector/service/extensions" + "go.opentelemetry.io/collector/service/pipelines" + "go.opentelemetry.io/collector/service/telemetry" + "go.uber.org/zap/zapcore" + + "github.com/jaegertracing/jaeger/cmd/jaeger-v2/internal/exporters/storageexporter" + "github.com/jaegertracing/jaeger/cmd/jaeger-v2/internal/extension/jaegerquery" + "github.com/jaegertracing/jaeger/cmd/jaeger-v2/internal/extension/jaegerstorage" +) + +type configProvider struct { + watcher chan error +} + +var _ otelcol.ConfigProvider = (*configProvider)(nil) + +// NewConfigProvider creates a new ConfigProvider. +func NewConfigProvider() *configProvider { + return &configProvider{ + watcher: make(chan error, 1), + } +} + +func (cp *configProvider) Get(ctx context.Context, factories otelcol.Factories) (*otelcol.Config, error) { + cfg := &otelcol.Config{ + Service: cp.makeServiceConfig(), + Extensions: make(map[component.ID]component.Config), + Receivers: make(map[component.ID]component.Config), + Processors: make(map[component.ID]component.Config), + Exporters: make(map[component.ID]component.Config), + } + defaultConfigs("extension", cfg.Service.Extensions, cfg.Extensions, factories.Extensions) + for _, pipeCfg := range cfg.Service.Pipelines { + defaultConfigs("receiver", pipeCfg.Receivers, cfg.Receivers, factories.Receivers) + defaultConfigs("processor", pipeCfg.Processors, cfg.Processors, factories.Processors) + defaultConfigs("exporter", pipeCfg.Exporters, cfg.Exporters, factories.Exporters) + } + return cfg, nil +} + +func defaultConfigs[TFactory component.Factory]( + componentType string, + comps []component.ID, + outCfg map[component.ID]component.Config, + factories map[component.Type]TFactory, +) error { + for _, compID := range comps { + f, ok := factories[compID.Type()] + if !ok { + return fmt.Errorf("no factory registered for %s %v", componentType, compID) + } + cfg := f.CreateDefaultConfig() + outCfg[compID] = cfg + } + return nil +} + +// makeServiceConfig creates default config that contains +// all standard all-in-one extensions and pipelines. +func (cp *configProvider) makeServiceConfig() service.Config { + return service.Config{ + Extensions: extensions.Config([]component.ID{ + jaegerstorage.ID, + jaegerquery.ID, + }), + Pipelines: pipelines.Config(map[component.ID]*pipelines.PipelineConfig{ + component.NewID("traces"): { + Receivers: []component.ID{ + component.NewID("otlp"), + component.NewID("jaeger"), + component.NewID("zipkin"), + }, + Processors: []component.ID{ + component.NewID("batch"), + }, + Exporters: []component.ID{ + storageexporter.ID, + }, + }, + }), + // OTel Collector currently (v0.87) hardcodes telemetry settings, this is a copy. + // https://github.com/open-telemetry/opentelemetry-collector/blob/35512c466577036b0cc306673d2d4ad039c77f1c/otelcol/unmarshaler.go#L43 + Telemetry: telemetry.Config{ + Logs: telemetry.LogsConfig{ + Level: zapcore.InfoLevel, + Development: false, + Encoding: "console", + Sampling: &telemetry.LogsSamplingConfig{ + Enabled: true, + Tick: 10 * time.Second, + Initial: 10, + Thereafter: 100, + }, + OutputPaths: []string{"stderr"}, + ErrorOutputPaths: []string{"stderr"}, + DisableCaller: false, + DisableStacktrace: false, + InitialFields: map[string]any(nil), + }, + Metrics: telemetry.MetricsConfig{ + Level: configtelemetry.LevelNone, + // Address: ":8888", + }, + // TODO initialize tracer + }, + } +} + +// Watch implements otelcol.ConfigProvider. +// The returned channel is never written to, as there is no configuration to watch. +func (cp *configProvider) Watch() <-chan error { + return cp.watcher +} + +// Shutdown implements otelcol.ConfigProvider. +func (cp *configProvider) Shutdown(ctx context.Context) error { + close(cp.watcher) + return nil +} diff --git a/cmd/jaeger-v2/internal/command.go b/cmd/jaeger-v2/internal/command.go index 577e45a49cb..f400e614299 100644 --- a/cmd/jaeger-v2/internal/command.go +++ b/cmd/jaeger-v2/internal/command.go @@ -5,11 +5,13 @@ package internal import ( "log" + "strings" "github.com/spf13/cobra" "go.opentelemetry.io/collector/component" "go.opentelemetry.io/collector/otelcol" + allinone "github.com/jaegertracing/jaeger/cmd/jaeger-v2/internal/all-in-one" "github.com/jaegertracing/jaeger/pkg/version" ) @@ -21,20 +23,47 @@ func Command() *cobra.Command { log.Fatalf("failed to build components: %v", err) } - versionInfo := version.Get() - info := component.BuildInfo{ Command: "jaeger-v2", Description: description, - Version: versionInfo.GitVersion, + Version: version.Get().String(), + } + + settings := otelcol.CollectorSettings{ + BuildInfo: info, + Factories: factories, } - cmd := otelcol.NewCommand( - otelcol.CollectorSettings{ - BuildInfo: info, - Factories: factories, - }, - ) + cmd := otelcol.NewCommand(settings) + + // We want to support running the binary in all-in-one mode without a config file. + // Since there are no explicit hooks in OTel Collector for that today (as of v0.87), + // we intercept the official RunE implementation and check if any --config flags are + // present in the args. If not, we pass a custom ConfigProvider, and create the + // collector manually. Unfortunately, `set`(ings) is passed to NewCommand above + // by value, otherwise we could've overwritten it in the interceptor and then delegated + // back to the official RunE. + otelRunE := cmd.RunE + cmd.RunE = func(cmd *cobra.Command, args []string) error { + configProvided := false + for _, arg := range args { + if strings.HasPrefix(arg, "--config") { + configProvided = true + break + } + } + if configProvided { + return otelRunE(cmd, args) + } + log.Print("No '--config' flags detected, using default All-in-One configuration with memory storage.") + log.Print("To customize All-in-One behavior, pass a proper configuration.") + settings.ConfigProvider = allinone.NewConfigProvider() + col, err := otelcol.NewCollector(settings) + if err != nil { + return err + } + return col.Run(cmd.Context()) + } cmd.Short = description cmd.Long = description diff --git a/cmd/jaeger-v2/internal/exporters/storageexporter/factory.go b/cmd/jaeger-v2/internal/exporters/storageexporter/factory.go index fb9171bb16e..b555a80bbec 100644 --- a/cmd/jaeger-v2/internal/exporters/storageexporter/factory.go +++ b/cmd/jaeger-v2/internal/exporters/storageexporter/factory.go @@ -10,11 +10,16 @@ import ( "go.opentelemetry.io/collector/consumer" "go.opentelemetry.io/collector/exporter" "go.opentelemetry.io/collector/exporter/exporterhelper" + + "github.com/jaegertracing/jaeger/cmd/jaeger-v2/internal/extension/jaegerstorage" ) // componentType is the name of this extension in configuration. const componentType = component.Type("jaeger_storage_exporter") +// ID is the identifier of this extension. +var ID = component.NewID(componentType) + // NewFactory creates a factory for jaeger_storage_exporter. func NewFactory() exporter.Factory { return exporter.NewFactory( @@ -25,7 +30,9 @@ func NewFactory() exporter.Factory { } func createDefaultConfig() component.Config { - return &Config{} + return &Config{ + TraceStorage: jaegerstorage.DefaultMemoryStore, + } } func createTracesExporter(ctx context.Context, set exporter.CreateSettings, config component.Config) (exporter.Traces, error) { diff --git a/cmd/jaeger-v2/internal/extension/jaegerquery/factory.go b/cmd/jaeger-v2/internal/extension/jaegerquery/factory.go index 7238fb55bc5..f752516b1d5 100644 --- a/cmd/jaeger-v2/internal/extension/jaegerquery/factory.go +++ b/cmd/jaeger-v2/internal/extension/jaegerquery/factory.go @@ -10,18 +10,23 @@ import ( "go.opentelemetry.io/collector/config/confighttp" "go.opentelemetry.io/collector/extension" + "github.com/jaegertracing/jaeger/cmd/jaeger-v2/internal/extension/jaegerstorage" "github.com/jaegertracing/jaeger/ports" ) // componentType is the name of this extension in configuration. const componentType = component.Type("jaeger_query") +// ID is the identifier of this extension. +var ID = component.NewID(componentType) + func NewFactory() extension.Factory { return extension.NewFactory(componentType, createDefaultConfig, createExtension, component.StabilityLevelBeta) } func createDefaultConfig() component.Config { return &Config{ + TraceStorage: jaegerstorage.DefaultMemoryStore, HTTPServerSettings: confighttp.HTTPServerSettings{ Endpoint: ports.PortToHostPort(ports.QueryHTTP), }, diff --git a/cmd/jaeger-v2/internal/extension/jaegerstorage/factory.go b/cmd/jaeger-v2/internal/extension/jaegerstorage/factory.go index 9da91008406..2cf2797bec3 100644 --- a/cmd/jaeger-v2/internal/extension/jaegerstorage/factory.go +++ b/cmd/jaeger-v2/internal/extension/jaegerstorage/factory.go @@ -8,10 +8,20 @@ import ( "go.opentelemetry.io/collector/component" "go.opentelemetry.io/collector/extension" + + memoryCfg "github.com/jaegertracing/jaeger/pkg/memory/config" +) + +const ( + // componentType is the name of this extension in configuration. + componentType = component.Type("jaeger_storage") + + // DefaultMemoryStore is the name of the memory storage included in the default configuration. + DefaultMemoryStore = "memstore" ) -// componentType is the name of this extension in configuration. -const componentType = component.Type("jaeger_storage") +// ID is the identifier of this extension. +var ID = component.NewID(componentType) func NewFactory() extension.Factory { return extension.NewFactory( @@ -23,7 +33,13 @@ func NewFactory() extension.Factory { } func createDefaultConfig() component.Config { - return &Config{} + return &Config{ + Memory: map[string]memoryCfg.Configuration{ + DefaultMemoryStore: { + MaxTraces: 100_000, + }, + }, + } } // createExtension creates the extension based on this config. diff --git a/pkg/version/build.go b/pkg/version/build.go index 1d51ed7eb9a..6d4fc208cc9 100644 --- a/pkg/version/build.go +++ b/pkg/version/build.go @@ -69,7 +69,7 @@ func NewInfoMetrics(metricsFactory metrics.Factory) *InfoMetrics { func (i Info) String() string { return fmt.Sprintf( - "application version: git-commit=%s, git-version=%s, build-date=%s", + "git-commit=%s, git-version=%s, build-date=%s", i.GitCommit, i.GitVersion, i.BuildDate, ) } diff --git a/pkg/version/command.go b/pkg/version/command.go index 55bc2dcfdfe..03919b6194b 100644 --- a/pkg/version/command.go +++ b/pkg/version/command.go @@ -25,7 +25,7 @@ import ( // Command creates version command func Command() *cobra.Command { info := Get() - log.Println(info) + log.Println("applicatio version:", info) return &cobra.Command{ Use: "version", Short: "Print the version.",