Skip to content

Commit b024680

Browse files
committed
commit
0 parents  commit b024680

23 files changed

+1206
-0
lines changed

.gitignore

+1
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
.sequence

.idea/.gitignore

+8
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

.idea/gitlab-exporter.iml

+9
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

.idea/modules.xml

+8
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

.idea/vcs.xml

+6
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

.vscode/launch.json

+7
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
{
2+
// Use IntelliSense to learn about possible attributes.
3+
// Hover to view descriptions of existing attributes.
4+
// For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387
5+
"version": "0.2.0",
6+
"configurations": []
7+
}

Dockerfile

+20
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
FROM golang:1.16 as build
2+
3+
ENV GO111MODULE=on
4+
5+
COPY ./ /go/src/github.com/aserdean/gitlab-exporter/
6+
WORKDIR /go/src/github.com/aserdean/gitlab-exporter/
7+
8+
RUN go mod download \
9+
&& CGO_ENABLED=0 GOOS=linux go build -o /bin/main
10+
11+
FROM alpine:3.11.3
12+
13+
RUN apk --no-cache add ca-certificates \
14+
&& addgroup exporter \
15+
&& adduser -S -G exporter exporter
16+
ADD VERSION .
17+
USER exporter
18+
COPY --from=build /bin/main /bin/main
19+
ENV LISTEN_PORT=8080
20+
ENTRYPOINT [ "/bin/main" ]

README.md

+22
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
# Prometheus Gitlab Exporter
2+
3+
Exposes metrics for your repositories from the Gitlab API, to a Prometheus compatible endpoint.
4+
5+
## Configuration
6+
7+
This exporter is setup to take input from environment variables. All variables are optional:
8+
9+
* `GROUPS` If supplied, the exporter will enumerate all repositories for that group. Expected in the format "group1, group2".
10+
* `REPOS` If supplied, The repos you wish to monitor, expected in the format "group/repo1, group/repo2". Can be across different Gitlab users/orgs.
11+
* `GITLAB_TOKEN` If supplied, enables the user to supply a gitlab authentication token that allows the API to be queried. If none supplied, the exporter will only have access to public repos. The token have `read_api` access.
12+
* `GITHUB_TOKEN_FILE` If supplied _instead of_ `GITHUB_TOKEN`, enables the user to supply a path to a file containing a gitlab authentication token.
13+
* `API_URL` Gitlab API URL, shouldn't need to change this. Defaults to `https://gitlab.com`
14+
* `LOG_LEVEL` The level of logging the exporter will run with, defaults to `debug`
15+
16+
## Install and deploy
17+
18+
Build a docker image:
19+
```
20+
docker build -t <image-name> .
21+
docker tag <built_image_hash> gitlab-exporter_gitlab-exporter:latest
22+
```

VERSION

+1
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
1.0.0

config/config.go

