Python: requests, JFrog Artifactory и деплой

Автор: | 01/26/2016
 

PythonПример скрипта деплоя.

Задача скрипта — сохранить результаты билда в Artifactory и деплоить из Artifactory на хост с JBoss.

Из «особенностей» — результатом билда являются два *.ear архива разных типов:

  • ${GIT_COMMIT}-${VERSION}-unlock-service.ear — бекенд из одного репозитория;
  • ${GIT_COMMIT}-${VERSION}-unlock-ui.ear — фронтенд из второго репозитория.

${GIT_COMMIT} грепается из git log${VERSION} — из файла build.gradle, в котором девелоперы устанавливают версию.

В Artifactory они сохраняются в два разных каталога.

При деплое из Artifactory на хост — по умолчанию выбирается последний модифицированный артефакт из репозитория (?lastModified) — но можно добавить переменную $DEPLOY_VERSION в билд Jenkins-а и указать имя артефакта для деплоя.

Собственно скрипт:

#!/usr/bin/env python

from __future__ import with_statement

import os
import subprocess
import re
import requests
import argparse
import json

from fabric.api import run, env, put, sudo, hide

# hate globals
jfrog_url = 'https://project.artifactoryonline.com/project/'
jfrog_api = 'api/storage'
jfrog_repo = 'project-releases-local'
jfrog_dir = 'repo'

jfrog_user = 'username'
jfrog_pswd = 'password'

def getopts():

    # e.g. /var/lib/jenkins/jobs/Project.UI.Build/workspace/
    jenkins_home = os.environ['WORKSPACE']

    parser = argparse.ArgumentParser()

    parser.add_argument('-t',
                        '--type',
                        action='store',
                        help='"UI" or "Service" to deploy to Artifactory or JBoss instance')

    parser.add_argument('-a',
                        '--artifactory',
                        action='store_true',
                        help='Deploy build results to Artifactory')

    parser.add_argument('-j',
                        '--jboss',
                        action='store_true',
                        help='Deploy build results to JBoss host')

    parser.add_argument('-H',
                        '--host',
                        action='store',
                        help='JBoss host to deploy to.')

    parser.add_argument('-T',
                        '--test',
                        action='store_true',
                        help='Test functions option. Use it to run new functionality.')

    res = parser.parse_args()

    if res.type == 'UI':
        if res.artifactory:
            artifact_path = os.path.join(jenkins_home, 'unlock-ear', 'build', 'libs')
            artifact_to_art = versioning(res.type, artifact_path) + 'unlock-ui.ear'
        jfrog_repo_type = 'ui'
    elif res.type == 'Service':
        if res.artifactory:
            artifact_path = os.path.join(jenkins_home, 'unlock-service-ear', 'build', 'libs')
            artifact_to_art = versioning(res.type, artifact_path) + 'unlock-service.ear'
        jfrog_repo_type = 'service'
    else:
        print ('ERROR: Unknown deploy type. Please set "UI" or "Service". Exit.')
        exit(1)

    if res.artifactory:
        deploy_to_art(res.type, jfrog_repo_type, artifact_path, artifact_to_art)
    if res.jboss:
        deploy_to_env(res.type, res.host, jfrog_repo_type)
    if res.test:
        getlast(res.type, jfrog_repo_type)


