client.go 4.7 KB

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