Exemple #1
0
 def test__get_imds_v2_token_headers_none_on_404(self, readurl):
     """A 404 on private AWS regions indicates lack IMDSv2 support."""
     readurl.side_effect = HTTPError("http://me", 404, "No IMDSv2 support",
                                     None, BytesIO())
     instance = UAAutoAttachAWSInstance()
     assert None is instance._get_imds_v2_token_headers(
         ip_address=IMDS_IPV4_ADDRESS)
     assert "IMDSv1" == instance._api_token
     # No retries on 404. It is a permanent indication of no IMDSv2 support.
     instance._get_imds_v2_token_headers(ip_address=IMDS_IPV4_ADDRESS)
     assert 1 == readurl.call_count
    def test_retry_backoff_on_failed_identity_doc(
        self, readurl, sleep, fail_count, exception, caplog_text
    ):
        """Retry backoff is attempted before failing to get AWS.identity_doc"""

        def fake_someurlerrors(url):
            if readurl.call_count <= fail_count:
                raise HTTPError(
                    "http://me",
                    700 + readurl.call_count,
                    "funky error msg",
                    None,
                    BytesIO(),
                )
            return "pkcs7WOOT!==", {"header": "stuff"}

        readurl.side_effect = fake_someurlerrors
        instance = UAAutoAttachAWSInstance()
        if exception:
            with pytest.raises(HTTPError) as excinfo:
                instance.identity_doc
            assert 704 == excinfo.value.code
        else:
            assert "pkcs7WOOT!==" == instance.identity_doc

        expected_sleep_calls = [mock.call(1), mock.call(2), mock.call(5)]
        assert expected_sleep_calls == sleep.call_args_list
        expected_logs = [
            "HTTP Error 701: funky error msg Retrying 3 more times.",
            "HTTP Error 702: funky error msg Retrying 2 more times.",
            "HTTP Error 703: funky error msg Retrying 1 more times.",
        ]
        logs = caplog_text()
        for log in expected_logs:
            assert log in logs
 def test_identity_doc_from_aws_url_pkcs7(self, readurl):
     """Return pkcs7 content from IMDS as AWS' identity doc"""
     readurl.return_value = "pkcs7WOOT!==", {"header": "stuff"}
     instance = UAAutoAttachAWSInstance()
     assert "pkcs7WOOT!==" == instance.identity_doc
     url = "http://169.254.169.254/latest/dynamic/instance-identity/pkcs7"
     assert [mock.call(url)] == readurl.call_args_list
Exemple #4
0
 def test__get_imds_v2_token_headers_caches_response(self, readurl):
     """Return API token headers for IMDSv2 access. Response is cached."""
     instance = UAAutoAttachAWSInstance()
     url = "http://169.254.169.254/latest/api/token"
     readurl.return_value = "somebase64token==", {"header": "stuff"}
     assert {
         AWS_TOKEN_PUT_HEADER: "somebase64token=="
     } == instance._get_imds_v2_token_headers(ip_address=IMDS_IPV4_ADDRESS)
     instance._get_imds_v2_token_headers(ip_address=IMDS_IPV4_ADDRESS)
     assert "somebase64token==" == instance._api_token
     assert [
         mock.call(
             url,
             method="PUT",
             headers={AWS_TOKEN_REQ_HEADER: AWS_TOKEN_TTL_SECONDS},
             timeout=1,
         )
     ] == readurl.call_args_list
