Skip to content

Commit

Permalink
podvm:aws: add support to precreated artifacts
Browse files Browse the repository at this point in the history
updated README with the instructions
  • Loading branch information
snir911 committed Jan 13, 2025
1 parent b40ca50 commit 13ab92c
Show file tree
Hide file tree
Showing 6 changed files with 429 additions and 30 deletions.
19 changes: 19 additions & 0 deletions config/peerpods/podvm/aws-podvm-image-cm.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -36,3 +36,22 @@ data:

# Custom Agent Policy
#AGENT_POLICY: "" # set to base64 encoded agent policy

# precreated artifacts
#BUCKET_NAME: existing-bucket-name
#PODVM_IMAGE_URI: bootc::image-registry.openshift-image-registry.svc:5000/openshift-sandboxed-containers-operator/podvm-bootc
# Custom bootc build configuration: https://osbuild.org/docs/bootc/#-build-config
#BOOTC_BUILD_CONFIG: |
# [[customizations.user]]
# name = "peerpod"
# password = "peerpod"
# key = "ssh-rsa AAAA..."
# groups = ["wheel", "root"]
#
# [[customizations.filesystem]]
# mountpoint = "/"
# minsize = "5 GiB"
#
# [[customizations.filesystem]]
# mountpoint = "/var/kata-containers"
# minsize = "15 GiB"
172 changes: 160 additions & 12 deletions config/peerpods/podvm/aws-podvm-image-handler.sh
Original file line number Diff line number Diff line change
Expand Up @@ -115,15 +115,6 @@ function create_ami_using_packer() {
# If any error occurs, exit the script with an error message
# The variables are set before calling the function

# Set the AMI version
# It should follow the Major(int).Minor(int).Patch(int)
AMI_VERSION="${AMI_VERSION_MAJ_MIN}.$(date +'%Y%m%d%S')"
export AMI_VERSION

# Set the image name
AMI_NAME="${AMI_BASE_NAME}-${AMI_VERSION}"
export AMI_NAME

# If PODVM_DISTRO is not set to rhel then exit
[[ "${PODVM_DISTRO}" != "rhel" ]] && error_exit "unsupport distro"

Expand All @@ -150,6 +141,17 @@ function create_ami_using_packer() {

}

function set_ami_name() {
# Set the AMI version
# It should follow the Major(int).Minor(int).Patch(int)
AMI_VERSION="${AMI_VERSION_MAJ_MIN}.$(date +'%Y%m%d%S')"
export AMI_VERSION

# Set the image name
AMI_NAME="${AMI_BASE_NAME}-${AMI_VERSION}"
export AMI_NAME
}

# Function to get the ami id of the newly created image

function get_ami_id() {
Expand Down Expand Up @@ -189,6 +191,29 @@ function get_all_ami_ids() {

}

# Function to convert bootc container image to cloud image
# Input:
# 1. container_image_repo_url: The registry URL of the source container image.
# 2. image_tag: The tag of the source container image.
# 3. auth_json_file (can be empty): Path to the registry secret file to use for downloading the image.
# 4. aws_ami_name: Name for the AMI in AWS
# 5. aws_bucket: Target S3 bucket name for intermediate storage when creating AMI (bucket must exist)
# Output: ami

function bootc_to_ami() {
container_image_repo_url="${1}"
image_tag="${2}"
auth_json_file="${3}"
aws_ami_name="${4}"
aws_bucket="${5}" # bucket must exist

[[ -n "$AWS_ACCESS_KEY_ID" ]] && [[ -n "$AWS_SECRET_ACCESS_KEY" ]] && [[ -n "$AWS_REGION" ]] || error_exit "bootc_to_ami failed: AWS_* keys or AWS_REGION are missing"
# TODO: check permissions
run_args="--env AWS_ACCESS_KEY_ID --env AWS_SECRET_ACCESS_KEY"
bib_args="--type ami --aws-ami-name ${aws_ami_name} --aws-bucket ${aws_bucket} --aws-region ${AWS_REGION}"
bootc_image_builder_conversion "${container_image_repo_url}" "${image_tag}" "${auth_json_file}" "${run_args}" "${bib_args}"
}

# Function to create or update podvm-images configmap with all the amis
# Input AMI_ID_LIST is a list of ami ids

Expand Down Expand Up @@ -287,10 +312,106 @@ function delete_ami_id_annotation_from_peer_pods_cm() {
echo "Ami id annotation deleted from peer-pods-cm configmap successfully"
}

# Function to create the ami in AWS
function create_ami_from_prebuilt_artifact() {
echo "Creating AWS AMI image from prebuilt artifact"

function create_ami() {
echo "Creating AWS AMI"
echo "Pulling the podvm image from the provided path"
image_src="/tmp/image"
extraction_destination_path="/image"
image_repo_auth_file="/tmp/regauth/auth.json"

[[ ! ${BUCKET_NAME} ]] && error_exit "BUCKET_NAME is not defined"

# Get the PODVM_IMAGE_TYPE, PODVM_IMAGE_TAG and PODVM_IMAGE_SRC_PATH
get_image_type_url_and_path

case "${PODVM_IMAGE_TYPE}" in
oci) # TODO: test
echo "Extracting the raw image from the given path."

mkdir -p "${extraction_destination_path}" ||
error_exit "Failed to create the image directory"

extract_container_image "${PODVM_IMAGE_URL}" \
"${PODVM_IMAGE_TAG}" \
"${image_src}" \
"${extraction_destination_path}" \
"${image_repo_auth_file}"

# Form the path of the podvm vhd image.
podvm_image_path="${extraction_destination_path}/rootfs/${PODVM_IMAGE_SRC_PATH}"

upload_image_to_s3

register_ami
;;
bootc)
echo "Converting the bootc image to AMI"

bootc_to_ami "${PODVM_IMAGE_URL}" "${PODVM_IMAGE_TAG}" "${image_repo_auth_file}" "${AMI_NAME}" "${BUCKET_NAME}"
;;
*)
error_exit "Currently only OCI image unpacking is supported, exiting."
;;
esac
}

