Skip to content

Commit

Permalink
Allow the .yaml file extension on the default EE file (#631)
Browse files Browse the repository at this point in the history
* Allow the .yaml file extension on the default EE file
  • Loading branch information
Shrews authored Nov 27, 2023
1 parent 3a89cef commit 8125210
Show file tree
Hide file tree
Showing 14 changed files with 104 additions and 21 deletions.
1 change: 1 addition & 0 deletions docs/collection_metadata.rst
Original file line number Diff line number Diff line change
Expand Up @@ -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.

Expand Down
4 changes: 3 additions & 1 deletion docs/definition.rst
Original file line number Diff line number Diff line change
Expand Up @@ -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.
Expand Down
4 changes: 2 additions & 2 deletions docs/index.rst
Original file line number Diff line number Diff line change
Expand Up @@ -36,10 +36,10 @@ Refer to the `Getting started with Execution Environments guide <https://docs.an
Quickstart for Ansible Builder
==============================

To get started with Ansible Builder, you must install the ``ansible-builder`` utility and a containerization tool.
To get started with Ansible Builder, you must install the ``ansible-builder`` utility and a containerization tool.

Once you have the tools you need, create an :ref:`execution environment definition <builder_ee_definition>` 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:

Expand Down
4 changes: 2 additions & 2 deletions docs/usage.rst
Original file line number Diff line number Diff line change
Expand Up @@ -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:

Expand Down Expand Up @@ -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::
Expand Down
20 changes: 14 additions & 6 deletions src/ansible_builder/_target_scripts/introspect.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,8 +7,8 @@

import requirements


base_collections_path = '/usr/share/ansible/collections'
default_file = 'execution-environment.yml'
logger = logging.getLogger(__name__)


Expand Down Expand Up @@ -142,11 +142,19 @@ 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)
else:

# NOTE: Filenames should match constants.DEAFULT_EE_BASENAME and constants.YAML_FILENAME_EXTENSIONS.
meta_file_base = os.path.join(collection_path, 'meta', 'execution-environment')
ee_exists = False
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)
ee_exists = True
break

if not ee_exists:
self.raw = {'version': 1, 'dependencies': {}}
# Automatically infer requirements for collection
for entry, filename in [('python', 'requirements.txt'), ('system', 'bindep.txt')]:
Expand Down
3 changes: 1 addition & 2 deletions src/ansible_builder/cli.py
Original file line number Diff line number Diff line change
Expand Up @@ -153,9 +153,8 @@ 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)')
help='The definition of the execution environment (default: execution-environment.(yml|yaml))')

p.add_argument('-c', '--context',
default=constants.default_build_context,
Expand Down
4 changes: 3 additions & 1 deletion src/ansible_builder/constants.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@
import shutil

default_file = 'execution-environment.yml'
default_tag = 'ansible-execution-env:latest'
default_build_context = 'context'
default_verbosity = 2
Expand Down Expand Up @@ -46,3 +45,6 @@
}

FINAL_IMAGE_BIN_PATH = "/opt/builder/bin"

DEFAULT_EE_BASENAME = "execution-environment"
YAML_FILENAME_EXTENSIONS = ('yml', 'yaml')
2 changes: 1 addition & 1 deletion src/ansible_builder/main.py
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand Down
30 changes: 24 additions & 6 deletions src/ansible_builder/user_definition.py
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
from __future__ import annotations

import logging
import os
import textwrap
Expand Down Expand Up @@ -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:
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
---
dependencies:
python: my-requirements.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
python-six
16 changes: 16 additions & 0 deletions test/unit/test_introspect.py
Original file line number Diff line number Diff line change
Expand Up @@ -67,3 +67,19 @@ def test_parse_args_default_action():
assert parser.user_bindep == user_bindep
assert parser.write_pip == write_pip
assert parser.write_bindep == write_bindep


def test_yaml_extension(data_dir):
"""
Test that introspection recognizes a collection meta directory EE with a .yaml file extension.
NOTE: This test depends on the meta EE in the collection to reference a file other than "requirements.txt"
because of the way CollectionDefinition.__init__() will fall through to a default if the meta EE is not
found.
"""
col_path = os.path.join(data_dir, 'alternate_collections')
files = process(col_path)
assert files == {
'python': {'test_collection.test_yaml_extension': ['python-six']},
'system': {},
}
33 changes: 33 additions & 0 deletions test/unit/test_user_definition.py
Original file line number Diff line number Diff line change
@@ -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
Expand Down Expand Up @@ -306,3 +307,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()

0 comments on commit 8125210

Please sign in to comment.