示例#1
0
    def test_errors_are_raised_appropriately(
        self,
        _m_getuid,
        m_get_contract_information,
        _m_get_avail_resources,
        _m_should_reboot,
        _m_remove_notice,
        _m_contract_changed,
        exception_to_throw,
        exception_type,
        exception_message,
        capsys,
        FakeConfig,
    ):
        """Check that simulated status json/yaml output raises errors."""

        m_get_contract_information.side_effect = exception_to_throw

        cfg = FakeConfig()

        args = mock.MagicMock(format="json",
                              all=False,
                              simulate_with_token="some_token")

        with pytest.raises(exception_type) as exc:
            action_status(args, cfg=cfg)

        assert exc.type == exception_type
        assert exception_message in getattr(exc.value, "msg", exc.value.args)
示例#2
0
    def test_is_contract_changed(
        self,
        m_add_notice,
        _m_getuid,
        _m_get_contract_information,
        _m_get_available_resources,
        _m_should_reboot,
        _m_remove_notice,
        _m_contract_changed,
        contract_changed,
        is_attached,
        capsys,
        FakeConfig,
    ):
        _m_contract_changed.return_value = contract_changed
        if is_attached:
            cfg = FakeConfig().for_attached_machine()
        else:
            cfg = FakeConfig()

        action_status(mock.MagicMock(all=False, simulate_with_token=None),
                      cfg=cfg)

        if is_attached:
            if contract_changed:
                assert [
                    mock.call("", messages.NOTICE_REFRESH_CONTRACT_WARNING)
                ] == m_add_notice.call_args_list
            else:
                assert [
                    mock.call("", messages.NOTICE_REFRESH_CONTRACT_WARNING)
                ] not in m_add_notice.call_args_list
        else:
            assert _m_contract_changed.call_count == 0
示例#3
0
    def test_unicode_dash_replacement_when_unprintable(
        self,
        _m_getuid,
        _m_get_contract_information,
        _m_get_avail_resources,
        _m_should_reboot,
        _m_remove_notice,
        _m_contract_changed,
        encoding,
        expected_dash,
        FakeConfig,
    ):
        # This test can't use capsys because it doesn't emulate sys.stdout
        # encoding accurately in older versions of pytest
        underlying_stdout = io.BytesIO()
        fake_stdout = io.TextIOWrapper(underlying_stdout, encoding=encoding)

        with mock.patch("sys.stdout", fake_stdout):
            action_status(
                mock.MagicMock(all=True, simulate_with_token=None),
                cfg=FakeConfig.for_attached_machine(),
            )

        fake_stdout.flush()  # Make sure all output is in underlying_stdout
        out = underlying_stdout.getvalue().decode(encoding)

        # Colour codes are converted to spaces, so strip them out for
        # comparison
        out = out.replace(" " * 17, " " * 8)

        expected_out = ATTACHED_STATUS.format(dash=expected_dash, notices="")
        assert expected_out == out
    def test_error_on_connectivity_errors(self, m_getuid,
                                          m_get_avail_resources, capsys):
        """Raise UrlError on connectivity issues"""
        m_get_avail_resources.side_effect = util.UrlError(
            socket.gaierror(-2, "Name or service not known"))

        cfg = FakeConfig()

        with pytest.raises(util.UrlError):
            action_status(mock.MagicMock(), cfg)
