diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml
index 8b3624383c..a5a09a98f1 100644
--- a/.github/workflows/ci.yml
+++ b/.github/workflows/ci.yml
@@ -143,6 +143,18 @@ jobs:
with:
parallelism: 12
thread: ${{ matrix.thread }}
+ build_all_status:
+ name: Report status of build Prometheus for all architectures
+ runs-on: ubuntu-latest
+ needs: [build_all]
+ if: github.event_name == 'pull_request' && startsWith(github.event.pull_request.base.ref, 'release-')
+ steps:
+ - name: Successful build
+ if: ${{ !(contains(needs.*.result, 'failure')) && !(contains(needs.*.result, 'cancelled')) }}
+ run: exit 0
+ - name: Failing or cancelled build
+ if: ${{ contains(needs.*.result, 'failure') || contains(needs.*.result, 'cancelled') }}
+ run: exit 1
check_generated_parser:
name: Check generated parser
runs-on: ubuntu-latest
diff --git a/CHANGELOG.md b/CHANGELOG.md
index e338d9c5c2..c019230f1c 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -1,11 +1,11 @@
# Changelog
+## unreleased
+
+* [FEATURE] OTLP receiver: Add new option `otlp.promote_resource_attributes`, for any OTel resource attributes that should be promoted to metric labels. #14200
+
## 2.54.0-rc.1 / 2024-08-05
-* [BUGFIX] TSDB: Exclude OOO chunks mapped after compaction starts (introduced by #14396). #14584
-
-## 2.54.0-rc.0 / 2024-07-19
-
Release 2.54 brings a release candidate of a major new version of [Remote Write: 2.0](https://prometheus.io/docs/specs/remote_write_spec_2_0/).
This is experimental at this time and may still change.
Remote-write v2 is enabled by default, but can be disabled via feature-flag `web.remote-write-receiver.accepted-protobuf-messages`.
@@ -22,7 +22,7 @@ Remote-write v2 is enabled by default, but can be disabled via feature-flag `web
* [ENHANCEMENT] TSDB: Optimise seek within index. #14393
* [ENHANCEMENT] TSDB: Optimise deletion of stale series. #14307
* [ENHANCEMENT] TSDB: Reduce locking to optimise adding and removing series. #13286,#14286
-* [ENHANCEMENT] TSDB: Small optimisation: streamline special handling for out-of-order data. #14396
+* [ENHANCEMENT] TSDB: Small optimisation: streamline special handling for out-of-order data. #14396,#14584
* [ENHANCEMENT] Regexps: Optimize patterns with multiple prefixes. #13843,#14368
* [ENHANCEMENT] Regexps: Optimize patterns containing multiple literal strings. #14173
* [ENHANCEMENT] AWS SD: expose Primary IPv6 addresses as __meta_ec2_primary_ipv6_addresses. #14156
diff --git a/README.md b/README.md
index cd14ed2ecb..df974e1097 100644
--- a/README.md
+++ b/README.md
@@ -12,9 +12,10 @@ examples and guides.
[][hub]
[](https://goreportcard.com/report/github.com/prometheus/prometheus)
[](https://bestpractices.coreinfrastructure.org/projects/486)
+[](https://securityscorecards.dev/viewer/?uri=github.com/prometheus/prometheus)
+[](https://clomonitor.io/projects/cncf/prometheus)
[](https://gitpod.io/#https://github.com/prometheus/prometheus)
[](https://bugs.chromium.org/p/oss-fuzz/issues/list?sort=-opened&can=1&q=proj:prometheus)
-[](https://securityscorecards.dev/viewer/?uri=github.com/prometheus/prometheus)
diff --git a/SECURITY-INSIGHTS.yml b/SECURITY-INSIGHTS.yml
new file mode 100644
index 0000000000..009b356214
--- /dev/null
+++ b/SECURITY-INSIGHTS.yml
@@ -0,0 +1,48 @@
+header:
+ schema-version: '1.0.0'
+ expiration-date: '2025-07-30T01:00:00.000Z'
+ last-updated: '2024-07-30'
+ last-reviewed: '2024-07-30'
+ project-url: https://github.com/prometheus/prometheus
+ changelog: https://github.com/prometheus/prometheus/blob/main/CHANGELOG.md
+ license: https://github.com/prometheus/prometheus/blob/main/LICENSE
+project-lifecycle:
+ status: active
+ bug-fixes-only: false
+ core-maintainers:
+ - https://github.com/prometheus/prometheus/blob/main/MAINTAINERS.md
+contribution-policy:
+ accepts-pull-requests: true
+ accepts-automated-pull-requests: true
+dependencies:
+ third-party-packages: true
+ dependencies-lists:
+ - https://github.com/prometheus/prometheus/blob/main/go.mod
+ - https://github.com/prometheus/prometheus/blob/main/web/ui/package.json
+ env-dependencies-policy:
+ policy-url: https://github.com/prometheus/prometheus/blob/main/CONTRIBUTING.md#dependency-management
+distribution-points:
+ - https://github.com/prometheus/prometheus/releases
+documentation:
+ - https://prometheus.io/docs/introduction/overview/
+security-contacts:
+ - type: email
+ value: prometheus-team@googlegroups.com
+security-testing:
+ - tool-type: sca
+ tool-name: Dependabot
+ tool-version: latest
+ integration:
+ ad-hoc: false
+ ci: true
+ before-release: true
+ - tool-type: sast
+ tool-name: CodeQL
+ tool-version: latest
+ integration:
+ ad-hoc: false
+ ci: true
+ before-release: true
+vulnerability-reporting:
+ accepts-vulnerability-reports: true
+ security-policy: https://github.com/prometheus/prometheus/security/policy
diff --git a/cmd/promtool/main.go b/cmd/promtool/main.go
index e1d275e97e..1c8e1dd1c8 100644
--- a/cmd/promtool/main.go
+++ b/cmd/promtool/main.go
@@ -204,6 +204,7 @@ func main() {
pushMetricsHeaders := pushMetricsCmd.Flag("header", "Prometheus remote write header.").StringMap()
testCmd := app.Command("test", "Unit testing.")
+ junitOutFile := testCmd.Flag("junit", "File path to store JUnit XML test results.").OpenFile(os.O_CREATE|os.O_WRONLY|os.O_TRUNC, 0o644)
testRulesCmd := testCmd.Command("rules", "Unit tests for rules.")
testRulesRun := testRulesCmd.Flag("run", "If set, will only run test groups whose names match the regular expression. Can be specified multiple times.").Strings()
testRulesFiles := testRulesCmd.Arg(
@@ -378,7 +379,11 @@ func main() {
os.Exit(QueryLabels(serverURL, httpRoundTripper, *queryLabelsMatch, *queryLabelsName, *queryLabelsBegin, *queryLabelsEnd, p))
case testRulesCmd.FullCommand():
- os.Exit(RulesUnitTest(
+ results := io.Discard
+ if *junitOutFile != nil {
+ results = *junitOutFile
+ }
+ os.Exit(RulesUnitTestResult(results,
promqltest.LazyLoaderOpts{
EnableAtModifier: true,
EnableNegativeOffset: true,
diff --git a/cmd/promtool/unittest.go b/cmd/promtool/unittest.go
index 5451c5296c..7030635d1c 100644
--- a/cmd/promtool/unittest.go
+++ b/cmd/promtool/unittest.go
@@ -18,6 +18,7 @@ import (
"encoding/json"
"errors"
"fmt"
+ "io"
"os"
"path/filepath"
"sort"
@@ -29,9 +30,10 @@ import (
"github.com/google/go-cmp/cmp"
"github.com/grafana/regexp"
"github.com/nsf/jsondiff"
- "github.com/prometheus/common/model"
"gopkg.in/yaml.v2"
+ "github.com/prometheus/common/model"
+
"github.com/prometheus/prometheus/model/histogram"
"github.com/prometheus/prometheus/model/labels"
"github.com/prometheus/prometheus/promql"
@@ -39,12 +41,18 @@ import (
"github.com/prometheus/prometheus/promql/promqltest"
"github.com/prometheus/prometheus/rules"
"github.com/prometheus/prometheus/storage"
+ "github.com/prometheus/prometheus/util/junitxml"
)
// RulesUnitTest does unit testing of rules based on the unit testing files provided.
// More info about the file format can be found in the docs.
func RulesUnitTest(queryOpts promqltest.LazyLoaderOpts, runStrings []string, diffFlag bool, files ...string) int {
+ return RulesUnitTestResult(io.Discard, queryOpts, runStrings, diffFlag, files...)
+}
+
+func RulesUnitTestResult(results io.Writer, queryOpts promqltest.LazyLoaderOpts, runStrings []string, diffFlag bool, files ...string) int {
failed := false
+ junit := &junitxml.JUnitXML{}
var run *regexp.Regexp
if runStrings != nil {
@@ -52,7 +60,7 @@ func RulesUnitTest(queryOpts promqltest.LazyLoaderOpts, runStrings []string, dif
}
for _, f := range files {
- if errs := ruleUnitTest(f, queryOpts, run, diffFlag); errs != nil {
+ if errs := ruleUnitTest(f, queryOpts, run, diffFlag, junit.Suite(f)); errs != nil {
fmt.Fprintln(os.Stderr, " FAILED:")
for _, e := range errs {
fmt.Fprintln(os.Stderr, e.Error())
@@ -64,25 +72,30 @@ func RulesUnitTest(queryOpts promqltest.LazyLoaderOpts, runStrings []string, dif
}
fmt.Println()
}
+ err := junit.WriteXML(results)
+ if err != nil {
+ fmt.Fprintf(os.Stderr, "failed to write JUnit XML: %s\n", err)
+ }
if failed {
return failureExitCode
}
return successExitCode
}
-func ruleUnitTest(filename string, queryOpts promqltest.LazyLoaderOpts, run *regexp.Regexp, diffFlag bool) []error {
- fmt.Println("Unit Testing: ", filename)
-
+func ruleUnitTest(filename string, queryOpts promqltest.LazyLoaderOpts, run *regexp.Regexp, diffFlag bool, ts *junitxml.TestSuite) []error {
b, err := os.ReadFile(filename)
if err != nil {
+ ts.Abort(err)
return []error{err}
}
var unitTestInp unitTestFile
if err := yaml.UnmarshalStrict(b, &unitTestInp); err != nil {
+ ts.Abort(err)
return []error{err}
}
if err := resolveAndGlobFilepaths(filepath.Dir(filename), &unitTestInp); err != nil {
+ ts.Abort(err)
return []error{err}
}
@@ -91,29 +104,38 @@ func ruleUnitTest(filename string, queryOpts promqltest.LazyLoaderOpts, run *reg
}
evalInterval := time.Duration(unitTestInp.EvaluationInterval)
-
+ ts.Settime(time.Now().Format("2006-01-02T15:04:05"))
// Giving number for groups mentioned in the file for ordering.
// Lower number group should be evaluated before higher number group.
groupOrderMap := make(map[string]int)
for i, gn := range unitTestInp.GroupEvalOrder {
if _, ok := groupOrderMap[gn]; ok {
- return []error{fmt.Errorf("group name repeated in evaluation order: %s", gn)}
+ err := fmt.Errorf("group name repeated in evaluation order: %s", gn)
+ ts.Abort(err)
+ return []error{err}
}
groupOrderMap[gn] = i
}
// Testing.
var errs []error
- for _, t := range unitTestInp.Tests {
+ for i, t := range unitTestInp.Tests {
if !matchesRun(t.TestGroupName, run) {
continue
}
-
+ testname := t.TestGroupName
+ if testname == "" {
+ testname = fmt.Sprintf("unnamed#%d", i)
+ }
+ tc := ts.Case(testname)
if t.Interval == 0 {
t.Interval = unitTestInp.EvaluationInterval
}
ers := t.test(evalInterval, groupOrderMap, queryOpts, diffFlag, unitTestInp.RuleFiles...)
if ers != nil {
+ for _, e := range ers {
+ tc.Fail(e.Error())
+ }
errs = append(errs, ers...)
}
}
diff --git a/cmd/promtool/unittest_test.go b/cmd/promtool/unittest_test.go
index 2dbd5a4e51..9bbac28e9f 100644
--- a/cmd/promtool/unittest_test.go
+++ b/cmd/promtool/unittest_test.go
@@ -14,11 +14,15 @@
package main
import (
+ "bytes"
+ "encoding/xml"
+ "fmt"
"testing"
"github.com/stretchr/testify/require"
"github.com/prometheus/prometheus/promql/promqltest"
+ "github.com/prometheus/prometheus/util/junitxml"
)
func TestRulesUnitTest(t *testing.T) {
@@ -125,13 +129,59 @@ func TestRulesUnitTest(t *testing.T) {
want: 0,
},
}
+ reuseFiles := []string{}
+ reuseCount := [2]int{}
for _, tt := range tests {
+ if (tt.queryOpts == promqltest.LazyLoaderOpts{
+ EnableNegativeOffset: true,
+ } || tt.queryOpts == promqltest.LazyLoaderOpts{
+ EnableAtModifier: true,
+ }) {
+ reuseFiles = append(reuseFiles, tt.args.files...)
+ reuseCount[tt.want] += len(tt.args.files)
+ }
t.Run(tt.name, func(t *testing.T) {
if got := RulesUnitTest(tt.queryOpts, nil, false, tt.args.files...); got != tt.want {
t.Errorf("RulesUnitTest() = %v, want %v", got, tt.want)
}
})
}
+ t.Run("Junit xml output ", func(t *testing.T) {
+ var buf bytes.Buffer
+ if got := RulesUnitTestResult(&buf, promqltest.LazyLoaderOpts{}, nil, false, reuseFiles...); got != 1 {
+ t.Errorf("RulesUnitTestResults() = %v, want 1", got)
+ }
+ var test junitxml.JUnitXML
+ output := buf.Bytes()
+ err := xml.Unmarshal(output, &test)
+ if err != nil {
+ fmt.Println("error in decoding XML:", err)
+ return
+ }
+ var total int
+ var passes int
+ var failures int
+ var cases int
+ total = len(test.Suites)
+ if total != len(reuseFiles) {
+ t.Errorf("JUnit output had %d testsuite elements; expected %d\n", total, len(reuseFiles))
+ }
+
+ for _, i := range test.Suites {
+ if i.FailureCount == 0 {
+ passes++
+ } else {
+ failures++
+ }
+ cases += len(i.Cases)
+ }
+ if total != passes+failures {
+ t.Errorf("JUnit output mismatch: Total testsuites (%d) does not equal the sum of passes (%d) and failures (%d).", total, passes, failures)
+ }
+ if cases < total {
+ t.Errorf("JUnit output had %d suites without test cases\n", total-cases)
+ }
+ })
}
func TestRulesUnitTestRun(t *testing.T) {
diff --git a/config/config.go b/config/config.go
index 173689d6af..7632a444fe 100644
--- a/config/config.go
+++ b/config/config.go
@@ -37,6 +37,7 @@ import (
"github.com/prometheus/prometheus/model/labels"
"github.com/prometheus/prometheus/model/relabel"
"github.com/prometheus/prometheus/storage/remote/azuread"
+ "github.com/prometheus/prometheus/storage/remote/googleiam"
)
var (
@@ -227,6 +228,9 @@ var (
DefaultExemplarsConfig = ExemplarsConfig{
MaxExemplars: 100000,
}
+
+ // DefaultOTLPConfig is the default OTLP configuration.
+ DefaultOTLPConfig = OTLPConfig{}
)
// Config is the top-level configuration for Prometheus's config files.
@@ -242,6 +246,7 @@ type Config struct {
RemoteWriteConfigs []*RemoteWriteConfig `yaml:"remote_write,omitempty"`
RemoteReadConfigs []*RemoteReadConfig `yaml:"remote_read,omitempty"`
+ OTLPConfig OTLPConfig `yaml:"otlp,omitempty"`
}
// SetDirectory joins any relative file paths with dir.
@@ -1120,6 +1125,7 @@ type RemoteWriteConfig struct {
MetadataConfig MetadataConfig `yaml:"metadata_config,omitempty"`
SigV4Config *sigv4.SigV4Config `yaml:"sigv4,omitempty"`
AzureADConfig *azuread.AzureADConfig `yaml:"azuread,omitempty"`
+ GoogleIAMConfig *googleiam.Config `yaml:"google_iam,omitempty"`
}
// SetDirectory joins any relative file paths with dir.
@@ -1157,17 +1163,33 @@ func (c *RemoteWriteConfig) UnmarshalYAML(unmarshal func(interface{}) error) err
return err
}
- httpClientConfigAuthEnabled := c.HTTPClientConfig.BasicAuth != nil ||
- c.HTTPClientConfig.Authorization != nil || c.HTTPClientConfig.OAuth2 != nil
+ return validateAuthConfigs(c)
+}
- if httpClientConfigAuthEnabled && (c.SigV4Config != nil || c.AzureADConfig != nil) {
- return fmt.Errorf("at most one of basic_auth, authorization, oauth2, sigv4, & azuread must be configured")
+// validateAuthConfigs validates that at most one of basic_auth, authorization, oauth2, sigv4, azuread or google_iam must be configured.
+func validateAuthConfigs(c *RemoteWriteConfig) error {
+ var authConfigured []string
+ if c.HTTPClientConfig.BasicAuth != nil {
+ authConfigured = append(authConfigured, "basic_auth")
}
-
- if c.SigV4Config != nil && c.AzureADConfig != nil {
- return fmt.Errorf("at most one of basic_auth, authorization, oauth2, sigv4, & azuread must be configured")
+ if c.HTTPClientConfig.Authorization != nil {
+ authConfigured = append(authConfigured, "authorization")
+ }
+ if c.HTTPClientConfig.OAuth2 != nil {
+ authConfigured = append(authConfigured, "oauth2")
+ }
+ if c.SigV4Config != nil {
+ authConfigured = append(authConfigured, "sigv4")
+ }
+ if c.AzureADConfig != nil {
+ authConfigured = append(authConfigured, "azuread")
+ }
+ if c.GoogleIAMConfig != nil {
+ authConfigured = append(authConfigured, "google_iam")
+ }
+ if len(authConfigured) > 1 {
+ return fmt.Errorf("at most one of basic_auth, authorization, oauth2, sigv4, azuread or google_iam must be configured. Currently configured: %v", authConfigured)
}
-
return nil
}
@@ -1186,7 +1208,7 @@ func validateHeadersForTracing(headers map[string]string) error {
func validateHeaders(headers map[string]string) error {
for header := range headers {
if strings.ToLower(header) == "authorization" {
- return errors.New("authorization header must be changed via the basic_auth, authorization, oauth2, sigv4, or azuread parameter")
+ return errors.New("authorization header must be changed via the basic_auth, authorization, oauth2, sigv4, azuread or google_iam parameter")
}
if _, ok := reservedHeaders[strings.ToLower(header)]; ok {
return fmt.Errorf("%s is a reserved header. It must not be changed", header)
@@ -1305,3 +1327,35 @@ func getGoGCEnv() int {
}
return DefaultRuntimeConfig.GoGC
}
+
+// OTLPConfig is the configuration for writing to the OTLP endpoint.
+type OTLPConfig struct {
+ PromoteResourceAttributes []string `yaml:"promote_resource_attributes,omitempty"`
+}
+
+// UnmarshalYAML implements the yaml.Unmarshaler interface.
+func (c *OTLPConfig) UnmarshalYAML(unmarshal func(interface{}) error) error {
+ *c = DefaultOTLPConfig
+ type plain OTLPConfig
+ if err := unmarshal((*plain)(c)); err != nil {
+ return err
+ }
+
+ seen := map[string]struct{}{}
+ var err error
+ for i, attr := range c.PromoteResourceAttributes {
+ attr = strings.TrimSpace(attr)
+ if attr == "" {
+ err = errors.Join(err, fmt.Errorf("empty promoted OTel resource attribute"))
+ continue
+ }
+ if _, exists := seen[attr]; exists {
+ err = errors.Join(err, fmt.Errorf("duplicated promoted OTel resource attribute %q", attr))
+ continue
+ }
+
+ seen[attr] = struct{}{}
+ c.PromoteResourceAttributes[i] = attr
+ }
+ return err
+}
diff --git a/config/config_test.go b/config/config_test.go
index 3c4907a46c..9b074bef1c 100644
--- a/config/config_test.go
+++ b/config/config_test.go
@@ -156,6 +156,12 @@ var expectedConf = &Config{
},
},
+ OTLPConfig: OTLPConfig{
+ PromoteResourceAttributes: []string{
+ "k8s.cluster.name", "k8s.job.name", "k8s.namespace.name",
+ },
+ },
+
RemoteReadConfigs: []*RemoteReadConfig{
{
URL: mustParseURL("http://remote1/read"),
@@ -1471,6 +1477,26 @@ func TestRemoteWriteRetryOnRateLimit(t *testing.T) {
require.False(t, got.RemoteWriteConfigs[1].QueueConfig.RetryOnRateLimit)
}
+func TestOTLPSanitizeResourceAttributes(t *testing.T) {
+ t.Run("good config", func(t *testing.T) {
+ want, err := LoadFile(filepath.Join("testdata", "otlp_sanitize_resource_attributes.good.yml"), false, false, log.NewNopLogger())
+ require.NoError(t, err)
+
+ out, err := yaml.Marshal(want)
+ require.NoError(t, err)
+ var got Config
+ require.NoError(t, yaml.UnmarshalStrict(out, &got))
+
+ require.Equal(t, []string{"k8s.cluster.name", "k8s.job.name", "k8s.namespace.name"}, got.OTLPConfig.PromoteResourceAttributes)
+ })
+
+ t.Run("bad config", func(t *testing.T) {
+ _, err := LoadFile(filepath.Join("testdata", "otlp_sanitize_resource_attributes.bad.yml"), false, false, log.NewNopLogger())
+ require.ErrorContains(t, err, `duplicated promoted OTel resource attribute "k8s.job.name"`)
+ require.ErrorContains(t, err, `empty promoted OTel resource attribute`)
+ })
+}
+
func TestLoadConfig(t *testing.T) {
// Parse a valid file that sets a global scrape timeout. This tests whether parsing
// an overwritten default field in the global config permanently changes the default.
@@ -1800,7 +1826,7 @@ var expectedErrors = []struct {
},
{
filename: "remote_write_authorization_header.bad.yml",
- errMsg: `authorization header must be changed via the basic_auth, authorization, oauth2, sigv4, or azuread parameter`,
+ errMsg: `authorization header must be changed via the basic_auth, authorization, oauth2, sigv4, azuread or google_iam parameter`,
},
{
filename: "remote_write_wrong_msg.bad.yml",
diff --git a/config/testdata/conf.good.yml b/config/testdata/conf.good.yml
index 0e0aa2bd5d..56741822c2 100644
--- a/config/testdata/conf.good.yml
+++ b/config/testdata/conf.good.yml
@@ -45,6 +45,9 @@ remote_write:
headers:
name: value
+otlp:
+ promote_resource_attributes: ["k8s.cluster.name", "k8s.job.name", "k8s.namespace.name"]
+
remote_read:
- url: http://remote1/read
read_recent: true
diff --git a/config/testdata/otlp_sanitize_resource_attributes.bad.yml b/config/testdata/otlp_sanitize_resource_attributes.bad.yml
new file mode 100644
index 0000000000..37ec5d1209
--- /dev/null
+++ b/config/testdata/otlp_sanitize_resource_attributes.bad.yml
@@ -0,0 +1,2 @@
+otlp:
+ promote_resource_attributes: ["k8s.cluster.name", " k8s.job.name ", "k8s.namespace.name", "k8s.job.name", ""]
diff --git a/config/testdata/otlp_sanitize_resource_attributes.good.yml b/config/testdata/otlp_sanitize_resource_attributes.good.yml
new file mode 100644
index 0000000000..67247e7743
--- /dev/null
+++ b/config/testdata/otlp_sanitize_resource_attributes.good.yml
@@ -0,0 +1,2 @@
+otlp:
+ promote_resource_attributes: ["k8s.cluster.name", " k8s.job.name ", "k8s.namespace.name"]
diff --git a/discovery/legacymanager/manager_test.go b/discovery/legacymanager/manager_test.go
index a455a8e341..f1be963113 100644
--- a/discovery/legacymanager/manager_test.go
+++ b/discovery/legacymanager/manager_test.go
@@ -1090,7 +1090,6 @@ func TestCoordinationWithReceiver(t *testing.T) {
}
for _, tc := range testCases {
- tc := tc
t.Run(tc.title, func(t *testing.T) {
ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second)
defer cancel()
diff --git a/docs/command-line/promtool.md b/docs/command-line/promtool.md
index 443cd3f0cb..6bb80169a9 100644
--- a/docs/command-line/promtool.md
+++ b/docs/command-line/promtool.md
@@ -442,6 +442,15 @@ Unit testing.
+#### Flags
+
+| Flag | Description |
+| --- | --- |
+| --junit
| File path to store JUnit XML test results. |
+
+
+
+
##### `promtool test rules`
Unit tests for rules.
diff --git a/docs/configuration/configuration.md b/docs/configuration/configuration.md
index ff24082e40..313a7f2f37 100644
--- a/docs/configuration/configuration.md
+++ b/docs/configuration/configuration.md
@@ -152,6 +152,10 @@ alerting:
remote_write:
[ - ... ]
+# Settings related to the OTLP receiver feature.
+otlp:
+ [ promote_resource_attributes: [, ...] | default = [ ] ]
+
# Settings related to the remote read feature.
remote_read:
[ - ... ]
@@ -3397,8 +3401,8 @@ authorization:
# It is mutually exclusive with `credentials`.
[ credentials_file: ]
-# Optionally configures AWS's Signature Verification 4 signing process to
-# sign requests. Cannot be set at the same time as basic_auth, authorization, or oauth2.
+# Optionally configures AWS's Signature Verification 4 signing process to sign requests.
+# Cannot be set at the same time as basic_auth, authorization, oauth2, azuread or google_iam.
# To use the default credentials from the AWS SDK, use `sigv4: {}`.
sigv4:
# The AWS region. If blank, the region from the default credentials chain
@@ -3651,12 +3655,12 @@ sigv4:
[ role_arn: ]
# Optional OAuth 2.0 configuration.
-# Cannot be used at the same time as basic_auth, authorization, sigv4, or azuread.
+# Cannot be used at the same time as basic_auth, authorization, sigv4, azuread or google_iam.
oauth2:
[ ]
# Optional AzureAD configuration.
-# Cannot be used at the same time as basic_auth, authorization, oauth2, or sigv4.
+# Cannot be used at the same time as basic_auth, authorization, oauth2, sigv4 or google_iam.
azuread:
# The Azure Cloud. Options are 'AzurePublic', 'AzureChina', or 'AzureGovernment'.
[ cloud: | default = AzurePublic ]
@@ -3676,6 +3680,14 @@ azuread:
[ sdk:
[ tenant_id: ] ]
+# WARNING: Remote write is NOT SUPPORTED by Google Cloud. This configuration is reserved for future use.
+# Optional Google Cloud Monitoring configuration.
+# Cannot be used at the same time as basic_auth, authorization, oauth2, sigv4 or azuread.
+# To use the default credentials from the Google Cloud SDK, use `google_iam: {}`.
+google_iam:
+ # Service account key with monitoring write permessions.
+ credentials_file:
+
# Configures the remote write request's TLS settings.
tls_config:
[ ]
diff --git a/docs/configuration/unit_testing_rules.md b/docs/configuration/unit_testing_rules.md
index 163fcb91f1..7fc676a251 100644
--- a/docs/configuration/unit_testing_rules.md
+++ b/docs/configuration/unit_testing_rules.md
@@ -92,7 +92,7 @@ series:
#
# Native histogram notation:
# Native histograms can be used instead of floating point numbers using the following notation:
-# {{schema:1 sum:-0.3 count:3.1 z_bucket:7.1 z_bucket_w:0.05 buckets:[5.1 10 7] offset:-3 n_buckets:[4.1 5] n_offset:-5}}
+# {{schema:1 sum:-0.3 count:3.1 z_bucket:7.1 z_bucket_w:0.05 buckets:[5.1 10 7] offset:-3 n_buckets:[4.1 5] n_offset:-5 counter_reset_hint:gauge}}
# Native histograms support the same expanding notation as floating point numbers, i.e. 'axn', 'a+bxn' and 'a-bxn'.
# All properties are optional and default to 0. The order is not important. The following properties are supported:
# - schema (int):
@@ -119,6 +119,8 @@ series:
# Observation counts in negative buckets. Each represents an absolute count.
# - n_offset (int):
# The starting index of the first entry in the negative buckets.
+# - counter_reset_hint (one of 'unknown', 'reset', 'not_reset' or 'gauge')
+# The counter reset hint associated with this histogram. Defaults to 'unknown' if not set.
values:
```
diff --git a/docs/querying/basics.md b/docs/querying/basics.md
index 1c72adb3e5..304c9f07d4 100644
--- a/docs/querying/basics.md
+++ b/docs/querying/basics.md
@@ -8,9 +8,15 @@ sort_rank: 1
Prometheus provides a functional query language called PromQL (Prometheus Query
Language) that lets the user select and aggregate time series data in real
-time. The result of an expression can either be shown as a graph, viewed as
-tabular data in Prometheus's expression browser, or consumed by external
-systems via the [HTTP API](api.md).
+time.
+
+When you send a query request to Prometheus, it can be an _instant query_, evaluated at one point in time,
+or a _range query_ at equally-spaced steps between a start and an end time. PromQL works exactly the same
+in each cases; the range query is just like an instant query run multiple times at different timestamps.
+
+In the Prometheus UI, the "Table" tab is for instant queries and the "Graph" tab is for range queries.
+
+Other programs can fetch the result of a PromQL expression via the [HTTP API](api.md).
## Examples
@@ -94,9 +100,7 @@ Examples:
## Time series selectors
-Time series selectors are responsible for selecting the times series and raw or inferred sample timestamps and values.
-
-Time series *selectors* are not to be confused with higher level concept of instant and range *queries* that can execute the time series *selectors*. A higher level instant query would evaluate the given selector at one point in time, however the range query would evaluate the selector at multiple different times in between a minimum and maximum timestamp at regular steps.
+These are the basic building-blocks that instruct PromQL what data to fetch.
### Instant vector selectors
diff --git a/documentation/examples/remote_storage/go.mod b/documentation/examples/remote_storage/go.mod
index 4c41a66061..35dca85a07 100644
--- a/documentation/examples/remote_storage/go.mod
+++ b/documentation/examples/remote_storage/go.mod
@@ -16,8 +16,8 @@ require (
require (
github.com/Azure/azure-sdk-for-go/sdk/azcore v1.11.1 // indirect
- github.com/Azure/azure-sdk-for-go/sdk/azidentity v1.5.2 // indirect
- github.com/Azure/azure-sdk-for-go/sdk/internal v1.6.0 // indirect
+ github.com/Azure/azure-sdk-for-go/sdk/azidentity v1.6.0 // indirect
+ github.com/Azure/azure-sdk-for-go/sdk/internal v1.8.0 // indirect
github.com/AzureAD/microsoft-authentication-library-for-go v1.2.2 // indirect
github.com/alecthomas/units v0.0.0-20231202071711-9a357b53e9c9 // indirect
github.com/aws/aws-sdk-go v1.53.16 // indirect
@@ -36,7 +36,6 @@ require (
github.com/jpillora/backoff v1.0.0 // indirect
github.com/json-iterator/go v1.1.12 // indirect
github.com/klauspost/compress v1.17.8 // indirect
- github.com/kr/text v0.2.0 // indirect
github.com/kylelemons/godebug v1.1.0 // indirect
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect
github.com/modern-go/reflect2 v1.0.2 // indirect
diff --git a/documentation/examples/remote_storage/go.sum b/documentation/examples/remote_storage/go.sum
index 9898d75d70..4c420092f0 100644
--- a/documentation/examples/remote_storage/go.sum
+++ b/documentation/examples/remote_storage/go.sum
@@ -1,9 +1,9 @@
github.com/Azure/azure-sdk-for-go/sdk/azcore v1.11.1 h1:E+OJmp2tPvt1W+amx48v1eqbjDYsgN+RzP4q16yV5eM=
github.com/Azure/azure-sdk-for-go/sdk/azcore v1.11.1/go.mod h1:a6xsAQUZg+VsS3TJ05SRp524Hs4pZ/AeFSr5ENf0Yjo=
-github.com/Azure/azure-sdk-for-go/sdk/azidentity v1.5.2 h1:FDif4R1+UUR+00q6wquyX90K7A8dN+R5E8GEadoP7sU=
-github.com/Azure/azure-sdk-for-go/sdk/azidentity v1.5.2/go.mod h1:aiYBYui4BJ/BJCAIKs92XiPyQfTaBWqvHujDwKb6CBU=
-github.com/Azure/azure-sdk-for-go/sdk/internal v1.6.0 h1:sUFnFjzDUie80h24I7mrKtwCKgLY9L8h5Tp2x9+TWqk=
-github.com/Azure/azure-sdk-for-go/sdk/internal v1.6.0/go.mod h1:52JbnQTp15qg5mRkMBHwp0j0ZFwHJ42Sx3zVV5RE9p0=
+github.com/Azure/azure-sdk-for-go/sdk/azidentity v1.6.0 h1:U2rTu3Ef+7w9FHKIAXM6ZyqF3UOWJZ12zIm8zECAFfg=
+github.com/Azure/azure-sdk-for-go/sdk/azidentity v1.6.0/go.mod h1:9kIvujWAA58nmPmWB1m23fyWic1kYZMxD9CxaWn4Qpg=
+github.com/Azure/azure-sdk-for-go/sdk/internal v1.8.0 h1:jBQA3cKT4L2rWMpgE7Yt3Hwh2aUj8KXjIGLxjHeYNNo=
+github.com/Azure/azure-sdk-for-go/sdk/internal v1.8.0/go.mod h1:4OG6tQ9EOP/MT0NMjDlRzWoVFxfu9rN9B2X+tlSVktg=
github.com/Azure/azure-sdk-for-go/sdk/resourcemanager/compute/armcompute/v5 v5.7.0 h1:LkHbJbgF3YyvC53aqYGR+wWQDn2Rdp9AQdGndf9QvY4=
github.com/Azure/azure-sdk-for-go/sdk/resourcemanager/compute/armcompute/v5 v5.7.0/go.mod h1:QyiQdW4f4/BIfB8ZutZ2s+28RAgfa/pT+zS++ZHyM1I=
github.com/Azure/azure-sdk-for-go/sdk/resourcemanager/network/armnetwork/v4 v4.3.0 h1:bXwSugBiSbgtz7rOtbfGf+woewp4f06orW9OP5BjHLA=
@@ -39,7 +39,6 @@ github.com/cespare/xxhash/v2 v2.3.0 h1:UL815xU9SqsFlibzuggzjXhog7bL6oX9BbNZnL2UF
github.com/cespare/xxhash/v2 v2.3.0/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs=
github.com/cncf/xds/go v0.0.0-20240318125728-8a4994d93e50 h1:DBmgJDC9dTfkVyGgipamEh2BpGYxScCH1TOF1LL1cXc=
github.com/cncf/xds/go v0.0.0-20240318125728-8a4994d93e50/go.mod h1:5e1+Vvlzido69INQaVO6d87Qn543Xr6nooe9Kz7oBFM=
-github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E=
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc h1:U9qPSI2PIWSS1VwoXQT9A3Wy9MM3WgvqSxFWenqJduM=
@@ -50,8 +49,6 @@ github.com/digitalocean/godo v1.117.0 h1:WVlTe09melDYTd7VCVyvHcNWbgB+uI1O115+5LO
github.com/digitalocean/godo v1.117.0/go.mod h1:Vk0vpCot2HOAJwc5WE8wljZGtJ3ZtWIc8MQ8rF38sdo=
github.com/distribution/reference v0.5.0 h1:/FUIFXtfc/x2gpa5/VGfiGLuOIdYa1t65IKK2OFGvA0=
github.com/distribution/reference v0.5.0/go.mod h1:BbU0aIcezP1/5jX/8MP0YiH4SdvB5Y4f/wlDRiLyi3E=
-github.com/dnaeon/go-vcr v1.2.0 h1:zHCHvJYTMh1N7xnV7zf1m1GPBF9Ad0Jk/whtQ1663qI=
-github.com/dnaeon/go-vcr v1.2.0/go.mod h1:R4UdLID7HZT3taECzJs4YgbbH6PIGXB6W/sc5OLb6RQ=
github.com/docker/docker v26.1.3+incompatible h1:lLCzRbrVZrljpVNobJu1J2FHk8V0s4BawoZippkc+xo=
github.com/docker/docker v26.1.3+incompatible/go.mod h1:eEKB0N0r5NX/I1kEveEz05bcu8tLC/8azJZsviup8Sk=
github.com/docker/go-connections v0.4.0 h1:El9xVISelRB7BuFusrZozjnkIM5YnzCViNKohAFqRJQ=
@@ -279,8 +276,8 @@ github.com/prometheus/procfs v0.15.1 h1:YagwOFzUgYfKKHX6Dr+sHT7km/hxC76UB0leargg
github.com/prometheus/procfs v0.15.1/go.mod h1:fB45yRUv8NstnjriLhBQLuOUt+WW4BsoGhij/e3PBqk=
github.com/prometheus/prometheus v0.53.1-0.20240704074759-c137febfcf8c h1:6GEA48LnonkYZhQ654v7QTIP5uBTbCEVm49oIhif5lc=
github.com/prometheus/prometheus v0.53.1-0.20240704074759-c137febfcf8c/go.mod h1:FcNs5wa7M9yV8IlxlB/05s5oy9vULUIlu/tZsviRIT8=
-github.com/rogpeppe/go-internal v1.10.0 h1:TMyTOH3F/DB16zRVcYyreMH6GnZZrwQVAoYjRBZyWFQ=
-github.com/rogpeppe/go-internal v1.10.0/go.mod h1:UQnix2H7Ngw/k4C5ijL5+65zddjncjaFoBhdsK/akog=
+github.com/rogpeppe/go-internal v1.12.0 h1:exVL4IDcn6na9z1rAb56Vxr+CgyK3nn3O+epU5NdKM8=
+github.com/rogpeppe/go-internal v1.12.0/go.mod h1:E+RYuTGaKKdloAfM02xzb0FW3Paa99yedzYV+kq4uf4=
github.com/scaleway/scaleway-sdk-go v1.0.0-beta.27 h1:yGAraK1uUjlhSXgNMIy8o/J4LFNcy7yeipBqt9N9mVg=
github.com/scaleway/scaleway-sdk-go v1.0.0-beta.27/go.mod h1:fCa7OJZ/9DRTnOKmxvT6pn+LPWUptQAmHF/SBJUGEcg=
github.com/sirupsen/logrus v1.2.0/go.mod h1:LxeOpSwHxABJmUn/MG1IvRgCAasNZTLOkJPxbbu5VWo=
diff --git a/go.mod b/go.mod
index 44c0aca7a7..c74452cd56 100644
--- a/go.mod
+++ b/go.mod
@@ -17,7 +17,7 @@ require (
github.com/bboreham/go-loser v0.0.0-20230920113527-fcc2c21820a3
github.com/cespare/xxhash/v2 v2.3.0
github.com/dennwc/varint v1.0.0
- github.com/digitalocean/godo v1.118.0
+ github.com/digitalocean/godo v1.119.0
github.com/docker/docker v27.0.3+incompatible
github.com/edsrzf/mmap-go v1.1.0
github.com/envoyproxy/go-control-plane v0.12.0
@@ -33,17 +33,17 @@ require (
github.com/google/go-cmp v0.6.0
github.com/google/pprof v0.0.0-20240711041743-f6c9dda6c6da
github.com/google/uuid v1.6.0
- github.com/gophercloud/gophercloud v1.13.0
+ github.com/gophercloud/gophercloud v1.14.0
github.com/grafana/regexp v0.0.0-20240518133315-a468a5bfb3bc
github.com/grpc-ecosystem/grpc-gateway v1.16.0
github.com/hashicorp/consul/api v1.29.2
github.com/hashicorp/nomad/api v0.0.0-20240717122358-3d93bd3778f3
- github.com/hetznercloud/hcloud-go/v2 v2.10.2
+ github.com/hetznercloud/hcloud-go/v2 v2.12.0
github.com/ionos-cloud/sdk-go/v6 v6.1.11
github.com/json-iterator/go v1.1.12
github.com/klauspost/compress v1.17.9
github.com/kolo/xmlrpc v0.0.0-20220921171641-a4b6fa1dd06b
- github.com/linode/linodego v1.37.0
+ github.com/linode/linodego v1.38.0
github.com/miekg/dns v1.1.61
github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822
github.com/mwitkow/go-conntrack v0.0.0-20190716064945-2f068394615f
@@ -82,7 +82,7 @@ require (
golang.org/x/text v0.16.0
golang.org/x/time v0.5.0
golang.org/x/tools v0.23.0
- google.golang.org/api v0.188.0
+ google.golang.org/api v0.189.0
google.golang.org/genproto/googleapis/api v0.0.0-20240711142825-46eb208f015d
google.golang.org/grpc v1.65.0
google.golang.org/protobuf v1.34.2
@@ -96,9 +96,9 @@ require (
)
require (
- cloud.google.com/go/auth v0.7.0 // indirect
- cloud.google.com/go/auth/oauth2adapt v0.2.2 // indirect
- cloud.google.com/go/compute/metadata v0.4.0 // indirect
+ cloud.google.com/go/auth v0.7.2 // indirect
+ cloud.google.com/go/auth/oauth2adapt v0.2.3 // indirect
+ cloud.google.com/go/compute/metadata v0.5.0 // indirect
github.com/Azure/azure-sdk-for-go/sdk/internal v1.10.0 // indirect
github.com/AzureAD/microsoft-authentication-library-for-go v1.2.2 // indirect
github.com/Microsoft/go-winio v0.6.1 // indirect
@@ -191,7 +191,7 @@ require (
golang.org/x/exp v0.0.0-20240119083558-1b970713d09a // indirect
golang.org/x/mod v0.19.0 // indirect
golang.org/x/term v0.22.0 // indirect
- google.golang.org/genproto/googleapis/rpc v0.0.0-20240708141625-4ad9e859172b // indirect
+ google.golang.org/genproto/googleapis/rpc v0.0.0-20240722135656-d784300faade // indirect
gopkg.in/inf.v0 v0.9.1 // indirect
gopkg.in/ini.v1 v1.67.0 // indirect
gotest.tools/v3 v3.0.3 // indirect
diff --git a/go.sum b/go.sum
index bb515753de..d96710c177 100644
--- a/go.sum
+++ b/go.sum
@@ -12,18 +12,18 @@ cloud.google.com/go v0.54.0/go.mod h1:1rq2OEkV3YMf6n/9ZvGWI3GWw0VoqH/1x2nd8Is/bP
cloud.google.com/go v0.56.0/go.mod h1:jr7tqZxxKOVYizybht9+26Z/gUq7tiRzu+ACVAMbKVk=
cloud.google.com/go v0.57.0/go.mod h1:oXiQ6Rzq3RAkkY7N6t3TcE6jE+CIBBbA36lwQ1JyzZs=
cloud.google.com/go v0.65.0/go.mod h1:O5N8zS7uWy9vkA9vayVHs65eM1ubvY4h553ofrNHObY=
-cloud.google.com/go/auth v0.7.0 h1:kf/x9B3WTbBUHkC+1VS8wwwli9TzhSt0vSTVBmMR8Ts=
-cloud.google.com/go/auth v0.7.0/go.mod h1:D+WqdrpcjmiCgWrXmLLxOVq1GACoE36chW6KXoEvuIw=
-cloud.google.com/go/auth/oauth2adapt v0.2.2 h1:+TTV8aXpjeChS9M+aTtN/TjdQnzJvmzKFt//oWu7HX4=
-cloud.google.com/go/auth/oauth2adapt v0.2.2/go.mod h1:wcYjgpZI9+Yu7LyYBg4pqSiaRkfEK3GQcpb7C/uyF1Q=
+cloud.google.com/go/auth v0.7.2 h1:uiha352VrCDMXg+yoBtaD0tUF4Kv9vrtrWPYXwutnDE=
+cloud.google.com/go/auth v0.7.2/go.mod h1:VEc4p5NNxycWQTMQEDQF0bd6aTMb6VgYDXEwiJJQAbs=
+cloud.google.com/go/auth/oauth2adapt v0.2.3 h1:MlxF+Pd3OmSudg/b1yZ5lJwoXCEaeedAguodky1PcKI=
+cloud.google.com/go/auth/oauth2adapt v0.2.3/go.mod h1:tMQXOfZzFuNuUxOypHlQEXgdfX5cuhwU+ffUuXRJE8I=
cloud.google.com/go/bigquery v1.0.1/go.mod h1:i/xbL2UlR5RvWAURpBYZTtm/cXjCha9lbfbpx4poX+o=
cloud.google.com/go/bigquery v1.3.0/go.mod h1:PjpwJnslEMmckchkHFfq+HTD2DmtT67aNFKH1/VBDHE=
cloud.google.com/go/bigquery v1.4.0/go.mod h1:S8dzgnTigyfTmLBfrtrhyYhwRxG72rYxvftPBK2Dvzc=
cloud.google.com/go/bigquery v1.5.0/go.mod h1:snEHRnqQbz117VIFhE8bmtwIDY80NLUZUMb4Nv6dBIg=
cloud.google.com/go/bigquery v1.7.0/go.mod h1://okPTzCYNXSlb24MZs83e2Do+h+VXtc4gLoIoXIAPc=
cloud.google.com/go/bigquery v1.8.0/go.mod h1:J5hqkt3O0uAFnINi6JXValWIb1v0goeZM77hZzJN/fQ=
-cloud.google.com/go/compute/metadata v0.4.0 h1:vHzJCWaM4g8XIcm8kopr3XmDA4Gy/lblD3EhhSux05c=
-cloud.google.com/go/compute/metadata v0.4.0/go.mod h1:SIQh1Kkb4ZJ8zJ874fqVkslA29PRXuleyj6vOzlbK7M=
+cloud.google.com/go/compute/metadata v0.5.0 h1:Zr0eK8JbFv6+Wi4ilXAR8FJ3wyNdpxHKJNPos6LTZOY=
+cloud.google.com/go/compute/metadata v0.5.0/go.mod h1:aHnloV2TPI38yx4s9+wAZhHykWvVCfu7hQbF+9CWoiY=
cloud.google.com/go/datastore v1.0.0/go.mod h1:LXYbyblFSglQ5pkeyhO+Qmw7ukd3C+pD7TKLgZqpHYE=
cloud.google.com/go/datastore v1.1.0/go.mod h1:umbIZjpQpHh4hmRpGhH4tLFup+FVzqBi1b3c64qFpCk=
cloud.google.com/go/pubsub v1.0.1/go.mod h1:R0Gpsv3s54REJCy4fxDixWD93lHJMoZTyQ2kNxGRt3I=
@@ -143,8 +143,8 @@ github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc/go.mod h1:J7Y8Yc
github.com/dennwc/varint v1.0.0 h1:kGNFFSSw8ToIy3obO/kKr8U9GZYUAxQEVuix4zfDWzE=
github.com/dennwc/varint v1.0.0/go.mod h1:hnItb35rvZvJrbTALZtY/iQfDs48JKRG1RPpgziApxA=
github.com/dgrijalva/jwt-go v3.2.0+incompatible/go.mod h1:E3ru+11k8xSBh+hMPgOLZmtrrCbhqsmaPHjLKYnJCaQ=
-github.com/digitalocean/godo v1.118.0 h1:lkzGFQmACrVCp7UqH1sAi4JK/PWwlc5aaxubgorKmC4=
-github.com/digitalocean/godo v1.118.0/go.mod h1:Vk0vpCot2HOAJwc5WE8wljZGtJ3ZtWIc8MQ8rF38sdo=
+github.com/digitalocean/godo v1.119.0 h1:dmFNQwSIAcH3z+FVovHLkazKDC2uA8oOlGvg5+H4vRw=
+github.com/digitalocean/godo v1.119.0/go.mod h1:WQVH83OHUy6gC4gXpEVQKtxTd4L5oCp+5OialidkPLY=
github.com/distribution/reference v0.5.0 h1:/FUIFXtfc/x2gpa5/VGfiGLuOIdYa1t65IKK2OFGvA0=
github.com/distribution/reference v0.5.0/go.mod h1:BbU0aIcezP1/5jX/8MP0YiH4SdvB5Y4f/wlDRiLyi3E=
github.com/dnaeon/go-vcr v1.2.0 h1:zHCHvJYTMh1N7xnV7zf1m1GPBF9Ad0Jk/whtQ1663qI=
@@ -334,8 +334,8 @@ github.com/googleapis/gax-go/v2 v2.0.4/go.mod h1:0Wqv26UfaUD9n4G6kQubkQ+KchISgw+
github.com/googleapis/gax-go/v2 v2.0.5/go.mod h1:DWXyrwAJ9X0FpwwEdw+IPEYBICEFu5mhpdKc/us6bOk=
github.com/googleapis/gax-go/v2 v2.12.5 h1:8gw9KZK8TiVKB6q3zHY3SBzLnrGp6HQjyfYBYGmXdxA=
github.com/googleapis/gax-go/v2 v2.12.5/go.mod h1:BUDKcWo+RaKq5SC9vVYL0wLADa3VcfswbOMMRmB9H3E=
-github.com/gophercloud/gophercloud v1.13.0 h1:8iY9d1DAbzMW6Vok1AxbbK5ZaUjzMp0tdyt4fX9IeJ0=
-github.com/gophercloud/gophercloud v1.13.0/go.mod h1:aAVqcocTSXh2vYFZ1JTvx4EQmfgzxRcNupUfxZbBNDM=
+github.com/gophercloud/gophercloud v1.14.0 h1:Bt9zQDhPrbd4qX7EILGmy+i7GP35cc+AAL2+wIJpUE8=
+github.com/gophercloud/gophercloud v1.14.0/go.mod h1:aAVqcocTSXh2vYFZ1JTvx4EQmfgzxRcNupUfxZbBNDM=
github.com/gopherjs/gopherjs v0.0.0-20181017120253-0766667cb4d1/go.mod h1:wJfORRmW1u3UXTncJ5qlYoELFm8eSnnEO6hX4iZ3EWY=
github.com/gorilla/context v1.1.1/go.mod h1:kBGZzfjB9CEq2AlWe17Uuf7NDRt0dE0s8S51q0aT7Yg=
github.com/gorilla/mux v1.6.2/go.mod h1:1lud6UwP+6orDFRuTfBEV8e9/aOM/c4fVVCaMa2zaAs=
@@ -414,8 +414,8 @@ github.com/hashicorp/nomad/api v0.0.0-20240717122358-3d93bd3778f3/go.mod h1:svtx
github.com/hashicorp/serf v0.8.2/go.mod h1:6hOLApaqBFA1NXqRQAsxw9QxuDEvNxSQRwA/JwenrHc=
github.com/hashicorp/serf v0.10.1 h1:Z1H2J60yRKvfDYAOZLd2MU0ND4AH/WDz7xYHDWQsIPY=
github.com/hashicorp/serf v0.10.1/go.mod h1:yL2t6BqATOLGc5HF7qbFkTfXoPIY0WZdWHfEvMqbG+4=
-github.com/hetznercloud/hcloud-go/v2 v2.10.2 h1:9gyTUPhfNbfbS40Spgij5mV5k37bOZgt8iHKCbfGs5I=
-github.com/hetznercloud/hcloud-go/v2 v2.10.2/go.mod h1:xQ+8KhIS62W0D78Dpi57jsufWh844gUw1az5OUvaeq8=
+github.com/hetznercloud/hcloud-go/v2 v2.12.0 h1:nOgfNTo0gyXZJJdM8mo/XH5MO/e80wAEpldRzdWayhY=
+github.com/hetznercloud/hcloud-go/v2 v2.12.0/go.mod h1:dhix40Br3fDiBhwaSG/zgaYOFFddpfBm/6R1Zz0IiF0=
github.com/hpcloud/tail v1.0.0/go.mod h1:ab1qPbhIpdTxEkNHXyeSf5vhxWSCs/tWer42PpOxQnU=
github.com/hudl/fargo v1.3.0/go.mod h1:y3CKSmjA+wD2gak7sUSXTAoopbhU08POFhmITJgmKTg=
github.com/ianlancetaylor/demangle v0.0.0-20181102032728-5e5cf60278f6/go.mod h1:aSSvb/t6k1mPoxDqO4vJh6VOCGPwU4O0C2/Eqndh1Sc=
@@ -472,8 +472,8 @@ github.com/kylelemons/godebug v1.1.0 h1:RPNrshWIDI6G2gRW9EHilWtl7Z6Sb1BR0xunSBf0
github.com/kylelemons/godebug v1.1.0/go.mod h1:9/0rRGxNHcop5bhtWyNeEfOS8JIWk580+fNqagV/RAw=
github.com/lightstep/lightstep-tracer-common/golang/gogo v0.0.0-20190605223551-bc2310a04743/go.mod h1:qklhhLq1aX+mtWk9cPHPzaBjWImj5ULL6C7HFJtXQMM=
github.com/lightstep/lightstep-tracer-go v0.18.1/go.mod h1:jlF1pusYV4pidLvZ+XD0UBX0ZE6WURAspgAczcDHrL4=
-github.com/linode/linodego v1.37.0 h1:B/2Spzv9jYXzKA+p+GD8fVCNJ7Wuw6P91ZDD9eCkkso=
-github.com/linode/linodego v1.37.0/go.mod h1:L7GXKFD3PoN2xSEtFc04wIXP5WK65O10jYQx0PQISWQ=
+github.com/linode/linodego v1.38.0 h1:wP3oW9OhGc6vhze8NPf2knbwH4TzSbrjzuCd9okjbTY=
+github.com/linode/linodego v1.38.0/go.mod h1:L7GXKFD3PoN2xSEtFc04wIXP5WK65O10jYQx0PQISWQ=
github.com/lyft/protoc-gen-validate v0.0.13/go.mod h1:XbGvPuh87YZc5TdIa2/I4pLk0QoUACkjt2znoq26NVQ=
github.com/mailru/easyjson v0.7.7 h1:UGYAvKxe3sBsEDzO8ZeWOSlIQfWFlxbzLZe7hwFURr0=
github.com/mailru/easyjson v0.7.7/go.mod h1:xzfreul335JAWq5oZzymOObrkdz5UnU4kGfJJLY9Nlc=
@@ -1047,8 +1047,8 @@ google.golang.org/api v0.20.0/go.mod h1:BwFmGc8tA3vsd7r/7kR8DY7iEEGSU04BFxCo5jP/
google.golang.org/api v0.22.0/go.mod h1:BwFmGc8tA3vsd7r/7kR8DY7iEEGSU04BFxCo5jP/sfE=
google.golang.org/api v0.24.0/go.mod h1:lIXQywCXRcnZPGlsd8NbLnOjtAoL6em04bJ9+z0MncE=
google.golang.org/api v0.28.0/go.mod h1:lIXQywCXRcnZPGlsd8NbLnOjtAoL6em04bJ9+z0MncE=
-google.golang.org/api v0.188.0 h1:51y8fJ/b1AaaBRJr4yWm96fPcuxSo0JcegXE3DaHQHw=
-google.golang.org/api v0.188.0/go.mod h1:VR0d+2SIiWOYG3r/jdm7adPW9hI2aRv9ETOSCQ9Beag=
+google.golang.org/api v0.189.0 h1:equMo30LypAkdkLMBqfeIqtyAnlyig1JSZArl4XPwdI=
+google.golang.org/api v0.189.0/go.mod h1:FLWGJKb0hb+pU2j+rJqwbnsF+ym+fQs73rbJ+KAUgy8=
google.golang.org/appengine v1.1.0/go.mod h1:EbEs0AVv82hx2wNQdGPgUI5lhzA/G0D9YwlJXL52JkM=
google.golang.org/appengine v1.2.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4=
google.golang.org/appengine v1.4.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4=
@@ -1087,8 +1087,8 @@ google.golang.org/genproto v0.0.0-20200618031413-b414f8b61790/go.mod h1:jDfRM7Fc
google.golang.org/genproto v0.0.0-20200825200019-8632dd797987/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no=
google.golang.org/genproto/googleapis/api v0.0.0-20240711142825-46eb208f015d h1:kHjw/5UfflP/L5EbledDrcG4C2597RtymmGRZvHiCuY=
google.golang.org/genproto/googleapis/api v0.0.0-20240711142825-46eb208f015d/go.mod h1:mw8MG/Qz5wfgYr6VqVCiZcHe/GJEfI+oGGDCohaVgB0=
-google.golang.org/genproto/googleapis/rpc v0.0.0-20240708141625-4ad9e859172b h1:04+jVzTs2XBnOZcPsLnmrTGqltqJbZQ1Ey26hjYdQQ0=
-google.golang.org/genproto/googleapis/rpc v0.0.0-20240708141625-4ad9e859172b/go.mod h1:Ue6ibwXGpU+dqIcODieyLOcgj7z8+IcskoNIgZxtrFY=
+google.golang.org/genproto/googleapis/rpc v0.0.0-20240722135656-d784300faade h1:oCRSWfwGXQsqlVdErcyTt4A93Y8fo0/9D4b1gnI++qo=
+google.golang.org/genproto/googleapis/rpc v0.0.0-20240722135656-d784300faade/go.mod h1:Ue6ibwXGpU+dqIcODieyLOcgj7z8+IcskoNIgZxtrFY=
google.golang.org/grpc v1.17.0/go.mod h1:6QZJwpn2B+Zp71q/5VxRsJ6NXXVCE5NRUHRo+f3cWCs=
google.golang.org/grpc v1.19.0/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c=
google.golang.org/grpc v1.20.0/go.mod h1:chYK+tFQF0nDUGJgXMSgLCQk3phJEuONr2DCgLDdAQM=
diff --git a/model/labels/labels.go b/model/labels/labels.go
index 01514abf38..cd30f4f8ff 100644
--- a/model/labels/labels.go
+++ b/model/labels/labels.go
@@ -38,10 +38,10 @@ func (ls Labels) Bytes(buf []byte) []byte {
b.WriteByte(labelSep)
for i, l := range ls {
if i > 0 {
- b.WriteByte(seps[0])
+ b.WriteByte(sep)
}
b.WriteString(l.Name)
- b.WriteByte(seps[0])
+ b.WriteByte(sep)
b.WriteString(l.Value)
}
return b.Bytes()
@@ -86,9 +86,9 @@ func (ls Labels) Hash() uint64 {
}
b = append(b, v.Name...)
- b = append(b, seps[0])
+ b = append(b, sep)
b = append(b, v.Value...)
- b = append(b, seps[0])
+ b = append(b, sep)
}
return xxhash.Sum64(b)
}
@@ -106,9 +106,9 @@ func (ls Labels) HashForLabels(b []byte, names ...string) (uint64, []byte) {
i++
default:
b = append(b, ls[i].Name...)
- b = append(b, seps[0])
+ b = append(b, sep)
b = append(b, ls[i].Value...)
- b = append(b, seps[0])
+ b = append(b, sep)
i++
j++
}
@@ -130,9 +130,9 @@ func (ls Labels) HashWithoutLabels(b []byte, names ...string) (uint64, []byte) {
continue
}
b = append(b, ls[i].Name...)
- b = append(b, seps[0])
+ b = append(b, sep)
b = append(b, ls[i].Value...)
- b = append(b, seps[0])
+ b = append(b, sep)
}
return xxhash.Sum64(b), b
}
@@ -151,10 +151,10 @@ func (ls Labels) BytesWithLabels(buf []byte, names ...string) []byte {
i++
default:
if b.Len() > 1 {
- b.WriteByte(seps[0])
+ b.WriteByte(sep)
}
b.WriteString(ls[i].Name)
- b.WriteByte(seps[0])
+ b.WriteByte(sep)
b.WriteString(ls[i].Value)
i++
j++
@@ -177,10 +177,10 @@ func (ls Labels) BytesWithoutLabels(buf []byte, names ...string) []byte {
continue
}
if b.Len() > 1 {
- b.WriteByte(seps[0])
+ b.WriteByte(sep)
}
b.WriteString(ls[i].Name)
- b.WriteByte(seps[0])
+ b.WriteByte(sep)
b.WriteString(ls[i].Value)
}
return b.Bytes()
diff --git a/model/labels/labels_common.go b/model/labels/labels_common.go
index 4bc94f84fe..6db86b03c7 100644
--- a/model/labels/labels_common.go
+++ b/model/labels/labels_common.go
@@ -29,10 +29,11 @@ const (
BucketLabel = "le"
InstanceName = "instance"
- labelSep = '\xfe'
+ labelSep = '\xfe' // Used at beginning of `Bytes` return.
+ sep = '\xff' // Used between labels in `Bytes` and `Hash`.
)
-var seps = []byte{'\xff'}
+var seps = []byte{sep} // Used with Hash, which has no WriteByte method.
// Label is a key/value pair of strings.
type Label struct {
diff --git a/model/labels/labels_dedupelabels.go b/model/labels/labels_dedupelabels.go
index 0e5bb048be..da8a88cc15 100644
--- a/model/labels/labels_dedupelabels.go
+++ b/model/labels/labels_dedupelabels.go
@@ -146,13 +146,13 @@ func (ls Labels) Bytes(buf []byte) []byte {
b := bytes.NewBuffer(buf[:0])
for i := 0; i < len(ls.data); {
if i > 0 {
- b.WriteByte(seps[0])
+ b.WriteByte(sep)
}
var name, value string
name, i = decodeString(ls.syms, ls.data, i)
value, i = decodeString(ls.syms, ls.data, i)
b.WriteString(name)
- b.WriteByte(seps[0])
+ b.WriteByte(sep)
b.WriteString(value)
}
return b.Bytes()
@@ -201,9 +201,9 @@ func (ls Labels) Hash() uint64 {
}
b = append(b, name...)
- b = append(b, seps[0])
+ b = append(b, sep)
b = append(b, value...)
- b = append(b, seps[0])
+ b = append(b, sep)
pos = newPos
}
return xxhash.Sum64(b)
@@ -226,9 +226,9 @@ func (ls Labels) HashForLabels(b []byte, names ...string) (uint64, []byte) {
}
if name == names[j] {
b = append(b, name...)
- b = append(b, seps[0])
+ b = append(b, sep)
b = append(b, value...)
- b = append(b, seps[0])
+ b = append(b, sep)
}
}
@@ -252,9 +252,9 @@ func (ls Labels) HashWithoutLabels(b []byte, names ...string) (uint64, []byte) {
continue
}
b = append(b, name...)
- b = append(b, seps[0])
+ b = append(b, sep)
b = append(b, value...)
- b = append(b, seps[0])
+ b = append(b, sep)
}
return xxhash.Sum64(b), b
}
@@ -275,10 +275,10 @@ func (ls Labels) BytesWithLabels(buf []byte, names ...string) []byte {
}
if lName == names[j] {
if b.Len() > 1 {
- b.WriteByte(seps[0])
+ b.WriteByte(sep)
}
b.WriteString(lName)
- b.WriteByte(seps[0])
+ b.WriteByte(sep)
b.WriteString(lValue)
}
pos = newPos
@@ -299,10 +299,10 @@ func (ls Labels) BytesWithoutLabels(buf []byte, names ...string) []byte {
}
if j == len(names) || lName != names[j] {
if b.Len() > 1 {
- b.WriteByte(seps[0])
+ b.WriteByte(sep)
}
b.WriteString(lName)
- b.WriteByte(seps[0])
+ b.WriteByte(sep)
b.WriteString(lValue)
}
pos = newPos
diff --git a/model/labels/labels_stringlabels.go b/model/labels/labels_stringlabels.go
index bccceb61fe..c8bce51234 100644
--- a/model/labels/labels_stringlabels.go
+++ b/model/labels/labels_stringlabels.go
@@ -112,9 +112,9 @@ func (ls Labels) HashForLabels(b []byte, names ...string) (uint64, []byte) {
}
if name == names[j] {
b = append(b, name...)
- b = append(b, seps[0])
+ b = append(b, sep)
b = append(b, value...)
- b = append(b, seps[0])
+ b = append(b, sep)
}
}
@@ -138,9 +138,9 @@ func (ls Labels) HashWithoutLabels(b []byte, names ...string) (uint64, []byte) {
continue
}
b = append(b, name...)
- b = append(b, seps[0])
+ b = append(b, sep)
b = append(b, value...)
- b = append(b, seps[0])
+ b = append(b, sep)
}
return xxhash.Sum64(b), b
}
diff --git a/model/labels/sharding.go b/model/labels/sharding.go
index 5e3e89fbbb..8b3a369397 100644
--- a/model/labels/sharding.go
+++ b/model/labels/sharding.go
@@ -39,9 +39,9 @@ func StableHash(ls Labels) uint64 {
}
b = append(b, v.Name...)
- b = append(b, seps[0])
+ b = append(b, sep)
b = append(b, v.Value...)
- b = append(b, seps[0])
+ b = append(b, sep)
}
return xxhash.Sum64(b)
}
diff --git a/model/labels/sharding_dedupelabels.go b/model/labels/sharding_dedupelabels.go
index 5912724f9b..5bf41b05d6 100644
--- a/model/labels/sharding_dedupelabels.go
+++ b/model/labels/sharding_dedupelabels.go
@@ -43,9 +43,9 @@ func StableHash(ls Labels) uint64 {
}
b = append(b, name...)
- b = append(b, seps[0])
+ b = append(b, sep)
b = append(b, value...)
- b = append(b, seps[0])
+ b = append(b, sep)
pos = newPos
}
return xxhash.Sum64(b)
diff --git a/model/labels/sharding_stringlabels.go b/model/labels/sharding_stringlabels.go
index 3ad2027d8c..798f268eb9 100644
--- a/model/labels/sharding_stringlabels.go
+++ b/model/labels/sharding_stringlabels.go
@@ -43,9 +43,9 @@ func StableHash(ls Labels) uint64 {
}
b = append(b, v.Name...)
- b = append(b, seps[0])
+ b = append(b, sep)
b = append(b, v.Value...)
- b = append(b, seps[0])
+ b = append(b, sep)
}
if h != nil {
return h.Sum64()
diff --git a/model/relabel/relabel.go b/model/relabel/relabel.go
index 4f33edda43..a880465969 100644
--- a/model/relabel/relabel.go
+++ b/model/relabel/relabel.go
@@ -213,6 +213,10 @@ func (re Regexp) IsZero() bool {
// String returns the original string used to compile the regular expression.
func (re Regexp) String() string {
+ if re.Regexp == nil {
+ return ""
+ }
+
str := re.Regexp.String()
// Trim the anchor `^(?:` prefix and `)$` suffix.
return str[4 : len(str)-2]
diff --git a/model/relabel/relabel_test.go b/model/relabel/relabel_test.go
index 0f11f7068d..fc9952134d 100644
--- a/model/relabel/relabel_test.go
+++ b/model/relabel/relabel_test.go
@@ -900,3 +900,16 @@ action: replace
})
}
}
+
+func TestRegexp_ShouldMarshalAndUnmarshalZeroValue(t *testing.T) {
+ var zero Regexp
+
+ marshalled, err := yaml.Marshal(&zero)
+ require.NoError(t, err)
+ require.Equal(t, "null\n", string(marshalled))
+
+ var unmarshalled Regexp
+ err = yaml.Unmarshal(marshalled, &unmarshalled)
+ require.NoError(t, err)
+ require.Nil(t, unmarshalled.Regexp)
+}
diff --git a/prompb/io/prometheus/write/v2/types.pb.go b/prompb/io/prometheus/write/v2/types.pb.go
index d6ea8398f7..3420d20e25 100644
--- a/prompb/io/prometheus/write/v2/types.pb.go
+++ b/prompb/io/prometheus/write/v2/types.pb.go
@@ -302,15 +302,10 @@ type Exemplar struct {
// value represents an exact example value. This can be useful when the exemplar
// is attached to a histogram, which only gives an estimated value through buckets.
Value float64 `protobuf:"fixed64,2,opt,name=value,proto3" json:"value,omitempty"`
- // timestamp represents an optional timestamp of the sample in ms.
+ // timestamp represents the timestamp of the exemplar in ms.
//
// For Go, see github.com/prometheus/prometheus/model/timestamp/timestamp.go
// for conversion from/to time.Time to Prometheus timestamp.
- //
- // Note that the "optional" keyword is omitted due to
- // https://cloud.google.com/apis/design/design_patterns.md#optional_primitive_fields
- // Zero value means value not set. If you need to use exactly zero value for
- // the timestamp, use 1 millisecond before or after.
Timestamp int64 `protobuf:"varint,3,opt,name=timestamp,proto3" json:"timestamp,omitempty"`
XXX_NoUnkeyedLiteral struct{} `json:"-"`
XXX_unrecognized []byte `json:"-"`
diff --git a/prompb/io/prometheus/write/v2/types.proto b/prompb/io/prometheus/write/v2/types.proto
index 0cc7b8bc4a..ff6c4936bb 100644
--- a/prompb/io/prometheus/write/v2/types.proto
+++ b/prompb/io/prometheus/write/v2/types.proto
@@ -107,15 +107,10 @@ message Exemplar {
// value represents an exact example value. This can be useful when the exemplar
// is attached to a histogram, which only gives an estimated value through buckets.
double value = 2;
- // timestamp represents an optional timestamp of the sample in ms.
+ // timestamp represents the timestamp of the exemplar in ms.
//
// For Go, see github.com/prometheus/prometheus/model/timestamp/timestamp.go
// for conversion from/to time.Time to Prometheus timestamp.
- //
- // Note that the "optional" keyword is omitted due to
- // https://cloud.google.com/apis/design/design_patterns.md#optional_primitive_fields
- // Zero value means value not set. If you need to use exactly zero value for
- // the timestamp, use 1 millisecond before or after.
int64 timestamp = 3;
}
diff --git a/promql/engine_test.go b/promql/engine_test.go
index 523c0613df..8e618d435c 100644
--- a/promql/engine_test.go
+++ b/promql/engine_test.go
@@ -26,7 +26,6 @@ import (
"time"
"github.com/stretchr/testify/require"
- "go.uber.org/goleak"
"github.com/prometheus/prometheus/model/histogram"
"github.com/prometheus/prometheus/model/labels"
@@ -51,7 +50,7 @@ const (
func TestMain(m *testing.M) {
// Enable experimental functions testing
parser.EnableExperimentalFunctions = true
- goleak.VerifyTestMain(m)
+ testutil.TolerantVerifyLeak(m)
}
func TestQueryConcurrency(t *testing.T) {
diff --git a/promql/functions.go b/promql/functions.go
index dcc2cd7590..b9e93b85af 100644
--- a/promql/functions.go
+++ b/promql/functions.go
@@ -97,9 +97,10 @@ func extrapolatedRate(vals []parser.Value, args parser.Expressions, enh *EvalNod
lastT = samples.Histograms[numSamplesMinusOne].T
var newAnnos annotations.Annotations
resultHistogram, newAnnos = histogramRate(samples.Histograms, isCounter, metricName, args[0].PositionRange())
+ annos.Merge(newAnnos)
if resultHistogram == nil {
// The histograms are not compatible with each other.
- return enh.Out, annos.Merge(newAnnos)
+ return enh.Out, annos
}
case len(samples.Floats) > 1:
numSamplesMinusOne = len(samples.Floats) - 1
@@ -189,6 +190,12 @@ func histogramRate(points []HPoint, isCounter bool, metricName string, pos posra
var annos annotations.Annotations
+ // We check for gauge type histograms in the loop below, but the loop below does not run on the first and last point,
+ // so check the first and last point now.
+ if isCounter && (prev.CounterResetHint == histogram.GaugeType || last.CounterResetHint == histogram.GaugeType) {
+ annos.Add(annotations.NewNativeHistogramNotCounterWarning(metricName, pos))
+ }
+
// First iteration to find out two things:
// - What's the smallest relevant schema?
// - Are all data points histograms?
@@ -241,7 +248,7 @@ func histogramRate(points []HPoint, isCounter bool, metricName string, pos posra
}
h.CounterResetHint = histogram.GaugeType
- return h.Compact(0), nil
+ return h.Compact(0), annos
}
// === delta(Matrix parser.ValueTypeMatrix) (Vector, Annotations) ===
diff --git a/promql/histogram_stats_iterator.go b/promql/histogram_stats_iterator.go
index dfafea5f8c..459d5924ae 100644
--- a/promql/histogram_stats_iterator.go
+++ b/promql/histogram_stats_iterator.go
@@ -48,7 +48,6 @@ func (f *histogramStatsIterator) AtHistogram(h *histogram.Histogram) (int64, *hi
var t int64
t, f.currentH = f.Iterator.AtHistogram(f.currentH)
if value.IsStaleNaN(f.currentH.Sum) {
- f.setLastH(f.currentH)
h = &histogram.Histogram{Sum: f.currentH.Sum}
return t, h
}
@@ -63,9 +62,13 @@ func (f *histogramStatsIterator) AtHistogram(h *histogram.Histogram) (int64, *hi
return t, h
}
- h.CounterResetHint = f.getResetHint(f.currentH)
- h.Count = f.currentH.Count
- h.Sum = f.currentH.Sum
+ returnValue := histogram.Histogram{
+ CounterResetHint: f.getResetHint(f.currentH),
+ Count: f.currentH.Count,
+ Sum: f.currentH.Sum,
+ }
+ returnValue.CopyTo(h)
+
f.setLastH(f.currentH)
return t, h
}
@@ -77,7 +80,6 @@ func (f *histogramStatsIterator) AtFloatHistogram(fh *histogram.FloatHistogram)
var t int64
t, f.currentFH = f.Iterator.AtFloatHistogram(f.currentFH)
if value.IsStaleNaN(f.currentFH.Sum) {
- f.setLastFH(f.currentFH)
return t, &histogram.FloatHistogram{Sum: f.currentFH.Sum}
}
@@ -91,9 +93,13 @@ func (f *histogramStatsIterator) AtFloatHistogram(fh *histogram.FloatHistogram)
return t, fh
}
- fh.CounterResetHint = f.getFloatResetHint(f.currentFH.CounterResetHint)
- fh.Count = f.currentFH.Count
- fh.Sum = f.currentFH.Sum
+ returnValue := histogram.FloatHistogram{
+ CounterResetHint: f.getFloatResetHint(f.currentFH.CounterResetHint),
+ Count: f.currentFH.Count,
+ Sum: f.currentFH.Sum,
+ }
+ returnValue.CopyTo(fh)
+
f.setLastFH(f.currentFH)
return t, fh
}
diff --git a/promql/histogram_stats_iterator_test.go b/promql/histogram_stats_iterator_test.go
index b71a9d6029..7a2953d3e2 100644
--- a/promql/histogram_stats_iterator_test.go
+++ b/promql/histogram_stats_iterator_test.go
@@ -14,62 +14,132 @@
package promql
import (
+ "fmt"
+ "math"
"testing"
"github.com/stretchr/testify/require"
"github.com/prometheus/prometheus/model/histogram"
"github.com/prometheus/prometheus/model/labels"
+ "github.com/prometheus/prometheus/model/value"
"github.com/prometheus/prometheus/tsdb/chunkenc"
"github.com/prometheus/prometheus/tsdb/tsdbutil"
)
func TestHistogramStatsDecoding(t *testing.T) {
- histograms := []*histogram.Histogram{
- tsdbutil.GenerateTestHistogram(0),
- tsdbutil.GenerateTestHistogram(1),
- tsdbutil.GenerateTestHistogram(2),
- tsdbutil.GenerateTestHistogram(2),
- }
- histograms[0].CounterResetHint = histogram.NotCounterReset
- histograms[1].CounterResetHint = histogram.UnknownCounterReset
- histograms[2].CounterResetHint = histogram.CounterReset
- histograms[3].CounterResetHint = histogram.UnknownCounterReset
-
- expectedHints := []histogram.CounterResetHint{
- histogram.NotCounterReset,
- histogram.NotCounterReset,
- histogram.CounterReset,
- histogram.NotCounterReset,
+ cases := []struct {
+ name string
+ histograms []*histogram.Histogram
+ expectedHints []histogram.CounterResetHint
+ }{
+ {
+ name: "unknown counter reset triggers detection",
+ histograms: []*histogram.Histogram{
+ tsdbutil.GenerateTestHistogramWithHint(0, histogram.NotCounterReset),
+ tsdbutil.GenerateTestHistogramWithHint(1, histogram.UnknownCounterReset),
+ tsdbutil.GenerateTestHistogramWithHint(2, histogram.CounterReset),
+ tsdbutil.GenerateTestHistogramWithHint(2, histogram.UnknownCounterReset),
+ },
+ expectedHints: []histogram.CounterResetHint{
+ histogram.NotCounterReset,
+ histogram.NotCounterReset,
+ histogram.CounterReset,
+ histogram.NotCounterReset,
+ },
+ },
+ {
+ name: "stale sample before unknown reset hint",
+ histograms: []*histogram.Histogram{
+ tsdbutil.GenerateTestHistogramWithHint(0, histogram.NotCounterReset),
+ tsdbutil.GenerateTestHistogramWithHint(1, histogram.UnknownCounterReset),
+ {Sum: math.Float64frombits(value.StaleNaN)},
+ tsdbutil.GenerateTestHistogramWithHint(1, histogram.UnknownCounterReset),
+ },
+ expectedHints: []histogram.CounterResetHint{
+ histogram.NotCounterReset,
+ histogram.NotCounterReset,
+ histogram.UnknownCounterReset,
+ histogram.NotCounterReset,
+ },
+ },
+ {
+ name: "unknown counter reset at the beginning",
+ histograms: []*histogram.Histogram{
+ tsdbutil.GenerateTestHistogramWithHint(1, histogram.UnknownCounterReset),
+ },
+ expectedHints: []histogram.CounterResetHint{
+ histogram.NotCounterReset,
+ },
+ },
+ {
+ name: "detect real counter reset",
+ histograms: []*histogram.Histogram{
+ tsdbutil.GenerateTestHistogramWithHint(2, histogram.UnknownCounterReset),
+ tsdbutil.GenerateTestHistogramWithHint(1, histogram.UnknownCounterReset),
+ },
+ expectedHints: []histogram.CounterResetHint{
+ histogram.NotCounterReset,
+ histogram.CounterReset,
+ },
+ },
+ {
+ name: "detect real counter reset after stale NaN",
+ histograms: []*histogram.Histogram{
+ tsdbutil.GenerateTestHistogramWithHint(2, histogram.UnknownCounterReset),
+ {Sum: math.Float64frombits(value.StaleNaN)},
+ tsdbutil.GenerateTestHistogramWithHint(1, histogram.UnknownCounterReset),
+ },
+ expectedHints: []histogram.CounterResetHint{
+ histogram.NotCounterReset,
+ histogram.UnknownCounterReset,
+ histogram.CounterReset,
+ },
+ },
}
- t.Run("histogram_stats", func(t *testing.T) {
- decodedStats := make([]*histogram.Histogram, 0)
- statsIterator := NewHistogramStatsIterator(newHistogramSeries(histograms).Iterator(nil))
- for statsIterator.Next() != chunkenc.ValNone {
- _, h := statsIterator.AtHistogram(nil)
- decodedStats = append(decodedStats, h)
- }
- for i := 0; i < len(histograms); i++ {
- require.Equal(t, expectedHints[i], decodedStats[i].CounterResetHint)
- require.Equal(t, histograms[i].Count, decodedStats[i].Count)
- require.Equal(t, histograms[i].Sum, decodedStats[i].Sum)
- }
- })
- t.Run("float_histogram_stats", func(t *testing.T) {
- decodedStats := make([]*histogram.FloatHistogram, 0)
- statsIterator := NewHistogramStatsIterator(newHistogramSeries(histograms).Iterator(nil))
- for statsIterator.Next() != chunkenc.ValNone {
- _, h := statsIterator.AtFloatHistogram(nil)
- decodedStats = append(decodedStats, h)
- }
- for i := 0; i < len(histograms); i++ {
- fh := histograms[i].ToFloat(nil)
- require.Equal(t, expectedHints[i], decodedStats[i].CounterResetHint)
- require.Equal(t, fh.Count, decodedStats[i].Count)
- require.Equal(t, fh.Sum, decodedStats[i].Sum)
- }
- })
+ for _, tc := range cases {
+ t.Run(tc.name, func(t *testing.T) {
+ t.Run("histogram_stats", func(t *testing.T) {
+ decodedStats := make([]*histogram.Histogram, 0)
+ statsIterator := NewHistogramStatsIterator(newHistogramSeries(tc.histograms).Iterator(nil))
+ for statsIterator.Next() != chunkenc.ValNone {
+ _, h := statsIterator.AtHistogram(nil)
+ decodedStats = append(decodedStats, h)
+ }
+ for i := 0; i < len(tc.histograms); i++ {
+ require.Equal(t, tc.expectedHints[i], decodedStats[i].CounterResetHint, fmt.Sprintf("mismatch in counter reset hint for histogram %d", i))
+ h := tc.histograms[i]
+ if value.IsStaleNaN(h.Sum) {
+ require.True(t, value.IsStaleNaN(decodedStats[i].Sum))
+ require.Equal(t, uint64(0), decodedStats[i].Count)
+ } else {
+ require.Equal(t, tc.histograms[i].Count, decodedStats[i].Count)
+ require.Equal(t, tc.histograms[i].Sum, decodedStats[i].Sum)
+ }
+ }
+ })
+ t.Run("float_histogram_stats", func(t *testing.T) {
+ decodedStats := make([]*histogram.FloatHistogram, 0)
+ statsIterator := NewHistogramStatsIterator(newHistogramSeries(tc.histograms).Iterator(nil))
+ for statsIterator.Next() != chunkenc.ValNone {
+ _, h := statsIterator.AtFloatHistogram(nil)
+ decodedStats = append(decodedStats, h)
+ }
+ for i := 0; i < len(tc.histograms); i++ {
+ require.Equal(t, tc.expectedHints[i], decodedStats[i].CounterResetHint)
+ fh := tc.histograms[i].ToFloat(nil)
+ if value.IsStaleNaN(fh.Sum) {
+ require.True(t, value.IsStaleNaN(decodedStats[i].Sum))
+ require.Equal(t, float64(0), decodedStats[i].Count)
+ } else {
+ require.Equal(t, fh.Count, decodedStats[i].Count)
+ require.Equal(t, fh.Sum, decodedStats[i].Sum)
+ }
+ }
+ })
+ })
+ }
}
type histogramSeries struct {
diff --git a/promql/parser/generated_parser.y b/promql/parser/generated_parser.y
index b99e67424f..b8e6aa373a 100644
--- a/promql/parser/generated_parser.y
+++ b/promql/parser/generated_parser.y
@@ -84,6 +84,7 @@ NEGATIVE_BUCKETS_DESC
ZERO_BUCKET_DESC
ZERO_BUCKET_WIDTH_DESC
CUSTOM_VALUES_DESC
+COUNTER_RESET_HINT_DESC
%token histogramDescEnd
// Operators.
@@ -149,6 +150,14 @@ START
END
%token preprocessorEnd
+// Counter reset hints.
+%token counterResetHintsStart
+%token -
+UNKNOWN_COUNTER_RESET
+COUNTER_RESET
+NOT_COUNTER_RESET
+GAUGE_TYPE
+%token counterResetHintsEnd
// Start symbols for the generated parser.
%token startSymbolsStart
@@ -163,7 +172,7 @@ START_METRIC_SELECTOR
// Type definitions for grammar rules.
%type label_match_list
%type label_matcher
-%type
- aggregate_op grouping_label match_op maybe_label metric_identifier unary_op at_modifier_preprocessors string_identifier
+%type
- aggregate_op grouping_label match_op maybe_label metric_identifier unary_op at_modifier_preprocessors string_identifier counter_reset_hint
%type label_set metric
%type label_set_list
%type