From 315e75077d9b486bd0b6b2f7cf9199e420962bcc Mon Sep 17 00:00:00 2001 From: Sergei Ivanov Date: Tue, 25 Jun 2019 03:34:34 +0100 Subject: [PATCH] Sanitise values before sending to CloudWatch (#17) Throw away data points with known invalid values to avoid the whole batch being rejected by CloudWatch API. Signed-off-by: Sergei Ivanov --- README.md | 4 ++-- README.yaml | 4 ++-- prometheus_to_cloudwatch.go | 40 +++++++++++++++++++++++++++++++++++-- 3 files changed, 42 insertions(+), 6 deletions(-) diff --git a/README.md b/README.md index e33c2d6..a7fadea 100644 --- a/README.md +++ b/README.md @@ -134,8 +134,8 @@ docker run -i --rm \ -e CERT_PATH="" \ -e KEY_PATH="" \ -e ACCEPT_INVALID_CERT=true \ - -e INCLUDE_METRICS='' \ - -e EXCLUDE_METRICS='' \ + -e INCLUDE_METRICS="" \ + -e EXCLUDE_METRICS="" \ prometheus-to-cloudwatch ``` diff --git a/README.yaml b/README.yaml index 4110d35..e43c631 100644 --- a/README.yaml +++ b/README.yaml @@ -124,8 +124,8 @@ examples: |- -e CERT_PATH="" \ -e KEY_PATH="" \ -e ACCEPT_INVALID_CERT=true \ - -e INCLUDE_METRICS='' \ - -e EXCLUDE_METRICS='' \ + -e INCLUDE_METRICS="" \ + -e EXCLUDE_METRICS="" \ prometheus-to-cloudwatch ``` diff --git a/prometheus_to_cloudwatch.go b/prometheus_to_cloudwatch.go index 477701f..8731391 100644 --- a/prometheus_to_cloudwatch.go +++ b/prometheus_to_cloudwatch.go @@ -16,6 +16,7 @@ import ( "github.com/prometheus/common/model" "io" "log" + "math" "mime" "net/http" "sort" @@ -253,11 +254,17 @@ func appendDatum(data []*cloudwatch.MetricDatum, name string, s *model.Sample, b return data } + // Check value before adding the datum + value := float64(s.Value) + if !validValue(value) { + return data + } + datum := &cloudwatch.MetricDatum{} kubeStateDimensions, replacedDimensions := getDimensions(metric, 10-len(b.additionalDimensions), b) datum.SetMetricName(name). - SetValue(float64(s.Value)). + SetValue(value). SetTimestamp(s.Timestamp.Time()). SetDimensions(append(kubeStateDimensions, getAdditionalDimensions(b)...)). SetStorageResolution(getResolution(metric)). @@ -268,7 +275,7 @@ func appendDatum(data []*cloudwatch.MetricDatum, name string, s *model.Sample, b if replacedDimensions != nil && len(replacedDimensions) > 0 { replacedDimensionDatum := &cloudwatch.MetricDatum{} replacedDimensionDatum.SetMetricName(name). - SetValue(float64(s.Value)). + SetValue(value). SetTimestamp(s.Timestamp.Time()). SetDimensions(append(replacedDimensions, getAdditionalDimensions(b)...)). SetStorageResolution(getResolution(metric)). @@ -279,6 +286,35 @@ func appendDatum(data []*cloudwatch.MetricDatum, name string, s *model.Sample, b return data } +var ( + valueTooSmall = math.Pow(2, -260) + valueTooLarge = math.Pow(2, 260) +) + +// According to the documentation: +// "CloudWatch rejects values that are either too small or too large. +// Values must be in the range of 8.515920e-109 to 1.174271e+108 (Base 10) +// or 2e-360 to 2e360 (Base 2). +// In addition, special values (for example, NaN, +Infinity, -Infinity) are not supported." +func validValue(v float64) bool { + if math.IsInf(v, 0) { + return false + } + if math.IsNaN(v) { + return false + } + // Check for zero first to avoid tripping on "value too small" + if v == 0.0 { + return true + } + // Check that a non-zero value is within the range of accepted values + a := math.Abs(v) + if a <= valueTooSmall || a >= valueTooLarge { + return false + } + return true +} + func getName(m model.Metric) string { if n, ok := m[model.MetricNameLabel]; ok { return string(n)