Skip to content

Commit dfd0073

Browse files
authored
Merge pull request #431 from ajayvic/libvirt-imagepull-support
KATA-2987: Enable OCI image pull support for libvirt provider
2 parents 2156263 + 690721a commit dfd0073

7 files changed

+223
-38
lines changed
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
FROM scratch
2+
3+
ARG PODVM_IMAGE_SRC
4+
ENV PODVM_IMAGE_PATH="/image/podvm.qcow2"
5+
6+
COPY $PODVM_IMAGE_SRC $PODVM_IMAGE_PATH

config/peerpods/podvm/README.md

+32
Original file line numberDiff line numberDiff line change
@@ -34,3 +34,35 @@ Now when you create a KataConfig with `enablePeerPods: true` with empty
3434
`AZURE_IMAGE_ID` or `AWS_AMI_ID` in `peer-pods-cm`, then depending on the cloud
3535
provider configured, the operator will create the pod VM image based on the
3636
provided config.
37+
38+
## PodVM Image Upload Configuration
39+
40+
The PodVM image can be embedded into a container image. This container image can then be unwrapped and uploaded to the libvirt volume specified in the `peer-pods-secret`. Please note that this feature is currently supported only for the libvirt provider.
41+
42+
To create an OCI image with the PodVM image, you can use the `Dockerfile.podvm-oci` as follows:
43+
44+
```bash
45+
docker build -t podvm-libvirt \
46+
--build-arg PODVM_IMAGE_SRC=<podvm_image_source> \
47+
-f Dockerfile.podvm-oci .
48+
```
49+
50+
In this context, `PODVM_IMAGE_SRC` refers to the location of the `qcow2` image on the host. Optionally, you can also set `PODVM_IMAGE_PATH`, which is the path of the qcow2 image inside the container. This path will be used as `<image_path>` in the `PODVM_IMAGE_URI` as described below.
51+
52+
`oci` is the only supported `image_repo_type` at present.
53+
54+
Ensure that `PODVM_IMAGE_URI` is configured in the `libvirt-podvm-image-cm` in the following format:
55+
56+
```bash
57+
PODVM_IMAGE_URI: "<image_repo_type>::<image_repo_url>:<image_tag>::<image_path>"
58+
```
59+
60+
For example:
61+
62+
```bash
63+
PODVM_IMAGE_URI: "oci::quay.io/openshift_sandboxed_containers/libvirt-podvm-image:latest::/image/podvm-390x.qcow2"
64+
```
65+
66+
In this example, `<image_tag>` and `<image_path>` are optional. If not provided, the default values will be `<image_tag>`: `latest` and `<image_path>`: `/image/podvm.qcow2`.
67+
68+
**Note:** When pulling container images from authenticated registries, make sure that the OpenShift `pull-secrets` are updated with the necessary registry credentials.

config/peerpods/podvm/lib.sh

+66-9
Original file line numberDiff line numberDiff line change
@@ -240,11 +240,11 @@ function prepare_source_code() {
240240

241241
}
242242

