From e1c796a2917a43c9cc117015389d639403fb0609 Mon Sep 17 00:00:00 2001 From: Grant Orndorff Date: Tue, 20 Jun 2023 15:31:12 -0400 Subject: [PATCH] contract: send activityInfo after attach --- uaclient/actions.py | 3 +++ ...i_u_pro_attach_auto_full_auto_attach_v1.py | 25 +++++++++++++++++++ .../u/pro/attach/auto/full_auto_attach/v1.py | 5 +++- uaclient/cli.py | 4 +++ uaclient/conftest.py | 13 ++++++++++ uaclient/tests/test_actions.py | 5 ++++ uaclient/tests/test_cli_attach.py | 19 ++++++++++++++ 7 files changed, 73 insertions(+), 1 deletion(-) diff --git a/uaclient/actions.py b/uaclient/actions.py index cbd7091199..5a5daf94b2 100644 --- a/uaclient/actions.py +++ b/uaclient/actions.py @@ -56,6 +56,7 @@ def attach_with_token( """ from uaclient.timer.update_messaging import update_motd_messages + contract_client = contract.UAContractClient(cfg) attached_at = datetime.datetime.now(tz=datetime.timezone.utc) try: @@ -67,12 +68,14 @@ def attach_with_token( attachment_data_file.write(AttachmentData(attached_at=attached_at)) ua_status.status(cfg=cfg) update_motd_messages(cfg) + contract_client.update_activity_token() raise exc except exceptions.UserFacingError as exc: # Persist updated status in the event of partial attach attachment_data_file.write(AttachmentData(attached_at=attached_at)) ua_status.status(cfg=cfg) update_motd_messages(cfg) + contract_client.update_activity_token() # raise this exception in case we cannot enable all services raise exc diff --git a/uaclient/api/tests/test_api_u_pro_attach_auto_full_auto_attach_v1.py b/uaclient/api/tests/test_api_u_pro_attach_auto_full_auto_attach_v1.py index 00717105b3..175c891013 100644 --- a/uaclient/api/tests/test_api_u_pro_attach_auto_full_auto_attach_v1.py +++ b/uaclient/api/tests/test_api_u_pro_attach_auto_full_auto_attach_v1.py @@ -160,6 +160,9 @@ def test_enable_services_by_name( @mock.patch("uaclient.files.notices.add") @mock.patch("uaclient.files.notices.remove") class TestFullAutoAttachV1: + @mock.patch( + M_PATH + "contract.UAContractClient.update_activity_token", + ) @mock.patch( "uaclient.actions.enable_entitlement_by_name", ) @@ -170,6 +173,7 @@ def test_error_invalid_ent_names( _auto_attach, _get_cloud_instance, m_enable_ent_by_name, + _m_update_activity_token, _notice_remove, _notice_add, FakeConfig, @@ -192,6 +196,9 @@ def enable_ent_side_effect(cfg, name, assume_yes, allow_beta): assert 5 == m_enable_ent_by_name.call_count + @mock.patch( + M_PATH + "contract.UAContractClient.update_activity_token", + ) @mock.patch( "uaclient.actions.enable_entitlement_by_name", return_value=(False, None), @@ -203,6 +210,7 @@ def test_error_full_auto_attach_fail( _auto_attach, _get_cloud_instance, enable_ent_by_name, + _m_update_activity_token, _notice_remove, _notice_add, FakeConfig, @@ -242,6 +250,7 @@ def test_lock_held( "enable_services_by_name_side_effect", "expected_enable_services_by_name_call_args", "raise_expectation", + "expect_activity_call", "expected_error_message", "expected_ret", ], @@ -255,6 +264,7 @@ def test_lock_held( [], [], pytest.raises(exceptions.AlreadyAttachedError), + False, messages.ALREADY_ATTACHED.format( account_name="test_account" ).msg, @@ -269,6 +279,7 @@ def test_lock_held( [], [], pytest.raises(exceptions.AutoAttachDisabledError), + False, messages.AUTO_ATTACH_DISABLED_ERROR.msg, None, ), @@ -281,6 +292,7 @@ def test_lock_held( [], [], does_not_raise(), + True, None, FullAutoAttachResult(), ), @@ -293,6 +305,7 @@ def test_lock_held( [[]], [mock.call(mock.ANY, ["cis"], allow_beta=False)], does_not_raise(), + True, None, FullAutoAttachResult(), ), @@ -305,6 +318,7 @@ def test_lock_held( [[]], [mock.call(mock.ANY, ["cis"], allow_beta=True)], does_not_raise(), + True, None, FullAutoAttachResult(), ), @@ -320,6 +334,7 @@ def test_lock_held( mock.call(mock.ANY, ["cis"], allow_beta=True), ], does_not_raise(), + True, None, FullAutoAttachResult(), ), @@ -338,11 +353,15 @@ def test_lock_held( mock.call(mock.ANY, ["cis"], allow_beta=True), ], pytest.raises(exceptions.EntitlementsNotEnabledError), + True, messages.ENTITLEMENTS_NOT_ENABLED_ERROR.msg, None, ), ], ) + @mock.patch( + M_PATH + "contract.UAContractClient.update_activity_token", + ) @mock.patch(M_PATH + "_enable_services_by_name") @mock.patch(M_PATH + "actions.auto_attach") @mock.patch(M_PATH + "actions.get_cloud_instance") @@ -353,6 +372,7 @@ def test_full_auto_attach_v1( m_get_cloud_instance, m_auto_attach, m_enable_services_by_name, + m_update_activity_token, _notice_remove, _notice_add, options, @@ -362,6 +382,7 @@ def test_full_auto_attach_v1( enable_services_by_name_side_effect, expected_enable_services_by_name_call_args, raise_expectation, + expect_activity_call, expected_error_message, expected_ret, mode, @@ -386,3 +407,7 @@ def test_full_auto_attach_v1( assert e.value.msg == expected_error_message if expected_ret is not None: assert ret == expected_ret + if expect_activity_call: + assert 1 == m_update_activity_token.call_count + else: + assert 0 == m_update_activity_token.call_count diff --git a/uaclient/api/u/pro/attach/auto/full_auto_attach/v1.py b/uaclient/api/u/pro/attach/auto/full_auto_attach/v1.py index 3c47db0405..c6c457524f 100644 --- a/uaclient/api/u/pro/attach/auto/full_auto_attach/v1.py +++ b/uaclient/api/u/pro/attach/auto/full_auto_attach/v1.py @@ -1,6 +1,6 @@ from typing import List, Optional, Tuple -from uaclient import actions, event_logger, lock, messages, util +from uaclient import actions, contract, event_logger, lock, messages, util from uaclient.api import exceptions from uaclient.api.api import APIEndpoint from uaclient.api.data_types import AdditionalInfo @@ -118,6 +118,9 @@ def _full_auto_attach_in_lock( cfg, options.enable_beta, allow_beta=True ) + contract_client = contract.UAContractClient(cfg) + contract_client.update_activity_token() + if len(failed) > 0: raise exceptions.EntitlementsNotEnabledError(failed) diff --git a/uaclient/cli.py b/uaclient/cli.py index 61bdba0c86..235b8ab8fb 100644 --- a/uaclient/cli.py +++ b/uaclient/cli.py @@ -1557,6 +1557,10 @@ def action_attach(args, *, cfg): event.info(msg.msg, file_type=sys.stderr) event.error(error_msg=msg.msg, error_code=msg.name) ret = 1 + + contract_client = contract.UAContractClient(cfg) + contract_client.update_activity_token() + _post_cli_attach(cfg) return ret diff --git a/uaclient/conftest.py b/uaclient/conftest.py index d493d4047a..bcfb3840e6 100644 --- a/uaclient/conftest.py +++ b/uaclient/conftest.py @@ -69,6 +69,19 @@ def util_we_are_currently_root(): yield original +@pytest.yield_fixture(scope="session", autouse=True) +def urllib_request_urlopen(): + """ + A fixture that mocks urlopen for all tests. + This prevents us from accidentally making requests in unit tests + """ + from urllib.request import urlopen + + original = urlopen + with mock.patch("urllib.request.urlopen"): + yield original + + @pytest.fixture def caplog_text(request): """ diff --git a/uaclient/tests/test_actions.py b/uaclient/tests/test_actions.py index 329b6b3acc..2e3cc694f6 100644 --- a/uaclient/tests/test_actions.py +++ b/uaclient/tests/test_actions.py @@ -43,6 +43,9 @@ class TestAttachWithToken: ), ], ) + @mock.patch( + M_PATH + "contract.UAContractClient.update_activity_token", + ) @mock.patch("uaclient.files.state_files.attachment_data_file.write") @mock.patch(M_PATH + "identity.get_instance_id", return_value="my-iid") @mock.patch("uaclient.timer.update_messaging.update_motd_messages") @@ -57,6 +60,7 @@ def test_attach_with_token( m_update_apt_and_motd_msgs, _m_get_instance_id, m_attachment_data_file_write, + m_update_activity_token, request_updated_contract_side_effect, expected_error_class, expect_status_call, @@ -69,6 +73,7 @@ def test_attach_with_token( if expected_error_class: with pytest.raises(expected_error_class): attach_with_token(cfg, "token", False) + assert 1 == m_update_activity_token.call_count else: attach_with_token(cfg, "token", False) if expect_status_call: diff --git a/uaclient/tests/test_cli_attach.py b/uaclient/tests/test_cli_attach.py index e898c3703d..228c717ec7 100644 --- a/uaclient/tests/test_cli_attach.py +++ b/uaclient/tests/test_cli_attach.py @@ -281,6 +281,9 @@ def fake_request_updated_contract( ), "Did not persist on disk status during attach failure" assert [mock.call(cfg)] == m_update_apt_and_motd_msgs.call_args_list + @mock.patch( + M_PATH + "contract.UAContractClient.update_activity_token", + ) @mock.patch("uaclient.files.state_files.attachment_data_file.write") @mock.patch("uaclient.system.should_reboot", return_value=False) @mock.patch("uaclient.files.notices.NoticesManager.remove") @@ -297,6 +300,7 @@ def test_happy_path_with_token_arg( _m_remove_notice, _m_should_reboot, _m_attachment_data_file_write, + m_update_activity_token, FakeConfig, event, ): @@ -323,6 +327,7 @@ def fake_contract_attach(contract_token, attachment_dt): ] assert expected_calls == contract_machine_attach.call_args_list assert [mock.call(cfg)] == m_update_apt_and_motd_msgs.call_args_list + assert 1 == m_update_activity_token.call_count # We need to do that since all config objects in this # test will share the same data dir. Since this will @@ -357,6 +362,9 @@ def fake_contract_attach(contract_token, attachment_dt): assert expected == json.loads(fake_stdout.getvalue()) @pytest.mark.parametrize("auto_enable", (True, False)) + @mock.patch( + M_PATH + "contract.UAContractClient.update_activity_token", + ) @mock.patch("uaclient.files.state_files.attachment_data_file.write") @mock.patch("uaclient.system.should_reboot", return_value=False) @mock.patch("uaclient.files.notices.NoticesManager.remove") @@ -369,6 +377,7 @@ def test_auto_enable_passed_through_to_request_updated_contract( _m_remove_notice, _m_should_reboot, _m_attachment_data_file_write, + _m_update_activity_token, auto_enable, FakeConfig, ): @@ -406,12 +415,16 @@ def test_attach_config_and_token_mutually_exclusive( action_attach(args, cfg=cfg) assert e.value.msg == messages.ATTACH_TOKEN_ARG_XOR_CONFIG.msg + @mock.patch( + M_PATH + "contract.UAContractClient.update_activity_token", + ) @mock.patch(M_PATH + "_post_cli_attach") @mock.patch(M_PATH + "actions.attach_with_token") def test_token_from_attach_config( self, m_attach_with_token, _m_post_cli_attach, + m_update_activity_token, FakeConfig, ): args = mock.MagicMock( @@ -423,6 +436,7 @@ def test_token_from_attach_config( assert [ mock.call(mock.ANY, token="faketoken", allow_enable=True) ] == m_attach_with_token.call_args_list + assert 1 == m_update_activity_token.call_count def test_attach_config_invalid_config( self, @@ -480,6 +494,9 @@ def test_attach_config_invalid_config( assert expected == json.loads(capsys.readouterr()[0]) @pytest.mark.parametrize("auto_enable", (True, False)) + @mock.patch( + M_PATH + "contract.UAContractClient.update_activity_token", + ) @mock.patch( M_PATH + "actions.enable_entitlement_by_name", return_value=(True, None), @@ -499,6 +516,7 @@ def test_attach_config_enable_services( m_handle_unicode, m_attach_with_token, m_enable, + m_update_activity_token, auto_enable, FakeConfig, event, @@ -525,6 +543,7 @@ def test_attach_config_enable_services( ] == m_enable.call_args_list else: assert [] == m_enable.call_args_list + assert 1 == m_update_activity_token.call_count args.attach_config = FakeFile( safe_dump({"token": "faketoken", "enable_services": ["cis"]})