#!/usr/bin/env python # 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 . import argparse import imp import errno import os import sys import glob import subprocess import tempfile BOTS = os.path.abspath(os.path.dirname(__file__)) BASE = os.path.normpath(os.path.join(BOTS, "..")) TEST = os.path.join(BASE, "test") os.environ["PATH"] = "{0}:{1}".format(os.environ.get("PATH"), BOTS) from machine import testvm 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='Build faster') parser.add_argument('-f', '--force', action='store_true', help='Force update of images') 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 = get_build_image(args.build_image or base_image) # Depending on the operating system ignore some things skips = [ "cockpit-integration-tests" ] 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 # Default to putting build output in the TEST_DATA directory results = os.path.join(os.environ.get("TEST_DATA", BASE), "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, ex: if ex.errno != errno.ENOENT: raise sys.stderr.write("image-prepare: missing tools to download images\n") except subprocess.CalledProcessError as e: 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, args.address) 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(BOTS, "images", "scripts", "lib") ], "/var/lib/testvm") machine.upload([ os.path.join(BOTS, "images", "scripts", "%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): if "/" not in base_image: base_image = os.path.join(BOTS, "images", base_image) test_images = os.path.join(TEST, "images") if not os.path.exists(test_images): os.makedirs(test_images) install_image = os.path.join(test_images, os.path.basename(base_image)) base_image = os.path.realpath(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: do_install = False skips = list(skips or []) if "atomic" in machine.image: skips.append("cockpit-kubernetes") else: skips.append("cockpit-ostree") skip_args = map(lambda skip: " --skip '%s'" % skip, 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) if install and args.containers: machine.execute("/var/lib/testvm/containers.install") 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) machine = testvm.VirtMachine(verbose=args.verbose, image=build_image, label="install") machine.start(maintain=True, memory_mb=4096, cpus=4) completed = False # While the machine is booting, make a tarball source = subprocess.check_output([ os.path.join(BASE, "tools", "make-source") ]).strip() 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("ADDRESS: {0}\n".format(machine.address)) raw_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, address): """Install Cockpit into a test image""" started = False if args.address: machine = testvm.Machine(address=args.address, verbose=args.verbose, image=image, label="install") else: image = prepare_install_image(image) machine = testvm.VirtMachine(verbose=args.verbose, image=image, label="install") machine.start(maintain=True) 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("ADDRESS: {0}\n".format(machine.address)) raw_input ("Press RET to continue... ") if started: machine.stop() # The Atomic variants can't build their own packages, so we build in # their non-Atomic siblings. For example, fedora-atomic is built # in fedora-25 def get_build_image(image): (test_os, unused) = os.path.splitext(os.path.basename(image)) if test_os == "fedora-atomic": image = "fedora-25" elif test_os == "rhel-atomic": image = "rhel-7" elif test_os == "continuous-atomic": image = "centos-7" return image if __name__ == "__main__": sys.exit(main())