Skip to content

Commit 891bcc2

Browse files
committed
Pass X-GitHub-Delivery Header Value downstream for better correlation
1 parent 0940b14 commit 891bcc2

8 files changed

Lines changed: 552 additions & 51 deletions

File tree

app/clients/gcloud_client.py

Lines changed: 51 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -54,7 +54,14 @@ def _get_template_name(self, template_name):
5454
except Exception:
5555
return None
5656

57-
def create_runner_instance(self, registration_token, repo_url, template_name, instance_label=None):
57+
def create_runner_instance(
58+
self,
59+
registration_token,
60+
repo_url,
61+
template_name,
62+
instance_label=None,
63+
delivery_id=None,
64+
):
5865
"""
5966
Create a new GCE instance for a GitHub Actions runner.
6067
@@ -63,16 +70,26 @@ def create_runner_instance(self, registration_token, repo_url, template_name, in
6370
repo_url (str): The URL of the repository or organization.
6471
template_name (str): The name of the instance template to use.
6572
instance_label (str): Label to add to the Instance for Cost Tracking.
73+
delivery_id (str): The GitHub webhook delivery ID for log correlation.
6674
6775
Returns:
6876
str: The name of the created instance.
6977
"""
7078
instance_template_resource = self._get_template_name(template_name)
7179
if instance_template_resource:
72-
logger.info(f"Found matching instance template: {instance_template_resource.name}")
80+
logger.info(
81+
"Found matching instance template: %s, delivery_id: %s",
82+
instance_template_resource.name,
83+
delivery_id,
84+
)
7385
else:
74-
logger.warning(f"No matching instance template found for label '{template_name}' in region {self.region}. "
75-
"Skipping instance creation.")
86+
logger.warning(
87+
"No matching instance template found for label '%s' in region %s. "
88+
"Skipping instance creation. delivery_id: %s",
89+
template_name,
90+
self.region,
91+
delivery_id,
92+
)
7693
return None
7794

