Repository: osbuild/osbuild-composer
Pull request #3121 (2022-11-08 19:26:35)

Add /blueprints/change/NAME/COMMIT route and save blueprint changes in the store
This pull request includes:

- [X] adequate testing for the new functionality or fixed issue
- [X] adequate documentation informing people about the change such as
  - [ ] 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/


Patch:
diff --git a/internal/blueprint/customizations.go b/internal/blueprint/customizations.go
index 69b0db2e2..299ccc6df 100644
--- a/internal/blueprint/customizations.go
+++ b/internal/blueprint/customizations.go
@@ -380,3 +380,7 @@ func (c *Customizations) GetOpenSCAP() *OpenSCAPCustomization {
 	}
 	return c.OpenSCAP
 }
+
+func (f *FDOCustomization) HasFDO() bool {
+	return f != nil
+}
diff --git a/internal/client/blueprints.go b/internal/client/blueprints.go
index 15731462b..2c1003fac 100644
--- a/internal/client/blueprints.go
+++ b/internal/client/blueprints.go
@@ -8,6 +8,7 @@ import (
 	"net/http"
 	"strings"
 
+	"github.com/osbuild/osbuild-composer/internal/blueprint"
 	"github.com/osbuild/osbuild-composer/internal/weldr"
 )
 
@@ -121,6 +122,21 @@ func GetBlueprintsChangesV0(socket *http.Client, bpNames []string) (weldr.Bluepr
 	return changes, nil, nil
 }
 
