Creating The Perfect Software Development Environment: Day 11

Hypervisor Drivers

Honestly, this is a topic I was hoping I wouldn't have to cover but it looks like I do.

"Work to add features in VirtualBox Guest Additions to the Linux kernel is underway by kernel developers. The first fruits of their labor will appear in Linux 4.16.

These additional features are licensed under the GPL and sit in the kernel tree. That means no additional proprietary code or binary blobs are loaded on your system.

Why does this matter?

Well, it means the next time you boot a Linux distribution with the Linux 4.16 (or later) kernel, VirtualBox Guest Additions will be already installed. You won’t need to enable, add, install or tweak anything (in theory).

Basic system integration features like seamless mode and copy and paste between guest and host will “just work”. Devs even plan to support shared folders out of the box in a future kernel release.

This is a huge win; it means more people will be able to cautiously sample Linux distros and get a better first impression of them. It will also make developers’ lives easier by allowing them to quickly test, deploy and iterate between multiple VMs without jumping through hoops for each and every one."

Unfortunately, after experimenting with Bionic Beaver in VirtualBox, I've come to the conclusion that support isn't quite there yet. Besides, what do you do if you are using a hypervisor other than VirtualBox, such as VMWare?

VirtualBox Guest Additions

When I'm developing, I run my VM in full screen mode and I often forget that I'm in a virtualized environment. The desktop experience is no different than if I was developing on a real machine. This only works, however, if you have Guest Additions installed. In short, not having Guest Additions installed is a deal breaker for me.

Installing Guest Additions

Packer, if you tell it to, will give you access to the Guest Additions files as the box is built. You just have to add the logic to mount the ISO and execute the necessary commands.

