Skip to content

Commit e9a0580

Browse files
authored
Merge pull request #55 from hexa2k9/feature-return-runner-name-in-webhook-operations
Provide additional Details for Webhook Operations
2 parents b1ddaf7 + 891bcc2 commit e9a0580

8 files changed

Lines changed: 935 additions & 75 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: 49 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -18,13 +18,15 @@ def webhook():
1818
"""Handle incoming GitHub webhook events."""
1919
# https://docs.github.com/en/webhooks/webhook-events-and-payloads
2020
event_type = request.headers.get('X-GitHub-Event')
21+
# https://docs.github.com/en/webhooks/webhook-events-and-payloads#delivery-headers
22+
delivery_id = request.headers.get('X-GitHub-Delivery')
2123

2224
# Validate event type
2325
if not event_type or not isinstance(event_type, str):
2426
logger.error("Missing or invalid X-GitHub-Event header")
2527
return jsonify({'status': 'error', 'message': 'Invalid event type'}), 400
2628

27-
logger.info("Received webhook event: %s", event_type)
29+
logger.info("Received webhook event: %s, delivery_id: %s", event_type, delivery_id)
2830

2931
# Handle ping event
3032
if event_type == 'ping':
@@ -34,36 +36,72 @@ def webhook():
3436
signature = request.headers.get('X-Hub-Signature-256')
3537

3638
if not verify_github_signature(request.data, signature):
37-
logger.error("GitHub webhook signature not successfully verified! Ignoring webhook event.")
39+
logger.error(
40+
"GitHub webhook signature not successfully verified! "
41+
"Ignoring webhook event. delivery_id: %s",
42+
delivery_id,
43+
)
3844
return jsonify({'status': 'forbidden', 'message': 'Invalid signature'}), 403
3945

4046
# Validate JSON payload
4147
try:
4248
payload = request.json
4349
if not payload:
44-
logger.error("Empty or invalid JSON payload")
50+
logger.error("Empty or invalid JSON payload, delivery_id: %s", delivery_id)
4551
return jsonify({'status': 'error', 'message': 'Invalid JSON payload'}), 400
4652
except Exception as e:
47-
logger.error("Failed to parse JSON payload: %s", str(e))
53+
logger.error(
54+
"Failed to parse JSON payload: %s, delivery_id: %s",
55+
str(e),
56+
delivery_id,
57+
)
4858
return jsonify({'status': 'error', 'message': 'Invalid JSON'}), 400
4959

5060
# https://docs.github.com/en/webhooks/webhook-events-and-payloads#workflow_job
5161
if event_type == 'workflow_job':
52-
return handle_workflow_job_event(payload)
62+
return handle_workflow_job_event(payload, delivery_id)
5363
else:
54-
logger.warning("Received unknown event type: %s", event_type)
64+
logger.warning(
65+
"Received unknown event type: %s, delivery_id: %s",
66+
event_type,
67+
delivery_id,
68+
)
5569
return jsonify({'status': 'ignored'}), 200
5670

5771

58-
def handle_workflow_job_event(payload):
72+
def handle_workflow_job_event(payload, delivery_id=None):
5973
"""Handle workflow_job event."""
6074
try:
6175
webhook_service = WebhookService()
62-
webhook_service.handle_workflow_job(payload)
63-
return jsonify({'status': 'success'}), 200
76+
result = webhook_service.handle_workflow_job(payload, delivery_id=delivery_id)
77+
logger.info(
78+
"Webhook processed successfully, action: %s, runner_name: %s, "
79+
"delivery_id: %s",
80+
result.get("action"),
81+
result.get("runner_name"),
82+
delivery_id,
83+
)
84+
return (
85+
jsonify(
86+
{
87+
'status': 'success',
88+
'action': result.get('action'),
89+
'runner_name': result.get('runner_name'),
90+
}
91+
),
92+
200,
93+
)
6494
except ValueError as e:
65-
logger.error("[Webhook] Validation error: %s", str(e))
95+
logger.error(
96+
"[Webhook] Validation error: %s, delivery_id: %s",
97+
str(e),
98+
delivery_id,
99+
)
66100
return jsonify({'status': 'error', 'message': 'Invalid payload'}), 400
67101
except Exception as e:
68-
logger.error("[Webhook] Error handling webhook: %s", str(e))
102+
logger.error(
103+
"[Webhook] Error handling webhook: %s, delivery_id: %s",
104+
str(e),
105+
delivery_id,
106+
)
69107
return jsonify({'status': 'error', 'message': 'Internal error'}), 500

0 commit comments

Comments
 (0)