Skip to content

Commit

Permalink
Add caching for GitHub App tokens
Browse files Browse the repository at this point in the history
Signed-off-by: Matheus Pimenta <[email protected]>
  • Loading branch information
matheuscscp committed Mar 5, 2025
1 parent c0bff9c commit 407e841
Show file tree
Hide file tree
Showing 8 changed files with 120 additions and 13 deletions.
21 changes: 21 additions & 0 deletions cmd/operator/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -7,10 +7,12 @@ import (
"crypto/fips140"
"errors"
"os"
"time"

"github.com/fluxcd/cli-utils/pkg/kstatus/polling"
"github.com/fluxcd/cli-utils/pkg/kstatus/polling/clusterreader"
"github.com/fluxcd/cli-utils/pkg/kstatus/polling/engine"
"github.com/fluxcd/pkg/cache"
runtimeCtrl "github.com/fluxcd/pkg/runtime/controller"
"github.com/fluxcd/pkg/runtime/logger"
"github.com/fluxcd/pkg/runtime/pprof"
Expand Down Expand Up @@ -55,6 +57,8 @@ func init() {
func main() {
var (
concurrent int
tokenCacheMaxSize int
tokenCacheMaxDuration time.Duration
metricsAddr string
healthAddr string
enableLeaderElection bool
Expand All @@ -65,6 +69,10 @@ func main() {
)

flag.IntVar(&concurrent, "concurrent", 10, "The number of concurrent resource reconciles.")
flag.IntVar(&tokenCacheMaxSize, "token-cache-max-size", 100,
"The maximum size of the cache in number of tokens.")
flag.DurationVar(&tokenCacheMaxDuration, "token-cache-max-duration", cache.TokenMaxDuration,
"The maximum duration a token is cached.")
flag.StringVar(&metricsAddr, "metrics-addr", ":8080", "The address the metric endpoint binds to.")
flag.StringVar(&healthAddr, "health-addr", ":8081", "The address the health endpoint binds to.")
flag.StringVar(&storagePath, "storage-path", "/data", "The local storage path.")
Expand Down Expand Up @@ -158,6 +166,18 @@ func main() {
os.Exit(1)
}

var tokenCache *cache.TokenCache
if tokenCacheMaxSize > 0 {
tokenCache, err = cache.NewTokenCache(tokenCacheMaxSize,
cache.WithMaxDuration(tokenCacheMaxDuration),
cache.WithMetricsRegisterer(reporter.Registerer()),
cache.WithMetricsPrefix("flux_token_"))
if err != nil {
setupLog.Error(err, "unable to create token cache")
os.Exit(1)
}
}

if err = (&controller.EntitlementReconciler{
Client: mgr.GetClient(),
Scheme: mgr.GetScheme(),
Expand Down Expand Up @@ -235,6 +255,7 @@ func main() {
Client: mgr.GetClient(),
StatusManager: controllerName,
EventRecorder: mgr.GetEventRecorderFor(controllerName),
TokenCache: tokenCache,
}).SetupWithManager(mgr,
controller.ResourceSetInputProviderReconcilerOptions{
RateLimiter: runtimeCtrl.GetRateLimiter(rateLimiterOptions),
Expand Down
7 changes: 4 additions & 3 deletions go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,8 @@ require (
github.com/fluxcd/cli-utils v0.36.0-flux.12
github.com/fluxcd/pkg/apis/kustomize v1.9.0
github.com/fluxcd/pkg/apis/meta v1.10.0
github.com/fluxcd/pkg/auth v0.3.0
github.com/fluxcd/pkg/auth v0.5.0
github.com/fluxcd/pkg/cache v0.5.0
github.com/fluxcd/pkg/kustomize v1.16.0
github.com/fluxcd/pkg/runtime v0.54.0
github.com/fluxcd/pkg/ssa v0.45.1
Expand All @@ -20,7 +21,7 @@ require (
github.com/golang-jwt/jwt/v4 v4.5.1
github.com/google/go-containerregistry v0.20.3
github.com/google/go-containerregistry/pkg/authn/k8schain v0.0.0-20250115185438-c4dd792fa06c
github.com/google/go-github/v68 v68.0.0
github.com/google/go-github/v69 v69.2.0
github.com/gosimple/slug v1.15.0
github.com/hashicorp/go-retryablehttp v0.7.7
github.com/olekukonko/tablewriter v0.0.5
Expand Down Expand Up @@ -75,7 +76,7 @@ require (
github.com/awslabs/amazon-ecr-credential-helper/ecr-login v0.0.0-20250115170608-608f37feb051 // indirect
github.com/beorn7/perks v1.0.1 // indirect
github.com/blang/semver/v4 v4.0.0 // indirect
github.com/bradleyfalzon/ghinstallation/v2 v2.13.0 // indirect
github.com/bradleyfalzon/ghinstallation/v2 v2.14.0 // indirect
github.com/cespare/xxhash/v2 v2.3.0 // indirect
github.com/chai2010/gettext-go v1.0.2 // indirect
github.com/chrismellard/docker-credential-acr-env v0.0.0-20230304212654-82a0ddb27589 // indirect
Expand Down
18 changes: 10 additions & 8 deletions go.sum
Original file line number Diff line number Diff line change
Expand Up @@ -76,8 +76,8 @@ 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/bradleyfalzon/ghinstallation/v2 v2.13.0 h1:5FhjW93/YLQJDmPdeyMPw7IjAPzqsr+0jHPfrPz0sZI=
github.com/bradleyfalzon/ghinstallation/v2 v2.13.0/go.mod h1:EJ6fgedVEHa2kUyBTTvslJCXJafS/mhJNNKEOCspZXQ=
github.com/bradleyfalzon/ghinstallation/v2 v2.14.0 h1:0D4vKCHOvYrDU8u61TnE2JfNT4VRrBLphmxtqazTO+M=
github.com/bradleyfalzon/ghinstallation/v2 v2.14.0/go.mod h1:LOVmdZYVZ8jqdr4n9wWm1ocDiMz9IfMGfRkaYC1a52A=
github.com/cespare/xxhash/v2 v2.3.0 h1:UL815xU9SqsFlibzuggzjXhog7bL6oX9BbNZnL2UFvs=
github.com/cespare/xxhash/v2 v2.3.0/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs=
github.com/chai2010/gettext-go v1.0.2 h1:1Lwwip6Q2QGsAdl/ZKPCwTe9fe0CjlUbqj5bFNSjIRk=
Expand Down Expand Up @@ -121,8 +121,10 @@ github.com/fluxcd/pkg/apis/kustomize v1.9.0 h1:SJpT1CK58AnTvCpDKeGfMNA0Xud/4VReZ
github.com/fluxcd/pkg/apis/kustomize v1.9.0/go.mod h1:AZl2GU03oPVue6SUivdiIYd/3mvF94j7t1G2JO26d4s=
github.com/fluxcd/pkg/apis/meta v1.10.0 h1:rqbAuyl5ug7A5jjRf/rNwBXmNl6tJ9wG2iIsriwnQUk=
github.com/fluxcd/pkg/apis/meta v1.10.0/go.mod h1:n7NstXHDaleAUMajcXTVkhz0MYkvEXy1C/eLI/t1xoI=
github.com/fluxcd/pkg/auth v0.3.0 h1:I1A3e81O+bpAgEcJ3e+rXqObKPjzBu6FLYXQTSxXLOs=
github.com/fluxcd/pkg/auth v0.3.0/go.mod h1:g9KJ4iNcCd6Sb7al4yN1+olgOfgwmU4lgCWbwvMsFRE=
github.com/fluxcd/pkg/auth v0.5.0 h1:+71QIqaDAP/J/ej9qfkjQ1Nw3YrPTgp080FBBx5XwOw=
github.com/fluxcd/pkg/auth v0.5.0/go.mod h1:DQbtz4abrw+bt7VJUNcVuKR1DO8AjHnbFwNnI4kRg/M=
github.com/fluxcd/pkg/cache v0.5.0 h1:2BLdxSeqRNXHAkKXm2GF5hxa1eGvmaRRUnIllKCJxw8=
github.com/fluxcd/pkg/cache v0.5.0/go.mod h1:VNMLzJa62iDHfojoywykJ2pdUlgSjVzTLrOgkNlPxEo=
github.com/fluxcd/pkg/envsubst v1.3.0 h1:84Ain+8EBvyzu6y0FsKRwNsvaSiKuqhTqeh/4yoGFFU=
github.com/fluxcd/pkg/envsubst v1.3.0/go.mod h1:lz6HvqDnxbX0sIqjr1fxw0oTGYACLVFcOE/srKS0VQQ=
github.com/fluxcd/pkg/kustomize v1.16.0 h1:UBOeIvkrC6y4owYs7vZwG5PUVFeqnRoDFN9eaNhuNPI=
Expand All @@ -133,8 +135,8 @@ github.com/fluxcd/pkg/sourceignore v0.11.0 h1:xzpYmc5/t/Ck+/DkJSX3r+VbahDRIAn5kb
github.com/fluxcd/pkg/sourceignore v0.11.0/go.mod h1:ri2FvlzX8ep2iszOK5gF/riYq2TNgpVvsfJ2QY0dLWI=
github.com/fluxcd/pkg/ssa v0.45.1 h1:ISl84TJwRP/GuZXrKiR9Tf8JOnG5XFgtjcYoR4XQYf4=
github.com/fluxcd/pkg/ssa v0.45.1/go.mod h1:8Anf7XVZ0zxOve7HXbDaW1s0gfmP95ksJBlKfDYinhQ=
github.com/fluxcd/pkg/ssh v0.16.0 h1:dhSWNp30p05EJ86bhICezad9pG3fJi4CAVKnZ3EmUV8=
github.com/fluxcd/pkg/ssh v0.16.0/go.mod h1:MyDegNZHnKNDAwM5/A2t/1FjpvpS8BsRZQ4WqEwCHc0=
github.com/fluxcd/pkg/ssh v0.17.0 h1:o+MgdM/OB8R/+KEc3W3ml/inEKZqCwT8V71dkbTAbm4=
github.com/fluxcd/pkg/ssh v0.17.0/go.mod h1:4yU099LjFWOJXZiu73rvqA70mOoSXG2yqxfPBxhnGgQ=
github.com/fluxcd/pkg/tar v0.11.0 h1:pjf/rzr6HNAPiuxT59mtba9tfBtdNiSQ/UqduG8vZ2I=
github.com/fluxcd/pkg/tar v0.11.0/go.mod h1:+kiP25NqibWMpFWgizyPEMqnMJIux7bCgEy+4pfxyI4=
github.com/fsnotify/fsnotify v1.8.0 h1:dAwr6QBTBZIkG8roQaJjGof0pp0EeF+tNV7YBP3F/8M=
Expand Down Expand Up @@ -186,8 +188,8 @@ github.com/google/go-containerregistry/pkg/authn/k8schain v0.0.0-20250115185438-
github.com/google/go-containerregistry/pkg/authn/k8schain v0.0.0-20250115185438-c4dd792fa06c/go.mod h1:8mk2eu7HGqCp+JSWQVFCnKQwk/K6cIY6ID9aX72iTRo=
github.com/google/go-containerregistry/pkg/authn/kubernetes v0.0.0-20241111191718-6bce25ecf029 h1:tmtax9EjrCFrrw72NeGso7qZUnJXTIP368kcjE4lZwE=
github.com/google/go-containerregistry/pkg/authn/kubernetes v0.0.0-20241111191718-6bce25ecf029/go.mod h1:zD6WJVa49IK5fhrZOUaq7UcSgxZFlnS80EJBrcVFkFI=
github.com/google/go-github/v68 v68.0.0 h1:ZW57zeNZiXTdQ16qrDiZ0k6XucrxZ2CGmoTvcCyQG6s=
github.com/google/go-github/v68 v68.0.0/go.mod h1:K9HAUBovM2sLwM408A18h+wd9vqdLOEqTUCbnRIcx68=
github.com/google/go-github/v69 v69.2.0 h1:wR+Wi/fN2zdUx9YxSmYE0ktiX9IAR/BeePzeaUUbEHE=
github.com/google/go-github/v69 v69.2.0/go.mod h1:xne4jymxLR6Uj9b7J7PyTpkMYstEMMwGZa0Aehh1azM=
github.com/google/go-querystring v1.1.0 h1:AnCroh3fv4ZBgVIf1Iwtovgjaw/GiKJo8M8yD/fhyJ8=
github.com/google/go-querystring v1.1.0/go.mod h1:Kcdr2DB4koayq7X8pmAG4sNG59So17icRSOU623lUBU=
github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg=
Expand Down
13 changes: 12 additions & 1 deletion internal/controller/resourcesetinputprovider_controller.go
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ import (

"github.com/fluxcd/pkg/apis/meta"
"github.com/fluxcd/pkg/auth/github"
"github.com/fluxcd/pkg/cache"
"github.com/fluxcd/pkg/runtime/conditions"
"github.com/fluxcd/pkg/runtime/patch"
"github.com/opencontainers/go-digest"
Expand All @@ -40,6 +41,7 @@ type ResourceSetInputProviderReconciler struct {
kuberecorder.EventRecorder

StatusManager string
TokenCache *cache.TokenCache
}

// +kubebuilder:rbac:groups=fluxcd.controlplane.io,resources=resourcesetinputproviders,verbs=get;list;watch;create;update;patch;delete
Expand Down Expand Up @@ -357,7 +359,16 @@ func (r *ResourceSetInputProviderReconciler) getGitHubToken(
return password, err
}

ghc, err := github.New(github.WithAppData(authData))
opts := []github.OptFunc{github.WithAppData(authData)}

if r.TokenCache != nil {
opts = append(opts, github.WithCache(r.TokenCache,
fluxcdv1.ResourceSetInputProviderKind,
obj.GetName(),
obj.GetNamespace()))
}

ghc, err := github.New(opts...)
if err != nil {
return "", err
}
Expand Down
40 changes: 40 additions & 0 deletions internal/controller/resourcesetinputprovider_controller_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -6,9 +6,13 @@ package controller
import (
"context"
"fmt"
"os"
"testing"
"time"

"github.com/fluxcd/pkg/apis/meta"
"github.com/fluxcd/pkg/auth/github"
"github.com/fluxcd/pkg/cache"
"github.com/fluxcd/pkg/runtime/conditions"
. "github.com/onsi/gomega"
corev1 "k8s.io/api/core/v1"
Expand Down Expand Up @@ -495,6 +499,42 @@ spec:
g.Expect(apierrors.IsNotFound(err)).To(BeTrue())
}

func TestResourceSetInputProviderReconciler_getGitHubToken_cached(t *testing.T) {
const key = "githubAppID=123,githubAppInstallationID=123456,githubAppBaseURL=https://github.com,githubAppPrivateKeyDigest=9d5b1bf1d595f2da0c8e9941bc84a82dabbad433fc95fea56aa596eda99e550b"

g := NewWithT(t)

ctx := context.Background()

tokenCache, err := cache.NewTokenCache(1)
g.Expect(err).NotTo(HaveOccurred())

r := &ResourceSetInputProviderReconciler{
TokenCache: tokenCache,
}

_, _, err = r.TokenCache.GetOrSet(ctx, key, func(context.Context) (cache.Token, error) {
return &github.AppToken{
Token: "my-gh-app-token",
ExpiresAt: time.Now().Add(time.Hour),
}, nil
})
g.Expect(err).NotTo(HaveOccurred())

privateKeyPEM, err := os.ReadFile("testdata/rsa-private-key.pem")
g.Expect(err).NotTo(HaveOccurred())

token, err := r.getGitHubToken(ctx, &fluxcdv1.ResourceSetInputProvider{}, map[string][]byte{
"githubAppID": []byte("123"),
"githubAppInstallationID": []byte("123456"),
"githubAppBaseURL": []byte("https://github.com"),
"githubAppPrivateKey": privateKeyPEM,
})

g.Expect(err).NotTo(HaveOccurred())
g.Expect(token).To(Equal("my-gh-app-token"))
}

func getResourceSetInputProviderReconciler() *ResourceSetInputProviderReconciler {
return &ResourceSetInputProviderReconciler{
Client: testClient,
Expand Down
27 changes: 27 additions & 0 deletions internal/controller/testdata/rsa-private-key.pem
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
-----BEGIN RSA PRIVATE KEY-----
MIIEpAIBAAKCAQEAq3CARSLLsDLQ53YVVo+MFGPLFdlr6bNOVxHb4dwV4+AXiYR5
00Bd7VFR6THUYucI1LjLAMt5pWuG9wvCo4r4E2HyVsjZcEwUI3XDLbX+73J8xt/w
e1rD4vKEz3zehlgJsm5x1GzrTjz6Vwa8WOO5A7r0M3aUluFsD769oykracs+wsXY
MUfnEghUgATb6/1givxkdWpL+n6Q5jkE68JNvCYrFT82oAj5hF253hjUWdcjR2Hl
+c6ocBLqTPfSk1WqIkWAk1oMGPOBUDZ2nKfVyWkXc6LwMYMHoyAh6yvcXd6UBpmP
psVWPn4Up2iH9qkuEvmJhYac1d3WfM7v3uCMtQIDAQABAoIBAAbGkf3pg6UJmnG4
xtwecpoFswDrOxVyoD3XDK/ZsRsfVV7P17luhFJ8/HXxIbns8/o6+XmlERWQG+FH
W4siKBmAAqzFEGRHUCPHTjj6xezJw2kKxb2NAfaba34AgJJNCB5qDgv9aAlgu0DV
mSZm4v46P9eJhWg3LZGrvLamSbsgcX/tFrCqQsPdYfKQk+Eenwm5n42TbADaC27I
5ChHBA/B0yY/HhSn2HKTVvMHsnmnHpDQ/RSihAztvtzhVK+jCjnS5EX1YYY9v7pv
t4koI39Qt6IP0s5gt4gtXFhJlS2Nza6JtaHZUmjVeQxKi5pe7Iqe9hwgaS3VqCWx
JSv/JzsCgYEAwgHR8HQkLa/KV8awtDDKSpeyYvv5xlCyWHTw8H1zU49pAVN/Xh46
DIwmkZH3OKBVpOr6wVKdniueeb3DO8dvIJ8hpw/f+mKBGJLsGKHfZ7/x1kT/q13h
C74OqFRLew4dFH0bqPkqvE568X6/C/CWnmbiTggqcn0tVjr0peeR6MsCgYEA4jiZ
xbsaD8lQE36E62Ol2dc4aySCb7pMaixtXOjyL9C8BO8BjiwgF5u+Otu9s6QhryBF
0BqOShZq8GxlcKpsJKXWCkmC2crx2Aayl3s3+7ScZ8Qp2KGrUPxrpL/RZL2lZ9lH
dX1LYC+TM5a9KcSf1qodH4slP6d2Aj2bWCKIMH8CgYEArKbYAWgqZioiJXlh+gnN
jRJxI1vgzdc00DnJzgumnX9r0E1RdR3rRQ1YqYXAADnX3ftsCq2OLZvd3bO90i5K
vDpBxZ4AEqClCIx/5e/wlDEidDBVY1kZlMyf2LejsLA/uuMXwYl0ub4R9WZ5eJO0
RuWCkjT8KYUy2qF+5UIu/H8CgYAQWCSMC7ObVmEpt2dlFmMCNTGHVDD0X5JrzV/t
aYst9zfOZ3JGUlvTONZqrDutgftJCtzgZzrGkY4SZtKBbF652x12ys7ga3BDumAm
36kwz2DJgnu/gha9mC8yzQUU8TrFIQavr2jFv0o0XPy3ytP9j3bhM41yZuf4y3iw
ynXqgwKBgQCQR7RK06eFAydcAFY1q/1Ozl/fruMox5ojBVAmlu/5C0FQK5UPLvoK
UKbj5fA/oWwE3X3yD+9iF4YjgZ7Tr3pBirA7rqsYrH9e2zaiiyAYQwLlbnAOrdjC
eOLmLSnyvUFp4E7RRYVoHmPzxAY+nfkODwtO7FGlfihB+5kPbKPQDA==
-----END RSA PRIVATE KEY-----
2 changes: 1 addition & 1 deletion internal/gitprovider/github.go
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ import (
"net/url"
"strings"

"github.com/google/go-github/v68/github"
"github.com/google/go-github/v69/github"
"golang.org/x/oauth2"
)

Expand Down
5 changes: 5 additions & 0 deletions internal/reporter/metrics.go
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,11 @@ import (
fluxcdv1 "github.com/controlplaneio-fluxcd/flux-operator/api/v1"
)

// Registerer returns the metrics registerer.
func Registerer() prometheus.Registerer {
return crtlmetrics.Registry
}

// MustRegisterMetrics attempts to register the metrics collectors
// in the controller-runtime metrics registry.
func MustRegisterMetrics() {
Expand Down

0 comments on commit 407e841

Please sign in to comment.