Skip to content

Commit

Permalink
Cache Linode API GET calls
Browse files Browse the repository at this point in the history
  • Loading branch information
Richard Kovacs authored and mhmxs committed Feb 13, 2024
1 parent 8b97d86 commit 114d9ad
Show file tree
Hide file tree
Showing 5 changed files with 131 additions and 7 deletions.
2 changes: 2 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
.DS_Store
.idea
.vscode
__debug_bin*
bin/*
kind-logs-*
cover.out
Expand Down
87 changes: 80 additions & 7 deletions cloud/scope/common.go
Original file line number Diff line number Diff line change
@@ -1,21 +1,94 @@
package scope

import (
"fmt"
"net/http"
"sync"

"github.com/bxcodec/gotcha"
"github.com/bxcodec/gotcha/cache"
"github.com/bxcodec/httpcache"
"github.com/bxcodec/httpcache/cache/inmem"
"github.com/linode/cluster-api-provider-linode/util"
"github.com/linode/linodego"
"golang.org/x/oauth2"
)

var (
initClient sync.Once
linodeClient linodego.Client
)

func createLinodeClient(apiKey string) *linodego.Client {
tokenSource := oauth2.StaticTokenSource(&oauth2.Token{AccessToken: apiKey})
initClient.Do(func() {
//nolint:forcetypeassert // Always OK.
baseTrans := http.DefaultTransport.(*http.Transport).Clone()
baseTrans.MaxIdleConns = util.DefaultLinodeAPIMaxIdleConns
// Otherwise we use just a few connections.
baseTrans.MaxConnsPerHost = baseTrans.MaxIdleConns
baseTrans.MaxIdleConnsPerHost = baseTrans.MaxIdleConns

oauth2Client := &http.Client{
Transport: &oauth2.Transport{
Source: tokenSource,
},
}
linodeClient := linodego.NewClient(oauth2Client)
oauthTrans := &oauth2.Transport{
Source: oauth2.StaticTokenSource(&oauth2.Token{AccessToken: apiKey}),
Base: baseTrans,
}

oauth2Client := &http.Client{
Timeout: util.DefaultLinodeAPITimeout,
Transport: oauthTrans,
}

cacheStore := gotcha.New(&cache.Option{
AlgorithmType: cache.LRUAlgorithm,
ExpiryTime: util.DefaultLinodeAPICacheExpiration,
MaxSizeItem: util.DefaultLinodeAPICacheMaxSizeItem,
MaxMemory: util.DefaultLinodeAPICacheMaxMemory,
})
cachedTrans := httpcache.NewCacheHandlerRoundtrip(&maxAgeFixRoundTripper{
oauthTrans,
}, false, inmem.NewCache(cacheStore))

oauth2Client.Transport = &reqURIFixRoundTripper{
plain: oauthTrans,
cached: cachedTrans,
}

linodeClient = linodego.NewClient(oauth2Client)
})

return &linodeClient
}

type maxAgeFixRoundTripper struct {
http.RoundTripper
}

func (r *maxAgeFixRoundTripper) RoundTrip(req *http.Request) (*http.Response, error) {
resp, err := r.RoundTripper.RoundTrip(req)
if err == nil && req.Method == http.MethodGet {
// Workaround, because Linode API sends max-age=0 all the time.
resp.Header.Set("Cache-Control", util.DefaultLinodeAPICacheControlOverride)
}

return resp, err
}

type reqURIFixRoundTripper struct {
plain http.RoundTripper
cached http.RoundTripper
}

func (r *reqURIFixRoundTripper) RoundTrip(req *http.Request) (*http.Response, error) {
if req.Method != http.MethodGet {
return r.plain.RoundTrip(req)
}

// Workaround because Linodego doesn't set it.
req.RequestURI = req.URL.Path

if filter := req.Header.Get("X-Filter"); filter != "" {
req.RequestURI = fmt.Sprintf("%s?filter=%s", req.RequestURI, filter)
}

return r.cached.RoundTrip(req)
}
2 changes: 2 additions & 0 deletions go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ go 1.21
toolchain go1.21.5

require (
github.com/bxcodec/httpcache v1.0.0-beta.3
github.com/go-logr/logr v1.4.1
github.com/google/uuid v1.3.1
github.com/linode/linodego v1.28.0
Expand All @@ -23,6 +24,7 @@ require (
require (
github.com/beorn7/perks v1.0.1 // indirect
github.com/blang/semver/v4 v4.0.0 // indirect
github.com/bxcodec/gotcha v1.0.0-beta.2 // indirect
github.com/cespare/xxhash/v2 v2.2.0 // indirect
github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc // indirect
github.com/emicklei/go-restful/v3 v3.11.0 // indirect
Expand Down
9 changes: 9 additions & 0 deletions go.sum
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,10 @@ github.com/beorn7/perks v1.0.1 h1:VlbKKnNfV8bJzeqoa4cOKqO6bYr3WgKZxO8Z16+hsOM=
github.com/beorn7/perks v1.0.1/go.mod h1:G2ZrVWU2WbWT9wwq4/hrbKbnv/1ERSJQ0ibhJ6rlkpw=
github.com/blang/semver/v4 v4.0.0 h1:1PFHFE6yCCTv8C1TeyNNarDzntLi7wMI5i/pzqYIsAM=
github.com/blang/semver/v4 v4.0.0/go.mod h1:IbckMUScFkM3pff0VJDNKRiT6TG/YpiHIM2yvyW5YoQ=
github.com/bxcodec/gotcha v1.0.0-beta.2 h1:0jY/Mx6O5jzM2fkcz84zzyy67hLu/bKGJEFTtcRmw5I=
github.com/bxcodec/gotcha v1.0.0-beta.2/go.mod h1:MEL9PRYL9Squu1zxreMIzJU6xtMouPmQybWEtXrL1nk=
github.com/bxcodec/httpcache v1.0.0-beta.3 h1:4h+Yoda40PthVkiFa5hPtCoH46qbDRq/iCT7M5ACoR4=
github.com/bxcodec/httpcache v1.0.0-beta.3/go.mod h1:NdIkH0u1smImRyp35nIGeqG5IFeKbnyyBIv8cEGSXMY=
github.com/cespare/xxhash/v2 v2.2.0 h1:DC2CZ1Ep5Y4k3ZQ899DldepgrayRUGE6BBZ/cd9Cj44=
github.com/cespare/xxhash/v2 v2.2.0/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs=
github.com/chzyer/logex v1.1.10/go.mod h1:+Ywpsq7O8HXn0nuIou7OrIPyXbp3wmkHB+jjWRnGsAI=
Expand Down Expand Up @@ -119,6 +123,8 @@ github.com/onsi/gomega v1.31.1 h1:KYppCUK+bUgAZwHOu7EXVBKyQA6ILvOESHkn/tgoqvo=
github.com/onsi/gomega v1.31.1/go.mod h1:y40C95dwAD1Nz36SsEnxvfFe8FFfNxzI5eJ0EYGyAy0=
github.com/opencontainers/go-digest v1.0.0 h1:apOUWs51W5PlhuyGyz9FCeeBIOUDA/6nW8Oi/yOhh5U=
github.com/opencontainers/go-digest v1.0.0/go.mod h1:0JzlMkj0TRzQZfJkVvzbP0HBR3IKzErnv2BNG4W4MAM=
github.com/patrickmn/go-cache v2.1.0+incompatible h1:HRMgzkcYKYpi3C8ajMPV8OFXaaRUnok+kx1WdO15EQc=
github.com/patrickmn/go-cache v2.1.0+incompatible/go.mod h1:3Qf8kWWT7OJRJbdiICTKqZju1ZixQ/KpMGzzAfe6+WQ=
github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4=
github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
Expand All @@ -145,8 +151,10 @@ github.com/stoewer/go-strcase v1.2.0 h1:Z2iHWqGXH00XYgqDmNgQbIBxf3wrNq0F3feEy0ai
github.com/stoewer/go-strcase v1.2.0/go.mod h1:IBiWB2sKIp3wVVQ3Y035++gc+knqhUQag1KpM8ahLw8=
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw=
github.com/stretchr/objx v0.5.0 h1:1zr/of2m5FGMsad5YfcqgdqdWrIhu+EBEJRhR1U7z/c=
github.com/stretchr/objx v0.5.0/go.mod h1:Yh+to48EsGEfYuaHDzXPcE3xhTkx73EhmCGUpEOglKo=
github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI=
github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4=
github.com/stretchr/testify v1.6.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU=
Expand Down Expand Up @@ -261,6 +269,7 @@ gopkg.in/inf.v0 v0.9.1 h1:73M5CoZyi3ZLMOyDlQh031Cx6N9NDJ2Vvfl76EDAgDc=
gopkg.in/inf.v0 v0.9.1/go.mod h1:cWUDdTG/fYaXco+Dcufb5Vnc6Gp2YChqWtbxRZE0mXw=
gopkg.in/ini.v1 v1.67.0 h1:Dgnx+6+nfE+IfzjUEISNeydPJh9AXNNsWbGP9KzCsOA=
gopkg.in/ini.v1 v1.67.0/go.mod h1:pNLf8WUiyNEtQjuu5G5vTm06TEv9tsIgeAvK8hOrP4k=
gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
gopkg.in/yaml.v2 v2.2.8/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
gopkg.in/yaml.v2 v2.4.0 h1:D8xgwECY7CYvx+Y2n4sBz93Jn9JRvxdiyyo8CTfuKaY=
gopkg.in/yaml.v2 v2.4.0/go.mod h1:RDklbk79AGWmwhnvt/jBztapEOGDOx6ZbXqjP6csGnQ=
Expand Down
38 changes: 38 additions & 0 deletions util/defaults.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
/*
Copyright 2023 Akamai Technologies, Inc.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/

package util

import (
"time"

"github.com/bxcodec/gotcha/cache"
)

const (
// DefaultLinodeAPITimeout is the default timeout for Linode API calls.
DefaultLinodeAPITimeout = 10 * time.Second
// DefaultLinodeAPIMaxIdleConns is the default max idle connections for Linode API calls.
DefaultLinodeAPIMaxIdleConns = 100
// DefaultLinodeAPICacheControlOverride is the default cache control override for Linode API calls.
DefaultLinodeAPICacheControlOverride = "max-age=60"
// DefaultLinodeAPICacheExpiration is the default cache expiration for Linode API calls.
DefaultLinodeAPICacheExpiration = 5 * time.Second
// DefaultLinodeAPICacheMaxSizeItem is the default cache max size item for Linode API calls.
DefaultLinodeAPICacheMaxSizeItem = 500
// DefaultLinodeAPICacheMaxMemory is the default cache max memory for Linode API calls.
DefaultLinodeAPICacheMaxMemory = 1 * cache.MB
)

0 comments on commit 114d9ad

Please sign in to comment.