diff --git a/METRICS.md b/METRICS.md index a0e4a1d..3e1cdeb 100644 --- a/METRICS.md +++ b/METRICS.md @@ -33,6 +33,9 @@ github_repo_open_issues{archived="false",fork="false",language="Go",license="mit # HELP github_repo_pull_request_count Total number of pull requests for given repository # TYPE github_repo_pull_request_count gauge github_repo_pull_request_count{repo="github-exporter",user="githubexporter"} 7 +# HELP github_repo_releases Total number of releases for given repository +# TYPE github_repo_releases gauge +github_repo_releases{repo="github-exporter",user="githubexporter"} 20 # HELP github_repo_size_kb Size in KB for given repository # TYPE github_repo_size_kb gauge github_repo_size_kb{archived="false",fork="false",language="Go",license="mit",private="false",repo="github-exporter",user="githubexporter"} 2185 diff --git a/exporter/metrics.go b/exporter/metrics.go index 590df9d..e2c812e 100644 --- a/exporter/metrics.go +++ b/exporter/metrics.go @@ -57,6 +57,11 @@ func AddMetrics(cfg *config.Config) map[string]*prometheus.Desc { "Size in KB for given repository", []string{"repo", "user", "private", "fork", "archived", "license", "language"}, nil, ) + APIMetrics["Releases"] = prometheus.NewDesc( + prometheus.BuildFQName("github", "repo", "releases"), + "Total number of releases for given repository", + []string{"repo", "user"}, nil, + ) APIMetrics["ReleaseDownloads"] = prometheus.NewDesc( prometheus.BuildFQName("github", "repo", "release_downloads"), "Download count for a given release", @@ -102,6 +107,12 @@ func (e *Exporter) processMetrics(data []*Datum, rates *[]RateLimit, ch chan<- p ch <- prometheus.MustNewConstMetric(e.APIMetrics["Watchers"], prometheus.GaugeValue, float64(x.Watchers), x.Name, x.Owner.Login, strconv.FormatBool(x.Private), strconv.FormatBool(x.Fork), strconv.FormatBool(x.Archived), x.License.Key, x.Language) ch <- prometheus.MustNewConstMetric(e.APIMetrics["Size"], prometheus.GaugeValue, float64(x.Size), x.Name, x.Owner.Login, strconv.FormatBool(x.Private), strconv.FormatBool(x.Fork), strconv.FormatBool(x.Archived), x.License.Key, x.Language) + // Only report a release count when releases were fetched, so a + // disabled fetch isn't reported as zero releases + if e.FetchRepoReleasesEnabled { + ch <- prometheus.MustNewConstMetric(e.APIMetrics["Releases"], prometheus.GaugeValue, float64(len(x.Releases)), x.Name, x.Owner.Login) + } + for _, release := range x.Releases { for _, asset := range release.Assets { ch <- prometheus.MustNewConstMetric(e.APIMetrics["ReleaseDownloads"], prometheus.GaugeValue, float64(asset.Downloads), x.Name, x.Owner.Login, release.Name, asset.Name, release.Tag, asset.CreatedAt) diff --git a/test/github_exporter_test.go b/test/github_exporter_test.go index d540da1..7352a52 100644 --- a/test/github_exporter_test.go +++ b/test/github_exporter_test.go @@ -63,6 +63,7 @@ func TestGithubExporter(t *testing.T) { Assert(bodyContains(`github_rate_reset{resource="search"} 3e+09`)). Assert(bodyContains(`github_repo_forks{archived="false",fork="false",language="Go",license="mit",private="false",repo="myRepo",user="myOrg"} 10`)). Assert(bodyContains(`github_repo_pull_request_count{repo="myRepo",user="myOrg"} 3`)). + Assert(bodyContains(`github_repo_releases{repo="myRepo",user="myOrg"} 2`)). Assert(bodyContains(`github_repo_open_issues{archived="false",fork="false",language="Go",license="mit",private="false",repo="myRepo",user="myOrg"} 2`)). Assert(bodyContains(`github_repo_size_kb{archived="false",fork="false",language="Go",license="mit",private="false",repo="myRepo",user="myOrg"} 946`)). Assert(bodyContains(`github_repo_stars{archived="false",fork="false",language="Go",license="mit",private="false",repo="myRepo",user="myOrg"} 120`)). @@ -73,6 +74,7 @@ func TestGithubExporter(t *testing.T) { Assert(bodyContains(`github_repo_release_downloads{created_at="2019-05-02T15:22:16Z",name="myRepo_2.0.0_windows_amd64.tar.gz",release="2.0.0",repo="myRepo",tag="2.0.0",user="myOrg"} 55`)). Assert(bodyContains(`github_repo_forks{archived="false",fork="false",language="Go",license="mit",private="false",repo="myRepo",user="myUser"} 10`)). Assert(bodyContains(`github_repo_pull_request_count{repo="myRepo",user="myUser"} 3`)). + Assert(bodyContains(`github_repo_releases{repo="myRepo",user="myUser"} 2`)). Assert(bodyContains(`github_repo_open_issues{archived="false",fork="false",language="Go",license="mit",private="false",repo="myRepo",user="myUser"} 2`)). Assert(bodyContains(`github_repo_size_kb{archived="false",fork="false",language="Go",license="mit",private="false",repo="myRepo",user="myUser"} 946`)). Assert(bodyContains(`github_repo_stars{archived="false",fork="false",language="Go",license="mit",private="false",repo="myRepo",user="myUser"} 120`)). @@ -85,6 +87,29 @@ func TestGithubExporter(t *testing.T) { End() } +func TestGithubExporterReleasesDisabled(t *testing.T) { + t.Setenv("FETCH_REPO_RELEASES_ENABLED", "false") + test, collector := apiTest(withConfig()) + defer prometheus.Unregister(&collector) + + test.Mocks( + githubRepos(), + githubPulls(), + githubUserRepos(), + githubUserPulls(), + githubOrgRepos(), + githubPulls(), + githubRateLimit(), + ). + Get("/metrics"). + Expect(t). + Assert(bodyContains(`github_repo_stars{archived="false",fork="false",language="Go",license="mit",private="false",repo="myRepo",user="myOrg"} 120`)). + Assert(bodyNotContains(`github_repo_releases`)). + Assert(bodyNotContains(`github_repo_release_downloads`)). + Status(http.StatusOK). + End() +} + func TestGithubExporterHttpErrorHandling(t *testing.T) { test, collector := apiTest(withConfig()) defer prometheus.Unregister(&collector) @@ -239,3 +264,17 @@ func bodyContains(substr string) func(*http.Response, *http.Request) error { return nil } } + +func bodyNotContains(substr string) func(*http.Response, *http.Request) error { + return func(res *http.Response, req *http.Request) error { + bytes, err := io.ReadAll(res.Body) + if err != nil { + panic(err) + } + response := string(bytes) + if strings.Contains(response, substr) { + return fmt.Errorf("response unexpectedly contained substring '%s'", substr) + } + return nil + } +}