File: prepare-restore.sh

package info (click to toggle)
snapd 2.71-3
  • links: PTS, VCS
  • area: main
  • in suites: forky, sid
  • size: 79,536 kB
  • sloc: ansic: 16,114; sh: 16,105; python: 9,941; makefile: 1,890; exp: 190; awk: 40; xml: 22
file content (941 lines) | stat: -rwxr-xr-x 33,355 bytes parent folder | download
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
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
895
896
897
898
899
900
901
902
903
904
905
906
907
908
909
910
911
912
913
914
915
916
917
918
919
920
921
922
923
924
925
926
927
928
929
930
931
932
933
934
935
936
937
938
939
940
941
#!/bin/bash
set -x
# NOTE: We must set -e so that any failures coming out of the various
# statements we execute stops the build. The code is not (yet) written to
# handle errors in general.
set -e

# shellcheck source=tests/lib/pkgdb.sh
. "$TESTSLIB/pkgdb.sh"

# shellcheck source=tests/lib/random.sh
. "$TESTSLIB/random.sh"

# shellcheck source=tests/lib/state.sh
. "$TESTSLIB/state.sh"


###
### Utility functions reused below.
###

create_test_user(){
   if ! id test >& /dev/null; then
        quiet groupadd --gid 12345 test
        case "$SPREAD_SYSTEM" in
            ubuntu-*)
                # manually setting the UID and GID to 12345 because we need to
                # know the numbers match for when we set up the user inside
                # the all-snap, which has its own user & group database.
                # Nothing special about 12345 beyond it being high enough it's
                # unlikely to ever clash with anything, and easy to remember.
                quiet adduser --uid 12345 --gid 12345 --disabled-password --gecos '' test
                ;;
            debian-*|fedora-*|opensuse-*|arch-*|amazon-*|centos-*)
                quiet useradd -m --uid 12345 --gid 12345 test
                ;;
            *)
                echo "ERROR: system $SPREAD_SYSTEM not yet supported!"
                exit 1
        esac

        # Allow the test user to access systemd journal.
        if getent group systemd-journal >/dev/null; then
            usermod -G systemd-journal -a test
            id test | MATCH systemd-journal
        fi
    fi

    owner=$( stat -c "%U:%G" /home/test )
    if [ "$owner" != "test:test" ]; then
        echo "expected /home/test to be test:test but it's $owner"
        exit 1
    fi
    unset owner

    if [ -d /etc/sudoers.d ]; then
        # this also works around systems where /etc/sudo is empty and /etc could
        # be ephemeral, eg. openSUSE Tumbleweed which keeps /usr/etc/sudoers
        echo 'test ALL=(ALL) NOPASSWD:ALL' >> /etc/sudoers.d/99-test-user
    else
        # Add a new line first to prevent an error which happens when
        # the file has not new line, and we see this:
        # syntax error, unexpected WORD, expecting END or ':' or '\n'
        echo >> /etc/sudoers
        echo 'test ALL=(ALL) NOPASSWD:ALL' >> /etc/sudoers
    fi

    chown test:test -R "$SPREAD_PATH"
    chown test:test "$SPREAD_PATH/../"
}