示例#5
0
    def test_wait_blocks_until_lock_released(
        self,
        m_sleep,
        _m_subp,
        _m_get_version,
        _m_getuid,
        _m_get_contract_information,
        _m_get_avail_resources,
        _m_should_reboot,
        _m_remove_notice,
        _m_contract_changed,
        capsys,
        FakeConfig,
    ):
        """Check that --wait will will block and poll until lock released."""
        cfg = FakeConfig()
        lock_file = cfg.data_path("lock")
        cfg.write_cache("lock", "123:ua auto-attach")

        def fake_sleep(seconds):
            if m_sleep.call_count == 3:
                os.unlink(lock_file)

        m_sleep.side_effect = fake_sleep

        assert 0 == action_status(mock.MagicMock(all=False,
                                                 simulate_with_token=None),
                                  cfg=cfg)
        assert [mock.call(1)] * 3 == m_sleep.call_args_list
        assert "...\n" + UNATTACHED_STATUS == capsys.readouterr()[0]
    def test_unattached_json(self, m_getuid, m_get_avail_resources, capsys,
                             FakeConfig):
        """Check that unattached status json output is emitted to console"""
        cfg = FakeConfig()

        args = mock.MagicMock(format="json")
        assert 0 == action_status(args, cfg)
        expected = {
            "_doc": ("Content provided in json response is currently "
                     "considered Experimental and may change"),
            "attached":
            False,
            "expires":
            "n/a",
            "origin":
            None,
            "services": [{
                "name": "livepatch",
                "description": "Canonical Livepatch service",
                "available": "yes",
            }],
            "techSupportLevel":
            "n/a",
        }
        assert expected == json.loads(capsys.readouterr()[0])
    def test_unattached(self, m_getuid, m_get_avail_resources, capsys,
                        FakeConfig):
        """Check that unattached status is emitted to console"""
        cfg = FakeConfig()

        assert 0 == action_status(mock.MagicMock(), cfg)
        assert UNATTACHED_STATUS == capsys.readouterr()[0]
示例#8
0
    def test_error_on_connectivity_errors(
        self,
        _m_getuid,
        _m_get_contract_information,
        m_get_avail_resources,
        _m_should_reboot,
        _m_remove_notice,
        _m_contract_changed,
        FakeConfig,
    ):
        """Raise UrlError on connectivity issues"""
        m_get_avail_resources.side_effect = exceptions.UrlError(
            socket.gaierror(-2, "Name or service not known"))

        cfg = FakeConfig()

        with pytest.raises(exceptions.UrlError):
            action_status(mock.MagicMock(all=False, simulate_with_token=None),
                          cfg=cfg)
    def test_attached(self, m_getuid, m_get_avail_resources, capsys):
        """Check that root and non-root will emit attached status"""
        cfg = FakeConfig.for_attached_machine()
        assert 0 == action_status(mock.MagicMock(), cfg)
        # capsys already converts colorized non-printable chars to space
        # Strip non-printables from output
        printable_stdout = capsys.readouterr()[0].replace(" " * 17, " " * 8)

        # On older versions of pytest, capsys doesn't set sys.stdout.encoding
        # to something that Python parses as UTF-8 compatible, so we get the
        # ASCII dash; testing for the "wrong" dash here is OK, because we have
        # a specific test that the correct one is used in
        # test_unicode_dash_replacement_when_unprintable
        expected_dash = "-"
        if sys.stdout.encoding and "UTF-8" in sys.stdout.encoding.upper():
            expected_dash = "\u2014"
        assert ATTACHED_STATUS.format(dash=expected_dash) == printable_stdout
示例#10
0
    def test_unattached(
        self,
        _m_getuid,
        _m_get_contract_information,
        _m_get_avail_resources,
        _m_should_reboot,
        _m_remove_notice,
        _m_contract_changed,
        capsys,
        FakeConfig,
    ):
        """Check that unattached status is emitted to console"""
        cfg = FakeConfig()

        assert 0 == action_status(mock.MagicMock(all=False,
                                                 simulate_with_token=None),
                                  cfg=cfg)
        assert UNATTACHED_STATUS == capsys.readouterr()[0]
示例#11
0
    def test_errors_for_token_dates(
        self,
        _m_getuid,
        m_get_contract_information,
        _m_get_avail_resources,
        _m_should_reboot,
        _m_remove_notice,
        _m_contract_changed,
        format_type,
        event_logger_mode,
        token_to_use,
        warning_message,
        contract_field,
        date_value,
        capsys,
        FakeConfig,
        event,
    ):
        """Check errors for expired tokens, and not valid yet tokens."""
        def contract_info_side_effect(cfg, token):
            response = copy.deepcopy(RESPONSE_CONTRACT_INFO)
            response["contractInfo"][contract_field] = date_value
            return response

        m_get_contract_information.side_effect = contract_info_side_effect

        cfg = FakeConfig()

        args = mock.MagicMock(format=format_type,
                              all=False,
                              simulate_with_token=token_to_use)

        with mock.patch.object(event, "_event_logger_mode",
                               event_logger_mode), mock.patch.object(
                                   event, "_command", "status"):
            assert 1 == action_status(args, cfg=cfg)

        if format_type == "json":
            output = json.loads(capsys.readouterr()[0])
        else:
            output = yaml.safe_load(capsys.readouterr()[0])

        assert output["errors"][0]["message"] == warning_message
