~auto-package-testing-dev/auto-package-testing/trunk

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
#!/bin/sh
# Description: 
#   This script prepares a test environment in a VM from a cloud image
#   that is used as testbed to run autopkgtest
#
# Copyright (C) 2012-2013, Canonical Ltd (http://www.canonical.com/)
#
# Author: Jean-Baptiste Lallement <jean-baptiste.lallement@canonical.com>
#
# This software is free software: you can redistribute it 
# and/or modify it under the terms of the GNU General Public License 
# as published by the Free Software Foundation, either version 3 of 
# the License, or (at your option) any later version.
# 
# This software is distributed in the hope that it will 
# be useful, but WITHOUT ANY WARRANTY; without even the implied warranty 
# of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
# GNU General Public License for more details.
# 
# You should have received a copy of the GNU General Public License
# along with this software.  If not, see <http://www.gnu.org/licenses/>.
#
# TODO: 
#   * Lock file to prevent job building the same target env
#   * Normalize exit statuses
#   * Create images in /dev/shm then move them to final destination
#   * Add support for apt_proxy

# Find out our current directory
BINDIR=$(dirname $(readlink -f $0))
EATMYDATA="$(which eatmydata)" || true
KVMCMD=kvm
APTURI=http://archive.ubuntu.com/ubuntu
USERDATAFILE=""
METADATAFILE=""

# New qemu in raring replaces kvm and requires explicit activation of kvm
# virtualization support
if expr $(lsb_release -rs) '>=' 13.04; then
    KVMCMD="qemu-system-x86_64 -enable-kvm"
fi


. $BINDIR/functions
. $BINDIR/../etc/config

# Overrides global configuration
[ -f ~/.adtrc ] && . ~/.adtrc

if [ ! -f "$VMCFG" ]; then
    log_info_msg "Using default VM configuration file"
    VMCFG=$BINDIR/../etc/vm.cfg
fi

# ~/.adtrc might override BASEDIR, so only set DISKDIR here
[ -n "$DISKDIR" ] || DISKDIR=$BASEDIR/disks

on_exit() {
    log_info_msg "Cleaning up"
    # Cleanup

    if [ -f "$DISKPATH.pid" ]; then
        kill -9 $(cat $DISKPATH.pid) || true
        rm -f $DISKPATH.pid
    fi

    rm -f $LOCKDIR/ssh.$SSHPORT.lock
    rm -f $LOCKDIR/vnc.$VNCPORT.lock
    [ -d "$TDIR" ] && rm -Rf $TDIR

    exit $RET
}

trap on_exit EXIT INT

# Main exit code
# on_exit callback exits with this value
RET=0
BUILDID="current"

usage() {
    cat <<EOF
Usage: $(basename $0) [OPTIONS] <arch>
Build a base testbed from the latest dev release image

  arch      Target architecture. i386 or amd64

Options:
  -h, --help      This help.
  -b, --build BUILDID
                  Use this specific build id instead of default ($BUILDID) 
  -k,--sshkey PATH
                  path to private key used to ssh to the VM. Public key is
                  copied to the VM and must be located in the same directory
                  than the private key and a .pub extension.
  -r,--release CODENAME
                  release (default:$RELEASE)
  -S,--disksize SIZE
                  disksize of the VM in GB
  -d,--debug      enable debug mode
  -u,--user-data FILE
                  Use custom user-data.
EOF
    exit 1
}

TEMP=$(getopt -o hb:k:r:S:du: --long help,build:,sshkey:,release:,disksize:,debug,user-data: -- "$@")
eval set -- "$TEMP"

while true ; do
    case "$1" in
        -h|--help) 
            usage;;
        -b|--build)
            BUILDID=$2
            shift 2;;
        -k|--sshkey)
            KEYPATH=$2
            shift 2;;
        -r|--release)
            RELEASE=$2
            shift 2;;
        -S|--size)
            DISKSIZE=$2
            shift 2;;
        -d|--debug)
            set -x
            shift;;
        -u|--user-data)
            USERDATAFILE=$2
            shift 2;;
        -m|--meta-data)
            METADATAFILE="$2"
            shift 2;;
        --) shift ; break ;;
        *) usage;;
    esac
done

exec 2>&1

if [ ! -z "$1" ]; then
    ARCH=$1
else
    usage
fi

TDIR=$(mktemp -d ${TMPDIR:-/tmp}/adt-${ARCH}.XXXXXX)
CLOUDIMGURL=http://cloud-images.ubuntu.com/$RELEASE/$BUILDID

