-
Notifications
You must be signed in to change notification settings - Fork 671
Add support for extended range selector modifiers smoothed & anchored #13398
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: main
Are you sure you want to change the base?
Changes from all commits
9b4bf10
4bc430c
60909cd
3f66e89
3dd1f0e
1c7b40c
9871e70
61ca856
3fa4ea4
44b9386
7c49f22
0b32e52
770065d
e8f0546
0bf73de
9b48285
6ffb7ed
8599e17
2eb8e88
ae3b216
aff095a
c3cfc54
69c0579
4f39452
6889720
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
tcp13equals2 marked this conversation as resolved.
Show resolved
Hide resolved
|
| Original file line number | Diff line number | Diff line change | ||||
|---|---|---|---|---|---|---|
|
|
@@ -16,6 +16,9 @@ import ( | |||||
|
|
||||||
| const ( | ||||||
| allExperimentalFunctions = "all" | ||||||
| isFunction = 1 | ||||||
| isAggregator = 2 | ||||||
| isExtendedRangeSelector = 3 | ||||||
| ) | ||||||
|
|
||||||
| type experimentalFunctionsMiddleware struct { | ||||||
|
|
@@ -26,6 +29,7 @@ type experimentalFunctionsMiddleware struct { | |||||
|
|
||||||
| // newExperimentalFunctionsMiddleware creates a middleware that blocks queries that contain PromQL experimental functions | ||||||
| // that are not enabled for the active tenant(s), allowing us to enable specific functions only for selected tenants. | ||||||
| // This middleware also manages blocking queries that contain PromQL experimental aggregates and extended range selector modifiers. | ||||||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Should we rename this type and file to match this new behaviour? |
||||||
| func newExperimentalFunctionsMiddleware(limits Limits, logger log.Logger) MetricsQueryMiddleware { | ||||||
| return MetricsQueryMiddlewareFunc(func(next MetricsQueryHandler) MetricsQueryHandler { | ||||||
| return &experimentalFunctionsMiddleware{ | ||||||
|
|
@@ -52,7 +56,17 @@ func (m *experimentalFunctionsMiddleware) Do(ctx context.Context, req MetricsQue | |||||
| } | ||||||
| } | ||||||
|
|
||||||
| if allExperimentalFunctionsEnabled { | ||||||
| enabledExtendedRangeSelectors := make(map[string][]string, len(tenantIDs)) | ||||||
| allExtendedRangeSelectorsEnabled := true | ||||||
| for _, tenantID := range tenantIDs { | ||||||
| enabled := m.limits.EnabledPromQLExtendedRangeSelectors(tenantID) | ||||||
| enabledExtendedRangeSelectors[tenantID] = enabled | ||||||
| if len(enabled) == 0 || enabled[0] != allExperimentalFunctions { | ||||||
| allExtendedRangeSelectorsEnabled = false | ||||||
| } | ||||||
| } | ||||||
|
|
||||||
| if allExperimentalFunctionsEnabled && allExtendedRangeSelectorsEnabled { | ||||||
| // If all experimental functions are enabled for all tenants here, we don't need to check the query | ||||||
| // for those functions and can skip this middleware. | ||||||
| return m.next.Do(ctx, req) | ||||||
|
|
@@ -68,17 +82,24 @@ func (m *experimentalFunctionsMiddleware) Do(ctx context.Context, req MetricsQue | |||||
| return m.next.Do(ctx, req) | ||||||
| } | ||||||
|
|
||||||
| // Make sure that every used experimental function is enabled for all the tenants here. | ||||||
| for name := range funcs { | ||||||
| for tenantID, enabled := range enabledExperimentalFunctions { | ||||||
| // Make sure that every used experimental function/modifier is enabled for all the tenants here. | ||||||
| var tenantMap map[string][]string | ||||||
| for name, t := range funcs { | ||||||
| switch t { | ||||||
| case isFunction, isAggregator: | ||||||
| tenantMap = enabledExperimentalFunctions | ||||||
| case isExtendedRangeSelector: | ||||||
| tenantMap = enabledExtendedRangeSelectors | ||||||
| } | ||||||
|
|
||||||
| for tenantID, enabled := range tenantMap { | ||||||
| if len(enabled) > 0 && enabled[0] == allExperimentalFunctions { | ||||||
| // If the first item matches the const value of allExperimentalFunctions, then all experimental | ||||||
| // functions are enabled for this tenant. | ||||||
| continue | ||||||
| } | ||||||
| if !slices.Contains(enabled, name) { | ||||||
| err := fmt.Errorf("function %q is not enabled for tenant %s", name, tenantID) | ||||||
| return nil, apierror.New(apierror.TypeBadData, DecorateWithParamName(err, "query").Error()) | ||||||
| return nil, raiseError(tenantID, name, t) | ||||||
| } | ||||||
| } | ||||||
| } | ||||||
|
|
@@ -87,18 +108,45 @@ func (m *experimentalFunctionsMiddleware) Do(ctx context.Context, req MetricsQue | |||||
| return m.next.Do(ctx, req) | ||||||
| } | ||||||
|
|
||||||
| func raiseError(tenantID string, name string, t int) error { | ||||||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. [nit] Perhaps |
||||||
| switch t { | ||||||
| case isFunction: | ||||||
| err := fmt.Errorf("function %q is not enabled for tenant %s", name, tenantID) | ||||||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I wouldn't bother including the tenant ID here - this won't mean much to users and we can see it in the query-frontend logs if the query fails because of this. |
||||||
| return apierror.New(apierror.TypeBadData, DecorateWithParamName(err, "query").Error()) | ||||||
| case isAggregator: | ||||||
| err := fmt.Errorf("aggregate %q is not enabled for tenant %s", name, tenantID) | ||||||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
Suggested change
|
||||||
| return apierror.New(apierror.TypeBadData, DecorateWithParamName(err, "query").Error()) | ||||||
| case isExtendedRangeSelector: | ||||||
| err := fmt.Errorf("extended range selector modifier %q is not enabled for tenant %s", name, tenantID) | ||||||
| return apierror.New(apierror.TypeBadData, DecorateWithParamName(err, "query").Error()) | ||||||
| } | ||||||
| return nil | ||||||
| } | ||||||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Bug: Silent Error Bypass Allows Invalid QueriesThe |
||||||
|
|
||||||
| // containedExperimentalFunctions returns any PromQL experimental functions used in the query. | ||||||
| func containedExperimentalFunctions(expr parser.Expr) map[string]struct{} { | ||||||
| expFuncNames := map[string]struct{}{} | ||||||
| func containedExperimentalFunctions(expr parser.Expr) map[string]int { | ||||||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Rather than returning a |
||||||
| expFuncNames := map[string]int{} | ||||||
| _ = inspect(expr, func(node parser.Node) error { | ||||||
| switch n := node.(type) { | ||||||
| case *parser.Call: | ||||||
| if parser.Functions[n.Func.Name].Experimental { | ||||||
| expFuncNames[n.Func.Name] = struct{}{} | ||||||
| expFuncNames[n.Func.Name] = isFunction | ||||||
| } | ||||||
| case *parser.AggregateExpr: | ||||||
| if n.Op.IsExperimentalAggregator() { | ||||||
| expFuncNames[n.Op.String()] = struct{}{} | ||||||
| expFuncNames[n.Op.String()] = isAggregator | ||||||
| } | ||||||
| case *parser.MatrixSelector: | ||||||
| // technically anchored & smoothed are range selectors not functions | ||||||
| vs, ok := n.VectorSelector.(*parser.VectorSelector) | ||||||
| if ok && vs.Anchored { | ||||||
| expFuncNames["anchored"] = isExtendedRangeSelector | ||||||
| } else if ok && vs.Smoothed { | ||||||
| expFuncNames["smoothed"] = isExtendedRangeSelector | ||||||
| } | ||||||
| case *parser.VectorSelector: | ||||||
| if n.Smoothed { | ||||||
| expFuncNames["smoothed"] = isExtendedRangeSelector | ||||||
| } | ||||||
| } | ||||||
| return nil | ||||||
|
|
||||||
Uh oh!
There was an error while loading. Please reload this page.