diff --git a/agent/client/client.go b/agent/client/client.go index 8a1829b7b7..ea20c065a8 100644 --- a/agent/client/client.go +++ b/agent/client/client.go @@ -15,6 +15,8 @@ import ( "github.com/kubeshop/tracetest/agent/proto" "go.uber.org/zap" "google.golang.org/grpc" + + "go.opentelemetry.io/otel/trace" ) const ( @@ -43,6 +45,7 @@ type Client struct { done chan bool logger *zap.Logger + tracer trace.Tracer stopListener func(context.Context, *proto.StopRequest) error triggerListener func(context.Context, *proto.TriggerRequest) error @@ -98,7 +101,7 @@ func (c *Client) Start(ctx context.Context) error { return err } - err = c.startHearthBeat(ctx) + err = c.startHeartBeat(ctx) if err != nil { return err } diff --git a/agent/client/options.go b/agent/client/options.go index 02b85e5bf7..8ee5565eab 100644 --- a/agent/client/options.go +++ b/agent/client/options.go @@ -3,6 +3,7 @@ package client import ( "time" + "go.opentelemetry.io/otel/trace" "go.uber.org/zap" ) @@ -31,3 +32,9 @@ func WithLogger(logger *zap.Logger) Option { c.logger = logger } } + +func WithTracer(tracer trace.Tracer) Option { + return func(c *Client) { + c.tracer = tracer + } +} diff --git a/agent/client/workflow_ping.go b/agent/client/workflow_ping.go index 1e7d8ad993..7794998011 100644 --- a/agent/client/workflow_ping.go +++ b/agent/client/workflow_ping.go @@ -8,7 +8,7 @@ import ( "github.com/kubeshop/tracetest/agent/proto" ) -func (c *Client) startHearthBeat(ctx context.Context) error { +func (c *Client) startHeartBeat(ctx context.Context) error { client := proto.NewOrchestratorClient(c.conn) ticker := time.NewTicker(c.config.PingPeriod) diff --git a/agent/config/config.go b/agent/config/config.go index 751acabe1a..07be1ac4ab 100644 --- a/agent/config/config.go +++ b/agent/config/config.go @@ -11,9 +11,11 @@ import ( ) type Config struct { - APIKey string `mapstructure:"api_key"` - Name string `mapstructure:"agent_name"` - ServerURL string `mapstructure:"server_url"` + APIKey string `mapstructure:"api_key"` + Name string `mapstructure:"agent_name"` + ServerURL string `mapstructure:"server_url"` + CollectorEndpoint string `mapstructure:"collector_endpoint"` + Mode string `mapstructure:"mode"` OTLPServer OtlpServer `mapstructure:"otlp_server"` } @@ -45,6 +47,8 @@ func LoadConfig() (Config, error) { vp.SetDefault("AGENT_NAME", getHostname()) vp.SetDefault("API_KEY", "") vp.SetDefault("SERVER_URL", "https://app.tracetest.io") + vp.SetDefault("COLLECTOR_ENDPOINT", "") + vp.SetDefault("MODE", "") vp.SetDefault("OTLP_SERVER.GRPC_PORT", 4317) vp.SetDefault("OTLP_SERVER.HTTP_PORT", 4318) diff --git a/agent/config/flags.go b/agent/config/flags.go index 068de3a81f..a0c599b867 100644 --- a/agent/config/flags.go +++ b/agent/config/flags.go @@ -8,12 +8,13 @@ const ( ) type Flags struct { - ServerURL string - OrganizationID string - EnvironmentID string - CI bool - AgentApiKey string - Token string - Mode Mode - LogLevel string + ServerURL string + OrganizationID string + EnvironmentID string + CI bool + AgentApiKey string + Token string + Mode Mode + LogLevel string + CollectorEndpoint string } diff --git a/agent/runner/session.go b/agent/runner/session.go index 1705a8227a..afba787f30 100644 --- a/agent/runner/session.go +++ b/agent/runner/session.go @@ -10,6 +10,7 @@ import ( "github.com/kubeshop/tracetest/agent/config" "github.com/kubeshop/tracetest/agent/event" "github.com/kubeshop/tracetest/agent/proto" + "github.com/kubeshop/tracetest/agent/telemetry" "github.com/kubeshop/tracetest/agent/workers" "github.com/kubeshop/tracetest/agent/workers/poller" @@ -36,8 +37,14 @@ func (s *Session) WaitUntilDisconnected() { func StartSession(ctx context.Context, cfg config.Config, observer event.Observer, logger *zap.Logger) (*Session, error) { observer = event.WrapObserver(observer) + tracer, err := telemetry.GetTracer(ctx, cfg.CollectorEndpoint, cfg.Name) + if err != nil { + observer.Error(err) + return nil, err + } + traceCache := collector.NewTraceCache() - controlPlaneClient, err := newControlPlaneClient(ctx, cfg, traceCache, observer, logger) + controlPlaneClient, err := newControlPlaneClient(ctx, cfg, traceCache, observer, logger, tracer) if err != nil { return nil, err } @@ -47,7 +54,7 @@ func StartSession(ctx context.Context, cfg config.Config, observer event.Observe return nil, err } - agentCollector, err := StartCollector(ctx, cfg, traceCache, observer, logger) + agentCollector, err := StartCollector(ctx, cfg, traceCache, observer, logger, tracer) if err != nil { return nil, err } @@ -73,8 +80,8 @@ func StartSession(ctx context.Context, cfg config.Config, observer event.Observe }, nil } -func StartCollector(ctx context.Context, config config.Config, traceCache collector.TraceCache, observer event.Observer, logger *zap.Logger) (collector.Collector, error) { - noopTracer := trace.NewNoopTracerProvider().Tracer("noop") +func StartCollector(ctx context.Context, config config.Config, traceCache collector.TraceCache, observer event.Observer, logger *zap.Logger, tracer trace.Tracer) (collector.Collector, error) { + collectorConfig := collector.Config{ HTTPPort: config.OTLPServer.HTTPPort, GRPCPort: config.OTLPServer.GRPCPort, @@ -90,7 +97,7 @@ func StartCollector(ctx context.Context, config config.Config, traceCache collec collector, err := collector.Start( ctx, collectorConfig, - noopTracer, + tracer, opts..., ) if err != nil { @@ -100,7 +107,7 @@ func StartCollector(ctx context.Context, config config.Config, traceCache collec return collector, nil } -func newControlPlaneClient(ctx context.Context, config config.Config, traceCache collector.TraceCache, observer event.Observer, logger *zap.Logger) (*client.Client, error) { +func newControlPlaneClient(ctx context.Context, config config.Config, traceCache collector.TraceCache, observer event.Observer, logger *zap.Logger, tracer trace.Tracer) (*client.Client, error) { controlPlaneClient, err := client.Connect(ctx, config.ServerURL, client.WithAPIKey(config.APIKey), client.WithAgentName(config.Name), @@ -116,6 +123,7 @@ func newControlPlaneClient(ctx context.Context, config config.Config, traceCache stopWorker := workers.NewStopperWorker( workers.WithStopperObserver(observer), workers.WithStopperCancelFuncList(processStopper.CancelMap()), + workers.WithStopperTracer(tracer), ) triggerWorker := workers.NewTriggerWorker( @@ -123,27 +131,32 @@ func newControlPlaneClient(ctx context.Context, config config.Config, traceCache workers.WithTraceCache(traceCache), workers.WithTriggerObserver(observer), workers.WithTriggerStoppableProcessRunner(processStopper.RunStoppableProcess), + workers.WithTriggerLogger(logger), + workers.WithTriggerTracer(tracer), ) pollingWorker := workers.NewPollerWorker( controlPlaneClient, workers.WithInMemoryDatastore(poller.NewInMemoryDatastore(traceCache)), - workers.WithObserver(observer), + workers.WithPollerObserver(observer), workers.WithPollerStoppableProcessRunner(processStopper.RunStoppableProcess), + workers.WithPollerLogger(logger), + workers.WithPollerTracer(tracer), ) - dataStoreTestConnectionWorker := workers.NewTestConnectionWorker(controlPlaneClient, observer) - - triggerWorker.SetLogger(logger) - pollingWorker.SetLogger(logger) - dataStoreTestConnectionWorker.SetLogger(logger) + dataStoreTestConnectionWorker := workers.NewTestConnectionWorker( + controlPlaneClient, + workers.WithTestConnectionLogger(logger), + workers.WithTestConnectionObserver(observer), + workers.WithTestConnectionTracer(tracer), + ) controlPlaneClient.OnDataStoreTestConnectionRequest(dataStoreTestConnectionWorker.Test) controlPlaneClient.OnStopRequest(stopWorker.Stop) controlPlaneClient.OnTriggerRequest(triggerWorker.Trigger) controlPlaneClient.OnPollingRequest(pollingWorker.Poll) controlPlaneClient.OnConnectionClosed(func(ctx context.Context, sr *proto.ShutdownRequest) error { - fmt.Printf("Server terminated the connection with the agent. Reason: %s\n", sr.Reason) + logger.Info(fmt.Sprintf("Server terminated the connection with the agent. Reason: %s\n", sr.Reason)) return controlPlaneClient.Close() }) diff --git a/agent/telemetry/tracer.go b/agent/telemetry/tracer.go new file mode 100644 index 0000000000..b301640f89 --- /dev/null +++ b/agent/telemetry/tracer.go @@ -0,0 +1,88 @@ +package telemetry + +import ( + "context" + "fmt" + "time" + + "go.opentelemetry.io/otel" + "go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc" + "go.opentelemetry.io/otel/propagation" + "go.opentelemetry.io/otel/sdk/resource" + sdkTrace "go.opentelemetry.io/otel/sdk/trace" + semconv "go.opentelemetry.io/otel/semconv/v1.4.0" + "go.opentelemetry.io/otel/trace" + "google.golang.org/grpc" + "google.golang.org/grpc/credentials/insecure" +) + +const spanExporterTimeout = 1 * time.Minute + +func GetTracer(ctx context.Context, otelExporterEndpoint, serviceName string) (trace.Tracer, error) { + if otelExporterEndpoint == "" { + // fallback, return noop + return trace.NewNoopTracerProvider().Tracer("noop"), nil + } + + realServiceName := fmt.Sprintf("tracetestAgent_%s", serviceName) + + spanExporter, err := newSpanExporter(ctx, otelExporterEndpoint) + if err != nil { + return nil, fmt.Errorf("failed to setup span exporter: %w", err) + } + + traceProvider, err := newTraceProvider(ctx, spanExporter, realServiceName) + if err != nil { + return nil, fmt.Errorf("failed to setup trace provider: %w", err) + } + + return traceProvider.Tracer(realServiceName), nil +} + +func newSpanExporter(ctx context.Context, otelExporterEndpoint string) (sdkTrace.SpanExporter, error) { + ctx, cancel := context.WithTimeout(ctx, spanExporterTimeout) + defer cancel() + + conn, err := grpc.DialContext(ctx, otelExporterEndpoint, grpc.WithTransportCredentials(insecure.NewCredentials()), grpc.WithBlock()) + if err != nil { + return nil, fmt.Errorf("failed to create gRPC connection to collector: %w", err) + } + + traceExporter, err := otlptracegrpc.New(ctx, otlptracegrpc.WithGRPCConn(conn)) + if err != nil { + return nil, fmt.Errorf("failed to create trace exporter: %w", err) + } + + return traceExporter, nil +} + +func newTraceProvider(ctx context.Context, spanExporter sdkTrace.SpanExporter, serviceName string) (*sdkTrace.TracerProvider, error) { + defaultResource := resource.Default() + + mergedResource, err := resource.Merge( + defaultResource, + resource.NewWithAttributes( + defaultResource.SchemaURL(), + semconv.ServiceNameKey.String(serviceName), + ), + ) + if err != nil { + return nil, fmt.Errorf("failed to create otel resource: %w", err) + } + + tp := sdkTrace.NewTracerProvider( + sdkTrace.WithBatcher(spanExporter), + sdkTrace.WithResource(mergedResource), + ) + + otel.SetTracerProvider(tp) + + otel.SetTextMapPropagator( + propagation.NewCompositeTextMapPropagator( + propagation.TraceContext{}, + propagation.Baggage{}, + ), + ) + + return tp, nil +} diff --git a/agent/workers/poller.go b/agent/workers/poller.go index 91ab2a5ce9..18434632d8 100644 --- a/agent/workers/poller.go +++ b/agent/workers/poller.go @@ -23,12 +23,12 @@ import ( type PollerWorker struct { client *client.Client - tracer trace.Tracer sentSpanIDs *gocache.Cache[string, bool] inmemoryDatastore tracedb.TraceDB logger *zap.Logger observer event.Observer stoppableProcessRunner StoppableProcessRunner + tracer trace.Tracer } type PollerOption func(*PollerWorker) @@ -39,7 +39,7 @@ func WithInMemoryDatastore(datastore tracedb.TraceDB) PollerOption { } } -func WithObserver(observer event.Observer) PollerOption { +func WithPollerObserver(observer event.Observer) PollerOption { return func(pw *PollerWorker) { pw.observer = observer } @@ -51,16 +51,25 @@ func WithPollerStoppableProcessRunner(stoppableProcessRunner StoppableProcessRun } } -func NewPollerWorker(client *client.Client, opts ...PollerOption) *PollerWorker { - // TODO: use a real tracer - tracer := trace.NewNoopTracerProvider().Tracer("noop") +func WithPollerLogger(logger *zap.Logger) PollerOption { + return func(pw *PollerWorker) { + pw.logger = logger + } +} +func WithPollerTracer(tracer trace.Tracer) PollerOption { + return func(pw *PollerWorker) { + pw.tracer = tracer + } +} + +func NewPollerWorker(client *client.Client, opts ...PollerOption) *PollerWorker { pollerWorker := &PollerWorker{ client: client, - tracer: tracer, sentSpanIDs: gocache.New[string, bool](), logger: zap.NewNop(), observer: event.NewNopObserver(), + tracer: trace.NewNoopTracerProvider().Tracer("noop"), } for _, opt := range opts { @@ -70,11 +79,10 @@ func NewPollerWorker(client *client.Client, opts ...PollerOption) *PollerWorker return pollerWorker } -func (w *PollerWorker) SetLogger(logger *zap.Logger) { - w.logger = logger -} - func (w *PollerWorker) Poll(ctx context.Context, request *proto.PollingRequest) error { + ctx, span := w.tracer.Start(ctx, "PollingRequest Worker operation") + defer span.End() + w.logger.Debug("Received polling request", zap.Any("request", request)) w.observer.StartTracePoll(request) @@ -110,7 +118,10 @@ func (w *PollerWorker) Poll(ctx context.Context, request *proto.PollingRequest) w.logger.Error("Error sending polling error", zap.Error(sendErr)) w.observer.Error(sendErr) - return fmt.Errorf("could not report polling error back to the server: %w. Original error: %s", sendErr, err.Error()) + formattedErr := fmt.Errorf("could not report polling error back to the server: %w. Original error: %s", sendErr, err.Error()) + span.RecordError(formattedErr) + + return formattedErr } } diff --git a/agent/workers/stopper.go b/agent/workers/stopper.go index 76e61a8c20..ebb3ddb7ff 100644 --- a/agent/workers/stopper.go +++ b/agent/workers/stopper.go @@ -7,6 +7,7 @@ import ( "github.com/kubeshop/tracetest/agent/event" "github.com/kubeshop/tracetest/agent/proto" + "go.opentelemetry.io/otel/trace" "go.uber.org/zap" ) @@ -14,6 +15,7 @@ type StopperWorker struct { logger *zap.Logger observer event.Observer cancelContexts *cancelCauseFuncMap + tracer trace.Tracer } type StopperOption func(*StopperWorker) @@ -30,6 +32,12 @@ func WithStopperObserver(observer event.Observer) StopperOption { } } +func WithStopperTracer(tracer trace.Tracer) StopperOption { + return func(tw *StopperWorker) { + tw.tracer = tracer + } +} + func NewStopperWorker(opts ...StopperOption) *StopperWorker { worker := &StopperWorker{ logger: zap.NewNop(), @@ -48,6 +56,9 @@ func (w *StopperWorker) SetLogger(logger *zap.Logger) { } func (w *StopperWorker) Stop(ctx context.Context, stopRequest *proto.StopRequest) error { + ctx, span := w.tracer.Start(ctx, "StopRequest Worker operation") + defer span.End() + w.logger.Debug("Stop request received", zap.Any("stopRequest", stopRequest)) w.observer.StartStopRequest(stopRequest) @@ -57,6 +68,8 @@ func (w *StopperWorker) Stop(ctx context.Context, stopRequest *proto.StopRequest err := fmt.Errorf("cancel func for StopRequest not found") w.logger.Error(err.Error(), zap.String("testID", stopRequest.TestID), zap.Int32("runID", stopRequest.RunID)) w.observer.EndStopRequest(stopRequest, err) + span.RecordError(err) + return err } diff --git a/agent/workers/testconnnection.go b/agent/workers/testconnnection.go index f9a847f01d..754dc25e6c 100644 --- a/agent/workers/testconnnection.go +++ b/agent/workers/testconnnection.go @@ -16,28 +16,50 @@ import ( type TestConnectionWorker struct { client *client.Client - tracer trace.Tracer logger *zap.Logger observer event.Observer + tracer trace.Tracer +} + +type TestConnectionOption func(*TestConnectionWorker) + +func WithTestConnectionLogger(logger *zap.Logger) TestConnectionOption { + return func(w *TestConnectionWorker) { + w.logger = logger + } +} + +func WithTestConnectionObserver(observer event.Observer) TestConnectionOption { + return func(w *TestConnectionWorker) { + w.observer = observer + } } -func NewTestConnectionWorker(client *client.Client, observer event.Observer) *TestConnectionWorker { - // TODO: use a real tracer - tracer := trace.NewNoopTracerProvider().Tracer("noop") +func WithTestConnectionTracer(tracer trace.Tracer) TestConnectionOption { + return func(w *TestConnectionWorker) { + w.tracer = tracer + } +} - return &TestConnectionWorker{ +func NewTestConnectionWorker(client *client.Client, opts ...TestConnectionOption) *TestConnectionWorker { + worker := &TestConnectionWorker{ client: client, - tracer: tracer, + tracer: trace.NewNoopTracerProvider().Tracer("noop"), logger: zap.NewNop(), - observer: observer, + observer: event.NewNopObserver(), } -} -func (w *TestConnectionWorker) SetLogger(logger *zap.Logger) { - w.logger = logger + for _, opt := range opts { + opt(worker) + } + + return worker } func (w *TestConnectionWorker) Test(ctx context.Context, request *proto.DataStoreConnectionTestRequest) error { + ctx, span := w.tracer.Start(ctx, "TestConnectionRequest Worker operation") + defer span.End() + w.logger.Debug("Received datastore connection test request") w.observer.StartDataStoreConnection(request) @@ -45,8 +67,10 @@ func (w *TestConnectionWorker) Test(ctx context.Context, request *proto.DataStor if err != nil { w.logger.Error("Invalid datastore", zap.Error(err)) w.observer.EndDataStoreConnection(request, err) + span.RecordError(err) return err } + w.logger.Debug("Converted datastore", zap.Any("datastore", datastoreConfig)) if datastoreConfig == nil { @@ -54,6 +78,7 @@ func (w *TestConnectionWorker) Test(ctx context.Context, request *proto.DataStor w.logger.Error("nil datastore", zap.Error(err)) w.observer.EndDataStoreConnection(request, err) + span.RecordError(err) return err } @@ -63,6 +88,7 @@ func (w *TestConnectionWorker) Test(ctx context.Context, request *proto.DataStor w.logger.Error("Invalid datastore", zap.Error(err)) log.Printf("Invalid datastore: %s", err.Error()) w.observer.EndDataStoreConnection(request, err) + span.RecordError(err) return err } @@ -95,6 +121,7 @@ func (w *TestConnectionWorker) Test(ctx context.Context, request *proto.DataStor if err != nil { w.logger.Error("Could not send datastore connection test result", zap.Error(err)) w.observer.Error(err) + span.RecordError(err) } else { w.logger.Debug("Sent datastore connection test result") } diff --git a/agent/workers/trigger.go b/agent/workers/trigger.go index 7ef4d2236b..f36d025252 100644 --- a/agent/workers/trigger.go +++ b/agent/workers/trigger.go @@ -26,6 +26,7 @@ type TriggerWorker struct { observer event.Observer sensor sensors.Sensor stoppableProcessRunner StoppableProcessRunner + tracer trace.Tracer } type TriggerOption func(*TriggerWorker) @@ -54,35 +55,46 @@ func WithSensor(sensor sensors.Sensor) TriggerOption { } } -func NewTriggerWorker(client *client.Client, opts ...TriggerOption) *TriggerWorker { - // TODO: use a real tracer - tracer := trace.NewNoopTracerProvider().Tracer("noop") +func WithTriggerLogger(logger *zap.Logger) TriggerOption { + return func(tw *TriggerWorker) { + tw.logger = logger + } +} - registry := agentTrigger.NewRegistry(tracer) - registry.Add(agentTrigger.HTTP()) - registry.Add(agentTrigger.GRPC()) - registry.Add(agentTrigger.TRACEID()) - registry.Add(agentTrigger.KAFKA()) +func WithTriggerTracer(tracer trace.Tracer) TriggerOption { + return func(tw *TriggerWorker) { + tw.tracer = tracer + } +} +func NewTriggerWorker(client *client.Client, opts ...TriggerOption) *TriggerWorker { worker := &TriggerWorker{ client: client, - registry: registry, logger: zap.NewNop(), observer: event.NewNopObserver(), + tracer: trace.NewNoopTracerProvider().Tracer("noop"), } for _, opt := range opts { opt(worker) } - return worker -} + registry := agentTrigger.NewRegistry(worker.tracer) + registry.Add(agentTrigger.HTTP()) + registry.Add(agentTrigger.GRPC()) + registry.Add(agentTrigger.TRACEID()) + registry.Add(agentTrigger.KAFKA()) + + // Assign registry into worker + worker.registry = registry -func (w *TriggerWorker) SetLogger(logger *zap.Logger) { - w.logger = logger + return worker } func (w *TriggerWorker) Trigger(ctx context.Context, triggerRequest *proto.TriggerRequest) error { + ctx, span := w.tracer.Start(ctx, "TriggerRequest Worker operation") + defer span.End() + w.logger.Debug("Trigger request received", zap.Any("triggerRequest", triggerRequest)) w.observer.StartTriggerExecution(triggerRequest) @@ -113,7 +125,10 @@ func (w *TriggerWorker) Trigger(ctx context.Context, triggerRequest *proto.Trigg w.logger.Error("Could not report trigger error back to the server", zap.Error(sendErr)) w.observer.Error(sendErr) - err = fmt.Errorf("could not report trigger error back to the server: %w. Original error: %s", sendErr, err.Error()) + formattedErr := fmt.Errorf("could not report trigger error back to the server: %w. Original error: %s", sendErr, err.Error()) + span.RecordError(formattedErr) + + return formattedErr } } diff --git a/cli/cmd/start_cmd.go b/cli/cmd/start_cmd.go index 1f79c07cc4..05fa9313d3 100644 --- a/cli/cmd/start_cmd.go +++ b/cli/cmd/start_cmd.go @@ -16,7 +16,7 @@ var ( defaultToken = os.Getenv("TRACETEST_TOKEN") defaultEndpoint = os.Getenv("TRACETEST_SERVER_URL") defaultAPIKey = os.Getenv("TRACETEST_API_KEY") - saveParams = &saveParameters{} + startParams = &startParameters{} ) var startCmd = &cobra.Command{ @@ -27,13 +27,14 @@ var startCmd = &cobra.Command{ PreRun: setupCommand(SkipConfigValidation(), SkipVersionMismatchCheck()), Run: WithResultHandler((func(ctx context.Context, _ *cobra.Command, _ []string) (string, error) { flags := agentConfig.Flags{ - OrganizationID: saveParams.organizationID, - EnvironmentID: saveParams.environmentID, - ServerURL: overrideEndpoint, - AgentApiKey: saveParams.agentApiKey, - Token: saveParams.token, - Mode: agentConfig.Mode(saveParams.mode), - LogLevel: saveParams.logLevel, + OrganizationID: startParams.organizationID, + EnvironmentID: startParams.environmentID, + ServerURL: overrideEndpoint, + AgentApiKey: startParams.agentApiKey, + Token: startParams.token, + Mode: agentConfig.Mode(startParams.mode), + LogLevel: startParams.logLevel, + CollectorEndpoint: startParams.collectorEndpoint, } // override organization and environment id from context. @@ -57,6 +58,18 @@ var startCmd = &cobra.Command{ flags.AgentApiKey = cfg.APIKey } + // if there is a config to the collector, it should preceed the flags + // otherwise, sync flags and config + if cfg.CollectorEndpoint != "" { + flags.CollectorEndpoint = cfg.CollectorEndpoint + } else { + cfg.CollectorEndpoint = flags.CollectorEndpoint + } + + if cfg.Mode != "" { + flags.Mode = agentConfig.Mode(cfg.Mode) + } + err = agentRunner.Run(ctx, cliConfig, flags) return "", err })), @@ -64,13 +77,14 @@ var startCmd = &cobra.Command{ } func init() { - startCmd.Flags().StringVarP(&saveParams.organizationID, "organization", "", "", "organization id") - startCmd.Flags().StringVarP(&saveParams.environmentID, "environment", "", "", "environment id") - startCmd.Flags().StringVarP(&saveParams.agentApiKey, "api-key", "", defaultAPIKey, "agent api key") - startCmd.Flags().StringVarP(&saveParams.token, "token", "", defaultToken, "token authentication key") + startCmd.Flags().StringVarP(&startParams.organizationID, "organization", "", "", "organization id") + startCmd.Flags().StringVarP(&startParams.environmentID, "environment", "", "", "environment id") + startCmd.Flags().StringVarP(&startParams.agentApiKey, "api-key", "", defaultAPIKey, "agent api key") + startCmd.Flags().StringVarP(&startParams.token, "token", "", defaultToken, "token authentication key") startCmd.Flags().StringVarP(&overrideEndpoint, "endpoint", "e", defaultEndpoint, "set the value for the endpoint, so the CLI won't ask for this value") - startCmd.Flags().StringVarP(&saveParams.mode, "mode", "m", "desktop", "set how the agent will start") - startCmd.Flags().StringVarP(&saveParams.logLevel, "log-level", "l", "debug", "set the agent log level") + startCmd.Flags().StringVarP(&startParams.mode, "mode", "m", "desktop", "set how the agent will start") + startCmd.Flags().StringVarP(&startParams.logLevel, "log-level", "l", "debug", "set the agent log level") + startCmd.Flags().StringVarP(&startParams.collectorEndpoint, "collector-endpoint", "", "", "address of the OTel Collector endpoint") startCmd.Flags().MarkDeprecated("endpoint", "use --server-url instead") startCmd.Flags().MarkShorthandDeprecated("e", "use --server-url instead") @@ -78,11 +92,12 @@ func init() { rootCmd.AddCommand(startCmd) } -type saveParameters struct { - organizationID string - environmentID string - agentApiKey string - token string - mode string - logLevel string +type startParameters struct { + organizationID string + environmentID string + agentApiKey string + token string + mode string + logLevel string + collectorEndpoint string } diff --git a/cli/config/configurator.go b/cli/config/configurator.go index 1f78a838d6..cdbee98d16 100644 --- a/cli/config/configurator.go +++ b/cli/config/configurator.go @@ -4,6 +4,7 @@ import ( "context" "fmt" "net/http" + "os" "strings" "github.com/golang-jwt/jwt" @@ -161,7 +162,40 @@ func (e invalidServerErr) Render() { e.ui.Error(msg) } +func (c Configurator) populateConfigWithDevConfig(ctx context.Context, cfg *Config) { + cfg.AgentEndpoint = os.Getenv("TRACETEST_DEV_AGENT_ENDPOINT") + if cfg.AgentEndpoint == "" { + cfg.AgentEndpoint = "localhost:8091" + } + + cfg.UIEndpoint = os.Getenv("TRACETEST_DEV_UI_ENDPOINT") + if cfg.UIEndpoint == "" { + cfg.UIEndpoint = "http://localhost:3000" + } + + cfg.Scheme = os.Getenv("TRACETEST_DEV_SCHEME") + if cfg.Scheme == "" { + cfg.Scheme = "http" + } + + cfg.Endpoint = os.Getenv("TRACETEST_DEV_ENDPOINT") + if cfg.Endpoint == "" { + cfg.Endpoint = "localhost:11633" + } + + cfg.ServerPath = os.Getenv("TRACETEST_DEV_SERVER_PATH") +} + func (c Configurator) populateConfigWithVersionInfo(ctx context.Context, cfg Config) (_ Config, _ error, isOSS bool) { + cliVersion := Version + if cliVersion == "dev" { + c.populateConfigWithDevConfig(ctx, &cfg) + + c.ui.Success("Configured Tracetest CLI in development mode") + + return cfg, nil, false + } + client := GetAPIClient(cfg) version, err := getVersionMetadata(ctx, client) if err != nil { diff --git a/go.sum b/go.sum index 9824a89642..0c4d564ec4 100644 --- a/go.sum +++ b/go.sum @@ -1945,8 +1945,6 @@ go.opentelemetry.io/collector/semconv v0.80.0/go.mod h1:TlYPtzvsXyHOgr5eATi43qEM go.opentelemetry.io/contrib v0.20.0/go.mod h1:G/EtFaa6qaN7+LxqfIAT3GiZa7Wv5DTBUzl5H4LY0Kc= go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.20.0/go.mod h1:oVGt1LRbBOBq1A5BQLlUg9UaU/54aiHw8cgjV3aWZ/E= go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.28.0/go.mod h1:vEhqr0m4eTc+DWxfsXoXue2GBgV2uUwVznkGIHW/e5w= -go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.44.0 h1:b8xjZxHbLrXAum4SxJd1Rlm7Y/fKaB+6ACI7/e5EfSA= -go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.44.0/go.mod h1:1ei0a32xOGkFoySu7y1DAHfcuIhC0pNZpvY2huXuMy4= go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.45.0 h1:RsQi0qJ2imFfCvZabqzM9cNXBG8k6gXMv1A0cXRmH6A= go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.45.0/go.mod h1:vsh3ySueQCiKPxFLvjWC4Z135gIa34TQ/NSqkDTZYUM= go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.20.0/go.mod h1:2AboqHi0CiIZU0qwhtUfCYD1GeUzvvIXWNkhDt7ZMG4=