From 6ad29907aed2bdad8386f256853d05275d7c7851 Mon Sep 17 00:00:00 2001 From: Ben Kochie Date: Wed, 6 Nov 2024 13:02:49 +0000 Subject: [PATCH 01/48] Mark sigv4 deprecated (#715) Mark the sigv4 module deprecated in favor of the new repo location. * https://github.com/prometheus/sigv4 Closes: https://github.com/prometheus/common/issues/709 Signed-off-by: SuperQ --- sigv4/sigv4.go | 1 + 1 file changed, 1 insertion(+) diff --git a/sigv4/sigv4.go b/sigv4/sigv4.go index ae0f76e52..979fd8466 100644 --- a/sigv4/sigv4.go +++ b/sigv4/sigv4.go @@ -11,6 +11,7 @@ // See the License for the specific language governing permissions and // limitations under the License. +// Deprecated: This package has been migrated to github.com/prometheus/sigv4. package sigv4 import ( From ee5cc87c607f1ffaa207e6b2d2a4e789886cc6ce Mon Sep 17 00:00:00 2001 From: Matthieu MOREL Date: Thu, 7 Nov 2024 09:29:19 +0100 Subject: [PATCH 02/48] Provide a way to get UserAgent (#716) Signed-off-by: Matthieu MOREL --- version/info.go | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/version/info.go b/version/info.go index 197d95e5c..3ca72ab3d 100644 --- a/version/info.go +++ b/version/info.go @@ -90,6 +90,10 @@ func GetTags() string { return computedTags } +func UserAgent() string { + return "Prometheus/" + Version +} + func init() { computedRevision, computedTags = computeRevision() } From b424bae8242b2dcda1c296c7f069e075b293aaaa Mon Sep 17 00:00:00 2001 From: PrometheusBot Date: Thu, 7 Nov 2024 09:29:38 +0100 Subject: [PATCH 03/48] Update common Prometheus files (#714) Signed-off-by: prombot --- .github/workflows/golangci-lint.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/golangci-lint.yml b/.github/workflows/golangci-lint.yml index 1c099932b..7183091ac 100644 --- a/.github/workflows/golangci-lint.yml +++ b/.github/workflows/golangci-lint.yml @@ -26,14 +26,14 @@ jobs: - name: Checkout repository uses: actions/checkout@d632683dd7b4114ad314bca15554477dd762a938 # v4.2.0 - name: Install Go - uses: actions/setup-go@0a12ed9d6a96ab950c8f026ed9f722fe0da7ef32 # v5.0.2 + uses: actions/setup-go@41dfa10bad2bb2ae585af6ee5bb4d7d973ad74ed # v5.1.0 with: go-version: 1.23.x - name: Install snmp_exporter/generator dependencies run: sudo apt-get update && sudo apt-get -y install libsnmp-dev if: github.repository == 'prometheus/snmp_exporter' - name: Lint - uses: golangci/golangci-lint-action@aaa42aa0628b4ae2578232a66b541047968fac86 # v6.1.0 + uses: golangci/golangci-lint-action@971e284b6050e8a5849b72094c50ab08da042db8 # v6.1.1 with: args: --verbose version: v1.60.2 From 28e4459e04718757da9d40d3c615d184d2cab751 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Thu, 7 Nov 2024 10:06:06 +0100 Subject: [PATCH 04/48] Bump golang.org/x/net from 0.29.0 to 0.30.0 (#712) * Bump golang.org/x/net from 0.29.0 to 0.30.0 Bumps [golang.org/x/net](https://github.com/golang/net) from 0.29.0 to 0.30.0. - [Commits](https://github.com/golang/net/compare/v0.29.0...v0.30.0) --- updated-dependencies: - dependency-name: golang.org/x/net dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] * Go mod tidy sigv4 Signed-off-by: SuperQ --------- Signed-off-by: dependabot[bot] Signed-off-by: SuperQ Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> Co-authored-by: SuperQ --- go.mod | 6 +++--- go.sum | 12 ++++++------ sigv4/go.mod | 6 +++--- sigv4/go.sum | 12 ++++++------ 4 files changed, 18 insertions(+), 18 deletions(-) diff --git a/go.mod b/go.mod index b5b11c5e2..03840e14a 100644 --- a/go.mod +++ b/go.mod @@ -11,7 +11,7 @@ require ( github.com/mwitkow/go-conntrack v0.0.0-20190716064945-2f068394615f github.com/prometheus/client_model v0.6.1 github.com/stretchr/testify v1.9.0 - golang.org/x/net v0.29.0 + golang.org/x/net v0.30.0 golang.org/x/oauth2 v0.23.0 google.golang.org/protobuf v1.34.2 gopkg.in/yaml.v2 v2.4.0 @@ -29,8 +29,8 @@ require ( github.com/prometheus/procfs v0.15.1 // indirect github.com/rogpeppe/go-internal v1.10.0 // indirect github.com/xhit/go-str2duration/v2 v2.1.0 // indirect - golang.org/x/sys v0.25.0 // indirect - golang.org/x/text v0.18.0 // indirect + golang.org/x/sys v0.26.0 // indirect + golang.org/x/text v0.19.0 // indirect gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c // indirect gopkg.in/yaml.v3 v3.0.1 // indirect ) diff --git a/go.sum b/go.sum index 73c7c618a..265aefd99 100644 --- a/go.sum +++ b/go.sum @@ -47,14 +47,14 @@ github.com/stretchr/testify v1.9.0 h1:HtqpIVDClZ4nwg75+f6Lvsy/wHu+3BoSGCbBAcpTsT github.com/stretchr/testify v1.9.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY= github.com/xhit/go-str2duration/v2 v2.1.0 h1:lxklc02Drh6ynqX+DdPyp5pCKLUQpRT8bp8Ydu2Bstc= github.com/xhit/go-str2duration/v2 v2.1.0/go.mod h1:ohY8p+0f07DiV6Em5LKB0s2YpLtXVyJfNt1+BlmyAsU= -golang.org/x/net v0.29.0 h1:5ORfpBpCs4HzDYoodCDBbwHzdR5UrLBZ3sOnUJmFoHo= -golang.org/x/net v0.29.0/go.mod h1:gLkgy8jTGERgjzMic6DS9+SP0ajcu6Xu3Orq/SpETg0= +golang.org/x/net v0.30.0 h1:AcW1SDZMkb8IpzCdQUaIq2sP4sZ4zw+55h6ynffypl4= +golang.org/x/net v0.30.0/go.mod h1:2wGyMJ5iFasEhkwi13ChkO/t1ECNC4X4eBKkVFyYFlU= golang.org/x/oauth2 v0.23.0 h1:PbgcYx2W7i4LvjJWEbf0ngHV6qJYr86PkAV3bXdLEbs= golang.org/x/oauth2 v0.23.0/go.mod h1:XYTD2NtWslqkgxebSiOHnXEap4TF09sJSc7H1sXbhtI= -golang.org/x/sys v0.25.0 h1:r+8e+loiHxRqhXVl6ML1nO3l1+oFoWbnlu2Ehimmi34= -golang.org/x/sys v0.25.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= -golang.org/x/text v0.18.0 h1:XvMDiNzPAl0jr17s6W9lcaIhGUfUORdGCNsuLmPG224= -golang.org/x/text v0.18.0/go.mod h1:BuEKDfySbSR4drPmRPG/7iBdf8hvFMuRexcpahXilzY= +golang.org/x/sys v0.26.0 h1:KHjCJyddX0LoSTb3J+vWpupP9p0oznkqVk/IfjymZbo= +golang.org/x/sys v0.26.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= +golang.org/x/text v0.19.0 h1:kTxAhCbGbxhK0IwgSKiMO5awPoDQ0RpfiVYBfK860YM= +golang.org/x/text v0.19.0/go.mod h1:BuEKDfySbSR4drPmRPG/7iBdf8hvFMuRexcpahXilzY= google.golang.org/protobuf v1.34.2 h1:6xV6lTsCfpGD21XK49h7MhtcApnLqkfYgPcdHftf6hg= google.golang.org/protobuf v1.34.2/go.mod h1:qYOHts0dSfpeUzUFpOMr/WGzszTmLH+DiWniOlNbLDw= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= diff --git a/sigv4/go.mod b/sigv4/go.mod index 0239df61a..0ab227241 100644 --- a/sigv4/go.mod +++ b/sigv4/go.mod @@ -24,10 +24,10 @@ require ( github.com/prometheus/client_golang v1.20.4 // indirect github.com/prometheus/client_model v0.6.1 // indirect github.com/prometheus/procfs v0.15.1 // indirect - golang.org/x/net v0.29.0 // indirect + golang.org/x/net v0.30.0 // indirect golang.org/x/oauth2 v0.23.0 // indirect - golang.org/x/sys v0.25.0 // indirect - golang.org/x/text v0.18.0 // indirect + golang.org/x/sys v0.26.0 // indirect + golang.org/x/text v0.19.0 // indirect google.golang.org/protobuf v1.34.2 // indirect gopkg.in/yaml.v3 v3.0.1 // indirect ) diff --git a/sigv4/go.sum b/sigv4/go.sum index 893285f17..a4d07977f 100644 --- a/sigv4/go.sum +++ b/sigv4/go.sum @@ -39,14 +39,14 @@ github.com/rogpeppe/go-internal v1.10.0/go.mod h1:UQnix2H7Ngw/k4C5ijL5+65zddjncj github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= github.com/stretchr/testify v1.9.0 h1:HtqpIVDClZ4nwg75+f6Lvsy/wHu+3BoSGCbBAcpTsTg= github.com/stretchr/testify v1.9.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY= -golang.org/x/net v0.29.0 h1:5ORfpBpCs4HzDYoodCDBbwHzdR5UrLBZ3sOnUJmFoHo= -golang.org/x/net v0.29.0/go.mod h1:gLkgy8jTGERgjzMic6DS9+SP0ajcu6Xu3Orq/SpETg0= +golang.org/x/net v0.30.0 h1:AcW1SDZMkb8IpzCdQUaIq2sP4sZ4zw+55h6ynffypl4= +golang.org/x/net v0.30.0/go.mod h1:2wGyMJ5iFasEhkwi13ChkO/t1ECNC4X4eBKkVFyYFlU= golang.org/x/oauth2 v0.23.0 h1:PbgcYx2W7i4LvjJWEbf0ngHV6qJYr86PkAV3bXdLEbs= golang.org/x/oauth2 v0.23.0/go.mod h1:XYTD2NtWslqkgxebSiOHnXEap4TF09sJSc7H1sXbhtI= -golang.org/x/sys v0.25.0 h1:r+8e+loiHxRqhXVl6ML1nO3l1+oFoWbnlu2Ehimmi34= -golang.org/x/sys v0.25.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= -golang.org/x/text v0.18.0 h1:XvMDiNzPAl0jr17s6W9lcaIhGUfUORdGCNsuLmPG224= -golang.org/x/text v0.18.0/go.mod h1:BuEKDfySbSR4drPmRPG/7iBdf8hvFMuRexcpahXilzY= +golang.org/x/sys v0.26.0 h1:KHjCJyddX0LoSTb3J+vWpupP9p0oznkqVk/IfjymZbo= +golang.org/x/sys v0.26.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= +golang.org/x/text v0.19.0 h1:kTxAhCbGbxhK0IwgSKiMO5awPoDQ0RpfiVYBfK860YM= +golang.org/x/text v0.19.0/go.mod h1:BuEKDfySbSR4drPmRPG/7iBdf8hvFMuRexcpahXilzY= google.golang.org/protobuf v1.34.2 h1:6xV6lTsCfpGD21XK49h7MhtcApnLqkfYgPcdHftf6hg= google.golang.org/protobuf v1.34.2/go.mod h1:qYOHts0dSfpeUzUFpOMr/WGzszTmLH+DiWniOlNbLDw= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= From 937c5b5a89f3845e65809e3606cf3dfbac996890 Mon Sep 17 00:00:00 2001 From: Matthieu MOREL Date: Thu, 7 Nov 2024 20:43:46 +0100 Subject: [PATCH 05/48] chore: enable perfsprint linter (#717) Signed-off-by: Matthieu MOREL --- .golangci.yml | 12 +++++++++ config/http_config.go | 48 ++++++++++++++++++------------------ config/http_config_test.go | 16 ++++++------ config/tls_config_test.go | 5 ++-- expfmt/encode.go | 4 +-- expfmt/expfmt.go | 4 +-- expfmt/openmetrics_create.go | 4 +-- expfmt/text_parse.go | 2 +- model/alert.go | 7 +++--- model/metric.go | 3 ++- model/silence.go | 17 +++++++------ model/value_float.go | 3 ++- model/value_histogram.go | 7 +++--- sigv4/sigv4.go | 3 ++- sigv4/sigv4_config.go | 4 +-- 15 files changed, 79 insertions(+), 60 deletions(-) diff --git a/.golangci.yml b/.golangci.yml index ce518d250..b94def309 100644 --- a/.golangci.yml +++ b/.golangci.yml @@ -11,6 +11,7 @@ linters: - govet - ineffassign - misspell + - perfsprint - revive - staticcheck - testifylint @@ -18,6 +19,17 @@ linters: linters-settings: goimports: local-prefixes: github.com/prometheus/common + perfsprint: + # Optimizes even if it requires an int or uint type cast. + int-conversion: true + # Optimizes into `err.Error()` even if it is only equivalent for non-nil errors. + err-error: true + # Optimizes `fmt.Errorf`. + errorf: true + # Optimizes `fmt.Sprintf` with only one argument. + sprintf1: true + # Optimizes into strings concatenation. + strconcat: false revive: rules: # https://github.com/mgechev/revive/blob/master/RULES_DESCRIPTIONS.md#unused-parameter diff --git a/config/http_config.go b/config/http_config.go index e6bdd4c03..57ec252ad 100644 --- a/config/http_config.go +++ b/config/http_config.go @@ -357,33 +357,33 @@ func nonZeroCount[T comparable](values ...T) int { func (c *HTTPClientConfig) Validate() error { // Backwards compatibility with the bearer_token field. if len(c.BearerToken) > 0 && len(c.BearerTokenFile) > 0 { - return fmt.Errorf("at most one of bearer_token & bearer_token_file must be configured") + return errors.New("at most one of bearer_token & bearer_token_file must be configured") } if (c.BasicAuth != nil || c.OAuth2 != nil) && (len(c.BearerToken) > 0 || len(c.BearerTokenFile) > 0) { - return fmt.Errorf("at most one of basic_auth, oauth2, bearer_token & bearer_token_file must be configured") + return errors.New("at most one of basic_auth, oauth2, bearer_token & bearer_token_file must be configured") } if c.BasicAuth != nil && nonZeroCount(string(c.BasicAuth.Username) != "", c.BasicAuth.UsernameFile != "", c.BasicAuth.UsernameRef != "") > 1 { - return fmt.Errorf("at most one of basic_auth username, username_file & username_ref must be configured") + return errors.New("at most one of basic_auth username, username_file & username_ref must be configured") } if c.BasicAuth != nil && nonZeroCount(string(c.BasicAuth.Password) != "", c.BasicAuth.PasswordFile != "", c.BasicAuth.PasswordRef != "") > 1 { - return fmt.Errorf("at most one of basic_auth password, password_file & password_ref must be configured") + return errors.New("at most one of basic_auth password, password_file & password_ref must be configured") } if c.Authorization != nil { if len(c.BearerToken) > 0 || len(c.BearerTokenFile) > 0 { - return fmt.Errorf("authorization is not compatible with bearer_token & bearer_token_file") + return errors.New("authorization is not compatible with bearer_token & bearer_token_file") } if nonZeroCount(string(c.Authorization.Credentials) != "", c.Authorization.CredentialsFile != "", c.Authorization.CredentialsRef != "") > 1 { - return fmt.Errorf("at most one of authorization credentials & credentials_file must be configured") + return errors.New("at most one of authorization credentials & credentials_file must be configured") } c.Authorization.Type = strings.TrimSpace(c.Authorization.Type) if len(c.Authorization.Type) == 0 { c.Authorization.Type = "Bearer" } if strings.ToLower(c.Authorization.Type) == "basic" { - return fmt.Errorf(`authorization type cannot be set to "basic", use "basic_auth" instead`) + return errors.New(`authorization type cannot be set to "basic", use "basic_auth" instead`) } if c.BasicAuth != nil || c.OAuth2 != nil { - return fmt.Errorf("at most one of basic_auth, oauth2 & authorization must be configured") + return errors.New("at most one of basic_auth, oauth2 & authorization must be configured") } } else { if len(c.BearerToken) > 0 { @@ -399,16 +399,16 @@ func (c *HTTPClientConfig) Validate() error { } if c.OAuth2 != nil { if c.BasicAuth != nil { - return fmt.Errorf("at most one of basic_auth, oauth2 & authorization must be configured") + return errors.New("at most one of basic_auth, oauth2 & authorization must be configured") } if len(c.OAuth2.ClientID) == 0 { - return fmt.Errorf("oauth2 client_id must be configured") + return errors.New("oauth2 client_id must be configured") } if len(c.OAuth2.TokenURL) == 0 { - return fmt.Errorf("oauth2 token_url must be configured") + return errors.New("oauth2 token_url must be configured") } if nonZeroCount(len(c.OAuth2.ClientSecret) > 0, len(c.OAuth2.ClientSecretFile) > 0, len(c.OAuth2.ClientSecretRef) > 0) > 1 { - return fmt.Errorf("at most one of oauth2 client_secret, client_secret_file & client_secret_ref must be configured") + return errors.New("at most one of oauth2 client_secret, client_secret_file & client_secret_ref must be configured") } } if err := c.ProxyConfig.Validate(); err != nil { @@ -735,7 +735,7 @@ func (s *FileSecret) Fetch(ctx context.Context) (string, error) { } func (s *FileSecret) Description() string { - return fmt.Sprintf("file %s", s.file) + return "file " + s.file } func (s *FileSecret) Immutable() bool { @@ -753,7 +753,7 @@ func (s *refSecret) Fetch(ctx context.Context) (string, error) { } func (s *refSecret) Description() string { - return fmt.Sprintf("ref %s", s.ref) + return "ref " + s.ref } func (s *refSecret) Immutable() bool { @@ -1045,7 +1045,7 @@ func NewTLSConfigWithContext(ctx context.Context, cfg *TLSConfig, optFuncs ...TL if cfg.MaxVersion != 0 && cfg.MinVersion != 0 { if cfg.MaxVersion < cfg.MinVersion { - return nil, fmt.Errorf("tls_config.max_version must be greater than or equal to tls_config.min_version if both are specified") + return nil, errors.New("tls_config.max_version must be greater than or equal to tls_config.min_version if both are specified") } } @@ -1144,19 +1144,19 @@ func (c *TLSConfig) UnmarshalYAML(unmarshal func(interface{}) error) error { // used. func (c *TLSConfig) Validate() error { if nonZeroCount(len(c.CA) > 0, len(c.CAFile) > 0, len(c.CARef) > 0) > 1 { - return fmt.Errorf("at most one of ca, ca_file & ca_ref must be configured") + return errors.New("at most one of ca, ca_file & ca_ref must be configured") } if nonZeroCount(len(c.Cert) > 0, len(c.CertFile) > 0, len(c.CertRef) > 0) > 1 { - return fmt.Errorf("at most one of cert, cert_file & cert_ref must be configured") + return errors.New("at most one of cert, cert_file & cert_ref must be configured") } if nonZeroCount(len(c.Key) > 0, len(c.KeyFile) > 0, len(c.KeyRef) > 0) > 1 { - return fmt.Errorf("at most one of key and key_file must be configured") + return errors.New("at most one of key and key_file must be configured") } if c.usingClientCert() && !c.usingClientKey() { - return fmt.Errorf("exactly one of key or key_file must be configured when a client certificate is configured") + return errors.New("exactly one of key or key_file must be configured when a client certificate is configured") } else if c.usingClientKey() && !c.usingClientCert() { - return fmt.Errorf("exactly one of cert or cert_file must be configured when a client key is configured") + return errors.New("exactly one of cert or cert_file must be configured when a client key is configured") } return nil @@ -1460,16 +1460,16 @@ type ProxyConfig struct { // UnmarshalYAML implements the yaml.Unmarshaler interface. func (c *ProxyConfig) Validate() error { if len(c.ProxyConnectHeader) > 0 && (!c.ProxyFromEnvironment && (c.ProxyURL.URL == nil || c.ProxyURL.String() == "")) { - return fmt.Errorf("if proxy_connect_header is configured, proxy_url or proxy_from_environment must also be configured") + return errors.New("if proxy_connect_header is configured, proxy_url or proxy_from_environment must also be configured") } if c.ProxyFromEnvironment && c.ProxyURL.URL != nil && c.ProxyURL.String() != "" { - return fmt.Errorf("if proxy_from_environment is configured, proxy_url must not be configured") + return errors.New("if proxy_from_environment is configured, proxy_url must not be configured") } if c.ProxyFromEnvironment && c.NoProxy != "" { - return fmt.Errorf("if proxy_from_environment is configured, no_proxy must not be configured") + return errors.New("if proxy_from_environment is configured, no_proxy must not be configured") } if c.ProxyURL.URL == nil && c.NoProxy != "" { - return fmt.Errorf("if no_proxy is configured, proxy_url must also be configured") + return errors.New("if no_proxy is configured, proxy_url must also be configured") } return nil } diff --git a/config/http_config_test.go b/config/http_config_test.go index 77382328d..7cf495a37 100644 --- a/config/http_config_test.go +++ b/config/http_config_test.go @@ -609,7 +609,7 @@ func TestNewClientFromInvalidConfig(t *testing.T) { InsecureSkipVerify: true, }, }, - errorMsg: fmt.Sprintf("unable to read CA cert: unable to read file %s", MissingCA), + errorMsg: "unable to read CA cert: unable to read file " + MissingCA, }, { clientConfig: HTTPClientConfig{ @@ -618,7 +618,7 @@ func TestNewClientFromInvalidConfig(t *testing.T) { InsecureSkipVerify: true, }, }, - errorMsg: fmt.Sprintf("unable to use specified CA cert file %s", InvalidCA), + errorMsg: "unable to use specified CA cert file " + InvalidCA, }, } @@ -864,7 +864,7 @@ func TestTLSConfigInvalidCA(t *testing.T) { ServerName: "", InsecureSkipVerify: false, }, - errorMessage: fmt.Sprintf("unable to read CA cert: unable to read file %s", MissingCA), + errorMessage: "unable to read CA cert: unable to read file " + MissingCA, }, { configTLSConfig: TLSConfig{ @@ -874,7 +874,7 @@ func TestTLSConfigInvalidCA(t *testing.T) { ServerName: "", InsecureSkipVerify: false, }, - errorMessage: fmt.Sprintf("unable to read specified client cert: unable to read file %s", MissingCert), + errorMessage: "unable to read specified client cert: unable to read file " + MissingCert, }, { configTLSConfig: TLSConfig{ @@ -884,7 +884,7 @@ func TestTLSConfigInvalidCA(t *testing.T) { ServerName: "", InsecureSkipVerify: false, }, - errorMessage: fmt.Sprintf("unable to read specified client key: unable to read file %s", MissingKey), + errorMessage: "unable to read specified client key: unable to read file " + MissingKey, }, { configTLSConfig: TLSConfig{ @@ -1715,7 +1715,7 @@ func TestOAuth2UserAgent(t *testing.T) { ClientSecret: "2", Scopes: []string{"A", "B"}, EndpointParams: map[string]string{"hi": "hello"}, - TokenURL: fmt.Sprintf("%s/token", ts.URL), + TokenURL: ts.URL + "/token", } rt, err := NewRoundTripperFromConfig(config, "test_oauth2", WithUserAgent("myuseragent")) @@ -2281,7 +2281,7 @@ func TestProxyConfig_Proxy(t *testing.T) { }, { name: "valid proxy_url and localhost", - proxyConfig: fmt.Sprintf(`proxy_url: %s`, proxyServer.URL), + proxyConfig: "proxy_url: " + proxyServer.URL, expectedProxyURL: proxyServer.URL, targetURL: "http://localhost/", }, @@ -2294,7 +2294,7 @@ no_proxy: prometheus.io`, proxyServer.URL), }, { name: "valid proxy_url", - proxyConfig: fmt.Sprintf(`proxy_url: %s`, proxyServer.URL), + proxyConfig: "proxy_url: " + proxyServer.URL, expectedProxyURL: proxyServer.URL, targetURL: "http://prometheus.io/", }, diff --git a/config/tls_config_test.go b/config/tls_config_test.go index 150c56193..829541c35 100644 --- a/config/tls_config_test.go +++ b/config/tls_config_test.go @@ -17,6 +17,7 @@ import ( "bytes" "crypto/tls" "encoding/json" + "errors" "fmt" "os" "path/filepath" @@ -159,7 +160,7 @@ func TestTLSVersionMarshalYAML(t *testing.T) { { input: TLSVersion(999), expected: "", - err: fmt.Errorf("unknown TLS version: 999"), + err: errors.New("unknown TLS version: 999"), }, } @@ -199,7 +200,7 @@ func TestTLSVersionMarshalJSON(t *testing.T) { { input: TLSVersion(999), expected: "", - err: fmt.Errorf("unknown TLS version: 999"), + err: errors.New("unknown TLS version: 999"), }, } diff --git a/expfmt/encode.go b/expfmt/encode.go index cf0c150c2..d7f3d76f5 100644 --- a/expfmt/encode.go +++ b/expfmt/encode.go @@ -68,7 +68,7 @@ func Negotiate(h http.Header) Format { if escapeParam := ac.Params[model.EscapingKey]; escapeParam != "" { switch Format(escapeParam) { case model.AllowUTF8, model.EscapeUnderscores, model.EscapeDots, model.EscapeValues: - escapingScheme = Format(fmt.Sprintf("; escaping=%s", escapeParam)) + escapingScheme = Format("; escaping=" + escapeParam) default: // If the escaping parameter is unknown, ignore it. } @@ -101,7 +101,7 @@ func NegotiateIncludingOpenMetrics(h http.Header) Format { if escapeParam := ac.Params[model.EscapingKey]; escapeParam != "" { switch Format(escapeParam) { case model.AllowUTF8, model.EscapeUnderscores, model.EscapeDots, model.EscapeValues: - escapingScheme = Format(fmt.Sprintf("; escaping=%s", escapeParam)) + escapingScheme = Format("; escaping=" + escapeParam) default: // If the escaping parameter is unknown, ignore it. } diff --git a/expfmt/expfmt.go b/expfmt/expfmt.go index d942af8ed..b26886560 100644 --- a/expfmt/expfmt.go +++ b/expfmt/expfmt.go @@ -15,7 +15,7 @@ package expfmt import ( - "fmt" + "errors" "strings" "github.com/prometheus/common/model" @@ -109,7 +109,7 @@ func NewOpenMetricsFormat(version string) (Format, error) { if version == OpenMetricsVersion_1_0_0 { return FmtOpenMetrics_1_0_0, nil } - return FmtUnknown, fmt.Errorf("unknown open metrics version string") + return FmtUnknown, errors.New("unknown open metrics version string") } // WithEscapingScheme returns a copy of Format with the specified escaping diff --git a/expfmt/openmetrics_create.go b/expfmt/openmetrics_create.go index 11c8ff4b9..f1c495dd6 100644 --- a/expfmt/openmetrics_create.go +++ b/expfmt/openmetrics_create.go @@ -152,8 +152,8 @@ func MetricFamilyToOpenMetrics(out io.Writer, in *dto.MetricFamily, options ...E if metricType == dto.MetricType_COUNTER && strings.HasSuffix(compliantName, "_total") { compliantName = name[:len(name)-6] } - if toOM.withUnit && in.Unit != nil && !strings.HasSuffix(compliantName, fmt.Sprintf("_%s", *in.Unit)) { - compliantName = compliantName + fmt.Sprintf("_%s", *in.Unit) + if toOM.withUnit && in.Unit != nil && !strings.HasSuffix(compliantName, "_"+*in.Unit) { + compliantName = compliantName + "_" + *in.Unit } // Comments, first HELP, then TYPE. diff --git a/expfmt/text_parse.go b/expfmt/text_parse.go index f085a923f..b4607fe4d 100644 --- a/expfmt/text_parse.go +++ b/expfmt/text_parse.go @@ -895,7 +895,7 @@ func histogramMetricName(name string) string { func parseFloat(s string) (float64, error) { if strings.ContainsAny(s, "pP_") { - return 0, fmt.Errorf("unsupported character in float") + return 0, errors.New("unsupported character in float") } return strconv.ParseFloat(s, 64) } diff --git a/model/alert.go b/model/alert.go index 80d1fe944..bd3a39e3e 100644 --- a/model/alert.go +++ b/model/alert.go @@ -14,6 +14,7 @@ package model import ( + "errors" "fmt" "time" ) @@ -89,16 +90,16 @@ func (a *Alert) StatusAt(ts time.Time) AlertStatus { // Validate checks whether the alert data is inconsistent. func (a *Alert) Validate() error { if a.StartsAt.IsZero() { - return fmt.Errorf("start time missing") + return errors.New("start time missing") } if !a.EndsAt.IsZero() && a.EndsAt.Before(a.StartsAt) { - return fmt.Errorf("start time must be before end time") + return errors.New("start time must be before end time") } if err := a.Labels.Validate(); err != nil { return fmt.Errorf("invalid label set: %w", err) } if len(a.Labels) == 0 { - return fmt.Errorf("at least one label pair required") + return errors.New("at least one label pair required") } if err := a.Annotations.Validate(); err != nil { return fmt.Errorf("invalid annotations: %w", err) diff --git a/model/metric.go b/model/metric.go index f50966bc4..12593c7ca 100644 --- a/model/metric.go +++ b/model/metric.go @@ -14,6 +14,7 @@ package model import ( + "errors" "fmt" "regexp" "sort" @@ -443,7 +444,7 @@ func (e EscapingScheme) String() string { func ToEscapingScheme(s string) (EscapingScheme, error) { if s == "" { - return NoEscaping, fmt.Errorf("got empty string instead of escaping scheme") + return NoEscaping, errors.New("got empty string instead of escaping scheme") } switch s { case AllowUTF8: diff --git a/model/silence.go b/model/silence.go index 910b0b71f..8f91a9702 100644 --- a/model/silence.go +++ b/model/silence.go @@ -15,6 +15,7 @@ package model import ( "encoding/json" + "errors" "fmt" "regexp" "time" @@ -34,7 +35,7 @@ func (m *Matcher) UnmarshalJSON(b []byte) error { } if len(m.Name) == 0 { - return fmt.Errorf("label name in matcher must not be empty") + return errors.New("label name in matcher must not be empty") } if m.IsRegex { if _, err := regexp.Compile(m.Value); err != nil { @@ -77,7 +78,7 @@ type Silence struct { // Validate returns true iff all fields of the silence have valid values. func (s *Silence) Validate() error { if len(s.Matchers) == 0 { - return fmt.Errorf("at least one matcher required") + return errors.New("at least one matcher required") } for _, m := range s.Matchers { if err := m.Validate(); err != nil { @@ -85,22 +86,22 @@ func (s *Silence) Validate() error { } } if s.StartsAt.IsZero() { - return fmt.Errorf("start time missing") + return errors.New("start time missing") } if s.EndsAt.IsZero() { - return fmt.Errorf("end time missing") + return errors.New("end time missing") } if s.EndsAt.Before(s.StartsAt) { - return fmt.Errorf("start time must be before end time") + return errors.New("start time must be before end time") } if s.CreatedBy == "" { - return fmt.Errorf("creator information missing") + return errors.New("creator information missing") } if s.Comment == "" { - return fmt.Errorf("comment missing") + return errors.New("comment missing") } if s.CreatedAt.IsZero() { - return fmt.Errorf("creation timestamp missing") + return errors.New("creation timestamp missing") } return nil } diff --git a/model/value_float.go b/model/value_float.go index ae35cc2ab..6bfc757d1 100644 --- a/model/value_float.go +++ b/model/value_float.go @@ -15,6 +15,7 @@ package model import ( "encoding/json" + "errors" "fmt" "math" "strconv" @@ -39,7 +40,7 @@ func (v SampleValue) MarshalJSON() ([]byte, error) { // UnmarshalJSON implements json.Unmarshaler. func (v *SampleValue) UnmarshalJSON(b []byte) error { if len(b) < 2 || b[0] != '"' || b[len(b)-1] != '"' { - return fmt.Errorf("sample value must be a quoted string") + return errors.New("sample value must be a quoted string") } f, err := strconv.ParseFloat(string(b[1:len(b)-1]), 64) if err != nil { diff --git a/model/value_histogram.go b/model/value_histogram.go index 54bb038cf..895e6a3e8 100644 --- a/model/value_histogram.go +++ b/model/value_histogram.go @@ -15,6 +15,7 @@ package model import ( "encoding/json" + "errors" "fmt" "strconv" "strings" @@ -32,7 +33,7 @@ func (v FloatString) MarshalJSON() ([]byte, error) { func (v *FloatString) UnmarshalJSON(b []byte) error { if len(b) < 2 || b[0] != '"' || b[len(b)-1] != '"' { - return fmt.Errorf("float value must be a quoted string") + return errors.New("float value must be a quoted string") } f, err := strconv.ParseFloat(string(b[1:len(b)-1]), 64) if err != nil { @@ -141,7 +142,7 @@ type SampleHistogramPair struct { func (s SampleHistogramPair) MarshalJSON() ([]byte, error) { if s.Histogram == nil { - return nil, fmt.Errorf("histogram is nil") + return nil, errors.New("histogram is nil") } t, err := json.Marshal(s.Timestamp) if err != nil { @@ -164,7 +165,7 @@ func (s *SampleHistogramPair) UnmarshalJSON(buf []byte) error { return fmt.Errorf("wrong number of fields: %d != %d", gotLen, wantLen) } if s.Histogram == nil { - return fmt.Errorf("histogram is null") + return errors.New("histogram is null") } return nil } diff --git a/sigv4/sigv4.go b/sigv4/sigv4.go index 979fd8466..17cc9139a 100644 --- a/sigv4/sigv4.go +++ b/sigv4/sigv4.go @@ -16,6 +16,7 @@ package sigv4 import ( "bytes" + "errors" "fmt" "io" "net/http" @@ -82,7 +83,7 @@ func NewSigV4RoundTripper(cfg *SigV4Config, next http.RoundTripper) (http.RoundT return nil, fmt.Errorf("could not get SigV4 credentials: %w", err) } if aws.StringValue(sess.Config.Region) == "" { - return nil, fmt.Errorf("region not configured in sigv4 or in default credentials chain") + return nil, errors.New("region not configured in sigv4 or in default credentials chain") } signerCreds := sess.Config.Credentials diff --git a/sigv4/sigv4_config.go b/sigv4/sigv4_config.go index 83ef73d89..2f5e7572a 100644 --- a/sigv4/sigv4_config.go +++ b/sigv4/sigv4_config.go @@ -14,7 +14,7 @@ package sigv4 import ( - "fmt" + "errors" "github.com/prometheus/common/config" ) @@ -33,7 +33,7 @@ type SigV4Config struct { func (c *SigV4Config) Validate() error { if (c.AccessKey == "") != (c.SecretKey == "") { - return fmt.Errorf("must provide a AWS SigV4 Access key and Secret Key if credentials are specified in the SigV4 config") + return errors.New("must provide a AWS SigV4 Access key and Secret Key if credentials are specified in the SigV4 config") } return nil } From ab952099d385d3509f9a32f275fce484560f3c63 Mon Sep 17 00:00:00 2001 From: Matthieu MOREL Date: Thu, 7 Nov 2024 21:06:31 +0100 Subject: [PATCH 06/48] chore: use testify instead of testing.Fatal (#718) Signed-off-by: Matthieu MOREL --- .golangci.yml | 17 +- assets/embed_gzip_test.go | 22 +- assets/go.mod | 8 + assets/go.sum | 9 + config/config_test.go | 45 +-- config/http_config_test.go | 533 ++++++++---------------------- config/tls_config_test.go | 22 +- expfmt/bench_test.go | 39 +-- expfmt/decode_test.go | 41 +-- expfmt/openmetrics_create_test.go | 5 +- expfmt/text_create_test.go | 9 +- model/signature_test.go | 22 +- model/time_test.go | 63 +--- model/value_float_test.go | 6 +- model/value_histogram_test.go | 6 +- model/value_test.go | 10 +- promlog/log_test.go | 52 ++- promslog/slog_test.go | 18 +- route/route_test.go | 66 +--- server/static_file_server_test.go | 10 +- sigv4/sigv4_config_test.go | 14 +- 21 files changed, 305 insertions(+), 712 deletions(-) create mode 100644 assets/go.sum diff --git a/.golangci.yml b/.golangci.yml index b94def309..e2f3e9459 100644 --- a/.golangci.yml +++ b/.golangci.yml @@ -38,16 +38,9 @@ linters-settings: disabled: true testifylint: disable: - - float-compare - go-require - enable: - - bool-compare - - compares - - empty - - error-is-as - - error-nil - - expected-actual - - len - - require-error - - suite-dont-use-pkg - - suite-extra-assert-call + enable-all: true + formatter: + require-f-funcs: true +run: + timeout: 5m \ No newline at end of file diff --git a/assets/embed_gzip_test.go b/assets/embed_gzip_test.go index ce866475a..656c49476 100644 --- a/assets/embed_gzip_test.go +++ b/assets/embed_gzip_test.go @@ -18,6 +18,8 @@ import ( "io" "strings" "testing" + + "github.com/stretchr/testify/require" ) //go:embed testdata @@ -62,19 +64,13 @@ func TestFS(t *testing.T) { for _, c := range cases { t.Run(c.name, func(t *testing.T) { f, err := testFS.Open(c.path) - if err != nil { - t.Fatal(err) - } + require.NoError(t, err) stat, err := f.Stat() - if err != nil { - t.Fatal(err) - } + require.NoError(t, err) size := stat.Size() - if size != c.expectedSize { - t.Fatalf("size is wrong, expected %d, got %d", c.expectedSize, size) - } + require.Equalf(t, c.expectedSize, size, "size is wrong, expected %d, got %d", c.expectedSize, size) if strings.HasSuffix(c.path, ".gz") { // don't read the comressed content @@ -82,12 +78,8 @@ func TestFS(t *testing.T) { } content, err := io.ReadAll(f) - if err != nil { - t.Fatal(err) - } - if string(content) != c.expectedContent { - t.Fatalf("content is wrong, expected %s, got %s", c.expectedContent, string(content)) - } + require.NoError(t, err) + require.Equalf(t, c.expectedContent, string(content), "content is wrong, expected %s, got %s", c.expectedContent, string(content)) }) } } diff --git a/assets/go.mod b/assets/go.mod index 4f73955c5..ec1105bff 100644 --- a/assets/go.mod +++ b/assets/go.mod @@ -1,3 +1,11 @@ module github.com/prometheus/common/assets go 1.21 + +require github.com/stretchr/testify v1.9.0 + +require ( + github.com/davecgh/go-spew v1.1.1 // indirect + github.com/pmezard/go-difflib v1.0.0 // indirect + gopkg.in/yaml.v3 v3.0.1 // indirect +) diff --git a/assets/go.sum b/assets/go.sum new file mode 100644 index 000000000..e20fa14b0 --- /dev/null +++ b/assets/go.sum @@ -0,0 +1,9 @@ +github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= +github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= +github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= +github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= +github.com/stretchr/testify v1.9.0 h1:HtqpIVDClZ4nwg75+f6Lvsy/wHu+3BoSGCbBAcpTsTg= +github.com/stretchr/testify v1.9.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY= +gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= +gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= +gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= diff --git a/config/config_test.go b/config/config_test.go index 9486ba1ad..f62b266af 100644 --- a/config/config_test.go +++ b/config/config_test.go @@ -20,6 +20,7 @@ import ( "reflect" "testing" + "github.com/stretchr/testify/require" "gopkg.in/yaml.v2" ) @@ -71,12 +72,8 @@ func TestJSONMarshalSecret(t *testing.T) { marshalFN = json.Marshal } c, err := marshalFN(tc.data) - if err != nil { - t.Fatal(err) - } - if tc.expected != string(c) { - t.Fatalf("Secret not marshaled correctly, got '%s'", string(c)) - } + require.NoError(t, err) + require.Equalf(t, tc.expected, string(c), "Secret not marshaled correctly, got '%s'", string(c)) }) } } @@ -109,9 +106,7 @@ func TestHeaderHTTPHeader(t *testing.T) { for name, tc := range testcases { t.Run(name, func(t *testing.T) { actual := tc.header.HTTPHeader() - if !reflect.DeepEqual(actual, tc.expected) { - t.Fatalf("expecting: %#v, actual: %#v", tc.expected, actual) - } + require.Truef(t, reflect.DeepEqual(actual, tc.expected), "expecting: %#v, actual: %#v", tc.expected, actual) }) } } @@ -146,12 +141,8 @@ func TestHeaderYamlUnmarshal(t *testing.T) { t.Run(name, func(t *testing.T) { var actual ProxyHeader err := yaml.Unmarshal([]byte(tc.input), &actual) - if err != nil { - t.Fatalf("error unmarshaling %s: %s", tc.input, err) - } - if !reflect.DeepEqual(actual, tc.expected) { - t.Fatalf("expecting: %#v, actual: %#v", tc.expected, actual) - } + require.NoErrorf(t, err, "error unmarshaling %s: %s", tc.input, err) + require.Truef(t, reflect.DeepEqual(actual, tc.expected), "expecting: %#v, actual: %#v", tc.expected, actual) }) } } @@ -182,12 +173,8 @@ func TestHeaderYamlMarshal(t *testing.T) { for name, tc := range testcases { t.Run(name, func(t *testing.T) { actual, err := yaml.Marshal(tc.input) - if err != nil { - t.Fatalf("error unmarshaling %#v: %s", tc.input, err) - } - if !bytes.Equal(actual, tc.expected) { - t.Fatalf("expecting: %q, actual: %q", tc.expected, actual) - } + require.NoErrorf(t, err, "error unmarshaling %#v: %s", tc.input, err) + require.Truef(t, bytes.Equal(actual, tc.expected), "expecting: %q, actual: %q", tc.expected, actual) }) } } @@ -222,12 +209,8 @@ func TestHeaderJsonUnmarshal(t *testing.T) { t.Run(name, func(t *testing.T) { var actual ProxyHeader err := json.Unmarshal([]byte(tc.input), &actual) - if err != nil { - t.Fatalf("error unmarshaling %s: %s", tc.input, err) - } - if !reflect.DeepEqual(actual, tc.expected) { - t.Fatalf("expecting: %#v, actual: %#v", tc.expected, actual) - } + require.NoErrorf(t, err, "error unmarshaling %s: %s", tc.input, err) + require.Truef(t, reflect.DeepEqual(actual, tc.expected), "expecting: %#v, actual: %#v", tc.expected, actual) }) } } @@ -258,12 +241,8 @@ func TestHeaderJsonMarshal(t *testing.T) { for name, tc := range testcases { t.Run(name, func(t *testing.T) { actual, err := json.Marshal(tc.input) - if err != nil { - t.Fatalf("error marshaling %#v: %s", tc.input, err) - } - if !bytes.Equal(actual, tc.expected) { - t.Fatalf("expecting: %q, actual: %q", tc.expected, actual) - } + require.NoErrorf(t, err, "error marshaling %#v: %s", tc.input, err) + require.Truef(t, bytes.Equal(actual, tc.expected), "expecting: %q, actual: %q", tc.expected, actual) }) } } diff --git a/config/http_config_test.go b/config/http_config_test.go index 7cf495a37..58d13b0dc 100644 --- a/config/http_config_test.go +++ b/config/http_config_test.go @@ -510,9 +510,7 @@ func TestNewClientFromConfig(t *testing.T) { for _, validConfig := range newClientValidConfig { t.Run("", func(t *testing.T) { testServer, err := newTestServer(validConfig.handler) - if err != nil { - t.Fatal(err.Error()) - } + require.NoError(t, err) defer testServer.Close() if validConfig.clientConfig.OAuth2 != nil { @@ -522,9 +520,7 @@ func TestNewClientFromConfig(t *testing.T) { } err = validConfig.clientConfig.Validate() - if err != nil { - t.Fatal(err.Error()) - } + require.NoError(t, err) client, err := NewClientFromConfig(validConfig.clientConfig, "test") if err != nil { t.Errorf("Can't create a client from this config: %+v", validConfig.clientConfig) @@ -585,13 +581,9 @@ func TestProxyConfiguration(t *testing.T) { t.Run(name, func(t *testing.T) { _, _, err := tc.loader(tc.testFn) if tc.isValid { - if err != nil { - t.Fatalf("Error validating %s: %s", tc.testFn, err) - } + require.NoErrorf(t, err, "Error validating %s: %s", tc.testFn, err) } else { - if err == nil { - t.Fatalf("Expecting error validating %s but got %s", tc.testFn, err) - } + require.Errorf(t, err, "Expecting error validating %s but got %s", tc.testFn, err) } }) } @@ -643,9 +635,7 @@ func TestCustomDialContextFunc(t *testing.T) { cfg := HTTPClientConfig{} client, err := NewClientFromConfig(cfg, "test", WithDialContextFunc(dialFn)) - if err != nil { - t.Fatalf("Can't create a client from this config: %+v", cfg) - } + require.NoErrorf(t, err, "Can't create a client from this config: %+v", cfg) _, err = client.Get("http://localhost") if err == nil || !strings.Contains(err.Error(), ExpectedError) { @@ -658,18 +648,12 @@ func TestCustomIdleConnTimeout(t *testing.T) { cfg := HTTPClientConfig{} rt, err := NewRoundTripperFromConfig(cfg, "test", WithIdleConnTimeout(timeout)) - if err != nil { - t.Fatalf("Can't create a round-tripper from this config: %+v", cfg) - } + require.NoErrorf(t, err, "Can't create a round-tripper from this config: %+v", cfg) transport, ok := rt.(*http.Transport) - if !ok { - t.Fatalf("Unexpected transport: %+v", transport) - } + require.Truef(t, ok, "Unexpected transport: %+v", transport) - if transport.IdleConnTimeout != timeout { - t.Fatalf("Unexpected idle connection timeout: %+v", timeout) - } + require.Equalf(t, transport.IdleConnTimeout, timeout, "Unexpected idle connection timeout: %+v", timeout) } func TestMissingBearerAuthFile(t *testing.T) { @@ -694,24 +678,16 @@ func TestMissingBearerAuthFile(t *testing.T) { } testServer, err := newTestServer(handler) - if err != nil { - t.Fatal(err.Error()) - } + require.NoError(t, err) defer testServer.Close() client, err := NewClientFromConfig(cfg, "test") - if err != nil { - t.Fatal(err) - } + require.NoError(t, err) _, err = client.Get(testServer.URL) - if err == nil { - t.Fatal("No error is returned here") - } + require.Errorf(t, err, "No error is returned here") - if !strings.Contains(err.Error(), "unable to read authorization credentials: unable to read file missing/bearer.token: open missing/bearer.token: no such file or directory") { - t.Fatal("wrong error message being returned") - } + require.ErrorContainsf(t, err, "unable to read authorization credentials: unable to read file missing/bearer.token: open missing/bearer.token: no such file or directory", "wrong error message being returned") } func TestBearerAuthRoundTripper(t *testing.T) { @@ -784,10 +760,8 @@ func TestTLSConfig(t *testing.T) { } tlsCAChain, err := os.ReadFile(TLSCAChainPath) - if err != nil { - t.Fatalf("Can't read the CA certificate chain (%s)", - TLSCAChainPath) - } + require.NoErrorf(t, err, "Can't read the CA certificate chain (%s)", + TLSCAChainPath) rootCAs := x509.NewCertPool() rootCAs.AppendCertsFromPEM(tlsCAChain) @@ -798,38 +772,26 @@ func TestTLSConfig(t *testing.T) { } tlsConfig, err := NewTLSConfig(&configTLSConfig) - if err != nil { - t.Fatalf("Can't create a new TLS Config from a configuration (%s).", err) - } + require.NoErrorf(t, err, "Can't create a new TLS Config from a configuration (%s).", err) clientCertificate, err := tls.LoadX509KeyPair(ClientCertificatePath, ClientKeyNoPassPath) - if err != nil { - t.Fatalf("Can't load the client key pair ('%s' and '%s'). Reason: %s", - ClientCertificatePath, ClientKeyNoPassPath, err) - } + require.NoErrorf(t, err, "Can't load the client key pair ('%s' and '%s'). Reason: %s", + ClientCertificatePath, ClientKeyNoPassPath, err) cert, err := tlsConfig.GetClientCertificate(nil) - if err != nil { - t.Fatalf("unexpected error returned by tlsConfig.GetClientCertificate(): %s", err) - } - if !reflect.DeepEqual(cert, &clientCertificate) { - t.Fatalf("Unexpected client certificate result: \n\n%+v\n expected\n\n%+v", cert, clientCertificate) - } + require.NoErrorf(t, err, "unexpected error returned by tlsConfig.GetClientCertificate(): %s", err) + require.Truef(t, reflect.DeepEqual(cert, &clientCertificate), "Unexpected client certificate result: \n\n%+v\n expected\n\n%+v", cert, clientCertificate) // tlsConfig.rootCAs.LazyCerts contains functions getCert() in go 1.16, which are // never equal. Compare the Subjects instead. //nolint:staticcheck // Ignore SA1019. (*CertPool).Subjects is deprecated because it may not include the system certs but it isn't the case here. - if !reflect.DeepEqual(tlsConfig.RootCAs.Subjects(), expectedTLSConfig.RootCAs.Subjects()) { - t.Fatalf("Unexpected RootCAs result: \n\n%+v\n expected\n\n%+v", tlsConfig.RootCAs.Subjects(), expectedTLSConfig.RootCAs.Subjects()) - } + require.Truef(t, reflect.DeepEqual(tlsConfig.RootCAs.Subjects(), expectedTLSConfig.RootCAs.Subjects()), "Unexpected RootCAs result: \n\n%+v\n expected\n\n%+v", tlsConfig.RootCAs.Subjects(), expectedTLSConfig.RootCAs.Subjects()) tlsConfig.RootCAs = nil expectedTLSConfig.RootCAs = nil // Non-nil functions are never equal. tlsConfig.GetClientCertificate = nil - if !reflect.DeepEqual(tlsConfig, expectedTLSConfig) { - t.Fatalf("Unexpected TLS Config result: \n\n%+v\n expected\n\n%+v", tlsConfig, expectedTLSConfig) - } + require.Truef(t, reflect.DeepEqual(tlsConfig, expectedTLSConfig), "Unexpected TLS Config result: \n\n%+v\n expected\n\n%+v", tlsConfig, expectedTLSConfig) } func TestTLSConfigEmpty(t *testing.T) { @@ -842,13 +804,9 @@ func TestTLSConfigEmpty(t *testing.T) { } tlsConfig, err := NewTLSConfig(&configTLSConfig) - if err != nil { - t.Fatalf("Can't create a new TLS Config from a configuration (%s).", err) - } + require.NoErrorf(t, err, "Can't create a new TLS Config from a configuration (%s).", err) - if !reflect.DeepEqual(tlsConfig, expectedTLSConfig) { - t.Fatalf("Unexpected TLS Config result: \n\n%+v\n expected\n\n%+v", tlsConfig, expectedTLSConfig) - } + require.Truef(t, reflect.DeepEqual(tlsConfig, expectedTLSConfig), "Unexpected TLS Config result: \n\n%+v\n expected\n\n%+v", tlsConfig, expectedTLSConfig) } func TestTLSConfigInvalidCA(t *testing.T) { @@ -924,41 +882,26 @@ func TestTLSConfigInvalidCA(t *testing.T) { func TestBasicAuthNoPassword(t *testing.T) { cfg, _, err := LoadHTTPConfigFile("testdata/http.conf.basic-auth.no-password.yaml") - if err != nil { - t.Fatalf("Error loading HTTP client config: %v", err) - } + require.NoErrorf(t, err, "Error loading HTTP client config: %v", err) client, err := NewClientFromConfig(*cfg, "test") - if err != nil { - t.Fatalf("Error creating HTTP Client: %v", err) - } + require.NoErrorf(t, err, "Error creating HTTP Client: %v", err) rt, ok := client.Transport.(*basicAuthRoundTripper) - if !ok { - t.Fatalf("Error casting to basic auth transport, %v", client.Transport) - } + require.Truef(t, ok, "Error casting to basic auth transport, %v", client.Transport) - if username, _ := rt.username.Fetch(context.Background()); username != "user" { - t.Errorf("Bad HTTP client username: %s", username) - } - if rt.password != nil { - t.Errorf("Expected empty HTTP client password") - } + username, _ := rt.username.Fetch(context.Background()) + require.Equalf(t, "user", username, "Bad HTTP client username: %s", username) + require.Nilf(t, rt.password, "Expected empty HTTP client password") } func TestBasicAuthNoUsername(t *testing.T) { cfg, _, err := LoadHTTPConfigFile("testdata/http.conf.basic-auth.no-username.yaml") - if err != nil { - t.Fatalf("Error loading HTTP client config: %v", err) - } + require.NoErrorf(t, err, "Error loading HTTP client config: %v", err) client, err := NewClientFromConfig(*cfg, "test") - if err != nil { - t.Fatalf("Error creating HTTP Client: %v", err) - } + require.NoErrorf(t, err, "Error creating HTTP Client: %v", err) rt, ok := client.Transport.(*basicAuthRoundTripper) - if !ok { - t.Fatalf("Error casting to basic auth transport, %v", client.Transport) - } + require.Truef(t, ok, "Error casting to basic auth transport, %v", client.Transport) if rt.username != nil { t.Errorf("Got unexpected username") @@ -970,18 +913,12 @@ func TestBasicAuthNoUsername(t *testing.T) { func TestBasicAuthPasswordFile(t *testing.T) { cfg, _, err := LoadHTTPConfigFile("testdata/http.conf.basic-auth.good.yaml") - if err != nil { - t.Fatalf("Error loading HTTP client config: %v", err) - } + require.NoErrorf(t, err, "Error loading HTTP client config: %v", err) client, err := NewClientFromConfig(*cfg, "test") - if err != nil { - t.Fatalf("Error creating HTTP Client: %v", err) - } + require.NoErrorf(t, err, "Error creating HTTP Client: %v", err) rt, ok := client.Transport.(*basicAuthRoundTripper) - if !ok { - t.Fatalf("Error casting to basic auth transport, %v", client.Transport) - } + require.Truef(t, ok, "Error casting to basic auth transport, %v", client.Transport) if username, _ := rt.username.Fetch(context.Background()); username != "user" { t.Errorf("Bad HTTP client username: %s", username) @@ -1005,9 +942,7 @@ func (m *secretManager) Fetch(ctx context.Context, secretRef string) (string, er func TestBasicAuthSecretManager(t *testing.T) { cfg, _, err := LoadHTTPConfigFile("testdata/http.conf.basic-auth.ref.yaml") - if err != nil { - t.Fatalf("Error loading HTTP client config: %v", err) - } + require.NoErrorf(t, err, "Error loading HTTP client config: %v", err) manager := secretManager{ data: map[string]string{ "admin": "user", @@ -1015,14 +950,10 @@ func TestBasicAuthSecretManager(t *testing.T) { }, } client, err := NewClientFromConfig(*cfg, "test", WithSecretManager(&manager)) - if err != nil { - t.Fatalf("Error creating HTTP Client: %v", err) - } + require.NoErrorf(t, err, "Error creating HTTP Client: %v", err) rt, ok := client.Transport.(*basicAuthRoundTripper) - if !ok { - t.Fatalf("Error casting to basic auth transport, %v", client.Transport) - } + require.Truef(t, ok, "Error casting to basic auth transport, %v", client.Transport) if username, _ := rt.username.Fetch(context.Background()); username != "user" { t.Errorf("Bad HTTP client username: %s", username) @@ -1034,9 +965,7 @@ func TestBasicAuthSecretManager(t *testing.T) { func TestBasicAuthSecretManagerNotFound(t *testing.T) { cfg, _, err := LoadHTTPConfigFile("testdata/http.conf.basic-auth.ref.yaml") - if err != nil { - t.Fatalf("Error loading HTTP client config: %v", err) - } + require.NoErrorf(t, err, "Error loading HTTP client config: %v", err) manager := secretManager{ data: map[string]string{ "admin1": "user", @@ -1044,14 +973,10 @@ func TestBasicAuthSecretManagerNotFound(t *testing.T) { }, } client, err := NewClientFromConfig(*cfg, "test", WithSecretManager(&manager)) - if err != nil { - t.Fatalf("Error creating HTTP Client: %v", err) - } + require.NoErrorf(t, err, "Error creating HTTP Client: %v", err) rt, ok := client.Transport.(*basicAuthRoundTripper) - if !ok { - t.Fatalf("Error casting to basic auth transport, %v", client.Transport) - } + require.Truef(t, ok, "Error casting to basic auth transport, %v", client.Transport) if _, err := rt.username.Fetch(context.Background()); !strings.Contains(err.Error(), "unknown secret admin") { t.Errorf("Unexpected error message: %s", err) @@ -1063,18 +988,12 @@ func TestBasicAuthSecretManagerNotFound(t *testing.T) { func TestBasicUsernameFile(t *testing.T) { cfg, _, err := LoadHTTPConfigFile("testdata/http.conf.basic-auth.username-file.good.yaml") - if err != nil { - t.Fatalf("Error loading HTTP client config: %v", err) - } + require.NoErrorf(t, err, "Error loading HTTP client config: %v", err) client, err := NewClientFromConfig(*cfg, "test") - if err != nil { - t.Fatalf("Error creating HTTP Client: %v", err) - } + require.NoErrorf(t, err, "Error creating HTTP Client: %v", err) rt, ok := client.Transport.(*basicAuthRoundTripper) - if !ok { - t.Fatalf("Error casting to basic auth transport, %v", client.Transport) - } + require.Truef(t, ok, "Error casting to basic auth transport, %v", client.Transport) if username, _ := rt.username.Fetch(context.Background()); username != "testuser" { t.Errorf("Bad HTTP client username: %s", username) @@ -1098,9 +1017,7 @@ func getCertificateBlobs(t *testing.T) map[string][]byte { bs := make(map[string][]byte, len(files)+1) for _, f := range files { b, err := os.ReadFile(f) - if err != nil { - t.Fatal(err) - } + require.NoError(t, err) bs[f] = b } @@ -1121,9 +1038,7 @@ func TestTLSRoundTripper(t *testing.T) { bs := getCertificateBlobs(t) tmpDir, err := os.MkdirTemp("", "tlsroundtripper") - if err != nil { - t.Fatal("Failed to create tmp dir", err) - } + require.NoErrorf(t, err, "Failed to create tmp dir") defer os.RemoveAll(tmpDir) ca, cert, key := filepath.Join(tmpDir, "ca"), filepath.Join(tmpDir, "cert"), filepath.Join(tmpDir, "key") @@ -1132,9 +1047,7 @@ func TestTLSRoundTripper(t *testing.T) { fmt.Fprint(w, ExpectedMessage) } testServer, err := newTestServer(handler) - if err != nil { - t.Fatal(err.Error()) - } + require.NoError(t, err) defer testServer.Close() testCases := []struct { @@ -1216,30 +1129,22 @@ func TestTLSRoundTripper(t *testing.T) { writeCertificate(bs, tc.key, key) if c == nil { c, err = NewClientFromConfig(cfg, "test") - if err != nil { - t.Fatalf("Error creating HTTP Client: %v", err) - } + require.NoErrorf(t, err, "Error creating HTTP Client: %v", err) } req, err := http.NewRequest(http.MethodGet, testServer.URL, nil) - if err != nil { - t.Fatalf("Error creating HTTP request: %v", err) - } + require.NoErrorf(t, err, "Error creating HTTP request: %v", err) r, err := c.Do(req) if len(tc.errMsg) > 0 { if err == nil { r.Body.Close() t.Fatalf("Could connect to the test server.") } - if !strings.Contains(err.Error(), tc.errMsg) { - t.Fatalf("Expected error message to contain %q, got %q", tc.errMsg, err) - } + require.ErrorContainsf(t, err, tc.errMsg, "Expected error message to contain %q, got %q", tc.errMsg, err) return } - if err != nil { - t.Fatalf("Can't connect to the test server") - } + require.NoErrorf(t, err, "Can't connect to the test server") b, err := io.ReadAll(r.Body) r.Body.Close() @@ -1260,9 +1165,7 @@ func TestTLSRoundTripper_Inline(t *testing.T) { fmt.Fprint(w, ExpectedMessage) } testServer, err := newTestServer(handler) - if err != nil { - t.Fatal(err.Error()) - } + require.NoError(t, err) defer testServer.Close() testCases := []struct { @@ -1346,22 +1249,16 @@ func TestTLSRoundTripper_Inline(t *testing.T) { c, err := NewClientFromConfig(cfg, "test") if tc.errMsg != "" { - if !strings.Contains(err.Error(), tc.errMsg) { - t.Fatalf("Expected error message to contain %q, got %q", tc.errMsg, err) - } + require.ErrorContainsf(t, err, tc.errMsg, "Expected error message to contain %q, got %q", tc.errMsg, err) return } else if err != nil { t.Fatalf("Error creating HTTP Client: %v", err) } req, err := http.NewRequest(http.MethodGet, testServer.URL, nil) - if err != nil { - t.Fatalf("Error creating HTTP request: %v", err) - } + require.NoErrorf(t, err, "Error creating HTTP request: %v", err) r, err := c.Do(req) - if err != nil { - t.Fatalf("Can't connect to the test server") - } + require.NoErrorf(t, err, "Can't connect to the test server") b, err := io.ReadAll(r.Body) r.Body.Close() @@ -1381,9 +1278,7 @@ func TestTLSRoundTripperRaces(t *testing.T) { bs := getCertificateBlobs(t) tmpDir, err := os.MkdirTemp("", "tlsroundtripper") - if err != nil { - t.Fatal("Failed to create tmp dir", err) - } + require.NoErrorf(t, err, "Failed to create tmp dir") defer os.RemoveAll(tmpDir) ca, cert, key := filepath.Join(tmpDir, "ca"), filepath.Join(tmpDir, "cert"), filepath.Join(tmpDir, "key") @@ -1392,9 +1287,7 @@ func TestTLSRoundTripperRaces(t *testing.T) { fmt.Fprint(w, ExpectedMessage) } testServer, err := newTestServer(handler) - if err != nil { - t.Fatal(err.Error()) - } + require.NoError(t, err) defer testServer.Close() cfg := HTTPClientConfig{ @@ -1411,9 +1304,7 @@ func TestTLSRoundTripperRaces(t *testing.T) { writeCertificate(bs, ClientCertificatePath, cert) writeCertificate(bs, ClientKeyNoPassPath, key) c, err = NewClientFromConfig(cfg, "test") - if err != nil { - t.Fatalf("Error creating HTTP Client: %v", err) - } + require.NoErrorf(t, err, "Error creating HTTP Client: %v", err) var wg sync.WaitGroup ch := make(chan struct{}) @@ -1461,22 +1352,16 @@ func TestTLSRoundTripperRaces(t *testing.T) { }() wg.Wait() - if ok == total { - t.Fatalf("Expecting some requests to fail but got %d/%d successful requests", ok, total) - } + require.NotEqualf(t, ok, total, "Expecting some requests to fail but got %d/%d successful requests", ok, total) } func TestHideHTTPClientConfigSecrets(t *testing.T) { c, _, err := LoadHTTPConfigFile("testdata/http.conf.good.yml") - if err != nil { - t.Fatalf("Error parsing %s: %s", "testdata/http.conf.good.yml", err) - } + require.NoErrorf(t, err, "Error parsing %s: %s", "testdata/http.conf.good.yml", err) // String method must not reveal authentication credentials. s := c.String() - if strings.Contains(s, "mysecret") { - t.Fatal("http client config's String method reveals authentication credentials.") - } + require.NotContainsf(t, s, "mysecret", "http client config's String method reveals authentication credentials.") } func TestDefaultFollowRedirect(t *testing.T) { @@ -1495,9 +1380,7 @@ func TestValidateHTTPConfig(t *testing.T) { t.Errorf("Error loading HTTP client config: %v", err) } err = cfg.Validate() - if err != nil { - t.Fatalf("Error validating %s: %s", "testdata/http.conf.good.yml", err) - } + require.NoErrorf(t, err, "Error validating %s: %s", "testdata/http.conf.good.yml", err) } func TestInvalidHTTPConfigs(t *testing.T) { @@ -1559,12 +1442,8 @@ func newTestOAuthServer(t testing.TB, expectedAuth *string) testOAuthServer { var previousAuth string tokenTS := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { auth := r.Header.Get("Authorization") - if auth != *expectedAuth { - t.Fatalf("bad auth, expected %s, got %s", *expectedAuth, auth) - } - if auth == previousAuth { - t.Fatal("token endpoint called twice") - } + require.Equalf(t, *expectedAuth, auth, "bad auth, expected %s, got %s", *expectedAuth, auth) + require.NotEqualf(t, auth, previousAuth, "token endpoint called twice") previousAuth = auth res, _ := json.Marshal(oauth2TestServerResponse{ AccessToken: "12345", @@ -1575,9 +1454,7 @@ func newTestOAuthServer(t testing.TB, expectedAuth *string) testOAuthServer { })) ts := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { auth := r.Header.Get("Authorization") - if auth != "Bearer 12345" { - t.Fatalf("bad auth, expected %s, got %s", "Bearer 12345", auth) - } + require.Equalf(t, "Bearer 12345", auth, "bad auth, expected %s, got %s", "Bearer 12345", auth) fmt.Fprintln(w, "Hello, client") })) return testOAuthServer{ @@ -1623,12 +1500,9 @@ endpoint_params: } var unmarshalledConfig OAuth2 - if err := yaml.Unmarshal([]byte(yamlConfig), &unmarshalledConfig); err != nil { - t.Fatalf("Expected no error unmarshalling yaml, got %v", err) - } - if !reflect.DeepEqual(unmarshalledConfig, expectedConfig) { - t.Fatalf("Got unmarshalled config %v, expected %v", unmarshalledConfig, expectedConfig) - } + err := yaml.Unmarshal([]byte(yamlConfig), &unmarshalledConfig) + require.NoErrorf(t, err, "Expected no error unmarshalling yaml, got %v", err) + require.Truef(t, reflect.DeepEqual(unmarshalledConfig, expectedConfig), "Got unmarshalled config %v, expected %v", unmarshalledConfig, expectedConfig) secret := NewInlineSecret(string(expectedConfig.ClientSecret)) rt := NewOAuth2RoundTripper(secret, &expectedConfig, http.DefaultTransport, &defaultHTTPClientOptions) @@ -1640,65 +1514,45 @@ endpoint_params: // Default secret. expectedAuth = "Basic MToy" resp, err := client.Get(ts.url()) - if err != nil { - t.Fatal(err) - } + require.NoError(t, err) authorization := resp.Request.Header.Get("Authorization") - if authorization != "Bearer 12345" { - t.Fatalf("Expected authorization header to be 'Bearer', got '%s'", authorization) - } + require.Equalf(t, "Bearer 12345", authorization, "Expected authorization header to be 'Bearer', got '%s'", authorization) // Making a second request with the same secret should not re-call the token API. _, err = client.Get(ts.url()) - if err != nil { - t.Fatal(err) - } + require.NoError(t, err) // Empty secret. expectedAuth = "Basic MTo=" expectedConfig.ClientSecret = "" resp, err = client.Get(ts.url()) - if err != nil { - t.Fatal(err) - } + require.NoError(t, err) authorization = resp.Request.Header.Get("Authorization") - if authorization != "Bearer 12345" { - t.Fatalf("Expected authorization header to be 'Bearer 12345', got '%s'", authorization) - } + require.Equalf(t, "Bearer 12345", authorization, "Expected authorization header to be 'Bearer 12345', got '%s'", authorization) // Making a second request with the same secret should not re-call the token API. resp, err = client.Get(ts.url()) - if err != nil { - t.Fatal(err) - } + require.NoError(t, err) // Update secret. expectedAuth = "Basic MToxMjM0NTY3" expectedConfig.ClientSecret = "1234567" _, err = client.Get(ts.url()) - if err != nil { - t.Fatal(err) - } + require.NoError(t, err) // Making a second request with the same secret should not re-call the token API. _, err = client.Get(ts.url()) - if err != nil { - t.Fatal(err) - } + require.NoError(t, err) authorization = resp.Request.Header.Get("Authorization") - if authorization != "Bearer 12345" { - t.Fatalf("Expected authorization header to be 'Bearer 12345', got '%s'", authorization) - } + require.Equalf(t, "Bearer 12345", authorization, "Expected authorization header to be 'Bearer 12345', got '%s'", authorization) } func TestOAuth2UserAgent(t *testing.T) { ts := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { - if r.Header.Get("User-Agent") != "myuseragent" { - t.Fatalf("Expected User-Agent header in oauth request to be 'myuseragent', got '%s'", r.Header.Get("User-Agent")) - } + require.Equalf(t, "myuseragent", r.Header.Get("User-Agent"), "Expected User-Agent header in oauth request to be 'myuseragent', got '%s'", r.Header.Get("User-Agent")) res, _ := json.Marshal(oauth2TestServerResponse{ AccessToken: "12345", @@ -1719,29 +1573,21 @@ func TestOAuth2UserAgent(t *testing.T) { } rt, err := NewRoundTripperFromConfig(config, "test_oauth2", WithUserAgent("myuseragent")) - if err != nil { - t.Fatal(err) - } + require.NoError(t, err) client := http.Client{ Transport: rt, } resp, err := client.Get(ts.URL) - if err != nil { - t.Fatal(err) - } + require.NoError(t, err) authorization := resp.Request.Header.Get("Authorization") - if authorization != "Bearer 12345" { - t.Fatalf("Expected authorization header to be 'Bearer 12345', got '%s'", authorization) - } + require.Equalf(t, "Bearer 12345", authorization, "Expected authorization header to be 'Bearer 12345', got '%s'", authorization) } func TestHost(t *testing.T) { ts := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { - if r.Host != "localhost.localdomain" { - t.Fatalf("Expected Host header in request to be 'localhost.localdomain', got '%s'", r.Host) - } + require.Equalf(t, "localhost.localdomain", r.Host, "Expected Host header in request to be 'localhost.localdomain', got '%s'", r.Host) w.Header().Add("Content-Type", "application/json") })) @@ -1750,17 +1596,13 @@ func TestHost(t *testing.T) { config := DefaultHTTPClientConfig rt, err := NewRoundTripperFromConfig(config, "test_host", WithHost("localhost.localdomain")) - if err != nil { - t.Fatal(err) - } + require.NoError(t, err) client := http.Client{ Transport: rt, } _, err = client.Get(ts.URL) - if err != nil { - t.Fatal(err) - } + require.NoError(t, err) } func TestOAuth2WithFile(t *testing.T) { @@ -1769,9 +1611,7 @@ func TestOAuth2WithFile(t *testing.T) { defer ts.close() secretFile, err := os.CreateTemp("", "oauth2_secret") - if err != nil { - t.Fatal(err) - } + require.NoError(t, err) defer os.Remove(secretFile.Name()) yamlConfig := fmt.Sprintf(` @@ -1794,12 +1634,8 @@ endpoint_params: var unmarshalledConfig OAuth2 err = yaml.Unmarshal([]byte(yamlConfig), &unmarshalledConfig) - if err != nil { - t.Fatalf("Expected no error unmarshalling yaml, got %v", err) - } - if !reflect.DeepEqual(unmarshalledConfig, expectedConfig) { - t.Fatalf("Got unmarshalled config %v, expected %v", unmarshalledConfig, expectedConfig) - } + require.NoErrorf(t, err, "Expected no error unmarshalling yaml, got %v", err) + require.Truef(t, reflect.DeepEqual(unmarshalledConfig, expectedConfig), "Got unmarshalled config %v, expected %v", unmarshalledConfig, expectedConfig) secret := NewFileSecret(expectedConfig.ClientSecretFile) rt := NewOAuth2RoundTripper(secret, &expectedConfig, http.DefaultTransport, &defaultHTTPClientOptions) @@ -1811,106 +1647,68 @@ endpoint_params: // Empty secret file. expectedAuth = "Basic MTo=" resp, err := client.Get(ts.url()) - if err != nil { - t.Fatal(err) - } + require.NoError(t, err) authorization := resp.Request.Header.Get("Authorization") - if authorization != "Bearer 12345" { - t.Fatalf("Expected authorization header to be 'Bearer', got '%s'", authorization) - } + require.Equalf(t, "Bearer 12345", authorization, "Expected authorization header to be 'Bearer', got '%s'", authorization) // Making a second request with the same file content should not re-call the token API. _, err = client.Get(ts.url()) - if err != nil { - t.Fatal(err) - } + require.NoError(t, err) // File populated. expectedAuth = "Basic MToxMjM0NTY=" - if _, err := secretFile.Write([]byte("123456")); err != nil { - t.Fatal(err) - } + _, err = secretFile.Write([]byte("123456")) + require.NoError(t, err) resp, err = client.Get(ts.url()) - if err != nil { - t.Fatal(err) - } + require.NoError(t, err) authorization = resp.Request.Header.Get("Authorization") - if authorization != "Bearer 12345" { - t.Fatalf("Expected authorization header to be 'Bearer 12345', got '%s'", authorization) - } + require.Equalf(t, "Bearer 12345", authorization, "Expected authorization header to be 'Bearer 12345', got '%s'", authorization) // Making a second request with the same file content should not re-call the token API. resp, err = client.Get(ts.url()) - if err != nil { - t.Fatal(err) - } + require.NoError(t, err) // Update file. expectedAuth = "Basic MToxMjM0NTY3" - if _, err := secretFile.Write([]byte("7")); err != nil { - t.Fatal(err) - } + _, err = secretFile.Write([]byte("7")) + require.NoError(t, err) _, err = client.Get(ts.url()) - if err != nil { - t.Fatal(err) - } + require.NoError(t, err) // Making a second request with the same file content should not re-call the token API. _, err = client.Get(ts.url()) - if err != nil { - t.Fatal(err) - } + require.NoError(t, err) authorization = resp.Request.Header.Get("Authorization") - if authorization != "Bearer 12345" { - t.Fatalf("Expected authorization header to be 'Bearer 12345', got '%s'", authorization) - } + require.Equalf(t, "Bearer 12345", authorization, "Expected authorization header to be 'Bearer 12345', got '%s'", authorization) } func TestMarshalURL(t *testing.T) { urlp, err := url.Parse("http://example.com/") - if err != nil { - t.Fatal(err) - } + require.NoError(t, err) u := &URL{urlp} c, err := json.Marshal(u) - if err != nil { - t.Fatal(err) - } - if string(c) != "\"http://example.com/\"" { - t.Fatalf("URL not properly marshaled in JSON got '%s'", string(c)) - } + require.NoError(t, err) + require.Equalf(t, "\"http://example.com/\"", string(c), "URL not properly marshaled in JSON got '%s'", string(c)) c, err = yaml.Marshal(u) - if err != nil { - t.Fatal(err) - } - if string(c) != "http://example.com/\n" { - t.Fatalf("URL not properly marshaled in YAML got '%s'", string(c)) - } + require.NoError(t, err) + require.Equalf(t, "http://example.com/\n", string(c), "URL not properly marshaled in YAML got '%s'", string(c)) } func TestMarshalURLWrapperWithNilValue(t *testing.T) { u := &URL{} c, err := json.Marshal(u) - if err != nil { - t.Fatal(err) - } - if string(c) != "null" { - t.Fatalf("URL with nil value not properly marshaled into JSON, got %q", c) - } + require.NoError(t, err) + require.Equalf(t, "null", string(c), "URL with nil value not properly marshaled into JSON, got %q", c) c, err = yaml.Marshal(u) - if err != nil { - t.Fatal(err) - } - if string(c) != "null\n" { - t.Fatalf("URL with nil value not properly marshaled into JSON, got %q", c) - } + require.NoError(t, err) + require.Equalf(t, "null\n", string(c), "URL with nil value not properly marshaled into JSON, got %q", c) } func TestUnmarshalNullURL(t *testing.T) { @@ -1919,23 +1717,16 @@ func TestUnmarshalNullURL(t *testing.T) { { var u URL err := json.Unmarshal(b, &u) - if err != nil { - t.Fatal(err) - } - if !isEmptyNonNilURL(u.URL) { - t.Fatalf("`null` literal not properly unmarshaled from JSON as URL, got %#v", u.URL) - } + require.NoError(t, err) + require.Truef(t, isEmptyNonNilURL(u.URL), "`null` literal not properly unmarshaled from JSON as URL, got %#v", u.URL) } { var u URL err := yaml.Unmarshal(b, &u) - if err != nil { - t.Fatal(err) - } - if u.URL != nil { // UnmarshalYAML is not called when parsing null literal. - t.Fatalf("`null` literal not properly unmarshaled from YAML as URL, got %#v", u.URL) - } + require.NoError(t, err) + // UnmarshalYAML is not called when parsing null literal. + require.Nilf(t, u.URL, "`null` literal not properly unmarshaled from YAML as URL, got %#v", u.URL) } } @@ -1945,23 +1736,15 @@ func TestUnmarshalEmptyURL(t *testing.T) { { var u URL err := json.Unmarshal(b, &u) - if err != nil { - t.Fatal(err) - } - if !isEmptyNonNilURL(u.URL) { - t.Fatalf("empty string not properly unmarshaled from JSON as URL, got %#v", u.URL) - } + require.NoError(t, err) + require.Truef(t, isEmptyNonNilURL(u.URL), "empty string not properly unmarshaled from JSON as URL, got %#v", u.URL) } { var u URL err := yaml.Unmarshal(b, &u) - if err != nil { - t.Fatal(err) - } - if !isEmptyNonNilURL(u.URL) { - t.Fatalf("empty string not properly unmarshaled from YAML as URL, got %#v", u.URL) - } + require.NoError(t, err) + require.Truef(t, isEmptyNonNilURL(u.URL), "empty string not properly unmarshaled from YAML as URL, got %#v", u.URL) } } @@ -1975,36 +1758,22 @@ func TestUnmarshalURL(t *testing.T) { var u URL err := json.Unmarshal(b, &u) - if err != nil { - t.Fatal(err) - } - if u.String() != "http://example.com/a%20b" { - t.Fatalf("URL not properly unmarshaled in JSON, got '%s'", u.String()) - } + require.NoError(t, err) + require.Equalf(t, "http://example.com/a%20b", u.String(), "URL not properly unmarshaled in JSON, got '%s'", u.String()) err = yaml.Unmarshal(b, &u) - if err != nil { - t.Fatal(err) - } - if u.String() != "http://example.com/a%20b" { - t.Fatalf("URL not properly unmarshaled in YAML, got '%s'", u.String()) - } + require.NoError(t, err) + require.Equalf(t, "http://example.com/a%20b", u.String(), "URL not properly unmarshaled in YAML, got '%s'", u.String()) } func TestMarshalURLWithSecret(t *testing.T) { var u URL err := yaml.Unmarshal([]byte("http://foo:bar@example.com"), &u) - if err != nil { - t.Fatal(err) - } + require.NoError(t, err) b, err := yaml.Marshal(u) - if err != nil { - t.Fatal(err) - } - if strings.TrimSpace(string(b)) != "http://foo:xxxxx@example.com" { - t.Fatalf("URL not properly marshaled in YAML, got '%s'", string(b)) - } + require.NoError(t, err) + require.Equalf(t, "http://foo:xxxxx@example.com", strings.TrimSpace(string(b)), "URL not properly marshaled in YAML, got '%s'", string(b)) } func TestHTTPClientConfig_Marshal(t *testing.T) { @@ -2101,9 +1870,7 @@ func TestModifyTLSCertificates(t *testing.T) { bs := getCertificateBlobs(t) tmpDir, err := os.MkdirTemp("", "modifytlscertificates") - if err != nil { - t.Fatal("Failed to create tmp dir", err) - } + require.NoErrorf(t, err, "Failed to create tmp dir") defer os.RemoveAll(tmpDir) ca, cert, key := filepath.Join(tmpDir, "ca"), filepath.Join(tmpDir, "cert"), filepath.Join(tmpDir, "key") @@ -2111,9 +1878,7 @@ func TestModifyTLSCertificates(t *testing.T) { fmt.Fprint(w, ExpectedMessage) } testServer, err := newTestServer(handler) - if err != nil { - t.Fatal(err.Error()) - } + require.NoError(t, err) defer testServer.Close() tests := []struct { @@ -2171,31 +1936,23 @@ func TestModifyTLSCertificates(t *testing.T) { writeCertificate(bs, tc.key, key) if c == nil { c, err = NewClientFromConfig(cfg, "test") - if err != nil { - t.Fatalf("Error creating HTTP Client: %v", err) - } + require.NoErrorf(t, err, "Error creating HTTP Client: %v", err) } req, err := http.NewRequest(http.MethodGet, testServer.URL, nil) - if err != nil { - t.Fatalf("Error creating HTTP request: %v", err) - } + require.NoErrorf(t, err, "Error creating HTTP request: %v", err) r, err := c.Do(req) if err == nil { r.Body.Close() t.Fatalf("Could connect to the test server.") } - if !strings.Contains(err.Error(), tc.errMsg) { - t.Fatalf("Expected error message to contain %q, got %q", tc.errMsg, err) - } + require.ErrorContainsf(t, err, tc.errMsg, "Expected error message to contain %q, got %q", tc.errMsg, err) tc.modification() r, err = c.Do(req) - if err != nil { - t.Fatalf("Expected no error, got %q", err) - } + require.NoErrorf(t, err, "Expected no error, got %q", err) b, err := io.ReadAll(r.Body) r.Body.Close() @@ -2374,9 +2131,7 @@ func readFile(t *testing.T, filename string) string { t.Helper() content, err := os.ReadFile(filename) - if err != nil { - t.Fatalf("Failed to read file %q: %s", filename, err) - } + require.NoErrorf(t, err, "Failed to read file %q: %s", filename, err) return string(content) } @@ -2397,18 +2152,12 @@ func TestHeaders(t *testing.T) { t.Cleanup(ts.Close) cfg, _, err := LoadHTTPConfigFile("testdata/http.conf.headers.good.yaml") - if err != nil { - t.Fatalf("Error loading HTTP client config: %v", err) - } + require.NoErrorf(t, err, "Error loading HTTP client config: %v", err) client, err := NewClientFromConfig(*cfg, "test") - if err != nil { - t.Fatalf("Error creating HTTP Client: %v", err) - } + require.NoErrorf(t, err, "Error creating HTTP Client: %v", err) _, err = client.Get(ts.URL) - if err != nil { - t.Fatalf("can't fetch URL: %v", err) - } + require.NoErrorf(t, err, "can't fetch URL: %v", err) } func TestMultipleHeaders(t *testing.T) { @@ -2427,16 +2176,10 @@ func TestMultipleHeaders(t *testing.T) { t.Cleanup(ts.Close) cfg, _, err := LoadHTTPConfigFile("testdata/http.conf.headers-multiple.good.yaml") - if err != nil { - t.Fatalf("Error loading HTTP client config: %v", err) - } + require.NoErrorf(t, err, "Error loading HTTP client config: %v", err) client, err := NewClientFromConfig(*cfg, "test") - if err != nil { - t.Fatalf("Error creating HTTP Client: %v", err) - } + require.NoErrorf(t, err, "Error creating HTTP Client: %v", err) _, err = client.Get(ts.URL) - if err != nil { - t.Fatalf("can't fetch URL: %v", err) - } + require.NoErrorf(t, err, "can't fetch URL: %v", err) } diff --git a/config/tls_config_test.go b/config/tls_config_test.go index 829541c35..b43a1a166 100644 --- a/config/tls_config_test.go +++ b/config/tls_config_test.go @@ -25,6 +25,7 @@ import ( "strings" "testing" + "github.com/stretchr/testify/require" "gopkg.in/yaml.v2" ) @@ -101,14 +102,10 @@ var expectedTLSConfigs = []struct { func TestValidTLSConfig(t *testing.T) { for _, cfg := range expectedTLSConfigs { got, err := LoadTLSConfig("testdata/" + cfg.filename) - if err != nil { - t.Fatalf("Error parsing %s: %s", cfg.filename, err) - } + require.NoErrorf(t, err, "Error parsing %s: %s", cfg.filename, err) // non-nil functions are never equal. got.GetClientCertificate = nil - if !reflect.DeepEqual(got, cfg.config) { - t.Fatalf("%v: unexpected config result: \n\n%v\n expected\n\n%v", cfg.filename, got, cfg.config) - } + require.Truef(t, reflect.DeepEqual(got, cfg.config), "%v: unexpected config result: \n\n%v\n expected\n\n%v", cfg.filename, got, cfg.config) } } @@ -136,9 +133,8 @@ func TestInvalidTLSConfig(t *testing.T) { } func TestTLSVersionStringer(t *testing.T) { - if s := (TLSVersion)(tls.VersionTLS13); s.String() != "TLS13" { - t.Fatalf("tls.VersionTLS13 string should be TLS13, got %s", s.String()) - } + s := (TLSVersion)(tls.VersionTLS13) + require.Equalf(t, "TLS13", s.String(), "tls.VersionTLS13 string should be TLS13, got %s", s.String()) } func TestTLSVersionMarshalYAML(t *testing.T) { @@ -174,9 +170,7 @@ func TestTLSVersionMarshalYAML(t *testing.T) { return } actual := string(actualBytes) - if actual != test.expected { - t.Fatalf("returned %s, expected %s", actual, test.expected) - } + require.Equalf(t, test.expected, actual, "returned %s, expected %s", actual, test.expected) }) } } @@ -214,9 +208,7 @@ func TestTLSVersionMarshalJSON(t *testing.T) { return } actual := string(actualBytes) - if actual != test.expected { - t.Fatalf("returned %s, expected %s", actual, test.expected) - } + require.Equalf(t, test.expected, actual, "returned %s, expected %s", actual, test.expected) }) } } diff --git a/expfmt/bench_test.go b/expfmt/bench_test.go index 6857f9a1c..4f691b310 100644 --- a/expfmt/bench_test.go +++ b/expfmt/bench_test.go @@ -25,6 +25,7 @@ import ( "google.golang.org/protobuf/encoding/protodelim" dto "github.com/prometheus/client_model/go" + "github.com/stretchr/testify/require" ) var parser TextParser @@ -50,15 +51,12 @@ var parser TextParser func BenchmarkParseText(b *testing.B) { b.StopTimer() data, err := os.ReadFile("testdata/text") - if err != nil { - b.Fatal(err) - } + require.NoError(b, err) b.StartTimer() for i := 0; i < b.N; i++ { - if _, err := parser.TextToMetricFamilies(bytes.NewReader(data)); err != nil { - b.Fatal(err) - } + _, err := parser.TextToMetricFamilies(bytes.NewReader(data)) + require.NoError(b, err) } } @@ -67,19 +65,14 @@ func BenchmarkParseText(b *testing.B) { func BenchmarkParseTextGzip(b *testing.B) { b.StopTimer() data, err := os.ReadFile("testdata/text.gz") - if err != nil { - b.Fatal(err) - } + require.NoError(b, err) b.StartTimer() for i := 0; i < b.N; i++ { in, err := gzip.NewReader(bytes.NewReader(data)) - if err != nil { - b.Fatal(err) - } - if _, err := parser.TextToMetricFamilies(in); err != nil { - b.Fatal(err) - } + require.NoError(b, err) + _, err = parser.TextToMetricFamilies(in) + require.NoError(b, err) } } @@ -92,9 +85,7 @@ func BenchmarkParseTextGzip(b *testing.B) { func BenchmarkParseProto(b *testing.B) { b.StopTimer() data, err := os.ReadFile("testdata/protobuf") - if err != nil { - b.Fatal(err) - } + require.NoError(b, err) b.StartTimer() for i := 0; i < b.N; i++ { @@ -120,17 +111,13 @@ func BenchmarkParseProto(b *testing.B) { func BenchmarkParseProtoGzip(b *testing.B) { b.StopTimer() data, err := os.ReadFile("testdata/protobuf.gz") - if err != nil { - b.Fatal(err) - } + require.NoError(b, err) b.StartTimer() for i := 0; i < b.N; i++ { family := &dto.MetricFamily{} gz, err := gzip.NewReader(bytes.NewReader(data)) - if err != nil { - b.Fatal(err) - } + require.NoError(b, err) in := bufio.NewReader(gz) unmarshaler := protodelim.UnmarshalOptions{ MaxSize: -1, @@ -154,9 +141,7 @@ func BenchmarkParseProtoGzip(b *testing.B) { func BenchmarkParseProtoMap(b *testing.B) { b.StopTimer() data, err := os.ReadFile("testdata/protobuf") - if err != nil { - b.Fatal(err) - } + require.NoError(b, err) b.StartTimer() for i := 0; i < b.N; i++ { diff --git a/expfmt/decode_test.go b/expfmt/decode_test.go index 1b261401e..10b12b667 100644 --- a/expfmt/decode_test.go +++ b/expfmt/decode_test.go @@ -27,6 +27,7 @@ import ( "testing" dto "github.com/prometheus/client_model/go" + "github.com/stretchr/testify/require" "google.golang.org/protobuf/proto" "github.com/prometheus/common/model" @@ -91,16 +92,12 @@ mf2 4 if err != nil && errors.Is(err, io.EOF) { break } - if err != nil { - t.Fatal(err) - } + require.NoError(t, err) all = append(all, smpls...) } sort.Sort(all) sort.Sort(out) - if !reflect.DeepEqual(all, out) { - t.Fatalf("output does not match") - } + require.Truef(t, reflect.DeepEqual(all, out), "output does not match") } func TestProtoDecoder(t *testing.T) { @@ -379,9 +376,7 @@ func TestProtoDecoder(t *testing.T) { break } if scenario.legacyNameFail { - if err == nil { - t.Fatal("Expected error when decoding without UTF-8 support enabled but got none") - } + require.Errorf(t, err, "Expected error when decoding without UTF-8 support enabled but got none") model.NameValidationScheme = model.UTF8Validation dec = &SampleDecoder{ Dec: &protoDecoder{r: strings.NewReader(scenario.in)}, @@ -393,34 +388,24 @@ func TestProtoDecoder(t *testing.T) { if errors.Is(err, io.EOF) { break } - if err != nil { - t.Fatalf("Unexpected error when decoding with UTF-8 support: %v", err) - } + require.NoErrorf(t, err, "Unexpected error when decoding with UTF-8 support: %v", err) } if scenario.fail { - if err == nil { - t.Fatal("Expected error but got none") - } + require.Errorf(t, err, "Expected error but got none") break } - if err != nil { - t.Fatal(err) - } + require.NoError(t, err) all = append(all, smpls...) } sort.Sort(all) sort.Sort(scenario.expected) - if !reflect.DeepEqual(all, scenario.expected) { - t.Fatalf("%d. output does not match, want: %#v, got %#v", i, scenario.expected, all) - } + require.Truef(t, reflect.DeepEqual(all, scenario.expected), "%d. output does not match, want: %#v, got %#v", i, scenario.expected, all) } } func TestProtoMultiMessageDecoder(t *testing.T) { data, err := os.ReadFile("testdata/protobuf-multimessage") - if err != nil { - t.Fatalf("Reading file failed: %v", err) - } + require.NoErrorf(t, err, "Reading file failed: %v", err) buf := bytes.NewReader(data) decoder := NewDecoder(buf, FmtProtoDelim) @@ -436,9 +421,7 @@ func TestProtoMultiMessageDecoder(t *testing.T) { metrics = append(metrics, &mf) } - if len(metrics) != 6 { - t.Fatalf("Expected %d metrics but got %d!", 6, len(metrics)) - } + require.Lenf(t, metrics, 6, "Expected %d metrics but got %d!", 6, len(metrics)) } func testDiscriminatorHTTPHeader(t testing.TB) { @@ -591,7 +574,5 @@ func TestTextDecoderWithBufioReader(t *testing.T) { } decoded = true } - if !decoded { - t.Fatal("Metric foo not decoded") - } + require.Truef(t, decoded, "Metric foo not decoded") } diff --git a/expfmt/openmetrics_create_test.go b/expfmt/openmetrics_create_test.go index 58b284e9e..a81bfed3f 100644 --- a/expfmt/openmetrics_create_test.go +++ b/expfmt/openmetrics_create_test.go @@ -24,6 +24,7 @@ import ( "google.golang.org/protobuf/types/known/timestamppb" dto "github.com/prometheus/client_model/go" + "github.com/stretchr/testify/require" "github.com/prometheus/common/model" ) @@ -862,9 +863,7 @@ func BenchmarkOpenMetricsCreate(b *testing.B) { for i := 0; i < b.N; i++ { _, err := MetricFamilyToOpenMetrics(out, mf) - if err != nil { - b.Fatal(err) - } + require.NoError(b, err) out.Reset() } } diff --git a/expfmt/text_create_test.go b/expfmt/text_create_test.go index 7cf042896..bb1c8f77a 100644 --- a/expfmt/text_create_test.go +++ b/expfmt/text_create_test.go @@ -22,6 +22,7 @@ import ( "google.golang.org/protobuf/proto" dto "github.com/prometheus/client_model/go" + "github.com/stretchr/testify/require" "github.com/prometheus/common/model" ) @@ -502,9 +503,7 @@ func BenchmarkCreate(b *testing.B) { for i := 0; i < b.N; i++ { _, err := MetricFamilyToText(out, mf) - if err != nil { - b.Fatal(err) - } + require.NoError(b, err) out.Reset() } } @@ -540,9 +539,7 @@ func BenchmarkCreateBuildInfo(b *testing.B) { for i := 0; i < b.N; i++ { _, err := MetricFamilyToText(out, mf) - if err != nil { - b.Fatal(err) - } + require.NoError(b, err) out.Reset() } } diff --git a/model/signature_test.go b/model/signature_test.go index a075bdc6d..3c08af4e6 100644 --- a/model/signature_test.go +++ b/model/signature_test.go @@ -18,6 +18,8 @@ import ( "runtime" "sync" "testing" + + "github.com/stretchr/testify/require" ) func TestLabelsToSignature(t *testing.T) { @@ -188,9 +190,8 @@ func TestSignatureWithoutLabels(t *testing.T) { func benchmarkLabelToSignature(b *testing.B, l map[string]string, e uint64) { for i := 0; i < b.N; i++ { - if a := LabelsToSignature(l); a != e { - b.Fatalf("expected signature of %d for %s, got %d", e, l, a) - } + a := LabelsToSignature(l) + require.Equalf(b, a, e, "expected signature of %d for %s, got %d", e, l, a) } } @@ -212,9 +213,8 @@ func BenchmarkLabelToSignatureTriple(b *testing.B) { func benchmarkMetricToFingerprint(b *testing.B, ls LabelSet, e Fingerprint) { for i := 0; i < b.N; i++ { - if a := labelSetToFingerprint(ls); a != e { - b.Fatalf("expected signature of %d for %s, got %d", e, ls, a) - } + a := labelSetToFingerprint(ls) + require.Equalf(b, a, e, "expected signature of %d for %s, got %d", e, ls, a) } } @@ -236,9 +236,8 @@ func BenchmarkMetricToFingerprintTriple(b *testing.B) { func benchmarkMetricToFastFingerprint(b *testing.B, ls LabelSet, e Fingerprint) { for i := 0; i < b.N; i++ { - if a := labelSetToFastFingerprint(ls); a != e { - b.Fatalf("expected signature of %d for %s, got %d", e, ls, a) - } + a := labelSetToFastFingerprint(ls) + require.Equalf(b, a, e, "expected signature of %d for %s, got %d", e, ls, a) } } @@ -272,9 +271,8 @@ func BenchmarkEmptyLabelSignature(b *testing.B) { runtime.ReadMemStats(&ms) - if got := ms.Alloc; alloc != got { - b.Fatal("expected LabelsToSignature with empty labels not to perform allocations") - } + got := ms.Alloc + require.Equalf(b, alloc, got, "expected LabelsToSignature with empty labels not to perform allocations") } func benchmarkMetricToFastFingerprintConc(b *testing.B, ls LabelSet, e Fingerprint, concLevel int) { diff --git a/model/time_test.go b/model/time_test.go index f5724122b..70f383947 100644 --- a/model/time_test.go +++ b/model/time_test.go @@ -18,6 +18,8 @@ import ( "strconv" "testing" "time" + + "github.com/stretchr/testify/require" ) func TestComparators(t *testing.T) { @@ -25,26 +27,14 @@ func TestComparators(t *testing.T) { t1b := TimeFromUnix(0) t2 := TimeFromUnix(2*second - 1) - if !t1a.Equal(t1b) { - t.Fatalf("Expected %s to be equal to %s", t1a, t1b) - } - if t1a.Equal(t2) { - t.Fatalf("Expected %s to not be equal to %s", t1a, t2) - } + require.Truef(t, t1a.Equal(t1b), "Expected %s to be equal to %s", t1a, t1b) + require.Falsef(t, t1a.Equal(t2), "Expected %s to not be equal to %s", t1a, t2) - if !t1a.Before(t2) { - t.Fatalf("Expected %s to be before %s", t1a, t2) - } - if t1a.Before(t1b) { - t.Fatalf("Expected %s to not be before %s", t1a, t1b) - } + require.Truef(t, t1a.Before(t2), "Expected %s to be before %s", t1a, t2) + require.Falsef(t, t1a.Before(t1b), "Expected %s to not be before %s", t1a, t1b) - if !t2.After(t1a) { - t.Fatalf("Expected %s to be after %s", t2, t1a) - } - if t1b.After(t1a) { - t.Fatalf("Expected %s to not be after %s", t1b, t1a) - } + require.Truef(t, t2.After(t1a), "Expected %s to be after %s", t2, t1a) + require.Falsef(t, t1b.After(t1a), "Expected %s to not be after %s", t1b, t1a) } func TestTimeConversions(t *testing.T) { @@ -56,19 +46,13 @@ func TestTimeConversions(t *testing.T) { t2 := time.Unix(unixSecs, unixNsecs) ts := TimeFromUnixNano(unixNano) - if !ts.Time().Equal(t1) { - t.Fatalf("Expected %s, got %s", t1, ts.Time()) - } + require.Truef(t, ts.Time().Equal(t1), "Expected %s, got %s", t1, ts.Time()) // Test available precision. ts = TimeFromUnixNano(t2.UnixNano()) - if !ts.Time().Equal(t1) { - t.Fatalf("Expected %s, got %s", t1, ts.Time()) - } + require.Truef(t, ts.Time().Equal(t1), "Expected %s, got %s", t1, ts.Time()) - if ts.UnixNano() != unixNano-unixNano%nanosPerTick { - t.Fatalf("Expected %d, got %d", unixNano, ts.UnixNano()) - } + require.Equalf(t, ts.UnixNano(), unixNano-unixNano%nanosPerTick, "Expected %d, got %d", unixNano, ts.UnixNano()) } func TestDuration(t *testing.T) { @@ -76,15 +60,11 @@ func TestDuration(t *testing.T) { goTime := time.Unix(1136239445, 0) ts := TimeFromUnix(goTime.Unix()) - if !goTime.Add(duration).Equal(ts.Add(duration).Time()) { - t.Fatalf("Expected %s to be equal to %s", goTime.Add(duration), ts.Add(duration)) - } + require.Truef(t, goTime.Add(duration).Equal(ts.Add(duration).Time()), "Expected %s to be equal to %s", goTime.Add(duration), ts.Add(duration)) earlier := ts.Add(-duration) delta := ts.Sub(earlier) - if delta != duration { - t.Fatalf("Expected %s to be equal to %s", delta, duration) - } + require.Equalf(t, delta, duration, "Expected %s to be equal to %s", delta, duration) } func TestParseDuration(t *testing.T) { @@ -358,22 +338,17 @@ func TestTimeJSON(t *testing.T) { for i, test := range tests { t.Run(strconv.Itoa(i), func(t *testing.T) { b, err := test.in.MarshalJSON() - if err != nil { - t.Fatalf("Error marshaling time: %v", err) - } + require.NoErrorf(t, err, "Error marshaling time: %v", err) if string(b) != test.out { t.Errorf("Mismatch in marshal expected=%s actual=%s", test.out, b) } var tm Time - if err := tm.UnmarshalJSON(b); err != nil { - t.Fatalf("Error Unmarshaling time: %v", err) - } + err = tm.UnmarshalJSON(b) + require.NoErrorf(t, err, "Error Unmarshaling time: %v", err) - if !test.in.Equal(tm) { - t.Fatalf("Mismatch after Unmarshal expected=%v actual=%v", test.in, tm) - } + require.Truef(t, test.in.Equal(tm), "Mismatch after Unmarshal expected=%v actual=%v", test.in, tm) }) } } @@ -383,8 +358,6 @@ func BenchmarkParseDuration(b *testing.B) { for i := 0; i < b.N; i++ { _, err := ParseDuration(data) - if err != nil { - b.Fatal(err) - } + require.NoError(b, err) } } diff --git a/model/value_float_test.go b/model/value_float_test.go index 911d0171c..7c4a84948 100644 --- a/model/value_float_test.go +++ b/model/value_float_test.go @@ -18,6 +18,8 @@ import ( "math" "reflect" "testing" + + "github.com/stretchr/testify/require" ) var ( @@ -300,8 +302,6 @@ func TestMatrixJSON(t *testing.T) { func BenchmarkJSONMarshallingSamplePairMatrix(b *testing.B) { for i := 0; i < b.N; i++ { _, err := json.Marshal(samplePairMatrixValue) - if err != nil { - b.Fatal("error marshalling") - } + require.NoErrorf(b, err, "error marshalling") } } diff --git a/model/value_histogram_test.go b/model/value_histogram_test.go index f36306fba..e239f4630 100644 --- a/model/value_histogram_test.go +++ b/model/value_histogram_test.go @@ -18,6 +18,8 @@ import ( "reflect" "regexp" "testing" + + "github.com/stretchr/testify/require" ) var ( @@ -750,8 +752,6 @@ func TestMatrixHistogramJSON(t *testing.T) { func BenchmarkJSONMarshallingSampleHistogramPairMatrix(b *testing.B) { for i := 0; i < b.N; i++ { _, err := json.Marshal(sampleHistogramPairMatrixValue) - if err != nil { - b.Fatal("error marshalling") - } + require.NoErrorf(b, err, "error marshalling") } } diff --git a/model/value_test.go b/model/value_test.go index c57efb165..1f1d8278b 100644 --- a/model/value_test.go +++ b/model/value_test.go @@ -18,6 +18,8 @@ import ( "math" "sort" "testing" + + "github.com/stretchr/testify/require" ) func TestEqualSamples(t *testing.T) { @@ -351,12 +353,8 @@ func TestVectorSort(t *testing.T) { actualFp := actual.Metric.Fingerprint() expectedFp := expected[i].Metric.Fingerprint() - if actualFp != expectedFp { - t.Fatalf("%d. Incorrect fingerprint. Got %s; want %s", i, actualFp.String(), expectedFp.String()) - } + require.Equalf(t, expectedFp, actualFp, "%d. Incorrect fingerprint. Got %s; want %s", i, actualFp.String(), expectedFp.String()) - if actual.Timestamp != expected[i].Timestamp { - t.Fatalf("%d. Incorrect timestamp. Got %s; want %s", i, actual.Timestamp, expected[i].Timestamp) - } + require.Equalf(t, actual.Timestamp, expected[i].Timestamp, "%d. Incorrect timestamp. Got %s; want %s", i, actual.Timestamp, expected[i].Timestamp) } } diff --git a/promlog/log_test.go b/promlog/log_test.go index 005670835..395e05164 100644 --- a/promlog/log_test.go +++ b/promlog/log_test.go @@ -18,6 +18,7 @@ import ( "testing" "github.com/go-kit/log/level" + "github.com/stretchr/testify/require" "gopkg.in/yaml.v2" ) @@ -26,9 +27,8 @@ import ( func TestDefaultConfig(t *testing.T) { logger := New(&Config{}) - if err := logger.Log("hello", "world"); err != nil { - t.Fatal(err) - } + err := logger.Log("hello", "world") + require.NoError(t, err) } func TestUnmarshallLevel(t *testing.T) { @@ -86,42 +86,28 @@ func TestDynamic(t *testing.T) { logger := NewDynamic(&Config{}) debugLevel := &AllowedLevel{} - if err := debugLevel.Set("debug"); err != nil { - t.Fatal(err) - } + err := debugLevel.Set("debug") + require.NoError(t, err) infoLevel := &AllowedLevel{} - if err := infoLevel.Set("info"); err != nil { - t.Fatal(err) - } + err = infoLevel.Set("info") + require.NoError(t, err) recorder := &recordKeyvalLogger{} logger.base = recorder logger.SetLevel(debugLevel) - if err := level.Debug(logger).Log("hello", "world"); err != nil { - t.Fatal(err) - } - if recorder.count != 1 { - t.Fatal("log not found") - } + err = level.Debug(logger).Log("hello", "world") + require.NoError(t, err) + require.Equalf(t, 1, recorder.count, "log not found") recorder.count = 0 logger.SetLevel(infoLevel) - if err := level.Debug(logger).Log("hello", "world"); err != nil { - t.Fatal(err) - } - if recorder.count != 0 { - t.Fatal("log found") - } - if err := level.Info(logger).Log("hello", "world"); err != nil { - t.Fatal(err) - } - if recorder.count != 1 { - t.Fatal("log not found") - } - if err := level.Debug(logger).Log("hello", "world"); err != nil { - t.Fatal(err) - } - if recorder.count != 1 { - t.Fatal("extra log found") - } + err = level.Debug(logger).Log("hello", "world") + require.NoError(t, err) + require.Equalf(t, 0, recorder.count, "log found") + err = level.Info(logger).Log("hello", "world") + require.NoError(t, err) + require.Equalf(t, 1, recorder.count, "log not found") + err = level.Debug(logger).Log("hello", "world") + require.NoError(t, err) + require.Equalf(t, 1, recorder.count, "extra log found") } diff --git a/promslog/slog_test.go b/promslog/slog_test.go index cc95a64c9..fc824e04f 100644 --- a/promslog/slog_test.go +++ b/promslog/slog_test.go @@ -115,15 +115,14 @@ func TestDynamicLevels(t *testing.T) { // Test that log level can be adjusted on-the-fly to debug and that a // log entry can be written to the file. - if err := config.Level.Set("debug"); err != nil { - t.Fatal(err) - } + err := config.Level.Set("debug") + require.NoError(t, err) logger.Info("info", "hello", "world") logger.Debug("debug", "hello", "world") counts := getLogEntryLevelCounts(buf.String(), tc.logStyleRegexp) - require.Equal(t, tc.wantedLevelCount["info"], counts["info"], "info log successfully detected") - require.Equal(t, tc.wantedLevelCount["debug"], counts["debug"], "debug log successfully detected") + require.Equalf(t, tc.wantedLevelCount["info"], counts["info"], "info log successfully detected") + require.Equalf(t, tc.wantedLevelCount["debug"], counts["debug"], "debug log successfully detected") // Print logs for humans to see, if needed. fmt.Println(buf.String()) buf.Reset() @@ -131,15 +130,14 @@ func TestDynamicLevels(t *testing.T) { // Test that log level can be adjusted on-the-fly to info and that a // subsequent call to write a debug level log is _not_ written to the // file. - if err := config.Level.Set("info"); err != nil { - t.Fatal(err) - } + err = config.Level.Set("info") + require.NoError(t, err) logger.Info("info", "hello", "world") logger.Debug("debug", "hello", "world") counts = getLogEntryLevelCounts(buf.String(), tc.logStyleRegexp) - require.Equal(t, tc.wantedLevelCount["info"], counts["info"], "info log successfully detected") - require.NotEqual(t, tc.wantedLevelCount["debug"], counts["debug"], "extra debug log detected") + require.Equalf(t, tc.wantedLevelCount["info"], counts["info"], "info log successfully detected") + require.NotEqualf(t, tc.wantedLevelCount["debug"], counts["debug"], "extra debug log detected") // Print logs for humans to see, if needed. fmt.Println(buf.String()) buf.Reset() diff --git a/route/route_test.go b/route/route_test.go index 0c22a66dc..24977b32a 100644 --- a/route/route_test.go +++ b/route/route_test.go @@ -17,26 +17,22 @@ import ( "net/http" "net/http/httptest" "testing" + + "github.com/stretchr/testify/require" ) func TestRedirect(t *testing.T) { router := New().WithPrefix("/test/prefix") w := httptest.NewRecorder() r, err := http.NewRequest("GET", "http://localhost:9090/foo", nil) - if err != nil { - t.Fatalf("Error building test request: %s", err) - } + require.NoErrorf(t, err, "Error building test request: %s", err) router.Redirect(w, r, "/some/endpoint", http.StatusFound) - if w.Code != http.StatusFound { - t.Fatalf("Unexpected redirect status code: got %d, want %d", w.Code, http.StatusFound) - } + require.Equalf(t, http.StatusFound, w.Code, "Unexpected redirect status code: got %d, want %d", w.Code, http.StatusFound) want := "/test/prefix/some/endpoint" got := w.Header()["Location"][0] - if want != got { - t.Fatalf("Unexpected redirect location: got %s, want %s", got, want) - } + require.Equalf(t, want, got, "Unexpected redirect location: got %s, want %s", got, want) } func TestContext(t *testing.T) { @@ -44,15 +40,11 @@ func TestContext(t *testing.T) { router.Get("/test/:foo/", func(w http.ResponseWriter, r *http.Request) { want := "bar" got := Param(r.Context(), "foo") - if want != got { - t.Fatalf("Unexpected context value: want %q, got %q", want, got) - } + require.Equalf(t, want, got, "Unexpected context value: want %q, got %q", want, got) }) r, err := http.NewRequest("GET", "http://localhost:9090/test/bar/", nil) - if err != nil { - t.Fatalf("Error building test request: %s", err) - } + require.NoErrorf(t, err, "Error building test request: %s", err) router.ServeHTTP(nil, r) } @@ -61,25 +53,17 @@ func TestContextWithValue(t *testing.T) { router.Get("/test/:foo/", func(w http.ResponseWriter, r *http.Request) { want := "bar" got := Param(r.Context(), "foo") - if want != got { - t.Fatalf("Unexpected context value: want %q, got %q", want, got) - } + require.Equalf(t, want, got, "Unexpected context value: want %q, got %q", want, got) want = "ipsum" got = Param(r.Context(), "lorem") - if want != got { - t.Fatalf("Unexpected context value: want %q, got %q", want, got) - } + require.Equalf(t, want, got, "Unexpected context value: want %q, got %q", want, got) want = "sit" got = Param(r.Context(), "dolor") - if want != got { - t.Fatalf("Unexpected context value: want %q, got %q", want, got) - } + require.Equalf(t, want, got, "Unexpected context value: want %q, got %q", want, got) }) r, err := http.NewRequest("GET", "http://localhost:9090/test/bar/", nil) - if err != nil { - t.Fatalf("Error building test request: %s", err) - } + require.NoErrorf(t, err, "Error building test request: %s", err) params := map[string]string{ "lorem": "ipsum", "dolor": "sit", @@ -98,15 +82,11 @@ func TestContextWithoutValue(t *testing.T) { router.Get("/test", func(w http.ResponseWriter, r *http.Request) { want := "" got := Param(r.Context(), "foo") - if want != got { - t.Fatalf("Unexpected context value: want %q, got %q", want, got) - } + require.Equalf(t, want, got, "Unexpected context value: want %q, got %q", want, got) }) r, err := http.NewRequest("GET", "http://localhost:9090/test", nil) - if err != nil { - t.Fatalf("Error building test request: %s", err) - } + require.NoErrorf(t, err, "Error building test request: %s", err) router.ServeHTTP(nil, r) } @@ -132,13 +112,9 @@ func TestInstrumentation(t *testing.T) { c.router.Get("/foo", func(w http.ResponseWriter, r *http.Request) {}) r, err := http.NewRequest("GET", "http://localhost:9090/foo", nil) - if err != nil { - t.Fatalf("Error building test request: %s", err) - } + require.NoErrorf(t, err, "Error building test request: %s", err) c.router.ServeHTTP(nil, r) - if c.want != got { - t.Fatalf("Unexpected value: want %q, got %q", c.want, got) - } + require.Equalf(t, c.want, got, "Unexpected value: want %q, got %q", c.want, got) } } @@ -176,17 +152,11 @@ func TestInstrumentations(t *testing.T) { c.router.Get("/foo", func(w http.ResponseWriter, r *http.Request) {}) r, err := http.NewRequest("GET", "http://localhost:9090/foo", nil) - if err != nil { - t.Fatalf("Error building test request: %s", err) - } + require.NoErrorf(t, err, "Error building test request: %s", err) c.router.ServeHTTP(nil, r) - if len(c.want) != len(got) { - t.Fatalf("Unexpected value: want %q, got %q", c.want, got) - } + require.Equalf(t, len(c.want), len(got), "Unexpected value: want %q, got %q", c.want, got) for i, v := range c.want { - if v != got[i] { - t.Fatalf("Unexpected value: want %q, got %q", c.want, got) - } + require.Equalf(t, v, got[i], "Unexpected value: want %q, got %q", c.want, got) } } } diff --git a/server/static_file_server_test.go b/server/static_file_server_test.go index 78fe56dfe..9aa8d76fb 100644 --- a/server/static_file_server_test.go +++ b/server/static_file_server_test.go @@ -17,6 +17,8 @@ import ( "net/http" "net/http/httptest" "testing" + + "github.com/stretchr/testify/require" ) type dummyFileSystem struct{} @@ -67,16 +69,12 @@ func TestServeHttp(t *testing.T) { t.Run(c.name, func(t *testing.T) { rr := httptest.NewRecorder() req, err := http.NewRequest("GET", "http://localhost/"+c.path, nil) - if err != nil { - t.Fatal(err) - } + require.NoError(t, err) s := StaticFileServer(dummyFileSystem{}) s.ServeHTTP(rr, req) - if rr.Header().Get("Content-Type") != c.contentType { - t.Fatalf("Unexpected Content-Type: %s", rr.Header().Get("Content-Type")) - } + require.Equalf(t, rr.Header().Get("Content-Type"), c.contentType, "Unexpected Content-Type: %s", rr.Header().Get("Content-Type")) }) } } diff --git a/sigv4/sigv4_config_test.go b/sigv4/sigv4_config_test.go index f88340da8..6c8058f64 100644 --- a/sigv4/sigv4_config_test.go +++ b/sigv4/sigv4_config_test.go @@ -15,9 +15,9 @@ package sigv4 import ( "os" - "strings" "testing" + "github.com/stretchr/testify/require" "gopkg.in/yaml.v2" ) @@ -35,9 +35,7 @@ func loadSigv4Config(filename string) (*SigV4Config, error) { func testGoodConfig(t *testing.T, filename string) { _, err := loadSigv4Config(filename) - if err != nil { - t.Fatalf("Unexpected error parsing %s: %s", filename, err) - } + require.NoErrorf(t, err, "Unexpected error parsing %s: %s", filename, err) } func TestGoodSigV4Configs(t *testing.T) { @@ -50,10 +48,6 @@ func TestGoodSigV4Configs(t *testing.T) { func TestBadSigV4Config(t *testing.T) { filename := "testdata/sigv4_bad.yaml" _, err := loadSigv4Config(filename) - if err == nil { - t.Fatalf("Did not receive expected error unmarshaling bad sigv4 config") - } - if !strings.Contains(err.Error(), "must provide a AWS SigV4 Access key and Secret Key") { - t.Errorf("Received unexpected error from unmarshal of %s: %s", filename, err.Error()) - } + require.Errorf(t, err, "Did not receive expected error unmarshaling bad sigv4 config") + require.ErrorContainsf(t, err, "must provide a AWS SigV4 Access key and Secret Key", "Received unexpected error from unmarshal of %s: %s", filename, err.Error()) } From 34874f2667e65aa48e9a2682cb2c3c00a87ea1fb Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Thu, 7 Nov 2024 21:26:45 +0100 Subject: [PATCH 07/48] Bump google.golang.org/protobuf from 1.34.2 to 1.35.1 (#711) * Bump google.golang.org/protobuf from 1.34.2 to 1.35.1 Bumps google.golang.org/protobuf from 1.34.2 to 1.35.1. --- updated-dependencies: - dependency-name: google.golang.org/protobuf dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] * Go mod tidy sigv4. Signed-off-by: SuperQ --------- Signed-off-by: dependabot[bot] Signed-off-by: SuperQ Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> Co-authored-by: SuperQ --- go.mod | 2 +- go.sum | 4 ++-- sigv4/go.mod | 2 +- sigv4/go.sum | 4 ++-- 4 files changed, 6 insertions(+), 6 deletions(-) diff --git a/go.mod b/go.mod index 03840e14a..6c87d62ec 100644 --- a/go.mod +++ b/go.mod @@ -13,7 +13,7 @@ require ( github.com/stretchr/testify v1.9.0 golang.org/x/net v0.30.0 golang.org/x/oauth2 v0.23.0 - google.golang.org/protobuf v1.34.2 + google.golang.org/protobuf v1.35.1 gopkg.in/yaml.v2 v2.4.0 ) diff --git a/go.sum b/go.sum index 265aefd99..395902cd4 100644 --- a/go.sum +++ b/go.sum @@ -55,8 +55,8 @@ golang.org/x/sys v0.26.0 h1:KHjCJyddX0LoSTb3J+vWpupP9p0oznkqVk/IfjymZbo= golang.org/x/sys v0.26.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= golang.org/x/text v0.19.0 h1:kTxAhCbGbxhK0IwgSKiMO5awPoDQ0RpfiVYBfK860YM= golang.org/x/text v0.19.0/go.mod h1:BuEKDfySbSR4drPmRPG/7iBdf8hvFMuRexcpahXilzY= -google.golang.org/protobuf v1.34.2 h1:6xV6lTsCfpGD21XK49h7MhtcApnLqkfYgPcdHftf6hg= -google.golang.org/protobuf v1.34.2/go.mod h1:qYOHts0dSfpeUzUFpOMr/WGzszTmLH+DiWniOlNbLDw= +google.golang.org/protobuf v1.35.1 h1:m3LfL6/Ca+fqnjnlqQXNpFPABW1UD7mjh8KO2mKFytA= +google.golang.org/protobuf v1.35.1/go.mod h1:9fA7Ob0pmnwhb644+1+CVWFRbNajQ6iRojtC/QF5bRE= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk= gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q= diff --git a/sigv4/go.mod b/sigv4/go.mod index 0ab227241..a4d6d2754 100644 --- a/sigv4/go.mod +++ b/sigv4/go.mod @@ -28,6 +28,6 @@ require ( golang.org/x/oauth2 v0.23.0 // indirect golang.org/x/sys v0.26.0 // indirect golang.org/x/text v0.19.0 // indirect - google.golang.org/protobuf v1.34.2 // indirect + google.golang.org/protobuf v1.35.1 // indirect gopkg.in/yaml.v3 v3.0.1 // indirect ) diff --git a/sigv4/go.sum b/sigv4/go.sum index a4d07977f..32025ad19 100644 --- a/sigv4/go.sum +++ b/sigv4/go.sum @@ -47,8 +47,8 @@ golang.org/x/sys v0.26.0 h1:KHjCJyddX0LoSTb3J+vWpupP9p0oznkqVk/IfjymZbo= golang.org/x/sys v0.26.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= golang.org/x/text v0.19.0 h1:kTxAhCbGbxhK0IwgSKiMO5awPoDQ0RpfiVYBfK860YM= golang.org/x/text v0.19.0/go.mod h1:BuEKDfySbSR4drPmRPG/7iBdf8hvFMuRexcpahXilzY= -google.golang.org/protobuf v1.34.2 h1:6xV6lTsCfpGD21XK49h7MhtcApnLqkfYgPcdHftf6hg= -google.golang.org/protobuf v1.34.2/go.mod h1:qYOHts0dSfpeUzUFpOMr/WGzszTmLH+DiWniOlNbLDw= +google.golang.org/protobuf v1.35.1 h1:m3LfL6/Ca+fqnjnlqQXNpFPABW1UD7mjh8KO2mKFytA= +google.golang.org/protobuf v1.35.1/go.mod h1:9fA7Ob0pmnwhb644+1+CVWFRbNajQ6iRojtC/QF5bRE= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk= gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q= From c971f562bc1b55562e6b4e1abb3e3e3a3811a9f4 Mon Sep 17 00:00:00 2001 From: Matthieu MOREL Date: Thu, 7 Nov 2024 21:27:11 +0100 Subject: [PATCH 08/48] setup dependabot for `github.com/prometheus/common/assets` (#719) Signed-off-by: Matthieu MOREL --- .github/dependabot.yml | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/.github/dependabot.yml b/.github/dependabot.yml index 9b81ffd8a..9b7d29d83 100644 --- a/.github/dependabot.yml +++ b/.github/dependabot.yml @@ -4,6 +4,10 @@ updates: directory: "/" schedule: interval: monthly + - package-ecosystem: "gomod" + directory: "/assets" + schedule: + interval: monthly - package-ecosystem: "gomod" directory: "/sigv4" schedule: From 8675cd95df84bb84238d2e1369d0f7dda04100d5 Mon Sep 17 00:00:00 2001 From: PrometheusBot Date: Fri, 8 Nov 2024 19:53:08 +0100 Subject: [PATCH 09/48] Update common Prometheus files (#721) Signed-off-by: prombot --- .github/workflows/golangci-lint.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/golangci-lint.yml b/.github/workflows/golangci-lint.yml index 7183091ac..305146993 100644 --- a/.github/workflows/golangci-lint.yml +++ b/.github/workflows/golangci-lint.yml @@ -24,7 +24,7 @@ jobs: runs-on: ubuntu-latest steps: - name: Checkout repository - uses: actions/checkout@d632683dd7b4114ad314bca15554477dd762a938 # v4.2.0 + uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2 - name: Install Go uses: actions/setup-go@41dfa10bad2bb2ae585af6ee5bb4d7d973ad74ed # v5.1.0 with: @@ -36,4 +36,4 @@ jobs: uses: golangci/golangci-lint-action@971e284b6050e8a5849b72094c50ab08da042db8 # v6.1.1 with: args: --verbose - version: v1.60.2 + version: v1.61.0 From b83ece486a6f012693f0dcb915bcbd132fc89c3d Mon Sep 17 00:00:00 2001 From: Ben Kochie Date: Mon, 11 Nov 2024 13:59:55 +0100 Subject: [PATCH 10/48] Mark promlog deprecated (#720) Now that all Prometheus repos have been migrated to the `promslog` package, mark `promlog` as deprecated. Signed-off-by: SuperQ --- promlog/flag/flag.go | 5 ++++- promlog/log.go | 4 ++++ 2 files changed, 8 insertions(+), 1 deletion(-) diff --git a/promlog/flag/flag.go b/promlog/flag/flag.go index e045b9739..16352a0fe 100644 --- a/promlog/flag/flag.go +++ b/promlog/flag/flag.go @@ -11,6 +11,9 @@ // See the License for the specific language governing permissions and // limitations under the License. +// Deprecated: This package has been deprecated in favor of migrating to +// `github.com/prometheus/common/promslog` which uses the Go standard library +// `log/slog` package. package flag import ( @@ -18,7 +21,7 @@ import ( kingpin "github.com/alecthomas/kingpin/v2" - "github.com/prometheus/common/promlog" + "github.com/prometheus/common/promlog" //nolint:staticcheck ) // LevelFlagName is the canonical flag name to configure the allowed log level diff --git a/promlog/log.go b/promlog/log.go index 50746544a..24a11622b 100644 --- a/promlog/log.go +++ b/promlog/log.go @@ -14,6 +14,10 @@ // Package promlog defines standardised ways to initialize Go kit loggers // across Prometheus components. // It should typically only ever be imported by main packages. +// +// Deprecated: This package has been deprecated in favor of migrating to +// `github.com/prometheus/common/promslog` which uses the Go standard library +// `log/slog` package. package promlog import ( From e25286d60073d90d7039a3fd8808065ce2617325 Mon Sep 17 00:00:00 2001 From: PrometheusBot Date: Tue, 12 Nov 2024 19:22:26 +0100 Subject: [PATCH 11/48] Update common Prometheus files (#722) Signed-off-by: prombot --- .yamllint | 2 +- Makefile.common | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/.yamllint b/.yamllint index 1859cb624..8d09c375f 100644 --- a/.yamllint +++ b/.yamllint @@ -1,7 +1,7 @@ --- extends: default ignore: | - ui/react-app/node_modules + **/node_modules rules: braces: diff --git a/Makefile.common b/Makefile.common index cbb5d8638..09e5bff85 100644 --- a/Makefile.common +++ b/Makefile.common @@ -61,7 +61,7 @@ PROMU_URL := https://github.com/prometheus/promu/releases/download/v$(PROMU_ SKIP_GOLANGCI_LINT := GOLANGCI_LINT := GOLANGCI_LINT_OPTS ?= -GOLANGCI_LINT_VERSION ?= v1.60.2 +GOLANGCI_LINT_VERSION ?= v1.61.0 # golangci-lint only supports linux, darwin and windows platforms on i386/amd64/arm64. # windows isn't included here because of the path separator being different. ifeq ($(GOHOSTOS),$(filter $(GOHOSTOS),linux darwin)) From 7ed45232224c2722ad677589452579095a42e4fe Mon Sep 17 00:00:00 2001 From: Matthieu MOREL Date: Sat, 16 Nov 2024 10:26:23 +0100 Subject: [PATCH 12/48] Allow custom user-agent definition (#725) * Allow custom user-agent definition --------- Signed-off-by: Matthieu MOREL Co-authored-by: Ben Kochie --- version/info.go | 8 ++++++-- version/info_test.go | 28 ++++++++++++++++++++++++++++ 2 files changed, 34 insertions(+), 2 deletions(-) create mode 100644 version/info_test.go diff --git a/version/info.go b/version/info.go index 3ca72ab3d..61ed1ba31 100644 --- a/version/info.go +++ b/version/info.go @@ -90,8 +90,12 @@ func GetTags() string { return computedTags } -func UserAgent() string { - return "Prometheus/" + Version +func PrometheusUserAgent() string { + return ComponentUserAgent("Prometheus") +} + +func ComponentUserAgent(component string) string { + return component + "/" + Version } func init() { diff --git a/version/info_test.go b/version/info_test.go new file mode 100644 index 000000000..a3689db9e --- /dev/null +++ b/version/info_test.go @@ -0,0 +1,28 @@ +// Copyright 2024 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 version + +import ( + "testing" + + "github.com/stretchr/testify/require" +) + +func TestPrometheusUserAgent(t *testing.T) { + require.Equal(t, "Prometheus/"+Version, PrometheusUserAgent()) +} + +func TestComponentUserAgent(t *testing.T) { + require.Equal(t, "Component/"+Version, ComponentUserAgent("Component")) +} From 39a62f7ded818701bb7c79d88119d6a1c9e72154 Mon Sep 17 00:00:00 2001 From: Owen Williams Date: Wed, 27 Nov 2024 13:50:08 -0500 Subject: [PATCH 13/48] fix: values escaping bugs (#727) Issues with underscores and large unicode value conversion Signed-off-by: Owen Williams --- model/metric.go | 28 ++++++++++------------------ model/metric_test.go | 32 ++++++++++++++++++++++++++++---- 2 files changed, 38 insertions(+), 22 deletions(-) diff --git a/model/metric.go b/model/metric.go index 12593c7ca..0daca836a 100644 --- a/model/metric.go +++ b/model/metric.go @@ -18,6 +18,7 @@ import ( "fmt" "regexp" "sort" + "strconv" "strings" "unicode/utf8" @@ -270,10 +271,6 @@ func metricNeedsEscaping(m *dto.Metric) bool { return false } -const ( - lowerhex = "0123456789abcdef" -) - // EscapeName escapes the incoming name according to the provided escaping // scheme. Depending on the rules of escaping, this may cause no change in the // string that is returned. (Especially NoEscaping, which by definition is a @@ -308,7 +305,7 @@ func EscapeName(name string, scheme EscapingScheme) string { } else if isValidLegacyRune(b, i) { escaped.WriteRune(b) } else { - escaped.WriteRune('_') + escaped.WriteString("__") } } return escaped.String() @@ -318,21 +315,15 @@ func EscapeName(name string, scheme EscapingScheme) string { } escaped.WriteString("U__") for i, b := range name { - if isValidLegacyRune(b, i) { + if b == '_' { + escaped.WriteString("__") + } else if isValidLegacyRune(b, i) { escaped.WriteRune(b) } else if !utf8.ValidRune(b) { escaped.WriteString("_FFFD_") - } else if b < 0x100 { - escaped.WriteRune('_') - for s := 4; s >= 0; s -= 4 { - escaped.WriteByte(lowerhex[b>>uint(s)&0xF]) - } - escaped.WriteRune('_') - } else if b < 0x10000 { + } else { escaped.WriteRune('_') - for s := 12; s >= 0; s -= 4 { - escaped.WriteByte(lowerhex[b>>uint(s)&0xF]) - } + escaped.WriteString(strconv.FormatInt(int64(b), 16)) escaped.WriteRune('_') } } @@ -390,8 +381,9 @@ func UnescapeName(name string, scheme EscapingScheme) string { // We think we are in a UTF-8 code, process it. var utf8Val uint for j := 0; i < len(escapedName); j++ { - // This is too many characters for a utf8 value. - if j > 4 { + // This is too many characters for a utf8 value based on the MaxRune + // value of '\U0010FFFF'. + if j >= 6 { return name } // Found a closing underscore, convert to a rune, check validity, and append. diff --git a/model/metric_test.go b/model/metric_test.go index ef08a053d..6152c5481 100644 --- a/model/metric_test.go +++ b/model/metric_test.go @@ -261,6 +261,14 @@ func TestEscapeName(t *testing.T) { expectedUnescapedDots: "mysystem.prod.west.cpu.load", expectedValue: "U__mysystem_2e_prod_2e_west_2e_cpu_2e_load", }, + { + name: "name with dots and underscore", + input: "mysystem.prod.west.cpu.load_total", + expectedUnderscores: "mysystem_prod_west_cpu_load_total", + expectedDots: "mysystem_dot_prod_dot_west_dot_cpu_dot_load__total", + expectedUnescapedDots: "mysystem.prod.west.cpu.load_total", + expectedValue: "U__mysystem_2e_prod_2e_west_2e_cpu_2e_load__total", + }, { name: "name with dots and colon", input: "http.status:sum", @@ -269,16 +277,32 @@ func TestEscapeName(t *testing.T) { expectedUnescapedDots: "http.status:sum", expectedValue: "U__http_2e_status:sum", }, + { + name: "name with spaces and emoji", + input: "label with 😱", + expectedUnderscores: "label_with__", + expectedDots: "label__with____", + expectedUnescapedDots: "label_with__", + expectedValue: "U__label_20_with_20__1f631_", + }, { name: "name with unicode characters > 0x100", input: "花火", expectedUnderscores: "__", - expectedDots: "__", + expectedDots: "____", // Dots-replacement does not know the difference between two replaced // characters and a single underscore. - expectedUnescapedDots: "_", + expectedUnescapedDots: "__", expectedValue: "U___82b1__706b_", }, + { + name: "name with spaces and edge-case value", + input: "label with \u0100", + expectedUnderscores: "label_with__", + expectedDots: "label__with____", + expectedUnescapedDots: "label_with__", + expectedValue: "U__label_20_with_20__100_", + }, } for _, scenario := range scenarios { @@ -564,7 +588,7 @@ func TestEscapeMetricFamily(t *testing.T) { }, }, expected: &dto.MetricFamily{ - Name: proto.String("unicode_dot_and_dot_dots_dot___"), + Name: proto.String("unicode_dot_and_dot_dots_dot_____"), Help: proto.String("some help text"), Type: dto.MetricType_GAUGE.Enum(), Metric: []*dto.Metric{ @@ -575,7 +599,7 @@ func TestEscapeMetricFamily(t *testing.T) { Label: []*dto.LabelPair{ { Name: proto.String("__name__"), - Value: proto.String("unicode_dot_and_dot_dots_dot___"), + Value: proto.String("unicode_dot_and_dot_dots_dot_____"), }, { Name: proto.String("some_label"), From 145b50adb4ece06ea09a5b0abcbedb76bd1b123a Mon Sep 17 00:00:00 2001 From: TJ Hoplock <33664289+tjhop@users.noreply.github.com> Date: Wed, 4 Dec 2024 16:38:42 -0500 Subject: [PATCH 14/48] fix(promslog): always use UTC for time (#735) @superq and I have both called this out in a few places; finally remembering to fix it. Prometheus has a UTC policy for logging because UTC is also used for metrics and so they should correlate. Before: ``` === RUN TestDynamicLevels/slog_log_style time=2024-12-02T13:51:08.463-05:00 level=INFO source=slog_test.go:120 msg=info hello=world time=2024-12-02T13:51:08.463-05:00 level=DEBUG source=slog_test.go:121 msg=debug hello=world ``` After: ``` === RUN TestDynamicLevels/slog_log_style time=2024-12-04T19:51:59.813Z level=INFO source=slog_test.go:120 msg=info hello=world time=2024-12-04T19:51:59.813Z level=DEBUG source=slog_test.go:121 msg=debug hello=world ``` Signed-off-by: TJ Hoplock --- promslog/slog.go | 17 ++++++++++------- 1 file changed, 10 insertions(+), 7 deletions(-) diff --git a/promslog/slog.go b/promslog/slog.go index 1677605af..c20c2f411 100644 --- a/promslog/slog.go +++ b/promslog/slog.go @@ -68,13 +68,16 @@ var ( return a } - truncateSourceAttrFunc = func(groups []string, a slog.Attr) slog.Attr { - if a.Key != slog.SourceKey { - return a - } - - if src, ok := a.Value.Any().(*slog.Source); ok { + defaultReplaceAttrFunc = func(groups []string, a slog.Attr) slog.Attr { + key := a.Key + switch key { + case slog.TimeKey: + t := a.Value.Time() + a.Value = slog.TimeValue(t.UTC()) + case slog.SourceKey: + src, _ := a.Value.Any().(*slog.Source) a.Value = slog.StringValue(filepath.Base(src.File) + ":" + strconv.Itoa(src.Line)) + default: } return a @@ -178,7 +181,7 @@ func New(config *Config) *slog.Logger { logHandlerOpts := &slog.HandlerOptions{ Level: config.Level.lvl, AddSource: true, - ReplaceAttr: truncateSourceAttrFunc, + ReplaceAttr: defaultReplaceAttrFunc, } if config.Style == GoKitStyle { From ec7291f51d0897a24d460e5b9d3640f78b37bdeb Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Wed, 4 Dec 2024 22:38:59 +0100 Subject: [PATCH 15/48] Bump github.com/stretchr/testify from 1.9.0 to 1.10.0 in /assets (#729) Bumps [github.com/stretchr/testify](https://github.com/stretchr/testify) from 1.9.0 to 1.10.0. - [Release notes](https://github.com/stretchr/testify/releases) - [Commits](https://github.com/stretchr/testify/compare/v1.9.0...v1.10.0) --- updated-dependencies: - dependency-name: github.com/stretchr/testify dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- assets/go.mod | 2 +- assets/go.sum | 5 +++-- 2 files changed, 4 insertions(+), 3 deletions(-) diff --git a/assets/go.mod b/assets/go.mod index ec1105bff..6e1afe3f6 100644 --- a/assets/go.mod +++ b/assets/go.mod @@ -2,7 +2,7 @@ module github.com/prometheus/common/assets go 1.21 -require github.com/stretchr/testify v1.9.0 +require github.com/stretchr/testify v1.10.0 require ( github.com/davecgh/go-spew v1.1.1 // indirect diff --git a/assets/go.sum b/assets/go.sum index e20fa14b0..713a0b4f0 100644 --- a/assets/go.sum +++ b/assets/go.sum @@ -2,8 +2,9 @@ github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= -github.com/stretchr/testify v1.9.0 h1:HtqpIVDClZ4nwg75+f6Lvsy/wHu+3BoSGCbBAcpTsTg= -github.com/stretchr/testify v1.9.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY= +github.com/stretchr/testify v1.10.0 h1:Xv5erBjTwe/5IxqUQTdXv5kgmIvbHo3QQyRwhJsOfJA= +github.com/stretchr/testify v1.10.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY= +gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= From 2c3c048674edaf6067181aaeae0496d8234d9b02 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Wed, 4 Dec 2024 23:25:49 +0100 Subject: [PATCH 16/48] Bump golang.org/x/oauth2 from 0.23.0 to 0.24.0 (#730) * Bump golang.org/x/oauth2 from 0.23.0 to 0.24.0 Bumps [golang.org/x/oauth2](https://github.com/golang/oauth2) from 0.23.0 to 0.24.0. - [Commits](https://github.com/golang/oauth2/compare/v0.23.0...v0.24.0) --- updated-dependencies: - dependency-name: golang.org/x/oauth2 dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] * Go mod tidy in sigv4. Signed-off-by: SuperQ --------- Signed-off-by: dependabot[bot] Signed-off-by: SuperQ Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> Co-authored-by: SuperQ --- go.mod | 2 +- go.sum | 4 ++-- sigv4/go.mod | 2 +- sigv4/go.sum | 4 ++-- 4 files changed, 6 insertions(+), 6 deletions(-) diff --git a/go.mod b/go.mod index 6c87d62ec..3e89b1f50 100644 --- a/go.mod +++ b/go.mod @@ -12,7 +12,7 @@ require ( github.com/prometheus/client_model v0.6.1 github.com/stretchr/testify v1.9.0 golang.org/x/net v0.30.0 - golang.org/x/oauth2 v0.23.0 + golang.org/x/oauth2 v0.24.0 google.golang.org/protobuf v1.35.1 gopkg.in/yaml.v2 v2.4.0 ) diff --git a/go.sum b/go.sum index 395902cd4..235e85a48 100644 --- a/go.sum +++ b/go.sum @@ -49,8 +49,8 @@ github.com/xhit/go-str2duration/v2 v2.1.0 h1:lxklc02Drh6ynqX+DdPyp5pCKLUQpRT8bp8 github.com/xhit/go-str2duration/v2 v2.1.0/go.mod h1:ohY8p+0f07DiV6Em5LKB0s2YpLtXVyJfNt1+BlmyAsU= golang.org/x/net v0.30.0 h1:AcW1SDZMkb8IpzCdQUaIq2sP4sZ4zw+55h6ynffypl4= golang.org/x/net v0.30.0/go.mod h1:2wGyMJ5iFasEhkwi13ChkO/t1ECNC4X4eBKkVFyYFlU= -golang.org/x/oauth2 v0.23.0 h1:PbgcYx2W7i4LvjJWEbf0ngHV6qJYr86PkAV3bXdLEbs= -golang.org/x/oauth2 v0.23.0/go.mod h1:XYTD2NtWslqkgxebSiOHnXEap4TF09sJSc7H1sXbhtI= +golang.org/x/oauth2 v0.24.0 h1:KTBBxWqUa0ykRPLtV69rRto9TLXcqYkeswu48x/gvNE= +golang.org/x/oauth2 v0.24.0/go.mod h1:XYTD2NtWslqkgxebSiOHnXEap4TF09sJSc7H1sXbhtI= golang.org/x/sys v0.26.0 h1:KHjCJyddX0LoSTb3J+vWpupP9p0oznkqVk/IfjymZbo= golang.org/x/sys v0.26.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= golang.org/x/text v0.19.0 h1:kTxAhCbGbxhK0IwgSKiMO5awPoDQ0RpfiVYBfK860YM= diff --git a/sigv4/go.mod b/sigv4/go.mod index a4d6d2754..531dcfe68 100644 --- a/sigv4/go.mod +++ b/sigv4/go.mod @@ -25,7 +25,7 @@ require ( github.com/prometheus/client_model v0.6.1 // indirect github.com/prometheus/procfs v0.15.1 // indirect golang.org/x/net v0.30.0 // indirect - golang.org/x/oauth2 v0.23.0 // indirect + golang.org/x/oauth2 v0.24.0 // indirect golang.org/x/sys v0.26.0 // indirect golang.org/x/text v0.19.0 // indirect google.golang.org/protobuf v1.35.1 // indirect diff --git a/sigv4/go.sum b/sigv4/go.sum index 32025ad19..79c16da83 100644 --- a/sigv4/go.sum +++ b/sigv4/go.sum @@ -41,8 +41,8 @@ github.com/stretchr/testify v1.9.0 h1:HtqpIVDClZ4nwg75+f6Lvsy/wHu+3BoSGCbBAcpTsT github.com/stretchr/testify v1.9.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY= golang.org/x/net v0.30.0 h1:AcW1SDZMkb8IpzCdQUaIq2sP4sZ4zw+55h6ynffypl4= golang.org/x/net v0.30.0/go.mod h1:2wGyMJ5iFasEhkwi13ChkO/t1ECNC4X4eBKkVFyYFlU= -golang.org/x/oauth2 v0.23.0 h1:PbgcYx2W7i4LvjJWEbf0ngHV6qJYr86PkAV3bXdLEbs= -golang.org/x/oauth2 v0.23.0/go.mod h1:XYTD2NtWslqkgxebSiOHnXEap4TF09sJSc7H1sXbhtI= +golang.org/x/oauth2 v0.24.0 h1:KTBBxWqUa0ykRPLtV69rRto9TLXcqYkeswu48x/gvNE= +golang.org/x/oauth2 v0.24.0/go.mod h1:XYTD2NtWslqkgxebSiOHnXEap4TF09sJSc7H1sXbhtI= golang.org/x/sys v0.26.0 h1:KHjCJyddX0LoSTb3J+vWpupP9p0oznkqVk/IfjymZbo= golang.org/x/sys v0.26.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= golang.org/x/text v0.19.0 h1:kTxAhCbGbxhK0IwgSKiMO5awPoDQ0RpfiVYBfK860YM= From b88f24cd1d8e9dc895a67e5de83aa15b75cb1243 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jan-Otto=20Kr=C3=B6pke?= Date: Wed, 4 Dec 2024 23:26:58 +0100 Subject: [PATCH 17/48] promslog: always lowercase log level from CLI (#728) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Jan-Otto Kröpke --- promslog/slog.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/promslog/slog.go b/promslog/slog.go index c20c2f411..6e8fbabce 100644 --- a/promslog/slog.go +++ b/promslog/slog.go @@ -118,7 +118,7 @@ func (l *AllowedLevel) Set(s string) error { l.lvl = &slog.LevelVar{} } - switch s { + switch strings.ToLower(s) { case "debug": l.lvl.Set(slog.LevelDebug) callerAddFunc = true From f99f029bc33e969a04aa799cb0e3fb96ab55ac7c Mon Sep 17 00:00:00 2001 From: PrometheusBot Date: Wed, 4 Dec 2024 23:27:18 +0100 Subject: [PATCH 18/48] Update common Prometheus files (#726) Signed-off-by: prombot --- .github/workflows/golangci-lint.yml | 2 +- Makefile.common | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/golangci-lint.yml b/.github/workflows/golangci-lint.yml index 305146993..e645ba30a 100644 --- a/.github/workflows/golangci-lint.yml +++ b/.github/workflows/golangci-lint.yml @@ -36,4 +36,4 @@ jobs: uses: golangci/golangci-lint-action@971e284b6050e8a5849b72094c50ab08da042db8 # v6.1.1 with: args: --verbose - version: v1.61.0 + version: v1.62.0 diff --git a/Makefile.common b/Makefile.common index 09e5bff85..fc47bdbb2 100644 --- a/Makefile.common +++ b/Makefile.common @@ -61,7 +61,7 @@ PROMU_URL := https://github.com/prometheus/promu/releases/download/v$(PROMU_ SKIP_GOLANGCI_LINT := GOLANGCI_LINT := GOLANGCI_LINT_OPTS ?= -GOLANGCI_LINT_VERSION ?= v1.61.0 +GOLANGCI_LINT_VERSION ?= v1.62.0 # golangci-lint only supports linux, darwin and windows platforms on i386/amd64/arm64. # windows isn't included here because of the path separator being different. ifeq ($(GOHOSTOS),$(filter $(GOHOSTOS),linux darwin)) From a0ef7371c3226c0353f663af2779e573dce2bd89 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Wed, 4 Dec 2024 23:42:15 +0100 Subject: [PATCH 19/48] Bump golang.org/x/net from 0.30.0 to 0.32.0 (#736) * Bump golang.org/x/net from 0.30.0 to 0.32.0 Bumps [golang.org/x/net](https://github.com/golang/net) from 0.30.0 to 0.32.0. - [Commits](https://github.com/golang/net/compare/v0.30.0...v0.32.0) --- updated-dependencies: - dependency-name: golang.org/x/net dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] * Go mod tidy in sigv4. Signed-off-by: SuperQ --------- Signed-off-by: dependabot[bot] Signed-off-by: SuperQ Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> Co-authored-by: SuperQ --- go.mod | 6 +++--- go.sum | 12 ++++++------ sigv4/go.mod | 6 +++--- sigv4/go.sum | 12 ++++++------ 4 files changed, 18 insertions(+), 18 deletions(-) diff --git a/go.mod b/go.mod index 3e89b1f50..22115e0ba 100644 --- a/go.mod +++ b/go.mod @@ -11,7 +11,7 @@ require ( github.com/mwitkow/go-conntrack v0.0.0-20190716064945-2f068394615f github.com/prometheus/client_model v0.6.1 github.com/stretchr/testify v1.9.0 - golang.org/x/net v0.30.0 + golang.org/x/net v0.32.0 golang.org/x/oauth2 v0.24.0 google.golang.org/protobuf v1.35.1 gopkg.in/yaml.v2 v2.4.0 @@ -29,8 +29,8 @@ require ( github.com/prometheus/procfs v0.15.1 // indirect github.com/rogpeppe/go-internal v1.10.0 // indirect github.com/xhit/go-str2duration/v2 v2.1.0 // indirect - golang.org/x/sys v0.26.0 // indirect - golang.org/x/text v0.19.0 // indirect + golang.org/x/sys v0.28.0 // indirect + golang.org/x/text v0.21.0 // indirect gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c // indirect gopkg.in/yaml.v3 v3.0.1 // indirect ) diff --git a/go.sum b/go.sum index 235e85a48..2ab5e6f29 100644 --- a/go.sum +++ b/go.sum @@ -47,14 +47,14 @@ github.com/stretchr/testify v1.9.0 h1:HtqpIVDClZ4nwg75+f6Lvsy/wHu+3BoSGCbBAcpTsT github.com/stretchr/testify v1.9.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY= github.com/xhit/go-str2duration/v2 v2.1.0 h1:lxklc02Drh6ynqX+DdPyp5pCKLUQpRT8bp8Ydu2Bstc= github.com/xhit/go-str2duration/v2 v2.1.0/go.mod h1:ohY8p+0f07DiV6Em5LKB0s2YpLtXVyJfNt1+BlmyAsU= -golang.org/x/net v0.30.0 h1:AcW1SDZMkb8IpzCdQUaIq2sP4sZ4zw+55h6ynffypl4= -golang.org/x/net v0.30.0/go.mod h1:2wGyMJ5iFasEhkwi13ChkO/t1ECNC4X4eBKkVFyYFlU= +golang.org/x/net v0.32.0 h1:ZqPmj8Kzc+Y6e0+skZsuACbx+wzMgo5MQsJh9Qd6aYI= +golang.org/x/net v0.32.0/go.mod h1:CwU0IoeOlnQQWJ6ioyFrfRuomB8GKF6KbYXZVyeXNfs= golang.org/x/oauth2 v0.24.0 h1:KTBBxWqUa0ykRPLtV69rRto9TLXcqYkeswu48x/gvNE= golang.org/x/oauth2 v0.24.0/go.mod h1:XYTD2NtWslqkgxebSiOHnXEap4TF09sJSc7H1sXbhtI= -golang.org/x/sys v0.26.0 h1:KHjCJyddX0LoSTb3J+vWpupP9p0oznkqVk/IfjymZbo= -golang.org/x/sys v0.26.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= -golang.org/x/text v0.19.0 h1:kTxAhCbGbxhK0IwgSKiMO5awPoDQ0RpfiVYBfK860YM= -golang.org/x/text v0.19.0/go.mod h1:BuEKDfySbSR4drPmRPG/7iBdf8hvFMuRexcpahXilzY= +golang.org/x/sys v0.28.0 h1:Fksou7UEQUWlKvIdsqzJmUmCX3cZuD2+P3XyyzwMhlA= +golang.org/x/sys v0.28.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= +golang.org/x/text v0.21.0 h1:zyQAAkrwaneQ066sspRyJaG9VNi/YJ1NfzcGB3hZ/qo= +golang.org/x/text v0.21.0/go.mod h1:4IBbMaMmOPCJ8SecivzSH54+73PCFmPWxNTLm+vZkEQ= google.golang.org/protobuf v1.35.1 h1:m3LfL6/Ca+fqnjnlqQXNpFPABW1UD7mjh8KO2mKFytA= google.golang.org/protobuf v1.35.1/go.mod h1:9fA7Ob0pmnwhb644+1+CVWFRbNajQ6iRojtC/QF5bRE= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= diff --git a/sigv4/go.mod b/sigv4/go.mod index 531dcfe68..dc2a856e3 100644 --- a/sigv4/go.mod +++ b/sigv4/go.mod @@ -24,10 +24,10 @@ require ( github.com/prometheus/client_golang v1.20.4 // indirect github.com/prometheus/client_model v0.6.1 // indirect github.com/prometheus/procfs v0.15.1 // indirect - golang.org/x/net v0.30.0 // indirect + golang.org/x/net v0.32.0 // indirect golang.org/x/oauth2 v0.24.0 // indirect - golang.org/x/sys v0.26.0 // indirect - golang.org/x/text v0.19.0 // indirect + golang.org/x/sys v0.28.0 // indirect + golang.org/x/text v0.21.0 // indirect google.golang.org/protobuf v1.35.1 // indirect gopkg.in/yaml.v3 v3.0.1 // indirect ) diff --git a/sigv4/go.sum b/sigv4/go.sum index 79c16da83..19bdc0765 100644 --- a/sigv4/go.sum +++ b/sigv4/go.sum @@ -39,14 +39,14 @@ github.com/rogpeppe/go-internal v1.10.0/go.mod h1:UQnix2H7Ngw/k4C5ijL5+65zddjncj github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= github.com/stretchr/testify v1.9.0 h1:HtqpIVDClZ4nwg75+f6Lvsy/wHu+3BoSGCbBAcpTsTg= github.com/stretchr/testify v1.9.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY= -golang.org/x/net v0.30.0 h1:AcW1SDZMkb8IpzCdQUaIq2sP4sZ4zw+55h6ynffypl4= -golang.org/x/net v0.30.0/go.mod h1:2wGyMJ5iFasEhkwi13ChkO/t1ECNC4X4eBKkVFyYFlU= +golang.org/x/net v0.32.0 h1:ZqPmj8Kzc+Y6e0+skZsuACbx+wzMgo5MQsJh9Qd6aYI= +golang.org/x/net v0.32.0/go.mod h1:CwU0IoeOlnQQWJ6ioyFrfRuomB8GKF6KbYXZVyeXNfs= golang.org/x/oauth2 v0.24.0 h1:KTBBxWqUa0ykRPLtV69rRto9TLXcqYkeswu48x/gvNE= golang.org/x/oauth2 v0.24.0/go.mod h1:XYTD2NtWslqkgxebSiOHnXEap4TF09sJSc7H1sXbhtI= -golang.org/x/sys v0.26.0 h1:KHjCJyddX0LoSTb3J+vWpupP9p0oznkqVk/IfjymZbo= -golang.org/x/sys v0.26.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= -golang.org/x/text v0.19.0 h1:kTxAhCbGbxhK0IwgSKiMO5awPoDQ0RpfiVYBfK860YM= -golang.org/x/text v0.19.0/go.mod h1:BuEKDfySbSR4drPmRPG/7iBdf8hvFMuRexcpahXilzY= +golang.org/x/sys v0.28.0 h1:Fksou7UEQUWlKvIdsqzJmUmCX3cZuD2+P3XyyzwMhlA= +golang.org/x/sys v0.28.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= +golang.org/x/text v0.21.0 h1:zyQAAkrwaneQ066sspRyJaG9VNi/YJ1NfzcGB3hZ/qo= +golang.org/x/text v0.21.0/go.mod h1:4IBbMaMmOPCJ8SecivzSH54+73PCFmPWxNTLm+vZkEQ= google.golang.org/protobuf v1.35.1 h1:m3LfL6/Ca+fqnjnlqQXNpFPABW1UD7mjh8KO2mKFytA= google.golang.org/protobuf v1.35.1/go.mod h1:9fA7Ob0pmnwhb644+1+CVWFRbNajQ6iRojtC/QF5bRE= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= From 05e3c4037f304834166c34a305a0658ef5b8db4c Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Wed, 4 Dec 2024 23:55:11 +0100 Subject: [PATCH 20/48] Bump github.com/stretchr/testify from 1.9.0 to 1.10.0 (#731) * Bump github.com/stretchr/testify from 1.9.0 to 1.10.0 Bumps [github.com/stretchr/testify](https://github.com/stretchr/testify) from 1.9.0 to 1.10.0. - [Release notes](https://github.com/stretchr/testify/releases) - [Commits](https://github.com/stretchr/testify/compare/v1.9.0...v1.10.0) --- updated-dependencies: - dependency-name: github.com/stretchr/testify dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] * Go mod tidy in sigv4. Signed-off-by: SuperQ --------- Signed-off-by: dependabot[bot] Signed-off-by: SuperQ Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> Co-authored-by: SuperQ --- go.mod | 2 +- go.sum | 4 ++-- sigv4/go.mod | 2 +- sigv4/go.sum | 4 ++-- 4 files changed, 6 insertions(+), 6 deletions(-) diff --git a/go.mod b/go.mod index 22115e0ba..8f8f26df9 100644 --- a/go.mod +++ b/go.mod @@ -10,7 +10,7 @@ require ( github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822 github.com/mwitkow/go-conntrack v0.0.0-20190716064945-2f068394615f github.com/prometheus/client_model v0.6.1 - github.com/stretchr/testify v1.9.0 + github.com/stretchr/testify v1.10.0 golang.org/x/net v0.32.0 golang.org/x/oauth2 v0.24.0 google.golang.org/protobuf v1.35.1 diff --git a/go.sum b/go.sum index 2ab5e6f29..1554acfde 100644 --- a/go.sum +++ b/go.sum @@ -43,8 +43,8 @@ github.com/rogpeppe/go-internal v1.10.0 h1:TMyTOH3F/DB16zRVcYyreMH6GnZZrwQVAoYjR github.com/rogpeppe/go-internal v1.10.0/go.mod h1:UQnix2H7Ngw/k4C5ijL5+65zddjncjaFoBhdsK/akog= github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4= -github.com/stretchr/testify v1.9.0 h1:HtqpIVDClZ4nwg75+f6Lvsy/wHu+3BoSGCbBAcpTsTg= -github.com/stretchr/testify v1.9.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY= +github.com/stretchr/testify v1.10.0 h1:Xv5erBjTwe/5IxqUQTdXv5kgmIvbHo3QQyRwhJsOfJA= +github.com/stretchr/testify v1.10.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY= github.com/xhit/go-str2duration/v2 v2.1.0 h1:lxklc02Drh6ynqX+DdPyp5pCKLUQpRT8bp8Ydu2Bstc= github.com/xhit/go-str2duration/v2 v2.1.0/go.mod h1:ohY8p+0f07DiV6Em5LKB0s2YpLtXVyJfNt1+BlmyAsU= golang.org/x/net v0.32.0 h1:ZqPmj8Kzc+Y6e0+skZsuACbx+wzMgo5MQsJh9Qd6aYI= diff --git a/sigv4/go.mod b/sigv4/go.mod index dc2a856e3..9d086fc83 100644 --- a/sigv4/go.mod +++ b/sigv4/go.mod @@ -7,7 +7,7 @@ replace github.com/prometheus/common => ../ require ( github.com/aws/aws-sdk-go v1.55.5 github.com/prometheus/common v0.59.1 - github.com/stretchr/testify v1.9.0 + github.com/stretchr/testify v1.10.0 gopkg.in/yaml.v2 v2.4.0 ) diff --git a/sigv4/go.sum b/sigv4/go.sum index 19bdc0765..614e13579 100644 --- a/sigv4/go.sum +++ b/sigv4/go.sum @@ -37,8 +37,8 @@ github.com/prometheus/procfs v0.15.1/go.mod h1:fB45yRUv8NstnjriLhBQLuOUt+WW4BsoG github.com/rogpeppe/go-internal v1.10.0 h1:TMyTOH3F/DB16zRVcYyreMH6GnZZrwQVAoYjRBZyWFQ= github.com/rogpeppe/go-internal v1.10.0/go.mod h1:UQnix2H7Ngw/k4C5ijL5+65zddjncjaFoBhdsK/akog= github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= -github.com/stretchr/testify v1.9.0 h1:HtqpIVDClZ4nwg75+f6Lvsy/wHu+3BoSGCbBAcpTsTg= -github.com/stretchr/testify v1.9.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY= +github.com/stretchr/testify v1.10.0 h1:Xv5erBjTwe/5IxqUQTdXv5kgmIvbHo3QQyRwhJsOfJA= +github.com/stretchr/testify v1.10.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY= golang.org/x/net v0.32.0 h1:ZqPmj8Kzc+Y6e0+skZsuACbx+wzMgo5MQsJh9Qd6aYI= golang.org/x/net v0.32.0/go.mod h1:CwU0IoeOlnQQWJ6ioyFrfRuomB8GKF6KbYXZVyeXNfs= golang.org/x/oauth2 v0.24.0 h1:KTBBxWqUa0ykRPLtV69rRto9TLXcqYkeswu48x/gvNE= From 7b484e97ca04d69d5db9a46326f399f230e1e4e0 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Thu, 5 Dec 2024 00:03:19 +0100 Subject: [PATCH 21/48] Bump google.golang.org/protobuf from 1.35.1 to 1.35.2 (#732) * Bump google.golang.org/protobuf from 1.35.1 to 1.35.2 Bumps google.golang.org/protobuf from 1.35.1 to 1.35.2. --- updated-dependencies: - dependency-name: google.golang.org/protobuf dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] * Go mod tidy in sigv4. Signed-off-by: SuperQ --------- Signed-off-by: dependabot[bot] Signed-off-by: SuperQ Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> Co-authored-by: SuperQ --- go.mod | 2 +- go.sum | 4 ++-- sigv4/go.mod | 2 +- sigv4/go.sum | 4 ++-- 4 files changed, 6 insertions(+), 6 deletions(-) diff --git a/go.mod b/go.mod index 8f8f26df9..87b897618 100644 --- a/go.mod +++ b/go.mod @@ -13,7 +13,7 @@ require ( github.com/stretchr/testify v1.10.0 golang.org/x/net v0.32.0 golang.org/x/oauth2 v0.24.0 - google.golang.org/protobuf v1.35.1 + google.golang.org/protobuf v1.35.2 gopkg.in/yaml.v2 v2.4.0 ) diff --git a/go.sum b/go.sum index 1554acfde..2bb9e8254 100644 --- a/go.sum +++ b/go.sum @@ -55,8 +55,8 @@ golang.org/x/sys v0.28.0 h1:Fksou7UEQUWlKvIdsqzJmUmCX3cZuD2+P3XyyzwMhlA= golang.org/x/sys v0.28.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= golang.org/x/text v0.21.0 h1:zyQAAkrwaneQ066sspRyJaG9VNi/YJ1NfzcGB3hZ/qo= golang.org/x/text v0.21.0/go.mod h1:4IBbMaMmOPCJ8SecivzSH54+73PCFmPWxNTLm+vZkEQ= -google.golang.org/protobuf v1.35.1 h1:m3LfL6/Ca+fqnjnlqQXNpFPABW1UD7mjh8KO2mKFytA= -google.golang.org/protobuf v1.35.1/go.mod h1:9fA7Ob0pmnwhb644+1+CVWFRbNajQ6iRojtC/QF5bRE= +google.golang.org/protobuf v1.35.2 h1:8Ar7bF+apOIoThw1EdZl0p1oWvMqTHmpA2fRTyZO8io= +google.golang.org/protobuf v1.35.2/go.mod h1:9fA7Ob0pmnwhb644+1+CVWFRbNajQ6iRojtC/QF5bRE= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk= gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q= diff --git a/sigv4/go.mod b/sigv4/go.mod index 9d086fc83..a751b7f15 100644 --- a/sigv4/go.mod +++ b/sigv4/go.mod @@ -28,6 +28,6 @@ require ( golang.org/x/oauth2 v0.24.0 // indirect golang.org/x/sys v0.28.0 // indirect golang.org/x/text v0.21.0 // indirect - google.golang.org/protobuf v1.35.1 // indirect + google.golang.org/protobuf v1.35.2 // indirect gopkg.in/yaml.v3 v3.0.1 // indirect ) diff --git a/sigv4/go.sum b/sigv4/go.sum index 614e13579..957af981d 100644 --- a/sigv4/go.sum +++ b/sigv4/go.sum @@ -47,8 +47,8 @@ golang.org/x/sys v0.28.0 h1:Fksou7UEQUWlKvIdsqzJmUmCX3cZuD2+P3XyyzwMhlA= golang.org/x/sys v0.28.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= golang.org/x/text v0.21.0 h1:zyQAAkrwaneQ066sspRyJaG9VNi/YJ1NfzcGB3hZ/qo= golang.org/x/text v0.21.0/go.mod h1:4IBbMaMmOPCJ8SecivzSH54+73PCFmPWxNTLm+vZkEQ= -google.golang.org/protobuf v1.35.1 h1:m3LfL6/Ca+fqnjnlqQXNpFPABW1UD7mjh8KO2mKFytA= -google.golang.org/protobuf v1.35.1/go.mod h1:9fA7Ob0pmnwhb644+1+CVWFRbNajQ6iRojtC/QF5bRE= +google.golang.org/protobuf v1.35.2 h1:8Ar7bF+apOIoThw1EdZl0p1oWvMqTHmpA2fRTyZO8io= +google.golang.org/protobuf v1.35.2/go.mod h1:9fA7Ob0pmnwhb644+1+CVWFRbNajQ6iRojtC/QF5bRE= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk= gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q= From cf5f48f181f5270eeac48e077e974ba0e00f5c58 Mon Sep 17 00:00:00 2001 From: Owen Williams Date: Thu, 5 Dec 2024 03:59:06 -0500 Subject: [PATCH 22/48] Change default validation scheme to UTF8Validation (#724) fixes https://github.com/prometheus/common/issues/723 Signed-off-by: Owen Williams --- model/alert_test.go | 5 +++++ model/metric.go | 14 +++++++------- 2 files changed, 12 insertions(+), 7 deletions(-) diff --git a/model/alert_test.go b/model/alert_test.go index 2a8d7bba1..fc3eaf108 100644 --- a/model/alert_test.go +++ b/model/alert_test.go @@ -22,6 +22,11 @@ import ( ) func TestAlertValidate(t *testing.T) { + oldScheme := NameValidationScheme + NameValidationScheme = LegacyValidation + defer func() { + NameValidationScheme = oldScheme + }() ts := time.Now() cases := []struct { diff --git a/model/metric.go b/model/metric.go index 0daca836a..5766107cf 100644 --- a/model/metric.go +++ b/model/metric.go @@ -28,13 +28,13 @@ import ( var ( // NameValidationScheme determines the method of name validation to be used by - // all calls to IsValidMetricName() and LabelName IsValid(). Setting UTF-8 mode - // in isolation from other components that don't support UTF-8 may result in - // bugs or other undefined behavior. This value is intended to be set by - // UTF-8-aware binaries as part of their startup. To avoid need for locking, - // this value should be set once, ideally in an init(), before multiple - // goroutines are started. - NameValidationScheme = LegacyValidation + // all calls to IsValidMetricName() and LabelName IsValid(). Setting UTF-8 + // mode in isolation from other components that don't support UTF-8 may result + // in bugs or other undefined behavior. This value can be set to + // LegacyValidation during startup if a binary is not UTF-8-aware binaries. To + // avoid need for locking, this value should be set once, ideally in an + // init(), before multiple goroutines are started. + NameValidationScheme = UTF8Validation // NameEscapingScheme defines the default way that names will be escaped when // presented to systems that do not support UTF-8 names. If the Content-Type From c3fdb171ceca4b3f86385f2f436c7e8cac676548 Mon Sep 17 00:00:00 2001 From: Ben Kochie Date: Thu, 5 Dec 2024 02:59:51 -0600 Subject: [PATCH 23/48] Remove deprecated promlog package (#738) Remove deprecated promlog package. This has been replaced by the promslog package. Signed-off-by: SuperQ --- README.md | 1 - go.mod | 2 - go.sum | 4 - promlog/flag/flag.go | 53 ------------ promlog/log.go | 196 ------------------------------------------- promlog/log_test.go | 113 ------------------------- 6 files changed, 369 deletions(-) delete mode 100644 promlog/flag/flag.go delete mode 100644 promlog/log.go delete mode 100644 promlog/log_test.go diff --git a/README.md b/README.md index 43ff7e75f..954cc91b4 100644 --- a/README.md +++ b/README.md @@ -9,7 +9,6 @@ any stability guarantees for external usage. * **config**: Common configuration structures * **expfmt**: Decoding and encoding for the exposition format * **model**: Shared data structures -* **promlog**: A logging wrapper around [go-kit/log](https://github.com/go-kit/kit/tree/master/log) * **promslog**: A logging wrapper around [log/slog](https://pkg.go.dev/log/slog) * **route**: A routing wrapper around [httprouter](https://github.com/julienschmidt/httprouter) using `context.Context` * **server**: Common servers diff --git a/go.mod b/go.mod index 87b897618..6f41dc5e0 100644 --- a/go.mod +++ b/go.mod @@ -4,7 +4,6 @@ go 1.21 require ( github.com/alecthomas/kingpin/v2 v2.4.0 - github.com/go-kit/log v0.2.1 github.com/google/go-cmp v0.6.0 github.com/julienschmidt/httprouter v1.3.0 github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822 @@ -22,7 +21,6 @@ require ( github.com/beorn7/perks v1.0.1 // indirect github.com/cespare/xxhash/v2 v2.3.0 // indirect github.com/davecgh/go-spew v1.1.1 // indirect - github.com/go-logfmt/logfmt v0.5.1 // indirect github.com/jpillora/backoff v1.0.0 // indirect github.com/pmezard/go-difflib v1.0.0 // indirect github.com/prometheus/client_golang v1.20.4 // indirect diff --git a/go.sum b/go.sum index 2bb9e8254..f374f9314 100644 --- a/go.sum +++ b/go.sum @@ -9,10 +9,6 @@ github.com/cespare/xxhash/v2 v2.3.0/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XL github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= -github.com/go-kit/log v0.2.1 h1:MRVx0/zhvdseW+Gza6N9rVzU/IVzaeE1SFI4raAhmBU= -github.com/go-kit/log v0.2.1/go.mod h1:NwTd00d/i8cPZ3xOwwiv2PO5MOcx78fFErGNcVmBjv0= -github.com/go-logfmt/logfmt v0.5.1 h1:otpy5pqBCBZ1ng9RQ0dPu4PN7ba75Y/aA+UpowDyNVA= -github.com/go-logfmt/logfmt v0.5.1/go.mod h1:WYhtIu8zTZfxdn5+rREduYbwxfcBr/Vr6KEVveWlfTs= github.com/google/go-cmp v0.6.0 h1:ofyhxvXcZhMsU5ulbFiLKl/XBFqE1GSq7atu8tAmTRI= github.com/google/go-cmp v0.6.0/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY= github.com/jpillora/backoff v1.0.0 h1:uvFg412JmmHBHw7iwprIxkPMI+sGQ4kzOWsMeHnm2EA= diff --git a/promlog/flag/flag.go b/promlog/flag/flag.go deleted file mode 100644 index 16352a0fe..000000000 --- a/promlog/flag/flag.go +++ /dev/null @@ -1,53 +0,0 @@ -// Copyright 2017 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. - -// Deprecated: This package has been deprecated in favor of migrating to -// `github.com/prometheus/common/promslog` which uses the Go standard library -// `log/slog` package. -package flag - -import ( - "strings" - - kingpin "github.com/alecthomas/kingpin/v2" - - "github.com/prometheus/common/promlog" //nolint:staticcheck -) - -// LevelFlagName is the canonical flag name to configure the allowed log level -// within Prometheus projects. -const LevelFlagName = "log.level" - -// LevelFlagHelp is the help description for the log.level flag. -var LevelFlagHelp = "Only log messages with the given severity or above. One of: [" + strings.Join(promlog.LevelFlagOptions, ", ") + "]" - -// FormatFlagName is the canonical flag name to configure the log format -// within Prometheus projects. -const FormatFlagName = "log.format" - -// FormatFlagHelp is the help description for the log.format flag. -var FormatFlagHelp = "Output format of log messages. One of: [" + strings.Join(promlog.FormatFlagOptions, ", ") + "]" - -// AddFlags adds the flags used by this package to the Kingpin application. -// To use the default Kingpin application, call AddFlags(kingpin.CommandLine) -func AddFlags(a *kingpin.Application, config *promlog.Config) { - config.Level = &promlog.AllowedLevel{} - a.Flag(LevelFlagName, LevelFlagHelp). - Default("info").HintOptions(promlog.LevelFlagOptions...). - SetValue(config.Level) - - config.Format = &promlog.AllowedFormat{} - a.Flag(FormatFlagName, FormatFlagHelp). - Default("logfmt").HintOptions(promlog.FormatFlagOptions...). - SetValue(config.Format) -} diff --git a/promlog/log.go b/promlog/log.go deleted file mode 100644 index 24a11622b..000000000 --- a/promlog/log.go +++ /dev/null @@ -1,196 +0,0 @@ -// Copyright 2017 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 promlog defines standardised ways to initialize Go kit loggers -// across Prometheus components. -// It should typically only ever be imported by main packages. -// -// Deprecated: This package has been deprecated in favor of migrating to -// `github.com/prometheus/common/promslog` which uses the Go standard library -// `log/slog` package. -package promlog - -import ( - "fmt" - "os" - "sync" - "time" - - "github.com/go-kit/log" - "github.com/go-kit/log/level" -) - -var ( - // This timestamp format differs from RFC3339Nano by using .000 instead - // of .999999999 which changes the timestamp from 9 variable to 3 fixed - // decimals (.130 instead of .130987456). - timestampFormat = log.TimestampFormat( - func() time.Time { return time.Now().UTC() }, - "2006-01-02T15:04:05.000Z07:00", - ) - - LevelFlagOptions = []string{"debug", "info", "warn", "error"} - FormatFlagOptions = []string{"logfmt", "json"} -) - -// AllowedLevel is a settable identifier for the minimum level a log entry -// must be have. -type AllowedLevel struct { - s string - o level.Option -} - -func (l *AllowedLevel) UnmarshalYAML(unmarshal func(interface{}) error) error { - var s string - type plain string - if err := unmarshal((*plain)(&s)); err != nil { - return err - } - if s == "" { - return nil - } - lo := &AllowedLevel{} - if err := lo.Set(s); err != nil { - return err - } - *l = *lo - return nil -} - -func (l *AllowedLevel) String() string { - return l.s -} - -// Set updates the value of the allowed level. -func (l *AllowedLevel) Set(s string) error { - switch s { - case "debug": - l.o = level.AllowDebug() - case "info": - l.o = level.AllowInfo() - case "warn": - l.o = level.AllowWarn() - case "error": - l.o = level.AllowError() - default: - return fmt.Errorf("unrecognized log level %q", s) - } - l.s = s - return nil -} - -// AllowedFormat is a settable identifier for the output format that the logger can have. -type AllowedFormat struct { - s string -} - -func (f *AllowedFormat) String() string { - return f.s -} - -// Set updates the value of the allowed format. -func (f *AllowedFormat) Set(s string) error { - switch s { - case "logfmt", "json": - f.s = s - default: - return fmt.Errorf("unrecognized log format %q", s) - } - return nil -} - -// Config is a struct containing configurable settings for the logger -type Config struct { - Level *AllowedLevel - Format *AllowedFormat -} - -// New returns a new leveled oklog logger. Each logged line will be annotated -// with a timestamp. The output always goes to stderr. -func New(config *Config) log.Logger { - if config.Format != nil && config.Format.s == "json" { - return NewWithLogger(log.NewJSONLogger(log.NewSyncWriter(os.Stderr)), config) - } - - return NewWithLogger(log.NewLogfmtLogger(log.NewSyncWriter(os.Stderr)), config) -} - -// NewWithLogger returns a new leveled oklog logger with a custom log.Logger. -// Each logged line will be annotated with a timestamp. -func NewWithLogger(l log.Logger, config *Config) log.Logger { - if config.Level != nil { - l = log.With(l, "ts", timestampFormat, "caller", log.Caller(5)) - l = level.NewFilter(l, config.Level.o) - } else { - l = log.With(l, "ts", timestampFormat, "caller", log.DefaultCaller) - } - return l -} - -// NewDynamic returns a new leveled logger. Each logged line will be annotated -// with a timestamp. The output always goes to stderr. Some properties can be -// changed, like the level. -func NewDynamic(config *Config) *logger { - if config.Format != nil && config.Format.s == "json" { - return NewDynamicWithLogger(log.NewJSONLogger(log.NewSyncWriter(os.Stderr)), config) - } - - return NewDynamicWithLogger(log.NewLogfmtLogger(log.NewSyncWriter(os.Stderr)), config) -} - -// NewDynamicWithLogger returns a new leveled logger with a custom io.Writer. -// Each logged line will be annotated with a timestamp. -// Some properties can be changed, like the level. -func NewDynamicWithLogger(l log.Logger, config *Config) *logger { - lo := &logger{ - base: l, - leveled: l, - } - - if config.Level != nil { - lo.SetLevel(config.Level) - } - - return lo -} - -type logger struct { - base log.Logger - leveled log.Logger - currentLevel *AllowedLevel - mtx sync.Mutex -} - -// Log implements logger.Log. -func (l *logger) Log(keyvals ...interface{}) error { - l.mtx.Lock() - defer l.mtx.Unlock() - return l.leveled.Log(keyvals...) -} - -// SetLevel changes the log level. -func (l *logger) SetLevel(lvl *AllowedLevel) { - l.mtx.Lock() - defer l.mtx.Unlock() - if lvl == nil { - l.leveled = log.With(l.base, "ts", timestampFormat, "caller", log.DefaultCaller) - l.currentLevel = nil - return - } - - if l.currentLevel != nil && l.currentLevel.s != lvl.s { - _ = l.base.Log("msg", "Log level changed", "prev", l.currentLevel, "current", lvl) - } - l.currentLevel = lvl - l.leveled = level.NewFilter(log.With(l.base, "ts", timestampFormat, "caller", log.Caller(5)), lvl.o) -} diff --git a/promlog/log_test.go b/promlog/log_test.go deleted file mode 100644 index 395e05164..000000000 --- a/promlog/log_test.go +++ /dev/null @@ -1,113 +0,0 @@ -// Copyright 2020 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 promlog - -import ( - "fmt" - "testing" - - "github.com/go-kit/log/level" - "github.com/stretchr/testify/require" - "gopkg.in/yaml.v2" -) - -// Make sure creating and using a logger with an empty configuration doesn't -// result in a panic. -func TestDefaultConfig(t *testing.T) { - logger := New(&Config{}) - - err := logger.Log("hello", "world") - require.NoError(t, err) -} - -func TestUnmarshallLevel(t *testing.T) { - l := &AllowedLevel{} - err := yaml.Unmarshal([]byte(`debug`), l) - if err != nil { - t.Error(err) - } - if l.s != "debug" { - t.Errorf("expected %s, got %s", "debug", l.s) - } -} - -func TestUnmarshallEmptyLevel(t *testing.T) { - l := &AllowedLevel{} - err := yaml.Unmarshal([]byte(``), l) - if err != nil { - t.Error(err) - } - if l.s != "" { - t.Errorf("expected empty level, got %s", l.s) - } -} - -func TestUnmarshallBadLevel(t *testing.T) { - l := &AllowedLevel{} - err := yaml.Unmarshal([]byte(`debugg`), l) - if err == nil { - t.Error("expected error") - } - expErr := `unrecognized log level "debugg"` - if err.Error() != expErr { - t.Errorf("expected error %s, got %s", expErr, err.Error()) - } - if l.s != "" { - t.Errorf("expected empty level, got %s", l.s) - } -} - -type recordKeyvalLogger struct { - count int -} - -func (r *recordKeyvalLogger) Log(keyvals ...interface{}) error { - for _, v := range keyvals { - if fmt.Sprintf("%v", v) == "Log level changed" { - return nil - } - } - r.count++ - return nil -} - -func TestDynamic(t *testing.T) { - logger := NewDynamic(&Config{}) - - debugLevel := &AllowedLevel{} - err := debugLevel.Set("debug") - require.NoError(t, err) - infoLevel := &AllowedLevel{} - err = infoLevel.Set("info") - require.NoError(t, err) - - recorder := &recordKeyvalLogger{} - logger.base = recorder - logger.SetLevel(debugLevel) - err = level.Debug(logger).Log("hello", "world") - require.NoError(t, err) - require.Equalf(t, 1, recorder.count, "log not found") - - recorder.count = 0 - logger.SetLevel(infoLevel) - err = level.Debug(logger).Log("hello", "world") - require.NoError(t, err) - require.Equalf(t, 0, recorder.count, "log found") - err = level.Info(logger).Log("hello", "world") - require.NoError(t, err) - require.Equalf(t, 1, recorder.count, "log not found") - err = level.Debug(logger).Log("hello", "world") - require.NoError(t, err) - require.Equalf(t, 1, recorder.count, "extra log found") -} From d88ee1f244f31e9376f4aa8ad865bd5c7dd4a814 Mon Sep 17 00:00:00 2001 From: Ben Kochie Date: Thu, 5 Dec 2024 03:00:09 -0600 Subject: [PATCH 24/48] Remove deprecated sigv4 module (#737) Remove deprecated sigv4 module, this has been migrated to https://github.com/prometheus/sigv4. Signed-off-by: SuperQ --- .circleci/config.yml | 2 - .github/dependabot.yml | 4 - sigv4/.yamllint | 27 ---- sigv4/Makefile | 17 --- sigv4/README.md | 12 -- sigv4/go.mod | 33 ----- sigv4/go.sum | 59 --------- sigv4/sigv4.go | 154 ---------------------- sigv4/sigv4_config.go | 48 ------- sigv4/sigv4_config_test.go | 53 -------- sigv4/sigv4_test.go | 117 ---------------- sigv4/testdata/sigv4_bad.yaml | 4 - sigv4/testdata/sigv4_good.yaml | 6 - sigv4/testdata/sigv4_good_empty_keys.yaml | 3 - 14 files changed, 539 deletions(-) delete mode 100644 sigv4/.yamllint delete mode 100644 sigv4/Makefile delete mode 100644 sigv4/README.md delete mode 100644 sigv4/go.mod delete mode 100644 sigv4/go.sum delete mode 100644 sigv4/sigv4.go delete mode 100644 sigv4/sigv4_config.go delete mode 100644 sigv4/sigv4_config_test.go delete mode 100644 sigv4/sigv4_test.go delete mode 100644 sigv4/testdata/sigv4_bad.yaml delete mode 100644 sigv4/testdata/sigv4_good.yaml delete mode 100644 sigv4/testdata/sigv4_good_empty_keys.yaml diff --git a/.circleci/config.yml b/.circleci/config.yml index 8e5fff950..8f035514d 100644 --- a/.circleci/config.yml +++ b/.circleci/config.yml @@ -25,7 +25,6 @@ jobs: - go/load-cache: key: v1-go<< parameters.go_version >> - run: make test - - run: make -C sigv4 test - when: condition: << parameters.use_gomod_cache >> steps: @@ -74,7 +73,6 @@ jobs: - go/load-cache: key: v1-go<< parameters.go_version >> - run: make style - - run: make -C sigv4 style - run: make -C assets style - run: make check-go-mod-version - when: diff --git a/.github/dependabot.yml b/.github/dependabot.yml index 9b7d29d83..a8d849c7c 100644 --- a/.github/dependabot.yml +++ b/.github/dependabot.yml @@ -8,7 +8,3 @@ updates: directory: "/assets" schedule: interval: monthly - - package-ecosystem: "gomod" - directory: "/sigv4" - schedule: - interval: monthly diff --git a/sigv4/.yamllint b/sigv4/.yamllint deleted file mode 100644 index 281c94646..000000000 --- a/sigv4/.yamllint +++ /dev/null @@ -1,27 +0,0 @@ ---- -extends: default - -rules: - braces: - max-spaces-inside: 1 - level: error - brackets: - max-spaces-inside: 1 - level: error - commas: disable - comments: disable - comments-indentation: disable - document-start: disable - indentation: - spaces: consistent - key-duplicates: - ignore: | - config/testdata/section_key_dup.bad.yml - line-length: disable - truthy: - ignore: | - .github/workflows/codeql-analysis.yml - .github/workflows/funcbench.yml - .github/workflows/fuzzing.yml - .github/workflows/prombench.yml - .github/workflows/golangci-lint.yml diff --git a/sigv4/Makefile b/sigv4/Makefile deleted file mode 100644 index 0b62dd325..000000000 --- a/sigv4/Makefile +++ /dev/null @@ -1,17 +0,0 @@ -# Copyright 2018 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. - -include ../Makefile.common - -.PHONY: test -test:: deps check_license unused common-test lint diff --git a/sigv4/README.md b/sigv4/README.md deleted file mode 100644 index 661680e64..000000000 --- a/sigv4/README.md +++ /dev/null @@ -1,12 +0,0 @@ -github.com/prometheus/common/sigv4 module -========================================= - -sigv4 provides a http.RoundTripper that will sign requests using -Amazon's Signature Verification V4 signing procedure, using credentials -from the default AWS credential chain. - -This is a separate module from github.com/prometheus/common to prevent -it from having and propagating a dependency on the AWS SDK. - -This module is considered internal to Prometheus, without any stability -guarantees for external usage. diff --git a/sigv4/go.mod b/sigv4/go.mod deleted file mode 100644 index a751b7f15..000000000 --- a/sigv4/go.mod +++ /dev/null @@ -1,33 +0,0 @@ -module github.com/prometheus/common/sigv4 - -go 1.21 - -replace github.com/prometheus/common => ../ - -require ( - github.com/aws/aws-sdk-go v1.55.5 - github.com/prometheus/common v0.59.1 - github.com/stretchr/testify v1.10.0 - gopkg.in/yaml.v2 v2.4.0 -) - -require ( - github.com/beorn7/perks v1.0.1 // indirect - github.com/cespare/xxhash/v2 v2.3.0 // indirect - github.com/davecgh/go-spew v1.1.1 // indirect - github.com/jmespath/go-jmespath v0.4.0 // indirect - github.com/jpillora/backoff v1.0.0 // indirect - github.com/kr/text v0.2.0 // indirect - github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822 // indirect - github.com/mwitkow/go-conntrack v0.0.0-20190716064945-2f068394615f // indirect - github.com/pmezard/go-difflib v1.0.0 // indirect - github.com/prometheus/client_golang v1.20.4 // indirect - github.com/prometheus/client_model v0.6.1 // indirect - github.com/prometheus/procfs v0.15.1 // indirect - golang.org/x/net v0.32.0 // indirect - golang.org/x/oauth2 v0.24.0 // indirect - golang.org/x/sys v0.28.0 // indirect - golang.org/x/text v0.21.0 // indirect - google.golang.org/protobuf v1.35.2 // indirect - gopkg.in/yaml.v3 v3.0.1 // indirect -) diff --git a/sigv4/go.sum b/sigv4/go.sum deleted file mode 100644 index 957af981d..000000000 --- a/sigv4/go.sum +++ /dev/null @@ -1,59 +0,0 @@ -github.com/aws/aws-sdk-go v1.55.5 h1:KKUZBfBoyqy5d3swXyiC7Q76ic40rYcbqH7qjh59kzU= -github.com/aws/aws-sdk-go v1.55.5/go.mod h1:eRwEWoyTWFMVYVQzKMNHWP5/RV4xIUGMQfXQHfHkpNU= -github.com/beorn7/perks v1.0.1 h1:VlbKKnNfV8bJzeqoa4cOKqO6bYr3WgKZxO8Z16+hsOM= -github.com/beorn7/perks v1.0.1/go.mod h1:G2ZrVWU2WbWT9wwq4/hrbKbnv/1ERSJQ0ibhJ6rlkpw= -github.com/cespare/xxhash/v2 v2.3.0 h1:UL815xU9SqsFlibzuggzjXhog7bL6oX9BbNZnL2UFvs= -github.com/cespare/xxhash/v2 v2.3.0/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs= -github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E= -github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= -github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= -github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= -github.com/google/go-cmp v0.6.0 h1:ofyhxvXcZhMsU5ulbFiLKl/XBFqE1GSq7atu8tAmTRI= -github.com/google/go-cmp v0.6.0/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY= -github.com/jmespath/go-jmespath v0.4.0 h1:BEgLn5cpjn8UN1mAw4NjwDrS35OdebyEtFe+9YPoQUg= -github.com/jmespath/go-jmespath v0.4.0/go.mod h1:T8mJZnbsbmF+m6zOOFylbeCJqk5+pHWvzYPziyZiYoo= -github.com/jmespath/go-jmespath/internal/testify v1.5.1 h1:shLQSRRSCCPj3f2gpwzGwWFoC7ycTf1rcQZHOlsJ6N8= -github.com/jmespath/go-jmespath/internal/testify v1.5.1/go.mod h1:L3OGu8Wl2/fWfCI6z80xFu9LTZmf1ZRjMHUOPmWr69U= -github.com/jpillora/backoff v1.0.0 h1:uvFg412JmmHBHw7iwprIxkPMI+sGQ4kzOWsMeHnm2EA= -github.com/jpillora/backoff v1.0.0/go.mod h1:J/6gKK9jxlEcS3zixgDgUAsiuZ7yrSoa/FX5e0EB2j4= -github.com/klauspost/compress v1.17.9 h1:6KIumPrER1LHsvBVuDa0r5xaG0Es51mhhB9BQB2qeMA= -github.com/klauspost/compress v1.17.9/go.mod h1:Di0epgTjJY877eYKx5yC51cX2A2Vl2ibi7bDH9ttBbw= -github.com/kr/pretty v0.3.1 h1:flRD4NNwYAUpkphVc1HcthR4KEIFJ65n8Mw5qdRn3LE= -github.com/kr/pretty v0.3.1/go.mod h1:hoEshYVHaxMs3cyo3Yncou5ZscifuDolrwPKZanG3xk= -github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY= -github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE= -github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822 h1:C3w9PqII01/Oq1c1nUAm88MOHcQC9l5mIlSMApZMrHA= -github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822/go.mod h1:+n7T8mK8HuQTcFwEeznm/DIxMOiR9yIdICNftLE1DvQ= -github.com/mwitkow/go-conntrack v0.0.0-20190716064945-2f068394615f h1:KUppIJq7/+SVif2QVs3tOP0zanoHgBEVAwHxUSIzRqU= -github.com/mwitkow/go-conntrack v0.0.0-20190716064945-2f068394615f/go.mod h1:qRWi+5nqEBWmkhHvq77mSJWrCKwh8bxhgT7d/eI7P4U= -github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= -github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= -github.com/prometheus/client_golang v1.20.4 h1:Tgh3Yr67PaOv/uTqloMsCEdeuFTatm5zIq5+qNN23vI= -github.com/prometheus/client_golang v1.20.4/go.mod h1:PIEt8X02hGcP8JWbeHyeZ53Y/jReSnHgO035n//V5WE= -github.com/prometheus/client_model v0.6.1 h1:ZKSh/rekM+n3CeS952MLRAdFwIKqeY8b62p8ais2e9E= -github.com/prometheus/client_model v0.6.1/go.mod h1:OrxVMOVHjw3lKMa8+x6HeMGkHMQyHDk9E3jmP2AmGiY= -github.com/prometheus/procfs v0.15.1 h1:YagwOFzUgYfKKHX6Dr+sHT7km/hxC76UB0learggepc= -github.com/prometheus/procfs v0.15.1/go.mod h1:fB45yRUv8NstnjriLhBQLuOUt+WW4BsoGhij/e3PBqk= -github.com/rogpeppe/go-internal v1.10.0 h1:TMyTOH3F/DB16zRVcYyreMH6GnZZrwQVAoYjRBZyWFQ= -github.com/rogpeppe/go-internal v1.10.0/go.mod h1:UQnix2H7Ngw/k4C5ijL5+65zddjncjaFoBhdsK/akog= -github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= -github.com/stretchr/testify v1.10.0 h1:Xv5erBjTwe/5IxqUQTdXv5kgmIvbHo3QQyRwhJsOfJA= -github.com/stretchr/testify v1.10.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY= -golang.org/x/net v0.32.0 h1:ZqPmj8Kzc+Y6e0+skZsuACbx+wzMgo5MQsJh9Qd6aYI= -golang.org/x/net v0.32.0/go.mod h1:CwU0IoeOlnQQWJ6ioyFrfRuomB8GKF6KbYXZVyeXNfs= -golang.org/x/oauth2 v0.24.0 h1:KTBBxWqUa0ykRPLtV69rRto9TLXcqYkeswu48x/gvNE= -golang.org/x/oauth2 v0.24.0/go.mod h1:XYTD2NtWslqkgxebSiOHnXEap4TF09sJSc7H1sXbhtI= -golang.org/x/sys v0.28.0 h1:Fksou7UEQUWlKvIdsqzJmUmCX3cZuD2+P3XyyzwMhlA= -golang.org/x/sys v0.28.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= -golang.org/x/text v0.21.0 h1:zyQAAkrwaneQ066sspRyJaG9VNi/YJ1NfzcGB3hZ/qo= -golang.org/x/text v0.21.0/go.mod h1:4IBbMaMmOPCJ8SecivzSH54+73PCFmPWxNTLm+vZkEQ= -google.golang.org/protobuf v1.35.2 h1:8Ar7bF+apOIoThw1EdZl0p1oWvMqTHmpA2fRTyZO8io= -google.golang.org/protobuf v1.35.2/go.mod h1:9fA7Ob0pmnwhb644+1+CVWFRbNajQ6iRojtC/QF5bRE= -gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= -gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk= -gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q= -gopkg.in/yaml.v2 v2.2.8/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= -gopkg.in/yaml.v2 v2.4.0 h1:D8xgwECY7CYvx+Y2n4sBz93Jn9JRvxdiyyo8CTfuKaY= -gopkg.in/yaml.v2 v2.4.0/go.mod h1:RDklbk79AGWmwhnvt/jBztapEOGDOx6ZbXqjP6csGnQ= -gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= -gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= diff --git a/sigv4/sigv4.go b/sigv4/sigv4.go deleted file mode 100644 index 17cc9139a..000000000 --- a/sigv4/sigv4.go +++ /dev/null @@ -1,154 +0,0 @@ -// Copyright 2021 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. - -// Deprecated: This package has been migrated to github.com/prometheus/sigv4. -package sigv4 - -import ( - "bytes" - "errors" - "fmt" - "io" - "net/http" - "net/textproto" - "path" - "sync" - "time" - - "github.com/aws/aws-sdk-go/aws/endpoints" - - "github.com/aws/aws-sdk-go/aws" - "github.com/aws/aws-sdk-go/aws/credentials" - "github.com/aws/aws-sdk-go/aws/credentials/stscreds" - "github.com/aws/aws-sdk-go/aws/session" - signer "github.com/aws/aws-sdk-go/aws/signer/v4" -) - -var sigv4HeaderDenylist = []string{ - "uber-trace-id", -} - -type sigV4RoundTripper struct { - region string - next http.RoundTripper - pool sync.Pool - - signer *signer.Signer -} - -// NewSigV4RoundTripper returns a new http.RoundTripper that will sign requests -// using Amazon's Signature Verification V4 signing procedure. The request will -// then be handed off to the next RoundTripper provided by next. If next is nil, -// http.DefaultTransport will be used. -// -// Credentials for signing are retrieved using the the default AWS credential -// chain. If credentials cannot be found, an error will be returned. -func NewSigV4RoundTripper(cfg *SigV4Config, next http.RoundTripper) (http.RoundTripper, error) { - if next == nil { - next = http.DefaultTransport - } - - creds := credentials.NewStaticCredentials(cfg.AccessKey, string(cfg.SecretKey), "") - if cfg.AccessKey == "" && cfg.SecretKey == "" { - creds = nil - } - - useFIPSSTSEndpoint := endpoints.FIPSEndpointStateDisabled - if cfg.UseFIPSSTSEndpoint { - useFIPSSTSEndpoint = endpoints.FIPSEndpointStateEnabled - } - - sess, err := session.NewSessionWithOptions(session.Options{ - Config: aws.Config{ - Region: aws.String(cfg.Region), - Credentials: creds, - UseFIPSEndpoint: useFIPSSTSEndpoint, - }, - Profile: cfg.Profile, - }) - if err != nil { - return nil, fmt.Errorf("could not create new AWS session: %w", err) - } - if _, err := sess.Config.Credentials.Get(); err != nil { - return nil, fmt.Errorf("could not get SigV4 credentials: %w", err) - } - if aws.StringValue(sess.Config.Region) == "" { - return nil, errors.New("region not configured in sigv4 or in default credentials chain") - } - - signerCreds := sess.Config.Credentials - if cfg.RoleARN != "" { - signerCreds = stscreds.NewCredentials(sess, cfg.RoleARN) - } - - rt := &sigV4RoundTripper{ - region: cfg.Region, - next: next, - signer: signer.NewSigner(signerCreds), - } - rt.pool.New = rt.newBuf - return rt, nil -} - -func (rt *sigV4RoundTripper) newBuf() interface{} { - return bytes.NewBuffer(make([]byte, 0, 1024)) -} - -func (rt *sigV4RoundTripper) RoundTrip(req *http.Request) (*http.Response, error) { - // rt.signer.Sign needs a seekable body, so we replace the body with a - // buffered reader filled with the contents of original body. - buf := rt.pool.Get().(*bytes.Buffer) - defer func() { - buf.Reset() - rt.pool.Put(buf) - }() - - if req.Body != nil { - if _, err := io.Copy(buf, req.Body); err != nil { - return nil, err - } - // Close the original body since we don't need it anymore. - _ = req.Body.Close() - } - - // Ensure our seeker is back at the start of the buffer once we return. - var seeker io.ReadSeeker = bytes.NewReader(buf.Bytes()) - defer func() { - _, _ = seeker.Seek(0, io.SeekStart) - }() - req.Body = io.NopCloser(seeker) - - // Clean path like documented in AWS documentation. - // https://docs.aws.amazon.com/general/latest/gr/sigv4-create-canonical-request.html - req.URL.Path = path.Clean(req.URL.Path) - - // Clone the request and trim out headers that we don't want to sign. - signReq := req.Clone(req.Context()) - for _, header := range sigv4HeaderDenylist { - signReq.Header.Del(header) - } - - headers, err := rt.signer.Sign(signReq, seeker, "aps", rt.region, time.Now().UTC()) - if err != nil { - return nil, fmt.Errorf("failed to sign request: %w", err) - } - - // Copy over signed headers. Authorization header is not returned by - // rt.signer.Sign and needs to be copied separately. - for k, v := range headers { - req.Header[textproto.CanonicalMIMEHeaderKey(k)] = v - } - req.Header.Set("Authorization", signReq.Header.Get("Authorization")) - - return rt.next.RoundTrip(req) -} diff --git a/sigv4/sigv4_config.go b/sigv4/sigv4_config.go deleted file mode 100644 index 2f5e7572a..000000000 --- a/sigv4/sigv4_config.go +++ /dev/null @@ -1,48 +0,0 @@ -// Copyright 2021 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 sigv4 - -import ( - "errors" - - "github.com/prometheus/common/config" -) - -// SigV4Config is the configuration for signing remote write requests with -// AWS's SigV4 verification process. Empty values will be retrieved using the -// AWS default credentials chain. -type SigV4Config struct { - Region string `yaml:"region,omitempty"` - AccessKey string `yaml:"access_key,omitempty"` - SecretKey config.Secret `yaml:"secret_key,omitempty"` - Profile string `yaml:"profile,omitempty"` - RoleARN string `yaml:"role_arn,omitempty"` - UseFIPSSTSEndpoint bool `yaml:"use_fips_sts_endpoint,omitempty"` -} - -func (c *SigV4Config) Validate() error { - if (c.AccessKey == "") != (c.SecretKey == "") { - return errors.New("must provide a AWS SigV4 Access key and Secret Key if credentials are specified in the SigV4 config") - } - return nil -} - -func (c *SigV4Config) UnmarshalYAML(unmarshal func(interface{}) error) error { - type plain SigV4Config - *c = SigV4Config{} - if err := unmarshal((*plain)(c)); err != nil { - return err - } - return c.Validate() -} diff --git a/sigv4/sigv4_config_test.go b/sigv4/sigv4_config_test.go deleted file mode 100644 index 6c8058f64..000000000 --- a/sigv4/sigv4_config_test.go +++ /dev/null @@ -1,53 +0,0 @@ -// Copyright 2021 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 sigv4 - -import ( - "os" - "testing" - - "github.com/stretchr/testify/require" - "gopkg.in/yaml.v2" -) - -func loadSigv4Config(filename string) (*SigV4Config, error) { - content, err := os.ReadFile(filename) - if err != nil { - return nil, err - } - cfg := SigV4Config{} - if err = yaml.UnmarshalStrict(content, &cfg); err != nil { - return nil, err - } - return &cfg, nil -} - -func testGoodConfig(t *testing.T, filename string) { - _, err := loadSigv4Config(filename) - require.NoErrorf(t, err, "Unexpected error parsing %s: %s", filename, err) -} - -func TestGoodSigV4Configs(t *testing.T) { - filesToTest := []string{"testdata/sigv4_good.yaml", "testdata/sigv4_good.yaml"} - for _, filename := range filesToTest { - testGoodConfig(t, filename) - } -} - -func TestBadSigV4Config(t *testing.T) { - filename := "testdata/sigv4_bad.yaml" - _, err := loadSigv4Config(filename) - require.Errorf(t, err, "Did not receive expected error unmarshaling bad sigv4 config") - require.ErrorContainsf(t, err, "must provide a AWS SigV4 Access key and Secret Key", "Received unexpected error from unmarshal of %s: %s", filename, err.Error()) -} diff --git a/sigv4/sigv4_test.go b/sigv4/sigv4_test.go deleted file mode 100644 index b470672c2..000000000 --- a/sigv4/sigv4_test.go +++ /dev/null @@ -1,117 +0,0 @@ -// Copyright 2021 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 sigv4 - -import ( - "net/http" - "os" - "strings" - "testing" - - "github.com/aws/aws-sdk-go/aws" - "github.com/aws/aws-sdk-go/aws/credentials" - "github.com/aws/aws-sdk-go/aws/session" - signer "github.com/aws/aws-sdk-go/aws/signer/v4" - "github.com/stretchr/testify/require" -) - -type RoundTripperFunc func(req *http.Request) (*http.Response, error) - -// RoundTrip implements the RoundTripper interface. -func (rt RoundTripperFunc) RoundTrip(r *http.Request) (*http.Response, error) { - return rt(r) -} - -func TestSigV4_Inferred_Region(t *testing.T) { - os.Setenv("AWS_ACCESS_KEY_ID", "secret") - os.Setenv("AWS_SECRET_ACCESS_KEY", "token") - os.Setenv("AWS_REGION", "us-west-2") - - sess, err := session.NewSession(&aws.Config{ - // Setting to an empty string to demostrate the default value from the yaml - // won't override the environment's region. - Region: aws.String(""), - }) - require.NoError(t, err) - _, err = sess.Config.Credentials.Get() - require.NoError(t, err) - - require.NotNil(t, sess.Config.Region) - require.Equal(t, "us-west-2", *sess.Config.Region) -} - -func TestSigV4RoundTripper(t *testing.T) { - var gotReq *http.Request - - rt := &sigV4RoundTripper{ - region: "us-east-2", - next: RoundTripperFunc(func(req *http.Request) (*http.Response, error) { - gotReq = req - return &http.Response{StatusCode: http.StatusOK}, nil - }), - signer: signer.NewSigner(credentials.NewStaticCredentials( - "test-id", - "secret", - "token", - )), - } - rt.pool.New = rt.newBuf - - cli := &http.Client{Transport: rt} - - req, err := http.NewRequest(http.MethodPost, "https://example.com", strings.NewReader("Hello, world!")) - require.NoError(t, err) - - _, err = cli.Do(req) - require.NoError(t, err) - require.NotNil(t, gotReq) - - origReq := gotReq - require.NotEmpty(t, origReq.Header.Get("Authorization")) - require.NotEmpty(t, origReq.Header.Get("X-Amz-Date")) - - // Perform the same request but with a header that shouldn't included in the - // signature; validate that the Authorization signature matches. - t.Run("Ignored Headers", func(t *testing.T) { - req, err := http.NewRequest(http.MethodPost, "https://example.com", strings.NewReader("Hello, world!")) - require.NoError(t, err) - - req.Header.Add("Uber-Trace-Id", "some-trace-id") - - _, err = cli.Do(req) - require.NoError(t, err) - require.NotNil(t, gotReq) - - require.Equal(t, origReq.Header.Get("Authorization"), gotReq.Header.Get("Authorization")) - }) - - t.Run("Escape URL", func(t *testing.T) { - req, err := http.NewRequest(http.MethodPost, "https://example.com/test//test", strings.NewReader("Hello, world!")) - require.NoError(t, err) - require.Equal(t, "/test//test", req.URL.Path) - - _, err = cli.Do(req) - require.NoError(t, err) - require.NotNil(t, gotReq) - - require.Equal(t, "/test/test", gotReq.URL.Path) - }) - - t.Run("No body", func(t *testing.T) { - req, err := http.NewRequest(http.MethodGet, "https://example.com/test/test", nil) - require.NoError(t, err) - _, err = cli.Do(req) - require.NoError(t, err) - }) -} diff --git a/sigv4/testdata/sigv4_bad.yaml b/sigv4/testdata/sigv4_bad.yaml deleted file mode 100644 index 7086f636b..000000000 --- a/sigv4/testdata/sigv4_bad.yaml +++ /dev/null @@ -1,4 +0,0 @@ -region: us-east-2 -access_key: AccessKey -profile: profile -role_arn: blah:role/arn diff --git a/sigv4/testdata/sigv4_good.yaml b/sigv4/testdata/sigv4_good.yaml deleted file mode 100644 index 629669458..000000000 --- a/sigv4/testdata/sigv4_good.yaml +++ /dev/null @@ -1,6 +0,0 @@ -region: us-east-2 -access_key: AccessKey -secret_key: SecretKey -profile: profile -role_arn: blah:role/arn -use_fips_sts_endpoint: true diff --git a/sigv4/testdata/sigv4_good_empty_keys.yaml b/sigv4/testdata/sigv4_good_empty_keys.yaml deleted file mode 100644 index de10c0ff3..000000000 --- a/sigv4/testdata/sigv4_good_empty_keys.yaml +++ /dev/null @@ -1,3 +0,0 @@ -region: us-east-2 -profile: profile -role_arn: blah:role/arn From e3926e2a56435b2213296803d4e6c6f66fc3c423 Mon Sep 17 00:00:00 2001 From: David Ashpole Date: Fri, 13 Dec 2024 21:35:31 +0000 Subject: [PATCH 25/48] update links to openmetrics to reference the v1.0.0 release Signed-off-by: David Ashpole --- expfmt/openmetrics_create.go | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/expfmt/openmetrics_create.go b/expfmt/openmetrics_create.go index f1c495dd6..a21ed4ec1 100644 --- a/expfmt/openmetrics_create.go +++ b/expfmt/openmetrics_create.go @@ -38,7 +38,7 @@ type EncoderOption func(*encoderOption) // WithCreatedLines is an EncoderOption that configures the OpenMetrics encoder // to include _created lines (See -// https://github.com/OpenObservability/OpenMetrics/blob/main/specification/OpenMetrics.md#counter-1). +// https://github.com/prometheus/OpenMetrics/blob/v1.0.0/specification/OpenMetrics.md#counter-1). // Created timestamps can improve the accuracy of series reset detection, but // come with a bandwidth cost. // @@ -102,7 +102,7 @@ func WithUnit() EncoderOption { // // - According to the OM specs, the `# UNIT` line is optional, but if populated, // the unit has to be present in the metric name as its suffix: -// (see https://github.com/OpenObservability/OpenMetrics/blob/main/specification/OpenMetrics.md#unit). +// (see https://github.com/prometheus/OpenMetrics/blob/v1.0.0/specification/OpenMetrics.md#unit). // However, in order to accommodate any potential scenario where such a change in the // metric name is not desirable, the users are here given the choice of either explicitly // opt in, in case they wish for the unit to be included in the output AND in the metric name From 5d9961df0b3ac7927a16495c3bf40f2de421725c Mon Sep 17 00:00:00 2001 From: PrometheusBot Date: Sun, 29 Dec 2024 19:19:07 +0100 Subject: [PATCH 26/48] Update common Prometheus files (#742) Signed-off-by: prombot --- .github/workflows/golangci-lint.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/golangci-lint.yml b/.github/workflows/golangci-lint.yml index e645ba30a..01b943b9b 100644 --- a/.github/workflows/golangci-lint.yml +++ b/.github/workflows/golangci-lint.yml @@ -26,7 +26,7 @@ jobs: - name: Checkout repository uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2 - name: Install Go - uses: actions/setup-go@41dfa10bad2bb2ae585af6ee5bb4d7d973ad74ed # v5.1.0 + uses: actions/setup-go@3041bf56c941b39c61721a86cd11f3bb1338122a # v5.2.0 with: go-version: 1.23.x - name: Install snmp_exporter/generator dependencies From fe88605b113417b6baba52ec2850187ac127cc1c Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Wed, 1 Jan 2025 18:57:44 +0100 Subject: [PATCH 27/48] Bump google.golang.org/protobuf from 1.35.2 to 1.36.1 (#744) Bumps google.golang.org/protobuf from 1.35.2 to 1.36.1. --- updated-dependencies: - dependency-name: google.golang.org/protobuf dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- go.mod | 2 +- go.sum | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/go.mod b/go.mod index 6f41dc5e0..c814eda45 100644 --- a/go.mod +++ b/go.mod @@ -12,7 +12,7 @@ require ( github.com/stretchr/testify v1.10.0 golang.org/x/net v0.32.0 golang.org/x/oauth2 v0.24.0 - google.golang.org/protobuf v1.35.2 + google.golang.org/protobuf v1.36.1 gopkg.in/yaml.v2 v2.4.0 ) diff --git a/go.sum b/go.sum index f374f9314..995ebb456 100644 --- a/go.sum +++ b/go.sum @@ -51,8 +51,8 @@ golang.org/x/sys v0.28.0 h1:Fksou7UEQUWlKvIdsqzJmUmCX3cZuD2+P3XyyzwMhlA= golang.org/x/sys v0.28.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= golang.org/x/text v0.21.0 h1:zyQAAkrwaneQ066sspRyJaG9VNi/YJ1NfzcGB3hZ/qo= golang.org/x/text v0.21.0/go.mod h1:4IBbMaMmOPCJ8SecivzSH54+73PCFmPWxNTLm+vZkEQ= -google.golang.org/protobuf v1.35.2 h1:8Ar7bF+apOIoThw1EdZl0p1oWvMqTHmpA2fRTyZO8io= -google.golang.org/protobuf v1.35.2/go.mod h1:9fA7Ob0pmnwhb644+1+CVWFRbNajQ6iRojtC/QF5bRE= +google.golang.org/protobuf v1.36.1 h1:yBPeRvTftaleIgM3PZ/WBIZ7XM/eEYAaEyCwvyjq/gk= +google.golang.org/protobuf v1.36.1/go.mod h1:9fA7Ob0pmnwhb644+1+CVWFRbNajQ6iRojtC/QF5bRE= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk= gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q= From 8d916fab5bd4b281fe2797100a6cabe2515d3d59 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Wed, 1 Jan 2025 18:58:00 +0100 Subject: [PATCH 28/48] Bump golang.org/x/net from 0.32.0 to 0.33.0 (#743) Bumps [golang.org/x/net](https://github.com/golang/net) from 0.32.0 to 0.33.0. - [Commits](https://github.com/golang/net/compare/v0.32.0...v0.33.0) --- updated-dependencies: - dependency-name: golang.org/x/net dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- go.mod | 2 +- go.sum | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/go.mod b/go.mod index c814eda45..4d62719bb 100644 --- a/go.mod +++ b/go.mod @@ -10,7 +10,7 @@ require ( github.com/mwitkow/go-conntrack v0.0.0-20190716064945-2f068394615f github.com/prometheus/client_model v0.6.1 github.com/stretchr/testify v1.10.0 - golang.org/x/net v0.32.0 + golang.org/x/net v0.33.0 golang.org/x/oauth2 v0.24.0 google.golang.org/protobuf v1.36.1 gopkg.in/yaml.v2 v2.4.0 diff --git a/go.sum b/go.sum index 995ebb456..b5955f01f 100644 --- a/go.sum +++ b/go.sum @@ -43,8 +43,8 @@ github.com/stretchr/testify v1.10.0 h1:Xv5erBjTwe/5IxqUQTdXv5kgmIvbHo3QQyRwhJsOf github.com/stretchr/testify v1.10.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY= github.com/xhit/go-str2duration/v2 v2.1.0 h1:lxklc02Drh6ynqX+DdPyp5pCKLUQpRT8bp8Ydu2Bstc= github.com/xhit/go-str2duration/v2 v2.1.0/go.mod h1:ohY8p+0f07DiV6Em5LKB0s2YpLtXVyJfNt1+BlmyAsU= -golang.org/x/net v0.32.0 h1:ZqPmj8Kzc+Y6e0+skZsuACbx+wzMgo5MQsJh9Qd6aYI= -golang.org/x/net v0.32.0/go.mod h1:CwU0IoeOlnQQWJ6ioyFrfRuomB8GKF6KbYXZVyeXNfs= +golang.org/x/net v0.33.0 h1:74SYHlV8BIgHIFC/LrYkOGIwL19eTYXQ5wc6TBuO36I= +golang.org/x/net v0.33.0/go.mod h1:HXLR5J+9DxmrqMwG9qjGCxZ+zKXxBru04zlTvWlWuN4= golang.org/x/oauth2 v0.24.0 h1:KTBBxWqUa0ykRPLtV69rRto9TLXcqYkeswu48x/gvNE= golang.org/x/oauth2 v0.24.0/go.mod h1:XYTD2NtWslqkgxebSiOHnXEap4TF09sJSc7H1sXbhtI= golang.org/x/sys v0.28.0 h1:Fksou7UEQUWlKvIdsqzJmUmCX3cZuD2+P3XyyzwMhlA= From aea8919bdc8b0b593086473f99c50ca6d5796c0c Mon Sep 17 00:00:00 2001 From: PrometheusBot Date: Thu, 16 Jan 2025 09:59:58 +0100 Subject: [PATCH 29/48] Update common Prometheus files (#747) Signed-off-by: prombot --- .github/workflows/golangci-lint.yml | 2 +- Makefile.common | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/golangci-lint.yml b/.github/workflows/golangci-lint.yml index 01b943b9b..0c00c410a 100644 --- a/.github/workflows/golangci-lint.yml +++ b/.github/workflows/golangci-lint.yml @@ -36,4 +36,4 @@ jobs: uses: golangci/golangci-lint-action@971e284b6050e8a5849b72094c50ab08da042db8 # v6.1.1 with: args: --verbose - version: v1.62.0 + version: v1.63.4 diff --git a/Makefile.common b/Makefile.common index fc47bdbb2..d1576bb31 100644 --- a/Makefile.common +++ b/Makefile.common @@ -61,7 +61,7 @@ PROMU_URL := https://github.com/prometheus/promu/releases/download/v$(PROMU_ SKIP_GOLANGCI_LINT := GOLANGCI_LINT := GOLANGCI_LINT_OPTS ?= -GOLANGCI_LINT_VERSION ?= v1.62.0 +GOLANGCI_LINT_VERSION ?= v1.63.4 # golangci-lint only supports linux, darwin and windows platforms on i386/amd64/arm64. # windows isn't included here because of the path separator being different. ifeq ($(GOHOSTOS),$(filter $(GOHOSTOS),linux darwin)) From 280b0e7d5bdf09ddfd2d93c226671cb2ebdb7d5f Mon Sep 17 00:00:00 2001 From: Bartlomiej Plotka Date: Thu, 16 Jan 2025 16:26:11 +0100 Subject: [PATCH 30/48] http_config: Allow customizing TLS config and settings. (#748) * http_config: Allow customizing TLS config and settings. Signed-off-by: bwplotka * Switched to newTLSConfigFunc Signed-off-by: bwplotka * Addressed comments. Signed-off-by: bwplotka --------- Signed-off-by: bwplotka --- config/http_config.go | 22 +++++++++++++++++++--- 1 file changed, 19 insertions(+), 3 deletions(-) diff --git a/config/http_config.go b/config/http_config.go index 57ec252ad..63809083a 100644 --- a/config/http_config.go +++ b/config/http_config.go @@ -52,7 +52,8 @@ var ( http2Enabled: true, // 5 minutes is typically above the maximum sane scrape interval. So we can // use keepalive for all configurations. - idleConnTimeout: 5 * time.Minute, + idleConnTimeout: 5 * time.Minute, + newTLSConfigFunc: NewTLSConfigWithContext, } ) @@ -452,8 +453,12 @@ func (a *BasicAuth) UnmarshalYAML(unmarshal func(interface{}) error) error { // by net.Dialer. type DialContextFunc func(context.Context, string, string) (net.Conn, error) +// NewTLSConfigFunc returns tls.Config. +type NewTLSConfigFunc func(context.Context, *TLSConfig, ...TLSConfigOption) (*tls.Config, error) + type httpClientOptions struct { dialContextFunc DialContextFunc + newTLSConfigFunc NewTLSConfigFunc keepAlivesEnabled bool http2Enabled bool idleConnTimeout time.Duration @@ -473,13 +478,23 @@ func (f httpClientOptionFunc) applyToHTTPClientOptions(options *httpClientOption f(options) } -// WithDialContextFunc allows you to override func gets used for the actual dialing. The default is `net.Dialer.DialContext`. +// WithDialContextFunc allows you to override the func gets used for the dialing. +// The default is `net.Dialer.DialContext`. func WithDialContextFunc(fn DialContextFunc) HTTPClientOption { return httpClientOptionFunc(func(opts *httpClientOptions) { opts.dialContextFunc = fn }) } +// WithNewTLSConfigFunc allows you to override the func that creates the TLS config +// from the prometheus http config. +// The default is `NewTLSConfigWithContext`. +func WithNewTLSConfigFunc(newTLSConfigFunc NewTLSConfigFunc) HTTPClientOption { + return httpClientOptionFunc(func(opts *httpClientOptions) { + opts.newTLSConfigFunc = newTLSConfigFunc + }) +} + // WithKeepAlivesDisabled allows to disable HTTP keepalive. func WithKeepAlivesDisabled() HTTPClientOption { return httpClientOptionFunc(func(opts *httpClientOptions) { @@ -670,7 +685,7 @@ func NewRoundTripperFromConfigWithContext(ctx context.Context, cfg HTTPClientCon return rt, nil } - tlsConfig, err := NewTLSConfig(&cfg.TLSConfig, WithSecretManager(opts.secretManager)) + tlsConfig, err := opts.newTLSConfigFunc(ctx, &cfg.TLSConfig, WithSecretManager(opts.secretManager)) if err != nil { return nil, err } @@ -679,6 +694,7 @@ func NewRoundTripperFromConfigWithContext(ctx context.Context, cfg HTTPClientCon if err != nil { return nil, err } + if tlsSettings.immutable() { // No need for a RoundTripper that reloads the files automatically. return newRT(tlsConfig) From d5631d28580dcd7b0cca5c475a405fb3eeecacda Mon Sep 17 00:00:00 2001 From: dongjiang Date: Sat, 18 Jan 2025 18:09:20 +0800 Subject: [PATCH 31/48] making this map a public variable (#741) Signed-off-by: dongjiang --- config/headers.go | 6 +++--- config/headers_test.go | 4 ++-- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/config/headers.go b/config/headers.go index 7276742ec..9beaae26c 100644 --- a/config/headers.go +++ b/config/headers.go @@ -24,9 +24,9 @@ import ( "strings" ) -// reservedHeaders that change the connection, are set by Prometheus, or can +// ReservedHeaders that change the connection, are set by Prometheus, or can // be changed otherwise. -var reservedHeaders = map[string]struct{}{ +var ReservedHeaders = map[string]struct{}{ "Authorization": {}, "Host": {}, "Content-Encoding": {}, @@ -72,7 +72,7 @@ func (h *Headers) SetDirectory(dir string) { // Validate validates the Headers config. func (h *Headers) Validate() error { for n := range h.Headers { - if _, ok := reservedHeaders[http.CanonicalHeaderKey(n)]; ok { + if _, ok := ReservedHeaders[http.CanonicalHeaderKey(n)]; ok { return fmt.Errorf("setting header %q is not allowed", http.CanonicalHeaderKey(n)) } } diff --git a/config/headers_test.go b/config/headers_test.go index 39c6f9ff3..c807fbc3b 100644 --- a/config/headers_test.go +++ b/config/headers_test.go @@ -22,10 +22,10 @@ import ( ) func TestReservedHeaders(t *testing.T) { - for k := range reservedHeaders { + for k := range ReservedHeaders { l := http.CanonicalHeaderKey(k) if k != l { - t.Errorf("reservedHeaders keys should be lowercase: got %q, expected %q", k, http.CanonicalHeaderKey(k)) + t.Errorf("ReservedHeaders keys should be lowercase: got %q, expected %q", k, http.CanonicalHeaderKey(k)) } } } From 1cc52972eb5e5f91ad03ab4b1b34ca085958bd32 Mon Sep 17 00:00:00 2001 From: Matthieu MOREL Date: Sat, 18 Jan 2025 12:27:44 +0100 Subject: [PATCH 32/48] setup ossf scorecard and codeql workflows (#564) * setup ossf scorecard and codql workflows --------- Signed-off-by: Matthieu MOREL Co-authored-by: Ben Kochie --- .github/workflows/codeql.yml | 50 +++++++++++++++++++++++++++++ .github/workflows/scorecard.yml | 56 +++++++++++++++++++++++++++++++++ README.md | 2 ++ 3 files changed, 108 insertions(+) create mode 100644 .github/workflows/codeql.yml create mode 100644 .github/workflows/scorecard.yml diff --git a/.github/workflows/codeql.yml b/.github/workflows/codeql.yml new file mode 100644 index 000000000..f7fc82f37 --- /dev/null +++ b/.github/workflows/codeql.yml @@ -0,0 +1,50 @@ +name: "CodeQL" + +on: + push: + branches: [ "main" ] + pull_request: + branches: [ "main" ] + schedule: + - cron: '24 1 * * 5' + +permissions: + contents: read + +jobs: + analyze: + name: Analyze + runs-on: ubuntu-latest + timeout-minutes: 360 + permissions: + # required for all workflows + security-events: write + + # only required for workflows in private repositories + actions: read + contents: read + + strategy: + fail-fast: false + matrix: + language: [ 'go' ] + + steps: + - name: Checkout repository + uses: actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11 # v4.1.1 + + # Initializes the CodeQL tools for scanning. + - name: Initialize CodeQL + uses: github/codeql-action/init@e5f05b81d5b6ff8cfa111c80c22c5fd02a384118 # v3.23.0 + with: + languages: ${{ matrix.language }} + + # Autobuild attempts to build any compiled languages (C/C++, C#, Go, Java, or Swift). + # If this step fails, then you should remove it and run the build manually (see below) + - name: Autobuild + uses: github/codeql-action/autobuild@e5f05b81d5b6ff8cfa111c80c22c5fd02a384118 # v3.23.0 + + - name: Perform CodeQL Analysis + uses: github/codeql-action/analyze@e5f05b81d5b6ff8cfa111c80c22c5fd02a384118 # v3.23.0 + with: + category: "/language:${{matrix.language}}" diff --git a/.github/workflows/scorecard.yml b/.github/workflows/scorecard.yml new file mode 100644 index 000000000..4793ce721 --- /dev/null +++ b/.github/workflows/scorecard.yml @@ -0,0 +1,56 @@ +name: Scorecard supply-chain security + +on: + # For Branch-Protection check. Only the default branch is supported. See + # https://github.com/ossf/scorecard/blob/main/docs/checks.md#branch-protection + branch_protection_rule: + # To guarantee Maintained check is occasionally updated. See + # https://github.com/ossf/scorecard/blob/main/docs/checks.md#maintained + schedule: + - cron: '26 18 * * 2' + push: + branches: [ "main" ] + +# Declare default permissions as read only. +permissions: read-all + +jobs: + analysis: + name: Scorecard analysis + runs-on: ubuntu-latest + permissions: + # Needed to upload the results to code-scanning dashboard. + security-events: write + # Needed to publish results and get a badge (see publish_results below). + id-token: write + # Uncomment the permissions below if installing in a private repository. + # contents: read + # actions: read + + steps: + - name: "Checkout code" + uses: actions/checkout@93ea575cb5d8a053eaa0ac8fa3b40d7e05a33cc8 # v3.1.0 + with: + persist-credentials: false + + - name: "Run analysis" + uses: ossf/scorecard-action@e38b1902ae4f44df626f11ba0734b14fb91f8f86 # v2.1.2 + with: + results_file: results.sarif + results_format: sarif + publish_results: true + + # Upload the results as artifacts (optional). Commenting out will disable uploads of run results in SARIF + # format to the repository Actions tab. + - name: "Upload artifact" + uses: actions/upload-artifact@3cea5372237819ed00197afe530f5a7ea3e805c8 # v3.1.0 + with: + name: SARIF file + path: results.sarif + retention-days: 5 + + # Upload the results to GitHub's code scanning dashboard. + - name: "Upload to code-scanning" + uses: github/codeql-action/upload-sarif@17573ee1cc1b9d061760f3a006fc4aac4f944fd5 # v2.2.4 + with: + sarif_file: results.sarif diff --git a/README.md b/README.md index 954cc91b4..f7d5342dc 100644 --- a/README.md +++ b/README.md @@ -1,5 +1,7 @@ # Common ![circleci](https://circleci.com/gh/prometheus/common/tree/main.svg?style=shield) +[![OpenSSF Scorecard](https://api.securityscorecards.dev/projects/github.com/prometheus/common/badge)](https://securityscorecards.dev/viewer/?uri=github.com/prometheus/common) + This repository contains Go libraries that are shared across Prometheus components and libraries. They are considered internal to Prometheus, without From df14882cbf97c96ef87ddc456cde77d7be7e4352 Mon Sep 17 00:00:00 2001 From: TJ Hoplock <33664289+tjhop@users.noreply.github.com> Date: Mon, 20 Jan 2025 08:26:12 -0500 Subject: [PATCH 33/48] feat(promslog): implement reserved keys, rename duplicates (#746) * feat(promslog): implement reserved keys, rename duplicates This commit adds support for "reserved" keys, where upon detecting a duplicate log attribute key, it will rename the key to prevent collisions/issues with core slog attribute keys in use. The reserved keys are: - `level` - `source` (for standard slog style) - `caller` (for legacy go-kit style) - `time` (for standard slog style) - `ts` (for legacy go-kit style) By supporting reserved keys, we allow users of the library to use these keys for their own supplemental log attributes. Fixes: #745 Example test output: ``` === RUN TestReservedKeys/slog_log_style time=2025-01-13T18:32:46.508Z level=INFO source=slog_test.go:212 msg="reserved keys test for slog_log_style" logged_level="surprise! I'm a string" logged_source="surprise! I'm a string" logged_time="surprise! I'm a string" === RUN TestReservedKeys/go-kit_log_style ts=2025-01-13T18:32:46.508Z level=info caller=slog_test.go:212 msg="reserved keys test for go-kit_log_style" logged_level="surprise! I'm a string" logged_caller="surprise! I'm a string" logged_ts="surprise! I'm a string" ``` Note: this implementation only technically removes duplicates of our core reserved keys. If a user adds multiple instances of a reserved key, the rest get renamed to `logged_$key`, which means there could be duplicate attributes with `logged_$key`. This is mostly to simplify implementation so we don't need to bother reference counting in the replace attr function to do things like `logged_$key1`, `logged_$key2`, etc. --------- Signed-off-by: TJ Hoplock --- promslog/slog.go | 97 +++++++++++++++++++++++++++++++++---------- promslog/slog_test.go | 39 +++++++++++++++++ 2 files changed, 115 insertions(+), 21 deletions(-) diff --git a/promslog/slog.go b/promslog/slog.go index 6e8fbabce..11c33fb66 100644 --- a/promslog/slog.go +++ b/promslog/slog.go @@ -25,6 +25,7 @@ import ( "path/filepath" "strconv" "strings" + "time" ) type LogStyle string @@ -32,6 +33,8 @@ type LogStyle string const ( SlogStyle LogStyle = "slog" GoKitStyle LogStyle = "go-kit" + + reservedKeyPrefix = "logged_" ) var ( @@ -43,26 +46,51 @@ var ( goKitStyleReplaceAttrFunc = func(groups []string, a slog.Attr) slog.Attr { key := a.Key switch key { - case slog.TimeKey: - a.Key = "ts" - - // This timestamp format differs from RFC3339Nano by using .000 instead - // of .999999999 which changes the timestamp from 9 variable to 3 fixed - // decimals (.130 instead of .130987456). - t := a.Value.Time() - a.Value = slog.StringValue(t.UTC().Format("2006-01-02T15:04:05.000Z07:00")) - case slog.SourceKey: - a.Key = "caller" - src, _ := a.Value.Any().(*slog.Source) + case slog.TimeKey, "ts": + if t, ok := a.Value.Any().(time.Time); ok { + a.Key = "ts" - switch callerAddFunc { - case true: - a.Value = slog.StringValue(filepath.Base(src.File) + "(" + filepath.Base(src.Function) + "):" + strconv.Itoa(src.Line)) - default: - a.Value = slog.StringValue(filepath.Base(src.File) + ":" + strconv.Itoa(src.Line)) + // This timestamp format differs from RFC3339Nano by using .000 instead + // of .999999999 which changes the timestamp from 9 variable to 3 fixed + // decimals (.130 instead of .130987456). + a.Value = slog.StringValue(t.UTC().Format("2006-01-02T15:04:05.000Z07:00")) + } else { + // If we can't cast the any from the value to a + // time.Time, it means the caller logged + // another attribute with a key of `ts`. + // Prevent duplicate keys (necessary for proper + // JSON) by renaming the key to `logged_ts`. + a.Key = reservedKeyPrefix + key + } + case slog.SourceKey, "caller": + if src, ok := a.Value.Any().(*slog.Source); ok { + a.Key = "caller" + switch callerAddFunc { + case true: + a.Value = slog.StringValue(filepath.Base(src.File) + "(" + filepath.Base(src.Function) + "):" + strconv.Itoa(src.Line)) + default: + a.Value = slog.StringValue(filepath.Base(src.File) + ":" + strconv.Itoa(src.Line)) + } + } else { + // If we can't cast the any from the value to + // an *slog.Source, it means the caller logged + // another attribute with a key of `caller`. + // Prevent duplicate keys (necessary for proper + // JSON) by renaming the key to + // `logged_caller`. + a.Key = reservedKeyPrefix + key } case slog.LevelKey: - a.Value = slog.StringValue(strings.ToLower(a.Value.String())) + if lvl, ok := a.Value.Any().(slog.Level); ok { + a.Value = slog.StringValue(strings.ToLower(lvl.String())) + } else { + // If we can't cast the any from the value to + // an slog.Level, it means the caller logged + // another attribute with a key of `level`. + // Prevent duplicate keys (necessary for proper + // JSON) by renaming the key to `logged_level`. + a.Key = reservedKeyPrefix + key + } default: } @@ -72,11 +100,38 @@ var ( key := a.Key switch key { case slog.TimeKey: - t := a.Value.Time() - a.Value = slog.TimeValue(t.UTC()) + if t, ok := a.Value.Any().(time.Time); ok { + a.Value = slog.TimeValue(t.UTC()) + } else { + // If we can't cast the any from the value to a + // time.Time, it means the caller logged + // another attribute with a key of `time`. + // Prevent duplicate keys (necessary for proper + // JSON) by renaming the key to `logged_time`. + a.Key = reservedKeyPrefix + key + } case slog.SourceKey: - src, _ := a.Value.Any().(*slog.Source) - a.Value = slog.StringValue(filepath.Base(src.File) + ":" + strconv.Itoa(src.Line)) + if src, ok := a.Value.Any().(*slog.Source); ok { + a.Value = slog.StringValue(filepath.Base(src.File) + ":" + strconv.Itoa(src.Line)) + } else { + // If we can't cast the any from the value to + // an *slog.Source, it means the caller logged + // another attribute with a key of `source`. + // Prevent duplicate keys (necessary for proper + // JSON) by renaming the key to + // `logged_source`. + a.Key = reservedKeyPrefix + key + } + case slog.LevelKey: + if _, ok := a.Value.Any().(slog.Level); !ok { + // If we can't cast the any from the value to + // an slog.Level, it means the caller logged + // another attribute with a key of `level`. + // Prevent duplicate keys (necessary for proper + // JSON) by renaming the key to + // `logged_level`. + a.Key = reservedKeyPrefix + key + } default: } diff --git a/promslog/slog_test.go b/promslog/slog_test.go index fc824e04f..6da14d987 100644 --- a/promslog/slog_test.go +++ b/promslog/slog_test.go @@ -188,3 +188,42 @@ func TestTruncateSourceFileName_GoKitStyle(t *testing.T) { t.Errorf("Expected no directory separators in caller, got: %s", output) } } + +func TestReservedKeys(t *testing.T) { + var buf bytes.Buffer + reservedKeyTestVal := "surprise! I'm a string" + + tests := map[string]struct { + logStyle LogStyle + levelKey string + sourceKey string + timeKey string + }{ + "slog_log_style": {logStyle: SlogStyle, levelKey: "level", sourceKey: "source", timeKey: "time"}, + "go-kit_log_style": {logStyle: GoKitStyle, levelKey: "level", sourceKey: "caller", timeKey: "ts"}, + } + + for name, tc := range tests { + t.Run(name, func(t *testing.T) { + buf.Reset() // Ensure buf is reset prior to tests + config := &Config{Writer: &buf, Style: tc.logStyle} + logger := New(config) + + logger.LogAttrs(context.Background(), + slog.LevelInfo, + "reserved keys test for "+name, + slog.String(tc.levelKey, reservedKeyTestVal), + slog.String(tc.sourceKey, reservedKeyTestVal), + slog.String(tc.timeKey, reservedKeyTestVal), + ) + + output := buf.String() + require.Containsf(t, output, fmt.Sprintf("%s%s=\"%s\"", reservedKeyPrefix, tc.levelKey, reservedKeyTestVal), "Expected duplicate level key to be renamed") + require.Containsf(t, output, fmt.Sprintf("%s%s=\"%s\"", reservedKeyPrefix, tc.sourceKey, reservedKeyTestVal), "Expected duplicate source key to be renamed") + require.Containsf(t, output, fmt.Sprintf("%s%s=\"%s\"", reservedKeyPrefix, tc.timeKey, reservedKeyTestVal), "Expected duplicate time key to be renamed") + + // Print logs for humans to see, if needed. + fmt.Println(buf.String()) + }) + } +} From 1b10c090a2be38b11ec58f9063e52e42a767cbe9 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 20 Jan 2025 14:32:07 +0100 Subject: [PATCH 34/48] Bump golang.org/x/oauth2 from 0.24.0 to 0.25.0 (#750) Bumps [golang.org/x/oauth2](https://github.com/golang/oauth2) from 0.24.0 to 0.25.0. - [Commits](https://github.com/golang/oauth2/compare/v0.24.0...v0.25.0) --- updated-dependencies: - dependency-name: golang.org/x/oauth2 dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- go.mod | 2 +- go.sum | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/go.mod b/go.mod index 4d62719bb..a907b7e13 100644 --- a/go.mod +++ b/go.mod @@ -11,7 +11,7 @@ require ( github.com/prometheus/client_model v0.6.1 github.com/stretchr/testify v1.10.0 golang.org/x/net v0.33.0 - golang.org/x/oauth2 v0.24.0 + golang.org/x/oauth2 v0.25.0 google.golang.org/protobuf v1.36.1 gopkg.in/yaml.v2 v2.4.0 ) diff --git a/go.sum b/go.sum index b5955f01f..106f42cf6 100644 --- a/go.sum +++ b/go.sum @@ -45,8 +45,8 @@ github.com/xhit/go-str2duration/v2 v2.1.0 h1:lxklc02Drh6ynqX+DdPyp5pCKLUQpRT8bp8 github.com/xhit/go-str2duration/v2 v2.1.0/go.mod h1:ohY8p+0f07DiV6Em5LKB0s2YpLtXVyJfNt1+BlmyAsU= golang.org/x/net v0.33.0 h1:74SYHlV8BIgHIFC/LrYkOGIwL19eTYXQ5wc6TBuO36I= golang.org/x/net v0.33.0/go.mod h1:HXLR5J+9DxmrqMwG9qjGCxZ+zKXxBru04zlTvWlWuN4= -golang.org/x/oauth2 v0.24.0 h1:KTBBxWqUa0ykRPLtV69rRto9TLXcqYkeswu48x/gvNE= -golang.org/x/oauth2 v0.24.0/go.mod h1:XYTD2NtWslqkgxebSiOHnXEap4TF09sJSc7H1sXbhtI= +golang.org/x/oauth2 v0.25.0 h1:CY4y7XT9v0cRI9oupztF8AgiIu99L/ksR/Xp/6jrZ70= +golang.org/x/oauth2 v0.25.0/go.mod h1:XYTD2NtWslqkgxebSiOHnXEap4TF09sJSc7H1sXbhtI= golang.org/x/sys v0.28.0 h1:Fksou7UEQUWlKvIdsqzJmUmCX3cZuD2+P3XyyzwMhlA= golang.org/x/sys v0.28.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= golang.org/x/text v0.21.0 h1:zyQAAkrwaneQ066sspRyJaG9VNi/YJ1NfzcGB3hZ/qo= From 5b0d088c83bef75c876d51144b3ec684f7fb0c6d Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 20 Jan 2025 14:43:46 +0100 Subject: [PATCH 35/48] Bump golang.org/x/net from 0.33.0 to 0.34.0 (#749) Bumps [golang.org/x/net](https://github.com/golang/net) from 0.33.0 to 0.34.0. - [Commits](https://github.com/golang/net/compare/v0.33.0...v0.34.0) --- updated-dependencies: - dependency-name: golang.org/x/net dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- go.mod | 4 ++-- go.sum | 8 ++++---- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/go.mod b/go.mod index a907b7e13..b9c113bc7 100644 --- a/go.mod +++ b/go.mod @@ -10,7 +10,7 @@ require ( github.com/mwitkow/go-conntrack v0.0.0-20190716064945-2f068394615f github.com/prometheus/client_model v0.6.1 github.com/stretchr/testify v1.10.0 - golang.org/x/net v0.33.0 + golang.org/x/net v0.34.0 golang.org/x/oauth2 v0.25.0 google.golang.org/protobuf v1.36.1 gopkg.in/yaml.v2 v2.4.0 @@ -27,7 +27,7 @@ require ( github.com/prometheus/procfs v0.15.1 // indirect github.com/rogpeppe/go-internal v1.10.0 // indirect github.com/xhit/go-str2duration/v2 v2.1.0 // indirect - golang.org/x/sys v0.28.0 // indirect + golang.org/x/sys v0.29.0 // indirect golang.org/x/text v0.21.0 // indirect gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c // indirect gopkg.in/yaml.v3 v3.0.1 // indirect diff --git a/go.sum b/go.sum index 106f42cf6..4365a3890 100644 --- a/go.sum +++ b/go.sum @@ -43,12 +43,12 @@ github.com/stretchr/testify v1.10.0 h1:Xv5erBjTwe/5IxqUQTdXv5kgmIvbHo3QQyRwhJsOf github.com/stretchr/testify v1.10.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY= github.com/xhit/go-str2duration/v2 v2.1.0 h1:lxklc02Drh6ynqX+DdPyp5pCKLUQpRT8bp8Ydu2Bstc= github.com/xhit/go-str2duration/v2 v2.1.0/go.mod h1:ohY8p+0f07DiV6Em5LKB0s2YpLtXVyJfNt1+BlmyAsU= -golang.org/x/net v0.33.0 h1:74SYHlV8BIgHIFC/LrYkOGIwL19eTYXQ5wc6TBuO36I= -golang.org/x/net v0.33.0/go.mod h1:HXLR5J+9DxmrqMwG9qjGCxZ+zKXxBru04zlTvWlWuN4= +golang.org/x/net v0.34.0 h1:Mb7Mrk043xzHgnRM88suvJFwzVrRfHEHJEl5/71CKw0= +golang.org/x/net v0.34.0/go.mod h1:di0qlW3YNM5oh6GqDGQr92MyTozJPmybPK4Ev/Gm31k= golang.org/x/oauth2 v0.25.0 h1:CY4y7XT9v0cRI9oupztF8AgiIu99L/ksR/Xp/6jrZ70= golang.org/x/oauth2 v0.25.0/go.mod h1:XYTD2NtWslqkgxebSiOHnXEap4TF09sJSc7H1sXbhtI= -golang.org/x/sys v0.28.0 h1:Fksou7UEQUWlKvIdsqzJmUmCX3cZuD2+P3XyyzwMhlA= -golang.org/x/sys v0.28.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= +golang.org/x/sys v0.29.0 h1:TPYlXGxvx1MGTn2GiZDhnjPA9wZzZeGKHHmKhHYvgaU= +golang.org/x/sys v0.29.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= golang.org/x/text v0.21.0 h1:zyQAAkrwaneQ066sspRyJaG9VNi/YJ1NfzcGB3hZ/qo= golang.org/x/text v0.21.0/go.mod h1:4IBbMaMmOPCJ8SecivzSH54+73PCFmPWxNTLm+vZkEQ= google.golang.org/protobuf v1.36.1 h1:yBPeRvTftaleIgM3PZ/WBIZ7XM/eEYAaEyCwvyjq/gk= From 7684929007b4a1722c382f95d06e597eec7956e6 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 20 Jan 2025 14:43:57 +0100 Subject: [PATCH 36/48] Bump google.golang.org/protobuf from 1.36.1 to 1.36.3 (#751) Bumps google.golang.org/protobuf from 1.36.1 to 1.36.3. --- updated-dependencies: - dependency-name: google.golang.org/protobuf dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- go.mod | 2 +- go.sum | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/go.mod b/go.mod index b9c113bc7..5d2f2cbb3 100644 --- a/go.mod +++ b/go.mod @@ -12,7 +12,7 @@ require ( github.com/stretchr/testify v1.10.0 golang.org/x/net v0.34.0 golang.org/x/oauth2 v0.25.0 - google.golang.org/protobuf v1.36.1 + google.golang.org/protobuf v1.36.3 gopkg.in/yaml.v2 v2.4.0 ) diff --git a/go.sum b/go.sum index 4365a3890..17504316a 100644 --- a/go.sum +++ b/go.sum @@ -51,8 +51,8 @@ golang.org/x/sys v0.29.0 h1:TPYlXGxvx1MGTn2GiZDhnjPA9wZzZeGKHHmKhHYvgaU= golang.org/x/sys v0.29.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= golang.org/x/text v0.21.0 h1:zyQAAkrwaneQ066sspRyJaG9VNi/YJ1NfzcGB3hZ/qo= golang.org/x/text v0.21.0/go.mod h1:4IBbMaMmOPCJ8SecivzSH54+73PCFmPWxNTLm+vZkEQ= -google.golang.org/protobuf v1.36.1 h1:yBPeRvTftaleIgM3PZ/WBIZ7XM/eEYAaEyCwvyjq/gk= -google.golang.org/protobuf v1.36.1/go.mod h1:9fA7Ob0pmnwhb644+1+CVWFRbNajQ6iRojtC/QF5bRE= +google.golang.org/protobuf v1.36.3 h1:82DV7MYdb8anAVi3qge1wSnMDrnKK7ebr+I0hHRN1BU= +google.golang.org/protobuf v1.36.3/go.mod h1:9fA7Ob0pmnwhb644+1+CVWFRbNajQ6iRojtC/QF5bRE= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk= gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q= From 9fedd39e6baf15bb14c7230af364325633a012b5 Mon Sep 17 00:00:00 2001 From: Peter Nguyen Date: Tue, 28 Jan 2025 11:23:50 -0800 Subject: [PATCH 37/48] Fix typo 'the an' Signed-off-by: Peter Nguyen --- model/labels.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/model/labels.go b/model/labels.go index 73b7aa3e6..f4a387605 100644 --- a/model/labels.go +++ b/model/labels.go @@ -22,7 +22,7 @@ import ( ) const ( - // AlertNameLabel is the name of the label containing the an alert's name. + // AlertNameLabel is the name of the label containing the alert's name. AlertNameLabel = "alertname" // ExportedLabelPrefix is the prefix to prepend to the label names present in From a784287e355f1b8850d454d9993c2aab112a6eaa Mon Sep 17 00:00:00 2001 From: Bartlomiej Plotka Date: Mon, 3 Feb 2025 09:50:08 +0100 Subject: [PATCH 38/48] promslog: Make AllowedLevel concurrency safe. (#754) * promslog: Make AllowedLevel concurrency safe. Needed for https://github.com/prometheus/prometheus/issues/10352 Also I renamed AllowedLevel and AllowedFormat to Level and Format. Default level (and String()) is also now 'info' not empty. It's a breaking change, but I suspect nobody was using those constructs directly, WDYT? Signed-off-by: bwplotka * info is by default, so no need for the set in New Signed-off-by: bwplotka --------- Signed-off-by: bwplotka --- promslog/flag/flag.go | 4 +- promslog/slog.go | 271 ++++++++++++++++++++++-------------------- promslog/slog_test.go | 18 +-- 3 files changed, 154 insertions(+), 139 deletions(-) diff --git a/promslog/flag/flag.go b/promslog/flag/flag.go index 0a164fcc1..85c67b250 100644 --- a/promslog/flag/flag.go +++ b/promslog/flag/flag.go @@ -42,12 +42,12 @@ var FormatFlagHelp = "Output format of log messages. One of: [" + strings.Join(p // AddFlags adds the flags used by this package to the Kingpin application. // To use the default Kingpin application, call AddFlags(kingpin.CommandLine) func AddFlags(a *kingpin.Application, config *promslog.Config) { - config.Level = &promslog.AllowedLevel{} + config.Level = promslog.NewLevel() a.Flag(LevelFlagName, LevelFlagHelp). Default("info").HintOptions(promslog.LevelFlagOptions...). SetValue(config.Level) - config.Format = &promslog.AllowedFormat{} + config.Format = promslog.NewFormat() a.Flag(FormatFlagName, FormatFlagHelp). Default("logfmt").HintOptions(promslog.FormatFlagOptions...). SetValue(config.Format) diff --git a/promslog/slog.go b/promslog/slog.go index 11c33fb66..f9f899663 100644 --- a/promslog/slog.go +++ b/promslog/slog.go @@ -28,6 +28,7 @@ import ( "time" ) +// LogStyle represents the common logging formats in the Prometheus ecosystem. type LogStyle string const ( @@ -38,115 +39,29 @@ const ( ) var ( - LevelFlagOptions = []string{"debug", "info", "warn", "error"} + // LevelFlagOptions represents allowed logging levels. + LevelFlagOptions = []string{"debug", "info", "warn", "error"} + // FormatFlagOptions represents allowed formats. FormatFlagOptions = []string{"logfmt", "json"} - callerAddFunc = false - defaultWriter = os.Stderr - goKitStyleReplaceAttrFunc = func(groups []string, a slog.Attr) slog.Attr { - key := a.Key - switch key { - case slog.TimeKey, "ts": - if t, ok := a.Value.Any().(time.Time); ok { - a.Key = "ts" - - // This timestamp format differs from RFC3339Nano by using .000 instead - // of .999999999 which changes the timestamp from 9 variable to 3 fixed - // decimals (.130 instead of .130987456). - a.Value = slog.StringValue(t.UTC().Format("2006-01-02T15:04:05.000Z07:00")) - } else { - // If we can't cast the any from the value to a - // time.Time, it means the caller logged - // another attribute with a key of `ts`. - // Prevent duplicate keys (necessary for proper - // JSON) by renaming the key to `logged_ts`. - a.Key = reservedKeyPrefix + key - } - case slog.SourceKey, "caller": - if src, ok := a.Value.Any().(*slog.Source); ok { - a.Key = "caller" - switch callerAddFunc { - case true: - a.Value = slog.StringValue(filepath.Base(src.File) + "(" + filepath.Base(src.Function) + "):" + strconv.Itoa(src.Line)) - default: - a.Value = slog.StringValue(filepath.Base(src.File) + ":" + strconv.Itoa(src.Line)) - } - } else { - // If we can't cast the any from the value to - // an *slog.Source, it means the caller logged - // another attribute with a key of `caller`. - // Prevent duplicate keys (necessary for proper - // JSON) by renaming the key to - // `logged_caller`. - a.Key = reservedKeyPrefix + key - } - case slog.LevelKey: - if lvl, ok := a.Value.Any().(slog.Level); ok { - a.Value = slog.StringValue(strings.ToLower(lvl.String())) - } else { - // If we can't cast the any from the value to - // an slog.Level, it means the caller logged - // another attribute with a key of `level`. - // Prevent duplicate keys (necessary for proper - // JSON) by renaming the key to `logged_level`. - a.Key = reservedKeyPrefix + key - } - default: - } - - return a - } - defaultReplaceAttrFunc = func(groups []string, a slog.Attr) slog.Attr { - key := a.Key - switch key { - case slog.TimeKey: - if t, ok := a.Value.Any().(time.Time); ok { - a.Value = slog.TimeValue(t.UTC()) - } else { - // If we can't cast the any from the value to a - // time.Time, it means the caller logged - // another attribute with a key of `time`. - // Prevent duplicate keys (necessary for proper - // JSON) by renaming the key to `logged_time`. - a.Key = reservedKeyPrefix + key - } - case slog.SourceKey: - if src, ok := a.Value.Any().(*slog.Source); ok { - a.Value = slog.StringValue(filepath.Base(src.File) + ":" + strconv.Itoa(src.Line)) - } else { - // If we can't cast the any from the value to - // an *slog.Source, it means the caller logged - // another attribute with a key of `source`. - // Prevent duplicate keys (necessary for proper - // JSON) by renaming the key to - // `logged_source`. - a.Key = reservedKeyPrefix + key - } - case slog.LevelKey: - if _, ok := a.Value.Any().(slog.Level); !ok { - // If we can't cast the any from the value to - // an slog.Level, it means the caller logged - // another attribute with a key of `level`. - // Prevent duplicate keys (necessary for proper - // JSON) by renaming the key to - // `logged_level`. - a.Key = reservedKeyPrefix + key - } - default: - } - - return a - } + defaultWriter = os.Stderr ) -// AllowedLevel is a settable identifier for the minimum level a log entry -// must be have. -type AllowedLevel struct { - s string +// Level controls a logging level, with an info default. +// It wraps slog.LevelVar with string-based level control. +// Level is safe to be used concurrently. +type Level struct { lvl *slog.LevelVar } -func (l *AllowedLevel) UnmarshalYAML(unmarshal func(interface{}) error) error { +// NewLevel returns a new Level. +func NewLevel() *Level { + return &Level{ + lvl: &slog.LevelVar{}, + } +} + +func (l *Level) UnmarshalYAML(unmarshal func(interface{}) error) error { var s string type plain string if err := unmarshal((*plain)(&s)); err != nil { @@ -155,55 +70,60 @@ func (l *AllowedLevel) UnmarshalYAML(unmarshal func(interface{}) error) error { if s == "" { return nil } - lo := &AllowedLevel{} - if err := lo.Set(s); err != nil { + if err := l.Set(s); err != nil { return err } - *l = *lo return nil } -func (l *AllowedLevel) String() string { - return l.s -} - -// Set updates the value of the allowed level. -func (l *AllowedLevel) Set(s string) error { - if l.lvl == nil { - l.lvl = &slog.LevelVar{} +// String returns the current level. +func (l *Level) String() string { + switch l.lvl.Level() { + case slog.LevelDebug: + return "debug" + case slog.LevelInfo: + return "info" + case slog.LevelWarn: + return "warn" + case slog.LevelError: + return "error" + default: + return "" } +} +// Set updates the logging level with the validation. +func (l *Level) Set(s string) error { switch strings.ToLower(s) { case "debug": l.lvl.Set(slog.LevelDebug) - callerAddFunc = true case "info": l.lvl.Set(slog.LevelInfo) - callerAddFunc = false case "warn": l.lvl.Set(slog.LevelWarn) - callerAddFunc = false case "error": l.lvl.Set(slog.LevelError) - callerAddFunc = false default: return fmt.Errorf("unrecognized log level %s", s) } - l.s = s return nil } -// AllowedFormat is a settable identifier for the output format that the logger can have. -type AllowedFormat struct { +// Format controls a logging output format. +// Not concurrency-safe. +type Format struct { s string } -func (f *AllowedFormat) String() string { +// NewFormat creates a new Format. +func NewFormat() *Format { return &Format{} } + +func (f *Format) String() string { return f.s } // Set updates the value of the allowed format. -func (f *AllowedFormat) Set(s string) error { +func (f *Format) Set(s string) error { switch s { case "logfmt", "json": f.s = s @@ -215,18 +135,113 @@ func (f *AllowedFormat) Set(s string) error { // Config is a struct containing configurable settings for the logger type Config struct { - Level *AllowedLevel - Format *AllowedFormat + Level *Level + Format *Format Style LogStyle Writer io.Writer } +func newGoKitStyleReplaceAttrFunc(lvl *Level) func(groups []string, a slog.Attr) slog.Attr { + return func(groups []string, a slog.Attr) slog.Attr { + key := a.Key + switch key { + case slog.TimeKey, "ts": + if t, ok := a.Value.Any().(time.Time); ok { + a.Key = "ts" + + // This timestamp format differs from RFC3339Nano by using .000 instead + // of .999999999 which changes the timestamp from 9 variable to 3 fixed + // decimals (.130 instead of .130987456). + a.Value = slog.StringValue(t.UTC().Format("2006-01-02T15:04:05.000Z07:00")) + } else { + // If we can't cast the any from the value to a + // time.Time, it means the caller logged + // another attribute with a key of `ts`. + // Prevent duplicate keys (necessary for proper + // JSON) by renaming the key to `logged_ts`. + a.Key = reservedKeyPrefix + key + } + case slog.SourceKey, "caller": + if src, ok := a.Value.Any().(*slog.Source); ok { + a.Key = "caller" + switch lvl.String() { + case "debug": + a.Value = slog.StringValue(filepath.Base(src.File) + "(" + filepath.Base(src.Function) + "):" + strconv.Itoa(src.Line)) + default: + a.Value = slog.StringValue(filepath.Base(src.File) + ":" + strconv.Itoa(src.Line)) + } + } else { + // If we can't cast the any from the value to + // an *slog.Source, it means the caller logged + // another attribute with a key of `caller`. + // Prevent duplicate keys (necessary for proper + // JSON) by renaming the key to + // `logged_caller`. + a.Key = reservedKeyPrefix + key + } + case slog.LevelKey: + if lvl, ok := a.Value.Any().(slog.Level); ok { + a.Value = slog.StringValue(strings.ToLower(lvl.String())) + } else { + // If we can't cast the any from the value to + // an slog.Level, it means the caller logged + // another attribute with a key of `level`. + // Prevent duplicate keys (necessary for proper + // JSON) by renaming the key to `logged_level`. + a.Key = reservedKeyPrefix + key + } + default: + } + return a + } +} + +func defaultReplaceAttr(_ []string, a slog.Attr) slog.Attr { + key := a.Key + switch key { + case slog.TimeKey: + if t, ok := a.Value.Any().(time.Time); ok { + a.Value = slog.TimeValue(t.UTC()) + } else { + // If we can't cast the any from the value to a + // time.Time, it means the caller logged + // another attribute with a key of `time`. + // Prevent duplicate keys (necessary for proper + // JSON) by renaming the key to `logged_time`. + a.Key = reservedKeyPrefix + key + } + case slog.SourceKey: + if src, ok := a.Value.Any().(*slog.Source); ok { + a.Value = slog.StringValue(filepath.Base(src.File) + ":" + strconv.Itoa(src.Line)) + } else { + // If we can't cast the any from the value to + // an *slog.Source, it means the caller logged + // another attribute with a key of `source`. + // Prevent duplicate keys (necessary for proper + // JSON) by renaming the key to + // `logged_source`. + a.Key = reservedKeyPrefix + key + } + case slog.LevelKey: + if _, ok := a.Value.Any().(slog.Level); !ok { + // If we can't cast the any from the value to + // an slog.Level, it means the caller logged + // another attribute with a key of `level`. + // Prevent duplicate keys (necessary for proper + // JSON) by renaming the key to + // `logged_level`. + a.Key = reservedKeyPrefix + key + } + default: + } + return a +} + // New returns a new slog.Logger. Each logged line will be annotated // with a timestamp. The output always goes to stderr. func New(config *Config) *slog.Logger { if config.Level == nil { - config.Level = &AllowedLevel{} - _ = config.Level.Set("info") + config.Level = NewLevel() } if config.Writer == nil { @@ -236,11 +251,11 @@ func New(config *Config) *slog.Logger { logHandlerOpts := &slog.HandlerOptions{ Level: config.Level.lvl, AddSource: true, - ReplaceAttr: defaultReplaceAttrFunc, + ReplaceAttr: defaultReplaceAttr, } if config.Style == GoKitStyle { - logHandlerOpts.ReplaceAttr = goKitStyleReplaceAttrFunc + logHandlerOpts.ReplaceAttr = newGoKitStyleReplaceAttrFunc(config.Level) } if config.Format != nil && config.Format.s == "json" { diff --git a/promslog/slog_test.go b/promslog/slog_test.go index 6da14d987..ea4e176c2 100644 --- a/promslog/slog_test.go +++ b/promslog/slog_test.go @@ -43,29 +43,29 @@ func TestDefaultConfig(t *testing.T) { } func TestUnmarshallLevel(t *testing.T) { - l := &AllowedLevel{} + l := NewLevel() err := yaml.Unmarshal([]byte(`debug`), l) if err != nil { t.Error(err) } - if l.s != "debug" { - t.Errorf("expected %s, got %s", "debug", l.s) + if got := l.String(); got != "debug" { + t.Errorf("expected %s, got %s", "debug", got) } } func TestUnmarshallEmptyLevel(t *testing.T) { - l := &AllowedLevel{} + l := NewLevel() err := yaml.Unmarshal([]byte(``), l) if err != nil { t.Error(err) } - if l.s != "" { - t.Errorf("expected empty level, got %s", l.s) + if got := l.String(); got != "info" { + t.Errorf("expected info (default) level, got %s", got) } } func TestUnmarshallBadLevel(t *testing.T) { - l := &AllowedLevel{} + l := NewLevel() err := yaml.Unmarshal([]byte(`debugg`), l) if err == nil { t.Error("expected error") @@ -74,8 +74,8 @@ func TestUnmarshallBadLevel(t *testing.T) { if err.Error() != expErr { t.Errorf("expected error %s, got %s", expErr, err.Error()) } - if l.s != "" { - t.Errorf("expected empty level, got %s", l.s) + if got := l.String(); got != "info" { + t.Errorf("expected info (default) level, got %s", got) } } From cc17dab08e7c33f70e3b5ab865d6789f0ff95761 Mon Sep 17 00:00:00 2001 From: PrometheusBot Date: Sun, 9 Feb 2025 09:19:44 +0100 Subject: [PATCH 39/48] Update common Prometheus files (#757) Signed-off-by: prombot --- .github/workflows/golangci-lint.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/golangci-lint.yml b/.github/workflows/golangci-lint.yml index 0c00c410a..def9007ac 100644 --- a/.github/workflows/golangci-lint.yml +++ b/.github/workflows/golangci-lint.yml @@ -26,14 +26,14 @@ jobs: - name: Checkout repository uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2 - name: Install Go - uses: actions/setup-go@3041bf56c941b39c61721a86cd11f3bb1338122a # v5.2.0 + uses: actions/setup-go@f111f3307d8850f501ac008e886eec1fd1932a34 # v5.3.0 with: go-version: 1.23.x - name: Install snmp_exporter/generator dependencies run: sudo apt-get update && sudo apt-get -y install libsnmp-dev if: github.repository == 'prometheus/snmp_exporter' - name: Lint - uses: golangci/golangci-lint-action@971e284b6050e8a5849b72094c50ab08da042db8 # v6.1.1 + uses: golangci/golangci-lint-action@ec5d18412c0aeab7936cb16880d708ba2a64e1ae # v6.2.0 with: args: --verbose version: v1.63.4 From ca40aa08f025cc1a857cec6d6773363e959b4ea4 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Wed, 19 Feb 2025 10:07:44 +0100 Subject: [PATCH 40/48] build(deps): bump google.golang.org/protobuf from 1.36.3 to 1.36.4 (#756) Bumps google.golang.org/protobuf from 1.36.3 to 1.36.4. --- updated-dependencies: - dependency-name: google.golang.org/protobuf dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- go.mod | 2 +- go.sum | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/go.mod b/go.mod index 5d2f2cbb3..adf229778 100644 --- a/go.mod +++ b/go.mod @@ -12,7 +12,7 @@ require ( github.com/stretchr/testify v1.10.0 golang.org/x/net v0.34.0 golang.org/x/oauth2 v0.25.0 - google.golang.org/protobuf v1.36.3 + google.golang.org/protobuf v1.36.4 gopkg.in/yaml.v2 v2.4.0 ) diff --git a/go.sum b/go.sum index 17504316a..8766963dd 100644 --- a/go.sum +++ b/go.sum @@ -51,8 +51,8 @@ golang.org/x/sys v0.29.0 h1:TPYlXGxvx1MGTn2GiZDhnjPA9wZzZeGKHHmKhHYvgaU= golang.org/x/sys v0.29.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= golang.org/x/text v0.21.0 h1:zyQAAkrwaneQ066sspRyJaG9VNi/YJ1NfzcGB3hZ/qo= golang.org/x/text v0.21.0/go.mod h1:4IBbMaMmOPCJ8SecivzSH54+73PCFmPWxNTLm+vZkEQ= -google.golang.org/protobuf v1.36.3 h1:82DV7MYdb8anAVi3qge1wSnMDrnKK7ebr+I0hHRN1BU= -google.golang.org/protobuf v1.36.3/go.mod h1:9fA7Ob0pmnwhb644+1+CVWFRbNajQ6iRojtC/QF5bRE= +google.golang.org/protobuf v1.36.4 h1:6A3ZDJHn/eNqc1i+IdefRzy/9PokBTPvcqMySR7NNIM= +google.golang.org/protobuf v1.36.4/go.mod h1:9fA7Ob0pmnwhb644+1+CVWFRbNajQ6iRojtC/QF5bRE= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk= gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q= From 0db99daa6f43d55a09dbb714fe3173182977bf8f Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Sun, 2 Mar 2025 20:40:43 +0100 Subject: [PATCH 41/48] build(deps): bump google.golang.org/protobuf from 1.36.4 to 1.36.5 (#761) Bumps google.golang.org/protobuf from 1.36.4 to 1.36.5. --- updated-dependencies: - dependency-name: google.golang.org/protobuf dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- go.mod | 2 +- go.sum | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/go.mod b/go.mod index adf229778..43cfb6b9e 100644 --- a/go.mod +++ b/go.mod @@ -12,7 +12,7 @@ require ( github.com/stretchr/testify v1.10.0 golang.org/x/net v0.34.0 golang.org/x/oauth2 v0.25.0 - google.golang.org/protobuf v1.36.4 + google.golang.org/protobuf v1.36.5 gopkg.in/yaml.v2 v2.4.0 ) diff --git a/go.sum b/go.sum index 8766963dd..51b5de64e 100644 --- a/go.sum +++ b/go.sum @@ -51,8 +51,8 @@ golang.org/x/sys v0.29.0 h1:TPYlXGxvx1MGTn2GiZDhnjPA9wZzZeGKHHmKhHYvgaU= golang.org/x/sys v0.29.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= golang.org/x/text v0.21.0 h1:zyQAAkrwaneQ066sspRyJaG9VNi/YJ1NfzcGB3hZ/qo= golang.org/x/text v0.21.0/go.mod h1:4IBbMaMmOPCJ8SecivzSH54+73PCFmPWxNTLm+vZkEQ= -google.golang.org/protobuf v1.36.4 h1:6A3ZDJHn/eNqc1i+IdefRzy/9PokBTPvcqMySR7NNIM= -google.golang.org/protobuf v1.36.4/go.mod h1:9fA7Ob0pmnwhb644+1+CVWFRbNajQ6iRojtC/QF5bRE= +google.golang.org/protobuf v1.36.5 h1:tPhr+woSbjfYvY6/GPufUoYizxw1cF/yFoxJ2fmpwlM= +google.golang.org/protobuf v1.36.5/go.mod h1:9fA7Ob0pmnwhb644+1+CVWFRbNajQ6iRojtC/QF5bRE= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk= gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q= From b516f6d622f0d210e24036fe64e2009fcc44885f Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Sun, 2 Mar 2025 20:41:00 +0100 Subject: [PATCH 42/48] build(deps): bump github.com/google/go-cmp from 0.6.0 to 0.7.0 (#763) Bumps [github.com/google/go-cmp](https://github.com/google/go-cmp) from 0.6.0 to 0.7.0. - [Release notes](https://github.com/google/go-cmp/releases) - [Commits](https://github.com/google/go-cmp/compare/v0.6.0...v0.7.0) --- updated-dependencies: - dependency-name: github.com/google/go-cmp dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- go.mod | 2 +- go.sum | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/go.mod b/go.mod index 43cfb6b9e..753e5efa7 100644 --- a/go.mod +++ b/go.mod @@ -4,7 +4,7 @@ go 1.21 require ( github.com/alecthomas/kingpin/v2 v2.4.0 - github.com/google/go-cmp v0.6.0 + github.com/google/go-cmp v0.7.0 github.com/julienschmidt/httprouter v1.3.0 github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822 github.com/mwitkow/go-conntrack v0.0.0-20190716064945-2f068394615f diff --git a/go.sum b/go.sum index 51b5de64e..8c8f76d62 100644 --- a/go.sum +++ b/go.sum @@ -9,8 +9,8 @@ github.com/cespare/xxhash/v2 v2.3.0/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XL github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= -github.com/google/go-cmp v0.6.0 h1:ofyhxvXcZhMsU5ulbFiLKl/XBFqE1GSq7atu8tAmTRI= -github.com/google/go-cmp v0.6.0/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY= +github.com/google/go-cmp v0.7.0 h1:wk8382ETsv4JYUZwIsn6YpYiWiBsYLSJiTsyBybVuN8= +github.com/google/go-cmp v0.7.0/go.mod h1:pXiqmnSA92OHEEa9HXL2W4E7lf9JzCmGVUdgjX3N/iU= github.com/jpillora/backoff v1.0.0 h1:uvFg412JmmHBHw7iwprIxkPMI+sGQ4kzOWsMeHnm2EA= github.com/jpillora/backoff v1.0.0/go.mod h1:J/6gKK9jxlEcS3zixgDgUAsiuZ7yrSoa/FX5e0EB2j4= github.com/julienschmidt/httprouter v1.3.0 h1:U0609e9tgbseu3rBINet9P48AI/D3oJs4dN7jwJOQ1U= From 56f6f3853a55cff5c626bc5a04993b3871ff260c Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Sun, 2 Mar 2025 20:46:46 +0100 Subject: [PATCH 43/48] build(deps): bump golang.org/x/net from 0.34.0 to 0.35.0 (#762) Bumps [golang.org/x/net](https://github.com/golang/net) from 0.34.0 to 0.35.0. - [Commits](https://github.com/golang/net/compare/v0.34.0...v0.35.0) --- updated-dependencies: - dependency-name: golang.org/x/net dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- go.mod | 6 +++--- go.sum | 12 ++++++------ 2 files changed, 9 insertions(+), 9 deletions(-) diff --git a/go.mod b/go.mod index 753e5efa7..b924a3f70 100644 --- a/go.mod +++ b/go.mod @@ -10,7 +10,7 @@ require ( github.com/mwitkow/go-conntrack v0.0.0-20190716064945-2f068394615f github.com/prometheus/client_model v0.6.1 github.com/stretchr/testify v1.10.0 - golang.org/x/net v0.34.0 + golang.org/x/net v0.35.0 golang.org/x/oauth2 v0.25.0 google.golang.org/protobuf v1.36.5 gopkg.in/yaml.v2 v2.4.0 @@ -27,8 +27,8 @@ require ( github.com/prometheus/procfs v0.15.1 // indirect github.com/rogpeppe/go-internal v1.10.0 // indirect github.com/xhit/go-str2duration/v2 v2.1.0 // indirect - golang.org/x/sys v0.29.0 // indirect - golang.org/x/text v0.21.0 // indirect + golang.org/x/sys v0.30.0 // indirect + golang.org/x/text v0.22.0 // indirect gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c // indirect gopkg.in/yaml.v3 v3.0.1 // indirect ) diff --git a/go.sum b/go.sum index 8c8f76d62..009bd785d 100644 --- a/go.sum +++ b/go.sum @@ -43,14 +43,14 @@ github.com/stretchr/testify v1.10.0 h1:Xv5erBjTwe/5IxqUQTdXv5kgmIvbHo3QQyRwhJsOf github.com/stretchr/testify v1.10.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY= github.com/xhit/go-str2duration/v2 v2.1.0 h1:lxklc02Drh6ynqX+DdPyp5pCKLUQpRT8bp8Ydu2Bstc= github.com/xhit/go-str2duration/v2 v2.1.0/go.mod h1:ohY8p+0f07DiV6Em5LKB0s2YpLtXVyJfNt1+BlmyAsU= -golang.org/x/net v0.34.0 h1:Mb7Mrk043xzHgnRM88suvJFwzVrRfHEHJEl5/71CKw0= -golang.org/x/net v0.34.0/go.mod h1:di0qlW3YNM5oh6GqDGQr92MyTozJPmybPK4Ev/Gm31k= +golang.org/x/net v0.35.0 h1:T5GQRQb2y08kTAByq9L4/bz8cipCdA8FbRTXewonqY8= +golang.org/x/net v0.35.0/go.mod h1:EglIi67kWsHKlRzzVMUD93VMSWGFOMSZgxFjparz1Qk= golang.org/x/oauth2 v0.25.0 h1:CY4y7XT9v0cRI9oupztF8AgiIu99L/ksR/Xp/6jrZ70= golang.org/x/oauth2 v0.25.0/go.mod h1:XYTD2NtWslqkgxebSiOHnXEap4TF09sJSc7H1sXbhtI= -golang.org/x/sys v0.29.0 h1:TPYlXGxvx1MGTn2GiZDhnjPA9wZzZeGKHHmKhHYvgaU= -golang.org/x/sys v0.29.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= -golang.org/x/text v0.21.0 h1:zyQAAkrwaneQ066sspRyJaG9VNi/YJ1NfzcGB3hZ/qo= -golang.org/x/text v0.21.0/go.mod h1:4IBbMaMmOPCJ8SecivzSH54+73PCFmPWxNTLm+vZkEQ= +golang.org/x/sys v0.30.0 h1:QjkSwP/36a20jFYWkSue1YwXzLmsV5Gfq7Eiy72C1uc= +golang.org/x/sys v0.30.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= +golang.org/x/text v0.22.0 h1:bofq7m3/HAFvbF51jz3Q9wLg3jkvSPuiZu/pD1XwgtM= +golang.org/x/text v0.22.0/go.mod h1:YRoo4H8PVmsu+E3Ou7cqLVH8oXWIHVoX0jqUWALQhfY= google.golang.org/protobuf v1.36.5 h1:tPhr+woSbjfYvY6/GPufUoYizxw1cF/yFoxJ2fmpwlM= google.golang.org/protobuf v1.36.5/go.mod h1:9fA7Ob0pmnwhb644+1+CVWFRbNajQ6iRojtC/QF5bRE= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= From 6b9636ca14ecb6f8bb49d70aea6019c4cef93aab Mon Sep 17 00:00:00 2001 From: Owen Williams Date: Tue, 4 Mar 2025 14:44:18 -0500 Subject: [PATCH 44/48] model: Clarify the purpose of model.NameValidationScheme (#765) There is a lot of confusion around this global variable. Clarify that it is meant to indicate that a project is aware of UTF-8 support, and that those projects should have their own flags to control validation mode. Signed-off-by: Owen Williams --- expfmt/decode_test.go | 4 ++-- model/metric.go | 28 ++++++++++++++++++++-------- 2 files changed, 22 insertions(+), 10 deletions(-) diff --git a/expfmt/decode_test.go b/expfmt/decode_test.go index 10b12b667..759ff7461 100644 --- a/expfmt/decode_test.go +++ b/expfmt/decode_test.go @@ -369,7 +369,7 @@ func TestProtoDecoder(t *testing.T) { var all model.Vector for { - model.NameValidationScheme = model.LegacyValidation + model.NameValidationScheme = model.LegacyValidation //nolint:staticcheck var smpls model.Vector err := dec.Decode(&smpls) if err != nil && errors.Is(err, io.EOF) { @@ -377,7 +377,7 @@ func TestProtoDecoder(t *testing.T) { } if scenario.legacyNameFail { require.Errorf(t, err, "Expected error when decoding without UTF-8 support enabled but got none") - model.NameValidationScheme = model.UTF8Validation + model.NameValidationScheme = model.UTF8Validation //nolint:staticcheck dec = &SampleDecoder{ Dec: &protoDecoder{r: strings.NewReader(scenario.in)}, Opts: &DecodeOptions{ diff --git a/model/metric.go b/model/metric.go index 5766107cf..a6b01755b 100644 --- a/model/metric.go +++ b/model/metric.go @@ -27,13 +27,25 @@ import ( ) var ( - // NameValidationScheme determines the method of name validation to be used by - // all calls to IsValidMetricName() and LabelName IsValid(). Setting UTF-8 - // mode in isolation from other components that don't support UTF-8 may result - // in bugs or other undefined behavior. This value can be set to - // LegacyValidation during startup if a binary is not UTF-8-aware binaries. To - // avoid need for locking, this value should be set once, ideally in an - // init(), before multiple goroutines are started. + // NameValidationScheme determines the global default method of the name + // validation to be used by all calls to IsValidMetricName() and LabelName + // IsValid(). + // + // Deprecated: This variable should not be used and might be removed in the + // far future. If you wish to stick to the legacy name validation use + // `IsValidLegacyMetricName()` and `LabelName.IsValidLegacy()` methods + // instead. This variable is here as an escape hatch for emergency cases, + // given the recent change from `LegacyValidation` to `UTF8Validation`, e.g., + // to delay UTF-8 migrations in time or aid in debugging unforeseen results of + // the change. In such a case, a temporary assignment to `LegacyValidation` + // value in the `init()` function in your main.go or so, could be considered. + // + // Historically we opted for a global variable for feature gating different + // validation schemes in operations that were not otherwise easily adjustable + // (e.g. Labels yaml unmarshaling). That could have been a mistake, a separate + // Labels structure or package might have been a better choice. Given the + // change was made and many upgraded the common already, we live this as-is + // with this warning and learning for the future. NameValidationScheme = UTF8Validation // NameEscapingScheme defines the default way that names will be escaped when @@ -50,7 +62,7 @@ var ( type ValidationScheme int const ( - // LegacyValidation is a setting that requirets that metric and label names + // LegacyValidation is a setting that requires that all metric and label names // conform to the original Prometheus character requirements described by // MetricNameRE and LabelNameRE. LegacyValidation ValidationScheme = iota From 0decf1fe7a23d909d3f5cd400b76b6d178eef576 Mon Sep 17 00:00:00 2001 From: George Robinson Date: Fri, 7 Mar 2025 09:34:37 +0000 Subject: [PATCH 45/48] Fix spelling mistake in godoc (#766) This commit fixes a spelling mistake in the godoc for ResolvedAt. Signed-off-by: George Robinson --- model/alert.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/model/alert.go b/model/alert.go index bd3a39e3e..460f554f2 100644 --- a/model/alert.go +++ b/model/alert.go @@ -65,7 +65,7 @@ func (a *Alert) Resolved() bool { return a.ResolvedAt(time.Now()) } -// ResolvedAt returns true off the activity interval ended before +// ResolvedAt returns true iff the activity interval ended before // the given timestamp. func (a *Alert) ResolvedAt(ts time.Time) bool { if a.EndsAt.IsZero() { From a9cc7f7df30d52a26a3937c9d32953ef2bf452c6 Mon Sep 17 00:00:00 2001 From: PrometheusBot Date: Sat, 8 Mar 2025 15:05:08 +0100 Subject: [PATCH 46/48] Update common Prometheus files (#767) Signed-off-by: prombot --- .github/workflows/golangci-lint.yml | 6 +++--- Makefile.common | 2 +- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/.github/workflows/golangci-lint.yml b/.github/workflows/golangci-lint.yml index def9007ac..e36a9f1a4 100644 --- a/.github/workflows/golangci-lint.yml +++ b/.github/workflows/golangci-lint.yml @@ -28,12 +28,12 @@ jobs: - name: Install Go uses: actions/setup-go@f111f3307d8850f501ac008e886eec1fd1932a34 # v5.3.0 with: - go-version: 1.23.x + go-version: 1.24.x - name: Install snmp_exporter/generator dependencies run: sudo apt-get update && sudo apt-get -y install libsnmp-dev if: github.repository == 'prometheus/snmp_exporter' - name: Lint - uses: golangci/golangci-lint-action@ec5d18412c0aeab7936cb16880d708ba2a64e1ae # v6.2.0 + uses: golangci/golangci-lint-action@2226d7cb06a077cd73e56eedd38eecad18e5d837 # v6.5.0 with: args: --verbose - version: v1.63.4 + version: v1.64.6 diff --git a/Makefile.common b/Makefile.common index d1576bb31..8cb383859 100644 --- a/Makefile.common +++ b/Makefile.common @@ -61,7 +61,7 @@ PROMU_URL := https://github.com/prometheus/promu/releases/download/v$(PROMU_ SKIP_GOLANGCI_LINT := GOLANGCI_LINT := GOLANGCI_LINT_OPTS ?= -GOLANGCI_LINT_VERSION ?= v1.63.4 +GOLANGCI_LINT_VERSION ?= v1.64.6 # golangci-lint only supports linux, darwin and windows platforms on i386/amd64/arm64. # windows isn't included here because of the path separator being different. ifeq ($(GOHOSTOS),$(filter $(GOHOSTOS),linux darwin)) From 227989ceacbb831801f90965ed5e8c9c55749774 Mon Sep 17 00:00:00 2001 From: Arthur Silva Sens Date: Wed, 12 Mar 2025 14:48:42 -0300 Subject: [PATCH 47/48] otlptranslator: Add dependency free package that translator OTLP data into Prometheus metric/label names Signed-off-by: Arthur Silva Sens --- otlptranslator/constants.go | 58 +++ otlptranslator/doc.go | 24 + otlptranslator/label_builder.go | 54 +++ otlptranslator/label_builder_bench_test.go | 35 ++ otlptranslator/label_builder_test.go | 44 ++ otlptranslator/metric_name_builder.go | 286 ++++++++++++ .../metric_name_builder_bench_test.go | 134 ++++++ otlptranslator/metric_name_builder_test.go | 420 ++++++++++++++++++ 8 files changed, 1055 insertions(+) create mode 100644 otlptranslator/constants.go create mode 100644 otlptranslator/doc.go create mode 100644 otlptranslator/label_builder.go create mode 100644 otlptranslator/label_builder_bench_test.go create mode 100644 otlptranslator/label_builder_test.go create mode 100644 otlptranslator/metric_name_builder.go create mode 100644 otlptranslator/metric_name_builder_bench_test.go create mode 100644 otlptranslator/metric_name_builder_test.go diff --git a/otlptranslator/constants.go b/otlptranslator/constants.go new file mode 100644 index 000000000..d719daa1e --- /dev/null +++ b/otlptranslator/constants.go @@ -0,0 +1,58 @@ +// Copyright 2025 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 otlptranslator + +const ( + // MetricMetadataTypeKey is the key used to store the original Prometheus + // type in metric metadata: + // https://github.com/open-telemetry/opentelemetry-specification/blob/e6eccba97ebaffbbfad6d4358408a2cead0ec2df/specification/compatibility/prometheus_and_openmetrics.md#metric-metadata + MetricMetadataTypeKey = "prometheus.type" + // ExemplarTraceIDKey is the key used to store the trace ID in Prometheus + // exemplars: + // https://github.com/open-telemetry/opentelemetry-specification/blob/e6eccba97ebaffbbfad6d4358408a2cead0ec2df/specification/compatibility/prometheus_and_openmetrics.md#exemplars + ExemplarTraceIDKey = "trace_id" + // ExemplarSpanIDKey is the key used to store the Span ID in Prometheus + // exemplars: + // https://github.com/open-telemetry/opentelemetry-specification/blob/e6eccba97ebaffbbfad6d4358408a2cead0ec2df/specification/compatibility/prometheus_and_openmetrics.md#exemplars + ExemplarSpanIDKey = "span_id" + // ScopeInfoMetricName is the name of the metric used to preserve scope + // attributes in Prometheus format: + // https://github.com/open-telemetry/opentelemetry-specification/blob/e6eccba97ebaffbbfad6d4358408a2cead0ec2df/specification/compatibility/prometheus_and_openmetrics.md#instrumentation-scope + ScopeInfoMetricName = "otel_scope_info" + // ScopeNameLabelKey is the name of the label key used to identify the name + // of the OpenTelemetry scope which produced the metric: + // https://github.com/open-telemetry/opentelemetry-specification/blob/e6eccba97ebaffbbfad6d4358408a2cead0ec2df/specification/compatibility/prometheus_and_openmetrics.md#instrumentation-scope + ScopeNameLabelKey = "otel_scope_name" + // ScopeVersionLabelKey is the name of the label key used to identify the + // version of the OpenTelemetry scope which produced the metric: + // https://github.com/open-telemetry/opentelemetry-specification/blob/e6eccba97ebaffbbfad6d4358408a2cead0ec2df/specification/compatibility/prometheus_and_openmetrics.md#instrumentation-scope + ScopeVersionLabelKey = "otel_scope_version" + // TargetInfoMetricName is the name of the metric used to preserve resource + // attributes in Prometheus format: + // https://github.com/open-telemetry/opentelemetry-specification/blob/e6eccba97ebaffbbfad6d4358408a2cead0ec2df/specification/compatibility/prometheus_and_openmetrics.md#resource-attributes-1 + // It originates from OpenMetrics: + // https://github.com/OpenObservability/OpenMetrics/blob/1386544931307dff279688f332890c31b6c5de36/specification/OpenMetrics.md#supporting-target-metadata-in-both-push-based-and-pull-based-systems + TargetInfoMetricName = "target_info" +) + +type MetricType string + +const ( + MetricTypeNonMonotonicCounter MetricType = "non-monotonic-counter" + MetricTypeMonotonicCounter MetricType = "monotonic-counter" + MetricTypeGauge MetricType = "gauge" + MetricTypeHistogram MetricType = "histogram" + MetricTypeExponentialHistogram MetricType = "exponential-histogram" + MetricTypeSummary MetricType = "summary" + MetricTypeUnknown MetricType = "unknown" +) diff --git a/otlptranslator/doc.go b/otlptranslator/doc.go new file mode 100644 index 000000000..88a7fed4b --- /dev/null +++ b/otlptranslator/doc.go @@ -0,0 +1,24 @@ +// Copyright 2025 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. + +// otlptranslator is a dependency free package that contains the logic for translating information, such as metric name, unit and type, +// from OpenTelemetry metrics to valid Prometheus metric and label names. +// +// Use BuildCompliantMetricName to build a metric name that complies with traditional Prometheus naming conventions. +// Such conventions exist from a time when Prometheus didn't have support for full UTF-8 characters in metric names. +// For more details see: https://prometheus.io/docs/practices/naming/ +// +// Use BuildMetricName to build a metric name that will be accepted by Prometheus with full UTF-8 support. +// +// Use NormalizeLabel to normalize a label name to a valid format that can be used in Prometheus before UTF-8 characters were supported. +package otlptranslator diff --git a/otlptranslator/label_builder.go b/otlptranslator/label_builder.go new file mode 100644 index 000000000..deb3bbd0c --- /dev/null +++ b/otlptranslator/label_builder.go @@ -0,0 +1,54 @@ +// Copyright 2025 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 otlptranslator + +import ( + "regexp" + "strings" + "unicode" +) + +var invalidLabelCharRE = regexp.MustCompile(`[^a-zA-Z0-9_]`) + +// Normalizes the specified label to follow Prometheus label names standard. +// +// See rules at https://prometheus.io/docs/concepts/data_model/#metric-names-and-labels. +// +// Labels that start with non-letter rune will be prefixed with "key_". +// An exception is made for double-underscores which are allowed. +func NormalizeLabel(label string) string { + // Trivial case. + if len(label) == 0 { + return label + } + + label = SanitizeLabelName(label) + + // If label starts with a number, prepend with "key_". + if unicode.IsDigit(rune(label[0])) { + label = "key_" + label + } else if strings.HasPrefix(label, "_") && !strings.HasPrefix(label, "__") { + label = "key" + label + } + + return label +} + +// SanitizeLabelName replaces anything that doesn't match +// client_label.LabelNameRE with an underscore. +// Note: this does not handle all Prometheus label name restrictions (such as +// not starting with a digit 0-9), and hence should only be used if the label +// name is prefixed with a known valid string. +func SanitizeLabelName(name string) string { + return invalidLabelCharRE.ReplaceAllString(name, "_") +} diff --git a/otlptranslator/label_builder_bench_test.go b/otlptranslator/label_builder_bench_test.go new file mode 100644 index 000000000..6f2ba120e --- /dev/null +++ b/otlptranslator/label_builder_bench_test.go @@ -0,0 +1,35 @@ +// Copyright 2025 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 otlptranslator + +import "testing" + +var labelBenchmarkInputs = []string{ + "", + "label:with:colons", + "LabelWithCapitalLetters", + "label!with&special$chars)", + "label_with_foreign_characters_字符", + "label.with.dots", + "123label", + "_label_starting_with_underscore", + "__label_starting_with_2underscores", +} + +func BenchmarkNormalizeLabel(b *testing.B) { + for i := 0; i < b.N; i++ { + for _, input := range labelBenchmarkInputs { + NormalizeLabel(input) + } + } +} diff --git a/otlptranslator/label_builder_test.go b/otlptranslator/label_builder_test.go new file mode 100644 index 000000000..48856a215 --- /dev/null +++ b/otlptranslator/label_builder_test.go @@ -0,0 +1,44 @@ +// Copyright 2025 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 otlptranslator + +import ( + "fmt" + "testing" +) + +func TestNormalizeLabel(t *testing.T) { + tests := []struct { + label string + expected string + }{ + {"", ""}, + {"label:with:colons", "label_with_colons"}, + {"LabelWithCapitalLetters", "LabelWithCapitalLetters"}, + {"label!with&special$chars)", "label_with_special_chars_"}, + {"label_with_foreign_characters_字符", "label_with_foreign_characters___"}, + {"label.with.dots", "label_with_dots"}, + {"123label", "key_123label"}, + {"_label_starting_with_underscore", "key_label_starting_with_underscore"}, + {"__label_starting_with_2underscores", "__label_starting_with_2underscores"}, + } + + for i, test := range tests { + t.Run(fmt.Sprintf("test_%d", i), func(t *testing.T) { + result := NormalizeLabel(test.label) + if test.expected != result { + t.Errorf("expected %s, got %s", test.expected, result) + } + }) + } +} diff --git a/otlptranslator/metric_name_builder.go b/otlptranslator/metric_name_builder.go new file mode 100644 index 000000000..a6b7d2757 --- /dev/null +++ b/otlptranslator/metric_name_builder.go @@ -0,0 +1,286 @@ +// Copyright 2025 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 otlptranslator + +import ( + "regexp" + "slices" + "strings" + "unicode" +) + +// The map to translate OTLP units to Prometheus units +// OTLP metrics use the c/s notation as specified at https://ucum.org/ucum.html +// (See also https://github.com/open-telemetry/opentelemetry-specification/blob/main/specification/metrics/semantic_conventions/README.md#instrument-units) +// Prometheus best practices for units: https://prometheus.io/docs/practices/naming/#base-units +// OpenMetrics specification for units: https://github.com/prometheus/OpenMetrics/blob/v1.0.0/specification/OpenMetrics.md#units-and-base-units +var unitMap = map[string]string{ + // Time + "d": "days", + "h": "hours", + "min": "minutes", + "s": "seconds", + "ms": "milliseconds", + "us": "microseconds", + "ns": "nanoseconds", + + // Bytes + "By": "bytes", + "KiBy": "kibibytes", + "MiBy": "mebibytes", + "GiBy": "gibibytes", + "TiBy": "tibibytes", + "KBy": "kilobytes", + "MBy": "megabytes", + "GBy": "gigabytes", + "TBy": "terabytes", + + // SI + "m": "meters", + "V": "volts", + "A": "amperes", + "J": "joules", + "W": "watts", + "g": "grams", + + // Misc + "Cel": "celsius", + "Hz": "hertz", + "1": "", + "%": "percent", +} + +// The map that translates the "per" unit +// Example: s => per second (singular) +var perUnitMap = map[string]string{ + "s": "second", + "m": "minute", + "h": "hour", + "d": "day", + "w": "week", + "mo": "month", + "y": "year", +} + +var ( + nonMetricNameCharRE = regexp.MustCompile(`[^a-zA-Z0-9:]`) + // Regexp for metric name characters that should be replaced with _. + invalidMetricCharRE = regexp.MustCompile(`[^a-zA-Z0-9:_]`) + multipleUnderscoresRE = regexp.MustCompile(`__+`) +) + +// BuildMetricName builds a valid metric name but without following Prometheus naming conventions. +// It doesn't do any character transformation, it only prefixes the metric name with the namespace, if any, +// and adds metric type suffixes, e.g. "_total" for counters and unit suffixes. +// +// Differently from BuildCompliantMetricName, it doesn't check for the presence of unit and type suffixes. +// If "addMetricSuffixes" is true, it will add them anyway. +// +// Please use BuildCompliantMetricName for a metric name that follows Prometheus naming conventions. +func BuildMetricName(name, unit string, metricType MetricType, addMetricSuffixes bool) string { + if addMetricSuffixes { + mainUnitSuffix, perUnitSuffix := buildUnitSuffixes(unit) + if mainUnitSuffix != "" { + name = name + "_" + mainUnitSuffix + } + if perUnitSuffix != "" { + name = name + "_" + perUnitSuffix + } + + // Append _total for Counters + if metricType == MetricTypeMonotonicCounter { + name = name + "_total" + } + + // Append _ratio for metrics with unit "1" + // Some OTel receivers improperly use unit "1" for counters of objects + // See https://github.com/open-telemetry/opentelemetry-collector-contrib/issues?q=is%3Aissue+some+metric+units+don%27t+follow+otel+semantic+conventions + // Until these issues have been fixed, we're appending `_ratio` for gauges ONLY + // Theoretically, counters could be ratios as well, but it's absurd (for mathematical reasons) + if unit == "1" && metricType == MetricTypeGauge { + name = name + "_ratio" + } + } + return name +} + +// BuildCompliantMetricName builds a Prometheus-compliant metric name for the specified metric. +// +// Metric name is prefixed with specified namespace and underscore (if any). +// Namespace is not cleaned up. Make sure specified namespace follows Prometheus +// naming convention. +// +// See rules at https://prometheus.io/docs/concepts/data_model/#metric-names-and-labels, +// https://prometheus.io/docs/practices/naming/#metric-and-label-naming +// and https://github.com/open-telemetry/opentelemetry-specification/blob/v1.38.0/specification/compatibility/prometheus_and_openmetrics.md#otlp-metric-points-to-prometheus. +func BuildCompliantMetricName(name, unit string, metricType MetricType, addMetricSuffixes bool) string { + // Full normalization following standard Prometheus naming conventions + if addMetricSuffixes { + return normalizeName(name, unit, metricType) + } + + // Simple case (no full normalization, no units, etc.). + metricName := strings.Join(strings.FieldsFunc(name, func(r rune) bool { + return invalidMetricCharRE.MatchString(string(r)) + }), "_") + + // Metric name starts with a digit? Prefix it with an underscore. + if metricName != "" && unicode.IsDigit(rune(metricName[0])) { + metricName = "_" + metricName + } + + return metricName +} + +// Build a normalized name for the specified metric. +func normalizeName(metric, unit string, metricType MetricType) string { + // Split metric name into "tokens" (of supported metric name runes). + // Note that this has the side effect of replacing multiple consecutive underscores with a single underscore. + // This is part of the OTel to Prometheus specification: https://github.com/open-telemetry/opentelemetry-specification/blob/v1.38.0/specification/compatibility/prometheus_and_openmetrics.md#otlp-metric-points-to-prometheus. + nameTokens := strings.FieldsFunc( + metric, + func(r rune) bool { return nonMetricNameCharRE.MatchString(string(r)) }, + ) + + mainUnitSuffix, perUnitSuffix := buildUnitSuffixes(unit) + nameTokens = addUnitTokens(nameTokens, CleanUpString(mainUnitSuffix), CleanUpString(perUnitSuffix)) + + // Append _total for Counters + if metricType == MetricTypeMonotonicCounter { + nameTokens = append(removeItem(nameTokens, "total"), "total") + } + + // Append _ratio for metrics with unit "1" + // Some OTel receivers improperly use unit "1" for counters of objects + // See https://github.com/open-telemetry/opentelemetry-collector-contrib/issues?q=is%3Aissue+some+metric+units+don%27t+follow+otel+semantic+conventions + // Until these issues have been fixed, we're appending `_ratio` for gauges ONLY + // Theoretically, counters could be ratios as well, but it's absurd (for mathematical reasons) + if unit == "1" && metricType == MetricTypeGauge { + nameTokens = append(removeItem(nameTokens, "ratio"), "ratio") + } + + // Build the string from the tokens, separated with underscores + normalizedName := strings.Join(nameTokens, "_") + + // Metric name cannot start with a digit, so prefix it with "_" in this case + if normalizedName != "" && unicode.IsDigit(rune(normalizedName[0])) { + normalizedName = "_" + normalizedName + } + + return normalizedName +} + +// buildUnitSuffixes builds the main and per unit suffixes for the specified unit +// but doesn't do any special character transformation to accommodate Prometheus naming conventions. +// Removing trailing underscores or appending suffixes is done in the caller. +func buildUnitSuffixes(unit string) (mainUnitSuffix, perUnitSuffix string) { + // Split unit at the '/' if any + unitTokens := strings.SplitN(unit, "/", 2) + + if len(unitTokens) > 0 { + // Main unit + // Update if not blank and doesn't contain '{}' + mainUnitOTel := strings.TrimSpace(unitTokens[0]) + if mainUnitOTel != "" && !strings.ContainsAny(mainUnitOTel, "{}") { + mainUnitSuffix = unitMapGetOrDefault(mainUnitOTel) + } + + // Per unit + // Update if not blank and doesn't contain '{}' + if len(unitTokens) > 1 && unitTokens[1] != "" { + perUnitOTel := strings.TrimSpace(unitTokens[1]) + if perUnitOTel != "" && !strings.ContainsAny(perUnitOTel, "{}") { + perUnitSuffix = perUnitMapGetOrDefault(perUnitOTel) + } + if perUnitSuffix != "" { + perUnitSuffix = "per_" + perUnitSuffix + } + } + } + + return mainUnitSuffix, perUnitSuffix +} + +// Retrieve the Prometheus "basic" unit corresponding to the specified "basic" unit +// Returns the specified unit if not found in unitMap +func unitMapGetOrDefault(unit string) string { + if promUnit, ok := unitMap[unit]; ok { + return promUnit + } + return unit +} + +// Retrieve the Prometheus "per" unit corresponding to the specified "per" unit +// Returns the specified unit if not found in perUnitMap +func perUnitMapGetOrDefault(perUnit string) string { + if promPerUnit, ok := perUnitMap[perUnit]; ok { + return promPerUnit + } + return perUnit +} + +// addUnitTokens will add the suffixes to the nameTokens if they are not already present. +// It will also remove trailing underscores from the main suffix to avoid double underscores +// when joining the tokens. +// +// If the 'per' unit ends with underscore, the underscore will be removed. If the per unit is just +// 'per_', it will be entirely removed. +func addUnitTokens(nameTokens []string, mainUnitSuffix, perUnitSuffix string) []string { + if slices.Contains(nameTokens, mainUnitSuffix) { + mainUnitSuffix = "" + } + + if perUnitSuffix == "per_" { + perUnitSuffix = "" + } else { + perUnitSuffix = strings.TrimSuffix(perUnitSuffix, "_") + if slices.Contains(nameTokens, perUnitSuffix) { + perUnitSuffix = "" + } + } + + if perUnitSuffix != "" { + mainUnitSuffix = strings.TrimSuffix(mainUnitSuffix, "_") + } + + if mainUnitSuffix != "" { + nameTokens = append(nameTokens, mainUnitSuffix) + } + if perUnitSuffix != "" { + nameTokens = append(nameTokens, perUnitSuffix) + } + return nameTokens +} + +// CleanUpString cleans up a string so it matches model.LabelNameRE. +// CleanUpString is usually used to clean up unit strings, but can be used for any string, e.g. namespaces. +func CleanUpString(s string) string { + // Multiple consecutive underscores are replaced with a single underscore. + // This is part of the OTel to Prometheus specification: https://github.com/open-telemetry/opentelemetry-specification/blob/v1.38.0/specification/compatibility/prometheus_and_openmetrics.md#otlp-metric-points-to-prometheus. + return strings.TrimPrefix(multipleUnderscoresRE.ReplaceAllString( + nonMetricNameCharRE.ReplaceAllString(s, "_"), + "_", + ), "_") +} + +// Remove the specified value from the slice +func removeItem(slice []string, value string) []string { + newSlice := make([]string, 0, len(slice)) + for _, sliceEntry := range slice { + if sliceEntry != value { + newSlice = append(newSlice, sliceEntry) + } + } + return newSlice +} diff --git a/otlptranslator/metric_name_builder_bench_test.go b/otlptranslator/metric_name_builder_bench_test.go new file mode 100644 index 000000000..bc0851d39 --- /dev/null +++ b/otlptranslator/metric_name_builder_bench_test.go @@ -0,0 +1,134 @@ +// Copyright 2025 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 otlptranslator + +import ( + "fmt" + "testing" +) + +var benchmarkInputs = []struct { + name string + metricName string + unit string + metricType MetricType +}{ + { + name: "simple_metric", + metricName: "http_requests", + unit: "", + metricType: MetricTypeGauge, + }, + { + name: "compound_unit", + metricName: "request_throughput", + unit: "By/s", + metricType: MetricTypeMonotonicCounter, + }, + { + name: "complex_unit", + metricName: "disk_usage", + unit: "KiBy/m", + metricType: MetricTypeGauge, + }, + { + name: "ratio_metric", + metricName: "cpu_utilization", + unit: "1", + metricType: MetricTypeGauge, + }, + { + name: "metric_with_dots", + metricName: "system.cpu.usage.idle", + unit: "%", + metricType: MetricTypeGauge, + }, + { + name: "metric_with_unicode", + metricName: "メモリ使用率", + unit: "By", + metricType: MetricTypeGauge, + }, + { + name: "metric_with_special_chars", + metricName: "error-rate@host{instance}/service#component", + unit: "ms", + metricType: MetricTypeMonotonicCounter, + }, + { + name: "metric_with_multiple_slashes", + metricName: "network/throughput/total", + unit: "By/s/min", + metricType: MetricTypeGauge, + }, + { + name: "metric_with_spaces", + metricName: "api response time total", + unit: "ms", + metricType: MetricTypeMonotonicCounter, + }, + { + name: "metric_with_curly_braces", + metricName: "custom_{tag}_metric", + unit: "{custom}/s", + metricType: MetricTypeGauge, + }, + { + name: "metric_starting_with_digit", + metricName: "5xx_error_count", + unit: "1", + metricType: MetricTypeMonotonicCounter, + }, + { + name: "empty_metric", + metricName: "", + unit: "", + metricType: MetricTypeGauge, + }, + { + name: "metric_with_SI_units", + metricName: "power_consumption", + unit: "W", + metricType: MetricTypeGauge, + }, + { + name: "metric_with_temperature", + metricName: "server_temperature", + unit: "Cel", + metricType: MetricTypeGauge, + }, +} + +func BenchmarkBuildMetricName(b *testing.B) { + for _, addSuffixes := range []bool{true, false} { + b.Run(fmt.Sprintf("with_metric_suffixes=%t", addSuffixes), func(b *testing.B) { + for _, input := range benchmarkInputs { + for i := 0; i < b.N; i++ { + BuildMetricName(input.metricName, input.unit, input.metricType, addSuffixes) + } + } + }) + } +} + +func BenchmarkBuildCompliantMetricName(b *testing.B) { + for _, addSuffixes := range []bool{true, false} { + b.Run(fmt.Sprintf("with_metric_suffixes=%t", addSuffixes), func(b *testing.B) { + for _, input := range benchmarkInputs { + for i := 0; i < b.N; i++ { + BuildCompliantMetricName(input.metricName, input.unit, input.metricType, addSuffixes) + } + } + }) + } +} diff --git a/otlptranslator/metric_name_builder_test.go b/otlptranslator/metric_name_builder_test.go new file mode 100644 index 000000000..59e12d708 --- /dev/null +++ b/otlptranslator/metric_name_builder_test.go @@ -0,0 +1,420 @@ +// Copyright 2025 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 otlptranslator + +import ( + "reflect" + "testing" +) + +func TestBuildMetricName(t *testing.T) { + tests := []struct { + name string + metricName string + unit string + metricType MetricType + addMetricSuffixes bool + expected string + }{ + { + name: "simple metric without suffixes", + metricName: "http_requests", + unit: "", + metricType: MetricTypeGauge, + addMetricSuffixes: false, + expected: "http_requests", + }, + { + name: "counter with total suffix", + metricName: "http_requests", + unit: "", + metricType: MetricTypeMonotonicCounter, + addMetricSuffixes: true, + expected: "http_requests_total", + }, + { + name: "gauge with time unit", + metricName: "request_duration", + unit: "s", + metricType: MetricTypeGauge, + addMetricSuffixes: true, + expected: "request_duration_seconds", + }, + { + name: "counter with time unit", + metricName: "request_duration", + unit: "ms", + metricType: MetricTypeMonotonicCounter, + addMetricSuffixes: true, + expected: "request_duration_milliseconds_total", + }, + { + name: "gauge with compound unit", + metricName: "throughput", + unit: "By/s", + metricType: MetricTypeGauge, + addMetricSuffixes: true, + expected: "throughput_bytes_per_second", + }, + { + name: "ratio metric", + metricName: "cpu_utilization", + unit: "1", + metricType: MetricTypeGauge, + addMetricSuffixes: true, + expected: "cpu_utilization_ratio", + }, + { + name: "counter with unit 1 (no ratio suffix)", + metricName: "error_count", + unit: "1", + metricType: MetricTypeMonotonicCounter, + addMetricSuffixes: true, + expected: "error_count_total", + }, + { + name: "metric with byte units", + metricName: "memory_usage", + unit: "MiBy", + metricType: MetricTypeGauge, + addMetricSuffixes: true, + expected: "memory_usage_mebibytes", + }, + { + name: "metric with SI units", + metricName: "temperature", + unit: "Cel", + metricType: MetricTypeGauge, + addMetricSuffixes: true, + expected: "temperature_celsius", + }, + { + name: "metric with dots", + metricName: "system.cpu.usage", + unit: "1", + metricType: MetricTypeGauge, + addMetricSuffixes: true, + expected: "system.cpu.usage_ratio", + }, + { + name: "metric with japanese characters (memory usage rate)", + metricName: "メモリ使用率", // memori shiyouritsu (memory usage rate) xD + unit: "By", + metricType: MetricTypeGauge, + addMetricSuffixes: true, + expected: "メモリ使用率_bytes", + }, + { + name: "metric with mixed special characters (system.memory.usage.rate)", + metricName: "system.メモリ.usage.率", // system.memory.usage.rate + unit: "By/s", + metricType: MetricTypeGauge, + addMetricSuffixes: true, + expected: "system.メモリ.usage.率_bytes_per_second", + }, + } + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + result := BuildMetricName(tt.metricName, tt.unit, tt.metricType, tt.addMetricSuffixes) + if tt.expected != result { + t.Errorf("expected %s, got %s", tt.expected, result) + } + }) + } +} + +func TestBuildUnitSuffixes(t *testing.T) { + tests := []struct { + name string + unit string + expectedMain string + expectedPerUnit string + }{ + { + name: "empty unit", + unit: "", + expectedMain: "", + expectedPerUnit: "", + }, + { + name: "simple time unit", + unit: "s", + expectedMain: "seconds", + expectedPerUnit: "", + }, + { + name: "compound unit", + unit: "By/s", + expectedMain: "bytes", + expectedPerUnit: "per_second", + }, + { + name: "complex compound unit", + unit: "KiBy/m", + expectedMain: "kibibytes", + expectedPerUnit: "per_minute", + }, + { + name: "unit with spaces", + unit: " ms / s ", + expectedMain: "milliseconds", + expectedPerUnit: "per_second", + }, + { + name: "invalid unit", + unit: "invalid", + expectedMain: "invalid", + expectedPerUnit: "", + }, + { + name: "unit with curly braces", + unit: "{custom}/s", + expectedMain: "", + expectedPerUnit: "per_second", + }, + { + name: "multiple slashes", + unit: "By/s/h", + expectedMain: "bytes", + expectedPerUnit: "per_s/h", + }, + } + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + mainUnit, perUnit := buildUnitSuffixes(tt.unit) + if tt.expectedMain != mainUnit { + t.Errorf("expected main unit %s, got %s", tt.expectedMain, mainUnit) + } + if tt.expectedPerUnit != perUnit { + t.Errorf("expected per unit %s, got %s", tt.expectedPerUnit, perUnit) + } + }) + } +} + +func TestBuildCompliantMetricName(t *testing.T) { + tests := []struct { + name string + metricName string + unit string + metricType MetricType + addMetricSuffixes bool + expected string + }{ + { + name: "simple valid metric name", + metricName: "http_requests", + unit: "", + metricType: MetricTypeGauge, + addMetricSuffixes: false, + expected: "http_requests", + }, + { + name: "metric name with invalid characters", + metricName: "http-requests@in_flight", + unit: "", + metricType: MetricTypeNonMonotonicCounter, + addMetricSuffixes: false, + expected: "http_requests_in_flight", + }, + { + name: "metric name starting with digit", + metricName: "5xx_errors", + unit: "", + metricType: MetricTypeGauge, + addMetricSuffixes: false, + expected: "_5xx_errors", + }, + { + name: "metric name with multiple consecutive invalid chars", + metricName: "api..//request--time", + unit: "", + metricType: MetricTypeGauge, + addMetricSuffixes: false, + expected: "api_request_time", + }, + { + name: "full normalization with units and type", + metricName: "system.cpu-utilization", + unit: "ms/s", + metricType: MetricTypeMonotonicCounter, + addMetricSuffixes: true, + expected: "system_cpu_utilization_milliseconds_per_second_total", + }, + { + name: "metric with special characters and ratio", + metricName: "memory.usage%rate", + unit: "1", + metricType: MetricTypeGauge, + addMetricSuffixes: true, + expected: "memory_usage_rate_ratio", + }, + { + name: "metric with unicode characters", + metricName: "error_rate_£_€_¥", + unit: "", + metricType: MetricTypeGauge, + addMetricSuffixes: false, + expected: "error_rate_____", + }, + { + name: "metric with multiple spaces", + metricName: "api response time", + unit: "ms", + metricType: MetricTypeGauge, + addMetricSuffixes: true, + expected: "api_response_time_milliseconds", + }, + { + name: "metric with colons (valid prometheus chars)", + metricName: "app:request:latency", + unit: "s", + metricType: MetricTypeGauge, + addMetricSuffixes: true, + expected: "app:request:latency_seconds", + }, + { + name: "empty metric name", + metricName: "", + unit: "", + metricType: MetricTypeGauge, + addMetricSuffixes: false, + expected: "", + }, + } + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + result := BuildCompliantMetricName(tt.metricName, tt.unit, tt.metricType, tt.addMetricSuffixes) + if tt.expected != result { + t.Errorf("expected %s, got %s", tt.expected, result) + } + }) + } +} + +func TestAddUnitTokens(t *testing.T) { + tests := []struct { + nameTokens []string + mainUnitSuffix string + perUnitSuffix string + expected []string + }{ + {[]string{}, "", "", []string{}}, + {[]string{"token1"}, "main", "", []string{"token1", "main"}}, + {[]string{"token1"}, "", "per", []string{"token1", "per"}}, + {[]string{"token1"}, "main", "per", []string{"token1", "main", "per"}}, + {[]string{"token1", "per"}, "main", "per", []string{"token1", "per", "main"}}, + {[]string{"token1", "main"}, "main", "per", []string{"token1", "main", "per"}}, + {[]string{"token1"}, "main_", "per", []string{"token1", "main", "per"}}, + {[]string{"token1"}, "main_unit", "per_seconds_", []string{"token1", "main_unit", "per_seconds"}}, // trailing underscores are removed + {[]string{"token1"}, "main_unit", "per_", []string{"token1", "main_unit"}}, // 'per_' is removed entirely + } + + for _, test := range tests { + result := addUnitTokens(test.nameTokens, test.mainUnitSuffix, test.perUnitSuffix) + if !reflect.DeepEqual(test.expected, result) { + t.Errorf("expected %v, got %v", test.expected, result) + } + } +} + +func TestRemoveItem(t *testing.T) { + if !reflect.DeepEqual([]string{}, removeItem([]string{}, "test")) { + t.Errorf("expected %v, got %v", []string{}, removeItem([]string{}, "test")) + } + if !reflect.DeepEqual([]string{}, removeItem([]string{}, "")) { + t.Errorf("expected %v, got %v", []string{}, removeItem([]string{}, "")) + } + if !reflect.DeepEqual([]string{"a", "b", "c"}, removeItem([]string{"a", "b", "c"}, "d")) { + t.Errorf("expected %v, got %v", []string{"a", "b", "c"}, removeItem([]string{"a", "b", "c"}, "d")) + } + if !reflect.DeepEqual([]string{"a", "b", "c"}, removeItem([]string{"a", "b", "c"}, "")) { + t.Errorf("expected %v, got %v", []string{"a", "b", "c"}, removeItem([]string{"a", "b", "c"}, "")) + } + if !reflect.DeepEqual([]string{"a", "b"}, removeItem([]string{"a", "b", "c"}, "c")) { + t.Errorf("expected %v, got %v", []string{"a", "b"}, removeItem([]string{"a", "b", "c"}, "c")) + } + if !reflect.DeepEqual([]string{"a", "c"}, removeItem([]string{"a", "b", "c"}, "b")) { + t.Errorf("expected %v, got %v", []string{"a", "c"}, removeItem([]string{"a", "b", "c"}, "b")) + } + if !reflect.DeepEqual([]string{"b", "c"}, removeItem([]string{"a", "b", "c"}, "a")) { + t.Errorf("expected %v, got %v", []string{"b", "c"}, removeItem([]string{"a", "b", "c"}, "a")) + } +} + +func TestCleanUpStrings(t *testing.T) { + tests := []struct { + name string + input string + expected string + }{ + { + name: "already valid string", + input: "valid_metric_name", + expected: "valid_metric_name", + }, + { + name: "invalid characters", + input: "metric-name@with#special$chars", + expected: "metric_name_with_special_chars", + }, + { + name: "multiple consecutive invalid chars", + input: "metric---name###special", + expected: "metric_name_special", + }, + { + name: "leading invalid chars", + input: "@#$metric_name", + expected: "metric_name", + }, + { + name: "trailing invalid chars", + input: "metric_name@#$", + expected: "metric_name_", + }, + { + name: "multiple consecutive underscores", + input: "metric___name____test", + expected: "metric_name_test", + }, + { + name: "empty string", + input: "", + expected: "", + }, + { + name: "only invalid chars", + input: "@#$%^&", + expected: "", + }, + { + name: "colons are valid", + input: "system.cpu:usage.rate", + expected: "system_cpu:usage_rate", + }, + } + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + result := CleanUpString(tt.input) + if result != tt.expected { + t.Errorf("expected %q, got %q", tt.expected, result) + } + }) + } +} From b35ad995d20d280cd0bc1a30386a4e3bbdc032f5 Mon Sep 17 00:00:00 2001 From: Arthur Silva Sens Date: Thu, 13 Mar 2025 13:27:31 -0300 Subject: [PATCH 48/48] Add test case for BuildCompliantMetricName with a metric that starts with a digit and addMetricSuffixes is true Signed-off-by: Arthur Silva Sens --- otlptranslator/metric_name_builder_test.go | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/otlptranslator/metric_name_builder_test.go b/otlptranslator/metric_name_builder_test.go index 59e12d708..49b729b43 100644 --- a/otlptranslator/metric_name_builder_test.go +++ b/otlptranslator/metric_name_builder_test.go @@ -238,6 +238,14 @@ func TestBuildCompliantMetricName(t *testing.T) { addMetricSuffixes: false, expected: "_5xx_errors", }, + { + name: "metric name starting with digit, with suffixes", + metricName: "5xx_errors", + unit: "", + metricType: MetricTypeMonotonicCounter, + addMetricSuffixes: true, + expected: "_5xx_errors_total", + }, { name: "metric name with multiple consecutive invalid chars", metricName: "api..//request--time",