+121
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,121 @@
1+
package config
2+
3+
import (
4+
"fmt"
5+
goGitlab "github.com/xanzy/go-gitlab"
6+
"io/ioutil"
7+
"strings"
8+
9+
log "github.com/sirupsen/logrus"
10+
11+
"os"
12+
)
13+
14+
// GetEnv - Allows us to supply a fallback option if nothing specified
15+
func GetEnv(key, fallback string) string {
16+
value := os.Getenv(key)
17+
if len(value) == 0 {
18+
return fallback
19+
}
20+
return value
21+
}
22+
23+
// Config struct holds all of the runtime confgiguration for the application
24+
type Config struct {
25+
APIURL string
26+
Repositories []string
27+
Organisations []string
28+
Users string
29+
APITokenEnv string
30+
APITokenFile string
31+
APIToken string
32+
}
33+
34+
// Init populates the Config struct based on environmental runtime configuration
35+
func Init() Config {
36+
37+
listenPort := GetEnv("LISTEN_PORT", "9171")
38+
os.Setenv("LISTEN_PORT", listenPort)
39+
url := "https://gitlab.com"
40+
repos := os.Getenv("REPOS")
41+
groups := os.Getenv("GROUPS")
42+
users := os.Getenv("USERS")
43+
tokenEnv := os.Getenv("GITLAB_TOKEN")
44+
tokenFile := os.Getenv("GITLAB_TOKEN_FILE")
45+
token, err := getAuth(tokenEnv, tokenFile)
46+
47+
if err != nil {
48+
log.Errorf("Error initialising Configuration, Error: %v", err)
49+
}
50+
51+
var reposList []string
52+
if repos != "" {
53+
rs := strings.Split(repos, ", ")
54+
for _, x := range rs {
55+
reposList = append(reposList, x)
56+
}
57+
}
58+
59+
var groupList []string
60+
if groups != "" {
61+
gs := strings.Split(groups, ", ")
62+
for _, x := range gs {
63+
groupList = append(reposList, x)
64+
groupRepos := getReposByGroup(x, token)
65+
reposList = append(reposList, groupRepos...)
66+
}
67+
}
68+
69+
70+
appConfig := Config{
71+
url,
72+
reposList,
73+
groupList,
74+
users,
75+
tokenEnv,
76+
tokenFile,
77+
token,
78+
}
79+
80+
return appConfig
81+
}
82+
83+
// getAuth returns oauth2 token as string for usage in http.request
84+
func getAuth(token string, tokenFile string) (string, error) {
85+
86+
if token != "" {
87+
return token, nil
88+
} else if tokenFile != "" {
89+
b, err := ioutil.ReadFile(tokenFile)
90+
if err != nil {
91+
return "", err
92+
}
93+
return strings.TrimSpace(string(b)), err
94+
95+
}
96+
97+
return "", nil
98+
}
99+
100+
// getReposByGroup returns a list of repositories
101+
// that belong to a group
102+
func getReposByGroup(group string, token string) []string {
103+
fmt.Println(token)
104+
git, err := goGitlab.NewClient(token)
105+
if err != nil {
106+
log.Fatalf("Failed to create client: %v", err)
107+
return nil
108+
}
109+
groupInfo, _, err := git.Groups.GetGroup(group)
110+
if err != nil {
111+
log.Errorf("Error obtaining repositories from group, Error: %v", err)
112+
return nil
113+
}
114+
var repos []string
115+
for _, p := range groupInfo.Projects {
116+
fullName := group + "/" + p.Path
117+
repos = append(repos, fullName)
118+
}
119+
120+
return repos
121+
}

docker-compose.yml

+41
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,41 @@
1+
version: "3"
2+
3+
services:
4+
gitlab-exporter:
5+
expose:
6+
- 8080
7+
ports:
8+
- 8080:8080
9+
image: gitlab-exporter_gitlab-exporter:latest
10+
environment:
11+
# It can use both repositories and organization names separated by comma ", "
12+
# eg:
13+
# REPOS=RepoName1, RepoName2
14+
# GROUPS= ORG1, ORG2
15+
- GROUPS=aserdean1
16+
- GITLAB_TOKEN=vi4j27pD9fZKCFJTN5Cn
17+
18+
prometheus:
19+
image: docker.io/prom/prometheus:v2.22.2
20+
ports:
21+
- 9090:9090
22+
links:
23+
- gitlab-exporter
24+
volumes:
25+
- ./prometheus/config.yml:/etc/prometheus/prometheus.yml
26+
27+
grafana:
28+
image: docker.io/grafana/grafana:7.3.3
29+
ports:
30+
- 3000:3000
31+
environment:
32+
GF_AUTH_ANONYMOUS_ENABLED: 'true'
33+
GF_INSTALL_PLUGINS: grafana-polystat-panel,yesoreyeram-boomtable-panel
34+
links:
35+
- prometheus
36+
volumes:
37+
- ./grafana/datasources.yml:/etc/grafana/provisioning/datasources/default.yml
38+
39+
networks:
40+
default:
41+
driver: bridge

exporter/gather.go