build_deb(){
    newver="$(dpkg-parsechangelog --show-field Version)"

    case "$SPREAD_SYSTEM" in
        ubuntu-fips-*)
            newver="${newver}+fips"
            FIPS_BUILD_OPTION=fips
            ;;
    esac
    # Use fake version to ensure we are always bigger than anything else
    dch --newversion "1337.$newver" "testing build"

    if os.query is-debian sid; then
        # ensure we really build without vendored packages
        mv ./vendor /tmp
    fi

    unshare -n -- \
            su -l -c "cd $PWD && DEB_BUILD_OPTIONS='nocheck testkeys ${FIPS_BUILD_OPTION}' dpkg-buildpackage -tc -b -Zgzip -uc -us" test
    # put our debs to a safe place
    cp ../*.deb "$GOHOME"

    if os.query is-debian sid; then
        # restore vendor dir, it's needed by e.g. fakestore
        mv /tmp/vendor ./
    fi
}

build_rpm() {
    distro=$(echo "$SPREAD_SYSTEM" | awk '{split($0,a,"-");print a[1]}')
    release=$(echo "$SPREAD_SYSTEM" | awk '{split($0,a,"-");print a[2]}')
    if os.query is-amazon-linux 2; then
        distro=amzn
        release=2
    fi
    if os.query is-amazon-linux 2023; then
        distro=amzn
        release=2023
    fi
    base_version="$(head -1 debian/changelog | awk -F '[()]' '{print $2}')"
    version="1337.$base_version"
    packaging_path=packaging/$distro-$release
    rpm_dir=$(rpm --eval "%_topdir")
    pack_args=
    case "$SPREAD_SYSTEM" in
        opensuse-*)
            # use bundled snapd*.vendor.tar.xz archive
            pack_args=-s
            ;;
        fedora-*|amazon-*|centos-*)
            ;;
        *)
            echo "ERROR: RPM build for system $SPREAD_SYSTEM is not yet supported"
            exit 1
    esac

    sed -i -e "s/^Version:.*$/Version: $version/g" "$packaging_path/snapd.spec"

    # Create a source tarball for the current snapd sources
    mkdir -p "$rpm_dir/SOURCES"
    cp "$packaging_path"/* "$rpm_dir/SOURCES/"
    # shellcheck disable=SC2086
    ./packaging/pack-source -v "$version" -o "$rpm_dir/SOURCES" $pack_args

    # Cleanup all artifacts from previous builds
    rm -rf "$rpm_dir"/BUILD/*
    # Install build dependencies
    distro_install_package rpmdevtools
    # XXX we should pass --with testkeys for completeness, but older versions of
    # rpmspec do not support it, and in any case testkeys does not result in any
    # additional build packages
    # shellcheck disable=SC2046
    distro_install_package $(rpmspec -q --buildrequires "$packaging_path/snapd.spec")

    # Build our source package
    unshare -n -- \
            rpmbuild --with testkeys -bs "$rpm_dir/SOURCES/snapd.spec"

    # And now build our binary package
    unshare -n -- \
            rpmbuild \
            --with testkeys \
            --nocheck \
            -ba \
            "$rpm_dir/SOURCES/snapd.spec"

    find "$rpm_dir"/RPMS -name '*.rpm' -exec cp -v {} "${GOPATH%%:*}" \;
}

build_arch_pkg() {
    base_version="$(head -1 debian/changelog | awk -F '[()]' '{print $2}')"
    version="1337.$base_version"
    packaging_path=packaging/arch

    rm -rf /tmp/pkg
    mkdir -p /tmp/pkg/
    cp -av "$packaging_path"/* /tmp/pkg

    # shellcheck disable=SC2086
    ./packaging/pack-source -v "$version" -o "/tmp/pkg/" -s

    sed -i \
        -e "s/pkgver=.*/pkgver=$version/" \
        /tmp/pkg/PKGBUILD

    chown -R test:test /tmp/pkg
    unshare -n -- \
            su -l -c "cd /tmp/pkg && WITH_TEST_KEYS=1 makepkg -f --nocheck" test

    # /etc/makepkg.conf defines PKGEXT which drives the compression alg and sets
    # the package file name extension, keep it simple and try a glob instead
    cp /tmp/pkg/snapd*.pkg.tar.* "${GOPATH%%:*}"
}

install_snapd_rpm_dependencies(){
    SRC_PATH=$1
    deps=()
    IFS=$'\n'
    for dep in $(rpm -qpR "$SRC_PATH"); do
        if [[ "$dep" = rpmlib* ]]; then
            continue
        fi
        deps+=("$dep")
    done
    distro_install_package "${deps[@]}"
}

