diff --git a/awx/main/models/projects.py b/awx/main/models/projects.py index d64e2483bd..c85cfd7000 100644 --- a/awx/main/models/projects.py +++ b/awx/main/models/projects.py @@ -615,16 +615,22 @@ class ProjectUpdate(UnifiedJob, ProjectOptions, JobNotificationMixin, TaskManage @property def preferred_instance_groups(self): + ''' + Project updates should pretty much always run on the control plane + however, we are not yet saying no to custom groupings within the control plane + Thus, we return custom groups and then unconditionally add the control plane + ''' if self.organization is not None: organization_groups = [x for x in self.organization.instance_groups.all()] else: organization_groups = [] template_groups = [x for x in super(ProjectUpdate, self).preferred_instance_groups] selected_groups = template_groups + organization_groups - if not any([not group.is_container_group for group in selected_groups]): - selected_groups = selected_groups + list(self.control_plane_instance_group) - if not selected_groups: - return self.global_instance_groups + + controlplane_ig = self.control_plane_instance_group + if controlplane_ig and controlplane_ig[0] and controlplane_ig[0] not in selected_groups: + selected_groups += controlplane_ig + return selected_groups def save(self, *args, **kwargs): diff --git a/awx/main/tests/conftest.py b/awx/main/tests/conftest.py index 012a8f1c93..79044b868e 100644 --- a/awx/main/tests/conftest.py +++ b/awx/main/tests/conftest.py @@ -84,6 +84,11 @@ def default_instance_group(instance_factory, instance_group_factory): return create_instance_group("default", instances=[create_instance("hostA")]) +@pytest.fixture +def controlplane_instance_group(instance_factory, instance_group_factory): + return create_instance_group("controlplane", instances=[create_instance("hostA")]) + + @pytest.fixture def job_template_with_survey_passwords_factory(job_template_factory): def rf(persisted): diff --git a/awx/main/tests/functional/task_management/test_rampart_groups.py b/awx/main/tests/functional/task_management/test_rampart_groups.py index b51fcab797..26b19399d4 100644 --- a/awx/main/tests/functional/task_management/test_rampart_groups.py +++ b/awx/main/tests/functional/task_management/test_rampart_groups.py @@ -30,7 +30,7 @@ def test_multi_group_basic_job_launch(instance_factory, default_instance_group, @pytest.mark.django_db -def test_multi_group_with_shared_dependency(instance_factory, default_instance_group, mocker, instance_group_factory, job_template_factory): +def test_multi_group_with_shared_dependency(instance_factory, controlplane_instance_group, mocker, instance_group_factory, job_template_factory): i1 = instance_factory("i1") i2 = instance_factory("i2") ig1 = instance_group_factory("ig1", instances=[i1]) @@ -54,7 +54,7 @@ def test_multi_group_with_shared_dependency(instance_factory, default_instance_g with mocker.patch("awx.main.scheduler.TaskManager.start_task"): TaskManager().schedule() pu = p.project_updates.first() - TaskManager.start_task.assert_called_once_with(pu, default_instance_group, [j1, j2], default_instance_group.instances.all()[0]) + TaskManager.start_task.assert_called_once_with(pu, controlplane_instance_group, [j1, j2], controlplane_instance_group.instances.all()[0]) pu.finished = pu.created + timedelta(seconds=1) pu.status = "successful" pu.save() diff --git a/awx/main/tests/functional/task_management/test_scheduler.py b/awx/main/tests/functional/task_management/test_scheduler.py index d6794e4f77..8b4db09a75 100644 --- a/awx/main/tests/functional/task_management/test_scheduler.py +++ b/awx/main/tests/functional/task_management/test_scheduler.py @@ -212,9 +212,9 @@ def test_multi_jt_capacity_blocking(default_instance_group, job_template_factory @pytest.mark.django_db -def test_single_job_dependencies_project_launch(default_instance_group, job_template_factory, mocker): +def test_single_job_dependencies_project_launch(controlplane_instance_group, job_template_factory, mocker): objects = job_template_factory('jt', organization='org1', project='proj', inventory='inv', credential='cred', jobs=["job_should_start"]) - instance = default_instance_group.instances.all()[0] + instance = controlplane_instance_group.instances.all()[0] j = objects.jobs["job_should_start"] j.status = 'pending' j.save() @@ -231,12 +231,12 @@ def test_single_job_dependencies_project_launch(default_instance_group, job_temp mock_pu.assert_called_once_with(j) pu = [x for x in p.project_updates.all()] assert len(pu) == 1 - TaskManager.start_task.assert_called_once_with(pu[0], default_instance_group, [j], instance) + TaskManager.start_task.assert_called_once_with(pu[0], controlplane_instance_group, [j], instance) pu[0].status = "successful" pu[0].save() with mock.patch("awx.main.scheduler.TaskManager.start_task"): TaskManager().schedule() - TaskManager.start_task.assert_called_once_with(j, default_instance_group, [], instance) + TaskManager.start_task.assert_called_once_with(j, controlplane_instance_group, [], instance) @pytest.mark.django_db @@ -297,8 +297,8 @@ def test_job_dependency_with_already_updated(default_instance_group, job_templat @pytest.mark.django_db -def test_shared_dependencies_launch(default_instance_group, job_template_factory, mocker, inventory_source_factory): - instance = default_instance_group.instances.all()[0] +def test_shared_dependencies_launch(controlplane_instance_group, job_template_factory, mocker, inventory_source_factory): + instance = controlplane_instance_group.instances.all()[0] objects = job_template_factory('jt', organization='org1', project='proj', inventory='inv', credential='cred', jobs=["first_job", "second_job"]) j1 = objects.jobs["first_job"] j1.status = 'pending' @@ -326,7 +326,7 @@ def test_shared_dependencies_launch(default_instance_group, job_template_factory pu = p.project_updates.first() iu = ii.inventory_updates.first() TaskManager.start_task.assert_has_calls( - [mock.call(iu, default_instance_group, [j1, j2, pu], instance), mock.call(pu, default_instance_group, [j1, j2, iu], instance)] + [mock.call(iu, controlplane_instance_group, [j1, j2, pu], instance), mock.call(pu, controlplane_instance_group, [j1, j2, iu], instance)] ) pu.status = "successful" pu.finished = pu.created + timedelta(seconds=1) @@ -336,12 +336,12 @@ def test_shared_dependencies_launch(default_instance_group, job_template_factory iu.save() with mock.patch("awx.main.scheduler.TaskManager.start_task"): TaskManager().schedule() - TaskManager.start_task.assert_called_once_with(j1, default_instance_group, [], instance) + TaskManager.start_task.assert_called_once_with(j1, controlplane_instance_group, [], instance) j1.status = "successful" j1.save() with mock.patch("awx.main.scheduler.TaskManager.start_task"): TaskManager().schedule() - TaskManager.start_task.assert_called_once_with(j2, default_instance_group, [], instance) + TaskManager.start_task.assert_called_once_with(j2, controlplane_instance_group, [], instance) pu = [x for x in p.project_updates.all()] iu = [x for x in ii.inventory_updates.all()] assert len(pu) == 1 diff --git a/awx/main/tests/functional/test_instances.py b/awx/main/tests/functional/test_instances.py index 65428886db..0c4d73a296 100644 --- a/awx/main/tests/functional/test_instances.py +++ b/awx/main/tests/functional/test_instances.py @@ -314,15 +314,15 @@ class TestInstanceGroupOrdering: # API does not allow setting IGs on inventory source, so ignore those assert iu.preferred_instance_groups == [ig_inv, ig_org] - def test_project_update_instance_groups(self, instance_group_factory, project, default_instance_group): + def test_project_update_instance_groups(self, instance_group_factory, project, controlplane_instance_group): pu = ProjectUpdate.objects.create(project=project, organization=project.organization) - assert pu.preferred_instance_groups == [default_instance_group] - ig_org = instance_group_factory("OrgIstGrp", [default_instance_group.instances.first()]) - ig_tmp = instance_group_factory("TmpIstGrp", [default_instance_group.instances.first()]) + assert pu.preferred_instance_groups == [controlplane_instance_group] + ig_org = instance_group_factory("OrgIstGrp", [controlplane_instance_group.instances.first()]) + ig_tmp = instance_group_factory("TmpIstGrp", [controlplane_instance_group.instances.first()]) project.organization.instance_groups.add(ig_org) - assert pu.preferred_instance_groups == [ig_org] + assert pu.preferred_instance_groups == [ig_org, controlplane_instance_group] project.instance_groups.add(ig_tmp) - assert pu.preferred_instance_groups == [ig_tmp, ig_org] + assert pu.preferred_instance_groups == [ig_tmp, ig_org, controlplane_instance_group] def test_job_instance_groups(self, instance_group_factory, inventory, project, default_instance_group): jt = JobTemplate.objects.create(inventory=inventory, project=project)