# This Vagrantfile builds a Centos 7 virtual machine with Charliecloud and
# Docker installed. It is documented in the HTML docs.

# A few design decisions:
#
# 1. We use the CentOS kernel (3.10 + lots of Red Hat patches) because it's
#    easier than installing the upstream kernel and seems to work fine once
#    user namespaces are turned on. Please let us know of any problems.
#
# 2. /tmp is the CentOS default: simply a directory on the root filesystem,
#    but cleaned out on each boot. This is to avoid (a) configuring it to be a
#    tmpfs and (b) to save swap.
#
# 3. We don't try any clever sizing of the VM appliance (e.g., number of
#    vCPUs, RAM). End users should adjust these values, but we want to leave
#    reasonable defaults in case they don't, even if they have a smallish
#    host. For example, we don't want to configure the appliance to take
#    advantage of your 96-core Beast Machine and then give it to some poor end
#    user to run that 96-vCPU VM on their 4-core laptop.
#
# 4. No OpenMPI is installed. This saves build time, a little disk space, and
#    the complexity of finding the right version to match the Charliecloud
#    examples, while the cost is low: a few tests are skipped. Users who want
#    to run single-node MPI apps in the VM should launch from within ch-run.

Vagrant.require_version ">= 2.1.3"
Vagrant.configure("2") do |c|

  c.vm.box = "centos/7"         # https://app.vagrantup.com/centos/boxes/7
  c.vm.box_version = "~>1809"   # updates: https://blog.centos.org/?s=vagrant
  c.vm.box_check_update = true  # warn if base box out of date

  c.vm.hostname = "charliebox"
  c.vagrant.plugins = ['vagrant-disksize',
                       'vagrant-proxyconf',
                       'vagrant-reload',
                       'vagrant-vbguest']

  # Note: Vagrant sets up a port mapping from localhost:2222 to charliebox:22
  # automatically, so we need no repeat that here.

  # Set up proxies if appropriate.
  if ENV["HTTP_PROXY"] or ENV["HTTPS_PROXY"] or ENV["NO_PROXY"]
    if not (ENV["HTTP_PROXY"] and ENV["HTTPS_PROXY"] and ENV["NO_PROXY"])
      abort("missing proxy variable(s): HTTP_PROXY HTTPS_PROXY and/or NO_PROXY")
    end
    c.proxy.http = ENV["HTTP_PROXY"]
    c.proxy.https = ENV["HTTPS_PROXY"]
    c.proxy.no_proxy = ENV["NO_PROXY"]
    c.vm.provision "proxy", type:"shell", privileged: true, inline: <<-EOF
      echo 'Defaults env_keep+="ALL_PROXY all_proxy auto_proxy RSYNC_PROXY"' \
          >> /etc/sudoers.d/proxy
    EOF
  end

  # Configure the appliance.
  c.vm.provider "virtualbox" do |vb|
    vb.name = "charliebox"
    vb.gui = false
    vb.memory = "4096"
    vb.cpus = 4
    c.disksize.size = '96GB'  # see also provisioner "disksize" below
    vb.customize ["modifyvm", :id, "--nictype1", "virtio"]
  end

  # Install a decent user environment.
  c.vm.provision "environment", type: "shell", privileged: true,
                 inline: <<-EOF
    set -e
    cd /tmp

    # Basic stuff from standard repos.
    yum makecache fast
    yum-config-manager --setopt=deltarpm=0 --save
    yum -y upgrade
    yum -y install emacs \
                   vim \
                   wget

    # Git from IUS. This also activates EPEL.
    # From here: https://ius.io/setup 
    # Likely to be deprecated again sometime in the future
    wget https://repo.ius.io/ius-release-el7.rpm
    yum -y install epel-release
    rpm --install ius-release-el7.rpm
    yum -y install git222

    # Optional dependencies.
    yum -y install bats pigz pv python36

    # Squash dependencies.
    yum -y install squashfs-tools.x86_64
    yum -y install squashfuse.x86_64

    # Add /usr/local/{bin,sbin} to $PATH.
    echo 'export PATH=/usr/local/sbin:/usr/local/bin:$PATH' > /etc/profile.d/path.sh
  EOF

  # Expand the root filesystem to use the full resized disk. This is needed so
  # the full-scope tests can finish. Notes:
  #
  #   1. This is specific to the provisioning scheme selected by the base box.
  #      See issue #285.
  #
  #   2. We install parted from Fedora 23 because the version of parted in
  #      CentOS 7 (3.1-29) won't resize mounted partitions. Fedora 23's parted
  #      is apparently the newest whose dependencies CentOS 7 still meets.
  #
  #   3. ---pretend-input-tty is an undocumented option (note third hyphen) to
  #      convince parted to accept "yes" to the warning even without a TTY.
  #      See: https://unix.stackexchange.com/a/365657
  #
  c.vm.provision "disksize", type: "shell", privileged: true,
                 inline: <<-EOF
    set -e
    cd /tmp

    yum -y install e2fsprogs
    wget -nv https://archives.fedoraproject.org/pub/archive/fedora/linux/releases/23/Everything/x86_64/os/Packages/p/parted-3.2-11.fc23.x86_64.rpm
    rpm --upgrade parted-*.x86_64.rpm

    parted /dev/sda ---pretend-input-tty resizepart 1 yes 100%
    parted /dev/sda p
    resize2fs /dev/sda1
    df -h
  EOF

  # Configure namespaces. This needs a reboot for the kernel command line
  # update to take effect.
  #
  # Note: This could be skipped if we installed an upstream kernel (e.g., via
  # ElRepo). However, we're not aware of any advantages vs. the CentOS kernel
  # for this use case.
  c.vm.provision "namespaces", type: "shell", privileged: true,
                 inline: <<-EOF
    set -e
    echo 'user.max_user_namespaces = 32767' > /etc/sysctl.d/51-userns.conf
  EOF
  c.vm.provision :reload

  # Install Docker.
  #
  # vagrant-proxyconf for Docker doesn't seem to work, so do it manually.
  c.vm.provision "docker", type: "shell", privileged: true,
                 inline: <<-EOF
    set -e
    yum-config-manager --add-repo https://download.docker.com/linux/centos/docker-ce.repo
    yum -y install docker-ce
    if [[ $HTTP_PROXY ]]; then
      echo 'configuring Docker proxy'
      mkdir -p /etc/systemd/system/docker.service.d
      cat << EOF2 > /etc/systemd/system/docker.service.d/http-proxy.conf
