Skip to content

Commit

Permalink
frontend: pass the request ID to Clusters Service
Browse files Browse the repository at this point in the history
This change injects the request ID generated by the RP frontend into all
requests made to the CS API.

Jira: https://issues.redhat.com/browse/ARO-14904

Signed-off-by: Simon Pasquier <[email protected]>
  • Loading branch information
simonpasquier committed Jan 29, 2025
1 parent bda68e7 commit 6b8e454
Show file tree
Hide file tree
Showing 4 changed files with 97 additions and 4 deletions.
6 changes: 4 additions & 2 deletions frontend/cmd/cmd.go
Original file line number Diff line number Diff line change
Expand Up @@ -138,10 +138,12 @@ func (opts *FrontendOpts) Run() error {
return err
}

// Initialize Clusters Service Client
// Initialize the Clusters Service Client.
conn, err := sdk.NewUnauthenticatedConnectionBuilder().
TransportWrapper(func(r http.RoundTripper) http.RoundTripper {
return otelhttp.NewTransport(r)
return otelhttp.NewTransport(
frontend.RequestIDPropagator(r),
)
}).
URL(opts.clustersServiceURL).
Insecure(opts.insecure).
Expand Down
26 changes: 26 additions & 0 deletions frontend/pkg/frontend/ocm.go
Original file line number Diff line number Diff line change
Expand Up @@ -406,3 +406,29 @@ func (f *Frontend) BuildCSNodePool(ctx context.Context, nodePool *api.HCPOpenShi

return npBuilder.Build()
}

// transportFunc implements the http.RoundTripper interface.
type transportFunc func(*http.Request) (*http.Response, error)

var _ = http.RoundTripper(transportFunc(nil))

func (rtf transportFunc) RoundTrip(r *http.Request) (*http.Response, error) {
return rtf(r)
}

const clusterServerRequestIDHeader = "X-Request-ID"

// RequestIDPropagator returns an http.RoundTripper interface which reads the
// request ID from the request's context and propagates it to the Clusters
// Service API via the "X-Request-ID" header.
func RequestIDPropagator(next http.RoundTripper) http.RoundTripper {
return transportFunc(func(r *http.Request) (*http.Response, error) {
correlationData, err := CorrelationDataFromContext(r.Context())
if err == nil {
r = r.Clone(r.Context())
r.Header.Set(clusterServerRequestIDHeader, correlationData.RequestID.String())
}

return next.RoundTrip(r)
})
}
65 changes: 65 additions & 0 deletions frontend/pkg/frontend/ocm_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,65 @@
package frontend

import (
"context"
"io"
"net/http"
"net/http/httptest"
"testing"

"github.com/Azure/ARO-HCP/internal/api/arm"
"github.com/google/uuid"
)

// Copyright (c) Microsoft Corporation.
// Licensed under the Apache License 2.0.

func TestRequestIDPropagator(t *testing.T) {
const testRequestID = "00000000-0000-0000-0000-000000000000"

ts := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
_, _ = w.Write([]byte(r.Header.Get(clusterServerRequestIDHeader)))
}))
defer ts.Close()

do := func(c *http.Client) string {
t.Helper()

ctx := context.Background()
r, err := http.NewRequestWithContext(ctx, http.MethodGet, ts.URL, nil)
if err != nil {
t.Fatalf("unexpected error: %s", err)
}
correlationData := arm.NewCorrelationData(r)
correlationData.RequestID = uuid.MustParse(testRequestID)
r = r.WithContext(ContextWithCorrelationData(ctx, correlationData))

rs, err := c.Do(r)
if err != nil {
t.Fatalf("unexpected error from server: %s", err)
}

if rs.StatusCode != http.StatusOK {
t.Fatalf("unexpected status code: %d", rs.StatusCode)
}

b, err := io.ReadAll(rs.Body)
if err != nil {
t.Fatalf("unexpected error reading response: %s", err)
}

return string(b)
}

// Without the transport wrapper, the request ID isn't echoed.
c := ts.Client()
if ret := do(c); ret != "" {
t.Fatalf("expecting an empty response, got %q", ret)
}

