Merge pull request #173 from mozilla/log-parsing-status

add log parsing status handling
This commit is contained in:
Mauro Doglio 2014-07-11 16:45:45 +01:00
Родитель 29e2d02bff 12e8977555
Коммит c8710cb5d6
14 изменённых файлов: 448 добавлений и 234 удалений

Просмотреть файл

@ -26,6 +26,6 @@ httplib2==0.7.4
jsonfield==0.9.20
git+git://github.com/jeads/datasource@143ac08d11
git+git://github.com/mozilla/treeherder-client@cc30664ad9
git+git://github.com/mozilla/treeherder-client@be6cb763dc
git+git://github.com/Julian/jsonschema@1976689051

Просмотреть файл

@ -1,8 +1,18 @@
import os
from os.path import dirname
import sys
from django.core.management import call_command
import json
import pytest
from django.core.management import call_command
from webtest.app import TestApp
from thclient.client import TreeherderRequest
from tests.sampledata import SampleData
from treeherder.etl.oauth_utils import OAuthCredentials
from treeherder.webapp.wsgi import application
def pytest_addoption(parser):
parser.addoption(
@ -309,3 +319,32 @@ def eleven_jobs_stored(jm, sample_data, sample_resultset):
def eleven_jobs_processed(jm, mock_log_parser, eleven_jobs_stored):
"""stores and processes list of 11 job samples"""
jm.process_objects(11, raise_errors=True)
@pytest.fixture
def mock_send_request(monkeypatch, jm):
def _send(th_request, endpoint, method=None, data=None):
OAuthCredentials.set_credentials(SampleData.get_credentials())
credentials = OAuthCredentials.get_credentials(jm.project)
th_request.oauth_key = credentials['consumer_key']
th_request.oauth_secret = credentials['consumer_secret']
if data and not isinstance(data, str):
data = json.dumps(data)
signed_uri = th_request.oauth_client.get_signed_uri(
data, th_request.get_uri(endpoint), method
)
response = getattr(TestApp(application), method.lower())(
str(signed_uri),
params=data,
content_type='application/json'
)
response.getcode = lambda: response.status_int
return response
monkeypatch.setattr(TreeherderRequest, 'send', _send)

Просмотреть файл

@ -1,16 +1,10 @@
import os
import pytest
import simplejson as json
from webtest.app import TestApp
from django.template import Context, Template
from thclient import (TreeherderJobCollection)
from treeherder.webapp.wsgi import application
from thclient import (TreeherderJobCollection, TreeherderRequest)
from treeherder.etl.oauth_utils import OAuthCredentials
from tests.sampledata import SampleData
from tests import test_utils
@ -91,27 +85,3 @@ def completed_jobs_loaded(jm, completed_jobs_stored):
jm.process_objects(1, raise_errors=True)
jm.disconnect()
@pytest.fixture
def mock_send_request(monkeypatch, jm):
def _send(th_request, th_collection):
OAuthCredentials.set_credentials(SampleData.get_credentials())
credentials = OAuthCredentials.get_credentials(jm.project)
th_request.oauth_key = credentials['consumer_key']
th_request.oauth_secret = credentials['consumer_secret']
signed_uri = th_request.get_signed_uri(
th_collection.to_json(), th_request.get_uri(th_collection)
)
response = TestApp(application).post_json(
str(signed_uri), params=th_collection.get_collection_data()
)
response.getcode = lambda: response.status_int
return response
monkeypatch.setattr(TreeherderRequest, 'send', _send)

Просмотреть файл

@ -26,8 +26,10 @@ def mock_post_json_data(monkeypatch, jm):
oauth_key=credentials['consumer_key'],
oauth_secret=credentials['consumer_secret']
)
signed_uri = tr.get_signed_uri(
th_collection.to_json(), tr.get_uri(th_collection)
signed_uri = tr.oauth_client.get_signed_uri(
th_collection.to_json(),
tr.get_uri(th_collection.endpoint_base),
"POST"
)
response = TestApp(application).post_json(

Просмотреть файл

@ -1,33 +0,0 @@
import pytest
from webtest.app import TestApp
from thclient import TreeherderRequest
from treeherder.etl.oauth_utils import OAuthCredentials
from treeherder.webapp.wsgi import application
from tests.sampledata import SampleData
@pytest.fixture
def mock_send_request(monkeypatch, jm):
def _send(th_request, th_collection):
OAuthCredentials.set_credentials(SampleData.get_credentials())
credentials = OAuthCredentials.get_credentials(jm.project)
th_request.oauth_key = credentials['consumer_key']
th_request.oauth_secret = credentials['consumer_secret']
signed_uri = th_request.get_signed_uri(
th_collection.to_json(), th_request.get_uri(th_collection)
)
response = TestApp(application).post_json(
str(signed_uri), params=th_collection.get_collection_data()
)
response.getcode = lambda: response.status_int
return response
monkeypatch.setattr(TreeherderRequest, 'send', _send)

Просмотреть файл

@ -1,17 +1,15 @@
import json
import itertools
from datadiff import diff
from webtest.app import TestApp, AppError
from sampledata import SampleData
from treeherder.model.derived.refdata import RefDataManager
from treeherder.etl.mixins import OAuthLoaderMixin
from treeherder.etl.oauth_utils import OAuthCredentials
from treeherder.webapp.wsgi import application
from thclient import TreeherderRequest
from tests.sampledata import SampleData
def post_collection(
project, th_collection, status=None, expect_errors=False,
consumer_key=None, consumer_secret=None):
@ -37,16 +35,19 @@ def post_collection(
oauth_secret=credentials['consumer_secret']
)
signed_uri = tr.get_signed_uri(
th_collection.to_json(), tr.get_uri(th_collection)
)
signed_uri = tr.oauth_client.get_signed_uri(
th_collection.to_json(),
tr.get_uri(th_collection.endpoint_base),
'POST'
)
response = TestApp(application).post_json(
str(signed_uri), params=th_collection.get_collection_data(), status=status
)
)
return response
def post_job_data(
project, uri, data, status=None, expect_errors=False):

Просмотреть файл

@ -129,7 +129,7 @@ class OAuthLoaderMixin(object):
oauth_secret=credentials.get('consumer_secret', None)
)
response = th_request.send(th_collections[project])
response = th_request.post(th_collections[project])
if not response or response.status != 200:
message = response.read()

