From b998aa6cc355125bcc52c5cd944e3cebdf8f3429 Mon Sep 17 00:00:00 2001 From: Lukasz Mierzwa Date: Thu, 13 Feb 2025 10:34:14 +0000 Subject: [PATCH] Add promql/impossible for dead code detection --- .golangci.yml | 1 - cmd/pint/tests/0018_match_alerting.txt | 4 +- cmd/pint/tests/0019_match_recording.txt | 4 +- cmd/pint/tests/0020_ignore_kind.txt | 4 +- cmd/pint/tests/0023_enabled_checks.txt | 8 +- cmd/pint/tests/0025_config.txt | 1 + cmd/pint/tests/0037_disable_checks.txt | 6 +- cmd/pint/tests/0039_prom_selected_path.txt | 6 +- cmd/pint/tests/0040_rule_match_label.txt | 8 +- cmd/pint/tests/0042_watch_metrics.txt | 2 + cmd/pint/tests/0052_match_multiple.txt | 4 +- cmd/pint/tests/0053_ignore_multiple.txt | 4 +- .../tests/0054_watch_metrics_prometheus.txt | 2 + .../0057_watch_metrics_prometheus_ignore.txt | 2 + cmd/pint/tests/0092_dir_symlink.txt | 2 +- cmd/pint/tests/0095_rulefmt_symlink.txt | 4 +- cmd/pint/tests/0099_symlink_outside_glob.txt | 2 +- cmd/pint/tests/0103_file_disable.txt | 2 +- cmd/pint/tests/0111_snooze.txt | 2 +- cmd/pint/tests/0112_expired_snooze.txt | 2 +- cmd/pint/tests/0113_config_env_expand.txt | 1 + cmd/pint/tests/0115_file_disable_tag.txt | 2 +- cmd/pint/tests/0116_file_snooze.txt | 4 +- cmd/pint/tests/0144_discovery_filepath.txt | 2 +- cmd/pint/tests/0148_discovery_prom_zero.txt | 2 +- cmd/pint/tests/0149_discovery_prom.txt | 2 +- .../tests/0152_discovery_prom_dup_uptime.txt | 2 +- cmd/pint/tests/0178_parser_include.txt | 2 +- cmd/pint/tests/0179_parser_exclude.txt | 2 +- cmd/pint/tests/0180_parser_exclude_md.txt | 2 +- cmd/pint/tests/0207_locked_rule.txt | 2 +- internal/checks/base.go | 1 + internal/checks/promql_impossible.go | 77 + internal/checks/promql_impossible_test.go | 93 + internal/checks/promql_series.go | 23 +- .../config/__snapshots__/config_test.snap | 1862 +++++++++-------- internal/config/config_test.go | 62 +- internal/config/parsed_rule.go | 1 + internal/parser/utils/source.go | 250 ++- internal/parser/utils/source_test.go | 1530 +++++++++----- 40 files changed, 2496 insertions(+), 1496 deletions(-) create mode 100644 internal/checks/promql_impossible.go create mode 100644 internal/checks/promql_impossible_test.go diff --git a/.golangci.yml b/.golangci.yml index 858ff82f..62ea60c1 100644 --- a/.golangci.yml +++ b/.golangci.yml @@ -9,7 +9,6 @@ linters: - revive - misspell - promlinter - - tenv - errorlint - errname - predeclared diff --git a/cmd/pint/tests/0018_match_alerting.txt b/cmd/pint/tests/0018_match_alerting.txt index 4d61a8f7..04861f3e 100644 --- a/cmd/pint/tests/0018_match_alerting.txt +++ b/cmd/pint/tests/0018_match_alerting.txt @@ -11,9 +11,9 @@ level=DEBUG msg="Glob finder completed" count=2 level=INFO msg="Checking Prometheus rules" entries=2 workers=10 online=true level=DEBUG msg="Generated all Prometheus servers" count=0 level=DEBUG msg="Found recording rule" path=rules/0001.yml record=colo:recording lines=1-2 state=noop -level=DEBUG msg="Configured checks for rule" enabled=["promql/syntax","alerts/for","alerts/comparison","alerts/template","promql/fragile","promql/regexp"] path=rules/0001.yml rule=colo:recording +level=DEBUG msg="Configured checks for rule" enabled=["promql/syntax","alerts/for","alerts/comparison","alerts/template","promql/fragile","promql/regexp","promql/impossible"] path=rules/0001.yml rule=colo:recording level=DEBUG msg="Found alerting rule" path=rules/0001.yml alert=colo:alerting lines=4-5 state=noop -level=DEBUG msg="Configured checks for rule" enabled=["promql/syntax","alerts/for","alerts/comparison","alerts/template","promql/fragile","promql/regexp","promql/aggregate(job:true)"] path=rules/0001.yml rule=colo:alerting +level=DEBUG msg="Configured checks for rule" enabled=["promql/syntax","alerts/for","alerts/comparison","alerts/template","promql/fragile","promql/regexp","promql/impossible","promql/aggregate(job:true)"] path=rules/0001.yml rule=colo:alerting rules/0001.yml:5 Warning: Alert query doesn't have any condition, it will always fire if the metric exists. (alerts/comparison) 5 | expr: sum(bar) without(job) diff --git a/cmd/pint/tests/0019_match_recording.txt b/cmd/pint/tests/0019_match_recording.txt index 13b9ba2e..efad05d2 100644 --- a/cmd/pint/tests/0019_match_recording.txt +++ b/cmd/pint/tests/0019_match_recording.txt @@ -11,9 +11,9 @@ level=DEBUG msg="Glob finder completed" count=2 level=INFO msg="Checking Prometheus rules" entries=2 workers=10 online=true level=DEBUG msg="Generated all Prometheus servers" count=0 level=DEBUG msg="Found recording rule" path=rules/0001.yml record=colo:recording lines=1-2 state=noop -level=DEBUG msg="Configured checks for rule" enabled=["promql/syntax","alerts/for","alerts/comparison","alerts/template","promql/fragile","promql/regexp","promql/aggregate(job:true)"] path=rules/0001.yml rule=colo:recording +level=DEBUG msg="Configured checks for rule" enabled=["promql/syntax","alerts/for","alerts/comparison","alerts/template","promql/fragile","promql/regexp","promql/impossible","promql/aggregate(job:true)"] path=rules/0001.yml rule=colo:recording level=DEBUG msg="Found alerting rule" path=rules/0001.yml alert=colo:alerting lines=4-5 state=noop -level=DEBUG msg="Configured checks for rule" enabled=["promql/syntax","alerts/for","alerts/comparison","alerts/template","promql/fragile","promql/regexp"] path=rules/0001.yml rule=colo:alerting +level=DEBUG msg="Configured checks for rule" enabled=["promql/syntax","alerts/for","alerts/comparison","alerts/template","promql/fragile","promql/regexp","promql/impossible"] path=rules/0001.yml rule=colo:alerting rules/0001.yml:2 Warning: `job` label is required and should be preserved when aggregating `^.+$` rules, remove job from `without()`. (promql/aggregate) 2 | expr: sum(foo) without(job) diff --git a/cmd/pint/tests/0020_ignore_kind.txt b/cmd/pint/tests/0020_ignore_kind.txt index 30d15b30..1444e0ff 100644 --- a/cmd/pint/tests/0020_ignore_kind.txt +++ b/cmd/pint/tests/0020_ignore_kind.txt @@ -11,9 +11,9 @@ level=DEBUG msg="Glob finder completed" count=2 level=INFO msg="Checking Prometheus rules" entries=2 workers=10 online=true level=DEBUG msg="Generated all Prometheus servers" count=0 level=DEBUG msg="Found recording rule" path=rules/0001.yml record=colo:recording lines=4-5 state=noop -level=DEBUG msg="Configured checks for rule" enabled=["promql/syntax","alerts/for","alerts/comparison","alerts/template","promql/fragile","promql/regexp","promql/aggregate(job:true)"] path=rules/0001.yml rule=colo:recording +level=DEBUG msg="Configured checks for rule" enabled=["promql/syntax","alerts/for","alerts/comparison","alerts/template","promql/fragile","promql/regexp","promql/impossible","promql/aggregate(job:true)"] path=rules/0001.yml rule=colo:recording level=DEBUG msg="Found alerting rule" path=rules/0001.yml alert=colo:alerting lines=7-8 state=noop -level=DEBUG msg="Configured checks for rule" enabled=["promql/syntax","alerts/for","alerts/comparison","alerts/template","promql/fragile","promql/regexp"] path=rules/0001.yml rule=colo:alerting +level=DEBUG msg="Configured checks for rule" enabled=["promql/syntax","alerts/for","alerts/comparison","alerts/template","promql/fragile","promql/regexp","promql/impossible"] path=rules/0001.yml rule=colo:alerting rules/0001.yml:5 Warning: `job` label is required and should be preserved when aggregating `^.+$` rules, remove job from `without()`. (promql/aggregate) 5 | expr: sum(foo) without(job) diff --git a/cmd/pint/tests/0023_enabled_checks.txt b/cmd/pint/tests/0023_enabled_checks.txt index 6a645282..8dcf574f 100644 --- a/cmd/pint/tests/0023_enabled_checks.txt +++ b/cmd/pint/tests/0023_enabled_checks.txt @@ -1,9 +1,9 @@ ! exec pint -l debug --no-color lint rules ! stdout . -stderr 'level=DEBUG msg="Configured checks for rule" enabled=\["promql/syntax","alerts/for","alerts/comparison","alerts/template","promql/fragile","promql/regexp","promql/rate\(prom\)","promql/series\(prom\)","promql/vector_matching\(prom\)"\,"promql/range_query\(prom\)","rule/duplicate\(prom\)","labels/conflict\(prom\)","alerts/external_labels\(prom\)","promql/counter\(prom\)","alerts/absent\(prom\)"] path=rules/1.yaml rule=one' -stderr 'level=DEBUG msg="Configured checks for rule" enabled=\["promql/syntax","alerts/for","alerts/comparison","alerts/template","promql/fragile","promql/regexp","promql/rate\(prom\)","promql/series\(prom\)","promql/vector_matching\(prom\)"\,"promql/range_query\(prom\)","rule/duplicate\(prom\)","labels/conflict\(prom\)","alerts/external_labels\(prom\)","promql/counter\(prom\)","alerts/absent\(prom\)"] path=rules/1.yaml rule=two' -stderr 'level=DEBUG msg="Configured checks for rule" enabled=\["promql/syntax","alerts/for","alerts/comparison","alerts/template","promql/fragile","promql/regexp","promql/rate\(prom\)","promql/series\(prom\)","promql/vector_matching\(prom\)"\,"promql/range_query\(prom\)","rule/duplicate\(prom\)","labels/conflict\(prom\)","alerts/external_labels\(prom\)","promql/counter\(prom\)","alerts/absent\(prom\)"] path=rules/2.yaml rule=one' -stderr 'level=DEBUG msg="Configured checks for rule" enabled=\["promql/syntax","alerts/for","alerts/comparison","alerts/template","promql/fragile","promql/regexp","promql/rate\(prom\)","promql/series\(prom\)","promql/vector_matching\(prom\)"\,"promql/range_query\(prom\)","rule/duplicate\(prom\)","labels/conflict\(prom\)","alerts/external_labels\(prom\)","promql/counter\(prom\)","alerts/absent\(prom\)"] path=rules/2.yaml rule=two' +stderr 'level=DEBUG msg="Configured checks for rule" enabled=\["promql/syntax","alerts/for","alerts/comparison","alerts/template","promql/fragile","promql/regexp","promql/impossible","promql/rate\(prom\)","promql/series\(prom\)","promql/vector_matching\(prom\)"\,"promql/range_query\(prom\)","rule/duplicate\(prom\)","labels/conflict\(prom\)","alerts/external_labels\(prom\)","promql/counter\(prom\)","alerts/absent\(prom\)"] path=rules/1.yaml rule=one' +stderr 'level=DEBUG msg="Configured checks for rule" enabled=\["promql/syntax","alerts/for","alerts/comparison","alerts/template","promql/fragile","promql/regexp","promql/impossible","promql/rate\(prom\)","promql/series\(prom\)","promql/vector_matching\(prom\)"\,"promql/range_query\(prom\)","rule/duplicate\(prom\)","labels/conflict\(prom\)","alerts/external_labels\(prom\)","promql/counter\(prom\)","alerts/absent\(prom\)"] path=rules/1.yaml rule=two' +stderr 'level=DEBUG msg="Configured checks for rule" enabled=\["promql/syntax","alerts/for","alerts/comparison","alerts/template","promql/fragile","promql/regexp","promql/impossible","promql/rate\(prom\)","promql/series\(prom\)","promql/vector_matching\(prom\)"\,"promql/range_query\(prom\)","rule/duplicate\(prom\)","labels/conflict\(prom\)","alerts/external_labels\(prom\)","promql/counter\(prom\)","alerts/absent\(prom\)"] path=rules/2.yaml rule=one' +stderr 'level=DEBUG msg="Configured checks for rule" enabled=\["promql/syntax","alerts/for","alerts/comparison","alerts/template","promql/fragile","promql/regexp","promql/impossible","promql/rate\(prom\)","promql/series\(prom\)","promql/vector_matching\(prom\)"\,"promql/range_query\(prom\)","rule/duplicate\(prom\)","labels/conflict\(prom\)","alerts/external_labels\(prom\)","promql/counter\(prom\)","alerts/absent\(prom\)"] path=rules/2.yaml rule=two' -- rules/1.yaml -- - record: one diff --git a/cmd/pint/tests/0025_config.txt b/cmd/pint/tests/0025_config.txt index 47bdbbba..9819f6b9 100644 --- a/cmd/pint/tests/0025_config.txt +++ b/cmd/pint/tests/0025_config.txt @@ -24,6 +24,7 @@ level=INFO msg="Loading configuration file" path=.pint.hcl "labels/conflict", "promql/aggregate", "alerts/comparison", + "promql/impossible", "promql/fragile", "promql/range_query", "promql/rate", diff --git a/cmd/pint/tests/0037_disable_checks.txt b/cmd/pint/tests/0037_disable_checks.txt index 0b45331e..3dd8a6cf 100644 --- a/cmd/pint/tests/0037_disable_checks.txt +++ b/cmd/pint/tests/0037_disable_checks.txt @@ -13,11 +13,11 @@ level=DEBUG msg="Starting query workers" name=prom uri=http://127.0.0.1 workers= level=INFO msg="Checking Prometheus rules" entries=3 workers=10 online=true level=DEBUG msg="Generated all Prometheus servers" count=1 level=DEBUG msg="Found alerting rule" path=rules/0001.yml alert=default-for lines=1-3 state=noop -level=DEBUG msg="Configured checks for rule" enabled=["promql/syntax","alerts/template","promql/fragile","promql/regexp","promql/vector_matching(prom)","rule/duplicate(prom)","labels/conflict(prom)","alerts/absent(prom)"] path=rules/0001.yml rule=default-for +level=DEBUG msg="Configured checks for rule" enabled=["promql/syntax","alerts/template","promql/fragile","promql/regexp","promql/impossible","promql/vector_matching(prom)","rule/duplicate(prom)","labels/conflict(prom)","alerts/absent(prom)"] path=rules/0001.yml rule=default-for level=DEBUG msg="Found recording rule" path=rules/0001.yml record=sum:job lines=5-6 state=noop -level=DEBUG msg="Configured checks for rule" enabled=["promql/syntax","alerts/template","promql/fragile","promql/regexp","promql/vector_matching(prom)","rule/duplicate(prom)","labels/conflict(prom)","alerts/absent(prom)","promql/aggregate(job:true)"] path=rules/0001.yml rule=sum:job +level=DEBUG msg="Configured checks for rule" enabled=["promql/syntax","alerts/template","promql/fragile","promql/regexp","promql/impossible","promql/vector_matching(prom)","rule/duplicate(prom)","labels/conflict(prom)","alerts/absent(prom)","promql/aggregate(job:true)"] path=rules/0001.yml rule=sum:job level=DEBUG msg="Found alerting rule" path=rules/0001.yml alert=no-comparison lines=8-9 state=noop -level=DEBUG msg="Configured checks for rule" enabled=["promql/syntax","alerts/template","promql/fragile","promql/regexp","promql/vector_matching(prom)","rule/duplicate(prom)","labels/conflict(prom)","alerts/absent(prom)"] path=rules/0001.yml rule=no-comparison +level=DEBUG msg="Configured checks for rule" enabled=["promql/syntax","alerts/template","promql/fragile","promql/regexp","promql/impossible","promql/vector_matching(prom)","rule/duplicate(prom)","labels/conflict(prom)","alerts/absent(prom)"] path=rules/0001.yml rule=no-comparison rules/0001.yml:6 Warning: `job` label is required and should be preserved when aggregating `^.+$` rules, use `by(job, ...)`. (promql/aggregate) 6 | expr: sum(foo) diff --git a/cmd/pint/tests/0039_prom_selected_path.txt b/cmd/pint/tests/0039_prom_selected_path.txt index f5e6c83c..65cfb36e 100644 --- a/cmd/pint/tests/0039_prom_selected_path.txt +++ b/cmd/pint/tests/0039_prom_selected_path.txt @@ -13,11 +13,11 @@ level=DEBUG msg="Starting query workers" name=disabled uri=http://127.0.0.1:123 level=INFO msg="Checking Prometheus rules" entries=3 workers=10 online=true level=DEBUG msg="Generated all Prometheus servers" count=1 level=DEBUG msg="Found alerting rule" path=rules/0001.yml alert=first lines=1-3 state=noop -level=DEBUG msg="Configured checks for rule" enabled=["promql/syntax","alerts/for","alerts/comparison","alerts/template","promql/fragile","promql/regexp"] path=rules/0001.yml rule=first +level=DEBUG msg="Configured checks for rule" enabled=["promql/syntax","alerts/for","alerts/comparison","alerts/template","promql/fragile","promql/regexp","promql/impossible"] path=rules/0001.yml rule=first level=DEBUG msg="Found recording rule" path=rules/0001.yml record=second lines=5-6 state=noop -level=DEBUG msg="Configured checks for rule" enabled=["promql/syntax","alerts/for","alerts/comparison","alerts/template","promql/fragile","promql/regexp","promql/aggregate(job:true)"] path=rules/0001.yml rule=second +level=DEBUG msg="Configured checks for rule" enabled=["promql/syntax","alerts/for","alerts/comparison","alerts/template","promql/fragile","promql/regexp","promql/impossible","promql/aggregate(job:true)"] path=rules/0001.yml rule=second level=DEBUG msg="Found alerting rule" path=rules/0001.yml alert=third lines=8-9 state=noop -level=DEBUG msg="Configured checks for rule" enabled=["promql/syntax","alerts/for","alerts/comparison","alerts/template","promql/fragile","promql/regexp"] path=rules/0001.yml rule=third +level=DEBUG msg="Configured checks for rule" enabled=["promql/syntax","alerts/for","alerts/comparison","alerts/template","promql/fragile","promql/regexp","promql/impossible"] path=rules/0001.yml rule=third rules/0001.yml:6 Warning: `job` label is required and should be preserved when aggregating `^.+$` rules, use `by(job, ...)`. (promql/aggregate) 6 | expr: sum(bar) diff --git a/cmd/pint/tests/0040_rule_match_label.txt b/cmd/pint/tests/0040_rule_match_label.txt index d0b158dd..e9069985 100644 --- a/cmd/pint/tests/0040_rule_match_label.txt +++ b/cmd/pint/tests/0040_rule_match_label.txt @@ -11,13 +11,13 @@ level=DEBUG msg="Glob finder completed" count=4 level=INFO msg="Checking Prometheus rules" entries=4 workers=10 online=true level=DEBUG msg="Generated all Prometheus servers" count=0 level=DEBUG msg="Found recording rule" path=rules/rules.yml record=ignore lines=1-2 state=noop -level=DEBUG msg="Configured checks for rule" enabled=["promql/syntax","alerts/for","alerts/comparison","alerts/template","promql/fragile","promql/regexp"] path=rules/rules.yml rule=ignore +level=DEBUG msg="Configured checks for rule" enabled=["promql/syntax","alerts/for","alerts/comparison","alerts/template","promql/fragile","promql/regexp","promql/impossible"] path=rules/rules.yml rule=ignore level=DEBUG msg="Found recording rule" path=rules/rules.yml record=match lines=4-7 state=noop -level=DEBUG msg="Configured checks for rule" enabled=["promql/syntax","alerts/for","alerts/comparison","alerts/template","promql/fragile","promql/regexp","promql/aggregate(job:true)"] path=rules/rules.yml rule=match +level=DEBUG msg="Configured checks for rule" enabled=["promql/syntax","alerts/for","alerts/comparison","alerts/template","promql/fragile","promql/regexp","promql/impossible","promql/aggregate(job:true)"] path=rules/rules.yml rule=match level=DEBUG msg="Found alerting rule" path=rules/rules.yml alert=ignore lines=9-10 state=noop -level=DEBUG msg="Configured checks for rule" enabled=["promql/syntax","alerts/for","alerts/comparison","alerts/template","promql/fragile","promql/regexp"] path=rules/rules.yml rule=ignore +level=DEBUG msg="Configured checks for rule" enabled=["promql/syntax","alerts/for","alerts/comparison","alerts/template","promql/fragile","promql/regexp","promql/impossible"] path=rules/rules.yml rule=ignore level=DEBUG msg="Found alerting rule" path=rules/rules.yml alert=match lines=12-15 state=noop -level=DEBUG msg="Configured checks for rule" enabled=["promql/syntax","alerts/for","alerts/comparison","alerts/template","promql/fragile","promql/regexp","promql/aggregate(job:true)"] path=rules/rules.yml rule=match +level=DEBUG msg="Configured checks for rule" enabled=["promql/syntax","alerts/for","alerts/comparison","alerts/template","promql/fragile","promql/regexp","promql/impossible","promql/aggregate(job:true)"] path=rules/rules.yml rule=match rules/rules.yml:5 Warning: `job` label is required and should be preserved when aggregating `^.*$` rules, use `by(job, ...)`. (promql/aggregate) 5 | expr: sum(foo) diff --git a/cmd/pint/tests/0042_watch_metrics.txt b/cmd/pint/tests/0042_watch_metrics.txt index c77feb93..50b10633 100644 --- a/cmd/pint/tests/0042_watch_metrics.txt +++ b/cmd/pint/tests/0042_watch_metrics.txt @@ -145,6 +145,8 @@ pint_check_duration_seconds_sum{check="promql/aggregate"} pint_check_duration_seconds_count{check="promql/aggregate"} pint_check_duration_seconds_sum{check="promql/fragile"} pint_check_duration_seconds_count{check="promql/fragile"} +pint_check_duration_seconds_sum{check="promql/impossible"} +pint_check_duration_seconds_count{check="promql/impossible"} pint_check_duration_seconds_sum{check="promql/regexp"} pint_check_duration_seconds_count{check="promql/regexp"} pint_check_duration_seconds_sum{check="promql/syntax"} diff --git a/cmd/pint/tests/0052_match_multiple.txt b/cmd/pint/tests/0052_match_multiple.txt index a28ed47d..f74e6391 100644 --- a/cmd/pint/tests/0052_match_multiple.txt +++ b/cmd/pint/tests/0052_match_multiple.txt @@ -11,9 +11,9 @@ level=DEBUG msg="Glob finder completed" count=2 level=INFO msg="Checking Prometheus rules" entries=2 workers=10 online=true level=DEBUG msg="Generated all Prometheus servers" count=0 level=DEBUG msg="Found recording rule" path=rules/0001.yml record=colo:recording lines=4-5 state=noop -level=DEBUG msg="Configured checks for rule" enabled=["promql/syntax","alerts/for","alerts/comparison","alerts/template","promql/fragile","promql/regexp","promql/aggregate(job:true)"] path=rules/0001.yml rule=colo:recording +level=DEBUG msg="Configured checks for rule" enabled=["promql/syntax","alerts/for","alerts/comparison","alerts/template","promql/fragile","promql/regexp","promql/impossible","promql/aggregate(job:true)"] path=rules/0001.yml rule=colo:recording level=DEBUG msg="Found alerting rule" path=rules/0001.yml alert=colo:alerting lines=7-8 state=noop -level=DEBUG msg="Configured checks for rule" enabled=["promql/syntax","alerts/for","alerts/comparison","alerts/template","promql/fragile","promql/regexp","promql/aggregate(job:true)"] path=rules/0001.yml rule=colo:alerting +level=DEBUG msg="Configured checks for rule" enabled=["promql/syntax","alerts/for","alerts/comparison","alerts/template","promql/fragile","promql/regexp","promql/impossible","promql/aggregate(job:true)"] path=rules/0001.yml rule=colo:alerting rules/0001.yml:5 Warning: `job` label is required and should be preserved when aggregating `^.+$` rules, remove job from `without()`. (promql/aggregate) 5 | expr: sum(foo) without(job) diff --git a/cmd/pint/tests/0053_ignore_multiple.txt b/cmd/pint/tests/0053_ignore_multiple.txt index 20cd4f1b..088175da 100644 --- a/cmd/pint/tests/0053_ignore_multiple.txt +++ b/cmd/pint/tests/0053_ignore_multiple.txt @@ -11,9 +11,9 @@ level=DEBUG msg="Glob finder completed" count=2 level=INFO msg="Checking Prometheus rules" entries=2 workers=10 online=true level=DEBUG msg="Generated all Prometheus servers" count=0 level=DEBUG msg="Found recording rule" path=rules/0001.yml record=colo:recording lines=4-5 state=noop -level=DEBUG msg="Configured checks for rule" enabled=["promql/syntax","alerts/for","alerts/comparison","alerts/template","promql/fragile","promql/regexp"] path=rules/0001.yml rule=colo:recording +level=DEBUG msg="Configured checks for rule" enabled=["promql/syntax","alerts/for","alerts/comparison","alerts/template","promql/fragile","promql/regexp","promql/impossible"] path=rules/0001.yml rule=colo:recording level=DEBUG msg="Found alerting rule" path=rules/0001.yml alert=colo:alerting lines=7-8 state=noop -level=DEBUG msg="Configured checks for rule" enabled=["promql/syntax","alerts/for","alerts/comparison","alerts/template","promql/fragile","promql/regexp"] path=rules/0001.yml rule=colo:alerting +level=DEBUG msg="Configured checks for rule" enabled=["promql/syntax","alerts/for","alerts/comparison","alerts/template","promql/fragile","promql/regexp","promql/impossible"] path=rules/0001.yml rule=colo:alerting -- rules/0001.yml -- groups: - name: foo diff --git a/cmd/pint/tests/0054_watch_metrics_prometheus.txt b/cmd/pint/tests/0054_watch_metrics_prometheus.txt index 6635bb03..63e11a6c 100644 --- a/cmd/pint/tests/0054_watch_metrics_prometheus.txt +++ b/cmd/pint/tests/0054_watch_metrics_prometheus.txt @@ -59,6 +59,8 @@ pint_check_duration_seconds_sum{check="promql/counter"} pint_check_duration_seconds_count{check="promql/counter"} pint_check_duration_seconds_sum{check="promql/fragile"} pint_check_duration_seconds_count{check="promql/fragile"} +pint_check_duration_seconds_sum{check="promql/impossible"} +pint_check_duration_seconds_count{check="promql/impossible"} pint_check_duration_seconds_sum{check="promql/range_query"} pint_check_duration_seconds_count{check="promql/range_query"} pint_check_duration_seconds_sum{check="promql/rate"} diff --git a/cmd/pint/tests/0057_watch_metrics_prometheus_ignore.txt b/cmd/pint/tests/0057_watch_metrics_prometheus_ignore.txt index 0703b3ca..c62da058 100644 --- a/cmd/pint/tests/0057_watch_metrics_prometheus_ignore.txt +++ b/cmd/pint/tests/0057_watch_metrics_prometheus_ignore.txt @@ -57,6 +57,8 @@ pint_check_duration_seconds_sum{check="promql/counter"} pint_check_duration_seconds_count{check="promql/counter"} pint_check_duration_seconds_sum{check="promql/fragile"} pint_check_duration_seconds_count{check="promql/fragile"} +pint_check_duration_seconds_sum{check="promql/impossible"} +pint_check_duration_seconds_count{check="promql/impossible"} pint_check_duration_seconds_sum{check="promql/range_query"} pint_check_duration_seconds_count{check="promql/range_query"} pint_check_duration_seconds_sum{check="promql/rate"} diff --git a/cmd/pint/tests/0092_dir_symlink.txt b/cmd/pint/tests/0092_dir_symlink.txt index 6499a5e6..8ea91dca 100644 --- a/cmd/pint/tests/0092_dir_symlink.txt +++ b/cmd/pint/tests/0092_dir_symlink.txt @@ -14,7 +14,7 @@ level=DEBUG msg="Glob finder completed" count=1 level=INFO msg="Checking Prometheus rules" entries=1 workers=10 online=true level=DEBUG msg="Generated all Prometheus servers" count=0 level=DEBUG msg="Found recording rule" path=rules/src/rule.yaml record=down lines=4-5 state=noop -level=DEBUG msg="Configured checks for rule" enabled=["promql/syntax","alerts/for","alerts/comparison","alerts/template","promql/fragile","promql/regexp"] path=rules/src/rule.yaml rule=down +level=DEBUG msg="Configured checks for rule" enabled=["promql/syntax","alerts/for","alerts/comparison","alerts/template","promql/fragile","promql/regexp","promql/impossible"] path=rules/src/rule.yaml rule=down -- rules/src/rule.yaml -- groups: - name: foo diff --git a/cmd/pint/tests/0095_rulefmt_symlink.txt b/cmd/pint/tests/0095_rulefmt_symlink.txt index cee52f62..17bca820 100644 --- a/cmd/pint/tests/0095_rulefmt_symlink.txt +++ b/cmd/pint/tests/0095_rulefmt_symlink.txt @@ -15,9 +15,9 @@ level=DEBUG msg="Glob finder completed" count=2 level=INFO msg="Checking Prometheus rules" entries=2 workers=10 online=true level=DEBUG msg="Generated all Prometheus servers" count=0 level=DEBUG msg="Found recording rule" path=rules/relaxed/1.yml record=foo lines=1-2 state=noop -level=DEBUG msg="Configured checks for rule" enabled=["promql/syntax","alerts/for","alerts/comparison","alerts/template","promql/fragile","promql/regexp"] path=rules/relaxed/1.yml rule=foo +level=DEBUG msg="Configured checks for rule" enabled=["promql/syntax","alerts/for","alerts/comparison","alerts/template","promql/fragile","promql/regexp","promql/impossible"] path=rules/relaxed/1.yml rule=foo level=DEBUG msg="Found recording rule" path=rules/strict/symlink.yml record=foo lines=1-2 state=noop -level=DEBUG msg="Configured checks for rule" enabled=["promql/syntax","alerts/for","alerts/comparison","alerts/template","promql/fragile","promql/regexp"] path=rules/strict/symlink.yml rule=foo +level=DEBUG msg="Configured checks for rule" enabled=["promql/syntax","alerts/for","alerts/comparison","alerts/template","promql/fragile","promql/regexp","promql/impossible"] path=rules/strict/symlink.yml rule=foo -- rules/relaxed/1.yml -- - record: foo expr: up == 0 diff --git a/cmd/pint/tests/0099_symlink_outside_glob.txt b/cmd/pint/tests/0099_symlink_outside_glob.txt index d8733700..6cebbad1 100644 --- a/cmd/pint/tests/0099_symlink_outside_glob.txt +++ b/cmd/pint/tests/0099_symlink_outside_glob.txt @@ -14,7 +14,7 @@ level=DEBUG msg="Glob finder completed" count=1 level=INFO msg="Checking Prometheus rules" entries=1 workers=10 online=true level=DEBUG msg="Generated all Prometheus servers" count=0 level=DEBUG msg="Found recording rule" path=rules/relaxed/1.yml record=foo lines=1-2 state=noop -level=DEBUG msg="Configured checks for rule" enabled=["promql/syntax","alerts/for","alerts/comparison","alerts/template","promql/fragile","promql/regexp"] path=rules/relaxed/1.yml rule=foo +level=DEBUG msg="Configured checks for rule" enabled=["promql/syntax","alerts/for","alerts/comparison","alerts/template","promql/fragile","promql/regexp","promql/impossible"] path=rules/relaxed/1.yml rule=foo -- rules/relaxed/1.yml -- - record: foo expr: up == 0 diff --git a/cmd/pint/tests/0103_file_disable.txt b/cmd/pint/tests/0103_file_disable.txt index 3904923d..0884d0ae 100644 --- a/cmd/pint/tests/0103_file_disable.txt +++ b/cmd/pint/tests/0103_file_disable.txt @@ -13,7 +13,7 @@ level=DEBUG msg="Starting query workers" name=prom uri=http://127.0.0.1:7103 wor level=INFO msg="Checking Prometheus rules" entries=1 workers=10 online=true level=DEBUG msg="Generated all Prometheus servers" count=1 level=DEBUG msg="Found recording rule" path=rules/0001.yml record=colo:test1 lines=9-10 state=noop -level=DEBUG msg="Configured checks for rule" enabled=["promql/syntax","alerts/for","alerts/comparison","alerts/template","promql/fragile","promql/regexp","promql/vector_matching(prom)","labels/conflict(prom)","alerts/external_labels(prom)","alerts/absent(prom)"] path=rules/0001.yml rule=colo:test1 +level=DEBUG msg="Configured checks for rule" enabled=["promql/syntax","alerts/for","alerts/comparison","alerts/template","promql/fragile","promql/regexp","promql/impossible","promql/vector_matching(prom)","labels/conflict(prom)","alerts/external_labels(prom)","alerts/absent(prom)"] path=rules/0001.yml rule=colo:test1 level=DEBUG msg="Stopping query workers" name=prom uri=http://127.0.0.1:7103 -- rules/0001.yml -- # This should skip all online checks diff --git a/cmd/pint/tests/0111_snooze.txt b/cmd/pint/tests/0111_snooze.txt index c835203b..9f32329a 100644 --- a/cmd/pint/tests/0111_snooze.txt +++ b/cmd/pint/tests/0111_snooze.txt @@ -12,7 +12,7 @@ level=INFO msg="Checking Prometheus rules" entries=1 workers=10 online=true level=DEBUG msg="Generated all Prometheus servers" count=0 level=DEBUG msg="Found recording rule" path=rules/0001.yml record=sum:job lines=2-3 state=noop level=DEBUG msg="Check snoozed by comment" check=promql/aggregate(job:true) match=promql/aggregate until="2099-11-28T10:24:18Z" -level=DEBUG msg="Configured checks for rule" enabled=["promql/syntax","alerts/for","alerts/comparison","alerts/template","promql/fragile","promql/regexp"] path=rules/0001.yml rule=sum:job +level=DEBUG msg="Configured checks for rule" enabled=["promql/syntax","alerts/for","alerts/comparison","alerts/template","promql/fragile","promql/regexp","promql/impossible"] path=rules/0001.yml rule=sum:job -- rules/0001.yml -- # pint snooze 2099-11-28T10:24:18Z promql/aggregate - record: sum:job diff --git a/cmd/pint/tests/0112_expired_snooze.txt b/cmd/pint/tests/0112_expired_snooze.txt index 67ba8ab7..96235bc8 100644 --- a/cmd/pint/tests/0112_expired_snooze.txt +++ b/cmd/pint/tests/0112_expired_snooze.txt @@ -11,7 +11,7 @@ level=DEBUG msg="Glob finder completed" count=1 level=INFO msg="Checking Prometheus rules" entries=1 workers=10 online=true level=DEBUG msg="Generated all Prometheus servers" count=0 level=DEBUG msg="Found recording rule" path=rules/0001.yml record=sum:job lines=2-3 state=noop -level=DEBUG msg="Configured checks for rule" enabled=["promql/syntax","alerts/for","alerts/comparison","alerts/template","promql/fragile","promql/regexp","promql/aggregate(job:true)"] path=rules/0001.yml rule=sum:job +level=DEBUG msg="Configured checks for rule" enabled=["promql/syntax","alerts/for","alerts/comparison","alerts/template","promql/fragile","promql/regexp","promql/impossible","promql/aggregate(job:true)"] path=rules/0001.yml rule=sum:job rules/0001.yml:3 Bug: `job` label is required and should be preserved when aggregating `^.+$` rules, use `by(job, ...)`. (promql/aggregate) 3 | expr: sum(foo) diff --git a/cmd/pint/tests/0113_config_env_expand.txt b/cmd/pint/tests/0113_config_env_expand.txt index d37c8c4d..32341ce6 100644 --- a/cmd/pint/tests/0113_config_env_expand.txt +++ b/cmd/pint/tests/0113_config_env_expand.txt @@ -29,6 +29,7 @@ level=INFO msg="Loading configuration file" path=.pint.hcl "labels/conflict", "promql/aggregate", "alerts/comparison", + "promql/impossible", "promql/fragile", "promql/range_query", "promql/rate", diff --git a/cmd/pint/tests/0115_file_disable_tag.txt b/cmd/pint/tests/0115_file_disable_tag.txt index bdeb3a76..444fc621 100644 --- a/cmd/pint/tests/0115_file_disable_tag.txt +++ b/cmd/pint/tests/0115_file_disable_tag.txt @@ -13,7 +13,7 @@ level=DEBUG msg="Starting query workers" name=prom uri=http://127.0.0.1:7103 wor level=INFO msg="Checking Prometheus rules" entries=1 workers=10 online=true level=DEBUG msg="Generated all Prometheus servers" count=1 level=DEBUG msg="Found recording rule" path=rules/0001.yml record=colo:test1 lines=6-8 state=noop -level=DEBUG msg="Configured checks for rule" enabled=["promql/syntax","alerts/for","alerts/comparison","alerts/template","promql/fragile","promql/regexp","alerts/external_labels(prom)","promql/counter(prom)","alerts/absent(prom)"] path=rules/0001.yml rule=colo:test1 +level=DEBUG msg="Configured checks for rule" enabled=["promql/syntax","alerts/for","alerts/comparison","alerts/template","promql/fragile","promql/regexp","promql/impossible","alerts/external_labels(prom)","promql/counter(prom)","alerts/absent(prom)"] path=rules/0001.yml rule=colo:test1 level=DEBUG msg="Scheduling Prometheus metrics metadata query" uri=http://127.0.0.1:7103 metric=foo level=DEBUG msg="Getting prometheus metrics metadata" uri=http://127.0.0.1:7103 metric=foo level=ERROR msg="Query returned an error" err="failed to query Prometheus metrics metadata: Get \"http://127.0.0.1:7103/api/v1/metadata?metric=foo\": dial tcp 127.0.0.1:7103: connect: connection refused" uri=http://127.0.0.1:7103 query=foo diff --git a/cmd/pint/tests/0116_file_snooze.txt b/cmd/pint/tests/0116_file_snooze.txt index 3e3c1be3..d5215070 100644 --- a/cmd/pint/tests/0116_file_snooze.txt +++ b/cmd/pint/tests/0116_file_snooze.txt @@ -13,9 +13,9 @@ level=DEBUG msg="Glob finder completed" count=2 level=INFO msg="Checking Prometheus rules" entries=2 workers=10 online=true level=DEBUG msg="Generated all Prometheus servers" count=0 level=DEBUG msg="Found recording rule" path=rules/0001.yml record=sum:job lines=4-5 state=noop -level=DEBUG msg="Configured checks for rule" enabled=["promql/syntax","alerts/comparison","alerts/template","promql/fragile","promql/regexp"] path=rules/0001.yml rule=sum:job +level=DEBUG msg="Configured checks for rule" enabled=["promql/syntax","alerts/comparison","alerts/template","promql/fragile","promql/regexp","promql/impossible"] path=rules/0001.yml rule=sum:job level=DEBUG msg="Found alerting rule" path=rules/0001.yml alert=Down lines=7-9 state=noop -level=DEBUG msg="Configured checks for rule" enabled=["promql/syntax","alerts/comparison","alerts/template","promql/fragile","promql/regexp"] path=rules/0001.yml rule=Down +level=DEBUG msg="Configured checks for rule" enabled=["promql/syntax","alerts/comparison","alerts/template","promql/fragile","promql/regexp","promql/impossible"] path=rules/0001.yml rule=Down -- rules/0001.yml -- # pint file/snooze 2099-11-28T10:24:18Z promql/aggregate(job:true) # pint file/snooze 2099-11-28T10:24:18Z alerts/for diff --git a/cmd/pint/tests/0144_discovery_filepath.txt b/cmd/pint/tests/0144_discovery_filepath.txt index 860ddf5b..1ce685de 100644 --- a/cmd/pint/tests/0144_discovery_filepath.txt +++ b/cmd/pint/tests/0144_discovery_filepath.txt @@ -30,7 +30,7 @@ level=DEBUG msg="Starting query workers" name=prom2 uri=https://prom2.example.co level=DEBUG msg="Starting query workers" name=prom2 uri=https://prom2-backup.example.com workers=16 level=DEBUG msg="Generated all Prometheus servers" count=2 level=DEBUG msg="Found recording rule" path=rules/0001.yml record=sum:up lines=4-5 state=noop -level=DEBUG msg="Configured checks for rule" enabled=["promql/syntax","alerts/for","alerts/comparison","alerts/template","promql/fragile","promql/regexp"] path=rules/0001.yml rule=sum:up +level=DEBUG msg="Configured checks for rule" enabled=["promql/syntax","alerts/for","alerts/comparison","alerts/template","promql/fragile","promql/regexp","promql/impossible"] path=rules/0001.yml rule=sum:up level=DEBUG msg="Stopping query workers" name=prom1 uri=https://prom1.example.com level=DEBUG msg="Stopping query workers" name=prom1 uri=https://prom1-backup.example.com level=DEBUG msg="Stopping query workers" name=prom2 uri=https://prom2.example.com diff --git a/cmd/pint/tests/0148_discovery_prom_zero.txt b/cmd/pint/tests/0148_discovery_prom_zero.txt index 2c1bf0bd..188d5a11 100644 --- a/cmd/pint/tests/0148_discovery_prom_zero.txt +++ b/cmd/pint/tests/0148_discovery_prom_zero.txt @@ -20,7 +20,7 @@ level=DEBUG msg="Parsed response" uri=http://127.0.0.1:7148 query=prometheus_rea level=DEBUG msg="Stopping query workers" name=discovery uri=http://127.0.0.1:7148 level=DEBUG msg="Generated all Prometheus servers" count=0 level=DEBUG msg="Found recording rule" path=rules/0001.yml record=sum:up lines=4-5 state=noop -level=DEBUG msg="Configured checks for rule" enabled=["promql/syntax","alerts/for","alerts/comparison","alerts/template","promql/fragile","promql/regexp"] path=rules/0001.yml rule=sum:up +level=DEBUG msg="Configured checks for rule" enabled=["promql/syntax","alerts/for","alerts/comparison","alerts/template","promql/fragile","promql/regexp","promql/impossible"] path=rules/0001.yml rule=sum:up -- rules/0001.yml -- groups: - name: foo diff --git a/cmd/pint/tests/0149_discovery_prom.txt b/cmd/pint/tests/0149_discovery_prom.txt index a92f0843..d5755605 100644 --- a/cmd/pint/tests/0149_discovery_prom.txt +++ b/cmd/pint/tests/0149_discovery_prom.txt @@ -26,7 +26,7 @@ level=DEBUG msg="Starting query workers" name=prom-ha uri=https://prom1.example. level=DEBUG msg="Starting query workers" name=prom-ha uri=https://prom2.example.com workers=16 level=DEBUG msg="Generated all Prometheus servers" count=1 level=DEBUG msg="Found recording rule" path=rules/0001.yml record=sum:up lines=4-5 state=noop -level=DEBUG msg="Configured checks for rule" enabled=["promql/syntax","alerts/for","alerts/comparison","alerts/template","promql/fragile","promql/regexp"] path=rules/0001.yml rule=sum:up +level=DEBUG msg="Configured checks for rule" enabled=["promql/syntax","alerts/for","alerts/comparison","alerts/template","promql/fragile","promql/regexp","promql/impossible"] path=rules/0001.yml rule=sum:up level=DEBUG msg="Stopping query workers" name=prom-ha uri=https://prom1.example.com level=DEBUG msg="Stopping query workers" name=prom-ha uri=https://prom2.example.com -- rules/0001.yml -- diff --git a/cmd/pint/tests/0152_discovery_prom_dup_uptime.txt b/cmd/pint/tests/0152_discovery_prom_dup_uptime.txt index 0b612b73..679514ad 100644 --- a/cmd/pint/tests/0152_discovery_prom_dup_uptime.txt +++ b/cmd/pint/tests/0152_discovery_prom_dup_uptime.txt @@ -34,7 +34,7 @@ level=DEBUG msg="Starting query workers" name=prom-ha uri=https://prom1.example. level=DEBUG msg="Starting query workers" name=prom-ha uri=https://prom2.example.com workers=16 level=DEBUG msg="Generated all Prometheus servers" count=1 level=DEBUG msg="Found recording rule" path=rules/0001.yml record=sum:up lines=4-5 state=noop -level=DEBUG msg="Configured checks for rule" enabled=["promql/syntax","alerts/for","alerts/comparison","alerts/template","promql/fragile","promql/regexp"] path=rules/0001.yml rule=sum:up +level=DEBUG msg="Configured checks for rule" enabled=["promql/syntax","alerts/for","alerts/comparison","alerts/template","promql/fragile","promql/regexp","promql/impossible"] path=rules/0001.yml rule=sum:up level=DEBUG msg="Stopping query workers" name=prom-ha uri=https://prom1.example.com level=DEBUG msg="Stopping query workers" name=prom-ha uri=https://prom2.example.com -- rules/0001.yml -- diff --git a/cmd/pint/tests/0178_parser_include.txt b/cmd/pint/tests/0178_parser_include.txt index 7659e540..0a940063 100644 --- a/cmd/pint/tests/0178_parser_include.txt +++ b/cmd/pint/tests/0178_parser_include.txt @@ -13,7 +13,7 @@ level=DEBUG msg="Glob finder completed" count=1 level=INFO msg="Checking Prometheus rules" entries=1 workers=10 online=true level=DEBUG msg="Generated all Prometheus servers" count=0 level=DEBUG msg="Found recording rule" path=rules/0001.yml record=ok lines=1-2 state=noop -level=DEBUG msg="Configured checks for rule" enabled=["promql/syntax","alerts/for","alerts/comparison","alerts/template","promql/fragile","promql/regexp"] path=rules/0001.yml rule=ok +level=DEBUG msg="Configured checks for rule" enabled=["promql/syntax","alerts/for","alerts/comparison","alerts/template","promql/fragile","promql/regexp","promql/impossible"] path=rules/0001.yml rule=ok -- rules/0001.yml -- - record: ok expr: sum(foo) without(job) diff --git a/cmd/pint/tests/0179_parser_exclude.txt b/cmd/pint/tests/0179_parser_exclude.txt index 9ad788ed..0402f72a 100644 --- a/cmd/pint/tests/0179_parser_exclude.txt +++ b/cmd/pint/tests/0179_parser_exclude.txt @@ -12,7 +12,7 @@ level=DEBUG msg="Glob finder completed" count=1 level=INFO msg="Checking Prometheus rules" entries=1 workers=10 online=true level=DEBUG msg="Generated all Prometheus servers" count=0 level=DEBUG msg="Found recording rule" path=rules/0001.yml record=ok lines=1-2 state=noop -level=DEBUG msg="Configured checks for rule" enabled=["promql/syntax","alerts/for","alerts/comparison","alerts/template","promql/fragile","promql/regexp"] path=rules/0001.yml rule=ok +level=DEBUG msg="Configured checks for rule" enabled=["promql/syntax","alerts/for","alerts/comparison","alerts/template","promql/fragile","promql/regexp","promql/impossible"] path=rules/0001.yml rule=ok -- rules/0001.yml -- - record: ok expr: sum(foo) without(job) diff --git a/cmd/pint/tests/0180_parser_exclude_md.txt b/cmd/pint/tests/0180_parser_exclude_md.txt index c01bf4ea..0040f4b1 100644 --- a/cmd/pint/tests/0180_parser_exclude_md.txt +++ b/cmd/pint/tests/0180_parser_exclude_md.txt @@ -12,7 +12,7 @@ level=DEBUG msg="Glob finder completed" count=1 level=INFO msg="Checking Prometheus rules" entries=1 workers=10 online=true level=DEBUG msg="Generated all Prometheus servers" count=0 level=DEBUG msg="Found recording rule" path=rules/0001.yml record=ok lines=1-2 state=noop -level=DEBUG msg="Configured checks for rule" enabled=["promql/syntax","alerts/for","alerts/comparison","alerts/template","promql/fragile","promql/regexp"] path=rules/0001.yml rule=ok +level=DEBUG msg="Configured checks for rule" enabled=["promql/syntax","alerts/for","alerts/comparison","alerts/template","promql/fragile","promql/regexp","promql/impossible"] path=rules/0001.yml rule=ok -- rules/0001.yml -- - record: ok expr: sum(foo) without(job) diff --git a/cmd/pint/tests/0207_locked_rule.txt b/cmd/pint/tests/0207_locked_rule.txt index 9e8a2d0f..8a2fb480 100644 --- a/cmd/pint/tests/0207_locked_rule.txt +++ b/cmd/pint/tests/0207_locked_rule.txt @@ -11,7 +11,7 @@ level=DEBUG msg="Glob finder completed" count=1 level=INFO msg="Checking Prometheus rules" entries=1 workers=10 online=true level=DEBUG msg="Generated all Prometheus servers" count=0 level=DEBUG msg="Found recording rule" path=rules/0001.yaml record=colo_job:up:byinstance lines=6-7 state=noop -level=DEBUG msg="Configured checks for rule" enabled=["promql/syntax","alerts/for","alerts/comparison","alerts/template","promql/fragile","promql/regexp","promql/aggregate(job:true)"] path=rules/0001.yaml rule=colo_job:up:byinstance +level=DEBUG msg="Configured checks for rule" enabled=["promql/syntax","alerts/for","alerts/comparison","alerts/template","promql/fragile","promql/regexp","promql/impossible","promql/aggregate(job:true)"] path=rules/0001.yaml rule=colo_job:up:byinstance rules/0001.yaml:7 Bug: `job` label is required and should be preserved when aggregating `^.+$` rules, use `by(job, ...)`. (promql/aggregate) 7 | expr: sum(byinstance) by(instance) diff --git a/internal/checks/base.go b/internal/checks/base.go index c01515e3..c8f952a3 100644 --- a/internal/checks/base.go +++ b/internal/checks/base.go @@ -21,6 +21,7 @@ var ( LabelsConflictCheckName, AggregationCheckName, ComparisonCheckName, + ImpossibleCheckName, FragileCheckName, RangeQueryCheckName, RateCheckName, diff --git a/internal/checks/promql_impossible.go b/internal/checks/promql_impossible.go new file mode 100644 index 00000000..6348e646 --- /dev/null +++ b/internal/checks/promql_impossible.go @@ -0,0 +1,77 @@ +package checks + +import ( + "context" + + "github.com/cloudflare/pint/internal/discovery" + "github.com/cloudflare/pint/internal/parser" + "github.com/cloudflare/pint/internal/parser/utils" +) + +const ( + ImpossibleCheckName = "promql/impossible" +) + +func NewImpossibleCheck() ImpossibleCheck { + return ImpossibleCheck{} +} + +type ImpossibleCheck struct{} + +func (c ImpossibleCheck) Meta() CheckMeta { + return CheckMeta{ + States: []discovery.ChangeType{ + discovery.Noop, + discovery.Added, + discovery.Modified, + discovery.Moved, + }, + Online: false, + AlwaysEnabled: false, + } +} + +func (c ImpossibleCheck) String() string { + return ImpossibleCheckName +} + +func (c ImpossibleCheck) Reporter() string { + return ImpossibleCheckName +} + +func (c ImpossibleCheck) Check(_ context.Context, _ discovery.Path, rule parser.Rule, _ []discovery.Entry) (problems []Problem) { + expr := rule.Expr() + if expr.SyntaxError != nil { + return problems + } + + for _, s := range utils.LabelsSource(expr.Value.Value, expr.Query.Expr) { + problems = append(problems, c.checkSource(expr, s)...) + for _, j := range s.Joins { + problems = append(problems, c.checkSource(expr, j.Src)...) + } + for _, u := range s.Unless { + problems = append(problems, c.checkSource(expr, u.Src)...) + } + } + + return problems +} + +func (c ImpossibleCheck) checkSource(expr parser.PromQLExpr, s utils.Source) (problems []Problem) { + if s.IsDead { + fragment := s.Fragment(expr.Value.Value) + if fragment == "" { + fragment = "This" + } else { + fragment = "`" + fragment + "`" + } + problems = append(problems, Problem{ + Lines: expr.Value.Lines, + Reporter: c.Reporter(), + Text: fragment + " is dead code because " + s.IsDeadReason + ".", + Severity: Warning, + }) + } + return problems +} diff --git a/internal/checks/promql_impossible_test.go b/internal/checks/promql_impossible_test.go new file mode 100644 index 00000000..af9ab303 --- /dev/null +++ b/internal/checks/promql_impossible_test.go @@ -0,0 +1,93 @@ +package checks_test + +import ( + "testing" + + "github.com/cloudflare/pint/internal/checks" + "github.com/cloudflare/pint/internal/parser" + "github.com/cloudflare/pint/internal/promapi" +) + +func newImpossibleCheck(_ *promapi.FailoverGroup) checks.RuleChecker { + return checks.NewImpossibleCheck() +} + +func TestImpossibleCheck(t *testing.T) { + testCases := []checkTest{ + { + description: "ignores rules with syntax errors", + content: "- record: foo\n expr: sum(foo) without(\n", + checker: newImpossibleCheck, + prometheus: newSimpleProm, + problems: noProblems, + }, + { + description: "vector(0) > 0", + content: ` +- alert: Foo + expr: ((( group(vector(0)) ))) > 0 +`, + checker: newImpossibleCheck, + prometheus: newSimpleProm, + problems: func(_ string) []checks.Problem { + return []checks.Problem{ + { + Lines: parser.LineRange{ + First: 3, + Last: 3, + }, + Reporter: checks.ImpossibleCheckName, + Text: "`vector(0)` is dead code because this query always evaluates to `0 > 0` which is not possible, so it will never return anything.", + Severity: checks.Warning, + }, + } + }, + }, + { + description: "0 > 0", + content: ` +- alert: Foo + expr: 0 > bool 0 +`, + checker: newImpossibleCheck, + prometheus: newSimpleProm, + problems: func(_ string) []checks.Problem { + return []checks.Problem{ + { + Lines: parser.LineRange{ + First: 3, + Last: 3, + }, + Reporter: checks.ImpossibleCheckName, + Text: "This is dead code because this query always evaluates to `0 > 0` which is not possible, so it will never return anything.", + Severity: checks.Warning, + }, + } + }, + }, + { + description: "sum(foo or vector(0)) > 0", + content: ` +- alert: Foo + expr: sum(foo or vector(0)) > 0 +`, + checker: newImpossibleCheck, + prometheus: newSimpleProm, + problems: func(_ string) []checks.Problem { + return []checks.Problem{ + { + Lines: parser.LineRange{ + First: 3, + Last: 3, + }, + Reporter: checks.ImpossibleCheckName, + Text: "`vector(0)` is dead code because this query always evaluates to `0 > 0` which is not possible, so it will never return anything.", + Severity: checks.Warning, + }, + } + }, + }, + } + + runTests(t, testCases) +} diff --git a/internal/checks/promql_series.go b/internal/checks/promql_series.go index f501a898..9dd1ab0d 100644 --- a/internal/checks/promql_series.go +++ b/internal/checks/promql_series.go @@ -774,7 +774,16 @@ func selectorWithoutOffset(vs *promParser.VectorSelector) *promParser.VectorSele func sourceHasFallback(src []utils.Source) bool { for _, ls := range src { - if ls.AlwaysReturns && len(ls.ReturnedNumbers) > 0 { + if ls.AlwaysReturns { + return true + } + } + return false +} + +func joinHasFallback(src []utils.Join) bool { + for _, ls := range src { + if ls.Src.AlwaysReturns { return true } } @@ -790,19 +799,19 @@ func getNonFallbackSelectors(n parser.PromQLExpr) (selectors []*promParser.Vecto selectors = append(selectors, selectorWithoutOffset(ls.Selector)) } } - if !sourceHasFallback(ls.Joins) { + if !joinHasFallback(ls.Joins) { for _, js := range ls.Joins { - if js.Selector != nil { - selectors = append(selectors, selectorWithoutOffset(js.Selector)) + if js.Src.Selector != nil { + selectors = append(selectors, selectorWithoutOffset(js.Src.Selector)) } } } for _, us := range ls.Unless { - if !us.IsConditional { + if !us.Src.IsConditional { continue } - if us.Selector != nil { - selectors = append(selectors, selectorWithoutOffset(us.Selector)) + if us.Src.Selector != nil { + selectors = append(selectors, selectorWithoutOffset(us.Src.Selector)) } } } diff --git a/internal/config/__snapshots__/config_test.snap b/internal/config/__snapshots__/config_test.snap index e0155f48..459396b4 100755 --- a/internal/config/__snapshots__/config_test.snap +++ b/internal/config/__snapshots__/config_test.snap @@ -1,5 +1,5 @@ -[TestGetChecksForRule/defaults - 1] +[TestGetChecksForRule/two_checks_enabled_via_config - 1] { "ci": { "baseBranch": "master", @@ -9,39 +9,38 @@ "repository": {}, "checks": { "enabled": [ - "alerts/absent", - "alerts/annotation", - "alerts/count", - "alerts/external_labels", - "alerts/for", - "alerts/template", - "labels/conflict", - "promql/aggregate", - "alerts/comparison", - "promql/fragile", - "promql/range_query", - "promql/rate", - "promql/regexp", "promql/syntax", - "promql/vector_matching", - "query/cost", - "promql/counter", - "promql/series", - "rule/dependency", - "rule/duplicate", - "rule/for", - "rule/name", - "rule/label", - "rule/link", - "rule/reject", - "rule/report" + "alerts/count" ] }, - "owners": {} + "owners": {}, + "prometheus": [ + { + "name": "prom1", + "uri": "http://localhost", + "timeout": "1s", + "uptime": "up", + "include": [ + "rules.yml" + ], + "concurrency": 16, + "rateLimit": 100, + "required": false + } + ], + "rules": [ + { + "alerts": { + "range": "1h", + "step": "1m", + "resolve": "5m" + } + } + ] } --- -[TestGetChecksForRule/single_prometheus_server - 1] +[TestGetChecksForRule/rule_with_ignore_block_/_mismatch - 1] { "ci": { "baseBranch": "master", @@ -51,50 +50,43 @@ "repository": {}, "checks": { "enabled": [ - "alerts/absent", - "alerts/annotation", - "alerts/count", - "alerts/external_labels", - "alerts/for", - "alerts/template", - "labels/conflict", - "promql/aggregate", - "alerts/comparison", - "promql/fragile", - "promql/range_query", - "promql/rate", - "promql/regexp", "promql/syntax", - "promql/vector_matching", - "query/cost", - "promql/counter", - "promql/series", - "rule/dependency", - "rule/duplicate", - "rule/for", - "rule/name", - "rule/label", - "rule/link", - "rule/reject", - "rule/report" + "alerts/count" ] }, "owners": {}, "prometheus": [ { - "name": "prom", + "name": "prom1", "uri": "http://localhost", "timeout": "1s", "uptime": "up", + "include": [ + "rules.yml" + ], "concurrency": 16, "rateLimit": 100, "required": false } + ], + "rules": [ + { + "ignore": [ + { + "path": "foo.xml" + } + ], + "alerts": { + "range": "1h", + "step": "1m", + "resolve": "5m" + } + } ] } --- -[TestGetChecksForRule/multiple_URIs - 1] +[TestGetChecksForRule/rule_with_ignore_block_/_match - 1] { "ci": { "baseBranch": "master", @@ -104,54 +96,43 @@ "repository": {}, "checks": { "enabled": [ - "alerts/absent", - "alerts/annotation", - "alerts/count", - "alerts/external_labels", - "alerts/for", - "alerts/template", - "labels/conflict", - "promql/aggregate", - "alerts/comparison", - "promql/fragile", - "promql/range_query", - "promql/rate", - "promql/regexp", "promql/syntax", - "promql/vector_matching", - "query/cost", - "promql/counter", - "promql/series", - "rule/dependency", - "rule/duplicate", - "rule/for", - "rule/name", - "rule/label", - "rule/link", - "rule/reject", - "rule/report" + "alerts/count" ] }, "owners": {}, "prometheus": [ { - "name": "prom", + "name": "prom1", "uri": "http://localhost", "timeout": "1s", "uptime": "up", - "failover": [ - "http://localhost/1", - "http://localhost/2" + "include": [ + "rules.yml" ], "concurrency": 16, "rateLimit": 100, "required": false } + ], + "rules": [ + { + "ignore": [ + { + "path": "rules.yml" + } + ], + "alerts": { + "range": "1h", + "step": "1m", + "resolve": "5m" + } + } ] } --- -[TestGetChecksForRule/two_prometheus_servers_/_disable_all_checks_via_comment - 1] +[TestGetChecksForRule/defaults - 1] { "ci": { "baseBranch": "master", @@ -170,6 +151,7 @@ "labels/conflict", "promql/aggregate", "alerts/comparison", + "promql/impossible", "promql/fragile", "promql/range_query", "promql/rate", @@ -187,33 +169,9 @@ "rule/link", "rule/reject", "rule/report" - ], - "disabled": [ - "alerts/template", - "alerts/external_labels" ] }, - "owners": {}, - "prometheus": [ - { - "name": "prom1", - "uri": "http://localhost/1", - "timeout": "1s", - "uptime": "up", - "concurrency": 16, - "rateLimit": 100, - "required": false - }, - { - "name": "prom2", - "uri": "http://localhost/2", - "timeout": "1s", - "uptime": "up", - "concurrency": 16, - "rateLimit": 100, - "required": false - } - ] + "owners": {} } --- @@ -236,6 +194,7 @@ "labels/conflict", "promql/aggregate", "alerts/comparison", + "promql/impossible", "promql/fragile", "promql/range_query", "promql/rate", @@ -292,6 +251,7 @@ "labels/conflict", "promql/aggregate", "alerts/comparison", + "promql/impossible", "promql/fragile", "promql/range_query", "promql/rate", @@ -351,6 +311,7 @@ "labels/conflict", "promql/aggregate", "alerts/comparison", + "promql/impossible", "promql/fragile", "promql/range_query", "promql/rate", @@ -388,7 +349,7 @@ } --- -[TestGetChecksForRule/single_prometheus_server_/_path_match - 1] +[TestGetChecksForRule/single_empty_rule - 1] { "ci": { "baseBranch": "master", @@ -407,6 +368,7 @@ "labels/conflict", "promql/aggregate", "alerts/comparison", + "promql/impossible", "promql/fragile", "promql/range_query", "promql/rate", @@ -427,24 +389,13 @@ ] }, "owners": {}, - "prometheus": [ - { - "name": "prom", - "uri": "http://localhost", - "timeout": "1s", - "uptime": "up", - "include": [ - "rules.yml" - ], - "concurrency": 16, - "rateLimit": 100, - "required": false - } + "rules": [ + {} ] } --- -[TestGetChecksForRule/multiple_prometheus_servers - 1] +[TestGetChecksForRule/rule_with_aggregate_checks - 1] { "ci": { "baseBranch": "master", @@ -463,6 +414,7 @@ "labels/conflict", "promql/aggregate", "alerts/comparison", + "promql/impossible", "promql/fragile", "promql/range_query", "promql/rate", @@ -483,144 +435,31 @@ ] }, "owners": {}, - "prometheus": [ - { - "name": "prom", - "uri": "http://localhost", - "timeout": "1s", - "uptime": "up", - "include": [ - "rules.yml" - ], - "concurrency": 16, - "rateLimit": 100, - "required": false - }, + "rules": [ { - "name": "ignore", - "uri": "http://localhost", - "timeout": "1s", - "uptime": "up", - "include": [ - "foo.+" - ], - "concurrency": 16, - "rateLimit": 100, - "required": false + "aggregate": [ + { + "name": ".+", + "severity": "bug", + "keep": [ + "job" + ] + }, + { + "name": ".+", + "severity": "bug", + "strip": [ + "instance", + "rack" + ] + } + ] } ] } --- -[TestGetChecksForRule/single_empty_rule - 1] -{ - "ci": { - "baseBranch": "master", - "maxCommits": 20 - }, - "parser": {}, - "repository": {}, - "checks": { - "enabled": [ - "alerts/absent", - "alerts/annotation", - "alerts/count", - "alerts/external_labels", - "alerts/for", - "alerts/template", - "labels/conflict", - "promql/aggregate", - "alerts/comparison", - "promql/fragile", - "promql/range_query", - "promql/rate", - "promql/regexp", - "promql/syntax", - "promql/vector_matching", - "query/cost", - "promql/counter", - "promql/series", - "rule/dependency", - "rule/duplicate", - "rule/for", - "rule/name", - "rule/label", - "rule/link", - "rule/reject", - "rule/report" - ] - }, - "owners": {}, - "rules": [ - {} - ] -} ---- - -[TestGetChecksForRule/rule_with_aggregate_checks - 1] -{ - "ci": { - "baseBranch": "master", - "maxCommits": 20 - }, - "parser": {}, - "repository": {}, - "checks": { - "enabled": [ - "alerts/absent", - "alerts/annotation", - "alerts/count", - "alerts/external_labels", - "alerts/for", - "alerts/template", - "labels/conflict", - "promql/aggregate", - "alerts/comparison", - "promql/fragile", - "promql/range_query", - "promql/rate", - "promql/regexp", - "promql/syntax", - "promql/vector_matching", - "query/cost", - "promql/counter", - "promql/series", - "rule/dependency", - "rule/duplicate", - "rule/for", - "rule/name", - "rule/label", - "rule/link", - "rule/reject", - "rule/report" - ] - }, - "owners": {}, - "rules": [ - { - "aggregate": [ - { - "name": ".+", - "severity": "bug", - "keep": [ - "job" - ] - }, - { - "name": ".+", - "severity": "bug", - "strip": [ - "instance", - "rack" - ] - } - ] - } - ] -} ---- - -[TestGetChecksForRule/multiple_checks_and_disable_comment - 1] +[TestGetChecksForRule/multiple_checks_and_disable_comment - 1] { "ci": { "baseBranch": "master", @@ -639,6 +478,7 @@ "labels/conflict", "promql/aggregate", "alerts/comparison", + "promql/impossible", "promql/fragile", "promql/range_query", "promql/rate", @@ -703,6 +543,7 @@ "labels/conflict", "promql/aggregate", "alerts/comparison", + "promql/impossible", "promql/fragile", "promql/range_query", "promql/rate", @@ -735,79 +576,6 @@ } --- -[TestGetChecksForRule/prometheus_check_with_prometheus_servers_and_disable_comment - 1] -{ - "ci": { - "baseBranch": "master", - "maxCommits": 20 - }, - "parser": {}, - "repository": {}, - "checks": { - "enabled": [ - "alerts/absent", - "alerts/annotation", - "alerts/count", - "alerts/external_labels", - "alerts/for", - "alerts/template", - "labels/conflict", - "promql/aggregate", - "alerts/comparison", - "promql/fragile", - "promql/range_query", - "promql/rate", - "promql/regexp", - "promql/syntax", - "promql/vector_matching", - "query/cost", - "promql/counter", - "promql/series", - "rule/dependency", - "rule/duplicate", - "rule/for", - "rule/name", - "rule/label", - "rule/link", - "rule/reject", - "rule/report" - ] - }, - "owners": {}, - "prometheus": [ - { - "name": "prom1", - "uri": "http://localhost", - "timeout": "1s", - "uptime": "up", - "include": [ - "rules.yml" - ], - "concurrency": 16, - "rateLimit": 100, - "required": false - }, - { - "name": "prom2", - "uri": "http://localhost", - "timeout": "1s", - "uptime": "up", - "include": [ - "rules.yml" - ], - "concurrency": 16, - "rateLimit": 100, - "required": false - } - ], - "rules": [ - { - "cost": {} - } - ] -} ---- - [TestGetChecksForRule/duplicated_rules - 1] { "ci": { @@ -827,6 +595,7 @@ "labels/conflict", "promql/aggregate", "alerts/comparison", + "promql/impossible", "promql/fragile", "promql/range_query", "promql/rate", @@ -908,94 +677,6 @@ } --- -[TestGetChecksForRule/multiple_cost_checks - 1] -{ - "ci": { - "baseBranch": "master", - "maxCommits": 20 - }, - "parser": {}, - "repository": {}, - "checks": { - "enabled": [ - "alerts/absent", - "alerts/annotation", - "alerts/count", - "alerts/external_labels", - "alerts/for", - "alerts/template", - "labels/conflict", - "promql/aggregate", - "alerts/comparison", - "promql/fragile", - "promql/range_query", - "promql/rate", - "promql/regexp", - "promql/syntax", - "promql/vector_matching", - "query/cost", - "promql/counter", - "promql/series", - "rule/dependency", - "rule/duplicate", - "rule/for", - "rule/name", - "rule/label", - "rule/link", - "rule/reject", - "rule/report" - ] - }, - "owners": {}, - "prometheus": [ - { - "name": "prom1", - "uri": "http://localhost", - "timeout": "1s", - "uptime": "up", - "include": [ - "rules.yml" - ], - "concurrency": 16, - "rateLimit": 100, - "required": false - }, - { - "name": "prom2", - "uri": "http://localhost", - "timeout": "1s", - "uptime": "up", - "include": [ - "rules.yml" - ], - "concurrency": 16, - "rateLimit": 100, - "required": false - } - ], - "rules": [ - { - "cost": { - "comment": "this is rule comment", - "severity": "info" - } - }, - { - "cost": { - "severity": "warning", - "maxSeries": 10000 - } - }, - { - "cost": { - "severity": "bug", - "maxSeries": 20000 - } - } - ] -} ---- - [TestGetChecksForRule/reject_rules - 1] { "ci": { @@ -1015,6 +696,7 @@ "labels/conflict", "promql/aggregate", "alerts/comparison", + "promql/impossible", "promql/fragile", "promql/range_query", "promql/rate", @@ -1079,6 +761,7 @@ "labels/conflict", "promql/aggregate", "alerts/comparison", + "promql/impossible", "promql/fragile", "promql/range_query", "promql/rate", @@ -1142,6 +825,7 @@ "labels/conflict", "promql/aggregate", "alerts/comparison", + "promql/impossible", "promql/fragile", "promql/range_query", "promql/rate", @@ -1205,6 +889,7 @@ "labels/conflict", "promql/aggregate", "alerts/comparison", + "promql/impossible", "promql/fragile", "promql/range_query", "promql/rate", @@ -1268,6 +953,7 @@ "labels/conflict", "promql/aggregate", "alerts/comparison", + "promql/impossible", "promql/fragile", "promql/range_query", "promql/rate", @@ -1331,6 +1017,7 @@ "labels/conflict", "promql/aggregate", "alerts/comparison", + "promql/impossible", "promql/fragile", "promql/range_query", "promql/rate", @@ -1394,6 +1081,7 @@ "labels/conflict", "promql/aggregate", "alerts/comparison", + "promql/impossible", "promql/fragile", "promql/range_query", "promql/rate", @@ -1457,6 +1145,7 @@ "labels/conflict", "promql/aggregate", "alerts/comparison", + "promql/impossible", "promql/fragile", "promql/range_query", "promql/rate", @@ -1503,250 +1192,7 @@ } --- -[TestGetChecksForRule/checks_disabled_via_config - 1] -{ - "ci": { - "baseBranch": "master", - "maxCommits": 20 - }, - "parser": {}, - "repository": {}, - "checks": { - "enabled": [ - "alerts/absent", - "alerts/annotation", - "alerts/count", - "alerts/external_labels", - "alerts/for", - "alerts/template", - "labels/conflict", - "promql/aggregate", - "alerts/comparison", - "promql/fragile", - "promql/range_query", - "promql/rate", - "promql/regexp", - "promql/syntax", - "promql/vector_matching", - "query/cost", - "promql/counter", - "promql/series", - "rule/dependency", - "rule/duplicate", - "rule/for", - "rule/name", - "rule/label", - "rule/link", - "rule/reject", - "rule/report" - ], - "disabled": [ - "promql/counter", - "promql/rate", - "promql/vector_matching", - "promql/range_query", - "rule/duplicate", - "labels/conflict", - "alerts/absent" - ] - }, - "owners": {}, - "prometheus": [ - { - "name": "prom1", - "uri": "http://localhost", - "timeout": "1s", - "uptime": "up", - "include": [ - "rules.yml" - ], - "concurrency": 16, - "rateLimit": 100, - "required": false - } - ], - "rules": [ - { - "alerts": { - "range": "1h", - "step": "1m", - "resolve": "5m" - } - } - ] -} ---- - -[TestGetChecksForRule/single_check_enabled_via_config - 1] -{ - "ci": { - "baseBranch": "master", - "maxCommits": 20 - }, - "parser": {}, - "repository": {}, - "checks": {}, - "owners": {}, - "prometheus": [ - { - "name": "prom1", - "uri": "http://localhost", - "timeout": "1s", - "uptime": "up", - "include": [ - "rules.yml" - ], - "concurrency": 16, - "rateLimit": 100, - "required": false - } - ], - "rules": [ - { - "alerts": { - "range": "1h", - "step": "1m", - "resolve": "5m" - } - } - ] -} ---- - -[TestGetChecksForRule/two_checks_enabled_via_config - 1] -{ - "ci": { - "baseBranch": "master", - "maxCommits": 20 - }, - "parser": {}, - "repository": {}, - "checks": { - "enabled": [ - "promql/syntax", - "alerts/count" - ] - }, - "owners": {}, - "prometheus": [ - { - "name": "prom1", - "uri": "http://localhost", - "timeout": "1s", - "uptime": "up", - "include": [ - "rules.yml" - ], - "concurrency": 16, - "rateLimit": 100, - "required": false - } - ], - "rules": [ - { - "alerts": { - "range": "1h", - "step": "1m", - "resolve": "5m" - } - } - ] -} ---- - -[TestGetChecksForRule/rule_with_ignore_block_/_mismatch - 1] -{ - "ci": { - "baseBranch": "master", - "maxCommits": 20 - }, - "parser": {}, - "repository": {}, - "checks": { - "enabled": [ - "promql/syntax", - "alerts/count" - ] - }, - "owners": {}, - "prometheus": [ - { - "name": "prom1", - "uri": "http://localhost", - "timeout": "1s", - "uptime": "up", - "include": [ - "rules.yml" - ], - "concurrency": 16, - "rateLimit": 100, - "required": false - } - ], - "rules": [ - { - "ignore": [ - { - "path": "foo.xml" - } - ], - "alerts": { - "range": "1h", - "step": "1m", - "resolve": "5m" - } - } - ] -} ---- - -[TestGetChecksForRule/rule_with_ignore_block_/_match - 1] -{ - "ci": { - "baseBranch": "master", - "maxCommits": 20 - }, - "parser": {}, - "repository": {}, - "checks": { - "enabled": [ - "promql/syntax", - "alerts/count" - ] - }, - "owners": {}, - "prometheus": [ - { - "name": "prom1", - "uri": "http://localhost", - "timeout": "1s", - "uptime": "up", - "include": [ - "rules.yml" - ], - "concurrency": 16, - "rateLimit": 100, - "required": false - } - ], - "rules": [ - { - "ignore": [ - { - "path": "rules.yml" - } - ], - "alerts": { - "range": "1h", - "step": "1m", - "resolve": "5m" - } - } - ] -} ---- - -[TestGetChecksForRule/for_match_/_passing - 1] +[TestGetChecksForRule/for_match_/_passing - 1] { "ci": { "baseBranch": "master", @@ -1765,6 +1211,7 @@ "labels/conflict", "promql/aggregate", "alerts/comparison", + "promql/impossible", "promql/fragile", "promql/range_query", "promql/rate", @@ -1822,6 +1269,7 @@ "labels/conflict", "promql/aggregate", "alerts/comparison", + "promql/impossible", "promql/fragile", "promql/range_query", "promql/rate", @@ -1880,6 +1328,7 @@ "labels/conflict", "promql/aggregate", "alerts/comparison", + "promql/impossible", "promql/fragile", "promql/range_query", "promql/rate", @@ -1937,6 +1386,7 @@ "labels/conflict", "promql/aggregate", "alerts/comparison", + "promql/impossible", "promql/fragile", "promql/range_query", "promql/rate", @@ -1994,6 +1444,7 @@ "labels/conflict", "promql/aggregate", "alerts/comparison", + "promql/impossible", "promql/fragile", "promql/range_query", "promql/rate", @@ -2051,6 +1502,7 @@ "labels/conflict", "promql/aggregate", "alerts/comparison", + "promql/impossible", "promql/fragile", "promql/range_query", "promql/rate", @@ -2108,6 +1560,7 @@ "labels/conflict", "promql/aggregate", "alerts/comparison", + "promql/impossible", "promql/fragile", "promql/range_query", "promql/rate", @@ -2165,6 +1618,7 @@ "labels/conflict", "promql/aggregate", "alerts/comparison", + "promql/impossible", "promql/fragile", "promql/range_query", "promql/rate", @@ -2223,6 +1677,7 @@ "labels/conflict", "promql/aggregate", "alerts/comparison", + "promql/impossible", "promql/fragile", "promql/range_query", "promql/rate", @@ -2280,6 +1735,7 @@ "labels/conflict", "promql/aggregate", "alerts/comparison", + "promql/impossible", "promql/fragile", "promql/range_query", "promql/rate", @@ -2338,6 +1794,7 @@ "labels/conflict", "promql/aggregate", "alerts/comparison", + "promql/impossible", "promql/fragile", "promql/range_query", "promql/rate", @@ -2370,7 +1827,7 @@ } --- -[TestGetChecksForRule/two_prometheus_servers_/_disable_checks_via_file/disable_comment - 1] +[TestGetChecksForRule/custom_range_query - 1] { "ci": { "baseBranch": "master", @@ -2389,6 +1846,7 @@ "labels/conflict", "promql/aggregate", "alerts/comparison", + "promql/impossible", "promql/fragile", "promql/range_query", "promql/rate", @@ -2406,29 +1864,596 @@ "rule/link", "rule/reject", "rule/report" - ], - "disabled": [ - "alerts/template", - "alerts/external_labels", - "alerts/absent" ] }, "owners": {}, - "prometheus": [ + "rules": [ { - "name": "prom1", - "uri": "http://localhost/1", - "timeout": "1s", - "uptime": "up", - "concurrency": 16, - "rateLimit": 100, - "required": false + "range_query": { + "max": "1h", + "severity": "bug" + } + } + ] +} +--- + +[TestGetChecksForRule/state_mismatch - 1] +{ + "ci": { + "baseBranch": "master", + "maxCommits": 20 + }, + "parser": {}, + "repository": {}, + "checks": { + "enabled": [ + "alerts/absent", + "alerts/annotation", + "alerts/count", + "alerts/external_labels", + "alerts/for", + "alerts/template", + "labels/conflict", + "promql/aggregate", + "alerts/comparison", + "promql/impossible", + "promql/fragile", + "promql/range_query", + "promql/rate", + "promql/regexp", + "promql/syntax", + "promql/vector_matching", + "query/cost", + "promql/counter", + "promql/series", + "rule/dependency", + "rule/duplicate", + "rule/for", + "rule/name", + "rule/label", + "rule/link", + "rule/reject", + "rule/report" + ] + }, + "owners": {}, + "rules": [ + { + "match": [ + { + "state": [ + "renamed" + ] + } + ], + "aggregate": [ + { + "name": ".+", + "severity": "bug", + "keep": [ + "job" + ] + } + ] + }, + { + "ignore": [ + { + "state": [ + "modified" + ] + } + ], + "aggregate": [ + { + "name": ".+", + "severity": "bug", + "strip": [ + "instance", + "rack" + ] + } + ] + } + ] +} +--- + +[TestGetChecksForRule/state_match - 1] +{ + "ci": { + "baseBranch": "master", + "maxCommits": 20 + }, + "parser": {}, + "repository": {}, + "checks": { + "enabled": [ + "alerts/absent", + "alerts/annotation", + "alerts/count", + "alerts/external_labels", + "alerts/for", + "alerts/template", + "labels/conflict", + "promql/aggregate", + "alerts/comparison", + "promql/impossible", + "promql/fragile", + "promql/range_query", + "promql/rate", + "promql/regexp", + "promql/syntax", + "promql/vector_matching", + "query/cost", + "promql/counter", + "promql/series", + "rule/dependency", + "rule/duplicate", + "rule/for", + "rule/name", + "rule/label", + "rule/link", + "rule/reject", + "rule/report" + ] + }, + "owners": {}, + "rules": [ + { + "match": [ + { + "state": [ + "renamed" + ] + } + ], + "aggregate": [ + { + "name": ".+", + "severity": "bug", + "keep": [ + "job" + ] + } + ] + }, + { + "ignore": [ + { + "state": [ + "modified" + ] + } + ], + "aggregate": [ + { + "name": ".+", + "severity": "bug", + "strip": [ + "instance", + "rack" + ] + } + ] + } + ] +} +--- + +[TestGetChecksForRule/check_enabled_globally_but_disabled_via_rule{} - 1] +{ + "ci": { + "baseBranch": "master", + "maxCommits": 20 + }, + "parser": {}, + "repository": {}, + "checks": { + "enabled": [ + "alerts/absent", + "alerts/annotation", + "alerts/count", + "alerts/external_labels", + "alerts/for", + "alerts/template", + "labels/conflict", + "promql/aggregate", + "alerts/comparison", + "promql/impossible", + "promql/fragile", + "promql/range_query", + "promql/rate", + "promql/regexp", + "promql/syntax", + "promql/vector_matching", + "query/cost", + "promql/counter", + "promql/series", + "rule/dependency", + "rule/duplicate", + "rule/for", + "rule/name", + "rule/label", + "rule/link", + "rule/reject", + "rule/report" + ] + }, + "owners": {}, + "rules": [ + { + "match": [ + { + "kind": "recording" + } + ], + "disable": [ + "rule/duplicate" + ] + } + ] +} +--- + +[TestGetChecksForRule/reject_rules#01 - 1] +{ + "ci": { + "baseBranch": "master", + "maxCommits": 20 + }, + "parser": {}, + "repository": {}, + "checks": { + "enabled": [ + "alerts/absent", + "alerts/annotation", + "alerts/count", + "alerts/external_labels", + "alerts/for", + "alerts/template", + "labels/conflict", + "promql/aggregate", + "alerts/comparison", + "promql/impossible", + "promql/fragile", + "promql/range_query", + "promql/rate", + "promql/regexp", + "promql/syntax", + "promql/vector_matching", + "query/cost", + "promql/counter", + "promql/series", + "rule/dependency", + "rule/duplicate", + "rule/for", + "rule/name", + "rule/label", + "rule/link", + "rule/reject", + "rule/report" + ] + }, + "owners": {}, + "rules": [ + { + "match": [ + { + "kind": "recording" + } + ], + "report": { + "comment": "You cannot add any recording rules to this Prometheus server.", + "severity": "bug" + } + } + ] +} +--- + +[TestGetChecksForRule/multiple_checks_and_disable_comment_/_locked_rule - 1] +{ + "ci": { + "baseBranch": "master", + "maxCommits": 20 + }, + "parser": {}, + "repository": {}, + "checks": { + "enabled": [ + "alerts/absent", + "alerts/annotation", + "alerts/count", + "alerts/external_labels", + "alerts/for", + "alerts/template", + "labels/conflict", + "promql/aggregate", + "alerts/comparison", + "promql/impossible", + "promql/fragile", + "promql/range_query", + "promql/rate", + "promql/regexp", + "promql/syntax", + "promql/vector_matching", + "query/cost", + "promql/counter", + "promql/series", + "rule/dependency", + "rule/duplicate", + "rule/for", + "rule/name", + "rule/label", + "rule/link", + "rule/reject", + "rule/report" + ] + }, + "owners": {}, + "rules": [ + { + "aggregate": [ + { + "name": ".+", + "severity": "bug", + "keep": [ + "job" + ] + } + ] + }, + { + "aggregate": [ + { + "name": ".+", + "comment": "this is rule comment", + "severity": "bug", + "strip": [ + "instance", + "rack" + ] + } + ], + "locked": true + } + ] +} +--- + +[TestGetChecksForRule/multiple_checks_and_snooze_comment_/_locked_rule - 1] +{ + "ci": { + "baseBranch": "master", + "maxCommits": 20 + }, + "parser": {}, + "repository": {}, + "checks": { + "enabled": [ + "alerts/absent", + "alerts/annotation", + "alerts/count", + "alerts/external_labels", + "alerts/for", + "alerts/template", + "labels/conflict", + "promql/aggregate", + "alerts/comparison", + "promql/impossible", + "promql/fragile", + "promql/range_query", + "promql/rate", + "promql/regexp", + "promql/syntax", + "promql/vector_matching", + "query/cost", + "promql/counter", + "promql/series", + "rule/dependency", + "rule/duplicate", + "rule/for", + "rule/name", + "rule/label", + "rule/link", + "rule/reject", + "rule/report" + ] + }, + "owners": {}, + "rules": [ + { + "aggregate": [ + { + "name": ".+", + "severity": "bug", + "keep": [ + "job" + ] + } + ] }, { - "name": "prom2", - "uri": "http://localhost/2", + "aggregate": [ + { + "name": ".+", + "comment": "this is rule comment", + "severity": "bug", + "strip": [ + "instance", + "rack" + ] + } + ], + "locked": true + } + ] +} +--- + +[TestGetChecksForRule/single_prometheus_server - 1] +{ + "ci": { + "baseBranch": "master", + "maxCommits": 20 + }, + "parser": {}, + "repository": {}, + "checks": { + "enabled": [ + "alerts/absent", + "alerts/annotation", + "alerts/count", + "alerts/external_labels", + "alerts/for", + "alerts/template", + "labels/conflict", + "promql/aggregate", + "alerts/comparison", + "promql/impossible", + "promql/fragile", + "promql/range_query", + "promql/rate", + "promql/regexp", + "promql/syntax", + "promql/vector_matching", + "query/cost", + "promql/counter", + "promql/series", + "rule/dependency", + "rule/duplicate", + "rule/for", + "rule/name", + "rule/label", + "rule/link", + "rule/reject", + "rule/report" + ] + }, + "owners": {}, + "prometheus": [ + { + "name": "prom", + "uri": "http://localhost", + "timeout": "1s", + "uptime": "up", + "concurrency": 16, + "rateLimit": 100, + "required": false + } + ] +} +--- + +[TestGetChecksForRule/multiple_URIs - 1] +{ + "ci": { + "baseBranch": "master", + "maxCommits": 20 + }, + "parser": {}, + "repository": {}, + "checks": { + "enabled": [ + "alerts/absent", + "alerts/annotation", + "alerts/count", + "alerts/external_labels", + "alerts/for", + "alerts/template", + "labels/conflict", + "promql/aggregate", + "alerts/comparison", + "promql/impossible", + "promql/fragile", + "promql/range_query", + "promql/rate", + "promql/regexp", + "promql/syntax", + "promql/vector_matching", + "query/cost", + "promql/counter", + "promql/series", + "rule/dependency", + "rule/duplicate", + "rule/for", + "rule/name", + "rule/label", + "rule/link", + "rule/reject", + "rule/report" + ] + }, + "owners": {}, + "prometheus": [ + { + "name": "prom", + "uri": "http://localhost", + "timeout": "1s", + "uptime": "up", + "failover": [ + "http://localhost/1", + "http://localhost/2" + ], + "concurrency": 16, + "rateLimit": 100, + "required": false + } + ] +} +--- + +[TestGetChecksForRule/single_prometheus_server_/_path_match - 1] +{ + "ci": { + "baseBranch": "master", + "maxCommits": 20 + }, + "parser": {}, + "repository": {}, + "checks": { + "enabled": [ + "alerts/absent", + "alerts/annotation", + "alerts/count", + "alerts/external_labels", + "alerts/for", + "alerts/template", + "labels/conflict", + "promql/aggregate", + "alerts/comparison", + "promql/impossible", + "promql/fragile", + "promql/range_query", + "promql/rate", + "promql/regexp", + "promql/syntax", + "promql/vector_matching", + "query/cost", + "promql/counter", + "promql/series", + "rule/dependency", + "rule/duplicate", + "rule/for", + "rule/name", + "rule/label", + "rule/link", + "rule/reject", + "rule/report" + ] + }, + "owners": {}, + "prometheus": [ + { + "name": "prom", + "uri": "http://localhost", "timeout": "1s", "uptime": "up", + "include": [ + "rules.yml" + ], "concurrency": 16, "rateLimit": 100, "required": false @@ -2437,7 +2462,7 @@ } --- -[TestGetChecksForRule/two_prometheus_servers_/_snoozed_checks_via_comment - 1] +[TestGetChecksForRule/multiple_prometheus_servers - 1] { "ci": { "baseBranch": "master", @@ -2456,6 +2481,7 @@ "labels/conflict", "promql/aggregate", "alerts/comparison", + "promql/impossible", "promql/fragile", "promql/range_query", "promql/rate", @@ -2473,28 +2499,30 @@ "rule/link", "rule/reject", "rule/report" - ], - "disabled": [ - "alerts/template", - "promql/regexp" ] }, "owners": {}, "prometheus": [ { - "name": "prom1", - "uri": "http://localhost/1", + "name": "prom", + "uri": "http://localhost", "timeout": "1s", "uptime": "up", + "include": [ + "rules.yml" + ], "concurrency": 16, "rateLimit": 100, "required": false }, { - "name": "prom2", - "uri": "http://localhost/2", + "name": "ignore", + "uri": "http://localhost", "timeout": "1s", "uptime": "up", + "include": [ + "foo.+" + ], "concurrency": 16, "rateLimit": 100, "required": false @@ -2503,7 +2531,7 @@ } --- -[TestGetChecksForRule/two_prometheus_servers_/_expired_snooze - 1] +[TestGetChecksForRule/prometheus_check_with_prometheus_servers_and_disable_comment - 1] { "ci": { "baseBranch": "master", @@ -2522,6 +2550,7 @@ "labels/conflict", "promql/aggregate", "alerts/comparison", + "promql/impossible", "promql/fragile", "promql/range_query", "promql/rate", @@ -2539,32 +2568,39 @@ "rule/link", "rule/reject", "rule/report" - ], - "disabled": [ - "alerts/template", - "promql/regexp" ] }, "owners": {}, "prometheus": [ { "name": "prom1", - "uri": "http://localhost/1", + "uri": "http://localhost", "timeout": "1s", "uptime": "up", + "include": [ + "rules.yml" + ], "concurrency": 16, "rateLimit": 100, "required": false }, { "name": "prom2", - "uri": "http://localhost/2", + "uri": "http://localhost", "timeout": "1s", "uptime": "up", + "include": [ + "rules.yml" + ], "concurrency": 16, "rateLimit": 100, "required": false } + ], + "rules": [ + { + "cost": {} + } ] } --- @@ -2588,6 +2624,7 @@ "labels/conflict", "promql/aggregate", "alerts/comparison", + "promql/impossible", "promql/fragile", "promql/range_query", "promql/rate", @@ -2667,6 +2704,7 @@ "labels/conflict", "promql/aggregate", "alerts/comparison", + "promql/impossible", "promql/fragile", "promql/range_query", "promql/rate", @@ -2746,6 +2784,7 @@ "labels/conflict", "promql/aggregate", "alerts/comparison", + "promql/impossible", "promql/fragile", "promql/range_query", "promql/rate", @@ -2808,6 +2847,7 @@ "labels/conflict", "promql/aggregate", "alerts/comparison", + "promql/impossible", "promql/fragile", "promql/range_query", "promql/rate", @@ -2854,7 +2894,7 @@ } --- -[TestGetChecksForRule/custom_range_query - 1] +[TestGetChecksForRule/two_prometheus_servers_/_disable_all_checks_via_comment - 1] { "ci": { "baseBranch": "master", @@ -2873,6 +2913,7 @@ "labels/conflict", "promql/aggregate", "alerts/comparison", + "promql/impossible", "promql/fragile", "promql/range_query", "promql/rate", @@ -2890,21 +2931,37 @@ "rule/link", "rule/reject", "rule/report" + ], + "disabled": [ + "alerts/template", + "alerts/external_labels" ] }, "owners": {}, - "rules": [ + "prometheus": [ { - "range_query": { - "max": "1h", - "severity": "bug" - } + "name": "prom1", + "uri": "http://localhost/1", + "timeout": "1s", + "uptime": "up", + "concurrency": 16, + "rateLimit": 100, + "required": false + }, + { + "name": "prom2", + "uri": "http://localhost/2", + "timeout": "1s", + "uptime": "up", + "concurrency": 16, + "rateLimit": 100, + "required": false } ] } --- -[TestGetChecksForRule/state_mismatch - 1] +[TestGetChecksForRule/multiple_cost_checks - 1] { "ci": { "baseBranch": "master", @@ -2923,6 +2980,7 @@ "labels/conflict", "promql/aggregate", "alerts/comparison", + "promql/impossible", "promql/fragile", "promql/range_query", "promql/rate", @@ -2943,130 +3001,56 @@ ] }, "owners": {}, - "rules": [ + "prometheus": [ { - "match": [ - { - "state": [ - "renamed" - ] - } + "name": "prom1", + "uri": "http://localhost", + "timeout": "1s", + "uptime": "up", + "include": [ + "rules.yml" ], - "aggregate": [ - { - "name": ".+", - "severity": "bug", - "keep": [ - "job" - ] - } - ] + "concurrency": 16, + "rateLimit": 100, + "required": false }, { - "ignore": [ - { - "state": [ - "modified" - ] - } + "name": "prom2", + "uri": "http://localhost", + "timeout": "1s", + "uptime": "up", + "include": [ + "rules.yml" ], - "aggregate": [ - { - "name": ".+", - "severity": "bug", - "strip": [ - "instance", - "rack" - ] - } - ] + "concurrency": 16, + "rateLimit": 100, + "required": false } - ] -} ---- - -[TestGetChecksForRule/state_match - 1] -{ - "ci": { - "baseBranch": "master", - "maxCommits": 20 - }, - "parser": {}, - "repository": {}, - "checks": { - "enabled": [ - "alerts/absent", - "alerts/annotation", - "alerts/count", - "alerts/external_labels", - "alerts/for", - "alerts/template", - "labels/conflict", - "promql/aggregate", - "alerts/comparison", - "promql/fragile", - "promql/range_query", - "promql/rate", - "promql/regexp", - "promql/syntax", - "promql/vector_matching", - "query/cost", - "promql/counter", - "promql/series", - "rule/dependency", - "rule/duplicate", - "rule/for", - "rule/name", - "rule/label", - "rule/link", - "rule/reject", - "rule/report" - ] - }, - "owners": {}, + ], "rules": [ { - "match": [ - { - "state": [ - "renamed" - ] - } - ], - "aggregate": [ - { - "name": ".+", - "severity": "bug", - "keep": [ - "job" - ] - } - ] + "cost": { + "comment": "this is rule comment", + "severity": "info" + } }, { - "ignore": [ - { - "state": [ - "modified" - ] - } - ], - "aggregate": [ - { - "name": ".+", - "severity": "bug", - "strip": [ - "instance", - "rack" - ] - } - ] + "cost": { + "severity": "warning", + "maxSeries": 10000 + } + }, + { + "cost": { + "severity": "bug", + "maxSeries": 20000 + } } ] } --- -[TestGetChecksForRule/check_disabled_globally_but_enabled_via_rule{} - 1] +[TestGetChecksForRule/checks_disabled_via_config - 1] { "ci": { "baseBranch": "master", @@ -3085,6 +3069,7 @@ "labels/conflict", "promql/aggregate", "alerts/comparison", + "promql/impossible", "promql/fragile", "promql/range_query", "promql/rate", @@ -3104,21 +3089,25 @@ "rule/report" ], "disabled": [ - "alerts/template", - "alerts/external_labels", + "promql/counter", + "promql/rate", + "promql/vector_matching", + "promql/range_query", "rule/duplicate", - "alerts/absent", - "promql/series", - "promql/vector_matching" + "labels/conflict", + "alerts/absent" ] }, "owners": {}, "prometheus": [ { - "name": "prom", + "name": "prom1", "uri": "http://localhost", "timeout": "1s", "uptime": "up", + "include": [ + "rules.yml" + ], "concurrency": 16, "rateLimit": 100, "required": false @@ -3126,30 +3115,53 @@ ], "rules": [ { - "disable": [ - "rule/duplicate" - ] - }, + "alerts": { + "range": "1h", + "step": "1m", + "resolve": "5m" + } + } + ] +} +--- + +[TestGetChecksForRule/single_check_enabled_via_config - 1] +{ + "ci": { + "baseBranch": "master", + "maxCommits": 20 + }, + "parser": {}, + "repository": {}, + "checks": {}, + "owners": {}, + "prometheus": [ { - "match": [ - { - "kind": "alerting" - } + "name": "prom1", + "uri": "http://localhost", + "timeout": "1s", + "uptime": "up", + "include": [ + "rules.yml" ], - "disable": [ - "promql/series" - ] - }, + "concurrency": 16, + "rateLimit": 100, + "required": false + } + ], + "rules": [ { - "enable": [ - "promql/series" - ] + "alerts": { + "range": "1h", + "step": "1m", + "resolve": "5m" + } } ] } --- -[TestGetChecksForRule/check_enabled_globally_but_disabled_via_rule{} - 1] +[TestGetChecksForRule/check_disabled_globally_but_enabled_via_rule{} - 1] { "ci": { "baseBranch": "master", @@ -3168,6 +3180,7 @@ "labels/conflict", "promql/aggregate", "alerts/comparison", + "promql/impossible", "promql/fragile", "promql/range_query", "promql/rate", @@ -3185,25 +3198,54 @@ "rule/link", "rule/reject", "rule/report" + ], + "disabled": [ + "alerts/template", + "alerts/external_labels", + "rule/duplicate", + "alerts/absent", + "promql/series", + "promql/vector_matching" ] }, "owners": {}, + "prometheus": [ + { + "name": "prom", + "uri": "http://localhost", + "timeout": "1s", + "uptime": "up", + "concurrency": 16, + "rateLimit": 100, + "required": false + } + ], "rules": [ + { + "disable": [ + "rule/duplicate" + ] + }, { "match": [ { - "kind": "recording" + "kind": "alerting" } ], "disable": [ - "rule/duplicate" + "promql/series" + ] + }, + { + "enable": [ + "promql/series" ] } ] } --- -[TestGetChecksForRule/two_prometheus_servers_/_check_disable_via_rule_{} - 1] +[TestGetChecksForRule/two_prometheus_servers_/_expired_snooze - 1] { "ci": { "baseBranch": "master", @@ -3222,6 +3264,7 @@ "labels/conflict", "promql/aggregate", "alerts/comparison", + "promql/impossible", "promql/fragile", "promql/range_query", "promql/rate", @@ -3265,27 +3308,11 @@ "rateLimit": 100, "required": false } - ], - "rules": [ - { - "match": [ - { - "path": "rules.yml" - } - ], - "disable": [ - "promql/series", - "promql/range_query", - "rule/duplicate", - "promql/vector_matching", - "promql/counter" - ] - } ] } --- -[TestGetChecksForRule/reject_rules#01 - 1] +[TestGetChecksForRule/two_prometheus_servers_/_snoozed_checks_via_comment - 1] { "ci": { "baseBranch": "master", @@ -3304,6 +3331,7 @@ "labels/conflict", "promql/aggregate", "alerts/comparison", + "promql/impossible", "promql/fragile", "promql/range_query", "promql/rate", @@ -3321,26 +3349,37 @@ "rule/link", "rule/reject", "rule/report" + ], + "disabled": [ + "alerts/template", + "promql/regexp" ] }, "owners": {}, - "rules": [ + "prometheus": [ { - "match": [ - { - "kind": "recording" - } - ], - "report": { - "comment": "You cannot add any recording rules to this Prometheus server.", - "severity": "bug" - } + "name": "prom1", + "uri": "http://localhost/1", + "timeout": "1s", + "uptime": "up", + "concurrency": 16, + "rateLimit": 100, + "required": false + }, + { + "name": "prom2", + "uri": "http://localhost/2", + "timeout": "1s", + "uptime": "up", + "concurrency": 16, + "rateLimit": 100, + "required": false } ] } --- -[TestGetChecksForRule/multiple_checks_and_disable_comment_/_locked_rule - 1] +[TestGetChecksForRule/two_prometheus_servers_/_disable_checks_via_file/disable_comment - 1] { "ci": { "baseBranch": "master", @@ -3359,6 +3398,7 @@ "labels/conflict", "promql/aggregate", "alerts/comparison", + "promql/impossible", "promql/fragile", "promql/range_query", "promql/rate", @@ -3376,40 +3416,38 @@ "rule/link", "rule/reject", "rule/report" + ], + "disabled": [ + "alerts/template", + "alerts/external_labels", + "alerts/absent" ] }, "owners": {}, - "rules": [ + "prometheus": [ { - "aggregate": [ - { - "name": ".+", - "severity": "bug", - "keep": [ - "job" - ] - } - ] + "name": "prom1", + "uri": "http://localhost/1", + "timeout": "1s", + "uptime": "up", + "concurrency": 16, + "rateLimit": 100, + "required": false }, { - "aggregate": [ - { - "name": ".+", - "comment": "this is rule comment", - "severity": "bug", - "strip": [ - "instance", - "rack" - ] - } - ], - "locked": true + "name": "prom2", + "uri": "http://localhost/2", + "timeout": "1s", + "uptime": "up", + "concurrency": 16, + "rateLimit": 100, + "required": false } ] } --- -[TestGetChecksForRule/multiple_checks_and_snooze_comment_/_locked_rule - 1] +[TestGetChecksForRule/two_prometheus_servers_/_check_disable_via_rule_{} - 1] { "ci": { "baseBranch": "master", @@ -3428,6 +3466,7 @@ "labels/conflict", "promql/aggregate", "alerts/comparison", + "promql/impossible", "promql/fragile", "promql/range_query", "promql/rate", @@ -3445,34 +3484,47 @@ "rule/link", "rule/reject", "rule/report" + ], + "disabled": [ + "alerts/template", + "promql/regexp" ] }, "owners": {}, - "rules": [ + "prometheus": [ { - "aggregate": [ - { - "name": ".+", - "severity": "bug", - "keep": [ - "job" - ] - } - ] + "name": "prom1", + "uri": "http://localhost/1", + "timeout": "1s", + "uptime": "up", + "concurrency": 16, + "rateLimit": 100, + "required": false }, { - "aggregate": [ + "name": "prom2", + "uri": "http://localhost/2", + "timeout": "1s", + "uptime": "up", + "concurrency": 16, + "rateLimit": 100, + "required": false + } + ], + "rules": [ + { + "match": [ { - "name": ".+", - "comment": "this is rule comment", - "severity": "bug", - "strip": [ - "instance", - "rack" - ] + "path": "rules.yml" } ], - "locked": true + "disable": [ + "promql/series", + "promql/range_query", + "rule/duplicate", + "promql/vector_matching", + "promql/counter" + ] } ] } diff --git a/internal/config/config_test.go b/internal/config/config_test.go index b8495202..d75ba8ec 100644 --- a/internal/config/config_test.go +++ b/internal/config/config_test.go @@ -196,6 +196,7 @@ func TestGetChecksForRule(t *testing.T) { checks.TemplateCheckName, checks.FragileCheckName, checks.RegexpCheckName, + checks.ImpossibleCheckName, }, }, { @@ -221,6 +222,7 @@ prometheus "prom" { checks.TemplateCheckName, checks.FragileCheckName, checks.RegexpCheckName, + checks.ImpossibleCheckName, checks.RateCheckName + "(prom)", checks.SeriesCheckName + "(prom)", checks.VectorMatchingCheckName + "(prom)", @@ -256,6 +258,7 @@ prometheus "prom" { checks.TemplateCheckName, checks.FragileCheckName, checks.RegexpCheckName, + checks.ImpossibleCheckName, checks.RateCheckName + "(prom)", checks.SeriesCheckName + "(prom)", checks.VectorMatchingCheckName + "(prom)", @@ -307,6 +310,7 @@ checks { checks.ComparisonCheckName, checks.FragileCheckName, checks.RegexpCheckName, + checks.ImpossibleCheckName, }, }, { @@ -333,6 +337,7 @@ prometheus "prom" { checks.TemplateCheckName, checks.FragileCheckName, checks.RegexpCheckName, + checks.ImpossibleCheckName, }, }, { @@ -360,6 +365,7 @@ prometheus "prom" { checks.TemplateCheckName, checks.FragileCheckName, checks.RegexpCheckName, + checks.ImpossibleCheckName, }, }, { @@ -386,6 +392,7 @@ prometheus "prom" { checks.TemplateCheckName, checks.FragileCheckName, checks.RegexpCheckName, + checks.ImpossibleCheckName, }, }, { @@ -412,6 +419,7 @@ prometheus "prom" { checks.TemplateCheckName, checks.FragileCheckName, checks.RegexpCheckName, + checks.ImpossibleCheckName, checks.RateCheckName + "(prom)", checks.SeriesCheckName + "(prom)", checks.VectorMatchingCheckName + "(prom)", @@ -452,6 +460,7 @@ prometheus "ignore" { checks.TemplateCheckName, checks.FragileCheckName, checks.RegexpCheckName, + checks.ImpossibleCheckName, checks.RateCheckName + "(prom)", checks.SeriesCheckName + "(prom)", checks.VectorMatchingCheckName + "(prom)", @@ -481,6 +490,7 @@ prometheus "ignore" { checks.TemplateCheckName, checks.FragileCheckName, checks.RegexpCheckName, + checks.ImpossibleCheckName, }, }, { @@ -511,6 +521,7 @@ rule { checks.TemplateCheckName, checks.FragileCheckName, checks.RegexpCheckName, + checks.ImpossibleCheckName, checks.AggregationCheckName + "(job:true)", checks.AggregationCheckName + "(instance:false)", checks.AggregationCheckName + "(rack:false)", @@ -539,6 +550,7 @@ rule { Rule: newRule(t, ` - record: foo # pint disable promql/aggregate(instance:false) + # pint disable promql/impossible expr: sum(foo) `), }, @@ -579,6 +591,7 @@ rule { checks.TemplateCheckName, checks.FragileCheckName, checks.RegexpCheckName, + checks.ImpossibleCheckName, }, }, { @@ -624,6 +637,7 @@ prometheus "prom2" { checks.TemplateCheckName, checks.FragileCheckName, checks.RegexpCheckName, + checks.ImpossibleCheckName, checks.RateCheckName + "(prom1)", checks.RangeQueryCheckName + "(prom1)", checks.LabelsConflictCheckName + "(prom1)", @@ -696,6 +710,7 @@ rule { checks.TemplateCheckName, checks.FragileCheckName, checks.RegexpCheckName, + checks.ImpossibleCheckName, checks.LabelCheckName + "(team:true)", checks.AnnotationCheckName + "(summary:true)", checks.LabelCheckName + "(team:false)", @@ -750,6 +765,7 @@ rule { # pint disable rule/duplicate # pint disable labels/conflict # pint disable alerts/external_labels +# pint disable promql/impossible - record: foo # pint disable promql/fragile # pint disable promql/regexp @@ -806,6 +822,7 @@ rule { checks.TemplateCheckName, checks.FragileCheckName, checks.RegexpCheckName, + checks.ImpossibleCheckName, checks.RejectCheckName + "(key=~'^http://.+$')", checks.RejectCheckName + "(val=~'^http://.+$')", checks.RejectCheckName + "(key=~'^.* +.*$')", @@ -844,6 +861,7 @@ rule { checks.TemplateCheckName, checks.FragileCheckName, checks.RegexpCheckName, + checks.ImpossibleCheckName, }, }, { @@ -878,6 +896,7 @@ rule { checks.TemplateCheckName, checks.FragileCheckName, checks.RegexpCheckName, + checks.ImpossibleCheckName, }, }, { @@ -912,6 +931,7 @@ rule { checks.TemplateCheckName, checks.FragileCheckName, checks.RegexpCheckName, + checks.ImpossibleCheckName, }, }, { @@ -946,6 +966,7 @@ rule { checks.TemplateCheckName, checks.FragileCheckName, checks.RegexpCheckName, + checks.ImpossibleCheckName, checks.LabelCheckName + "(priority=~^(1|2|3|4|5)$:true)", }, }, @@ -981,6 +1002,7 @@ rule { checks.TemplateCheckName, checks.FragileCheckName, checks.RegexpCheckName, + checks.ImpossibleCheckName, }, }, { @@ -1015,6 +1037,7 @@ rule { checks.TemplateCheckName, checks.FragileCheckName, checks.RegexpCheckName, + checks.ImpossibleCheckName, }, }, { @@ -1051,6 +1074,7 @@ rule { checks.TemplateCheckName, checks.FragileCheckName, checks.RegexpCheckName, + checks.ImpossibleCheckName, checks.LabelCheckName + "(priority=~^(1|2|3|4|5)$:true)", }, }, @@ -1100,6 +1124,7 @@ prometheus "prom1" { checks.TemplateCheckName, checks.FragileCheckName, checks.RegexpCheckName, + checks.ImpossibleCheckName, checks.AlertsExternalLabelsCheckName + "(prom1)", checks.AlertsCheckName + "(prom1)", }, @@ -1141,6 +1166,7 @@ prometheus "prom1" { checks.TemplateCheckName, checks.FragileCheckName, checks.RegexpCheckName, + checks.ImpossibleCheckName, checks.RateCheckName + "(prom1)", checks.SeriesCheckName + "(prom1)", checks.VectorMatchingCheckName + "(prom1)", @@ -1299,6 +1325,7 @@ rule { checks.TemplateCheckName, checks.FragileCheckName, checks.RegexpCheckName, + checks.ImpossibleCheckName, checks.AnnotationCheckName + "(summary:true)", }, }, @@ -1330,6 +1357,7 @@ rule { checks.TemplateCheckName, checks.FragileCheckName, checks.RegexpCheckName, + checks.ImpossibleCheckName, }, }, { @@ -1359,6 +1387,7 @@ rule { checks.TemplateCheckName, checks.FragileCheckName, checks.RegexpCheckName, + checks.ImpossibleCheckName, checks.AnnotationCheckName + "(summary:true)", }, }, @@ -1389,6 +1418,7 @@ rule { checks.TemplateCheckName, checks.FragileCheckName, checks.RegexpCheckName, + checks.ImpossibleCheckName, }, }, { @@ -1418,6 +1448,7 @@ rule { checks.TemplateCheckName, checks.FragileCheckName, checks.RegexpCheckName, + checks.ImpossibleCheckName, }, }, { @@ -1447,6 +1478,7 @@ rule { checks.TemplateCheckName, checks.FragileCheckName, checks.RegexpCheckName, + checks.ImpossibleCheckName, }, }, { @@ -1476,6 +1508,7 @@ rule { checks.TemplateCheckName, checks.FragileCheckName, checks.RegexpCheckName, + checks.ImpossibleCheckName, checks.AnnotationCheckName + "(summary:true)", }, }, @@ -1507,6 +1540,7 @@ rule { checks.TemplateCheckName, checks.FragileCheckName, checks.RegexpCheckName, + checks.ImpossibleCheckName, }, }, { @@ -1536,6 +1570,7 @@ rule { checks.TemplateCheckName, checks.FragileCheckName, checks.RegexpCheckName, + checks.ImpossibleCheckName, checks.AnnotationCheckName + "(summary:true)", }, }, @@ -1569,6 +1604,7 @@ rule { checks.TemplateCheckName, checks.FragileCheckName, checks.RegexpCheckName, + checks.ImpossibleCheckName, checks.RuleLinkCheckName + "(^https?://(.+)$)", }, }, @@ -1594,6 +1630,7 @@ rule { checks.TemplateCheckName, checks.FragileCheckName, checks.RegexpCheckName, + checks.ImpossibleCheckName, checks.RuleNameCheckName + "(^total:.+$)", }, }, @@ -1634,6 +1671,7 @@ checks { checks.ComparisonCheckName, checks.FragileCheckName, checks.RegexpCheckName, + checks.ImpossibleCheckName, }, }, { @@ -1677,6 +1715,7 @@ checks { checks.AlertForCheckName, checks.ComparisonCheckName, checks.FragileCheckName, + checks.ImpossibleCheckName, checks.LabelsConflictCheckName + "(prom1)", checks.AlertsExternalLabelsCheckName + "(prom1)", checks.SeriesCheckName + "(prom2)", @@ -1721,6 +1760,7 @@ checks { checks.AlertForCheckName, checks.ComparisonCheckName, checks.FragileCheckName, + checks.ImpossibleCheckName, checks.SeriesCheckName + "(prom1)", checks.VectorMatchingCheckName + "(prom1)", checks.RangeQueryCheckName + "(prom1)", @@ -1783,7 +1823,9 @@ prometheus "prom3" { checks.ComparisonCheckName, checks.TemplateCheckName, checks.FragileCheckName, - checks.RegexpCheckName, checks.RateCheckName + "(prom2)", + checks.RegexpCheckName, + checks.ImpossibleCheckName, + checks.RateCheckName + "(prom2)", checks.SeriesCheckName + "(prom2)", checks.VectorMatchingCheckName + "(prom2)", checks.RangeQueryCheckName + "(prom2)", @@ -1848,6 +1890,7 @@ prometheus "prom3" { checks.TemplateCheckName, checks.FragileCheckName, checks.RegexpCheckName, + checks.ImpossibleCheckName, checks.RateCheckName + "(prom2)", checks.SeriesCheckName + "(prom2)", checks.VectorMatchingCheckName + "(prom2)", @@ -1898,7 +1941,9 @@ rule { checks.ComparisonCheckName, checks.TemplateCheckName, checks.FragileCheckName, - checks.RegexpCheckName, checks.RateCheckName + "(prom)", + checks.RegexpCheckName, + checks.ImpossibleCheckName, + checks.RateCheckName + "(prom)", checks.SeriesCheckName + "(prom)", checks.VectorMatchingCheckName + "(prom)", checks.RangeQueryCheckName + "(prom)", @@ -1943,7 +1988,9 @@ rule { checks.ComparisonCheckName, checks.TemplateCheckName, checks.FragileCheckName, - checks.RegexpCheckName, checks.RateCheckName + "(prom)", + checks.RegexpCheckName, + checks.ImpossibleCheckName, + checks.RateCheckName + "(prom)", checks.SeriesCheckName + "(prom)", checks.VectorMatchingCheckName + "(prom)", checks.RangeQueryCheckName + "(prom)", @@ -1978,6 +2025,7 @@ rule { checks.TemplateCheckName, checks.FragileCheckName, checks.RegexpCheckName, + checks.ImpossibleCheckName, checks.RangeQueryCheckName + "(1h)", }, }, @@ -2020,6 +2068,7 @@ rule { checks.TemplateCheckName, checks.FragileCheckName, checks.RegexpCheckName, + checks.ImpossibleCheckName, }, }, { @@ -2061,6 +2110,7 @@ rule { checks.TemplateCheckName, checks.FragileCheckName, checks.RegexpCheckName, + checks.ImpossibleCheckName, checks.AggregationCheckName + "(job:true)", checks.AggregationCheckName + "(instance:false)", checks.AggregationCheckName + "(rack:false)", @@ -2105,6 +2155,7 @@ rule { checks.ComparisonCheckName, checks.FragileCheckName, checks.RegexpCheckName, + checks.ImpossibleCheckName, checks.SeriesCheckName + "(prom)", checks.LabelsConflictCheckName + "(prom)", checks.CounterCheckName + "(prom)", @@ -2138,6 +2189,7 @@ rule { checks.ComparisonCheckName, checks.FragileCheckName, checks.RegexpCheckName, + checks.ImpossibleCheckName, }, }, { @@ -2179,6 +2231,7 @@ rule { checks.AlertForCheckName, checks.ComparisonCheckName, checks.FragileCheckName, + checks.ImpossibleCheckName, checks.LabelsConflictCheckName + "(prom1)", checks.AlertsExternalLabelsCheckName + "(prom1)", checks.LabelsConflictCheckName + "(prom2)", @@ -2213,6 +2266,7 @@ rule { checks.TemplateCheckName, checks.FragileCheckName, checks.RegexpCheckName, + checks.ImpossibleCheckName, checks.ReportCheckName, }, }, @@ -2253,6 +2307,7 @@ rule { checks.TemplateCheckName, checks.FragileCheckName, checks.RegexpCheckName, + checks.ImpossibleCheckName, checks.AggregationCheckName + "(instance:false)", checks.AggregationCheckName + "(rack:false)", }, @@ -2294,6 +2349,7 @@ rule { checks.TemplateCheckName, checks.FragileCheckName, checks.RegexpCheckName, + checks.ImpossibleCheckName, checks.AggregationCheckName + "(instance:false)", checks.AggregationCheckName + "(rack:false)", }, diff --git a/internal/config/parsed_rule.go b/internal/config/parsed_rule.go index 9722dc69..1421f52b 100644 --- a/internal/config/parsed_rule.go +++ b/internal/config/parsed_rule.go @@ -124,6 +124,7 @@ func baseRules(proms []*promapi.FailoverGroup, match []Match) (rules []parsedRul baseParsedRule(match, checks.FragileCheckName, checks.NewFragileCheck(), nil), baseParsedRule(match, checks.RegexpCheckName, checks.NewRegexpCheck(), nil), baseParsedRule(match, checks.RuleDependencyCheckName, checks.NewRuleDependencyCheck(), nil), + baseParsedRule(match, checks.ImpossibleCheckName, checks.NewImpossibleCheck(), nil), ) for _, p := range proms { diff --git a/internal/parser/utils/source.go b/internal/parser/utils/source.go index 4d2e45e8..ae968bc1 100644 --- a/internal/parser/utils/source.go +++ b/internal/parser/utils/source.go @@ -4,6 +4,7 @@ import ( "fmt" "math" "slices" + "strconv" "strings" "github.com/prometheus/prometheus/model/labels" @@ -11,6 +12,8 @@ import ( "github.com/prometheus/prometheus/promql/parser/posrange" ) +var guaranteedLabelsMatches = []labels.MatchType{labels.MatchEqual, labels.MatchRegexp} + type SourceType int const ( @@ -27,18 +30,24 @@ type ExcludedLabel struct { Fragment string } +type Join struct { + Src Source +} + type Source struct { - Joins []Source // Any other sources this source joins with. - Unless []Source // Any other sources this source is suppressed by. - Selector *promParser.VectorSelector - Call *promParser.Call - ExcludeReason map[string]ExcludedLabel // Reason why a label was excluded + Selector *promParser.VectorSelector // Vector selector used for this source. + Call *promParser.Call // Most outer call used inside this source. + Aggregation *promParser.AggregateExpr // Most outer aggregation expression used inside this source. + ExcludeReason map[string]ExcludedLabel // Reason why a label was excluded Operation string + IsDeadReason string Returns promParser.ValueType - ReturnedNumbers []float64 // If AlwaysReturns=true this is the number that's returned - IncludedLabels []string // Labels that are included by filters, they will be present if exist on source series (by). - ExcludedLabels []string // Labels guaranteed to be excluded from the results (without). - GuaranteedLabels []string // Labels guaranteed to be present on the results (matchers). + Joins []Join // Any other sources this source joins with. + Unless []Join // Any other sources this source is suppressed by. + IncludedLabels []string // Labels that are included by filters, they will be present if exist on source series (by). + ExcludedLabels []string // Labels guaranteed to be excluded from the results (without). + GuaranteedLabels []string // Labels guaranteed to be present on the results (matchers). + ReturnedNumber float64 // If AlwaysReturns=true this is the number that's returned Type SourceType FixedLabels bool // Labels are fixed and only allowed labels can be present. IsDead bool // True if this source cannot be reached and is dead code. @@ -46,6 +55,19 @@ type Source struct { IsConditional bool // True if this source is guarded by 'foo > 5' or other condition. } +func (s Source) Fragment(expr string) string { + switch { + case s.Selector != nil: + return getQueryFragment(expr, s.Selector.PosRange) + case s.Call != nil: + return getQueryFragment(expr, s.Call.PosRange) + case s.Aggregation != nil: + return getQueryFragment(expr, s.Aggregation.PosRange) + default: + return "" + } +} + func LabelsSource(expr string, node promParser.Node) (src []Source) { return walkNode(expr, node) } @@ -71,7 +93,7 @@ func walkNode(expr string, node promParser.Node) (src []Source) { case *promParser.NumberLiteral: s.Type = NumberSource s.Returns = promParser.ValueTypeScalar - s.ReturnedNumbers = append(s.ReturnedNumbers, n.Val) + s.ReturnedNumber = n.Val s.IncludedLabels = nil s.GuaranteedLabels = nil s.FixedLabels = true @@ -165,6 +187,17 @@ func guaranteeLabel(s Source, names ...string) Source { return s } +func restrictGuaranteedLabels(s Source, names []string) Source { + todo := []string{} + for _, name := range s.GuaranteedLabels { + if !slices.Contains(names, name) { + todo = append(todo, name) + } + } + s.GuaranteedLabels = removeFromSlice(s.GuaranteedLabels, todo...) + return s +} + func excludeLabel(s Source, names ...string) Source { s.ExcludedLabels = appendToSlice(s.ExcludedLabels, names...) s.IncludedLabels = removeFromSlice(s.IncludedLabels, names...) @@ -180,8 +213,6 @@ func setInMap(dst map[string]ExcludedLabel, key string, val ExcludedLabel) map[s return dst } -var guaranteedLabelsMatches = []labels.MatchType{labels.MatchEqual, labels.MatchRegexp} - func labelsFromSelectors(matches []labels.MatchType, selector *promParser.VectorSelector) (names []string) { if selector == nil { return nil @@ -212,51 +243,61 @@ func getQueryFragment(expr string, pos posrange.PositionRange) string { return expr[pos.Start:pos.End] } +// FIXME Aggregations strip __name__. func walkAggregation(expr string, n *promParser.AggregateExpr) (src []Source) { var s Source switch n.Op { case promParser.SUM: for _, s = range parseAggregation(expr, n) { + s.Aggregation = n s.Operation = "sum" src = append(src, s) } case promParser.MIN: for _, s = range parseAggregation(expr, n) { + s.Aggregation = n s.Operation = "min" src = append(src, s) } case promParser.MAX: for _, s = range parseAggregation(expr, n) { + s.Aggregation = n s.Operation = "max" src = append(src, s) } case promParser.AVG: for _, s = range parseAggregation(expr, n) { + s.Aggregation = n s.Operation = "avg" src = append(src, s) } case promParser.GROUP: for _, s = range parseAggregation(expr, n) { + s.Aggregation = n s.Operation = "group" src = append(src, s) } case promParser.STDDEV: for _, s = range parseAggregation(expr, n) { + s.Aggregation = n s.Operation = "stddev" src = append(src, s) } case promParser.STDVAR: for _, s = range parseAggregation(expr, n) { + s.Aggregation = n s.Operation = "stdvar" src = append(src, s) } case promParser.COUNT: for _, s = range parseAggregation(expr, n) { + s.Aggregation = n s.Operation = "count" src = append(src, s) } case promParser.COUNT_VALUES: for _, s = range parseAggregation(expr, n) { + s.Aggregation = n s.Operation = "count_values" // Param is the label to store the count value in. s = includeLabel(s, n.Param.(*promParser.StringLiteral).Val) @@ -265,18 +306,21 @@ func walkAggregation(expr string, n *promParser.AggregateExpr) (src []Source) { } case promParser.QUANTILE: for _, s = range parseAggregation(expr, n) { + s.Aggregation = n s.Operation = "quantile" src = append(src, s) } case promParser.TOPK: for _, s = range walkNode(expr, n.Expr) { s.Type = AggregateSource + s.Aggregation = n s.Operation = "topk" src = append(src, s) } case promParser.BOTTOMK: for _, s = range walkNode(expr, n.Expr) { s.Type = AggregateSource + s.Aggregation = n s.Operation = "bottomk" src = append(src, s) } @@ -337,17 +381,12 @@ func parseAggregation(expr string, n *promParser.AggregateExpr) (src []Source) { }, ) } - for _, name := range s.GuaranteedLabels { - if !slices.Contains(n.Grouping, name) { - s.GuaranteedLabels = removeFromSlice(s.GuaranteedLabels, name) - } - } + s = restrictGuaranteedLabels(s, n.Grouping) } s.FixedLabels = true } s.Type = AggregateSource s.Returns = promParser.ValueTypeVector - s.Call = nil src = append(src, s) } return src @@ -512,7 +551,7 @@ If you're hoping to get instance specific labels this way and alert when some ta s.FixedLabels = true s.AlwaysReturns = true if v, ok := n.Args[0].(*promParser.NumberLiteral); ok { - s.ReturnedNumbers = append(s.ReturnedNumbers, v.Val) + s.ReturnedNumber = v.Val } s.ExcludeReason = setInMap( s.ExcludeReason, @@ -581,11 +620,12 @@ func parseBinOps(expr string, n *promParser.BinaryExpr) (src []Source) { switch { case ls.AlwaysReturns && rs.AlwaysReturns: // Both sides always return something - for i, lv := range ls.ReturnedNumbers { - for _, rv := range rs.ReturnedNumbers { - ls.ReturnedNumbers[i], ls.IsDead = calculateStaticReturn(lv, rv, n.Op, ls.IsDead) - } - } + ls.ReturnedNumber, ls.IsDead, ls.IsDeadReason = calculateStaticReturn( + expr, + ls, rs, + n.Op, + ls.IsDead, + ) src = append(src, ls) case ls.Returns == promParser.ValueTypeVector, ls.Returns == promParser.ValueTypeMatrix: // Use labels from LHS @@ -649,30 +689,52 @@ func parseBinOps(expr string, n *promParser.BinaryExpr) (src []Source) { if s.Operation == "" { s.Operation = n.VectorMatching.Card.String() } - s.Joins = append(s.Joins, rhs...) + for _, rs := range rhs { + if ok, s := canJoin(s, rs, n.VectorMatching); !ok { + rs.IsDead = true + rs.IsDeadReason = s + } + s.Joins = append(s.Joins, Join{ + Src: rs, + }) + } s.IsConditional = n.Op.IsComparisonOperator() src = append(src, s) } - // foo{} + on(...) group_left(...) bar{} - // foo{} + ignoring(...) group_left(...) bar{} + // foo{} + on(...) group_right(...) bar{} + // foo{} + ignoring(...) group_right(...) bar{} case n.VectorMatching.Card == promParser.CardOneToMany: lhs := walkNode(expr, n.LHS) for _, s = range walkNode(expr, n.RHS) { s = includeLabel(s, n.VectorMatching.Include...) + // If we have: + // foo * on(instance) group_left(a,b) bar{x="y"} + // then only group_left() labels will be included. if n.VectorMatching.On { s = includeLabel(s, n.VectorMatching.MatchingLabels...) } if s.Operation == "" { s.Operation = n.VectorMatching.Card.String() } - s.Joins = append(s.Joins, lhs...) + for _, ls := range lhs { + if n.VectorMatching.On { + ls = restrictGuaranteedLabels(ls, n.VectorMatching.Include) + } + if ok, s := canJoin(s, ls, n.VectorMatching); !ok { + ls.IsDead = true + ls.IsDeadReason = s + } + s.Joins = append(s.Joins, Join{ + Src: ls, + }) + } s.IsConditional = n.Op.IsComparisonOperator() src = append(src, s) } - // foo{} + on(...) group_right(...) bar{} - // foo{} + ignoring(...) group_right(...) bar{} + // foo{} + on(...) group_left(...) bar{} + // foo{} + ignoring(...) group_left(...) bar{} case n.VectorMatching.Card == promParser.CardManyToOne: rhs := walkNode(expr, n.RHS) for _, s = range walkNode(expr, n.LHS) { @@ -683,7 +745,18 @@ func parseBinOps(expr string, n *promParser.BinaryExpr) (src []Source) { if s.Operation == "" { s.Operation = n.VectorMatching.Card.String() } - s.Joins = append(s.Joins, rhs...) + for _, rs := range rhs { + if n.VectorMatching.On { + rs = restrictGuaranteedLabels(rs, n.VectorMatching.Include) + } + if ok, s := canJoin(s, rs, n.VectorMatching); !ok { + rs.IsDead = true + rs.IsDeadReason = s + } + s.Joins = append(s.Joins, Join{ + Src: rs, + }) + } s.IsConditional = n.Op.IsComparisonOperator() src = append(src, s) } @@ -704,11 +777,26 @@ func parseBinOps(expr string, n *promParser.BinaryExpr) (src []Source) { if !s.AlwaysReturns { lhsCanBeEmpty = true } - switch { - case n.Op == promParser.LUNLESS: - s.Unless = append(s.Unless, rhs...) - case n.Op != promParser.LOR: - s.Joins = append(s.Joins, rhs...) + for _, rs := range rhs { + if ok, s := canJoin(s, rs, n.VectorMatching); !ok { + rs.IsDead = true + rs.IsDeadReason = s + } + switch { + case n.Op == promParser.LUNLESS: + if n.VectorMatching.On && len(n.VectorMatching.MatchingLabels) == 0 && rs.AlwaysReturns { + s.IsDead = true + s.IsDeadReason = "this query will never return anything because the `unless` query always returns something" + } + s.Unless = append(s.Unless, Join{ + Src: rs, + }) + case n.Op != promParser.LOR: + + s.Joins = append(s.Joins, Join{ + Src: rs, + }) + } } src = append(src, s) } @@ -720,6 +808,7 @@ func parseBinOps(expr string, n *promParser.BinaryExpr) (src []Source) { // If LHS can NOT be empty then RHS is dead code. if !lhsCanBeEmpty { s.IsDead = true + s.IsDeadReason = "the left hand side always returs something and so the right hand side is never used" } src = append(src, s) } @@ -728,44 +817,93 @@ func parseBinOps(expr string, n *promParser.BinaryExpr) (src []Source) { return src } -func calculateStaticReturn(lv, rv float64, op promParser.ItemType, isDead bool) (float64, bool) { +func canJoin(ls, rs Source, vm *promParser.VectorMatching) (bool, string) { + switch { + case vm.On && len(vm.MatchingLabels) == 0: // ls on() unless rs + return true, "" + case vm.On: // ls on(...) unless rs + for _, name := range vm.MatchingLabels { + if canHaveLabels(ls, name) && !canHaveLabels(rs, name) { + return false, fmt.Sprintf("the right hand side will never be matched because it doesn't have `%s` label from `on(...)`", name) + } + } + default: // ls unless rs + for _, name := range ls.GuaranteedLabels { + if canHaveLabels(ls, name) && !canHaveLabels(rs, name) { + return false, fmt.Sprintf("the right hand side will never be matched because it doesn't have `%s` label while the left hand side will", name) + } + } + } + return true, "" +} + +func canHaveLabels(s Source, name string) bool { + if s.Type == NumberSource || s.Type == StringSource { + return false + } + if slices.Contains(s.ExcludedLabels, name) { + return false + } + if slices.Contains(s.IncludedLabels, name) { + return true + } + if slices.Contains(s.GuaranteedLabels, name) { + return true + } + return !s.FixedLabels +} + +func ftos(v float64) string { + return strconv.FormatFloat(v, 'f', -1, 64) +} + +func calculateStaticReturn(expr string, ls, rs Source, op promParser.ItemType, isDead bool) (float64, bool, string) { + lf := ls.Fragment(expr) + rf := rs.Fragment(expr) + var cmpPrefix string + if lf != "" && rf != "" { + cmpPrefix = fmt.Sprintf("`%s %s %s` always evaluates to", lf, op, rf) + } else { + cmpPrefix = "this query always evaluates to" + } + cmpSuffix := "which is not possible, so it will never return anything" switch op { case promParser.EQLC: - if lv != rv { - return lv, true + if ls.ReturnedNumber != rs.ReturnedNumber { + return ls.ReturnedNumber, true, fmt.Sprintf("%s `%s == %s` %s", cmpPrefix, ftos(ls.ReturnedNumber), ftos(rs.ReturnedNumber), cmpSuffix) } case promParser.NEQ: - if lv == rv { - return lv, true + if ls.ReturnedNumber == rs.ReturnedNumber { + return ls.ReturnedNumber, true, fmt.Sprintf("%s `%s != %s` %s", cmpPrefix, ftos(ls.ReturnedNumber), ftos(rs.ReturnedNumber), cmpSuffix) } case promParser.LTE: - if lv > rv { - return lv, true + if ls.ReturnedNumber > rs.ReturnedNumber { + return ls.ReturnedNumber, true, fmt.Sprintf("%s `%s <= %s` %s", cmpPrefix, ftos(ls.ReturnedNumber), ftos(rs.ReturnedNumber), cmpSuffix) } case promParser.LSS: - if lv >= rv { - return lv, true + if ls.ReturnedNumber >= rs.ReturnedNumber { + return ls.ReturnedNumber, true, fmt.Sprintf("%s `%s < %s` %s", cmpPrefix, ftos(ls.ReturnedNumber), ftos(rs.ReturnedNumber), cmpSuffix) } case promParser.GTE: - if lv < rv { - return lv, true + if ls.ReturnedNumber < rs.ReturnedNumber { + return ls.ReturnedNumber, true, fmt.Sprintf("%s `%s >= %s` %s", cmpPrefix, ftos(ls.ReturnedNumber), ftos(rs.ReturnedNumber), cmpSuffix) } case promParser.GTR: - if lv <= rv { - return lv, true + if ls.ReturnedNumber <= rs.ReturnedNumber { + return ls.ReturnedNumber, true, fmt.Sprintf("%s `%s > %s` %s", cmpPrefix, ftos(ls.ReturnedNumber), ftos(rs.ReturnedNumber), cmpSuffix) } case promParser.ADD: - return lv + rv, isDead + return ls.ReturnedNumber + rs.ReturnedNumber, isDead, "" case promParser.SUB: - return lv - rv, isDead + return ls.ReturnedNumber - rs.ReturnedNumber, isDead, "" case promParser.MUL: - return lv * rv, isDead + return ls.ReturnedNumber * rs.ReturnedNumber, isDead, "" case promParser.DIV: - return lv / rv, isDead + return ls.ReturnedNumber / rs.ReturnedNumber, isDead, "" case promParser.MOD: - return math.Mod(lv, rv), isDead + return math.Mod(ls.ReturnedNumber, rs.ReturnedNumber), isDead, "" case promParser.POW: - return math.Pow(lv, rv), isDead + return math.Pow(ls.ReturnedNumber, rs.ReturnedNumber), isDead, "" } - return lv, isDead + return ls.ReturnedNumber, isDead, "" } diff --git a/internal/parser/utils/source_test.go b/internal/parser/utils/source_test.go index 945c54ca..dc10df6f 100644 --- a/internal/parser/utils/source_test.go +++ b/internal/parser/utils/source_test.go @@ -31,11 +31,11 @@ func TestLabelsSource(t *testing.T) { expr: "1", output: []utils.Source{ { - Type: utils.NumberSource, - Returns: promParser.ValueTypeScalar, - FixedLabels: true, - AlwaysReturns: true, - ReturnedNumbers: []float64{1}, + Type: utils.NumberSource, + Returns: promParser.ValueTypeScalar, + FixedLabels: true, + AlwaysReturns: true, + ReturnedNumber: 1, ExcludeReason: map[string]utils.ExcludedLabel{ "": { Reason: "This returns a number value with no labels.", @@ -49,11 +49,11 @@ func TestLabelsSource(t *testing.T) { expr: "1 / 5", output: []utils.Source{ { - Type: utils.NumberSource, - Returns: promParser.ValueTypeScalar, - FixedLabels: true, - AlwaysReturns: true, - ReturnedNumbers: []float64{0.2}, + Type: utils.NumberSource, + Returns: promParser.ValueTypeScalar, + FixedLabels: true, + AlwaysReturns: true, + ReturnedNumber: 0.2, ExcludeReason: map[string]utils.ExcludedLabel{ "": { Reason: "This returns a number value with no labels.", @@ -67,12 +67,13 @@ func TestLabelsSource(t *testing.T) { expr: "(2 ^ 5) == bool 5", output: []utils.Source{ { - Type: utils.NumberSource, - Returns: promParser.ValueTypeScalar, - FixedLabels: true, - AlwaysReturns: true, - IsDead: true, - ReturnedNumbers: []float64{32}, + Type: utils.NumberSource, + Returns: promParser.ValueTypeScalar, + FixedLabels: true, + AlwaysReturns: true, + IsDead: true, + IsDeadReason: "this query always evaluates to `32 == 5` which is not possible, so it will never return anything", + ReturnedNumber: 32, ExcludeReason: map[string]utils.ExcludedLabel{ "": { Reason: "This returns a number value with no labels.", @@ -87,12 +88,13 @@ func TestLabelsSource(t *testing.T) { expr: "(2 ^ 5 + 11) % 5 <= bool 2", output: []utils.Source{ { - Type: utils.NumberSource, - Returns: promParser.ValueTypeScalar, - FixedLabels: true, - AlwaysReturns: true, - IsDead: true, - ReturnedNumbers: []float64{3}, + Type: utils.NumberSource, + Returns: promParser.ValueTypeScalar, + FixedLabels: true, + AlwaysReturns: true, + IsDead: true, + IsDeadReason: "this query always evaluates to `3 <= 2` which is not possible, so it will never return anything", + ReturnedNumber: 3, ExcludeReason: map[string]utils.ExcludedLabel{ "": { Reason: "This returns a number value with no labels.", @@ -107,12 +109,13 @@ func TestLabelsSource(t *testing.T) { expr: "(2 ^ 5 + 11) % 5 >= bool 20", output: []utils.Source{ { - Type: utils.NumberSource, - Returns: promParser.ValueTypeScalar, - FixedLabels: true, - AlwaysReturns: true, - IsDead: true, - ReturnedNumbers: []float64{3}, + Type: utils.NumberSource, + Returns: promParser.ValueTypeScalar, + FixedLabels: true, + AlwaysReturns: true, + IsDead: true, + IsDeadReason: "this query always evaluates to `3 >= 20` which is not possible, so it will never return anything", + ReturnedNumber: 3, ExcludeReason: map[string]utils.ExcludedLabel{ "": { Reason: "This returns a number value with no labels.", @@ -127,11 +130,11 @@ func TestLabelsSource(t *testing.T) { expr: "(2 ^ 5 + 11) % 5 <= bool 3", output: []utils.Source{ { - Type: utils.NumberSource, - Returns: promParser.ValueTypeScalar, - FixedLabels: true, - AlwaysReturns: true, - ReturnedNumbers: []float64{3}, + Type: utils.NumberSource, + Returns: promParser.ValueTypeScalar, + FixedLabels: true, + AlwaysReturns: true, + ReturnedNumber: 3, ExcludeReason: map[string]utils.ExcludedLabel{ "": { Reason: "This returns a number value with no labels.", @@ -146,12 +149,13 @@ func TestLabelsSource(t *testing.T) { expr: "(2 ^ 5 + 11) % 5 < bool 1", output: []utils.Source{ { - Type: utils.NumberSource, - Returns: promParser.ValueTypeScalar, - FixedLabels: true, - AlwaysReturns: true, - IsDead: true, - ReturnedNumbers: []float64{3}, + Type: utils.NumberSource, + Returns: promParser.ValueTypeScalar, + FixedLabels: true, + AlwaysReturns: true, + IsDead: true, + IsDeadReason: "this query always evaluates to `3 < 1` which is not possible, so it will never return anything", + ReturnedNumber: 3, ExcludeReason: map[string]utils.ExcludedLabel{ "": { Reason: "This returns a number value with no labels.", @@ -166,12 +170,13 @@ func TestLabelsSource(t *testing.T) { expr: "20 - 15 < bool 1", output: []utils.Source{ { - Type: utils.NumberSource, - Returns: promParser.ValueTypeScalar, - FixedLabels: true, - AlwaysReturns: true, - IsDead: true, - ReturnedNumbers: []float64{5}, + Type: utils.NumberSource, + Returns: promParser.ValueTypeScalar, + FixedLabels: true, + AlwaysReturns: true, + IsDead: true, + IsDeadReason: "this query always evaluates to `5 < 1` which is not possible, so it will never return anything", + ReturnedNumber: 5, ExcludeReason: map[string]utils.ExcludedLabel{ "": { Reason: "This returns a number value with no labels.", @@ -186,11 +191,11 @@ func TestLabelsSource(t *testing.T) { expr: "2 * 5", output: []utils.Source{ { - Type: utils.NumberSource, - Returns: promParser.ValueTypeScalar, - FixedLabels: true, - AlwaysReturns: true, - ReturnedNumbers: []float64{10}, + Type: utils.NumberSource, + Returns: promParser.ValueTypeScalar, + FixedLabels: true, + AlwaysReturns: true, + ReturnedNumber: 10, ExcludeReason: map[string]utils.ExcludedLabel{ "": { Reason: "This returns a number value with no labels.", @@ -227,12 +232,12 @@ func TestLabelsSource(t *testing.T) { Selector: mustParse[*promParser.VectorSelector](t, "foo", 1), }, { - Type: utils.FuncSource, - Returns: promParser.ValueTypeVector, - Operation: "vector", - FixedLabels: true, - AlwaysReturns: true, - ReturnedNumbers: []float64{10}, + Type: utils.FuncSource, + Returns: promParser.ValueTypeVector, + Operation: "vector", + FixedLabels: true, + AlwaysReturns: true, + ReturnedNumber: 10, ExcludeReason: map[string]utils.ExcludedLabel{ "": { Reason: "Calling `vector()` will return a vector value with no labels.", @@ -251,38 +256,43 @@ func TestLabelsSource(t *testing.T) { Returns: promParser.ValueTypeVector, Operation: promParser.CardManyToMany.String(), Selector: mustParse[*promParser.VectorSelector](t, "foo", 1), - Joins: []utils.Source{ + Joins: []utils.Join{ { - Type: utils.FuncSource, - Returns: promParser.ValueTypeVector, - Operation: "vector", - FixedLabels: true, - AlwaysReturns: true, - ReturnedNumbers: []float64{2}, - Call: mustParse[*promParser.Call](t, "vector(2)", 22), - ExcludeReason: map[string]utils.ExcludedLabel{ - "": { - Reason: "Calling `vector()` will return a vector value with no labels.", - Fragment: "vector(2)", + Src: utils.Source{ + Type: utils.FuncSource, + Returns: promParser.ValueTypeVector, + Operation: "vector", + FixedLabels: true, + AlwaysReturns: true, + ReturnedNumber: 2, + Call: mustParse[*promParser.Call](t, "vector(2)", 22), + ExcludeReason: map[string]utils.ExcludedLabel{ + "": { + Reason: "Calling `vector()` will return a vector value with no labels.", + Fragment: "vector(2)", + }, }, }, }, { - Type: utils.SelectorSource, - Returns: promParser.ValueTypeVector, - Operation: promParser.CardManyToMany.String(), - Selector: mustParse[*promParser.VectorSelector](t, "bar", 35), - IsDead: true, + Src: utils.Source{ + Type: utils.SelectorSource, + Returns: promParser.ValueTypeVector, + Operation: promParser.CardManyToMany.String(), + Selector: mustParse[*promParser.VectorSelector](t, "bar", 35), + IsDead: true, + IsDeadReason: "the left hand side always returs something and so the right hand side is never used", + }, }, }, }, { - Type: utils.FuncSource, - Returns: promParser.ValueTypeVector, - Operation: "vector", - FixedLabels: true, - AlwaysReturns: true, - ReturnedNumbers: []float64{5}, // FIXME should be 10 really but it's one-to-one binops + Type: utils.FuncSource, + Returns: promParser.ValueTypeVector, + Operation: "vector", + FixedLabels: true, + AlwaysReturns: true, + ReturnedNumber: 5, // FIXME should be 10 really but it's one-to-one binops ExcludeReason: map[string]utils.ExcludedLabel{ "": { Reason: "Calling `vector()` will return a vector value with no labels.", @@ -290,28 +300,33 @@ func TestLabelsSource(t *testing.T) { }, }, Call: mustParse[*promParser.Call](t, "vector(5)", 8), - Joins: []utils.Source{ + Joins: []utils.Join{ { - Type: utils.FuncSource, - Returns: promParser.ValueTypeVector, - Operation: "vector", - FixedLabels: true, - AlwaysReturns: true, - ReturnedNumbers: []float64{2}, - Call: mustParse[*promParser.Call](t, "vector(2)", 22), - ExcludeReason: map[string]utils.ExcludedLabel{ - "": { - Reason: "Calling `vector()` will return a vector value with no labels.", - Fragment: "vector(2)", + Src: utils.Source{ + Type: utils.FuncSource, + Returns: promParser.ValueTypeVector, + Operation: "vector", + FixedLabels: true, + AlwaysReturns: true, + ReturnedNumber: 2, + Call: mustParse[*promParser.Call](t, "vector(2)", 22), + ExcludeReason: map[string]utils.ExcludedLabel{ + "": { + Reason: "Calling `vector()` will return a vector value with no labels.", + Fragment: "vector(2)", + }, }, }, }, { - Type: utils.SelectorSource, - Returns: promParser.ValueTypeVector, - Operation: promParser.CardManyToMany.String(), - Selector: mustParse[*promParser.VectorSelector](t, "bar", 35), - IsDead: true, + Src: utils.Source{ + Type: utils.SelectorSource, + Returns: promParser.ValueTypeVector, + Operation: promParser.CardManyToMany.String(), + Selector: mustParse[*promParser.VectorSelector](t, "bar", 35), + IsDead: true, + IsDeadReason: "the left hand side always returs something and so the right hand side is never used", + }, }, }, }, @@ -321,11 +336,11 @@ func TestLabelsSource(t *testing.T) { expr: `1 > bool 0`, output: []utils.Source{ { - Type: utils.NumberSource, - Returns: promParser.ValueTypeScalar, - FixedLabels: true, - AlwaysReturns: true, - ReturnedNumbers: []float64{1}, + Type: utils.NumberSource, + Returns: promParser.ValueTypeScalar, + FixedLabels: true, + AlwaysReturns: true, + ReturnedNumber: 1, ExcludeReason: map[string]utils.ExcludedLabel{ "": { Reason: "This returns a number value with no labels.", @@ -340,11 +355,11 @@ func TestLabelsSource(t *testing.T) { expr: `20 > bool 10`, output: []utils.Source{ { - Type: utils.NumberSource, - Returns: promParser.ValueTypeScalar, - FixedLabels: true, - AlwaysReturns: true, - ReturnedNumbers: []float64{20}, + Type: utils.NumberSource, + Returns: promParser.ValueTypeScalar, + FixedLabels: true, + AlwaysReturns: true, + ReturnedNumber: 20, ExcludeReason: map[string]utils.ExcludedLabel{ "": { Reason: "This returns a number value with no labels.", @@ -446,7 +461,7 @@ func TestLabelsSource(t *testing.T) { output: []utils.Source{ { Type: utils.SelectorSource, - Returns: promParser.ValueTypeVector, + Returns: promParser.ValueTypeVector, // FIXME Matrix ?? Selector: mustParse[*promParser.VectorSelector](t, "foo", 0), }, }, @@ -511,6 +526,7 @@ func TestLabelsSource(t *testing.T) { Returns: promParser.ValueTypeVector, Operation: "sum", Selector: mustParse[*promParser.VectorSelector](t, `foo{job="myjob"}`, 4), + Aggregation: mustParse[*promParser.AggregateExpr](t, `sum(foo{job="myjob"})`, 0), FixedLabels: true, ExcludeReason: map[string]utils.ExcludedLabel{ "": { @@ -521,6 +537,25 @@ func TestLabelsSource(t *testing.T) { }, }, }, + { + expr: `sum(count(foo{job="myjob"}) by(instance))`, + output: []utils.Source{ + { + Type: utils.AggregateSource, + Returns: promParser.ValueTypeVector, + Operation: "sum", + Selector: mustParse[*promParser.VectorSelector](t, `foo{job="myjob"}`, 10), + Aggregation: mustParse[*promParser.AggregateExpr](t, `sum(count(foo{job="myjob"}) by(instance))`, 0), + FixedLabels: true, + ExcludeReason: map[string]utils.ExcludedLabel{ + "": { + Reason: "Query is using aggregation that removes all labels.", + Fragment: `sum(count(foo{job="myjob"}) by(instance))`, + }, + }, + }, + }, + }, { expr: `sum(foo{job="myjob"}) > 20`, output: []utils.Source{ @@ -529,6 +564,7 @@ func TestLabelsSource(t *testing.T) { Returns: promParser.ValueTypeVector, Operation: "sum", Selector: mustParse[*promParser.VectorSelector](t, `foo{job="myjob"}`, 4), + Aggregation: mustParse[*promParser.AggregateExpr](t, `sum(foo{job="myjob"})`, 0), FixedLabels: true, ExcludeReason: map[string]utils.ExcludedLabel{ "": { @@ -548,6 +584,7 @@ func TestLabelsSource(t *testing.T) { Returns: promParser.ValueTypeVector, Operation: "sum", Selector: mustParse[*promParser.VectorSelector](t, `foo{job="myjob"}`, 4), + Aggregation: mustParse[*promParser.AggregateExpr](t, `sum(foo{job="myjob"}) without(job)`, 0), ExcludedLabels: []string{"job"}, ExcludeReason: map[string]utils.ExcludedLabel{ "job": { @@ -566,6 +603,7 @@ func TestLabelsSource(t *testing.T) { Returns: promParser.ValueTypeVector, Operation: "sum", Selector: mustParse[*promParser.VectorSelector](t, `foo`, 4), + Aggregation: mustParse[*promParser.AggregateExpr](t, `sum(foo) by(job)`, 0), IncludedLabels: []string{"job"}, FixedLabels: true, ExcludeReason: map[string]utils.ExcludedLabel{ @@ -585,6 +623,7 @@ func TestLabelsSource(t *testing.T) { Returns: promParser.ValueTypeVector, Operation: "sum", Selector: mustParse[*promParser.VectorSelector](t, `foo{job="myjob"}`, 4), + Aggregation: mustParse[*promParser.AggregateExpr](t, `sum(foo{job="myjob"}) by(job)`, 0), IncludedLabels: []string{"job"}, GuaranteedLabels: []string{"job"}, FixedLabels: true, @@ -639,6 +678,7 @@ func TestLabelsSource(t *testing.T) { Returns: promParser.ValueTypeVector, Operation: "sum", Selector: mustParse[*promParser.VectorSelector](t, `foo{job="myjob"}`, 4), + Aggregation: mustParse[*promParser.AggregateExpr](t, `sum(foo{job="myjob"} or bar{cluster="dev"}) without(instance)`, 0), GuaranteedLabels: []string{"job"}, ExcludedLabels: []string{"instance"}, ExcludeReason: map[string]utils.ExcludedLabel{ @@ -653,6 +693,7 @@ func TestLabelsSource(t *testing.T) { Returns: promParser.ValueTypeVector, Operation: "sum", Selector: mustParse[*promParser.VectorSelector](t, `bar{cluster="dev"}`, 24), + Aggregation: mustParse[*promParser.AggregateExpr](t, `sum(foo{job="myjob"} or bar{cluster="dev"}) without(instance)`, 0), GuaranteedLabels: []string{"cluster"}, ExcludedLabels: []string{"instance"}, ExcludeReason: map[string]utils.ExcludedLabel{ @@ -672,6 +713,7 @@ func TestLabelsSource(t *testing.T) { Returns: promParser.ValueTypeVector, Operation: "sum", Selector: mustParse[*promParser.VectorSelector](t, `foo{job="myjob"}`, 4), + Aggregation: mustParse[*promParser.AggregateExpr](t, `sum(foo{job="myjob"}) without(instance)`, 0), GuaranteedLabels: []string{"job"}, ExcludedLabels: []string{"instance"}, ExcludeReason: map[string]utils.ExcludedLabel{ @@ -691,6 +733,7 @@ func TestLabelsSource(t *testing.T) { Returns: promParser.ValueTypeVector, Operation: "min", Selector: mustParse[*promParser.VectorSelector](t, `foo{job="myjob"}`, 4), + Aggregation: mustParse[*promParser.AggregateExpr](t, `min(foo{job="myjob"})`, 0), FixedLabels: true, ExcludeReason: map[string]utils.ExcludedLabel{ "": { @@ -698,17 +741,20 @@ func TestLabelsSource(t *testing.T) { Fragment: `min(foo{job="myjob"})`, }, }, - Joins: []utils.Source{ + Joins: []utils.Join{ { - Type: utils.AggregateSource, - Operation: "max", - Returns: promParser.ValueTypeVector, - Selector: mustParse[*promParser.VectorSelector](t, `foo{job="myjob"}`, 28), - FixedLabels: true, - ExcludeReason: map[string]utils.ExcludedLabel{ - "": { - Reason: "Query is using aggregation that removes all labels.", - Fragment: `max(foo{job="myjob"})`, + Src: utils.Source{ + Type: utils.AggregateSource, + Operation: "max", + Returns: promParser.ValueTypeVector, + Selector: mustParse[*promParser.VectorSelector](t, `foo{job="myjob"}`, 28), + Aggregation: mustParse[*promParser.AggregateExpr](t, `max(foo{job="myjob"})`, 24), + FixedLabels: true, + ExcludeReason: map[string]utils.ExcludedLabel{ + "": { + Reason: "Query is using aggregation that removes all labels.", + Fragment: `max(foo{job="myjob"})`, + }, }, }, }, @@ -724,6 +770,7 @@ func TestLabelsSource(t *testing.T) { Returns: promParser.ValueTypeVector, Operation: "max", Selector: mustParse[*promParser.VectorSelector](t, `foo{job="myjob"}`, 4), + Aggregation: mustParse[*promParser.AggregateExpr](t, `max(foo{job="myjob"})`, 0), FixedLabels: true, ExcludeReason: map[string]utils.ExcludedLabel{ "": { @@ -731,17 +778,20 @@ func TestLabelsSource(t *testing.T) { Fragment: `max(foo{job="myjob"})`, }, }, - Joins: []utils.Source{ + Joins: []utils.Join{ { - Type: utils.AggregateSource, - Operation: "min", - Returns: promParser.ValueTypeVector, - Selector: mustParse[*promParser.VectorSelector](t, `foo{job="myjob"}`, 28), - FixedLabels: true, - ExcludeReason: map[string]utils.ExcludedLabel{ - "": { - Reason: "Query is using aggregation that removes all labels.", - Fragment: `min(foo{job="myjob"})`, + Src: utils.Source{ + Type: utils.AggregateSource, + Operation: "min", + Returns: promParser.ValueTypeVector, + Selector: mustParse[*promParser.VectorSelector](t, `foo{job="myjob"}`, 28), + Aggregation: mustParse[*promParser.AggregateExpr](t, `min(foo{job="myjob"})`, 24), + FixedLabels: true, + ExcludeReason: map[string]utils.ExcludedLabel{ + "": { + Reason: "Query is using aggregation that removes all labels.", + Fragment: `min(foo{job="myjob"})`, + }, }, }, }, @@ -757,6 +807,7 @@ func TestLabelsSource(t *testing.T) { Returns: promParser.ValueTypeVector, Operation: "avg", Selector: mustParse[*promParser.VectorSelector](t, `foo{job="myjob"}`, 4), + Aggregation: mustParse[*promParser.AggregateExpr](t, `avg(foo{job="myjob"}) by(job)`, 0), GuaranteedLabels: []string{"job"}, IncludedLabels: []string{"job"}, FixedLabels: true, @@ -777,6 +828,7 @@ func TestLabelsSource(t *testing.T) { Returns: promParser.ValueTypeVector, Operation: "group", Selector: mustParse[*promParser.VectorSelector](t, `foo`, 6), + Aggregation: mustParse[*promParser.AggregateExpr](t, `group(foo) by(job)`, 0), IncludedLabels: []string{"job"}, FixedLabels: true, ExcludeReason: map[string]utils.ExcludedLabel{ @@ -796,6 +848,8 @@ func TestLabelsSource(t *testing.T) { Returns: promParser.ValueTypeVector, Operation: "stddev", Selector: mustParse[*promParser.VectorSelector](t, `foo`, 12), + Call: mustParse[*promParser.Call](t, "rate(foo[5m])", 7), + Aggregation: mustParse[*promParser.AggregateExpr](t, `stddev(rate(foo[5m]))`, 0), FixedLabels: true, ExcludeReason: map[string]utils.ExcludedLabel{ "": { @@ -814,6 +868,8 @@ func TestLabelsSource(t *testing.T) { Returns: promParser.ValueTypeVector, Operation: "stdvar", Selector: mustParse[*promParser.VectorSelector](t, `foo`, 12), + Call: mustParse[*promParser.Call](t, "rate(foo[5m])", 7), + Aggregation: mustParse[*promParser.AggregateExpr](t, `stdvar(rate(foo[5m]))`, 0), FixedLabels: true, ExcludeReason: map[string]utils.ExcludedLabel{ "": { @@ -856,6 +912,8 @@ func TestLabelsSource(t *testing.T) { Returns: promParser.ValueTypeVector, Operation: "quantile", Selector: mustParse[*promParser.VectorSelector](t, `foo`, 19), + Call: mustParse[*promParser.Call](t, "rate(foo[5m])", 14), + Aggregation: mustParse[*promParser.AggregateExpr](t, `quantile(0.9, rate(foo[5m]))`, 0), FixedLabels: true, ExcludeReason: map[string]utils.ExcludedLabel{ "": { @@ -874,6 +932,7 @@ func TestLabelsSource(t *testing.T) { Returns: promParser.ValueTypeVector, Operation: "count_values", Selector: mustParse[*promParser.VectorSelector](t, `build_version`, 24), + Aggregation: mustParse[*promParser.AggregateExpr](t, `count_values("version", build_version)`, 0), GuaranteedLabels: []string{"version"}, IncludedLabels: []string{"version"}, FixedLabels: true, @@ -894,6 +953,7 @@ func TestLabelsSource(t *testing.T) { Returns: promParser.ValueTypeVector, Operation: "count_values", Selector: mustParse[*promParser.VectorSelector](t, `build_version`, 24), + Aggregation: mustParse[*promParser.AggregateExpr](t, `count_values("version", build_version) without(job)`, 0), IncludedLabels: []string{"version"}, GuaranteedLabels: []string{"version"}, ExcludedLabels: []string{"job"}, @@ -914,6 +974,7 @@ func TestLabelsSource(t *testing.T) { Returns: promParser.ValueTypeVector, Operation: "count_values", Selector: mustParse[*promParser.VectorSelector](t, `build_version{job="foo"}`, 24), + Aggregation: mustParse[*promParser.AggregateExpr](t, `count_values("version", build_version{job="foo"}) without(job)`, 0), IncludedLabels: []string{"version"}, GuaranteedLabels: []string{"version"}, ExcludedLabels: []string{"job"}, @@ -934,6 +995,7 @@ func TestLabelsSource(t *testing.T) { Returns: promParser.ValueTypeVector, Operation: "count_values", Selector: mustParse[*promParser.VectorSelector](t, `build_version`, 24), + Aggregation: mustParse[*promParser.AggregateExpr](t, `count_values("version", build_version) by(job)`, 0), GuaranteedLabels: []string{"version"}, IncludedLabels: []string{"job", "version"}, FixedLabels: true, @@ -954,6 +1016,7 @@ func TestLabelsSource(t *testing.T) { Returns: promParser.ValueTypeVector, Operation: "topk", Selector: mustParse[*promParser.VectorSelector](t, `foo{job="myjob"}`, 9), + Aggregation: mustParse[*promParser.AggregateExpr](t, `topk(10, foo{job="myjob"})`, 0), GuaranteedLabels: []string{"job"}, IsConditional: true, }, @@ -963,16 +1026,18 @@ func TestLabelsSource(t *testing.T) { expr: `topk(10, foo or bar)`, output: []utils.Source{ { - Type: utils.AggregateSource, - Returns: promParser.ValueTypeVector, - Operation: "topk", - Selector: mustParse[*promParser.VectorSelector](t, `foo`, 9), + Type: utils.AggregateSource, + Returns: promParser.ValueTypeVector, + Operation: "topk", + Selector: mustParse[*promParser.VectorSelector](t, `foo`, 9), + Aggregation: mustParse[*promParser.AggregateExpr](t, `topk(10, foo or bar)`, 0), }, { - Type: utils.AggregateSource, - Operation: "topk", - Returns: promParser.ValueTypeVector, - Selector: mustParse[*promParser.VectorSelector](t, `bar`, 16), + Type: utils.AggregateSource, + Operation: "topk", + Returns: promParser.ValueTypeVector, + Selector: mustParse[*promParser.VectorSelector](t, `bar`, 16), + Aggregation: mustParse[*promParser.AggregateExpr](t, `topk(10, foo or bar)`, 0), }, }, }, @@ -996,6 +1061,8 @@ func TestLabelsSource(t *testing.T) { Returns: promParser.ValueTypeVector, Operation: "sum", Selector: mustParse[*promParser.VectorSelector](t, `foo`, 9), + Call: mustParse[*promParser.Call](t, "rate(foo[10m])", 4), + Aggregation: mustParse[*promParser.AggregateExpr](t, `sum(rate(foo[10m])) without(instance)`, 0), ExcludedLabels: []string{"instance"}, ExcludeReason: map[string]utils.ExcludedLabel{ "instance": { @@ -1015,11 +1082,13 @@ func TestLabelsSource(t *testing.T) { Operation: promParser.CardOneToOne.String(), Selector: mustParse[*promParser.VectorSelector](t, `foo{job="foo"}`, 0), GuaranteedLabels: []string{"job"}, - Joins: []utils.Source{ + Joins: []utils.Join{ { - Type: utils.SelectorSource, - Returns: promParser.ValueTypeVector, - Selector: mustParse[*promParser.VectorSelector](t, `bar`, 17), + Src: utils.Source{ + Type: utils.SelectorSource, + Returns: promParser.ValueTypeVector, + Selector: mustParse[*promParser.VectorSelector](t, `bar`, 17), + }, }, }, }, @@ -1042,11 +1111,13 @@ func TestLabelsSource(t *testing.T) { Fragment: `foo{job="foo"} * on(instance) bar`, }, }, - Joins: []utils.Source{ + Joins: []utils.Join{ { - Type: utils.SelectorSource, - Returns: promParser.ValueTypeVector, - Selector: mustParse[*promParser.VectorSelector](t, `bar`, 30), + Src: utils.Source{ + Type: utils.SelectorSource, + Returns: promParser.ValueTypeVector, + Selector: mustParse[*promParser.VectorSelector](t, `bar`, 30), + }, }, }, }, @@ -1062,11 +1133,13 @@ func TestLabelsSource(t *testing.T) { Selector: mustParse[*promParser.VectorSelector](t, `foo{job="foo"}`, 0), IncludedLabels: []string{"bar", "instance"}, GuaranteedLabels: []string{"job"}, - Joins: []utils.Source{ + Joins: []utils.Join{ { - Type: utils.SelectorSource, - Returns: promParser.ValueTypeVector, - Selector: mustParse[*promParser.VectorSelector](t, `bar`, 46), + Src: utils.Source{ + Type: utils.SelectorSource, + Returns: promParser.ValueTypeVector, + Selector: mustParse[*promParser.VectorSelector](t, `bar`, 46), + }, }, }, }, @@ -1082,12 +1155,14 @@ func TestLabelsSource(t *testing.T) { Selector: mustParse[*promParser.VectorSelector](t, `foo{job="foo"}`, 0), IncludedLabels: []string{"cluster", "instance"}, GuaranteedLabels: []string{"job"}, - Joins: []utils.Source{ + Joins: []utils.Join{ { - Type: utils.SelectorSource, - Returns: promParser.ValueTypeVector, - Selector: mustParse[*promParser.VectorSelector](t, `bar{cluster="bar", ignored="true"}`, 50), - GuaranteedLabels: []string{"cluster", "ignored"}, + Src: utils.Source{ + Type: utils.SelectorSource, + Returns: promParser.ValueTypeVector, + Selector: mustParse[*promParser.VectorSelector](t, `bar{cluster="bar", ignored="true"}`, 50), + GuaranteedLabels: []string{"cluster"}, + }, }, }, }, @@ -1103,12 +1178,14 @@ func TestLabelsSource(t *testing.T) { Selector: mustParse[*promParser.VectorSelector](t, `bar{cluster="bar"}`, 63), IncludedLabels: []string{"job", "instance"}, GuaranteedLabels: []string{"cluster"}, - Joins: []utils.Source{ + Joins: []utils.Join{ { - Type: utils.SelectorSource, - Returns: promParser.ValueTypeVector, - Selector: mustParse[*promParser.VectorSelector](t, `foo{job="foo", ignored="true"}`, 0), - GuaranteedLabels: []string{"job", "ignored"}, + Src: utils.Source{ + Type: utils.SelectorSource, + Returns: promParser.ValueTypeVector, + Selector: mustParse[*promParser.VectorSelector](t, `foo{job="foo", ignored="true"}`, 0), + GuaranteedLabels: []string{"job"}, + }, }, }, }, @@ -1122,6 +1199,7 @@ func TestLabelsSource(t *testing.T) { Returns: promParser.ValueTypeVector, Operation: "count", Selector: mustParse[*promParser.VectorSelector](t, `foo`, 6), + Aggregation: mustParse[*promParser.AggregateExpr](t, `count(foo / bar)`, 0), FixedLabels: true, ExcludeReason: map[string]utils.ExcludedLabel{ "": { @@ -1129,11 +1207,13 @@ func TestLabelsSource(t *testing.T) { Fragment: `count(foo / bar)`, }, }, - Joins: []utils.Source{ + Joins: []utils.Join{ { - Type: utils.SelectorSource, - Returns: promParser.ValueTypeVector, - Selector: mustParse[*promParser.VectorSelector](t, `bar`, 12), + Src: utils.Source{ + Type: utils.SelectorSource, + Returns: promParser.ValueTypeVector, + Selector: mustParse[*promParser.VectorSelector](t, `bar`, 12), + }, }, }, }, @@ -1147,6 +1227,7 @@ func TestLabelsSource(t *testing.T) { Returns: promParser.ValueTypeVector, Operation: "count", Selector: mustParse[*promParser.VectorSelector](t, `up{job="a"}`, 6), + Aggregation: mustParse[*promParser.AggregateExpr](t, `count(up{job="a"} / on () up{job="b"})`, 0), FixedLabels: true, ExcludeReason: map[string]utils.ExcludedLabel{ "": { @@ -1154,12 +1235,14 @@ func TestLabelsSource(t *testing.T) { Fragment: `count(up{job="a"} / on () up{job="b"})`, }, }, - Joins: []utils.Source{ + Joins: []utils.Join{ { - Type: utils.SelectorSource, - Returns: promParser.ValueTypeVector, - Selector: mustParse[*promParser.VectorSelector](t, `up{job="b"}`, 26), - GuaranteedLabels: []string{"job"}, + Src: utils.Source{ + Type: utils.SelectorSource, + Returns: promParser.ValueTypeVector, + Selector: mustParse[*promParser.VectorSelector](t, `up{job="b"}`, 26), + GuaranteedLabels: []string{"job"}, + }, }, }, }, @@ -1173,6 +1256,7 @@ func TestLabelsSource(t *testing.T) { Returns: promParser.ValueTypeVector, Operation: "count", Selector: mustParse[*promParser.VectorSelector](t, `up{job="a"}`, 6), + Aggregation: mustParse[*promParser.AggregateExpr](t, `count(up{job="a"} / on (env) up{job="b"})`, 0), FixedLabels: true, ExcludeReason: map[string]utils.ExcludedLabel{ "": { @@ -1180,12 +1264,14 @@ func TestLabelsSource(t *testing.T) { Fragment: `count(up{job="a"} / on (env) up{job="b"})`, }, }, - Joins: []utils.Source{ + Joins: []utils.Join{ { - Type: utils.SelectorSource, - Returns: promParser.ValueTypeVector, - Selector: mustParse[*promParser.VectorSelector](t, `up{job="b"}`, 29), - GuaranteedLabels: []string{"job"}, + Src: utils.Source{ + Type: utils.SelectorSource, + Returns: promParser.ValueTypeVector, + Selector: mustParse[*promParser.VectorSelector](t, `up{job="b"}`, 29), + GuaranteedLabels: []string{"job"}, + }, }, }, }, @@ -1200,11 +1286,13 @@ func TestLabelsSource(t *testing.T) { Operation: promParser.CardManyToMany.String(), Selector: mustParse[*promParser.VectorSelector](t, `foo{job="foo", instance="1"}`, 0), GuaranteedLabels: []string{"job", "instance"}, - Joins: []utils.Source{ + Joins: []utils.Join{ { - Type: utils.SelectorSource, - Returns: promParser.ValueTypeVector, - Selector: mustParse[*promParser.VectorSelector](t, `bar`, 33), + Src: utils.Source{ + Type: utils.SelectorSource, + Returns: promParser.ValueTypeVector, + Selector: mustParse[*promParser.VectorSelector](t, `bar`, 33), + }, }, }, }, @@ -1220,11 +1308,13 @@ func TestLabelsSource(t *testing.T) { Selector: mustParse[*promParser.VectorSelector](t, `foo{job="foo", instance="1"}`, 0), IncludedLabels: []string{"cluster"}, GuaranteedLabels: []string{"job", "instance"}, - Joins: []utils.Source{ + Joins: []utils.Join{ { - Type: utils.SelectorSource, - Returns: promParser.ValueTypeVector, - Selector: mustParse[*promParser.VectorSelector](t, `bar`, 45), + Src: utils.Source{ + Type: utils.SelectorSource, + Returns: promParser.ValueTypeVector, + Selector: mustParse[*promParser.VectorSelector](t, `bar`, 45), + }, }, }, }, @@ -1234,10 +1324,11 @@ func TestLabelsSource(t *testing.T) { expr: `topk(10, foo)`, output: []utils.Source{ { - Type: utils.AggregateSource, - Returns: promParser.ValueTypeVector, - Operation: "topk", - Selector: mustParse[*promParser.VectorSelector](t, `foo`, 9), + Type: utils.AggregateSource, + Returns: promParser.ValueTypeVector, + Operation: "topk", + Selector: mustParse[*promParser.VectorSelector](t, `foo`, 9), + Aggregation: mustParse[*promParser.AggregateExpr](t, `topk(10, foo)`, 0), }, }, }, @@ -1245,10 +1336,11 @@ func TestLabelsSource(t *testing.T) { expr: `topk(10, foo) without(cluster)`, output: []utils.Source{ { - Type: utils.AggregateSource, - Returns: promParser.ValueTypeVector, - Operation: "topk", - Selector: mustParse[*promParser.VectorSelector](t, `foo`, 9), + Type: utils.AggregateSource, + Returns: promParser.ValueTypeVector, + Operation: "topk", + Selector: mustParse[*promParser.VectorSelector](t, `foo`, 9), + Aggregation: mustParse[*promParser.AggregateExpr](t, `topk(10, foo) without(cluster)`, 0), }, }, }, @@ -1256,10 +1348,11 @@ func TestLabelsSource(t *testing.T) { expr: `topk(10, foo) by(cluster)`, output: []utils.Source{ { - Type: utils.AggregateSource, - Returns: promParser.ValueTypeVector, - Operation: "topk", - Selector: mustParse[*promParser.VectorSelector](t, `foo`, 9), + Type: utils.AggregateSource, + Returns: promParser.ValueTypeVector, + Operation: "topk", + Selector: mustParse[*promParser.VectorSelector](t, `foo`, 9), + Aggregation: mustParse[*promParser.AggregateExpr](t, `topk(10, foo) by(cluster)`, 0), }, }, }, @@ -1271,6 +1364,8 @@ func TestLabelsSource(t *testing.T) { Returns: promParser.ValueTypeVector, Operation: "bottomk", Selector: mustParse[*promParser.VectorSelector](t, `foo`, 21), + Call: mustParse[*promParser.Call](t, "rate(foo[5m])", 16), + Aggregation: mustParse[*promParser.AggregateExpr](t, `bottomk(10, sum(rate(foo[5m])) without(job))`, 0), ExcludedLabels: []string{"job"}, ExcludeReason: map[string]utils.ExcludedLabel{ "job": { @@ -1352,11 +1447,13 @@ func TestLabelsSource(t *testing.T) { Returns: promParser.ValueTypeVector, Operation: promParser.CardManyToMany.String(), Selector: mustParse[*promParser.VectorSelector](t, `foo`, 0), - Unless: []utils.Source{ + Unless: []utils.Join{ { - Type: utils.SelectorSource, - Returns: promParser.ValueTypeVector, - Selector: mustParse[*promParser.VectorSelector](t, `bar`, 11), + Src: utils.Source{ + Type: utils.SelectorSource, + Returns: promParser.ValueTypeVector, + Selector: mustParse[*promParser.VectorSelector](t, `bar`, 11), + }, }, }, }, @@ -1370,12 +1467,14 @@ func TestLabelsSource(t *testing.T) { Returns: promParser.ValueTypeVector, Operation: promParser.CardManyToMany.String(), Selector: mustParse[*promParser.VectorSelector](t, `foo`, 0), - Unless: []utils.Source{ + Unless: []utils.Join{ { - Type: utils.SelectorSource, - Returns: promParser.ValueTypeVector, - Selector: mustParse[*promParser.VectorSelector](t, `bar`, 11), - IsConditional: true, + Src: utils.Source{ + Type: utils.SelectorSource, + Returns: promParser.ValueTypeVector, + Selector: mustParse[*promParser.VectorSelector](t, `bar`, 11), + IsConditional: true, + }, }, }, }, @@ -1389,16 +1488,20 @@ func TestLabelsSource(t *testing.T) { Returns: promParser.ValueTypeVector, Operation: promParser.CardManyToMany.String(), Selector: mustParse[*promParser.VectorSelector](t, `foo`, 0), - Unless: []utils.Source{ + Unless: []utils.Join{ { - Type: utils.SelectorSource, - Returns: promParser.ValueTypeVector, - Selector: mustParse[*promParser.VectorSelector](t, `bar`, 11), + Src: utils.Source{ + Type: utils.SelectorSource, + Returns: promParser.ValueTypeVector, + Selector: mustParse[*promParser.VectorSelector](t, `bar`, 11), + }, }, { - Type: utils.SelectorSource, - Returns: promParser.ValueTypeVector, - Selector: mustParse[*promParser.VectorSelector](t, `baz`, 22), + Src: utils.Source{ + Type: utils.SelectorSource, + Returns: promParser.ValueTypeVector, + Selector: mustParse[*promParser.VectorSelector](t, `baz`, 22), + }, }, }, }, @@ -1412,6 +1515,7 @@ func TestLabelsSource(t *testing.T) { Returns: promParser.ValueTypeVector, Operation: "count", Selector: mustParse[*promParser.VectorSelector](t, `up{job="foo", cluster="dev"}`, 10), + Aggregation: mustParse[*promParser.AggregateExpr](t, `count(sum(up{job="foo", cluster="dev"}) by(job, cluster) == 0) without(job, cluster)`, 0), ExcludedLabels: []string{"job", "cluster"}, // FIXME empty FixedLabels: true, ExcludeReason: map[string]utils.ExcludedLabel{ @@ -1491,29 +1595,35 @@ sum(foo:count) by(job) > 20`, Returns: promParser.ValueTypeVector, Operation: "sum", Selector: mustParse[*promParser.VectorSelector](t, `foo:sum`, 8), + Aggregation: mustParse[*promParser.AggregateExpr](t, `sum(foo:sum > 0) without(notify)`, 4), IncludedLabels: []string{"notify", "job"}, ExcludeReason: map[string]utils.ExcludedLabel{}, IsConditional: false, // FIXME should be true - Joins: []utils.Source{ + Joins: []utils.Join{ { - Type: utils.SelectorSource, - Returns: promParser.ValueTypeVector, - Selector: mustParse[*promParser.VectorSelector](t, `job:notify`, 68), + Src: utils.Source{ + Type: utils.SelectorSource, + Returns: promParser.ValueTypeVector, + Selector: mustParse[*promParser.VectorSelector](t, `job:notify`, 68), + }, }, { - Type: utils.AggregateSource, - Returns: promParser.ValueTypeVector, - Operation: "sum", - Selector: mustParse[*promParser.VectorSelector](t, `foo:count`, 97), - IncludedLabels: []string{"job"}, - FixedLabels: true, - ExcludeReason: map[string]utils.ExcludedLabel{ - "": { - Reason: "Query is using aggregation with `by(job)`, only labels included inside `by(...)` will be present on the results.", - Fragment: `sum(foo:count) by(job)`, + Src: utils.Source{ + Type: utils.AggregateSource, + Returns: promParser.ValueTypeVector, + Operation: "sum", + Selector: mustParse[*promParser.VectorSelector](t, `foo:count`, 97), + Aggregation: mustParse[*promParser.AggregateExpr](t, `sum(foo:count) by(job)`, 93), + IncludedLabels: []string{"job"}, + FixedLabels: true, + ExcludeReason: map[string]utils.ExcludedLabel{ + "": { + Reason: "Query is using aggregation with `by(job)`, only labels included inside `by(...)` will be present on the results.", + Fragment: `sum(foo:count) by(job)`, + }, }, + IsConditional: true, }, - IsConditional: true, }, }, }, @@ -1535,12 +1645,14 @@ sum(foo:count) by(job) > 20`, Fragment: `container_file_descriptors / on (instance, app_name) container_ulimits_soft{ulimit="max_open_files"}`, }, }, - Joins: []utils.Source{ + Joins: []utils.Join{ { - Type: utils.SelectorSource, - Returns: promParser.ValueTypeVector, - Selector: mustParse[*promParser.VectorSelector](t, `container_ulimits_soft{ulimit="max_open_files"}`, 53), - GuaranteedLabels: []string{"ulimit"}, + Src: utils.Source{ + Type: utils.SelectorSource, + Returns: promParser.ValueTypeVector, + Selector: mustParse[*promParser.VectorSelector](t, `container_ulimits_soft{ulimit="max_open_files"}`, 53), + GuaranteedLabels: []string{"ulimit"}, + }, }, }, }, @@ -1555,12 +1667,13 @@ sum(foo:count) by(job) > 20`, Operation: promParser.CardManyToOne.String(), Selector: mustParse[*promParser.VectorSelector](t, `container_file_descriptors`, 0), IncludedLabels: []string{"instance", "app_name"}, - Joins: []utils.Source{ + Joins: []utils.Join{ { - Type: utils.SelectorSource, - Returns: promParser.ValueTypeVector, - Selector: mustParse[*promParser.VectorSelector](t, `container_ulimits_soft{ulimit="max_open_files"}`, 66), - GuaranteedLabels: []string{"ulimit"}, + Src: utils.Source{ + Type: utils.SelectorSource, + Returns: promParser.ValueTypeVector, + Selector: mustParse[*promParser.VectorSelector](t, `container_ulimits_soft{ulimit="max_open_files"}`, 66), + }, }, }, }, @@ -1616,6 +1729,7 @@ sum(foo:count) by(job) > 20`, Returns: promParser.ValueTypeVector, Operation: "absent", Selector: mustParse[*promParser.VectorSelector](t, `foo`, 11), + Aggregation: mustParse[*promParser.AggregateExpr](t, `sum(foo) by(job, instance)`, 7), FixedLabels: true, ExcludeReason: map[string]utils.ExcludedLabel{ "": { @@ -1645,11 +1759,13 @@ sum(foo:count) by(job) > 20`, }, }, Call: mustParse[*promParser.Call](t, `absent(foo{job="prometheus", xxx="1"})`, 0), - Joins: []utils.Source{ + Joins: []utils.Join{ { - Type: utils.SelectorSource, - Returns: promParser.ValueTypeVector, - Selector: mustParse[*promParser.VectorSelector](t, "prometheus_build_info", 51), + Src: utils.Source{ + Type: utils.SelectorSource, + Returns: promParser.ValueTypeVector, + Selector: mustParse[*promParser.VectorSelector](t, "prometheus_build_info", 51), + }, }, }, }, @@ -1663,6 +1779,7 @@ sum(foo:count) by(job) > 20`, Returns: promParser.ValueTypeVector, Operation: "sum", Selector: mustParse[*promParser.VectorSelector](t, `foo`, 8), + Aggregation: mustParse[*promParser.AggregateExpr](t, `sum(foo) by(notjob)`, 4), IncludedLabels: []string{"notjob"}, FixedLabels: true, ExcludeReason: map[string]utils.ExcludedLabel{ @@ -1682,7 +1799,8 @@ sum(foo:count) by(job) > 20`, Returns: promParser.ValueTypeVector, Operation: "count", Selector: mustParse[*promParser.VectorSelector](t, `node_exporter_build_info`, 6), - IncludedLabels: []string{"instance", "version", "foo"}, // FIXME foo shouldn't be there because count() doesn't produce it + Aggregation: mustParse[*promParser.AggregateExpr](t, `count(node_exporter_build_info) by (instance, version)`, 0), + IncludedLabels: []string{"instance", "version", "foo"}, FixedLabels: true, ExcludeReason: map[string]utils.ExcludedLabel{ "": { @@ -1691,18 +1809,21 @@ sum(foo:count) by(job) > 20`, }, }, IsConditional: true, - Joins: []utils.Source{ + Joins: []utils.Join{ { - Type: utils.AggregateSource, - Returns: promParser.ValueTypeVector, - Operation: "count", - Selector: mustParse[*promParser.VectorSelector](t, "deb_package_version", 106), - IncludedLabels: []string{"instance", "version", "package"}, - FixedLabels: true, - ExcludeReason: map[string]utils.ExcludedLabel{ - "": { - Reason: "Query is using aggregation with `by(instance, version, package)`, only labels included inside `by(...)` will be present on the results.", - Fragment: `count(deb_package_version) by (instance, version, package)`, + Src: utils.Source{ + Type: utils.AggregateSource, + Returns: promParser.ValueTypeVector, + Operation: "count", + Selector: mustParse[*promParser.VectorSelector](t, "deb_package_version", 106), + Aggregation: mustParse[*promParser.AggregateExpr](t, `count(deb_package_version) by (instance, version, package)`, 100), + IncludedLabels: []string{"instance", "version", "package"}, + FixedLabels: true, + ExcludeReason: map[string]utils.ExcludedLabel{ + "": { + Reason: "Query is using aggregation with `by(instance, version, package)`, only labels included inside `by(...)` will be present on the results.", + Fragment: `count(deb_package_version) by (instance, version, package)`, + }, }, }, }, @@ -1794,11 +1915,13 @@ sum(foo:count) by(job) > 20`, }, }, Call: mustParse[*promParser.Call](t, `absent(foo{job="xxx"})`, 37), - Joins: []utils.Source{ + Joins: []utils.Join{ { - Type: utils.SelectorSource, - Returns: promParser.ValueTypeVector, - Selector: mustParse[*promParser.VectorSelector](t, "bar", 0), + Src: utils.Source{ + Type: utils.SelectorSource, + Returns: promParser.ValueTypeVector, + Selector: mustParse[*promParser.VectorSelector](t, "bar", 0), + }, }, }, }, @@ -1822,11 +1945,13 @@ sum(foo:count) by(job) > 20`, }, }, Call: mustParse[*promParser.Call](t, `absent(foo{job="xxx"})`, 25), - Joins: []utils.Source{ + Joins: []utils.Join{ { - Type: utils.SelectorSource, - Returns: promParser.ValueTypeVector, - Selector: mustParse[*promParser.VectorSelector](t, "bar", 0), + Src: utils.Source{ + Type: utils.SelectorSource, + Returns: promParser.ValueTypeVector, + Selector: mustParse[*promParser.VectorSelector](t, "bar", 0), + }, }, }, }, @@ -1836,12 +1961,12 @@ sum(foo:count) by(job) > 20`, expr: "vector(1)", output: []utils.Source{ { - Type: utils.FuncSource, - Returns: promParser.ValueTypeVector, - Operation: "vector", - FixedLabels: true, - AlwaysReturns: true, - ReturnedNumbers: []float64{1}, + Type: utils.FuncSource, + Returns: promParser.ValueTypeVector, + Operation: "vector", + FixedLabels: true, + AlwaysReturns: true, + ReturnedNumber: 1, ExcludeReason: map[string]utils.ExcludedLabel{ "": { Reason: "Calling `vector()` will return a vector value with no labels.", @@ -1880,6 +2005,8 @@ sum(foo:count) by(job) > 20`, Operation: "vector", FixedLabels: true, AlwaysReturns: true, + IsDead: true, + IsDeadReason: "this query always evaluates to `0 == 1` which is not possible, so it will never return anything", ExcludeReason: map[string]utils.ExcludedLabel{ "": { Reason: "Calling `vector()` will return a vector value with no labels.", @@ -1957,6 +2084,7 @@ sum(foo:count) by(job) > 20`, Returns: promParser.ValueTypeVector, Operation: "label_replace", Selector: mustParse[*promParser.VectorSelector](t, `pod_status`, 28), + Aggregation: mustParse[*promParser.AggregateExpr](t, `sum by (pod) (pod_status)`, 14), FixedLabels: true, IsConditional: true, IncludedLabels: []string{"pod"}, @@ -1998,12 +2126,14 @@ sum(foo:count) by(job) > 20`, Fragment: `up{instance="a", job="prometheus"} * ignoring(job) up{instance="a", job="pint"}`, }, }, - Joins: []utils.Source{ + Joins: []utils.Join{ { - Type: utils.SelectorSource, - Returns: promParser.ValueTypeVector, - Selector: mustParse[*promParser.VectorSelector](t, `up{instance="a", job="pint"}`, 51), - GuaranteedLabels: []string{"instance", "job"}, + Src: utils.Source{ + Type: utils.SelectorSource, + Returns: promParser.ValueTypeVector, + Selector: mustParse[*promParser.VectorSelector](t, `up{instance="a", job="pint"}`, 51), + GuaranteedLabels: []string{"instance", "job"}, + }, }, }, }, @@ -2020,10 +2150,12 @@ or avg without(router, colo_id, instance) (router_anycast_prefix_enabled{cidr_us `, output: []utils.Source{ { - Type: utils.AggregateSource, - Returns: promParser.ValueTypeVector, - Operation: "avg", - Selector: mustParse[*promParser.VectorSelector](t, `router_anycast_prefix_enabled{cidr_use_case!~".*offpeak.*"}`, 41), + Type: utils.AggregateSource, + Returns: promParser.ValueTypeVector, + Operation: "avg", + Selector: mustParse[*promParser.VectorSelector](t, `router_anycast_prefix_enabled{cidr_use_case!~".*offpeak.*"}`, 41), + Aggregation: mustParse[*promParser.AggregateExpr](t, `avg without(router, colo_id, instance) (router_anycast_prefix_enabled{cidr_use_case!~".*offpeak.*"})`, 1), + ExcludedLabels: []string{"router", "colo_id", "instance"}, ExcludeReason: map[string]utils.ExcludedLabel{ "router": { @@ -2042,10 +2174,12 @@ or avg without(router, colo_id, instance) (router_anycast_prefix_enabled{cidr_us IsConditional: true, }, { - Type: utils.AggregateSource, - Returns: promParser.ValueTypeVector, - Operation: "sum", - Selector: mustParse[*promParser.VectorSelector](t, `router_anycast_prefix_enabled{cidr_use_case=~".*tier1.*"}`, 155), + Type: utils.AggregateSource, + Returns: promParser.ValueTypeVector, + Operation: "sum", + Selector: mustParse[*promParser.VectorSelector](t, `router_anycast_prefix_enabled{cidr_use_case=~".*tier1.*"}`, 155), + Aggregation: mustParse[*promParser.AggregateExpr](t, `sum without(router, colo_id, instance) (router_anycast_prefix_enabled{cidr_use_case=~".*tier1.*"})`, 115), + GuaranteedLabels: []string{"cidr_use_case"}, ExcludedLabels: []string{"router", "colo_id", "instance"}, FixedLabels: true, @@ -2068,17 +2202,20 @@ or avg without(router, colo_id, instance) (router_anycast_prefix_enabled{cidr_us }, }, IsConditional: true, - Joins: []utils.Source{ + Joins: []utils.Join{ { - Type: utils.AggregateSource, - Returns: promParser.ValueTypeVector, - Operation: "count", - Selector: mustParse[*promParser.VectorSelector](t, `colo_router_tier:disabled_pops:max{tier="1",router=~"edge.*"}`, 227), - FixedLabels: true, - ExcludeReason: map[string]utils.ExcludedLabel{ - "": { - Reason: "Query is using aggregation that removes all labels.", - Fragment: `count(colo_router_tier:disabled_pops:max{tier="1",router=~"edge.*"})`, + Src: utils.Source{ + Type: utils.AggregateSource, + Returns: promParser.ValueTypeVector, + Operation: "count", + Selector: mustParse[*promParser.VectorSelector](t, `colo_router_tier:disabled_pops:max{tier="1",router=~"edge.*"}`, 227), + Aggregation: mustParse[*promParser.AggregateExpr](t, `count(colo_router_tier:disabled_pops:max{tier="1",router=~"edge.*"})`, 221), + FixedLabels: true, + ExcludeReason: map[string]utils.ExcludedLabel{ + "": { + Reason: "Query is using aggregation that removes all labels.", + Fragment: `count(colo_router_tier:disabled_pops:max{tier="1",router=~"edge.*"})`, + }, }, }, }, @@ -2089,6 +2226,7 @@ or avg without(router, colo_id, instance) (router_anycast_prefix_enabled{cidr_us Returns: promParser.ValueTypeVector, Operation: "avg", Selector: mustParse[*promParser.VectorSelector](t, `router_anycast_prefix_enabled{cidr_use_case=~".*regional.*"}`, 343), + Aggregation: mustParse[*promParser.AggregateExpr](t, `avg without(router, colo_id, instance) (router_anycast_prefix_enabled{cidr_use_case=~".*regional.*"})`, 303), GuaranteedLabels: []string{"cidr_use_case"}, ExcludedLabels: []string{"router", "colo_id", "instance"}, ExcludeReason: map[string]utils.ExcludedLabel{ @@ -2117,6 +2255,7 @@ or avg without(router, colo_id, instance) (router_anycast_prefix_enabled{cidr_us Returns: promParser.ValueTypeVector, Operation: "label_replace", Selector: mustParse[*promParser.VectorSelector](t, `foo`, 18), + Aggregation: mustParse[*promParser.AggregateExpr](t, `sum(foo) without(instance)`, 14), GuaranteedLabels: []string{"instance"}, ExcludeReason: map[string]utils.ExcludedLabel{}, Call: mustParse[*promParser.Call](t, `label_replace(sum(foo) without(instance), "instance", "none", "", "")`, 0), @@ -2132,10 +2271,17 @@ sum by (region, target, colo_name) ( ) == 0`, output: []utils.Source{ { - Type: utils.AggregateSource, - Returns: promParser.ValueTypeVector, - Operation: "sum", - Selector: mustParse[*promParser.VectorSelector](t, `probe_success{job="abc"}`, 56), + Type: utils.AggregateSource, + Returns: promParser.ValueTypeVector, + Operation: "sum", + Selector: mustParse[*promParser.VectorSelector](t, `probe_success{job="abc"}`, 56), + Call: mustParse[*promParser.Call](t, `sum_over_time(probe_success{job="abc"}[5m])`, 42), + Aggregation: mustParse[*promParser.AggregateExpr](t, ` +sum by (region, target, colo_name) ( + sum_over_time(probe_success{job="abc"}[5m]) + or + vector(1) +)`, 0), // FIXME 0? should be 1 IncludedLabels: []string{"region", "target", "colo_name"}, FixedLabels: true, ExcludeReason: map[string]utils.ExcludedLabel{ @@ -2147,13 +2293,21 @@ sum by (region, target, colo_name) ( IsConditional: true, }, { - Type: utils.AggregateSource, - Returns: promParser.ValueTypeVector, - Operation: "sum", - FixedLabels: true, - AlwaysReturns: true, - IsDead: true, - ReturnedNumbers: []float64{1}, + Type: utils.AggregateSource, + Returns: promParser.ValueTypeVector, + Operation: "sum", + Call: mustParse[*promParser.Call](t, `vector(1)`, 91), + Aggregation: mustParse[*promParser.AggregateExpr](t, ` +sum by (region, target, colo_name) ( + sum_over_time(probe_success{job="abc"}[5m]) + or + vector(1) +)`, 0), // FIXME 0? should be 1 + FixedLabels: true, + AlwaysReturns: true, + IsDead: true, + IsDeadReason: "this query always evaluates to `1 == 0` which is not possible, so it will never return anything", + ReturnedNumber: 1, ExcludeReason: map[string]utils.ExcludedLabel{ "": { Reason: "Calling `vector()` will return a vector value with no labels.", @@ -2168,12 +2322,12 @@ sum by (region, target, colo_name) ( expr: `vector(1) or foo`, output: []utils.Source{ { - Type: utils.FuncSource, - Returns: promParser.ValueTypeVector, - Operation: "vector", - FixedLabels: true, - AlwaysReturns: true, - ReturnedNumbers: []float64{1}, + Type: utils.FuncSource, + Returns: promParser.ValueTypeVector, + Operation: "vector", + FixedLabels: true, + AlwaysReturns: true, + ReturnedNumber: 1, ExcludeReason: map[string]utils.ExcludedLabel{ "": { Reason: "Calling `vector()` will return a vector value with no labels.", @@ -2183,11 +2337,12 @@ sum by (region, target, colo_name) ( Call: mustParse[*promParser.Call](t, "vector(1)", 0), }, { - Type: utils.SelectorSource, - Operation: promParser.CardManyToMany.String(), - Returns: promParser.ValueTypeVector, - Selector: mustParse[*promParser.VectorSelector](t, "foo", 13), - IsDead: true, + Type: utils.SelectorSource, + Operation: promParser.CardManyToMany.String(), + Returns: promParser.ValueTypeVector, + Selector: mustParse[*promParser.VectorSelector](t, "foo", 13), + IsDead: true, + IsDeadReason: "the left hand side always returs something and so the right hand side is never used", }, }, }, @@ -2195,13 +2350,14 @@ sum by (region, target, colo_name) ( expr: `vector(0) > 0`, output: []utils.Source{ { - Type: utils.FuncSource, - Returns: promParser.ValueTypeVector, - Operation: "vector", - FixedLabels: true, - AlwaysReturns: true, - ReturnedNumbers: []float64{0}, - IsDead: true, + Type: utils.FuncSource, + Returns: promParser.ValueTypeVector, + Operation: "vector", + FixedLabels: true, + AlwaysReturns: true, + ReturnedNumber: 0, + IsDead: true, + IsDeadReason: "this query always evaluates to `0 > 0` which is not possible, so it will never return anything", ExcludeReason: map[string]utils.ExcludedLabel{ "": { Reason: "Calling `vector()` will return a vector value with no labels.", @@ -2221,6 +2377,7 @@ sum by (region, target, colo_name) ( Returns: promParser.ValueTypeVector, Operation: "sum", Selector: mustParse[*promParser.VectorSelector](t, `foo`, 4), + Aggregation: mustParse[*promParser.AggregateExpr](t, `sum(foo or vector(0))`, 0), FixedLabels: true, ExcludeReason: map[string]utils.ExcludedLabel{ "": { @@ -2231,13 +2388,16 @@ sum by (region, target, colo_name) ( IsConditional: true, }, { - Type: utils.AggregateSource, - Returns: promParser.ValueTypeVector, - Operation: "sum", - FixedLabels: true, - AlwaysReturns: true, - ReturnedNumbers: []float64{0}, - IsDead: true, + Type: utils.AggregateSource, + Returns: promParser.ValueTypeVector, + Operation: "sum", + Call: mustParse[*promParser.Call](t, `vector(0)`, 11), + Aggregation: mustParse[*promParser.AggregateExpr](t, `sum(foo or vector(0))`, 0), + FixedLabels: true, + AlwaysReturns: true, + ReturnedNumber: 0, + IsDead: true, + IsDeadReason: "this query always evaluates to `0 > 0` which is not possible, so it will never return anything", ExcludeReason: map[string]utils.ExcludedLabel{ "": { Reason: "Query is using aggregation that removes all labels.", @@ -2256,6 +2416,7 @@ sum by (region, target, colo_name) ( Returns: promParser.ValueTypeVector, Operation: "sum", Selector: mustParse[*promParser.VectorSelector](t, `foo`, 5), + Aggregation: mustParse[*promParser.AggregateExpr](t, `sum(foo or vector(1))`, 1), FixedLabels: true, ExcludeReason: map[string]utils.ExcludedLabel{ "": { @@ -2266,13 +2427,16 @@ sum by (region, target, colo_name) ( IsConditional: true, }, { - Type: utils.AggregateSource, - Returns: promParser.ValueTypeVector, - Operation: "sum", - FixedLabels: true, - AlwaysReturns: true, - ReturnedNumbers: []float64{1}, - IsDead: true, + Type: utils.AggregateSource, + Returns: promParser.ValueTypeVector, + Operation: "sum", + Call: mustParse[*promParser.Call](t, `vector(1)`, 12), + Aggregation: mustParse[*promParser.AggregateExpr](t, `sum(foo or vector(1))`, 1), + FixedLabels: true, + AlwaysReturns: true, + ReturnedNumber: 1, + IsDead: true, + IsDeadReason: "this query always evaluates to `1 == 2` which is not possible, so it will never return anything", ExcludeReason: map[string]utils.ExcludedLabel{ "": { Reason: "Query is using aggregation that removes all labels.", @@ -2291,6 +2455,7 @@ sum by (region, target, colo_name) ( Returns: promParser.ValueTypeVector, Operation: "sum", Selector: mustParse[*promParser.VectorSelector](t, `foo`, 5), + Aggregation: mustParse[*promParser.AggregateExpr](t, `sum(foo or vector(1))`, 1), FixedLabels: true, ExcludeReason: map[string]utils.ExcludedLabel{ "": { @@ -2301,12 +2466,14 @@ sum by (region, target, colo_name) ( IsConditional: true, }, { - Type: utils.AggregateSource, - Returns: promParser.ValueTypeVector, - Operation: "sum", - FixedLabels: true, - AlwaysReturns: true, - ReturnedNumbers: []float64{1}, + Type: utils.AggregateSource, + Returns: promParser.ValueTypeVector, + Operation: "sum", + Call: mustParse[*promParser.Call](t, `vector(1)`, 12), + Aggregation: mustParse[*promParser.AggregateExpr](t, `sum(foo or vector(1))`, 1), + FixedLabels: true, + AlwaysReturns: true, + ReturnedNumber: 1, ExcludeReason: map[string]utils.ExcludedLabel{ "": { Reason: "Query is using aggregation that removes all labels.", @@ -2325,6 +2492,7 @@ sum by (region, target, colo_name) ( Returns: promParser.ValueTypeVector, Operation: "sum", Selector: mustParse[*promParser.VectorSelector](t, `foo`, 5), + Aggregation: mustParse[*promParser.AggregateExpr](t, `sum(foo or vector(2))`, 1), FixedLabels: true, ExcludeReason: map[string]utils.ExcludedLabel{ "": { @@ -2335,13 +2503,16 @@ sum by (region, target, colo_name) ( IsConditional: true, }, { - Type: utils.AggregateSource, - Returns: promParser.ValueTypeVector, - Operation: "sum", - FixedLabels: true, - AlwaysReturns: true, - ReturnedNumbers: []float64{2}, - IsDead: true, + Type: utils.AggregateSource, + Returns: promParser.ValueTypeVector, + Operation: "sum", + Call: mustParse[*promParser.Call](t, `vector(2)`, 12), + Aggregation: mustParse[*promParser.AggregateExpr](t, `sum(foo or vector(2))`, 1), + FixedLabels: true, + AlwaysReturns: true, + ReturnedNumber: 2, + IsDead: true, + IsDeadReason: "this query always evaluates to `2 != 2` which is not possible, so it will never return anything", ExcludeReason: map[string]utils.ExcludedLabel{ "": { Reason: "Query is using aggregation that removes all labels.", @@ -2353,8 +2524,7 @@ sum by (region, target, colo_name) ( }, }, { - expr: ` -(sum(sometimes{foo!="bar"} or vector(0))) + expr: `(sum(sometimes{foo!="bar"} or vector(0))) or ((bob > 10) or sum(foo) or vector(1))`, output: []utils.Source{ @@ -2362,7 +2532,9 @@ or Type: utils.AggregateSource, Returns: promParser.ValueTypeVector, Operation: "sum", - Selector: mustParse[*promParser.VectorSelector](t, `sometimes{foo!="bar"}`, 6), + Selector: mustParse[*promParser.VectorSelector](t, `sometimes{foo!="bar"}`, 5), + Aggregation: mustParse[*promParser.AggregateExpr](t, `sum(sometimes{foo!="bar"} or vector(0) )`, 1), // FIXME extra end + FixedLabels: true, ExcludeReason: map[string]utils.ExcludedLabel{ "": { @@ -2372,12 +2544,14 @@ or }, }, { - Type: utils.AggregateSource, - Returns: promParser.ValueTypeVector, - Operation: "sum", - AlwaysReturns: true, - ReturnedNumbers: []float64{0}, - FixedLabels: true, + Type: utils.AggregateSource, + Returns: promParser.ValueTypeVector, + Operation: "sum", + Call: mustParse[*promParser.Call](t, `vector(0)`, 30), + Aggregation: mustParse[*promParser.AggregateExpr](t, `sum(sometimes{foo!="bar"} or vector(0) )`, 1), // FIXME extra end + AlwaysReturns: true, + ReturnedNumber: 0, + FixedLabels: true, ExcludeReason: map[string]utils.ExcludedLabel{ "": { Reason: "Query is using aggregation that removes all labels.", @@ -2389,14 +2563,15 @@ or Type: utils.SelectorSource, Operation: promParser.CardManyToMany.String(), Returns: promParser.ValueTypeVector, - Selector: mustParse[*promParser.VectorSelector](t, `bob`, 48), + Selector: mustParse[*promParser.VectorSelector](t, `bob`, 47), IsConditional: true, }, { Type: utils.AggregateSource, Returns: promParser.ValueTypeVector, Operation: "sum", - Selector: mustParse[*promParser.VectorSelector](t, `foo`, 65), + Selector: mustParse[*promParser.VectorSelector](t, `foo`, 64), + Aggregation: mustParse[*promParser.AggregateExpr](t, `sum(foo)`, 60), FixedLabels: true, ExcludeReason: map[string]utils.ExcludedLabel{ "": { @@ -2406,13 +2581,13 @@ or }, }, { - Type: utils.FuncSource, - Returns: promParser.ValueTypeVector, - Operation: "vector", - AlwaysReturns: true, - ReturnedNumbers: []float64{1}, - FixedLabels: true, - Call: mustParse[*promParser.Call](t, "vector(1)", 73), + Type: utils.FuncSource, + Returns: promParser.ValueTypeVector, + Operation: "vector", + AlwaysReturns: true, + ReturnedNumber: 1, + FixedLabels: true, + Call: mustParse[*promParser.Call](t, "vector(1)", 72), ExcludeReason: map[string]utils.ExcludedLabel{ "": { Reason: "Calling `vector()` will return a vector value with no labels.", @@ -2439,6 +2614,7 @@ or Returns: promParser.ValueTypeVector, Operation: "sum", Selector: mustParse[*promParser.VectorSelector](t, `sometimes{foo!="bar"}`, 8), + Aggregation: mustParse[*promParser.AggregateExpr](t, `sum(sometimes{foo!="bar"})`, 4), FixedLabels: true, ExcludeReason: map[string]utils.ExcludedLabel{ "": { @@ -2446,77 +2622,91 @@ or Fragment: `sum(sometimes{foo!="bar"})`, }, }, - Joins: []utils.Source{ + Joins: []utils.Join{ { - Type: utils.SelectorSource, - Operation: promParser.CardManyToMany.String(), - Returns: promParser.ValueTypeVector, - Selector: mustParse[*promParser.VectorSelector](t, `bob`, 57), - IsConditional: true, + Src: utils.Source{ + Type: utils.SelectorSource, + Operation: promParser.CardManyToMany.String(), + Returns: promParser.ValueTypeVector, + Selector: mustParse[*promParser.VectorSelector](t, `bob`, 57), + IsConditional: true, + }, }, { - Type: utils.AggregateSource, - Returns: promParser.ValueTypeVector, - Operation: "sum", - Selector: mustParse[*promParser.VectorSelector](t, `bar`, 74), - FixedLabels: true, - ExcludeReason: map[string]utils.ExcludedLabel{ - "": { - Reason: "Query is using aggregation that removes all labels.", - Fragment: `sum(bar))`, // FIXME bogus ) + Src: utils.Source{ + Type: utils.AggregateSource, + Returns: promParser.ValueTypeVector, + Operation: "sum", + Selector: mustParse[*promParser.VectorSelector](t, `bar`, 74), + Aggregation: mustParse[*promParser.AggregateExpr](t, `sum(bar )`, 70), + FixedLabels: true, + ExcludeReason: map[string]utils.ExcludedLabel{ + "": { + Reason: "Query is using aggregation that removes all labels.", + Fragment: `sum(bar))`, // FIXME bogus ) + }, }, }, }, { - Type: utils.SelectorSource, - Operation: promParser.CardManyToMany.String(), - Returns: promParser.ValueTypeVector, - Selector: mustParse[*promParser.VectorSelector](t, `notfound`, 85), - IsConditional: true, + Src: utils.Source{ + Type: utils.SelectorSource, + Operation: promParser.CardManyToMany.String(), + Returns: promParser.ValueTypeVector, + Selector: mustParse[*promParser.VectorSelector](t, `notfound`, 85), + IsConditional: true, + }, }, }, }, { - Type: utils.FuncSource, - Returns: promParser.ValueTypeVector, - Operation: "vector", - AlwaysReturns: true, - ReturnedNumbers: []float64{1}, - FixedLabels: true, - Call: mustParse[*promParser.Call](t, "vector(1)", 36), + Type: utils.FuncSource, + Returns: promParser.ValueTypeVector, + Operation: "vector", + AlwaysReturns: true, + ReturnedNumber: 1, + FixedLabels: true, + Call: mustParse[*promParser.Call](t, "vector(1)", 36), ExcludeReason: map[string]utils.ExcludedLabel{ "": { Reason: "Calling `vector()` will return a vector value with no labels.", Fragment: "vector(1)", }, }, - Joins: []utils.Source{ + Joins: []utils.Join{ { - Type: utils.SelectorSource, - Operation: promParser.CardManyToMany.String(), - Returns: promParser.ValueTypeVector, - Selector: mustParse[*promParser.VectorSelector](t, `bob`, 57), - IsConditional: true, + Src: utils.Source{ + Type: utils.SelectorSource, + Operation: promParser.CardManyToMany.String(), + Returns: promParser.ValueTypeVector, + Selector: mustParse[*promParser.VectorSelector](t, `bob`, 57), + IsConditional: true, + }, }, { - Type: utils.AggregateSource, - Returns: promParser.ValueTypeVector, - Operation: "sum", - Selector: mustParse[*promParser.VectorSelector](t, `bar`, 74), - FixedLabels: true, - ExcludeReason: map[string]utils.ExcludedLabel{ - "": { - Reason: "Query is using aggregation that removes all labels.", - Fragment: `sum(bar))`, // FIXME bogus ) + Src: utils.Source{ + Type: utils.AggregateSource, + Returns: promParser.ValueTypeVector, + Operation: "sum", + Selector: mustParse[*promParser.VectorSelector](t, `bar`, 74), + Aggregation: mustParse[*promParser.AggregateExpr](t, `sum(bar )`, 70), // FIXME extra end + FixedLabels: true, + ExcludeReason: map[string]utils.ExcludedLabel{ + "": { + Reason: "Query is using aggregation that removes all labels.", + Fragment: `sum(bar))`, // FIXME bogus ) + }, }, }, }, { - Type: utils.SelectorSource, - Operation: promParser.CardManyToMany.String(), - Returns: promParser.ValueTypeVector, - Selector: mustParse[*promParser.VectorSelector](t, `notfound`, 85), - IsConditional: true, + Src: utils.Source{ + Type: utils.SelectorSource, + Operation: promParser.CardManyToMany.String(), + Returns: promParser.ValueTypeVector, + Selector: mustParse[*promParser.VectorSelector](t, `notfound`, 85), + IsConditional: true, + }, }, }, }, @@ -2547,62 +2737,70 @@ or Selector: mustParse[*promParser.VectorSelector](t, "metric2", 7), Call: mustParse[*promParser.Call](t, "rate(metric2[5m])", 2), IsConditional: true, - Joins: []utils.Source{ + Joins: []utils.Join{ { - Type: utils.FuncSource, - Operation: "rate", - Returns: promParser.ValueTypeVector, - Selector: mustParse[*promParser.VectorSelector](t, "metric1", 42), - Call: mustParse[*promParser.Call](t, "rate(metric1[5m])", 37), + Src: utils.Source{ + Type: utils.FuncSource, + Operation: "rate", + Returns: promParser.ValueTypeVector, + Selector: mustParse[*promParser.VectorSelector](t, "metric1", 42), + Call: mustParse[*promParser.Call](t, "rate(metric1[5m])", 37), + }, }, { - Type: utils.FuncSource, - Returns: promParser.ValueTypeVector, - Operation: "vector", - AlwaysReturns: true, - ReturnedNumbers: []float64{1}, - FixedLabels: true, - Call: mustParse[*promParser.Call](t, "vector(1)", 58), - ExcludeReason: map[string]utils.ExcludedLabel{ - "": { - Reason: "Calling `vector()` will return a vector value with no labels.", - Fragment: "vector(1)", + Src: utils.Source{ + Type: utils.FuncSource, + Returns: promParser.ValueTypeVector, + Operation: "vector", + AlwaysReturns: true, + ReturnedNumber: 1, + FixedLabels: true, + Call: mustParse[*promParser.Call](t, "vector(1)", 58), + ExcludeReason: map[string]utils.ExcludedLabel{ + "": { + Reason: "Calling `vector()` will return a vector value with no labels.", + Fragment: "vector(1)", + }, }, }, }, { - Type: utils.FuncSource, - Operation: "rate", - Returns: promParser.ValueTypeVector, - Selector: mustParse[*promParser.VectorSelector](t, `metric3{log_name="samplerd"}`, 77), - Call: mustParse[*promParser.Call](t, `rate(metric3{log_name="samplerd"}[5m])`, 72), - GuaranteedLabels: []string{"log_name"}, + Src: utils.Source{ + Type: utils.FuncSource, + Operation: "rate", + Returns: promParser.ValueTypeVector, + Selector: mustParse[*promParser.VectorSelector](t, `metric3{log_name="samplerd"}`, 77), + Call: mustParse[*promParser.Call](t, `rate(metric3{log_name="samplerd"}[5m])`, 72), + GuaranteedLabels: []string{"log_name"}, + }, }, { - Type: utils.FuncSource, - Returns: promParser.ValueTypeVector, - Operation: "vector", - AlwaysReturns: true, - ReturnedNumbers: []float64{2}, - FixedLabels: true, - Call: mustParse[*promParser.Call](t, "vector(2)", 114), - ExcludeReason: map[string]utils.ExcludedLabel{ - "": { - Reason: "Calling `vector()` will return a vector value with no labels.", - Fragment: "vector(2)", + Src: utils.Source{ + Type: utils.FuncSource, + Returns: promParser.ValueTypeVector, + Operation: "vector", + AlwaysReturns: true, + ReturnedNumber: 2, + FixedLabels: true, + Call: mustParse[*promParser.Call](t, "vector(2)", 114), + ExcludeReason: map[string]utils.ExcludedLabel{ + "": { + Reason: "Calling `vector()` will return a vector value with no labels.", + Fragment: "vector(2)", + }, }, }, }, }, }, { - Type: utils.FuncSource, - Returns: promParser.ValueTypeVector, - Operation: "vector", - AlwaysReturns: true, - ReturnedNumbers: []float64{0}, - FixedLabels: true, - Call: mustParse[*promParser.Call](t, "vector(0)", 23), + Type: utils.FuncSource, + Returns: promParser.ValueTypeVector, + Operation: "vector", + AlwaysReturns: true, + ReturnedNumber: 0, + FixedLabels: true, + Call: mustParse[*promParser.Call](t, "vector(0)", 23), ExcludeReason: map[string]utils.ExcludedLabel{ "": { Reason: "Calling `vector()` will return a vector value with no labels.", @@ -2611,49 +2809,58 @@ or }, IsConditional: true, IsDead: true, - Joins: []utils.Source{ + IsDeadReason: "this query always evaluates to `0 > 0` which is not possible, so it will never return anything", + Joins: []utils.Join{ { - Type: utils.FuncSource, - Operation: "rate", - Returns: promParser.ValueTypeVector, - Selector: mustParse[*promParser.VectorSelector](t, "metric1", 42), - Call: mustParse[*promParser.Call](t, "rate(metric1[5m])", 37), + Src: utils.Source{ + Type: utils.FuncSource, + Operation: "rate", + Returns: promParser.ValueTypeVector, + Selector: mustParse[*promParser.VectorSelector](t, "metric1", 42), + Call: mustParse[*promParser.Call](t, "rate(metric1[5m])", 37), + }, }, { - Type: utils.FuncSource, - Returns: promParser.ValueTypeVector, - Operation: "vector", - AlwaysReturns: true, - ReturnedNumbers: []float64{1}, - FixedLabels: true, - Call: mustParse[*promParser.Call](t, "vector(1)", 58), - ExcludeReason: map[string]utils.ExcludedLabel{ - "": { - Reason: "Calling `vector()` will return a vector value with no labels.", - Fragment: "vector(1)", + Src: utils.Source{ + Type: utils.FuncSource, + Returns: promParser.ValueTypeVector, + Operation: "vector", + AlwaysReturns: true, + ReturnedNumber: 1, + FixedLabels: true, + Call: mustParse[*promParser.Call](t, "vector(1)", 58), + ExcludeReason: map[string]utils.ExcludedLabel{ + "": { + Reason: "Calling `vector()` will return a vector value with no labels.", + Fragment: "vector(1)", + }, }, }, }, { - Type: utils.FuncSource, - Operation: "rate", - Returns: promParser.ValueTypeVector, - Selector: mustParse[*promParser.VectorSelector](t, `metric3{log_name="samplerd"}`, 77), - Call: mustParse[*promParser.Call](t, `rate(metric3{log_name="samplerd"}[5m])`, 72), - GuaranteedLabels: []string{"log_name"}, + Src: utils.Source{ + Type: utils.FuncSource, + Operation: "rate", + Returns: promParser.ValueTypeVector, + Selector: mustParse[*promParser.VectorSelector](t, `metric3{log_name="samplerd"}`, 77), + Call: mustParse[*promParser.Call](t, `rate(metric3{log_name="samplerd"}[5m])`, 72), + GuaranteedLabels: []string{"log_name"}, + }, }, { - Type: utils.FuncSource, - Returns: promParser.ValueTypeVector, - Operation: "vector", - AlwaysReturns: true, - ReturnedNumbers: []float64{2}, - FixedLabels: true, - Call: mustParse[*promParser.Call](t, "vector(2)", 114), - ExcludeReason: map[string]utils.ExcludedLabel{ - "": { - Reason: "Calling `vector()` will return a vector value with no labels.", - Fragment: "vector(2)", + Src: utils.Source{ + Type: utils.FuncSource, + Returns: promParser.ValueTypeVector, + Operation: "vector", + AlwaysReturns: true, + ReturnedNumber: 2, + FixedLabels: true, + Call: mustParse[*promParser.Call](t, "vector(2)", 114), + ExcludeReason: map[string]utils.ExcludedLabel{ + "": { + Reason: "Calling `vector()` will return a vector value with no labels.", + Fragment: "vector(2)", + }, }, }, }, @@ -2670,7 +2877,7 @@ or Operation: "label_replace", AlwaysReturns: true, FixedLabels: true, - ReturnedNumbers: []float64{1}, + ReturnedNumber: 1, GuaranteedLabels: []string{"nexthop_tag"}, Call: mustParse[*promParser.Call](t, `label_replace(vector(1), "nexthop_tag", "$1", "nexthop", "(.+)")`, 0), ExcludeReason: map[string]utils.ExcludedLabel{ @@ -2682,6 +2889,365 @@ or }, }, }, + { + expr: `(sum(foo{job="myjob"}))`, + output: []utils.Source{ + { + Type: utils.AggregateSource, + Returns: promParser.ValueTypeVector, + Operation: "sum", + Selector: mustParse[*promParser.VectorSelector](t, `foo{job="myjob"}`, 5), + Aggregation: mustParse[*promParser.AggregateExpr](t, `sum(foo{job="myjob"} )`, 1), + FixedLabels: true, + ExcludeReason: map[string]utils.ExcludedLabel{ + "": { + Reason: "Query is using aggregation that removes all labels.", + Fragment: `sum(foo{job="myjob"}))`, // FIXME bogus ) + }, + }, + }, + }, + }, + { + expr: `(-foo{job="myjob"})`, + output: []utils.Source{ + { + Type: utils.SelectorSource, + Returns: promParser.ValueTypeVector, + Selector: mustParse[*promParser.VectorSelector](t, `foo{job="myjob"}`, 2), + GuaranteedLabels: []string{"job"}, + }, + }, + }, + { + expr: "\n((( group(vector(0)) ))) > 0", + output: []utils.Source{ + { + Type: utils.AggregateSource, + Operation: "group", + Returns: promParser.ValueTypeVector, + FixedLabels: true, + AlwaysReturns: true, + IsConditional: true, + IsDead: true, + IsDeadReason: "this query always evaluates to `0 > 0` which is not possible, so it will never return anything", + ReturnedNumber: 0, + Call: mustParse[*promParser.Call](t, `vector(0)`, 11), + Aggregation: mustParse[*promParser.AggregateExpr](t, "group(vector(0) )", 5), // FIXME + ExcludeReason: map[string]utils.ExcludedLabel{ + "": { + Reason: "Query is using aggregation that removes all labels.", + Fragment: "group(vector(0)) )", // FIXME bogus ) + }, + }, + }, + }, + }, + { + expr: "1 > bool 5", + output: []utils.Source{ + { + Type: utils.NumberSource, + Returns: promParser.ValueTypeScalar, + FixedLabels: true, + AlwaysReturns: true, + IsConditional: true, + IsDead: true, + IsDeadReason: "this query always evaluates to `1 > 5` which is not possible, so it will never return anything", + ReturnedNumber: 1, + ExcludeReason: map[string]utils.ExcludedLabel{ + "": { + Reason: "This returns a number value with no labels.", + Fragment: "1", + }, + }, + }, + }, + }, + { + expr: `prometheus_ready{job="prometheus"} unless vector(0)`, + output: []utils.Source{ + { + Type: utils.SelectorSource, + Operation: promParser.CardManyToMany.String(), + Returns: promParser.ValueTypeVector, + Selector: mustParse[*promParser.VectorSelector](t, `prometheus_ready{job="prometheus"}`, 0), + GuaranteedLabels: []string{"job"}, + Unless: []utils.Join{ + { + Src: utils.Source{ + Type: utils.FuncSource, + Returns: promParser.ValueTypeVector, + Operation: "vector", + AlwaysReturns: true, + ReturnedNumber: 0, + FixedLabels: true, + Call: mustParse[*promParser.Call](t, "vector(0)", 42), + ExcludeReason: map[string]utils.ExcludedLabel{ + "": { + Reason: "Calling `vector()` will return a vector value with no labels.", + Fragment: "vector(0)", + }, + }, + IsDead: true, + IsDeadReason: "the right hand side will never be matched because it doesn't have `job` label while the left hand side will", + }, + }, + }, + }, + }, + }, + { + expr: `prometheus_ready{job="prometheus"} unless on() vector(0)`, + output: []utils.Source{ + { + Type: utils.SelectorSource, + Operation: promParser.CardManyToMany.String(), + Returns: promParser.ValueTypeVector, + IsDead: true, + IsDeadReason: "this query will never return anything because the `unless` query always returns something", + Selector: mustParse[*promParser.VectorSelector](t, `prometheus_ready{job="prometheus"}`, 0), + GuaranteedLabels: []string{"job"}, + Unless: []utils.Join{ + { + Src: utils.Source{ + Type: utils.FuncSource, + Returns: promParser.ValueTypeVector, + Operation: "vector", + AlwaysReturns: true, + ReturnedNumber: 0, + FixedLabels: true, + Call: mustParse[*promParser.Call](t, "vector(0)", 47), + ExcludeReason: map[string]utils.ExcludedLabel{ + "": { + Reason: "Calling `vector()` will return a vector value with no labels.", + Fragment: "vector(0)", + }, + }, + }, + }, + }, + }, + }, + }, + { + expr: `prometheus_ready{job="prometheus"} unless on(job) vector(0)`, + output: []utils.Source{ + { + Type: utils.SelectorSource, + Operation: promParser.CardManyToMany.String(), + Returns: promParser.ValueTypeVector, + Selector: mustParse[*promParser.VectorSelector](t, `prometheus_ready{job="prometheus"}`, 0), + IncludedLabels: []string{"job"}, + GuaranteedLabels: []string{"job"}, + Unless: []utils.Join{ + { + Src: utils.Source{ + Type: utils.FuncSource, + Returns: promParser.ValueTypeVector, + Operation: "vector", + AlwaysReturns: true, + IsDead: true, + IsDeadReason: "the right hand side will never be matched because it doesn't have `job` label from `on(...)`", + ReturnedNumber: 0, + FixedLabels: true, + Call: mustParse[*promParser.Call](t, "vector(0)", 50), + ExcludeReason: map[string]utils.ExcludedLabel{ + "": { + Reason: "Calling `vector()` will return a vector value with no labels.", + Fragment: "vector(0)", + }, + }, + }, + }, + }, + }, + }, + }, + { + expr: ` +max by (instance, cluster) (cf_node_role{kubernetes_role="master",role="kubernetes"}) +unless + sum by (instance, cluster) (time() - node_systemd_timer_last_trigger_seconds{name=~"etcd-defrag-.*.timer"}) + * on (instance) group_left (cluster) + cf_node_role{kubernetes_role="master",role="kubernetes"} +`, + output: []utils.Source{ + { + Type: utils.AggregateSource, + Operation: "max", + Returns: promParser.ValueTypeVector, + Selector: mustParse[*promParser.VectorSelector](t, `cf_node_role{kubernetes_role="master",role="kubernetes"}`, 29), + Aggregation: mustParse[*promParser.AggregateExpr](t, `max by (instance, cluster) (cf_node_role{kubernetes_role="master",role="kubernetes"})`, 1), + FixedLabels: true, + IncludedLabels: []string{"instance", "cluster"}, + ExcludeReason: map[string]utils.ExcludedLabel{ + "": { + Reason: "Query is using aggregation with `by(instance, cluster)`, only labels included inside `by(...)` will be present on the results.", + Fragment: `max by (instance, cluster) (cf_node_role{kubernetes_role="master",role="kubernetes"})`, + }, + }, + Unless: []utils.Join{ + { + Src: utils.Source{ + Type: utils.AggregateSource, + Operation: "sum", + Returns: promParser.ValueTypeVector, + Selector: mustParse[*promParser.VectorSelector](t, `node_systemd_timer_last_trigger_seconds{name=~"etcd-defrag-.*.timer"}`, 132), + Aggregation: mustParse[*promParser.AggregateExpr](t, `sum by (instance, cluster) (time() - node_systemd_timer_last_trigger_seconds{name=~"etcd-defrag-.*.timer"})`, 95), + FixedLabels: true, + IncludedLabels: []string{"instance", "cluster"}, + ExcludeReason: map[string]utils.ExcludedLabel{ + "": { + Reason: "Query is using aggregation with `by(instance, cluster)`, only labels included inside `by(...)` will be present on the results.", + Fragment: `sum by (instance, cluster) (time() - node_systemd_timer_last_trigger_seconds{name=~"etcd-defrag-.*.timer"})`, + }, + }, + Joins: []utils.Join{ + { + Src: utils.Source{ + Type: utils.SelectorSource, + Returns: promParser.ValueTypeVector, + Selector: mustParse[*promParser.VectorSelector](t, `cf_node_role{kubernetes_role="master",role="kubernetes"}`, 247), + }, + }, + }, + }, + }, + }, + }, + }, + }, + { + expr: `foo{a="1"} * on(instance) group_left(c,d) bar{b="2"}`, + output: []utils.Source{ + { + Type: utils.SelectorSource, + Returns: promParser.ValueTypeVector, + Operation: promParser.CardManyToOne.String(), + IncludedLabels: []string{"c", "d", "instance"}, + GuaranteedLabels: []string{"a"}, + Selector: mustParse[*promParser.VectorSelector](t, `foo{a="1"}`, 0), + Joins: []utils.Join{ + { + Src: utils.Source{ + Type: utils.SelectorSource, + Returns: promParser.ValueTypeVector, + Selector: mustParse[*promParser.VectorSelector](t, `bar{b="2"}`, 42), + }, + }, + }, + }, + }, + }, + { + expr: `foo{a="1"} * on(instance) group_right(c,d) bar{b="2"}`, + output: []utils.Source{ + { + Type: utils.SelectorSource, + Returns: promParser.ValueTypeVector, + Operation: promParser.CardOneToMany.String(), + IncludedLabels: []string{"c", "d", "instance"}, + GuaranteedLabels: []string{"b"}, + Selector: mustParse[*promParser.VectorSelector](t, `bar{b="2"}`, 43), + Joins: []utils.Join{ + { + Src: utils.Source{ + Type: utils.SelectorSource, + Returns: promParser.ValueTypeVector, + Selector: mustParse[*promParser.VectorSelector](t, `foo{a="1"}`, 0), + }, + }, + }, + }, + }, + }, + { + expr: ` + max without (source_instance) ( + increase(kernel_device_io_errors_total{device!~"loop.+"}[120m]) > 3 unless on(instance, device) ( + increase(kernel_device_io_soft_errors_total{device!~"loop.+"}[125m])*2 > increase(kernel_device_io_errors_total[120m]) + ) + and on(device, instance) absent(node_disk_info) + ) * on(instance) group_left(group) label_replace(salt_highstate_runner_configured_minions, "instance", "$1", "minion", "(.+)") +`, + output: []utils.Source{ + { + Type: utils.AggregateSource, + Returns: promParser.ValueTypeVector, + Operation: "max", + Selector: mustParse[*promParser.VectorSelector](t, `kernel_device_io_errors_total{device!~"loop.+"}`, 46), + Call: mustParse[*promParser.Call](t, `increase(kernel_device_io_errors_total{device!~"loop.+"}[120m])`, 37), + Aggregation: mustParse[*promParser.AggregateExpr](t, + `max without (source_instance) ( + increase(kernel_device_io_errors_total{device!~"loop.+"}[120m]) > 3 unless on(instance, device) ( + increase(kernel_device_io_soft_errors_total{device!~"loop.+"}[125m])*2 > increase(kernel_device_io_errors_total[120m]) + ) + and on(device, instance) absent(node_disk_info) + )`, 2), + IncludedLabels: []string{"instance", "device", "group"}, + ExcludedLabels: []string{"source_instance"}, + ExcludeReason: map[string]utils.ExcludedLabel{ + "source_instance": { + Reason: "Query is using aggregation with `without(source_instance)`, all labels included inside `without(...)` will be removed from the results.", + Fragment: "max without (source_instance) (\n increase(kernel_device_io_errors_total{device!~\"loop.+\"}[120m]) > 3 unless on(instance, device) (\n increase(kernel_device_io_soft_errors_total{device!~\"loop.+\"}[125m])*2 > increase(kernel_device_io_errors_total[120m])\n )\n and on(device, instance) absent(node_disk_info)\n )", + }, + }, + Joins: []utils.Join{ + { + Src: utils.Source{ + Type: utils.FuncSource, + Returns: promParser.ValueTypeVector, + Operation: "absent", + FixedLabels: true, + Selector: mustParse[*promParser.VectorSelector](t, `node_disk_info`, 299), + Call: mustParse[*promParser.Call](t, `absent(node_disk_info)`, 292), + IsDead: true, + IsDeadReason: "the right hand side will never be matched because it doesn't have `device` label from `on(...)`", + ExcludeReason: map[string]utils.ExcludedLabel{ + "": { + Reason: "The [absent()](https://prometheus.io/docs/prometheus/latest/querying/functions/#absent) function is used to check if provided query doesn't match any time series.\nYou will only get any results back if the metric selector you pass doesn't match anything.\nSince there are no matching time series there are also no labels. If some time series is missing you cannot read its labels.\nThis means that the only labels you can get back from absent call are the ones you pass to it.\nIf you're hoping to get instance specific labels this way and alert when some target is down then that won't work, use the `up` metric instead.", + Fragment: `absent(node_disk_info)`, + }, + }, + }, + }, + { + Src: utils.Source{ + Type: utils.FuncSource, + Returns: promParser.ValueTypeVector, + Operation: "label_replace", + Selector: mustParse[*promParser.VectorSelector](t, `salt_highstate_runner_configured_minions`, 365), + Call: mustParse[*promParser.Call](t, `label_replace(salt_highstate_runner_configured_minions, "instance", "$1", "minion", "(.+)")`, 351), + }, + }, + }, + Unless: []utils.Join{ + { + Src: utils.Source{ + Type: utils.FuncSource, + Returns: promParser.ValueTypeVector, + Operation: "increase", + IsConditional: true, + Selector: mustParse[*promParser.VectorSelector](t, `kernel_device_io_soft_errors_total{device!~"loop.+"}`, 149), + Call: mustParse[*promParser.Call](t, `increase(kernel_device_io_soft_errors_total{device!~"loop.+"}[125m])`, 140), + Joins: []utils.Join{ + { + Src: utils.Source{ + Type: utils.FuncSource, + Returns: promParser.ValueTypeVector, + Operation: "increase", + Selector: mustParse[*promParser.VectorSelector](t, `kernel_device_io_errors_total`, 222), + Call: mustParse[*promParser.Call](t, `increase(kernel_device_io_errors_total[120m])`, 213), + }, + }, + }, + }, + }, + }, + }, + }, + }, } for _, tc := range testCases {