// With the transport wrapper, the request ID is echoed.
c.Transport = RequestIDPropagator(c.Transport)
if ret := do(c); ret != testRequestID {
t.Fatalf("expecting %q, got %q", testRequestID, ret)
}
}
4 changes: 2 additions & 2 deletions go.work.sum
Original file line number Diff line number Diff line change
Expand Up @@ -1409,7 +1409,6 @@ github.com/stretchr/objx v0.1.0 h1:4G4v2dO3VZwixGIRoQ5Lfboy6nUhCyYzaqnIAPPhYs4=
github.com/stretchr/objx v0.1.1/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
github.com/stretchr/objx v0.2.0/go.mod h1:qt09Ya8vawLte6SNmTgCsAVtYtaKzEcn8ATUoHMkEqE=
github.com/stretchr/objx v0.5.0 h1:1zr/of2m5FGMsad5YfcqgdqdWrIhu+EBEJRhR1U7z/c=
github.com/stretchr/objx v0.5.2 h1:xuMeJ0Sdp5ZMRXx/aWO6RZxdr3beISkG5/G/aIRr3pY=
github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs=
github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4=
github.com/stretchr/testify v1.5.1/go.mod h1:5W2xD1RspED5o8YsWQXVCued0rvSQ+mT+I5cxcmMvtA=
Expand Down Expand Up @@ -1739,6 +1738,7 @@ golang.org/x/mod v0.17.0/go.mod h1:hTbmBsO62+eylJbnUtE2MGJUyE7QWk4xUqPFrRgJ+7c=
golang.org/x/mod v0.18.0/go.mod h1:hTbmBsO62+eylJbnUtE2MGJUyE7QWk4xUqPFrRgJ+7c=
golang.org/x/mod v0.19.0 h1:fEdghXQSo20giMthA7cd28ZC+jts4amQ3YMXiP5oMQ8=
golang.org/x/mod v0.19.0/go.mod h1:hTbmBsO62+eylJbnUtE2MGJUyE7QWk4xUqPFrRgJ+7c=
golang.org/x/mod v0.21.0 h1:vvrHzRwRfVKSiLrG+d4FMl/Qi4ukBCE6kZlTUkDYRT0=
golang.org/x/mod v0.21.0/go.mod h1:6SkKJ3Xj0I0BrPOZoBy3bdMptDDU9oJrpohJ3eWZ1fY=
golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
golang.org/x/net v0.0.0-20180826012351-8a410e7b638d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
Expand Down Expand Up @@ -1814,7 +1814,6 @@ golang.org/x/oauth2 v0.18.0/go.mod h1:Wf7knwG0MPoWIMMBgFlEaSUDaKskp0dCfrlJRJXbBi
golang.org/x/oauth2 v0.20.0/go.mod h1:XYTD2NtWslqkgxebSiOHnXEap4TF09sJSc7H1sXbhtI=
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/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20181108010431-42b317875d0f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20181221193216-37e7f081c4d4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
Expand Down Expand Up @@ -2015,6 +2014,7 @@ golang.org/x/tools v0.18.0/go.mod h1:GL7B4CwcLLeo59yx/9UWWuNOW1n3VZ4f5axWfML7Lcg
golang.org/x/tools v0.20.0/go.mod h1:WvitBU7JJf6A4jOdg4S1tviW9bhUxkgeCui/0JHctQg=
golang.org/x/tools v0.21.0/go.mod h1:aiJjzUbINMkxbQROHiO6hDPo2LHcIPhhQsa9DLh0yGk=
golang.org/x/tools v0.22.0/go.mod h1:aCwcsjqvq7Yqt6TNyX7QMU2enbQ/Gt0bo6krSeEri+c=
golang.org/x/tools v0.26.0 h1:v/60pFQmzmT9ExmjDv2gGIfi3OqfKoEP6I5+umXlbnQ=
golang.org/x/tools v0.26.0/go.mod h1:TPVVj70c7JJ3WCazhD8OdXcZg/og+b9+tH/KxylGwH0=
golang.org/x/xerrors v0.0.0-20190410155217-1f06c39b4373/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
golang.org/x/xerrors v0.0.0-20190513163551-3ee3066db522/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
Expand Down

0 comments on commit 6b8e454

Please sign in to comment.