Path: blob/main/tests/integration_tests/functional/test_signals.py
1958 views
# Copyright 2019 Amazon.com, Inc. or its affiliates. All Rights Reserved.1# SPDX-License-Identifier: Apache-2.02"""Tests scenarios for Firecracker signal handling."""34import json5import os6from signal import \7(SIGBUS, SIGSEGV, SIGXFSZ,8SIGXCPU, SIGPIPE, SIGHUP, SIGILL, SIGSYS)9from time import sleep10import resource as res11import pytest1213import host_tools.network as net_tools14import framework.utils as utils1516signum_str = {17SIGBUS: "sigbus",18SIGSEGV: "sigsegv",19SIGXFSZ: "sigxfsz",20SIGXCPU: "sigxcpu",21SIGPIPE: "sigpipe",22SIGHUP: "sighup",23SIGILL: "sigill",24SIGSYS: "sigsys"25}262728@pytest.mark.parametrize(29"signum",30[SIGBUS, SIGSEGV, SIGXFSZ, SIGXCPU, SIGPIPE, SIGHUP, SIGILL, SIGSYS]31)32def test_generic_signal_handler(test_microvm_with_api, signum):33"""Test signal handling for all handled signals."""34microvm = test_microvm_with_api35microvm.spawn()3637# We don't need to monitor the memory for this test.38microvm.memory_monitor = None3940microvm.basic_config()4142# Configure metrics based on a file.43metrics_path = os.path.join(microvm.path, 'metrics_fifo')44utils.run_cmd("touch {}".format(metrics_path))45response = microvm.metrics.put(46metrics_path=microvm.create_jailed_resource(metrics_path)47)48assert microvm.api_session.is_status_no_content(response.status_code)4950microvm.start()51firecracker_pid = int(microvm.jailer_clone_pid)52sleep(0.5)5354metrics_jail_path = os.path.join(microvm.chroot(), metrics_path)55metrics_fd = open(metrics_jail_path)5657line_metrics = metrics_fd.readlines()58assert len(line_metrics) == 15960os.kill(firecracker_pid, signum)61# Firecracker gracefully handles SIGPIPE (doesn't terminate).62if signum == int(SIGPIPE):63msg = 'Received signal 13'64# Flush metrics to file, so we can see the SIGPIPE at bottom assert.65# This is going to fail if process has exited.66response = microvm.actions.put(action_type='FlushMetrics')67assert microvm.api_session.is_status_no_content(response.status_code)68else:69microvm.expect_kill_by_signal = True70# Ensure that the process was terminated.71utils.wait_process_termination(firecracker_pid)72msg = 'Shutting down VM after intercepting signal {}'.format(signum)7374microvm.check_log_message(msg)7576if signum != SIGSYS:77metric_line = json.loads(metrics_fd.readlines()[0])78assert metric_line["signals"][signum_str[signum]] == 1798081def test_sigxfsz_handler(test_microvm_with_api):82"""Test intercepting and handling SIGXFSZ."""83microvm = test_microvm_with_api84microvm.spawn()8586# We don't need to monitor the memory for this test.87microvm.memory_monitor = None8889microvm.basic_config()9091# Configure metrics based on a file.92metrics_path = os.path.join(microvm.path, 'metrics_fifo')93utils.run_cmd("touch {}".format(metrics_path))94response = microvm.metrics.put(95metrics_path=microvm.create_jailed_resource(metrics_path)96)97assert microvm.api_session.is_status_no_content(response.status_code)9899microvm.start()100101metrics_jail_path = os.path.join(microvm.jailer.chroot_path(),102metrics_path)103metrics_fd = open(metrics_jail_path)104line_metrics = metrics_fd.readlines()105assert len(line_metrics) == 1106107firecracker_pid = int(microvm.jailer_clone_pid)108size = os.path.getsize(metrics_jail_path)109# The SIGXFSZ is triggered because the size of rootfs is bigger than110# the size of metrics file times 3. Since the metrics file is flushed111# twice we have to make sure that the limit is bigger than that112# in order to make sure the SIGXFSZ metric is logged113res.prlimit(firecracker_pid, res.RLIMIT_FSIZE, (size*3, res.RLIM_INFINITY))114115while True:116try:117utils.run_cmd("ps -p {}".format(firecracker_pid))118sleep(1)119except ChildProcessError:120break121122microvm.expect_kill_by_signal = True123msg = 'Shutting down VM after intercepting signal 25, code 0'124microvm.check_log_message(msg)125metric_line = json.loads(metrics_fd.readlines()[0])126assert metric_line["signals"]["sigxfsz"] == 1127128129def test_handled_signals(test_microvm_with_ssh, network_config):130"""Test that handled signals don't kill the microVM."""131microvm = test_microvm_with_ssh132microvm.spawn()133134# We don't need to monitor the memory for this test.135microvm.memory_monitor = None136137microvm.basic_config(vcpu_count=2)138139# Configure a network interface.140_tap, _, _ = microvm.ssh_network_config(network_config, '1')141142microvm.start()143firecracker_pid = int(microvm.jailer_clone_pid)144145# Open a SSH connection to validate the microVM stays alive.146ssh_connection = net_tools.SSHConnection(microvm.ssh_config)147# Just validate a simple command: `nproc`148cmd = "nproc"149_, stdout, stderr = ssh_connection.execute_command(cmd)150assert stderr.read() == ""151assert int(stdout.read()) == 2152153# We have a handler installed for this signal.154# The 35 is the SIGRTMIN for musl libc.155# We hardcode this value since the SIGRTMIN python reports156# is 34, which is likely the one for glibc.157os.kill(firecracker_pid, 35)158159# Validate the microVM is still up and running.160_, stdout, stderr = ssh_connection.execute_command(cmd)161assert stderr.read() == ""162assert int(stdout.read()) == 2163164165