Make the Guest Additions ISO Available


    "builders": [{
        "type"        : "virtualbox-ovf",
        "source_path" : "{{user `virtualbox_appliance`}}",
        "ssh_username": "{{user `ssh_name`}}",
        "ssh_password": "{{user `ssh_pass`}}",

        "boot_wait"            : "30s",
        "format"               : "ova",
        "guest_additions_mode" : "upload",
        "guest_additions_path" : "VBoxGuestAdditions_{{.Version}}.iso",
        "headless"             : false,
        "keep_registered"      : false,
        "shutdown_command"     : "sudo shutdown --poweroff now",
        "shutdown_timeout"     : "2m",
        "skip_export"          : false,
        "output_directory"     : "output-virtualbox-ovf",
        "vm_name"              : "packer-bionic-xubuntu"
    },
           

What we've done here is to tell Packer to make the ISO available to us in the home directory.

Install The Guest Additions


        {
            "type"            : "shell",
            "environment_vars": ["DEBIAN_FRONTEND=noninteractive"],
            "scripts"         : ["scripts/install-hypervisor-tools.sh","scripts/prepare-for-compression.sh"]
        }
           

Here, we are introducing a new script into the build pipeline that will manipulate the ISO and install the tools.

Installation Bash Script


#!/usr/bin/env bash

echo "PACKER_BUILD_NAME is ${PACKER_BUILD_NAME}"
echo "PACKER_BUILDER_TYPE is ${PACKER_BUILDER_TYPE}"

if [ "${PACKER_BUILDER_TYPE}" == "vmware-vmx" ]
then
    echo "Installing VMWare Tools"
    ISO=/home/vagrant/linux.iso
    sudo mount -o loop ${ISO} /mnt
    ls -alh /mnt
    cp /mnt/VMwareTools-10.2.5-8068393.tar.gz /tmp
    cd /tmp && tar zxvf VMwareTools-10.2.5-8068393.tar.gz && cd vmware-tools-distrib && sudo ./vmware-install.pl -d default
    sudo umount /mnt
    sudo rm -rf /home/vagrant/${ISO}
    until sudo apt-get --yes update; do echo "Waiting for apt lock"; sleep 5; done
    sudo apt-get update
    until sudo apt-get --yes update; do echo "Waiting for apt lock"; sleep 5; done
    sudo apt-get --yes install open-vm-tools open-vm-tools-desktop
else
    echo "Installing VirtualBox Guest Additions"
    until sudo apt-get --yes update; do echo "Waiting for apt lock"; sleep 5; done
    sudo apt-get update
    until sudo apt-get --yes update; do echo "Waiting for apt lock"; sleep 5; done
    sudo apt-get --yes install gcc make perl

    VBOX_VERSION=$(cat /home/vagrant/.vbox_version)
    VBOX_ISO=VBoxGuestAdditions_$VBOX_VERSION.iso
    sudo mount -o loop ${VBOX_ISO} /mnt
    sudo /mnt/VBoxLinuxAdditions.run
    sudo umount /mnt
    sudo rm -rf /home/vagrant/${VBOX_ISO}
fi
          

In the script, you'll notice that we run different commands based on the value of the PACKER_BUILDER_TYPE. This is because I have Packer build boxes for both VirtualBox and VMWare at the same time. Most people don't do that so your script will be simpler. The basic steps are:

  1. install necessary packages needed to install the Guest Additions
  2. mount the ISO
  3. run the script
  4. clean up

Conclusion

That is pretty much it. The trick is to learn how to run the installation tools in unattended mode. Be aware that as new versions of the tools come out, sometimes their dependencies change and you have to install different packages. The best way to troubleshoot that is to spin up a throwaway VM and install the tools by hand, noting what is broken and/or missing.

Full Packer File


{
    "description": "Builds a Xubuntu 18.04 desktop box with various software development tools installed",
    "min_packer_version": "1.2.3",

    "variables": {
        "ssh_name"            : "vagrant",
        "ssh_pass"            : "vagrant",
        "virtualbox_appliance": "bionic-beaver.ova",
        "vmware_appliance"    : "bionic-beaver.vmx",
        "comment"             : "Ubuntu 18.04 Bionic Beaver with VirtualBox and VMWare support.  See [README](https://github.com/kurron/jvm-development-environment) for launch instructions.",
        "cloud_user"          : "{{env `VAGRANT_CLOUD_ACCOUNT`}}",
        "cloud_token"         : "{{env `VAGRANT_CLOUD_TOKEN`}}"
    },

    "builders": [{
        "type"        : "virtualbox-ovf",
        "source_path" : "{{user `virtualbox_appliance`}}",
        "ssh_username": "{{user `ssh_name`}}",
        "ssh_password": "{{user `ssh_pass`}}",

        "boot_wait"            : "30s",
        "format"               : "ova",
        "guest_additions_mode" : "upload",
        "guest_additions_path" : "VBoxGuestAdditions_{{.Version}}.iso",
        "headless"             : false,
        "keep_registered"      : false,
        "shutdown_command"     : "sudo shutdown --poweroff now",
        "shutdown_timeout"     : "2m",
        "skip_export"          : false,
        "output_directory"     : "output-virtualbox-ovf",
        "vm_name"              : "packer-bionic-xubuntu"
    },
    {
        "type"        : "vmware-vmx",
        "source_path" : "{{user `vmware_appliance`}}",
        "ssh_username": "{{user `ssh_name`}}",
        "ssh_password": "{{user `ssh_pass`}}",

        "boot_wait"            : "30s",
        "disable_vnc"          : true,
        "headless"             : false,
        "output_directory"     : "output-vmware-vmx",
        "shutdown_command"     : "sudo shutdown --poweroff now",
        "shutdown_timeout"     : "2m",
        "skip_compaction"      : false,
        "tools_upload_flavor"  : "linux",
        "vm_name"              : "packer-bionic-xubuntu"
    }],
    "provisioners": [
        {
            "type"            : "shell",
            "environment_vars": ["DEBIAN_FRONTEND=noninteractive"],
            "script"          : "scripts/install-ansible.sh"
        },
        {
            "type"            : "ansible-local",
            "playbook_file"   : "ansible/playbook.yml",
            "galaxy_file"     : "ansible/requirements.yml",
            "extra_arguments" : ["--verbose"]
        },
        {
            "type"            : "shell",
            "environment_vars": ["DEBIAN_FRONTEND=noninteractive"],
            "scripts"         : ["scripts/install-hypervisor-tools.sh","scripts/prepare-for-compression.sh"]
        }
    ],
    "post-processors": [
        [
            {
                "compression_level"  : 9,
                "keep_input_artifact": false,
                "output"             : "vagrant/xubuntu-bionic-{{.Provider}}.box",
                "type"               : "vagrant",
                "vagrantfile_template": "vagrantfile.template"
            },
            {
                 "access_token"       : "{{user `cloud_token`}}",
                 "box_tag"            : "{{user `cloud_user`}}/bionic-xubuntu",
                 "type"               : "vagrant-cloud",
                 "version"            : "{{isotime \"2006.01.0215\"}}",
                 "version_description": "{{user `comment`}}"
            }
        ]
    ]
}
            

Full scripts/install-hypervisor-tools.sh File


#!/usr/bin/env bash

echo "PACKER_BUILD_NAME is ${PACKER_BUILD_NAME}"
echo "PACKER_BUILDER_TYPE is ${PACKER_BUILDER_TYPE}"
echo "PACKER_HTTP_ADDR is ${PACKER_HTTP_ADDR}"

if [ "${PACKER_BUILDER_TYPE}" == "vmware-vmx" ]
then
    echo "Installing VMWare Tools"
    ISO=/home/vagrant/linux.iso
    sudo mount -o loop ${ISO} /mnt
    ls -alh /mnt
    cp /mnt/VMwareTools-10.2.5-8068393.tar.gz /tmp
    cd /tmp && tar zxvf VMwareTools-10.2.5-8068393.tar.gz && cd vmware-tools-distrib && sudo ./vmware-install.pl -d default
    sudo umount /mnt
    sudo rm -rf /home/vagrant/${ISO}
    until sudo apt-get --yes update; do echo "Waiting for apt lock"; sleep 5; done
    sudo apt-get update
    until sudo apt-get --yes update; do echo "Waiting for apt lock"; sleep 5; done
    sudo apt-get --yes install open-vm-tools open-vm-tools-desktop
else
    echo "Installing VirtualBox Guest Additions"
    until sudo apt-get --yes update; do echo "Waiting for apt lock"; sleep 5; done
    sudo apt-get update
    until sudo apt-get --yes update; do echo "Waiting for apt lock"; sleep 5; done
    sudo apt-get --yes install gcc make perl

    VBOX_VERSION=$(cat /home/vagrant/.vbox_version)
    VBOX_ISO=VBoxGuestAdditions_$VBOX_VERSION.iso
    sudo mount -o loop ${VBOX_ISO} /mnt
    sudo /mnt/VBoxLinuxAdditions.run
    sudo umount /mnt
    sudo rm -rf /home/vagrant/${VBOX_ISO}
fi