243-
# Download and extract pause container image
243+
# Download and extract the pause container image
244244
# Accepts three arguments:
245245
# 1. pause_image_repo_url: The registry URL of the OCP pause image.
246246
# 2. pause_image_tag: The tag of the OCP pause image.
247-
# 2. auth_json_file (optional): Path to the registry secret file to use for downloading the image
247+
# 3. auth_json_file (optional): Path to the registry secret file to use for downloading the image
248248
function download_and_extract_pause_image() {
249249

250250
# Set default values for the OCP pause image
@@ -266,20 +266,43 @@ function download_and_extract_pause_image() {
266266
mkdir -p "${pause_bundle}" ||
267267
error_exit "Failed to create the pause_bundle directory"
268268

269+
extract_container_image "${pause_image_repo_url}" "${pause_image_tag}" "${pause_src}" "${pause_bundle}" "${auth_json_file}"
270+
}
271+
272+
# Function to download and extract a container image.
273+
# Accepts six arguments:
274+
# 1. container_image_repo_url: The registry URL of the source container image.
275+
# 2. image_tag: The tag of the source container image.
276+
# 3. dest_image: The destination image name.
277+
# 4. destination_path: The destination path where the image is to be extracted.
278+
# 5. auth_json_file (optional): Path to the registry secret file to use for downloading the image.
279+
function extract_container_image() {
280+
281+
# Set the values required for the container image extraction.
282+
container_image_repo_url="${1}"
283+
image_tag="${2}"
284+
dest_image="${3}"
285+
destination_path="${4}"
286+
auth_json_file="${5}"
287+
288+
# If arguments are not provided, exit the script with an error message
289+
[[ $# -lt 4 ]] &&
290+
error_exit "Usage: extract_container_image <container_image_repo_url> <image_tag> <dest_image> <destination_path> [registry_secret]"
291+
269292
# Form the skopeo CLI. Add authfile if provided
270-
if [[ -n "${3}" ]]; then
293+
if [[ -n "${5}" ]]; then
271294
SKOPEO_CLI="skopeo copy --authfile ${auth_json_file}"
272295
else
273296
SKOPEO_CLI="skopeo copy"
274297
fi
275298

276-
# Download the pause image
277-
$SKOPEO_CLI "docker://${pause_image_repo_url}:${pause_image_tag}" "oci:${pause_src}:${pause_image_tag}" ||
278-
error_exit "Failed to download the pause image"
299+
# Download the container image
300+
$SKOPEO_CLI "docker://${container_image_repo_url}:${image_tag}" "oci:${dest_image}:${image_tag}" ||
301+
error_exit "Failed to download the container image"
279302

280-
# Extract the pause image using umoci into pause_bundle directory
281-
umoci unpack --rootless --image "${pause_src}:${pause_image_tag}" "${pause_bundle}" ||
282-
error_exit "Failed to extract the pause image"
303+
# Extract the container image using umoci into provided directory
304+
umoci unpack --rootless --image "${dest_image}:${image_tag}" "${destination_path}" ||
305+
error_exit "Failed to extract the container image"
283306

284307
}
285308

@@ -313,6 +336,40 @@ EOF
313336

314337
}
315338

339+
# Function to split image type, url and path from PODVM_IMAGE_URI for pre-built image scenario.
340+
function get_image_type_url_and_path() {
341+
342+
# Use pattern matching to split on '::' and then on ':', and capture output
343+
if [[ $PODVM_IMAGE_URI =~ ^([^:]+)::([^:]+)(:([^:]+))?(::(.+))?$ ]]; then
344+
PODVM_IMAGE_TYPE="${BASH_REMATCH[1]}"
345+
PODVM_IMAGE_URL="${BASH_REMATCH[2]}"
346+
PODVM_IMAGE_TAG="${BASH_REMATCH[4]}" # This will be empty if not present
347+
PODVM_IMAGE_SRC_PATH="${BASH_REMATCH[6]}" # This will be empty if not present
348+
fi
349+
350+
if [[ -z "${PODVM_IMAGE_TAG}" ]]; then
351+
PODVM_IMAGE_TAG="latest"
352+
fi
353+
354+
if [[ -z "${PODVM_IMAGE_SRC_PATH}" ]]; then
355+
PODVM_IMAGE_SRC_PATH="/image/podvm.qcow2"
356+
fi
357+
358+
export PODVM_IMAGE_TYPE PODVM_IMAGE_URL PODVM_IMAGE_TAG PODVM_IMAGE_SRC_PATH
359+
}
360+
361+
# Function to validate the podvm image type.
362+
function validate_podvm_image() {
363+
PODVM_IMAGE_PATH="${1}"
364+
365+
# Currently only qcow2 based PodVM images are supported for image upload.
366+
if [[ "$(file -b $PODVM_IMAGE_PATH)" != *QCOW2* ]]; then
367+
error_exit "PodVM image is not a valid qcow2, exiting."
368+
fi
369+
370+
echo "Checksum of the PodVM image: $(sha256sum $PODVM_IMAGE_PATH)"
371+
}
372+
316373
# Global variables
317374

318375
# Set global variable for the source code directory

config/peerpods/podvm/libvirt-podvm-image-cm.yaml

+3-1
Original file line numberDiff line numberDiff line change
@@ -25,4 +25,6 @@ data:
2525

2626
# To Enable SE for IBM Z
2727
SE_BOOT: "true"
28-
28+
29+
# For Pre-built PodVM images.
30+
PODVM_IMAGE_URI: "" # eg: oci::quay.io/openshift_sandboxed_containers/libvirt-podvm-image:latest::/image/podvm.qcow2

config/peerpods/podvm/libvirt-podvm-image-handler.sh

+90-28
Original file line numberDiff line numberDiff line change
@@ -21,23 +21,72 @@ function verify_vars() {
2121

2222
[[ -z "${ORG_ID}" ]] && error_exit "ORG_ID is not set"
2323
[[ -z "${ACTIVATION_KEY}" ]] && error_exit "ACTIVATION_KEY is not set"
24-
[[ -z "${BASE_OS_VERSION}" ]] && error_exit "BASE_OS_VERSION is not set"
2524

26-
[[ -z "${PODVM_DISTRO}" ]] && error_exit "PODVM_DISTRO is not set"
25+
if [[ "${IMAGE_TYPE}" == "operator-built" ]]; then
26+
[[ -z "${BASE_OS_VERSION}" ]] && error_exit "BASE_OS_VERSION is not set"
2727

28-
[[ -z "${CAA_SRC}" ]] && error_exit "CAA_SRC is empty"
29-
[[ -z "${CAA_REF}" ]] && error_exit "CAA_REF is empty"
28+
[[ -z "${PODVM_DISTRO}" ]] && error_exit "PODVM_DISTRO is not set"
3029

31-
[[ -z "${REDHAT_OFFLINE_TOKEN}" ]] && error_exit "Redhat token is not set"
30+
[[ -z "${CAA_SRC}" ]] && error_exit "CAA_SRC is empty"
31+
[[ -z "${CAA_REF}" ]] && error_exit "CAA_REF is empty"
3232

33-
# Ensure booleans are set
34-
[[ -z "${DOWNLOAD_SOURCES}" ]] && error_exit "DOWNLOAD_SOURCES is empty"
33+
[[ -z "${REDHAT_OFFLINE_TOKEN}" ]] && error_exit "Redhat token is not set"
34+
35+
# Ensure booleans are set
36+
[[ -z "${DOWNLOAD_SOURCES}" ]] && error_exit "DOWNLOAD_SOURCES is empty"
37+
fi
3538
}
3639

40+
# Function to create the libvirt image based on the type.
41+
function create_libvirt_image() {
42+
# Based on the value of `IMAGE_TYPE` the image is either build from scratch or using the prebuilt artifact.
43+
if [[ "${IMAGE_TYPE}" == "operator-built" ]]; then
44+
create_libvirt_image_from_scratch
45+
elif [[ "${IMAGE_TYPE}" == "pre-built" ]]; then
46+
create_libvirt_image_from_prebuilt_artifact
47+
fi
3748

49+
}
3850

39-
function create_libvirt_image() {
40-
echo "Creating libvirt qcow2 image"
51+
# Function to create the libvirt image from a prebuilt artifact.
52+
function create_libvirt_image_from_prebuilt_artifact() {
53+
echo "Pulling the podvm image from the provided path"
54+
IMAGE_SRC="/tmp/image"
55+
EXTRACTION_DESTINATION_PATH="/image"
56+
IMAGE_REPO_AUTH_FILE="/tmp/regauth/auth.json"
57+
58+
get_image_type_url_and_path
59+
60+
case "${PODVM_IMAGE_TYPE}" in
61+
oci)
62+
echo "Extracting the Libvirt qcow2 image from the given path."
63+
64+
mkdir -p "${EXTRACTION_DESTINATION_PATH}" ||
65+
error_exit "Failed to create the image directory"
66+
67+
extract_container_image "${PODVM_IMAGE_URL}" "${PODVM_IMAGE_TAG}" "${IMAGE_SRC}" "${EXTRACTION_DESTINATION_PATH}" "${IMAGE_REPO_AUTH_FILE}"
68+
69+
# Form the path of the podvm qcow2 image.
70+
PODVM_IMAGE_PATH="${EXTRACTION_DESTINATION_PATH}/rootfs${PODVM_IMAGE_SRC_PATH}"
71+
72+
# Check whether the podvm image is a valid qcow2 or not.
73+
validate_podvm_image "${PODVM_IMAGE_PATH}"
74+
75+
# Upload the created qcow2 to the volume
76+
upload_libvirt_image "${PODVM_IMAGE_PATH}"
77+
78+
# Add the libvirt_volume_name to peer-pods-cm configmap
79+
add_libvirt_vol_to_peer_pods_cm
80+
;;
81+
*)
82+
error_exit "Currently only OCI image unpacking is supported, exiting."
83+
;;
84+
esac
85+
}
86+
87+
# Function to create the libvirt image from scratch.
88+
function create_libvirt_image_from_scratch() {
89+
echo "Creating qcow2 image for libvirt provider from scratch"
4190

4291
# If any error occurs, exit the script with an error message
4392

@@ -59,11 +108,11 @@ function create_libvirt_image() {
59108
error_exit "Failed to change directory to "${CAA_SRC_DIR}"/podvm"
60109
LIBC=gnu make BINARIES= PAUSE_BUNDLE= image
61110

62-
export PODVM_IMAGE_PATH=/payload/podvm-libvirt.qcow2
111+
PODVM_IMAGE_PATH=/payload/podvm-libvirt.qcow2
63112
cp -pr "${CAA_SRC_DIR}"/podvm/output/*.qcow2 "${PODVM_IMAGE_PATH}"
64113

65114
# Upload the created qcow2 to the volume
66-
upload_libvirt_image
115+
upload_libvirt_image "${PODVM_IMAGE_PATH}"
67116

68117
# Add the libvirt_volume_name to peer-pods-cm configmap
69118
add_libvirt_vol_to_peer_pods_cm
@@ -112,6 +161,8 @@ function download_rhel_kvm_guest_qcow2() {
112161
# Function to upload the qcow2 image to volume
113162

114163
function upload_libvirt_image() {
164+
PODVM_IMAGE_PATH="${1}"
165+
115166
echo "LIBVIRT_VOL_NAME: "${LIBVIRT_VOL_NAME}"" && echo "LIBVIRT_POOL: "${LIBVIRT_POOL}"" && \
116167
echo "LIBVIRT_URI: "${LIBVIRT_URI}"" && echo "PODVM_IMAGE_PATH: "${PODVM_IMAGE_PATH}""
117168
echo "Starting to upload the image."
@@ -199,14 +250,18 @@ function install_packages(){
199250

200251
install_binary_packages
201252

202-
#Install other libvirt specific binaries packages
253+
# Install packages required for libvirt in addition to the binary packages.
203254
ARCH=$(uname -m)
204255
export ARCH
205256
if [[ -n "${ACTIVATION_KEY}" && -n "${ORG_ID}" ]]; then
206-
subscription-manager register --org="${ORG_ID}" --activationkey="${ACTIVATION_KEY}"
257+
subscription-manager register --org="${ORG_ID}" --activationkey="${ACTIVATION_KEY}" ||
258+
error_exit "Failed to subscribe"
207259
fi
208-
subscription-manager repos --enable codeready-builder-for-rhel-9-"${ARCH}"-rpms
209-
dnf install -y gcc genisoimage qemu-kvm libvirt-client
260+
261+
subscription-manager repos --enable codeready-builder-for-rhel-9-"${ARCH}"-rpms ||
262+
error_exit "Failed to enable codeready-builder"
263+
264+
dnf install -y libvirt-client gcc file
210265

211266
GO_VERSION="1.21.9"
212267
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 && \
@@ -216,28 +271,35 @@ function install_packages(){
216271
export GOPATH="/src"
217272

218273
if [ "${ARCH}" == "s390x" ]; then
219-
# Build packer from source for s390x as there are no prebuilt binaries for the required packer version
220-
PACKER_VERSION="v1.9.4"
221-
git clone --depth 1 --single-branch https://github.com/hashicorp/packer.git -b "${PACKER_VERSION}"
222-
cd packer
223-
sed -i -- "s/ALL_XC_ARCH=.*/ALL_XC_ARCH=\"${ARCH}\"/g" scripts/build.sh
224-
sed -i -- "s/ALL_XC_OS=.*/ALL_XC_OS=\"Linux\"/g" scripts/build.sh
225-
make bin && cp bin/packer /usr/local/bin/
226-
227274
# Build umoci from source for s390x as there are no prebuilt binaries
228275
mkdir -p umoci
229276
git clone https://github.com/opencontainers/umoci.git
230277
cd umoci
231278
make
232279
cp -pr umoci /usr/local/bin/
233280
fi
281+
282+
if [[ "${IMAGE_TYPE}" == "operator-built" ]]; then
283+
dnf install -y genisoimage qemu-kvm
284+
285+
if [ "${ARCH}" == "s390x" ]; then
286+
# Build packer from source for s390x as there are no prebuilt binaries for the required packer version
287+
PACKER_VERSION="v1.9.4"
288+
git clone --depth 1 --single-branch https://github.com/hashicorp/packer.git -b "${PACKER_VERSION}"
289+
cd packer
290+
sed -i -- "s/ALL_XC_ARCH=.*/ALL_XC_ARCH=\"${ARCH}\"/g" scripts/build.sh
291+
sed -i -- "s/ALL_XC_OS=.*/ALL_XC_OS=\"Linux\"/g" scripts/build.sh
292+
make bin && cp bin/packer /usr/local/bin/
293+
fi
234294

235-
# set a correspond qemu-system-* named link to qemu-kvm
236-
ln -s /usr/libexec/qemu-kvm /usr/bin/qemu-system-"${ARCH}"
295+
# set a correspond qemu-system-* named link to qemu-kvm
296+
ln -s /usr/libexec/qemu-kvm /usr/bin/qemu-system-"${ARCH}"
237297

238-
# Build cloud-utils package from source as prebuilt binary is not available
239-
git clone https://github.com/canonical/cloud-utils
240-
cd cloud-utils && make install
298+
# Build cloud-utils package from source as prebuilt binary is not available
299+
git clone https://github.com/canonical/cloud-utils
300+
cd cloud-utils && make install
301+
fi
302+
241303

242304
}
243305

config/peerpods/podvm/podvm-builder.sh

+20
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,23 @@ function check_peer_pods_cm_exists() {
3333
fi
3434
}
3535

36+
# Function to check if the image should be built or use the pre-built artifact.
37+
function set_podvm_image_type() {
38+
echo "Checking if PODVM_IMAGE_URI is set"
39+
40+
# If the value of the PODVM_IMAGE_URI is empty or not set, then build the image from scratch else use the prebuilt artifact.
41+
if [[ -z "${PODVM_IMAGE_URI}" ]]; then
42+
IMAGE_TYPE="operator-built"
43+
echo "Initiating the operator to build the podvm image"
44+
else
45+
IMAGE_TYPE="pre-built"
46+
echo "Initiating the operator to use the pre-built podvm image"
47+
fi
48+
49+
export IMAGE_TYPE
50+
51+
}
52+
3653
# Function to create podvm image
3754
function create_podvm_image() {
3855
case "${CLOUD_PROVIDER}" in
@@ -278,6 +295,9 @@ function display_usage() {
278295
echo "Usage: $0 {create|delete [-f] [-g]|delete-gallery [-f]}"
279296
}
280297

298+
# Set the PodVM image type based on the `PODVM_IMAGE_URI`
299+
set_podvm_image_type
300+
281301
# Check if CLOUD_PROVIDER is set to azure or aws or libvirt
282302
# Install the required dependencies
283303
case "${CLOUD_PROVIDER}" in

config/peerpods/podvm/podvm-handling.md

+6
Original file line numberDiff line numberDiff line change
@@ -133,3 +133,9 @@ delete the image gallery defined in the `IMAGE_GALLERY_NAME` key in
133133
configMap is updated with empty value (""). Also the annotations
134134
LATEST_AMI_ID (for AWS) or LATEST_IMAGE_ID and `IMAGE_GALLERY_NAME` (for
135135
Azure) are removed from the `peer-pods-cm` configMap
136+
137+
## PodVM Image Upload flow via OSC operator
138+
139+
* The code verifies all the required config parameters
140+
* Based on the `PODVM_IMAGE_URI` presence on the cloud provider specific configMap (eg: `libvirt-podvm-image-cm`), `IMAGE_TYPE` is set to either `operator-built` or `pre-built`.
141+
* Based on the `IMAGE_TYPE` it will invoke the create image from scratch for `operator-built` and pull an existing image if it's `pre-built`.

0 commit comments

Comments
 (0)