1*bb4ee6a4SAndroid Build Coastguard Worker#!/usr/bin/env python3 2*bb4ee6a4SAndroid Build Coastguard Worker# Copyright 2021 The ChromiumOS Authors 3*bb4ee6a4SAndroid Build Coastguard Worker# Use of this source code is governed by a BSD-style license that can be 4*bb4ee6a4SAndroid Build Coastguard Worker# found in the LICENSE file. 5*bb4ee6a4SAndroid Build Coastguard Worker 6*bb4ee6a4SAndroid Build Coastguard Worker# Usage: 7*bb4ee6a4SAndroid Build Coastguard Worker# 8*bb4ee6a4SAndroid Build Coastguard Worker# To get an interactive shell for development: 9*bb4ee6a4SAndroid Build Coastguard Worker# ./tools/dev_container 10*bb4ee6a4SAndroid Build Coastguard Worker# 11*bb4ee6a4SAndroid Build Coastguard Worker# To run a command in the container, e.g. to run presubmits: 12*bb4ee6a4SAndroid Build Coastguard Worker# ./tools/dev_container ./tools/presubmit 13*bb4ee6a4SAndroid Build Coastguard Worker# 14*bb4ee6a4SAndroid Build Coastguard Worker# The state of the container (including build artifacts) are preserved between 15*bb4ee6a4SAndroid Build Coastguard Worker# calls. To stop the container call: 16*bb4ee6a4SAndroid Build Coastguard Worker# ./tools/dev_container --stop 17*bb4ee6a4SAndroid Build Coastguard Worker# 18*bb4ee6a4SAndroid Build Coastguard Worker# The dev container can also be called with a fresh container for each call that 19*bb4ee6a4SAndroid Build Coastguard Worker# is cleaned up afterwards (e.g. when run by Kokoro): 20*bb4ee6a4SAndroid Build Coastguard Worker# 21*bb4ee6a4SAndroid Build Coastguard Worker# ./tools/dev_container --hermetic CMD 22*bb4ee6a4SAndroid Build Coastguard Worker# 23*bb4ee6a4SAndroid Build Coastguard Worker# There's an alternative container which can be used to test crosvm in crOS tree. 24*bb4ee6a4SAndroid Build Coastguard Worker# It can be launched with: 25*bb4ee6a4SAndroid Build Coastguard Worker# ./tools/dev_container --cros 26*bb4ee6a4SAndroid Build Coastguard Worker 27*bb4ee6a4SAndroid Build Coastguard Workerimport argparse 28*bb4ee6a4SAndroid Build Coastguard Workerfrom pathlib import Path 29*bb4ee6a4SAndroid Build Coastguard Workerimport shutil 30*bb4ee6a4SAndroid Build Coastguard Workerfrom impl.util import ( 31*bb4ee6a4SAndroid Build Coastguard Worker add_common_args, 32*bb4ee6a4SAndroid Build Coastguard Worker confirm, 33*bb4ee6a4SAndroid Build Coastguard Worker cros_repo_root, 34*bb4ee6a4SAndroid Build Coastguard Worker CROSVM_ROOT, 35*bb4ee6a4SAndroid Build Coastguard Worker is_cros_repo, 36*bb4ee6a4SAndroid Build Coastguard Worker is_kiwi_repo, 37*bb4ee6a4SAndroid Build Coastguard Worker kiwi_repo_root, 38*bb4ee6a4SAndroid Build Coastguard Worker is_aosp_repo, 39*bb4ee6a4SAndroid Build Coastguard Worker aosp_repo_root, 40*bb4ee6a4SAndroid Build Coastguard Worker) 41*bb4ee6a4SAndroid Build Coastguard Workerfrom impl.command import ( 42*bb4ee6a4SAndroid Build Coastguard Worker chdir, 43*bb4ee6a4SAndroid Build Coastguard Worker cmd, 44*bb4ee6a4SAndroid Build Coastguard Worker quoted, 45*bb4ee6a4SAndroid Build Coastguard Worker) 46*bb4ee6a4SAndroid Build Coastguard Workerfrom typing import Optional, List 47*bb4ee6a4SAndroid Build Coastguard Workerimport getpass 48*bb4ee6a4SAndroid Build Coastguard Workerimport sys 49*bb4ee6a4SAndroid Build Coastguard Workerimport unittest 50*bb4ee6a4SAndroid Build Coastguard Workerimport os 51*bb4ee6a4SAndroid Build Coastguard Workerimport zlib 52*bb4ee6a4SAndroid Build Coastguard Worker 53*bb4ee6a4SAndroid Build Coastguard WorkerDEV_CONTAINER_NAME = ( 54*bb4ee6a4SAndroid Build Coastguard Worker f"crosvm_dev_{getpass.getuser()}_{zlib.crc32(os.path.realpath(__file__).encode('utf-8')):x}" 55*bb4ee6a4SAndroid Build Coastguard Worker) 56*bb4ee6a4SAndroid Build Coastguard WorkerCROS_CONTAINER_NAME = ( 57*bb4ee6a4SAndroid Build Coastguard Worker f"crosvm_cros_{getpass.getuser()}_{zlib.crc32(os.path.realpath(__file__).encode('utf-8')):x}" 58*bb4ee6a4SAndroid Build Coastguard Worker) 59*bb4ee6a4SAndroid Build Coastguard Worker 60*bb4ee6a4SAndroid Build Coastguard WorkerDEV_IMAGE_NAME = "gcr.io/crosvm-infra/crosvm_dev" 61*bb4ee6a4SAndroid Build Coastguard WorkerCROS_IMAGE_NAME = "gcr.io/crosvm-infra/crosvm_cros_cloudbuild" 62*bb4ee6a4SAndroid Build Coastguard WorkerDEV_IMAGE_VERSION = (CROSVM_ROOT / "tools/impl/dev_container/version").read_text().strip() 63*bb4ee6a4SAndroid Build Coastguard Worker 64*bb4ee6a4SAndroid Build Coastguard WorkerCACHE_DIR = os.environ.get("CROSVM_CONTAINER_CACHE", None) 65*bb4ee6a4SAndroid Build Coastguard Worker 66*bb4ee6a4SAndroid Build Coastguard WorkerCOMMON_ARGS = [ 67*bb4ee6a4SAndroid Build Coastguard Worker # Share cache dir 68*bb4ee6a4SAndroid Build Coastguard Worker f"--volume {CACHE_DIR}:/cache:rw" if CACHE_DIR else None, 69*bb4ee6a4SAndroid Build Coastguard Worker # Use tmpfs in the container for faster performance. 70*bb4ee6a4SAndroid Build Coastguard Worker "--mount type=tmpfs,destination=/tmp", 71*bb4ee6a4SAndroid Build Coastguard Worker # KVM is required to run a VM for testing. 72*bb4ee6a4SAndroid Build Coastguard Worker "--device /dev/kvm" if Path("/dev/kvm").is_char_device() else None, 73*bb4ee6a4SAndroid Build Coastguard Worker # Enable terminal colors 74*bb4ee6a4SAndroid Build Coastguard Worker f"--env TERM={os.environ.get('TERM', 'xterm-256color')}", 75*bb4ee6a4SAndroid Build Coastguard Worker] 76*bb4ee6a4SAndroid Build Coastguard Worker 77*bb4ee6a4SAndroid Build Coastguard WorkerDOCKER_ARGS = [ 78*bb4ee6a4SAndroid Build Coastguard Worker *COMMON_ARGS, 79*bb4ee6a4SAndroid Build Coastguard Worker] 80*bb4ee6a4SAndroid Build Coastguard Worker 81*bb4ee6a4SAndroid Build Coastguard WorkerPODMAN_ARGS = [ 82*bb4ee6a4SAndroid Build Coastguard Worker *COMMON_ARGS, 83*bb4ee6a4SAndroid Build Coastguard Worker # Allow access to group permissions of the user (e.g. for kvm access). 84*bb4ee6a4SAndroid Build Coastguard Worker "--group-add keep-groups" if os.name == "posix" else None, 85*bb4ee6a4SAndroid Build Coastguard Worker # Increase number of PIDs the container can spawn (we run a lot of test processes in parallel) 86*bb4ee6a4SAndroid Build Coastguard Worker "--pids-limit=4096" if os.name == "posix" else None, 87*bb4ee6a4SAndroid Build Coastguard Worker] 88*bb4ee6a4SAndroid Build Coastguard Worker 89*bb4ee6a4SAndroid Build Coastguard Worker# Environment variables to pass through to the container if they are specified. 90*bb4ee6a4SAndroid Build Coastguard WorkerENV_PASSTHROUGH = [ 91*bb4ee6a4SAndroid Build Coastguard Worker "NEXTEST_PROFILE", 92*bb4ee6a4SAndroid Build Coastguard Worker "http_proxy", 93*bb4ee6a4SAndroid Build Coastguard Worker "https_proxy", 94*bb4ee6a4SAndroid Build Coastguard Worker] 95*bb4ee6a4SAndroid Build Coastguard Worker 96*bb4ee6a4SAndroid Build Coastguard Worker 97*bb4ee6a4SAndroid Build Coastguard Workerdef machine_is_running(docker: cmd): 98*bb4ee6a4SAndroid Build Coastguard Worker machine_state = docker("machine info").stdout() 99*bb4ee6a4SAndroid Build Coastguard Worker return "MachineState: Running" in machine_state 100*bb4ee6a4SAndroid Build Coastguard Worker 101*bb4ee6a4SAndroid Build Coastguard Worker 102*bb4ee6a4SAndroid Build Coastguard Workerdef container_name(cros: bool): 103*bb4ee6a4SAndroid Build Coastguard Worker if cros: 104*bb4ee6a4SAndroid Build Coastguard Worker return CROS_CONTAINER_NAME 105*bb4ee6a4SAndroid Build Coastguard Worker else: 106*bb4ee6a4SAndroid Build Coastguard Worker return DEV_CONTAINER_NAME 107*bb4ee6a4SAndroid Build Coastguard Worker 108*bb4ee6a4SAndroid Build Coastguard Worker 109*bb4ee6a4SAndroid Build Coastguard Workerdef container_revision(docker: cmd, container_id: str): 110*bb4ee6a4SAndroid Build Coastguard Worker image = docker("container inspect -f {{.Config.Image}}", container_id).stdout() 111*bb4ee6a4SAndroid Build Coastguard Worker parts = image.split(":") 112*bb4ee6a4SAndroid Build Coastguard Worker assert len(parts) == 2, f"Invalid image name {image}" 113*bb4ee6a4SAndroid Build Coastguard Worker return parts[1] 114*bb4ee6a4SAndroid Build Coastguard Worker 115*bb4ee6a4SAndroid Build Coastguard Worker 116*bb4ee6a4SAndroid Build Coastguard Workerdef container_id(docker: cmd, cros: bool): 117*bb4ee6a4SAndroid Build Coastguard Worker return docker(f"ps -a -q -f name={container_name(cros)}").stdout() 118*bb4ee6a4SAndroid Build Coastguard Worker 119*bb4ee6a4SAndroid Build Coastguard Worker 120*bb4ee6a4SAndroid Build Coastguard Workerdef container_is_running(docker: cmd, cros: bool): 121*bb4ee6a4SAndroid Build Coastguard Worker return bool(docker(f"ps -q -f name={container_name(cros)}").stdout()) 122*bb4ee6a4SAndroid Build Coastguard Worker 123*bb4ee6a4SAndroid Build Coastguard Worker 124*bb4ee6a4SAndroid Build Coastguard Workerdef delete_container(docker: cmd, cros: bool): 125*bb4ee6a4SAndroid Build Coastguard Worker cid = container_id(docker, cros) 126*bb4ee6a4SAndroid Build Coastguard Worker if cid: 127*bb4ee6a4SAndroid Build Coastguard Worker print(f"Deleting dev-container {cid}.") 128*bb4ee6a4SAndroid Build Coastguard Worker docker("rm -f", cid).fg(quiet=True) 129*bb4ee6a4SAndroid Build Coastguard Worker return True 130*bb4ee6a4SAndroid Build Coastguard Worker return False 131*bb4ee6a4SAndroid Build Coastguard Worker 132*bb4ee6a4SAndroid Build Coastguard Worker 133*bb4ee6a4SAndroid Build Coastguard Workerdef workspace_mount_args(cros: bool): 134*bb4ee6a4SAndroid Build Coastguard Worker """ 135*bb4ee6a4SAndroid Build Coastguard Worker Returns arguments for mounting the crosvm sources to /workspace. 136*bb4ee6a4SAndroid Build Coastguard Worker 137*bb4ee6a4SAndroid Build Coastguard Worker In ChromeOS checkouts the crosvm repo uses a symlink or worktree checkout, which links to a 138*bb4ee6a4SAndroid Build Coastguard Worker different folder in the ChromeOS checkout. So we need to mount the whole CrOS checkout. 139*bb4ee6a4SAndroid Build Coastguard Worker """ 140*bb4ee6a4SAndroid Build Coastguard Worker if cros: 141*bb4ee6a4SAndroid Build Coastguard Worker return ["--workdir /home/crosvmdev/chromiumos/src/platform/crosvm"] 142*bb4ee6a4SAndroid Build Coastguard Worker elif is_cros_repo(): 143*bb4ee6a4SAndroid Build Coastguard Worker return [ 144*bb4ee6a4SAndroid Build Coastguard Worker f"--volume {quoted(cros_repo_root())}:/workspace:rw", 145*bb4ee6a4SAndroid Build Coastguard Worker "--workdir /workspace/src/platform/crosvm", 146*bb4ee6a4SAndroid Build Coastguard Worker ] 147*bb4ee6a4SAndroid Build Coastguard Worker elif is_kiwi_repo(): 148*bb4ee6a4SAndroid Build Coastguard Worker return [ 149*bb4ee6a4SAndroid Build Coastguard Worker f"--volume {quoted(kiwi_repo_root())}:/workspace:rw", 150*bb4ee6a4SAndroid Build Coastguard Worker # We override /scratch because we run out of memory if we use memory to back the 151*bb4ee6a4SAndroid Build Coastguard Worker # `/scratch` mount point. 152*bb4ee6a4SAndroid Build Coastguard Worker f"--volume {quoted(kiwi_repo_root())}/scratch:/scratch/cargo_target:rw", 153*bb4ee6a4SAndroid Build Coastguard Worker "--workdir /workspace/platform/crosvm", 154*bb4ee6a4SAndroid Build Coastguard Worker ] 155*bb4ee6a4SAndroid Build Coastguard Worker elif is_aosp_repo(): 156*bb4ee6a4SAndroid Build Coastguard Worker return [ 157*bb4ee6a4SAndroid Build Coastguard Worker f"--volume {quoted(aosp_repo_root())}:/workspace:rw", 158*bb4ee6a4SAndroid Build Coastguard Worker "--workdir /workspace/external/crosvm", 159*bb4ee6a4SAndroid Build Coastguard Worker ] 160*bb4ee6a4SAndroid Build Coastguard Worker else: 161*bb4ee6a4SAndroid Build Coastguard Worker return [ 162*bb4ee6a4SAndroid Build Coastguard Worker f"--volume {quoted(CROSVM_ROOT)}:/workspace:rw", 163*bb4ee6a4SAndroid Build Coastguard Worker ] 164*bb4ee6a4SAndroid Build Coastguard Worker 165*bb4ee6a4SAndroid Build Coastguard Worker 166*bb4ee6a4SAndroid Build Coastguard Workerdef ensure_container_is_alive(docker: cmd, docker_args: List[Optional[str]], cros: bool): 167*bb4ee6a4SAndroid Build Coastguard Worker cid = container_id(docker, cros) 168*bb4ee6a4SAndroid Build Coastguard Worker if cid and not container_is_running(docker, cros): 169*bb4ee6a4SAndroid Build Coastguard Worker print("Existing container is not running.") 170*bb4ee6a4SAndroid Build Coastguard Worker delete_container(docker, cros) 171*bb4ee6a4SAndroid Build Coastguard Worker elif cid and not cros and container_revision(docker, cid) != DEV_IMAGE_VERSION: 172*bb4ee6a4SAndroid Build Coastguard Worker print(f"New image is available.") 173*bb4ee6a4SAndroid Build Coastguard Worker delete_container(docker, cros) 174*bb4ee6a4SAndroid Build Coastguard Worker 175*bb4ee6a4SAndroid Build Coastguard Worker if not container_is_running(docker, cros): 176*bb4ee6a4SAndroid Build Coastguard Worker # Run neverending sleep to keep container alive while we 'docker exec' commands. 177*bb4ee6a4SAndroid Build Coastguard Worker print(f"Starting container...") 178*bb4ee6a4SAndroid Build Coastguard Worker docker( 179*bb4ee6a4SAndroid Build Coastguard Worker f"run --detach --name {container_name(cros)}", 180*bb4ee6a4SAndroid Build Coastguard Worker *docker_args, 181*bb4ee6a4SAndroid Build Coastguard Worker "sleep infinity", 182*bb4ee6a4SAndroid Build Coastguard Worker ).fg(quiet=False) 183*bb4ee6a4SAndroid Build Coastguard Worker cid = container_id(docker, cros) 184*bb4ee6a4SAndroid Build Coastguard Worker else: 185*bb4ee6a4SAndroid Build Coastguard Worker cid = container_id(docker, cros) 186*bb4ee6a4SAndroid Build Coastguard Worker print(f"Using existing container ({cid}).") 187*bb4ee6a4SAndroid Build Coastguard Worker return cid 188*bb4ee6a4SAndroid Build Coastguard Worker 189*bb4ee6a4SAndroid Build Coastguard Worker 190*bb4ee6a4SAndroid Build Coastguard Workerdef validate_podman(podman: cmd): 191*bb4ee6a4SAndroid Build Coastguard Worker graph_driver_name = podman("info --format={{.Store.GraphDriverName}}").stdout() 192*bb4ee6a4SAndroid Build Coastguard Worker config_file_name = podman("info --format={{.Store.ConfigFile}}").stdout() 193*bb4ee6a4SAndroid Build Coastguard Worker if graph_driver_name == "vfs": 194*bb4ee6a4SAndroid Build Coastguard Worker print("You are using vfs as a storage driver. This will be extremely slow.") 195*bb4ee6a4SAndroid Build Coastguard Worker print("Using the overlay driver is strongly recommended.") 196*bb4ee6a4SAndroid Build Coastguard Worker print("Note: This will delete all existing podman images and containers.") 197*bb4ee6a4SAndroid Build Coastguard Worker if confirm(f"Do you want me to update your config in {config_file_name}?"): 198*bb4ee6a4SAndroid Build Coastguard Worker podman("system reset -f").fg() 199*bb4ee6a4SAndroid Build Coastguard Worker with open(config_file_name, "a") as config_file: 200*bb4ee6a4SAndroid Build Coastguard Worker print("[storage]", file=config_file) 201*bb4ee6a4SAndroid Build Coastguard Worker print('driver = "overlay"', file=config_file) 202*bb4ee6a4SAndroid Build Coastguard Worker 203*bb4ee6a4SAndroid Build Coastguard Worker if os.name == "posix": 204*bb4ee6a4SAndroid Build Coastguard Worker username = os.environ["USER"] 205*bb4ee6a4SAndroid Build Coastguard Worker subuids = Path("/etc/subuid").read_text() 206*bb4ee6a4SAndroid Build Coastguard Worker if not username in subuids: 207*bb4ee6a4SAndroid Build Coastguard Worker print("Rootless podman requires subuid's to be set up for your user.") 208*bb4ee6a4SAndroid Build Coastguard Worker usermod = cmd( 209*bb4ee6a4SAndroid Build Coastguard Worker "sudo usermod --add-subuids 900000-965535 --add-subgids 900000-965535", username 210*bb4ee6a4SAndroid Build Coastguard Worker ) 211*bb4ee6a4SAndroid Build Coastguard Worker print("I can fix that by running:", usermod) 212*bb4ee6a4SAndroid Build Coastguard Worker if confirm("Ok?"): 213*bb4ee6a4SAndroid Build Coastguard Worker usermod.fg() 214*bb4ee6a4SAndroid Build Coastguard Worker podman("system migrate").fg() 215*bb4ee6a4SAndroid Build Coastguard Worker 216*bb4ee6a4SAndroid Build Coastguard Worker 217*bb4ee6a4SAndroid Build Coastguard Workerdef main(argv: List[str]): 218*bb4ee6a4SAndroid Build Coastguard Worker parser = argparse.ArgumentParser() 219*bb4ee6a4SAndroid Build Coastguard Worker add_common_args(parser) 220*bb4ee6a4SAndroid Build Coastguard Worker parser.add_argument("--stop", action="store_true") 221*bb4ee6a4SAndroid Build Coastguard Worker parser.add_argument("--clean", action="store_true") 222*bb4ee6a4SAndroid Build Coastguard Worker parser.add_argument("--hermetic", action="store_true") 223*bb4ee6a4SAndroid Build Coastguard Worker parser.add_argument("--no-interactive", action="store_true") 224*bb4ee6a4SAndroid Build Coastguard Worker parser.add_argument("--use-docker", action="store_true") 225*bb4ee6a4SAndroid Build Coastguard Worker parser.add_argument("--self-test", action="store_true") 226*bb4ee6a4SAndroid Build Coastguard Worker parser.add_argument("--pull", action="store_true") 227*bb4ee6a4SAndroid Build Coastguard Worker parser.add_argument("--cros", action="store_true") 228*bb4ee6a4SAndroid Build Coastguard Worker parser.add_argument("command", nargs=argparse.REMAINDER) 229*bb4ee6a4SAndroid Build Coastguard Worker 230*bb4ee6a4SAndroid Build Coastguard Worker args = parser.parse_args(argv) 231*bb4ee6a4SAndroid Build Coastguard Worker 232*bb4ee6a4SAndroid Build Coastguard Worker chdir(CROSVM_ROOT) 233*bb4ee6a4SAndroid Build Coastguard Worker 234*bb4ee6a4SAndroid Build Coastguard Worker if CACHE_DIR: 235*bb4ee6a4SAndroid Build Coastguard Worker Path(CACHE_DIR).mkdir(exist_ok=True) 236*bb4ee6a4SAndroid Build Coastguard Worker 237*bb4ee6a4SAndroid Build Coastguard Worker has_docker = shutil.which("docker") != None 238*bb4ee6a4SAndroid Build Coastguard Worker has_podman = shutil.which("podman") != None 239*bb4ee6a4SAndroid Build Coastguard Worker if not has_podman and not has_docker: 240*bb4ee6a4SAndroid Build Coastguard Worker raise Exception("Please install podman (or docker) to use the dev container.") 241*bb4ee6a4SAndroid Build Coastguard Worker 242*bb4ee6a4SAndroid Build Coastguard Worker use_docker = args.use_docker 243*bb4ee6a4SAndroid Build Coastguard Worker if has_docker and not has_podman: 244*bb4ee6a4SAndroid Build Coastguard Worker use_docker = True 245*bb4ee6a4SAndroid Build Coastguard Worker 246*bb4ee6a4SAndroid Build Coastguard Worker # cros container only works in docker 247*bb4ee6a4SAndroid Build Coastguard Worker if args.cros: 248*bb4ee6a4SAndroid Build Coastguard Worker use_docker = True 249*bb4ee6a4SAndroid Build Coastguard Worker 250*bb4ee6a4SAndroid Build Coastguard Worker if use_docker: 251*bb4ee6a4SAndroid Build Coastguard Worker print( 252*bb4ee6a4SAndroid Build Coastguard Worker "WARNING: Running dev_container with docker may cause root-owned files to be created." 253*bb4ee6a4SAndroid Build Coastguard Worker ) 254*bb4ee6a4SAndroid Build Coastguard Worker print("Use podman to prevent this.") 255*bb4ee6a4SAndroid Build Coastguard Worker print() 256*bb4ee6a4SAndroid Build Coastguard Worker docker = cmd("docker") 257*bb4ee6a4SAndroid Build Coastguard Worker docker_args = [ 258*bb4ee6a4SAndroid Build Coastguard Worker *DOCKER_ARGS, 259*bb4ee6a4SAndroid Build Coastguard Worker *workspace_mount_args(args.cros), 260*bb4ee6a4SAndroid Build Coastguard Worker ] 261*bb4ee6a4SAndroid Build Coastguard Worker else: 262*bb4ee6a4SAndroid Build Coastguard Worker docker = cmd("podman") 263*bb4ee6a4SAndroid Build Coastguard Worker 264*bb4ee6a4SAndroid Build Coastguard Worker # On windows, podman uses wsl vm. start the default podman vm for the rest of the script 265*bb4ee6a4SAndroid Build Coastguard Worker # to work properly. 266*bb4ee6a4SAndroid Build Coastguard Worker if os.name == "nt" and not machine_is_running(docker): 267*bb4ee6a4SAndroid Build Coastguard Worker print("Starting podman default machine.") 268*bb4ee6a4SAndroid Build Coastguard Worker docker("machine start").fg(quiet=True) 269*bb4ee6a4SAndroid Build Coastguard Worker docker_args = [ 270*bb4ee6a4SAndroid Build Coastguard Worker *PODMAN_ARGS, 271*bb4ee6a4SAndroid Build Coastguard Worker *workspace_mount_args(args.cros), 272*bb4ee6a4SAndroid Build Coastguard Worker ] 273*bb4ee6a4SAndroid Build Coastguard Worker validate_podman(docker) 274*bb4ee6a4SAndroid Build Coastguard Worker 275*bb4ee6a4SAndroid Build Coastguard Worker if args.cros: 276*bb4ee6a4SAndroid Build Coastguard Worker docker_args.append("--privileged") # cros container requires privileged container 277*bb4ee6a4SAndroid Build Coastguard Worker docker_args.append(CROS_IMAGE_NAME) 278*bb4ee6a4SAndroid Build Coastguard Worker else: 279*bb4ee6a4SAndroid Build Coastguard Worker docker_args.append(DEV_IMAGE_NAME + ":" + DEV_IMAGE_VERSION) 280*bb4ee6a4SAndroid Build Coastguard Worker 281*bb4ee6a4SAndroid Build Coastguard Worker # Add environment variables to command line 282*bb4ee6a4SAndroid Build Coastguard Worker exec_args: List[str] = [] 283*bb4ee6a4SAndroid Build Coastguard Worker for key in ENV_PASSTHROUGH: 284*bb4ee6a4SAndroid Build Coastguard Worker value = os.environ.get(key) 285*bb4ee6a4SAndroid Build Coastguard Worker if value is not None: 286*bb4ee6a4SAndroid Build Coastguard Worker exec_args.append("--env") 287*bb4ee6a4SAndroid Build Coastguard Worker exec_args.append(f"{key}={quoted(value)}") 288*bb4ee6a4SAndroid Build Coastguard Worker 289*bb4ee6a4SAndroid Build Coastguard Worker if args.self_test: 290*bb4ee6a4SAndroid Build Coastguard Worker TestDevContainer.docker = docker 291*bb4ee6a4SAndroid Build Coastguard Worker suite = unittest.defaultTestLoader.loadTestsFromTestCase(TestDevContainer) 292*bb4ee6a4SAndroid Build Coastguard Worker unittest.TextTestRunner().run(suite) 293*bb4ee6a4SAndroid Build Coastguard Worker return 294*bb4ee6a4SAndroid Build Coastguard Worker 295*bb4ee6a4SAndroid Build Coastguard Worker if args.stop: 296*bb4ee6a4SAndroid Build Coastguard Worker if not delete_container(docker, args.cros): 297*bb4ee6a4SAndroid Build Coastguard Worker print(f"container is not running.") 298*bb4ee6a4SAndroid Build Coastguard Worker return 299*bb4ee6a4SAndroid Build Coastguard Worker 300*bb4ee6a4SAndroid Build Coastguard Worker if args.clean: 301*bb4ee6a4SAndroid Build Coastguard Worker delete_container(docker, args.cros) 302*bb4ee6a4SAndroid Build Coastguard Worker 303*bb4ee6a4SAndroid Build Coastguard Worker if args.pull: 304*bb4ee6a4SAndroid Build Coastguard Worker if args.cros: 305*bb4ee6a4SAndroid Build Coastguard Worker docker("pull", CROS_IMAGE_NAME).fg() 306*bb4ee6a4SAndroid Build Coastguard Worker else: 307*bb4ee6a4SAndroid Build Coastguard Worker docker("pull", f"gcr.io/crosvm-infra/crosvm_dev:{DEV_IMAGE_VERSION}").fg() 308*bb4ee6a4SAndroid Build Coastguard Worker return 309*bb4ee6a4SAndroid Build Coastguard Worker 310*bb4ee6a4SAndroid Build Coastguard Worker command = args.command 311*bb4ee6a4SAndroid Build Coastguard Worker 312*bb4ee6a4SAndroid Build Coastguard Worker # Default to interactive mode if a tty is present. 313*bb4ee6a4SAndroid Build Coastguard Worker tty_args: List[str] = [] 314*bb4ee6a4SAndroid Build Coastguard Worker if sys.stdin.isatty(): 315*bb4ee6a4SAndroid Build Coastguard Worker tty_args += ["--tty"] 316*bb4ee6a4SAndroid Build Coastguard Worker if not args.no_interactive: 317*bb4ee6a4SAndroid Build Coastguard Worker tty_args += ["--interactive"] 318*bb4ee6a4SAndroid Build Coastguard Worker 319*bb4ee6a4SAndroid Build Coastguard Worker # Start an interactive shell by default 320*bb4ee6a4SAndroid Build Coastguard Worker if args.hermetic: 321*bb4ee6a4SAndroid Build Coastguard Worker # cmd is passed to entrypoint 322*bb4ee6a4SAndroid Build Coastguard Worker quoted_cmd = list(map(quoted, command)) 323*bb4ee6a4SAndroid Build Coastguard Worker docker(f"run --rm", *tty_args, *docker_args, *exec_args, *quoted_cmd).fg() 324*bb4ee6a4SAndroid Build Coastguard Worker else: 325*bb4ee6a4SAndroid Build Coastguard Worker # cmd is executed directly 326*bb4ee6a4SAndroid Build Coastguard Worker cid = ensure_container_is_alive(docker, docker_args, args.cros) 327*bb4ee6a4SAndroid Build Coastguard Worker if not command: 328*bb4ee6a4SAndroid Build Coastguard Worker command = ("/bin/bash",) 329*bb4ee6a4SAndroid Build Coastguard Worker quoted_cmd = list(map(quoted, command)) 330*bb4ee6a4SAndroid Build Coastguard Worker docker("exec", *tty_args, *exec_args, cid, *quoted_cmd).fg() 331*bb4ee6a4SAndroid Build Coastguard Worker 332*bb4ee6a4SAndroid Build Coastguard Worker 333*bb4ee6a4SAndroid Build Coastguard Workerclass TestDevContainer(unittest.TestCase): 334*bb4ee6a4SAndroid Build Coastguard Worker """ 335*bb4ee6a4SAndroid Build Coastguard Worker Runs live tests using the docker service. 336*bb4ee6a4SAndroid Build Coastguard Worker 337*bb4ee6a4SAndroid Build Coastguard Worker Note: This test is not run by health-check since it cannot be run inside the 338*bb4ee6a4SAndroid Build Coastguard Worker container. It is run by infra/recipes/health_check.py before running health checks. 339*bb4ee6a4SAndroid Build Coastguard Worker """ 340*bb4ee6a4SAndroid Build Coastguard Worker 341*bb4ee6a4SAndroid Build Coastguard Worker docker: cmd 342*bb4ee6a4SAndroid Build Coastguard Worker docker_args = [ 343*bb4ee6a4SAndroid Build Coastguard Worker *workspace_mount_args(cros=False), 344*bb4ee6a4SAndroid Build Coastguard Worker *DOCKER_ARGS, 345*bb4ee6a4SAndroid Build Coastguard Worker ] 346*bb4ee6a4SAndroid Build Coastguard Worker 347*bb4ee6a4SAndroid Build Coastguard Worker def setUp(self): 348*bb4ee6a4SAndroid Build Coastguard Worker # Start with a stopped container for each test. 349*bb4ee6a4SAndroid Build Coastguard Worker delete_container(self.docker, cros=False) 350*bb4ee6a4SAndroid Build Coastguard Worker 351*bb4ee6a4SAndroid Build Coastguard Worker def test_stopped_container(self): 352*bb4ee6a4SAndroid Build Coastguard Worker # Create but do not run a new container. 353*bb4ee6a4SAndroid Build Coastguard Worker self.docker( 354*bb4ee6a4SAndroid Build Coastguard Worker f"create --name {DEV_CONTAINER_NAME}", *self.docker_args, "sleep infinity" 355*bb4ee6a4SAndroid Build Coastguard Worker ).stdout() 356*bb4ee6a4SAndroid Build Coastguard Worker self.assertTrue(container_id(self.docker, cros=False)) 357*bb4ee6a4SAndroid Build Coastguard Worker self.assertFalse(container_is_running(self.docker, cros=False)) 358*bb4ee6a4SAndroid Build Coastguard Worker 359*bb4ee6a4SAndroid Build Coastguard Worker def test_container_reuse(self): 360*bb4ee6a4SAndroid Build Coastguard Worker cid = ensure_container_is_alive(self.docker, self.docker_args, cros=False) 361*bb4ee6a4SAndroid Build Coastguard Worker cid2 = ensure_container_is_alive(self.docker, self.docker_args, cros=False) 362*bb4ee6a4SAndroid Build Coastguard Worker self.assertEqual(cid, cid2) 363*bb4ee6a4SAndroid Build Coastguard Worker 364*bb4ee6a4SAndroid Build Coastguard Worker def test_handling_of_stopped_container(self): 365*bb4ee6a4SAndroid Build Coastguard Worker cid = ensure_container_is_alive(self.docker, self.docker_args, cros=False) 366*bb4ee6a4SAndroid Build Coastguard Worker self.docker("kill", cid).fg() 367*bb4ee6a4SAndroid Build Coastguard Worker 368*bb4ee6a4SAndroid Build Coastguard Worker # Make sure we can get back into a good state and execute commands. 369*bb4ee6a4SAndroid Build Coastguard Worker ensure_container_is_alive(self.docker, self.docker_args, cros=False) 370*bb4ee6a4SAndroid Build Coastguard Worker self.assertTrue(container_is_running(self.docker, cros=False)) 371*bb4ee6a4SAndroid Build Coastguard Worker main(["true"]) 372*bb4ee6a4SAndroid Build Coastguard Worker 373*bb4ee6a4SAndroid Build Coastguard Worker 374*bb4ee6a4SAndroid Build Coastguard Workerif __name__ == "__main__": 375*bb4ee6a4SAndroid Build Coastguard Worker main(sys.argv[1:]) 376