Skip to content

Commit

Permalink
contract: support old airgapped server machineInfo
Browse files Browse the repository at this point in the history
  • Loading branch information
orndorffgrant committed Jul 17, 2023
1 parent 8055ba7 commit ffc1175
Show file tree
Hide file tree
Showing 3 changed files with 121 additions and 12 deletions.
1 change: 1 addition & 0 deletions features/airgapped.feature
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,7 @@ Feature: Performing attach using ua-airgapped
"""
500 .*:8000/ubuntu jammy-infra-security/main
"""
Then I verify that running `pro refresh` `with sudo` exits `0`

Examples: ubuntu release
| release |
Expand Down
33 changes: 31 additions & 2 deletions uaclient/contract.py
Original file line number Diff line number Diff line change
Expand Up @@ -78,8 +78,9 @@ def add_contract_machine(
activity_info = self._get_activity_info()
activity_info["lastAttachment"] = attachment_dt.isoformat()
data = {"machineId": machine_id, "activityInfo": activity_info}
backcompat_data = _support_old_machine_info(data)
response = self.request_url(
API_V1_ADD_CONTRACT_MACHINE, data=data, headers=headers
API_V1_ADD_CONTRACT_MACHINE, data=backcompat_data, headers=headers
)
if response.code == 401:
raise exceptions.AttachInvalidTokenError()
Expand Down Expand Up @@ -380,11 +381,12 @@ def update_contract_machine(
"machineId": machine_id,
"activityInfo": self._get_activity_info(),
}
backcompat_data = _support_old_machine_info(data)
url = API_V1_UPDATE_CONTRACT_MACHINE.format(
contract=contract_id, machine=machine_id
)
response = self.request_url(
url, headers=headers, method="POST", data=data
url, headers=headers, method="POST", data=backcompat_data
)
if response.code != 200:
raise exceptions.ContractAPIError(
Expand Down Expand Up @@ -427,6 +429,33 @@ def _get_activity_info(self):
}


def _support_old_machine_info(request_body: dict):
"""
Transforms a request_body that has the new activity_info into a body that
includes both old and new forms of machineInfo/activityInfo
This is necessary because there may be old ua-airgapped contract
servers deployed that we need to support.
This function is used for attach and refresh calls.
"""
activity_info = request_body.get("activityInfo", {})

return {
"machineId": request_body.get("machineId"),
"activityInfo": activity_info,
"architecture": activity_info.get("architecture"),
"os": {
"distribution": activity_info.get("distribution"),
"kernel": activity_info.get("kernel"),
"series": activity_info.get("series"),
# These two are required for old ua-airgapped but not sent anymore
# in the new activityInfo
"type": "Linux",
"release": system.get_release_info().release,
},
}


def process_entitlements_delta(
cfg: UAConfig,
past_entitlements: Dict[str, Any],
Expand Down
99 changes: 89 additions & 10 deletions uaclient/tests/test_contract.py
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@
UAContractClient,
_create_attach_forbidden_message,
_get_override_weight,
_support_old_machine_info,
apply_contract_overrides,
get_available_resources,
get_contract_information,
Expand Down Expand Up @@ -103,12 +104,14 @@ class TestUAContractClient:
),
],
)
@mock.patch("uaclient.contract._support_old_machine_info")
@mock.patch("uaclient.contract.UAContractClient._get_activity_info")
@mock.patch("uaclient.contract.UAContractClient.headers")
def test_update_contract_machine(
self,
m_headers,
m_get_activity_info,
m_support_old_machine_info,
m_get_machine_id,
m_request_url,
machine_id,
Expand All @@ -119,6 +122,9 @@ def test_update_contract_machine(
):
m_headers.return_value = {"header": "headerval"}
m_get_activity_info.return_value = mock.sentinel.activity_info
m_support_old_machine_info.return_value = (
mock.sentinel.support_old_machine_info
)
m_request_url.side_effect = request_url_side_effect
m_get_machine_id.return_value = "get_machine_id"

Expand All @@ -131,6 +137,14 @@ def test_update_contract_machine(
if expected_result:
assert expected_result == result

assert [
mock.call(
{
"machineId": expected_machine_id,
"activityInfo": mock.sentinel.activity_info,
}
)
] == m_support_old_machine_info.call_args_list
assert [
mock.call(
"/v1/contracts/cId/context/machines/" + expected_machine_id,
Expand All @@ -139,10 +153,7 @@ def test_update_contract_machine(
"Authorization": "Bearer mToken",
},
method="POST",
data={
"machineId": expected_machine_id,
"activityInfo": mock.sentinel.activity_info,
},
data=mock.sentinel.support_old_machine_info,
)
] == m_request_url.call_args_list

Expand Down Expand Up @@ -420,12 +431,14 @@ def test_update_activity_token(
],
)
@mock.patch("uaclient.contract._create_attach_forbidden_message")
@mock.patch("uaclient.contract._support_old_machine_info")
@mock.patch("uaclient.contract.UAContractClient._get_activity_info")
@mock.patch("uaclient.contract.UAContractClient.headers")
def test_add_contract_machine(
self,
m_headers,
m_get_activity_info,
m_support_old_machine_info,
m_create_attach_forbidden_message,
m_get_machine_id,
m_request_url,
Expand All @@ -440,6 +453,9 @@ def test_add_contract_machine(
m_get_activity_info.return_value = {
"activity": "info",
}
m_support_old_machine_info.return_value = (
mock.sentinel.support_old_machine_info
)
m_request_url.side_effect = request_url_side_effect
m_get_machine_id.return_value = mock.sentinel.get_machine_id

Expand All @@ -458,18 +474,23 @@ def test_add_contract_machine(

assert [
mock.call(
"/v1/context/machines/token",
headers={
"header": "headerval",
"Authorization": "Bearer cToken",
},
data={
{
"machineId": expected_machine_id,
"activityInfo": {
"activity": "info",
"lastAttachment": "2000-01-02T03:04:05+00:00",
},
}
)
] == m_support_old_machine_info.call_args_list
assert [
mock.call(
"/v1/context/machines/token",
headers={
"header": "headerval",
"Authorization": "Bearer cToken",
},
data=mock.sentinel.support_old_machine_info,
)
] == m_request_url.call_args_list
assert (
Expand Down Expand Up @@ -850,6 +871,64 @@ def test_get_activity_info(
assert expected == client._get_activity_info()


class TestSupportOldMachineInfo:
@pytest.mark.parametrize(
[
"request_body",
"release_info",
"expected_result",
],
[
(
{
"machineId": "mach",
"activityInfo": {
"architecture": "arch",
"distribution": "dist",
"kernel": "kern",
"series": "seri",
"other": "othe",
},
},
system.ReleaseInfo(
distribution="",
release="rele",
series="",
pretty_version="",
),
{
"machineId": "mach",
"activityInfo": {
"architecture": "arch",
"distribution": "dist",
"kernel": "kern",
"series": "seri",
"other": "othe",
},
"architecture": "arch",
"os": {
"distribution": "dist",
"kernel": "kern",
"series": "seri",
"type": "Linux",
"release": "rele",
},
},
),
],
)
@mock.patch(M_PATH + "system.get_release_info")
def test_support_old_machine_info(
self,
m_get_release_info,
request_body,
release_info,
expected_result,
):
m_get_release_info.return_value = release_info
assert expected_result == _support_old_machine_info(request_body)


class TestProcessEntitlementDeltas:
def test_error_on_missing_entitlement_type(self, FakeConfig):
"""Raise an error when neither dict contains entitlement type."""
Expand Down

0 comments on commit ffc1175

Please sign in to comment.