IMGDIR=${BASEDIR}/images/
IMGNAME=$RELEASE-server-cloudimg-${ARCH}-disk1.img
IMGPATH=$IMGDIR/$IMGNAME

DISKNAME=pristine-${RELEASE}-${ARCH}-$(date +%Y%m%d_%H%M%S).img
DISKPATH=$DISKDIR/$DISKNAME

USERDATA=${DISKDIR}/user-data
METADATA=${DISKDIR}/meta-data
EXTRAENV=""

# Download Server ISO that we'll use to build the testbed
DODOWNLOAD=""
mkdir -p $IMGDIR $DISKDIR

DEFAULT_KEY=$DISKDIR/adtkey
if [ ! -f $DEFAULT_KEY ]; then
    log_info_msg "Generating default ssh key"
    ssh-keygen -N '' -f $DEFAULT_KEY
fi

if [ ! -f "$IMGPATH" ]; then
    DODOWNLOAD="Y"
else
    # Verify Checksum
    log_info_msg "Verifying checksum"
    LOCALCHKSUM=$(md5sum $IMGPATH | cut -d' ' -f1)
    wget -O $TDIR/MD5SUMS $CLOUDIMGURL/MD5SUMS
    REMOTECHKSUM=$( grep $IMGNAME $TDIR/MD5SUMS | cut -d' ' -f1)

    if [ "$LOCALCHKSUM" = "$REMOTECHKSUM" ]; then
       log_info_msg "Checksums match. Not downloading again"
    else
       log_info_msg "Checksums differs. Marking for download"
       DODOWNLOAD="Y"
    fi
fi

if [ ! -z "$DODOWNLOAD" ]; then
    if wget -O $TDIR/$IMGNAME $CLOUDIMGURL/$IMGNAME; then
        # TODO Make sure there is no running snapshot
        mv $TDIR/$IMGNAME $IMGPATH
    else
        log_failure_msg "An error occurred during download. Aborting!"
        RET=1
        exit $RET
    fi
fi
cp $IMGPATH $DISKPATH

qemu-img resize $DISKPATH +${DISKSIZE}

cd $DISKDIR

if [ ! -z "$KEYPATH" ]; then
    PUBKEY="$( cat $KEYPATH.pub )"
else
    [ ! -f $DEFAULT_KEY ] && ssh-keygen -N '' -f $DEFAULT_KEY
    KEYPATH=$DEFAULT_KEY
    PUBKEY="$( cat ${KEYPATH}.pub )"
fi

if [ -f "$METADATAFILE" ]; then
    cp "$METADATAFILE" "$METADATA"
else
    LOCALHOSTNAME=autopkgtest
    { echo instance-id: nocloud; echo local-hostname: $LOCALHOSTNAME; } > $METADATA
fi

TZ="Etc/UTC"
[ -e /etc/timezone ] && TZ=$(cat /etc/timezone)

# make VM use the same proxy as the host
if [ -n "$http_proxy" ]; then
    EXTRAENV="http_proxy=\"$http_proxy\""
fi
if [ -n "$https_proxy" ]; then
    EXTRAENV="$EXTRAENV\nhttps_proxy=\"$https_proxy\""
fi
if [ -n "$no_proxy" ]; then
    EXTRAENV="$EXTRAENV\nno_proxy=\"$no_proxy\""
fi

if [ -f "$USERDATAFILE" ]; then
    cat $USERDATAFILE > $USERDATA
else
    cat > $USERDATA << EOF
#cloud-config
locale: en_US.UTF-8
timezone: $TZ

password: ubuntu
chpasswd: { expire: False }
ssh_pwauth: True
manage_etc_hosts: True

ssh_authorized_keys:
 - $PUBKEY

apt_proxy: ${APTPROXY:-}
apt_mirror: $APTURI
apt_sources: 
 - source: deb $APTURI $RELEASE restricted multiverse
 - source: deb-src $APTURI $RELEASE restricted multiverse
 - source: deb $APTURI $RELEASE-updates restricted multiverse
 - source: deb-src $APTURI $RELEASE-updates restricted multiverse

apt_update: true
apt_upgrade: true
byobu_by_default: system

packages:
 - eatmydata
 - linux-image-generic
 - autopkgtest
 - dpkg-dev
 - pbuilder
 - bzr
 - distro-info
 - debian-keyring

runcmd:
 - [ sh, -c, 'echo "${EXTRAENV}" >> /etc/environment' ]
EOF
fi

SEED=${DISKPATH}.seed
genisoimage  -output $SEED -volid cidata -joliet -rock user-data meta-data

