diff --git a/sat/cli/bootprep/input/configuration.py b/sat/cli/bootprep/input/configuration.py index 6e5b5223..48c5189d 100644 --- a/sat/cli/bootprep/input/configuration.py +++ b/sat/cli/bootprep/input/configuration.py @@ -186,6 +186,7 @@ class GitInputConfigurationLayer(InputConfigurationLayer): and a branch or a commit hash. """ @property + @jinja_rendered def clone_url(self): # The 'url' property is required by the schema return self.layer_data['git']['url'] @@ -197,6 +198,7 @@ def branch(self): return self.layer_data['git'].get('branch') @property + @jinja_rendered def commit(self): # The 'commit' property is optional return self.layer_data['git'].get('commit') @@ -271,6 +273,7 @@ def branch(self): return self.layer_data['product'].get('branch') @cached_property + @jinja_rendered def commit(self): # The 'commit' property is optional return self.layer_data['product'].get('commit') diff --git a/sat/cli/bootprep/input/image.py b/sat/cli/bootprep/input/image.py index 9c5ae594..1c0befec 100644 --- a/sat/cli/bootprep/input/image.py +++ b/sat/cli/bootprep/input/image.py @@ -609,6 +609,7 @@ def base_is_recipe(self): class IMSInputImageV2(IMSInputImage): """An input image that specifies a base IMS image/recipe under the 'ims' property under the 'base' property.""" @property + @jinja_rendered def ims_data(self): """dict: the data that defines the base IMS image or recipe""" return self.image_data['base']['ims'] diff --git a/sat/cli/bootprep/input/session_template.py b/sat/cli/bootprep/input/session_template.py index 55abb9e7..b9e41e73 100644 --- a/sat/cli/bootprep/input/session_template.py +++ b/sat/cli/bootprep/input/session_template.py @@ -357,6 +357,7 @@ def ims_image_name(self): return get_val_by_path(self.data, 'image.ims.name') @property + @jinja_rendered def ims_image_id(self): """str or None: the id specified for the IMS image, or None if not specified""" return get_val_by_path(self.data, 'image.ims.id') diff --git a/tests/cli/bootprep/input/test_configuration.py b/tests/cli/bootprep/input/test_configuration.py index 691b9f4a..00a5d9b2 100644 --- a/tests/cli/bootprep/input/test_configuration.py +++ b/tests/cli/bootprep/input/test_configuration.py @@ -200,6 +200,18 @@ def test_clone_url_property(self): self.mock_cfs_client) self.assertEqual(self.branch_layer_data['git']['url'], layer.clone_url) + def test_clone_url_property_jinja_template(self): + """Test the clone_url property when the clone URL uses Jinja2 templating""" + repo_name = 'foo-config-management' + self.jinja_env.globals['test'] = { + 'repo_name': repo_name + } + + self.branch_layer_data['git']['url'] = 'https://api-gw-service-nmn.local/vcs/cray/{{test.repo_name}}.git' + layer = GitInputConfigurationLayer(self.branch_layer_data, 0, self.jinja_env, + self.mock_cfs_client) + self.assertEqual(f'https://api-gw-service-nmn.local/vcs/cray/{repo_name}.git', layer.clone_url) + def test_branch_property_present(self): """Test the branch property when the branch is in the layer data""" layer = GitInputConfigurationLayer(self.branch_layer_data, 0, self.jinja_env, @@ -231,6 +243,20 @@ def test_commit_property_not_present(self): self.mock_cfs_client) self.assertIsNone(layer.commit) + def test_commit_property_jinja_template(self): + """Test the commit property when the commit uses Jinja2 templating""" + commit_hash = 'abc1234' + self.jinja_env.globals['test'] = { + 'commit_hash': commit_hash + } + + self.commit_layer_data['git']['commit'] = '{{test.commit_hash}}' + + layer = GitInputConfigurationLayer(self.commit_layer_data, 0, self.jinja_env, + self.mock_cfs_client) + + self.assertEqual(commit_hash, layer.commit) + def test_validate_playbook_cfs_v3(self): """Test the validate_playbook_specified_with_cfs_v3 method with a CFSV3Client and present playbook""" mock_cfs_v3_client = Mock(spec=CFSV3Client) @@ -423,7 +449,7 @@ def test_branch_property_not_present(self): def test_branch_property_jinja_template(self): """Test the branch property when the branch uses Jinja2 templating""" - # Have to double the literal brackets that make up the Jinja2 variable reference + # Use non f-string to include the literal brackets that make up the Jinja2 variable reference self.branch_layer_data['product']['branch'] = 'integration-{{' + f'{self.product_name}.version' + '}}' layer = ProductInputConfigurationLayer(self.branch_layer_data, 0, self.jinja_env, @@ -439,6 +465,21 @@ def test_commit_property_not_present(self): """Test the commit property when a branch is in the layer data""" self.assertIsNone(self.branch_layer.commit) + def test_commit_property_jinja_template(self): + """Test the commit property when the commit uses Jinja2 templating""" + commit_hash = 'abc1234' + self.jinja_env.globals['test'] = { + 'commit_hash': commit_hash + } + + # Use non f-string to include the literal brackets that make up the Jinja2 variable reference + self.commit_layer_data['product']['commit'] = '{{test.commit_hash}}' + + layer = ProductInputConfigurationLayer(self.commit_layer_data, 0, self.jinja_env, + self.mock_cfs_client, self.mock_product_catalog) + + self.assertEqual(commit_hash, layer.commit) + def test_commit_property_resolve_branches(self): """Test the commit property when resolving branches and commit specified in the input file""" with self.patch_resolve_branches(True): diff --git a/tests/cli/bootprep/input/test_image.py b/tests/cli/bootprep/input/test_image.py index dc9544bd..2e391db5 100644 --- a/tests/cli/bootprep/input/test_image.py +++ b/tests/cli/bootprep/input/test_image.py @@ -31,10 +31,60 @@ from sat.apiclient.ims import IMSClient from sat.cli.bootprep.errors import ImageCreateError -from sat.cli.bootprep.input.image import ProductInputImage +from sat.cli.bootprep.input.image import IMSInputImageV2, ProductInputImage from sat.cli.bootprep.input.instance import InputInstance +class TestIMSInputImageV2(unittest.TestCase): + """Tests for IMSInputImageV2""" + def setUp(self): + self.mock_input_instance = Mock(spec=InputInstance) + self.mock_product_catalog = Mock(spec=ProductCatalog) + self.mock_cfs_client = Mock(spec=CFSClientBase) + self.mock_ims_client = Mock(spec=IMSClient) + self.jinja_env = Environment() + + def test_ims_data(self): + """Test the ims_data property of IMSInputImageV2""" + base_type = 'image' + image_id = 'abcdef12' + input_data = { + 'name': 'my-image', + 'base': { + 'ims': { + 'type': base_type, + 'id': image_id + } + } + } + ims_input_image = IMSInputImageV2(input_data, 0, self.mock_input_instance, self.jinja_env, + self.mock_product_catalog, self.mock_ims_client, + self.mock_cfs_client) + self.assertEqual({'type': base_type, 'id': image_id}, ims_input_image.ims_data) + + def test_ims_data_jinja_rendered(self): + """Test the ims_data property of IMSInputImageV2 when it uses variable substitution""" + image_id = '1234abcd' + base_type = 'recipe' + self.jinja_env.globals['test'] = { + 'id': image_id, + 'type': base_type + } + input_data = { + 'name': 'my-image', + 'base': { + 'ims': { + 'type': '{{test.type}}', + 'id': '{{test.id}}' + } + } + } + ims_input_image = IMSInputImageV2(input_data, 0, self.mock_input_instance, self.jinja_env, + self.mock_product_catalog, self.mock_ims_client, + self.mock_cfs_client) + self.assertEqual({'type': base_type, 'id': image_id}, ims_input_image.ims_data) + + class TestProductInputImage(unittest.TestCase): """Tests for ProductInputImage""" diff --git a/tests/cli/bootprep/input/test_session_template.py b/tests/cli/bootprep/input/test_session_template.py index 9919bef7..c54bc5c5 100644 --- a/tests/cli/bootprep/input/test_session_template.py +++ b/tests/cli/bootprep/input/test_session_template.py @@ -134,6 +134,66 @@ def get_input_and_expected_bos_data(self, name='my-session-template', return input_data, bos_payload + def test_ims_image_name(self): + """Test the ims_image_name property""" + image_name = 'my-example-image' + input_data, _ = self.get_input_and_expected_bos_data(image_name=image_name) + input_session_template = self.simplified_session_template_v2( + input_data, self.input_instance, 0, self.jinja_env, + self.bos_client, self.cfs_client, self.ims_client + ) + self.assertEqual(image_name, input_session_template.ims_image_name) + + def test_ims_image_name_jinja_rendered(self): + """Test the ims_image_name property with Jinja rendering""" + image_name = 'some-image' + self.jinja_env.globals['test'] = { + 'image_name': image_name + } + image_property = '{{test.image_name}}' + input_data, _ = self.get_input_and_expected_bos_data(image_name=image_property) + input_session_template = self.simplified_session_template_v2( + input_data, self.input_instance, 0, self.jinja_env, + self.bos_client, self.cfs_client, self.ims_client + ) + self.assertEqual(image_name, input_session_template.ims_image_name) + + def test_ims_image_id(self): + """Test the ims_image_id property""" + image_id = 'abcdef12345' + input_data = { + 'name': 'my-session-template', + 'image': {'ims': {'id': image_id}}, + 'configuration': 'my-configuration', + 'bos_parameters': {} + } + + input_session_template = self.simplified_session_template_v2( + input_data, self.input_instance, 0, self.jinja_env, + self.bos_client, self.cfs_client, self.ims_client + ) + self.assertEqual(image_id, input_session_template.ims_image_id) + + def test_ims_image_id_jinja_rendered(self): + """Test the ims_image_id property with Jinja rendering""" + image_id = 'abcdef12345' + self.jinja_env.globals['test'] = { + 'image_id': image_id + } + id_property = '{{test.image_id}}' + input_data = { + 'name': 'my-session-template', + 'image': {'ims': {'id': id_property}}, + 'configuration': 'my-configuration', + 'bos_parameters': {} + } + + input_session_template = self.simplified_session_template_v2( + input_data, self.input_instance, 0, self.jinja_env, + self.bos_client, self.cfs_client, self.ims_client + ) + self.assertEqual(image_id, input_session_template.ims_image_id) + def test_get_create_item_data_no_arch(self): """Test get_create_item_data method with no architecture specified""" input_data, expected_bos_data = self.get_input_and_expected_bos_data()