Annotation: Optionally allow storing longer annotation tags (#54754)

* Annotation: Optionally allow longer annotation tags

* Do not accept configuration lower than today's default (500)

* Apply suggestion from code review
This commit is contained in:
Sofia Papagiannaki
2022-09-23 13:04:41 +03:00
committed by GitHub
parent 883c7a802b
commit d0e7765c6a
10 changed files with 101 additions and 22 deletions

View File

@@ -5,10 +5,12 @@ import (
"errors"
"github.com/grafana/grafana/pkg/setting"
"github.com/grafana/grafana/pkg/util/errutil"
)
var (
ErrTimerangeMissing = errors.New("missing timerange")
ErrTimerangeMissing = errors.New("missing timerange")
ErrBaseTagLimitExceeded = errutil.NewBase(errutil.StatusBadRequest, "annotations.tag-limit-exceeded", errutil.WithPublicMessage("Tags length exceeds the maximum allowed."))
)
type Repository interface {

View File

@@ -17,10 +17,11 @@ type RepositoryImpl struct {
func ProvideService(db db.DB, cfg *setting.Cfg, tagService tag.Service) *RepositoryImpl {
return &RepositoryImpl{
store: &xormRepositoryImpl{
cfg: cfg,
db: db,
log: log.New("annotations"),
tagService: tagService,
cfg: cfg,
db: db,
log: log.New("annotations"),
tagService: tagService,
maximumTagsLength: cfg.AnnotationMaximumTagsLength,
},
}
}

View File

@@ -41,10 +41,11 @@ func validateTimeRange(item *annotations.Item) error {
}
type xormRepositoryImpl struct {
cfg *setting.Cfg
db db.DB
log log.Logger
tagService tag.Service
cfg *setting.Cfg
db db.DB
log log.Logger
maximumTagsLength int64
tagService tag.Service
}
func (r *xormRepositoryImpl) Add(ctx context.Context, item *annotations.Item) error {
@@ -55,7 +56,7 @@ func (r *xormRepositoryImpl) Add(ctx context.Context, item *annotations.Item) er
if item.Epoch == 0 {
item.Epoch = item.Created
}
if err := validateTimeRange(item); err != nil {
if err := r.validateItem(item); err != nil {
return err
}
@@ -106,10 +107,6 @@ func (r *xormRepositoryImpl) Update(ctx context.Context, item *annotations.Item)
existing.EpochEnd = item.EpochEnd
}
if err := validateTimeRange(existing); err != nil {
return err
}
if item.Tags != nil {
tags, err := r.tagService.EnsureTagsExist(ctx, tag.ParseTagPairs(item.Tags))
if err != nil {
@@ -127,6 +124,10 @@ func (r *xormRepositoryImpl) Update(ctx context.Context, item *annotations.Item)
existing.Tags = item.Tags
if err := r.validateItem(existing); err != nil {
return err
}
_, err = sess.Table("annotation").ID(existing.Id).Cols("epoch", "text", "epoch_end", "updated", "tags").Update(existing)
return err
})
@@ -379,6 +380,33 @@ func (r *xormRepositoryImpl) GetTags(ctx context.Context, query *annotations.Tag
return annotations.FindTagsResult{Tags: tags}, nil
}
func (r *xormRepositoryImpl) validateItem(item *annotations.Item) error {
if err := validateTimeRange(item); err != nil {
return err
}
if err := r.validateTagsLength(item); err != nil {
return err
}
return nil
}
func (r *xormRepositoryImpl) validateTagsLength(item *annotations.Item) error {
estimatedTagsLength := 1 // leading: [
for i, t := range item.Tags {
if i == 0 {
estimatedTagsLength += len(t) + 2 // quotes
} else {
estimatedTagsLength += len(t) + 3 // leading comma and quotes
}
}
estimatedTagsLength += 1 // trailing: ]
if estimatedTagsLength > int(r.maximumTagsLength) {
return annotations.ErrBaseTagLimitExceeded.Errorf("tags length (%d) exceeds the maximum allowed (%d): modify the configuration to increase it", estimatedTagsLength, r.maximumTagsLength)
}
return nil
}
func (r *xormRepositoryImpl) CleanAnnotations(ctx context.Context, cfg setting.AnnotationCleanupSettings, annotationType string) (int64, error) {
var totalAffected int64
if cfg.MaxAge > 0 {

View File

@@ -3,6 +3,7 @@ package annotationsimpl
import (
"context"
"fmt"
"strings"
"testing"
"github.com/grafana/grafana/pkg/infra/log"
@@ -28,7 +29,8 @@ func TestIntegrationAnnotations(t *testing.T) {
t.Skip("skipping integration test")
}
sql := sqlstore.InitTestDB(t)
repo := xormRepositoryImpl{db: sql, cfg: setting.NewCfg(), log: log.New("annotation.test"), tagService: tagimpl.ProvideService(sql)}
var maximumTagsLength int64 = 60
repo := xormRepositoryImpl{db: sql, cfg: setting.NewCfg(), log: log.New("annotation.test"), tagService: tagimpl.ProvideService(sql), maximumTagsLength: maximumTagsLength}
testUser := &user.SignedInUser{
OrgID: 1,
@@ -148,6 +150,18 @@ func TestIntegrationAnnotations(t *testing.T) {
assert.Equal(t, items[0].Updated, items[0].Created)
})
badAnnotation := &annotations.Item{
OrgId: 1,
UserId: 1,
Text: "rollback",
Type: "",
Epoch: 17,
Tags: []string{strings.Repeat("a", int(maximumTagsLength+1))},
}
err = repo.Add(context.Background(), badAnnotation)
require.Error(t, err)
require.ErrorIs(t, err, annotations.ErrBaseTagLimitExceeded)
t.Run("Can query for annotation by id", func(t *testing.T) {
items, err := repo.Get(context.Background(), &annotations.ItemQuery{
OrgId: 1,
@@ -394,7 +408,8 @@ func TestIntegrationAnnotationListingWithRBAC(t *testing.T) {
t.Skip("skipping integration test")
}
sql := sqlstore.InitTestDB(t, sqlstore.InitTestDBOpt{})
repo := xormRepositoryImpl{db: sql, cfg: setting.NewCfg(), log: log.New("annotation.test"), tagService: tagimpl.ProvideService(sql)}
var maximumTagsLength int64 = 60
repo := xormRepositoryImpl{db: sql, cfg: setting.NewCfg(), log: log.New("annotation.test"), tagService: tagimpl.ProvideService(sql), maximumTagsLength: maximumTagsLength}
dashboardStore := dashboardstore.ProvideDashboardStore(sql, featuremgmt.WithFeatures(), tagimpl.ProvideService(sql))
testDashboard1 := models.SaveDashboardCommand{

View File

@@ -178,6 +178,10 @@ func addAnnotationMig(mg *Migrator) {
mg.AddMigration("Add index for alert_id on annotation table", NewAddIndexMigration(table, &Index{
Cols: []string{"alert_id"}, Type: IndexType,
}))
mg.AddMigration("Increase tags column to length 4096", NewRawSQLMigration("").
Postgres("ALTER TABLE annotation ALTER COLUMN tags TYPE VARCHAR(4096);").
Mysql("ALTER TABLE annotation MODIFY tags VARCHAR(4096);"))
}
type AddMakeRegionSingleRowMigration struct {