Skip to content

Commit

Permalink
Add support for Tempo datasource definition
Browse files Browse the repository at this point in the history
  • Loading branch information
K-Phoen committed Dec 23, 2021
1 parent 3b74334 commit d60928d
Show file tree
Hide file tree
Showing 4 changed files with 305 additions and 0 deletions.
128 changes: 128 additions & 0 deletions datasource/tempo/options.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,128 @@
package tempo

import "time"

// Default configures this datasource to be the default one.
func Default() Option {
return func(datasource *Tempo) {
datasource.builder.IsDefault = true
}
}

// Timeout sets the timeout for HTTP requests.
func Timeout(timeout time.Duration) Option {
return func(datasource *Tempo) {
datasource.builder.JSONData.(map[string]interface{})["timeout"] = int(timeout.Seconds())
}
}

// BasicAuth configures basic authentication for this datasource.
func BasicAuth(username string, password string) Option {
return func(datasource *Tempo) {
yep := true
datasource.builder.BasicAuth = &yep
datasource.builder.BasicAuthUser = &username
datasource.builder.BasicAuthPassword = &password
}
}

// SkipTLSVerify disables verification of SSL certificates.
func SkipTLSVerify() Option {
return func(datasource *Tempo) {
datasource.builder.JSONData.(map[string]interface{})["tlsSkipVerify"] = true
}
}

// WithCertificate sets a self-signed certificate that can be verified against.
func WithCertificate(certificate string) Option {
return func(datasource *Tempo) {
datasource.builder.JSONData.(map[string]interface{})["tlsSkipVerify"] = false
datasource.builder.JSONData.(map[string]interface{})["tlsAuthWithCACert"] = true
datasource.builder.SecureJSONData.(map[string]interface{})["tlsCACert"] = certificate
}
}

// WithCredentials joins credentials such as cookies or auth headers to cross-site requests.
func WithCredentials() Option {
return func(datasource *Tempo) {
datasource.builder.WithCredentials = true
}
}

// ForwardOauthIdentity forward the user's upstream OAuth identity to the data
// source (Their access token gets passed along).
func ForwardOauthIdentity() Option {
return func(datasource *Tempo) {
datasource.builder.JSONData.(map[string]interface{})["oauthPassThru"] = true
}
}

// ForwardCookies configures a list of cookies that should be forwarded to the
// datasource.
func ForwardCookies(cookies ...string) Option {
return func(datasource *Tempo) {
datasource.builder.JSONData.(map[string]interface{})["keepCookies"] = cookies
}
}

// WithNodeGraph enables the Node Graph visualization in the trace viewer.
func WithNodeGraph() Option {
return func(datasource *Tempo) {
datasource.builder.JSONData.(map[string]interface{})["nodeGraph"] = map[string]interface{}{
"enabled": true,
}
}
}

// TraceToLogs defines how to navigate from a trace span to the selected datasource logs.
func TraceToLogs(logsDatasourceUID string, options ...TraceToLogsOption) Option {
settings := map[string]interface{}{
"datasourceUid": logsDatasourceUID,
}

for _, opt := range options {
opt(settings)
}

return func(datasource *Tempo) {
datasource.builder.JSONData.(map[string]interface{})["tracesToLogs"] = settings
}
}

// Tags defines tags that will be used in the Loki query.
// Default tags: 'cluster', 'hostname', 'namespace', 'pod'.
func Tags(tags ...string) TraceToLogsOption {
return func(settings map[string]interface{}) {
settings["tags"] = tags
}
}

// SpanStartShift shifts the start time of the span.
// Default 0 (Time units can be used here, for example: 5s, 1m, 3h)
func SpanStartShift(shift time.Duration) TraceToLogsOption {
return func(settings map[string]interface{}) {
settings["spanStartTimeShift"] = shift.String()
}
}

// SpanEndShift shifts the start time of the span.
// Default 0 (Time units can be used here, for example: 5s, 1m, 3h)
func SpanEndShift(shift time.Duration) TraceToLogsOption {
return func(settings map[string]interface{}) {
settings["spanEndTimeShift"] = shift.String()
}
}

// FilterByTrace filters logs by Trace ID. Appends '|=<trace id>' to the query.
func FilterByTrace() TraceToLogsOption {
return func(settings map[string]interface{}) {
settings["filterByTraceID"] = true
}
}

// FilterBySpan filters logs by Trace ID. Appends '|=<trace id>' to the query.
func FilterBySpan() TraceToLogsOption {
return func(settings map[string]interface{}) {
settings["filterBySpanID"] = true
}
}
111 changes: 111 additions & 0 deletions datasource/tempo/options_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,111 @@
package tempo

import (
"testing"
"time"

"github.com/stretchr/testify/require"
)

func TestDefault(t *testing.T) {
req := require.New(t)

datasource := New("", "", Default())

req.True(datasource.builder.IsDefault)
}

