diff --git a/awx_collection/plugins/modules/project.py b/awx_collection/plugins/modules/project.py index ed0d475b1481..b093e4f9bc59 100644 --- a/awx_collection/plugins/modules/project.py +++ b/awx_collection/plugins/modules/project.py @@ -247,6 +247,18 @@ def wait_for_project_update(module, last_request): def main(): + """Entry point for the project module. + + Builds the argument spec, resolves related resources (organization, + execution environment, credential, signature_validation_credential, + notification templates), and creates, updates, or deletes the + Automation Platform Controller project accordingly. + + For credential and signature_validation_credential specifically: an + empty string ("") is treated as an explicit request to clear the + field, mapping it to None instead of attempting (and failing) to + resolve a credential literally named "". + """ # Any additional arguments that are not fields of the item can be added here argument_spec = dict( name=dict(required=True), @@ -383,7 +395,7 @@ def main(): (signature_validation_credential, 'signature_validation_credential', 'credentials'), ): if variable is not None: - project_fields[field] = module.resolve_name_to_id(endpoint, variable) + project_fields[field] = module.resolve_name_to_id(endpoint, variable) if variable else None if org_id is not None: # this is resolved earlier, so save an API call and don't do it again in the loop above diff --git a/awx_collection/test/awx/test_project.py b/awx_collection/test/awx/test_project.py index 27b7b96e05d6..d565dc1950b5 100644 --- a/awx_collection/test/awx/test_project.py +++ b/awx_collection/test/awx/test_project.py @@ -66,3 +66,40 @@ def test_create_project_copy_from(run_module, admin_user, organization, silence_ admin_user, ) silence_warning.assert_called_with("A project with the name {0} already exists.".format(proj_name)) + + +@pytest.mark.django_db +def test_clear_project_credential(run_module, admin_user, organization, scm_credential): + result = run_module( + 'project', + dict( + name='foo', + organization=organization.name, + scm_type='git', + scm_url='https://foo.invalid', + credential=scm_credential.name, + wait=False, + ), + admin_user, + ) + assert result.pop('changed', None), result + + proj = Project.objects.get(name='foo') + assert proj.credential_id == scm_credential.id + + result = run_module( + 'project', + dict( + name='foo', + organization=organization.name, + scm_type='git', + scm_url='https://foo.invalid', + credential='', + wait=False, + ), + admin_user, + ) + assert result.pop('changed', None), result + + proj.refresh_from_db() + assert proj.credential_id is None