Skip to content

Commit 717e09c

Browse files
committed
RELENG-6059 add the ability to add more relabel jobs
1 parent 633e726 commit 717e09c

File tree

11 files changed

+224
-63
lines changed

11 files changed

+224
-63
lines changed
Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
apiVersion: v1
2+
kind: ConfigMap
3+
metadata:
4+
name: {{ .Release.Name }}-config
5+
data:
6+
config.yaml: |-
7+
{{ .Values.config | toYaml | indent 4 }}

charts/gh-action-exporter/templates/deployment.yaml

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -47,6 +47,16 @@ spec:
4747
port: http
4848
resources:
4949
{{- toYaml .Values.resources | nindent 12 }}
50+
env:
51+
- name: CONFIG_FILE
52+
value: /app/config/config.yaml
53+
volumeMounts:
54+
- name: config
55+
mountPath: /app/config
56+
volumes:
57+
- name: config
58+
configMap:
59+
name: {{ .Release.Name }}-config
5060
{{- with .Values.nodeSelector }}
5161
nodeSelector:
5262
{{- toYaml . | nindent 8 }}

charts/gh-action-exporter/values.yaml

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,10 @@ imagePullSecrets: []
1414
nameOverride: ""
1515
fullnameOverride: ""
1616

17+
18+
config:
19+
job_relabelling: []
20+
1721
serviceAccount:
1822
# Specifies whether a service account should be created
1923
create: true

gh_actions_exporter/Webhook.py

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
import logging
2+
from gh_actions_exporter.config import Settings
23
from gh_actions_exporter.types import WebHook
34
from gh_actions_exporter.metrics import Metrics
45

@@ -9,10 +10,11 @@ class WebhookManager(object):
910
event: str
1011
payload: WebHook
1112

12-
def __init__(self, payload: WebHook, event: str, metrics: Metrics):
13+
def __init__(self, payload: WebHook, event: str, metrics: Metrics, settings: Settings):
1314
self.event = event
1415
self.payload = payload
1516
self.metrics = metrics
17+
self.settings = settings
1618

1719
def __call__(self, *args, **kwargs):
1820
# Check if we managed this event
@@ -28,8 +30,8 @@ def workflow_run(self):
2830
self.metrics.handle_workflow_rebuild(self.payload)
2931

3032
def workflow_job(self):
31-
self.metrics.handle_job_status(self.payload)
32-
self.metrics.handle_job_duration(self.payload)
33+
self.metrics.handle_job_status(self.payload, self.settings)
34+
self.metrics.handle_job_duration(self.payload, self.settings)
3335

3436
def ping(self):
3537
logger.info('Ping from Github')

gh_actions_exporter/config.py

Lines changed: 47 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,47 @@
1+
import yaml
2+
from pathlib import Path
3+
from typing import Dict, Any, List, Optional
4+
5+
from pydantic import BaseModel, BaseSettings
6+
7+
8+
def yaml_config_settings_source(settings: BaseSettings) -> Dict[str, Any]:
9+
"""
10+
A simple settings source that loads variables from a yaml file
11+
12+
"""
13+
config_file: Path = settings.__config__.config.config_file
14+
if config_file:
15+
return yaml.full_load(config_file.read_text())
16+
return {}
17+
18+
19+
class Relabel(BaseModel):
20+
label: str
21+
description: Optional[str]
22+
values: List[str]
23+
24+
25+
class ConfigFile(BaseSettings):
26+
config_file: Optional[Path]
27+
28+
29+
class Settings(BaseSettings):
30+
job_relabelling: Optional[List[Relabel]] = []
31+
32+
class Config:
33+
config: ConfigFile = ConfigFile()
34+
35+
@classmethod
36+
def customise_sources(
37+
cls,
38+
init_settings,
39+
env_settings,
40+
file_secret_settings,
41+
):
42+
return (
43+
init_settings,
44+
yaml_config_settings_source,
45+
env_settings,
46+
file_secret_settings,
47+
)

gh_actions_exporter/main.py

Lines changed: 28 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,26 @@
11
import uvicorn
22

3-
from fastapi import FastAPI, Request
3+
from fastapi import Depends, FastAPI, Request
4+
from functools import lru_cache
45

56
from gh_actions_exporter.metrics import prometheus_metrics, Metrics
67
from gh_actions_exporter.types import WebHook
78
from gh_actions_exporter.Webhook import WebhookManager
9+
from gh_actions_exporter.config import Settings
10+
11+
12+
@lru_cache()
13+
def get_settings() -> Settings:
14+
return Settings()
15+
16+
17+
@lru_cache()
18+
def metrics() -> Metrics:
19+
return Metrics(get_settings())
20+
821

922
app = FastAPI()
10-
metrics = Metrics()
23+
1124

