Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[Breaking] Synchronize Job Template with name instead of ID in AWX(RHAAP) #800

Closed
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
# Generated by Django 4.2.6 on 2024-11-20 13:19

from django.db import migrations


class Migration(migrations.Migration):

dependencies = [
('service_catalog', '0043_operation_when'),
]

operations = [
migrations.AlterUniqueTogether(
name='jobtemplate',
unique_together={('name', 'tower_server')},
),
]
4 changes: 2 additions & 2 deletions service_catalog/models/job_templates.py
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@

class JobTemplate(SquestModel):
class Meta:
unique_together = ('tower_id', 'tower_server',)
unique_together = ('name', 'tower_server',)
default_permissions = ('add', 'change', 'delete', 'view', 'list')

name = CharField(max_length=100)
Expand All @@ -33,7 +33,7 @@ def __str__(self):
def execute(self, extra_vars, inventory_override=None, credentials_override=None, tags_override=None,
skip_tags_override=None, limit_override=None, verbosity_override=None, job_type_override=None,
diff_mode_override=None):
tower_job_template = self.tower_server.get_tower_instance().get_job_template_by_id(self.tower_id)
tower_job_template = self.tower_server.get_tower_instance().get_job_template_by_name(self.name)
if tower_job_template is None:
raise ExceptionServiceCatalog.JobTemplateNotFound(tower_name=self.tower_server.name,
job_template_id=self.tower_id)
Expand Down
28 changes: 17 additions & 11 deletions service_catalog/models/tower_server.py
Original file line number Diff line number Diff line change
Expand Up @@ -42,14 +42,16 @@ def sync(self, job_template_id=None):

# sync job template
if job_template_id is None:
job_template_id_in_tower = []
job_template_names = []
for job_template_from_tower in tower.job_templates:
job_template_id_in_tower.append(job_template_from_tower.id)
self._update_job_template_from_tower(job_template_from_tower)
JobTemplateLocal.objects.filter(tower_server=self).exclude(tower_id__in=job_template_id_in_tower).delete()
job_template_names.append(job_template_from_tower.name)
self._update_job_template_from_tower(job_template_from_tower.name)
JobTemplateLocal.objects.filter(tower_server=self).exclude(name__in=job_template_names).delete()
else:
job_template = JobTemplateLocal.objects.get(id=job_template_id)
self._update_job_template_from_tower(tower.get_job_template_by_id(job_template.tower_id))
existing = self._update_job_template_from_tower(job_template.name)
if not existing:
job_template.delete()

# sync inventories
inventory_ids_in_tower = []
Expand All @@ -65,7 +67,7 @@ def sync(self, job_template_id=None):
for credential_from_tower in tower.credentials:
credentials_ids_in_tower.append(credential_from_tower.id)
self._update_credential_from_tower(credential_from_tower)
# delete inventories that do not exist anymore in Tower
# delete credentials that do not exist anymore in Tower
from .credential import Credential as CredentialLocal
CredentialLocal.objects.filter(tower_server=self).exclude(tower_id__in=credentials_ids_in_tower).delete()

Expand All @@ -77,18 +79,22 @@ def url(self):
def get_tower_instance(self):
return Tower(self.host, None, None, secure=self.secure, ssl_verify=self.ssl_verify, token=self.token)

def _update_job_template_from_tower(self, job_template_from_tower):
def _update_job_template_from_tower(self, job_template_name):
tower_server = self.get_tower_instance()
job_template_from_tower = tower_server.get_job_template_by_name(job_template_name)
if job_template_from_tower is None:
return False
from .job_templates import JobTemplate as JobTemplateLocal
logger.info(f"Sync job template id '{job_template_from_tower.id}'")
job_template, _ = JobTemplateLocal.objects.get_or_create(tower_id=job_template_from_tower.id,
tower_server=self,
defaults={'name': job_template_from_tower.name})
job_template, _ = JobTemplateLocal.objects.get_or_create(tower_server=self, name=job_template_from_tower.name, defaults={'tower_id': job_template_from_tower.id })

# update data
job_template.name = job_template_from_tower.name
job_template.tower_id = job_template_from_tower.id
job_template.tower_job_template_data = job_template_from_tower._data
job_template.survey = job_template_from_tower.survey_spec
job_template.is_compliant = job_template.check_is_compliant()
job_template.save()
return True

def _update_inventory_from_tower(self, inventory_from_tower):
from .inventory import Inventory as InventoryLocal
Expand Down
24 changes: 22 additions & 2 deletions tests/test_service_catalog/base.py
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,27 @@


class BaseTestCommon(TransactionTestCase):
class FakeJobTemplate:
def __init__(self, job_template_id, name, survey_spec, data):
self.id = job_template_id
self.name = name
self.survey_spec = survey_spec
self._data = data

class FakeTower:
def __init__(self, testing_survey, data, job_template_count=1):
self.job_templates = [BaseTestCommon.FakeJobTemplate(i, f"job-template-test-{i}", testing_survey, data) for i in range(1, job_template_count+1)]
self.inventories = []
self.credentials = []