示例#12
0
    def test_wait_blocks_until_lock_released(
        self,
        m_sleep,
        m_subp,
        m_getuid,
        m_get_avail_resources,
        capsys,
        FakeConfig,
    ):
        """Check that --wait will will block and poll until lock released."""
        cfg = FakeConfig()
        lock_file = cfg.data_path("lock")
        cfg.write_cache("lock", "123:ua auto-attach")

        def fake_sleep(seconds):
            if m_sleep.call_count == 3:
                os.unlink(lock_file)

        m_sleep.side_effect = fake_sleep

        assert 0 == action_status(mock.MagicMock(), cfg)
        assert [mock.call(1)] * 3 == m_sleep.call_args_list
        assert "...\n" + UNATTACHED_STATUS == capsys.readouterr()[0]
示例#13
0
    def test_attached(
        self,
        _m_getuid,
        _m_get_contract_information,
        _m_get_avail_resources,
        _m_should_reboot,
        _m_remove_notice,
        _m_contract_changed,
        notices,
        notice_status,
        use_all,
        capsys,
        FakeConfig,
    ):
        """Check that root and non-root will emit attached status"""
        cfg = FakeConfig.for_attached_machine()
        cfg.write_cache("notices", notices)
        assert 0 == action_status(mock.MagicMock(all=use_all,
                                                 simulate_with_token=None),
                                  cfg=cfg)
        # capsys already converts colorized non-printable chars to space
        # Strip non-printables from output
        printable_stdout = capsys.readouterr()[0].replace(" " * 17, " " * 8)

        # On older versions of pytest, capsys doesn't set sys.stdout.encoding
        # to something that Python parses as UTF-8 compatible, so we get the
        # ASCII dash; testing for the "wrong" dash here is OK, because we have
        # a specific test that the correct one is used in
        # test_unicode_dash_replacement_when_unprintable
        expected_dash = "-"
        status_tmpl = ATTACHED_STATUS if use_all else ATTACHED_STATUS_NOBETA

        if sys.stdout.encoding and "UTF-8" in sys.stdout.encoding.upper():
            expected_dash = "\u2014"
        assert (status_tmpl.format(dash=expected_dash,
                                   notices=notice_status) == printable_stdout)
示例#14
0
    def test_simulated_formats(
        self,
        _m_getuid,
        _m_get_contract_information,
        _m_get_avail_resources,
        _m_should_reboot,
        _m_remove_notice,
        _m_contract_changed,
        use_all,
        format_type,
        event_logger_mode,
        capsys,
        FakeConfig,
        event,
    ):
        """Check that simulated status json output is emitted to console"""
        cfg = FakeConfig()

        args = mock.MagicMock(format=format_type,
                              all=use_all,
                              simulate_with_token="some_token")

        with mock.patch.object(event, "_event_logger_mode",
                               event_logger_mode), mock.patch.object(
                                   event, "_command", "status"):
            assert 0 == action_status(args, cfg=cfg)

        expected_services = [
            {
                "auto_enabled": "yes",
                "available": "yes",
                "description": "UA Apps: Extended Security Maintenance (ESM)",
                "entitled": "no",
                "name": "esm-apps",
            },
            {
                "auto_enabled": "yes",
                "available": "no",
                "description": "UA Infra: Extended Security Maintenance (ESM)",
                "entitled": "yes",
                "name": "esm-infra",
            },
            {
                "auto_enabled": "no",
                "available": "no",
                "description": "NIST-certified core packages",
                "entitled": "no",
                "name": "fips",
            },
            {
                "auto_enabled": "no",
                "available": "no",
                "description": "NIST-certified core packages with priority"
                " security updates",
                "entitled": "no",
                "name": "fips-updates",
            },
            {
                "auto_enabled": "no",
                "available": "yes",
                "description": "Canonical Livepatch service",
                "entitled": "yes",
                "name": "livepatch",
            },
            {
                "auto_enabled": "no",
                "available": "no",
                "description": "Beta-version Ubuntu Kernel with PREEMPT_RT"
                " patches",
                "entitled": "no",
                "name": "realtime-kernel",
            },
            {
                "auto_enabled": "no",
                "available": "no",
                "description": "Security Updates for the Robot Operating"
                " System",
                "entitled": "no",
                "name": "ros",
            },
            {
                "auto_enabled": "no",
                "available": "no",
                "description": "All Updates for the Robot Operating System",
                "entitled": "no",
                "name": "ros-updates",
            },
        ]

        if not use_all:
            expected_services = expected_services[1:-3]

        expected = {
            "_doc": "Content provided in json response is currently considered"
            " Experimental and may change",
            "_schema_version": "0.1",
            "attached": False,
            "machine_id": None,
            "notices": [],
            "account": {
                "created_at": "2019-06-14T06:45:50Z",
                "external_account_ids": [],
                "id": "some_id",
                "name": "Name",
            },
            "contract": {
                "created_at": "2021-05-21T20:00:53Z",
                "id": "some_id",
                "name": "Name",
                "products": ["uai-essential-virtual"],
                "tech_support_level": "essential",
            },
            "environment_vars": [],
            "execution_status": "inactive",
            "execution_details": "No Ubuntu Advantage operations are running",
            "expires": "9999-12-31T00:00:00Z",
            "effective": None,
            "services": expected_services,
            "simulated": True,
            "version": version.get_version(features=cfg.features),
            "config_path": None,
            "config": {
                "data_dir": mock.ANY
            },
            "errors": [],
            "warnings": [],
            "result": "success",
        }

        if format_type == "json":
            assert expected == json.loads(capsys.readouterr()[0])
        else:
            assert expected == yaml.safe_load(capsys.readouterr()[0])
