Skip to content

Commit

Permalink
Cleanup util/validation code. (grafana#5285)
Browse files Browse the repository at this point in the history
* Cleanup `util/validation` code.

* Remove `errors.go` file.
  • Loading branch information
DylanGuedes authored Jan 31, 2022
1 parent efffe0c commit ad3c8d0
Show file tree
Hide file tree
Showing 2 changed files with 0 additions and 381 deletions.
172 changes: 0 additions & 172 deletions pkg/util/validation/errors.go

This file was deleted.

209 changes: 0 additions & 209 deletions pkg/util/validation/validate.go
Original file line number Diff line number Diff line change
@@ -1,49 +1,9 @@
package validation

import (
"net/http"
"strings"
"time"

"github.com/go-kit/log"
"github.com/go-kit/log/level"
"github.com/prometheus/client_golang/prometheus"
"github.com/prometheus/common/model"
"github.com/weaveworks/common/httpgrpc"

"github.com/grafana/loki/pkg/logproto"
"github.com/grafana/loki/pkg/util"
"github.com/grafana/loki/pkg/util/extract"
)

const (
discardReasonLabel = "reason"

errMetadataMissingMetricName = "metadata missing metric name"
errMetadataTooLong = "metadata '%s' value too long: %.200q metric %.200q"

typeMetricName = "METRIC_NAME"
typeHelp = "HELP"
typeUnit = "UNIT"

metricNameTooLong = "metric_name_too_long"
helpTooLong = "help_too_long"
unitTooLong = "unit_too_long"

// ErrQueryTooLong is used in chunk store, querier and query frontend.
ErrQueryTooLong = "the query time range exceeds the limit (query length: %s, limit: %s)"

missingMetricName = "missing_metric_name"
invalidMetricName = "metric_name_invalid"
greaterThanMaxSampleAge = "greater_than_max_sample_age"
maxLabelNamesPerSeries = "max_label_names_per_series"
tooFarInFuture = "too_far_in_future"
invalidLabel = "label_invalid"
labelNameTooLong = "label_name_too_long"
duplicateLabelNames = "duplicate_label_names"
labelsNotSorted = "labels_not_sorted"
labelValueTooLong = "label_value_too_long"

// RateLimited is one of the values for the reason to discard samples.
// Declared here to avoid duplication in ingester and distributor.
RateLimited = "rate_limited"
Expand All @@ -60,172 +20,3 @@ const (
// https://github.com/OpenObservability/OpenMetrics/blob/main/specification/OpenMetrics.md#exemplars
ExemplarMaxLabelSetLength = 128
)

// DiscardedSamples is a metric of the number of discarded samples, by reason.
var DiscardedSamples = prometheus.NewCounterVec(
prometheus.CounterOpts{
Name: "cortex_discarded_samples_total",
Help: "The total number of samples that were discarded.",
},
[]string{discardReasonLabel, "user"},
)

// DiscardedExemplars is a metric of the number of discarded exemplars, by reason.
var DiscardedExemplars = prometheus.NewCounterVec(
prometheus.CounterOpts{
Name: "cortex_discarded_exemplars_total",
Help: "The total number of exemplars that were discarded.",
},
[]string{discardReasonLabel, "user"},
)

// DiscardedMetadata is a metric of the number of discarded metadata, by reason.
var DiscardedMetadata = prometheus.NewCounterVec(
prometheus.CounterOpts{
Name: "cortex_discarded_metadata_total",
Help: "The total number of metadata that were discarded.",
},
[]string{discardReasonLabel, "user"},
)

func init() {
prometheus.MustRegister(DiscardedSamples)
prometheus.MustRegister(DiscardedExemplars)
prometheus.MustRegister(DiscardedMetadata)
}

// SampleValidationConfig helps with getting required config to validate sample.
type SampleValidationConfig interface {
RejectOldSamples(userID string) bool
RejectOldSamplesMaxAge(userID string) time.Duration
CreationGracePeriod(userID string) time.Duration
}

// ValidateSample returns an err if the sample is invalid.
// The returned error may retain the provided series labels.
func ValidateSample(cfg SampleValidationConfig, userID string, ls []logproto.LabelAdapter, s logproto.Sample) ValidationError {
unsafeMetricName, _ := extract.UnsafeMetricNameFromLabelAdapters(ls)

if cfg.RejectOldSamples(userID) && model.Time(s.Timestamp) < model.Now().Add(-cfg.RejectOldSamplesMaxAge(userID)) {
DiscardedSamples.WithLabelValues(greaterThanMaxSampleAge, userID).Inc()
return newSampleTimestampTooOldError(unsafeMetricName, s.Timestamp)
}

if model.Time(s.Timestamp) > model.Now().Add(cfg.CreationGracePeriod(userID)) {
DiscardedSamples.WithLabelValues(tooFarInFuture, userID).Inc()
return newSampleTimestampTooNewError(unsafeMetricName, s.Timestamp)
}

return nil
}

// LabelValidationConfig helps with getting required config to validate labels.
type LabelValidationConfig interface {
EnforceMetricName(userID string) bool
MaxLabelNamesPerSeries(userID string) int
MaxLabelNameLength(userID string) int
MaxLabelValueLength(userID string) int
}

// ValidateLabels returns an err if the labels are invalid.
// The returned error may retain the provided series labels.
func ValidateLabels(cfg LabelValidationConfig, userID string, ls []logproto.LabelAdapter, skipLabelNameValidation bool) ValidationError {
if cfg.EnforceMetricName(userID) {
unsafeMetricName, err := extract.UnsafeMetricNameFromLabelAdapters(ls)
if err != nil {
DiscardedSamples.WithLabelValues(missingMetricName, userID).Inc()
return newNoMetricNameError()
}

if !model.IsValidMetricName(model.LabelValue(unsafeMetricName)) {
DiscardedSamples.WithLabelValues(invalidMetricName, userID).Inc()
return newInvalidMetricNameError(unsafeMetricName)
}
}

numLabelNames := len(ls)
if numLabelNames > cfg.MaxLabelNamesPerSeries(userID) {
DiscardedSamples.WithLabelValues(maxLabelNamesPerSeries, userID).Inc()
return newTooManyLabelsError(ls, cfg.MaxLabelNamesPerSeries(userID))
}

maxLabelNameLength := cfg.MaxLabelNameLength(userID)
maxLabelValueLength := cfg.MaxLabelValueLength(userID)
lastLabelName := ""
for _, l := range ls {
if !skipLabelNameValidation && !model.LabelName(l.Name).IsValid() {
DiscardedSamples.WithLabelValues(invalidLabel, userID).Inc()
return newInvalidLabelError(ls, l.Name)
} else if len(l.Name) > maxLabelNameLength {
DiscardedSamples.WithLabelValues(labelNameTooLong, userID).Inc()
return newLabelNameTooLongError(ls, l.Name)
} else if len(l.Value) > maxLabelValueLength {
DiscardedSamples.WithLabelValues(labelValueTooLong, userID).Inc()
return newLabelValueTooLongError(ls, l.Value)
} else if cmp := strings.Compare(lastLabelName, l.Name); cmp >= 0 {
if cmp == 0 {
DiscardedSamples.WithLabelValues(duplicateLabelNames, userID).Inc()
return newDuplicatedLabelError(ls, l.Name)
}

DiscardedSamples.WithLabelValues(labelsNotSorted, userID).Inc()
return newLabelsNotSortedError(ls, l.Name)
}

lastLabelName = l.Name
}
return nil
}

// MetadataValidationConfig helps with getting required config to validate metadata.
type MetadataValidationConfig interface {
EnforceMetadataMetricName(userID string) bool
MaxMetadataLength(userID string) int
}

// ValidateMetadata returns an err if a metric metadata is invalid.
func ValidateMetadata(cfg MetadataValidationConfig, userID string, metadata *logproto.MetricMetadata) error {
if cfg.EnforceMetadataMetricName(userID) && metadata.GetMetricFamilyName() == "" {
DiscardedMetadata.WithLabelValues(missingMetricName, userID).Inc()
return httpgrpc.Errorf(http.StatusBadRequest, errMetadataMissingMetricName)
}

maxMetadataValueLength := cfg.MaxMetadataLength(userID)
var reason string
var cause string
var metadataType string
if len(metadata.GetMetricFamilyName()) > maxMetadataValueLength {
metadataType = typeMetricName
reason = metricNameTooLong
cause = metadata.GetMetricFamilyName()
} else if len(metadata.Help) > maxMetadataValueLength {
metadataType = typeHelp
reason = helpTooLong
cause = metadata.Help
} else if len(metadata.Unit) > maxMetadataValueLength {
metadataType = typeUnit
reason = unitTooLong
cause = metadata.Unit
}

if reason != "" {
DiscardedMetadata.WithLabelValues(reason, userID).Inc()
return httpgrpc.Errorf(http.StatusBadRequest, errMetadataTooLong, metadataType, cause, metadata.GetMetricFamilyName())
}

return nil
}

func DeletePerUserValidationMetrics(userID string, log log.Logger) {
filter := map[string]string{"user": userID}

if err := util.DeleteMatchingLabels(DiscardedSamples, filter); err != nil {
level.Warn(log).Log("msg", "failed to remove cortex_discarded_samples_total metric for user", "user", userID, "err", err)
}
if err := util.DeleteMatchingLabels(DiscardedExemplars, filter); err != nil {
level.Warn(log).Log("msg", "failed to remove cortex_discarded_exemplars_total metric for user", "user", userID, "err", err)
}
if err := util.DeleteMatchingLabels(DiscardedMetadata, filter); err != nil {
level.Warn(log).Log("msg", "failed to remove cortex_discarded_metadata_total metric for user", "user", userID, "err", err)
}
}

0 comments on commit ad3c8d0

Please sign in to comment.