Skip to content

Commit

Permalink
Add bootc image builder case
Browse files Browse the repository at this point in the history
utilities and case scripts are provided,
and cover Image type:ami vmdk qcow2 anaconda-iso raw

container image reference: quay.io/centos-bootc/centos-bootc:stream9,
quay.io/centos-bootc/fedora-bootc:eln, localhost/bootc:eln

Signed-off-by: Chunfu Wen <[email protected]>
  • Loading branch information
chunfuwen committed Mar 27, 2024
1 parent 3a28269 commit 1e1f1b8
Show file tree
Hide file tree
Showing 3 changed files with 391 additions and 0 deletions.
237 changes: 237 additions & 0 deletions provider/bootc_image_builder/bootc_image_build_utils.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,237 @@
# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
#
# Copyright Red Hat
#
# SPDX-License-Identifier: GPL-2.0
#
# Author: [email protected]
#
# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

"""Helper functions for bootc image builder"""

import logging
import json
import os
import shutil
import textwrap
import pathlib

from avocado.utils import path, process
from virttest import utils_package

LOG = logging.getLogger('avocado.' + __name__)


def install_bib_packages():
"""
install necessary bootc image builder necessary packages
"""
package_list = ["podman", "skopeo", "virt-install", "curl", "virt-manager"]
for pkg in package_list:
try:
path.find_command(pkg)
except path.CmdNotFoundError:
utils_package.package_install(pkg)


def podman_command_build(bib_image_url, disk_image_type, image_ref, config=None, local_container=False, tls_verify="true", chownership=None, options=None, **dargs):
"""
Use podman run command to launch bootc image builder
:param bib_image_url: bootc image builder url
:param disk_image_type: image type to build [qcow2, ami] (default "qcow2")
:param image_ref: image reference
:param config: config file
:param local_container: whether use local container image
:param tls_verify: whether verify tls connection
:param local_container: whether use local container image
:param chownership: whether change output ownership
:param options: additional options if needed
:param dargs: standardized virsh function API keywords
:return: CmdResult object
"""
if not os.path.exists("/var/lib/libvirt/images/output"):
os.makedirs("/var/lib/libvirt/images/output")
cmd = "sudo podman run --rm -it --privileged --pull=newer --security-opt label=type:unconfined_t -v /var/lib/libvirt/images/output:/output"
if config:
cmd += " -v %s:/config.json " % config

if local_container:
cmd += " -v /var/lib/containers/storage:/var/lib/containers/storage "

cmd += " %s " \
" --type %s --tls-verify=%s " % (bib_image_url, disk_image_type, tls_verify)

if config:
cmd += " --config /config.json "

if local_container:
cmd += " --local %s " % image_ref
else:
cmd += " %s " % image_ref

if chownership:
cmd += " --chown %s " % chownership

if options is not None:
cmd += " %s" % options

debug = dargs.get("debug", True)

ignore_status = dargs.get("ignore_status", False)
timeout = int(dargs.get("timeout", "1800"))
LOG.debug("the whole podman command: %s\n" % cmd)

ret = process.run(
cmd, timeout=timeout, verbose=debug, ignore_status=ignore_status, shell=True)

ret.stdout = ret.stdout_text
ret.stderr = ret.stderr_text
return ret


def podman_login(podman_username, podman_password, registry):
"""
Use podman to login in registry
:param podman_username: podman username
:param podman_password: podman password
:param registry: registry to login
:return: CmdResult object
"""
command = "sudo podman login -u='%s' -p='%s' %s " % (podman_username, podman_password, registry)
process.run(
command, timeout=60, verbose=True, ignore_status=False, shell=True)


def podman_push(podman_username, podman_password, registry, container_url):
"""
Use podman image to registry
:param podman_username: podman username
:param podman_password: podman password
:param registry: registry to login
:param container_url: image url
:return: CmdResult object
"""
podman_login(podman_username, podman_password, registry)
command = "sudo podman push %s " % container_url
process.run(
command, timeout=1200, verbose=True, ignore_status=False, shell=True)


def create_config_json_file(folder, username, password):
"""
install necessary bootc image builder necessary packages
:param folder: the folder that config.json reside in
:param username: user name
:param password: user password
"""
public_key_path = os.path.join(os.path.expanduser("~/.ssh/"), "id_rsa.pub")
if not os.path.exists(public_key_path):
LOG.debug("public key doesn't exist, please create one")
key_gen_cmd = "ssh-keygen -q -t rsa -N '' <<< $'\ny' >/dev/null 2>&1"
process.run(key_gen_cmd, shell=True, ignore_status=False)

