Repository: cekit/cekit

Issue:
AttributeError: 'Module' object has no attribute 'version' with certain module dependency graph
Given this stripped-down example of inter-dependent modules, that looks like this:

![graph-e6af185b1aaa16e8c7af0bb8213b0c32f0dc389d](https://user-images.githubusercontent.com/63694/47289863-9d253d00-d5f4-11e8-9ecd-52bb87b44520.png)

The version dependency from image.yaml is crucial to reproduce the bug

image.yaml:
```
schema_version: 1

from: "rhel7"
name: "testimage"
version: "1.0"

modules:
  repositories:
  - path: modules
  install:
  - name: a
    version: "foo"
  - name: b
```
modules/a/module.yaml:
```
schema_version: 1
name: a
version: "foo"
```
modules/b/module.yaml:
```
schema_version: 1
name: b
version: '1.0'

modules:
  install:
  - name: c
```
modules/c/module.yaml:
```
schema_version: 1
name: c
version: '1.0'

modules:
  install:
  - name: d
```
modules/d/module.yaml:
```
schema_version: 1
name: d
version: '1.0'

modules:
  install:
  - name: a
```

The result is as follows

```
$ cekit -v generate
2018-10-19 08:35:32,942 cekit        DEBUG    Running version 2.1.4
2018-10-19 08:35:32,943 cekit        INFO     Generating files for docker engine.
2018-10-19 08:35:32,944 cekit        DEBUG    Loading descriptor from path 'image.yaml'.
2018-10-19 08:35:32,974 cekit        INFO     Initializing image descriptor...
2018-10-19 08:35:32,974 cekit        DEBUG    Retrieving module repositories for 'testimage'
2018-10-19 08:35:32,975 cekit        DEBUG    Preparing resource 'modules'
2018-10-19 08:35:32,975 cekit        DEBUG    Copying repository from '/home/ce/mod-bug/strip/redhat-openjdk-18-openshift-image/modules' to 'target/repo/modules'.
2018-10-19 08:35:32,982 cekit        DEBUG    Loading descriptor from path 'target/repo/modules/a/module.yaml'.
2018-10-19 08:35:32,985 cekit        DEBUG    Retrieving module repositories for 'a'
2018-10-19 08:35:32,985 cekit        DEBUG    No module repositories specified in descriptor
2018-10-19 08:35:32,985 cekit        DEBUG    Adding module 'a', path: 'target/repo/modules/a'
2018-10-19 08:35:32,985 cekit        DEBUG    Loading descriptor from path 'target/repo/modules/b/module.yaml'.
2018-10-19 08:35:32,989 cekit        DEBUG    Retrieving module repositories for 'b'
2018-10-19 08:35:32,989 cekit        DEBUG    No module repositories specified in descriptor
2018-10-19 08:35:32,989 cekit        DEBUG    Adding module 'b', path: 'target/repo/modules/b'
2018-10-19 08:35:32,989 cekit        DEBUG    Loading descriptor from path 'target/repo/modules/c/module.yaml'.
2018-10-19 08:35:32,993 cekit        DEBUG    Retrieving module repositories for 'c'
2018-10-19 08:35:32,993 cekit        DEBUG    No module repositories specified in descriptor
2018-10-19 08:35:32,993 cekit        DEBUG    Adding module 'c', path: 'target/repo/modules/c'
2018-10-19 08:35:32,993 cekit        DEBUG    Loading descriptor from path 'target/repo/modules/d/module.yaml'.
2018-10-19 08:35:32,996 cekit        DEBUG    Retrieving module repositories for 'd'
2018-10-19 08:35:32,996 cekit        DEBUG    No module repositories specified in descriptor
2018-10-19 08:35:32,997 cekit        DEBUG    Adding module 'd', path: 'target/repo/modules/d'
2018-10-19 08:35:32,997 cekit        DEBUG    Loading descriptor from path 'target/repo/modules/a/module.yaml'.
2018-10-19 08:35:32,999 cekit        DEBUG    Retrieving module repositories for 'a'
2018-10-19 08:35:32,999 cekit        DEBUG    No module repositories specified in descriptor
2018-10-19 08:35:32,999 cekit        DEBUG    Adding module 'a', path: 'target/repo/modules/a'
2018-10-19 08:35:32,999 cekit        DEBUG    Loading descriptor from path 'target/repo/modules/b/module.yaml'.
2018-10-19 08:35:33,004 cekit        DEBUG    Retrieving module repositories for 'b'
2018-10-19 08:35:33,004 cekit        DEBUG    No module repositories specified in descriptor
2018-10-19 08:35:33,004 cekit        DEBUG    Adding module 'b', path: 'target/repo/modules/b'
2018-10-19 08:35:33,004 cekit        DEBUG    Loading descriptor from path 'target/repo/modules/c/module.yaml'.
2018-10-19 08:35:33,007 cekit        DEBUG    Retrieving module repositories for 'c'
2018-10-19 08:35:33,007 cekit        DEBUG    No module repositories specified in descriptor
2018-10-19 08:35:33,007 cekit        DEBUG    Adding module 'c', path: 'target/repo/modules/c'
2018-10-19 08:35:33,007 cekit        DEBUG    Loading descriptor from path 'target/repo/modules/d/module.yaml'.
2018-10-19 08:35:33,011 cekit        DEBUG    Retrieving module repositories for 'd'
2018-10-19 08:35:33,011 cekit        DEBUG    No module repositories specified in descriptor
2018-10-19 08:35:33,011 cekit        DEBUG    Adding module 'd', path: 'target/repo/modules/d'
2018-10-19 08:35:33,012 cekit        DEBUG    Preparing module 'b' requested by 'testimage'.
2018-10-19 08:35:33,012 cekit        DEBUG    Copying module 'b' to: 'target/image/modules/b'
2018-10-19 08:35:33,012 cekit        DEBUG    Preparing module 'c' requested by 'b'.
2018-10-19 08:35:33,013 cekit        DEBUG    Copying module 'c' to: 'target/image/modules/c'
2018-10-19 08:35:33,013 cekit        DEBUG    Preparing module 'd' requested by 'c'.
2018-10-19 08:35:33,013 cekit        DEBUG    Copying module 'd' to: 'target/image/modules/d'
2018-10-19 08:35:33,014 cekit        DEBUG    Preparing module 'a' requested by 'd'.
2018-10-19 08:35:33,014 cekit        DEBUG    Copying module 'a' to: 'target/image/modules/a'
2018-10-19 08:35:33,017 cekit        DEBUG    Merging 'a' module into 'd'.
2018-10-19 08:35:33,017 cekit        DEBUG    Merging 'd' module into 'c'.
2018-10-19 08:35:33,017 cekit        DEBUG    Merging 'c' module into 'b'.
2018-10-19 08:35:33,018 cekit        DEBUG    Merging 'b' module into 'testimage'.
2018-10-19 08:35:33,018 cekit        DEBUG    Preparing module 'a' requested by 'testimage'.
2018-10-19 08:35:33,018 cekit        DEBUG    Loading descriptor from path 'target/image/modules/a/module.yaml'.

Traceback (most recent call last):
  File "/usr/bin/cekit", line 9, in <module>
    load_entry_point('cekit==2.1.4', 'console_scripts', 'cekit')()
  File "/usr/lib/python2.7/site-packages/cekit/cli.py", line 283, in run
    Cekit().parse().run()
  File "/usr/lib/python2.7/site-packages/cekit/cli.py", line 222, in run
    generator.prepare_modules()
  File "/usr/lib/python2.7/site-packages/cekit/generator/base.py", line 102, in prepare_modules
    os.path.join(self.target, 'image', 'modules'))
  File "/usr/lib/python2.7/site-packages/cekit/module.py", line 40, in copy_module_to_target
    check_module_version(dest, version)
  File "/usr/lib/python2.7/site-packages/cekit/module.py", line 54, in check_module_version
    if descriptor.version != version:
AttributeError: 'Module' object has no attribute 'version'
```

Discussion:
jmtd: I might raise a second PR to implement a test based on the stuff in #322 comments
jmtd: > I might raise a second PR to implement a test based on the stuff in #322 comments

done

Patch:
diff --git a/cekit/builders/docker_builder.py b/cekit/builders/docker_builder.py
index cca5865..7383404 100644
--- a/cekit/builders/docker_builder.py
+++ b/cekit/builders/docker_builder.py
@@ -50,9 +50,9 @@ class DockerBuilder(Builder):
         logger.info("Building container image...")
 
         try:
-            last_tag = ""
+            docker_layer_ids = []
             out = docker_client.build(**args)
-            lastmsg = ""
+            build_log = [""]
             for line in out:
                 if b'stream' in line:
                     line = yaml.safe_load(line)['stream']
@@ -63,14 +63,17 @@ class DockerBuilder(Builder):
                     raise CekitError("Image build failed: '%s'" % line)
 
                 if '---> Running in ' in line:
-                    last_tag = line.split(' ')[-1]
+                    docker_layer_ids.append(line.split(' ')[-1])
 
-                if line != lastmsg:
+                if '---> Using cache' in build_log[-1]:
+                    docker_layer_ids.append(line.split(' ')[-1])
+
+                if line != build_log[-1]:
                     # this prevents poluting cekit log with dowloading/extracting msgs
                     log_msg = ANSI_ESCAPE.sub('', line).strip()
                     for msg in log_msg.split('\n'):
                         logger.info('Docker: %s' % msg)
-                    lastmsg = line
+                    build_log.append(line)
 
             for tag in self._tags[1:]:
                 if ':' in tag:
@@ -82,14 +85,14 @@ class DockerBuilder(Builder):
                         % ", ".join(self._tags))
 
         except Exception as ex:
