diff --git a/awx/api/serializers.py b/awx/api/serializers.py index e3bc187531..1084ac8bbd 100644 --- a/awx/api/serializers.py +++ b/awx/api/serializers.py @@ -84,7 +84,7 @@ SUMMARIZABLE_FK_FIELDS = { 'groups_with_active_failures', 'has_inventory_sources'), 'project': DEFAULT_SUMMARY_FIELDS + ('status', 'scm_type'), - 'scm_project': DEFAULT_SUMMARY_FIELDS + ('status', 'scm_type'), + 'source_project': DEFAULT_SUMMARY_FIELDS + ('status', 'scm_type'), 'project_update': DEFAULT_SUMMARY_FIELDS + ('status', 'failed',), 'credential': DEFAULT_SUMMARY_FIELDS + ('kind', 'cloud'), 'cloud_credential': DEFAULT_SUMMARY_FIELDS + ('kind', 'cloud'), @@ -975,7 +975,7 @@ class ProjectSerializer(UnifiedJobTemplateSerializer, ProjectOptionsSerializer): inventory_files = self.reverse('api:project_inventories', kwargs={'pk': obj.pk}), update = self.reverse('api:project_update_view', kwargs={'pk': obj.pk}), project_updates = self.reverse('api:project_updates_list', kwargs={'pk': obj.pk}), - scm_inventories = self.reverse('api:project_scm_inventory_sources', kwargs={'pk': obj.pk}), + scm_inventory_sources = self.reverse('api:project_scm_inventory_sources', kwargs={'pk': obj.pk}), schedules = self.reverse('api:project_schedules_list', kwargs={'pk': obj.pk}), activity_stream = self.reverse('api:project_activity_stream_list', kwargs={'pk': obj.pk}), notification_templates_any = self.reverse('api:project_notification_templates_any_list', kwargs={'pk': obj.pk}), @@ -1526,7 +1526,7 @@ class InventorySourceSerializer(UnifiedJobTemplateSerializer, InventorySourceOpt class Meta: model = InventorySource fields = ('*', 'name', 'inventory', 'update_on_launch', 'update_cache_timeout', - 'scm_project', 'update_on_project_update') + \ + 'source_project', 'update_on_project_update') + \ ('last_update_failed', 'last_updated', 'group') # Backwards compatibility. def get_related(self, obj): @@ -1544,8 +1544,8 @@ class InventorySourceSerializer(UnifiedJobTemplateSerializer, InventorySourceOpt )) if obj.inventory: res['inventory'] = self.reverse('api:inventory_detail', kwargs={'pk': obj.inventory.pk}) - if obj.scm_project_id is not None: - res['scm_project'] = self.reverse('api:project_detail', kwargs={'pk': obj.scm_project.pk}) + if obj.source_project_id is not None: + res['source_project'] = self.reverse('api:project_detail', kwargs={'pk': obj.source_project.pk}) # Backwards compatibility. if obj.current_update: res['current_update'] = self.reverse('api:inventory_update_detail', @@ -1584,7 +1584,7 @@ class InventorySourceSerializer(UnifiedJobTemplateSerializer, InventorySourceOpt def build_relational_field(self, field_name, relation_info): field_class, field_kwargs = super(InventorySourceSerializer, self).build_relational_field(field_name, relation_info) # SCM Project and inventory are read-only unless creating a new inventory. - if self.instance and field_name in ['scm_project', 'inventory']: + if self.instance and field_name in ['source_project', 'inventory']: field_kwargs['read_only'] = True field_kwargs.pop('queryset', None) return field_class, field_kwargs @@ -1628,7 +1628,7 @@ class InventoryUpdateSerializer(UnifiedJobSerializer, InventorySourceOptionsSeri class Meta: model = InventoryUpdate - fields = ('*', 'inventory_source', 'license_error') + fields = ('*', 'inventory_source', 'license_error', 'source_project_update') def get_related(self, obj): res = super(InventoryUpdateSerializer, self).get_related(obj) @@ -1637,6 +1637,9 @@ class InventoryUpdateSerializer(UnifiedJobSerializer, InventorySourceOptionsSeri cancel = self.reverse('api:inventory_update_cancel', kwargs={'pk': obj.pk}), notifications = self.reverse('api:inventory_update_notifications_list', kwargs={'pk': obj.pk}), )) + if obj.source_project_update_id: + res['source_project_update'] = self.reverse('api:project_update_detail', + kwargs={'pk': obj.source_project_update.pk}) return res diff --git a/awx/api/views.py b/awx/api/views.py index 40ddefc472..c496f99d9f 100644 --- a/awx/api/views.py +++ b/awx/api/views.py @@ -1127,7 +1127,7 @@ class ProjectScmInventorySources(SubListCreateAPIView): serializer_class = InventorySourceSerializer parent_model = Project relationship = 'scm_inventory_sources' - parent_key = 'scm_project' + parent_key = 'source_project' new_in_320 = True @@ -1263,7 +1263,7 @@ class ProjectUpdateScmInventoryUpdates(SubListCreateAPIView): serializer_class = InventoryUpdateSerializer parent_model = ProjectUpdate relationship = 'scm_inventory_updates' - parent_key = 'scm_project_update' + parent_key = 'source_project_update' new_in_320 = True @@ -2406,8 +2406,8 @@ class InventorySourceUpdateView(RetrieveAPIView): if obj.source == 'scm' and obj.update_on_project_update: if not self.request.user or self.request.user.can_access(self.model, 'update', obj): raise PermissionDenied(detail=_( - 'You do not have permission to update project `{}`.'.format(obj.scm_project.name))) - return self._build_update_response(obj.scm_project.update(), request) + 'You do not have permission to update project `{}`.'.format(obj.source_project.name))) + return self._build_update_response(obj.source_project.update(), request) return self._build_update_response(obj.update(), request) else: return self.http_method_not_allowed(request, *args, **kwargs) diff --git a/awx/main/access.py b/awx/main/access.py index 015c7d3dd8..de118499c5 100644 --- a/awx/main/access.py +++ b/awx/main/access.py @@ -743,7 +743,7 @@ class InventorySourceAccess(BaseAccess): def can_add(self, data): if not data or 'inventory' not in data: return False - if not self.check_related('scm_project', Project, data, role_field='admin_role'): + if not self.check_related('source_project', Project, data, role_field='admin_role'): return False # Checks for admin or change permission on inventory. return self.check_related('inventory', Inventory, data) diff --git a/awx/main/migrations/0038_v320_release.py b/awx/main/migrations/0038_v320_release.py index 443c6d1caa..3abf394adf 100644 --- a/awx/main/migrations/0038_v320_release.py +++ b/awx/main/migrations/0038_v320_release.py @@ -81,12 +81,12 @@ class Migration(migrations.Migration): ), migrations.AddField( model_name='inventorysource', - name='scm_project', + name='source_project', field=models.ForeignKey(related_name='scm_inventory_sources', default=None, blank=True, to='main.Project', help_text='Project containing inventory file used as source.', null=True), ), migrations.AddField( model_name='inventoryupdate', - name='scm_project_update', + name='source_project_update', field=models.ForeignKey(related_name='scm_inventory_updates', default=None, blank=True, to='main.ProjectUpdate', help_text='Inventory files from this Project Update were used for the inventory update.', null=True), ), migrations.AddField( diff --git a/awx/main/models/inventory.py b/awx/main/models/inventory.py index d6ed975545..3cd94c3446 100644 --- a/awx/main/models/inventory.py +++ b/awx/main/models/inventory.py @@ -1122,7 +1122,7 @@ class InventorySource(UnifiedJobTemplate, InventorySourceOptions): on_delete=models.CASCADE, ) - scm_project = models.ForeignKey( + source_project = models.ForeignKey( 'Project', related_name='scm_inventory_sources', help_text=_('Project containing inventory file used as source.'), @@ -1155,7 +1155,7 @@ class InventorySource(UnifiedJobTemplate, InventorySourceOptions): def _get_unified_job_field_names(cls): return ['name', 'description', 'source', 'source_path', 'source_script', 'source_vars', 'schedule', 'credential', 'source_regions', 'instance_filters', 'group_by', 'overwrite', 'overwrite_vars', - 'timeout', 'verbosity', 'launch_type', 'scm_project_update',] + 'timeout', 'verbosity', 'launch_type', 'source_project_update',] def save(self, *args, **kwargs): # If update_fields has been specified, add our field names to it, @@ -1180,7 +1180,7 @@ class InventorySource(UnifiedJobTemplate, InventorySourceOptions): # Reset revision if SCM source has changed parameters if self.source=='scm' and not is_new_instance: before_is = self.__class__.objects.get(pk=self.pk) - if before_is.source_path != self.source_path or before_is.scm_project_id != self.scm_project_id: + if before_is.source_path != self.source_path or before_is.source_project_id != self.source_project_id: # Reset the scm_revision if file changed to force update self.scm_revision = None if 'scm_revision' not in update_fields: @@ -1195,9 +1195,9 @@ class InventorySource(UnifiedJobTemplate, InventorySourceOptions): super(InventorySource, self).save(update_fields=['name']) if self.source=='scm' and is_new_instance and self.update_on_project_update: # Schedule a new Project update if one is not already queued - if self.scm_project and not self.scm_project.project_updates.filter( + if self.source_project and not self.source_project.project_updates.filter( status__in=['new', 'pending', 'waiting']).exists(): - self.scm_project.update() + self.source_project.update() if not getattr(_inventory_updates, 'is_updating', False): if self.inventory is not None: self.inventory.update_computed_fields(update_groups=False, update_hosts=False) @@ -1221,7 +1221,7 @@ class InventorySource(UnifiedJobTemplate, InventorySourceOptions): if self.source == 'custom': return bool(self.source_script) elif self.source == 'scm': - return bool(self.scm_project) + return bool(self.source_project) else: return bool(self.source in CLOUD_INVENTORY_SOURCES) @@ -1294,7 +1294,7 @@ class InventoryUpdate(UnifiedJob, InventorySourceOptions, JobNotificationMixin): default=False, editable=False, ) - scm_project_update = models.ForeignKey( + source_project_update = models.ForeignKey( 'ProjectUpdate', related_name='scm_inventory_updates', help_text=_('Inventory files from this Project Update were used for the inventory update.'), @@ -1344,10 +1344,10 @@ class InventoryUpdate(UnifiedJob, InventorySourceOptions, JobNotificationMixin): def get_actual_source_path(self): '''Alias to source_path that combines with project path for for SCM file based sources''' - if self.inventory_source_id is None or self.inventory_source.scm_project_id is None: + if self.inventory_source_id is None or self.inventory_source.source_project_id is None: return self.source_path return os.path.join( - self.inventory_source.scm_project.get_project_path(check_if_exists=False), + self.inventory_source.source_project.get_project_path(check_if_exists=False), self.source_path) @property @@ -1364,7 +1364,7 @@ class InventoryUpdate(UnifiedJob, InventorySourceOptions, JobNotificationMixin): if (self.source not in ('custom', 'ec2', 'scm') and not (self.credential)): return False - elif self.source == 'scm' and not self.inventory_source.scm_project: + elif self.source == 'scm' and not self.inventory_source.source_project: return False elif self.source == 'file': return False diff --git a/awx/main/tasks.py b/awx/main/tasks.py index 893220b3ea..3104527b96 100644 --- a/awx/main/tasks.py +++ b/awx/main/tasks.py @@ -1128,7 +1128,7 @@ class RunJob(BaseTask): job_request_id = '' if self.request.id is None else self.request.id local_project_sync = job.project.create_project_update( launch_type="sync", - _eager_params=dict( + _eager_fields=dict( job_type='run', status='running', celery_task_id=job_request_id)) @@ -1367,11 +1367,11 @@ class RunProjectUpdate(BaseTask): 'another update is already active.'.format(inv.name)) continue local_inv_update = inv_src.create_inventory_update( - scm_project_update_id=project_update.id, launch_type='scm', _eager_fields=dict( status='running', - celery_task_id=str(project_request_id))) + celery_task_id=str(project_request_id), + source_project_update=project_update)) inv_update_task = local_inv_update._get_task_class() try: task_instance = inv_update_task() @@ -1443,7 +1443,7 @@ class RunProjectUpdate(BaseTask): # Update any inventories that depend on this project if len(dependent_inventory_sources) > 0: if status == 'successful' and instance.launch_type != 'sync': - self._update_dependent_inventories(p, dependent_inventory_sources) + self._update_dependent_inventories(instance, dependent_inventory_sources) class RunInventoryUpdate(BaseTask): @@ -1802,15 +1802,15 @@ class RunInventoryUpdate(BaseTask): def pre_run_hook(self, inventory_update, **kwargs): self.custom_dir_path = [] - scm_project = None + source_project = None if inventory_update.inventory_source: - scm_project = inventory_update.inventory_source.scm_project - if (inventory_update.source=='scm' and scm_project and + source_project = inventory_update.inventory_source.source_project + if (inventory_update.source=='scm' and source_project and not inventory_update.inventory_source.update_on_project_update): request_id = '' if self.request.id is None else self.request.id - local_project_sync = scm_project.create_project_update( + local_project_sync = source_project.create_project_update( launch_type="sync", - _eager_params=dict( + _eager_fields=dict( job_type='run', status='running', celery_task_id=request_id)) diff --git a/awx/main/tests/functional/api/test_inventory.py b/awx/main/tests/functional/api/test_inventory.py index 03d5dc78b4..fcddef40d6 100644 --- a/awx/main/tests/functional/api/test_inventory.py +++ b/awx/main/tests/functional/api/test_inventory.py @@ -203,7 +203,7 @@ def test_inventory_source_update(post, inventory_source, alice, role_field, expe def scm_inventory(inventory, project): inventory.inventory_sources.create( name='foobar', update_on_project_update=True, source='scm', - scm_project=project) + source_project=project) return inventory diff --git a/awx/main/tests/functional/conftest.py b/awx/main/tests/functional/conftest.py index 8196d0527e..c6e54773ce 100644 --- a/awx/main/tests/functional/conftest.py +++ b/awx/main/tests/functional/conftest.py @@ -237,7 +237,7 @@ def inventory(organization): def scm_inventory_source(inventory, project): return InventorySource.objects.create( name="test-scm-inv", - scm_project=project, + source_project=project, source='scm', source_path='inventory_file', update_on_project_update=True, diff --git a/awx/main/tests/functional/models/test_inventory.py b/awx/main/tests/functional/models/test_inventory.py index 861ff7c607..2b42a30d1c 100644 --- a/awx/main/tests/functional/models/test_inventory.py +++ b/awx/main/tests/functional/models/test_inventory.py @@ -14,12 +14,12 @@ class TestSCMUpdateFeatures: def test_automatic_project_update_on_create(self, inventory, project): inv_src = InventorySource( - scm_project=project, + source_project=project, source_path='inventory_file', inventory=inventory, update_on_project_update=True, source='scm') - with mock.patch.object(inv_src.scm_project, 'update') as mck_update: + with mock.patch.object(inv_src.source_project, 'update') as mck_update: inv_src.save() mck_update.assert_called_once_with() @@ -32,7 +32,7 @@ class TestSCMUpdateFeatures: def test_no_unwanted_updates(self, scm_inventory_source): # Changing the non-sensitive fields should not trigger update - with mock.patch.object(scm_inventory_source.scm_project, 'update') as mck_update: + with mock.patch.object(scm_inventory_source.source_project, 'update') as mck_update: scm_inventory_source.name = 'edited_inventory' scm_inventory_source.description = "I'm testing this!" scm_inventory_source.save() diff --git a/awx/main/tests/functional/test_tasks.py b/awx/main/tests/functional/test_tasks.py index 6d6d21819d..ee80e85a7a 100644 --- a/awx/main/tests/functional/test_tasks.py +++ b/awx/main/tests/functional/test_tasks.py @@ -21,10 +21,10 @@ class TestDependentInventoryUpdate: def test_dependent_inventory_updates_is_called(self, scm_inventory_source, scm_revision_file): task = RunProjectUpdate() task.revision_path = scm_revision_file - proj_update = ProjectUpdate.objects.create(project=scm_inventory_source.scm_project) + proj_update = ProjectUpdate.objects.create(project=scm_inventory_source.source_project) with mock.patch.object(RunProjectUpdate, '_update_dependent_inventories') as inv_update_mck: task.post_run_hook(proj_update, 'successful') - inv_update_mck.assert_called_once_with(scm_inventory_source.scm_project, mock.ANY) + inv_update_mck.assert_called_once_with(proj_update, mock.ANY) def test_no_unwanted_dependent_inventory_updates(self, project, scm_revision_file): task = RunProjectUpdate() @@ -36,8 +36,10 @@ class TestDependentInventoryUpdate: def test_dependent_inventory_updates(self, scm_inventory_source): task = RunProjectUpdate() + proj_update = ProjectUpdate.objects.create(project=scm_inventory_source.source_project) with mock.patch.object(RunInventoryUpdate, 'run') as iu_run_mock: - task._update_dependent_inventories(scm_inventory_source.scm_project, [scm_inventory_source]) + task._update_dependent_inventories(proj_update, [scm_inventory_source]) assert InventoryUpdate.objects.count() == 1 inv_update = InventoryUpdate.objects.first() iu_run_mock.assert_called_once_with(inv_update.id) + assert inv_update.source_project_update_id == proj_update.pk diff --git a/docs/scm_file_inventory.md b/docs/scm_file_inventory.md index 4fbfbb373a..b9dde25380 100644 --- a/docs/scm_file_inventory.md +++ b/docs/scm_file_inventory.md @@ -7,7 +7,7 @@ a project as an Ansible inventory file. Fields that should be specified on creation of SCM inventory source: - - `scm_project` - project to use + - `source_project` - project to use - `source_path` - relative path inside of the project indicating a directory or a file, if left blank, "" is still a relative path indicating the root directory of the project