Skip to content

Commit

Permalink
Merge pull request #15 from scality/feature/cost-explorer
Browse files Browse the repository at this point in the history
RELENG-7210 Tracking cost of jobs
  • Loading branch information
gaspardmoindrot authored Apr 18, 2023
2 parents 0df8b5d + d000b32 commit 6add10a
Show file tree
Hide file tree
Showing 5 changed files with 77 additions and 18 deletions.
1 change: 1 addition & 0 deletions gh_actions_exporter/Webhook.py
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,7 @@ def workflow_run(self):
def workflow_job(self):
self.metrics.handle_job_status(self.payload, self.settings)
self.metrics.handle_job_duration(self.payload, self.settings)
self.metrics.handle_job_cost(self.payload, self.settings)

def ping(self):
logger.info('Ping from Github')
9 changes: 9 additions & 0 deletions gh_actions_exporter/config.py
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,15 @@ class ConfigFile(BaseSettings):

class Settings(BaseSettings):
job_relabelling: Optional[List[Relabel]] = []
job_costs: Optional[Dict[str, float]] = {
'medium': 0.008,
'large': 0.016,
'xlarge': 0.032,
'2xlarge': 0.064,
'3xlarge': 0.128
}
flavor_label: Optional[str] = 'flavor'
default_cost: Optional[float] = 0.008

class Config:
config: ConfigFile = ConfigFile()
Expand Down
25 changes: 23 additions & 2 deletions gh_actions_exporter/metrics.py
Original file line number Diff line number Diff line change
Expand Up @@ -115,6 +115,12 @@ def __init__(self, settings: Settings):
'Time between when a job is requested and started',
labelnames=self.job_labelnames
)
# Metrics to sum the cost of a job
self.job_cost = Counter(
'github_actions_job_cost_count',
'Cost of a job',
labelnames=self.job_labelnames
)

def workflow_labels(self, webhook: WebHook) -> dict:
return dict(
Expand Down Expand Up @@ -211,13 +217,28 @@ def handle_job_status(self, webhook: WebHook, settings: Settings):
elif webhook.workflow_job.status == 'queued':
self.job_status_queued.labels(**labels).inc()

def _get_job_duration(self, webhook: WebHook) -> float:
if webhook.workflow_job.conclusion:
return (webhook.workflow_job.completed_at.timestamp()
- webhook.workflow_job.started_at.timestamp())
return 0

def handle_job_duration(self, webhook: WebHook, settings: Settings):
labels = self.job_labels(webhook, settings)
if webhook.workflow_job.conclusion:
duration = (webhook.workflow_job.completed_at.timestamp()
- webhook.workflow_job.started_at.timestamp())
duration = self._get_job_duration(webhook)
self.job_duration.labels(**labels).observe(duration)
elif webhook.workflow_job.status == "in_progress":
duration = (webhook.workflow_job.steps[0].started_at.timestamp()
- webhook.workflow_job.started_at.timestamp())
self.job_start_duration.labels(**labels).observe(duration)

def handle_job_cost(self, webhook: WebHook, settings: Settings):
labels = self.job_labels(webhook, settings)
# look for the flavor label
flavor = labels.get(settings.flavor_label, None)
cost_per_min = settings.job_costs.get(flavor, settings.default_cost)
if webhook.workflow_job.conclusion and cost_per_min:
duration = self._get_job_duration(webhook)
# Cost of runner is duration / 60 * cost_per_min
self.job_cost.labels(**labels).inc(duration / 60 * cost_per_min)
43 changes: 27 additions & 16 deletions tests/api/conftest.py
Original file line number Diff line number Diff line change
Expand Up @@ -9,22 +9,33 @@

@lru_cache()
def job_relabel_config():
return Settings(job_relabelling=[
Relabel(
label="cloud",
values=[
"mycloud"
],
type="name",
default="github-hosted"
),
Relabel(
label="image",
values=[
"ubuntu-latest"
],
)
])
return Settings(
job_relabelling=[
Relabel(
label="cloud",
values=[
"mycloud"
],
type="name",
default="github-hosted"
),

Relabel(
label="image",
values=[
"ubuntu-latest"
],
),

Relabel(
label="flavor",
values=[
"large"
],
default="medium"
)
],
)


@lru_cache()
Expand Down
17 changes: 17 additions & 0 deletions tests/api/test_job.py
Original file line number Diff line number Diff line change
Expand Up @@ -86,3 +86,20 @@ def test_job_relabel(override_job_config, client, workflow_job, headers):
if "image=\"ubuntu-latest\"" in line:
result = True
assert result is True


def test_job_cost(client, workflow_job, headers):
metrics = client.get('/metrics')
for line in metrics.text.split('\n'):
if 'github_actions_job_cost_count_total{' in line:
assert '0.0' in line

workflow_job['workflow_job']['conclusion'] = 'success'
workflow_job['workflow_job']['completed_at'] = '2021-11-29T14:59:57Z'
response = client.post('/webhook', json=workflow_job, headers=headers)
assert response.status_code == 202

metrics = client.get('/metrics')
for line in metrics.text.split('\n'):
if 'github_actions_job_cost_count_total{' in line:
assert '0.104' in line

0 comments on commit 6add10a

Please sign in to comment.