diff --git a/artifacts/flagger/crd.yaml b/artifacts/flagger/crd.yaml index 1dd9e35e..68522b86 100644 --- a/artifacts/flagger/crd.yaml +++ b/artifacts/flagger/crd.yaml @@ -1313,6 +1313,13 @@ spec: address: description: API address of this provider type: string + headers: + description: Headers to add to HTTP(S) requests + type: object + additionalProperties: + type: array + items: + type: string secretRef: description: Kubernetes secret reference containing the provider credentials type: object diff --git a/charts/flagger/crds/crd.yaml b/charts/flagger/crds/crd.yaml index 1dd9e35e..68522b86 100644 --- a/charts/flagger/crds/crd.yaml +++ b/charts/flagger/crds/crd.yaml @@ -1313,6 +1313,13 @@ spec: address: description: API address of this provider type: string + headers: + description: Headers to add to HTTP(S) requests + type: object + additionalProperties: + type: array + items: + type: string secretRef: description: Kubernetes secret reference containing the provider credentials type: object diff --git a/kustomize/base/flagger/crd.yaml b/kustomize/base/flagger/crd.yaml index 1dd9e35e..68522b86 100644 --- a/kustomize/base/flagger/crd.yaml +++ b/kustomize/base/flagger/crd.yaml @@ -1313,6 +1313,13 @@ spec: address: description: API address of this provider type: string + headers: + description: Headers to add to HTTP(S) requests + type: object + additionalProperties: + type: array + items: + type: string secretRef: description: Kubernetes secret reference containing the provider credentials type: object diff --git a/pkg/apis/flagger/v1beta1/metric.go b/pkg/apis/flagger/v1beta1/metric.go index aef96c6c..c34e7516 100644 --- a/pkg/apis/flagger/v1beta1/metric.go +++ b/pkg/apis/flagger/v1beta1/metric.go @@ -17,6 +17,7 @@ limitations under the License. package v1beta1 import ( + "net/http" "text/template" corev1 "k8s.io/api/core/v1" @@ -67,6 +68,10 @@ type MetricTemplateProvider struct { // +optional Address string `json:"address,omitempty"` + // Headers to be supplied to HTTP(S) request of this provider + // +optional + Headers http.Header `json:"headers,omitempty"` + // Secret reference containing the provider credentials // +optional SecretRef *corev1.LocalObjectReference `json:"secretRef,omitempty"` diff --git a/pkg/apis/flagger/v1beta1/zz_generated.deepcopy.go b/pkg/apis/flagger/v1beta1/zz_generated.deepcopy.go index 340fc043..a9a325d7 100644 --- a/pkg/apis/flagger/v1beta1/zz_generated.deepcopy.go +++ b/pkg/apis/flagger/v1beta1/zz_generated.deepcopy.go @@ -22,6 +22,8 @@ limitations under the License. package v1beta1 import ( + http "net/http" + gatewayapiv1beta1 "github.com/fluxcd/flagger/pkg/apis/gatewayapi/v1beta1" istiov1beta1 "github.com/fluxcd/flagger/pkg/apis/istio/v1beta1" v1 "k8s.io/api/core/v1" @@ -810,6 +812,21 @@ func (in *MetricTemplateModel) DeepCopy() *MetricTemplateModel { // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. func (in *MetricTemplateProvider) DeepCopyInto(out *MetricTemplateProvider) { *out = *in + if in.Headers != nil { + in, out := &in.Headers, &out.Headers + *out = make(http.Header, len(*in)) + for key, val := range *in { + var outVal []string + if val == nil { + (*out)[key] = nil + } else { + in, out := &val, &outVal + *out = make([]string, len(*in)) + copy(*out, *in) + } + (*out)[key] = outVal + } + } if in.SecretRef != nil { in, out := &in.SecretRef, &out.SecretRef *out = new(v1.LocalObjectReference) diff --git a/pkg/metrics/providers/prometheus.go b/pkg/metrics/providers/prometheus.go index 270012e5..57ad4966 100644 --- a/pkg/metrics/providers/prometheus.go +++ b/pkg/metrics/providers/prometheus.go @@ -39,6 +39,7 @@ const prometheusOnlineQuery = "vector(1)" type PrometheusProvider struct { timeout time.Duration url url.URL + headers http.Header username string password string token string @@ -69,6 +70,7 @@ func NewPrometheusProvider(provider flaggerv1.MetricTemplateProvider, credential prom := PrometheusProvider{ timeout: 5 * time.Second, url: *promURL, + headers: provider.Headers, client: http.DefaultClient, } @@ -116,6 +118,10 @@ func (p *PrometheusProvider) RunQuery(query string) (float64, error) { return 0, fmt.Errorf("http.NewRequest failed: %w", err) } + if p.headers != nil { + req.Header = p.headers + } + if p.token != "" { req.Header.Add("Authorization", "Bearer "+p.token) } else if p.username != "" && p.password != "" { diff --git a/pkg/metrics/providers/prometheus_test.go b/pkg/metrics/providers/prometheus_test.go index 1a4f475a..72083593 100644 --- a/pkg/metrics/providers/prometheus_test.go +++ b/pkg/metrics/providers/prometheus_test.go @@ -307,3 +307,40 @@ func TestPrometheusProvider_IsOnline(t *testing.T) { assert.Equal(t, true, ok) }) } + +func TestPrometheusProvider_RunQueryWithProviderHeaders(t *testing.T) { + t.Run("ok", func(t *testing.T) { + expected := `sum(envoy_cluster_upstream_rq)` + ts := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { + promql := r.URL.Query()["query"][0] + assert.Equal(t, expected, promql) + + assert.Equal(t, []string{"tenant1"}, r.Header.Values("X-Scope-Orgid")) + + json := `{"status":"success","data":{"resultType":"vector","result":[{"metric":{},"value":[1545905245.458,"100"]}]}}` + w.Write([]byte(json)) + })) + defer ts.Close() + + clients := prometheusFake() + + template, err := clients.flaggerClient.FlaggerV1beta1().MetricTemplates("default").Get(context.TODO(), "prometheus", metav1.GetOptions{}) + require.NoError(t, err) + + template.Spec.Provider.Address = ts.URL + template.Spec.Provider.Headers = http.Header{ + "X-Scope-OrgID": []string{"tenant1"}, + } + + secret, err := clients.kubeClient.CoreV1().Secrets("default").Get(context.TODO(), "prometheus", metav1.GetOptions{}) + require.NoError(t, err) + + prom, err := NewPrometheusProvider(template.Spec.Provider, secret.Data) + require.NoError(t, err) + + val, err := prom.RunQuery(template.Spec.Query) + require.NoError(t, err) + + assert.Equal(t, float64(100), val) + }) +}