Repository: osbuild/osbuild-composer
Pull request #432 (2020-03-31 14:24:40)

tests: add coverage for common/helpers.go
This also changes the helpers.go to enable mocking all the different architectures.  If you have a better idea for the name of the newly created variable I'll be glad to hear it :)  
The `runtime.GOARCH` is actually a constant that is taken from `sys` package https://golang.org/pkg/runtime/#pkg-constants

Refs #419


Patch:
diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md
index fc6d8863a..2155b68b2 100644
--- a/CONTRIBUTING.md
+++ b/CONTRIBUTING.md
@@ -1,10 +1,14 @@
 # Contributing to osbuild-composer
 
-First of all, thank you for taking the time to contribute to osbuild-composer. In this document you will find information that can help you with your contribution.
+First of all, thank you for taking the time to contribute to osbuild-composer.
+In this document you will find information that can help you with your
+contribution.
 
 ### Running from sources
 
-We recommend using the latest stable Fedora for development but latest minor release of RHEL/CentOS 8 should work just fine as well. To run osbuild-composer from sources, follow these steps:
+We recommend using the latest stable Fedora for development but latest minor
+release of RHEL/CentOS 8 should work just fine as well. To run osbuild-composer
+from sources, follow these steps:
 
 1. Clone the repository and create a local checkout:
 
@@ -22,7 +26,8 @@ $ sudo dnf -y install cockpit-composer             # Optional: Install cockpit i
 $ sudo systemctl start cockpit.socket              # Optional: Start cockpit
 ```
 
-3. Finally, use the following to compile the project from the working directory, install it, and then run it:
+3. Finally, use the following to compile the project from the working
+directory, install it, and then run it:
 
 ```
 $ make rpm
