Path: blob/main/component/otelcol/auth/sigv4/sigv4_test.go
4096 views
package sigv4_test12import (3"context"4"fmt"5"net/http"6"net/http/httptest"7"strings"8"testing"9"time"1011"github.com/grafana/agent/component/otelcol/auth"12"github.com/grafana/agent/component/otelcol/auth/sigv4"13"github.com/grafana/agent/pkg/flow/componenttest"14"github.com/grafana/agent/pkg/river"15"github.com/grafana/agent/pkg/util"16"github.com/stretchr/testify/assert"17"github.com/stretchr/testify/require"18"go.opentelemetry.io/collector/config/configauth"19)2021// Test performs a basic integration test which runs the otelcol.auth.sigv422// component and ensures that it can be used for authentication.23func Test(t *testing.T) {24type TestDefinition struct {25testName string26awsAccessKeyId string27awsSecredAccessKey string28region string29service string30assumeRoleARN string31assumeRoleSessionName string32assumeRoleStsRegion string33riverConfig string34}3536tests := []TestDefinition{37{38testName: "Test1",39awsAccessKeyId: "example_access_key_id",40awsSecredAccessKey: "example_secret_access_key",41region: "example_region",42service: "example_service",43assumeRoleARN: "",44assumeRoleSessionName: "role_session_name",45assumeRoleStsRegion: "",46riverConfig: "",47},48{49testName: "Test2",50awsAccessKeyId: "example_access_key_id",51awsSecredAccessKey: "example_secret_access_key",52region: "example_region",53service: "example_service",54assumeRoleARN: "",55assumeRoleSessionName: "",56assumeRoleStsRegion: "region",57riverConfig: "",58},59{60testName: "Test3",61awsAccessKeyId: "example_access_key_id",62awsSecredAccessKey: "example_secret_access_key",63region: "example_region",64service: "",65assumeRoleARN: "",66assumeRoleSessionName: "",67assumeRoleStsRegion: "",68riverConfig: "",69},70{71testName: "Test4",72awsAccessKeyId: "example_access_key_id",73awsSecredAccessKey: "example_secret_access_key",74region: "",75service: "example_service",76assumeRoleARN: "",77assumeRoleSessionName: "",78assumeRoleStsRegion: "",79riverConfig: "",80},81}8283{84var tt = &tests[0]85tt.riverConfig = fmt.Sprintf(`86assume_role {87session_name = "%s"88}89region = "%s"90service = "%s"91`, tt.assumeRoleSessionName, tt.region, tt.service)92}93{94var tt = &tests[1]95tt.riverConfig = fmt.Sprintf(`96assume_role {97sts_region = "%s"98}99region = "%s"100service = "%s"101`, tt.assumeRoleStsRegion, tt.region, tt.service)102}103{104var tt = &tests[2]105tt.riverConfig = fmt.Sprintf(`106region = "%s"107`, tt.region)108}109{110var tt = &tests[3]111tt.riverConfig = fmt.Sprintf(`112service = "%s"113`, tt.service)114}115116for _, tt := range tests {117// Create an HTTP server which will assert that sigv4 auth has been injected118// into the request.119srv := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {120// The date looks like "20230306T125949Z;"121dateTimeSplit := strings.Split(r.Header.Get("X-Amz-Date"), "T")122assert.Equal(t, len(dateTimeSplit), 2)123date := dateTimeSplit[0]124assert.Equal(t, len(date), 8)125126authHeaderSplit := strings.Split(r.Header.Get("Authorization"), " ")127128assert.Equal(t, authHeaderSplit[0], "AWS4-HMAC-SHA256")129130credential := fmt.Sprintf("Credential=%s/%s/%s/%s/aws4_request,", tt.awsAccessKeyId, date, tt.region, tt.service)131assert.Equal(t, authHeaderSplit[1], credential)132133assert.Equal(t, authHeaderSplit[2], "SignedHeaders=host;x-amz-date,")134135signatureSplit := strings.Split(authHeaderSplit[3], "=")136assert.Equal(t, 2, len(signatureSplit))137assert.Equal(t, "Signature", signatureSplit[0])138139// SHA256 will always produce a 64 character string140require.Equal(t, 64, len(signatureSplit[1]))141142w.WriteHeader(http.StatusOK)143}))144defer srv.Close()145146t.Setenv("AWS_ACCESS_KEY_ID", tt.awsAccessKeyId)147t.Setenv("AWS_SECRET_ACCESS_KEY", tt.awsSecredAccessKey)148149ctx := componenttest.TestContext(t)150ctx, cancel := context.WithTimeout(ctx, time.Minute)151defer cancel()152153l := util.TestLogger(t)154155// Create and run our component156ctrl, err := componenttest.NewControllerFromID(l, "otelcol.auth.sigv4")157require.NoError(t, err)158159cfg := tt.riverConfig160t.Logf("River configuration: %s", cfg)161var args sigv4.Arguments162require.NoError(t, river.Unmarshal([]byte(cfg), &args))163164go func() {165err := ctrl.Run(ctx, args)166require.NoError(t, err)167}()168169require.NoError(t, ctrl.WaitRunning(time.Second), "component never started")170require.NoError(t, ctrl.WaitExports(time.Second), "component never exported anything")171172// Get the authentication extension from our component and use it to make a173// request to our test server.174exports := ctrl.Exports().(auth.Exports)175require.NotNil(t, exports.Handler.Extension, "handler extension is nil")176177clientAuth, ok := exports.Handler.Extension.(configauth.ClientAuthenticator)178require.True(t, ok, "handler does not implement configauth.ClientAuthenticator")179180rt, err := clientAuth.RoundTripper(http.DefaultTransport)181require.NoError(t, err)182cli := &http.Client{Transport: rt}183184// Wait until the request finishes. We don't assert anything else here; our185// HTTP handler won't write the response until it ensures that the sigv4186// data is set.187req, err := http.NewRequestWithContext(ctx, http.MethodGet, srv.URL, nil)188require.NoError(t, err)189resp, err := cli.Do(req)190require.NoError(t, err, "HTTP request failed")191require.Equal(t, http.StatusOK, resp.StatusCode)192}193}194195196