# When we are called in Jenkins, add some jitter to avoid lots of VMs starting
# up at the same time
if [ -n "$BUILD_ID" ]; then
    SLEEP=$( shuf -i 1-30 -n1) 
    log_info_msg "Waiting $SLEEP seconds before starting VM"
    sleep $SLEEP
fi

# Find free SSH and VNC ports
get_free_port "ssh"
SSHPORT=$?
get_free_port "vnc"
VNCPORT=$?
VNCKVM=$((VNCPORT - 5900))

# Launch in background, -daemonize option causes very slow network IO
log_info_msg "Starting $DISKNAME on port $SSHPORT"
$EATMYDATA $KVMCMD -m $RAMSIZE -smp $VCPU -localtime \
    -no-reboot -net user -redir tcp:$SSHPORT::22 \
    -drive file=$DISKNAME,if=virtio \
    -drive file=$SEED,if=virtio -vnc localhost:$VNCKVM \
    -pidfile $DISKPATH.pid -readconfig $VMCFG &

if [ $? -gt 0 ]; then
    log_failure_msg "KVM failed to start. Exiting!"
    exit 1
fi

wait_vm_startup $SSHPORT

LOOP=0
RET=1
log_info_msg "Waiting for installation to finish"
until [ $RET -eq 0 ]; do
    scp $SSHOPTS -P $SSHPORT -i $KEYPATH $BINDIR/testbed/wait_bootfinished ubuntu@localhost:~/
    RET=$?
    if [ $LOOP -gt 12 ]; then
        log_failure_msg "Failed to reach the VM after 120s. Exiting."
        RET=1
        exit 1
    fi
    sleep 10
    LOOP=$(( LOOP + 1 ))
done

RET=0
ssh $SSHOPTS -i $KEYPATH -p $SSHPORT -l ubuntu localhost /home/ubuntu/wait_bootfinished
RET=$?

if [ $RET -gt 0 ]; then
    log_failure_msg "Remote command exited with status $RET. Exiting!"
    exit 1
fi

# stop qemu
if [ -f "$DISKPATH.pid" ]; then
    log_info_msg "Stopping QEMU"
    pid=$(cat $DISKPATH.pid)
    kill -9 $pid || true
    LOOP=0
    while [ -d /proc/$pid ]; do
        sleep 0.5
        LOOP=$(( LOOP + 1 ))
         if [ $LOOP -gt 20 ]; then
            log_failure_msg "Failed to kill VM after 10 s. Exiting."
            RET=1
            exit 1
        fi
    done
    rm -f $DISKPATH.pid
fi

# try to run the new VM to ensure that it actually boots and works
log_info_msg "Verifying that the VM works"

$KVMCMD -m $RAMSIZE -smp $VCPU -localtime \
    -no-reboot -net user -redir tcp:$SSHPORT::22 \
    -drive file=$DISKNAME,if=virtio,index=0 \
    -drive file=$SEED,if=virtio,index=1,readonly \
    -vnc localhost:$VNCKVM \
    -pidfile $DISKPATH.pid -readconfig $VMCFG &

wait_vm_startup $SSHPORT

timeout=10
while true; do
    OUT=$(ssh $SSHOPTS -i $KEYPATH -p $SSHPORT -l ubuntu localhost which adt-run)
    if [ "$OUT" = "/usr/bin/adt-run" ]; then
        log_info_msg "Succeeded to ssh into VM, and adt-run is installed"
        break
    fi

    sleep 1
    timeout=$(( timeout - 1 ))
    if [ "$timeout" -lt 0 ]; then
        log_failure_msg "Failed to ssh into VM, or adt-run not installed. Exiting!"
        RET=1
        exit 1
    fi
done

# Create link to new test file
LINKNAME=pristine-${RELEASE}-${ARCH}.img
rm -f ${DISKDIR}/${LINKNAME}
ln -s ${DISKPATH} ${DISKDIR}/${LINKNAME}

# Cleanup old images
for DISKFILE in ${DISKDIR}/pristine-${RELEASE}-${ARCH}*.img; do
    if [ -f $DISKFILE -a ! -L $DISKFILE -a "$(basename $DISKFILE)" != "$DISKNAME" ]; then
        if ! fuser -s $DISKFILE; then
            log_info_msg "Removing $DISKFILE"
            [ -f "${DISKFILE}.pid" ] && rm ${DISKFILE}.pid
            [ -f "${DISKFILE}.seed" ] && rm ${DISKFILE}.seed
            rm $DISKFILE
        fi
    fi
done

log_info_msg "Installation finished"