Exemplo n.º 1
0
def api_v1_org(org):
    """Organization overview. Returns a list of repositories that have been
    run through the system, with their most recent created date and run number.

    :param org: The GitHub username or organization (e.g. 'puppetlabs')
    :return: JSON containing an organization's repositories known to the system.
    """
    response = es.search(q="source:{} AND payload.repository.owner.name:{}".format('jenkins-hookshot', org))

    repos = []
    for hit in response['hits']['hits']:
        repos.append(hit['_source']['payload']['repository']['full_name'])

    result = []
    for repo in set(repos):
        last_run_uuid = redis.lindex(repo, -1).decode('utf-8')
        last_run_num = get_run_number(repo, last_run_uuid)

        resp = es.search(q="source:{} AND id:{}".format('jenkins-hookshot', last_run_uuid))
        hit = resp['hits']['hits'][0]['_source']
        repo = hit['repo']
        last_run_timestamp = hit['@timestamp']

        result.append(dict(
            name=repo,
            last_run_uuid=last_run_uuid,
            last_run_number=last_run_num,
            last_run_timestamp=last_run_timestamp,
        ))

    return jsonify(dict(org=org, repos=sorted(result, key=itemgetter('name'))))
Exemplo n.º 2
0
def api_v1_repo(org, repo):
    """Repo overview. For the given repository (e.g. 'puppetlabs/puppet'),
    return a list of runs.

    :param org: GitHub username or organization (e.g. 'puppetlabs')
    :param repo: GitHub repository name (e.g. 'puppet')
    :return: JSON containing runs known to the system.
    """

    # Note: here we're using the fullname of the repo, e.g. 'puppetlabs/puppet',
    # as is GitHub's convention. However, Flask sees the '/' character as a path
    # separator, so it has to be two individual arguments that we later combine.
    repo_full_name = '/'.join([org, repo])
    result = []
    response = es.search(q="source:{} AND payload.repository.full_name:\"{}\"".format('jenkins-hookshot', repo_full_name))

    # TODO: limit the output of this endpoint to the last N runs
    for hit in response['hits']['hits']:
        hit = hit['_source']
        uuid = hit['id']
        number = get_run_number(repo_full_name, uuid)
        timestamp = hit['@timestamp']
        head_sha = hit['payload']['head_commit']['id']
        ref = hit['payload']['ref']

        result.append(dict(
            run_uuid=uuid,
            run_number=number,
            timestamp=timestamp,
            head_sha=head_sha,
            ref=ref,
        ))

    return jsonify(repo=repo_full_name, runs=sorted(result, key=itemgetter('run_number'), reverse=True))
Exemplo n.º 3
0
def api_v1_recent():
    """Query Redis and Elasticsearch and return some basic info on recent runs.

    :return: JSON containing recent run info.
    """
    result = []

    # Query Redis for all the UUIDs stored in the 'recent' list
    # TODO: limit the output of this endpoint to the last N runs
    # TODO: Alternately, set a TTL on the items in the 'recent' list in Redis
    for run_uuid in redis.lrange('recent', 0, -1):
        run_uuid = run_uuid.decode('utf-8')
        response = es.search(q="source:{} AND id:{}".format('jenkins-hookshot', run_uuid), size=1)
        hook_payload = response['hits']['hits'][0]['_source']
        repo = hook_payload['payload']['repository']['full_name']

        result.append(dict(
            run_uuid=run_uuid,
            timestamp=hook_payload['@timestamp'],
            org=repo.split('/')[0],
            repo=repo.split('/')[1],
            head_sha=hook_payload['payload']['head_commit']['id'],
            run_number=get_run_number(repo, run_uuid),
            ref=hook_payload['payload']['ref']
        ))

    return jsonify(runs=result)
Exemplo n.º 4
0
def api_v1_run_job_log(org, repo, run_number, job_name):
    """Return a plaintext (raw) console log for a particular job.

    :param org: GitHub organization or username (e.g. 'puppetlabs')
    :param repo: GitHub repository name (e.g. 'puppet')
    :param run_number: A specific build number (integer)
    :param job_name: Job name (string)
    :return: JSON containing job info
    """
    # The Jenkins Logstash plugin creates a new item in an array for each line
    # of the Jenkins console log output. Because it takes so long for Javascript
    # in the user's browser to try and join the array, we do it here.
    run_uuid = get_run_uuid('/'.join([org, repo]), run_number)
    job_resp = es.search(q="source:{} AND data.rootProjectName:{}".format(
        'jenkins', '__'.join([run_uuid, job_name])), _source_include="message", size=1)

    log = '\n'.join(job_resp['hits']['hits'][0]['_source']['message'])
    return log, 200, { 'Content-Type': 'text/plain; charset=utf-8' }
