diff --git a/github/github.go b/github/github.go index a716f763..3e92e023 100644 --- a/github/github.go +++ b/github/github.go @@ -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{ diff --git a/github/metrics/transport.go b/github/metrics/transport.go new file mode 100644 index 00000000..321d1264 --- /dev/null +++ b/github/metrics/transport.go @@ -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)) + } +} diff --git a/go.mod b/go.mod index 0ef66dab..bcb81699 100644 --- a/go.mod +++ b/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