From 8957c39278a47c800dbd74d7dd9b766b1359f39a Mon Sep 17 00:00:00 2001 From: Ian Jackson Date: Fri, 23 Mar 2007 16:59:04 +0000 Subject: [PATCH] * adt-run: new --instantiate option to allow package installation to be forced for testing. (This is not an ideal approach - it's not very flexible - but works well enough for the current requirements.) * adt-testreport-onepackage: new ability to test binary as well as source packages, using new --instantiate option. * new `timeout=' option on `execute' virt server command. --- debian/changelog | 11 +++ doc/README.virtualisation-server | 13 +++ runner/adt-run | 18 +++- runner/adt-testreport-onepackage | 142 +++++++++++++++++++++---------- runner/onepackage-config | 2 +- virt-subproc/VirtSubproc.py | 49 ++++++++--- 6 files changed, 177 insertions(+), 58 deletions(-) diff --git a/debian/changelog b/debian/changelog index 87176ba..6bcc368 100644 --- a/debian/changelog +++ b/debian/changelog @@ -1,3 +1,14 @@ +autopkgtest (0.7.2~~iwj) unstable; urgency=low + + * adt-run: new --instantiate option to allow package installation to be + forced for testing. (This is not an ideal approach - it's not very + flexible - but works well enough for the current requirements.) + * adt-testreport-onepackage: new ability to test binary as well as + source packages, using new --instantiate option. + * new `timeout=' option on `execute' virt server command. + + -- Ian Jackson Fri, 23 Mar 2007 16:58:42 +0000 + autopkgtest (0.7.2) feisty; urgency=low * adt-testreport-onepackage: new management script, with some diff --git a/doc/README.virtualisation-server b/doc/README.virtualisation-server index ab7243c..e01137e 100644 --- a/doc/README.virtualisation-server +++ b/doc/README.virtualisation-server @@ -119,6 +119,19 @@ Protocol If this feature is available, execute-debug will be advertised. Only one such plumbing is available. + timeout= + + Ensures that the whole execute command does not take + more than . If it does, the response is + timeout + instead of `ok '. + + An effort will be made to kill the processes on the + testbed but this is not guaranteed to be possible or + successful. After an `execute' has timed out, the + testbed should probably be reverted with `revert' if + that facility is available. + * Commands copydown copyup diff --git a/runner/adt-run b/runner/adt-run index 8873757..403a9d3 100755 --- a/runner/adt-run +++ b/runner/adt-run @@ -447,6 +447,15 @@ def parse_args(): help='use binary package DEB according' ' to most recent --binaries-* settings') + def cb_actnoarg(op,optstr,value,parser, largsentry): + parser.largs.append(largsentry) + pa('','--instantiate', action='callback', callback=cb_actnoarg, + callback_args=((None, ('instantiate',)),), + help='instantiate testbed now (during testing phase)' + ' and install packages' + ' selected for automatic installation, even' + ' if this might apparently not be required otherwise') + pa_action('override-control', 'CONTROL', ('control',), is_act=0, help='run tests from control file CONTROL instead,' ' (applies to next test suite only)') @@ -608,7 +617,8 @@ def parse_args(): if kind == 'dsc': fwhatx = '/' + os.path.basename(pathstr) else: fwhatx = '-'+kind - af = constructor(what+fwhatx, pathstr, arghandling['tb']) + if pathstr is None: af = None + else: af = constructor(what+fwhatx, pathstr, arghandling['tb']) opts.actions.append(Action(kind, af, arghandling, what)) def setup_trace(): @@ -1690,7 +1700,7 @@ def process_actions(): binaries = Binaries() for act in opts.actions: - if not act.af.spec_tbp: + if act.af is not None and not act.af.spec_tbp: testbed.register_ephemeral(act.af) binaries.reset() @@ -1715,6 +1725,8 @@ def process_actions(): act.binaries = [] if act.kind.endswith('tree') or act.kind == 'dsc': control_override = None + if act.kind == 'instantiate': + pass debug_a1('builds done.') @@ -1750,6 +1762,8 @@ def process_actions(): debug_a3('run_tests ...') run_tests(stanzas, act.af) control_override = None + if act.kind == 'instantiate': + testbed.prepare([]) debug_a1('tests done.') def main(): diff --git a/runner/adt-testreport-onepackage b/runner/adt-testreport-onepackage index 2fc283d..d912fd7 100755 --- a/runner/adt-testreport-onepackage +++ b/runner/adt-testreport-onepackage @@ -15,6 +15,8 @@ from="$salutation Jackson " rsync=rsync disable=true interactive=true +target=source +arch=`dpkg --print-architecture` for config in "$@"; do case "$config" in @@ -28,6 +30,23 @@ if $disable; then exit 1 fi +: ${destdirtail:=$distro-$target} +: ${destdirfin:="$destdircommon$destdirtail"} + +case $target in +source) + sources=Sources + descx='' + ;; +binary-*) + sources=Packages + descx="${target#binary-}" + ;; +*) + echo >&2 'target must be source or binary-*' + exit 1 +esac + exec 3>&1 printf >&3 "starting " @@ -52,11 +71,13 @@ gurl () { curl -fsS "$1" >"$2" } -gurl "$mirror/dists/$distro/$suite/source/Sources.gz" "$tmp"/_sources.gz -zcat "$tmp"/_sources.gz >"$tmp"/_sources-in +gurl "$mirror/dists/$distro/$suite/$target/$sources.gz" "$tmp"/_$sources.gz +zcat "$tmp"/_$sources.gz >"$tmp"/_$sources-in + +lastinfo="$var"/lastinfo-$target now=`date +%s` ->>"$var"/last-info +>>"$lastinfo" progress selecting @@ -80,29 +101,45 @@ if [ "x$pkg" = x ]; then f2(); f1(); $best_score= -1; + sub scorepackage () { + return if $skip; + return if $score < $best_score + or ($score==$best_score and \ + $package gt $best_package); + $best_score= $score; + $best_package= $package; + } + sub endpackage () { + return unless (defined $package + or defined $version + or defined $skip); + die unless defined $package; + die unless defined $version; + scorepackage(); + undef $package; + undef $version; + undef $skip; + } while () { if (m/^Package: ($pre)$/) { - die if length $package or length $version; + die if defined $package; $package= $1; } elsif (m/^Version: ($vre)$/) { - die unless length $package; - die if length $version; + die if defined $version; $version= $1; $score= '$now' - $lasttime{$package}; $score= 1e7 if $score>1e7; $score *= 5 if $lastver{$package} ne $version; $score *= 10 unless $extras{$package} =~ m/ nt /; - next if $score < $best_score - or ($score==$best_score and \ - $package gt $best_package); - $best_score= $score; - $best_package= $package; + } elsif (m/^Architecture:.*/ && + !m/\s'$arch'\s/) { + $skip= 1; } elsif (m/^$/) { - die if length $package and !length $version; - $package= $version= ""; + endpackage(); } } f2(); + endpackage(); die unless length $best_package; open L, ">&4" or die $!; printf L "selected %s (age %s, score %d)\n", @@ -112,13 +149,13 @@ if [ "x$pkg" = x ]; then : "", $best_score; print "$best_package\n" or die $!; - ' "$var"/last-info "$tmp"/_sources-in`" + ' "$lastinfo" "$tmp"/_$sources-in`" else printf >&4 "package forced: %s\n" "$pkg" fi sed -n "/^Package: $pkg\$/,/^\$/p" \ - <"$tmp"/_sources-in >"$tmp"/_this-stanza + <"$tmp"/_$sources-in >"$tmp"/_this-stanza echo cat "$tmp"/_this-stanza @@ -130,29 +167,42 @@ getfield () { `"' } -getfield Directory -getfield Version - -leafnames="` - sed -n '/^Files:/,/^([^ ].*)?$/{ /^ /{ - s/^ [0-9a-z][0-9a-z]* *[0-9][0-9]* //; p - }}' \ - <"$tmp"/_this-stanza -`" - printf >&3 "selected \"%s\" " $pkg tp="$tmp/$pkg" mkdir "$tp" "$tp/src" "$tp/tmp" "$tp/out" -for leafname in $leafnames; do - df="$tp/src/$leafname" - case "$leafname" in - */*|.*) echo >&2 "bad leafname: $leafname"; exit 1;; - *.dsc) dsc="$df";; - esac - gurl "$mirror/$pDirectory/$leafname" "$df" -done +getfield Version + +if test $target = source; then + getfield Directory + leafnames="` + sed -n '/^Files:/,/^([^ ].*)?$/{ /^ /{ + s/^ [0-9a-z][0-9a-z]* *[0-9][0-9]* //; p + }}' \ + <"$tmp"/_this-stanza + `" + for leafname in $leafnames; do + df="$tp/src/$leafname" + case "$leafname" in + */*|.*) echo >&2 "bad leafname: $leafname"; exit 1;; + *.dsc) fot="$df";; + esac + gurl "$mirror/$pDirectory/$leafname" "$df" + done + testmode=--source + testmode2='' + desc="$pkg" + : ${upload_if_ok:=true} +else + getfield Filename + fot="$tp/src/$pkg.deb" + gurl "$mirror/$pFilename" "$fot" + testmode='--binaries=install --binary' + testmode2=--instantiate + desc="$pkg $descx" + : ${upload_if_ok:=false} +fi if [ "x$maintainer_email_override" = x ]; then getfield Maintainer @@ -180,7 +230,7 @@ xrc adt-run --tmp-dir "$tp"/tmp \ --log-file "$tp"/log \ --summary "$tmp"/_summary \ $adtrun_extra_opts \ - --source "$dsc" \ + $testmode "$fot" $testmode2 \ --- \ adt-virt-xenlvm \ $adtvirt_extra_opts \ @@ -195,8 +245,10 @@ upload=true extras='' case "$rc" in -0) summary='all OK'; email='' ;; -2) summary='OK (some skipped)'; email='' ;; +0) summary='all OK'; email='' + upload=$upload_if_ok ;; +2) summary='OK (some skipped)'; email='' + upload=$upload_if_ok ;; 8) summary='package declares no tests'; email='' upload=false; extras='nt' ;; 4|6) summary='test(s) failed!'; email="$maintainer_email" ;; @@ -220,7 +272,7 @@ if $upload; then progress "uploading" printf >&3 "uploading" - $rsync -rltH --safe-links --delete "$tp" "$destrsynchead/$destdirtail/" + $rsync -rltH --safe-links --delete "$tp" "$destrsynchead/$destdirfin/" printf >&3 " " fi @@ -231,9 +283,9 @@ if [ "x$email" != x ]; then cat >"$tmp"/_email <>"$tmp"/_email @@ -272,7 +324,7 @@ The test log, which is intended to be sufficient to diagnose most failures, can be found below. However, in case this is not sufficient, another copy can be found along with output files, saved temporary files, and so on, at: - $desthttphead/$destdirtail/ + $desthttphead/$destdirfin/ If you have any questions about this service please contact me at: $from @@ -284,14 +336,14 @@ $salutation END fi -printf >>"$var"/log "package=%s rc=%s emailed='%s'\n" \ - "$pkg" $rc "$email_addr" +printf >>"$var"/log "%s=%s rc=%s emailed='%s'\n" \ + "$target" "$pkg" $rc "$email_addr" if [ "x$ourx" = x0 ]; then - sed -e "/^$pkg /d" <"$var"/last-info >"$var"/last-info.new + sed -e "/^$pkg /d" <"$lastinfo" >"$lastinfo".new printf "%s %s %s %s\n" "$pkg" "$pVersion" "$now" "$extras" \ - >>"$var"/last-info.new - mv "$var"/last-info.new "$var"/last-info + >>"$lastinfo".new + mv "$lastinfo".new "$lastinfo" progress "tested." else progress "fault ($ourx)." diff --git a/runner/onepackage-config b/runner/onepackage-config index e864343..883a703 100644 --- a/runner/onepackage-config +++ b/runner/onepackage-config @@ -2,4 +2,4 @@ cd /root/adt-play disable=false desthttphead=http://www.chiark.greenend.org.uk/~ijackson/ destrsynchead=ijackson@login.chiark.greenend.org.uk:public-html/ -destdirtail=autopkgtest-output/$distro +destdircommon=autopkgtest-output/ diff --git a/virt-subproc/VirtSubproc.py b/virt-subproc/VirtSubproc.py index 32af6d5..8a4216c 100644 --- a/virt-subproc/VirtSubproc.py +++ b/virt-subproc/VirtSubproc.py @@ -35,10 +35,19 @@ debuglevel = None progname = "" devnull_read = file('/dev/null','r') caller = __main__ +copy_timeout = 300 class Quit: def __init__(q,ec,m): q.ec = ec; q.m = m +class Timeout: pass +def alarm_handler(*a): raise Timeout() +def timeout_start(to): signal.alarm(to) +def timeout_stop(): signal.alarm(0) + +class FailedCmd: + def __init__(fc,e): fc.e = e + def debug(m): if not debuglevel: return print >> sys.stderr, progname+": debug:", m @@ -70,17 +79,19 @@ def cmd_close(c, ce): def preexecfn(): caller.hook_forked_inchild() -def execute_raw(what, instr, *popenargs, **popenargsk): +def execute_raw(what, instr, timeout, *popenargs, **popenargsk): debug(" ++ %s" % string.join(popenargs[0])) sp = subprocess.Popen(preexec_fn=preexecfn, *popenargs, **popenargsk) if instr is None: popenargsk['stdin'] = devnull_read + timeout_start(timeout) (out, err) = sp.communicate(instr) + timeout_stop() if err: bomb("%s unexpectedly produced stderr output `%s'" % (what, err)) status = sp.wait() return (status, out) -def execute(cmd_string, cmd_list=[], downp=False, outp=False): +def execute(cmd_string, cmd_list=[], downp=False, outp=False, timeout=0): cmdl = cmd_string.split() if downp: perhaps_down = down @@ -92,7 +103,8 @@ def execute(cmd_string, cmd_list=[], downp=False, outp=False): cmd = cmdl + cmd_list if len(perhaps_down): cmd = perhaps_down + [' '.join(cmd)] - (status, out) = execute_raw(cmdl[0], None, cmd, stdout=stdout) + (status, out) = execute_raw(cmdl[0], None, timeout, + cmd, stdout=stdout) if status: bomb("%s%s failed (exit status %d)" % ((downp and "(down) " or ""), cmdl[0], status)) @@ -148,6 +160,7 @@ def cmd_execute(c, ce): cmdnumargs(c, ce, 5, None) debug_re = regexp.compile('debug=(\d+)\-(\d+)$') debug_g = None + timeout = 0 envs = [] for kw in ce[6:]: if kw.startswith('debug='): @@ -155,6 +168,9 @@ def cmd_execute(c, ce): m = debug_re.match(kw) if not m: bomb("invalid execute debug arg `%s'" % kw) debug_g = m.groups() + elif kw.startswith('timeout='): + try: timeout = int(kw[8:],0) + except ValueError: bomb("invalid timeout arg `%s'" %kw) elif kw.startswith('env='): es = kw[4:]; eq = es.find('=') if eq <= 0: bomb("invalid env arg `%s'" % kw) @@ -195,8 +211,13 @@ def cmd_execute(c, ce): " os._exit(127)\n") cmdl = down_python_script(gobody) - (status, out) = execute_raw('sub-python', None, cmdl, stdout=stdout, - stdin=devnull_read, stderr=subprocess.PIPE) + try: + (status, out) = execute_raw('sub-python', None, timeout, + cmdl, stdout=stdout, stdin=devnull_read, + stderr=subprocess.PIPE) + except Timeout: + raise FailedCmd(['timeout']) + if out: bomb("sub-python unexpected produced stdout" " visible to us `%s'" % out) return [`status`] @@ -268,11 +289,15 @@ def copyupdown(c, ce, upp): debug(" +> %s" % string.join(cmdls[1])) subprocs[1] = subprocess.Popen(cmdls[1], stdin=subprocs[0].stdout, stdout=deststdout, preexec_fn=preexecfn) + timeout_start(copy_timeout) for sdn in [1,0]: debug(" +"+"<>"[sdn]+"?"); status = subprocs[sdn].wait() - if status: bomb("%s %s failed, status %d" % - (wh, ['source','destination'][sdn], status)) + if status: + timeout_stop() + bomb("%s %s failed, status %d" % + (wh, ['source','destination'][sdn], status)) + timeout_stop() def cmd_copydown(c, ce): copyupdown(c, ce, False) def cmd_copyup(c, ce): copyupdown(c, ce, True) @@ -287,9 +312,12 @@ def command(): debug('executing '+string.join(ce)) try: f = globals()['cmd_'+c[0]] except KeyError: bomb("unknown command `%s'" % ce[0]) - r = f(c, ce) - if not r: r = [] - r.insert(0, 'ok') + try: + r = f(c, ce) + if not r: r = [] + r.insert(0, 'ok') + except FailedCmd, fc: + r = fc.e print string.join(r) def cleanup(): @@ -345,6 +373,7 @@ def mainloop(): sys.exit(16) def main(): + signal.signal(signal.SIGALRM, alarm_handler) debug("down = %s" % string.join(down)) ok() prepare() -- 2.30.2