Exemplo n.º 5
0
def api_v1_run_job(org, repo, run_number, job_name):
    """Return info about a particular job in a run, excluding the console log.

    :param org: GitHub organization or username (e.g. 'puppetlabs')
    :param repo: GitHub repository name (e.g. 'puppet')
    :param run_number: A specific build number (integer)
    :param job_name: Job name (string)
    :return: JSON containing job info
    """
    run_uuid = get_run_uuid('/'.join([org, repo]), run_number)
    job_resp = es.search(q="source:{} AND data.rootProjectName:{}".format(
        'jenkins', '__'.join([run_uuid, job_name])), _source_exclude="message", size=1)

    job = job_resp['hits']['hits'][0]['_source']['data']

    if 'testResults' in job:
        test_results = dict(
            total=job['testResults']['totalCount'],
            skipped=job['testResults']['skipCount'],
            failed=job['testResults']['failCount'],
            passed=job['testResults']['totalCount'] - job['testResults']['skipCount'] - job['testResults']['failCount'],
            tests=dict(
                failed=job['testResults']['failedTests'],
            ),
        )
    else:
        test_results = {}

    return jsonify(dict(
        org=org,
        repo=repo,
        run_number=run_number,
        run_uuid=run_uuid,
        name=job_name,
        timestamp=job['timestamp'],
        duration=millis_to_secs(job['buildDuration']),
        status=job['result'].lower(),
        test_results=test_results,
    ))
Exemplo n.º 6
0
def api_v1_run(org, repo, run_number):
    """Given a specific repo and run number, return info on the run and the
    jobs in the run.

    :param org: GitHub organization or username (e.g. 'puppetlabs')
    :param repo: GitHub repository name (e.g. 'puppet')
    :param run_number: A specific build number (integer)
    :return: JSON containing run info
    """
    run_uuid = get_run_uuid('/'.join([org, repo]), run_number)
    hook_resp = es.search(q="source:{} AND id:{}".format('jenkins-hookshot', run_uuid), size=1)
    hook_payload = hook_resp['hits']['hits'][0]['_source']['payload']

    commits = []
    for commit in hook_payload['commits']:
        commits.append(dict(
            author=dict(
                email=commit['author']['email'],
                name=commit['author']['name'],
                github_username=commit['author']['username'],
            ),
            comitter=dict(
                email=commit['committer']['email'],
                name=commit['committer']['name'],
                github_username=commit['committer']['username'],
            ),
            sha=commit['id'],
            subject=commit['message'].split('\n')[0],
            message='\n'.join(commit['message'].split('\n')[2::]),
            timestamp=commit['timestamp'],
            url=commit['url'],
        ))

    # Get job data, excluding the console log. Limit jobs to 500.
    job_resp = es.search(q="source:{} AND data.rootProjectName:{}".format(
        'jenkins', run_uuid), _source_exclude="message", size=500)

    jobs = []
    for hit in job_resp['hits']['hits']:
        hit = hit['_source']['data']
        job = {}

        # As mentioned in the "caveats" section of the README, we assume that
        # jobs are named using '__' as a separator between the UUID and whatever
        # arbitrary job name the user decides to create. As such, '__' should
        # *never* be used after the UUID.
        #
        # I tried experimenting with different separators, but the separator
        # needs to be compatible with the URLs jenkins-hookshot generates to
        # POST the seed job config to Jenkins. Double underscore seemed sane.
        # -- roger, 2015-02-12
        #
        job['name'] = hit['rootProjectName'].split('__')[-1]
        job['timestamp'] = hit['timestamp']
        job['duration'] = millis_to_secs(hit['buildDuration'])
        job['status'] = hit['result'].lower()
        job['build_variables'] = hit['buildVariables']
        job['cell'] = hit['projectName']
        jobs.append(job)

    return jsonify(dict(
        org=org,
        repo=repo,
        repo_description=hook_payload['repository']['description'],
        repo_url=hook_payload['repository']['url'],
        org_url=hook_payload['sender']['html_url'],
        head_sha=hook_payload['head_commit']['id'],
        ref=hook_payload['ref'],
        commits=commits,
        run_number=run_number,
        run_uuid=run_uuid,
        jobs=jobs,
    ))