Reasons for this change: - Mixed versions of composer and worker aren't a realistic use-case for the weldr API (on prem) but we do run mixed versions in hosted IB, so this test is closer to real world scenarios. - The cloud API runs depsolve jobs in the worker, whereas the weldr API runs them in composer. By testing the cloud API we also test the backwards compatibility of the depsolve job. The change requires osbuild-worker v51 or newer to be able to handle depsolve and manifest jobs on the worker as well as depsolve chains.
420 lines
13 KiB
Bash
420 lines
13 KiB
Bash
#!/bin/bash
|
|
|
|
# Verify that an older worker (v51) is still compatible with this composer
|
|
# version.
|
|
#
|
|
# Any tweaks to the worker api need to be backwards compatible.
|
|
|
|
set -exuo pipefail
|
|
|
|
# Colorful timestamped output.
|
|
function greenprint {
|
|
echo -e "\033[1;32m[$(date -Isecond)] ${1}\033[0m"
|
|
}
|
|
|
|
ARTIFACTS=ci-artifacts
|
|
mkdir -p "${ARTIFACTS}"
|
|
|
|
source /usr/libexec/osbuild-composer-test/set-env-variables.sh
|
|
|
|
# Only run this on x86 and rhel8 GA
|
|
if [ "$ARCH" != "x86_64" ] || [ "$ID" != rhel ] || ! sudo subscription-manager status; then
|
|
echo "Test only supported on GA RHEL."
|
|
exit 0
|
|
fi
|
|
|
|
# Provision the software under test.
|
|
/usr/libexec/osbuild-composer-test/provision.sh
|
|
|
|
WORKER_VERSION=67727d1e5cb3f1f86eafd890541381834d001743
|
|
WORKER_RPM=osbuild-composer-worker-51-1.20220504git67727d1.el8.x86_64
|
|
|
|
# Container image used for cloud provider CLI tools
|
|
CONTAINER_IMAGE_CLOUD_TOOLS="quay.io/osbuild/cloud-tools:latest"
|
|
|
|
greenprint "Copying repository configs from test rpms"
|
|
REPOS=$(mktemp -d)
|
|
sudo dnf -y install osbuild-composer-tests
|
|
sudo cp -a /usr/share/tests/osbuild-composer/repositories "$REPOS/repositories"
|
|
|
|
greenprint "Stop and disable all services and sockets"
|
|
sudo systemctl stop osbuild-composer.service osbuild-composer.socket osbuild-worker@1.service osbuild-dnf-json.service osbuild-dnf-json.socket
|
|
sudo systemctl disable osbuild-composer.service osbuild-composer.socket osbuild-worker@1.service osbuild-dnf-json.service osbuild-dnf-json.socket
|
|
|
|
greenprint "Removing latest worker"
|
|
sudo dnf remove -y osbuild-composer osbuild-composer-worker osbuild-composer-tests
|
|
|
|
function setup_repo {
|
|
local project=$1
|
|
local commit=$2
|
|
local priority=${3:-10}
|
|
echo "Setting up dnf repository for ${project} ${commit}"
|
|
sudo tee "/etc/yum.repos.d/${project}.repo" << EOF
|
|
[${project}]
|
|
name=${project} ${commit}
|
|
baseurl=http://osbuild-composer-repos.s3-website.us-east-2.amazonaws.com/${project}/rhel-8-cdn/x86_64/${commit}
|
|
enabled=1
|
|
gpgcheck=0
|
|
priority=${priority}
|
|
EOF
|
|
}
|
|
|
|
# Composer v51
|
|
greenprint "Installing osbuild-composer-worker from commit ${WORKER_VERSION}"
|
|
setup_repo osbuild-composer "$WORKER_VERSION" 20
|
|
sudo dnf install -y osbuild-composer-worker osbuild-composer-dnf-json podman composer-cli
|
|
|
|
# verify the right worker is installed just to be sure
|
|
rpm -q "$WORKER_RPM"
|
|
|
|
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 used for cloud provider CLI tools
|
|
CONTAINER_IMAGE_CLOUD_TOOLS="quay.io/osbuild/cloud-tools:latest"
|
|
|
|
greenprint "Pulling and running composer container for this commit"
|
|
sudo ${CONTAINER_RUNTIME} pull --creds "${V2_QUAY_USERNAME}":"${V2_QUAY_PASSWORD}" \
|
|
"quay.io/osbuild/osbuild-composer-ubi-pr:${CI_COMMIT_SHA}"
|
|
|
|
cat <<EOF | sudo tee "/etc/osbuild-composer/osbuild-composer.toml"
|
|
log_level = "debug"
|
|
[koji]
|
|
allowed_domains = [ "localhost", "client.osbuild.org" ]
|
|
ca = "/etc/osbuild-composer/ca-crt.pem"
|
|
[koji.aws_config]
|
|
bucket = "${AWS_BUCKET}"
|
|
[worker]
|
|
allowed_domains = [ "localhost", "worker.osbuild.org" ]
|
|
ca = "/etc/osbuild-composer/ca-crt.pem"
|
|
EOF
|
|
|
|
# The host entitlement doesn't get picked up by composer
|
|
# see https://github.com/osbuild/osbuild-composer/issues/1845
|
|
sudo ${CONTAINER_RUNTIME} run \
|
|
--name=composer \
|
|
-d \
|
|
-v /etc/osbuild-composer:/etc/osbuild-composer:Z \
|
|
-v /etc/rhsm:/etc/rhsm:Z \
|
|
-v /etc/pki/entitlement:/etc/pki/entitlement:Z \
|
|
-v "$REPOS/repositories":/usr/share/osbuild-composer/repositories:Z \
|
|
-p 8700:8700 \
|
|
-p 8080:8080 \
|
|
"quay.io/osbuild/osbuild-composer-ubi-pr:${CI_COMMIT_SHA}" \
|
|
--remote-worker-api --no-local-worker-api
|
|
|
|
greenprint "Wait for composer API"
|
|
while ! openapi=$(curl --silent --show-error --cacert /etc/osbuild-composer/ca-crt.pem --key /etc/osbuild-composer/client-key.pem --cert /etc/osbuild-composer/client-crt.pem https://localhost:8080/api/image-builder-composer/v2/openapi); do
|
|
sleep 10
|
|
done
|
|
jq . <<< "${openapi}"
|
|
|
|
|
|
greenprint "Starting osbuild-remote-worker service and dnf-json socket"
|
|
set +e
|
|
# reload in case there were changes in units
|
|
sudo systemctl daemon-reload
|
|
sudo systemctl enable --now osbuild-remote-worker@localhost:8700.service
|
|
while ! sudo systemctl --quiet is-active osbuild-remote-worker@localhost:8700.service; do
|
|
sudo systemctl status osbuild-remote-worker@localhost:8700.service
|
|
sleep 1
|
|
sudo systemctl enable --now osbuild-remote-worker@localhost:8700.service
|
|
done
|
|
sudo systemctl enable --now osbuild-dnf-json.socket
|
|
while ! sudo systemctl --quiet is-active osbuild-dnf-json.socket; do
|
|
sudo systemctl status osbuild-dnf-json.socket
|
|
sleep 1
|
|
sudo systemctl enable --now osbuild-dnf-json.socket
|
|
done
|
|
set -e
|
|
|
|
# Check that needed variables are set to access AWS.
|
|
printenv AWS_REGION AWS_BUCKET V2_AWS_ACCESS_KEY_ID V2_AWS_SECRET_ACCESS_KEY AWS_API_TEST_SHARE_ACCOUNT > /dev/null
|
|
|
|
# Check that needed variables are set to register to RHSM
|
|
printenv API_TEST_SUBSCRIPTION_ORG_ID API_TEST_SUBSCRIPTION_ACTIVATION_KEY_V2 > /dev/null
|
|
|
|
function cleanupAWSS3() {
|
|
local S3_URL
|
|
S3_URL=$(echo "$UPLOAD_OPTIONS" | jq -r '.url')
|
|
|
|
# extract filename component from URL
|
|
local S3_FILENAME
|
|
S3_FILENAME=$(echo "${S3_URL}" | grep -oP '(?<=/)[^/]+(?=\?)')
|
|
|
|
# prepend bucket
|
|
local S3_URI
|
|
S3_URI="s3://${AWS_BUCKET}/${S3_FILENAME}"
|
|
|
|
# since this function can be called at any time, ensure that we don't expand unbound variables
|
|
AWS_CMD="${AWS_CMD:-}"
|
|
|
|
if [ -n "$AWS_CMD" ]; then
|
|
$AWS_CMD s3 rm "${S3_URI}"
|
|
fi
|
|
}
|
|
|
|
# Set up cleanup functions
|
|
# Create a temporary directory and ensure it gets deleted when this script
|
|
# terminates in any way.
|
|
WORKDIR=$(mktemp -d)
|
|
KILL_PIDS=()
|
|
function cleanup() {
|
|
set +eu
|
|
cleanupAWSS3
|
|
|
|
sudo ${CONTAINER_RUNTIME} kill composer
|
|
sudo ${CONTAINER_RUNTIME} rm composer
|
|
|
|
sudo rm -rf "$WORKDIR"
|
|
|
|
for P in "${KILL_PIDS[@]}"; do
|
|
sudo pkill -P "$P"
|
|
done
|
|
set -eu
|
|
}
|
|
trap cleanup EXIT
|
|
|
|
greenprint "Creating dummy rpm and repository to test payload_repositories"
|
|
sudo dnf install -y rpm-build createrepo
|
|
DUMMYRPMDIR=$(mktemp -d)
|
|
DUMMYSPECFILE="$DUMMYRPMDIR/dummy.spec"
|
|
PAYLOAD_REPO_PORT="9999"
|
|
PAYLOAD_REPO_URL="http://localhost:9999"
|
|
pushd "$DUMMYRPMDIR"
|
|
|
|
cat <<EOF > "$DUMMYSPECFILE"
|
|
#----------- spec file starts ---------------
|
|
Name: dummy
|
|
Version: 1.0.0
|
|
Release: 0
|
|
BuildArch: noarch
|
|
Vendor: dummy
|
|
Summary: Provides %{name}
|
|
License: BSD
|
|
Provides: dummy
|
|
%description
|
|
%{summary}
|
|
%files
|
|
EOF
|
|
|
|
mkdir -p "DUMMYRPMDIR/rpmbuild"
|
|
rpmbuild --quiet --define "_topdir $DUMMYRPMDIR/rpmbuild" -bb "$DUMMYSPECFILE"
|
|
|
|
mkdir -p "$DUMMYRPMDIR/repo"
|
|
cp "$DUMMYRPMDIR"/rpmbuild/RPMS/noarch/*rpm "$DUMMYRPMDIR/repo"
|
|
pushd "$DUMMYRPMDIR/repo"
|
|
createrepo .
|
|
sudo python3 -m http.server "$PAYLOAD_REPO_PORT" &
|
|
KILL_PIDS+=("$!")
|
|
popd
|
|
popd
|
|
|
|
greenprint "Installing aws client tools"
|
|
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 ${WORKDIR}:${WORKDIR}: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
|
|
|
|
greenprint "Preparing request"
|
|
REQUEST_FILE="${WORKDIR}/request.json"
|
|
ARCH=$(uname -m)
|
|
CLOUD_PROVIDER="aws.s3"
|
|
IMAGE_TYPE="guest-image"
|
|
|
|
# This removes dot from VERSION_ID.
|
|
# ID == rhel && VERSION_ID == 8.6 => DISTRO == rhel-86
|
|
# ID == centos && VERSION_ID == 8 => DISTRO == centos-8
|
|
# ID == fedora && VERSION_ID == 35 => DISTRO == fedora-35
|
|
DISTRO="$ID-${VERSION_ID//./}"
|
|
|
|
cat > "$REQUEST_FILE" << EOF
|
|
{
|
|
"distribution": "$DISTRO",
|
|
"customizations": {
|
|
"payload_repositories": [
|
|
{
|
|
"baseurl": "$PAYLOAD_REPO_URL"
|
|
}
|
|
],
|
|
"packages": [
|
|
"postgresql",
|
|
"dummy"
|
|
],
|
|
"users": [
|
|
{
|
|
"name": "user1",
|
|
"groups": ["wheel"]
|
|
},
|
|
{
|
|
"name": "user2"
|
|
}
|
|
]
|
|
},
|
|
"image_request": {
|
|
"architecture": "$ARCH",
|
|
"image_type": "${IMAGE_TYPE}",
|
|
"repositories": $(jq ".\"$ARCH\" | .[] | select((has(\"image_type_tags\") | not) or (.\"image_type_tags\" | index(\"${IMAGE_TYPE}\")))" "${REPOS}/repositories/${DISTRO}".json | jq -s .),
|
|
"upload_options": {
|
|
"region": "${AWS_REGION}"
|
|
}
|
|
}
|
|
}
|
|
EOF
|
|
|
|
greenprint "Request data"
|
|
cat "${REQUEST_FILE}"
|
|
|
|
#
|
|
# Send the request and wait for the job to finish.
|
|
#
|
|
# Separate `curl` and `jq` commands here, because piping them together hides
|
|
# the server's response in case of an error.
|
|
#
|
|
|
|
function sendCompose() {
|
|
OUTPUT=$(mktemp)
|
|
HTTPSTATUS=$(curl \
|
|
--silent \
|
|
--show-error \
|
|
--cacert /etc/osbuild-composer/ca-crt.pem \
|
|
--key /etc/osbuild-composer/client-key.pem \
|
|
--cert /etc/osbuild-composer/client-crt.pem \
|
|
--header 'Content-Type: application/json' \
|
|
--request POST \
|
|
--data @"$1" \
|
|
--write-out '%{http_code}' \
|
|
--output "$OUTPUT" \
|
|
https://localhost:8080/api/image-builder-composer/v2/compose)
|
|
|
|
test "$HTTPSTATUS" = "201"
|
|
COMPOSE_ID=$(jq -r '.id' "$OUTPUT")
|
|
}
|
|
|
|
function waitForState() {
|
|
local DESIRED_STATE="${1:-success}"
|
|
|
|
while true
|
|
do
|
|
OUTPUT=$(curl \
|
|
--silent \
|
|
--show-error \
|
|
--cacert /etc/osbuild-composer/ca-crt.pem \
|
|
--key /etc/osbuild-composer/client-key.pem \
|
|
--cert /etc/osbuild-composer/client-crt.pem \
|
|
"https://localhost:8080/api/image-builder-composer/v2/composes/$COMPOSE_ID")
|
|
|
|
COMPOSE_STATUS=$(echo "$OUTPUT" | jq -r '.image_status.status')
|
|
UPLOAD_STATUS=$(echo "$OUTPUT" | jq -r '.image_status.upload_status.status')
|
|
UPLOAD_TYPE=$(echo "$OUTPUT" | jq -r '.image_status.upload_status.type')
|
|
UPLOAD_OPTIONS=$(echo "$OUTPUT" | jq -r '.image_status.upload_status.options')
|
|
|
|
case "$COMPOSE_STATUS" in
|
|
"$DESIRED_STATE")
|
|
break
|
|
;;
|
|
# all valid status values for a compose which hasn't finished yet
|
|
"pending"|"building"|"uploading"|"registering")
|
|
;;
|
|
# default undesired state
|
|
"failure")
|
|
echo "Image compose failed"
|
|
exit 1
|
|
;;
|
|
*)
|
|
echo "API returned unexpected image_status.status value: '$COMPOSE_STATUS'"
|
|
exit 1
|
|
;;
|
|
esac
|
|
|
|
sleep 30
|
|
done
|
|
}
|
|
|
|
greenprint "Sending compose request to composer"
|
|
sendCompose "$REQUEST_FILE"
|
|
greenprint "Waiting for success"
|
|
waitForState success
|
|
|
|
test "$UPLOAD_STATUS" = "success"
|
|
test "$UPLOAD_TYPE" = "$CLOUD_PROVIDER"
|
|
|
|
# Verify upload options
|
|
S3_URL=$(echo "$UPLOAD_OPTIONS" | jq -r '.url')
|
|
|
|
# S3 URL contains region and bucket name
|
|
echo "$S3_URL" | grep -F "$AWS_BUCKET" -
|
|
echo "$S3_URL" | grep -F "$AWS_REGION" -
|
|
|
|
# verify S3 blob
|
|
S3_URL=$(echo "$UPLOAD_OPTIONS" | jq -r '.url')
|
|
greenprint "Verifying S3 object at ${S3_URL}"
|
|
|
|
# Tag the resource as a test file
|
|
S3_FILENAME=$(echo "${S3_URL}" | grep -oP '(?<=/)[^/]+(?=\?)')
|
|
|
|
# tag the object, also verifying that it exists in the bucket as expected
|
|
$AWS_CMD s3api put-object-tagging \
|
|
--bucket "${AWS_BUCKET}" \
|
|
--key "${S3_FILENAME}" \
|
|
--tagging '{"TagSet": [{ "Key": "gitlab-ci-test", "Value": "true" }]}'
|
|
|
|
greenprint "✅ Successfully tagged S3 object"
|
|
|
|
greenprint "Installing osbuild-composer-tests for image-info"
|
|
sudo dnf install -y osbuild-composer-tests
|
|
|
|
curl "${S3_URL}" --output "${WORKDIR}/disk.qcow2"
|
|
|
|
# Verify image blobs from s3
|
|
function verifyDisk() {
|
|
filename="$1"
|
|
greenprint "Verifying contents of ${filename}"
|
|
|
|
infofile="${filename}-info.json"
|
|
sudo /usr/libexec/osbuild-composer-test/image-info "${filename}" | tee "${infofile}" > /dev/null
|
|
|
|
# save image info to artifacts
|
|
cp -v "${infofile}" "${ARTIFACTS}/image-info.json"
|
|
|
|
# check compose request users in passwd
|
|
if ! jq .passwd "${infofile}" | grep -q "user1"; then
|
|
greenprint "❌ user1 not found in passwd file"
|
|
exit 1
|
|
fi
|
|
if ! jq .passwd "${infofile}" | grep -q "user2"; then
|
|
greenprint "❌ user2 not found in passwd file"
|
|
exit 1
|
|
fi
|
|
# check packages for postgresql
|
|
if ! jq .packages "${infofile}" | grep -q "postgresql"; then
|
|
greenprint "❌ postgresql not found in packages"
|
|
exit 1
|
|
fi
|
|
|
|
greenprint "✅ ${filename} image info verified"
|
|
}
|
|
|
|
|
|
verifyDisk "${WORKDIR}/disk.qcow2"
|
|
greenprint "✅ Successfully verified S3 object"
|
|
|
|
greenprint "Test passed!"
|
|
exit 0
|