Exemple #5
0
    def test_retry_backoff_on__get_imds_v2_token_headers_caches_response(
            self, readurl, sleep, fail_count, exception, caplog_text):
        """Retry backoff before failing _get_imds_v2_token_headers."""
        def fake_someurlerrors(url, method=None, headers=None, timeout=1):
            if readurl.call_count <= fail_count:
                raise HTTPError(
                    "http://me",
                    700 + readurl.call_count,
                    "funky error msg",
                    None,
                    BytesIO(),
                )
            return "base64token==", {"header": "stuff"}

        readurl.side_effect = fake_someurlerrors
        instance = UAAutoAttachAWSInstance()
        if exception:
            with pytest.raises(HTTPError) as excinfo:
                instance._get_imds_v2_token_headers(
                    ip_address=IMDS_IPV4_ADDRESS)
            assert 704 == excinfo.value.code
        else:
            assert {
                AWS_TOKEN_PUT_HEADER: "base64token=="
            } == instance._get_imds_v2_token_headers(
                ip_address=IMDS_IPV4_ADDRESS)

        expected_sleep_calls = [mock.call(1), mock.call(2), mock.call(5)]
        assert expected_sleep_calls == sleep.call_args_list
        expected_logs = [
            "HTTP Error 701: funky error msg Retrying 3 more times.",
            "HTTP Error 702: funky error msg Retrying 2 more times.",
            "HTTP Error 703: funky error msg Retrying 1 more times.",
        ]
        logs = caplog_text()
        for log in expected_logs:
            assert log in logs
    def test_is_viable_based_on_sys_product_serial_and_uuid(
            self, load_file, hypervisor_uuid, prod_uuid, prod_serial, viable):
        """Platform is viable when product serial and uuid start with ec2"""
        def fake_load_file(f_name):
            if f_name == "/sys/hypervisor/uuid":
                if hypervisor_uuid is not None:
                    return hypervisor_uuid
                raise FileNotFoundError()
            if f_name == "/sys/class/dmi/id/product_uuid":
                return prod_uuid
            if f_name == "/sys/class/dmi/id/product_serial":
                return prod_serial
            raise AssertionError("Invalid load_file of {}".format(f_name))

        load_file.side_effect = fake_load_file
        instance = UAAutoAttachAWSInstance()
        assert viable is instance.is_viable
Exemple #7
0
 def test_identity_doc_from_aws_url_pkcs7(self, readurl):
     """Return pkcs7 content from IMDS as AWS' identity doc"""
     readurl.return_value = "pkcs7WOOT!==", {"header": "stuff"}
     instance = UAAutoAttachAWSInstance()
     assert {"pkcs7": "pkcs7WOOT!=="} == instance.identity_doc
     url = "http://169.254.169.254/latest/dynamic/instance-identity/pkcs7"
     token_url = "http://169.254.169.254/latest/api/token"
     assert [
         mock.call(
             token_url,
             method="PUT",
             headers={AWS_TOKEN_REQ_HEADER: AWS_TOKEN_TTL_SECONDS},
             timeout=1,
         ),
         mock.call(url,
                   headers={AWS_TOKEN_PUT_HEADER: "pkcs7WOOT!=="},
                   timeout=1),
     ] == readurl.call_args_list
Exemple #8
0
    def test_identity_doc_default_to_ipv6_if_ipv4_fail(self, readurl,
                                                       caplog_text):
        instance = UAAutoAttachAWSInstance()
        ipv4_address = IMDS_IPV4_ADDRESS
        ipv6_address = IMDS_IPV6_ADDRESS

        def fake_someurlerrors(url, method=None, headers=None, timeout=1):
            if ipv4_address in url:
                raise Exception("IPv4 exception")

            if url == IMDS_V2_TOKEN_URL.format(ipv6_address):
                return "base64token==", {"header": "stuff"}

            if url == IMDS_URL.format(ipv6_address):
                return "pkcs7WOOT!==", {"header": "stuff"}

        readurl.side_effect = fake_someurlerrors
        assert {"pkcs7": "pkcs7WOOT!=="} == instance.identity_doc
        expected = [
            mock.call(
                IMDS_V2_TOKEN_URL.format(ipv4_address),
                method="PUT",
                headers={AWS_TOKEN_REQ_HEADER: AWS_TOKEN_TTL_SECONDS},
                timeout=1,
            ),
            mock.call(
                IMDS_V2_TOKEN_URL.format(ipv6_address),
                method="PUT",
                headers={AWS_TOKEN_REQ_HEADER: AWS_TOKEN_TTL_SECONDS},
                timeout=1,
            ),
            mock.call(
                IMDS_URL.format(ipv6_address),
                headers={AWS_TOKEN_PUT_HEADER: "base64token=="},
                timeout=1,
            ),
        ]

        assert expected == readurl.call_args_list

        expected_log = "Could not reach AWS IMDS at http://169.254.169.254:"
        assert expected_log in caplog_text()
