1
0
Fork 0

Move code to pkg

This commit is contained in:
Ludovic Fernandez 2019-03-15 09:42:03 +01:00 committed by Traefiker Bot
parent bd4c822670
commit f1b085fa36
465 changed files with 656 additions and 680 deletions

View file

@ -0,0 +1,57 @@
package tracing
import (
"context"
"net/http"
"github.com/containous/alice"
"github.com/containous/traefik/pkg/middlewares"
"github.com/containous/traefik/pkg/tracing"
"github.com/opentracing/opentracing-go"
"github.com/opentracing/opentracing-go/ext"
)
const (
entryPointTypeName = "TracingEntryPoint"
)
// NewEntryPoint creates a new middleware that the incoming request.
func NewEntryPoint(ctx context.Context, t *tracing.Tracing, entryPointName string, next http.Handler) http.Handler {
middlewares.GetLogger(ctx, "tracing", entryPointTypeName).Debug("Creating middleware")
return &entryPointMiddleware{
entryPoint: entryPointName,
Tracing: t,
next: next,
}
}
type entryPointMiddleware struct {
*tracing.Tracing
entryPoint string
next http.Handler
}
func (e *entryPointMiddleware) ServeHTTP(rw http.ResponseWriter, req *http.Request) {
spanCtx, _ := e.Extract(opentracing.HTTPHeaders, tracing.HTTPHeadersCarrier(req.Header))
span, req, finish := e.StartSpanf(req, ext.SpanKindRPCServerEnum, "EntryPoint", []string{e.entryPoint, req.Host}, " ", ext.RPCServerOption(spanCtx))
defer finish()
ext.Component.Set(span, e.ServiceName)
tracing.LogRequest(span, req)
req = req.WithContext(tracing.WithTracing(req.Context(), e.Tracing))
recorder := newStatusCodeRecoder(rw, http.StatusOK)
e.next.ServeHTTP(recorder, req)
tracing.LogResponseCode(span, recorder.Status())
}
// WrapEntryPointHandler Wraps tracing to alice.Constructor.
func WrapEntryPointHandler(ctx context.Context, tracer *tracing.Tracing, entryPointName string) alice.Constructor {
return func(next http.Handler) (http.Handler, error) {
return NewEntryPoint(ctx, tracer, entryPointName, next), nil
}
}

View file

@ -0,0 +1,87 @@
package tracing
import (
"context"
"net/http"
"net/http/httptest"
"testing"
"github.com/containous/traefik/pkg/tracing"
"github.com/opentracing/opentracing-go/ext"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
)
func TestEntryPointMiddleware(t *testing.T) {
type expected struct {
Tags map[string]interface{}
OperationName string
}
testCases := []struct {
desc string
entryPoint string
spanNameLimit int
tracing *trackingBackenMock
expected expected
}{
{
desc: "no truncation test",
entryPoint: "test",
spanNameLimit: 0,
tracing: &trackingBackenMock{
tracer: &MockTracer{Span: &MockSpan{Tags: make(map[string]interface{})}},
},
expected: expected{
Tags: map[string]interface{}{
"span.kind": ext.SpanKindRPCServerEnum,
"http.method": http.MethodGet,
"component": "",
"http.url": "http://www.test.com",
"http.host": "www.test.com",
},
OperationName: "EntryPoint test www.test.com",
},
},
{
desc: "basic test",
entryPoint: "test",
spanNameLimit: 25,
tracing: &trackingBackenMock{
tracer: &MockTracer{Span: &MockSpan{Tags: make(map[string]interface{})}},
},
expected: expected{
Tags: map[string]interface{}{
"span.kind": ext.SpanKindRPCServerEnum,
"http.method": http.MethodGet,
"component": "",
"http.url": "http://www.test.com",
"http.host": "www.test.com",
},
OperationName: "EntryPoint te... ww... 0c15301b",
},
},
}
for _, test := range testCases {
t.Run(test.desc, func(t *testing.T) {
newTracing, err := tracing.NewTracing("", test.spanNameLimit, test.tracing)
require.NoError(t, err)
req := httptest.NewRequest(http.MethodGet, "http://www.test.com", nil)
rw := httptest.NewRecorder()
next := http.HandlerFunc(func(http.ResponseWriter, *http.Request) {
span := test.tracing.tracer.(*MockTracer).Span
tags := span.Tags
assert.Equal(t, test.expected.Tags, tags)
assert.Equal(t, test.expected.OperationName, span.OpName)
})
handler := NewEntryPoint(context.Background(), newTracing, test.entryPoint, next)
handler.ServeHTTP(rw, req)
})
}
}

