#!/bin/bash -ex
# vi: ts=4 noexpandtab
#
# Generate a generic Vagrant Box.
#
# Vagrant images are essentially nothing more than OVA's with extra-metadata
# and some preinstalled packages.
#
# We can't use the OVA's for Vagrant since Vagrant uses SSH to modify the
# instance. This build step creates a cloud-config ISO so that Cloud-Init
# will configure the initial user, creates meta-data that tells Vagrant how
# to interact with the cloud-init created users, and finally create the OVA.
#
# For this step, we make a deriviative of binary/boot/disk.ext4 and install
# some packages in it, convert it to a vmdk, and then assemble the vagrant
# box.

case ${SUBPROJECT:-} in
    minimized)
        echo "Skipping minimized $0 build as images won't boot with linux-kvm"
        exit 0
        ;;
    *)
        ;;
esac

cur_d=${PWD}
my_d=$(dirname $(readlink -f ${0}))

# Switch on $ARCH to determine which ID and description to use in the produced
# OVF. We have fancy Ubuntu-specific IDs in the OVF specification, we might as
# well use them.
case $ARCH in
        amd64)
           ovf_id=94
           ovf_os_type="ubuntu64Guest"
           ovf_desc_bits=64 ;;
        *)
           echo "Vagrant images are not supported for $ARCH yet."
           exit 0;;
esac

. config/functions

# Lets be safe about this
box_d=$(mktemp -d)
seed_d=$(mktemp -d)
mount_d=$(mktemp -d)

create_derivative "disk" "vagrant" #sets ${derivative_img}
mount_disk_image ${derivative_img} ${mount_d}

cleanup_vagrant() {
    if [ -d "$mount_d" ]; then
        umount_disk_image "$mount_d"
    fi
    rm -rf ${box_d} ${seed_d} ${mount_d} ${derivative_img}
}
trap cleanup_vagrant EXIT

chroot ${mount_d} apt-get update
# virtualbox-guest-utils Recommends: virtualbox-guest-x11, which we want to
# avoid pulling into a cloud image.
chroot ${mount_d} apt-get install --no-install-recommends -y virtualbox-guest-utils
chroot ${mount_d} apt-get clean

# Create and setup users inside the image.
# Vagrant users expect a "vagrant" user with a "vagrant" username.
# See https://www.vagrantup.com/docs/boxes/base.html
# Note: We decided NOT to allow root login with a default password.
chroot ${mount_d} adduser vagrant --disabled-password --gecos ""
echo "vagrant:vagrant" | chroot ${mount_d} chpasswd

# The vagrant user should have passwordless sudo.
cat << EOF > ${mount_d}/etc/sudoers.d/vagrant
vagrant ALL=(ALL) NOPASSWD:ALL
EOF

# Add the insecure vagrant pubkey to the vagrant user, as is expected by the
# vagrant ecosystem (https://www.vagrantup.com/docs/boxes/base.html)
chroot ${mount_d} chmod 0440 /etc/sudoers.d/vagrant
chroot ${mount_d} mkdir -p /home/vagrant/.ssh
cat << EOF > ${mount_d}/home/vagrant/.ssh/authorized_keys
ssh-rsa AAAAB3NzaC1yc2EAAAABIwAAAQEA6NF8iallvQVp22WDkTkyrtvp9eWW6A8YVr+kz4TjGYe7gHzIw+niNltGEFHzD8+v1I2YJ6oXevct1YeS0o9HZyN1Q9qgCgzUFtdOKLv6IedplqoPkcmF0aYet2PkEDo3MlTBckFXPITAMzF8dJSIFo9D8HfdOV0IAdx4O7PtixWKn5y2hMNG0zQPyUecp4pzC6kivAIhyfHilFR61RGL+GPXQ2MWZWFYbAGjyiYJnAmCP3NOTd0jMZEnDkbUvxhMmBYSdETk1rRgm+R4LOzFUGaHqHDLKLX+FIPKcF96hrucXzcWyLbIbEgE98OHlnVYCzRdK8jlqm8tehUc9c9WhQ== vagrant insecure public key
EOF
chroot ${mount_d} chown -R vagrant:vagrant /home/vagrant/.ssh
chroot ${mount_d} chmod 700 /home/vagrant/.ssh

umount_disk_image "$mount_d"
rmdir "$mount_d"

# Used to identify bits
suite=$(chroot chroot lsb_release -c -s)
version=$(chroot chroot lsb_release --release --short)
distro=$(chroot chroot lsb_release --id --short | tr [:upper:] [:lower:])

# Get the VMDK in place
prefix="${distro}-${suite}-${version}-cloudimg"
vmdk_f="${box_d}/${prefix}.vmdk"
create_vmdk ${derivative_img} ${vmdk_f}

####################################
# Create the ConfigDrive
# This is a cloud-init piece that instructs cloud-init to configure
# a default user at first boot.

cdrom_vmdk_f="${box_d}/${prefix}-configdrive.vmdk"

# Create the user-data. This is totally insecure, but so is Vagrant. To
# mitigate this insecurity, the vagrant instance is not accessible
# except via local host.
cat > ${seed_d}/user-data <<END
#cloud-config
manage_etc_hosts: localhost
END

# Create the fake meta-data
cat > ${seed_d}/meta-data <<END
instance-id: iid-$(openssl rand -hex 8)
local-hostname: ubuntu-${suite}
END

