Vagrant/VirtualBox VM provisioning: rbenv installs

2019-03-09 12:25发布

问题:

I am using Vagrant + VirtualBox to set up a virtual machine for my Rails app. I am working on cleaning up a .sh provisioning script that is referenced in Vagrantfile like so:

config.vm.provision "shell", path: "script/provision-script.sh" 

The provision script does a number of things, but towards the end it is supposed to install rbenv Ruby versioning and then use rbenv to install Ruby 2.2.1. That part of the provision script looks like this:

echo "setting up rbenv"
# execute the remaining commands as vagrant user, instead of root
sudo -H -u vagrant bash -c "git clone https://github.com/sstephenson/rbenv.git ~vagrant/.rbenv"
sudo -H -u vagrant bash -c "git clone https://github.com/sstephenson/ruby-build.git ~vagrant/.rbenv/plugins/ruby-build"
sudo -H -u vagrant bash -c "git clone https://github.com/sstephenson/rbenv-gem-rehash.git ~vagrant/.rbenv/plugins/rbenv-gem-rehash"
echo "setting up rbenv environment in bash"
echo 'export PATH="$HOME/.rbenv/bin:$PATH"' >> ~vagrant/.bashrc
echo 'eval "$(rbenv init -)"' >> ~vagrant/.bashrc

# start new vagrant shell so rbenv will work
echo "building ruby"
su vagrant
rbenv install 2.2.1 && rbenv global 2.2.1 && rbenv rehash && cd /path/to/my/app && gem install bundler rake && rbenv rehash && bundle && rbenv rehash

Everything up to the rbenv install... part works correctly. Installing ruby fails with the following error:

==> default: setting up rbenv
==> default: Cloning into '/home/vagrant/.rbenv'...
==> default: Cloning into '/home/vagrant/.rbenv/plugins/ruby-build'...
==> default: Cloning into '/home/vagrant/.rbenv/plugins/rbenv-gem-rehash'...
==> default: setting up rbenv environment in bash
==> default: building ruby
==> default: /tmp/vagrant-shell: line 73: rbenv: command not found

The script then finishes. I can open the vm with vagrant ssh and then successfully run rbenv install 2.2.1, so I'm guessing that during provisioning a new vagrant shell is not actually being started. I was under the impression that this should happen with su vagrant right before rbenv install 2.2.1.

What can I do to make sure that a new shell is initialized during this provisioning and that the rbenv command will work?

回答1:

I had similar problem because I was trying to install rbenv and the vagrant provisioning was giving to me the error:

==> default: /tmp/vagrant-shell: line 10: rbenv: command not found

First of all is very important to understand that vagrant provisioning script is running in sudo mode. So, when into the script we refer to ~/ path, we are refering to /root/ path and not to /home/vagrant/ path. The problem is that I was installing rbenv for root user and after trying to call rbenv command from vagrant user and of course it didn't worked!

So what I did is specify to vagrant to run the provisioner in NOT sudo user adding privileged: false:

config.vm.provision :shell, privileged: false, inline: $script

Then into my script I was considering that everything is called from vagrant user. Here @Casper answer helped me a lot, because it works only specifing: sudo -H -u vagrant bash -i -c '......'

Since you just updated .bashrc with a new path and other settings, you will want to run "sudo bash" with the -i option. This will force bash to simulate an interactive login shell, and therefore read .bashrc and load the correct path for rbenv.

Below my final Vagrantfile.

# -*- mode: ruby -*-
# vi: set ft=ruby :

$script = <<SCRIPT
  sudo apt-get -y update
  sudo apt-get -y install curl git-core python-software-properties ruby-dev libpq-dev build-essential nginx libsqlite3-0 libsqlite3-dev libxml2 libxml2-dev libxslt1-dev nodejs postgresql postgresql-contrib imagemagick

  git clone https://github.com/sstephenson/rbenv.git ~/.rbenv
  echo 'export PATH="$HOME/.rbenv/bin:$PATH"' >> ~/.bashrc
  echo 'eval "$(rbenv init -)"'               >> ~/.bashrc
  source ~/.bashrc

  git clone https://github.com/sstephenson/ruby-build.git ~/.rbenv/plugins/ruby-build
  sudo -H -u vagrant bash -i -c 'rbenv install 2.1.3'
  sudo -H -u vagrant bash -i -c 'rbenv rehash'
  sudo -H -u vagrant bash -i -c 'rbenv global 2.1.3'
  sudo -H -u vagrant bash -i -c 'gem install bundler --no-ri --no-rdoc'
  sudo -H -u vagrant bash -i -c 'rbenv rehash'
  sudo -u postgres createdb --locale en_US.utf8 --encoding UTF8 --template template0 development
  echo "ALTER USER postgres WITH PASSWORD \'develop\';" | sudo -u postgres psql
SCRIPT

VAGRANTFILE_API_VERSION = "2"

Vagrant.configure(VAGRANTFILE_API_VERSION) do |config|
  config.vm.box = "hashicorp/precise64"
  config.vm.network "forwarded_port", guest: 3000, host: 3000

  # config.vm.provider :virtualbox do |vb|
  #   vb.customize ["modifyvm", :id, "--memory", "1024"]
  # end

  config.vm.provision :shell, privileged: false, inline: $script
end

Hoping it will be helpful to someone else.



回答2:

I'm surprised the provisioning script exits, as running su vagrant should in theory hang the script at that point (you're running the command su which does not normally exit by itself).

The problem is you cannot change the user that is running a shell script "on the fly" by running su.
Your only option is to use sudo.

Since you just updated .bashrc with a new path and other settings, you will want to run "sudo bash" with the -i option. This will force bash to simulate an interactive login shell, and therefore read .bashrc and load the correct path for rbenv.

So, something like this should hopefully work:

echo "building ruby"
sudo -H -u vagrant bash -i -c 'rbenv install 2.2.1 ...'