[Service]
Environment="HTTP_PROXY=$HTTP_PROXY"
Environment="HTTPS_PROXY=$HTTPS_PROXY"
EOF2
    fi
    systemctl enable docker
    systemctl daemon-reload
    systemctl restart docker
    systemctl show --property=Environment docker
    docker run hello-world
  EOF

  # Install Charliecloud.
  c.vm.provision "charliecloud", type: "shell", privileged: false,
                 env: { "CH_VERSION" => ENV["CH_VERSION"] },
                 inline: <<-EOF
    set -e
    cd /usr/local/src
    sudo chmod 1777 .

    sudo tee /etc/profile.d/charliecloud.sh << 'EOF2'
export CH_TEST_TARDIR=/var/tmp/tarballs
export CH_TEST_IMGDIR=/var/tmp/images
export CH_TEST_PERMDIRS=skip
EOF2

    # Install dependencies for autotools
    sudo yum -y install autoconf automake autoconf-archive

    git clone --recursive https://github.com/hpc/charliecloud.git

    cd charliecloud
    if [[ -z $CH_VERSION ]]; then
      CH_VERSION=$(git tag --sort=-version:refname --format='%(refname:strip=2)' | head -n 1)
    fi
    echo "checking out Charliecloud: $CH_VERSION"
    git checkout $CH_VERSION
    ./autogen.sh
    ./configure
    make
    sudo make install
    which ch-run
    ch-run --version
  EOF

  # Install Buildah.
  c.vm.provision "buildah", type: "shell", privileged: false,
                 inline: <<-EOF
    set -e
    cd /usr/local/src

    # CentOS/EPEL/IUI don't have the version of shadow-utils (newuidmap and
    # newgidmap) we need for runc (used by Buildah), so install from source.
    wget -nv https://github.com/shadow-maint/shadow/releases/download/4.6/shadow-4.6.tar.xz
    tar xf shadow-4.6.tar.xz
    (cd shadow-4.6 && ./configure && sudo make install)

    sudo yum -y install btrfs-progs-devel \
                        bzip2 \
                        device-mapper-devel \
                        glib2-devel \
                        golang \
                        go-md2man \
                        gpgme-devel \
                        libassuan-devel \
                        libseccomp-devel \
                        make \
                        ostree-devel \
                        runc

    mkdir buildah
    export GOPATH=$(readlink -f buildah)
    export BUILDAH_VERSION=v1.11.2
    git clone https://github.com/containers/buildah ${GOPATH}/src/github.com/containers/buildah
    cd ${GOPATH}/src/github.com/containers/buildah
    git checkout "$BUILDAH_VERSION"
    make
    sudo make install
    # Setup buildah's system configuration files
    sudo mkdir -p /etc/containers
    # These files are already on the image, but the defaults don't work with our buildah
    sudo cp tests/policy.json /etc/containers
    sudo cp tests/registries.conf /etc/containers
  EOF

  # Install ch-grow dependencies.
  c.vm.provision "ch-grow", type: "shell", privileged: false,
                 inline: <<-EOF
    set -e
    cd /usr/local/src
    sudo yum -y install python36 python36-pip
    sudo pip3 install lark-parser
    sudo pip3 install requests
  EOF

  # Twiddle vagrant user so Charliecloud tests will pass (add to a second
  # group, and permit sudo to UID 0 and GID != 0).
  c.vm.provision "vagrant-user", type: "shell", privileged: true,
                 inline: <<-EOF
    set -e
    usermod -aG users vagrant
    echo '%vagrant ALL=(ALL:ALL) NOPASSWD: ALL' > /etc/sudoers.d/vagrant

    # Configure subuids and subgids for runc.
    sudo usermod --add-subuids 10000-65536 vagrant
    sudo usermod --add-subgids 10000-65536 vagrant
  EOF

  # Remove unneeded packages.
  c.vm.provision "cleanup", type: "shell", privileged: true,
                 inline: <<-EOF
    set -e
    package-cleanup -y --oldkernels --count=1
  EOF

  # Prepare for .ova export. In this case, end users need not know or care
  # that the VM was set up using Vagrant.
  c.vm.provision "ova", type: "shell", run: "never", privileged: true,
                 inline: <<-EOF
    set -e

    # Create a user "charlie" for the end user (see documentation).
    #
    # Strictly speaking, this is not necessary, as they could just use the
    # existing "vagrant" user. However, I eventually concluded that I'd prefer
    # to (1) keep the "charlie" user that we've been promoting for some time,
    # and (2) leave the door open for other VM build schemes in the future.
    adduser --password='*' --groups users,vagrant charlie

    # chown(2) /usr/local/src/charliecloud to charlie so end user can update,
    # rebuild, etc.
    chown -R charlie:charlie /usr/local/src/charliecloud

    # Configure subuids and subgids for runc.
    sudo usermod --add-subuids 10000-65536 charlie
    sudo usermod --add-subgids 10000-65536 charlie

    # Automatically log in "charlie" on the console, so they have a way to get
    # in if SSH isn't working.
    cd /etc/systemd/system/getty.target.wants
    rm -f getty@tty1.service
    cp /lib/systemd/system/getty@.service getty@tty1.service
    sed -ri 's|^ExecStart=.*$|ExecStart=-/sbin/agetty --autologin charlie --noclear %I|' getty@tty1.service

    # Configure SSH to allow password logins. We would prefer to keep the
    # Vagrant default of SSH keys only, but I can't figure out how to get the
    # key into the VM in a way that's easy for end users.
    sed -ri 's/^PasswordAuthentication no$/PasswordAuthentication yes/' /etc/ssh/sshd_config
    systemctl restart sshd

    # Fix /etc/shadow permissions. Not clear where they were broken, but
    # passwd(1) below fails without this.
    sudo restorecon -v /etc/shadow

    # Lock out password login for root and vagrant, because the default
    # password is well-known and we now allow password login.
    passwd -l root
    passwd -l vagrant

  EOF

  # Test Charliecloud (optional).
  #
  # Note: This will grow the image quite a bit. Don't run it before taking the
  # snapshot to be distributed to end users.
  c.vm.provision "test", type: "shell", run: "never", privileged: false,
                 inline: <<-EOF
    set -e
    if ( id -u charlie 2>/dev/null ); then  # issue #309
        user=charlie
    else
        user=vagrant
    fi
    sudo -iu $user -- sh -c "ch-test --pedantic yes -b docker all"
    sudo -iu $user -- sh -c "ch-test --pedantic yes -b buildah all"
    sudo -iu $user -- sh -c "ch-test --pedantic yes -b ch-grow all"
  EOF
end


# vi: set ft=ruby
