Repository: cekit/cekit

Issue:
Provide support to the ARG property
**Describe the solution you'd like**
Sometimes there is a need to have a build argument to be used in a multi-stage build, as part o f the build process.

In this case, we would like to have CeKit support the usage of the ARG property in the `image.yaml` descriptor.
A simple use case can be found in this Dockerfile:


```
FROM quay.io/kiegroup/kogito-swf-builder-nightly:latest AS builder

ARG QUARKUS_EXTENSIONS

# Copy from build context to skeleton resources project    
COPY --chmod=777 * ./resources/

RUN /home/kogito/launch/build-app.sh ./resources

RUN ls -lah
RUN pwd
RUN cat /home/kogito/serverless-workflow-project/pom.xml
RUN ls -lah  /home/kogito/serverless-workflow-project/target/
#=============================
# Runtime Run
#=============================
FROM registry.access.redhat.com/ubi8/openjdk-11:latest

ENV LANG='en_US.UTF-8' LANGUAGE='en_US:en'

# We make four distinct layers so if there are application changes the library layers can be re-used
COPY --from=builder --chown=185 /home/kogito/serverless-workflow-project/target/quarkus-app/lib/ /deployments/lib/
COPY --from=builder --chown=185 /home/kogito/serverless-workflow-project/target/quarkus-app/*.jar /deployments/
COPY --from=builder --chown=185 /home/kogito/serverless-workflow-project/target/quarkus-app/app/ /deployments/app/
COPY --from=builder --chown=185 /home/kogito/serverless-workflow-project/target/quarkus-app/quarkus/ /deployments/quarkus/

EXPOSE 8080
USER 185
ENV AB_JOLOKIA_OFF=""
ENV JAVA_OPTS="-Dquarkus.http.host=0.0.0.0 -Djava.util.logging.manager=org.jboss.logmanager.LogManager"
ENV JAVA_APP_JAR="/deployments/quarkus-run.jar"
```


**Additional context**
Add any other context about the feature request here.