Просмотреть файл

@ -59,7 +59,7 @@ def fetch_push_logs():
rdm.disconnect()
@task(name='fetch-hg-push-logs', time_limit=30)
@task(name='fetch-hg-push-logs', time_limit=60)
def fetch_hg_push_log(repo_name, repo_url):
"""
Run a HgPushlog etl process

Просмотреть файл

@ -8,7 +8,7 @@ http://docs.celeryproject.org/en/latest/userguide/canvas.html#guide-canvas
"""
import simplejson as json
import re
import urllib
import time
from celery import task
from django.conf import settings
@ -16,106 +16,138 @@ from django.core.urlresolvers import reverse
from thclient import TreeherderArtifactCollection, TreeherderRequest
from treeherder.log_parser.artifactbuildercollection import ArtifactBuilderCollection
from treeherder.log_parser.artifactbuildercollection import \
ArtifactBuilderCollection
from treeherder.log_parser.utils import (get_error_search_term,
get_crash_signature, get_bugs_for_search_term)
get_crash_signature,
get_bugs_for_search_term)
from treeherder.etl.oauth_utils import OAuthCredentials
@task(name='parse-log')
def parse_log(project, log_url, job_guid, resultset, check_errors=False):
def parse_log(project, job_log_url, job_guid, check_errors=False):
"""
Call ArtifactBuilderCollection on the given job.
"""
mozharness_pattern = re.compile(
'^\d+:\d+:\d+[ ]+(?:DEBUG|INFO|WARNING|ERROR|CRITICAL|FATAL) - [ ]?'
)
bugs_cache = {'open': {}, 'closed': {}}
bug_suggestions = {'open': {}, 'closed': {}}
bugscache_uri = '{0}{1}'.format(
settings.API_HOSTNAME,
reverse("bugscache-list")
)
credentials = OAuthCredentials.get_credentials(project)
req = TreeherderRequest(
protocol=settings.TREEHERDER_REQUEST_PROTOCOL,
host=settings.TREEHERDER_REQUEST_HOST,
project=project,
oauth_key=credentials.get('consumer_key', None),
oauth_secret=credentials.get('consumer_secret', None),
)
update_endpoint = 'job-log-url/{0}/update_parse_status'.format(job_log_url['id'])
if log_url:
# parse a log given its url
artifact_bc = ArtifactBuilderCollection(
log_url,
check_errors=check_errors,
try:
log_url = job_log_url['url']
mozharness_pattern = re.compile(
'^\d+:\d+:\d+[ ]+(?:DEBUG|INFO|WARNING|ERROR|CRITICAL|FATAL) - [ ]?'
)
artifact_bc.parse()
artifact_list = []
for name, artifact in artifact_bc.artifacts.items():
if name == 'talos_data':
data_type = 'performance'
if artifact[name]:
artifact_list.append(
(job_guid, name, data_type, json.dumps(artifact[name][0]))
)
else:
data_type = 'json'
artifact_list.append((job_guid, name, data_type, json.dumps(artifact)))
if check_errors:
all_errors = artifact_bc.artifacts.get(
'Structured Log', {}
).get(
'step_data', {}
bugs_cache = {'open': {}, 'closed': {}}
bug_suggestions = {'open': {}, 'closed': {}}
# return the resultset with the job id to identify if the UI wants
# to fetch the whole thing.
bugscache_uri = '{0}{1}'.format(
settings.API_HOSTNAME,
reverse("bugscache-list")
)
if log_url:
# parse a log given its url
artifact_bc = ArtifactBuilderCollection(
log_url,
check_errors=check_errors,
)
artifact_bc.parse()
artifact_list = []
for name, artifact in artifact_bc.artifacts.items():
artifact_list.append((job_guid, name, 'json', json.dumps(artifact)))
if check_errors:
all_errors = artifact_bc.artifacts.get(
'Structured Log', {}
).get(
'all_errors', [] )
'step_data', {}
).get(
'all_errors', [] )
for err in all_errors:
# remove the mozharness prefix
clean_line = mozharness_pattern.sub('', err['line']).strip()
# get a meaningful search term out of the error line
search_term = get_error_search_term(clean_line)
# collect open and closed bugs suggestions
for status in ('open', 'closed'):
if not search_term:
bug_suggestions[status][clean_line] = []
continue
if search_term not in bugs_cache[status]:
# retrieve the list of suggestions from the api
bugs_cache[status][search_term] = get_bugs_for_search_term(
search_term,
status,
bugscache_uri
)
# no suggestions, try to use the crash signature as search term
if not bugs_cache[status][search_term]:
crash_signature = get_crash_signature(search_term)
if crash_signature:
bugs_cache[status][search_term] = get_bugs_for_search_term(
search_term,
status,
bugscache_uri
)
bug_suggestions[status][clean_line] = bugs_cache[status][search_term]
for err in all_errors:
# remove the mozharness prefix
clean_line = mozharness_pattern.sub('', err['line']).strip()
# get a meaningful search term out of the error line
search_term = get_error_search_term(clean_line)
# collect open and closed bugs suggestions
for status in ('open', 'closed'):
if not search_term:
bug_suggestions[status][clean_line] = []
continue
if search_term not in bugs_cache[status]:
# retrieve the list of suggestions from the api
bugs_cache[status][search_term] = get_bugs_for_search_term(
search_term,
status,
bugscache_uri
)
# no suggestions, try to use the crash signature as search term
if not bugs_cache[status][search_term]:
crash_signature = get_crash_signature(search_term)
if crash_signature:
bugs_cache[status][search_term] = get_bugs_for_search_term(
search_term,
status,
bugscache_uri
)
bug_suggestions[status][clean_line] = bugs_cache[status][search_term]
artifact_list.append((job_guid, 'Open bugs', 'json', json.dumps(bug_suggestions['open'])))
artifact_list.append((job_guid, 'Closed bugs', 'json', json.dumps(bug_suggestions['closed'])))
# store the artifacts generated
tac = TreeherderArtifactCollection()
for artifact in artifact_list:
ta = tac.get_artifact({
"job_guid": artifact[0],
"name": artifact[1],
"type": artifact[2],
"blob": artifact[3]
})
tac.add(ta)
# store the artifacts generated
tac = TreeherderArtifactCollection()
for artifact in artifact_list:
ta = tac.get_artifact({
"job_guid": artifact[0],
"name": artifact[1],
"type": artifact[2],
"blob": artifact[3]
})
tac.add(ta)
req = TreeherderRequest(
protocol=settings.TREEHERDER_REQUEST_PROTOCOL,
host=settings.TREEHERDER_REQUEST_HOST,
project=project,
oauth_key=credentials.get('consumer_key', None),
oauth_secret=credentials.get('consumer_secret', None),
req.post(tac)
# send an update to job_log_url
# the job_log_url status changes
# from pending to running
current_timestamp = time.time()
status = 'parsed'
req.send(
update_endpoint,
method='POST',
data={
'parse_status': status,
'parse_timestamp': current_timestamp
}
)
except Exception, e:
# send an update to job_log_url
# the job_log_url status changes
# from pending to running
current_timestamp = time.time()
status = 'failed'
req.send(
update_endpoint,
method='POST',
data={
'parse_status': status,
'parse_timestamp': current_timestamp
}
)
req.send(tac)
# re raise the exception to leave a trace in the log
raise

Просмотреть файл

@ -21,7 +21,7 @@ from treeherder.events.publisher import JobStatusPublisher
from treeherder.etl.common import get_guid_root
from .base import TreeherderModelBase
from .base import TreeherderModelBase, ObjectNotFoundException
from datasource.DataHub import DataHub
@ -1850,9 +1850,6 @@ class JobsModel(TreeherderModelBase):
task['routing_key'] = 'parse_log.success'
tasks.append(task)
# a dict of result_set_id => push_timestamp
push_timestamp_lookup = self.get_push_timestamp_lookup(result_sets)
# Store the log references
self.get_jobs_dhub().execute(
proc='jobs.inserts.set_job_log_url',
@ -1860,18 +1857,67 @@ class JobsModel(TreeherderModelBase):
placeholders=log_placeholders,
executemany=True)
# I need to find the jog_log_url ids
# just inserted but there's no unique key.
# Also, the url column is not indexed, so it's
# not a good idea to search based on that.
# I'm gonna retrieve the logs by job ids and then
# use their url to create a map.
job_ids = [j["id"] for j in job_id_lookup.values()]
job_log_url_list = self.get_job_log_url_list(job_ids)
log_url_lookup = dict([(jlu['url'], jlu)
for jlu in job_log_url_list])
for task in tasks:
parse_log.apply_async(
args=[
self.project,
task['log_url'],
log_url_lookup[task['log_url']],
task['job_guid'],
push_timestamp_lookup[task['result_set_id']]
],
kwargs={'check_errors': task['check_errors']},
routing_key=task['routing_key']
)
def get_job_log_url_detail(self, job_log_url_id):
obj = self.get_jobs_dhub().execute(
proc='jobs.selects.get_job_log_url_detail',
debug_show=self.DEBUG,
placeholders=[job_log_url_id])
if len(obj) == 0:
raise ObjectNotFoundException("job_log_url", id=job_log_url_id)
return obj[0]
def get_job_log_url_list(self, job_ids):
"""
Return a list of logs belonging to the given job_id(s).
"""
if len(job_ids) == 0:
return []
replacement = []
id_placeholders = ["%s"] * len(job_ids)
replacement.append(','.join(id_placeholders))
data = self.get_jobs_dhub().execute(
proc="jobs.selects.get_job_log_url_list",
placeholders=job_ids,
replace=replacement,
debug_show=self.DEBUG,
)
return data
def update_job_log_url_status(self, job_log_url_id,
parse_status, parse_timestamp):
self.get_jobs_dhub().execute(
proc='jobs.updates.update_job_log_url',
debug_show=self.DEBUG,
placeholders=[parse_status, parse_timestamp, job_log_url_id])
def store_job_artifact(self, artifact_placeholders):
"""

Просмотреть файл

@ -51,7 +51,6 @@
},
"inserts":{
"create_job_data":{
"sql":"INSERT INTO `job` (
`job_guid`,
`signature`,
@ -294,6 +293,14 @@
",
"host":"master_host"
},
"update_job_log_url":{
"sql":"UPDATE `job_log_url`
SET `parse_status` = ?, `parse_timestamp` = ?
WHERE `id` = ?
AND `active_status` = 'active'",
"host":"master_host"
}
},
"selects":{
@ -589,6 +596,19 @@
"host":"read_host"
},
"get_job_log_url_detail":{
"sql": "SELECT `id`, `job_id`, `name`, `url`, `parse_status`, `parse_timestamp`
FROM job_log_url
WHERE id = ? and active_status = 'active'",
"host":"read_host"
},
"get_job_log_url_list":{
"sql": "SELECT `id`, `job_id`, `name`, `url`, `parse_status`, `parse_timestamp`
FROM job_log_url
WHERE job_id in (REP0)
and active_status='active'",
"host": "read_host"
},
"get_job_artifact":{
"sql":"SELECT
id,

Просмотреть файл

@ -0,0 +1,92 @@
from rest_framework import viewsets
from rest_framework.response import Response
from rest_framework.exceptions import ParseError
from rest_framework.decorators import action
from rest_framework.permissions import IsAuthenticated
from treeherder.webapp.api.utils import with_jobs, oauth_required
class JobLogUrlViewSet(viewsets.ViewSet):
"""
A job_log_url object holds a reference to a job log.
"""
@with_jobs
def retrieve(self, request, project, jm, pk=None):
"""
Returns a job_log_url object given its ID
"""
obj = jm.get_job_log_url_detail(pk)
return Response(obj)
@with_jobs
def list(self, request, project, jm):
"""
GET method implementation for list view
job_id -- Mandatory filter indicating which job these log belongs to.
"""
job_id = request.QUERY_PARAMS.get('job_id')
if not job_id:
raise ParseError(detail="The job_id parameter is mandatory for this endpoint")
try:
job_id = int(job_id)
except ValueError:
raise ParseError(detail="The job_id parameter must be an integer")
job_note_list = jm.get_job_log_url_list(job_id=job_id)
return Response(job_note_list)
@with_jobs
def list(self, request, project, jm):
"""
GET method implementation for list view
job_id -- Mandatory filter indicating which job these log belongs to.
"""
job_id = request.QUERY_PARAMS.get('job_id')
if not job_id:
raise ParseError(detail="The job_id parameter is mandatory for this endpoint")
try:
job_id = int(job_id)
except ValueError:
raise ParseError(detail="The job_id parameter must be an integer")
# get_job_log_url_list takes a lost of job ids
job_log_url_list = jm.get_job_log_url_list([job_id])
return Response(job_log_url_list)
@action()
@with_jobs
@oauth_required
def update_parse_status(self, request, project, jm, pk=None):
"""
Change the state of a job.
"""
try:
parse_status = request.DATA["parse_status"]
parse_timestamp = request.DATA["parse_timestamp"]
jm.update_job_log_url_status(pk, parse_status, parse_timestamp)
obj = jm.get_job_log_url_detail(pk)
return Response(obj)
except KeyError:
raise ParseError(detail=("The parse_status and parse_timestamp parameters"
" are mandatory for this endpoint"))
@action(permission_classes=[IsAuthenticated])
@with_jobs
def parse(self, request, project, jm, pk=None):
"""
Trigger an async task to parse this log. This can be requested by the ui
in case the log parsing had an intermittent failure
"""
log_obj = jm.get_job_log_url_detail(pk)
job = jm.get_job(log_obj["job_id"])
has_failed = job["result"] in jm.FAILED_RESULTS
from treeherder.log_parser.tasks import parse_log
parse_log.delay(project, log_obj["url"],
job["job_guid"], job["resultset_id"],
check_errors=has_failed)
return Response({"message": "Log parsing triggered successfully"})

Просмотреть файл

@ -1,6 +1,7 @@
from django.conf.urls import patterns, include, url
from treeherder.webapp.api import (refdata, objectstore, jobs, resultset,
artifact, note, revision, bug, logslice)
artifact, note, revision, bug, logslice,
job_log_url)
from rest_framework import routers
@ -56,6 +57,12 @@ project_bound_router.register(
base_name='logslice',
)
project_bound_router.register(
r'job-log-url',
job_log_url.JobLogUrlViewSet,
base_name='job-log-url',
)
# this is the default router for plain restful endpoints
# refdata endpoints:

164
vendor/thclient/client.py поставляемый
Просмотреть файл

@ -6,7 +6,9 @@ from __future__ import unicode_literals
import httplib
import oauth2 as oauth
import time
import urllib
import logging
logger = logging.getLogger(__name__)
try:
import json
@ -219,11 +221,11 @@ class TreeherderJob(TreeherderData, ValidatorMixin):
def add_artifact(self, name, artifact_type, blob):
if blob:
self.data['job']['artifact'] = {
'name': name,
'type': artifact_type,
'blob': blob
}
self.data['job']['artifacts'].append({
'name': name,
'type': artifact_type,
'blob': blob
})
def init_data(self):
@ -315,11 +317,7 @@ class TreeherderJob(TreeherderData, ValidatorMixin):
# project_jobs_1.job_artifact.name
# project_jobs_1.job_artifact.type
# project_jobs_1.job_artifact.blob
'artifact': {
'name': '',
'type': '',
'blob': ''
}
'artifacts': []
},
# List of job_guids that were coallesced to this job
@ -411,11 +409,11 @@ class TreeherderResultSet(TreeherderData, ValidatorMixin):
'type': '',
# TODO: add resultset artifact table in treeherder-service
'artifact': {
'type': '',
'name': '',
'blob': ''
}
'name': "",
'type': "",
'blob': ""
}
}
def add_push_timestamp(self, push_timestamp):
self.data['push_timestamp'] = push_timestamp
@ -443,10 +441,10 @@ class TreeherderResultSet(TreeherderData, ValidatorMixin):
def add_artifact(self, name, artifact_type, blob):
if blob:
self.data['artifact'] = {
'name': name,
'type': artifact_type,
'blob': blob
}
'name': name,
'type': artifact_type,
'blob': blob
}
def get_revision(self, data={}):
return TreeherderRevision(data)
@ -585,6 +583,47 @@ class TreeherderArtifactCollection(TreeherderCollection):
return TreeherderArtifact(data)
class OauthClient(object):
"""
A utility class containing the logic to sign a oauth request
"""
def __init__(self, oauth_key, oauth_secret, user):
self.oauth_key = oauth_key
self.oauth_secret = oauth_secret
self.user = user
def get_signed_uri(self, serialized_body, uri, http_method):
# There is no requirement for the token in two-legged
# OAuth but we still need the token object.
token = oauth.Token(key='', secret='')
consumer = oauth.Consumer(key=self.oauth_key, secret=self.oauth_secret)
parameters = {
'user': self.user,
'oauth_version': '1.0',
'oauth_nonce': oauth.generate_nonce(),
'oauth_timestamp': int(time.time())
}
try:
req = oauth.Request(
method=http_method,
body=serialized_body,
url=uri,
parameters=parameters
)
except AssertionError, e:
logger.error('uri: %s' % uri)
logger.error('body: %s' % serialized_body)
raise
signature_method = oauth.SignatureMethod_HMAC_SHA1()
req.sign_request(signature_method, consumer, token)
return req.to_url()
class TreeherderRequest(object):
"""
Treeherder request object that manages test submission.
@ -604,6 +643,10 @@ class TreeherderRequest(object):
self.project = project
self.oauth_key = oauth_key
self.oauth_secret = oauth_secret
self.use_oauth = bool(self.oauth_key and self.oauth_secret)
self.oauth_client = None
if self.use_oauth:
self.oauth_client = OauthClient(oauth_key, oauth_secret, self.project)
if protocol not in self.protocols:
raise AssertionError('Protocol "%s" not supported; please use one of %s' %
@ -615,9 +658,11 @@ class TreeherderRequest(object):
msg = "{0}: project required for posting".format(self.__class__.__name__)
raise TreeherderClientError(msg, [])
def send(self, collection_inst):
"""
Send given treeherder collection instance data to server; returns httplib Response.
def post(self, collection_inst):
"""Shortcut method to send a treeherder collection via POST
:param collection_inst: a TreeherderCollection instance
:returns: an httplib Response object
"""
if not isinstance(collection_inst, TreeherderCollection):
@ -642,65 +687,58 @@ class TreeherderRequest(object):
collection_inst.validate()
return self.send(collection_inst.endpoint_base,
method="POST",
data=collection_inst.to_json())
def send(self, endpoint, method=None, data=None):
"""send data to the given endpoint with the given http method.
:param endpoint: the target endpoint for this request
:param method: can be one of GET,POST,PUT
:param data: the body of this request
:returns: an httplib Response object
"""
if method not in ("GET", "POST", "PUT"):
msg = "{0}: {1} is not a supported method".format(
self.__class__.__name__,
method
)
raise TreeherderClientError(msg, [])
# Build the header
headers = {'Content-Type': 'application/json'}
use_oauth = bool(self.oauth_key and self.oauth_secret)
if data:
if not isinstance(data, str):
# if the body is not serialized yet, do it now
serialized_body = json.dumps(data)
else:
serialized_body = data
else:
serialized_body = None
serialized_body = collection_inst.to_json()
uri = self.get_uri(endpoint)
uri = self.get_uri(collection_inst)
if self.use_oauth:
uri = self.oauth_client.get_signed_uri(serialized_body, uri, method)
if use_oauth:
uri = self.get_signed_uri(serialized_body, uri)
# Make the POST request
# Make the request
conn = None
if self.protocol == 'http':
conn = httplib.HTTPConnection(self.host)
else:
conn = httplib.HTTPSConnection(self.host)
conn.request('POST', uri, serialized_body, headers)
conn.request(method, uri, serialized_body, headers)
return conn.getresponse()
def get_signed_uri(self, serialized_body, uri):
# There is no requirement for the token in two-legged
# OAuth but we still need the token object.
token = oauth.Token(key='', secret='')
consumer = oauth.Consumer(key=self.oauth_key, secret=self.oauth_secret)
parameters = {
'user':self.project,
'oauth_version':'1.0',
'oauth_nonce':oauth.generate_nonce(),
'oauth_timestamp':int(time.time())
}
try:
req = oauth.Request(
method='POST',
body=serialized_body,
url=uri,
parameters=parameters
)
except AssertionError, e:
print 'uri: %s' % uri
print 'body: %s' % serialized_body
raise
signature_method = oauth.SignatureMethod_HMAC_SHA1()
req.sign_request(signature_method, consumer, token)
return req.to_url()
def get_uri(self, collection_inst):
def get_uri(self, endpoint):
uri = '{0}://{1}/api/project/{2}/{3}/'.format(
self.protocol, self.host, self.project, collection_inst.endpoint_base
self.protocol, self.host, self.project, endpoint
)
return uri