#!/bin/bash

set -exuo pipefail

export LC_ALL=C
export LANG=C

source tools.sh
source createdisk-library.sh

SSH="ssh -o StrictHostKeyChecking=no -o UserKnownHostsFile=/dev/null -i id_ecdsa_crc"
SCP="scp -o StrictHostKeyChecking=no -o UserKnownHostsFile=/dev/null -i id_ecdsa_crc"

INSTALL_DIR=${1:-crc-tmp-install-data}

OPENSHIFT_VERSION=$(${JQ} -r .clusterInfo.openshiftVersion $INSTALL_DIR/crc-bundle-info.json)
BASE_DOMAIN=$(${JQ} -r .clusterInfo.baseDomain $INSTALL_DIR/crc-bundle-info.json)
BUNDLE_TYPE=$(${JQ} -r .type $INSTALL_DIR/crc-bundle-info.json)
ADDITIONAL_PACKAGES="cloud-init gvisor-tap-vsock-gvforwarder"
PRE_DOWNLOADED_ADDITIONAL_PACKAGES=""

case ${BUNDLE_TYPE} in
    microshift)
        destDirPrefix="crc_${BUNDLE_TYPE}"
        BASE_OS=rhel
        ;;
    okd)
        destDirPrefix="crc_${BUNDLE_TYPE}"
        # Base OS is not changed for scos-okd because `/proc/cmdline` still contain fedora-coreos
        # https://github.com/okd-project/okd-scos/issues/18
        BASE_OS=fedora-coreos
        ;;
    snc)
        destDirPrefix="crc"
        BASE_OS=rhcos
        ;;
    *)
        echo "Unknown bundle type '$BUNDLE_TYPE'"
        exit 1
        ;;
esac

# SNC_PRODUCT_NAME: If user want to use other than default product name (crc)
# VM_PREFIX: short VM name (set by SNC_PRODUCT_NAME) + random string generated by openshift-installer
SNC_PRODUCT_NAME=${SNC_PRODUCT_NAME:-crc}
VM_NAME=${SNC_PRODUCT_NAME}

VM_IP=$(sudo virsh domifaddr ${VM_NAME} | tail -2 | head -1 | awk '{print $4}' | cut -d/ -f1)

wait_for_ssh ${VM_NAME} ${VM_IP}

if [ ${BUNDLE_TYPE} != "microshift" ]; then
    # Disable kubelet service
    ${SSH} core@${VM_IP} -- sudo systemctl disable kubelet
    
    # Stop the kubelet service so it will not reprovision the pods
    ${SSH} core@${VM_IP} -- sudo systemctl stop kubelet
fi

# Enable the system and user level  podman.socket service for API V2
${SSH} core@${VM_IP} -- sudo systemctl enable podman.socket
${SSH} core@${VM_IP} -- systemctl --user enable podman.socket

if [ ${BUNDLE_TYPE} == "microshift" ]; then
    # Pull openshift release images because as part of microshift bundle creation we
    # don't run microshift service which fetch these image but instead service is run
    # as part of crc so user have a fresh cluster instead something already provisioned
    # but images we cache it as part of bundle.
    ${SSH} core@${VM_IP} 'sudo bash -x -s' <<EOF
     jq --raw-output '.images | to_entries | map(.value) | join("\n")' /usr/share/microshift/release/release-$(uname -i).json | xargs -n1 podman pull --authfile /etc/crio/openshift-pull-secret
EOF
    # Disable firewalld otherwise generated bundle have it running and each podman container
    # which try to expose a port need to added to firewalld rule manually
    # also in case of microshift the ports like 2222, 443, 80 ..etc need to be manually added
    # and OCP/OKD/podman bundles have it disabled by default.
    ${SSH} core@${VM_IP} -- sudo systemctl disable firewalld
    # Make sure `baseDomain` is set to crc.testing
    ${SSH} core@${VM_IP} -- "grep '^\s\+baseDomain: ${SNC_PRODUCT_NAME}.${BASE_DOMAIN}' /etc/microshift/config.d/00-microshift-dns.yaml"
    # Remove the lvm system.device file since it have diskID and deviceName which changes
    # for different hypervisor and as per `man lvmdevices` if the file does not exist, or if lvm.conf
    # includes use_devicesfile=0, then lvm will not use a devices file.
    ${SSH} core@${VM_IP} -- "sudo rm -fr /etc/lvm/devices/system.devices"