Discussion:
codecov[bot]: ## [Codecov](https://app.codecov.io/gh/cekit/cekit/pull/868?src=pr&el=h1&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=cekit) Report
Patch coverage: **`91.30%`** and project coverage change: **`-0.19%`** :warning:
> Comparison is base [(`ea54549`)](https://app.codecov.io/gh/cekit/cekit/commit/ea545490124271801b333df8520bc5aa23306898?el=desc&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=cekit) 90.34% compared to head [(`fd8b822`)](https://app.codecov.io/gh/cekit/cekit/pull/868?src=pr&el=desc&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=cekit) 90.16%.

<details><summary>Additional details and impacted files</summary>


```diff
@@             Coverage Diff             @@
##           develop     #868      +/-   ##
===========================================
- Coverage    90.34%   90.16%   -0.19%     
===========================================
  Files           41       42       +1     
  Lines         3210     3202       -8     
===========================================
- Hits          2900     2887      -13     
- Misses         310      315       +5     
```


| [Files Changed](https://app.codecov.io/gh/cekit/cekit/pull/868?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=cekit) | Coverage Δ | |
|---|---|---|
| [cekit/descriptor/arg.py](https://app.codecov.io/gh/cekit/cekit/pull/868?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=cekit#diff-Y2VraXQvZGVzY3JpcHRvci9hcmcucHk=) | `89.47% <89.47%> (ø)` | |
| [cekit/descriptor/\_\_init\_\_.py](https://app.codecov.io/gh/cekit/cekit/pull/868?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=cekit#diff-Y2VraXQvZGVzY3JpcHRvci9fX2luaXRfXy5weQ==) | `100.00% <100.00%> (ø)` | |
| [cekit/descriptor/image.py](https://app.codecov.io/gh/cekit/cekit/pull/868?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=cekit#diff-Y2VraXQvZGVzY3JpcHRvci9pbWFnZS5weQ==) | `88.40% <100.00%> (-0.24%)` | :arrow_down: |

... and [6 files with indirect coverage changes](https://app.codecov.io/gh/cekit/cekit/pull/868/indirect-changes?src=pr&el=tree-more&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=cekit)


</details>

[:umbrella: View full report in Codecov by Sentry](https://app.codecov.io/gh/cekit/cekit/pull/868?src=pr&el=continue&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=cekit).   
:loudspeaker: Have feedback on the report? [Share it here](https://about.codecov.io/codecov-pr-comment-feedback/?utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=cekit).


Patch:
diff --git a/.github/workflows/cekit.yml b/.github/workflows/cekit.yml
index 3e1084f..260eb42 100644
--- a/.github/workflows/cekit.yml
+++ b/.github/workflows/cekit.yml
@@ -68,6 +68,7 @@ jobs:
         mkdir $HOME/bin
         ln -s /usr/bin/true $HOME/bin/brew
         ln -s /usr/bin/true $HOME/bin/skopeo
+        ln -s /usr/bin/true $HOME/bin/buildah
     - name: Run tests
       run: |
         PV=${{ matrix.python-version }}
diff --git a/cekit/builders/buildah.py b/cekit/builders/buildah.py
index d966a1a..9f80927 100644
--- a/cekit/builders/buildah.py
+++ b/cekit/builders/buildah.py
@@ -1,9 +1,10 @@
 import logging
 import os
+from typing import List
 
 from cekit.builder import Builder
 from cekit.cekit_types import DependencyDefinition
-from cekit.tools import run_wrapper
+from cekit.tools import locate_binary, run_wrapper
 
 LOGGER = logging.getLogger("cekit")
 
@@ -24,8 +25,9 @@ class BuildahBuilder(Builder):
 
     def run(self) -> None:
         """Build container image using buildah."""
-        tags = self.params.tags
-        cmd = ["buildah", "build-using-dockerfile"]
+        tags: List[str] = self.params.tags
+        cmd: List[str] = [locate_binary("buildah"), "build-using-dockerfile"]
+        args: List[str] = self.params.args
 
         if not tags:
             tags = self.generator.get_tags()
@@ -33,6 +35,9 @@ class BuildahBuilder(Builder):
         if not self.params.no_squash:
             cmd.append("--squash")
 
+        if self.params.trace:
+            cmd += ["--log-level", "debug"]
+
         if self.params.pull:
             cmd.append("--pull-always")
 
@@ -46,6 +51,10 @@ class BuildahBuilder(Builder):
         for tag in tags:
             cmd.extend(["-t", tag])
 
+        if args:
+            for arg in args:
+                cmd.extend(["--build-arg=" + arg])
+
         LOGGER.info("Building container image...")
 
         cmd.append(os.path.join(self.target, "image"))
diff --git a/cekit/builders/docker_builder.py b/cekit/builders/docker_builder.py
index a89d7ec..9603c26 100644
--- a/cekit/builders/docker_builder.py
+++ b/cekit/builders/docker_builder.py
@@ -74,6 +74,15 @@ class DockerBuilder(Builder):
         docker_args["rm"] = True
         if self.params.platform:
             docker_args["platform"] = self.params.platform
+        if self.params.args:
+            buildargs = {}
+            for arg in self.params.args:
+                if "=" in arg:
+                    split = arg.split("=")
+                    buildargs.update({split[0]: split[1]})
+                else:
+                    buildargs.update({arg: None})
+            docker_args["buildargs"] = buildargs
 
         build_log = []
         docker_layer_ids = []
diff --git a/cekit/builders/osbs.py b/cekit/builders/osbs.py
index 3ae29c8..29f4e12 100644
--- a/cekit/builders/osbs.py
+++ b/cekit/builders/osbs.py
@@ -260,6 +260,8 @@ class OSBSBuilder(Builder):
         cmd = [self._fedpkg]
         if self.params.user:
             cmd += ["--user", self.params.user]
+        if self.params.trace:
+            cmd += ["--debug"]
         cmd += ["new-sources"] + cache_artifacts
 
         with Chdir(self.dist_git_dir):
@@ -278,6 +280,9 @@ class OSBSBuilder(Builder):
         git_tag: str = ""
         cmd: List[str] = [self._koji]
 
+        if self.params.trace:
+            cmd += ["--debug"]
+
         if self.params.user:
             cmd += ["--user", self.params.user]
 
diff --git a/cekit/builders/podman.py b/cekit/builders/podman.py
index 979f16b..674dae4 100644
--- a/cekit/builders/podman.py
+++ b/cekit/builders/podman.py
@@ -1,11 +1,10 @@
 import logging
 import os
-import shutil
 from typing import List
 
 from cekit.builder import Builder
 from cekit.cekit_types import DependencyDefinition
-from cekit.tools import run_wrapper
+from cekit.tools import locate_binary, run_wrapper
 
 LOGGER = logging.getLogger("cekit")
 
@@ -27,15 +26,16 @@ class PodmanBuilder(Builder):
     def run(self):
         """Build container image using podman."""
 
-        podman = shutil.which("podman")
-        if podman is None:
-            raise RuntimeError("podman binary was not found in the system.")
         tags: List[str] = self.params.tags
-        cmd: List[str] = [podman, "build"]
+        cmd: List[str] = [locate_binary("podman"), "build"]
+        args: List[str] = self.params.args
 
         if not tags:
             tags = self.generator.get_tags()
 
+        if self.params.trace:
+            cmd += ["--log-level", "debug"]
+
         if self.params.pull:
             cmd.append("--pull-always")
 
@@ -52,6 +52,10 @@ class PodmanBuilder(Builder):
         for tag in tags:
             cmd.extend(["-t", tag])
 
+        if args:
+            for arg in args:
+                cmd.extend(["--build-arg=" + arg])
+
         LOGGER.info("Building container image...")
 
         cmd.append(os.path.join(self.target, "image"))
diff --git a/cekit/cli.py b/cekit/cli.py
index 0cd464c..dcbd360 100644
--- a/cekit/cli.py
+++ b/cekit/cli.py
@@ -39,6 +39,7 @@ def verify_release_passed(ctx, param, value):
     show_default=True,
 )
 @click.option("-v", "--verbose", help="Enable verbose output.", is_flag=True)
+@click.option("--trace", help="Enable trace output.", is_flag=True)
 @click.option("--nocolor", help="Disable color output.", is_flag=True)
 @click.option(
     "--work-dir",
@@ -67,7 +68,7 @@ def verify_release_passed(ctx, param, value):
     show_default=True,
 )
 @click.version_option(message="%(version)s", version=__version__)
-def cli(descriptor, verbose, nocolor, work_dir, config, redhat, target):
+def cli(descriptor, verbose, nocolor, work_dir, config, redhat, target, trace):
     """
     ABOUT
 
@@ -166,12 +167,19 @@ def build(validate, dry_run, container_file, overrides):
     help="Use specified tag to tag the image after build, can be specified multiple times.",
     multiple=True,
 )
+@click.option(
+    "--build-arg",
+    "args",
+    metavar="ARG[=VALUE]",
+    help="Pass specified build-arg to the build process, can be specified multiple times.",
+    multiple=True,
+)
 @click.option(
     "--platform",
     help="Set the ARCH of the image to the provided value instead of the architecture of the host.",
 )
 @click.pass_context
-def build_docker(ctx, pull, no_squash, tags, platform):
+def build_docker(ctx, pull, no_squash, tags, args, platform):
     """
     DESCRIPTION
 
@@ -196,12 +204,19 @@ def build_docker(ctx, pull, no_squash, tags, platform):
     help="Use specified tag to tag the image after build, can be specified multiple times.",
     multiple=True,
 )
+@click.option(
+    "--build-arg",
+    "args",
+    metavar="ARG[=VALUE]",
+    help="Pass specified build-arg to the build process, can be specified multiple times.",
+    multiple=True,
+)
 @click.option(
     "--platform",
     help="Set the ARCH of the image to the provided value instead of the architecture of the host.",
 )
 @click.pass_context
-def build_buildah(ctx, pull, no_squash, tags, platform):
+def build_buildah(ctx, pull, no_squash, tags, args, platform):
     """
     DESCRIPTION
 
@@ -224,12 +239,19 @@ def build_buildah(ctx, pull, no_squash, tags, platform):
     help="Use specified tag to tag the image after build, can be specified multiple times.",
     multiple=True,
 )
+@click.option(
+    "--build-arg",
+    "args",
+    metavar="ARG[=VALUE]",
+    help="Pass specified build-arg to the build process, can be specified multiple times.",
+    multiple=True,
+)
 @click.option(
     "--platform",
     help="Set the ARCH of the image to the provided value instead of the architecture of the host.",
 )
 @click.pass_context
-def build_podman(ctx, pull, no_squash, tags, platform):
+def build_podman(ctx, pull, no_squash, tags, args, platform):
     """
     DESCRIPTION
 
diff --git a/cekit/descriptor/__init__.py b/cekit/descriptor/__init__.py
index b68f02f..6ab38a1 100644
--- a/cekit/descriptor/__init__.py
+++ b/cekit/descriptor/__init__.py
@@ -1,5 +1,6 @@
 # flake8: noqa
 # forwarding import
+from cekit.descriptor.arg import Arg
 from cekit.descriptor.base import Descriptor
 from cekit.descriptor.resource import Resource
 from cekit.descriptor.label import Label
diff --git a/cekit/descriptor/arg.py b/cekit/descriptor/arg.py
new file mode 100644
index 0000000..67f880c
--- /dev/null
+++ b/cekit/descriptor/arg.py
@@ -0,0 +1,39 @@
+import yaml
+
+from cekit.descriptor.base import Descriptor
+
+arg_schemas = yaml.safe_load(
+    """
+map:
+  name: {type: str, required: True}
+  value: {type: str}
+"""
+)
+
+
+class Arg(Descriptor):
+    """Object representing arg descriptor.
+
+    Args:
+      descriptor - yaml object with Arg
+    """
+
+    def __init__(self, descriptor: dict):
+        self.schema = arg_schemas
+        super(Arg, self).__init__(descriptor)
+
+    @property
+    def name(self) -> str:
+        return self.get("name")
+
+    @name.setter
+    def name(self, value: str):
+        self._descriptor["name"] = value
+
+    @property
+    def value(self) -> str:
+        return self.get("value")
+
+    @value.setter
+    def value(self, value: str):
+        self._descriptor["value"] = value
diff --git a/cekit/descriptor/image.py b/cekit/descriptor/image.py
index 5bba7bb..899af4d 100644
--- a/cekit/descriptor/image.py
+++ b/cekit/descriptor/image.py
@@ -9,6 +9,7 @@ import cekit
 from cekit.cekit_types import _T, RawDescriptor
 from cekit.config import Config
 from cekit.descriptor import (
+    Arg,
     Descriptor,
     Env,
     Label,
@@ -41,6 +42,7 @@ map:
   from: {type: str}
   follow_tag: {type: str}
   description: {type: text}
+  args:  {type: any}
   labels: {type: any}
   envs:  {type: any}
   ports: {type: any}
@@ -192,6 +194,10 @@ class Image(Descriptor):
     def envs(self) -> List[Env]:
         return self.get("envs", [])
 
+    @property
+    def args(self) -> List[Arg]:
+        return self.get("args", [])
+
     @property
     def ports(self) -> List[Port]:
         return self.get("ports", [])
diff --git a/cekit/templates/template.jinja b/cekit/templates/template.jinja
index 03973c0..d11ab12 100644
--- a/cekit/templates/template.jinja
+++ b/cekit/templates/template.jinja
@@ -85,11 +85,12 @@ rm{% for repo in repositories %} /etc/yum.repos.d/{{ repo.filename }}{% endfor %
  # 2. Copies any defined stage artifacts (built in previous stages as part of multi-stage builds)
  # 3. Copies module content (if a module is processed, skipped on image)
  # 4. Installs any defined packages
- # 5. Sets environment variables
- # 6. Sets labels
- # 7. Exposes ports
- # 8. Executes any defined scripts
- # 9. Exposes any defined volumes
+ # 5. Add ARG variables
+ # 6. Sets environment variables
+ # 7. Sets labels
+ # 8. Exposes ports
+ # 9. Executes any defined scripts
+ # 10. Exposes any defined volumes
  #}
 {%- macro process_module(module, parent_image=None) %}
 {% set module_type = 'module' %}
@@ -141,6 +142,17 @@ rm{% for repo in repositories %} /etc/yum.repos.d/{{ repo.filename }}{% endfor %
         {% endif -%}
         {% endif -%}
 
+        {% if module.args|count > 0 %}
+        {% for arg in module.args|sort(attribute='name') %}
+        {% if 'value' in arg %}
+        ARG {{ arg.name }}="{{ arg.value }}"
+        {% else %}
+        ARG {{ arg.name }}
+        {% endif -%}
+        {% endfor %}
+
+        {% endif -%}
+
         {% if module.envs|selectattr("value")|list|length > 0 %}
         # Set '{{ module.name }}' {{ module_type }} defined environment variables
         ENV \
@@ -193,6 +205,7 @@ rm{% for repo in repositories %} /etc/yum.repos.d/{{ repo.filename }}{% endfor %
     {% if animage.from != 'scratch' %}
     USER root
     {% endif -%}
+
     {#
      # Add Cachito support
      #}
diff --git a/cekit/tools.py b/cekit/tools.py
index f925966..f08ec23 100644
--- a/cekit/tools.py
+++ b/cekit/tools.py
@@ -377,6 +377,13 @@ def run_wrapper(
     return result
 
 
+def locate_binary(executable: str) -> str:
+    path = shutil.which(executable)
+    if path is None:
+        raise CekitError(f"{executable} binary was not found in the system.")
+    return path
+
+
 class Chdir(object):
     """Context manager for changing the current working directory"""
 
diff --git a/docs/descriptor/common_keys.rst b/docs/descriptor/common_keys.rst
index 744f797..1c15432 100644
--- a/docs/descriptor/common_keys.rst
+++ b/docs/descriptor/common_keys.rst
@@ -2,6 +2,7 @@
 .. include:: includes/description.rst
 .. include:: includes/from.rst
 .. include:: includes/follow_tag.rst
+.. include:: includes/args.rst
 .. include:: includes/envs.rst
 .. include:: includes/labels.rst
 .. include:: includes/artifacts.rst
diff --git a/docs/descriptor/includes/args.rst b/docs/descriptor/includes/args.rst
new file mode 100644
index 0000000..7f88231
--- /dev/null
+++ b/docs/descriptor/includes/args.rst
@@ -0,0 +1,21 @@
+Args
+------
+
+Key
+    ``args``
+Required
+    No
+
+Every image can include arg instructions. See https://docs.docker.com/engine/reference/builder/#arg for more
+details. CEKit makes it easy to do so with the ``args`` section. An optional default value may be provided.
+
+.. note::
+
+    ARGS will be rendered before any environment variables, labels or execution sections.
+
+.. code-block:: yaml
+
+    args:
+        - name: "user1"
+          value: "someuser"
+        - name: "QUARKUS_EXTENSION"
diff --git a/docs/descriptor/includes/module/execute.rst b/docs/descriptor/includes/module/execute.rst
index eb7479a..c9907c0 100644
--- a/docs/descriptor/includes/module/execute.rst
+++ b/docs/descriptor/includes/module/execute.rst
@@ -21,4 +21,13 @@ as defined.
         - script: install.sh
         # Then the redefine.sh file will be executed as jboss user
         - script: redefine.sh
-          user: jboss
\ No newline at end of file
+          user: jboss
+
+This will be rendered as
+
+.. code-block:: sh
+
+    RUN [ "sh" "-x" "/tmp/scripts/install.sh" ]
+
+    USER jboss
+    RUN [ "sh" "-x" "/tmp/scripts/redefine.sh" ]


Test patch:
diff --git a/tests/test_builder.py b/tests/test_builder.py
index 3ec675b..55656c8 100644
--- a/tests/test_builder.py
+++ b/tests/test_builder.py
@@ -895,7 +895,7 @@ def test_buildah_builder_run(mocker):
 
     run.assert_called_once_with(
         [
-            "buildah",
+            shutil.which("buildah"),
             "build-using-dockerfile",
             "--squash",
             "-t",
@@ -919,7 +919,7 @@ def test_buildah_builder_run_platform(mocker):
 
     run.assert_called_once_with(
         [
-            "buildah",
+            shutil.which("buildah"),
             "build-using-dockerfile",
             "--squash",
             "--platform",
@@ -945,7 +945,7 @@ def test_buildah_builder_run_pull(mocker):
 
     run.assert_called_once_with(
         [
-            "buildah",
+            shutil.which("buildah"),
             "build-using-dockerfile",
             "--squash",
             "--pull-always",
@@ -1110,7 +1110,7 @@ def test_buildah_builder_run_with_generator(mocker):
 
     run.assert_called_once_with(
         [
-            "buildah",
+            shutil.which("buildah"),
             "build-using-dockerfile",
             "--squash",
             "-t",
@@ -1134,7 +1134,7 @@ def test_buildah_builder_with_squashing_disabled(mocker):
 
     run.assert_called_once_with(
         [
-            "buildah",
+            shutil.which("buildah"),
             "build-using-dockerfile",
             "-t",
             "foo",
@@ -1149,14 +1149,91 @@ def test_buildah_builder_with_squashing_disabled(mocker):
     )
 
 
+def test_buildah_builder_with_build_arg(mocker):
+    params = {"tags": ["foo", "bar"], "no_squash": True, "args": ["KEY=VALUE"]}
+    run = mocker.patch.object(subprocess, "run")
+    builder = create_builder_object(mocker, "buildah", params)
+    builder.run()
+
+    run.assert_called_once_with(
+        [
+            shutil.which("buildah"),
+            "build-using-dockerfile",
+            "-t",
+            "foo",
+            "-t",
+            "bar",
+            "--build-arg=KEY=VALUE",
+            "something/image",
+        ],
+        stderr=None,
+        stdout=None,
+        check=True,
+        universal_newlines=True,
+    )
+
+
 def test_podman_builder_with_squashing_disabled(mocker):
-    params = {"tags": ["foo", "bar"], "no_squash": True}
+    params = {"no_squash": True}
+    run = mocker.patch.object(subprocess, "run")
+    builder = create_builder_object(mocker, "podman", params)
+    builder.generator = DockerGenerator("", "", "", [])
+    builder.generator.image = Image(
+        yaml.safe_load(
+            """
+    name: foo
+    version: 1.9
+    """
+        ),
+        "foo",
+    )
+    builder.run()
+
+    run.assert_called_once_with(
+        [
+            shutil.which("podman"),
+            "build",
+            "-t",
+            "foo:1.9",
+            "-t",
+            "foo:latest",
+            "something/image",
+        ],
+        stderr=None,
+        stdout=None,
+        check=True,
+        universal_newlines=True,
+    )
+
+
+def test_podman_builder_with_build_arg(mocker):
+    params = {"args": ["KEY=VALUE"]}
     run = mocker.patch.object(subprocess, "run")
     builder = create_builder_object(mocker, "podman", params)
+    builder.generator = DockerGenerator("", "", "", [])
+    builder.generator.image = Image(
+        yaml.safe_load(
+            """
+    name: foo
+    version: 1.9
+    """
+        ),
+        "foo",
+    )
     builder.run()
 
     run.assert_called_once_with(
-        [shutil.which("podman"), "build", "-t", "foo", "-t", "bar", "something/image"],
+        [
+            shutil.which("podman"),
+            "build",
+            "--squash",
+            "-t",
+            "foo:1.9",
+            "-t",
+            "foo:latest",
+            "--build-arg=KEY=VALUE",
+            "something/image",
+        ],
         stderr=None,
         stdout=None,
         check=True,
diff --git a/tests/test_dockerfile.py b/tests/test_dockerfile.py
index 1f8e0d9..3a8912c 100644
--- a/tests/test_dockerfile.py
+++ b/tests/test_dockerfile.py
@@ -776,6 +776,56 @@ def test_package_reinstall(tmpdir):
     regex_dockerfile(target, "reinstall -y tzdata")
 
 
+def test_args_podman(tmpdir):
+    target = str(tmpdir.mkdir("target"))
+
+    generate(
+        target,
+        ["-v", "build", "--dry-run", "--container-file", "Dockerfile", "podman"],
+        descriptor=[
+            {
+                "release": 1,
+                "version": 1,
+                "from": "fromimage",
+                "name": "targetimage",
+                "args": [{"name": "foo", "value": "bar"}, {"name": "labela"}],
+            },
+        ],
+    )
+    with open(os.path.join(target, "target", "image", "Dockerfile"), "r") as _file:
+        dockerfile = _file.read()
+    assert (
+        """        ARG foo="bar"
+        ARG labela"""
+        in dockerfile
+    )
+
+
+def test_args_buildah(tmpdir):
+    target = str(tmpdir.mkdir("target"))
+
+    generate(
+        target,
+        ["-v", "build", "--dry-run", "--container-file", "Dockerfile", "buildah"],
+        descriptor=[
+            {
+                "release": 1,
+                "version": 1,
+                "from": "fromimage",
+                "name": "targetimage",
+                "args": [{"name": "foo", "value": "bar"}, {"name": "labela"}],
+            },
+        ],
+    )
+    with open(os.path.join(target, "target", "image", "Dockerfile"), "r") as _file:
+        dockerfile = _file.read()
+    assert (
+        """        ARG foo="bar"
+        ARG labela"""
+        in dockerfile
+    )
+
+
 def generate(image_dir, command, descriptor=None, exit_code=0):
     desc = basic_config.copy()
 
diff --git a/tests/test_integ_podman.py b/tests/test_integ_podman.py
index 20f0b2e..e319c98 100644
--- a/tests/test_integ_podman.py
+++ b/tests/test_integ_podman.py
@@ -5,6 +5,7 @@ from __future__ import print_function
 import os
 import shutil
 
+from _pytest.capture import CaptureResult
 from click.testing import CliRunner
 
 from cekit.cli import cli
@@ -50,7 +51,7 @@ def test_podman_from_scratch(tmpdir):
     run_cekit(os.path.join(tmpdir, "scratch"), env={"BUILDAH_LAYERS": "false"})
 
 
-def test_podman_operator_metadata(tmpdir):
+def test_podman_operator_metadata(tmpdir, capfd):
     tmpdir = str(tmpdir)
 
     shutil.copytree(
@@ -62,6 +63,14 @@ def test_podman_operator_metadata(tmpdir):
     # See: https://bugzilla.redhat.com/show_bug.cgi?id=1746022
     run_cekit(
         os.path.join(tmpdir, "operator-metadata"),
-        args=["--redhat", "build", "podman"],
+        args=["--redhat", "--trace", "build", "podman"],
         env={"BUILDAH_LAYERS": "false"},
     )
+
+    output: CaptureResult = capfd.readouterr()
+    print(output.err)
+    assert 'level=debug msg="Called build.PersistentPreRunE' in output.err
+    assert (
+        "Successfully tagged localhost/amq7/amq-streams-rhel7-operator-metadata:latest"
+        in output.out
+    )
diff --git a/tests/test_unit_args.py b/tests/test_unit_args.py
index 349ec4e..124e7f7 100644
--- a/tests/test_unit_args.py
+++ b/tests/test_unit_args.py
@@ -24,6 +24,7 @@ def _get_class_by_name(clazz):
             ["--redhat", "build", "docker"],
             "cekit.builders.docker_builder.DockerBuilder",
             {
+                "args": (),
                 "container_file": None,
                 "descriptor": "image.yaml",
                 "verbose": False,
@@ -32,6 +33,7 @@ def _get_class_by_name(clazz):
                 "config": "~/.cekit/config",
                 "redhat": True,
                 "target": "target",
+                "trace": False,
                 "validate": False,
                 "dry_run": False,
                 "overrides": (),
@@ -46,6 +48,7 @@ def _get_class_by_name(clazz):
             ["--target", "custom-target", "build", "docker"],
             "cekit.builders.docker_builder.DockerBuilder",
             {
+                "args": (),
                 "container_file": None,
                 "descriptor": "image.yaml",
                 "verbose": False,
@@ -54,6 +57,7 @@ def _get_class_by_name(clazz):
                 "config": "~/.cekit/config",
                 "redhat": False,
                 "target": "custom-target",
+                "trace": False,
                 "validate": False,
                 "dry_run": False,
                 "overrides": (),
@@ -68,6 +72,7 @@ def _get_class_by_name(clazz):
             ["--work-dir", "custom-workdir", "build", "docker"],
             "cekit.builders.docker_builder.DockerBuilder",
             {
+                "args": (),
                 "container_file": None,
                 "descriptor": "image.yaml",
                 "verbose": False,
@@ -76,6 +81,7 @@ def _get_class_by_name(clazz):
                 "config": "~/.cekit/config",
                 "redhat": False,
                 "target": "target",
+                "trace": False,
                 "validate": False,
                 "dry_run": False,
                 "overrides": (),
@@ -90,6 +96,7 @@ def _get_class_by_name(clazz):
             ["--config", "custom-config", "build", "docker"],
             "cekit.builders.docker_builder.DockerBuilder",
             {
+                "args": (),
                 "container_file": None,
                 "descriptor": "image.yaml",
                 "verbose": False,
@@ -98,6 +105,7 @@ def _get_class_by_name(clazz):
                 "config": "custom-config",
                 "redhat": False,
                 "target": "target",
+                "trace": False,
                 "validate": False,
                 "dry_run": False,
                 "overrides": (),
@@ -112,6 +120,7 @@ def _get_class_by_name(clazz):
             ["build", "docker"],
             "cekit.builders.docker_builder.DockerBuilder",
             {
+                "args": (),
                 "container_file": None,
                 "descriptor": "image.yaml",
                 "verbose": False,
@@ -120,6 +129,7 @@ def _get_class_by_name(clazz):
                 "config": "~/.cekit/config",
                 "redhat": False,
                 "target": "target",
+                "trace": False,
                 "validate": False,
                 "dry_run": False,
                 "overrides": (),
@@ -134,6 +144,7 @@ def _get_class_by_name(clazz):
             ["build", "--overrides", "foo", "--overrides-file", "bar", "docker"],
             "cekit.builders.docker_builder.DockerBuilder",
             {
+                "args": (),
                 "container_file": None,
                 "descriptor": "image.yaml",
                 "verbose": False,
@@ -142,6 +153,7 @@ def _get_class_by_name(clazz):
                 "config": "~/.cekit/config",
                 "redhat": False,
                 "target": "target",
+                "trace": False,
                 "validate": False,
                 "dry_run": False,
                 "overrides": ("foo", "bar"),
@@ -164,6 +176,7 @@ def _get_class_by_name(clazz):
                 "config": "~/.cekit/config",
                 "redhat": False,
                 "target": "target",
+                "trace": False,
                 "validate": False,
                 "dry_run": False,
                 "overrides": (),
@@ -190,6 +203,7 @@ def _get_class_by_name(clazz):
                 "config": "~/.cekit/config",
                 "redhat": False,
                 "target": "target",
+                "trace": False,
                 "validate": False,
                 "dry_run": False,
                 "overrides": (),
@@ -216,6 +230,7 @@ def _get_class_by_name(clazz):
                 "config": "~/.cekit/config",
                 "redhat": False,
                 "target": "target",
+                "trace": False,
                 "validate": False,
                 "dry_run": False,
                 "overrides": (),
@@ -242,6 +257,7 @@ def _get_class_by_name(clazz):
                 "config": "~/.cekit/config",
                 "redhat": False,
                 "target": "target",
+                "trace": False,
                 "validate": False,
                 "dry_run": False,
                 "overrides": (),
@@ -266,6 +282,7 @@ def _get_class_by_name(clazz):
                 "config": "~/.cekit/config",
                 "redhat": False,
                 "target": "target",
+                "trace": False,
                 "overrides": (),
                 "image": "image:1.0",
                 "steps_url": "https://github.com/cekit/behave-test-steps.git",
@@ -279,6 +296,7 @@ def _get_class_by_name(clazz):
             ["build", "docker", "--pull"],
             "cekit.builders.docker_builder.DockerBuilder",
             {
+                "args": (),
                 "container_file": None,
                 "descriptor": "image.yaml",
                 "verbose": False,
@@ -287,6 +305,7 @@ def _get_class_by_name(clazz):
                 "config": "~/.cekit/config",
                 "redhat": False,
                 "target": "target",
+                "trace": False,
                 "validate": False,
                 "dry_run": False,
                 "overrides": (),
@@ -308,6 +327,7 @@ def _get_class_by_name(clazz):
                 "config": "~/.cekit/config",
                 "redhat": False,
                 "target": "target",
+                "trace": False,
                 "validate": False,
                 "dry_run": False,
                 "overrides": (),
@@ -325,6 +345,7 @@ def _get_class_by_name(clazz):
             ["build", "docker"],
             "cekit.builders.docker_builder.DockerBuilder",
             {
+                "args": (),
                 "container_file": None,
                 "descriptor": "image.yaml",
                 "verbose": False,
@@ -333,6 +354,7 @@ def _get_class_by_name(clazz):
                 "config": "~/.cekit/config",
                 "redhat": False,
                 "target": "target",
+                "trace": False,
                 "validate": False,
                 "dry_run": False,
                 "overrides": (),
@@ -354,11 +376,13 @@ def _get_class_by_name(clazz):
                 "config": "~/.cekit/config",
                 "redhat": False,
                 "target": "target",
+                "trace": False,
                 "validate": False,
                 "dry_run": False,
                 "overrides": (),
                 "pull": False,
                 "tags": (),
+                "args": (),
                 "platform": None,
                 "no_squash": False,
             },
diff --git a/tests/test_unit_descriptor.py b/tests/test_unit_descriptor.py
index a25fa59..3e640d1 100644
--- a/tests/test_unit_descriptor.py
+++ b/tests/test_unit_descriptor.py
@@ -2,7 +2,7 @@ import pytest
 import yaml
 
 from cekit.config import Config
-from cekit.descriptor import Env, Image, Label, Osbs, Packages, Port, Volume
+from cekit.descriptor import Arg, Env, Image, Label, Osbs, Packages, Port, Volume
 from cekit.errors import CekitError
 
 config = Config()
@@ -22,6 +22,29 @@ def test_label():
     assert label["value"] == "JBoss A-MQ 6.2"
 
 
+def test_arg():
+    arg = Arg(
+        yaml.safe_load(
+            """
+      name: "io.k8s.display-name"
+      value: "JBoss A-MQ 6.2"
+"""
+        )
+    )
+    assert arg.name == "io.k8s.display-name"
+    assert arg.value == "JBoss A-MQ 6.2"
+
+    arg = Arg(
+        yaml.safe_load(
+            """
+        name: "QUARKUS_EXTENSIONS"
+"""
+        )
+    )
+    assert arg.name == "QUARKUS_EXTENSIONS"
+    assert arg.value is None
+
+
 def test_env():
     env = Env(
         yaml.safe_load(
diff --git a/tests/test_unit_tools.py b/tests/test_unit_tools.py
index 8de227c..c37538a 100644
--- a/tests/test_unit_tools.py
+++ b/tests/test_unit_tools.py
@@ -13,7 +13,7 @@ from cekit import tools
 from cekit.descriptor import Descriptor, Image, Module, Osbs, Overrides, Run
 from cekit.descriptor.base import _merge_descriptors, _merge_lists
 from cekit.errors import CekitError
-from cekit.tools import Chdir, run_wrapper
+from cekit.tools import Chdir, locate_binary, run_wrapper
 
 rhel_7_os_release = '''NAME="Red Hat Enterprise Linux Server"
 VERSION="7.7 (Maipo)"
@@ -940,3 +940,9 @@ def test_run_wrapper_capture_error(tmpdir) -> None:
         assert result.stdout == ""
         assert not result.stderr.endswith("\n")
         assert result.returncode == 128
+
+
+def test_locate_binary(caplog):
+    assert "/bin/ls" in locate_binary("ls")
+    with pytest.raises(CekitError):
+        locate_binary("no-ls")