diff --git a/pkg/metrics/providers/externalmetrics.go b/pkg/metrics/providers/externalmetrics.go index 3fce2806..36fa4ef5 100644 --- a/pkg/metrics/providers/externalmetrics.go +++ b/pkg/metrics/providers/externalmetrics.go @@ -47,19 +47,7 @@ func NewExternalMetricsProvider( ) } - // clone to avoid mutating the shared config - restConfig := rest.CopyConfig(config) - - // apply overrides from MetricTemplateProvider - if provider.Address != "" { - restConfig.Host = provider.Address - } - restConfig.TLSClientConfig.Insecure = provider.InsecureSkipVerify - if tokenBytes, ok := credentials["token"]; ok { - restConfig.BearerToken = string(tokenBytes) - } - - restConfig.Timeout = 5 * time.Second + restConfig := externalMetricsRestConfig(provider, credentials, config) client, err := externalmetrics_client.NewForConfig(restConfig) if err != nil { @@ -71,6 +59,29 @@ func NewExternalMetricsProvider( }, nil } +func externalMetricsRestConfig( + provider flaggerv1.MetricTemplateProvider, + credentials map[string][]byte, + config *rest.Config, +) *rest.Config { + var restConfig *rest.Config + if provider.Address == "" { + restConfig = rest.CopyConfig(config) + } else { + restConfig = &rest.Config{ + Host: provider.Address, + } + } + + restConfig.TLSClientConfig.Insecure = provider.InsecureSkipVerify + if tokenBytes, ok := credentials["token"]; ok { + restConfig.BearerToken = strings.TrimSpace(string(tokenBytes)) + } + restConfig.Timeout = 5 * time.Second + + return restConfig +} + // RunQuery retrieves the ExternalMetricValue from the External Metrics API // at the ExternalMetricsProvider's address, using the provided query string, // and returns the *first* result as a float64. diff --git a/pkg/metrics/providers/externalmetrics_test.go b/pkg/metrics/providers/externalmetrics_test.go index 746ccf01..623caab5 100644 --- a/pkg/metrics/providers/externalmetrics_test.go +++ b/pkg/metrics/providers/externalmetrics_test.go @@ -18,6 +18,10 @@ package providers import ( "errors" + "net/http" + "net/http/httptest" + "os" + "path/filepath" "strings" "testing" @@ -99,6 +103,67 @@ func TestExternalMetrics_NewProvider_NilConfig(t *testing.T) { require.Error(t, err) } +func TestExternalMetrics_CustomProviderAddressDoesNotSendInheritedAuth(t *testing.T) { + tokenFile := filepath.Join(t.TempDir(), "token") + require.NoError(t, os.WriteFile(tokenFile, []byte("operator-file-token"), 0o600)) + + tests := []struct { + name string + credentials map[string][]byte + wantAuthorization string + }{ + { + name: "without credentials", + credentials: nil, + wantAuthorization: "", + }, + { + name: "with provider token", + credentials: map[string][]byte{ + "token": []byte("provider-token"), + }, + wantAuthorization: "Bearer provider-token", + }, + } + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + authHeaders := make(chan string, 1) + ts := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { + authHeaders <- r.Header.Get("Authorization") + w.Header().Set("Content-Type", "application/json") + _, err := w.Write([]byte(`{ + "kind": "ExternalMetricValueList", + "apiVersion": "external.metrics.k8s.io/v1beta1", + "metadata": {}, + "items": [] + }`)) + require.NoError(t, err) + })) + defer ts.Close() + + emp, err := NewExternalMetricsProvider( + flaggerv1.MetricTemplateProvider{ + Address: ts.URL, + }, + tt.credentials, + &rest.Config{ + Host: "https://kubernetes.default.svc", + BearerToken: "operator-token", + BearerTokenFile: tokenFile, + }, + ) + require.NoError(t, err) + + online, err := emp.IsOnline() + require.NoError(t, err) + assert.True(t, online) + require.Len(t, authHeaders, 1) + assert.Equal(t, tt.wantAuthorization, <-authHeaders) + }) + } +} + func TestExternalMetrics_ParseQuery(t *testing.T) { tests := []struct { name string