From d6fb629f013bc25764f07968d107a9ba21745956 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E5=B0=8F=E5=93=88=E7=A7=8B?= <765390765@qq.com> Date: Thu, 5 May 2022 22:58:39 +0800 Subject: [PATCH] Feat/tracing (#200) * support tracing Co-authored-by: fm --- client/manager.go | 10 +- examples/sample-plugin/main.go | 2 +- go.mod | 38 +++-- go.sum | 102 +++++++++-- multicluster/clusterregistry_client.go | 3 + plugin/client/plugin_client.go | 3 + sharedmain/app.go | 56 +++---- sharedmain/main.go | 2 + tracing/config.go | 145 ++++++++++++++++ tracing/config_test.go | 128 ++++++++++++++ tracing/filter.go | 77 +++++++++ tracing/filter_test.go | 74 ++++++++ tracing/options.go | 90 ++++++++++ tracing/options_test.go | 216 ++++++++++++++++++++++++ tracing/setup.go | 99 +++++++++++ tracing/setup_test.go | 162 ++++++++++++++++++ tracing/suite_test.go | 103 ++++++++++++ tracing/tracing.go | 224 +++++++++++++++++++++++++ tracing/transport.go | 102 +++++++++++ tracing/transport_test.go | 133 +++++++++++++++ 20 files changed, 1705 insertions(+), 64 deletions(-) create mode 100644 tracing/config.go create mode 100644 tracing/config_test.go create mode 100644 tracing/filter.go create mode 100644 tracing/filter_test.go create mode 100644 tracing/options.go create mode 100644 tracing/options_test.go create mode 100644 tracing/setup.go create mode 100644 tracing/setup_test.go create mode 100644 tracing/suite_test.go create mode 100644 tracing/tracing.go create mode 100644 tracing/transport.go create mode 100644 tracing/transport_test.go diff --git a/client/manager.go b/client/manager.go index d043829539..7f97e4763e 100644 --- a/client/manager.go +++ b/client/manager.go @@ -20,6 +20,7 @@ import ( "context" "time" + "github.com/katanomi/pkg/tracing" "k8s.io/client-go/dynamic" "github.com/emicklei/go-restful/v3" @@ -50,12 +51,19 @@ func NewManager(ctx context.Context, get GetConfigFunc, baseConfig GetBaseConfig if get == nil { get = FromBearerToken } + configGetter := func(req *restful.Request, baseConfig GetBaseConfigFunc) (*rest.Config, error) { + cfg, err := get(req, baseConfig) + if cfg != nil { + cfg.Wrap(tracing.WrapTransport) + } + return cfg, err + } if baseConfig == nil { baseConfig = config.GetConfig } return &Manager{ SugaredLogger: logging.FromContext(ctx), - GetConfig: get, + GetConfig: configGetter, GetBasicConfig: baseConfig, } } diff --git a/examples/sample-plugin/main.go b/examples/sample-plugin/main.go index 1a8ab44655..e91037f960 100644 --- a/examples/sample-plugin/main.go +++ b/examples/sample-plugin/main.go @@ -26,7 +26,7 @@ import ( func main() { sharedmain.App("harbor-example"). Log(). - Tracing(nil). + Tracing(). Profiling(). APIDocs(). Plugins( diff --git a/go.mod b/go.mod index adde64559b..ea985b1840 100644 --- a/go.mod +++ b/go.mod @@ -15,15 +15,15 @@ require ( github.com/go-openapi/spec v0.20.2 // indirect github.com/go-resty/resty/v2 v2.6.0 github.com/golang/mock v1.6.0 - github.com/google/go-cmp v0.5.6 + github.com/google/go-cmp v0.5.7 github.com/googleapis/gnostic v0.5.3 // indirect github.com/jarcoal/httpmock v1.0.8 - github.com/onsi/ginkgo v1.14.1 - github.com/onsi/gomega v1.10.3 + github.com/onsi/ginkgo v1.16.5 + github.com/onsi/gomega v1.16.0 github.com/opencontainers/image-spec v1.0.2 github.com/opentracing/opentracing-go v1.1.0 github.com/prometheus/client_golang v1.11.0 - github.com/sirupsen/logrus v1.7.0 // indirect + github.com/sirupsen/logrus v1.8.1 // indirect github.com/uber/jaeger-client-go v2.29.1+incompatible github.com/uber/jaeger-lib v2.4.1+incompatible go.uber.org/zap v1.18.1 @@ -42,6 +42,16 @@ require ( yunion.io/x/pkg v0.0.0-20210218105412-13a69f60034c ) +require ( + go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.27.0 + go.opentelemetry.io/otel v1.2.0 + go.opentelemetry.io/otel/exporters/jaeger v1.2.0 + go.opentelemetry.io/otel/exporters/zipkin v1.2.0 + go.opentelemetry.io/otel/metric v0.25.0 // indirect + go.opentelemetry.io/otel/sdk v1.2.0 + go.opentelemetry.io/otel/trace v1.2.0 +) + require ( contrib.go.opencensus.io/exporter/ocagent v0.7.1-0.20200907061046-05415f1de66d // indirect contrib.go.opencensus.io/exporter/prometheus v0.3.0 // indirect @@ -55,6 +65,7 @@ require ( github.com/cespare/xxhash/v2 v2.1.1 // indirect github.com/evanphx/json-patch v4.9.0+incompatible // indirect github.com/evanphx/json-patch/v5 v5.5.0 // indirect + github.com/felixge/httpsnoop v1.0.2 // indirect github.com/fsnotify/fsnotify v1.4.9 // indirect github.com/go-openapi/jsonpointer v0.19.5 // indirect github.com/go-openapi/jsonreference v0.19.5 // indirect @@ -64,7 +75,7 @@ require ( github.com/golang/protobuf v1.5.2 // indirect github.com/google/gofuzz v1.2.0 // indirect github.com/google/uuid v1.2.0 // indirect - github.com/grpc-ecosystem/grpc-gateway v1.14.8 // indirect + github.com/grpc-ecosystem/grpc-gateway v1.16.0 // indirect github.com/hashicorp/golang-lru v0.5.4 // indirect github.com/imdario/mergo v0.3.10 // indirect github.com/josharian/intern v1.0.0 // indirect @@ -77,8 +88,9 @@ require ( github.com/mgutz/ansi v0.0.0-20170206155736-9520e82c474b // indirect github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect github.com/modern-go/reflect2 v1.0.1 // indirect - github.com/nxadm/tail v1.4.4 // indirect + github.com/nxadm/tail v1.4.8 // indirect github.com/opencontainers/go-digest v1.0.0 // indirect + github.com/openzipkin/zipkin-go v0.4.0 // indirect github.com/pkg/errors v0.9.1 // indirect github.com/prometheus/client_model v0.2.0 // indirect github.com/prometheus/common v0.26.0 // indirect @@ -86,22 +98,22 @@ require ( github.com/prometheus/statsd_exporter v0.20.0 // indirect github.com/spf13/pflag v1.0.5 // indirect go.opencensus.io v0.23.0 // indirect + go.opentelemetry.io/otel/internal/metric v0.25.0 // indirect go.uber.org/atomic v1.8.0 // indirect go.uber.org/automaxprocs v1.4.0 // indirect go.uber.org/multierr v1.6.0 // indirect - golang.org/x/crypto v0.0.0-20210513164829-c07d793c2f9a // indirect - golang.org/x/net v0.0.0-20210614182718-04defd469f4e // indirect + golang.org/x/crypto v0.0.0-20210920023735-84f357641f63 // indirect + golang.org/x/net v0.0.0-20210917221730-978cfadd31cf // indirect golang.org/x/oauth2 v0.0.0-20210628180205-a41e5a781914 // indirect - golang.org/x/sys v0.0.0-20210603081109-ebe580a85c40 // indirect + golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1 // indirect golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1 // indirect - golang.org/x/text v0.3.6 // indirect - golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1 // indirect + golang.org/x/text v0.3.7 // indirect gomodules.xyz/jsonpatch/v2 v2.2.0 // indirect google.golang.org/api v0.36.0 // indirect google.golang.org/appengine v1.6.7 // indirect google.golang.org/genproto v0.0.0-20210416161957-9910b6c460de // indirect - google.golang.org/grpc v1.38.0 // indirect - google.golang.org/protobuf v1.26.0 // indirect + google.golang.org/grpc v1.41.0 // indirect + google.golang.org/protobuf v1.27.1 // indirect gopkg.in/alecthomas/kingpin.v2 v2.2.6 // indirect gopkg.in/inf.v0 v0.9.1 // indirect gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7 // indirect diff --git a/go.sum b/go.sum index 7b8aa342b2..afd8e9faf3 100644 --- a/go.sum +++ b/go.sum @@ -60,7 +60,9 @@ github.com/PuerkitoBio/purell v1.1.1/go.mod h1:c11w/QuzBsJSee3cPx9rAFu61PvFxuPbt github.com/PuerkitoBio/urlesc v0.0.0-20170810143723-de5bf2ad4578 h1:d+Bc7a5rLufV/sSk/8dngufqelfh6jnri85riMAaF/M= github.com/PuerkitoBio/urlesc v0.0.0-20170810143723-de5bf2ad4578/go.mod h1:uGdkoq3SwY9Y+13GIhn11/XLaGBb4BfwItxLd5jeuXE= github.com/Shopify/sarama v1.19.0/go.mod h1:FVkBWblsNy7DGZRfXLU0O9RCGt5g3g3yEuWXgklEdEo= +github.com/Shopify/sarama v1.30.0/go.mod h1:zujlQQx1kzHsh4jfV1USnptCQrHAEZ2Hk8fTKCulPVs= github.com/Shopify/toxiproxy v2.1.4+incompatible/go.mod h1:OXgGpZ6Cli1/URJOF1DMxUHB2q5Ap20/P/eIdh4G0pI= +github.com/Shopify/toxiproxy/v2 v2.1.6-0.20210914104332-15ea381dcdae/go.mod h1:/cvHQkZ1fst0EmZnA5dFtiQdWCNCFYzb+uE2vqVgvx0= github.com/VividCortex/gohistogram v1.0.0/go.mod h1:Pf5mBqqDxYaXu3hDrrU+w6nw50o/4+TcAqDqk/vUH7g= github.com/afex/hystrix-go v0.0.0-20180502004556-fa1af6a1f4f5/go.mod h1:SkGFH1ia65gfNATL8TAiHDNxPzPdmEL5uirI2Uyuz6c= github.com/ajstarks/svgo v0.0.0-20180226025133-644b8db467af/go.mod h1:K08gAheRH3/J6wwsYMMT4xOr94bZjxIelGM0+d/wbFw= @@ -119,6 +121,7 @@ github.com/cloudevents/sdk-go/v2 v2.5.0 h1:Ts6aLHbBUJfcNcZ4ouAfJ4+Np7SE1Yf2w4ADK github.com/cloudevents/sdk-go/v2 v2.5.0/go.mod h1:nlXhgFkf0uTopxmRXalyMwS2LG70cRGPrxzmjJgSG0U= github.com/cncf/udpa/go v0.0.0-20191209042840-269d4d468f6f/go.mod h1:M8M6+tZqaGXZJjfX53e64911xZQV5JYwmTeXPW+k8Sc= github.com/cncf/udpa/go v0.0.0-20201120205902-5459f2c99403/go.mod h1:WmhPx2Nbnhtbo57+VJT5O0JRkEi1Wbu0z5j0R8u5Hbk= +github.com/cncf/xds/go v0.0.0-20210805033703-aa0b78936158/go.mod h1:eXthEFrGJvWHgFFCl3hGmgk+/aYT6PnTQLykKQRLhEs= github.com/cockroachdb/datadriven v0.0.0-20190809214429-80d97fb3cbaa/go.mod h1:zn76sxSg3SzpJ0PPJaLDCu+Bu0Lg3sKTORVIj19EIF8= github.com/codahale/hdrhistogram v0.0.0-20161010025455-3a0bb77429bd/go.mod h1:sE/e/2PUdi/liOCUjSTXgM1o87ZssimdTWN964YiIeI= github.com/coreos/bbolt v1.3.2/go.mod h1:iRUV2dpdMOn7Bo10OQBFzIJO9kkE559Wcmn+qkEiiKk= @@ -147,6 +150,7 @@ github.com/docopt/docopt-go v0.0.0-20180111231733-ee0de3bc6815/go.mod h1:WwZ+bS3 github.com/dustin/go-humanize v0.0.0-20171111073723-bb3d318650d4/go.mod h1:HtrtbFcZ19U5GC7JDqmcUSB87Iq5E25KnS6fMYU6eOk= github.com/dustin/go-humanize v1.0.0/go.mod h1:HtrtbFcZ19U5GC7JDqmcUSB87Iq5E25KnS6fMYU6eOk= github.com/eapache/go-resiliency v1.1.0/go.mod h1:kFI+JgMyC7bLPUVY133qvEBtVayf5mFgVsvEsIPBvNs= +github.com/eapache/go-resiliency v1.2.0/go.mod h1:kFI+JgMyC7bLPUVY133qvEBtVayf5mFgVsvEsIPBvNs= github.com/eapache/go-xerial-snappy v0.0.0-20180814174437-776d5712da21/go.mod h1:+020luEh2TKB4/GOp8oxxtq0Daoen/Cii55CzbTV6DU= github.com/eapache/queue v1.1.0/go.mod h1:6eCeP0CKFpHLu8blIFXhExK/dRa7WDZfr6jVFPTqq+I= github.com/edsrzf/mmap-go v1.0.0/go.mod h1:YO35OhQPt3KJa3ryjFM5Bs14WD66h8eGKpfaBNrHW5M= @@ -165,6 +169,7 @@ github.com/envoyproxy/go-control-plane v0.9.1-0.20191026205805-5f8ba28d4473/go.m github.com/envoyproxy/go-control-plane v0.9.4/go.mod h1:6rpuAdCZL397s3pYoYcLgu1mIlRU8Am5FuJP05cCM98= github.com/envoyproxy/go-control-plane v0.9.9-0.20201210154907-fd9021fe5dad/go.mod h1:cXg6YxExXjJnVBQHBLXeUAgxn2UodCpnH306RInaBQk= github.com/envoyproxy/go-control-plane v0.9.9-0.20210217033140-668b12f5399d/go.mod h1:cXg6YxExXjJnVBQHBLXeUAgxn2UodCpnH306RInaBQk= +github.com/envoyproxy/go-control-plane v0.9.10-0.20210907150352-cf90f659a021/go.mod h1:AFq3mo9L8Lqqiid3OhADV3RfLJnjiw63cSpi+fDTRC0= github.com/envoyproxy/protoc-gen-validate v0.1.0/go.mod h1:iSmxcyjqTsJpI2R4NaDN7+kN2VEUnK/pcBlmesArF7c= github.com/evanphx/json-patch v0.5.2/go.mod h1:ZWS5hhDbVDyob71nXKNL0+PWn6ToqBHMikGIFbs31qQ= github.com/evanphx/json-patch v4.5.0+incompatible/go.mod h1:50XU6AFN0ol/bzJsmQLiYLvXMP4fmwYFNcr97nuDLSk= @@ -173,10 +178,14 @@ github.com/evanphx/json-patch v4.9.0+incompatible/go.mod h1:50XU6AFN0ol/bzJsmQLi github.com/evanphx/json-patch/v5 v5.5.0 h1:bAmFiUJ+o0o2B4OiTFeE3MqCOtyo+jjPP9iZ0VRxYUc= github.com/evanphx/json-patch/v5 v5.5.0/go.mod h1:G79N1coSVB93tBe7j6PhzjmR3/2VvlbKOFpnXhI9Bw4= github.com/fatih/color v1.7.0/go.mod h1:Zm6kSWBoL9eyXnKyktHP6abPY2pDugNf5KwzbycvMj4= +github.com/felixge/httpsnoop v1.0.2 h1:+nS9g82KMXccJ/wp0zyRW9ZBHFETmMGtkk+2CTTrW4o= +github.com/felixge/httpsnoop v1.0.2/go.mod h1:m8KPJKqk1gH5J9DgRY2ASl2lWCfGKXixSwevea8zH2U= github.com/fogleman/gg v1.2.1-0.20190220221249-0403632d5b90/go.mod h1:R/bRT+9gY/C5z7JzPU0zXsXHKM4/ayA+zqcVNZzPa1k= github.com/form3tech-oss/jwt-go v3.2.2+incompatible/go.mod h1:pbq4aXjuKjdthFRnoDwaVPLA+WlJuPGy+QneDUgJi2k= +github.com/fortytw2/leaktest v1.3.0/go.mod h1:jDsjWgpAGjm2CA7WthBh/CdZYEPF31XHquHwclZch5g= github.com/franela/goblin v0.0.0-20200105215937-c9ffbefa60db/go.mod h1:7dvUGVsVBjqR7JHJk0brhHOZYGmfBYOrK0ZhYMEtBr4= github.com/franela/goreq v0.0.0-20171204163338-bcd34c9993f8/go.mod h1:ZhphrRTfi2rbfLwlschooIH4+wKKDR4Pdxhh+TRoA20= +github.com/frankban/quicktest v1.11.3/go.mod h1:wRf/ReqHper53s+kmmSZizM8NamnL3IM0I9ntUbOk+k= github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo= github.com/fsnotify/fsnotify v1.4.9 h1:hsms1Qyu0jgnwNXIxa+/V/PDsU6CfLf6CNO8H7IWoS4= github.com/fsnotify/fsnotify v1.4.9/go.mod h1:znqG4EE+3YCdAaPaxE2ZRY/06pZUdp0tY4IgpuI1SZQ= @@ -221,6 +230,7 @@ github.com/go-resty/resty/v2 v2.6.0 h1:joIR5PNLM2EFqqESUjCMGXrWmXNHEU9CEiK813oKY github.com/go-resty/resty/v2 v2.6.0/go.mod h1:PwvJS6hvaPkjtjNg9ph+VrSD92bi5Zq73w/BIH7cC3Q= github.com/go-sql-driver/mysql v1.4.0/go.mod h1:zAC/RDZ24gD3HViQzih4MyKcchzm+sOG5ZlKdlhCg5w= github.com/go-stack/stack v1.8.0/go.mod h1:v0f6uXyyMGvRgIKkXu+yp6POWl0qKG85gN/melR3HDY= +github.com/go-task/slim-sprig v0.0.0-20210107165309-348f09dbbbc0/go.mod h1:fyg7847qk6SyHyPtNmDHnmrv/HOrqktSC+C9fM+CJOE= github.com/gobuffalo/flect v0.2.3/go.mod h1:vmkQwuZYhN5Pc4ljYQZzP+1sq+NEkK+lh20jmEmX3jc= github.com/gogo/googleapis v1.1.0/go.mod h1:gf4bu3Q80BeJ6H1S1vYPm8/ELATdvryBaNFGgqEef3s= github.com/gogo/protobuf v1.1.1/go.mod h1:r8qH/GZQm5c6nD/R0oafs1akxWv10x8SbQlK7atdtwQ= @@ -264,6 +274,7 @@ github.com/golang/protobuf v1.5.0/go.mod h1:FsONVRAS9T7sI+LIUmWTfcYkHO4aIWwzhcaS github.com/golang/protobuf v1.5.2 h1:ROPKBNFfQgOUMifHyP+KYbvpjbdoFNs+aK7DXlji0Tw= github.com/golang/protobuf v1.5.2/go.mod h1:XVQd3VNwM+JqD3oG2Ue2ip4fOMUkwXdXDdiuN0vRsmY= github.com/golang/snappy v0.0.0-20180518054509-2e65f85255db/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q= +github.com/golang/snappy v0.0.4/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q= github.com/gonum/blas v0.0.0-20181208220705-f22b278b28ac/go.mod h1:P32wAyui1PQ58Oce/KYkOqQv8cVw1zAapXOl+dRFGbc= github.com/gonum/diff v0.0.0-20181124234638-500114f11e71/go.mod h1:22dM4PLscQl+Nzf64qNBurVJvfyvZELT0iRW2l/NN70= github.com/gonum/floats v0.0.0-20181209220543-c233463c7e82/go.mod h1:PxC8OnwL11+aosOB5+iEPoV3picfs8tUpkVd0pDo+Kg= @@ -286,8 +297,9 @@ github.com/google/go-cmp v0.5.2/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/ github.com/google/go-cmp v0.5.3/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.6 h1:BKbKCqvP6I+rmFHt06ZmyQtvB8xAkWdhFyr0ZUNZcxQ= github.com/google/go-cmp v0.5.6/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= +github.com/google/go-cmp v0.5.7 h1:81/ik6ipDQS2aGcBfIN5dHDB36BwrStyeAQquSYCV4o= +github.com/google/go-cmp v0.5.7/go.mod h1:n+brtR0CgQNWTVd5ZUFpTBC8YFBDLK/h/bpaJ8/DtOE= github.com/google/go-github/v27 v27.0.6/go.mod h1:/0Gr8pJ55COkmv+S/yPKCczSkUPIM/LnFyubufRNIS0= github.com/google/go-querystring v1.0.0/go.mod h1:odCYkC5MyYFN7vkCjXpyrEuKhc/BUO6wN/zVPAxq5ck= github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg= @@ -323,6 +335,9 @@ github.com/gorilla/context v1.1.1/go.mod h1:kBGZzfjB9CEq2AlWe17Uuf7NDRt0dE0s8S51 github.com/gorilla/mux v1.6.2/go.mod h1:1lud6UwP+6orDFRuTfBEV8e9/aOM/c4fVVCaMa2zaAs= github.com/gorilla/mux v1.7.3/go.mod h1:1lud6UwP+6orDFRuTfBEV8e9/aOM/c4fVVCaMa2zaAs= github.com/gorilla/mux v1.7.4/go.mod h1:DVbg23sWSpFRCP0SfiEN6jmj59UnW/n46BH5rLB71So= +github.com/gorilla/mux v1.8.0/go.mod h1:DVbg23sWSpFRCP0SfiEN6jmj59UnW/n46BH5rLB71So= +github.com/gorilla/securecookie v1.1.1/go.mod h1:ra0sb63/xPlUeL+yeDciTfxMRAA+MP+HVt/4epWDjd4= +github.com/gorilla/sessions v1.2.1/go.mod h1:dk2InVEVJ0sfLlnXv9EAgkf6ecYs/i80K/zI+bUmuGM= github.com/gorilla/websocket v0.0.0-20170926233335-4201258b820c/go.mod h1:E7qHFY5m1UJ88s3WnNqhKjPHQ0heANvMoAMk2YaljkQ= github.com/gorilla/websocket v1.4.2/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE= github.com/gregjones/httpcache v0.0.0-20180305231024-9cad4c3443a7/go.mod h1:FecbI9+v66THATjSRHfNgh1IVFe/9kFxbXtjV0ctIMA= @@ -332,8 +347,9 @@ github.com/grpc-ecosystem/go-grpc-prometheus v1.2.0/go.mod h1:8NvIoxWQoOIhqOTXgf github.com/grpc-ecosystem/grpc-gateway v1.9.0/go.mod h1:vNeuVxBJEsws4ogUvrchl83t/GYV9WGTSLVdBhOQFDY= github.com/grpc-ecosystem/grpc-gateway v1.9.5/go.mod h1:vNeuVxBJEsws4ogUvrchl83t/GYV9WGTSLVdBhOQFDY= github.com/grpc-ecosystem/grpc-gateway v1.14.6/go.mod h1:zdiPV4Yse/1gnckTHtghG4GkDEdKCRJduHpTxT3/jcw= -github.com/grpc-ecosystem/grpc-gateway v1.14.8 h1:hXClj+iFpmLM8i3lkO6i4Psli4P2qObQuQReiII26U8= github.com/grpc-ecosystem/grpc-gateway v1.14.8/go.mod h1:NZE8t6vs6TnwLL/ITkaK8W3ecMLGAbh2jXTclvpiwYo= +github.com/grpc-ecosystem/grpc-gateway v1.16.0 h1:gmcG1KaJ57LophUzW0Hy8NmPhnMZb4M0+kPpLofRdBo= +github.com/grpc-ecosystem/grpc-gateway v1.16.0/go.mod h1:BDjrQk3hbvj6Nolgz8mAMFbcEtjT1g+wF4CSlocrBnw= github.com/hashicorp/consul/api v1.1.0/go.mod h1:VmuI/Lkw1nC05EYQWNKwWGbkg+FbDBtguAZLlVdkD9Q= github.com/hashicorp/consul/api v1.3.0/go.mod h1:MmDNSzIMUjNpY/mQ398R4bk2FnqQLoPndWW5VkKPlCE= github.com/hashicorp/consul/sdk v0.1.1/go.mod h1:VKf9jXwCTEY1QZP2MOLRhb5i/I/ssyNV1vwHyQBF0x8= @@ -348,6 +364,7 @@ github.com/hashicorp/go-sockaddr v1.0.0/go.mod h1:7Xibr9yA9JjQq1JpNB2Vw7kxv8xerX github.com/hashicorp/go-syslog v1.0.0/go.mod h1:qPfqrKkXGihmCqbJM2mZgkZGvKG1dFdvsLplgctolz4= github.com/hashicorp/go-uuid v1.0.0/go.mod h1:6SBZvOh/SIDV7/2o3Jml5SYk/TvGqwFJ/bN7x4byOro= github.com/hashicorp/go-uuid v1.0.1/go.mod h1:6SBZvOh/SIDV7/2o3Jml5SYk/TvGqwFJ/bN7x4byOro= +github.com/hashicorp/go-uuid v1.0.2/go.mod h1:6SBZvOh/SIDV7/2o3Jml5SYk/TvGqwFJ/bN7x4byOro= github.com/hashicorp/go-version v1.2.0/go.mod h1:fltr4n8CU8Ke44wwGCBoEymUuxUHl09ZGVZPK5anwXA= github.com/hashicorp/go.net v0.0.1/go.mod h1:hjKkEWcCURg++eb33jQU7oqQcI9XDCnUzHA0oac0k90= github.com/hashicorp/golang-lru v0.5.0/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8= @@ -373,6 +390,12 @@ github.com/influxdata/tdigest v0.0.0-20180711151920-a7d76c6f093a/go.mod h1:9Gkys github.com/influxdata/tdigest v0.0.0-20181121200506-bf2b5ad3c0a9/go.mod h1:Js0mqiSBE6Ffsg94weZZ2c+v/ciT8QRHFOap7EKDrR0= github.com/jarcoal/httpmock v1.0.8 h1:8kI16SoO6LQKgPE7PvQuV+YuD/inwHd7fOOe2zMbo4k= github.com/jarcoal/httpmock v1.0.8/go.mod h1:ATjnClrvW/3tijVmpL/va5Z3aAyGvqU3gCT8nX0Txik= +github.com/jcmturner/aescts/v2 v2.0.0/go.mod h1:AiaICIRyfYg35RUkr8yESTqvSy7csK90qZ5xfvvsoNs= +github.com/jcmturner/dnsutils/v2 v2.0.0/go.mod h1:b0TnjGOvI/n42bZa+hmXL+kFJZsFT7G4t3HTlQ184QM= +github.com/jcmturner/gofork v1.0.0/go.mod h1:MK8+TM0La+2rjBD4jE12Kj1pCCxK7d2LK/UM3ncEo0o= +github.com/jcmturner/goidentity/v6 v6.0.1/go.mod h1:X1YW3bgtvwAXju7V3LCIMpY0Gbxyjn/mY9zx4tFonSg= +github.com/jcmturner/gokrb5/v8 v8.4.2/go.mod h1:sb+Xq/fTY5yktf/VxLsE3wlfPqQjp0aWNYyvBVK62bc= +github.com/jcmturner/rpc/v2 v2.0.3/go.mod h1:VUJYCIDm3PVOEHw8sgt091/20OJjskO/YJki3ELg/Hc= github.com/jessevdk/go-flags v1.4.0/go.mod h1:4FA24M0QyGHXBuZZK/XkWh8h0e1EYbRYJSGM75WSRxI= github.com/jmespath/go-jmespath v0.0.0-20180206201540-c2b33e8439af/go.mod h1:Nht3zPeWKUH0NzdCt2Blrr5ys8VGpn0CEB0cQHVjt7k= github.com/jonboulle/clockwork v0.1.0/go.mod h1:Ii8DK3G1RaLaWxj9trq07+26W01tbo22gdxWY5EU2bo= @@ -401,11 +424,14 @@ github.com/kisielk/errcheck v1.1.0/go.mod h1:EZBBE59ingxPouuu3KfxchcWSUPOHkagtvW github.com/kisielk/errcheck v1.2.0/go.mod h1:/BMXB+zMLi60iA8Vv6Ksmxu/1UDYcXs4uQLJ+jE2L00= github.com/kisielk/errcheck v1.5.0/go.mod h1:pFxgyoBC7bSaBwPgfKdkLd5X25qrDl4LWUI2bnpBCr8= github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck= +github.com/klauspost/compress v1.13.6/go.mod h1:/3/Vjq9QcHkK5uEr5lBEmyoZ1iFhe47etQ6QUkpK6sk= 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/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.2.0/go.mod h1:ipq/a2n7PKx3OHsz4KJII5eveXtPO4qwEXGdVfWzfnI= +github.com/kr/pretty v0.2.1 h1:Fmg33tUaq4/8ym9TJN1x7sLJnHVwhP33CNkpYV/7rwI= +github.com/kr/pretty v0.2.1/go.mod h1:ipq/a2n7PKx3OHsz4KJII5eveXtPO4qwEXGdVfWzfnI= github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ= github.com/kr/pty v1.1.5/go.mod h1:9r2w37qlBe7rQ6e1fg1S/9xpWHSnaqNdHD3WcMdbPDA= github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI= @@ -465,10 +491,10 @@ github.com/nats-io/nats.go v1.9.1/go.mod h1:ZjDU1L/7fJ09jvUSRVBR2e7+RnLiiIQyqyzE github.com/nats-io/nkeys v0.1.0/go.mod h1:xpnFELMwJABBLVhffcfd1MZx6VsNRFpEugbxziKVo7w= github.com/nats-io/nkeys v0.1.3/go.mod h1:xpnFELMwJABBLVhffcfd1MZx6VsNRFpEugbxziKVo7w= github.com/nats-io/nuid v1.0.1/go.mod h1:19wcPz3Ph3q0Jbyiqsd0kePYG7A95tJPxeL+1OSON2c= -github.com/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e h1:fD57ERR4JtEqsWbfPhv4DMiApHyliiK5xCTNVSPiaAs= github.com/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e/go.mod h1:zD1mROLANZcx1PVRCS0qkT7pwLkGfwJo4zjcN/Tysno= -github.com/nxadm/tail v1.4.4 h1:DQuhQpB1tVlglWS2hLQ5OV6B5r8aGxSrPc5Qo6uTN78= github.com/nxadm/tail v1.4.4/go.mod h1:kenIhsEOeOJmVchQTgglprH7qJGnHDVpk1VPCcaMI8A= +github.com/nxadm/tail v1.4.8 h1:nPr65rt6Y5JFSKQO7qToXr7pePgD6Gwiw05lkbyAQTE= +github.com/nxadm/tail v1.4.8/go.mod h1:+ncqLTQzXmGhMZNUePPaPqPvBxHAIsmXswZKocGu+AU= github.com/oklog/oklog v0.3.2/go.mod h1:FCV+B7mhrz4o+ueLpx+KqkyXRGMWOYEvfiXtdGtbWGs= github.com/oklog/run v1.0.0/go.mod h1:dlhp/R75TPv97u0XWUtDeV/lRKWPKSdTuV0TZvrmrQA= github.com/oklog/ulid v1.3.1/go.mod h1:CirwcVhetQ6Lv90oh/F+FBtV6XMibvdAFo93nm5qn4U= @@ -478,16 +504,18 @@ github.com/onsi/ginkgo v1.6.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+W github.com/onsi/ginkgo v1.7.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE= github.com/onsi/ginkgo v1.11.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE= github.com/onsi/ginkgo v1.12.1/go.mod h1:zj2OWP4+oCPe1qIXoGWkgMRwljMUYCdkwsT2108oapk= -github.com/onsi/ginkgo v1.14.1 h1:jMU0WaQrP0a/YAEq8eJmJKjBoMs+pClEr1vDMlM/Do4= github.com/onsi/ginkgo v1.14.1/go.mod h1:iSB4RoI2tjJc9BBv4NKIKWKya62Rps+oPG/Lv9klQyY= +github.com/onsi/ginkgo v1.16.4/go.mod h1:dX+/inL/fNMqNlz0e9LfyB9TswhZpCVdJM/Z6Vvnwo0= +github.com/onsi/ginkgo v1.16.5 h1:8xi0RTUf59SOSfEtZMvwTvXYMzG4gV23XVHOZiXNtnE= +github.com/onsi/ginkgo v1.16.5/go.mod h1:+E8gABHa3K6zRBolWtd+ROzc/U5bkGt0FwiG042wbpU= github.com/onsi/gomega v0.0.0-20170829124025-dcabb60a477c/go.mod h1:C1qb7wdrVGGVU+Z6iS04AVkA3Q65CEZX59MT0QO5uiA= github.com/onsi/gomega v1.4.3/go.mod h1:ex+gbHU/CVuBBDIJjb2X0qEXbFg53c61hWP/1CpauHY= github.com/onsi/gomega v1.7.0/go.mod h1:ex+gbHU/CVuBBDIJjb2X0qEXbFg53c61hWP/1CpauHY= github.com/onsi/gomega v1.7.1/go.mod h1:XdKZgCCFLUoM/7CFJVPcG8C1xQ1AJ0vpAezJrB7JYyY= github.com/onsi/gomega v1.10.1/go.mod h1:iN09h71vgCQne3DLsj+A5owkum+a2tYe+TOCB1ybHNo= github.com/onsi/gomega v1.10.2/go.mod h1:iN09h71vgCQne3DLsj+A5owkum+a2tYe+TOCB1ybHNo= -github.com/onsi/gomega v1.10.3 h1:gph6h/qe9GSUw1NhH1gp+qb+h8rXD8Cy60Z32Qw3ELA= -github.com/onsi/gomega v1.10.3/go.mod h1:V9xEwhxec5O8UDM77eCW8vLymOMltsqPVYWrpDsH8xc= +github.com/onsi/gomega v1.16.0 h1:6gjqkI8iiRHMvdccRJM8rVKjCWk6ZIm6FTm3ddIe4/c= +github.com/onsi/gomega v1.16.0/go.mod h1:HnhC7FXeEQY45zxNK3PPoIUhzk/80Xly9PcubAlGdZY= github.com/op/go-logging v0.0.0-20160315200505-970db520ece7/go.mod h1:HzydrMdWErDVzsI23lYNej1Htcns9BCg93Dk0bBINWk= github.com/opencontainers/go-digest v1.0.0 h1:apOUWs51W5PlhuyGyz9FCeeBIOUDA/6nW8Oi/yOhh5U= github.com/opencontainers/go-digest v1.0.0/go.mod h1:0JzlMkj0TRzQZfJkVvzbP0HBR3IKzErnv2BNG4W4MAM= @@ -503,6 +531,9 @@ github.com/openzipkin/zipkin-go v0.1.6/go.mod h1:QgAqvLzwWbR/WpD4A3cGpPtJrZXNIiJ github.com/openzipkin/zipkin-go v0.2.1/go.mod h1:NaW6tEwdmWMaCDZzg8sh+IBNOxHMPnhQw8ySjnjRyN4= github.com/openzipkin/zipkin-go v0.2.2/go.mod h1:NaW6tEwdmWMaCDZzg8sh+IBNOxHMPnhQw8ySjnjRyN4= github.com/openzipkin/zipkin-go v0.2.5/go.mod h1:KpXfKdgRDnnhsxw4pNIH9Md5lyFqKUa4YDFlwRYAMyE= +github.com/openzipkin/zipkin-go v0.3.0/go.mod h1:4c3sLeE8xjNqehmF5RpAFLPLJxXscc0R4l6Zg0P1tTQ= +github.com/openzipkin/zipkin-go v0.4.0 h1:CtfRrOVZtbDj8rt1WXjklw0kqqJQwICrCKmlfUuBUUw= +github.com/openzipkin/zipkin-go v0.4.0/go.mod h1:4c3sLeE8xjNqehmF5RpAFLPLJxXscc0R4l6Zg0P1tTQ= github.com/pact-foundation/pact-go v1.0.4/go.mod h1:uExwJY4kCzNPcHRj+hCR/HBbOOIwwtUjcrb0b5/5kLM= github.com/pascaldekloe/goe v0.0.0-20180627143212-57f6aae5913c/go.mod h1:lzWF7FIEvWOWxwDKqyGYQf6ZUaNfKdP144TG7ZOy1lc= github.com/pborman/uuid v1.2.0/go.mod h1:X/NO0urCmaxf9VXbdlT7C2Yzkj2IKimNn4k+gtPdI/k= @@ -511,6 +542,7 @@ github.com/performancecopilot/speed v3.0.0+incompatible/go.mod h1:/CLtqpZ5gBg1M9 github.com/peterbourgon/diskv v2.0.1+incompatible/go.mod h1:uqqh8zWWbv1HBMNONnaR/tNboyR3/BZd58JJSHlUSCU= github.com/pierrec/lz4 v1.0.2-0.20190131084431-473cd7ce01a1/go.mod h1:3/3N9NVKO0jef7pBehbT1qWhCMrIgbYNnFAZCqQ5LRc= github.com/pierrec/lz4 v2.0.5+incompatible/go.mod h1:pdkljMzZIN41W+lC3N2tnIh5sFi+IEE17M5jbnwPHcY= +github.com/pierrec/lz4 v2.6.1+incompatible/go.mod h1:pdkljMzZIN41W+lC3N2tnIh5sFi+IEE17M5jbnwPHcY= 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= @@ -560,7 +592,9 @@ github.com/prometheus/procfs v0.6.0/go.mod h1:cz+aTbrPOrUb4q7XlbU9ygM+/jj0fzG6c1 github.com/prometheus/statsd_exporter v0.20.0 h1:M0hQphnq2WyWKS5CefQL8PqWwBOBPhiAkyLo5l4ZYvE= github.com/prometheus/statsd_exporter v0.20.0/go.mod h1:YL3FWCG8JBBtaUSxAg4Gz2ZYu22bS84XM89ZQXXTWmQ= github.com/prometheus/tsdb v0.7.1/go.mod h1:qhTCs0VvXwvX/y3TZrWD7rabWM+ijKTux40TwIPHuXU= +github.com/rabbitmq/amqp091-go v1.1.0/go.mod h1:ogQDLSOACsLPsIq0NpbtiifNZi2YOz0VTJ0kHRghqbM= github.com/rcrowley/go-metrics v0.0.0-20181016184325-3113b8401b8a/go.mod h1:bCqnVzQkZxMG4s8nGwiZ5l3QUCyqpo9Y+/ZMZ9VjZe4= +github.com/rcrowley/go-metrics v0.0.0-20201227073835-cf1acfcdf475/go.mod h1:bCqnVzQkZxMG4s8nGwiZ5l3QUCyqpo9Y+/ZMZ9VjZe4= github.com/rogpeppe/fastuuid v0.0.0-20150106093220-6724a57986af/go.mod h1:XWv6SoW27p1b0cqNHllgS5HIMJraePCO15w5zCzIWYg= github.com/rogpeppe/fastuuid v1.2.0/go.mod h1:jVj6XXZzXRy/MSR5jhDC/2q6DgLz+nrA6LYCDYWNEvQ= github.com/rogpeppe/go-internal v1.3.0/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFRclV5y23lUDJ4= @@ -572,8 +606,8 @@ github.com/shurcooL/sanitized_anchor_name v1.0.0/go.mod h1:1NzhyTcUVG4SuEtjjoZeV 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/sirupsen/logrus v1.7.0 h1:ShrD1U9pZB12TX0cVy0DtePoCH97K8EtX+mg7ZARUtM= -github.com/sirupsen/logrus v1.7.0/go.mod h1:yWOB1SBYBC5VeMP7gHvWumXLIWorT60ONWic61uBYv0= +github.com/sirupsen/logrus v1.8.1 h1:dJKuHgqk1NNQlqoA6BTlM1Wf9DOH3NBjQyu0h9+AZZE= +github.com/sirupsen/logrus v1.8.1/go.mod h1:yWOB1SBYBC5VeMP7gHvWumXLIWorT60ONWic61uBYv0= github.com/smartystreets/assertions v0.0.0-20180927180507-b2de0cb4f26d/go.mod h1:OnSkiWE9lh6wB0YB77sQom3nweQdgAjqCqsofrRNTgc= github.com/smartystreets/goconvey v1.6.4/go.mod h1:syvi0/a8iFYH4r/RixwvyeAJjdLS9QV7WQ/tjFTllLA= github.com/soheilhy/cmux v0.1.4/go.mod h1:IM3LyeVVIOuxMH7sFAkER9+bJ4dT7Ms6E4xg4kGIyLM= @@ -618,8 +652,12 @@ github.com/uber/jaeger-lib v2.4.1+incompatible h1:td4jdvLcExb4cBISKIpHuGoVXh+dVK github.com/uber/jaeger-lib v2.4.1+incompatible/go.mod h1:ComeNDZlWwrWnDv8aPp0Ba6+uUTzImX/AauajbLI56U= github.com/urfave/cli v1.20.0/go.mod h1:70zkFmudgCuE/ngEzBv17Jvp/497gISqfk5gWijbERA= github.com/urfave/cli v1.22.1/go.mod h1:Gos4lmkARVdJ6EkW0WaNv/tZAAMe9V7XWyB60NtXRu0= +github.com/urfave/cli/v2 v2.3.0/go.mod h1:LJmUH05zAU44vOAcrfzZQKsZbVcdbOG8rtL3/XcUArI= github.com/valyala/bytebufferpool v1.0.0 h1:GqA5TC/0021Y/b9FG4Oi9Mr3q7XYx6KllzawFIhcdPw= github.com/valyala/bytebufferpool v1.0.0/go.mod h1:6bBcMArwyJ5K/AmCkWv1jt77kVWyCJ6HpOuEn7z0Csc= +github.com/xdg-go/pbkdf2 v1.0.0/go.mod h1:jrpuAogTd400dnrH08LKmI/xc1MbPOebTwRqcT5RDeI= +github.com/xdg-go/scram v1.0.2/go.mod h1:1WAq6h33pAW+iRreB34OORO2Nf7qel3VV3fjBj+hCSs= +github.com/xdg-go/stringprep v1.0.2/go.mod h1:8F9zXuvzgwmyT5DUm4GUfZGDdT3W+LCvS6+da4O5kxM= github.com/xiang90/probing v0.0.0-20190116061207-43a291ad63a2/go.mod h1:UETIi67q53MR2AWcXfiuqkDkRtnGDLqkBTpCHuJHxtU= github.com/yuin/goldmark v1.1.25/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= github.com/yuin/goldmark v1.1.27/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= @@ -641,6 +679,23 @@ 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.opencensus.io v0.23.0 h1:gqCw0LfLxScz8irSi8exQc7fyQ0fKQU/qnC/X8+V/1M= go.opencensus.io v0.23.0/go.mod h1:XItmlyltB5F7CS4xOC1DcqMoFqwtC6OG2xF7mCv7P7E= +go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.27.0 h1:0BgiNWjN7rUWO9HdjF4L12r8OW86QkVQcYmCjnayJLo= +go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.27.0/go.mod h1:bdvm3YpMxWAgEfQhtTBaVR8ceXPRuRBSQrvOBnIlHxc= +go.opentelemetry.io/otel v1.2.0 h1:YOQDvxO1FayUcT9MIhJhgMyNO1WqoduiyvQHzGN0kUQ= +go.opentelemetry.io/otel v1.2.0/go.mod h1:aT17Fk0Z1Nor9e0uisf98LrntPGMnk4frBO9+dkf69I= +go.opentelemetry.io/otel/exporters/jaeger v1.2.0 h1:C/5Egj3MJBXRJi22cSl07suqPqtZLnLFmH//OxETUEc= +go.opentelemetry.io/otel/exporters/jaeger v1.2.0/go.mod h1:KJLFbEMKTNPIfOxcg/WikIozEoKcPgJRz3Ce1vLlM8E= +go.opentelemetry.io/otel/exporters/zipkin v1.2.0 h1:Xt8MqxI81nFGb+MyRuS4PeziYE899tYTFMt/3yzOAT4= +go.opentelemetry.io/otel/exporters/zipkin v1.2.0/go.mod h1:A/bgTSkoiCULZEvVfS2zEkSpD16LVVfgL/8ix6oUEpQ= +go.opentelemetry.io/otel/internal/metric v0.25.0 h1:w/7RXe16WdPylaIXDgcYM6t/q0K5lXgSdZOEbIEyliE= +go.opentelemetry.io/otel/internal/metric v0.25.0/go.mod h1:Nhuw26QSX7d6n4duoqAFi5KOQR4AuzyMcl5eXOgwxtc= +go.opentelemetry.io/otel/metric v0.25.0 h1:7cXOnCADUsR3+EOqxPaSKwhEuNu0gz/56dRN1hpIdKw= +go.opentelemetry.io/otel/metric v0.25.0/go.mod h1:E884FSpQfnJOMMUaq+05IWlJ4rjZpk2s/F1Ju+TEEm8= +go.opentelemetry.io/otel/sdk v1.2.0 h1:wKN260u4DesJYhyjxDa7LRFkuhH7ncEVKU37LWcyNIo= +go.opentelemetry.io/otel/sdk v1.2.0/go.mod h1:jNN8QtpvbsKhgaC6V5lHiejMoKD+V8uadoSafgHPx1U= +go.opentelemetry.io/otel/trace v1.2.0 h1:Ys3iqbqZhcf28hHzrm5WAquMkDHNZTUkw7KHbuNjej0= +go.opentelemetry.io/otel/trace v1.2.0/go.mod h1:N5FLswTubnxKxOJHM7XZC074qpeEdLy3CgAVsdMucK0= +go.opentelemetry.io/proto/otlp v0.7.0/go.mod h1:PqfVotwruBrMGOCsRd/89rSnXhoiJIqeYNgFYFoEGnI= go.uber.org/atomic v1.3.2/go.mod h1:gD2HeocX3+yG+ygLZcrzQJaqmWj9AIm7n08wl/qW/PE= go.uber.org/atomic v1.4.0/go.mod h1:gD2HeocX3+yG+ygLZcrzQJaqmWj9AIm7n08wl/qW/PE= go.uber.org/atomic v1.7.0/go.mod h1:fEN4uk6kAWBTFdckzkM89CLk9XfWZrxpCo0nPH17wJc= @@ -664,8 +719,10 @@ golang.org/x/crypto v0.0.0-20190829043050-9756ffdc2472/go.mod h1:yigFU9vqHzYiE8U golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= golang.org/x/crypto v0.0.0-20201002170205-7f63de1d35b0/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= -golang.org/x/crypto v0.0.0-20210513164829-c07d793c2f9a h1:kr2P4QFmQr29mSLA43kwrOcgcReGTfbE9N577tCTuBc= +golang.org/x/crypto v0.0.0-20201112155050-0c6587e931a9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= golang.org/x/crypto v0.0.0-20210513164829-c07d793c2f9a/go.mod h1:P+XmwS30IXTQdn5tA2iutPOUgjI07+tq3H3K9MVA1s8= +golang.org/x/crypto v0.0.0-20210920023735-84f357641f63 h1:kETrAMYZq6WVGPa8IIixL0CaEcIUNi+1WX7grUoi3y8= +golang.org/x/crypto v0.0.0-20210920023735-84f357641f63/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc= golang.org/x/exp v0.0.0-20180321215751-8460e604b9de/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= golang.org/x/exp v0.0.0-20180807140117-3d87b88a115f/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= @@ -741,7 +798,6 @@ golang.org/x/net v0.0.0-20200520182314-0ba52f642ac2/go.mod h1:qpuaurCH72eLCgpAm/ golang.org/x/net v0.0.0-20200625001655-4c5254603344/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA= golang.org/x/net v0.0.0-20200707034311-ab3426394381/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA= golang.org/x/net v0.0.0-20200822124328-c89045814202/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA= -golang.org/x/net v0.0.0-20201006153459-a7d1128ccaa0/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU= golang.org/x/net v0.0.0-20201021035429-f5854403a974/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU= golang.org/x/net v0.0.0-20201031054903-ff519b6c9102/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU= golang.org/x/net v0.0.0-20201110031124-69a78807bb2b/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU= @@ -749,8 +805,10 @@ golang.org/x/net v0.0.0-20210119194325-5f4716e94777/go.mod h1:m0MpNAwzfU5UDzcl9v golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg= golang.org/x/net v0.0.0-20210316092652-d523dce5a7f4/go.mod h1:RBQZq4jEuRlivfhVLdyRGr576XBO4/greRjx4P4O3yc= golang.org/x/net v0.0.0-20210405180319-a5a99cb37ef4/go.mod h1:p54w0d4576C0XHj96bSt6lcn1PtDYWL6XObtHCRCNQM= -golang.org/x/net v0.0.0-20210614182718-04defd469f4e h1:XpT3nA5TvE525Ne3hInMh6+GETgn27Zfm9dxsThnX2Q= +golang.org/x/net v0.0.0-20210428140749-89ef3d95e781/go.mod h1:OJAsFXCWl8Ukc7SiCT/9KSuxbyM7479/AVlXFRxuMCk= golang.org/x/net v0.0.0-20210614182718-04defd469f4e/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= +golang.org/x/net v0.0.0-20210917221730-978cfadd31cf h1:R150MpwJIv1MpS0N/pc+NhTM8ajzvlmxlY5OYsrevXQ= +golang.org/x/net v0.0.0-20210917221730-978cfadd31cf/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= 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= @@ -824,15 +882,18 @@ golang.org/x/sys v0.0.0-20201112073958-5cba982894dd/go.mod h1:h1NjWce9XRLGQEsW7w golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20201201145000-ef89a241ccb3/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20201214210602-f9fddec55a1e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20210112080510-489259a85091/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-20210315160823-c6e025ad8005/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210320140829-1e4c9ba3b0c4/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210330210617-4fbd30eecc44/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-20210510120138-977fb7262007/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.0.0-20210603081109-ebe580a85c40 h1:JWgyZ1qgdTaF3N3oxC+MdTV7qvEEgHo3otj+HB5CM7Q= golang.org/x/sys v0.0.0-20210603081109-ebe580a85c40/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1 h1:SrN+KX8Art/Sf4HNj6Zcz06G7VEz+7w9tdXTPOZ7+l4= +golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1 h1:v+OssWQX+hTHEmOBgwxdZxK4zHq3yOs8F9J7mk0PY8E= 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= @@ -842,8 +903,9 @@ 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.3.5/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= -golang.org/x/text v0.3.6 h1:aRYxNxv6iGQlyVaZmk6ZgYEDa+Jg18DxebPSrd6bg1M= golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= +golang.org/x/text v0.3.7 h1:olpwvP2KacW1ZWvsR7uQhoyTYvKAupfQrRGBFM352Gk= +golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ= golang.org/x/time v0.0.0-20180412165947-fbb02b2291d2/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= 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= @@ -909,6 +971,7 @@ golang.org/x/tools v0.0.0-20200825202427-b303f430e36d/go.mod h1:njjCfa9FT2d7l9Bc golang.org/x/tools v0.0.0-20200904185747-39188db58858/go.mod h1:Cj7w3i3Rnn0Xh82ur9kSqwfTHTeVxaDqrfMjpcNT6bE= golang.org/x/tools v0.0.0-20201110124207-079ba7bd75cd/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= golang.org/x/tools v0.0.0-20201201161351-ac6f37ff4c2a/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= +golang.org/x/tools v0.0.0-20201224043029-2b0845dc783e/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= golang.org/x/tools v0.0.0-20210106214847-113979e3529a/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= golang.org/x/tools v0.1.0/go.mod h1:xkSsbof2nBLbhDlRMhhhyNLN/zl3eTqcnHD5viDpcZ0= golang.org/x/tools v0.1.1/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk= @@ -1016,10 +1079,13 @@ google.golang.org/grpc v1.29.1/go.mod h1:itym6AZVZYACWQqET3MqgPpjcuV5QH3BxFS3Iji google.golang.org/grpc v1.30.0/go.mod h1:N36X2cJ7JwdamYAgDz+s+rVMFjt3numwzf/HckM8pak= google.golang.org/grpc v1.31.0/go.mod h1:N36X2cJ7JwdamYAgDz+s+rVMFjt3numwzf/HckM8pak= google.golang.org/grpc v1.31.1/go.mod h1:N36X2cJ7JwdamYAgDz+s+rVMFjt3numwzf/HckM8pak= +google.golang.org/grpc v1.33.1/go.mod h1:fr5YgcSWrqhRRxogOsw7RzIpsmvOZ6IcH4kBYTpR3n0= google.golang.org/grpc v1.33.2/go.mod h1:JMHMWHQWaTccqQQlmk3MJZS+GWXOdAesneDmEnv2fbc= +google.golang.org/grpc v1.36.0/go.mod h1:qjiiYl8FncCW8feJPdyg3v6XW24KsRHe+dy9BAGRRjU= google.golang.org/grpc v1.36.1/go.mod h1:qjiiYl8FncCW8feJPdyg3v6XW24KsRHe+dy9BAGRRjU= -google.golang.org/grpc v1.38.0 h1:/9BgsAsa5nWe26HqOlvlgJnqBuktYOLCgjCPqsa56W0= google.golang.org/grpc v1.38.0/go.mod h1:NREThFqKR1f3iQ6oBuvc5LadQuXVGo9rkm5ZGrQdJfM= +google.golang.org/grpc v1.41.0 h1:f+PlOh7QV4iIJkPrx5NQ7qaNGFQ3OTse67yaDHfju4E= +google.golang.org/grpc v1.41.0/go.mod h1:U3l9uK9J0sini8mHphKoXyaqDA/8VyGnDee1zzIUK6k= 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= @@ -1031,15 +1097,17 @@ google.golang.org/protobuf v1.23.1-0.20200526195155-81db48ad09cc/go.mod h1:EGpAD 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 h1:bxAC2xTBsZGibn2RTntX0oH50xLsqy1OxA9tTL3p/lk= google.golang.org/protobuf v1.26.0/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc= +google.golang.org/protobuf v1.27.1 h1:SnqbnDw1V7RiZcXPx5MEeqPv2s79L9i7BJUlG/+RurQ= +google.golang.org/protobuf v1.27.1/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc= gopkg.in/alecthomas/kingpin.v2 v2.2.6 h1:jMFz6MfLP0/4fUyZle81rXUoxOBFi19VUFKVDOQfozc= 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/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-20200227125254-8fa46927fb4f h1:BLraFXnmrev5lT+xlilqcH8XK9/i0At2xKjWk4p6zsU= gopkg.in/check.v1 v1.0.0-20200227125254-8fa46927fb4f/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= +gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk= +gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q= gopkg.in/cheggaaa/pb.v1 v1.0.25/go.mod h1:V/YB90LKu/1FcN3WVnfiiE5oMCibMjukxqG/qStrOgw= gopkg.in/errgo.v2 v2.1.0/go.mod h1:hNsd1EY+bozCKY1Ytp96fpM3vjJbqLJn88ws8XvfDNI= gopkg.in/fsnotify.v1 v1.4.7/go.mod h1:Tz8NjZHkW78fSQdbUxIjBTcgA1z1m8ZHf0WmKUhAMys= diff --git a/multicluster/clusterregistry_client.go b/multicluster/clusterregistry_client.go index ea5285900f..b00741e0d0 100644 --- a/multicluster/clusterregistry_client.go +++ b/multicluster/clusterregistry_client.go @@ -22,6 +22,7 @@ import ( "encoding/json" "errors" + "github.com/katanomi/pkg/tracing" corev1 "k8s.io/api/core/v1" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" "k8s.io/apimachinery/pkg/apis/meta/v1/unstructured" @@ -158,6 +159,8 @@ func (m *ClusterRegistryClient) GetConfigFromCluster(ctx context.Context, cluste }, } + config.Wrap(tracing.WrapTransport) + // Uses the controller secret for controller auth // this configuration or config should only be used by controllers if clusterObj.Spec.AuthInfo.Controller != nil { diff --git a/plugin/client/plugin_client.go b/plugin/client/plugin_client.go index df555bbe35..66635c28ad 100644 --- a/plugin/client/plugin_client.go +++ b/plugin/client/plugin_client.go @@ -24,6 +24,7 @@ import ( "github.com/go-resty/resty/v2" "github.com/katanomi/pkg/client" perrors "github.com/katanomi/pkg/errors" + "github.com/katanomi/pkg/tracing" corev1 "k8s.io/api/core/v1" "k8s.io/apimachinery/pkg/api/errors" duckv1 "knative.dev/pkg/apis/duck/v1" @@ -58,6 +59,8 @@ func NewPluginClient(opts ...BuildOptions) *PluginClient { for _, op := range opts { op(pluginClient) } + + tracing.WrapTransportForRestyClient(pluginClient.client) return pluginClient } diff --git a/sharedmain/app.go b/sharedmain/app.go index 2952d1964c..2e572797d6 100644 --- a/sharedmain/app.go +++ b/sharedmain/app.go @@ -45,11 +45,10 @@ import ( kmanager "github.com/katanomi/pkg/manager" "github.com/katanomi/pkg/multicluster" "github.com/katanomi/pkg/plugin/client" - "github.com/katanomi/pkg/plugin/component/tracing" - "github.com/katanomi/pkg/plugin/config" "github.com/katanomi/pkg/plugin/route" "github.com/katanomi/pkg/restclient" kscheme "github.com/katanomi/pkg/scheme" + "github.com/katanomi/pkg/tracing" "go.uber.org/zap" "golang.org/x/sync/errgroup" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" @@ -70,6 +69,11 @@ import ( ctrllog "sigs.k8s.io/controller-runtime/pkg/log" ) +const ( + healthzRoutePath = "healthz" + readyzRoutePath = "readyz" +) + var ( DefaultTimeout = kclient.DefaultTimeout DefaultQPS = kclient.DefaultQPS @@ -113,9 +117,6 @@ type AppBuilder struct { // plugins plugins []client.Interface - // tracing - tracingConfig *config.Config - // restful container container *restful.Container filters []restful.FilterFunction @@ -168,6 +169,7 @@ func (a *AppBuilder) init() { restyClient.SetTLSClientConfig(&tls.Config{ InsecureSkipVerify: InsecureSkipVerify, // nolint: gosec // G402: TLS InsecureSkipVerify set true. }) + tracing.WrapTransportForRestyClient(restyClient) a.Context = restclient.WithRESTClient(a.Context, restyClient) a.ConfigMapWatcher = sharedmain.SetupConfigMapWatchOrDie(a.Context, a.Logger) @@ -225,6 +227,21 @@ func (a *AppBuilder) initClient(clientVar ctrlclient.Client) { }) } +// Tracing adds tracing and tracer to the app +func (a *AppBuilder) Tracing(ops ...tracing.TraceOption) *AppBuilder { + a.init() + ops = append([]tracing.TraceOption{ + tracing.WithServiceName(a.Name), + }, ops...) + t := tracing.NewTracing(a.Logger, ops...) + err := tracing.SetupDynamicPublishing(t, a.ConfigMapWatcher) + if err != nil { + log.Fatal("Error reading/parsing tracing configuration: ", err) + } + a.filters = append(a.filters, tracing.RestfulFilter(a.Name, healthzRoutePath, readyzRoutePath)) + return a +} + // Log adds logging and logger to the app func (a *AppBuilder) Log() *AppBuilder { a.init() @@ -298,10 +315,10 @@ func (a *AppBuilder) Controllers(ctors ...controllers.SetupChecker) *AppBuilder if err != nil { a.Logger.Fatalw("unable to start manager", "err", err) } - if err := a.Manager.AddHealthzCheck("healthz", healthz.Ping); err != nil { + if err := a.Manager.AddHealthzCheck(healthzRoutePath, healthz.Ping); err != nil { a.Logger.Fatalw("unable to set up health check", "err", err) } - if err := a.Manager.AddReadyzCheck("readyz", healthz.Ping); err != nil { + if err := a.Manager.AddReadyzCheck(readyzRoutePath, healthz.Ping); err != nil { a.Logger.Fatalw("unable to set up ready check", "err", err) } @@ -367,31 +384,6 @@ func (a *AppBuilder) Webhooks(objs ...runtime.Object) *AppBuilder { return a } -// Tracing adds tracing capabilities to this app -// TODO: change this configuration to use a configmap watcher and turn on/off on the fly -func (a *AppBuilder) Tracing(cfg *config.Config) *AppBuilder { - if cfg == nil { - cfg = config.NewConfig() - } - a.tracingConfig = cfg - - if a.tracingConfig.Trace.Enable { - closer, err := tracing.Config(&a.tracingConfig.Trace) - if err != nil { - a.Logger.Fatalw("tracing start error", "err", err) - } - if closer != nil { - a.startFunc = append(a.startFunc, func(ctx context.Context) error { - // waits until it is shutting down to close - <-ctx.Done() - return closer.Close() - }) - - } - } - return a -} - // Filters customize filters to this app func (a *AppBuilder) Filters(filters ...restful.FilterFunction) *AppBuilder { a.filters = append(a.filters, filters...) diff --git a/sharedmain/main.go b/sharedmain/main.go index 48c7f48f59..a2b5aea3c0 100644 --- a/sharedmain/main.go +++ b/sharedmain/main.go @@ -23,6 +23,7 @@ import ( "os" "time" + "github.com/katanomi/pkg/tracing" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" cminformer "knative.dev/pkg/configmap/informer" @@ -62,6 +63,7 @@ func GetConfigOrDie(ctx context.Context) (context.Context, *rest.Config) { cfg = ctrl.GetConfigOrDie() ctx = injection.WithConfig(ctx, cfg) } + cfg.Wrap(tracing.WrapTransport) return ctx, cfg } diff --git a/tracing/config.go b/tracing/config.go new file mode 100644 index 0000000000..eab46f7cee --- /dev/null +++ b/tracing/config.go @@ -0,0 +1,145 @@ +/* +Copyright 2021 The Katanomi Authors. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +package tracing + +import ( + "encoding/json" + "os" + "time" + + corev1 "k8s.io/api/core/v1" + cm "knative.dev/pkg/configmap" +) + +const ( + requestIDHeaderKey = "X-Request-ID" + + configMapNameEnv = "CONFIG_TRACING_NAME" + defaultServiceName = "katanomi" + defaultConfigMapName = "katanomi-config-tracing" + + enableKey = "enable" + backendKey = "backend" + samplingRatioKey = "sampling-ratio" + jaegerConfigKey = "jaeger-config" + zipkinConfigKey = "zipkin-config" + customConfigKey = "custom-config" +) + +// ExporterBackend Built-in supported export types +type ExporterBackend string + +var ( + ExporterBackendJaeger ExporterBackend = "jaeger" + ExporterBackendZipkin ExporterBackend = "zipkin" + ExporterBackendCustom ExporterBackend = "custom" +) + +// Config tracing config +type Config struct { + // Enable Controls whether to enable tracing. + // default false. + Enable bool `json:"enable" yaml:"enable"` + + // SamplingRatio Control the rate of sampling. + // SamplingRatio >= 1 will always sample. + // SamplingRatio <= 0 will never sample. + // default 0. + SamplingRatio float64 `json:"sampling_ratio" yaml:"samplingRatio"` + + // Backend The type of exporter backend + Backend ExporterBackend `json:"backend" yaml:"backend"` + + // Jaeger The configuration used by jaeger backend + Jaeger JaegerConfig `json:"jaeger" yaml:"jaeger"` + + // Zipkin The configuration used by zipkin backend + Zipkin ZipkinConfig `json:"zipkin" yaml:"zipkin"` + + // Custom The configuration used by custom backend + Custom string `json:"custom" yaml:"custom"` + // todo support OTLP +} + +// ZipkinConfig The configuration used by zipkin backend +type ZipkinConfig struct { + // Url The collector url of zipkin backend + Url string `json:"url" yaml:"url"` +} + +// JaegerConfig The configuration used by Jaeger backend +type JaegerConfig struct { + // Host The host of jaeger backend. + Host string `json:"host" yaml:"host"` + + // Port the port of jaeger backend. + Port string `json:"port" yaml:"port"` + + // MaxPacketSize The maximum UDP packet size for transport to the Jaeger agent. + MaxPacketSize int `json:"max_packet_size" yaml:"maxPacketSize"` + + // DisableAttemptReconnecting Disable reconnecting udp client. + DisableAttemptReconnecting bool `json:"disable_attempt_reconnecting" yaml:"disableAttemptReconnecting"` + + // AttemptReconnectInterval The interval between attempts to re resolve agent endpoint. + AttemptReconnectInterval time.Duration `json:"attempt_reconnect_interval" yaml:"attemptReconnectInterval"` +} + +// newTracingConfigFromConfigMap returns a Config for the given configmap +func newTracingConfigFromConfigMap(config *corev1.ConfigMap) (*Config, error) { + if config == nil { + return nil, nil + } + c := &Config{} + backend := "" + err := cm.Parse(config.Data, + cm.AsBool(enableKey, &c.Enable), + cm.AsString(backendKey, &backend), + cm.AsString(customConfigKey, &c.Custom), + cm.AsFloat64(samplingRatioKey, &c.SamplingRatio), + ) + if err != nil { + return nil, err + } + c.Backend = ExporterBackend(backend) + if !c.Enable || c.SamplingRatio <= 0 { + return c, nil + } + switch c.Backend { + case ExporterBackendJaeger: + if s, ok := config.Data[jaegerConfigKey]; ok && s != "" { + if err := json.Unmarshal([]byte(s), &c.Jaeger); err != nil { + return nil, err + } + } + case ExporterBackendZipkin: + if s, ok := config.Data[zipkinConfigKey]; ok && s != "" { + if err := json.Unmarshal([]byte(s), &c.Zipkin); err != nil { + return nil, err + } + } + } + return c, nil +} + +// ConfigMapName gets the name of the tracing ConfigMap +func ConfigMapName() string { + if name := os.Getenv(configMapNameEnv); name != "" { + return name + } + return defaultConfigMapName +} diff --git a/tracing/config_test.go b/tracing/config_test.go new file mode 100644 index 0000000000..4076d374f6 --- /dev/null +++ b/tracing/config_test.go @@ -0,0 +1,128 @@ +/* +Copyright 2021 The Katanomi Authors. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +package tracing + +import ( + "os" + "testing" + + . "github.com/onsi/gomega" + v1 "k8s.io/api/core/v1" +) + +func Test_newTracingConfigFromConfigMap(t *testing.T) { + tests := []struct { + name string + input map[string]string + want *Config + wantErr bool + }{ + { + input: map[string]string{}, + want: &Config{}, + wantErr: false, + }, + { + input: map[string]string{ + enableKey: "true", + backendKey: "jaeger", + samplingRatioKey: "0.5", + }, + want: &Config{ + Enable: true, + Backend: ExporterBackendJaeger, + SamplingRatio: 0.5, + }, + wantErr: false, + }, + { + input: map[string]string{ + enableKey: "true", + backendKey: "zipkin", + samplingRatioKey: "1.1", + }, + want: &Config{ + Enable: true, + Backend: ExporterBackendZipkin, + SamplingRatio: 1.1, + }, + wantErr: false, + }, + { + input: map[string]string{ + enableKey: "true", + backendKey: "custom", + samplingRatioKey: "1.1", + }, + want: &Config{ + Enable: true, + Backend: ExporterBackendCustom, + SamplingRatio: 1.1, + }, + wantErr: false, + }, + { + input: map[string]string{ + enableKey: "true typo", + backendKey: "custom", + samplingRatioKey: "0.5", + }, + want: nil, + wantErr: true, + }, + } + g := NewGomegaWithT(t) + for _, tt := range tests { + got, err := newTracingConfigFromConfigMap(&v1.ConfigMap{ + Data: tt.input, + }) + if tt.wantErr { + g.Expect(err).ShouldNot(BeNil()) + } else { + g.Expect(err).Should(BeNil()) + } + g.Expect(got).Should(Equal(tt.want)) + } +} + +func TestConfigMapName(t *testing.T) { + tests := []struct { + setting func() + want string + }{ + { + setting: func() { + os.Setenv(configMapNameEnv, "test-config-tracing-name") + }, + want: "test-config-tracing-name", + }, + { + setting: func() { + os.Unsetenv(configMapNameEnv) + }, + want: defaultConfigMapName, + }, + } + g := NewGomegaWithT(t) + for _, tt := range tests { + if tt.setting != nil { + tt.setting() + } + got := ConfigMapName() + g.Expect(got).Should(Equal(tt.want)) + } +} diff --git a/tracing/filter.go b/tracing/filter.go new file mode 100644 index 0000000000..612a18f433 --- /dev/null +++ b/tracing/filter.go @@ -0,0 +1,77 @@ +/* +Copyright 2021 The Katanomi Authors. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +package tracing + +import ( + "strings" + + "github.com/emicklei/go-restful/v3" + "go.opentelemetry.io/otel" + "go.opentelemetry.io/otel/propagation" + semconv "go.opentelemetry.io/otel/semconv/v1.7.0" + "go.opentelemetry.io/otel/trace" +) + +// RestfulFilter Set the tracing middleware for go-restful web service framework. +// If ignorePaths param is specified, these paths will not be sampled. +func RestfulFilter(serviceName string, ignorePaths ...string) restful.FilterFunction { + for index, item := range ignorePaths { + ignorePaths[index] = strings.TrimPrefix(item, "/") + } + isIgnoredPath := func(req *restful.Request) bool { + routePath := strings.TrimPrefix(req.SelectedRoutePath(), "/") + for _, item := range ignorePaths { + if item == routePath { + return true + } + } + return false + } + return func(req *restful.Request, resp *restful.Response, chain *restful.FilterChain) { + if isIgnoredPath(req) { + chain.ProcessFilter(req, resp) + return + } + tracer := otel.GetTracerProvider().Tracer(serviceName) + propagator := otel.GetTextMapPropagator() + + r := req.Request + ctx := propagator.Extract(r.Context(), propagation.HeaderCarrier(r.Header)) + route := req.SelectedRoutePath() + spanName := r.Method + " " + route + + ctx, span := tracer.Start(ctx, spanName, + trace.WithAttributes(semconv.NetAttributesFromHTTPRequest("tcp", r)...), + trace.WithAttributes(semconv.HTTPServerAttributesFromHTTPRequest(serviceName, route, r)...), + trace.WithSpanKind(trace.SpanKindServer), + ) + defer span.End() + + // pass the span through the request context + req.Request = req.Request.WithContext(ctx) + if spanContext := span.SpanContext(); spanContext.IsSampled() { + resp.AddHeader(requestIDHeaderKey, spanContext.TraceID().String()) + } + + chain.ProcessFilter(req, resp) + + attrs := semconv.HTTPAttributesFromHTTPStatusCode(resp.StatusCode()) + spanStatus, spanMessage := semconv.SpanStatusFromHTTPStatusCode(resp.StatusCode()) + span.SetAttributes(attrs...) + span.SetStatus(spanStatus, spanMessage) + } +} diff --git a/tracing/filter_test.go b/tracing/filter_test.go new file mode 100644 index 0000000000..385e2d2051 --- /dev/null +++ b/tracing/filter_test.go @@ -0,0 +1,74 @@ +/* +Copyright 2021 The Katanomi Authors. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +package tracing + +import ( + "context" + "net/http" + "net/http/httptest" + + "github.com/emicklei/go-restful/v3" + . "github.com/onsi/ginkgo" + . "github.com/onsi/gomega" + "go.opentelemetry.io/otel" + "go.opentelemetry.io/otel/propagation" + "go.opentelemetry.io/otel/trace" +) + +var _ = Describe("test tracing filter", func() { + var sc trace.SpanContext + + injectTraceContext := func(req *http.Request) { + ctx := trace.ContextWithRemoteSpanContext(context.Background(), sc) + ctx, _ = trace.NewNoopTracerProvider().Tracer(defaultServiceName).Start(ctx, "test") + otel.GetTextMapPropagator().Inject(ctx, propagation.HeaderCarrier(req.Header)) + } + + BeforeEach(func() { + sc = trace.NewSpanContext(trace.SpanContextConfig{ + TraceID: trace.TraceID{0x01}, + SpanID: trace.SpanID{0x01}, + }) + otel.SetTextMapPropagator(propagation.TraceContext{}) + }) + + Context("testing for normal path", func() { + + It("spans should be same", func() { + server, request := testWebService("/test-path", RestfulFilter(defaultServiceName), func(req *restful.Request, resp *restful.Response) { + span := trace.SpanFromContext(req.Request.Context()) + Expect(sc.TraceID()).Should(Equal(span.SpanContext().TraceID())) + resp.WriteHeader(http.StatusOK) + }) + injectTraceContext(request) + server.ServeHTTP(httptest.NewRecorder(), request) + }) + }) + + Context("testing for ignored path", func() { + It("span should be empty", func() { + server, request := testWebService("/test-path", RestfulFilter(defaultServiceName, "test-path"), func(req *restful.Request, resp *restful.Response) { + span := trace.SpanFromContext(req.Request.Context()) + Expect(span.SpanContext()).Should(Equal(trace.SpanContext{})) + Expect(span.IsRecording()).Should(BeFalse()) + resp.WriteHeader(http.StatusOK) + }) + injectTraceContext(request) + server.ServeHTTP(httptest.NewRecorder(), request) + }) + }) +}) diff --git a/tracing/options.go b/tracing/options.go new file mode 100644 index 0000000000..c875a49f04 --- /dev/null +++ b/tracing/options.go @@ -0,0 +1,90 @@ +/* +Copyright 2021 The Katanomi Authors. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +package tracing + +import ( + "go.opentelemetry.io/otel/propagation" + "go.opentelemetry.io/otel/sdk/resource" + "go.opentelemetry.io/otel/sdk/trace" +) + +// TraceOption optional setting of Tracing instance +type TraceOption func(tracing *Tracing) + +// ExporterConstructor Construct exporter by the specified config +type ExporterConstructor func(config *Config) (trace.SpanExporter, error) + +// ResourceConstructor Construct resource by the specified config +type ResourceConstructor func(config *Config) (*resource.Resource, error) + +// TraceProviderConstructor Construct `TraceProvider` by the specified config +type TraceProviderConstructor func(config *Config) (*trace.TracerProvider, error) + +// WithServiceName Configures the service name for Tracing instance +func WithServiceName(name string) TraceOption { + return func(tracing *Tracing) { + if name != "" { + tracing.ServiceName = name + } + } +} + +// WithExporter Configures the exporter backend for `Tracing` instance. +func WithExporter(f ExporterConstructor) TraceOption { + return func(tracing *Tracing) { + if f != nil { + tracing.exporterConstructor = f + } + } +} + +// WithResource Configures the resource for `Tracing` instance. +func WithResource(f ResourceConstructor) TraceOption { + return func(tracing *Tracing) { + if f != nil { + tracing.resourceConstructor = f + } + } +} + +// WithTraceProvider Configures the TraceProvider for `Tracing` instance +// If `TraceProvider` is specified, `WithExporter` and `WithResource` function will not work. +func WithTraceProvider(f TraceProviderConstructor) TraceOption { + return func(tracing *Tracing) { + if f != nil { + tracing.traceProviderConstructor = f + } + } +} + +// WithTracerProviderOption Configures the options for built-in traceProvider +func WithTracerProviderOption(ops ...trace.TracerProviderOption) TraceOption { + return func(tracing *Tracing) { + tracing.traceProviderOptions = append(tracing.traceProviderOptions, ops...) + } +} + +// WithTextMapPropagator Configures the propagator options for traceProvider +func WithTextMapPropagator(ops ...propagation.TextMapPropagator) TraceOption { + return func(tracing *Tracing) { + for _, item := range ops { + if item != nil { + tracing.Propagators = append(tracing.Propagators, item) + } + } + } +} diff --git a/tracing/options_test.go b/tracing/options_test.go new file mode 100644 index 0000000000..dda0759d66 --- /dev/null +++ b/tracing/options_test.go @@ -0,0 +1,216 @@ +/* +Copyright 2021 The Katanomi Authors. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +package tracing + +import ( + "context" + + . "github.com/onsi/ginkgo" + . "github.com/onsi/gomega" + "go.opentelemetry.io/otel" + "go.opentelemetry.io/otel/propagation" + "go.opentelemetry.io/otel/sdk/resource" + "go.opentelemetry.io/otel/sdk/trace" + "go.uber.org/zap" +) + +var _ = Describe("testing for customizing service name", func() { + Context("specify a service name", func() { + tracing := &Tracing{ServiceName: defaultServiceName} + It("service name should be modified", func() { + WithServiceName("hello")(tracing) + Expect(tracing.ServiceName).Should(Equal("hello")) + }) + }) + + Context("specify an empty service name", func() { + tracing := &Tracing{ServiceName: defaultServiceName} + It("service name should not be modified", func() { + WithServiceName("")(tracing) + Expect(tracing.ServiceName).Should(Equal(defaultServiceName)) + }) + }) +}) + +var _ = Describe("testing for customizing exporter", func() { + var testConfig *Config + + BeforeEach(func() { + testConfig = &Config{Enable: true} + }) + + Context("when a valid exporter is provided", func() { + tracing := &Tracing{ServiceName: defaultServiceName} + exporter := func(config *Config) (trace.SpanExporter, error) { + Expect(config).Should(Equal(testConfig)) + return nil, nil + } + It("exporterConstructor property should be set", func() { + WithExporter(exporter)(tracing) + Expect(tracing.exporterConstructor).ShouldNot(BeNil()) + + tracing.ApplyConfig(testConfig) + + r1, r2 := tracing.exporterConstructor(testConfig) + Expect(r1).Should(BeNil()) + Expect(r2).Should(BeNil()) + }) + }) + + Context("when a nil exporter is provided", func() { + tracing := &Tracing{ServiceName: defaultServiceName} + It("exporterConstructor property should not be set", func() { + WithExporter(nil)(tracing) + Expect(tracing.exporterConstructor).Should(BeNil()) + }) + }) +}) + +var _ = Describe("testing for customizing resource", func() { + var testConfig *Config + + BeforeEach(func() { + testConfig = &Config{Enable: true} + }) + + Context("when a valid resource is provided", func() { + tracing := &Tracing{ServiceName: defaultServiceName} + resource := func(config *Config) (*resource.Resource, error) { + Expect(config).Should(Equal(testConfig)) + return nil, nil + } + It("resourceConstructor property should be set", func() { + WithResource(resource)(tracing) + Expect(tracing.resourceConstructor).ShouldNot(BeNil()) + + tracing.ApplyConfig(testConfig) + + r1, r2 := tracing.resourceConstructor(testConfig) + Expect(r1).Should(BeNil()) + Expect(r2).Should(BeNil()) + }) + }) + + Context("when a nil resource is provided", func() { + tracing := &Tracing{ServiceName: defaultServiceName} + It("resourceConstructor property should not be set", func() { + WithResource(nil)(tracing) + Expect(tracing.resourceConstructor).Should(BeNil()) + }) + }) +}) + +var _ = Describe("testing for customizing traceProvider", func() { + var testConfig *Config + + BeforeEach(func() { + testConfig = &Config{Enable: true} + }) + + Context("when a valid traceProvider is provided", func() { + tracing := &Tracing{ServiceName: defaultServiceName} + traceProvider := func(config *Config) (*trace.TracerProvider, error) { + Expect(config).Should(Equal(testConfig)) + return nil, nil + } + WithTraceProvider(traceProvider)(tracing) + + It("traceProviderConstructor property should be set", func() { + Expect(tracing.traceProviderConstructor).ShouldNot(BeNil()) + + tracing.ApplyConfig(testConfig) + + r1, r2 := tracing.traceProviderConstructor(testConfig) + Expect(r1).Should(BeNil()) + Expect(r2).Should(BeNil()) + }) + + Context("with a valid exporter is provided", func() { + exporter := func(config *Config) (trace.SpanExporter, error) { + panic("should not be executed") + } + resource := func(config *Config) (*resource.Resource, error) { + Expect(config).Should(Equal(testConfig)) + return nil, nil + } + It("exporterConstructor property should be set", func() { + WithExporter(exporter)(tracing) + Expect(tracing.exporterConstructor).ShouldNot(BeNil()) + + tracing.ApplyConfig(testConfig) + }) + It("resourceConstructor property should be set", func() { + WithResource(resource)(tracing) + Expect(tracing.resourceConstructor).ShouldNot(BeNil()) + + tracing.ApplyConfig(testConfig) + }) + }) + }) + + Context("with a nil traceProvider", func() { + tracing := &Tracing{ServiceName: defaultServiceName} + It("traceProviderConstructor property should not be set", func() { + WithTraceProvider(nil)(tracing) + Expect(tracing.traceProviderConstructor).Should(BeNil()) + }) + }) +}) + +var _ = Describe("testing for customizing TraceProvider option", func() { + Context("when always sample", func() { + tracing := NewTracing(zap.NewNop().Sugar(), + WithTracerProviderOption(trace.WithSampler(trace.AlwaysSample())), + ) + It("The count of TracProvider options should be 1", func() { + Expect(len(tracing.traceProviderOptions)).Should(Equal(1)) + }) + It("Should be Sampled", func() { + tracing.ApplyConfig(getValidTracingConfig()) + _, span := otel.Tracer("xx").Start(context.Background(), "xx") + Expect(span.SpanContext().IsSampled()).Should(BeTrue()) + }) + }) + + Context("when never sample", func() { + tracing := NewTracing(zap.NewNop().Sugar(), + WithTracerProviderOption(trace.WithSampler(trace.NeverSample())), + ) + It("The count of TracProvider options should be 1", func() { + Expect(len(tracing.traceProviderOptions)).Should(Equal(1)) + }) + It("Should not be Sampled", func() { + tracing.ApplyConfig(getValidTracingConfig()) + _, span := otel.Tracer("xx").Start(context.Background(), "xx") + Expect(span.SpanContext().IsSampled()).ShouldNot(BeTrue()) + }) + }) +}) + +var _ = Describe("testing for customizing propagator option", func() { + testPropagator := propagation.TraceContext{} + Context("when always sample", func() { + tracing := NewTracing(zap.NewNop().Sugar(), + WithTextMapPropagator(testPropagator), + ) + It("Should be Sampled", func() { + tracing.ApplyConfig(getValidTracingConfig()) + Expect(otel.GetTextMapPropagator()). + Should(Equal(propagation.NewCompositeTextMapPropagator(testPropagator))) + }) + }) +}) diff --git a/tracing/setup.go b/tracing/setup.go new file mode 100644 index 0000000000..283bdc24b1 --- /dev/null +++ b/tracing/setup.go @@ -0,0 +1,99 @@ +/* +Copyright 2021 The Katanomi Authors. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +package tracing + +import ( + "go.uber.org/zap" + v1 "k8s.io/api/core/v1" + "knative.dev/pkg/configmap" + cminformer "knative.dev/pkg/configmap/informer" +) + +func defaultConfigMap(name string) *v1.ConfigMap { + cm := &v1.ConfigMap{} + cm.Name = name + return cm +} + +// SetupDynamicPublishing sets up trace publishing for the process, by watching a +// ConfigMap for the configuration. Note that other pieces still need to generate the traces, this +// just ensures that if generated, they are collected appropriately. This is normally done by using +// tracing.HTTPSpanMiddleware as a middleware HTTP handler. The configuration will be dynamically +// updated when the ConfigMap is updated. +func SetupDynamicPublishing(tracing *Tracing, configMapWatcher *cminformer.InformedWatcher) error { + tracerUpdater := func(name string, value interface{}) { + if name != tracing.ConfigMapName { + return + } + cfg := value.(*Config) + tracing.ApplyConfig(cfg) + } + + // Set up our config store. + w := NewDftConfigMapWatcher("config-tracing-store", tracing.logger, configMapWatcher) + w.AddWatch(tracing.ConfigMapName, newTracingConfigFromConfigMap, defaultConfigMap(tracing.ConfigMapName)) + w.Run(tracerUpdater) + + return nil +} + +// NewDftConfigMapWatcher constructs new dftConfigMapWatcher +func NewDftConfigMapWatcher(name string, logger *zap.SugaredLogger, informedWatcher *cminformer.InformedWatcher) *dftConfigMapWatcher { + return &dftConfigMapWatcher{ + name: name, + InformedWatcher: informedWatcher, + constructors: map[string]interface{}{}, + defaults: map[string]v1.ConfigMap{}, + logger: logger, + } +} + +// dftConfigMapWatcher describe configmap watcher +type dftConfigMapWatcher struct { + *cminformer.InformedWatcher + + name string + constructors map[string]interface{} + defaults map[string]v1.ConfigMap + + logger *zap.SugaredLogger +} + +// AddWatch watch the configmap based on the given name +// If there is no configmap specified in the current namespace, +// the default configuration you provide will be used, if not, `InformedWatcher` will exit +func (d *dftConfigMapWatcher) AddWatch(name string, constructor interface{}, defaultCM *v1.ConfigMap) { + d.constructors[name] = constructor + if defaultCM != nil { + d.defaults[name] = *defaultCM + } +} + +// Run register the watcher to configStore +func (d *dftConfigMapWatcher) Run(onAfterStore ...func(name string, value interface{})) { + configStore := configmap.NewUntypedStore(d.name, d.logger, d.constructors, onAfterStore...) + configStore.WatchConfigs(d) +} + +// Watch register the watcher to InformedWatcher +func (d *dftConfigMapWatcher) Watch(name string, obs ...configmap.Observer) { + if cm, ok := d.defaults[name]; ok { + d.InformedWatcher.WatchWithDefault(cm, obs...) + } else { + d.InformedWatcher.Watch(name, obs...) + } +} diff --git a/tracing/setup_test.go b/tracing/setup_test.go new file mode 100644 index 0000000000..9535cf259d --- /dev/null +++ b/tracing/setup_test.go @@ -0,0 +1,162 @@ +/* +Copyright 2021 The Katanomi Authors. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +package tracing + +import ( + "context" + "fmt" + "sync/atomic" + "time" + + . "github.com/onsi/ginkgo" + . "github.com/onsi/gomega" + "go.uber.org/zap" + v1 "k8s.io/api/core/v1" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + "knative.dev/pkg/configmap/informer" +) + +func testConfigMap(name string, data map[string]string) *v1.ConfigMap { + return &v1.ConfigMap{ + ObjectMeta: metav1.ObjectMeta{ + Name: name, + Namespace: "default", + }, + Data: data, + } +} + +var _ = Describe("testing for configmap watcher", func() { + var clear func() + + // if a default configMap is provided, it will first trigger the update + // handler when the informer starts + var startInformer func(*v1.ConfigMap, func(name string, value interface{})) + var createCm func(name string, cm *v1.ConfigMap) + var ctx = context.Background() + var logger = zap.NewNop().Sugar() + var testCmName = "test-config-name" + var testNs = "default" + var testCm = testConfigMap(testCmName, map[string]string{ + enableKey: "true", + backendKey: "jaeger", + samplingRatioKey: "1", + }) + var testCount = int64(0) + var triggeredByDefaultCount = 1 + + AfterEach(func() { + if clear != nil { + clear() + } + }) + + BeforeEach(func() { + stopCh := make(chan struct{}) + testCount = 0 + var createdConfigMaps []string + + clear = func() { + close(stopCh) + for _, name := range createdConfigMaps { + err := k8sConfigSet.CoreV1().ConfigMaps(testNs).Delete(ctx, name, metav1.DeleteOptions{}) + Expect(err).ShouldNot(HaveOccurred()) + } + } + + createCm = func(name string, cm *v1.ConfigMap) { + newCm := cm.DeepCopy() + newCm.Name = name + _, err := k8sConfigSet.CoreV1().ConfigMaps(testNs).Create(ctx, newCm, metav1.CreateOptions{}) + Expect(err).ShouldNot(HaveOccurred()) + createdConfigMaps = append(createdConfigMaps, name) + } + + startInformer = func(defaultCm *v1.ConfigMap, handler func(name string, value interface{})) { + watcher := informer.NewInformedWatcher(k8sConfigSet, testNs) + dftCmWatcher := NewDftConfigMapWatcher("config-tracing-store", logger, watcher) + dftCmWatcher.AddWatch(testCmName, newTracingConfigFromConfigMap, defaultCm) + dftCmWatcher.Run(func(name string, value interface{}) { + if name == testCmName { + atomic.AddInt64(&testCount, 1) + } + if handler != nil { + handler(name, value) + } + }) + err := watcher.Start(stopCh) + Expect(err).ShouldNot(HaveOccurred()) + } + }) + + Context("when create new configmap", func() { + It("configmap that is not listening will not trigger update handler", func() { + startInformer(testCm, nil) + + for i := 0; i < 10; i++ { + createCm(fmt.Sprintf("test-cm-%d", i), testCm) + } + // wait for async trigger + time.Sleep(time.Second * 2) + Expect(testCount).Should(Equal(int64(triggeredByDefaultCount))) + }) + + }) + + Context("when configmap is existed", func() { + It("The update handler should be triggered when the informer started", func() { + createCm(testCmName, testCm) + startInformer(nil, func(name string, value interface{}) { + cfg, ok := value.(*Config) + Expect(ok).Should(BeTrue()) + Expect(cfg.Enable).Should(BeTrue()) + Expect(cfg.Backend).Should(Equal(ExporterBackendJaeger)) + Expect(cfg.SamplingRatio).Should(Equal(float64(1))) + }) + // wait for async trigger + time.Sleep(time.Second * 2) + Expect(testCount).Should(Equal(int64(1))) + }) + }) + + Context("when configmap is not exist", func() { + It("The update handler should be triggered with empty config", func() { + defaultCM := testConfigMap(testCmName, map[string]string{ + enableKey: "false", + }) + var currentConfig *Config + startInformer(defaultCM, func(name string, value interface{}) { + cfg, ok := value.(*Config) + Expect(ok).Should(BeTrue()) + currentConfig = cfg + }) + // wait for async trigger + time.Sleep(time.Second * 2) + Expect(testCount).Should(Equal(int64(triggeredByDefaultCount))) + Expect(currentConfig.Enable).Should(BeFalse()) + Expect(currentConfig.Backend).Should(BeEmpty()) + + By("create the configmap") + createCm(testCmName, testCm) + // wait for async trigger + time.Sleep(time.Second * 2) + Expect(testCount).Should(Equal(int64(1 + triggeredByDefaultCount))) + Expect(currentConfig.Enable).Should(BeTrue()) + Expect(currentConfig.Backend).Should(Equal(ExporterBackendJaeger)) + }) + }) +}) diff --git a/tracing/suite_test.go b/tracing/suite_test.go new file mode 100644 index 0000000000..adfa9a3f2e --- /dev/null +++ b/tracing/suite_test.go @@ -0,0 +1,103 @@ +/* +Copyright 2021 The Katanomi Authors. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +package tracing + +import ( + "net/http" + "net/http/httptest" + "reflect" + "testing" + + "github.com/emicklei/go-restful/v3" + . "github.com/onsi/ginkgo" + . "github.com/onsi/gomega" + "go.opentelemetry.io/otel" + "k8s.io/client-go/kubernetes" + "sigs.k8s.io/controller-runtime/pkg/client" + "sigs.k8s.io/controller-runtime/pkg/envtest" + logf "sigs.k8s.io/controller-runtime/pkg/log" + "sigs.k8s.io/controller-runtime/pkg/log/zap" +) + +var k8sClient client.Client +var k8sConfigSet *kubernetes.Clientset +var testEnv *envtest.Environment +var originalPropagator = otel.GetTextMapPropagator() +var originalTraceProvider = otel.GetTracerProvider() + +func TestPkg(t *testing.T) { + RegisterFailHandler(Fail) + RunSpecs(t, "Tracing Suite") +} + +var _ = AfterEach(func() { + if !reflect.DeepEqual(otel.GetTextMapPropagator(), originalPropagator) { + otel.SetTextMapPropagator(originalPropagator) + } + if !reflect.DeepEqual(otel.GetTracerProvider(), originalTraceProvider) { + otel.SetTracerProvider(originalTraceProvider) + } +}) + +var _ = BeforeSuite(func() { + logf.SetLogger(zap.New(zap.WriteTo(GinkgoWriter), zap.UseDevMode(true))) + + By("bootstrapping test environment") + testEnv = &envtest.Environment{} + + cfg, err := testEnv.Start() + Expect(err).NotTo(HaveOccurred()) + Expect(cfg).NotTo(BeNil()) + + // +kubebuilder:scaffold:scheme + k8sClient, err = client.New(cfg, client.Options{}) + Expect(err).NotTo(HaveOccurred()) + Expect(k8sClient).NotTo(BeNil()) + + k8sConfigSet, err = kubernetes.NewForConfig(cfg) + Expect(err).NotTo(HaveOccurred()) + Expect(k8sConfigSet).NotTo(BeNil()) +}) + +var _ = AfterSuite(func() { + err := testEnv.Stop() + Expect(err).NotTo(HaveOccurred()) +}) + +func testWebService(path string, filter restful.FilterFunction, handler restful.RouteFunction) ( + *restful.Container, *http.Request) { + ws := &restful.WebService{} + ws.Route(ws.GET(path).To(handler)) + + container := restful.NewContainer() + container.Filter(filter) + container.Add(ws) + + r := httptest.NewRequest("GET", path, nil) + return container, r +} + +func getValidTracingConfig() *Config { + return &Config{ + Enable: true, + SamplingRatio: 1, + Backend: ExporterBackendZipkin, + Zipkin: ZipkinConfig{ + Url: "127.0.0.1:11211", + }, + } +} diff --git a/tracing/tracing.go b/tracing/tracing.go new file mode 100644 index 0000000000..ab5a2cf66e --- /dev/null +++ b/tracing/tracing.go @@ -0,0 +1,224 @@ +/* +Copyright 2021 The Katanomi Authors. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +package tracing + +import ( + "sync" + + "go.opentelemetry.io/otel" + "go.opentelemetry.io/otel/exporters/jaeger" + "go.opentelemetry.io/otel/exporters/zipkin" + "go.opentelemetry.io/otel/propagation" + "go.opentelemetry.io/otel/sdk/resource" + "go.opentelemetry.io/otel/sdk/trace" + semconv "go.opentelemetry.io/otel/semconv/v1.7.0" + traceApi "go.opentelemetry.io/otel/trace" + "go.uber.org/zap" +) + +// NewTracing construct `Tracing` instance +// `TraceOption` can be used to customize settings. +func NewTracing(logger *zap.SugaredLogger, ops ...TraceOption) *Tracing { + tracing := &Tracing{ + logger: logger, + ServiceName: defaultServiceName, + ConfigMapName: ConfigMapName(), + } + + for _, op := range ops { + if op != nil { + op(tracing) + } + } + return tracing +} + +// Tracing describe an entity that watching configuration file changes and +// maintain the global tracing. +type Tracing struct { + applyOnce sync.Once + lock sync.Mutex + logger *zap.SugaredLogger + + ServiceName string + ConfigMapName string + + exporterConstructor ExporterConstructor + resourceConstructor ResourceConstructor + traceProviderConstructor TraceProviderConstructor + + traceProviderOptions []trace.TracerProviderOption + Propagators []propagation.TextMapPropagator +} + +// ApplyConfig Apply configuration and reinitialize global tracing. +// This method will be triggered when the configuration changes. +// If an error occurs, the initialization process will be skipped. +func (t *Tracing) ApplyConfig(cfg *Config) { + t.applyOnce.Do(func() { + otel.SetTracerProvider(traceApi.NewNoopTracerProvider()) + + propagators := propagation.TextMapPropagator(propagation.TraceContext{}) + if len(t.Propagators) > 0 { + propagators = propagation.NewCompositeTextMapPropagator(t.Propagators...) + } + otel.SetTextMapPropagator(propagators) + }) + t.lock.Lock() + defer t.lock.Unlock() + + tp, err := t.traceProvider(cfg) + if err != nil { + return + } + + otel.SetTracerProvider(tp) +} + +// traceProvider construct traceProvider according to the specified configuration. +func (t *Tracing) traceProvider(cfg *Config) (tp traceApi.TracerProvider, err error) { + if t.traceProviderConstructor != nil { + tp, err = t.traceProviderConstructor(cfg) + if err != nil { + t.logger.Errorw("Tracing construct trace provider err", + "err", err, + "config", cfg, + ) + } + return + } + + if cfg == nil { + return traceApi.NewNoopTracerProvider(), nil + } + + exp, err := t.exporter(cfg) + if err != nil { + return + } + + res, err := t.resource(cfg) + if err != nil { + return + } + + ops := []trace.TracerProviderOption{ + trace.WithBatcher(exp), + trace.WithResource(res), + trace.WithSampler(trace.TraceIDRatioBased(cfg.SamplingRatio)), + } + ops = append(ops, t.traceProviderOptions...) + tp = trace.NewTracerProvider(ops...) + return tp, nil +} + +// exporter construct backend exporter according to the specified configuration. +func (t *Tracing) exporter(config *Config) (exporter trace.SpanExporter, err error) { + if t.exporterConstructor != nil { + exporter, err = t.exporterConstructor(config) + if err != nil { + t.logger.Errorw("Tracing construct exporter err", + "err", err, + "config", config, + ) + return nil, err + } + } + + if exporter == nil { + switch config.Backend { + case ExporterBackendJaeger: + exporter, err = t.constructJaegerExporter(config.Jaeger) + case ExporterBackendZipkin: + exporter, err = t.constructZipkinExporter(config.Zipkin) + case ExporterBackendCustom: + t.logger.Errorw("Use WithExporter function to customize exporter", + "err", err, + "config", config, + ) + } + } + + return exporter, nil +} + +// constructZipkinExporter construct zipkin exporter according to the specified configuration. +func (t *Tracing) constructZipkinExporter(cfg ZipkinConfig) (exporter trace.SpanExporter, err error) { + exporter, err = zipkin.New(cfg.Url) + if err != nil { + t.logger.Errorw("Tracing construct zipkin exporter error", + "err", err, + "config", cfg, + ) + } + return exporter, err +} + +// constructJaegerExporter construct jaeger exporter according to the specified configuration. +func (t *Tracing) constructJaegerExporter(cfg JaegerConfig) (exporter trace.SpanExporter, err error) { + ops := make([]jaeger.AgentEndpointOption, 0) + if cfg.Host != "" { + ops = append(ops, jaeger.WithAgentHost(cfg.Host)) + } + if cfg.Port != "" { + ops = append(ops, jaeger.WithAgentPort(cfg.Port)) + } + if cfg.MaxPacketSize > 0 { + ops = append(ops, jaeger.WithMaxPacketSize(cfg.MaxPacketSize)) + } + if cfg.DisableAttemptReconnecting { + ops = append(ops, jaeger.WithDisableAttemptReconnecting()) + } + if cfg.AttemptReconnectInterval > 0 { + ops = append(ops, jaeger.WithAttemptReconnectingInterval(cfg.AttemptReconnectInterval)) + } + exporter, err = jaeger.New( + jaeger.WithAgentEndpoint(ops...), + ) + if err != nil { + t.logger.Errorw("Tracing construct jaeger exporter error", + "err", err, + "config", cfg, + ) + } + return exporter, err +} + +// resource construct Resource according to the specified configuration. +func (t *Tracing) resource(config *Config) (r *resource.Resource, err error) { + if t.resourceConstructor != nil { + r, err = t.resourceConstructor(config) + if err != nil { + t.logger.Errorw("Tracing construct resource err", + "err", err, + "config", config, + ) + return nil, err + } + } + + if r == nil { + r, _ = resource.Merge( + resource.Default(), + resource.NewWithAttributes( + semconv.SchemaURL, + semconv.ServiceNameKey.String(t.ServiceName), + ), + ) + } + return r, nil +} diff --git a/tracing/transport.go b/tracing/transport.go new file mode 100644 index 0000000000..78c682ea87 --- /dev/null +++ b/tracing/transport.go @@ -0,0 +1,102 @@ +/* +Copyright 2021 The Katanomi Authors. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +package tracing + +import ( + "net/http" + + "github.com/go-resty/resty/v2" + "go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp" +) + +// defaultSpanNameFormatter Format span name according to http.Request +func defaultSpanNameFormatter(_ string, r *http.Request) string { + return r.Method + " " + r.URL.EscapedPath() +} + +// WrapTransportForRestyClient Specifically wrapped for the go-resty client for tracking +// Warning: Because some methods in go-resty (such as SetTLSClientConfig、SetProxy) +// rely on *http.Transport, this method must be called after initialization. +// +// correct example: +// restyClient := resty.New() +// restyClient.SetTLSClientConfig(&tls.Config{ +// InsecureSkipVerify: true, +// }) +// tracing.WrapTransportForRestyClient(restyClient) +// +// wrong example: +// restyClient := resty.New() +// tracing.WrapTransportForRestyClient(restyClient) +// restyClient.SetTLSClientConfig(&tls.Config{ +// InsecureSkipVerify: true, +// }) +func WrapTransportForRestyClient(client *resty.Client) { + if client == nil { + return + } + if httpClient := client.GetClient(); httpClient != nil { + client.SetTransport(WrapTransport(httpClient.Transport)) + } else { + client.SetTransport(WrapTransport(nil)) + } +} + +// WrapTransport Wrap Transport for Tracing +// When rt is nil, default transport will be used +func WrapTransport(rt http.RoundTripper) http.RoundTripper { + if rt == nil { + rt = http.DefaultTransport + } + return &Transport{ + originalRT: rt, + Transport: otelhttp.NewTransport(rt, + otelhttp.WithSpanNameFormatter(defaultSpanNameFormatter), + ), + } +} + +// Transport for tracing +type Transport struct { + // originalRT The original RoundTripper + // Because the original RoundTripper is wrapped, other + // interfaces implemented by original RoundTripper will fail. + // + // It is possible to customize the methods and make certain + // implementations available through assertions. + originalRT http.RoundTripper + *otelhttp.Transport +} + +// RoundTrip creates a Span and propagates its context via the provided request's headers +// before handing the request to the configured base RoundTripper. The created span will +// end when the response body is closed or when a read from the body returns io.EOF. +func (t *Transport) RoundTrip(req *http.Request) (*http.Response, error) { + return t.Transport.RoundTrip(req) +} + +// CancelRequest cancels an in-flight request by closing its connection. +// It works when the original RoundTripper implementation canceler interface. +func (t *Transport) CancelRequest(req *http.Request) { + type canceler interface { + CancelRequest(*http.Request) + } + + if rt, ok := t.originalRT.(canceler); ok { + rt.CancelRequest(req) + } +} diff --git a/tracing/transport_test.go b/tracing/transport_test.go new file mode 100644 index 0000000000..f598716f22 --- /dev/null +++ b/tracing/transport_test.go @@ -0,0 +1,133 @@ +/* +Copyright 2021 The Katanomi Authors. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +package tracing + +import ( + "io/ioutil" + "net/http" + "net/http/httptest" + "net/url" + "strings" + "testing" + "time" + + . "github.com/onsi/ginkgo" + . "github.com/onsi/gomega" + "go.opentelemetry.io/otel" + "go.opentelemetry.io/otel/propagation" + "go.opentelemetry.io/otel/sdk/trace" + "golang.org/x/net/context" +) + +var _ = Describe("testing for WrapTransport", func() { + Context("when nil transport is provided", func() { + It("should use the default transport", func() { + rt := WrapTransport(nil) + tp, ok := rt.(*Transport) + Expect(ok).Should(BeTrue()) + Expect(tp.originalRT).Should(Equal(http.DefaultTransport)) + }) + }) + Context("when special transport is provided", func() { + It("should use the special transport", func() { + originalTp := &http.Transport{} + rt := WrapTransport(originalTp) + tp, ok := rt.(*Transport) + Expect(ok).Should(BeTrue()) + Expect(tp.originalRT).Should(Equal(originalTp)) + }) + }) + Context("when handler request", func() { + var shutdown []func() + var client http.Client + + testHttpServer := func(handler func(http.ResponseWriter, *http.Request)) *http.Request { + testServer := httptest.NewServer(http.HandlerFunc(handler)) + shutdown = append(shutdown, func() { + testServer.Close() + }) + testRequest, err := http.NewRequest(http.MethodGet, testServer.URL, nil) + Expect(err).ShouldNot(HaveOccurred()) + return testRequest + } + + BeforeEach(func() { + client = http.Client{Transport: WrapTransport(nil)} + otel.SetTracerProvider(trace.NewTracerProvider( + trace.WithSampler(trace.AlwaysSample()), + )) + otel.SetTextMapPropagator(propagation.TextMapPropagator(propagation.TraceContext{})) + }) + + It("should have a Tracing header", func() { + body := []byte("ok") + req := testHttpServer(func(response http.ResponseWriter, request *http.Request) { + Expect(request.Header.Get("Traceparent")).ShouldNot(BeEmpty()) + _, _ = response.Write(body) + }) + + resp, err := client.Do(req) + Expect(err).ShouldNot(HaveOccurred()) + defer resp.Body.Close() + Expect(err).ShouldNot(HaveOccurred()) + Expect(ioutil.ReadAll(resp.Body)).Should(Equal(body)) + }) + + It("need to cancel the request when it times out", func() { + body := []byte("ok") + req := testHttpServer(func(response http.ResponseWriter, request *http.Request) { + Expect(request.Header.Get("Traceparent")).ShouldNot(BeEmpty()) + time.Sleep(2 * time.Second) + response.Write(body) + }) + + ctx, cancelFunc := context.WithTimeout(context.Background(), time.Second*1) + defer cancelFunc() + req = req.WithContext(ctx) + + _, err := client.Do(req) + Expect(strings.Contains(err.Error(), "deadline exceeded")).Should(BeTrue()) + }) + }) +}) + +func Test_defaultSpanNameFormatter(t *testing.T) { + tests := []struct { + url string + want string + }{ + { + url: "https://a.com/a/b/c?a=b", + want: "GET /a/b/c", + }, + { + url: "https://a.com/a/b/c/?a=b", + want: "GET /a/b/c/", + }, + } + g := NewGomegaWithT(t) + for _, tt := range tests { + url, err := url.Parse(tt.url) + g.Expect(err).ShouldNot(HaveOccurred()) + req := &http.Request{ + URL: url, + Method: http.MethodGet, + } + spanName := defaultSpanNameFormatter("", req) + g.Expect(spanName).Should(Equal(tt.want)) + } +}