-            if last_tag:
-                failed_img = self._tags[0] + '-failed'
-                if ':' in failed_img:
-                    img_repo, img_tag = failed_img.split(":")
-                    docker_client.commit(last_tag, img_repo, tag=img_tag)
-                else:
-                    docker_client.commit(last_tag, failed_img)
-
+            msg = "Image build failed, see logs above."
+            if len(docker_layer_ids) >= 2:
                 logger.error("You can look inside the failed image by running "
-                             "'docker run --rm -ti %s bash'" % failed_img)
-            raise CekitError("Image build failed, see logs above.", ex)
+                             "'docker run --rm -ti %s bash'" % docker_layer_ids[-2])
+            if "To enable Red Hat Subscription Management repositories:" in ' '.join(build_log) and \
+               not os.path.exists(os.path.join(self.target, 'image', 'repos')):
+                msg = "Image build failed with a yum error and you don't " \
+                    "have any yum repository configured, please check " \
+                    "your image/module descriptor for proper repository " \
+                    " definitions."
+            raise CekitError(msg, ex)
diff --git a/cekit/cli.py b/cekit/cli.py
index edc011b..c97f557 100644
--- a/cekit/cli.py
+++ b/cekit/cli.py
@@ -59,6 +59,12 @@ class Cekit(object):
                             dest='work_dir',
                             help="Location of Cekit working directory.")
 