with open(public_key_path, 'r') as ssh:
key_value = ssh.read().rstrip()
cfg = {
"blueprint": {
"customizations": {
"user": [
{
"name": username,
"password": password,
"groups": ["wheel"],
"key": "%s" % key_value,
},
],
},
},
}
LOG.debug("what is cfg:%s", cfg)
config_json_path = pathlib.Path(folder) / "config.json"
config_json_path.write_text(json.dumps(cfg), encoding="utf-8")
return os.path.join(folder, "config.json")


def create_and_build_container_file(folder, build_container, container_tag):
"""
Create container file and build container tag
:param folder: the folder that config.json reside in
:param build_container: the base container image
:param container_tag: container tag
"""
# clean up existed image
clean_image_cmd = "sudo podman rmi %s" % container_tag
process.run(clean_image_cmd, shell=True, ignore_status=True)

container_path = pathlib.Path(folder) / "Containerfile_tmp"
shutil.copy("/etc/yum.repos.d/beaker-BaseOS.repo", folder)
shutil.copy("/etc/yum.repos.d/beaker-AppStream.repo", folder)
container_path.write_text(textwrap.dedent(f"""\n
FROM {build_container}
COPY beaker-BaseOS.repo /etc/yum.repos.d/
COPY beaker-AppStream.repo /etc/yum.repos.d/
RUN dnf install -y vim && dnf clean all
"""), encoding="utf8")
build_cmd = "sudo podman build -t %s -f %s" % (container_tag, str(container_path))
process.run(build_cmd, shell=True, ignore_status=False)


def install_vmware_govc_tool():
"""
Download VmWare govc tool and install it
"""
govc_install_cmd = "curl -L -o - 'https://github.com/vmware/govmomi/releases/latest/download/govc_Linux_x86_64.tar.gz' " \
"| tar -C /usr/local/bin -xvzf - govc"
print(govc_install_cmd)
if not os.path.exists("/usr/local/bin/govc"):
process.run(govc_install_cmd, shell=True, ignore_status=False)


def setup_vCenter_env(params):
"""
Download VmWare govc tool and install it
@param params: one dictionary wrapping various parameter
"""
# vCenter information
os.environ["GOVC_URL"] = params.get("GOVC_URL")
os.environ["GOVC_USERNAME"] = params.get("GOVC_USERNAME")
os.environ["GOVC_PASSWORD"] = params.get("GOVC_PASSWORD")
os.environ["DATA_CENTER"] = params.get("DATA_CENTER")
os.environ["DATA_STORE"] = params.get("DATA_STORE")
os.environ["GOVC_INSECURE"] = "true"
process.run("govc about", shell=True, ignore_status=False)


def parse_container_url(params):
"""
Parse repository information from container url
@param params: wrapped dictionary containing url
"""
container_url = params.get("container_url")
repository_info = container_url.split('/')[-1]
repository_name = repository_info.split(':')[0]
if "localhost" in container_url:
repository_name = "localhost-%s" % repository_name
return repository_name


