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

PTFE-1547 Add branch and event label to prometheus metrics #110

Merged
merged 1 commit into from
Apr 4, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion gh_actions_exporter/config.py
Original file line number Diff line number Diff line change
Expand Up @@ -42,7 +42,7 @@ class Settings(BaseSettings):
"ubuntu-20.04": 0.008,
}
default_cost: Optional[float] = 0

branches: List[str] = ["main", "master"]
check_runs_enabled: bool = False
github_app_id: Optional[int]
github_app_installation_id: Optional[int]
Expand Down
39 changes: 37 additions & 2 deletions gh_actions_exporter/metrics.py
Original file line number Diff line number Diff line change
@@ -1,10 +1,11 @@
import fnmatch
from typing import Dict, List

from prometheus_client import Counter, Histogram

from gh_actions_exporter.config import Relabel, RelabelType, Settings
from gh_actions_exporter.cost import Cost
from gh_actions_exporter.types import WebHook, WorkflowJob
from gh_actions_exporter.types import WebHook, WorkflowJob, WorkflowRun


class Metrics(object):
Expand All @@ -17,7 +18,10 @@ def __init__(self, settings: Settings):
"workflow_name",
"repository_visibility",
]
self.workflow_labelnames = self.common_labelnames.copy()
self.workflow_labelnames = self.common_labelnames.copy() + [
"branch",
"event",
]
self.job_labelnames = self.common_labelnames.copy() + [
"job_name",
"runner_type",
Expand Down Expand Up @@ -107,11 +111,42 @@ def __init__(self, settings: Settings):
labelnames=self.job_labelnames,
)

def retrieve_branch(self, workflow_run: WorkflowRun) -> str:
"""
Add the branch label to the metrics exposed by the exporter while
also taking into account the cardinality limitations of prometheus:

- For workflows triggered on pull_request event
retrieve the base branch (target branch).

- For other events, retrieve the head branch.

Then check if the branch matches any of the patterns defined in
self.settings.branches like: development/*, main, feature/*, etc.

Return the branch name if it matches any of the patterns,
otherwise return "dev".
"""
ref: str
if workflow_run.event == "pull_request":
assert workflow_run.pull_requests
ref = workflow_run.pull_requests[0].base.ref
else:
ref = workflow_run.head_branch
for branch in self.settings.branches:
if fnmatch.fnmatch(ref, branch):
return ref
return "dev"

def workflow_labels(self, webhook: WebHook) -> dict:
assert webhook.workflow_run
branch = self.retrieve_branch(webhook.workflow_run)
return dict(
workflow_name=webhook.workflow_run.name,
repository=webhook.repository.full_name,
repository_visibility=webhook.repository.visibility,
branch=branch,
event=webhook.workflow_run.event,
)

def runner_type(self, webhook: WebHook) -> str:
Expand Down
10 changes: 10 additions & 0 deletions gh_actions_exporter/types.py
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,15 @@ class WorkflowJob(BaseModel):
steps: Optional[list[Steps]] = None


class BasePullRequest(BaseModel):
ref: str


class PullRequest(BaseModel):
number: int
base: BasePullRequest


class WorkflowRun(BaseModel):
id: int
name: str
Expand All @@ -44,6 +53,7 @@ class WorkflowRun(BaseModel):
workflow_id: int
run_number: int
head_branch: str
pull_requests: Optional[list[PullRequest]] = None
created_at: datetime
updated_at: Optional[datetime] = None
run_attempt: int
Expand Down
25 changes: 25 additions & 0 deletions tests/api/conftest.py
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,31 @@ def workflow_run():
"created_at": "2021-11-16T17:52:47Z",
"run_started_at": "2021-11-16T17:52:47Z",
"updated_at": "2021-11-16T17:53:32Z",
"pull_requests": [
{
"url": "https://api.github.com/repos/org/repo/pulls/555",
"id": 1805247,
"number": 555,
"head": {
"ref": "feature/my-feature",
"sha": "fc98b69edf5ac3f4789c210e758b1ee4b4396b9e",
"repo": {
"id": 395065315,
"url": "https://api.github.com/repos/org/repo",
"name": "repo",
},
},
"base": {
"ref": "master",
"sha": "966917b16d284edde596b0705a49f8c65ecf1c84",
"repo": {
"id": 395065125,
"url": "https://api.github.com/repos/org/repo",
"name": "repo",
},
},
}
],
},
"repository": {
"name": "repo",
Expand Down
4 changes: 3 additions & 1 deletion tests/api/metrics/conftest.py
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,9 @@ def job_relabel_config():

@lru_cache()
def default_settings():
return Settings()
return Settings(
branches=["main", "master", "development/*"],
)


@lru_cache()
Expand Down
38 changes: 38 additions & 0 deletions tests/api/metrics/test_workflow.py
Original file line number Diff line number Diff line change
Expand Up @@ -54,3 +54,41 @@ def test_rebuild(client, workflow_run, headers):
for line in metrics.text.split("\n"):
if "workflow_rebuild_count_total{" in line:
assert "1.0" in line


def test_branch_label(client, workflow_run, headers):
# Ensure a feature branch is not indexed
upload = client.post("/webhook", json=workflow_run, headers=headers)
assert upload.status_code == 202

metrics = client.get("/metrics")
assert metrics.status_code == 200
assert 'branch="feature"' not in metrics.text
assert 'branch="dev"' in metrics.text

# Index a push event with a branch label
workflow_run["workflow_run"]["head_branch"] = "main"
upload = client.post("/webhook", json=workflow_run, headers=headers)
assert upload.status_code == 202

metrics = client.get("/metrics")
assert metrics.status_code == 200
assert 'branch="main"' in metrics.text

# Index a branch with a fnmatch pattern configured
workflow_run["workflow_run"]["head_branch"] = "development/1.2"
upload = client.post("/webhook", json=workflow_run, headers=headers)
assert upload.status_code == 202

metrics = client.get("/metrics")
assert metrics.status_code == 200
assert 'branch="development/1.2"' in metrics.text

# Index the base branch of a pull request
workflow_run["workflow_run"]["event"] = "pull_request"
upload = client.post("/webhook", json=workflow_run, headers=headers)
assert upload.status_code == 202

metrics = client.get("/metrics")
assert metrics.status_code == 200
assert 'branch="master"' in metrics.text
Loading