Exemple #9
0
    def test_identity_doc_logs_error_if_both_ipv4_and_ipv6_fails(
            self, readurl, caplog_text):

        instance = UAAutoAttachAWSInstance()
        ipv4_address = IMDS_IPV4_ADDRESS
        ipv6_address = IMDS_IPV6_ADDRESS

        readurl.side_effect = Exception("Exception")

        expected_error = ("No valid AWS IMDS endpoint discovered at "
                          "addresses: {}, {}".format(IMDS_IPV4_ADDRESS,
                                                     IMDS_IPV6_ADDRESS))
        with pytest.raises(exceptions.UserFacingError,
                           match=re.escape(expected_error)):
            instance.identity_doc

        expected = [
            mock.call(
                IMDS_V2_TOKEN_URL.format(ipv4_address),
                method="PUT",
                headers={AWS_TOKEN_REQ_HEADER: AWS_TOKEN_TTL_SECONDS},
                timeout=1,
            ),
            mock.call(
                IMDS_V2_TOKEN_URL.format(ipv6_address),
                method="PUT",
                headers={AWS_TOKEN_REQ_HEADER: AWS_TOKEN_TTL_SECONDS},
                timeout=1,
            ),
        ]
        assert expected == readurl.call_args_list

        expected_logs = [
            "Could not reach AWS IMDS at http://169.254.169.254:",
            "Could not reach AWS IMDS at http://[fd00:ec2::254]:",
        ]

        for expected_log in expected_logs:
            assert expected_log in caplog_text()
