File: image-prepare

package info (click to toggle)
cockpit 239-1
  • links: PTS, VCS
  • area: main
  • in suites: bullseye
  • size: 67,268 kB
  • sloc: javascript: 245,474; ansic: 72,273; python: 23,634; xml: 6,155; sh: 2,919; makefile: 923; sed: 5
file content (209 lines) | stat: -rwxr-xr-x 8,664 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
#!/usr/bin/env python3
# This file is part of Cockpit.
#
# Copyright (C) 2015 Red Hat, Inc.
#
# Cockpit is free software; you can redistribute it and/or modify it
# under the terms of the GNU Lesser General Public License as published by
# the Free Software Foundation; either version 2.1 of the License, or
# (at your option) any later version.
#
# Cockpit 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
# Lesser General Public License for more details.
#
# You should have received a copy of the GNU Lesser General Public License
# along with Cockpit; If not, see <http://www.gnu.org/licenses/>.

import argparse
import errno
import os
import sys
import subprocess

from verify import parent
import make_dist
import testvm

parent  # pyflakes

TEST = os.path.abspath(os.path.dirname(__file__))
BASE = os.path.normpath(os.path.join(TEST, ".."))
BOTS = os.path.join(BASE, "bots")
os.environ["PATH"] = "{0}:{1}".format(os.environ.get("PATH"), BOTS)

networking = None


def main():
    parser = argparse.ArgumentParser(
        description='Prepare testing environment, download images and build and install cockpit',
        formatter_class=argparse.ArgumentDefaultsHelpFormatter)
    parser.add_argument('-v', '--verbose', action='store_true', help='Display verbose progress details')
    parser.add_argument('-s', '--sit', action='store_true', help='Sit and wait if install script fails')
    parser.add_argument('-q', '--quick', action='store_true', help='Skip unit tests to build faster')
    parser.add_argument('-o', '--overlay', action='store_true', help='Install into existing test/image/ overlay instead of from pristine base image')
    parser.add_argument('-b', '--build-image', action='store', help='Build in this image')
    parser.add_argument('-B', '--build-only', action='store_true', help='Only build and download results')
    parser.add_argument('-I', '--install-only', action='store_true', help='Only upload and install')
    parser.add_argument('-c', '--containers', action='store_true', help='Install container images')
    parser.add_argument('--address', help='Address of already running machine, implies --install-only')
    parser.add_argument('image', nargs='?', default=testvm.DEFAULT_IMAGE, help='The image to use')
    args = parser.parse_args()

    # The basic operating system we're preparing on top of. Never written to
    base_image = args.image

    # The image we're going to build in. This image is never written to
    build_image = None
    if not args.install_only:
        build_image = testvm.get_build_image(args.build_image or base_image)

    # Depending on the operating system ignore some things
    skips = []
    if args.address:
        skips.append("cockpit-tests")
        args.install_only = True

    if args.install_only and args.build_only:
        sys.stderr.write("image-prepare: use of --install-only with --build-only is incompatible\n")
        return 2

    results = os.path.abspath("tmp/build-results")

    # Make sure any images are downloaded
    try:
        if not args.address and not os.path.exists(base_image):
            subprocess.check_call(["image-download", base_image])
        if build_image and not os.path.exists(build_image):
            subprocess.check_call(["image-download", build_image])
    except OSError as ex:
        if ex.errno != errno.ENOENT:
            raise
        sys.stderr.write("image-prepare: missing tools to download images\n")
    except subprocess.CalledProcessError:
        sys.stderr.write("image-prepare: unable to download all necessary images\n")
        return 1

    try:
        if not args.install_only:
            build_and_install(build_image, results, skips, args)
        if not args.build_only and build_image != base_image:
            only_install(base_image, results, skips, args)
    except (RuntimeError, testvm.Failure) as ex:
        sys.stderr.write("image-prepare: {0}\n".format(str(ex)))
        return 2

    return 0