View file

@ -0,0 +1,58 @@
package tracing
import (
"context"
"net/http"
"github.com/containous/traefik/pkg/middlewares"
"github.com/containous/traefik/pkg/tracing"
"github.com/opentracing/opentracing-go/ext"
)
const (
forwarderTypeName = "TracingForwarder"
)
type forwarderMiddleware struct {
router string
service string
next http.Handler
}
// NewForwarder creates a new forwarder middleware that traces the outgoing request.
func NewForwarder(ctx context.Context, router, service string, next http.Handler) http.Handler {
middlewares.GetLogger(ctx, "tracing", forwarderTypeName).
Debugf("Added outgoing tracing middleware %s", service)
return &forwarderMiddleware{
router: router,
service: service,
next: next,
}
}
func (f *forwarderMiddleware) ServeHTTP(rw http.ResponseWriter, req *http.Request) {
tr, err := tracing.FromContext(req.Context())
if err != nil {
f.next.ServeHTTP(rw, req)
return
}
opParts := []string{f.service, f.router}
span, req, finish := tr.StartSpanf(req, ext.SpanKindRPCClientEnum, "forward", opParts, "/")
defer finish()
span.SetTag("service.name", f.service)
span.SetTag("router.name", f.router)
ext.HTTPMethod.Set(span, req.Method)
ext.HTTPUrl.Set(span, req.URL.String())
span.SetTag("http.host", req.Host)
tracing.InjectRequestHeaders(req)
recorder := newStatusCodeRecoder(rw, 200)
f.next.ServeHTTP(recorder, req)
tracing.LogResponseCode(span, recorder.Status())
}

View file

