~coreygoldberg/uci-engine/subunit-results

« back to all changes in this revision

Viewing changes to charms/precise/chroot-builder/hooks/hooks.py

Merge trunk, resolving conflicts

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
#!/usr/bin/env python
 
2
# Ubuntu CI Engine
 
3
# Copyright 2014 Canonical Ltd.
 
4
 
 
5
# This program is free software: you can redistribute it and/or modify it
 
6
# under the terms of the GNU Affero General Public License version 3, as
 
7
# published by the Free Software Foundation.
 
8
 
 
9
# This program is distributed in the hope that it will be useful, but
 
10
# WITHOUT ANY WARRANTY; without even the implied warranties of
 
11
# MERCHANTABILITY, SATISFACTORY QUALITY, or FITNESS FOR A PARTICULAR
 
12
# PURPOSE.  See the GNU Affero General Public License for more details.
 
13
 
 
14
# You should have received a copy of the GNU Affero General Public License
 
15
# along with this program.  If not, see <http://www.gnu.org/licenses/>.
 
16
 
 
17
import copy
 
18
import datetime
 
19
import os
 
20
import subprocess
 
21
import shutil
 
22
import sys
 
23
 
 
24
from charmhelpers import (
 
25
    core,
 
26
    fetch,
 
27
)
 
28
 
 
29
CHROOT_USER_HOME = '/root'
 
30
CHROOT_ROOT_PATH = '/var/cache/pbuilder'
 
31
 
 
32
 
 
33
def juju_info(msg, level='INFO'):
 
34
    core.hookenv.log(msg, level)
 
35
 
 
36
 
 
37
def chroot_exists(path, dist, arch):
 
38
    chroot_path = os.path.join(path, '{}-{}'.format(dist, arch))
 
39
    return os.path.isdir(chroot_path)
 
40
 
 
41
 
 
42
def get_chroot_from_url(path, dist, arch, url):
 
43
    if not url:
 
44
        return False
 
45
    chroot_path = os.path.join(path, '{}-{}'.format(dist, arch))
 
46
    wget_path = os.path.join(path, '{}-{}.tgz'.format(dist, arch))
 
47
    url_path = '{}/{}-{}.tgz'.format(url, dist, arch)
 
48
    try:
 
49
        subprocess.check_call(['wget', '-O', wget_path, url_path])
 
50
        subprocess.check_call(['tar', '-xzf', wget_path, '-C', path])
 
51
    except subprocess.CalledProcessError:
 
52
        juju_info('Could not download or extract {}'.format(url_path),
 
53
                  'WARNING')
 
54
        # Clean up
 
55
        try:
 
56
            subprocess.check_call(['rm', '-rf', chroot_path])
 
57
            subprocess.check_call(['rm', '-rf', wget_path])
 
58
        except subprocess.CalledProcessError:
 
59
            raise EnvironmentError(
 
60
                'Could not cleanup chroot {}'.format(chroot_path))
 
61
        return False
 
62
    return True
 
63
 
 
64
 
 
65
def update_chroot(path, dist, arch):
 
66
    chroot_path = os.path.join(path, '{}-{}'.format(dist, arch))
 
67
    chroot_env = copy.copy(os.environ)
 
68
    chroot_env['DIST'] = dist
 
69
    chroot_env['ARCH'] = arch
 
70
    chroot_env['HOME'] = CHROOT_USER_HOME
 
71
    try:
 
72
        start = datetime.datetime.now()
 
73
        subprocess.check_call(['cowbuilder', '--update'],
 
74
                              env=chroot_env)
 
75
        end = datetime.datetime.now()
 
76
        juju_info('chroot update: {}'.format(end - start))
 
77
    except subprocess.CalledProcessError:
 
78
        raise EnvironmentError(
 
79
            'Could not update chroot {}'.format(chroot_path))
 
80
 
 
81
 
 
82
def create_chroot(path, dist, arch):
 
83
    '''Creates a cowbuilder chroot given the dist and arch.'''
 
84
    chroot_env = copy.copy(os.environ)
 
85
    chroot_env['DIST'] = dist
 
86
    chroot_env['ARCH'] = arch
 
87
    chroot_env['HOME'] = CHROOT_USER_HOME
 
88
    start = datetime.datetime.now()
 
89
    juju_info('Attempting: cowbuilder --create for {}-{}'.format(dist, arch))
 
90
    try:
 