Exemple #10
0
class TestPollForProLicense:
    @pytest.mark.parametrize(
        "is_config_value_true,"
        "is_attached,"
        "is_current_series_lts,"
        "cloud_instance,"
        "should_poll,"
        "is_pro_license_present,"
        "cfg_poll_for_pro_licenses,"
        "expected_log_debug_calls,"
        "expected_is_pro_license_present_calls,"
        "expected_attempt_auto_attach_calls",
        [
            (
                True,
                None,
                None,
                None,
                None,
                None,
                None,
                [mock.call("Configured to not auto attach, shutting down")],
                [],
                [],
            ),
            (
                False,
                True,
                None,
                None,
                None,
                None,
                None,
                [mock.call("Already attached, shutting down")],
                [],
                [],
            ),
            (
                False,
                False,
                False,
                None,
                None,
                None,
                None,
                [mock.call("Not on LTS, shutting down")],
                [],
                [],
            ),
            (
                False,
                False,
                True,
                exceptions.CloudFactoryError("none"),
                None,
                None,
                None,
                [mock.call("Not on cloud, shutting down")],
                [],
                [],
            ),
            (
                False,
                False,
                True,
                UAAutoAttachAWSInstance(),
                None,
                None,
                None,
                [mock.call("Not on gcp, shutting down")],
                [],
                [],
            ),
            (
                False,
                False,
                True,
                UAAutoAttachGCPInstance(),
                False,
                None,
                None,
                [mock.call("Not on supported instance, shutting down")],
                [],
                [],
            ),
            (
                False,
                False,
                True,
                UAAutoAttachGCPInstance(),
                True,
                True,
                None,
                [],
                [mock.call(wait_for_change=False)],
                [mock.call(mock.ANY, mock.ANY)],
            ),
            (
                False,
                False,
                True,
                UAAutoAttachGCPInstance(),
                True,
                exceptions.CancelProLicensePolling(),
                None,
                [mock.call("Cancelling polling")],
                [mock.call(wait_for_change=False)],
                [],
            ),
            (
                False,
                False,
                True,
                UAAutoAttachGCPInstance(),
                True,
                False,
                False,
                [
                    mock.call(
                        "Configured to not poll for pro license, shutting down"
                    )
                ],
                [mock.call(wait_for_change=False)],
                [],
            ),
            (
                False,
                False,
                True,
                UAAutoAttachGCPInstance(),
                True,
                False,
                False,
                [
                    mock.call(
                        "Configured to not poll for pro license, shutting down"
                    )
                ],
                [mock.call(wait_for_change=False)],
                [],
            ),
        ],
    )
    def test_before_polling_loop_checks(
        self,
        m_is_config_value_true,
        m_is_current_series_lts,
        m_cloud_instance_factory,
        m_should_poll,
        m_is_pro_license_present,
        m_attempt_auto_attach,
        m_time,
        m_sleep,
        m_log_debug,
        is_config_value_true,
        is_attached,
        is_current_series_lts,
        cloud_instance,
        should_poll,
        is_pro_license_present,
        cfg_poll_for_pro_licenses,
        expected_log_debug_calls,
        expected_is_pro_license_present_calls,
        expected_attempt_auto_attach_calls,
        FakeConfig,
    ):
        if is_attached:
            cfg = FakeConfig.for_attached_machine()
        else:
            cfg = FakeConfig()
        cfg.cfg.update(
            {"ua_config": {
                "poll_for_pro_license": cfg_poll_for_pro_licenses
            }})

        m_is_config_value_true.return_value = is_config_value_true
        m_is_current_series_lts.return_value = is_current_series_lts
        m_cloud_instance_factory.side_effect = [cloud_instance]
        m_should_poll.return_value = should_poll
        m_is_pro_license_present.side_effect = [is_pro_license_present]

        poll_for_pro_license(cfg)

        assert expected_log_debug_calls == m_log_debug.call_args_list
        assert (expected_is_pro_license_present_calls ==
                m_is_pro_license_present.call_args_list)
        assert (expected_attempt_auto_attach_calls ==
                m_attempt_auto_attach.call_args_list)

    @pytest.mark.parametrize(
        "is_pro_license_present_side_effect,"
        "time_side_effect,"
        "expected_is_pro_license_present_calls,"
        "expected_attempt_auto_attach_calls,"
        "expected_log_debug_calls,"
        "expected_sleep_calls",
        [
            (
                [False, True],
                time_mock_side_effect_increment_by(100),
                [
                    mock.call(wait_for_change=False),
                    mock.call(wait_for_change=True),
                ],
                [mock.call(mock.ANY, mock.ANY)],
                [],
                [],
            ),
            (
                [False, False, False, False, False, True],
                time_mock_side_effect_increment_by(100),
                [
                    mock.call(wait_for_change=False),
                    mock.call(wait_for_change=True),
                    mock.call(wait_for_change=True),
                    mock.call(wait_for_change=True),
                    mock.call(wait_for_change=True),
                    mock.call(wait_for_change=True),
                ],
                [mock.call(mock.ANY, mock.ANY)],
                [],
                [],
            ),
            (
                [False, False, True],
                time_mock_side_effect_increment_by(1),
                [
                    mock.call(wait_for_change=False),
                    mock.call(wait_for_change=True),
                    mock.call(wait_for_change=True),
                ],
                [mock.call(mock.ANY, mock.ANY)],
                [
                    mock.call(
                        "wait_for_change returned quickly and no pro license"
                        " present. Waiting 123 seconds before polling again")
                ],
                [mock.call(123)],
            ),
            (
                [False, False, False, False, False, True],
                time_mock_side_effect_increment_by(1),
                [
                    mock.call(wait_for_change=False),
                    mock.call(wait_for_change=True),
                    mock.call(wait_for_change=True),
                    mock.call(wait_for_change=True),
                    mock.call(wait_for_change=True),
                    mock.call(wait_for_change=True),
                ],
                [mock.call(mock.ANY, mock.ANY)],
                [
                    mock.call(mock.ANY),
                    mock.call(mock.ANY),
                    mock.call(mock.ANY),
                    mock.call(mock.ANY),
                ],
                [
                    mock.call(123),
                    mock.call(123),
                    mock.call(123),
                    mock.call(123),
                ],
            ),
            (
                [
                    False,
                    False,
                    exceptions.DelayProLicensePolling(),
                    False,
                    exceptions.DelayProLicensePolling(),
                    True,
                ],
                time_mock_side_effect_increment_by(100),
                [
                    mock.call(wait_for_change=False),
                    mock.call(wait_for_change=True),
                    mock.call(wait_for_change=True),
                    mock.call(wait_for_change=True),
                    mock.call(wait_for_change=True),
                    mock.call(wait_for_change=True),
                ],
                [mock.call(mock.ANY, mock.ANY)],
                [],
                [mock.call(123), mock.call(123)],
            ),
            (
                [False, False,
                 exceptions.CancelProLicensePolling()],
                time_mock_side_effect_increment_by(100),
                [
                    mock.call(wait_for_change=False),
                    mock.call(wait_for_change=True),
                    mock.call(wait_for_change=True),
                ],
                [],
                [mock.call("Cancelling polling")],
                [],
            ),
        ],
    )
    def test_polling_loop(
        self,
        m_is_config_value_true,
        m_is_current_series_lts,
        m_cloud_instance_factory,
        m_should_poll,
        m_is_pro_license_present,
        m_attempt_auto_attach,
        m_time,
        m_sleep,
        m_log_debug,
        is_pro_license_present_side_effect,
        time_side_effect,
        expected_is_pro_license_present_calls,
        expected_attempt_auto_attach_calls,
        expected_log_debug_calls,
        expected_sleep_calls,
        FakeConfig,
    ):
        cfg = FakeConfig()
        cfg.cfg.update({
            "ua_config": {
                "poll_for_pro_license": True,
                "polling_error_retry_delay": 123,
            }
        })

        m_is_config_value_true.return_value = False
        m_is_current_series_lts.return_value = True
        m_cloud_instance_factory.return_value = UAAutoAttachGCPInstance()
        m_should_poll.return_value = True
        m_is_pro_license_present.side_effect = (
            is_pro_license_present_side_effect)
        m_time.side_effect = time_side_effect

        poll_for_pro_license(cfg)

        assert expected_sleep_calls == m_sleep.call_args_list
        assert expected_log_debug_calls == m_log_debug.call_args_list
        assert (expected_is_pro_license_present_calls ==
                m_is_pro_license_present.call_args_list)
        assert (expected_attempt_auto_attach_calls ==
                m_attempt_auto_attach.call_args_list)
 def test_is_viable_based_on_sys_hypervisor_uuid(self, load_file, uuid):
     """Viable ec2 platform is determined by /sys/hypervisor/uuid prefix"""
     load_file.return_value = uuid
     instance = UAAutoAttachAWSInstance()
     assert True is instance.is_viable
 def test_cloud_type(self):
     instance = UAAutoAttachAWSInstance()
     assert "aws" == instance.cloud_type
Exemple #13
0
 def test_unsupported_is_pro_license_present(self):
     """Unsupported"""
     instance = UAAutoAttachAWSInstance()
     with pytest.raises(exceptions.InPlaceUpgradeNotSupportedError):
         instance.is_pro_license_present(wait_for_change=False)
Exemple #14
0
 def test_unsupported_should_poll_for_pro_license(self):
     """Unsupported"""
     instance = UAAutoAttachAWSInstance()
     assert not instance.should_poll_for_pro_license()