1225
app.add_route('/metrics', prometheus_metrics)
1326

@@ -18,13 +31,23 @@ def index():
1831

1932

2033
@app.post("/webhook", status_code=202)
21-
async def webhook(webhook: WebHook, request: Request):
22-
WebhookManager(payload=webhook, event=request.headers['X-Github-Event'], metrics=metrics)()
34+
async def webhook(
35+
webhook: WebHook,
36+
request: Request,
37+
settings: Settings = Depends(get_settings),
38+
metrics: Metrics = Depends(metrics)
39+
):
40+
WebhookManager(
41+
payload=webhook,
42+
event=request.headers['X-Github-Event'],
43+
metrics=metrics,
44+
settings=settings
45+
)()
2346
return "Accepted"
2447

2548

2649
@app.delete("/clear", status_code=200)
27-
async def clear():
50+
async def clear(metrics: Metrics = Depends(metrics)):
2851
metrics.workflow_rebuild.clear()
2952
metrics.workflow_duration.clear()
3053
metrics.job_duration.clear()

gh_actions_exporter/metrics.py

Lines changed: 23 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -3,8 +3,10 @@
33
from prometheus_client import (CONTENT_TYPE_LATEST, REGISTRY,
44
CollectorRegistry, generate_latest)
55
from prometheus_client.multiprocess import MultiProcessCollector
6+
from typing import Dict, List
67
from fastapi.requests import Request
78
from fastapi.responses import Response
9+
from gh_actions_exporter.config import Relabel, Settings
810
from gh_actions_exporter.types import WebHook
911
from prometheus_client import Counter, Histogram
1012

@@ -23,8 +25,8 @@ def prometheus_metrics(request: Request) -> Response:
2325

2426

2527
class Metrics(object):
26-
27-
def __init__(self):
28+
def __init__(self, settings: Settings):
29+
self.settings = settings
2830
self.workflow_labelnames = [
2931
'repository',
3032
'workflow_name',
@@ -36,6 +38,9 @@ def __init__(self):
3638
'repository_visibility',
3739
'runner_type'
3840
]
41+
for relabel in self.settings.job_relabelling:
42+
self.job_labelnames.append(relabel.label)
43+
3944
self.workflow_rebuild = Counter(
4045
'github_actions_workflow_rebuild_count', 'The number of workflow rebuild',
4146
labelnames=self.workflow_labelnames
@@ -123,13 +128,24 @@ def runner_type(self, webhook: WebHook) -> str:
123128
return 'self-hosted'
124129
return 'github-hosted'
125130

126-
def job_labels(self, webhook: WebHook) -> dict:
131+
def relabel_job(self, relabel: Relabel, labels: List[str]) -> Dict[str, str or None]:
132+
result = {
133+
relabel.label: None
134+
}
135+
for label in relabel.values:
136+
if label in labels:
137+
result[relabel.label] = label
138+
return result
139+
140+
def job_labels(self, webhook: WebHook, settings: Settings) -> dict:
127141
labels = dict(
128142
runner_type=self.runner_type(webhook),
129143
job_name=webhook.workflow_job.name,
130144
repository_visibility=webhook.repository.visibility,
131145
repository=webhook.repository.full_name,
132146
)
147+
for relabel in settings.job_relabelling:
148+
labels.update(self.relabel_job(relabel, webhook.workflow_job.labels))
133149
return labels
134150

135151
def handle_workflow_rebuild(self, webhook: WebHook):
@@ -165,8 +181,8 @@ def handle_workflow_duration(self, webhook: WebHook):
165181
- webhook.workflow_run.run_started_at.timestamp())
166182
self.workflow_duration.labels(**labels).observe(duration)
167183

168-
def handle_job_status(self, webhook: WebHook):
169-
labels = self.job_labels(webhook)
184+
def handle_job_status(self, webhook: WebHook, settings: Settings):
185+
labels = self.job_labels(webhook, settings)
170186
if webhook.workflow_job.conclusion:
171187
if webhook.workflow_job.conclusion == 'success':
172188
self.job_status_success.labels(**labels).inc()
@@ -180,8 +196,8 @@ def handle_job_status(self, webhook: WebHook):
180196
elif webhook.workflow_job.status == 'queued':
181197
self.job_status_queued.labels(**labels).inc()
182198

183-
def handle_job_duration(self, webhook: WebHook):
184-
labels = self.job_labels(webhook)
199+
def handle_job_duration(self, webhook: WebHook, settings: Settings):
200+
labels = self.job_labels(webhook, settings)
185201
if webhook.workflow_job.conclusion:
186202
duration = (webhook.workflow_job.completed_at.timestamp()
187203
- webhook.workflow_job.started_at.timestamp())

0 commit comments

Comments
 (0)