fi

remove_pull_secret_from_disk

if [ "${SNC_GENERATE_WINDOWS_BUNDLE}" != "0" ]; then
    prepare_hyperV ${VM_IP}
fi

prepare_qemu_guest_agent ${VM_IP}

image_tag="latest"
if podman manifest inspect quay.io/crcont/routes-controller:${OPENSHIFT_VERSION} >/dev/null 2>&1; then
    image_tag=${OPENSHIFT_VERSION}
fi

# create the tap device interface with specified mac address
# this mac address is used to allocate a specific IP to the VM
# when tap device is in use.
${SSH} core@${VM_IP} 'sudo bash -x -s' <<EOF
  nmcli connection add type tun ifname tap0 con-name tap0 mode tap autoconnect yes 802-3-ethernet.cloned-mac-address 5A:94:EF:E4:0C:EE ipv6.method "disabled"
EOF


# Add gvisor-tap-vsock service
${SSH} core@${VM_IP} 'sudo bash -x -s' <<EOF
  tee /etc/systemd/system/gv-user-network@.service <<TEE
[Unit]
Description=gvisor-tap-vsock Network Traffic Forwarder
After=sys-devices-virtual-net-%i.device

[Service]
Restart=on-failure
Environment="GV_VSOCK_PORT=1024"
EnvironmentFile=-/etc/sysconfig/gv-user-network
ExecStart=/usr/libexec/podman/gvforwarder -preexisting -iface %i -url vsock://2:"\\\${GV_VSOCK_PORT}"/connect

[Install]
WantedBy=multi-user.target

TEE
  systemctl daemon-reload
  systemctl enable gv-user-network@tap0.service
EOF

# Add dummy crio-wipe service to instance
cat crio-wipe.service | ${SSH} core@${VM_IP} "sudo tee -a /etc/systemd/system/crio-wipe.service"

# Preload routes controller
${SSH} core@${VM_IP} -- "sudo podman pull quay.io/crcont/routes-controller:${image_tag}"
TAG=${image_tag} envsubst < routes-controller.yaml.in > $INSTALL_DIR/routes-controller.yaml
${SCP} $INSTALL_DIR/routes-controller.yaml core@${VM_IP}:/home/core/
${SSH} core@${VM_IP} -- 'sudo mkdir -p /opt/crc && sudo mv /home/core/routes-controller.yaml /opt/crc/'

if [ ${BUNDLE_TYPE} != "microshift" ]; then
    # Add internalIP as node IP for kubelet systemd unit file
    # More details at https://bugzilla.redhat.com/show_bug.cgi?id=1872632
    ${SSH} core@${VM_IP} 'sudo bash -x -s' <<EOF
    echo '[Service]' > /etc/systemd/system/kubelet.service.d/80-nodeip.conf
    echo 'Environment=KUBELET_NODE_IP="${VM_IP}"' >> /etc/systemd/system/kubelet.service.d/80-nodeip.conf
EOF
fi

if [ "${ARCH}" == "aarch64" ] && [ ${BUNDLE_TYPE} != "okd" ]; then
   # Install qemu-user-static-x86 package from fedora-updates repo to run x86 image on M1
   # Not supported by RHEL https://access.redhat.com/solutions/5654221 and not included
   # in any subscription repo.
   cat > /tmp/fedora-updates.repo <<'EOF'
[fedora-updates]
name=Fedora 41 - $basearch - Updates
metalink=https://mirrors.fedoraproject.org/metalink?repo=updates-released-f41&arch=$basearch
enabled=1
type=rpm
repo_gpgcheck=0
gpgcheck=0
EOF
   ${SCP} /tmp/fedora-updates.repo core@${VM_IP}:/tmp
   ${SSH} core@${VM_IP} -- "sudo mv /tmp/fedora-updates.repo /etc/yum.repos.d"
   ${SSH} core@${VM_IP} -- "mkdir -p ~/packages && dnf download --downloadonly --downloaddir ~/packages qemu-user-static-x86 --resolve"
   ${SSH} core@${VM_IP} -- "sudo rm -fr /etc/yum.repos.d/fedora-updates.repo"
   PRE_DOWNLOADED_ADDITIONAL_PACKAGES+=" qemu-user-static-x86"
