From 64906e96dce2f08e38ac9a37058b98dedc191f13 Mon Sep 17 00:00:00 2001 From: David Hurta Date: Tue, 21 Nov 2023 15:47:45 +0100 Subject: [PATCH] pkg/clusterconditions/promql: Create GET requests This commit will create GET requests to the Prometheus API server. The used library doesn't allow to specify the type of requests (GET/POST) being made to the Prometheus API server. The tenancy port of the thanos-querier.openshift-monitoring.svc service being utilized in self-managed HyperShift requires different permissions depending on the type of requests. This commit will make sure that the CVO uses GET requests to the PromQL service. The used Prometheus client library attempts POST requests and on 501 and 405 response status codes fall-backs to GET requests. This commit will return the 501 status code even before sending a given HTTP request, resulting in a GET request being made as a fallback by the used library. --- pkg/clusterconditions/promql/promql.go | 38 +++++++++++++++++++++++++- 1 file changed, 37 insertions(+), 1 deletion(-) diff --git a/pkg/clusterconditions/promql/promql.go b/pkg/clusterconditions/promql/promql.go index ebf5577ed4..f8e8da77ab 100644 --- a/pkg/clusterconditions/promql/promql.go +++ b/pkg/clusterconditions/promql/promql.go @@ -7,8 +7,11 @@ import ( "context" "errors" "fmt" + "io" "net" + "net/http" "net/url" + "strings" "time" configv1 "github.com/openshift/api/config/v1" @@ -25,6 +28,35 @@ import ( "github.com/openshift/cluster-version-operator/pkg/clusterconditions/cache" ) +// statusCodeNotImplementedForPostClient returns an empty response containing the status +// code 501 (Not Implemented) for POST requests. +// +// The downstream client attempts a POST request and, on a 405 or 501 status code, +// fallbacks to a GET request. The default PromQL service in OpenShift maps the initial +// POST request to the CREATE verb using a kube-rbac-proxy (when the service's tenancy +// port is accessed). To ensure the request is mapped to the GET verb, this client will +// force the downstream client to create a GET request. +type statusCodeNotImplementedForPostClient struct { + client api.Client +} + +func (c *statusCodeNotImplementedForPostClient) Do(ctx context.Context, req *http.Request) (*http.Response, []byte, error) { + if req.Method == http.MethodPost { + if req.Body != nil { + req.Body.Close() // typically, the HTTP client's transport is responsible for closing the body + } + return &http.Response{ + StatusCode: http.StatusNotImplemented, + Body: io.NopCloser(strings.NewReader("")), // the body must not be nill as per documentation + }, []byte{}, nil + } + return c.client.Do(ctx, req) +} + +func (c *statusCodeNotImplementedForPostClient) URL(ep string, args map[string]string) *url.URL { + return c.client.URL(ep, args) +} + // PromQL implements a cluster condition that matches based on PromQL. type PromQL struct { kubeClient kubernetes.Interface @@ -117,11 +149,15 @@ func (p *PromQL) Match(ctx context.Context, condition *configv1.ClusterCondition return false, fmt.Errorf("creating PromQL round-tripper: %w", err) } - client, err := api.NewClient(clientConfig) + promqlClient, err := api.NewClient(clientConfig) if err != nil { return false, fmt.Errorf("creating PromQL client: %w", err) } + client := &statusCodeNotImplementedForPostClient{ + client: promqlClient, + } + v1api := prometheusv1.NewAPI(client) queryContext := ctx