import os
import pathlib
import pytest
import subprocess


def expected_sysctl_defaults():
    """
    Inspect sysctl.d snippets shipped by procps to gather expected settings.
    """
    expected = []

    arch = subprocess.check_output(
        ['dpkg', '--print-architecture']
    ).decode().strip()

    paths = [
        f.strip() for f in subprocess.check_output(
            ['dpkg', '-L', 'procps']
        ).decode().splitlines()
    ]

    for path in paths:
        p = pathlib.Path(path)

        if not p.is_file():
            continue

        if not p.match('*/sysctl.d/*'):
            continue

        if p.suffix not in ('.conf', f'.{arch}'):
            # arch-specific config, not for this arch
            continue

        with p.open() as conf:
            lines = [line.strip() for line in conf.readlines()]

        for line in lines:
            if not line or line.startswith('#'):
                continue

            (key, _, value) = line.partition('=')

            expected.append((key.strip().lstrip('-'), value.strip()))

    return expected


@pytest.mark.parametrize("key, expected", expected_sysctl_defaults())
def test_sysctl_defaults(key, expected):
    # If the sysctl key does not exist, skip the test.
    if not os.path.exists(f'/proc/sys/{key}'.replace('.', '/')):
        pytest.skip(f'{key} does not exist on this system, ignoring')

    actual = subprocess.check_output(['sysctl', '-n', key])
    actual = actual.decode().strip()
    actual = ' '.join(actual.split())

    # Due to LP: #1068756, cloud images ship /etc/sysctl.d/cloudimg-ipv6.conf
    # which overrides procps's defaults for net.ipv6.conf.{all,default}.use_tempaddr.
    #
    # If that override exists, expect a failure for those settings.
    if (
        os.path.exists('/etc/sysctl.d/99-cloudimg-ipv6.conf') and
        key in (
            'net.ipv6.conf.all.use_tempaddr',
            'net.ipv6.conf.default.use_tempaddr',
        )
    ):
        pytest.xfail(
            f'/etc/systctl.d/99-cloudimg-ipv6.conf overrides {key}'
        )

    assert actual == expected