+// GetBlueprintChangeV1 returns a specific blueprint change
+func GetBlueprintChangeV1(socket *http.Client, name, commit string) (blueprint.Blueprint, *APIResponse, error) {
+	route := fmt.Sprintf("/api/v1/blueprints/change/%s/%s", name, commit)
+	body, resp, err := GetRaw(socket, "GET", route)
+	if resp != nil || err != nil {
+		return blueprint.Blueprint{}, resp, err
+	}
+	var bp blueprint.Blueprint
+	err = json.Unmarshal(body, &bp)
+	if err != nil {
+		return blueprint.Blueprint{}, nil, err
+	}
+	return bp, nil, nil
+}
+
 // UndoBlueprintChangeV0 reverts a blueprint to a previous commit
 func UndoBlueprintChangeV0(socket *http.Client, blueprint, commit string) (*APIResponse, error) {
 	request := fmt.Sprintf("/api/v0/blueprints/undo/%s/%s", blueprint, commit)
diff --git a/internal/distro/rhel8/distro.go b/internal/distro/rhel8/distro.go
index 5c6ef191c..f7b8a4bb5 100644
--- a/internal/distro/rhel8/distro.go
+++ b/internal/distro/rhel8/distro.go
@@ -606,24 +606,24 @@ func (t *imageType) checkOptions(customizations *blueprint.Customizations, optio
 			if customizations.GetInstallationDevice() == "" {
 				return fmt.Errorf("boot ISO image type %q requires specifying an installation device to install to", t.name)
 			}
-			if customizations.GetFDO() == nil {
-				return fmt.Errorf("boot ISO image type %q requires specifying FDO configuration to install to", t.name)
-			}
-			if customizations.GetFDO().ManufacturingServerURL == "" {
-				return fmt.Errorf("boot ISO image type %q requires specifying FDO.ManufacturingServerURL configuration to install to", t.name)
-			}
-			var diunSet int
-			if customizations.GetFDO().DiunPubKeyHash != "" {
-				diunSet++
-			}
-			if customizations.GetFDO().DiunPubKeyInsecure != "" {
-				diunSet++
-			}
-			if customizations.GetFDO().DiunPubKeyRootCerts != "" {
-				diunSet++
-			}
-			if diunSet != 1 {
-				return fmt.Errorf("boot ISO image type %q requires specifying one of [FDO.DiunPubKeyHash,FDO.DiunPubKeyInsecure,FDO.DiunPubKeyRootCerts] configuration to install to", t.name)
+			//making fdo optional so that simplified installer can be composed w/o the FDO section in the blueprint
+			if customizations.GetFDO() != nil {
+				if customizations.GetFDO().ManufacturingServerURL == "" {
+					return fmt.Errorf("boot ISO image type %q requires specifying FDO.ManufacturingServerURL configuration to install to", t.name)
+				}
+				var diunSet int
+				if customizations.GetFDO().DiunPubKeyHash != "" {
+					diunSet++
+				}
+				if customizations.GetFDO().DiunPubKeyInsecure != "" {
+					diunSet++
+				}
+				if customizations.GetFDO().DiunPubKeyRootCerts != "" {
+					diunSet++
+				}
+				if diunSet != 1 {
+					return fmt.Errorf("boot ISO image type %q requires specifying one of [FDO.DiunPubKeyHash,FDO.DiunPubKeyInsecure,FDO.DiunPubKeyRootCerts] configuration to install to", t.name)
+				}
 			}
 		} else if t.name == "edge-installer" {
 			allowed := []string{"User", "Group"}
diff --git a/internal/distro/rhel8/pipelines.go b/internal/distro/rhel8/pipelines.go
index f531e247e..124d5684c 100644
--- a/internal/distro/rhel8/pipelines.go
+++ b/internal/distro/rhel8/pipelines.go
@@ -899,7 +899,7 @@ func simplifiedInstallerTreePipeline(repos []rpmmd.RepoConfig, packages []rpmmd.
 		"coreos-installer",
 		"fdo",
 	})
-	if fdo.DiunPubKeyRootCerts != "" {
+	if fdo.HasFDO() && fdo.DiunPubKeyRootCerts != "" {
 		p.AddStage(osbuild.NewFDOStageForRootCerts(fdo.DiunPubKeyRootCerts))
 		dracutStageOptions.Install = []string{"/fdo_diun_pub_key_root_certs.pem"}
 	}
diff --git a/internal/distro/rhel8/stage_options.go b/internal/distro/rhel8/stage_options.go
index e9e57c2a6..873b2d469 100644
--- a/internal/distro/rhel8/stage_options.go
+++ b/internal/distro/rhel8/stage_options.go
@@ -236,17 +236,18 @@ func grubISOStageOptions(installDevice, kernelVer, arch, vendor, product, osVers
 		Vendor:        vendor,
 	}
 
-	grubISOStageOptions.Kernel.Opts = append(grubISOStageOptions.Kernel.Opts, "fdo.manufacturing_server_url="+fdo.ManufacturingServerURL)
-	if fdo.DiunPubKeyInsecure != "" {
-		grubISOStageOptions.Kernel.Opts = append(grubISOStageOptions.Kernel.Opts, "fdo.diun_pub_key_insecure="+fdo.DiunPubKeyInsecure)
-	}
-	if fdo.DiunPubKeyHash != "" {
-		grubISOStageOptions.Kernel.Opts = append(grubISOStageOptions.Kernel.Opts, "fdo.diun_pub_key_hash="+fdo.DiunPubKeyHash)
-	}
-	if fdo.DiunPubKeyRootCerts != "" {
-		grubISOStageOptions.Kernel.Opts = append(grubISOStageOptions.Kernel.Opts, "fdo.diun_pub_key_root_certs=/fdo_diun_pub_key_root_certs.pem")
+	if fdo.HasFDO() {
+		grubISOStageOptions.Kernel.Opts = append(grubISOStageOptions.Kernel.Opts, "fdo.manufacturing_server_url="+fdo.ManufacturingServerURL)
+		if fdo.DiunPubKeyInsecure != "" {
+			grubISOStageOptions.Kernel.Opts = append(grubISOStageOptions.Kernel.Opts, "fdo.diun_pub_key_insecure="+fdo.DiunPubKeyInsecure)
+		}
+		if fdo.DiunPubKeyHash != "" {
+			grubISOStageOptions.Kernel.Opts = append(grubISOStageOptions.Kernel.Opts, "fdo.diun_pub_key_hash="+fdo.DiunPubKeyHash)
+		}
+		if fdo.DiunPubKeyRootCerts != "" {
+			grubISOStageOptions.Kernel.Opts = append(grubISOStageOptions.Kernel.Opts, "fdo.diun_pub_key_root_certs=/fdo_diun_pub_key_root_certs.pem")
+		}
 	}
-
 	return grubISOStageOptions
 }
 
diff --git a/internal/distro/rhel9/distro.go b/internal/distro/rhel9/distro.go
index 95f033e0d..c9e4952db 100644
--- a/internal/distro/rhel9/distro.go
+++ b/internal/distro/rhel9/distro.go
@@ -572,24 +572,24 @@ func (t *imageType) checkOptions(customizations *blueprint.Customizations, optio
 			if customizations.GetInstallationDevice() == "" {
 				return fmt.Errorf("boot ISO image type %q requires specifying an installation device to install to", t.name)
 			}
-			if customizations.GetFDO() == nil {
-				return fmt.Errorf("boot ISO image type %q requires specifying FDO configuration to install to", t.name)
-			}
-			if customizations.GetFDO().ManufacturingServerURL == "" {
-				return fmt.Errorf("boot ISO image type %q requires specifying FDO.ManufacturingServerURL configuration to install to", t.name)
-			}
-			var diunSet int
-			if customizations.GetFDO().DiunPubKeyHash != "" {
-				diunSet++
-			}
-			if customizations.GetFDO().DiunPubKeyInsecure != "" {
-				diunSet++
-			}
-			if customizations.GetFDO().DiunPubKeyRootCerts != "" {
-				diunSet++
-			}
-			if diunSet != 1 {
-				return fmt.Errorf("boot ISO image type %q requires specifying one of [FDO.DiunPubKeyHash,FDO.DiunPubKeyInsecure,FDO.DiunPubKeyRootCerts] configuration to install to", t.name)
+			//making fdo optional so that simplified installer can be composed w/o the FDO section in the blueprint
+			if customizations.GetFDO() != nil {
+				if customizations.GetFDO().ManufacturingServerURL == "" {
+					return fmt.Errorf("boot ISO image type %q requires specifying FDO.ManufacturingServerURL configuration to install to", t.name)
+				}
+				var diunSet int
+				if customizations.GetFDO().DiunPubKeyHash != "" {
+					diunSet++
+				}
+				if customizations.GetFDO().DiunPubKeyInsecure != "" {
+					diunSet++
+				}
+				if customizations.GetFDO().DiunPubKeyRootCerts != "" {
+					diunSet++
+				}
+				if diunSet != 1 {
+					return fmt.Errorf("boot ISO image type %q requires specifying one of [FDO.DiunPubKeyHash,FDO.DiunPubKeyInsecure,FDO.DiunPubKeyRootCerts] configuration to install to", t.name)
+				}
 			}
 		} else if t.name == "edge-installer" {
 			allowed := []string{"User", "Group"}
diff --git a/internal/distro/rhel9/pipelines.go b/internal/distro/rhel9/pipelines.go
index bee95b699..76fefd783 100644
--- a/internal/distro/rhel9/pipelines.go
+++ b/internal/distro/rhel9/pipelines.go
@@ -898,7 +898,7 @@ func simplifiedInstallerTreePipeline(repos []rpmmd.RepoConfig, packages []rpmmd.
 		"coreos-installer",
 		"fdo",
 	})
-	if fdo.DiunPubKeyRootCerts != "" {
+	if fdo.HasFDO() && fdo.DiunPubKeyRootCerts != "" {
 		p.AddStage(osbuild.NewFDOStageForRootCerts(fdo.DiunPubKeyRootCerts))
 		dracutStageOptions.Install = []string{"/fdo_diun_pub_key_root_certs.pem"}
 	}
diff --git a/internal/distro/rhel9/stage_options.go b/internal/distro/rhel9/stage_options.go
index 7b0a932b0..38343e790 100644
--- a/internal/distro/rhel9/stage_options.go
+++ b/internal/distro/rhel9/stage_options.go
@@ -235,16 +235,17 @@ func grubISOStageOptions(installDevice, kernelVer, arch, vendor, product, osVers
 		Architectures: architectures,
 		Vendor:        vendor,
 	}
-
-	grubISOStageOptions.Kernel.Opts = append(grubISOStageOptions.Kernel.Opts, "fdo.manufacturing_server_url="+fdo.ManufacturingServerURL)
-	if fdo.DiunPubKeyInsecure != "" {
-		grubISOStageOptions.Kernel.Opts = append(grubISOStageOptions.Kernel.Opts, "fdo.diun_pub_key_insecure="+fdo.DiunPubKeyInsecure)
-	}
-	if fdo.DiunPubKeyHash != "" {
-		grubISOStageOptions.Kernel.Opts = append(grubISOStageOptions.Kernel.Opts, "fdo.diun_pub_key_hash="+fdo.DiunPubKeyHash)
-	}
-	if fdo.DiunPubKeyRootCerts != "" {
-		grubISOStageOptions.Kernel.Opts = append(grubISOStageOptions.Kernel.Opts, "fdo.diun_pub_key_root_certs=/fdo_diun_pub_key_root_certs.pem")
+	if fdo.HasFDO() {
+		grubISOStageOptions.Kernel.Opts = append(grubISOStageOptions.Kernel.Opts, "fdo.manufacturing_server_url="+fdo.ManufacturingServerURL)
+		if fdo.DiunPubKeyInsecure != "" {
+			grubISOStageOptions.Kernel.Opts = append(grubISOStageOptions.Kernel.Opts, "fdo.diun_pub_key_insecure="+fdo.DiunPubKeyInsecure)
+		}
+		if fdo.DiunPubKeyHash != "" {
+			grubISOStageOptions.Kernel.Opts = append(grubISOStageOptions.Kernel.Opts, "fdo.diun_pub_key_hash="+fdo.DiunPubKeyHash)
+		}
+		if fdo.DiunPubKeyRootCerts != "" {
+			grubISOStageOptions.Kernel.Opts = append(grubISOStageOptions.Kernel.Opts, "fdo.diun_pub_key_root_certs=/fdo_diun_pub_key_root_certs.pem")
+		}
 	}
 
 	return grubISOStageOptions
diff --git a/internal/store/json.go b/internal/store/json.go
index 096d73334..9815b0170 100644
--- a/internal/store/json.go
+++ b/internal/store/json.go
@@ -71,11 +71,11 @@ type sourceV0 struct {
 type sourcesV0 map[string]sourceV0
 
 type changeV0 struct {
-	Commit    string `json:"commit"`
-	Message   string `json:"message"`
-	Revision  *int   `json:"revision"`
-	Timestamp string `json:"timestamp"`
-	// BUG: We are currently not (un)marshalling the Blueprint field.
+	Commit    string              `json:"commit"`
+	Message   string              `json:"message"`
+	Revision  *int                `json:"revision"`
+	Timestamp string              `json:"timestamp"`
+	Blueprint blueprint.Blueprint `json:"blueprint"`
 }
 
 type changesV0 map[string]map[string]changeV0
@@ -203,6 +203,7 @@ func newChangesFromV0(changesStruct changesV0) map[string]map[string]blueprint.C
 				Message:   change.Message,
 				Revision:  change.Revision,
 				Timestamp: change.Timestamp,
+				Blueprint: change.Blueprint,
 			}
 		}
 		changes[name] = commits
@@ -332,6 +333,7 @@ func newChangesV0(changes map[string]map[string]blueprint.Change) changesV0 {
 				Message:   change.Message,
 				Revision:  change.Revision,
 				Timestamp: change.Timestamp,
+				Blueprint: change.Blueprint,
 			}
 		}
 		changesStruct[name] = commitsStruct
diff --git a/internal/weldr/api.go b/internal/weldr/api.go
index 032f786c2..8091a44e1 100644
--- a/internal/weldr/api.go
+++ b/internal/weldr/api.go
@@ -236,6 +236,7 @@ func setupRouter(api *API) *API {
 	api.router.GET("/api/v:version/blueprints/depsolve/*blueprints", api.blueprintsDepsolveHandler)
 	api.router.GET("/api/v:version/blueprints/freeze/*blueprints", api.blueprintsFreezeHandler)
 	api.router.GET("/api/v:version/blueprints/diff/:blueprint/:from/:to", api.blueprintsDiffHandler)
+	api.router.GET("/api/v:version/blueprints/change/:blueprint/:commit", api.blueprintsChangeHandler)
 	api.router.GET("/api/v:version/blueprints/changes/*blueprints", api.blueprintsChangesHandler)
 	api.router.POST("/api/v:version/blueprints/new", api.blueprintsNewHandler)
 	api.router.POST("/api/v:version/blueprints/workspace", api.blueprintsWorkspaceHandler)
@@ -1805,6 +1806,70 @@ func (api *API) blueprintsDiffHandler(writer http.ResponseWriter, request *http.
 	common.PanicOnError(err)
 }
 
+// blueprintsChangeHandler returns a specific change to a blueprint
+func (api *API) blueprintsChangeHandler(writer http.ResponseWriter, request *http.Request, params httprouter.Params) {
+	if !verifyRequestVersion(writer, params, 1) {
+		return
+	}
+	name := params.ByName("blueprint")
+	if !verifyStringsWithRegex(writer, []string{name}, ValidBlueprintName) {
+		return
+	}
+
+	commit := params.ByName("commit")
+	if !verifyStringsWithRegex(writer, []string{commit}, ValidBlueprintName) {
+		return
+	}
+
+	bpChange, err := api.store.GetBlueprintChange(name, commit)
+	if err != nil {
+		errors := responseError{
+			ID:  "UnknownCommit",
+			Msg: err.Error(),
+		}
+		statusResponseError(writer, http.StatusBadRequest, errors)
+		return
+	}
+
+	bp := bpChange.Blueprint
+	if len(bpChange.Blueprint.Name) == 0 {
+		errors := responseError{
+			ID:  "BlueprintsError",
+			Msg: fmt.Sprintf("no blueprint found for commit %s", commit),
+		}
+		statusResponseError(writer, http.StatusBadRequest, errors)
+		return
+	}
+
+	query, err := url.ParseQuery(request.URL.RawQuery)
+	if err != nil {
+		errors := responseError{
+			ID:  "InvalidChars",
+			Msg: fmt.Sprintf("invalid query string: %v", err),
+		}
+		statusResponseError(writer, http.StatusBadRequest, errors)
+		return
+	}
+
+	format := query.Get("format")
+	if format == "json" || format == "" {
+		err := json.NewEncoder(writer).Encode(bp)
+		common.PanicOnError(err)
+	} else if format == "toml" {
+		encoder := toml.NewEncoder(writer)
+		encoder.Indent = ""
+		err := encoder.Encode(bp)
+		common.PanicOnError(err)
+	} else {
+		errors := responseError{
+			ID:  "InvalidChars",
+			Msg: fmt.Sprintf("invalid format parameter: %s", format),
+		}
+		statusResponseError(writer, http.StatusBadRequest, errors)
+		return
+	}
+}
+
 func (api *API) blueprintsChangesHandler(writer http.ResponseWriter, request *http.Request, params httprouter.Params) {
 	if !verifyRequestVersion(writer, params, 0) {
 		return
diff --git a/osbuild-composer.spec b/osbuild-composer.spec
index 990aeb1c7..21c4c41b4 100644
--- a/osbuild-composer.spec
+++ b/osbuild-composer.spec
@@ -326,8 +326,9 @@ The worker for osbuild-composer
 %preun worker
 # systemd_preun uses systemctl disable --now which doesn't work well with template services.
 # See https://github.com/systemd/systemd/issues/15620
-# The following lines mimicks its behaviour by running two commands:
-if [ -d /run/systemd/system ]; then
+# The following lines mimicks its behaviour by running two commands.
+# The scriptlet is supposed to run only when the package is being removed.
+if [ $1 -eq 0 ] && [ -d /run/systemd/system ]; then
     # disable and stop all the worker services
     systemctl --no-reload disable osbuild-worker@.service osbuild-remote-worker@.service
     systemctl stop "osbuild-worker@*.service" "osbuild-remote-worker@*.service"
diff --git a/schutzbot/terraform b/schutzbot/terraform
index 81d870ad8..4d0d19669 100644
--- a/schutzbot/terraform
+++ b/schutzbot/terraform
@@ -1,1 +1,1 @@
-0896913e87404485295bd2d19261fa1a4379971e
+79e0bf92fa92a143a703acc795d4c9cfc8001e66


Test patch:
diff --git a/internal/client/blueprints_test.go b/internal/client/blueprints_test.go
index 75e795916..d53d1552c 100644
--- a/internal/client/blueprints_test.go
+++ b/internal/client/blueprints_test.go
@@ -1082,4 +1082,54 @@ func TestFreezeInvalidBlueprintV0(t *testing.T) {
 	require.Contains(t, api.Errors[0].Msg, "Invalid characters in API path")
 }
 
-// TODO diff of blueprint changes
+// Make several changes to a blueprint and get the oldest change by commit hash
+func TestBlueprintChangeV1(t *testing.T) {
+	bps := []string{`{
+		"name": "test-blueprint-change-v1",
+		"description": "CheckBlueprintChangeV1",
+		"version": "0.0.1",
+		"packages": [{"name": "bash", "version": "*"}],
+		"modules": [{"name": "util-linux", "version": "*"}]
+	}`,
+		`{
+		"name": "test-blueprint-change-v1",
+		"description": "CheckBlueprintChangeV1",
+		"version": "0.1.0",
+		"packages": [{"name": "bash", "version": "*"}, {"name": "tmux", "version": "*"}],
+		"modules": [{"name": "util-linux", "version": "*"}],
+		"customizations": {"user": [{"name": "root", "password": "qweqweqwe"}]}
+	}`,
+		`{
+		"name": "test-blueprint-change-v1",
+		"description": "CheckBlueprintChangeV1",
+		"version": "0.1.1",
+		"packages": [{"name": "bash", "version": "*"}, {"name": "tmux", "version": "*"}],
+		"modules": [],
+		"customizations": {"user": [{"name": "root", "password": "asdasdasd"}]}
+	}`}
+
+	// Push 3 changes to the blueprint
+	for i := range bps {
+		resp, err := PostJSONBlueprintV0(testState.socket, bps[i])
+		require.NoError(t, err, "POST blueprint #%d failed with a client error")
+		require.True(t, resp.Status, "POST blueprint #%d failed: %#v", i, resp)
+	}
+
+	// List the changes
+	changes, api, err := GetBlueprintsChangesV0(testState.socket, []string{"test-blueprint-change-v1"})
+	require.NoError(t, err, "GET blueprint failed with a client error")
+	require.Nil(t, api, "GetBlueprintsChanges failed: %#v", api)
+	require.Equal(t, 1, len(changes.BlueprintsChanges), "No changes returned")
+	require.Equal(t, "test-blueprint-change-v1", changes.BlueprintsChanges[0].Name, "Wrong blueprint changes returned")
+	require.Greater(t, len(changes.BlueprintsChanges[0].Changes), 2, "Wrong number of changes returned")
+
+	// Get the oldest commit
+	commit := changes.BlueprintsChanges[0].Changes[2].Commit
+	t.Logf("commit = %s", commit)
+	t.Logf("changes = %v", changes)
+	bp, api, err := GetBlueprintChangeV1(testState.socket, "test-blueprint-change-v1", commit)
+	require.NoError(t, err, "GET blueprint change failed with a client error")
+	require.Nil(t, api, "GET blueprint change failed: %#v", api)
+	require.NotNil(t, bp, "GET blueprint change failed: missing blueprint")
+	assert.Equal(t, bp.Version, "0.0.1")
+}
diff --git a/internal/store/json_test.go b/internal/store/json_test.go
index 89846a101..03b64e876 100644
--- a/internal/store/json_test.go
+++ b/internal/store/json_test.go
@@ -699,6 +699,69 @@ func Test_newChangesFromV0(t *testing.T) {
 					},
 				},
 			},
+		},
+		{
+			name: "Blueprint Changes With Blueprint",
+			changes: changesV0{
+				"test-blueprint-changes-1": {
+					"4774980638f4162d9909a613c3ccd938e60bb3a9": {
+						Commit:    "4774980638f4162d9909a613c3ccd938e60bb3a9",
+						Message:   "Recipe test-blueprint-changes-1, version 0.1.2 saved.",
+						Revision:  nil,
+						Timestamp: "2020-07-29T09:52:07Z",
+						Blueprint: blueprint.Blueprint{
+							Name:    "test-blueprint-changes-1",
+							Version: "0.1.2",
+						},
+					},
+					"72fdb76b9994bd72770e283bf3a5206756daf497": {
+						Commit:    "72fdb76b9994bd72770e283bf3a5206756daf497",
+						Message:   "Recipe test-blueprint-changes-1, version 0.1.1 saved.",
+						Revision:  nil,
+						Timestamp: "2020-07-09T13:33:06Z",
+						Blueprint: blueprint.Blueprint{
+							Name:    "test-blueprint-changes-1",
+							Version: "0.1.1",
+						},
+					},
+					"79e2043a83637ffdd4db078c6da23deaae09c84b": {
+						Commit:    "79e2043a83637ffdd4db078c6da23deaae09c84b",
+						Message:   "Recipe test-blueprint-changes-1, version 0.0.1 saved.",
+						Revision:  nil,
+						Timestamp: "2020-07-07T02:57:00Z",
+					},
+				},
+			},
+			want: map[string]map[string]blueprint.Change{
+				"test-blueprint-changes-1": {
+					"4774980638f4162d9909a613c3ccd938e60bb3a9": {
+						Commit:    "4774980638f4162d9909a613c3ccd938e60bb3a9",
+						Message:   "Recipe test-blueprint-changes-1, version 0.1.2 saved.",
+						Revision:  nil,
+						Timestamp: "2020-07-29T09:52:07Z",
+						Blueprint: blueprint.Blueprint{
+							Name:    "test-blueprint-changes-1",
+							Version: "0.1.2",
+						},
+					},
+					"72fdb76b9994bd72770e283bf3a5206756daf497": {
+						Commit:    "72fdb76b9994bd72770e283bf3a5206756daf497",
+						Message:   "Recipe test-blueprint-changes-1, version 0.1.1 saved.",
+						Revision:  nil,
+						Timestamp: "2020-07-09T13:33:06Z",
+						Blueprint: blueprint.Blueprint{
+							Name:    "test-blueprint-changes-1",
+							Version: "0.1.1",
+						},
+					},
+					"79e2043a83637ffdd4db078c6da23deaae09c84b": {
+						Commit:    "79e2043a83637ffdd4db078c6da23deaae09c84b",
+						Message:   "Recipe test-blueprint-changes-1, version 0.0.1 saved.",
+						Revision:  nil,
+						Timestamp: "2020-07-07T02:57:00Z",
+					},
+				},
+			},
 		}}
 	for _, tt := range tests {
 		t.Run(tt.name, func(t *testing.T) {
@@ -818,6 +881,69 @@ func Test_newChangesV0(t *testing.T) {
 				},
 			},
 		},
+		{
+			name: "Blueprint Changes With Blueprint",
+			changes: map[string]map[string]blueprint.Change{
+				"test-blueprint-changes-1": {
+					"4774980638f4162d9909a613c3ccd938e60bb3a9": {
+						Commit:    "4774980638f4162d9909a613c3ccd938e60bb3a9",
+						Message:   "Recipe test-blueprint-changes-1, version 0.1.2 saved.",
+						Revision:  nil,
+						Timestamp: "2020-07-29T09:52:07Z",
+						Blueprint: blueprint.Blueprint{
+							Name:    "test-blueprint-changes-1",
+							Version: "0.1.2",
+						},
+					},
+					"72fdb76b9994bd72770e283bf3a5206756daf497": {
+						Commit:    "72fdb76b9994bd72770e283bf3a5206756daf497",
+						Message:   "Recipe test-blueprint-changes-1, version 0.1.1 saved.",
+						Revision:  nil,
+						Timestamp: "2020-07-09T13:33:06Z",
+						Blueprint: blueprint.Blueprint{
+							Name:    "test-blueprint-changes-1",
+							Version: "0.1.1",
+						},
+					},
+					"79e2043a83637ffdd4db078c6da23deaae09c84b": {
+						Commit:    "79e2043a83637ffdd4db078c6da23deaae09c84b",
+						Message:   "Recipe test-blueprint-changes-1, version 0.0.1 saved.",
+						Revision:  nil,
+						Timestamp: "2020-07-07T02:57:00Z",
+					},
+				},
+			},
+			want: changesV0{
+				"test-blueprint-changes-1": {
+					"4774980638f4162d9909a613c3ccd938e60bb3a9": {
+						Commit:    "4774980638f4162d9909a613c3ccd938e60bb3a9",
+						Message:   "Recipe test-blueprint-changes-1, version 0.1.2 saved.",
+						Revision:  nil,
+						Timestamp: "2020-07-29T09:52:07Z",
+						Blueprint: blueprint.Blueprint{
+							Name:    "test-blueprint-changes-1",
+							Version: "0.1.2",
+						},
+					},
+					"72fdb76b9994bd72770e283bf3a5206756daf497": {
+						Commit:    "72fdb76b9994bd72770e283bf3a5206756daf497",
+						Message:   "Recipe test-blueprint-changes-1, version 0.1.1 saved.",
+						Revision:  nil,
+						Timestamp: "2020-07-09T13:33:06Z",
+						Blueprint: blueprint.Blueprint{
+							Name:    "test-blueprint-changes-1",
+							Version: "0.1.1",
+						},
+					},
+					"79e2043a83637ffdd4db078c6da23deaae09c84b": {
+						Commit:    "79e2043a83637ffdd4db078c6da23deaae09c84b",
+						Message:   "Recipe test-blueprint-changes-1, version 0.0.1 saved.",
+						Revision:  nil,
+						Timestamp: "2020-07-07T02:57:00Z",
+					},
+				},
+			},
+		},
 	}
 	for _, tt := range tests {
 		t.Run(tt.name, func(t *testing.T) {
diff --git a/internal/weldr/api_test.go b/internal/weldr/api_test.go
index 62c79286c..44fa16f4e 100644
--- a/internal/weldr/api_test.go
+++ b/internal/weldr/api_test.go
@@ -792,6 +792,42 @@ func TestBlueprintsChanges(t *testing.T) {
 	test.SendHTTP(api, true, "DELETE", "/api/v0/blueprints/delete/"+id, ``)
 }
 
+// TestBlueprintChange tests getting a single blueprint commit
+func TestBlueprintChange(t *testing.T) {
+	api, _ := createWeldrAPI(t.TempDir(), rpmmd_mock.BaseFixture)
+	rand.Seed(time.Now().UnixNano())
+	// math/rand is good enough in this case
+	/* #nosec G404 */
+	id := strconv.Itoa(rand.Int())
+
+	test.SendHTTP(api, true, "POST", "/api/v0/blueprints/new", `{"name":"`+id+`","description":"Test","packages":[{"name":"httpd","version":"2.4.*"}],"version":"0.0.1"}`)
+	test.SendHTTP(api, true, "POST", "/api/v0/blueprints/new", `{"name":"`+id+`","description":"Test","packages":[{"name":"httpd","version":"2.4.*"}],"version":"0.0.2"}`)
+
+	resp := test.SendHTTP(api, true, "GET", "/api/v0/blueprints/changes/"+id, ``)
+	body, err := ioutil.ReadAll(resp.Body)
+	require.Nil(t, err)
+	require.Equal(t, http.StatusOK, resp.StatusCode)
+
+	var changes BlueprintsChangesV0
+	err = json.Unmarshal(body, &changes)
+	require.Nil(t, err)
+	require.Equal(t, 1, len(changes.BlueprintsChanges))
+	require.Equal(t, 2, len(changes.BlueprintsChanges[0].Changes))
+	commit := changes.BlueprintsChanges[0].Changes[1].Commit
+
+	// Get the blueprint's oldest commit
+	route := fmt.Sprintf("/api/v1/blueprints/change/%s/%s", id, commit)
+	resp = test.SendHTTP(api, true, "GET", route, ``)
+	body, err = ioutil.ReadAll(resp.Body)
+	require.Nil(t, err)
+	require.Equal(t, http.StatusOK, resp.StatusCode)
+
+	var bp blueprint.Blueprint
+	err = json.Unmarshal(body, &bp)
+	require.Nil(t, err)
+	require.Equal(t, "0.0.1", bp.Version)
+}
+
 func TestBlueprintsDepsolve(t *testing.T) {
 	var cases = []struct {
 		Fixture        rpmmd_mock.FixtureGenerator
diff --git a/test/cases/ostree-simplified-installer.sh b/test/cases/ostree-simplified-installer.sh
index f883c063f..bfb15973c 100755
--- a/test/cases/ostree-simplified-installer.sh
+++ b/test/cases/ostree-simplified-installer.sh
@@ -12,6 +12,7 @@ source /usr/libexec/tests/osbuild-composer/shared_lib.sh
 
 # Start firewalld
 sudo systemctl enable --now firewalld
+sudo pip3 install yq
 
 # Start libvirtd and test it.
 greenprint "🚀 Starting libvirt daemon"
@@ -81,6 +82,7 @@ CONTAINER_TYPE=edge-container
 CONTAINER_FILENAME=container.tar
 INSTALLER_TYPE=edge-simplified-installer
 INSTALLER_FILENAME=simplified-installer.iso
+MEMORY=3072
 
 # Set up temporary files.
 TEMPDIR=$(mktemp -d)
@@ -210,12 +212,12 @@ wait_for_ssh_up () {
 clean_up () {
     greenprint "🧼 Cleaning up"
 
-    # Clear vm
-    if [[ $(sudo virsh domstate "${IMAGE_KEY}-fdorootcert") == "running" ]]; then
-        sudo virsh destroy "${IMAGE_KEY}-fdorootcert"
+    # Clean up BIOS VM
+    greenprint "🧹 Clean up BIOS VM"
+    if [[ $(sudo virsh domstate "${IMAGE_KEY}-simplified_iso_without_fdo") == "running" ]]; then
+        sudo virsh destroy "${IMAGE_KEY}-simplified_iso_without_fdo"
     fi
-    sudo virsh undefine "${IMAGE_KEY}-fdorootcert" --nvram
-    # Remove qcow2 file.
+    sudo virsh undefine "${IMAGE_KEY}-simplified_iso_without_fdo" --nvram
     sudo virsh vol-delete --pool images "$LIBVIRT_IMAGE_PATH"
 
     # Remove any status containers if exist
@@ -291,6 +293,8 @@ sudo podman run -v "$PWD"/aio/:/aio:z \
 
 # TODO: tweak config aio/configs/serviceinfo_api_server.yml to test basic FDO functionalities
 #       like adding user/key/pwd, re-encryption, files, commands etc etc
+# TODO: check tpm2 config
+sudo /usr/local/bin/yq -iy '.service_info.diskencryption_clevis |= [{disk_label: "/dev/vda4", reencrypt: true, binding: {pin: "tpm2", config: "{}"}}]' aio/configs/serviceinfo_api_server.yml
 
 greenprint "🔧 Prepare fdo AIO manufacturing DIUN"
 DIUN_PUB_KEY_ROOT_CERTS=$(sudo cat aio/keys/diun_cert.pem)
@@ -457,7 +461,7 @@ sudo podman ps -a
 greenprint "📋 Install edge vm via http boot"
 sudo virt-install --name="${IMAGE_KEY}-http"\
                   --disk path="${LIBVIRT_IMAGE_PATH}",format=qcow2 \
-                  --ram 3072 \
+                  --ram "${MEMORY}" \
                   --vcpus 2 \
                   --network network=integration,mac=34:49:22:B0:83:30 \
                   --os-type linux \
@@ -523,6 +527,9 @@ if [[ $(sudo virsh domstate "${IMAGE_KEY}-http") == "running" ]]; then
 fi
 sudo virsh undefine "${IMAGE_KEY}-http" --nvram
 sudo virsh vol-delete --pool images "$LIBVIRT_IMAGE_PATH"
+sudo umount /mnt/installer
+sudo rm -rf /mnt/installer "${ISO_FILENAME}"
+sudo rm -rf "/var/lib/libvirt/images/${ISO_FILENAME}"
 
 ####################################################################
 ##
@@ -578,7 +585,7 @@ sudo qemu-img create -f qcow2 "${LIBVIRT_IMAGE_PATH}" 20G
 greenprint "💿 Install ostree image via installer(ISO) on UEFI VM"
 sudo virt-install  --name="${IMAGE_KEY}-fdosshkey"\
                    --disk path="${LIBVIRT_IMAGE_PATH}",format=qcow2 \
-                   --ram 3072 \
+                   --ram "${MEMORY}" \
                    --vcpus 2 \
                    --network network=integration,mac=34:49:22:B0:83:31 \
                    --os-type linux \
@@ -700,7 +707,7 @@ sudo qemu-img create -f qcow2 "${LIBVIRT_IMAGE_PATH}" 20G
 greenprint "💿 Install ostree image via installer(ISO) on UEFI VM"
 sudo virt-install  --name="${IMAGE_KEY}-fdorootcert"\
                    --disk path="${LIBVIRT_IMAGE_PATH}",format=qcow2 \
-                   --ram 3072 \
+                   --ram "${MEMORY}" \
                    --vcpus 2 \
                    --network network=integration,mac=34:49:22:B0:83:32 \
                    --os-type linux \
@@ -759,6 +766,123 @@ EOF
 sudo ansible-playbook -v -i "${TEMPDIR}"/inventory -e image_type=redhat -e ostree_commit="${INSTALL_HASH}" -e fdo_credential="true" /usr/share/tests/osbuild-composer/ansible/check_ostree.yaml || RESULTS=0
 check_result
 
+greenprint "🧹 Clean up VM"
+if [[ $(sudo virsh domstate "${IMAGE_KEY}-fdorootcert") == "running" ]]; then
+    sudo virsh destroy "${IMAGE_KEY}-fdorootcert"
+fi
+sudo virsh undefine "${IMAGE_KEY}-fdorootcert" --nvram
+sudo virsh vol-delete --pool images "$LIBVIRT_IMAGE_PATH"
+
+##################################################################
+##
+## Build edge-simplified-installer without FDO 
+##
+##################################################################
+
+tee "$BLUEPRINT_FILE" > /dev/null << EOF
+name = "simplified_iso_without_fdo"
+description = "A rhel-edge simplified-installer image without FDO"
+version = "0.0.1"
+modules = []
+groups = []
+
+[customizations]
+installation_device = "/dev/vda"
+
+EOF
+
+greenprint "📄 simplified_iso_without_fdo blueprint"
+cat "$BLUEPRINT_FILE"
+
+# Prepare the blueprint for the compose.
+greenprint "📋 Preparing installer blueprint"
+sudo composer-cli blueprints push "$BLUEPRINT_FILE"
+sudo composer-cli blueprints depsolve simplified_iso_without_fdo
+
+# Build simplified installer iso image.
+build_image simplified_iso_without_fdo "${INSTALLER_TYPE}" "${PROD_REPO_URL}/"
+
+# Download the image
+greenprint "📥 Downloading the simplified_iso_without_fdo image"
+sudo composer-cli compose image "${COMPOSE_ID}" > /dev/null
+ISO_FILENAME="${COMPOSE_ID}-${INSTALLER_FILENAME}"
+sudo cp "${ISO_FILENAME}" /var/lib/libvirt/images
+
+# Clean compose and blueprints.
+greenprint "🧹 Clean up simplified_iso_without_fdo blueprint and compose"
+sudo composer-cli compose delete "${COMPOSE_ID}" > /dev/null
+sudo composer-cli blueprints delete simplified_iso_without_fdo > /dev/null
+
+# Ensure SELinux is happy with our new images.
+greenprint "👿 Running restorecon on image directory"
+sudo restorecon -Rv /var/lib/libvirt/images/
+
+# Create qcow2 file for virt install.
+greenprint "🖥 Create qcow2 file for virt install"
+sudo qemu-img create -f qcow2 "${LIBVIRT_IMAGE_PATH}" 20G
+
+greenprint "💿 Install ostree image via installer(ISO) on UEFI VM"
+sudo virt-install  --name="${IMAGE_KEY}-simplified_iso_without_fdo"\
+                   --disk path="${LIBVIRT_IMAGE_PATH}",format=qcow2 \
+                   --ram 3072 \
+                   --vcpus 2 \
+                   --network network=integration,mac=34:49:22:B0:83:32 \
+                   --os-type linux \
+                   --os-variant ${OS_VARIANT} \
+                   --cdrom "/var/lib/libvirt/images/${ISO_FILENAME}" \
+                   --boot uefi,loader_ro=yes,loader_type=pflash,nvram_template=/usr/share/edk2/ovmf/OVMF_VARS.fd,loader_secure=no \
+                   --tpm backend.type=emulator,backend.version=2.0,model=tpm-crb \
+                   --nographics \
+                   --noautoconsole \
+                   --wait=15 \
+                   --noreboot
+
+# Installation can get stuck, destroying VM helps
+# See https://github.com/osbuild/osbuild-composer/issues/2413
+if [[ $(sudo virsh domstate "${IMAGE_KEY}-simplified_iso_without_fdo") == "running" ]]; then
+    sudo virsh destroy "${IMAGE_KEY}-simplified_iso_without_fdo"
+fi
+
+# Start VM.
+greenprint "💻 Start UEFI VM"
+sudo virsh start "${IMAGE_KEY}-simplified_iso_without_fdo"
+
+# Check for ssh ready to go.
+greenprint "🛃 Checking for SSH is ready to go"
+for LOOP_COUNTER in $(seq 0 30); do
+    RESULTS="$(wait_for_ssh_up $ROOT_CERT_GUEST_ADDRESS)"
+    if [[ $RESULTS == 1 ]]; then
+        echo "SSH is ready now! 🥳"
+        break
+    fi
+    sleep 10
+done
+
+# Check image installation result
+check_result
+
+greenprint "🕹 Get ostree install commit value"
+INSTALL_HASH=$(curl "${PROD_REPO_URL}/refs/heads/${OSTREE_REF}")
+
+# Add instance IP address into /etc/ansible/hosts
+sudo tee "${TEMPDIR}"/inventory > /dev/null << EOF
+[ostree_guest]
+${ROOT_CERT_GUEST_ADDRESS}
+
+[ostree_guest:vars]
+ansible_python_interpreter=/usr/bin/python3
+ansible_user=admin
+ansible_private_key_file=${SSH_KEY}
+ansible_ssh_common_args="-o StrictHostKeyChecking=no -o UserKnownHostsFile=/dev/null"
+ansible_become=yes 
+ansible_become_method=sudo
+ansible_become_pass=${EDGE_USER_PASSWORD}
+EOF
+
+# Test IoT/Edge OS
+sudo ansible-playbook -v -i "${TEMPDIR}"/inventory -e image_type=redhat -e ostree_commit="${INSTALL_HASH}" -e fdo_credential="false" /usr/share/tests/osbuild-composer/ansible/check_ostree.yaml || RESULTS=0
+check_result
+
 ########################
 ##
 ## Build upgrade image
@@ -890,7 +1014,7 @@ ansible_become_pass=${EDGE_USER_PASSWORD}
 EOF
 
 # Test IoT/Edge OS
-sudo ansible-playbook -v -i "${TEMPDIR}"/inventory -e image_type=redhat -e ostree_commit="${UPGRADE_HASH}" -e fdo_credential="true" /usr/share/tests/osbuild-composer/ansible/check_ostree.yaml || RESULTS=0
+sudo ansible-playbook -v -i "${TEMPDIR}"/inventory -e image_type=redhat -e ostree_commit="${UPGRADE_HASH}" -e fdo_credential="false" /usr/share/tests/osbuild-composer/ansible/check_ostree.yaml || RESULTS=0
 check_result
 
 # Final success clean up
diff --git a/test/cases/shared_lib.sh b/test/cases/shared_lib.sh
index 99c76a9dd..b38f55863 100755
--- a/test/cases/shared_lib.sh
+++ b/test/cases/shared_lib.sh
@@ -26,7 +26,7 @@ function get_build_info() {
     local fname="$2"
     if rpm -q --quiet weldr-client; then
         key=".body${key}"
-        if nvrGreaterOrEqual "weldr-client" "35.6"; then
+        if nvrGreaterOrEqual "weldr-client" "35.6" 2> /dev/null; then
             key=".[0]${key}"
         fi
     fi
diff --git a/test/data/ansible/check_ostree.yaml b/test/data/ansible/check_ostree.yaml
index 758d54f26..14ea86194 100644
--- a/test/data/ansible/check_ostree.yaml
+++ b/test/data/ansible/check_ostree.yaml
@@ -651,6 +651,13 @@
           set_fact:
             failed_counter: "{{ failed_counter | int + 1 }}"
 
+    - name: check fdo-client-linuxapp logs
+      command: journalctl -u fdo-client-linuxapp
+      register: result_fdo_client_linuxapp_journalctl
+
+    - debug: 
+        var: result_fdo_client_linuxapp_journalctl
+
     # case: check rollback function if boot error found
     - name: install sanely failing health check unit to test red boot status behavior
       block:
@@ -719,6 +726,35 @@
         - skip_rollback_test == "false"
         - result_rollback is succeeded
 
+    - name: check fdo-client-linuxapp logs
+      command: journalctl -u fdo-client-linuxapp
+      register: result_fdo_client_linuxapp_journalctl
+
+    - debug: 
+        var: result_fdo_client_linuxapp_journalctl
+
+    - name: wait for FDO re-encryption
+      block:
+        - shell: cryptsetup luksDump /dev/vda4
+          register: result
+          until: not result.stdout_lines is search("cipher_null-ecb")
+          retries: 30
+          delay: 60
+      always:
+        - set_fact:
+            total_counter: "{{ total_counter | int + 1 }}"
+      rescue:
+        - name: failed count + 1
+          set_fact:
+            failed_counter: "{{ failed_counter | int + 1 }}"
+      when: fdo_credential == "true"
+
+    - name: check fdo-client-linuxapp logs
+      command: journalctl -u fdo-client-linuxapp
+      register: result_fdo_client_linuxapp_journalctl
+
+    - debug: 
+        var: result_fdo_client_linuxapp_journalctl
 
     # case: check greenboot* services log again
     - name: fallback log should be found here
diff --git a/test/data/repositories/centos-stream-8.json b/test/data/repositories/centos-stream-8.json
index 38aa1cb19..63f640d9f 100644
--- a/test/data/repositories/centos-stream-8.json
+++ b/test/data/repositories/centos-stream-8.json
@@ -2,13 +2,13 @@
   "aarch64": [
     {
       "name": "baseos",
-      "baseurl": "https://rpmrepo.osbuild.org/v2/mirror/public/el8/cs8-aarch64-baseos-20220322/",
+      "baseurl": "https://rpmrepo.osbuild.org/v2/mirror/public/el8/cs8-aarch64-baseos-20220501/",
       "gpgkey": "-----BEGIN PGP PUBLIC KEY BLOCK-----\nVersion: GnuPG v2.0.22 (GNU/Linux)\n\nmQINBFzMWxkBEADHrskpBgN9OphmhRkc7P/YrsAGSvvl7kfu+e9KAaU6f5MeAVyn\nrIoM43syyGkgFyWgjZM8/rur7EMPY2yt+2q/1ZfLVCRn9856JqTIq0XRpDUe4nKQ\n8BlA7wDVZoSDxUZkSuTIyExbDf0cpw89Tcf62Mxmi8jh74vRlPy1PgjWL5494b3X\n5fxDidH4bqPZyxTBqPrUFuo+EfUVEqiGF94Ppq6ZUvrBGOVo1V1+Ifm9CGEK597c\naevcGc1RFlgxIgN84UpuDjPR9/zSndwJ7XsXYvZ6HXcKGagRKsfYDWGPkA5cOL/e\nf+yObOnC43yPUvpggQ4KaNJ6+SMTZOKikM8yciyBwLqwrjo8FlJgkv8Vfag/2UR7\nJINbyqHHoLUhQ2m6HXSwK4YjtwidF9EUkaBZWrrskYR3IRZLXlWqeOi/+ezYOW0m\nvufrkcvsh+TKlVVnuwmEPjJ8mwUSpsLdfPJo1DHsd8FS03SCKPaXFdD7ePfEjiYk\nnHpQaKE01aWVSLUiygn7F7rYemGqV9Vt7tBw5pz0vqSC72a5E3zFzIIuHx6aANry\nGat3aqU3qtBXOrA/dPkX9cWE+UR5wo/A2UdKJZLlGhM2WRJ3ltmGT48V9CeS6N9Y\nm4CKdzvg7EWjlTlFrd/8WJ2KoqOE9leDPeXRPncubJfJ6LLIHyG09h9kKQARAQAB\ntDpDZW50T1MgKENlbnRPUyBPZmZpY2lhbCBTaWduaW5nIEtleSkgPHNlY3VyaXR5\nQGNlbnRvcy5vcmc+iQI3BBMBAgAhBQJczFsZAhsDBgsJCAcDAgYVCAIJCgsDFgIB\nAh4BAheAAAoJEAW1VbOEg8ZdjOsP/2ygSxH9jqffOU9SKyJDlraL2gIutqZ3B8pl\nGy/Qnb9QD1EJVb4ZxOEhcY2W9VJfIpnf3yBuAto7zvKe/G1nxH4Bt6WTJQCkUjcs\nN3qPWsx1VslsAEz7bXGiHym6Ay4xF28bQ9XYIokIQXd0T2rD3/lNGxNtORZ2bKjD\nvOzYzvh2idUIY1DgGWJ11gtHFIA9CvHcW+SMPEhkcKZJAO51ayFBqTSSpiorVwTq\na0cB+cgmCQOI4/MY+kIvzoexfG7xhkUqe0wxmph9RQQxlTbNQDCdaxSgwbF2T+gw\nbyaDvkS4xtR6Soj7BKjKAmcnf5fn4C5Or0KLUqMzBtDMbfQQihn62iZJN6ZZ/4dg\nq4HTqyVpyuzMXsFpJ9L/FqH2DJ4exGGpBv00ba/Zauy7GsqOc5PnNBsYaHCply0X\n407DRx51t9YwYI/ttValuehq9+gRJpOTTKp6AjZn/a5Yt3h6jDgpNfM/EyLFIY9z\nV6CXqQQ/8JRvaik/JsGCf+eeLZOw4koIjZGEAg04iuyNTjhx0e/QHEVcYAqNLhXG\nrCTTbCn3NSUO9qxEXC+K/1m1kaXoCGA0UWlVGZ1JSifbbMx0yxq/brpEZPUYm+32\no8XfbocBWljFUJ+6aljTvZ3LQLKTSPW7TFO+GXycAOmCGhlXh2tlc6iTc41PACqy\nyy+mHmSv\n=kkH7\n-----END PGP PUBLIC KEY BLOCK-----\n",
       "check_gpg": true
     },
     {
       "name": "appstream",
-      "baseurl": "https://rpmrepo.osbuild.org/v2/mirror/public/el8/cs8-aarch64-appstream-20220322/",
+      "baseurl": "https://rpmrepo.osbuild.org/v2/mirror/public/el8/cs8-aarch64-appstream-20220501/",
       "gpgkey": "-----BEGIN PGP PUBLIC KEY BLOCK-----\nVersion: GnuPG v2.0.22 (GNU/Linux)\n\nmQINBFzMWxkBEADHrskpBgN9OphmhRkc7P/YrsAGSvvl7kfu+e9KAaU6f5MeAVyn\nrIoM43syyGkgFyWgjZM8/rur7EMPY2yt+2q/1ZfLVCRn9856JqTIq0XRpDUe4nKQ\n8BlA7wDVZoSDxUZkSuTIyExbDf0cpw89Tcf62Mxmi8jh74vRlPy1PgjWL5494b3X\n5fxDidH4bqPZyxTBqPrUFuo+EfUVEqiGF94Ppq6ZUvrBGOVo1V1+Ifm9CGEK597c\naevcGc1RFlgxIgN84UpuDjPR9/zSndwJ7XsXYvZ6HXcKGagRKsfYDWGPkA5cOL/e\nf+yObOnC43yPUvpggQ4KaNJ6+SMTZOKikM8yciyBwLqwrjo8FlJgkv8Vfag/2UR7\nJINbyqHHoLUhQ2m6HXSwK4YjtwidF9EUkaBZWrrskYR3IRZLXlWqeOi/+ezYOW0m\nvufrkcvsh+TKlVVnuwmEPjJ8mwUSpsLdfPJo1DHsd8FS03SCKPaXFdD7ePfEjiYk\nnHpQaKE01aWVSLUiygn7F7rYemGqV9Vt7tBw5pz0vqSC72a5E3zFzIIuHx6aANry\nGat3aqU3qtBXOrA/dPkX9cWE+UR5wo/A2UdKJZLlGhM2WRJ3ltmGT48V9CeS6N9Y\nm4CKdzvg7EWjlTlFrd/8WJ2KoqOE9leDPeXRPncubJfJ6LLIHyG09h9kKQARAQAB\ntDpDZW50T1MgKENlbnRPUyBPZmZpY2lhbCBTaWduaW5nIEtleSkgPHNlY3VyaXR5\nQGNlbnRvcy5vcmc+iQI3BBMBAgAhBQJczFsZAhsDBgsJCAcDAgYVCAIJCgsDFgIB\nAh4BAheAAAoJEAW1VbOEg8ZdjOsP/2ygSxH9jqffOU9SKyJDlraL2gIutqZ3B8pl\nGy/Qnb9QD1EJVb4ZxOEhcY2W9VJfIpnf3yBuAto7zvKe/G1nxH4Bt6WTJQCkUjcs\nN3qPWsx1VslsAEz7bXGiHym6Ay4xF28bQ9XYIokIQXd0T2rD3/lNGxNtORZ2bKjD\nvOzYzvh2idUIY1DgGWJ11gtHFIA9CvHcW+SMPEhkcKZJAO51ayFBqTSSpiorVwTq\na0cB+cgmCQOI4/MY+kIvzoexfG7xhkUqe0wxmph9RQQxlTbNQDCdaxSgwbF2T+gw\nbyaDvkS4xtR6Soj7BKjKAmcnf5fn4C5Or0KLUqMzBtDMbfQQihn62iZJN6ZZ/4dg\nq4HTqyVpyuzMXsFpJ9L/FqH2DJ4exGGpBv00ba/Zauy7GsqOc5PnNBsYaHCply0X\n407DRx51t9YwYI/ttValuehq9+gRJpOTTKp6AjZn/a5Yt3h6jDgpNfM/EyLFIY9z\nV6CXqQQ/8JRvaik/JsGCf+eeLZOw4koIjZGEAg04iuyNTjhx0e/QHEVcYAqNLhXG\nrCTTbCn3NSUO9qxEXC+K/1m1kaXoCGA0UWlVGZ1JSifbbMx0yxq/brpEZPUYm+32\no8XfbocBWljFUJ+6aljTvZ3LQLKTSPW7TFO+GXycAOmCGhlXh2tlc6iTc41PACqy\nyy+mHmSv\n=kkH7\n-----END PGP PUBLIC KEY BLOCK-----\n",
       "check_gpg": true
     }
@@ -16,13 +16,13 @@
   "ppc64le": [
     {
       "name": "baseos",
-      "baseurl": "https://rpmrepo.osbuild.org/v2/mirror/public/el8/cs8-ppc64le-baseos-20220322/",
+      "baseurl": "https://rpmrepo.osbuild.org/v2/mirror/public/el8/cs8-ppc64le-baseos-20220501/",
       "gpgkey": "-----BEGIN PGP PUBLIC KEY BLOCK-----\nVersion: GnuPG v2.0.22 (GNU/Linux)\n\nmQINBFzMWxkBEADHrskpBgN9OphmhRkc7P/YrsAGSvvl7kfu+e9KAaU6f5MeAVyn\nrIoM43syyGkgFyWgjZM8/rur7EMPY2yt+2q/1ZfLVCRn9856JqTIq0XRpDUe4nKQ\n8BlA7wDVZoSDxUZkSuTIyExbDf0cpw89Tcf62Mxmi8jh74vRlPy1PgjWL5494b3X\n5fxDidH4bqPZyxTBqPrUFuo+EfUVEqiGF94Ppq6ZUvrBGOVo1V1+Ifm9CGEK597c\naevcGc1RFlgxIgN84UpuDjPR9/zSndwJ7XsXYvZ6HXcKGagRKsfYDWGPkA5cOL/e\nf+yObOnC43yPUvpggQ4KaNJ6+SMTZOKikM8yciyBwLqwrjo8FlJgkv8Vfag/2UR7\nJINbyqHHoLUhQ2m6HXSwK4YjtwidF9EUkaBZWrrskYR3IRZLXlWqeOi/+ezYOW0m\nvufrkcvsh+TKlVVnuwmEPjJ8mwUSpsLdfPJo1DHsd8FS03SCKPaXFdD7ePfEjiYk\nnHpQaKE01aWVSLUiygn7F7rYemGqV9Vt7tBw5pz0vqSC72a5E3zFzIIuHx6aANry\nGat3aqU3qtBXOrA/dPkX9cWE+UR5wo/A2UdKJZLlGhM2WRJ3ltmGT48V9CeS6N9Y\nm4CKdzvg7EWjlTlFrd/8WJ2KoqOE9leDPeXRPncubJfJ6LLIHyG09h9kKQARAQAB\ntDpDZW50T1MgKENlbnRPUyBPZmZpY2lhbCBTaWduaW5nIEtleSkgPHNlY3VyaXR5\nQGNlbnRvcy5vcmc+iQI3BBMBAgAhBQJczFsZAhsDBgsJCAcDAgYVCAIJCgsDFgIB\nAh4BAheAAAoJEAW1VbOEg8ZdjOsP/2ygSxH9jqffOU9SKyJDlraL2gIutqZ3B8pl\nGy/Qnb9QD1EJVb4ZxOEhcY2W9VJfIpnf3yBuAto7zvKe/G1nxH4Bt6WTJQCkUjcs\nN3qPWsx1VslsAEz7bXGiHym6Ay4xF28bQ9XYIokIQXd0T2rD3/lNGxNtORZ2bKjD\nvOzYzvh2idUIY1DgGWJ11gtHFIA9CvHcW+SMPEhkcKZJAO51ayFBqTSSpiorVwTq\na0cB+cgmCQOI4/MY+kIvzoexfG7xhkUqe0wxmph9RQQxlTbNQDCdaxSgwbF2T+gw\nbyaDvkS4xtR6Soj7BKjKAmcnf5fn4C5Or0KLUqMzBtDMbfQQihn62iZJN6ZZ/4dg\nq4HTqyVpyuzMXsFpJ9L/FqH2DJ4exGGpBv00ba/Zauy7GsqOc5PnNBsYaHCply0X\n407DRx51t9YwYI/ttValuehq9+gRJpOTTKp6AjZn/a5Yt3h6jDgpNfM/EyLFIY9z\nV6CXqQQ/8JRvaik/JsGCf+eeLZOw4koIjZGEAg04iuyNTjhx0e/QHEVcYAqNLhXG\nrCTTbCn3NSUO9qxEXC+K/1m1kaXoCGA0UWlVGZ1JSifbbMx0yxq/brpEZPUYm+32\no8XfbocBWljFUJ+6aljTvZ3LQLKTSPW7TFO+GXycAOmCGhlXh2tlc6iTc41PACqy\nyy+mHmSv\n=kkH7\n-----END PGP PUBLIC KEY BLOCK-----\n",
       "check_gpg": true
     },
     {
       "name": "appstream",
-      "baseurl": "https://rpmrepo.osbuild.org/v2/mirror/public/el8/cs8-ppc64le-appstream-20220322/",
+      "baseurl": "https://rpmrepo.osbuild.org/v2/mirror/public/el8/cs8-ppc64le-appstream-20220501/",
       "gpgkey": "-----BEGIN PGP PUBLIC KEY BLOCK-----\nVersion: GnuPG v2.0.22 (GNU/Linux)\n\nmQINBFzMWxkBEADHrskpBgN9OphmhRkc7P/YrsAGSvvl7kfu+e9KAaU6f5MeAVyn\nrIoM43syyGkgFyWgjZM8/rur7EMPY2yt+2q/1ZfLVCRn9856JqTIq0XRpDUe4nKQ\n8BlA7wDVZoSDxUZkSuTIyExbDf0cpw89Tcf62Mxmi8jh74vRlPy1PgjWL5494b3X\n5fxDidH4bqPZyxTBqPrUFuo+EfUVEqiGF94Ppq6ZUvrBGOVo1V1+Ifm9CGEK597c\naevcGc1RFlgxIgN84UpuDjPR9/zSndwJ7XsXYvZ6HXcKGagRKsfYDWGPkA5cOL/e\nf+yObOnC43yPUvpggQ4KaNJ6+SMTZOKikM8yciyBwLqwrjo8FlJgkv8Vfag/2UR7\nJINbyqHHoLUhQ2m6HXSwK4YjtwidF9EUkaBZWrrskYR3IRZLXlWqeOi/+ezYOW0m\nvufrkcvsh+TKlVVnuwmEPjJ8mwUSpsLdfPJo1DHsd8FS03SCKPaXFdD7ePfEjiYk\nnHpQaKE01aWVSLUiygn7F7rYemGqV9Vt7tBw5pz0vqSC72a5E3zFzIIuHx6aANry\nGat3aqU3qtBXOrA/dPkX9cWE+UR5wo/A2UdKJZLlGhM2WRJ3ltmGT48V9CeS6N9Y\nm4CKdzvg7EWjlTlFrd/8WJ2KoqOE9leDPeXRPncubJfJ6LLIHyG09h9kKQARAQAB\ntDpDZW50T1MgKENlbnRPUyBPZmZpY2lhbCBTaWduaW5nIEtleSkgPHNlY3VyaXR5\nQGNlbnRvcy5vcmc+iQI3BBMBAgAhBQJczFsZAhsDBgsJCAcDAgYVCAIJCgsDFgIB\nAh4BAheAAAoJEAW1VbOEg8ZdjOsP/2ygSxH9jqffOU9SKyJDlraL2gIutqZ3B8pl\nGy/Qnb9QD1EJVb4ZxOEhcY2W9VJfIpnf3yBuAto7zvKe/G1nxH4Bt6WTJQCkUjcs\nN3qPWsx1VslsAEz7bXGiHym6Ay4xF28bQ9XYIokIQXd0T2rD3/lNGxNtORZ2bKjD\nvOzYzvh2idUIY1DgGWJ11gtHFIA9CvHcW+SMPEhkcKZJAO51ayFBqTSSpiorVwTq\na0cB+cgmCQOI4/MY+kIvzoexfG7xhkUqe0wxmph9RQQxlTbNQDCdaxSgwbF2T+gw\nbyaDvkS4xtR6Soj7BKjKAmcnf5fn4C5Or0KLUqMzBtDMbfQQihn62iZJN6ZZ/4dg\nq4HTqyVpyuzMXsFpJ9L/FqH2DJ4exGGpBv00ba/Zauy7GsqOc5PnNBsYaHCply0X\n407DRx51t9YwYI/ttValuehq9+gRJpOTTKp6AjZn/a5Yt3h6jDgpNfM/EyLFIY9z\nV6CXqQQ/8JRvaik/JsGCf+eeLZOw4koIjZGEAg04iuyNTjhx0e/QHEVcYAqNLhXG\nrCTTbCn3NSUO9qxEXC+K/1m1kaXoCGA0UWlVGZ1JSifbbMx0yxq/brpEZPUYm+32\no8XfbocBWljFUJ+6aljTvZ3LQLKTSPW7TFO+GXycAOmCGhlXh2tlc6iTc41PACqy\nyy+mHmSv\n=kkH7\n-----END PGP PUBLIC KEY BLOCK-----\n",
       "check_gpg": true
     }
@@ -30,13 +30,13 @@
   "x86_64": [
     {
       "name": "baseos",
-      "baseurl": "https://rpmrepo.osbuild.org/v2/mirror/public/el8/cs8-x86_64-baseos-20220322/",
+      "baseurl": "https://rpmrepo.osbuild.org/v2/mirror/public/el8/cs8-x86_64-baseos-20220501/",
       "gpgkey": "-----BEGIN PGP PUBLIC KEY BLOCK-----\nVersion: GnuPG v2.0.22 (GNU/Linux)\n\nmQINBFzMWxkBEADHrskpBgN9OphmhRkc7P/YrsAGSvvl7kfu+e9KAaU6f5MeAVyn\nrIoM43syyGkgFyWgjZM8/rur7EMPY2yt+2q/1ZfLVCRn9856JqTIq0XRpDUe4nKQ\n8BlA7wDVZoSDxUZkSuTIyExbDf0cpw89Tcf62Mxmi8jh74vRlPy1PgjWL5494b3X\n5fxDidH4bqPZyxTBqPrUFuo+EfUVEqiGF94Ppq6ZUvrBGOVo1V1+Ifm9CGEK597c\naevcGc1RFlgxIgN84UpuDjPR9/zSndwJ7XsXYvZ6HXcKGagRKsfYDWGPkA5cOL/e\nf+yObOnC43yPUvpggQ4KaNJ6+SMTZOKikM8yciyBwLqwrjo8FlJgkv8Vfag/2UR7\nJINbyqHHoLUhQ2m6HXSwK4YjtwidF9EUkaBZWrrskYR3IRZLXlWqeOi/+ezYOW0m\nvufrkcvsh+TKlVVnuwmEPjJ8mwUSpsLdfPJo1DHsd8FS03SCKPaXFdD7ePfEjiYk\nnHpQaKE01aWVSLUiygn7F7rYemGqV9Vt7tBw5pz0vqSC72a5E3zFzIIuHx6aANry\nGat3aqU3qtBXOrA/dPkX9cWE+UR5wo/A2UdKJZLlGhM2WRJ3ltmGT48V9CeS6N9Y\nm4CKdzvg7EWjlTlFrd/8WJ2KoqOE9leDPeXRPncubJfJ6LLIHyG09h9kKQARAQAB\ntDpDZW50T1MgKENlbnRPUyBPZmZpY2lhbCBTaWduaW5nIEtleSkgPHNlY3VyaXR5\nQGNlbnRvcy5vcmc+iQI3BBMBAgAhBQJczFsZAhsDBgsJCAcDAgYVCAIJCgsDFgIB\nAh4BAheAAAoJEAW1VbOEg8ZdjOsP/2ygSxH9jqffOU9SKyJDlraL2gIutqZ3B8pl\nGy/Qnb9QD1EJVb4ZxOEhcY2W9VJfIpnf3yBuAto7zvKe/G1nxH4Bt6WTJQCkUjcs\nN3qPWsx1VslsAEz7bXGiHym6Ay4xF28bQ9XYIokIQXd0T2rD3/lNGxNtORZ2bKjD\nvOzYzvh2idUIY1DgGWJ11gtHFIA9CvHcW+SMPEhkcKZJAO51ayFBqTSSpiorVwTq\na0cB+cgmCQOI4/MY+kIvzoexfG7xhkUqe0wxmph9RQQxlTbNQDCdaxSgwbF2T+gw\nbyaDvkS4xtR6Soj7BKjKAmcnf5fn4C5Or0KLUqMzBtDMbfQQihn62iZJN6ZZ/4dg\nq4HTqyVpyuzMXsFpJ9L/FqH2DJ4exGGpBv00ba/Zauy7GsqOc5PnNBsYaHCply0X\n407DRx51t9YwYI/ttValuehq9+gRJpOTTKp6AjZn/a5Yt3h6jDgpNfM/EyLFIY9z\nV6CXqQQ/8JRvaik/JsGCf+eeLZOw4koIjZGEAg04iuyNTjhx0e/QHEVcYAqNLhXG\nrCTTbCn3NSUO9qxEXC+K/1m1kaXoCGA0UWlVGZ1JSifbbMx0yxq/brpEZPUYm+32\no8XfbocBWljFUJ+6aljTvZ3LQLKTSPW7TFO+GXycAOmCGhlXh2tlc6iTc41PACqy\nyy+mHmSv\n=kkH7\n-----END PGP PUBLIC KEY BLOCK-----\n",
       "check_gpg": true
     },
     {
       "name": "appstream",
-      "baseurl": "https://rpmrepo.osbuild.org/v2/mirror/public/el8/cs8-x86_64-appstream-20220322/",
+      "baseurl": "https://rpmrepo.osbuild.org/v2/mirror/public/el8/cs8-x86_64-appstream-20220501/",
       "gpgkey": "-----BEGIN PGP PUBLIC KEY BLOCK-----\nVersion: GnuPG v2.0.22 (GNU/Linux)\n\nmQINBFzMWxkBEADHrskpBgN9OphmhRkc7P/YrsAGSvvl7kfu+e9KAaU6f5MeAVyn\nrIoM43syyGkgFyWgjZM8/rur7EMPY2yt+2q/1ZfLVCRn9856JqTIq0XRpDUe4nKQ\n8BlA7wDVZoSDxUZkSuTIyExbDf0cpw89Tcf62Mxmi8jh74vRlPy1PgjWL5494b3X\n5fxDidH4bqPZyxTBqPrUFuo+EfUVEqiGF94Ppq6ZUvrBGOVo1V1+Ifm9CGEK597c\naevcGc1RFlgxIgN84UpuDjPR9/zSndwJ7XsXYvZ6HXcKGagRKsfYDWGPkA5cOL/e\nf+yObOnC43yPUvpggQ4KaNJ6+SMTZOKikM8yciyBwLqwrjo8FlJgkv8Vfag/2UR7\nJINbyqHHoLUhQ2m6HXSwK4YjtwidF9EUkaBZWrrskYR3IRZLXlWqeOi/+ezYOW0m\nvufrkcvsh+TKlVVnuwmEPjJ8mwUSpsLdfPJo1DHsd8FS03SCKPaXFdD7ePfEjiYk\nnHpQaKE01aWVSLUiygn7F7rYemGqV9Vt7tBw5pz0vqSC72a5E3zFzIIuHx6aANry\nGat3aqU3qtBXOrA/dPkX9cWE+UR5wo/A2UdKJZLlGhM2WRJ3ltmGT48V9CeS6N9Y\nm4CKdzvg7EWjlTlFrd/8WJ2KoqOE9leDPeXRPncubJfJ6LLIHyG09h9kKQARAQAB\ntDpDZW50T1MgKENlbnRPUyBPZmZpY2lhbCBTaWduaW5nIEtleSkgPHNlY3VyaXR5\nQGNlbnRvcy5vcmc+iQI3BBMBAgAhBQJczFsZAhsDBgsJCAcDAgYVCAIJCgsDFgIB\nAh4BAheAAAoJEAW1VbOEg8ZdjOsP/2ygSxH9jqffOU9SKyJDlraL2gIutqZ3B8pl\nGy/Qnb9QD1EJVb4ZxOEhcY2W9VJfIpnf3yBuAto7zvKe/G1nxH4Bt6WTJQCkUjcs\nN3qPWsx1VslsAEz7bXGiHym6Ay4xF28bQ9XYIokIQXd0T2rD3/lNGxNtORZ2bKjD\nvOzYzvh2idUIY1DgGWJ11gtHFIA9CvHcW+SMPEhkcKZJAO51ayFBqTSSpiorVwTq\na0cB+cgmCQOI4/MY+kIvzoexfG7xhkUqe0wxmph9RQQxlTbNQDCdaxSgwbF2T+gw\nbyaDvkS4xtR6Soj7BKjKAmcnf5fn4C5Or0KLUqMzBtDMbfQQihn62iZJN6ZZ/4dg\nq4HTqyVpyuzMXsFpJ9L/FqH2DJ4exGGpBv00ba/Zauy7GsqOc5PnNBsYaHCply0X\n407DRx51t9YwYI/ttValuehq9+gRJpOTTKp6AjZn/a5Yt3h6jDgpNfM/EyLFIY9z\nV6CXqQQ/8JRvaik/JsGCf+eeLZOw4koIjZGEAg04iuyNTjhx0e/QHEVcYAqNLhXG\nrCTTbCn3NSUO9qxEXC+K/1m1kaXoCGA0UWlVGZ1JSifbbMx0yxq/brpEZPUYm+32\no8XfbocBWljFUJ+6aljTvZ3LQLKTSPW7TFO+GXycAOmCGhlXh2tlc6iTc41PACqy\nyy+mHmSv\n=kkH7\n-----END PGP PUBLIC KEY BLOCK-----\n",
       "check_gpg": true
     },