def test_non_root_users_are_rejected(self, m_getuid, _m_prompt, FakeConfig, event, capsys): """Check that a UID != 0 will receive a message and exit non-zero""" m_getuid.return_value = 1 args = mock.MagicMock() cfg = FakeConfig.for_attached_machine() with pytest.raises(exceptions.NonRootUserError): action_detach(args, cfg=cfg) with pytest.raises(SystemExit): with mock.patch.object(event, "_event_logger_mode", event_logger.EventLoggerMode.JSON): main_error_handler(action_detach)(args, cfg) expected_message = messages.NONROOT_USER expected = { "_schema_version": event_logger.JSON_SCHEMA_VERSION, "result": "failure", "errors": [{ "message": expected_message.msg, "message_code": expected_message.name, "service": None, "type": "system", }], "failed_services": [], "needs_reboot": False, "processed_services": [], "warnings": [], } assert expected == json.loads(capsys.readouterr()[0])
def test_correct_message_emitted( self, m_update_apt_and_motd_msgs, m_client, m_entitlements, m_getuid, _m_prompt, capsys, FakeConfig, tmpdir, ): m_getuid.return_value = 0 m_entitlements.ENTITLEMENT_CLASSES = [] fake_client = FakeContractClient(FakeConfig.for_attached_machine()) m_client.return_value = fake_client m_cfg = mock.MagicMock() m_cfg.check_lock_info.return_value = (-1, "") m_cfg.data_path.return_value = tmpdir.join("lock").strpath action_detach(mock.MagicMock(), m_cfg) out, _err = capsys.readouterr() assert messages.DETACH_SUCCESS + "\n" == out assert [mock.call(m_cfg)] == m_update_apt_and_motd_msgs.call_args_list
def test_unattached_error_message(self, m_getuid, _m_prompt, FakeConfig, capsys, event): """Check that root user gets unattached message.""" m_getuid.return_value = 0 cfg = FakeConfig() args = mock.MagicMock() with pytest.raises(exceptions.UnattachedError) as err: action_detach(args, cfg=cfg) assert messages.UNATTACHED.msg == err.value.msg with pytest.raises(SystemExit): with mock.patch.object(event, "_event_logger_mode", event_logger.EventLoggerMode.JSON): main_error_handler(action_detach)(args, cfg) expected_message = messages.UNATTACHED expected = { "_schema_version": event_logger.JSON_SCHEMA_VERSION, "result": "failure", "errors": [{ "message": expected_message.msg, "message_code": expected_message.name, "service": None, "type": "system", }], "failed_services": [], "needs_reboot": False, "processed_services": [], "warnings": [], } assert expected == json.loads(capsys.readouterr()[0])
def test_non_root_users_are_rejected(self, m_getuid, _m_prompt): """Check that a UID != 0 will receive a message and exit non-zero""" m_getuid.return_value = 1 cfg = FakeConfig.for_attached_machine() with pytest.raises(exceptions.NonRootUserError): action_detach(mock.MagicMock(), cfg)
def _prompt_for_new_token(cfg: UAConfig) -> bool: """Prompt for attach a new subscription token to the user. :return: True if attach performed. """ import argparse from uaclient import cli _inform_ubuntu_pro_existence_if_applicable() print(messages.SECURITY_UPDATE_NOT_INSTALLED_EXPIRED) choice = util.prompt_choices( "Choose: [R]enew your subscription (at {}) [C]ancel".format( BASE_UA_URL ), valid_choices=["r", "c"], ) if choice == "r": print(messages.PROMPT_EXPIRED_ENTER_TOKEN) token = input("> ") print(colorize_commands([["ua", "detach"]])) cli.action_detach( argparse.Namespace(assume_yes=True, format="cli"), cfg ) return _run_ua_attach(cfg, token) return False
def test_informational_message_emitted( self, m_client, m_entitlements, m_getuid, _m_prompt, capsys, classes, expected_message, FakeConfig, tmpdir, ): m_getuid.return_value = 0 m_entitlements.ENTITLEMENT_CLASSES = classes fake_client = FakeContractClient(FakeConfig.for_attached_machine()) m_client.return_value = fake_client m_cfg = mock.MagicMock() m_cfg.data_path.return_value = tmpdir.join("lock").strpath action_detach(mock.MagicMock(), m_cfg) out, _err = capsys.readouterr() assert expected_message in out
def test_config_cache_deleted(self, m_entitlements, m_getuid, _m_prompt): m_getuid.return_value = 0 m_entitlements.ENTITLEMENT_CLASSES = [] cfg = mock.MagicMock() action_detach(mock.MagicMock(), cfg) assert [mock.call()] == cfg.delete_cache.call_args_list
def test_unattached_error_message(self, m_getuid, _m_prompt, FakeConfig): """Check that root user gets unattached message.""" m_getuid.return_value = 0 cfg = FakeConfig() with pytest.raises(exceptions.UnattachedError) as err: action_detach(mock.MagicMock(), cfg) assert status.MESSAGE_UNATTACHED == err.value.msg
def test_correct_message_emitted( self, m_entitlements, m_getuid, _m_prompt, capsys ): m_getuid.return_value = 0 m_entitlements.ENTITLEMENT_CLASSES = [] action_detach(mock.MagicMock(), mock.MagicMock()) out, _err = capsys.readouterr() assert status.MESSAGE_DETACH_SUCCESS + "\n" == out
def test_lock_file_exists(self, m_subp, m_getuid, m_prompt, FakeConfig): """Check when an operation holds a lock file, detach cannot run.""" m_getuid.return_value = 0 cfg = FakeConfig.for_attached_machine() with open(cfg.data_path("lock"), "w") as stream: stream.write("123:ua enable") with pytest.raises(exceptions.LockHeldError) as err: action_detach(mock.MagicMock(), cfg) assert [mock.call(["ps", "123"])] == m_subp.call_args_list assert ("Unable to perform: ua detach.\n" "Operation in progress: ua enable (pid:123)") == err.value.msg
def test_config_cache_deleted(self, m_client, m_entitlements, m_getuid, _m_prompt, FakeConfig): m_getuid.return_value = 0 m_entitlements.ENTITLEMENT_CLASSES = [] fake_client = FakeContractClient(FakeConfig.for_attached_machine()) m_client.return_value = fake_client cfg = mock.MagicMock() action_detach(mock.MagicMock(), cfg) assert [mock.call()] == cfg.delete_cache.call_args_list
def test_correct_message_emitted(self, m_client, m_entitlements, m_getuid, _m_prompt, capsys, FakeConfig): m_getuid.return_value = 0 m_entitlements.ENTITLEMENT_CLASSES = [] fake_client = FakeContractClient(FakeConfig.for_attached_machine()) m_client.return_value = fake_client action_detach(mock.MagicMock(), mock.MagicMock()) out, _err = capsys.readouterr() assert status.MESSAGE_DETACH_SUCCESS + "\n" == out
def test_config_cache_deleted(self, m_client, m_entitlements, m_getuid, _m_prompt, FakeConfig, tmpdir): m_getuid.return_value = 0 m_entitlements.ENTITLEMENT_CLASSES = [] fake_client = FakeContractClient(FakeConfig.for_attached_machine()) m_client.return_value = fake_client m_cfg = mock.MagicMock() m_cfg.data_path.return_value = tmpdir.join("lock").strpath action_detach(mock.MagicMock(), m_cfg) assert [mock.call()] == m_cfg.delete_cache.call_args_list
def test_informational_message_emitted( self, m_update_apt_and_motd_msgs, m_client, m_entitlements, m_getuid, _m_prompt, capsys, classes, expected_message, disabled_services, FakeConfig, tmpdir, event, ): m_getuid.return_value = 0 m_entitlements.ENTITLEMENT_CLASSES = classes fake_client = FakeContractClient(FakeConfig.for_attached_machine()) m_client.return_value = fake_client m_cfg = mock.MagicMock() m_cfg.check_lock_info.return_value = (-1, "") m_cfg.data_path.return_value = tmpdir.join("lock").strpath args = mock.MagicMock() action_detach(args, m_cfg) out, _err = capsys.readouterr() assert expected_message in out assert [mock.call(m_cfg)] == m_update_apt_and_motd_msgs.call_args_list cfg = FakeConfig.for_attached_machine() fake_stdout = io.StringIO() with contextlib.redirect_stdout(fake_stdout): with mock.patch.object(event, "_event_logger_mode", event_logger.EventLoggerMode.JSON): main_error_handler(action_detach)(args, cfg) expected = { "_schema_version": event_logger.JSON_SCHEMA_VERSION, "result": "success", "errors": [], "failed_services": [], "needs_reboot": False, "processed_services": disabled_services, "warnings": [], } assert expected == json.loads(fake_stdout.getvalue())
def test_informational_message_emitted( self, m_entitlements, m_getuid, _m_prompt, capsys, classes, expected_message, ): m_getuid.return_value = 0 m_entitlements.ENTITLEMENT_CLASSES = classes action_detach(mock.MagicMock(), mock.MagicMock()) out, _err = capsys.readouterr() assert expected_message in out
def test_returns_zero(self, m_entitlements, m_getuid, _m_prompt): m_getuid.return_value = 0 m_entitlements.ENTITLEMENT_CLASSES = [] ret = action_detach(mock.MagicMock(), mock.MagicMock()) assert 0 == ret
def test_entitlements_disabled_appropriately( self, m_client, m_entitlements, m_getuid, m_prompt, prompt_response, assume_yes, expect_disable, FakeConfig, ): # The three parameters: # prompt_response: the user's response to the prompt # assume_yes: the value of the --assume-yes flag in the args passed # to the action # expect_disable: whether or not the enabled entitlement is expected # to be disabled by the action m_getuid.return_value = 0 cfg = FakeConfig.for_attached_machine() fake_client = FakeContractClient(cfg) m_client.return_value = fake_client m_prompt.return_value = prompt_response m_entitlements.ENTITLEMENT_CLASSES = [ entitlement_cls_mock_factory(False), entitlement_cls_mock_factory(True), entitlement_cls_mock_factory(False), ] args = mock.MagicMock(assume_yes=assume_yes) return_code = action_detach(args, cfg) # Check that can_disable is called correctly for ent_cls in m_entitlements.ENTITLEMENT_CLASSES: assert [mock.call(silent=True) ] == ent_cls.return_value.can_disable.call_args_list # Check that disable is only called when can_disable is true for undisabled_cls in [ m_entitlements.ENTITLEMENT_CLASSES[0], m_entitlements.ENTITLEMENT_CLASSES[2], ]: assert 0 == undisabled_cls.return_value.disable.call_count disabled_cls = m_entitlements.ENTITLEMENT_CLASSES[1] if expect_disable: assert [mock.call(silent=True) ] == disabled_cls.return_value.disable.call_args_list assert 0 == return_code else: assert 0 == disabled_cls.return_value.disable.call_count assert 1 == return_code assert [mock.call(assume_yes=assume_yes)] == m_prompt.call_args_list
def test_returns_zero(self, m_client, m_entitlements, m_getuid, _m_prompt, FakeConfig): m_getuid.return_value = 0 m_entitlements.ENTITLEMENT_CLASSES = [] fake_client = FakeContractClient(FakeConfig.for_attached_machine()) m_client.return_value = fake_client ret = action_detach(mock.MagicMock(), mock.MagicMock()) assert 0 == ret
def test_returns_zero(self, m_client, m_entitlements, m_getuid, _m_prompt, FakeConfig, tmpdir): m_getuid.return_value = 0 m_entitlements.ENTITLEMENT_CLASSES = [] fake_client = FakeContractClient(FakeConfig.for_attached_machine()) m_client.return_value = fake_client m_cfg = mock.MagicMock() m_cfg.data_path.return_value = tmpdir.join("lock").strpath ret = action_detach(mock.MagicMock(), m_cfg) assert 0 == ret
def test_lock_file_exists(self, m_subp, m_getuid, m_prompt, FakeConfig, capsys, event): """Check when an operation holds a lock file, detach cannot run.""" m_getuid.return_value = 0 cfg = FakeConfig.for_attached_machine() args = mock.MagicMock() with open(cfg.data_path("lock"), "w") as stream: stream.write("123:ua enable") with pytest.raises(exceptions.LockHeldError) as err: action_detach(args, cfg=cfg) assert [mock.call(["ps", "123"])] == m_subp.call_args_list expected_error_msg = messages.LOCK_HELD_ERROR.format( lock_request="ua detach", lock_holder="ua enable", pid="123") assert expected_error_msg.msg == err.value.msg with pytest.raises(SystemExit): with mock.patch.object(event, "_event_logger_mode", event_logger.EventLoggerMode.JSON): main_error_handler(action_detach)(args, cfg) expected = { "_schema_version": event_logger.JSON_SCHEMA_VERSION, "result": "failure", "errors": [{ "message": expected_error_msg.msg, "message_code": expected_error_msg.name, "service": None, "type": "system", }], "failed_services": [], "needs_reboot": False, "processed_services": [], "warnings": [], } assert expected == json.loads(capsys.readouterr()[0])
def test_returns_zero( self, m_update_apt_and_motd_msgs, m_client, m_entitlements, m_getuid, _m_prompt, FakeConfig, tmpdir, ): m_getuid.return_value = 0 m_entitlements.ENTITLEMENT_CLASSES = [] fake_client = FakeContractClient(FakeConfig.for_attached_machine()) m_client.return_value = fake_client m_cfg = mock.MagicMock() m_cfg.check_lock_info.return_value = (-1, "") m_cfg.data_path.return_value = tmpdir.join("lock").strpath ret = action_detach(mock.MagicMock(), m_cfg) assert 0 == ret assert [mock.call(m_cfg)] == m_update_apt_and_motd_msgs.call_args_list
def test_entitlements_disabled_if_can_disable_and_prompt_true( self, m_entitlements, m_getuid, m_prompt, prompt_response ): m_getuid.return_value = 0 m_prompt.return_value = prompt_response m_entitlements.ENTITLEMENT_CLASSES = [ entitlement_cls_mock_factory(False), entitlement_cls_mock_factory(True), entitlement_cls_mock_factory(False), ] return_code = action_detach( mock.MagicMock(), FakeConfig.for_attached_machine() ) # Check that can_disable is called correctly for ent_cls in m_entitlements.ENTITLEMENT_CLASSES: assert [ mock.call(silent=True) ] == ent_cls.return_value.can_disable.call_args_list # Check that disable is only called when can_disable is true for undisabled_cls in [ m_entitlements.ENTITLEMENT_CLASSES[0], m_entitlements.ENTITLEMENT_CLASSES[2], ]: assert 0 == undisabled_cls.return_value.disable.call_count disabled_cls = m_entitlements.ENTITLEMENT_CLASSES[1] if prompt_response: assert [ mock.call(silent=True) ] == disabled_cls.return_value.disable.call_args_list assert 0 == return_code else: assert 0 == disabled_cls.return_value.disable.call_count assert 1 == return_code
def test_entitlements_disabled_appropriately( self, m_update_apt_and_motd_msgs, m_client, m_entitlements, m_getuid, m_prompt, prompt_response, assume_yes, expect_disable, FakeConfig, event, capsys, ): # The three parameters: # prompt_response: the user's response to the prompt # assume_yes: the value of the --assume-yes flag in the args passed # to the action # expect_disable: whether or not the enabled entitlement is expected # to be disabled by the action m_getuid.return_value = 0 cfg = FakeConfig.for_attached_machine() fake_client = FakeContractClient(cfg) m_client.return_value = fake_client m_prompt.return_value = prompt_response m_entitlements.ENTITLEMENT_CLASSES = [ entitlement_cls_mock_factory(False), entitlement_cls_mock_factory(True, name="test"), entitlement_cls_mock_factory(False), ] args = mock.MagicMock(assume_yes=assume_yes) return_code = action_detach(args, cfg=cfg) # Check that can_disable is called correctly for ent_cls in m_entitlements.ENTITLEMENT_CLASSES: assert [mock.call(ignore_dependent_services=True) ] == ent_cls.return_value.can_disable.call_args_list assert [mock.call(cfg=cfg, assume_yes=assume_yes)] == ent_cls.call_args_list # Check that disable is only called when can_disable is true for undisabled_cls in [ m_entitlements.ENTITLEMENT_CLASSES[0], m_entitlements.ENTITLEMENT_CLASSES[2], ]: assert 0 == undisabled_cls.return_value.disable.call_count disabled_cls = m_entitlements.ENTITLEMENT_CLASSES[1] if expect_disable: assert [mock.call() ] == disabled_cls.return_value.disable.call_args_list assert 0 == return_code else: assert 0 == disabled_cls.return_value.disable.call_count assert 1 == return_code assert [mock.call(assume_yes=assume_yes)] == m_prompt.call_args_list if expect_disable: assert [mock.call(cfg) ] == m_update_apt_and_motd_msgs.call_args_list cfg = FakeConfig.for_attached_machine() fake_stdout = io.StringIO() # On json response, we will never prompt the user m_prompt.return_value = True with contextlib.redirect_stdout(fake_stdout): with mock.patch.object(event, "_event_logger_mode", event_logger.EventLoggerMode.JSON): main_error_handler(action_detach)(args, cfg) expected = { "_schema_version": event_logger.JSON_SCHEMA_VERSION, "result": "success", "errors": [], "failed_services": [], "needs_reboot": False, "processed_services": ["test"], "warnings": [], } assert expected == json.loads(fake_stdout.getvalue())