fi

# Beyond this point, packages added to the ADDITIONAL_PACKAGES variable won’t be installed in the guest
install_additional_packages ${VM_IP}
copy_systemd_units

cleanup_vm_image ${VM_NAME} ${VM_IP}

# Enable cloud-init service
${SSH} core@${VM_IP} -- "sudo systemctl enable cloud-init cloud-config cloud-final"

# Delete all the pods except openshift-multus (which have file for crio cni config)
# and lease from the etcd db so that when this bundle is use for the cluster provision, everything comes up in clean state.
if [ ${BUNDLE_TYPE} != "microshift" ]; then
    etcd_image=$(${SSH} core@${VM_IP} -- "sudo jq -r '.spec.containers[] | select(.name == \"etcd\") | .image' /etc/kubernetes/manifests/etcd-pod.yaml")
    ${SSH} core@${VM_IP} -- "sudo podman run --rm --network=host --privileged --replace --name crc-etcd --detach --entrypoint etcd -v /var/lib/etcd:/store \"${etcd_image}\" --data-dir /store"
    sleep 5
    ${SSH} core@${VM_IP} 'sudo bash -x -s' << EOF
podman exec crc-etcd etcdctl get /kubernetes.io/pods/ --prefix --keys-only | \
grep -v "^/kubernetes.io/pods/openshift-network" | \
xargs -I {} podman exec crc-etcd etcdctl del "{}"
EOF
    ${SSH} core@${VM_IP} -- "sudo podman exec crc-etcd etcdctl del --prefix /kubernetes.io/leases"
    ${SSH} core@${VM_IP} -- "sudo podman stop crc-etcd"
fi

podman_version=$(${SSH} core@${VM_IP} -- 'rpm -q --qf %{version} podman')

# Disable cloud-init network config
${SSH} core@${VM_IP} 'sudo bash -x -s' << EOF
cat << EFF > /etc/cloud/cloud.cfg.d/05_disable-network.cfg
network:
    config: disabled
EFF
EOF

# Disable cloud-init hostname update
${SSH} core@${VM_IP} -- 'sudo sed -i "s/^preserve_hostname: false$/preserve_hostname: true/" /etc/cloud/cloud.cfg'

# Cleanup cloud-init config
${SSH} core@${VM_IP} -- "sudo cloud-init clean --logs"

# Shutdown the VM
shutdown_vm ${VM_NAME}

# Download podman clients
download_podman $podman_version ${yq_ARCH}

# libvirt image generation
get_dest_dir_suffix "${OPENSHIFT_VERSION}"
destDirSuffix="${DEST_DIR_SUFFIX}"

libvirtDestDir="${destDirPrefix}_libvirt_${destDirSuffix}"
rm -fr ${libvirtDestDir} ${libvirtDestDir}.crcbundle
mkdir "$libvirtDestDir"

create_qemu_image "$libvirtDestDir"
copy_additional_files "$INSTALL_DIR" "$libvirtDestDir" "${VM_NAME}"
if [ "${SNC_GENERATE_LINUX_BUNDLE}" != "0" ]; then
    create_tarball "$libvirtDestDir"
fi

# HyperV image generation
#
# This must be done after the generation of libvirt image as it reuses some of
# the content of $libvirtDestDir
if [ "${SNC_GENERATE_WINDOWS_BUNDLE}" != "0" ]; then
    hypervDestDir="${destDirPrefix}_hyperv_${destDirSuffix}"
    rm -fr ${hypervDestDir} ${hypervDestDir}.crcbundle
    generate_hyperv_bundle "$libvirtDestDir" "$hypervDestDir"
fi

# vfkit image generation
# This must be done after the generation of libvirt image as it reuses some of
# the content of $libvirtDestDir
if [ "${SNC_GENERATE_MACOS_BUNDLE}" != "0" ]; then
    vfkitDestDir="${destDirPrefix}_vfkit_${destDirSuffix}"
    rm -fr ${vfkitDestDir} ${vfkitDestDir}.crcbundle
    generate_vfkit_bundle "$libvirtDestDir" "$vfkitDestDir"
fi