def get_job_template_by_name(self, name):
for job in self.job_templates:
if name == job.name:
return job
return None

def get_tower_instance(self):
return
def setUp(self):
# Organization
self.test_quota_scope_org = Organization.objects.create(name='Test Organization 1')
Expand Down Expand Up @@ -268,12 +288,12 @@ def setUp(self):
}
]
}
self.job_template_test = JobTemplate.objects.create(name="job-template-test",
self.job_template_test = JobTemplate.objects.create(name="job-template-test-1",
survey=self.testing_survey,
tower_id=1,
tower_server=self.tower_server_test,
tower_job_template_data=self.job_template_testing_data)
self.job_template_test_2 = JobTemplate.objects.create(name="job-template-test",
self.job_template_test_2 = JobTemplate.objects.create(name="job-template-test-2",
survey=self.testing_survey,
tower_id=1,
tower_server=self.tower_server_test_2,
Expand Down
15 changes: 3 additions & 12 deletions tests/test_service_catalog/test_models/test_job_template.py
Original file line number Diff line number Diff line change
Expand Up @@ -11,16 +11,7 @@ def setUp(self):

@patch('service_catalog.models.tower_server.TowerServer.get_tower_instance')
def set_up_tower(self, mock_tower_instance):
mock_tower_instance.return_value = MagicMock(
job_templates=[
MagicMock(
id=1,
survey_spec=self.testing_survey,
_data=self.job_template_testing_data
)
]
)
mock_tower_instance.return_value.job_templates[0].name = "Mock"
mock_tower_instance.return_value = BaseTest.FakeTower(self.testing_survey, self.job_template_testing_data)
self.tower_server_test.sync()
self.job_templates = JobTemplate.objects.filter(tower_server=self.tower_server_test)

Expand All @@ -37,15 +28,15 @@ def test_is_not_compliant(self):

@patch('service_catalog.models.tower_server.TowerServer.get_tower_instance')
def test_execute_no_job_template_returned(self, mock_tower_instance):
mock_tower_instance.return_value.get_job_template_by_id.return_value = None
mock_tower_instance.return_value.get_job_template_by_name.return_value = None
with self.assertRaises(ExceptionServiceCatalog.JobTemplateNotFound):
self.job_template_test.execute(extra_vars={"test_var": "test_val"})

@patch('service_catalog.models.tower_server.TowerServer.get_tower_instance')
def test_execute_job_template_returned(self, mock_tower_instance):
mock_tower_instance.return_value = MagicMock(name="mock_tower_instance")
mock_job_template = MagicMock(name="mock_job_template")
mock_tower_instance.return_value.get_job_template_by_id.return_value = mock_job_template
mock_tower_instance.return_value.get_job_template_by_name.return_value = mock_job_template
mock_job_template.launch.return_value.id = 5
self.job_template_test.execute(extra_vars={"test_var": "test_val"})
mock_job_template.launch.assert_called()
60 changes: 27 additions & 33 deletions tests/test_service_catalog/test_models/test_tower_server.py
Original file line number Diff line number Diff line change
Expand Up @@ -46,36 +46,18 @@ def test_str(self):
@patch('service_catalog.models.tower_server.TowerServer.get_tower_instance')
def test_sync_same_survey(self, mock_tower_instance):
# test sync with same job template
mock_tower_instance.return_value = MagicMock(
job_templates=[
MagicMock(
id=1,
survey_spec=self.testing_survey,
_data=self.job_template_testing_data
)
]
)
mock_tower_instance.return_value.job_templates[0].name = "Mock"
mock_tower_instance.return_value = BaseTest.FakeTower(self.testing_survey, self.job_template_testing_data)
self.tower_server_test.sync()
mock_tower_instance.assert_called()
# assert that the survey is the same
self.assertDictEqual(self.testing_survey, self.job_template_test.survey)

@patch('service_catalog.models.tower_server.TowerServer.get_tower_instance')
def test_sync_survey_changed(self, mock_tower_instance):
mock_tower_instance.return_value = MagicMock(
job_templates=[
MagicMock(
id=1,
survey_spec=self.new_survey,
_data=self.job_template_testing_data
)
]
)
mock_tower_instance.return_value.job_templates[0].name = "Mock"
mock_tower_instance.return_value = BaseTest.FakeTower(self.new_survey, self.job_template_testing_data)
self.tower_server_test.sync()
# assert that the survey has been updated
updated_template = JobTemplate.objects.get(id=self.job_template_test.id,
updated_template = JobTemplate.objects.get(name="job-template-test-1",
tower_server=self.tower_server_test)
self.assertDictEqual(self.new_survey, updated_template.survey)
# check that the operation tower_survey_fields has been updated
Expand All @@ -84,30 +66,42 @@ def test_sync_survey_changed(self, mock_tower_instance):
self.assertTrue(updated_operation.tower_survey_fields.filter(variable=field["variable"], is_customer_field=True).exists())

@patch('service_catalog.models.tower_server.TowerServer._update_job_template_from_tower')
@patch('towerlib.towerlib.Tower.get_job_template_by_id')
@patch('towerlib.towerlib.Tower.get_job_template_by_name')
@patch('service_catalog.models.tower_server.TowerServer.get_tower_instance')
def test_sync_selected_job_template(self, mock_tower_instance, mock_get_job_template_by_id,
def test_sync_selected_job_template(self, mock_tower_instance, mock_get_job_template_by_name,
mock_update_job_template_from_tower):
self.job_template_test.tower_id = 20
self.job_template_test.name = "test-survey"
self.job_template_test.save()
mock_tower_instance.return_value = MagicMock()
mock_get_job_template_by_id.return_value = MagicMock(
mock_tower_instance.return_value = BaseTest.FakeTower(self.testing_survey, self.job_template_testing_data)
mock_get_job_template_by_name.return_value = MagicMock(
id=1,
survey_spec=self.new_survey,
_data=self.job_template_testing_data
)
self.tower_server_test.sync(job_template_id=self.job_template_test.id)
mock_update_job_template_from_tower.assert_called()

def test_update_job_template_from_tower(self):
@patch('service_catalog.models.tower_server.TowerServer.get_tower_instance')
def test_update_job_template_from_tower(self, mock_tower_instance):
self.job_template_test.tower_id = 10
self.job_template_test.save()
job_template_from_tower = MagicMock(id=10,
_data=self.job_template_testing_data,
survey_spec=self.new_survey)
job_template_from_tower.name = "tower_job_template_update"
self.tower_server_test._update_job_template_from_tower(job_template_from_tower)
self.job_template_test.refresh_from_db()
self.assertEqual(self.job_template_test.name, "tower_job_template_update")
self.assertEqual(self.job_template_test.tower_id, 10)
mock_tower_instance.return_value = BaseTest.FakeTower(self.new_survey, self.job_template_testing_data)
self.tower_server_test._update_job_template_from_tower(self.job_template_test.name)
self.job_template_test.refresh_from_db()
self.assertEqual(self.job_template_test.tower_id, 1)
self.assertEqual(self.job_template_test.survey, self.new_survey)
self.assertEqual(self.job_template_test.tower_job_template_data, self.job_template_testing_data)

@patch('towerlib.towerlib.Tower.get_job_template_by_name')
@patch('service_catalog.models.tower_server.TowerServer.get_tower_instance')
def test_update_non_existing_job_template_from_tower(self, mock_tower_instance, mock_get_job_template_by_name):
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

add rename test

self.job_template_test.name = "non-existing"
self.job_template_test.save()
job_template_id = self.job_template_test.id
mock_tower_instance.return_value = BaseTest.FakeTower(self.testing_survey, self.job_template_testing_data)
mock_get_job_template_by_name.return_value = None
self.assertTrue(JobTemplate.objects.filter(id=job_template_id).exists())
self.tower_server_test.sync(job_template_id)
self.assertFalse(JobTemplate.objects.filter(id=job_template_id).exists())
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import json
import copy
from unittest import mock
from unittest.mock import MagicMock
from unittest.mock import patch, MagicMock

from django.urls import reverse

Expand Down Expand Up @@ -32,28 +32,17 @@ def test_towerserver_sync_when_added_job_template(self):
def test_towerserver_sync_when_deleted_job_template(self):
self.mock_tower_sync(-1)

def mock_tower_sync(self, delta):
with mock.patch('service_catalog.models.tower_server.TowerServer.get_tower_instance') as mock_tower_instance:
current_number_job_template = JobTemplate.objects.filter(tower_server=self.tower_server_test).count()
target_number_job_template = current_number_job_template + delta
job_template_list = list()
for i in range(target_number_job_template):
magic_mock = MagicMock(
id=i + 100,
survey_spec=self.testing_survey,
_data=self.job_template_testing_data
)
magic_mock.name = f"Test {i}"
job_template_list.append(magic_mock)
mock_tower_instance.return_value = MagicMock(
job_templates=job_template_list
)
self.tower_server_test.sync()
self.tower_server_test.refresh_from_db()
mock_tower_instance.assert_called()
# assert that the survey is the same
self.assertEqual(JobTemplate.objects.filter(tower_server=self.tower_server_test).count(),
target_number_job_template)
@patch('service_catalog.models.tower_server.TowerServer.get_tower_instance')
def mock_tower_sync(self, delta, mock_tower_instance):
current_number_job_template = JobTemplate.objects.filter(tower_server=self.tower_server_test).count()
target_number_job_template = current_number_job_template + delta
mock_tower_instance.return_value = BaseTestTower.FakeTower(self.testing_survey, self.job_template_testing_data, target_number_job_template)
self.tower_server_test.sync()
self.tower_server_test.refresh_from_db()
mock_tower_instance.assert_called()
# assert that the survey is the same
self.assertEqual(JobTemplate.objects.filter(tower_server=self.tower_server_test).count(),
target_number_job_template)

def test_jobtemplate_sync(self):
with mock.patch("service_catalog.models.tower_server.TowerServer.sync") as mock_sync:
Expand Down
Loading