request.go 2.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990
  1. package nodestats
  2. import (
  3. "fmt"
  4. "math"
  5. "net/http"
  6. "strconv"
  7. "time"
  8. "github.com/opencost/opencost/core/pkg/log"
  9. )
  10. // HttpClient is an interface that captures the Do method of the http.Client. We use this interface to allow
  11. // mocking in tests.
  12. type HttpClient interface {
  13. Do(req *http.Request) (*http.Response, error)
  14. }
  15. type NodeHttpClient struct {
  16. client HttpClient
  17. }
  18. // NewNodeHttpClient creates a new NodeHttpClient with the provided HttpClient.
  19. func NewNodeHttpClient(client HttpClient) *NodeHttpClient {
  20. return &NodeHttpClient{
  21. client: client,
  22. }
  23. }
  24. // AttemptEndPoint will hit a specified endpoint with as many retries as it is allotted.
  25. func (c *NodeHttpClient) AttemptEndPoint(method string, URL string, bearerToken string) (*http.Response, error) {
  26. attempts := uint(1)
  27. for i := uint(0); i < attempts; i++ {
  28. if i > 0 {
  29. time.Sleep(time.Duration(int64(math.Pow(2, float64(i)))) * time.Second)
  30. }
  31. data, err := c.makeRequest(method, URL, bearerToken)
  32. if err == nil {
  33. return data, nil
  34. }
  35. log.Warnf("Error making request to %s: %s", URL, err)
  36. }
  37. return nil, fmt.Errorf("requests to %v failed", URL)
  38. }
  39. // makeRequest will call out to an endpoint and attempt to decode the body into an existing
  40. // data type.
  41. func (c *NodeHttpClient) makeRequest(method string, URL string, bearerToken string) (*http.Response, error) {
  42. request, err := http.NewRequest(method, URL, nil)
  43. if err != nil {
  44. return nil, err
  45. }
  46. if bearerToken != "" {
  47. request.Header.Add("Authorization", "bearer "+bearerToken)
  48. }
  49. resp, err := c.client.Do(request)
  50. if err != nil {
  51. return nil, err
  52. }
  53. if !(resp.StatusCode >= 200 && resp.StatusCode <= 299) {
  54. return nil, fmt.Errorf("invalid response %s", strconv.Itoa(resp.StatusCode))
  55. }
  56. return resp, nil
  57. }
  58. // NodeHttpConnect is a struct that represents a connection to a node using an http client and endpoint formatter.
  59. type NodeHttpConnection struct {
  60. formatter NodeEndpointFormatter
  61. client *NodeHttpClient
  62. }
  63. // NewNodeHttpConnection creates a new HttpConnection with the provided NodeHttpClient and NodeEndpointFormatter.
  64. func NewNodeHttpConnection(client *NodeHttpClient, formatter NodeEndpointFormatter) *NodeHttpConnection {
  65. return &NodeHttpConnection{
  66. formatter: formatter,
  67. client: client,
  68. }
  69. }
  70. // AttemptEndPoint will hit a specified endpoint leveraging the internal http client and formatter for the endpoint.
  71. func (nhc *NodeHttpConnection) AttemptEndPoint(method string, url string, bearerToken string) (*http.Response, error) {
  72. return nhc.client.AttemptEndPoint(method, nhc.formatter.FormatEndpoint(url), bearerToken)
  73. }