Skip to content

Commit

Permalink
fix(models): filter out reserved tag keys in points parser (#16412)
Browse files Browse the repository at this point in the history
* fix(models): filter out reserved tag keys in points parser

* chore(models): update changelog to reflect reserved tag key change
  • Loading branch information
GeorgeMac authored Jan 24, 2020
1 parent d1c0ddc commit 281c08e
Show file tree
Hide file tree
Showing 3 changed files with 54 additions and 13 deletions.
2 changes: 2 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,9 @@
1. [16522](https://github.com/influxdata/influxdb/pull/16522): Introduce resource logger to tasks, buckets and organizations

### Bug Fixes

1. [16656](https://github.com/influxdata/influxdb/pull/16656): Check engine closed before collecting index metrics
1. [16412](https://github.com/influxdata/influxdb/pull/16412): Reject writes which use any of the reserved tag keys

### UI Improvements

Expand Down
31 changes: 29 additions & 2 deletions models/points.go
Original file line number Diff line number Diff line change
Expand Up @@ -18,16 +18,31 @@ import (
"github.com/influxdata/influxdb/pkg/escape"
)

// Values used to store the field key and measurement name as special internal tags.
const (
// Values used to store the field key and measurement name as special internal tags.
FieldKeyTagKey = "\xff"
MeasurementTagKey = "\x00"

// reserved tag keys which when present cause the point to be discarded
// and an error returned
reservedFieldTagKey = "_field"
reservedMeasurementTagKey = "_measurement"
reservedTimeTagKey = "time"
)

// Predefined byte representations of special tag keys.
var (
// Predefined byte representations of special tag keys.
FieldKeyTagKeyBytes = []byte(FieldKeyTagKey)
MeasurementTagKeyBytes = []byte(MeasurementTagKey)

// set of reserved tag keys which cannot be present when a point is being parsed.
reservedTagKeys = [][]byte{
FieldKeyTagKeyBytes,
MeasurementTagKeyBytes,
[]byte(reservedFieldTagKey),
[]byte(reservedMeasurementTagKey),
[]byte(reservedTimeTagKey),
}
)

type escapeSet struct {
Expand Down Expand Up @@ -422,6 +437,18 @@ func scanKey(buf []byte, i int) (int, []byte, error) {
}
}

// Iterate over tags keys ensure that we do not encounter any
// of the reserved tag keys such as _measurement or _field.
for j := 0; j < commas; j++ {
_, key := scanTo(buf[indices[j]:indices[j+1]-1], 0, '=')

for _, reserved := range reservedTagKeys {
if bytes.Equal(key, reserved) {
return i, buf[start:i], fmt.Errorf("cannot use reserved tag key %q", key)
}
}
}

// Now we know where the key region is within buf, and the location of tags, we
// need to determine if duplicate tags exist and if the tags are sorted. This iterates
// over the list comparing each tag in the sequence with each other.
Expand Down
34 changes: 23 additions & 11 deletions models/points_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ package models_test

import (
"bytes"
"errors"
"fmt"
"io"
"io/ioutil"
Expand Down Expand Up @@ -106,24 +107,35 @@ func TestPoint_Tags(t *testing.T) {
examples := []struct {
Point string
Tags models.Tags
Err error
}{
{`cpu value=1`, models.NewTags(map[string]string{models.MeasurementTagKey: "cpu", models.FieldKeyTagKey: "value"})},
{"cpu,tag0=v0 value=1", models.NewTags(map[string]string{models.MeasurementTagKey: "cpu", models.FieldKeyTagKey: "value", "tag0": "v0"})},
{"cpu,tag0=v0,tag1=v0 value=1", models.NewTags(map[string]string{models.MeasurementTagKey: "cpu", models.FieldKeyTagKey: "value", "tag0": "v0", "tag1": "v0"})},
{`cpu,tag0=v\ 0 value=1`, models.NewTags(map[string]string{models.MeasurementTagKey: "cpu", models.FieldKeyTagKey: "value", "tag0": "v 0"})},
{`cpu,tag0=v\ 0\ 1,tag1=v2 value=1`, models.NewTags(map[string]string{models.MeasurementTagKey: "cpu", models.FieldKeyTagKey: "value", "tag0": "v 0 1", "tag1": "v2"})},
{`cpu,tag0=\, value=1`, models.NewTags(map[string]string{models.MeasurementTagKey: "cpu", models.FieldKeyTagKey: "value", "tag0": ","})},
{`cpu,ta\ g0=\, value=1`, models.NewTags(map[string]string{models.MeasurementTagKey: "cpu", models.FieldKeyTagKey: "value", "ta g0": ","})},
{`cpu,tag0=\,1 value=1`, models.NewTags(map[string]string{models.MeasurementTagKey: "cpu", models.FieldKeyTagKey: "value", "tag0": ",1"})},
{`cpu,tag0=1\"\",t=k value=1`, models.NewTags(map[string]string{models.MeasurementTagKey: "cpu", models.FieldKeyTagKey: "value", "tag0": `1\"\"`, "t": "k"})},
{`cpu value=1`, models.NewTags(map[string]string{models.MeasurementTagKey: "cpu", models.FieldKeyTagKey: "value"}), nil},
{"cpu,tag0=v0 value=1", models.NewTags(map[string]string{models.MeasurementTagKey: "cpu", models.FieldKeyTagKey: "value", "tag0": "v0"}), nil},
{"cpu,tag0=v0,tag1=v0 value=1", models.NewTags(map[string]string{models.MeasurementTagKey: "cpu", models.FieldKeyTagKey: "value", "tag0": "v0", "tag1": "v0"}), nil},
{`cpu,tag0=v\ 0 value=1`, models.NewTags(map[string]string{models.MeasurementTagKey: "cpu", models.FieldKeyTagKey: "value", "tag0": "v 0"}), nil},
{`cpu,tag0=v\ 0\ 1,tag1=v2 value=1`, models.NewTags(map[string]string{models.MeasurementTagKey: "cpu", models.FieldKeyTagKey: "value", "tag0": "v 0 1", "tag1": "v2"}), nil},
{`cpu,tag0=\, value=1`, models.NewTags(map[string]string{models.MeasurementTagKey: "cpu", models.FieldKeyTagKey: "value", "tag0": ","}), nil},
{`cpu,ta\ g0=\, value=1`, models.NewTags(map[string]string{models.MeasurementTagKey: "cpu", models.FieldKeyTagKey: "value", "ta g0": ","}), nil},
{`cpu,tag0=\,1 value=1`, models.NewTags(map[string]string{models.MeasurementTagKey: "cpu", models.FieldKeyTagKey: "value", "tag0": ",1"}), nil},
{`cpu,tag0=1\"\",t=k value=1`, models.NewTags(map[string]string{models.MeasurementTagKey: "cpu", models.FieldKeyTagKey: "value", "tag0": `1\"\"`, "t": "k"}), nil},
{"cpu,_measurement=v0,tag0=v0 value=1", nil, errors.New(`unable to parse 'cpu,_measurement=v0,tag0=v0 value=1': cannot use reserved tag key "_measurement"`)},
// the following are all unsorted tag keys to ensure this works for both cases
{"cpu,tag0=v0,_measurement=v0 value=1", nil, errors.New(`unable to parse 'cpu,tag0=v0,_measurement=v0 value=1': cannot use reserved tag key "_measurement"`)},
{"cpu,tag0=v0,_field=v0 value=1", nil, errors.New(`unable to parse 'cpu,tag0=v0,_field=v0 value=1': cannot use reserved tag key "_field"`)},
{"cpu,tag0=v0,time=v0 value=1", nil, errors.New(`unable to parse 'cpu,tag0=v0,time=v0 value=1': cannot use reserved tag key "time"`)},
}

for _, example := range examples {
t.Run(example.Point, func(t *testing.T) {
pts, err := models.ParsePointsString(example.Point, "mm")
if err != nil {
t.Fatal(err)
} else if len(pts) != 1 {
if !reflect.DeepEqual(example.Err, err) {
t.Fatalf("expected %#v, found %#v", example.Err, err)
}
return
}

if len(pts) != 1 {
t.Fatalf("parsed %d points, expected 1", len(pts))
}

Expand Down

0 comments on commit 281c08e

Please sign in to comment.