Repository: osbuild/osbuild-composer
Pull request #1540 (2021-07-09 11:43:14)

ci: add slack notifications
This pull request includes:

Trying out how slack notifications about nightly pipeline execution could work.


- [ ] adequate testing for the new functionality or fixed issue
- [ ] adequate documentation informing people about the change such as
  - [ ] create a file in [news/unreleased](https://github.com/osbuild/osbuild-composer/tree/main/docs/news/unreleased) directory if this change should be mentioned in the release news
  - [ ] submit a PR for the [guides](https://github.com/osbuild/guides) repository if this PR changed any behavior described there: https://www.osbuild.org/guides/

<!--
Thanks for proposing a change to osbuild-composer!

Please don't remove the above check list. These are things that each pull
request must have before it is merged. It helps maintainers to not forget
anything.

If the reason for ticking any of the boxes is ambiguous, please add a short
note explaining why.

For user-visible changes, "adequate documentation" is an entry describing the
change for users in docs/news. Please refer to docs/news/README.md for details.

In addition, if this pull request fixes a downstream issue, please refer to
test/README.md and add these additional items:

- [ ] 1st commit of any `rhbz#` related PR contains bug reproducer; CI reports FAIL or
- [ ] PR contains automated tests for new functionality and
- [ ] QE has approved reproducer/new tests and
- [ ] Subsequent commits provide bug fixes without modifying the reproducer; CI reports PASS and
- [ ] QE approves this PR; RHBZ status is set to `MODIFIED + Verified=Tested`
-->


jrusz: I did some testing and it works as intended which is to either report failure or success of the scheduled nightly pipeline to our slack channel. 
Example run of failure: https://gitlab.com/osbuild/ci/osbuild-composer/-/pipelines/334477690
Example run of success: https://gitlab.com/osbuild/ci/osbuild-composer/-/pipelines/334479852
I tested it in #bot-testing channel, this is how it's going to look: 
![image](https://user-images.githubusercontent.com/55534579/125081400-3f97ee00-e0c6-11eb-87a7-3c72ee90f0fe.png)
![image](https://user-images.githubusercontent.com/55534579/125081451-4de60a00-e0c6-11eb-96b1-5af1e2609d5e.png)
atodorov: In the previous Telegram implementation we had the COMPOSE_ID integrated in the message. Can you either add that or the entire COMPOSE_URL to easily distinguish what the reported status is for.
atodorov: > #bot-testing channel

How/where do you control in which channel messages are sent ? 


jrusz: > In the previous Telegram implementation we had the COMPOSE_ID integrated in the message. Can you either add that or the entire COMPOSE_URL to easily distinguish what the reported status is for.

I'll try, I'm not sure from where to get it in that stage since I don't think it's in any of CI env vars, but maybe I could parse it from some artifacts. 

>How/where do you control in which channel messages are sent ? Ideally we'd like for this to be in source code so that anyone can change it if necessary.

The `WEBHOOK_URL` will be defined as a scheduled pipeline variable and this url is taken from the slack app settings. It seems  that I can add collaborators there, I'll try it and send you a link over chat.

Patch:
diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml
index 26d764a8d..d997fad56 100644
--- a/.gitlab-ci.yml
+++ b/.gitlab-ci.yml
@@ -236,6 +236,25 @@ API:
         INTERNAL_NETWORK: ["true"]
         DISTRO_CODE: ["rhel_90"]
 
+NIGHTLY_FAIL:
+  stage: finish
+  tags:
+    - shell
+  rules:
+    - if: '$CI_PIPELINE_SOURCE == "schedule"'
+      when: on_failure
+  script:
+    - schutzbot/slack_notification.sh FAILED ":big-sad:"
+
+NIGHTLY_SUCCESS:
+  stage: finish
+  tags:
+    - shell
+  rules:
+    - if: '$CI_PIPELINE_SOURCE == "schedule"'
+  script:
+    - schutzbot/slack_notification.sh SUCCESS ":partymeow:"
+
 # Installer:
 #   stage: test
 #   extends: .terraform
diff --git a/cmd/osbuild-composer/composer.go b/cmd/osbuild-composer/composer.go
index ec8e7e8b6..13523db08 100644
--- a/cmd/osbuild-composer/composer.go
+++ b/cmd/osbuild-composer/composer.go
@@ -13,13 +13,10 @@ import (
 	"path"
 
 	"github.com/osbuild/osbuild-composer/internal/cloudapi"
-	"github.com/osbuild/osbuild-composer/internal/common"
 	"github.com/osbuild/osbuild-composer/internal/distroregistry"
 	"github.com/osbuild/osbuild-composer/internal/jobqueue/fsjobqueue"
 	"github.com/osbuild/osbuild-composer/internal/kojiapi"
-	"github.com/osbuild/osbuild-composer/internal/reporegistry"
 	"github.com/osbuild/osbuild-composer/internal/rpmmd"
-	"github.com/osbuild/osbuild-composer/internal/store"
 	"github.com/osbuild/osbuild-composer/internal/weldr"
 	"github.com/osbuild/osbuild-composer/internal/worker"
 )
@@ -73,35 +70,11 @@ func NewComposer(config *ComposerConfigFile, stateDir, cacheDir string, logger *
 	return &c, nil
 }
 
-func (c *Composer) InitWeldr(repoPaths []string, weldrListener net.Listener) error {
-	archName := common.CurrentArch()
-
-	hostDistro := c.distros.FromHost()
-	if hostDistro == nil {
-		return fmt.Errorf("host distro is not supported")
-	}
-
-	arch, err := hostDistro.GetArch(archName)
-	if err != nil {
-		return fmt.Errorf("Host distro does not support host architecture: %v", err)
-	}
-
-	rr, err := reporegistry.New(repoPaths)
+func (c *Composer) InitWeldr(repoPaths []string, weldrListener net.Listener) (err error) {
+	c.weldr, err = weldr.New(repoPaths, c.stateDir, c.rpm, c.distros, c.logger, c.workers)
 	if err != nil {
-		return fmt.Errorf("error loading repository definitions: %v", err)
+		return err
 	}
-
-	// Check if repositories for the host distro and arch were loaded
-	_, err = rr.ReposByArch(arch, false)
-	if err != nil {
-		return fmt.Errorf("loaded repository definitions don't contain any for the host distro/arch: %v", err)
-	}
-
-	store := store.New(&c.stateDir, arch, c.logger)
-	compatOutputDir := path.Join(c.stateDir, "outputs")
-
-	c.weldr = weldr.New(c.rpm, arch, hostDistro, rr, c.logger, store, c.workers, compatOutputDir)
-
 	c.weldrListener = weldrListener
 
 	return nil
diff --git a/docs/news/unreleased/other-distro-build.md b/docs/news/unreleased/other-distro-build.md
new file mode 100644
index 000000000..7d968ad96
--- /dev/null
+++ b/docs/news/unreleased/other-distro-build.md
@@ -0,0 +1,145 @@
+# Building images for other distributions
+
+Previously osbuild-composer could only build images for the same distribution
+as the host. With the addition of the distro field in blueprint it is now
+possible to build for any supported distribution shipped with osbuild-composer.
+
+
+## New API route: /distros/list
+
+The API now supports listing the available distributions. It will return a JSON
+object listing the installed distro names that can be used by blueprints,
+sources, and the optional `?distro=` selection on API routes.
+
+eg. `curl --unix-socket /run/weldr/api.socket http://localhost/api/v1/distros/list`
+
+    {
+        "distros": [
+            "centos-8",
+            "fedora-32",
+            "fedora-33",
+            "rhel-8",
+            "rhel-84",
+            "rhel-85",
+            "rhel-90"
+        ]
+    }
+
+
+## Distribution selection with blueprints
+
+The blueprint now supports a new `distro` field that will be used to select the
+distribution to use when composing images, or depsolving the blueprint. If
+`distro` is left blank it will use the host distribution. If you upgrade the
+host operating system the blueprints with no `distro` set will build using the
+new os.
+
+eg. A blueprint that will always build a fedora-32 image, no matter what
+version is running on the host:
+
+    name = "tmux"
+    description = "tmux image with openssh"
+    version = "1.2.16"
+    distro = "fedora-32"
+
+    [[packages]]
+    name = "tmux"
+    version = "*"
+
+    [[packages]]
+    name = "openssh-server"
+    version = "*"
+
+
+## Using sources with specific distributions
+
+A new optional field has been added to the repository source format. It is a
+list of distribution strings that the source will be used with when depsolving
+and building images.
+
+Sources with no `distros` will be used with all composes. If you want to use a
+source for a specific distro you set the `distros` list to the distro name(s)
+to use it with.
+
+eg. A source that is only used when depsolving or building fedora 32:
+
+    check_gpg = true
+    check_ssl = true
+    distros = ["fedora-32"]
+    id = "f32-local"
+    name = "local packages for fedora32"
+    system = false
+    type = "yum-baseurl"
+    url = "http://local/repos/fedora32/projectrepo/"
+
+This source will be used for any requests that specify fedora-32, eg. listing
+packages and specifying fedora-32 will include this source, but listing
+packages for the host distro will not.
+
+
+## Optional distribution selection for routes
+
+Many of the API routes now support selecting the distribution to use when
+returning results.  Add `?distro=<DISTRO-NAME>` to the API request and it will
+return results using `fedora-32` instead of the host distro.
+
+The following routes support distro selection:
+
+* /compose/types
+* /modules/list
+* /modules/info
+* /projects/list
+* /projects/info
+* /projects/depsolve
+
+The compose start uses the distribution specified by the blueprint to select
+which one to use.
+
+eg. Show the image types supported by `centos-8`:
+
+    curl --unix-socket /run/weldr/api.socket http://localhost/api/v1/compose/types?distro=centos-8
+    {
+        "types": [
+            {
+                "name": "ami",
+                "enabled": true
+            },
+            {
+                "name": "openstack",
+                "enabled": true
+            },
+            {
+                "name": "qcow2",
+                "enabled": true
+            },
+            {
+                "name": "tar",
+                "enabled": true
+            },
+            {
+                "name": "vhd",
+                "enabled": true
+            },
+            {
+                "name": "vmdk",
+                "enabled": true
+            }
+        ]
+    }
+
+
+## Unknown Distributions
+
+If an unknown distribution is selected the response from the API server will be
+a `DistroError`, like this:
+
+    {
+        "status": false,
+        "errors": [
+            {
+                "id": "DistroError",
+                "msg": "Invalid distro: fedora-1"
+            }
+        ]
+    }
+
diff --git a/internal/blueprint/blueprint.go b/internal/blueprint/blueprint.go
index c30a16a46..6248bb961 100644
--- a/internal/blueprint/blueprint.go
+++ b/internal/blueprint/blueprint.go
@@ -17,6 +17,7 @@ type Blueprint struct {
 	Modules        []Package       `json:"modules" toml:"modules"`
 	Groups         []Group         `json:"groups" toml:"groups"`
 	Customizations *Customizations `json:"customizations,omitempty" toml:"customizations,omitempty"`
+	Distro         string          `json:"distro" toml:"distro"`
 }
 
 type Change struct {
diff --git a/internal/client/utils.go b/internal/client/utils.go
index 6b0eccc4f..ed0ad83ac 100644
--- a/internal/client/utils.go
+++ b/internal/client/utils.go
@@ -9,7 +9,6 @@ import (
 	"fmt"
 	"net"
 	"net/http"
-	"sort"
 	"strconv"
 )
 
@@ -21,17 +20,6 @@ type TestState struct {
 	imageTypeName string
 }
 
-// isStringInSlice returns true if the string is present, false if not
-// slice must be sorted
-// TODO decide if this belongs in a more widely useful package location
-func isStringInSlice(slice []string, s string) bool {
-	i := sort.SearchStrings(slice, s)
-	if i < len(slice) && slice[i] == s {
-		return true
-	}
-	return false
-}
-
 func setUpTestState(socketPath string, imageTypeName string, unitTest bool) (*TestState, error) {
 	state := TestState{imageTypeName: imageTypeName, unitTest: unitTest}
 
diff --git a/internal/common/helpers.go b/internal/common/helpers.go
index 1c05aa9f1..04e8a291a 100644
--- a/internal/common/helpers.go
+++ b/internal/common/helpers.go
@@ -1,6 +1,9 @@
 package common
 
-import "runtime"
+import (
+	"runtime"
+	"sort"
+)
 
 var RuntimeGOARCH = runtime.GOARCH
 
@@ -23,3 +26,13 @@ func PanicOnError(err error) {
 		panic(err)
 	}
 }
+
+// IsStringInSortedSlice returns true if the string is present, false if not
+// slice must be sorted
+func IsStringInSortedSlice(slice []string, s string) bool {
+	i := sort.SearchStrings(slice, s)
+	if i < len(slice) && slice[i] == s {
+		return true
+	}
+	return false
+}
diff --git a/internal/reporegistry/reporegistry.go b/internal/reporegistry/reporegistry.go
index 8cb494035..27a8f968e 100644
--- a/internal/reporegistry/reporegistry.go
+++ b/internal/reporegistry/reporegistry.go
@@ -52,7 +52,7 @@ func (r *RepoRegistry) ReposByImageType(imageType distro.ImageType) ([]rpmmd.Rep
 func (r *RepoRegistry) reposByImageTypeName(distro, arch, imageType string) ([]rpmmd.RepoConfig, error) {
 	repositories := []rpmmd.RepoConfig{}
 
-	archRepos, err := r.reposByArchName(distro, arch, true)
+	archRepos, err := r.ReposByArchName(distro, arch, true)
 	if err != nil {
 		return nil, err
 	}
@@ -84,7 +84,7 @@ func (r *RepoRegistry) ReposByArch(arch distro.Arch, includeTagged bool) ([]rpmm
 	if arch.Distro() == nil || reflect.ValueOf(arch.Distro()).IsNil() {
 		return nil, fmt.Errorf("there is no distribution associated with the provided architecture")
 	}
-	return r.reposByArchName(arch.Distro().Name(), arch.Name(), includeTagged)
+	return r.ReposByArchName(arch.Distro().Name(), arch.Name(), includeTagged)
 }
 
 // reposByArchName returns a slice of rpmmd.RepoConfig instances, which should be used for building image types for the
@@ -93,14 +93,10 @@ func (r *RepoRegistry) ReposByArch(arch distro.Arch, includeTagged bool) ([]rpmm
 // slice or not.
 //
 // The method does not verify if the given architecture name is actually part of the specific distribution definition.
-func (r *RepoRegistry) reposByArchName(distro, arch string, includeTagged bool) ([]rpmmd.RepoConfig, error) {
+func (r *RepoRegistry) ReposByArchName(distro, arch string, includeTagged bool) ([]rpmmd.RepoConfig, error) {
 	repositories := []rpmmd.RepoConfig{}
 
-	distroRepos, found := r.repos[distro]
-	if !found {
-		return nil, fmt.Errorf("there are no repositories for distribution '%s'", distro)
-	}
-	archRepos, found := distroRepos[arch]
+	archRepos, found := r.DistroHasRepos(distro, arch)
 	if !found {
 		return nil, fmt.Errorf("there are no repositories for distribution '%s' and architecture '%s'", distro, arch)
 	}
@@ -116,3 +112,14 @@ func (r *RepoRegistry) reposByArchName(distro, arch string, includeTagged bool)
 
 	return repositories, nil
 }
+
+// DistroHasRepos returns the repositories for the distro+arch, and a found flag
+func (r *RepoRegistry) DistroHasRepos(distro, arch string) (repos []rpmmd.RepoConfig, found bool) {
+	distroRepos, found := r.repos[distro]
+	if !found {
+		return repos, false
+	}
+	repos, found = distroRepos[arch]
+
+	return repos, found
+}
diff --git a/internal/store/fixtures.go b/internal/store/fixtures.go
index 33fec0a67..26039063c 100644
--- a/internal/store/fixtures.go
+++ b/internal/store/fixtures.go
@@ -291,5 +291,17 @@ func FixtureEmpty() *Store {
 
 	s.blueprints[bName] = b
 
+	// 2nd distro blueprint
+	b2 := b
+	b2.Name = "test-distro-2"
+	b2.Distro = "test-distro-2"
+	s.blueprints[b2.Name] = b2
+
+	// Unknown distro blueprint
+	b3 := b
+	b3.Name = "test-fedora-1"
+	b3.Distro = "fedora-1"
+	s.blueprints[b3.Name] = b3
+
 	return s
 }
diff --git a/internal/store/json.go b/internal/store/json.go
index 478473731..1e43cf275 100644
--- a/internal/store/json.go
+++ b/internal/store/json.go
@@ -56,12 +56,13 @@ type imageBuildV0 struct {
 }
 
 type sourceV0 struct {
-	Name     string `json:"name"`
-	Type     string `json:"type"`
-	URL      string `json:"url"`
-	CheckGPG bool   `json:"check_gpg"`
-	CheckSSL bool   `json:"check_ssl"`
-	System   bool   `json:"system"`
+	Name     string   `json:"name"`
+	Type     string   `json:"type"`
+	URL      string   `json:"url"`
+	CheckGPG bool     `json:"check_gpg"`
+	CheckSSL bool     `json:"check_ssl"`
+	System   bool     `json:"system"`
+	Distros  []string `json:"distros"`
 }
 
 type sourcesV0 map[string]sourceV0
diff --git a/internal/store/store.go b/internal/store/store.go
index f9321f7e6..3a9d9d04d 100644
--- a/internal/store/store.go
+++ b/internal/store/store.go
@@ -43,12 +43,13 @@ type Store struct {
 }
 
 type SourceConfig struct {
-	Name     string `json:"name" toml:"name"`
-	Type     string `json:"type" toml:"type"`
-	URL      string `json:"url" toml:"url"`
-	CheckGPG bool   `json:"check_gpg" toml:"check_gpg"`
-	CheckSSL bool   `json:"check_ssl" toml:"check_ssl"`
-	System   bool   `json:"system" toml:"system"`
+	Name     string   `json:"name" toml:"name"`
+	Type     string   `json:"type" toml:"type"`
+	URL      string   `json:"url" toml:"url"`
+	CheckGPG bool     `json:"check_gpg" toml:"check_gpg"`
+	CheckSSL bool     `json:"check_ssl" toml:"check_ssl"`
+	System   bool     `json:"system" toml:"system"`
+	Distros  []string `json:"distros" toml:"distros"`
 }
 
 type NotFoundError struct {
@@ -524,6 +525,29 @@ func (s *Store) GetAllSourcesByID() map[string]SourceConfig {
 	return sources
 }
 
+// GetAllDistroSources returns the sources using the repo id as the key
+// skipping sources that cannot be used with the selected distribution
+func (s *Store) GetAllDistroSources(distro string) map[string]SourceConfig {
+	s.mu.RLock()
+	defer s.mu.RUnlock()
+
+	sources := make(map[string]SourceConfig)
+
+	for k, v := range s.sources {
+		found := false
+		for _, d := range v.Distros {
+			if d == distro {
+				found = true
+			}
+		}
+		if len(v.Distros) == 0 || found {
+			sources[k] = v
+		}
+	}
+
+	return sources
+}
+
 func NewSourceConfig(repo rpmmd.RepoConfig, system bool) SourceConfig {
 	sc := SourceConfig{
 		Name:     repo.Name,
diff --git a/internal/weldr/api.go b/internal/weldr/api.go
index 07a1b274c..941a1094c 100644
--- a/internal/weldr/api.go
+++ b/internal/weldr/api.go
@@ -30,6 +30,7 @@ import (
 	"github.com/osbuild/osbuild-composer/internal/blueprint"
 	"github.com/osbuild/osbuild-composer/internal/common"
 	"github.com/osbuild/osbuild-composer/internal/distro"
+	"github.com/osbuild/osbuild-composer/internal/distroregistry"
 	"github.com/osbuild/osbuild-composer/internal/jobqueue"
 	osbuild "github.com/osbuild/osbuild-composer/internal/osbuild1"
 	"github.com/osbuild/osbuild-composer/internal/ostree"
@@ -46,13 +47,16 @@ type API struct {
 
 	rpmmd        rpmmd.RPMMD
 	arch         distro.Arch
-	distro       distro.Distro
 	repoRegistry *reporegistry.RepoRegistry
 
 	logger *log.Logger
 	router *httprouter.Router
 
 	compatOutputDir string
+
+	hostDistroName string                   // Name of the host distro
+	distroRegistry *distroregistry.Registry // Available distros
+	distros        []string                 // Supported distro names
 }
 
 type ComposeState int
@@ -92,20 +96,93 @@ func (api *API) systemRepoNames() (names []string) {
 	return names
 }
 
+// validDistros returns a list of distributions that also have repositories
+func validDistros(rr *reporegistry.RepoRegistry, dr *distroregistry.Registry, arch string, logger *log.Logger) []string {
+	distros := []string{}
+	for _, d := range dr.List() {
+		_, found := rr.DistroHasRepos(d, arch)
+		if found {
+			distros = append(distros, d)
+		} else {
+			logger.Printf("Distro %s has no repositories, skipping.", d)
+		}
+	}
+
+	// NOTE: distro list is already sorted, so result will be sorted
+	return distros
+}
+
 var ValidBlueprintName = regexp.MustCompile(`^[a-zA-Z0-9._-]+$`)
 
-func New(rpmmd rpmmd.RPMMD, arch distro.Arch, distro distro.Distro, repoRegistry *reporegistry.RepoRegistry, logger *log.Logger, store *store.Store, workers *worker.Server, compatOutputDir string) *API {
+// NewTestAPI is used for the test framework, sets up a single distro
+func NewTestAPI(rpm rpmmd.RPMMD, arch distro.Arch, dr *distroregistry.Registry,
+	rr *reporegistry.RepoRegistry, logger *log.Logger,
+	store *store.Store, workers *worker.Server, compatOutputDir string) *API {
+
+	// Use the first entry as the host distribution
+	hostDistro := dr.GetDistro(dr.List()[0])
 	api := &API{
 		store:           store,
 		workers:         workers,
-		rpmmd:           rpmmd,
+		rpmmd:           rpm,
 		arch:            arch,
-		distro:          distro,
-		repoRegistry:    repoRegistry,
+		repoRegistry:    rr,
 		logger:          logger,
 		compatOutputDir: compatOutputDir,
+		hostDistroName:  hostDistro.Name(),
+		distroRegistry:  dr,
+		distros:         validDistros(rr, dr, arch.Name(), logger),
+	}
+	return setupRouter(api)
+}
+
+func New(repoPaths []string, stateDir string, rpm rpmmd.RPMMD, dr *distroregistry.Registry, logger *log.Logger, workers *worker.Server) (*API, error) {
+	if logger == nil {
+		logger = log.New(os.Stdout, "", 0)
+	}
+
+	hostDistroName, _, _, err := distro.GetHostDistroName()
+	if err != nil {
+		return nil, fmt.Errorf("host distro is not supported")
+	}
+	archName := common.CurrentArch()
+
+	hostDistro := dr.GetDistro(hostDistroName)
+	hostArch, err := hostDistro.GetArch(archName)
+	if err != nil {
+		return nil, fmt.Errorf("Host distro does not support host architecture: %v", err)
+	}
+
+	rr, err := reporegistry.New(repoPaths)
+	if err != nil {
+		return nil, fmt.Errorf("error loading repository definitions: %v", err)
+	}
+
+	// Check if repositories for the host distro and arch were loaded
+	_, err = rr.ReposByArch(hostArch, false)
+	if err != nil {
+		return nil, fmt.Errorf("loaded repository definitions don't contain any for the host distro/arch: %v", err)
 	}
 
+	store := store.New(&stateDir, hostArch, logger)
+	compatOutputDir := path.Join(stateDir, "outputs")
+
+	api := &API{
+		store:           store,
+		workers:         workers,
+		rpmmd:           rpm,
+		arch:            hostArch,
+		repoRegistry:    rr,
+		logger:          logger,
+		compatOutputDir: compatOutputDir,
+		hostDistroName:  hostDistro.Name(),
+		distroRegistry:  dr,
+		distros:         validDistros(rr, dr, hostArch.Name(), logger),
+	}
+	return setupRouter(api), nil
+}
+
+func setupRouter(api *API) *API {
 	api.router = httprouter.New()
 	api.router.RedirectTrailingSlash = false
 	api.router.RedirectFixedPath = false
@@ -172,6 +249,7 @@ func New(rpmmd rpmmd.RPMMD, arch distro.Arch, distro distro.Distro, repoRegistry
 	api.router.POST("/api/v:version/upload/providers/save", api.providersSaveHandler)
 	api.router.DELETE("/api/v:version/upload/providers/delete/:provider/:profile", api.providersDeleteHandler)
 
+	api.router.GET("/api/v:version/distros/list", api.distrosListHandler)
 	return api
 }
 
@@ -300,6 +378,40 @@ func (api *API) openImageFile(composeId uuid.UUID, compose store.Compose) (io.Re
 	return reader, size, nil
 }
 
+// getImageType returns the ImageType for the selected distro
+// This is necessary because different distros support different image types, and the image
+// type may have a different package set than other distros.
+func (api *API) getImageType(distroName, imageType string) (distro.ImageType, error) {
+	distro := api.getDistro(distroName)
+	if distro == nil {
+		return nil, fmt.Errorf("GetDistro - unknown distribution: %s", distroName)
+	}
+	arch, err := distro.GetArch(api.arch.Name())
+	if err != nil {
+		return nil, err
+	}
+	return arch.GetImageType(imageType)
+}
+
+func (api *API) parseDistro(query url.Values) (string, error) {
+	if distro := query.Get("distro"); distro != "" {
+		if common.IsStringInSortedSlice(api.distros, distro) {
+			return distro, nil
+		}
+		return "", errors_package.New("Invalid distro: " + distro)
+	}
+	return api.hostDistroName, nil
+}
+
+// getDistro returns the named distro or nil
+// It excludes unsupported distros by first checking the api.distros list
+func (api *API) getDistro(name string) distro.Distro {
+	if !common.IsStringInSortedSlice(api.distros, name) {
+		return nil
+	}
+	return api.distroRegistry.GetDistro(name)
+}
+
 func verifyRequestVersion(writer http.ResponseWriter, params httprouter.Params, minVersion uint) bool {
 	versionString := params.ByName("version")
 
@@ -719,7 +831,6 @@ func (api *API) sourceNewHandler(writer http.ResponseWriter, request *http.Reque
 			err = errors_package.New("'url' field is missing from request")
 		}
 	}
-
 	if err != nil {
 		errors := responseError{
 			ID:  "ProjectsError",
@@ -729,6 +840,22 @@ func (api *API) sourceNewHandler(writer http.ResponseWriter, request *http.Reque
 		return
 	}
 
+	// If there is a list of distros, check to make sure they are valid
+	invalid := []string{}
+	for _, d := range source.SourceConfig().Distros {
+		if !common.IsStringInSortedSlice(api.distros, d) {
+			invalid = append(invalid, d)
+		}
+	}
+	if len(invalid) > 0 {
+		errors := responseError{
+			ID:  "ProjectsError",
+			Msg: "Invalid distributions: " + strings.Join(invalid, ","),
+		}
+		statusResponseError(writer, http.StatusBadRequest, errors)
+		return
+	}
+
 	// Is there an existing System Repo using this id?
 	for _, n := range api.systemRepoNames() {
 		if n == source.GetKey() {
@@ -816,7 +943,18 @@ func (api *API) modulesListHandler(writer http.ResponseWriter, request *http.Req
 
 	modulesParam := params.ByName("modules")
 
-	availablePackages, err := api.fetchPackageList()
+	// Optional distro parameter
+	// If it is empty it will return api.hostDistroName
+	distroName, err := api.parseDistro(request.URL.Query())
+	if err != nil {
+		errors := responseError{
+			ID:  "DistroError",
+			Msg: err.Error(),
+		}
+		statusResponseError(writer, http.StatusBadRequest, errors)
+		return
+	}
+	availablePackages, err := api.fetchPackageList(distroName)
 
 	if err != nil {
 		errors := responseError{
@@ -902,7 +1040,18 @@ func (api *API) projectsListHandler(writer http.ResponseWriter, request *http.Re
 		return
 	}
 
-	availablePackages, err := api.fetchPackageList()
+	// Optional distro parameter
+	// If it is empty it will return api.hostDistroName
+	distroName, err := api.parseDistro(request.URL.Query())
+	if err != nil {
+		errors := responseError{
+			ID:  "DistroError",
+			Msg: err.Error(),
+		}
+		statusResponseError(writer, http.StatusBadRequest, errors)
+		return
+	}
+	availablePackages, err := api.fetchPackageList(distroName)
 
 	if err != nil {
 		errors := responseError{
@@ -974,7 +1123,18 @@ func (api *API) modulesInfoHandler(writer http.ResponseWriter, request *http.Req
 
 	names := strings.Split(modules, ",")
 
-	availablePackages, err := api.fetchPackageList()
+	// Optional distro parameter
+	// If it is empty it will return api.hostDistroName
+	distroName, err := api.parseDistro(request.URL.Query())
+	if err != nil {
+		errors := responseError{
+			ID:  "DistroError",
+			Msg: err.Error(),
+		}
+		statusResponseError(writer, http.StatusBadRequest, errors)
+		return
+	}
+	availablePackages, err := api.fetchPackageList(distroName)
 
 	if err != nil {
 		errors := responseError{
@@ -1008,7 +1168,7 @@ func (api *API) modulesInfoHandler(writer http.ResponseWriter, request *http.Req
 	packageInfos := foundPackages.ToPackageInfos()
 
 	if modulesRequested {
-		repos, err := api.repoRegistry.ReposByArch(api.arch, false)
+		repos, err := api.allRepositories(distroName)
 		if err != nil {
 			errors := responseError{
 				ID:  "InternalError",
@@ -1017,9 +1177,18 @@ func (api *API) modulesInfoHandler(writer http.ResponseWriter, request *http.Req
 			statusResponseError(writer, http.StatusBadRequest, errors)
 			return
 		}
+		d := api.getDistro(distroName)
+		if d == nil {
+			errors := responseError{
+				ID:  "DistroError",
+				Msg: fmt.Sprintf("Unknown distribution: %s", distroName),
+			}
+			statusResponseError(writer, http.StatusBadRequest, errors)
+			return
+		}
 
 		for i := range packageInfos {
-			err := packageInfos[i].FillDependencies(api.rpmmd, repos, api.distro.ModulePlatformID(), api.arch.Name())
+			err := packageInfos[i].FillDependencies(api.rpmmd, repos, d.ModulePlatformID(), api.arch.Name())
 			if err != nil {
 				errors := responseError{
 					ID:  errorId,
@@ -1063,21 +1232,45 @@ func (api *API) projectsDepsolveHandler(writer http.ResponseWriter, request *htt
 	projects = projects[1:]
 	names := strings.Split(projects, ",")
 
-	repos, err := api.repoRegistry.ReposByArch(api.arch, false)
+	// Optional distro parameter
+	// If it is empty it will return api.hostDistroName
+	distroName, err := api.parseDistro(request.URL.Query())
 	if err != nil {
 		errors := responseError{
-			ID:  "InternalError",
-			Msg: fmt.Sprintf("error while getting system repos: %v", err),
+			ID:  "DistroError",
+			Msg: err.Error(),
 		}
 		statusResponseError(writer, http.StatusBadRequest, errors)
 		return
 	}
 
-	packages, _, err := api.rpmmd.Depsolve(rpmmd.PackageSet{Include: names}, repos, api.distro.ModulePlatformID(), api.arch.Name())
+	d := api.getDistro(distroName)
+	if d == nil {
+		errors := responseError{
+			ID:  "DistroError",
+			Msg: fmt.Sprintf("Unknown distribution: %s", distroName),
+		}
+		statusResponseError(writer, http.StatusBadRequest, errors)
+		return
+	}
+
+	repos, err := api.allRepositories(distroName)
+	if err != nil {
+		errors := responseError{
+			ID:  "ProjectsError",
+			Msg: err.Error(),
+		}
+		statusResponseError(writer, http.StatusBadRequest, errors)
+		return
+	}
 
+	packages, _, err := api.rpmmd.Depsolve(rpmmd.PackageSet{Include: names},
+		repos,
+		d.ModulePlatformID(),
+		api.arch.Name())
 	if err != nil {
 		errors := responseError{
-			ID:  "PROJECTS_ERROR",
+			ID:  "ProjectsError",
 			Msg: fmt.Sprintf("BadRequest: %s", err.Error()),
 		}
 		statusResponseError(writer, http.StatusBadRequest, errors)
@@ -1268,7 +1461,7 @@ func (api *API) blueprintsDepsolveHandler(writer http.ResponseWriter, request *h
 			continue
 		}
 
-		dependencies, err := api.depsolveBlueprint(blueprint)
+		dependencies, err := api.depsolveBlueprint(*blueprint)
 
 		if err != nil {
 			blueprintsErrors = append(blueprintsErrors, responseError{
@@ -1357,7 +1550,7 @@ func (api *API) blueprintsFreezeHandler(writer http.ResponseWriter, request *htt
 		}
 		// Make a copy of the blueprint since we will be replacing the version globs
 		blueprint := bp.DeepCopy()
-		dependencies, err := api.depsolveBlueprint(&blueprint)
+		dependencies, err := api.depsolveBlueprint(blueprint)
 		if err != nil {
 			rerr := responseError{
 				ID:  "BlueprintsError",
@@ -1656,6 +1849,18 @@ func (api *API) blueprintsNewHandler(writer http.ResponseWriter, request *http.R
 		return
 	}
 
+	// Check the blueprint's distro to make sure it is valid
+	if len(blueprint.Distro) > 0 {
+		if !common.IsStringInSortedSlice(api.distros, blueprint.Distro) {
+			errors := responseError{
+				ID:  "BlueprintsError",
+				Msg: fmt.Sprintf("'%s' is not a valid distribution", blueprint.Distro),
+			}
+			statusResponseError(writer, http.StatusBadRequest, errors)
+			return
+		}
+	}
+
 	commitMsg := "Recipe " + blueprint.Name + ", version " + blueprint.Version + " saved."
 	err = api.store.PushBlueprint(blueprint, commitMsg)
 	if err != nil {
@@ -1834,17 +2039,31 @@ func (api *API) blueprintsTagHandler(writer http.ResponseWriter, request *http.R
 	statusResponseOK(writer)
 }
 
-func (api *API) depsolveBlueprintForImageType(bp *blueprint.Blueprint, imageType distro.ImageType) (map[string][]rpmmd.PackageSpec, error) {
-	packageSets := imageType.PackageSets(*bp)
+// depsolveBlueprintForImageType handles depsolving the blueprint package list and
+// the packages required for the image type.
+// NOTE: The imageType *must* be from the same distribution as the blueprint.
+func (api *API) depsolveBlueprintForImageType(bp blueprint.Blueprint, imageType distro.ImageType) (map[string][]rpmmd.PackageSpec, error) {
+	// Depsolve using the host distro if none has been specified
+	if bp.Distro == "" {
+		bp.Distro = api.hostDistroName
+	}
+
+	if bp.Distro != imageType.Arch().Distro().Name() {
+		return nil, fmt.Errorf("Blueprint distro %s does not match imageType distro %s", bp.Distro, imageType.Arch().Distro().Name())
+	}
+	packageSets := imageType.PackageSets(bp)
 	packageSpecSets := make(map[string][]rpmmd.PackageSpec)
 
 	imageTypeRepos, err := api.allRepositoriesByImageType(imageType)
 	if err != nil {
 		return nil, err
 	}
-
+	platformID := imageType.Arch().Distro().ModulePlatformID()
 	for name, packageSet := range packageSets {
-		packageSpecs, _, err := api.rpmmd.Depsolve(packageSet, imageTypeRepos, api.distro.ModulePlatformID(), api.arch.Name())
+		packageSpecs, _, err := api.rpmmd.Depsolve(packageSet,
+			imageTypeRepos,
+			platformID,
+			api.arch.Name())
 		if err != nil {
 			return nil, err
 		}
@@ -1896,7 +2115,35 @@ func (api *API) composeHandler(writer http.ResponseWriter, request *http.Request
 		return
 	}
 
-	imageType, err := api.arch.GetImageType(cr.ComposeType)
+	if !verifyStringsWithRegex(writer, []string{cr.BlueprintName}, ValidBlueprintName) {
+		return
+	}
+
+	bp := api.store.GetBlueprintCommitted(cr.BlueprintName)
+	if bp == nil {
+		errors := responseError{
+			ID:  "UnknownBlueprint",
+			Msg: fmt.Sprintf("Unknown blueprint name: %s", cr.BlueprintName),
+		}
+		statusResponseError(writer, http.StatusBadRequest, errors)
+		return
+	}
+
+	distroName := bp.Distro
+	if distroName == "" {
+		distroName = api.hostDistroName
+	}
+	if api.getDistro(distroName) == nil {
+		errors := responseError{
+			ID:  "DistroError",
+			Msg: fmt.Sprintf("Unknown distribution: %s", distroName),
+		}
+		statusResponseError(writer, http.StatusBadRequest, errors)
+		return
+	}
+
+	// Get the imageType that corresponds to the distribution selected by the blueprint
+	imageType, err := api.getImageType(distroName, cr.ComposeType)
 	if err != nil {
 		errors := responseError{
 			ID:  "UnknownComposeType",
@@ -1918,10 +2165,6 @@ func (api *API) composeHandler(writer http.ResponseWriter, request *http.Request
 		return
 	}
 
-	if !verifyStringsWithRegex(writer, []string{cr.BlueprintName}, ValidBlueprintName) {
-		return
-	}
-
 	composeID := uuid.New()
 
 	var targets []*target.Target
@@ -1930,16 +2173,6 @@ func (api *API) composeHandler(writer http.ResponseWriter, request *http.Request
 		targets = append(targets, t)
 	}
 
-	bp := api.store.GetBlueprintCommitted(cr.BlueprintName)
-	if bp == nil {
-		errors := responseError{
-			ID:  "UnknownBlueprint",
-			Msg: fmt.Sprintf("Unknown blueprint name: %s", cr.BlueprintName),
-		}
-		statusResponseError(writer, http.StatusBadRequest, errors)
-		return
-	}
-
 	// Check for test parameter
 	q, err := url.ParseQuery(request.URL.RawQuery)
 	if err != nil {
@@ -1984,7 +2217,7 @@ func (api *API) composeHandler(writer http.ResponseWriter, request *http.Request
 		cr.OSTree.Parent = parent
 	}
 
-	packageSets, err := api.depsolveBlueprintForImageType(bp, imageType)
+	packageSets, err := api.depsolveBlueprintForImageType(*bp, imageType)
 	if err != nil {
 		errors := responseError{
 			ID:  "DepsolveError",
@@ -2003,7 +2236,7 @@ func (api *API) composeHandler(writer http.ResponseWriter, request *http.Request
 	seed := bigSeed.Int64()
 
 	imageRepos, err := api.allRepositoriesByImageType(imageType)
-	// this shoudl not happen if the api.depsolveBlueprintForImageType() call above worked
+	// this should not happen if the api.depsolveBlueprintForImageType() call above worked
 	if err != nil {
 		errors := responseError{
 			ID:  "InternalError",
@@ -2206,6 +2439,39 @@ func (api *API) composeTypesHandler(writer http.ResponseWriter, request *http.Re
 	if !verifyRequestVersion(writer, params, 0) {
 		return
 	}
+	// Optional distro parameter
+	// If it is empty it will return api.hostDistroName
+	distroName, err := api.parseDistro(request.URL.Query())
+	if err != nil {
+		errors := responseError{
+			ID:  "DistroError",
+			Msg: err.Error(),
+		}
+		statusResponseError(writer, http.StatusBadRequest, errors)
+		return
+	}
+
+	d := api.getDistro(distroName)
+	if d == nil {
+		errors := responseError{
+			ID:  "DistroError",
+			Msg: fmt.Sprintf("Unknown distribution: %s", distroName),
+		}
+		statusResponseError(writer, http.StatusBadRequest, errors)
+		return
+	}
+
+	// Get the distro specific arch so that we can return the correct list of image types
+	arch, err := d.GetArch(api.arch.Name())
+	if err != nil {
+		errors := responseError{
+			ID:  "DistroError",
+			Msg: fmt.Sprintf("Unknown arch: %s", api.arch.Name()),
+		}
+		statusResponseError(writer, http.StatusBadRequest, errors)
+		return
+	}
+
 	type composeType struct {
 		Name    string `json:"name"`
 		Enabled bool   `json:"enabled"`
@@ -2215,11 +2481,11 @@ func (api *API) composeTypesHandler(writer http.ResponseWriter, request *http.Re
 		Types []composeType `json:"types"`
 	}
 
-	for _, format := range api.arch.ListImageTypes() {
+	for _, format := range arch.ListImageTypes() {
 		reply.Types = append(reply.Types, composeType{format, true})
 	}
 
-	err := json.NewEncoder(writer).Encode(reply)
+	err = json.NewEncoder(writer).Encode(reply)
 	common.PanicOnError(err)
 }
 
@@ -2764,20 +3030,25 @@ func (api *API) composeFailedHandler(writer http.ResponseWriter, request *http.R
 	common.PanicOnError(err)
 }
 
-func (api *API) fetchPackageList() (rpmmd.PackageList, error) {
-	repos, err := api.allRepositories()
+// fetchPackageList returns the package list or the selected distribution
+func (api *API) fetchPackageList(distroName string) (rpmmd.PackageList, error) {
+	d := api.getDistro(distroName)
+	if d == nil {
+		return nil, fmt.Errorf("GetDistro - unknown distribution: %s", distroName)
+	}
+	repos, err := api.allRepositories(distroName)
 	if err != nil {
 		return nil, err
 	}
 
-	packages, _, err := api.rpmmd.FetchMetadata(repos, api.distro.ModulePlatformID(), api.arch.Name())
+	packages, _, err := api.rpmmd.FetchMetadata(repos, d.ModulePlatformID(), api.arch.Name())
 	return packages, err
 }
 
 // Returns all configured repositories (base, depending on the image type + sources) as rpmmd.RepoConfig
 // The difference from allRepositories() is that this method may return additional repositories,
 // which are needed to build the specific image type. The allRepositories() can't do this, because
-// it is used in paces where image types are not considered.
+// it is used in places where image types are not considered.
 func (api *API) allRepositoriesByImageType(imageType distro.ImageType) ([]rpmmd.RepoConfig, error) {
 	imageTypeRepos, err := api.repoRegistry.ReposByImageType(imageType)
 	if err != nil {
@@ -2785,7 +3056,7 @@ func (api *API) allRepositoriesByImageType(imageType distro.ImageType) ([]rpmmd.
 	}
 
 	repos := append([]rpmmd.RepoConfig{}, imageTypeRepos...)
-	for id, source := range api.store.GetAllSourcesByID() {
+	for id, source := range api.store.GetAllDistroSources(imageType.Arch().Distro().Name()) {
 		repos = append(repos, source.RepoConfig(id))
 	}
 
@@ -2793,27 +3064,36 @@ func (api *API) allRepositoriesByImageType(imageType distro.ImageType) ([]rpmmd.
 }
 
 // Returns all configured repositories (base + sources) as rpmmd.RepoConfig
-func (api *API) allRepositories() ([]rpmmd.RepoConfig, error) {
-	archRepos, err := api.repoRegistry.ReposByArch(api.arch, false)
+func (api *API) allRepositories(distroName string) ([]rpmmd.RepoConfig, error) {
+	archRepos, err := api.repoRegistry.ReposByArchName(distroName, api.arch.Name(), false)
 	if err != nil {
 		return nil, err
 	}
 
 	repos := append([]rpmmd.RepoConfig{}, archRepos...)
-	for id, source := range api.store.GetAllSourcesByID() {
+	for id, source := range api.store.GetAllDistroSources(distroName) {
 		repos = append(repos, source.RepoConfig(id))
 	}
 
 	return repos, nil
 }
 
-func (api *API) depsolveBlueprint(bp *blueprint.Blueprint) ([]rpmmd.PackageSpec, error) {
-	repos, err := api.allRepositories()
+func (api *API) depsolveBlueprint(bp blueprint.Blueprint) ([]rpmmd.PackageSpec, error) {
+	// Depsolve using the host distro if none has been specified
+	if bp.Distro == "" {
+		bp.Distro = api.hostDistroName
+	}
+
+	d := api.getDistro(bp.Distro)
+	if d == nil {
+		return nil, fmt.Errorf("GetDistro - unknown distribution: %s", bp.Distro)
+	}
+	repos, err := api.allRepositories(bp.Distro)
 	if err != nil {
 		return nil, err
 	}
 
-	packages, _, err := api.rpmmd.Depsolve(rpmmd.PackageSet{Include: bp.GetPackages()}, repos, api.distro.ModulePlatformID(), api.arch.Name())
+	packages, _, err := api.rpmmd.Depsolve(rpmmd.PackageSet{Include: bp.GetPackages()}, repos, d.ModulePlatformID(), api.arch.Name())
 	if err != nil {
 		return nil, err
 	}
@@ -2901,3 +3181,17 @@ func (api *API) providersDeleteHandler(writer http.ResponseWriter, request *http
 	// TODO: implement this route (it is v1 only)
 	notImplementedHandler(writer, request, params)
 }
+
+func (api *API) distrosListHandler(writer http.ResponseWriter, request *http.Request, params httprouter.Params) {
+	if !verifyRequestVersion(writer, params, 1) {
+		return
+	}
+
+	var reply struct {
+		Distros []string `json:"distros"`
+	}
+	reply.Distros = api.distros
+
+	err := json.NewEncoder(writer).Encode(reply)
+	common.PanicOnError(err)
+}
diff --git a/internal/weldr/json.go b/internal/weldr/json.go
index 669b93f8b..6514ffc8e 100644
--- a/internal/weldr/json.go
+++ b/internal/weldr/json.go
@@ -186,6 +186,7 @@ func NewSourceConfigV1(id string, s store.SourceConfig) SourceConfigV1 {
 	sc.CheckGPG = s.CheckGPG
 	sc.CheckSSL = s.CheckSSL
 	sc.System = s.System
+	sc.Distros = s.Distros
 
 	return sc
 }
@@ -201,6 +202,7 @@ type SourceConfigV1 struct {
 	System   bool     `json:"system" toml:"system"`
 	Proxy    string   `json:"proxy,omitempty" toml:"proxy,omitempty"`
 	GPGUrls  []string `json:"gpgkey_urls,omitempty" toml:"gpgkey_urls,omitempty"`
+	Distros  []string `json:"distros,omitempty" toml:"distros,omitempty"`
 }
 
 // Key returns the key, .ID in this case
@@ -226,6 +228,7 @@ func (s SourceConfigV1) SourceConfig() (ssc store.SourceConfig) {
 	ssc.URL = s.URL
 	ssc.CheckGPG = s.CheckGPG
 	ssc.CheckSSL = s.CheckSSL
+	ssc.Distros = s.Distros
 
 	return ssc
 }
diff --git a/schutzbot/slack_notification.sh b/schutzbot/slack_notification.sh
new file mode 100755
index 000000000..47cd7ff29
--- /dev/null
+++ b/schutzbot/slack_notification.sh
@@ -0,0 +1,12 @@
+#!/bin/bash
+
+set -eux
+
+COMPOSE_ID=$(cat COMPOSE_ID)
+MESSAGE="\"Nightly pipeline execution on *$COMPOSE_ID* finished with status *$1* $2 \n Link to results: $CI_PIPELINE_URL \""
+
+curl \
+    -X POST \
+    -H 'Content-type: application/json' \
+    --data '{"text": "test", "blocks": [ { "type": "section", "text": {"type": "mrkdwn", "text":'"$MESSAGE"'}}]}' \
+    "$SLACK_WEBHOOK_URL"
diff --git a/tools/define-compose-url.sh b/tools/define-compose-url.sh
index 8ac8c0cf8..6d44d4fee 100755
--- a/tools/define-compose-url.sh
+++ b/tools/define-compose-url.sh
@@ -8,7 +8,7 @@ COMPOSE_ID=$(cat COMPOSE_ID)
 COMPOSE_URL="${COMPOSE_URL:-http://download.devel.redhat.com/rhel-8/nightly/RHEL-8/$COMPOSE_ID}"
 
 # in case COMPOSE_URL was defined from the outside refresh COMPOSE_ID file,
-# used for telegram messages in case of success/failure
+# used for slack messages in case of success/failure
 curl -L "$COMPOSE_URL/COMPOSE_ID" > COMPOSE_ID
 
 # Make sure the compose URL really exists
diff --git a/tools/provision.sh b/tools/provision.sh
index 4af900d10..119c74f86 100755
--- a/tools/provision.sh
+++ b/tools/provision.sh
@@ -47,32 +47,38 @@ EOF
 fi
 
 # Copy rpmrepo snapshots for use in weldr tests
-sudo mkdir -p /etc/osbuild-composer/repositories
+REPODIR=/etc/osbuild-composer/repositories
+sudo mkdir -p $REPODIR
 # Copy all fedora repo overrides
-sudo cp -a /usr/share/tests/osbuild-composer/repositories/{fedora,centos}-*.json \
-    /etc/osbuild-composer/repositories/
+sudo cp -a /usr/share/tests/osbuild-composer/repositories/{fedora,centos}-*.json "$REPODIR"
+# Copy RHEL point relese repos
+sudo cp /usr/share/tests/osbuild-composer/repositories/rhel-84.json "$REPODIR"
+sudo cp /usr/share/tests/osbuild-composer/repositories/rhel-85.json "$REPODIR"
+
 # RHEL nightly repos need to be overridden in rhel-8.json and rhel-8-beta.json
 case "${ID}-${VERSION_ID}" in
     "rhel-8.4")
         # Override old rhel-8.json and rhel-8-beta.json because RHEL 8.4 test needs nightly repos
-        sudo cp /usr/share/tests/osbuild-composer/repositories/rhel-84.json /etc/osbuild-composer/repositories/rhel-8.json
+        sudo cp /usr/share/tests/osbuild-composer/repositories/rhel-84.json "$REPODIR/rhel-8.json"
         # If multiple tests are run and call provision.sh the symlink will need to be overridden with -f
-        sudo ln -sf /etc/osbuild-composer/repositories/rhel-8.json /etc/osbuild-composer/repositories/rhel-8-beta.json;;
+        sudo ln -sf /etc/osbuild-composer/repositories/rhel-8.json "$REPODIR/rhel-8-beta.json"
+        ;;
     "rhel-8.5")
         # Override old rhel-8.json and rhel-8-beta.json because RHEL 8.5 test needs nightly repos
-        sudo cp /usr/share/tests/osbuild-composer/repositories/rhel-85.json /etc/osbuild-composer/repositories/rhel-8.json
+        sudo cp /usr/share/tests/osbuild-composer/repositories/rhel-85.json "$REPODIR/rhel-8.json"
         # If multiple tests are run and call provision.sh the symlink will need to be overridden with -f
-        sudo ln -sf /etc/osbuild-composer/repositories/rhel-8.json /etc/osbuild-composer/repositories/rhel-8-beta.json;;
+        sudo ln -sf /etc/osbuild-composer/repositories/rhel-8.json "$REPODIR/rhel-8-beta.json"
+        ;;
     *) ;;
 esac
 
 # overrides for RHEL nightly builds testing
 if [ -f "rhel-8.json" ]; then
-    sudo cp rhel-8.json /etc/osbuild-composer/repositories/
+    sudo cp rhel-8.json "$REPODIR"
 fi
 
 if [ -f "rhel-8-beta.json" ]; then
-    sudo cp rhel-8-beta.json /etc/osbuild-composer/repositories/
+    sudo cp rhel-8-beta.json "$REPODIR"
 fi
 
 # Generate all X.509 certificates for the tests


Test patch:
diff --git a/internal/client/blueprints_test.go b/internal/client/blueprints_test.go
index 67c635b0d..75e795916 100644
--- a/internal/client/blueprints_test.go
+++ b/internal/client/blueprints_test.go
@@ -16,6 +16,8 @@ import (
 	"strings"
 	"testing"
 
+	"github.com/osbuild/osbuild-composer/internal/common"
+
 	"github.com/BurntSushi/toml"
 	"github.com/stretchr/testify/assert"
 	"github.com/stretchr/testify/require"
@@ -606,7 +608,7 @@ func TestBumpBlueprintVersionV0(t *testing.T) {
 
 	// If the blueprint already exists it needs to be deleted to start from a known state
 	sorted := sort.StringSlice(list)
-	if isStringInSlice(sorted, "test-bump-blueprint-1-v0") {
+	if common.IsStringInSortedSlice(sorted, "test-bump-blueprint-1-v0") {
 		// Delete this blueprint if it already exists
 		resp, err := DeleteBlueprintV0(testState.socket, "test-bump-blueprint-1-v0")
 		require.NoError(t, err, "DELETE blueprint failed with a client error")
diff --git a/internal/client/unit_test.go b/internal/client/unit_test.go
index 9fa0db91e..ae596486e 100644
--- a/internal/client/unit_test.go
+++ b/internal/client/unit_test.go
@@ -15,6 +15,7 @@ import (
 	"testing"
 
 	"github.com/osbuild/osbuild-composer/internal/distro/test_distro"
+	"github.com/osbuild/osbuild-composer/internal/distroregistry"
 	rpmmd_mock "github.com/osbuild/osbuild-composer/internal/mocks/rpmmd"
 	"github.com/osbuild/osbuild-composer/internal/reporegistry"
 	"github.com/osbuild/osbuild-composer/internal/rpmmd"
@@ -45,11 +46,13 @@ func executeTests(m *testing.M) int {
 	}
 	fixture := rpmmd_mock.BaseFixture(path.Join(tmpdir, "/jobs"))
 	rpm := rpmmd_mock.NewRPMMDMock(fixture)
-	distro := test_distro.New()
-	arch, err := distro.GetArch(test_distro.TestArchName)
+
+	distro1 := test_distro.New()
+	arch, err := distro1.GetArch(test_distro.TestArchName)
 	if err != nil {
 		panic(err)
 	}
+	distro2 := test_distro.New2()
 
 	rr := reporegistry.NewFromDistrosRepoConfigs(rpmmd.DistrosRepoConfigs{
 		test_distro.TestDistroName: {
@@ -59,8 +62,13 @@ func executeTests(m *testing.M) int {
 		},
 	})
 
+	dr, err := distroregistry.New(distro1, distro1, distro2)
+	if err != nil {
+		panic(err)
+	}
+
 	logger := log.New(os.Stdout, "", 0)
-	api := weldr.New(rpm, arch, distro, rr, logger, fixture.Store, fixture.Workers, "")
+	api := weldr.NewTestAPI(rpm, arch, dr, rr, logger, fixture.Store, fixture.Workers, "")
 	server := http.Server{Handler: api}
 	defer server.Close()
 
diff --git a/internal/common/helpers_test.go b/internal/common/helpers_test.go
index 2ec687676..467093221 100644
--- a/internal/common/helpers_test.go
+++ b/internal/common/helpers_test.go
@@ -45,3 +45,10 @@ func TestPanicOnError(t *testing.T) {
 	err := errors.New("Error message")
 	assert.PanicsWithValue(t, err, func() { PanicOnError(err) })
 }
+
+func TestIsStringInSortedSlice(t *testing.T) {
+	assert.True(t, IsStringInSortedSlice([]string{"bart", "homer", "lisa", "marge"}, "homer"))
+	assert.False(t, IsStringInSortedSlice([]string{"bart", "lisa", "marge"}, "homer"))
+	assert.False(t, IsStringInSortedSlice([]string{"bart", "lisa", "marge"}, ""))
+	assert.False(t, IsStringInSortedSlice([]string{}, "homer"))
+}
diff --git a/internal/reporegistry/reporegistry_test.go b/internal/reporegistry/reporegistry_test.go
index a5b5e7010..09df8d5c7 100644
--- a/internal/reporegistry/reporegistry_test.go
+++ b/internal/reporegistry/reporegistry_test.go
@@ -132,9 +132,9 @@ func TestInvalidReposByImageType(t *testing.T) {
 	assert.NotNil(t, err)
 }
 
-// TestInvalidReposByImageTypeName tests return values from reposByImageTypeName
+// TestInvalidreposByImageTypeName tests return values from reposByImageTypeName
 // for invalid distro name, arch and image type
-func TestInvalidReposByImageTypeName(t *testing.T) {
+func TestInvalidreposByImageTypeName(t *testing.T) {
 	rr := getTestingRepoRegistry()
 
 	type args struct {
@@ -289,7 +289,7 @@ func TestReposByArch(t *testing.T) {
 				t.Errorf("ReposByArch() =\n got: %#v\n want: %#v", gotNames, tt.want)
 			}
 
-			got, err = rr.reposByArchName(tt.args.arch.Distro().Name(), tt.args.arch.Name(), tt.args.taggedRepos)
+			got, err = rr.ReposByArchName(tt.args.arch.Distro().Name(), tt.args.arch.Name(), tt.args.taggedRepos)
 			assert.Nil(t, err)
 			gotNames = []string{}
 			for _, r := range got {
@@ -297,7 +297,7 @@ func TestReposByArch(t *testing.T) {
 			}
 
 			if !reflect.DeepEqual(gotNames, tt.want) {
-				t.Errorf("reposByArchName() =\n got: %#v\n want: %#v", gotNames, tt.want)
+				t.Errorf("ReposByArchName() =\n got: %#v\n want: %#v", gotNames, tt.want)
 			}
 		})
 	}
@@ -319,7 +319,7 @@ func TestInvalidReposByArch(t *testing.T) {
 	assert.NotNil(t, err)
 }
 
-// TestInvalidReposByArchName tests return values from reposByArchName
+// TestInvalidReposByArchName tests return values from ReposByArchName
 // for invalid distro name and arch
 func TestInvalidReposByArchName(t *testing.T) {
 	rr := getTestingRepoRegistry()
@@ -397,7 +397,7 @@ func TestInvalidReposByArchName(t *testing.T) {
 	}
 	for _, tt := range tests {
 		t.Run(tt.name, func(t *testing.T) {
-			got, err := rr.reposByArchName(tt.args.distro, tt.args.arch, tt.args.taggedRepos)
+			got, err := rr.ReposByArchName(tt.args.distro, tt.args.arch, tt.args.taggedRepos)
 			assert.True(t, tt.want(got, err))
 		})
 	}
diff --git a/internal/weldr/api_test.go b/internal/weldr/api_test.go
index b8c07403c..814a9ce5e 100644
--- a/internal/weldr/api_test.go
+++ b/internal/weldr/api_test.go
@@ -19,6 +19,7 @@ import (
 	"github.com/osbuild/osbuild-composer/internal/common"
 	"github.com/osbuild/osbuild-composer/internal/distro"
 	"github.com/osbuild/osbuild-composer/internal/distro/test_distro"
+	"github.com/osbuild/osbuild-composer/internal/distroregistry"
 	rpmmd_mock "github.com/osbuild/osbuild-composer/internal/mocks/rpmmd"
 	"github.com/osbuild/osbuild-composer/internal/reporegistry"
 	"github.com/osbuild/osbuild-composer/internal/rpmmd"
@@ -41,15 +42,26 @@ func createWeldrAPI(tempdir string, fixtureGenerator rpmmd_mock.FixtureGenerator
 				{Name: "test-id", BaseURL: "http://example.com/test/os/x86_64", CheckGPG: true},
 			},
 		},
+		test_distro.TestDistro2Name: {
+			test_distro.TestArchName: {
+				{Name: "test-id-2", BaseURL: "http://example.com/test-2/os/x86_64", CheckGPG: true},
+			},
+		},
 	})
 
-	d := test_distro.New()
-	arch, err := d.GetArch(test_distro.TestArchName)
+	distro1 := test_distro.New()
+	arch, err := distro1.GetArch(test_distro.TestArchName)
+	if err != nil {
+		panic(err)
+	}
+	distro2 := test_distro.New2()
+
+	dr, err := distroregistry.New(distro1, distro1, distro2)
 	if err != nil {
 		panic(err)
 	}
 
-	return New(rpm, arch, d, rr, nil, fixture.Store, fixture.Workers, ""), fixture.Store
+	return NewTestAPI(rpm, arch, dr, rr, nil, fixture.Store, fixture.Workers, ""), fixture.Store
 }
 
 func TestBasic(t *testing.T) {
@@ -71,6 +83,10 @@ func TestBasic(t *testing.T) {
 		{"/api/v0/blueprints/list", http.StatusOK, `{"total":1,"offset":0,"limit":1,"blueprints":["test"]}`},
 		{"/api/v0/blueprints/info/", http.StatusNotFound, `{"errors":[{"code":404,"id":"HTTPError","msg":"Not Found"}],"status":false}`},
 		{"/api/v0/blueprints/info/foo", http.StatusOK, `{"blueprints":[],"changes":[],"errors":[{"id":"UnknownBlueprint","msg":"foo: "}]}`},
+		{"/api/v1/distros/list", http.StatusOK, `{"distros": ["test-distro", "test-distro-2"]}`},
+		{"/api/v1/compose/types", http.StatusOK, `{"types": [{"enabled":true, "name":"test_type"}]}`},
+		{"/api/v1/compose/types?distro=test-distro-2", http.StatusOK, `{"types": [{"enabled":true, "name":"test_type"}]}`},
+		{"/api/v1/compose/types?distro=fedora-1", http.StatusBadRequest, `{"status":false,"errors":[{"id":"DistroError","msg":"Invalid distro: fedora-1"}]}`},
 	}
 
 	tempdir, err := ioutil.TempDir("", "weldr-tests-")
@@ -96,6 +112,9 @@ func TestBlueprintsNew(t *testing.T) {
 		{"POST", "/api/v0/blueprints/new", `{"name":"test","description":"Test","packages:}`, http.StatusBadRequest, `{"status":false,"errors":[{"id":"BlueprintsError","msg":"400 Bad Request: The browser (or proxy) sent a request that this server could not understand: unexpected EOF"}]}`},
 		{"POST", "/api/v0/blueprints/new", `{"name":"","description":"Test","packages":[{"name":"httpd","version":"2.4.*"}],"version":"0.0.0"}`, http.StatusBadRequest, `{"status":false,"errors":[{"id":"InvalidChars","msg":"Invalid characters in API path"}]}`},
 		{"POST", "/api/v0/blueprints/new", ``, http.StatusBadRequest, `{"status":false,"errors":[{"id":"BlueprintsError","msg":"Missing blueprint"}]}`},
+		{"POST", "/api/v0/blueprints/new", `{"name":"test","description":"Test","distro":"test-distro","packages":[],"version":""}`, http.StatusOK, `{"status":true}`},
+		{"POST", "/api/v0/blueprints/new", `{"name":"test2","description":"Test 2","distro":"test-distro-2","packages":[],"version":""}`, http.StatusOK, `{"status":true}`},
+		{"POST", "/api/v0/blueprints/new", `{"name":"test","description":"Test","distro":"fedora-1","packages":[],"version":""}`, http.StatusBadRequest, `{"status":false,"errors":[{"id":"BlueprintsError","msg":"'fedora-1' is not a valid distribution"}]}`},
 	}
 
 	tempdir, err := ioutil.TempDir("", "weldr-tests-")
@@ -271,9 +290,9 @@ func TestBlueprintsInfo(t *testing.T) {
 		ExpectedStatus int
 		ExpectedJSON   string
 	}{
-		{"GET", "/api/v0/blueprints/info/test1", ``, http.StatusOK, `{"blueprints":[{"name":"test1","description":"Test","modules":[],"packages":[{"name":"httpd","version":"2.4.*"}],"groups":[],"version":"0.0.0"}],
+		{"GET", "/api/v0/blueprints/info/test1", ``, http.StatusOK, `{"blueprints":[{"name":"test1","description":"Test","distro":"","modules":[],"packages":[{"name":"httpd","version":"2.4.*"}],"groups":[],"version":"0.0.0"}],
 		"changes":[{"name":"test1","changed":false}], "errors":[]}`},
-		{"GET", "/api/v0/blueprints/info/test2", ``, http.StatusOK, `{"blueprints":[{"name":"test2","description":"Test","modules":[],"packages":[{"name":"systemd","version":"123"}],"groups":[],"version":"0.0.0"}],
+		{"GET", "/api/v0/blueprints/info/test2", ``, http.StatusOK, `{"blueprints":[{"name":"test2","description":"Test","distro":"","modules":[],"packages":[{"name":"systemd","version":"123"}],"groups":[],"version":"0.0.0"}],
 		"changes":[{"name":"test2","changed":true}], "errors":[]}`},
 		{"GET", "/api/v0/blueprints/info/test3-non", ``, http.StatusOK, `{"blueprints":[],"changes":[],"errors":[{"id":"UnknownBlueprint","msg":"test3-non: "}]}`},
 	}
@@ -316,6 +335,7 @@ func TestBlueprintsInfoToml(t *testing.T) {
 		Name:        "test1",
 		Description: "Test",
 		Version:     "0.0.0",
+		Distro:      "",
 		Packages: []blueprint.Package{
 			{
 				Name:    "httpd",
@@ -394,7 +414,7 @@ func TestBlueprintsFreeze(t *testing.T) {
 			ExpectedStatus int
 			ExpectedJSON   string
 		}{
-			{rpmmd_mock.BaseFixture, "/api/v0/blueprints/freeze/test,test2", http.StatusOK, `{"blueprints":[{"blueprint":{"name":"test","description":"Test","version":"0.0.1","packages":[{"name":"dep-package1","version":"1.33-2.fc30.x86_64"},{"name":"dep-package3","version":"7:3.0.3-1.fc30.x86_64"}],"modules":[{"name":"dep-package2","version":"2.9-1.fc30.x86_64"}],"groups":[]}},{"blueprint":{"name":"test2","description":"Test","version":"0.0.0","packages":[{"name":"dep-package1","version":"1.33-2.fc30.x86_64"},{"name":"dep-package3","version":"7:3.0.3-1.fc30.x86_64"}],"modules":[{"name":"dep-package2","version":"2.9-1.fc30.x86_64"}],"groups":[]}}],"errors":[]}`},
+			{rpmmd_mock.BaseFixture, "/api/v0/blueprints/freeze/test,test2", http.StatusOK, `{"blueprints":[{"blueprint":{"name":"test","description":"Test","distro":"","version":"0.0.1","packages":[{"name":"dep-package1","version":"1.33-2.fc30.x86_64"},{"name":"dep-package3","version":"7:3.0.3-1.fc30.x86_64"}],"modules":[{"name":"dep-package2","version":"2.9-1.fc30.x86_64"}],"groups":[]}},{"blueprint":{"name":"test2","description":"Test","distro":"","version":"0.0.0","packages":[{"name":"dep-package1","version":"1.33-2.fc30.x86_64"},{"name":"dep-package3","version":"7:3.0.3-1.fc30.x86_64"}],"modules":[{"name":"dep-package2","version":"2.9-1.fc30.x86_64"}],"groups":[]}}],"errors":[]}`},
 		}
 
 		tempdir, err := ioutil.TempDir("", "weldr-tests-")
@@ -416,7 +436,7 @@ func TestBlueprintsFreeze(t *testing.T) {
 			ExpectedStatus int
 			ExpectedTOML   string
 		}{
-			{rpmmd_mock.BaseFixture, "/api/v0/blueprints/freeze/test?format=toml", http.StatusOK, "name=\"test\"\n description=\"Test\"\n version=\"0.0.1\"\n groups = []\n [[packages]]\n name=\"dep-package1\"\n version=\"1.33-2.fc30.x86_64\"\n [[packages]]\n name=\"dep-package3\"\n version=\"7:3.0.3-1.fc30.x86_64\"\n [[modules]]\n name=\"dep-package2\"\n version=\"2.9-1.fc30.x86_64\""},
+			{rpmmd_mock.BaseFixture, "/api/v0/blueprints/freeze/test?format=toml", http.StatusOK, "name=\"test\"\n description=\"Test\"\n distro=\"\"\n version=\"0.0.1\"\n groups = []\n [[packages]]\n name=\"dep-package1\"\n version=\"1.33-2.fc30.x86_64\"\n [[packages]]\n name=\"dep-package3\"\n version=\"7:3.0.3-1.fc30.x86_64\"\n [[modules]]\n name=\"dep-package2\"\n version=\"2.9-1.fc30.x86_64\""},
 			{rpmmd_mock.BaseFixture, "/api/v0/blueprints/freeze/missing?format=toml", http.StatusOK, ""},
 		}
 
@@ -527,9 +547,9 @@ func TestBlueprintsDepsolve(t *testing.T) {
 		ExpectedStatus int
 		ExpectedJSON   string
 	}{
-		{rpmmd_mock.BaseFixture, http.StatusOK, `{"blueprints":[{"blueprint":{"name":"test","description":"Test","version":"0.0.1","packages":[{"name":"dep-package1","version":"*"}],"groups":[],"modules":[{"name":"dep-package3","version":"*"}]},"dependencies":[{"name":"dep-package3","epoch":7,"version":"3.0.3","release":"1.fc30","arch":"x86_64"},{"name":"dep-package1","epoch":0,"version":"1.33","release":"2.fc30","arch":"x86_64"},{"name":"dep-package2","epoch":0,"version":"2.9","release":"1.fc30","arch":"x86_64"}]}],"errors":[]}`},
-		{rpmmd_mock.NonExistingPackage, http.StatusOK, `{"blueprints":[{"blueprint":{"name":"test","description":"Test","version":"0.0.1","packages":[{"name":"dep-package1","version":"*"}],"groups":[],"modules":[{"name":"dep-package3","version":"*"}]},"dependencies":[]}],"errors":[{"id":"BlueprintsError","msg":"test: DNF error occured: MarkingErrors: Error occurred when marking packages for installation: Problems in request:\nmissing packages: fash"}]}`},
-		{rpmmd_mock.BadDepsolve, http.StatusOK, `{"blueprints":[{"blueprint":{"name":"test","description":"Test","version":"0.0.1","packages":[{"name":"dep-package1","version":"*"}],"groups":[],"modules":[{"name":"dep-package3","version":"*"}]},"dependencies":[]}],"errors":[{"id":"BlueprintsError","msg":"test: DNF error occured: DepsolveError: There was a problem depsolving ['go2rpm']: \n Problem: conflicting requests\n  - nothing provides askalono-cli needed by go2rpm-1-4.fc31.noarch"}]}`},
+		{rpmmd_mock.BaseFixture, http.StatusOK, `{"blueprints":[{"blueprint":{"name":"test","description":"Test","distro":"","version":"0.0.1","packages":[{"name":"dep-package1","version":"*"}],"groups":[],"modules":[{"name":"dep-package3","version":"*"}]},"dependencies":[{"name":"dep-package3","epoch":7,"version":"3.0.3","release":"1.fc30","arch":"x86_64"},{"name":"dep-package1","epoch":0,"version":"1.33","release":"2.fc30","arch":"x86_64"},{"name":"dep-package2","epoch":0,"version":"2.9","release":"1.fc30","arch":"x86_64"}]}],"errors":[]}`},
+		{rpmmd_mock.NonExistingPackage, http.StatusOK, `{"blueprints":[{"blueprint":{"name":"test","description":"Test","distro":"","version":"0.0.1","packages":[{"name":"dep-package1","version":"*"}],"groups":[],"modules":[{"name":"dep-package3","version":"*"}]},"dependencies":[]}],"errors":[{"id":"BlueprintsError","msg":"test: DNF error occured: MarkingErrors: Error occurred when marking packages for installation: Problems in request:\nmissing packages: fash"}]}`},
+		{rpmmd_mock.BadDepsolve, http.StatusOK, `{"blueprints":[{"blueprint":{"name":"test","description":"Test","distro":"","version":"0.0.1","packages":[{"name":"dep-package1","version":"*"}],"groups":[],"modules":[{"name":"dep-package3","version":"*"}]},"dependencies":[]}],"errors":[{"id":"BlueprintsError","msg":"test: DNF error occured: DepsolveError: There was a problem depsolving ['go2rpm']: \n Problem: conflicting requests\n  - nothing provides askalono-cli needed by go2rpm-1-4.fc31.noarch"}]}`},
 	}
 
 	tempdir, err := ioutil.TempDir("", "weldr-tests-")
@@ -671,6 +691,32 @@ func TestCompose(t *testing.T) {
 		Packages: []rpmmd.PackageSpec{},
 	}
 
+	// For 2nd distribution
+	arch2, err := test_distro.New2().GetArch(test_distro.TestArchName)
+	require.NoError(t, err)
+	imgType2, err := arch2.GetImageType(test_distro.TestImageTypeName)
+	require.NoError(t, err)
+	manifest2, err := imgType.Manifest(nil, distro.ImageOptions{}, nil, nil, 0)
+	require.NoError(t, err)
+
+	expectedComposeGoodDistro := &store.Compose{
+		Blueprint: &blueprint.Blueprint{
+			Name:           "test-distro-2",
+			Version:        "0.0.0",
+			Packages:       []blueprint.Package{},
+			Modules:        []blueprint.Package{},
+			Groups:         []blueprint.Group{},
+			Customizations: nil,
+			Distro:         "test-distro-2",
+		},
+		ImageBuild: store.ImageBuild{
+			QueueStatus: common.IBWaiting,
+			ImageType:   imgType2,
+			Manifest:    manifest2,
+		},
+		Packages: []rpmmd.PackageSpec{},
+	}
+
 	var cases = []struct {
 		External        bool
 		Method          string
@@ -688,6 +734,8 @@ func TestCompose(t *testing.T) {
 		{false, "POST", "/api/v1/compose?test=2", fmt.Sprintf(`{"blueprint_name": "test","compose_type":"%s","branch":"master","ostree":{"ref":"refid","parent":"","url":"http://ostree/"}}`, test_distro.TestImageTypeName), http.StatusOK, `{"status": true}`, expectedComposeOSTreeURL, []string{"build_id"}},
 		{false, "POST", "/api/v1/compose", fmt.Sprintf(`{"blueprint_name": "test","compose_type":"%s","branch":"master","ostree":{"ref":"refid","parent":"","url":"invalid-url"}}`, test_distro.TestImageTypeName), http.StatusBadRequest, `{"status":false,"errors":[{"id":"OSTreeCommitError","msg":"Get \"invalid-url/refs/heads/refid\": unsupported protocol scheme \"\""}]}`, nil, []string{"build_id"}},
 		{false, "POST", "/api/v1/compose", fmt.Sprintf(`{"blueprint_name": "test","compose_type":"%s","branch":"master","ostree":{"ref":"/bad/ref","parent":"","url":"http://ostree/"}}`, test_distro.TestImageTypeName), http.StatusBadRequest, `{"status":false,"errors":[{"id":"InvalidChars","msg":"Invalid ostree ref"}]}`, expectedComposeOSTreeURL, []string{"build_id"}},
+		{false, "POST", "/api/v1/compose", fmt.Sprintf(`{"blueprint_name": "test-distro-2","compose_type": "%s","branch": "master"}`, test_distro.TestImageTypeName), http.StatusOK, `{"status": true}`, expectedComposeGoodDistro, []string{"build_id"}},
+		{false, "POST", "/api/v1/compose", fmt.Sprintf(`{"blueprint_name": "test-fedora-1","compose_type": "%s","branch": "master"}`, test_distro.TestImageTypeName), http.StatusBadRequest, `{"status": false,"errors":[{"id":"DistroError", "msg":"Unknown distribution: fedora-1"}]}`, nil, []string{"build_id"}},
 	}
 
 	tempdir, err := ioutil.TempDir("", "weldr-tests-")
@@ -796,8 +844,8 @@ func TestComposeInfo(t *testing.T) {
 		ExpectedStatus int
 		ExpectedJSON   string
 	}{
-		{rpmmd_mock.BaseFixture, "GET", "/api/v0/compose/info/30000000-0000-0000-0000-000000000000", ``, http.StatusOK, fmt.Sprintf(`{"id":"30000000-0000-0000-0000-000000000000","config":"","blueprint":{"name":"test","description":"","version":"0.0.0","packages":[],"modules":[],"groups":[]},"commit":"","deps":{"packages":[]},"compose_type":"%s","queue_status":"WAITING","image_size":0}`, test_distro.TestImageTypeName)},
-		{rpmmd_mock.BaseFixture, "GET", "/api/v1/compose/info/30000000-0000-0000-0000-000000000000", ``, http.StatusOK, fmt.Sprintf(`{"id":"30000000-0000-0000-0000-000000000000","config":"","blueprint":{"name":"test","description":"","version":"0.0.0","packages":[],"modules":[],"groups":[]},"commit":"","deps":{"packages":[]},"compose_type":"%s","queue_status":"WAITING","image_size":0,"uploads":[{"uuid":"10000000-0000-0000-0000-000000000000","status":"WAITING","provider_name":"aws","image_name":"awsimage","creation_time":1574857140,"settings":{"region":"frankfurt","bucket":"clay","key":"imagekey"}}]}`, test_distro.TestImageTypeName)},
+		{rpmmd_mock.BaseFixture, "GET", "/api/v0/compose/info/30000000-0000-0000-0000-000000000000", ``, http.StatusOK, fmt.Sprintf(`{"id":"30000000-0000-0000-0000-000000000000","config":"","blueprint":{"name":"test","description":"","distro":"","version":"0.0.0","packages":[],"modules":[],"groups":[]},"commit":"","deps":{"packages":[]},"compose_type":"%s","queue_status":"WAITING","image_size":0}`, test_distro.TestImageTypeName)},
+		{rpmmd_mock.BaseFixture, "GET", "/api/v1/compose/info/30000000-0000-0000-0000-000000000000", ``, http.StatusOK, fmt.Sprintf(`{"id":"30000000-0000-0000-0000-000000000000","config":"","blueprint":{"name":"test","description":"","distro":"","version":"0.0.0","packages":[],"modules":[],"groups":[]},"commit":"","deps":{"packages":[]},"compose_type":"%s","queue_status":"WAITING","image_size":0,"uploads":[{"uuid":"10000000-0000-0000-0000-000000000000","status":"WAITING","provider_name":"aws","image_name":"awsimage","creation_time":1574857140,"settings":{"region":"frankfurt","bucket":"clay","key":"imagekey"}}]}`, test_distro.TestImageTypeName)},
 		{rpmmd_mock.BaseFixture, "GET", "/api/v1/compose/info/30000000-0000-0000-0000", ``, http.StatusBadRequest, `{"status":false,"errors":[{"id":"UnknownUUID","msg":"30000000-0000-0000-0000 is not a valid build uuid"}]}`},
 		{rpmmd_mock.BaseFixture, "GET", "/api/v1/compose/info/42000000-0000-0000-0000-000000000000", ``, http.StatusBadRequest, `{"status":false,"errors":[{"id":"UnknownUUID","msg":"42000000-0000-0000-0000-000000000000 is not a valid build uuid"}]}`},
 	}
@@ -1016,6 +1064,8 @@ func TestSourcesNew(t *testing.T) {
 		{"POST", "/api/v0/projects/source/new", `{"name": "fish", "url": "https://download.opensuse.org/repositories/shells:/fish:/release:/3/Fedora_29/","check_ssl": false,"check_gpg": false}`, http.StatusBadRequest, `{"errors": [{"id": "ProjectsError","msg": "Problem parsing POST body: 'type' field is missing from request"}],"status":false}`},
 		{"POST", "/api/v0/projects/source/new", `{"name": "test-id", "url": "https://download.opensuse.org/repositories/shells:/fish:/release:/3/Fedora_29/","type": "yum-baseurl","check_ssl": false,"check_gpg": false}`, http.StatusBadRequest, `{"errors": [{"id": "SystemSource","msg": "test-id is a system source, it cannot be changed."}],"status":false}`},
 		{"POST", "/api/v1/projects/source/new", `{"id": "test-id", "name": "test system repo", "url": "https://download.opensuse.org/repositories/shells:/fish:/release:/3/Fedora_29/","type": "yum-baseurl","check_ssl": false,"check_gpg": false}`, http.StatusBadRequest, `{"errors": [{"id": "SystemSource","msg": "test-id is a system source, it cannot be changed."}],"status":false}`},
+		{"POST", "/api/v1/projects/source/new", `{"id": "fish","name":"fish repo","url": "https://download.opensuse.org/repositories/shells:/fish:/release:/3/Fedora_29/","type": "yum-baseurl","check_ssl": false,"check_gpg": false,"distros":["test-distro", "test-distro-2"]}`, http.StatusOK, `{"status":true}`},
+		{"POST", "/api/v1/projects/source/new", `{"id": "fish","name":"fish repo","url": "https://download.opensuse.org/repositories/shells:/fish:/release:/3/Fedora_29/","type": "yum-baseurl","check_ssl": false,"check_gpg": false,"distros":["fedora-1"]}`, http.StatusBadRequest, `{"status":false, "errors":[{"id":"ProjectsError", "msg":"Invalid distributions: fedora-1"}]}`},
 	}
 
 	tempdir, err := ioutil.TempDir("", "weldr-tests-")
@@ -1241,9 +1291,11 @@ func TestProjectsDepsolve(t *testing.T) {
 		ExpectedStatus int
 		ExpectedJSON   string
 	}{
-		{rpmmd_mock.NonExistingPackage, "/api/v0/projects/depsolve/fash", http.StatusBadRequest, `{"status":false,"errors":[{"id":"PROJECTS_ERROR","msg":"BadRequest: DNF error occured: MarkingErrors: Error occurred when marking packages for installation: Problems in request:\nmissing packages: fash"}]}`},
+		{rpmmd_mock.NonExistingPackage, "/api/v0/projects/depsolve/fash", http.StatusBadRequest, `{"status":false,"errors":[{"id":"ProjectsError","msg":"BadRequest: DNF error occured: MarkingErrors: Error occurred when marking packages for installation: Problems in request:\nmissing packages: fash"}]}`},
 		{rpmmd_mock.BaseFixture, "/api/v0/projects/depsolve/fish", http.StatusOK, `{"projects":[{"name":"dep-package3","epoch":7,"version":"3.0.3","release":"1.fc30","arch":"x86_64"},{"name":"dep-package1","epoch":0,"version":"1.33","release":"2.fc30","arch":"x86_64"},{"name":"dep-package2","epoch":0,"version":"2.9","release":"1.fc30","arch":"x86_64"}]}`},
-		{rpmmd_mock.BadDepsolve, "/api/v0/projects/depsolve/go2rpm", http.StatusBadRequest, `{"status":false,"errors":[{"id":"PROJECTS_ERROR","msg":"BadRequest: DNF error occured: DepsolveError: There was a problem depsolving ['go2rpm']: \n Problem: conflicting requests\n  - nothing provides askalono-cli needed by go2rpm-1-4.fc31.noarch"}]}`},
+		{rpmmd_mock.BaseFixture, "/api/v0/projects/depsolve/fish?distro=test-distro-2", http.StatusOK, `{"projects":[{"name":"dep-package3","epoch":7,"version":"3.0.3","release":"1.fc30","arch":"x86_64"},{"name":"dep-package1","epoch":0,"version":"1.33","release":"2.fc30","arch":"x86_64"},{"name":"dep-package2","epoch":0,"version":"2.9","release":"1.fc30","arch":"x86_64"}]}`},
+		{rpmmd_mock.BadDepsolve, "/api/v0/projects/depsolve/go2rpm", http.StatusBadRequest, `{"status":false,"errors":[{"id":"ProjectsError","msg":"BadRequest: DNF error occured: DepsolveError: There was a problem depsolving ['go2rpm']: \n Problem: conflicting requests\n  - nothing provides askalono-cli needed by go2rpm-1-4.fc31.noarch"}]}`},
+		{rpmmd_mock.BaseFixture, "/api/v0/projects/depsolve/fish?distro=fedora-1", http.StatusBadRequest, `{"status":false,"errors":[{"id":"DistroError","msg":"Invalid distro: fedora-1"}]}`},
 	}
 
 	tempdir, err := ioutil.TempDir("", "weldr-tests-")
@@ -1269,6 +1321,8 @@ func TestProjectsInfo(t *testing.T) {
 		{rpmmd_mock.BaseFixture, "/api/v0/projects/info/*", http.StatusOK, `{"projects":[{"name":"package0","summary":"pkg0 sum","description":"pkg0 desc","homepage":"https://pkg0.example.com","upstream_vcs":"UPSTREAM_VCS","builds":[{"arch":"x86_64","build_time":"2006-01-03T15:04:05","epoch":0,"release":"0.fc30","changelog":"CHANGELOG_NEEDED","build_config_ref":"BUILD_CONFIG_REF","build_env_ref":"BUILD_ENV_REF","metadata":{},"source":{"license":"MIT","version":"0.1","source_ref":"SOURCE_REF","metadata":{}}},{"arch":"x86_64","build_time":"2006-01-02T15:04:05","epoch":0,"release":"0.fc30","changelog":"CHANGELOG_NEEDED","build_config_ref":"BUILD_CONFIG_REF","build_env_ref":"BUILD_ENV_REF","metadata":{},"source":{"license":"MIT","version":"0.0","source_ref":"SOURCE_REF","metadata":{}}}]},{"name":"package1","summary":"pkg1 sum","description":"pkg1 desc","homepage":"https://pkg1.example.com","upstream_vcs":"UPSTREAM_VCS","builds":[{"arch":"x86_64","build_time":"2006-02-02T15:04:05","epoch":0,"release":"1.fc30","changelog":"CHANGELOG_NEEDED","build_config_ref":"BUILD_CONFIG_REF","build_env_ref":"BUILD_ENV_REF","metadata":{},"source":{"license":"MIT","version":"1.0","source_ref":"SOURCE_REF","metadata":{}}},{"arch":"x86_64","build_time":"2006-02-03T15:04:05","epoch":0,"release":"1.fc30","changelog":"CHANGELOG_NEEDED","build_config_ref":"BUILD_CONFIG_REF","build_env_ref":"BUILD_ENV_REF","metadata":{},"source":{"license":"MIT","version":"1.1","source_ref":"SOURCE_REF","metadata":{}}}]},{"name":"package10","summary":"pkg10 sum","description":"pkg10 desc","homepage":"https://pkg10.example.com","upstream_vcs":"UPSTREAM_VCS","builds":[{"arch":"x86_64","build_time":"2006-11-02T15:04:05","epoch":0,"release":"10.fc30","changelog":"CHANGELOG_NEEDED","build_config_ref":"BUILD_CONFIG_REF","build_env_ref":"BUILD_ENV_REF","metadata":{},"source":{"license":"MIT","version":"10.0","source_ref":"SOURCE_REF","metadata":{}}},{"arch":"x86_64","build_time":"2006-11-03T15:04:05","epoch":0,"release":"10.fc30","changelog":"CHANGELOG_NEEDED","build_config_ref":"BUILD_CONFIG_REF","build_env_ref":"BUILD_ENV_REF","metadata":{},"source":{"license":"MIT","version":"10.1","source_ref":"SOURCE_REF","metadata":{}}}]},{"name":"package11","summary":"pkg11 sum","description":"pkg11 desc","homepage":"https://pkg11.example.com","upstream_vcs":"UPSTREAM_VCS","builds":[{"arch":"x86_64","build_time":"2006-12-03T15:04:05","epoch":0,"release":"11.fc30","changelog":"CHANGELOG_NEEDED","build_config_ref":"BUILD_CONFIG_REF","build_env_ref":"BUILD_ENV_REF","metadata":{},"source":{"license":"MIT","version":"11.1","source_ref":"SOURCE_REF","metadata":{}}},{"arch":"x86_64","build_time":"2006-12-02T15:04:05","epoch":0,"release":"11.fc30","changelog":"CHANGELOG_NEEDED","build_config_ref":"BUILD_CONFIG_REF","build_env_ref":"BUILD_ENV_REF","metadata":{},"source":{"license":"MIT","version":"11.0","source_ref":"SOURCE_REF","metadata":{}}}]},{"name":"package12","summary":"pkg12 sum","description":"pkg12 desc","homepage":"https://pkg12.example.com","upstream_vcs":"UPSTREAM_VCS","builds":[{"arch":"x86_64","build_time":"2007-01-02T15:04:05","epoch":0,"release":"12.fc30","changelog":"CHANGELOG_NEEDED","build_config_ref":"BUILD_CONFIG_REF","build_env_ref":"BUILD_ENV_REF","metadata":{},"source":{"license":"MIT","version":"12.0","source_ref":"SOURCE_REF","metadata":{}}},{"arch":"x86_64","build_time":"2007-01-03T15:04:05","epoch":0,"release":"12.fc30","changelog":"CHANGELOG_NEEDED","build_config_ref":"BUILD_CONFIG_REF","build_env_ref":"BUILD_ENV_REF","metadata":{},"source":{"license":"MIT","version":"12.1","source_ref":"SOURCE_REF","metadata":{}}}]},{"name":"package13","summary":"pkg13 sum","description":"pkg13 desc","homepage":"https://pkg13.example.com","upstream_vcs":"UPSTREAM_VCS","builds":[{"arch":"x86_64","build_time":"2007-02-02T15:04:05","epoch":0,"release":"13.fc30","changelog":"CHANGELOG_NEEDED","build_config_ref":"BUILD_CONFIG_REF","build_env_ref":"BUILD_ENV_REF","metadata":{},"source":{"license":"MIT","version":"13.0","source_ref":"SOURCE_REF","metadata":{}}},{"arch":"x86_64","build_time":"2007-02-03T15:04:05","epoch":0,"release":"13.fc30","changelog":"CHANGELOG_NEEDED","build_config_ref":"BUILD_CONFIG_REF","build_env_ref":"BUILD_ENV_REF","metadata":{},"source":{"license":"MIT","version":"13.1","source_ref":"SOURCE_REF","metadata":{}}}]},{"name":"package14","summary":"pkg14 sum","description":"pkg14 desc","homepage":"https://pkg14.example.com","upstream_vcs":"UPSTREAM_VCS","builds":[{"arch":"x86_64","build_time":"2007-03-03T15:04:05","epoch":0,"release":"14.fc30","changelog":"CHANGELOG_NEEDED","build_config_ref":"BUILD_CONFIG_REF","build_env_ref":"BUILD_ENV_REF","metadata":{},"source":{"license":"MIT","version":"14.1","source_ref":"SOURCE_REF","metadata":{}}},{"arch":"x86_64","build_time":"2007-03-02T15:04:05","epoch":0,"release":"14.fc30","changelog":"CHANGELOG_NEEDED","build_config_ref":"BUILD_CONFIG_REF","build_env_ref":"BUILD_ENV_REF","metadata":{},"source":{"license":"MIT","version":"14.0","source_ref":"SOURCE_REF","metadata":{}}}]},{"name":"package15","summary":"pkg15 sum","description":"pkg15 desc","homepage":"https://pkg15.example.com","upstream_vcs":"UPSTREAM_VCS","builds":[{"arch":"x86_64","build_time":"2007-04-03T15:04:05","epoch":0,"release":"15.fc30","changelog":"CHANGELOG_NEEDED","build_config_ref":"BUILD_CONFIG_REF","build_env_ref":"BUILD_ENV_REF","metadata":{},"source":{"license":"MIT","version":"15.1","source_ref":"SOURCE_REF","metadata":{}}},{"arch":"x86_64","build_time":"2007-04-02T15:04:05","epoch":0,"release":"15.fc30","changelog":"CHANGELOG_NEEDED","build_config_ref":"BUILD_CONFIG_REF","build_env_ref":"BUILD_ENV_REF","metadata":{},"source":{"license":"MIT","version":"15.0","source_ref":"SOURCE_REF","metadata":{}}}]},{"name":"package16","summary":"pkg16 sum","description":"pkg16 desc","homepage":"https://pkg16.example.com","upstream_vcs":"UPSTREAM_VCS","builds":[{"arch":"x86_64","build_time":"2007-05-02T15:04:05","epoch":0,"release":"16.fc30","changelog":"CHANGELOG_NEEDED","build_config_ref":"BUILD_CONFIG_REF","build_env_ref":"BUILD_ENV_REF","metadata":{},"source":{"license":"MIT","version":"16.0","source_ref":"SOURCE_REF","metadata":{}}},{"arch":"x86_64","build_time":"2007-05-03T15:04:05","epoch":0,"release":"16.fc30","changelog":"CHANGELOG_NEEDED","build_config_ref":"BUILD_CONFIG_REF","build_env_ref":"BUILD_ENV_REF","metadata":{},"source":{"license":"MIT","version":"16.1","source_ref":"SOURCE_REF","metadata":{}}}]},{"name":"package17","summary":"pkg17 sum","description":"pkg17 desc","homepage":"https://pkg17.example.com","upstream_vcs":"UPSTREAM_VCS","builds":[{"arch":"x86_64","build_time":"2007-06-03T15:04:05","epoch":0,"release":"17.fc30","changelog":"CHANGELOG_NEEDED","build_config_ref":"BUILD_CONFIG_REF","build_env_ref":"BUILD_ENV_REF","metadata":{},"source":{"license":"MIT","version":"17.1","source_ref":"SOURCE_REF","metadata":{}}},{"arch":"x86_64","build_time":"2007-06-02T15:04:05","epoch":0,"release":"17.fc30","changelog":"CHANGELOG_NEEDED","build_config_ref":"BUILD_CONFIG_REF","build_env_ref":"BUILD_ENV_REF","metadata":{},"source":{"license":"MIT","version":"17.0","source_ref":"SOURCE_REF","metadata":{}}}]},{"name":"package18","summary":"pkg18 sum","description":"pkg18 desc","homepage":"https://pkg18.example.com","upstream_vcs":"UPSTREAM_VCS","builds":[{"arch":"x86_64","build_time":"2007-07-02T15:04:05","epoch":0,"release":"18.fc30","changelog":"CHANGELOG_NEEDED","build_config_ref":"BUILD_CONFIG_REF","build_env_ref":"BUILD_ENV_REF","metadata":{},"source":{"license":"MIT","version":"18.0","source_ref":"SOURCE_REF","metadata":{}}},{"arch":"x86_64","build_time":"2007-07-03T15:04:05","epoch":0,"release":"18.fc30","changelog":"CHANGELOG_NEEDED","build_config_ref":"BUILD_CONFIG_REF","build_env_ref":"BUILD_ENV_REF","metadata":{},"source":{"license":"MIT","version":"18.1","source_ref":"SOURCE_REF","metadata":{}}}]},{"name":"package19","summary":"pkg19 sum","description":"pkg19 desc","homepage":"https://pkg19.example.com","upstream_vcs":"UPSTREAM_VCS","builds":[{"arch":"x86_64","build_time":"2007-08-03T15:04:05","epoch":0,"release":"19.fc30","changelog":"CHANGELOG_NEEDED","build_config_ref":"BUILD_CONFIG_REF","build_env_ref":"BUILD_ENV_REF","metadata":{},"source":{"license":"MIT","version":"19.1","source_ref":"SOURCE_REF","metadata":{}}},{"arch":"x86_64","build_time":"2007-08-02T15:04:05","epoch":0,"release":"19.fc30","changelog":"CHANGELOG_NEEDED","build_config_ref":"BUILD_CONFIG_REF","build_env_ref":"BUILD_ENV_REF","metadata":{},"source":{"license":"MIT","version":"19.0","source_ref":"SOURCE_REF","metadata":{}}}]},{"name":"package2","summary":"pkg2 sum","description":"pkg2 desc","homepage":"https://pkg2.example.com","upstream_vcs":"UPSTREAM_VCS","builds":[{"arch":"x86_64","build_time":"2006-03-02T15:04:05","epoch":0,"release":"2.fc30","changelog":"CHANGELOG_NEEDED","build_config_ref":"BUILD_CONFIG_REF","build_env_ref":"BUILD_ENV_REF","metadata":{},"source":{"license":"MIT","version":"2.0","source_ref":"SOURCE_REF","metadata":{}}},{"arch":"x86_64","build_time":"2006-03-03T15:04:05","epoch":0,"release":"2.fc30","changelog":"CHANGELOG_NEEDED","build_config_ref":"BUILD_CONFIG_REF","build_env_ref":"BUILD_ENV_REF","metadata":{},"source":{"license":"MIT","version":"2.1","source_ref":"SOURCE_REF","metadata":{}}}]},{"name":"package20","summary":"pkg20 sum","description":"pkg20 desc","homepage":"https://pkg20.example.com","upstream_vcs":"UPSTREAM_VCS","builds":[{"arch":"x86_64","build_time":"2007-09-03T15:04:05","epoch":0,"release":"20.fc30","changelog":"CHANGELOG_NEEDED","build_config_ref":"BUILD_CONFIG_REF","build_env_ref":"BUILD_ENV_REF","metadata":{},"source":{"license":"MIT","version":"20.1","source_ref":"SOURCE_REF","metadata":{}}},{"arch":"x86_64","build_time":"2007-09-02T15:04:05","epoch":0,"release":"20.fc30","changelog":"CHANGELOG_NEEDED","build_config_ref":"BUILD_CONFIG_REF","build_env_ref":"BUILD_ENV_REF","metadata":{},"source":{"license":"MIT","version":"20.0","source_ref":"SOURCE_REF","metadata":{}}}]},{"name":"package21","summary":"pkg21 sum","description":"pkg21 desc","homepage":"https://pkg21.example.com","upstream_vcs":"UPSTREAM_VCS","builds":[{"arch":"x86_64","build_time":"2007-10-02T15:04:05","epoch":0,"release":"21.fc30","changelog":"CHANGELOG_NEEDED","build_config_ref":"BUILD_CONFIG_REF","build_env_ref":"BUILD_ENV_REF","metadata":{},"source":{"license":"MIT","version":"21.0","source_ref":"SOURCE_REF","metadata":{}}},{"arch":"x86_64","build_time":"2007-10-03T15:04:05","epoch":0,"release":"21.fc30","changelog":"CHANGELOG_NEEDED","build_config_ref":"BUILD_CONFIG_REF","build_env_ref":"BUILD_ENV_REF","metadata":{},"source":{"license":"MIT","version":"21.1","source_ref":"SOURCE_REF","metadata":{}}}]},{"name":"package3","summary":"pkg3 sum","description":"pkg3 desc","homepage":"https://pkg3.example.com","upstream_vcs":"UPSTREAM_VCS","builds":[{"arch":"x86_64","build_time":"2006-04-03T15:04:05","epoch":0,"release":"3.fc30","changelog":"CHANGELOG_NEEDED","build_config_ref":"BUILD_CONFIG_REF","build_env_ref":"BUILD_ENV_REF","metadata":{},"source":{"license":"MIT","version":"3.1","source_ref":"SOURCE_REF","metadata":{}}},{"arch":"x86_64","build_time":"2006-04-02T15:04:05","epoch":0,"release":"3.fc30","changelog":"CHANGELOG_NEEDED","build_config_ref":"BUILD_CONFIG_REF","build_env_ref":"BUILD_ENV_REF","metadata":{},"source":{"license":"MIT","version":"3.0","source_ref":"SOURCE_REF","metadata":{}}}]},{"name":"package4","summary":"pkg4 sum","description":"pkg4 desc","homepage":"https://pkg4.example.com","upstream_vcs":"UPSTREAM_VCS","builds":[{"arch":"x86_64","build_time":"2006-05-03T15:04:05","epoch":0,"release":"4.fc30","changelog":"CHANGELOG_NEEDED","build_config_ref":"BUILD_CONFIG_REF","build_env_ref":"BUILD_ENV_REF","metadata":{},"source":{"license":"MIT","version":"4.1","source_ref":"SOURCE_REF","metadata":{}}},{"arch":"x86_64","build_time":"2006-05-02T15:04:05","epoch":0,"release":"4.fc30","changelog":"CHANGELOG_NEEDED","build_config_ref":"BUILD_CONFIG_REF","build_env_ref":"BUILD_ENV_REF","metadata":{},"source":{"license":"MIT","version":"4.0","source_ref":"SOURCE_REF","metadata":{}}}]},{"name":"package5","summary":"pkg5 sum","description":"pkg5 desc","homepage":"https://pkg5.example.com","upstream_vcs":"UPSTREAM_VCS","builds":[{"arch":"x86_64","build_time":"2006-06-03T15:04:05","epoch":0,"release":"5.fc30","changelog":"CHANGELOG_NEEDED","build_config_ref":"BUILD_CONFIG_REF","build_env_ref":"BUILD_ENV_REF","metadata":{},"source":{"license":"MIT","version":"5.1","source_ref":"SOURCE_REF","metadata":{}}},{"arch":"x86_64","build_time":"2006-06-02T15:04:05","epoch":0,"release":"5.fc30","changelog":"CHANGELOG_NEEDED","build_config_ref":"BUILD_CONFIG_REF","build_env_ref":"BUILD_ENV_REF","metadata":{},"source":{"license":"MIT","version":"5.0","source_ref":"SOURCE_REF","metadata":{}}}]},{"name":"package6","summary":"pkg6 sum","description":"pkg6 desc","homepage":"https://pkg6.example.com","upstream_vcs":"UPSTREAM_VCS","builds":[{"arch":"x86_64","build_time":"2006-07-02T15:04:05","epoch":0,"release":"6.fc30","changelog":"CHANGELOG_NEEDED","build_config_ref":"BUILD_CONFIG_REF","build_env_ref":"BUILD_ENV_REF","metadata":{},"source":{"license":"MIT","version":"6.0","source_ref":"SOURCE_REF","metadata":{}}},{"arch":"x86_64","build_time":"2006-07-03T15:04:05","epoch":0,"release":"6.fc30","changelog":"CHANGELOG_NEEDED","build_config_ref":"BUILD_CONFIG_REF","build_env_ref":"BUILD_ENV_REF","metadata":{},"source":{"license":"MIT","version":"6.1","source_ref":"SOURCE_REF","metadata":{}}}]},{"name":"package7","summary":"pkg7 sum","description":"pkg7 desc","homepage":"https://pkg7.example.com","upstream_vcs":"UPSTREAM_VCS","builds":[{"arch":"x86_64","build_time":"2006-08-02T15:04:05","epoch":0,"release":"7.fc30","changelog":"CHANGELOG_NEEDED","build_config_ref":"BUILD_CONFIG_REF","build_env_ref":"BUILD_ENV_REF","metadata":{},"source":{"license":"MIT","version":"7.0","source_ref":"SOURCE_REF","metadata":{}}},{"arch":"x86_64","build_time":"2006-08-03T15:04:05","epoch":0,"release":"7.fc30","changelog":"CHANGELOG_NEEDED","build_config_ref":"BUILD_CONFIG_REF","build_env_ref":"BUILD_ENV_REF","metadata":{},"source":{"license":"MIT","version":"7.1","source_ref":"SOURCE_REF","metadata":{}}}]},{"name":"package8","summary":"pkg8 sum","description":"pkg8 desc","homepage":"https://pkg8.example.com","upstream_vcs":"UPSTREAM_VCS","builds":[{"arch":"x86_64","build_time":"2006-09-03T15:04:05","epoch":0,"release":"8.fc30","changelog":"CHANGELOG_NEEDED","build_config_ref":"BUILD_CONFIG_REF","build_env_ref":"BUILD_ENV_REF","metadata":{},"source":{"license":"MIT","version":"8.1","source_ref":"SOURCE_REF","metadata":{}}},{"arch":"x86_64","build_time":"2006-09-02T15:04:05","epoch":0,"release":"8.fc30","changelog":"CHANGELOG_NEEDED","build_config_ref":"BUILD_CONFIG_REF","build_env_ref":"BUILD_ENV_REF","metadata":{},"source":{"license":"MIT","version":"8.0","source_ref":"SOURCE_REF","metadata":{}}}]},{"name":"package9","summary":"pkg9 sum","description":"pkg9 desc","homepage":"https://pkg9.example.com","upstream_vcs":"UPSTREAM_VCS","builds":[{"arch":"x86_64","build_time":"2006-10-02T15:04:05","epoch":0,"release":"9.fc30","changelog":"CHANGELOG_NEEDED","build_config_ref":"BUILD_CONFIG_REF","build_env_ref":"BUILD_ENV_REF","metadata":{},"source":{"license":"MIT","version":"9.0","source_ref":"SOURCE_REF","metadata":{}}},{"arch":"x86_64","build_time":"2006-10-03T15:04:05","epoch":0,"release":"9.fc30","changelog":"CHANGELOG_NEEDED","build_config_ref":"BUILD_CONFIG_REF","build_env_ref":"BUILD_ENV_REF","metadata":{},"source":{"license":"MIT","version":"9.1","source_ref":"SOURCE_REF","metadata":{}}}]}]}`},
 		{rpmmd_mock.BaseFixture, "/api/v0/projects/info/package2*,package16", http.StatusOK, `{"projects":[{"name":"package16","summary":"pkg16 sum","description":"pkg16 desc","homepage":"https://pkg16.example.com","upstream_vcs":"UPSTREAM_VCS","builds":[{"arch":"x86_64","build_time":"2007-05-02T15:04:05","epoch":0,"release":"16.fc30","changelog":"CHANGELOG_NEEDED","build_config_ref":"BUILD_CONFIG_REF","build_env_ref":"BUILD_ENV_REF","metadata":{},"source":{"license":"MIT","version":"16.0","source_ref":"SOURCE_REF","metadata":{}}},{"arch":"x86_64","build_time":"2007-05-03T15:04:05","epoch":0,"release":"16.fc30","changelog":"CHANGELOG_NEEDED","build_config_ref":"BUILD_CONFIG_REF","build_env_ref":"BUILD_ENV_REF","metadata":{},"source":{"license":"MIT","version":"16.1","source_ref":"SOURCE_REF","metadata":{}}}]},{"name":"package2","summary":"pkg2 sum","description":"pkg2 desc","homepage":"https://pkg2.example.com","upstream_vcs":"UPSTREAM_VCS","builds":[{"arch":"x86_64","build_time":"2006-03-02T15:04:05","epoch":0,"release":"2.fc30","changelog":"CHANGELOG_NEEDED","build_config_ref":"BUILD_CONFIG_REF","build_env_ref":"BUILD_ENV_REF","metadata":{},"source":{"license":"MIT","version":"2.0","source_ref":"SOURCE_REF","metadata":{}}},{"arch":"x86_64","build_time":"2006-03-03T15:04:05","epoch":0,"release":"2.fc30","changelog":"CHANGELOG_NEEDED","build_config_ref":"BUILD_CONFIG_REF","build_env_ref":"BUILD_ENV_REF","metadata":{},"source":{"license":"MIT","version":"2.1","source_ref":"SOURCE_REF","metadata":{}}}]},{"name":"package20","summary":"pkg20 sum","description":"pkg20 desc","homepage":"https://pkg20.example.com","upstream_vcs":"UPSTREAM_VCS","builds":[{"arch":"x86_64","build_time":"2007-09-03T15:04:05","epoch":0,"release":"20.fc30","changelog":"CHANGELOG_NEEDED","build_config_ref":"BUILD_CONFIG_REF","build_env_ref":"BUILD_ENV_REF","metadata":{},"source":{"license":"MIT","version":"20.1","source_ref":"SOURCE_REF","metadata":{}}},{"arch":"x86_64","build_time":"2007-09-02T15:04:05","epoch":0,"release":"20.fc30","changelog":"CHANGELOG_NEEDED","build_config_ref":"BUILD_CONFIG_REF","build_env_ref":"BUILD_ENV_REF","metadata":{},"source":{"license":"MIT","version":"20.0","source_ref":"SOURCE_REF","metadata":{}}}]},{"name":"package21","summary":"pkg21 sum","description":"pkg21 desc","homepage":"https://pkg21.example.com","upstream_vcs":"UPSTREAM_VCS","builds":[{"arch":"x86_64","build_time":"2007-10-02T15:04:05","epoch":0,"release":"21.fc30","changelog":"CHANGELOG_NEEDED","build_config_ref":"BUILD_CONFIG_REF","build_env_ref":"BUILD_ENV_REF","metadata":{},"source":{"license":"MIT","version":"21.0","source_ref":"SOURCE_REF","metadata":{}}},{"arch":"x86_64","build_time":"2007-10-03T15:04:05","epoch":0,"release":"21.fc30","changelog":"CHANGELOG_NEEDED","build_config_ref":"BUILD_CONFIG_REF","build_env_ref":"BUILD_ENV_REF","metadata":{},"source":{"license":"MIT","version":"21.1","source_ref":"SOURCE_REF","metadata":{}}}]}]}`},
 		{rpmmd_mock.BadFetch, "/api/v0/projects/info/package2*,package16", http.StatusBadRequest, `{"status":false,"errors":[{"id":"ModulesError","msg":"msg: DNF error occured: FetchError: There was a problem when fetching packages."}]}`},
+		{rpmmd_mock.BaseFixture, "/api/v0/projects/info/package16?distro=test-distro-2", http.StatusOK, `{"projects":[{"name":"package16","summary":"pkg16 sum","description":"pkg16 desc","homepage":"https://pkg16.example.com","upstream_vcs":"UPSTREAM_VCS","builds":[{"arch":"x86_64","build_time":"2007-05-02T15:04:05","epoch":0,"release":"16.fc30","changelog":"CHANGELOG_NEEDED","build_config_ref":"BUILD_CONFIG_REF","build_env_ref":"BUILD_ENV_REF","metadata":{},"source":{"license":"MIT","version":"16.0","source_ref":"SOURCE_REF","metadata":{}}},{"arch":"x86_64","build_time":"2007-05-03T15:04:05","epoch":0,"release":"16.fc30","changelog":"CHANGELOG_NEEDED","build_config_ref":"BUILD_CONFIG_REF","build_env_ref":"BUILD_ENV_REF","metadata":{},"source":{"license":"MIT","version":"16.1","source_ref":"SOURCE_REF","metadata":{}}}]}]}`},
+		{rpmmd_mock.BaseFixture, "/api/v0/projects/info/package16?distro=fedora-1", http.StatusBadRequest, `{"status":false,"errors":[{"id":"DistroError","msg":"Invalid distro: fedora-1"}]}`},
 	}
 
 	tempdir, err := ioutil.TempDir("", "weldr-tests-")
@@ -1296,6 +1350,8 @@ func TestModulesInfo(t *testing.T) {
 		{rpmmd_mock.BaseFixture, "/api/v0/modules/info/*", http.StatusOK, `{"modules":[{"name":"package0","summary":"pkg0 sum","description":"pkg0 desc","homepage":"https://pkg0.example.com","upstream_vcs":"UPSTREAM_VCS","builds":[{"arch":"x86_64","build_time":"2006-01-03T15:04:05","epoch":0,"release":"0.fc30","changelog":"CHANGELOG_NEEDED","build_config_ref":"BUILD_CONFIG_REF","build_env_ref":"BUILD_ENV_REF","metadata":{},"source":{"license":"MIT","version":"0.1","source_ref":"SOURCE_REF","metadata":{}}},{"arch":"x86_64","build_time":"2006-01-02T15:04:05","epoch":0,"release":"0.fc30","changelog":"CHANGELOG_NEEDED","build_config_ref":"BUILD_CONFIG_REF","build_env_ref":"BUILD_ENV_REF","metadata":{},"source":{"license":"MIT","version":"0.0","source_ref":"SOURCE_REF","metadata":{}}}],"dependencies":[{"name":"dep-package3","epoch":7,"version":"3.0.3","release":"1.fc30","arch":"x86_64"},{"name":"dep-package1","epoch":0,"version":"1.33","release":"2.fc30","arch":"x86_64"},{"name":"dep-package2","epoch":0,"version":"2.9","release":"1.fc30","arch":"x86_64"}]},{"name":"package1","summary":"pkg1 sum","description":"pkg1 desc","homepage":"https://pkg1.example.com","upstream_vcs":"UPSTREAM_VCS","builds":[{"arch":"x86_64","build_time":"2006-02-02T15:04:05","epoch":0,"release":"1.fc30","changelog":"CHANGELOG_NEEDED","build_config_ref":"BUILD_CONFIG_REF","build_env_ref":"BUILD_ENV_REF","metadata":{},"source":{"license":"MIT","version":"1.0","source_ref":"SOURCE_REF","metadata":{}}},{"arch":"x86_64","build_time":"2006-02-03T15:04:05","epoch":0,"release":"1.fc30","changelog":"CHANGELOG_NEEDED","build_config_ref":"BUILD_CONFIG_REF","build_env_ref":"BUILD_ENV_REF","metadata":{},"source":{"license":"MIT","version":"1.1","source_ref":"SOURCE_REF","metadata":{}}}],"dependencies":[{"name":"dep-package3","epoch":7,"version":"3.0.3","release":"1.fc30","arch":"x86_64"},{"name":"dep-package1","epoch":0,"version":"1.33","release":"2.fc30","arch":"x86_64"},{"name":"dep-package2","epoch":0,"version":"2.9","release":"1.fc30","arch":"x86_64"}]},{"name":"package10","summary":"pkg10 sum","description":"pkg10 desc","homepage":"https://pkg10.example.com","upstream_vcs":"UPSTREAM_VCS","builds":[{"arch":"x86_64","build_time":"2006-11-02T15:04:05","epoch":0,"release":"10.fc30","changelog":"CHANGELOG_NEEDED","build_config_ref":"BUILD_CONFIG_REF","build_env_ref":"BUILD_ENV_REF","metadata":{},"source":{"license":"MIT","version":"10.0","source_ref":"SOURCE_REF","metadata":{}}},{"arch":"x86_64","build_time":"2006-11-03T15:04:05","epoch":0,"release":"10.fc30","changelog":"CHANGELOG_NEEDED","build_config_ref":"BUILD_CONFIG_REF","build_env_ref":"BUILD_ENV_REF","metadata":{},"source":{"license":"MIT","version":"10.1","source_ref":"SOURCE_REF","metadata":{}}}],"dependencies":[{"name":"dep-package3","epoch":7,"version":"3.0.3","release":"1.fc30","arch":"x86_64"},{"name":"dep-package1","epoch":0,"version":"1.33","release":"2.fc30","arch":"x86_64"},{"name":"dep-package2","epoch":0,"version":"2.9","release":"1.fc30","arch":"x86_64"}]},{"name":"package11","summary":"pkg11 sum","description":"pkg11 desc","homepage":"https://pkg11.example.com","upstream_vcs":"UPSTREAM_VCS","builds":[{"arch":"x86_64","build_time":"2006-12-03T15:04:05","epoch":0,"release":"11.fc30","changelog":"CHANGELOG_NEEDED","build_config_ref":"BUILD_CONFIG_REF","build_env_ref":"BUILD_ENV_REF","metadata":{},"source":{"license":"MIT","version":"11.1","source_ref":"SOURCE_REF","metadata":{}}},{"arch":"x86_64","build_time":"2006-12-02T15:04:05","epoch":0,"release":"11.fc30","changelog":"CHANGELOG_NEEDED","build_config_ref":"BUILD_CONFIG_REF","build_env_ref":"BUILD_ENV_REF","metadata":{},"source":{"license":"MIT","version":"11.0","source_ref":"SOURCE_REF","metadata":{}}}],"dependencies":[{"name":"dep-package3","epoch":7,"version":"3.0.3","release":"1.fc30","arch":"x86_64"},{"name":"dep-package1","epoch":0,"version":"1.33","release":"2.fc30","arch":"x86_64"},{"name":"dep-package2","epoch":0,"version":"2.9","release":"1.fc30","arch":"x86_64"}]},{"name":"package12","summary":"pkg12 sum","description":"pkg12 desc","homepage":"https://pkg12.example.com","upstream_vcs":"UPSTREAM_VCS","builds":[{"arch":"x86_64","build_time":"2007-01-02T15:04:05","epoch":0,"release":"12.fc30","changelog":"CHANGELOG_NEEDED","build_config_ref":"BUILD_CONFIG_REF","build_env_ref":"BUILD_ENV_REF","metadata":{},"source":{"license":"MIT","version":"12.0","source_ref":"SOURCE_REF","metadata":{}}},{"arch":"x86_64","build_time":"2007-01-03T15:04:05","epoch":0,"release":"12.fc30","changelog":"CHANGELOG_NEEDED","build_config_ref":"BUILD_CONFIG_REF","build_env_ref":"BUILD_ENV_REF","metadata":{},"source":{"license":"MIT","version":"12.1","source_ref":"SOURCE_REF","metadata":{}}}],"dependencies":[{"name":"dep-package3","epoch":7,"version":"3.0.3","release":"1.fc30","arch":"x86_64"},{"name":"dep-package1","epoch":0,"version":"1.33","release":"2.fc30","arch":"x86_64"},{"name":"dep-package2","epoch":0,"version":"2.9","release":"1.fc30","arch":"x86_64"}]},{"name":"package13","summary":"pkg13 sum","description":"pkg13 desc","homepage":"https://pkg13.example.com","upstream_vcs":"UPSTREAM_VCS","builds":[{"arch":"x86_64","build_time":"2007-02-02T15:04:05","epoch":0,"release":"13.fc30","changelog":"CHANGELOG_NEEDED","build_config_ref":"BUILD_CONFIG_REF","build_env_ref":"BUILD_ENV_REF","metadata":{},"source":{"license":"MIT","version":"13.0","source_ref":"SOURCE_REF","metadata":{}}},{"arch":"x86_64","build_time":"2007-02-03T15:04:05","epoch":0,"release":"13.fc30","changelog":"CHANGELOG_NEEDED","build_config_ref":"BUILD_CONFIG_REF","build_env_ref":"BUILD_ENV_REF","metadata":{},"source":{"license":"MIT","version":"13.1","source_ref":"SOURCE_REF","metadata":{}}}],"dependencies":[{"name":"dep-package3","epoch":7,"version":"3.0.3","release":"1.fc30","arch":"x86_64"},{"name":"dep-package1","epoch":0,"version":"1.33","release":"2.fc30","arch":"x86_64"},{"name":"dep-package2","epoch":0,"version":"2.9","release":"1.fc30","arch":"x86_64"}]},{"name":"package14","summary":"pkg14 sum","description":"pkg14 desc","homepage":"https://pkg14.example.com","upstream_vcs":"UPSTREAM_VCS","builds":[{"arch":"x86_64","build_time":"2007-03-03T15:04:05","epoch":0,"release":"14.fc30","changelog":"CHANGELOG_NEEDED","build_config_ref":"BUILD_CONFIG_REF","build_env_ref":"BUILD_ENV_REF","metadata":{},"source":{"license":"MIT","version":"14.1","source_ref":"SOURCE_REF","metadata":{}}},{"arch":"x86_64","build_time":"2007-03-02T15:04:05","epoch":0,"release":"14.fc30","changelog":"CHANGELOG_NEEDED","build_config_ref":"BUILD_CONFIG_REF","build_env_ref":"BUILD_ENV_REF","metadata":{},"source":{"license":"MIT","version":"14.0","source_ref":"SOURCE_REF","metadata":{}}}],"dependencies":[{"name":"dep-package3","epoch":7,"version":"3.0.3","release":"1.fc30","arch":"x86_64"},{"name":"dep-package1","epoch":0,"version":"1.33","release":"2.fc30","arch":"x86_64"},{"name":"dep-package2","epoch":0,"version":"2.9","release":"1.fc30","arch":"x86_64"}]},{"name":"package15","summary":"pkg15 sum","description":"pkg15 desc","homepage":"https://pkg15.example.com","upstream_vcs":"UPSTREAM_VCS","builds":[{"arch":"x86_64","build_time":"2007-04-03T15:04:05","epoch":0,"release":"15.fc30","changelog":"CHANGELOG_NEEDED","build_config_ref":"BUILD_CONFIG_REF","build_env_ref":"BUILD_ENV_REF","metadata":{},"source":{"license":"MIT","version":"15.1","source_ref":"SOURCE_REF","metadata":{}}},{"arch":"x86_64","build_time":"2007-04-02T15:04:05","epoch":0,"release":"15.fc30","changelog":"CHANGELOG_NEEDED","build_config_ref":"BUILD_CONFIG_REF","build_env_ref":"BUILD_ENV_REF","metadata":{},"source":{"license":"MIT","version":"15.0","source_ref":"SOURCE_REF","metadata":{}}}],"dependencies":[{"name":"dep-package3","epoch":7,"version":"3.0.3","release":"1.fc30","arch":"x86_64"},{"name":"dep-package1","epoch":0,"version":"1.33","release":"2.fc30","arch":"x86_64"},{"name":"dep-package2","epoch":0,"version":"2.9","release":"1.fc30","arch":"x86_64"}]},{"name":"package16","summary":"pkg16 sum","description":"pkg16 desc","homepage":"https://pkg16.example.com","upstream_vcs":"UPSTREAM_VCS","builds":[{"arch":"x86_64","build_time":"2007-05-02T15:04:05","epoch":0,"release":"16.fc30","changelog":"CHANGELOG_NEEDED","build_config_ref":"BUILD_CONFIG_REF","build_env_ref":"BUILD_ENV_REF","metadata":{},"source":{"license":"MIT","version":"16.0","source_ref":"SOURCE_REF","metadata":{}}},{"arch":"x86_64","build_time":"2007-05-03T15:04:05","epoch":0,"release":"16.fc30","changelog":"CHANGELOG_NEEDED","build_config_ref":"BUILD_CONFIG_REF","build_env_ref":"BUILD_ENV_REF","metadata":{},"source":{"license":"MIT","version":"16.1","source_ref":"SOURCE_REF","metadata":{}}}],"dependencies":[{"name":"dep-package3","epoch":7,"version":"3.0.3","release":"1.fc30","arch":"x86_64"},{"name":"dep-package1","epoch":0,"version":"1.33","release":"2.fc30","arch":"x86_64"},{"name":"dep-package2","epoch":0,"version":"2.9","release":"1.fc30","arch":"x86_64"}]},{"name":"package17","summary":"pkg17 sum","description":"pkg17 desc","homepage":"https://pkg17.example.com","upstream_vcs":"UPSTREAM_VCS","builds":[{"arch":"x86_64","build_time":"2007-06-03T15:04:05","epoch":0,"release":"17.fc30","changelog":"CHANGELOG_NEEDED","build_config_ref":"BUILD_CONFIG_REF","build_env_ref":"BUILD_ENV_REF","metadata":{},"source":{"license":"MIT","version":"17.1","source_ref":"SOURCE_REF","metadata":{}}},{"arch":"x86_64","build_time":"2007-06-02T15:04:05","epoch":0,"release":"17.fc30","changelog":"CHANGELOG_NEEDED","build_config_ref":"BUILD_CONFIG_REF","build_env_ref":"BUILD_ENV_REF","metadata":{},"source":{"license":"MIT","version":"17.0","source_ref":"SOURCE_REF","metadata":{}}}],"dependencies":[{"name":"dep-package3","epoch":7,"version":"3.0.3","release":"1.fc30","arch":"x86_64"},{"name":"dep-package1","epoch":0,"version":"1.33","release":"2.fc30","arch":"x86_64"},{"name":"dep-package2","epoch":0,"version":"2.9","release":"1.fc30","arch":"x86_64"}]},{"name":"package18","summary":"pkg18 sum","description":"pkg18 desc","homepage":"https://pkg18.example.com","upstream_vcs":"UPSTREAM_VCS","builds":[{"arch":"x86_64","build_time":"2007-07-02T15:04:05","epoch":0,"release":"18.fc30","changelog":"CHANGELOG_NEEDED","build_config_ref":"BUILD_CONFIG_REF","build_env_ref":"BUILD_ENV_REF","metadata":{},"source":{"license":"MIT","version":"18.0","source_ref":"SOURCE_REF","metadata":{}}},{"arch":"x86_64","build_time":"2007-07-03T15:04:05","epoch":0,"release":"18.fc30","changelog":"CHANGELOG_NEEDED","build_config_ref":"BUILD_CONFIG_REF","build_env_ref":"BUILD_ENV_REF","metadata":{},"source":{"license":"MIT","version":"18.1","source_ref":"SOURCE_REF","metadata":{}}}],"dependencies":[{"name":"dep-package3","epoch":7,"version":"3.0.3","release":"1.fc30","arch":"x86_64"},{"name":"dep-package1","epoch":0,"version":"1.33","release":"2.fc30","arch":"x86_64"},{"name":"dep-package2","epoch":0,"version":"2.9","release":"1.fc30","arch":"x86_64"}]},{"name":"package19","summary":"pkg19 sum","description":"pkg19 desc","homepage":"https://pkg19.example.com","upstream_vcs":"UPSTREAM_VCS","builds":[{"arch":"x86_64","build_time":"2007-08-03T15:04:05","epoch":0,"release":"19.fc30","changelog":"CHANGELOG_NEEDED","build_config_ref":"BUILD_CONFIG_REF","build_env_ref":"BUILD_ENV_REF","metadata":{},"source":{"license":"MIT","version":"19.1","source_ref":"SOURCE_REF","metadata":{}}},{"arch":"x86_64","build_time":"2007-08-02T15:04:05","epoch":0,"release":"19.fc30","changelog":"CHANGELOG_NEEDED","build_config_ref":"BUILD_CONFIG_REF","build_env_ref":"BUILD_ENV_REF","metadata":{},"source":{"license":"MIT","version":"19.0","source_ref":"SOURCE_REF","metadata":{}}}],"dependencies":[{"name":"dep-package3","epoch":7,"version":"3.0.3","release":"1.fc30","arch":"x86_64"},{"name":"dep-package1","epoch":0,"version":"1.33","release":"2.fc30","arch":"x86_64"},{"name":"dep-package2","epoch":0,"version":"2.9","release":"1.fc30","arch":"x86_64"}]},{"name":"package2","summary":"pkg2 sum","description":"pkg2 desc","homepage":"https://pkg2.example.com","upstream_vcs":"UPSTREAM_VCS","builds":[{"arch":"x86_64","build_time":"2006-03-02T15:04:05","epoch":0,"release":"2.fc30","changelog":"CHANGELOG_NEEDED","build_config_ref":"BUILD_CONFIG_REF","build_env_ref":"BUILD_ENV_REF","metadata":{},"source":{"license":"MIT","version":"2.0","source_ref":"SOURCE_REF","metadata":{}}},{"arch":"x86_64","build_time":"2006-03-03T15:04:05","epoch":0,"release":"2.fc30","changelog":"CHANGELOG_NEEDED","build_config_ref":"BUILD_CONFIG_REF","build_env_ref":"BUILD_ENV_REF","metadata":{},"source":{"license":"MIT","version":"2.1","source_ref":"SOURCE_REF","metadata":{}}}],"dependencies":[{"name":"dep-package3","epoch":7,"version":"3.0.3","release":"1.fc30","arch":"x86_64"},{"name":"dep-package1","epoch":0,"version":"1.33","release":"2.fc30","arch":"x86_64"},{"name":"dep-package2","epoch":0,"version":"2.9","release":"1.fc30","arch":"x86_64"}]},{"name":"package20","summary":"pkg20 sum","description":"pkg20 desc","homepage":"https://pkg20.example.com","upstream_vcs":"UPSTREAM_VCS","builds":[{"arch":"x86_64","build_time":"2007-09-03T15:04:05","epoch":0,"release":"20.fc30","changelog":"CHANGELOG_NEEDED","build_config_ref":"BUILD_CONFIG_REF","build_env_ref":"BUILD_ENV_REF","metadata":{},"source":{"license":"MIT","version":"20.1","source_ref":"SOURCE_REF","metadata":{}}},{"arch":"x86_64","build_time":"2007-09-02T15:04:05","epoch":0,"release":"20.fc30","changelog":"CHANGELOG_NEEDED","build_config_ref":"BUILD_CONFIG_REF","build_env_ref":"BUILD_ENV_REF","metadata":{},"source":{"license":"MIT","version":"20.0","source_ref":"SOURCE_REF","metadata":{}}}],"dependencies":[{"name":"dep-package3","epoch":7,"version":"3.0.3","release":"1.fc30","arch":"x86_64"},{"name":"dep-package1","epoch":0,"version":"1.33","release":"2.fc30","arch":"x86_64"},{"name":"dep-package2","epoch":0,"version":"2.9","release":"1.fc30","arch":"x86_64"}]},{"name":"package21","summary":"pkg21 sum","description":"pkg21 desc","homepage":"https://pkg21.example.com","upstream_vcs":"UPSTREAM_VCS","builds":[{"arch":"x86_64","build_time":"2007-10-02T15:04:05","epoch":0,"release":"21.fc30","changelog":"CHANGELOG_NEEDED","build_config_ref":"BUILD_CONFIG_REF","build_env_ref":"BUILD_ENV_REF","metadata":{},"source":{"license":"MIT","version":"21.0","source_ref":"SOURCE_REF","metadata":{}}},{"arch":"x86_64","build_time":"2007-10-03T15:04:05","epoch":0,"release":"21.fc30","changelog":"CHANGELOG_NEEDED","build_config_ref":"BUILD_CONFIG_REF","build_env_ref":"BUILD_ENV_REF","metadata":{},"source":{"license":"MIT","version":"21.1","source_ref":"SOURCE_REF","metadata":{}}}],"dependencies":[{"name":"dep-package3","epoch":7,"version":"3.0.3","release":"1.fc30","arch":"x86_64"},{"name":"dep-package1","epoch":0,"version":"1.33","release":"2.fc30","arch":"x86_64"},{"name":"dep-package2","epoch":0,"version":"2.9","release":"1.fc30","arch":"x86_64"}]},{"name":"package3","summary":"pkg3 sum","description":"pkg3 desc","homepage":"https://pkg3.example.com","upstream_vcs":"UPSTREAM_VCS","builds":[{"arch":"x86_64","build_time":"2006-04-03T15:04:05","epoch":0,"release":"3.fc30","changelog":"CHANGELOG_NEEDED","build_config_ref":"BUILD_CONFIG_REF","build_env_ref":"BUILD_ENV_REF","metadata":{},"source":{"license":"MIT","version":"3.1","source_ref":"SOURCE_REF","metadata":{}}},{"arch":"x86_64","build_time":"2006-04-02T15:04:05","epoch":0,"release":"3.fc30","changelog":"CHANGELOG_NEEDED","build_config_ref":"BUILD_CONFIG_REF","build_env_ref":"BUILD_ENV_REF","metadata":{},"source":{"license":"MIT","version":"3.0","source_ref":"SOURCE_REF","metadata":{}}}],"dependencies":[{"name":"dep-package3","epoch":7,"version":"3.0.3","release":"1.fc30","arch":"x86_64"},{"name":"dep-package1","epoch":0,"version":"1.33","release":"2.fc30","arch":"x86_64"},{"name":"dep-package2","epoch":0,"version":"2.9","release":"1.fc30","arch":"x86_64"}]},{"name":"package4","summary":"pkg4 sum","description":"pkg4 desc","homepage":"https://pkg4.example.com","upstream_vcs":"UPSTREAM_VCS","builds":[{"arch":"x86_64","build_time":"2006-05-03T15:04:05","epoch":0,"release":"4.fc30","changelog":"CHANGELOG_NEEDED","build_config_ref":"BUILD_CONFIG_REF","build_env_ref":"BUILD_ENV_REF","metadata":{},"source":{"license":"MIT","version":"4.1","source_ref":"SOURCE_REF","metadata":{}}},{"arch":"x86_64","build_time":"2006-05-02T15:04:05","epoch":0,"release":"4.fc30","changelog":"CHANGELOG_NEEDED","build_config_ref":"BUILD_CONFIG_REF","build_env_ref":"BUILD_ENV_REF","metadata":{},"source":{"license":"MIT","version":"4.0","source_ref":"SOURCE_REF","metadata":{}}}],"dependencies":[{"name":"dep-package3","epoch":7,"version":"3.0.3","release":"1.fc30","arch":"x86_64"},{"name":"dep-package1","epoch":0,"version":"1.33","release":"2.fc30","arch":"x86_64"},{"name":"dep-package2","epoch":0,"version":"2.9","release":"1.fc30","arch":"x86_64"}]},{"name":"package5","summary":"pkg5 sum","description":"pkg5 desc","homepage":"https://pkg5.example.com","upstream_vcs":"UPSTREAM_VCS","builds":[{"arch":"x86_64","build_time":"2006-06-03T15:04:05","epoch":0,"release":"5.fc30","changelog":"CHANGELOG_NEEDED","build_config_ref":"BUILD_CONFIG_REF","build_env_ref":"BUILD_ENV_REF","metadata":{},"source":{"license":"MIT","version":"5.1","source_ref":"SOURCE_REF","metadata":{}}},{"arch":"x86_64","build_time":"2006-06-02T15:04:05","epoch":0,"release":"5.fc30","changelog":"CHANGELOG_NEEDED","build_config_ref":"BUILD_CONFIG_REF","build_env_ref":"BUILD_ENV_REF","metadata":{},"source":{"license":"MIT","version":"5.0","source_ref":"SOURCE_REF","metadata":{}}}],"dependencies":[{"name":"dep-package3","epoch":7,"version":"3.0.3","release":"1.fc30","arch":"x86_64"},{"name":"dep-package1","epoch":0,"version":"1.33","release":"2.fc30","arch":"x86_64"},{"name":"dep-package2","epoch":0,"version":"2.9","release":"1.fc30","arch":"x86_64"}]},{"name":"package6","summary":"pkg6 sum","description":"pkg6 desc","homepage":"https://pkg6.example.com","upstream_vcs":"UPSTREAM_VCS","builds":[{"arch":"x86_64","build_time":"2006-07-02T15:04:05","epoch":0,"release":"6.fc30","changelog":"CHANGELOG_NEEDED","build_config_ref":"BUILD_CONFIG_REF","build_env_ref":"BUILD_ENV_REF","metadata":{},"source":{"license":"MIT","version":"6.0","source_ref":"SOURCE_REF","metadata":{}}},{"arch":"x86_64","build_time":"2006-07-03T15:04:05","epoch":0,"release":"6.fc30","changelog":"CHANGELOG_NEEDED","build_config_ref":"BUILD_CONFIG_REF","build_env_ref":"BUILD_ENV_REF","metadata":{},"source":{"license":"MIT","version":"6.1","source_ref":"SOURCE_REF","metadata":{}}}],"dependencies":[{"name":"dep-package3","epoch":7,"version":"3.0.3","release":"1.fc30","arch":"x86_64"},{"name":"dep-package1","epoch":0,"version":"1.33","release":"2.fc30","arch":"x86_64"},{"name":"dep-package2","epoch":0,"version":"2.9","release":"1.fc30","arch":"x86_64"}]},{"name":"package7","summary":"pkg7 sum","description":"pkg7 desc","homepage":"https://pkg7.example.com","upstream_vcs":"UPSTREAM_VCS","builds":[{"arch":"x86_64","build_time":"2006-08-02T15:04:05","epoch":0,"release":"7.fc30","changelog":"CHANGELOG_NEEDED","build_config_ref":"BUILD_CONFIG_REF","build_env_ref":"BUILD_ENV_REF","metadata":{},"source":{"license":"MIT","version":"7.0","source_ref":"SOURCE_REF","metadata":{}}},{"arch":"x86_64","build_time":"2006-08-03T15:04:05","epoch":0,"release":"7.fc30","changelog":"CHANGELOG_NEEDED","build_config_ref":"BUILD_CONFIG_REF","build_env_ref":"BUILD_ENV_REF","metadata":{},"source":{"license":"MIT","version":"7.1","source_ref":"SOURCE_REF","metadata":{}}}],"dependencies":[{"name":"dep-package3","epoch":7,"version":"3.0.3","release":"1.fc30","arch":"x86_64"},{"name":"dep-package1","epoch":0,"version":"1.33","release":"2.fc30","arch":"x86_64"},{"name":"dep-package2","epoch":0,"version":"2.9","release":"1.fc30","arch":"x86_64"}]},{"name":"package8","summary":"pkg8 sum","description":"pkg8 desc","homepage":"https://pkg8.example.com","upstream_vcs":"UPSTREAM_VCS","builds":[{"arch":"x86_64","build_time":"2006-09-03T15:04:05","epoch":0,"release":"8.fc30","changelog":"CHANGELOG_NEEDED","build_config_ref":"BUILD_CONFIG_REF","build_env_ref":"BUILD_ENV_REF","metadata":{},"source":{"license":"MIT","version":"8.1","source_ref":"SOURCE_REF","metadata":{}}},{"arch":"x86_64","build_time":"2006-09-02T15:04:05","epoch":0,"release":"8.fc30","changelog":"CHANGELOG_NEEDED","build_config_ref":"BUILD_CONFIG_REF","build_env_ref":"BUILD_ENV_REF","metadata":{},"source":{"license":"MIT","version":"8.0","source_ref":"SOURCE_REF","metadata":{}}}],"dependencies":[{"name":"dep-package3","epoch":7,"version":"3.0.3","release":"1.fc30","arch":"x86_64"},{"name":"dep-package1","epoch":0,"version":"1.33","release":"2.fc30","arch":"x86_64"},{"name":"dep-package2","epoch":0,"version":"2.9","release":"1.fc30","arch":"x86_64"}]},{"name":"package9","summary":"pkg9 sum","description":"pkg9 desc","homepage":"https://pkg9.example.com","upstream_vcs":"UPSTREAM_VCS","builds":[{"arch":"x86_64","build_time":"2006-10-02T15:04:05","epoch":0,"release":"9.fc30","changelog":"CHANGELOG_NEEDED","build_config_ref":"BUILD_CONFIG_REF","build_env_ref":"BUILD_ENV_REF","metadata":{},"source":{"license":"MIT","version":"9.0","source_ref":"SOURCE_REF","metadata":{}}},{"arch":"x86_64","build_time":"2006-10-03T15:04:05","epoch":0,"release":"9.fc30","changelog":"CHANGELOG_NEEDED","build_config_ref":"BUILD_CONFIG_REF","build_env_ref":"BUILD_ENV_REF","metadata":{},"source":{"license":"MIT","version":"9.1","source_ref":"SOURCE_REF","metadata":{}}}],"dependencies":[{"name":"dep-package3","epoch":7,"version":"3.0.3","release":"1.fc30","arch":"x86_64"},{"name":"dep-package1","epoch":0,"version":"1.33","release":"2.fc30","arch":"x86_64"},{"name":"dep-package2","epoch":0,"version":"2.9","release":"1.fc30","arch":"x86_64"}]}]}`},
 		{rpmmd_mock.BadFetch, "/api/v0/modules/info/package2*,package16", http.StatusBadRequest, `{"status":false,"errors":[{"id":"ModulesError","msg":"msg: DNF error occured: FetchError: There was a problem when fetching packages."}]}`},
 		{rpmmd_mock.BaseFixture, "/api/v1/modules/info/package2*,package16", http.StatusOK, `{"modules":[{"name":"package16","summary":"pkg16 sum","description":"pkg16 desc","homepage":"https://pkg16.example.com","upstream_vcs":"UPSTREAM_VCS","builds":[{"arch":"x86_64","build_time":"2007-05-02T15:04:05","epoch":0,"release":"16.fc30","changelog":"CHANGELOG_NEEDED","build_config_ref":"BUILD_CONFIG_REF","build_env_ref":"BUILD_ENV_REF","metadata":{},"source":{"license":"MIT","version":"16.0","source_ref":"SOURCE_REF","metadata":{}}},{"arch":"x86_64","build_time":"2007-05-03T15:04:05","epoch":0,"release":"16.fc30","changelog":"CHANGELOG_NEEDED","build_config_ref":"BUILD_CONFIG_REF","build_env_ref":"BUILD_ENV_REF","metadata":{},"source":{"license":"MIT","version":"16.1","source_ref":"SOURCE_REF","metadata":{}}}],"dependencies":[{"name":"dep-package3","epoch":7,"version":"3.0.3","release":"1.fc30","arch":"x86_64"},{"name":"dep-package1","epoch":0,"version":"1.33","release":"2.fc30","arch":"x86_64"},{"name":"dep-package2","epoch":0,"version":"2.9","release":"1.fc30","arch":"x86_64"}]},{"name":"package2","summary":"pkg2 sum","description":"pkg2 desc","homepage":"https://pkg2.example.com","upstream_vcs":"UPSTREAM_VCS","builds":[{"arch":"x86_64","build_time":"2006-03-02T15:04:05","epoch":0,"release":"2.fc30","changelog":"CHANGELOG_NEEDED","build_config_ref":"BUILD_CONFIG_REF","build_env_ref":"BUILD_ENV_REF","metadata":{},"source":{"license":"MIT","version":"2.0","source_ref":"SOURCE_REF","metadata":{}}},{"arch":"x86_64","build_time":"2006-03-03T15:04:05","epoch":0,"release":"2.fc30","changelog":"CHANGELOG_NEEDED","build_config_ref":"BUILD_CONFIG_REF","build_env_ref":"BUILD_ENV_REF","metadata":{},"source":{"license":"MIT","version":"2.1","source_ref":"SOURCE_REF","metadata":{}}}],"dependencies":[{"name":"dep-package3","epoch":7,"version":"3.0.3","release":"1.fc30","arch":"x86_64"},{"name":"dep-package1","epoch":0,"version":"1.33","release":"2.fc30","arch":"x86_64"},{"name":"dep-package2","epoch":0,"version":"2.9","release":"1.fc30","arch":"x86_64"}]},{"name":"package20","summary":"pkg20 sum","description":"pkg20 desc","homepage":"https://pkg20.example.com","upstream_vcs":"UPSTREAM_VCS","builds":[{"arch":"x86_64","build_time":"2007-09-03T15:04:05","epoch":0,"release":"20.fc30","changelog":"CHANGELOG_NEEDED","build_config_ref":"BUILD_CONFIG_REF","build_env_ref":"BUILD_ENV_REF","metadata":{},"source":{"license":"MIT","version":"20.1","source_ref":"SOURCE_REF","metadata":{}}},{"arch":"x86_64","build_time":"2007-09-02T15:04:05","epoch":0,"release":"20.fc30","changelog":"CHANGELOG_NEEDED","build_config_ref":"BUILD_CONFIG_REF","build_env_ref":"BUILD_ENV_REF","metadata":{},"source":{"license":"MIT","version":"20.0","source_ref":"SOURCE_REF","metadata":{}}}],"dependencies":[{"name":"dep-package3","epoch":7,"version":"3.0.3","release":"1.fc30","arch":"x86_64"},{"name":"dep-package1","epoch":0,"version":"1.33","release":"2.fc30","arch":"x86_64"},{"name":"dep-package2","epoch":0,"version":"2.9","release":"1.fc30","arch":"x86_64"}]},{"name":"package21","summary":"pkg21 sum","description":"pkg21 desc","homepage":"https://pkg21.example.com","upstream_vcs":"UPSTREAM_VCS","builds":[{"arch":"x86_64","build_time":"2007-10-02T15:04:05","epoch":0,"release":"21.fc30","changelog":"CHANGELOG_NEEDED","build_config_ref":"BUILD_CONFIG_REF","build_env_ref":"BUILD_ENV_REF","metadata":{},"source":{"license":"MIT","version":"21.0","source_ref":"SOURCE_REF","metadata":{}}},{"arch":"x86_64","build_time":"2007-10-03T15:04:05","epoch":0,"release":"21.fc30","changelog":"CHANGELOG_NEEDED","build_config_ref":"BUILD_CONFIG_REF","build_env_ref":"BUILD_ENV_REF","metadata":{},"source":{"license":"MIT","version":"21.1","source_ref":"SOURCE_REF","metadata":{}}}],"dependencies":[{"name":"dep-package3","epoch":7,"version":"3.0.3","release":"1.fc30","arch":"x86_64"},{"name":"dep-package1","epoch":0,"version":"1.33","release":"2.fc30","arch":"x86_64"},{"name":"dep-package2","epoch":0,"version":"2.9","release":"1.fc30","arch":"x86_64"}]}]}`},
+		{rpmmd_mock.BaseFixture, "/api/v1/modules/info/package16?distro=test-distro-2", http.StatusOK, `{"modules":[{"name":"package16","summary":"pkg16 sum","description":"pkg16 desc","homepage":"https://pkg16.example.com","upstream_vcs":"UPSTREAM_VCS","builds":[{"arch":"x86_64","build_time":"2007-05-02T15:04:05","epoch":0,"release":"16.fc30","changelog":"CHANGELOG_NEEDED","build_config_ref":"BUILD_CONFIG_REF","build_env_ref":"BUILD_ENV_REF","metadata":{},"source":{"license":"MIT","version":"16.0","source_ref":"SOURCE_REF","metadata":{}}},{"arch":"x86_64","build_time":"2007-05-03T15:04:05","epoch":0,"release":"16.fc30","changelog":"CHANGELOG_NEEDED","build_config_ref":"BUILD_CONFIG_REF","build_env_ref":"BUILD_ENV_REF","metadata":{},"source":{"license":"MIT","version":"16.1","source_ref":"SOURCE_REF","metadata":{}}}],"dependencies":[{"name":"dep-package3","epoch":7,"version":"3.0.3","release":"1.fc30","arch":"x86_64"},{"name":"dep-package1","epoch":0,"version":"1.33","release":"2.fc30","arch":"x86_64"},{"name":"dep-package2","epoch":0,"version":"2.9","release":"1.fc30","arch":"x86_64"}]}]}`},
+		{rpmmd_mock.BaseFixture, "/api/v1/modules/info/package16?distro=fedora-1", http.StatusBadRequest, `{"status":false,"errors":[{"id":"DistroError","msg":"Invalid distro: fedora-1"}]}`},
 	}
 
 	tempdir, err := ioutil.TempDir("", "weldr-tests-")
@@ -1319,6 +1375,8 @@ func TestProjectsList(t *testing.T) {
 		{rpmmd_mock.BaseFixture, "/api/v0/projects/list/", http.StatusOK, `{"total":22,"offset":0,"limit":20,"projects":[{"name":"package0","summary":"pkg0 sum","description":"pkg0 desc","homepage":"https://pkg0.example.com","upstream_vcs":"UPSTREAM_VCS","builds":[{"arch":"x86_64","build_time":"2006-01-03T15:04:05","epoch":0,"release":"0.fc30","changelog":"CHANGELOG_NEEDED","build_config_ref":"BUILD_CONFIG_REF","build_env_ref":"BUILD_ENV_REF","metadata":{},"source":{"license":"MIT","version":"0.1","source_ref":"SOURCE_REF","metadata":{}}},{"arch":"x86_64","build_time":"2006-01-02T15:04:05","epoch":0,"release":"0.fc30","changelog":"CHANGELOG_NEEDED","build_config_ref":"BUILD_CONFIG_REF","build_env_ref":"BUILD_ENV_REF","metadata":{},"source":{"license":"MIT","version":"0.0","source_ref":"SOURCE_REF","metadata":{}}}]},{"name":"package1","summary":"pkg1 sum","description":"pkg1 desc","homepage":"https://pkg1.example.com","upstream_vcs":"UPSTREAM_VCS","builds":[{"arch":"x86_64","build_time":"2006-02-02T15:04:05","epoch":0,"release":"1.fc30","changelog":"CHANGELOG_NEEDED","build_config_ref":"BUILD_CONFIG_REF","build_env_ref":"BUILD_ENV_REF","metadata":{},"source":{"license":"MIT","version":"1.0","source_ref":"SOURCE_REF","metadata":{}}},{"arch":"x86_64","build_time":"2006-02-03T15:04:05","epoch":0,"release":"1.fc30","changelog":"CHANGELOG_NEEDED","build_config_ref":"BUILD_CONFIG_REF","build_env_ref":"BUILD_ENV_REF","metadata":{},"source":{"license":"MIT","version":"1.1","source_ref":"SOURCE_REF","metadata":{}}}]},{"name":"package10","summary":"pkg10 sum","description":"pkg10 desc","homepage":"https://pkg10.example.com","upstream_vcs":"UPSTREAM_VCS","builds":[{"arch":"x86_64","build_time":"2006-11-02T15:04:05","epoch":0,"release":"10.fc30","changelog":"CHANGELOG_NEEDED","build_config_ref":"BUILD_CONFIG_REF","build_env_ref":"BUILD_ENV_REF","metadata":{},"source":{"license":"MIT","version":"10.0","source_ref":"SOURCE_REF","metadata":{}}},{"arch":"x86_64","build_time":"2006-11-03T15:04:05","epoch":0,"release":"10.fc30","changelog":"CHANGELOG_NEEDED","build_config_ref":"BUILD_CONFIG_REF","build_env_ref":"BUILD_ENV_REF","metadata":{},"source":{"license":"MIT","version":"10.1","source_ref":"SOURCE_REF","metadata":{}}}]},{"name":"package11","summary":"pkg11 sum","description":"pkg11 desc","homepage":"https://pkg11.example.com","upstream_vcs":"UPSTREAM_VCS","builds":[{"arch":"x86_64","build_time":"2006-12-03T15:04:05","epoch":0,"release":"11.fc30","changelog":"CHANGELOG_NEEDED","build_config_ref":"BUILD_CONFIG_REF","build_env_ref":"BUILD_ENV_REF","metadata":{},"source":{"license":"MIT","version":"11.1","source_ref":"SOURCE_REF","metadata":{}}},{"arch":"x86_64","build_time":"2006-12-02T15:04:05","epoch":0,"release":"11.fc30","changelog":"CHANGELOG_NEEDED","build_config_ref":"BUILD_CONFIG_REF","build_env_ref":"BUILD_ENV_REF","metadata":{},"source":{"license":"MIT","version":"11.0","source_ref":"SOURCE_REF","metadata":{}}}]},{"name":"package12","summary":"pkg12 sum","description":"pkg12 desc","homepage":"https://pkg12.example.com","upstream_vcs":"UPSTREAM_VCS","builds":[{"arch":"x86_64","build_time":"2007-01-02T15:04:05","epoch":0,"release":"12.fc30","changelog":"CHANGELOG_NEEDED","build_config_ref":"BUILD_CONFIG_REF","build_env_ref":"BUILD_ENV_REF","metadata":{},"source":{"license":"MIT","version":"12.0","source_ref":"SOURCE_REF","metadata":{}}},{"arch":"x86_64","build_time":"2007-01-03T15:04:05","epoch":0,"release":"12.fc30","changelog":"CHANGELOG_NEEDED","build_config_ref":"BUILD_CONFIG_REF","build_env_ref":"BUILD_ENV_REF","metadata":{},"source":{"license":"MIT","version":"12.1","source_ref":"SOURCE_REF","metadata":{}}}]},{"name":"package13","summary":"pkg13 sum","description":"pkg13 desc","homepage":"https://pkg13.example.com","upstream_vcs":"UPSTREAM_VCS","builds":[{"arch":"x86_64","build_time":"2007-02-02T15:04:05","epoch":0,"release":"13.fc30","changelog":"CHANGELOG_NEEDED","build_config_ref":"BUILD_CONFIG_REF","build_env_ref":"BUILD_ENV_REF","metadata":{},"source":{"license":"MIT","version":"13.0","source_ref":"SOURCE_REF","metadata":{}}},{"arch":"x86_64","build_time":"2007-02-03T15:04:05","epoch":0,"release":"13.fc30","changelog":"CHANGELOG_NEEDED","build_config_ref":"BUILD_CONFIG_REF","build_env_ref":"BUILD_ENV_REF","metadata":{},"source":{"license":"MIT","version":"13.1","source_ref":"SOURCE_REF","metadata":{}}}]},{"name":"package14","summary":"pkg14 sum","description":"pkg14 desc","homepage":"https://pkg14.example.com","upstream_vcs":"UPSTREAM_VCS","builds":[{"arch":"x86_64","build_time":"2007-03-03T15:04:05","epoch":0,"release":"14.fc30","changelog":"CHANGELOG_NEEDED","build_config_ref":"BUILD_CONFIG_REF","build_env_ref":"BUILD_ENV_REF","metadata":{},"source":{"license":"MIT","version":"14.1","source_ref":"SOURCE_REF","metadata":{}}},{"arch":"x86_64","build_time":"2007-03-02T15:04:05","epoch":0,"release":"14.fc30","changelog":"CHANGELOG_NEEDED","build_config_ref":"BUILD_CONFIG_REF","build_env_ref":"BUILD_ENV_REF","metadata":{},"source":{"license":"MIT","version":"14.0","source_ref":"SOURCE_REF","metadata":{}}}]},{"name":"package15","summary":"pkg15 sum","description":"pkg15 desc","homepage":"https://pkg15.example.com","upstream_vcs":"UPSTREAM_VCS","builds":[{"arch":"x86_64","build_time":"2007-04-03T15:04:05","epoch":0,"release":"15.fc30","changelog":"CHANGELOG_NEEDED","build_config_ref":"BUILD_CONFIG_REF","build_env_ref":"BUILD_ENV_REF","metadata":{},"source":{"license":"MIT","version":"15.1","source_ref":"SOURCE_REF","metadata":{}}},{"arch":"x86_64","build_time":"2007-04-02T15:04:05","epoch":0,"release":"15.fc30","changelog":"CHANGELOG_NEEDED","build_config_ref":"BUILD_CONFIG_REF","build_env_ref":"BUILD_ENV_REF","metadata":{},"source":{"license":"MIT","version":"15.0","source_ref":"SOURCE_REF","metadata":{}}}]},{"name":"package16","summary":"pkg16 sum","description":"pkg16 desc","homepage":"https://pkg16.example.com","upstream_vcs":"UPSTREAM_VCS","builds":[{"arch":"x86_64","build_time":"2007-05-02T15:04:05","epoch":0,"release":"16.fc30","changelog":"CHANGELOG_NEEDED","build_config_ref":"BUILD_CONFIG_REF","build_env_ref":"BUILD_ENV_REF","metadata":{},"source":{"license":"MIT","version":"16.0","source_ref":"SOURCE_REF","metadata":{}}},{"arch":"x86_64","build_time":"2007-05-03T15:04:05","epoch":0,"release":"16.fc30","changelog":"CHANGELOG_NEEDED","build_config_ref":"BUILD_CONFIG_REF","build_env_ref":"BUILD_ENV_REF","metadata":{},"source":{"license":"MIT","version":"16.1","source_ref":"SOURCE_REF","metadata":{}}}]},{"name":"package17","summary":"pkg17 sum","description":"pkg17 desc","homepage":"https://pkg17.example.com","upstream_vcs":"UPSTREAM_VCS","builds":[{"arch":"x86_64","build_time":"2007-06-03T15:04:05","epoch":0,"release":"17.fc30","changelog":"CHANGELOG_NEEDED","build_config_ref":"BUILD_CONFIG_REF","build_env_ref":"BUILD_ENV_REF","metadata":{},"source":{"license":"MIT","version":"17.1","source_ref":"SOURCE_REF","metadata":{}}},{"arch":"x86_64","build_time":"2007-06-02T15:04:05","epoch":0,"release":"17.fc30","changelog":"CHANGELOG_NEEDED","build_config_ref":"BUILD_CONFIG_REF","build_env_ref":"BUILD_ENV_REF","metadata":{},"source":{"license":"MIT","version":"17.0","source_ref":"SOURCE_REF","metadata":{}}}]},{"name":"package18","summary":"pkg18 sum","description":"pkg18 desc","homepage":"https://pkg18.example.com","upstream_vcs":"UPSTREAM_VCS","builds":[{"arch":"x86_64","build_time":"2007-07-02T15:04:05","epoch":0,"release":"18.fc30","changelog":"CHANGELOG_NEEDED","build_config_ref":"BUILD_CONFIG_REF","build_env_ref":"BUILD_ENV_REF","metadata":{},"source":{"license":"MIT","version":"18.0","source_ref":"SOURCE_REF","metadata":{}}},{"arch":"x86_64","build_time":"2007-07-03T15:04:05","epoch":0,"release":"18.fc30","changelog":"CHANGELOG_NEEDED","build_config_ref":"BUILD_CONFIG_REF","build_env_ref":"BUILD_ENV_REF","metadata":{},"source":{"license":"MIT","version":"18.1","source_ref":"SOURCE_REF","metadata":{}}}]},{"name":"package19","summary":"pkg19 sum","description":"pkg19 desc","homepage":"https://pkg19.example.com","upstream_vcs":"UPSTREAM_VCS","builds":[{"arch":"x86_64","build_time":"2007-08-03T15:04:05","epoch":0,"release":"19.fc30","changelog":"CHANGELOG_NEEDED","build_config_ref":"BUILD_CONFIG_REF","build_env_ref":"BUILD_ENV_REF","metadata":{},"source":{"license":"MIT","version":"19.1","source_ref":"SOURCE_REF","metadata":{}}},{"arch":"x86_64","build_time":"2007-08-02T15:04:05","epoch":0,"release":"19.fc30","changelog":"CHANGELOG_NEEDED","build_config_ref":"BUILD_CONFIG_REF","build_env_ref":"BUILD_ENV_REF","metadata":{},"source":{"license":"MIT","version":"19.0","source_ref":"SOURCE_REF","metadata":{}}}]},{"name":"package2","summary":"pkg2 sum","description":"pkg2 desc","homepage":"https://pkg2.example.com","upstream_vcs":"UPSTREAM_VCS","builds":[{"arch":"x86_64","build_time":"2006-03-02T15:04:05","epoch":0,"release":"2.fc30","changelog":"CHANGELOG_NEEDED","build_config_ref":"BUILD_CONFIG_REF","build_env_ref":"BUILD_ENV_REF","metadata":{},"source":{"license":"MIT","version":"2.0","source_ref":"SOURCE_REF","metadata":{}}},{"arch":"x86_64","build_time":"2006-03-03T15:04:05","epoch":0,"release":"2.fc30","changelog":"CHANGELOG_NEEDED","build_config_ref":"BUILD_CONFIG_REF","build_env_ref":"BUILD_ENV_REF","metadata":{},"source":{"license":"MIT","version":"2.1","source_ref":"SOURCE_REF","metadata":{}}}]},{"name":"package20","summary":"pkg20 sum","description":"pkg20 desc","homepage":"https://pkg20.example.com","upstream_vcs":"UPSTREAM_VCS","builds":[{"arch":"x86_64","build_time":"2007-09-03T15:04:05","epoch":0,"release":"20.fc30","changelog":"CHANGELOG_NEEDED","build_config_ref":"BUILD_CONFIG_REF","build_env_ref":"BUILD_ENV_REF","metadata":{},"source":{"license":"MIT","version":"20.1","source_ref":"SOURCE_REF","metadata":{}}},{"arch":"x86_64","build_time":"2007-09-02T15:04:05","epoch":0,"release":"20.fc30","changelog":"CHANGELOG_NEEDED","build_config_ref":"BUILD_CONFIG_REF","build_env_ref":"BUILD_ENV_REF","metadata":{},"source":{"license":"MIT","version":"20.0","source_ref":"SOURCE_REF","metadata":{}}}]},{"name":"package21","summary":"pkg21 sum","description":"pkg21 desc","homepage":"https://pkg21.example.com","upstream_vcs":"UPSTREAM_VCS","builds":[{"arch":"x86_64","build_time":"2007-10-02T15:04:05","epoch":0,"release":"21.fc30","changelog":"CHANGELOG_NEEDED","build_config_ref":"BUILD_CONFIG_REF","build_env_ref":"BUILD_ENV_REF","metadata":{},"source":{"license":"MIT","version":"21.0","source_ref":"SOURCE_REF","metadata":{}}},{"arch":"x86_64","build_time":"2007-10-03T15:04:05","epoch":0,"release":"21.fc30","changelog":"CHANGELOG_NEEDED","build_config_ref":"BUILD_CONFIG_REF","build_env_ref":"BUILD_ENV_REF","metadata":{},"source":{"license":"MIT","version":"21.1","source_ref":"SOURCE_REF","metadata":{}}}]},{"name":"package3","summary":"pkg3 sum","description":"pkg3 desc","homepage":"https://pkg3.example.com","upstream_vcs":"UPSTREAM_VCS","builds":[{"arch":"x86_64","build_time":"2006-04-03T15:04:05","epoch":0,"release":"3.fc30","changelog":"CHANGELOG_NEEDED","build_config_ref":"BUILD_CONFIG_REF","build_env_ref":"BUILD_ENV_REF","metadata":{},"source":{"license":"MIT","version":"3.1","source_ref":"SOURCE_REF","metadata":{}}},{"arch":"x86_64","build_time":"2006-04-02T15:04:05","epoch":0,"release":"3.fc30","changelog":"CHANGELOG_NEEDED","build_config_ref":"BUILD_CONFIG_REF","build_env_ref":"BUILD_ENV_REF","metadata":{},"source":{"license":"MIT","version":"3.0","source_ref":"SOURCE_REF","metadata":{}}}]},{"name":"package4","summary":"pkg4 sum","description":"pkg4 desc","homepage":"https://pkg4.example.com","upstream_vcs":"UPSTREAM_VCS","builds":[{"arch":"x86_64","build_time":"2006-05-03T15:04:05","epoch":0,"release":"4.fc30","changelog":"CHANGELOG_NEEDED","build_config_ref":"BUILD_CONFIG_REF","build_env_ref":"BUILD_ENV_REF","metadata":{},"source":{"license":"MIT","version":"4.1","source_ref":"SOURCE_REF","metadata":{}}},{"arch":"x86_64","build_time":"2006-05-02T15:04:05","epoch":0,"release":"4.fc30","changelog":"CHANGELOG_NEEDED","build_config_ref":"BUILD_CONFIG_REF","build_env_ref":"BUILD_ENV_REF","metadata":{},"source":{"license":"MIT","version":"4.0","source_ref":"SOURCE_REF","metadata":{}}}]},{"name":"package5","summary":"pkg5 sum","description":"pkg5 desc","homepage":"https://pkg5.example.com","upstream_vcs":"UPSTREAM_VCS","builds":[{"arch":"x86_64","build_time":"2006-06-03T15:04:05","epoch":0,"release":"5.fc30","changelog":"CHANGELOG_NEEDED","build_config_ref":"BUILD_CONFIG_REF","build_env_ref":"BUILD_ENV_REF","metadata":{},"source":{"license":"MIT","version":"5.1","source_ref":"SOURCE_REF","metadata":{}}},{"arch":"x86_64","build_time":"2006-06-02T15:04:05","epoch":0,"release":"5.fc30","changelog":"CHANGELOG_NEEDED","build_config_ref":"BUILD_CONFIG_REF","build_env_ref":"BUILD_ENV_REF","metadata":{},"source":{"license":"MIT","version":"5.0","source_ref":"SOURCE_REF","metadata":{}}}]},{"name":"package6","summary":"pkg6 sum","description":"pkg6 desc","homepage":"https://pkg6.example.com","upstream_vcs":"UPSTREAM_VCS","builds":[{"arch":"x86_64","build_time":"2006-07-02T15:04:05","epoch":0,"release":"6.fc30","changelog":"CHANGELOG_NEEDED","build_config_ref":"BUILD_CONFIG_REF","build_env_ref":"BUILD_ENV_REF","metadata":{},"source":{"license":"MIT","version":"6.0","source_ref":"SOURCE_REF","metadata":{}}},{"arch":"x86_64","build_time":"2006-07-03T15:04:05","epoch":0,"release":"6.fc30","changelog":"CHANGELOG_NEEDED","build_config_ref":"BUILD_CONFIG_REF","build_env_ref":"BUILD_ENV_REF","metadata":{},"source":{"license":"MIT","version":"6.1","source_ref":"SOURCE_REF","metadata":{}}}]},{"name":"package7","summary":"pkg7 sum","description":"pkg7 desc","homepage":"https://pkg7.example.com","upstream_vcs":"UPSTREAM_VCS","builds":[{"arch":"x86_64","build_time":"2006-08-02T15:04:05","epoch":0,"release":"7.fc30","changelog":"CHANGELOG_NEEDED","build_config_ref":"BUILD_CONFIG_REF","build_env_ref":"BUILD_ENV_REF","metadata":{},"source":{"license":"MIT","version":"7.0","source_ref":"SOURCE_REF","metadata":{}}},{"arch":"x86_64","build_time":"2006-08-03T15:04:05","epoch":0,"release":"7.fc30","changelog":"CHANGELOG_NEEDED","build_config_ref":"BUILD_CONFIG_REF","build_env_ref":"BUILD_ENV_REF","metadata":{},"source":{"license":"MIT","version":"7.1","source_ref":"SOURCE_REF","metadata":{}}}]}]}`},
 		{rpmmd_mock.BadFetch, "/api/v0/projects/list/", http.StatusBadRequest, `{"status":false,"errors":[{"id":"ProjectsError","msg":"msg: DNF error occured: FetchError: There was a problem when fetching packages."}]}`},
 		{rpmmd_mock.BaseFixture, "/api/v0/projects/list?offset=1&limit=1", http.StatusOK, `{"total":22,"offset":1,"limit":1,"projects":[{"name":"package1","summary":"pkg1 sum","description":"pkg1 desc","homepage":"https://pkg1.example.com","upstream_vcs":"UPSTREAM_VCS","builds":[{"arch":"x86_64","build_time":"2006-02-02T15:04:05","epoch":0,"release":"1.fc30","changelog":"CHANGELOG_NEEDED","build_config_ref":"BUILD_CONFIG_REF","build_env_ref":"BUILD_ENV_REF","metadata":{},"source":{"license":"MIT","version":"1.0","source_ref":"SOURCE_REF","metadata":{}}},{"arch":"x86_64","build_time":"2006-02-03T15:04:05","epoch":0,"release":"1.fc30","changelog":"CHANGELOG_NEEDED","build_config_ref":"BUILD_CONFIG_REF","build_env_ref":"BUILD_ENV_REF","metadata":{},"source":{"license":"MIT","version":"1.1","source_ref":"SOURCE_REF","metadata":{}}}]}]}`},
+		{rpmmd_mock.BaseFixture, "/api/v0/projects/list?distro=test-distro-2&offset=1&limit=1", http.StatusOK, `{"total":22,"offset":1,"limit":1,"projects":[{"name":"package1","summary":"pkg1 sum","description":"pkg1 desc","homepage":"https://pkg1.example.com","upstream_vcs":"UPSTREAM_VCS","builds":[{"arch":"x86_64","build_time":"2006-02-02T15:04:05","epoch":0,"release":"1.fc30","changelog":"CHANGELOG_NEEDED","build_config_ref":"BUILD_CONFIG_REF","build_env_ref":"BUILD_ENV_REF","metadata":{},"source":{"license":"MIT","version":"1.0","source_ref":"SOURCE_REF","metadata":{}}},{"arch":"x86_64","build_time":"2006-02-03T15:04:05","epoch":0,"release":"1.fc30","changelog":"CHANGELOG_NEEDED","build_config_ref":"BUILD_CONFIG_REF","build_env_ref":"BUILD_ENV_REF","metadata":{},"source":{"license":"MIT","version":"1.1","source_ref":"SOURCE_REF","metadata":{}}}]}]}`},
+		{rpmmd_mock.BaseFixture, "/api/v0/projects/list?distro=fedora-1&offset=1&limit=1", http.StatusBadRequest, `{"status":false,"errors":[{"id":"DistroError","msg":"Invalid distro: fedora-1"}]}`},
 	}
 
 	tempdir, err := ioutil.TempDir("", "weldr-tests-")
@@ -1345,6 +1403,8 @@ func TestModulesList(t *testing.T) {
 		{rpmmd_mock.BadFetch, "/api/v0/modules/list/package2*,package16", http.StatusBadRequest, `{"status":false,"errors":[{"id":"ModulesError","msg":"msg: DNF error occured: FetchError: There was a problem when fetching packages."}]}`},
 		{rpmmd_mock.BaseFixture, "/api/v0/modules/list/package2*,package16?offset=1&limit=1", http.StatusOK, `{"total":4,"offset":1,"limit":1,"modules":[{"name":"package2","group_type":"rpm"}]}`},
 		{rpmmd_mock.BaseFixture, "/api/v0/modules/list/*", http.StatusOK, `{"total":22,"offset":0,"limit":20,"modules":[{"name":"package0","group_type":"rpm"},{"name":"package1","group_type":"rpm"},{"name":"package10","group_type":"rpm"},{"name":"package11","group_type":"rpm"},{"name":"package12","group_type":"rpm"},{"name":"package13","group_type":"rpm"},{"name":"package14","group_type":"rpm"},{"name":"package15","group_type":"rpm"},{"name":"package16","group_type":"rpm"},{"name":"package17","group_type":"rpm"},{"name":"package18","group_type":"rpm"},{"name":"package19","group_type":"rpm"},{"name":"package2","group_type":"rpm"},{"name":"package20","group_type":"rpm"},{"name":"package21","group_type":"rpm"},{"name":"package3","group_type":"rpm"},{"name":"package4","group_type":"rpm"},{"name":"package5","group_type":"rpm"},{"name":"package6","group_type":"rpm"},{"name":"package7","group_type":"rpm"}]}`},
+		{rpmmd_mock.BaseFixture, "/api/v0/modules/list/package2*,package16?distro=test-distro-2&offset=1&limit=1", http.StatusOK, `{"total":4,"offset":1,"limit":1,"modules":[{"name":"package2","group_type":"rpm"}]}`},
+		{rpmmd_mock.BaseFixture, "/api/v0/modules/list/package2*,package16?distro=fedora-1&offset=1&limit=1", http.StatusBadRequest, `{"status":false,"errors":[{"id":"DistroError","msg":"Invalid distro: fedora-1"}]}`},
 	}
 
 	tempdir, err := ioutil.TempDir("", "weldr-tests-")
diff --git a/test/cases/azure.sh b/test/cases/azure.sh
index 03b533d81..b44c26c51 100755
--- a/test/cases/azure.sh
+++ b/test/cases/azure.sh
@@ -3,8 +3,8 @@ set -euo pipefail
 
 source /etc/os-release
 DISTRO_CODE="${DISTRO_CODE:-${ID}_${VERSION_ID//./}}"
-BRANCH_NAME="${BRANCH_NAME:-${CI_COMMIT_BRANCH}}"
-BUILD_ID="${BUILD_ID:-${CI_BUILD_ID}}"
+BRANCH_NAME="${CI_COMMIT_BRANCH:-'local'}"
+BUILD_ID="${CI_BUILD_ID:-$(uuidgen)}"
 HYPER_V_GEN="${HYPER_V_GEN:-V1}"
 
 # Colorful output.