From 7f99fce74ced2e150717df20f4272df4f23b7d21 Mon Sep 17 00:00:00 2001 From: SungJin1212 Date: Thu, 21 May 2026 20:21:02 +0900 Subject: [PATCH 1/2] upgrade prometheus 3.9.1 Signed-off-by: SungJin1212 --- go.mod | 48 +-- go.sum | 186 ++++---- integration/e2e/images/images.go | 2 +- pkg/api/handlers.go | 2 + pkg/ingester/ingester.go | 6 +- pkg/querier/error_translate_queryable_test.go | 2 + pkg/querier/stats_renderer_test.go | 2 + pkg/ruler/compat.go | 8 +- pkg/util/validation/validate.go | 10 +- pkg/util/validation/validate_test.go | 48 ++- .../azure-sdk-for-go/sdk/azcore/CHANGELOG.md | 11 + .../internal/resource/resource_identifier.go | 1 + .../sdk/azcore/internal/exported/exported.go | 2 +- .../sdk/azcore/internal/shared/constants.go | 2 +- .../sdk/azcore/runtime/pager.go | 11 +- .../sdk/azidentity/CHANGELOG.md | 29 ++ .../azure-sdk-for-go/sdk/azidentity/README.md | 3 +- .../sdk/azidentity/TOKEN_CACHING.MD | 1 + .../sdk/azidentity/TROUBLESHOOTING.md | 29 ++ .../azidentity/azure_powershell_credential.go | 234 ++++++++++ .../azidentity/default_azure_credential.go | 29 +- .../azidentity/developer_credential_util.go | 25 +- .../developer_credential_util_nonwindows.go | 17 + .../developer_credential_util_windows.go | 22 + .../azure-sdk-for-go/sdk/azidentity/errors.go | 2 + .../sdk/azidentity/version.go | 2 +- .../apps/confidential/confidential.go | 103 ++++- .../apps/internal/base/base.go | 47 ++- .../apps/internal/base/storage/items.go | 17 +- .../apps/internal/base/storage/storage.go | 51 ++- .../oauth/ops/accesstokens/accesstokens.go | 17 + .../internal/oauth/ops/authority/authority.go | 84 +++- .../apps/public/public.go | 4 +- vendor/github.com/hashicorp/consul/api/txn.go | 2 +- .../github.com/hashicorp/go-version/LICENSE | 2 +- .../github.com/hashicorp/go-version/README.md | 5 +- .../hashicorp/go-version/constraint.go | 73 ++-- .../hashicorp/go-version/version.go | 64 ++- .../go-version/version_collection.go | 2 +- vendor/github.com/miekg/dns/README.md | 312 +++++++------- vendor/github.com/miekg/dns/edns.go | 131 +++++- vendor/github.com/miekg/dns/server.go | 4 +- vendor/github.com/miekg/dns/version.go | 2 +- .../prometheus/discovery/discovery.go | 10 +- .../prometheus/discovery/dns/dns.go | 15 +- .../prometheus/discovery/manager.go | 21 + .../prometheus/discovery/metrics_refresh.go | 29 +- .../prometheus/discovery/refresh/refresh.go | 4 +- .../prometheus/discovery/registry.go | 10 + .../model/histogram/float_histogram.go | 76 +++- .../prometheus/model/histogram/generic.go | 37 +- .../prometheus/model/histogram/histogram.go | 46 +- .../prometheus/model/labels/labels_common.go | 4 +- .../model/labels/labels_dedupelabels.go | 3 + .../model/labels/labels_slicelabels.go | 15 +- .../model/labels/labels_stringlabels.go | 3 + .../prometheus/model/metadata/metadata.go | 26 +- .../prometheus/model/textparse/interface.go | 14 +- .../prometheus/model/textparse/nhcbparse.go | 20 +- .../model/textparse/openmetricsparse.go | 92 ++-- .../prometheus/model/textparse/promparse.go | 4 +- .../model/textparse/protobufparse.go | 27 +- .../prometheus/notifier/alertmanagerset.go | 3 +- .../prometheus/prometheus/notifier/manager.go | 4 +- .../prometheus/prometheus/notifier/metric.go | 33 +- .../prometheus/prometheus/promql/engine.go | 372 +++++++++++----- .../prometheus/prometheus/promql/functions.go | 33 +- .../prometheus/prometheus/promql/info.go | 14 +- .../prometheus/promql/parser/features.go | 57 +++ .../prometheus/promql/parser/printer.go | 19 +- .../prometheus/promql/promqltest/test.go | 28 +- .../promqltest/testdata/extended_vectors.test | 2 - .../promql/promqltest/testdata/functions.test | 35 ++ .../testdata/name_label_dropping.test | 45 ++ .../promql/promqltest/testdata/operators.test | 58 +++ .../prometheus/prometheus/rules/alerting.go | 6 +- .../prometheus/prometheus/rules/group.go | 49 ++- .../prometheus/prometheus/rules/manager.go | 12 + .../prometheus/prometheus/rules/recording.go | 6 +- .../prometheus/prometheus/schema/labels.go | 38 +- .../prometheus/prometheus/scrape/manager.go | 13 +- .../prometheus/prometheus/scrape/metrics.go | 29 ++ .../prometheus/prometheus/scrape/scrape.go | 85 ++-- .../prometheus/prometheus/scrape/target.go | 24 +- .../prometheus/prometheus/storage/fanout.go | 12 +- .../prometheus/storage/interface.go | 95 +++-- .../prometheus/storage/interface_append.go | 182 ++++++++ .../storage/remote/azuread/azuread.go | 25 +- .../prometheus/storage/remote/codec.go | 98 +++-- .../combined_appender.go | 66 +-- .../prometheusremotewrite/histograms.go | 8 +- .../prometheusremotewrite/metrics_to_prw.go | 8 - .../number_data_points.go | 8 +- .../storage/remote/queue_manager.go | 377 ++++++++++------- .../prometheus/storage/remote/write.go | 16 +- .../storage/remote/write_handler.go | 52 ++- .../prometheus/template/template.go | 27 ++ .../prometheus/prometheus/tsdb/block.go | 10 - .../prometheus/prometheus/tsdb/blockwriter.go | 6 + .../tsdb/chunkenc/float_histogram.go | 17 +- .../prometheus/tsdb/chunkenc/histogram.go | 34 +- .../prometheus/tsdb/chunks/head_chunks.go | 20 +- .../prometheus/prometheus/tsdb/compact.go | 21 +- .../prometheus/prometheus/tsdb/db.go | 87 +++- .../prometheus/prometheus/tsdb/head.go | 15 + .../prometheus/prometheus/tsdb/head_append.go | 139 +++--- .../prometheus/tsdb/head_append_v2.go | 398 ++++++++++++++++++ .../prometheus/prometheus/tsdb/head_read.go | 15 - .../prometheus/prometheus/tsdb/index/index.go | 57 --- .../prometheus/tsdb/index/postings.go | 83 ++-- .../prometheus/tsdb/ooo_head_read.go | 4 - .../prometheus/tsdb/record/record.go | 8 +- .../prometheus/prometheus/tsdb/testutil.go | 16 +- .../prometheus/util/features/features.go | 127 ++++++ .../prometheus/prometheus/web/api/v1/api.go | 40 +- .../collector/component/component.go | 2 +- .../collector/component/host.go | 2 + .../collector/confmap/internal/decoder.go | 91 ++-- .../collector/confmap/xconfmap/config.go | 2 +- .../internal/generated_proto_location.go | 40 +- .../pdata/internal/generated_proto_profile.go | 125 ++---- .../internal/generated_proto_valuetype.go | 37 +- .../collector/pdata/internal/proto/marshal.go | 1 - .../collector/pdata/internal/proto/size.go | 1 - .../pdata/internal/proto/unmarshal.go | 3 - .../collector/pdata/pcommon/timestamp.go | 2 - .../collector/pdata/pcommon/value.go | 2 - vendor/go.uber.org/zap/.golangci.yml | 2 +- vendor/go.uber.org/zap/CHANGELOG.md | 10 + vendor/go.uber.org/zap/CODE_OF_CONDUCT.md | 4 +- vendor/go.uber.org/zap/LICENSE | 2 +- vendor/go.uber.org/zap/Makefile | 2 +- vendor/go.uber.org/zap/field.go | 10 + vendor/go.uber.org/zap/http_handler.go | 2 +- vendor/go.uber.org/zap/logger.go | 6 +- vendor/go.uber.org/zap/options.go | 6 +- vendor/go.uber.org/zap/sink.go | 2 +- .../zap/zapcore/buffered_write_syncer.go | 23 +- .../zap/zapcore/console_encoder.go | 2 +- vendor/go.uber.org/zap/zapcore/entry.go | 14 +- vendor/go.uber.org/zap/zapcore/lazy_with.go | 35 +- vendor/go.uber.org/zap/zapcore/level.go | 14 +- .../iamcredentials/v1/iamcredentials-api.json | 2 +- .../google.golang.org/api/internal/version.go | 2 +- vendor/modules.txt | 57 +-- 145 files changed, 4026 insertions(+), 1623 deletions(-) create mode 100644 vendor/github.com/Azure/azure-sdk-for-go/sdk/azidentity/azure_powershell_credential.go create mode 100644 vendor/github.com/Azure/azure-sdk-for-go/sdk/azidentity/developer_credential_util_nonwindows.go create mode 100644 vendor/github.com/Azure/azure-sdk-for-go/sdk/azidentity/developer_credential_util_windows.go create mode 100644 vendor/github.com/prometheus/prometheus/promql/parser/features.go create mode 100644 vendor/github.com/prometheus/prometheus/storage/interface_append.go create mode 100644 vendor/github.com/prometheus/prometheus/tsdb/head_append_v2.go create mode 100644 vendor/github.com/prometheus/prometheus/util/features/features.go diff --git a/go.mod b/go.mod index ff195426da8..a4d850615f8 100644 --- a/go.mod +++ b/go.mod @@ -24,7 +24,7 @@ require ( github.com/gorilla/mux v1.8.1 github.com/grafana/regexp v0.0.0-20250905093917-f7b3be9d1853 github.com/grpc-ecosystem/go-grpc-middleware v1.4.0 - github.com/hashicorp/consul/api v1.32.0 + github.com/hashicorp/consul/api v1.32.1 github.com/hashicorp/go-cleanhttp v0.5.2 github.com/hashicorp/go-metrics v0.5.4 github.com/hashicorp/go-sockaddr v1.0.7 @@ -44,7 +44,7 @@ require ( github.com/prometheus/client_model v0.6.2 github.com/prometheus/common v0.67.5 // Prometheus maps version 3.x.y to tags v0.30x.y. - github.com/prometheus/prometheus v0.308.1 + github.com/prometheus/prometheus v0.309.1 github.com/segmentio/fasthash v1.0.3 github.com/sony/gobreaker v1.0.0 github.com/spf13/afero v1.11.0 @@ -94,7 +94,7 @@ require ( github.com/prometheus/procfs v0.16.1 github.com/sercand/kuberesolver/v5 v5.1.1 github.com/tjhop/slog-gokit v0.1.4 - go.opentelemetry.io/collector/pdata v1.45.0 + go.opentelemetry.io/collector/pdata v1.48.0 go.uber.org/automaxprocs v1.6.0 google.golang.org/protobuf v1.36.11 ) @@ -108,11 +108,11 @@ require ( cloud.google.com/go/iam v1.5.2 // indirect cloud.google.com/go/monitoring v1.24.2 // indirect cloud.google.com/go/storage v1.50.0 // indirect - github.com/Azure/azure-sdk-for-go/sdk/azcore v1.19.1 // indirect - github.com/Azure/azure-sdk-for-go/sdk/azidentity v1.12.0 // indirect + github.com/Azure/azure-sdk-for-go/sdk/azcore v1.20.0 // indirect + github.com/Azure/azure-sdk-for-go/sdk/azidentity v1.13.1 // indirect github.com/Azure/azure-sdk-for-go/sdk/internal v1.11.2 // indirect github.com/Azure/azure-sdk-for-go/sdk/storage/azblob v1.6.1 // indirect - github.com/AzureAD/microsoft-authentication-library-for-go v1.5.0 // indirect + github.com/AzureAD/microsoft-authentication-library-for-go v1.6.0 // indirect github.com/GoogleCloudPlatform/opentelemetry-operations-go/detectors/gcp v1.31.0 // indirect github.com/GoogleCloudPlatform/opentelemetry-operations-go/exporter/metric v0.50.0 // indirect github.com/GoogleCloudPlatform/opentelemetry-operations-go/internal/resourcemapping v0.52.0 // indirect @@ -185,9 +185,9 @@ require ( github.com/golang-jwt/jwt/v5 v5.3.0 // indirect github.com/golang/groupcache v0.0.0-20241129210726-2c02b8208cf8 // indirect github.com/google/btree v1.1.3 // indirect - github.com/google/pprof v0.0.0-20250923004556-9e5a51aed1e8 // indirect + github.com/google/pprof v0.0.0-20251213031049-b05bdaca462f // indirect github.com/google/s2a-go v0.1.9 // indirect - github.com/googleapis/enterprise-certificate-proxy v0.3.6 // indirect + github.com/googleapis/enterprise-certificate-proxy v0.3.7 // indirect github.com/googleapis/gax-go/v2 v2.15.0 // indirect github.com/grpc-ecosystem/go-grpc-middleware/v2 v2.3.2 // indirect github.com/grpc-ecosystem/grpc-gateway/v2 v2.28.0 // indirect @@ -197,7 +197,7 @@ require ( github.com/hashicorp/go-msgpack v0.5.5 // indirect github.com/hashicorp/go-multierror v1.1.1 // indirect github.com/hashicorp/go-rootcerts v1.0.2 // indirect - github.com/hashicorp/go-version v1.7.0 // indirect + github.com/hashicorp/go-version v1.8.0 // indirect github.com/hashicorp/golang-lru v0.6.0 // indirect github.com/hashicorp/serf v0.10.1 // indirect github.com/jessevdk/go-flags v1.6.1 // indirect @@ -217,7 +217,7 @@ require ( github.com/mdlayher/socket v0.5.1 // indirect github.com/mdlayher/vsock v1.2.1 // indirect github.com/metalmatze/signal v0.0.0-20210307161603-1c9aa721a97a // indirect - github.com/miekg/dns v1.1.68 // indirect + github.com/miekg/dns v1.1.69 // indirect github.com/minio/crc64nvme v1.1.1 // indirect github.com/minio/md5-simd v1.1.2 // indirect github.com/minio/sha256-simd v1.0.1 // indirect @@ -230,9 +230,9 @@ require ( github.com/mwitkow/go-conntrack v0.0.0-20190716064945-2f068394615f // indirect github.com/ncw/swift v1.0.53 // indirect github.com/oklog/run v1.2.0 // indirect - github.com/open-telemetry/opentelemetry-collector-contrib/internal/exp/metrics v0.139.0 // indirect - github.com/open-telemetry/opentelemetry-collector-contrib/pkg/pdatautil v0.139.0 // indirect - github.com/open-telemetry/opentelemetry-collector-contrib/processor/deltatocumulativeprocessor v0.139.0 // indirect + github.com/open-telemetry/opentelemetry-collector-contrib/internal/exp/metrics v0.142.0 // indirect + github.com/open-telemetry/opentelemetry-collector-contrib/pkg/pdatautil v0.142.0 // indirect + github.com/open-telemetry/opentelemetry-collector-contrib/processor/deltatocumulativeprocessor v0.142.0 // indirect github.com/parquet-go/bitpack v1.0.0 // indirect github.com/parquet-go/jsonlite v1.0.0 // indirect github.com/philhofer/fwd v1.2.0 // indirect @@ -263,13 +263,13 @@ require ( github.com/yuin/gopher-lua v1.1.1 // indirect go.opencensus.io v0.24.0 // indirect go.opentelemetry.io/auto/sdk v1.2.1 // indirect - go.opentelemetry.io/collector/component v1.45.0 // indirect - go.opentelemetry.io/collector/confmap v1.45.0 // indirect - go.opentelemetry.io/collector/confmap/xconfmap v0.139.0 // indirect - go.opentelemetry.io/collector/consumer v1.45.0 // indirect - go.opentelemetry.io/collector/featuregate v1.45.0 // indirect - go.opentelemetry.io/collector/pipeline v1.45.0 // indirect - go.opentelemetry.io/collector/processor v1.45.0 // indirect + go.opentelemetry.io/collector/component v1.48.0 // indirect + go.opentelemetry.io/collector/confmap v1.48.0 // indirect + go.opentelemetry.io/collector/confmap/xconfmap v0.142.0 // indirect + go.opentelemetry.io/collector/consumer v1.48.0 // indirect + go.opentelemetry.io/collector/featuregate v1.48.0 // indirect + go.opentelemetry.io/collector/pipeline v1.48.0 // indirect + go.opentelemetry.io/collector/processor v1.48.0 // indirect go.opentelemetry.io/collector/semconv v0.128.0 // indirect go.opentelemetry.io/contrib/detectors/gcp v1.39.0 // indirect go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.61.0 // indirect @@ -285,7 +285,7 @@ require ( go.opentelemetry.io/proto/otlp v1.9.0 // indirect go.uber.org/goleak v1.3.0 // indirect go.uber.org/multierr v1.11.0 // indirect - go.uber.org/zap v1.27.0 // indirect + go.uber.org/zap v1.27.1 // indirect go.yaml.in/yaml/v2 v2.4.3 // indirect go.yaml.in/yaml/v3 v3.0.4 // indirect go4.org/intern v0.0.0-20230525184215-6c62f75575cb // indirect @@ -298,13 +298,13 @@ require ( golang.org/x/text v0.35.0 // indirect golang.org/x/tools v0.42.0 // indirect gonum.org/v1/gonum v0.17.0 // indirect - google.golang.org/api v0.252.0 // indirect + google.golang.org/api v0.257.0 // indirect google.golang.org/genproto v0.0.0-20250603155806-513f23925822 // indirect google.golang.org/genproto/googleapis/api v0.0.0-20260209200024-4cfbd4190f57 // indirect google.golang.org/genproto/googleapis/rpc v0.0.0-20260209200024-4cfbd4190f57 // indirect gopkg.in/telebot.v3 v3.3.8 // indirect - k8s.io/apimachinery v0.34.1 // indirect - k8s.io/client-go v0.34.1 // indirect + k8s.io/apimachinery v0.34.3 // indirect + k8s.io/client-go v0.34.3 // indirect k8s.io/klog/v2 v2.130.1 // indirect k8s.io/utils v0.0.0-20250604170112-4c0f3b243397 // indirect ) diff --git a/go.sum b/go.sum index 725e2bbbf99..6225f9fa1d4 100644 --- a/go.sum +++ b/go.sum @@ -76,10 +76,10 @@ cloud.google.com/go/storage v1.50.0/go.mod h1:l7XeiD//vx5lfqE3RavfmU9yvk5Pp0Zhcv cloud.google.com/go/trace v1.11.6 h1:2O2zjPzqPYAHrn3OKl029qlqG6W8ZdYaOWRyr8NgMT4= cloud.google.com/go/trace v1.11.6/go.mod h1:GA855OeDEBiBMzcckLPE2kDunIpC72N+Pq8WFieFjnI= dmitri.shuralyov.com/gpu/mtl v0.0.0-20190408044501-666a987793e9/go.mod h1:H6x//7gZCb22OMCxBHrMx7a5I7Hp++hsVxbQ4BYO7hU= -github.com/Azure/azure-sdk-for-go/sdk/azcore v1.19.1 h1:5YTBM8QDVIBN3sxBil89WfdAAqDZbyJTgh688DSxX5w= -github.com/Azure/azure-sdk-for-go/sdk/azcore v1.19.1/go.mod h1:YD5h/ldMsG0XiIw7PdyNhLxaM317eFh5yNLccNfGdyw= -github.com/Azure/azure-sdk-for-go/sdk/azidentity v1.12.0 h1:wL5IEG5zb7BVv1Kv0Xm92orq+5hB5Nipn3B5tn4Rqfk= -github.com/Azure/azure-sdk-for-go/sdk/azidentity v1.12.0/go.mod h1:J7MUC/wtRpfGVbQ5sIItY5/FuVWmvzlY21WAOfQnq/I= +github.com/Azure/azure-sdk-for-go/sdk/azcore v1.20.0 h1:JXg2dwJUmPB9JmtVmdEB16APJ7jurfbY5jnfXpJoRMc= +github.com/Azure/azure-sdk-for-go/sdk/azcore v1.20.0/go.mod h1:YD5h/ldMsG0XiIw7PdyNhLxaM317eFh5yNLccNfGdyw= +github.com/Azure/azure-sdk-for-go/sdk/azidentity v1.13.1 h1:Hk5QBxZQC1jb2Fwj6mpzme37xbCDdNTxU7O9eb5+LB4= +github.com/Azure/azure-sdk-for-go/sdk/azidentity v1.13.1/go.mod h1:IYus9qsFobWIc2YVwe/WPjcnyCkPKtnHAqUYeebc8z0= github.com/Azure/azure-sdk-for-go/sdk/azidentity/cache v0.3.2 h1:yz1bePFlP5Vws5+8ez6T3HWXPmwOK7Yvq8QxDBD3SKY= github.com/Azure/azure-sdk-for-go/sdk/azidentity/cache v0.3.2/go.mod h1:Pa9ZNPuoNu/GztvBSKk9J1cDJW6vk/n0zLtV4mgd8N8= github.com/Azure/azure-sdk-for-go/sdk/internal v1.11.2 h1:9iefClla7iYpfYWdzPCRDozdmndjTm8DXdpCzPajMgA= @@ -96,8 +96,8 @@ github.com/Azure/go-ansiterm v0.0.0-20230124172434-306776ec8161 h1:L/gRVlceqvL25 github.com/Azure/go-ansiterm v0.0.0-20230124172434-306776ec8161/go.mod h1:xomTg63KZ2rFqZQzSB4Vz2SUXa1BpHTVz9L5PTmPC4E= github.com/AzureAD/microsoft-authentication-extensions-for-go/cache v0.1.1 h1:WJTmL004Abzc5wDB5VtZG2PJk5ndYDgVacGqfirKxjM= github.com/AzureAD/microsoft-authentication-extensions-for-go/cache v0.1.1/go.mod h1:tCcJZ0uHAmvjsVYzEFivsRTN00oz5BEsRgQHu5JZ9WE= -github.com/AzureAD/microsoft-authentication-library-for-go v1.5.0 h1:XkkQbfMyuH2jTSjQjSoihryI8GINRcs4xp8lNawg0FI= -github.com/AzureAD/microsoft-authentication-library-for-go v1.5.0/go.mod h1:HKpQxkWaGLJ+D/5H8QRpyQXA1eKjxkFlOMwck5+33Jk= +github.com/AzureAD/microsoft-authentication-library-for-go v1.6.0 h1:XRzhVemXdgvJqCH0sFfrBUTnUJSBrBf7++ypk+twtRs= +github.com/AzureAD/microsoft-authentication-library-for-go v1.6.0/go.mod h1:HKpQxkWaGLJ+D/5H8QRpyQXA1eKjxkFlOMwck5+33Jk= github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU= github.com/BurntSushi/xgb v0.0.0-20160522181843-27f122750802/go.mod h1:IVnqGOEym/WlBOVXweHU+Q+/VP0lqqI8lqeDx9IjBqo= github.com/Code-Hex/go-generics-cache v1.5.1 h1:6vhZGc5M7Y/YD8cIUcY8kcuQLB4cHR7U+0KMqAA0KcU= @@ -165,18 +165,18 @@ github.com/aws/aws-sdk-go-v2/internal/ini v1.8.6 h1:qYQ4pzQ2Oz6WpQ8T3HvGHnZydA72 github.com/aws/aws-sdk-go-v2/internal/ini v1.8.6/go.mod h1:O3h0IK87yXci+kg6flUKzJnWeziQUKciKrLjcatSNcY= github.com/aws/aws-sdk-go-v2/service/dynamodb v1.50.1 h1:MXUnj1TKjwQvotPPHFMfynlUljcpl5UccMrkiauKdWI= github.com/aws/aws-sdk-go-v2/service/dynamodb v1.50.1/go.mod h1:fe3UQAYwylCQRlGnihsqU/tTQkrc2nrW/IhWYwlW9vg= -github.com/aws/aws-sdk-go-v2/service/ec2 v1.262.0 h1:5qBb1XV/D18qtCHd3bmmxoVglI+fZ4QWuS/EB8kIXYQ= -github.com/aws/aws-sdk-go-v2/service/ec2 v1.262.0/go.mod h1:NDdDLLW5PtLLXN661gKcvJvqAH5OBXsfhMlmKVu1/pY= -github.com/aws/aws-sdk-go-v2/service/ecs v1.67.2 h1:oeICOX/+D0XXV1aMYJPXVe3CO37zYr7fB6HFgxchleU= -github.com/aws/aws-sdk-go-v2/service/ecs v1.67.2/go.mod h1:rrhqfkXfa2DSNq0RyFhnnFEAyI+yJB4+2QlZKeJvMjs= +github.com/aws/aws-sdk-go-v2/service/ec2 v1.277.0 h1:RHJSkRXDGkAKrV4CTEsZsZkOmSpxXKO4aKx4rXd94K4= +github.com/aws/aws-sdk-go-v2/service/ec2 v1.277.0/go.mod h1:Wg68QRgy2gEGGdmTPU/UbVpdv8sM14bUZmF64KFwAsY= +github.com/aws/aws-sdk-go-v2/service/ecs v1.69.5 h1:5nkhwt0d/gjuT3AQ2LUK0aFRNB3MGlzB2elqy/ZsKP4= +github.com/aws/aws-sdk-go-v2/service/ecs v1.69.5/go.mod h1:LQMlcWBoiFVD3vUVEz42ST0yTiaDujv2dRE6sXt1yPE= github.com/aws/aws-sdk-go-v2/service/internal/accept-encoding v1.13.7 h1:5EniKhLZe4xzL7a+fU3C2tfUN4nWIqlLesfrjkuPFTY= github.com/aws/aws-sdk-go-v2/service/internal/accept-encoding v1.13.7/go.mod h1:x0nZssQ3qZSnIcePWLvcoFisRXJzcTVvYpAAdYX8+GI= github.com/aws/aws-sdk-go-v2/service/internal/endpoint-discovery v1.11.6 h1:34ojKW9OV123FZ6Q8Nua3Uwy6yVTcshZ+gLE4gpMDEs= github.com/aws/aws-sdk-go-v2/service/internal/endpoint-discovery v1.11.6/go.mod h1:sXXWh1G9LKKkNbuR0f0ZPd/IvDXlMGiag40opt4XEgY= github.com/aws/aws-sdk-go-v2/service/internal/presigned-url v1.13.21 h1:c31//R3xgIJMSC8S6hEVq+38DcvUlgFY0FM6mSI5oto= github.com/aws/aws-sdk-go-v2/service/internal/presigned-url v1.13.21/go.mod h1:r6+pf23ouCB718FUxaqzZdbpYFyDtehyZcmP5KL9FkA= -github.com/aws/aws-sdk-go-v2/service/lightsail v1.50.4 h1:/1o2AYwHJojUDeMvQNyJiKZwcWCc3e4kQuTXqRLuThc= -github.com/aws/aws-sdk-go-v2/service/lightsail v1.50.4/go.mod h1:Nn2xx6HojGuNMtUFxxz/nyNLSS+tHMRsMhe3+W3wB5k= +github.com/aws/aws-sdk-go-v2/service/lightsail v1.50.10 h1:MQuZZ6Tq1qQabPlkVxrCMdyVl70Ogl4AERZKo+y9Wzo= +github.com/aws/aws-sdk-go-v2/service/lightsail v1.50.10/go.mod h1:U5C3JME1ibKESmpzBAqlRpTYZfVbTqrb5ICJm+sVVd8= github.com/aws/aws-sdk-go-v2/service/signin v1.0.9 h1:QKZH0S178gCmFEgst8hN0mCX1KxLgHBKKY/CLqwP8lg= github.com/aws/aws-sdk-go-v2/service/signin v1.0.9/go.mod h1:7yuQJoT+OoH8aqIxw9vwF+8KpvLZ8AWmvmUWHsGQZvI= github.com/aws/aws-sdk-go-v2/service/sns v1.39.15 h1:rOWMUrXJPcTXnk75ja6Bxv1P+j83dPhIWjfJ2cujj34= @@ -275,8 +275,8 @@ github.com/dgryski/go-rendezvous v0.0.0-20200823014737-9f7001d12a5f h1:lO4WD4F/r github.com/dgryski/go-rendezvous v0.0.0-20200823014737-9f7001d12a5f/go.mod h1:cuUVRXasLTGF7a8hSLbxyZXjz+1KgoB3wDUb6vlszIc= github.com/dhui/dktest v0.4.3 h1:wquqUxAFdcUgabAVLvSCOKOlag5cIZuaOjYIBOWdsR0= github.com/dhui/dktest v0.4.3/go.mod h1:zNK8IwktWzQRm6I/l2Wjp7MakiyaFWv4G1hjmodmMTs= -github.com/digitalocean/godo v1.168.0 h1:mlORtUcPD91LQeJoznrH3XvfvgK3t8Wvrpph9giUT/Q= -github.com/digitalocean/godo v1.168.0/go.mod h1:xQsWpVCCbkDrWisHA72hPzPlnC+4W5w/McZY5ij9uvU= +github.com/digitalocean/godo v1.171.0 h1:QwpkwWKr3v7yxc8D4NQG973NoR9APCEWjYnLOQeXVpQ= +github.com/digitalocean/godo v1.171.0/go.mod h1:xQsWpVCCbkDrWisHA72hPzPlnC+4W5w/McZY5ij9uvU= github.com/distribution/reference v0.6.0 h1:0IXCQ5g4/QMHHkarYzh5l+u8T3t73zM5QvfrDyIgxBk= github.com/distribution/reference v0.6.0/go.mod h1:BbU0aIcezP1/5jX/8MP0YiH4SdvB5Y4f/wlDRiLyi3E= github.com/docker/docker v28.5.2+incompatible h1:DBX0Y0zAjZbSrm1uzOkdr1onVghKaftjlSWt4AFexzM= @@ -424,8 +424,8 @@ github.com/go-playground/universal-translator v0.17.0/go.mod h1:UkSxE5sNxxRwHyU+ github.com/go-playground/validator/v10 v10.4.1/go.mod h1:nlOn6nFhuKACm19sB/8EGNn9GlaMV7XkbRSipzJ0Ii4= github.com/go-redis/redis/v8 v8.11.5 h1:AcZZR7igkdvfVmQTPnu9WE37LRrO/YrBH5zWyjDC0oI= github.com/go-redis/redis/v8 v8.11.5/go.mod h1:gREzHqY1hg6oD9ngVRbLStwAWKhA0FEgq8Jd4h5lpwo= -github.com/go-resty/resty/v2 v2.16.5 h1:hBKqmWrr7uRc3euHVqmh1HTHcKn99Smr7o5spptdhTM= -github.com/go-resty/resty/v2 v2.16.5/go.mod h1:hkJtXbA2iKHzJheXYvQ8snQES5ZLGKMwQ07xAwp/fiA= +github.com/go-resty/resty/v2 v2.17.1 h1:x3aMpHK1YM9e4va/TMDRlusDDoZiQ+ViDu/WpA6xTM4= +github.com/go-resty/resty/v2 v2.17.1/go.mod h1:kCKZ3wWmwJaNc7S29BRtUhJwy7iqmn+2mLtQrOyQlVA= github.com/go-stack/stack v1.8.0/go.mod h1:v0f6uXyyMGvRgIKkXu+yp6POWl0qKG85gN/melR3HDY= github.com/go-viper/mapstructure/v2 v2.5.0 h1:vM5IJoUAy3d7zRSVtIwQgBj7BiWtMPfmPEgAXnvj1Ro= github.com/go-viper/mapstructure/v2 v2.5.0/go.mod h1:oJDH3BJKyqBA2TXFhDsKDGDTlndYOZ6rGS0BRZIxGhM= @@ -539,16 +539,16 @@ github.com/google/pprof v0.0.0-20210601050228-01bbb1931b22/go.mod h1:kpwsk12EmLe github.com/google/pprof v0.0.0-20210609004039-a478d1d731e9/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE= github.com/google/pprof v0.0.0-20210720184732-4bb14d4b1be1/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE= github.com/google/pprof v0.0.0-20240227163752-401108e1b7e7/go.mod h1:czg5+yv1E0ZGTi6S6vVK1mke0fV+FaUhNGcd6VRS9Ik= -github.com/google/pprof v0.0.0-20250923004556-9e5a51aed1e8 h1:ZI8gCoCjGzPsum4L21jHdQs8shFBIQih1TM9Rd/c+EQ= -github.com/google/pprof v0.0.0-20250923004556-9e5a51aed1e8/go.mod h1:I6V7YzU0XDpsHqbsyrghnFZLO1gwK6NPTNvmetQIk9U= +github.com/google/pprof v0.0.0-20251213031049-b05bdaca462f h1:HU1RgM6NALf/KW9HEY6zry3ADbDKcmpQ+hJedoNGQYQ= +github.com/google/pprof v0.0.0-20251213031049-b05bdaca462f/go.mod h1:67FPmZWbr+KDT/VlpWtw6sO9XSjpJmLuHpoLmWiTGgY= github.com/google/renameio v0.1.0/go.mod h1:KWCgfxg9yswjAJkECMjeO8J8rahYeXnNhOm40UhjYkI= github.com/google/s2a-go v0.1.9 h1:LGD7gtMgezd8a/Xak7mEWL0PjoTQFvpRudN895yqKW0= github.com/google/s2a-go v0.1.9/go.mod h1:YA0Ei2ZQL3acow2O62kdp9UlnvMmU7kA6Eutn0dXayM= github.com/google/uuid v1.1.2/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= github.com/google/uuid v1.6.0 h1:NIvaJDMOsjHA8n1jAhLSgzrAzy1Hgr+hNrb57e+94F0= github.com/google/uuid v1.6.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= -github.com/googleapis/enterprise-certificate-proxy v0.3.6 h1:GW/XbdyBFQ8Qe+YAmFU9uHLo7OnF5tL52HFAgMmyrf4= -github.com/googleapis/enterprise-certificate-proxy v0.3.6/go.mod h1:MkHOF77EYAE7qfSuSS9PU6g4Nt4e11cnsDUowfwewLA= +github.com/googleapis/enterprise-certificate-proxy v0.3.7 h1:zrn2Ee/nWmHulBx5sAVrGgAa0f2/R35S4DJwfFaUPFQ= +github.com/googleapis/enterprise-certificate-proxy v0.3.7/go.mod h1:MkHOF77EYAE7qfSuSS9PU6g4Nt4e11cnsDUowfwewLA= github.com/googleapis/gax-go/v2 v2.0.4/go.mod h1:0Wqv26UfaUD9n4G6kQubkQ+KchISgw+vpHVxEJEs9eg= github.com/googleapis/gax-go/v2 v2.0.5/go.mod h1:DWXyrwAJ9X0FpwwEdw+IPEYBICEFu5mhpdKc/us6bOk= github.com/googleapis/gax-go/v2 v2.1.0/go.mod h1:Q3nei7sK6ybPYH7twZdmQpAd1MKb7pfu6SK+H1/DsU0= @@ -559,8 +559,8 @@ github.com/googleapis/gax-go/v2 v2.4.0/go.mod h1:XOTVJ59hdnfJLIP/dh8n5CGryZR2LxK github.com/googleapis/gax-go/v2 v2.15.0 h1:SyjDc1mGgZU5LncH8gimWo9lW1DtIfPibOG81vgd/bo= github.com/googleapis/gax-go/v2 v2.15.0/go.mod h1:zVVkkxAQHa1RQpg9z2AUCMnKhi0Qld9rcmyfL1OZhoc= github.com/googleapis/google-cloud-go-testing v0.0.0-20200911160855-bcd43fbb19e8/go.mod h1:dvDLG8qkwmyD9a/MJJN3XJcT3xFxOKAvTZGvuZmac9g= -github.com/gophercloud/gophercloud/v2 v2.8.0 h1:of2+8tT6+FbEYHfYC8GBu8TXJNsXYSNm9KuvpX7Neqo= -github.com/gophercloud/gophercloud/v2 v2.8.0/go.mod h1:Ki/ILhYZr/5EPebrPL9Ej+tUg4lqx71/YH2JWVeU+Qk= +github.com/gophercloud/gophercloud/v2 v2.9.0 h1:Y9OMrwKF9EDERcHFSOTpf/6XGoAI0yOxmsLmQki4LPM= +github.com/gophercloud/gophercloud/v2 v2.9.0/go.mod h1:Ki/ILhYZr/5EPebrPL9Ej+tUg4lqx71/YH2JWVeU+Qk= github.com/gorilla/mux v1.8.1 h1:TuBL49tXwgrFYWhqrNgrUNEY92u81SPhu7sTdzQEiWY= github.com/gorilla/mux v1.8.1/go.mod h1:AKf9I4AEqPTmMytcMc0KkNouC66V3BtZ4qD5fmWSiMQ= github.com/gorilla/websocket v1.5.4-0.20250319132907-e064f32e3674 h1:JeSE6pjso5THxAzdVpqr6/geYxZytqFMBCOtn/ujyeo= @@ -580,8 +580,8 @@ github.com/grpc-ecosystem/grpc-gateway v1.16.0/go.mod h1:BDjrQk3hbvj6Nolgz8mAMFb github.com/grpc-ecosystem/grpc-gateway/v2 v2.28.0 h1:HWRh5R2+9EifMyIHV7ZV+MIZqgz+PMpZ14Jynv3O2Zs= github.com/grpc-ecosystem/grpc-gateway/v2 v2.28.0/go.mod h1:JfhWUomR1baixubs02l85lZYYOm7LV6om4ceouMv45c= github.com/hashicorp/consul/api v1.12.0/go.mod h1:6pVBMo0ebnYdt2S3H87XhekM/HHrUoTD2XXb/VrZVy0= -github.com/hashicorp/consul/api v1.32.0 h1:5wp5u780Gri7c4OedGEPzmlUEzi0g2KyiPphSr6zjVg= -github.com/hashicorp/consul/api v1.32.0/go.mod h1:Z8YgY0eVPukT/17ejW+l+C7zJmKwgPHtjU1q16v/Y40= +github.com/hashicorp/consul/api v1.32.1 h1:0+osr/3t/aZNAdJX558crU3PEjVrG4x6715aZHRgceE= +github.com/hashicorp/consul/api v1.32.1/go.mod h1:mXUWLnxftwTmDv4W3lzxYCPD199iNLLUyLfLGFJbtl4= github.com/hashicorp/consul/sdk v0.8.0/go.mod h1:GBvyrGALthsZObzUGsfgHZQDXjg4lOjagTIwIR1vPms= github.com/hashicorp/consul/sdk v0.16.1 h1:V8TxTnImoPD5cj0U9Spl0TUxcytjcbbJeADFF07KdHg= github.com/hashicorp/consul/sdk v0.16.1/go.mod h1:fSXvwxB2hmh1FMZCNl6PwX0Q/1wdWtHJcZ7Ea5tns0s= @@ -623,8 +623,8 @@ github.com/hashicorp/go-uuid v1.0.0/go.mod h1:6SBZvOh/SIDV7/2o3Jml5SYk/TvGqwFJ/b github.com/hashicorp/go-uuid v1.0.1/go.mod h1:6SBZvOh/SIDV7/2o3Jml5SYk/TvGqwFJ/bN7x4byOro= github.com/hashicorp/go-uuid v1.0.3 h1:2gKiV6YVmrJ1i2CKKa9obLvRieoRGviZFL26PcT/Co8= github.com/hashicorp/go-uuid v1.0.3/go.mod h1:6SBZvOh/SIDV7/2o3Jml5SYk/TvGqwFJ/bN7x4byOro= -github.com/hashicorp/go-version v1.7.0 h1:5tqGy27NaOTB8yJKUZELlFAS/LTKJkrmONwQKeRZfjY= -github.com/hashicorp/go-version v1.7.0/go.mod h1:fltr4n8CU8Ke44wwGCBoEymUuxUHl09ZGVZPK5anwXA= +github.com/hashicorp/go-version v1.8.0 h1:KAkNb1HAiZd1ukkxDFGmokVZe1Xy9HG6NUp+bPle2i4= +github.com/hashicorp/go-version v1.8.0/go.mod h1:fltr4n8CU8Ke44wwGCBoEymUuxUHl09ZGVZPK5anwXA= github.com/hashicorp/golang-lru v0.5.0/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8= github.com/hashicorp/golang-lru v0.5.1/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8= github.com/hashicorp/golang-lru v0.5.4/go.mod h1:iADmTwqILo4mZ8BN3D2Q6+9jd8WM5uGBxy+E8yxSoD4= @@ -635,14 +635,14 @@ github.com/hashicorp/golang-lru/v2 v2.0.7/go.mod h1:QeFd9opnmA6QUJc5vARoKUSoFhyf github.com/hashicorp/hcl v1.0.0/go.mod h1:E5yfLk+7swimpb2L/Alb/PJmXilQ/rhwaUYs4T20WEQ= github.com/hashicorp/logutils v1.0.0/go.mod h1:QIAnNjmIWmVIIkWDTG1z5v++HQmx9WQRO+LraFDTW64= github.com/hashicorp/mdns v1.0.4/go.mod h1:mtBihi+LeNXGtG8L9dX59gAEa12BDtBQSp4v/YAJqrc= -github.com/hashicorp/nomad/api v0.0.0-20250930071859-eaa0fe0e27af h1:ScAYf8O+9xTqTJPZH8MIlUfO+ak8cb31rW1aYJgS+jE= -github.com/hashicorp/nomad/api v0.0.0-20250930071859-eaa0fe0e27af/go.mod h1:sldFTIgs+FsUeKU3LwVjviAIuksxD8TzDOn02MYwslE= +github.com/hashicorp/nomad/api v0.0.0-20251216171439-1dee0671280e h1:wGl06iy/H90NSbWjfXWeRwk9SJOks0u4voIryeJFlSA= +github.com/hashicorp/nomad/api v0.0.0-20251216171439-1dee0671280e/go.mod h1:sldFTIgs+FsUeKU3LwVjviAIuksxD8TzDOn02MYwslE= github.com/hashicorp/serf v0.9.6/go.mod h1:TXZNMjZQijwlDvp+r0b63xZ45H7JmCmgg4gpTwn9UV4= github.com/hashicorp/serf v0.9.7/go.mod h1:TXZNMjZQijwlDvp+r0b63xZ45H7JmCmgg4gpTwn9UV4= 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.29.0 h1:LzNFw5XLBfftyu3WM1sdSLjOZBlWORtz2hgGydHaYV8= -github.com/hetznercloud/hcloud-go/v2 v2.29.0/go.mod h1:XBU4+EDH2KVqu2KU7Ws0+ciZcX4ygukQl/J0L5GS8P8= +github.com/hetznercloud/hcloud-go/v2 v2.32.0 h1:BRe+k7ESdYv3xQLBGdKUfk+XBFRJNGKzq70nJI24ciM= +github.com/hetznercloud/hcloud-go/v2 v2.32.0/go.mod h1:hAanyyfn9M0cMmZ68CXzPCF54KRb9EXd8eiE2FHKGIE= github.com/hexops/gotextdiff v1.0.3 h1:gitA9+qJrrTCsiCl7+kh75nPqQt1cx4ZkudSTLoUqJM= github.com/hexops/gotextdiff v1.0.3/go.mod h1:pSWU5MAI3yDq+fZBTazCSJysOMbxWL1BSow5/V2vxeg= github.com/huaweicloud/huaweicloud-sdk-go-obs v3.25.4+incompatible h1:yNjwdvn9fwuN6Ouxr0xHM0cVu03YMUWUyFmu2van/Yc= @@ -716,8 +716,8 @@ github.com/leesper/go_rng v0.0.0-20190531154944-a612b043e353/go.mod h1:N0SVk0uhy github.com/leodido/go-urn v1.2.0/go.mod h1:+8+nEpDfqqsY+g338gtMEUOtuK+4dEMhiQEgxpxOKII= github.com/lib/pq v1.11.2 h1:x6gxUeu39V0BHZiugWe8LXZYZ+Utk7hSJGThs8sdzfs= github.com/lib/pq v1.11.2/go.mod h1:/p+8NSbOcwzAEI7wiMXFlgydTwcgTr3OSKMsD2BitpA= -github.com/linode/linodego v1.60.0 h1:SgsebJFRCi+lSmYy+C40wmKZeJllGGm+W12Qw4+yVdI= -github.com/linode/linodego v1.60.0/go.mod h1:1+Bt0oTz5rBnDOJbGhccxn7LYVytXTIIfAy7QYmijDs= +github.com/linode/linodego v1.63.0 h1:MdjizfXNJDVJU6ggoJmMO5O9h4KGPGivNX0fzrAnstk= +github.com/linode/linodego v1.63.0/go.mod h1:GoiwLVuLdBQcAebxAVKVL3mMYUgJZR/puOUSla04xBE= github.com/magiconair/properties v1.8.6/go.mod h1:y3VJvCyxH9uVvJTWEGAELF3aiYNyPKd5NZ3oSwXrF60= github.com/mailru/easyjson v0.0.0-20190614124828-94de47d64c63/go.mod h1:C1wdFJiN94OJF2b5HbByQZoLdCWB1Yqtg26g4irojpc= github.com/mailru/easyjson v0.0.0-20190626092158-b2ccc519800e/go.mod h1:C1wdFJiN94OJF2b5HbByQZoLdCWB1Yqtg26g4irojpc= @@ -748,8 +748,8 @@ github.com/metalmatze/signal v0.0.0-20210307161603-1c9aa721a97a h1:0usWxe5SGXKQo github.com/metalmatze/signal v0.0.0-20210307161603-1c9aa721a97a/go.mod h1:3OETvrxfELvGsU2RoGGWercfeZ4bCL3+SOwzIWtJH/Q= github.com/miekg/dns v1.1.26/go.mod h1:bPDLeHnStXmXAq1m/Ch/hvfNHr14JKNPMBo3VZKjuso= github.com/miekg/dns v1.1.41/go.mod h1:p6aan82bvRIyn+zDIv9xYNUpwa73JcSh9BKwknJysuI= -github.com/miekg/dns v1.1.68 h1:jsSRkNozw7G/mnmXULynzMNIsgY2dHC8LO6U6Ij2JEA= -github.com/miekg/dns v1.1.68/go.mod h1:fujopn7TB3Pu3JM69XaawiU0wqjpL9/8xGop5UrTPps= +github.com/miekg/dns v1.1.69 h1:Kb7Y/1Jo+SG+a2GtfoFUfDkG//csdRPwRLkCsxDG9Sc= +github.com/miekg/dns v1.1.69/go.mod h1:7OyjD9nEba5OkqQ/hB4fy3PIoxafSZJtducccIelz3g= github.com/minio/crc64nvme v1.1.1 h1:8dwx/Pz49suywbO+auHCBpCtlW1OfpcLN7wYgVR6wAI= github.com/minio/crc64nvme v1.1.1/go.mod h1:eVfm2fAzLlxMdUGc0EEBGSMmPwmXD5XiNRpnu9J3bvg= github.com/minio/md5-simd v1.1.2 h1:Gdi1DZK69+ZVMoNHRXJyNcxrMA4dSxoYHZSQbirFg34= @@ -809,12 +809,12 @@ github.com/onsi/ginkgo v1.16.5 h1:8xi0RTUf59SOSfEtZMvwTvXYMzG4gV23XVHOZiXNtnE= github.com/onsi/ginkgo v1.16.5/go.mod h1:+E8gABHa3K6zRBolWtd+ROzc/U5bkGt0FwiG042wbpU= github.com/onsi/gomega v1.36.2 h1:koNYke6TVk6ZmnyHrCXba/T/MoLBXFjeC1PtvYgw0A8= github.com/onsi/gomega v1.36.2/go.mod h1:DdwyADRjrc825LhMEkD76cHR5+pUnjhUN8GlHlRPHzY= -github.com/open-telemetry/opentelemetry-collector-contrib/internal/exp/metrics v0.139.0 h1:D5aGQCErSCb4sKIHoZhgR4El6AzgviTRYlHUpbSFqDo= -github.com/open-telemetry/opentelemetry-collector-contrib/internal/exp/metrics v0.139.0/go.mod h1:ZjeRsA5oaVk89fg5D+iXStx2QncmhAvtGbdSumT07H4= -github.com/open-telemetry/opentelemetry-collector-contrib/pkg/pdatautil v0.139.0 h1:6/j0Ta8ZJnmAFVEoC3aZ1Hs19RB4fHzlN6kOZhsBJqM= -github.com/open-telemetry/opentelemetry-collector-contrib/pkg/pdatautil v0.139.0/go.mod h1:VfA8xHz4xg7Fyj5bBsCDbOO3iVYzDn9wP/QFsjcAE5c= -github.com/open-telemetry/opentelemetry-collector-contrib/processor/deltatocumulativeprocessor v0.139.0 h1:iRNX/ueuad1psOVgnNkxuQmXxvF3ze5ZZCP66xKFk/w= -github.com/open-telemetry/opentelemetry-collector-contrib/processor/deltatocumulativeprocessor v0.139.0/go.mod h1:bW09lo3WgHsPsZ1mgsJvby9wCefT5o13patM5phdfIU= +github.com/open-telemetry/opentelemetry-collector-contrib/internal/exp/metrics v0.142.0 h1:agYk41V3eIfV6aIMxIeRQ7SFhfaW5k2O96HEebpmPwM= +github.com/open-telemetry/opentelemetry-collector-contrib/internal/exp/metrics v0.142.0/go.mod h1:ZmMdcBia20ih8NYia5b4dNhfNLT68xHgaqF+fNW+TLM= +github.com/open-telemetry/opentelemetry-collector-contrib/pkg/pdatautil v0.142.0 h1:bLp+Ii1UQ9cNr+Dm1jKzbcklhd0eBnPuIFQY6NPzkZ0= +github.com/open-telemetry/opentelemetry-collector-contrib/pkg/pdatautil v0.142.0/go.mod h1:6N36UrFd9Yiz2aYpXM5xiK7Eqp2RyAr3O8lUE+wK2Y8= +github.com/open-telemetry/opentelemetry-collector-contrib/processor/deltatocumulativeprocessor v0.142.0 h1:fL8LBVeje+nbts2VIInvRa4T5LlsC0BZCI60wNGoS+Y= +github.com/open-telemetry/opentelemetry-collector-contrib/processor/deltatocumulativeprocessor v0.142.0/go.mod h1:fSnKuTN91I68Ou1Lgfwe3Mt6BGl9kcA8PYCpnGkPnsY= github.com/opencontainers/go-digest v1.0.0 h1:apOUWs51W5PlhuyGyz9FCeeBIOUDA/6nW8Oi/yOhh5U= github.com/opencontainers/go-digest v1.0.0/go.mod h1:0JzlMkj0TRzQZfJkVvzbP0HBR3IKzErnv2BNG4W4MAM= github.com/opencontainers/image-spec v1.1.0 h1:8SG7/vwALn54lVB/0yZ/MMwhFrPYtpEHQb2IpWsCzug= @@ -902,8 +902,8 @@ github.com/prometheus/procfs v0.1.3/go.mod h1:lV6e/gmhEcM9IjHGsFOCxxuZ+z1YqCvr4O github.com/prometheus/procfs v0.6.0/go.mod h1:cz+aTbrPOrUb4q7XlbU9ygM+/jj0fzG6c1xBZuNvfVA= github.com/prometheus/procfs v0.16.1 h1:hZ15bTNuirocR6u0JZ6BAHHmwS1p8B4P6MRqxtzMyRg= github.com/prometheus/procfs v0.16.1/go.mod h1:teAbpZRB1iIAJYREa1LsoWUXykVXA1KlTmWl8x/U+Is= -github.com/prometheus/prometheus v0.308.1 h1:ApMNI/3/es3Ze90Z7CMb+wwU2BsSYur0m5VKeqHj7h4= -github.com/prometheus/prometheus v0.308.1/go.mod h1:aHjYCDz9zKRyoUXvMWvu13K9XHOkBB12XrEqibs3e0A= +github.com/prometheus/prometheus v0.309.1 h1:jutK6eCYDpWdPTUbVbkcQsNCMO9CCkSwjQRMLds4jSo= +github.com/prometheus/prometheus v0.309.1/go.mod h1:d+dOGiVhuNDa4MaFXHVdnUBy/CzqlcNTooR8oM1wdTU= github.com/prometheus/sigv4 v0.4.1 h1:EIc3j+8NBea9u1iV6O5ZAN8uvPq2xOIUPcqCTivHuXs= github.com/prometheus/sigv4 v0.4.1/go.mod h1:eu+ZbRvsc5TPiHwqh77OWuCnWK73IdkETYY46P4dXOU= github.com/puzpuzpuz/xsync/v3 v3.5.1 h1:GJYJZwO6IdxN/IKbneznS6yPkVC+c3zyY/j19c++5Fg= @@ -954,8 +954,8 @@ github.com/spf13/pflag v1.0.6/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An github.com/spf13/viper v1.13.0/go.mod h1:Icm2xNL3/8uyh/wFuB1jI7TiTNKp8632Nwegu+zgdYw= github.com/spiffe/go-spiffe/v2 v2.6.0 h1:l+DolpxNWYgruGQVV0xsfeya3CsC7m8iBzDnMpsbLuo= github.com/spiffe/go-spiffe/v2 v2.6.0/go.mod h1:gm2SeUoMZEtpnzPNs2Csc0D/gX33k1xIx7lEzqblHEs= -github.com/stackitcloud/stackit-sdk-go/core v0.17.3 h1:GsZGmRRc/3GJLmCUnsZswirr5wfLRrwavbnL/renOqg= -github.com/stackitcloud/stackit-sdk-go/core v0.17.3/go.mod h1:HBCXJGPgdRulplDzhrmwC+Dak9B/x0nzNtmOpu+1Ahg= +github.com/stackitcloud/stackit-sdk-go/core v0.20.1 h1:odiuhhRXmxvEvnVTeZSN9u98edvw2Cd3DcnkepncP3M= +github.com/stackitcloud/stackit-sdk-go/core v0.20.1/go.mod h1:fqto7M82ynGhEnpZU6VkQKYWYoFG5goC076JWXTUPRQ= github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= github.com/stretchr/objx v0.1.1/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw= @@ -1037,38 +1037,40 @@ go.opencensus.io v0.24.0 h1:y73uSU6J157QMP2kn2r30vwW1A2W2WFwSCGnAVxeaD0= go.opencensus.io v0.24.0/go.mod h1:vNK8G9p7aAivkbmorf4v+7Hgx+Zs0yY+0fOtgBfjQKo= go.opentelemetry.io/auto/sdk v1.2.1 h1:jXsnJ4Lmnqd11kwkBV2LgLoFMZKizbCi5fNZ/ipaZ64= go.opentelemetry.io/auto/sdk v1.2.1/go.mod h1:KRTj+aOaElaLi+wW1kO/DZRXwkF4C5xPbEe3ZiIhN7Y= -go.opentelemetry.io/collector/component v1.45.0 h1:gGFfVdbQ+1YuyUkJjWo85I7euu3H/CiupuzCHv8OgHA= -go.opentelemetry.io/collector/component v1.45.0/go.mod h1:xoNFnRKE8Iv6gmlqAKgjayWraRnDcYLLgrPt9VgyO2g= -go.opentelemetry.io/collector/component/componentstatus v0.139.0 h1:bQmkv1t7xW7uIDireE0a2Am4IMOprXm6zQr/qDtGCIA= -go.opentelemetry.io/collector/component/componentstatus v0.139.0/go.mod h1:ibZOohpG0u081/NaT/jMCTsKwRbbwwxWrjZml+owpyM= -go.opentelemetry.io/collector/component/componenttest v0.139.0 h1:x9Yu2eYhrHxdZ7sFXWtAWVjQ3UIraje557LgNurDC2I= -go.opentelemetry.io/collector/component/componenttest v0.139.0/go.mod h1:S9cj+qkf9FgHMzjvlYsLwQKd9BiS7B7oLZvxvlENM/c= -go.opentelemetry.io/collector/confmap v1.45.0 h1:7M7TTlpzX4r+mIzP/ARdxZBAvI4N+1V96phDane+akU= -go.opentelemetry.io/collector/confmap v1.45.0/go.mod h1:AE1dnkjv0T9gptsh5+mTX0XFGdXx0n7JS4b7CcPfJ6Q= -go.opentelemetry.io/collector/confmap/xconfmap v0.139.0 h1:uQGpFuWnTCXqdMbI3gDSvkwU66/kF/aoC0kVMrit1EM= -go.opentelemetry.io/collector/confmap/xconfmap v0.139.0/go.mod h1:d0ucaeNq2rojFRSQsCHF/gkT3cgBx5H2bVkPQMj57ck= -go.opentelemetry.io/collector/consumer v1.45.0 h1:TtqXxgW+1GSCwdoohq0fzqnfqrZBKbfo++1XRj8mrEA= -go.opentelemetry.io/collector/consumer v1.45.0/go.mod h1:pJzqTWBubwLt8mVou+G4/Hs23b3m425rVmld3LqOYpY= -go.opentelemetry.io/collector/consumer/consumertest v0.139.0 h1:06mu43mMO7l49ASJ/GEbKgTWcV3py5zE/pKhNBZ1b3k= -go.opentelemetry.io/collector/consumer/consumertest v0.139.0/go.mod h1:gaeCpRQGbCFYTeLzi+Z2cTDt40GiIa3hgIEgLEmiC78= -go.opentelemetry.io/collector/consumer/xconsumer v0.139.0 h1:FhzDv+idglnrfjqPvnUw3YAEOkXSNv/FuNsuMiXQwcY= -go.opentelemetry.io/collector/consumer/xconsumer v0.139.0/go.mod h1:yWrg/6FE/A4Q7eo/Mg++CzkBoSILHdeMnTlxV3serI0= -go.opentelemetry.io/collector/featuregate v1.45.0 h1:D06hpf1F2KzKC+qXLmVv5e8IZpgCyZVeVVC8iOQxVmw= -go.opentelemetry.io/collector/featuregate v1.45.0/go.mod h1:d0tiRzVYrytB6LkcYgz2ESFTv7OktRPQe0QEQcPt1L4= -go.opentelemetry.io/collector/pdata v1.45.0 h1:q4XaISpeX640BcwXwb2mKOVw/gb67r22HjGWl8sbWsk= -go.opentelemetry.io/collector/pdata v1.45.0/go.mod h1:5q2f001YhwMQO8QvpFhCOa4Cq/vtwX9W4HRMsXkU/nE= -go.opentelemetry.io/collector/pdata/pprofile v0.139.0 h1:UA5TgFzYmRuJN3Wz0GR1efLUfjbs5rH0HTaxfASpTR8= -go.opentelemetry.io/collector/pdata/pprofile v0.139.0/go.mod h1:sI5qHt+zzE2fhOWFdJIaiDBR0yGGjD4A4ZvDFU0tiHk= -go.opentelemetry.io/collector/pdata/testdata v0.139.0 h1:n7O5bmLLhc3T6PePV4447fFcI/6QWcMhBsLtfCaD0do= -go.opentelemetry.io/collector/pdata/testdata v0.139.0/go.mod h1:fxZ2VrhYLYBLHYBHC1XQRKZ6IJXwy0I2rPaaRlebYaY= -go.opentelemetry.io/collector/pipeline v1.45.0 h1:sn9JJAEBe3XABTkWechMk0eH60QMBjjNe5V+ccBl+Uo= -go.opentelemetry.io/collector/pipeline v1.45.0/go.mod h1:xUrAqiebzYbrgxyoXSkk6/Y3oi5Sy3im2iCA51LwUAI= -go.opentelemetry.io/collector/processor v1.45.0 h1:GH5km9BkDQOoz7MR0jzTnzB1Kb5vtKzPwa/wDmRg2dQ= -go.opentelemetry.io/collector/processor v1.45.0/go.mod h1:wdlaTTC3wqlZIJP9R9/SLc2q7h+MFGARsxfjgPtwbes= -go.opentelemetry.io/collector/processor/processortest v0.139.0 h1:30akUdruFNG7EDpayuBhXoX2lV+hcfxW9Gl3Z6MYHb0= -go.opentelemetry.io/collector/processor/processortest v0.139.0/go.mod h1:RTll3UKHrqj/VS6RGjTHtuGIJzyLEwFhbw8KuCL3pjo= -go.opentelemetry.io/collector/processor/xprocessor v0.139.0 h1:O9x9RF/OG8gZ+HrOcB4f6F1fjniby484xf2D8GBxgqU= -go.opentelemetry.io/collector/processor/xprocessor v0.139.0/go.mod h1:hqGhEZ1/PftD/QHaYna0o1xAqZUsb7GhqpOiaTTDJnQ= +go.opentelemetry.io/collector/component v1.48.0 h1:0hZKOvT6fIlXoE+6t40UXbXOH7r/h9jyE3eIt0W19Qg= +go.opentelemetry.io/collector/component v1.48.0/go.mod h1:Kmc9Z2CT53M2oRRf+WXHUHHgjCC+ADbiqfPO5mgZe3g= +go.opentelemetry.io/collector/component/componentstatus v0.142.0 h1:a1KkLCtShI5SfhO2ga75VqWjjBRGgrerelt/2JXWLBI= +go.opentelemetry.io/collector/component/componentstatus v0.142.0/go.mod h1:IRWKvFcUrFrkz1gJEV+cKAdE2ZBT128gk1sHt0OzKI4= +go.opentelemetry.io/collector/component/componenttest v0.142.0 h1:a8XclEutO5dv4AnzThHK8dfqR4lDWjJKLtRNM2aVUFM= +go.opentelemetry.io/collector/component/componenttest v0.142.0/go.mod h1:JhX/zKaEbjhFcsiV2ha2spzo24A6RL/jqNBS0svURD0= +go.opentelemetry.io/collector/confmap v1.48.0 h1:vGhg25NEUX5DiYziJEw2siwdzsvtXBRZVuYyLVinFR8= +go.opentelemetry.io/collector/confmap v1.48.0/go.mod h1:8tJHJowmvUkJ8AHzZ6SaH61dcWbdfRE9Sd/hwsKLgRE= +go.opentelemetry.io/collector/confmap/xconfmap v0.142.0 h1:SNfuFP8TA0PmUkx6ryY63uNjLN2HMh5VeGO++IYdPgA= +go.opentelemetry.io/collector/confmap/xconfmap v0.142.0/go.mod h1:FXuX6B8b7Ub7qkLqloWKanmPhADL18EEkaFptcd4eDQ= +go.opentelemetry.io/collector/consumer v1.48.0 h1:g1uroz2AA0cqnEsjqFTSZG+y8uH1gQBqqyzk8kd3QiM= +go.opentelemetry.io/collector/consumer v1.48.0/go.mod h1:lC6PnVXBwI456SV5WtvJqE7vjCNN6DAUc8xjFQ9wUV4= +go.opentelemetry.io/collector/consumer/consumertest v0.142.0 h1:TRt8zR57Vk1PTjtqjHOwOAMbIl+IeloHxWAuF8sWdRw= +go.opentelemetry.io/collector/consumer/consumertest v0.142.0/go.mod h1:yq2dhMxFUlCFkRN7LES3fzsTmUDw9VaunyRAka2TEaY= +go.opentelemetry.io/collector/consumer/xconsumer v0.142.0 h1:qOoQnLZXQ9sRLexTkkmBx3qfaOmEgco9VBPmryg5UhA= +go.opentelemetry.io/collector/consumer/xconsumer v0.142.0/go.mod h1:oPN0yJzEpovwlWvmSaiYgtDqGuOmMMLmmg352sqZdsE= +go.opentelemetry.io/collector/featuregate v1.48.0 h1:jiGRcl93yzUFgZVDuskMAftFraE21jANdxXTQfSQScc= +go.opentelemetry.io/collector/featuregate v1.48.0/go.mod h1:/1bclXgP91pISaEeNulRxzzmzMTm4I5Xih2SnI4HRSo= +go.opentelemetry.io/collector/internal/testutil v0.142.0 h1:MHnAVRimQdsfYqYHC3YuJRkIUap4VmSpJkkIT2N7jJA= +go.opentelemetry.io/collector/internal/testutil v0.142.0/go.mod h1:YAD9EAkwh/l5asZNbEBEUCqEjoL1OKMjAMoPjPqH76c= +go.opentelemetry.io/collector/pdata v1.48.0 h1:CKZ+9v/lGTX/cTGx2XVp8kp0E8R//60kHFCBdZudrTg= +go.opentelemetry.io/collector/pdata v1.48.0/go.mod h1:jaf2JQGpfUreD1TOtGBPsq00ecOqM66NG15wALmdxKA= +go.opentelemetry.io/collector/pdata/pprofile v0.142.0 h1:Ivyw7WY8SIIWqzXsnNmjEgz3ysVs/OkIf0KIpJUnuuo= +go.opentelemetry.io/collector/pdata/pprofile v0.142.0/go.mod h1:94GAph54K4WDpYz9xirhroHB3ptNLuPiY02k8fyoNUI= +go.opentelemetry.io/collector/pdata/testdata v0.142.0 h1:+jf9RyLWl8WyhIVjpg7yuH+bRdQH4mW20cPtCMlY1cI= +go.opentelemetry.io/collector/pdata/testdata v0.142.0/go.mod h1:kgAu5ZLEcVuPH3RFiHDg23RGitgm1M0cUAVwiGX4SB8= +go.opentelemetry.io/collector/pipeline v1.48.0 h1:E4zyQ7+4FTGvdGS4pruUnItuyRTGhN0Qqk1CN71lfW0= +go.opentelemetry.io/collector/pipeline v1.48.0/go.mod h1:xUrAqiebzYbrgxyoXSkk6/Y3oi5Sy3im2iCA51LwUAI= +go.opentelemetry.io/collector/processor v1.48.0 h1:3Kttw79mnrf463QKJGoGZzFfiNzQuMWK0p2nHuvOhaQ= +go.opentelemetry.io/collector/processor v1.48.0/go.mod h1:A3OsW6ga+a48J1mrnVNH5L5kB0v+n9nVFlmOQB5/Jwk= +go.opentelemetry.io/collector/processor/processortest v0.142.0 h1:wQnJeXDejBL6r8ov66AYAGf8Q0/JspjuqAjPVBdCUoI= +go.opentelemetry.io/collector/processor/processortest v0.142.0/go.mod h1:QU5SWj0L+92MSvQxZDjwWCsKssNDm+nD6SHn7IvviUE= +go.opentelemetry.io/collector/processor/xprocessor v0.142.0 h1:7a1Crxrd5iBMVnebTxkcqxVkRHAlOBUUmNTUVUTnlCU= +go.opentelemetry.io/collector/processor/xprocessor v0.142.0/go.mod h1:LY/GS2DiJILJKS3ynU3eOLLWSP8CmN1FtdpAMsVV8AU= go.opentelemetry.io/collector/semconv v0.128.0 h1:MzYOz7Vgb3Kf5D7b49pqqgeUhEmOCuT10bIXb/Cc+k4= go.opentelemetry.io/collector/semconv v0.128.0/go.mod h1:OPXer4l43X23cnjLXIZnRj/qQOjSuq4TgBLI76P9hns= go.opentelemetry.io/contrib/detectors/gcp v1.39.0 h1:kWRNZMsfBHZ+uHjiH4y7Etn2FK26LAGkNFw7RHv1DhE= @@ -1112,12 +1114,12 @@ go.opentelemetry.io/otel/trace v1.43.0/go.mod h1:/QJhyVBUUswCphDVxq+8mld+AvhXZLh go.opentelemetry.io/proto/otlp v0.7.0/go.mod h1:PqfVotwruBrMGOCsRd/89rSnXhoiJIqeYNgFYFoEGnI= go.opentelemetry.io/proto/otlp v1.9.0 h1:l706jCMITVouPOqEnii2fIAuO3IVGBRPV5ICjceRb/A= go.opentelemetry.io/proto/otlp v1.9.0/go.mod h1:xE+Cx5E/eEHw+ISFkwPLwCZefwVjY+pqKg1qcK03+/4= -go.opentelemetry.io/proto/slim/otlp v1.8.0 h1:afcLwp2XOeCbGrjufT1qWyruFt+6C9g5SOuymrSPUXQ= -go.opentelemetry.io/proto/slim/otlp v1.8.0/go.mod h1:Yaa5fjYm1SMCq0hG0x/87wV1MP9H5xDuG/1+AhvBcsI= -go.opentelemetry.io/proto/slim/otlp/collector/profiles/v1development v0.1.0 h1:Uc+elixz922LHx5colXGi1ORbsW8DTIGM+gg+D9V7HE= -go.opentelemetry.io/proto/slim/otlp/collector/profiles/v1development v0.1.0/go.mod h1:VyU6dTWBWv6h9w/+DYgSZAPMabWbPTFTuxp25sM8+s0= -go.opentelemetry.io/proto/slim/otlp/profiles/v1development v0.1.0 h1:i8YpvWGm/Uq1koL//bnbJ/26eV3OrKWm09+rDYo7keU= -go.opentelemetry.io/proto/slim/otlp/profiles/v1development v0.1.0/go.mod h1:pQ70xHY/ZVxNUBPn+qUWPl8nwai87eWdqL3M37lNi9A= +go.opentelemetry.io/proto/slim/otlp v1.9.0 h1:fPVMv8tP3TrsqlkH1HWYUpbCY9cAIemx184VGkS6vlE= +go.opentelemetry.io/proto/slim/otlp v1.9.0/go.mod h1:xXdeJJ90Gqyll+orzUkY4bOd2HECo5JofeoLpymVqdI= +go.opentelemetry.io/proto/slim/otlp/collector/profiles/v1development v0.2.0 h1:o13nadWDNkH/quoDomDUClnQBpdQQ2Qqv0lQBjIXjE8= +go.opentelemetry.io/proto/slim/otlp/collector/profiles/v1development v0.2.0/go.mod h1:Gyb6Xe7FTi/6xBHwMmngGoHqL0w29Y4eW8TGFzpefGA= +go.opentelemetry.io/proto/slim/otlp/profiles/v1development v0.2.0 h1:EiUYvtwu6PMrMHVjcPfnsG3v+ajPkbUeH+IL93+QYyk= +go.opentelemetry.io/proto/slim/otlp/profiles/v1development v0.2.0/go.mod h1:mUUHKFiN2SST3AhJ8XhJxEoeVW12oqfXog0Bo8W3Ec4= go.uber.org/atomic v1.7.0/go.mod h1:fEN4uk6kAWBTFdckzkM89CLk9XfWZrxpCo0nPH17wJc= go.uber.org/atomic v1.11.0 h1:ZvwS0R+56ePWxUNi+Atn9dWONBPp/AUETXlHW0DxSjE= go.uber.org/atomic v1.11.0/go.mod h1:LUxbIzbOniOlMKjJjyPfpl4v+PKK2cNJn91OQbhoJI0= @@ -1131,8 +1133,8 @@ go.uber.org/multierr v1.11.0 h1:blXXJkSxSSfBVBlC76pxqeO+LN3aDfLQo+309xJstO0= go.uber.org/multierr v1.11.0/go.mod h1:20+QtiLqy0Nd6FdQB9TLXag12DsQkrbs3htMFfDN80Y= go.uber.org/zap v1.17.0/go.mod h1:MXVU+bhUf/A7Xi2HNOnopQOrmycQ5Ih87HtOu4q5SSo= go.uber.org/zap v1.18.1/go.mod h1:xg/QME4nWcxGxrpdeYfq7UvYrLh66cuVKdrbD1XF/NI= -go.uber.org/zap v1.27.0 h1:aJMhYGrd5QSmlpLMr2MftRKl7t8J8PTZPA732ud/XR8= -go.uber.org/zap v1.27.0/go.mod h1:GB2qFLM7cTU87MWRP2mPIjqfIDnGu+VIO4V/SdhGo2E= +go.uber.org/zap v1.27.1 h1:08RqriUEv8+ArZRYSTXy1LeBScaMpVSTBhCeaZYfMYc= +go.uber.org/zap v1.27.1/go.mod h1:GB2qFLM7cTU87MWRP2mPIjqfIDnGu+VIO4V/SdhGo2E= go.yaml.in/yaml/v2 v2.4.3 h1:6gvOSjQoTB3vt1l+CU+tSyi/HOjfOjRLJ4YwYZGwRO0= go.yaml.in/yaml/v2 v2.4.3/go.mod h1:zSxWcmIDjOzPXpjlTTbAsKokqkDNAVtZO0WOMiT90s8= go.yaml.in/yaml/v3 v3.0.4 h1:tfq32ie2Jv2UxXFdLJdh3jXuOzWiL1fo0bu/FbuKpbc= @@ -1491,8 +1493,8 @@ google.golang.org/api v0.74.0/go.mod h1:ZpfMZOVRMywNyvJFeqL9HRWBgAuRfSjJFpe9QtRR google.golang.org/api v0.75.0/go.mod h1:pU9QmyHLnzlpar1Mjt4IbapUCy8J+6HD6GeELN69ljA= google.golang.org/api v0.78.0/go.mod h1:1Sg78yoMLOhlQTeF+ARBoytAcH1NNyyl390YMy6rKmw= google.golang.org/api v0.81.0/go.mod h1:FA6Mb/bZxj706H2j+j2d6mHEEaHBmbbWnkfvmorOCko= -google.golang.org/api v0.252.0 h1:xfKJeAJaMwb8OC9fesr369rjciQ704AjU/psjkKURSI= -google.golang.org/api v0.252.0/go.mod h1:dnHOv81x5RAmumZ7BWLShB/u7JZNeyalImxHmtTHxqw= +google.golang.org/api v0.257.0 h1:8Y0lzvHlZps53PEaw+G29SsQIkuKrumGWs9puiexNAA= +google.golang.org/api v0.257.0/go.mod h1:4eJrr+vbVaZSqs7vovFd1Jb/A6ml6iw2e6FBYf3GAO4= google.golang.org/appengine v1.1.0/go.mod h1:EbEs0AVv82hx2wNQdGPgUI5lhzA/G0D9YwlJXL52JkM= google.golang.org/appengine v1.4.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4= google.golang.org/appengine v1.5.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4= @@ -1677,12 +1679,12 @@ honnef.co/go/tools v0.0.0-20190523083050-ea95bdfd59fc/go.mod h1:rf3lG4BRIbNafJWh honnef.co/go/tools v0.0.1-2019.2.3/go.mod h1:a3bituU0lyd329TUQxRnasdCoJDkEUEAqEt0JzvZhAg= honnef.co/go/tools v0.0.1-2020.1.3/go.mod h1:X/FiERA/W4tHapMX5mGpAtMSVEeEUOyHaw9vFzvIQ3k= honnef.co/go/tools v0.0.1-2020.1.4/go.mod h1:X/FiERA/W4tHapMX5mGpAtMSVEeEUOyHaw9vFzvIQ3k= -k8s.io/api v0.34.1 h1:jC+153630BMdlFukegoEL8E/yT7aLyQkIVuwhmwDgJM= -k8s.io/api v0.34.1/go.mod h1:SB80FxFtXn5/gwzCoN6QCtPD7Vbu5w2n1S0J5gFfTYk= -k8s.io/apimachinery v0.34.1 h1:dTlxFls/eikpJxmAC7MVE8oOeP1zryV7iRyIjB0gky4= -k8s.io/apimachinery v0.34.1/go.mod h1:/GwIlEcWuTX9zKIg2mbw0LRFIsXwrfoVxn+ef0X13lw= -k8s.io/client-go v0.34.1 h1:ZUPJKgXsnKwVwmKKdPfw4tB58+7/Ik3CrjOEhsiZ7mY= -k8s.io/client-go v0.34.1/go.mod h1:kA8v0FP+tk6sZA0yKLRG67LWjqufAoSHA2xVGKw9Of8= +k8s.io/api v0.34.3 h1:D12sTP257/jSH2vHV2EDYrb16bS7ULlHpdNdNhEw2S4= +k8s.io/api v0.34.3/go.mod h1:PyVQBF886Q5RSQZOim7DybQjAbVs8g7gwJNhGtY5MBk= +k8s.io/apimachinery v0.34.3 h1:/TB+SFEiQvN9HPldtlWOTp0hWbJ+fjU+wkxysf/aQnE= +k8s.io/apimachinery v0.34.3/go.mod h1:/GwIlEcWuTX9zKIg2mbw0LRFIsXwrfoVxn+ef0X13lw= +k8s.io/client-go v0.34.3 h1:wtYtpzy/OPNYf7WyNBTj3iUA0XaBHVqhv4Iv3tbrF5A= +k8s.io/client-go v0.34.3/go.mod h1:OxxeYagaP9Kdf78UrKLa3YZixMCfP6bgPwPwNBQBzpM= k8s.io/klog/v2 v2.130.1 h1:n9Xl7H1Xvksem4KFG4PYbdQCQxqc/tTUyrgXaOhHSzk= k8s.io/klog/v2 v2.130.1/go.mod h1:3Jpz1GvMt720eyJH1ckRHK1EDfpxISzJ7I9OYgaDtPE= k8s.io/kube-openapi v0.0.0-20250710124328-f3f2b991d03b h1:MloQ9/bdJyIu9lb1PzujOPolHyvO06MXG5TUIj2mNAA= diff --git a/integration/e2e/images/images.go b/integration/e2e/images/images.go index 0cf845f976d..c5fd5999ce8 100644 --- a/integration/e2e/images/images.go +++ b/integration/e2e/images/images.go @@ -11,5 +11,5 @@ var ( Minio = "minio/minio:RELEASE.2024-05-28T17-19-04Z" Consul = "consul:1.8.4" ETCD = "quay.io/coreos/etcd:v3.5.29" - Prometheus = "quay.io/prometheus/prometheus:v3.8.1" + Prometheus = "quay.io/prometheus/prometheus:v3.9.1" ) diff --git a/pkg/api/handlers.go b/pkg/api/handlers.go index 5852a65cbb1..586f52b4fe7 100644 --- a/pkg/api/handlers.go +++ b/pkg/api/handlers.go @@ -21,6 +21,7 @@ import ( "github.com/prometheus/common/version" "github.com/prometheus/prometheus/config" "github.com/prometheus/prometheus/storage" + "github.com/prometheus/prometheus/util/features" v1 "github.com/prometheus/prometheus/web/api/v1" "github.com/weaveworks/common/instrument" "github.com/weaveworks/common/middleware" @@ -245,6 +246,7 @@ func NewQuerierHandler( false, false, nil, + features.NewRegistry(), ) // Let's clear all codecs to create the instrumented ones api.ClearCodecs() diff --git a/pkg/ingester/ingester.go b/pkg/ingester/ingester.go index 6116a318992..2f38b160e27 100644 --- a/pkg/ingester/ingester.go +++ b/pkg/ingester/ingester.go @@ -1474,8 +1474,7 @@ func (i *Ingester) Push(ctx context.Context, req *cortexpb.WriteRequest) (*corte var err error if s.StartTimestampMs != 0 && s.TimestampMs != 0 { - // TODO(SungJin1212): Change to AppendSTZeroSample after update the Prometheus v3.9.0+ - if _, err = app.AppendCTZeroSample(ref, copiedLabels, s.TimestampMs, s.StartTimestampMs); err != nil && !errors.Is(err, storage.ErrOutOfOrderCT) { + if _, err = app.AppendSTZeroSample(ref, copiedLabels, s.TimestampMs, s.StartTimestampMs); err != nil && !errors.Is(err, storage.ErrOutOfOrderST) { startTimestampSampleAppendFailCount++ i.metrics.startTimestampFail.WithLabelValues(sampleMetricTypeFloat).Inc() } @@ -1528,8 +1527,7 @@ func (i *Ingester) Push(ctx context.Context, req *cortexpb.WriteRequest) (*corte } if hp.StartTimestampMs != 0 && hp.TimestampMs != 0 { - // TODO(SungJin1212): Change to AppendHistogramSTZeroSample after update the Prometheus v3.9.0+ - if _, err = app.AppendHistogramCTZeroSample(ref, copiedLabels, hp.TimestampMs, hp.StartTimestampMs, h, fh); err != nil && !errors.Is(err, storage.ErrOutOfOrderCT) { + if _, err = app.AppendHistogramSTZeroSample(ref, copiedLabels, hp.TimestampMs, hp.StartTimestampMs, h, fh); err != nil && !errors.Is(err, storage.ErrOutOfOrderST) { startTimestampHistogramAppendFailCount++ i.metrics.startTimestampFail.WithLabelValues(sampleMetricTypeHistogram).Inc() } diff --git a/pkg/querier/error_translate_queryable_test.go b/pkg/querier/error_translate_queryable_test.go index 03b11368d30..ef9f04c70c2 100644 --- a/pkg/querier/error_translate_queryable_test.go +++ b/pkg/querier/error_translate_queryable_test.go @@ -19,6 +19,7 @@ import ( "github.com/prometheus/prometheus/promql" "github.com/prometheus/prometheus/storage" "github.com/prometheus/prometheus/util/annotations" + "github.com/prometheus/prometheus/util/features" v1 "github.com/prometheus/prometheus/web/api/v1" "github.com/stretchr/testify/require" "github.com/weaveworks/common/httpgrpc" @@ -193,6 +194,7 @@ func createPrometheusAPI(q storage.SampleAndChunkQueryable, engine promql.QueryE false, false, nil, + features.NewRegistry(), ) promRouter := route.New().WithPrefix("/api/v1") diff --git a/pkg/querier/stats_renderer_test.go b/pkg/querier/stats_renderer_test.go index 9021faebb75..2969d832972 100644 --- a/pkg/querier/stats_renderer_test.go +++ b/pkg/querier/stats_renderer_test.go @@ -15,6 +15,7 @@ import ( "github.com/prometheus/prometheus/config" "github.com/prometheus/prometheus/promql" "github.com/prometheus/prometheus/storage" + "github.com/prometheus/prometheus/util/features" v1 "github.com/prometheus/prometheus/web/api/v1" "github.com/stretchr/testify/assert" "github.com/weaveworks/common/user" @@ -95,6 +96,7 @@ func Test_StatsRenderer(t *testing.T) { false, false, nil, + features.NewRegistry(), ) promRouter := route.New().WithPrefix("/api/v1") diff --git a/pkg/ruler/compat.go b/pkg/ruler/compat.go index ff9a5995b2b..97ad799b275 100644 --- a/pkg/ruler/compat.go +++ b/pkg/ruler/compat.go @@ -80,13 +80,13 @@ func (a *PusherAppender) SetOptions(opts *storage.AppendOptions) { a.opts = opts } -func (a *PusherAppender) AppendHistogramCTZeroSample(ref storage.SeriesRef, l labels.Labels, t, ct int64, h *histogram.Histogram, fh *histogram.FloatHistogram) (storage.SeriesRef, error) { - // AppendHistogramCTZeroSample is a no-op for PusherAppender as it happens during scrape time only. +func (a *PusherAppender) AppendHistogramSTZeroSample(ref storage.SeriesRef, l labels.Labels, t, ct int64, h *histogram.Histogram, fh *histogram.FloatHistogram) (storage.SeriesRef, error) { + // AppendHistogramSTZeroSample is a no-op for PusherAppender as it happens during scrape time only. return 0, nil } -func (a *PusherAppender) AppendCTZeroSample(_ storage.SeriesRef, _ labels.Labels, _, _ int64) (storage.SeriesRef, error) { - // AppendCTZeroSample is a no-op for PusherAppender as it happens during scrape time only. +func (a *PusherAppender) AppendSTZeroSample(_ storage.SeriesRef, _ labels.Labels, _, _ int64) (storage.SeriesRef, error) { + // AppendSTZeroSample is a no-op for PusherAppender as it happens during scrape time only. return 0, nil } diff --git a/pkg/util/validation/validate.go b/pkg/util/validation/validate.go index a2f7bc395df..31c4b1f34cd 100644 --- a/pkg/util/validation/validate.go +++ b/pkg/util/validation/validate.go @@ -432,7 +432,10 @@ func ValidateNativeHistogram(validateMetrics *ValidateMetrics, limits *Limits, u validateMetrics.DiscardedSamples.WithLabelValues(nativeHistogramBucketCountLimitExceeded, userID).Inc() return cortexpb.Histogram{}, newHistogramBucketLimitExceededError(ls, limits.MaxNativeHistogramBuckets) } - fh = fh.ReduceResolution(fh.Schema - 1) + if err := fh.ReduceResolution(fh.Schema - 1); err != nil { + validateMetrics.DiscardedSamples.WithLabelValues(nativeHistogramInvalid, userID).Inc() + return cortexpb.Histogram{}, newNativeHistogramInvalidError(ls, err) + } } if oBuckets != len(fh.PositiveBuckets)+len(fh.NegativeBuckets) { validateMetrics.HistogramSamplesReducedResolution.WithLabelValues(userID).Inc() @@ -476,7 +479,10 @@ func ValidateNativeHistogram(validateMetrics *ValidateMetrics, limits *Limits, u validateMetrics.DiscardedSamples.WithLabelValues(nativeHistogramBucketCountLimitExceeded, userID).Inc() return cortexpb.Histogram{}, newHistogramBucketLimitExceededError(ls, limits.MaxNativeHistogramBuckets) } - h = h.ReduceResolution(h.Schema - 1) + if err := h.ReduceResolution(h.Schema - 1); err != nil { + validateMetrics.DiscardedSamples.WithLabelValues(nativeHistogramInvalid, userID).Inc() + return cortexpb.Histogram{}, newNativeHistogramInvalidError(ls, err) + } } if oBuckets != len(h.PositiveBuckets)+len(h.NegativeBuckets) { validateMetrics.HistogramSamplesReducedResolution.WithLabelValues(userID).Inc() diff --git a/pkg/util/validation/validate_test.go b/pkg/util/validation/validate_test.go index 6832ee61f0e..6a55e735a9a 100644 --- a/pkg/util/validation/validate_test.go +++ b/pkg/util/validation/validate_test.go @@ -509,33 +509,49 @@ func TestValidateNativeHistogram(t *testing.T) { discardedSampleLabelValue: nativeHistogramBucketCountLimitExceeded, }, { - name: "exceed limit and reduce resolution for 1 level, histogram", - bucketLimit: 6, - histogram: cortexpb.HistogramToHistogramProto(0, h.Copy()), - expectedHistogram: cortexpb.HistogramToHistogramProto(0, h.Copy().ReduceResolution(0)), + name: "exceed limit and reduce resolution for 1 level, histogram", + bucketLimit: 6, + histogram: cortexpb.HistogramToHistogramProto(0, h.Copy()), + expectedHistogram: func() cortexpb.Histogram { + hc := h.Copy() + require.NoError(t, hc.ReduceResolution(0)) + return cortexpb.HistogramToHistogramProto(0, hc) + }(), resolutionReduced: true, discardedSampleLabelValue: nativeHistogramBucketCountLimitExceeded, }, { - name: "exceed limit and reduce resolution for 1 level, float histogram", - bucketLimit: 6, - histogram: cortexpb.FloatHistogramToHistogramProto(0, fh.Copy()), - expectedHistogram: cortexpb.FloatHistogramToHistogramProto(0, fh.Copy().ReduceResolution(0)), + name: "exceed limit and reduce resolution for 1 level, float histogram", + bucketLimit: 6, + histogram: cortexpb.FloatHistogramToHistogramProto(0, fh.Copy()), + expectedHistogram: func() cortexpb.Histogram { + fhc := fh.Copy() + require.NoError(t, fhc.ReduceResolution(0)) + return cortexpb.FloatHistogramToHistogramProto(0, fhc) + }(), resolutionReduced: true, discardedSampleLabelValue: nativeHistogramBucketCountLimitExceeded, }, { - name: "exceed limit and reduce resolution for 2 levels, histogram", - bucketLimit: 4, - histogram: cortexpb.HistogramToHistogramProto(0, h.Copy()), - expectedHistogram: cortexpb.HistogramToHistogramProto(0, h.Copy().ReduceResolution(-1)), + name: "exceed limit and reduce resolution for 2 levels, histogram", + bucketLimit: 4, + histogram: cortexpb.HistogramToHistogramProto(0, h.Copy()), + expectedHistogram: func() cortexpb.Histogram { + hc := h.Copy() + require.NoError(t, hc.ReduceResolution(-1)) + return cortexpb.HistogramToHistogramProto(0, hc) + }(), discardedSampleLabelValue: nativeHistogramBucketCountLimitExceeded, }, { - name: "exceed limit and reduce resolution for 2 levels, float histogram", - bucketLimit: 4, - histogram: cortexpb.FloatHistogramToHistogramProto(0, fh.Copy()), - expectedHistogram: cortexpb.FloatHistogramToHistogramProto(0, fh.Copy().ReduceResolution(-1)), + name: "exceed limit and reduce resolution for 2 levels, float histogram", + bucketLimit: 4, + histogram: cortexpb.FloatHistogramToHistogramProto(0, fh.Copy()), + expectedHistogram: func() cortexpb.Histogram { + fhc := fh.Copy() + require.NoError(t, fhc.ReduceResolution(-1)) + return cortexpb.FloatHistogramToHistogramProto(0, fhc) + }(), discardedSampleLabelValue: nativeHistogramBucketCountLimitExceeded, }, { diff --git a/vendor/github.com/Azure/azure-sdk-for-go/sdk/azcore/CHANGELOG.md b/vendor/github.com/Azure/azure-sdk-for-go/sdk/azcore/CHANGELOG.md index 1799c6ef223..47d2b85fa86 100644 --- a/vendor/github.com/Azure/azure-sdk-for-go/sdk/azcore/CHANGELOG.md +++ b/vendor/github.com/Azure/azure-sdk-for-go/sdk/azcore/CHANGELOG.md @@ -1,5 +1,16 @@ # Release History +## 1.20.0 (2025-11-06) + +### Features Added + +* Added `runtime.FetcherForNextLinkOptions.HTTPVerb` to specify the HTTP verb when fetching the next page via next link. Defaults to `http.MethodGet`. + +### Bugs Fixed + +* Fixed potential panic when decoding base64 strings. +* Fixed an issue in resource identifier parsing which prevented it from returning an error for malformed resource IDs. + ## 1.19.1 (2025-09-11) ### Bugs Fixed diff --git a/vendor/github.com/Azure/azure-sdk-for-go/sdk/azcore/arm/internal/resource/resource_identifier.go b/vendor/github.com/Azure/azure-sdk-for-go/sdk/azcore/arm/internal/resource/resource_identifier.go index b8348b7d82e..8a40ebe4d2f 100644 --- a/vendor/github.com/Azure/azure-sdk-for-go/sdk/azcore/arm/internal/resource/resource_identifier.go +++ b/vendor/github.com/Azure/azure-sdk-for-go/sdk/azcore/arm/internal/resource/resource_identifier.go @@ -217,6 +217,7 @@ func appendNext(parent *ResourceID, parts []string, id string) (*ResourceID, err func splitStringAndOmitEmpty(v, sep string) []string { r := make([]string, 0) for _, s := range strings.Split(v, sep) { + s = strings.TrimSpace(s) if len(s) == 0 { continue } diff --git a/vendor/github.com/Azure/azure-sdk-for-go/sdk/azcore/internal/exported/exported.go b/vendor/github.com/Azure/azure-sdk-for-go/sdk/azcore/internal/exported/exported.go index 460170034aa..612af11ac61 100644 --- a/vendor/github.com/Azure/azure-sdk-for-go/sdk/azcore/internal/exported/exported.go +++ b/vendor/github.com/Azure/azure-sdk-for-go/sdk/azcore/internal/exported/exported.go @@ -92,7 +92,7 @@ func DecodeByteArray(s string, v *[]byte, format Base64Encoding) error { return nil } payload := string(s) - if payload[0] == '"' { + if len(payload) >= 2 && payload[0] == '"' && payload[len(payload)-1] == '"' { // remove surrounding quotes payload = payload[1 : len(payload)-1] } diff --git a/vendor/github.com/Azure/azure-sdk-for-go/sdk/azcore/internal/shared/constants.go b/vendor/github.com/Azure/azure-sdk-for-go/sdk/azcore/internal/shared/constants.go index 8aebe5ce53b..f152000913d 100644 --- a/vendor/github.com/Azure/azure-sdk-for-go/sdk/azcore/internal/shared/constants.go +++ b/vendor/github.com/Azure/azure-sdk-for-go/sdk/azcore/internal/shared/constants.go @@ -40,5 +40,5 @@ const ( Module = "azcore" // Version is the semantic version (see http://semver.org) of this module. - Version = "v1.19.1" + Version = "v1.20.0" ) diff --git a/vendor/github.com/Azure/azure-sdk-for-go/sdk/azcore/runtime/pager.go b/vendor/github.com/Azure/azure-sdk-for-go/sdk/azcore/runtime/pager.go index c66fc0a90a5..edb4a3cd44f 100644 --- a/vendor/github.com/Azure/azure-sdk-for-go/sdk/azcore/runtime/pager.go +++ b/vendor/github.com/Azure/azure-sdk-for-go/sdk/azcore/runtime/pager.go @@ -99,6 +99,11 @@ type FetcherForNextLinkOptions struct { // StatusCodes contains additional HTTP status codes indicating success. // The default value is http.StatusOK. StatusCodes []int + + // HTTPVerb specifies the HTTP verb to use when fetching the next page. + // The default value is http.MethodGet. + // This field is only used when NextReq is not specified. + HTTPVerb string } // FetcherForNextLink is a helper containing boilerplate code to simplify creating a PagingHandler[T].Fetcher from a next link URL. @@ -119,7 +124,11 @@ func FetcherForNextLink(ctx context.Context, pl Pipeline, nextLink string, first if options.NextReq != nil { req, err = options.NextReq(ctx, nextLink) } else { - req, err = NewRequest(ctx, http.MethodGet, nextLink) + verb := http.MethodGet + if options.HTTPVerb != "" { + verb = options.HTTPVerb + } + req, err = NewRequest(ctx, verb, nextLink) } } if err != nil { diff --git a/vendor/github.com/Azure/azure-sdk-for-go/sdk/azidentity/CHANGELOG.md b/vendor/github.com/Azure/azure-sdk-for-go/sdk/azidentity/CHANGELOG.md index ab63f9c031b..4a6349e1678 100644 --- a/vendor/github.com/Azure/azure-sdk-for-go/sdk/azidentity/CHANGELOG.md +++ b/vendor/github.com/Azure/azure-sdk-for-go/sdk/azidentity/CHANGELOG.md @@ -1,5 +1,34 @@ # Release History +## 1.13.1 (2025-11-10) + +### Bugs Fixed + +- `AzureCLICredential` quoted arguments incorrectly on Windows + +## 1.13.0 (2025-10-07) + +### Features Added + +- Added `AzurePowerShellCredential`, which authenticates as the identity logged in to Azure PowerShell + (thanks [ArmaanMcleod](https://github.com/ArmaanMcleod)) +- When `AZURE_TOKEN_CREDENTIALS` is set to `ManagedIdentityCredential`, `DefaultAzureCredential` behaves the same as + does `ManagedIdentityCredential` when used directly. It doesn't apply special retry configuration or attempt to + determine whether IMDS is available. ([#25265](https://github.com/Azure/azure-sdk-for-go/issues/25265)) + +### Breaking Changes + +* Removed the `WorkloadIdentityCredential` support for identity binding mode added in v1.13.0-beta.1. + It will return in v1.14.0-beta.1 + +## 1.13.0-beta.1 (2025-09-17) + +### Features Added + +- Added `AzurePowerShellCredential`, which authenticates as the identity logged in to Azure PowerShell + (thanks [ArmaanMcleod](https://github.com/ArmaanMcleod)) +- `WorkloadIdentityCredential` supports identity binding mode ([#25056](https://github.com/Azure/azure-sdk-for-go/issues/25056)) + ## 1.12.0 (2025-09-16) ### Features Added diff --git a/vendor/github.com/Azure/azure-sdk-for-go/sdk/azidentity/README.md b/vendor/github.com/Azure/azure-sdk-for-go/sdk/azidentity/README.md index 069bc688d52..127c25b72cf 100644 --- a/vendor/github.com/Azure/azure-sdk-for-go/sdk/azidentity/README.md +++ b/vendor/github.com/Azure/azure-sdk-for-go/sdk/azidentity/README.md @@ -1,6 +1,6 @@ # Azure Identity Client Module for Go -The Azure Identity module provides Microsoft Entra ID ([formerly Azure Active Directory](https://learn.microsoft.com/entra/fundamentals/new-name)) token authentication support across the Azure SDK. It includes a set of `TokenCredential` implementations, which can be used with Azure SDK clients supporting token authentication. +The Azure Identity module provides [Microsoft Entra ID](https://learn.microsoft.com/entra/fundamentals/whatis) token-based authentication support across the Azure SDK. It includes a set of `TokenCredential` implementations, which can be used with Azure SDK clients supporting token authentication. [![PkgGoDev](https://pkg.go.dev/badge/github.com/Azure/azure-sdk-for-go/sdk/azidentity)](https://pkg.go.dev/github.com/Azure/azure-sdk-for-go/sdk/azidentity) | [Microsoft Entra ID documentation](https://learn.microsoft.com/entra/identity/) @@ -153,6 +153,7 @@ client := armresources.NewResourceGroupsClient("subscription ID", chain, nil) |-|- |[AzureCLICredential](https://pkg.go.dev/github.com/Azure/azure-sdk-for-go/sdk/azidentity#AzureCLICredential)|Authenticate as the user signed in to the Azure CLI |[AzureDeveloperCLICredential](https://pkg.go.dev/github.com/Azure/azure-sdk-for-go/sdk/azidentity#AzureDeveloperCLICredential)|Authenticates as the user signed in to the Azure Developer CLI +|[AzurePowerShellCredential](https://pkg.go.dev/github.com/Azure/azure-sdk-for-go/sdk/azidentity#AzurePowerShellCredential)|Authenticates as the user signed in to Azure PowerShell ## Environment Variables diff --git a/vendor/github.com/Azure/azure-sdk-for-go/sdk/azidentity/TOKEN_CACHING.MD b/vendor/github.com/Azure/azure-sdk-for-go/sdk/azidentity/TOKEN_CACHING.MD index da2094e36b1..8bdaf816515 100644 --- a/vendor/github.com/Azure/azure-sdk-for-go/sdk/azidentity/TOKEN_CACHING.MD +++ b/vendor/github.com/Azure/azure-sdk-for-go/sdk/azidentity/TOKEN_CACHING.MD @@ -40,6 +40,7 @@ The following table indicates the state of in-memory and persistent caching in e | ------------------------------ | ------------------------------------------------------------------- | ------------------------ | | `AzureCLICredential` | Not Supported | Not Supported | | `AzureDeveloperCLICredential` | Not Supported | Not Supported | +| `AzurePowerShellCredential` | Not Supported | Not Supported | | `AzurePipelinesCredential` | Supported | Supported | | `ClientAssertionCredential` | Supported | Supported | | `ClientCertificateCredential` | Supported | Supported | diff --git a/vendor/github.com/Azure/azure-sdk-for-go/sdk/azidentity/TROUBLESHOOTING.md b/vendor/github.com/Azure/azure-sdk-for-go/sdk/azidentity/TROUBLESHOOTING.md index 838601d69c8..517006a424f 100644 --- a/vendor/github.com/Azure/azure-sdk-for-go/sdk/azidentity/TROUBLESHOOTING.md +++ b/vendor/github.com/Azure/azure-sdk-for-go/sdk/azidentity/TROUBLESHOOTING.md @@ -12,6 +12,7 @@ This troubleshooting guide covers failure investigation techniques, common error - [Troubleshoot AzureCLICredential authentication issues](#troubleshoot-azureclicredential-authentication-issues) - [Troubleshoot AzureDeveloperCLICredential authentication issues](#troubleshoot-azuredeveloperclicredential-authentication-issues) - [Troubleshoot AzurePipelinesCredential authentication issues](#troubleshoot-azurepipelinescredential-authentication-issues) +- [Troubleshoot AzurePowerShellCredential authentication issues](#troubleshoot-azurepowershellcredential-authentication-issues) - [Troubleshoot ClientCertificateCredential authentication issues](#troubleshoot-clientcertificatecredential-authentication-issues) - [Troubleshoot ClientSecretCredential authentication issues](#troubleshoot-clientsecretcredential-authentication-issues) - [Troubleshoot DefaultAzureCredential authentication issues](#troubleshoot-defaultazurecredential-authentication-issues) @@ -205,6 +206,34 @@ azd auth token --output json --scope https://management.core.windows.net/.defaul ``` >Note that output of this command will contain a valid access token, and SHOULD NOT BE SHARED to avoid compromising account security. + +## Troubleshoot `AzurePowerShellCredential` authentication issues + +| Error Message |Description| Mitigation | +|---|---|---| +|executable not found on path|No local installation of PowerShell was found.|Ensure that PowerShell is properly installed on the machine. Instructions for installing PowerShell can be found [here](https://learn.microsoft.com/powershell/scripting/install/installing-powershell).| +|Az.Accounts module not found|The Az.Account module needed for authentication in Azure PowerShell isn't installed.|Install the latest Az.Account module. Installation instructions can be found [here](https://learn.microsoft.com/powershell/azure/install-az-ps).| +|Please run "Connect-AzAccount" to set up account.|No account is currently logged into Azure PowerShell.|| + +#### __Verify Azure PowerShell can obtain tokens__ + +You can manually verify that Azure PowerShell is authenticated and can obtain tokens. First, use the `Get-AzContext` command to verify the account that is currently logged in to Azure PowerShell. + +``` +PS C:\> Get-AzContext + +Name Account SubscriptionName Environment TenantId +---- ------- ---------------- ----------- -------- +Subscription1 (xxxxxxxx-xxxx-xxxx-xxx... test@outlook.com Subscription1 AzureCloud xxxxxxxx-x... +``` + +Once you've verified Azure PowerShell is using correct account, validate that it's able to obtain tokens for this account: + +```bash +Get-AzAccessToken -ResourceUrl "https://management.core.windows.net" +``` +>Note that output of this command will contain a valid access token, and SHOULD NOT BE SHARED to avoid compromising account security. + ## Troubleshoot `WorkloadIdentityCredential` authentication issues diff --git a/vendor/github.com/Azure/azure-sdk-for-go/sdk/azidentity/azure_powershell_credential.go b/vendor/github.com/Azure/azure-sdk-for-go/sdk/azidentity/azure_powershell_credential.go new file mode 100644 index 00000000000..0829655545f --- /dev/null +++ b/vendor/github.com/Azure/azure-sdk-for-go/sdk/azidentity/azure_powershell_credential.go @@ -0,0 +1,234 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. + +package azidentity + +import ( + "context" + "encoding/base64" + "encoding/binary" + "encoding/json" + "errors" + "fmt" + "os/exec" + "runtime" + "strings" + "sync" + "time" + "unicode/utf16" + + "github.com/Azure/azure-sdk-for-go/sdk/azcore" + "github.com/Azure/azure-sdk-for-go/sdk/azcore/policy" + "github.com/Azure/azure-sdk-for-go/sdk/internal/log" +) + +const ( + credNameAzurePowerShell = "AzurePowerShellCredential" + noAzAccountModule = "Az.Accounts module not found" +) + +// AzurePowerShellCredentialOptions contains optional parameters for AzurePowerShellCredential. +type AzurePowerShellCredentialOptions struct { + // AdditionallyAllowedTenants specifies tenants to which the credential may authenticate, in addition to + // TenantID. When TenantID is empty, this option has no effect and the credential will authenticate to + // any requested tenant. Add the wildcard value "*" to allow the credential to authenticate to any tenant. + AdditionallyAllowedTenants []string + + // TenantID identifies the tenant the credential should authenticate in. + // Defaults to Azure PowerShell's default tenant, which is typically the home tenant of the logged in user. + TenantID string + + // inDefaultChain is true when the credential is part of DefaultAzureCredential + inDefaultChain bool + + // exec is used by tests to fake invoking Azure PowerShell + exec executor +} + +// AzurePowerShellCredential authenticates as the identity logged in to Azure PowerShell. +type AzurePowerShellCredential struct { + mu *sync.Mutex + opts AzurePowerShellCredentialOptions +} + +// NewAzurePowerShellCredential constructs an AzurePowerShellCredential. Pass nil to accept default options. +func NewAzurePowerShellCredential(options *AzurePowerShellCredentialOptions) (*AzurePowerShellCredential, error) { + cp := AzurePowerShellCredentialOptions{} + + if options != nil { + cp = *options + } + + if cp.TenantID != "" && !validTenantID(cp.TenantID) { + return nil, errInvalidTenantID + } + + if cp.exec == nil { + cp.exec = shellExec + } + + cp.AdditionallyAllowedTenants = resolveAdditionalTenants(cp.AdditionallyAllowedTenants) + + return &AzurePowerShellCredential{mu: &sync.Mutex{}, opts: cp}, nil +} + +// GetToken requests a token from Azure PowerShell. This credential doesn't cache tokens, so every call invokes Azure PowerShell. +// This method is called automatically by Azure SDK clients. +func (c *AzurePowerShellCredential) GetToken(ctx context.Context, opts policy.TokenRequestOptions) (azcore.AccessToken, error) { + at := azcore.AccessToken{} + + if len(opts.Scopes) != 1 { + return at, errors.New(credNameAzurePowerShell + ": GetToken() requires exactly one scope") + } + + if !validScope(opts.Scopes[0]) { + return at, fmt.Errorf("%s.GetToken(): invalid scope %q", credNameAzurePowerShell, opts.Scopes[0]) + } + + tenant, err := resolveTenant(c.opts.TenantID, opts.TenantID, credNameAzurePowerShell, c.opts.AdditionallyAllowedTenants) + if err != nil { + return at, err + } + + // Always pass a Microsoft Entra ID v1 resource URI (not a v2 scope) because Get-AzAccessToken only supports v1 resource URIs. + resource := strings.TrimSuffix(opts.Scopes[0], defaultSuffix) + + tenantArg := "" + if tenant != "" { + tenantArg = fmt.Sprintf(" -TenantId '%s'", tenant) + } + + if opts.Claims != "" { + encoded := base64.StdEncoding.EncodeToString([]byte(opts.Claims)) + return at, fmt.Errorf( + "%s.GetToken(): Azure PowerShell requires multifactor authentication or additional claims. Run this command then retry the operation: Connect-AzAccount%s -ClaimsChallenge '%s'", + credNameAzurePowerShell, + tenantArg, + encoded, + ) + } + + // Inline script to handle Get-AzAccessToken differences between Az.Accounts versions with SecureString handling and minimum version requirement + script := fmt.Sprintf(` +$ErrorActionPreference = 'Stop' +[version]$minimumVersion = '2.2.0' + +$mod = Import-Module Az.Accounts -MinimumVersion $minimumVersion -PassThru -ErrorAction SilentlyContinue + +if (-not $mod) { + Write-Error '%s' +} + +$params = @{ + ResourceUrl = '%s' + WarningAction = 'Ignore' +} + +# Only force AsSecureString for Az.Accounts versions > 2.17.0 and < 5.0.0 which return plain text token by default. +# Newer Az.Accounts versions return SecureString token by default and no longer use AsSecureString parameter. +if ($mod.Version -ge [version]'2.17.0' -and $mod.Version -lt [version]'5.0.0') { + $params['AsSecureString'] = $true +} + +$tenantId = '%s' +if ($tenantId.Length -gt 0) { + $params['TenantId'] = '%s' +} + +$token = Get-AzAccessToken @params + +$customToken = New-Object -TypeName psobject + +# The following .NET interop pattern is supported in all PowerShell versions and safely converts SecureString to plain text. +if ($token.Token -is [System.Security.SecureString]) { + $ssPtr = [System.Runtime.InteropServices.Marshal]::SecureStringToBSTR($token.Token) + try { + $plainToken = [System.Runtime.InteropServices.Marshal]::PtrToStringBSTR($ssPtr) + } finally { + [System.Runtime.InteropServices.Marshal]::ZeroFreeBSTR($ssPtr) + } + $customToken | Add-Member -MemberType NoteProperty -Name Token -Value $plainToken +} else { + $customToken | Add-Member -MemberType NoteProperty -Name Token -Value $token.Token +} +$customToken | Add-Member -MemberType NoteProperty -Name ExpiresOn -Value $token.ExpiresOn.ToUnixTimeSeconds() + +$jsonToken = $customToken | ConvertTo-Json +return $jsonToken +`, noAzAccountModule, resource, tenant, tenant) + + // Windows: prefer pwsh.exe (PowerShell Core), fallback to powershell.exe (Windows PowerShell) + // Unix: only support pwsh (PowerShell Core) + exe := "pwsh" + if runtime.GOOS == "windows" { + if _, err := exec.LookPath("pwsh.exe"); err == nil { + exe = "pwsh.exe" + } else { + exe = "powershell.exe" + } + } + + command := exe + " -NoProfile -NonInteractive -OutputFormat Text -EncodedCommand " + base64EncodeUTF16LE(script) + + c.mu.Lock() + defer c.mu.Unlock() + + b, err := c.opts.exec(ctx, credNameAzurePowerShell, command) + if err == nil { + at, err = c.createAccessToken(b) + } + + if err != nil { + err = unavailableIfInDAC(err, c.opts.inDefaultChain) + return at, err + } + + msg := fmt.Sprintf("%s.GetToken() acquired a token for scope %q", credNameAzurePowerShell, strings.Join(opts.Scopes, ", ")) + log.Write(EventAuthentication, msg) + + return at, nil +} + +func (c *AzurePowerShellCredential) createAccessToken(tk []byte) (azcore.AccessToken, error) { + t := struct { + Token string `json:"Token"` + ExpiresOn int64 `json:"ExpiresOn"` + }{} + + err := json.Unmarshal(tk, &t) + if err != nil { + return azcore.AccessToken{}, err + } + + converted := azcore.AccessToken{ + Token: t.Token, + ExpiresOn: time.Unix(t.ExpiresOn, 0).UTC(), + } + + return converted, nil +} + +// Encodes a string to Base64 using UTF-16LE encoding +func base64EncodeUTF16LE(text string) string { + u16 := utf16.Encode([]rune(text)) + buf := make([]byte, len(u16)*2) + for i, v := range u16 { + binary.LittleEndian.PutUint16(buf[i*2:], v) + } + return base64.StdEncoding.EncodeToString(buf) +} + +// Decodes a Base64 UTF-16LE string back to string +func base64DecodeUTF16LE(encoded string) (string, error) { + data, err := base64.StdEncoding.DecodeString(encoded) + if err != nil { + return "", err + } + u16 := make([]uint16, len(data)/2) + for i := range u16 { + u16[i] = binary.LittleEndian.Uint16(data[i*2:]) + } + return string(utf16.Decode(u16)), nil +} + +var _ azcore.TokenCredential = (*AzurePowerShellCredential)(nil) diff --git a/vendor/github.com/Azure/azure-sdk-for-go/sdk/azidentity/default_azure_credential.go b/vendor/github.com/Azure/azure-sdk-for-go/sdk/azidentity/default_azure_credential.go index c041a52dbbe..aaaabc5c2f3 100644 --- a/vendor/github.com/Azure/azure-sdk-for-go/sdk/azidentity/default_azure_credential.go +++ b/vendor/github.com/Azure/azure-sdk-for-go/sdk/azidentity/default_azure_credential.go @@ -26,6 +26,7 @@ const ( managedIdentity az azd + azurePowerShell ) // DefaultAzureCredentialOptions contains optional parameters for DefaultAzureCredential. @@ -71,6 +72,7 @@ type DefaultAzureCredentialOptions struct { // - [ManagedIdentityCredential] // - [AzureCLICredential] // - [AzureDeveloperCLICredential] +// - [AzurePowerShellCredential] // // Consult the documentation for these credential types for more information on how they authenticate. // Once a credential has successfully authenticated, DefaultAzureCredential will use that credential for @@ -83,7 +85,7 @@ type DefaultAzureCredentialOptions struct { // Valid values for AZURE_TOKEN_CREDENTIALS are the name of any single type in the above chain, for example // "EnvironmentCredential" or "AzureCLICredential", and these special values: // -// - "dev": try [AzureCLICredential] and [AzureDeveloperCLICredential], in that order +// - "dev": try [AzureCLICredential], [AzureDeveloperCLICredential], and [AzurePowerShellCredential], in that order // - "prod": try [EnvironmentCredential], [WorkloadIdentityCredential], and [ManagedIdentityCredential], in that order // // [DefaultAzureCredentialOptions].RequireAzureTokenCredentials controls whether AZURE_TOKEN_CREDENTIALS must be set. @@ -104,13 +106,13 @@ func NewDefaultAzureCredential(options *DefaultAzureCredentialOptions) (*Default var ( creds []azcore.TokenCredential errorMessages []string - selected = env | workloadIdentity | managedIdentity | az | azd + selected = env | workloadIdentity | managedIdentity | az | azd | azurePowerShell ) if atc, ok := os.LookupEnv(azureTokenCredentials); ok { switch { case atc == "dev": - selected = az | azd + selected = az | azd | azurePowerShell case atc == "prod": selected = env | workloadIdentity | managedIdentity case strings.EqualFold(atc, credNameEnvironment): @@ -123,6 +125,8 @@ func NewDefaultAzureCredential(options *DefaultAzureCredentialOptions) (*Default selected = az case strings.EqualFold(atc, credNameAzureDeveloperCLI): selected = azd + case strings.EqualFold(atc, credNameAzurePowerShell): + selected = azurePowerShell default: return nil, fmt.Errorf(`invalid %s value %q. Valid values are "dev", "prod", or the name of any credential type in the default chain. See https://aka.ms/azsdk/go/identity/docs#DefaultAzureCredential for more information`, azureTokenCredentials, atc) } @@ -164,7 +168,11 @@ func NewDefaultAzureCredential(options *DefaultAzureCredentialOptions) (*Default } } if selected&managedIdentity != 0 { - o := &ManagedIdentityCredentialOptions{ClientOptions: options.ClientOptions, dac: true} + o := &ManagedIdentityCredentialOptions{ + ClientOptions: options.ClientOptions, + // enable special DefaultAzureCredential behavior (IMDS probing) only when the chain contains another credential + dac: selected^managedIdentity != 0, + } if ID, ok := os.LookupEnv(azureClientID); ok { o.ID = ClientID(ID) } @@ -202,6 +210,19 @@ func NewDefaultAzureCredential(options *DefaultAzureCredentialOptions) (*Default creds = append(creds, &defaultCredentialErrorReporter{credType: credNameAzureDeveloperCLI, err: err}) } } + if selected&azurePowerShell != 0 { + azurePowerShellCred, err := NewAzurePowerShellCredential(&AzurePowerShellCredentialOptions{ + AdditionallyAllowedTenants: additionalTenants, + TenantID: options.TenantID, + inDefaultChain: true, + }) + if err == nil { + creds = append(creds, azurePowerShellCred) + } else { + errorMessages = append(errorMessages, credNameAzurePowerShell+": "+err.Error()) + creds = append(creds, &defaultCredentialErrorReporter{credType: credNameAzurePowerShell, err: err}) + } + } if len(errorMessages) > 0 { log.Writef(EventAuthentication, "NewDefaultAzureCredential failed to initialize some credentials:\n\t%s", strings.Join(errorMessages, "\n\t")) diff --git a/vendor/github.com/Azure/azure-sdk-for-go/sdk/azidentity/developer_credential_util.go b/vendor/github.com/Azure/azure-sdk-for-go/sdk/azidentity/developer_credential_util.go index 14f8a031265..cb7dbe2e4b8 100644 --- a/vendor/github.com/Azure/azure-sdk-for-go/sdk/azidentity/developer_credential_util.go +++ b/vendor/github.com/Azure/azure-sdk-for-go/sdk/azidentity/developer_credential_util.go @@ -12,7 +12,6 @@ import ( "errors" "os" "os/exec" - "runtime" "strings" "time" ) @@ -30,17 +29,9 @@ var shellExec = func(ctx context.Context, credName, command string) ([]byte, err ctx, cancel = context.WithTimeout(ctx, cliTimeout) defer cancel() } - var cmd *exec.Cmd - if runtime.GOOS == "windows" { - dir := os.Getenv("SYSTEMROOT") - if dir == "" { - return nil, newCredentialUnavailableError(credName, `environment variable "SYSTEMROOT" has no value`) - } - cmd = exec.CommandContext(ctx, "cmd.exe", "/c", command) - cmd.Dir = dir - } else { - cmd = exec.CommandContext(ctx, "/bin/sh", "-c", command) - cmd.Dir = "/bin" + cmd, err := buildCmd(ctx, credName, command) + if err != nil { + return nil, err } cmd.Env = os.Environ() stderr := bytes.Buffer{} @@ -57,7 +48,15 @@ var shellExec = func(ctx context.Context, credName, command string) ([]byte, err msg := stderr.String() var exErr *exec.ExitError if errors.As(err, &exErr) && exErr.ExitCode() == 127 || strings.Contains(msg, "' is not recognized") { - return nil, newCredentialUnavailableError(credName, "CLI executable not found on path") + return nil, newCredentialUnavailableError(credName, "executable not found on path") + } + if credName == credNameAzurePowerShell { + if strings.Contains(msg, "Connect-AzAccount") { + msg = `Please run "Connect-AzAccount" to set up an account` + } + if strings.Contains(msg, noAzAccountModule) { + msg = noAzAccountModule + } } if msg == "" { msg = err.Error() diff --git a/vendor/github.com/Azure/azure-sdk-for-go/sdk/azidentity/developer_credential_util_nonwindows.go b/vendor/github.com/Azure/azure-sdk-for-go/sdk/azidentity/developer_credential_util_nonwindows.go new file mode 100644 index 00000000000..681fcd0cf9f --- /dev/null +++ b/vendor/github.com/Azure/azure-sdk-for-go/sdk/azidentity/developer_credential_util_nonwindows.go @@ -0,0 +1,17 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. + +//go:build !windows + +package azidentity + +import ( + "context" + "os/exec" +) + +func buildCmd(ctx context.Context, _, command string) (*exec.Cmd, error) { + cmd := exec.CommandContext(ctx, "/bin/sh", "-c", command) + cmd.Dir = "/bin" + return cmd, nil +} diff --git a/vendor/github.com/Azure/azure-sdk-for-go/sdk/azidentity/developer_credential_util_windows.go b/vendor/github.com/Azure/azure-sdk-for-go/sdk/azidentity/developer_credential_util_windows.go new file mode 100644 index 00000000000..09c7a1a977c --- /dev/null +++ b/vendor/github.com/Azure/azure-sdk-for-go/sdk/azidentity/developer_credential_util_windows.go @@ -0,0 +1,22 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. + +package azidentity + +import ( + "context" + "os" + "os/exec" + "syscall" +) + +func buildCmd(ctx context.Context, credName, command string) (*exec.Cmd, error) { + dir := os.Getenv("SYSTEMROOT") + if dir == "" { + return nil, newCredentialUnavailableError(credName, `environment variable "SYSTEMROOT" has no value`) + } + cmd := exec.CommandContext(ctx, "cmd.exe") + cmd.Dir = dir + cmd.SysProcAttr = &syscall.SysProcAttr{CmdLine: "/c " + command} + return cmd, nil +} diff --git a/vendor/github.com/Azure/azure-sdk-for-go/sdk/azidentity/errors.go b/vendor/github.com/Azure/azure-sdk-for-go/sdk/azidentity/errors.go index a6d7c6cbc78..33cb63be09a 100644 --- a/vendor/github.com/Azure/azure-sdk-for-go/sdk/azidentity/errors.go +++ b/vendor/github.com/Azure/azure-sdk-for-go/sdk/azidentity/errors.go @@ -99,6 +99,8 @@ func (e *AuthenticationFailedError) Error() string { anchor = "apc" case credNameCert: anchor = "client-cert" + case credNameAzurePowerShell: + anchor = "azure-pwsh" case credNameSecret: anchor = "client-secret" case credNameManagedIdentity: diff --git a/vendor/github.com/Azure/azure-sdk-for-go/sdk/azidentity/version.go b/vendor/github.com/Azure/azure-sdk-for-go/sdk/azidentity/version.go index 4c88605366d..041f11658df 100644 --- a/vendor/github.com/Azure/azure-sdk-for-go/sdk/azidentity/version.go +++ b/vendor/github.com/Azure/azure-sdk-for-go/sdk/azidentity/version.go @@ -14,5 +14,5 @@ const ( module = "github.com/Azure/azure-sdk-for-go/sdk/" + component // Version is the semantic version (see http://semver.org) of this module. - version = "v1.12.0" + version = "v1.13.1" ) diff --git a/vendor/github.com/AzureAD/microsoft-authentication-library-for-go/apps/confidential/confidential.go b/vendor/github.com/AzureAD/microsoft-authentication-library-for-go/apps/confidential/confidential.go index 549d68ab991..29c004320d6 100644 --- a/vendor/github.com/AzureAD/microsoft-authentication-library-for-go/apps/confidential/confidential.go +++ b/vendor/github.com/AzureAD/microsoft-authentication-library-for-go/apps/confidential/confidential.go @@ -596,6 +596,11 @@ func (cca Client) AcquireTokenSilent(ctx context.Context, scopes []string, opts return AuthResult{}, errors.New("call another AcquireToken method to request a new token having these claims") } + // For service principal scenarios, require WithSilentAccount for public API + if o.account.IsZero() { + return AuthResult{}, errors.New("WithSilentAccount option is required") + } + silentParameters := base.AcquireTokenSilentParameters{ Scopes: scopes, Account: o.account, @@ -604,8 +609,15 @@ func (cca Client) AcquireTokenSilent(ctx context.Context, scopes []string, opts IsAppCache: o.account.IsZero(), TenantID: o.tenantID, AuthnScheme: o.authnScheme, + Claims: o.claims, } + return cca.acquireTokenSilentInternal(ctx, silentParameters) +} + +// acquireTokenSilentInternal is the internal implementation shared by AcquireTokenSilent and AcquireTokenByCredential +func (cca Client) acquireTokenSilentInternal(ctx context.Context, silentParameters base.AcquireTokenSilentParameters) (AuthResult, error) { + return cca.base.AcquireTokenSilent(ctx, silentParameters) } @@ -708,8 +720,10 @@ func (cca Client) AcquireTokenByAuthCode(ctx context.Context, code string, redir // acquireTokenByCredentialOptions contains optional configuration for AcquireTokenByCredential type acquireTokenByCredentialOptions struct { - claims, tenantID string - authnScheme AuthenticationScheme + claims, tenantID string + authnScheme AuthenticationScheme + extraBodyParameters map[string]string + cacheKeyComponents map[string]string } // AcquireByCredentialOption is implemented by options for AcquireTokenByCredential @@ -719,7 +733,7 @@ type AcquireByCredentialOption interface { // AcquireTokenByCredential acquires a security token from the authority, using the client credentials grant. // -// Options: [WithClaims], [WithTenantID] +// Options: [WithClaims], [WithTenantID], [WithFMIPath], [WithAttribute] func (cca Client) AcquireTokenByCredential(ctx context.Context, scopes []string, opts ...AcquireByCredentialOption) (AuthResult, error) { o := acquireTokenByCredentialOptions{} err := options.ApplyOptions(&o, opts) @@ -736,6 +750,29 @@ func (cca Client) AcquireTokenByCredential(ctx context.Context, scopes []string, if o.authnScheme != nil { authParams.AuthnScheme = o.authnScheme } + authParams.ExtraBodyParameters = o.extraBodyParameters + authParams.CacheKeyComponents = o.cacheKeyComponents + if o.claims == "" { + silentParameters := base.AcquireTokenSilentParameters{ + Scopes: scopes, + Account: Account{}, // empty account for app token + RequestType: accesstokens.ATConfidential, + Credential: cca.cred, + IsAppCache: true, + TenantID: o.tenantID, + AuthnScheme: o.authnScheme, + Claims: o.claims, + ExtraBodyParameters: o.extraBodyParameters, + CacheKeyComponents: o.cacheKeyComponents, + } + + // Use internal method with empty account (service principal scenario) + cache, err := cca.acquireTokenSilentInternal(ctx, silentParameters) + if err == nil { + return cache, nil + } + } + token, err := cca.base.Token.Credential(ctx, authParams, cca.cred) if err != nil { return AuthResult{}, err @@ -781,3 +818,63 @@ func (cca Client) Account(ctx context.Context, accountID string) (Account, error func (cca Client) RemoveAccount(ctx context.Context, account Account) error { return cca.base.RemoveAccount(ctx, account) } + +// WithFMIPath specifies the path to a federated managed identity. +// The path should point to a valid FMI configuration file that contains the necessary +// identity information for authentication. +func WithFMIPath(path string) interface { + AcquireByCredentialOption + options.CallOption +} { + return struct { + AcquireByCredentialOption + options.CallOption + }{ + CallOption: options.NewCallOption( + func(a any) error { + switch t := a.(type) { + case *acquireTokenByCredentialOptions: + if t.extraBodyParameters == nil { + t.extraBodyParameters = make(map[string]string) + } + if t.cacheKeyComponents == nil { + t.cacheKeyComponents = make(map[string]string) + } + t.cacheKeyComponents["fmi_path"] = path + t.extraBodyParameters["fmi_path"] = path + default: + return fmt.Errorf("unexpected options type %T", a) + } + return nil + }, + ), + } +} + +// WithAttribute specifies an identity attribute to include in the token request. +// The attribute is sent as "attributes" in the request body and returned as "xmc_attr" +// in the access token claims. This is sometimes used withFMIPath +func WithAttribute(attrValue string) interface { + AcquireByCredentialOption + options.CallOption +} { + return struct { + AcquireByCredentialOption + options.CallOption + }{ + CallOption: options.NewCallOption( + func(a any) error { + switch t := a.(type) { + case *acquireTokenByCredentialOptions: + if t.extraBodyParameters == nil { + t.extraBodyParameters = make(map[string]string) + } + t.extraBodyParameters["attributes"] = attrValue + default: + return fmt.Errorf("unexpected options type %T", a) + } + return nil + }, + ), + } +} diff --git a/vendor/github.com/AzureAD/microsoft-authentication-library-for-go/apps/internal/base/base.go b/vendor/github.com/AzureAD/microsoft-authentication-library-for-go/apps/internal/base/base.go index 61c1c4cec1e..abf54f7e509 100644 --- a/vendor/github.com/AzureAD/microsoft-authentication-library-for-go/apps/internal/base/base.go +++ b/vendor/github.com/AzureAD/microsoft-authentication-library-for-go/apps/internal/base/base.go @@ -46,16 +46,18 @@ type accountManager interface { // AcquireTokenSilentParameters contains the parameters to acquire a token silently (from cache). type AcquireTokenSilentParameters struct { - Scopes []string - Account shared.Account - RequestType accesstokens.AppType - Credential *accesstokens.Credential - IsAppCache bool - TenantID string - UserAssertion string - AuthorizationType authority.AuthorizeType - Claims string - AuthnScheme authority.AuthenticationScheme + Scopes []string + Account shared.Account + RequestType accesstokens.AppType + Credential *accesstokens.Credential + IsAppCache bool + TenantID string + UserAssertion string + AuthorizationType authority.AuthorizeType + Claims string + AuthnScheme authority.AuthenticationScheme + ExtraBodyParameters map[string]string + CacheKeyComponents map[string]string } // AcquireTokenAuthCodeParameters contains the parameters required to acquire an access token using the auth code flow. @@ -327,7 +329,12 @@ func (b Client) AcquireTokenSilent(ctx context.Context, silent AcquireTokenSilen if silent.AuthnScheme != nil { authParams.AuthnScheme = silent.AuthnScheme } - + if silent.CacheKeyComponents != nil { + authParams.CacheKeyComponents = silent.CacheKeyComponents + } + if silent.ExtraBodyParameters != nil { + authParams.ExtraBodyParameters = silent.ExtraBodyParameters + } m := b.pmanager if authParams.AuthorizationType != authority.ATOnBehalfOf { authParams.AuthorizationType = authority.ATRefreshToken @@ -367,8 +374,19 @@ func (b Client) AcquireTokenSilent(ctx context.Context, silent AcquireTokenSilen // If the token is not same, we don't need to refresh it. // Which means it refreshed. if str, err := m.Read(ctx, authParams); err == nil && str.AccessToken.Secret == ar.AccessToken { - if tr, er := b.Token.Credential(ctx, authParams, silent.Credential); er == nil { - return b.AuthResultFromToken(ctx, authParams, tr) + switch silent.RequestType { + case accesstokens.ATConfidential: + if tr, er := b.Token.Credential(ctx, authParams, silent.Credential); er == nil { + return b.AuthResultFromToken(ctx, authParams, tr) + } + case accesstokens.ATPublic: + token, err := b.Token.Refresh(ctx, silent.RequestType, authParams, silent.Credential, storageTokenResponse.RefreshToken) + if err != nil { + return ar, err + } + return b.AuthResultFromToken(ctx, authParams, token) + case accesstokens.ATUnknown: + return ar, errors.New("silent request type cannot be ATUnknown") } } } @@ -446,6 +464,9 @@ func (b Client) AcquireTokenOnBehalfOf(ctx context.Context, onBehalfOfParams Acq authParams.Claims = onBehalfOfParams.Claims authParams.Scopes = onBehalfOfParams.Scopes authParams.UserAssertion = onBehalfOfParams.UserAssertion + if authParams.ExtraBodyParameters != nil { + authParams.ExtraBodyParameters = silentParameters.ExtraBodyParameters + } token, err := b.Token.OnBehalfOf(ctx, authParams, onBehalfOfParams.Credential) if err == nil { ar, err = b.AuthResultFromToken(ctx, authParams, token) diff --git a/vendor/github.com/AzureAD/microsoft-authentication-library-for-go/apps/internal/base/storage/items.go b/vendor/github.com/AzureAD/microsoft-authentication-library-for-go/apps/internal/base/storage/items.go index 7379e2233c8..b7d1a670b1e 100644 --- a/vendor/github.com/AzureAD/microsoft-authentication-library-for-go/apps/internal/base/storage/items.go +++ b/vendor/github.com/AzureAD/microsoft-authentication-library-for-go/apps/internal/base/storage/items.go @@ -79,6 +79,7 @@ type AccessToken struct { UserAssertionHash string `json:"user_assertion_hash,omitempty"` TokenType string `json:"token_type,omitempty"` AuthnSchemeKeyID string `json:"keyid,omitempty"` + ExtCacheKey string `json:"ext_cache_key,omitempty"` AdditionalFields map[string]interface{} } @@ -105,15 +106,21 @@ func NewAccessToken(homeID, env, realm, clientID string, cachedAt, refreshOn, ex // Key outputs the key that can be used to uniquely look up this entry in a map. func (a AccessToken) Key() string { ks := []string{a.HomeAccountID, a.Environment, a.CredentialType, a.ClientID, a.Realm, a.Scopes} - key := strings.Join( - ks, - shared.CacheKeySeparator, - ) + // add token type to key for new access tokens types. skip for bearer token type to // preserve fwd and back compat between a common cache and msal clients if !strings.EqualFold(a.TokenType, authority.AccessTokenTypeBearer) { - key = strings.Join([]string{key, a.TokenType}, shared.CacheKeySeparator) + ks = append(ks, a.TokenType) } + // add extra body param hash to key if present + if a.ExtCacheKey != "" { + ks[2] = "atext" // if the there is extra cache we add "atext" to the key replacing accesstoken + ks = append(ks, a.ExtCacheKey) + } + key := strings.Join( + ks, + shared.CacheKeySeparator, + ) return strings.ToLower(key) } diff --git a/vendor/github.com/AzureAD/microsoft-authentication-library-for-go/apps/internal/base/storage/storage.go b/vendor/github.com/AzureAD/microsoft-authentication-library-for-go/apps/internal/base/storage/storage.go index 84a234967ff..825d8a0f660 100644 --- a/vendor/github.com/AzureAD/microsoft-authentication-library-for-go/apps/internal/base/storage/storage.go +++ b/vendor/github.com/AzureAD/microsoft-authentication-library-for-go/apps/internal/base/storage/storage.go @@ -135,7 +135,8 @@ func (m *Manager) Read(ctx context.Context, authParameters authority.AuthParams) aliases = metadata.Aliases } - accessToken := m.readAccessToken(homeAccountID, aliases, realm, clientID, scopes, tokenType, authnSchemeKeyID) + accessToken := m.readAccessToken(homeAccountID, aliases, realm, clientID, scopes, tokenType, authnSchemeKeyID, authParameters.CacheExtKeyGenerator()) + tr.AccessToken = accessToken if homeAccountID == "" { @@ -203,6 +204,7 @@ func (m *Manager) Write(authParameters authority.AuthParams, tokenResponse acces authnSchemeKeyID, ) + accessToken.ExtCacheKey = authParameters.CacheExtKeyGenerator() // Since we have a valid access token, cache it before moving on. if err := accessToken.Validate(); err == nil { if err := m.writeAccessToken(accessToken); err != nil { @@ -291,26 +293,49 @@ func (m *Manager) aadMetadata(ctx context.Context, authorityInfo authority.Info) return m.aadCache[authorityInfo.Host], nil } -func (m *Manager) readAccessToken(homeID string, envAliases []string, realm, clientID string, scopes []string, tokenType, authnSchemeKeyID string) AccessToken { +func (m *Manager) readAccessToken(homeID string, envAliases []string, realm, clientID string, scopes []string, tokenType, authnSchemeKeyID, extCacheKey string) AccessToken { m.contractMu.RLock() - // TODO: linear search (over a map no less) is slow for a large number (thousands) of tokens. - // this shows up as the dominating node in a profile. for real-world scenarios this likely isn't - // an issue, however if it does become a problem then we know where to look. - for k, at := range m.contract.AccessTokens { + + tokensToSearch := m.contract.AccessTokens + + for k, at := range tokensToSearch { + // TODO: linear search (over a map no less) is slow for a large number (thousands) of tokens. + // this shows up as the dominating node in a profile. for real-world scenarios this likely isn't + // an issue, however if it does become a problem then we know where to look. if at.HomeAccountID == homeID && at.Realm == realm && at.ClientID == clientID { - if (strings.EqualFold(at.TokenType, tokenType) && at.AuthnSchemeKeyID == authnSchemeKeyID) || (at.TokenType == "" && (tokenType == "" || tokenType == "Bearer")) { - if checkAlias(at.Environment, envAliases) && isMatchingScopes(scopes, at.Scopes) { - m.contractMu.RUnlock() - if needsUpgrade(k) { - m.contractMu.Lock() - defer m.contractMu.Unlock() - at = upgrade(m.contract.AccessTokens, k) + // Match token type and authentication scheme + tokenTypeMatch := (strings.EqualFold(at.TokenType, tokenType) && at.AuthnSchemeKeyID == authnSchemeKeyID) || + (at.TokenType == "" && (tokenType == "" || tokenType == "Bearer")) + environmentAndScopesMatch := checkAlias(at.Environment, envAliases) && isMatchingScopes(scopes, at.Scopes) + + if tokenTypeMatch && environmentAndScopesMatch { + // For hashed tokens, check that the key contains the hash + if extCacheKey != "" { + if !strings.Contains(k, extCacheKey) { + continue // Skip this token if the key doesn't contain the hash + } + } else { + // If no extCacheKey is provided, only match tokens that also have no extCacheKey + if at.ExtCacheKey != "" { + continue // Skip tokens that require a hash when no hash is provided } + } + // Handle token upgrade if needed + if needsUpgrade(k) { + m.contractMu.RUnlock() + m.contractMu.Lock() + at = upgrade(tokensToSearch, k) + m.contractMu.Unlock() return at } + + m.contractMu.RUnlock() + return at } } } + + // No token found, unlock and return empty token m.contractMu.RUnlock() return AccessToken{} } diff --git a/vendor/github.com/AzureAD/microsoft-authentication-library-for-go/apps/internal/oauth/ops/accesstokens/accesstokens.go b/vendor/github.com/AzureAD/microsoft-authentication-library-for-go/apps/internal/oauth/ops/accesstokens/accesstokens.go index d738c7591ee..481f9e43411 100644 --- a/vendor/github.com/AzureAD/microsoft-authentication-library-for-go/apps/internal/oauth/ops/accesstokens/accesstokens.go +++ b/vendor/github.com/AzureAD/microsoft-authentication-library-for-go/apps/internal/oauth/ops/accesstokens/accesstokens.go @@ -281,6 +281,9 @@ func (c Client) FromClientSecret(ctx context.Context, authParameters authority.A qv.Set(clientID, authParameters.ClientID) addScopeQueryParam(qv, authParameters) + // Add extra body parameters if provided + addExtraBodyParameters(ctx, qv, authParameters) + return c.doTokenResp(ctx, authParameters, qv) } @@ -296,6 +299,9 @@ func (c Client) FromAssertion(ctx context.Context, authParameters authority.Auth qv.Set(clientInfo, clientInfoVal) addScopeQueryParam(qv, authParameters) + // Add extra body parameters if provided + addExtraBodyParameters(ctx, qv, authParameters) + return c.doTokenResp(ctx, authParameters, qv) } @@ -329,6 +335,8 @@ func (c Client) FromUserAssertionClientCertificate(ctx context.Context, authPara qv.Set("requested_token_use", "on_behalf_of") addScopeQueryParam(qv, authParameters) + // Add extra body parameters if provided + addExtraBodyParameters(ctx, qv, authParameters) return c.doTokenResp(ctx, authParameters, qv) } @@ -466,3 +474,12 @@ func addScopeQueryParam(queryParams url.Values, authParameters authority.AuthPar scopes := AppendDefaultScopes(authParameters) queryParams.Set("scope", strings.Join(scopes, " ")) } + +// addExtraBodyParameters evaluates and adds extra body parameters to the request +func addExtraBodyParameters(ctx context.Context, v url.Values, ap authority.AuthParams) { + for key, value := range ap.ExtraBodyParameters { + if value != "" { + v.Set(key, value) + } + } +} diff --git a/vendor/github.com/AzureAD/microsoft-authentication-library-for-go/apps/internal/oauth/ops/authority/authority.go b/vendor/github.com/AzureAD/microsoft-authentication-library-for-go/apps/internal/oauth/ops/authority/authority.go index 3f403746404..debd465dbad 100644 --- a/vendor/github.com/AzureAD/microsoft-authentication-library-for-go/apps/internal/oauth/ops/authority/authority.go +++ b/vendor/github.com/AzureAD/microsoft-authentication-library-for-go/apps/internal/oauth/ops/authority/authority.go @@ -15,6 +15,7 @@ import ( "net/url" "os" "path" + "sort" "strings" "time" @@ -47,6 +48,8 @@ type jsonCaller interface { } // For backward compatibility, accept both old and new China endpoints for a transition period. +// This list is derived from the AAD instance discovery metadata and represents all known trusted hosts +// across different Azure clouds (Public, China, Germany, US Government, etc.) var aadTrustedHostList = map[string]bool{ "login.windows.net": true, // Microsoft Azure Worldwide - Used in validation scenarios where host is not this list "login.partner.microsoftonline.cn": true, // Microsoft Azure China (new) @@ -55,6 +58,9 @@ var aadTrustedHostList = map[string]bool{ "login-us.microsoftonline.com": true, // Microsoft Azure US Government - Legacy "login.microsoftonline.us": true, // Microsoft Azure US Government "login.microsoftonline.com": true, // Microsoft Azure Worldwide + "login.microsoft.com": true, + "sts.windows.net": true, + "login.usgovcloudapi.net": true, } // TrustedHost checks if an AAD host is trusted/valid. @@ -103,36 +109,46 @@ func (r *TenantDiscoveryResponse) Validate() error { // ValidateIssuerMatchesAuthority validates that the issuer in the TenantDiscoveryResponse matches the authority. // This is used to identity security or configuration issues in authorities and the OIDC endpoint func (r *TenantDiscoveryResponse) ValidateIssuerMatchesAuthority(authorityURI string, aliases map[string]bool) error { - if authorityURI == "" { return errors.New("TenantDiscoveryResponse: empty authorityURI provided for validation") } + if r.Issuer == "" { + return errors.New("TenantDiscoveryResponse: empty issuer in response") + } - // Parse the issuer URL issuerURL, err := url.Parse(r.Issuer) if err != nil { return fmt.Errorf("TenantDiscoveryResponse: failed to parse issuer URL: %w", err) } + authorityURL, err := url.Parse(authorityURI) + if err != nil { + return fmt.Errorf("TenantDiscoveryResponse: failed to parse authority URL: %w", err) + } + + // Fast path: exact scheme + host match + if issuerURL.Scheme == authorityURL.Scheme && issuerURL.Host == authorityURL.Host { + return nil + } - // Even if it doesn't match the authority, issuers from known and trusted hosts are valid + // Alias-based acceptance if aliases != nil && aliases[issuerURL.Host] { return nil } - // Parse the authority URL for comparison - authorityURL, err := url.Parse(authorityURI) - if err != nil { - return fmt.Errorf("TenantDiscoveryResponse: failed to parse authority URL: %w", err) + issuerHost := issuerURL.Host + authorityHost := authorityURL.Host + + // Accept if issuer host is trusted + if TrustedHost(issuerHost) { + return nil } - // Check if the scheme and host match (paths can be ignored when validating the issuer) - if issuerURL.Scheme == authorityURL.Scheme && issuerURL.Host == authorityURL.Host { + // Accept if authority is a regional variant ending with "." + if strings.HasSuffix(authorityHost, "."+issuerHost) { return nil } - // If we get here, validation failed - return fmt.Errorf("TenantDiscoveryResponse: issuer from OIDC discovery '%s' does not match authority '%s' or a known pattern", - r.Issuer, authorityURI) + return fmt.Errorf("TenantDiscoveryResponse: issuer '%s' does not match authority '%s' or any trusted/alias rule", r.Issuer, authorityURI) } type InstanceDiscoveryMetadata struct { @@ -256,6 +272,12 @@ type AuthParams struct { DomainHint string // AuthnScheme is an optional scheme for formatting access tokens AuthnScheme AuthenticationScheme + // ExtraBodyParameters are additional parameters to include in token requests. + // The functions are evaluated at request time to get the parameter values. + // These parameters are also included in the cache key. + ExtraBodyParameters map[string]string + // CacheKeyComponents are additional components to include in the cache key. + CacheKeyComponents map[string]string } // NewAuthParams creates an authorization parameters object. @@ -642,8 +664,42 @@ func (a *AuthParams) AssertionHash() string { } func (a *AuthParams) AppKey() string { + baseKey := a.ClientID + "_" if a.AuthorityInfo.Tenant != "" { - return fmt.Sprintf("%s_%s_AppTokenCache", a.ClientID, a.AuthorityInfo.Tenant) + baseKey += a.AuthorityInfo.Tenant + } + + // Include extra body parameters in the cache key + paramHash := a.CacheExtKeyGenerator() + if paramHash != "" { + baseKey = fmt.Sprintf("%s_%s", baseKey, paramHash) + } + + return baseKey + "_AppTokenCache" +} + +// CacheExtKeyGenerator computes a hash of the Cache key components key and values +// to include in the cache key. This ensures tokens acquired with different +// parameters are cached separately. +func (a *AuthParams) CacheExtKeyGenerator() string { + if len(a.CacheKeyComponents) == 0 { + return "" + } + + // Sort keys to ensure consistent hashing + keys := make([]string, 0, len(a.CacheKeyComponents)) + for k := range a.CacheKeyComponents { + keys = append(keys, k) } - return fmt.Sprintf("%s__AppTokenCache", a.ClientID) + sort.Strings(keys) + + // Create a string by concatenating key+value pairs + keyStr := "" + for _, key := range keys { + // Append key followed by its value with no separator + keyStr += key + a.CacheKeyComponents[key] + } + + hash := sha256.Sum256([]byte(keyStr)) + return strings.ToLower(base64.RawURLEncoding.EncodeToString(hash[:])) } diff --git a/vendor/github.com/AzureAD/microsoft-authentication-library-for-go/apps/public/public.go b/vendor/github.com/AzureAD/microsoft-authentication-library-for-go/apps/public/public.go index 7beed26174e..797c086cb87 100644 --- a/vendor/github.com/AzureAD/microsoft-authentication-library-for-go/apps/public/public.go +++ b/vendor/github.com/AzureAD/microsoft-authentication-library-for-go/apps/public/public.go @@ -368,9 +368,9 @@ type AcquireByUsernamePasswordOption interface { acquireByUsernamePasswordOption() } -// AcquireTokenByUsernamePassword acquires a security token from the authority, via Username/Password Authentication. -// NOTE: this flow is NOT recommended. +// Deprecated: This API will be removed in a future release. Use a more secure flow instead. Follow this migration guide: https://aka.ms/msal-ropc-migration // +// AcquireTokenByUsernamePassword acquires a security token from the authority, via Username/Password Authentication. // Options: [WithClaims], [WithTenantID] func (pca Client) AcquireTokenByUsernamePassword(ctx context.Context, scopes []string, username, password string, opts ...AcquireByUsernamePasswordOption) (AuthResult, error) { o := acquireTokenByUsernamePasswordOptions{} diff --git a/vendor/github.com/hashicorp/consul/api/txn.go b/vendor/github.com/hashicorp/consul/api/txn.go index 59adafdac3d..65b6e8830f6 100644 --- a/vendor/github.com/hashicorp/consul/api/txn.go +++ b/vendor/github.com/hashicorp/consul/api/txn.go @@ -36,7 +36,7 @@ type TxnOps []*TxnOp type TxnResult struct { KV *KVPair Node *Node - Service *CatalogService + Service *AgentService Check *HealthCheck } diff --git a/vendor/github.com/hashicorp/go-version/LICENSE b/vendor/github.com/hashicorp/go-version/LICENSE index 1409d6ab92f..bb1e9a486af 100644 --- a/vendor/github.com/hashicorp/go-version/LICENSE +++ b/vendor/github.com/hashicorp/go-version/LICENSE @@ -1,4 +1,4 @@ -Copyright (c) 2014 HashiCorp, Inc. +Copyright IBM Corp. 2014, 2025 Mozilla Public License, version 2.0 diff --git a/vendor/github.com/hashicorp/go-version/README.md b/vendor/github.com/hashicorp/go-version/README.md index 4b7806cd964..83a8249f72c 100644 --- a/vendor/github.com/hashicorp/go-version/README.md +++ b/vendor/github.com/hashicorp/go-version/README.md @@ -1,6 +1,7 @@ # Versioning Library for Go + ![Build Status](https://github.com/hashicorp/go-version/actions/workflows/go-tests.yml/badge.svg) -[![GoDoc](https://godoc.org/github.com/hashicorp/go-version?status.svg)](https://godoc.org/github.com/hashicorp/go-version) +[![Go Reference](https://pkg.go.dev/badge/github.com/hashicorp/go-version.svg)](https://pkg.go.dev/github.com/hashicorp/go-version) go-version is a library for parsing versions and version constraints, and verifying versions against a set of constraints. go-version @@ -12,7 +13,7 @@ Versions used with go-version must follow [SemVer](http://semver.org/). ## Installation and Usage Package documentation can be found on -[GoDoc](http://godoc.org/github.com/hashicorp/go-version). +[Go Reference](https://pkg.go.dev/github.com/hashicorp/go-version). Installation can be done with a normal `go get`: diff --git a/vendor/github.com/hashicorp/go-version/constraint.go b/vendor/github.com/hashicorp/go-version/constraint.go index 29bdc4d2b5d..3964da070d6 100644 --- a/vendor/github.com/hashicorp/go-version/constraint.go +++ b/vendor/github.com/hashicorp/go-version/constraint.go @@ -1,4 +1,4 @@ -// Copyright (c) HashiCorp, Inc. +// Copyright IBM Corp. 2014, 2025 // SPDX-License-Identifier: MPL-2.0 package version @@ -8,8 +8,26 @@ import ( "regexp" "sort" "strings" + "sync" ) +var ( + constraintRegexp *regexp.Regexp + constraintRegexpOnce sync.Once +) + +func getConstraintRegexp() *regexp.Regexp { + constraintRegexpOnce.Do(func() { + // This heavy lifting only happens the first time this function is called + constraintRegexp = regexp.MustCompile(fmt.Sprintf( + `^\s*(%s)\s*(%s)\s*$`, + `<=|>=|!=|~>|<|>|=|`, + VersionRegexpRaw, + )) + }) + return constraintRegexp +} + // Constraint represents a single constraint for a version, such as // ">= 1.0". type Constraint struct { @@ -29,38 +47,11 @@ type Constraints []*Constraint type constraintFunc func(v, c *Version) bool -var constraintOperators map[string]constraintOperation - type constraintOperation struct { op operator f constraintFunc } -var constraintRegexp *regexp.Regexp - -func init() { - constraintOperators = map[string]constraintOperation{ - "": {op: equal, f: constraintEqual}, - "=": {op: equal, f: constraintEqual}, - "!=": {op: notEqual, f: constraintNotEqual}, - ">": {op: greaterThan, f: constraintGreaterThan}, - "<": {op: lessThan, f: constraintLessThan}, - ">=": {op: greaterThanEqual, f: constraintGreaterThanEqual}, - "<=": {op: lessThanEqual, f: constraintLessThanEqual}, - "~>": {op: pessimistic, f: constraintPessimistic}, - } - - ops := make([]string, 0, len(constraintOperators)) - for k := range constraintOperators { - ops = append(ops, regexp.QuoteMeta(k)) - } - - constraintRegexp = regexp.MustCompile(fmt.Sprintf( - `^\s*(%s)\s*(%s)\s*$`, - strings.Join(ops, "|"), - VersionRegexpRaw)) -} - // NewConstraint will parse one or more constraints from the given // constraint string. The string must be a comma-separated list of // constraints. @@ -107,7 +98,7 @@ func (cs Constraints) Check(v *Version) bool { // to '>0.2' it is *NOT* treated as equal. // // Missing operator is treated as equal to '=', whitespaces -// are ignored and constraints are sorted before comaparison. +// are ignored and constraints are sorted before comparison. func (cs Constraints) Equals(c Constraints) bool { if len(cs) != len(c) { return false @@ -176,9 +167,9 @@ func (c *Constraint) String() string { } func parseSingle(v string) (*Constraint, error) { - matches := constraintRegexp.FindStringSubmatch(v) + matches := getConstraintRegexp().FindStringSubmatch(v) if matches == nil { - return nil, fmt.Errorf("Malformed constraint: %s", v) + return nil, fmt.Errorf("malformed constraint: %s", v) } check, err := NewVersion(matches[2]) @@ -186,7 +177,25 @@ func parseSingle(v string) (*Constraint, error) { return nil, err } - cop := constraintOperators[matches[1]] + var cop constraintOperation + switch matches[1] { + case "=": + cop = constraintOperation{op: equal, f: constraintEqual} + case "!=": + cop = constraintOperation{op: notEqual, f: constraintNotEqual} + case ">": + cop = constraintOperation{op: greaterThan, f: constraintGreaterThan} + case "<": + cop = constraintOperation{op: lessThan, f: constraintLessThan} + case ">=": + cop = constraintOperation{op: greaterThanEqual, f: constraintGreaterThanEqual} + case "<=": + cop = constraintOperation{op: lessThanEqual, f: constraintLessThanEqual} + case "~>": + cop = constraintOperation{op: pessimistic, f: constraintPessimistic} + default: + cop = constraintOperation{op: equal, f: constraintEqual} + } return &Constraint{ f: cop.f, diff --git a/vendor/github.com/hashicorp/go-version/version.go b/vendor/github.com/hashicorp/go-version/version.go index 7c683c2813a..17b29732ee1 100644 --- a/vendor/github.com/hashicorp/go-version/version.go +++ b/vendor/github.com/hashicorp/go-version/version.go @@ -1,23 +1,39 @@ -// Copyright (c) HashiCorp, Inc. +// Copyright IBM Corp. 2014, 2025 // SPDX-License-Identifier: MPL-2.0 package version import ( - "bytes" "database/sql/driver" "fmt" "regexp" "strconv" "strings" + "sync" ) // The compiled regular expression used to test the validity of a version. var ( - versionRegexp *regexp.Regexp - semverRegexp *regexp.Regexp + versionRegexp *regexp.Regexp + versionRegexpOnce sync.Once + semverRegexp *regexp.Regexp + semverRegexpOnce sync.Once ) +func getVersionRegexp() *regexp.Regexp { + versionRegexpOnce.Do(func() { + versionRegexp = regexp.MustCompile("^" + VersionRegexpRaw + "$") + }) + return versionRegexp +} + +func getSemverRegexp() *regexp.Regexp { + semverRegexpOnce.Do(func() { + semverRegexp = regexp.MustCompile("^" + SemverRegexpRaw + "$") + }) + return semverRegexp +} + // The raw regular expression string used for testing the validity // of a version. const ( @@ -42,28 +58,23 @@ type Version struct { original string } -func init() { - versionRegexp = regexp.MustCompile("^" + VersionRegexpRaw + "$") - semverRegexp = regexp.MustCompile("^" + SemverRegexpRaw + "$") -} - // NewVersion parses the given version and returns a new // Version. func NewVersion(v string) (*Version, error) { - return newVersion(v, versionRegexp) + return newVersion(v, getVersionRegexp()) } // NewSemver parses the given version and returns a new // Version that adheres strictly to SemVer specs // https://semver.org/ func NewSemver(v string) (*Version, error) { - return newVersion(v, semverRegexp) + return newVersion(v, getSemverRegexp()) } func newVersion(v string, pattern *regexp.Regexp) (*Version, error) { matches := pattern.FindStringSubmatch(v) if matches == nil { - return nil, fmt.Errorf("Malformed version: %s", v) + return nil, fmt.Errorf("malformed version: %s", v) } segmentsStr := strings.Split(matches[1], ".") segments := make([]int64, len(segmentsStr)) @@ -71,7 +82,7 @@ func newVersion(v string, pattern *regexp.Regexp) (*Version, error) { val, err := strconv.ParseInt(str, 10, 64) if err != nil { return nil, fmt.Errorf( - "Error parsing version: %s", err) + "error parsing version: %s", err) } segments[i] = val @@ -174,7 +185,7 @@ func (v *Version) Compare(other *Version) int { } else if lhs < rhs { return -1 } - // Otherwis, rhs was > lhs, they're not equal + // Otherwise, rhs was > lhs, they're not equal return 1 } @@ -382,22 +393,29 @@ func (v *Version) Segments64() []int64 { // missing parts (1.0 => 1.0.0) will be made into a canonicalized form // as shown in the parenthesized examples. func (v *Version) String() string { - var buf bytes.Buffer - fmtParts := make([]string, len(v.segments)) + return string(v.bytes()) +} + +func (v *Version) bytes() []byte { + var buf []byte for i, s := range v.segments { - // We can ignore err here since we've pre-parsed the values in segments - str := strconv.FormatInt(s, 10) - fmtParts[i] = str + if i > 0 { + buf = append(buf, '.') + } + buf = strconv.AppendInt(buf, s, 10) } - fmt.Fprintf(&buf, strings.Join(fmtParts, ".")) + if v.pre != "" { - fmt.Fprintf(&buf, "-%s", v.pre) + buf = append(buf, '-') + buf = append(buf, v.pre...) } + if v.metadata != "" { - fmt.Fprintf(&buf, "+%s", v.metadata) + buf = append(buf, '+') + buf = append(buf, v.metadata...) } - return buf.String() + return buf } // Original returns the original parsed version as-is, including any diff --git a/vendor/github.com/hashicorp/go-version/version_collection.go b/vendor/github.com/hashicorp/go-version/version_collection.go index 83547fe13d6..11bc8b1c56c 100644 --- a/vendor/github.com/hashicorp/go-version/version_collection.go +++ b/vendor/github.com/hashicorp/go-version/version_collection.go @@ -1,4 +1,4 @@ -// Copyright (c) HashiCorp, Inc. +// Copyright IBM Corp. 2014, 2025 // SPDX-License-Identifier: MPL-2.0 package version diff --git a/vendor/github.com/miekg/dns/README.md b/vendor/github.com/miekg/dns/README.md index 54471f5c225..2a7d8c265bf 100644 --- a/vendor/github.com/miekg/dns/README.md +++ b/vendor/github.com/miekg/dns/README.md @@ -3,6 +3,11 @@ [![Go Report Card](https://goreportcard.com/badge/github.com/miekg/dns)](https://goreportcard.com/report/miekg/dns) [![](https://godoc.org/github.com/miekg/dns?status.svg)](https://godoc.org/github.com/miekg/dns) +DNS version 2 is now available at , check it out if you want to +help shape the next 15 years of the Go DNS package. + +The version here will see no new features and less and less development. + # Alternative (more granular) approach to a DNS library > Less is more. @@ -17,96 +22,97 @@ avoiding breaking changes wherever reasonable. We support the last two versions # Goals -* KISS; -* Fast; -* Small API. If it's easy to code in Go, don't make a function for it. +- KISS; +- Fast; +- Small API. If it's easy to code in Go, don't make a function for it. # Users A not-so-up-to-date-list-that-may-be-actually-current: -* https://github.com/coredns/coredns -* https://github.com/abh/geodns -* https://github.com/baidu/bfe -* http://www.statdns.com/ -* http://www.dnsinspect.com/ -* https://github.com/chuangbo/jianbing-dictionary-dns -* http://www.dns-lg.com/ -* https://github.com/fcambus/rrda -* https://github.com/kenshinx/godns -* https://github.com/skynetservices/skydns -* https://github.com/hashicorp/consul -* https://github.com/DevelopersPL/godnsagent -* https://github.com/duedil-ltd/discodns -* https://github.com/StalkR/dns-reverse-proxy -* https://github.com/tianon/rawdns -* https://mesosphere.github.io/mesos-dns/ -* https://github.com/fcambus/statzone -* https://github.com/benschw/dns-clb-go -* https://github.com/corny/dnscheck for -* https://github.com/miekg/unbound -* https://github.com/miekg/exdns -* https://dnslookup.org -* https://github.com/looterz/grimd -* https://github.com/phamhongviet/serf-dns -* https://github.com/mehrdadrad/mylg -* https://github.com/bamarni/dockness -* https://github.com/fffaraz/microdns -* https://github.com/ipdcode/hades -* https://github.com/StackExchange/dnscontrol/ -* https://www.dnsperf.com/ -* https://dnssectest.net/ -* https://github.com/oif/apex -* https://github.com/jedisct1/dnscrypt-proxy -* https://github.com/jedisct1/rpdns -* https://github.com/xor-gate/sshfp -* https://github.com/rs/dnstrace -* https://blitiri.com.ar/p/dnss ([github mirror](https://github.com/albertito/dnss)) -* https://render.com -* https://github.com/peterzen/goresolver -* https://github.com/folbricht/routedns -* https://domainr.com/ -* https://zonedb.org/ -* https://router7.org/ -* https://github.com/fortio/dnsping -* https://github.com/Luzilla/dnsbl_exporter -* https://github.com/bodgit/tsig -* https://github.com/v2fly/v2ray-core (test only) -* https://kuma.io/ -* https://www.misaka.io/services/dns -* https://ping.sx/dig -* https://fleetdeck.io/ -* https://github.com/markdingo/autoreverse -* https://github.com/slackhq/nebula -* https://addr.tools/ -* https://dnscheck.tools/ -* https://github.com/egbakou/domainverifier -* https://github.com/semihalev/sdns -* https://github.com/wintbiit/NineDNS -* https://linuxcontainers.org/incus/ -* https://ifconfig.es -* https://github.com/zmap/zdns -* https://framagit.org/bortzmeyer/check-soa +- https://github.com/coredns/coredns +- https://github.com/abh/geodns +- https://github.com/baidu/bfe +- http://www.statdns.com/ +- http://www.dnsinspect.com/ +- https://github.com/chuangbo/jianbing-dictionary-dns +- http://www.dns-lg.com/ +- https://github.com/fcambus/rrda +- https://github.com/kenshinx/godns +- https://github.com/skynetservices/skydns +- https://github.com/hashicorp/consul +- https://github.com/DevelopersPL/godnsagent +- https://github.com/duedil-ltd/discodns +- https://github.com/StalkR/dns-reverse-proxy +- https://github.com/tianon/rawdns +- https://mesosphere.github.io/mesos-dns/ +- https://github.com/fcambus/statzone +- https://github.com/benschw/dns-clb-go +- https://github.com/corny/dnscheck for +- https://github.com/miekg/unbound +- https://github.com/miekg/exdns +- https://dnslookup.org +- https://github.com/looterz/grimd +- https://github.com/phamhongviet/serf-dns +- https://github.com/mehrdadrad/mylg +- https://github.com/bamarni/dockness +- https://github.com/fffaraz/microdns +- https://github.com/ipdcode/hades +- https://github.com/StackExchange/dnscontrol/ +- https://www.dnsperf.com/ +- https://dnssectest.net/ +- https://github.com/oif/apex +- https://github.com/jedisct1/dnscrypt-proxy +- https://github.com/jedisct1/rpdns +- https://github.com/xor-gate/sshfp +- https://github.com/rs/dnstrace +- https://blitiri.com.ar/p/dnss ([github mirror](https://github.com/albertito/dnss)) +- https://render.com +- https://github.com/peterzen/goresolver +- https://github.com/folbricht/routedns +- https://domainr.com/ +- https://zonedb.org/ +- https://router7.org/ +- https://github.com/fortio/dnsping +- https://github.com/Luzilla/dnsbl_exporter +- https://github.com/bodgit/tsig +- https://github.com/v2fly/v2ray-core (test only) +- https://kuma.io/ +- https://www.misaka.io/services/dns +- https://ping.sx/dig +- https://fleetdeck.io/ +- https://github.com/markdingo/autoreverse +- https://github.com/slackhq/nebula +- https://addr.tools/ +- https://dnscheck.tools/ +- https://github.com/egbakou/domainverifier +- https://github.com/semihalev/sdns +- https://github.com/wintbiit/NineDNS +- https://linuxcontainers.org/incus/ +- https://ifconfig.es +- https://github.com/zmap/zdns +- https://framagit.org/bortzmeyer/check-soa +- https://github.com/jkerdreux-imt/owns Send pull request if you want to be listed here. # Features -* UDP/TCP queries, IPv4 and IPv6 -* RFC 1035 zone file parsing ($INCLUDE, $ORIGIN, $TTL and $GENERATE (for all record types) are supported -* Fast -* Server side programming (mimicking the net/http package) -* Client side programming -* DNSSEC: signing, validating and key generation for DSA, RSA, ECDSA and Ed25519 -* EDNS0, NSID, Cookies -* AXFR/IXFR -* TSIG, SIG(0) -* DNS over TLS (DoT): encrypted connection between client and server over TCP -* DNS name compression +- UDP/TCP queries, IPv4 and IPv6 +- RFC 1035 zone file parsing ($INCLUDE, $ORIGIN, $TTL and $GENERATE (for all record types) are supported +- Fast +- Server side programming (mimicking the net/http package) +- Client side programming +- DNSSEC: signing, validating and key generation for DSA, RSA, ECDSA and Ed25519 +- EDNS0, NSID, Cookies +- AXFR/IXFR +- TSIG, SIG(0) +- DNS over TLS (DoT): encrypted connection between client and server over TCP +- DNS name compression Have fun! -Miek Gieben - 2010-2012 - +Miek Gieben - 2010-2012 - DNS Authors 2012- # Building @@ -126,81 +132,83 @@ Example programs can be found in the `github.com/miekg/exdns` repository. ## Supported RFCs -*all of them* - -* 103{4,5} - DNS standard -* 1183 - ISDN, X25 and other deprecated records -* 1348 - NSAP record (removed the record) -* 1982 - Serial Arithmetic -* 1876 - LOC record -* 1995 - IXFR -* 1996 - DNS notify -* 2136 - DNS Update (dynamic updates) -* 2181 - RRset definition - there is no RRset type though, just []RR -* 2537 - RSAMD5 DNS keys -* 2065 - DNSSEC (updated in later RFCs) -* 2671 - EDNS record -* 2782 - SRV record -* 2845 - TSIG record -* 2915 - NAPTR record -* 2929 - DNS IANA Considerations -* 3110 - RSASHA1 DNS keys -* 3123 - APL record -* 3225 - DO bit (DNSSEC OK) -* 340{1,2,3} - NAPTR record -* 3445 - Limiting the scope of (DNS)KEY -* 3596 - AAAA record -* 3597 - Unknown RRs -* 4025 - A Method for Storing IPsec Keying Material in DNS -* 403{3,4,5} - DNSSEC + validation functions -* 4255 - SSHFP record -* 4343 - Case insensitivity -* 4408 - SPF record -* 4509 - SHA256 Hash in DS -* 4592 - Wildcards in the DNS -* 4635 - HMAC SHA TSIG -* 4701 - DHCID -* 4892 - id.server -* 5001 - NSID -* 5155 - NSEC3 record -* 5205 - HIP record -* 5702 - SHA2 in the DNS -* 5936 - AXFR -* 5966 - TCP implementation recommendations -* 6605 - ECDSA -* 6725 - IANA Registry Update -* 6742 - ILNP DNS -* 6840 - Clarifications and Implementation Notes for DNS Security -* 6844 - CAA record -* 6891 - EDNS0 update -* 6895 - DNS IANA considerations -* 6944 - DNSSEC DNSKEY Algorithm Status -* 6975 - Algorithm Understanding in DNSSEC -* 7043 - EUI48/EUI64 records -* 7314 - DNS (EDNS) EXPIRE Option -* 7477 - CSYNC RR -* 7828 - edns-tcp-keepalive EDNS0 Option -* 7553 - URI record -* 7858 - DNS over TLS: Initiation and Performance Considerations -* 7871 - EDNS0 Client Subnet -* 7873 - Domain Name System (DNS) Cookies -* 8080 - EdDSA for DNSSEC -* 8490 - DNS Stateful Operations -* 8499 - DNS Terminology -* 8659 - DNS Certification Authority Authorization (CAA) Resource Record -* 8777 - DNS Reverse IP Automatic Multicast Tunneling (AMT) Discovery -* 8914 - Extended DNS Errors -* 8976 - Message Digest for DNS Zones (ZONEMD RR) -* 9460 - Service Binding and Parameter Specification via the DNS -* 9461 - Service Binding Mapping for DNS Servers -* 9462 - Discovery of Designated Resolvers -* 9460 - SVCB and HTTPS Records -* 9606 - DNS Resolver Information -* Draft - Compact Denial of Existence in DNSSEC +_all of them_ + +- 103{4,5} - DNS standard +- 1183 - ISDN, X25 and other deprecated records +- 1348 - NSAP record (removed the record) +- 1982 - Serial Arithmetic +- 1876 - LOC record +- 1995 - IXFR +- 1996 - DNS notify +- 2136 - DNS Update (dynamic updates) +- 2181 - RRset definition - there is no RRset type though, just []RR +- 2537 - RSAMD5 DNS keys +- 2065 - DNSSEC (updated in later RFCs) +- 2671 - EDNS record +- 2782 - SRV record +- 2845 - TSIG record +- 2915 - NAPTR record +- 2929 - DNS IANA Considerations +- 3110 - RSASHA1 DNS keys +- 3123 - APL record +- 3225 - DO bit (DNSSEC OK) +- 340{1,2,3} - NAPTR record +- 3445 - Limiting the scope of (DNS)KEY +- 3596 - AAAA record +- 3597 - Unknown RRs +- 4025 - A Method for Storing IPsec Keying Material in DNS +- 403{3,4,5} - DNSSEC + validation functions +- 4255 - SSHFP record +- 4343 - Case insensitivity +- 4408 - SPF record +- 4509 - SHA256 Hash in DS +- 4592 - Wildcards in the DNS +- 4635 - HMAC SHA TSIG +- 4701 - DHCID +- 4892 - id.server +- 5001 - NSID +- 5155 - NSEC3 record +- 5205 - HIP record +- 5702 - SHA2 in the DNS +- 5936 - AXFR +- 5966 - TCP implementation recommendations +- 6605 - ECDSA +- 6725 - IANA Registry Update +- 6742 - ILNP DNS +- 6840 - Clarifications and Implementation Notes for DNS Security +- 6844 - CAA record +- 6891 - EDNS0 update +- 6895 - DNS IANA considerations +- 6944 - DNSSEC DNSKEY Algorithm Status +- 6975 - Algorithm Understanding in DNSSEC +- 7043 - EUI48/EUI64 records +- 7314 - DNS (EDNS) EXPIRE Option +- 7477 - CSYNC RR +- 7828 - edns-tcp-keepalive EDNS0 Option +- 7553 - URI record +- 7858 - DNS over TLS: Initiation and Performance Considerations +- 7871 - EDNS0 Client Subnet +- 7873 - Domain Name System (DNS) Cookies +- 8080 - EdDSA for DNSSEC +- 8490 - DNS Stateful Operations +- 8499 - DNS Terminology +- 8659 - DNS Certification Authority Authorization (CAA) Resource Record +- 8777 - DNS Reverse IP Automatic Multicast Tunneling (AMT) Discovery +- 8914 - Extended DNS Errors +- 8976 - Message Digest for DNS Zones (ZONEMD RR) +- 9460 - Service Binding and Parameter Specification via the DNS +- 9461 - Service Binding Mapping for DNS Servers +- 9462 - Discovery of Designated Resolvers +- 9460 - SVCB and HTTPS Records +- 9567 - DNS Error Reporting +- 9606 - DNS Resolver Information +- 9660 - DNS Zone Version (ZONEVERSION) Option +- Draft - Compact Denial of Existence in DNSSEC ## Loosely Based Upon -* ldns - -* NSD - -* Net::DNS - -* GRONG - +- ldns - +- NSD - +- Net::DNS - +- GRONG - diff --git a/vendor/github.com/miekg/dns/edns.go b/vendor/github.com/miekg/dns/edns.go index 5c970ca7ed8..89318b75004 100644 --- a/vendor/github.com/miekg/dns/edns.go +++ b/vendor/github.com/miekg/dns/edns.go @@ -24,6 +24,8 @@ const ( EDNS0TCPKEEPALIVE = 0xb // EDNS0 tcp keep alive (See RFC 7828) EDNS0PADDING = 0xc // EDNS0 padding (See RFC 7830) EDNS0EDE = 0xf // EDNS0 extended DNS errors (See RFC 8914) + EDNS0REPORTING = 0x12 // EDNS0 reporting (See RFC 9567) + EDNS0ZONEVERSION = 0x13 // EDNS0 Zone Version (See RFC 9660) EDNS0LOCALSTART = 0xFDE9 // Beginning of range reserved for local/experimental use (See RFC 6891) EDNS0LOCALEND = 0xFFFE // End of range reserved for local/experimental use (See RFC 6891) _DO = 1 << 15 // DNSSEC OK @@ -60,6 +62,10 @@ func makeDataOpt(code uint16) EDNS0 { return new(EDNS0_EDE) case EDNS0ESU: return new(EDNS0_ESU) + case EDNS0REPORTING: + return new(EDNS0_REPORTING) + case EDNS0ZONEVERSION: + return new(EDNS0_ZONEVERSION) default: e := new(EDNS0_LOCAL) e.Code = code @@ -75,17 +81,16 @@ type OPT struct { func (rr *OPT) String() string { s := "\n;; OPT PSEUDOSECTION:\n; EDNS: version " + strconv.Itoa(int(rr.Version())) + "; " + s += "flags:" if rr.Do() { - if rr.Co() { - s += "flags: do, co; " - } else { - s += "flags: do; " - } - } else { - s += "flags:; " + s += " do" } - if rr.Hdr.Ttl&0x7FFF != 0 { - s += fmt.Sprintf("MBZ: 0x%04x, ", rr.Hdr.Ttl&0x7FFF) + if rr.Co() { + s += " co" + } + s += "; " + if z := rr.Z(); z != 0 { + s += fmt.Sprintf("MBZ: 0x%04x, ", z) } s += "udp: " + strconv.Itoa(int(rr.UDPSize())) @@ -127,6 +132,10 @@ func (rr *OPT) String() string { s += "\n; EDE: " + o.String() case *EDNS0_ESU: s += "\n; ESU: " + o.String() + case *EDNS0_REPORTING: + s += "\n; REPORT-CHANNEL: " + o.String() + case *EDNS0_ZONEVERSION: + s += "\n; ZONEVERSION: " + o.String() } } return s @@ -308,10 +317,6 @@ type EDNS0_SUBNET struct { func (e *EDNS0_SUBNET) Option() uint16 { return EDNS0SUBNET } func (e *EDNS0_SUBNET) pack() ([]byte, error) { - b := make([]byte, 4) - binary.BigEndian.PutUint16(b[0:], e.Family) - b[2] = e.SourceNetmask - b[3] = e.SourceScope switch e.Family { case 0: // "dig" sets AddressFamily to 0 if SourceNetmask is also 0 @@ -319,16 +324,27 @@ func (e *EDNS0_SUBNET) pack() ([]byte, error) { if e.SourceNetmask != 0 { return nil, errors.New("bad address family") } + b := make([]byte, 4) + b[3] = e.SourceScope + return b, nil case 1: if e.SourceNetmask > net.IPv4len*8 { return nil, errors.New("bad netmask") } - if len(e.Address.To4()) != net.IPv4len { + ip4 := e.Address.To4() + if len(ip4) != net.IPv4len { return nil, errors.New("bad address") } - ip := e.Address.To4().Mask(net.CIDRMask(int(e.SourceNetmask), net.IPv4len*8)) needLength := (e.SourceNetmask + 8 - 1) / 8 // division rounding up - b = append(b, ip[:needLength]...) + b := make([]byte, 4+needLength) + binary.BigEndian.PutUint16(b[0:], e.Family) + b[2] = e.SourceNetmask + b[3] = e.SourceScope + if needLength > 0 { + ip := ip4.Mask(net.CIDRMask(int(e.SourceNetmask), net.IPv4len*8)) + copy(b[4:], ip[:needLength]) + } + return b, nil case 2: if e.SourceNetmask > net.IPv6len*8 { return nil, errors.New("bad netmask") @@ -336,13 +352,19 @@ func (e *EDNS0_SUBNET) pack() ([]byte, error) { if len(e.Address) != net.IPv6len { return nil, errors.New("bad address") } - ip := e.Address.Mask(net.CIDRMask(int(e.SourceNetmask), net.IPv6len*8)) needLength := (e.SourceNetmask + 8 - 1) / 8 // division rounding up - b = append(b, ip[:needLength]...) + b := make([]byte, 4+needLength) + binary.BigEndian.PutUint16(b[0:], e.Family) + b[2] = e.SourceNetmask + b[3] = e.SourceScope + if needLength > 0 { + ip := e.Address.Mask(net.CIDRMask(int(e.SourceNetmask), net.IPv6len*8)) + copy(b[4:], ip[:needLength]) + } + return b, nil default: return nil, errors.New("bad address family") } - return b, nil } func (e *EDNS0_SUBNET) unpack(b []byte) error { @@ -875,3 +897,74 @@ func (e *EDNS0_ESU) unpack(b []byte) error { e.Uri = string(b) return nil } + +// EDNS0_REPORTING implements the EDNS0 Reporting Channel option (RFC 9567). +type EDNS0_REPORTING struct { + Code uint16 // always EDNS0REPORTING + AgentDomain string +} + +func (e *EDNS0_REPORTING) Option() uint16 { return EDNS0REPORTING } +func (e *EDNS0_REPORTING) String() string { return e.AgentDomain } +func (e *EDNS0_REPORTING) copy() EDNS0 { return &EDNS0_REPORTING{e.Code, e.AgentDomain} } +func (e *EDNS0_REPORTING) pack() ([]byte, error) { + b := make([]byte, 255) + off1, err := PackDomainName(Fqdn(e.AgentDomain), b, 0, nil, false) + if err != nil { + return nil, fmt.Errorf("bad agent domain: %w", err) + } + return b[:off1], nil +} +func (e *EDNS0_REPORTING) unpack(b []byte) error { + domain, _, err := UnpackDomainName(b, 0) + if err != nil { + return fmt.Errorf("bad agent domain: %w", err) + } + e.AgentDomain = domain + return nil +} + +// EDNS0_ZONEVERSION implements the EDNS0 Zone Version option (RFC 9660). +type EDNS0_ZONEVERSION struct { + // always EDNS0ZONEVERSION (19) + Code uint16 + // An unsigned 1-octet Label Count indicating + // the number of labels for the name of the zone that VERSION value refers to. + LabelCount uint8 + // An unsigned 1-octet type number distinguishing the format and meaning of version. + // 0 SOA-SERIAL, 1-245 Unassigned, 246-255 Reserved for private use, see RFC 9660. + Type uint8 + // An opaque octet string conveying the zone version data (VERSION). + Version string +} + +func (e *EDNS0_ZONEVERSION) Option() uint16 { return EDNS0ZONEVERSION } +func (e *EDNS0_ZONEVERSION) String() string { return e.Version } +func (e *EDNS0_ZONEVERSION) copy() EDNS0 { + return &EDNS0_ZONEVERSION{e.Code, e.LabelCount, e.Type, e.Version} +} +func (e *EDNS0_ZONEVERSION) pack() ([]byte, error) { + b := []byte{ + // first octet label count + e.LabelCount, + // second octet is type + e.Type, + } + if len(e.Version) > 0 { + b = append(b, []byte(e.Version)...) + } + return b, nil +} +func (e *EDNS0_ZONEVERSION) unpack(b []byte) error { + if len(b) < 2 { + return ErrBuf + } + e.LabelCount = b[0] + e.Type = b[1] + if len(b) > 2 { + e.Version = string(b[2:]) + } else { + e.Version = "" + } + return nil +} diff --git a/vendor/github.com/miekg/dns/server.go b/vendor/github.com/miekg/dns/server.go index 364149cfcea..50478b32409 100644 --- a/vendor/github.com/miekg/dns/server.go +++ b/vendor/github.com/miekg/dns/server.go @@ -194,7 +194,9 @@ type DecorateWriter func(Writer) Writer // rejected (or ignored) by the MsgAcceptFunc, or passed to this function. type MsgInvalidFunc func(m []byte, err error) -func DefaultMsgInvalidFunc(m []byte, err error) {} +var DefaultMsgInvalidFunc MsgInvalidFunc = defaultMsgInvalidFunc + +func defaultMsgInvalidFunc(m []byte, err error) {} // A Server defines parameters for running an DNS server. type Server struct { diff --git a/vendor/github.com/miekg/dns/version.go b/vendor/github.com/miekg/dns/version.go index 4af7d286c7f..c53a63d7c8b 100644 --- a/vendor/github.com/miekg/dns/version.go +++ b/vendor/github.com/miekg/dns/version.go @@ -3,7 +3,7 @@ package dns import "fmt" // Version is current version of this library. -var Version = v{1, 1, 68} +var Version = v{1, 1, 69} // v holds the version of this library. type v struct { diff --git a/vendor/github.com/prometheus/prometheus/discovery/discovery.go b/vendor/github.com/prometheus/prometheus/discovery/discovery.go index 2157b820b97..e643cb10af1 100644 --- a/vendor/github.com/prometheus/prometheus/discovery/discovery.go +++ b/vendor/github.com/prometheus/prometheus/discovery/discovery.go @@ -54,19 +54,23 @@ type DiscovererOptions struct { // Extra HTTP client options to expose to Discoverers. This field may be // ignored; Discoverer implementations must opt-in to reading it. HTTPClientOptions []config.HTTPClientOption + + // SetName identifies this discoverer set. + SetName string } // RefreshMetrics are used by the "refresh" package. // We define them here in the "discovery" package in order to avoid a cyclic dependency between // "discovery" and "refresh". type RefreshMetrics struct { - Failures prometheus.Counter - Duration prometheus.Observer + Failures prometheus.Counter + Duration prometheus.Observer + DurationHistogram prometheus.Observer } // RefreshMetricsInstantiator instantiates the metrics used by the "refresh" package. type RefreshMetricsInstantiator interface { - Instantiate(mech string) *RefreshMetrics + Instantiate(mech, setName string) *RefreshMetrics } // RefreshMetricsManager is an interface for registering, unregistering, and diff --git a/vendor/github.com/prometheus/prometheus/discovery/dns/dns.go b/vendor/github.com/prometheus/prometheus/discovery/dns/dns.go index 24af8f65d96..1e0a78698ba 100644 --- a/vendor/github.com/prometheus/prometheus/discovery/dns/dns.go +++ b/vendor/github.com/prometheus/prometheus/discovery/dns/dns.go @@ -78,7 +78,7 @@ func (*SDConfig) Name() string { return "dns" } // NewDiscoverer returns a Discoverer for the Config. func (c *SDConfig) NewDiscoverer(opts discovery.DiscovererOptions) (discovery.Discoverer, error) { - return NewDiscovery(*c, opts.Logger, opts.Metrics) + return NewDiscovery(*c, opts) } // UnmarshalYAML implements the yaml.Unmarshaler interface. @@ -118,14 +118,14 @@ type Discovery struct { } // NewDiscovery returns a new Discovery which periodically refreshes its targets. -func NewDiscovery(conf SDConfig, logger *slog.Logger, metrics discovery.DiscovererMetrics) (*Discovery, error) { - m, ok := metrics.(*dnsMetrics) +func NewDiscovery(conf SDConfig, opts discovery.DiscovererOptions) (*Discovery, error) { + m, ok := opts.Metrics.(*dnsMetrics) if !ok { return nil, errors.New("invalid discovery metrics type") } - if logger == nil { - logger = promslog.NewNopLogger() + if opts.Logger == nil { + opts.Logger = promslog.NewNopLogger() } qtype := dns.TypeSRV @@ -145,15 +145,16 @@ func NewDiscovery(conf SDConfig, logger *slog.Logger, metrics discovery.Discover names: conf.Names, qtype: qtype, port: conf.Port, - logger: logger, + logger: opts.Logger, lookupFn: lookupWithSearchPath, metrics: m, } d.Discovery = refresh.NewDiscovery( refresh.Options{ - Logger: logger, + Logger: opts.Logger, Mech: "dns", + SetName: opts.SetName, Interval: time.Duration(conf.RefreshInterval), RefreshF: d.refresh, MetricsInstantiator: m.refreshMetrics, diff --git a/vendor/github.com/prometheus/prometheus/discovery/manager.go b/vendor/github.com/prometheus/prometheus/discovery/manager.go index 6688152da98..431050aa0b7 100644 --- a/vendor/github.com/prometheus/prometheus/discovery/manager.go +++ b/vendor/github.com/prometheus/prometheus/discovery/manager.go @@ -27,6 +27,7 @@ import ( "github.com/prometheus/common/promslog" "github.com/prometheus/prometheus/discovery/targetgroup" + "github.com/prometheus/prometheus/util/features" ) type poolKey struct { @@ -111,6 +112,13 @@ func NewManager(ctx context.Context, logger *slog.Logger, registerer prometheus. } mgr.metrics = metrics + // Register all available service discovery providers with the feature registry. + if mgr.featureRegistry != nil { + for _, sdName := range RegisteredConfigNames() { + mgr.featureRegistry.Enable(features.ServiceDiscoveryProviders, sdName) + } + } + return mgr } @@ -141,6 +149,15 @@ func HTTPClientOptions(opts ...config.HTTPClientOption) func(*Manager) { } } +// FeatureRegistry sets the feature registry for the manager. +func FeatureRegistry(fr features.Collector) func(*Manager) { + return func(m *Manager) { + m.mtx.Lock() + defer m.mtx.Unlock() + m.featureRegistry = fr + } +} + // Manager maintains a set of discovery providers and sends each update to a map channel. // Targets are grouped by the target set name. type Manager struct { @@ -175,6 +192,9 @@ type Manager struct { metrics *Metrics sdMetrics map[string]DiscovererMetrics + + // featureRegistry is used to track which service discovery providers are configured. + featureRegistry features.Collector } // Providers returns the currently configured SD providers. @@ -479,6 +499,7 @@ func (m *Manager) registerProviders(cfgs Configs, setName string) int { Logger: m.logger.With("discovery", typ, "config", setName), HTTPClientOptions: m.httpOpts, Metrics: m.sdMetrics[typ], + SetName: setName, }) if err != nil { m.logger.Error("Cannot create service discovery", "err", err, "type", typ, "config", setName) diff --git a/vendor/github.com/prometheus/prometheus/discovery/metrics_refresh.go b/vendor/github.com/prometheus/prometheus/discovery/metrics_refresh.go index ef49e591a35..9f3eb27b49b 100644 --- a/vendor/github.com/prometheus/prometheus/discovery/metrics_refresh.go +++ b/vendor/github.com/prometheus/prometheus/discovery/metrics_refresh.go @@ -14,6 +14,8 @@ package discovery import ( + "time" + "github.com/prometheus/client_golang/prometheus" ) @@ -21,8 +23,9 @@ import ( // We define them here in the "discovery" package in order to avoid a cyclic dependency between // "discovery" and "refresh". type RefreshMetricsVecs struct { - failuresVec *prometheus.CounterVec - durationVec *prometheus.SummaryVec + failuresVec *prometheus.CounterVec + durationVec *prometheus.SummaryVec + durationHistVec *prometheus.HistogramVec metricRegisterer MetricRegisterer } @@ -36,13 +39,23 @@ func NewRefreshMetrics(reg prometheus.Registerer) RefreshMetricsManager { Name: "prometheus_sd_refresh_failures_total", Help: "Number of refresh failures for the given SD mechanism.", }, - []string{"mechanism"}), + []string{"mechanism", "config"}), durationVec: prometheus.NewSummaryVec( prometheus.SummaryOpts{ Name: "prometheus_sd_refresh_duration_seconds", Help: "The duration of a refresh in seconds for the given SD mechanism.", Objectives: map[float64]float64{0.5: 0.05, 0.9: 0.01, 0.99: 0.001}, }, + []string{"mechanism", "config"}), + durationHistVec: prometheus.NewHistogramVec( + prometheus.HistogramOpts{ + Name: "prometheus_sd_refresh_duration_histogram_seconds", + Help: "The duration of a refresh for the given SD mechanism.", + Buckets: []float64{.01, .1, 1, 10}, + NativeHistogramBucketFactor: 1.1, + NativeHistogramMaxBucketNumber: 100, + NativeHistogramMinResetDuration: 1 * time.Hour, + }, []string{"mechanism"}), } @@ -51,16 +64,18 @@ func NewRefreshMetrics(reg prometheus.Registerer) RefreshMetricsManager { m.metricRegisterer = NewMetricRegisterer(reg, []prometheus.Collector{ m.failuresVec, m.durationVec, + m.durationHistVec, }) return m } -// Instantiate returns metrics out of metric vectors. -func (m *RefreshMetricsVecs) Instantiate(mech string) *RefreshMetrics { +// Instantiate returns metrics out of metric vectors for a given mechanism and config. +func (m *RefreshMetricsVecs) Instantiate(mech, config string) *RefreshMetrics { return &RefreshMetrics{ - Failures: m.failuresVec.WithLabelValues(mech), - Duration: m.durationVec.WithLabelValues(mech), + Failures: m.failuresVec.WithLabelValues(mech, config), + Duration: m.durationVec.WithLabelValues(mech, config), + DurationHistogram: m.durationHistVec.WithLabelValues(mech), } } diff --git a/vendor/github.com/prometheus/prometheus/discovery/refresh/refresh.go b/vendor/github.com/prometheus/prometheus/discovery/refresh/refresh.go index 31646c0e4c1..0613fd6c6d6 100644 --- a/vendor/github.com/prometheus/prometheus/discovery/refresh/refresh.go +++ b/vendor/github.com/prometheus/prometheus/discovery/refresh/refresh.go @@ -28,6 +28,7 @@ import ( type Options struct { Logger *slog.Logger Mech string + SetName string Interval time.Duration RefreshF func(ctx context.Context) ([]*targetgroup.Group, error) MetricsInstantiator discovery.RefreshMetricsInstantiator @@ -43,7 +44,7 @@ type Discovery struct { // NewDiscovery returns a Discoverer function that calls a refresh() function at every interval. func NewDiscovery(opts Options) *Discovery { - m := opts.MetricsInstantiator.Instantiate(opts.Mech) + m := opts.MetricsInstantiator.Instantiate(opts.Mech, opts.SetName) var logger *slog.Logger if opts.Logger == nil { @@ -107,6 +108,7 @@ func (d *Discovery) refresh(ctx context.Context) ([]*targetgroup.Group, error) { now := time.Now() defer func() { d.metrics.Duration.Observe(time.Since(now).Seconds()) + d.metrics.DurationHistogram.Observe(time.Since(now).Seconds()) }() tgs, err := d.refreshf(ctx) diff --git a/vendor/github.com/prometheus/prometheus/discovery/registry.go b/vendor/github.com/prometheus/prometheus/discovery/registry.go index 33938cef3e7..b3b82cdeecc 100644 --- a/vendor/github.com/prometheus/prometheus/discovery/registry.go +++ b/vendor/github.com/prometheus/prometheus/discovery/registry.go @@ -280,3 +280,13 @@ func RegisterSDMetrics(registerer prometheus.Registerer, rmm RefreshMetricsManag } return metrics, nil } + +// RegisteredConfigNames returns the names of all registered service discovery providers. +func RegisteredConfigNames() []string { + names := make([]string, 0, len(configNames)) + for name := range configNames { + names = append(names, name) + } + sort.Strings(names) + return names +} diff --git a/vendor/github.com/prometheus/prometheus/model/histogram/float_histogram.go b/vendor/github.com/prometheus/prometheus/model/histogram/float_histogram.go index c607448f38e..91fcac1cfbf 100644 --- a/vendor/github.com/prometheus/prometheus/model/histogram/float_histogram.go +++ b/vendor/github.com/prometheus/prometheus/model/histogram/float_histogram.go @@ -164,8 +164,8 @@ func (h *FloatHistogram) CopyToSchema(targetSchema int32) *FloatHistogram { Sum: h.Sum, } - c.PositiveSpans, c.PositiveBuckets = reduceResolution(h.PositiveSpans, h.PositiveBuckets, h.Schema, targetSchema, false, false) - c.NegativeSpans, c.NegativeBuckets = reduceResolution(h.NegativeSpans, h.NegativeBuckets, h.Schema, targetSchema, false, false) + c.PositiveSpans, c.PositiveBuckets = mustReduceResolution(h.PositiveSpans, h.PositiveBuckets, h.Schema, targetSchema, false, false) + c.NegativeSpans, c.NegativeBuckets = mustReduceResolution(h.NegativeSpans, h.NegativeBuckets, h.Schema, targetSchema, false, false) return &c } @@ -393,13 +393,13 @@ func (h *FloatHistogram) Add(other *FloatHistogram) (res *FloatHistogram, counte switch { case other.Schema < h.Schema: - hPositiveSpans, hPositiveBuckets = reduceResolution(hPositiveSpans, hPositiveBuckets, h.Schema, other.Schema, false, true) - hNegativeSpans, hNegativeBuckets = reduceResolution(hNegativeSpans, hNegativeBuckets, h.Schema, other.Schema, false, true) + hPositiveSpans, hPositiveBuckets = mustReduceResolution(hPositiveSpans, hPositiveBuckets, h.Schema, other.Schema, false, true) + hNegativeSpans, hNegativeBuckets = mustReduceResolution(hNegativeSpans, hNegativeBuckets, h.Schema, other.Schema, false, true) h.Schema = other.Schema case other.Schema > h.Schema: - otherPositiveSpans, otherPositiveBuckets = reduceResolution(otherPositiveSpans, otherPositiveBuckets, other.Schema, h.Schema, false, false) - otherNegativeSpans, otherNegativeBuckets = reduceResolution(otherNegativeSpans, otherNegativeBuckets, other.Schema, h.Schema, false, false) + otherPositiveSpans, otherPositiveBuckets = mustReduceResolution(otherPositiveSpans, otherPositiveBuckets, other.Schema, h.Schema, false, false) + otherNegativeSpans, otherNegativeBuckets = mustReduceResolution(otherNegativeSpans, otherNegativeBuckets, other.Schema, h.Schema, false, false) } h.PositiveSpans, h.PositiveBuckets = addBuckets(h.Schema, h.ZeroThreshold, false, hPositiveSpans, hPositiveBuckets, otherPositiveSpans, otherPositiveBuckets) @@ -459,12 +459,12 @@ func (h *FloatHistogram) Sub(other *FloatHistogram) (res *FloatHistogram, counte switch { case other.Schema < h.Schema: - hPositiveSpans, hPositiveBuckets = reduceResolution(hPositiveSpans, hPositiveBuckets, h.Schema, other.Schema, false, true) - hNegativeSpans, hNegativeBuckets = reduceResolution(hNegativeSpans, hNegativeBuckets, h.Schema, other.Schema, false, true) + hPositiveSpans, hPositiveBuckets = mustReduceResolution(hPositiveSpans, hPositiveBuckets, h.Schema, other.Schema, false, true) + hNegativeSpans, hNegativeBuckets = mustReduceResolution(hNegativeSpans, hNegativeBuckets, h.Schema, other.Schema, false, true) h.Schema = other.Schema case other.Schema > h.Schema: - otherPositiveSpans, otherPositiveBuckets = reduceResolution(otherPositiveSpans, otherPositiveBuckets, other.Schema, h.Schema, false, false) - otherNegativeSpans, otherNegativeBuckets = reduceResolution(otherNegativeSpans, otherNegativeBuckets, other.Schema, h.Schema, false, false) + otherPositiveSpans, otherPositiveBuckets = mustReduceResolution(otherPositiveSpans, otherPositiveBuckets, other.Schema, h.Schema, false, false) + otherNegativeSpans, otherNegativeBuckets = mustReduceResolution(otherNegativeSpans, otherNegativeBuckets, other.Schema, h.Schema, false, false) } h.PositiveSpans, h.PositiveBuckets = addBuckets(h.Schema, h.ZeroThreshold, true, hPositiveSpans, hPositiveBuckets, otherPositiveSpans, otherPositiveBuckets) @@ -1050,16 +1050,21 @@ func (i *floatBucketIterator) Next() bool { if i.spansIdx >= len(i.spans) { return false } + span := i.spans[i.spansIdx] if i.schema == i.targetSchema { // Fast path for the common case. - span := i.spans[i.spansIdx] if i.bucketsIdx == 0 { // Seed origIdx for the first bucket. i.currIdx = span.Offset } else { i.currIdx++ } + if i.bucketsIdx >= len(i.buckets) { + // This protects against index out of range panic, which + // can only happen with an invalid histogram. + return false + } for i.idxInSpan >= span.Length { // We have exhausted the current span and have to find a new @@ -1080,7 +1085,6 @@ func (i *floatBucketIterator) Next() bool { // Copy all of these into local variables so that we can forward to the // next bucket and then roll back if needed. origIdx, spansIdx, idxInSpan := i.origIdx, i.spansIdx, i.idxInSpan - span := i.spans[spansIdx] firstPass := true i.currCount = 0 @@ -1092,6 +1096,14 @@ func (i *floatBucketIterator) Next() bool { } else { origIdx++ } + if i.bucketsIdx >= len(i.buckets) { + // This protects against index out of range panic, which + // can only happen with an invalid histogram. + if firstPass { + return false + } + break mergeLoop + } for idxInSpan >= span.Length { // We have exhausted the current span and have to find a new // one. We even handle pathologic spans of length 0 here. @@ -1152,6 +1164,11 @@ func (i *reverseFloatBucketIterator) Next() bool { // We have exhausted the current span and have to find a new // one. We'll even handle pathologic spans of length 0. i.spansIdx-- + if i.spansIdx < 0 { + // This protects against index out of range panic, which + // can only happen with an invalid histogram. + return false + } i.idxInSpan = int32(i.spans[i.spansIdx].Length) - 1 i.currIdx -= i.spans[i.spansIdx+1].Offset } @@ -1565,25 +1582,40 @@ func addCustomBucketsWithMismatches( } // ReduceResolution reduces the float histogram's spans, buckets into target schema. -// The target schema must be smaller than the current float histogram's schema. -// This will panic if the histogram has custom buckets or if the target schema is -// a custom buckets schema. -func (h *FloatHistogram) ReduceResolution(targetSchema int32) *FloatHistogram { +// An error is returned in the following cases: +// - The target schema is not smaller than the current histogram's schema. +// - The histogram has custom buckets. +// - The target schema is a custom buckets schema. +// - Any spans have an invalid offset. +// - The spans are inconsistent with the number of buckets. +func (h *FloatHistogram) ReduceResolution(targetSchema int32) error { + // Note that the follow three returns are not returning a + // histogram.Error because they are programming errors. if h.UsesCustomBuckets() { - panic("cannot reduce resolution when there are custom buckets") + return errors.New("cannot reduce resolution when there are custom buckets") } if IsCustomBucketsSchema(targetSchema) { - panic("cannot reduce resolution to custom buckets schema") + return errors.New("cannot reduce resolution to custom buckets schema") } if targetSchema >= h.Schema { - panic(fmt.Errorf("cannot reduce resolution from schema %d to %d", h.Schema, targetSchema)) + return fmt.Errorf("cannot reduce resolution from schema %d to %d", h.Schema, targetSchema) } - h.PositiveSpans, h.PositiveBuckets = reduceResolution(h.PositiveSpans, h.PositiveBuckets, h.Schema, targetSchema, false, true) - h.NegativeSpans, h.NegativeBuckets = reduceResolution(h.NegativeSpans, h.NegativeBuckets, h.Schema, targetSchema, false, true) + var err error + + if h.PositiveSpans, h.PositiveBuckets, err = reduceResolution( + h.PositiveSpans, h.PositiveBuckets, h.Schema, targetSchema, false, true, + ); err != nil { + return err + } + if h.NegativeSpans, h.NegativeBuckets, err = reduceResolution( + h.NegativeSpans, h.NegativeBuckets, h.Schema, targetSchema, false, true, + ); err != nil { + return err + } h.Schema = targetSchema - return h + return nil } // checkSchemaAndBounds checks if two histograms are compatible because they diff --git a/vendor/github.com/prometheus/prometheus/model/histogram/generic.go b/vendor/github.com/prometheus/prometheus/model/histogram/generic.go index cd385407d53..649db769c7e 100644 --- a/vendor/github.com/prometheus/prometheus/model/histogram/generic.go +++ b/vendor/github.com/prometheus/prometheus/model/histogram/generic.go @@ -738,6 +738,8 @@ var exponentialBounds = [][]float64{ // deltas. Set it to false if the buckets contain absolute counts. // Set inplace to true to reuse input slices and avoid allocations (otherwise // new slices will be allocated for result). +// The functions returns an error if there are too many or too few buckets for the spans +// or if any span except the first has a negative offset. func reduceResolution[IBC InternalBucketCount]( originSpans []Span, originBuckets []IBC, @@ -745,7 +747,7 @@ func reduceResolution[IBC InternalBucketCount]( targetSchema int32, deltaBuckets bool, inplace bool, -) ([]Span, []IBC) { +) ([]Span, []IBC, error) { var ( targetSpans []Span // The spans in the target schema. targetBuckets []IBC // The bucket counts in the target schema. @@ -764,10 +766,18 @@ func reduceResolution[IBC InternalBucketCount]( targetBuckets = originBuckets[:0] } - for _, span := range originSpans { + for n, span := range originSpans { + if n > 0 && span.Offset < 0 { + return nil, nil, fmt.Errorf("span number %d with offset %d: %w", n+1, span.Offset, ErrHistogramSpanNegativeOffset) + } // Determine the index of the first bucket in this span. bucketIdx += span.Offset for j := 0; j < int(span.Length); j++ { + // Protect against too few buckets in the origin. + if bucketCountIdx >= len(originBuckets) { + return nil, nil, fmt.Errorf("have %d buckets but spans need more: %w", len(originBuckets), ErrHistogramSpansBucketsMismatch) + } + // Determine the index of the bucket in the target schema from the index in the original schema. targetBucketIdx = targetIdx(bucketIdx, originSchema, targetSchema) @@ -826,12 +836,33 @@ func reduceResolution[IBC InternalBucketCount]( targetBuckets = append(targetBuckets, originBuckets[bucketCountIdx]) } } - bucketIdx++ bucketCountIdx++ } } + if bucketCountIdx != len(originBuckets) { + return nil, nil, fmt.Errorf("spans need %d buckets, have %d buckets: %w", bucketCountIdx, len(originBuckets), ErrHistogramSpansBucketsMismatch) + } + return targetSpans, targetBuckets, nil +} +// mustReduceResolution works like reduceResolution, but panics instead of +// returning an error. Use mustReduceResolution if you are sure that the spans +// and buckets are valid. +func mustReduceResolution[IBC InternalBucketCount]( + originSpans []Span, + originBuckets []IBC, + originSchema, + targetSchema int32, + deltaBuckets bool, + inplace bool, +) ([]Span, []IBC) { + targetSpans, targetBuckets, err := reduceResolution( + originSpans, originBuckets, originSchema, targetSchema, deltaBuckets, inplace, + ) + if err != nil { + panic(err) + } return targetSpans, targetBuckets } diff --git a/vendor/github.com/prometheus/prometheus/model/histogram/histogram.go b/vendor/github.com/prometheus/prometheus/model/histogram/histogram.go index a7d9ce80f01..5fc68ef9d02 100644 --- a/vendor/github.com/prometheus/prometheus/model/histogram/histogram.go +++ b/vendor/github.com/prometheus/prometheus/model/histogram/histogram.go @@ -14,6 +14,7 @@ package histogram import ( + "errors" "fmt" "math" "slices" @@ -515,6 +516,11 @@ func (r *regularBucketIterator) Next() bool { r.currIdx += span.Offset } + // This protects against index out of range panic, which + // can only happen with an invalid histogram. + if r.bucketsIdx >= len(r.buckets) { + return false + } r.currCount += r.buckets[r.bucketsIdx] r.idxInSpan++ r.bucketsIdx++ @@ -576,6 +582,11 @@ func (c *cumulativeBucketIterator) Next() bool { c.initialized = true } + // This protects against index out of range panic, which + // can only happen with an invalid histogram. + if c.posBucketsIdx >= len(c.h.PositiveBuckets) { + return false + } c.currCount += c.h.PositiveBuckets[c.posBucketsIdx] c.currCumulativeCount += uint64(c.currCount) c.currUpper = getBound(c.currIdx, c.h.Schema, c.h.CustomValues) @@ -607,26 +618,37 @@ func (c *cumulativeBucketIterator) At() Bucket[uint64] { } // ReduceResolution reduces the histogram's spans, buckets into target schema. -// The target schema must be smaller than the current histogram's schema. -// This will panic if the histogram has custom buckets or if the target schema is -// a custom buckets schema. -func (h *Histogram) ReduceResolution(targetSchema int32) *Histogram { +// An error is returned in the following cases: +// - The target schema is not smaller than the current histogram's schema. +// - The histogram has custom buckets. +// - The target schema is a custom buckets schema. +// - Any spans have an invalid offset. +// - The spans are inconsistent with the number of buckets. +func (h *Histogram) ReduceResolution(targetSchema int32) error { + // Note that the follow three returns are not returning a + // histogram.Error because they are programming errors. if h.UsesCustomBuckets() { - panic("cannot reduce resolution when there are custom buckets") + return errors.New("cannot reduce resolution when there are custom buckets") } if IsCustomBucketsSchema(targetSchema) { - panic("cannot reduce resolution to custom buckets schema") + return errors.New("cannot reduce resolution to custom buckets schema") } if targetSchema >= h.Schema { - panic(fmt.Errorf("cannot reduce resolution from schema %d to %d", h.Schema, targetSchema)) + return fmt.Errorf("cannot reduce resolution from schema %d to %d", h.Schema, targetSchema) } - h.PositiveSpans, h.PositiveBuckets = reduceResolution( + var err error + + if h.PositiveSpans, h.PositiveBuckets, err = reduceResolution( h.PositiveSpans, h.PositiveBuckets, h.Schema, targetSchema, true, true, - ) - h.NegativeSpans, h.NegativeBuckets = reduceResolution( + ); err != nil { + return err + } + if h.NegativeSpans, h.NegativeBuckets, err = reduceResolution( h.NegativeSpans, h.NegativeBuckets, h.Schema, targetSchema, true, true, - ) + ); err != nil { + return err + } h.Schema = targetSchema - return h + return nil } diff --git a/vendor/github.com/prometheus/prometheus/model/labels/labels_common.go b/vendor/github.com/prometheus/prometheus/model/labels/labels_common.go index 5a3979784cb..ab82ae6a8fc 100644 --- a/vendor/github.com/prometheus/prometheus/model/labels/labels_common.go +++ b/vendor/github.com/prometheus/prometheus/model/labels/labels_common.go @@ -26,7 +26,9 @@ import ( const ( // MetricName is a special label name that represent a metric name. // - // Deprecated: Use schema.Metadata structure and its methods. + // Deprecated: Instead, consider using schema.Metadata structure and its methods for consistent metadata behaviour with the newly added __type__ and __unit__ labels. Alternatively use github.com/prometheus/common/model.MetricNameLabel for the direct replacement. + // + // labels package is providing label transport, agnostic to semantic meaning of each label. MetricName = "__name__" AlertName = "alertname" diff --git a/vendor/github.com/prometheus/prometheus/model/labels/labels_dedupelabels.go b/vendor/github.com/prometheus/prometheus/model/labels/labels_dedupelabels.go index 1e736c832ea..4518482c968 100644 --- a/vendor/github.com/prometheus/prometheus/model/labels/labels_dedupelabels.go +++ b/vendor/github.com/prometheus/prometheus/model/labels/labels_dedupelabels.go @@ -24,6 +24,9 @@ import ( "github.com/cespare/xxhash/v2" ) +// ImplementationName is the name of the labels implementation. +const ImplementationName = "dedupelabels" + // Labels is implemented by a SymbolTable and string holding name/value // pairs encoded as indexes into the table in varint encoding. // Names are in alphabetical order. diff --git a/vendor/github.com/prometheus/prometheus/model/labels/labels_slicelabels.go b/vendor/github.com/prometheus/prometheus/model/labels/labels_slicelabels.go index 21ad145c1cc..71dbcd00449 100644 --- a/vendor/github.com/prometheus/prometheus/model/labels/labels_slicelabels.go +++ b/vendor/github.com/prometheus/prometheus/model/labels/labels_slicelabels.go @@ -25,6 +25,9 @@ import ( "github.com/cespare/xxhash/v2" ) +// ImplementationName is the name of the labels implementation. +const ImplementationName = "slicelabels" + // Labels is a sorted set of labels. Order has to be guaranteed upon // instantiation. type Labels []Label @@ -297,12 +300,9 @@ func FromStrings(ss ...string) Labels { // Compare compares the two label sets. // The result will be 0 if a==b, <0 if a < b, and >0 if a > b. func Compare(a, b Labels) int { - l := len(a) - if len(b) < l { - l = len(b) - } + l := min(len(b), len(a)) - for i := 0; i < l; i++ { + for i := range l { if a[i].Name != b[i].Name { if a[i].Name < b[i].Name { return -1 @@ -419,10 +419,7 @@ func (b *Builder) Labels() Labels { return b.base } - expectedSize := len(b.base) + len(b.add) - len(b.del) - if expectedSize < 1 { - expectedSize = 1 - } + expectedSize := max(len(b.base)+len(b.add)-len(b.del), 1) res := make(Labels, 0, expectedSize) for _, l := range b.base { if slices.Contains(b.del, l.Name) || contains(b.add, l.Name) { diff --git a/vendor/github.com/prometheus/prometheus/model/labels/labels_stringlabels.go b/vendor/github.com/prometheus/prometheus/model/labels/labels_stringlabels.go index f0872238029..1460e7db932 100644 --- a/vendor/github.com/prometheus/prometheus/model/labels/labels_stringlabels.go +++ b/vendor/github.com/prometheus/prometheus/model/labels/labels_stringlabels.go @@ -23,6 +23,9 @@ import ( "github.com/cespare/xxhash/v2" ) +// ImplementationName is the name of the labels implementation. +const ImplementationName = "stringlabels" + // Labels is implemented by a single flat string holding name/value pairs. // Each name and value is preceded by its length, encoded as a single byte // for size 0-254, or the following 3 bytes little-endian, if the first byte is 255. diff --git a/vendor/github.com/prometheus/prometheus/model/metadata/metadata.go b/vendor/github.com/prometheus/prometheus/model/metadata/metadata.go index 1b7e63e0f35..d2a91bb5603 100644 --- a/vendor/github.com/prometheus/prometheus/model/metadata/metadata.go +++ b/vendor/github.com/prometheus/prometheus/model/metadata/metadata.go @@ -1,4 +1,4 @@ -// Copyright 2022 The Prometheus Authors +// Copyright The Prometheus Authors // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at @@ -13,7 +13,11 @@ package metadata -import "github.com/prometheus/common/model" +import ( + "strings" + + "github.com/prometheus/common/model" +) // Metadata stores a series' metadata information. type Metadata struct { @@ -21,3 +25,21 @@ type Metadata struct { Unit string `json:"unit"` Help string `json:"help"` } + +// IsEmpty returns true if metadata structure is empty, including unknown type case. +func (m Metadata) IsEmpty() bool { + return (m.Type == "" || m.Type == model.MetricTypeUnknown) && m.Unit == "" && m.Help == "" +} + +// Equals returns true if m is semantically the same as other metadata. +func (m Metadata) Equals(other Metadata) bool { + if strings.Compare(m.Unit, other.Unit) != 0 || strings.Compare(m.Help, other.Help) != 0 { + return false + } + + // Unknown means the same as empty string. + if m.Type == "" || m.Type == model.MetricTypeUnknown { + return other.Type == "" || other.Type == model.MetricTypeUnknown + } + return m.Type == other.Type +} diff --git a/vendor/github.com/prometheus/prometheus/model/textparse/interface.go b/vendor/github.com/prometheus/prometheus/model/textparse/interface.go index 37b1b761a03..bbc52290ade 100644 --- a/vendor/github.com/prometheus/prometheus/model/textparse/interface.go +++ b/vendor/github.com/prometheus/prometheus/model/textparse/interface.go @@ -29,7 +29,7 @@ import ( type Parser interface { // Series returns the bytes of a series with a simple float64 as a // value, the timestamp if set, and the value of the current sample. - // TODO(bwplotka): Similar to CreatedTimestamp, have ts == 0 meaning no timestamp provided. + // TODO(bwplotka): Similar to StartTimestamp, have ts == 0 meaning no timestamp provided. // We already accepted in many places (PRW, proto parsing histograms) that 0 timestamp is not a // a valid timestamp. If needed it can be represented as 0+1ms. Series() ([]byte, *int64, float64) @@ -38,7 +38,7 @@ type Parser interface { // value, the timestamp if set, and the histogram in the current sample. // Depending on the parsed input, the function returns an (integer) Histogram // or a FloatHistogram, with the respective other return value being nil. - // TODO(bwplotka): Similar to CreatedTimestamp, have ts == 0 meaning no timestamp provided. + // TODO(bwplotka): Similar to StartTimestamp, have ts == 0 meaning no timestamp provided. // We already accepted in many places (PRW, proto parsing histograms) that 0 timestamp is not a // a valid timestamp. If needed it can be represented as 0+1ms. Histogram() ([]byte, *int64, *histogram.Histogram, *histogram.FloatHistogram) @@ -76,10 +76,10 @@ type Parser interface { // retrieved (including the case where no exemplars exist at all). Exemplar(l *exemplar.Exemplar) bool - // CreatedTimestamp returns the created timestamp (in milliseconds) for the + // StartTimestamp returns the created timestamp (in milliseconds) for the // current sample. It returns 0 if it is unknown e.g. if it wasn't set or // if the scrape protocol or metric type does not support created timestamps. - CreatedTimestamp() int64 + StartTimestamp() int64 // Next advances the parser to the next sample. // It returns (EntryInvalid, io.EOF) if no samples were read. @@ -146,9 +146,9 @@ type ParserOptions struct { // that is also present as a native histogram. (Proto parsing only). KeepClassicOnClassicAndNativeHistograms bool - // OpenMetricsSkipCTSeries determines whether to skip `_created` timestamp series + // OpenMetricsSkipSTSeries determines whether to skip `_created` timestamp series // during (OpenMetrics parsing only). - OpenMetricsSkipCTSeries bool + OpenMetricsSkipSTSeries bool // FallbackContentType specifies the fallback content type to use when the provided // Content-Type header cannot be parsed or is not supported. @@ -175,7 +175,7 @@ func New(b []byte, contentType string, st *labels.SymbolTable, opts ParserOption switch mediaType { case "application/openmetrics-text": baseParser = NewOpenMetricsParser(b, st, func(o *openMetricsParserOptions) { - o.skipCTSeries = opts.OpenMetricsSkipCTSeries + o.skipSTSeries = opts.OpenMetricsSkipSTSeries o.enableTypeAndUnitLabels = opts.EnableTypeAndUnitLabels }) case "application/vnd.google.protobuf": diff --git a/vendor/github.com/prometheus/prometheus/model/textparse/nhcbparse.go b/vendor/github.com/prometheus/prometheus/model/textparse/nhcbparse.go index 8ec541de8ab..79441e1f754 100644 --- a/vendor/github.com/prometheus/prometheus/model/textparse/nhcbparse.go +++ b/vendor/github.com/prometheus/prometheus/model/textparse/nhcbparse.go @@ -83,7 +83,7 @@ type NHCBParser struct { fhNHCB *histogram.FloatHistogram lsetNHCB labels.Labels exemplars []exemplar.Exemplar - ctNHCB int64 + stNHCB int64 metricStringNHCB string // Collates values from the classic histogram series to build @@ -92,7 +92,7 @@ type NHCBParser struct { tempNHCB convertnhcb.TempHistogram tempExemplars []exemplar.Exemplar tempExemplarCount int - tempCT int64 + tempST int64 // Remembers the last base histogram metric name (assuming it's // a classic histogram) so we can tell if the next float series @@ -159,16 +159,16 @@ func (p *NHCBParser) Exemplar(ex *exemplar.Exemplar) bool { return p.parser.Exemplar(ex) } -func (p *NHCBParser) CreatedTimestamp() int64 { +func (p *NHCBParser) StartTimestamp() int64 { switch p.state { case stateStart, stateInhibiting: if p.entry == EntrySeries || p.entry == EntryHistogram { - return p.parser.CreatedTimestamp() + return p.parser.StartTimestamp() } case stateCollecting: - return p.tempCT + return p.tempST case stateEmitting: - return p.ctNHCB + return p.stNHCB } return 0 } @@ -318,7 +318,7 @@ func (p *NHCBParser) handleClassicHistogramSeries(lset labels.Labels) bool { func (p *NHCBParser) processClassicHistogramSeries(lset labels.Labels, name string, updateHist func(*convertnhcb.TempHistogram)) { if p.state != stateCollecting { p.storeClassicLabels(name) - p.tempCT = p.parser.CreatedTimestamp() + p.tempST = p.parser.StartTimestamp() p.state = stateCollecting p.tempLsetNHCB = convertnhcb.GetHistogramMetricBase(lset, name) } @@ -352,7 +352,7 @@ func (p *NHCBParser) swapExemplars() { } // processNHCB converts the collated classic histogram series to NHCB and caches the info -// to be returned to callers. Retruns true if the conversion was successful. +// to be returned to callers. Returns true if the conversion was successful. func (p *NHCBParser) processNHCB() bool { if p.state != stateCollecting { return false @@ -385,13 +385,13 @@ func (p *NHCBParser) processNHCB() bool { p.bytesNHCB = []byte(p.metricStringNHCB) p.lsetNHCB = p.tempLsetNHCB p.swapExemplars() - p.ctNHCB = p.tempCT + p.stNHCB = p.tempST p.state = stateEmitting } else { p.state = stateStart } p.tempNHCB.Reset() p.tempExemplarCount = 0 - p.tempCT = 0 + p.tempST = 0 return err == nil } diff --git a/vendor/github.com/prometheus/prometheus/model/textparse/openmetricsparse.go b/vendor/github.com/prometheus/prometheus/model/textparse/openmetricsparse.go index 505e45fc40e..207ceb45737 100644 --- a/vendor/github.com/prometheus/prometheus/model/textparse/openmetricsparse.go +++ b/vendor/github.com/prometheus/prometheus/model/textparse/openmetricsparse.go @@ -103,34 +103,34 @@ type OpenMetricsParser struct { hasExemplarTs bool // Created timestamp parsing state. - ct int64 - ctHashSet uint64 + st int64 + stHashSet uint64 // ignoreExemplar instructs the parser to not overwrite exemplars (to keep them while peeking ahead). ignoreExemplar bool // visitedMFName is the metric family name of the last visited metric when peeking ahead - // for _created series during the execution of the CreatedTimestamp method. + // for _created series during the execution of the StartTimestamp method. visitedMFName []byte - skipCTSeries bool + skipSTSeries bool enableTypeAndUnitLabels bool } type openMetricsParserOptions struct { - skipCTSeries bool + skipSTSeries bool enableTypeAndUnitLabels bool } type OpenMetricsOption func(*openMetricsParserOptions) -// WithOMParserCTSeriesSkipped turns off exposing _created lines +// WithOMParserSTSeriesSkipped turns off exposing _created lines // as series, which makes those only used for parsing created timestamp -// for `CreatedTimestamp` method purposes. +// for `StartTimestamp` method purposes. // // It's recommended to use this option to avoid using _created lines for other // purposes than created timestamp, but leave false by default for the // best-effort compatibility. -func WithOMParserCTSeriesSkipped() OpenMetricsOption { +func WithOMParserSTSeriesSkipped() OpenMetricsOption { return func(o *openMetricsParserOptions) { - o.skipCTSeries = true + o.skipSTSeries = true } } @@ -142,7 +142,7 @@ func WithOMParserTypeAndUnitLabels() OpenMetricsOption { } } -// NewOpenMetricsParser returns a new parser for the byte slice with option to skip CT series parsing. +// NewOpenMetricsParser returns a new parser for the byte slice with option to skip ST series parsing. func NewOpenMetricsParser(b []byte, st *labels.SymbolTable, opts ...OpenMetricsOption) Parser { options := &openMetricsParserOptions{} @@ -153,7 +153,7 @@ func NewOpenMetricsParser(b []byte, st *labels.SymbolTable, opts ...OpenMetricsO parser := &OpenMetricsParser{ l: &openMetricsLexer{b: b}, builder: labels.NewScratchBuilderWithSymbolTable(st, 16), - skipCTSeries: options.skipCTSeries, + skipSTSeries: options.skipSTSeries, enableTypeAndUnitLabels: options.enableTypeAndUnitLabels, } @@ -285,12 +285,12 @@ func (p *OpenMetricsParser) Exemplar(e *exemplar.Exemplar) bool { return true } -// CreatedTimestamp returns the created timestamp for a current Metric if exists or nil. +// StartTimestamp returns the created timestamp for a current Metric if exists or nil. // NOTE(Maniktherana): Might use additional CPU/mem resources due to deep copy of parser required for peeking given 1.0 OM specification on _created series. -func (p *OpenMetricsParser) CreatedTimestamp() int64 { - if !typeRequiresCT(p.mtype) { - // Not a CT supported metric type, fast path. - p.ctHashSet = 0 // Use ctHashSet as a single way of telling "empty cache" +func (p *OpenMetricsParser) StartTimestamp() int64 { + if !typeRequiresST(p.mtype) { + // Not a ST supported metric type, fast path. + p.stHashSet = 0 // Use stHashSet as a single way of telling "empty cache" return 0 } @@ -307,8 +307,8 @@ func (p *OpenMetricsParser) CreatedTimestamp() int64 { currHash := p.seriesHash(&buf, currName) // Check cache, perhaps we fetched something already. - if currHash == p.ctHashSet && p.ct > 0 { - return p.ct + if currHash == p.stHashSet && p.st > 0 { + return p.st } // Create a new lexer and other core state details to reset the parser once this function is done executing. @@ -322,7 +322,7 @@ func (p *OpenMetricsParser) CreatedTimestamp() int64 { resetStart := p.start resetMType := p.mtype - p.skipCTSeries = false + p.skipSTSeries = false p.ignoreExemplar = true defer func() { p.l = resetLexer @@ -334,38 +334,38 @@ func (p *OpenMetricsParser) CreatedTimestamp() int64 { for { eType, err := p.Next() if err != nil { - // This means p.Next() will give error too later on, so def no CT line found. - // This might result in partial scrape with wrong/missing CT, but only + // This means p.Next() will give error too later on, so def no ST line found. + // This might result in partial scrape with wrong/missing ST, but only // spec improvement would help. - // TODO: Make sure OM 1.1/2.0 pass CT via metadata or exemplar-like to avoid this. - p.resetCTParseValues() + // TODO: Make sure OM 1.1/2.0 pass ST via metadata or exemplar-like to avoid this. + p.resetSTParseValues() return 0 } if eType != EntrySeries { - // Assume we hit different family, no CT line found. - p.resetCTParseValues() + // Assume we hit different family, no ST line found. + p.resetSTParseValues() return 0 } peekedName := p.series[p.offsets[0]-p.start : p.offsets[1]-p.start] if len(peekedName) < 8 || string(peekedName[len(peekedName)-8:]) != "_created" { - // Not a CT line, search more. + // Not a ST line, search more. continue } // Remove _created suffix. peekedHash := p.seriesHash(&buf, peekedName[:len(peekedName)-8]) if peekedHash != currHash { - // Found CT line for a different series, for our series no CT. - p.resetCTParseValues() + // Found ST line for a different series, for our series no ST. + p.resetSTParseValues() return 0 } // All timestamps in OpenMetrics are Unix Epoch in seconds. Convert to milliseconds. // https://github.com/prometheus/OpenMetrics/blob/v1.0.0/specification/OpenMetrics.md#timestamps - ct := int64(p.val * 1000.0) - p.setCTParseValues(ct, currHash, currName, true) - return ct + st := int64(p.val * 1000.0) + p.setSTParseValues(st, currHash, currName, true) + return st } } @@ -404,23 +404,23 @@ func (p *OpenMetricsParser) seriesHash(offsetsArr *[]byte, metricFamilyName []by return hashedOffsets } -// setCTParseValues sets the parser to the state after CreatedTimestamp method was called and CT was found. -// This is useful to prevent re-parsing the same series again and early return the CT value. -func (p *OpenMetricsParser) setCTParseValues(ct int64, ctHashSet uint64, mfName []byte, skipCTSeries bool) { - p.ct = ct - p.ctHashSet = ctHashSet +// setSTParseValues sets the parser to the state after StartTimestamp method was called and ST was found. +// This is useful to prevent re-parsing the same series again and early return the ST value. +func (p *OpenMetricsParser) setSTParseValues(st int64, stHashSet uint64, mfName []byte, skipSTSeries bool) { + p.st = st + p.stHashSet = stHashSet p.visitedMFName = mfName - p.skipCTSeries = skipCTSeries // Do we need to set it? + p.skipSTSeries = skipSTSeries // Do we need to set it? } -// resetCTParseValues resets the parser to the state before CreatedTimestamp method was called. -func (p *OpenMetricsParser) resetCTParseValues() { - p.ctHashSet = 0 - p.skipCTSeries = true +// resetSTParseValues resets the parser to the state before StartTimestamp method was called. +func (p *OpenMetricsParser) resetSTParseValues() { + p.stHashSet = 0 + p.skipSTSeries = true } -// typeRequiresCT returns true if the metric type requires a _created timestamp. -func typeRequiresCT(t model.MetricType) bool { +// typeRequiresST returns true if the metric type requires a _created timestamp. +func typeRequiresST(t model.MetricType) bool { switch t { case model.MetricTypeCounter, model.MetricTypeSummary, model.MetricTypeHistogram: return true @@ -544,7 +544,7 @@ func (p *OpenMetricsParser) Next() (Entry, error) { if err := p.parseSeriesEndOfLine(p.nextToken()); err != nil { return EntryInvalid, err } - if p.skipCTSeries && p.isCreatedSeries() { + if p.skipSTSeries && p.isCreatedSeries() { return p.Next() } return EntrySeries, nil @@ -565,7 +565,7 @@ func (p *OpenMetricsParser) Next() (Entry, error) { if err := p.parseSeriesEndOfLine(t2); err != nil { return EntryInvalid, err } - if p.skipCTSeries && p.isCreatedSeries() { + if p.skipSTSeries && p.isCreatedSeries() { return p.Next() } return EntrySeries, nil @@ -697,7 +697,7 @@ func (p *OpenMetricsParser) parseLVals(offsets []int, isExemplar bool) ([]int, e func (p *OpenMetricsParser) isCreatedSeries() bool { metricName := p.series[p.offsets[0]-p.start : p.offsets[1]-p.start] // check length so the metric is longer than len("_created") - if typeRequiresCT(p.mtype) && len(metricName) >= 8 && string(metricName[len(metricName)-8:]) == "_created" { + if typeRequiresST(p.mtype) && len(metricName) >= 8 && string(metricName[len(metricName)-8:]) == "_created" { return true } return false diff --git a/vendor/github.com/prometheus/prometheus/model/textparse/promparse.go b/vendor/github.com/prometheus/prometheus/model/textparse/promparse.go index 2b4b750b4d7..4a75bcd8d8b 100644 --- a/vendor/github.com/prometheus/prometheus/model/textparse/promparse.go +++ b/vendor/github.com/prometheus/prometheus/model/textparse/promparse.go @@ -274,9 +274,9 @@ func (*PromParser) Exemplar(*exemplar.Exemplar) bool { return false } -// CreatedTimestamp returns 0 as it's not implemented yet. +// StartTimestamp returns 0 as it's not implemented yet. // TODO(bwplotka): https://github.com/prometheus/prometheus/issues/12980 -func (*PromParser) CreatedTimestamp() int64 { +func (*PromParser) StartTimestamp() int64 { return 0 } diff --git a/vendor/github.com/prometheus/prometheus/model/textparse/protobufparse.go b/vendor/github.com/prometheus/prometheus/model/textparse/protobufparse.go index 800f02085e2..a48aa4af69a 100644 --- a/vendor/github.com/prometheus/prometheus/model/textparse/protobufparse.go +++ b/vendor/github.com/prometheus/prometheus/model/textparse/protobufparse.go @@ -19,7 +19,6 @@ import ( "fmt" "io" "math" - "strings" "unicode/utf8" "github.com/gogo/protobuf/types" @@ -400,24 +399,24 @@ func (p *ProtobufParser) Exemplar(ex *exemplar.Exemplar) bool { return true } -// CreatedTimestamp returns CT or 0 if CT is not present on counters, summaries or histograms. -func (p *ProtobufParser) CreatedTimestamp() int64 { - var ct *types.Timestamp +// StartTimestamp returns ST or 0 if ST is not present on counters, summaries or histograms. +func (p *ProtobufParser) StartTimestamp() int64 { + var st *types.Timestamp switch p.dec.GetType() { case dto.MetricType_COUNTER: - ct = p.dec.GetCounter().GetCreatedTimestamp() + st = p.dec.GetCounter().GetCreatedTimestamp() case dto.MetricType_SUMMARY: - ct = p.dec.GetSummary().GetCreatedTimestamp() + st = p.dec.GetSummary().GetCreatedTimestamp() case dto.MetricType_HISTOGRAM, dto.MetricType_GAUGE_HISTOGRAM: - ct = p.dec.GetHistogram().GetCreatedTimestamp() + st = p.dec.GetHistogram().GetCreatedTimestamp() default: } - if ct == nil { + if st == nil { return 0 } // Same as the gogo proto types.TimestampFromProto but straight to integer. // and without validation. - return ct.GetSeconds()*1e3 + int64(ct.GetNanos())/1e6 + return st.GetSeconds()*1e3 + int64(st.GetNanos())/1e6 } // Next advances the parser to the next "sample" (emulating the behavior of a @@ -466,16 +465,6 @@ func (p *ProtobufParser) Next() (Entry, error) { default: return EntryInvalid, fmt.Errorf("unknown metric type for metric %q: %s", name, p.dec.GetType()) } - unit := p.dec.GetUnit() - if len(unit) > 0 { - if p.dec.GetType() == dto.MetricType_COUNTER && strings.HasSuffix(name, "_total") { - if !strings.HasSuffix(name[:len(name)-6], unit) || len(name)-6 < len(unit)+1 || name[len(name)-6-len(unit)-1] != '_' { - return EntryInvalid, fmt.Errorf("unit %q not a suffix of counter %q", unit, name) - } - } else if !strings.HasSuffix(name, unit) || len(name) < len(unit)+1 || name[len(name)-len(unit)-1] != '_' { - return EntryInvalid, fmt.Errorf("unit %q not a suffix of metric %q", unit, name) - } - } p.entryBytes.Reset() p.entryBytes.WriteString(name) p.state = EntryHelp diff --git a/vendor/github.com/prometheus/prometheus/notifier/alertmanagerset.go b/vendor/github.com/prometheus/prometheus/notifier/alertmanagerset.go index c47c9ea23ad..b6d1b8c4aa1 100644 --- a/vendor/github.com/prometheus/prometheus/notifier/alertmanagerset.go +++ b/vendor/github.com/prometheus/prometheus/notifier/alertmanagerset.go @@ -111,7 +111,8 @@ func (s *alertmanagerSet) sync(tgs []*targetgroup.Group) { if _, ok := seen[us]; ok { continue } - s.metrics.latency.DeleteLabelValues(us) + s.metrics.latencySummary.DeleteLabelValues(us) + s.metrics.latencyHistogram.DeleteLabelValues(us) s.metrics.sent.DeleteLabelValues(us) s.metrics.errors.DeleteLabelValues(us) seen[us] = struct{}{} diff --git a/vendor/github.com/prometheus/prometheus/notifier/manager.go b/vendor/github.com/prometheus/prometheus/notifier/manager.go index 65adfd5c3ea..e37f59a2506 100644 --- a/vendor/github.com/prometheus/prometheus/notifier/manager.go +++ b/vendor/github.com/prometheus/prometheus/notifier/manager.go @@ -498,7 +498,9 @@ func (n *Manager) sendAll(alerts ...*Alert) bool { amSetCovered.CompareAndSwap(k, false, true) } - n.metrics.latency.WithLabelValues(url).Observe(time.Since(begin).Seconds()) + durationSeconds := time.Since(begin).Seconds() + n.metrics.latencySummary.WithLabelValues(url).Observe(durationSeconds) + n.metrics.latencyHistogram.WithLabelValues(url).Observe(durationSeconds) n.metrics.sent.WithLabelValues(url).Add(float64(count)) wg.Done() diff --git a/vendor/github.com/prometheus/prometheus/notifier/metric.go b/vendor/github.com/prometheus/prometheus/notifier/metric.go index b9a55b3ec74..3f4abdda93c 100644 --- a/vendor/github.com/prometheus/prometheus/notifier/metric.go +++ b/vendor/github.com/prometheus/prometheus/notifier/metric.go @@ -13,10 +13,15 @@ package notifier -import "github.com/prometheus/client_golang/prometheus" +import ( + "time" + + "github.com/prometheus/client_golang/prometheus" +) type alertMetrics struct { - latency *prometheus.SummaryVec + latencySummary *prometheus.SummaryVec + latencyHistogram *prometheus.HistogramVec errors *prometheus.CounterVec sent *prometheus.CounterVec dropped prometheus.Counter @@ -25,9 +30,13 @@ type alertMetrics struct { alertmanagersDiscovered prometheus.GaugeFunc } -func newAlertMetrics(r prometheus.Registerer, queueCap int, queueLen, alertmanagersDiscovered func() float64) *alertMetrics { +func newAlertMetrics( + r prometheus.Registerer, + queueCap int, + queueLen, alertmanagersDiscovered func() float64, +) *alertMetrics { m := &alertMetrics{ - latency: prometheus.NewSummaryVec(prometheus.SummaryOpts{ + latencySummary: prometheus.NewSummaryVec(prometheus.SummaryOpts{ Namespace: namespace, Subsystem: subsystem, Name: "latency_seconds", @@ -36,6 +45,19 @@ func newAlertMetrics(r prometheus.Registerer, queueCap int, queueLen, alertmanag }, []string{alertmanagerLabel}, ), + latencyHistogram: prometheus.NewHistogramVec(prometheus.HistogramOpts{ + Namespace: namespace, + Subsystem: subsystem, + Name: "latency_histogram_seconds", + Help: "Latency histogram for sending alert notifications.", + + Buckets: []float64{.01, .1, 1, 10}, + NativeHistogramBucketFactor: 1.1, + NativeHistogramMaxBucketNumber: 100, + NativeHistogramMinResetDuration: 1 * time.Hour, + }, + []string{alertmanagerLabel}, + ), errors: prometheus.NewCounterVec(prometheus.CounterOpts{ Namespace: namespace, Subsystem: subsystem, @@ -80,7 +102,8 @@ func newAlertMetrics(r prometheus.Registerer, queueCap int, queueLen, alertmanag if r != nil { r.MustRegister( - m.latency, + m.latencySummary, + m.latencyHistogram, m.errors, m.sent, m.dropped, diff --git a/vendor/github.com/prometheus/prometheus/promql/engine.go b/vendor/github.com/prometheus/prometheus/promql/engine.go index 75fc9b05d34..5a08da121cd 100644 --- a/vendor/github.com/prometheus/prometheus/promql/engine.go +++ b/vendor/github.com/prometheus/prometheus/promql/engine.go @@ -49,6 +49,7 @@ import ( "github.com/prometheus/prometheus/storage" "github.com/prometheus/prometheus/tsdb/chunkenc" "github.com/prometheus/prometheus/util/annotations" + "github.com/prometheus/prometheus/util/features" "github.com/prometheus/prometheus/util/logging" "github.com/prometheus/prometheus/util/stats" "github.com/prometheus/prometheus/util/zeropool" @@ -76,15 +77,19 @@ const ( ) type engineMetrics struct { - currentQueries prometheus.Gauge - maxConcurrentQueries prometheus.Gauge - queryLogEnabled prometheus.Gauge - queryLogFailures prometheus.Counter - queryQueueTime prometheus.Observer - queryPrepareTime prometheus.Observer - queryInnerEval prometheus.Observer - queryResultSort prometheus.Observer - querySamples prometheus.Counter + currentQueries prometheus.Gauge + maxConcurrentQueries prometheus.Gauge + queryLogEnabled prometheus.Gauge + queryLogFailures prometheus.Counter + queryQueueTime prometheus.Observer + queryQueueTimeHistogram prometheus.Observer + queryPrepareTime prometheus.Observer + queryPrepareTimeHistogram prometheus.Observer + queryInnerEval prometheus.Observer + queryInnerEvalHistogram prometheus.Observer + queryResultSort prometheus.Observer + queryResultSortHistogram prometheus.Observer + querySamples prometheus.Counter } type ( @@ -326,6 +331,9 @@ type EngineOpts struct { EnableDelayedNameRemoval bool // EnableTypeAndUnitLabels will allow PromQL Engine to make decisions based on the type and unit labels. EnableTypeAndUnitLabels bool + + // FeatureRegistry is the registry for tracking enabled/disabled features. + FeatureRegistry features.Collector } // Engine handles the lifetime of queries from beginning to end. @@ -363,6 +371,19 @@ func NewEngine(opts EngineOpts) *Engine { []string{"slice"}, ) + queryResultHistogram := prometheus.NewHistogramVec(prometheus.HistogramOpts{ + Namespace: namespace, + Subsystem: subsystem, + Name: "query_duration_histogram_seconds", + Help: "The duration of various parts of PromQL query execution.", + Buckets: []float64{.01, .1, 1, 10}, + NativeHistogramBucketFactor: 1.1, + NativeHistogramMaxBucketNumber: 100, + NativeHistogramMinResetDuration: 1 * time.Hour, + }, + []string{"slice"}, + ) + metrics := &engineMetrics{ currentQueries: prometheus.NewGauge(prometheus.GaugeOpts{ Namespace: namespace, @@ -394,10 +415,14 @@ func NewEngine(opts EngineOpts) *Engine { Name: "query_samples_total", Help: "The total number of samples loaded by all queries.", }), - queryQueueTime: queryResultSummary.WithLabelValues("queue_time"), - queryPrepareTime: queryResultSummary.WithLabelValues("prepare_time"), - queryInnerEval: queryResultSummary.WithLabelValues("inner_eval"), - queryResultSort: queryResultSummary.WithLabelValues("result_sort"), + queryQueueTime: queryResultSummary.WithLabelValues("queue_time"), + queryQueueTimeHistogram: queryResultHistogram.WithLabelValues("queue_time"), + queryPrepareTime: queryResultSummary.WithLabelValues("prepare_time"), + queryPrepareTimeHistogram: queryResultHistogram.WithLabelValues("prepare_time"), + queryInnerEval: queryResultSummary.WithLabelValues("inner_eval"), + queryInnerEvalHistogram: queryResultHistogram.WithLabelValues("inner_eval"), + queryResultSort: queryResultSummary.WithLabelValues("result_sort"), + queryResultSortHistogram: queryResultHistogram.WithLabelValues("result_sort"), } if t := opts.ActiveQueryTracker; t != nil { @@ -421,9 +446,22 @@ func NewEngine(opts EngineOpts) *Engine { metrics.queryLogFailures, metrics.querySamples, queryResultSummary, + queryResultHistogram, ) } + if r := opts.FeatureRegistry; r != nil { + r.Set(features.PromQL, "at_modifier", opts.EnableAtModifier) + r.Set(features.PromQL, "negative_offset", opts.EnableNegativeOffset) + r.Set(features.PromQL, "per_step_stats", opts.EnablePerStepStats) + r.Set(features.PromQL, "delayed_name_removal", opts.EnableDelayedNameRemoval) + r.Set(features.PromQL, "type_and_unit_labels", opts.EnableTypeAndUnitLabels) + r.Enable(features.PromQL, "per_query_lookback_delta") + r.Enable(features.PromQL, "subqueries") + + parser.RegisterFeatures(r) + } + return &Engine{ timeout: opts.Timeout, logger: opts.Logger, @@ -701,7 +739,7 @@ func (ng *Engine) queueActive(ctx context.Context, q *query) (func(), error) { if ng.activeQueryTracker == nil { return func() {}, nil } - queueSpanTimer, _ := q.stats.GetSpanTimer(ctx, stats.ExecQueueTime, ng.metrics.queryQueueTime) + queueSpanTimer, _ := q.stats.GetSpanTimer(ctx, stats.ExecQueueTime, ng.metrics.queryQueueTime, ng.metrics.queryQueueTimeHistogram) queryIndex, err := ng.activeQueryTracker.Insert(ctx, q.q) queueSpanTimer.Finish() return func() { ng.activeQueryTracker.Delete(queryIndex) }, err @@ -717,7 +755,7 @@ func durationMilliseconds(d time.Duration) int64 { // execEvalStmt evaluates the expression of an evaluation statement for the given time range. func (ng *Engine) execEvalStmt(ctx context.Context, query *query, s *parser.EvalStmt) (parser.Value, annotations.Annotations, error) { - prepareSpanTimer, ctxPrepare := query.stats.GetSpanTimer(ctx, stats.QueryPreparationTime, ng.metrics.queryPrepareTime) + prepareSpanTimer, ctxPrepare := query.stats.GetSpanTimer(ctx, stats.QueryPreparationTime, ng.metrics.queryPrepareTime, ng.metrics.queryPrepareTimeHistogram) mint, maxt := FindMinMaxTime(s) querier, err := query.queryable.Querier(mint, maxt) if err != nil { @@ -732,7 +770,7 @@ func (ng *Engine) execEvalStmt(ctx context.Context, query *query, s *parser.Eval // Modify the offset of vector and matrix selectors for the @ modifier // w.r.t. the start time since only 1 evaluation will be done on them. setOffsetForAtModifier(timeMilliseconds(s.Start), s.Expr) - evalSpanTimer, ctxInnerEval := query.stats.GetSpanTimer(ctx, stats.InnerEvalTime, ng.metrics.queryInnerEval) + evalSpanTimer, ctxInnerEval := query.stats.GetSpanTimer(ctx, stats.InnerEvalTime, ng.metrics.queryInnerEval, ng.metrics.queryInnerEvalHistogram) // Instant evaluation. This is executed as a range evaluation with one step. if s.Start.Equal(s.End) && s.Interval == 0 { start := timeMilliseconds(s.Start) @@ -835,7 +873,7 @@ func (ng *Engine) execEvalStmt(ctx context.Context, query *query, s *parser.Eval } func (ng *Engine) sortMatrixResult(ctx context.Context, query *query, mat Matrix) { - sortSpanTimer, _ := query.stats.GetSpanTimer(ctx, stats.ResultSortTime, ng.metrics.queryResultSort) + sortSpanTimer, _ := query.stats.GetSpanTimer(ctx, stats.ResultSortTime, ng.metrics.queryResultSort, ng.metrics.queryResultSortHistogram) sort.Sort(mat) sortSpanTimer.Finish() } @@ -1137,15 +1175,20 @@ func (ev *evaluator) Eval(ctx context.Context, expr parser.Expr) (v parser.Value v, ws = ev.eval(ctx, expr) if ev.enableDelayedNameRemoval { - ev.cleanupMetricLabels(v) + v = ev.cleanupMetricLabels(v) } return v, ws, nil } // EvalSeriesHelper stores extra information about a series. type EvalSeriesHelper struct { - // Used to map left-hand to right-hand in binary operations. - signature string + // Ordinal number of join signature, used to map left-hand to right-hand in + // binary operations. For example given the following series, if + // the join signature is job, instance then: + // metric{job="a", instance="1", other="x"} -> sigOrdinal 0 + // metric{job="a", instance="1", other="y"} -> sigOrdinal 0 + // metric{job="a", instance="2", other="x"} -> sigOrdinal 1 + sigOrdinal int } // EvalNodeHelper stores extra information and caches for evaluating a single node across steps. @@ -1165,9 +1208,13 @@ type EvalNodeHelper struct { lblResultBuf []byte // For binary vector matching. - rightSigs map[string]Sample - matchedSigs map[string]map[uint64]struct{} + rightSigs map[int]Sample + matchedSigs map[int]map[uint64]struct{} resultMetric map[string]labels.Labels + numSigs int + + // For info series matching. + rightStrSigs map[string]Sample // Additional options for the evaluation. enableDelayedNameRemoval bool @@ -1250,13 +1297,14 @@ func (enh *EvalNodeHelper) resetHistograms(inVec Vector, arg parser.Expr) annota // the given funcCall with the values computed for each expression at that // step. The return value is the combination into time series of all the // function call results. -// The prepSeries function (if provided) can be used to prepare the helper +// The matching (if provided) can be used to prepare the helper // for each series, then passed to each call funcCall. -func (ev *evaluator) rangeEval(ctx context.Context, prepSeries func(labels.Labels, *EvalSeriesHelper), funcCall func([]Vector, Matrix, [][]EvalSeriesHelper, *EvalNodeHelper) (Vector, annotations.Annotations), exprs ...parser.Expr) (Matrix, annotations.Annotations) { +func (ev *evaluator) rangeEval(ctx context.Context, matching *parser.VectorMatching, funcCall func([]Vector, Matrix, [][]EvalSeriesHelper, *EvalNodeHelper) (Vector, annotations.Annotations), exprs ...parser.Expr) (Matrix, annotations.Annotations) { numSteps := int((ev.endTimestamp-ev.startTimestamp)/ev.interval) + 1 matrixes := make([]Matrix, len(exprs)) origMatrixes := make([]Matrix, len(exprs)) originalNumSamples := ev.currentSamples + useSignatures := matching != nil var warnings annotations.Annotations for i, e := range exprs { @@ -1297,18 +1345,46 @@ func (ev *evaluator) rangeEval(ctx context.Context, prepSeries func(labels.Label bufHelpers [][]EvalSeriesHelper // Buffer updated on each step ) - // If the series preparation function is provided, we should run it for - // every single series in the matrix. - if prepSeries != nil { + if useSignatures { + var ( + // Function to compute the join signature for each series. + sigf func(labels.Labels) string + buf = make([]byte, 0, 1024) + names = slices.Clone(matching.MatchingLabels) + ) + if matching.On { + slices.Sort(names) + sigf = func(lset labels.Labels) string { + return string(lset.BytesWithLabels(buf, names...)) + } + } else { // "without" + names = append([]string{labels.MetricName}, names...) + slices.Sort(names) + sigf = func(lset labels.Labels) string { + return string(lset.BytesWithoutLabels(buf, names...)) + } + } + seriesHelpers = make([][]EvalSeriesHelper, len(exprs)) bufHelpers = make([][]EvalSeriesHelper, len(exprs)) + signatureToOrdinal := make(map[string]int) + for i := range exprs { seriesHelpers[i] = make([]EvalSeriesHelper, len(matrixes[i])) bufHelpers[i] = make([]EvalSeriesHelper, len(matrixes[i])) for si, series := range matrixes[i] { - prepSeries(series.Metric, &seriesHelpers[i][si]) + strSig := sigf(series.Metric) + + if sigOrd, ok := signatureToOrdinal[strSig]; ok { + seriesHelpers[i][si] = EvalSeriesHelper{sigOrdinal: sigOrd} + continue + } + + signatureToOrdinal[strSig] = enh.numSigs + seriesHelpers[i][si] = EvalSeriesHelper{sigOrdinal: enh.numSigs} + enh.numSigs++ } } } @@ -1323,12 +1399,12 @@ func (ev *evaluator) rangeEval(ctx context.Context, prepSeries func(labels.Label for i := range exprs { var bh []EvalSeriesHelper var sh []EvalSeriesHelper - if prepSeries != nil { + if useSignatures { bh = bufHelpers[i][:0] sh = seriesHelpers[i] } vectors[i], bh = ev.gatherVector(ts, matrixes[i], vectors[i], bh, sh) - if prepSeries != nil { + if useSignatures { bufHelpers[i] = bh } } @@ -2115,8 +2191,8 @@ func (ev *evaluator) eval(ctx context.Context, expr parser.Expr) (parser.Value, mat[i].Histograms[j].H = mat[i].Histograms[j].H.Copy().Mul(-1) } } - if !ev.enableDelayedNameRemoval && mat.ContainsSameLabelset() { - ev.errorf("vector cannot contain metrics with the same labelset") + if !ev.enableDelayedNameRemoval { + mat = ev.mergeSeriesWithSameLabelset(mat) } } return mat, ws @@ -2129,27 +2205,21 @@ func (ev *evaluator) eval(ctx context.Context, expr parser.Expr) (parser.Value, return append(enh.Out, Sample{F: val}), nil }, e.LHS, e.RHS) case lt == parser.ValueTypeVector && rt == parser.ValueTypeVector: - // Function to compute the join signature for each series. - buf := make([]byte, 0, 1024) - sigf := signatureFunc(e.VectorMatching.On, buf, e.VectorMatching.MatchingLabels...) - initSignatures := func(series labels.Labels, h *EvalSeriesHelper) { - h.signature = sigf(series) - } switch e.Op { case parser.LAND: - return ev.rangeEval(ctx, initSignatures, func(v []Vector, _ Matrix, sh [][]EvalSeriesHelper, enh *EvalNodeHelper) (Vector, annotations.Annotations) { + return ev.rangeEval(ctx, e.VectorMatching, func(v []Vector, _ Matrix, sh [][]EvalSeriesHelper, enh *EvalNodeHelper) (Vector, annotations.Annotations) { return ev.VectorAnd(v[0], v[1], e.VectorMatching, sh[0], sh[1], enh), nil }, e.LHS, e.RHS) case parser.LOR: - return ev.rangeEval(ctx, initSignatures, func(v []Vector, _ Matrix, sh [][]EvalSeriesHelper, enh *EvalNodeHelper) (Vector, annotations.Annotations) { + return ev.rangeEval(ctx, e.VectorMatching, func(v []Vector, _ Matrix, sh [][]EvalSeriesHelper, enh *EvalNodeHelper) (Vector, annotations.Annotations) { return ev.VectorOr(v[0], v[1], e.VectorMatching, sh[0], sh[1], enh), nil }, e.LHS, e.RHS) case parser.LUNLESS: - return ev.rangeEval(ctx, initSignatures, func(v []Vector, _ Matrix, sh [][]EvalSeriesHelper, enh *EvalNodeHelper) (Vector, annotations.Annotations) { + return ev.rangeEval(ctx, e.VectorMatching, func(v []Vector, _ Matrix, sh [][]EvalSeriesHelper, enh *EvalNodeHelper) (Vector, annotations.Annotations) { return ev.VectorUnless(v[0], v[1], e.VectorMatching, sh[0], sh[1], enh), nil }, e.LHS, e.RHS) default: - return ev.rangeEval(ctx, initSignatures, func(v []Vector, _ Matrix, sh [][]EvalSeriesHelper, enh *EvalNodeHelper) (Vector, annotations.Annotations) { + return ev.rangeEval(ctx, e.VectorMatching, func(v []Vector, _ Matrix, sh [][]EvalSeriesHelper, enh *EvalNodeHelper) (Vector, annotations.Annotations) { vec, err := ev.VectorBinop(e.Op, v[0], v[1], e.VectorMatching, e.ReturnBool, sh[0], sh[1], enh, e.PositionRange()) return vec, handleVectorBinopError(err, e) }, e.LHS, e.RHS) @@ -2720,16 +2790,15 @@ func (*evaluator) VectorAnd(lhs, rhs Vector, matching *parser.VectorMatching, lh return nil // Short-circuit: AND with nothing is nothing. } - // The set of signatures for the right-hand side Vector. - rightSigs := map[string]struct{}{} - // Add all rhs samples to a map so we can easily find matches later. + // Ordinals of signatures present on the right-hand side. + rightSigOrdinalsPresent := make([]bool, enh.numSigs) for _, sh := range rhsh { - rightSigs[sh.signature] = struct{}{} + rightSigOrdinalsPresent[sh.sigOrdinal] = true } for i, ls := range lhs { // If there's a matching entry in the right-hand side Vector, add the sample. - if _, ok := rightSigs[lhsh[i].signature]; ok { + if rightSigOrdinalsPresent[lhsh[i].sigOrdinal] { enh.Out = append(enh.Out, ls) } } @@ -2748,15 +2817,15 @@ func (*evaluator) VectorOr(lhs, rhs Vector, matching *parser.VectorMatching, lhs return enh.Out } - leftSigs := map[string]struct{}{} + leftSigOrdinalsPresent := make([]bool, enh.numSigs) // Add everything from the left-hand-side Vector. for i, ls := range lhs { - leftSigs[lhsh[i].signature] = struct{}{} + leftSigOrdinalsPresent[lhsh[i].sigOrdinal] = true enh.Out = append(enh.Out, ls) } // Add all right-hand side elements which have not been added from the left-hand side. for j, rs := range rhs { - if _, ok := leftSigs[rhsh[j].signature]; !ok { + if !leftSigOrdinalsPresent[rhsh[j].sigOrdinal] { enh.Out = append(enh.Out, rs) } } @@ -2774,13 +2843,14 @@ func (*evaluator) VectorUnless(lhs, rhs Vector, matching *parser.VectorMatching, return enh.Out } - rightSigs := map[string]struct{}{} + // Ordinals of signatures present on the right-hand side. + rightSigOrdinalsPresent := make([]bool, enh.numSigs) for _, sh := range rhsh { - rightSigs[sh.signature] = struct{}{} + rightSigOrdinalsPresent[sh.sigOrdinal] = true } for i, ls := range lhs { - if _, ok := rightSigs[lhsh[i].signature]; !ok { + if !rightSigOrdinalsPresent[lhsh[i].sigOrdinal] { enh.Out = append(enh.Out, ls) } } @@ -2804,22 +2874,20 @@ func (ev *evaluator) VectorBinop(op parser.ItemType, lhs, rhs Vector, matching * lhsh, rhsh = rhsh, lhsh } - // All samples from the rhs hashed by the matching label/values. + // All samples from the rhs by their join signature ordinal. if enh.rightSigs == nil { - enh.rightSigs = make(map[string]Sample, len(enh.Out)) + enh.rightSigs = make(map[int]Sample, len(enh.Out)) } else { - for k := range enh.rightSigs { - delete(enh.rightSigs, k) - } + clear(enh.rightSigs) } rightSigs := enh.rightSigs // Add all rhs samples to a map so we can easily find matches later. for i, rs := range rhs { - sig := rhsh[i].signature + sigOrd := rhsh[i].sigOrdinal // The rhs is guaranteed to be the 'one' side. Having multiple samples // with the same signature means that the matching is many-to-many. - if duplSample, found := rightSigs[sig]; found { + if duplSample, found := rightSigs[sigOrd]; found { // oneSide represents which side of the vector represents the 'one' in the many-to-one relationship. oneSide := "right" if matching.Card == parser.CardOneToMany { @@ -2830,17 +2898,15 @@ func (ev *evaluator) VectorBinop(op parser.ItemType, lhs, rhs Vector, matching * ev.errorf("found duplicate series for the match group %s on the %s hand-side of the operation: [%s, %s]"+ ";many-to-many matching not allowed: matching labels must be unique on one side", matchedLabels.String(), oneSide, rs.Metric.String(), duplSample.Metric.String()) } - rightSigs[sig] = rs + rightSigs[sigOrd] = rs } - // Tracks the match-signature. For one-to-one operations the value is nil. For many-to-one - // the value is a set of signatures to detect duplicated result elements. + // Tracks the matching by signature ordinals. For one-to-one operations the value is nil. + // For many-to-one the value is a set of hashes to detect duplicated result elements. if enh.matchedSigs == nil { - enh.matchedSigs = make(map[string]map[uint64]struct{}, len(rightSigs)) + enh.matchedSigs = make(map[int]map[uint64]struct{}, len(rightSigs)) } else { - for k := range enh.matchedSigs { - delete(enh.matchedSigs, k) - } + clear(enh.matchedSigs) } matchedSigs := enh.matchedSigs @@ -2848,9 +2914,9 @@ func (ev *evaluator) VectorBinop(op parser.ItemType, lhs, rhs Vector, matching * // the binary operation. var lastErr error for i, ls := range lhs { - sig := lhsh[i].signature + sigOrd := lhsh[i].sigOrdinal - rs, found := rightSigs[sig] // Look for a match in the rhs Vector. + rs, found := rightSigs[sigOrd] // Look for a match in the rhs Vector. if !found { continue } @@ -2870,27 +2936,25 @@ func (ev *evaluator) VectorBinop(op parser.ItemType, lhs, rhs Vector, matching * if info != nil { lastErr = info } - switch { - case returnBool: + if returnBool { histogramValue = nil if keep { floatValue = 1.0 } else { floatValue = 0.0 } - case !keep: - continue } + metric := resultMetric(ls.Metric, rs.Metric, op, matching, enh) if !ev.enableDelayedNameRemoval && returnBool { metric = metric.DropReserved(schema.IsMetadataLabel) } - insertedSigs, exists := matchedSigs[sig] + insertedSigs, exists := matchedSigs[sigOrd] if matching.Card == parser.CardOneToOne { if exists { ev.errorf("multiple matches for labels: many-to-one matching must be explicit (group_left/group_right)") } - matchedSigs[sig] = nil // Set existence to true. + matchedSigs[sigOrd] = nil // Set existence to true. } else { // In many-to-one matching the grouping labels have to ensure a unique metric // for the result Vector. Check whether those labels have already been added for @@ -2899,13 +2963,17 @@ func (ev *evaluator) VectorBinop(op parser.ItemType, lhs, rhs Vector, matching * if !exists { insertedSigs = map[uint64]struct{}{} - matchedSigs[sig] = insertedSigs + matchedSigs[sigOrd] = insertedSigs } else if _, duplicate := insertedSigs[insertSig]; duplicate { ev.errorf("multiple matches for labels: grouping labels must ensure unique matches") } insertedSigs[insertSig] = struct{}{} } + if !keep && !returnBool { + continue + } + enh.Out = append(enh.Out, Sample{ Metric: metric, F: floatValue, @@ -2916,20 +2984,6 @@ func (ev *evaluator) VectorBinop(op parser.ItemType, lhs, rhs Vector, matching * return enh.Out, lastErr } -func signatureFunc(on bool, b []byte, names ...string) func(labels.Labels) string { - if on { - slices.Sort(names) - return func(lset labels.Labels) string { - return string(lset.BytesWithLabels(b, names...)) - } - } - names = append([]string{labels.MetricName}, names...) - slices.Sort(names) - return func(lset labels.Labels) string { - return string(lset.BytesWithoutLabels(b, names...)) - } -} - // resultMetric returns the metric for the given sample(s) based on the Vector // binary operation and the matching options. func resultMetric(lhs, rhs labels.Labels, op parser.ItemType, matching *parser.VectorMatching, enh *EvalNodeHelper) labels.Labels { @@ -2937,7 +2991,6 @@ func resultMetric(lhs, rhs labels.Labels, op parser.ItemType, matching *parser.V enh.resultMetric = make(map[string]labels.Labels, len(enh.Out)) } - enh.resetBuilder(lhs) buf := bytes.NewBuffer(enh.lblResultBuf[:0]) enh.lblBuf = lhs.Bytes(enh.lblBuf) buf.Write(enh.lblBuf) @@ -2950,6 +3003,7 @@ func resultMetric(lhs, rhs labels.Labels, op parser.ItemType, matching *parser.V } str := string(enh.lblResultBuf) + enh.resetBuilder(lhs) if changesMetricSchema(op) { // Setting empty Metadata causes the deletion of those if they exists. schema.Metadata{}.SetToLabels(enh.lb) @@ -3058,7 +3112,6 @@ func scalarBinop(op parser.ItemType, lhs, rhs float64) float64 { // vectorElemBinop evaluates a binary operation between two Vector elements. func vectorElemBinop(op parser.ItemType, lhs, rhs float64, hlhs, hrhs *histogram.FloatHistogram, pos posrange.PositionRange) (res float64, resH *histogram.FloatHistogram, keep bool, info, err error) { - opName := parser.ItemTypeStr[op] switch { case hlhs == nil && hrhs == nil: { @@ -3097,7 +3150,7 @@ func vectorElemBinop(op parser.ItemType, lhs, rhs float64, hlhs, hrhs *histogram case parser.MUL: return 0, hrhs.Copy().Mul(lhs).Compact(0), true, nil, nil case parser.ADD, parser.SUB, parser.DIV, parser.POW, parser.MOD, parser.EQLC, parser.NEQ, parser.GTR, parser.LSS, parser.GTE, parser.LTE, parser.ATAN2: - return 0, nil, false, nil, annotations.NewIncompatibleTypesInBinOpInfo("float", opName, "histogram", pos) + return 0, nil, false, nil, annotations.NewIncompatibleTypesInBinOpInfo("float", parser.ItemTypeStr[op], "histogram", pos) } } case hlhs != nil && hrhs == nil: @@ -3108,7 +3161,7 @@ func vectorElemBinop(op parser.ItemType, lhs, rhs float64, hlhs, hrhs *histogram case parser.DIV: return 0, hlhs.Copy().Div(rhs).Compact(0), true, nil, nil case parser.ADD, parser.SUB, parser.POW, parser.MOD, parser.EQLC, parser.NEQ, parser.GTR, parser.LSS, parser.GTE, parser.LTE, parser.ATAN2: - return 0, nil, false, nil, annotations.NewIncompatibleTypesInBinOpInfo("histogram", opName, "float", pos) + return 0, nil, false, nil, annotations.NewIncompatibleTypesInBinOpInfo("histogram", parser.ItemTypeStr[op], "float", pos) } } case hlhs != nil && hrhs != nil: @@ -3148,7 +3201,7 @@ func vectorElemBinop(op parser.ItemType, lhs, rhs float64, hlhs, hrhs *histogram // This operation expects that both histograms are compacted. return 0, hlhs, !hlhs.Equals(hrhs), nil, nil case parser.MUL, parser.DIV, parser.POW, parser.MOD, parser.GTR, parser.LSS, parser.GTE, parser.LTE, parser.ATAN2: - return 0, nil, false, nil, annotations.NewIncompatibleTypesInBinOpInfo("histogram", opName, "histogram", pos) + return 0, nil, false, nil, annotations.NewIncompatibleTypesInBinOpInfo("histogram", parser.ItemTypeStr[op], "histogram", pos) } } } @@ -3172,6 +3225,7 @@ type groupedAggregation struct { incrementalMean bool // True after reverting to incremental calculation of the mean value. counterResetSeen bool // Counter reset hint CounterReset seen. Currently only used for histogram samples. notCounterResetSeen bool // Counter reset hint NotCounterReset seen. Currently only used for histogram samples. + dropName bool // True if any sample in this group has DropName set. } // aggregation evaluates sum, avg, count, stdvar, stddev or quantile at one timestep on inputMatrix. @@ -3200,6 +3254,7 @@ func (ev *evaluator) aggregation(e *parser.AggregateExpr, q float64, inputMatrix floatMean: f, incompatibleHistograms: false, groupCount: 1, + dropName: inputMatrix[si].DropName, } switch op { case parser.AVG, parser.SUM: @@ -3256,6 +3311,10 @@ func (ev *evaluator) aggregation(e *parser.AggregateExpr, q float64, inputMatrix continue } + if inputMatrix[si].DropName { + group.dropName = true + } + switch op { case parser.SUM: if h != nil { @@ -3494,7 +3553,7 @@ func (ev *evaluator) aggregation(e *parser.AggregateExpr, q float64, inputMatrix ss := &outputMatrix[ri] addToSeries(ss, enh.Ts, aggr.floatValue, aggr.histogramValue, numSteps) - ss.DropName = inputMatrix[ri].DropName + ss.DropName = aggr.dropName } return annos @@ -3773,7 +3832,7 @@ func (*evaluator) aggregationCountValues(e *parser.AggregateExpr, grouping []str return enh.Out, nil } -func (ev *evaluator) cleanupMetricLabels(v parser.Value) { +func (ev *evaluator) cleanupMetricLabels(v parser.Value) parser.Value { if v.Type() == parser.ValueTypeMatrix { mat := v.(Matrix) for i := range mat { @@ -3781,9 +3840,7 @@ func (ev *evaluator) cleanupMetricLabels(v parser.Value) { mat[i].Metric = mat[i].Metric.DropReserved(schema.IsMetadataLabel) } } - if mat.ContainsSameLabelset() { - ev.errorf("vector cannot contain metrics with the same labelset") - } + return ev.mergeSeriesWithSameLabelset(mat) } else if v.Type() == parser.ValueTypeVector { vec := v.(Vector) for i := range vec { @@ -3794,7 +3851,75 @@ func (ev *evaluator) cleanupMetricLabels(v parser.Value) { if vec.ContainsSameLabelset() { ev.errorf("vector cannot contain metrics with the same labelset") } + return vec } + return v +} + +// mergeSeriesWithSameLabelset merges series in a matrix that have the same labelset +// after __name__ label removal. This happens when delayed name removal is enabled and +// operations like OR combine series that originally had different names but end up +// with the same labelset after dropping the name. If series with the same labelset +// have overlapping timestamps, an error is returned. +func (ev *evaluator) mergeSeriesWithSameLabelset(mat Matrix) Matrix { + if len(mat) <= 1 { + return mat + } + + // Fast path: check if there are any duplicate labelsets without allocating. + // This is the common case and we want to avoid allocations. + if !mat.ContainsSameLabelset() { + return mat + } + + // Slow path: there are duplicates, so we need to merge series with non-overlapping timestamps. + // Group series by their labelset hash. + seriesByHash := make(map[uint64][]int) + for i := range mat { + hash := mat[i].Metric.Hash() + seriesByHash[hash] = append(seriesByHash[hash], i) + } + + // Merge series with the same labelset. + merged := make(Matrix, 0, len(seriesByHash)) + for _, indices := range seriesByHash { + if len(indices) == 1 { + // No collision, add as-is. + merged = append(merged, mat[indices[0]]) + continue + } + + // Multiple series with the same labelset - merge all samples. + base := mat[indices[0]] + for _, idx := range indices[1:] { + base.Floats = append(base.Floats, mat[idx].Floats...) + base.Histograms = append(base.Histograms, mat[idx].Histograms...) + } + + // Sort merged samples by timestamp. + sort.Slice(base.Floats, func(i, j int) bool { + return base.Floats[i].T < base.Floats[j].T + }) + sort.Slice(base.Histograms, func(i, j int) bool { + return base.Histograms[i].T < base.Histograms[j].T + }) + + // Check for duplicate timestamps in sorted samples. + for i := 1; i < len(base.Floats); i++ { + if base.Floats[i].T == base.Floats[i-1].T { + ev.errorf("vector cannot contain metrics with the same labelset") + } + } + for i := 1; i < len(base.Histograms); i++ { + if base.Histograms[i].T == base.Histograms[i-1].T { + ev.errorf("vector cannot contain metrics with the same labelset") + } + } + + merged = append(merged, base) + } + + return merged } func addToSeries(ss *Series, ts int64, f float64, h *histogram.FloatHistogram, numSteps int) { @@ -3838,13 +3963,13 @@ func handleVectorBinopError(err error, e *parser.BinaryExpr) annotations.Annotat if err == nil { return nil } - op := parser.ItemTypeStr[e.Op] - pos := e.PositionRange() if errors.Is(err, annotations.PromQLInfo) || errors.Is(err, annotations.PromQLWarning) { return annotations.New().Add(err) } // TODO(NeerajGartia21): Test the exact annotation output once the testing framework can do so. if errors.Is(err, histogram.ErrHistogramsIncompatibleSchema) { + op := parser.ItemTypeStr[e.Op] + pos := e.PositionRange() return annotations.New().Add(annotations.NewIncompatibleBucketLayoutInBinOpWarning(op, pos)) } return nil @@ -4132,13 +4257,17 @@ func makeInt64Pointer(val int64) *int64 { // RatioSampler allows unit-testing (previously: Randomizer). type RatioSampler interface { - // Return this sample "offset" between [0.0, 1.0] - sampleOffset(ts int64, sample *Sample) float64 - AddRatioSample(r float64, sample *Sample) bool + // SampleOffset returns this sample "offset" between [0.0, 1.0]. + SampleOffset(metric *labels.Labels) float64 + // AddRatioSample reports whether the sampling offset for the given sample falls within the specified ratio limit. + AddRatioSample(ratioLimit float64, sample *Sample) bool + // AddRatioSampleWithOffset reports whether the given sampling offset falls within the specified ratio limit. + AddRatioSampleWithOffset(ratioLimit, sampleOffset float64) bool } // HashRatioSampler uses Hash(labels.String()) / maxUint64 as a "deterministic" // value in [0.0, 1.0]. +// It is a utility used for limit_ratio aggregations. type HashRatioSampler struct{} var ratiosampler RatioSampler = NewHashRatioSampler() @@ -4147,14 +4276,42 @@ func NewHashRatioSampler() *HashRatioSampler { return &HashRatioSampler{} } -func (*HashRatioSampler) sampleOffset(_ int64, sample *Sample) float64 { +// SampleOffset returns a deterministic sampling offset in the range [0, 1) +// derived from the hash of the provided metric labels. +// +// The offset is computed by normalizing the 64-bit hash value of the label set +// to a float64 fraction of math.MaxUint64. This ensures that metrics with the +// same label set always produce the same offset, while different label sets +// produce uniformly distributed offsets suitable for sampling decisions. +func (*HashRatioSampler) SampleOffset(metric *labels.Labels) float64 { const ( float64MaxUint64 = float64(math.MaxUint64) ) - return float64(sample.Metric.Hash()) / float64MaxUint64 + return float64(metric.Hash()) / float64MaxUint64 } +// AddRatioSample returns a bool indicating if the sampling offset for the given sample is +// within the given ratio limit. +// +// See SampleOffset() for further details on the sample offset. +// See AddRatioSampleWithOffset() for further details on the ratioLimit and sampling offset comparison. func (s *HashRatioSampler) AddRatioSample(ratioLimit float64, sample *Sample) bool { + sampleOffset := s.SampleOffset(&sample.Metric) + return s.AddRatioSampleWithOffset(ratioLimit, sampleOffset) +} + +// AddRatioSampleWithOffset reports whether the given sampling offset falls within +// the specified ratio limit. +// +// The ratioLimit must be in the range [-1, 1]. The sampleOffset should be derived +// using SampleOffset(). +// +// When ratioLimit >= 0, the function returns true if sampleOffset < ratioLimit. +// When ratioLimit < 0, the function returns true if sampleOffset >= 1 + ratioLimit. +// +// Note that this method could be moved into AddRatioSample and removed from the Prometheus codebase, +// but it is useful for downstream projects using this code as a library. +func (*HashRatioSampler) AddRatioSampleWithOffset(ratioLimit, sampleOffset float64) bool { // If ratioLimit >= 0: add sample if sampleOffset is lesser than ratioLimit // // 0.0 ratioLimit 1.0 @@ -4175,7 +4332,6 @@ func (s *HashRatioSampler) AddRatioSample(ratioLimit float64, sample *Sample) bo // e.g.: // sampleOffset==0.3 && ratioLimit==-0.6 // 0.3 >= 0.4 ? --> don't add sample - sampleOffset := s.sampleOffset(sample.T, sample) return (ratioLimit >= 0 && sampleOffset < ratioLimit) || (ratioLimit < 0 && sampleOffset >= (1.0+ratioLimit)) } diff --git a/vendor/github.com/prometheus/prometheus/promql/functions.go b/vendor/github.com/prometheus/prometheus/promql/functions.go index ca8cfdce154..f844bf5ada5 100644 --- a/vendor/github.com/prometheus/prometheus/promql/functions.go +++ b/vendor/github.com/prometheus/prometheus/promql/functions.go @@ -400,6 +400,7 @@ func histogramRate(points []HPoint, isCounter bool, metricName string, pos posra annos.Add(annotations.NewNativeHistogramNotGaugeWarning(metricName, pos)) } + h.CounterResetHint = histogram.GaugeType return h.Compact(0), annos } @@ -1709,15 +1710,19 @@ func funcHistogramQuantile(vectorVals []Vector, _ Matrix, args parser.Expression // pickFirstSampleIndex returns the index of the last sample before // or at the range start, or 0 if none exist before the range start. -// If the vector selector is not anchored, it always returns 0. -func pickFirstSampleIndex(floats []FPoint, args parser.Expressions, enh *EvalNodeHelper) int { +// If the vector selector is not anchored, it always returns 0, true. +// The second return value is false if there are no samples in range (for anchored selectors). +func pickFirstSampleIndex(floats []FPoint, args parser.Expressions, enh *EvalNodeHelper) (int, bool) { ms := args[0].(*parser.MatrixSelector) vs := ms.VectorSelector.(*parser.VectorSelector) if !vs.Anchored { - return 0 + return 0, true } rangeStart := enh.Ts - durationMilliseconds(ms.Range+vs.Offset) - return max(0, sort.Search(len(floats)-1, func(i int) bool { return floats[i].T > rangeStart })-1) + if len(floats) == 0 || floats[len(floats)-1].T <= rangeStart { + return 0, false + } + return max(0, sort.Search(len(floats)-1, func(i int) bool { return floats[i].T > rangeStart })-1), true } // === resets(Matrix parser.ValueTypeMatrix) (Vector, Annotations) === @@ -1730,7 +1735,10 @@ func funcResets(_ []Vector, matrixVal Matrix, args parser.Expressions, enh *Eval } var prevSample, curSample Sample - firstSampleIndex := pickFirstSampleIndex(floats, args, enh) + firstSampleIndex, found := pickFirstSampleIndex(floats, args, enh) + if !found { + return enh.Out, nil + } for iFloat, iHistogram := firstSampleIndex, 0; iFloat < len(floats) || iHistogram < len(histograms); { switch { // Process a float sample if no histogram sample remains or its timestamp is earlier. @@ -1776,7 +1784,10 @@ func funcChanges(_ []Vector, matrixVal Matrix, args parser.Expressions, enh *Eva } var prevSample, curSample Sample - firstSampleIndex := pickFirstSampleIndex(floats, args, enh) + firstSampleIndex, found := pickFirstSampleIndex(floats, args, enh) + if !found { + return enh.Out, nil + } for iFloat, iHistogram := firstSampleIndex, 0; iFloat < len(floats) || iHistogram < len(histograms); { switch { // Process a float sample if no histogram sample remains or its timestamp is earlier. @@ -1848,11 +1859,8 @@ func (ev *evaluator) evalLabelReplace(ctx context.Context, args parser.Expressio } } } - if matrix.ContainsSameLabelset() { - ev.errorf("vector cannot contain metrics with the same labelset") - } - return matrix, ws + return ev.mergeSeriesWithSameLabelset(matrix), ws } // === Vector(s Scalar) (Vector, Annotations) === @@ -1902,11 +1910,8 @@ func (ev *evaluator) evalLabelJoin(ctx context.Context, args parser.Expressions) matrix[i].DropName = el.DropName } } - if matrix.ContainsSameLabelset() { - ev.errorf("vector cannot contain metrics with the same labelset") - } - return matrix, ws + return ev.mergeSeriesWithSameLabelset(matrix), ws } // Common code for date related functions. diff --git a/vendor/github.com/prometheus/prometheus/promql/info.go b/vendor/github.com/prometheus/prometheus/promql/info.go index 0067fce845b..d5ffda6af2b 100644 --- a/vendor/github.com/prometheus/prometheus/promql/info.go +++ b/vendor/github.com/prometheus/prometheus/promql/info.go @@ -337,10 +337,10 @@ func (ev *evaluator) combineWithInfoVector(base, info Vector, ignoreSeries map[u } // All samples from the info Vector hashed by the matching label/values. - if enh.rightSigs == nil { - enh.rightSigs = make(map[string]Sample, len(enh.Out)) + if enh.rightStrSigs == nil { + enh.rightStrSigs = make(map[string]Sample, len(enh.Out)) } else { - clear(enh.rightSigs) + clear(enh.rightStrSigs) } for _, s := range info { @@ -351,7 +351,7 @@ func (ev *evaluator) combineWithInfoVector(base, info Vector, ignoreSeries map[u origT := int64(s.F) sig := infoSigs[s.Metric.Hash()] - if existing, exists := enh.rightSigs[sig]; exists { + if existing, exists := enh.rightStrSigs[sig]; exists { // We encode original info sample timestamps via the float value. existingOrigT := int64(existing.F) switch { @@ -359,14 +359,14 @@ func (ev *evaluator) combineWithInfoVector(base, info Vector, ignoreSeries map[u // Keep the other info sample, since it's newer. case existingOrigT < origT: // Keep this info sample, since it's newer. - enh.rightSigs[sig] = s + enh.rightStrSigs[sig] = s default: // The two info samples have the same timestamp - conflict. ev.errorf("found duplicate series for info metric: existing %s @ %d, new %s @ %d", existing.Metric.String(), existingOrigT, s.Metric.String(), origT) } } else { - enh.rightSigs[sig] = s + enh.rightStrSigs[sig] = s } } @@ -389,7 +389,7 @@ func (ev *evaluator) combineWithInfoVector(base, info Vector, ignoreSeries map[u // For every info metric name, try to find an info series with the same signature. seenInfoMetrics := map[string]struct{}{} for infoName, sig := range baseSigs[hash] { - is, exists := enh.rightSigs[sig] + is, exists := enh.rightStrSigs[sig] if !exists { continue } diff --git a/vendor/github.com/prometheus/prometheus/promql/parser/features.go b/vendor/github.com/prometheus/prometheus/promql/parser/features.go new file mode 100644 index 00000000000..ec64678237f --- /dev/null +++ b/vendor/github.com/prometheus/prometheus/promql/parser/features.go @@ -0,0 +1,57 @@ +// Copyright The Prometheus Authors +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package parser + +import "github.com/prometheus/prometheus/util/features" + +// RegisterFeatures registers all PromQL features with the feature registry. +// This includes operators (arithmetic and comparison/set), aggregators (standard +// and experimental), and functions. +func RegisterFeatures(r features.Collector) { + // Register core PromQL language keywords. + for keyword, itemType := range key { + if itemType.IsKeyword() { + // Handle experimental keywords separately. + switch keyword { + case "anchored", "smoothed": + r.Set(features.PromQL, keyword, EnableExtendedRangeSelectors) + default: + r.Enable(features.PromQL, keyword) + } + } + } + + // Register operators. + for o := ItemType(operatorsStart + 1); o < operatorsEnd; o++ { + if o.IsOperator() { + r.Set(features.PromQLOperators, o.String(), true) + } + } + + // Register aggregators. + for a := ItemType(aggregatorsStart + 1); a < aggregatorsEnd; a++ { + if a.IsAggregator() { + experimental := a.IsExperimentalAggregator() && !EnableExperimentalFunctions + r.Set(features.PromQLOperators, a.String(), !experimental) + } + } + + // Register functions. + for f, fc := range Functions { + r.Set(features.PromQLFunctions, f, !fc.Experimental || EnableExperimentalFunctions) + } + + // Register experimental parser features. + r.Set(features.PromQL, "duration_expr", ExperimentalDurationExpr) +} diff --git a/vendor/github.com/prometheus/prometheus/promql/parser/printer.go b/vendor/github.com/prometheus/prometheus/promql/parser/printer.go index a562b88044d..961167428b0 100644 --- a/vendor/github.com/prometheus/prometheus/promql/parser/printer.go +++ b/vendor/github.com/prometheus/prometheus/promql/parser/printer.go @@ -37,15 +37,16 @@ func tree(node Node, level string) string { } typs := strings.Split(fmt.Sprintf("%T", node), ".")[1] - t := fmt.Sprintf("%s |---- %s :: %s\n", level, typs, node) + var t strings.Builder + t.WriteString(fmt.Sprintf("%s |---- %s :: %s\n", level, typs, node)) level += " · · ·" for e := range ChildrenIter(node) { - t += tree(e, level) + t.WriteString(tree(e, level)) } - return t + return t.String() } func (node *EvalStmt) String() string { @@ -147,12 +148,14 @@ func (node *BinaryExpr) ShortString() string { func (node *BinaryExpr) getMatchingStr() string { matching := "" vm := node.VectorMatching - if vm != nil && (len(vm.MatchingLabels) > 0 || vm.On) { - vmTag := "ignoring" - if vm.On { - vmTag = "on" + if vm != nil { + if len(vm.MatchingLabels) > 0 || vm.On || vm.Card == CardManyToOne || vm.Card == CardOneToMany { + vmTag := "ignoring" + if vm.On { + vmTag = "on" + } + matching = fmt.Sprintf(" %s (%s)", vmTag, strings.Join(vm.MatchingLabels, ", ")) } - matching = fmt.Sprintf(" %s (%s)", vmTag, strings.Join(vm.MatchingLabels, ", ")) if vm.Card == CardManyToOne || vm.Card == CardOneToMany { vmCard := "right" diff --git a/vendor/github.com/prometheus/prometheus/promql/promqltest/test.go b/vendor/github.com/prometheus/prometheus/promql/promqltest/test.go index 41d8cdde20a..d4a11b9e502 100644 --- a/vendor/github.com/prometheus/prometheus/promql/promqltest/test.go +++ b/vendor/github.com/prometheus/prometheus/promql/promqltest/test.go @@ -231,7 +231,7 @@ func raise(line int, format string, v ...any) error { } } -func parseLoad(lines []string, i int) (int, *loadCmd, error) { +func parseLoad(lines []string, i int, startTime time.Time) (int, *loadCmd, error) { if !patLoad.MatchString(lines[i]) { return i, nil, raise(i, "invalid load command. (load[_with_nhcb] )") } @@ -245,6 +245,7 @@ func parseLoad(lines []string, i int) (int, *loadCmd, error) { return i, nil, raise(i, "invalid step definition %q: %s", step, err) } cmd := newLoadCmd(time.Duration(gap), withNHCB) + cmd.startTime = startTime for i+1 < len(lines) { i++ defLine := lines[i] @@ -579,7 +580,7 @@ func (t *test) parse(input string) error { case c == "clear": cmd = &clearCmd{} case strings.HasPrefix(c, "load"): - i, cmd, err = parseLoad(lines, i) + i, cmd, err = parseLoad(lines, i, testStartTime) case strings.HasPrefix(c, "eval"): i, cmd, err = t.parseEval(lines, i) default: @@ -611,6 +612,7 @@ type loadCmd struct { defs map[uint64][]promql.Sample exemplars map[uint64][]exemplar.Exemplar withNHCB bool + startTime time.Time } func newLoadCmd(gap time.Duration, withNHCB bool) *loadCmd { @@ -620,6 +622,7 @@ func newLoadCmd(gap time.Duration, withNHCB bool) *loadCmd { defs: map[uint64][]promql.Sample{}, exemplars: map[uint64][]exemplar.Exemplar{}, withNHCB: withNHCB, + startTime: testStartTime, } } @@ -632,7 +635,7 @@ func (cmd *loadCmd) set(m labels.Labels, vals ...parser.SequenceValue) { h := m.Hash() samples := make([]promql.Sample, 0, len(vals)) - ts := testStartTime + ts := cmd.startTime for _, v := range vals { if !v.Omitted { samples = append(samples, promql.Sample{ @@ -1430,6 +1433,11 @@ func (t *test) execEval(cmd *evalCmd, engine promql.QueryEngine) error { func (t *test) execRangeEval(cmd *evalCmd, engine promql.QueryEngine) error { q, err := engine.NewRangeQuery(t.context, t.storage, nil, cmd.expr, cmd.start, cmd.end, cmd.step) if err != nil { + if cmd.isFail() { + if err := cmd.checkExpectedFailure(err); err == nil { + return nil + } + } return fmt.Errorf("error creating range query for %q (line %d): %w", cmd.expr, cmd.line, err) } defer q.Close() @@ -1473,6 +1481,11 @@ func (t *test) execInstantEval(cmd *evalCmd, engine promql.QueryEngine) error { func (t *test) runInstantQuery(iq atModifierTestCase, cmd *evalCmd, engine promql.QueryEngine) error { q, err := engine.NewInstantQuery(t.context, t.storage, nil, iq.expr, iq.evalTime) if err != nil { + if cmd.isFail() { + if err := cmd.checkExpectedFailure(err); err == nil { + return nil + } + } return fmt.Errorf("error creating instant query for %q (line %d): %w", cmd.expr, cmd.line, err) } defer q.Close() @@ -1617,6 +1630,8 @@ type LazyLoaderOpts struct { // Currently defaults to false, matches the "promql-delayed-name-removal" // feature flag. EnableDelayedNameRemoval bool + // StartTime is the start time for the test. If zero, defaults to Unix epoch. + StartTime time.Time } // NewLazyLoader returns an initialized empty LazyLoader. @@ -1642,7 +1657,12 @@ func (ll *LazyLoader) parse(input string) error { continue } if strings.HasPrefix(strings.ToLower(patSpace.Split(l, 2)[0]), "load") { - _, cmd, err := parseLoad(lines, i) + // Determine the start time to use for loading samples. + startTime := testStartTime + if !ll.opts.StartTime.IsZero() { + startTime = ll.opts.StartTime + } + _, cmd, err := parseLoad(lines, i, startTime) if err != nil { return err } diff --git a/vendor/github.com/prometheus/prometheus/promql/promqltest/testdata/extended_vectors.test b/vendor/github.com/prometheus/prometheus/promql/promqltest/testdata/extended_vectors.test index 8e116b1ac58..8f431dcfd31 100644 --- a/vendor/github.com/prometheus/prometheus/promql/promqltest/testdata/extended_vectors.test +++ b/vendor/github.com/prometheus/prometheus/promql/promqltest/testdata/extended_vectors.test @@ -319,7 +319,6 @@ eval instant at 2m changes(metric[1m] anchored) {id="2"} 1 eval instant at 3m changes(metric[1m] anchored) - {id="1"} 1 {id="2"} 1 eval instant at 8m changes(metric[1m] anchored) @@ -342,7 +341,6 @@ eval instant at 2m resets(metric[1m] anchored) {id="2"} 1 eval instant at 3m resets(metric[1m] anchored) - {id="1"} 1 {id="2"} 1 eval instant at 8m resets(metric[1m] anchored) diff --git a/vendor/github.com/prometheus/prometheus/promql/promqltest/testdata/functions.test b/vendor/github.com/prometheus/prometheus/promql/promqltest/testdata/functions.test index ba3df76ff61..7bc4bcb6244 100644 --- a/vendor/github.com/prometheus/prometheus/promql/promqltest/testdata/functions.test +++ b/vendor/github.com/prometheus/prometheus/promql/promqltest/testdata/functions.test @@ -2014,3 +2014,38 @@ eval instant at 0m scalar({type="histogram"}) # One float in the vector. eval instant at 0m scalar({l="x"}) 1 + +clear +load 20m + series{label="a", idx="1"} 2 _ + series{label="a", idx="2"} _ 4 + +eval instant at 0 label_replace(series, "idx", "replaced", "idx", ".*") + series{label="a", idx="replaced"} 2 + +eval instant at 20m label_replace(series, "idx", "replaced", "idx", ".*") + series{label="a", idx="replaced"} 4 + +eval range from 0 to 20m step 20m label_replace(series, "idx", "replaced", "idx", ".*") + series{label="a", idx="replaced"} 2 4 + +# Test label_join with non-overlapping series. +eval instant at 0 label_join(series, "idx", ",", "label", "label") + series{label="a", idx="a,a"} 2 + +eval instant at 20m label_join(series, "idx", ",", "label", "label") + series{label="a", idx="a,a"} 4 + +eval range from 0 to 20m step 20m label_join(series, "idx", ",", "label", "label") + series{label="a", idx="a,a"} 2 4 + +# Test label_replace failure with overlapping timestamps (same labelset at same time). +clear +load 1m + overlap{label="a", idx="1"} 1 + overlap{label="a", idx="2"} 2 + +eval_fail instant at 0 label_replace(overlap, "idx", "same", "idx", ".*") + +# Test label_join failure with overlapping timestamps (same labelset at same time). +eval_fail instant at 0 label_join(overlap, "idx", ",", "label", "label") diff --git a/vendor/github.com/prometheus/prometheus/promql/promqltest/testdata/name_label_dropping.test b/vendor/github.com/prometheus/prometheus/promql/promqltest/testdata/name_label_dropping.test index 3682021ba93..e0180c7ffe9 100644 --- a/vendor/github.com/prometheus/prometheus/promql/promqltest/testdata/name_label_dropping.test +++ b/vendor/github.com/prometheus/prometheus/promql/promqltest/testdata/name_label_dropping.test @@ -91,3 +91,48 @@ eval instant at 10m topk(10, sum by (__name__, env) (metric_total{env="1"})) eval instant at 10m topk(10, sum by (__name__, env) (rate(metric_total{env="1"}[10m]))) {env="1"} 0.2 + +clear + +# More testing for __name__ label drop with different input series. +load 1m + metric_total{env="1"} 0+1x10 + metric_total{env="2"} 0+3x10 + +# Metric name is preserved as there is no function that drops it. +eval instant at 10m sum by (__name__) (metric_total{env="1"}) + metric_total 10 + +# Metric name is dropped at the end because of rate and because there is no label function to preserve it. +eval instant at 10m sum by (__name__) (rate(metric_total{env="2"}[5m])) + {} 0.05 + +# Metric name is preserved with label_replace even though it would have been dropped with rate. +eval instant at 10m label_replace(sum by (__name__) (rate(metric_total{env="2"}[5m])), "__name__", "$1", "__name__", "(.+)") + metric_total 0.05 + +# Combining the above cases in an OR expression, we drop the name if any of the series drops it. +eval instant at 10m sum by (__name__) (metric_total{env="1"} or rate(metric_total{env="2"}[5m])) + {} 10.05 + +# Changing the order of the OR expression should not change the result. +eval instant at 10m sum by (__name__) (rate(metric_total{env="2"}[5m]) or metric_total{env="1"}) + {} 10.05 + +# With non-matching first selector, we use the second to determine if __name__ is dropped. +eval instant at 10m sum by (__name__) (metric_total{env="3"} or rate(metric_total{env="2"}[5m])) + {} 0.05 + +# Same as above, but with reversed order. +eval instant at 10m sum by (__name__) (rate(metric_total{env="3"}[5m]) or metric_total{env="1"}) + metric_total 10 + +clear + +# Test delayed name removal with range queries and OR operator. +load 10m + metric_a 1 _ + metric_b 3 4 + +eval range from 0 to 20m step 10m -metric_a or -metric_b + {} -1 -4 _ diff --git a/vendor/github.com/prometheus/prometheus/promql/promqltest/testdata/operators.test b/vendor/github.com/prometheus/prometheus/promql/promqltest/testdata/operators.test index 0e779f192cb..cd608b3c36b 100644 --- a/vendor/github.com/prometheus/prometheus/promql/promqltest/testdata/operators.test +++ b/vendor/github.com/prometheus/prometheus/promql/promqltest/testdata/operators.test @@ -316,6 +316,27 @@ eval instant at 5m http_requests_histogram == http_requests_histogram eval instant at 5m http_requests_histogram != http_requests_histogram expect no_info +clear + +# Check that we track many-to-one vector matching errors even when all but 0 or 1 +# series on the "many" side are filtered away. +load 5m + many_side{label="foo",job="test"} 0 + many_side{label="bar",job="test"} 1 + one_side{job="test"} 1 + +# Check 0 series surviving the filtering producing an error. +eval instant at 0m many_side > on(job) one_side + expect fail + +# Check 1 series surviving the filtering producing an error. +eval instant at 0m many_side >= on(job) one_side + expect fail + +# Check 2 series surviving the filtering producing an error. +eval instant at 0m many_side <= on(job) one_side + expect fail + # group_left/group_right. clear @@ -959,3 +980,40 @@ eval instant at 10m (testhistogram) and on() (vector(-1) == 1) eval range from 0 to 10m step 5m (testhistogram) and on() (vector(-1) == 1) clear + +# Test unary negation with non-overlapping series that have different metric names. +# After negation, the __name__ label is dropped, so series with different names +# but same other labels should merge if they don't overlap in time. +load 20m + http_requests{job="api"} 2 _ + http_errors{job="api"} _ 4 + +eval instant at 0 -{job="api"} + {job="api"} -2 + +eval instant at 20m -{job="api"} + {job="api"} -4 + +eval range from 0 to 20m step 20m -{job="api"} + {job="api"} -2 -4 + +# Test unary negation failure with overlapping timestamps (same labelset at same time). +clear +load 1m + http_requests{job="api"} 1 + http_errors{job="api"} 2 + +eval_fail instant at 0 -{job="api"} + +clear + +# Test unary negation with "or" operator combining metrics with removed names. +load 10m + metric_a 1 _ + metric_b 3 4 + +# Use "-" unary operator as a simple way to remove the metric name. +eval range from 0 to 20m step 10m -metric_a or -metric_b + {} -1 -4 + +clear diff --git a/vendor/github.com/prometheus/prometheus/rules/alerting.go b/vendor/github.com/prometheus/prometheus/rules/alerting.go index b0151d7cb38..bb0763fbc64 100644 --- a/vendor/github.com/prometheus/prometheus/rules/alerting.go +++ b/vendor/github.com/prometheus/prometheus/rules/alerting.go @@ -46,6 +46,10 @@ const ( alertStateLabel = "alertstate" ) +// ErrDuplicateAlertLabelSet is returned when an alerting rule evaluation produces +// metrics with identical labelsets after applying alert labels. +var ErrDuplicateAlertLabelSet = errors.New("vector contains metrics with the same labelset after applying alert labels") + // AlertState denotes the state of an active alert. type AlertState int @@ -441,7 +445,7 @@ func (r *AlertingRule) Eval(ctx context.Context, queryOffset time.Duration, ts t resultFPs[h] = struct{}{} if _, ok := alerts[h]; ok { - return nil, errors.New("vector contains metrics with the same labelset after applying alert labels") + return nil, ErrDuplicateAlertLabelSet } alerts[h] = &Alert{ diff --git a/vendor/github.com/prometheus/prometheus/rules/group.go b/vendor/github.com/prometheus/prometheus/rules/group.go index 8cedcd40d16..47afe6f7158 100644 --- a/vendor/github.com/prometheus/prometheus/rules/group.go +++ b/vendor/github.com/prometheus/prometheus/rules/group.go @@ -519,6 +519,7 @@ func (g *Group) Eval(ctx context.Context, ts time.Time) { since := time.Since(t) g.metrics.EvalDuration.Observe(since.Seconds()) + g.metrics.EvalDurationHistogram.Observe(since.Seconds()) rule.SetEvaluationDuration(since) rule.SetEvaluationTimestamp(t) }(time.Now()) @@ -910,19 +911,21 @@ const namespace = "prometheus" // Metrics for rule evaluation. type Metrics struct { - EvalDuration prometheus.Summary - IterationDuration prometheus.Summary - IterationsMissed *prometheus.CounterVec - IterationsScheduled *prometheus.CounterVec - EvalTotal *prometheus.CounterVec - EvalFailures *prometheus.CounterVec - GroupInterval *prometheus.GaugeVec - GroupLastEvalTime *prometheus.GaugeVec - GroupLastDuration *prometheus.GaugeVec - GroupLastRuleDurationSum *prometheus.GaugeVec - GroupLastRestoreDuration *prometheus.GaugeVec - GroupRules *prometheus.GaugeVec - GroupSamples *prometheus.GaugeVec + EvalDuration prometheus.Summary + EvalDurationHistogram prometheus.Histogram + IterationDuration prometheus.Summary + IterationDurationHistogram prometheus.Histogram + IterationsMissed *prometheus.CounterVec + IterationsScheduled *prometheus.CounterVec + EvalTotal *prometheus.CounterVec + EvalFailures *prometheus.CounterVec + GroupInterval *prometheus.GaugeVec + GroupLastEvalTime *prometheus.GaugeVec + GroupLastDuration *prometheus.GaugeVec + GroupLastRuleDurationSum *prometheus.GaugeVec + GroupLastRestoreDuration *prometheus.GaugeVec + GroupRules *prometheus.GaugeVec + GroupSamples *prometheus.GaugeVec } // NewGroupMetrics creates a new instance of Metrics and registers it with the provided registerer, @@ -936,12 +939,30 @@ func NewGroupMetrics(reg prometheus.Registerer) *Metrics { Help: "The duration for a rule to execute.", Objectives: map[float64]float64{0.5: 0.05, 0.9: 0.01, 0.99: 0.001}, }), + EvalDurationHistogram: prometheus.NewHistogram(prometheus.HistogramOpts{ + Namespace: namespace, + Name: "rule_evaluation_duration_histogram_seconds", + Help: "The duration for a rule to execute.", + Buckets: []float64{.01, .1, 1, 10}, + NativeHistogramBucketFactor: 1.1, + NativeHistogramMaxBucketNumber: 100, + NativeHistogramMinResetDuration: 1 * time.Hour, + }), IterationDuration: prometheus.NewSummary(prometheus.SummaryOpts{ Namespace: namespace, Name: "rule_group_duration_seconds", Help: "The duration of rule group evaluations.", Objectives: map[float64]float64{0.01: 0.001, 0.05: 0.005, 0.5: 0.05, 0.90: 0.01, 0.99: 0.001}, }), + IterationDurationHistogram: prometheus.NewHistogram(prometheus.HistogramOpts{ + Namespace: namespace, + Name: "rule_group_duration_histogram_seconds", + Help: "The duration of rule group evaluations.", + Buckets: []float64{.01, .1, 1, 10}, + NativeHistogramBucketFactor: 1.1, + NativeHistogramMaxBucketNumber: 100, + NativeHistogramMinResetDuration: 1 * time.Hour, + }), IterationsMissed: prometheus.NewCounterVec( prometheus.CounterOpts{ Namespace: namespace, @@ -1035,7 +1056,9 @@ func NewGroupMetrics(reg prometheus.Registerer) *Metrics { if reg != nil { reg.MustRegister( m.EvalDuration, + m.EvalDurationHistogram, m.IterationDuration, + m.IterationDurationHistogram, m.IterationsMissed, m.IterationsScheduled, m.EvalTotal, diff --git a/vendor/github.com/prometheus/prometheus/rules/manager.go b/vendor/github.com/prometheus/prometheus/rules/manager.go index d2fb0a77974..d610c154be2 100644 --- a/vendor/github.com/prometheus/prometheus/rules/manager.go +++ b/vendor/github.com/prometheus/prometheus/rules/manager.go @@ -37,6 +37,7 @@ import ( "github.com/prometheus/prometheus/promql" "github.com/prometheus/prometheus/promql/parser" "github.com/prometheus/prometheus/storage" + "github.com/prometheus/prometheus/util/features" "github.com/prometheus/prometheus/util/strutil" ) @@ -85,6 +86,7 @@ func DefaultEvalIterationFunc(ctx context.Context, g *Group, evalTimestamp time. timeSinceStart := time.Since(start) g.metrics.IterationDuration.Observe(timeSinceStart.Seconds()) + g.metrics.IterationDurationHistogram.Observe(timeSinceStart.Seconds()) g.updateRuleEvaluationTimeSum() g.setEvaluationTime(timeSinceStart) g.setLastEvaluation(start) @@ -133,6 +135,9 @@ type ManagerOptions struct { RestoreNewRuleGroups bool Metrics *Metrics + + // FeatureRegistry is used to register rule manager features. + FeatureRegistry features.Collector } // NewManager returns an implementation of Manager, ready to be started @@ -173,6 +178,13 @@ func NewManager(o *ManagerOptions) *Manager { o.Logger = promslog.NewNopLogger() } + // Register rule manager features if a registry is provided. + if o.FeatureRegistry != nil { + o.FeatureRegistry.Set(features.Rules, "concurrent_rule_eval", o.ConcurrentEvalsEnabled) + o.FeatureRegistry.Enable(features.Rules, "query_offset") + o.FeatureRegistry.Enable(features.Rules, "keep_firing_for") + } + m := &Manager{ groups: map[string]*Group{}, opts: o, diff --git a/vendor/github.com/prometheus/prometheus/rules/recording.go b/vendor/github.com/prometheus/prometheus/rules/recording.go index 2da6885f5b9..1bc41b834ad 100644 --- a/vendor/github.com/prometheus/prometheus/rules/recording.go +++ b/vendor/github.com/prometheus/prometheus/rules/recording.go @@ -30,6 +30,10 @@ import ( "github.com/prometheus/prometheus/promql/parser" ) +// ErrDuplicateRecordingLabelSet is returned when a recording rule evaluation produces +// metrics with identical labelsets after applying rule labels. +var ErrDuplicateRecordingLabelSet = errors.New("vector contains metrics with the same labelset after applying rule labels") + // A RecordingRule records its vector expression into new timeseries. type RecordingRule struct { name string @@ -104,7 +108,7 @@ func (rule *RecordingRule) Eval(ctx context.Context, queryOffset time.Duration, // Check that the rule does not produce identical metrics after applying // labels. if vector.ContainsSameLabelset() { - return nil, errors.New("vector contains metrics with the same labelset after applying rule labels") + return nil, ErrDuplicateRecordingLabelSet } numSeries := len(vector) diff --git a/vendor/github.com/prometheus/prometheus/schema/labels.go b/vendor/github.com/prometheus/prometheus/schema/labels.go index 6df74451711..05329af7f6a 100644 --- a/vendor/github.com/prometheus/prometheus/schema/labels.go +++ b/vendor/github.com/prometheus/prometheus/schema/labels.go @@ -19,20 +19,10 @@ import ( "github.com/prometheus/prometheus/model/labels" ) -const ( - // Special label names and selectors for schema.Metadata fields. - // They are currently private to ensure __name__, __type__ and __unit__ are used - // together and remain extensible in Prometheus. See NewMetadataFromLabels and Metadata - // methods for the interactions with the labels package structs. - metricName = "__name__" - metricType = "__type__" - metricUnit = "__unit__" -) - // IsMetadataLabel returns true if the given label name is a special // schema Metadata label. func IsMetadataLabel(name string) bool { - return name == metricName || name == metricType || name == metricUnit + return name == model.MetricNameLabel || name == model.MetricTypeLabel || name == model.MetricUnitLabel } // Metadata represents the core metric schema/metadata elements that: @@ -79,13 +69,13 @@ type Metadata struct { // NewMetadataFromLabels returns the schema metadata from the labels. func NewMetadataFromLabels(ls labels.Labels) Metadata { typ := model.MetricTypeUnknown - if got := ls.Get(metricType); got != "" { + if got := ls.Get(model.MetricTypeLabel); got != "" { typ = model.MetricType(got) } return Metadata{ - Name: ls.Get(metricName), + Name: ls.Get(model.MetricNameLabel), Type: typ, - Unit: ls.Get(metricUnit), + Unit: ls.Get(model.MetricUnitLabel), } } @@ -99,11 +89,11 @@ func (m Metadata) IsTypeEmpty() bool { // IsEmptyFor returns true. func (m Metadata) IsEmptyFor(labelName string) bool { switch labelName { - case metricName: + case model.MetricNameLabel: return m.Name == "" - case metricType: + case model.MetricTypeLabel: return m.IsTypeEmpty() - case metricUnit: + case model.MetricUnitLabel: return m.Unit == "" default: return true @@ -114,13 +104,13 @@ func (m Metadata) IsEmptyFor(labelName string) bool { // Empty Metadata fields will be ignored (not added). func (m Metadata) AddToLabels(b *labels.ScratchBuilder) { if m.Name != "" { - b.Add(metricName, m.Name) + b.Add(model.MetricNameLabel, m.Name) } if !m.IsTypeEmpty() { - b.Add(metricType, string(m.Type)) + b.Add(model.MetricTypeLabel, string(m.Type)) } if m.Unit != "" { - b.Add(metricUnit, m.Unit) + b.Add(model.MetricUnitLabel, m.Unit) } } @@ -128,15 +118,15 @@ func (m Metadata) AddToLabels(b *labels.ScratchBuilder) { // It follows the labels.Builder.Set semantics, so empty Metadata fields will // remove the corresponding existing labels if they were previously set. func (m Metadata) SetToLabels(b *labels.Builder) { - b.Set(metricName, m.Name) + b.Set(model.MetricNameLabel, m.Name) if m.Type == model.MetricTypeUnknown { // Unknown equals empty semantically, so remove the label on unknown too as per // method signature comment. - b.Set(metricType, "") + b.Set(model.MetricTypeLabel, "") } else { - b.Set(metricType, string(m.Type)) + b.Set(model.MetricTypeLabel, string(m.Type)) } - b.Set(metricUnit, m.Unit) + b.Set(model.MetricUnitLabel, m.Unit) } // NewIgnoreOverriddenMetadataLabelScratchBuilder creates IgnoreOverriddenMetadataLabelScratchBuilder. diff --git a/vendor/github.com/prometheus/prometheus/scrape/manager.go b/vendor/github.com/prometheus/prometheus/scrape/manager.go index 7389f24b523..9bb6988df9e 100644 --- a/vendor/github.com/prometheus/prometheus/scrape/manager.go +++ b/vendor/github.com/prometheus/prometheus/scrape/manager.go @@ -33,6 +33,7 @@ import ( "github.com/prometheus/prometheus/discovery/targetgroup" "github.com/prometheus/prometheus/model/labels" "github.com/prometheus/prometheus/storage" + "github.com/prometheus/prometheus/util/features" "github.com/prometheus/prometheus/util/logging" "github.com/prometheus/prometheus/util/osutil" "github.com/prometheus/prometheus/util/pool" @@ -67,6 +68,13 @@ func NewManager(o *Options, logger *slog.Logger, newScrapeFailureLogger func(str m.metrics.setTargetMetadataCacheGatherer(m) + // Register scrape features. + if r := o.FeatureRegistry; r != nil { + r.Set(features.Scrape, "extra_scrape_metrics", o.ExtraMetrics) + r.Set(features.Scrape, "start_timestamp_zero_ingestion", o.EnableStartTimestampZeroIngestion) + r.Set(features.Scrape, "type_and_unit_labels", o.EnableTypeAndUnitLabels) + } + return m, nil } @@ -85,7 +93,7 @@ type Options struct { DiscoveryReloadInterval model.Duration // Option to enable the ingestion of the created timestamp as a synthetic zero sample. // See: https://github.com/prometheus/proposals/blob/main/proposals/2023-06-13_created-timestamp.md - EnableCreatedTimestampZeroIngestion bool + EnableStartTimestampZeroIngestion bool // EnableTypeAndUnitLabels EnableTypeAndUnitLabels bool @@ -93,6 +101,9 @@ type Options struct { // Optional HTTP client options to use when scraping. HTTPClientOptions []config_util.HTTPClientOption + // FeatureRegistry is the registry for tracking enabled/disabled features. + FeatureRegistry features.Collector + // private option for testability. skipOffsetting bool } diff --git a/vendor/github.com/prometheus/prometheus/scrape/metrics.go b/vendor/github.com/prometheus/prometheus/scrape/metrics.go index e7395c6191c..634c52fb2d7 100644 --- a/vendor/github.com/prometheus/prometheus/scrape/metrics.go +++ b/vendor/github.com/prometheus/prometheus/scrape/metrics.go @@ -15,6 +15,7 @@ package scrape import ( "fmt" + "time" "github.com/prometheus/client_golang/prometheus" ) @@ -36,6 +37,7 @@ type scrapeMetrics struct { targetScrapePoolTargetsAdded *prometheus.GaugeVec targetScrapePoolSymbolTableItems *prometheus.GaugeVec targetSyncIntervalLength *prometheus.SummaryVec + targetSyncIntervalLengthHistogram *prometheus.HistogramVec targetSyncFailed *prometheus.CounterVec // Used by targetScraper. @@ -46,6 +48,7 @@ type scrapeMetrics struct { // Used by scrapeLoop. targetIntervalLength *prometheus.SummaryVec + targetIntervalLengthHistogram *prometheus.HistogramVec targetScrapeSampleLimit prometheus.Counter targetScrapeSampleDuplicate prometheus.Counter targetScrapeSampleOutOfOrder prometheus.Counter @@ -152,6 +155,17 @@ func newScrapeMetrics(reg prometheus.Registerer) (*scrapeMetrics, error) { }, []string{"scrape_job"}, ) + sm.targetSyncIntervalLengthHistogram = prometheus.NewHistogramVec( + prometheus.HistogramOpts{ + Name: "prometheus_target_sync_length_histogram_seconds", + Help: "Actual interval to sync the scrape pool.", + Buckets: []float64{.01, .1, 1, 10}, + NativeHistogramBucketFactor: 1.1, + NativeHistogramMaxBucketNumber: 100, + NativeHistogramMinResetDuration: 1 * time.Hour, + }, + []string{"scrape_job"}, + ) sm.targetSyncFailed = prometheus.NewCounterVec( prometheus.CounterOpts{ Name: "prometheus_target_sync_failed_total", @@ -185,6 +199,17 @@ func newScrapeMetrics(reg prometheus.Registerer) (*scrapeMetrics, error) { }, []string{"interval"}, ) + sm.targetIntervalLengthHistogram = prometheus.NewHistogramVec( + prometheus.HistogramOpts{ + Name: "prometheus_target_interval_length_histogram_seconds", + Help: "Actual intervals between scrapes.", + Buckets: []float64{.01, .1, 1, 10}, + NativeHistogramBucketFactor: 1.1, + NativeHistogramMaxBucketNumber: 100, + NativeHistogramMinResetDuration: 1 * time.Hour, + }, + []string{"interval"}, + ) sm.targetScrapeSampleLimit = prometheus.NewCounter( prometheus.CounterOpts{ Name: "prometheus_target_scrapes_exceeded_sample_limit_total", @@ -238,6 +263,7 @@ func newScrapeMetrics(reg prometheus.Registerer) (*scrapeMetrics, error) { sm.targetScrapePoolReloads, sm.targetScrapePoolReloadsFailed, sm.targetSyncIntervalLength, + sm.targetSyncIntervalLengthHistogram, sm.targetScrapePoolSyncsCounter, sm.targetScrapePoolExceededTargetLimit, sm.targetScrapePoolTargetLimit, @@ -250,6 +276,7 @@ func newScrapeMetrics(reg prometheus.Registerer) (*scrapeMetrics, error) { sm.targetScrapeCacheFlushForced, // Used by scrapeLoop. sm.targetIntervalLength, + sm.targetIntervalLengthHistogram, sm.targetScrapeSampleLimit, sm.targetScrapeSampleDuplicate, sm.targetScrapeSampleOutOfOrder, @@ -279,6 +306,7 @@ func (sm *scrapeMetrics) Unregister() { sm.reg.Unregister(sm.targetScrapePoolReloads) sm.reg.Unregister(sm.targetScrapePoolReloadsFailed) sm.reg.Unregister(sm.targetSyncIntervalLength) + sm.reg.Unregister(sm.targetSyncIntervalLengthHistogram) sm.reg.Unregister(sm.targetScrapePoolSyncsCounter) sm.reg.Unregister(sm.targetScrapePoolExceededTargetLimit) sm.reg.Unregister(sm.targetScrapePoolTargetLimit) @@ -288,6 +316,7 @@ func (sm *scrapeMetrics) Unregister() { sm.reg.Unregister(sm.targetScrapeExceededBodySizeLimit) sm.reg.Unregister(sm.targetScrapeCacheFlushForced) sm.reg.Unregister(sm.targetIntervalLength) + sm.reg.Unregister(sm.targetIntervalLengthHistogram) sm.reg.Unregister(sm.targetScrapeSampleLimit) sm.reg.Unregister(sm.targetScrapeSampleDuplicate) sm.reg.Unregister(sm.targetScrapeSampleOutOfOrder) diff --git a/vendor/github.com/prometheus/prometheus/scrape/scrape.go b/vendor/github.com/prometheus/prometheus/scrape/scrape.go index 09652d04849..a8cd15d30c4 100644 --- a/vendor/github.com/prometheus/prometheus/scrape/scrape.go +++ b/vendor/github.com/prometheus/prometheus/scrape/scrape.go @@ -215,7 +215,7 @@ func newScrapePool(cfg *config.ScrapeConfig, app storage.Appendable, offsetSeed opts.alwaysScrapeClassicHist, opts.convertClassicHistToNHCB, cfg.ScrapeNativeHistogramsEnabled(), - options.EnableCreatedTimestampZeroIngestion, + options.EnableStartTimestampZeroIngestion, options.EnableTypeAndUnitLabels, options.ExtraMetrics, options.AppendMetadata, @@ -309,6 +309,7 @@ func (sp *scrapePool) stop() { sp.metrics.targetScrapePoolTargetsAdded.DeleteLabelValues(sp.config.JobName) sp.metrics.targetScrapePoolSymbolTableItems.DeleteLabelValues(sp.config.JobName) sp.metrics.targetSyncIntervalLength.DeleteLabelValues(sp.config.JobName) + sp.metrics.targetSyncIntervalLengthHistogram.DeleteLabelValues(sp.config.JobName) sp.metrics.targetSyncFailed.DeleteLabelValues(sp.config.JobName) } } @@ -505,6 +506,9 @@ func (sp *scrapePool) Sync(tgs []*targetgroup.Group) { sp.metrics.targetSyncIntervalLength.WithLabelValues(sp.config.JobName).Observe( time.Since(start).Seconds(), ) + sp.metrics.targetSyncIntervalLengthHistogram.WithLabelValues(sp.config.JobName).Observe( + time.Since(start).Seconds(), + ) sp.metrics.targetScrapePoolSyncsCounter.WithLabelValues(sp.config.JobName).Inc() } @@ -712,13 +716,11 @@ func mutateSampleLabels(lset labels.Labels, target *Target, honor bool, rc []*re } } - res := lb.Labels() - - if len(rc) > 0 { - res, _ = relabel.Process(res, rc...) + if keep := relabel.ProcessBuilder(lb, rc...); !keep { + return labels.EmptyLabels() } - return res + return lb.Labels() } func resolveConflictingExposedLabels(lb *labels.Builder, conflictingExposedLabels []labels.Label) { @@ -951,7 +953,7 @@ type scrapeLoop struct { alwaysScrapeClassicHist bool convertClassicHistToNHCB bool - enableCTZeroIngestion bool + enableSTZeroIngestion bool enableTypeAndUnitLabels bool fallbackScrapeProtocol string @@ -996,11 +998,9 @@ type scrapeCache struct { // be a pointer so we can update it. droppedSeries map[string]*uint64 - // seriesCur and seriesPrev store the labels of series that were seen - // in the current and previous scrape. - // We hold two maps and swap them out to save allocations. - seriesCur map[uint64]*cacheEntry - seriesPrev map[uint64]*cacheEntry + // Series that were seen in the current and previous scrape, for staleness detection. + seriesCur map[storage.SeriesRef]*cacheEntry + seriesPrev map[storage.SeriesRef]*cacheEntry // TODO(bwplotka): Consider moving Metadata API to use WAL instead of scrape loop to // avoid locking (using metadata API can block scraping). @@ -1027,8 +1027,8 @@ func newScrapeCache(metrics *scrapeMetrics) *scrapeCache { return &scrapeCache{ series: map[string]*cacheEntry{}, droppedSeries: map[string]*uint64{}, - seriesCur: map[uint64]*cacheEntry{}, - seriesPrev: map[uint64]*cacheEntry{}, + seriesCur: map[storage.SeriesRef]*cacheEntry{}, + seriesPrev: map[storage.SeriesRef]*cacheEntry{}, metadata: map[string]*metaEntry{}, metrics: metrics, } @@ -1076,13 +1076,9 @@ func (c *scrapeCache) iterDone(flushCache bool) { c.metaMtx.Unlock() } - // Swap current and previous series. + // Swap current and previous series then clear the new current, to save allocations. c.seriesPrev, c.seriesCur = c.seriesCur, c.seriesPrev - - // We have to delete every single key in the map. - for k := range c.seriesCur { - delete(c.seriesCur, k) - } + clear(c.seriesCur) c.iter++ } @@ -1119,13 +1115,13 @@ func (c *scrapeCache) getDropped(met []byte) bool { return ok } -func (c *scrapeCache) trackStaleness(hash uint64, ce *cacheEntry) { - c.seriesCur[hash] = ce +func (c *scrapeCache) trackStaleness(ref storage.SeriesRef, ce *cacheEntry) { + c.seriesCur[ref] = ce } func (c *scrapeCache) forEachStale(f func(storage.SeriesRef, labels.Labels) bool) { - for h, ce := range c.seriesPrev { - if _, ok := c.seriesCur[h]; !ok { + for ref, ce := range c.seriesPrev { + if _, ok := c.seriesCur[ref]; !ok { if !f(ce.ref, ce.lset) { break } @@ -1264,7 +1260,7 @@ func newScrapeLoop(ctx context.Context, alwaysScrapeClassicHist bool, convertClassicHistToNHCB bool, enableNativeHistogramScraping bool, - enableCTZeroIngestion bool, + enableSTZeroIngestion bool, enableTypeAndUnitLabels bool, reportExtraMetrics bool, appendMetadataToWAL bool, @@ -1321,7 +1317,7 @@ func newScrapeLoop(ctx context.Context, timeout: timeout, alwaysScrapeClassicHist: alwaysScrapeClassicHist, convertClassicHistToNHCB: convertClassicHistToNHCB, - enableCTZeroIngestion: enableCTZeroIngestion, + enableSTZeroIngestion: enableSTZeroIngestion, enableTypeAndUnitLabels: enableTypeAndUnitLabels, fallbackScrapeProtocol: fallbackScrapeProtocol, enableNativeHistogramScraping: enableNativeHistogramScraping, @@ -1426,6 +1422,9 @@ func (sl *scrapeLoop) scrapeAndReport(last, appendTime time.Time, errc chan<- er sl.metrics.targetIntervalLength.WithLabelValues(sl.interval.String()).Observe( time.Since(last).Seconds(), ) + sl.metrics.targetIntervalLengthHistogram.WithLabelValues(sl.interval.String()).Observe( + time.Since(last).Seconds(), + ) } var total, added, seriesAdded, bytesRead int @@ -1458,7 +1457,10 @@ func (sl *scrapeLoop) scrapeAndReport(last, appendTime time.Time, errc chan<- er sl.l.Warn("Append failed", "err", err) } if errc != nil { - errc <- forcedErr + select { + case errc <- forcedErr: + case <-sl.ctx.Done(): + } } return start @@ -1495,7 +1497,10 @@ func (sl *scrapeLoop) scrapeAndReport(last, appendTime time.Time, errc chan<- er } sl.scrapeFailureLoggerMtx.RUnlock() if errc != nil { - errc <- scrapeErr + select { + case errc <- scrapeErr: + case <-sl.ctx.Done(): + } } if errors.Is(scrapeErr, errBodySizeLimit) { bytesRead = -1 @@ -1660,7 +1665,7 @@ func (sl *scrapeLoop) append(app storage.Appender, b []byte, contentType string, IgnoreNativeHistograms: !sl.enableNativeHistogramScraping, ConvertClassicHistogramsToNHCB: sl.convertClassicHistToNHCB, KeepClassicOnClassicAndNativeHistograms: sl.alwaysScrapeClassicHist, - OpenMetricsSkipCTSeries: sl.enableCTZeroIngestion, + OpenMetricsSkipSTSeries: sl.enableSTZeroIngestion, FallbackContentType: sl.fallbackScrapeProtocol, }) if p == nil { @@ -1801,21 +1806,21 @@ loop: if seriesAlreadyScraped && parsedTimestamp == nil { err = storage.ErrDuplicateSampleForTimestamp } else { - if sl.enableCTZeroIngestion { - if ctMs := p.CreatedTimestamp(); ctMs != 0 { + if sl.enableSTZeroIngestion { + if stMs := p.StartTimestamp(); stMs != 0 { if isHistogram { if h != nil { - ref, err = app.AppendHistogramCTZeroSample(ref, lset, t, ctMs, h, nil) + ref, err = app.AppendHistogramSTZeroSample(ref, lset, t, stMs, h, nil) } else { - ref, err = app.AppendHistogramCTZeroSample(ref, lset, t, ctMs, nil, fh) + ref, err = app.AppendHistogramSTZeroSample(ref, lset, t, stMs, nil, fh) } } else { - ref, err = app.AppendCTZeroSample(ref, lset, t, ctMs) + ref, err = app.AppendSTZeroSample(ref, lset, t, stMs) } - if err != nil && !errors.Is(err, storage.ErrOutOfOrderCT) { // OOO is a common case, ignoring completely for now. - // CT is an experimental feature. For now, we don't need to fail the + if err != nil && !errors.Is(err, storage.ErrOutOfOrderST) { // OOO is a common case, ignoring completely for now. + // ST is an experimental feature. For now, we don't need to fail the // scrape on errors updating the created timestamp, log debug. - sl.l.Debug("Error when appending CT in scrape loop", "series", string(met), "ct", ctMs, "t", t, "err", err) + sl.l.Debug("Error when appending ST in scrape loop", "series", string(met), "ct", stMs, "t", t, "err", err) } } } @@ -1833,7 +1838,7 @@ loop: if err == nil { if (parsedTimestamp == nil || sl.trackTimestampsStaleness) && ce != nil { - sl.cache.trackStaleness(ce.hash, ce) + sl.cache.trackStaleness(ce.ref, ce) } } @@ -1854,7 +1859,7 @@ loop: if ce != nil && (parsedTimestamp == nil || sl.trackTimestampsStaleness) { // Bypass staleness logic if there is an explicit timestamp. // But make sure we only do this if we have a cache entry (ce) for our series. - sl.cache.trackStaleness(hash, ce) + sl.cache.trackStaleness(ref, ce) } if sampleAdded && sampleLimitErr == nil && bucketLimitErr == nil { seriesAdded++ @@ -1913,7 +1918,7 @@ loop: if !seriesCached || lastMeta.lastIterChange == sl.cache.iter { // In majority cases we can trust that the current series/histogram is matching the lastMeta and lastMFName. // However, optional TYPE etc metadata and broken OM text can break this, detect those cases here. - // TODO(bwplotka): Consider moving this to parser as many parser users end up doing this (e.g. CT and NHCB parsing). + // TODO(bwplotka): Consider moving this to parser as many parser users end up doing this (e.g. ST and NHCB parsing). if isSeriesPartOfFamily(lset.Get(labels.MetricName), lastMFName, lastMeta.Type) { if _, merr := app.UpdateMetadata(ref, lset, lastMeta.Metadata); merr != nil { // No need to fail the scrape on errors appending metadata. diff --git a/vendor/github.com/prometheus/prometheus/scrape/target.go b/vendor/github.com/prometheus/prometheus/scrape/target.go index 563fe33f82f..2aabff20e2c 100644 --- a/vendor/github.com/prometheus/prometheus/scrape/target.go +++ b/vendor/github.com/prometheus/prometheus/scrape/target.go @@ -389,6 +389,7 @@ type bucketLimitAppender struct { } func (app *bucketLimitAppender) AppendHistogram(ref storage.SeriesRef, lset labels.Labels, t int64, h *histogram.Histogram, fh *histogram.FloatHistogram) (storage.SeriesRef, error) { + var err error if h != nil { // Return with an early error if the histogram has too many buckets and the // schema is not exponential, in which case we can't reduce the resolution. @@ -399,7 +400,9 @@ func (app *bucketLimitAppender) AppendHistogram(ref storage.SeriesRef, lset labe if h.Schema <= histogram.ExponentialSchemaMin { return 0, errBucketLimit } - h = h.ReduceResolution(h.Schema - 1) + if err = h.ReduceResolution(h.Schema - 1); err != nil { + return 0, err + } } } if fh != nil { @@ -412,11 +415,12 @@ func (app *bucketLimitAppender) AppendHistogram(ref storage.SeriesRef, lset labe if fh.Schema <= histogram.ExponentialSchemaMin { return 0, errBucketLimit } - fh = fh.ReduceResolution(fh.Schema - 1) + if err = fh.ReduceResolution(fh.Schema - 1); err != nil { + return 0, err + } } } - ref, err := app.Appender.AppendHistogram(ref, lset, t, h, fh) - if err != nil { + if ref, err = app.Appender.AppendHistogram(ref, lset, t, h, fh); err != nil { return 0, err } return ref, nil @@ -429,18 +433,22 @@ type maxSchemaAppender struct { } func (app *maxSchemaAppender) AppendHistogram(ref storage.SeriesRef, lset labels.Labels, t int64, h *histogram.Histogram, fh *histogram.FloatHistogram) (storage.SeriesRef, error) { + var err error if h != nil { if histogram.IsExponentialSchemaReserved(h.Schema) && h.Schema > app.maxSchema { - h = h.ReduceResolution(app.maxSchema) + if err = h.ReduceResolution(app.maxSchema); err != nil { + return 0, err + } } } if fh != nil { if histogram.IsExponentialSchemaReserved(fh.Schema) && fh.Schema > app.maxSchema { - fh = fh.ReduceResolution(app.maxSchema) + if err = fh.ReduceResolution(app.maxSchema); err != nil { + return 0, err + } } } - ref, err := app.Appender.AppendHistogram(ref, lset, t, h, fh) - if err != nil { + if ref, err = app.Appender.AppendHistogram(ref, lset, t, h, fh); err != nil { return 0, err } return ref, nil diff --git a/vendor/github.com/prometheus/prometheus/storage/fanout.go b/vendor/github.com/prometheus/prometheus/storage/fanout.go index f99edb473a0..a699a97b023 100644 --- a/vendor/github.com/prometheus/prometheus/storage/fanout.go +++ b/vendor/github.com/prometheus/prometheus/storage/fanout.go @@ -199,14 +199,14 @@ func (f *fanoutAppender) AppendHistogram(ref SeriesRef, l labels.Labels, t int64 return ref, nil } -func (f *fanoutAppender) AppendHistogramCTZeroSample(ref SeriesRef, l labels.Labels, t, ct int64, h *histogram.Histogram, fh *histogram.FloatHistogram) (SeriesRef, error) { - ref, err := f.primary.AppendHistogramCTZeroSample(ref, l, t, ct, h, fh) +func (f *fanoutAppender) AppendHistogramSTZeroSample(ref SeriesRef, l labels.Labels, t, st int64, h *histogram.Histogram, fh *histogram.FloatHistogram) (SeriesRef, error) { + ref, err := f.primary.AppendHistogramSTZeroSample(ref, l, t, st, h, fh) if err != nil { return ref, err } for _, appender := range f.secondaries { - if _, err := appender.AppendHistogramCTZeroSample(ref, l, t, ct, h, fh); err != nil { + if _, err := appender.AppendHistogramSTZeroSample(ref, l, t, st, h, fh); err != nil { return 0, err } } @@ -227,14 +227,14 @@ func (f *fanoutAppender) UpdateMetadata(ref SeriesRef, l labels.Labels, m metada return ref, nil } -func (f *fanoutAppender) AppendCTZeroSample(ref SeriesRef, l labels.Labels, t, ct int64) (SeriesRef, error) { - ref, err := f.primary.AppendCTZeroSample(ref, l, t, ct) +func (f *fanoutAppender) AppendSTZeroSample(ref SeriesRef, l labels.Labels, t, st int64) (SeriesRef, error) { + ref, err := f.primary.AppendSTZeroSample(ref, l, t, st) if err != nil { return ref, err } for _, appender := range f.secondaries { - if _, err := appender.AppendCTZeroSample(ref, l, t, ct); err != nil { + if _, err := appender.AppendSTZeroSample(ref, l, t, st); err != nil { return 0, err } } diff --git a/vendor/github.com/prometheus/prometheus/storage/interface.go b/vendor/github.com/prometheus/prometheus/storage/interface.go index 9d7e5d93a6b..ae8bec033e5 100644 --- a/vendor/github.com/prometheus/prometheus/storage/interface.go +++ b/vendor/github.com/prometheus/prometheus/storage/interface.go @@ -44,13 +44,14 @@ var ( ErrExemplarsDisabled = errors.New("exemplar storage is disabled or max exemplars is less than or equal to 0") ErrNativeHistogramsDisabled = errors.New("native histograms are disabled") - // ErrOutOfOrderCT indicates failed append of CT to the storage - // due to CT being older the then newer sample. + // ErrOutOfOrderST indicates failed append of ST to the storage + // due to ST being older the then newer sample. // NOTE(bwplotka): This can be both an instrumentation failure or commonly expected // behaviour, and we currently don't have a way to determine this. As a result // it's recommended to ignore this error for now. - ErrOutOfOrderCT = errors.New("created timestamp out of order, ignoring") - ErrCTNewerThanSample = errors.New("CT is newer or the same as sample's timestamp, ignoring") + // TODO(bwplotka): Remove with appender v1 flow; not used in v2. + ErrOutOfOrderST = errors.New("start timestamp out of order, ignoring") + ErrSTNewerThanSample = errors.New("ST is newer or the same as sample's timestamp, ignoring") ) // SeriesRef is a generic series reference. In prometheus it is either a @@ -58,11 +59,14 @@ var ( // their own reference types. type SeriesRef uint64 -// Appendable allows creating appenders. +// Appendable allows creating Appender. +// +// WARNING: Work AppendableV2 is in progress. Appendable will be removed soon (ETA: Q2 2026). type Appendable interface { - // Appender returns a new appender for the storage. The implementation - // can choose whether or not to use the context, for deadlines or to check - // for errors. + // Appender returns a new appender for the storage. + // + // Implementations CAN choose whether to use the context e.g. for deadlines, + // but it's not mandatory. Appender(ctx context.Context) Appender } @@ -255,7 +259,13 @@ func (f QueryableFunc) Querier(mint, maxt int64) (Querier, error) { return f(mint, maxt) } +// AppendOptions provides options for implementations of the Appender interface. +// +// WARNING: Work AppendableV2 is in progress. Appendable will be removed soon (ETA: Q2 2026). type AppendOptions struct { + // DiscardOutOfOrder tells implementation that this append should not be out + // of order. An OOO append MUST be rejected with storage.ErrOutOfOrderSample + // error. DiscardOutOfOrder bool } @@ -264,9 +274,14 @@ type AppendOptions struct { // // Operations on the Appender interface are not goroutine-safe. // -// The type of samples (float64, histogram, etc) appended for a given series must remain same within an Appender. -// The behaviour is undefined if samples of different types are appended to the same series in a single Commit(). +// The order of samples appended via the Appender is preserved within each series. +// I.e. timestamp order within batch is not validated, samples are not reordered per timestamp or by float/histogram +// type. +// +// WARNING: Work AppendableV2 is in progress. Appendable will be removed soon (ETA: Q2 2026). type Appender interface { + AppenderTransaction + // Append adds a sample pair for the given series. // An optional series reference can be provided to accelerate calls. // A series reference number is returned which can be used to add further @@ -277,16 +292,6 @@ type Appender interface { // If the reference is 0 it must not be used for caching. Append(ref SeriesRef, l labels.Labels, t int64, v float64) (SeriesRef, error) - // Commit submits the collected samples and purges the batch. If Commit - // returns a non-nil error, it also rolls back all modifications made in - // the appender so far, as Rollback would do. In any case, an Appender - // must not be used anymore after Commit has been called. - Commit() error - - // Rollback rolls back all modifications made in the appender so far. - // Appender has to be discarded after rollback. - Rollback() error - // SetOptions configures the appender with specific append options such as // discarding out-of-order samples even if out-of-order is enabled in the TSDB. SetOptions(opts *AppendOptions) @@ -294,14 +299,14 @@ type Appender interface { ExemplarAppender HistogramAppender MetadataUpdater - CreatedTimestampAppender + StartTimestampAppender } // GetRef is an extra interface on Appenders used by downstream projects // (e.g. Cortex) to avoid maintaining a parallel set of references. type GetRef interface { - // Returns reference number that can be used to pass to Appender.Append(), - // and a set of labels that will not cause another copy when passed to Appender.Append(). + // GetRef returns a reference number that can be used to pass to AppenderV2.Append(), + // and a set of labels that will not cause another copy when passed to AppenderV2.Append(). // 0 means the appender does not have a reference to this series. // hash should be a hash of lset. GetRef(lset labels.Labels, hash uint64) (SeriesRef, labels.Labels) @@ -309,6 +314,8 @@ type GetRef interface { // ExemplarAppender provides an interface for adding samples to exemplar storage, which // within Prometheus is in-memory only. +// +// WARNING: Work AppendableV2 is in progress. Appendable will be removed soon (ETA: Q2 2026). type ExemplarAppender interface { // AppendExemplar adds an exemplar for the given series labels. // An optional reference number can be provided to accelerate calls. @@ -325,6 +332,8 @@ type ExemplarAppender interface { } // HistogramAppender provides an interface for appending histograms to the storage. +// +// WARNING: Work AppendableV2 is in progress. Appendable will be removed soon (ETA: Q2 2026). type HistogramAppender interface { // AppendHistogram adds a histogram for the given series labels. An // optional reference number can be provided to accelerate calls. A @@ -338,23 +347,25 @@ type HistogramAppender interface { // pointer. AppendHistogram won't mutate the histogram, but in turn // depends on the caller to not mutate it either. AppendHistogram(ref SeriesRef, l labels.Labels, t int64, h *histogram.Histogram, fh *histogram.FloatHistogram) (SeriesRef, error) - // AppendHistogramCTZeroSample adds synthetic zero sample for the given ct timestamp, + // AppendHistogramSTZeroSample adds synthetic zero sample for the given st timestamp, // which will be associated with given series, labels and the incoming - // sample's t (timestamp). AppendHistogramCTZeroSample returns error if zero sample can't be - // appended, for example when ct is too old, or when it would collide with + // sample's t (timestamp). AppendHistogramSTZeroSample returns error if zero sample can't be + // appended, for example when st is too old, or when it would collide with // incoming sample (sample has priority). // - // AppendHistogramCTZeroSample has to be called before the corresponding histogram AppendHistogram. + // AppendHistogramSTZeroSample has to be called before the corresponding histogram AppendHistogram. // A series reference number is returned which can be used to modify the - // CT for the given series in the same or later transactions. + // ST for the given series in the same or later transactions. // Returned reference numbers are ephemeral and may be rejected in calls - // to AppendHistogramCTZeroSample() at any point. + // to AppendHistogramSTZeroSample() at any point. // // If the reference is 0 it must not be used for caching. - AppendHistogramCTZeroSample(ref SeriesRef, l labels.Labels, t, ct int64, h *histogram.Histogram, fh *histogram.FloatHistogram) (SeriesRef, error) + AppendHistogramSTZeroSample(ref SeriesRef, l labels.Labels, t, st int64, h *histogram.Histogram, fh *histogram.FloatHistogram) (SeriesRef, error) } // MetadataUpdater provides an interface for associating metadata to stored series. +// +// WARNING: Work AppendableV2 is in progress. Appendable will be removed soon (ETA: Q2 2026). type MetadataUpdater interface { // UpdateMetadata updates a metadata entry for the given series and labels. // A series reference number is returned which can be used to modify the @@ -366,22 +377,24 @@ type MetadataUpdater interface { UpdateMetadata(ref SeriesRef, l labels.Labels, m metadata.Metadata) (SeriesRef, error) } -// CreatedTimestampAppender provides an interface for appending CT to storage. -type CreatedTimestampAppender interface { - // AppendCTZeroSample adds synthetic zero sample for the given ct timestamp, +// StartTimestampAppender provides an interface for appending ST to storage. +// +// WARNING: Work AppendableV2 is in progress. Appendable will be removed soon (ETA: Q2 2026). +type StartTimestampAppender interface { + // AppendSTZeroSample adds synthetic zero sample for the given st timestamp, // which will be associated with given series, labels and the incoming - // sample's t (timestamp). AppendCTZeroSample returns error if zero sample can't be - // appended, for example when ct is too old, or when it would collide with + // sample's t (timestamp). AppendSTZeroSample returns error if zero sample can't be + // appended, for example when st is too old, or when it would collide with // incoming sample (sample has priority). // - // AppendCTZeroSample has to be called before the corresponding sample Append. + // AppendSTZeroSample has to be called before the corresponding sample Append. // A series reference number is returned which can be used to modify the - // CT for the given series in the same or later transactions. + // ST for the given series in the same or later transactions. // Returned reference numbers are ephemeral and may be rejected in calls - // to AppendCTZeroSample() at any point. + // to AppendSTZeroSample() at any point. // // If the reference is 0 it must not be used for caching. - AppendCTZeroSample(ref SeriesRef, l labels.Labels, t, ct int64) (SeriesRef, error) + AppendSTZeroSample(ref SeriesRef, l labels.Labels, t, st int64) (SeriesRef, error) } // SeriesSet contains a set of series. @@ -389,10 +402,10 @@ type SeriesSet interface { Next() bool // At returns full series. Returned series should be iterable even after Next is called. At() Series - // The error that iteration has failed with. + // Err returns the error that iteration has failed with. // When an error occurs, set cannot continue to iterate. Err() error - // A collection of warnings for the whole set. + // Warnings returns a collection of warnings for the whole set. // Warnings could be return even iteration has not failed with error. Warnings() annotations.Annotations } diff --git a/vendor/github.com/prometheus/prometheus/storage/interface_append.go b/vendor/github.com/prometheus/prometheus/storage/interface_append.go new file mode 100644 index 00000000000..cc7045dbd58 --- /dev/null +++ b/vendor/github.com/prometheus/prometheus/storage/interface_append.go @@ -0,0 +1,182 @@ +// Copyright The Prometheus Authors +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package storage + +import ( + "context" + "errors" + + "github.com/prometheus/prometheus/model/exemplar" + "github.com/prometheus/prometheus/model/histogram" + "github.com/prometheus/prometheus/model/labels" + "github.com/prometheus/prometheus/model/metadata" +) + +// AppendableV2 allows creating AppenderV2. +type AppendableV2 interface { + // AppenderV2 returns a new appender for the storage. + // + // Implementations CAN choose whether to use the context e.g. for deadlines, + // but it's not mandatory. + AppenderV2(ctx context.Context) AppenderV2 +} + +// AOptions is a shorthand for AppendV2Options. +// NOTE: AppendOption is used already. +type AOptions = AppendV2Options + +// AppendV2Options provides optional, auxiliary data and configuration for AppenderV2.Append. +type AppendV2Options struct { + // MetricFamilyName (optional) provides metric family name for the appended sample's + // series. If the client of the AppenderV2 has this information + // (e.g. from scrape) it's recommended to pass it to the appender. + // + // Provided string bytes are unsafe to reuse, it only lives for the duration of the Append call. + // + // Some implementations use this to avoid slow and prone to error metric family detection for: + // * Metadata per metric family storages (e.g. Prometheus metadata WAL/API/RW1) + // * Strictly complex types storages (e.g. OpenTelemetry Collector). + // + // NOTE(krajorama): Example purpose is highlighted in OTLP ingestion: OTLP calculates the + // metric family name for all metrics and uses it for generating summary, + // histogram series by adding the magic suffixes. The metric family name is + // passed down to the appender in case the storage needs it for metadata updates. + // Known user of this is Mimir that implements /api/v1/metadata and uses + // Remote-Write 1.0 for this. Might be removed later if no longer + // needed by any downstream project. + // NOTE(bwplotka): Long term, once Prometheus uses complex types on storage level + // the MetricFamilyName can be removed as MetricFamilyName will equal to __name__ always. + MetricFamilyName string + + // Metadata (optional) attached to the appended sample. + // Metadata strings are safe for reuse. + // IMPORTANT: Appender v1 was only providing update. This field MUST be + // set (if known) even if it didn't change since the last iteration. + // This moves the responsibility for metadata storage options to TSDB. + Metadata metadata.Metadata + + // Exemplars (optional) attached to the appended sample. + // Exemplar slice MUST be sorted by Exemplar.TS. + // Exemplar slice is unsafe for reuse. + Exemplars []exemplar.Exemplar + + // RejectOutOfOrder tells implementation that this append should not be out + // of order. An OOO append MUST be rejected with storage.ErrOutOfOrderSample + // error. + RejectOutOfOrder bool +} + +// AppendPartialError represents an AppenderV2.Append error that tells +// callers sample was written but some auxiliary optional data (e.g. exemplars) +// was not (or partially written) +// +// It's up to the caller to decide if it's an ignorable error or not, plus +// it allows extra reporting (e.g. for Remote Write 2.0 X-Remote-Write-Written headers). +type AppendPartialError struct { + ExemplarErrors []error +} + +// Error returns combined error string. +func (e *AppendPartialError) Error() string { + errs := errors.Join(e.ExemplarErrors...) + if errs == nil { + return "" + } + return errs.Error() +} + +var _ error = &AppendPartialError{} + +// AppenderV2 provides appends against a storage for all types of samples. +// It must be completed with a call to Commit or Rollback and must not be reused afterwards. +// +// Operations on the AppenderV2 interface are not goroutine-safe. +// +// The order of samples appended via the AppenderV2 is preserved within each series. +// I.e. timestamp order within batch is not validated, samples are not reordered per timestamp or by float/histogram +// type. +type AppenderV2 interface { + AppenderTransaction + + // Append appends a sample and related exemplars, metadata, and start timestamp (st) to the storage. + // + // ref (optional) represents the stable ID for the given series identified by ls (excluding metadata). + // Callers MAY provide the ref to help implementation avoid ls -> ref computation, otherwise ref MUST be 0 (unknown). + // + // ls represents labels for the sample's series. + // + // st (optional) represents sample start timestamp. 0 means unknown. Implementations + // are responsible for any potential ST storage logic (e.g. ST zero injections). + // + // t represents sample timestamp. + // + // v, h, fh represents sample value for each sample type. + // Callers MUST only provide one of the sample types (either v, h or fh). + // Implementations can detect the type of the sample with the following switch: + // + // switch { + // case fh != nil: It's a float histogram append. + // case h != nil: It's a histogram append. + // default: It's a float append. + // } + // TODO(bwplotka): We plan to experiment on using generics for complex sampleType, but do it after we unify interface (derisk) and before we add native summaries. + // + // Implementations MUST attempt to append sample even if metadata, exemplar or (st) start timestamp appends fail. + // Implementations MAY return AppendPartialError as an error. Use errors.As to detect. + // For the successful Append, Implementations MUST return valid SeriesRef that represents ls. + // NOTE(bwplotka): Given OTLP and native histograms and the relaxation of the requirement for + // type and unit suffixes in metric names we start to hit cases of ls being not enough for id + // of the series (metadata matters). Current solution is to enable 'type-and-unit-label' features for those cases, but we may + // start to extend the id with metadata one day. + Append(ref SeriesRef, ls labels.Labels, st, t int64, v float64, h *histogram.Histogram, fh *histogram.FloatHistogram, opts AppendV2Options) (SeriesRef, error) +} + +// AppenderTransaction allows transactional appends. +type AppenderTransaction interface { + // Commit submits the collected samples and purges the batch. If Commit + // returns a non-nil error, it also rolls back all modifications made in + // the appender so far, as Rollback would do. In any case, an Appender + // must not be used anymore after Commit has been called. + Commit() error + + // Rollback rolls back all modifications made in the appender so far. + // Appender has to be discarded after rollback. + Rollback() error +} + +// LimitedAppenderV1 is an Appender that only supports appending float and histogram samples. +// This is to support migration to AppenderV2. +// TODO(bwplotka): Remove once migration to AppenderV2 is fully complete. +type LimitedAppenderV1 interface { + Append(ref SeriesRef, l labels.Labels, t int64, v float64) (SeriesRef, error) + AppendHistogram(ref SeriesRef, l labels.Labels, t int64, h *histogram.Histogram, fh *histogram.FloatHistogram) (SeriesRef, error) +} + +// AppenderV2AsLimitedV1 returns appender that exposes AppenderV2 as LimitedAppenderV1 +// TODO(bwplotka): Remove once migration to AppenderV2 is fully complete. +func AppenderV2AsLimitedV1(app AppenderV2) LimitedAppenderV1 { + return &limitedAppenderV1{AppenderV2: app} +} + +type limitedAppenderV1 struct { + AppenderV2 +} + +func (a *limitedAppenderV1) Append(ref SeriesRef, l labels.Labels, t int64, v float64) (SeriesRef, error) { + return a.AppenderV2.Append(ref, l, 0, t, v, nil, nil, AppendV2Options{}) +} + +func (a *limitedAppenderV1) AppendHistogram(ref SeriesRef, l labels.Labels, t int64, h *histogram.Histogram, fh *histogram.FloatHistogram) (SeriesRef, error) { + return a.AppenderV2.Append(ref, l, 0, t, 0, h, fh, AppendV2Options{}) +} diff --git a/vendor/github.com/prometheus/prometheus/storage/remote/azuread/azuread.go b/vendor/github.com/prometheus/prometheus/storage/remote/azuread/azuread.go index ea2a816d942..638ba586fce 100644 --- a/vendor/github.com/prometheus/prometheus/storage/remote/azuread/azuread.go +++ b/vendor/github.com/prometheus/prometheus/storage/remote/azuread/azuread.go @@ -103,6 +103,9 @@ type AzureADConfig struct { //nolint:revive // exported. // Cloud is the Azure cloud in which the service is running. Example: AzurePublic/AzureGovernment/AzureChina. Cloud string `yaml:"cloud,omitempty"` + + // Scope is the custom OAuth 2.0 scope to request when acquiring tokens. + Scope string `yaml:"scope,omitempty"` } // azureADRoundTripper is used to store the roundtripper and the tokenprovider. @@ -211,6 +214,12 @@ func (c *AzureADConfig) Validate() error { } } + if c.Scope != "" { + if matched, err := regexp.MatchString("^[\\w\\s:/.\\-]+$", c.Scope); err != nil || !matched { + return errors.New("the provided scope contains invalid characters") + } + } + return nil } @@ -360,14 +369,22 @@ func newSDKTokenCredential(clientOpts *azcore.ClientOptions, sdkConfig *SDKConfi // newTokenProvider helps to fetch accessToken for different types of credential. This also takes care of // refreshing the accessToken before expiry. This accessToken is attached to the Authorization header while making requests. func newTokenProvider(cfg *AzureADConfig, cred azcore.TokenCredential) (*tokenProvider, error) { - audience, err := getAudience(cfg.Cloud) - if err != nil { - return nil, err + var scopes []string + + // Use custom scope if provided, otherwise fallback to cloud-specific audience + if cfg.Scope != "" { + scopes = []string{cfg.Scope} + } else { + audience, err := getAudience(cfg.Cloud) + if err != nil { + return nil, err + } + scopes = []string{audience} } tokenProvider := &tokenProvider{ credentialClient: cred, - options: &policy.TokenRequestOptions{Scopes: []string{audience}}, + options: &policy.TokenRequestOptions{Scopes: scopes}, } return tokenProvider, nil diff --git a/vendor/github.com/prometheus/prometheus/storage/remote/codec.go b/vendor/github.com/prometheus/prometheus/storage/remote/codec.go index 7e21909354e..059d5e66ce2 100644 --- a/vendor/github.com/prometheus/prometheus/storage/remote/codec.go +++ b/vendor/github.com/prometheus/prometheus/storage/remote/codec.go @@ -389,6 +389,11 @@ type concreteSeriesIterator struct { curValType chunkenc.ValueType series *concreteSeries err error + + // These are pre-filled with the current model histogram if curValType + // is ValHistogram or ValFloatHistogram, respectively. + curH *histogram.Histogram + curFH *histogram.FloatHistogram } func newConcreteSeriesIterator(series *concreteSeries) chunkenc.Iterator { @@ -461,9 +466,7 @@ func (c *concreteSeriesIterator) Seek(t int64) chunkenc.ValueType { c.curValType = chunkenc.ValHistogram } if c.curValType == chunkenc.ValHistogram { - h := &c.series.histograms[c.histogramsCur] - c.curValType = getHistogramValType(h) - c.err = validateHistogramSchema(h) + c.setCurrentHistogram() } if c.err != nil { c.curValType = chunkenc.ValNone @@ -471,18 +474,57 @@ func (c *concreteSeriesIterator) Seek(t int64) chunkenc.ValueType { return c.curValType } -func validateHistogramSchema(h *prompb.Histogram) error { - if histogram.IsKnownSchema(h.Schema) { - return nil +// setCurrentHistogram pre-fills either the curH or the curFH field with a +// converted model histogram and sets c.curValType accordingly. It validates the +// histogram and sets c.err accordingly. This all has to be done in Seek() and +// Next() already so that we know if the histogram we got from the remote-read +// source is valid or not before we allow the AtHistogram()/AtFloatHistogram() +// call. +func (c *concreteSeriesIterator) setCurrentHistogram() { + pbH := c.series.histograms[c.histogramsCur] + + // Basic schema check first. + schema := pbH.Schema + if !histogram.IsKnownSchema(schema) { + c.err = histogram.UnknownSchemaError(schema) + return } - return histogram.UnknownSchemaError(h.Schema) -} -func getHistogramValType(h *prompb.Histogram) chunkenc.ValueType { - if h.IsFloatHistogram() { - return chunkenc.ValFloatHistogram + if pbH.IsFloatHistogram() { + c.curValType = chunkenc.ValFloatHistogram + mFH := pbH.ToFloatHistogram() + if mFH.Schema > histogram.ExponentialSchemaMax && mFH.Schema <= histogram.ExponentialSchemaMaxReserved { + // This is a very slow path, but it should only happen if the + // sample is from a newer Prometheus version that supports higher + // resolution. + if err := mFH.ReduceResolution(histogram.ExponentialSchemaMax); err != nil { + c.err = err + return + } + } + if err := mFH.Validate(); err != nil { + c.err = err + return + } + c.curFH = mFH + return } - return chunkenc.ValHistogram + c.curValType = chunkenc.ValHistogram + mH := pbH.ToIntHistogram() + if mH.Schema > histogram.ExponentialSchemaMax && mH.Schema <= histogram.ExponentialSchemaMaxReserved { + // This is a very slow path, but it should only happen if the + // sample is from a newer Prometheus version that supports higher + // resolution. + if err := mH.ReduceResolution(histogram.ExponentialSchemaMax); err != nil { + c.err = err + return + } + } + if err := mH.Validate(); err != nil { + c.err = err + return + } + c.curH = mH } // At implements chunkenc.Iterator. @@ -499,31 +541,19 @@ func (c *concreteSeriesIterator) AtHistogram(*histogram.Histogram) (int64, *hist if c.curValType != chunkenc.ValHistogram { panic("iterator is not on an integer histogram sample") } - h := c.series.histograms[c.histogramsCur] - mh := h.ToIntHistogram() - if mh.Schema > histogram.ExponentialSchemaMax && mh.Schema <= histogram.ExponentialSchemaMaxReserved { - // This is a very slow path, but it should only happen if the - // sample is from a newer Prometheus version that supports higher - // resolution. - mh.ReduceResolution(histogram.ExponentialSchemaMax) - } - return h.Timestamp, mh + return c.series.histograms[c.histogramsCur].Timestamp, c.curH } // AtFloatHistogram implements chunkenc.Iterator. func (c *concreteSeriesIterator) AtFloatHistogram(*histogram.FloatHistogram) (int64, *histogram.FloatHistogram) { - if c.curValType == chunkenc.ValHistogram || c.curValType == chunkenc.ValFloatHistogram { - fh := c.series.histograms[c.histogramsCur] - mfh := fh.ToFloatHistogram() // integer will be auto-converted. - if mfh.Schema > histogram.ExponentialSchemaMax && mfh.Schema <= histogram.ExponentialSchemaMaxReserved { - // This is a very slow path, but it should only happen if the - // sample is from a newer Prometheus version that supports higher - // resolution. - mfh.ReduceResolution(histogram.ExponentialSchemaMax) - } - return fh.Timestamp, mfh + switch c.curValType { + case chunkenc.ValFloatHistogram: + return c.series.histograms[c.histogramsCur].Timestamp, c.curFH + case chunkenc.ValHistogram: + return c.series.histograms[c.histogramsCur].Timestamp, c.curH.ToFloat(nil) + default: + panic("iterator is not on a histogram sample") } - panic("iterator is not on a histogram sample") } // AtT implements chunkenc.Iterator. @@ -571,9 +601,7 @@ func (c *concreteSeriesIterator) Next() chunkenc.ValueType { } if c.curValType == chunkenc.ValHistogram { - h := &c.series.histograms[c.histogramsCur] - c.curValType = getHistogramValType(h) - c.err = validateHistogramSchema(h) + c.setCurrentHistogram() } if c.err != nil { c.curValType = chunkenc.ValNone diff --git a/vendor/github.com/prometheus/prometheus/storage/remote/otlptranslator/prometheusremotewrite/combined_appender.go b/vendor/github.com/prometheus/prometheus/storage/remote/otlptranslator/prometheusremotewrite/combined_appender.go index 1441aecb6d0..883b8d31423 100644 --- a/vendor/github.com/prometheus/prometheus/storage/remote/otlptranslator/prometheusremotewrite/combined_appender.go +++ b/vendor/github.com/prometheus/prometheus/storage/remote/otlptranslator/prometheusremotewrite/combined_appender.go @@ -49,10 +49,10 @@ type Metadata struct { type CombinedAppender interface { // AppendSample appends a sample and related exemplars, metadata, and // created timestamp to the storage. - AppendSample(ls labels.Labels, meta Metadata, ct, t int64, v float64, es []exemplar.Exemplar) error + AppendSample(ls labels.Labels, meta Metadata, st, t int64, v float64, es []exemplar.Exemplar) error // AppendHistogram appends a histogram and related exemplars, metadata, and // created timestamp to the storage. - AppendHistogram(ls labels.Labels, meta Metadata, ct, t int64, h *histogram.Histogram, es []exemplar.Exemplar) error + AppendHistogram(ls labels.Labels, meta Metadata, st, t int64, h *histogram.Histogram, es []exemplar.Exemplar) error } // CombinedAppenderMetrics is for the metrics observed by the @@ -82,11 +82,12 @@ func NewCombinedAppenderMetrics(reg prometheus.Registerer) CombinedAppenderMetri // NewCombinedAppender creates a combined appender that sets start times and // updates metadata for each series only once, and appends samples and // exemplars for each call. -func NewCombinedAppender(app storage.Appender, logger *slog.Logger, ingestCTZeroSample bool, metrics CombinedAppenderMetrics) CombinedAppender { +func NewCombinedAppender(app storage.Appender, logger *slog.Logger, ingestSTZeroSample, appendMetadata bool, metrics CombinedAppenderMetrics) CombinedAppender { return &combinedAppender{ app: app, logger: logger, - ingestCTZeroSample: ingestCTZeroSample, + ingestSTZeroSample: ingestSTZeroSample, + appendMetadata: appendMetadata, refs: make(map[uint64]seriesRef), samplesAppendedWithoutMetadata: metrics.samplesAppendedWithoutMetadata, outOfOrderExemplars: metrics.outOfOrderExemplars, @@ -95,7 +96,7 @@ func NewCombinedAppender(app storage.Appender, logger *slog.Logger, ingestCTZero type seriesRef struct { ref storage.SeriesRef - ct int64 + st int64 ls labels.Labels meta metadata.Metadata } @@ -105,27 +106,28 @@ type combinedAppender struct { logger *slog.Logger samplesAppendedWithoutMetadata prometheus.Counter outOfOrderExemplars prometheus.Counter - ingestCTZeroSample bool + ingestSTZeroSample bool + appendMetadata bool // Used to ensure we only update metadata and created timestamps once, and to share storage.SeriesRefs. // To detect hash collision it also stores the labels. // There is no overflow/conflict list, the TSDB will handle that part. refs map[uint64]seriesRef } -func (b *combinedAppender) AppendSample(ls labels.Labels, meta Metadata, ct, t int64, v float64, es []exemplar.Exemplar) (err error) { - return b.appendFloatOrHistogram(ls, meta.Metadata, ct, t, v, nil, es) +func (b *combinedAppender) AppendSample(ls labels.Labels, meta Metadata, st, t int64, v float64, es []exemplar.Exemplar) (err error) { + return b.appendFloatOrHistogram(ls, meta.Metadata, st, t, v, nil, es) } -func (b *combinedAppender) AppendHistogram(ls labels.Labels, meta Metadata, ct, t int64, h *histogram.Histogram, es []exemplar.Exemplar) (err error) { +func (b *combinedAppender) AppendHistogram(ls labels.Labels, meta Metadata, st, t int64, h *histogram.Histogram, es []exemplar.Exemplar) (err error) { if h == nil { // Sanity check, we should never get here with a nil histogram. b.logger.Error("Received nil histogram in CombinedAppender.AppendHistogram", "series", ls.String()) return errors.New("internal error, attempted to append nil histogram") } - return b.appendFloatOrHistogram(ls, meta.Metadata, ct, t, 0, h, es) + return b.appendFloatOrHistogram(ls, meta.Metadata, st, t, 0, h, es) } -func (b *combinedAppender) appendFloatOrHistogram(ls labels.Labels, meta metadata.Metadata, ct, t int64, v float64, h *histogram.Histogram, es []exemplar.Exemplar) (err error) { +func (b *combinedAppender) appendFloatOrHistogram(ls labels.Labels, meta metadata.Metadata, st, t int64, v float64, h *histogram.Histogram, es []exemplar.Exemplar) (err error) { hash := ls.Hash() series, exists := b.refs[hash] ref := series.ref @@ -138,28 +140,28 @@ func (b *combinedAppender) appendFloatOrHistogram(ls labels.Labels, meta metadat exists = false ref = 0 } - updateRefs := !exists || series.ct != ct - if updateRefs && ct != 0 && ct < t && b.ingestCTZeroSample { + updateRefs := !exists || series.st != st + if updateRefs && st != 0 && st < t && b.ingestSTZeroSample { var newRef storage.SeriesRef if h != nil { - newRef, err = b.app.AppendHistogramCTZeroSample(ref, ls, t, ct, h, nil) + newRef, err = b.app.AppendHistogramSTZeroSample(ref, ls, t, st, h, nil) } else { - newRef, err = b.app.AppendCTZeroSample(ref, ls, t, ct) + newRef, err = b.app.AppendSTZeroSample(ref, ls, t, st) } if err != nil { - if !errors.Is(err, storage.ErrOutOfOrderCT) && !errors.Is(err, storage.ErrDuplicateSampleForTimestamp) { + if !errors.Is(err, storage.ErrOutOfOrderST) && !errors.Is(err, storage.ErrDuplicateSampleForTimestamp) { // Even for the first sample OOO is a common scenario because - // we can't tell if a CT was already ingested in a previous request. + // we can't tell if a ST was already ingested in a previous request. // We ignore the error. // ErrDuplicateSampleForTimestamp is also a common scenario because // unknown start times in Opentelemetry are indicated by setting // the start time to the same as the first sample time. // https://opentelemetry.io/docs/specs/otel/metrics/data-model/#cumulative-streams-handling-unknown-start-time - b.logger.Warn("Error when appending CT from OTLP", "err", err, "series", ls.String(), "created_timestamp", ct, "timestamp", t, "sample_type", sampleType(h)) + b.logger.Warn("Error when appending ST from OTLP", "err", err, "series", ls.String(), "start_timestamp", st, "timestamp", t, "sample_type", sampleType(h)) } } else { // We only use the returned reference on success as otherwise an - // error of CT append could invalidate the series reference. + // error of ST append could invalidate the series reference. ref = newRef } } @@ -189,25 +191,29 @@ func (b *combinedAppender) appendFloatOrHistogram(ls labels.Labels, meta metadat return err } - if !exists || series.meta.Help != meta.Help || series.meta.Type != meta.Type || series.meta.Unit != meta.Unit { - updateRefs = true - // If this is the first time we see this series, set the metadata. - _, err := b.app.UpdateMetadata(ref, ls, meta) - if err != nil { - b.samplesAppendedWithoutMetadata.Add(1) - b.logger.Warn("Error while updating metadata from OTLP", "err", err) - } - } + metadataChanged := exists && (series.meta.Help != meta.Help || series.meta.Type != meta.Type || series.meta.Unit != meta.Unit) - if updateRefs { + // Update cache if references changed or metadata changed. + if updateRefs || metadataChanged { b.refs[hash] = seriesRef{ ref: ref, - ct: ct, + st: st, ls: ls, meta: meta, } } + // Update metadata in storage if enabled and needed. + if b.appendMetadata && (!exists || metadataChanged) { + // Only update metadata in WAL if the metadata-wal-records feature is enabled. + // Without this feature, metadata is not persisted to WAL. + _, err := b.app.UpdateMetadata(ref, ls, meta) + if err != nil { + b.samplesAppendedWithoutMetadata.Add(1) + b.logger.Warn("Error while updating metadata from OTLP", "err", err) + } + } + b.appendExemplars(ref, ls, es) return err diff --git a/vendor/github.com/prometheus/prometheus/storage/remote/otlptranslator/prometheusremotewrite/histograms.go b/vendor/github.com/prometheus/prometheus/storage/remote/otlptranslator/prometheusremotewrite/histograms.go index 5606fa4d91a..c93a00db760 100644 --- a/vendor/github.com/prometheus/prometheus/storage/remote/otlptranslator/prometheusremotewrite/histograms.go +++ b/vendor/github.com/prometheus/prometheus/storage/remote/otlptranslator/prometheusremotewrite/histograms.go @@ -67,13 +67,13 @@ func (c *PrometheusConverter) addExponentialHistogramDataPoints(ctx context.Cont return annots, err } ts := convertTimeStamp(pt.Timestamp()) - ct := convertTimeStamp(pt.StartTimestamp()) + st := convertTimeStamp(pt.StartTimestamp()) exemplars, err := c.getPromExemplars(ctx, pt.Exemplars()) if err != nil { return annots, err } // OTel exponential histograms are always Int Histograms. - if err = c.appender.AppendHistogram(lbls, meta, ct, ts, hp, exemplars); err != nil { + if err = c.appender.AppendHistogram(lbls, meta, st, ts, hp, exemplars); err != nil { return annots, err } } @@ -286,12 +286,12 @@ func (c *PrometheusConverter) addCustomBucketsHistogramDataPoints(ctx context.Co return annots, err } ts := convertTimeStamp(pt.Timestamp()) - ct := convertTimeStamp(pt.StartTimestamp()) + st := convertTimeStamp(pt.StartTimestamp()) exemplars, err := c.getPromExemplars(ctx, pt.Exemplars()) if err != nil { return annots, err } - if err = c.appender.AppendHistogram(lbls, meta, ct, ts, hp, exemplars); err != nil { + if err = c.appender.AppendHistogram(lbls, meta, st, ts, hp, exemplars); err != nil { return annots, err } } diff --git a/vendor/github.com/prometheus/prometheus/storage/remote/otlptranslator/prometheusremotewrite/metrics_to_prw.go b/vendor/github.com/prometheus/prometheus/storage/remote/otlptranslator/prometheusremotewrite/metrics_to_prw.go index 5e575e61744..f43e4964b11 100644 --- a/vendor/github.com/prometheus/prometheus/storage/remote/otlptranslator/prometheusremotewrite/metrics_to_prw.go +++ b/vendor/github.com/prometheus/prometheus/storage/remote/otlptranslator/prometheusremotewrite/metrics_to_prw.go @@ -140,14 +140,6 @@ func (c *PrometheusConverter) FromMetrics(ctx context.Context, md pmetric.Metric c.seenTargetInfo = make(map[targetInfoKey]struct{}) resourceMetricsSlice := md.ResourceMetrics() - numMetrics := 0 - for i := 0; i < resourceMetricsSlice.Len(); i++ { - scopeMetricsSlice := resourceMetricsSlice.At(i).ScopeMetrics() - for j := 0; j < scopeMetricsSlice.Len(); j++ { - numMetrics += scopeMetricsSlice.At(j).Metrics().Len() - } - } - for i := 0; i < resourceMetricsSlice.Len(); i++ { resourceMetrics := resourceMetricsSlice.At(i) resource := resourceMetrics.Resource() diff --git a/vendor/github.com/prometheus/prometheus/storage/remote/otlptranslator/prometheusremotewrite/number_data_points.go b/vendor/github.com/prometheus/prometheus/storage/remote/otlptranslator/prometheusremotewrite/number_data_points.go index cdae9787365..8f30dbb6b60 100644 --- a/vendor/github.com/prometheus/prometheus/storage/remote/otlptranslator/prometheusremotewrite/number_data_points.go +++ b/vendor/github.com/prometheus/prometheus/storage/remote/otlptranslator/prometheusremotewrite/number_data_points.go @@ -61,8 +61,8 @@ func (c *PrometheusConverter) addGaugeNumberDataPoints(ctx context.Context, data val = math.Float64frombits(value.StaleNaN) } ts := convertTimeStamp(pt.Timestamp()) - ct := convertTimeStamp(pt.StartTimestamp()) - if err := c.appender.AppendSample(labels, meta, ct, ts, val, nil); err != nil { + st := convertTimeStamp(pt.StartTimestamp()) + if err := c.appender.AppendSample(labels, meta, st, ts, val, nil); err != nil { return err } } @@ -104,12 +104,12 @@ func (c *PrometheusConverter) addSumNumberDataPoints(ctx context.Context, dataPo val = math.Float64frombits(value.StaleNaN) } ts := convertTimeStamp(pt.Timestamp()) - ct := convertTimeStamp(pt.StartTimestamp()) + st := convertTimeStamp(pt.StartTimestamp()) exemplars, err := c.getPromExemplars(ctx, pt.Exemplars()) if err != nil { return err } - if err := c.appender.AppendSample(lbls, meta, ct, ts, val, exemplars); err != nil { + if err := c.appender.AppendSample(lbls, meta, st, ts, val, exemplars); err != nil { return err } } diff --git a/vendor/github.com/prometheus/prometheus/storage/remote/queue_manager.go b/vendor/github.com/prometheus/prometheus/storage/remote/queue_manager.go index 25d3a94b6ac..5fc5f5564ba 100644 --- a/vendor/github.com/prometheus/prometheus/storage/remote/queue_manager.go +++ b/vendor/github.com/prometheus/prometheus/storage/remote/queue_manager.go @@ -31,6 +31,7 @@ import ( "go.opentelemetry.io/otel" "go.opentelemetry.io/otel/attribute" semconv "go.opentelemetry.io/otel/semconv/v1.21.0" + "go.opentelemetry.io/otel/trace" "go.uber.org/atomic" "github.com/prometheus/prometheus/config" @@ -644,63 +645,78 @@ func isSampleOld(baseTime time.Time, sampleAgeLimit time.Duration, ts int64) boo return sampleTs.Before(limitTs) } +// timeSeriesAgeChecker encapsulates the logic for checking if time series data is too old. +type timeSeriesAgeChecker struct { + metrics *queueManagerMetrics + baseTime time.Time + sampleAgeLimit time.Duration +} + +// checkAndRecordIfOld checks if a timestamp is too old and records the appropriate metric. +// Returns true if the data should be dropped. +func (c *timeSeriesAgeChecker) checkAndRecordIfOld(timestamp int64, dataType string) bool { + if c.sampleAgeLimit == 0 { + // If sampleAgeLimit is unset, then we never skip samples due to their age. + return false + } + + if !isSampleOld(c.baseTime, c.sampleAgeLimit, timestamp) { + return false + } + + // Record the drop in metrics. + switch dataType { + case "sample": + c.metrics.droppedSamplesTotal.WithLabelValues(reasonTooOld).Inc() + case "histogram": + c.metrics.droppedHistogramsTotal.WithLabelValues(reasonTooOld).Inc() + case "exemplar": + c.metrics.droppedExemplarsTotal.WithLabelValues(reasonTooOld).Inc() + } + return true +} + func isTimeSeriesOldFilter(metrics *queueManagerMetrics, baseTime time.Time, sampleAgeLimit time.Duration) func(ts prompb.TimeSeries) bool { + checker := &timeSeriesAgeChecker{ + metrics: metrics, + baseTime: baseTime, + sampleAgeLimit: sampleAgeLimit, + } + return func(ts prompb.TimeSeries) bool { - if sampleAgeLimit == 0 { - // If sampleAgeLimit is unset, then we never skip samples due to their age. - return false - } - switch { // Only the first element should be set in the series, therefore we only check the first element. + switch { case len(ts.Samples) > 0: - if isSampleOld(baseTime, sampleAgeLimit, ts.Samples[0].Timestamp) { - metrics.droppedSamplesTotal.WithLabelValues(reasonTooOld).Inc() - return true - } + return checker.checkAndRecordIfOld(ts.Samples[0].Timestamp, "sample") case len(ts.Histograms) > 0: - if isSampleOld(baseTime, sampleAgeLimit, ts.Histograms[0].Timestamp) { - metrics.droppedHistogramsTotal.WithLabelValues(reasonTooOld).Inc() - return true - } + return checker.checkAndRecordIfOld(ts.Histograms[0].Timestamp, "histogram") case len(ts.Exemplars) > 0: - if isSampleOld(baseTime, sampleAgeLimit, ts.Exemplars[0].Timestamp) { - metrics.droppedExemplarsTotal.WithLabelValues(reasonTooOld).Inc() - return true - } + return checker.checkAndRecordIfOld(ts.Exemplars[0].Timestamp, "exemplar") default: return false } - return false } } func isV2TimeSeriesOldFilter(metrics *queueManagerMetrics, baseTime time.Time, sampleAgeLimit time.Duration) func(ts writev2.TimeSeries) bool { + checker := &timeSeriesAgeChecker{ + metrics: metrics, + baseTime: baseTime, + sampleAgeLimit: sampleAgeLimit, + } + return func(ts writev2.TimeSeries) bool { - if sampleAgeLimit == 0 { - // If sampleAgeLimit is unset, then we never skip samples due to their age. - return false - } - switch { // Only the first element should be set in the series, therefore we only check the first element. + switch { case len(ts.Samples) > 0: - if isSampleOld(baseTime, sampleAgeLimit, ts.Samples[0].Timestamp) { - metrics.droppedSamplesTotal.WithLabelValues(reasonTooOld).Inc() - return true - } + return checker.checkAndRecordIfOld(ts.Samples[0].Timestamp, "sample") case len(ts.Histograms) > 0: - if isSampleOld(baseTime, sampleAgeLimit, ts.Histograms[0].Timestamp) { - metrics.droppedHistogramsTotal.WithLabelValues(reasonTooOld).Inc() - return true - } + return checker.checkAndRecordIfOld(ts.Histograms[0].Timestamp, "histogram") case len(ts.Exemplars) > 0: - if isSampleOld(baseTime, sampleAgeLimit, ts.Exemplars[0].Timestamp) { - metrics.droppedExemplarsTotal.WithLabelValues(reasonTooOld).Inc() - return true - } + return checker.checkAndRecordIfOld(ts.Exemplars[0].Timestamp, "exemplar") default: return false } - return false } } @@ -1737,6 +1753,20 @@ func (s *shards) sendSamplesWithBackoff(ctx context.Context, samples []prompb.Ti } reqSize := len(req) + sc := sendBatchContext{ + ctx: ctx, + sampleCount: sampleCount, + exemplarCount: exemplarCount, + histogramCount: histogramCount, + metadataCount: metadataCount, + reqSize: reqSize, + } + + metricsUpdater := batchMetricsUpdater{ + metrics: s.qm.metrics, + storeClient: s.qm.storeClient, + sentDuration: s.qm.metrics.sentBatchDuration, + } // Since we retry writes via attemptStore and sendWriteRequestWithBackoff we need // to track the total amount of accepted data across the various attempts. @@ -1772,33 +1802,14 @@ func (s *shards) sendSamplesWithBackoff(ctx context.Context, samples []prompb.Ti req = req2 } - ctx, span := otel.Tracer("").Start(ctx, "Remote Send Batch") + ctx, span := createBatchSpan(sc.ctx, sc, s.qm.storeClient.Name(), s.qm.storeClient.Endpoint(), try) defer span.End() - span.SetAttributes( - attribute.Int("request_size", reqSize), - attribute.Int("samples", sampleCount), - attribute.Int("try", try), - attribute.String("remote_name", s.qm.storeClient.Name()), - attribute.String("remote_url", s.qm.storeClient.Endpoint()), - ) - - if exemplarCount > 0 { - span.SetAttributes(attribute.Int("exemplars", exemplarCount)) - } - if histogramCount > 0 { - span.SetAttributes(attribute.Int("histograms", histogramCount)) - } - begin := time.Now() - s.qm.metrics.samplesTotal.Add(float64(sampleCount)) - s.qm.metrics.exemplarsTotal.Add(float64(exemplarCount)) - s.qm.metrics.histogramsTotal.Add(float64(histogramCount)) - s.qm.metrics.metadataTotal.Add(float64(metadataCount)) + metricsUpdater.recordBatchAttempt(sc, begin) // Technically for v1, we will likely have empty response stats, but for // newer Receivers this might be not, so used it in a best effort. rs, err := s.qm.client().Store(ctx, req, try) - s.qm.metrics.sentBatchDuration.Observe(time.Since(begin).Seconds()) // TODO(bwplotka): Revisit this once we have Receivers doing retriable partial error // so far we don't have those, so it's ok to potentially skew statistics. addStats(rs) @@ -1811,9 +1822,7 @@ func (s *shards) sendSamplesWithBackoff(ctx context.Context, samples []prompb.Ti } onRetry := func() { - s.qm.metrics.retriedSamplesTotal.Add(float64(sampleCount)) - s.qm.metrics.retriedExemplarsTotal.Add(float64(exemplarCount)) - s.qm.metrics.retriedHistogramsTotal.Add(float64(histogramCount)) + metricsUpdater.recordRetry(sc) } err = s.qm.sendWriteRequestWithBackoff(ctx, attemptStore, onRetry) @@ -1850,6 +1859,20 @@ func (s *shards) sendV2SamplesWithBackoff(ctx context.Context, samples []writev2 } reqSize := len(req) + sc := sendBatchContext{ + ctx: ctx, + sampleCount: sampleCount, + exemplarCount: exemplarCount, + histogramCount: histogramCount, + metadataCount: metadataCount, + reqSize: reqSize, + } + + metricsUpdater := batchMetricsUpdater{ + metrics: s.qm.metrics, + storeClient: s.qm.storeClient, + sentDuration: s.qm.metrics.sentBatchDuration, + } // Since we retry writes via attemptStore and sendWriteRequestWithBackoff we need // to track the total amount of accepted data across the various attempts. @@ -1885,31 +1908,12 @@ func (s *shards) sendV2SamplesWithBackoff(ctx context.Context, samples []writev2 req = req2 } - ctx, span := otel.Tracer("").Start(ctx, "Remote Send Batch") + ctx, span := createBatchSpan(sc.ctx, sc, s.qm.storeClient.Name(), s.qm.storeClient.Endpoint(), try) defer span.End() - span.SetAttributes( - attribute.Int("request_size", reqSize), - attribute.Int("samples", sampleCount), - attribute.Int("try", try), - attribute.String("remote_name", s.qm.storeClient.Name()), - attribute.String("remote_url", s.qm.storeClient.Endpoint()), - ) - - if exemplarCount > 0 { - span.SetAttributes(attribute.Int("exemplars", exemplarCount)) - } - if histogramCount > 0 { - span.SetAttributes(attribute.Int("histograms", histogramCount)) - } - begin := time.Now() - s.qm.metrics.samplesTotal.Add(float64(sampleCount)) - s.qm.metrics.exemplarsTotal.Add(float64(exemplarCount)) - s.qm.metrics.histogramsTotal.Add(float64(histogramCount)) - s.qm.metrics.metadataTotal.Add(float64(metadataCount)) + metricsUpdater.recordBatchAttempt(sc, begin) rs, err := s.qm.client().Store(ctx, req, try) - s.qm.metrics.sentBatchDuration.Observe(time.Since(begin).Seconds()) // TODO(bwplotka): Revisit this once we have Receivers doing retriable partial error // so far we don't have those, so it's ok to potentially skew statistics. addStats(rs) @@ -1933,9 +1937,7 @@ func (s *shards) sendV2SamplesWithBackoff(ctx context.Context, samples []writev2 } onRetry := func() { - s.qm.metrics.retriedSamplesTotal.Add(float64(sampleCount)) - s.qm.metrics.retriedExemplarsTotal.Add(float64(exemplarCount)) - s.qm.metrics.retriedHistogramsTotal.Add(float64(histogramCount)) + metricsUpdater.recordRetry(sc) } err = s.qm.sendWriteRequestWithBackoff(ctx, attemptStore, onRetry) @@ -2101,48 +2103,27 @@ func setAtomicToNewer(value *atomic.Int64, newValue int64) (previous int64, upda } } -func buildTimeSeries(timeSeries []prompb.TimeSeries, filter func(prompb.TimeSeries) bool) (int64, int64, []prompb.TimeSeries, int, int, int) { - var highest int64 - var lowest int64 - var droppedSamples, droppedExemplars, droppedHistograms int - +func buildTimeSeries(timeSeries []prompb.TimeSeries, filter func(prompb.TimeSeries) bool) ([]prompb.TimeSeries, *timeSeriesStats) { + stats := newTimeSeriesStats() keepIdx := 0 - lowest = math.MaxInt64 + for i, ts := range timeSeries { if filter != nil && filter(ts) { - if len(ts.Samples) > 0 { - droppedSamples++ - } - if len(ts.Exemplars) > 0 { - droppedExemplars++ - } - if len(ts.Histograms) > 0 { - droppedHistograms++ - } + stats.recordDropped(len(ts.Samples) > 0, len(ts.Exemplars) > 0, len(ts.Histograms) > 0) continue } // At the moment we only ever append a TimeSeries with a single sample or exemplar in it. - if len(ts.Samples) > 0 && ts.Samples[0].Timestamp > highest { - highest = ts.Samples[0].Timestamp + if len(ts.Samples) > 0 { + stats.updateTimestamp(ts.Samples[0].Timestamp) } - if len(ts.Exemplars) > 0 && ts.Exemplars[0].Timestamp > highest { - highest = ts.Exemplars[0].Timestamp + if len(ts.Exemplars) > 0 { + stats.updateTimestamp(ts.Exemplars[0].Timestamp) } - if len(ts.Histograms) > 0 && ts.Histograms[0].Timestamp > highest { - highest = ts.Histograms[0].Timestamp + if len(ts.Histograms) > 0 { + stats.updateTimestamp(ts.Histograms[0].Timestamp) } - // Get lowest timestamp - if len(ts.Samples) > 0 && ts.Samples[0].Timestamp < lowest { - lowest = ts.Samples[0].Timestamp - } - if len(ts.Exemplars) > 0 && ts.Exemplars[0].Timestamp < lowest { - lowest = ts.Exemplars[0].Timestamp - } - if len(ts.Histograms) > 0 && ts.Histograms[0].Timestamp < lowest { - lowest = ts.Histograms[0].Timestamp - } if i != keepIdx { // We have to swap the kept timeseries with the one which should be dropped. // Copying any elements within timeSeries could cause data corruptions when reusing the slice in a next batch (shards.populateTimeSeries). @@ -2151,16 +2132,14 @@ func buildTimeSeries(timeSeries []prompb.TimeSeries, filter func(prompb.TimeSeri keepIdx++ } - timeSeries = timeSeries[:keepIdx] - return highest, lowest, timeSeries, droppedSamples, droppedExemplars, droppedHistograms + return timeSeries[:keepIdx], stats } func buildWriteRequest(logger *slog.Logger, timeSeries []prompb.TimeSeries, metadata []prompb.MetricMetadata, pBuf *proto.Buffer, filter func(prompb.TimeSeries) bool, buf compression.EncodeBuffer, compr compression.Type) (_ []byte, highest, lowest int64, _ error) { - highest, lowest, timeSeries, - droppedSamples, droppedExemplars, droppedHistograms := buildTimeSeries(timeSeries, filter) + timeSeries, stats := buildTimeSeries(timeSeries, filter) - if droppedSamples > 0 || droppedExemplars > 0 || droppedHistograms > 0 { - logger.Debug("dropped data due to their age", "droppedSamples", droppedSamples, "droppedExemplars", droppedExemplars, "droppedHistograms", droppedHistograms) + if stats.droppedSamples > 0 || stats.droppedExemplars > 0 || stats.droppedHistograms > 0 { + logger.Debug("dropped data due to their age", "droppedSamples", stats.droppedSamples, "droppedExemplars", stats.droppedExemplars, "droppedHistograms", stats.droppedHistograms) } req := &prompb.WriteRequest{ @@ -2174,21 +2153,21 @@ func buildWriteRequest(logger *slog.Logger, timeSeries []prompb.TimeSeries, meta pBuf.Reset() } if err := pBuf.Marshal(req); err != nil { - return nil, highest, lowest, err + return nil, stats.highest, stats.lowest, err } compressed, err := compression.Encode(compr, pBuf.Bytes(), buf) if err != nil { - return nil, highest, lowest, err + return nil, stats.highest, stats.lowest, err } - return compressed, highest, lowest, nil + return compressed, stats.highest, stats.lowest, nil } func buildV2WriteRequest(logger *slog.Logger, samples []writev2.TimeSeries, labels []string, pBuf *[]byte, filter func(writev2.TimeSeries) bool, buf compression.EncodeBuffer, compr compression.Type) (compressed []byte, highest, lowest int64, _ error) { - highest, lowest, timeSeries, droppedSamples, droppedExemplars, droppedHistograms := buildV2TimeSeries(samples, filter) + timeSeries, stats := buildV2TimeSeries(samples, filter) - if droppedSamples > 0 || droppedExemplars > 0 || droppedHistograms > 0 { - logger.Debug("dropped data due to their age", "droppedSamples", droppedSamples, "droppedExemplars", droppedExemplars, "droppedHistograms", droppedHistograms) + if stats.droppedSamples > 0 || stats.droppedExemplars > 0 || stats.droppedHistograms > 0 { + logger.Debug("dropped data due to their age", "droppedSamples", stats.droppedSamples, "droppedExemplars", stats.droppedExemplars, "droppedHistograms", stats.droppedHistograms) } req := &writev2.Request{ @@ -2202,59 +2181,38 @@ func buildV2WriteRequest(logger *slog.Logger, samples []writev2.TimeSeries, labe data, err := req.OptimizedMarshal(*pBuf) if err != nil { - return nil, highest, lowest, err + return nil, stats.highest, stats.lowest, err } *pBuf = data compressed, err = compression.Encode(compr, *pBuf, buf) if err != nil { - return nil, highest, lowest, err + return nil, stats.highest, stats.lowest, err } - return compressed, highest, lowest, nil + return compressed, stats.highest, stats.lowest, nil } -func buildV2TimeSeries(timeSeries []writev2.TimeSeries, filter func(writev2.TimeSeries) bool) (int64, int64, []writev2.TimeSeries, int, int, int) { - var highest int64 - var lowest int64 - var droppedSamples, droppedExemplars, droppedHistograms int - +func buildV2TimeSeries(timeSeries []writev2.TimeSeries, filter func(writev2.TimeSeries) bool) ([]writev2.TimeSeries, *timeSeriesStats) { + stats := newTimeSeriesStats() keepIdx := 0 - lowest = math.MaxInt64 + for i, ts := range timeSeries { if filter != nil && filter(ts) { - if len(ts.Samples) > 0 { - droppedSamples++ - } - if len(ts.Exemplars) > 0 { - droppedExemplars++ - } - if len(ts.Histograms) > 0 { - droppedHistograms++ - } + stats.recordDropped(len(ts.Samples) > 0, len(ts.Exemplars) > 0, len(ts.Histograms) > 0) continue } // At the moment we only ever append a TimeSeries with a single sample or exemplar in it. - if len(ts.Samples) > 0 && ts.Samples[0].Timestamp > highest { - highest = ts.Samples[0].Timestamp + if len(ts.Samples) > 0 { + stats.updateTimestamp(ts.Samples[0].Timestamp) } - if len(ts.Exemplars) > 0 && ts.Exemplars[0].Timestamp > highest { - highest = ts.Exemplars[0].Timestamp + if len(ts.Exemplars) > 0 { + stats.updateTimestamp(ts.Exemplars[0].Timestamp) } - if len(ts.Histograms) > 0 && ts.Histograms[0].Timestamp > highest { - highest = ts.Histograms[0].Timestamp + if len(ts.Histograms) > 0 { + stats.updateTimestamp(ts.Histograms[0].Timestamp) } - // Get the lowest timestamp. - if len(ts.Samples) > 0 && ts.Samples[0].Timestamp < lowest { - lowest = ts.Samples[0].Timestamp - } - if len(ts.Exemplars) > 0 && ts.Exemplars[0].Timestamp < lowest { - lowest = ts.Exemplars[0].Timestamp - } - if len(ts.Histograms) > 0 && ts.Histograms[0].Timestamp < lowest { - lowest = ts.Histograms[0].Timestamp - } if i != keepIdx { // We have to swap the kept timeseries with the one which should be dropped. // Copying any elements within timeSeries could cause data corruptions when reusing the slice in a next batch (shards.populateTimeSeries). @@ -2263,6 +2221,99 @@ func buildV2TimeSeries(timeSeries []writev2.TimeSeries, filter func(writev2.Time keepIdx++ } - timeSeries = timeSeries[:keepIdx] - return highest, lowest, timeSeries, droppedSamples, droppedExemplars, droppedHistograms + return timeSeries[:keepIdx], stats +} + +// timeSeriesStats tracks statistics during time series processing. +type timeSeriesStats struct { + highest int64 + lowest int64 + droppedSamples int + droppedExemplars int + droppedHistograms int +} + +// newTimeSeriesStats creates a new timeSeriesStats with lowest initialized to MaxInt64. +func newTimeSeriesStats() *timeSeriesStats { + return &timeSeriesStats{ + lowest: math.MaxInt64, + } +} + +// updateTimestamp updates highest and lowest timestamps if the given timestamp is valid. +func (s *timeSeriesStats) updateTimestamp(timestamp int64) { + if timestamp > 0 { + if timestamp > s.highest { + s.highest = timestamp + } + if timestamp < s.lowest { + s.lowest = timestamp + } + } +} + +// recordDropped increments the dropped counters based on what data exists. +func (s *timeSeriesStats) recordDropped(hasSamples, hasExemplars, hasHistograms bool) { + if hasSamples { + s.droppedSamples++ + } + if hasExemplars { + s.droppedExemplars++ + } + if hasHistograms { + s.droppedHistograms++ + } +} + +// sendBatchContext encapsulates the common parameters for sending batches. +// This reduces the number of function arguments (addresses "too many arguments" issue). +type sendBatchContext struct { + ctx context.Context + sampleCount int + exemplarCount int + histogramCount int + metadataCount int + reqSize int +} + +// batchMetricsUpdater encapsulates metrics update operations for batch sending. +type batchMetricsUpdater struct { + metrics *queueManagerMetrics + storeClient WriteClient + sentDuration prometheus.Observer +} + +// recordBatchAttempt records metrics for a batch send attempt. +func (b *batchMetricsUpdater) recordBatchAttempt(sc sendBatchContext, begin time.Time) { + b.metrics.samplesTotal.Add(float64(sc.sampleCount)) + b.metrics.exemplarsTotal.Add(float64(sc.exemplarCount)) + b.metrics.histogramsTotal.Add(float64(sc.histogramCount)) + b.metrics.metadataTotal.Add(float64(sc.metadataCount)) + b.sentDuration.Observe(time.Since(begin).Seconds()) +} + +// recordRetry records retry metrics for a batch. +func (b *batchMetricsUpdater) recordRetry(sc sendBatchContext) { + b.metrics.retriedSamplesTotal.Add(float64(sc.sampleCount)) + b.metrics.retriedExemplarsTotal.Add(float64(sc.exemplarCount)) + b.metrics.retriedHistogramsTotal.Add(float64(sc.histogramCount)) +} + +// createBatchSpan creates and configures an OpenTelemetry span for batch sending. +func createBatchSpan(ctx context.Context, sc sendBatchContext, remoteName, remoteURL string, try int) (context.Context, trace.Span) { + ctx, span := otel.Tracer("").Start(ctx, "Remote Send Batch") + span.SetAttributes( + attribute.Int("request_size", sc.reqSize), + attribute.Int("samples", sc.sampleCount), + attribute.Int("try", try), + attribute.String("remote_name", remoteName), + attribute.String("remote_url", remoteURL), + ) + if sc.exemplarCount > 0 { + span.SetAttributes(attribute.Int("exemplars", sc.exemplarCount)) + } + if sc.histogramCount > 0 { + span.SetAttributes(attribute.Int("histograms", sc.histogramCount)) + } + return ctx, span } diff --git a/vendor/github.com/prometheus/prometheus/storage/remote/write.go b/vendor/github.com/prometheus/prometheus/storage/remote/write.go index 6bc02bd6fed..1a036c17956 100644 --- a/vendor/github.com/prometheus/prometheus/storage/remote/write.go +++ b/vendor/github.com/prometheus/prometheus/storage/remote/write.go @@ -318,22 +318,22 @@ func (t *timestampTracker) AppendHistogram(_ storage.SeriesRef, _ labels.Labels, return 0, nil } -func (t *timestampTracker) AppendCTZeroSample(_ storage.SeriesRef, _ labels.Labels, _, ct int64) (storage.SeriesRef, error) { +func (t *timestampTracker) AppendSTZeroSample(_ storage.SeriesRef, _ labels.Labels, _, st int64) (storage.SeriesRef, error) { t.samples++ - if ct > t.highestTimestamp { - // Theoretically, we should never see a CT zero sample with a timestamp higher than the highest timestamp we've seen so far. + if st > t.highestTimestamp { + // Theoretically, we should never see a ST zero sample with a timestamp higher than the highest timestamp we've seen so far. // However, we're not going to enforce that here, as it is not the responsibility of the tracker to enforce this. - t.highestTimestamp = ct + t.highestTimestamp = st } return 0, nil } -func (t *timestampTracker) AppendHistogramCTZeroSample(_ storage.SeriesRef, _ labels.Labels, _, ct int64, _ *histogram.Histogram, _ *histogram.FloatHistogram) (storage.SeriesRef, error) { +func (t *timestampTracker) AppendHistogramSTZeroSample(_ storage.SeriesRef, _ labels.Labels, _, st int64, _ *histogram.Histogram, _ *histogram.FloatHistogram) (storage.SeriesRef, error) { t.histograms++ - if ct > t.highestTimestamp { - // Theoretically, we should never see a CT zero sample with a timestamp higher than the highest timestamp we've seen so far. + if st > t.highestTimestamp { + // Theoretically, we should never see a ST zero sample with a timestamp higher than the highest timestamp we've seen so far. // However, we're not going to enforce that here, as it is not the responsibility of the tracker to enforce this. - t.highestTimestamp = ct + t.highestTimestamp = st } return 0, nil } diff --git a/vendor/github.com/prometheus/prometheus/storage/remote/write_handler.go b/vendor/github.com/prometheus/prometheus/storage/remote/write_handler.go index 2bc65e8286a..4291b0505a3 100644 --- a/vendor/github.com/prometheus/prometheus/storage/remote/write_handler.go +++ b/vendor/github.com/prometheus/prometheus/storage/remote/write_handler.go @@ -53,7 +53,7 @@ type writeHandler struct { samplesWithInvalidLabelsTotal prometheus.Counter samplesAppendedWithoutMetadata prometheus.Counter - ingestCTZeroSample bool + ingestSTZeroSample bool enableTypeAndUnitLabels bool appendMetadata bool } @@ -65,7 +65,7 @@ const maxAheadTime = 10 * time.Minute // // NOTE(bwplotka): When accepting v2 proto and spec, partial writes are possible // as per https://prometheus.io/docs/specs/remote_write_spec_2_0/#partial-write. -func NewWriteHandler(logger *slog.Logger, reg prometheus.Registerer, appendable storage.Appendable, acceptedMsgs remoteapi.MessageTypes, ingestCTZeroSample, enableTypeAndUnitLabels, appendMetadata bool) http.Handler { +func NewWriteHandler(logger *slog.Logger, reg prometheus.Registerer, appendable storage.Appendable, acceptedMsgs remoteapi.MessageTypes, ingestSTZeroSample, enableTypeAndUnitLabels, appendMetadata bool) http.Handler { h := &writeHandler{ logger: logger, appendable: appendable, @@ -82,7 +82,7 @@ func NewWriteHandler(logger *slog.Logger, reg prometheus.Registerer, appendable Help: "The total number of received remote write samples (and histogram samples) which were ingested without corresponding metadata.", }), - ingestCTZeroSample: ingestCTZeroSample, + ingestSTZeroSample: ingestSTZeroSample, enableTypeAndUnitLabels: enableTypeAndUnitLabels, appendMetadata: appendMetadata, } @@ -329,7 +329,11 @@ func (h *writeHandler) appendV2(app storage.Appender, req *writev2.Request, rs * if h.enableTypeAndUnitLabels && (m.Type != model.MetricTypeUnknown || m.Unit != "") { slb := labels.NewScratchBuilder(ls.Len() + 2) // +2 for __type__ and __unit__ ls.Range(func(l labels.Label) { - slb.Add(l.Name, l.Value) + // Skip __type__ and __unit__ labels if they exist in the incoming labels. + // They will be added from metadata to avoid duplicates. + if l.Name != model.MetricTypeLabel && l.Name != model.MetricUnitLabel { + slb.Add(l.Name, l.Value) + } }) schema.Metadata{Type: m.Type, Unit: m.Unit}.AddToLabels(&slb) slb.Sort() @@ -358,13 +362,13 @@ func (h *writeHandler) appendV2(app storage.Appender, req *writev2.Request, rs * allSamplesSoFar := rs.AllSamples() var ref storage.SeriesRef for _, s := range ts.Samples { - if h.ingestCTZeroSample && s.StartTimestamp != 0 && s.Timestamp != 0 { - ref, err = app.AppendCTZeroSample(ref, ls, s.Timestamp, s.StartTimestamp) + if h.ingestSTZeroSample && s.StartTimestamp != 0 && s.Timestamp != 0 { + ref, err = app.AppendSTZeroSample(ref, ls, s.Timestamp, s.StartTimestamp) // We treat OOO errors specially as it's a common scenario given: // * We can't tell if ST was already ingested in a previous request. // * We don't check if ST changed for stream of samples (we typically have one though), // as it's checked in the AppendSTZeroSample reliably. - if err != nil && !errors.Is(err, storage.ErrOutOfOrderCT) { + if err != nil && !errors.Is(err, storage.ErrOutOfOrderST) { h.logger.Debug("Error when appending ST from remote write request", "err", err, "series", ls.String(), "start_timestamp", s.StartTimestamp, "timestamp", s.Timestamp) } } @@ -389,13 +393,13 @@ func (h *writeHandler) appendV2(app storage.Appender, req *writev2.Request, rs * // Native Histograms. for _, hp := range ts.Histograms { - if h.ingestCTZeroSample && hp.StartTimestamp != 0 && hp.Timestamp != 0 { + if h.ingestSTZeroSample && hp.StartTimestamp != 0 && hp.Timestamp != 0 { ref, err = h.handleHistogramZeroSample(app, ref, ls, hp, hp.StartTimestamp) // We treat OOO errors specially as it's a common scenario given: // * We can't tell if ST was already ingested in a previous request. // * We don't check if ST changed for stream of samples (we typically have one though), // as it's checked in the ingestSTZeroSample reliably. - if err != nil && !errors.Is(err, storage.ErrOutOfOrderCT) { + if err != nil && !errors.Is(err, storage.ErrOutOfOrderST) { h.logger.Debug("Error when appending ST from remote write request", "err", err, "series", ls.String(), "start_timestamp", hp.StartTimestamp, "timestamp", hp.Timestamp) } } @@ -480,9 +484,9 @@ func (h *writeHandler) appendV2(app storage.Appender, req *writev2.Request, rs * func (*writeHandler) handleHistogramZeroSample(app storage.Appender, ref storage.SeriesRef, l labels.Labels, hist writev2.Histogram, st int64) (storage.SeriesRef, error) { var err error if hist.IsFloatHistogram() { - ref, err = app.AppendHistogramCTZeroSample(ref, l, hist.Timestamp, st, nil, hist.ToFloatHistogram()) + ref, err = app.AppendHistogramSTZeroSample(ref, l, hist.Timestamp, st, nil, hist.ToFloatHistogram()) } else { - ref, err = app.AppendHistogramCTZeroSample(ref, l, hist.Timestamp, st, hist.ToIntHistogram(), nil) + ref, err = app.AppendHistogramSTZeroSample(ref, l, hist.Timestamp, st, hist.ToIntHistogram(), nil) } return ref, err } @@ -499,9 +503,11 @@ type OTLPOptions struct { LookbackDelta time.Duration // Add type and unit labels to the metrics. EnableTypeAndUnitLabels bool - // IngestCTZeroSample enables writing zero samples based on the start time + // IngestSTZeroSample enables writing zero samples based on the start time // of metrics. - IngestCTZeroSample bool + IngestSTZeroSample bool + // AppendMetadata enables writing metadata to WAL when metadata-wal-records feature is enabled. + AppendMetadata bool } // NewOTLPWriteHandler creates a http.Handler that accepts OTLP write requests and @@ -518,8 +524,9 @@ func NewOTLPWriteHandler(logger *slog.Logger, reg prometheus.Registerer, appenda config: configFunc, allowDeltaTemporality: opts.NativeDelta, lookbackDelta: opts.LookbackDelta, - ingestCTZeroSample: opts.IngestCTZeroSample, + ingestSTZeroSample: opts.IngestSTZeroSample, enableTypeAndUnitLabels: opts.EnableTypeAndUnitLabels, + appendMetadata: opts.AppendMetadata, // Register metrics. metrics: otlptranslator.NewCombinedAppenderMetrics(reg), } @@ -560,8 +567,9 @@ type rwExporter struct { config func() config.Config allowDeltaTemporality bool lookbackDelta time.Duration - ingestCTZeroSample bool + ingestSTZeroSample bool enableTypeAndUnitLabels bool + appendMetadata bool // Metrics. metrics otlptranslator.CombinedAppenderMetrics @@ -573,7 +581,7 @@ func (rw *rwExporter) ConsumeMetrics(ctx context.Context, md pmetric.Metrics) er Appender: rw.appendable.Appender(ctx), maxTime: timestamp.FromTime(time.Now().Add(maxAheadTime)), } - combinedAppender := otlptranslator.NewCombinedAppender(app, rw.logger, rw.ingestCTZeroSample, rw.metrics) + combinedAppender := otlptranslator.NewCombinedAppender(app, rw.logger, rw.ingestSTZeroSample, rw.appendMetadata, rw.metrics) converter := otlptranslator.NewPrometheusConverter(combinedAppender) annots, err := converter.FromMetrics(ctx, md, otlptranslator.Settings{ AddMetricSuffixes: otlpCfg.TranslationStrategy.ShouldAddSuffixes(), @@ -693,19 +701,23 @@ func (app *remoteWriteAppender) Append(ref storage.SeriesRef, lset labels.Labels } func (app *remoteWriteAppender) AppendHistogram(ref storage.SeriesRef, l labels.Labels, t int64, h *histogram.Histogram, fh *histogram.FloatHistogram) (storage.SeriesRef, error) { + var err error if t > app.maxTime { return 0, fmt.Errorf("%w: timestamp is too far in the future", storage.ErrOutOfBounds) } if h != nil && histogram.IsExponentialSchemaReserved(h.Schema) && h.Schema > histogram.ExponentialSchemaMax { - h = h.ReduceResolution(histogram.ExponentialSchemaMax) + if err = h.ReduceResolution(histogram.ExponentialSchemaMax); err != nil { + return 0, err + } } if fh != nil && histogram.IsExponentialSchemaReserved(fh.Schema) && fh.Schema > histogram.ExponentialSchemaMax { - fh = fh.ReduceResolution(histogram.ExponentialSchemaMax) + if err = fh.ReduceResolution(histogram.ExponentialSchemaMax); err != nil { + return 0, err + } } - ref, err := app.Appender.AppendHistogram(ref, l, t, h, fh) - if err != nil { + if ref, err = app.Appender.AppendHistogram(ref, l, t, h, fh); err != nil { return 0, err } return ref, nil diff --git a/vendor/github.com/prometheus/prometheus/template/template.go b/vendor/github.com/prometheus/prometheus/template/template.go index ea7e93b18c1..572e8450d35 100644 --- a/vendor/github.com/prometheus/prometheus/template/template.go +++ b/vendor/github.com/prometheus/prometheus/template/template.go @@ -36,6 +36,7 @@ import ( "golang.org/x/text/language" "github.com/prometheus/prometheus/promql" + "github.com/prometheus/prometheus/util/features" "github.com/prometheus/prometheus/util/strutil" ) @@ -413,3 +414,29 @@ func floatToTime(v float64) (*time.Time, error) { t := model.TimeFromUnixNano(int64(timestamp)).Time().UTC() return &t, nil } + +// templateFunctions returns a representative funcMap with all available template functions. +// This is used to discover which functions are available for feature registration. +func templateFunctions() text_template.FuncMap { + // Create a dummy expander to get the function map. + expander := NewTemplateExpander( + context.Background(), + "", + "", + nil, + 0, + nil, + &url.URL{}, + nil, + ) + return expander.funcMap +} + +// RegisterFeatures registers all template functions with the feature registry. +func RegisterFeatures(r features.Collector) { + // Get all function names from the template function map. + funcMap := templateFunctions() + for name := range funcMap { + r.Enable(features.TemplatingFunctions, name) + } +} diff --git a/vendor/github.com/prometheus/prometheus/tsdb/block.go b/vendor/github.com/prometheus/prometheus/tsdb/block.go index 44c6ef50531..dcbb172e723 100644 --- a/vendor/github.com/prometheus/prometheus/tsdb/block.go +++ b/vendor/github.com/prometheus/prometheus/tsdb/block.go @@ -102,11 +102,6 @@ type IndexReader interface { // LabelNames returns all the unique label names present in the index in sorted order. LabelNames(ctx context.Context, matchers ...*labels.Matcher) ([]string, error) - // LabelValueFor returns label value for the given label name in the series referred to by ID. - // If the series couldn't be found or the series doesn't have the requested label a - // storage.ErrNotFound is returned as error. - LabelValueFor(ctx context.Context, id storage.SeriesRef, label string) (string, error) - // LabelNamesFor returns all the label names for the series referred to by the postings. // The names returned are sorted. LabelNamesFor(ctx context.Context, postings index.Postings) ([]string, error) @@ -551,11 +546,6 @@ func (r blockIndexReader) Close() error { return nil } -// LabelValueFor returns label value for the given label name in the series referred to by ID. -func (r blockIndexReader) LabelValueFor(ctx context.Context, id storage.SeriesRef, label string) (string, error) { - return r.ir.LabelValueFor(ctx, id, label) -} - // LabelNamesFor returns all the label names for the series referred to by the postings. // The names returned are sorted. func (r blockIndexReader) LabelNamesFor(ctx context.Context, postings index.Postings) ([]string, error) { diff --git a/vendor/github.com/prometheus/prometheus/tsdb/blockwriter.go b/vendor/github.com/prometheus/prometheus/tsdb/blockwriter.go index 14137f12cca..e0388122241 100644 --- a/vendor/github.com/prometheus/prometheus/tsdb/blockwriter.go +++ b/vendor/github.com/prometheus/prometheus/tsdb/blockwriter.go @@ -86,6 +86,12 @@ func (w *BlockWriter) Appender(ctx context.Context) storage.Appender { return w.head.Appender(ctx) } +// AppenderV2 returns a new appender on the database. +// AppenderV2 can't be called concurrently. However, the returned AppenderV2 can safely be used concurrently. +func (w *BlockWriter) AppenderV2(ctx context.Context) storage.AppenderV2 { + return w.head.AppenderV2(ctx) +} + // Flush implements the Writer interface. This is where actual block writing // happens. After flush completes, no writes can be done. func (w *BlockWriter) Flush(ctx context.Context) (ulid.ULID, error) { diff --git a/vendor/github.com/prometheus/prometheus/tsdb/chunkenc/float_histogram.go b/vendor/github.com/prometheus/prometheus/tsdb/chunkenc/float_histogram.go index 8002dd0d4e9..d960e835f25 100644 --- a/vendor/github.com/prometheus/prometheus/tsdb/chunkenc/float_histogram.go +++ b/vendor/github.com/prometheus/prometheus/tsdb/chunkenc/float_histogram.go @@ -884,7 +884,14 @@ func (it *floatHistogramIterator) AtFloatHistogram(fh *histogram.FloatHistogram) // chunk is from a newer Prometheus version that supports higher // resolution. fh = fh.Copy() - fh.ReduceResolution(histogram.ExponentialSchemaMax) + if err := fh.ReduceResolution(histogram.ExponentialSchemaMax); err != nil { + // With the checks above, this can only happen + // with invalid data in a chunk. As this is a + // rare edge case of a rare edge case, we'd + // rather not create all the plumbing to handle + // this error gracefully. + panic(err) + } } return it.t, fh } @@ -915,7 +922,13 @@ func (it *floatHistogramIterator) AtFloatHistogram(fh *histogram.FloatHistogram) // This is a very slow path, but it should only happen if the // chunk is from a newer Prometheus version that supports higher // resolution. - fh.ReduceResolution(histogram.ExponentialSchemaMax) + if err := fh.ReduceResolution(histogram.ExponentialSchemaMax); err != nil { + // With the checks above, this can only happen with + // invalid data in a chunk. As this is a rare edge case + // of a rare edge case, we'd rather not create all the + // plumbing to handle this error gracefully. + panic(err) + } } return it.t, fh diff --git a/vendor/github.com/prometheus/prometheus/tsdb/chunkenc/histogram.go b/vendor/github.com/prometheus/prometheus/tsdb/chunkenc/histogram.go index cc1d771235d..be1c31ae76e 100644 --- a/vendor/github.com/prometheus/prometheus/tsdb/chunkenc/histogram.go +++ b/vendor/github.com/prometheus/prometheus/tsdb/chunkenc/histogram.go @@ -939,7 +939,14 @@ func (it *histogramIterator) AtHistogram(h *histogram.Histogram) (int64, *histog // chunk is from a newer Prometheus version that supports higher // resolution. h = h.Copy() - h.ReduceResolution(histogram.ExponentialSchemaMax) + if err := h.ReduceResolution(histogram.ExponentialSchemaMax); err != nil { + // With the checks above, this can only happen + // with invalid data in a chunk. As this is a + // rare edge case of a rare edge case, we'd + // rather not create all the plumbing to handle + // this error gracefully. + panic(err) + } } return it.t, h } @@ -970,7 +977,13 @@ func (it *histogramIterator) AtHistogram(h *histogram.Histogram) (int64, *histog // This is a very slow path, but it should only happen if the // chunk is from a newer Prometheus version that supports higher // resolution. - h.ReduceResolution(histogram.ExponentialSchemaMax) + if err := h.ReduceResolution(histogram.ExponentialSchemaMax); err != nil { + // With the checks above, this can only happen with + // invalid data in a chunk. As this is a rare edge case + // of a rare edge case, we'd rather not create all the + // plumbing to handle this error gracefully. + panic(err) + } } return it.t, h @@ -1000,7 +1013,14 @@ func (it *histogramIterator) AtFloatHistogram(fh *histogram.FloatHistogram) (int // chunk is from a newer Prometheus version that supports higher // resolution. fh = fh.Copy() - fh.ReduceResolution(histogram.ExponentialSchemaMax) + if err := fh.ReduceResolution(histogram.ExponentialSchemaMax); err != nil { + // With the checks above, this can only happen + // with invalid data in a chunk. As this is a + // rare edge case of a rare edge case, we'd + // rather not create all the plumbing to handle + // this error gracefully. + panic(err) + } } return it.t, fh } @@ -1039,7 +1059,13 @@ func (it *histogramIterator) AtFloatHistogram(fh *histogram.FloatHistogram) (int // This is a very slow path, but it should only happen if the // chunk is from a newer Prometheus version that supports higher // resolution. - fh.ReduceResolution(histogram.ExponentialSchemaMax) + if err := fh.ReduceResolution(histogram.ExponentialSchemaMax); err != nil { + // With the checks above, this can only happen with + // invalid data in a chunk. As this is a rare edge case + // of a rare edge case, we'd rather not create all the + // plumbing to handle this error gracefully. + panic(err) + } } return it.t, fh diff --git a/vendor/github.com/prometheus/prometheus/tsdb/chunks/head_chunks.go b/vendor/github.com/prometheus/prometheus/tsdb/chunks/head_chunks.go index 41fce69c723..5e143b8b322 100644 --- a/vendor/github.com/prometheus/prometheus/tsdb/chunks/head_chunks.go +++ b/vendor/github.com/prometheus/prometheus/tsdb/chunks/head_chunks.go @@ -20,6 +20,7 @@ import ( "fmt" "hash" "io" + "math" "os" "path/filepath" "slices" @@ -768,8 +769,25 @@ func (cdm *ChunkDiskMapper) Chunk(ref ChunkDiskMapperRef) (chunkenc.Chunk, error } } + if chkDataLen > uint64(math.MaxInt) { + return nil, &CorruptionErr{ + Dir: cdm.dir.Name(), + FileIndex: sgmIndex, + Err: fmt.Errorf("chunk length %d exceeds supported size", chkDataLen), + } + } + + chkDataLenInt := int(chkDataLen) + if chkDataLenStart > math.MaxInt-n-chkDataLenInt { + return nil, &CorruptionErr{ + Dir: cdm.dir.Name(), + FileIndex: sgmIndex, + Err: fmt.Errorf("chunk data end overflows supported size (start=%d, len=%d, n=%d)", chkDataLenStart, chkDataLenInt, n), + } + } + // Verify the chunk data end. - chkDataEnd := chkDataLenStart + n + int(chkDataLen) + chkDataEnd := chkDataLenStart + n + chkDataLenInt if chkDataEnd > mmapFile.byteSlice.Len() { return nil, &CorruptionErr{ Dir: cdm.dir.Name(), diff --git a/vendor/github.com/prometheus/prometheus/tsdb/compact.go b/vendor/github.com/prometheus/prometheus/tsdb/compact.go index 49e88d63205..7ad6f8bb240 100644 --- a/vendor/github.com/prometheus/prometheus/tsdb/compact.go +++ b/vendor/github.com/prometheus/prometheus/tsdb/compact.go @@ -87,6 +87,7 @@ type LeveledCompactor struct { maxBlockChunkSegmentSize int64 useUncachedIO bool mergeFunc storage.VerticalChunkSeriesMergeFunc + blockExcludeFunc BlockExcludeFilterFunc postingsEncoder index.PostingsEncoder postingsDecoderFactory PostingsDecoderFactory enableOverlappingCompaction bool @@ -160,16 +161,24 @@ type LeveledCompactorOptions struct { // PE specifies the postings encoder. It is called when compactor is writing out the postings for a label name/value pair during compaction. // If it is nil then the default encoder is used. At the moment that is the "raw" encoder. See index.EncodePostingsRaw for more. PE index.PostingsEncoder + // PD specifies the postings decoder factory to return different postings decoder based on BlockMeta. It is called when opening a block or opening the index file. // If it is nil then a default decoder is used, compatible with Prometheus v2. PD PostingsDecoderFactory + // MaxBlockChunkSegmentSize is the max block chunk segment size. If it is 0 then the default chunks.DefaultChunkSegmentSize is used. MaxBlockChunkSegmentSize int64 + // MergeFunc is used for merging series together in vertical compaction. By default storage.NewCompactingChunkSeriesMerger(storage.ChainedSeriesMerge) is used. MergeFunc storage.VerticalChunkSeriesMergeFunc + + // BlockExcludeFilter is used to decide which blocks are exluded from compactions. + BlockExcludeFilter BlockExcludeFilterFunc + // EnableOverlappingCompaction enables compaction of overlapping blocks. In Prometheus it is always enabled. // It is useful for downstream projects like Mimir, Cortex, Thanos where they have a separate component that does compaction. EnableOverlappingCompaction bool + // Metrics is set of metrics for Compactor. By default, NewCompactorMetrics would be called to initialize metrics unless it is provided. Metrics *CompactorMetrics // UseUncachedIO allows bypassing the page cache when appropriate. @@ -178,7 +187,9 @@ type LeveledCompactorOptions struct { type PostingsDecoderFactory func(meta *BlockMeta) index.PostingsDecoder -func DefaultPostingsDecoderFactory(*BlockMeta) index.PostingsDecoder { +type BlockExcludeFilterFunc func(meta *BlockMeta) bool + +func DefaultPostingsDecoderFactory(_ *BlockMeta) index.PostingsDecoder { return index.DecodePostingsRaw } @@ -226,6 +237,7 @@ func NewLeveledCompactorWithOptions(ctx context.Context, r prometheus.Registerer postingsEncoder: pe, postingsDecoderFactory: opts.PD, enableOverlappingCompaction: opts.EnableOverlappingCompaction, + blockExcludeFunc: opts.BlockExcludeFilter, }, nil } @@ -250,12 +262,19 @@ func (c *LeveledCompactor) Plan(dir string) ([]string, error) { if err != nil { return nil, err } + if c.blockExcludeFunc != nil && c.blockExcludeFunc(meta) { + break + } dms = append(dms, dirMeta{dir, meta}) } return c.plan(dms) } func (c *LeveledCompactor) plan(dms []dirMeta) ([]string, error) { + if len(dms) == 0 { + return nil, nil + } + slices.SortFunc(dms, func(a, b dirMeta) int { switch { case a.meta.MinTime < b.meta.MinTime: diff --git a/vendor/github.com/prometheus/prometheus/tsdb/db.go b/vendor/github.com/prometheus/prometheus/tsdb/db.go index c57ae84c9c0..f765710dd72 100644 --- a/vendor/github.com/prometheus/prometheus/tsdb/db.go +++ b/vendor/github.com/prometheus/prometheus/tsdb/db.go @@ -47,6 +47,7 @@ import ( "github.com/prometheus/prometheus/tsdb/tsdbutil" "github.com/prometheus/prometheus/tsdb/wlog" "github.com/prometheus/prometheus/util/compression" + "github.com/prometheus/prometheus/util/features" ) const ( @@ -93,6 +94,7 @@ func DefaultOptions() *Options { CompactionDelayMaxPercent: DefaultCompactionDelayMaxPercent, CompactionDelay: time.Duration(0), PostingsDecoderFactory: DefaultPostingsDecoderFactory, + BlockReloadInterval: 1 * time.Minute, } } @@ -219,6 +221,30 @@ type Options struct { // UseUncachedIO allows bypassing the page cache when appropriate. UseUncachedIO bool + + // EnableSTAsZeroSample represents 'created-timestamp-zero-ingestion' feature flag. + // If true, ST, if non-zero and earlier than sample timestamp, will be stored + // as a zero sample before the actual sample. + // + // The zero sample is best-effort, only debug log on failure is emitted. + // NOTE(bwplotka): This feature might be deprecated and removed once PROM-60 + // is implemented. + EnableSTAsZeroSample bool + + // EnableMetadataWALRecords represents 'metadata-wal-records' feature flag. + // NOTE(bwplotka): This feature might be deprecated and removed once PROM-60 + // is implemented. + EnableMetadataWALRecords bool + + // BlockCompactionExcludeFunc is a function which returns true for blocks that should NOT be compacted. + // It's passed down to the TSDB compactor. + BlockCompactionExcludeFunc BlockExcludeFilterFunc + + // BlockReloadInterval is the interval at which blocks are reloaded. + BlockReloadInterval time.Duration + + // FeatureRegistry is used to register TSDB features. + FeatureRegistry features.Collector } type NewCompactorFunc func(ctx context.Context, r prometheus.Registerer, l *slog.Logger, ranges []int64, pool chunkenc.Pool, opts *Options) (Compactor, error) @@ -779,6 +805,15 @@ func Open(dir string, l *slog.Logger, r prometheus.Registerer, opts *Options, st var rngs []int64 opts, rngs = validateOpts(opts, nil) + // Register TSDB features if a registry is provided. + if opts.FeatureRegistry != nil { + opts.FeatureRegistry.Set(features.TSDB, "exemplar_storage", opts.EnableExemplarStorage) + opts.FeatureRegistry.Set(features.TSDB, "delayed_compaction", opts.EnableDelayedCompaction) + opts.FeatureRegistry.Set(features.TSDB, "isolation", !opts.IsolationDisabled) + opts.FeatureRegistry.Set(features.TSDB, "use_uncached_io", opts.UseUncachedIO) + opts.FeatureRegistry.Enable(features.TSDB, "native_histograms") + } + return open(dir, l, r, opts, rngs, stats) } @@ -813,6 +848,9 @@ func validateOpts(opts *Options, rngs []int64) (*Options, []int64) { if opts.OutOfOrderTimeWindow < 0 { opts.OutOfOrderTimeWindow = 0 } + if opts.BlockReloadInterval < 1*time.Second { + opts.BlockReloadInterval = 1 * time.Second + } if len(rngs) == 0 { // Start with smallest block duration and create exponential buckets until the exceed the @@ -908,6 +946,7 @@ func open(dir string, l *slog.Logger, r prometheus.Registerer, opts *Options, rn EnableOverlappingCompaction: opts.EnableOverlappingCompaction, PD: opts.PostingsDecoderFactory, UseUncachedIO: opts.UseUncachedIO, + BlockExcludeFilter: opts.BlockCompactionExcludeFunc, }) } if err != nil { @@ -968,6 +1007,8 @@ func open(dir string, l *slog.Logger, r prometheus.Registerer, opts *Options, rn headOpts.OutOfOrderTimeWindow.Store(opts.OutOfOrderTimeWindow) headOpts.OutOfOrderCapMax.Store(opts.OutOfOrderCapMax) headOpts.EnableSharding = opts.EnableSharding + headOpts.EnableSTAsZeroSample = opts.EnableSTAsZeroSample + headOpts.EnableMetadataWALRecords = opts.EnableMetadataWALRecords if opts.WALReplayConcurrency > 0 { headOpts.WALReplayConcurrency = opts.WALReplayConcurrency } @@ -1097,7 +1138,7 @@ func (db *DB) run(ctx context.Context) { } select { - case <-time.After(1 * time.Minute): + case <-time.After(db.opts.BlockReloadInterval): db.cmtx.Lock() if err := db.reloadBlocks(); err != nil { db.logger.Error("reloadBlocks", "err", err) @@ -1131,11 +1172,16 @@ func (db *DB) run(ctx context.Context) { } } -// Appender opens a new appender against the database. +// Appender opens a new Appender against the database. func (db *DB) Appender(ctx context.Context) storage.Appender { return dbAppender{db: db, Appender: db.head.Appender(ctx)} } +// AppenderV2 opens a new AppenderV2 against the database. +func (db *DB) AppenderV2(ctx context.Context) storage.AppenderV2 { + return dbAppenderV2{db: db, AppenderV2: db.head.AppenderV2(ctx)} +} + // ApplyConfig applies a new config to the DB. // Behaviour of 'OutOfOrderTimeWindow' is as follows: // OOO enabled = oooTimeWindow > 0. OOO disabled = oooTimeWindow is 0. @@ -1249,6 +1295,36 @@ func (a dbAppender) Commit() error { return err } +// dbAppenderV2 wraps the DB's head appender and triggers compactions on commit +// if necessary. +type dbAppenderV2 struct { + storage.AppenderV2 + db *DB +} + +var _ storage.GetRef = dbAppenderV2{} + +func (a dbAppenderV2) GetRef(lset labels.Labels, hash uint64) (storage.SeriesRef, labels.Labels) { + if g, ok := a.AppenderV2.(storage.GetRef); ok { + return g.GetRef(lset, hash) + } + return 0, labels.EmptyLabels() +} + +func (a dbAppenderV2) Commit() error { + err := a.AppenderV2.Commit() + + // We could just run this check every few minutes practically. But for benchmarks + // and high frequency use cases this is the safer way. + if a.db.head.compactable() { + select { + case a.db.compactc <- struct{}{}: + default: + } + } + return err +} + // waitingForCompactionDelay returns true if the DB is waiting for the Head compaction delay. // This doesn't guarantee that the Head is really compactable. func (db *DB) waitingForCompactionDelay() bool { @@ -1981,6 +2057,13 @@ func (db *DB) Head() *Head { // Close the partition. func (db *DB) Close() error { + // Allow close-after-close operation for simpler use (e.g. tests). + select { + case <-db.donec: + return nil + default: + } + close(db.stopc) if db.compactCancel != nil { db.compactCancel() diff --git a/vendor/github.com/prometheus/prometheus/tsdb/head.go b/vendor/github.com/prometheus/prometheus/tsdb/head.go index 2c71977b1a9..25a1b88cec7 100644 --- a/vendor/github.com/prometheus/prometheus/tsdb/head.go +++ b/vendor/github.com/prometheus/prometheus/tsdb/head.go @@ -187,6 +187,20 @@ type HeadOptions struct { // EnableSharding enables ShardedPostings() support in the Head. EnableSharding bool + + // EnableSTAsZeroSample represents 'created-timestamp-zero-ingestion' feature flag. + // If true, ST, if non-empty and earlier than sample timestamp, will be stored + // as a zero sample before the actual sample. + // + // The zero sample is best-effort, only debug log on failure is emitted. + // NOTE(bwplotka): This feature might be deprecated and removed once PROM-60 + // is implemented. + EnableSTAsZeroSample bool + + // EnableMetadataWALRecords represents 'metadata-wal-records' feature flag. + // NOTE(bwplotka): This feature might be deprecated and removed once PROM-60 + // is implemented. + EnableMetadataWALRecords bool } const ( @@ -562,6 +576,7 @@ func newHeadMetrics(h *Head, r prometheus.Registerer) *headMetrics { m.checkpointDeleteTotal, m.checkpointCreationFail, m.checkpointCreationTotal, + m.oooHistogram, m.mmapChunksTotal, m.mmapChunkCorruptionTotal, m.snapshotReplayErrorTotal, diff --git a/vendor/github.com/prometheus/prometheus/tsdb/head_append.go b/vendor/github.com/prometheus/prometheus/tsdb/head_append.go index 8740d2f5ad5..356d1c453f8 100644 --- a/vendor/github.com/prometheus/prometheus/tsdb/head_append.go +++ b/vendor/github.com/prometheus/prometheus/tsdb/head_append.go @@ -83,14 +83,14 @@ func (a *initAppender) AppendHistogram(ref storage.SeriesRef, l labels.Labels, t return a.app.AppendHistogram(ref, l, t, h, fh) } -func (a *initAppender) AppendHistogramCTZeroSample(ref storage.SeriesRef, l labels.Labels, t, ct int64, h *histogram.Histogram, fh *histogram.FloatHistogram) (storage.SeriesRef, error) { +func (a *initAppender) AppendHistogramSTZeroSample(ref storage.SeriesRef, l labels.Labels, t, st int64, h *histogram.Histogram, fh *histogram.FloatHistogram) (storage.SeriesRef, error) { if a.app != nil { - return a.app.AppendHistogramCTZeroSample(ref, l, t, ct, h, fh) + return a.app.AppendHistogramSTZeroSample(ref, l, t, st, h, fh) } a.head.initTime(t) a.app = a.head.appender() - return a.app.AppendHistogramCTZeroSample(ref, l, t, ct, h, fh) + return a.app.AppendHistogramSTZeroSample(ref, l, t, st, h, fh) } func (a *initAppender) UpdateMetadata(ref storage.SeriesRef, l labels.Labels, m metadata.Metadata) (storage.SeriesRef, error) { @@ -102,15 +102,15 @@ func (a *initAppender) UpdateMetadata(ref storage.SeriesRef, l labels.Labels, m return a.app.UpdateMetadata(ref, l, m) } -func (a *initAppender) AppendCTZeroSample(ref storage.SeriesRef, lset labels.Labels, t, ct int64) (storage.SeriesRef, error) { +func (a *initAppender) AppendSTZeroSample(ref storage.SeriesRef, lset labels.Labels, t, st int64) (storage.SeriesRef, error) { if a.app != nil { - return a.app.AppendCTZeroSample(ref, lset, t, ct) + return a.app.AppendSTZeroSample(ref, lset, t, st) } a.head.initTime(t) a.app = a.head.appender() - return a.app.AppendCTZeroSample(ref, lset, t, ct) + return a.app.AppendSTZeroSample(ref, lset, t, st) } // initTime initializes a head with the first timestamp. This only needs to be called @@ -165,17 +165,19 @@ func (h *Head) appender() *headAppender { minValidTime := h.appendableMinValidTime() appendID, cleanupAppendIDsBelow := h.iso.newAppendID(minValidTime) // Every appender gets an ID that is cleared upon commit/rollback. return &headAppender{ - head: h, - minValidTime: minValidTime, - mint: math.MaxInt64, - maxt: math.MinInt64, - headMaxt: h.MaxTime(), - oooTimeWindow: h.opts.OutOfOrderTimeWindow.Load(), - seriesRefs: h.getRefSeriesBuffer(), - series: h.getSeriesBuffer(), - typesInBatch: h.getTypeMap(), - appendID: appendID, - cleanupAppendIDsBelow: cleanupAppendIDsBelow, + headAppenderBase: headAppenderBase{ + head: h, + minValidTime: minValidTime, + mint: math.MaxInt64, + maxt: math.MinInt64, + headMaxt: h.MaxTime(), + oooTimeWindow: h.opts.OutOfOrderTimeWindow.Load(), + seriesRefs: h.getRefSeriesBuffer(), + series: h.getSeriesBuffer(), + typesInBatch: h.getTypeMap(), + appendID: appendID, + cleanupAppendIDsBelow: cleanupAppendIDsBelow, + }, } } @@ -382,7 +384,7 @@ func (b *appendBatch) close(h *Head) { b.exemplars = nil } -type headAppender struct { +type headAppenderBase struct { head *Head minValidTime int64 // No samples below this timestamp are allowed. mint, maxt int64 @@ -397,7 +399,10 @@ type headAppender struct { appendID, cleanupAppendIDsBelow uint64 closed bool - hints *storage.AppendOptions +} +type headAppender struct { + headAppenderBase + hints *storage.AppendOptions } func (a *headAppender) SetOptions(opts *storage.AppendOptions) { @@ -483,12 +488,12 @@ func (a *headAppender) Append(ref storage.SeriesRef, lset labels.Labels, t int64 return storage.SeriesRef(s.ref), nil } -// AppendCTZeroSample appends synthetic zero sample for ct timestamp. It returns +// AppendSTZeroSample appends synthetic zero sample for st timestamp. It returns // error when sample can't be appended. See -// storage.CreatedTimestampAppender.AppendCTZeroSample for further documentation. -func (a *headAppender) AppendCTZeroSample(ref storage.SeriesRef, lset labels.Labels, t, ct int64) (storage.SeriesRef, error) { - if ct >= t { - return 0, storage.ErrCTNewerThanSample +// storage.StartTimestampAppender.AppendSTZeroSample for further documentation. +func (a *headAppender) AppendSTZeroSample(ref storage.SeriesRef, lset labels.Labels, t, st int64) (storage.SeriesRef, error) { + if st >= t { + return 0, storage.ErrSTNewerThanSample } s := a.head.series.getByID(chunks.HeadSeriesRef(ref)) @@ -500,11 +505,11 @@ func (a *headAppender) AppendCTZeroSample(ref storage.SeriesRef, lset labels.Lab } } - // Check if CT wouldn't be OOO vs samples we already might have for this series. + // Check if ST wouldn't be OOO vs samples we already might have for this series. // NOTE(bwplotka): This will be often hit as it's expected for long living - // counters to share the same CT. + // counters to share the same ST. s.Lock() - isOOO, _, err := s.appendable(ct, 0, a.headMaxt, a.minValidTime, a.oooTimeWindow) + isOOO, _, err := s.appendable(st, 0, a.headMaxt, a.minValidTime, a.oooTimeWindow) if err == nil { s.pendingCommit = true } @@ -513,19 +518,19 @@ func (a *headAppender) AppendCTZeroSample(ref storage.SeriesRef, lset labels.Lab return 0, err } if isOOO { - return storage.SeriesRef(s.ref), storage.ErrOutOfOrderCT + return storage.SeriesRef(s.ref), storage.ErrOutOfOrderST } - if ct > a.maxt { - a.maxt = ct + if st > a.maxt { + a.maxt = st } b := a.getCurrentBatch(stFloat, s.ref) - b.floats = append(b.floats, record.RefSample{Ref: s.ref, T: ct, V: 0.0}) + b.floats = append(b.floats, record.RefSample{Ref: s.ref, T: st, V: 0.0}) b.floatSeries = append(b.floatSeries, s) return storage.SeriesRef(s.ref), nil } -func (a *headAppender) getOrCreate(lset labels.Labels) (s *memSeries, created bool, err error) { +func (a *headAppenderBase) getOrCreate(lset labels.Labels) (s *memSeries, created bool, err error) { // Ensure no empty labels have gotten through. lset = lset.WithoutEmpty() if lset.IsEmpty() { @@ -550,7 +555,7 @@ func (a *headAppender) getOrCreate(lset labels.Labels) (s *memSeries, created bo // getCurrentBatch returns the current batch if it fits the provided sampleType // for the provided series. Otherwise, it adds a new batch and returns it. -func (a *headAppender) getCurrentBatch(st sampleType, s chunks.HeadSeriesRef) *appendBatch { +func (a *headAppenderBase) getCurrentBatch(st sampleType, s chunks.HeadSeriesRef) *appendBatch { h := a.head newBatch := func() *appendBatch { @@ -902,9 +907,9 @@ func (a *headAppender) AppendHistogram(ref storage.SeriesRef, lset labels.Labels return storage.SeriesRef(s.ref), nil } -func (a *headAppender) AppendHistogramCTZeroSample(ref storage.SeriesRef, lset labels.Labels, t, ct int64, h *histogram.Histogram, fh *histogram.FloatHistogram) (storage.SeriesRef, error) { - if ct >= t { - return 0, storage.ErrCTNewerThanSample +func (a *headAppender) AppendHistogramSTZeroSample(ref storage.SeriesRef, lset labels.Labels, t, st int64, h *histogram.Histogram, fh *histogram.FloatHistogram) (storage.SeriesRef, error) { + if st >= t { + return 0, storage.ErrSTNewerThanSample } s := a.head.series.getByID(chunks.HeadSeriesRef(ref)) @@ -919,7 +924,7 @@ func (a *headAppender) AppendHistogramCTZeroSample(ref storage.SeriesRef, lset l switch { case h != nil: zeroHistogram := &histogram.Histogram{ - // The CTZeroSample represents a counter reset by definition. + // The STZeroSample represents a counter reset by definition. CounterResetHint: histogram.CounterReset, // Replicate other fields to avoid needless chunk creation. Schema: h.Schema, @@ -927,41 +932,41 @@ func (a *headAppender) AppendHistogramCTZeroSample(ref storage.SeriesRef, lset l CustomValues: h.CustomValues, } s.Lock() - // For CTZeroSamples OOO is not allowed. + // For STZeroSamples OOO is not allowed. // We set it to true to make this implementation as close as possible to the float implementation. - isOOO, _, err := s.appendableHistogram(ct, zeroHistogram, a.headMaxt, a.minValidTime, a.oooTimeWindow) + isOOO, _, err := s.appendableHistogram(st, zeroHistogram, a.headMaxt, a.minValidTime, a.oooTimeWindow) if err != nil { s.Unlock() if errors.Is(err, storage.ErrOutOfOrderSample) { - return 0, storage.ErrOutOfOrderCT + return 0, storage.ErrOutOfOrderST } return 0, err } - // OOO is not allowed because after the first scrape, CT will be the same for most (if not all) future samples. + // OOO is not allowed because after the first scrape, ST will be the same for most (if not all) future samples. // This is to prevent the injected zero from being marked as OOO forever. if isOOO { s.Unlock() - return 0, storage.ErrOutOfOrderCT + return 0, storage.ErrOutOfOrderST } s.pendingCommit = true s.Unlock() - st := stHistogram + sTyp := stHistogram if h.UsesCustomBuckets() { - st = stCustomBucketHistogram + sTyp = stCustomBucketHistogram } - b := a.getCurrentBatch(st, s.ref) + b := a.getCurrentBatch(sTyp, s.ref) b.histograms = append(b.histograms, record.RefHistogramSample{ Ref: s.ref, - T: ct, + T: st, H: zeroHistogram, }) b.histogramSeries = append(b.histogramSeries, s) case fh != nil: zeroFloatHistogram := &histogram.FloatHistogram{ - // The CTZeroSample represents a counter reset by definition. + // The STZeroSample represents a counter reset by definition. CounterResetHint: histogram.CounterReset, // Replicate other fields to avoid needless chunk creation. Schema: fh.Schema, @@ -970,40 +975,40 @@ func (a *headAppender) AppendHistogramCTZeroSample(ref storage.SeriesRef, lset l } s.Lock() // We set it to true to make this implementation as close as possible to the float implementation. - isOOO, _, err := s.appendableFloatHistogram(ct, zeroFloatHistogram, a.headMaxt, a.minValidTime, a.oooTimeWindow) // OOO is not allowed for CTZeroSamples. + isOOO, _, err := s.appendableFloatHistogram(st, zeroFloatHistogram, a.headMaxt, a.minValidTime, a.oooTimeWindow) // OOO is not allowed for STZeroSamples. if err != nil { s.Unlock() if errors.Is(err, storage.ErrOutOfOrderSample) { - return 0, storage.ErrOutOfOrderCT + return 0, storage.ErrOutOfOrderST } return 0, err } - // OOO is not allowed because after the first scrape, CT will be the same for most (if not all) future samples. + // OOO is not allowed because after the first scrape, ST will be the same for most (if not all) future samples. // This is to prevent the injected zero from being marked as OOO forever. if isOOO { s.Unlock() - return 0, storage.ErrOutOfOrderCT + return 0, storage.ErrOutOfOrderST } s.pendingCommit = true s.Unlock() - st := stFloatHistogram + sTyp := stFloatHistogram if fh.UsesCustomBuckets() { - st = stCustomBucketFloatHistogram + sTyp = stCustomBucketFloatHistogram } - b := a.getCurrentBatch(st, s.ref) + b := a.getCurrentBatch(sTyp, s.ref) b.floatHistograms = append(b.floatHistograms, record.RefFloatHistogramSample{ Ref: s.ref, - T: ct, + T: st, FH: zeroFloatHistogram, }) b.floatHistogramSeries = append(b.floatHistogramSeries, s) } - if ct > a.maxt { - a.maxt = ct + if st > a.maxt { + a.maxt = st } return storage.SeriesRef(s.ref), nil @@ -1043,7 +1048,7 @@ func (a *headAppender) UpdateMetadata(ref storage.SeriesRef, lset labels.Labels, var _ storage.GetRef = &headAppender{} -func (a *headAppender) GetRef(lset labels.Labels, hash uint64) (storage.SeriesRef, labels.Labels) { +func (a *headAppenderBase) GetRef(lset labels.Labels, hash uint64) (storage.SeriesRef, labels.Labels) { s := a.head.series.getByHash(hash, lset) if s == nil { return 0, labels.EmptyLabels() @@ -1053,7 +1058,7 @@ func (a *headAppender) GetRef(lset labels.Labels, hash uint64) (storage.SeriesRe } // log writes all headAppender's data to the WAL. -func (a *headAppender) log() error { +func (a *headAppenderBase) log() error { if a.head.wal == nil { return nil } @@ -1185,7 +1190,7 @@ type appenderCommitContext struct { } // commitExemplars adds all exemplars from the provided batch to the head's exemplar storage. -func (a *headAppender) commitExemplars(b *appendBatch) { +func (a *headAppenderBase) commitExemplars(b *appendBatch) { // No errors logging to WAL, so pass the exemplars along to the in memory storage. for _, e := range b.exemplars { s := a.head.series.getByID(chunks.HeadSeriesRef(e.ref)) @@ -1205,7 +1210,7 @@ func (a *headAppender) commitExemplars(b *appendBatch) { } } -func (acc *appenderCommitContext) collectOOORecords(a *headAppender) { +func (acc *appenderCommitContext) collectOOORecords(a *headAppenderBase) { if a.head.wbl == nil { // WBL is not enabled. So no need to collect. acc.wblSamples = nil @@ -1310,7 +1315,7 @@ func handleAppendableError(err error, appended, oooRejected, oobRejected, tooOld // operations on the series after appending the samples. // // There are also specific functions to commit histograms and float histograms. -func (a *headAppender) commitFloats(b *appendBatch, acc *appenderCommitContext) { +func (a *headAppenderBase) commitFloats(b *appendBatch, acc *appenderCommitContext) { var ok, chunkCreated bool var series *memSeries @@ -1466,7 +1471,7 @@ func (a *headAppender) commitFloats(b *appendBatch, acc *appenderCommitContext) } // For details on the commitHistograms function, see the commitFloats docs. -func (a *headAppender) commitHistograms(b *appendBatch, acc *appenderCommitContext) { +func (a *headAppenderBase) commitHistograms(b *appendBatch, acc *appenderCommitContext) { var ok, chunkCreated bool var series *memSeries @@ -1575,7 +1580,7 @@ func (a *headAppender) commitHistograms(b *appendBatch, acc *appenderCommitConte } // For details on the commitFloatHistograms function, see the commitFloats docs. -func (a *headAppender) commitFloatHistograms(b *appendBatch, acc *appenderCommitContext) { +func (a *headAppenderBase) commitFloatHistograms(b *appendBatch, acc *appenderCommitContext) { var ok, chunkCreated bool var series *memSeries @@ -1697,7 +1702,7 @@ func commitMetadata(b *appendBatch) { } } -func (a *headAppender) unmarkCreatedSeriesAsPendingCommit() { +func (a *headAppenderBase) unmarkCreatedSeriesAsPendingCommit() { for _, s := range a.series { s.Lock() s.pendingCommit = false @@ -1707,7 +1712,7 @@ func (a *headAppender) unmarkCreatedSeriesAsPendingCommit() { // Commit writes to the WAL and adds the data to the Head. // TODO(codesome): Refactor this method to reduce indentation and make it more readable. -func (a *headAppender) Commit() (err error) { +func (a *headAppenderBase) Commit() (err error) { if a.closed { return ErrAppenderClosed } @@ -2238,7 +2243,7 @@ func handleChunkWriteError(err error) { } // Rollback removes the samples and exemplars from headAppender and writes any series to WAL. -func (a *headAppender) Rollback() (err error) { +func (a *headAppenderBase) Rollback() (err error) { if a.closed { return ErrAppenderClosed } diff --git a/vendor/github.com/prometheus/prometheus/tsdb/head_append_v2.go b/vendor/github.com/prometheus/prometheus/tsdb/head_append_v2.go new file mode 100644 index 00000000000..241fb42e975 --- /dev/null +++ b/vendor/github.com/prometheus/prometheus/tsdb/head_append_v2.go @@ -0,0 +1,398 @@ +// Copyright The Prometheus Authors +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package tsdb + +import ( + "context" + "errors" + "fmt" + "math" + + "github.com/prometheus/prometheus/model/exemplar" + "github.com/prometheus/prometheus/model/histogram" + "github.com/prometheus/prometheus/model/labels" + "github.com/prometheus/prometheus/model/value" + "github.com/prometheus/prometheus/storage" + "github.com/prometheus/prometheus/tsdb/chunks" + "github.com/prometheus/prometheus/tsdb/record" +) + +// initAppenderV2 is a helper to initialize the time bounds of the head +// upon the first sample it receives. +type initAppenderV2 struct { + app storage.AppenderV2 + head *Head +} + +var _ storage.GetRef = &initAppenderV2{} + +func (a *initAppenderV2) Append(ref storage.SeriesRef, ls labels.Labels, st, t int64, v float64, h *histogram.Histogram, fh *histogram.FloatHistogram, opts storage.AOptions) (storage.SeriesRef, error) { + if a.app == nil { + a.head.initTime(t) + a.app = a.head.appenderV2() + } + return a.app.Append(ref, ls, st, t, v, h, fh, opts) +} + +func (a *initAppenderV2) GetRef(lset labels.Labels, hash uint64) (storage.SeriesRef, labels.Labels) { + if g, ok := a.app.(storage.GetRef); ok { + return g.GetRef(lset, hash) + } + return 0, labels.EmptyLabels() +} + +func (a *initAppenderV2) Commit() error { + if a.app == nil { + a.head.metrics.activeAppenders.Dec() + return nil + } + return a.app.Commit() +} + +func (a *initAppenderV2) Rollback() error { + if a.app == nil { + a.head.metrics.activeAppenders.Dec() + return nil + } + return a.app.Rollback() +} + +// AppenderV2 returns a new AppenderV2 on the database. +func (h *Head) AppenderV2(context.Context) storage.AppenderV2 { + h.metrics.activeAppenders.Inc() + + // The head cache might not have a starting point yet. The init appender + // picks up the first appended timestamp as the base. + if !h.initialized() { + return &initAppenderV2{ + head: h, + } + } + return h.appenderV2() +} + +func (h *Head) appenderV2() *headAppenderV2 { + minValidTime := h.appendableMinValidTime() + appendID, cleanupAppendIDsBelow := h.iso.newAppendID(minValidTime) // Every appender gets an ID that is cleared upon commit/rollback. + return &headAppenderV2{ + headAppenderBase: headAppenderBase{ + head: h, + minValidTime: minValidTime, + mint: math.MaxInt64, + maxt: math.MinInt64, + headMaxt: h.MaxTime(), + oooTimeWindow: h.opts.OutOfOrderTimeWindow.Load(), + seriesRefs: h.getRefSeriesBuffer(), + series: h.getSeriesBuffer(), + typesInBatch: h.getTypeMap(), + appendID: appendID, + cleanupAppendIDsBelow: cleanupAppendIDsBelow, + }, + } +} + +type headAppenderV2 struct { + headAppenderBase +} + +func (a *headAppenderV2) Append(ref storage.SeriesRef, ls labels.Labels, st, t int64, v float64, h *histogram.Histogram, fh *histogram.FloatHistogram, opts storage.AOptions) (storage.SeriesRef, error) { + var ( + // Avoid shadowing err variables for reliability. + valErr, appErr, partialErr error + sampleMetricType = sampleMetricTypeFloat + isStale bool + ) + // Fail fast on incorrect histograms. + + switch { + case fh != nil: + sampleMetricType = sampleMetricTypeHistogram + valErr = fh.Validate() + case h != nil: + sampleMetricType = sampleMetricTypeHistogram + valErr = h.Validate() + } + if valErr != nil { + return 0, valErr + } + + // Fail fast if OOO is disabled and the sample is out of bounds. + // Otherwise, a full check will be done later to decide if the sample is in-order or out-of-order. + if a.oooTimeWindow == 0 && t < a.minValidTime { + a.head.metrics.outOfBoundSamples.WithLabelValues(sampleMetricType).Inc() + return 0, storage.ErrOutOfBounds + } + + s := a.head.series.getByID(chunks.HeadSeriesRef(ref)) + if s == nil { + var err error + s, _, err = a.getOrCreate(ls) + if err != nil { + return 0, err + } + } + + // TODO(bwplotka): Handle ST natively (as per PROM-60). + if a.head.opts.EnableSTAsZeroSample && st != 0 { + a.bestEffortAppendSTZeroSample(s, ls, st, t, h, fh) + } + + switch { + case fh != nil: + isStale = value.IsStaleNaN(fh.Sum) + appErr = a.appendFloatHistogram(s, t, fh, opts.RejectOutOfOrder) + case h != nil: + isStale = value.IsStaleNaN(h.Sum) + appErr = a.appendHistogram(s, t, h, opts.RejectOutOfOrder) + default: + isStale = value.IsStaleNaN(v) + if isStale { + // If we have added a sample before with this same appender, we + // can check the previously used type and turn a stale float + // sample into a stale histogram sample or stale float histogram + // sample as appropriate. This prevents an unnecessary creation + // of a new batch. However, since other appenders might append + // to the same series concurrently, this is not perfect but just + // an optimization for the more likely case. + switch a.typesInBatch[s.ref] { + case stHistogram, stCustomBucketHistogram: + return a.Append(storage.SeriesRef(s.ref), ls, st, t, 0, &histogram.Histogram{Sum: v}, nil, storage.AOptions{ + RejectOutOfOrder: opts.RejectOutOfOrder, + }) + case stFloatHistogram, stCustomBucketFloatHistogram: + return a.Append(storage.SeriesRef(s.ref), ls, st, t, 0, nil, &histogram.FloatHistogram{Sum: v}, storage.AOptions{ + RejectOutOfOrder: opts.RejectOutOfOrder, + }) + } + // Note that a series reference not yet in the map will come out + // as stNone, but since we do not handle that case separately, + // we do not need to check for the difference between "unknown + // series" and "known series with stNone". + } + appErr = a.appendFloat(s, t, v, opts.RejectOutOfOrder) + } + // Handle append error, if any. + if appErr != nil { + switch { + case errors.Is(appErr, storage.ErrOutOfOrderSample): + a.head.metrics.outOfOrderSamples.WithLabelValues(sampleMetricType).Inc() + case errors.Is(appErr, storage.ErrTooOldSample): + a.head.metrics.tooOldSamples.WithLabelValues(sampleMetricType).Inc() + } + return 0, appErr + } + + if t < a.mint { + a.mint = t + } + if t > a.maxt { + a.maxt = t + } + + if isStale { + // For stale values we never attempt to process metadata/exemplars, claim the success. + return storage.SeriesRef(s.ref), nil + } + + // Append exemplars if any and if storage was configured for it. + if len(opts.Exemplars) > 0 && a.head.opts.EnableExemplarStorage && a.head.opts.MaxExemplars.Load() > 0 { + // Currently only exemplars can return partial errors. + partialErr = a.appendExemplars(s, opts.Exemplars) + } + + // TODO(bwplotka): Move/reuse metadata tests from scrape, once scrape adopts AppenderV2. + // Currently tsdb package does not test metadata. + if a.head.opts.EnableMetadataWALRecords && !opts.Metadata.IsEmpty() { + s.Lock() + metaChanged := s.meta == nil || !s.meta.Equals(opts.Metadata) + s.Unlock() + if metaChanged { + b := a.getCurrentBatch(stNone, s.ref) + b.metadata = append(b.metadata, record.RefMetadata{ + Ref: s.ref, + Type: record.GetMetricType(opts.Metadata.Type), + Unit: opts.Metadata.Unit, + Help: opts.Metadata.Help, + }) + b.metadataSeries = append(b.metadataSeries, s) + } + } + return storage.SeriesRef(s.ref), partialErr +} + +func (a *headAppenderV2) appendFloat(s *memSeries, t int64, v float64, fastRejectOOO bool) error { + s.Lock() + // TODO(codesome): If we definitely know at this point that the sample is ooo, then optimise + // to skip that sample from the WAL and write only in the WBL. + isOOO, delta, err := s.appendable(t, v, a.headMaxt, a.minValidTime, a.oooTimeWindow) + if isOOO && fastRejectOOO { + s.Unlock() + return storage.ErrOutOfOrderSample + } + if err == nil { + s.pendingCommit = true + } + s.Unlock() + if delta > 0 { + a.head.metrics.oooHistogram.Observe(float64(delta) / 1000) + } + if err != nil { + return err + } + + b := a.getCurrentBatch(stFloat, s.ref) + b.floats = append(b.floats, record.RefSample{Ref: s.ref, T: t, V: v}) + b.floatSeries = append(b.floatSeries, s) + return nil +} + +func (a *headAppenderV2) appendHistogram(s *memSeries, t int64, h *histogram.Histogram, fastRejectOOO bool) error { + s.Lock() + // TODO(codesome): If we definitely know at this point that the sample is ooo, then optimise + // to skip that sample from the WAL and write only in the WBL. + isOOO, delta, err := s.appendableHistogram(t, h, a.headMaxt, a.minValidTime, a.oooTimeWindow) + if isOOO && fastRejectOOO { + s.Unlock() + return storage.ErrOutOfOrderSample + } + if err == nil { + s.pendingCommit = true + } + s.Unlock() + if delta > 0 { + a.head.metrics.oooHistogram.Observe(float64(delta) / 1000) + } + if err != nil { + return err + } + st := stHistogram + if h.UsesCustomBuckets() { + st = stCustomBucketHistogram + } + b := a.getCurrentBatch(st, s.ref) + b.histograms = append(b.histograms, record.RefHistogramSample{Ref: s.ref, T: t, H: h}) + b.histogramSeries = append(b.histogramSeries, s) + return nil +} + +func (a *headAppenderV2) appendFloatHistogram(s *memSeries, t int64, fh *histogram.FloatHistogram, fastRejectOOO bool) error { + s.Lock() + // TODO(codesome): If we definitely know at this point that the sample is ooo, then optimise + // to skip that sample from the WAL and write only in the WBL. + isOOO, delta, err := s.appendableFloatHistogram(t, fh, a.headMaxt, a.minValidTime, a.oooTimeWindow) + if isOOO && fastRejectOOO { + s.Unlock() + return storage.ErrOutOfOrderSample + } + if err == nil { + s.pendingCommit = true + } + s.Unlock() + if delta > 0 { + a.head.metrics.oooHistogram.Observe(float64(delta) / 1000) + } + if err != nil { + return err + } + st := stFloatHistogram + if fh.UsesCustomBuckets() { + st = stCustomBucketFloatHistogram + } + b := a.getCurrentBatch(st, s.ref) + b.floatHistograms = append(b.floatHistograms, record.RefFloatHistogramSample{Ref: s.ref, T: t, FH: fh}) + b.floatHistogramSeries = append(b.floatHistogramSeries, s) + return nil +} + +func (a *headAppenderV2) appendExemplars(s *memSeries, exemplar []exemplar.Exemplar) error { + var errs []error + for _, e := range exemplar { + // Ensure no empty labels have gotten through. + e.Labels = e.Labels.WithoutEmpty() + if err := a.head.exemplars.ValidateExemplar(s.labels(), e); err != nil { + if !errors.Is(err, storage.ErrDuplicateExemplar) && !errors.Is(err, storage.ErrExemplarsDisabled) { + // Except duplicates, return partial errors. + errs = append(errs, err) + continue + } + if !errors.Is(err, storage.ErrOutOfOrderExemplar) { + a.head.logger.Debug("Error while adding an exemplar on AppendSample", "exemplars", fmt.Sprintf("%+v", e), "err", err) + } + continue + } + b := a.getCurrentBatch(stNone, s.ref) + b.exemplars = append(b.exemplars, exemplarWithSeriesRef{storage.SeriesRef(s.ref), e}) + } + if len(errs) > 0 { + return &storage.AppendPartialError{ExemplarErrors: errs} + } + return nil +} + +// NOTE(bwplotka): This feature might be deprecated and removed once PROM-60 +// is implemented. +// +// ST is an experimental feature, we don't fail the append on errors, just debug log. +func (a *headAppenderV2) bestEffortAppendSTZeroSample(s *memSeries, ls labels.Labels, st, t int64, h *histogram.Histogram, fh *histogram.FloatHistogram) { + // NOTE: Use lset instead of s.lset to avoid locking memSeries. Using s.ref is acceptable without locking. + if st >= t { + a.head.logger.Debug("Error when appending ST", "series", ls.String(), "st", st, "t", t, "err", storage.ErrSTNewerThanSample) + return + } + if st < a.minValidTime { + a.head.logger.Debug("Error when appending ST", "series", ls.String(), "st", st, "t", t, "err", storage.ErrOutOfBounds) + return + } + + var err error + switch { + case fh != nil: + zeroFloatHistogram := &histogram.FloatHistogram{ + // The STZeroSample represents a counter reset by definition. + CounterResetHint: histogram.CounterReset, + // Replicate other fields to avoid needless chunk creation. + Schema: fh.Schema, + ZeroThreshold: fh.ZeroThreshold, + CustomValues: fh.CustomValues, + } + err = a.appendFloatHistogram(s, st, zeroFloatHistogram, true) + case h != nil: + zeroHistogram := &histogram.Histogram{ + // The STZeroSample represents a counter reset by definition. + CounterResetHint: histogram.CounterReset, + // Replicate other fields to avoid needless chunk creation. + Schema: h.Schema, + ZeroThreshold: h.ZeroThreshold, + CustomValues: h.CustomValues, + } + err = a.appendHistogram(s, st, zeroHistogram, true) + default: + err = a.appendFloat(s, st, 0, true) + } + + if err != nil { + if errors.Is(err, storage.ErrOutOfOrderSample) { + // OOO errors are common and expected (cumulative). Explicitly ignored. + return + } + a.head.logger.Debug("Error when appending ST", "series", s.lset.String(), "st", st, "t", t, "err", err) + return + } + + if st > a.maxt { + a.maxt = st + } +} + +var _ storage.GetRef = &headAppenderV2{} diff --git a/vendor/github.com/prometheus/prometheus/tsdb/head_read.go b/vendor/github.com/prometheus/prometheus/tsdb/head_read.go index 8485d654357..f2681accc04 100644 --- a/vendor/github.com/prometheus/prometheus/tsdb/head_read.go +++ b/vendor/github.com/prometheus/prometheus/tsdb/head_read.go @@ -261,21 +261,6 @@ func unpackHeadChunkRef(ref chunks.ChunkRef) (seriesID chunks.HeadSeriesRef, chu return sid, (cid & (oooChunkIDMask - 1)), (cid & oooChunkIDMask) != 0 } -// LabelValueFor returns label value for the given label name in the series referred to by ID. -func (h *headIndexReader) LabelValueFor(_ context.Context, id storage.SeriesRef, label string) (string, error) { - memSeries := h.head.series.getByID(chunks.HeadSeriesRef(id)) - if memSeries == nil { - return "", storage.ErrNotFound - } - - value := memSeries.labels().Get(label) - if value == "" { - return "", storage.ErrNotFound - } - - return value, nil -} - // LabelNamesFor returns all the label names for the series referred to by the postings. // The names returned are sorted. func (h *headIndexReader) LabelNamesFor(ctx context.Context, series index.Postings) ([]string, error) { diff --git a/vendor/github.com/prometheus/prometheus/tsdb/index/index.go b/vendor/github.com/prometheus/prometheus/tsdb/index/index.go index 28eacd7c007..253a5158159 100644 --- a/vendor/github.com/prometheus/prometheus/tsdb/index/index.go +++ b/vendor/github.com/prometheus/prometheus/tsdb/index/index.go @@ -1447,32 +1447,6 @@ func (r *Reader) LabelNamesFor(ctx context.Context, postings Postings) ([]string return names, nil } -// LabelValueFor returns label value for the given label name in the series referred to by ID. -func (r *Reader) LabelValueFor(ctx context.Context, id storage.SeriesRef, label string) (string, error) { - offset := id - // In version 2 series IDs are no longer exact references but series are 16-byte padded - // and the ID is the multiple of 16 of the actual position. - if r.version != FormatV1 { - offset = id * seriesByteAlign - } - d := encoding.NewDecbufUvarintAt(r.b, int(offset), castagnoliTable) - buf := d.Get() - if d.Err() != nil { - return "", fmt.Errorf("label values for: %w", d.Err()) - } - - value, err := r.dec.LabelValueFor(ctx, buf, label) - if err != nil { - return "", storage.ErrNotFound - } - - if value == "" { - return "", storage.ErrNotFound - } - - return value, nil -} - // Series reads the series with the given ID and writes its labels and chunks into builder and chks. func (r *Reader) Series(id storage.SeriesRef, builder *labels.ScratchBuilder, chks *[]chunks.Meta) error { offset := id @@ -1809,37 +1783,6 @@ func (*Decoder) LabelNamesOffsetsFor(b []byte) ([]uint32, error) { return offsets, d.Err() } -// LabelValueFor decodes a label for a given series. -func (dec *Decoder) LabelValueFor(ctx context.Context, b []byte, label string) (string, error) { - d := encoding.Decbuf{B: b} - k := d.Uvarint() - - for range k { - lno := uint32(d.Uvarint()) - lvo := uint32(d.Uvarint()) - - if d.Err() != nil { - return "", fmt.Errorf("read series label offsets: %w", d.Err()) - } - - ln, err := dec.LookupSymbol(ctx, lno) - if err != nil { - return "", fmt.Errorf("lookup label name: %w", err) - } - - if ln == label { - lv, err := dec.LookupSymbol(ctx, lvo) - if err != nil { - return "", fmt.Errorf("lookup label value: %w", err) - } - - return lv, nil - } - } - - return "", d.Err() -} - // Series decodes a series entry from the given byte slice into builder and chks. // Previous contents of builder can be overwritten - make sure you copy before retaining. // Skips reading chunks metadata if chks is nil. diff --git a/vendor/github.com/prometheus/prometheus/tsdb/index/postings.go b/vendor/github.com/prometheus/prometheus/tsdb/index/postings.go index d5a17c3daac..0185f58819f 100644 --- a/vendor/github.com/prometheus/prometheus/tsdb/index/postings.go +++ b/vendor/github.com/prometheus/prometheus/tsdb/index/postings.go @@ -391,7 +391,7 @@ func (p *MemPostings) Iter(f func(labels.Label, Postings) error) error { for n, e := range p.m { for v, p := range e { - if err := f(labels.Label{Name: n, Value: v}, newListPostings(p...)); err != nil { + if err := f(labels.Label{Name: n, Value: v}, NewListPostings(p)); err != nil { return err } } @@ -478,8 +478,8 @@ func (p *MemPostings) PostingsForLabelMatching(ctx context.Context, name string, } // Now `vals` only contains the values that matched, get their postings. - its := make([]*ListPostings, 0, len(vals)) - lps := make([]ListPostings, len(vals)) + its := make([]*listPostings, 0, len(vals)) + lps := make([]listPostings, len(vals)) p.mtx.RLock() e := p.m[name] for i, v := range vals { @@ -488,7 +488,7 @@ func (p *MemPostings) PostingsForLabelMatching(ctx context.Context, name string, // If we didn't let the mutex go, we'd have these postings here, but they would be pointing nowhere // because there would be a `MemPostings.Delete()` call waiting for the lock to delete these labels, // because the series were deleted already. - lps[i] = ListPostings{list: refs} + lps[i] = listPostings{list: refs} its = append(its, &lps[i]) } } @@ -500,13 +500,13 @@ func (p *MemPostings) PostingsForLabelMatching(ctx context.Context, name string, // Postings returns a postings iterator for the given label values. func (p *MemPostings) Postings(ctx context.Context, name string, values ...string) Postings { - res := make([]*ListPostings, 0, len(values)) - lps := make([]ListPostings, len(values)) + res := make([]*listPostings, 0, len(values)) + lps := make([]listPostings, len(values)) p.mtx.RLock() postingsMapForName := p.m[name] for i, value := range values { if lp := postingsMapForName[value]; lp != nil { - lps[i] = ListPostings{list: lp} + lps[i] = listPostings{list: lp} res = append(res, &lps[i]) } } @@ -518,12 +518,12 @@ func (p *MemPostings) PostingsForAllLabelValues(ctx context.Context, name string p.mtx.RLock() e := p.m[name] - its := make([]*ListPostings, 0, len(e)) - lps := make([]ListPostings, len(e)) + its := make([]*listPostings, 0, len(e)) + lps := make([]listPostings, len(e)) i := 0 for _, refs := range e { if len(refs) > 0 { - lps[i] = ListPostings{list: refs} + lps[i] = listPostings{list: refs} its = append(its, &lps[i]) } i++ @@ -542,7 +542,7 @@ func ExpandPostings(p Postings) (res []storage.SeriesRef, err error) { return res, p.Err() } -// Postings provides iterative access over a postings list. +// Postings provides iterative access over an ordered list of SeriesRef. type Postings interface { // Next advances the iterator and returns true if another value was found. Next() bool @@ -641,15 +641,28 @@ func (it *intersectPostings) Seek(target storage.SeriesRef) bool { } func (it *intersectPostings) Next() bool { - target := it.current - for _, p := range it.postings { + // Move forward the first Postings and take its value as the target to match. + if !it.postings[0].Next() { + return false + } + target := it.postings[0].At() + allEqual := true + for _, p := range it.postings[1:] { // Now move forward all the other ones and check if they match. if !p.Next() { return false } - if p.At() > target { - target = p.At() + at := p.At() + if at > target { // This one is past the target, so pick up a new target to Seek at the end. + target = at + allEqual = false + } else if at < target { // This one needs to Seek to the target, but carry on with other postings in case they have an even higher target. + allEqual = false } } + if allEqual { + it.current = target + return true + } return it.Seek(target) } @@ -814,25 +827,23 @@ func (rp *removedPostings) Err() error { return rp.remove.Err() } -// ListPostings implements the Postings interface over a plain list. -type ListPostings struct { +// listPostings implements the Postings interface over a plain list. +type listPostings struct { list []storage.SeriesRef cur storage.SeriesRef } +// NewListPostings creates a Postings from the supplied SeriesRefs, which must be in order. +// The list slice passed in is retained. func NewListPostings(list []storage.SeriesRef) Postings { - return newListPostings(list...) -} - -func newListPostings(list ...storage.SeriesRef) *ListPostings { - return &ListPostings{list: list} + return &listPostings{list: list} } -func (it *ListPostings) At() storage.SeriesRef { +func (it *listPostings) At() storage.SeriesRef { return it.cur } -func (it *ListPostings) Next() bool { +func (it *listPostings) Next() bool { if len(it.list) > 0 { it.cur = it.list[0] it.list = it.list[1:] @@ -842,7 +853,7 @@ func (it *ListPostings) Next() bool { return false } -func (it *ListPostings) Seek(x storage.SeriesRef) bool { +func (it *listPostings) Seek(x storage.SeriesRef) bool { // If the current value satisfies, then return. if it.cur >= x { return true @@ -851,23 +862,25 @@ func (it *ListPostings) Seek(x storage.SeriesRef) bool { return false } - // Do binary search between current position and end. - i, _ := slices.BinarySearch(it.list, x) - if i < len(it.list) { - it.cur = it.list[i] - it.list = it.list[i+1:] - return true + i := 0 // Check the next item in the list, otherwise binary search between current position and end. + if it.list[0] < x { + i, _ = slices.BinarySearch(it.list, x) + if i >= len(it.list) { // Off the end - terminate the iterator. + it.list = nil + return false + } } - it.list = nil - return false + it.cur = it.list[i] + it.list = it.list[i+1:] + return true } -func (*ListPostings) Err() error { +func (*listPostings) Err() error { return nil } // Len returns the remaining number of postings in the list. -func (it *ListPostings) Len() int { +func (it *listPostings) Len() int { return len(it.list) } diff --git a/vendor/github.com/prometheus/prometheus/tsdb/ooo_head_read.go b/vendor/github.com/prometheus/prometheus/tsdb/ooo_head_read.go index af8f9b1f83f..4cecb9fd6c4 100644 --- a/vendor/github.com/prometheus/prometheus/tsdb/ooo_head_read.go +++ b/vendor/github.com/prometheus/prometheus/tsdb/ooo_head_read.go @@ -500,10 +500,6 @@ func (*OOOCompactionHeadIndexReader) LabelNames(context.Context, ...*labels.Matc return nil, errors.New("not implemented") } -func (*OOOCompactionHeadIndexReader) LabelValueFor(context.Context, storage.SeriesRef, string) (string, error) { - return "", errors.New("not implemented") -} - func (*OOOCompactionHeadIndexReader) LabelNamesFor(context.Context, index.Postings) ([]string, error) { return nil, errors.New("not implemented") } diff --git a/vendor/github.com/prometheus/prometheus/tsdb/record/record.go b/vendor/github.com/prometheus/prometheus/tsdb/record/record.go index 561810a3a54..5791f60df40 100644 --- a/vendor/github.com/prometheus/prometheus/tsdb/record/record.go +++ b/vendor/github.com/prometheus/prometheus/tsdb/record/record.go @@ -475,7 +475,9 @@ func (d *Decoder) HistogramSamples(rec []byte, histograms []RefHistogramSample) // This is a very slow path, but it should only happen if the // record is from a newer Prometheus version that supports higher // resolution. - rh.H.ReduceResolution(histogram.ExponentialSchemaMax) + if err := rh.H.ReduceResolution(histogram.ExponentialSchemaMax); err != nil { + return nil, fmt.Errorf("error reducing resolution of histogram #%d: %w", len(histograms)+1, err) + } } histograms = append(histograms, rh) @@ -579,7 +581,9 @@ func (d *Decoder) FloatHistogramSamples(rec []byte, histograms []RefFloatHistogr // This is a very slow path, but it should only happen if the // record is from a newer Prometheus version that supports higher // resolution. - rh.FH.ReduceResolution(histogram.ExponentialSchemaMax) + if err := rh.FH.ReduceResolution(histogram.ExponentialSchemaMax); err != nil { + return nil, fmt.Errorf("error reducing resolution of histogram #%d: %w", len(histograms)+1, err) + } } histograms = append(histograms, rh) diff --git a/vendor/github.com/prometheus/prometheus/tsdb/testutil.go b/vendor/github.com/prometheus/prometheus/tsdb/testutil.go index 4d413322c8a..d41591750b6 100644 --- a/vendor/github.com/prometheus/prometheus/tsdb/testutil.go +++ b/vendor/github.com/prometheus/prometheus/tsdb/testutil.go @@ -44,14 +44,14 @@ type testValue struct { type sampleTypeScenario struct { sampleType string - appendFunc func(appender storage.Appender, lbls labels.Labels, ts, value int64) (storage.SeriesRef, sample, error) + appendFunc func(appender storage.LimitedAppenderV1, lbls labels.Labels, ts, value int64) (storage.SeriesRef, sample, error) sampleFunc func(ts, value int64) sample } var sampleTypeScenarios = map[string]sampleTypeScenario{ float: { sampleType: sampleMetricTypeFloat, - appendFunc: func(appender storage.Appender, lbls labels.Labels, ts, value int64) (storage.SeriesRef, sample, error) { + appendFunc: func(appender storage.LimitedAppenderV1, lbls labels.Labels, ts, value int64) (storage.SeriesRef, sample, error) { s := sample{t: ts, f: float64(value)} ref, err := appender.Append(0, lbls, ts, s.f) return ref, s, err @@ -62,7 +62,7 @@ var sampleTypeScenarios = map[string]sampleTypeScenario{ }, intHistogram: { sampleType: sampleMetricTypeHistogram, - appendFunc: func(appender storage.Appender, lbls labels.Labels, ts, value int64) (storage.SeriesRef, sample, error) { + appendFunc: func(appender storage.LimitedAppenderV1, lbls labels.Labels, ts, value int64) (storage.SeriesRef, sample, error) { s := sample{t: ts, h: tsdbutil.GenerateTestHistogram(value)} ref, err := appender.AppendHistogram(0, lbls, ts, s.h, nil) return ref, s, err @@ -73,7 +73,7 @@ var sampleTypeScenarios = map[string]sampleTypeScenario{ }, floatHistogram: { sampleType: sampleMetricTypeHistogram, - appendFunc: func(appender storage.Appender, lbls labels.Labels, ts, value int64) (storage.SeriesRef, sample, error) { + appendFunc: func(appender storage.LimitedAppenderV1, lbls labels.Labels, ts, value int64) (storage.SeriesRef, sample, error) { s := sample{t: ts, fh: tsdbutil.GenerateTestFloatHistogram(value)} ref, err := appender.AppendHistogram(0, lbls, ts, nil, s.fh) return ref, s, err @@ -84,7 +84,7 @@ var sampleTypeScenarios = map[string]sampleTypeScenario{ }, customBucketsIntHistogram: { sampleType: sampleMetricTypeHistogram, - appendFunc: func(appender storage.Appender, lbls labels.Labels, ts, value int64) (storage.SeriesRef, sample, error) { + appendFunc: func(appender storage.LimitedAppenderV1, lbls labels.Labels, ts, value int64) (storage.SeriesRef, sample, error) { s := sample{t: ts, h: tsdbutil.GenerateTestCustomBucketsHistogram(value)} ref, err := appender.AppendHistogram(0, lbls, ts, s.h, nil) return ref, s, err @@ -95,7 +95,7 @@ var sampleTypeScenarios = map[string]sampleTypeScenario{ }, customBucketsFloatHistogram: { sampleType: sampleMetricTypeHistogram, - appendFunc: func(appender storage.Appender, lbls labels.Labels, ts, value int64) (storage.SeriesRef, sample, error) { + appendFunc: func(appender storage.LimitedAppenderV1, lbls labels.Labels, ts, value int64) (storage.SeriesRef, sample, error) { s := sample{t: ts, fh: tsdbutil.GenerateTestCustomBucketsFloatHistogram(value)} ref, err := appender.AppendHistogram(0, lbls, ts, nil, s.fh) return ref, s, err @@ -106,7 +106,7 @@ var sampleTypeScenarios = map[string]sampleTypeScenario{ }, gaugeIntHistogram: { sampleType: sampleMetricTypeHistogram, - appendFunc: func(appender storage.Appender, lbls labels.Labels, ts, value int64) (storage.SeriesRef, sample, error) { + appendFunc: func(appender storage.LimitedAppenderV1, lbls labels.Labels, ts, value int64) (storage.SeriesRef, sample, error) { s := sample{t: ts, h: tsdbutil.GenerateTestGaugeHistogram(value)} ref, err := appender.AppendHistogram(0, lbls, ts, s.h, nil) return ref, s, err @@ -117,7 +117,7 @@ var sampleTypeScenarios = map[string]sampleTypeScenario{ }, gaugeFloatHistogram: { sampleType: sampleMetricTypeHistogram, - appendFunc: func(appender storage.Appender, lbls labels.Labels, ts, value int64) (storage.SeriesRef, sample, error) { + appendFunc: func(appender storage.LimitedAppenderV1, lbls labels.Labels, ts, value int64) (storage.SeriesRef, sample, error) { s := sample{t: ts, fh: tsdbutil.GenerateTestGaugeFloatHistogram(value)} ref, err := appender.AppendHistogram(0, lbls, ts, nil, s.fh) return ref, s, err diff --git a/vendor/github.com/prometheus/prometheus/util/features/features.go b/vendor/github.com/prometheus/prometheus/util/features/features.go new file mode 100644 index 00000000000..d52384dbd88 --- /dev/null +++ b/vendor/github.com/prometheus/prometheus/util/features/features.go @@ -0,0 +1,127 @@ +// Copyright The Prometheus Authors +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package features + +import ( + "maps" + "sync" +) + +// Category constants define the standard feature flag categories used in Prometheus. +const ( + API = "api" + OTLPReceiver = "otlp_receiver" + Prometheus = "prometheus" + PromQL = "promql" + PromQLFunctions = "promql_functions" + PromQLOperators = "promql_operators" + Rules = "rules" + Scrape = "scrape" + ServiceDiscoveryProviders = "service_discovery_providers" + TemplatingFunctions = "templating_functions" + TSDB = "tsdb" + UI = "ui" +) + +// Collector defines the interface for collecting and managing feature flags. +// It provides methods to enable, disable, and retrieve feature states. +type Collector interface { + // Enable marks a feature as enabled in the registry. + // The category and name should use snake_case naming convention. + Enable(category, name string) + + // Disable marks a feature as disabled in the registry. + // The category and name should use snake_case naming convention. + Disable(category, name string) + + // Set sets a feature to the specified enabled state. + // The category and name should use snake_case naming convention. + Set(category, name string, enabled bool) + + // Get returns a copy of all registered features organized by category. + // Returns a map where the keys are category names and values are maps + // of feature names to their enabled status. + Get() map[string]map[string]bool +} + +// registry is the private implementation of the Collector interface. +// It stores feature information organized by category. +type registry struct { + mu sync.RWMutex + features map[string]map[string]bool +} + +// DefaultRegistry is the package-level registry used by Prometheus. +var DefaultRegistry = NewRegistry() + +// NewRegistry creates a new feature registry. +func NewRegistry() Collector { + return ®istry{ + features: make(map[string]map[string]bool), + } +} + +// Enable marks a feature as enabled in the registry. +func (r *registry) Enable(category, name string) { + r.Set(category, name, true) +} + +// Disable marks a feature as disabled in the registry. +func (r *registry) Disable(category, name string) { + r.Set(category, name, false) +} + +// Set sets a feature to the specified enabled state. +func (r *registry) Set(category, name string, enabled bool) { + r.mu.Lock() + defer r.mu.Unlock() + + if r.features[category] == nil { + r.features[category] = make(map[string]bool) + } + r.features[category][name] = enabled +} + +// Get returns a copy of all registered features organized by category. +func (r *registry) Get() map[string]map[string]bool { + r.mu.RLock() + defer r.mu.RUnlock() + + result := make(map[string]map[string]bool, len(r.features)) + for category, features := range r.features { + result[category] = make(map[string]bool, len(features)) + maps.Copy(result[category], features) + } + return result +} + +// Enable marks a feature as enabled in the default registry. +func Enable(category, name string) { + DefaultRegistry.Enable(category, name) +} + +// Disable marks a feature as disabled in the default registry. +func Disable(category, name string) { + DefaultRegistry.Disable(category, name) +} + +// Set sets a feature to the specified enabled state in the default registry. +func Set(category, name string, enabled bool) { + DefaultRegistry.Set(category, name, enabled) +} + +// Get returns all features from the default registry. +func Get() map[string]map[string]bool { + return DefaultRegistry.Get() +} diff --git a/vendor/github.com/prometheus/prometheus/web/api/v1/api.go b/vendor/github.com/prometheus/prometheus/web/api/v1/api.go index baddedd4951..2a6036ba0b8 100644 --- a/vendor/github.com/prometheus/prometheus/web/api/v1/api.go +++ b/vendor/github.com/prometheus/prometheus/web/api/v1/api.go @@ -56,6 +56,7 @@ import ( "github.com/prometheus/prometheus/tsdb" "github.com/prometheus/prometheus/tsdb/index" "github.com/prometheus/prometheus/util/annotations" + "github.com/prometheus/prometheus/util/features" "github.com/prometheus/prometheus/util/httputil" "github.com/prometheus/prometheus/util/notifications" "github.com/prometheus/prometheus/util/stats" @@ -255,6 +256,8 @@ type API struct { otlpWriteHandler http.Handler codecs []Codec + + featureRegistry features.Collector } // NewAPI returns an initialized API type. @@ -290,11 +293,12 @@ func NewAPI( rwEnabled bool, acceptRemoteWriteProtoMsgs remoteapi.MessageTypes, otlpEnabled, otlpDeltaToCumulative, otlpNativeDeltaIngestion bool, - ctZeroIngestionEnabled bool, + stZeroIngestionEnabled bool, lookbackDelta time.Duration, enableTypeAndUnitLabels bool, appendMetadata bool, overrideErrorCode OverrideErrorCode, + featureRegistry features.Collector, ) *API { a := &API{ QueryEngine: qe, @@ -324,6 +328,7 @@ func NewAPI( notificationsGetter: notificationsGetter, notificationsSub: notificationsSub, overrideErrorCode: overrideErrorCode, + featureRegistry: featureRegistry, remoteReadHandler: remote.NewReadHandler(logger, registerer, q, configFunc, remoteReadSampleLimit, remoteReadConcurrencyLimit, remoteReadMaxBytesInFrame), } @@ -339,15 +344,16 @@ func NewAPI( } if rwEnabled { - a.remoteWriteHandler = remote.NewWriteHandler(logger, registerer, ap, acceptRemoteWriteProtoMsgs, ctZeroIngestionEnabled, enableTypeAndUnitLabels, appendMetadata) + a.remoteWriteHandler = remote.NewWriteHandler(logger, registerer, ap, acceptRemoteWriteProtoMsgs, stZeroIngestionEnabled, enableTypeAndUnitLabels, appendMetadata) } if otlpEnabled { a.otlpWriteHandler = remote.NewOTLPWriteHandler(logger, registerer, ap, configFunc, remote.OTLPOptions{ ConvertDelta: otlpDeltaToCumulative, NativeDelta: otlpNativeDeltaIngestion, LookbackDelta: lookbackDelta, - IngestCTZeroSample: ctZeroIngestionEnabled, + IngestSTZeroSample: stZeroIngestionEnabled, EnableTypeAndUnitLabels: enableTypeAndUnitLabels, + AppendMetadata: appendMetadata, }) } @@ -444,6 +450,7 @@ func (api *API) Register(r *route.Router) { r.Get("/status/flags", wrap(api.serveFlags)) r.Get("/status/tsdb", wrapAgent(api.serveTSDBStatus)) r.Get("/status/tsdb/blocks", wrapAgent(api.serveTSDBBlocks)) + r.Get("/features", wrap(api.features)) r.Get("/status/walreplay", api.serveWALReplayStatus) r.Get("/notifications", api.notifications) r.Get("/notifications/live", api.notificationsSSE) @@ -1788,6 +1795,29 @@ func (api *API) serveFlags(*http.Request) apiFuncResult { return apiFuncResult{api.flagsMap, nil, nil, nil} } +// featuresData wraps feature flags data to provide custom JSON marshaling without HTML escaping. +// featuresData does not contain user-provided input, and it is more convenient to have unescaped +// representation of PromQL operators like >=. +type featuresData struct { + data map[string]map[string]bool +} + +func (f featuresData) MarshalJSON() ([]byte, error) { + json := jsoniter.Config{ + EscapeHTML: false, + SortMapKeys: true, + ValidateJsonRawMessage: true, + }.Froze() + return json.Marshal(f.data) +} + +func (api *API) features(*http.Request) apiFuncResult { + if api.featureRegistry == nil { + return apiFuncResult{nil, &apiError{errorInternal, errors.New("feature registry not configured")}, nil, nil} + } + return apiFuncResult{featuresData{data: api.featureRegistry.Get()}, nil, nil, nil} +} + // TSDBStat holds the information about individual cardinality. type TSDBStat struct { Name string `json:"name"` @@ -1836,12 +1866,16 @@ func (api *API) serveTSDBBlocks(*http.Request) apiFuncResult { } func (api *API) serveTSDBStatus(r *http.Request) apiFuncResult { + const maxTSDBLimit = 10000 limit := 10 if s := r.FormValue("limit"); s != "" { var err error if limit, err = strconv.Atoi(s); err != nil || limit < 1 { return apiFuncResult{nil, &apiError{errorBadData, errors.New("limit must be a positive number")}, nil, nil} } + if limit > maxTSDBLimit { + return apiFuncResult{nil, &apiError{errorBadData, fmt.Errorf("limit must not exceed %d", maxTSDBLimit)}, nil, nil} + } } s, err := api.db.Stats(labels.MetricName, limit) if err != nil { diff --git a/vendor/go.opentelemetry.io/collector/component/component.go b/vendor/go.opentelemetry.io/collector/component/component.go index 5a32c5041d7..1da518ac335 100644 --- a/vendor/go.opentelemetry.io/collector/component/component.go +++ b/vendor/go.opentelemetry.io/collector/component/component.go @@ -46,7 +46,7 @@ type Component interface { // If there are any background operations running by the component they must be aborted before // this function returns. Remember that if you started any long-running background operations from // the Start() method, those operations must be also cancelled. If there are any buffers in the - // component, they should be cleared and the data sent immediately to the next component. + // component, they should be flushed with the data being sent immediately to the next component. // // The component's lifecycle is completed once the Shutdown() method returns. No other // methods of the component are called after that. If necessary a new component with diff --git a/vendor/go.opentelemetry.io/collector/component/host.go b/vendor/go.opentelemetry.io/collector/component/host.go index dc8210ffbe3..f425b76bb67 100644 --- a/vendor/go.opentelemetry.io/collector/component/host.go +++ b/vendor/go.opentelemetry.io/collector/component/host.go @@ -17,5 +17,7 @@ type Host interface { // // GetExtensions can be called by the component anytime after Component.Start() begins and // until Component.Shutdown() ends. + // + // The returned map should only be nil if the host does not support extensions at all. GetExtensions() map[ID]Component } diff --git a/vendor/go.opentelemetry.io/collector/confmap/internal/decoder.go b/vendor/go.opentelemetry.io/collector/confmap/internal/decoder.go index 17f172a2ec3..7009c9f410b 100644 --- a/vendor/go.opentelemetry.io/collector/confmap/internal/decoder.go +++ b/vendor/go.opentelemetry.io/collector/confmap/internal/decoder.go @@ -7,7 +7,6 @@ import ( "encoding" "errors" "fmt" - "maps" "reflect" "slices" "strings" @@ -57,7 +56,7 @@ func Decode(input, result any, settings UnmarshalOptions, skipTopLevelUnmarshale unmarshalerHookFunc(result, skipTopLevelUnmarshaler), // after the main unmarshaler hook is called, // we unmarshal the embedded structs if present to merge with the result: - unmarshalerEmbeddedStructsHookFunc(), + unmarshalerEmbeddedStructsHookFunc(settings), zeroSliceAndMapHookFunc(), ), } @@ -168,7 +167,7 @@ func mapKeyStringToMapKeyTextUnmarshalerHookFunc() mapstructure.DecodeHookFuncTy } // Create a map with key value of to's key to bool. - fieldNameSet := reflect.MakeMap(reflect.MapOf(to.Key(), reflect.TypeOf(true))) + fieldNameSet := reflect.MakeMap(reflect.MapOf(to.Key(), reflect.TypeFor[bool]())) for k := range data.(map[string]any) { // Create a new value of the to's key type. tKey := reflect.New(to.Key()) @@ -189,7 +188,7 @@ func mapKeyStringToMapKeyTextUnmarshalerHookFunc() mapstructure.DecodeHookFuncTy // unmarshalerEmbeddedStructsHookFunc provides a mechanism for embedded structs to define their own unmarshal logic, // by implementing the Unmarshaler interface. -func unmarshalerEmbeddedStructsHookFunc() mapstructure.DecodeHookFuncValue { +func unmarshalerEmbeddedStructsHookFunc(settings UnmarshalOptions) mapstructure.DecodeHookFuncValue { return safeWrapDecodeHookFunc(func(from, to reflect.Value) (any, error) { if to.Type().Kind() != reflect.Struct { return from.Interface(), nil @@ -198,32 +197,72 @@ func unmarshalerEmbeddedStructsHookFunc() mapstructure.DecodeHookFuncValue { if !ok { return from.Interface(), nil } + + // First call Unmarshaler on squashed embedded fields, if necessary. + var squashedUnmarshalers []int for i := 0; i < to.Type().NumField(); i++ { - // embedded structs passed in via `squash` cannot be pointers. We just check if they are structs: f := to.Type().Field(i) - if f.IsExported() && slices.Contains(strings.Split(f.Tag.Get(MapstructureTag), ","), "squash") { - if unmarshaler, ok := to.Field(i).Addr().Interface().(Unmarshaler); ok { - c := NewFromStringMap(fromAsMap) - c.skipTopLevelUnmarshaler = true - if err := unmarshaler.Unmarshal(c); err != nil { - return nil, err - } - // the struct we receive from this unmarshaling only contains fields related to the embedded struct. - // we merge this partially unmarshaled struct with the rest of the result. - // note we already unmarshaled the main struct earlier, and therefore merge with it. - conf := New() - if err := conf.Marshal(unmarshaler); err != nil { - return nil, err - } - resultMap := conf.ToStringMap() - if fromAsMap == nil && len(resultMap) > 0 { - fromAsMap = make(map[string]any, len(resultMap)) - } - maps.Copy(fromAsMap, resultMap) - } + if !f.IsExported() { + continue + } + tagParts := strings.Split(f.Tag.Get(MapstructureTag), ",") + if !slices.Contains(tagParts[1:], "squash") { + continue + } + unmarshaler, ok := to.Field(i).Addr().Interface().(Unmarshaler) + if !ok { + continue } + c := NewFromStringMap(fromAsMap) + c.skipTopLevelUnmarshaler = true + if err := unmarshaler.Unmarshal(c); err != nil { + return nil, err + } + squashedUnmarshalers = append(squashedUnmarshalers, i) + } + + // No squashed unmarshalers, we can let mapstructure do its job. + if len(squashedUnmarshalers) == 0 { + return fromAsMap, nil } - return fromAsMap, nil + + // We need to unmarshal into all other fields without overwriting the output of the Unmarshal calls. + // To do that, create a custom "partial" struct containing only the non-squashed fields. + var fields []reflect.StructField + var fieldValues []reflect.Value + for i := 0; i < to.Type().NumField(); i++ { + f := to.Type().Field(i) + if !f.IsExported() { + continue + } + if slices.Contains(squashedUnmarshalers, i) { + continue + } + fields = append(fields, f) + fieldValues = append(fieldValues, to.Field(i)) + } + restType := reflect.StructOf(fields) + restValue := reflect.New(restType) + + // Copy initial values into partial struct. + for i, fieldValue := range fieldValues { + restValue.Elem().Field(i).Set(fieldValue) + } + + // Decode into the partial struct. + // This performs a recursive call into this hook, which will be handled by the "no squashed unmarshalers" case above. + // We need to set `IgnoreUnused` to avoid errors from the map containing fields only present in the full struct. + settings.IgnoreUnused = true + if err := Decode(fromAsMap, restValue.Interface(), settings, true); err != nil { + return nil, err + } + + // Copy decoding results back to the original struct. + for i, fieldValue := range fieldValues { + fieldValue.Set(restValue.Elem().Field(i)) + } + + return to, nil }) } diff --git a/vendor/go.opentelemetry.io/collector/confmap/xconfmap/config.go b/vendor/go.opentelemetry.io/collector/confmap/xconfmap/config.go index ba90b54bc28..113d0a3fce1 100644 --- a/vendor/go.opentelemetry.io/collector/confmap/xconfmap/config.go +++ b/vendor/go.opentelemetry.io/collector/confmap/xconfmap/config.go @@ -15,7 +15,7 @@ import ( // As interface types are only used for static typing, a common idiom to find the reflection Type // for an interface type Foo is to use a *Foo value. -var configValidatorType = reflect.TypeOf((*Validator)(nil)).Elem() +var configValidatorType = reflect.TypeFor[Validator]() // Validator defines an optional interface for configurations to implement to do validation. type Validator interface { diff --git a/vendor/go.opentelemetry.io/collector/pdata/internal/generated_proto_location.go b/vendor/go.opentelemetry.io/collector/pdata/internal/generated_proto_location.go index 7b16e381ff5..bceb7556171 100644 --- a/vendor/go.opentelemetry.io/collector/pdata/internal/generated_proto_location.go +++ b/vendor/go.opentelemetry.io/collector/pdata/internal/generated_proto_location.go @@ -18,7 +18,7 @@ import ( type Location struct { MappingIndex int32 Address uint64 - Line []*Line + Lines []*Line AttributeIndices []int32 } @@ -47,8 +47,8 @@ func DeleteLocation(orig *Location, nullable bool) { return } - for i := range orig.Line { - DeleteLine(orig.Line[i], true) + for i := range orig.Lines { + DeleteLine(orig.Lines[i], true) } orig.Reset() @@ -74,7 +74,7 @@ func CopyLocation(dest, src *Location) *Location { dest.Address = src.Address - dest.Line = CopyLinePtrSlice(dest.Line, src.Line) + dest.Lines = CopyLinePtrSlice(dest.Lines, src.Lines) dest.AttributeIndices = append(dest.AttributeIndices[:0], src.AttributeIndices...) @@ -144,13 +144,13 @@ func (orig *Location) MarshalJSON(dest *json.Stream) { dest.WriteObjectField("address") dest.WriteUint64(orig.Address) } - if len(orig.Line) > 0 { - dest.WriteObjectField("line") + if len(orig.Lines) > 0 { + dest.WriteObjectField("lines") dest.WriteArrayStart() - orig.Line[0].MarshalJSON(dest) - for i := 1; i < len(orig.Line); i++ { + orig.Lines[0].MarshalJSON(dest) + for i := 1; i < len(orig.Lines); i++ { dest.WriteMore() - orig.Line[i].MarshalJSON(dest) + orig.Lines[i].MarshalJSON(dest) } dest.WriteArrayEnd() } @@ -175,10 +175,10 @@ func (orig *Location) UnmarshalJSON(iter *json.Iterator) { orig.MappingIndex = iter.ReadInt32() case "address": orig.Address = iter.ReadUint64() - case "line": + case "lines": for iter.ReadArray() { - orig.Line = append(orig.Line, NewLine()) - orig.Line[len(orig.Line)-1].UnmarshalJSON(iter) + orig.Lines = append(orig.Lines, NewLine()) + orig.Lines[len(orig.Lines)-1].UnmarshalJSON(iter) } case "attributeIndices", "attribute_indices": @@ -202,8 +202,8 @@ func (orig *Location) SizeProto() int { if orig.Address != 0 { n += 1 + proto.Sov(uint64(orig.Address)) } - for i := range orig.Line { - l = orig.Line[i].SizeProto() + for i := range orig.Lines { + l = orig.Lines[i].SizeProto() n += 1 + proto.Sov(uint64(l)) + l } if len(orig.AttributeIndices) > 0 { @@ -230,8 +230,8 @@ func (orig *Location) MarshalProto(buf []byte) int { pos-- buf[pos] = 0x10 } - for i := len(orig.Line) - 1; i >= 0; i-- { - l = orig.Line[i].MarshalProto(buf[:pos]) + for i := len(orig.Lines) - 1; i >= 0; i-- { + l = orig.Lines[i].MarshalProto(buf[:pos]) pos -= l pos = proto.EncodeVarint(buf, pos, uint64(l)) pos-- @@ -291,7 +291,7 @@ func (orig *Location) UnmarshalProto(buf []byte) error { case 3: if wireType != proto.WireTypeLen { - return fmt.Errorf("proto: wrong wireType = %d for field Line", wireType) + return fmt.Errorf("proto: wrong wireType = %d for field Lines", wireType) } var length int length, pos, err = proto.ConsumeLen(buf, pos) @@ -299,8 +299,8 @@ func (orig *Location) UnmarshalProto(buf []byte) error { return err } startPos := pos - length - orig.Line = append(orig.Line, NewLine()) - err = orig.Line[len(orig.Line)-1].UnmarshalProto(buf[startPos:pos]) + orig.Lines = append(orig.Lines, NewLine()) + err = orig.Lines[len(orig.Lines)-1].UnmarshalProto(buf[startPos:pos]) if err != nil { return err } @@ -348,7 +348,7 @@ func GenTestLocation() *Location { orig := NewLocation() orig.MappingIndex = int32(13) orig.Address = uint64(13) - orig.Line = []*Line{{}, GenTestLine()} + orig.Lines = []*Line{{}, GenTestLine()} orig.AttributeIndices = []int32{int32(0), int32(13)} return orig } diff --git a/vendor/go.opentelemetry.io/collector/pdata/internal/generated_proto_profile.go b/vendor/go.opentelemetry.io/collector/pdata/internal/generated_proto_profile.go index eb1e0b9f922..c3a6460d20a 100644 --- a/vendor/go.opentelemetry.io/collector/pdata/internal/generated_proto_profile.go +++ b/vendor/go.opentelemetry.io/collector/pdata/internal/generated_proto_profile.go @@ -19,12 +19,11 @@ import ( type Profile struct { SampleType ValueType - Sample []*Sample + Samples []*Sample TimeUnixNano uint64 DurationNano uint64 PeriodType ValueType Period int64 - CommentStrindices []int32 ProfileId ProfileID DroppedAttributesCount uint32 OriginalPayloadFormat string @@ -58,8 +57,8 @@ func DeleteProfile(orig *Profile, nullable bool) { } DeleteValueType(&orig.SampleType, false) - for i := range orig.Sample { - DeleteSample(orig.Sample[i], true) + for i := range orig.Samples { + DeleteSample(orig.Samples[i], true) } DeleteValueType(&orig.PeriodType, false) DeleteProfileID(&orig.ProfileId, false) @@ -85,7 +84,7 @@ func CopyProfile(dest, src *Profile) *Profile { } CopyValueType(&dest.SampleType, &src.SampleType) - dest.Sample = CopySamplePtrSlice(dest.Sample, src.Sample) + dest.Samples = CopySamplePtrSlice(dest.Samples, src.Samples) dest.TimeUnixNano = src.TimeUnixNano @@ -95,7 +94,6 @@ func CopyProfile(dest, src *Profile) *Profile { dest.Period = src.Period - dest.CommentStrindices = append(dest.CommentStrindices[:0], src.CommentStrindices...) CopyProfileID(&dest.ProfileId, &src.ProfileId) dest.DroppedAttributesCount = src.DroppedAttributesCount @@ -166,13 +164,13 @@ func (orig *Profile) MarshalJSON(dest *json.Stream) { dest.WriteObjectStart() dest.WriteObjectField("sampleType") orig.SampleType.MarshalJSON(dest) - if len(orig.Sample) > 0 { - dest.WriteObjectField("sample") + if len(orig.Samples) > 0 { + dest.WriteObjectField("samples") dest.WriteArrayStart() - orig.Sample[0].MarshalJSON(dest) - for i := 1; i < len(orig.Sample); i++ { + orig.Samples[0].MarshalJSON(dest) + for i := 1; i < len(orig.Samples); i++ { dest.WriteMore() - orig.Sample[i].MarshalJSON(dest) + orig.Samples[i].MarshalJSON(dest) } dest.WriteArrayEnd() } @@ -190,16 +188,6 @@ func (orig *Profile) MarshalJSON(dest *json.Stream) { dest.WriteObjectField("period") dest.WriteInt64(orig.Period) } - if len(orig.CommentStrindices) > 0 { - dest.WriteObjectField("commentStrindices") - dest.WriteArrayStart() - dest.WriteInt32(orig.CommentStrindices[0]) - for i := 1; i < len(orig.CommentStrindices); i++ { - dest.WriteMore() - dest.WriteInt32(orig.CommentStrindices[i]) - } - dest.WriteArrayEnd() - } if !orig.ProfileId.IsEmpty() { dest.WriteObjectField("profileId") orig.ProfileId.MarshalJSON(dest) @@ -237,10 +225,10 @@ func (orig *Profile) UnmarshalJSON(iter *json.Iterator) { case "sampleType", "sample_type": orig.SampleType.UnmarshalJSON(iter) - case "sample": + case "samples": for iter.ReadArray() { - orig.Sample = append(orig.Sample, NewSample()) - orig.Sample[len(orig.Sample)-1].UnmarshalJSON(iter) + orig.Samples = append(orig.Samples, NewSample()) + orig.Samples[len(orig.Samples)-1].UnmarshalJSON(iter) } case "timeUnixNano", "time_unix_nano": @@ -252,11 +240,6 @@ func (orig *Profile) UnmarshalJSON(iter *json.Iterator) { orig.PeriodType.UnmarshalJSON(iter) case "period": orig.Period = iter.ReadInt64() - case "commentStrindices", "comment_strindices": - for iter.ReadArray() { - orig.CommentStrindices = append(orig.CommentStrindices, iter.ReadInt32()) - } - case "profileId", "profile_id": orig.ProfileId.UnmarshalJSON(iter) @@ -283,8 +266,8 @@ func (orig *Profile) SizeProto() int { _ = l l = orig.SampleType.SizeProto() n += 1 + proto.Sov(uint64(l)) + l - for i := range orig.Sample { - l = orig.Sample[i].SizeProto() + for i := range orig.Samples { + l = orig.Samples[i].SizeProto() n += 1 + proto.Sov(uint64(l)) + l } if orig.TimeUnixNano != 0 { @@ -298,13 +281,6 @@ func (orig *Profile) SizeProto() int { if orig.Period != 0 { n += 1 + proto.Sov(uint64(orig.Period)) } - if len(orig.CommentStrindices) > 0 { - l = 0 - for _, e := range orig.CommentStrindices { - l += proto.Sov(uint64(e)) - } - n += 1 + proto.Sov(uint64(l)) + l - } l = orig.ProfileId.SizeProto() n += 1 + proto.Sov(uint64(l)) + l if orig.DroppedAttributesCount != 0 { @@ -338,8 +314,8 @@ func (orig *Profile) MarshalProto(buf []byte) int { pos-- buf[pos] = 0xa - for i := len(orig.Sample) - 1; i >= 0; i-- { - l = orig.Sample[i].MarshalProto(buf[:pos]) + for i := len(orig.Samples) - 1; i >= 0; i-- { + l = orig.Samples[i].MarshalProto(buf[:pos]) pos -= l pos = proto.EncodeVarint(buf, pos, uint64(l)) pos-- @@ -367,26 +343,16 @@ func (orig *Profile) MarshalProto(buf []byte) int { pos-- buf[pos] = 0x30 } - l = len(orig.CommentStrindices) - if l > 0 { - endPos := pos - for i := l - 1; i >= 0; i-- { - pos = proto.EncodeVarint(buf, pos, uint64(orig.CommentStrindices[i])) - } - pos = proto.EncodeVarint(buf, pos, uint64(endPos-pos)) - pos-- - buf[pos] = 0x3a - } l = orig.ProfileId.MarshalProto(buf[:pos]) pos -= l pos = proto.EncodeVarint(buf, pos, uint64(l)) pos-- - buf[pos] = 0x42 + buf[pos] = 0x3a if orig.DroppedAttributesCount != 0 { pos = proto.EncodeVarint(buf, pos, uint64(orig.DroppedAttributesCount)) pos-- - buf[pos] = 0x48 + buf[pos] = 0x40 } l = len(orig.OriginalPayloadFormat) if l > 0 { @@ -394,7 +360,7 @@ func (orig *Profile) MarshalProto(buf []byte) int { copy(buf[pos:], orig.OriginalPayloadFormat) pos = proto.EncodeVarint(buf, pos, uint64(l)) pos-- - buf[pos] = 0x52 + buf[pos] = 0x4a } l = len(orig.OriginalPayload) if l > 0 { @@ -402,7 +368,7 @@ func (orig *Profile) MarshalProto(buf []byte) int { copy(buf[pos:], orig.OriginalPayload) pos = proto.EncodeVarint(buf, pos, uint64(l)) pos-- - buf[pos] = 0x5a + buf[pos] = 0x52 } l = len(orig.AttributeIndices) if l > 0 { @@ -412,7 +378,7 @@ func (orig *Profile) MarshalProto(buf []byte) int { } pos = proto.EncodeVarint(buf, pos, uint64(endPos-pos)) pos-- - buf[pos] = 0x62 + buf[pos] = 0x5a } return len(buf) - pos } @@ -450,7 +416,7 @@ func (orig *Profile) UnmarshalProto(buf []byte) error { case 2: if wireType != proto.WireTypeLen { - return fmt.Errorf("proto: wrong wireType = %d for field Sample", wireType) + return fmt.Errorf("proto: wrong wireType = %d for field Samples", wireType) } var length int length, pos, err = proto.ConsumeLen(buf, pos) @@ -458,8 +424,8 @@ func (orig *Profile) UnmarshalProto(buf []byte) error { return err } startPos := pos - length - orig.Sample = append(orig.Sample, NewSample()) - err = orig.Sample[len(orig.Sample)-1].UnmarshalProto(buf[startPos:pos]) + orig.Samples = append(orig.Samples, NewSample()) + err = orig.Samples[len(orig.Samples)-1].UnmarshalProto(buf[startPos:pos]) if err != nil { return err } @@ -515,38 +481,8 @@ func (orig *Profile) UnmarshalProto(buf []byte) error { } orig.Period = int64(num) - case 7: - switch wireType { - case proto.WireTypeLen: - var length int - length, pos, err = proto.ConsumeLen(buf, pos) - if err != nil { - return err - } - startPos := pos - length - var num uint64 - for startPos < pos { - num, startPos, err = proto.ConsumeVarint(buf[:pos], startPos) - if err != nil { - return err - } - orig.CommentStrindices = append(orig.CommentStrindices, int32(num)) - } - if startPos != pos { - return fmt.Errorf("proto: invalid field len = %d for field CommentStrindices", pos-startPos) - } - case proto.WireTypeVarint: - var num uint64 - num, pos, err = proto.ConsumeVarint(buf, pos) - if err != nil { - return err - } - orig.CommentStrindices = append(orig.CommentStrindices, int32(num)) - default: - return fmt.Errorf("proto: wrong wireType = %d for field CommentStrindices", wireType) - } - case 8: + case 7: if wireType != proto.WireTypeLen { return fmt.Errorf("proto: wrong wireType = %d for field ProfileId", wireType) } @@ -562,7 +498,7 @@ func (orig *Profile) UnmarshalProto(buf []byte) error { return err } - case 9: + case 8: if wireType != proto.WireTypeVarint { return fmt.Errorf("proto: wrong wireType = %d for field DroppedAttributesCount", wireType) } @@ -574,7 +510,7 @@ func (orig *Profile) UnmarshalProto(buf []byte) error { orig.DroppedAttributesCount = uint32(num) - case 10: + case 9: if wireType != proto.WireTypeLen { return fmt.Errorf("proto: wrong wireType = %d for field OriginalPayloadFormat", wireType) } @@ -586,7 +522,7 @@ func (orig *Profile) UnmarshalProto(buf []byte) error { startPos := pos - length orig.OriginalPayloadFormat = string(buf[startPos:pos]) - case 11: + case 10: if wireType != proto.WireTypeLen { return fmt.Errorf("proto: wrong wireType = %d for field OriginalPayload", wireType) } @@ -600,7 +536,7 @@ func (orig *Profile) UnmarshalProto(buf []byte) error { orig.OriginalPayload = make([]byte, length) copy(orig.OriginalPayload, buf[startPos:pos]) } - case 12: + case 11: switch wireType { case proto.WireTypeLen: var length int @@ -643,12 +579,11 @@ func (orig *Profile) UnmarshalProto(buf []byte) error { func GenTestProfile() *Profile { orig := NewProfile() orig.SampleType = *GenTestValueType() - orig.Sample = []*Sample{{}, GenTestSample()} + orig.Samples = []*Sample{{}, GenTestSample()} orig.TimeUnixNano = uint64(13) orig.DurationNano = uint64(13) orig.PeriodType = *GenTestValueType() orig.Period = int64(13) - orig.CommentStrindices = []int32{int32(0), int32(13)} orig.ProfileId = *GenTestProfileID() orig.DroppedAttributesCount = uint32(13) orig.OriginalPayloadFormat = "test_originalpayloadformat" diff --git a/vendor/go.opentelemetry.io/collector/pdata/internal/generated_proto_valuetype.go b/vendor/go.opentelemetry.io/collector/pdata/internal/generated_proto_valuetype.go index a070ab3e70f..7742846f24a 100644 --- a/vendor/go.opentelemetry.io/collector/pdata/internal/generated_proto_valuetype.go +++ b/vendor/go.opentelemetry.io/collector/pdata/internal/generated_proto_valuetype.go @@ -14,11 +14,10 @@ import ( "go.opentelemetry.io/collector/pdata/internal/proto" ) -// ValueType describes the type and units of a value, with an optional aggregation temporality. +// ValueType describes the type and units of a value. type ValueType struct { - TypeStrindex int32 - UnitStrindex int32 - AggregationTemporality AggregationTemporality + TypeStrindex int32 + UnitStrindex int32 } var ( @@ -69,8 +68,6 @@ func CopyValueType(dest, src *ValueType) *ValueType { dest.UnitStrindex = src.UnitStrindex - dest.AggregationTemporality = src.AggregationTemporality - return dest } @@ -137,11 +134,6 @@ func (orig *ValueType) MarshalJSON(dest *json.Stream) { dest.WriteObjectField("unitStrindex") dest.WriteInt32(orig.UnitStrindex) } - - if int32(orig.AggregationTemporality) != 0 { - dest.WriteObjectField("aggregationTemporality") - dest.WriteInt32(int32(orig.AggregationTemporality)) - } dest.WriteObjectEnd() } @@ -153,8 +145,6 @@ func (orig *ValueType) UnmarshalJSON(iter *json.Iterator) { orig.TypeStrindex = iter.ReadInt32() case "unitStrindex", "unit_strindex": orig.UnitStrindex = iter.ReadInt32() - case "aggregationTemporality", "aggregation_temporality": - orig.AggregationTemporality = AggregationTemporality(iter.ReadEnumValue(AggregationTemporality_value)) default: iter.Skip() } @@ -171,9 +161,6 @@ func (orig *ValueType) SizeProto() int { if orig.UnitStrindex != 0 { n += 1 + proto.Sov(uint64(orig.UnitStrindex)) } - if orig.AggregationTemporality != 0 { - n += 1 + proto.Sov(uint64(orig.AggregationTemporality)) - } return n } @@ -191,11 +178,6 @@ func (orig *ValueType) MarshalProto(buf []byte) int { pos-- buf[pos] = 0x10 } - if orig.AggregationTemporality != 0 { - pos = proto.EncodeVarint(buf, pos, uint64(orig.AggregationTemporality)) - pos-- - buf[pos] = 0x18 - } return len(buf) - pos } @@ -237,18 +219,6 @@ func (orig *ValueType) UnmarshalProto(buf []byte) error { } orig.UnitStrindex = int32(num) - - case 3: - if wireType != proto.WireTypeVarint { - return fmt.Errorf("proto: wrong wireType = %d for field AggregationTemporality", wireType) - } - var num uint64 - num, pos, err = proto.ConsumeVarint(buf, pos) - if err != nil { - return err - } - - orig.AggregationTemporality = AggregationTemporality(num) default: pos, err = proto.ConsumeUnknown(buf, pos, wireType) if err != nil { @@ -263,7 +233,6 @@ func GenTestValueType() *ValueType { orig := NewValueType() orig.TypeStrindex = int32(13) orig.UnitStrindex = int32(13) - orig.AggregationTemporality = AggregationTemporality(13) return orig } diff --git a/vendor/go.opentelemetry.io/collector/pdata/internal/proto/marshal.go b/vendor/go.opentelemetry.io/collector/pdata/internal/proto/marshal.go index 1655533a2ce..b4d494e6e54 100644 --- a/vendor/go.opentelemetry.io/collector/pdata/internal/proto/marshal.go +++ b/vendor/go.opentelemetry.io/collector/pdata/internal/proto/marshal.go @@ -8,7 +8,6 @@ func EncodeVarint(buf []byte, offset int, v uint64) int { offset -= Sov(v) base := offset for v >= 1<<7 { - //nolint:gosec buf[offset] = uint8(v&0x7f | 0x80) v >>= 7 offset++ diff --git a/vendor/go.opentelemetry.io/collector/pdata/internal/proto/size.go b/vendor/go.opentelemetry.io/collector/pdata/internal/proto/size.go index 6f48443bf9d..bfc52147599 100644 --- a/vendor/go.opentelemetry.io/collector/pdata/internal/proto/size.go +++ b/vendor/go.opentelemetry.io/collector/pdata/internal/proto/size.go @@ -12,6 +12,5 @@ func Sov(x uint64) (n int) { } func Soz(x uint64) (n int) { - //nolint:gosec return Sov((x << 1) ^ uint64((int64(x) >> 63))) } diff --git a/vendor/go.opentelemetry.io/collector/pdata/internal/proto/unmarshal.go b/vendor/go.opentelemetry.io/collector/pdata/internal/proto/unmarshal.go index 6ecf5ca3a3a..b6dc0c79495 100644 --- a/vendor/go.opentelemetry.io/collector/pdata/internal/proto/unmarshal.go +++ b/vendor/go.opentelemetry.io/collector/pdata/internal/proto/unmarshal.go @@ -89,7 +89,6 @@ func ConsumeLen(buf []byte, pos int) (int, int, error) { if err != nil { return 0, 0, err } - //nolint:gosec length := int(num) if length < 0 { return 0, 0, ErrInvalidLength @@ -116,9 +115,7 @@ func ConsumeTag(buf []byte, pos int) (int32, WireType, int, error) { if err != nil { return 0, 0, 0, err } - //nolint:gosec fieldNum := int32(tag >> 3) - //nolint:gosec wireType := int8(tag & 0x7) if fieldNum <= 0 { return 0, 0, 0, fmt.Errorf("proto: Link: illegal field=%d (tag=%d, pos=%d)", fieldNum, tag, pos) diff --git a/vendor/go.opentelemetry.io/collector/pdata/pcommon/timestamp.go b/vendor/go.opentelemetry.io/collector/pdata/pcommon/timestamp.go index 037213a0caf..5fd1758b1be 100644 --- a/vendor/go.opentelemetry.io/collector/pdata/pcommon/timestamp.go +++ b/vendor/go.opentelemetry.io/collector/pdata/pcommon/timestamp.go @@ -13,13 +13,11 @@ type Timestamp uint64 // NewTimestampFromTime constructs a new Timestamp from the provided time.Time. func NewTimestampFromTime(t time.Time) Timestamp { - //nolint:gosec return Timestamp(uint64(t.UnixNano())) } // AsTime converts this to a time.Time. func (ts Timestamp) AsTime() time.Time { - //nolint:gosec return time.Unix(0, int64(ts)).UTC() } diff --git a/vendor/go.opentelemetry.io/collector/pdata/pcommon/value.go b/vendor/go.opentelemetry.io/collector/pdata/pcommon/value.go index f74fa79467b..3c39e704ed9 100644 --- a/vendor/go.opentelemetry.io/collector/pdata/pcommon/value.go +++ b/vendor/go.opentelemetry.io/collector/pdata/pcommon/value.go @@ -166,7 +166,6 @@ func (v Value) FromRaw(iv any) error { case int64: v.SetInt(tv) case uint: - //nolint:gosec v.SetInt(int64(tv)) case uint8: v.SetInt(int64(tv)) @@ -175,7 +174,6 @@ func (v Value) FromRaw(iv any) error { case uint32: v.SetInt(int64(tv)) case uint64: - //nolint:gosec v.SetInt(int64(tv)) case float32: v.SetDouble(float64(tv)) diff --git a/vendor/go.uber.org/zap/.golangci.yml b/vendor/go.uber.org/zap/.golangci.yml index 2346df13517..74faaa71d8c 100644 --- a/vendor/go.uber.org/zap/.golangci.yml +++ b/vendor/go.uber.org/zap/.golangci.yml @@ -25,7 +25,7 @@ linters-settings: govet: # These govet checks are disabled by default, but they're useful. enable: - - niliness + - nilness - reflectvaluecompare - sortslice - unusedwrite diff --git a/vendor/go.uber.org/zap/CHANGELOG.md b/vendor/go.uber.org/zap/CHANGELOG.md index 6d6cd5f4d70..86e7e6f9820 100644 --- a/vendor/go.uber.org/zap/CHANGELOG.md +++ b/vendor/go.uber.org/zap/CHANGELOG.md @@ -3,6 +3,16 @@ All notable changes to this project will be documented in this file. This project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). +## 1.27.1 (19 Nov 2025) +Enhancements: +* [#1501][]: prevent `Object` from panicking on nils +* [#1511][]: Fix a race condition in `WithLazy`. + +Thanks to @rabbbit, @alshopov, @jquirke, @arukiidou for their contributions to this release. + +[#1501]: https://github.com/uber-go/zap/pull/1501 +[#1511]: https://github.com/uber-go/zap/pull/1511 + ## 1.27.0 (20 Feb 2024) Enhancements: * [#1378][]: Add `WithLazy` method for `SugaredLogger`. diff --git a/vendor/go.uber.org/zap/CODE_OF_CONDUCT.md b/vendor/go.uber.org/zap/CODE_OF_CONDUCT.md index e327d9aa5cd..bc988b72ed0 100644 --- a/vendor/go.uber.org/zap/CODE_OF_CONDUCT.md +++ b/vendor/go.uber.org/zap/CODE_OF_CONDUCT.md @@ -71,5 +71,5 @@ This Code of Conduct is adapted from the [Contributor Covenant][homepage], version 1.4, available at [http://contributor-covenant.org/version/1/4][version]. -[homepage]: http://contributor-covenant.org -[version]: http://contributor-covenant.org/version/1/4/ +[homepage]: https://contributor-covenant.org +[version]: https://contributor-covenant.org/version/1/4/ diff --git a/vendor/go.uber.org/zap/LICENSE b/vendor/go.uber.org/zap/LICENSE index 6652bed45f4..3883b9a7eab 100644 --- a/vendor/go.uber.org/zap/LICENSE +++ b/vendor/go.uber.org/zap/LICENSE @@ -1,4 +1,4 @@ -Copyright (c) 2016-2017 Uber Technologies, Inc. +Copyright (c) 2016-2024 Uber Technologies, Inc. Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal diff --git a/vendor/go.uber.org/zap/Makefile b/vendor/go.uber.org/zap/Makefile index eb1cee53bd5..f9db385b34c 100644 --- a/vendor/go.uber.org/zap/Makefile +++ b/vendor/go.uber.org/zap/Makefile @@ -24,7 +24,7 @@ golangci-lint: @$(foreach mod,$(MODULE_DIRS), \ (cd $(mod) && \ echo "[lint] golangci-lint: $(mod)" && \ - golangci-lint run --path-prefix $(mod)) &&) true + golangci-lint run --path-prefix $(mod) ./...) &&) true .PHONY: tidy tidy: diff --git a/vendor/go.uber.org/zap/field.go b/vendor/go.uber.org/zap/field.go index 6743930b823..1884afabcd3 100644 --- a/vendor/go.uber.org/zap/field.go +++ b/vendor/go.uber.org/zap/field.go @@ -398,6 +398,9 @@ func Durationp(key string, val *time.Duration) Field { // struct-like user-defined types to the logging context. The struct's // MarshalLogObject method is called lazily. func Object(key string, val zapcore.ObjectMarshaler) Field { + if val == nil { + return nilField(key) + } return Field{Key: key, Type: zapcore.ObjectMarshalerType, Interface: val} } @@ -431,6 +434,13 @@ func (d dictObject) MarshalLogObject(enc zapcore.ObjectEncoder) error { return nil } +// DictObject constructs a [zapcore.ObjectMarshaler] with the given list of fields. +// The resulting object marshaler can be used as input to [Object], [Objects], or +// any other functions that expect an object marshaler. +func DictObject(val ...Field) zapcore.ObjectMarshaler { + return dictObject(val) +} + // We discovered an issue where zap.Any can cause a performance degradation // when used in new goroutines. // diff --git a/vendor/go.uber.org/zap/http_handler.go b/vendor/go.uber.org/zap/http_handler.go index 2be8f651500..1cae2c164b5 100644 --- a/vendor/go.uber.org/zap/http_handler.go +++ b/vendor/go.uber.org/zap/http_handler.go @@ -71,7 +71,7 @@ import ( func (lvl AtomicLevel) ServeHTTP(w http.ResponseWriter, r *http.Request) { if err := lvl.serveHTTP(w, r); err != nil { w.WriteHeader(http.StatusInternalServerError) - fmt.Fprintf(w, "internal error: %v", err) + _, _ = fmt.Fprintf(w, "internal error: %v", err) } } diff --git a/vendor/go.uber.org/zap/logger.go b/vendor/go.uber.org/zap/logger.go index c4d30032394..2d0ef141bcf 100644 --- a/vendor/go.uber.org/zap/logger.go +++ b/vendor/go.uber.org/zap/logger.go @@ -381,7 +381,11 @@ func (log *Logger) check(lvl zapcore.Level, msg string) *zapcore.CheckedEntry { if stack.Count() == 0 { if log.addCaller { - fmt.Fprintf(log.errorOutput, "%v Logger.check error: failed to get caller\n", ent.Time.UTC()) + _, _ = fmt.Fprintf( + log.errorOutput, + "%v Logger.check error: failed to get caller\n", + ent.Time.UTC(), + ) _ = log.errorOutput.Sync() } return ce diff --git a/vendor/go.uber.org/zap/options.go b/vendor/go.uber.org/zap/options.go index 43d357ac902..04a3c1e6357 100644 --- a/vendor/go.uber.org/zap/options.go +++ b/vendor/go.uber.org/zap/options.go @@ -125,7 +125,11 @@ func IncreaseLevel(lvl zapcore.LevelEnabler) Option { return optionFunc(func(log *Logger) { core, err := zapcore.NewIncreaseLevelCore(log.core, lvl) if err != nil { - fmt.Fprintf(log.errorOutput, "failed to IncreaseLevel: %v\n", err) + _, _ = fmt.Fprintf( + log.errorOutput, + "failed to IncreaseLevel: %v\n", + err, + ) } else { log.core = core } diff --git a/vendor/go.uber.org/zap/sink.go b/vendor/go.uber.org/zap/sink.go index 499772a00dc..92202280f13 100644 --- a/vendor/go.uber.org/zap/sink.go +++ b/vendor/go.uber.org/zap/sink.go @@ -71,7 +71,7 @@ func newSinkRegistry() *sinkRegistry { return sr } -// RegisterScheme registers the given factory for the specific scheme. +// RegisterSink registers the given factory for the specific scheme. func (sr *sinkRegistry) RegisterSink(scheme string, factory func(*url.URL) (Sink, error)) error { sr.mu.Lock() defer sr.mu.Unlock() diff --git a/vendor/go.uber.org/zap/zapcore/buffered_write_syncer.go b/vendor/go.uber.org/zap/zapcore/buffered_write_syncer.go index a40e93b3ec8..4b426a56487 100644 --- a/vendor/go.uber.org/zap/zapcore/buffered_write_syncer.go +++ b/vendor/go.uber.org/zap/zapcore/buffered_write_syncer.go @@ -188,32 +188,33 @@ func (s *BufferedWriteSyncer) flushLoop() { // Stop closes the buffer, cleans up background goroutines, and flushes // remaining unwritten data. func (s *BufferedWriteSyncer) Stop() (err error) { - var stopped bool - // Critical section. - func() { + stopped := func() bool { s.mu.Lock() defer s.mu.Unlock() if !s.initialized { - return + return false } - stopped = s.stopped - if stopped { - return + if s.stopped { + return false } s.stopped = true s.ticker.Stop() close(s.stop) // tell flushLoop to stop - <-s.done // and wait until it has + return true }() - // Don't call Sync on consecutive Stops. + // Not initialized, or already stopped, no need for any cleanup. if !stopped { - err = s.Sync() + return } - return err + // Wait for flushLoop to end outside of the lock, as it may need the lock to complete. + // See https://github.com/uber-go/zap/issues/1428 for details. + <-s.done + + return s.Sync() } diff --git a/vendor/go.uber.org/zap/zapcore/console_encoder.go b/vendor/go.uber.org/zap/zapcore/console_encoder.go index cc2b4e07b93..98eea5154d4 100644 --- a/vendor/go.uber.org/zap/zapcore/console_encoder.go +++ b/vendor/go.uber.org/zap/zapcore/console_encoder.go @@ -105,7 +105,7 @@ func (c consoleEncoder) EncodeEntry(ent Entry, fields []Field) (*buffer.Buffer, if i > 0 { line.AppendString(c.ConsoleSeparator) } - fmt.Fprint(line, arr.elems[i]) + _, _ = fmt.Fprint(line, arr.elems[i]) } putSliceEncoder(arr) diff --git a/vendor/go.uber.org/zap/zapcore/entry.go b/vendor/go.uber.org/zap/zapcore/entry.go index 459a5d7ce3c..841752f2ed6 100644 --- a/vendor/go.uber.org/zap/zapcore/entry.go +++ b/vendor/go.uber.org/zap/zapcore/entry.go @@ -241,7 +241,12 @@ func (ce *CheckedEntry) Write(fields ...Field) { // If the entry is dirty, log an internal error; because the // CheckedEntry is being used after it was returned to the pool, // the message may be an amalgamation from multiple call sites. - fmt.Fprintf(ce.ErrorOutput, "%v Unsafe CheckedEntry re-use near Entry %+v.\n", ce.Time, ce.Entry) + _, _ = fmt.Fprintf( + ce.ErrorOutput, + "%v Unsafe CheckedEntry re-use near Entry %+v.\n", + ce.Time, + ce.Entry, + ) _ = ce.ErrorOutput.Sync() // ignore error } return @@ -253,7 +258,12 @@ func (ce *CheckedEntry) Write(fields ...Field) { err = multierr.Append(err, ce.cores[i].Write(ce.Entry, fields)) } if err != nil && ce.ErrorOutput != nil { - fmt.Fprintf(ce.ErrorOutput, "%v write error: %v\n", ce.Time, err) + _, _ = fmt.Fprintf( + ce.ErrorOutput, + "%v write error: %v\n", + ce.Time, + err, + ) _ = ce.ErrorOutput.Sync() // ignore error } diff --git a/vendor/go.uber.org/zap/zapcore/lazy_with.go b/vendor/go.uber.org/zap/zapcore/lazy_with.go index 05288d6a88e..500809de08b 100644 --- a/vendor/go.uber.org/zap/zapcore/lazy_with.go +++ b/vendor/go.uber.org/zap/zapcore/lazy_with.go @@ -23,7 +23,8 @@ package zapcore import "sync" type lazyWithCore struct { - Core + core Core + originalCore Core sync.Once fields []Field } @@ -32,23 +33,45 @@ type lazyWithCore struct { // the logger is written to (or is further chained in a lon-lazy manner). func NewLazyWith(core Core, fields []Field) Core { return &lazyWithCore{ - Core: core, - fields: fields, + core: nil, // core is allocated once `initOnce` is called. + originalCore: core, + fields: fields, } } func (d *lazyWithCore) initOnce() { d.Once.Do(func() { - d.Core = d.Core.With(d.fields) + d.core = d.originalCore.With(d.fields) }) } func (d *lazyWithCore) With(fields []Field) Core { d.initOnce() - return d.Core.With(fields) + return d.core.With(fields) } func (d *lazyWithCore) Check(e Entry, ce *CheckedEntry) *CheckedEntry { + // This is safe because `lazyWithCore` doesn't change the level. + // So we can delagate the level check, any not `initOnce` + // just for the check. + if !d.originalCore.Enabled(e.Level) { + return ce + } + d.initOnce() + return d.core.Check(e, ce) +} + +func (d *lazyWithCore) Enabled(level Level) bool { + // Like above, this is safe because `lazyWithCore` doesn't change the level. + return d.originalCore.Enabled(level) +} + +func (d *lazyWithCore) Write(e Entry, fields []Field) error { + d.initOnce() + return d.core.Write(e, fields) +} + +func (d *lazyWithCore) Sync() error { d.initOnce() - return d.Core.Check(e, ce) + return d.core.Sync() } diff --git a/vendor/go.uber.org/zap/zapcore/level.go b/vendor/go.uber.org/zap/zapcore/level.go index e01a2413166..f3e166d67b0 100644 --- a/vendor/go.uber.org/zap/zapcore/level.go +++ b/vendor/go.uber.org/zap/zapcore/level.go @@ -179,19 +179,19 @@ func (l *Level) UnmarshalText(text []byte) error { func (l *Level) unmarshalText(text []byte) bool { switch string(text) { - case "debug", "DEBUG": + case "debug": *l = DebugLevel - case "info", "INFO", "": // make the zero value useful + case "info", "": // make the zero value useful *l = InfoLevel - case "warn", "WARN": + case "warn", "warning": *l = WarnLevel - case "error", "ERROR": + case "error": *l = ErrorLevel - case "dpanic", "DPANIC": + case "dpanic": *l = DPanicLevel - case "panic", "PANIC": + case "panic": *l = PanicLevel - case "fatal", "FATAL": + case "fatal": *l = FatalLevel default: return false diff --git a/vendor/google.golang.org/api/iamcredentials/v1/iamcredentials-api.json b/vendor/google.golang.org/api/iamcredentials/v1/iamcredentials-api.json index 7d8b3f719f1..3694e4aebb3 100644 --- a/vendor/google.golang.org/api/iamcredentials/v1/iamcredentials-api.json +++ b/vendor/google.golang.org/api/iamcredentials/v1/iamcredentials-api.json @@ -340,7 +340,7 @@ } } }, - "revision": "20250911", + "revision": "20251022", "rootUrl": "https://iamcredentials.googleapis.com/", "schemas": { "GenerateAccessTokenRequest": { diff --git a/vendor/google.golang.org/api/internal/version.go b/vendor/google.golang.org/api/internal/version.go index 6646bbcbfc0..6368b1d5cc6 100644 --- a/vendor/google.golang.org/api/internal/version.go +++ b/vendor/google.golang.org/api/internal/version.go @@ -5,4 +5,4 @@ package internal // Version is the current tagged release of the library. -const Version = "0.252.0" +const Version = "0.257.0" diff --git a/vendor/modules.txt b/vendor/modules.txt index af452941bcd..a831bf05f2a 100644 --- a/vendor/modules.txt +++ b/vendor/modules.txt @@ -49,7 +49,7 @@ cloud.google.com/go/storage/experimental cloud.google.com/go/storage/internal cloud.google.com/go/storage/internal/apiv2 cloud.google.com/go/storage/internal/apiv2/storagepb -# github.com/Azure/azure-sdk-for-go/sdk/azcore v1.19.1 +# github.com/Azure/azure-sdk-for-go/sdk/azcore v1.20.0 ## explicit; go 1.23.0 github.com/Azure/azure-sdk-for-go/sdk/azcore github.com/Azure/azure-sdk-for-go/sdk/azcore/arm/internal/resource @@ -71,7 +71,7 @@ github.com/Azure/azure-sdk-for-go/sdk/azcore/runtime github.com/Azure/azure-sdk-for-go/sdk/azcore/streaming github.com/Azure/azure-sdk-for-go/sdk/azcore/to github.com/Azure/azure-sdk-for-go/sdk/azcore/tracing -# github.com/Azure/azure-sdk-for-go/sdk/azidentity v1.12.0 +# github.com/Azure/azure-sdk-for-go/sdk/azidentity v1.13.1 ## explicit; go 1.23.0 github.com/Azure/azure-sdk-for-go/sdk/azidentity github.com/Azure/azure-sdk-for-go/sdk/azidentity/internal @@ -99,7 +99,7 @@ github.com/Azure/azure-sdk-for-go/sdk/storage/azblob/internal/shared github.com/Azure/azure-sdk-for-go/sdk/storage/azblob/pageblob github.com/Azure/azure-sdk-for-go/sdk/storage/azblob/sas github.com/Azure/azure-sdk-for-go/sdk/storage/azblob/service -# github.com/AzureAD/microsoft-authentication-library-for-go v1.5.0 +# github.com/AzureAD/microsoft-authentication-library-for-go v1.6.0 ## explicit; go 1.18 github.com/AzureAD/microsoft-authentication-library-for-go/apps/cache github.com/AzureAD/microsoft-authentication-library-for-go/apps/confidential @@ -611,8 +611,8 @@ github.com/google/go-cmp/cmp/internal/diff github.com/google/go-cmp/cmp/internal/flags github.com/google/go-cmp/cmp/internal/function github.com/google/go-cmp/cmp/internal/value -# github.com/google/pprof v0.0.0-20250923004556-9e5a51aed1e8 -## explicit; go 1.24.0 +# github.com/google/pprof v0.0.0-20251213031049-b05bdaca462f +## explicit; go 1.24.9 github.com/google/pprof/profile # github.com/google/s2a-go v0.1.9 ## explicit; go 1.20 @@ -640,7 +640,7 @@ github.com/google/s2a-go/stream # github.com/google/uuid v1.6.0 ## explicit github.com/google/uuid -# github.com/googleapis/enterprise-certificate-proxy v0.3.6 +# github.com/googleapis/enterprise-certificate-proxy v0.3.7 ## explicit; go 1.23.0 github.com/googleapis/enterprise-certificate-proxy/client github.com/googleapis/enterprise-certificate-proxy/client/util @@ -675,8 +675,8 @@ github.com/grpc-ecosystem/go-grpc-middleware/v2/interceptors/logging github.com/grpc-ecosystem/grpc-gateway/v2/internal/httprule github.com/grpc-ecosystem/grpc-gateway/v2/runtime github.com/grpc-ecosystem/grpc-gateway/v2/utilities -# github.com/hashicorp/consul/api v1.32.0 -## explicit; go 1.22.12 +# github.com/hashicorp/consul/api v1.32.1 +## explicit; go 1.23.8 github.com/hashicorp/consul/api # github.com/hashicorp/errwrap v1.1.0 ## explicit @@ -706,7 +706,7 @@ github.com/hashicorp/go-rootcerts # github.com/hashicorp/go-sockaddr v1.0.7 ## explicit; go 1.19 github.com/hashicorp/go-sockaddr -# github.com/hashicorp/go-version v1.7.0 +# github.com/hashicorp/go-version v1.8.0 ## explicit github.com/hashicorp/go-version # github.com/hashicorp/golang-lru v0.6.0 @@ -807,8 +807,8 @@ github.com/mdlayher/vsock # github.com/metalmatze/signal v0.0.0-20210307161603-1c9aa721a97a ## explicit; go 1.14 github.com/metalmatze/signal/server/signalhttp -# github.com/miekg/dns v1.1.68 -## explicit; go 1.23.0 +# github.com/miekg/dns v1.1.69 +## explicit; go 1.24.0 github.com/miekg/dns # github.com/minio/crc64nvme v1.1.1 ## explicit; go 1.22 @@ -874,13 +874,13 @@ github.com/oklog/ulid # github.com/oklog/ulid/v2 v2.1.1 ## explicit; go 1.15 github.com/oklog/ulid/v2 -# github.com/open-telemetry/opentelemetry-collector-contrib/internal/exp/metrics v0.139.0 +# github.com/open-telemetry/opentelemetry-collector-contrib/internal/exp/metrics v0.142.0 ## explicit; go 1.24.0 github.com/open-telemetry/opentelemetry-collector-contrib/internal/exp/metrics/identity -# github.com/open-telemetry/opentelemetry-collector-contrib/pkg/pdatautil v0.139.0 +# github.com/open-telemetry/opentelemetry-collector-contrib/pkg/pdatautil v0.142.0 ## explicit; go 1.24.0 github.com/open-telemetry/opentelemetry-collector-contrib/pkg/pdatautil -# github.com/open-telemetry/opentelemetry-collector-contrib/processor/deltatocumulativeprocessor v0.139.0 +# github.com/open-telemetry/opentelemetry-collector-contrib/processor/deltatocumulativeprocessor v0.142.0 ## explicit; go 1.24.0 github.com/open-telemetry/opentelemetry-collector-contrib/processor/deltatocumulativeprocessor github.com/open-telemetry/opentelemetry-collector-contrib/processor/deltatocumulativeprocessor/internal/data @@ -1083,8 +1083,8 @@ github.com/prometheus/otlptranslator github.com/prometheus/procfs github.com/prometheus/procfs/internal/fs github.com/prometheus/procfs/internal/util -# github.com/prometheus/prometheus v0.308.1 -## explicit; go 1.24.0 +# github.com/prometheus/prometheus v0.309.1 +## explicit; go 1.24.9 github.com/prometheus/prometheus/config github.com/prometheus/prometheus/discovery github.com/prometheus/prometheus/discovery/dns @@ -1133,6 +1133,7 @@ github.com/prometheus/prometheus/util/almost github.com/prometheus/prometheus/util/annotations github.com/prometheus/prometheus/util/compression github.com/prometheus/prometheus/util/convertnhcb +github.com/prometheus/prometheus/util/features github.com/prometheus/prometheus/util/gate github.com/prometheus/prometheus/util/httputil github.com/prometheus/prometheus/util/jsonutil @@ -1416,26 +1417,26 @@ go.opencensus.io/trace/tracestate ## explicit; go 1.24.0 go.opentelemetry.io/auto/sdk go.opentelemetry.io/auto/sdk/internal/telemetry -# go.opentelemetry.io/collector/component v1.45.0 +# go.opentelemetry.io/collector/component v1.48.0 ## explicit; go 1.24.0 go.opentelemetry.io/collector/component -# go.opentelemetry.io/collector/confmap v1.45.0 +# go.opentelemetry.io/collector/confmap v1.48.0 ## explicit; go 1.24.0 go.opentelemetry.io/collector/confmap go.opentelemetry.io/collector/confmap/internal go.opentelemetry.io/collector/confmap/internal/mapstructure go.opentelemetry.io/collector/confmap/internal/third_party/composehook -# go.opentelemetry.io/collector/confmap/xconfmap v0.139.0 +# go.opentelemetry.io/collector/confmap/xconfmap v0.142.0 ## explicit; go 1.24.0 go.opentelemetry.io/collector/confmap/xconfmap -# go.opentelemetry.io/collector/consumer v1.45.0 +# go.opentelemetry.io/collector/consumer v1.48.0 ## explicit; go 1.24.0 go.opentelemetry.io/collector/consumer go.opentelemetry.io/collector/consumer/internal -# go.opentelemetry.io/collector/featuregate v1.45.0 +# go.opentelemetry.io/collector/featuregate v1.48.0 ## explicit; go 1.24.0 go.opentelemetry.io/collector/featuregate -# go.opentelemetry.io/collector/pdata v1.45.0 +# go.opentelemetry.io/collector/pdata v1.48.0 ## explicit; go 1.24.0 go.opentelemetry.io/collector/pdata/internal go.opentelemetry.io/collector/pdata/internal/json @@ -1447,11 +1448,11 @@ go.opentelemetry.io/collector/pdata/plog go.opentelemetry.io/collector/pdata/pmetric go.opentelemetry.io/collector/pdata/pmetric/pmetricotlp go.opentelemetry.io/collector/pdata/ptrace -# go.opentelemetry.io/collector/pipeline v1.45.0 +# go.opentelemetry.io/collector/pipeline v1.48.0 ## explicit; go 1.24.0 go.opentelemetry.io/collector/pipeline go.opentelemetry.io/collector/pipeline/internal/globalsignal -# go.opentelemetry.io/collector/processor v1.45.0 +# go.opentelemetry.io/collector/processor v1.48.0 ## explicit; go 1.24.0 go.opentelemetry.io/collector/processor go.opentelemetry.io/collector/processor/internal @@ -1591,7 +1592,7 @@ go.uber.org/goleak/internal/stack # go.uber.org/multierr v1.11.0 ## explicit; go 1.19 go.uber.org/multierr -# go.uber.org/zap v1.27.0 +# go.uber.org/zap v1.27.1 ## explicit; go 1.19 go.uber.org/zap go.uber.org/zap/buffer @@ -1723,7 +1724,7 @@ golang.org/x/tools/internal/versions gonum.org/v1/gonum/floats gonum.org/v1/gonum/floats/scalar gonum.org/v1/gonum/internal/asm/f64 -# google.golang.org/api v0.252.0 +# google.golang.org/api v0.257.0 ## explicit; go 1.24.0 google.golang.org/api/googleapi google.golang.org/api/googleapi/transport @@ -1975,11 +1976,11 @@ gopkg.in/yaml.v2 # gopkg.in/yaml.v3 v3.0.1 ## explicit gopkg.in/yaml.v3 -# k8s.io/apimachinery v0.34.1 +# k8s.io/apimachinery v0.34.3 ## explicit; go 1.24.0 k8s.io/apimachinery/pkg/util/runtime k8s.io/apimachinery/pkg/util/sets -# k8s.io/client-go v0.34.1 +# k8s.io/client-go v0.34.3 ## explicit; go 1.24.0 k8s.io/client-go/tools/metrics k8s.io/client-go/util/workqueue From e4faa9eb139c86594a52ebe297a07d70a5749c4a Mon Sep 17 00:00:00 2001 From: SungJin1212 Date: Thu, 21 May 2026 21:16:39 +0900 Subject: [PATCH 2/2] Add BlockReloadInterval Signed-off-by: SungJin1212 --- pkg/ingester/ingester.go | 1 + 1 file changed, 1 insertion(+) diff --git a/pkg/ingester/ingester.go b/pkg/ingester/ingester.go index 2f38b160e27..d2361872851 100644 --- a/pkg/ingester/ingester.go +++ b/pkg/ingester/ingester.go @@ -2951,6 +2951,7 @@ func (i *Ingester) createTSDB(userID string) (*userTSDB, error) { OutOfOrderCapMax: i.cfg.BlocksStorageConfig.TSDB.OutOfOrderCapMax, EnableOverlappingCompaction: false, // Always let compactors handle overlapped blocks, e.g. OOO blocks. BlockChunkQuerierFunc: i.blockChunkQuerierFunc(userID), + BlockReloadInterval: 1 * time.Minute, // use default value }, nil) if err != nil { return nil, errors.Wrapf(err, "failed to open TSDB: %s", udir)