mirror of
https://github.com/VictoriaMetrics/VictoriaMetrics.git
synced 2024-12-01 14:47:38 +00:00
app/vmselect/prometheus: properly encode Prometheus label values at /federate endpoint
Prometheus spec says that only \, \n and " must be escaped inside label values.
See 995743836e/content/docs/instrumenting/exposition_formats.md (L90)
See https://github.com/VictoriaMetrics/VictoriaMetrics/issues/5431
This commit is contained in:
parent
4f82bde65a
commit
ae38a4db97
5 changed files with 180 additions and 4 deletions
|
@ -149,11 +149,11 @@
|
||||||
{% if len(mn.Tags) > 0 %}
|
{% if len(mn.Tags) > 0 %}
|
||||||
{
|
{
|
||||||
{% code tags := mn.Tags %}
|
{% code tags := mn.Tags %}
|
||||||
{%z= tags[0].Key %}={%qz= tags[0].Value %}
|
{%z= tags[0].Key %}={%= escapePrometheusLabel(tags[0].Value) %}
|
||||||
{% code tags = tags[1:] %}
|
{% code tags = tags[1:] %}
|
||||||
{% for i := range tags %}
|
{% for i := range tags %}
|
||||||
{% code tag := &tags[i] %}
|
{% code tag := &tags[i] %}
|
||||||
,{%z= tag.Key %}={%qz= tag.Value %}
|
,{%z= tag.Key %}={%= escapePrometheusLabel(tag.Value) %}
|
||||||
{% endfor %}
|
{% endfor %}
|
||||||
}
|
}
|
||||||
{% endif %}
|
{% endif %}
|
||||||
|
@ -173,4 +173,26 @@
|
||||||
{% endif %}
|
{% endif %}
|
||||||
{% endfunc %}
|
{% endfunc %}
|
||||||
|
|
||||||
|
{% func escapePrometheusLabel(b []byte) %}
|
||||||
|
"
|
||||||
|
{% for len(b) > 0 %}
|
||||||
|
{% code n := bytes.IndexAny(b, "\\\n\"") %}
|
||||||
|
{% if n < 0 %}
|
||||||
|
{%z= b %}
|
||||||
|
{% break %}
|
||||||
|
{% endif %}
|
||||||
|
{%z= b[:n] %}
|
||||||
|
{% switch b[n] %}
|
||||||
|
{% case '\\' %}
|
||||||
|
\\
|
||||||
|
{% case '\n' %}
|
||||||
|
\n
|
||||||
|
{% case '"' %}
|
||||||
|
\"
|
||||||
|
{% endswitch %}
|
||||||
|
{% code b = b[n+1:] %}
|
||||||
|
{% endfor %}
|
||||||
|
"
|
||||||
|
{% endfunc %}
|
||||||
|
|
||||||
{% endstripspace %}
|
{% endstripspace %}
|
||||||
|
|
|
@ -495,7 +495,7 @@ func streamprometheusMetricName(qw422016 *qt422016.Writer, mn *storage.MetricNam
|
||||||
//line app/vmselect/prometheus/export.qtpl:152
|
//line app/vmselect/prometheus/export.qtpl:152
|
||||||
qw422016.N().S(`=`)
|
qw422016.N().S(`=`)
|
||||||
//line app/vmselect/prometheus/export.qtpl:152
|
//line app/vmselect/prometheus/export.qtpl:152
|
||||||
qw422016.N().QZ(tags[0].Value)
|
streamescapePrometheusLabel(qw422016, tags[0].Value)
|
||||||
//line app/vmselect/prometheus/export.qtpl:153
|
//line app/vmselect/prometheus/export.qtpl:153
|
||||||
tags = tags[1:]
|
tags = tags[1:]
|
||||||
|
|
||||||
|
@ -511,7 +511,7 @@ func streamprometheusMetricName(qw422016 *qt422016.Writer, mn *storage.MetricNam
|
||||||
//line app/vmselect/prometheus/export.qtpl:156
|
//line app/vmselect/prometheus/export.qtpl:156
|
||||||
qw422016.N().S(`=`)
|
qw422016.N().S(`=`)
|
||||||
//line app/vmselect/prometheus/export.qtpl:156
|
//line app/vmselect/prometheus/export.qtpl:156
|
||||||
qw422016.N().QZ(tag.Value)
|
streamescapePrometheusLabel(qw422016, tag.Value)
|
||||||
//line app/vmselect/prometheus/export.qtpl:157
|
//line app/vmselect/prometheus/export.qtpl:157
|
||||||
}
|
}
|
||||||
//line app/vmselect/prometheus/export.qtpl:157
|
//line app/vmselect/prometheus/export.qtpl:157
|
||||||
|
@ -599,3 +599,74 @@ func convertValueToSpecialJSON(v float64) string {
|
||||||
return qs422016
|
return qs422016
|
||||||
//line app/vmselect/prometheus/export.qtpl:174
|
//line app/vmselect/prometheus/export.qtpl:174
|
||||||
}
|
}
|
||||||
|
|
||||||
|
//line app/vmselect/prometheus/export.qtpl:176
|
||||||
|
func streamescapePrometheusLabel(qw422016 *qt422016.Writer, b []byte) {
|
||||||
|
//line app/vmselect/prometheus/export.qtpl:176
|
||||||
|
qw422016.N().S(`"`)
|
||||||
|
//line app/vmselect/prometheus/export.qtpl:178
|
||||||
|
for len(b) > 0 {
|
||||||
|
//line app/vmselect/prometheus/export.qtpl:179
|
||||||
|
n := bytes.IndexAny(b, "\\\n\"")
|
||||||
|
|
||||||
|
//line app/vmselect/prometheus/export.qtpl:180
|
||||||
|
if n < 0 {
|
||||||
|
//line app/vmselect/prometheus/export.qtpl:181
|
||||||
|
qw422016.N().Z(b)
|
||||||
|
//line app/vmselect/prometheus/export.qtpl:182
|
||||||
|
break
|
||||||
|
//line app/vmselect/prometheus/export.qtpl:183
|
||||||
|
}
|
||||||
|
//line app/vmselect/prometheus/export.qtpl:184
|
||||||
|
qw422016.N().Z(b[:n])
|
||||||
|
//line app/vmselect/prometheus/export.qtpl:185
|
||||||
|
switch b[n] {
|
||||||
|
//line app/vmselect/prometheus/export.qtpl:186
|
||||||
|
case '\\':
|
||||||
|
//line app/vmselect/prometheus/export.qtpl:186
|
||||||
|
qw422016.N().S(`\\`)
|
||||||
|
//line app/vmselect/prometheus/export.qtpl:188
|
||||||
|
case '\n':
|
||||||
|
//line app/vmselect/prometheus/export.qtpl:188
|
||||||
|
qw422016.N().S(`\n`)
|
||||||
|
//line app/vmselect/prometheus/export.qtpl:190
|
||||||
|
case '"':
|
||||||
|
//line app/vmselect/prometheus/export.qtpl:190
|
||||||
|
qw422016.N().S(`\"`)
|
||||||
|
//line app/vmselect/prometheus/export.qtpl:192
|
||||||
|
}
|
||||||
|
//line app/vmselect/prometheus/export.qtpl:193
|
||||||
|
b = b[n+1:]
|
||||||
|
|
||||||
|
//line app/vmselect/prometheus/export.qtpl:194
|
||||||
|
}
|
||||||
|
//line app/vmselect/prometheus/export.qtpl:194
|
||||||
|
qw422016.N().S(`"`)
|
||||||
|
//line app/vmselect/prometheus/export.qtpl:196
|
||||||
|
}
|
||||||
|
|
||||||
|
//line app/vmselect/prometheus/export.qtpl:196
|
||||||
|
func writeescapePrometheusLabel(qq422016 qtio422016.Writer, b []byte) {
|
||||||
|
//line app/vmselect/prometheus/export.qtpl:196
|
||||||
|
qw422016 := qt422016.AcquireWriter(qq422016)
|
||||||
|
//line app/vmselect/prometheus/export.qtpl:196
|
||||||
|
streamescapePrometheusLabel(qw422016, b)
|
||||||
|
//line app/vmselect/prometheus/export.qtpl:196
|
||||||
|
qt422016.ReleaseWriter(qw422016)
|
||||||
|
//line app/vmselect/prometheus/export.qtpl:196
|
||||||
|
}
|
||||||
|
|
||||||
|
//line app/vmselect/prometheus/export.qtpl:196
|
||||||
|
func escapePrometheusLabel(b []byte) string {
|
||||||
|
//line app/vmselect/prometheus/export.qtpl:196
|
||||||
|
qb422016 := qt422016.AcquireByteBuffer()
|
||||||
|
//line app/vmselect/prometheus/export.qtpl:196
|
||||||
|
writeescapePrometheusLabel(qb422016, b)
|
||||||
|
//line app/vmselect/prometheus/export.qtpl:196
|
||||||
|
qs422016 := string(qb422016.B)
|
||||||
|
//line app/vmselect/prometheus/export.qtpl:196
|
||||||
|
qt422016.ReleaseByteBuffer(qb422016)
|
||||||
|
//line app/vmselect/prometheus/export.qtpl:196
|
||||||
|
return qs422016
|
||||||
|
//line app/vmselect/prometheus/export.qtpl:196
|
||||||
|
}
|
||||||
|
|
43
app/vmselect/prometheus/federate_test.go
Normal file
43
app/vmselect/prometheus/federate_test.go
Normal file
|
@ -0,0 +1,43 @@
|
||||||
|
package prometheus
|
||||||
|
|
||||||
|
import (
|
||||||
|
"testing"
|
||||||
|
|
||||||
|
"github.com/VictoriaMetrics/VictoriaMetrics/app/vmselect/netstorage"
|
||||||
|
"github.com/VictoriaMetrics/VictoriaMetrics/lib/storage"
|
||||||
|
)
|
||||||
|
|
||||||
|
func TestFederate(t *testing.T) {
|
||||||
|
f := func(rs *netstorage.Result, expectedResult string) {
|
||||||
|
t.Helper()
|
||||||
|
result := Federate(rs)
|
||||||
|
if result != expectedResult {
|
||||||
|
t.Fatalf("unexpected result; got\n%s\nwant\n%s", result, expectedResult)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
f(&netstorage.Result{}, ``)
|
||||||
|
|
||||||
|
f(&netstorage.Result{
|
||||||
|
MetricName: storage.MetricName{
|
||||||
|
MetricGroup: []byte("foo"),
|
||||||
|
Tags: []storage.Tag{
|
||||||
|
{
|
||||||
|
Key: []byte("a"),
|
||||||
|
Value: []byte("b"),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Key: []byte("qqq"),
|
||||||
|
Value: []byte("\\"),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Key: []byte("abc"),
|
||||||
|
// Verify that < isn't encoded. See https://github.com/VictoriaMetrics/VictoriaMetrics/issues/5431
|
||||||
|
Value: []byte("a<b\"\\c"),
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
Values: []float64{1.23},
|
||||||
|
Timestamps: []int64{123},
|
||||||
|
}, `foo{a="b",qqq="\\",abc="a<b\"\\c"} 1.23 123`+"\n")
|
||||||
|
}
|
38
app/vmselect/prometheus/federate_timing_test.go
Normal file
38
app/vmselect/prometheus/federate_timing_test.go
Normal file
|
@ -0,0 +1,38 @@
|
||||||
|
package prometheus
|
||||||
|
|
||||||
|
import (
|
||||||
|
"bytes"
|
||||||
|
"testing"
|
||||||
|
|
||||||
|
"github.com/VictoriaMetrics/VictoriaMetrics/app/vmselect/netstorage"
|
||||||
|
"github.com/VictoriaMetrics/VictoriaMetrics/lib/storage"
|
||||||
|
)
|
||||||
|
|
||||||
|
func BenchmarkFederate(b *testing.B) {
|
||||||
|
rs := &netstorage.Result{
|
||||||
|
MetricName: storage.MetricName{
|
||||||
|
MetricGroup: []byte("foo_bar_bazaaaa_total"),
|
||||||
|
Tags: []storage.Tag{
|
||||||
|
{
|
||||||
|
Key: []byte("instance"),
|
||||||
|
Value: []byte("foobarbaz:2344"),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Key: []byte("job"),
|
||||||
|
Value: []byte("aaabbbccc"),
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
Values: []float64{112.23},
|
||||||
|
Timestamps: []int64{1234567890},
|
||||||
|
}
|
||||||
|
|
||||||
|
b.ReportAllocs()
|
||||||
|
b.RunParallel(func(pb *testing.PB) {
|
||||||
|
var bb bytes.Buffer
|
||||||
|
for pb.Next() {
|
||||||
|
bb.Reset()
|
||||||
|
WriteFederate(&bb, rs)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
|
@ -18,6 +18,8 @@ The following tip changes can be tested by building VictoriaMetrics components f
|
||||||
* SECURITY: upgrade base docker image (Alpine) from 3.18.4 to 3.18.5. See [alpine 3.18.5 release notes](https://www.alpinelinux.org/posts/Alpine-3.15.11-3.16.8-3.17.6-3.18.5-released.html).
|
* SECURITY: upgrade base docker image (Alpine) from 3.18.4 to 3.18.5. See [alpine 3.18.5 release notes](https://www.alpinelinux.org/posts/Alpine-3.15.11-3.16.8-3.17.6-3.18.5-released.html).
|
||||||
* SECURITY: upgrade Go builder from Go1.21.4 to Go1.21.5. See [the list of issues addressed in Go1.21.5](https://github.com/golang/go/issues?q=milestone%3AGo1.21.5+label%3ACherryPickApproved).
|
* SECURITY: upgrade Go builder from Go1.21.4 to Go1.21.5. See [the list of issues addressed in Go1.21.5](https://github.com/golang/go/issues?q=milestone%3AGo1.21.5+label%3ACherryPickApproved).
|
||||||
|
|
||||||
|
* BUGFIX: properly escape `<` character in responses returned via [`/federate`](https://docs.victoriametrics.com/#federation) endpoint. See [this issue](https://github.com/VictoriaMetrics/VictoriaMetrics/issues/5431).
|
||||||
|
|
||||||
## [v1.87.11](https://github.com/VictoriaMetrics/VictoriaMetrics/releases/tag/v1.87.11)
|
## [v1.87.11](https://github.com/VictoriaMetrics/VictoriaMetrics/releases/tag/v1.87.11)
|
||||||
|
|
||||||
Released at 2023-11-14
|
Released at 2023-11-14
|
||||||
|
|
Loading…
Reference in a new issue