Merge ~paride/autopkgtest:nova-reboot into ~ubuntu-release/autopkgtest/+git/autopkgtest:master

Proposed by Paride Legovini
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
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.

To post a comment you must log in.

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

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
1diff --git a/lib/adt_testbed.py b/lib/adt_testbed.py
2index 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
402diff --git a/lib/adtlog.py b/lib/adtlog.py
403index 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):
425diff --git a/lib/autopkgtest_args.py b/lib/autopkgtest_args.py
426index 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
448diff --git a/lib/testdesc.py b/lib/testdesc.py
449index 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)
635diff --git a/runner/autopkgtest b/runner/autopkgtest
636index 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()
843diff --git a/runner/autopkgtest.1 b/runner/autopkgtest.1
844index 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
872diff --git a/setup-commands/setup-testbed b/setup-commands/setup-testbed
873index 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
927diff --git a/ssh-setup/nova b/ssh-setup/nova
928index 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
968diff --git a/tests/autopkgtest b/tests/autopkgtest
969index 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)
990diff --git a/tests/testdesc b/tests/testdesc
991index 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')
1021diff --git a/tools/autopkgtest-build-lxd b/tools/autopkgtest-build-lxd
1022index 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
1049diff --git a/virt/autopkgtest-virt-lxc b/virt/autopkgtest-virt-lxc
1050index 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']))
1065diff --git a/virt/autopkgtest-virt-lxd b/virt/autopkgtest-virt-lxd
1066index 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:
1132diff --git a/virt/autopkgtest-virt-qemu b/virt/autopkgtest-virt-qemu
1133index 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()
1151diff --git a/virt/autopkgtest-virt-ssh b/virt/autopkgtest-virt-ssh
1152index 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()

Subscribers

People subscribed via source and target branches