# Pad the cdrom, otherwise the VMDK will be invalid
dd if=/dev/zero of=${seed_d}/bloat_file bs=1M count=10

# Create the ISO
genisoimage \
    -output ${seed_d}/seed.iso \
    -volid cidata \
    -joliet -rock \
    -input-charset utf-8 \
    ${seed_d}/user-data \
    ${seed_d}/meta-data

# Make a VMDK out of the seed file.
create_vmdk ${seed_d}/seed.iso ${cdrom_vmdk_f} 10

### END Create ConfigDrive
##########################

##########################
# VAGRANT meta-data

# Create the Vagrant file. This file is used by Vagrant to define how
# Vagrant uses Virtualbox and how Vagrant interacts with the host.
macaddr="02$(openssl rand -hex 5 | tr [:lower:] [:upper:])"
cat > ${box_d}/Vagrantfile <<EOF
# Front load the includes
include_vagrantfile = File.expand_path("../include/_Vagrantfile", __FILE__)
load include_vagrantfile if File.exist?(include_vagrantfile)

Vagrant.configure("2") do |config|
  config.vm.base_mac = "${macaddr}"

  config.vm.provider "virtualbox" do |vb|
     vb.customize [ "modifyvm", :id, "--uart1", "0x3F8", "4" ]
# Creating a console log file is not an expected behavior for vagrant boxes. LP #1777827
#     vb.customize [ "modifyvm", :id, "--uartmode1", "file", File.join(Dir.pwd, "${prefix}-console.log") ]
  end
end
EOF

# Tag it as a Virtualbox Vagrant
cat > ${box_d}/metadata.json <<EOF
{
  "provider": "virtualbox"
}
EOF

# END
##########################

##########################
# Create the actual box

# Get information about the disks for the OVF
vmdk_size=$(du -b "${vmdk_f}" | cut -f1)
vmdk_capacity=$(qemu-img info "${vmdk_f}" | awk '-F[\( ]' '$1 ~ /virtual/ && $NF ~ /bytes.*/ {print$(NF-1)}')
vmdk_sha256=$(sha256sum ${vmdk_f} | cut -d' ' -f1)

cdrom_size=$(du -b "${cdrom_vmdk_f}" | cut -f1)
cdrom_capacity=$(qemu-img info "${cdrom_vmdk_f}" | awk '-F[\( ]' '$1 ~ /virtual/ && $NF ~ /bytes.*/ {print$(NF-1)}')
cdrom_sha256=$(sha256sum ${cdrom_vmdk_f} | cut -d' ' -f1)

# Populate the OVF template
ovf="${box_d}/box.ovf"
cp ${my_d}/ovf/ubuntu-ova-v1-cloudcfg-vmdk.tmpl ${ovf}
serial_stamp=$(date +%Y%m%d)
sed -i "${ovf}" \
    -e "s/@@NAME@@/${prefix}-${serial_stamp}/g" \
    -e "s/@@FILENAME1@@/${vmdk_f##*/}/g" \
    -e "s/@@VMDK_FILE_SIZE@@/${vmdk_size}/g" \
    -e "s/@@VMDK_CAPACITY@@/${vmdk_capacity}/g" \
    -e "s/@@FILENAME2@@/${cdrom_vmdk_f##*/}/g" \
    -e "s/@@VMDK_FILE_SIZE2@@/${cdrom_size}/g" \
    -e "s/@@VMDK_CAPACITY2@@/${cdrom_capacity}/g" \
    -e "s/@@NUM_CPUS@@/2/g" \
    -e "s/@@VERSION@@/${version}/g" \
    -e "s/@@DATE@@/${serial_stamp}/g" \
    -e "s/@@MEM_SIZE@@/1024/g" \
    -e "s/@@OVF_ID@@/${ovf_id}/g" \
    -e "s/@@OVF_OS_TYPE@@/${ovf_os_type}/g" \
    -e "s/@@OVF_DESC_BITS@@/${ovf_desc_bits}/g"

ovf_sha256=$(sha256sum ${ovf} | cut -d' ' -f1)

# Generate the manifest
manifest="${box_d}/${prefix}.mf"
cat > "${manifest}" <<EOF
SHA256(${vmdk_f##*/})= ${vmdk_sha256}
SHA256(${cdrom_vmdk_f##*/})= ${cdrom_sha256}
SHA256(${ovf##*/})= ${ovf_sha256}
EOF

# Now create the box
echo "Creating OVA with the following attributes:"
cat <<EOM
OVA information:
    Name:           ${prefix}
    Size:           ${vmdk_size}
    VMDK Name:      ${vmdk_f##*/}
    VMDK Capacity:  ${vmdk_capacity}
    VMDK SHA256:    ${vmdk_sha256}
    CDROM Name:     ${cdrom_vmdk_f##*/}
    CDROM Capacity: ${cdrom_capacity}
    CDROM SHA256:   ${cdrom_sha256}
EOM

tar -C ${box_d} \
    -cf ${cur_d}/livecd.ubuntu-cpc.vagrant.box \
    box.ovf \
    Vagrantfile \
    metadata.json \
    ${prefix}.mf \
    ${vmdk_f##*/} \
    ${cdrom_vmdk_f##*/}