diff --git a/cmd/optimizely/main.go b/cmd/optimizely/main.go index 7d5958e5..1e64396d 100644 --- a/cmd/optimizely/main.go +++ b/cmd/optimizely/main.go @@ -18,6 +18,8 @@ package main import ( "bytes" "context" + "errors" + "fmt" "os" "os/signal" "runtime" @@ -42,6 +44,15 @@ import ( // Initiate the loading of the userprofileservice plugins _ "github.com/optimizely/agent/plugins/userprofileservice/all" "github.com/optimizely/go-sdk/pkg/logging" + "go.opentelemetry.io/otel" + "go.opentelemetry.io/otel/exporters/otlp/otlptrace" + "go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc" + "go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracehttp" + "go.opentelemetry.io/otel/exporters/stdout/stdouttrace" + "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" ) // Version holds the admin version @@ -112,6 +123,95 @@ func initLogging(conf config.LogConfig) { } } +func getStdOutTraceProvider(conf config.OTELTracingConfig) (*sdktrace.TracerProvider, error) { + f, err := os.Create(conf.Services.StdOut.Filename) + if err != nil { + return nil, fmt.Errorf("failed to create the trace file, error: %s", err.Error()) + } + + exp, err := stdouttrace.New( + stdouttrace.WithPrettyPrint(), + stdouttrace.WithWriter(f), + ) + if err != nil { + return nil, fmt.Errorf("failed to create the collector exporter, error: %s", err.Error()) + } + + res, err := resource.New( + context.Background(), + resource.WithAttributes( + semconv.ServiceNameKey.String(conf.ServiceName), + semconv.DeploymentEnvironmentKey.String(conf.Env), + ), + ) + if err != nil { + return nil, fmt.Errorf("failed to create the otel resource, error: %s", err.Error()) + } + + return sdktrace.NewTracerProvider( + sdktrace.WithBatcher(exp), + sdktrace.WithResource(res), + ), nil +} + +func getOTELTraceClient(conf config.OTELTracingConfig) (otlptrace.Client, error) { + switch conf.Services.Remote.Protocol { + case config.TracingRemoteProtocolHTTP: + return otlptracehttp.NewClient( + otlptracehttp.WithInsecure(), + otlptracehttp.WithEndpoint(conf.Services.Remote.Endpoint), + ), nil + case config.TracingRemoteProtocolGRPC: + return otlptracegrpc.NewClient( + otlptracegrpc.WithInsecure(), + otlptracegrpc.WithEndpoint(conf.Services.Remote.Endpoint), + ), nil + default: + return nil, errors.New("unknown remote tracing protocal") + } +} + +func getRemoteTraceProvider(conf config.OTELTracingConfig) (*sdktrace.TracerProvider, error) { + res, err := resource.New( + context.Background(), + resource.WithAttributes( + semconv.ServiceNameKey.String(conf.ServiceName), + semconv.DeploymentEnvironmentKey.String(conf.Env), + ), + ) + if err != nil { + return nil, fmt.Errorf("failed to create the otel resource, error: %s", err.Error()) + } + + traceClient, err := getOTELTraceClient(conf) + if err != nil { + return nil, fmt.Errorf("failed to create the remote trace client, error: %s", err.Error()) + } + + traceExporter, err := otlptrace.New(context.Background(), traceClient) + if err != nil { + return nil, fmt.Errorf("failed to create the remote trace exporter, error: %s", err.Error()) + } + + bsp := sdktrace.NewBatchSpanProcessor(traceExporter) + return sdktrace.NewTracerProvider( + sdktrace.WithSampler(sdktrace.ParentBased(sdktrace.TraceIDRatioBased(conf.Services.Remote.SampleRate))), + sdktrace.WithResource(res), + sdktrace.WithSpanProcessor(bsp), + ), nil +} + +func initTracing(conf config.OTELTracingConfig) (*sdktrace.TracerProvider, error) { + switch conf.Default { + case config.TracingServiceTypeRemote: + return getRemoteTraceProvider(conf) + case config.TracingServiceTypeStdOut: + return getStdOutTraceProvider(conf) + default: + return nil, errors.New("unknown tracing service type") + } +} + func setRuntimeEnvironment(conf config.RuntimeConfig) { if conf.BlockProfileRate != 0 { log.Warn().Msgf("Setting non-zero blockProfileRate is NOT recommended for production") @@ -133,11 +233,29 @@ func main() { conf := loadConfig(v) initLogging(conf.Log) + if conf.Tracing.Enabled { + tp, err := initTracing(conf.Tracing.OpenTelemetry) + if err != nil { + log.Panic().Err(err).Msg("Unable to initialize tracing") + } + defer func() { + if err := tp.Shutdown(context.Background()); err != nil { + log.Error().Err(err).Msg("Failed to shutdown tracing") + } + }() + otel.SetTracerProvider(tp) + otel.SetTextMapPropagator(propagation.TraceContext{}) + log.Info().Msg(fmt.Sprintf("Tracing enabled with service %q", conf.Tracing.OpenTelemetry.Default)) + } else { + log.Info().Msg("Tracing disabled") + } + conf.LogConfigWarnings() setRuntimeEnvironment(conf.Runtime) - agentMetricsRegistry := metrics.NewRegistry() + // Set metrics type to be used + agentMetricsRegistry := metrics.NewRegistry(conf.Admin.MetricsType) sdkMetricsRegistry := optimizely.NewRegistry(agentMetricsRegistry) ctx, cancel := context.WithCancel(context.Background()) // Create default service context diff --git a/cmd/optimizely/main_test.go b/cmd/optimizely/main_test.go index 6d7f9828..d23e989a 100644 --- a/cmd/optimizely/main_test.go +++ b/cmd/optimizely/main_test.go @@ -98,6 +98,7 @@ func assertLog(t *testing.T, actual config.LogConfig) { func assertAdmin(t *testing.T, actual config.AdminConfig) { assert.Equal(t, "3002", actual.Port) + assert.Equal(t, "prometheus", actual.MetricsType) } func assertAdminAuth(t *testing.T, actual config.ServiceAuthConfig) { @@ -232,6 +233,7 @@ func TestViperProps(t *testing.T) { v.Set("log.level", "debug") v.Set("admin.port", "3002") + v.Set("admin.metricsType", "prometheus") v.Set("admin.auth.ttl", "30m") v.Set("admin.auth.hmacSecrets", "efgh,ijkl") v.Set("admin.auth.jwksURL", "admin_jwks_url") @@ -319,6 +321,7 @@ func TestViperEnv(t *testing.T) { _ = os.Setenv("OPTIMIZELY_LOG_LEVEL", "debug") _ = os.Setenv("OPTIMIZELY_ADMIN_PORT", "3002") + _ = os.Setenv("OPTIMIZELY_ADMIN_METRICSTYPE", "prometheus") _ = os.Setenv("OPTIMIZELY_API_MAXCONNS", "100") _ = os.Setenv("OPTIMIZELY_API_PORT", "3000") @@ -359,3 +362,92 @@ func TestLoggingWithIncludeSdkKey(t *testing.T) { }) assert.False(t, optimizely.ShouldIncludeSDKKey) } + +func Test_initTracing(t *testing.T) { + type args struct { + conf config.OTELTracingConfig + } + tests := []struct { + name string + args args + wantErr bool + }{ + { + name: "should return error when exporter type is not supported", + args: args{ + conf: config.OTELTracingConfig{ + Default: "unsupported", + }, + }, + wantErr: true, + }, + { + name: "should return no error stdout tracing exporter", + args: args{ + conf: config.OTELTracingConfig{ + Default: "stdout", + Services: config.TracingServiceConfig{ + StdOut: config.TracingStdOutConfig{ + Filename: "trace.out", + }, + }, + }, + }, + wantErr: false, + }, + { + name: "should return no error for remote tracing exporter with http protocal", + args: args{ + conf: config.OTELTracingConfig{ + Default: "remote", + Services: config.TracingServiceConfig{ + Remote: config.TracingRemoteConfig{ + Endpoint: "localhost:1234", + Protocol: "http", + }, + }, + }, + }, + wantErr: false, + }, + { + name: "should return no error for remote tracing exporter with grpc protocal", + args: args{ + conf: config.OTELTracingConfig{ + Default: "remote", + Services: config.TracingServiceConfig{ + Remote: config.TracingRemoteConfig{ + Endpoint: "localhost:1234", + Protocol: "grpc", + }, + }, + }, + }, + wantErr: false, + }, + { + name: "should return no error for remote tracing exporter with invalid protocal", + args: args{ + conf: config.OTELTracingConfig{ + Default: "remote", + Services: config.TracingServiceConfig{ + Remote: config.TracingRemoteConfig{ + Endpoint: "localhost:1234", + Protocol: "udp/invalid", + }, + }, + }, + }, + wantErr: true, + }, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + _, err := initTracing(tt.args.conf) + if (err != nil) != tt.wantErr { + t.Errorf("initTracing() error = %v, wantErr %v", err, tt.wantErr) + return + } + }) + } +} diff --git a/cmd/optimizely/testdata/default.yaml b/cmd/optimizely/testdata/default.yaml index 8abb6ad6..972189de 100644 --- a/cmd/optimizely/testdata/default.yaml +++ b/cmd/optimizely/testdata/default.yaml @@ -52,6 +52,7 @@ client: admin: port: "3002" + metricsType: "prometheus" auth: ttl: 30m hmacSecrets: diff --git a/config.yaml b/config.yaml index 6943496f..b8b85779 100644 --- a/config.yaml +++ b/config.yaml @@ -26,6 +26,44 @@ log: ## to set whether or not the SDK key is included in the logging output. includeSdkKey: true +## +## tracing: tracing configuration +## +## For distributed tracing, trace context should be sent on "traceparent" header +## The value set in HTTP Header must be a hex compliant with the W3C trace-context specification. +## See more at https://www.w3.org/TR/trace-context/#trace-id +tracing: + ## bydefault tracing is disabled + ## to enable tracing set enabled to true + enabled: false + # opentelemetry tracing configuration + opentelemetry: + ## bydefault stdout exporter is enabled + ## to enable remote exporter set default as "remote" + default: "stdout" + ## tracing service name + serviceName: "optimizely-agent" + ## tracing environment name + ## example: for production environment env can be set as "prod" + env: "dev" + ## tracing service configuration + services: + ## stdout exporter configuration + stdout: + ## for stdout tracing data is saved in the specified file + filename: "trace.out" + ## remote exporter configuration + remote: + ## remote collector endpoint + endpoint: "localhost:4317" + ## supported protocols are "http" and "grpc" + protocol: "grpc" + ## "sampleRate" refers to the rate at which traces are collected and recorded. + ## sampleRate >= 1 will always sample. + ## sampleRate < 0 are treated as zero i.e. never sample. + sampleRate: 1.0 + + ## ## http server configuration ## @@ -95,6 +133,10 @@ api: admin: ## http listener port port: "8088" + ## metrics package to use + ## supported packages are expvar and prometheus + ## default is expvar + metricsType: "" ## ## webhook service receives update notifications to your Optimizely project. Receipt of the webhook will ## trigger an immediate download of the datafile from the CDN diff --git a/config/config.go b/config/config.go index ae2a49df..4155b3ef 100644 --- a/config/config.go +++ b/config/config.go @@ -39,7 +39,8 @@ func NewDefaultConfig() *AgentConfig { JwksURL: "", JwksUpdateInterval: 0, }, - Port: "8088", + Port: "8088", + MetricsType: "expvar", }, API: APIConfig{ Auth: ServiceAuthConfig{ @@ -70,6 +71,9 @@ func NewDefaultConfig() *AgentConfig { IncludeSDKKey: true, Level: "info", }, + Tracing: TracingConfig{ + Enabled: false, + }, Client: ClientConfig{ PollingInterval: 1 * time.Minute, BatchSize: 10, @@ -123,6 +127,7 @@ type AgentConfig struct { Admin AdminConfig `json:"admin"` API APIConfig `json:"api"` Log LogConfig `json:"log"` + Tracing TracingConfig `json:"tracing"` Client ClientConfig `json:"client"` Runtime RuntimeConfig `json:"runtime"` Server ServerConfig `json:"server"` @@ -172,6 +177,47 @@ type LogConfig struct { Level string `json:"level"` } +type TracingConfig struct { + Enabled bool `json:"enabled"` + OpenTelemetry OTELTracingConfig `json:"opentelemetry"` +} + +type TracingServiceType string + +const ( + TracingServiceTypeStdOut TracingServiceType = "stdout" + TracingServiceTypeRemote TracingServiceType = "remote" +) + +type TracingRemoteProtocol string + +const ( + TracingRemoteProtocolGRPC TracingRemoteProtocol = "grpc" + TracingRemoteProtocolHTTP TracingRemoteProtocol = "http" +) + +type OTELTracingConfig struct { + Default TracingServiceType `json:"default"` + ServiceName string `json:"serviceName"` + Env string `json:"env"` + Services TracingServiceConfig `json:"services"` +} + +type TracingServiceConfig struct { + StdOut TracingStdOutConfig `json:"stdout"` + Remote TracingRemoteConfig `json:"remote"` +} + +type TracingStdOutConfig struct { + Filename string `json:"filename"` +} + +type TracingRemoteConfig struct { + Endpoint string `json:"endpoint"` + Protocol TracingRemoteProtocol `json:"protocol"` + SampleRate float64 `json:"sampleRate"` +} + // PluginConfigs defines the generic mapping of middleware plugins type PluginConfigs map[string]interface{} @@ -227,8 +273,9 @@ type CORSConfig struct { // AdminConfig holds the configuration for the admin web interface type AdminConfig struct { - Auth ServiceAuthConfig `json:"-"` - Port string `json:"port"` + Auth ServiceAuthConfig `json:"-"` + Port string `json:"port"` + MetricsType string `json:"metricsType"` } // WebhookConfig holds configuration for Optimizely Webhooks diff --git a/config/config_test.go b/config/config_test.go index 4cc52bd2..464991d5 100644 --- a/config/config_test.go +++ b/config/config_test.go @@ -50,6 +50,7 @@ func TestDefaultConfig(t *testing.T) { assert.Equal(t, "info", conf.Log.Level) assert.Equal(t, "8088", conf.Admin.Port) + assert.Equal(t, "expvar", conf.Admin.MetricsType) assert.Equal(t, make([]OAuthClientCredentials, 0), conf.Admin.Auth.Clients) assert.Equal(t, make([]string, 0), conf.Admin.Auth.HMACSecrets) assert.Equal(t, time.Duration(0), conf.Admin.Auth.TTL) diff --git a/go.mod b/go.mod index 57fff9f0..1d199067 100644 --- a/go.mod +++ b/go.mod @@ -14,15 +14,43 @@ require ( github.com/lestrrat-go/jwx v0.9.0 github.com/optimizely/go-sdk v1.8.4-0.20230308225321-5a0d450ca834 github.com/orcaman/concurrent-map v1.0.0 + github.com/prometheus/client_golang v1.11.0 github.com/rakyll/statik v0.1.7 github.com/rs/zerolog v1.29.0 github.com/spf13/viper v1.15.0 - github.com/stretchr/testify v1.8.2 - golang.org/x/crypto v0.6.0 - golang.org/x/sync v0.1.0 + github.com/stretchr/testify v1.8.4 + go.opentelemetry.io/otel v1.19.0 + go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.19.0 + go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc v1.19.0 + go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracehttp v1.19.0 + go.opentelemetry.io/otel/exporters/stdout/stdouttrace v1.19.0 + go.opentelemetry.io/otel/sdk v1.19.0 + go.opentelemetry.io/otel/trace v1.19.0 + golang.org/x/crypto v0.11.0 + golang.org/x/sync v0.3.0 gopkg.in/yaml.v2 v2.4.0 ) +require ( + github.com/beorn7/perks v1.0.1 // indirect + github.com/cenkalti/backoff/v4 v4.2.1 // indirect + github.com/go-logr/logr v1.2.4 // indirect + github.com/go-logr/stdr v1.2.2 // indirect + github.com/golang/protobuf v1.5.3 // indirect + github.com/grpc-ecosystem/grpc-gateway/v2 v2.16.0 // indirect + github.com/matttproud/golang_protobuf_extensions v1.0.1 // indirect + github.com/prometheus/client_model v0.2.0 // indirect + github.com/prometheus/common v0.30.0 // indirect + github.com/prometheus/procfs v0.7.3 // indirect + go.opentelemetry.io/otel/metric v1.19.0 // indirect + go.opentelemetry.io/proto/otlp v1.0.0 // indirect + golang.org/x/net v0.12.0 // indirect + google.golang.org/genproto/googleapis/api v0.0.0-20230711160842-782d3b101e98 // indirect + google.golang.org/genproto/googleapis/rpc v0.0.0-20230711160842-782d3b101e98 // indirect + google.golang.org/grpc v1.58.2 // indirect + google.golang.org/protobuf v1.31.0 // indirect +) + require ( github.com/VividCortex/gohistogram v1.0.0 // indirect github.com/ajg/form v1.5.1 // indirect @@ -51,8 +79,8 @@ require ( github.com/stretchr/objx v0.5.0 // indirect github.com/subosito/gotenv v1.4.2 // indirect github.com/twmb/murmur3 v1.1.6 // indirect - golang.org/x/sys v0.5.0 // indirect - golang.org/x/text v0.7.0 // indirect + golang.org/x/sys v0.12.0 // indirect + golang.org/x/text v0.11.0 // indirect gopkg.in/ini.v1 v1.67.0 // indirect gopkg.in/yaml.v3 v3.0.1 // indirect ) diff --git a/go.sum b/go.sum index 4bc5c6a4..b1e14991 100644 --- a/go.sum +++ b/go.sum @@ -42,7 +42,19 @@ github.com/VividCortex/gohistogram v1.0.0 h1:6+hBz+qvs0JOrrNhhmR7lFxo5sINxBCGXrd github.com/VividCortex/gohistogram v1.0.0/go.mod h1:Pf5mBqqDxYaXu3hDrrU+w6nw50o/4+TcAqDqk/vUH7g= github.com/ajg/form v1.5.1 h1:t9c7v8JUKu/XxOGBU0yjNpaMloxGEJhUkqFRq0ibGeU= github.com/ajg/form v1.5.1/go.mod h1:uL1WgH+h2mgNtvBq0339dVnzXdBETtL2LeUXaIv25UY= +github.com/alecthomas/template v0.0.0-20160405071501-a0175ee3bccc/go.mod h1:LOuyumcjzFXgccqObfd/Ljyb9UuFJ6TxHnclSeseNhc= +github.com/alecthomas/template v0.0.0-20190718012654-fb15b899a751/go.mod h1:LOuyumcjzFXgccqObfd/Ljyb9UuFJ6TxHnclSeseNhc= +github.com/alecthomas/units v0.0.0-20151022065526-2efee857e7cf/go.mod h1:ybxpYRFXyAe+OPACYpWeL0wqObRcbAqCMya13uyzqw0= +github.com/alecthomas/units v0.0.0-20190717042225-c3de453c63f4/go.mod h1:ybxpYRFXyAe+OPACYpWeL0wqObRcbAqCMya13uyzqw0= +github.com/alecthomas/units v0.0.0-20190924025748-f65c72e2690d/go.mod h1:rBZYJk541a8SKzHPHnH3zbiI+7dagKZ0cgpgrD7Fyho= +github.com/beorn7/perks v0.0.0-20180321164747-3a771d992973/go.mod h1:Dwedo/Wpr24TaqPxmxbtue+5NUziq4I4S80YR8gNf3Q= +github.com/beorn7/perks v1.0.0/go.mod h1:KWe93zE9D1o94FZ5RNwFwVgaQK1VOXiVxmqh+CedLV8= +github.com/beorn7/perks v1.0.1 h1:VlbKKnNfV8bJzeqoa4cOKqO6bYr3WgKZxO8Z16+hsOM= +github.com/beorn7/perks v1.0.1/go.mod h1:G2ZrVWU2WbWT9wwq4/hrbKbnv/1ERSJQ0ibhJ6rlkpw= +github.com/cenkalti/backoff/v4 v4.2.1 h1:y4OZtCnogmCPw98Zjyt5a6+QwPLGkiQsYW5oUqylYbM= +github.com/cenkalti/backoff/v4 v4.2.1/go.mod h1:Y3VNntkOUPxTVeUxJ/G5vcM//AlwfmyYozVcomhLiZE= github.com/census-instrumentation/opencensus-proto v0.2.1/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU= +github.com/cespare/xxhash/v2 v2.1.1/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs= github.com/cespare/xxhash/v2 v2.2.0 h1:DC2CZ1Ep5Y4k3ZQ899DldepgrayRUGE6BBZ/cd9Cj44= github.com/cespare/xxhash/v2 v2.2.0/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs= github.com/chzyer/logex v1.1.10/go.mod h1:+Ywpsq7O8HXn0nuIou7OrIPyXbp3wmkHB+jjWRnGsAI= @@ -81,14 +93,28 @@ github.com/go-chi/render v1.0.2/go.mod h1:/gr3hVkmYR0YlEy3LxCuVRFzEu9Ruok+gFqbIo github.com/go-gl/glfw v0.0.0-20190409004039-e6da0acd62b1/go.mod h1:vR7hzQXu2zJy9AVAgeJqvqgH9Q5CA+iKCZ2gyEVpxRU= github.com/go-gl/glfw/v3.3/glfw v0.0.0-20191125211704-12ad95a8df72/go.mod h1:tQ2UAYgL5IevRw8kRxooKSPJfGvJ9fJQFa0TUsXzTg8= github.com/go-gl/glfw/v3.3/glfw v0.0.0-20200222043503-6f7a984d4dc4/go.mod h1:tQ2UAYgL5IevRw8kRxooKSPJfGvJ9fJQFa0TUsXzTg8= +github.com/go-kit/kit v0.8.0/go.mod h1:xBxKIO96dXMWWy0MnWVtmwkA9/13aqxPnvrjFYMA2as= +github.com/go-kit/kit v0.9.0/go.mod h1:xBxKIO96dXMWWy0MnWVtmwkA9/13aqxPnvrjFYMA2as= github.com/go-kit/kit v0.12.0 h1:e4o3o3IsBfAKQh5Qbbiqyfu97Ku7jrO/JbohvztANh4= github.com/go-kit/kit v0.12.0/go.mod h1:lHd+EkCZPIwYItmGDDRdhinkzX2A1sj+M9biaEaizzs= +github.com/go-kit/log v0.1.0/go.mod h1:zbhenjAZHb184qTLMA9ZjW7ThYL0H2mk7Q6pNt4vbaY= +github.com/go-logfmt/logfmt v0.3.0/go.mod h1:Qt1PoO58o5twSAckw1HlFXLmHsOX5/0LbT9GBnD5lWE= +github.com/go-logfmt/logfmt v0.4.0/go.mod h1:3RMwSq7FuexP4Kalkev3ejPJsZTpXXBr9+V4qmtdjCk= +github.com/go-logfmt/logfmt v0.5.0/go.mod h1:wCYkCAKZfumFQihp8CzCvQ3paCTfi41vtzG1KdI/P7A= +github.com/go-logr/logr v1.2.2/go.mod h1:jdQByPbusPIv2/zmleS9BjJVeZ6kBagPoEUsqbVz/1A= +github.com/go-logr/logr v1.2.4 h1:g01GSCwiDw2xSZfjJ2/T9M+S6pFdcNtFYsp+Y43HYDQ= +github.com/go-logr/logr v1.2.4/go.mod h1:jdQByPbusPIv2/zmleS9BjJVeZ6kBagPoEUsqbVz/1A= +github.com/go-logr/stdr v1.2.2 h1:hSWxHoqTgW2S2qGc0LTAI563KZ5YKYRhT3MFKZMbjag= +github.com/go-logr/stdr v1.2.2/go.mod h1:mMo/vtBO5dYbehREoey6XUKy/eSumjCCveDpRre4VKE= github.com/go-redis/redis/v8 v8.11.5 h1:AcZZR7igkdvfVmQTPnu9WE37LRrO/YrBH5zWyjDC0oI= github.com/go-redis/redis/v8 v8.11.5/go.mod h1:gREzHqY1hg6oD9ngVRbLStwAWKhA0FEgq8Jd4h5lpwo= +github.com/go-stack/stack v1.8.0/go.mod h1:v0f6uXyyMGvRgIKkXu+yp6POWl0qKG85gN/melR3HDY= github.com/godbus/dbus/v5 v5.0.4/go.mod h1:xhWf0FNVPg57R7Z0UbKHbJfkEywrmjJnf7w5xrFpKfA= +github.com/gogo/protobuf v1.1.1/go.mod h1:r8qH/GZQm5c6nD/R0oafs1akxWv10x8SbQlK7atdtwQ= github.com/golang-jwt/jwt/v4 v4.5.0 h1:7cYmW1XlMY7h7ii7UhUyChSgS5wUJEnm9uZVTGqOWzg= github.com/golang-jwt/jwt/v4 v4.5.0/go.mod h1:m21LjoU+eqJr34lmDMbreY2eSTRJ1cv77w39/MY0Ch0= github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q= +github.com/golang/glog v1.1.0 h1:/d3pCKDPWNnvIWe0vVUpNP32qc8U3PDVxySP/y360qE= github.com/golang/groupcache v0.0.0-20190702054246-869f871628b6/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= github.com/golang/groupcache v0.0.0-20191227052852-215e87163ea7/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= github.com/golang/groupcache v0.0.0-20200121045136-8c9f03a8e57e/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= @@ -113,6 +139,9 @@ github.com/golang/protobuf v1.4.0/go.mod h1:jodUvKwWbYaEsadDk5Fwe5c77LiNKVO9IDvq github.com/golang/protobuf v1.4.1/go.mod h1:U8fpvMrcmy5pZrNK1lt4xCsGvpyWQ/VVv6QDs8UjoX8= github.com/golang/protobuf v1.4.2/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw735rRwI= github.com/golang/protobuf v1.4.3/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw735rRwI= +github.com/golang/protobuf v1.5.0/go.mod h1:FsONVRAS9T7sI+LIUmWTfcYkHO4aIWwzhcaSAoJOfIk= +github.com/golang/protobuf v1.5.3 h1:KhyjKVUg7Usr/dYsdSqoFveMYd5ko72D+zANwlG1mmg= +github.com/golang/protobuf v1.5.3/go.mod h1:XVQd3VNwM+JqD3oG2Ue2ip4fOMUkwXdXDdiuN0vRsmY= github.com/google/btree v0.0.0-20180813153112-4030bb1f1f0c/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ= github.com/google/btree v1.0.0/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ= github.com/google/go-cmp v0.2.0/go.mod h1:oXzfMopK8JAjlY9xF4vHSVASa0yLyX7SntLO5aqRK0M= @@ -124,6 +153,7 @@ github.com/google/go-cmp v0.5.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/ github.com/google/go-cmp v0.5.1/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= github.com/google/go-cmp v0.5.2/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= github.com/google/go-cmp v0.5.4/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= +github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= github.com/google/go-cmp v0.5.9 h1:O2Tfq5qg4qc4AmwVlvv0oLiVAGB7enBSJ2x2DqQFi38= github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg= github.com/google/martian v2.1.0+incompatible/go.mod h1:9I4somxYTbIHy5NJKHRl3wXiIaQGbYVAs8BPL6v8lEs= @@ -147,6 +177,8 @@ github.com/google/uuid v1.3.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+ github.com/googleapis/gax-go/v2 v2.0.4/go.mod h1:0Wqv26UfaUD9n4G6kQubkQ+KchISgw+vpHVxEJEs9eg= github.com/googleapis/gax-go/v2 v2.0.5/go.mod h1:DWXyrwAJ9X0FpwwEdw+IPEYBICEFu5mhpdKc/us6bOk= github.com/googleapis/google-cloud-go-testing v0.0.0-20200911160855-bcd43fbb19e8/go.mod h1:dvDLG8qkwmyD9a/MJJN3XJcT3xFxOKAvTZGvuZmac9g= +github.com/grpc-ecosystem/grpc-gateway/v2 v2.16.0 h1:YBftPWNWd4WwGqtY2yeZL2ef8rHAxPBD8KFhJpmcqms= +github.com/grpc-ecosystem/grpc-gateway/v2 v2.16.0/go.mod h1:YN5jB8ie0yfIUg6VvR9Kz84aCaG7AsGZnLjhHbUqwPg= github.com/hashicorp/errwrap v1.0.0/go.mod h1:YH+1FKiLXxHSkmPseP+kNlulaMuP3n2brvKWEqk/Jc4= github.com/hashicorp/errwrap v1.1.0 h1:OxrOeh75EUXMY8TBjag2fzXGZ40LB6IKw45YeGUDY2I= github.com/hashicorp/errwrap v1.1.0/go.mod h1:YH+1FKiLXxHSkmPseP+kNlulaMuP3n2brvKWEqk/Jc4= @@ -159,14 +191,23 @@ github.com/hashicorp/hcl v1.0.0 h1:0Anlzjpi4vEasTeNFn2mLJgTSwt0+6sfsiTG8qcWGx4= github.com/hashicorp/hcl v1.0.0/go.mod h1:E5yfLk+7swimpb2L/Alb/PJmXilQ/rhwaUYs4T20WEQ= github.com/ianlancetaylor/demangle v0.0.0-20181102032728-5e5cf60278f6/go.mod h1:aSSvb/t6k1mPoxDqO4vJh6VOCGPwU4O0C2/Eqndh1Sc= github.com/ianlancetaylor/demangle v0.0.0-20200824232613-28f6c0f3b639/go.mod h1:aSSvb/t6k1mPoxDqO4vJh6VOCGPwU4O0C2/Eqndh1Sc= +github.com/jpillora/backoff v1.0.0/go.mod h1:J/6gKK9jxlEcS3zixgDgUAsiuZ7yrSoa/FX5e0EB2j4= +github.com/json-iterator/go v1.1.6/go.mod h1:+SdeFBvtyEkXs7REEP0seUULqWtbJapLOCVDaaPEHmU= +github.com/json-iterator/go v1.1.10/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4= +github.com/json-iterator/go v1.1.11/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4= github.com/json-iterator/go v1.1.12 h1:PV8peI4a0ysnczrg+LtxykD8LfKY9ML6u2jnxaEnrnM= github.com/json-iterator/go v1.1.12/go.mod h1:e30LSqwooZae/UwlEbR2852Gd8hjQvJoHmT4TnhNGBo= github.com/jstemmer/go-junit-report v0.0.0-20190106144839-af01ea7f8024/go.mod h1:6v2b51hI/fHJwM22ozAgKL4VKDeJcHhJFhtBdhmNjmU= github.com/jstemmer/go-junit-report v0.9.1/go.mod h1:Brl9GWCQeLvo8nXZwPNNblvFj/XSXhF0NWZEnDohbsk= +github.com/julienschmidt/httprouter v1.2.0/go.mod h1:SYymIcj16QtmaHHD7aYtjjsJG7VTCxuUUipMqKk8s4w= +github.com/julienschmidt/httprouter v1.3.0/go.mod h1:JR6WtHb+2LUe8TCKY3cZOxFyyO8IZAc4RVcycCCAKdM= github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck= +github.com/konsorten/go-windows-terminal-sequences v1.0.1/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ= +github.com/konsorten/go-windows-terminal-sequences v1.0.3/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ= github.com/kr/fs v0.1.0/go.mod h1:FFnZGqtBN9Gxj7eW1uZ42v5BccTP0vu6NEaFoC2HwRg= +github.com/kr/logfmt v0.0.0-20140226030751-b84e30acd515/go.mod h1:+0opPa2QZZtGFBFZlji/RkVcI2GknAs/DXo4wKdlNEc= github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo= -github.com/kr/pretty v0.3.0 h1:WgNl7dwNpEZ6jJ9k1snq4pZsg7DOEN8hP9Xw0Tsjwk0= +github.com/kr/pretty v0.3.1 h1:flRD4NNwYAUpkphVc1HcthR4KEIFJ65n8Mw5qdRn3LE= github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ= github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI= github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY= @@ -181,13 +222,19 @@ github.com/mattn/go-isatty v0.0.14/go.mod h1:7GGIvUiUoEMVVmxf/4nioHXj79iQHKdU27k github.com/mattn/go-isatty v0.0.16/go.mod h1:kYGgaQfpe5nmfYZH+SKPsOc2e4SrIfOl2e/yFXSvRLM= github.com/mattn/go-isatty v0.0.17 h1:BTarxUcIeDqL27Mc+vyvdWYSL28zpIhv3RoTdsLMPng= github.com/mattn/go-isatty v0.0.17/go.mod h1:kYGgaQfpe5nmfYZH+SKPsOc2e4SrIfOl2e/yFXSvRLM= +github.com/matttproud/golang_protobuf_extensions v1.0.1 h1:4hp9jkHxhMHkqkrB3Ix0jegS5sx/RkqARlsWZ6pIwiU= +github.com/matttproud/golang_protobuf_extensions v1.0.1/go.mod h1:D8He9yQNgCq6Z5Ld7szi9bcBfOoFv/3dc6xSMkL2PC0= github.com/mitchellh/mapstructure v1.5.0 h1:jeMsZIYE/09sWLaz43PL7Gy6RuMjD2eJVyuac5Z2hdY= github.com/mitchellh/mapstructure v1.5.0/go.mod h1:bFUtVrKA4DC2yAKiSyO/QUcy7e+RRV2QTWOzhPopBRo= github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q= github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd h1:TRLaZ9cD/w8PVh93nsPXa1VrQ6jlwL5oN8l14QlcNfg= github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q= +github.com/modern-go/reflect2 v0.0.0-20180701023420-4b7aa43c6742/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0= +github.com/modern-go/reflect2 v1.0.1/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0= github.com/modern-go/reflect2 v1.0.2 h1:xBagoLtFs94CBntxluKeaWgTMpvLxC4ur3nMaC9Gz0M= github.com/modern-go/reflect2 v1.0.2/go.mod h1:yWuevngMOJpCy52FWWMvUC8ws7m/LJsjYzDa0/r8luk= +github.com/mwitkow/go-conntrack v0.0.0-20161129095857-cc309e4a2223/go.mod h1:qRWi+5nqEBWmkhHvq77mSJWrCKwh8bxhgT7d/eI7P4U= +github.com/mwitkow/go-conntrack v0.0.0-20190716064945-2f068394615f/go.mod h1:qRWi+5nqEBWmkhHvq77mSJWrCKwh8bxhgT7d/eI7P4U= github.com/nxadm/tail v1.4.8 h1:nPr65rt6Y5JFSKQO7qToXr7pePgD6Gwiw05lkbyAQTE= github.com/onsi/ginkgo v1.16.5 h1:8xi0RTUf59SOSfEtZMvwTvXYMzG4gV23XVHOZiXNtnE= github.com/onsi/gomega v1.18.1 h1:M1GfJqGRrBrrGGsbxzV5dqM2U2ApXefZCQpkukxYRLE= @@ -197,6 +244,7 @@ github.com/orcaman/concurrent-map v1.0.0 h1:I/2A2XPCb4IuQWcQhBhSwGfiuybl/J0ev9HD github.com/orcaman/concurrent-map v1.0.0/go.mod h1:Lu3tH6HLW3feq74c2GC+jIMS/K2CFcDWnWD9XkenwhI= github.com/pelletier/go-toml/v2 v2.0.6 h1:nrzqCb7j9cDFj2coyLNLaZuJTLjWjlaz6nvTvIwycIU= github.com/pelletier/go-toml/v2 v2.0.6/go.mod h1:eumQOmlWiOPt5WriQQqoM5y18pDHwha2N+QD+EUNTek= +github.com/pkg/errors v0.8.0/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4= github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= @@ -204,16 +252,39 @@ github.com/pkg/profile v1.3.0/go.mod h1:hJw3o1OdXxsrSjjVksARp5W95eeEaEfptyVZyv6J github.com/pkg/sftp v1.13.1/go.mod h1:3HaPG6Dq1ILlpPZRO0HVMrsydcdLt6HRDccSgb87qRg= github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= +github.com/prometheus/client_golang v0.9.1/go.mod h1:7SWBe2y4D6OKWSNQJUaRYU/AaXPKyh/dDVn+NZz0KFw= +github.com/prometheus/client_golang v1.0.0/go.mod h1:db9x61etRT2tGnBNRi70OPL5FsnadC4Ky3P0J6CfImo= +github.com/prometheus/client_golang v1.7.1/go.mod h1:PY5Wy2awLA44sXw4AOSfFBetzPP4j5+D6mVACh+pe2M= +github.com/prometheus/client_golang v1.11.0 h1:HNkLOAEQMIDv/K+04rukrLx6ch7msSRwf3/SASFAGtQ= +github.com/prometheus/client_golang v1.11.0/go.mod h1:Z6t4BnS23TR94PD6BsDNk8yVqroYurpAkEiz0P2BEV0= +github.com/prometheus/client_model v0.0.0-20180712105110-5c3871d89910/go.mod h1:MbSGuTsp3dbXC40dX6PRTWyKYBIrTGTE9sqQNg2J8bo= +github.com/prometheus/client_model v0.0.0-20190129233127-fd36f4220a90/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA= github.com/prometheus/client_model v0.0.0-20190812154241-14fe0d1b01d4/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA= +github.com/prometheus/client_model v0.2.0 h1:uq5h0d+GuxiXLJLNABMgp2qUWDPiLvgCzz2dUR+/W/M= +github.com/prometheus/client_model v0.2.0/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA= +github.com/prometheus/common v0.4.1/go.mod h1:TNfzLD0ON7rHzMJeJkieUDPYmFC7Snx/y86RQel1bk4= +github.com/prometheus/common v0.10.0/go.mod h1:Tlit/dnDKsSWFlCLTWaA1cyBgKHSMdTB80sz/V91rCo= +github.com/prometheus/common v0.26.0/go.mod h1:M7rCNAaPfAosfx8veZJCuw84e35h3Cfd9VFqTh1DIvc= +github.com/prometheus/common v0.30.0 h1:JEkYlQnpzrzQFxi6gnukFPdQ+ac82oRhzMcIduJu/Ug= +github.com/prometheus/common v0.30.0/go.mod h1:vu+V0TpY+O6vW9J44gczi3Ap/oXXR10b+M/gUGO4Hls= +github.com/prometheus/procfs v0.0.0-20181005140218-185b4288413d/go.mod h1:c3At6R/oaqEKCNdg8wHV1ftS6bRYblBhIjjI8uT2IGk= +github.com/prometheus/procfs v0.0.2/go.mod h1:TjEm7ze935MbeOT/UhFTIMYKhuLP4wbCsTZCD3I8kEA= +github.com/prometheus/procfs v0.1.3/go.mod h1:lV6e/gmhEcM9IjHGsFOCxxuZ+z1YqCvr4OA4YeYWdaU= +github.com/prometheus/procfs v0.6.0/go.mod h1:cz+aTbrPOrUb4q7XlbU9ygM+/jj0fzG6c1xBZuNvfVA= +github.com/prometheus/procfs v0.7.3 h1:4jVXhlkAyzOScmCkXBTOLRLTz8EeU+eyjrwB/EPq0VU= +github.com/prometheus/procfs v0.7.3/go.mod h1:cz+aTbrPOrUb4q7XlbU9ygM+/jj0fzG6c1xBZuNvfVA= github.com/rakyll/statik v0.1.7 h1:OF3QCZUuyPxuGEP7B4ypUa7sB/iHtqOTDYZXGM8KOdQ= github.com/rakyll/statik v0.1.7/go.mod h1:AlZONWzMtEnMs7W4e/1LURLiI49pIMmp6V9Unghqrcc= github.com/rogpeppe/go-internal v1.3.0/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFRclV5y23lUDJ4= -github.com/rogpeppe/go-internal v1.6.1 h1:/FiVV8dS/e+YqF2JvO3yXRFbBLTIuSDkuC7aBOAvL+k= +github.com/rogpeppe/go-internal v1.10.0 h1:TMyTOH3F/DB16zRVcYyreMH6GnZZrwQVAoYjRBZyWFQ= github.com/rs/xid v1.3.0/go.mod h1:trrq9SKmegXys3aeAKXMUTdJsYXVwGY3RLcfgqegfbg= github.com/rs/xid v1.4.0/go.mod h1:trrq9SKmegXys3aeAKXMUTdJsYXVwGY3RLcfgqegfbg= github.com/rs/zerolog v1.27.0/go.mod h1:7frBqO0oezxmnO7GF86FY++uy8I0Tk/If5ni1G9Qc0U= github.com/rs/zerolog v1.29.0 h1:Zes4hju04hjbvkVkOhdl2HpZa+0PmVwigmo8XoORE5w= github.com/rs/zerolog v1.29.0/go.mod h1:NILgTygv/Uej1ra5XxGf82ZFSLk58MFGAUS2o6usyD0= +github.com/sirupsen/logrus v1.2.0/go.mod h1:LxeOpSwHxABJmUn/MG1IvRgCAasNZTLOkJPxbbu5VWo= +github.com/sirupsen/logrus v1.4.2/go.mod h1:tLMulIdttU9McNUspp0xgXVQah82FyeX6MwdIuYE2rE= +github.com/sirupsen/logrus v1.6.0/go.mod h1:7uNnSEd1DgxDLC74fIahvMZmmYsHGZGEOFrfsX/uA88= github.com/spf13/afero v1.9.4 h1:Sd43wM1IWz/s1aVXdOBkjJvuP8UdyqioeE4AmM0QsBs= github.com/spf13/afero v1.9.4/go.mod h1:iUV7ddyEEZPO5gA3zD4fJt6iStLlL+Lg4m2cihcDf8Y= github.com/spf13/cast v1.5.0 h1:rj3WzYc11XZaIZMPKmwP96zkFEnnAmV8s6XbB2aY32w= @@ -225,6 +296,7 @@ github.com/spf13/pflag v1.0.5/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An github.com/spf13/viper v1.15.0 h1:js3yy885G8xwJa6iOISGFwd+qlUo5AvyXb7CiihdtiU= github.com/spf13/viper v1.15.0/go.mod h1:fFcTBJxvhhzSJiZy8n+PeW6t8l+KeT/uTARa0jHOQLA= github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= +github.com/stretchr/objx v0.1.1/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw= github.com/stretchr/objx v0.5.0 h1:1zr/of2m5FGMsad5YfcqgdqdWrIhu+EBEJRhR1U7z/c= github.com/stretchr/objx v0.5.0/go.mod h1:Yh+to48EsGEfYuaHDzXPcE3xhTkx73EhmCGUpEOglKo= @@ -236,8 +308,8 @@ github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/ github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU= github.com/stretchr/testify v1.8.1/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4= -github.com/stretchr/testify v1.8.2 h1:+h33VjcLVPDHtOdpUCuF+7gSuG3yGIftsP1YvFihtJ8= -github.com/stretchr/testify v1.8.2/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4= +github.com/stretchr/testify v1.8.4 h1:CcVxjf3Q8PM0mHUKJCdn+eZZtm5yQwehR5yeSVQQcUk= +github.com/stretchr/testify v1.8.4/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo= github.com/subosito/gotenv v1.4.2 h1:X1TuBLAMDFbaTAChgCBLu3DU3UPyELpnF2jjJ2cz/S8= github.com/subosito/gotenv v1.4.2/go.mod h1:ayKnFf/c6rvx/2iiLrJUk1e6plDbT3edrFNGqEflhK0= github.com/twmb/murmur3 v1.0.0/go.mod h1:5Y5m8Y8WIyucaICVP+Aep5C8ydggjEuRQHDq1icoOYo= @@ -253,6 +325,26 @@ go.opencensus.io v0.22.2/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw= go.opencensus.io v0.22.3/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw= go.opencensus.io v0.22.4/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw= go.opencensus.io v0.22.5/go.mod h1:5pWMHQbX5EPX2/62yrJeAkowc+lfs/XD7Uxpq3pI6kk= +go.opentelemetry.io/otel v1.19.0 h1:MuS/TNf4/j4IXsZuJegVzI1cwut7Qc00344rgH7p8bs= +go.opentelemetry.io/otel v1.19.0/go.mod h1:i0QyjOq3UPoTzff0PJB2N66fb4S0+rSbSB15/oyH9fY= +go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.19.0 h1:Mne5On7VWdx7omSrSSZvM4Kw7cS7NQkOOmLcgscI51U= +go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.19.0/go.mod h1:IPtUMKL4O3tH5y+iXVyAXqpAwMuzC1IrxVS81rummfE= +go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc v1.19.0 h1:3d+S281UTjM+AbF31XSOYn1qXn3BgIdWl8HNEpx08Jk= +go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc v1.19.0/go.mod h1:0+KuTDyKL4gjKCF75pHOX4wuzYDUZYfAQdSu43o+Z2I= +go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracehttp v1.19.0 h1:IeMeyr1aBvBiPVYihXIaeIZba6b8E1bYp7lbdxK8CQg= +go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracehttp v1.19.0/go.mod h1:oVdCUtjq9MK9BlS7TtucsQwUcXcymNiEDjgDD2jMtZU= +go.opentelemetry.io/otel/exporters/stdout/stdouttrace v1.19.0 h1:Nw7Dv4lwvGrI68+wULbcq7su9K2cebeCUrDjVrUJHxM= +go.opentelemetry.io/otel/exporters/stdout/stdouttrace v1.19.0/go.mod h1:1MsF6Y7gTqosgoZvHlzcaaM8DIMNZgJh87ykokoNH7Y= +go.opentelemetry.io/otel/metric v1.19.0 h1:aTzpGtV0ar9wlV4Sna9sdJyII5jTVJEvKETPiOKwvpE= +go.opentelemetry.io/otel/metric v1.19.0/go.mod h1:L5rUsV9kM1IxCj1MmSdS+JQAcVm319EUrDVLrt7jqt8= +go.opentelemetry.io/otel/sdk v1.19.0 h1:6USY6zH+L8uMH8L3t1enZPR3WFEmSTADlqldyHtJi3o= +go.opentelemetry.io/otel/sdk v1.19.0/go.mod h1:NedEbbS4w3C6zElbLdPJKOpJQOrGUJ+GfzpjUvI0v1A= +go.opentelemetry.io/otel/trace v1.19.0 h1:DFVQmlVbfVeOuBRrwdtaehRrWiL1JoVs9CPIQ1Dzxpg= +go.opentelemetry.io/otel/trace v1.19.0/go.mod h1:mfaSyvGyEJEI0nyV2I4qhNQnbBOUUmYZpYojqMnX2vo= +go.opentelemetry.io/proto/otlp v1.0.0 h1:T0TX0tmXU8a3CbNXzEKGeU5mIVOdf0oykP+u2lIVU/I= +go.opentelemetry.io/proto/otlp v1.0.0/go.mod h1:Sy6pihPLfYHkr3NkUbEhGHFhINUSI/v80hjKIs5JXpM= +go.uber.org/goleak v1.2.1 h1:NBol2c7O1ZokfZ0LEU9K6Whx/KnwvepVetCUhtKja4A= +golang.org/x/crypto v0.0.0-20180904163835-0709b304e793/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4= golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= golang.org/x/crypto v0.0.0-20190510104115-cbcb75029529/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= golang.org/x/crypto v0.0.0-20190605123033-f99c8df09eb5/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= @@ -260,8 +352,8 @@ golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8U golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= golang.org/x/crypto v0.0.0-20210421170649-83a5a9bb288b/go.mod h1:T9bdIzuCu7OtxOm1hfPfRQxPLYneinmdGuTeoZ9dtd4= golang.org/x/crypto v0.0.0-20211108221036-ceb1ce70b4fa/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc= -golang.org/x/crypto v0.6.0 h1:qfktjS5LUO+fFKeJXZ+ikTRijMmljikvG68fpMMruSc= -golang.org/x/crypto v0.6.0/go.mod h1:OFC/31mSvZgRz0V1QTNCzfAI1aIRzbiufJtkMIlEp58= +golang.org/x/crypto v0.11.0 h1:6Ewdq3tDic1mg5xRO4milcWCfMVQhI4NkqWWvqejpuA= +golang.org/x/crypto v0.11.0/go.mod h1:xgJhtzW8F9jGdVFWZESrid1U1bjeNy4zgy5cRr/CIio= golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= golang.org/x/exp v0.0.0-20190306152737-a1d7652674e8/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= golang.org/x/exp v0.0.0-20190510132918-efd6b22b2522/go.mod h1:ZjyILWgesfNpC6sMxTJOJm9Kp84zZh5NQWvqDGG3Qr8= @@ -297,6 +389,7 @@ golang.org/x/mod v0.4.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= golang.org/x/mod v0.4.1/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20180826012351-8a410e7b638d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= +golang.org/x/net v0.0.0-20181114220301-adae6a3d119a/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20190108225652-1e06a53dbb7e/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20190213061140-3a22650c66bd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= @@ -304,6 +397,7 @@ golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn golang.org/x/net v0.0.0-20190501004415-9ce7a6920f09/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= golang.org/x/net v0.0.0-20190503192946-f4e77d36d62c/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= golang.org/x/net v0.0.0-20190603091049-60506f45cf65/go.mod h1:HSz+uSET+XFnRR8LxR5pz3Of3rY3CfYBVs4xY44aLks= +golang.org/x/net v0.0.0-20190613194153-d28f0bde5980/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= golang.org/x/net v0.0.0-20190628185345-da137c7871d7/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= golang.org/x/net v0.0.0-20190724013045-ca1201d0de80/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= @@ -326,7 +420,9 @@ golang.org/x/net v0.0.0-20201031054903-ff519b6c9102/go.mod h1:sp8m0HH+o8qH0wwXwY golang.org/x/net v0.0.0-20201209123823-ac852fbbde11/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg= golang.org/x/net v0.0.0-20201224014010-6772e930b67b/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg= golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg= -golang.org/x/net v0.6.0 h1:L4ZwwTvKW9gr0ZMS1yrHD9GZhIuVjOBBnaKH+SPQK0Q= +golang.org/x/net v0.0.0-20210525063256-abc453219eb5/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= +golang.org/x/net v0.12.0 h1:cfawfvKITfUsFCeJIHJrbSxpeu/E81khclypR0GVT50= +golang.org/x/net v0.12.0/go.mod h1:zEVYFnQC7m/vmpQFELhcD1EWkZlX69l4oqgmer6hfKA= golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U= golang.org/x/oauth2 v0.0.0-20190226205417-e64efc72b421/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= golang.org/x/oauth2 v0.0.0-20190604053449-0f29369cfe45/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= @@ -336,6 +432,7 @@ golang.org/x/oauth2 v0.0.0-20200902213428-5d25da1a8d43/go.mod h1:KelEdhl1UZF7XfJ golang.org/x/oauth2 v0.0.0-20201109201403-9fd604954f58/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A= golang.org/x/oauth2 v0.0.0-20201208152858-08078c50e5b5/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A= golang.org/x/oauth2 v0.0.0-20210218202405-ba52d332ba99/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A= +golang.org/x/oauth2 v0.0.0-20210514164344-f6687ab2804c/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A= golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20181108010431-42b317875d0f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20181221193216-37e7f081c4d4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= @@ -346,12 +443,15 @@ golang.org/x/sync v0.0.0-20200317015054-43a5402ce75a/go.mod h1:RxMgew5VJxzue5/jJ golang.org/x/sync v0.0.0-20200625203802-6e8e738ad208/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20201020160332-67f06af15bc9/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20201207232520-09787c993a3a/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= -golang.org/x/sync v0.1.0 h1:wsuoTGHzEhffawBOhz5CYhcrV4IdKZbEyZjBMuTp12o= -golang.org/x/sync v0.1.0/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.3.0 h1:ftCYgMx6zT/asHUrPw8BLLscYtGznsLAnjq5RH9P66E= +golang.org/x/sync v0.3.0/go.mod h1:FU7BRWz2tNW+3quACPkgCx/L+uEAv1htQ0V83Z9Rj+Y= golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= +golang.org/x/sys v0.0.0-20180905080454-ebe1bf3edb33/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= +golang.org/x/sys v0.0.0-20181116152217-5ac8a444bdc5/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20190312061237-fead79001313/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20190422165155-953cdadca894/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190502145724-3ef323f4f1fd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190507160741-ecd444e8653b/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190606165138-5da285871e9c/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= @@ -360,6 +460,7 @@ golang.org/x/sys v0.0.0-20190726091711-fc99dfbffb4e/go.mod h1:h1NjWce9XRLGQEsW7w golang.org/x/sys v0.0.0-20191001151750-bb3f8db39f24/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20191204072324-ce4227a45e2e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20191228213918-04cbcbbfeed8/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200106162015-b016eb3dc98e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200113162924-86b910548bc1/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200122134326-e047566fdf82/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200202164722-d101bd2416d5/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= @@ -372,6 +473,8 @@ golang.org/x/sys v0.0.0-20200501052902-10377860bb8e/go.mod h1:h1NjWce9XRLGQEsW7w golang.org/x/sys v0.0.0-20200511232937-7e40ca221e25/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200515095857-1151b9dac4a9/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200523222454-059865788121/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200615200032-f1bc736245b1/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200625212154-ddb9806d33ae/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200803210538-64077c9b5642/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200905004654-be1d3432aa8f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= @@ -379,16 +482,19 @@ golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7w golang.org/x/sys v0.0.0-20201201145000-ef89a241ccb3/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210104204734-6f8348627aad/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210119212857-b64e53b001e4/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20210124154548-22da62e12c0c/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210225134936-a50acf3fe073/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20210423082822-04245dca01da/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210423185535-09eb48e85fd7/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20210603081109-ebe580a85c40/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20210630005230-0f9fa26af87c/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20210927094055-39ccf1dd6fa6/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220715151400-c0bba94af5f8/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220811171246-fbc7d0a398ab/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220908164124-27713097b956/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.5.0 h1:MUK/U/4lj1t1oPg0HfuXDN/Z1wv31ZJ/YcPiGccS4DU= -golang.org/x/sys v0.5.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.12.0 h1:CM0HF96J0hcLAwsHPJZjfdNzs0gftsLfgKt57wWHJ0o= +golang.org/x/sys v0.12.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= golang.org/x/text v0.0.0-20170915032832-14c0d48ead0c/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= @@ -396,8 +502,9 @@ golang.org/x/text v0.3.1-0.20180807135948-17ff2d5776d2/go.mod h1:NqM8EUOU14njkJ3 golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk= golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= golang.org/x/text v0.3.4/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= -golang.org/x/text v0.7.0 h1:4BRB4x83lYWy72KwLD/qYDuTu7q9PjSagHvijDw7cLo= -golang.org/x/text v0.7.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8= +golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= +golang.org/x/text v0.11.0 h1:LAntKIrcmeSKERyiOh0XMV39LXS8IE9UL2yP7+f5ij4= +golang.org/x/text v0.11.0/go.mod h1:TvPlkZtksWOMsz7fbANvkp4WM8x/WCo/om8BMLbz+aE= golang.org/x/time v0.0.0-20181108054448-85acf8d2951c/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= golang.org/x/time v0.0.0-20190308202827-9d24e82272b4/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= golang.org/x/time v0.0.0-20191024005414-555d28b269f0/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= @@ -514,6 +621,11 @@ google.golang.org/genproto v0.0.0-20201210142538-e3217bee35cc/go.mod h1:FWY/as6D google.golang.org/genproto v0.0.0-20201214200347-8c77b98c765d/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= google.golang.org/genproto v0.0.0-20210108203827-ffc7fda8c3d7/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= google.golang.org/genproto v0.0.0-20210226172003-ab064af71705/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= +google.golang.org/genproto v0.0.0-20230711160842-782d3b101e98 h1:Z0hjGZePRE0ZBWotvtrwxFNrNE9CUAGtplaDK5NNI/g= +google.golang.org/genproto/googleapis/api v0.0.0-20230711160842-782d3b101e98 h1:FmF5cCW94Ij59cfpoLiwTgodWmm60eEV0CjlsVg2fuw= +google.golang.org/genproto/googleapis/api v0.0.0-20230711160842-782d3b101e98/go.mod h1:rsr7RhLuwsDKL7RmgDDCUc6yaGr1iqceVb5Wv6f6YvQ= +google.golang.org/genproto/googleapis/rpc v0.0.0-20230711160842-782d3b101e98 h1:bVf09lpb+OJbByTj913DRJioFFAjf/ZGxEz7MajTp2U= +google.golang.org/genproto/googleapis/rpc v0.0.0-20230711160842-782d3b101e98/go.mod h1:TUfxEVdsvPg18p6AslUXFoLdpED4oBnGwyqk3dV1XzM= google.golang.org/grpc v1.19.0/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c= google.golang.org/grpc v1.20.1/go.mod h1:10oTOabMzJvdu6/UiuZezV6QK5dSlG84ov/aaiqXj38= google.golang.org/grpc v1.21.1/go.mod h1:oYelfM1adQP15Ek0mdvEgi9Df8B9CZIaU1084ijfRaM= @@ -530,6 +642,8 @@ google.golang.org/grpc v1.31.1/go.mod h1:N36X2cJ7JwdamYAgDz+s+rVMFjt3numwzf/HckM google.golang.org/grpc v1.33.2/go.mod h1:JMHMWHQWaTccqQQlmk3MJZS+GWXOdAesneDmEnv2fbc= google.golang.org/grpc v1.34.0/go.mod h1:WotjhfgOW/POjDeRt8vscBtXq+2VjORFy659qA51WJ8= google.golang.org/grpc v1.35.0/go.mod h1:qjiiYl8FncCW8feJPdyg3v6XW24KsRHe+dy9BAGRRjU= +google.golang.org/grpc v1.58.2 h1:SXUpjxeVF3FKrTYQI4f4KvbGD5u2xccdYdurwowix5I= +google.golang.org/grpc v1.58.2/go.mod h1:tgX3ZQDlNJGU96V6yHh1T/JeoBQ2TXdr43YbYSsCJk0= google.golang.org/protobuf v0.0.0-20200109180630-ec00e32a8dfd/go.mod h1:DFci5gLYBciE7Vtevhsrf46CRTquxDuWsQurQQe4oz8= google.golang.org/protobuf v0.0.0-20200221191635-4d8936d0db64/go.mod h1:kwYJMbMJ01Woi6D6+Kah6886xMZcty6N08ah7+eCXa0= google.golang.org/protobuf v0.0.0-20200228230310-ab0ca4ff8a60/go.mod h1:cfTl7dwQJ+fmap5saPgwCLgHXTUD7jkjRqWcaiX5VyM= @@ -540,14 +654,24 @@ google.golang.org/protobuf v1.23.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2 google.golang.org/protobuf v1.23.1-0.20200526195155-81db48ad09cc/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU= google.golang.org/protobuf v1.24.0/go.mod h1:r/3tXBNzIEhYS9I1OUVjXDlt8tc493IdKGjtUeSXeh4= google.golang.org/protobuf v1.25.0/go.mod h1:9JNX74DMeImyA3h4bdi1ymwjUzf21/xIlbajtzgsN7c= +google.golang.org/protobuf v1.26.0-rc.1/go.mod h1:jlhhOSvTdKEhbULTjvd4ARK9grFBp09yW+WbY/TyQbw= +google.golang.org/protobuf v1.26.0/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc= +google.golang.org/protobuf v1.31.0 h1:g0LDEJHgrBl9N9r17Ru3sqWhkIx2NB67okBHPwC7hs8= +google.golang.org/protobuf v1.31.0/go.mod h1:HV8QOd/L58Z+nl8r43ehVNZIU/HEI6OcFqwMG9pJV4I= +gopkg.in/alecthomas/kingpin.v2 v2.2.6/go.mod h1:FMv+mEhP44yOT+4EoQTLFTRgOQ1FBLkstjWtayDeSgw= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= -gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127 h1:qIbj1fsPNlZgppZ+VLlY7N33q108Sa+fhmuc+sWQYwY= gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= +gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= +gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk= gopkg.in/errgo.v2 v2.1.0/go.mod h1:hNsd1EY+bozCKY1Ytp96fpM3vjJbqLJn88ws8XvfDNI= gopkg.in/ini.v1 v1.67.0 h1:Dgnx+6+nfE+IfzjUEISNeydPJh9AXNNsWbGP9KzCsOA= gopkg.in/ini.v1 v1.67.0/go.mod h1:pNLf8WUiyNEtQjuu5G5vTm06TEv9tsIgeAvK8hOrP4k= gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7 h1:uRGJdciOHaEIrze2W8Q3AKkepLTh2hOroT7a+7czfdQ= +gopkg.in/yaml.v2 v2.2.1/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= +gopkg.in/yaml.v2 v2.2.4/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= +gopkg.in/yaml.v2 v2.2.5/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= +gopkg.in/yaml.v2 v2.3.0/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= gopkg.in/yaml.v2 v2.4.0 h1:D8xgwECY7CYvx+Y2n4sBz93Jn9JRvxdiyyo8CTfuKaY= gopkg.in/yaml.v2 v2.4.0/go.mod h1:RDklbk79AGWmwhnvt/jBztapEOGDOx6ZbXqjP6csGnQ= gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= diff --git a/pkg/handlers/activate.go b/pkg/handlers/activate.go index 0a35d662..4a48f90a 100644 --- a/pkg/handlers/activate.go +++ b/pkg/handlers/activate.go @@ -76,10 +76,10 @@ func Activate(w http.ResponseWriter, r *http.Request) { switch value { case "experiment": logger.Debug().Str("experimentKey", key).Msg("fetching experiment decision") - d, err = optlyClient.ActivateExperiment(key, uc, disableTracking) + d, err = optlyClient.ActivateExperiment(r.Context(), key, uc, disableTracking) case "feature": logger.Debug().Str("featureKey", key).Msg("fetching feature decision") - d, err = optlyClient.ActivateFeature(key, uc, disableTracking) + d, err = optlyClient.ActivateFeature(r.Context(), key, uc, disableTracking) case "experimentKey-not-found": logger.Debug().Str("experimentKey", key).Msg("experimentKey not found") d = &optimizely.Decision{ diff --git a/pkg/handlers/admin_entities.go b/pkg/handlers/admin_entities.go index 9b555fd5..1c6e9abc 100644 --- a/pkg/handlers/admin_entities.go +++ b/pkg/handlers/admin_entities.go @@ -1,5 +1,5 @@ /**************************************************************************** - * Copyright 2019-2020, Optimizely, Inc. and contributors * + * Copyright 2019-2020,2023 Optimizely, Inc. and contributors * * * * Licensed under the Apache License, Version 2.0 (the "License"); * * you may not use this file except in compliance with the License. * @@ -18,14 +18,13 @@ package handlers import ( - "expvar" "net/http" "os" "time" "github.com/go-chi/render" - "github.com/optimizely/agent/config" + "github.com/optimizely/agent/pkg/metrics" ) var startTime = time.Now() @@ -87,5 +86,5 @@ func (a Admin) AppInfoHeader(next http.Handler) http.Handler { // Metrics returns expvar info func (a Admin) Metrics(w http.ResponseWriter, r *http.Request) { - expvar.Handler().ServeHTTP(w, r) + metrics.GetHandler(a.Config.Admin.MetricsType).ServeHTTP(w, r) } diff --git a/pkg/handlers/override.go b/pkg/handlers/override.go index 749bbf4b..dff9aa9e 100644 --- a/pkg/handlers/override.go +++ b/pkg/handlers/override.go @@ -61,7 +61,7 @@ func Override(w http.ResponseWriter, r *http.Request) { // Empty variation means remove if body.VariationKey == "" { - if override, err := optlyClient.RemoveForcedVariation(experimentKey, body.UserID); err != nil { + if override, err := optlyClient.RemoveForcedVariation(r.Context(), experimentKey, body.UserID); err != nil { RenderError(err, http.StatusInternalServerError, w, r) } else { render.JSON(w, r, override) @@ -70,7 +70,7 @@ func Override(w http.ResponseWriter, r *http.Request) { } logger.Debug().Str("experimentKey", experimentKey).Str("variationKey", body.VariationKey).Msg("setting override") - if override, err := optlyClient.SetForcedVariation(experimentKey, body.UserID, body.VariationKey); err != nil { + if override, err := optlyClient.SetForcedVariation(r.Context(), experimentKey, body.UserID, body.VariationKey); err != nil { RenderError(err, http.StatusInternalServerError, w, r) } else { render.JSON(w, r, override) diff --git a/pkg/handlers/track.go b/pkg/handlers/track.go index 8359ee42..61e40c7b 100644 --- a/pkg/handlers/track.go +++ b/pkg/handlers/track.go @@ -60,7 +60,7 @@ func TrackEvent(w http.ResponseWriter, r *http.Request) { Attributes: body.UserAttributes, } - track, err := optlyClient.TrackEvent(eventKey, uc, body.EventTags) + track, err := optlyClient.TrackEvent(r.Context(), eventKey, uc, body.EventTags) if err != nil { RenderError(err, http.StatusInternalServerError, w, r) return diff --git a/pkg/metrics/metrics.go b/pkg/metrics/metrics.go index cd8379ce..09c15ea3 100644 --- a/pkg/metrics/metrics.go +++ b/pkg/metrics/metrics.go @@ -18,11 +18,17 @@ package metrics import ( + "expvar" + "net/http" + "regexp" + "strings" "sync" go_kit_metrics "github.com/go-kit/kit/metrics" - go_kit_expvar "github.com/go-kit/kit/metrics/expvar" + go_kit_prometheus "github.com/go-kit/kit/metrics/prometheus" + stdprometheus "github.com/prometheus/client_golang/prometheus" + "github.com/prometheus/client_golang/prometheus/promhttp" "github.com/rs/zerolog/log" ) @@ -33,12 +39,42 @@ const ( TimerPrefix = "timer" ) +const ( + prometheusPackage = "prometheus" +) + +// GetHandler returns request handler for provided metrics package type +func GetHandler(packageType string) http.Handler { + switch packageType { + case prometheusPackage: + return promhttp.Handler() + default: + // expvar + return expvar.Handler() + } +} + +// Timer is the collection of some timers +type Timer struct { + hits go_kit_metrics.Counter + totalTime go_kit_metrics.Counter + histogram go_kit_metrics.Histogram +} + +// Update timer components +func (t *Timer) Update(delta float64) { + t.hits.Add(1) + t.totalTime.Add(delta) + t.histogram.Observe(delta) +} + // Registry initializes expvar metrics registry type Registry struct { metricsCounterVars map[string]go_kit_metrics.Counter metricsGaugeVars map[string]go_kit_metrics.Gauge metricsHistogramVars map[string]go_kit_metrics.Histogram metricsTimerVars map[string]*Timer + metricsType string gaugeLock sync.RWMutex counterLock sync.RWMutex @@ -46,24 +82,6 @@ type Registry struct { timerLock sync.RWMutex } -// NewRegistry initializes metrics registry -func NewRegistry() *Registry { - - return &Registry{ - metricsCounterVars: map[string]go_kit_metrics.Counter{}, - metricsGaugeVars: map[string]go_kit_metrics.Gauge{}, - metricsHistogramVars: map[string]go_kit_metrics.Histogram{}, - metricsTimerVars: map[string]*Timer{}, - } -} - -// Timer is the collection of some timers -type Timer struct { - hits go_kit_metrics.Counter - totalTime go_kit_metrics.Counter - histogram go_kit_metrics.Histogram -} - // NewTimer constructs Timer func (m *Registry) NewTimer(key string) *Timer { if key == "" { @@ -81,16 +99,22 @@ func (m *Registry) NewTimer(key string) *Timer { return m.createTimer(combinedKey) } -// Update timer components -func (t *Timer) Update(delta float64) { - t.hits.Add(1) - t.totalTime.Add(delta) - t.histogram.Observe(delta) +// GetCounter gets go-kit expvar Counter +// NewRegistry initializes metrics registry +func NewRegistry(metricsType string) *Registry { + + registry := &Registry{ + metricsCounterVars: map[string]go_kit_metrics.Counter{}, + metricsGaugeVars: map[string]go_kit_metrics.Gauge{}, + metricsHistogramVars: map[string]go_kit_metrics.Histogram{}, + metricsTimerVars: map[string]*Timer{}, + metricsType: metricsType, + } + return registry } -// GetCounter gets go-kit expvar Counter +// GetCounter gets go-kit Counter func (m *Registry) GetCounter(key string) go_kit_metrics.Counter { - if key == "" { log.Warn().Msg("metrics counter key is empty") return nil @@ -103,13 +127,11 @@ func (m *Registry) GetCounter(key string) go_kit_metrics.Counter { if val, ok := m.metricsCounterVars[combinedKey]; ok { return val } - return m.createCounter(combinedKey) } -// GetGauge gets go-kit expvar Gauge +// GetGauge gets go-kit Gauge func (m *Registry) GetGauge(key string) go_kit_metrics.Gauge { - if key == "" { log.Warn().Msg("metrics gauge key is empty") return nil @@ -127,9 +149,8 @@ func (m *Registry) GetGauge(key string) go_kit_metrics.Gauge { // GetHistogram gets go-kit Histogram func (m *Registry) GetHistogram(key string) go_kit_metrics.Histogram { - if key == "" { - log.Warn().Msg("metrics gauge key is empty") + log.Warn().Msg("metrics histogram key is empty") return nil } @@ -141,25 +162,52 @@ func (m *Registry) GetHistogram(key string) go_kit_metrics.Histogram { return m.createHistogram(key) } -func (m *Registry) createGauge(key string) *go_kit_expvar.Gauge { - gaugeVar := go_kit_expvar.NewGauge(key) +func (m *Registry) createGauge(key string) (gaugeVar go_kit_metrics.Gauge) { + // This is required since naming convention for every package differs + name := m.getPackageSupportedName(key) + switch m.metricsType { + case prometheusPackage: + gaugeVar = go_kit_prometheus.NewGaugeFrom(stdprometheus.GaugeOpts{ + Name: name, + }, []string{}) + default: + // Default expvar + gaugeVar = go_kit_expvar.NewGauge(name) + } m.metricsGaugeVars[key] = gaugeVar - return gaugeVar - + return } -func (m *Registry) createCounter(key string) *go_kit_expvar.Counter { - counterVar := go_kit_expvar.NewCounter(key) +func (m *Registry) createCounter(key string) (counterVar go_kit_metrics.Counter) { + // This is required since naming convention for every package differs + name := m.getPackageSupportedName(key) + switch m.metricsType { + case prometheusPackage: + counterVar = go_kit_prometheus.NewCounterFrom(stdprometheus.CounterOpts{ + Name: name, + }, []string{}) + default: + // Default expvar + counterVar = go_kit_expvar.NewCounter(name) + } m.metricsCounterVars[key] = counterVar - return counterVar - + return } -func (m *Registry) createHistogram(key string) *go_kit_expvar.Histogram { - histogramVar := go_kit_expvar.NewHistogram(key, 50) +func (m *Registry) createHistogram(key string) (histogramVar go_kit_metrics.Histogram) { + // This is required since naming convention for every package differs + name := m.getPackageSupportedName(key) + switch m.metricsType { + case prometheusPackage: + histogramVar = go_kit_prometheus.NewHistogramFrom(stdprometheus.HistogramOpts{ + Name: name, + }, []string{}) + default: + // Default expvar + histogramVar = go_kit_expvar.NewHistogram(name, 50) + } m.metricsHistogramVars[key] = histogramVar - return histogramVar - + return } func (m *Registry) createTimer(key string) *Timer { @@ -168,8 +216,33 @@ func (m *Registry) createTimer(key string) *Timer { totalTime: m.createCounter(key + ".responseTime"), histogram: m.createHistogram(key + ".responseTimeHist"), } - m.metricsTimerVars[key] = timerVar return timerVar +} +// getPackageSupportedName converts name to package supported type +func (m *Registry) getPackageSupportedName(name string) string { + switch m.metricsType { + case prometheusPackage: + // https://prometheus.io/docs/practices/naming/ + return toSnakeCase(name) + default: + // Default expvar + return name + } +} + +func toSnakeCase(name string) string { + v := strings.Replace(name, "-", "_", -1) + strArray := strings.Split(v, ".") + var matchFirstCap = regexp.MustCompile("(.)([A-Z][a-z]+)") + var matchAllCap = regexp.MustCompile("([a-z0-9])([A-Z])") + convertedArray := []string{} + + for _, v := range strArray { + snake := matchFirstCap.ReplaceAllString(v, "${1}_${2}") + snake = matchAllCap.ReplaceAllString(snake, "${1}_${2}") + convertedArray = append(convertedArray, strings.ToLower(snake)) + } + return strings.Join(convertedArray, "_") } diff --git a/pkg/metrics/metrics_prometheus_test.go b/pkg/metrics/metrics_prometheus_test.go new file mode 100644 index 00000000..a8a4cc10 --- /dev/null +++ b/pkg/metrics/metrics_prometheus_test.go @@ -0,0 +1,204 @@ +/**************************************************************************** + * Copyright 2023, Optimizely, Inc. and contributors * + * * + * 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 optimizely // +package metrics + +import ( + "expvar" + "io/ioutil" + "net/http/httptest" + "strings" + "testing" + + "github.com/prometheus/client_golang/prometheus/promhttp" + "github.com/stretchr/testify/assert" +) + +func TestPrometheusCounterValid(t *testing.T) { + + rec := httptest.NewRecorder() + req := httptest.NewRequest("GET", "/", nil) + + metricsRegistry := NewRegistry(prometheusPackage) + counter := metricsRegistry.GetCounter("metrics") + counter.Add(12) + counter.Add(23) + + expvar.Handler().ServeHTTP(rec, req) + + promhttp.Handler().ServeHTTP(rec, req) + resp, err := ioutil.ReadAll(rec.Body) + assert.Nil(t, err) + strResponse := string(resp) + strings.Contains(strResponse, "counter_metrics 35") +} + +func TestPrometheusCounterMultipleRetrievals(t *testing.T) { + + rec := httptest.NewRecorder() + req := httptest.NewRequest("GET", "/", nil) + + metricsRegistry := NewRegistry(prometheusPackage) + counterKey := "next_counter_metrics" + counter := metricsRegistry.GetCounter(counterKey) + counter.Add(12) + + nextCounter := metricsRegistry.GetCounter(counterKey) + nextCounter.Add(23) + + promhttp.Handler().ServeHTTP(rec, req) + resp, err := ioutil.ReadAll(rec.Body) + assert.Nil(t, err) + strResponse := string(resp) + strings.Contains(strResponse, "counter_"+counterKey+"35") +} + +func TestPrometheusCounterEmptyKey(t *testing.T) { + + metricsRegistry := NewRegistry(prometheusPackage) + counter := metricsRegistry.GetCounter("") + assert.Nil(t, counter) +} + +func TestPrometheusGaugeValid(t *testing.T) { + + rec := httptest.NewRecorder() + req := httptest.NewRequest("GET", "/", nil) + + metricsRegistry := NewRegistry(prometheusPackage) + gauge := metricsRegistry.GetGauge("metrics") + gauge.Set(12) + gauge.Set(23) + + promhttp.Handler().ServeHTTP(rec, req) + resp, err := ioutil.ReadAll(rec.Body) + assert.Nil(t, err) + strResponse := string(resp) + strings.Contains(strResponse, "gauge_metrics") +} + +func TestPrometheusGaugeMultipleRetrievals(t *testing.T) { + + rec := httptest.NewRecorder() + req := httptest.NewRequest("GET", "/", nil) + + metricsRegistry := NewRegistry(prometheusPackage) + guageKey := "next_gauge_metrics" + gauge := metricsRegistry.GetGauge(guageKey) + gauge.Set(12) + nextGauge := metricsRegistry.GetGauge(guageKey) + nextGauge.Set(23) + + promhttp.Handler().ServeHTTP(rec, req) + resp, err := ioutil.ReadAll(rec.Body) + assert.Nil(t, err) + strResponse := string(resp) + strings.Contains(strResponse, "gauge_"+guageKey+" 23") +} + +func TestPrometheusGaugeEmptyKey(t *testing.T) { + + metricsRegistry := NewRegistry(prometheusPackage) + gauge := metricsRegistry.GetGauge("") + assert.Nil(t, gauge) +} + +func TestPrometheusHistogramValid(t *testing.T) { + + rec := httptest.NewRecorder() + req := httptest.NewRequest("GET", "/", nil) + + metricsRegistry := NewRegistry(prometheusPackage) + histogram := metricsRegistry.GetHistogram("metrics") + histogram.Observe(12) + histogram.Observe(23) + + promhttp.Handler().ServeHTTP(rec, req) + resp, err := ioutil.ReadAll(rec.Body) + assert.Nil(t, err) + strResponse := string(resp) + strings.Contains(strResponse, "timer_metrics_response_time_hist_sum 35") + strings.Contains(strResponse, "timer_metrics_response_time_hist_count 2") +} + +func TestPrometheusHistogramMultipleRetrievals(t *testing.T) { + + rec := httptest.NewRecorder() + req := httptest.NewRequest("GET", "/", nil) + + metricsRegistry := NewRegistry(prometheusPackage) + histogramKey := "next_histogram_metrics" + histogram := metricsRegistry.GetHistogram(histogramKey) + histogram.Observe(12) + nextGauge := metricsRegistry.GetHistogram(histogramKey) + nextGauge.Observe(23) + + promhttp.Handler().ServeHTTP(rec, req) + resp, err := ioutil.ReadAll(rec.Body) + assert.Nil(t, err) + strResponse := string(resp) + strings.Contains(strResponse, "timer_metrics_response_time_hist_sum 35") + strings.Contains(strResponse, "timer_metrics_response_time_hist_count 2") +} + +func TestPrometheusHistogramEmptyKey(t *testing.T) { + + metricsRegistry := NewRegistry(prometheusPackage) + histogram := metricsRegistry.GetHistogram("") + + assert.Nil(t, histogram) +} + +func TestPrometheusTimerValid(t *testing.T) { + + rec := httptest.NewRecorder() + req := httptest.NewRequest("GET", "/", nil) + + metricsRegistry := NewRegistry(prometheusPackage) + timer := metricsRegistry.NewTimer("metrics") + timer.Update(12) + timer.Update(23) + + promhttp.Handler().ServeHTTP(rec, req) + resp, err := ioutil.ReadAll(rec.Body) + assert.Nil(t, err) + strResponse := string(resp) + strings.Contains(strResponse, "timer_metrics_response_time_hist_sum 35") + strings.Contains(strResponse, "timer_metrics_response_time_hist_count 2") + strings.Contains(strResponse, `timer_metrics_response_time_hist_bucket{le="+Inf"} 2`) +} + +func TestPrometheusTimerMultipleRetrievals(t *testing.T) { + + rec := httptest.NewRecorder() + req := httptest.NewRequest("GET", "/", nil) + + metricsRegistry := NewRegistry(prometheusPackage) + timerKey := "next_timer_metrics" + timer := metricsRegistry.NewTimer(timerKey) + timer.Update(12) + nextTimer := metricsRegistry.NewTimer(timerKey) + nextTimer.Update(23) + + promhttp.Handler().ServeHTTP(rec, req) + resp, err := ioutil.ReadAll(rec.Body) + assert.Nil(t, err) + strResponse := string(resp) + strings.Contains(strResponse, "timer_metrics_response_time_hist_sum 35") + strings.Contains(strResponse, "timer_metrics_response_time_hist_count 2") + strings.Contains(strResponse, `timer_metrics_response_time_hist_bucket{le="+Inf"} 2`) +} diff --git a/pkg/metrics/metrics_test.go b/pkg/metrics/metrics_test.go index 9b108798..1eadda22 100644 --- a/pkg/metrics/metrics_test.go +++ b/pkg/metrics/metrics_test.go @@ -1,5 +1,5 @@ /**************************************************************************** - * Copyright 2019, Optimizely, Inc. and contributors * + * Copyright 2019,2023 Optimizely, Inc. and contributors * * * * Licensed under the Apache License, Version 2.0 (the "License"); * * you may not use this file except in compliance with the License. * @@ -28,12 +28,18 @@ import ( type JSON map[string]interface{} +func TestGetHandler(t *testing.T) { + assert.NotNil(t, GetHandler("")) + assert.NotNil(t, GetHandler("expvar")) + assert.NotNil(t, GetHandler("123131231")) +} + func TestCounterValid(t *testing.T) { rec := httptest.NewRecorder() req := httptest.NewRequest("GET", "/", nil) - metricsRegistry := NewRegistry() + metricsRegistry := NewRegistry("") counter := metricsRegistry.GetCounter("metrics") counter.Add(12) counter.Add(23) @@ -52,7 +58,7 @@ func TestCounterMultipleRetrievals(t *testing.T) { rec := httptest.NewRecorder() req := httptest.NewRequest("GET", "/", nil) - metricsRegistry := NewRegistry() + metricsRegistry := NewRegistry("") counterKey := "next_counter_metrics" counter := metricsRegistry.GetCounter(counterKey) counter.Add(12) @@ -70,7 +76,7 @@ func TestCounterMultipleRetrievals(t *testing.T) { func TestCounterEmptyKey(t *testing.T) { - metricsRegistry := NewRegistry() + metricsRegistry := NewRegistry("") counter := metricsRegistry.GetCounter("") assert.Nil(t, counter) @@ -82,7 +88,7 @@ func TestGaugeValid(t *testing.T) { rec := httptest.NewRecorder() req := httptest.NewRequest("GET", "/", nil) - metricsRegistry := NewRegistry() + metricsRegistry := NewRegistry("") gauge := metricsRegistry.GetGauge("metrics") gauge.Set(12) gauge.Set(23) @@ -101,7 +107,7 @@ func TestGaugeMultipleRetrievals(t *testing.T) { rec := httptest.NewRecorder() req := httptest.NewRequest("GET", "/", nil) - metricsRegistry := NewRegistry() + metricsRegistry := NewRegistry("") guageKey := "next_gauge_metrics" gauge := metricsRegistry.GetGauge(guageKey) gauge.Set(12) @@ -119,7 +125,7 @@ func TestGaugeMultipleRetrievals(t *testing.T) { func TestGaugeEmptyKey(t *testing.T) { - metricsRegistry := NewRegistry() + metricsRegistry := NewRegistry("") gauge := metricsRegistry.GetGauge("") assert.Nil(t, gauge) @@ -131,7 +137,7 @@ func TestHistorgramValid(t *testing.T) { rec := httptest.NewRecorder() req := httptest.NewRequest("GET", "/", nil) - metricsRegistry := NewRegistry() + metricsRegistry := NewRegistry("") histogram := metricsRegistry.GetHistogram("metrics") histogram.Observe(12) histogram.Observe(23) @@ -151,7 +157,7 @@ func TestHistogramMultipleRetrievals(t *testing.T) { rec := httptest.NewRecorder() req := httptest.NewRequest("GET", "/", nil) - metricsRegistry := NewRegistry() + metricsRegistry := NewRegistry("") histogramKey := "next_histogram_metrics" histogram := metricsRegistry.GetHistogram(histogramKey) histogram.Observe(12) @@ -170,7 +176,7 @@ func TestHistogramMultipleRetrievals(t *testing.T) { func TestHistogramEmptyKey(t *testing.T) { - metricsRegistry := NewRegistry() + metricsRegistry := NewRegistry("") histogram := metricsRegistry.GetHistogram("") assert.Nil(t, histogram) @@ -181,7 +187,7 @@ func TestTimerValid(t *testing.T) { rec := httptest.NewRecorder() req := httptest.NewRequest("GET", "/", nil) - metricsRegistry := NewRegistry() + metricsRegistry := NewRegistry("") timer := metricsRegistry.NewTimer("metrics") timer.Update(12) timer.Update(23) @@ -202,7 +208,7 @@ func TestTimerMultipleRetrievals(t *testing.T) { rec := httptest.NewRecorder() req := httptest.NewRequest("GET", "/", nil) - metricsRegistry := NewRegistry() + metricsRegistry := NewRegistry("") timerKey := "next_timer_metrics" timer := metricsRegistry.NewTimer(timerKey) timer.Update(12) @@ -219,3 +225,18 @@ func TestTimerMultipleRetrievals(t *testing.T) { assert.Equal(t, 23.0, expVarMap["timer.next_timer_metrics.responseTimeHist.p99"]) } + +func TestToSnakeCase(t *testing.T) { + assert.Equal(t, "", toSnakeCase("")) + assert.Equal(t, "abc", toSnakeCase("abc")) + assert.Equal(t, "abc_123", toSnakeCase("abc_123")) + assert.Equal(t, "abc_efg", toSnakeCase("abcEfg")) + assert.Equal(t, "timer_activate_response_time", toSnakeCase("timer.activate.responseTime")) + assert.Equal(t, "timer_activate_response_time_hist_p95", toSnakeCase("timer.activate.responseTimeHist.p95")) + assert.Equal(t, "timer_create_api_access_token_response_time_hist_p50", toSnakeCase("timer.create-api-access-token.responseTimeHist.p50")) + assert.Equal(t, "timer_get_config_response_time_hist_p50", toSnakeCase("timer.get-config.responseTimeHist.p50")) + assert.Equal(t, "timer_track_event_response_time_hist_p50", toSnakeCase("timer.track-event.responseTimeHist.p50")) + assert.Equal(t, "counter_dispatcher_success_flush", toSnakeCase("counter.dispatcher.successFlush")) + assert.Equal(t, "timer_get_config_response_time", toSnakeCase("timer.get-config.responseTime")) + assert.Equal(t, "timer_get_config_hits", toSnakeCase("timer.get-config.hits")) +} diff --git a/pkg/middleware/metrics_test.go b/pkg/middleware/metrics_test.go index 6148a4e8..5d35a3e2 100644 --- a/pkg/middleware/metrics_test.go +++ b/pkg/middleware/metrics_test.go @@ -53,7 +53,7 @@ func (rm *RequestMetrics) SetupRoute(key string) { r := httptest.NewRequest("GET", "/", nil) rm.req = r.WithContext(context.WithValue(r.Context(), responseTime, time.Now())) - rm.handler = http.Handler(Metricize(key, metrics.NewRegistry())(getTestMetrics())) + rm.handler = http.Handler(Metricize(key, metrics.NewRegistry(""))(getTestMetrics())) } diff --git a/pkg/middleware/trace.go b/pkg/middleware/trace.go new file mode 100644 index 00000000..e5270230 --- /dev/null +++ b/pkg/middleware/trace.go @@ -0,0 +1,58 @@ +/**************************************************************************** + * Copyright 2023 Optimizely, Inc. and contributors * + * * + * 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 middleware // +package middleware + +import ( + "net/http" + + "github.com/go-chi/chi/v5/middleware" + "go.opentelemetry.io/otel" + "go.opentelemetry.io/otel/attribute" + "go.opentelemetry.io/otel/propagation" + semconv "go.opentelemetry.io/otel/semconv/v1.4.0" +) + +func AddTracing(tracerName, spanName string) func(http.Handler) http.Handler { + return func(next http.Handler) http.Handler { + fn := func(w http.ResponseWriter, r *http.Request) { + prop := otel.GetTextMapPropagator() + propCtx := prop.Extract(r.Context(), propagation.HeaderCarrier(r.Header)) + + ctx, span := otel.Tracer(tracerName).Start(propCtx, spanName) + defer span.End() + + span.SetAttributes( + semconv.HTTPMethodKey.String(r.Method), + semconv.HTTPRouteKey.String(r.URL.Path), + semconv.HTTPURLKey.String(r.URL.String()), + semconv.HTTPHostKey.String(r.Host), + semconv.HTTPSchemeKey.String(r.URL.Scheme), + attribute.String(OptlySDKHeader, r.Header.Get(OptlySDKHeader)), + ) + + respWriter := middleware.NewWrapResponseWriter(w, r.ProtoMajor) + + next.ServeHTTP(respWriter, r.WithContext(ctx)) + + span.SetAttributes( + semconv.HTTPStatusCodeKey.Int(respWriter.Status()), + ) + } + return http.HandlerFunc(fn) + } +} diff --git a/pkg/middleware/trace_test.go b/pkg/middleware/trace_test.go new file mode 100644 index 00000000..91e15e6d --- /dev/null +++ b/pkg/middleware/trace_test.go @@ -0,0 +1,51 @@ +/**************************************************************************** + * Copyright 2023 Optimizely, Inc. and contributors * + * * + * 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 middleware // +package middleware + +import ( + "net/http" + "net/http/httptest" + "testing" +) + +func TestAddTracing(t *testing.T) { + handler := http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { + w.Header().Set("Content-Type", "application/text") + w.WriteHeader(http.StatusOK) + w.Write([]byte("OK")) + }) + + req := httptest.NewRequest("GET", "/", nil) + rr := httptest.NewRecorder() + middleware := http.Handler(AddTracing("test-tracer", "test-span")(handler)) + + // Serve the request through the middleware + middleware.ServeHTTP(rr, req) + + if status := rr.Code; status != http.StatusOK { + t.Errorf("Expected status code %v, but got %v", http.StatusOK, status) + } + + if body := rr.Body.String(); body != "OK" { + t.Errorf("Expected response body %v, but got %v", "OK", body) + } + + if typeHeader := rr.Header().Get("Content-Type"); typeHeader != "application/text" { + t.Errorf("Expected Content-Type header %v, but got %v", "application/text", typeHeader) + } +} diff --git a/pkg/optimizely/cache_test.go b/pkg/optimizely/cache_test.go index 398d644c..be7af2e1 100644 --- a/pkg/optimizely/cache_test.go +++ b/pkg/optimizely/cache_test.go @@ -104,7 +104,7 @@ func (suite *CacheTestSuite) TestUpdateConfigs() { } func (suite *CacheTestSuite) TestNewCache() { - agentMetricsRegistry := metrics.NewRegistry() + agentMetricsRegistry := metrics.NewRegistry("") sdkMetricsRegistry := NewRegistry(agentMetricsRegistry) // To improve coverage @@ -252,7 +252,7 @@ type DefaultLoaderTestSuite struct { func (s *DefaultLoaderTestSuite) SetupTest() { // Need the registry to be created only once since it panics if we create gauges with the same name again and again doOnce.Do(func() { - s.registry = &MetricsRegistry{metrics.NewRegistry()} + s.registry = &MetricsRegistry{metrics.NewRegistry("")} }) s.upsMap = cmap.New() s.bpFactory = func(options ...event.BPOptionConfig) *event.BatchEventProcessor { diff --git a/pkg/optimizely/client.go b/pkg/optimizely/client.go index 5e84e6db..3a3593bd 100644 --- a/pkg/optimizely/client.go +++ b/pkg/optimizely/client.go @@ -18,11 +18,14 @@ package optimizely import ( + "context" "errors" optimizelyclient "github.com/optimizely/go-sdk/pkg/client" "github.com/optimizely/go-sdk/pkg/decision" "github.com/optimizely/go-sdk/pkg/entities" + "go.opentelemetry.io/otel" + "go.opentelemetry.io/otel/attribute" ) // ErrEntityNotFound is returned when no entity exists with a given key @@ -81,7 +84,11 @@ func (c *OptlyClient) UpdateConfig() { } // TrackEvent checks for the existence of the event before calling the OptimizelyClient Track method -func (c *OptlyClient) TrackEvent(eventKey string, uc entities.UserContext, eventTags map[string]interface{}) (*Track, error) { +func (c *OptlyClient) TrackEvent(ctx context.Context, eventKey string, uc entities.UserContext, eventTags map[string]interface{}) (*Track, error) { + _, span := otel.Tracer("trackHandler").Start(ctx, "TrackEvent") + defer span.End() + span.SetAttributes(attribute.String("trackEventKey", eventKey)) + tr := &Track{ UserID: uc.ID, EventKey: eventKey, @@ -104,7 +111,10 @@ func (c *OptlyClient) TrackEvent(eventKey string, uc entities.UserContext, event // SetForcedVariation sets a forced variation for the argument experiment key and user ID // Returns false if the same forced variation was already set for the argument experiment and user, true otherwise // Returns an error when forced variations are not available on this OptlyClient instance -func (c *OptlyClient) SetForcedVariation(experimentKey, userID, variationKey string) (*Override, error) { +func (c *OptlyClient) SetForcedVariation(ctx context.Context, experimentKey, userID, variationKey string) (*Override, error) { + _, span := otel.Tracer("overrideHandler").Start(ctx, "SetForcedVariation") + defer span.End() + if c.ForcedVariations == nil { return &Override{}, ErrForcedVariationsUninitialized } @@ -139,12 +149,18 @@ func (c *OptlyClient) SetForcedVariation(experimentKey, userID, variationKey str override.Messages = messages } + span.SetAttributes(attribute.String("variationKey", override.VariationKey)) + span.SetAttributes(attribute.String("experimentKey", override.ExperimentKey)) + c.ForcedVariations.SetVariation(forcedVariationKey, variationKey) return &override, nil } // RemoveForcedVariation removes any forced variation that was previously set for the argument experiment key and user ID -func (c *OptlyClient) RemoveForcedVariation(experimentKey, userID string) (*Override, error) { +func (c *OptlyClient) RemoveForcedVariation(ctx context.Context, experimentKey, userID string) (*Override, error) { + _, span := otel.Tracer("overrideHandler").Start(ctx, "RemoveForcedVariation") + defer span.End() + if c.ForcedVariations == nil { return &Override{}, ErrForcedVariationsUninitialized } @@ -171,12 +187,18 @@ func (c *OptlyClient) RemoveForcedVariation(experimentKey, userID string) (*Over override.Messages = messages c.ForcedVariations.RemoveVariation(forcedVariationKey) + span.SetAttributes(attribute.String("variationKey", override.VariationKey)) + span.SetAttributes(attribute.String("experimentKey", override.ExperimentKey)) + return &override, nil } // ActivateFeature activates a feature for a given user by getting the feature enabled status and all // associated variables -func (c *OptlyClient) ActivateFeature(key string, uc entities.UserContext, disableTracking bool) (*Decision, error) { +func (c *OptlyClient) ActivateFeature(ctx context.Context, key string, uc entities.UserContext, disableTracking bool) (*Decision, error) { + _, span := otel.Tracer("activateHandler").Start(ctx, "ActivateFeature") + defer span.End() + unsafeDecisionInfo, err := c.GetDetailedFeatureDecisionUnsafe(key, uc, disableTracking) if err != nil { return &Decision{}, err @@ -192,11 +214,20 @@ func (c *OptlyClient) ActivateFeature(key string, uc entities.UserContext, disab VariationKey: unsafeDecisionInfo.VariationKey, } + span.SetAttributes(attribute.String("variationKey", dec.VariationKey)) + span.SetAttributes(attribute.String("experimentKey", dec.ExperimentKey)) + span.SetAttributes(attribute.String("featureKey", dec.FeatureKey)) + span.SetAttributes(attribute.Bool("enabled", dec.Enabled)) + span.SetAttributes(attribute.String("type", dec.Type)) + return dec, nil } // ActivateExperiment activates an experiment -func (c *OptlyClient) ActivateExperiment(key string, uc entities.UserContext, disableTracking bool) (*Decision, error) { +func (c *OptlyClient) ActivateExperiment(ctx context.Context, key string, uc entities.UserContext, disableTracking bool) (*Decision, error) { + _, span := otel.Tracer("activateHandler").Start(ctx, "ActivateExperiment") + defer span.End() + var variation string var err error @@ -218,5 +249,10 @@ func (c *OptlyClient) ActivateExperiment(key string, uc entities.UserContext, di Variables: map[string]interface{}{}, } + span.SetAttributes(attribute.String("variationKey", dec.VariationKey)) + span.SetAttributes(attribute.String("experimentKey", dec.ExperimentKey)) + span.SetAttributes(attribute.Bool("enabled", dec.Enabled)) + span.SetAttributes(attribute.String("type", dec.Type)) + return dec, nil } diff --git a/pkg/optimizely/client_test.go b/pkg/optimizely/client_test.go index 0bb347fe..090241f7 100644 --- a/pkg/optimizely/client_test.go +++ b/pkg/optimizely/client_test.go @@ -18,6 +18,7 @@ package optimizely import ( + "context" "fmt" "testing" @@ -65,7 +66,7 @@ func (suite *ClientTestSuite) TestTrackEvent() { eventKey := "eventKey" suite.testClient.AddEvent(entities.Event{Key: eventKey}) tags := map[string]interface{}{"tag": "value"} - actual, err := suite.optlyClient.TrackEvent(eventKey, suite.userContext, tags) + actual, err := suite.optlyClient.TrackEvent(context.Background(), eventKey, suite.userContext, tags) suite.NoError(err) expected := &Track{ @@ -122,7 +123,7 @@ func (suite *ClientTestSuite) TestValidSetForcedVariations() { userID := "testUser" for _, scenario := range scenarios { - actual, err := suite.optlyClient.SetForcedVariation(scenario.experimentKey, userID, scenario.variationKey) + actual, err := suite.optlyClient.SetForcedVariation(context.Background(), scenario.experimentKey, userID, scenario.variationKey) suite.NoError(err) expected := &Override{ @@ -161,10 +162,10 @@ func (suite *ClientTestSuite) TestRemoveForcedVariation() { } userID := "testUser" - _, _ = suite.optlyClient.SetForcedVariation(suite.featureExp.Key, userID, "enabled_var") + _, _ = suite.optlyClient.SetForcedVariation(context.Background(), suite.featureExp.Key, userID, "enabled_var") for _, scenario := range scenarios { - actual, err := suite.optlyClient.RemoveForcedVariation(suite.featureExp.Key, userID) + actual, err := suite.optlyClient.RemoveForcedVariation(context.Background(), suite.featureExp.Key, userID) suite.NoError(err) expected := &Override{ @@ -211,7 +212,7 @@ func (suite *ClientTestSuite) TestActivateFeature() { // Response should be the same regardless of the flag for _, flag := range []bool{true, false} { - actual, err := suite.optlyClient.ActivateFeature(feature.Key, entities.UserContext{ID: "testUser"}, flag) + actual, err := suite.optlyClient.ActivateFeature(context.Background(), feature.Key, entities.UserContext{ID: "testUser"}, flag) suite.NoError(err) suite.Equal(expected, actual) } @@ -237,7 +238,7 @@ func (suite *ClientTestSuite) TestActivateExperiment() { // Response should be the same regardless of the flag for _, flag := range []bool{true, false} { - actual, err := suite.optlyClient.ActivateExperiment(experiment.Key, entities.UserContext{ID: "testUser"}, flag) + actual, err := suite.optlyClient.ActivateExperiment(context.Background(), experiment.Key, entities.UserContext{ID: "testUser"}, flag) suite.NoError(err) suite.Equal(expected, actual) } @@ -314,7 +315,7 @@ func TestTrackErrorConfigManager(t *testing.T) { } uc := entities.UserContext{ID: "userId"} - actual, err := optlyClient.TrackEvent("something", uc, map[string]interface{}{}) + actual, err := optlyClient.TrackEvent(context.Background(), "something", uc, map[string]interface{}{}) assert.EqualError(t, err, "config error") expected := &Track{} @@ -341,7 +342,7 @@ func TestTrackErrorClient(t *testing.T) { } uc := entities.UserContext{ID: "userId"} - actual, err := optlyClient.TrackEvent("something", uc, map[string]interface{}{}) + actual, err := optlyClient.TrackEvent(context.Background(), "something", uc, map[string]interface{}{}) assert.NoError(t, err) expected := &Track{ diff --git a/pkg/optimizely/metrics_test.go b/pkg/optimizely/metrics_test.go index 76ce9c8b..b64ed154 100644 --- a/pkg/optimizely/metrics_test.go +++ b/pkg/optimizely/metrics_test.go @@ -35,7 +35,7 @@ func TestCounterValid(t *testing.T) { rec := httptest.NewRecorder() req := httptest.NewRequest("GET", "/", nil) - metricsRegistry := metrics.NewRegistry() + metricsRegistry := metrics.NewRegistry("") metricsSDKRegistry := NewRegistry(metricsRegistry) counter := metricsSDKRegistry.GetCounter("metrics") @@ -56,7 +56,7 @@ func TestGaugeValid(t *testing.T) { rec := httptest.NewRecorder() req := httptest.NewRequest("GET", "/", nil) - metricsRegistry := metrics.NewRegistry() + metricsRegistry := metrics.NewRegistry("") metricsSDKRegistry := NewRegistry(metricsRegistry) gauge := metricsSDKRegistry.GetGauge("metrics") diff --git a/pkg/routers/api.go b/pkg/routers/api.go index c2e513c8..b7d2aac5 100644 --- a/pkg/routers/api.go +++ b/pkg/routers/api.go @@ -64,7 +64,6 @@ func forbiddenHandler(message string) http.HandlerFunc { // NewDefaultAPIRouter creates a new router with the default backing optimizely.Cache func NewDefaultAPIRouter(optlyCache optimizely.Cache, conf config.APIConfig, metricsRegistry *metrics.Registry) http.Handler { - authProvider := middleware.NewAuth(&conf.Auth) if authProvider == nil { log.Error().Msg("unable to initialize api auth middleware.") @@ -134,6 +133,18 @@ func WithAPIRouter(opt *APIOptions, r chi.Router) { createAccesstokenTimer := middleware.Metricize("create-api-access-token", opt.metricsRegistry) contentTypeMiddleware := chimw.AllowContentType("application/json") + configTracer := middleware.AddTracing("configHandler", "OptimizelyConfig") + datafileTracer := middleware.AddTracing("datafileHandler", "OptimizelyDatafile") + activateTracer := middleware.AddTracing("activateHandler", "Activate") + decideTracer := middleware.AddTracing("decideHandler", "Decide") + trackTracer := middleware.AddTracing("trackHandler", "Track") + overrideTracer := middleware.AddTracing("overrideHandler", "Override") + lookupTracer := middleware.AddTracing("lookupHandler", "Lookup") + saveTracer := middleware.AddTracing("saveHandler", "Save") + sendOdpEventTracer := middleware.AddTracing("sendOdpEventHandler", "SendOdpEvent") + nStreamTracer := middleware.AddTracing("notificationHandler", "SendNotificationEvent") + authTracer := middleware.AddTracing("authHandler", "AuthToken") + if opt.maxConns > 0 { // Note this is NOT a rate limiter, but a concurrency threshold r.Use(chimw.Throttle(opt.maxConns)) @@ -144,19 +155,19 @@ func WithAPIRouter(opt *APIOptions, r chi.Router) { r.Route("/v1", func(r chi.Router) { r.Use(opt.corsHandler, opt.sdkMiddleware) - r.With(getConfigTimer, opt.oAuthMiddleware).Get("/config", opt.configHandler) - r.With(getDatafileTimer, opt.oAuthMiddleware).Get("/datafile", opt.datafileHandler) - r.With(activateTimer, opt.oAuthMiddleware, contentTypeMiddleware).Post("/activate", opt.activateHandler) - r.With(decideTimer, opt.oAuthMiddleware, contentTypeMiddleware).Post("/decide", opt.decideHandler) - r.With(trackTimer, opt.oAuthMiddleware, contentTypeMiddleware).Post("/track", opt.trackHandler) - r.With(overrideTimer, opt.oAuthMiddleware, contentTypeMiddleware).Post("/override", opt.overrideHandler) - r.With(lookupTimer, opt.oAuthMiddleware, contentTypeMiddleware).Post("/lookup", opt.lookupHandler) - r.With(saveTimer, opt.oAuthMiddleware, contentTypeMiddleware).Post("/save", opt.saveHandler) - r.With(sendOdpEventTimer, opt.oAuthMiddleware, contentTypeMiddleware).Post("/send-odp-event", opt.sendOdpEventHandler) - r.With(opt.oAuthMiddleware).Get("/notifications/event-stream", opt.nStreamHandler) + r.With(getConfigTimer, opt.oAuthMiddleware, configTracer).Get("/config", opt.configHandler) + r.With(getDatafileTimer, opt.oAuthMiddleware, datafileTracer).Get("/datafile", opt.datafileHandler) + r.With(activateTimer, opt.oAuthMiddleware, contentTypeMiddleware, activateTracer).Post("/activate", opt.activateHandler) + r.With(decideTimer, opt.oAuthMiddleware, contentTypeMiddleware, decideTracer).Post("/decide", opt.decideHandler) + r.With(trackTimer, opt.oAuthMiddleware, contentTypeMiddleware, trackTracer).Post("/track", opt.trackHandler) + r.With(overrideTimer, opt.oAuthMiddleware, contentTypeMiddleware, overrideTracer).Post("/override", opt.overrideHandler) + r.With(lookupTimer, opt.oAuthMiddleware, contentTypeMiddleware, lookupTracer).Post("/lookup", opt.lookupHandler) + r.With(saveTimer, opt.oAuthMiddleware, contentTypeMiddleware, saveTracer).Post("/save", opt.saveHandler) + r.With(sendOdpEventTimer, opt.oAuthMiddleware, contentTypeMiddleware, sendOdpEventTracer).Post("/send-odp-event", opt.sendOdpEventHandler) + r.With(opt.oAuthMiddleware, nStreamTracer).Get("/notifications/event-stream", opt.nStreamHandler) }) - r.With(createAccesstokenTimer).Post("/oauth/token", opt.oAuthHandler) + r.With(createAccesstokenTimer, authTracer).Post("/oauth/token", opt.oAuthHandler) statikFS, err := fs.New() if err != nil { diff --git a/pkg/routers/api_test.go b/pkg/routers/api_test.go index 2f0fb84f..bc7776e9 100644 --- a/pkg/routers/api_test.go +++ b/pkg/routers/api_test.go @@ -33,7 +33,7 @@ import ( "github.com/optimizely/agent/pkg/optimizely/optimizelytest" ) -var metricsRegistry = metrics.NewRegistry() +var metricsRegistry = metrics.NewRegistry("") const methodHeaderKey = "X-Method-Header" const clientHeaderKey = "X-Client-Header" @@ -373,8 +373,7 @@ func TestNewDefaultClientRouterInvalidMiddlewareConfig(t *testing.T) { } func TestForbiddenRoutes(t *testing.T) { - conf := config.APIConfig{} - mux := NewDefaultAPIRouter(MockCache{}, conf, metricsRegistry) + mux := NewDefaultAPIRouter(MockCache{}, config.APIConfig{}, metricsRegistry) routes := []struct { method string