nebius/SWE-rebench-V2-PRs huggingface dataset:
Repository: prometheus/common
Pull request #647 (2024-06-05 06:55:14)

Don't always fetch a OAuth2 token, if the secret from a file didn't change
When configuring a OAuth2 client using the `client_secret_file` configuration option, the original intent of the code was to *not* forcefully refetch a token if the content of the file didn't change.

However, both the test and the implementation had 2 typos and a new token token was fetched on every request through the HTTP client:

* The code was always keeping track of the very first `secret` configured ; in the case of a secret coming from a file, the secret kept in the state was always empty and would never match the content of the file (except if the file contains nothing, in which case the authentication couldn't proceed anyway).
* The test for this feature was using an inline secret which was never changing, instead of the intended secret file. Thus, as the secret was never changed, the token endpoint was never called.

I extracted the secret variable from the tests to make it, hopefully, a bit clearer to see it's actual type.

multani: I pushed-force to sign the commit.
roidelapluie: Thanks, LGTM, Can you rebase?

Patch:
diff --git a/config/http_config.go b/config/http_config.go
index 3e32013..228db63 100644
--- a/config/http_config.go
+++ b/config/http_config.go
@@ -964,7 +964,7 @@ func (rt *oauth2RoundTripper) RoundTrip(req *http.Request) (*http.Response, erro
 			}
 
 			rt.mtx.Lock()
-			rt.lastSecret = secret
+			rt.lastSecret = newSecret
 			rt.lastRT.Source = source
 			if rt.client != nil {
 				rt.client.CloseIdleConnections()


Test patch:
diff --git a/config/http_config_test.go b/config/http_config_test.go
index 5be8438..9236f7c 100644
--- a/config/http_config_test.go
+++ b/config/http_config_test.go
@@ -1630,7 +1630,8 @@ endpoint_params:
 		t.Fatalf("Got unmarshalled config %v, expected %v", unmarshalledConfig, expectedConfig)
 	}
 
-	rt := NewOAuth2RoundTripper(NewInlineSecret(string(expectedConfig.ClientSecret)), &expectedConfig, http.DefaultTransport, &defaultHTTPClientOptions)
+	secret := NewInlineSecret(string(expectedConfig.ClientSecret))
+	rt := NewOAuth2RoundTripper(secret, &expectedConfig, http.DefaultTransport, &defaultHTTPClientOptions)
 
 	client := http.Client{
 		Transport: rt,
@@ -1800,7 +1801,8 @@ endpoint_params:
 		t.Fatalf("Got unmarshalled config %v, expected %v", unmarshalledConfig, expectedConfig)
 	}
 
-	rt := NewOAuth2RoundTripper(NewInlineSecret(string(expectedConfig.ClientSecret)), &expectedConfig, http.DefaultTransport, &defaultHTTPClientOptions)
+	secret := NewFileSecret(expectedConfig.ClientSecretFile)
+	rt := NewOAuth2RoundTripper(secret, &expectedConfig, http.DefaultTransport, &defaultHTTPClientOptions)
 
 	client := http.Client{
 		Transport: rt,
Repository: prometheus/common
Pull request #653 (2024-06-07 21:27:25)

Expose secret as FileSecret from config package
Similar to https://github.com/prometheus/common/pull/650

Thanos also need the FileSecret to be exposed:

See: https://github.com/thanos-io/thanos/blob/c08dc141dd671f9c6ecc6dd85a9f39f8be32419c/pkg/clientconfig/http.go#L167

and https://github.com/prometheus/common/pull/538#issuecomment-2150596862


Patch:
diff --git a/config/http_config.go b/config/http_config.go
index 0724132..c89f586 100644
--- a/config/http_config.go
+++ b/config/http_config.go
@@ -718,11 +718,15 @@ func (s *InlineSecret) Immutable() bool {
 	return true
 }
 
-type fileSecret struct {
+type FileSecret struct {
 	file string
 }
 
-func (s *fileSecret) Fetch(ctx context.Context) (string, error) {
+func NewFileSecret(file string) *FileSecret {
+	return &FileSecret{file: file}
+}
+
+func (s *FileSecret) Fetch(ctx context.Context) (string, error) {
 	fileBytes, err := os.ReadFile(s.file)
 	if err != nil {
 		return "", fmt.Errorf("unable to read file %s: %w", s.file, err)
@@ -730,11 +734,11 @@ func (s *fileSecret) Fetch(ctx context.Context) (string, error) {
 	return strings.TrimSpace(string(fileBytes)), nil
 }
 
-func (s *fileSecret) Description() string {
+func (s *FileSecret) Description() string {
 	return fmt.Sprintf("file %s", s.file)
 }
 
-func (s *fileSecret) Immutable() bool {
+func (s *FileSecret) Immutable() bool {
 	return false
 }
 
@@ -763,9 +767,7 @@ func toSecret(secretManager SecretManager, text Secret, file, ref string) (Secre
 		return NewInlineSecret(string(text)), nil
 	}
 	if file != "" {
-		return &fileSecret{
-			file: file,
-		}, nil
+		return NewFileSecret(file), nil
 	}
 	if ref != "" {
 		if secretManager == nil {


Test patch:
diff --git a/config/http_config_test.go b/config/http_config_test.go
index b66a469..3e7a126 100644
--- a/config/http_config_test.go
+++ b/config/http_config_test.go
@@ -756,7 +756,7 @@ func TestBearerAuthFileRoundTripper(t *testing.T) {
 	}, nil, nil)
 
 	// Normal flow.
-	bearerAuthRoundTripper := NewAuthorizationCredentialsRoundTripper("Bearer", &fileSecret{file: BearerTokenFile}, fakeRoundTripper)
+	bearerAuthRoundTripper := NewAuthorizationCredentialsRoundTripper("Bearer", &FileSecret{file: BearerTokenFile}, fakeRoundTripper)
 	request, _ := http.NewRequest("GET", "/hitchhiker", nil)
 	request.Header.Set("User-Agent", "Douglas Adams mind")
 	_, err := bearerAuthRoundTripper.RoundTrip(request)
@@ -765,7 +765,7 @@ func TestBearerAuthFileRoundTripper(t *testing.T) {
 	}
 
 	// Should honor already Authorization header set.
-	bearerAuthRoundTripperShouldNotModifyExistingAuthorization := NewAuthorizationCredentialsRoundTripper("Bearer", &fileSecret{file: MissingBearerTokenFile}, fakeRoundTripper)
+	bearerAuthRoundTripperShouldNotModifyExistingAuthorization := NewAuthorizationCredentialsRoundTripper("Bearer", &FileSecret{file: MissingBearerTokenFile}, fakeRoundTripper)
 	request, _ = http.NewRequest("GET", "/hitchhiker", nil)
 	request.Header.Set("Authorization", ExpectedBearer)
 	_, err = bearerAuthRoundTripperShouldNotModifyExistingAuthorization.RoundTrip(request)
Repository: prometheus/common
Pull request #655 (2024-06-12 22:21:56)

Set http_headers to be omit empty
https://github.com/prometheus/common/pull/416 was introduced in release v0.54.0 to allow `http_headers` in the http client config. 

This PR marks this new field omit empty so that an older version of Prometheus can run successfully using a config file marshalled with the newer version of the common library.


Patch:
diff --git a/config/http_config.go b/config/http_config.go
index c89f586..3e32013 100644
--- a/config/http_config.go
+++ b/config/http_config.go
@@ -323,7 +323,7 @@ type HTTPClientConfig struct {
 	ProxyConfig `yaml:",inline"`
 	// HTTPHeaders specify headers to inject in the requests. Those headers
 	// could be marshalled back to the users.
-	HTTPHeaders *Headers `yaml:"http_headers" json:"http_headers"`
+	HTTPHeaders *Headers `yaml:"http_headers,omitempty" json:"http_headers,omitempty"`
 }
 
 // SetDirectory joins any relative file paths with dir.


Test patch:
diff --git a/config/http_config_test.go b/config/http_config_test.go
index 3e7a126..5be8438 100644
--- a/config/http_config_test.go
+++ b/config/http_config_test.go
@@ -2023,7 +2023,6 @@ func TestHTTPClientConfig_Marshal(t *testing.T) {
 proxy_url: "http://localhost:8080"
 follow_redirects: false
 enable_http2: false
-http_headers: null
 `, string(actualYAML))
 
 			// Unmarshalling the YAML should get the same struct in input.
@@ -2040,8 +2039,7 @@ http_headers: null
 				"proxy_url":"http://localhost:8080",
 				"tls_config":{"insecure_skip_verify":false},
 				"follow_redirects":false,
-				"enable_http2":false,
-				"http_headers":null
+				"enable_http2":false
 			}`, string(actualJSON))
 
 			// Unmarshalling the JSON should get the same struct in input.
Repository: prometheus/common
Pull request #690 (2024-09-04 17:22:39)

Change the default escape method to UnderscoreEscaping
fixes https://github.com/prometheus/common/issues/689


Patch:
diff --git a/model/metric.go b/model/metric.go
index c44f93f..f50966b 100644
--- a/model/metric.go
+++ b/model/metric.go
@@ -34,10 +34,13 @@ var (
 	// goroutines are started.
 	NameValidationScheme = LegacyValidation
 
-	// 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 "escaping" term is specified, that will override this value.
-	NameEscapingScheme = ValueEncodingEscaping
+	// 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
+	// "escaping" term is specified, that will override this value.
+	// NameEscapingScheme should not be set to the NoEscaping value. That string
+	// is used in content negotiation to indicate that a system supports UTF-8 and
+	// has that feature enabled.
+	NameEscapingScheme = UnderscoreEscaping
 )
 
 // ValidationScheme is a Go enum for determining how metric and label names will


Test patch:
diff --git a/expfmt/encode_test.go b/expfmt/encode_test.go
index 5a46234..d91faaa 100644
--- a/expfmt/encode_test.go
+++ b/expfmt/encode_test.go
@@ -383,9 +383,9 @@ func TestEscapedEncode(t *testing.T) {
 		t.Errorf("expected the output bytes buffer to be non-empty")
 	}
 
-	expected := `# TYPE U__foo_2e_metric untyped
-U__foo_2e_metric 1.234
-U__foo_2e_metric{U__dotted_2e_label_2e_name="my.label.value"} 8
+	expected := `# TYPE foo_metric untyped
+foo_metric 1.234
+foo_metric{dotted_label_name="my.label.value"} 8
 `
 
 	if string(out) != expected {
diff --git a/expfmt/expfmt_test.go b/expfmt/expfmt_test.go
index bc946ec..d9373bc 100644
--- a/expfmt/expfmt_test.go
+++ b/expfmt/expfmt_test.go
@@ -105,11 +105,11 @@ func TestToEscapingScheme(t *testing.T) {
 	}{
 		{
 			format:   FmtProtoCompact,
-			expected: model.ValueEncodingEscaping,
+			expected: model.UnderscoreEscaping,
 		},
 		{
-			format:   "application/openmetrics-text; version=1.0.0; charset=utf-8; escaping=underscores",
-			expected: model.UnderscoreEscaping,
+			format:   "application/openmetrics-text; version=1.0.0; charset=utf-8; escaping=dots",
+			expected: model.DotsEscaping,
 		},
 		{
 			format:   "application/openmetrics-text; version=1.0.0; charset=utf-8; escaping=allow-utf-8",
Repository: prometheus/common
Pull request #694 (2024-09-07 21:22:14)

slog: expose io.Writer
While implementing the new slog logger at windows_exporter, I figure out that I'm unable to set the Writer as I could on slog.

Define the Writer is important for windows. stdout and stderr are not captured, if programs running as Windows Service. 

On windows_exporter, I implement a io.Writer which is able to write data to the Windows Event Log and I would like to use that writer.


Patch:
diff --git a/promslog/slog.go b/promslog/slog.go
index 7fb24d4..5a3a6f8 100644
--- a/promslog/slog.go
+++ b/promslog/slog.go
@@ -148,10 +148,10 @@ func (f *AllowedFormat) Set(s string) error {
 
 // Config is a struct containing configurable settings for the logger
 type Config struct {
-	Level    *AllowedLevel
-	Format   *AllowedFormat
-	Style    LogStyle
-	ioWriter io.Writer
+	Level  *AllowedLevel
+	Format *AllowedFormat
+	Style  LogStyle
+	Writer io.Writer
 }
 
 // New returns a new slog.Logger. Each logged line will be annotated
@@ -162,8 +162,8 @@ func New(config *Config) *slog.Logger {
 		_ = config.Level.Set("info")
 	}
 
-	if config.ioWriter == nil {
-		config.ioWriter = defaultWriter
+	if config.Writer == nil {
+		config.Writer = defaultWriter
 	}
 
 	logHandlerOpts := &slog.HandlerOptions{
@@ -176,7 +176,7 @@ func New(config *Config) *slog.Logger {
 	}
 
 	if config.Format != nil && config.Format.s == "json" {
-		return slog.New(slog.NewJSONHandler(config.ioWriter, logHandlerOpts))
+		return slog.New(slog.NewJSONHandler(config.Writer, logHandlerOpts))
 	}
-	return slog.New(slog.NewTextHandler(config.ioWriter, logHandlerOpts))
+	return slog.New(slog.NewTextHandler(config.Writer, logHandlerOpts))
 }


Test patch:
diff --git a/promslog/slog_test.go b/promslog/slog_test.go
index 5f37e18..69e3f6f 100644
--- a/promslog/slog_test.go
+++ b/promslog/slog_test.go
@@ -112,7 +112,7 @@ func TestDynamicLevels(t *testing.T) {
 	for name, tc := range tests {
 		t.Run(name, func(t *testing.T) {
 			buf.Reset() // Ensure buf is reset prior to tests
-			config := &Config{ioWriter: &buf, Style: tc.logStyle}
+			config := &Config{Writer: &buf, Style: tc.logStyle}
 			logger := New(config)
 
 			// Test that log level can be adjusted on-the-fly to debug and that a
Repository: prometheus/common
Pull request #725 (2024-11-16 09:04:30)

Open user-agent definition
Most of the time the User-agent is Prometheus/Version but might be changed to Component/Version in certain occasion.
This expose a PrometheusUserAgent which is the most common case and also provide a ComponentUserAgent to support a component customization.


Patch:
diff --git a/version/info.go b/version/info.go
index 3ca72ab..61ed1ba 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() {


Test patch:
diff --git a/version/info_test.go b/version/info_test.go
new file mode 100644
index 0000000..a3689db
--- /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"))
+}