def convert_disk_image_name(params):
"""
Convert disk type image name
@param params: wrapped dictionary containing parameters
"""
repository_name = parse_container_url(params)
origin_disk_name, extension = os.path.splitext(params.get("output_name"))
dest_disk_name = "%s-%s%s" % (origin_disk_name, repository_name, extension)
return dest_disk_name
74 changes: 74 additions & 0 deletions virttools/tests/cfg/bootc_image_builder/bootc_disk_image_build.cfg
Original file line number Diff line number Diff line change
@@ -0,0 +1,74 @@
- bootc_image_builder.bib.disk_image_generation:
type = bootc_disk_image_build
only x86_64
start_vm = False
take_regular_screendumps = "no"
start_vm = "no"
output_base_folder = "/var/lib/libvirt/images/output"
# vCenter information
GOVC_URL = "example_url"
GOVC_USERNAME = "username"
GOVC_PASSWORD = "userpassword"
DATA_CENTER = "example_datacenter"
DATA_STORE = "example_datastore"
bib_image_url = "quay.io/centos-bootc/bootc-image-builder:latest"
registry = "quay.io"
variants:
- owner_change_enable:
ownership ="107:107"
- owner_change_disable:
variants:
- tls_verify_enable:
enable_tls_verify="true"
- tls_verify_disable:
enable_tls_verify="false"
variants config_json:
- use_config_json:
config_file_path = "/var/lib/libvirt/images"
os_username = "alice"
os_password = "bob"
- unuse_config_json:
variants image_ref:
- centos:
container_url = "quay.io/centos-bootc/centos-bootc:stream9"
- fedora:
container_url = "quay.io/centos-bootc/fedora-bootc:eln"
- local_image:
container_base_folder = "/var/lib/libvirt/images"
container_url = "localhost/bootc:eln"
local_container = "yes"
build_container = "registry-proxy.engineering.redhat.com/rh-osbs/rhel9-rhel_bootc:rhel-9.4"
- rhel:
podman_redhat_username = "podman_redhat_username"
podman_redhat_password = "podman_redhat_password"
redhat_registry = "registry.redhat.io"
container_base_folder = "/var/lib/libvirt/images"
bib_image_url = "registry.redhat.io/rhel9-beta/bootc-image-builder:9.4"
build_container = "registry.redhat.io/rhel9-beta/rhel-bootc:9.4"
container_url = "quay.io/wenbaoxin/rhel9test"
podman_quay_username = "podman_quay_username"
podman_quay_password = "podman_quay_password"
only use_config_json..tls_verify_enable..owner_change_disable
no vmdk
no anaconda-iso
variants:
- ami:
disk_image_type = "ami"
output_sub_folder = "image"
output_name = "disk.raw"
- qcow:
disk_image_type = "qcow2"
output_sub_folder = "qcow2"
output_name = "disk.qcow2"
- vmdk:
disk_image_type = "vmdk"
output_sub_folder = "vmdk"
output_name = "disk.vmdk"
- anaconda-iso:
disk_image_type = "anaconda-iso"
output_sub_folder = "bootiso"
output_name = "install.iso"
- raw:
disk_image_type = "raw"
output_sub_folder = "image"
output_name = "disk.raw"
80 changes: 80 additions & 0 deletions virttools/tests/src/bootc_image_builder/bootc_disk_image_build.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,80 @@
# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
#
# Copyright Red Hat
#
# SPDX-License-Identifier: GPL-2.0

# Author: Chunfu Wen <[email protected]>
# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

import logging
import os

from provider.bootc_image_builder import bootc_image_build_utils as bib_utils

LOG = logging.getLogger('avocado.' + __name__)
cleanup_files = []


def validate_bib_output(params, test):
"""
Common method to check whether image build output exists
:param params: one collective object representing wrapped parameters
:param test: test object
"""
base_folder = params.get("output_base_folder")
output_sub_folder = params.get("output_sub_folder")
output_name = params.get("output_name")
full_path = os.path.join(base_folder, output_sub_folder, output_name)
if not os.path.exists(full_path):
test.fail("bootc image build fail to generate outputs for image type: %s" % params.get("disk_image_type"))


def run(test, params, env):
"""
Test boot container image builder.
"""
disk_image_type = params.get("disk_image_type")
bib_image_url = params.get("bib_image_url", "quay.io/centos-bootc/bootc-image-builder:latest")
image_ref = params.get("image_ref")
container_url = params.get("container_url")
local_container = "yes" == params.get("local_container")
build_container = params.get("build_container")

enable_tls_verify = params.get("enable_tls_verify")
config_json = params.get("config_json")
config_json_file = None

ownership = params.get("ownership")

try:
bib_utils.install_bib_packages()
if config_json == "use_config_json":
config_json_file = bib_utils.create_config_json_file(params.get("config_file_path"),
params.get("os_username"), params.get("os_password"))
# pull base image and build local image after change
if build_container:
if image_ref == "rhel":
bib_utils.podman_login(params.get("podman_redhat_username"), params.get("podman_redhat_password"),
params.get("redhat_registry"))
bib_utils.create_and_build_container_file(params.get("container_base_folder"),
build_container, container_url)
if image_ref == "rhel":
bib_utils.podman_push(params.get("podman_quay_username"), params.get("podman_quay_password"),
params.get("registry"), container_url)

bib_utils.podman_command_build(bib_image_url, disk_image_type, container_url, config_json_file,
local_container, enable_tls_verify, ownership, options=None)
# validate build output
validate_bib_output(params, test)
if disk_image_type == "vmdk":
bib_utils.install_vmware_govc_tool()
bib_utils.setup_vCenter_env(params)
except Exception as ex:
raise ex
finally:
# Clean up files
for file_path in cleanup_files:
if os.path.exists(file_path):
os.remove(file_path)

0 comments on commit 1e1f1b8

Please sign in to comment.