install_dependencies_gce_bucket(){
    case "$SPREAD_SYSTEM" in
        ubuntu-*|debian-*)
            cp "$PROJECT_PATH"/../*.deb "$GOHOME"
            ;;
        fedora-*|opensuse-*|amazon-*|centos-*)
            install_snapd_rpm_dependencies "$PROJECT_PATH"/../snapd-1337.*.src.rpm
            # sources are not needed to run the tests
            rm "$PROJECT_PATH"/../snapd-1337.*.src.rpm
            find "$PROJECT_PATH"/.. -name '*.rpm' -exec cp -v {} "${GOPATH%%:*}" \;
            ;;
        arch-*)
            cp "$PROJECT_PATH"/../snapd*.pkg.tar.* "${GOPATH%%:*}"
            ;;
    esac
}

###
### Prepare / restore functions for {project,suite}
###

prepare_project() {
    if os.query is-ubuntu && os.query is-classic; then
        apt-get remove --purge -y lxd lxcfs || true
        apt-get autoremove --purge -y
        "$TESTSTOOLS"/lxd-state undo-mount-changes
    fi

    # Check if running inside a container.
    # The testsuite will not work in such an environment
    if systemd-detect-virt -c; then
        echo "Tests cannot run inside a container"
        exit 1
    fi

    # no need to modify anything further for autopkgtest
    # we want to run as pristine as possible
    if [ "$SPREAD_BACKEND" = autopkgtest ]; then
        create_test_user
        systemctl enable --now snapd.socket
        exit 0
    fi

    # Set REUSE_PROJECT to reuse the previous prepare when also reusing the server.
    [ "$REUSE_PROJECT" != 1 ] || exit 0
    echo "Running with SNAP_REEXEC: $SNAP_REEXEC"

    # check that we are not updating
    if [ "$("$TESTSTOOLS"/boot-state bootenv show snap_mode)" = "try" ]; then
        echo "Ongoing reboot upgrade process, please try again when finished"
        exit 1
    fi

    # Prepare the state directories for execution
    prepare_state

    # declare the "quiet" wrapper

    if [ "$SPREAD_BACKEND" = "external" ]; then
        chown test:test -R "$PROJECT_PATH"
        exit 0
    fi

    if [ "$SPREAD_BACKEND" = "testflinger" ]; then
        if os.query is-core-ge 24; then
            useradd --uid 12345 --create-home --extrausers test
        else
            adduser --uid 12345 --extrausers --quiet --disabled-password --gecos '' test
        fi
        echo test:ubuntu | sudo chpasswd
        echo 'test ALL=(ALL) NOPASSWD:ALL' | sudo tee /etc/sudoers.d/create-user-test
        chown test:test -R "$PROJECT_PATH"
        exit 0
    fi

    if [ "$SPREAD_BACKEND" = qemu ]; then
        if [ -d /etc/apt/apt.conf.d ]; then
            # qemu images may be built with pre-baked proxy settings that can be wrong
            rm -f /etc/apt/apt.conf.d/90cloud-init-aptproxy
            rm -f /etc/apt/apt.conf.d/99proxy
            if [ -n "${HTTP_PROXY:-}" ]; then
                printf 'Acquire::http::Proxy "%s";\n' "$HTTP_PROXY" >> /etc/apt/apt.conf.d/99proxy
            fi
            if [ -n "${HTTPS_PROXY:-}" ]; then
                printf 'Acquire::https::Proxy "%s";\n' "$HTTPS_PROXY" >> /etc/apt/apt.conf.d/99proxy
            fi
        fi
        if [ -f /etc/dnf/dnf.conf ]; then
            if [ -n "${HTTP_PROXY:-}" ]; then
                echo "proxy=$HTTP_PROXY" >> /etc/dnf/dnf.conf
            fi
        fi
        # TODO: zypper proxy, yum proxy
    fi

    create_test_user

    distro_update_package_db
    # XXX this should be part of the image update in spread-images
    # remove any packages that are marked for auto removal before running any tests
    distro_auto_remove_packages

    if os.query is-amazon-linux 2023; then
        # perform system upgrade to the latest release
        if [[ "$SPREAD_REBOOT" == 0 ]]; then
            if distro_upgrade | MATCH "reboot"; then
                echo "system upgraded, reboot required"
                REBOOT
            fi
        fi
    fi

    if os.query is-arch-linux; then
        # perform system upgrade on Arch so that we run with most recent kernel
        # and userspace
        if [[ "$SPREAD_REBOOT" == 0 ]]; then
            if distro_upgrade | MATCH "reboot"; then
                echo "system upgraded, reboot required"
                REBOOT
            fi
            # arch uses a single kernel package which could have gotten updated
            # just now, reboot in case we're still on the old kernel
            if [ ! -d "/lib/modules/$(uname -r)" ]; then
                echo "rebooting to new kernel"
                REBOOT
            fi
        fi
        # double check we are running the installed kernel
        # NOTE: LOCALVERSION is set by scripts/setlocalversion and loos like
        # 4.17.11-arch1, since this may not match pacman -Qi output, we'll list
        # the files within the package instead
        # pacman -Ql linux output:
        # ...
        # linux /usr/lib/modules/4.17.11-arch1/modules.alias
        if [[ "$(pacman -Ql linux | cut -f2 -d' ' |grep '/usr/lib/modules/.*/modules'|cut -f5 -d/ | uniq)" != "$(uname -r)" ]]; then
            echo "running unexpected kernel version $(uname -r)"
            exit 1
        fi
    fi

    # debian-sid packaging is special
    if os.query is-debian sid; then
        if [ ! -d packaging/debian-sid ]; then
            echo "no packaging/debian-sid/ directory "
            echo "broken test setup"
            exit 1
        fi

        # remove etckeeper
        apt purge -y etckeeper

        # debian has its own packaging
        rm -f debian
        # the debian dir must be a real dir, a symlink will make
        # dpkg-buildpackage choke later.
        mv packaging/debian-sid debian

        # get the build-deps
        apt build-dep -y ./

        # and ensure we don't take any of the vendor deps
        rm -rf vendor/*/

        # and create a fake upstream tarball
        tar -c -z -f ../snapd_"$(dpkg-parsechangelog --show-field Version|cut -d- -f1)".orig.tar.gz --exclude=./debian --exclude=./.git --exclude='*.pyc' .

        # and build a source package - this will be used during the sbuild test
        dpkg-buildpackage -S -uc -us
    fi

    # so is ubuntu-14.04
    if os.query is-trusty; then
        if [ ! -d packaging/ubuntu-14.04 ]; then
            echo "no packaging/ubuntu-14.04/ directory "
            echo "broken test setup"
            exit 1
        fi

        # 14.04 has its own packaging
        ./generate-packaging-dir

        quiet eatmydata apt-get install -y software-properties-common

	# FIXME: trusty-proposed disabled because there is an inconsistency
	#        in the trusty-proposed archive:
	# linux-generic-lts-xenial : Depends: linux-image-generic-lts-xenial (= 4.4.0.143.124) but 4.4.0.141.121 is to be installed
        #echo 'deb http://archive.ubuntu.com/ubuntu/ trusty-proposed main universe' >> /etc/apt/sources.list
        quiet add-apt-repository ppa:snappy-dev/image
        quiet eatmydata apt-get update

        quiet eatmydata apt-get install -y --install-recommends linux-generic-lts-xenial
        quiet eatmydata apt-get install -y --force-yes apparmor libapparmor1 seccomp libseccomp2 systemd cgroup-lite util-linux
    fi

    # ubuntu-16.04 is EOL so the updated go-1.18 is only available via
    # the ppa:snappy-dev/image ppa for now. if needed the package could
    # be copied from the PPA to the ESM archive.
    if os.query is-xenial; then
        quiet add-apt-repository ppa:snappy-dev/image
        quiet eatmydata apt-get update
    fi

    # WORKAROUND for older postrm scripts that did not do
    # "rm -rf /var/cache/snapd"
    rm -rf /var/cache/snapd/aux
    case "$SPREAD_SYSTEM" in
        ubuntu-*)
            # Ubuntu is the only system where snapd is preinstalled, so we have
            # to purge it

            # first mask snapd.failure so that even if we kill snapd and it 
            # dies, snap-failure doesn't run and try to revive snapd
            systemctl mask snapd.failure

            # next abort all ongoing changes and wait for them all to be done
            for chg in $(snap changes | tail -n +2 | grep Do | grep -v Done | awk '{print $1}'); do
                snap abort "$chg" || true
                snap watch "$chg" || true
            done

            # now remove all snaps that aren't a base, core or snapd
            for sn in $(snap list | tail -n +2 | awk '{print $1,$6}' | grep -Po '(.+)\s+(?!base)' | awk '{print $1}'); do
                if [ "$sn" != snapd ] && [ "$sn" != core ]; then
                    snap remove "$sn" || true
                fi
            done

            # now we can attempt to purge the actual distro package via apt
            distro_purge_package snapd
            # XXX: the original package's purge may have left socket units behind
            find /etc/systemd/system -name "snap.*.socket" | while read -r f; do
                systemctl stop "$(basename "$f")" || true
                rm -f "$f"
            done
            # double check that purge really worked
            if [ -d /var/lib/snapd ]; then
                echo "# /var/lib/snapd"
                ls -lR /var/lib/snapd || true
                journalctl --no-pager || true
                cat /var/lib/snapd/state.json || true
                snap debug state /var/lib/snapd/state.json || true
                (
                    for chg in $(snap debug state /var/lib/snapd/state.json | tail -n +2 | awk '{print $1}'); do
                        snap debug state --abs-time "--change=$chg" /var/lib/snapd/state.json || true
                    done
                ) || true
                exit 1
            fi

            # unmask snapd.failure so that it can run during tests if needed
            systemctl unmask snapd.failure 
            ;;
        *)
            # snapd state directory must not exist when the package is not
            # installed
            if [ -d /var/lib/snapd ]; then
                echo "# /var/lib/snapd"
                ls -lR /var/lib/snapd || true
                exit 1
            fi
            ;;
    esac

    restart_logind=
    local systemd_ver
    systemd_ver="$(systemctl --version | awk '/systemd [0-9]+/ { print $2 }' | cut -f1 -d"~")"
    if [ "$systemd_ver" -lt 246 ]; then
        restart_logind=maybe
    fi

    install_pkg_dependencies

    if [ "$restart_logind" = maybe ]; then
        if [ "$systemd_ver" -ge 246 ]; then
            restart_logind=yes
        else
            restart_logind=
        fi
    fi

    # Work around systemd / Debian bug interaction. We are installing
    # libsystemd-dev which upgrades systemd to 246-2 (from 245-*) leaving
    # behind systemd-logind.service from the old version. This is tracked as
    # Debian bug https://bugs.debian.org/cgi-bin/bugreport.cgi?bug=919509 and
    # it really affects Desktop systems where Wayland/X don't like logind from
    # ever being restarted.
    #
    # As a workaround we tried to restart logind ourselves but this caused
    # another issue.  Restarted logind, as of systemd v245, forgets about the
    # root session and subsequent loginctl enable-linger root, loginctl
    # disable-linger stops the running systemd --user for the root session,
    # along with other services like session bus.
    #
    # In consequence all the code that restarts logind for one reason or
    # another is coalesced below and ends with REBOOT. This ensures that after
    # rebooting, we have an up-to-date, working logind and that the initial
    # session used by spread is tracked.
    if ! loginctl enable-linger test; then
        if systemctl cat systemd-logind.service | not grep -q StateDirectory; then
            mkdir -p /mnt/system-data/etc/systemd/system/systemd-logind.service.d
            # NOTE: The here-doc below must use tabs for proper operation.
            cat >/mnt/system-data/etc/systemd/system/systemd-logind.service.d/linger.conf <<-CONF
	[Service]
	StateDirectory=systemd/linger
	CONF
            mkdir -p /var/lib/systemd/linger
            test "$(command -v restorecon)" != "" && restorecon /var/lib/systemd/linger
            restart_logind=yes
        fi
    fi
    loginctl disable-linger test || true

    # FIXME: In an ideal world we'd just do this:
    #   systemctl daemon-reload
    #   systemctl restart systemd-logind.service
    # But due to this issue, restarting systemd-logind is unsafe.
    # https://github.com/systemd/systemd/issues/16685#issuecomment-671239737
    if [ "$restart_logind" = yes ]; then
        echo "logind upgraded, reboot required"
        REBOOT
    fi

    # We take a special case for Debian/Ubuntu where we install additional build deps
    # base on the packaging. In Fedora/Suse this is handled via mock/osc
    case "$SPREAD_SYSTEM" in
        debian-*|ubuntu-*)
            best_golang=golang-1.18
            case "$SPREAD_SYSTEM" in
                ubuntu-fips-*)
                    # we are limited by the FIPS variants of go toolchain
                    # available from the PPA, and we need to match the Go
                    # version expected by during FIPS build of the deb, which
                    # currently expects 1.21, see:
                    # https://launchpad.net/~ubuntu-toolchain-r/+archive/ubuntu/golang-fips
                    best_golang=golang-1.21
                    quiet apt install -y golang-1.21
                    ;;
            esac
            # in 16.04: "apt build-dep -y ./" would also work but not on 14.04
            gdebi --quiet --apt-line ./debian/control >deps.txt
            quiet xargs -r eatmydata apt-get install -y < deps.txt
            # The go 1.18 backport is not using alternatives or anything else so
            # we need to get it on path somehow. This is not perfect but simple.
            if [ -z "$(command -v go)" ]; then
                # the path filesystem path is: /usr/lib/go-1.18/bin
                ln -s "/usr/lib/${best_golang/lang/}/bin/go" /usr/bin/go
            fi
            ;;
    esac

    # Retry go mod vendor to minimize the number of connection errors during the sync
    retry -n 10 go mod vendor
    # Update C dependencies
    ( cd c-vendor && retry -n 10 ./vendor.sh )

    # go mod runs as root and will leave strange permissions
    chown test:test -R "$SPREAD_PATH"

    # We are testing snapd snap on top of snapd from the archive
    # of the tested distribution. Download snapd and snap-confine
    # as they exist in the archive for further use.
    if tests.info is-snapd-from-archive; then
        ( cd "${GOHOME}" && tests.pkgs download snapd snap-confine)
    else
        case "$SPREAD_SYSTEM" in
            ubuntu-*|debian-*)
                build_deb
                ;;
            fedora-*|opensuse-*|amazon-*|centos-*)
                build_rpm
                ;;
            arch-*)
                build_arch_pkg
                ;;
            *)
                echo "ERROR: No build instructions available for system $SPREAD_SYSTEM"
                exit 1
                ;;
        esac
    fi

    # Build fakestore.
    fakestore_tags=
    if [ "$REMOTE_STORE" = staging ]; then
        fakestore_tags="-tags withstagingkeys"
    fi

    # eval to prevent expansion errors on opensuse (the variable keeps quotes)
    eval "go install $fakestore_tags ./tests/lib/fakestore/cmd/fakestore"

    # Build additional utilities we need for testing
    go install ./tests/lib/fakedevicesvc
    go install ./tests/lib/systemd-escape

    # Build the tool for signing model assertions
    go install ./tests/lib/gendeveloper1

    # and the U20 create partitions wrapper
    go install ./tests/lib/uc20-create-partitions

    # On core systems, the journal service is configured once the final core system
    # is created and booted what is done during the first test suite preparation
    if os.query is-classic; then
        # shellcheck source=tests/lib/prepare.sh
        . "$TESTSLIB"/prepare.sh
        disable_journald_rate_limiting
        disable_journald_start_limiting
    fi
}

prepare_project_each() {
    # Clear the kernel ring buffer.
    dmesg -c > /dev/null

    fixup_dev_random
}

prepare_suite() {
    # shellcheck source=tests/lib/prepare.sh
    . "$TESTSLIB"/prepare.sh

    # os.query cannot be used because first time the suite is prepared, the current system
    # is classic ubuntu, so it is needed to check the system set in $SPREAD_SYSTEM
    if is_test_target_core; then
        prepare_ubuntu_core
    else
        prepare_classic
    fi

    if [ -n "$TAG_FEATURES" ]; then
        snap set system journal.persistent=true
    fi

    # Make sure the suite starts with a clean environment and with the snapd state restored
    # shellcheck source=tests/lib/reset.sh
    "$TESTSLIB"/reset.sh --reuse-core
}

prepare_suite_each() {
    local variant="$1"

    # Create runtime files in case those don't exist
    # This is for the first test of the suite. We cannot perform these operations in prepare_suite
    # because not all suites are triggering it (for example the tools suite doesn't).
    touch "$RUNTIME_STATE_PATH/runs"
    touch "$RUNTIME_STATE_PATH/journalctl_cursor"

    # Clean the dmesg log
    dmesg --read-clear

    # Start fs monitor
    "$TESTSTOOLS"/fs-state start-monitor

    # Save all the installed packages
    if os.query is-classic; then
        tests.pkgs list-installed > installed-initial.pkgs
    fi

    # back test directory to be restored during the restore
    tests.backup prepare

    # save the job which is going to be executed in the system
    echo -n "${SPREAD_JOB:-} " >> "$RUNTIME_STATE_PATH/runs"

    # Restart journal log and reset systemd journal cursor.
    systemctl reset-failed systemd-journald.service
    if ! systemctl restart systemd-journald.service; then
        systemctl status systemd-journald.service || true
        echo "Failed to restart systemd-journald.service, exiting..."
        exit 1
    fi
    "$TESTSTOOLS"/journal-state start-new-log

    # Check if journalctl is ready to run the test
    "$TESTSTOOLS"/journal-state check-log-started

    # In case of nested tests the next checks and changes are not needed
    if tests.nested is-nested; then
        return 0
    fi

    if [[ "$variant" = full ]]; then
        # shellcheck source=tests/lib/prepare.sh
        . "$TESTSLIB"/prepare.sh
        # shellcheck source=tests/lib/prepare.sh
        . "$TESTSLIB"/prepare.sh
        if os.query is-classic; then
            prepare_each_classic
        else
            prepare_each_core
        fi
        prepare_state_lock "$SPREAD_JOB"
    fi

    case "$SPREAD_SYSTEM" in
        fedora-*|centos-*|amazon-*)
            ausearch -i -m AVC --checkpoint "$RUNTIME_STATE_PATH/audit-stamp" || true
            ;;
    esac

    # Check for invariants late, in order to detect any bugs in the code above.
    if [[ "$variant" = full ]]; then
        "$TESTSTOOLS"/cleanup-state pre-invariant
    fi
    tests.invariant check
}

restore_suite_each() {
    if not tests.nested is-nested; then
        "$TESTSLIB"/collect-artifacts.sh features --after-non-nested-task
        "$TESTSLIB"/collect-artifacts.sh locks
    fi
    local variant="$1"

    rm -f "$RUNTIME_STATE_PATH/audit-stamp"

    # Run the cleanup restore in case the commands have not been restored
    tests.cleanup restore

    # restore test directory saved during prepare
    tests.backup restore

    # Save all the installed packages and remove the new packages installed 
    if os.query is-classic; then
        tests.pkgs list-installed > installed-final.pkgs
        diff -u installed-initial.pkgs installed-final.pkgs | grep -E "^\+" | tail -n+2 | cut -c 2- > installed-in-test.pkgs
        diff -u installed-initial.pkgs installed-final.pkgs | grep -E "^\-" | tail -n+2 | cut -c 2- > removed-in-test.pkgs

        # shellcheck disable=SC2002
        packages="$(cat installed-in-test.pkgs | tr "\n" " ")"
        if [ -n "$packages" ]; then
            # shellcheck disable=SC2086
            tests.pkgs remove $packages
        fi
        # shellcheck disable=SC2002
        packages="$(cat removed-in-test.pkgs | tr "\n" " ")"
        if [ -n "$packages" ]; then
            # shellcheck disable=SC2086
            tests.pkgs install $packages
        fi
    fi

    # In case of nested tests the next checks and changes are not needed
    # Just is needed to cleanup the snaps installed
    if tests.nested is-nested; then
        "$TESTSTOOLS"/snaps.cleanup
        return 0
    fi

    # On Arch it seems that using sudo / su for working with the test user
    # spawns the /run/user/12345 tmpfs for XDG_RUNTIME_DIR which asynchronously
    # cleans up itself sometime after the test but not instantly, leading to
    # random failures in the mount leak detector. Give it a moment but don't
    # clean it up ourselves, this should report actual test errors, if any.
    for i in $(seq 10); do
        if not mountinfo.query /run/user/12345 .fs_type=tmpfs; then
            break
        fi
        sleep 1
    done

    if [[ "$variant" = full ]]; then
        # Reset the failed status of snapd, snapd.socket, and snapd.failure.socket
        # to prevent hitting the system restart rate-limit for these services.
        systemctl reset-failed snapd.service snapd.socket
        # This unit may not be present, it is masked on some systems.
        if systemctl status snapd.failure.service; then
            systemctl reset-failed snapd.failure.service
        fi
    fi

    if [[ "$variant" = full ]]; then
        # shellcheck source=tests/lib/reset.sh
        "$TESTSLIB"/reset.sh --reuse-core
    fi

    # Check for invariants late, in order to detect any bugs in the code above.
    if [[ "$variant" = full ]]; then
        "$TESTSTOOLS"/cleanup-state pre-invariant
    fi
    tests.invariant check

    "$TESTSTOOLS"/fs-state check-monitor
}

restore_suite() {
    # shellcheck source=tests/lib/reset.sh
    if [ "$REMOTE_STORE" = staging ]; then
        "$TESTSTOOLS"/store-state teardown-staging-store
    fi

    if os.query is-classic; then
        # shellcheck source=tests/lib/pkgdb.sh
        . "$TESTSLIB"/pkgdb.sh
        distro_purge_package snapd
        # On Tumbleweed, removing the package doesn't stop the snapd units so ensure they are stopped
        systemctl stop snapd.socket snapd.service || true
        if [[ "$SPREAD_SYSTEM" != opensuse-* && "$SPREAD_SYSTEM" != arch-* ]]; then
            # A snap-confine package never existed on openSUSE or Arch
            distro_purge_package snap-confine
        fi
    fi
}

restore_project_each() {
    "$TESTSTOOLS"/cleanup-state pre-invariant
    # Check for invariants early, in order not to mask bugs in tests.
    tests.invariant check
    "$TESTSTOOLS"/cleanup-state post-invariant

    # TODO: move this to tests.cleanup.
    restore_dev_random

    # TODO: move this to tests.invariant.
    # Udev rules are notoriously hard to write and seemingly correct but subtly
    # wrong rules can pass review. Whenever that happens udev logs an error
    # message. As a last resort from lack of a better mechanism we can try to
    # pick up such errors.
    if grep "invalid .*snap.*.rules" /var/log/syslog; then
        echo "Invalid udev file detected, test most likely broke it"
        exit 1
    fi

    # TODO: move this to tests.invariant.
    # Check if the OOM killer got invoked - if that is the case our tests
    # will most likely not function correctly anymore. It looks like this
    # happens with: https://forum.snapcraft.io/t/4101 and is a source of
    # failure in the autopkgtest environment.
    # Also catch a scenario when snapd service hits the MemoryMax limit set while
    # preparing the tests.
    if dmesg|grep "oom-killer"; then
        echo "oom-killer got invoked during the tests, this should not happen."
        echo "Dmesg debug output:"
        dmesg
        echo "Meminfo debug output:"
        cat /proc/meminfo
        exit 1
    fi

    # TODO: move this to tests.invariant.
    # check if there is a shutdown pending, no test should trigger this
    # and it leads to very confusing test failures
    if [ -e /run/systemd/shutdown/scheduled ]; then
        echo "Test triggered a shutdown, this should not happen"
        snap changes
        exit 1
    fi

    # TODO: move this to tests.invariant.
    # Check for kernel oops during the tests
    if dmesg|grep "Oops: "; then
        echo "A kernel oops happened during the tests, test results will be unreliable"
        echo "Dmesg debug output:"
        dmesg
        exit 1
    fi

    # TODO: move this to tests.invariant.
    if getent passwd snap_daemon; then
        echo "Test left the snap_daemon user behind, this should not happen"
        exit 1
    fi
    if getent group snap_daemon; then
        echo "Test left the snap_daemon group behind, this should not happen"
        exit 1
    fi

    # TODO: move this to tests.invariant.
    # Something is hosing the filesystem so look for signs of that
    not grep -F "//deleted /etc" /proc/self/mountinfo

    # TODO: move this to tests.invariant.
    if journalctl -u snapd.service | grep -F "signal: terminated"; then
        exit 1;
    fi

    # TODO: move this to tests.invariant.
    case "$SPREAD_SYSTEM" in
        fedora-*|centos-*)
            # Make sure that we are not leaving behind incorrectly labeled snap
            # files on systems supporting SELinux
            (
                find /root/snap -printf '%Z\t%H/%P\n' || true
                find /home -regex '/home/[^/]*/snap\(/.*\)?' -printf '%Z\t%H/%P\n' || true
            ) | grep -c -v snappy_home_t | MATCH "0"

            find /var/snap -printf '%Z\t%H/%P\n' | grep -c -v snappy_var_t  | MATCH "0"
            ;;
    esac
}

