Skip to content

Commit

Permalink
feat: Enable register with environment names and environment types
Browse files Browse the repository at this point in the history
* Allow to register also with environment names. It is possible
  to use environment names with username & password and
  activation-keys & organization authentication
* It is not allowed to use `environmets` and `environment_names`
  registration options together, because it is not possible to use
  env. IDs and names together on candlepin server
* Introduce environment_type as another registration_option
  * When consumer object is returned and it contains environments,
    then all environments are checked if type of environment
    matches given environment_type. If type is missing or is
    different, then system is unregistered and exception is
    raised
* Modified few unit tests related to environments
* Added few unit tests
  • Loading branch information
jirihnidek committed Dec 13, 2024
1 parent 5eee229 commit 7092ac7
Show file tree
Hide file tree
Showing 3 changed files with 141 additions and 4 deletions.
6 changes: 6 additions & 0 deletions src/rhsm/connection.py
Original file line number Diff line number Diff line change
Expand Up @@ -1536,6 +1536,7 @@ def registerConsumer(
facts: Optional[dict] = None,
owner: str = None,
environments: str = None,
environment_names: str = None,
keys: str = None,
installed_products: list = None,
uuid: str = None,
Expand Down Expand Up @@ -1581,6 +1582,11 @@ def registerConsumer(
for environment in environments.split(","):
env_list.append({"id": environment})
params["environments"] = env_list
elif environment_names is not None and self.has_capability(MULTI_ENV):
env_name_list = []
for env_name in environment_names.split(","):
env_name_list.append({"name": env_name})
params["environments"] = env_name_list

headers = {}
if jwt_token:
Expand Down
30 changes: 30 additions & 0 deletions src/rhsmlib/services/register.py
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,8 @@ def register(
org: Optional[str],
activation_keys: list = None,
environments: list = None,
environment_names: list = None,
environment_type: str = None,
force: bool = False,
name: str = None,
consumerid: str = None,
Expand All @@ -66,6 +68,9 @@ def register(
if kwargs:
raise exceptions.ValidationError(_("Unknown arguments: %s") % kwargs.keys())

if environments is not None and environment_names is not None:
raise exceptions.ValidationError(_("Environment IDs and environment names are mutually exclusive"))

syspurpose = syspurposelib.read_syspurpose()

save_syspurpose = False
Expand Down Expand Up @@ -95,6 +100,7 @@ def register(
options = {
"activation_keys": activation_keys,
"environments": environments,
"environment_names": environment_names,
"force": force,
"name": name,
"consumerid": consumerid,
Expand Down Expand Up @@ -123,6 +129,7 @@ def register(
facts=facts_dict,
owner=org,
environments=environments,
environment_names=environment_names,
keys=options.get("activation_keys"),
installed_products=self.installed_mgr.format_for_server(),
content_tags=self.installed_mgr.tags,
Expand All @@ -138,8 +145,31 @@ def register(
cp_provider: CPProvider = inj.require(inj.CP_PROVIDER)
cp_provider.close_all_connections()

# If environment type was specified, then check that all returned
# environments have required type. Otherwise, raise exception
wrong_type = False
wrong_env_name = None
if environment_type:
for environment in consumer.get("environments", []):
env_type = environment.get("type", None)
if env_type != environment_type:
wrong_type = True
wrong_env_name = environment["name"]
break

managerlib.persist_consumer_cert(consumer)

if wrong_type is True:
# We will not use this consumer object. Thus, delete this object
# on the server
self.identity.reload()
UnregisterService(inj.require(inj.CP_PROVIDER).get_consumer_auth_cp()).unregister()
raise exceptions.ServiceError(
_("Environment '{env_name}' does not have required type '{environment_type}'".format(
env_name=wrong_env_name, environment_type=environment_type
))
)

access_mode: str = consumer.get("owner", {}).get("contentAccessMode", "unknown")
if access_mode != "org_environment":
log.error(
Expand Down
109 changes: 105 additions & 4 deletions test/rhsmlib/services/test_register.py
Original file line number Diff line number Diff line change
Expand Up @@ -222,13 +222,14 @@ def test_register_normally(self, mock_persist_consumer, mock_write_cache):
self.mock_cp.registerConsumer.return_value = expected_consumer

register_service = register.RegisterService(self.mock_cp)
register_service.register("org", name="name", environments="environment")
register_service.register("org", name="name", environments=["environment"])

self.mock_cp.registerConsumer.assert_called_once_with(
name="name",
facts={},
owner="org",
environments="environment",
environments=["environment"],
environment_names=None,
keys=None,
installed_products=[],
jwt_token=None,
Expand All @@ -249,6 +250,103 @@ def test_register_normally(self, mock_persist_consumer, mock_write_cache):
]
self.assertEqual(expected_plugin_calls, self.mock_pm.run.call_args_list)

@mock.patch("rhsmlib.services.register.syspurposelib.write_syspurpose_cache", return_value=True)
@mock.patch("rhsmlib.services.register.managerlib.persist_consumer_cert")
def test_register_multiple_environment_ids(self, mock_persist_consumer, mock_write_cache):
self.mock_identity.is_valid.return_value = False
self.mock_installed_products.format_for_server.return_value = []
self.mock_installed_products.tags = []
expected_consumer = json.loads(CONSUMER_CONTENT_JSON)
self.mock_cp.registerConsumer.return_value = expected_consumer

register_service = register.RegisterService(self.mock_cp)
register_service.register("org", name="name", environments=["env-id-1", "env-id-2"])

self.mock_cp.registerConsumer.assert_called_once_with(
name="name",
facts={},
owner="org",
environments=["env-id-1", "env-id-2"],
environment_names=None,
keys=None,
installed_products=[],
jwt_token=None,
content_tags=[],
consumer_type="system",
role="",
addons=[],
service_level="",
usage="",
)
self.mock_installed_products.write_cache.assert_called()

mock_persist_consumer.assert_called_once_with(expected_consumer)
mock_write_cache.assert_called_once()
expected_plugin_calls = [
mock.call("pre_register_consumer", name="name", facts={}),
mock.call("post_register_consumer", consumer=expected_consumer, facts={}),
]
self.assertEqual(expected_plugin_calls, self.mock_pm.run.call_args_list)

@mock.patch("rhsmlib.services.register.syspurposelib.write_syspurpose_cache", return_value=True)
@mock.patch("rhsmlib.services.register.managerlib.persist_consumer_cert")
def test_register_multiple_environment_names(self, mock_persist_consumer, mock_write_cache):
self.mock_identity.is_valid.return_value = False
self.mock_installed_products.format_for_server.return_value = []
self.mock_installed_products.tags = []
expected_consumer = json.loads(CONSUMER_CONTENT_JSON)
self.mock_cp.registerConsumer.return_value = expected_consumer

register_service = register.RegisterService(self.mock_cp)
register_service.register("org", name="name", environment_names=["env-name-1", "env-name-2"])

self.mock_cp.registerConsumer.assert_called_once_with(
name="name",
facts={},
owner="org",
environments=None,
environment_names=["env-name-1", "env-name-2"],
keys=None,
installed_products=[],
jwt_token=None,
content_tags=[],
consumer_type="system",
role="",
addons=[],
service_level="",
usage="",
)
self.mock_installed_products.write_cache.assert_called()

mock_persist_consumer.assert_called_once_with(expected_consumer)
mock_write_cache.assert_called_once()
expected_plugin_calls = [
mock.call("pre_register_consumer", name="name", facts={}),
mock.call("post_register_consumer", consumer=expected_consumer, facts={}),
]
self.assertEqual(expected_plugin_calls, self.mock_pm.run.call_args_list)

@mock.patch("rhsmlib.services.register.syspurposelib.write_syspurpose_cache", return_value=True)
@mock.patch("rhsmlib.services.register.managerlib.persist_consumer_cert")
def test_register_not_allow_environment_ids_and_names(self, mock_persist_consumer, mock_write_cache):
self.mock_identity.is_valid.return_value = False
self.mock_installed_products.format_for_server.return_value = []
self.mock_installed_products.tags = []
expected_consumer = json.loads(CONSUMER_CONTENT_JSON)
self.mock_cp.registerConsumer.return_value = expected_consumer

register_service = register.RegisterService(self.mock_cp)
with self.assertRaisesRegex(
exceptions.ValidationError,
r".*Environment IDs and environment names are mutually exclusive.*"
):
register_service.register(
"org",
name="name",
environments=["env-id-1", "env-id-2"],
environment_names=["env-name-1", "env-name-2"]
)

@mock.patch("rhsmlib.services.register.syspurposelib.write_syspurpose_cache", return_value=True)
@mock.patch("rhsmlib.services.register.managerlib.clean_all_data", return_value=None)
@mock.patch("rhsmlib.services.register.managerlib.persist_consumer_cert")
Expand Down Expand Up @@ -341,13 +439,14 @@ def _no_owner_cb(username):

self.assertIsNotNone(org)

register_service.register(org, name="name", environments="environment")
register_service.register(org, name="name")

self.mock_cp.registerConsumer.assert_called_once_with(
name="name",
facts={},
owner="snowwhite",
environments="environment",
environments=None,
environment_names=None,
keys=None,
installed_products=[],
jwt_token=None,
Expand Down Expand Up @@ -388,6 +487,7 @@ def test_register_with_activation_keys(self, mock_persist_consumer, mock_write_c
facts={},
owner="org",
environments=None,
environment_names=None,
keys=[1],
installed_products=[],
jwt_token=None,
Expand Down Expand Up @@ -473,6 +573,7 @@ def test_reads_syspurpose(self, mock_persist_consumer, mock_write_cache):
addons=["addon1"],
content_tags=[],
environments=None,
environment_names=None,
facts={},
installed_products=[],
jwt_token=None,
Expand Down

0 comments on commit 7092ac7

Please sign in to comment.