Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
40 changes: 33 additions & 7 deletions util/ratelimits.go
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@
"sync"
"time"

"github.com/go-logr/logr"
"github.com/go-resty/resty/v2"
)

Expand All @@ -46,15 +47,40 @@
return nil
}

var err error
c.ReqRemaining, err = strconv.Atoi(resp.Header().Get("X-Ratelimit-Remaining"))
if err != nil {
return err
var (
err error
logger = logr.FromContextOrDiscard(resp.Request.Context())
now = time.Now().Add(15 * time.Second).Unix()

Check failure on line 53 in util/ratelimits.go

View workflow job for this annotation

GitHub Actions / go-analyze

Magic number: 15, in <argument> detected (mnd)
epochTime int64
// Linode creation limits ref: https://techdocs.akamai.com/linode-api/reference/rate-limits#specific-operation-rate-limits
// Creating Linodes has a dedicated rate limit of 20 requests per 15 seconds.
instancesPostDefaultLimit = 20
reqRemaining int
rateLimitRemainingHeader = resp.Header().Get("X-Ratelimit-Remaining")
rateLimitResetHeader = resp.Header().Get("X-Ratelimit-Reset")
)

if rateLimitRemainingHeader == "" {
logger.Info("missing X-Ratelimit-Remaining header, using default value")
reqRemaining = instancesPostDefaultLimit - 1
} else {
reqRemaining, err = strconv.Atoi(rateLimitRemainingHeader)
if err != nil {
logger.Error(err, "Failed to parse X-Ratelimit-Remaining header, using default value")
reqRemaining = instancesPostDefaultLimit - 1
}
}
c.ReqRemaining = reqRemaining

epochTime, err := strconv.ParseInt(resp.Header().Get("X-Ratelimit-Reset"), 10, 64)
if err != nil {
return err
if rateLimitResetHeader == "" {
logger.Info("missing X-Ratelimit-Reset header, using default value", "X-Ratelimit-Reset", now)
epochTime = now
} else {
epochTime, err = strconv.ParseInt(rateLimitResetHeader, 10, 64)
if err != nil {
logger.Error(err, "Failed to parse X-Ratelimit-Reset header, using default value")
epochTime = now
}
}
c.RefreshTime = time.Unix(epochTime, 0)
return nil
Expand Down
11 changes: 7 additions & 4 deletions util/ratelimits_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -149,7 +149,7 @@ func TestPostRequestCounter_ApiResponseRatelimitCounter(t *testing.T) {
wantErr: false,
},
{
name: "no headers in response",
name: "no headers in response, expect default values",
fields: &PostRequestCounter{
ReqRemaining: 4,
RefreshTime: now,
Expand All @@ -159,8 +159,11 @@ func TestPostRequestCounter_ApiResponseRatelimitCounter(t *testing.T) {
Method: http.MethodPost,
URL: "/v4/linode/instances",
},
RawResponse: &http.Response{
Header: http.Header{"X-Ratelimit-Remaining": []string{"19"}, "X-Ratelimit-Reset": []string{strconv.Itoa(int(time.Now().Add(15 * time.Second).Unix()))}},
},
},
wantErr: true,
wantErr: false,
},
{
name: "missing one value in response header",
Expand All @@ -174,10 +177,10 @@ func TestPostRequestCounter_ApiResponseRatelimitCounter(t *testing.T) {
URL: "/v4/linode/instances",
},
RawResponse: &http.Response{
Header: http.Header{"X-Ratelimit-Remaining": []string{"5"}},
Header: http.Header{"X-Ratelimit-Remaining": []string{"5"}, "X-Ratelimit-Reset": []string{strconv.Itoa(int(time.Now().Add(15 * time.Second).Unix()))}},
},
},
wantErr: true,
wantErr: false,
},
{
name: "correct headers in response",
Expand Down
Loading