function upload_image_to_s3() {
echo "Uploading the image to S3"

# Check if the image exists
[[ ! -f "${podvm_image_path}" ]] && error_exit "Image does not exist"

# Upload the image to S3
aws s3 cp "${podvm_image_path}" "s3://${BUCKET_NAME}/${AMI_NAME}" ||
error_exit "Failed to upload the image to S3"

echo "Image uploaded to S3 successfully"
}

function register_ami() {
echo "Starting image import..."

get_podvm_image_format podvm_image_path
local import_task_id=$(aws ec2 import-image \
--description "podvm-image created from qcow2/raw" \
--disk-containers "file://<(echo '{\"Description\":\"podvm-image created from qcow2/raw\",\"Format\":\"${PODVM_IMAGE_FORMAT}\",\"UserBucket\":{\"S3Bucket\":\"${BUCKET_NAME}\",\"S3Key\":\"${AMI_NAME}\"}}')" \
--query 'ImportTaskId' \
--output text)

echo "Import task started. Task ID: $IMPORT_TASK_ID"

echo "Monitoring the import task..."
while true; do
local status=$(aws ec2 describe-import-image-tasks \
--import-task-ids "$import_task_id" \
--query 'ImportImageTasks[0].Status' \
--output text)

echo "Current status: $status"

if [[ "$status" == "completed" ]]; then
local ami_id=$(aws ec2 describe-import-image-tasks \
--import-task-ids "$IMPORT_TASK_ID" \
--query 'ImportImageTasks[0].ImageId' \
--output text)
echo "Import completed. AMI ID: $ami_id"
break
elif [[ "$status" == "deleted" || "$status" == "cancelled" ]]; then
echo "Import task failed or was cancelled."
exit 1
fi

sleep 15
done

# Tag the AMI
aws ec2 create-tags --resources "$ami_id" --tags Key="name",Value="${AMI_NAME}"
echo "Tags added successfully."
}

