Projet

Général

Profil

0002-Jenkinsfile-use-temporary-directory-and-database-311.patch

Benjamin Dauvergne, 31 mai 2019 13:20

Télécharger (8,71 ko)

Voir les différences:

Subject: [PATCH 2/2] Jenkinsfile: use temporary directory and database
 (#31192)

pg_virtualenv is used to allocate a temporary postgressql database with
fsync=off option.
 Jenkinsfile            | 13 ++++++++-
 merge-junit-results.py | 65 ++++++++++++++++++++++++++++++++++++++++++
 tests/settings.py      |  9 ++++--
 tests/wcs/conftest.py  | 45 +++++++++++++++++------------
 tox.ini                | 20 ++++++++++---
 5 files changed, 125 insertions(+), 27 deletions(-)
 create mode 100755 merge-junit-results.py
Jenkinsfile
2 2

  
3 3
pipeline {
4 4
    agent any
5
    options { disableConcurrentBuilds() }
6
    environment {
7
       TMPDIR = "/tmp/$BUILD_TAG"
8
    }
5 9
    stages {
6 10
        stage('Unit Tests') {
7 11
            steps {
8
                sh 'tox -rv'
12
                sh "mkdir ${env.TMPDIR}"
13
                sh """
14
virtualenv -p python3 ${env.TMPDIR}/venv/
15
${env.TMPDIR}/venv/bin/pip install tox
16
PGPORT=`python -c 'import struct; import socket; s=socket.socket(); s.setsockopt(socket.SOL_SOCKET, socket.SO_LINGER, struct.pack("ii", 1, 0)); s.bind(("", 0)); print(s.getsockname()[1]); s.close()'` pg_virtualenv -o fsync=off ${env.TMPDIR}/venv/bin/tox -rv"""
9 17
            }
10 18
            post {
11 19
                always {
......
14 22
                        utils.publish_coverage('coverage.xml')
15 23
                        utils.publish_coverage_native('index.html')
16 24
                        utils.publish_pylint('pylint.out')
25
                        sh './merge-junit-results.py junit-*.xml >junit.xml'
26
                        sh 'rm junit-*.xml'
17 27
                    }
18 28
                    junit '*_results.xml'
19 29
                }
......
35 45
                utils = new Utils()
36 46
                utils.mail_notify(currentBuild, env, 'admin+jenkins-passerelle@entrouvert.com')
37 47
            }
48
            sh "rm -rf ${env.TMPDIR}"
38 49
        }
39 50
        success {
40 51
            cleanWs()
merge-junit-results.py
1
#!/usr/bin/env python
2
#
3
#  Corey Goldberg, Dec 2012
4
#
5

  
6
import os
7
import sys
8
import xml.etree.ElementTree as ET
9

  
10

  
11
"""Merge multiple JUnit XML files into a single results file.
12
Output dumps to sdtdout.
13
example usage:
14
    $ python merge_junit_results.py results1.xml results2.xml > results.xml
15
"""
16

  
17

  
18
def main():
19
    args = sys.argv[1:]
20
    if not args:
21
        usage()
22
        sys.exit(2)
23
    if '-h' in args or '--help' in args:
24
        usage()
25
        sys.exit(2)
26
    merge_results(args[:])
27

  
28

  
29
def merge_results(xml_files):
30
    failures = 0
31
    tests = 0
32
    errors = 0
33
    time = 0.0
34
    cases = []
35

  
36
    for file_name in xml_files:
37
        tree = ET.parse(file_name)
38
        test_suite = tree.getroot()
39
        failures += int(test_suite.attrib['failures'])
40
        tests += int(test_suite.attrib['tests'])
41
        errors += int(test_suite.attrib['errors'])
42
        time += float(test_suite.attrib['time'])
43
        name = test_suite.attrib.get('name', '')
44
        for child in test_suite.getchildren():
45
            child.attrib['classname'] = '%s-%s' % (name, child.attrib.get('classname', ''))
46
        cases.append(test_suite.getchildren())
47

  
48
    new_root = ET.Element('testsuite')
49
    new_root.attrib['failures'] = '%s' % failures
50
    new_root.attrib['tests'] = '%s' % tests
51
    new_root.attrib['errors'] = '%s' % errors
52
    new_root.attrib['time'] = '%s' % time
53
    for case in cases:
54
        new_root.extend(case)
55
    new_tree = ET.ElementTree(new_root)
56
    ET.dump(new_tree)
57

  
58

  
59
def usage():
60
    this_file = os.path.basename(__file__)
61
    print('Usage:  %s results1.xml results2.xml' % this_file)
62

  
63

  
64
if __name__ == '__main__':
65
    main()
tests/settings.py
62 62
DATABASES = {
63 63
    'default': {
64 64
        'ENGINE': os.environ.get('DB_ENGINE', 'django.db.backends.sqlite3'),
65
        'TEST': {
66
            'NAME': 'passerelle-test-%s' % os.environ.get("BRANCH_NAME", "").replace('/', '-')[:63],
67
        },
65
        'NAME': 'passerelle',
68 66
    }
69 67
}
68

  
69
if 'postgres' in DATABASES['default']['ENGINE']:
70
    for key in ('PGPORT', 'PGHOST', 'PGUSER', 'PGPASSWORD'):
71
        if key in os.environ:
72
            DATABASES['default'][key[2:]] = os.environ[key]
tests/wcs/conftest.py
41 41

  
42 42
@contextlib.contextmanager
43 43
def postgres_db_factory():
44
    database = 'db%s' % random.getrandbits(20)
45

  
46
    with contextlib.closing(psycopg2.connect('')) as conn:
44
    dsn = {
45
        'database': 'postgres',
46
    }
47
    for key, env in [
48
            ('host', 'PGHOST'),
49
            ('port', 'PGPORT'),
50
            ('user', 'PGUSER'),
51
            ('password', 'PGPASSWORD')]:
52
        if env in os.environ:
53
            dsn[key] = os.environ[env]
54

  
55
    dbname = 'db%s' % random.getrandbits(20)
56

  
57
    with contextlib.closing(psycopg2.connect(**dsn)) as conn:
47 58
        conn.set_isolation_level(0)
48 59
        with conn.cursor() as cursor:
49
            cursor.execute('CREATE DATABASE %s' % database)
60
            cursor.execute('CREATE DATABASE %s' % dbname)
50 61
    try:
51
        yield PostgresDB(database)
62
        db_dsn = dsn.copy()
63
        db_dsn['database'] = dbname
64
        yield PostgresDB(db_dsn)
52 65
    finally:
53
        with contextlib.closing(psycopg2.connect('')) as conn:
66
        with contextlib.closing(psycopg2.connect(**dsn)) as conn:
54 67
            conn.set_isolation_level(0)
55 68
            with conn.cursor() as cursor:
56
                cursor.execute('DROP DATABASE IF EXISTS %s' % database)
69
                cursor.execute('DROP DATABASE IF EXISTS %s' % dbname)
57 70

  
58 71

  
59 72
class PostgresDB(object):
60
    def __init__(self, database):
61
        self.database = database
62

  
63
    @property
64
    def dsn(self):
65
        return 'dbname={self.database}'.format(self=self)
73
    def __init__(self, dsn):
74
        self.dsn = dsn
66 75

  
67 76
    @contextlib.contextmanager
68 77
    def conn(self):
69
        with contextlib.closing(psycopg2.connect(self.dsn)) as conn:
78
        with contextlib.closing(psycopg2.connect(**self.dsn)) as conn:
70 79
            yield conn
71 80

  
72 81
    def __repr__(self):
73
        return '<Postgres Database %r>' % self.database
82
        return '<Postgres Database %r>' % self.dsn
74 83

  
75 84

  
76 85
@pytest.fixture
......
193 202
            config.set('options', 'postgresql', 'true')
194 203

  
195 204
        with self.config_pck as config:
196
            config['postgresql'] = {
197
                'database': database,
198
            }
205
            config['postgresql'] = database.dsn
199 206
        self.run_in_context(self._wcs_init_sql)
200 207

  
201 208
    def _wcs_init_sql(self):
......
446 453

  
447 454
@pytest.fixture
448 455
def wcs_host(wcs, postgres_db, datasource):
449
        with wcs.host('127.0.0.1', database=postgres_db.database) as wcs_host:
456
        with wcs.host('127.0.0.1', database=postgres_db) as wcs_host:
450 457
            yield wcs_host
tox.ini
1 1
[tox]
2
toxworkdir = {env:TMPDIR:/tmp}/tox-{env:USER}/passerelle/{env:BRANCH_NAME:}
2
toxworkdir = {env:TMPDIR:/tmp}/tox-{env:USER}/passerelle/
3 3
envlist = py27-django{18,111}-{sqlite,pg}
4 4

  
5 5
[testenv]
......
12 12
  fast: FAST=--nomigrations
13 13
  sqlite: DB_ENGINE=django.db.backends.sqlite3
14 14
  pg: DB_ENGINE=django.db.backends.postgresql_psycopg2
15
  PGPORT={env:PGPORT:}
16
  PGHOST={env:PGHOST:}
17
  PGUSER={env:PGUSER:}
18
  PGPASSWORD={env:PGPASSWORD:}
19
  JUNIT={tty::-o junit_suite_name={envname} --junit-xml=junit-{envname}.xml}
20
  coverage: COVERAGE=--cov=passerelle/ --cov-branch --cov-append --cov-report xml --cov-report html --cov-config .coveragerc
15 21
deps =
16 22
  django18: django>=1.8,<1.9
17 23
  django111: django>=1.11,<1.12
......
36 42
  vobject
37 43
commands =
38 44
  ./get_wcs.sh
39
  django18: py.test {posargs: {env:FAST:} --junitxml=test_{envname}_results.xml --cov-report xml --cov-report html --cov=passerelle/ --cov-config .coveragerc tests/}
40
  django18: ./pylint.sh passerelle/
41
  django111: py.test {posargs: --junitxml=test_{envname}_results.xml tests/}
45
  py.test {posargs: {env:FAST:} {env:JUNIT:} {env:COVERAGE:} tests/}
46

  
47
[testenv:pylint]
48
basepython = python2.7
49
deps =
50
    pylint<1.8
51
    pylint-django<0.8.1
52
commands =
53
    /bin/bash -c "./pylint.sh passerelle/"
42
-