summaryrefslogtreecommitdiff
path: root/mason/tests/build.py
blob: 625252d4349a1c275c323d1d9ec5c844c7b98385 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
# Copyright 2014 Codethink Ltd
#
# This program is free software; you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation; version 2 of the License.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License along
# with this program; if not, write to the Free Software Foundation, Inc.,
# 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.

import cliapp
import json
import logging
import morphlib
import os
import subprocess
import time
import urlparse

import mason


class Build(object):

    """A single build instance."""

    def __init__(self, name, controller, logfile):
        self.system_name = name
        self.controller = controller
        self.log_path = logfile
        self.logfile = open(logfile, 'w+')
        #TODO: use distbuild not local build
        self.command = [
            'morph', 'build', self.system_name]

    def start(self):
        self.process = subprocess.Popen(self.command, stdout=self.logfile, stderr=self.logfile)

    def completed(self):
        return (self.process.poll() is not None)

    def close_log(self):
        self.logfile.close()


class Runner(mason.runners.JobRunner):

    """Test that the built system will deploy and run

    This handles running the build-deploy-build test, which
    is used to ensure that Baserock can build Baserock.
    """

    log = logging.getLogger("mason.tests.build.Runner")

    def __init__(self, worker_server, plugin_config, job_name):
        super(Runner, self).__init__(worker_server, plugin_config, job_name)

        self.total_steps = 4

    def run_job(self):
        self.log.debug('Got job: %s', self.job_arguments)

        self.log.info('Step 1: Creating a workspace')
        self._create_workspace()

        self.log.info('Step 2: Prepare build log directory')
        self._prepare_build_log_dir()

        self.log.info('Step 3: Building the systems')
        try:
            self._build_systems()
        except Exception as e:
            self._remove_workspace()
            raise e

        self.log.info('Step 4: Clean up')
        self._clean_up()

    @staticmethod
    def _parse_controllers(conf):
        return dict(item.split(':', 1) for item in conf['controllers'])

    def _prepare_builds(self, conf):
        cluster = self.morph_helper.load_morphology(conf['cluster-morphology'])
        systems = set(self.morph_helper.iterate_systems(cluster['systems']))
        controllers = self._parse_controllers(conf)
        builds = []
        for system_name in systems:
            system = self.morph_helper.load_morphology(system_name)
            print 'loaded %s' % system_name
            if system['arch'] in controllers:
                logfile = os.path.join(self.logdir, '%s.log' % system['name'])
                builds.append(Build(system_name, controllers[system['arch']], logfile))
        print 'prepared builds'
        return builds

    @mason.util.job_step
    def _prepare_build_log_dir(self):
        self.logdir = '/var/www/logs/%s-%s/build' % \
                (self.project, self.commit[:7])
        if not os.path.exists(self.logdir):
            os.makedirs(self.logdir)

    @mason.util.job_step
    def _build_systems(self):
        builds = self._prepare_builds(self.plugin_config['config'])
        os.chdir(self.defs_checkout)
        for build in builds:
            build.start()
            # TODO: Don't force serialisation when we change to distbuild.
            while not build.completed():
                time.sleep(1)

        fail = False
        for build in builds:
            build.close_log()
            if build.process.returncode != 0:
                fail = True
                logging.error('Building failed for %s. Log is at %s.' % 
                              (build.system_name, build.log_path))
        if fail:
            raise cliapp.AppException('Building of systems failed.')