From 28c4ff2ac558bc291d763d9c6627de67d6a196a6 Mon Sep 17 00:00:00 2001 From: Cem Mergenci Date: Wed, 27 Mar 2024 11:11:00 +0300 Subject: [PATCH] Count external API calls. Signed-off-by: Cem Mergenci --- go.mod | 13 +++--- go.sum | 22 ++++----- internal/clients/aws.go | 70 +++++++++++++++++++++++++++-- internal/clients/provider_config.go | 1 + 4 files changed, 86 insertions(+), 20 deletions(-) diff --git a/go.mod b/go.mod index d48c2c12a9..6b5b9bb4bb 100644 --- a/go.mod +++ b/go.mod @@ -8,7 +8,8 @@ go 1.21 require ( dario.cat/mergo v1.0.0 - github.com/aws/aws-sdk-go-v2 v1.24.0 + github.com/aws/aws-sdk-go v1.49.2 + github.com/aws/aws-sdk-go-v2 v1.24.1 github.com/aws/aws-sdk-go-v2/config v1.26.1 github.com/aws/aws-sdk-go-v2/credentials v1.16.12 github.com/aws/aws-sdk-go-v2/feature/ec2/imds v1.14.10 @@ -17,7 +18,7 @@ require ( github.com/aws/smithy-go v1.19.0 github.com/crossplane/crossplane-runtime v1.16.0-rc.1.0.20240213134610-7fcb8c5cad6f github.com/crossplane/crossplane-tools v0.0.0-20230925130601-628280f8bf79 - github.com/crossplane/upjet v1.3.0-rc.0.0.20240314162745-2ef7077f6d16 + github.com/crossplane/upjet v1.3.0-rc.0.0.20240328123350-4c67d8ebd380 github.com/go-ini/ini v1.46.0 github.com/google/go-cmp v0.6.0 github.com/hashicorp/terraform-json v0.18.0 @@ -44,11 +45,10 @@ require ( github.com/antchfx/htmlquery v1.2.4 // indirect github.com/antchfx/xpath v1.2.0 // indirect github.com/apparentlymart/go-textseg/v15 v15.0.0 // indirect - github.com/aws/aws-sdk-go v1.49.2 // indirect github.com/aws/aws-sdk-go-v2/aws/protocol/eventstream v1.5.4 // indirect github.com/aws/aws-sdk-go-v2/feature/s3/manager v1.15.7 // indirect - github.com/aws/aws-sdk-go-v2/internal/configsources v1.2.9 // indirect - github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.5.9 // indirect + github.com/aws/aws-sdk-go-v2/internal/configsources v1.2.10 // indirect + github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.5.10 // indirect github.com/aws/aws-sdk-go-v2/internal/ini v1.7.2 // indirect github.com/aws/aws-sdk-go-v2/internal/v4a v1.2.9 // indirect github.com/aws/aws-sdk-go-v2/service/accessanalyzer v1.26.5 // indirect @@ -140,6 +140,7 @@ require ( github.com/aws/aws-sdk-go-v2/service/sso v1.18.5 // indirect github.com/aws/aws-sdk-go-v2/service/ssoadmin v1.23.5 // indirect github.com/aws/aws-sdk-go-v2/service/ssooidc v1.21.5 // indirect + github.com/aws/aws-sdk-go-v2/service/support v1.19.6 // indirect github.com/aws/aws-sdk-go-v2/service/swf v1.20.5 // indirect github.com/aws/aws-sdk-go-v2/service/timestreamwrite v1.23.6 // indirect github.com/aws/aws-sdk-go-v2/service/transcribe v1.34.5 // indirect @@ -277,6 +278,6 @@ require ( replace golang.org/x/exp => golang.org/x/exp v0.0.0-20231006140011-7918f672742d -replace github.com/hashicorp/terraform-provider-aws => github.com/upbound/terraform-provider-aws v0.0.0-20240129145938-c69f68a59916 +replace github.com/hashicorp/terraform-provider-aws => github.com/upbound/terraform-provider-aws v0.0.0-20240328111213-f2f0fdd63866 replace github.com/hashicorp/terraform-plugin-log => github.com/gdavison/terraform-plugin-log v0.0.0-20230928191232-6c653d8ef8fb diff --git a/go.sum b/go.sum index 4174fb30ed..2f9ce1753d 100644 --- a/go.sum +++ b/go.sum @@ -28,8 +28,8 @@ github.com/apparentlymart/go-textseg/v15 v15.0.0 h1:uYvfpb3DyLSCGWnctWKGj857c6ew github.com/apparentlymart/go-textseg/v15 v15.0.0/go.mod h1:K8XmNZdhEBkdlyDdvbmmsvpAG721bKi0joRfFdHIWJ4= github.com/aws/aws-sdk-go v1.49.2 h1:+4BEcm1nPCoDbVd+gg8cdxpa1qJfrvnddy12vpEVWjw= github.com/aws/aws-sdk-go v1.49.2/go.mod h1:LF8svs817+Nz+DmiMQKTO3ubZ/6IaTpq3TjupRn3Eqk= -github.com/aws/aws-sdk-go-v2 v1.24.0 h1:890+mqQ+hTpNuw0gGP6/4akolQkSToDJgHfQE7AwGuk= -github.com/aws/aws-sdk-go-v2 v1.24.0/go.mod h1:LNh45Br1YAkEKaAqvmE1m8FUx6a5b/V0oAKV7of29b4= +github.com/aws/aws-sdk-go-v2 v1.24.1 h1:xAojnj+ktS95YZlDf0zxWBkbFtymPeDP+rvUQIH3uAU= +github.com/aws/aws-sdk-go-v2 v1.24.1/go.mod h1:LNh45Br1YAkEKaAqvmE1m8FUx6a5b/V0oAKV7of29b4= github.com/aws/aws-sdk-go-v2/aws/protocol/eventstream v1.5.4 h1:OCs21ST2LrepDfD3lwlQiOqIGp6JiEUqG84GzTDoyJs= github.com/aws/aws-sdk-go-v2/aws/protocol/eventstream v1.5.4/go.mod h1:usURWEKSNNAcAZuzRn/9ZYPT8aZQkR7xcCtunK/LkJo= github.com/aws/aws-sdk-go-v2/config v1.26.1 h1:z6DqMxclFGL3Zfo+4Q0rLnAZ6yVkzCRxhRMsiRQnD1o= @@ -40,10 +40,10 @@ github.com/aws/aws-sdk-go-v2/feature/ec2/imds v1.14.10 h1:w98BT5w+ao1/r5sUuiH6Jk github.com/aws/aws-sdk-go-v2/feature/ec2/imds v1.14.10/go.mod h1:K2WGI7vUvkIv1HoNbfBA1bvIZ+9kL3YVmWxeKuLQsiw= github.com/aws/aws-sdk-go-v2/feature/s3/manager v1.15.7 h1:FnLf60PtjXp8ZOzQfhJVsqF0OtYKQZWQfqOLshh8YXg= github.com/aws/aws-sdk-go-v2/feature/s3/manager v1.15.7/go.mod h1:tDVvl8hyU6E9B8TrnNrZQEVkQlB8hjJwcgpPhgtlnNg= -github.com/aws/aws-sdk-go-v2/internal/configsources v1.2.9 h1:v+HbZaCGmOwnTTVS86Fleq0vPzOd7tnJGbFhP0stNLs= -github.com/aws/aws-sdk-go-v2/internal/configsources v1.2.9/go.mod h1:Xjqy+Nyj7VDLBtCMkQYOw1QYfAEZCVLrfI0ezve8wd4= -github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.5.9 h1:N94sVhRACtXyVcjXxrwK1SKFIJrA9pOJ5yu2eSHnmls= -github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.5.9/go.mod h1:hqamLz7g1/4EJP+GH5NBhcUMLjW+gKLQabgyz6/7WAU= +github.com/aws/aws-sdk-go-v2/internal/configsources v1.2.10 h1:vF+Zgd9s+H4vOXd5BMaPWykta2a6Ih0AKLq/X6NYKn4= +github.com/aws/aws-sdk-go-v2/internal/configsources v1.2.10/go.mod h1:6BkRjejp/GR4411UGqkX8+wFMbFbqsUIimfK4XjOKR4= +github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.5.10 h1:nYPe006ktcqUji8S2mqXf9c/7NdiKriOwMvWQHgYztw= +github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.5.10/go.mod h1:6UV4SZkVvmODfXKql4LCbaZUpF7HO2BX38FgBf9ZOLw= github.com/aws/aws-sdk-go-v2/internal/ini v1.7.2 h1:GrSw8s0Gs/5zZ0SX+gX4zQjRnRsMJDJ2sLur1gRBhEM= github.com/aws/aws-sdk-go-v2/internal/ini v1.7.2/go.mod h1:6fQQgfuGmw8Al/3M2IgIllycxV7ZW7WCdVSqfBeUiCY= github.com/aws/aws-sdk-go-v2/internal/v4a v1.2.9 h1:ugD6qzjYtB7zM5PN/ZIeaAIyefPaD82G8+SJopgvUpw= @@ -230,6 +230,8 @@ github.com/aws/aws-sdk-go-v2/service/ssooidc v1.21.5 h1:2k9KmFawS63euAkY4/ixVNsY github.com/aws/aws-sdk-go-v2/service/ssooidc v1.21.5/go.mod h1:W+nd4wWDVkSUIox9bacmkBP5NMFQeTJ/xqNabpzSR38= github.com/aws/aws-sdk-go-v2/service/sts v1.26.5 h1:5UYvv8JUvllZsRnfrcMQ+hJ9jNICmcgKPAO1CER25Wg= github.com/aws/aws-sdk-go-v2/service/sts v1.26.5/go.mod h1:XX5gh4CB7wAs4KhcF46G6C8a2i7eupU19dcAAE+EydU= +github.com/aws/aws-sdk-go-v2/service/support v1.19.6 h1:ZD8OSo915ouOvw9JIjT0pccHwubdLoHmQYjAXIxFA0I= +github.com/aws/aws-sdk-go-v2/service/support v1.19.6/go.mod h1:Mzty8X8zv84IXyvPJ0nI1gZhurnKgrD46J6MRgJsGGk= github.com/aws/aws-sdk-go-v2/service/swf v1.20.5 h1:9CU3kwRGpUReKubOsmxgG9LfaVpZ1PW/ON+5ZTKu5Gs= github.com/aws/aws-sdk-go-v2/service/swf v1.20.5/go.mod h1:i01QTdCHqrntRqtNeYmxUSDCcmXERzFCePIcHDjASHE= github.com/aws/aws-sdk-go-v2/service/timestreamwrite v1.23.6 h1:+7xZRneTlcraXL4+oN2kUlQX9ULh4aIxmcpUoR/faGA= @@ -266,8 +268,8 @@ github.com/crossplane/crossplane-runtime v1.16.0-rc.1.0.20240213134610-7fcb8c5ca github.com/crossplane/crossplane-runtime v1.16.0-rc.1.0.20240213134610-7fcb8c5cad6f/go.mod h1:kRcJjJQmBFrR2n/KhwL8wYS7xNfq3D8eK4JliEScOHI= github.com/crossplane/crossplane-tools v0.0.0-20230925130601-628280f8bf79 h1:HigXs5tEQxWz0fcj8hzbU2UAZgEM7wPe0XRFOsrtF8Y= github.com/crossplane/crossplane-tools v0.0.0-20230925130601-628280f8bf79/go.mod h1:+e4OaFlOcmr0JvINHl/yvEYBrZawzTgj6pQumOH1SS0= -github.com/crossplane/upjet v1.3.0-rc.0.0.20240314162745-2ef7077f6d16 h1:rga2kPfuFXAeodP2+Ni8svo38BrfsdaaCv6yejn/+2M= -github.com/crossplane/upjet v1.3.0-rc.0.0.20240314162745-2ef7077f6d16/go.mod h1:0bHLtnejZ9bDeyXuBb9MSOQLvKo3+aoTeUBO8N0dGSA= +github.com/crossplane/upjet v1.3.0-rc.0.0.20240328123350-4c67d8ebd380 h1:qNtHCHpg9+gVEOSvR+lbE/eVejQ7ERF7CXe6/jH+feI= +github.com/crossplane/upjet v1.3.0-rc.0.0.20240328123350-4c67d8ebd380/go.mod h1:0bHLtnejZ9bDeyXuBb9MSOQLvKo3+aoTeUBO8N0dGSA= github.com/cyphar/filepath-securejoin v0.2.4 h1:Ugdm7cg7i6ZK6x3xDF1oEu1nfkyfH53EtKeQYTC3kyg= github.com/cyphar/filepath-securejoin v0.2.4/go.mod h1:aPGpWjXOXUn2NCNjFvBE6aRxGGx79pTxQpKOJNYHHl4= github.com/dave/jennifer v1.4.1 h1:XyqG6cn5RQsTj3qlWQTKlRGAyrTcsk1kUmWdZBzRjDw= @@ -552,8 +554,8 @@ github.com/stretchr/testify v1.8.4 h1:CcVxjf3Q8PM0mHUKJCdn+eZZtm5yQwehR5yeSVQQcU github.com/stretchr/testify v1.8.4/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo= github.com/tmccombs/hcl2json v0.3.3 h1:+DLNYqpWE0CsOQiEZu+OZm5ZBImake3wtITYxQ8uLFQ= github.com/tmccombs/hcl2json v0.3.3/go.mod h1:Y2chtz2x9bAeRTvSibVRVgbLJhLJXKlUeIvjeVdnm4w= -github.com/upbound/terraform-provider-aws v0.0.0-20240129145938-c69f68a59916 h1:W3WAB6utkebXviDpnym5bWaQtqASXeudJ7bu7v9CJA4= -github.com/upbound/terraform-provider-aws v0.0.0-20240129145938-c69f68a59916/go.mod h1:Kb86v3lyFUggXmDTi53PPHLENdWUdD8t3IfjS7rFd+0= +github.com/upbound/terraform-provider-aws v0.0.0-20240328111213-f2f0fdd63866 h1:pBLZE97vtQmP21UGdr+CCWuNilZZYDidbGdBauve6tM= +github.com/upbound/terraform-provider-aws v0.0.0-20240328111213-f2f0fdd63866/go.mod h1:iJUX0JshS3o+xF8KZs+dcnR93xkK5dHr/FHh9jskl4Y= github.com/vmihailenco/msgpack v3.3.3+incompatible/go.mod h1:fy3FlTQTDXWkZ7Bh6AcGMlsjHatGryHQYUTf1ShIgkk= github.com/vmihailenco/msgpack v4.0.4+incompatible h1:dSLoQfGFAo3F6OoNhwUmLwVgaUXK79GlxNBwueZn0xI= github.com/vmihailenco/msgpack v4.0.4+incompatible/go.mod h1:fy3FlTQTDXWkZ7Bh6AcGMlsjHatGryHQYUTf1ShIgkk= diff --git a/internal/clients/aws.go b/internal/clients/aws.go index 13fa90b9ab..c8ea8e73eb 100644 --- a/internal/clients/aws.go +++ b/internal/clients/aws.go @@ -6,12 +6,14 @@ package clients import ( "context" - "reflect" - "unsafe" "github.com/aws/aws-sdk-go-v2/aws" + awsmiddleware "github.com/aws/aws-sdk-go-v2/aws/middleware" "github.com/aws/aws-sdk-go-v2/feature/ec2/imds" + awsrequest "github.com/aws/aws-sdk-go/aws/request" + "github.com/aws/smithy-go/middleware" "github.com/crossplane/crossplane-runtime/pkg/resource" + "github.com/crossplane/upjet/pkg/metrics" "github.com/crossplane/upjet/pkg/terraform" "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema" "github.com/hashicorp/terraform-provider-aws/xpprovider" @@ -111,6 +113,48 @@ func (m *metaOnlyPrimary) Meta() any { return m.meta } +// withExternalAPICallCounter configures an AWS SDK v2 stack (client) +// with an API call counter. AWS SDK v2 offers configuring +// "middlewares" to customize a request. Middlewares can be plugged +// into different steps of the stack. Middlewares can save and access +// metadata in the stack, such as ServiceID (EC2, IAM, etc.) and +// OperationName (DescribeVPCs, etc.). For documentation, see: +// https://aws.github.io/aws-sdk-go-v2/docs/middleware/ +func withExternalAPICallCounter(stack *middleware.Stack) error { + externalAPICallCounterMiddleware := middleware.DeserializeMiddlewareFunc("externalAPICallCounter", + func(ctx context.Context, input middleware.DeserializeInput, next middleware.DeserializeHandler) (middleware.DeserializeOutput, middleware.Metadata, error) { + serviceID := awsmiddleware.GetServiceID(ctx) + operationName := awsmiddleware.GetOperationName(ctx) + + // next.HandleDeserialize() calls the next middleware function + // in the stack, which in turn calls the next. Finally, the + // request is performed. Each middleware function receives the + // output from the middleware function it invoked, processes it, + // and returns its result to the middleware function that + // invoked itself. + output, metadata, err := next.HandleDeserialize(ctx, input) + if err == nil { + metrics.ExternalAPICalls.WithLabelValues(serviceID, operationName).Inc() + } + return output, metadata, err + }, + ) + + // We register the call counter to the end of the deserialization + // step, so that we're right next to Transport handler + // (http.RoundTripper) in the stack (see + // https://aws.github.io/aws-sdk-go-v2/docs/middleware/). In this + // case, it's easy to distinguish API errors from connection + // errors, because only connection errors cause a non-nil error + // returned by next.HandleDeserialize() (see middleware + // implementation above). If we were to register the call counter + // to any other position (such as earlier stack steps (finalize, + // build, etc.) or even the beginning of deserialization step), we + // would have to implement a logic to distinguish between API + // errors and connection errors. + return stack.Deserialize.Add(externalAPICallCounterMiddleware, middleware.After) +} + // configureNoForkAWSClient populates the supplied *terraform.Setup with // Terraform Plugin SDK style AWS client (Meta) and Terraform Plugin Framework // style FrameworkProvider @@ -146,8 +190,7 @@ func configureNoForkAWSClient(ctx context.Context, ps *terraform.Setup, config * // only used for retrieving the ServicePackages from the singleton provider instance p := config.TerraformProvider.Meta() tfAwsConnsClient, diags := tfAwsConnsCfg.GetClient(ctx, &xpprovider.AWSClient{ - // #nosec G103 - ServicePackages: (*xpprovider.AWSClient)(unsafe.Pointer(reflect.ValueOf(p).Pointer())).ServicePackages, + ServicePackages: p.(*xpprovider.AWSClient).ServicePackages, }) if diags.HasError() { return errors.Errorf("cannot construct TF AWS Client from TF AWS Config, %v", diags) @@ -162,5 +205,24 @@ func configureNoForkAWSClient(ctx context.Context, ps *terraform.Setup, config * ps.Meta = tfAwsConnsClient fwProvider := xpprovider.GetFrameworkProviderWithMeta(&metaOnlyPrimary{meta: tfAwsConnsClient}) ps.FrameworkProvider = fwProvider + + // Register AWS SDK v1 call counter. Unlike AWS SDK v2, v1 doesn't + // store service ID (EC2, IAM, etc.) and operation name + // (DescribeVPCs, etc.) in request context. Therefore, it's not + // possible to implement this session handler's functionality with + // an http.RoundTripper. To learn how SDK v1 session handler phases + // map to SDK v2 middleware stack steps, see: + // https://aws.github.io/aws-sdk-go-v2/docs/migrating/#handler-phases + tfAwsConnsClient.Session.Handlers.Send.PushBack(func(r *awsrequest.Request) { + // In case of API errors (or no errors), r.Error is nil. + // In case of connection errors, r.Error is non-nil. + if r.Error == nil { + metrics.ExternalAPICalls.WithLabelValues(r.ClientInfo.ServiceID, r.Operation.Name).Inc() + } + }) + + // Register AWS SDK v2 call counter + tfAwsConnsClient.AppendAPIOptions(withExternalAPICallCounter) + return nil } diff --git a/internal/clients/provider_config.go b/internal/clients/provider_config.go index 4e16df5564..f941401e25 100644 --- a/internal/clients/provider_config.go +++ b/internal/clients/provider_config.go @@ -68,6 +68,7 @@ const ( var userAgentV2 = config.WithAPIOptions([]func(*middleware.Stack) error{ awsmiddleware.AddUserAgentKeyValue("upbound-provider-aws", version.Version), awsmiddleware.AddUserAgentKeyValue("crossplane-provider-aws", version.Version), + withExternalAPICallCounter, }) func getRegion(obj runtime.Object) (string, error) {