function create_ami_from_scratch() {
echo "Creating AWS AMI from scratch"

# Create the AWS image
# If any error occurs, exit the script with an error message
Expand Down Expand Up @@ -318,6 +439,33 @@ function create_ami() {

# Create AWS ami using packer
create_ami_using_packer
}

# Function to create the ami in AWS

function create_ami() {
echo "Creating AWS AMI"

# Create the AWS image
# If any error occurs, exit the script with an error message

# Install packages if INSTALL_PACKAGES is set to yes
if [[ "${INSTALL_PACKAGES}" == "yes" ]]; then
# Install required rpm packages
install_rpm_packages

# Install required binary packages
install_binary_packages
fi

# generate & set the ami name
set_ami_name

if [[ "${IMAGE_TYPE}" == "operator-built" ]]; then
create_ami_from_scratch
elif [[ "${IMAGE_TYPE}" == "pre-built" ]]; then
create_ami_from_prebuilt_artifact
fi

# Get the ami id of the newly created image
# This will set the AMI_ID environment variable
Expand Down
15 changes: 14 additions & 1 deletion config/peerpods/podvm/bootc/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -52,12 +52,13 @@ podman run \
registry.redhat.io/rhel9/bootc-image-builder:latest \
--type qcow2 \
--rootfs xfs \
--local \
--local \ # if pulled locally
"${IMG}"
```
Artifact will be located at output/qcow2/disk.qcow2
Upload it to your cloud-provider.

**AWS:** See [Bootc Image Builder Instructions for AMI artifact](https://github.com/osbuild/bootc-image-builder?tab=readme-ov-file#amazon-machine-images-amis)

### **In-Cluster** Podvm Disk & Image Creation

Expand Down Expand Up @@ -87,3 +88,15 @@ BOOTC_BUILD_CONFIG: | # Custom bootc build configuration: https://osbuild.org/d
minsize = "15 GiB"
```

#### AWS specifics

In order to convert image to AMI (Amazon Machine Image) in-cluster you'll need:
* An existing s3 bucket in the region of your cluster
* Your cluster's AWS credntials needs to have the following [permissions](https://docs.aws.amazon.com/vm-import/latest/userguide/required-permissions.html#iam-permissions-image)
* [vmimport service role](https://docs.aws.amazon.com/vm-import/latest/userguide/required-permissions.html#vmimport-role) set
* The created bucket name needs to be specified in the aws-podvm-image-cm as follows:
```
BUCKET_NAME=<existing-bucket-name>
```

**NOTE:** you may use the [ami-helper.sh](../../../../hack/ami-helper.sh) script to help and set the above requirements
57 changes: 40 additions & 17 deletions config/peerpods/podvm/lib.sh
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,9 @@

[[ "$DEBUG" == "true" ]] && set -x

# Bootc Defaults
BIB_IMAGE=${BIB_IMAGE:-registry.redhat.io/rhel9/bootc-image-builder:9.5}

# Defaults for pause image
# This pause image is multi-arch
PAUSE_IMAGE_REPO_DEFAULT="quay.io/openshift-release-dev/ocp-v4.0-art-dev@sha256"
Expand Down Expand Up @@ -458,53 +461,73 @@ function validate_podvm_image() {
echo "Checksum of the PodVM image: $(sha256sum "$image_path")"
}
# Function to download and extract a container image.
# Accepts six arguments:
# Function to convert qcow2 image to vhd image
# Function to convert local bootc container image to cloud image
# Input:
# 1. container_image_repo_url: The registry URL of the source container image.
# 2. image_tag: The tag of the source container image.
# 3. auth_json_file (optional): Path to the registry secret file to use for downloading the image.
# Output: qcow2 image at output/qcow2/disk.qcow2
function bootc_to_qcow2() {
# 3. auth_json_file (can be empty): Path to the registry secret file to use for downloading the image.
# 4. run_args: arguments for the podman run command that runs bootc image builder
# 5. bib_args: arguments for bootc image builder itself
# Output: based on input, either, qcow2 image at output/qcow2/disk.qcow2 or ami
# see: https://github.com/osbuild/bootc-image-builder
function bootc_image_builder_conversion() {
container_image_repo_url="${1}"
image_tag="${2}"
auth_json_file="${3}"
run_args="${4}"
bib_args="${5}"
# some VM customizations , TODO: allow custom config
# some VM customizations
echo "${BOOTC_BUILD_CONFIG}" >> ./config.toml
echo "config.toml:"
cat ./config.toml
# login for local registry pulling # TODO: can we use token instead?
if [[ "${PODVM_IMAGE_URL}" == *"image-registry.openshift-image-registry.svc"* ]]; then
if [[ "${container_image_repo_url}" == *"image-registry.openshift-image-registry.svc"* ]]; then
echo "login to local registry"
mkdir /etc/containers/certs.d/image-registry.openshift-image-registry.svc:5000
ln -s /var/run/secrets/kubernetes.io/serviceaccount/service-ca.crt /etc/containers/certs.d/image-registry.openshift-image-registry.svc:5000/service-ca.crt
podman login -u kubeadmin -p $(cat /var/run/secrets/kubernetes.io/serviceaccount/token) image-registry.openshift-image-registry.svc:5000
podman pull "${container_image_repo_url}:${image_tag}" || error_exit "Failed to pull local bootc image"
else
# pull first to authenticate properly, if REGISTRY_AUTH_FILE is empty, it's ignored
REGISTRY_AUTH_FILE=${auth_json_file} podman pull "${container_image_repo_url}:${image_tag}" || error_exit "Failed to pull bootc image"
fi
# pull first to authenticate properly, if REGISTRY_AUTH_FILE is empty, it's ignored
REGISTRY_AUTH_FILE=${auth_json_file} podman pull "${container_image_repo_url}:${image_tag}" || error_exit "Failed to pull bootc image"
# execute bootc-image-builder
# TODO: check if we can avoid this to drop the /store volumeMount
# TODO: use fixed version
# REGISTRY_AUTH_FILE is needed to access bib image
mkdir output
REGISTRY_AUTH_FILE=${CLUSTER_PULL_SECRET_AUTH_FILE} podman run \
-it \
--privileged \
--security-opt label=type:unconfined_t \
-v $(pwd)/config.toml:/config.toml:ro \
-v $(pwd)/output:/output \
-v /store:/store \
-v /var/lib/containers/storage:/var/lib/containers/storage \
registry.redhat.io/rhel9/bootc-image-builder:latest \
--type qcow2 \
${run_args} \
${BIB_IMAGE} \
${bib_args} \
--rootfs xfs \
--local \
"${container_image_repo_url}:${image_tag}" || error_exit "Failed to convert bootc image"
${container_image_repo_url}:${image_tag} || error_exit "Failed to convert bootc image"
}
# Function to convert bootc container image to qcow2
# Input:
# 1. container_image_repo_url: The registry URL of the source container image.
# 2. image_tag: The tag of the source container image.
# 3. auth_json_file (can be empty): Path to the registry secret file to use for downloading the image.
# Output: qcow2 image at output/qcow2/disk.qcow2
function bootc_to_qcow2() {
container_image_repo_url="${1}"
image_tag="${2}"
auth_json_file="${3}"
mkdir output
run_args="-v $(pwd)/output:/output"
bib_args="--type qcow2"
bootc_image_builder_conversion "${container_image_repo_url}" "${image_tag}" "${auth_json_file}" "${run_args}" "${bib_args}"
}
# Function to convert qcow2 image to vhd image
Expand Down
3 changes: 3 additions & 0 deletions config/peerpods/podvm/osc-podvm-create-job.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,9 @@ spec:
envFrom:
- secretRef:
name: peer-pods-secret
- secretRef:
name: peer-pods-image-creation-secret # must come after peer-pods-secret to override the values
optional: true
- configMapRef:
name: peer-pods-cm
optional: true
Expand Down
Loading

0 comments on commit 13ab92c

Please sign in to comment.