restore_project() {
    # Delete the snapd state used to accelerate prepare/restore code in certain suites.
    delete_snapd_state

    # Remove all of the code we pushed and any build results. This removes
    # stale files and we cannot do incremental builds anyway so there's little
    # point in keeping them.
    if [ -n "$GOPATH" ]; then
        rm -rf "${GOPATH%%:*}"
    fi

    rm -rf /etc/systemd/journald.conf.d/no-rate-limit.conf
    rmdir /etc/systemd/journald.conf.d || true
}

case "$1" in
    --prepare-project)
        prepare_project
        ;;
    --prepare-project-each)
        prepare_project_each
        ;;
    --prepare-suite)
        prepare_suite
        ;;
    --prepare-suite-each)
        prepare_suite_each full
        ;;
    --prepare-suite-each-minimal-no-snaps)
        prepare_suite_each minimal-no-snaps
        ;;
    --restore-suite-each)
        restore_suite_each full
        ;;
    --restore-suite-each-minimal-no-snaps)
        restore_suite_each minimal-no-snaps
        ;;
    --restore-suite)
        restore_suite
        ;;
    --restore-project-each)
        restore_project_each
        ;;
    --restore-project)
        restore_project
        ;;
    *)
        echo "unsupported argument: $1"
        echo "try one of --{prepare,restore}-{project,suite}{,-each} or --{prepare,restore}-suite-each-minimal-no-snaps"
        exit 1
        ;;
esac