client.go (4788B)
1 package client 2 3 import ( 4 "context" 5 "fmt" 6 "net/http" 7 "time" 8 9 "github.com/aws/aws-sdk-go-v2/aws" 10 "github.com/aws/aws-sdk-go-v2/aws/middleware" 11 "github.com/aws/aws-sdk-go-v2/aws/retry" 12 awshttp "github.com/aws/aws-sdk-go-v2/aws/transport/http" 13 "github.com/aws/smithy-go" 14 smithymiddleware "github.com/aws/smithy-go/middleware" 15 smithyhttp "github.com/aws/smithy-go/transport/http" 16 ) 17 18 // ServiceID is the client identifer 19 const ServiceID = "endpoint-credentials" 20 21 // HTTPClient is a client for sending HTTP requests 22 type HTTPClient interface { 23 Do(*http.Request) (*http.Response, error) 24 } 25 26 // Options is the endpoint client configurable options 27 type Options struct { 28 // The endpoint to retrieve credentials from 29 Endpoint string 30 31 // The HTTP client to invoke API calls with. Defaults to client's default HTTP 32 // implementation if nil. 33 HTTPClient HTTPClient 34 35 // Retryer guides how HTTP requests should be retried in case of recoverable 36 // failures. When nil the API client will use a default retryer. 37 Retryer aws.Retryer 38 39 // Set of options to modify how the credentials operation is invoked. 40 APIOptions []func(*smithymiddleware.Stack) error 41 } 42 43 // Copy creates a copy of the API options. 44 func (o Options) Copy() Options { 45 to := o 46 to.APIOptions = make([]func(*smithymiddleware.Stack) error, len(o.APIOptions)) 47 copy(to.APIOptions, o.APIOptions) 48 return to 49 } 50 51 // Client is an client for retrieving AWS credentials from an endpoint 52 type Client struct { 53 options Options 54 } 55 56 // New constructs a new Client from the given options 57 func New(options Options, optFns ...func(*Options)) *Client { 58 options = options.Copy() 59 60 if options.HTTPClient == nil { 61 options.HTTPClient = awshttp.NewBuildableClient() 62 } 63 64 if options.Retryer == nil { 65 // Amazon-owned implementations of this endpoint are known to sometimes 66 // return plaintext responses (i.e. no Code) like normal, add a few 67 // additional status codes 68 options.Retryer = retry.NewStandard(func(o *retry.StandardOptions) { 69 o.Retryables = append(o.Retryables, retry.RetryableHTTPStatusCode{ 70 Codes: map[int]struct{}{ 71 http.StatusTooManyRequests: {}, 72 }, 73 }) 74 }) 75 } 76 77 for _, fn := range optFns { 78 fn(&options) 79 } 80 81 client := &Client{ 82 options: options, 83 } 84 85 return client 86 } 87 88 // GetCredentialsInput is the input to send with the endpoint service to receive credentials. 89 type GetCredentialsInput struct { 90 AuthorizationToken string 91 } 92 93 // GetCredentials retrieves credentials from credential endpoint 94 func (c *Client) GetCredentials(ctx context.Context, params *GetCredentialsInput, optFns ...func(*Options)) (*GetCredentialsOutput, error) { 95 stack := smithymiddleware.NewStack("GetCredentials", smithyhttp.NewStackRequest) 96 options := c.options.Copy() 97 for _, fn := range optFns { 98 fn(&options) 99 } 100 101 stack.Serialize.Add(&serializeOpGetCredential{}, smithymiddleware.After) 102 stack.Build.Add(&buildEndpoint{Endpoint: options.Endpoint}, smithymiddleware.After) 103 stack.Deserialize.Add(&deserializeOpGetCredential{}, smithymiddleware.After) 104 addProtocolFinalizerMiddlewares(stack, options, "GetCredentials") 105 retry.AddRetryMiddlewares(stack, retry.AddRetryMiddlewaresOptions{Retryer: options.Retryer}) 106 middleware.AddSDKAgentKey(middleware.FeatureMetadata, ServiceID) 107 smithyhttp.AddErrorCloseResponseBodyMiddleware(stack) 108 smithyhttp.AddCloseResponseBodyMiddleware(stack) 109 110 for _, fn := range options.APIOptions { 111 if err := fn(stack); err != nil { 112 return nil, err 113 } 114 } 115 116 handler := smithymiddleware.DecorateHandler(smithyhttp.NewClientHandler(options.HTTPClient), stack) 117 result, _, err := handler.Handle(ctx, params) 118 if err != nil { 119 return nil, err 120 } 121 122 return result.(*GetCredentialsOutput), err 123 } 124 125 // GetCredentialsOutput is the response from the credential endpoint 126 type GetCredentialsOutput struct { 127 Expiration *time.Time 128 AccessKeyID string 129 SecretAccessKey string 130 Token string 131 } 132 133 // EndpointError is an error returned from the endpoint service 134 type EndpointError struct { 135 Code string `json:"code"` 136 Message string `json:"message"` 137 Fault smithy.ErrorFault `json:"-"` 138 statusCode int `json:"-"` 139 } 140 141 // Error is the error mesage string 142 func (e *EndpointError) Error() string { 143 return fmt.Sprintf("%s: %s", e.Code, e.Message) 144 } 145 146 // ErrorCode is the error code returned by the endpoint 147 func (e *EndpointError) ErrorCode() string { 148 return e.Code 149 } 150 151 // ErrorMessage is the error message returned by the endpoint 152 func (e *EndpointError) ErrorMessage() string { 153 return e.Message 154 } 155 156 // ErrorFault indicates error fault classification 157 func (e *EndpointError) ErrorFault() smithy.ErrorFault { 158 return e.Fault 159 } 160 161 // HTTPStatusCode implements retry.HTTPStatusCode. 162 func (e *EndpointError) HTTPStatusCode() int { 163 return e.statusCode 164 }