+129
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,129 @@
1+
package exporter
2+
3+
import (
4+
log "github.com/sirupsen/logrus"
5+
goGitlab "github.com/xanzy/go-gitlab"
6+
)
7+
8+
9+
10+
// XXX gatherData - Collects the data from the API and stores into struct
11+
func (e *Exporter) gatherData() ([]*Datum, error) {
12+
13+
data := []*Datum{}
14+
15+
// this needs to use something returned from getGroups
16+
for _, repo := range e.Config.Repositories {
17+
git, err := goGitlab.NewClient(e.Config.APIToken)
18+
if err != nil {
19+
log.Fatalf("Failed to create client: %v", err)
20+
}
21+
22+
d := new(Datum)
23+
24+
d.RepoName = repo
25+
26+
d.Branches = getBranches(repo, git)
27+
28+
d.Releases = getReleases(repo, git)
29+
30+
d.MergeRequests = getMergeRequests(repo, git)
31+
32+
for _, b := range d.Branches {
33+
c := new(CommitsPerBranch)
34+
c.Branch = b.Name
35+
c.BranchCommits = getCommits(repo, b.Name, git)
36+
d.Commits = append(d.Commits, c)
37+
}
38+
39+
data = append(data, d)
40+
41+
log.Infof("API data fetched for repository: %s", repo)
42+
}
43+
44+
//return data, rates, err
45+
return data, nil
46+
47+
}
48+
49+
type MergeRequests []*goGitlab.MergeRequest
50+
51+
func getMergeRequests(repo string, client *goGitlab.Client) MergeRequests {
52+
var mrs MergeRequests
53+
opts := &goGitlab.ListProjectMergeRequestsOptions{
54+
ListOptions: goGitlab.ListOptions{
55+
Page: 0,
56+
},
57+
}
58+
for {
59+
pageMRs, resp, err := client.MergeRequests.ListProjectMergeRequests(repo, opts)
60+
if err != nil {
61+
log.Errorf("Unable to obtain merge requests from API, Error: %s", err)
62+
}
63+
64+
mrs = append(mrs, pageMRs...)
65+
66+
// Exit the loop when we've seen all pages.
67+
if resp.NextPage == 0 {
68+
break
69+
}
70+
71+
// Update the page number to get the next page.
72+
opts.Page = resp.NextPage
73+
}
74+
75+
return mrs
76+
}
77+
78+
type Commits []*goGitlab.Commit
79+
80+
func getCommits(repo string, branch string, client *goGitlab.Client) Commits {
81+
var commits Commits
82+
opts := &goGitlab.ListCommitsOptions{
83+
RefName: &branch,
84+
ListOptions: goGitlab.ListOptions{
85+
Page: 0,
86+
},
87+
}
88+
for {
89+
pageCs, resp, err := client.Commits.ListCommits(repo, opts)
90+
if err != nil {
91+
log.Errorf("Unable to obtain commits from API, Error: %s", err)
92+
}
93+
94+
commits = append(commits, pageCs...)
95+
96+
// Exit the loop when we've seen all pages.
97+
if resp.NextPage == 0 {
98+
break
99+
}
100+
101+
// Update the page number to get the next page.
102+
opts.Page = resp.NextPage
103+
}
104+
105+
return commits
106+
}
107+
108+
type Releases []*goGitlab.Release
109+
110+
func getReleases(repo string, client *goGitlab.Client) Releases {
111+
var releases Releases
112+
releases, _, err := client.Releases.ListReleases(repo, &goGitlab.ListReleasesOptions{PerPage: 1000})
113+
if err != nil {
114+
log.Errorf("Unable to obtain releases from API, Error: %s", err)
115+
}
116+
return releases
117+
}
118+
119+
type Branches []*goGitlab.Branch
120+
121+
func getBranches(repo string, client *goGitlab.Client) Branches {
122+
var branches Branches
123+
pageOpts := goGitlab.ListOptions{PerPage: 1000}
124+
branches, _, err := client.Branches.ListBranches(repo, &goGitlab.ListBranchesOptions{ListOptions: pageOpts})
125+
if err != nil {
126+
log.Errorf("Unable to obtain branches from API, Error: %s", err)
127+
}
128+
return branches
129+
}

0 commit comments

Comments
 (0)