91
        subprocess.check_call(['cowbuilder', '--create',
 
92
                               '--distribution', dist,
 
93
                               '--architecture', arch,
 
94
                               '--debootstrapopts', '--variant=buildd'],
 
95
                              env=chroot_env)
 
96
        subprocess.check_call(['cowbuilder', '--save',
 
97
                               '--execute', '--', '/usr/bin/apt-get',
 
98
                               'install', 'eatmydata', 'bzr-builddeb',
 
99
                               'software-properties-common', '-yq',
 
100
                               '--force-yes'],
 
101
                              env=chroot_env)
 
102
    except subprocess.CalledProcessError as e:
 
103
        raise EnvironmentError(
 
104
            'Could not create chroot for {}-{} return: {}'.format(
 
105
                dist, arch, e.returncode))
 
106
    end = datetime.datetime.now()
 
107
    juju_info('chroot create: {}'.format(end - start))
 
108
 
 
109
 
 
110
def setup_chroots(config):
 
111
    distros = config['distros']
 
112
    architectures = config['architectures']
 
113
    base_url = config.get('chroot_base_url', '')
 
114
 
 
115
    try:
 
116
        dist_list = distros.split()
 
117
    except:
 
118
        juju_info('''Can't determine distros from [{}]'''.format(distros),
 
119
                  'ERROR')
 
120
        return 1
 
121
 
 
122
    try:
 
123
        architecture_list = architectures.split()
 
124
    except:
 
125
        juju_info('''Can't determine architectures from [{}]'''.format(
 
126
            architectures), 'ERROR')
 
127
        return 1
 
128
 
 
129
    for dist in dist_list:
 
130
        for arch in architecture_list:
 
131
            try:
 
132
                if chroot_exists(CHROOT_ROOT_PATH, dist, arch):
 
133
                    update_chroot(CHROOT_ROOT_PATH, dist, arch)
 
134
                elif get_chroot_from_url(CHROOT_ROOT_PATH, dist, arch,
 
135
                                         base_url):
 
136
                    update_chroot(CHROOT_ROOT_PATH, dist, arch)
 
137
                else:
 
138
                    create_chroot(CHROOT_ROOT_PATH, dist, arch)
 
139
            except EnvironmentError as e:
 
140
                juju_info('{}'.format(e), 'ERROR')
 
141
                return 1
 
142
    return 0
 
143
 
 
144
 
 
145
def setup_dependencies(config):
 
146
    required_packages = ['bzr', 'cowbuilder']
 
147
    fetch.apt_update()
 
148
    juju_info('installing apt packages...')
 
149
    fetch.apt_install(required_packages, fatal=True)
 
150
 
 
151
 
 
152
def setup_pbuilderrc(config):
 
153
    cu2d_dir = '/srv/cu2d'
 
154
    if os.path.exists(cu2d_dir):
 
155
        subprocess.check_call(['bzr', 'update', cu2d_dir])
 
156
    else:
 
157
        try:
 
158
            subprocess.check_call(['bzr', 'branch',
 
159
                                   'lp:cupstream2distro/uci-airline',
 
160
                                   cu2d_dir])
 
161
        except subprocess.CalledProcessError as e:
 
162
            raise EnvironmentError(
 
163
                'Could not get cu2d (return: {})'.format(e.returncode))
 
164
 
 
165
    shutil.copy(os.path.join(cu2d_dir, 'chroot-tools', '.pbuilderrc'),
 
166
                CHROOT_USER_HOME)
 
167
 
 
168
 
 
169
def config_changed(config):
 
170
    setup_dependencies(config)
 
171
    setup_pbuilderrc(config)
 
172
    setup_chroots(config)
 
173
 
 
174
 
 
175
def main():
 
176
    hook = os.path.basename(sys.argv[0])
 
177
    juju_info('Running hook: {}'.format(hook))
 
178
 
 
179
    hook_py = hook.replace('-', '_')
 
180
    funcs = globals()
 
181
    if hook_py not in funcs:
 
182
        print('Unknown hook: {}'.format(hook))
 
183
        return 1
 
184
 
 
185
    config = core.hookenv.config()
 
186
    juju_info('Config: {}'.format(config))
 
187
    try:
 
188
        return funcs[hook_py](config)
 
189
    except subprocess.CalledProcessError as e:
 
190
        juju_info('Error running: {}: {}'.format(e.cmd, e.output))
 
191
        return e.returncode
 
192
 
 
193
 
 
194
if __name__ == '__main__':
 
195
    exit(main())