Merge ~paride/autopkgtest:nova-reboot into ~ubuntu-release/autopkgtest/+git/autopkgtest:master
- Git
- lp:~paride/autopkgtest
- nova-reboot
- Merge into master
Status: | Superseded |
---|---|
Proposed branch: | ~paride/autopkgtest:nova-reboot |
Merge into: | ~ubuntu-release/autopkgtest/+git/autopkgtest:master |
Diff against target: |
1218 lines (+589/-59) (has conflicts) 15 files modified
lib/adt_testbed.py (+291/-8) lib/adtlog.py (+11/-1) lib/autopkgtest_args.py (+12/-0) lib/testdesc.py (+59/-12) runner/autopkgtest (+111/-21) runner/autopkgtest.1 (+10/-1) setup-commands/setup-testbed (+17/-0) ssh-setup/nova (+6/-3) tests/autopkgtest (+2/-2) tests/testdesc (+3/-3) tools/autopkgtest-build-lxd (+10/-0) virt/autopkgtest-virt-lxc (+4/-0) virt/autopkgtest-virt-lxd (+31/-3) virt/autopkgtest-virt-qemu (+4/-0) virt/autopkgtest-virt-ssh (+18/-5) Conflict in lib/adt_testbed.py Conflict in lib/autopkgtest_args.py Conflict in lib/testdesc.py Conflict in runner/autopkgtest Conflict in setup-commands/setup-testbed Conflict in virt/autopkgtest-virt-lxc Conflict in virt/autopkgtest-virt-lxd Conflict in virt/autopkgtest-virt-qemu |
Related bugs: |
Reviewer | Review Type | Date Requested | Status |
---|---|---|---|
Ubuntu Release Team | Pending | ||
Review via email: mp+434809@code.launchpad.net |
This proposal has been superseded by a proposal from 2022-12-16.
Commit message
Description of the change
Unmerged commits
- a3de103... by Paride Legovini
-
nova script: force nova reboot for now (NOVA_REBOOT=1)
This is needed to workaround arm64 VMs that fail to reboot. We need to
figure out why that is happening and revert this commit.One side effect of this change is that we end up with VMs rebooting
*twice*: once because the reboot command has been called in the VM, and
one more time from wait_reboot(). This interferes with some kernel
testing relying on setting kernel cmdline parameters. - 56f13dc... by Paride Legovini
-
doc: mention the default Pin-Priority for pockets
- c92df58... by Paride Legovini
-
Pin pockets with Pin-Priority: 500
Ubuntu is switching to NotAutomatic: yes for the proposed pocket:
the change is already operative on >= Lunar and under consideration
for older stable releases. This means that by default packages from
-proposed won't be installed unless explicitly requested.The change is meant to make it easier for end users to keep -proposed
enabled (for SRU verification or individual package installation)
without manually adding a pin to avoid pulling in all of -proposed.However when running autopkgtests with --apt-pocket=
proposed we do want
to pull in all the pocket, so let's always give it Pin-Priority: 500.
This is similar to what Launchpad does with the buildds.Note: This change does not really treat -proposed in a special way:
any pocket will get Pin-Priority: 500. It makes sense to keep the tool
behavior consistent, and we're not aware of cases where this could be
cause issues. - f7b890b... by Paride Legovini
-
autopkgtest-
build-lxd: umount lxcfs before snapd is purged One preparatory step for autopkgtest LXD images is purging snapd,
however this currently fails while removing the lxd snap (LP: #1989603).
As as workaround manually umount the problematic mountpoint. - e568cbc... by Paride Legovini
-
autopkgtest-
build-lxd: mask snapd.seeded. service autopkgtest-
build-lxd: mask snapd.seeded. service as a workaround for
LP: #1952084. Can be reverted once that bug is properly fixed or if
we switch to minimal images, which do not ship with snapd, but aren't
available for armhf as of today. - 55cf0ad... by Brian Murray
-
Reset the timeout-ssh period back to its default
- 7c0bddf... by Brian Murray
-
Wait even longer for ssh to work out
- 5db4fa8... by Brian Murray
-
Increase default value of timeout-ssh to 900
- 4edddb8... by Brian Murray
-
Increase the wait_for_ssh timeout to 900
- d9d863f... by Brian Murray
-
kill processes with SIGKILL if SIGTERM fails
When trying to kill long running test processes on armhf they don't
always get killed so if the process is still running kill it again with
SIGKILL.
Preview Diff
1 | diff --git a/lib/adt_testbed.py b/lib/adt_testbed.py |
2 | index 90e2009..9ff0475 100644 |
3 | --- a/lib/adt_testbed.py |
4 | +++ b/lib/adt_testbed.py |
5 | @@ -54,7 +54,7 @@ class Testbed: |
6 | setup_commands=[], setup_commands_boot=[], add_apt_pockets=[], |
7 | copy_files=[], pin_packages=[], add_apt_sources=[], |
8 | add_apt_releases=[], apt_default_release=None, |
9 | - enable_apt_fallback=True, shell_fail=False, needs_internet='run'): |
10 | + enable_apt_fallback=True, shell_fail=False, needs_internet='run', cross_arch=None): |
11 | self.sp = None |
12 | self.lastsend = None |
13 | self.scratch = None |
14 | @@ -62,6 +62,8 @@ class Testbed: |
15 | self._need_reset_apt = False |
16 | self.stop_sent = False |
17 | self.dpkg_arch = None |
18 | + self.cross_arch = cross_arch |
19 | + self.cross_env = [] |
20 | self.exec_cmd = None |
21 | self.output_dir = output_dir |
22 | self.shared_downtmp = None # testbed's downtmp on the host, if supported |
23 | @@ -302,6 +304,23 @@ class Testbed: |
24 | self.dpkg_arch = self.check_exec(['dpkg', '--print-architecture'], True).strip() |
25 | adtlog.info('testbed dpkg architecture: ' + self.dpkg_arch) |
26 | |
27 | + # set up environment for cross-architecture if needed |
28 | + if self.cross_arch: |
29 | + self.cross_env = [] |
30 | + argv = ['dpkg-architecture', '-a', self.cross_arch] |
31 | + # ignore stderr |
32 | + (code, vars, err) = self.execute(argv, |
33 | + stdout=subprocess.PIPE, |
34 | + stderr=subprocess.PIPE) |
35 | + if code != 0: |
36 | + self.bomb('"%s" failed with status %i' % (' '.join(argv), code), |
37 | + adtlog.AutopkgtestError) |
38 | + |
39 | + for var in vars.split('\n'): |
40 | + if var.startswith('DEB_HOST'): |
41 | + self.cross_env.append(var) |
42 | + adtlog.info('testbed target architecture: ' + self.cross_arch) |
43 | + |
44 | # do we have eatmydata? |
45 | (code, out, err) = self.execute( |
46 | ['sh', '-ec', 'command -v eatmydata'], |
47 | @@ -391,15 +410,22 @@ class Testbed: |
48 | ''' % (pocket, pocket, pocket) |
49 | self.check_exec(['sh', '-ec', script]) |
50 | |
51 | - # create apt pinning for --apt-pocket with package list |
52 | + # create apt pinning for --apt-pocket |
53 | for pocket in self.add_apt_pockets: |
54 | # do we have a package list? |
55 | - try: |
56 | + pkglist = '' |
57 | + if '=' in pocket: |
58 | (pocket, pkglist) = pocket.split('=', 1) |
59 | - except ValueError: |
60 | - continue |
61 | - self._create_apt_pinning_for_packages(self._get_default_release() + '-' + pocket, pkglist, |
62 | - [self._get_default_release() + '-updates']) |
63 | + |
64 | + # set priority for the specified pocket. |
65 | + # this is useful to override NotAutomatic: yes. |
66 | + self._create_apt_pinning_for_pocket(self._get_default_release() + '-' + pocket) |
67 | + |
68 | + # set priorities for individual packages if specified |
69 | + if pkglist: |
70 | + self._create_apt_pinning_for_packages(self._get_default_release() + '-' + pocket, pkglist, |
71 | + [self._get_default_release() + '-updates']) |
72 | + |
73 | if self.add_apt_releases: |
74 | get_mirror_and_components = ''' |
75 | sed -rn 's/^(deb|deb-src) +(\\[.*\\] *)?((http|https|file):[^ ]*) +([^ ]+) +(.*)$/\\2\\3 \\6/p' \\ |
76 | @@ -474,6 +500,7 @@ class Testbed: |
77 | self.deps_installed = deps_new |
78 | if not deps_new: |
79 | return |
80 | +<<<<<<< lib/adt_testbed.py |
81 | self.satisfy_dependencies_string(', '.join(deps_new), 'install-deps', shell_on_failure=shell_on_failure, synth_deps=synth_deps) |
82 | |
83 | def _provide_sudo(self) -> str: |
84 | @@ -569,6 +596,25 @@ class Testbed: |
85 | |
86 | if self._tried_provide_sudo is not True: |
87 | raise Unsupported(name, self._tried_provide_sudo) |
88 | +======= |
89 | + if self.cross_arch: |
90 | + # mock up a .dsc with our dependencies string as our build-deps |
91 | + dsc = TempPath(self, 'autopkgtest-satdep.dsc') |
92 | + with open(dsc.host, 'w', encoding='UTF-8') as f_dsc: |
93 | + f_dsc.write("Source: autopkgtest-satdep\n" |
94 | + "Binary: autopkgtest-satdep\n" |
95 | + "Architecture: any all\n" |
96 | + "Build-Depends: " + ', '.join(deps_new) + "\n") |
97 | + dsc.copydown() |
98 | + |
99 | + # feed the result to apt-get build-dep |
100 | + self.satisfy_build_deps(dsc.tb, recommends, shell_on_failure) |
101 | + # Now handle the synthetic deps as well |
102 | + self.install_apt('', recommends, shell_on_failure, synth_deps) |
103 | + |
104 | + else: |
105 | + self.satisfy_dependencies_string(', '.join(deps_new), 'install-deps', recommends, shell_on_failure=shell_on_failure, synth_deps=synth_deps) |
106 | +>>>>>>> lib/adt_testbed.py |
107 | |
108 | def needs_reset(self): |
109 | # show what caused a reset |
110 | @@ -671,6 +717,11 @@ class Testbed: |
111 | env.append('DEBIAN_FRONTEND=noninteractive') |
112 | env.append('APT_LISTBUGS_FRONTEND=none') |
113 | env.append('APT_LISTCHANGES_FRONTEND=none') |
114 | +<<<<<<< lib/adt_testbed.py |
115 | +======= |
116 | + env += self.install_tmp_env |
117 | + env += self.cross_env |
118 | +>>>>>>> lib/adt_testbed.py |
119 | |
120 | adtlog.debug('testbed command %s, kind %s, sout %s, serr %s, env %s' % |
121 | (argv, kind, stdout and 'pipe' or 'raw', |
122 | @@ -709,8 +760,13 @@ class Testbed: |
123 | adtlog.debug('testbed command exited with code %i' % proc.returncode) |
124 | |
125 | if proc.returncode in (254, 255): |
126 | +<<<<<<< lib/adt_testbed.py |
127 | self.debug_fail() |
128 | self.bomb('testbed auxverb failed with exit code %i' % proc.returncode) |
129 | +======= |
130 | + self.command('auxverb_debug_fail') |
131 | + self.bomb('testbed auxverb failed with exit code %i, argv: %r\nstdout: %s\nstderr: %s' % (proc.returncode, argv, out, err)) |
132 | +>>>>>>> lib/adt_testbed.py |
133 | |
134 | return (proc.returncode, out, err) |
135 | |
136 | @@ -941,7 +997,205 @@ Description: satisfy autopkgtest test dependencies |
137 | |
138 | self.execute(['dpkg', '--purge', 'autopkgtest-satdep']) |
139 | |
140 | +<<<<<<< lib/adt_testbed.py |
141 | def satisfy_dependencies_string(self, deps, what, |
142 | +======= |
143 | + def install_click(self, clickpath): |
144 | + # copy click into testbed |
145 | + tp = Path(self, clickpath, os.path.join( |
146 | + self.scratch, os.path.basename(clickpath))) |
147 | + tp.copydown() |
148 | + # install it |
149 | + clickopts = ['--all-users'] |
150 | + if 'AUTOPKGTEST_CLICK_NO_FRAMEWORK_CHECK' in os.environ: |
151 | + # this is mostly for testing |
152 | + clickopts.append('--force-missing-framework') |
153 | + if 'root-on-testbed' in self.caps: |
154 | + rc = self.execute(['click', 'install', '--allow-unauthenticated'] + |
155 | + clickopts + [tp.tb], kind='install')[0] |
156 | + else: |
157 | + rc = self.execute(['pkcon', 'install-local', '--allow-untrusted', |
158 | + tp.tb], kind='install')[0] |
159 | + if rc != 0: |
160 | + self.badpkg('click install failed with status %i' % rc) |
161 | + |
162 | + # work around https://launchpad.net/bugs/1333215 |
163 | + # we don't want su -l here which resets the environment from |
164 | + # self.execute(); so emulate the parts that we want |
165 | + # FIXME: move "run as user" as an argument of execute()/check_exec() and run with -l |
166 | + self.check_exec(['su', '--shell=/bin/sh', self.su_user, '-c', |
167 | + ('export USER=%s;' % self.user) + |
168 | + '. /etc/profile >/dev/null 2>&1 || true; ' |
169 | + ' . ~/.profile >/dev/null 2>&1 || true; ' |
170 | + '[ -z "$UPSTART_SESSION" ] || /sbin/initctl --user start click-user-hooks']) |
171 | + |
172 | + def apparmor_click(self, clickpkgs, installed_clicks): |
173 | + '''Update AppArmor rules for click tests |
174 | + |
175 | + Return True if anything was modified and apparmor_restore_click() |
176 | + needs to be called. |
177 | + ''' |
178 | + # check if we are in a click+AppArmor environment |
179 | + if self.execute(['sh', '-ec', |
180 | + '[ -d /var/cache/apparmor -a -d /var/lib/apparmor/clicks -a ! -e /var/cache/apparmor/click-ap.rules ] && ' |
181 | + 'type aa-clickhook >/dev/null 2>&1'])[0] != 0: |
182 | + adtlog.debug('testbed does not have AppArmor/click or already has Autopilot click rules, no need to adjust rules') |
183 | + return False |
184 | + adtlog.debug('testbed has AppArmor/click') |
185 | + |
186 | + if 'root-on-testbed' not in self.caps: |
187 | + adtlog.warning('Cannot adjust AppArmor rules without root/sudo ' |
188 | + 'privileges; Autopilot tests will fail and test ' |
189 | + 'dependencies will not be available!') |
190 | + return False |
191 | + |
192 | + rules = 'dbus (receive, send) bus=session path=/com/canonical/Autopilot/**,' |
193 | + for e in self.install_tmp_env: |
194 | + if e.startswith('QT_PLUGIN_PATH='): |
195 | + for p in e.split('=', 1)[1].split(':'): |
196 | + p = p.strip() |
197 | + if p: |
198 | + rules += ' %s/** r,' % p |
199 | + break |
200 | + |
201 | + script = '''echo '%s' > /var/cache/apparmor/click-ap.rules; ''' % rules |
202 | + |
203 | + if clickpkgs or installed_clicks: |
204 | + adtlog.info('Updating AppArmor rules to allow autopilot introspection for tested clicks') |
205 | + script += 'for c in %s; do ' \ |
206 | + ' info=$(click info %s %s/$(basename "$c")); ' \ |
207 | + ''' name=$(echo "$info" | sed -rn '/"name"/ {s/^.*: *"([^"]+)",/\\1/; p}'); ''' \ |
208 | + ''' version=$(echo "$info" | sed -rn '/"version"/ {s/^.*: *"([^"]+)",/\\1/; p}'); ''' \ |
209 | + ' touch -h /var/lib/apparmor/clicks/${name}_*_${version}.json >/dev/null || true; '\ |
210 | + 'done; ' \ |
211 | + 'for c in %s; do ' \ |
212 | + ' touch -h /var/lib/apparmor/clicks/${c}_*.json 2>/dev/null || true; ' \ |
213 | + 'done; ' \ |
214 | + 'aa-clickhook --include=/var/cache/apparmor/click-ap.rules' % ( |
215 | + ' '.join(clickpkgs), |
216 | + self.user and ('--user ' + self.user) or '', |
217 | + self.scratch, |
218 | + ' '.join(installed_clicks)) |
219 | + else: |
220 | + adtlog.info('Updating AppArmor rules to allow autopilot introspection for all clicks (will take a minute)...') |
221 | + script += 'aa-clickhook --force --include=/var/cache/apparmor/click-ap.rules' |
222 | + |
223 | + if self.execute(['sh', adtlog.verbosity >= 2 and '-exc' or '-ec', script], kind='install')[0] != 0: |
224 | + self.bomb('Failed to update click AppArmor rules') |
225 | + |
226 | + return True |
227 | + |
228 | + def apparmor_restore_click(self, clickpkgs, installed_clicks): |
229 | + '''Restore AppArmor rules after click tests''' |
230 | + |
231 | + adtlog.info('Restoring click package AppArmor rules') |
232 | + # if we only modified some clicks above, --force will be fast, so it's |
233 | + # ok to always do that |
234 | + script = 'rm -f /var/cache/apparmor/click-ap.rules; aa-clickhook --force' |
235 | + if self.execute(['sh', adtlog.verbosity >= 2 and '-exc' or '-ec', script], kind='install')[0] != 0: |
236 | + self.bomb('Failed to update click AppArmor rules') |
237 | + |
238 | + def _run_apt_build_dep(self, what, prefix, recommends, ignorerc=False): |
239 | + '''actually run apt-get build-dep''' |
240 | + |
241 | + # capture status-fd to stderr |
242 | + (rc, _, serr) = self.execute(['/bin/sh', '-ec', '%s apt-get build-dep ' |
243 | + '--assume-yes %s ' |
244 | + '-o APT::Status-Fd=3 ' |
245 | + '-o APT::Install-Recommends=%s ' |
246 | + '-o Dpkg::Options::=--force-confnew ' |
247 | + '-o Debug::pkgProblemResolver=true 3>&2 2>&1' % |
248 | + (prefix, what, recommends)], |
249 | + kind='install', stderr=subprocess.PIPE) |
250 | + if not ignorerc and rc != 0: |
251 | + adtlog.debug('apt-get build-dep %s failed; status-fd:\n%s' % (what, serr)) |
252 | + # check if apt failed during package download, which might be a |
253 | + # transient error, so retry |
254 | + if 'dlstatus:' in serr and 'pmstatus:' not in serr: |
255 | + raise adtlog.AptDownloadError |
256 | + else: |
257 | + raise adtlog.AptPermanentError |
258 | + |
259 | + def satisfy_build_deps(self, source_pkg, recommends=False, |
260 | + shell_on_failure=False): |
261 | + '''Install build-dependencies into testbed with apt-get build-dep |
262 | + |
263 | + This requires root privileges and a writable file system. |
264 | + ''' |
265 | + # check if we can use apt-get |
266 | + can_apt_get = False |
267 | + if 'root-on-testbed' in self.caps: |
268 | + rc = self.execute(['test', '-w', '/var/lib/dpkg/status'])[0] |
269 | + if rc == 0: |
270 | + can_apt_get = True |
271 | + adtlog.debug('can use apt-get on testbed: %s' % can_apt_get) |
272 | + |
273 | + if not can_apt_get: |
274 | + self.bomb('no root on testbed, unsupported when specifying target architecture') |
275 | + |
276 | + what = source_pkg |
277 | + if self.cross_arch: |
278 | + what += ' -a ' + self.cross_arch |
279 | + # install the build-dependencies for the specified source (which can |
280 | + # be a source package name, or a path); our apt pinning is not |
281 | + # very clever wrt. resolving transitional dependencies in the pocket, |
282 | + # so we might need to retry without pinning |
283 | + download_fail_retries = 3 |
284 | + while True: |
285 | + rc = 0 |
286 | + try: |
287 | + self._run_apt_build_dep(what, ' '.join(self.eatmydata_prefix), recommends) |
288 | + |
289 | + # check if apt failed during package download, which might be a |
290 | + # transient error, so retry |
291 | + except adtlog.AptDownloadError: |
292 | + download_fail_retries -= 1 |
293 | + if download_fail_retries > 0: |
294 | + adtlog.warning('apt failed to download packages, retrying in 10s...') |
295 | + time.sleep(10) |
296 | + continue |
297 | + else: |
298 | + self.bomb('apt repeatedly failed to download packages') |
299 | + |
300 | + except adtlog.AptPermanentError: |
301 | + rc = -1 |
302 | + if shell_on_failure: |
303 | + self.run_shell() |
304 | + |
305 | + if rc != 0: |
306 | + if self.apt_pin_for_releases and self.enable_apt_fallback: |
307 | + release = self.apt_pin_for_releases.pop() |
308 | + adtlog.warning('Test dependencies are unsatisfiable using apt pinning. ' |
309 | + 'Retrying using all packages from %s' % release) |
310 | + self.check_exec(['/bin/sh', '-ec', 'rm /etc/apt/preferences.d/autopkgtest-' + release]) |
311 | + if not self.apt_pin_for_releases: |
312 | + self.check_exec(['/bin/sh', '-ec', 'rm -f /etc/apt/preferences.d/autopkgtest-default-release']) |
313 | + continue |
314 | + |
315 | + adtlog.warning('Test dependencies are unsatisfiable - calling ' |
316 | + 'apt install on test deps directly for further ' |
317 | + 'data about failing dependencies in test logs') |
318 | + self._run_apt_build_dep('--simulate ' + what, |
319 | + ' '.join(self.eatmydata_prefix), |
320 | + recommends, ignorerc=True) |
321 | + |
322 | + if shell_on_failure: |
323 | + self.run_shell() |
324 | + if self.enable_apt_fallback: |
325 | + self.badpkg('Test dependencies are unsatisfiable. A common reason is ' |
326 | + 'that your testbed is out of date with respect to the ' |
327 | + 'archive, and you need to use a current testbed or run ' |
328 | + 'apt-get update or use -U.') |
329 | + else: |
330 | + self.badpkg('Test dependencies are unsatisfiable. A common reason is ' |
331 | + 'that the requested apt pinning prevented dependencies ' |
332 | + 'from the non-default suite to be installed. In that ' |
333 | + 'case you need to add those dependencies to the pinning ' |
334 | + 'list.') |
335 | + break |
336 | + |
337 | + def satisfy_dependencies_string(self, deps, what, recommends=False, |
338 | +>>>>>>> lib/adt_testbed.py |
339 | build_dep=False, shell_on_failure=False, synth_deps=[]): |
340 | '''Install dependencies from a string into the testbed''' |
341 | |
342 | @@ -1266,6 +1520,14 @@ Description: satisfy autopkgtest test dependencies |
343 | # helper methods |
344 | # |
345 | |
346 | + def _create_apt_pinning_for_pocket(self, release): |
347 | + '''Create apt pinning for --apt-pocket=pocket[=pkglist] with |
348 | + Pin-Priority: 500 to undo the effect of NotAutomatic: yes''' |
349 | + |
350 | + script = 'printf "Package: *\\nPin: release a=%(release)s\\nPin-Priority: 500\\n" > /etc/apt/preferences.d/autopkgtest-%(release)s-baseline; ' % \ |
351 | + {'release': release} |
352 | + self.check_exec(['sh', '-ec', script]) |
353 | + |
354 | def _create_apt_pinning_for_packages(self, release, pkglist, default_releases=[]): |
355 | '''Create apt pinning for --apt-pocket=pocket=pkglist and |
356 | --pin-packages=release=pkglist''' |
357 | @@ -1286,7 +1548,7 @@ Description: satisfy autopkgtest test dependencies |
358 | # translate src:name entries into binaries of that source |
359 | if srcpkgs: |
360 | script += 'PKGS="$PKGS $(apt-cache showsrc %s | ' \ |
361 | - '''awk '/^Package-List:/ { show=1; next } (/^ / && show==1) { print $1; next } { show=0 }' |''' \ |
362 | + '''awk '/^Package-List:/ { show=1; next } (/^ / && show==1) { print $1 ":*"; next } { show=0 }' |''' \ |
363 | '''sort -u | tr '\\n' ' ')"; ''' % \ |
364 | ' '.join(srcpkgs) |
365 | |
366 | @@ -1495,10 +1757,13 @@ def killtree(pid): |
367 | |
368 | for child in child_ps(pid): |
369 | killtree(child) |
370 | + with open('/proc/%s/cmdline' % pid, 'rb') as fd: |
371 | + cmd = fd.read() |
372 | try: |
373 | os.kill(pid, signal.SIGTERM) |
374 | except OSError: |
375 | pass |
376 | +<<<<<<< lib/adt_testbed.py |
377 | |
378 | |
379 | def locate_setup_command(name: str) -> str: |
380 | @@ -1510,3 +1775,21 @@ def locate_setup_command(name: str) -> str: |
381 | return shipped |
382 | |
383 | return name |
384 | +======= |
385 | + for _ in range(21): |
386 | + if not os.path.exists('/proc/%s' % pid): |
387 | + return |
388 | + else: |
389 | + time.sleep(0.2) |
390 | + try: |
391 | + with open('/proc/%s/cmdline' % pid, 'rb') as fd: |
392 | + if cmd != fd.read(): |
393 | + return |
394 | + except FileNotFoundError: |
395 | + return |
396 | + try: |
397 | + adtlog.info('kill with SIGTERM did not work sending SIGKILL') |
398 | + os.kill(pid, signal.SIGKILL) |
399 | + except OSError: |
400 | + pass |
401 | +>>>>>>> lib/adt_testbed.py |
402 | diff --git a/lib/adtlog.py b/lib/adtlog.py |
403 | index 4405c76..061f883 100644 |
404 | --- a/lib/adtlog.py |
405 | +++ b/lib/adtlog.py |
406 | @@ -67,7 +67,17 @@ def log(message, level, prefix='', timestamp=False, color=None): |
407 | time.sleep(0.05) |
408 | else: |
409 | raise |
410 | - sys.stderr.buffer.flush() |
411 | + retries = 10 |
412 | + while retries >= 0: |
413 | + try: |
414 | + sys.stderr.buffer.flush() |
415 | + break |
416 | + except IOError as e: |
417 | + if e.errno == errno.EAGAIN: |
418 | + retries -= 1 |
419 | + time.sleep(0.05) |
420 | + else: |
421 | + raise |
422 | |
423 | |
424 | def error(message): |
425 | diff --git a/lib/autopkgtest_args.py b/lib/autopkgtest_args.py |
426 | index b55f683..0bcc018 100644 |
427 | --- a/lib/autopkgtest_args.py |
428 | +++ b/lib/autopkgtest_args.py |
429 | @@ -186,6 +186,18 @@ for details.''' |
430 | g_test.add_argument('-B', '--no-built-binaries', dest='built_binaries', |
431 | action='store_false', default=True, |
432 | help='do not build/use binaries from .dsc, git source, or unbuilt tree') |
433 | +<<<<<<< lib/autopkgtest_args.py |
434 | +======= |
435 | + g_test.add_argument('--installed-click', metavar='CLICKNAME', |
436 | + help='Run tests from already installed click package ' |
437 | + '(e. g. "com.example.myapp"), from specified click ' |
438 | + 'source directory or manifest\'s x-source.') |
439 | + g_test.add_argument('-a', '--architecture', metavar='ARCH', |
440 | + help='run tests for (and when asked, build binaries ' |
441 | + 'for) ARCH instead of the testbed host architecture. ' |
442 | + 'Assumes ARCH is available as a foreign architecture ' |
443 | + 'on the testbed.') |
444 | +>>>>>>> lib/autopkgtest_args.py |
445 | g_test.add_argument('packages', nargs='*', |
446 | help='testsrc source package and testbinary packages as above') |
447 | |
448 | diff --git a/lib/testdesc.py b/lib/testdesc.py |
449 | index af3209f..cc0b9c8 100644 |
450 | --- a/lib/testdesc.py |
451 | +++ b/lib/testdesc.py |
452 | @@ -283,7 +283,7 @@ def _debian_check_unknown_fields(name, record): |
453 | raise Unsupported(name, 'unknown field %s' % unknown_keys.pop()) |
454 | |
455 | |
456 | -def _debian_packages_from_source(srcdir): |
457 | +def _debian_packages_from_source(srcdir, cross_arch=None): |
458 | packages = [] |
459 | packages_no_arch = [] |
460 | |
461 | @@ -296,10 +296,17 @@ def _debian_packages_from_source(srcdir): |
462 | st.get('Package-type', 'deb') != 'deb': |
463 | continue |
464 | arch = st['Architecture'] |
465 | - if arch in ('all', 'any'): |
466 | + qual_pkg = st['Package'] |
467 | + # take care to emit an arch qualifier only for arch-dependent |
468 | + # packages, not for arch: all ones |
469 | + if cross_arch: |
470 | + qual_pkg += ':' + cross_arch |
471 | + if arch == 'all': |
472 | packages.append(st['Package']) |
473 | + elif arch == 'any': |
474 | + packages.append(qual_pkg) |
475 | else: |
476 | - packages.append('%s [%s]' % (st['Package'], arch)) |
477 | + packages.append('%s [%s]' % (qual_pkg, arch)) |
478 | packages_no_arch.append(st['Package']) |
479 | |
480 | return (packages, packages_no_arch) |
481 | @@ -380,7 +387,7 @@ def _debian_build_deps_from_source(srcdir, testbed_arch): |
482 | deps = [d.strip() for d in deps.split(',')] |
483 | |
484 | # @builddeps@ should always imply build-essential |
485 | - deps.append('build-essential') |
486 | + deps.append('build-essential:native') |
487 | return deps |
488 | |
489 | |
490 | @@ -442,7 +449,8 @@ def _synthesize_deps(dep, testbed_arch): |
491 | return None |
492 | |
493 | |
494 | -def _parse_debian_depends(testname, dep_str, srcdir, testbed_arch): |
495 | +def _parse_debian_depends(testname, dep_str, srcdir, testbed_arch, |
496 | + cross_arch=None): |
497 | '''Parse Depends: line in a Debian package |
498 | |
499 | Split dependencies (comma separated), validate their syntax, and expand @, |
500 | @@ -453,7 +461,12 @@ def _parse_debian_depends(testname, dep_str, srcdir, testbed_arch): |
501 | ''' |
502 | deps = [] |
503 | synthdeps = [] |
504 | - (my_packages, my_packages_no_arch) = _debian_packages_from_source(srcdir) |
505 | + (my_packages, my_packages_no_arch) = _debian_packages_from_source(srcdir, |
506 | + cross_arch=cross_arch) |
507 | + if cross_arch: |
508 | + target_arch = cross_arch |
509 | + else: |
510 | + target_arch = testbed_arch |
511 | for alt_group_str in dep_str.split(','): |
512 | alt_group_str = alt_group_str.strip() |
513 | if not alt_group_str: |
514 | @@ -464,17 +477,32 @@ def _parse_debian_depends(testname, dep_str, srcdir, testbed_arch): |
515 | for d in my_packages: |
516 | adtlog.debug('synthesised dependency %s' % d) |
517 | deps.append(d) |
518 | - s = _synthesize_deps(d, testbed_arch) |
519 | + s = _synthesize_deps(d, target_arch) |
520 | if s: |
521 | synthdeps.append(s) |
522 | elif alt_group_str == '@builddeps@': |
523 | for d in _debian_build_deps_from_source(srcdir, testbed_arch): |
524 | adtlog.debug('synthesised dependency %s' % d) |
525 | deps.append(d) |
526 | +<<<<<<< lib/testdesc.py |
527 | elif alt_group_str == '@recommends@': |
528 | for d in _debian_recommends_from_source(srcdir, testbed_arch): |
529 | adtlog.debug('synthesised dependency %s' % d) |
530 | deps.append(d) |
531 | +======= |
532 | + elif alt_group_str == 'build-essential': |
533 | + # special case; this is how packages declare they want to build |
534 | + # code during the test (but not necessarily doing a full package |
535 | + # build), but for cross-architecture testing this declaration is |
536 | + # wrong because it tries to pull in build-essential for the wrong |
537 | + # arch. So we apply the same fix-up here that we do in |
538 | + # runner/autopkgtest. We can't expect the package maintainer to |
539 | + # do this because they don't know what crossbuild-essential-$arch |
540 | + # to pull in. |
541 | + deps.append('build-essential:native') |
542 | + if cross_arch: |
543 | + deps.append('crossbuild-essential-%s:native' % cross_arch) |
544 | +>>>>>>> lib/testdesc.py |
545 | else: |
546 | synthdep_alternatives = [] |
547 | for dep in alt_group_str.split('|'): |
548 | @@ -482,8 +510,10 @@ def _parse_debian_depends(testname, dep_str, srcdir, testbed_arch): |
549 | if pkg not in my_packages_no_arch: |
550 | synthdep_alternatives = [] |
551 | break |
552 | - s = _synthesize_deps(dep, testbed_arch) |
553 | + s = _synthesize_deps(dep, target_arch) |
554 | if s: |
555 | + if cross_arch: |
556 | + s += ':' + cross_arch |
557 | synthdep_alternatives.append(s) |
558 | if synthdep_alternatives: |
559 | adtlog.debug('marked alternatives %s as a synthesised dependency' % synthdep_alternatives) |
560 | @@ -491,6 +521,13 @@ def _parse_debian_depends(testname, dep_str, srcdir, testbed_arch): |
561 | synthdeps.append(synthdep_alternatives) |
562 | else: |
563 | synthdeps.append(synthdep_alternatives[0]) |
564 | + if cross_arch: |
565 | + for mine in my_packages: |
566 | + # ignore [arch] filters for matching packages |
567 | + if alt_group_str + ':' + cross_arch == mine.split(' ')[0]: |
568 | + adtlog.debug('%s is from our source package, adding arch qualifier for cross-testing' % s) |
569 | + alt_group_str += ':' + cross_arch |
570 | + break |
571 | deps.append(alt_group_str) |
572 | |
573 | return (deps, synthdeps) |
574 | @@ -582,8 +619,13 @@ def _check_architecture(name, testbed_arch, architectures): |
575 | |
576 | |
577 | def parse_debian_source(srcdir, testbed_caps, testbed_arch, control_path=None, |
578 | +<<<<<<< lib/testdesc.py |
579 | auto_control=True, ignore_restrictions=(), |
580 | only_tests=()): |
581 | +======= |
582 | + auto_control=True, cross_arch=None, ignore_restrictions=(), |
583 | + testname=None): |
584 | +>>>>>>> lib/testdesc.py |
585 | '''Parse test descriptions from a Debian DEP-8 source dir |
586 | |
587 | @ignore_restrictions: If we would skip the test due to these restrictions, |
588 | @@ -662,6 +704,11 @@ def parse_debian_source(srcdir, testbed_caps, testbed_arch, control_path=None, |
589 | architectures = record.get('Architecture', '').replace( |
590 | ',', ' ').split() |
591 | |
592 | + if cross_arch: |
593 | + target_arch = cross_arch |
594 | + else: |
595 | + target_arch = testbed_arch |
596 | + |
597 | if 'Tests' in record: |
598 | test_names = record['Tests'].replace(',', ' ').split() |
599 | if len(test_names) == 0: |
600 | @@ -670,7 +717,7 @@ def parse_debian_source(srcdir, testbed_caps, testbed_arch, control_path=None, |
601 | test_names[0], |
602 | record.get('Depends', '@'), |
603 | srcdir, |
604 | - testbed_arch) |
605 | + testbed_arch, cross_arch=cross_arch) |
606 | if 'Test-command' in record: |
607 | raise InvalidControl('*', 'Only one of "Tests" or ' |
608 | '"Test-Command" may be given') |
609 | @@ -682,7 +729,7 @@ def parse_debian_source(srcdir, testbed_caps, testbed_arch, control_path=None, |
610 | for n in test_names: |
611 | try: |
612 | _debian_check_unknown_fields(n, record) |
613 | - _check_architecture(n, testbed_arch, architectures) |
614 | + _check_architecture(n, target_arch, architectures) |
615 | |
616 | test = Test(n, os.path.join(test_dir, n), None, |
617 | restrictions, features, depends, synth_depends) |
618 | @@ -699,14 +746,14 @@ def parse_debian_source(srcdir, testbed_caps, testbed_arch, control_path=None, |
619 | command, |
620 | record.get('Depends', '@'), |
621 | srcdir, |
622 | - testbed_arch) |
623 | + testbed_arch, cross_arch=cross_arch) |
624 | if feature_test_name is None: |
625 | command_counter += 1 |
626 | name = 'command%i' % command_counter |
627 | else: |
628 | name = feature_test_name |
629 | _debian_check_unknown_fields(name, record) |
630 | - _check_architecture(name, testbed_arch, architectures) |
631 | + _check_architecture(name, target_arch, architectures) |
632 | test = Test(name, None, command, restrictions, features, |
633 | depends, synth_depends) |
634 | test.check_testbed_compat(testbed_caps, ignore_restrictions) |
635 | diff --git a/runner/autopkgtest b/runner/autopkgtest |
636 | index 9eb01fe..0ca7446 100755 |
637 | --- a/runner/autopkgtest |
638 | +++ b/runner/autopkgtest |
639 | @@ -117,6 +117,8 @@ def setup_trace(): |
640 | adtlog.enable_colors = False |
641 | os.dup2(out_tee.stdin.fileno(), sys.stdout.fileno()) |
642 | os.dup2(err_tee.stdin.fileno(), sys.stderr.fileno()) |
643 | + os.set_blocking(sys.stdout.fileno(), True) |
644 | + os.set_blocking(sys.stderr.fileno(), True) |
645 | |
646 | def cleanup(): |
647 | os.close(sys.stdout.fileno()) |
648 | @@ -160,7 +162,13 @@ def run_tests(tests, tree): |
649 | doTest = True |
650 | |
651 | try: |
652 | +<<<<<<< runner/autopkgtest |
653 | testbed.install_deps(t.depends, opts.shell_fail, t.synth_depends) |
654 | +======= |
655 | + testbed.install_deps(t.depends, |
656 | + 'needs-recommends' in t.restrictions, |
657 | + opts.shell_fail, t.synth_depends) |
658 | +>>>>>>> runner/autopkgtest |
659 | except adtlog.BadPackageError as e: |
660 | if 'skip-not-installable' in t.restrictions: |
661 | errorcode |= 2 |
662 | @@ -392,6 +400,12 @@ def build_source(kind, arg, built_binaries): |
663 | return tests_tree |
664 | |
665 | elif kind == 'apt-source': |
666 | + # Make sure we are selecting the binaries based on the actual target |
667 | + # architecture, not necessarily the testbed architecture |
668 | + if opts.architecture: |
669 | + arch = opts.architecture |
670 | + else: |
671 | + arch = testbed.dpkg_arch |
672 | # The default is to determine the version for "apt-get source |
673 | # pkg=version" that conforms to the current apt pinning. We only |
674 | # consider binaries which are shipped in all available versions, |
675 | @@ -447,7 +461,7 @@ pkgs=$(echo "$pkgs\n" | awk " |
676 | if (foundarch == 0 || archmatch == 1) thissrc[\\$1] = 1; |
677 | next } |
678 | { if (!inlist) next; |
679 | - inlist=0;''' % {'src': arg, 'arch': testbed.dpkg_arch} |
680 | + inlist=0;''' % {'src': arg, 'arch': arch} |
681 | |
682 | create_command_part2_check_all_pkgs = ''' |
683 | remaining=0; |
684 | @@ -609,24 +623,8 @@ dpkg-source -x %(src)s_*.dsc src >/dev/null''' % {'src': arg} |
685 | if kind not in ['dsc', 'apt-source']: |
686 | testbed.install_deps([]) |
687 | |
688 | - if kind in ('apt-source', 'git-source'): |
689 | - # we need to get the downloaded debian/control from the testbed, so |
690 | - # that we can avoid calling "apt-get build-dep" and thus |
691 | - # introducing a second mechanism for installing build deps |
692 | - pkg_control = adt_testbed.Path(testbed, |
693 | - os.path.join(tmp, 'apt-control'), |
694 | - os.path.join(result_pwd, 'debian/control'), False) |
695 | - pkg_control.copyup() |
696 | - dsc = pkg_control.host |
697 | - |
698 | - with open(dsc, encoding='UTF-8') as f: |
699 | - d = deb822.Deb822(sequence=f) |
700 | - bd = d.get('Build-Depends', '') |
701 | - bdi = d.get('Build-Depends-Indep', '') |
702 | - bda = d.get('Build-Depends-Arch', '') |
703 | - |
704 | # determine build command and build-essential packages |
705 | - build_essential = ['build-essential'] |
706 | + build_essential = ['build-essential:native'] |
707 | assert testbed.nproc |
708 | dpkg_buildpackage = 'DEB_BUILD_OPTIONS="parallel=%s $DEB_BUILD_OPTIONS" dpkg-buildpackage -us -uc -b' % ( |
709 | opts.build_parallel or testbed.nproc) |
710 | @@ -636,8 +634,46 @@ dpkg-source -x %(src)s_*.dsc src >/dev/null''' % {'src': arg} |
711 | if testbed.user or 'root-on-testbed' not in testbed.caps: |
712 | build_essential += ['fakeroot'] |
713 | |
714 | - testbed.satisfy_dependencies_string(bd + ', ' + bdi + ', ' + bda + ', ' + ', '.join(build_essential), arg, |
715 | - build_dep=True, shell_on_failure=opts.shell_fail) |
716 | + if opts.architecture: |
717 | + dpkg_buildpackage += ' -a' + opts.architecture |
718 | + build_essential += ['crossbuild-essential-%s:native' % opts.architecture] |
719 | + # apt-get build-dep is the best option here, but we don't call |
720 | + # it unconditionally because it doesn't take a path to a source |
721 | + # package as an option in very old releases; so for compatibility |
722 | + # only use it when we need its multiarch build-dep resolution |
723 | + # support. |
724 | + # This is supported in apt 1.1~exp2 and newer, which covers |
725 | + # Debian oldstable (at time of writing) and Ubuntu 16.04 and |
726 | + # newer, so once we can rely on this version of apt everywhere, |
727 | + # the old build-dep resolver code should be deprecated. |
728 | + all_build_deps = '' |
729 | + testbed.satisfy_build_deps(result_pwd, |
730 | + shell_on_failure=opts.shell_fail) |
731 | + else: |
732 | + if kind in ('apt-source', 'git-source'): |
733 | + # we need to get the downloaded debian/control from the |
734 | + # testbed, so that we can avoid calling "apt-get build-dep" |
735 | + # and thus introducing a second mechanism for installing |
736 | + # build deps |
737 | + pkg_control = adt_testbed.Path(testbed, |
738 | + os.path.join(tmp, 'apt-control'), |
739 | + os.path.join(result_pwd, 'debian/control'), False) |
740 | + pkg_control.copyup() |
741 | + dsc = pkg_control.host |
742 | + |
743 | + with open(dsc, encoding='UTF-8') as f: |
744 | + d = deb822.Deb822(sequence=f) |
745 | + bd = d.get('Build-Depends', '') |
746 | + bdi = d.get('Build-Depends-Indep', '') |
747 | + bda = d.get('Build-Depends-Arch', '') |
748 | + |
749 | + all_build_deps = bd + ', ' + bdi + ', ' + bda + ', ' |
750 | + |
751 | + testbed.satisfy_dependencies_string(all_build_deps + |
752 | + ', '.join(build_essential), |
753 | + arg, |
754 | + build_dep=True, |
755 | + shell_on_failure=opts.shell_fail) |
756 | |
757 | # keep patches applied for tests |
758 | source_rules_command([dpkg_buildpackage, 'dpkg-source --before-build .'], 'build', cwd=result_pwd) |
759 | @@ -709,6 +745,7 @@ def process_actions(): |
760 | adtlog.debug('cleaning up previous tests tree %s on testbed' % tests_tree.tb) |
761 | testbed.execute(['rm', '-rf', tests_tree.tb]) |
762 | |
763 | +<<<<<<< runner/autopkgtest |
764 | tests_tree = build_source(kind, arg, built_binaries) |
765 | try: |
766 | (tests, skipped) = testdesc.parse_debian_source( |
767 | @@ -719,6 +756,56 @@ def process_actions(): |
768 | only_tests=only_tests) |
769 | except testdesc.InvalidControl as e: |
770 | adtlog.badpkg(str(e)) |
771 | +======= |
772 | + if kind == 'click': |
773 | + if control_override: |
774 | + # locally specified manifest |
775 | + with open(control_override) as f: |
776 | + manifest = f.read() |
777 | + clicks = [] |
778 | + use_installed = False |
779 | + if os.path.exists(arg): |
780 | + clicks.append(arg) |
781 | + use_installed = True |
782 | + (srcdir, tests, skipped) = testdesc.parse_click_manifest( |
783 | + manifest, testbed.caps, clicks, use_installed, pending_click_source, |
784 | + opts.ignore_restrictions, testname=testname) |
785 | + |
786 | + elif os.path.exists(arg): |
787 | + # local .click package file |
788 | + (srcdir, tests, skipped) = testdesc.parse_click( |
789 | + arg, testbed.caps, srcdir=pending_click_source) |
790 | + else: |
791 | + # already installed click package name |
792 | + if testbed.user: |
793 | + u = ['--user', testbed.user] |
794 | + else: |
795 | + u = [] |
796 | + manifest = testbed.check_exec(['click', 'info'] + u + [arg], stdout=True) |
797 | + (srcdir, tests, skipped) = testdesc.parse_click_manifest( |
798 | + manifest, testbed.caps, [], True, pending_click_source, |
799 | + opts.ignore_restrictions, testname=testname) |
800 | + |
801 | + if not srcdir: |
802 | + adtlog.bomb('No click source available for %s' % arg) |
803 | + |
804 | + tests_tree = adt_testbed.Path( |
805 | + testbed, srcdir, os.path.join(testbed.scratch, 'tree'), |
806 | + is_dir=True) |
807 | + pending_click_source = None |
808 | + else: |
809 | + tests_tree = build_source(kind, arg, built_binaries) |
810 | + try: |
811 | + (tests, skipped) = testdesc.parse_debian_source( |
812 | + tests_tree.host, testbed.caps, testbed.dpkg_arch, |
813 | + control_path=control_override, |
814 | + auto_control=opts.auto_control, |
815 | + ignore_restrictions=opts.ignore_restrictions, |
816 | + testname=testname, |
817 | + cross_arch=opts.architecture) |
818 | + except testdesc.InvalidControl as e: |
819 | + adtlog.badpkg(str(e)) |
820 | +>>>>>>> runner/autopkgtest |
821 | |
822 | if opts.validate: |
823 | adtlog.report("*", "Test specification is valid") |
824 | @@ -768,6 +855,8 @@ def main(): |
825 | signal.signal(signal.SIGTERM, signal_handler) |
826 | signal.signal(signal.SIGQUIT, signal_handler) |
827 | |
828 | + os.set_blocking(sys.stderr.fileno(), True) |
829 | + |
830 | try: |
831 | setup_trace() |
832 | testbed = adt_testbed.Testbed(vserver_argv=vserver_args, |
833 | @@ -783,7 +872,8 @@ def main(): |
834 | add_apt_sources=getattr(opts, 'add_apt_sources', []), |
835 | add_apt_releases=getattr(opts, 'add_apt_releases', []), |
836 | pin_packages=opts.pin_packages, |
837 | - apt_default_release=opts.apt_default_release) |
838 | + apt_default_release=opts.apt_default_release, |
839 | + cross_arch=opts.architecture) |
840 | testbed.start() |
841 | testbed.open() |
842 | process_actions() |
843 | diff --git a/runner/autopkgtest.1 b/runner/autopkgtest.1 |
844 | index 3fe795a..63c83e6 100644 |
845 | --- a/runner/autopkgtest.1 |
846 | +++ b/runner/autopkgtest.1 |
847 | @@ -103,6 +103,13 @@ is implied. |
848 | .SH TEST OPTIONS |
849 | |
850 | .TP |
851 | +.BR -a " ARCH" " | --architecture=" ARCH |
852 | + |
853 | +Run tests for the specified architecture, rather than for the host |
854 | +architecture as defined by dpkg \-\-print-architecture. When building |
855 | +packages from source, cross-build for the target architecture as well. |
856 | + |
857 | +.TP |
858 | .BR -B " | " --no-built-binaries |
859 | Binaries from unbuilt source packages (see above) |
860 | will not be built or ignored, and dependencies are satisfied with packages from |
861 | @@ -262,7 +269,9 @@ that pocket to |
862 | .B /etc/apt/sources.list.d/\fIpocket\fB.list\fR. |
863 | This also calls |
864 | .B apt-get update |
865 | -for the new pocket (but not for anything else). |
866 | +for the new pocket (but not for anything else). The pocket will be pinned with |
867 | +Pin-Priority: 500, so the "NotAutomatic: yes" setting will have no effect on |
868 | +the testbed system. |
869 | |
870 | If a package list is given after =, set up apt pinning to use only those |
871 | packages from |
872 | diff --git a/setup-commands/setup-testbed b/setup-commands/setup-testbed |
873 | index 1b801d5..87645d6 100755 |
874 | --- a/setup-commands/setup-testbed |
875 | +++ b/setup-commands/setup-testbed |
876 | @@ -315,6 +315,7 @@ fi |
877 | |
878 | # go-faster apt/dpkg |
879 | echo "Acquire::Languages \"none\";" > "$root"/etc/apt/apt.conf.d/90nolanguages |
880 | +echo "Acquire::Retries \"10\";" > "$root"/etc/apt/apt.conf.d/90retry |
881 | echo 'force-unsafe-io' > "$root"/etc/dpkg/dpkg.cfg.d/autopkgtest |
882 | |
883 | # avoid failures do to transient timeouts |
884 | @@ -427,10 +428,19 @@ if [ -x /sbin/init ]; then |
885 | chroot "$root" apt-get install -y libpam-systemd </dev/null |
886 | fi |
887 | |
888 | +<<<<<<< setup-commands/setup-testbed |
889 | # some tests use a lot of /dev/random, avoid hangs |
890 | if ! systemd-detect-virt --quiet --container; then |
891 | chroot "$root" apt-get install -y rng-tools </dev/null |
892 | fi |
893 | +======= |
894 | +if ! systemd-detect-virt --quiet --container; then |
895 | + chroot "$root" apt-get install -y haveged </dev/null |
896 | +fi |
897 | + |
898 | +if [ ! -e "$root/usr/share/doc/libpam-systemd" ] && chroot "$root" apt-cache show libpam-systemd >/dev/null 2>&1; then |
899 | + chroot "$root" apt-get install -y libpam-systemd </dev/null |
900 | +>>>>>>> setup-commands/setup-testbed |
901 | fi |
902 | |
903 | # optimization as we need to install it for most tests anyway |
904 | @@ -456,7 +466,11 @@ if [ -z "${AUTOPKGTEST_IS_SETUP_COMMAND:-}" ]; then |
905 | unattended-upgrades update-notifier-common ureadahead debootstrap \ |
906 | lxcfs ppp pppconfig pppoeconf snapd snap-confine ubuntu-core-launcher \ |
907 | thermald xdg-user-dirs zerofree xml-core needrestart; do |
908 | +<<<<<<< setup-commands/setup-testbed |
909 | if package_is_present "$p"; then |
910 | +======= |
911 | + if [ -d "$root/usr/share/doc/$p" ]; then |
912 | +>>>>>>> setup-commands/setup-testbed |
913 | purge_list="$purge_list $p" |
914 | fi |
915 | done |
916 | @@ -522,7 +536,10 @@ echo 'APT::Periodic::Enable "0";' > "$root/etc/apt/apt.conf.d/02periodic" |
917 | |
918 | # always include phased updates, so that the output is what we expect. |
919 | echo 'APT::Get::Always-Include-Phased-Updates "true";' > "$root/etc/apt/apt.conf.d/90always-include-phased-updates" |
920 | +<<<<<<< setup-commands/setup-testbed |
921 | |
922 | if [ -n "$need_update_initramfs" ]; then |
923 | chroot "$root" update-initramfs -u |
924 | fi |
925 | +======= |
926 | +>>>>>>> setup-commands/setup-testbed |
927 | diff --git a/ssh-setup/nova b/ssh-setup/nova |
928 | index ad1c5f2..b828b1c 100755 |
929 | --- a/ssh-setup/nova |
930 | +++ b/ssh-setup/nova |
931 | @@ -78,7 +78,7 @@ FLOATING_IP="" |
932 | SECURITY_GROUPS="" |
933 | EXTRA_ENV="" |
934 | MIRROR="" |
935 | -NOVA_REBOOT="" |
936 | +NOVA_REBOOT="1" |
937 | |
938 | DEBUG="" |
939 | |
940 | @@ -227,6 +227,9 @@ EOF |
941 | imageuuid=$(echo "$last" | cut -f1) |
942 | image=$(echo "$last" | cut -f2) |
943 | |
944 | + echo "Waiting..." >&2 |
945 | + flock -x /home/ubuntu/$OS_REGION_NAME.boot.lock sleep 30 |
946 | + |
947 | # Boot a nova instance and returns its connection parameters |
948 | [ -n "$SRVNAME" ] || SRVNAME=`mktemp -u autopkgtest-nova-XXXXXX` |
949 | echo "Creating nova instance $SRVNAME from image ${image} (UUID $imageuuid)..." >&2 |
950 | @@ -253,7 +256,7 @@ EOF |
951 | EXTRA_OPTS='' |
952 | if [ -n "$NET_ID" ]; then |
953 | # translate a name into a UUID |
954 | - OUT=$(nova network-show $NET_ID) |
955 | + OUT=$(openstack network show $NET_ID 2>/dev/null) |
956 | NET_ID="$(echo "$OUT"| awk -F'|' '/ id / {gsub(" ", "", $3); print $3}')" |
957 | EXTRA_OPTS="$EXTRA_OPTS --nic net-id=$NET_ID" |
958 | fi |
959 | @@ -315,7 +318,7 @@ EOF |
960 | OUT=$(nova show --minimal $SRVUUID) |
961 | ipaddr=$(echo "$OUT" | awk 'BEGIN {FS="|"} $2 ~ /network/ {n=split($3,i,/,\s*/); gsub(" ", "", i[n]); print i[n]}') |
962 | retry=$(( retry - 1 )) |
963 | - if [ $retry -le 0 ]; then |
964 | + if [ $retry -le 0 ] || echo "$OUT" | grep -q 'network\s*|.*,'; then |
965 | error "Failed to acquire an IP address. Aborting!" |
966 | error "$OUT" |
967 | cleanup |
968 | diff --git a/tests/autopkgtest b/tests/autopkgtest |
969 | index 985d07b..95ef393 100755 |
970 | --- a/tests/autopkgtest |
971 | +++ b/tests/autopkgtest |
972 | @@ -2256,7 +2256,7 @@ Restrictions: needs-root |
973 | self.assertIn('synthesised dependency bdep4\n', err) |
974 | self.assertIn('synthesised dependency bdep5\n', err) |
975 | self.assertIn('synthesised dependency bdep6\n', err) |
976 | - self.assertIn('synthesised dependency build-essential\n', err) |
977 | + self.assertIn('synthesised dependency build-essential:native\n', err) |
978 | self.assertIn('processing dependency testdep2\n', err) |
979 | |
980 | def test_build_deps_profiles(self): |
981 | @@ -2276,7 +2276,7 @@ Restrictions: needs-root |
982 | self.assertRegex(out, r'pass\s+PASS') |
983 | |
984 | self.assertIn('synthesised dependency bdepyes\n', err) |
985 | - self.assertIn('synthesised dependency build-essential\n', err) |
986 | + self.assertIn('synthesised dependency build-essential:native\n', err) |
987 | |
988 | dpkg_deps_ver = subprocess.check_output(['perl', '-MDpkg::Deps', '-e', 'print $Dpkg::Deps::VERSION'], |
989 | universal_newlines=True) |
990 | diff --git a/tests/testdesc b/tests/testdesc |
991 | index 236df41..48e29c4 100755 |
992 | --- a/tests/testdesc |
993 | +++ b/tests/testdesc |
994 | @@ -401,7 +401,7 @@ Recommends: ${package:one}, two (<= 10) <!nocheck>, three (<= ${ver:three}~), fo |
995 | 'Package: one\nArchitecture: any') |
996 | self.assertEqual(ts[0].depends, ['one', 'bd1', 'bd3:native (>= 7) | bd4', |
997 | 'bdi1', 'bdi2', 'bda1', 'bda2', |
998 | - 'build-essential', 'foo (>= 7)']) |
999 | + 'build-essential:native', 'foo (>= 7)']) |
1000 | self.assertFalse(skipped) |
1001 | |
1002 | @unittest.skipUnless(have_dpkg_build_profiles, |
1003 | @@ -414,7 +414,7 @@ Recommends: ${package:one}, two (<= 10) <!nocheck>, three (<= ${ver:three}~), fo |
1004 | 'Source: nums\nBuild-Depends: bd1, bd2 <!check>, bd3 <!cross>, bdnotme <stage1> <cross>\n' |
1005 | '\n' |
1006 | 'Package: one\nArchitecture: any') |
1007 | - self.assertEqual(ts[0].depends, ['one', 'bd1', 'bd2', 'bd3', 'build-essential']) |
1008 | + self.assertEqual(ts[0].depends, ['one', 'bd1', 'bd2', 'bd3', 'build-essential:native']) |
1009 | self.assertFalse(skipped) |
1010 | |
1011 | def test_complex_deps(self): |
1012 | @@ -463,7 +463,7 @@ Recommends: ${package:one}, two (<= 10) <!nocheck>, three (<= ${ver:three}~), fo |
1013 | ' , bd2\n' |
1014 | '\n' |
1015 | 'Package: one\nArchitecture: any') |
1016 | - self.assertEqual(ts[0].depends, ['one', 'bd1', 'bd2', 'build-essential']) |
1017 | + self.assertEqual(ts[0].depends, ['one', 'bd1', 'bd2', 'build-essential:native']) |
1018 | self.assertFalse(skipped) |
1019 | |
1020 | @patch('adtlog.report') |
1021 | diff --git a/tools/autopkgtest-build-lxd b/tools/autopkgtest-build-lxd |
1022 | index 14601db..a0a3c88 100755 |
1023 | --- a/tools/autopkgtest-build-lxd |
1024 | +++ b/tools/autopkgtest-build-lxd |
1025 | @@ -69,6 +69,12 @@ setup() { |
1026 | fi |
1027 | |
1028 | sleep 5 |
1029 | + |
1030 | + # Workaround for LP: #1952084 |
1031 | + # Drop if fixed or if we switch to minimal images in armhf-lxd.userdata. |
1032 | + # This has to go before the "we get a numeric runlevel" check below. |
1033 | + lxc exec "$CONTAINER" -- systemctl mask snapd.seeded.service --now |
1034 | + |
1035 | if lxc exec "$CONTAINER" -- systemctl mask serial-getty@getty.service; then |
1036 | lxc exec "$CONTAINER" -- reboot |
1037 | fi |
1038 | @@ -86,6 +92,10 @@ setup() { |
1039 | exit 1 |
1040 | } |
1041 | |
1042 | + # Workaround for LP: #1989603 |
1043 | + # Drop if fixed or if we switch to minimal images in armhf-lxd.userdata. |
1044 | + lxc exec "$CONTAINER" -- umount /var/snap/lxd/common/var/lib/lxcfs || true |
1045 | + |
1046 | # wait until a systemd based container has networking |
1047 | if ! echo '[ ! -d /run/systemd/system ] || systemctl start network-online.target' | timeout 60 lxc exec "$CONTAINER" -- sh -e; then |
1048 | echo "Timed out waiting for container to start network-online.target" >&2 |
1049 | diff --git a/virt/autopkgtest-virt-lxc b/virt/autopkgtest-virt-lxc |
1050 | index 61e7eed..015ac4f 100755 |
1051 | --- a/virt/autopkgtest-virt-lxc |
1052 | +++ b/virt/autopkgtest-virt-lxc |
1053 | @@ -303,7 +303,11 @@ def hook_revert(): |
1054 | hook_open() |
1055 | |
1056 | |
1057 | +<<<<<<< virt/autopkgtest-virt-lxc |
1058 | def hook_wait_reboot(*func_args, **kwargs): |
1059 | +======= |
1060 | +def hook_wait_reboot(*args, **kwargs): |
1061 | +>>>>>>> virt/autopkgtest-virt-lxc |
1062 | adtlog.debug('hook_wait_reboot: waiting for container to shut down...') |
1063 | VirtSubproc.execute_timeout(None, 65, sudoify( |
1064 | ['lxc-wait', '-n', lxc_container_name, '-s', 'STOPPED', '-t', '60'])) |
1065 | diff --git a/virt/autopkgtest-virt-lxd b/virt/autopkgtest-virt-lxd |
1066 | index 373f2ed..cb4a10b 100755 |
1067 | --- a/virt/autopkgtest-virt-lxd |
1068 | +++ b/virt/autopkgtest-virt-lxd |
1069 | @@ -81,7 +81,7 @@ def get_available_container_name(): |
1070 | rnd = [random.choice(string.ascii_lowercase) for i in range(6)] |
1071 | candidate = 'autopkgtest-lxd-' + ''.join(rnd) |
1072 | |
1073 | - rc = VirtSubproc.execute_timeout(None, 10, ['lxc', 'info', candidate], |
1074 | + rc = VirtSubproc.execute_timeout(None, 100, ['lxc', 'info', candidate], |
1075 | stdout=subprocess.DEVNULL, |
1076 | stderr=subprocess.STDOUT)[0] |
1077 | if rc != 0: |
1078 | @@ -89,7 +89,35 @@ def get_available_container_name(): |
1079 | |
1080 | |
1081 | def wait_booted(): |
1082 | +<<<<<<< virt/autopkgtest-virt-lxd |
1083 | VirtSubproc.wait_booted(['lxc', 'exec', container_name, '--'], timeout=180) |
1084 | +======= |
1085 | + '''Wait until the container has sufficiently booted to interact with it |
1086 | + |
1087 | + Do this by checking that the runlevel is someting numeric, i. e. not |
1088 | + "unknown" or "S". |
1089 | + ''' |
1090 | + timeout = 60 |
1091 | + while timeout > 0: |
1092 | + timeout -= 1 |
1093 | + time.sleep(1) |
1094 | + (rc, out, _) = VirtSubproc.execute_timeout( |
1095 | + None, 100, ['lxc', 'exec', container_name, 'runlevel'], |
1096 | + stdout=subprocess.PIPE) |
1097 | + if rc != 0: |
1098 | + adtlog.debug('wait_booted: lxc exec failed, retrying...') |
1099 | + continue |
1100 | + out = out.strip() |
1101 | + if out.split()[-1].isdigit(): |
1102 | + adtlog.debug('waiting for network') |
1103 | + VirtSubproc.check_exec(['lxc', 'exec', container_name, '--', 'sh', '-ec', r'if [ -d /run/systemd/system ]; then systemctl start network-online.target; else while ps -ef | grep -q "/etc/init\.d/rc"; do sleep 1; done; fi'], timeout=60) |
1104 | + return |
1105 | + |
1106 | + adtlog.debug('wait_booted: runlevel "%s", retrying...' % out) |
1107 | + |
1108 | + VirtSubproc.bomb('timed out waiting for container %s to start; ' |
1109 | + 'last runlevel "%s"' % (container_name, out)) |
1110 | +>>>>>>> virt/autopkgtest-virt-lxd |
1111 | |
1112 | |
1113 | def determine_normal_user(): |
1114 | @@ -102,7 +130,7 @@ def determine_normal_user(): |
1115 | cmd = ['lxc', 'exec', container_name, '--', 'sh', '-c', |
1116 | 'getent passwd | sort -t: -nk3 | ' |
1117 | "awk -F: '{if ($3 >= 1000 && $3 <= 59999) { print $1; exit } }'"] |
1118 | - out = VirtSubproc.execute_timeout(None, 10, cmd, |
1119 | + out = VirtSubproc.execute_timeout(None, 100, cmd, |
1120 | stdout=subprocess.PIPE)[1].strip() |
1121 | if out: |
1122 | normal_user = out |
1123 | @@ -184,7 +212,7 @@ def hook_revert(): |
1124 | def get_uptime(): |
1125 | try: |
1126 | (rc, out, _) = VirtSubproc.execute_timeout( |
1127 | - None, 10, ['lxc', 'exec', container_name, '--', 'cat', '/proc/uptime'], |
1128 | + None, 100, ['lxc', 'exec', container_name, '--', 'cat', '/proc/uptime'], |
1129 | stdout=subprocess.PIPE, stderr=subprocess.PIPE) |
1130 | |
1131 | if rc != 0: |
1132 | diff --git a/virt/autopkgtest-virt-qemu b/virt/autopkgtest-virt-qemu |
1133 | index 2b5b3fe..838c076 100755 |
1134 | --- a/virt/autopkgtest-virt-qemu |
1135 | +++ b/virt/autopkgtest-virt-qemu |
1136 | @@ -641,10 +641,14 @@ def hook_prepare_reboot() -> None: |
1137 | monitor.close() |
1138 | |
1139 | |
1140 | +<<<<<<< virt/autopkgtest-virt-qemu |
1141 | def hook_wait_reboot(*func_args: Any, **kwargs: Any) -> None: |
1142 | assert args is not None |
1143 | assert qemu is not None |
1144 | |
1145 | +======= |
1146 | +def hook_wait_reboot(*func_args, **kwargs): |
1147 | +>>>>>>> virt/autopkgtest-virt-qemu |
1148 | os.unlink(os.path.join(qemu.shareddir, 'done_shared')) |
1149 | wait_boot() |
1150 | tty = setup_shell() |
1151 | diff --git a/virt/autopkgtest-virt-ssh b/virt/autopkgtest-virt-ssh |
1152 | index 186ebe6..230bb74 100755 |
1153 | --- a/virt/autopkgtest-virt-ssh |
1154 | +++ b/virt/autopkgtest-virt-ssh |
1155 | @@ -29,6 +29,7 @@ |
1156 | import sys |
1157 | import os |
1158 | import argparse |
1159 | +import errno |
1160 | import tempfile |
1161 | import shlex |
1162 | import shutil |
1163 | @@ -58,6 +59,7 @@ sshconfig = {'identity': None, |
1164 | # Note: Running in jenkins might require -tt |
1165 | sshopts = '-q -o BatchMode=yes -o UserKnownHostsFile=/dev/null '\ |
1166 | '-o StrictHostKeyChecking=no -o CheckHostIP=no '\ |
1167 | + '-o ConnectionAttempts=20 '\ |
1168 | '-o ControlMaster=auto -o ControlPersist=60 '\ |
1169 | '-o ControlPath=%s/ssh_control-%%r@%%h:%%p' |
1170 | |
1171 | @@ -205,7 +207,17 @@ def execute_setup_script(command, fail_ok=False, print_stderr=True): |
1172 | stderr=subprocess.PIPE) |
1173 | if print_stderr: |
1174 | # Keep outputting the error on stderr as well as capturing it |
1175 | - sys.stderr.write(err) |
1176 | + retries = 10 |
1177 | + while retries >= 0: |
1178 | + try: |
1179 | + sys.stderr.write(err) |
1180 | + break |
1181 | + except IOError as e: |
1182 | + if e.errno == errno.EAGAIN: |
1183 | + retries -= 1 |
1184 | + time.sleep(0.05) |
1185 | + else: |
1186 | + raise |
1187 | if status != 0: |
1188 | err = 'setup script failed with code %i: %s' % (status, |
1189 | ' '.join(cmd)) |
1190 | @@ -346,16 +358,16 @@ def build_auxverb(): |
1191 | # create remote wrapper |
1192 | rc = VirtSubproc.execute_timeout( |
1193 | terminal_kill_wrapper % extra_cmd, 30, sshcmd + |
1194 | - ['rm -f /tmp/autopkgtest-run-wrapper; set -C; cat > /tmp/autopkgtest-run-wrapper; chmod 755 /tmp/autopkgtest-run-wrapper'], |
1195 | + ['rm -f /var/tmp/autopkgtest-run-wrapper; set -C; cat > /var/tmp/autopkgtest-run-wrapper; chmod 755 /var/tmp/autopkgtest-run-wrapper'], |
1196 | stdin=subprocess.PIPE)[0] |
1197 | if rc != 0: |
1198 | - VirtSubproc.bomb('Failed to create /tmp/autopkgtest-run-wrapper') |
1199 | + VirtSubproc.bomb('Failed to create /var/tmp/autopkgtest-run-wrapper') |
1200 | |
1201 | # create local auxverb script |
1202 | auxverb = os.path.join(workdir, 'runcmd') |
1203 | with open(auxverb, 'w') as f: |
1204 | f.write('''#!/bin/bash |
1205 | -exec %s -- %s /tmp/autopkgtest-run-wrapper $(printf '%%q ' "${@%% }") |
1206 | +exec %s -- %s /var/tmp/autopkgtest-run-wrapper $(printf '%%q ' "${@%% }") |
1207 | ''' % (" ".join(sshcmd), sudocmd or '')) |
1208 | os.chmod(auxverb, 0o755) |
1209 | VirtSubproc.auxverb = [auxverb] |
1210 | @@ -517,6 +529,7 @@ def hook_cleanup(): |
1211 | def hook_capabilities(): |
1212 | return capabilities |
1213 | |
1214 | - |
1215 | +# Somehow it ends up non-blocking |
1216 | +os.set_blocking(sys.stderr.fileno(), True) |
1217 | parse_args() |
1218 | VirtSubproc.main() |