def versioning(res_type, artifact_path):

    # "1.0.0-SNAPSHOT" will be parsed from here
    #
    if res_type == 'UI':
        gradlefile = 'build.gradle'
        to_grab = 'version '
    elif res_type == 'Service':
        gradlefile = os.path.join('unlock-service-ear', 'build.gradle')
        to_grab = 'version = '
    else:
        print('ERROR: unknown res_type! Exit.n')
        exit(1)

    print('Gradlefile: {}nTo Grab: {}n'.format(gradlefile, to_grab))

    if os.path.isdir('.git'):
        proc = subprocess.Popen('git log | head -n 1', shell=True, stdout=subprocess.PIPE)
        commitID = re.sub('commit ', '', proc.stdout.read())[0:7]
    else:
        print('ERROR: No .git dir found. Exit.n')
        exit(1)

    try:
        with open(gradlefile, 'r') as versionfile:
            for line in versionfile:
                if re.search('version', line):
                    # grab 1.0.0-SNAPSHOT from build.gradle file
                    version = re.sub(''', '', re.sub(to_grab, '', line)).rstrip()
                    print('VERSION: {}'.format(version))
                    # to grab only first occurrence
                    break
    except IOError:
        print('ERROR: no {} file found. Exit.n'.format(gradlefile))
        exit(1)

    arch = (commitID + '-' + version + '-')

    return arch


def deploy_to_art(res_type, jfrog_repo_type, artifact_path, artifact_to_art):

    # i.e. URL:
    # https://project.artifactoryonline.com/project/project-releases-local/repo/unlock-ui-0.0.1-SNAPSHOT.ear
    # for artifact:
    # unlock-ui-0.0.1-SNAPSHOT.ear

    deployurl = os.path.join(jfrog_url, jfrog_repo, jfrog_dir, jfrog_repo_type, artifact_to_art)

    if os.path.isfile(os.path.join(artifact_path, artifact_to_art)):

        print ('n{} artifact found in {}'.format(res_type, artifact_to_art))
        print ('nExecuting deploy {} to {}.'.format(artifact_to_art, deployurl))

        os.chdir(artifact_path)
        print ('nWorking now in {}.nnArtifacts ready to deployment:n '.format(artifact_path))
        content = os.listdir('.')
        for f in content:
            print ('{}'.format(f))

        with open(artifact_to_art, 'rb') as inartifact:
            data_put = requests.put(deployurl, auth=(jfrog_user, jfrog_pswd), data=inartifact)
            data_put.raise_for_status()
            print ('nResult code: {}'.format(data_put.status_code))
            print ('n{}n'.format(data_put.text))

    else:
        print ('nERROR: {} artifact not found in {}! Exit.n'.format(res_type, artifact_to_art))
        exit(1)


def getlast(res_type, jfrog_repo_type):

    try:
        custom_artifact_name = os.environ['DEPLOY_VERSION']
        custom_artifact_url = os.path.join(jfrog_url, jfrog_repo, jfrog_dir, jfrog_repo_type, custom_artifact_name)

        print('nATTENTION: Custom artifact name used for deploy: {}n'.format(custom_artifact_name))

        return [custom_artifact_url, custom_artifact_name]

    except KeyError:
        artifact_url = os.path.join(jfrog_url, jfrog_api, jfrog_repo, jfrog_dir, jfrog_repo_type + '?lastModified')
        data_last = requests.get(artifact_url, auth=(jfrog_user, jfrog_pswd))
        json_data = json.loads(data_last.text)

        last_artifact_api_url = json_data['uri']

        # last_artifact_api_url return URL to artifact JSON data
        # parse downloadUri from here
        geturl = requests.get(last_artifact_api_url, auth=(jfrog_user, jfrog_pswd))
        urldata = json.loads(geturl.text)
        last_artifact_url = urldata['downloadUri']

        # i.e.a1ea78b-1.0.0-SNAPSHOT-unlock-ui.ear
        last_artifact_name = json_data['uri'].rsplit('/')[-1]

        # return value with full URL to latest modified artifact in selected directory
        # from jfrog_repo_type variable - "repo/ui" or "repo/service"
        # URL example
        # https://project.artifactoryonline.com/project/api/storage/project-releases-local/repo/ui/a1ea78b-1.0.0-SNAPSHOT-unlock-ui.ear
        return [last_artifact_url, last_artifact_name]


def deploy_to_env(res_type, host, jfrog_repo_type):

    print ('nDeploying to {}'.format(host))

    env.host_string = host
    env.key_filename = [os.path.join('.ssh', 'id_rsa')]
    env.user = 'knife'
    env.project_root = os.path.join(os.sep, 'opt', 'jboss', 'standalone', 'deployments')

    artifact_to_art = getlast(res_type, jfrog_repo_type)[1]

    # Commands list

    if res_type == 'UI':
        cleanup_file_mask = '*-unlock-ui.ear'
    elif res_type == 'Service':
        cleanup_file_mask = '*-unlock-service.ear'

    # 95c25ba-1.0.0-SNAPSHOT-unlock-service.ear
    ##cleanup = ('if test -e {0}; then '
    ##           'rm -v {0};'
    ##           'else echo -e "No file found {0}, skipping.";'
    ##           'fi'.format(os.path.join(env.project_root, artifact_to_art)))

    cleanup = ('if test -e {0};'
               'then rm -v {0};'
               'else echo -e "No file found {0}, skipping.";'
               'fi'.format(os.path.join(env.project_root, cleanup_file_mask)))

    # {0} /opt/jboss/standalone/deployments
    # {1} jfrog_user
    # {2} jfrog_pswd
    # {3} full URL to artifact
    upload = ('cd {0} && wget --user {1} --password={2} {3}'.format(
        os.path.join(env.project_root),
        jfrog_user, jfrog_pswd,
        # os.path.join(jfrog_url, jfrog_repo, jfrog_dir, artifact_to_art),
        getlast(res_type, jfrog_repo_type)[0]
    ))

    chown = ('chown jboss:jboss {}'.format(os.path.join(env.project_root, artifact_to_art)))
    getcontent = ('ls -l {}'.format(os.path.join(env.project_root, artifact_to_art)))

    with hide('output', 'running'):

        print ('nRemoving outdated data:n')
        removed = sudo(cleanup)
        print ('{}n'.format(removed))

        print ('Uploading {} to {} in to {}...n'.format(os.path.join(jfrog_url, jfrog_repo, jfrog_dir, artifact_to_art),
                                                       host,
                                                       os.path.join(env.project_root, artifact_to_art)))
        sudo(upload)

        print ("Chown {} to JBoss's user...n".format(os.path.join(env.project_root, artifact_to_art)))
        sudo(chown)

        print ('Done:n')
        ch = run(getcontent)
        print ('{}n'.format(ch))


if __name__ == "__main__":

    getopts()

Вызов скрипта в Jenkins-е:

Screen Shot 2016-01-26 at 12.02.11

И пример деплоя результатов UI билда в Artifactory:

...
[workspace] $ /bin/sh -xe /tmp/hudson2349366895544039566.sh
+ python ci/deploy.py -a -t UI
Gradlefile: build.gradle
To Grab: version

VERSION: 1.0.0-SNAPSHOT

UI artifact found in f50500e-1.0.0-SNAPSHOT-unlock-ui.ear

Executing deploy f50500e-1.0.0-SNAPSHOT-unlock-ui.ear to https://project.artifactoryonline.com/project/project-releases-local/repo/ui/f50500e-1.0.0-SNAPSHOT-unlock-ui.ear.

Working now in /var/lib/jenkins/jobs/Project.UI.Build/workspace/unlock-ear/build/libs.

Artifacts ready to deployment:

f50500e-1.0.0-SNAPSHOT-unlock-ui.ear

Result code: 201

{
  "repo" : "project-releases-local",
  "path" : "/bub/ui/f50500e-1.0.0-SNAPSHOT-unlock-ui.ear",
  "created" : "2016-01-22T15:04:23.536Z",
  "createdBy" : "username",
  "downloadUri" : "https://project.artifactoryonline.com/project/project-releases-local/repo/ui/f50500e-1.0.0-SNAPSHOT-unlock-ui.ear",
  "mimeType" : "application/java-archive",
  "size" : "11318654",
  "checksums" : {
    "sha1" : "6f0f567f609578974245d7226bc87784d7b35edd",
    "md5" : "a4c899f08c1017a81fbaa9c62ebbd6e5"
  },
  "originalChecksums" : {
  },
  "uri" : "https://project.artifactoryonline.com/project/project-releases-local/repo/ui/f50500e-1.0.0-SNAPSHOT-unlock-ui.ear"
}

Triggering projects: Project.UI.Deploy.VMD
Finished: SUCCESS