+        parser.add_argument('--package-manager',
+                            dest='package_manager',
+                            choices=['yum', 'microdnf'],
+                            help='Package manager to use. Supports yum and microdnf, \
+                                defaults: yum')
+
         test_group = parser.add_argument_group('test',
                                                "Arguments valid for the 'test' target")
 
@@ -182,7 +188,8 @@ class Cekit(object):
 
         config.configure(self.args.config, {'redhat': self.args.redhat,
                                             'work_dir': self.args.work_dir,
-                                            'addhelp': self.args.addhelp})
+                                            'addhelp': self.args.addhelp,
+                                            'package_manager': self.args.package_manager})
 
         cleanup(self.args.target)
 
@@ -191,7 +198,8 @@ class Cekit(object):
         params = {
             'addhelp': config.get('doc', 'addhelp'),
             'redhat':  config.get('common', 'redhat'),
-            'help_template': config.get('doc', 'help_template')
+            'help_template': config.get('doc', 'help_template'),
+            'package_manager': config.get('common', 'package_manager')
         }
 
         self.generator = Generator(self.args.descriptor,
@@ -273,7 +281,7 @@ class Cekit(object):
             if self.args.verbose:
                 logger.exception(e)
             else:
-                logger.error(str(e))
+                logger.error(e.message)
             sys.exit(1)
 
 
diff --git a/cekit/config.py b/cekit/config.py
index e6f8fe0..75cfa5d 100644
--- a/cekit/config.py
+++ b/cekit/config.py
@@ -22,6 +22,8 @@ class Config(object):
             cls.cfg['common']['redhat'] = cmdline_args.get('redhat')
         if cmdline_args.get('work_dir'):
             cls.cfg['common']['work_dir'] = cmdline_args.get('work_dir')
+        if cmdline_args.get('package_manager'):
+            cls.cfg['common']['package_manager'] = cmdline_args.get('package_manager')
         if isinstance(cmdline_args.get('addhelp'), bool):
             cls.cfg['doc']['addhelp'] = cmdline_args.get('addhelp')
 
@@ -39,6 +41,8 @@ class Config(object):
         cls.cfg['common']['work_dir'] = cls.cfg.get('common').get('work_dir', '~/.cekit')
         cls.cfg['common']['redhat'] = yaml.safe_load(
             cls.cfg.get('common', {}).get('redhat', 'False'))
+        cls.cfg['common']['package_manager'] = yaml.safe_load(
+            cls.cfg.get('common', {}).get('package_manager', 'yum'))
 
         cls.cfg['doc'] = cls.cfg.get('doc', {})
         cls.cfg['doc']['addhelp'] = yaml.safe_load(
diff --git a/cekit/errors.py b/cekit/errors.py
index 6d45af9..54d1627 100644
--- a/cekit/errors.py
+++ b/cekit/errors.py
@@ -1,7 +1,5 @@
+class CekitError(Exception):
 
-class Error(Exception):
-    pass
-
-
-class CekitError(Error):
-    pass
+    def __init__(self, message, *args,  **kwargs):
+        super(CekitError, self).__init__(message, *args, **kwargs)
+        self.message = message
diff --git a/cekit/generator/base.py b/cekit/generator/base.py
index a2d00ce..cfa46b2 100644
--- a/cekit/generator/base.py
+++ b/cekit/generator/base.py
@@ -185,6 +185,7 @@ class Generator(object):
         if self._params.get('redhat'):
             self._inject_redhat_defaults()
 
+        self.image['pkg_manager'] = self._params.get('package_manager', 'yum')
         self.image.process_defaults()
 
         template_file = os.path.join(os.path.dirname(__file__),
diff --git a/cekit/module.py b/cekit/module.py
index 711b7a8..c3ca796 100644
--- a/cekit/module.py
+++ b/cekit/module.py
@@ -40,7 +40,7 @@ def copy_module_to_target(name, version, target):
                 check_module_version(dest, version)
 
             if not os.path.exists(dest):
-                logger.debug("Copying module '%s' to: '%s'" % (name, dest))
+                logger.debug("Copying module '%s' from '%s' to: '%s'" % (name, module.path, dest))
                 shutil.copytree(module.path, dest)
             return module
 
@@ -51,9 +51,9 @@ def check_module_version(path, version):
     descriptor = Module(tools.load_descriptor(os.path.join(path, 'module.yaml')),
                         path,
                         os.path.dirname(os.path.abspath(os.path.join(path, 'module.yaml'))))
-    if descriptor.version != version:
+    if hasattr(descriptor, 'version') and descriptor.version != version:
         raise CekitError("Requested conflicting version '%s' of module '%s'" %
-                             (version, descriptor['name']))
+                     (version, descriptor['name']))
 
 
 def get_dependencies(descriptor, base_dir):
diff --git a/cekit/templates/template.jinja b/cekit/templates/template.jinja
index bdde650..8daa324 100644
--- a/cekit/templates/template.jinja
+++ b/cekit/templates/template.jinja
@@ -1,3 +1,29 @@
+{% macro repo_create(pkg_manager, repositories) -%}
+    {% if pkg_manager in ['microdnf', 'yum'] %}
+        {% for repo in repositories %}
+repos/{{ repo.filename }} \
+        {% endfor %}
+    /etc/yum.repos.d/
+    {% endif %}
+{%- endmacro %}
+{% macro repo_remove(pkg_manager, repositories) -%}
+    {% if pkg_manager in ['microdnf', 'yum'] %}
+rm{% for repo in repositories %} /etc/yum.repos.d/{{ repo.filename }}{% endfor %}
+    {% endif %}
+{%- endmacro %}
+{% macro repo_install(pkg_manager, rpm) -%}
+    {% if pkg_manager in ['microdnf', 'yum'] %}
+{{ pkg_manager }} install -y {{ rpm }} \
+    && {{ pkg_manager }} clean all && rm -rf /var/cache/yum
+    {% endif %}
+{%- endmacro %}
+{% macro pkg_install(pkg_manager, packages) -%}
+    {% if pkg_manager in ['microdnf', 'yum'] %}
+{{ pkg_manager }} install -y {%- for package in packages %} {{ package }}{% endfor %} \
+    && {{ pkg_manager }} clean all && rm -rf /var/cache/yum \
+    && rpm -q {% for package in packages %}{{ package }}{% endfor %}
+    {% endif %}
+{%- endmacro %}
 # Copyright 2017 Red Hat
 #
 # Licensed under the Apache License, Version 2.0 (the "License");
@@ -23,30 +49,24 @@ USER root
 {% if packages and packages.install %}
 {% if packages.repositories_injected %}
 # Add custom repo files
-COPY \ {% for repo in packages.repositories_injected %} 
-    repos/{{ repo.filename }} \
-{% endfor %}
-    /etc/yum.repos.d/
+COPY {{ repo_create(pkg_manager, packages.repositories_injected) }}
 
 {% endif %}
 {% if packages.repositories %}
 {% for repo in packages.repositories %}
 {% if repo.present and repo.rpm %}
-RUN yum install -y {{ repo.rpm }} \
-    && yum clean all && rm -rf /var/cache/yum
+RUN {{ repo_install(pkg_manager, repo.rpm) }}
 
 {% endif %}
 {% endfor %}
 {% endif %}
 
 # Install required RPMs and ensure that the packages were installed
-RUN yum install -y {%- for package in packages.install %} {{ package }}{% endfor %} \
-    && yum clean all && rm -rf /var/cache/yum && \
-    rpm -q {% for package in packages.install %} {{ package }}{% endfor %}
+RUN {{ pkg_install(pkg_manager, packages.install) }}
 
 {% if packages.repositories_injected %}
 # Remove custom repo files
-RUN rm{% for repo in packages.repositories_injected %} /etc/yum.repos.d/{{ repo.filename }}{% endfor %}
+RUN {{ repo_remove(pkg_manager, packages.repositories_injected) }}
 
 {% endif %}
 
diff --git a/completion/bash/cekit b/completion/bash/cekit
index 2398fe7..13c1599 100755
--- a/completion/bash/cekit
+++ b/completion/bash/cekit
@@ -81,6 +81,7 @@ _cekit_complete()
         options+='--version '
         options+='--config '
         options+='--redhat '
+        options+='--package-manager '
         COMPREPLY=( $( compgen -W "$options" -- $cur ) )
         return
     fi
diff --git a/completion/zsh/_cekit b/completion/zsh/_cekit
index cb33cc1..b2b0db7 100644
--- a/completion/zsh/_cekit
+++ b/completion/zsh/_cekit
@@ -30,6 +30,7 @@ _cekit() {
     "--config[path for cekit config file (~/.cekit/config is default)]:config:_files" \
     "--redhat[Set default options for Red Hat internal infrastructure]" \
     "--work-dir[Location of cekit working directory, it's used to store dist-git repos]:workdir:_files -/" \
+    "--package-manager[Package  manager to use for installing dependencies. default: 'yum']:package_manager:" \
     "*--tag[tag used to build/test the image, can be used multiple times]:tags:" \
     "*--overrides[a YAML object to override image descriptor, can be used multiple times]:overrides:" \
     "*--overrides-file[path to a file containing overrides, can be used multiple times]:overrides-files:_files" \
diff --git a/docs/build.rst b/docs/build.rst
index 74eca71..33145df 100644
--- a/docs/build.rst
+++ b/docs/build.rst
@@ -23,6 +23,7 @@ You can execute an container image build by running:
 * `--add-help`` -- add generated `help.md` file to the image
 * `--no-add-help`` -- don't add generated `help.md` file to the image
 * ``--work-dir`` -- sets Cekit works directory where dist_git repositories are cloned into See :ref:`Configuration section for work_dir<workdir_config>`
+* ``--package-manager`` -- allows selecting between different package managers such as ``yum`` or ``microdnf``. Defaults to ``yum```
 * ``--build-engine`` -- a builder engine to use ``osbs``, ``buildah`` or ``docker`` [#f1]_
 * ``--build-pull`` -- ask a builder engine to check and fetch latest base image
 * ``--build-osbs-stage`` -- use ``rhpkg-stage`` tool instead of ``rhpkg``
@@ -32,7 +33,7 @@ You can execute an container image build by running:
 * ``--build-osbs-commit-msg`` -- custom commit message for dist-git
 * ``--build-osbs-nowait`` -- run `rhpkg container-build` with `--nowait` option specified
 * ``--build-tech-preview`` [#f2]_ -- updates image descriptor ``name`` key to contain ``-tech-preview`` suffix in family part of the image name
-  
+
   **Example**: If your ``name`` in image descriptor is: ``jboss-eap-7/eap7``, generated name will be: ``jboss-eap-7-tech-preview/eap7``.
 
 .. [#f1] docker build engine is default


Test patch:
diff --git a/tests/issue_322/image.yaml b/tests/issue_322/image.yaml
new file mode 100644
index 0000000..ba3ceb0
--- /dev/null
+++ b/tests/issue_322/image.yaml
@@ -0,0 +1,13 @@
+schema_version: 1
+
+from: "rhel7"
+name: "testimage"
+version: "1.0"
+
+modules:
+  repositories:
+  - path: modules
+  install:
+  - name: a
+    version: "foo"
+  - name: b
diff --git a/tests/issue_322/modules/a/module.yaml b/tests/issue_322/modules/a/module.yaml
new file mode 100644
index 0000000..eac2e6b
--- /dev/null
+++ b/tests/issue_322/modules/a/module.yaml
@@ -0,0 +1,3 @@
+schema_version: 1
+name: a
+version: "foo"
diff --git a/tests/issue_322/modules/b/module.yaml b/tests/issue_322/modules/b/module.yaml
new file mode 100644
index 0000000..7263d7b
--- /dev/null
+++ b/tests/issue_322/modules/b/module.yaml
@@ -0,0 +1,7 @@
+schema_version: 1
+name: b
+version: '1.0'
+
+modules:
+  install:
+  - name: c
diff --git a/tests/issue_322/modules/c/module.yaml b/tests/issue_322/modules/c/module.yaml
new file mode 100644
index 0000000..15e74c5
--- /dev/null
+++ b/tests/issue_322/modules/c/module.yaml
@@ -0,0 +1,7 @@
+schema_version: 1
+name: c
+version: '1.0'
+
+modules:
+  install:
+  - name: d
diff --git a/tests/issue_322/modules/d/module.yaml b/tests/issue_322/modules/d/module.yaml
new file mode 100644
index 0000000..9e6fb07
--- /dev/null
+++ b/tests/issue_322/modules/d/module.yaml
@@ -0,0 +1,7 @@
+schema_version: 1
+name: d
+version: '1.0'
+
+modules:
+  install:
+  - name: a
diff --git a/tests/test_dockerfile.py b/tests/test_dockerfile.py
index ba4b627..971fd9f 100644
--- a/tests/test_dockerfile.py
+++ b/tests/test_dockerfile.py
@@ -143,6 +143,21 @@ def test_dockerfile_docker_odcs_rpm(tmpdir, mocker):
     generator.render_dockerfile()
     regex_dockerfile(target, 'RUN yum install -y foo-repo.rpm')
 
+def test_dockerfile_docker_odcs_rpm_microdnf(tmpdir, mocker):
+    mocker.patch.object(subprocess, 'check_output', return_value=odcs_fake_resp)
+    mocker.patch.object(Repository, 'fetch')
+    target = str(tmpdir.mkdir('target'))
+    desc_part = {'packages': {'repositories': [{'name': 'foo',
+                                                'rpm': 'foo-repo.rpm'}],
+                              'install': ['a']}}
+
+    generator = prepare_generator(target, desc_part, 'image')
+    generator._params['package_manager'] = 'microdnf'
+    generator.prepare_repositories()
+    generator.render_dockerfile()
+    regex_dockerfile(target, 'RUN microdnf install -y foo-repo.rpm')
+    regex_dockerfile(target, 'RUN microdnf install -y a')
+    regex_dockerfile(target, 'rpm -q a')
 
 def test_dockerfile_osbs_odcs_pulp(tmpdir, mocker):
     mocker.patch.object(subprocess, 'check_output', return_value=odcs_fake_resp)
@@ -279,7 +294,24 @@ def test_dockerfile_osbs_odcs_rpm(tmpdir, mocker):
     generator.render_dockerfile()
     regex_dockerfile(target, 'RUN yum install -y foo-repo.rpm')
 
-    
+
+def test_dockerfile_osbs_odcs_rpm_microdnf(tmpdir, mocker):
+    mocker.patch.object(subprocess, 'check_output', return_value=odcs_fake_resp)
+    mocker.patch.object(Repository, 'fetch')
+    target = str(tmpdir.mkdir('target'))
+    desc_part = {'packages': {'repositories': [{'name': 'foo',
+                                                'rpm': 'foo-repo.rpm'}],
+                              'install': ['a']}}
+
+    generator = prepare_generator(target, desc_part, 'image', 'osbs')
+    generator._params['package_manager'] = 'microdnf'
+    generator.prepare_repositories()
+    generator.render_dockerfile()
+    regex_dockerfile(target, 'RUN microdnf install -y foo-repo.rpm')
+    regex_dockerfile(target, 'RUN microdnf install -y a')
+    regex_dockerfile(target, 'rpm -q a')
+
+
 def prepare_generator(target, desc_part, desc_type="image", engine="docker"):
     # create basic descriptor
 
diff --git a/tests/test_unit_module.py b/tests/test_unit_module.py
index 31b7e4f..49d6761 100644
--- a/tests/test_unit_module.py
+++ b/tests/test_unit_module.py
@@ -1,8 +1,10 @@
 import os
+import yaml
 
 from cekit.config import Config
 from cekit.descriptor import Module
 from cekit.module import modules
+from cekit.generator.base import Generator
 
 module_desc = {
     'schema_version': 1,
@@ -26,3 +28,18 @@ def test_modules_repos(tmpdir):
     module = Module(module_desc, os.getcwd(), '/tmp')
     module.fetch_dependencies(tmpdir)
     assert 'foo' in [m['name'] for m in modules]
+
+def test_issue_322(tmpdir):
+    """tests a particular inheritance issue reported as GitHub issue #322"""
+    target_dir = str(tmpdir.mkdir('target'))
+    artifact_dir = str(tmpdir.mkdir('artifacts'))
+    clone_dir = str(tmpdir.mkdir('clone'))
+
+    descriptor = yaml.load(open("tests/issue_322/image.yaml").read())
+    image = Module(descriptor=descriptor, path="tests/issue_322", artifact_dir=artifact_dir)
+    image.fetch_dependencies(clone_dir)
+
+    generator = Generator.__new__(Generator, descriptor_path="tests/issue_322", target=target_dir, builder="docker", overrides=None, params={})
+    generator.image = image
+    generator.target = target_dir
+    generator.prepare_modules()