def upload_scripts(machine, args):
    machine.execute("rm -rf /var/lib/testvm")
    machine.upload([os.path.join(testvm.SCRIPTS_DIR, "lib")], "/var/lib/testvm")
    machine.upload([os.path.join(BASE, "tools", "make-srpm")], "/var/lib/testvm")
    machine.upload([os.path.join(testvm.SCRIPTS_DIR, "%s.install" % machine.image)], "/var/tmp")
    machine.upload([os.path.join(BASE, "containers")], "/var/tmp")


# Create the necessary layered image for the build/install
def prepare_install_image(base_image, overlay=False):
    if "/" not in base_image:
        base_image = os.path.join(testvm.IMAGES_DIR, base_image)
    test_images = os.path.join(TEST, "images")
    os.makedirs(test_images, exist_ok=True)
    install_image = os.path.join(test_images, os.path.basename(base_image))
    if not os.path.exists(install_image) or not overlay:
        base_image = os.path.realpath(testvm.get_test_image(base_image))
        qcow2_image = "{0}.qcow2".format(install_image)
        subprocess.check_call(["qemu-img", "create", "-q", "-f", "qcow2",
                               "-o", "backing_file={0},backing_fmt=qcow2".format(base_image),
                               qcow2_image])
        if os.path.lexists(install_image):
            os.unlink(install_image)
        os.symlink(os.path.basename(qcow2_image), install_image)
    return install_image


def run_install_script(machine, do_build, do_install, skips, arg, args):
    install = do_install
    if args.containers and not do_build:
        do_install = False

    skips = list(skips or [])

    skip_args = [" --skip '%s'" % skip for skip in skips]
    cmd = "cd /var/tmp; ./%s.install%s%s%s%s%s%s" % (machine.image,
                                                     " --verbose" if args.verbose else "",
                                                     " --quick" if args.quick else "",
                                                     " --build" if do_build else "",
                                                     " --install" if do_install else "",
                                                     " ".join(skip_args),
                                                     " '%s'" % arg if arg else "")
    machine.execute(cmd, timeout=1800)

    if install and args.containers:
        machine.execute("/var/lib/testvm/containers.install", timeout=900)


def build_and_install(build_image, build_results, skips, args):
    """Build and maybe install Cockpit into a test image"""
    build_image = prepare_install_image(build_image, args.overlay)
    machine = testvm.VirtMachine(verbose=args.verbose, image=build_image,
                                 memory_mb=2048, cpus=4, maintain=True, networking=networking)
    completed = False

    # While the machine is booting, make a dist tarball
    source = make_dist.make_dist()

    machine.start()
    completed = False

    try:
        machine.wait_boot()
        upload_scripts(machine, args=args)
        machine.upload([source], "/var/tmp")
        run_install_script(machine, True, True, skips, os.path.basename(source), args)
        completed = True
    finally:
        if not completed and args.sit:
            sys.stderr.write(machine.diagnose())
            input("Press RET to continue... ")
        try:
            if os.path.exists(build_results):
                subprocess.check_call(["rm", "-rf", build_results])
            os.makedirs(build_results)
            machine.download("/var/tmp/build-results/*", build_results)
        finally:
            machine.stop()


def only_install(image, build_results, skips, args):
    """Install Cockpit into a test image"""
    started = False
    if args.address:
        machine = testvm.Machine(address=args.address, verbose=args.verbose, image=image)
    else:
        image = prepare_install_image(image, args.overlay)
        machine = testvm.VirtMachine(verbose=args.verbose, image=image, networking=networking, maintain=True)
        machine.start()
        started = True
    completed = False
    try:
        if started:
            machine.wait_boot()
        upload_scripts(machine, args=args)
        machine.execute("rm -rf /var/tmp/build-results")
        machine.upload([build_results], "/var/tmp/build-results")
        run_install_script(machine, False, True, skips, None, args)
        completed = True
    finally:
        if not completed and args.sit:
            sys.stderr.write(machine.diagnose())
            input("Press RET to continue... ")
        if started:
            machine.stop()


if __name__ == "__main__":
    sys.exit(main())