示例#15
0
    def test_attached_formats(
        self,
        _m_getuid,
        _m_get_contract_information,
        _m_get_avail_resources,
        _m_should_reboot,
        _m_remove_notice,
        _m_contract_changed,
        use_all,
        environ,
        format_type,
        event_logger_mode,
        capsys,
        FakeConfig,
        event,
    ):
        """Check that unattached status json output is emitted to console"""
        cfg = FakeConfig.for_attached_machine()

        args = mock.MagicMock(format=format_type,
                              all=use_all,
                              simulate_with_token=None)

        with mock.patch.object(os, "environ", environ):
            with mock.patch.object(event, "_event_logger_mode",
                                   event_logger_mode), mock.patch.object(
                                       event, "_command", "status"):
                assert 0 == action_status(args, cfg=cfg)

        expected_environment = []
        if environ:
            expected_environment = [
                {
                    "name": "UA_CONFIG_FILE",
                    "value": "config_file"
                },
                {
                    "name": "UA_DATA_DIR",
                    "value": "data_dir"
                },
                {
                    "name": "UA_FEATURES_ALLOW_BETA",
                    "value": "true"
                },
            ]

        if use_all:
            services = SERVICES_JSON_ALL
        else:
            services = [
                svc for svc in SERVICES_JSON_ALL
                if svc["name"] not in BETA_SVC_NAMES
            ]

        inapplicable_services = [
            service["name"] for service in RESPONSE_AVAILABLE_SERVICES
            if not service["available"]
        ]

        filtered_services = [
            service for service in services
            if service["name"] not in inapplicable_services
        ]

        if format_type == "json":
            contract_created_at = "2020-05-08T19:02:26+00:00"
            account_created_at = "2019-06-14T06:45:50+00:00"
            expires = "2040-05-08T19:02:26+00:00"
            effective = "2000-05-08T19:02:26+00:00"
        else:
            contract_created_at = datetime.datetime(
                2020, 5, 8, 19, 2, 26, tzinfo=datetime.timezone.utc)
            account_created_at = datetime.datetime(
                2019, 6, 14, 6, 45, 50, tzinfo=datetime.timezone.utc)
            expires = datetime.datetime(2040,
                                        5,
                                        8,
                                        19,
                                        2,
                                        26,
                                        tzinfo=datetime.timezone.utc)
            effective = datetime.datetime(2000,
                                          5,
                                          8,
                                          19,
                                          2,
                                          26,
                                          tzinfo=datetime.timezone.utc)

        tech_support_level = status.UserFacingStatus.INAPPLICABLE.value
        expected = {
            "_doc": ("Content provided in json response is currently "
                     "considered Experimental and may change"),
            "_schema_version":
            "0.1",
            "version":
            version.get_version(features=cfg.features),
            "execution_status":
            status.UserFacingConfigStatus.INACTIVE.value,
            "execution_details":
            messages.NO_ACTIVE_OPERATIONS,
            "attached":
            True,
            "machine_id":
            "test_machine_id",
            "effective":
            effective,
            "expires":
            expires,
            "notices": [],
            "services":
            filtered_services,
            "environment_vars":
            expected_environment,
            "contract": {
                "id": "cid",
                "name": "test_contract",
                "created_at": contract_created_at,
                "products": ["free"],
                "tech_support_level": tech_support_level,
            },
            "account": {
                "id": "acct-1",
                "name": "test_account",
                "created_at": account_created_at,
                "external_account_ids": [{
                    "IDs": ["id1"],
                    "Origin": "AWS"
                }],
            },
            "config_path":
            None,
            "config": {
                "data_dir": mock.ANY
            },
            "simulated":
            False,
            "errors": [],
            "warnings": [],
            "result":
            "success",
        }

        if format_type == "json":
            assert expected == json.loads(capsys.readouterr()[0])
        else:
            yaml_output = yaml.safe_load(capsys.readouterr()[0])

            # On earlier versions of pyyaml, we don't add the timezone
            # info when converting a date string into a datetime object.
            # Since we only want to test if we are producing a valid
            # yaml file in the status output, we can manually add
            # the timezone info to make the test work as expected
            for key, value in yaml_output.items():
                if isinstance(value, datetime.datetime):
                    yaml_output[key] = value.replace(
                        tzinfo=datetime.timezone.utc)

                elif isinstance(value, dict):
                    for inner_key, inner_value in value.items():
                        if isinstance(inner_value, datetime.datetime):
                            yaml_output[key][inner_key] = inner_value.replace(
                                tzinfo=datetime.timezone.utc)

            assert expected == yaml_output
