How to check for a precondition when starting a vagrant machine? - ruby

I have a multi-machine Vagrantfile, and I would like to do a quick check when the user runs vagrant up machineB, and exit with an error message if it fails.
The specific test I have in mind is to curl some URL and verify a 200 response, but I don't think the details should matter.
The idea is to save the user the time it takes to start the machine, sync some folders and run the provision script, only to discover that a required resource is not available.
So far the best idea I have is to put this check at the beginning of the provision script, but I'd like to do it earlier.
I know I can just check at the beginning of the Vagrantfile, kind of like how it is done here, but then the check will run on every vagrant command, and I'd like it to run specifically only when trying to start machineB.

You can run your condition in the specific block for your machineB so it will run only when you call commands for machineB
You can check ARGV[0] argument from command line to make sure it is up
this will look something
Vagrant.configure("2") do |config|
config.vm.box = xxx
config.ssh.username = xxx
config.vm.define "machineA" do |db|
db.vm.hostname = xxx
p system ("curl http://www.google.fr")
and your condition here
end
config.vm.define "machineB", primary: true do |app|
app.vm.hostname = xxx
if "up".eql? ARGV[0]
p system ("curl http://www.google.fr")
and your condition here
end
end
not sure exactly what you want to end up with the curl but you could just use the net/http lib

Related

Passing the host's current user to to vagrant shell provisioner

I want to pass the current user within my Vagrantfile, but I'm not sure how to do it.
I've tried this:
config.vm.provision :shell, inline: "echo $(whoami) > /etc/profile.d/me"
But it results in 'root' being put into the file, which I assume is the vagrant host's user. I want to get the username for the host.
That's because your inline shell script runs inside the vagrant box.
You can do it like this:
Get username from host depending on platform (you can simplify this if you never expect a windows host).
#host_user = Gem.win_platform? ? "#{ENV['USERNAME']}" : "#{ENV['USER']}"
Pass the username from the host as environment variable during the provisioning and use it in an inline script.
config.vm.provision "Passing host username as env var...", type: :shell, inline: $hostUser, env: {"HOST_USER" => "#{#host_user}"}
Add this outside the ruby part, it gets then run by the code above and appends the username which got passed as environment variable to the file you specified:
$hostUser = <<-SET_HOST_USER
echo "$HOST_USER" > /etc/profile.d/me"
SET_HOST_USER

Ansible start-at-task from within Vagrant

Is there a method to using Ansible's start-at-task from within a Vagrantfile? I want to specify the exact task to start at for debugging purposes. I realize host vars will be missing, this is fine. Other similar questions don't seem to be asking exactly this.
One idea is to set an ENV_VAR, Vagrant populates that and passes it to the playbook. ie:
# export START_TASK='task-name'
# Run: "vagrant provision --provision-with resume"
config.vm.provision "resume", type: "ansible_local" do |resume|
resume.playbook = "playbooks/playbook.yml --start-at-task=ENV['START_TASK']"
end
The playbook command doesn't parse the env_var like that but I'm essentially trying to run that command. I'm basically just trying to parse that env_var and pass it to Vagrant ansible provisioner.
Note: #retry on the playbook only re-runs the entire failed playbook for that single host not just a single command so that's not a solution.
Just needed to add the following, which I couldn't find anywhere in Vagrant's documentation.
resume.start_at_task = ENV['START_AT_TASK']

Vagrant create alias for long machine name

I'm on MacOS and when I start my vagrant box (vagrant up && vagrant ssh) I see a fairly long prompt
vagrant#vagrant-ubuntu-trusty-64
How can I shorten it? I want something like this
vagrant#box
The simplest approach would be to define a logical name for the box (see the config.vm docs).
For example a minimal Vagrantfile might look like this:
Vagrant.configure("2") do |config|
config.vm.box = "ubuntu/trusty64"
config.vm.hostname = "web1"
end
And then the shell prompt will be user#hostname, like:
$ vagrant ssh
vagrant#web1:~$
This is also handy if you ever do multiple Vagrant VMs which have different roles, like "web" versus "db" for example.