7895
instance_uuid = uuid.uuid4().hex[:16]
@@ -81,7 +98,12 @@ def create_runner_instance(self, registration_token, repo_url, template_name, in
8198
else:
8299
instance_name = f"runner-{instance_uuid}"
83100

84-
logger.info(f"Creating GCE instance {instance_name} with template {instance_template_resource.self_link}")
101+
logger.info(
102+
"Creating GCE instance %s with template %s, delivery_id: %s",
103+
instance_name,
104+
instance_template_resource.self_link,
105+
delivery_id,
106+
)
85107

86108
# Set instance name
87109
instance_resource = compute_v1.Instance() # google.cloud.compute_v1.types.Instance
@@ -122,30 +144,46 @@ def create_runner_instance(self, registration_token, repo_url, template_name, in
122144

123145
try:
124146
# https://docs.cloud.google.com/compute/docs/reference/rest/v1/instances/insert
125-
operation = self.instance_client.insert(
126-
request=request
147+
operation = self.instance_client.insert(request=request)
148+
logger.info(
149+
"Instance creation operation started: %s, delivery_id: %s",
150+
operation.name,
151+
delivery_id,
127152
)
128-
logger.info(f"Instance creation operation started: {operation.name}")
129153
return instance_name
130154
except Exception as e:
131-
logger.error(f"Failed to create instance: {e}")
155+
logger.error(
156+
"Failed to create instance: %s, delivery_id: %s", e, delivery_id
157+
)
132158
raise
133159

134-
def delete_runner_instance(self, instance_name):
160+
def delete_runner_instance(self, instance_name, delivery_id=None):
135161
"""
136162
Delete a GCE instance.
137163
138164
Args:
139165
instance_name (str): The name of the instance to delete.
166+
delivery_id (str): The GitHub webhook delivery ID for log correlation.
140167
"""
141-
logger.info(f"Deleting GCE instance {instance_name}")
168+
logger.info(
169+
"Deleting GCE instance %s, delivery_id: %s", instance_name, delivery_id
170+
)
142171
try:
143172
operation = self.instance_client.delete(
144173
project=self.project_id,
145174
zone=self.zone,
146175
instance=instance_name
147176
)
148-
logger.info(f"Instance deletion operation started: {operation.name}")
177+
logger.info(
178+
"Instance deletion operation started: %s, delivery_id: %s",
179+
operation.name,
180+
delivery_id,
181+
)
149182
except Exception as e:
150-
logger.error(f"Failed to delete instance {instance_name}: {e}")
183+
logger.error(
184+
"Failed to delete instance %s: %s, delivery_id: %s",
185+
instance_name,
186+
e,
187+
delivery_id,
188+
)
151189
raise

app/clients/github_client.py

Lines changed: 13 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -79,7 +79,7 @@ def get_installation_access_token(self):
7979
# The installation access token will expire after 1 hour.
8080
return response.json()['token']
8181

82-
def get_registration_token(self, org_name=None, repo_name=None):
82+
def get_registration_token(self, org_name=None, repo_name=None, delivery_id=None):
8383
"""Gets a runner registration token."""
8484
# https://docs.github.com/en/rest/actions/self-hosted-runners
8585
token = self.get_installation_access_token()
@@ -90,12 +90,20 @@ def get_registration_token(self, org_name=None, repo_name=None):
9090
}
9191
if org_name:
9292
# GitHub Docs: https://t.ly/dAyGK
93-
url = f'https://api.github.com/orgs/{org_name}/actions/runners/registration-token'
94-
logger.info(f"Create registration token for organization: {org_name}")
93+
url = f"https://api.github.com/orgs/{org_name}/actions/runners/registration-token"
94+
logger.info(
95+
"Create registration token for organization: %s, delivery_id: %s",
96+
org_name,
97+
delivery_id,
98+
)
9599
elif repo_name:
96100
# GitHub Docs: https://t.ly/n0w2a
97-
url = f'https://api.github.com/repos/{repo_name}/actions/runners/registration-token'
98-
logger.info(f"Create registration token for repository: {repo_name}")
101+
url = f"https://api.github.com/repos/{repo_name}/actions/runners/registration-token"
102+
logger.info(
103+
"Create registration token for repository: %s, delivery_id: %s",
104+
repo_name,
105+
delivery_id,
106+
)
99107
else:
100108
raise ValueError("Either org_name or repo_name must be provided")
101109

app/routes/webhook.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -73,7 +73,7 @@ def handle_workflow_job_event(payload, delivery_id=None):
7373
"""Handle workflow_job event."""
7474
try:
7575
webhook_service = WebhookService()
76-
result = webhook_service.handle_workflow_job(payload)
76+
result = webhook_service.handle_workflow_job(payload, delivery_id=delivery_id)
7777
logger.info(
7878
"Webhook processed successfully, action: %s, runner_name: %s, "
7979
"delivery_id: %s",

app/services/webhook_service.py

Lines changed: 68 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -40,7 +40,7 @@ def _validate_payload(self, payload):
4040

4141
return True
4242

43-
def handle_workflow_job(self, payload):
43+
def handle_workflow_job(self, payload, delivery_id=None):
4444
"""Process the workflow_job webhook payload from GitHub.
4545
4646
Returns:
@@ -59,7 +59,12 @@ def handle_workflow_job(self, payload):
5959
org_name = payload.get('organization', {}).get('login')
6060

6161
# Sanitize log output - don't log full payload
62-
logger.info("Processing workflow_job action: %s for %s", action, org_name or repo_name)
62+
logger.info(
63+
"Processing workflow_job action: %s for %s, delivery_id: %s",
64+
action,
65+
org_name or repo_name,
66+
delivery_id,
67+
)
6368

6469
# https://docs.github.com/en/webhooks/webhook-events-and-payloads?actionType=queued#workflow_job
6570
if action == 'queued':
@@ -70,27 +75,46 @@ def handle_workflow_job(self, payload):
7075
template_name = label
7176
break
7277
if template_name:
73-
logger.info("Found matching label prefix: %s", template_name)
78+
logger.info(
79+
"Found matching label prefix: %s, delivery_id: %s",
80+
template_name,
81+
delivery_id,
82+
)
7483
instance_name = self._handle_queued_job(
75-
template_name, repo_url, repo_owner_url, repo_name, org_name
84+
template_name,
85+
repo_url,
86+
repo_owner_url,
87+
repo_name,
88+
org_name,
89+
delivery_id=delivery_id,
7690
)
7791
return {'action': 'created', 'runner_name': instance_name}
7892
else:
7993
logger.warning(
80-
"No matching gcp- label prefix found for labels %s. Ignoring job.",
94+
"No matching gcp- label prefix found for labels %s. "
95+
"Ignoring job. delivery_id: %s",
8196
labels,
97+
delivery_id,
8298
)
8399
return {'action': 'ignored', 'runner_name': None}
84100

85101
# https://docs.github.com/en/webhooks/webhook-events-and-payloads?actionType=completed#workflow_job
86102
elif action == 'completed':
87-
runner_name = self._handle_completed_job(workflow_job)
103+
runner_name = self._handle_completed_job(
104+
workflow_job, delivery_id=delivery_id
105+
)
88106
return {'action': 'deleted', 'runner_name': runner_name}
89107

90108
return {'action': 'ignored', 'runner_name': None}
91109

92110
def _handle_queued_job(
93-
self, template_name, repo_url, repo_owner_url, repo_name, org_name
111+
self,
112+
template_name,
113+
repo_url,
114+
repo_owner_url,
115+
repo_name,
116+
org_name,
117+
delivery_id=None,
94118
):
95119
"""Handle queued workflow job.
96120
@@ -101,42 +125,68 @@ def _handle_queued_job(
101125
# Get registration token
102126
if org_name:
103127
# Create GitHub Actions runner instance for organization
104-
token = self.github_client.get_registration_token(org_name=org_name)
128+
token = self.github_client.get_registration_token(
129+
org_name=org_name, delivery_id=delivery_id
130+
)
105131
return self.gcloud_client.create_runner_instance(
106-
token, repo_owner_url, template_name, repo_name
132+
token,
133+
repo_owner_url,
134+
template_name,
135+
repo_name,
136+
delivery_id=delivery_id,
107137
)
108138
elif repo_name:
109139
# Create GitHub Actions runner instance for repository
110-
token = self.github_client.get_registration_token(repo_name=repo_name)
140+
token = self.github_client.get_registration_token(
141+
repo_name=repo_name, delivery_id=delivery_id
142+
)
111143
return self.gcloud_client.create_runner_instance(
112-
token, repo_url, template_name, repo_name
144+
token, repo_url, template_name, repo_name, delivery_id=delivery_id
113145
)
114146
else:
115147
logger.error(
116-
"Neither repository nor organization found in payload. Ignoring job."
148+
"Neither repository nor organization found in payload. "
149+
"Ignoring job. delivery_id: %s",
150+
delivery_id,
117151
)
118152
return None
119153

120154
except Exception as e:
121-
logger.error("Failed to spawn runner: %s", str(e))
155+
logger.error(
156+
"Failed to spawn runner: %s, delivery_id: %s", str(e), delivery_id
157+
)
122158
raise
123159

124-
def _handle_completed_job(self, workflow_job):
160+
def _handle_completed_job(self, workflow_job, delivery_id=None):
125161
"""Handle completed workflow job.
126162
127163
Returns:
128164
str or None: The name of the deleted runner instance.
129165
"""
130166
runner_name = workflow_job.get('runner_name')
131-
logger.info("Job completed. Cleaning up runner: %s", runner_name)
167+
logger.info(
168+
"Job completed. Cleaning up runner: %s, delivery_id: %s",
169+
runner_name,
170+
delivery_id,
171+
)
132172

133173
if not runner_name:
134-
logger.warning("Job completed but no runner_name found in payload.")
174+
logger.warning(
175+
"Job completed but no runner_name found in payload. delivery_id: %s",
176+
delivery_id,
177+
)
135178
return None
136179

137180
try:
138-
self.gcloud_client.delete_runner_instance(runner_name)
181+
self.gcloud_client.delete_runner_instance(
182+
runner_name, delivery_id=delivery_id
183+
)
139184
return runner_name
140185
except Exception as e:
141-
logger.error("Failed to delete runner %s: %s", runner_name, str(e))
186+
logger.error(
187+
"Failed to delete runner %s: %s, delivery_id: %s",
188+
runner_name,
189+
str(e),
190+
delivery_id,
191+
)
142192
return runner_name

0 commit comments

Comments
 (0)