Skip to content

Commit ccd1f33

Browse files
authored
Merge pull request #48 from DerekTBrown/fetch_metrics_for_orgs
feat: fetch all repositories
2 parents 967e2f3 + 627d88f commit ccd1f33

12 files changed

+384
-356
lines changed

README.md

+1-1
Original file line numberDiff line numberDiff line change
@@ -25,7 +25,7 @@ Authentication can either via a Github Token or the Github App Authentication 3
2525
| Github App Private Key | app_private_key, gpk | GITHUB_APP_PRIVATE_KEY | - | Github App Authentication Private Key |
2626
| Github Refresh | github_refresh, gr | GITHUB_REFRESH | 30 | Refresh time Github Actions status in sec |
2727
| Github Organizations | github_orgas, go | GITHUB_ORGAS | - | List all organizations you want get informations. Format \<orga1>,\<orga2>,\<orga3> (like test1,test2) |
28-
| Github Repos | github_repos, grs | GITHUB_REPOS | - | List all repositories you want get informations. Format \<orga>/\<repo>,\<orga>/\<repo2>,\<orga>/\<repo3> (like test/test) |
28+
| Github Repos | github_repos, grs | GITHUB_REPOS | - | [Optional] List all repositories you want get informations. Format \<orga>/\<repo>,\<orga>/\<repo2>,\<orga>/\<repo3> (like test/test). Defaults to all repositories owned by the organizations. |
2929
| Exporter port | port, p | PORT | 9999 | Exporter port |
3030
| Github Api URL | github_api_url, url | GITHUB_API_URL | api.github.com | Github API URL (primarily for Github Enterprise usage) |
3131
| Github Enterprise Name | enterprise_name | ENTERPRISE_NAME | "" | Enterprise name. Needed for enterprise endpoints (/enterprises/{ENTERPRISE_NAME}/*). Currently used to get Enterprise level tunners status |

go.mod

+4-5
Original file line numberDiff line numberDiff line change
@@ -5,12 +5,11 @@ go 1.16
55
require (
66
github.com/andybalholm/brotli v1.0.3 // indirect
77
github.com/bradleyfalzon/ghinstallation v1.1.1
8+
github.com/die-net/lrucache v0.0.0-20220628165024-20a71bc65bf1 // indirect
89
github.com/fasthttp/router v1.3.9
9-
github.com/go-kit/kit v0.10.0 // indirect
10-
github.com/google/go-github v17.0.0+incompatible // indirect
11-
github.com/google/go-github/v38 v38.1.0
12-
github.com/gregjones/httpcache v0.0.0-20190611155906-901d90724c79 // indirect
13-
github.com/peterbourgon/diskv v2.0.1+incompatible // indirect
10+
github.com/google/go-github v17.0.0+incompatible
11+
github.com/google/go-github/v45 v45.2.0
12+
github.com/gregjones/httpcache v0.0.0-20190611155906-901d90724c79
1413
github.com/prometheus/client_golang v1.11.0
1514
github.com/urfave/cli/v2 v2.3.0
1615
github.com/valyala/fasthttp v1.22.0

go.sum

+24-211
Large diffs are not rendered by default.

pkg/config/config.go

+23-5
Original file line numberDiff line numberDiff line change
@@ -12,12 +12,16 @@ var (
1212
Refresh int64
1313
Repositories cli.StringSlice
1414
Organizations cli.StringSlice
15-
APIURL string
15+
APIURL string
16+
CacheSizeBytes int64
1617
}
17-
Port int
18-
Debug bool
19-
EnterpriseName string
20-
WorkflowFields string
18+
Metrics struct {
19+
FetchWorkflowRunUsage bool
20+
}
21+
Port int
22+
Debug bool
23+
EnterpriseName string
24+
WorkflowFields string
2125
)
2226

2327
// InitConfiguration - set configuration from env vars or command parameters
@@ -109,5 +113,19 @@ func InitConfiguration() []cli.Flag {
109113
Value: "repo,id,node_id,head_branch,head_sha,run_number,workflow_id,workflow,event,status",
110114
Destination: &WorkflowFields,
111115
},
116+
&cli.BoolFlag{
117+
Name: "fetch_workflow_run_usage",
118+
EnvVars: []string{"FETCH_WORKFLOW_RUN_USAGE"},
119+
Usage: "When true, will perform an API call per workflow run to fetch the workflow usage",
120+
Value: true,
121+
Destination: &Metrics.FetchWorkflowRunUsage,
122+
},
123+
&cli.Int64Flag{
124+
Name: "github_cache_size_bytes",
125+
EnvVars: []string{"GITHUB_CACHE_SIZE_BYTES"},
126+
Value: 100 * 1024 * 1024,
127+
Usage: "Size of Github HTTP cache in bytes",
128+
Destination: &Github.CacheSizeBytes,
129+
},
112130
}
113131
}

pkg/metrics/get_billable_from_github.go

+15-5
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@ import (
88
"strings"
99
"time"
1010

11+
"github.com/google/go-github/github"
1112
"github.com/prometheus/client_golang/prometheus"
1213
)
1314

@@ -24,17 +25,26 @@ var (
2425
// getBillableFromGithub - return billable informations for MACOS, WINDOWS and UBUNTU runners.
2526
func getBillableFromGithub() {
2627
for {
27-
for _, repo := range config.Github.Repositories.Value() {
28+
for _, repo := range repositories {
2829
for k, v := range workflows[repo] {
2930
r := strings.Split(repo, "/")
30-
resp, _, err := client.Actions.GetWorkflowUsageByID(context.Background(), r[0], r[1], k)
31-
if err != nil {
32-
log.Printf("GetWorkflowUsageByID error for %s: %s", repo, err.Error())
33-
} else {
31+
32+
for {
33+
resp, _, err := client.Actions.GetWorkflowUsageByID(context.Background(), r[0], r[1], k)
34+
if rl_err, ok := err.(*github.RateLimitError); ok {
35+
log.Printf("GetWorkflowUsageByID ratelimited. Pausing until %s", rl_err.Rate.Reset.Time.String())
36+
time.Sleep(time.Until(rl_err.Rate.Reset.Time))
37+
continue
38+
} else if err != nil {
39+
log.Printf("GetWorkflowUsageByID error for %s: %s", repo, err.Error())
40+
break
41+
}
3442
workflowBillGauge.WithLabelValues(repo, strconv.FormatInt(*v.ID, 10), *v.NodeID, *v.Name, *v.State, "MACOS").Set(float64(resp.GetBillable().MacOS.GetTotalMS()) / 1000)
3543
workflowBillGauge.WithLabelValues(repo, strconv.FormatInt(*v.ID, 10), *v.NodeID, *v.Name, *v.State, "WINDOWS").Set(float64(resp.GetBillable().Windows.GetTotalMS()) / 1000)
3644
workflowBillGauge.WithLabelValues(repo, strconv.FormatInt(*v.ID, 10), *v.NodeID, *v.Name, *v.State, "UBUNTU").Set(float64(resp.GetBillable().Ubuntu.GetTotalMS()) / 1000)
45+
break
3746
}
47+
3848
}
3949
}
4050

pkg/metrics/get_runners_enterprise_from_github.go

+39-14
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@ import (
77
"strconv"
88
"time"
99

10+
"github.com/google/go-github/v45/github"
1011
"github.com/prometheus/client_golang/prometheus"
1112
)
1213

@@ -20,22 +21,46 @@ var (
2021
)
2122
)
2223

24+
func getAllEnterpriseRunners() []*github.Runner {
25+
var runners []*github.Runner
26+
opt := &github.ListOptions{PerPage: 200}
27+
28+
for {
29+
resp, rr, err := client.Enterprise.ListRunners(context.Background(), config.EnterpriseName, nil)
30+
if rl_err, ok := err.(*github.RateLimitError); ok {
31+
log.Printf("ListRunners ratelimited. Pausing until %s", rl_err.Rate.Reset.Time.String())
32+
time.Sleep(time.Until(rl_err.Rate.Reset.Time))
33+
continue
34+
} else if err != nil {
35+
log.Printf("ListRunners error for enterprise %s: %s", config.EnterpriseName, err.Error())
36+
return nil
37+
}
38+
39+
runners = append(runners, resp.Runners...)
40+
if rr.NextPage == 0 {
41+
break
42+
}
43+
opt.Page = rr.NextPage
44+
}
45+
46+
return runners
47+
}
48+
2349
func getRunnersEnterpriseFromGithub() {
24-
if config.EnterpriseName != "" {
25-
for {
26-
runners, _, err := client.Enterprise.ListRunners(context.Background(), config.EnterpriseName, nil)
27-
if err != nil {
28-
log.Printf("Enterprise.ListRunners error: %s", err.Error())
29-
} else {
30-
for _, runner := range runners.Runners {
31-
var integerStatus float64
32-
if integerStatus = 0; runner.GetStatus() == "online" {
33-
integerStatus = 1
34-
}
35-
runnersEnterpriseGauge.WithLabelValues(*runner.OS, *runner.Name, strconv.FormatInt(runner.GetID(), 10)).Set(integerStatus)
36-
}
50+
if config.EnterpriseName == "" {
51+
return
52+
}
53+
for {
54+
runners := getAllEnterpriseRunners()
55+
56+
for _, runner := range runners {
57+
var integerStatus float64
58+
if integerStatus = 0; runner.GetStatus() == "online" {
59+
integerStatus = 1
3760
}
61+
runnersEnterpriseGauge.WithLabelValues(*runner.OS, *runner.Name, strconv.FormatInt(runner.GetID(), 10)).Set(integerStatus)
62+
}
63+
3864
time.Sleep(time.Duration(config.Github.Refresh) * time.Second)
3965
}
40-
}
4166
}

pkg/metrics/get_runners_from_github.go

+34-11
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@ import (
88
"strings"
99
"time"
1010

11+
"github.com/google/go-github/v45/github"
1112
"github.com/prometheus/client_golang/prometheus"
1213
)
1314

@@ -21,21 +22,43 @@ var (
2122
)
2223
)
2324

25+
func getAllRepoRunners(owner string, repo string) []*github.Runner {
26+
var runners []*github.Runner
27+
opt := &github.ListOptions{PerPage: 200}
28+
29+
for {
30+
resp, rr, err := client.Actions.ListRunners(context.Background(), owner, repo, opt)
31+
if rl_err, ok := err.(*github.RateLimitError); ok {
32+
log.Printf("ListRunners ratelimited. Pausing until %s", rl_err.Rate.Reset.Time.String())
33+
time.Sleep(time.Until(rl_err.Rate.Reset.Time))
34+
continue
35+
} else if err != nil {
36+
log.Printf("ListRunners error for repo %s: %s", repo, err.Error())
37+
return nil
38+
}
39+
40+
runners = append(runners, resp.Runners...)
41+
if rr.NextPage == 0 {
42+
break
43+
}
44+
opt.Page = rr.NextPage
45+
}
46+
47+
return runners
48+
}
49+
2450
// getRunnersFromGithub - return information about runners and their status for a specific repo
2551
func getRunnersFromGithub() {
2652
for {
27-
for _, repo := range config.Github.Repositories.Value() {
53+
for _, repo := range repositories {
2854
r := strings.Split(repo, "/")
29-
resp, _, err := client.Actions.ListRunners(context.Background(), r[0], r[1], nil)
30-
if err != nil {
31-
log.Printf("ListRunners error for %s: %s", repo, err.Error())
32-
} else {
33-
for _, runner := range resp.Runners {
34-
if runner.GetStatus() == "online" {
35-
runnersGauge.WithLabelValues(repo, *runner.OS, *runner.Name, strconv.FormatInt(runner.GetID(), 10), strconv.FormatBool(runner.GetBusy())).Set(1)
36-
} else {
37-
runnersGauge.WithLabelValues(repo, *runner.OS, *runner.Name, strconv.FormatInt(runner.GetID(), 10), strconv.FormatBool(runner.GetBusy())).Set(0)
38-
}
55+
56+
runners := getAllRepoRunners(r[0], r[1])
57+
for _, runner := range runners {
58+
if runner.GetStatus() == "online" {
59+
runnersGauge.WithLabelValues(repo, *runner.OS, *runner.Name, strconv.FormatInt(runner.GetID(), 10), strconv.FormatBool(runner.GetBusy())).Set(1)
60+
} else {
61+
runnersGauge.WithLabelValues(repo, *runner.OS, *runner.Name, strconv.FormatInt(runner.GetID(), 10), strconv.FormatBool(runner.GetBusy())).Set(0)
3962
}
4063
}
4164
}

pkg/metrics/get_runners_organization_from_github.go

+30-17
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@ import (
77
"strconv"
88
"time"
99

10-
"github.com/google/go-github/v38/github"
10+
"github.com/google/go-github/v45/github"
1111
"github.com/prometheus/client_golang/prometheus"
1212
)
1313

@@ -21,27 +21,40 @@ var (
2121
)
2222
)
2323

24+
func getAllOrgRunners(orga string) []*github.Runner {
25+
var runners []*github.Runner
26+
opt := &github.ListOptions{PerPage: 200}
27+
28+
for {
29+
resp, rr, err := client.Actions.ListOrganizationRunners(context.Background(), orga, opt)
30+
if rl_err, ok := err.(*github.RateLimitError); ok {
31+
log.Printf("ListOrganizationRunners ratelimited. Pausing until %s", rl_err.Rate.Reset.Time.String())
32+
time.Sleep(time.Until(rl_err.Rate.Reset.Time))
33+
continue
34+
} else if err != nil {
35+
log.Printf("ListOrganizationRunners error for org %s: %s", orga, err.Error())
36+
return runners
37+
}
38+
39+
runners = append(runners, resp.Runners...)
40+
if rr.NextPage == 0 {
41+
break
42+
}
43+
opt.Page = rr.NextPage
44+
}
45+
return runners
46+
}
47+
2448
// getRunnersOrganizationFromGithub - return information about runners and their status for an organization
2549
func getRunnersOrganizationFromGithub() {
2650
for {
2751
for _, orga := range config.Github.Organizations.Value() {
28-
opt := &github.ListOptions{PerPage: 10}
29-
for {
30-
resp, rr, err := client.Actions.ListOrganizationRunners(context.Background(), orga, opt)
31-
if err != nil {
32-
log.Printf("ListOrganizationRunners error for %s: %s", orga, err.Error())
52+
runners := getAllOrgRunners(orga)
53+
for _, runner := range runners {
54+
if runner.GetStatus() == "online" {
55+
runnersOrganizationGauge.WithLabelValues(orga, *runner.OS, *runner.Name, strconv.FormatInt(runner.GetID(), 10), strconv.FormatBool(runner.GetBusy())).Set(1)
3356
} else {
34-
for _, runner := range resp.Runners {
35-
if runner.GetStatus() == "online" {
36-
runnersOrganizationGauge.WithLabelValues(orga, *runner.OS, *runner.Name, strconv.FormatInt(runner.GetID(), 10), strconv.FormatBool(runner.GetBusy())).Set(1)
37-
} else {
38-
runnersOrganizationGauge.WithLabelValues(orga, *runner.OS, *runner.Name, strconv.FormatInt(runner.GetID(), 10), strconv.FormatBool(runner.GetBusy())).Set(0)
39-
}
40-
}
41-
if rr.NextPage == 0 {
42-
break
43-
}
44-
opt.Page = rr.NextPage
57+
runnersOrganizationGauge.WithLabelValues(orga, *runner.OS, *runner.Name, strconv.FormatInt(runner.GetID(), 10), strconv.FormatBool(runner.GetBusy())).Set(0)
4558
}
4659
}
4760
}

0 commit comments

Comments
 (0)