Can I load a Vagrantfile that loads another Vagrantfile? - vagrant

I'm trying to set up an inheritance structure in my forest of Vagrantfiles. Basically I wanted something like this:
/base
Vagrantfile
/testvm
Vagrantfile
/nginx-test
Vagrantfile
The testvm/Vagrantfile would load the base/Vagrantfile and the nginx-test/Vagrantfile would load the testvm/Vagrantfile. In my test case, the base Vagrantfile specifies one box type (eg bento/ubuntu-18.04) and sets 1GB of memory for the VM and some other stuff. The testvm Vagrantfile loads that and overrides the box to bento/ubuntu-22.04 and sets a different hostname. The nginx-test Vagrantfile loads that last file and sets its own hostname plus adds another provisioning block to install nginx.
I've tried this but I get this SystemStackError: stack level too deep error:
$ vagrant up
Vagrant failed to initialize at a very early stage:
There was an error loading a Vagrantfile. The file being loaded
and the error message are shown below. This is usually caused by
a syntax error.
Path: /Users/donseiler/vagrant_test/testvm/nginx-test/Vagrantfile
Line number: 5
Message: SystemStackError: stack level too deep
I've been searching but haven't found anyone attempting multiple layers of loading yet. I can run vagrant up just fine from the base and testvm directories to bring those machines up. I can't run any vagrant commands from the nginx-test directory (not even vagrant status, etc)
Here are the file contents:
/base/Vagrantfile:
VAGRANTFILE_API_VERSION = "2"
NAME = "basevm"
Vagrant.configure(VAGRANTFILE_API_VERSION) do |config|
# Default to bento bionic box
config.vm.box = "bento/ubuntu-18.04"
config.vm.hostname = NAME
# Set default virtualbox provider settings
config.vm.provider :virtualbox do |vb|
vb.name = NAME
vb.memory = "1024"
vb.gui = false
end
config.vm.provision "shell", inline: "DEBIAN_FRONTEND=noninteractive apt-get update"
end
/base/testvm/Vagrantfile:
# Load parent Vagrantfile
load "../Vagrantfile"
# define hostname
NAME = "testvm"
Vagrant.configure(VAGRANTFILE_API_VERSION) do |config|
# Change to jammy
config.vm.box = "bento/ubuntu-22.04"
config.vm.provision "shell", inline: <<-SHELL
export DEBIAN_FRONTEND=noninteractive
apt-get -y install curl
SHELL
end
/base/testvm/nginx-test/Vagrantfile:
# Load parent Vagrantfile
load "../Vagrantfile"
# define hostname
NAME = "nginx-test"
Vagrant.configure(VAGRANTFILE_API_VERSION) do |config|
config.vm.provision "shell", inline: <<-SHELL
export DEBIAN_FRONTEND=noninteractive
apt-get -y install nginx
SHELL
end
UPDATE: It looks like I can do multiple loading just fine as long as I use absolute file paths to the Vagrantfiles (or only use relative paths in the pwd's Vagrantfile, or if all Vagrantfiles are in the same directory). The relative path apparently is relative to the pwd, so when ../Vagrantfile also tries to load ../Vagrantfile, it does so from pwd and not from the parent dir. Then it becomes somewhat of an infinite loop as the parent dir's Vagrantfile keeps trying to load itself from the child dir until vagrant short-circuits itself.
Basically, it loads Vagrantfiles from wherever but the commands in them are executed from the pwd, so relative paths are always relative to the pwd, NOT relative to where the Vagrantfile lives.
Not being able to use relative paths makes things a little less flexible but at least it's a working alternative.

Related

Create Vagrant VM with an alias

I want to create a couple of Vagrant VM's. Most of them could be Ubuntu 16.04. But I want the VM's to be named as webserver01, webserver02 and webserver03. Basically I want the Vagrantfile for each VM to be inside the directory name I provided above.
Command vagrant init ubuntu/xenial64 might create a new VM but how do I make sure I create three webservers{1..3} as above and run vagrant up command from inside that directory?
I want all the VM's to be in a specific directory so I just open CMD inside that directory and run vagrant up from inside them.
you can spawn as many VMs with the help of loops.
https://www.vagrantup.com/docs/vagrantfile/tips.
Vagrant.configure("2") do |config|
config.vm.box = "ubuntu/trusty64"
(1..3).each do |i|
config.vm.define "node-#{i}" do |node|
node.vm.provision "shell",
inline: "echo hello from node #{i}"
end
end
end
you can access any machine from the cli e.g. from the given example in the link you can control node-1 like
vagrant up node-1 && vagrant ssh node-1

How to use aliases in Vagrant

I'm trying to create aliases that I can use in Vagrant any time I run the VM. I've found several sources on the web about it, but can't get it working. I tried making a .bash_profile in my synced folder, but that didn't work. I noticed if I run the command alias name="command" this will work, but only for the current session. Anyone know how to do this? I'm using macOS. Thanks for your help!
Here is my Vagrantfile:
# -*- mode: ruby -*-
# vi: set ft=ruby :
unless Vagrant.has_plugin?("vagrant-vbguest")
warn "\nWARNING: The vagrant-vbguest plugin should be installed or your shared folders might not mount properly!"
warn "You can do this by running the command 'vagrant plugin install vagrant-vbguest'.\n\n"
end
VAGRANTFILE_API_VERSION = "2"
Vagrant.configure(VAGRANTFILE_API_VERSION) do |config|
config.vm.box = "pype_vm"
config.vm.box_url = "https://.../pype_vm.json"
config.vm.network "private_network", ip: ""
config.vm.boot_timeout = 600
config.vm.provider "virtualbox" do |v|
# This forces VirtualBox to use the host's DNS resolver instead of
# VirtualBox's
v.customize ["modifyvm", :id, "", "on"]
# This enables the PAE/NX option, which resolved at least one user's
# issues with the VM hanging on boot
v.customize ["modifyvm", :id, "--pae", "on"]
# The RHEL VM was created with 2GB of memory to facilitate provisioning,
# but this is causing issues with certain workstations. This reduces
# the amount of memory allocated to the VM but should not impact development
# performance. The number is in MB and can be increased if desired.
v.memory = 1024
end
# Share an additional folder to the guest VM.
config.vm.synced_folder File.dirname(__FILE__), "/pype"
end
The details depend on the specific of the guest being run, but some notes:
Assuming the default user account is active for vagrant ssh, ensure that any dotfiles you wish to override are copied to /home/vagrant.
If overriding .bashrc, ensure that the remote shell is started with the interactive flag (if this is true, echo $- will include i).
If overriding .bash_profile, ensure that the remote shell is started as a login shell (if this is true, echo $- will include l).

Multi-machine Vagrant project not provisioning as per docs

I’ve trying to set up a multi-machine Vagrant project. According to the docs (https://www.vagrantup.com/docs/multi-machine/), provisioning is “outside in”, meaning any top-level provisioning scripts are executed before provisioning scripts in individual machine blocks.
The project contains a Laravel project, and a Symfony project. My Vagrantfile looks like this:
require "json"
require "yaml"
confDir = $confDir ||= File.expand_path("vendor/laravel/homestead", File.dirname(__FILE__))
homesteadYamlPath = "web/Homestead.yaml"
homesteadJsonPath = "web/Homestead.json"
afterScriptPath = "web/after.sh"
aliasesPath = "web/aliases"
require File.expand_path(confDir + "/scripts/homestead.rb")
Vagrant.configure(2) do |config|
config.vm.provision "shell", path: "init.sh"
config.vm.define "web" do |web|
web.ssh.forward_x11 = true
if File.exists? aliasesPath then
web.vm.provision "file", source: aliasesPath, destination: "~/.bash_aliases"
end
if File.exists? homesteadYamlPath then
Homestead.configure(web, YAML::load(File.read(homesteadYamlPath)))
elsif File.exists? homesteadJsonPath then
Homestead.configure(web, JSON.parse(File.read(homesteadJsonPath)))
end
if File.exists? afterScriptPath then
web.vm.provision "shell", path: afterScriptPath
end
end
config.vm.define "api" do |api|
api.vm.box = "ubuntu/trusty64"
api.vm.provider :virtualbox do |vb|
vb.customize ["modifyvm", :id, "--memory", "2048"]
end
api.vm.network "private_network", ip: "10.1.1.34"
api.vm.network "forwarded_port", guest: 80, host: 8001
api.vm.network "forwarded_port", guest: 3306, host: 33061
api.vm.network "forwarded_port", guest: 9200, host: 9201
api.vm.synced_folder "api", "/var/www/api"
api.vm.provision "shell", path: "api/provision.sh"
end
end
I have a block (web) for the Laravel project, where I’ve copied the contents of the Homestead-based Vagrantfile, and an api block that uses the “standard” Vagrant configuration.
To bootstrap the projects, I created a simple shell script (init.sh) that simply clones the Git repositories into git-ignored directories. Given the documentation says configuration works outside-in, I’d therefore expect that script to run, and then the machine-specific blocks, but this doesn’t seem to be happening. Instead, on vagrant up, I receive the following error:
There are errors in the configuration of this machine. Please fix the following errors and try again:
vm:
* A box must be specified.
It seems it’s still trying to provision the individual machines, before running the shell script. I know the shell script isn’t getting called as I added an echo statement to it. Instead, the terminal just outputs the following:
Bringing machine 'web' up with 'virtualbox' provider...
Bringing machine 'api' up with 'virtualbox' provider...
So how can I get Vagrant to run my shell script first? I think it’s failing because the web group is checking if my web/Homestead.yaml file exists and if so, use the values in there for configuring (including the box name), but as my shell script hasn’t been ran and hasn’t cloned the repository that file does not exist, so there is no box specified, which Vagrant complains about.
The issue is that you do not define a box for the web machine. You need to either define the box in the outer space like
config.vm.box = "ubuntu/trusty64"
if you plan to use the same box/OS for both machines or define in the web scope
web.vm.box = "another box"
EDIT
Using the provision property will run the script in the VM, which is not what you want here, as you want the script to run on your host. (and because it runs in the VM, it needs the VM to be booted first)
Vagrantfile is just a simple ruby script, so you could add your script or even an execution to it (from ruby call), a potential issue I could see is that you cannot guarantee the execution and specially that the execution of your init script will be complete before vagrant does it things on the VM.
A possibility is to use the vagrant trigger plugin and execute your shell script before the up event
config.trigger.before :up do
info "Dumping the database before destroying the VM..."
run "init.sh"
end
Running it this way, vagrant will wait for the script to be executed before it runs its part of the up command.
You would need to do some check in your script to make sure it runs only when needed, otherwise, it will run everytime you start the machine (invoking vagrant up), e.g. you could make a check on the presence of the yaml file

When i do a vagrant provisioning, can i ignore some provision codes from Vagrantfile on vagrant reload?

In my Vagrantfile, I have two shell provisions: one is for installing system dependencies for my project, and another is for starting up nginx server.
So what I wanted to have is when I vagrant reload --provision, can I ignore the provision for installing the system dependencies, and just start up the nginx server instead?
Sample code:
VAGRANTFILE_API_VERSION = '2'
Vagrant.configure(VAGRANTFILE_API_VERSION) do |config|
...
# Ignore this line on VM reload
config.vm.provision 'shell', path: 'provision/install.sh'
# Execute this one only on VM reload
config.vm.provision 'shell', path: 'provision/start_nginx.sh'
...
end
One simple solution, but a little bit hack method is
You can pass environment variables while running vagrant reload command like this
RELOAD=true vagrant reload --provision
then in VagrantFile
VAGRANTFILE_API_VERSION = '2'
Vagrant.configure(VAGRANTFILE_API_VERSION) do |config|
...
# Ignore this line on VM reload
if (ENV['RELOAD'] != true)
config.vm.provision 'shell', path: 'provision/install.sh'
end
# Execute this one only on VM reload
config.vm.provision 'shell', path: 'provision/start_nginx.sh'
...
end

managing existing hosts with vagrant bypassing the provider

I'm managing a few web services residing on different fixed hosts with ssh. I wanted to use vagrant so that I can edit local files and have them synced automagically.
however I'm having problems as I'm not using no provider or box, it's a fixed host and it feels like I'm going against vagramt's aim.
here's my Vagrantfile:
# -*- mode: ruby -*-
# vi: set ft=ruby :
# Vagrantfile API/syntax version. Don't touch unless you know what you're doing!
VAGRANTFILE_API_VERSION = "2"
Vagrant.configure(VAGRANTFILE_API_VERSION) do |config|
config.ssh.host = ...
config.ssh.username = ...
config.ssh.private_key_path = ".ssh/id_rsa"
config.vm.synced_folder "src/", "..."
config.vm.box = "myhost"
config.vm.provision :shell, :path => "bootstrap.sh"
end
and here's my bootstrap.sh file:
pip install flask sqlalchemy
but I can't make vagrant skip providing (with virtualbox or so)
well, as it always comes out - fighting against your tool in order to force it to do things it's not designed to are a bad idea.
there was probably a way to make vagrant use a void box but vagrant is too much for just keeping 2 directories synced. I found this nice tool that does exactly the same as vagrant for sync just without all the provider/provision etc.

Resources