From 428f56330ff77a5163d30bccb81eb6e3f6c0bc7a Mon Sep 17 00:00:00 2001 From: David Shrewsbury Date: Fri, 10 Nov 2023 12:02:05 -0500 Subject: [PATCH] Allow the .yaml file extension on the default EE file. --- docs/collection_metadata.rst | 1 + docs/definition.rst | 4 ++- docs/index.rst | 4 +-- docs/usage.rst | 4 +-- .../_target_scripts/introspect.py | 15 ++++++--- src/ansible_builder/cli.py | 1 - src/ansible_builder/constants.py | 4 ++- src/ansible_builder/main.py | 2 +- src/ansible_builder/user_definition.py | 30 +++++++++++++---- test/unit/test_user_definition.py | 33 +++++++++++++++++++ 10 files changed, 79 insertions(+), 19 deletions(-) diff --git a/docs/collection_metadata.rst b/docs/collection_metadata.rst index 37cb808f..c98ee29f 100644 --- a/docs/collection_metadata.rst +++ b/docs/collection_metadata.rst @@ -9,6 +9,7 @@ For Ansible Builder to find and install collection dependencies, those dependenc * The ``meta/execution-environment.yml`` file containing the Python and/or bindep requirements or referencing other files listing them. + The ``.yaml`` extension is also valid on this file. * The ``requirements.txt`` file in the root level of the collection. * The ``bindep.txt`` file in the root level of the collection. diff --git a/docs/definition.rst b/docs/definition.rst index bbb0593e..1e8e95c6 100644 --- a/docs/definition.rst +++ b/docs/definition.rst @@ -3,7 +3,9 @@ Execution environment definition ================================ -You define the content of your execution environment in a YAML file. By default, this file is called ``execution-environment.yml``. This file tells Ansible Builder how to create the build instruction file (``Containerfile`` for Podman, ``Dockerfile`` for Docker) and build context for your container image. +You define the content of your execution environment in a YAML file. By default, this file is called ``execution-environment.yml`` +or ``execution-environment.yaml``. This file tells Ansible Builder how to create the build instruction file +(``Containerfile`` for Podman, ``Dockerfile`` for Docker) and build context for your container image. .. note:: This page documents the definition schema for Ansible Builder 3.x. If you are running an older version of Ansible Builder, you need an older schema version. Please consult older versions of the docs for more information. We recommend using version 3, which offers substantially more configurability and functionality than previous versions. diff --git a/docs/index.rst b/docs/index.rst index f8ea657d..c31947be 100644 --- a/docs/index.rst +++ b/docs/index.rst @@ -36,10 +36,10 @@ Refer to the `Getting started with Execution Environments guide ` file. -By default, this file is called ``execution-environment.yml``. +By default, this file is called ``execution-environment.yml`` (the ``.yaml`` extension is also accepted). In the execution environment definition file, you can specify the exact content you want to include in your execution environment. You can specify these items: diff --git a/docs/usage.rst b/docs/usage.rst index 0bddd9ce..f4f9f3be 100644 --- a/docs/usage.rst +++ b/docs/usage.rst @@ -18,7 +18,7 @@ The ``ansible-builder build`` command: * creates a build context necessary for building an execution environment image, * builds the image. -By default, it looks for a file named ``execution-environment.yml`` in the current directory. +By default, it looks for a file named ``execution-environment.yml`` (or ``execution-environment.yaml``) in the current directory. To build an execution environment using the default definition file, run: @@ -52,7 +52,7 @@ More recent versions of ``ansible-builder`` support multiple tags: ``--file`` ********** -Specifies the execution environment file. To use a file named something other than ``execution-environment.yml``: +Specifies the execution environment file. To use a file other than the default: .. code:: diff --git a/src/ansible_builder/_target_scripts/introspect.py b/src/ansible_builder/_target_scripts/introspect.py index e41a6819..0bfea5f7 100644 --- a/src/ansible_builder/_target_scripts/introspect.py +++ b/src/ansible_builder/_target_scripts/introspect.py @@ -7,8 +7,8 @@ import requirements + base_collections_path = '/usr/share/ansible/collections' -default_file = 'execution-environment.yml' logger = logging.getLogger(__name__) @@ -142,10 +142,15 @@ class CollectionDefinition: def __init__(self, collection_path): self.reference_path = collection_path - meta_file = os.path.join(collection_path, 'meta', default_file) - if os.path.exists(meta_file): - with open(meta_file, 'r') as f: - self.raw = yaml.safe_load(f) + + # NOTE: Filenames should match constants.DEAFULT_EE_BASENAME and constants.YAML_FILENAME_EXTENSIONS. + meta_file_base = os.path.join(collection_path, 'meta', 'execution-environment') + for ext in ('yml', 'yaml'): + meta_file = f"{meta_file_base}.{ext}" + if os.path.exists(meta_file): + with open(meta_file, 'r') as f: + self.raw = yaml.safe_load(f) + break else: self.raw = {'version': 1, 'dependencies': {}} # Automatically infer requirements for collection diff --git a/src/ansible_builder/cli.py b/src/ansible_builder/cli.py index 97726ba7..1aa7b32d 100644 --- a/src/ansible_builder/cli.py +++ b/src/ansible_builder/cli.py @@ -153,7 +153,6 @@ def add_container_options(parser): for p in [create_command_parser, build_command_parser]: p.add_argument('-f', '--file', - default=constants.default_file, dest='filename', help='The definition of the execution environment (default: %(default)s)') diff --git a/src/ansible_builder/constants.py b/src/ansible_builder/constants.py index b499aabd..6fbbccc3 100644 --- a/src/ansible_builder/constants.py +++ b/src/ansible_builder/constants.py @@ -1,6 +1,5 @@ import shutil -default_file = 'execution-environment.yml' default_tag = 'ansible-execution-env:latest' default_build_context = 'context' default_verbosity = 2 @@ -46,3 +45,6 @@ } FINAL_IMAGE_BIN_PATH = "/opt/builder/bin" + +DEFAULT_EE_BASENAME = "execution-environment" +YAML_FILENAME_EXTENSIONS = ('yml', 'yaml)') diff --git a/src/ansible_builder/main.py b/src/ansible_builder/main.py index 8a8e214f..465225fc 100644 --- a/src/ansible_builder/main.py +++ b/src/ansible_builder/main.py @@ -16,7 +16,7 @@ class AnsibleBuilder: def __init__(self, action: str, - filename: str = constants.default_file, + filename: str | None = None, build_args: dict[str, str] | None = None, build_context: str = constants.default_build_context, tag: list | None = None, diff --git a/src/ansible_builder/user_definition.py b/src/ansible_builder/user_definition.py index 881e143f..a1bf8e8e 100644 --- a/src/ansible_builder/user_definition.py +++ b/src/ansible_builder/user_definition.py @@ -1,3 +1,5 @@ +from __future__ import annotations + import logging import os import textwrap @@ -63,27 +65,43 @@ class UserDefinition: Class representing the Execution Environment file. """ - def __init__(self, filename): + def __init__(self, filename=None): """ Initialize the UserDefinition object. - :param str filename: Path to the EE file. + :param str filename: Path to the EE file. If this is None, we will + look for the default file: execution-environment.(yml|yaml) """ - self.filename = filename # A dict that is the raw representation of the EE file. self.raw = {} + + if filename is None: + for ext in constants.YAML_FILENAME_EXTENSIONS: + ee_file = f'{constants.DEFAULT_EE_BASENAME}.{ext}' + if os.path.exists(ee_file): + filename = ee_file + break + if filename is None: + raise DefinitionError(textwrap.dedent( + """ + Default execution environment file not found in current directory. + Use -f to specify the correct file. + """)) + + self.filename = filename + # The folder which dependencies are specified relative to. - self.reference_path = os.path.dirname(filename) + self.reference_path = os.path.dirname(self.filename) try: - with open(filename, 'r') as ee_file: + with open(self.filename, 'r') as ee_file: data = yaml.safe_load(ee_file) self.raw = data if data else {} except FileNotFoundError as exc: raise DefinitionError(textwrap.dedent( f""" - Could not detect '{filename}' file in this directory. + Could not detect '{self.filename}' file in this directory. Use -f to specify a different location. """)) from exc except (yaml.parser.ParserError, yaml.scanner.ScannerError) as exc: diff --git a/test/unit/test_user_definition.py b/test/unit/test_user_definition.py index 8323b679..1b42122c 100644 --- a/test/unit/test_user_definition.py +++ b/test/unit/test_user_definition.py @@ -1,6 +1,7 @@ import os import pytest +from ansible_builder import constants from ansible_builder.exceptions import DefinitionError from ansible_builder.main import AnsibleBuilder from ansible_builder.user_definition import UserDefinition, ImageDescription @@ -299,3 +300,35 @@ def test_missing_orig_name_tag(self, key, image): with pytest.raises(DefinitionError) as error: ImageDescription(ee_section, key) assert f"Container image requires a tag: {image}" in str(error.value.args[0]) + + +class TestDefaultEEFilename: + """ + Class to handle test setup/teardown of tests that need to change working + directory to test the default EE filename in the current directory. + """ + + def setup_method(self): + self._old_workdir = os.getcwd() # pylint: disable=W0201 + + def teardown_method(self): + os.chdir(self._old_workdir) + + def test_all_default_filenames(self, tmp_path): + """ + Test finding all variations of the default execution environment filename. + """ + os.chdir(str(tmp_path)) + for ext in constants.YAML_FILENAME_EXTENSIONS: + ee = tmp_path / f"{constants.DEFAULT_EE_BASENAME}.{ext}" + ee.write_text("version: 3") + UserDefinition() # This would fail if the file is not found + ee.unlink() + + def test_missing_default_file(self, tmp_path): + """ + Test that we fail if the default file is not found. + """ + os.chdir(str(tmp_path)) + with pytest.raises(DefinitionError, match="Default execution environment file not found in current directory."): + UserDefinition()