@ -0,0 +1,136 @@
package tracing
import (
"context"
"net/http"
"net/http/httptest"
"testing"
"github.com/containous/traefik/pkg/tracing"
"github.com/opentracing/opentracing-go/ext"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
)
func TestNewForwarder(t *testing.T) {
type expected struct {
Tags map[string]interface{}
OperationName string
}
testCases := []struct {
desc string
spanNameLimit int
tracing *trackingBackenMock
service string
router string
expected expected
}{
{
desc: "Simple Forward Tracer without truncation and hashing",
spanNameLimit: 101,
tracing: &trackingBackenMock{
tracer: &MockTracer{Span: &MockSpan{Tags: make(map[string]interface{})}},
},
service: "some-service.domain.tld",
router: "some-service.domain.tld",
expected: expected{
Tags: map[string]interface{}{
"http.host": "www.test.com",
"http.method": "GET",
"http.url": "http://www.test.com/toto",
"service.name": "some-service.domain.tld",
"router.name": "some-service.domain.tld",
"span.kind": ext.SpanKindRPCClientEnum,
},
OperationName: "forward some-service.domain.tld/some-service.domain.tld",
},
}, {
desc: "Simple Forward Tracer with truncation and hashing",
spanNameLimit: 101,
tracing: &trackingBackenMock{
tracer: &MockTracer{Span: &MockSpan{Tags: make(map[string]interface{})}},
},
service: "some-service-100.slug.namespace.environment.domain.tld",
router: "some-service-100.slug.namespace.environment.domain.tld",
expected: expected{
Tags: map[string]interface{}{
"http.host": "www.test.com",
"http.method": "GET",
"http.url": "http://www.test.com/toto",
"service.name": "some-service-100.slug.namespace.environment.domain.tld",
"router.name": "some-service-100.slug.namespace.environment.domain.tld",
"span.kind": ext.SpanKindRPCClientEnum,
},
OperationName: "forward some-service-100.slug.namespace.enviro.../some-service-100.slug.namespace.enviro.../bc4a0d48",
},
},
{
desc: "Exactly 101 chars",
spanNameLimit: 101,
tracing: &trackingBackenMock{
tracer: &MockTracer{Span: &MockSpan{Tags: make(map[string]interface{})}},
},
service: "some-service1.namespace.environment.domain.tld",
router: "some-service1.namespace.environment.domain.tld",
expected: expected{
Tags: map[string]interface{}{
"http.host": "www.test.com",
"http.method": "GET",
"http.url": "http://www.test.com/toto",
"service.name": "some-service1.namespace.environment.domain.tld",
"router.name": "some-service1.namespace.environment.domain.tld",
"span.kind": ext.SpanKindRPCClientEnum,
},
OperationName: "forward some-service1.namespace.environment.domain.tld/some-service1.namespace.environment.domain.tld",
},
},
{
desc: "More than 101 chars",
spanNameLimit: 101,
tracing: &trackingBackenMock{
tracer: &MockTracer{Span: &MockSpan{Tags: make(map[string]interface{})}},
},
service: "some-service1.frontend.namespace.environment.domain.tld",
router: "some-service1.backend.namespace.environment.domain.tld",
expected: expected{
Tags: map[string]interface{}{
"http.host": "www.test.com",
"http.method": "GET",
"http.url": "http://www.test.com/toto",
"service.name": "some-service1.frontend.namespace.environment.domain.tld",
"router.name": "some-service1.backend.namespace.environment.domain.tld",
"span.kind": ext.SpanKindRPCClientEnum,
},
OperationName: "forward some-service1.frontend.namespace.envir.../some-service1.backend.namespace.enviro.../fa49dd23",
},
},
}
for _, test := range testCases {
t.Run(test.desc, func(t *testing.T) {
newTracing, err := tracing.NewTracing("", test.spanNameLimit, test.tracing)
require.NoError(t, err)
req := httptest.NewRequest(http.MethodGet, "http://www.test.com/toto", nil)
req = req.WithContext(tracing.WithTracing(req.Context(), newTracing))
rw := httptest.NewRecorder()
next := http.HandlerFunc(func(http.ResponseWriter, *http.Request) {
span := test.tracing.tracer.(*MockTracer).Span
tags := span.Tags
assert.Equal(t, test.expected.Tags, tags)
assert.True(t, len(test.expected.OperationName) <= test.spanNameLimit,
"the len of the operation name %q [len: %d] doesn't respect limit %d",
test.expected.OperationName, len(test.expected.OperationName), test.spanNameLimit)
assert.Equal(t, test.expected.OperationName, span.OpName)
})
handler := NewForwarder(context.Background(), test.router, test.service, next)
handler.ServeHTTP(rw, req)
})
}
}

View file

@ -0,0 +1,70 @@
package tracing
import (
"io"
"github.com/opentracing/opentracing-go"
"github.com/opentracing/opentracing-go/log"
)
type MockTracer struct {
Span *MockSpan
}
// StartSpan belongs to the Tracer interface.
func (n MockTracer) StartSpan(operationName string, opts ...opentracing.StartSpanOption) opentracing.Span {
n.Span.OpName = operationName
return n.Span
}
// Inject belongs to the Tracer interface.
func (n MockTracer) Inject(sp opentracing.SpanContext, format interface{}, carrier interface{}) error {
return nil
}
// Extract belongs to the Tracer interface.
func (n MockTracer) Extract(format interface{}, carrier interface{}) (opentracing.SpanContext, error) {
return nil, opentracing.ErrSpanContextNotFound
}
// MockSpanContext
type MockSpanContext struct{}
func (n MockSpanContext) ForeachBaggageItem(handler func(k, v string) bool) {}
// MockSpan
type MockSpan struct {
OpName string
Tags map[string]interface{}
}
func (n MockSpan) Context() opentracing.SpanContext { return MockSpanContext{} }
func (n MockSpan) SetBaggageItem(key, val string) opentracing.Span {
return MockSpan{Tags: make(map[string]interface{})}
}
func (n MockSpan) BaggageItem(key string) string { return "" }
func (n MockSpan) SetTag(key string, value interface{}) opentracing.Span {
n.Tags[key] = value
return n
}
func (n MockSpan) LogFields(fields ...log.Field) {}
func (n MockSpan) LogKV(keyVals ...interface{}) {}
func (n MockSpan) Finish() {}
func (n MockSpan) FinishWithOptions(opts opentracing.FinishOptions) {}
func (n MockSpan) SetOperationName(operationName string) opentracing.Span { return n }
func (n MockSpan) Tracer() opentracing.Tracer { return MockTracer{} }
func (n MockSpan) LogEvent(event string) {}
func (n MockSpan) LogEventWithPayload(event string, payload interface{}) {}
func (n MockSpan) Log(data opentracing.LogData) {}
func (n MockSpan) Reset() {
n.Tags = make(map[string]interface{})
}
type trackingBackenMock struct {
tracer opentracing.Tracer
}
func (t *trackingBackenMock) Setup(componentName string) (opentracing.Tracer, io.Closer, error) {
opentracing.SetGlobalTracer(t.tracer)
return t.tracer, nil, nil
}

