Add metrics for GitHub API rate limit (#312)
This commit is contained in:
		
							parent
							
								
									2623140c9a
								
							
						
					
					
						commit
						4f3f2fb60d
					
				|  | @ -11,6 +11,7 @@ import ( | |||
| 
 | ||||
| 	"github.com/bradleyfalzon/ghinstallation" | ||||
| 	"github.com/google/go-github/v33/github" | ||||
| 	"github.com/summerwind/actions-runner-controller/github/metrics" | ||||
| 	"golang.org/x/oauth2" | ||||
| ) | ||||
| 
 | ||||
|  | @ -34,15 +35,9 @@ type Client struct { | |||
| 
 | ||||
| // NewClient creates a Github Client
 | ||||
| func (c *Config) NewClient() (*Client, error) { | ||||
| 	var ( | ||||
| 		httpClient *http.Client | ||||
| 		client     *github.Client | ||||
| 	) | ||||
| 	githubBaseURL := "https://github.com/" | ||||
| 	var transport http.RoundTripper | ||||
| 	if len(c.Token) > 0 { | ||||
| 		httpClient = oauth2.NewClient(context.Background(), oauth2.StaticTokenSource( | ||||
| 			&oauth2.Token{AccessToken: c.Token}, | ||||
| 		)) | ||||
| 		transport = oauth2.NewClient(context.Background(), oauth2.StaticTokenSource(&oauth2.Token{AccessToken: c.Token})).Transport | ||||
| 	} else { | ||||
| 		tr, err := ghinstallation.NewKeyFromFile(http.DefaultTransport, c.AppID, c.AppInstallationID, c.AppPrivateKey) | ||||
| 		if err != nil { | ||||
|  | @ -55,9 +50,13 @@ func (c *Config) NewClient() (*Client, error) { | |||
| 			} | ||||
| 			tr.BaseURL = githubAPIURL | ||||
| 		} | ||||
| 		httpClient = &http.Client{Transport: tr} | ||||
| 		transport = tr | ||||
| 	} | ||||
| 	transport = metrics.Transport{Transport: transport} | ||||
| 	httpClient := &http.Client{Transport: transport} | ||||
| 
 | ||||
| 	var client *github.Client | ||||
| 	var githubBaseURL string | ||||
| 	if len(c.EnterpriseURL) > 0 { | ||||
| 		var err error | ||||
| 		client, err = github.NewEnterpriseClient(c.EnterpriseURL, c.EnterpriseURL, httpClient) | ||||
|  | @ -67,6 +66,7 @@ func (c *Config) NewClient() (*Client, error) { | |||
| 		githubBaseURL = fmt.Sprintf("%s://%s%s", client.BaseURL.Scheme, client.BaseURL.Host, strings.TrimSuffix(client.BaseURL.Path, "api/v3/")) | ||||
| 	} else { | ||||
| 		client = github.NewClient(httpClient) | ||||
| 		githubBaseURL = "https://github.com/" | ||||
| 	} | ||||
| 
 | ||||
| 	return &Client{ | ||||
|  |  | |||
|  | @ -0,0 +1,63 @@ | |||
| // Package metrics provides monitoring of the GitHub related metrics.
 | ||||
| //
 | ||||
| // This depends on the metrics exporter of kubebuilder.
 | ||||
| // See https://book.kubebuilder.io/reference/metrics.html for details.
 | ||||
| package metrics | ||||
| 
 | ||||
| import ( | ||||
| 	"net/http" | ||||
| 	"strconv" | ||||
| 
 | ||||
| 	"github.com/prometheus/client_golang/prometheus" | ||||
| 	"sigs.k8s.io/controller-runtime/pkg/metrics" | ||||
| ) | ||||
| 
 | ||||
| func init() { | ||||
| 	metrics.Registry.MustRegister(metricRateLimit, metricRateLimitRemaining) | ||||
| } | ||||
| 
 | ||||
| var ( | ||||
| 	// https://docs.github.com/en/rest/overview/resources-in-the-rest-api#rate-limiting
 | ||||
| 	metricRateLimit = prometheus.NewGauge( | ||||
| 		prometheus.GaugeOpts{ | ||||
| 			Name: "github_rate_limit", | ||||
| 			Help: "The maximum number of requests you're permitted to make per hour", | ||||
| 		}, | ||||
| 	) | ||||
| 	metricRateLimitRemaining = prometheus.NewGauge( | ||||
| 		prometheus.GaugeOpts{ | ||||
| 			Name: "github_rate_limit_remaining", | ||||
| 			Help: "The number of requests remaining in the current rate limit window", | ||||
| 		}, | ||||
| 	) | ||||
| ) | ||||
| 
 | ||||
| const ( | ||||
| 	// https://docs.github.com/en/rest/overview/resources-in-the-rest-api#rate-limiting
 | ||||
| 	headerRateLimit          = "X-RateLimit-Limit" | ||||
| 	headerRateLimitRemaining = "X-RateLimit-Remaining" | ||||
| ) | ||||
| 
 | ||||
| // Transport wraps a transport with metrics monitoring
 | ||||
| type Transport struct { | ||||
| 	Transport http.RoundTripper | ||||
| } | ||||
| 
 | ||||
| func (t Transport) RoundTrip(req *http.Request) (*http.Response, error) { | ||||
| 	resp, err := t.Transport.RoundTrip(req) | ||||
| 	if resp != nil { | ||||
| 		parseResponse(resp) | ||||
| 	} | ||||
| 	return resp, err | ||||
| } | ||||
| 
 | ||||
| func parseResponse(resp *http.Response) { | ||||
| 	rateLimit, err := strconv.Atoi(resp.Header.Get(headerRateLimit)) | ||||
| 	if err == nil { | ||||
| 		metricRateLimit.Set(float64(rateLimit)) | ||||
| 	} | ||||
| 	rateLimitRemaining, err := strconv.Atoi(resp.Header.Get(headerRateLimitRemaining)) | ||||
| 	if err == nil { | ||||
| 		metricRateLimitRemaining.Set(float64(rateLimitRemaining)) | ||||
| 	} | ||||
| } | ||||
							
								
								
									
										1
									
								
								go.mod
								
								
								
								
							
							
						
						
									
										1
									
								
								go.mod
								
								
								
								
							|  | @ -11,6 +11,7 @@ require ( | |||
| 	github.com/kelseyhightower/envconfig v1.4.0 | ||||
| 	github.com/onsi/ginkgo v1.8.0 | ||||
| 	github.com/onsi/gomega v1.5.0 | ||||
| 	github.com/prometheus/client_golang v0.9.2 | ||||
| 	github.com/stretchr/testify v1.4.0 // indirect | ||||
| 	golang.org/x/oauth2 v0.0.0-20190604053449-0f29369cfe45 | ||||
| 	k8s.io/api v0.0.0-20190918155943-95b840bb6a1f | ||||
|  |  | |||
		Loading…
	
		Reference in New Issue