~ci-train-bot/ubuntu-system-image/system-image-ubuntu-wily-proposed

« back to all changes in this revision

Viewing changes to systemimage/testing/controller.py

  • Committer: Barry Warsaw
  • Date: 2015-05-20 21:10:36 UTC
  • mfrom: (240.2.2 system-image)
  • Revision ID: barry@python.org-20150520211036-it6uoej3ai06fo5n
Manual train silo merge, required because of bot permission snafu

Show diffs side-by-side

added added

removed removed

Lines of Context:
1
 
# Copyright (C) 2013-2014 Canonical Ltd.
 
1
# Copyright (C) 2013-2015 Canonical Ltd.
2
2
# Author: Barry Warsaw <barry@ubuntu.com>
3
3
 
4
4
# This program is free software: you can redistribute it and/or modify
29
29
import psutil
30
30
import subprocess
31
31
 
 
32
try:
 
33
    import pycurl
 
34
except ImportError:
 
35
    pycurl = None
 
36
 
32
37
from contextlib import ExitStack
33
38
from distutils.spawn import find_executable
34
39
from pkg_resources import resource_string as resource_bytes
35
40
from systemimage.helpers import temporary_directory
36
41
from systemimage.testing.helpers import (
37
 
    data_path, find_dbus_process, reset_envar)
 
42
    data_path, find_dbus_process, makedirs, reset_envar, wait_for_service)
 
43
from unittest.mock import patch
38
44
 
39
45
 
40
46
SPACE = ' '
41
 
OVERRIDE = os.environ.get('SYSTEMIMAGE_DBUS_DAEMON_HUP_SLEEP_SECONDS')
42
 
HUP_SLEEP = (0 if OVERRIDE is None else int(OVERRIDE))
 
47
DLSERVICE = os.environ.get(
 
48
    'SYSTEMIMAGE_DLSERVICE',
 
49
    '/usr/bin/ubuntu-download-manager'
 
50
    # For debugging the in-tree version of u-d-m.
 
51
    #'/bin/sh $HOME/projects/phone/trunk/tools/runme.sh'
 
52
    )
43
53
 
44
54
 
45
55
def start_system_image(controller):
46
 
    bus = dbus.SystemBus()
47
 
    service = bus.get_object('com.canonical.SystemImage', '/Service')
48
 
    iface = dbus.Interface(service, 'com.canonical.SystemImage')
49
 
    iface.Info()
 
56
    wait_for_service(reload=False)
50
57
    process = find_dbus_process(controller.ini_path)
51
58
    if process is None:
52
59
        raise RuntimeError('Could not start system-image-dbus')
78
85
 
79
86
 
80
87
def start_downloader(controller):
81
 
    bus = dbus.SystemBus()
82
 
    service = bus.get_object('com.canonical.applications.Downloader', '/')
83
 
    iface = dbus.Interface(
84
 
        service, 'com.canonical.applications.DownloadManager')
 
88
    service = dbus.SystemBus().get_object('org.freedesktop.DBus', '/')
 
89
    iface = dbus.Interface(service, 'org.freedesktop.DBus')
 
90
    reply = 0
 
91
    while reply != 2:
 
92
        reply = iface.StartServiceByName(
 
93
            'com.canonical.applications.Downloader', 0)
 
94
        time.sleep(0.1)
85
95
    # Something innocuous.
86
 
    iface.defaultThrottle()
87
96
    process = _find_udm_process()
88
97
    if process is None:
89
98
        raise RuntimeError('Could not start ubuntu-download-manager')
105
114
        process.wait(60)
106
115
 
107
116
 
108
 
DLSERVICE = '/usr/bin/ubuntu-download-manager'
109
 
# For debugging the in-tree version of u-d-m.
110
 
#DLSERVICE = '/bin/sh /home/barry/projects/phone/runme'
111
 
 
112
 
 
113
117
SERVICES = [
114
118
   ('com.canonical.SystemImage',
115
 
    '{python} -m {self.MODULE} -C {self.ini_path} --testing {self.mode}',
 
119
    '{python} -m {self.MODULE} -C {self.ini_path} '
 
120
    '{self.curl_cert} --testing {self.mode}',
116
121
    start_system_image,
117
122
    stop_system_image,
118
123
   ),
119
 
   ('com.canonical.applications.Downloader',
 
124
   ]
 
125
 
 
126
 
 
127
if pycurl is None:
 
128
    USING_PYCURL = False
 
129
else:
 
130
    USING_PYCURL = int(os.environ.get('SYSTEMIMAGE_PYCURL', '0'))
 
131
 
 
132
if not USING_PYCURL:
 
133
    SERVICES.append(
 
134
    ('com.canonical.applications.Downloader',
120
135
    DLSERVICE +
121
 
        ' {self.certs} -disable-timeout -stoppable -log-dir {self.tmpdir}',
 
136
        ' {self.udm_certs} -disable-timeout -stoppable -log-dir {self.tmpdir}',
122
137
    start_downloader,
123
138
    stop_downloader,
124
 
   ),
125
 
   ]
 
139
   )
 
140
   )
126
141
 
127
142
 
128
143
class Controller:
131
146
    MODULE = 'systemimage.testing.service'
132
147
 
133
148
    def __init__(self, logfile=None, loglevel='info'):
 
149
        self.loglevel = loglevel
134
150
        # Non-public.
135
151
        self._stack = ExitStack()
136
152
        self._stoppers = []
137
153
        # Public.
138
154
        self.tmpdir = self._stack.enter_context(temporary_directory())
139
155
        self.config_path = os.path.join(self.tmpdir, 'dbus-system.conf')
