summaryrefslogtreecommitdiff
path: root/tests/integration_tests/bugs/test_lp1835584.py
blob: 4e732446eed35548a05464a544ffb71084fb667c (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
""" Integration test for LP #1835584

Upstream linux kernels prior to 4.15 provide DMI product_uuid in uppercase.
More recent kernels switched to lowercase for DMI product_uuid. Azure
datasource uses this product_uuid as the instance-id for cloud-init.

The linux-azure-fips kernel installed in PRO FIPs images, that product UUID is
uppercase whereas the linux-azure cloud-optimized kernel reports the UUID as
lowercase.

In cases where product_uuid changes case, ensure cloud-init doesn't
recreate ssh hostkeys across reboot (due to detecting an instance_id change).

This currently only affects linux-azure-fips -> linux-azure on Bionic.

The test will launch a specific Bionic Ubuntu PRO FIPS image which has a
linux-azure-fips kernel known to report product_uuid as uppercase. Then upgrade
and reboot into linux-azure kernel which is known to report product_uuid as
lowercase.

Across the reboot, assert that we didn't re-run config_ssh by virtue of
seeing only one semaphore creation log entry of type:

 Writing to /var/lib/cloud/instances/<UUID>/sem/config_ssh -

https://bugs.launchpad.net/cloud-init/+bug/1835584
"""
import re

import pytest
from pycloudlib.cloud import ImageType

from tests.integration_tests.clouds import ImageSpecification, IntegrationCloud
from tests.integration_tests.conftest import get_validated_source
from tests.integration_tests.instances import IntegrationInstance


def _check_iid_insensitive_across_kernel_upgrade(
    instance: IntegrationInstance,
):
    uuid = instance.read_from_file("/sys/class/dmi/id/product_uuid")
    assert (
        uuid.isupper()
    ), "Expected uppercase UUID on Ubuntu FIPS image {}".format(uuid)
    orig_kernel = instance.execute("uname -r").strip()
    assert "azure-fips" in orig_kernel
    result = instance.execute("apt-get update")
    # Install a 5.4+ kernel which provides lowercase product_uuid
    result = instance.execute("apt-get install linux-azure --assume-yes")
    if not result.ok:
        pytest.fail("Unable to install linux-azure kernel: {}".format(result))
    # Remove ubuntu-azure-fips metapkg which mandates FIPS-flavour kernel
    result = instance.execute("ua disable fips --assume-yes")
    assert result.ok, "Unable to disable fips: {}".format(result)
    instance.restart()
    new_kernel = instance.execute("uname -r").strip()
    assert orig_kernel != new_kernel
    assert "azure-fips" not in new_kernel
    assert "azure" in new_kernel
    new_uuid = instance.read_from_file("/sys/class/dmi/id/product_uuid")
    assert (
        uuid.lower() == new_uuid
    ), "Expected UUID on linux-azure to be lowercase of FIPS: {}".format(uuid)
    log = instance.read_from_file("/var/log/cloud-init.log")
    RE_CONFIG_SSH_SEMAPHORE = r"Writing.*sem/config_ssh "
    ssh_runs = len(re.findall(RE_CONFIG_SSH_SEMAPHORE, log))
    assert 1 == ssh_runs, "config_ssh ran too many times {}".format(ssh_runs)


@pytest.mark.azure
@pytest.mark.integration_cloud_args(image_type=ImageType.PRO_FIPS)
def test_azure_kernel_upgrade_case_insensitive_uuid(
    session_cloud: IntegrationCloud,
):
    cfg_image_spec = ImageSpecification.from_os_image()
    if (cfg_image_spec.os, cfg_image_spec.release) != ("ubuntu", "bionic"):
        pytest.skip(
            "Test only supports ubuntu:bionic not {0.os}:{0.release}".format(
                cfg_image_spec
            )
        )
    source = get_validated_source(session_cloud)
    if not source.installs_new_version():
        pytest.skip(
            "Provide CLOUD_INIT_SOURCE to install expected working cloud-init"
        )
    with session_cloud.launch(
        launch_kwargs={
            "image_id": session_cloud.cloud_instance.daily_image(
                cfg_image_spec.image_id, image_type=ImageType.PRO_FIPS
            )
        }
    ) as instance:
        # We can't use setup_image fixture here because we want to avoid
        # taking a snapshot or cleaning the booted machine after cloud-init
        # upgrade.
        instance.install_new_cloud_init(
            source, take_snapshot=False, clean=False
        )
        _check_iid_insensitive_across_kernel_upgrade(instance)