mirror of https://github.com/grafana/grafana.git
Jaeger: Run dependency graph queries through backend behind the feature toggle (#103768)
* Start with dependency graph * Update and add tests * Fix lint and ordering in test
This commit is contained in:
parent
7aa028122b
commit
242ac6bfa7
|
|
@ -26,6 +26,20 @@ type ServicesResponse struct {
|
|||
Total int `json:"total"`
|
||||
}
|
||||
|
||||
type DependenciesResponse struct {
|
||||
Data []ServiceDependency `json:"data"`
|
||||
Errors []struct {
|
||||
Code int `json:"code"`
|
||||
Msg string `json:"msg"`
|
||||
} `json:"errors"`
|
||||
}
|
||||
|
||||
type ServiceDependency struct {
|
||||
Parent string `json:"parent"`
|
||||
Child string `json:"child"`
|
||||
CallCount int `json:"callCount"`
|
||||
}
|
||||
|
||||
func New(url string, hc *http.Client, logger log.Logger, traceIdTimeEnabled bool) (JaegerClient, error) {
|
||||
client := JaegerClient{
|
||||
logger: logger,
|
||||
|
|
@ -158,3 +172,59 @@ func (j *JaegerClient) Trace(ctx context.Context, traceID string, start, end int
|
|||
trace = response.Data[0]
|
||||
return trace, err
|
||||
}
|
||||
|
||||
func (j *JaegerClient) Dependencies(ctx context.Context, start, end int64) (DependenciesResponse, error) {
|
||||
logger := j.logger.FromContext(ctx)
|
||||
var dependencies DependenciesResponse
|
||||
|
||||
u, err := url.JoinPath(j.url, "/api/dependencies")
|
||||
if err != nil {
|
||||
return dependencies, backend.DownstreamError(fmt.Errorf("failed to join url: %w", err))
|
||||
}
|
||||
|
||||
// Add time parameters
|
||||
parsedURL, err := url.Parse(u)
|
||||
if err != nil {
|
||||
return dependencies, backend.DownstreamError(fmt.Errorf("failed to parse url: %w", err))
|
||||
}
|
||||
|
||||
query := parsedURL.Query()
|
||||
if end > 0 {
|
||||
query.Set("endTs", fmt.Sprintf("%d", end))
|
||||
}
|
||||
if start > 0 {
|
||||
lookback := end - start
|
||||
query.Set("lookback", fmt.Sprintf("%d", lookback))
|
||||
}
|
||||
|
||||
parsedURL.RawQuery = query.Encode()
|
||||
u = parsedURL.String()
|
||||
|
||||
res, err := j.httpClient.Get(u)
|
||||
if err != nil {
|
||||
if backend.IsDownstreamHTTPError(err) {
|
||||
return dependencies, backend.DownstreamError(err)
|
||||
}
|
||||
return dependencies, err
|
||||
}
|
||||
|
||||
defer func() {
|
||||
if err = res.Body.Close(); err != nil {
|
||||
logger.Error("Failed to close response body", "error", err)
|
||||
}
|
||||
}()
|
||||
|
||||
if res != nil && res.StatusCode/100 != 2 {
|
||||
err := backend.DownstreamError(fmt.Errorf("request failed: %s", res.Status))
|
||||
if backend.ErrorSourceFromHTTPStatus(res.StatusCode) == backend.ErrorSourceDownstream {
|
||||
return dependencies, backend.DownstreamError(err)
|
||||
}
|
||||
return dependencies, err
|
||||
}
|
||||
|
||||
if err := json.NewDecoder(res.Body).Decode(&dependencies); err != nil {
|
||||
return dependencies, err
|
||||
}
|
||||
|
||||
return dependencies, nil
|
||||
}
|
||||
|
|
|
|||
|
|
@ -276,3 +276,112 @@ func TestJaegerClient_Trace(t *testing.T) {
|
|||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestJaegerClient_Dependencies(t *testing.T) {
|
||||
tests := []struct {
|
||||
name string
|
||||
start int64
|
||||
end int64
|
||||
mockResponse string
|
||||
mockStatusCode int
|
||||
mockStatus string
|
||||
expectedURL string
|
||||
expectError bool
|
||||
expectedError error
|
||||
}{
|
||||
{
|
||||
name: "Successful response with time range",
|
||||
start: 1000,
|
||||
end: 2000,
|
||||
mockResponse: `{"data":[{"parent":"serviceA","child":"serviceB","callCount":1}]}`,
|
||||
mockStatusCode: http.StatusOK,
|
||||
mockStatus: "OK",
|
||||
expectedURL: "/api/dependencies?endTs=2000&lookback=1000",
|
||||
expectError: false,
|
||||
expectedError: nil,
|
||||
},
|
||||
{
|
||||
name: "Successful response without time range",
|
||||
start: 0,
|
||||
end: 0,
|
||||
mockResponse: `{"data":[{"parent":"serviceA","child":"serviceB","callCount":1}]}`,
|
||||
mockStatusCode: http.StatusOK,
|
||||
mockStatus: "OK",
|
||||
expectedURL: "/api/dependencies",
|
||||
expectError: false,
|
||||
expectedError: nil,
|
||||
},
|
||||
{
|
||||
name: "Non-200 response",
|
||||
start: 1000,
|
||||
end: 2000,
|
||||
mockResponse: "",
|
||||
mockStatusCode: http.StatusInternalServerError,
|
||||
mockStatus: "Internal Server Error",
|
||||
expectedURL: "/api/dependencies?endTs=2000&lookback=1000",
|
||||
expectError: true,
|
||||
expectedError: backend.PluginError(errors.New("Internal Server Error")),
|
||||
},
|
||||
{
|
||||
name: "Invalid JSON response",
|
||||
start: 1000,
|
||||
end: 2000,
|
||||
mockResponse: `{invalid json`,
|
||||
mockStatusCode: http.StatusOK,
|
||||
mockStatus: "OK",
|
||||
expectedURL: "/api/dependencies?endTs=2000&lookback=1000",
|
||||
expectError: true,
|
||||
expectedError: &json.SyntaxError{},
|
||||
},
|
||||
{
|
||||
name: "Empty dependencies response and no errors",
|
||||
start: 1000,
|
||||
end: 2000,
|
||||
mockResponse: `{"data":[]}`,
|
||||
mockStatusCode: http.StatusOK,
|
||||
mockStatus: "OK",
|
||||
expectedURL: "/api/dependencies?endTs=2000&lookback=1000",
|
||||
expectError: false,
|
||||
expectedError: nil,
|
||||
},
|
||||
{
|
||||
name: "Response with errors",
|
||||
start: 1000,
|
||||
end: 2000,
|
||||
mockResponse: `{"data":[],"errors":[{"code":500,"msg":"Internal error"}]}`,
|
||||
mockStatusCode: http.StatusOK,
|
||||
mockStatus: "OK",
|
||||
expectedURL: "/api/dependencies?endTs=2000&lookback=1000",
|
||||
expectError: false,
|
||||
expectedError: nil,
|
||||
},
|
||||
}
|
||||
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
var actualURL string
|
||||
server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
||||
actualURL = r.URL.String()
|
||||
w.WriteHeader(tt.mockStatusCode)
|
||||
_, _ = w.Write([]byte(tt.mockResponse))
|
||||
}))
|
||||
defer server.Close()
|
||||
|
||||
client, err := New(server.URL, server.Client(), log.NewNullLogger(), false)
|
||||
assert.NoError(t, err)
|
||||
|
||||
dependencies, err := client.Dependencies(context.Background(), tt.start, tt.end)
|
||||
|
||||
if tt.expectError {
|
||||
assert.Error(t, err)
|
||||
if tt.expectedError != nil {
|
||||
assert.IsType(t, tt.expectedError, err)
|
||||
}
|
||||
} else {
|
||||
assert.NoError(t, err)
|
||||
assert.NotNil(t, dependencies)
|
||||
}
|
||||
assert.Equal(t, tt.expectedURL, actualURL)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -4,6 +4,7 @@ import (
|
|||
"context"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"sort"
|
||||
|
||||
"github.com/grafana/grafana-plugin-sdk-go/backend"
|
||||
"github.com/grafana/grafana-plugin-sdk-go/data"
|
||||
|
|
@ -45,6 +46,23 @@ func queryData(ctx context.Context, dsInfo *datasourceInfo, req *backend.QueryDa
|
|||
Frames: []*data.Frame{frame},
|
||||
}
|
||||
}
|
||||
|
||||
if query.QueryType == "dependencyGraph" {
|
||||
dependencies, err := dsInfo.JaegerClient.Dependencies(ctx, q.TimeRange.From.UnixMilli(), q.TimeRange.To.UnixMilli())
|
||||
if err != nil {
|
||||
response.Responses[q.RefID] = backend.ErrorResponseWithErrorSource(err)
|
||||
continue
|
||||
}
|
||||
|
||||
if len(dependencies.Errors) > 0 {
|
||||
response.Responses[q.RefID] = backend.ErrorResponseWithErrorSource(backend.DownstreamError(fmt.Errorf("error while fetching dependencies, code: %v, message: %v", dependencies.Errors[0].Code, dependencies.Errors[0].Msg)))
|
||||
continue
|
||||
}
|
||||
frames := transformDependenciesResponse(dependencies, q.RefID)
|
||||
response.Responses[q.RefID] = backend.DataResponse{
|
||||
Frames: frames,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return response, nil
|
||||
|
|
@ -215,3 +233,70 @@ type TracesResponse struct {
|
|||
Offset int `json:"offset"`
|
||||
Total int `json:"total"`
|
||||
}
|
||||
|
||||
func transformDependenciesResponse(dependencies DependenciesResponse, refID string) []*data.Frame {
|
||||
// Create nodes frame
|
||||
nodesFrame := data.NewFrame(refID+"_nodes",
|
||||
data.NewField("id", nil, []string{}),
|
||||
data.NewField("title", nil, []string{}),
|
||||
)
|
||||
nodesFrame.Meta = &data.FrameMeta{
|
||||
PreferredVisualization: "nodeGraph",
|
||||
}
|
||||
|
||||
// Create edges frame
|
||||
mainStatField := data.NewField("mainstat", nil, []int64{})
|
||||
mainStatField.Config = &data.FieldConfig{
|
||||
DisplayName: "Call count",
|
||||
}
|
||||
edgesFrame := data.NewFrame(refID+"_edges",
|
||||
data.NewField("id", nil, []string{}),
|
||||
data.NewField("source", nil, []string{}),
|
||||
data.NewField("target", nil, []string{}),
|
||||
mainStatField,
|
||||
)
|
||||
|
||||
edgesFrame.Meta = &data.FrameMeta{
|
||||
PreferredVisualization: "nodeGraph",
|
||||
}
|
||||
|
||||
// Return early if there are no dependencies
|
||||
if len(dependencies.Data) == 0 {
|
||||
return []*data.Frame{nodesFrame, edgesFrame}
|
||||
}
|
||||
|
||||
// Create a map to store unique service nodes
|
||||
servicesByName := make(map[string]bool)
|
||||
|
||||
// Process each dependency
|
||||
for _, dependency := range dependencies.Data {
|
||||
// Add services to the map to track unique services
|
||||
servicesByName[dependency.Parent] = true
|
||||
servicesByName[dependency.Child] = true
|
||||
|
||||
// Add edge data
|
||||
edgesFrame.AppendRow(
|
||||
dependency.Parent+"--"+dependency.Child,
|
||||
dependency.Parent,
|
||||
dependency.Child,
|
||||
int64(dependency.CallCount),
|
||||
)
|
||||
}
|
||||
|
||||
// Convert map keys to slice and sort them - this is to ensure the returned nodes are in a consistent order
|
||||
services := make([]string, 0, len(servicesByName))
|
||||
for service := range servicesByName {
|
||||
services = append(services, service)
|
||||
}
|
||||
sort.Strings(services)
|
||||
|
||||
// Add node data in sorted order
|
||||
for _, service := range services {
|
||||
nodesFrame.AppendRow(
|
||||
service,
|
||||
service,
|
||||
)
|
||||
}
|
||||
|
||||
return []*data.Frame{nodesFrame, edgesFrame}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -184,3 +184,97 @@ func TestTransformTraceResponse(t *testing.T) {
|
|||
experimental.CheckGoldenJSONFrame(t, "./testdata", "complex_trace.golden", frame, false)
|
||||
})
|
||||
}
|
||||
|
||||
func TestTransformDependenciesResponse(t *testing.T) {
|
||||
t.Run("simple_dependencies", func(t *testing.T) {
|
||||
dependencies := DependenciesResponse{
|
||||
Data: []ServiceDependency{
|
||||
{
|
||||
Parent: "serviceA",
|
||||
Child: "serviceB",
|
||||
CallCount: 1,
|
||||
},
|
||||
{
|
||||
Parent: "serviceA",
|
||||
Child: "serviceC",
|
||||
CallCount: 2,
|
||||
},
|
||||
{
|
||||
Parent: "serviceB",
|
||||
Child: "serviceC",
|
||||
CallCount: 3,
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
frames := transformDependenciesResponse(dependencies, "test")
|
||||
experimental.CheckGoldenJSONFrame(t, "./testdata", "simple_dependencies_nodes.golden", frames[0], false)
|
||||
experimental.CheckGoldenJSONFrame(t, "./testdata", "simple_dependencies_edges.golden", frames[1], false)
|
||||
})
|
||||
|
||||
t.Run("empty_dependencies", func(t *testing.T) {
|
||||
dependencies := DependenciesResponse{
|
||||
Data: []ServiceDependency{},
|
||||
}
|
||||
|
||||
frames := transformDependenciesResponse(dependencies, "test")
|
||||
experimental.CheckGoldenJSONFrame(t, "./testdata", "empty_dependencies_nodes.golden", frames[0], false)
|
||||
experimental.CheckGoldenJSONFrame(t, "./testdata", "empty_dependencies_edges.golden", frames[1], false)
|
||||
})
|
||||
|
||||
t.Run("complex_dependencies", func(t *testing.T) {
|
||||
dependencies := DependenciesResponse{
|
||||
Data: []ServiceDependency{
|
||||
{
|
||||
Parent: "frontend",
|
||||
Child: "auth-service",
|
||||
CallCount: 150,
|
||||
},
|
||||
{
|
||||
Parent: "frontend",
|
||||
Child: "api-gateway",
|
||||
CallCount: 300,
|
||||
},
|
||||
{
|
||||
Parent: "api-gateway",
|
||||
Child: "user-service",
|
||||
CallCount: 200,
|
||||
},
|
||||
{
|
||||
Parent: "api-gateway",
|
||||
Child: "order-service",
|
||||
CallCount: 100,
|
||||
},
|
||||
{
|
||||
Parent: "order-service",
|
||||
Child: "payment-service",
|
||||
CallCount: 80,
|
||||
},
|
||||
{
|
||||
Parent: "order-service",
|
||||
Child: "inventory-service",
|
||||
CallCount: 90,
|
||||
},
|
||||
{
|
||||
Parent: "user-service",
|
||||
Child: "database",
|
||||
CallCount: 500,
|
||||
},
|
||||
{
|
||||
Parent: "payment-service",
|
||||
Child: "database",
|
||||
CallCount: 200,
|
||||
},
|
||||
{
|
||||
Parent: "inventory-service",
|
||||
Child: "database",
|
||||
CallCount: 300,
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
frames := transformDependenciesResponse(dependencies, "test")
|
||||
experimental.CheckGoldenJSONFrame(t, "./testdata", "complex_dependencies_nodes.golden", frames[0], false)
|
||||
experimental.CheckGoldenJSONFrame(t, "./testdata", "complex_dependencies_edges.golden", frames[1], false)
|
||||
})
|
||||
}
|
||||
|
|
|
|||
|
|
@ -0,0 +1,127 @@
|
|||
// 🌟 This was machine generated. Do not edit. 🌟
|
||||
//
|
||||
// Frame[0] {
|
||||
// "typeVersion": [
|
||||
// 0,
|
||||
// 0
|
||||
// ],
|
||||
// "preferredVisualisationType": "nodeGraph"
|
||||
// }
|
||||
// Name: test_edges
|
||||
// Dimensions: 4 Fields by 9 Rows
|
||||
// +----------------------------------+-------------------+-------------------+----------------+
|
||||
// | Name: id | Name: source | Name: target | Name: mainstat |
|
||||
// | Labels: | Labels: | Labels: | Labels: |
|
||||
// | Type: []string | Type: []string | Type: []string | Type: []int64 |
|
||||
// +----------------------------------+-------------------+-------------------+----------------+
|
||||
// | frontend--auth-service | frontend | auth-service | 150 |
|
||||
// | frontend--api-gateway | frontend | api-gateway | 300 |
|
||||
// | api-gateway--user-service | api-gateway | user-service | 200 |
|
||||
// | api-gateway--order-service | api-gateway | order-service | 100 |
|
||||
// | order-service--payment-service | order-service | payment-service | 80 |
|
||||
// | order-service--inventory-service | order-service | inventory-service | 90 |
|
||||
// | user-service--database | user-service | database | 500 |
|
||||
// | payment-service--database | payment-service | database | 200 |
|
||||
// | inventory-service--database | inventory-service | database | 300 |
|
||||
// +----------------------------------+-------------------+-------------------+----------------+
|
||||
//
|
||||
//
|
||||
// 🌟 This was machine generated. Do not edit. 🌟
|
||||
{
|
||||
"status": 200,
|
||||
"frames": [
|
||||
{
|
||||
"schema": {
|
||||
"name": "test_edges",
|
||||
"meta": {
|
||||
"typeVersion": [
|
||||
0,
|
||||
0
|
||||
],
|
||||
"preferredVisualisationType": "nodeGraph"
|
||||
},
|
||||
"fields": [
|
||||
{
|
||||
"name": "id",
|
||||
"type": "string",
|
||||
"typeInfo": {
|
||||
"frame": "string"
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "source",
|
||||
"type": "string",
|
||||
"typeInfo": {
|
||||
"frame": "string"
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "target",
|
||||
"type": "string",
|
||||
"typeInfo": {
|
||||
"frame": "string"
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "mainstat",
|
||||
"type": "number",
|
||||
"typeInfo": {
|
||||
"frame": "int64"
|
||||
},
|
||||
"config": {
|
||||
"displayName": "Call count"
|
||||
}
|
||||
}
|
||||
]
|
||||
},
|
||||
"data": {
|
||||
"values": [
|
||||
[
|
||||
"frontend--auth-service",
|
||||
"frontend--api-gateway",
|
||||
"api-gateway--user-service",
|
||||
"api-gateway--order-service",
|
||||
"order-service--payment-service",
|
||||
"order-service--inventory-service",
|
||||
"user-service--database",
|
||||
"payment-service--database",
|
||||
"inventory-service--database"
|
||||
],
|
||||
[
|
||||
"frontend",
|
||||
"frontend",
|
||||
"api-gateway",
|
||||
"api-gateway",
|
||||
"order-service",
|
||||
"order-service",
|
||||
"user-service",
|
||||
"payment-service",
|
||||
"inventory-service"
|
||||
],
|
||||
[
|
||||
"auth-service",
|
||||
"api-gateway",
|
||||
"user-service",
|
||||
"order-service",
|
||||
"payment-service",
|
||||
"inventory-service",
|
||||
"database",
|
||||
"database",
|
||||
"database"
|
||||
],
|
||||
[
|
||||
150,
|
||||
300,
|
||||
200,
|
||||
100,
|
||||
80,
|
||||
90,
|
||||
500,
|
||||
200,
|
||||
300
|
||||
]
|
||||
]
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
|
|
@ -0,0 +1,85 @@
|
|||
// 🌟 This was machine generated. Do not edit. 🌟
|
||||
//
|
||||
// Frame[0] {
|
||||
// "typeVersion": [
|
||||
// 0,
|
||||
// 0
|
||||
// ],
|
||||
// "preferredVisualisationType": "nodeGraph"
|
||||
// }
|
||||
// Name: test_nodes
|
||||
// Dimensions: 2 Fields by 8 Rows
|
||||
// +-------------------+-------------------+
|
||||
// | Name: id | Name: title |
|
||||
// | Labels: | Labels: |
|
||||
// | Type: []string | Type: []string |
|
||||
// +-------------------+-------------------+
|
||||
// | api-gateway | api-gateway |
|
||||
// | auth-service | auth-service |
|
||||
// | database | database |
|
||||
// | frontend | frontend |
|
||||
// | inventory-service | inventory-service |
|
||||
// | order-service | order-service |
|
||||
// | payment-service | payment-service |
|
||||
// | user-service | user-service |
|
||||
// +-------------------+-------------------+
|
||||
//
|
||||
//
|
||||
// 🌟 This was machine generated. Do not edit. 🌟
|
||||
{
|
||||
"status": 200,
|
||||
"frames": [
|
||||
{
|
||||
"schema": {
|
||||
"name": "test_nodes",
|
||||
"meta": {
|
||||
"typeVersion": [
|
||||
0,
|
||||
0
|
||||
],
|
||||
"preferredVisualisationType": "nodeGraph"
|
||||
},
|
||||
"fields": [
|
||||
{
|
||||
"name": "id",
|
||||
"type": "string",
|
||||
"typeInfo": {
|
||||
"frame": "string"
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "title",
|
||||
"type": "string",
|
||||
"typeInfo": {
|
||||
"frame": "string"
|
||||
}
|
||||
}
|
||||
]
|
||||
},
|
||||
"data": {
|
||||
"values": [
|
||||
[
|
||||
"api-gateway",
|
||||
"auth-service",
|
||||
"database",
|
||||
"frontend",
|
||||
"inventory-service",
|
||||
"order-service",
|
||||
"payment-service",
|
||||
"user-service"
|
||||
],
|
||||
[
|
||||
"api-gateway",
|
||||
"auth-service",
|
||||
"database",
|
||||
"frontend",
|
||||
"inventory-service",
|
||||
"order-service",
|
||||
"payment-service",
|
||||
"user-service"
|
||||
]
|
||||
]
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
|
|
@ -0,0 +1,78 @@
|
|||
// 🌟 This was machine generated. Do not edit. 🌟
|
||||
//
|
||||
// Frame[0] {
|
||||
// "typeVersion": [
|
||||
// 0,
|
||||
// 0
|
||||
// ],
|
||||
// "preferredVisualisationType": "nodeGraph"
|
||||
// }
|
||||
// Name: test_edges
|
||||
// Dimensions: 4 Fields by 0 Rows
|
||||
// +----------------+----------------+----------------+----------------+
|
||||
// | Name: id | Name: source | Name: target | Name: mainstat |
|
||||
// | Labels: | Labels: | Labels: | Labels: |
|
||||
// | Type: []string | Type: []string | Type: []string | Type: []int64 |
|
||||
// +----------------+----------------+----------------+----------------+
|
||||
// +----------------+----------------+----------------+----------------+
|
||||
//
|
||||
//
|
||||
// 🌟 This was machine generated. Do not edit. 🌟
|
||||
{
|
||||
"status": 200,
|
||||
"frames": [
|
||||
{
|
||||
"schema": {
|
||||
"name": "test_edges",
|
||||
"meta": {
|
||||
"typeVersion": [
|
||||
0,
|
||||
0
|
||||
],
|
||||
"preferredVisualisationType": "nodeGraph"
|
||||
},
|
||||
"fields": [
|
||||
{
|
||||
"name": "id",
|
||||
"type": "string",
|
||||
"typeInfo": {
|
||||
"frame": "string"
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "source",
|
||||
"type": "string",
|
||||
"typeInfo": {
|
||||
"frame": "string"
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "target",
|
||||
"type": "string",
|
||||
"typeInfo": {
|
||||
"frame": "string"
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "mainstat",
|
||||
"type": "number",
|
||||
"typeInfo": {
|
||||
"frame": "int64"
|
||||
},
|
||||
"config": {
|
||||
"displayName": "Call count"
|
||||
}
|
||||
}
|
||||
]
|
||||
},
|
||||
"data": {
|
||||
"values": [
|
||||
[],
|
||||
[],
|
||||
[],
|
||||
[]
|
||||
]
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
|
|
@ -0,0 +1,59 @@
|
|||
// 🌟 This was machine generated. Do not edit. 🌟
|
||||
//
|
||||
// Frame[0] {
|
||||
// "typeVersion": [
|
||||
// 0,
|
||||
// 0
|
||||
// ],
|
||||
// "preferredVisualisationType": "nodeGraph"
|
||||
// }
|
||||
// Name: test_nodes
|
||||
// Dimensions: 2 Fields by 0 Rows
|
||||
// +----------------+----------------+
|
||||
// | Name: id | Name: title |
|
||||
// | Labels: | Labels: |
|
||||
// | Type: []string | Type: []string |
|
||||
// +----------------+----------------+
|
||||
// +----------------+----------------+
|
||||
//
|
||||
//
|
||||
// 🌟 This was machine generated. Do not edit. 🌟
|
||||
{
|
||||
"status": 200,
|
||||
"frames": [
|
||||
{
|
||||
"schema": {
|
||||
"name": "test_nodes",
|
||||
"meta": {
|
||||
"typeVersion": [
|
||||
0,
|
||||
0
|
||||
],
|
||||
"preferredVisualisationType": "nodeGraph"
|
||||
},
|
||||
"fields": [
|
||||
{
|
||||
"name": "id",
|
||||
"type": "string",
|
||||
"typeInfo": {
|
||||
"frame": "string"
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "title",
|
||||
"type": "string",
|
||||
"typeInfo": {
|
||||
"frame": "string"
|
||||
}
|
||||
}
|
||||
]
|
||||
},
|
||||
"data": {
|
||||
"values": [
|
||||
[],
|
||||
[]
|
||||
]
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
|
|
@ -0,0 +1,97 @@
|
|||
// 🌟 This was machine generated. Do not edit. 🌟
|
||||
//
|
||||
// Frame[0] {
|
||||
// "typeVersion": [
|
||||
// 0,
|
||||
// 0
|
||||
// ],
|
||||
// "preferredVisualisationType": "nodeGraph"
|
||||
// }
|
||||
// Name: test_edges
|
||||
// Dimensions: 4 Fields by 3 Rows
|
||||
// +--------------------+----------------+----------------+----------------+
|
||||
// | Name: id | Name: source | Name: target | Name: mainstat |
|
||||
// | Labels: | Labels: | Labels: | Labels: |
|
||||
// | Type: []string | Type: []string | Type: []string | Type: []int64 |
|
||||
// +--------------------+----------------+----------------+----------------+
|
||||
// | serviceA--serviceB | serviceA | serviceB | 1 |
|
||||
// | serviceA--serviceC | serviceA | serviceC | 2 |
|
||||
// | serviceB--serviceC | serviceB | serviceC | 3 |
|
||||
// +--------------------+----------------+----------------+----------------+
|
||||
//
|
||||
//
|
||||
// 🌟 This was machine generated. Do not edit. 🌟
|
||||
{
|
||||
"status": 200,
|
||||
"frames": [
|
||||
{
|
||||
"schema": {
|
||||
"name": "test_edges",
|
||||
"meta": {
|
||||
"typeVersion": [
|
||||
0,
|
||||
0
|
||||
],
|
||||
"preferredVisualisationType": "nodeGraph"
|
||||
},
|
||||
"fields": [
|
||||
{
|
||||
"name": "id",
|
||||
"type": "string",
|
||||
"typeInfo": {
|
||||
"frame": "string"
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "source",
|
||||
"type": "string",
|
||||
"typeInfo": {
|
||||
"frame": "string"
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "target",
|
||||
"type": "string",
|
||||
"typeInfo": {
|
||||
"frame": "string"
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "mainstat",
|
||||
"type": "number",
|
||||
"typeInfo": {
|
||||
"frame": "int64"
|
||||
},
|
||||
"config": {
|
||||
"displayName": "Call count"
|
||||
}
|
||||
}
|
||||
]
|
||||
},
|
||||
"data": {
|
||||
"values": [
|
||||
[
|
||||
"serviceA--serviceB",
|
||||
"serviceA--serviceC",
|
||||
"serviceB--serviceC"
|
||||
],
|
||||
[
|
||||
"serviceA",
|
||||
"serviceA",
|
||||
"serviceB"
|
||||
],
|
||||
[
|
||||
"serviceB",
|
||||
"serviceC",
|
||||
"serviceC"
|
||||
],
|
||||
[
|
||||
1,
|
||||
2,
|
||||
3
|
||||
]
|
||||
]
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
|
|
@ -0,0 +1,70 @@
|
|||
// 🌟 This was machine generated. Do not edit. 🌟
|
||||
//
|
||||
// Frame[0] {
|
||||
// "typeVersion": [
|
||||
// 0,
|
||||
// 0
|
||||
// ],
|
||||
// "preferredVisualisationType": "nodeGraph"
|
||||
// }
|
||||
// Name: test_nodes
|
||||
// Dimensions: 2 Fields by 3 Rows
|
||||
// +----------------+----------------+
|
||||
// | Name: id | Name: title |
|
||||
// | Labels: | Labels: |
|
||||
// | Type: []string | Type: []string |
|
||||
// +----------------+----------------+
|
||||
// | serviceA | serviceA |
|
||||
// | serviceB | serviceB |
|
||||
// | serviceC | serviceC |
|
||||
// +----------------+----------------+
|
||||
//
|
||||
//
|
||||
// 🌟 This was machine generated. Do not edit. 🌟
|
||||
{
|
||||
"status": 200,
|
||||
"frames": [
|
||||
{
|
||||
"schema": {
|
||||
"name": "test_nodes",
|
||||
"meta": {
|
||||
"typeVersion": [
|
||||
0,
|
||||
0
|
||||
],
|
||||
"preferredVisualisationType": "nodeGraph"
|
||||
},
|
||||
"fields": [
|
||||
{
|
||||
"name": "id",
|
||||
"type": "string",
|
||||
"typeInfo": {
|
||||
"frame": "string"
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "title",
|
||||
"type": "string",
|
||||
"typeInfo": {
|
||||
"frame": "string"
|
||||
}
|
||||
}
|
||||
]
|
||||
},
|
||||
"data": {
|
||||
"values": [
|
||||
[
|
||||
"serviceA",
|
||||
"serviceB",
|
||||
"serviceC"
|
||||
],
|
||||
[
|
||||
"serviceA",
|
||||
"serviceB",
|
||||
"serviceC"
|
||||
]
|
||||
]
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
|
|
@ -72,10 +72,15 @@ export class JaegerDatasource extends DataSourceWithBackend<JaegerQuery, JaegerJ
|
|||
// No query type means that the query is a trace ID query
|
||||
// If all targets are trace ID queries, we can use the backend querying
|
||||
const allTargetsTraceIdQuery = options.targets.every((target) => !target.queryType);
|
||||
const allTargetsDependencyGraph = options.targets.every((target) => target.queryType === 'dependencyGraph');
|
||||
// We have not migrated the node graph to the backend
|
||||
// If the node graph is disabled, we can use the backend migration
|
||||
const nodeGraphDisabled = !this.nodeGraph?.enabled;
|
||||
if (config.featureToggles.jaegerBackendMigration && allTargetsTraceIdQuery && nodeGraphDisabled) {
|
||||
if (
|
||||
config.featureToggles.jaegerBackendMigration &&
|
||||
(allTargetsTraceIdQuery || allTargetsDependencyGraph) &&
|
||||
nodeGraphDisabled
|
||||
) {
|
||||
return super.query(options);
|
||||
}
|
||||
|
||||
|
|
|
|||
Loading…
Reference in New Issue