Repository: osbuild/osbuild-composer
Pull request #1992 (2021-11-10 12:38:05)

osbuild2: new stage sshd config
This stage was introduced in osbuild 41. Add support into
osbuild-composer and a test using test data from osbuild repo.


This pull request includes:

- [ ] adequate testing for the new functionality or fixed issue
- [ ] 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/

<!--
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.

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`
-->


msehnout: > In general the code looks good, although there is one part that I commented on that should be IMO changed. In addition you should add case for the new stage into https://github.com/osbuild/osbuild-composer/blob/main/internal/osbuild2/stage.go#L36 and also to the relevant unit tests file.

Thanks for the review! I've fixed the PR, can you take a look again?

Patch:
diff --git a/.packit.yaml b/.packit.yaml
index 2fe0a1c69..292d178d7 100644
--- a/.packit.yaml
+++ b/.packit.yaml
@@ -9,6 +9,8 @@ synced_files:
 upstream_package_name: osbuild-composer
 downstream_package_name: osbuild-composer
 
+copy_upstream_release_description: true
+
 upstream_tag_template: v{version}
 
 actions:
diff --git a/internal/osbuild2/sshd_config_stage.go b/internal/osbuild2/sshd_config_stage.go
new file mode 100644
index 000000000..30d2afa4a
--- /dev/null
+++ b/internal/osbuild2/sshd_config_stage.go
@@ -0,0 +1,20 @@
+package osbuild2
+
+type SshdConfigConfig struct {
+	PasswordAuthentication          *bool `json:"PasswordAuthentication,omitempty"`
+	ChallengeResponseAuthentication *bool `json:"ChallengeResponseAuthentication,omitempty"`
+	ClientAliveInterval             *int  `json:"ClientAliveInterval,omitempty"`
+}
+
+type SshdConfigStageOptions struct {
+	Config SshdConfigConfig `json:"config"`
+}
+
+func (SshdConfigStageOptions) isStageOptions() {}
+
+func NewSshdConfigStage(options *SshdConfigStageOptions) *Stage {
+	return &Stage{
+		Type:    "org.osbuild.sshd.config",
+		Options: options,
+	}
+}
diff --git a/internal/osbuild2/stage.go b/internal/osbuild2/stage.go
index bc88dbdf4..6e6fc4bc0 100644
--- a/internal/osbuild2/stage.go
+++ b/internal/osbuild2/stage.go
@@ -146,6 +146,8 @@ func (stage *Stage) UnmarshalJSON(data []byte) error {
 		// The stage accepts also source input, but we need to rework all inputs first to handle this nicely here.
 		// Only files input is used by the XZ stage at this moment.
 		inputs = new(FilesInputs)
+	case "org.osbuild.sshd.config":
+		options = new(SshdConfigStageOptions)
 	default:
 		return fmt.Errorf("unexpected stage type: %s", rawStage.Type)
 	}
diff --git a/schutzbot/scheduled_cloud_cleaner.sh b/schutzbot/scheduled_cloud_cleaner.sh
index 81fe82e79..0b93ad3a0 100755
--- a/schutzbot/scheduled_cloud_cleaner.sh
+++ b/schutzbot/scheduled_cloud_cleaner.sh
@@ -84,3 +84,106 @@ for i in $(seq 0 $(("$STORAGE_ACCOUNT_COUNT"-1))); do
 done
 
 echo "Azure cleanup complete!"
+
+
+#---------------------------------------------------------------
+# 			AWS cleanup
+#---------------------------------------------------------------
+
+greenprint "Starting aws cleanup"
+
+TEMPDIR=$(mktemp -d)
+function cleanup() {
+    sudo rm -rf "$TEMPDIR"
+}
+trap cleanup EXIT
+
+# Check available container runtime
+if which podman 2>/dev/null >&2; then
+    CONTAINER_RUNTIME=podman
+elif which docker 2>/dev/null >&2; then
+    CONTAINER_RUNTIME=docker
+else
+    echo No container runtime found, install podman or docker.
+    exit 2
+fi
+
+CONTAINER_IMAGE_CLOUD_TOOLS="quay.io/osbuild/cloud-tools:latest"
+
+SSH_DATA_DIR=$(/usr/libexec/osbuild-composer-test/gen-ssh.sh)
+
+# We need awscli to talk to AWS.
+if ! hash aws; then
+    echo "Using 'awscli' from a container"
+    sudo ${CONTAINER_RUNTIME} pull ${CONTAINER_IMAGE_CLOUD_TOOLS}
+
+    AWS_CMD="sudo ${CONTAINER_RUNTIME} run --rm \
+        -e AWS_ACCESS_KEY_ID=${V2_AWS_ACCESS_KEY_ID} \
+        -e AWS_SECRET_ACCESS_KEY=${V2_AWS_SECRET_ACCESS_KEY} \
+        -v ${TEMPDIR}:${TEMPDIR}:Z \
+        -v ${SSH_DATA_DIR}:${SSH_DATA_DIR}:Z \
+        ${CONTAINER_IMAGE_CLOUD_TOOLS} aws --region $AWS_REGION --output json --color on"
+else
+    echo "Using pre-installed 'aws' from the system"
+    AWS_CMD="aws --region $AWS_REGION --output json --color on"
+fi
+$AWS_CMD --version
+
+
+# Remove tagged and old enough instances
+INSTANCES=$(${AWS_CMD} ec2 describe-instances | jq -c '.Reservations[].Instances[]|try {"Tag": .Tags[],"InstanceId": .InstanceId,"LaunchTime": .LaunchTime}')
+
+for instance in ${INSTANCES}; do
+	TAG=$(echo "${instance}" | jq '.Tag.Key' | tr -d '"')
+	TAG_VALUE=$(echo "${instance}" | jq '.Tag.Value' | tr -d '"')
+	INSTANCE_ID=$(echo "${instance}" | jq '.InstanceId' | tr -d '"')
+	LAUNCH_TIME=$(echo "${instance}" | jq '.LaunchTime' | tr -d '"')
+
+	if [[ ${TAG} == "gitlab-ci-test" && ${TAG_VALUE} == "true" ]]; then	
+		if [[ $(date -d "${LAUNCH_TIME}" +%s) -lt "${DELETE_TIME}" ]]; then
+			$AWS_CMD ec2 terminate-instances --instance-id "${INSTANCE_ID}"
+			echo "The instance with id ${INSTANCE_ID} was terminated"
+        	else
+        		echo "The instance with id ${INSTANCE_ID} was launched less than ${HOURS_BACK} hours ago"
+		fi
+	fi
+done
+
+
+# Remove tagged and old enough images
+IMAGES=$($AWS_CMD ec2 describe-images --owner self | jq -c '.Images[] | try {"Tag": .Tags[], "ImageId": .ImageId, "CreationDate": .CreationDate}')
+
+for image in ${IMAGES}; do
+	TAG=$(echo "${image}" | jq '.Tag.Key' | tr -d '"')
+	TAG_VALUE=$(echo "${image}" | jq '.Tag.Value' | tr -d '"')
+	IMAGE_ID=$(echo "${image}" | jq '.ImageId' | tr -d '"')
+	CREATION_DATE=$(echo "${image}" | jq '.CreationDate' | tr -d '"')
+
+	if [[ ${TAG} == "gitlab-ci-test" && ${TAG_VALUE} == "true" ]]; then
+		if [[ $(date -d "${CREATION_DATE}" +%s) -lt "${DELETE_TIME}" ]]; then
+			$AWS_CMD ec2 deregister-image --image-id "${IMAGE_ID}"
+			echo "The image with id ${IMAGE_ID} was deregistered"
+		else
+			echo "The image with id ${IMAGE_ID} was created less than ${HOURS_BACK} hours ago"
+		fi
+	fi
+done
+
+# Remove tagged and old enough snapshots
+SNAPSHOTS=$($AWS_CMD --color on ec2 describe-snapshots --owner self | jq -c '.Snapshots[] | try {"Tag": .Tags[], "SnapshotId": .SnapshotId, "StartTime": .StartTime}')
+
+for snapshot in ${SNAPSHOTS}; do
+	TAG=$(echo "${snapshot}" | jq '.Tag.Key' | tr -d '"')
+	TAG_VALUE=$(echo "${snapshot}" | jq '.Tag.Value' | tr -d '"')
+	SNAPSHOT_ID=$(echo "${snapshot}" | jq '.SnapshotId' | tr -d '"')
+	START_TIME=$(echo "${snapshot}" | jq '.StartTime' | tr -d '"')
+
+	if [[ ${TAG} == "gitlab-ci-test" && ${TAG_VALUE} == "true" ]]; then
+		if [[ $(date -d "${START_TIME}" +%s) -lt $(date -d "- 2 minutes" +%s) ]]; then
+			$AWS_CMD ec2 delete-snapshot --snapshot-id "${SNAPSHOT_ID}"
+			echo "The snapshot with id ${SNAPSHOT_ID} was deleted"
+		else
+			echo "The snapshot with id ${SNAPSHOT_ID} was created less than ${HOURS_BACK} hours ago"
+		fi
+	fi
+done


Test patch:
diff --git a/internal/osbuild2/sshd_config_stage_test.go b/internal/osbuild2/sshd_config_stage_test.go
new file mode 100644
index 000000000..f59d027b4
--- /dev/null
+++ b/internal/osbuild2/sshd_config_stage_test.go
@@ -0,0 +1,53 @@
+package osbuild2
+
+import (
+	"encoding/json"
+	"reflect"
+	"testing"
+
+	"github.com/osbuild/osbuild-composer/internal/common"
+	"github.com/stretchr/testify/assert"
+)
+
+func TestNewSshdConfigStage(t *testing.T) {
+	expectedStage := &Stage{
+		Type:    "org.osbuild.sshd.config",
+		Options: &SshdConfigStageOptions{},
+	}
+	actualStage := NewSshdConfigStage(&SshdConfigStageOptions{})
+	assert.Equal(t, expectedStage, actualStage)
+}
+
+func TestJsonSshdConfigStage(t *testing.T) {
+	// First test that the JSON can be parsed into the expected structure.
+	expectedOptions := SshdConfigStageOptions{
+		Config: SshdConfigConfig{
+			PasswordAuthentication:          common.BoolToPtr(false),
+			ChallengeResponseAuthentication: common.BoolToPtr(false),
+			ClientAliveInterval:             common.IntToPtr(180),
+		},
+	}
+	inputString := `{
+		"config": {
+		  "PasswordAuthentication": false,
+		  "ChallengeResponseAuthentication": false,
+		  "ClientAliveInterval": 180
+		}
+	  }`
+	var inputOptions SshdConfigStageOptions
+	err := json.Unmarshal([]byte(inputString), &inputOptions)
+	assert.NoError(t, err, "failed to parse JSON into sshd config")
+	assert.True(t, reflect.DeepEqual(expectedOptions, inputOptions))
+
+	// Second try the other way around with stress on missing values
+	// for those parameters that the user didn't specify.
+	inputOptions = SshdConfigStageOptions{
+		Config: SshdConfigConfig{
+			PasswordAuthentication: common.BoolToPtr(true),
+		},
+	}
+	expectedString := `{"config":{"PasswordAuthentication":true}}`
+	inputBytes, err := json.Marshal(inputOptions)
+	assert.NoError(t, err, "failed to marshal sshd config into JSON")
+	assert.Equal(t, expectedString, string(inputBytes))
+}
diff --git a/internal/osbuild2/stage_test.go b/internal/osbuild2/stage_test.go
index 0789fcb34..bd318921c 100644
--- a/internal/osbuild2/stage_test.go
+++ b/internal/osbuild2/stage_test.go
@@ -637,6 +637,16 @@ func TestStage_UnmarshalJSON(t *testing.T) {
 				data: []byte(`{"type":"org.osbuild.users","options":{"users":null}}`),
 			},
 		},
+		{
+			name: "sshd.config",
+			fields: fields{
+				Type:    "org.osbuild.sshd.config",
+				Options: &SshdConfigStageOptions{},
+			},
+			args: args{
+				data: []byte(`{"type":"org.osbuild.sshd.config","options":{"config":{}}}`),
+			},
+		},
 	}
 	for idx, tt := range tests {
 		t.Run(tt.name, func(t *testing.T) {
diff --git a/test/cases/api.sh b/test/cases/api.sh
index cdd6c2284..2bae8e1ca 100755
--- a/test/cases/api.sh
+++ b/test/cases/api.sh
@@ -460,7 +460,7 @@ fi
 ssh-keygen -t rsa-sha2-512 -f /tmp/usertest -C "usertest" -N ""
 
 function createReqFileAWS() {
-  AWS_SNAPSHOT_NAME=$(uuidgen)
+  AWS_SNAPSHOT_NAME=${TEST_ID}
 
   cat > "$REQUEST_FILE" << EOF
 {
@@ -860,6 +860,13 @@ function verifyInAWS() {
 
   AMI_IMAGE_ID=$(jq -r '.Images[].ImageId' "$WORKDIR/ami.json")
   AWS_SNAPSHOT_ID=$(jq -r '.Images[].BlockDeviceMappings[].Ebs.SnapshotId' "$WORKDIR/ami.json")
+  
+  # Tag image and snapshot with "gitlab-ci-test" tag
+  $AWS_CMD ec2 create-tags \
+    --resources "${AWS_SNAPSHOT_ID}" "${AMI_IMAGE_ID}" \
+    --tags Key=gitlab-ci-test,Value=true
+
+  
   SHARE_OK=1
 
   # Verify that the ec2 snapshot was shared
@@ -888,7 +895,7 @@ function verifyInAWS() {
   chmod 400 ./keypair.pem
 
   # Create an instance based on the ami
-  $AWS_CMD ec2 run-instances --image-id "$AMI_IMAGE_ID" --count 1 --instance-type t2.micro --key-name "key-for-$AMI_IMAGE_ID" > "$WORKDIR/instances.json"
+  $AWS_CMD ec2 run-instances --image-id "$AMI_IMAGE_ID" --count 1 --instance-type t2.micro --key-name "key-for-$AMI_IMAGE_ID" --tag-specifications 'ResourceType=instance,Tags=[{Key=gitlab-ci-test,Value=true}]' > "$WORKDIR/instances.json"
   AWS_INSTANCE_ID=$(jq -r '.Instances[].InstanceId' "$WORKDIR/instances.json")
 
   $AWS_CMD ec2 wait instance-running --instance-ids "$AWS_INSTANCE_ID"
diff --git a/test/cases/aws.sh b/test/cases/aws.sh
index 7365e60cd..21913a983 100755
--- a/test/cases/aws.sh
+++ b/test/cases/aws.sh
@@ -39,8 +39,18 @@ function cleanup() {
 }
 trap cleanup EXIT
 
-TEST_UUID=$(uuidgen)
-IMAGE_KEY=osbuild-composer-aws-test-${TEST_UUID}
+# Generate a string, which can be used as a predictable resource name,
+# especially when running the test in CI where we may need to clean up
+# resources in case the test unexpectedly fails or is canceled
+CI="${CI:-false}"
+if [[ "$CI" == true ]]; then
+  # in CI, imitate GenerateCIArtifactName() from internal/test/helpers.go
+  TEST_ID="$DISTRO_CODE-$ARCH-$CI_COMMIT_BRANCH-$CI_BUILD_ID"
+else
+  # if not running in Jenkins, generate ID not relying on specific env variables
+  TEST_ID=$(uuidgen);
+fi
+
 
 # Jenkins sets WORKSPACE to the job workspace, but if this script runs
 # outside of Jenkins, we can set up a temporary directory instead.
@@ -52,11 +62,11 @@ fi
 AWS_CONFIG=${TEMPDIR}/aws.toml
 BLUEPRINT_FILE=${TEMPDIR}/blueprint.toml
 AWS_INSTANCE_JSON=${TEMPDIR}/aws-instance.json
-COMPOSE_START=${TEMPDIR}/compose-start-${IMAGE_KEY}.json
-COMPOSE_INFO=${TEMPDIR}/compose-info-${IMAGE_KEY}.json
-AMI_DATA=${TEMPDIR}/ami-data-${IMAGE_KEY}.json
-INSTANCE_DATA=${TEMPDIR}/instance-data-${IMAGE_KEY}.json
-INSTANCE_CONSOLE=${TEMPDIR}/instance-console-${IMAGE_KEY}.json
+COMPOSE_START=${TEMPDIR}/compose-start-${TEST_ID}.json
+COMPOSE_INFO=${TEMPDIR}/compose-info-${TEST_ID}.json
+AMI_DATA=${TEMPDIR}/ami-data-${TEST_ID}.json
+INSTANCE_DATA=${TEMPDIR}/instance-data-${TEST_ID}.json
+INSTANCE_CONSOLE=${TEMPDIR}/instance-console-${TEST_ID}.json
 
 SSH_DATA_DIR=$(/usr/libexec/osbuild-composer-test/gen-ssh.sh)
 SSH_KEY=${SSH_DATA_DIR}/id_rsa
@@ -142,7 +152,7 @@ accessKeyID = "${V2_AWS_ACCESS_KEY_ID}"
 secretAccessKey = "${V2_AWS_SECRET_ACCESS_KEY}"
 bucket = "${AWS_BUCKET}"
 region = "${AWS_REGION}"
-key = "${IMAGE_KEY}"
+key = "${TEST_ID}"
 EOF
 
 # Write a basic blueprint for our image.
@@ -172,7 +182,7 @@ trap 'sudo pkill -P ${WORKER_JOURNAL_PID}' EXIT
 
 # Start the compose and upload to AWS.
 greenprint "🚀 Starting compose"
-sudo composer-cli --json compose start bash ami "$IMAGE_KEY" "$AWS_CONFIG" | tee "$COMPOSE_START"
+sudo composer-cli --json compose start bash ami "$TEST_ID" "$AWS_CONFIG" | tee "$COMPOSE_START"
 COMPOSE_ID=$(get_build_info ".build_id" "$COMPOSE_START")
 
 # Wait for the compose to finish.
@@ -209,10 +219,16 @@ fi
 greenprint "🔍 Search for created AMI"
 $AWS_CMD ec2 describe-images \
     --owners self \
-    --filters Name=name,Values="${IMAGE_KEY}" \
+    --filters Name=name,Values="${TEST_ID}" \
     | tee "$AMI_DATA" > /dev/null
 
 AMI_IMAGE_ID=$(jq -r '.Images[].ImageId' "$AMI_DATA")
+SNAPSHOT_ID=$(jq -r '.Images[].BlockDeviceMappings[].Ebs.SnapshotId' "$AMI_DATA")
+
+# Tag image and snapshot with "gitlab-ci-test" tag
+$AWS_CMD ec2 create-tags \
+    --resources "${SNAPSHOT_ID}" "${AMI_IMAGE_ID}" \
+    --tags Key=gitlab-ci-test,Value=true
 
 # NOTE(mhayden): Getting TagSpecifications to play along with bash's
 # parsing of curly braces and square brackets is nuts, so we just write some
@@ -225,7 +241,11 @@ tee "$AWS_INSTANCE_JSON" > /dev/null << EOF
             "Tags": [
                 {
                     "Key": "Name",
-                    "Value": "${IMAGE_KEY}"
+                    "Value": "${TEST_ID}"
+                },
+                {
+                    "Key": "gitlab-ci-test",
+                    "Value": "true"
                 }
             ]
         }
@@ -297,13 +317,12 @@ done
 
 # Ensure the image was properly tagged.
 IMAGE_TAG=$($AWS_CMD ec2 describe-images --image-ids "${AMI_IMAGE_ID}" | jq -r '.Images[0].Tags[] | select(.Key=="Name") | .Value')
-if [[ ! $IMAGE_TAG == "${IMAGE_KEY}" ]]; then
+if [[ ! $IMAGE_TAG == "${TEST_ID}" ]]; then
     RESULTS=0
 fi
 
 # Clean up our mess.
 greenprint "🧼 Cleaning up"
-SNAPSHOT_ID=$(jq -r '.Images[].BlockDeviceMappings[].Ebs.SnapshotId' "$AMI_DATA")
 $AWS_CMD ec2 terminate-instances --instance-id "${INSTANCE_ID}"
 $AWS_CMD ec2 deregister-image --image-id "${AMI_IMAGE_ID}"
 $AWS_CMD ec2 delete-snapshot --snapshot-id "${SNAPSHOT_ID}"
diff --git a/test/cases/ostree-ng.sh b/test/cases/ostree-ng.sh
index daf805b49..400f4c3b2 100755
--- a/test/cases/ostree-ng.sh
+++ b/test/cases/ostree-ng.sh
@@ -72,8 +72,6 @@ polkit.addRule(function(action, subject) {
 EOF
 
 # Set up variables.
-OSTREE_REF="test/rhel/8/${ARCH}/edge"
-OS_VARIANT="rhel8-unknown"
 TEST_UUID=$(uuidgen)
 IMAGE_KEY="osbuild-composer-ostree-test-${TEST_UUID}"
 BIOS_GUEST_ADDRESS=192.168.100.50
@@ -87,6 +85,11 @@ QUAY_REPO_TAG=$(tr -dc a-z0-9 < /dev/urandom | head -c 4 ; echo '')
 STAGE_OCP4_SERVER_NAME="edge-stage-server"
 STAGE_OCP4_REPO_URL="http://${STAGE_OCP4_SERVER_NAME}-${QUAY_REPO_TAG}-frontdoor.apps.ocp.ci.centos.org/repo/"
 ARTIFACTS="ci-artifacts"
+# For CS8, CS9, RHEL 8.5 and above
+CONTAINER_TYPE=edge-container
+CONTAINER_FILENAME=container.tar
+INSTALLER_TYPE=edge-installer
+INSTALLER_FILENAME=installer.iso
 mkdir -p "${ARTIFACTS}"
 
 # Set up temporary files.
@@ -104,6 +107,8 @@ SSH_KEY_PUB=$(cat "${SSH_KEY}".pub)
 
 case "${ID}-${VERSION_ID}" in
     "rhel-8.4")
+        OSTREE_REF="test/rhel/8/${ARCH}/edge"
+        OS_VARIANT="rhel8-unknown"
         CONTAINER_TYPE=rhel-edge-container
         CONTAINER_FILENAME=rhel84-container.tar
         INSTALLER_TYPE=rhel-edge-installer
@@ -111,11 +116,27 @@ case "${ID}-${VERSION_ID}" in
         USER_IN_UPGRADE_BP="true"
         INSTALLER_PATH="/ostree/repo"
         ;;
-    "rhel-8.6" | "centos-8" | "rhel-9.0" | "centos-9")
-        CONTAINER_TYPE=edge-container
-        CONTAINER_FILENAME=container.tar
-        INSTALLER_TYPE=edge-installer
-        INSTALLER_FILENAME=installer.iso
+    "rhel-8.6")
+        OSTREE_REF="test/rhel/8/${ARCH}/edge"
+        OS_VARIANT="rhel8-unknown"
+        USER_IN_UPGRADE_BP="false"
+        INSTALLER_PATH="/run/install/repo/ostree/repo"
+        ;;
+    "rhel-9.0")
+        OSTREE_REF="test/rhel/9/${ARCH}/edge"
+        OS_VARIANT="rhel9-unknown"
+        USER_IN_UPGRADE_BP="false"
+        INSTALLER_PATH="/run/install/repo/ostree/repo"
+        ;;
+    "centos-8")
+        OSTREE_REF="test/centos/8/${ARCH}/edge"
+        OS_VARIANT="centos8"
+        USER_IN_UPGRADE_BP="false"
+        INSTALLER_PATH="/run/install/repo/ostree/repo"
+        ;;
+    "centos-9")
+        OSTREE_REF="test/centos/9/${ARCH}/edge"
+        OS_VARIANT="centos9"
         USER_IN_UPGRADE_BP="false"
         INSTALLER_PATH="/run/install/repo/ostree/repo"
         ;;
diff --git a/test/cases/ostree-raw-image.sh b/test/cases/ostree-raw-image.sh
index f6e9ec1f9..46cfefb86 100755
--- a/test/cases/ostree-raw-image.sh
+++ b/test/cases/ostree-raw-image.sh
@@ -68,8 +68,6 @@ polkit.addRule(function(action, subject) {
 EOF
 
 # Set up variables.
-OSTREE_REF="rhel/8/${ARCH}/edge"
-OS_VARIANT="rhel8-unknown"
 TEST_UUID=$(uuidgen)
 IMAGE_KEY="edge-${TEST_UUID}"
 BIOS_GUEST_ADDRESS=192.168.100.50
@@ -79,6 +77,10 @@ PROD_REPO=/var/www/html/repo
 STAGE_REPO_ADDRESS=192.168.200.1
 STAGE_REPO_URL="http://${STAGE_REPO_ADDRESS}:8080/repo/"
 ARTIFACTS="ci-artifacts"
+CONTAINER_TYPE=edge-container
+CONTAINER_FILENAME=container.tar
+INSTALLER_TYPE=edge-raw-image
+INSTALLER_FILENAME=image.raw.xz
 mkdir -p "${ARTIFACTS}"
 
 # Set up temporary files.
@@ -94,11 +96,21 @@ SSH_KEY=${SSH_DATA_DIR}/id_rsa
 SSH_KEY_PUB=$(cat "${SSH_KEY}".pub)
 
 case "${ID}-${VERSION_ID}" in
-    "rhel-8.6" | "centos-8" | "rhel-9.0" | "centos-9")
-        CONTAINER_TYPE=edge-container
-        CONTAINER_FILENAME=container.tar
-        INSTALLER_TYPE=edge-raw-image
-        INSTALLER_FILENAME=image.raw.xz
+    "rhel-8.6")
+        OSTREE_REF="rhel/8/${ARCH}/edge"
+        OS_VARIANT="rhel8-unknown"
+        ;;
+    "rhel-9.0")
+        OSTREE_REF="rhel/9/${ARCH}/edge"
+        OS_VARIANT="rhel9-unknown"
+        ;;
+    "centos-8")
+        OSTREE_REF="centos/8/${ARCH}/edge"
+        OS_VARIANT="centos8"
+        ;;
+    "centos-9")
+        OSTREE_REF="centos/9/${ARCH}/edge"
+        OS_VARIANT="centos9"
         ;;
     *)
         echo "unsupported distro: ${ID}-${VERSION_ID}"
diff --git a/test/cases/ostree-simplified-installer.sh b/test/cases/ostree-simplified-installer.sh
index 8a81548c5..e470c3d9c 100755
--- a/test/cases/ostree-simplified-installer.sh
+++ b/test/cases/ostree-simplified-installer.sh
@@ -109,14 +109,14 @@ case "${ID}-${VERSION_ID}" in
         OSTREE_REF="rhel/8/${ARCH}/edge"
         OS_VARIANT="rhel8-unknown"
         ;;
-    "centos-8")
-        OSTREE_REF="centos/8/${ARCH}/edge"
-        OS_VARIANT="rhel8-unknown"
-        ;;
     "rhel-9.0")
         OSTREE_REF="rhel/9/${ARCH}/edge"
         OS_VARIANT="rhel9-unknown"
         ;;
+    "centos-8")
+        OSTREE_REF="centos/8/${ARCH}/edge"
+        OS_VARIANT="rhel8-unknown"
+        ;;
     "centos-9")
         OSTREE_REF="centos/9/${ARCH}/edge"
         OS_VARIANT="rhel9-unknown"
diff --git a/test/cases/ostree.sh b/test/cases/ostree.sh
index ec52e0e64..fd57a089b 100755
--- a/test/cases/ostree.sh
+++ b/test/cases/ostree.sh
@@ -16,31 +16,43 @@ case "${ID}-${VERSION_ID}" in
         OSTREE_REF="fedora/33/${ARCH}/iot"
         OS_VARIANT="fedora33"
         USER_IN_COMMIT="false"
-        BOOT_LOCATION="https://mirrors.rit.edu/fedora/fedora/linux/releases/33/Everything/x86_64/os/";;
+        BOOT_LOCATION="https://mirrors.rit.edu/fedora/fedora/linux/releases/33/Everything/x86_64/os/"
+        ;;
     "rhel-8.4")
         IMAGE_TYPE=rhel-edge-commit
         OSTREE_REF="rhel/8/${ARCH}/edge"
         OS_VARIANT="rhel8.4"
         USER_IN_COMMIT="false"
-        BOOT_LOCATION="http://download.devel.redhat.com/released/rhel-8/RHEL-8/8.4.0/BaseOS/x86_64/os/";;
+        BOOT_LOCATION="http://download.devel.redhat.com/released/rhel-8/RHEL-8/8.4.0/BaseOS/x86_64/os/"
+        ;;
     "rhel-8.6")
         IMAGE_TYPE=edge-commit
         OSTREE_REF="rhel/8/${ARCH}/edge"
         OS_VARIANT="rhel8-unknown"
         USER_IN_COMMIT="true"
-        BOOT_LOCATION="$COMPOSE_URL/compose/BaseOS/x86_64/os/";;
+        BOOT_LOCATION="$COMPOSE_URL/compose/BaseOS/x86_64/os/"
+        ;;
     "rhel-9.0")
         IMAGE_TYPE=edge-commit
         OSTREE_REF="rhel/9/${ARCH}/edge"
         OS_VARIANT="rhel9.0"
         USER_IN_COMMIT="true"
-        BOOT_LOCATION="$COMPOSE_URL/compose/BaseOS/x86_64/os/";;
+        BOOT_LOCATION="$COMPOSE_URL/compose/BaseOS/x86_64/os/"
+        ;;
+    "centos-8")
+        IMAGE_TYPE=edge-commit
+        OSTREE_REF="centos/8/${ARCH}/edge"
+        OS_VARIANT="centos8"
+        USER_IN_COMMIT="true"
+        BOOT_LOCATION="$COMPOSE_URL/compose/BaseOS/x86_64/os/"
+        ;;
     "centos-9")
         IMAGE_TYPE=edge-commit
         OSTREE_REF="centos/9/${ARCH}/edge"
         OS_VARIANT="centos9"
         USER_IN_COMMIT="true"
-        BOOT_LOCATION="$COMPOSE_URL/compose/BaseOS/x86_64/os/";;
+        BOOT_LOCATION="$COMPOSE_URL/compose/BaseOS/x86_64/os/"
+        ;;
     *)
         echo "unsupported distro: ${ID}-${VERSION_ID}"
         exit 1;;