Skip to content

Commit cceca89

Browse files
re-add rate limit threshold
1 parent f6b3669 commit cceca89

File tree

4 files changed

+65
-1
lines changed

4 files changed

+65
-1
lines changed

README.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@ This exporter is configured via environment variables. All variables are optiona
1818
| `GITHUB_APP_INSTALLATION_ID` | The Installation ID of the GitHub App. Required if `GITHUB_APP` is `true`. | |
1919
| `GITHUB_APP_KEY_PATH` | Path to the GitHub App private key file. Required if `GITHUB_APP` is `true`. | |
2020
| `GITHUB_RATE_LIMIT_ENABLED` | Whether to fetch GitHub API rate limit metrics (`true` or `false`). | `true` |
21+
| `GITHUB_RATE_LIMIT` | Core API quota threshold for proactive GitHub App token refresh. When the remaining `core` requests drop below this value, a new installation token is requested. `0` disables this behaviour. | `0` |
2122
| `GITHUB_RESULTS_PER_PAGE` | Number of results to request per page from the GitHub API (max 100). | `100` |
2223
| `FETCH_REPO_RELEASES_ENABLED` | Whether to fetch repository release metrics (`true` or `false`). | `true` |
2324
| `FETCH_ORGS_CONCURRENCY` | Number of concurrent requests to make when fetching organization data. | `1` |

config/config.go

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,7 @@ type Config struct {
2828
GithubTokenFile string `envconfig:"GITHUB_TOKEN_FILE" required:"false"`
2929
GitHubApp bool `envconfig:"GITHUB_APP" required:"false" default:"false"`
3030
GitHubRateLimitEnabled bool `envconfig:"GITHUB_RATE_LIMIT_ENABLED" required:"false" default:"true"`
31+
GitHubRateLimit float64 `envconfig:"GITHUB_RATE_LIMIT" required:"false" default:"0"`
3132
FetchRepoReleasesEnabled bool `envconfig:"FETCH_REPO_RELEASES_ENABLED" required:"false" default:"true"`
3233
FetchOrgsConcurrency int `envconfig:"FETCH_ORGS_CONCURRENCY" required:"false" default:"1"`
3334
FetchOrgReposConcurrency int `envconfig:"FETCH_ORG_REPOS_CONCURRENCY" required:"false" default:"1"`
@@ -88,7 +89,7 @@ func (c *Config) GetClient() (*github.Client, error) {
8889

8990
// Add custom transport for GitHub App authentication if enabled
9091
if c.GitHubApp {
91-
itr, err := ghinstallation.NewKeyFromFile(http.DefaultTransport, c.GitHubAppId, c.GitHubAppInstallationId, c.GitHubAppKeyPath)
92+
itr, err := ghinstallation.NewKeyFromFile(transport, c.GitHubAppId, c.GitHubAppInstallationId, c.GitHubAppKeyPath)
9293
if err != nil {
9394
return nil, fmt.Errorf("creating GitHub App installation transport: %v", err)
9495
}

config/config_test.go

Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,7 @@ func TestConfig(t *testing.T) {
3434
GitHubApp: false,
3535
GitHubAppConfig: nil,
3636
GitHubRateLimitEnabled: true,
37+
GitHubRateLimit: 0,
3738
FetchRepoReleasesEnabled: true,
3839
FetchOrgsConcurrency: 1,
3940
FetchOrgReposConcurrency: 1,
@@ -87,6 +88,7 @@ func TestConfig(t *testing.T) {
8788
GitHubApp: false,
8889
GitHubAppConfig: nil,
8990
GitHubRateLimitEnabled: false,
91+
GitHubRateLimit: 0,
9092
FetchRepoReleasesEnabled: false,
9193
FetchOrgsConcurrency: 2,
9294
FetchOrgReposConcurrency: 3,
@@ -95,6 +97,37 @@ func TestConfig(t *testing.T) {
9597
},
9698
expectedErr: nil,
9799
},
100+
{
101+
name: "github rate limit threshold config",
102+
envVars: map[string]string{
103+
"GITHUB_RATE_LIMIT": "15000",
104+
},
105+
expectedCfg: &Config{
106+
MetricsPath: "/metrics",
107+
ListenPort: "9171",
108+
LogLevel: "INFO",
109+
ApiUrl: &url.URL{
110+
Scheme: "https",
111+
Host: "api.github.com",
112+
},
113+
Repositories: []string{},
114+
Organisations: []string{},
115+
Users: []string{},
116+
GitHubResultsPerPage: 100,
117+
GithubToken: "",
118+
GithubTokenFile: "",
119+
GitHubApp: false,
120+
GitHubAppConfig: nil,
121+
GitHubRateLimitEnabled: true,
122+
GitHubRateLimit: 15000,
123+
FetchRepoReleasesEnabled: true,
124+
FetchOrgsConcurrency: 1,
125+
FetchOrgReposConcurrency: 1,
126+
FetchReposConcurrency: 1,
127+
FetchUsersConcurrency: 1,
128+
},
129+
expectedErr: nil,
130+
},
98131
{
99132
name: "invalid url",
100133
expectedCfg: nil,

exporter/prometheus.go

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -57,6 +57,9 @@ func (e *Exporter) Collect(ch chan<- prometheus.Metric) {
5757
return
5858
}
5959

60+
// Refresh client if rate limit threshold enabled
61+
e.rateLimitRefresh(r)
62+
6063
// Set prometheus gauge metrics using the data gathered
6164
err = e.processMetrics(data, r, ch)
6265
if err != nil {
@@ -107,6 +110,32 @@ func (e *Exporter) getRateLimits(ctx context.Context) (*[]RateLimit, error) {
107110
return &rls, nil
108111
}
109112

113+
func (e *Exporter) rateLimitRefresh(rates *[]RateLimit) {
114+
if e.GitHubRateLimit <= 0 || rates == nil {
115+
return
116+
}
117+
118+
for _, rl := range *rates {
119+
if rl.Resource == "core" && rl.Remaining <= e.GitHubRateLimit {
120+
log.Infof("GitHub API core rate limit is low (%.0f remaining, threshold: %.0f)", rl.Remaining, e.GitHubRateLimit)
121+
if !e.GitHubApp {
122+
log.Warn("GITHUB_RATE_LIMIT threshold breached but GitHub App auth is not configured; cannot refresh token automatically")
123+
return
124+
}
125+
126+
log.Info("Refreshing GitHub App installation token due to low rate limit")
127+
newClient, err := e.GetClient()
128+
if err != nil {
129+
log.Errorf("refreshing GitHub App client after low rate limit: %v", err)
130+
return
131+
}
132+
133+
e.Client = newClient
134+
return
135+
}
136+
}
137+
}
138+
110139
// getRepoMetrics fetches metrics for the configured repositories
111140
func (e *Exporter) getRepoMetrics(ctx context.Context) ([]*Datum, error) {
112141
var data []*Datum

0 commit comments

Comments
 (0)