140
 
        self.ini_path = None
141
156
        self.serverdir = self._stack.enter_context(temporary_directory())
142
157
        self.daemon_pid = None
143
158
        self.mode = 'live'
144
 
        self.certs = ''
 
159
        self.udm_certs = ''
 
160
        self.curl_cert = ''
 
161
        self.patcher = None
145
162
        # Set up the dbus-daemon system configuration file.
146
163
        path = data_path('dbus-system.conf.in')
147
164
        with open(path, 'r', encoding='utf-8') as fp:
151
168
        with open(self.config_path, 'w', encoding='utf-8') as fp:
152
169
            fp.write(config)
153
170
        # We need a client.ini file for the subprocess.
154
 
        ini_tmpdir = self._stack.enter_context(temporary_directory())
155
 
        ini_vardir = self._stack.enter_context(temporary_directory())
156
 
        ini_logfile = (os.path.join(ini_tmpdir, 'client.log')
157
 
                       if logfile is None
158
 
                       else logfile)
159
 
        self.ini_path = os.path.join(self.tmpdir, 'client.ini')
 
171
        self.ini_tmpdir = self._stack.enter_context(temporary_directory())
 
172
        self.ini_vardir = self._stack.enter_context(temporary_directory())
 
173
        self.ini_logfile = (os.path.join(self.ini_tmpdir, 'client.log')
 
174
                            if logfile is None
 
175
                            else logfile)
 
176
        self.ini_path = os.path.join(self.tmpdir, 'config.d')
 
177
        makedirs(self.ini_path)
 
178
        self._reset_configs()
 
179
 
 
180
    def _reset_configs(self):
 
181
        for filename in os.listdir(self.ini_path):
 
182
            if filename.endswith('.ini'):
 
183
                os.remove(os.path.join(self.ini_path, filename))
160
184
        template = resource_bytes(
161
 
            'systemimage.tests.data', 'config_03.ini').decode('utf-8')
162
 
        with open(self.ini_path, 'w', encoding='utf-8') as fp:
163
 
            print(template.format(tmpdir=ini_tmpdir,
164
 
                                  vardir=ini_vardir,
165
 
                                  logfile=ini_logfile,
166
 
                                  loglevel=loglevel),
 
185
            'systemimage.tests.data', '01.ini').decode('utf-8')
 
186
        defaults = os.path.join(self.ini_path, '00_defaults.ini')
 
187
        with open(defaults, 'w', encoding='utf-8') as fp:
 
188
            print(template.format(tmpdir=self.ini_tmpdir,
 
189
                                  vardir=self.ini_vardir,
 
190
                                  logfile=self.ini_logfile,
 
191
                                  loglevel=self.loglevel),
167
192
                  file=fp)
168
193
 
169
194
    def _configure_services(self):
184
209
            self._stoppers.append(stopper)
185
210
        # If the dbus-daemon is running, reload its configuration files.
186
211
        if self.daemon_pid is not None:
187
 
            service = dbus.SystemBus().get_object('org.freedesktop.DBus', '/')
188
 
            iface = dbus.Interface(service, 'org.freedesktop.DBus')
189
 
            iface.ReloadConfig()
190
 
            time.sleep(HUP_SLEEP)
 
212
            wait_for_service()
 
213
 
 
214
    def _set_udm_certs(self, cert_pem, certificate_path):
 
215
        self.udm_certs = (
 
216
            '' if cert_pem is None
 
217
            else '-self-signed-certs ' + certificate_path)
 
218
 
 
219
    def _set_curl_certs(self, cert_pem, certificate_path):
 
220
        # We have to set up the PyCURL downloader's self-signed certificate for
 
221
        # the test in two ways.  First, because we might be spawning the D-Bus
 
222
        # service, we have to pass the path to the cert to that service...
 
223
        self.curl_cert = (
 
224
            '' if cert_pem is None
 
225
            else '--self-signed-cert ' + certificate_path)
 
226
        # ...but the controller is also used to set the mode for foreground
 
227
        # tests, such as test_download.py.  Here we don't spawn any D-Bus
 
228
        # processes, but we still have to mock make_testable() in curl.py so
 
229
        # that the PyCURL object accepts the self-signed cert.
 
230
        if self.patcher is not None:
 
231
            self.patcher.stop()
 
232
            self.patcher = None
 
233
        if cert_pem is not None:
 
234
            def self_sign(c):
 
235
                c.setopt(pycurl.CAINFO, certificate_path)
 
236
            self.patcher = patch('systemimage.curl.make_testable', self_sign)
 
237
            self.patcher.start()
191
238
 
192
239
    def set_mode(self, *, cert_pem=None, service_mode=''):
193
240
        self.mode = service_mode
194
 
        self.certs = (
195
 
            '' if cert_pem is None
196
 
            else '-self-signed-certs ' + data_path(cert_pem))
 
241
        certificate_path = data_path(cert_pem)
 
242
        if USING_PYCURL:
 
243
            self._set_curl_certs(cert_pem, certificate_path)
 
244
        else:
 
245
            self._set_udm_certs(cert_pem, certificate_path)
 
246
        self._reset_configs()
197
247
        self._configure_services()
198
248
 
199
249
    def _start(self):
213
263
            daemon_exe,
214
264
            #'/usr/lib/x86_64-linux-gnu/dbus-1.0/debug-build/bin/dbus-daemon',
215
265
            '--fork',
216
 
            '--config-file=' + self.config_path,
 
266
            '--config-file=' + str(self.config_path),
217
267
            # Return the address and pid on stdout.
218
268
            '--print-address=1',
219
269
            '--print-pid=1',