Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

WIP: Re-write #54

Open
wants to merge 20 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Jump to
The table of contents is too big for display.
Diff view
Diff view
  •  
  •  
  •  
4 changes: 3 additions & 1 deletion .gitignore
Original file line number Diff line number Diff line change
@@ -1 +1,3 @@
.sequence
.sequence
TODO
github-exporter
4 changes: 4 additions & 0 deletions Dockerfile
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,10 @@ RUN go mod download \
&& go test ./... \
&& CGO_ENABLED=0 GOOS=linux go build -o /bin/main


RUN CGO_ENABLED=0 GOOS=linux go build -o /bin/main


FROM alpine:3.11.3

RUN apk --no-cache add ca-certificates \
Expand Down
42 changes: 33 additions & 9 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,14 +6,39 @@ Exposes basic metrics for your repositories from the GitHub API, to a Prometheus

## Configuration

This exporter is setup to take input from environment variables. All variables are optional:
This exporter is setup to take configuration through environment variables. All variables are optional:

### Targets

All targets support arrays of values (comma delimited). e.g. "org1/repo1, org2/repo3".

* `ORGS` If supplied, the exporter will enumerate all repositories for any organizations are processed.
* `USERS` If supplied, the exporter will enumerate all repositories for that users in a similar fashion to organisations.
* `REPOS` If supplied, allows you to explicitly set the repos you wish to monitor Can be across different Github users/orgs.

### Authentication

Either through environmental variables or passed in through a token file (more secure).

* `ORGS` If supplied, the exporter will enumerate all repositories for that organization. Expected in the format "org1, org2".
* `REPOS` If supplied, The repos you wish to monitor, expected in the format "user/repo1, user/repo2". Can be across different Github users/orgs.
* `USERS` If supplied, the exporter will enumerate all repositories for that users. Expected in
the format "user1, user2".
* `GITHUB_TOKEN` If supplied, enables the user to supply a github authentication token that allows the API to be queried more often. Optional, but recommended.
* `GITHUB_TOKEN_FILE` If supplied _instead of_ `GITHUB_TOKEN`, enables the user to supply a path to a file containing a github authentication token that allows the API to be queried more often. Optional, but recommended.

### Optional Metrics

* `OPTIONAL_METRICS` allows you to specify additional collection of the following metrics per repository;

- Commits
- Releases
- Pulls

**Please be aware the above metrics can only be collected from the V3 API on a per repositry basis. As a result they are very expensive to capture and you may exceed the GitHub API rate limits if you're monitoring hundreds of repositories.**

In order to collect any of the above, populate the `OPTIONAL_METRICS` variable with a comma delimited set of metrics you wish to capture, e.g. `OPTIONAL_METRICS="commits, releases, pulls"`.

### Operating Configuration

Likely something most users will leave as the default, feel free to override as you see fit.

* `API_URL` Github API URL, shouldn't need to change this. Defaults to `https://api.github.com`
* `LISTEN_PORT` The port you wish to run the container on, the Dockerfile defaults this to `9171`
* `METRICS_PATH` the metrics URL path you wish to use, defaults to `/metrics`
Expand All @@ -24,13 +49,13 @@ the format "user1, user2".

Run manually from Docker Hub:
```
docker run -d --restart=always -p 9171:9171 -e REPOS="infinityworks/ranch-eye, infinityworks/prom-conf" infinityworks/github-exporter
docker run -d --restart=always -p 9171:9171 -e REPOS="infinityworks/policies, infinityworks/prom-conf" infinityworks/github-exporter
```

Build a docker image:
```
docker build -t <image-name> .
docker run -d --restart=always -p 9171:9171 -e REPOS="infinityworks/ranch-eye, infinityworks/prom-conf" <image-name>
docker run -d --restart=always -p 9171:9171 -e REPOS="infinityworks/policies, infinityworks/prom-conf" <image-name>
```

## Docker compose
Expand All @@ -52,8 +77,7 @@ github-exporter:

## Metrics

Metrics will be made available on port 9171 by default
An example of these metrics can be found in the `METRICS.md` markdown file in the root of this repository
Metrics will be made available on port 9171 by default. An example of these metrics can be found in the `METRICS.md` markdown file in the root of this repository

## Tests

Expand Down
83 changes: 18 additions & 65 deletions config/config.go
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
package config

import (
"fmt"
"io/ioutil"
"strings"

Expand All @@ -14,90 +13,44 @@ import (

// Config struct holds all of the runtime confgiguration for the application
type Config struct {
*cfg.BaseConfig
APIURL string
Repositories string
Organisations string
Users string
APITokenEnv string
APITokenFile string
APIToken string
TargetURLs []string
Config *cfg.BaseConfig
APIURL string
Repositories []string
Organisations []string
Users []string
APITokenEnv string
APITokenFile string
APIToken string
OptionalMetrics []string
}

// Init populates the Config struct based on environmental runtime configuration
func Init() Config {

ac := cfg.Init()
url := cfg.GetEnv("API_URL", "https://api.github.com")
repos := os.Getenv("REPOS")
orgs := os.Getenv("ORGS")
users := os.Getenv("USERS")
tokenEnv := os.Getenv("GITHUB_TOKEN")
tokenFile := os.Getenv("GITHUB_TOKEN_FILE")
token, err := getAuth(tokenEnv, tokenFile)
scraped, err := getScrapeURLs(url, repos, orgs, users)

if err != nil {
log.Errorf("Error initialising Configuration, Error: %v", err)
}

appConfig := Config{
&ac,
url,
repos,
orgs,
users,
tokenEnv,
tokenFile,
token,
scraped,
Config: &ac,
APIURL: cfg.GetEnv("API_URL", "https://api.github.com"),
Repositories: strings.Split(os.Getenv("REPOS"), ","),
Organisations: strings.Split(os.Getenv("ORGS"), ","),
Users: strings.Split(os.Getenv("USERS"), ","),
APITokenEnv: os.Getenv("GITHUB_TOKEN"),
APITokenFile: tokenFile,
APIToken: token,
OptionalMetrics: strings.Split(os.Getenv("OPTIONAL_METRICS"), ","),
}

return appConfig
}

// Init populates the Config struct based on environmental runtime configuration
// All URL's are added to the TargetURL's string array
func getScrapeURLs(apiURL, repos, orgs, users string) ([]string, error) {

urls := []string{}

opts := "?&per_page=100" // Used to set the Github API to return 100 results per page (max)

if len(repos) == 0 && len(orgs) == 0 && len(users) == 0 {
log.Info("No targets specified. Only rate limit endpoint will be scraped")
}

// Append repositories to the array
if repos != "" {
rs := strings.Split(repos, ", ")
for _, x := range rs {
y := fmt.Sprintf("%s/repos/%s%s", apiURL, x, opts)
urls = append(urls, y)
}
}

// Append github orginisations to the array
if orgs != "" {
o := strings.Split(orgs, ", ")
for _, x := range o {
y := fmt.Sprintf("%s/orgs/%s/repos%s", apiURL, x, opts)
urls = append(urls, y)
}
}

if users != "" {
us := strings.Split(users, ", ")
for _, x := range us {
y := fmt.Sprintf("%s/users/%s/repos%s", apiURL, x, opts)
urls = append(urls, y)
}
}

return urls, nil
}

// getAuth returns oauth2 token as string for usage in http.request
func getAuth(token string, tokenFile string) (string, error) {

Expand Down