View file

@ -0,0 +1,57 @@
package tracing
import (
"bufio"
"net"
"net/http"
)
type statusCodeRecoder interface {
http.ResponseWriter
Status() int
}
type statusCodeWithoutCloseNotify struct {
http.ResponseWriter
status int
}
// WriteHeader captures the status code for later retrieval.
func (s *statusCodeWithoutCloseNotify) WriteHeader(status int) {
s.status = status
s.ResponseWriter.WriteHeader(status)
}
// Status get response status
func (s *statusCodeWithoutCloseNotify) Status() int {
return s.status
}
// Hijack hijacks the connection
func (s *statusCodeWithoutCloseNotify) Hijack() (net.Conn, *bufio.ReadWriter, error) {
return s.ResponseWriter.(http.Hijacker).Hijack()
}
// Flush sends any buffered data to the client.
func (s *statusCodeWithoutCloseNotify) Flush() {
if flusher, ok := s.ResponseWriter.(http.Flusher); ok {
flusher.Flush()
}
}
type statusCodeWithCloseNotify struct {
*statusCodeWithoutCloseNotify
}
func (s *statusCodeWithCloseNotify) CloseNotify() <-chan bool {
return s.ResponseWriter.(http.CloseNotifier).CloseNotify()
}
// newStatusCodeRecoder returns an initialized statusCodeRecoder.
func newStatusCodeRecoder(rw http.ResponseWriter, status int) statusCodeRecoder {
recorder := &statusCodeWithoutCloseNotify{rw, status}
if _, ok := rw.(http.CloseNotifier); ok {
return &statusCodeWithCloseNotify{recorder}
}
return recorder
}

View file

@ -0,0 +1,68 @@
package tracing
import (
"context"
"net/http"
"github.com/containous/alice"
"github.com/containous/traefik/pkg/log"
"github.com/containous/traefik/pkg/tracing"
"github.com/opentracing/opentracing-go/ext"
)
// Tracable embeds tracing information.
type Tracable interface {
GetTracingInformation() (name string, spanKind ext.SpanKindEnum)
}
// Wrap adds tracability to an alice.Constructor.
func Wrap(ctx context.Context, constructor alice.Constructor) alice.Constructor {
return func(next http.Handler) (http.Handler, error) {
if constructor == nil {
return nil, nil
}
handler, err := constructor(next)
if err != nil {
return nil, err
}
if tracableHandler, ok := handler.(Tracable); ok {
name, spanKind := tracableHandler.GetTracingInformation()
log.FromContext(ctx).WithField(log.MiddlewareName, name).Debug("Adding tracing to middleware")
return NewWrapper(handler, name, spanKind), nil
}
return handler, nil
}
}
// NewWrapper returns a http.Handler struct
func NewWrapper(next http.Handler, name string, spanKind ext.SpanKindEnum) http.Handler {
return &Wrapper{
next: next,
name: name,
spanKind: spanKind,
}
}
// Wrapper is used to wrap http handler middleware.
type Wrapper struct {
next http.Handler
name string
spanKind ext.SpanKindEnum
}
func (w *Wrapper) ServeHTTP(rw http.ResponseWriter, req *http.Request) {
_, err := tracing.FromContext(req.Context())
if err != nil {
w.next.ServeHTTP(rw, req)
return
}
var finish func()
_, req, finish = tracing.StartSpan(req, w.name, w.spanKind)
defer finish()
if w.next != nil {
w.next.ServeHTTP(rw, req)
}
}