From 013efad9fb172fc893e52898d16ff4bbef3890ad Mon Sep 17 00:00:00 2001 From: Albert <26584478+albertteoh@users.noreply.github.com> Date: Sun, 13 Jun 2021 01:15:26 +1000 Subject: [PATCH] Hook up MetricsQueryService to main funcs (#3079) --- cmd/all-in-one/main.go | 34 ++++++++- .../app/querysvc/metrics_query_service.go | 18 +---- .../querysvc/metrics_query_service_test.go | 34 --------- cmd/query/app/server.go | 14 ++-- cmd/query/app/server_test.go | 30 +++++--- cmd/query/main.go | 30 +++++++- plugin/metrics/disabled/factory.go | 48 ++++++++++++ plugin/metrics/disabled/factory_test.go | 42 +++++++++++ plugin/metrics/disabled/reader.go | 64 ++++++++++++++++ plugin/metrics/disabled/reader_test.go | 74 +++++++++++++++++++ plugin/metrics/factory.go | 6 ++ plugin/metrics/factory_test.go | 21 ++++-- 12 files changed, 335 insertions(+), 80 deletions(-) create mode 100644 plugin/metrics/disabled/factory.go create mode 100644 plugin/metrics/disabled/factory_test.go create mode 100644 plugin/metrics/disabled/reader.go create mode 100644 plugin/metrics/disabled/reader_test.go diff --git a/cmd/all-in-one/main.go b/cmd/all-in-one/main.go index 4433194cf47..24e12ddfc1c 100644 --- a/cmd/all-in-one/main.go +++ b/cmd/all-in-one/main.go @@ -16,6 +16,7 @@ package main import ( + "fmt" "io" "log" "os" @@ -44,10 +45,12 @@ import ( "github.com/jaegertracing/jaeger/cmd/status" "github.com/jaegertracing/jaeger/pkg/config" "github.com/jaegertracing/jaeger/pkg/version" + metricsPlugin "github.com/jaegertracing/jaeger/plugin/metrics" ss "github.com/jaegertracing/jaeger/plugin/sampling/strategystore" "github.com/jaegertracing/jaeger/plugin/storage" "github.com/jaegertracing/jaeger/ports" "github.com/jaegertracing/jaeger/storage/dependencystore" + "github.com/jaegertracing/jaeger/storage/metricsstore" "github.com/jaegertracing/jaeger/storage/spanstore" storageMetrics "github.com/jaegertracing/jaeger/storage/spanstore/metrics" ) @@ -71,6 +74,12 @@ func main() { log.Fatalf("Cannot initialize sampling strategy store factory: %v", err) } + fc := metricsPlugin.FactoryConfigFromEnv() + metricsReaderFactory, err := metricsPlugin.NewFactory(fc) + if err != nil { + log.Fatalf("Cannot initialize metrics store factory: %v", err) + } + v := viper.New() command := &cobra.Command{ Use: "jaeger-all-in-one", @@ -107,6 +116,11 @@ by default uses only in-memory database.`, logger.Fatal("Failed to create dependency reader", zap.Error(err)) } + metricsReader, err := createMetricsReader(metricsReaderFactory, v, logger) + if err != nil { + logger.Fatal("Failed to create metrics reader", zap.Error(err)) + } + strategyStoreFactory.InitFromViper(v) if err := strategyStoreFactory.Initialize(metricsFactory, logger); err != nil { logger.Fatal("Failed to init sampling strategy store factory", zap.Error(err)) @@ -157,8 +171,8 @@ by default uses only in-memory database.`, // query querySrv := startQuery( svc, qOpts, qOpts.BuildQueryServiceOptions(storageFactory, logger), - spanReader, dependencyReader, - rootMetricsFactory, metricsFactory, + spanReader, dependencyReader, metricsReader, + metricsFactory, ) svc.RunAndThen(func() { @@ -196,6 +210,7 @@ by default uses only in-memory database.`, collectorApp.AddFlags, queryApp.AddFlags, strategyStoreFactory.AddFlags, + metricsReaderFactory.AddFlags, ) if err := command.Execute(); err != nil { @@ -229,12 +244,13 @@ func startQuery( queryOpts *querysvc.QueryServiceOptions, spanReader spanstore.Reader, depReader dependencystore.Reader, - rootFactory metrics.Factory, + metricsReader metricsstore.Reader, baseFactory metrics.Factory, ) *queryApp.Server { spanReader = storageMetrics.NewReadMetricsDecorator(spanReader, baseFactory.Namespace(metrics.NSOptions{Name: "query"})) qs := querysvc.NewQueryService(spanReader, depReader, *queryOpts) - server, err := queryApp.NewServer(svc.Logger, qs, qOpts, opentracing.GlobalTracer()) + mqs := querysvc.NewMetricsQueryService(metricsReader) + server, err := queryApp.NewServer(svc.Logger, qs, mqs, qOpts, opentracing.GlobalTracer()) if err != nil { svc.Logger.Fatal("Could not start jaeger-query service", zap.Error(err)) } @@ -272,3 +288,13 @@ func initTracer(metricsFactory metrics.Factory, logger *zap.Logger) io.Closer { opentracing.SetGlobalTracer(tracer) return closer } + +func createMetricsReader(factory *metricsPlugin.Factory, v *viper.Viper, logger *zap.Logger) (metricsstore.Reader, error) { + if err := factory.Initialize(logger); err != nil { + return nil, fmt.Errorf("failed to init metrics reader factory: %w", err) + } + + // Ensure default parameter values are loaded correctly. + factory.InitFromViper(v) + return factory.CreateMetricsReader() +} diff --git a/cmd/query/app/querysvc/metrics_query_service.go b/cmd/query/app/querysvc/metrics_query_service.go index ac29cb7fc1b..5596901bb5b 100644 --- a/cmd/query/app/querysvc/metrics_query_service.go +++ b/cmd/query/app/querysvc/metrics_query_service.go @@ -16,22 +16,18 @@ package querysvc import ( "context" - "errors" "time" "github.com/jaegertracing/jaeger/proto-gen/api_v2/metrics" "github.com/jaegertracing/jaeger/storage/metricsstore" ) -// MetricsQueryService contains the underlying reader required for querying the metrics store. +// MetricsQueryService provides a means of querying R.E.D metrics from an underlying metrics store. type MetricsQueryService struct { metricsReader metricsstore.Reader } -var errNilReader = errors.New("no reader defined for MetricsQueryService") - // NewMetricsQueryService returns a new MetricsQueryService. -// A nil reader will result in a nil MetricsQueryService being returned. func NewMetricsQueryService(reader metricsstore.Reader) *MetricsQueryService { return &MetricsQueryService{ metricsReader: reader, @@ -40,32 +36,20 @@ func NewMetricsQueryService(reader metricsstore.Reader) *MetricsQueryService { // GetLatencies is the queryService implementation of metricsstore.Reader. func (mqs MetricsQueryService) GetLatencies(ctx context.Context, params *metricsstore.LatenciesQueryParameters) (*metrics.MetricFamily, error) { - if mqs.metricsReader == nil { - return nil, errNilReader - } return mqs.metricsReader.GetLatencies(ctx, params) } // GetCallRates is the queryService implementation of metricsstore.Reader. func (mqs MetricsQueryService) GetCallRates(ctx context.Context, params *metricsstore.CallRateQueryParameters) (*metrics.MetricFamily, error) { - if mqs.metricsReader == nil { - return nil, errNilReader - } return mqs.metricsReader.GetCallRates(ctx, params) } // GetErrorRates is the queryService implementation of metricsstore.Reader. func (mqs MetricsQueryService) GetErrorRates(ctx context.Context, params *metricsstore.ErrorRateQueryParameters) (*metrics.MetricFamily, error) { - if mqs.metricsReader == nil { - return nil, errNilReader - } return mqs.metricsReader.GetErrorRates(ctx, params) } // GetMinStepDuration is the queryService implementation of metricsstore.Reader. func (mqs MetricsQueryService) GetMinStepDuration(ctx context.Context, params *metricsstore.MinStepDurationQueryParameters) (time.Duration, error) { - if mqs.metricsReader == nil { - return 0, errNilReader - } return mqs.metricsReader.GetMinStepDuration(ctx, params) } diff --git a/cmd/query/app/querysvc/metrics_query_service_test.go b/cmd/query/app/querysvc/metrics_query_service_test.go index f75a6395497..5783f1e54a1 100644 --- a/cmd/query/app/querysvc/metrics_query_service_test.go +++ b/cmd/query/app/querysvc/metrics_query_service_test.go @@ -34,11 +34,9 @@ type testMetricsQueryService struct { func initializeTestMetricsQueryService() *testMetricsQueryService { metricsReader := &metricsmocks.Reader{} - tqs := testMetricsQueryService{ metricsReader: metricsReader, } - tqs.queryService = NewMetricsQueryService(metricsReader) return &tqs } @@ -58,14 +56,6 @@ func TestGetLatencies(t *testing.T) { assert.Equal(t, expectedLatencies, actualLatencies) } -func TestGetLatenciesNilReader(t *testing.T) { - qs := NewMetricsQueryService(nil) - qParams := &metricsstore.LatenciesQueryParameters{} - r, err := qs.GetLatencies(context.Background(), qParams) - assert.Zero(t, r) - assert.EqualError(t, err, errNilReader.Error()) -} - // Test QueryService.GetCallRates() func TestGetCallRates(t *testing.T) { tqs := initializeTestMetricsQueryService() @@ -81,14 +71,6 @@ func TestGetCallRates(t *testing.T) { assert.Equal(t, expectedCallRates, actualCallRates) } -func TestGetCallRatesNilReader(t *testing.T) { - qs := NewMetricsQueryService(nil) - qParams := &metricsstore.CallRateQueryParameters{} - r, err := qs.GetCallRates(context.Background(), qParams) - assert.Zero(t, r) - assert.EqualError(t, err, errNilReader.Error()) -} - // Test QueryService.GetErrorRates() func TestGetErrorRates(t *testing.T) { tqs := initializeTestMetricsQueryService() @@ -101,14 +83,6 @@ func TestGetErrorRates(t *testing.T) { assert.Equal(t, expectedErrorRates, actualErrorRates) } -func TestGetErrorRatesNilReader(t *testing.T) { - qs := NewMetricsQueryService(nil) - qParams := &metricsstore.ErrorRateQueryParameters{} - r, err := qs.GetErrorRates(context.Background(), qParams) - assert.Zero(t, r) - assert.EqualError(t, err, errNilReader.Error()) -} - // Test QueryService.GetMinStepDurations() func TestGetMinStepDurations(t *testing.T) { tqs := initializeTestMetricsQueryService() @@ -120,11 +94,3 @@ func TestGetMinStepDurations(t *testing.T) { assert.NoError(t, err) assert.Equal(t, expectedMinStep, actualMinStep) } - -func TestGetMinStepDurationsNilReader(t *testing.T) { - qs := NewMetricsQueryService(nil) - qParams := &metricsstore.MinStepDurationQueryParameters{} - r, err := qs.GetMinStepDuration(context.Background(), qParams) - assert.Zero(t, r) - assert.EqualError(t, err, errNilReader.Error()) -} diff --git a/cmd/query/app/server.go b/cmd/query/app/server.go index 52be92b45c4..606f8b12766 100644 --- a/cmd/query/app/server.go +++ b/cmd/query/app/server.go @@ -52,7 +52,7 @@ type Server struct { } // NewServer creates and initializes Server -func NewServer(logger *zap.Logger, querySvc *querysvc.QueryService, options *QueryOptions, tracer opentracing.Tracer) (*Server, error) { +func NewServer(logger *zap.Logger, querySvc *querysvc.QueryService, metricsQuerySvc *querysvc.MetricsQueryService, options *QueryOptions, tracer opentracing.Tracer) (*Server, error) { _, httpPort, err := net.SplitHostPort(options.HTTPHostPort) if err != nil { @@ -67,12 +67,12 @@ func NewServer(logger *zap.Logger, querySvc *querysvc.QueryService, options *Que return nil, errors.New("server with TLS enabled can not use same host ports for gRPC and HTTP. Use dedicated HTTP and gRPC host ports instead") } - grpcServer, err := createGRPCServer(querySvc, options, logger, tracer) + grpcServer, err := createGRPCServer(querySvc, metricsQuerySvc, options, logger, tracer) if err != nil { return nil, err } - httpServer, err := createHTTPServer(querySvc, options, tracer, logger) + httpServer, err := createHTTPServer(querySvc, metricsQuerySvc, options, tracer, logger) if err != nil { return nil, err } @@ -94,7 +94,7 @@ func (s Server) HealthCheckStatus() chan healthcheck.Status { return s.unavailableChannel } -func createGRPCServer(querySvc *querysvc.QueryService, options *QueryOptions, logger *zap.Logger, tracer opentracing.Tracer) (*grpc.Server, error) { +func createGRPCServer(querySvc *querysvc.QueryService, metricsQuerySvc *querysvc.MetricsQueryService, options *QueryOptions, logger *zap.Logger, tracer opentracing.Tracer) (*grpc.Server, error) { var grpcOpts []grpc.ServerOption if options.TLSGRPC.Enabled { @@ -111,11 +111,15 @@ func createGRPCServer(querySvc *querysvc.QueryService, options *QueryOptions, lo server := grpc.NewServer(grpcOpts...) handler := NewGRPCHandler(querySvc, logger, tracer) + + // TODO: Register MetricsQueryService api_v2.RegisterQueryServiceServer(server, handler) + return server, nil } -func createHTTPServer(querySvc *querysvc.QueryService, queryOpts *QueryOptions, tracer opentracing.Tracer, logger *zap.Logger) (*http.Server, error) { +func createHTTPServer(querySvc *querysvc.QueryService, metricsQuerySvc *querysvc.MetricsQueryService, queryOpts *QueryOptions, tracer opentracing.Tracer, logger *zap.Logger) (*http.Server, error) { + // TODO: Add HandlerOptions.MetricsQueryService apiHandlerOptions := []HandlerOption{ HandlerOptions.Logger(logger), HandlerOptions.Tracer(tracer), diff --git a/cmd/query/app/server_test.go b/cmd/query/app/server_test.go index 9bc3579d8c0..3ade4591b79 100644 --- a/cmd/query/app/server_test.go +++ b/cmd/query/app/server_test.go @@ -24,7 +24,7 @@ import ( "testing" "time" - opentracing "github.com/opentracing/opentracing-go" + "github.com/opentracing/opentracing-go" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/mock" "github.com/stretchr/testify/require" @@ -64,7 +64,7 @@ func TestCreateTLSServerSinglePortError(t *testing.T) { ClientCAPath: testCertKeyLocation + "/example-CA-cert.pem", } - _, err := NewServer(zap.NewNop(), &querysvc.QueryService{}, + _, err := NewServer(zap.NewNop(), &querysvc.QueryService{}, querysvc.NewMetricsQueryService(nil), &QueryOptions{HTTPHostPort: ":8080", GRPCHostPort: ":8080", TLSGRPC: tlsCfg, TLSHTTP: tlsCfg}, opentracing.NoopTracer{}) assert.NotNil(t, err) } @@ -77,7 +77,7 @@ func TestCreateTLSGrpcServerError(t *testing.T) { ClientCAPath: "invalid/path", } - _, err := NewServer(zap.NewNop(), &querysvc.QueryService{}, + _, err := NewServer(zap.NewNop(), &querysvc.QueryService{}, querysvc.NewMetricsQueryService(nil), &QueryOptions{HTTPHostPort: ":8080", GRPCHostPort: ":8081", TLSGRPC: tlsCfg}, opentracing.NoopTracer{}) assert.NotNil(t, err) } @@ -90,7 +90,7 @@ func TestCreateTLSHttpServerError(t *testing.T) { ClientCAPath: "invalid/path", } - _, err := NewServer(zap.NewNop(), &querysvc.QueryService{}, + _, err := NewServer(zap.NewNop(), &querysvc.QueryService{}, querysvc.NewMetricsQueryService(nil), &QueryOptions{HTTPHostPort: ":8080", GRPCHostPort: ":8081", TLSHTTP: tlsCfg}, opentracing.NoopTracer{}) assert.NotNil(t, err) } @@ -331,7 +331,8 @@ func TestServerHTTPTLS(t *testing.T) { spanReader.On("GetServices", mock.AnythingOfType("*context.valueCtx")).Return(expectedServices, nil) querySvc := querysvc.NewQueryService(spanReader, dependencyReader, querysvc.QueryServiceOptions{}) - server, err := NewServer(flagsSvc.Logger, querySvc, + metricsQuerySvc := querysvc.NewMetricsQueryService(nil) + server, err := NewServer(flagsSvc.Logger, querySvc, metricsQuerySvc, serverOptions, opentracing.NoopTracer{}) assert.Nil(t, err) @@ -491,7 +492,8 @@ func TestServerGRPCTLS(t *testing.T) { spanReader.On("GetServices", mock.AnythingOfType("*context.valueCtx")).Return(expectedServices, nil) querySvc := querysvc.NewQueryService(spanReader, dependencyReader, querysvc.QueryServiceOptions{}) - server, err := NewServer(flagsSvc.Logger, querySvc, + metricsQuerySvc := querysvc.NewMetricsQueryService(nil) + server, err := NewServer(flagsSvc.Logger, querySvc, metricsQuerySvc, serverOptions, opentracing.NoopTracer{}) assert.Nil(t, err) @@ -545,12 +547,12 @@ func TestServerGRPCTLS(t *testing.T) { } func TestServerBadHostPort(t *testing.T) { - _, err := NewServer(zap.NewNop(), &querysvc.QueryService{}, + _, err := NewServer(zap.NewNop(), &querysvc.QueryService{}, querysvc.NewMetricsQueryService(nil), &QueryOptions{HTTPHostPort: "8080", GRPCHostPort: "127.0.0.1:8081", BearerTokenPropagation: true}, opentracing.NoopTracer{}) assert.NotNil(t, err) - _, err = NewServer(zap.NewNop(), &querysvc.QueryService{}, + _, err = NewServer(zap.NewNop(), &querysvc.QueryService{}, querysvc.NewMetricsQueryService(nil), &QueryOptions{HTTPHostPort: "127.0.0.1:8081", GRPCHostPort: "9123", BearerTokenPropagation: true}, opentracing.NoopTracer{}) @@ -576,6 +578,7 @@ func TestServerInUseHostPort(t *testing.T) { server, err := NewServer( zap.NewNop(), &querysvc.QueryService{}, + querysvc.NewMetricsQueryService(nil), &QueryOptions{ HTTPHostPort: tc.httpHostPort, GRPCHostPort: tc.grpcHostPort, @@ -608,8 +611,8 @@ func TestServerSinglePort(t *testing.T) { spanReader.On("GetServices", mock.AnythingOfType("*context.valueCtx")).Return(expectedServices, nil) querySvc := querysvc.NewQueryService(spanReader, dependencyReader, querysvc.QueryServiceOptions{}) - - server, err := NewServer(flagsSvc.Logger, querySvc, + metricsQuerySvc := querysvc.NewMetricsQueryService(nil) + server, err := NewServer(flagsSvc.Logger, querySvc, metricsQuerySvc, &QueryOptions{GRPCHostPort: hostPort, HTTPHostPort: hostPort, BearerTokenPropagation: true}, opentracing.NoopTracer{}) assert.Nil(t, err) @@ -658,8 +661,10 @@ func TestServerGracefulExit(t *testing.T) { hostPort := ports.PortToHostPort(ports.QueryAdminHTTP) querySvc := &querysvc.QueryService{} + metricsQuerySvc := querysvc.NewMetricsQueryService(nil) tracer := opentracing.NoopTracer{} - server, err := NewServer(flagsSvc.Logger, querySvc, &QueryOptions{GRPCHostPort: hostPort, HTTPHostPort: hostPort}, tracer) + + server, err := NewServer(flagsSvc.Logger, querySvc, metricsQuerySvc, &QueryOptions{GRPCHostPort: hostPort, HTTPHostPort: hostPort}, tracer) assert.Nil(t, err) assert.NoError(t, server.Start()) go func() { @@ -685,8 +690,9 @@ func TestServerHandlesPortZero(t *testing.T) { flagsSvc.Logger = zap.New(zapCore) querySvc := &querysvc.QueryService{} + metricsQuerySvc := querysvc.NewMetricsQueryService(nil) tracer := opentracing.NoopTracer{} - server, err := NewServer(flagsSvc.Logger, querySvc, &QueryOptions{GRPCHostPort: ":0", HTTPHostPort: ":0"}, tracer) + server, err := NewServer(flagsSvc.Logger, querySvc, metricsQuerySvc, &QueryOptions{GRPCHostPort: ":0", HTTPHostPort: ":0"}, tracer) assert.Nil(t, err) assert.NoError(t, server.Start()) server.Close() diff --git a/cmd/query/main.go b/cmd/query/main.go index adbbfe46d08..eec8879b8c2 100644 --- a/cmd/query/main.go +++ b/cmd/query/main.go @@ -37,6 +37,7 @@ import ( "github.com/jaegertracing/jaeger/cmd/status" "github.com/jaegertracing/jaeger/pkg/config" "github.com/jaegertracing/jaeger/pkg/version" + metricsPlugin "github.com/jaegertracing/jaeger/plugin/metrics" "github.com/jaegertracing/jaeger/plugin/storage" "github.com/jaegertracing/jaeger/ports" "github.com/jaegertracing/jaeger/storage/spanstore" @@ -51,6 +52,12 @@ func main() { log.Fatalf("Cannot initialize storage factory: %v", err) } + fc := metricsPlugin.FactoryConfigFromEnv() + metricsReaderFactory, err := metricsPlugin.NewFactory(fc) + if err != nil { + log.Fatalf("Cannot initialize metrics factory: %v", err) + } + v := viper.New() var command = &cobra.Command{ Use: "jaeger-query", @@ -101,13 +108,17 @@ func main() { if err != nil { logger.Fatal("Failed to create dependency reader", zap.Error(err)) } + + metricsQueryService, err := createMetricsQueryService(metricsReaderFactory, v, logger) + if err != nil { + logger.Fatal("Failed to create metrics query service", zap.Error(err)) + } queryServiceOptions := queryOpts.BuildQueryServiceOptions(storageFactory, logger) queryService := querysvc.NewQueryService( spanReader, dependencyReader, *queryServiceOptions) - - server, err := app.NewServer(svc.Logger, queryService, queryOpts, tracer) + server, err := app.NewServer(svc.Logger, queryService, metricsQueryService, queryOpts, tracer) if err != nil { logger.Fatal("Failed to create server", zap.Error(err)) } @@ -143,6 +154,7 @@ func main() { svc.AddFlags, storageFactory.AddFlags, app.AddFlags, + metricsReaderFactory.AddFlags, ) if err := command.Execute(); err != nil { @@ -150,3 +162,17 @@ func main() { os.Exit(1) } } + +func createMetricsQueryService(factory *metricsPlugin.Factory, v *viper.Viper, logger *zap.Logger) (*querysvc.MetricsQueryService, error) { + if err := factory.Initialize(logger); err != nil { + return nil, fmt.Errorf("failed to init metrics factory: %w", err) + } + + // Ensure default parameter values are loaded correctly. + factory.InitFromViper(v) + metricsReader, err := factory.CreateMetricsReader() + if err != nil { + return nil, fmt.Errorf("failed to create metrics reader: %w", err) + } + return querysvc.NewMetricsQueryService(metricsReader), nil +} diff --git a/plugin/metrics/disabled/factory.go b/plugin/metrics/disabled/factory.go new file mode 100644 index 00000000000..fec3f45c9bf --- /dev/null +++ b/plugin/metrics/disabled/factory.go @@ -0,0 +1,48 @@ +// Copyright (c) 2021 The Jaeger Authors. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package disabled + +import ( + "flag" + + "github.com/spf13/viper" + "go.uber.org/zap" + + "github.com/jaegertracing/jaeger/storage/metricsstore" +) + +// Factory implements storage.Factory that returns a Disabled metrics reader. +type Factory struct{} + +// NewFactory creates a new Factory. +func NewFactory() *Factory { + return &Factory{} +} + +// AddFlags implements plugin.Configurable. +func (f *Factory) AddFlags(_ *flag.FlagSet) {} + +// InitFromViper implements plugin.Configurable. +func (f *Factory) InitFromViper(_ *viper.Viper) {} + +// Initialize implements storage.MetricsFactory. +func (f *Factory) Initialize(_ *zap.Logger) error { + return nil +} + +// CreateMetricsReader implements storage.MetricsFactory. +func (f *Factory) CreateMetricsReader() (metricsstore.Reader, error) { + return NewMetricsReader() +} diff --git a/plugin/metrics/disabled/factory_test.go b/plugin/metrics/disabled/factory_test.go new file mode 100644 index 00000000000..4f1d1444e7b --- /dev/null +++ b/plugin/metrics/disabled/factory_test.go @@ -0,0 +1,42 @@ +// Copyright (c) 2021 The Jaeger Authors. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package disabled + +import ( + "testing" + + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" + "go.uber.org/zap" + + "github.com/jaegertracing/jaeger/storage" +) + +var _ storage.MetricsFactory = new(Factory) + +func TestPrometheusFactory(t *testing.T) { + f := NewFactory() + assert.NoError(t, f.Initialize(zap.NewNop())) + + err := f.Initialize(nil) + require.NoError(t, err) + + f.AddFlags(nil) + f.InitFromViper(nil) + + reader, err := f.CreateMetricsReader() + assert.NoError(t, err) + assert.NotNil(t, reader) +} diff --git a/plugin/metrics/disabled/reader.go b/plugin/metrics/disabled/reader.go new file mode 100644 index 00000000000..8f8a3d9860e --- /dev/null +++ b/plugin/metrics/disabled/reader.go @@ -0,0 +1,64 @@ +// Copyright (c) 2021 The Jaeger Authors. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package disabled + +import ( + "context" + "time" + + "github.com/jaegertracing/jaeger/proto-gen/api_v2/metrics" + "github.com/jaegertracing/jaeger/storage/metricsstore" +) + +type ( + // MetricsReader represents a "disabled" metricsstore.Reader implementation where + // the METRICS_STORAGE_TYPE has not been set. + MetricsReader struct{} + + // errMetricsQueryDisabled is the error returned by disabledMetricsQueryService. + errMetricsQueryDisabled struct{} +) + +// ErrDisabled is the error returned by a "disabled" MetricsQueryService on all of its endpoints. +var ErrDisabled = &errMetricsQueryDisabled{} + +func (m *errMetricsQueryDisabled) Error() string { + return "metrics querying is currently disabled" +} + +// NewMetricsReader returns a new Disabled MetricsReader. +func NewMetricsReader() (*MetricsReader, error) { + return &MetricsReader{}, nil +} + +// GetLatencies gets the latency metrics for the given set of latency query parameters. +func (m *MetricsReader) GetLatencies(ctx context.Context, requestParams *metricsstore.LatenciesQueryParameters) (*metrics.MetricFamily, error) { + return nil, ErrDisabled +} + +// GetCallRates gets the call rate metrics for the given set of call rate query parameters. +func (m *MetricsReader) GetCallRates(ctx context.Context, requestParams *metricsstore.CallRateQueryParameters) (*metrics.MetricFamily, error) { + return nil, ErrDisabled +} + +// GetErrorRates gets the error rate metrics for the given set of error rate query parameters. +func (m *MetricsReader) GetErrorRates(ctx context.Context, requestParams *metricsstore.ErrorRateQueryParameters) (*metrics.MetricFamily, error) { + return nil, ErrDisabled +} + +// GetMinStepDuration gets the minimum step duration (the smallest possible duration between two data points in a time series) supported. +func (m *MetricsReader) GetMinStepDuration(_ context.Context, _ *metricsstore.MinStepDurationQueryParameters) (time.Duration, error) { + return 0, ErrDisabled +} diff --git a/plugin/metrics/disabled/reader_test.go b/plugin/metrics/disabled/reader_test.go new file mode 100644 index 00000000000..b8bc873a1a2 --- /dev/null +++ b/plugin/metrics/disabled/reader_test.go @@ -0,0 +1,74 @@ +// Copyright (c) 2021 The Jaeger Authors. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package disabled + +import ( + "context" + "errors" + "testing" + + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" + + "github.com/jaegertracing/jaeger/storage/metricsstore" +) + +func TestGetLatencies(t *testing.T) { + reader, err := NewMetricsReader() + require.NoError(t, err) + require.NotNil(t, reader) + + qParams := &metricsstore.LatenciesQueryParameters{} + r, err := reader.GetLatencies(context.Background(), qParams) + assert.Zero(t, r) + assert.True(t, errors.Is(err, ErrDisabled)) + assert.EqualError(t, err, ErrDisabled.Error()) +} + +func TestGetCallRates(t *testing.T) { + reader, err := NewMetricsReader() + require.NoError(t, err) + require.NotNil(t, reader) + + qParams := &metricsstore.CallRateQueryParameters{} + r, err := reader.GetCallRates(context.Background(), qParams) + assert.Zero(t, r) + assert.True(t, errors.Is(err, ErrDisabled)) + assert.EqualError(t, err, ErrDisabled.Error()) +} + +func TestGetErrorRates(t *testing.T) { + reader, err := NewMetricsReader() + require.NoError(t, err) + require.NotNil(t, reader) + + qParams := &metricsstore.ErrorRateQueryParameters{} + r, err := reader.GetErrorRates(context.Background(), qParams) + assert.Zero(t, r) + assert.True(t, errors.Is(err, ErrDisabled)) + assert.EqualError(t, err, ErrDisabled.Error()) +} + +func TestGetMinStepDurations(t *testing.T) { + reader, err := NewMetricsReader() + require.NoError(t, err) + require.NotNil(t, reader) + + qParams := &metricsstore.MinStepDurationQueryParameters{} + r, err := reader.GetMinStepDuration(context.Background(), qParams) + assert.Zero(t, r) + assert.True(t, errors.Is(err, ErrDisabled)) + assert.EqualError(t, err, ErrDisabled.Error()) +} diff --git a/plugin/metrics/factory.go b/plugin/metrics/factory.go index 73153e78732..433be025b8f 100644 --- a/plugin/metrics/factory.go +++ b/plugin/metrics/factory.go @@ -22,12 +22,16 @@ import ( "go.uber.org/zap" "github.com/jaegertracing/jaeger/plugin" + "github.com/jaegertracing/jaeger/plugin/metrics/disabled" "github.com/jaegertracing/jaeger/plugin/metrics/prometheus" "github.com/jaegertracing/jaeger/storage" "github.com/jaegertracing/jaeger/storage/metricsstore" ) const ( + // disabledStorageType is the storage type used when METRICS_STORAGE_TYPE is unset. + disabledStorageType = "" + prometheusStorageType = "prometheus" ) @@ -61,6 +65,8 @@ func (f *Factory) getFactoryOfType(factoryType string) (storage.MetricsFactory, switch factoryType { case prometheusStorageType: return prometheus.NewFactory(), nil + case disabledStorageType: + return disabled.NewFactory(), nil } return nil, fmt.Errorf("unknown metrics type %q. Valid types are %v", factoryType, AllStorageTypes) } diff --git a/plugin/metrics/factory_test.go b/plugin/metrics/factory_test.go index 49c770e8320..fe8c4e1592b 100644 --- a/plugin/metrics/factory_test.go +++ b/plugin/metrics/factory_test.go @@ -23,20 +23,21 @@ import ( "github.com/stretchr/testify/require" "go.uber.org/zap" + "github.com/jaegertracing/jaeger/plugin/metrics/disabled" "github.com/jaegertracing/jaeger/storage" "github.com/jaegertracing/jaeger/storage/mocks" ) var _ storage.MetricsFactory = new(Factory) -func defaultCfg() FactoryConfig { +func withConfig(storageType string) FactoryConfig { return FactoryConfig{ - MetricsStorageType: prometheusStorageType, + MetricsStorageType: storageType, } } func TestNewFactory(t *testing.T) { - f, err := NewFactory(defaultCfg()) + f, err := NewFactory(withConfig(prometheusStorageType)) require.NoError(t, err) assert.NotEmpty(t, f.factories) assert.NotEmpty(t, f.factories[prometheusStorageType]) @@ -44,14 +45,22 @@ func TestNewFactory(t *testing.T) { } func TestUnsupportedMetricsStorageType(t *testing.T) { - f, err := NewFactory(FactoryConfig{MetricsStorageType: "foo"}) + f, err := NewFactory(withConfig("foo")) require.Error(t, err) assert.Nil(t, f) assert.EqualError(t, err, `unknown metrics type "foo". Valid types are [prometheus]`) } +func TestDisabledMetricsStorageType(t *testing.T) { + f, err := NewFactory(withConfig(disabledStorageType)) + require.NoError(t, err) + assert.NotEmpty(t, f.factories) + assert.Equal(t, &disabled.Factory{}, f.factories[disabledStorageType]) + assert.Equal(t, disabledStorageType, f.MetricsStorageType) +} + func TestCreateMetricsReader(t *testing.T) { - f, err := NewFactory(defaultCfg()) + f, err := NewFactory(withConfig(prometheusStorageType)) require.NoError(t, err) require.NotNil(t, f) @@ -89,7 +98,7 @@ func TestConfigurable(t *testing.T) { clearEnv(t) defer clearEnv(t) - f, err := NewFactory(defaultCfg()) + f, err := NewFactory(withConfig(prometheusStorageType)) require.NoError(t, err) assert.NotEmpty(t, f.factories) assert.NotEmpty(t, f.factories[prometheusStorageType])