Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add caching for GitHub App tokens #202

Merged
merged 1 commit into from
Mar 6, 2025
Merged
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
22 changes: 22 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,19 @@ 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_"),
cache.WithEventNamespaceLabel("exported_namespace"))
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 +256,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.6.0
github.com/fluxcd/pkg/cache v0.6.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.6.0 h1:plHUxZ/+IJn7lm1jEnxwr1hvIgMKIepRmheOHZz3ltA=
github.com/fluxcd/pkg/auth v0.6.0/go.mod h1:mdlybbiiRJ5AoRYx1jM7jFoEUYxzB2Ma406fYYt01qA=
github.com/fluxcd/pkg/cache v0.6.0 h1:l6tly7YGYkCaMj0Sfn9m+jiLir9CDksI+QDOE/BwK0c=
github.com/fluxcd/pkg/cache v0.6.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
Loading