diff --git a/awx/main/models/credential.py b/awx/main/models/credential.py index a035718202..a4bd1061bf 100644 --- a/awx/main/models/credential.py +++ b/awx/main/models/credential.py @@ -531,6 +531,7 @@ class CredentialType(CommonModelNameNotUnique): existing = ct_class.objects.filter(name=default.name, kind=default.kind).first() if existing is not None: existing.namespace = default.namespace + existing.description = getattr(default, 'description', '') existing.inputs = {} existing.injectors = {} existing.save() @@ -570,7 +571,14 @@ class CredentialType(CommonModelNameNotUnique): @classmethod def load_plugin(cls, ns, plugin): # TODO: User "side-loaded" credential custom_injectors isn't supported - ManagedCredentialType.registry[ns] = SimpleNamespace(namespace=ns, name=plugin.name, kind='external', inputs=plugin.inputs, backend=plugin.backend) + ManagedCredentialType.registry[ns] = SimpleNamespace( + namespace=ns, + name=plugin.name, + kind='external', + inputs=plugin.inputs, + backend=plugin.backend, + description=getattr(plugin, 'plugin_description', ''), + ) def inject_credential(self, credential, env, safe_env, args, private_data_dir, container_root=None): from awx_plugins.interfaces._temporary_private_inject_api import inject_credential @@ -582,7 +590,13 @@ class CredentialTypeHelper: @classmethod def get_creation_params(cls, cred_type): if cred_type.kind == 'external': - return dict(namespace=cred_type.namespace, kind=cred_type.kind, name=cred_type.name, managed=True) + return { + 'namespace': cred_type.namespace, + 'kind': cred_type.kind, + 'name': cred_type.name, + 'managed': True, + 'description': getattr(cred_type, 'description', ''), + } return dict( namespace=cred_type.namespace, kind=cred_type.kind, diff --git a/awx/main/tests/unit/models/test_credential.py b/awx/main/tests/unit/models/test_credential.py index 778029ffa4..437945efc2 100644 --- a/awx/main/tests/unit/models/test_credential.py +++ b/awx/main/tests/unit/models/test_credential.py @@ -2,7 +2,11 @@ import pytest +from types import SimpleNamespace +from unittest import mock + from awx.main.models import Credential, CredentialType +from awx.main.models.credential import CredentialTypeHelper, ManagedCredentialType from django.apps import apps @@ -78,3 +82,53 @@ def test_credential_context_property_independent_instances(): assert cred1.context == {'key1': 'value1'} assert cred2.context == {'key2': 'value2'} assert cred1.context is not cred2.context + + +def test_load_plugin_passes_description(): + plugin = SimpleNamespace(name='test_plugin', inputs={'fields': []}, backend=None, plugin_description='A test plugin') + CredentialType.load_plugin('test_ns', plugin) + entry = ManagedCredentialType.registry['test_ns'] + assert entry.description == 'A test plugin' + del ManagedCredentialType.registry['test_ns'] + + +def test_load_plugin_missing_description(): + plugin = SimpleNamespace(name='test_plugin', inputs={'fields': []}, backend=None) + CredentialType.load_plugin('test_ns', plugin) + entry = ManagedCredentialType.registry['test_ns'] + assert entry.description == '' + del ManagedCredentialType.registry['test_ns'] + + +def test_get_creation_params_external_includes_description(): + cred_type = SimpleNamespace(namespace='test_ns', kind='external', name='Test', description='My description') + params = CredentialTypeHelper.get_creation_params(cred_type) + assert params['description'] == 'My description' + + +def test_get_creation_params_external_missing_description(): + cred_type = SimpleNamespace(namespace='test_ns', kind='external', name='Test') + params = CredentialTypeHelper.get_creation_params(cred_type) + assert params['description'] == '' + + +@pytest.mark.django_db +def test_setup_tower_managed_defaults_updates_description(): + registry_entry = SimpleNamespace( + namespace='test_ns', + kind='external', + name='Test Plugin', + inputs={'fields': []}, + backend=None, + description='Updated description', + ) + # Create an existing credential type with no description + ct = CredentialType.objects.create(name='Test Plugin', kind='external', namespace='old_ns') + assert ct.description == '' + + with mock.patch.dict(ManagedCredentialType.registry, {'test_ns': registry_entry}, clear=True): + CredentialType._setup_tower_managed_defaults() + + ct.refresh_from_db() + assert ct.description == 'Updated description' + assert ct.namespace == 'test_ns'