diff --git a/ansible_builder/cli.py b/ansible_builder/cli.py index c9f70e4d..ff99887a 100644 --- a/ansible_builder/cli.py +++ b/ansible_builder/cli.py @@ -2,6 +2,7 @@ import logging import sys import yaml +import os from . import __version__, constants @@ -26,7 +27,10 @@ def run(): action = getattr(ab, ab.action) try: if action(): - print(MessageColors.OKGREEN + "Complete! The build context can be found at: {0}".format(ab.build_context) + MessageColors.ENDC) + print( + MessageColors.OKGREEN + "Complete! The build context can be found at: {0}".format( + os.path.abspath(ab.build_context) + ) + MessageColors.ENDC) sys.exit(0) except DefinitionError as e: logger.error(e.args[0]) @@ -104,6 +108,14 @@ def parse_args(args=sys.argv[1:]): ', '.join(constants.build_arg_defaults.keys())) ) + p.add_argument('--output-filename', + choices=list(constants.runtime_files.values()), + default=None, + help='Name of file to write image definition to ' + '(default depends on --container-runtime, {0})'.format( + ' and '.join([' for '.join([v, k]) for k, v in constants.runtime_files.items()])) + ) + p.add_argument('-v', '--verbosity', dest='verbosity', type=int, diff --git a/ansible_builder/main.py b/ansible_builder/main.py index caffb898..b4575a4d 100644 --- a/ansible_builder/main.py +++ b/ansible_builder/main.py @@ -39,6 +39,7 @@ def __init__(self, action=None, build_context=constants.default_build_context, tag=constants.default_tag, container_runtime=constants.default_container_runtime, + output_filename=None, verbosity=constants.default_verbosity): self.action = action self.definition = UserDefinition(filename=filename) @@ -53,6 +54,7 @@ def __init__(self, action=None, definition=self.definition, build_context=self.build_context, container_runtime=self.container_runtime, + output_filename=output_filename, tag=self.tag) self.verbosity = verbosity @@ -305,13 +307,17 @@ class Containerfile: def __init__(self, definition, build_context=None, container_runtime=None, + output_filename=None, tag=None): self.build_context = build_context self.build_outputs_dir = os.path.join( build_context, constants.user_content_subfolder) self.definition = definition - filename = constants.runtime_files[container_runtime] + if output_filename is None: + filename = constants.runtime_files[container_runtime] + else: + filename = output_filename self.path = os.path.join(self.build_context, filename) self.container_runtime = container_runtime self.tag = tag diff --git a/ansible_builder/utils.py b/ansible_builder/utils.py index e8f1eb53..47c33199 100644 --- a/ansible_builder/utils.py +++ b/ansible_builder/utils.py @@ -8,6 +8,7 @@ from collections import deque from .colors import MessageColors +from . import constants logger = logging.getLogger(__name__) @@ -69,7 +70,22 @@ def run_command(command, capture_output=False, allow_error=False): stdout=subprocess.PIPE, stderr=subprocess.STDOUT) except FileNotFoundError: - logger.error(f"You do not have {command[0]} installed, please specify a different container runtime for this command.") + msg = f"You do not have {command[0]} installed." + if command[0] in constants.runtime_files: + install_summary = ', '.join([ + '{runtime}: {blurb}'.format( + runtime=runtime, + blurb={True: 'installed', False: 'not installed'}.get(bool(shutil.which(runtime))) + ) for runtime in constants.runtime_files + ]) + msg += ( + f'\nYou do not have {command[0]} installed.\n' + f'Please either install {command[0]} or specify an alternative container ' + f'runtime by passing --container-runtime on the command line.\n' + f'Below are the supported container runtimes and whether ' + f'or not they were found on your system.\n{install_summary}' + ) + logger.error(msg) sys.exit(1) output = [] diff --git a/test/test_main.py b/test/test_main.py index 460b5488..8ebbacaa 100644 --- a/test/test_main.py +++ b/test/test_main.py @@ -145,6 +145,22 @@ def test_ansible_config_for_galaxy(exec_env_definition_file, tmpdir): assert f'ADD {constants.user_content_subfolder}/ansible.cfg ~/.ansible.cfg' in content +def test_use_dockerfile_with_podman(exec_env_definition_file, tmpdir): + path = exec_env_definition_file(content={'version': 1}) + aee = AnsibleBuilder( + filename=path, build_context=tmpdir.mkdir('bc'), + container_runtime='podman', output_filename='Dockerfile' + ) + aee.build() + + assert aee.containerfile.path.endswith('Dockerfile') + + with open(aee.containerfile.path) as f: + content = f.read() + + assert 'FROM' in content + + class TestDefinitionErrors: def test_definition_syntax_error(self, data_dir): diff --git a/test/test_utils.py b/test/test_utils.py index 26ef9687..b0bb7ce3 100644 --- a/test/test_utils.py +++ b/test/test_utils.py @@ -3,6 +3,7 @@ from pathlib import Path import pytest +from unittest import mock from ansible_builder.utils import write_file, copy_file, run_command @@ -84,3 +85,23 @@ def test_failed_command_with_allow_error(): allow_error=True ) assert rc == 1, f'Failed on iteration {i}' + + +@pytest.mark.run_command +def test_invalid_non_docker_command(caplog): + with pytest.raises(SystemExit): + run_command(['thisisnotacommand'], capture_output=True) + record = caplog.records[-1] # final log message emitted + assert 'You do not have thisisnotacommand installed' in record.msg + assert 'container-runtime' not in record.msg + + +@pytest.mark.run_command +def test_invalid_docker_command(caplog): + with mock.patch('ansible_builder.utils.subprocess.Popen', side_effect=FileNotFoundError): + with mock.patch('ansible_builder.utils.shutil.which', return_value=False): + with pytest.raises(SystemExit): + run_command(['docker', 'history', 'quay.io/foo/fooooo'], capture_output=True) + record = caplog.records[-1] # final log message emitted + assert 'You do not have docker installed' in record.msg + assert 'podman: not installed, docker: not installed' in record.msg