func TestBasicAuth(t *testing.T) {
req := require.New(t)

datasource := New("", "", BasicAuth("joe", "lafrite"))

req.True(*datasource.builder.BasicAuth)
req.Equal("joe", *datasource.builder.BasicAuthUser)
req.Equal("lafrite", *datasource.builder.BasicAuthPassword)
}

func TestTimeout(t *testing.T) {
req := require.New(t)

datasource := New("", "", Timeout(30*time.Second))

req.Equal(30, datasource.builder.JSONData.(map[string]interface{})["timeout"])
}

func TestSkipTlsVerify(t *testing.T) {
req := require.New(t)

datasource := New("", "", SkipTLSVerify())

req.Equal(true, datasource.builder.JSONData.(map[string]interface{})["tlsSkipVerify"])
}

func TestWithCertificate(t *testing.T) {
req := require.New(t)

datasource := New("", "", WithCertificate("certificate-content"))

req.Equal(false, datasource.builder.JSONData.(map[string]interface{})["tlsSkipVerify"])
req.Equal(true, datasource.builder.JSONData.(map[string]interface{})["tlsAuthWithCACert"])
req.Equal("certificate-content", datasource.builder.SecureJSONData.(map[string]interface{})["tlsCACert"])
}

func TestWithCredentials(t *testing.T) {
req := require.New(t)

datasource := New("", "", WithCredentials())

req.True(datasource.builder.WithCredentials)
}

func TestForwardOauthIdentity(t *testing.T) {
req := require.New(t)

datasource := New("", "", ForwardOauthIdentity())

req.Equal(true, datasource.builder.JSONData.(map[string]interface{})["oauthPassThru"])
}

func TestForwardCookies(t *testing.T) {
req := require.New(t)

datasource := New("", "", ForwardCookies("foo", "bar"))

req.ElementsMatch([]string{"foo", "bar"}, datasource.builder.JSONData.(map[string]interface{})["keepCookies"])
}

func TestWithNodeGraph(t *testing.T) {
req := require.New(t)

datasource := New("", "", WithNodeGraph())

jsonData := datasource.builder.JSONData.(map[string]interface{})

req.Equal(true, jsonData["nodeGraph"].(map[string]interface{})["enabled"])
}

func TestTraceToLogs(t *testing.T) {
req := require.New(t)

lokiDatasourceUID := "lala"
datasource := New("", "", TraceToLogs(
lokiDatasourceUID,
Tags("pod", "namespace"),
SpanStartShift(2*time.Second),
SpanEndShift(1*time.Second),
FilterByTrace(),
FilterBySpan(),
))

jsonData := datasource.builder.JSONData.(map[string]interface{})
traceToLogsSettings := jsonData["tracesToLogs"].(map[string]interface{})

req.NotEmpty(traceToLogsSettings)
req.Equal(lokiDatasourceUID, traceToLogsSettings["datasourceUid"])
req.ElementsMatch([]string{"pod", "namespace"}, traceToLogsSettings["tags"])
req.Equal("2s", traceToLogsSettings["spanStartTimeShift"])
req.Equal("1s", traceToLogsSettings["spanEndTimeShift"])
req.Equal(true, traceToLogsSettings["filterByTraceID"])
req.Equal(true, traceToLogsSettings["filterBySpanID"])
}
44 changes: 44 additions & 0 deletions datasource/tempo/tempo.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
package tempo

import (
"encoding/json"

"github.com/K-Phoen/grabana/datasource"
"github.com/K-Phoen/sdk"
)

var _ datasource.Datasource = Tempo{}

type Tempo struct {
builder *sdk.Datasource
}

type Option func(datasource *Tempo)
type TraceToLogsOption func(settings map[string]interface{})

func New(name string, url string, options ...Option) Tempo {
jaeger := &Tempo{
builder: &sdk.Datasource{
Name: name,
Type: "tempo",
Access: "proxy",
URL: url,
JSONData: map[string]interface{}{},
SecureJSONData: map[string]interface{}{},
},
}

for _, opt := range options {
opt(jaeger)
}

return *jaeger
}

func (datasource Tempo) Name() string {
return datasource.builder.Name
}

func (datasource Tempo) MarshalJSON() ([]byte, error) {
return json.Marshal(datasource.builder)
}
22 changes: 22 additions & 0 deletions datasource/tempo/tempo_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
package tempo

import (
"testing"

"github.com/stretchr/testify/require"
)

func TestNewTempo(t *testing.T) {
req := require.New(t)

datasource := New("ds-tempo", "http://localhost:3100")

req.Equal("ds-tempo", datasource.Name())
req.Equal("http://localhost:3100", datasource.builder.URL)
req.Equal("tempo", datasource.builder.Type)
req.NotNil(datasource.builder.JSONData)
req.NotNil(datasource.builder.SecureJSONData)

_, err := datasource.MarshalJSON()
req.NoError(err)
}

0 comments on commit d60928d

Please sign in to comment.