@@ -30,11 +35,15 @@ $ sudo dnf -y install rpmbuild/RPMS/x86_64/*
 $ sudo systemctl start osbuild-composer.socket
 ```
 
-You can now open https://localhost:9090 in your browser to open cockpit console and check the "Image Builder" section.
+You can now open https://localhost:9090 in your browser to open cockpit console
+and check the "Image Builder" section.
 
-Alternatively you can use `composer-cli` to interact with the Weldr API. We don't have any client for the RCM API, so the only option there is a plain `curl`.
+Alternatively you can use `composer-cli` to interact with the Weldr API. We
+don't have any client for the RCM API, so the only option there is a
+plain `curl`.
 
-When developing the code, use `go` executable to build, run, and test you code [1]:
+When developing the code, use `go` executable to build, run, and test you
+code [1]:
 
 ```
 $ go test ./...
@@ -48,21 +57,29 @@ See [test/README.md](test/README.md) for more information about testing.
 
 ### Planning the work
 
-In general we encourage you to first fill in an issue and discuss the feature you would like to work on before you start. This can prevent the scenario where you work on something we don't want to include in our code.
+In general we encourage you to first fill in an issue and discuss the feature
+you would like to work on before you start. This can prevent the scenario where
+you work on something we don't want to include in our code.
 
-That being said, you are of course welcome to implement an example of what you would like to achieve.
+That being said, you are of course welcome to implement an example of what you
+would like to achieve.
 
 ### Creating a PR
 
 The commits in the PR should have these properties:
 
 * Preferably minimal and well documented
-  * Where minimal means: don't do unrelated changes even if the code is obviously wrong
+  * Where minimal means: don't do unrelated changes even if the code is
+    obviously wrong
   * Well documented: both code and commit message
-  * The commit message should start with the module you work on, like: `weldr: `, or `rcm:`
-* The code should compile and the composer should start, so that we can run `git bisect` on it
+  * The commit message should start with the module you work on,
+    like: `weldr: `, or `rcm:`
+* The code should compile and the composer should start, so that we can run
+  `git bisect` on it
 * All code should be covered by unit-tests
 
 ### Notes:
 
-[1] If you are running macOS, you can still compile osbuild-composer. If it doesn't work out of the box, use `-tags macos` together with any `go` command, for example: `go test -tags macos ./...`
+[1] If you are running macOS, you can still compile osbuild-composer. If it
+    doesn't work out of the box, use `-tags macos` together with any `go`
+    command, for example: `go test -tags macos ./...`
diff --git a/Makefile b/Makefile
index b5773b5a3..27495e59c 100644
--- a/Makefile
+++ b/Makefile
@@ -1,4 +1,110 @@
-COMMIT=$(shell git rev-parse HEAD)
+#
+# Maintenance Helpers
+#
+# This makefile contains targets used for development, as well as helpers to
+# aid automatization of maintenance. Unless a target is documented in
+# `make help`, it is not supported and is only meant to be used by developers
+# to aid their daily development work.
+#
+# All supported targets honor the `SRCDIR` variable to find the source-tree.
+# For most unsupported targets, you are expected to have the source-tree as
+# your working directory. To specify a different source-tree, simply override
+# the variable via `SRCDIR=<path>` on the commandline. By default, the working
+# directory is used for build output, but `BUILDDIR=<path>` allows overriding
+# it.
+#
+
+BUILDDIR ?= .
+SRCDIR ?= .
+
+RST2MAN ?= rst2man
+
+#
+# Automatic Variables
+#
+# This section contains a bunch of automatic variables used all over the place.
+# They mostly try to fetch information from the repository sources to avoid
+# hard-coding them in this makefile.
+#
+# Most of the variables here are pre-fetched so they will only ever be
+# evaluated once. This, however, means they are always executed regardless of
+# which target is run.
+#
+#     VERSION:
+#         This evaluates the `Version` field of the specfile. Therefore, it will
+#         be set to the latest version number of this repository without any
+#         prefix (just a plain number).
+#
+#     COMMIT:
+#         This evaluates to the latest git commit sha. This will not work if
+#         the source is not a git checkout. Hence, this variable is not
+#         pre-fetched but evaluated at time of use.
+#
+
+VERSION := $(shell (cd "$(SRCDIR)" && grep "^Version:" osbuild-composer.spec | sed 's/.*\([[:digit:]]\+\).*/\1/'))
+COMMIT = $(shell (cd "$(SRCDIR)" && git rev-parse HEAD))
+
+#
+# Generic Targets
+#
+# The following is a set of generic targets used across the makefile. The
+# following targets are defined:
+#
+#     help
+#         This target prints all supported targets. It is meant as
+#         documentation of targets we support and might use outside of this
+#         repository.
+#         This is also the default target.
+#
+#     $(BUILDDIR)/
+#     $(BUILDDIR)/%/
+#         This target simply creates the specified directory. It is limited to
+#         the build-dir as a safety measure. Note that this requires you to use
+#         a trailing slash after the directory to not mix it up with regular
+#         files. Lastly, you mostly want this as order-only dependency, since
+#         timestamps on directories do not affect their content.
+#
+
+.PHONY: help
+help:
+	@echo "make [TARGETS...]"
+	@echo
+	@echo "This is the maintenance makefile of osbuild. The following"
+	@echo "targets are available:"
+	@echo
+	@echo "    help:               Print this usage information."
+	@echo "    man:                Generate all man-pages"
+
+$(BUILDDIR)/:
+	mkdir -p "$@"
+
+$(BUILDDIR)/%/:
+	mkdir -p "$@"
+
+#
+# Documentation
+#
+# The following targets build the included documentation. This includes the
+# packaged man-pages, but also all other kinds of documentation that needs to
+# be generated. Note that these targets are relied upon by automatic
+# deployments to our website, as well as package manager scripts.
+#
+
+MANPAGES_RST = $(wildcard $(SRCDIR)/docs/*.[0123456789].rst)
+MANPAGES_TROFF = $(patsubst $(SRCDIR)/%.rst,$(BUILDDIR)/%,$(MANPAGES_RST))
+
+$(MANPAGES_TROFF): $(BUILDDIR)/docs/%: $(SRCDIR)/docs/%.rst | $(BUILDDIR)/docs/
+	$(RST2MAN) "$<" "$@"
+
+.PHONY: man
+man: $(MANPAGES_TROFF)
+
+#
+# Maintenance Targets
+#
+# The following targets are meant for development and repository maintenance.
+# They are not supported nor is their use recommended in scripts.
+#
 
 .PHONY: build
 build:
@@ -106,3 +212,66 @@ old-rpm: $(OLD_RPM_SPECFILE) $(RPM_TARBALL)
 	rpmbuild -bb \
 		--define "_topdir $(CURDIR)/rpmbuild" \
 		$(OLD_RPM_SPECFILE)
+
+#
+# Releasing
+#
+
+NEXT_VERSION := $(shell expr "$(VERSION)" + 1)
+
+.PHONY: release
+release:
+	@echo
+	@echo "Checklist for release of osbuild-composer-$(NEXT_VERSION):"
+	@echo
+	@echo " * Create news entry in NEWS.md with a short description of"
+	@echo "   any changes since the last release, which are relevant to"
+	@echo "   users, packagers, distributors, or dependent projects."
+	@echo
+	@echo "   Use the following template, break lines at 80ch:"
+	@echo
+	@echo "--------------------------------------------------------------------------------"
+	@echo "## CHANGES WITH $(NEXT_VERSION):"
+	@echo
+	@echo "        * ..."
+	@echo
+	@echo "        * ..."
+	@echo
+	@echo -n "        Contributions from: "
+	# We omit the contributor list if `git log` fails. If you hit this,
+	# consider fetching missing tags via `git fetch --tags`, or just copy
+	# this command and remove the stderr-redirect.
+	@echo `( git log --format='%an, ' v$(VERSION)..HEAD 2>/dev/null | sort -u | tr -d '\n' | sed 's/, $$//' ) || echo`
+	@echo
+	@echo "        - Location, YYYY-MM-DD"
+	@echo "--------------------------------------------------------------------------------"
+	@echo
+	@echo "   To get a list of changes since the last release, you may use:"
+	@echo
+	@echo "        git log v$(VERSION)..HEAD"
+	@echo
+	@echo " * Bump the project version. The canonical location so far is"
+	@echo "   'osbuild-composer.spec'."
+	@echo
+	@echo " * Make sure the spec-file is updated for the new release and"
+	@echo "   correctly supports all new features. This should already be"
+	@echo "   done by previous commits that introduced the changes, but"
+	@echo "   a sanity check does not hurt."
+	@echo
+	@echo " * Commit the version bump, specfile changes and NEWS.md in any"
+	@echo "   order you want."
+	@echo
+	@echo " * Tag the release via:"
+	@echo
+	@echo "        git tag -s -m 'osbuild-composer $(NEXT_VERSION)' v$(NEXT_VERSION) HEAD"
+	@echo
+	@echo " * Push master as well as the tag:"
+	@echo
+	@echo "        git push origin master"
+	@echo "        git push origin v$(NEXT_VERSION)"
+	@echo
+	@echo " * Create a release on github. Use 'NEWS.md' verbatim from the"
+	@echo "   top until the end of the section for this release as release"
+	@echo "   notes. Use 'v$(NEXT_VERSION)' as release name and as tag for"
+	@echo "   the release."
+	@echo
diff --git a/NEWS.md b/NEWS.md
new file mode 100644
index 000000000..9a19ae8be
--- /dev/null
+++ b/NEWS.md
@@ -0,0 +1,105 @@
+# OSBuild Composer - Operating System Image Composition Services
+
+## CHANGES WITH 9:
+
+        * Fedora is now build with updates and modules repositories
+         enabled, therefore up-to-date images are now produced.
+         
+        * A new man-page `osbuild-composer(7)` with high-level
+         description of the project is now available. It can be built
+         by the new man target in the Makfile.
+         
+        * All Fedora images have now a generic initramfs. This should
+         make the images more reproducible and less likely failing to boot
+         if the image build was done in a less usual environment.
+         
+        * Metalink is now used to access the Fedora repositories. This change
+         should hopefully lead to more stable builds.
+
+        * Composer is now released to Fedora 32 and 33 in a new
+         osbuild-composer package. The old golang-github-osbuild-composer
+         package will be automatically upgraded to the new one.
+
+        * The internal osbuild-pipeline command now has a more user-friendly
+         interface.
+
+        * The RCM API (in development, experimental) is reworked to allow
+         any distribution-architecture-image type combination.
+
+        * The work on a high-level description of image types began.
+         See image-types directory.
+         
+        * The osbuild-worker arguments are reworked, they are now much more
+         flexible.
+         
+        * The image-info tool used in the integration tests can be now run
+         on Fedora 32.
+         
+        * The unit test coverage is now much bigger, thanks to all
+         contributors!
+         
+        * Internal distribution representation is significantly reworked,
+         this simplifies the process of adding the support for all currently
+         missing architectures.
+         
+        * Integration tests were also improved, the image tests are fully
+         switched to the new Go implementation and an automatic way
+         of generating test cases is added. The weldr API coverage is also
+         much better. Several bugs in it were fixed in the process.
+         
+        * Codecov.io is now used to monitor the test coverage of the code.
+         
+        * As always, minor fixes and improvements all over the place.
+
+        Contributions from: Alexander Todorov, Brian C. Lane, David
+                            Rheinsberg, Jacob Kozol, Jakub Rusz, Jiri
+                            Kortus, Lars Karlitski, Martin Sehnoutka,
+                            Ondřej Budai, Tom Gundersen
+
+        - Liberec, 2020-04-01
+
+## CHANGES WITH 8:
+
+        * All generated pipelines now use the `org.osbuild.rpm` stage of
+          *osbuild*, rather than `org.osbuild.dnf`. This improves on splitting
+          resource acquisition from image building and should make image
+          composition more reliable and faster.
+
+        * The `STATE_DIRECTORY` environment variable now allows changing the
+          state directory path of `osbuild-composer`. This is to support older
+          systemd versions that do not pass in `StateDirectory=` to the service
+          executable.
+
+        * Minor fixes and improvements all over the place.
+
+        Contributions from: Alexander Todorov, Brian C. Lane, Jacob Kozol, Jakub
+                            Rusz, Lars Karlitski, Major Hayden, Martin
+                            Sehnoutka, Ondřej Budai, Tom Gundersen
+
+        - Berlin, 2020-03-18
+
+## CHANGES WITH 7:
+
+        * Support for `RHEL 8.1` as image type is now available.
+
+        * Semantic versioning of blueprints in the lorax API is now enforced.
+          This was always the case for the original lorax API, and *Composer*
+          now follows this as well.
+
+        * Lots of internal improvements, including many automatic tests,
+          improved error handling, better cache directory management, as well
+          as preparations to move over from `org.osbuild.dnf` to
+          `org.osbuild.rpm` in all build pipelines.
+
+        Contributions from: Alexander Todorov, Brian C. Lane, Jacob Kozol, Lars
+                            Karlitski, Major Hayden, Ondřej Budai, Tom Gundersen
+
+        - Berlin, 2020-03-05
+
+## CHANGES BEFORE 7:
+
+        * Initial implementation of 'osbuild-composer'.
+
+        Contributions from: Alexander Todorov, Brian C. Lane, Christian Kellner,
+                            Jacob Kozol, Jakub Rusz, Lars Karlitski, Martin
+                            Sehnoutka, Ondřej Budai, Tom Gundersen
diff --git a/README.md b/README.md
index 8117742b8..de5271cb8 100644
--- a/README.md
+++ b/README.md
@@ -1,62 +1,82 @@
-# osbuild-composer
+OSBuild Composer
+================
 
-An HTTP service for building bootable OS images. It provides the same API as [lorax-composer](https://github.com/weldr/lorax) but in the background it uses [osbuild](https://github.com/osbuild/osbuild) to create the images.
+Operating System Image Composition Services
 
-You can control it in [Cockpit](https://github.com/weldr/cockpit-composer) or using the [composer-cli](https://weldr.io/lorax/composer-cli.html). To get started on Fedora, run:
+The composer project is a set of HTTP services for composing operating system
+images. It builds on the pipeline execution engine of *osbuild* and defines
+its own class of images that it supports building.
 
-```
-# dnf install cockpit-composer osbuild-composer composer-cli
-# systemctl enable --now cockpit.socket
-# systemctl enable --now osbuild-composer.socket
-```
+Multiple APIs are available to access a composer service. This includes
+support for the [lorax-composer](https://github.com/weldr/lorax) API, and as
+such can serve as drop-in replacement for lorax-composer.
 
-Now you can access the service using `composer-cli`, for example:
+You can control a composer instance either directly via the provided APIs, or
+through higher-level user-interfaces from external projects. This, for
+instance, includes a
+[Cockpit Module](https://github.com/weldr/cockpit-composer) or using the
+[composer-cli](https://weldr.io/lorax/composer-cli.html) command-line tool.
 
-```
-composer-cli status show
-```
+### Project
 
-or using a browser: `http://localhost:9090`
+ * **Website**: <https://www.osbuild.org>
+ * **Bug Tracker**: <https://github.com/osbuild/osbuild-composer/issues>
 
-## API documentation
+### About
 
-Please refer to the [lorax-composer](https://github.com/weldr/lorax)'s documenation as osbuild-composer is a drop-in replacement.
+Composer is a middleman between the workhorses from *osbuild* and the
+user-interfaces like *cockpit-composer*, *composer-cli*, or others. It defines
+a set of high-level image compositions that it supports building. Builds of
+these compositions can be requested via the different APIs of *Composer*, which
+will then translate the requests into pipeline-descriptions for *osbuild*. The
+pipeline output is then either provided back to the user, or uploaded to a user
+specified target.
 
-## High-level overview
+The following image visualizes the overall architecture of the OSBuild
+infrastructure and the place that *Composer* takes:
 
 ![overview](docs/osbuild-composer.svg)
 
-### Frontends
+Consult the `osbuild-composer(7)` man-page for an introduction into composer,
+information on running your own composer instance, as well as details on the
+provided infrastructure and services.
+
+### Requirements
+
+The requirements for this project are:
 
-`osbuild-composer` is meant to be used with 2 different front-ends. The primary one, which is meant for general use, is cockpit-composer. It is part of the Cockpit project and unless you have a strong reason not to use it, you should use it. `composer-cli` is a command line tool that can be used with `osbuild-composer`.
+ * `osbuild >= 7`
+ * `systemd >= 244`
 
-### Compose
-* Compose is what the user submits over one of the frontends
-* It contains of one or more **image builds**
-* It contains zero or more **upload actions**
+At build-time, the following software is required:
 
-### Image build
-* The resulting *image* has a *type*: https://github.com/osbuild/osbuild-composer/blob/master/internal/distro/fedora30/distro.go#L19
-* Running build in osbuild-composer is referred to as a "job" (internal terminology, not related to end-user experience)
+ * `go >= 1.14`
+ * `python-docutils >= 0.13`
 
-### Job
-* What composer submits to a worker
-* Is a unit of work performed by `osbuild` (internally it is a single execution of `osbuild`)
-* Consists of **one** image build and **zero or more** Upload actions
+### Build
 
-### Image type
-* In the cockpit-composer, for examples these are image types:
-  * Openstack
-  * Azure
-  * AWS
-* As of now, we name them internally by their file format: vhd, ami, etc.
-* You can see a list of types by executing: `composer-cli compose types`
+The standard go package system is used. Consult upstream documentation for
+detailed help. In most situations the following commands are sufficient to
+build and install from source:
+
+```sh
+mkdir build
+go build -o build ./...
+```
+
+The man-pages require `python-docutils` and can be built via:
+
+```sh
+make man
+```
 
-### Upload action
-* Each image can be, but does not have to be, uploaded to a remote location
-* One image can be uploaded to multiple locations
+### Repository:
 
+ - **web**:   <https://github.com/osbuild/osbuild-composer>
+ - **https**: `https://github.com/osbuild/osbuild-composer.git`
+ - **ssh**:   `git@github.com:osbuild/osbuild-composer.git`
 
-## Testing
+### License:
 
-See [test/README.md](test/README.md)
+ - **Apache-2.0**
+ - See LICENSE file for details.
diff --git a/docs/osbuild-composer.7.rst b/docs/osbuild-composer.7.rst
new file mode 100644
index 000000000..ca6884ea6
--- /dev/null
+++ b/docs/osbuild-composer.7.rst
@@ -0,0 +1,77 @@
+================
+osbuild-composer
+================
+
+------------------------
+OSBuild Composer Service
+------------------------
+
+:Manual section: 7
+:Manual group: Miscellaneous
+
+DESCRIPTION
+===========
+
+The composer project is a set of HTTP services for composing operating system
+images. It builds on the pipeline execution engine of *osbuild* [#osbuild]_ and
+defines its own class of images that it supports building.
+
+Multiple APIs are available to access a composer service. This includes
+support for the *lorax-composer* [#lorax-github]_ API, and as
+such can serve as drop-in replacement for lorax-composer.
+
+You can control a composer instance either directly via the provided APIs, or
+through higher-level user-interfaces from external projects. This, for
+instance, includes a *Cockpit Module* [#cockpit-composer]_ or using the
+*composer-cli* [#composer-cli]_ command-line tool.
+
+Frontends
+---------
+
+*Composer* does not ship with frontends itself. However, several external
+frontends for *Composer* already exist. These include:
+
+**Cockpit Composer**
+    This module for *Cockpit* [#cockpit]_ allows a great level of control of a
+    *Composer* instance running on a cockpit-managed machine.
+
+**Composer CLI**
+    This command-line tool originated in the *lorax* [#lorax-github]_ project,
+    but can be used with *Composer* just as well.
+
+RUNNING
+=======
+
+To deploy a composer instance, all you need is to 
+
+    |
+    | # systemctl start osbuild-composer.socket
+    |
+
+Now you can access the service using `composer-cli`, for example:
+
+    |
+    | # composer-cli status show
+    |
+
+or using *Cockpit* with the *Cockpit Composer* module from a
+browser: `http://localhost:9090`
+
+SEE ALSO
+========
+
+``osbuild``\(1), ``osbuild-manifest``\(5)
+
+NOTES
+=====
+
+.. [#osbuild] OSBuild:
+              https://www.osbuild.org
+.. [#lorax-github] Lorax Composer:
+                   https://github.com/weldr/lorax
+.. [#cockpit-composer] Cockpit Composer:
+                       https://github.com/weldr/cockpit-composer
+.. [#composer-cli] Composer CLI:
+                   https://weldr.io/lorax/composer-cli.html
+.. [#cockpit] Cockpit Project:
+              https://www.cockpit-project.org/
diff --git a/golang-github-osbuild-composer.spec b/golang-github-osbuild-composer.spec
index 4fad4b50a..eecc0d802 100644
--- a/golang-github-osbuild-composer.spec
+++ b/golang-github-osbuild-composer.spec
@@ -1,6 +1,6 @@
 %global goipath         github.com/osbuild/osbuild-composer
 
-Version:        8
+Version:        9
 
 %gometa
 
diff --git a/internal/common/compose_result.go b/internal/common/compose_result.go
index c427e6e44..fcbff3f65 100644
--- a/internal/common/compose_result.go
+++ b/internal/common/compose_result.go
@@ -6,32 +6,33 @@ import (
 	"io"
 )
 
+type assembler struct {
+	Name    string          `json:"name"`
+	Options json.RawMessage `json:"options"`
+	Success bool            `json:"success"`
+	Output  string          `json:"output"`
+}
+
+type stage struct {
+	Name    string          `json:"name"`
+	Options json.RawMessage `json:"options"`
+	Success bool            `json:"success"`
+	Output  string          `json:"output"`
+}
+
+type build struct {
+	Stages  []stage `json:"stages"`
+	TreeID  string  `json:"tree_id"`
+	Success bool    `json:"success"`
+}
+
 type ComposeResult struct {
-	TreeID   string `json:"tree_id"`
-	OutputID string `json:"output_id"`
-	Build    *struct {
-		Stages []struct {
-			Name    string          `json:"name"`
-			Options json.RawMessage `json:"options"`
-			Success bool            `json:"success"`
-			Output  string          `json:"output"`
-		} `json:"stages"`
-		TreeID  string `json:"tree_id"`
-		Success bool   `json:"success"`
-	} `json:"build"`
-	Stages []struct {
-		Name    string          `json:"name"`
-		Options json.RawMessage `json:"options"`
-		Success bool            `json:"success"`
-		Output  string          `json:"output"`
-	} `json:"stages"`
-	Assembler *struct {
-		Name    string          `json:"name"`
-		Options json.RawMessage `json:"options"`
-		Success bool            `json:"success"`
-		Output  string          `json:"output"`
-	} `json:"assembler"`
-	Success bool `json:"success"`
+	TreeID    string     `json:"tree_id"`
+	OutputID  string     `json:"output_id"`
+	Build     *build     `json:"build"`
+	Stages    []stage    `json:"stages"`
+	Assembler *assembler `json:"assembler"`
+	Success   bool       `json:"success"`
 }
 
 func (cr *ComposeResult) Write(writer io.Writer) error {
diff --git a/internal/common/helpers.go b/internal/common/helpers.go
index 2f12b33fe..1c05aa9f1 100644
--- a/internal/common/helpers.go
+++ b/internal/common/helpers.go
@@ -2,14 +2,16 @@ package common
 
 import "runtime"
 
+var RuntimeGOARCH = runtime.GOARCH
+
 func CurrentArch() string {
-	if runtime.GOARCH == "amd64" {
+	if RuntimeGOARCH == "amd64" {
 		return "x86_64"
-	} else if runtime.GOARCH == "arm64" {
+	} else if RuntimeGOARCH == "arm64" {
 		return "aarch64"
-	} else if runtime.GOARCH == "ppc64le" {
+	} else if RuntimeGOARCH == "ppc64le" {
 		return "ppc64le"
-	} else if runtime.GOARCH == "s390x" {
+	} else if RuntimeGOARCH == "s390x" {
 		return "s390x"
 	} else {
 		panic("unsupported architecture")
diff --git a/internal/distro/fedora30/distro.go b/internal/distro/fedora30/distro.go
index a9fc0c1ec..2e0e7cca8 100644
--- a/internal/distro/fedora30/distro.go
+++ b/internal/distro/fedora30/distro.go
@@ -344,7 +344,6 @@ func New() *Fedora30 {
 			"libxcrypt-compat",
 			"initscripts",
 			"glibc-all-langpacks",
-			"dracut-config-generic",
 		},
 		excludedPackages: []string{
 			"dracut-config-rescue",
@@ -407,6 +406,7 @@ func New() *Fedora30 {
 		distro: &r,
 		name:   "x86_64",
 		bootloaderPackages: []string{
+			"dracut-config-generic",
 			"grub2-pc",
 		},
 		buildPackages: []string{
diff --git a/internal/distro/fedora31/distro.go b/internal/distro/fedora31/distro.go
index 9201479d0..2b1c9ad45 100644
--- a/internal/distro/fedora31/distro.go
+++ b/internal/distro/fedora31/distro.go
@@ -343,7 +343,6 @@ func New() *Fedora31 {
 			"libxcrypt-compat",
 			"initscripts",
 			"glibc-all-langpacks",
-			"dracut-config-generic",
 		},
 		excludedPackages: []string{
 			"dracut-config-rescue",
@@ -405,6 +404,7 @@ func New() *Fedora31 {
 		distro: &r,
 		name:   "x86_64",
 		bootloaderPackages: []string{
+			"dracut-config-generic",
 			"grub2-pc",
 		},
 		buildPackages: []string{
diff --git a/internal/distro/fedora32/distro.go b/internal/distro/fedora32/distro.go
index ae1ec56a2..c66aff883 100644
--- a/internal/distro/fedora32/distro.go
+++ b/internal/distro/fedora32/distro.go
@@ -344,7 +344,6 @@ func New() *Fedora32 {
 			"libxcrypt-compat",
 			"initscripts",
 			"glibc-all-langpacks",
-			"dracut-config-generic",
 		},
 		excludedPackages: []string{
 			"dracut-config-rescue",
@@ -407,6 +406,7 @@ func New() *Fedora32 {
 		distro: &r,
 		name:   "x86_64",
 		bootloaderPackages: []string{
+			"dracut-config-generic",
 			"grub2-pc",
 		},
 		buildPackages: []string{
diff --git a/internal/distro/rhel81/distro.go b/internal/distro/rhel81/distro.go
index 5c71da16b..e01d93264 100644
--- a/internal/distro/rhel81/distro.go
+++ b/internal/distro/rhel81/distro.go
@@ -166,7 +166,6 @@ func New() *RHEL81 {
 		buildPackages: []string{
 			"dnf",
 			"dosfstools",
-			"dracut-config-generic",
 			"e2fsprogs",
 			"glibc",
 			"policycoreutils",
@@ -181,6 +180,7 @@ func New() *RHEL81 {
 			"x86_64": arch{
 				name: "x86_64",
 				bootloaderPackages: []string{
+					"dracut-config-generic",
 					"grub2-pc",
 				},
 				buildPackages: []string{
@@ -212,7 +212,6 @@ func New() *RHEL81 {
 			"cloud-utils-growpart",
 			"@core",
 			"dhcp-client",
-			"dracut-config-generic",
 			"gdisk",
 			"insights-client",
 			"kernel",
@@ -287,7 +286,6 @@ func New() *RHEL81 {
 			"kernel",
 			"firewalld",
 			"chrony",
-			"dracut-config-generic",
 			"langpacks-en",
 		},
 		excludedPackages: []string{
@@ -309,7 +307,6 @@ func New() *RHEL81 {
 		packages: []string{
 			"@core",
 			"chrony",
-			"dracut-config-generic",
 			"firewalld",
 			"kernel",
 			"langpacks-en",
@@ -345,7 +342,6 @@ func New() *RHEL81 {
 			"python3-jsonschema",
 			"qemu-guest-agent",
 			"cloud-utils-growpart",
-			"dracut-config-generic",
 			"dracut-norescue",
 			"tar",
 			"tcpdump",
@@ -422,10 +418,6 @@ func New() *RHEL81 {
 			"@Core",
 			"langpacks-en",
 
-			// Don't run dracut in host-only mode, in order to pull in
-			// the hv_vmbus, hv_netvsc and hv_storvsc modules into the initrd.
-			"dracut-config-generic",
-
 			// From the lorax kickstart
 			"kernel",
 			"selinux-policy-targeted",
@@ -453,7 +445,6 @@ func New() *RHEL81 {
 			"kernel",
 			"firewalld",
 			"chrony",
-			"dracut-config-generic",
 			"langpacks-en",
 		},
 		excludedPackages: []string{
@@ -476,10 +467,6 @@ func New() *RHEL81 {
 			"@Core",
 			"langpacks-en",
 
-			// Don't run dracut in host-only mode, in order to pull in
-			// the hv_vmbus, hv_netvsc and hv_storvsc modules into the initrd.
-			"dracut-config-generic",
-
 			// From the lorax kickstart
 			"kernel",
 			"selinux-policy-targeted",
@@ -517,7 +504,6 @@ func New() *RHEL81 {
 		packages: []string{
 			"@core",
 			"chrony",
-			"dracut-config-generic",
 			"firewalld",
 			"kernel",
 			"langpacks-en",
diff --git a/internal/distro/rhel82/distro.go b/internal/distro/rhel82/distro.go
index 90ad1aa14..eff49c1f5 100644
--- a/internal/distro/rhel82/distro.go
+++ b/internal/distro/rhel82/distro.go
@@ -166,7 +166,6 @@ func New() *RHEL82 {
 		buildPackages: []string{
 			"dnf",
 			"dosfstools",
-			"dracut-config-generic",
 			"e2fsprogs",
 			"glibc",
 			"policycoreutils",
@@ -181,6 +180,7 @@ func New() *RHEL82 {
 			"x86_64": arch{
 				name: "x86_64",
 				bootloaderPackages: []string{
+					"dracut-config-generic",
 					"grub2-pc",
 				},
 				buildPackages: []string{
@@ -212,7 +212,6 @@ func New() *RHEL82 {
 			"cloud-utils-growpart",
 			"@core",
 			"dhcp-client",
-			"dracut-config-generic",
 			"gdisk",
 			"insights-client",
 			"kernel",
@@ -287,7 +286,6 @@ func New() *RHEL82 {
 			"kernel",
 			"firewalld",
 			"chrony",
-			"dracut-config-generic",
 			"langpacks-en",
 		},
 		excludedPackages: []string{
@@ -309,7 +307,6 @@ func New() *RHEL82 {
 		packages: []string{
 			"@core",
 			"chrony",
-			"dracut-config-generic",
 			"firewalld",
 			"kernel",
 			"langpacks-en",
@@ -345,7 +342,6 @@ func New() *RHEL82 {
 			"python3-jsonschema",
 			"qemu-guest-agent",
 			"cloud-utils-growpart",
-			"dracut-config-generic",
 			"dracut-norescue",
 			"tar",
 			"tcpdump",
@@ -422,10 +418,6 @@ func New() *RHEL82 {
 			"@Core",
 			"langpacks-en",
 
-			// Don't run dracut in host-only mode, in order to pull in
-			// the hv_vmbus, hv_netvsc and hv_storvsc modules into the initrd.
-			"dracut-config-generic",
-
 			// From the lorax kickstart
 			"kernel",
 			"selinux-policy-targeted",
@@ -453,7 +445,6 @@ func New() *RHEL82 {
 			"kernel",
 			"firewalld",
 			"chrony",
-			"dracut-config-generic",
 			"langpacks-en",
 		},
 		excludedPackages: []string{
@@ -476,10 +467,6 @@ func New() *RHEL82 {
 			"@Core",
 			"langpacks-en",
 
-			// Don't run dracut in host-only mode, in order to pull in
-			// the hv_vmbus, hv_netvsc and hv_storvsc modules into the initrd.
-			"dracut-config-generic",
-
 			// From the lorax kickstart
 			"kernel",
 			"selinux-policy-targeted",
@@ -517,7 +504,6 @@ func New() *RHEL82 {
 		packages: []string{
 			"@core",
 			"chrony",
-			"dracut-config-generic",
 			"firewalld",
 			"kernel",
 			"langpacks-en",
diff --git a/internal/rcm/api.go b/internal/rcm/api.go
index da4216776..de0978e82 100644
--- a/internal/rcm/api.go
+++ b/internal/rcm/api.go
@@ -205,9 +205,7 @@ func (api *API) submit(writer http.ResponseWriter, request *http.Request, _ http
 	}
 
 	// Push the requested compose to the store
-	composeUUID := uuid.New()
-	// nil is used as an upload target, because LocalTarget is already used in the PushCompose function
-	err = api.store.PushCompose(distro, arch, imageType, composeUUID, &blueprint.Blueprint{}, repoConfigs, packages, buildPackages, 0, nil)
+	composeID, err := api.store.PushCompose(imageType, &blueprint.Blueprint{}, repoConfigs, packages, buildPackages, 0, nil)
 	if err != nil {
 		if api.logger != nil {
 			api.logger.Println("RCM API failed to push compose:", err)
@@ -223,7 +221,7 @@ func (api *API) submit(writer http.ResponseWriter, request *http.Request, _ http
 	var reply struct {
 		UUID uuid.UUID `json:"compose_id"`
 	}
-	reply.UUID = composeUUID
+	reply.UUID = composeID
 	// TODO: handle error
 	_ = json.NewEncoder(writer).Encode(reply)
 }
diff --git a/internal/store/store.go b/internal/store/store.go
index d2a6d192d..a073eab59 100644
--- a/internal/store/store.go
+++ b/internal/store/store.go
@@ -553,11 +553,13 @@ func (s *Store) getImageBuildDirectory(composeID uuid.UUID, imageBuildID int) st
 	return fmt.Sprintf("%s/%d", s.getComposeDirectory(composeID), imageBuildID)
 }
 
-func (s *Store) PushCompose(distro distro.Distro, arch distro.Arch, imageType distro.ImageType, composeID uuid.UUID, bp *blueprint.Blueprint, repos []rpmmd.RepoConfig, packages, buildPackages []rpmmd.PackageSpec, size uint64, targets []*target.Target) error {
+func (s *Store) PushCompose(imageType distro.ImageType, bp *blueprint.Blueprint, repos []rpmmd.RepoConfig, packages, buildPackages []rpmmd.PackageSpec, size uint64, targets []*target.Target) (uuid.UUID, error) {
 	if targets == nil {
 		targets = []*target.Target{}
 	}
 
+	composeID := uuid.New()
+
 	// Compatibility layer for image types in Weldr API v0
 	imageTypeCommon, exists := common.ImageTypeFromCompatString(imageType.Name())
 	if !exists {
@@ -569,18 +571,13 @@ func (s *Store) PushCompose(distro distro.Distro, arch distro.Arch, imageType di
 
 		err := os.MkdirAll(outputDir, 0755)
 		if err != nil {
-			return fmt.Errorf("cannot create output directory for job %v: %#v", composeID, err)
+			return uuid.Nil, fmt.Errorf("cannot create output directory for job %v: %#v", composeID, err)
 		}
 	}
 
-	allRepos := append([]rpmmd.RepoConfig{}, repos...)
-	for _, source := range s.Sources {
-		allRepos = append(allRepos, source.RepoConfig())
-	}
-
-	manifestStruct, err := imageType.Manifest(bp.Customizations, allRepos, packages, buildPackages, imageType.Size(size))
+	manifestStruct, err := imageType.Manifest(bp.Customizations, repos, packages, buildPackages, imageType.Size(size))
 	if err != nil {
-		return err
+		return uuid.Nil, err
 	}
 	// FIXME: handle or comment this possible error
 	_ = s.change(func() error {
@@ -606,7 +603,7 @@ func (s *Store) PushCompose(distro distro.Distro, arch distro.Arch, imageType di
 		Targets:      targets,
 	}
 
-	return nil
+	return composeID, nil
 }
 
 // DeleteCompose deletes the compose from the state file and also removes all files on disk that are
diff --git a/internal/weldr/api.go b/internal/weldr/api.go
index 68cdf9e54..b4696d89e 100644
--- a/internal/weldr/api.go
+++ b/internal/weldr/api.go
@@ -1458,12 +1458,7 @@ func (api *API) composeHandler(writer http.ResponseWriter, request *http.Request
 		return
 	}
 
-	reply := ComposeReply{
-		BuildID: uuid.New(),
-		Status:  true,
-	}
-
-	err = api.store.PushCompose(api.distro, api.arch, imageType, reply.BuildID, bp, api.allRepositories(), packages, buildPackages, cr.Size, targets)
+	composeID, err := api.store.PushCompose(imageType, bp, api.allRepositories(), packages, buildPackages, cr.Size, targets)
 
 	// TODO: we should probably do some kind of blueprint validation in future
 	// for now, let's just 500 and bail out
@@ -1477,7 +1472,10 @@ func (api *API) composeHandler(writer http.ResponseWriter, request *http.Request
 		return
 	}
 
-	err = json.NewEncoder(writer).Encode(reply)
+	err = json.NewEncoder(writer).Encode(ComposeReply{
+		BuildID: composeID,
+		Status:  true,
+	})
 	common.PanicOnError(err)
 }
 
diff --git a/osbuild-composer.spec b/osbuild-composer.spec
index 3936b8354..3cf8c5574 100644
--- a/osbuild-composer.spec
+++ b/osbuild-composer.spec
@@ -4,7 +4,7 @@
 
 %global goipath         github.com/osbuild/osbuild-composer
 
-Version:        8
+Version:        9
 
 %gometa
 


Test patch:
diff --git a/.github/workflows/tests.yml b/.github/workflows/tests.yml
index 8415348f0..830e3c26e 100644
--- a/.github/workflows/tests.yml
+++ b/.github/workflows/tests.yml
@@ -23,7 +23,7 @@ jobs:
         id: go
 
       - name: Check out code into the Go module directory
-        uses: actions/checkout@v1
+        uses: actions/checkout@v2
 
       - name: Check that source has been prepared
         run: |
diff --git a/internal/blueprint/customizations_test.go b/internal/blueprint/customizations_test.go
new file mode 100644
index 000000000..1ebe21c3c
--- /dev/null
+++ b/internal/blueprint/customizations_test.go
@@ -0,0 +1,267 @@
+package blueprint
+
+import (
+	"github.com/stretchr/testify/assert"
+	"testing"
+)
+
+func TestGetHostname(t *testing.T) {
+
+	var expectedHostname = "Hostname"
+
+	TestCustomizations := Customizations{
+		Hostname: &expectedHostname,
+	}
+
+	retHostname := TestCustomizations.GetHostname()
+	assert.Equal(t, &expectedHostname, retHostname)
+
+}
+
+func TestGetKernel(t *testing.T) {
+
+	expectedKernel := KernelCustomization{
+		Append: "--test",
+	}
+
+	TestCustomizations := Customizations{
+		Kernel: &expectedKernel,
+	}
+
+	retKernel := TestCustomizations.GetKernel()
+
+	assert.Equal(t, &expectedKernel, retKernel)
+}
+
+func TestSSHKey(t *testing.T) {
+
+	expectedSSHKeys := []SSHKeyCustomization{
+		SSHKeyCustomization{
+			User: "test-user",
+			Key:  "test-key",
+		},
+	}
+	TestCustomizations := Customizations{
+		SSHKey: expectedSSHKeys,
+	}
+
+	retUser := TestCustomizations.GetUsers()[0].Name
+	retKey := *TestCustomizations.GetUsers()[0].Key
+
+	assert.Equal(t, expectedSSHKeys[0].User, retUser)
+	assert.Equal(t, expectedSSHKeys[0].Key, retKey)
+
+}
+
+func TestGetUsers(t *testing.T) {
+
+	Desc := "Test descritpion"
+	Pass := "testpass"
+	Key := "testkey"
+	Home := "Home"
+	Shell := "Shell"
+	Groups := []string{
+		"Group",
+	}
+	UID := 123
+	GID := 321
+
+	expectedUsers := []UserCustomization{
+		UserCustomization{
+			Name:        "John",
+			Description: &Desc,
+			Password:    &Pass,
+			Key:         &Key,
+			Home:        &Home,
+			Shell:       &Shell,
+			Groups:      Groups,
+			UID:         &UID,
+			GID:         &GID,
+		},
+	}
+
+	TestCustomizations := Customizations{
+		User: expectedUsers,
+	}
+
+	retUsers := TestCustomizations.GetUsers()
+
+	assert.ElementsMatch(t, expectedUsers, retUsers)
+}
+
+func TestGetGroups(t *testing.T) {
+
+	GID := 1234
+	expectedGroups := []GroupCustomization{
+		GroupCustomization{
+			Name: "TestGroup",
+			GID:  &GID,
+		},
+	}
+
+	TestCustomizations := Customizations{
+		Group: expectedGroups,
+	}
+
+	retGroups := TestCustomizations.GetGroups()
+
+	assert.ElementsMatch(t, expectedGroups, retGroups)
+}
+
+func TestGetTimezoneSettings(t *testing.T) {
+
+	expectedTimezone := "testZONE"
+	expectedNTPServers := []string{
+		"server",
+	}
+
+	expectedTimezoneCustomization := TimezoneCustomization{
+		Timezone:   &expectedTimezone,
+		NTPServers: expectedNTPServers,
+	}
+
+	TestCustomizations := Customizations{
+		Timezone: &expectedTimezoneCustomization,
+	}
+
+	retTimezone, retNTPServers := TestCustomizations.GetTimezoneSettings()
+
+	assert.Equal(t, expectedTimezone, *retTimezone)
+	assert.Equal(t, expectedNTPServers, retNTPServers)
+
+}
+
+func TestGetPrimaryLocale(t *testing.T) {
+
+	expectedLanguages := []string{
+		"enUS",
+	}
+	expectedKeyboard := "en"
+
+	expectedLocaleCustomization := LocaleCustomization{
+		Languages: expectedLanguages,
+		Keyboard:  &expectedKeyboard,
+	}
+
+	TestCustomizations := Customizations{
+		Locale: &expectedLocaleCustomization,
+	}
+
+	retLanguage, retKeyboard := TestCustomizations.GetPrimaryLocale()
+
+	assert.Equal(t, expectedLanguages[0], *retLanguage)
+	assert.Equal(t, expectedKeyboard, *retKeyboard)
+}
+
+func TestGetFirewall(t *testing.T) {
+
+	expectedPorts := []string{"22", "9090"}
+
+	expectedServices := FirewallServicesCustomization{
+		Enabled:  []string{"cockpit", "osbuild-composer"},
+		Disabled: []string{"TCP", "httpd"},
+	}
+
+	expectedFirewall := FirewallCustomization{
+		Ports:    expectedPorts,
+		Services: &expectedServices,
+	}
+
+	TestCustomizations := Customizations{
+		Firewall: &expectedFirewall,
+	}
+
+	retFirewall := TestCustomizations.GetFirewall()
+
+	assert.ElementsMatch(t, expectedFirewall.Ports, retFirewall.Ports)
+	assert.ElementsMatch(t, expectedFirewall.Services.Enabled, retFirewall.Services.Enabled)
+	assert.ElementsMatch(t, expectedFirewall.Services.Disabled, retFirewall.Services.Disabled)
+}
+
+func TestGetServices(t *testing.T) {
+
+	expectedServices := ServicesCustomization{
+		Enabled:  []string{"cockpit", "osbuild-composer"},
+		Disabled: []string{"sshd", "ftp"},
+	}
+
+	TestCustomizations := Customizations{
+		Services: &expectedServices,
+	}
+
+	retServices := TestCustomizations.GetServices()
+
+	assert.ElementsMatch(t, expectedServices.Enabled, retServices.Enabled)
+	assert.ElementsMatch(t, expectedServices.Disabled, retServices.Disabled)
+}
+
+func TestError(t *testing.T) {
+	expectedError := CustomizationError{
+		Message: "test error",
+	}
+
+	retError := expectedError.Error()
+
+	assert.Equal(t, expectedError.Message, retError)
+
+}
+
+//This tests calling all the functions on a Blueprint with no Customizations
+func TestNoCustomizationsInBlueprint(t *testing.T) {
+
+	TestBP := Blueprint{}
+
+	assert.Nil(t, TestBP.Customizations.GetHostname())
+	assert.Nil(t, TestBP.Customizations.GetUsers())
+	assert.Nil(t, TestBP.Customizations.GetGroups())
+	assert.Nil(t, TestBP.Customizations.GetKernel())
+	assert.Nil(t, TestBP.Customizations.GetFirewall())
+	assert.Nil(t, TestBP.Customizations.GetServices())
+
+	nilLanguage, nilKeyboard := TestBP.Customizations.GetPrimaryLocale()
+	assert.Nil(t, nilLanguage)
+	assert.Nil(t, nilKeyboard)
+
+	nilTimezone, nilNTPServers := TestBP.Customizations.GetTimezoneSettings()
+	assert.Nil(t, nilTimezone)
+	assert.Nil(t, nilNTPServers)
+}
+
+//This tests additional scenarios where GetPrimaryLocale() returns nil values
+func TestNilGetPrimaryLocale(t *testing.T) {
+
+	//Case empty Customization
+	TestCustomizationsEmpty := Customizations{}
+
+	retLanguage, retKeyboard := TestCustomizationsEmpty.GetPrimaryLocale()
+
+	assert.Nil(t, retLanguage)
+	assert.Nil(t, retKeyboard)
+
+	//Case empty Languages
+	expectedKeyboard := "en"
+	expectedLocaleCustomization := LocaleCustomization{
+		Keyboard: &expectedKeyboard,
+	}
+
+	TestCustomizations := Customizations{
+		Locale: &expectedLocaleCustomization,
+	}
+
+	retLanguage, retKeyboard = TestCustomizations.GetPrimaryLocale()
+
+	assert.Nil(t, retLanguage)
+	assert.Equal(t, expectedKeyboard, *retKeyboard)
+
+}
+
+//This tests additional scenario where GetTimezoneSEtting() returns nil values
+func TestNilGetTimezoneSettings(t *testing.T) {
+
+	TestCustomizationsEmpty := Customizations{}
+
+	retTimezone, retNTPServers := TestCustomizationsEmpty.GetTimezoneSettings()
+
+	assert.Nil(t, retTimezone)
+	assert.Nil(t, retNTPServers)
+}
diff --git a/internal/common/compose_result_test.go b/internal/common/compose_result_test.go
new file mode 100644
index 000000000..501ee171c
--- /dev/null
+++ b/internal/common/compose_result_test.go
@@ -0,0 +1,80 @@
+package common
+
+import (
+	"bytes"
+	"github.com/stretchr/testify/assert"
+	"testing"
+)
+
+func TestWriteFull(t *testing.T) {
+
+	const testOptions = `{"msg": "test"}`
+
+	testStage := stage{
+		Name:    "testStage",
+		Options: []byte(testOptions),
+		Success: true,
+		Output:  "Finished",
+	}
+
+	testBuild := build{
+		Stages:  []stage{testStage},
+		TreeID:  "treeID",
+		Success: true,
+	}
+
+	testAssembler := assembler{
+		Name:    "testAssembler",
+		Options: []byte(testOptions),
+		Success: true,
+		Output:  "Done",
+	}
+
+	testComposeResult := ComposeResult{
+		TreeID:    "TreeID",
+		OutputID:  "OutputID",
+		Build:     &testBuild,
+		Stages:    []stage{testStage},
+		Assembler: &testAssembler,
+		Success:   true,
+	}
+
+	var b bytes.Buffer
+	assert.NoError(t, testComposeResult.Write(&b))
+	expectedMessage :=
+		`Build pipeline:
+Stage testStage
+{
+  "msg": "test"
+}
+
+Output:
+Finished
+Stages:
+Stage: testStage
+{
+  "msg": "test"
+}
+
+Output:
+Finished
+Assembler testAssembler:
+{
+  "msg": "test"
+}
+
+Output:
+Done
+`
+	assert.Equal(t, expectedMessage, b.String())
+}
+
+func TestWriteEmpty(t *testing.T) {
+
+	testComposeResult := ComposeResult{}
+
+	var b bytes.Buffer
+	assert.NoError(t, testComposeResult.Write(&b))
+	assert.Equal(t, "The compose result is empty.\n", b.String())
+
+}
diff --git a/internal/common/helpers_test.go b/internal/common/helpers_test.go
new file mode 100644
index 000000000..2ec687676
--- /dev/null
+++ b/internal/common/helpers_test.go
@@ -0,0 +1,47 @@
+package common
+
+import (
+	"errors"
+	"github.com/stretchr/testify/assert"
+	"testing"
+)
+
+func TestCurrentArchAMD64(t *testing.T) {
+	origRuntimeGOARCH := RuntimeGOARCH
+	defer func() { RuntimeGOARCH = origRuntimeGOARCH }()
+	RuntimeGOARCH = "amd64"
+	assert.Equal(t, "x86_64", CurrentArch())
+}
+
+func TestCurrentArchARM64(t *testing.T) {
+	origRuntimeGOARCH := RuntimeGOARCH
+	defer func() { RuntimeGOARCH = origRuntimeGOARCH }()
+	RuntimeGOARCH = "arm64"
+	assert.Equal(t, "aarch64", CurrentArch())
+}
+
+func TestCurrentArchPPC64LE(t *testing.T) {
+	origRuntimeGOARCH := RuntimeGOARCH
+	defer func() { RuntimeGOARCH = origRuntimeGOARCH }()
+	RuntimeGOARCH = "ppc64le"
+	assert.Equal(t, "ppc64le", CurrentArch())
+}
+
+func TestCurrentArchS390X(t *testing.T) {
+	origRuntimeGOARCH := RuntimeGOARCH
+	defer func() { RuntimeGOARCH = origRuntimeGOARCH }()
+	RuntimeGOARCH = "s390x"
+	assert.Equal(t, "s390x", CurrentArch())
+}
+
+func TestCurrentArchUnsupported(t *testing.T) {
+	origRuntimeGOARCH := RuntimeGOARCH
+	defer func() { RuntimeGOARCH = origRuntimeGOARCH }()
+	RuntimeGOARCH = "UKNOWN"
+	assert.PanicsWithValue(t, "unsupported architecture", func() { CurrentArch() })
+}
+
+func TestPanicOnError(t *testing.T) {
+	err := errors.New("Error message")
+	assert.PanicsWithValue(t, err, func() { PanicOnError(err) })
+}
diff --git a/internal/jobqueue/api_test.go b/internal/jobqueue/api_test.go
index 85bcb0656..382dccbe0 100644
--- a/internal/jobqueue/api_test.go
+++ b/internal/jobqueue/api_test.go
@@ -39,7 +39,6 @@ func TestBasic(t *testing.T) {
 }
 
 func TestCreate(t *testing.T) {
-	id := uuid.MustParse("ffffffff-ffff-ffff-ffff-ffffffffffff")
 	distroStruct := fedoratest.New()
 	arch, err := distroStruct.GetArch("x86_64")
 	if err != nil {
@@ -52,17 +51,16 @@ func TestCreate(t *testing.T) {
 	store := store.New(nil)
 	api := jobqueue.New(nil, store)
 
-	err = store.PushCompose(distroStruct, arch, imageType, id, &blueprint.Blueprint{}, nil, nil, nil, 0, nil)
+	id, err := store.PushCompose(imageType, &blueprint.Blueprint{}, nil, nil, nil, 0, nil)
 	if err != nil {
 		t.Fatalf("error pushing compose: %v", err)
 	}
 
 	test.TestRoute(t, api, false, "POST", "/job-queue/v1/jobs", `{}`, http.StatusCreated,
-		`{"id":"ffffffff-ffff-ffff-ffff-ffffffffffff","image_build_id":0,"manifest":{"sources":{},"pipeline":{}},"targets":[]}`, "created", "uuid")
+		`{"id":"`+id.String()+`","image_build_id":0,"manifest":{"sources":{},"pipeline":{}},"targets":[]}`, "created")
 }
 
 func testUpdateTransition(t *testing.T, from, to string, expectedStatus int, expectedResponse string) {
-	id := uuid.MustParse("ffffffff-ffff-ffff-ffff-ffffffffffff")
 	distroStruct := fedoratest.New()
 	arch, err := distroStruct.GetArch("x86_64")
 	if err != nil {
@@ -75,20 +73,22 @@ func testUpdateTransition(t *testing.T, from, to string, expectedStatus int, exp
 	store := store.New(nil)
 	api := jobqueue.New(nil, store)
 
+	id := uuid.Nil
 	if from != "VOID" {
-		err := store.PushCompose(distroStruct, arch, imageType, id, &blueprint.Blueprint{}, nil, nil, nil, 0, nil)
+		id, err = store.PushCompose(imageType, &blueprint.Blueprint{}, nil, nil, nil, 0, nil)
 		if err != nil {
 			t.Fatalf("error pushing compose: %v", err)
 		}
+
 		if from != "WAITING" {
 			test.SendHTTP(api, false, "POST", "/job-queue/v1/jobs", `{}`)
 			if from != "RUNNING" {
-				test.SendHTTP(api, false, "PATCH", "/job-queue/v1/jobs/ffffffff-ffff-ffff-ffff-ffffffffffff/builds/0", `{"status":"`+from+`"}`)
+				test.SendHTTP(api, false, "PATCH", "/job-queue/v1/jobs/"+id.String()+"/builds/0", `{"status":"`+from+`"}`)
 			}
 		}
 	}
 
-	test.TestNonJsonRoute(t, api, false, "PATCH", "/job-queue/v1/jobs/ffffffff-ffff-ffff-ffff-ffffffffffff/builds/0", `{"status":"`+to+`"}`, expectedStatus, expectedResponse)
+	test.TestNonJsonRoute(t, api, false, "PATCH", "/job-queue/v1/jobs/"+id.String()+"/builds/0", `{"status":"`+to+`"}`, expectedStatus, expectedResponse)
 }
 
 func TestUpdate(t *testing.T) {
diff --git a/internal/osbuild/rpm_stage_test.go b/internal/osbuild/rpm_stage_test.go
new file mode 100644
index 000000000..caa4e90cd
--- /dev/null
+++ b/internal/osbuild/rpm_stage_test.go
@@ -0,0 +1,16 @@
+package osbuild
+
+import (
+	"testing"
+
+	"github.com/stretchr/testify/assert"
+)
+
+func TestNewRPMStage(t *testing.T) {
+	expectedStage := &Stage{
+		Name:    "org.osbuild.rpm",
+		Options: &RPMStageOptions{},
+	}
+	actualStage := NewRPMStage(&RPMStageOptions{})
+	assert.Equal(t, expectedStage, actualStage)
+}
diff --git a/internal/osbuild/script_stage_test.go b/internal/osbuild/script_stage_test.go
new file mode 100644
index 000000000..1c181df7d
--- /dev/null
+++ b/internal/osbuild/script_stage_test.go
@@ -0,0 +1,24 @@
+package osbuild
+
+import (
+	"testing"
+
+	"github.com/stretchr/testify/assert"
+)
+
+func TestNewScriptStageOptions(t *testing.T) {
+	expectedOptions := &ScriptStageOptions{
+		Script: "/root/test.sh",
+	}
+	actualOptions := NewScriptStageOptions("/root/test.sh")
+	assert.Equal(t, expectedOptions, actualOptions)
+}
+
+func TestNewScriptStage(t *testing.T) {
+	expectedStage := &Stage{
+		Name:    "org.osbuild.script",
+		Options: &ScriptStageOptions{},
+	}
+	actualStage := NewScriptStage(&ScriptStageOptions{})
+	assert.Equal(t, expectedStage, actualStage)
+}
diff --git a/internal/osbuild/selinux_stage_test.go b/internal/osbuild/selinux_stage_test.go
new file mode 100644
index 000000000..9aaf56209
--- /dev/null
+++ b/internal/osbuild/selinux_stage_test.go
@@ -0,0 +1,24 @@
+package osbuild
+
+import (
+	"testing"
+
+	"github.com/stretchr/testify/assert"
+)
+
+func TestNewSELinuxStageOptions(t *testing.T) {
+	expectedOptions := &SELinuxStageOptions{
+		FileContexts: "etc/selinux/targeted/contexts/files/file_contexts",
+	}
+	actualOptions := NewSELinuxStageOptions("etc/selinux/targeted/contexts/files/file_contexts")
+	assert.Equal(t, expectedOptions, actualOptions)
+}
+
+func TestNewSELinuxStage(t *testing.T) {
+	expectedStage := &Stage{
+		Name:    "org.osbuild.selinux",
+		Options: &SELinuxStageOptions{},
+	}
+	actualStage := NewSELinuxStage(&SELinuxStageOptions{})
+	assert.Equal(t, expectedStage, actualStage)
+}
diff --git a/internal/osbuild/systemd_stage_test.go b/internal/osbuild/systemd_stage_test.go
new file mode 100644
index 000000000..7d7fddbd0
--- /dev/null
+++ b/internal/osbuild/systemd_stage_test.go
@@ -0,0 +1,16 @@
+package osbuild
+
+import (
+	"testing"
+
+	"github.com/stretchr/testify/assert"
+)
+
+func TestNewSystemdStage(t *testing.T) {
+	expectedStage := &Stage{
+		Name:    "org.osbuild.systemd",
+		Options: &SystemdStageOptions{},
+	}
+	actualStage := NewSystemdStage(&SystemdStageOptions{})
+	assert.Equal(t, expectedStage, actualStage)
+}
diff --git a/internal/osbuild/timezone_stage_test.go b/internal/osbuild/timezone_stage_test.go
new file mode 100644
index 000000000..350705c21
--- /dev/null
+++ b/internal/osbuild/timezone_stage_test.go
@@ -0,0 +1,16 @@
+package osbuild
+
+import (
+	"testing"
+
+	"github.com/stretchr/testify/assert"
+)
+
+func TestNewTimezoneStage(t *testing.T) {
+	expectedStage := &Stage{
+		Name:    "org.osbuild.timezone",
+		Options: &TimezoneStageOptions{},
+	}
+	actualStage := NewTimezoneStage(&TimezoneStageOptions{})
+	assert.Equal(t, expectedStage, actualStage)
+}
diff --git a/internal/osbuild/users_stage_test.go b/internal/osbuild/users_stage_test.go
new file mode 100644
index 000000000..1b24b4774
--- /dev/null
+++ b/internal/osbuild/users_stage_test.go
@@ -0,0 +1,16 @@
+package osbuild
+
+import (
+	"testing"
+
+	"github.com/stretchr/testify/assert"
+)
+
+func TestNewUsersStage(t *testing.T) {
+	expectedStage := &Stage{
+		Name:    "org.osbuild.users",
+		Options: &UsersStageOptions{},
+	}
+	actualStage := NewUsersStage(&UsersStageOptions{})
+	assert.Equal(t, expectedStage, actualStage)
+}
diff --git a/test/cases/f30-x86_64-ami-boot.json b/test/cases/f30-x86_64-ami-boot.json
index d022bb7c2..ba125b2b6 100644
--- a/test/cases/f30-x86_64-ami-boot.json
+++ b/test/cases/f30-x86_64-ami-boot.json
@@ -182,6 +182,7 @@
           "sha256:7efe8696caf1e8b1471225cf3be33c95ace91b2b58443efa6d16fb744754874c": "http://dl.fedoraproject.org/pub/fedora/linux/releases/30/Everything/x86_64/os/Packages/c/chrony-3.4-2.fc30.x86_64.rpm",
           "sha256:806617532d01219c819194085466aab3a6bc4a0b16da7d5851370576861116b0": "http://dl.fedoraproject.org/pub/fedora/linux/releases/30/Everything/x86_64/os/Packages/g/grep-3.1-9.fc30.x86_64.rpm",
           "sha256:81560ff4cd2a766c691fc733a98b0ba6c99d1ea48fc3a1414d49c6c82826b984": "http://dl.fedoraproject.org/pub/fedora/linux/releases/30/Everything/x86_64/os/Packages/c/checkpolicy-2.9-1.fc30.x86_64.rpm",
+          "sha256:8185411da358027837a036579f2336e627d7e65cb8e15a3fe79f7e9f1a2286ba": "http://dl.fedoraproject.org/pub/fedora/linux/releases/30/Everything/x86_64/os/Packages/d/dracut-config-generic-049-26.git20181204.fc30.x86_64.rpm",
           "sha256:82b7ab937022da773f88246233f2e806460a534e74cb3a30c6554e7e568f962f": "http://dl.fedoraproject.org/pub/fedora/linux/releases/30/Everything/x86_64/os/Packages/l/libedit-3.1-26.20181209cvs.fc30.x86_64.rpm",
           "sha256:83106462e3330c682a5f3c8ddee487131666f6692fa6c7e071be61c831ee3766": "http://dl.fedoraproject.org/pub/fedora/linux/releases/30/Everything/x86_64/os/Packages/f/file-5.36-2.fc30.x86_64.rpm",
           "sha256:835813f87c48afef832488374fa1517a175626a5b8a36836f0abd73fe298b581": "http://dl.fedoraproject.org/pub/fedora/linux/releases/30/Everything/x86_64/os/Packages/l/libsemanage-2.9-1.fc30.x86_64.rpm",
@@ -656,6 +657,7 @@
               "sha256:a8e1a237699150c2eb2196079e7af1690c89297ab99b0696389cb4d2058e6ee2",
               "sha256:44d6eac48fdf84e2577d7ba97cf50409051d8882f86f9f001140a210b0ea6c1d",
               "sha256:76962ec5ea720cb22eb43f808013d8c974fb6862ebd6c193a76e49c987341856",
+              "sha256:8185411da358027837a036579f2336e627d7e65cb8e15a3fe79f7e9f1a2286ba",
               "sha256:109f8a9036587d3df953290ef6424780f81b11f4fc162f44d1f1bd20fa8f90a7",
               "sha256:4eefc58e32aca46c0b3d64da2454ffd52aa0beb03a7ce5e00c3cff0e49736639",
               "sha256:d342deb7dc135e1056185670b89989225fdf898101d26b70abbe6cfacfe507e9",
@@ -3348,6 +3350,15 @@
         "remote_location": "http://dl.fedoraproject.org/pub/fedora/linux/releases/30/Everything/x86_64/os/Packages/d/dracut-049-26.git20181204.fc30.x86_64.rpm",
         "checksum": "sha256:76962ec5ea720cb22eb43f808013d8c974fb6862ebd6c193a76e49c987341856"
       },
+      {
+        "name": "dracut-config-generic",
+        "epoch": 0,
+        "version": "049",
+        "release": "26.git20181204.fc30",
+        "arch": "x86_64",
+        "remote_location": "http://dl.fedoraproject.org/pub/fedora/linux/releases/30/Everything/x86_64/os/Packages/d/dracut-config-generic-049-26.git20181204.fc30.x86_64.rpm",
+        "checksum": "sha256:8185411da358027837a036579f2336e627d7e65cb8e15a3fe79f7e9f1a2286ba"
+      },
       {
         "name": "e2fsprogs",
         "epoch": 0,
@@ -6202,6 +6213,7 @@
       "dnf-plugins-core-4.0.6-1.fc30.noarch",
       "dnf-yum-4.2.2-2.fc30.noarch",
       "dracut-049-26.git20181204.fc30.x86_64",
+      "dracut-config-generic-049-26.git20181204.fc30.x86_64",
       "e2fsprogs-1.44.6-1.fc30.x86_64",
       "e2fsprogs-libs-1.44.6-1.fc30.x86_64",
       "ebtables-2.0.10-31.fc30.x86_64",
diff --git a/test/cases/f30-x86_64-openstack-boot.json b/test/cases/f30-x86_64-openstack-boot.json
index 0f3d4b781..1b187358d 100644
--- a/test/cases/f30-x86_64-openstack-boot.json
+++ b/test/cases/f30-x86_64-openstack-boot.json
@@ -198,6 +198,7 @@
           "sha256:7efe8696caf1e8b1471225cf3be33c95ace91b2b58443efa6d16fb744754874c": "http://dl.fedoraproject.org/pub/fedora/linux/releases/30/Everything/x86_64/os/Packages/c/chrony-3.4-2.fc30.x86_64.rpm",
           "sha256:806617532d01219c819194085466aab3a6bc4a0b16da7d5851370576861116b0": "http://dl.fedoraproject.org/pub/fedora/linux/releases/30/Everything/x86_64/os/Packages/g/grep-3.1-9.fc30.x86_64.rpm",
           "sha256:81560ff4cd2a766c691fc733a98b0ba6c99d1ea48fc3a1414d49c6c82826b984": "http://dl.fedoraproject.org/pub/fedora/linux/releases/30/Everything/x86_64/os/Packages/c/checkpolicy-2.9-1.fc30.x86_64.rpm",
+          "sha256:8185411da358027837a036579f2336e627d7e65cb8e15a3fe79f7e9f1a2286ba": "http://dl.fedoraproject.org/pub/fedora/linux/releases/30/Everything/x86_64/os/Packages/d/dracut-config-generic-049-26.git20181204.fc30.x86_64.rpm",
           "sha256:82b7ab937022da773f88246233f2e806460a534e74cb3a30c6554e7e568f962f": "http://dl.fedoraproject.org/pub/fedora/linux/releases/30/Everything/x86_64/os/Packages/l/libedit-3.1-26.20181209cvs.fc30.x86_64.rpm",
           "sha256:83106462e3330c682a5f3c8ddee487131666f6692fa6c7e071be61c831ee3766": "http://dl.fedoraproject.org/pub/fedora/linux/releases/30/Everything/x86_64/os/Packages/f/file-5.36-2.fc30.x86_64.rpm",
           "sha256:835813f87c48afef832488374fa1517a175626a5b8a36836f0abd73fe298b581": "http://dl.fedoraproject.org/pub/fedora/linux/releases/30/Everything/x86_64/os/Packages/l/libsemanage-2.9-1.fc30.x86_64.rpm",
@@ -685,6 +686,7 @@
               "sha256:a8e1a237699150c2eb2196079e7af1690c89297ab99b0696389cb4d2058e6ee2",
               "sha256:44d6eac48fdf84e2577d7ba97cf50409051d8882f86f9f001140a210b0ea6c1d",
               "sha256:76962ec5ea720cb22eb43f808013d8c974fb6862ebd6c193a76e49c987341856",
+              "sha256:8185411da358027837a036579f2336e627d7e65cb8e15a3fe79f7e9f1a2286ba",
               "sha256:109f8a9036587d3df953290ef6424780f81b11f4fc162f44d1f1bd20fa8f90a7",
               "sha256:4eefc58e32aca46c0b3d64da2454ffd52aa0beb03a7ce5e00c3cff0e49736639",
               "sha256:d342deb7dc135e1056185670b89989225fdf898101d26b70abbe6cfacfe507e9",
@@ -3404,6 +3406,15 @@
         "remote_location": "http://dl.fedoraproject.org/pub/fedora/linux/releases/30/Everything/x86_64/os/Packages/d/dracut-049-26.git20181204.fc30.x86_64.rpm",
         "checksum": "sha256:76962ec5ea720cb22eb43f808013d8c974fb6862ebd6c193a76e49c987341856"
       },
+      {
+        "name": "dracut-config-generic",
+        "epoch": 0,
+        "version": "049",
+        "release": "26.git20181204.fc30",
+        "arch": "x86_64",
+        "remote_location": "http://dl.fedoraproject.org/pub/fedora/linux/releases/30/Everything/x86_64/os/Packages/d/dracut-config-generic-049-26.git20181204.fc30.x86_64.rpm",
+        "checksum": "sha256:8185411da358027837a036579f2336e627d7e65cb8e15a3fe79f7e9f1a2286ba"
+      },
       {
         "name": "e2fsprogs",
         "epoch": 0,
@@ -6404,6 +6415,7 @@
       "dnf-plugins-core-4.0.6-1.fc30.noarch",
       "dnf-yum-4.2.2-2.fc30.noarch",
       "dracut-049-26.git20181204.fc30.x86_64",
+      "dracut-config-generic-049-26.git20181204.fc30.x86_64",
       "e2fsprogs-1.44.6-1.fc30.x86_64",
       "e2fsprogs-libs-1.44.6-1.fc30.x86_64",
       "ebtables-2.0.10-31.fc30.x86_64",
diff --git a/test/cases/f30-x86_64-partitioned_disk-boot.json b/test/cases/f30-x86_64-partitioned_disk-boot.json
index 75d97b1f2..675cc602b 100644
--- a/test/cases/f30-x86_64-partitioned_disk-boot.json
+++ b/test/cases/f30-x86_64-partitioned_disk-boot.json
@@ -182,6 +182,7 @@
           "sha256:7e61fb39689669e2c96909008f7970ea5037046fd4133c9bb38364ce82813966": "http://dl.fedoraproject.org/pub/fedora/linux/releases/30/Everything/x86_64/os/Packages/c/cyrus-sasl-lib-2.1.27-0.6rc7.fc30.x86_64.rpm",
           "sha256:7efe8696caf1e8b1471225cf3be33c95ace91b2b58443efa6d16fb744754874c": "http://dl.fedoraproject.org/pub/fedora/linux/releases/30/Everything/x86_64/os/Packages/c/chrony-3.4-2.fc30.x86_64.rpm",
           "sha256:806617532d01219c819194085466aab3a6bc4a0b16da7d5851370576861116b0": "http://dl.fedoraproject.org/pub/fedora/linux/releases/30/Everything/x86_64/os/Packages/g/grep-3.1-9.fc30.x86_64.rpm",
+          "sha256:8185411da358027837a036579f2336e627d7e65cb8e15a3fe79f7e9f1a2286ba": "http://dl.fedoraproject.org/pub/fedora/linux/releases/30/Everything/x86_64/os/Packages/d/dracut-config-generic-049-26.git20181204.fc30.x86_64.rpm",
           "sha256:82b7ab937022da773f88246233f2e806460a534e74cb3a30c6554e7e568f962f": "http://dl.fedoraproject.org/pub/fedora/linux/releases/30/Everything/x86_64/os/Packages/l/libedit-3.1-26.20181209cvs.fc30.x86_64.rpm",
           "sha256:83106462e3330c682a5f3c8ddee487131666f6692fa6c7e071be61c831ee3766": "http://dl.fedoraproject.org/pub/fedora/linux/releases/30/Everything/x86_64/os/Packages/f/file-5.36-2.fc30.x86_64.rpm",
           "sha256:835813f87c48afef832488374fa1517a175626a5b8a36836f0abd73fe298b581": "http://dl.fedoraproject.org/pub/fedora/linux/releases/30/Everything/x86_64/os/Packages/l/libsemanage-2.9-1.fc30.x86_64.rpm",
@@ -633,6 +634,7 @@
               "sha256:a8e1a237699150c2eb2196079e7af1690c89297ab99b0696389cb4d2058e6ee2",
               "sha256:44d6eac48fdf84e2577d7ba97cf50409051d8882f86f9f001140a210b0ea6c1d",
               "sha256:76962ec5ea720cb22eb43f808013d8c974fb6862ebd6c193a76e49c987341856",
+              "sha256:8185411da358027837a036579f2336e627d7e65cb8e15a3fe79f7e9f1a2286ba",
               "sha256:109f8a9036587d3df953290ef6424780f81b11f4fc162f44d1f1bd20fa8f90a7",
               "sha256:4eefc58e32aca46c0b3d64da2454ffd52aa0beb03a7ce5e00c3cff0e49736639",
               "sha256:d342deb7dc135e1056185670b89989225fdf898101d26b70abbe6cfacfe507e9",
@@ -3276,6 +3278,15 @@
         "remote_location": "http://dl.fedoraproject.org/pub/fedora/linux/releases/30/Everything/x86_64/os/Packages/d/dracut-049-26.git20181204.fc30.x86_64.rpm",
         "checksum": "sha256:76962ec5ea720cb22eb43f808013d8c974fb6862ebd6c193a76e49c987341856"
       },
+      {
+        "name": "dracut-config-generic",
+        "epoch": 0,
+        "version": "049",
+        "release": "26.git20181204.fc30",
+        "arch": "x86_64",
+        "remote_location": "http://dl.fedoraproject.org/pub/fedora/linux/releases/30/Everything/x86_64/os/Packages/d/dracut-config-generic-049-26.git20181204.fc30.x86_64.rpm",
+        "checksum": "sha256:8185411da358027837a036579f2336e627d7e65cb8e15a3fe79f7e9f1a2286ba"
+      },
       {
         "name": "e2fsprogs",
         "epoch": 0,
@@ -5832,6 +5843,7 @@
       "dnf-plugins-core-4.0.6-1.fc30.noarch",
       "dnf-yum-4.2.2-2.fc30.noarch",
       "dracut-049-26.git20181204.fc30.x86_64",
+      "dracut-config-generic-049-26.git20181204.fc30.x86_64",
       "e2fsprogs-1.44.6-1.fc30.x86_64",
       "e2fsprogs-libs-1.44.6-1.fc30.x86_64",
       "ebtables-2.0.10-31.fc30.x86_64",
diff --git a/test/cases/f30-x86_64-vmdk-boot.json b/test/cases/f30-x86_64-vmdk-boot.json
index 20d9f99ea..b4552177c 100644
--- a/test/cases/f30-x86_64-vmdk-boot.json
+++ b/test/cases/f30-x86_64-vmdk-boot.json
@@ -190,6 +190,7 @@
           "sha256:7e61fb39689669e2c96909008f7970ea5037046fd4133c9bb38364ce82813966": "http://dl.fedoraproject.org/pub/fedora/linux/releases/30/Everything/x86_64/os/Packages/c/cyrus-sasl-lib-2.1.27-0.6rc7.fc30.x86_64.rpm",
           "sha256:7efe8696caf1e8b1471225cf3be33c95ace91b2b58443efa6d16fb744754874c": "http://dl.fedoraproject.org/pub/fedora/linux/releases/30/Everything/x86_64/os/Packages/c/chrony-3.4-2.fc30.x86_64.rpm",
           "sha256:806617532d01219c819194085466aab3a6bc4a0b16da7d5851370576861116b0": "http://dl.fedoraproject.org/pub/fedora/linux/releases/30/Everything/x86_64/os/Packages/g/grep-3.1-9.fc30.x86_64.rpm",
+          "sha256:8185411da358027837a036579f2336e627d7e65cb8e15a3fe79f7e9f1a2286ba": "http://dl.fedoraproject.org/pub/fedora/linux/releases/30/Everything/x86_64/os/Packages/d/dracut-config-generic-049-26.git20181204.fc30.x86_64.rpm",
           "sha256:82b7ab937022da773f88246233f2e806460a534e74cb3a30c6554e7e568f962f": "http://dl.fedoraproject.org/pub/fedora/linux/releases/30/Everything/x86_64/os/Packages/l/libedit-3.1-26.20181209cvs.fc30.x86_64.rpm",
           "sha256:83106462e3330c682a5f3c8ddee487131666f6692fa6c7e071be61c831ee3766": "http://dl.fedoraproject.org/pub/fedora/linux/releases/30/Everything/x86_64/os/Packages/f/file-5.36-2.fc30.x86_64.rpm",
           "sha256:835813f87c48afef832488374fa1517a175626a5b8a36836f0abd73fe298b581": "http://dl.fedoraproject.org/pub/fedora/linux/releases/30/Everything/x86_64/os/Packages/l/libsemanage-2.9-1.fc30.x86_64.rpm",
@@ -648,6 +649,7 @@
               "sha256:a8e1a237699150c2eb2196079e7af1690c89297ab99b0696389cb4d2058e6ee2",
               "sha256:44d6eac48fdf84e2577d7ba97cf50409051d8882f86f9f001140a210b0ea6c1d",
               "sha256:76962ec5ea720cb22eb43f808013d8c974fb6862ebd6c193a76e49c987341856",
+              "sha256:8185411da358027837a036579f2336e627d7e65cb8e15a3fe79f7e9f1a2286ba",
               "sha256:109f8a9036587d3df953290ef6424780f81b11f4fc162f44d1f1bd20fa8f90a7",
               "sha256:4eefc58e32aca46c0b3d64da2454ffd52aa0beb03a7ce5e00c3cff0e49736639",
               "sha256:d342deb7dc135e1056185670b89989225fdf898101d26b70abbe6cfacfe507e9",
@@ -3307,6 +3309,15 @@
         "remote_location": "http://dl.fedoraproject.org/pub/fedora/linux/releases/30/Everything/x86_64/os/Packages/d/dracut-049-26.git20181204.fc30.x86_64.rpm",
         "checksum": "sha256:76962ec5ea720cb22eb43f808013d8c974fb6862ebd6c193a76e49c987341856"
       },
+      {
+        "name": "dracut-config-generic",
+        "epoch": 0,
+        "version": "049",
+        "release": "26.git20181204.fc30",
+        "arch": "x86_64",
+        "remote_location": "http://dl.fedoraproject.org/pub/fedora/linux/releases/30/Everything/x86_64/os/Packages/d/dracut-config-generic-049-26.git20181204.fc30.x86_64.rpm",
+        "checksum": "sha256:8185411da358027837a036579f2336e627d7e65cb8e15a3fe79f7e9f1a2286ba"
+      },
       {
         "name": "e2fsprogs",
         "epoch": 0,
@@ -6007,6 +6018,7 @@
       "dnf-plugins-core-4.0.6-1.fc30.noarch",
       "dnf-yum-4.2.2-2.fc30.noarch",
       "dracut-049-26.git20181204.fc30.x86_64",
+      "dracut-config-generic-049-26.git20181204.fc30.x86_64",
       "e2fsprogs-1.44.6-1.fc30.x86_64",
       "e2fsprogs-libs-1.44.6-1.fc30.x86_64",
       "ebtables-2.0.10-31.fc30.x86_64",
diff --git a/test/cases/f31-x86_64-ami-boot.json b/test/cases/f31-x86_64-ami-boot.json
index 59bbb9961..18e0551b9 100644
--- a/test/cases/f31-x86_64-ami-boot.json
+++ b/test/cases/f31-x86_64-ami-boot.json
@@ -105,6 +105,7 @@
           "sha256:33f37ee132feff578bdf50f89f6f6a18c3c7fcc699b5ea7922317087fd210c18": "http://dl.fedoraproject.org/pub/fedora/linux/releases/31/Everything/x86_64/os/Packages/l/libarchive-3.4.0-1.fc31.x86_64.rpm",
           "sha256:3434fe7dfffa29d996d94d2664dd2597ce446abf6b0d75920cc691540e139fcc": "http://dl.fedoraproject.org/pub/fedora/linux/releases/31/Everything/x86_64/os/Packages/l/libXft-2.3.3-2.fc31.x86_64.rpm",
           "sha256:34a182fca42c4cac66aa6186603509b90659076d62147ac735def1adb72883dd": "http://dl.fedoraproject.org/pub/fedora/linux/releases/31/Everything/x86_64/os/Packages/g/gcr-3.33.4-1.fc31.x86_64.rpm",
+          "sha256:34a9986b8b61812ceaf32ce3b189bd0b2cb4adaaf47d76ec1f50ce07c45b5675": "http://dl.fedoraproject.org/pub/fedora/linux/releases/31/Everything/x86_64/os/Packages/d/dracut-config-generic-049-27.git20181204.fc31.1.x86_64.rpm",
           "sha256:34f7954cf6c6ceb4385fdcc587dced94405913ddfe5e3213fcbd72562f286fbc": "http://dl.fedoraproject.org/pub/fedora/linux/releases/31/Everything/x86_64/os/Packages/d/dejavu-fonts-common-2.37-2.fc31.noarch.rpm",
           "sha256:35413dd963ebd9e61467067c18a53c7027dcd95ebcae5a5ffaf4727507e37c8c": "http://dl.fedoraproject.org/pub/fedora/linux/releases/31/Everything/x86_64/os/Packages/p/python-unversioned-command-3.7.4-5.fc31.noarch.rpm",
           "sha256:359d74d5f3988e1c2c5327f9d4ea59d0c49a2383541928084ef00383d70b6d55": "http://dl.fedoraproject.org/pub/fedora/linux/releases/31/Everything/x86_64/os/Packages/p/publicsuffix-list-dafsa-20190417-2.fc31.noarch.rpm",
@@ -790,6 +791,7 @@
               "sha256:02301d2744b96674019251b6c8d2199790960e4cc79d90fbdb946a298fc2c4ea",
               "sha256:16ea1e6ba5bbf16cb6a052b2326d25b9980971fd72c46e7d701e09f267d33063",
               "sha256:ef55145ef56d4d63c0085d04e856943d5c61c11ba10c70a383d8f67b74818159",
+              "sha256:34a9986b8b61812ceaf32ce3b189bd0b2cb4adaaf47d76ec1f50ce07c45b5675",
               "sha256:71c02de0e50e07999d0f4f40bce06ca4904e0ab786220bd7ffebc4a60a4d3cd7",
               "sha256:de678f5a55f5ff690f3587adcbc7a1b7d477fefe85ffd5d91fc1447ddba63c89",
               "sha256:cab1b0c3bdae2a07e15b90b414f50753c759e325b6f0cddfa27895748c77f082",
@@ -4224,6 +4226,15 @@
         "remote_location": "http://dl.fedoraproject.org/pub/fedora/linux/releases/31/Everything/x86_64/os/Packages/d/dracut-049-27.git20181204.fc31.1.x86_64.rpm",
         "checksum": "sha256:ef55145ef56d4d63c0085d04e856943d5c61c11ba10c70a383d8f67b74818159"
       },
+      {
+        "name": "dracut-config-generic",
+        "epoch": 0,
+        "version": "049",
+        "release": "27.git20181204.fc31.1",
+        "arch": "x86_64",
+        "remote_location": "http://dl.fedoraproject.org/pub/fedora/linux/releases/31/Everything/x86_64/os/Packages/d/dracut-config-generic-049-27.git20181204.fc31.1.x86_64.rpm",
+        "checksum": "sha256:34a9986b8b61812ceaf32ce3b189bd0b2cb4adaaf47d76ec1f50ce07c45b5675"
+      },
       {
         "name": "e2fsprogs",
         "epoch": 0,
@@ -7530,6 +7541,7 @@
       "dnf-data-4.2.9-5.fc31.noarch",
       "dnf-plugins-core-4.0.9-1.fc31.noarch",
       "dracut-049-27.git20181204.fc31.1.x86_64",
+      "dracut-config-generic-049-27.git20181204.fc31.1.x86_64",
       "e2fsprogs-1.45.3-1.fc31.x86_64",
       "e2fsprogs-libs-1.45.3-1.fc31.x86_64",
       "ebtables-legacy-2.0.10-37.fc31.x86_64",
diff --git a/test/cases/f31-x86_64-openstack-boot.json b/test/cases/f31-x86_64-openstack-boot.json
index 459c9d97e..51bd94ea3 100644
--- a/test/cases/f31-x86_64-openstack-boot.json
+++ b/test/cases/f31-x86_64-openstack-boot.json
@@ -119,6 +119,7 @@
           "sha256:33f37ee132feff578bdf50f89f6f6a18c3c7fcc699b5ea7922317087fd210c18": "http://dl.fedoraproject.org/pub/fedora/linux/releases/31/Everything/x86_64/os/Packages/l/libarchive-3.4.0-1.fc31.x86_64.rpm",
           "sha256:3434fe7dfffa29d996d94d2664dd2597ce446abf6b0d75920cc691540e139fcc": "http://dl.fedoraproject.org/pub/fedora/linux/releases/31/Everything/x86_64/os/Packages/l/libXft-2.3.3-2.fc31.x86_64.rpm",
           "sha256:34a182fca42c4cac66aa6186603509b90659076d62147ac735def1adb72883dd": "http://dl.fedoraproject.org/pub/fedora/linux/releases/31/Everything/x86_64/os/Packages/g/gcr-3.33.4-1.fc31.x86_64.rpm",
+          "sha256:34a9986b8b61812ceaf32ce3b189bd0b2cb4adaaf47d76ec1f50ce07c45b5675": "http://dl.fedoraproject.org/pub/fedora/linux/releases/31/Everything/x86_64/os/Packages/d/dracut-config-generic-049-27.git20181204.fc31.1.x86_64.rpm",
           "sha256:34f7954cf6c6ceb4385fdcc587dced94405913ddfe5e3213fcbd72562f286fbc": "http://dl.fedoraproject.org/pub/fedora/linux/releases/31/Everything/x86_64/os/Packages/d/dejavu-fonts-common-2.37-2.fc31.noarch.rpm",
           "sha256:35413dd963ebd9e61467067c18a53c7027dcd95ebcae5a5ffaf4727507e37c8c": "http://dl.fedoraproject.org/pub/fedora/linux/releases/31/Everything/x86_64/os/Packages/p/python-unversioned-command-3.7.4-5.fc31.noarch.rpm",
           "sha256:3597ed358e421f7dc161d2f263e7e36c61aa1b4c9e547ed94ab5db2bd35f42de": "http://dl.fedoraproject.org/pub/fedora/linux/releases/31/Everything/x86_64/os/Packages/y/yajl-2.1.0-13.fc31.x86_64.rpm",
@@ -814,6 +815,7 @@
               "sha256:02301d2744b96674019251b6c8d2199790960e4cc79d90fbdb946a298fc2c4ea",
               "sha256:16ea1e6ba5bbf16cb6a052b2326d25b9980971fd72c46e7d701e09f267d33063",
               "sha256:ef55145ef56d4d63c0085d04e856943d5c61c11ba10c70a383d8f67b74818159",
+              "sha256:34a9986b8b61812ceaf32ce3b189bd0b2cb4adaaf47d76ec1f50ce07c45b5675",
               "sha256:71c02de0e50e07999d0f4f40bce06ca4904e0ab786220bd7ffebc4a60a4d3cd7",
               "sha256:de678f5a55f5ff690f3587adcbc7a1b7d477fefe85ffd5d91fc1447ddba63c89",
               "sha256:cab1b0c3bdae2a07e15b90b414f50753c759e325b6f0cddfa27895748c77f082",
@@ -4267,6 +4269,15 @@
         "remote_location": "http://dl.fedoraproject.org/pub/fedora/linux/releases/31/Everything/x86_64/os/Packages/d/dracut-049-27.git20181204.fc31.1.x86_64.rpm",
         "checksum": "sha256:ef55145ef56d4d63c0085d04e856943d5c61c11ba10c70a383d8f67b74818159"
       },
+      {
+        "name": "dracut-config-generic",
+        "epoch": 0,
+        "version": "049",
+        "release": "27.git20181204.fc31.1",
+        "arch": "x86_64",
+        "remote_location": "http://dl.fedoraproject.org/pub/fedora/linux/releases/31/Everything/x86_64/os/Packages/d/dracut-config-generic-049-27.git20181204.fc31.1.x86_64.rpm",
+        "checksum": "sha256:34a9986b8b61812ceaf32ce3b189bd0b2cb4adaaf47d76ec1f50ce07c45b5675"
+      },
       {
         "name": "e2fsprogs",
         "epoch": 0,
@@ -7647,6 +7658,7 @@
       "dnf-data-4.2.9-5.fc31.noarch",
       "dnf-plugins-core-4.0.9-1.fc31.noarch",
       "dracut-049-27.git20181204.fc31.1.x86_64",
+      "dracut-config-generic-049-27.git20181204.fc31.1.x86_64",
       "e2fsprogs-1.45.3-1.fc31.x86_64",
       "e2fsprogs-libs-1.45.3-1.fc31.x86_64",
       "ebtables-legacy-2.0.10-37.fc31.x86_64",
diff --git a/test/cases/f31-x86_64-partitioned_disk-boot.json b/test/cases/f31-x86_64-partitioned_disk-boot.json
index 6b992c8b0..7dcdcfa70 100644
--- a/test/cases/f31-x86_64-partitioned_disk-boot.json
+++ b/test/cases/f31-x86_64-partitioned_disk-boot.json
@@ -111,6 +111,7 @@
           "sha256:33f37ee132feff578bdf50f89f6f6a18c3c7fcc699b5ea7922317087fd210c18": "http://dl.fedoraproject.org/pub/fedora/linux/releases/31/Everything/x86_64/os/Packages/l/libarchive-3.4.0-1.fc31.x86_64.rpm",
           "sha256:3434fe7dfffa29d996d94d2664dd2597ce446abf6b0d75920cc691540e139fcc": "http://dl.fedoraproject.org/pub/fedora/linux/releases/31/Everything/x86_64/os/Packages/l/libXft-2.3.3-2.fc31.x86_64.rpm",
           "sha256:34a182fca42c4cac66aa6186603509b90659076d62147ac735def1adb72883dd": "http://dl.fedoraproject.org/pub/fedora/linux/releases/31/Everything/x86_64/os/Packages/g/gcr-3.33.4-1.fc31.x86_64.rpm",
+          "sha256:34a9986b8b61812ceaf32ce3b189bd0b2cb4adaaf47d76ec1f50ce07c45b5675": "http://dl.fedoraproject.org/pub/fedora/linux/releases/31/Everything/x86_64/os/Packages/d/dracut-config-generic-049-27.git20181204.fc31.1.x86_64.rpm",
           "sha256:34f7954cf6c6ceb4385fdcc587dced94405913ddfe5e3213fcbd72562f286fbc": "http://dl.fedoraproject.org/pub/fedora/linux/releases/31/Everything/x86_64/os/Packages/d/dejavu-fonts-common-2.37-2.fc31.noarch.rpm",
           "sha256:35413dd963ebd9e61467067c18a53c7027dcd95ebcae5a5ffaf4727507e37c8c": "http://dl.fedoraproject.org/pub/fedora/linux/releases/31/Everything/x86_64/os/Packages/p/python-unversioned-command-3.7.4-5.fc31.noarch.rpm",
           "sha256:359d74d5f3988e1c2c5327f9d4ea59d0c49a2383541928084ef00383d70b6d55": "http://dl.fedoraproject.org/pub/fedora/linux/releases/31/Everything/x86_64/os/Packages/p/publicsuffix-list-dafsa-20190417-2.fc31.noarch.rpm",
@@ -769,6 +770,7 @@
               "sha256:02301d2744b96674019251b6c8d2199790960e4cc79d90fbdb946a298fc2c4ea",
               "sha256:16ea1e6ba5bbf16cb6a052b2326d25b9980971fd72c46e7d701e09f267d33063",
               "sha256:ef55145ef56d4d63c0085d04e856943d5c61c11ba10c70a383d8f67b74818159",
+              "sha256:34a9986b8b61812ceaf32ce3b189bd0b2cb4adaaf47d76ec1f50ce07c45b5675",
               "sha256:71c02de0e50e07999d0f4f40bce06ca4904e0ab786220bd7ffebc4a60a4d3cd7",
               "sha256:de678f5a55f5ff690f3587adcbc7a1b7d477fefe85ffd5d91fc1447ddba63c89",
               "sha256:cab1b0c3bdae2a07e15b90b414f50753c759e325b6f0cddfa27895748c77f082",
@@ -4156,6 +4158,15 @@
         "remote_location": "http://dl.fedoraproject.org/pub/fedora/linux/releases/31/Everything/x86_64/os/Packages/d/dracut-049-27.git20181204.fc31.1.x86_64.rpm",
         "checksum": "sha256:ef55145ef56d4d63c0085d04e856943d5c61c11ba10c70a383d8f67b74818159"
       },
+      {
+        "name": "dracut-config-generic",
+        "epoch": 0,
+        "version": "049",
+        "release": "27.git20181204.fc31.1",
+        "arch": "x86_64",
+        "remote_location": "http://dl.fedoraproject.org/pub/fedora/linux/releases/31/Everything/x86_64/os/Packages/d/dracut-config-generic-049-27.git20181204.fc31.1.x86_64.rpm",
+        "checksum": "sha256:34a9986b8b61812ceaf32ce3b189bd0b2cb4adaaf47d76ec1f50ce07c45b5675"
+      },
       {
         "name": "e2fsprogs",
         "epoch": 0,
@@ -7182,6 +7193,7 @@
       "dnf-data-4.2.9-5.fc31.noarch",
       "dnf-plugins-core-4.0.9-1.fc31.noarch",
       "dracut-049-27.git20181204.fc31.1.x86_64",
+      "dracut-config-generic-049-27.git20181204.fc31.1.x86_64",
       "e2fsprogs-1.45.3-1.fc31.x86_64",
       "e2fsprogs-libs-1.45.3-1.fc31.x86_64",
       "ebtables-legacy-2.0.10-37.fc31.x86_64",
diff --git a/test/cases/f31-x86_64-vmdk-boot.json b/test/cases/f31-x86_64-vmdk-boot.json
index 246eb0175..f1330711f 100644
--- a/test/cases/f31-x86_64-vmdk-boot.json
+++ b/test/cases/f31-x86_64-vmdk-boot.json
@@ -115,6 +115,7 @@
           "sha256:33f37ee132feff578bdf50f89f6f6a18c3c7fcc699b5ea7922317087fd210c18": "http://dl.fedoraproject.org/pub/fedora/linux/releases/31/Everything/x86_64/os/Packages/l/libarchive-3.4.0-1.fc31.x86_64.rpm",
           "sha256:3434fe7dfffa29d996d94d2664dd2597ce446abf6b0d75920cc691540e139fcc": "http://dl.fedoraproject.org/pub/fedora/linux/releases/31/Everything/x86_64/os/Packages/l/libXft-2.3.3-2.fc31.x86_64.rpm",
           "sha256:34a182fca42c4cac66aa6186603509b90659076d62147ac735def1adb72883dd": "http://dl.fedoraproject.org/pub/fedora/linux/releases/31/Everything/x86_64/os/Packages/g/gcr-3.33.4-1.fc31.x86_64.rpm",
+          "sha256:34a9986b8b61812ceaf32ce3b189bd0b2cb4adaaf47d76ec1f50ce07c45b5675": "http://dl.fedoraproject.org/pub/fedora/linux/releases/31/Everything/x86_64/os/Packages/d/dracut-config-generic-049-27.git20181204.fc31.1.x86_64.rpm",
           "sha256:34f7954cf6c6ceb4385fdcc587dced94405913ddfe5e3213fcbd72562f286fbc": "http://dl.fedoraproject.org/pub/fedora/linux/releases/31/Everything/x86_64/os/Packages/d/dejavu-fonts-common-2.37-2.fc31.noarch.rpm",
           "sha256:35413dd963ebd9e61467067c18a53c7027dcd95ebcae5a5ffaf4727507e37c8c": "http://dl.fedoraproject.org/pub/fedora/linux/releases/31/Everything/x86_64/os/Packages/p/python-unversioned-command-3.7.4-5.fc31.noarch.rpm",
           "sha256:359d74d5f3988e1c2c5327f9d4ea59d0c49a2383541928084ef00383d70b6d55": "http://dl.fedoraproject.org/pub/fedora/linux/releases/31/Everything/x86_64/os/Packages/p/publicsuffix-list-dafsa-20190417-2.fc31.noarch.rpm",
@@ -784,6 +785,7 @@
               "sha256:02301d2744b96674019251b6c8d2199790960e4cc79d90fbdb946a298fc2c4ea",
               "sha256:16ea1e6ba5bbf16cb6a052b2326d25b9980971fd72c46e7d701e09f267d33063",
               "sha256:ef55145ef56d4d63c0085d04e856943d5c61c11ba10c70a383d8f67b74818159",
+              "sha256:34a9986b8b61812ceaf32ce3b189bd0b2cb4adaaf47d76ec1f50ce07c45b5675",
               "sha256:71c02de0e50e07999d0f4f40bce06ca4904e0ab786220bd7ffebc4a60a4d3cd7",
               "sha256:de678f5a55f5ff690f3587adcbc7a1b7d477fefe85ffd5d91fc1447ddba63c89",
               "sha256:cab1b0c3bdae2a07e15b90b414f50753c759e325b6f0cddfa27895748c77f082",
@@ -4187,6 +4189,15 @@
         "remote_location": "http://dl.fedoraproject.org/pub/fedora/linux/releases/31/Everything/x86_64/os/Packages/d/dracut-049-27.git20181204.fc31.1.x86_64.rpm",
         "checksum": "sha256:ef55145ef56d4d63c0085d04e856943d5c61c11ba10c70a383d8f67b74818159"
       },
+      {
+        "name": "dracut-config-generic",
+        "epoch": 0,
+        "version": "049",
+        "release": "27.git20181204.fc31.1",
+        "arch": "x86_64",
+        "remote_location": "http://dl.fedoraproject.org/pub/fedora/linux/releases/31/Everything/x86_64/os/Packages/d/dracut-config-generic-049-27.git20181204.fc31.1.x86_64.rpm",
+        "checksum": "sha256:34a9986b8b61812ceaf32ce3b189bd0b2cb4adaaf47d76ec1f50ce07c45b5675"
+      },
       {
         "name": "e2fsprogs",
         "epoch": 0,
@@ -7357,6 +7368,7 @@
       "dnf-data-4.2.9-5.fc31.noarch",
       "dnf-plugins-core-4.0.9-1.fc31.noarch",
       "dracut-049-27.git20181204.fc31.1.x86_64",
+      "dracut-config-generic-049-27.git20181204.fc31.1.x86_64",
       "e2fsprogs-1.45.3-1.fc31.x86_64",
       "e2fsprogs-libs-1.45.3-1.fc31.x86_64",
       "ebtables-legacy-2.0.10-37.fc31.x86_64",