From 6daf24d003f0a798edc53d837a79ff7d94bc0a51 Mon Sep 17 00:00:00 2001 From: Wesley Hayutin Date: Fri, 24 Apr 2026 12:30:07 -0600 Subject: [PATCH 1/2] need an install path for latest openshift-virt in upstream Signed-off-by: Wesley Hayutin --- .gitignore | 3 + hack/install_upstream_kubevirt/install_hco.sh | 460 ++++++++++++++++++ .../uninstall_hco.sh | 216 ++++++++ 3 files changed, 679 insertions(+) create mode 100755 hack/install_upstream_kubevirt/install_hco.sh create mode 100755 hack/install_upstream_kubevirt/uninstall_hco.sh diff --git a/.gitignore b/.gitignore index 05921057c15..65d18743440 100644 --- a/.gitignore +++ b/.gitignore @@ -45,3 +45,6 @@ must-gather/oadp-must-gather must-gather/must-gather/ must-gather/must-gather.local.*/ tests/e2e/must-gather/ + +# hack virt install +kustomize diff --git a/hack/install_upstream_kubevirt/install_hco.sh b/hack/install_upstream_kubevirt/install_hco.sh new file mode 100755 index 00000000000..e3aa8d126e6 --- /dev/null +++ b/hack/install_upstream_kubevirt/install_hco.sh @@ -0,0 +1,460 @@ +#!/bin/bash +# Install upstream HyperConverged Cluster Operator (HCO) and KubeVirt on OpenShift via OLM. +# +# Implements the kustomize-based installation approach documented at: +# https://github.com/kubevirt/hyperconverged-cluster-operator#installing-hco-using-kustomize-openshift-olm-only +# +# All images are 100% upstream (quay.io/kubevirt). No Red Hat IIB / internal images are used. +# +# Usage: +# ./install_hco.sh +# +# Environment variables (all optional — defaults shown): +# +# HCO_CHANNEL OLM subscription channel. +# (default: candidate-v1.18) +# +# HCO_INDEX_IMAGE CatalogSource grpc index image. Use: +# Unstable (latest from main): quay.io/kubevirt/hyperconverged-cluster-index:1.18.0-unstable +# Stable tagged release: quay.io/kubevirt/hyperconverged-cluster-index:1.18.0 +# (default: quay.io/kubevirt/hyperconverged-cluster-index:1.18.0-unstable) +# +# HCO_VERSION Pin a specific startingCSV version, e.g. "1.18.0" or "1.18.0-202604211450". +# Leave empty (default) to use the channel head (recommended for unstable index). +# (default: "" — no startingCSV pinning) +# +# HCO_GIT_REF Git ref to download kustomize manifests from. +# Use "main" for latest or "v1.18.0" for a tagged release. +# (default: main) +# +# HCO_NAMESPACE Namespace to install HCO into. +# (default: kubevirt-hyperconverged) +# +# KVM_EMULATION true = patch HCO CR with KVM software emulation (for nodes without HW virt). +# (default: false) +# +# TIMEOUT Timeout for oc wait readiness checks, e.g. "15m" or "30m". +# (default: 15m) +# +# OC_TOOL oc or kubectl binary. +# (default: oc) +# +# Examples: +# # Install latest unstable build from main branch (no version pinning): +# ./install_hco.sh +# +# # Install a specific stable tagged release: +# HCO_CHANNEL=stable-v1.18 \ +# HCO_INDEX_IMAGE=quay.io/kubevirt/hyperconverged-cluster-index:1.18.0 \ +# HCO_GIT_REF=v1.18.0 \ +# ./install_hco.sh +# +# # Pin to the exact CSV shown by: oc get packagemanifest community-kubevirt-hyperconverged +# HCO_VERSION=1.18.0-202604211450 ./install_hco.sh +# +# # Enable KVM software emulation on bare-metal without hardware virt: +# KVM_EMULATION=true ./install_hco.sh +# +# # Extend timeout for slow clusters: +# TIMEOUT=30m ./install_hco.sh + +set -euo pipefail + +# --------------------------------------------------------------------------- +# Defaults +# --------------------------------------------------------------------------- +HCO_CHANNEL="${HCO_CHANNEL:-candidate-v1.18}" +HCO_INDEX_IMAGE="${HCO_INDEX_IMAGE:-quay.io/kubevirt/hyperconverged-cluster-index:1.18.0-unstable}" +HCO_VERSION="${HCO_VERSION:-}" # empty = no startingCSV pin; use channel head +HCO_GIT_REF="${HCO_GIT_REF:-main}" +HCO_NAMESPACE="${HCO_NAMESPACE:-kubevirt-hyperconverged}" +KVM_EMULATION="${KVM_EMULATION:-false}" +TIMEOUT="${TIMEOUT:-15m}" +OC_TOOL="${OC_TOOL:-oc}" + +CATALOGSOURCE_NAME="kubevirt-hyperconverged" +CATALOGSOURCE_NS="openshift-marketplace" +SUBSCRIPTION_NAME="hco-operatorhub" +PACKAGE_NAME="community-kubevirt-hyperconverged" + +# --------------------------------------------------------------------------- +# Helpers +# --------------------------------------------------------------------------- +log() { echo "[INFO] $(date +%H:%M:%S) $*"; } +warn() { echo "[WARN] $(date +%H:%M:%S) $*"; } +err() { echo "[ERROR] $(date +%H:%M:%S) $*" >&2; } + +WORK_DIR="" +cleanup() { + if [[ -n "${WORK_DIR}" && -d "${WORK_DIR}" ]]; then + log "Cleaning up temp directory: ${WORK_DIR}" + rm -rf "${WORK_DIR}" + fi +} +trap cleanup EXIT + +require_tool() { + if ! command -v "$1" &>/dev/null; then + err "Required tool '$1' not found in PATH." + exit 1 + fi +} + +# --------------------------------------------------------------------------- +# Pre-flight checks +# --------------------------------------------------------------------------- +preflight() { + log "Running pre-flight checks..." + require_tool "${OC_TOOL}" + require_tool curl + require_tool tar + + if ! ${OC_TOOL} whoami &>/dev/null; then + err "Not logged in to a cluster. Run 'oc login' first." + exit 1 + fi + log "Logged in as: $(${OC_TOOL} whoami)" + log "Cluster: $(${OC_TOOL} whoami --show-server)" + + if ${OC_TOOL} get namespace "${HCO_NAMESPACE}" &>/dev/null; then + warn "Namespace '${HCO_NAMESPACE}' already exists." + if ${OC_TOOL} get hyperconverged kubevirt-hyperconverged \ + -n "${HCO_NAMESPACE}" &>/dev/null 2>&1; then + err "HyperConverged CR already exists. Run uninstall_hco.sh first." + exit 1 + fi + # Clean up any leftover OLM resources from a previous partial install + # (e.g. a subscription with a stale startingCSV that would block resolution) + log "Cleaning up any leftover subscriptions from previous install attempt..." + ${OC_TOOL} delete subscription "${SUBSCRIPTION_NAME}" \ + -n "${HCO_NAMESPACE}" --ignore-not-found 2>/dev/null || true + ${OC_TOOL} delete clusterserviceversions \ + -n "${HCO_NAMESPACE}" --all --ignore-not-found 2>/dev/null || true + fi +} + +# --------------------------------------------------------------------------- +# Download the kustomize directory from the HCO GitHub repo +# +# GitHub tarball structure: +# kubevirt-hyperconverged-cluster-operator-/deploy/kustomize/... +# +# With --strip-components=1 and cd into WORK_DIR, files land at: +# ${WORK_DIR}/deploy/kustomize/... +# --------------------------------------------------------------------------- +download_kustomize() { + WORK_DIR="$(mktemp -d -t hco-kustomize-XXXXXX)" + local tarball_url="https://api.github.com/repos/kubevirt/hyperconverged-cluster-operator/tarball/${HCO_GIT_REF}" + + log "Downloading HCO kustomize manifests (git ref: '${HCO_GIT_REF}')..." + ( + cd "${WORK_DIR}" + curl -fsSL "${tarball_url}" \ + | tar --strip-components=1 -xzf - \ + --wildcards \ + "kubevirt-hyperconverged-cluster-operator-*/deploy/kustomize" + ) + + KUSTOMIZE_DIR="${WORK_DIR}/deploy/kustomize" + if [[ ! -f "${KUSTOMIZE_DIR}/deploy_kustomize.sh" ]]; then + err "deploy_kustomize.sh not found after extraction." + err "Expected: ${KUSTOMIZE_DIR}/deploy_kustomize.sh" + exit 1 + fi + log "Kustomize dir ready: ${KUSTOMIZE_DIR}" +} + +# --------------------------------------------------------------------------- +# Step 1: Create the CatalogSource pointing to the upstream index image +# --------------------------------------------------------------------------- +apply_catalogsource() { + log "Creating CatalogSource '${CATALOGSOURCE_NAME}' → ${HCO_INDEX_IMAGE} ..." + + ${OC_TOOL} apply -f - </dev/null || true) + if [[ "${state}" == "READY" ]]; then + log "CatalogSource is READY." + break + fi + if [[ $(date +%s) -gt ${deadline} ]]; then + err "CatalogSource did not become READY within 10m." + ${OC_TOOL} describe catalogsource "${CATALOGSOURCE_NAME}" \ + -n "${CATALOGSOURCE_NS}" || true + exit 1 + fi + log " CatalogSource state: '${state:-}' — retrying in 10s..." + sleep 10 + done +} + +# --------------------------------------------------------------------------- +# Step 2: Discover the actual CSV name in the channel (informational). +# Uses --selector=catalog= to target our specific CatalogSource. +# Critical for unstable builds that use date-stamped version suffixes. +# --------------------------------------------------------------------------- +discover_csv() { + log "Discovering current CSV in channel '${HCO_CHANNEL}' of '${PACKAGE_NAME}'..." + local retries=0 max_retries=18 # 18 × 10s = 3 minutes + local csv="" + while [[ -z "${csv}" ]]; do + if (( retries == max_retries )); then + warn "Timed out waiting for PackageManifest. Proceeding with channel head." + DISCOVERED_CSV="" + return 0 + fi + sleep 10 + # Use --selector=catalog to target our specific CatalogSource + csv=$(${OC_TOOL} get packagemanifest "${PACKAGE_NAME}" \ + --selector="catalog=${CATALOGSOURCE_NAME}" \ + -o jsonpath="{.status.channels[?(@.name==\"${HCO_CHANNEL}\")].currentCSV}" \ + 2>/dev/null || true) + # Fall back to any catalog if our custom one hasn't propagated yet + if [[ -z "${csv}" ]]; then + csv=$(${OC_TOOL} get packagemanifest "${PACKAGE_NAME}" \ + -o jsonpath="{.status.channels[?(@.name==\"${HCO_CHANNEL}\")].currentCSV}" \ + 2>/dev/null || true) + fi + (( retries += 1 )) + done + DISCOVERED_CSV="${csv}" + log "Discovered CSV: ${DISCOVERED_CSV}" +} + +# --------------------------------------------------------------------------- +# Step 3: Create Namespace, OperatorGroup, Subscription +# --------------------------------------------------------------------------- +apply_olm_resources() { + log "Creating Namespace '${HCO_NAMESPACE}'..." + ${OC_TOOL} apply -f - </dev/null || true) + (( retries += 1 )) + done + log "InstallPlan: ${install_plan}" + + log "Waiting for InstallPlan ${install_plan} to complete (timeout: 5m)..." + ${OC_TOOL} wait installplan "${install_plan}" \ + -n "${HCO_NAMESPACE}" \ + --for=condition=Installed \ + --timeout=5m + log "InstallPlan complete." +} + +# --------------------------------------------------------------------------- +# Step 5: Wait for the HCO operator deployment to be Available +# --------------------------------------------------------------------------- +wait_for_hco_operator() { + log "Waiting for HCO operator deployment to be Available (timeout: 5m)..." + ${OC_TOOL} wait deployments \ + --selector="operators.coreos.com/${PACKAGE_NAME}.${HCO_NAMESPACE}" \ + -n "${HCO_NAMESPACE}" \ + --for=condition=Available \ + --timeout=5m + log "HCO operator deployment is Available." +} + +# --------------------------------------------------------------------------- +# Step 6: Create the HyperConverged CR +# +# Uses the HCO v1.18 API structure where: +# - spec.featureGates is []Object {name, state} +# - virtualization settings live under spec.virtualization.* +# - cert config lives under spec.security.certConfig +# - workloadSources.enableCommonBootImageImport replaces spec.enableCommonBootImageImport +# --------------------------------------------------------------------------- +apply_hco_cr() { + log "Creating HyperConverged CR..." + + local kvm_developer_config="" + if [[ "${KVM_EMULATION}" == "true" ]]; then + log " KVM_EMULATION=true: enabling software emulation" + kvm_developer_config=" + developerConfiguration: + useEmulation: true" + fi + + ${OC_TOOL} apply -f - </dev/null || true + + echo "" + echo "=== KubeVirt ===" + ${OC_TOOL} get kubevirt -n "${HCO_NAMESPACE}" 2>/dev/null || true + + echo "" + echo "=== CDI ===" + ${OC_TOOL} get cdi -n "${HCO_NAMESPACE}" 2>/dev/null || true + + echo "" + echo "=== Pods ===" + ${OC_TOOL} get pods -n "${HCO_NAMESPACE}" 2>/dev/null || true +} + +# --------------------------------------------------------------------------- +# Main +# --------------------------------------------------------------------------- +main() { + log "============================================================" + log " HCO / KubeVirt Upstream Install" + log "============================================================" + log " HCO_CHANNEL = ${HCO_CHANNEL}" + log " HCO_INDEX_IMAGE = ${HCO_INDEX_IMAGE}" + log " HCO_VERSION = ${HCO_VERSION:-}" + log " HCO_GIT_REF = ${HCO_GIT_REF}" + log " HCO_NAMESPACE = ${HCO_NAMESPACE}" + log " KVM_EMULATION = ${KVM_EMULATION}" + log " TIMEOUT = ${TIMEOUT}" + log "============================================================" + + preflight + download_kustomize # validates connectivity; WORK_DIR set for cleanup + apply_catalogsource + discover_csv + apply_olm_resources + wait_for_installplan + wait_for_hco_operator + apply_hco_cr + wait_for_hco + print_status +} + +main "$@" diff --git a/hack/install_upstream_kubevirt/uninstall_hco.sh b/hack/install_upstream_kubevirt/uninstall_hco.sh new file mode 100755 index 00000000000..5ec065cc927 --- /dev/null +++ b/hack/install_upstream_kubevirt/uninstall_hco.sh @@ -0,0 +1,216 @@ +#!/bin/bash +# Uninstall upstream HyperConverged Cluster Operator (HCO) and KubeVirt from OpenShift. +# +# Handles finalizers and ordered teardown carefully to avoid stuck resources. +# +# Usage: +# ./uninstall_hco.sh +# +# Environment variables: +# HCO_NAMESPACE Namespace HCO was installed into. +# Auto-detected from hco-operator deployment if not set. +# (default: kubevirt-hyperconverged) +# +# OC_TOOL oc or kubectl binary. +# (default: oc) + +set -euo pipefail + +# --------------------------------------------------------------------------- +# Defaults +# --------------------------------------------------------------------------- +OC_TOOL="${OC_TOOL:-oc}" + +# Auto-detect namespace from the running hco-operator deployment, fall back to default +HCO_NAMESPACE="${HCO_NAMESPACE:-}" +if [[ -z "${HCO_NAMESPACE}" ]]; then + HCO_NAMESPACE=$( + ${OC_TOOL} get deployments --all-namespaces \ + --field-selector='metadata.name=hco-operator' \ + -o jsonpath='{$.items[0].metadata.namespace}' \ + --ignore-not-found 2>/dev/null || true + ) + HCO_NAMESPACE="${HCO_NAMESPACE:-kubevirt-hyperconverged}" +fi + +readonly HCO_NAMESPACE OC_TOOL + +CATALOGSOURCE_NAME="kubevirt-hyperconverged" +CATALOGSOURCE_NS="openshift-marketplace" + +# --------------------------------------------------------------------------- +# Helpers +# --------------------------------------------------------------------------- +log() { echo "[INFO] $(date +%H:%M:%S) $*"; } +warn() { echo "[WARN] $(date +%H:%M:%S) $*"; } +err() { echo "[ERROR] $(date +%H:%M:%S) $*" >&2; } + +oc_delete() { ${OC_TOOL} delete --ignore-not-found "$@"; } + +# --------------------------------------------------------------------------- +# Main +# --------------------------------------------------------------------------- +main() { + log "============================================================" + log " HCO / KubeVirt Upstream Uninstall" + log "============================================================" + log " HCO_NAMESPACE = ${HCO_NAMESPACE}" + log "============================================================" + + if ! ${OC_TOOL} whoami &>/dev/null; then + err "Not logged in to a cluster. Run 'oc login' first." + exit 1 + fi + + # ----------------------------------------------------------------------- + # 1. Set uninstallStrategy=RemoveWorkloads on KubeVirt and CDI so that + # HCO does not block deletion due to running workloads. + # ----------------------------------------------------------------------- + log "Setting uninstallStrategy=RemoveWorkloads on KubeVirt/CDI..." + ${OC_TOOL} get cdis,kubevirts -n "${HCO_NAMESPACE}" \ + --output='name' --ignore-not-found 2>/dev/null \ + | xargs -r ${OC_TOOL} patch -n "${HCO_NAMESPACE}" \ + --type='merge' \ + --patch='{"spec":{"uninstallStrategy":"RemoveWorkloads"}}' \ + || true + + # ----------------------------------------------------------------------- + # 2. Disable common boot image import to stop background DataVolume creation + # ----------------------------------------------------------------------- + log "Disabling HCO boot image import..." + if ${OC_TOOL} get hyperconverged kubevirt-hyperconverged \ + -n "${HCO_NAMESPACE}" &>/dev/null 2>&1; then + ${OC_TOOL} patch hyperconverged kubevirt-hyperconverged \ + -n "${HCO_NAMESPACE}" \ + --type='merge' \ + --patch='{"spec":{"enableCommonBootImageImport":false}}' \ + || true + sleep 5 + fi + + # ----------------------------------------------------------------------- + # 3. Delete VMs, VMIs, DataVolumes, DataImportCrons + # (order matters: VM → VMI → DV → DataImportCron) + # ----------------------------------------------------------------------- + log "Removing VM workloads..." + for kind in vm vmi dv dataimportcron; do + if ${OC_TOOL} get "${kind}" --all-namespaces &>/dev/null 2>&1; then + oc_delete "${kind}" --all-namespaces --all || true + ${OC_TOOL} wait "${kind}" --all-namespaces --all \ + --for=delete --timeout=3m || true + fi + done + + # ----------------------------------------------------------------------- + # 4. Delete HPP (HostPath Provisioner) and related StorageClasses + # ----------------------------------------------------------------------- + log "Removing HostPath Provisioner..." + if ${OC_TOOL} get hostpathprovisioners --all-namespaces &>/dev/null 2>&1; then + oc_delete hostpathprovisioners --all-namespaces --all || true + fi + for sc in hostpath-provisioner hostpath-csi hostpath-csi-basic hostpath-csi-pvc-block; do + oc_delete storageclass "${sc}" || true + done + ${OC_TOOL} wait daemonsets -n "${HCO_NAMESPACE}" \ + --selector='k8s-app=hostpath-provisioner' \ + --for=delete --timeout=3m || true + + # ----------------------------------------------------------------------- + # 5. Delete image streams created by the OS image namespace + # ----------------------------------------------------------------------- + log "Removing image streams..." + oc_delete imagestream -n openshift-virtualization-os-images --all || true + + # ----------------------------------------------------------------------- + # 6. Delete the HyperConverged CR and wait for operator-driven teardown + # ----------------------------------------------------------------------- + log "Deleting HyperConverged CR..." + if ${OC_TOOL} get hyperconverged kubevirt-hyperconverged \ + -n "${HCO_NAMESPACE}" &>/dev/null 2>&1; then + oc_delete hyperconverged --all-namespaces --all || true + fi + log "Waiting for HCO-managed deployments/daemonsets to be removed (2m)..." + ${OC_TOOL} wait deployments,daemonsets -n "${HCO_NAMESPACE}" \ + --selector='!olm.owner' \ + --for=delete --timeout=2m || true + + # ----------------------------------------------------------------------- + # 7. Remove OLM resources: CSV → Subscription → OperatorGroup → CatalogSource + # ----------------------------------------------------------------------- + log "Removing OLM resources..." + oc_delete clusterserviceversions -n "${HCO_NAMESPACE}" \ + --selector="operators.coreos.com/community-kubevirt-hyperconverged.${HCO_NAMESPACE}" \ + || true + # Wait for operator-owned deployments to go away + ${OC_TOOL} wait deployments -n "${HCO_NAMESPACE}" \ + --selector='olm.owner' \ + --for=delete --timeout=3m || true + + oc_delete subscriptions.operators -n "${HCO_NAMESPACE}" --all || true + oc_delete operatorgroups -n "${HCO_NAMESPACE}" --all || true + oc_delete catalogsource "${CATALOGSOURCE_NAME}" \ + -n "${CATALOGSOURCE_NS}" || true + + # ----------------------------------------------------------------------- + # 8. Remove cluster-scoped leftovers: CRDs, webhooks, RBAC, API services + # Use awk to match kubevirt-owned resources safely (avoids false matches) + # ----------------------------------------------------------------------- + log "Removing cluster-scoped kubevirt resources..." + for kind in \ + clusterroles \ + clusterrolebindings \ + validatingwebhookconfigurations \ + mutatingwebhookconfigurations \ + apiservices \ + customresourcedefinitions + do + ${OC_TOOL} get "${kind}" --no-headers --ignore-not-found 2>/dev/null \ + | awk '$1 ~ /[.\-]kubevirt[.\-]?/ || $1 ~ /^kubevirt/ { print $1 }' \ + | xargs -r ${OC_TOOL} delete "${kind}" --ignore-not-found \ + || true + done + + # ----------------------------------------------------------------------- + # 9. Delete the HCO namespace and the OS images namespace + # ----------------------------------------------------------------------- + log "Deleting namespaces..." + oc_delete namespace "${HCO_NAMESPACE}" || true + oc_delete namespace openshift-virtualization-os-images || true + + # Wait for namespace termination + log "Waiting for namespace '${HCO_NAMESPACE}' to terminate (5m)..." + ${OC_TOOL} wait namespace "${HCO_NAMESPACE}" \ + --for=delete --timeout=5m || true + + # ----------------------------------------------------------------------- + # 10. Reclaim any HPP LSO PVs + # ----------------------------------------------------------------------- + log "Reclaiming HPP local storage PVs (if any)..." + ${OC_TOOL} get pv \ + -l storage.openshift.com/local-volume-owner-name=local-block-hpp \ + -o name --ignore-not-found 2>/dev/null \ + | xargs -r -I{} ${OC_TOOL} patch {} \ + --type='merge' \ + --patch='{"spec":{"claimRef":null}}' \ + || true + + log "============================================================" + log " Uninstall Complete" + log "============================================================" + + # Final check for lingering kubevirt CRDs + local remaining + remaining=$(${OC_TOOL} get crd --no-headers --ignore-not-found 2>/dev/null \ + | awk '$1 ~ /kubevirt|hco\.kubevirt|cdi\.kubevirt|ssp\.kubevirt/' \ + | wc -l || true) + if (( remaining > 0 )); then + warn "${remaining} kubevirt-related CRD(s) still present — may need manual removal:" + ${OC_TOOL} get crd --no-headers 2>/dev/null \ + | awk '$1 ~ /kubevirt|hco\.kubevirt|cdi\.kubevirt|ssp\.kubevirt/ { print " " $1 }' \ + || true + else + log "No kubevirt CRDs remaining. Cluster is clean." + fi +} + +main "$@" From 9e90ed35b8fa2e84ad12fb7d36f494332f50e227 Mon Sep 17 00:00:00 2001 From: Wesley Hayutin Date: Mon, 27 Apr 2026 12:09:54 -0600 Subject: [PATCH 2/2] install the upstream nightly for kubevirt and hco Signed-off-by: Wesley Hayutin --- hack/install_upstream_kubevirt/install_hco.sh | 35 +++++++++++-------- 1 file changed, 21 insertions(+), 14 deletions(-) diff --git a/hack/install_upstream_kubevirt/install_hco.sh b/hack/install_upstream_kubevirt/install_hco.sh index e3aa8d126e6..b22e910f728 100755 --- a/hack/install_upstream_kubevirt/install_hco.sh +++ b/hack/install_upstream_kubevirt/install_hco.sh @@ -336,8 +336,19 @@ wait_for_hco_operator() { # --------------------------------------------------------------------------- # Step 6: Create the HyperConverged CR # -# Uses the HCO v1.18 API structure where: -# - spec.featureGates is []Object {name, state} +# NOTE on featureGates: HCO v1.18 nightly builds (e.g. v1.18.0-202604*) have a +# CRD/webhook mismatch — the CRD schema declares featureGates as []Object{name,state} +# but the running webhook Go struct still expects the old map/struct format. Any +# featureGates entry (even an empty list) causes the webhook to reject the CR with +# "failed to parse the HyperConverged". Omit featureGates entirely; the two gates we +# care about (decentralizedLiveMigration, videoConfig) are beta-phase and on by default. +# incrementalBackup (alpha) can be re-added once the nightly build stabilises. +# +# NOTE on KVM emulation: spec.virtualization has no developerConfiguration field. +# useEmulation is set on the KubeVirt CR (spec.configuration.developerConfiguration) +# via a jsonpatch annotation on the HyperConverged CR after initial creation. +# +# API structure for v1.18: # - virtualization settings live under spec.virtualization.* # - cert config lives under spec.security.certConfig # - workloadSources.enableCommonBootImageImport replaces spec.enableCommonBootImageImport @@ -345,12 +356,13 @@ wait_for_hco_operator() { apply_hco_cr() { log "Creating HyperConverged CR..." - local kvm_developer_config="" + # KVM emulation annotation: HCO forwards this jsonpatch to the KubeVirt CR + local kvm_annotation="" if [[ "${KVM_EMULATION}" == "true" ]]; then - log " KVM_EMULATION=true: enabling software emulation" - kvm_developer_config=" - developerConfiguration: - useEmulation: true" + log " KVM_EMULATION=true: will patch KubeVirt CR for software emulation" + kvm_annotation=' annotations: + kubevirt.kubevirt.io/jsonpatch: > + [{"op":"add","path":"/spec/configuration/developerConfiguration","value":{"useEmulation":true}}]' fi ${OC_TOOL} apply -f - <