# Copyright 2018 Amazon.com, Inc. or its affiliates. All Rights Reserved.
# SPDX-License-Identifier: Apache-2.0
"""Test that the process startup time up to socket bind is within spec."""

# pylint: disable=redefined-outer-name

import json
import os
import time

import pytest

import host_tools.logging as log_tools
from framework.properties import global_props
from host_tools.cargo_build import run_seccompiler_bin


@pytest.fixture
def startup_time(metrics, record_property):
    """Fixture to capture the startup time"""
    metrics.set_dimensions(
        {
            "instance": global_props.instance,
            "cpu_model": global_props.cpu_model,
            "host_kernel": "linux-" + global_props.host_linux_version,
        }
    )

    def record_startup_time(startup_time):
        metrics.put_metric("startup_time", startup_time, unit="Microseconds")
        record_property("startup_time_μs", startup_time)

    return record_startup_time


def test_startup_time_new_pid_ns(test_microvm_with_api, startup_time):
    """
    Check startup time when jailer is spawned in a new PID namespace.
    """
    microvm = test_microvm_with_api
    microvm.bin_cloner_path = None
    microvm.jailer.new_pid_ns = True
    startup_time(_test_startup_time(microvm))


def test_startup_time_daemonize(test_microvm_with_api, startup_time):
    """
    Check startup time when jailer detaches Firecracker from the controlling terminal.
    """
    microvm = test_microvm_with_api
    startup_time(_test_startup_time(microvm))


def test_startup_time_custom_seccomp(test_microvm_with_api, startup_time):
    """
    Check the startup time when using custom seccomp filters.
    """
    microvm = test_microvm_with_api
    _custom_filter_setup(microvm)
    startup_time(_test_startup_time(microvm))


def _test_startup_time(microvm):
    microvm.spawn()

    microvm.basic_config(vcpu_count=2, mem_size_mib=1024)

    # Configure metrics.
    metrics_fifo_path = os.path.join(microvm.path, "metrics_fifo")
    metrics_fifo = log_tools.Fifo(metrics_fifo_path)

    response = microvm.metrics.put(
        metrics_path=microvm.create_jailed_resource(metrics_fifo.path)
    )
    assert microvm.api_session.is_status_no_content(response.status_code)

    microvm.start()
    time.sleep(0.4)

    # The metrics fifo should be at index 1.
    # Since metrics are flushed at InstanceStart, the first line will suffice.
    lines = metrics_fifo.sequential_reader(1)
    metrics = json.loads(lines[0])
    startup_time_us = metrics["api_server"]["process_startup_time_us"]
    cpu_startup_time_us = metrics["api_server"]["process_startup_time_cpu_us"]

    print(
        "Process startup time is: {} us ({} CPU us)".format(
            startup_time_us, cpu_startup_time_us
        )
    )

    assert cpu_startup_time_us > 0
    return cpu_startup_time_us


def _custom_filter_setup(test_microvm):
    bpf_path = os.path.join(test_microvm.path, "bpf.out")

    run_seccompiler_bin(bpf_path)

    test_microvm.create_jailed_resource(bpf_path)
    test_microvm.jailer.extra_args.update({"seccomp-filter": "bpf.out"})