diff --git a/README.md b/README.md index b00c3da..be37fab 100644 --- a/README.md +++ b/README.md @@ -6,11 +6,11 @@ This connector is built using the [Go Data Connector SDK](https://github.com/has ## Features -### Metrics Collection +### Metrics #### How it works -The connector can introspect and automatically transform available metrics on the Prometheus server to collection queries. Each collection has a common structure: +The connector can introspect and automatically transform available metrics on the Prometheus server to collection queries. Each metric has a common structure: ```gql { @@ -108,7 +108,7 @@ The equivalent GraphQL query will be: #### How it works -When simple queries don't meet your need you can define native queries in [the configuration file](./tests/configuration/configuration.yaml) with prepared variables with the `${}` template. +When simple queries don't meet your need you can define native queries in [the configuration file](./tests/configuration/configuration.yaml) with prepared variables with the `${}` template. Native queries are defined as collections. ```yaml metadata: @@ -131,16 +131,17 @@ The native query is exposed as a read-only function with 2 required fields `job` ```gql { service_up( - start: "2024-09-24T00:00:00Z" - job: "node" - instance: "localhost:9090" + args: { step: "1m", job: "node", instance: "node-exporter:9100" } + where: { + timestamp: { _gt: "2024-10-11T00:00:00Z" } + job: { _in: ["node"] } + } ) { - timestamp - value - labels + job + instance values { - value timestamp + value } } } @@ -149,14 +150,8 @@ The native query is exposed as a read-only function with 2 required fields `job` > [!NOTE] > Labels aren't automatically added. You need to define them manually. -#### Common arguments - -- `start` & `end`: time range arguments for the range query. -- `time`: Evaluation timestamp. Use this argument if you want to run an instant query. -- `step`: the query resolution step width in duration format or float number of seconds. The step should be explicitly set for range queries. Even though the connector can estimate the approximate step width the result may be empty due to too far interval. -- `timeout`: the evaluation timeout of the request. -- `flat`: flatten grouped values out of the root array. Use the runtime setting if the value is null -- `where`: boolean expression to post-filter results by labels. This argument is designed for permissions. +> [!NOTE] +> Label and value boolean expressions in `where` are used to filter results after the query was executed. ### Prometheus APIs diff --git a/connector/internal/collection.go b/connector/internal/collection.go index 4a0087e..72e0f22 100644 --- a/connector/internal/collection.go +++ b/connector/internal/collection.go @@ -354,11 +354,7 @@ func (qce *QueryCollectionExecutor) evalValueComparisonCondition(operator *schem if operator == nil { return "", nil } - value, err := qce.getComparisonValue(operator.Value) - if err != nil { - return "", fmt.Errorf("invalid value expression: %s", err) - } - v, err := utils.DecodeNullableFloat[float64](value) + v, err := getComparisonValueFloat64(operator.Value, qce.Variables) if err != nil { return "", fmt.Errorf("invalid value expression: %s", err) } diff --git a/connector/internal/native_query.go b/connector/internal/native_query.go index 39cc5bd..3622861 100644 --- a/connector/internal/native_query.go +++ b/connector/internal/native_query.go @@ -159,7 +159,7 @@ func (nqe *NativeQueryExecutor) queryRange(ctx context.Context, queryString stri span := trace.SpanFromContext(ctx) span.AddEvent("post_filter_results", trace.WithAttributes(utils.JSONAttribute("expression", params.Expression))) - matrix, err = nqe.filterMatrixResults(matrix, params.Expression) + matrix, err = nqe.filterMatrixResults(matrix, params) if err != nil { return nil, schema.UnprocessableContentError(err.Error(), nil) } @@ -176,7 +176,7 @@ func (nqe *NativeQueryExecutor) filterVectorResults(vector model.Vector, expr sc } results := model.Vector{} for _, item := range vector { - valid, err := nqe.validateLabelBoolExp(item.Metric, expr) + valid, err := nqe.validateBoolExp(item.Metric, item.Value, expr) if err != nil { return nil, err } @@ -187,34 +187,82 @@ func (nqe *NativeQueryExecutor) filterVectorResults(vector model.Vector, expr sc return results, nil } -func (nqe *NativeQueryExecutor) filterMatrixResults(matrix model.Matrix, expr schema.Expression) (model.Matrix, error) { - if expr == nil || len(matrix) == 0 { +func (nqe *NativeQueryExecutor) filterMatrixResults(matrix model.Matrix, params *NativeQueryRequest) (model.Matrix, error) { + if params.Expression == nil || len(matrix) == 0 { return matrix, nil } results := model.Matrix{} for _, item := range matrix { - valid, err := nqe.validateLabelBoolExp(item.Metric, expr) - if err != nil { - return nil, err + if !params.HasValueBoolExp { + valid, err := nqe.validateBoolExp(item.Metric, 0, params.Expression) + if err != nil { + return nil, err + } + if valid { + results = append(results, item) + } + continue } - if valid { - results = append(results, item) + + newItem := model.SampleStream{ + Metric: item.Metric, + Histograms: item.Histograms, + } + + for _, v := range item.Values { + valid, err := nqe.validateBoolExp(item.Metric, v.Value, params.Expression) + if err != nil { + return nil, err + } + if valid { + newItem.Values = append(newItem.Values, v) + } + } + + if len(newItem.Values) > 0 { + results = append(results, &newItem) } } return results, nil } -func (nqe *NativeQueryExecutor) validateLabelBoolExp(labels model.Metric, expr schema.Expression) (bool, error) { +func (nqe *NativeQueryExecutor) validateBoolExp(labels model.Metric, value model.SampleValue, expr schema.Expression) (bool, error) { switch exprs := expr.Interface().(type) { case *schema.ExpressionAnd: for _, e := range exprs.Expressions { - valid, err := nqe.validateLabelBoolExp(labels, e) + valid, err := nqe.validateBoolExp(labels, value, e) if !valid || err != nil { return false, err } } return true, nil case *schema.ExpressionBinaryComparisonOperator: + if exprs.Column.Name == metadata.ValueKey { + floatValue, err := getComparisonValueFloat64(exprs.Value, nqe.Variables) + if err != nil { + return false, err + } + if floatValue == nil { + return true, nil + } + switch exprs.Operator { + case metadata.Equal: + return value.Equal(model.SampleValue(*floatValue)), nil + case metadata.NotEqual: + return !value.Equal(model.SampleValue(*floatValue)), nil + case metadata.Least: + return float64(value) < *floatValue, nil + case metadata.LeastOrEqual: + return float64(value) <= *floatValue, nil + case metadata.Greater: + return float64(value) > *floatValue, nil + case metadata.GreaterOrEqual: + return float64(value) >= *floatValue, nil + default: + return false, fmt.Errorf("unsupported value operator %s", exprs.Operator) + } + } + labelValue := labels[model.LabelName(exprs.Column.Name)] switch exprs.Operator { case metadata.Equal, metadata.NotEqual, metadata.Regex, metadata.NotRegex: @@ -255,7 +303,7 @@ func (nqe *NativeQueryExecutor) validateLabelBoolExp(labels model.Metric, expr s } } case *schema.ExpressionNot: - valid, err := nqe.validateLabelBoolExp(labels, exprs.Expression) + valid, err := nqe.validateBoolExp(labels, value, exprs.Expression) if err != nil { return false, err } @@ -265,7 +313,7 @@ func (nqe *NativeQueryExecutor) validateLabelBoolExp(labels model.Metric, expr s return true, nil } for _, e := range exprs.Expressions { - valid, err := nqe.validateLabelBoolExp(labels, e) + valid, err := nqe.validateBoolExp(labels, value, e) if err != nil { return false, err } diff --git a/connector/internal/native_query_request.go b/connector/internal/native_query_request.go index 4555ada..cccf0e9 100644 --- a/connector/internal/native_query_request.go +++ b/connector/internal/native_query_request.go @@ -10,14 +10,15 @@ import ( // NativeQueryRequest the structured native request which is evaluated from the raw expression type NativeQueryRequest struct { - Timestamp any - Start any - End any - Timeout any - Step any - OrderBy []ColumnOrder - Variables map[string]any - Expression schema.Expression + Timestamp any + Start any + End any + Timeout any + Step any + OrderBy []ColumnOrder + Variables map[string]any + Expression schema.Expression + HasValueBoolExp bool } // EvalNativeQueryRequest evaluates the requested collection data of the query request @@ -61,6 +62,20 @@ func (pr *NativeQueryRequest) evalQueryPredicate(expression schema.Expression) ( } } return schema.NewExpressionAnd(exprs...), nil + case *schema.ExpressionOr: + exprs := []schema.ExpressionEncoder{} + for _, nestedExpr := range expr.Expressions { + evalExpr, err := pr.evalQueryPredicate(nestedExpr) + if err != nil { + return nil, err + } + if evalExpr != nil { + exprs = append(exprs, evalExpr) + } + } + return schema.NewExpressionOr(exprs...), nil + case *schema.ExpressionNot, *schema.ExpressionUnaryComparisonOperator: + return expr, nil case *schema.ExpressionBinaryComparisonOperator: if expr.Column.Type != schema.ComparisonTargetTypeColumn { return nil, fmt.Errorf("%s: unsupported comparison target `%s`", expr.Column.Name, expr.Column.Type) @@ -102,6 +117,9 @@ func (pr *NativeQueryRequest) evalQueryPredicate(expression schema.Expression) ( default: return nil, fmt.Errorf("unsupported operator `%s` for the timestamp", expr.Operator) } + case metadata.ValueKey: + pr.HasValueBoolExp = true + return expr, nil default: return expr, nil } diff --git a/connector/internal/native_query_test.go b/connector/internal/native_query_test.go new file mode 100644 index 0000000..7de9be5 --- /dev/null +++ b/connector/internal/native_query_test.go @@ -0,0 +1,196 @@ +package internal + +import ( + "fmt" + "math/rand/v2" + "testing" + "time" + + "github.com/hasura/ndc-sdk-go/schema" + "github.com/prometheus/common/model" + "gotest.tools/v3/assert" +) + +func TestFilterVectorResults(t *testing.T) { + now := model.Now() + vectorFixtures := model.Vector{} + for i := 0; i < 100; i++ { + vectorFixtures = append(vectorFixtures, &model.Sample{ + Metric: model.Metric{ + "job": "ndc-prometheus", + "instance": "ndc-prometheus:8080", + }, + Timestamp: now.Add(time.Duration(i) * time.Minute), + Value: model.SampleValue(float64(rand.IntN(100)) / 100), + }) + } + + nqe := &NativeQueryExecutor{} + results, err := nqe.filterVectorResults(vectorFixtures, nil) + assert.NilError(t, err) + assert.DeepEqual(t, results, vectorFixtures) + + testCases := []struct { + Name string + Expression schema.Expression + Expected model.Vector + }{ + { + Name: "equal", + Expression: schema.NewExpressionAnd( + schema.NewExpressionBinaryComparisonOperator( + *schema.NewComparisonTargetColumn("job", []string{}, []schema.PathElement{}), + "_eq", + schema.NewComparisonValueScalar("foo"), + ), + ).Encode(), + Expected: model.Vector{}, + }, + { + Name: "is_null", + Expression: schema.NewExpressionAnd( + schema.NewExpressionUnaryComparisonOperator( + *schema.NewComparisonTargetColumn("job", []string{}, []schema.PathElement{}), + schema.UnaryComparisonOperatorIsNull, + ), + ).Encode(), + Expected: model.Vector{}, + }, + { + Name: "neq", + Expression: schema.NewExpressionAnd( + schema.NewExpressionBinaryComparisonOperator( + *schema.NewComparisonTargetColumn("job", []string{}, []schema.PathElement{}), + "_neq", + schema.NewComparisonValueScalar("foo"), + ), + ).Encode(), + Expected: vectorFixtures, + }, + { + Name: "in", + Expression: schema.NewExpressionAnd( + schema.NewExpressionBinaryComparisonOperator( + *schema.NewComparisonTargetColumn("job", []string{}, []schema.PathElement{}), + "_in", + schema.NewComparisonValueScalar([]string{"foo"}), + ), + ).Encode(), + Expected: model.Vector{}, + }, + { + Name: "nin", + Expression: schema.NewExpressionAnd( + schema.NewExpressionBinaryComparisonOperator( + *schema.NewComparisonTargetColumn("job", []string{}, []schema.PathElement{}), + "_nin", + schema.NewComparisonValueScalar([]string{"foo"}), + ), + ).Encode(), + Expected: vectorFixtures, + }, + } + + for _, tc := range testCases { + t.Run(tc.Name, func(t *testing.T) { + results, err = nqe.filterVectorResults(vectorFixtures, tc.Expression) + assert.NilError(t, err) + assert.DeepEqual(t, results, tc.Expected) + }) + } +} + +func TestFilterMatrixResults(t *testing.T) { + now := model.Now() + matrixFixtures := model.Matrix{} + for i := 0; i < 10; i++ { + matrixFixtures = append(matrixFixtures, &model.SampleStream{ + Metric: model.Metric{ + "job": "ndc-prometheus", + "instance": model.LabelValue(fmt.Sprintf("ndc-prometheus:%d", i)), + }, + Values: []model.SamplePair{ + { + Timestamp: now.Add(time.Duration(i) * time.Minute), + Value: model.SampleValue(float64(rand.IntN(100)) / 100), + }, + { + Timestamp: now.Add(time.Duration(i+1) * time.Minute), + Value: model.SampleValue(float64(rand.IntN(100)) / 100), + }, + }, + }) + } + + nqe := &NativeQueryExecutor{} + results, err := nqe.filterMatrixResults(matrixFixtures, &NativeQueryRequest{}) + assert.NilError(t, err) + assert.DeepEqual(t, results, matrixFixtures) + + testCases := []struct { + Name string + Request *NativeQueryRequest + Expected model.Matrix + }{ + { + Name: "or", + Request: &NativeQueryRequest{ + Expression: schema.NewExpressionOr( + schema.NewExpressionBinaryComparisonOperator( + *schema.NewComparisonTargetColumn("job", []string{}, []schema.PathElement{}), + "_eq", + schema.NewComparisonValueScalar("foo"), + ), + ).Encode(), + }, + Expected: model.Matrix{}, + }, + { + Name: "value", + Request: &NativeQueryRequest{ + HasValueBoolExp: true, + Expression: schema.NewExpressionOr( + schema.NewExpressionBinaryComparisonOperator( + *schema.NewComparisonTargetColumn("job", []string{}, []schema.PathElement{}), + "_regex", + schema.NewComparisonValueScalar("foo"), + ), + schema.NewExpressionBinaryComparisonOperator( + *schema.NewComparisonTargetColumn("value", []string{}, []schema.PathElement{}), + "_gte", + schema.NewComparisonValueScalar(0), + ), + ).Encode(), + }, + Expected: matrixFixtures, + }, + { + Name: "not", + Request: &NativeQueryRequest{ + HasValueBoolExp: true, + Expression: schema.NewExpressionNot(schema.NewExpressionOr( + schema.NewExpressionBinaryComparisonOperator( + *schema.NewComparisonTargetColumn("job", []string{}, []schema.PathElement{}), + "_nregex", + schema.NewComparisonValueScalar("foo"), + ), + schema.NewExpressionBinaryComparisonOperator( + *schema.NewComparisonTargetColumn("value", []string{}, []schema.PathElement{}), + "_lte", + schema.NewComparisonValueScalar(0), + ), + )).Encode(), + }, + Expected: model.Matrix{}, + }, + } + + for _, tc := range testCases { + t.Run(tc.Name, func(t *testing.T) { + results, err = nqe.filterMatrixResults(matrixFixtures, tc.Request) + assert.NilError(t, err) + assert.DeepEqual(t, results, tc.Expected) + }) + } + +} diff --git a/connector/internal/pagination.go b/connector/internal/pagination.go index 52d73d0..db42b32 100644 --- a/connector/internal/pagination.go +++ b/connector/internal/pagination.go @@ -32,7 +32,11 @@ func sortVector(vector model.Vector, sortElements []ColumnOrder) { if math.IsNaN(float64(b.Value)) { return -1 * iOrder } - return int(float64(a.Value)-float64(b.Value)) * iOrder + if a.Value > b.Value { + return 1 * iOrder + } else { + return -1 * iOrder + } case metadata.TimestampKey: difference := a.Timestamp.Sub(b.Timestamp) if difference == 0 { @@ -65,58 +69,15 @@ func sortMatrix(matrix model.Matrix, sortElements []ColumnOrder) { } slices.SortFunc(matrix, func(a *model.SampleStream, b *model.SampleStream) int { - lenA := len(a.Values) - lenB := len(b.Values) - for _, elem := range sortElements { iOrder := 1 if elem.Descending { iOrder = -1 } switch elem.Name { - case metadata.ValueKey: - if lenA == 0 && lenB == 0 { - continue - } - if lenA == 0 { - return -1 * iOrder - } - if lenB == 0 { - return 1 * iOrder - } - - valueA := a.Values[lenA-1].Value - valueB := b.Values[lenB-1].Value - - if valueA.Equal(valueB) { - continue - } - if math.IsNaN(float64(valueA)) { - return 1 * iOrder - } - if math.IsNaN(float64(valueB)) { - return -1 * iOrder - } - return int(float64(valueA)-float64(valueB)) * iOrder - case metadata.TimestampKey: - if lenA == 0 && lenB == 0 { - continue - } - if lenA == 0 { - return -1 * iOrder - } - if lenB == 0 { - return 1 * iOrder - } - - tsA := a.Values[lenA-1].Timestamp - tsB := b.Values[lenB-1].Timestamp - - difference := tsA.Sub(tsB) - if difference == 0 { - continue - } - return int(difference) * iOrder + case metadata.ValueKey, metadata.TimestampKey: + sortSamplePair(a.Values, elem.Name, iOrder) + sortSamplePair(b.Values, elem.Name, iOrder) default: if len(a.Metric) == 0 { continue @@ -137,6 +98,32 @@ func sortMatrix(matrix model.Matrix, sortElements []ColumnOrder) { }) } +func sortSamplePair(values []model.SamplePair, key string, iOrder int) { + slices.SortFunc(values, func(a model.SamplePair, b model.SamplePair) int { + switch key { + case metadata.ValueKey: + if a.Value.Equal(b.Value) { + return 0 + } + if math.IsNaN(float64(a.Value)) { + return 1 * iOrder + } + if math.IsNaN(float64(b.Value)) { + return -1 * iOrder + } + if a.Value > b.Value { + return 1 * iOrder + } else { + return -1 * iOrder + } + case metadata.TimestampKey: + return int(a.Timestamp.Sub(b.Timestamp)) * iOrder + default: + return 0 + } + }) +} + func paginateVector(vector model.Vector, q schema.Query) model.Vector { if q.Offset != nil && *q.Offset > 0 { if len(vector) <= *q.Offset { diff --git a/connector/internal/pagination_test.go b/connector/internal/pagination_test.go new file mode 100644 index 0000000..4ace2e1 --- /dev/null +++ b/connector/internal/pagination_test.go @@ -0,0 +1,141 @@ +package internal + +import ( + "fmt" + "math/rand/v2" + "strings" + "testing" + "time" + + "github.com/hasura/ndc-prometheus/connector/metadata" + "github.com/hasura/ndc-sdk-go/schema" + "github.com/hasura/ndc-sdk-go/utils" + "github.com/prometheus/common/model" + "gotest.tools/v3/assert" +) + +func TestSortAndPaginateVector(t *testing.T) { + now := model.Now() + vectorFixtures := model.Vector{} + for i := 0; i < 100; i++ { + vectorFixtures = append(vectorFixtures, &model.Sample{ + Metric: model.Metric{ + "job": "ndc-prometheus", + "instance": "ndc-prometheus:8080", + }, + Timestamp: now.Add(time.Duration(i) * time.Minute), + Value: model.SampleValue(float64(rand.IntN(100)) / 100), + }) + } + + results := append(model.Vector{}, vectorFixtures...) + sortVector(results, []ColumnOrder{{ + Name: metadata.ValueKey, + Descending: false, + }}) + + for i := 1; i < len(results); i++ { + assert.Assert(t, results[i-1].Value <= results[i].Value) + } + + sortVector(results, []ColumnOrder{{ + Name: metadata.ValueKey, + Descending: true, + }}) + + for i := 1; i < len(results); i++ { + assert.Assert(t, results[i-1].Value >= results[i].Value) + } + + sortVector(results, []ColumnOrder{{ + Name: metadata.TimestampKey, + Descending: false, + }}) + + for i := 1; i < len(results); i++ { + assert.Assert(t, results[i-1].Timestamp < results[i].Timestamp) + } + + sortVector(results, []ColumnOrder{{ + Name: "job", + Descending: false, + }}) + + for i := 1; i < len(results); i++ { + assert.Assert(t, results[i-1].Metric["job"] == results[i].Metric["job"]) + } + + assert.DeepEqual(t, paginateVector(results, schema.Query{ + Offset: utils.ToPtr(50), + Limit: utils.ToPtr(1), + })[0], results[50]) +} + +func TestSortAndPaginateMatrix(t *testing.T) { + now := model.Now() + matrixFixtures := model.Matrix{} + for i := 0; i < 10; i++ { + matrixFixtures = append(matrixFixtures, &model.SampleStream{ + Metric: model.Metric{ + "job": "ndc-prometheus", + "instance": model.LabelValue(fmt.Sprintf("ndc-prometheus:%d", i)), + }, + Values: []model.SamplePair{ + { + Timestamp: now.Add(time.Duration(i) * time.Minute), + Value: model.SampleValue(float64(rand.IntN(100)) / 100), + }, + { + Timestamp: now.Add(time.Duration(i+1) * time.Minute), + Value: model.SampleValue(float64(rand.IntN(100)) / 100), + }, + }, + }) + } + + results := append(model.Matrix{}, matrixFixtures...) + sortMatrix(results, []ColumnOrder{{ + Name: metadata.ValueKey, + Descending: false, + }}) + + for i := 1; i < len(results); i++ { + assert.Assert(t, results[i].Values[0].Value <= results[i].Values[1].Value) + } + + sortMatrix(results, []ColumnOrder{{ + Name: metadata.ValueKey, + Descending: true, + }}) + + for i := 1; i < len(results); i++ { + assert.Assert(t, results[i].Values[0].Value >= results[i].Values[1].Value) + } + + sortMatrix(results, []ColumnOrder{{ + Name: metadata.TimestampKey, + Descending: false, + }}) + + for i := 1; i < len(results); i++ { + assert.Assert(t, results[i].Values[0].Timestamp < results[i].Values[1].Timestamp) + } + + sortMatrix(results, []ColumnOrder{{ + Name: "instance", + Descending: false, + }}) + + for i := 1; i < len(results); i++ { + assert.Assert(t, strings.Compare(string(results[i-1].Metric["instance"]), string(results[i].Metric["instance"])) == -1) + } + + mapResults := createGroupQueryResultsFromMatrix(results, map[string]metadata.LabelInfo{ + "job": {}, + "instance": {}, + }, &metadata.RuntimeSettings{}) + assert.DeepEqual(t, paginateQueryResults(mapResults, schema.Query{ + Offset: utils.ToPtr(5), + Limit: utils.ToPtr(1), + })[0], mapResults[5]) +} diff --git a/connector/internal/utils.go b/connector/internal/utils.go index dbb4106..fe7ec59 100644 --- a/connector/internal/utils.go +++ b/connector/internal/utils.go @@ -178,6 +178,14 @@ func getComparisonValue(input schema.ComparisonValue, variables map[string]any) } } +func getComparisonValueFloat64(input schema.ComparisonValue, variables map[string]any) (*float64, error) { + rawValue, err := getComparisonValue(input, variables) + if err != nil { + return nil, err + } + return utils.DecodeNullableFloat[float64](rawValue) +} + func getComparisonValueString(input schema.ComparisonValue, variables map[string]any) (*string, error) { rawValue, err := getComparisonValue(input, variables) if err != nil { diff --git a/connector/testdata/query/process_cpu_seconds_total/request.json b/connector/testdata/query/process_cpu_seconds_total/request.json index 12d1d0e..5d9bbf1 100644 --- a/connector/testdata/query/process_cpu_seconds_total/request.json +++ b/connector/testdata/query/process_cpu_seconds_total/request.json @@ -25,7 +25,9 @@ "type": "literal", "value": [{ "increase": "1m" }, { "sum": ["job"] }, { "avg": ["job"] }] }, - "step": { "type": "literal", "value": "5m" } + "step": { "type": "literal", "value": "5m" }, + "timeout": { "type": "literal", "value": "1m" }, + "flat": { "type": "literal", "value": true } }, "collection_relationships": {} } diff --git a/connector/testdata/query/process_cpu_seconds_total_variables/request.json b/connector/testdata/query/process_cpu_seconds_total_variables/request.json index aabc07c..d358909 100644 --- a/connector/testdata/query/process_cpu_seconds_total_variables/request.json +++ b/connector/testdata/query/process_cpu_seconds_total_variables/request.json @@ -38,7 +38,9 @@ "type": "literal", "value": [{ "increase": "1m" }, { "sum": ["job"] }, { "avg": ["job"] }] }, - "step": { "type": "literal", "value": "5m" } + "step": { "type": "literal", "value": "5m" }, + "timeout": { "type": "literal", "value": "1m" }, + "flat": { "type": "literal", "value": false } }, "collection_relationships": {}, "variables": [{ "$job": "ndc-prometheus" }, { "$job": "node" }] diff --git a/connector/testdata/query/service_up/request.json b/connector/testdata/query/service_up/request.json index cba7c5b..88ae0cf 100644 --- a/connector/testdata/query/service_up/request.json +++ b/connector/testdata/query/service_up/request.json @@ -46,7 +46,9 @@ "arguments": { "instance": { "type": "literal", "value": "node-exporter:9100" }, "job": { "type": "literal", "value": "node" }, - "step": { "type": "literal", "value": "1m" } + "step": { "type": "literal", "value": "1m" }, + "timeout": { "type": "literal", "value": "1m" }, + "flat": { "type": "literal", "value": true } }, "collection_relationships": {} } diff --git a/connector/testdata/query/service_up_range/request.json b/connector/testdata/query/service_up_range/request.json index 4d20d2c..f77566d 100644 --- a/connector/testdata/query/service_up_range/request.json +++ b/connector/testdata/query/service_up_range/request.json @@ -46,7 +46,9 @@ "arguments": { "instance": { "type": "literal", "value": "node-exporter:9100" }, "job": { "type": "literal", "value": "node" }, - "step": { "type": "literal", "value": "1d" } + "step": { "type": "literal", "value": "1d" }, + "timeout": { "type": "literal", "value": "1m" }, + "flat": { "type": "literal", "value": false } }, "collection_relationships": {} }