mirror of
https://github.com/ansible/awx.git
synced 2026-05-20 07:17:40 -02:30
Add logic to post the job status for webhooks back to the service
under some circumstances.
This commit is contained in:
@@ -2,6 +2,7 @@ from hashlib import sha1
|
|||||||
import hmac
|
import hmac
|
||||||
import json
|
import json
|
||||||
import logging
|
import logging
|
||||||
|
import urllib.parse
|
||||||
|
|
||||||
from django.utils.encoding import force_bytes
|
from django.utils.encoding import force_bytes
|
||||||
from django.views.decorators.csrf import csrf_exempt
|
from django.views.decorators.csrf import csrf_exempt
|
||||||
@@ -53,7 +54,7 @@ class WebhookReceiverBase(APIView):
|
|||||||
permission_classes = (AllowAny,)
|
permission_classes = (AllowAny,)
|
||||||
authentication_classes = ()
|
authentication_classes = ()
|
||||||
|
|
||||||
event_keys = {}
|
ref_keys = {}
|
||||||
|
|
||||||
def get_queryset(self):
|
def get_queryset(self):
|
||||||
qs_models = {
|
qs_models = {
|
||||||
@@ -83,8 +84,11 @@ class WebhookReceiverBase(APIView):
|
|||||||
def get_event_guid(self):
|
def get_event_guid(self):
|
||||||
raise NotImplementedError
|
raise NotImplementedError
|
||||||
|
|
||||||
|
def get_event_status_api(self):
|
||||||
|
raise NotImplementedError
|
||||||
|
|
||||||
def get_event_ref(self):
|
def get_event_ref(self):
|
||||||
key = self.event_keys.get(self.get_event_type(), '')
|
key = self.ref_keys.get(self.get_event_type(), '')
|
||||||
value = self.request.data
|
value = self.request.data
|
||||||
for element in key.split('.'):
|
for element in key.split('.'):
|
||||||
try:
|
try:
|
||||||
@@ -126,6 +130,7 @@ class WebhookReceiverBase(APIView):
|
|||||||
event_type = self.get_event_type()
|
event_type = self.get_event_type()
|
||||||
event_guid = self.get_event_guid()
|
event_guid = self.get_event_guid()
|
||||||
event_ref = self.get_event_ref()
|
event_ref = self.get_event_ref()
|
||||||
|
status_api = self.get_event_status_api()
|
||||||
|
|
||||||
kwargs = {
|
kwargs = {
|
||||||
'webhook_service': obj.webhook_service,
|
'webhook_service': obj.webhook_service,
|
||||||
@@ -147,11 +152,10 @@ class WebhookReceiverBase(APIView):
|
|||||||
'tower_webhook_event_type': event_type,
|
'tower_webhook_event_type': event_type,
|
||||||
'tower_webhook_event_guid': event_guid,
|
'tower_webhook_event_guid': event_guid,
|
||||||
'tower_webhook_event_ref': event_ref,
|
'tower_webhook_event_ref': event_ref,
|
||||||
|
'tower_webhook_status_api': status_api,
|
||||||
'tower_webhook_payload': request.data,
|
'tower_webhook_payload': request.data,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
# if event_ref:
|
|
||||||
# kwargs['scm_branch'] = event_ref
|
|
||||||
|
|
||||||
new_job = obj.create_unified_job(**kwargs)
|
new_job = obj.create_unified_job(**kwargs)
|
||||||
new_job.signal_start()
|
new_job.signal_start()
|
||||||
@@ -162,7 +166,7 @@ class WebhookReceiverBase(APIView):
|
|||||||
class GithubWebhookReceiver(WebhookReceiverBase):
|
class GithubWebhookReceiver(WebhookReceiverBase):
|
||||||
service = 'github'
|
service = 'github'
|
||||||
|
|
||||||
event_keys = {
|
ref_keys = {
|
||||||
'pull_request': 'pull_request.head.sha',
|
'pull_request': 'pull_request.head.sha',
|
||||||
'pull_request_review': 'pull_request.head.sha',
|
'pull_request_review': 'pull_request.head.sha',
|
||||||
'pull_request_review_comment': 'pull_request.head.sha',
|
'pull_request_review_comment': 'pull_request.head.sha',
|
||||||
@@ -179,6 +183,11 @@ class GithubWebhookReceiver(WebhookReceiverBase):
|
|||||||
def get_event_guid(self):
|
def get_event_guid(self):
|
||||||
return self.request.META.get('HTTP_X_GITHUB_DELIVERY')
|
return self.request.META.get('HTTP_X_GITHUB_DELIVERY')
|
||||||
|
|
||||||
|
def get_event_status_api(self):
|
||||||
|
if self.get_event_type() != 'pull_request':
|
||||||
|
return
|
||||||
|
return self.request.data.get('pull_request', {}).get('statuses_url')
|
||||||
|
|
||||||
def get_signature(self):
|
def get_signature(self):
|
||||||
header_sig = self.request.META.get('HTTP_X_HUB_SIGNATURE')
|
header_sig = self.request.META.get('HTTP_X_HUB_SIGNATURE')
|
||||||
if not header_sig:
|
if not header_sig:
|
||||||
@@ -192,7 +201,7 @@ class GithubWebhookReceiver(WebhookReceiverBase):
|
|||||||
class GitlabWebhookReceiver(WebhookReceiverBase):
|
class GitlabWebhookReceiver(WebhookReceiverBase):
|
||||||
service = 'gitlab'
|
service = 'gitlab'
|
||||||
|
|
||||||
event_keys = {
|
ref_keys = {
|
||||||
'Push Hook': 'checkout_sha',
|
'Push Hook': 'checkout_sha',
|
||||||
'Tag Push Hook': 'checkout_sha',
|
'Tag Push Hook': 'checkout_sha',
|
||||||
'Merge Request Hook': 'object_attributes.last_commit.id',
|
'Merge Request Hook': 'object_attributes.last_commit.id',
|
||||||
@@ -207,6 +216,18 @@ class GitlabWebhookReceiver(WebhookReceiverBase):
|
|||||||
h.update(force_bytes(self.request.body))
|
h.update(force_bytes(self.request.body))
|
||||||
return h.hexdigest()
|
return h.hexdigest()
|
||||||
|
|
||||||
|
def get_event_status_api(self):
|
||||||
|
if self.get_event_type() != 'Merge Request Hook':
|
||||||
|
return
|
||||||
|
project = self.request.data.get('project', {})
|
||||||
|
repo_url = project.get('web_url')
|
||||||
|
if not repo_url:
|
||||||
|
return
|
||||||
|
parsed = urllib.parse.urlparse(repo_url)
|
||||||
|
|
||||||
|
return "{}://{}/projects/{}/repository/commits/{}/statuses".format(
|
||||||
|
parsed.scheme, parsed.netloc, project['id'], self.get_event_ref())
|
||||||
|
|
||||||
def get_signature(self):
|
def get_signature(self):
|
||||||
return force_bytes(self.request.META.get('HTTP_X_GITLAB_TOKEN'))
|
return force_bytes(self.request.META.get('HTTP_X_GITLAB_TOKEN'))
|
||||||
|
|
||||||
@@ -224,7 +245,7 @@ class GitlabWebhookReceiver(WebhookReceiverBase):
|
|||||||
class BitbucketWebhookReceiver(WebhookReceiverBase):
|
class BitbucketWebhookReceiver(WebhookReceiverBase):
|
||||||
service = 'bitbucket'
|
service = 'bitbucket'
|
||||||
|
|
||||||
event_keys = {
|
ref_keys = {
|
||||||
# Bitbucket Server
|
# Bitbucket Server
|
||||||
'repo:refs_changed': 'changes.0.toHash',
|
'repo:refs_changed': 'changes.0.toHash',
|
||||||
'repo:comment:added': 'commit',
|
'repo:comment:added': 'commit',
|
||||||
|
|||||||
@@ -1,8 +1,10 @@
|
|||||||
# Python
|
# Python
|
||||||
import os
|
|
||||||
import json
|
|
||||||
from copy import copy, deepcopy
|
from copy import copy, deepcopy
|
||||||
|
import json
|
||||||
|
import logging
|
||||||
|
import os
|
||||||
|
|
||||||
|
import requests
|
||||||
|
|
||||||
# Django
|
# Django
|
||||||
from django.apps import apps
|
from django.apps import apps
|
||||||
@@ -27,6 +29,9 @@ from awx.main.fields import JSONField, AskForField
|
|||||||
from awx.main.constants import ACTIVE_STATES
|
from awx.main.constants import ACTIVE_STATES
|
||||||
|
|
||||||
|
|
||||||
|
logger = logging.getLogger('awx.main.models.mixins')
|
||||||
|
|
||||||
|
|
||||||
__all__ = ['ResourceMixin', 'SurveyJobTemplateMixin', 'SurveyJobMixin',
|
__all__ = ['ResourceMixin', 'SurveyJobTemplateMixin', 'SurveyJobMixin',
|
||||||
'TaskManagerUnifiedJobMixin', 'TaskManagerJobMixin', 'TaskManagerProjectUpdateMixin',
|
'TaskManagerUnifiedJobMixin', 'TaskManagerJobMixin', 'TaskManagerProjectUpdateMixin',
|
||||||
'TaskManagerInventoryUpdateMixin', 'CustomVirtualEnvMixin']
|
'TaskManagerInventoryUpdateMixin', 'CustomVirtualEnvMixin']
|
||||||
@@ -553,3 +558,29 @@ class WebhookMixin(models.Model):
|
|||||||
blank=True,
|
blank=True,
|
||||||
max_length=128
|
max_length=128
|
||||||
)
|
)
|
||||||
|
|
||||||
|
def update_scm_status(self, status):
|
||||||
|
if not self.webhook_credential:
|
||||||
|
logger.debug("No credential configured to post back webhook status, skipping.")
|
||||||
|
return
|
||||||
|
|
||||||
|
status_api = self.extra_vars_dict.get('tower_webhook_status_api')
|
||||||
|
if not status_api:
|
||||||
|
logger.debug("Webhook event did not have a status API endpoint associated, skipping.")
|
||||||
|
return
|
||||||
|
|
||||||
|
service_header = {
|
||||||
|
'github': 'Authorization',
|
||||||
|
'gitlab': 'PRIVATE-TOKEN',
|
||||||
|
}
|
||||||
|
try:
|
||||||
|
headers = {service_header[self.webhook_service]: self.webhook_credential.get_input('token')}
|
||||||
|
response = requests.post(status_api, headers=headers)
|
||||||
|
except Exception:
|
||||||
|
logger.exception("Posting webhook status caused an error.")
|
||||||
|
return
|
||||||
|
|
||||||
|
if response.status_code < 400:
|
||||||
|
logger.debug("Webhook status update sent.")
|
||||||
|
else:
|
||||||
|
logger.debug("Posting webhook status failed, code: {}".format(response.status_code))
|
||||||
|
|||||||
@@ -1422,3 +1422,6 @@ class UnifiedJob(PolymorphicModel, PasswordFieldsModel, CommonModelNameNotUnique
|
|||||||
|
|
||||||
def is_isolated(self):
|
def is_isolated(self):
|
||||||
return bool(self.controller_node)
|
return bool(self.controller_node)
|
||||||
|
|
||||||
|
def update_scm_status(self, status):
|
||||||
|
return
|
||||||
|
|||||||
Reference in New Issue
Block a user