示例#16
0
    def test_unattached_formats(
        self,
        _m_getuid,
        _m_get_contract_information,
        _m_get_avail_resources,
        _m_should_reboot,
        _m_remove_notice,
        _m_contract_changed,
        use_all,
        environ,
        format_type,
        event_logger_mode,
        capsys,
        FakeConfig,
        event,
    ):
        """Check that unattached status json output is emitted to console"""
        cfg = FakeConfig()

        args = mock.MagicMock(format=format_type,
                              all=use_all,
                              simulate_with_token=None)
        with mock.patch.object(os, "environ", environ):
            with mock.patch.object(event, "_event_logger_mode",
                                   event_logger_mode), mock.patch.object(
                                       event, "_command", "status"):
                assert 0 == action_status(args, cfg=cfg)

        expected_environment = []
        if environ:
            expected_environment = [
                {
                    "name": "UA_CONFIG_FILE",
                    "value": "config_file"
                },
                {
                    "name": "UA_DATA_DIR",
                    "value": "data_dir"
                },
                {
                    "name": "UA_FEATURES_ALLOW_BETA",
                    "value": "true"
                },
            ]

        expected_services = [
            {
                "name": "esm-apps",
                "description": "UA Apps: Extended Security Maintenance (ESM)",
                "available": "yes",
            },
            {
                "name": "livepatch",
                "description": "Canonical Livepatch service",
                "available": "yes",
            },
        ]
        if not use_all:
            expected_services.pop(0)

        expected = {
            "_doc": ("Content provided in json response is currently "
                     "considered Experimental and may change"),
            "_schema_version":
            "0.1",
            "version":
            version.get_version(features=cfg.features),
            "execution_status":
            status.UserFacingConfigStatus.INACTIVE.value,
            "execution_details":
            messages.NO_ACTIVE_OPERATIONS,
            "attached":
            False,
            "machine_id":
            None,
            "effective":
            None,
            "expires":
            None,
            "notices": [],
            "services":
            expected_services,
            "environment_vars":
            expected_environment,
            "contract": {
                "id": "",
                "name": "",
                "created_at": "",
                "products": [],
                "tech_support_level": "n/a",
            },
            "account": {
                "name": "",
                "id": "",
                "created_at": "",
                "external_account_ids": [],
            },
            "config_path":
            None,
            "config": {
                "data_dir": mock.ANY
            },
            "simulated":
            False,
            "errors": [],
            "warnings": [],
            "result":
            "success",
        }

        if format_type == "json":
            assert expected == json.loads(capsys.readouterr()[0])
        else:
            assert expected == yaml.safe_load(capsys.readouterr()[0])