mirror of
https://github.com/prometheus/prometheus.git
synced 2024-11-13 17:14:05 -08:00
d41e5a5582
* Prettifier: Add spaces with non-callable keywords I prefer to have a difference between, on one side: functions calls, end(), start(), and on the other side with, without, ignoring, by and group_rrigt, group_left. The reasoning is that the former ones are not calls, while other are functions. Additionally, it matches the examples in our documentation. Signed-off-by: Julien Pivotto <roidelapluie@o11y.eu> * Fix tests Signed-off-by: Julien Pivotto <roidelapluie@o11y.eu>
671 lines
13 KiB
Go
671 lines
13 KiB
Go
// Copyright 2022 The Prometheus Authors
|
|
// Licensed under the Apache License, Version 2.0 (the "License");
|
|
// you may not use this file except in compliance with the License.
|
|
// You may obtain a copy of the License at
|
|
//
|
|
// http://www.apache.org/licenses/LICENSE-2.0
|
|
//
|
|
// Unless required by applicable law or agreed to in writing, software
|
|
// distributed under the License is distributed on an "AS IS" BASIS,
|
|
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
// See the License for the specific language governing permissions and
|
|
// limitations under the License.
|
|
|
|
package parser
|
|
|
|
import (
|
|
"fmt"
|
|
"testing"
|
|
|
|
"github.com/stretchr/testify/require"
|
|
)
|
|
|
|
func TestAggregateExprPretty(t *testing.T) {
|
|
maxCharactersPerLine = 10
|
|
inputs := []struct {
|
|
in, out string
|
|
}{
|
|
{
|
|
in: `sum(foo)`,
|
|
out: `sum(foo)`,
|
|
},
|
|
{
|
|
in: `sum by() (task:errors:rate10s{job="s"})`,
|
|
out: `sum(
|
|
task:errors:rate10s{job="s"}
|
|
)`,
|
|
},
|
|
{
|
|
in: `sum without(job,foo) (task:errors:rate10s{job="s"})`,
|
|
out: `sum without (job, foo) (
|
|
task:errors:rate10s{job="s"}
|
|
)`,
|
|
},
|
|
{
|
|
in: `sum(task:errors:rate10s{job="s"}) without(job,foo)`,
|
|
out: `sum without (job, foo) (
|
|
task:errors:rate10s{job="s"}
|
|
)`,
|
|
},
|
|
{
|
|
in: `sum by(job,foo) (task:errors:rate10s{job="s"})`,
|
|
out: `sum by (job, foo) (
|
|
task:errors:rate10s{job="s"}
|
|
)`,
|
|
},
|
|
{
|
|
in: `sum (task:errors:rate10s{job="s"}) by(job,foo)`,
|
|
out: `sum by (job, foo) (
|
|
task:errors:rate10s{job="s"}
|
|
)`,
|
|
},
|
|
{
|
|
in: `topk(10, ask:errors:rate10s{job="s"})`,
|
|
out: `topk(
|
|
10,
|
|
ask:errors:rate10s{job="s"}
|
|
)`,
|
|
},
|
|
{
|
|
in: `sum by(job,foo) (sum by(job,foo) (task:errors:rate10s{job="s"}))`,
|
|
out: `sum by (job, foo) (
|
|
sum by (job, foo) (
|
|
task:errors:rate10s{job="s"}
|
|
)
|
|
)`,
|
|
},
|
|
{
|
|
in: `sum by(job,foo) (sum by(job,foo) (sum by(job,foo) (task:errors:rate10s{job="s"})))`,
|
|
out: `sum by (job, foo) (
|
|
sum by (job, foo) (
|
|
sum by (job, foo) (
|
|
task:errors:rate10s{job="s"}
|
|
)
|
|
)
|
|
)`,
|
|
},
|
|
{
|
|
in: `sum by(job,foo)
|
|
(sum by(job,foo) (task:errors:rate10s{job="s"}))`,
|
|
out: `sum by (job, foo) (
|
|
sum by (job, foo) (
|
|
task:errors:rate10s{job="s"}
|
|
)
|
|
)`,
|
|
},
|
|
{
|
|
in: `sum by(job,foo)
|
|
(sum(task:errors:rate10s{job="s"}) without(job,foo))`,
|
|
out: `sum by (job, foo) (
|
|
sum without (job, foo) (
|
|
task:errors:rate10s{job="s"}
|
|
)
|
|
)`,
|
|
},
|
|
{
|
|
in: `sum by(job,foo) # Comment 1.
|
|
(sum by(job,foo) ( # Comment 2.
|
|
task:errors:rate10s{job="s"}))`,
|
|
out: `sum by (job, foo) (
|
|
sum by (job, foo) (
|
|
task:errors:rate10s{job="s"}
|
|
)
|
|
)`,
|
|
},
|
|
}
|
|
for _, test := range inputs {
|
|
expr, err := ParseExpr(test.in)
|
|
require.NoError(t, err)
|
|
|
|
require.Equal(t, test.out, Prettify(expr))
|
|
}
|
|
}
|
|
|
|
func TestBinaryExprPretty(t *testing.T) {
|
|
maxCharactersPerLine = 10
|
|
inputs := []struct {
|
|
in, out string
|
|
}{
|
|
{
|
|
in: `a+b`,
|
|
out: `a + b`,
|
|
},
|
|
{
|
|
in: `a == bool 1`,
|
|
out: ` a
|
|
== bool
|
|
1`,
|
|
},
|
|
{
|
|
in: `a + ignoring(job) b`,
|
|
out: ` a
|
|
+ ignoring (job)
|
|
b`,
|
|
},
|
|
{
|
|
in: `foo_1 + foo_2`,
|
|
out: ` foo_1
|
|
+
|
|
foo_2`,
|
|
},
|
|
{
|
|
in: `foo_1 + foo_2 + foo_3`,
|
|
out: ` foo_1
|
|
+
|
|
foo_2
|
|
+
|
|
foo_3`,
|
|
},
|
|
{
|
|
in: `foo + baar + foo_3`,
|
|
out: ` foo + baar
|
|
+
|
|
foo_3`,
|
|
},
|
|
{
|
|
in: `foo_1 + foo_2 + foo_3 + foo_4`,
|
|
out: ` foo_1
|
|
+
|
|
foo_2
|
|
+
|
|
foo_3
|
|
+
|
|
foo_4`,
|
|
},
|
|
{
|
|
in: `foo_1 + ignoring(foo) foo_2 + ignoring(job) group_left foo_3 + on(instance) group_right foo_4`,
|
|
out: ` foo_1
|
|
+ ignoring (foo)
|
|
foo_2
|
|
+ ignoring (job) group_left ()
|
|
foo_3
|
|
+ on (instance) group_right ()
|
|
foo_4`,
|
|
},
|
|
}
|
|
for _, test := range inputs {
|
|
t.Run(test.in, func(t *testing.T) {
|
|
expr, err := ParseExpr(test.in)
|
|
require.NoError(t, err)
|
|
|
|
require.Equal(t, test.out, Prettify(expr))
|
|
})
|
|
}
|
|
}
|
|
|
|
func TestCallExprPretty(t *testing.T) {
|
|
maxCharactersPerLine = 10
|
|
inputs := []struct {
|
|
in, out string
|
|
}{
|
|
{
|
|
in: `rate(foo[1m])`,
|
|
out: `rate(
|
|
foo[1m]
|
|
)`,
|
|
},
|
|
{
|
|
in: `sum_over_time(foo[1m])`,
|
|
out: `sum_over_time(
|
|
foo[1m]
|
|
)`,
|
|
},
|
|
{
|
|
in: `rate(long_vector_selector[10m:1m] @ start() offset 1m)`,
|
|
out: `rate(
|
|
long_vector_selector[10m:1m] @ start() offset 1m
|
|
)`,
|
|
},
|
|
{
|
|
in: `histogram_quantile(0.9, rate(foo[1m]))`,
|
|
out: `histogram_quantile(
|
|
0.9,
|
|
rate(
|
|
foo[1m]
|
|
)
|
|
)`,
|
|
},
|
|
{
|
|
in: `max_over_time(rate(demo_api_request_duration_seconds_count[1m])[1m:] @ start() offset 1m)`,
|
|
out: `max_over_time(
|
|
rate(
|
|
demo_api_request_duration_seconds_count[1m]
|
|
)[1m:] @ start() offset 1m
|
|
)`,
|
|
},
|
|
{
|
|
in: `label_replace(up{job="api-server",service="a:c"}, "foo", "$1", "service", "(.*):.*")`,
|
|
out: `label_replace(
|
|
up{job="api-server",service="a:c"},
|
|
"foo",
|
|
"$1",
|
|
"service",
|
|
"(.*):.*"
|
|
)`,
|
|
},
|
|
{
|
|
in: `label_replace(label_replace(up{job="api-server",service="a:c"}, "foo", "$1", "service", "(.*):.*"), "foo", "$1", "service", "(.*):.*")`,
|
|
out: `label_replace(
|
|
label_replace(
|
|
up{job="api-server",service="a:c"},
|
|
"foo",
|
|
"$1",
|
|
"service",
|
|
"(.*):.*"
|
|
),
|
|
"foo",
|
|
"$1",
|
|
"service",
|
|
"(.*):.*"
|
|
)`,
|
|
},
|
|
}
|
|
for _, test := range inputs {
|
|
expr, err := ParseExpr(test.in)
|
|
require.NoError(t, err)
|
|
|
|
fmt.Println("=>", expr.String())
|
|
require.Equal(t, test.out, Prettify(expr))
|
|
}
|
|
}
|
|
|
|
func TestParenExprPretty(t *testing.T) {
|
|
maxCharactersPerLine = 10
|
|
inputs := []struct {
|
|
in, out string
|
|
}{
|
|
{
|
|
in: `(foo)`,
|
|
out: `(foo)`,
|
|
},
|
|
{
|
|
in: `(_foo_long_)`,
|
|
out: `(
|
|
_foo_long_
|
|
)`,
|
|
},
|
|
{
|
|
in: `((foo_long))`,
|
|
out: `(
|
|
(foo_long)
|
|
)`,
|
|
},
|
|
{
|
|
in: `((_foo_long_))`,
|
|
out: `(
|
|
(
|
|
_foo_long_
|
|
)
|
|
)`,
|
|
},
|
|
{
|
|
in: `(((foo_long)))`,
|
|
out: `(
|
|
(
|
|
(foo_long)
|
|
)
|
|
)`,
|
|
},
|
|
}
|
|
for _, test := range inputs {
|
|
expr, err := ParseExpr(test.in)
|
|
require.NoError(t, err)
|
|
|
|
require.Equal(t, test.out, Prettify(expr))
|
|
}
|
|
}
|
|
|
|
func TestStepInvariantExpr(t *testing.T) {
|
|
maxCharactersPerLine = 10
|
|
inputs := []struct {
|
|
in, out string
|
|
}{
|
|
{
|
|
in: `a @ 1`,
|
|
out: `a @ 1.000`,
|
|
},
|
|
{
|
|
in: `a @ start()`,
|
|
out: `a @ start()`,
|
|
},
|
|
{
|
|
in: `vector_selector @ start()`,
|
|
out: `vector_selector @ start()`,
|
|
},
|
|
}
|
|
for _, test := range inputs {
|
|
expr, err := ParseExpr(test.in)
|
|
require.NoError(t, err)
|
|
|
|
require.Equal(t, test.out, Prettify(expr))
|
|
}
|
|
}
|
|
|
|
func TestExprPretty(t *testing.T) {
|
|
maxCharactersPerLine = 10
|
|
inputs := []struct {
|
|
in, out string
|
|
}{
|
|
{
|
|
in: `(1 + 2)`,
|
|
out: `(1 + 2)`,
|
|
},
|
|
{
|
|
in: `(foo + bar)`,
|
|
out: `(
|
|
foo + bar
|
|
)`,
|
|
},
|
|
{
|
|
in: `(foo_long + bar_long)`,
|
|
out: `(
|
|
foo_long
|
|
+
|
|
bar_long
|
|
)`,
|
|
},
|
|
{
|
|
in: `(foo_long + bar_long + bar_2_long)`,
|
|
out: `(
|
|
foo_long
|
|
+
|
|
bar_long
|
|
+
|
|
bar_2_long
|
|
)`,
|
|
},
|
|
{
|
|
in: `((foo_long + bar_long) + bar_2_long)`,
|
|
out: `(
|
|
(
|
|
foo_long
|
|
+
|
|
bar_long
|
|
)
|
|
+
|
|
bar_2_long
|
|
)`,
|
|
},
|
|
{
|
|
in: `(1111 + 2222)`,
|
|
out: `(
|
|
1111
|
|
+
|
|
2222
|
|
)`,
|
|
},
|
|
{
|
|
in: `(sum_over_time(foo[1m]))`,
|
|
out: `(
|
|
sum_over_time(
|
|
foo[1m]
|
|
)
|
|
)`,
|
|
},
|
|
{
|
|
in: `histogram_quantile(0.9, rate(foo[1m] @ start()))`,
|
|
out: `histogram_quantile(
|
|
0.9,
|
|
rate(
|
|
foo[1m] @ start()
|
|
)
|
|
)`,
|
|
},
|
|
{
|
|
in: `(label_replace(up{job="api-server",service="a:c"}, "foo", "$1", "service", "(.*):.*"))`,
|
|
out: `(
|
|
label_replace(
|
|
up{job="api-server",service="a:c"},
|
|
"foo",
|
|
"$1",
|
|
"service",
|
|
"(.*):.*"
|
|
)
|
|
)`,
|
|
},
|
|
{
|
|
in: `(label_replace(label_replace(up{job="api-server",service="a:c"}, "foo", "$1", "service", "(.*):.*"), "foo", "$1", "service", "(.*):.*"))`,
|
|
out: `(
|
|
label_replace(
|
|
label_replace(
|
|
up{job="api-server",service="a:c"},
|
|
"foo",
|
|
"$1",
|
|
"service",
|
|
"(.*):.*"
|
|
),
|
|
"foo",
|
|
"$1",
|
|
"service",
|
|
"(.*):.*"
|
|
)
|
|
)`,
|
|
},
|
|
{
|
|
in: `(label_replace(label_replace((up{job="api-server",service="a:c"}), "foo", "$1", "service", "(.*):.*"), "foo", "$1", "service", "(.*):.*"))`,
|
|
out: `(
|
|
label_replace(
|
|
label_replace(
|
|
(
|
|
up{job="api-server",service="a:c"}
|
|
),
|
|
"foo",
|
|
"$1",
|
|
"service",
|
|
"(.*):.*"
|
|
),
|
|
"foo",
|
|
"$1",
|
|
"service",
|
|
"(.*):.*"
|
|
)
|
|
)`,
|
|
},
|
|
// Following queries have been taken from https://monitoring.mixins.dev/
|
|
{
|
|
in: `(node_filesystem_avail_bytes{job="node",fstype!=""} / node_filesystem_size_bytes{job="node",fstype!=""} * 100 < 40 and predict_linear(node_filesystem_avail_bytes{job="node",fstype!=""}[6h], 24*60*60) < 0 and node_filesystem_readonly{job="node",fstype!=""} == 0)`,
|
|
out: `(
|
|
node_filesystem_avail_bytes{fstype!="",job="node"}
|
|
/
|
|
node_filesystem_size_bytes{fstype!="",job="node"}
|
|
*
|
|
100
|
|
<
|
|
40
|
|
and
|
|
predict_linear(
|
|
node_filesystem_avail_bytes{fstype!="",job="node"}[6h],
|
|
24 * 60
|
|
*
|
|
60
|
|
)
|
|
<
|
|
0
|
|
and
|
|
node_filesystem_readonly{fstype!="",job="node"}
|
|
==
|
|
0
|
|
)`,
|
|
},
|
|
{
|
|
in: `(node_filesystem_avail_bytes{job="node",fstype!=""} / node_filesystem_size_bytes{job="node",fstype!=""} * 100 < 20 and predict_linear(node_filesystem_avail_bytes{job="node",fstype!=""}[6h], 4*60*60) < 0 and node_filesystem_readonly{job="node",fstype!=""} == 0)`,
|
|
out: `(
|
|
node_filesystem_avail_bytes{fstype!="",job="node"}
|
|
/
|
|
node_filesystem_size_bytes{fstype!="",job="node"}
|
|
*
|
|
100
|
|
<
|
|
20
|
|
and
|
|
predict_linear(
|
|
node_filesystem_avail_bytes{fstype!="",job="node"}[6h],
|
|
4 * 60
|
|
*
|
|
60
|
|
)
|
|
<
|
|
0
|
|
and
|
|
node_filesystem_readonly{fstype!="",job="node"}
|
|
==
|
|
0
|
|
)`,
|
|
},
|
|
{
|
|
in: `(node_timex_offset_seconds > 0.05 and deriv(node_timex_offset_seconds[5m]) >= 0) or (node_timex_offset_seconds < -0.05 and deriv(node_timex_offset_seconds[5m]) <= 0)`,
|
|
out: ` (
|
|
node_timex_offset_seconds
|
|
>
|
|
0.05
|
|
and
|
|
deriv(
|
|
node_timex_offset_seconds[5m]
|
|
)
|
|
>=
|
|
0
|
|
)
|
|
or
|
|
(
|
|
node_timex_offset_seconds
|
|
<
|
|
-0.05
|
|
and
|
|
deriv(
|
|
node_timex_offset_seconds[5m]
|
|
)
|
|
<=
|
|
0
|
|
)`,
|
|
},
|
|
{
|
|
in: `1 - ((node_memory_MemAvailable_bytes{job="node"} or (node_memory_Buffers_bytes{job="node"} + node_memory_Cached_bytes{job="node"} + node_memory_MemFree_bytes{job="node"} + node_memory_Slab_bytes{job="node"}) ) / node_memory_MemTotal_bytes{job="node"})`,
|
|
out: ` 1
|
|
-
|
|
(
|
|
(
|
|
node_memory_MemAvailable_bytes{job="node"}
|
|
or
|
|
(
|
|
node_memory_Buffers_bytes{job="node"}
|
|
+
|
|
node_memory_Cached_bytes{job="node"}
|
|
+
|
|
node_memory_MemFree_bytes{job="node"}
|
|
+
|
|
node_memory_Slab_bytes{job="node"}
|
|
)
|
|
)
|
|
/
|
|
node_memory_MemTotal_bytes{job="node"}
|
|
)`,
|
|
},
|
|
{
|
|
in: `min by (job, integration) (rate(alertmanager_notifications_failed_total{job="alertmanager", integration=~".*"}[5m]) / rate(alertmanager_notifications_total{job="alertmanager", integration="~.*"}[5m])) > 0.01`,
|
|
out: ` min by (job, integration) (
|
|
rate(
|
|
alertmanager_notifications_failed_total{integration=~".*",job="alertmanager"}[5m]
|
|
)
|
|
/
|
|
rate(
|
|
alertmanager_notifications_total{integration="~.*",job="alertmanager"}[5m]
|
|
)
|
|
)
|
|
>
|
|
0.01`,
|
|
},
|
|
{
|
|
in: `(count by (job) (changes(process_start_time_seconds{job="alertmanager"}[10m]) > 4) / count by (job) (up{job="alertmanager"})) >= 0.5`,
|
|
out: ` (
|
|
count by (job) (
|
|
changes(
|
|
process_start_time_seconds{job="alertmanager"}[10m]
|
|
)
|
|
>
|
|
4
|
|
)
|
|
/
|
|
count by (job) (
|
|
up{job="alertmanager"}
|
|
)
|
|
)
|
|
>=
|
|
0.5`,
|
|
},
|
|
}
|
|
for _, test := range inputs {
|
|
expr, err := ParseExpr(test.in)
|
|
require.NoError(t, err)
|
|
require.Equal(t, test.out, Prettify(expr))
|
|
}
|
|
}
|
|
|
|
func TestUnaryPretty(t *testing.T) {
|
|
maxCharactersPerLine = 10
|
|
inputs := []struct {
|
|
in, out string
|
|
}{
|
|
{
|
|
in: `-1`,
|
|
out: `-1`,
|
|
},
|
|
{
|
|
in: `-vector_selector`,
|
|
out: `-vector_selector`,
|
|
},
|
|
{
|
|
in: `(-vector_selector)`,
|
|
out: `(
|
|
-vector_selector
|
|
)`,
|
|
},
|
|
{
|
|
in: `-histogram_quantile(0.9,rate(foo[1m]))`,
|
|
out: `-histogram_quantile(
|
|
0.9,
|
|
rate(
|
|
foo[1m]
|
|
)
|
|
)`,
|
|
},
|
|
{
|
|
in: `-histogram_quantile(0.99, sum by (le) (rate(foo[1m])))`,
|
|
out: `-histogram_quantile(
|
|
0.99,
|
|
sum by (le) (
|
|
rate(
|
|
foo[1m]
|
|
)
|
|
)
|
|
)`,
|
|
},
|
|
{
|
|
in: `-histogram_quantile(0.9, -rate(foo[1m] @ start()))`,
|
|
out: `-histogram_quantile(
|
|
0.9,
|
|
-rate(
|
|
foo[1m] @ start()
|
|
)
|
|
)`,
|
|
},
|
|
{
|
|
in: `(-histogram_quantile(0.9, -rate(foo[1m] @ start())))`,
|
|
out: `(
|
|
-histogram_quantile(
|
|
0.9,
|
|
-rate(
|
|
foo[1m] @ start()
|
|
)
|
|
)
|
|
)`,
|
|
},
|
|
}
|
|
for _, test := range inputs {
|
|
t.Run(test.in, func(t *testing.T) {
|
|
expr, err := ParseExpr(test.in)
|
|
require.NoError(t, err)
|
|
require.Equal(t, test.out, Prettify(expr))
|
|
})
|
|
}
|
|
}
|