From 1c23503c2284ec0dcf46d0d6449b0663546375a1 Mon Sep 17 00:00:00 2001 From: Suprit-Iti Date: Fri, 19 Jul 2024 11:25:37 +0530 Subject: [PATCH] Add libvirt provider support for image building Added a new handler for libvirt to build the qcow2 image. New ConfigMap added for libvirt specific data. Modified the podvm create job to accomodate libvirt as well. Added required changes for the image_generator controller. Signed-off-by: Suprit-Iti --- config/peerpods/peerpodssecret.yaml | 1 + .../peerpods/podvm/Dockerfile.podvm-builder | 2 +- config/peerpods/podvm/lib.sh | 36 ++- .../podvm/libvirt-podvm-image-cm.yaml | 23 ++ .../podvm/libvirt-podvm-image-handler.sh | 283 ++++++++++++++++++ .../peerpods/podvm/osc-podvm-create-job.yaml | 16 +- .../peerpods/podvm/osc-podvm-delete-job.yaml | 18 ++ config/peerpods/podvm/podvm-builder.sh | 62 +++- controllers/image_generator.go | 35 ++- controllers/utils.go | 5 +- 10 files changed, 461 insertions(+), 20 deletions(-) create mode 100644 config/peerpods/podvm/libvirt-podvm-image-cm.yaml create mode 100755 config/peerpods/podvm/libvirt-podvm-image-handler.sh diff --git a/config/peerpods/peerpodssecret.yaml b/config/peerpods/peerpodssecret.yaml index 45743954..2772dd58 100644 --- a/config/peerpods/peerpodssecret.yaml +++ b/config/peerpods/peerpodssecret.yaml @@ -9,3 +9,4 @@ stringData: #LIBVIRT_URI: "qemu+ssh://root@192.168.122.1/system?no_verify=1" #LIBVIRT_NET: "default #LIBVIRT_POOL: "default" + #REDHAT_OFFLINE_TOKEN: "" #Required to download rhel base image : Download token from https://access.redhat.com/management/api \ No newline at end of file diff --git a/config/peerpods/podvm/Dockerfile.podvm-builder b/config/peerpods/podvm/Dockerfile.podvm-builder index 447e7a76..6803e79c 100644 --- a/config/peerpods/podvm/Dockerfile.podvm-builder +++ b/config/peerpods/podvm/Dockerfile.podvm-builder @@ -12,7 +12,7 @@ LABEL kata_src_commit=stable-3.6 RUN mkdir -p /scripts -ADD lib.sh aws-podvm-image-handler.sh azure-podvm-image-handler.sh /scripts/ +ADD lib.sh libvirt-podvm-image-handler.sh aws-podvm-image-handler.sh azure-podvm-image-handler.sh /scripts/ RUN /scripts/azure-podvm-image-handler.sh -- install_rpms diff --git a/config/peerpods/podvm/lib.sh b/config/peerpods/podvm/lib.sh index 0234fd4c..3681a2d4 100644 --- a/config/peerpods/podvm/lib.sh +++ b/config/peerpods/podvm/lib.sh @@ -29,6 +29,7 @@ function install_rpm_packages() { "make" "unzip" "skopeo" + "jq" ) # Create a new array to store rpm packages that are not installed @@ -65,20 +66,28 @@ function install_rpm_packages() { # are available in the variable REQUIRED_BINARY_PACKAGES # the function will download the packages, extract them and install them in /usr/local/bin # Following are the packages that are installed: -# TBD: add multi-arch support for these binaries #"packer=https://releases.hashicorp.com/packer/1.9.4/packer_1.9.4_linux_amd64.zip" #"kubectl=https://mirror.openshift.com/pub/openshift-v4/x86_64/clients/ocp/4.14.9/openshift-client-linux.tar.gz" #"yq=https://github.com/mikefarah/yq/releases/download/v4.35.2/yq_linux_amd64.tar.gz" #"umoci=https://github.com/opencontainers/umoci/releases/download/v0.4.7/umoci.amd64" install_binary_packages() { - # Define the required binary packages - REQUIRED_BINARY_PACKAGES=( - "packer=https://releases.hashicorp.com/packer/1.9.4/packer_1.9.4_linux_amd64.zip" - "kubectl=https://mirror.openshift.com/pub/openshift-v4/x86_64/clients/ocp/4.14.9/openshift-client-linux.tar.gz" - "yq=https://github.com/mikefarah/yq/releases/download/v4.35.2/yq_linux_amd64.tar.gz" - "umoci=https://github.com/opencontainers/umoci/releases/download/v0.4.7/umoci.amd64" - ) + ARCH=$(uname -m) + if [ "${ARCH}" == "s390x" ]; then + # Define the required binary packages + REQUIRED_BINARY_PACKAGES=( + "kubectl=https://mirror.openshift.com/pub/openshift-v4/s390x/clients/ocp/4.14.9/openshift-client-linux.tar.gz" + "yq=https://github.com/mikefarah/yq/releases/download/v4.35.1/yq_linux_s390x.tar.gz" + ) + else + # Define the required binary packages + REQUIRED_BINARY_PACKAGES=( + "packer=https://releases.hashicorp.com/packer/1.9.4/packer_1.9.4_linux_amd64.zip" + "kubectl=https://mirror.openshift.com/pub/openshift-v4/x86_64/clients/ocp/4.14.9/openshift-client-linux.tar.gz" + "yq=https://github.com/mikefarah/yq/releases/download/v4.35.2/yq_linux_amd64.tar.gz" + "umoci=https://github.com/opencontainers/umoci/releases/download/v0.4.7/umoci.amd64" + ) + fi # Specify the installation directory local install_dir="/usr/local/bin" @@ -110,9 +119,14 @@ install_binary_packages() { fi echo "Marking ${install_dir}/${package_name} executable" - # yq extracted file name is yq_linux_amd64. Rename it - [[ "${package_name}" == "yq" ]] && - mv "${install_dir}/yq_linux_amd64" "${install_dir}/yq" + # yq extracted file name is yq_linux_${ARCH}. Rename it + if [[ "${package_name}" == "yq" ]]; then + if [ "${ARCH}" == "s390x" ]; then + mv "${install_dir}/yq_linux_"${ARCH}"" "${install_dir}/yq" + else + mv "${install_dir}/yq_linux_amd64" "${install_dir}/yq" + fi + fi chmod +x "${install_dir}/${package_name}" || error_exit "Failed to mark ${package_name} executable" diff --git a/config/peerpods/podvm/libvirt-podvm-image-cm.yaml b/config/peerpods/podvm/libvirt-podvm-image-cm.yaml new file mode 100644 index 00000000..8a9d2d52 --- /dev/null +++ b/config/peerpods/podvm/libvirt-podvm-image-cm.yaml @@ -0,0 +1,23 @@ +apiVersion: v1 +kind: ConfigMap +metadata: + name: libvirt-podvm-image-cm + namespace: openshift-sandboxed-containers-operator +data: + # PodVM image distro + PODVM_DISTRO: "rhel" + + # Pod VM sources + # If changing the source, then ensure the respective payload binaries are available + # for the new source + CAA_SRC: "https://github.com/confidential-containers/cloud-api-adaptor" + CAA_REF: "v0.8.2" + + # Booleans + DOWNLOAD_SOURCES: "no" + CONFIDENTIAL_COMPUTE_ENABLED: "no" + + # Libvirt specific + ORG_ID: "" + ACTIVATION_KEY: "" + BASE_OS_VERSION: "9.2" diff --git a/config/peerpods/podvm/libvirt-podvm-image-handler.sh b/config/peerpods/podvm/libvirt-podvm-image-handler.sh new file mode 100755 index 00000000..58b98570 --- /dev/null +++ b/config/peerpods/podvm/libvirt-podvm-image-handler.sh @@ -0,0 +1,283 @@ +#!/bin/bash +# FILEPATH: libvirt-podvm-image-handler.sh + +# This script is used to create or delete libvirt qcow2 image for podvm +# The basic assumption is that the required variables are set as environment variables in the pod +# Typically the variables are read from configmaps and set as environment variables in the pod +# The script will be called with one of the following options: +# Create image (-c) +# Delete image (-C) + +set -x +# include common functions from lib.sh +# shellcheck source=/dev/null +# The directory is where libvirt-podvm-image-handler.sh is located +source /scripts/lib.sh + +# Function to verify that the required variables are set + +function verify_vars() { + # Ensure CLOUD_PROVIDER is set to libvirt + [[ -z "${CLOUD_PROVIDER}" || "${CLOUD_PROVIDER}" != "libvirt" ]] && error_exit "CLOUD_PROVIDER is empty or not set to libvirt" + + [[ -z "${ORG_ID}" ]] && error_exit "ORG_ID is not set" + [[ -z "${ACTIVATION_KEY}" ]] && error_exit "ACTIVATION_KEY is not set" + [[ -z "${BASE_OS_VERSION}" ]] && error_exit "BASE_OS_VERSION is not set" + + [[ -z "${PODVM_DISTRO}" ]] && error_exit "PODVM_DISTRO is not set" + + [[ -z "${CAA_SRC}" ]] && error_exit "CAA_SRC is empty" + [[ -z "${CAA_REF}" ]] && error_exit "CAA_REF is empty" + + [[ -z "${REDHAT_OFFLINE_TOKEN}" ]] && error_exit "Redhat token is not set" + + # Ensure booleans are set + [[ -z "${DOWNLOAD_SOURCES}" ]] && error_exit "DOWNLOAD_SOURCES is empty" +} + + + +function create_libvirt_image() { + echo "Creating libvirt qcow2 image" + + # If any error occurs, exit the script with an error message + + if [[ "${DOWNLOAD_SOURCES}" == "yes" ]]; then + # Download source code from GitHub + download_source_code + fi + + # Prepare the source code for building the libvirt + prepare_source_code + + # Dowload the base rhel image for packer + download_rhel_kvm_guest_qcow2 + + # Prepare the pause image for embedding into the libvirt image + download_and_extract_pause_image "${PAUSE_IMAGE_REPO}" "${PAUSE_IMAGE_VERSION}" "${PAUSE_IMAGE_REPO_AUTH_FILE}" + + cd "${CAA_SRC_DIR}"/podvm || \ + error_exit "Failed to change directory to "${CAA_SRC_DIR}"/podvm" + LIBC=gnu make BINARIES= PAUSE_BUNDLE= image + + export PODVM_IMAGE_PATH=/payload/podvm-libvirt.qcow2 + cp -pr "${CAA_SRC_DIR}"/podvm/output/*.qcow2 "${PODVM_IMAGE_PATH}" + + # Upload the created qcow2 to the volume + upload_libvirt_image + + # Add the libvirt_volume_name to peer-pods-cm configmap + add_libvirt_vol_to_peer_pods_cm +} + +# Function to dowload the rhel base image + +function download_rhel_kvm_guest_qcow2() { + ARCH=$(uname -m) + export ARCH + + # Define the API endpoints + TOKEN_GENERATOR_URI=https://sso.redhat.com/auth/realms/redhat-external/protocol/openid-connect/token + IMAGES_URI=https://api.access.redhat.com/management/v1/images/rhel/"${BASE_OS_VERSION}"/"${ARCH}" + + filename="rhel-"${BASE_OS_VERSION}"-"${ARCH}"-kvm.qcow2" + + token=$(curl "${TOKEN_GENERATOR_URI}" \ + -d grant_type=refresh_token -d client_id=rhsm-api -d refresh_token="${REDHAT_OFFLINE_TOKEN}" | jq --raw-output .access_token) + images=$(curl -X 'GET' "${IMAGES_URI}" \ + -H 'accept: application/json' -H "Authorization: Bearer "${token}"" | jq ) + + download_href=$(echo "${images}" | jq -r --arg fn "${filename}" '.body[] | select(.filename == $fn) | .downloadHref') + + download_url=$(curl -X 'GET' "${download_href}" \ + -H "Authorization: Bearer "${token}"" -H 'accept: application/json' | jq -r .body.href ) + + curl -X GET "${download_url}" -H "Authorization: Bearer "${token}"" --output rhel-"${BASE_OS_VERSION}"-"${ARCH}"-kvm.qcow2 + + cp -pr rhel-"${BASE_OS_VERSION}"-"${ARCH}"-kvm.qcow2 "${CAA_SRC_DIR}"/podvm/rhel-"${BASE_OS_VERSION}"-"${ARCH}"-kvm.qcow2 + + export IMAGE_URL="${CAA_SRC_DIR}"/podvm/rhel-"${BASE_OS_VERSION}"-"${ARCH}"-kvm.qcow2 + export IMAGE_CHECKSUM=$(sha256sum "${IMAGE_URL}" | awk '{ print $1 }') + +} + +# Function to upload the qcow2 image to volume + +function upload_libvirt_image() { + echo "LIBVIRT_VOL_NAME: "${LIBVIRT_VOL_NAME}"" && echo "LIBVIRT_POOL: "${LIBVIRT_POOL}"" && \ + echo "LIBVIRT_URI: "${LIBVIRT_URI}"" && echo "PODVM_IMAGE_PATH: "${PODVM_IMAGE_PATH}"" + echo "Starting to upload the image." + virsh -d 0 -c "${LIBVIRT_URI}" vol-upload --vol "${LIBVIRT_VOL_NAME}" "${PODVM_IMAGE_PATH}" --pool "${LIBVIRT_POOL}" --sparse + if [ $? -eq 0 ]; then + echo "Uploaded the image successfully" + fi +} + +# Function to add the libvirt_volume_name in the peer-pods-cm configmap + +function add_libvirt_vol_to_peer_pods_cm(){ + echo "Adding libvirt volume to peer-pods-cm configmap" + + # Check if the peer-pods-cm configmap exists + if ! kubectl get configmap peer-pods-cm -n openshift-sandboxed-containers-operator >/dev/null 2>&1; then + echo "peer-pods-cm configmap does not exist. Skipping adding the libvirt volume name" + return + fi + + # Add the libvirt image id to peer-pods-cm configmap + kubectl patch configmap peer-pods-cm -n openshift-sandboxed-containers-operator \ + --type merge -p "{\"data\":{\"LIBVIRT_IMAGE_ID\":\"${LIBVIRT_VOL_NAME}\"}}" || + error_exit "Failed to add the libvirt image id to peer-pods-cm configmap" + echo "libvirt image id added to peer-pods-cm configmap successfully" + +} + +# function to delete the libvirt +# LIBVIRT_POOL name must be set as an environment variable + +function delete_libvirt_image() { + echo "Deleting Libvirt image" + + # Delete the Libvirt pool + # If any error occurs, exit the script with an error message + + # LIBVIRT_POOL shouldn't be empty + [[ -z "${LIBVIRT_POOL}" ]] && error_exit "LIBVIRT_POOL is empty" + + # Delete the libvirt pool + echo "Deleting libvirt pool." + virsh -d 0 -c "${LIBVIRT_URI}" pool-destroy "${LIBVIRT_POOL}" || + error_exit "Failed to destroy the libvirt pool" + + virsh -d 0 -c "${LIBVIRT_URI}" pool-undefine "${LIBVIRT_POOL}" || + error_exit "Failed to undefine the libvirt pool" + + echo "Deleted libvirt image successfully" + + # Remove the libvirt_volume_name from peer-pods-cm configmap + delete_libvirt_vol_from_peer_pods_cm + +} + +# Function to delete the libvirt image id from the peer-pods-cm configmap + +function delete_libvirt_vol_from_peer_pods_cm() { + echo "Deleting libvirt volume from peer-pods-cm configmap" + + # Check if the peer-pods-cm configmap exists + if ! kubectl get configmap peer-pods-cm -n openshift-sandboxed-containers-operator >/dev/null 2>&1; then + echo "peer-pods-cm configmap does not exist. Skipping deleting the ami id" + return + fi + + # Delete the libvirt image id from peer-pods-cm configmap + kubectl patch configmap peer-pods-cm -n openshift-sandboxed-containers-operator \ + --type merge -p "{\"data\":{\"LIBVIRT_IMAGE_ID\":\"\"}}" || + error_exit "Failed to delete the libvirt image id from peer-pods-cm configmap" + echo "libvirt image id deleted from peer-pods-cm configmap successfully" +} + +# display help message + +function display_help() { + echo "This script is used to create libvirt qcow2 for podvm" + echo "Usage: $0 [-c|-C] [-- install_binaries]" + echo "Options:" + echo "-c Create image" + echo "-C Delete image" +} + +function install_packages(){ + + install_binary_packages + + #Install other libvirt specific binaries packages + ARCH=$(uname -m) + export ARCH + if [[ -n "${ACTIVATION_KEY}" && -n "${ORG_ID}" ]]; then + subscription-manager register --org="${ORG_ID}" --activationkey="${ACTIVATION_KEY}" + fi + subscription-manager repos --enable codeready-builder-for-rhel-9-"${ARCH}"-rpms + dnf install -y gcc genisoimage qemu-kvm libvirt-client + + GO_VERSION="1.21.9" + curl https://dl.google.com/go/go"${GO_VERSION}".linux-"${ARCH/x86_64/amd64}".tar.gz -o go"${GO_VERSION}".linux-"${ARCH/x86_64/amd64}".tar.gz && \ + rm -rf /usr/local/go && tar -C /usr/local -xzf go"${GO_VERSION}".linux-"${ARCH/x86_64/amd64}".tar.gz && \ + rm -f go"${GO_VERSION}".linux-"${ARCH/x86_64/amd64}".tar.gz + + export PATH="/usr/local/go/bin:"${PATH}"" + export GOPATH="/src" + +# Install packer. Packer doesn't does not have prebuilt s390x arch binaries above Packer version 0.1.5 + PACKER_VERSION="v1.9.4" + if [ "${ARCH}" == "s390x" ]; then + git clone --depth 1 --single-branch https://github.com/hashicorp/packer.git -b "${PACKER_VERSION}" + cd packer + sed -i -- "s/ALL_XC_ARCH=.*/ALL_XC_ARCH=\"${ARCH}\"/g" scripts/build.sh + sed -i -- "s/ALL_XC_OS=.*/ALL_XC_OS=\"Linux\"/g" scripts/build.sh + make bin && cp bin/packer /usr/local/bin/ + else + yum-config-manager --add-repo https://rpm.releases.hashicorp.com/RHEL/hashicorp.repo && \ + dnf install -y packer + fi + + # set a correspond qemu-system-* named link to qemu-kvm + ln -s /usr/libexec/qemu-kvm /usr/bin/qemu-system-"${ARCH}" + + # cloud-utils package is not available for rhel. + git clone https://github.com/canonical/cloud-utils + cd cloud-utils && make install + + # Install umoci from source as s390x binaries not available + mkdir -p umoci + git clone https://github.com/opencontainers/umoci.git + cd umoci + make + cp -pr umoci /usr/local/bin/ + +} +# main function + +if [ "$#" -eq 0 ]; then + display_help + exit 1 +fi + +if [ "$1" = "--" ]; then + shift + # Handle positional parameters + case "$1" in + + install_binaries) + install_packages + ;; + *) + echo "Unknown argument: $1" + exit 1 + ;; + esac +else + while getopts "cCh" opt; do + verify_vars + case ${opt} in + c) + # Create the libvirt image + create_libvirt_image + ;; + C) + # Delete the libvirt image + delete_libvirt_image + ;; + h) + # Display help + display_help + exit 0 + ;; + *) + # Invalid option + display_help + exit 1 + ;; + esac + done +fi diff --git a/config/peerpods/podvm/osc-podvm-create-job.yaml b/config/peerpods/podvm/osc-podvm-create-job.yaml index c07adccd..de741ac3 100644 --- a/config/peerpods/podvm/osc-podvm-create-job.yaml +++ b/config/peerpods/podvm/osc-podvm-create-job.yaml @@ -49,16 +49,30 @@ spec: - configMapRef: name: aws-podvm-image-cm optional: true + - configMapRef: + name: libvirt-podvm-image-cm + optional: true command: ["/podvm-builder.sh", "create"] volumeMounts: - name: payload mountPath: /payload - name: regauth - mountPath: /tmp/regauth + mountPath: /tmp/regauth + - name: ssh-key-secret + mountPath: "/root/.ssh/" + readOnly: true + optional: true volumes: - name: payload emptyDir: {} - name: regauth secret: secretName: auth-json-secret + - name: ssh-key-secret + secret: + secretName: ssh-key-secret + items: + - key: id_rsa + path: "id_rsa" + defaultMode: 0400 restartPolicy: Never diff --git a/config/peerpods/podvm/osc-podvm-delete-job.yaml b/config/peerpods/podvm/osc-podvm-delete-job.yaml index c4e079d4..f71a3513 100644 --- a/config/peerpods/podvm/osc-podvm-delete-job.yaml +++ b/config/peerpods/podvm/osc-podvm-delete-job.yaml @@ -27,6 +27,8 @@ spec: value: "" # Set this to the aws ami id to delete - name: IMAGE_ID value: "" # Set this to the azure image id to delete + - name: IMAGE_ID + value: "" # Set this to the libvirt image id to delete envFrom: - secretRef: name: peer-pods-secret @@ -39,6 +41,22 @@ spec: - configMapRef: name: aws-podvm-image-cm optional: true + - configMapRef: + name: libvirt-podvm-image-cm + optional: true command: ["/podvm-builder.sh", "delete", "-f"] + volumeMounts: + - name: ssh-key-secret + mountPath: "/root/.ssh/" + readOnly: true + optional: true + volumes: + - name: ssh-key-secret + secret: + secretName: ssh-key-secret + items: + - key: id_rsa + path: "id_rsa" + defaultMode: 0400 restartPolicy: Never diff --git a/config/peerpods/podvm/podvm-builder.sh b/config/peerpods/podvm/podvm-builder.sh index 7cd2ced7..d9195ed5 100755 --- a/config/peerpods/podvm/podvm-builder.sh +++ b/config/peerpods/podvm/podvm-builder.sh @@ -17,6 +17,13 @@ function install_aws_deps() { /scripts/aws-podvm-image-handler.sh -- install_binaries } +# Function to install libvirt deps +function install_libvirt_deps() { + echo "Installing libvirt deps" + # Install the required packages + /scripts/libvirt-podvm-image-handler.sh -- install_binaries +} + # Function to check if peer-pods-cm configmap exists function check_peer_pods_cm_exists() { if kubectl get configmap peer-pods-cm -n openshift-sandboxed-containers-operator >/dev/null 2>&1; then @@ -76,8 +83,19 @@ function create_podvm_image() { kubectl patch configmap peer-pods-cm -n openshift-sandboxed-containers-operator --type merge -p "{\"data\":{\"PODVM_AMI_ID\":\"${AMI_ID}\"}}" fi ;; + libvirt) + echo "Creating Libvirt qcow2" + /scripts/libvirt-podvm-image-handler.sh -c + if [ "${UPDATE_PEERPODS_CM}" == "yes" ]; then + # Check if peer-pods-cm configmap exists + if ! check_peer_pods_cm_exists; then + echo "peer-pods-cm configmap does not exist. Skipping the update of peer-pods-cm" + exit 1 + fi + fi + ;; *) - echo "CLOUD_PROVIDER is not set to azure or aws" + echo "CLOUD_PROVIDER is not set to azure or aws or libvirt" exit 1 ;; esac @@ -181,9 +199,42 @@ function delete_podvm_image() { kubectl patch configmap peer-pods-cm -n openshift-sandboxed-containers-operator --type merge -p "{\"data\":{\"PODVM_AMI_ID\":\"\"}}" fi + ;; + libvirt) + # If IMAGE_ID is not set, then exit + if [ -z "${IMAGE_ID}" ]; then + echo "IMAGE_ID is not set. Skipping the deletion of libvirt image" + exit 1 + fi + + LIBVIRT_IMAGE_ID=$(kubectl get configmap peer-pods-cm -n openshift-sandboxed-containers-operator -o jsonpath='{.data.LIBVIRT_IMAGE_ID}') + + # If LIBVIRT_IMAGE_ID is not set, then exit + if [ -z "${LIBVIRT_IMAGE_ID}" ]; then + echo "LIBVIRT_IMAGE_ID is not set in peer-pods-cm. Skipping the deletion of Libvirt image" + exit 1 + fi + + # check if the LIBVIRT_IMAGE_ID value in peer-pods-cm is same as the input IMAGE_ID + # If yes, then don't delete the image unless force option is provided + if [ "${LIBVIRT_IMAGE_ID}" == "${IMAGE_ID}" ]; then + if [ "$1" != "-f" ]; then + echo "LIBVIRT_IMAGE_ID in peer-pods-cm is same as the input image to be deleted. Skipping the deletion of Libvirt image" + exit 0 + fi + fi + + echo "Deleting Libvirt image id" + /scripts/libvirt-podvm-image-handler.sh -C + + # Update the peer-pods-cm configmap and remove the LIBVIRT_IMAGE_ID value + if [ "${UPDATE_PEERPODS_CM}" == "yes" ]; then + kubectl patch configmap peer-pods-cm -n openshift-sandboxed-containers-operator --type merge -p "{\"data\":{\"LIBVIRT_IMAGE_ID\":\"\"}}" + fi + ;; *) - echo "CLOUD_PROVIDER is not set to azure or aws" + echo "CLOUD_PROVIDER is not set to azure or aws or libvirt" exit 1 ;; esac @@ -227,7 +278,7 @@ function display_usage() { echo "Usage: $0 {create|delete [-f] [-g]|delete-gallery [-f]}" } -# Check if CLOUD_PROVIDER is set to azure or aws +# Check if CLOUD_PROVIDER is set to azure or aws or libvirt # Install the required dependencies case "${CLOUD_PROVIDER}" in azure) @@ -236,8 +287,11 @@ azure) aws) install_aws_deps ;; +libvirt) + install_libvirt_deps + ;; *) - echo "CLOUD_PROVIDER is not set to azure or aws" + echo "CLOUD_PROVIDER is not set to azure or aws or libvirt" display_usage exit 1 ;; diff --git a/controllers/image_generator.go b/controllers/image_generator.go index b4673be8..7f607f15 100644 --- a/controllers/image_generator.go +++ b/controllers/image_generator.go @@ -51,10 +51,12 @@ const ( peerpodsCMName = "peer-pods-cm" peerpodsCMAWSImageKey = "PODVM_AMI_ID" peerpodsCMAzureImageKey = "AZURE_IMAGE_ID" + peerpodsLibvirtImageKey = "LIBVIRT_IMAGE_ID" fipsCMKey = "BOOT_FIPS" procFIPS = "/proc/sys/crypto/fips_enabled" AWSProvider = "aws" AzureProvider = "azure" + LibvirtProvider = "libvirt" peerpodsImageJobsPathLocation = "/config/peerpods/podvm" azureImageGalleryPrefix = "PodVMGallery" ) @@ -237,6 +239,10 @@ func newImageGenerator(client client.Client) (*ImageGenerator, error) { case AzureProvider: ig.CMimageIDKey = peerpodsCMAzureImageKey ig.provider = provider + case LibvirtProvider: + igLogger.Info("libvirt is our provider", "provider", provider) + ig.CMimageIDKey = peerpodsLibvirtImageKey + ig.provider = provider default: igLogger.Info("unsupported cloud provider, image creation/deletion will be disabled", "provider", ig.provider) ig.provider = unsupportedCloudProvider @@ -295,6 +301,11 @@ func (r *ImageGenerator) createJobFromFile(jobFileName string) (*batchv1.Job, er Name: "AMI_ID", Value: imageId, }) + } else if r.provider == LibvirtProvider { + job.Spec.Template.Spec.Containers[0].Env = append(job.Spec.Template.Spec.Containers[0].Env, corev1.EnvVar{ + Name: "IMAGE_ID", + Value: imageId, + }) } } @@ -382,6 +393,7 @@ func (r *ImageGenerator) getPeerPodsCM() (*corev1.ConfigMap, error) { // cloud provider specific podvm image config is present // azure-podvm-image-cm.yaml for Azure // aws-podvm-image-cm.yaml for AWS +// libvirt-podvm-image-cm.yaml for Libvirt func (r *ImageGenerator) imageCreateJobRunner() (int, error) { igLogger.Info("imageCreateJobRunner: Start") @@ -462,6 +474,7 @@ func (r *ImageGenerator) imageCreateJobRunner() (int, error) { // cloud provider specific podvm image config is present // azure-podvm-image-cm.yaml for Azure // aws-podvm-image-cm.yaml for AWS +// libvirt-podvm-image-cm.yaml for Libvirt // Successful deletion removes the image ID from peer-pods-cm func (r *ImageGenerator) imageDeleteJobRunner() (int, error) { @@ -575,6 +588,10 @@ func (r *ImageGenerator) validatePeerPodsConfigs() error { azureSecretKeys := []string{"AZURE_TENANT_ID", "AZURE_CLIENT_ID", "AZURE_CLIENT_SECRET", "AZURE_SUBSCRIPTION_ID"} // azure ConfigMap Keys azureConfigMapKeys := []string{"AZURE_RESOURCE_GROUP", "AZURE_REGION", "CLOUD_PROVIDER"} + // libvirt Secret Keys + libvirtSecretKeys := []string{"CLOUD_PROVIDER", "LIBVIRT_URI", "LIBVIRT_POOL", "LIBVIRT_VOL_NAME"} + // libvirt ConfigMap Keys + libvirtConfigMapKeys := []string{"CLOUD_PROVIDER", "PROXY_TIMEOUT"} // Check for each cloud provider if respective ConfigMap keys are present in the peerPodsConfigMap switch r.provider { @@ -600,6 +617,18 @@ func (r *ImageGenerator) validatePeerPodsConfigs() error { return fmt.Errorf("validatePeerPodsConfigs: cannot find the required keys in peer-pods-cm ConfigMap") } + case "libvirt": + igLogger.Info("checking for libvirt keys and validating") + // Check if libvirt Secret Keys are present in the peerPodsSecret + if !checkKeysPresentAndNotEmpty(peerPodsSecret.Data, libvirtSecretKeys) { + return fmt.Errorf("validatePeerPodsConfigs: cannot find the required keys in peer-pods-secret Secret") + } + + // Check if libvirt ConfigMap Keys are present in the peerPodsConfigMap + if !checkKeysPresentAndNotEmpty(peerPodsCM.Data, libvirtConfigMapKeys) { + return fmt.Errorf("validatePeerPodsConfigs: cannot find the required keys in peer-pods-cm ConfigMap") + } + default: return fmt.Errorf("validatePeerPodsConfigs: unsupported cloud provider %s", r.provider) } @@ -650,11 +679,12 @@ func (r *ImageGenerator) getImageConfigMapName() string { // Function to create ConfigMap from a YAML file based on cloud provider // azure-podvm-image-cm.yaml for Azure // aws-podvm-image-cm.yaml for AWS +// libvirt-podvm-image-cm.yaml for Libvirt // Returns error if the ConfigMap creation fails func (r *ImageGenerator) createImageConfigMapFromFile() error { - // file format: [azure|aws]-podvm-image-cm.yaml - // ConfigMap name: [azure|aws]-podvm-image-cm + // file format: [azure|aws|libvirt]-podvm-image-cm.yaml + // ConfigMap name: [azure|aws|libvirt]-podvm-image-cm // Check if the ConfigMap already exists // If it exists, return nil @@ -702,6 +732,7 @@ func (r *ImageGenerator) createImageConfigMapFromFile() error { // Method to update image configmap with FIPS or other required values // azure-podvm-image-cm.yaml for Azure // aws-podvm-image-cm.yaml for AWS +// libvirt-podvm-image-cm.yaml for Libvirt // Returns error if the update fails func (r *ImageGenerator) updateImageConfigMap() error { diff --git a/controllers/utils.go b/controllers/utils.go index 61742c99..da2485cc 100644 --- a/controllers/utils.go +++ b/controllers/utils.go @@ -171,7 +171,10 @@ func getCloudProviderFromInfra(c client.Client) (string, error) { } if infrastructure.Status.PlatformStatus == nil { - return "", fmt.Errorf("Infrastructure.status.platformStatus is empty") + return "", fmt.Errorf("Infrastructure.status.platformStatus is empty and is not libvirt") + } else if string(infrastructure.Status.PlatformStatus.Type) == "None" { + // For libvirt provider, PlatformStatus.Type is empty string, so returning libvirt provider + return LibvirtProvider, nil } return strings.ToLower(string(infrastructure.Status.PlatformStatus.Type)), nil