Are Vagrant shell provisioner scripts idempotent or OTO?

I want to write a Vagrantfile and accompanying shell script so that the script runs/executes only the very 1st time a user executes a vagrant up for that VM. That's because this shell script will install all sorts of system services that should only happen one time.
According to the shell provisioner docs, it looks like I might be able to do something like:
Vagrant.configure("2") do |config|
config.vm.provision "shell", path: "init-services.sh"
end
However, from the docs I can't tell if init-services.sh will be executed every time a user does a vagrant up (in which case I need to write it carefully so as to be idempotent), or whether it truly only executes the script one time, when the box is first being provisioned.
And, if it does only execute the script one time, then how does Vagrant handle updates to the script (if we want to, say, add a new service to the machine)?
However, from the docs I can't tell if init-services.sh will be executed every time a user does a vagrant up (in which case I need to write it carefully so as to be idempotent), or whether it truly only executes the script one time, when the box is first being provisioned.
yes the script will be executed only at the first time the machine is spinned up during the vagrant up. There is an option is you want to run it everytime (even though its not something you want in this case)
Vagrant.configure("2") do |config|
config.vm.provision "shell", path: "init-services.sh", :run => 'always'
end
And, if it does only execute the script one time, then how does Vagrant handle updates to the script (if we want to, say, add a new service to the machine)?
There are 2 commands you can use for this:
A specific call to vagrant provision will still force the script to run wether the machine has already been initialized or not.
Calling vagrant up --provision when spining an existing VM will run the provisioning script
on this point though vagrant will not check what are the update in your script, it will just run the whole script again; if you need to run just a specific update you will need to manage this yourself in your script file.
You can read a bit more about how the provisioning work in the doc

How to tell if I am inside a Vagrant host?

Whats a bulletproof way to determine if I am running inside a vagrant machine?
Guest OS is Debian Linux, though if there are indicators per-os that would be great to have documented as well.
AFAIK, there isn't a way outside of your own customizations. One idea that comes to mind is to touch a file that you then reference from your apps. Add something like config.vm.provision "shell", inline: "touch /etc/is_vagrant_vm" to the bottom of your Vagrantfile and base your conditionals around the existence of that file.
Provisioning a file that you can check the existence of seems like the most reliable way to handle this.
Another option if you don't want to write files to the box would be to check for the existence of the vagrant user itself:
grep -q '^vagrant:' /etc/passwd && echo 'Vagrant environment: true'
This is not foolproof as others have indicated, and it is possible (although uncommon) to have a vagrant box that uses a different user to connect as.
Checking the user's existence also would not work reliably if you have machines in your environment with a user account called vagrant that are not actually vagrant boxes, but that would also be fairly uncommon.
I don't know if there is any bulletproof way for this, but one thing I often do is to config my vagrant environment's shell UI to be different from the shell UI of my host machine. This way I can tell the difference at first glance. It also helps if you want to distinguish among multiple vagrant environments.
To customize the shell UI, oh-my-zsh will come in handy.
Assuming you don't run a vagrant instance in your vagrant instance:
if which vagrant > /dev/null 2>&1; then
echo "This is most likely the host"
fi
You can use facter
facter | grep vagrant
This is how I was able to answer the question with a very small chunk of bash. The -e flag checks if the file exists. Change provision to a file path that makes sense for your build.
#!/usr/bin/env bash
user=$USER
provision=/vagrant/Vagrantfile
if [ -e $provision ]
then
echo "Vagrantfile found..."
echo "Setting $user to vagrant..."
user=vagrant
fi
# Example usage
cd /home/$user
Tested on "centos/7"
In vagrant your are in control of which IP you are running against, so you could change it to for example 192.168.0.10
Then:
curl localhost:8080/health => in local e.g spring-boot
curl 192.168.0.10:8080/health => in vagrant
NOTE: /health assumes spring-boot but you could also use your own implementation of a /health endpoint as well

Resources