As part of provisioning a VM, I need to create a configuration file at provision time. On my host machine, I'm able to pass variables to an erb file:
erb x=1 y=2 some_conf.erb
Is it possible to render the erb file on the host machine, and then pipe the result to the guest?
If I put the erb command in a config.vm.provision "shell" , the command is run on the guest machine (which does not have Ruby), e.g.
# this command runs on the guest
# where as I want erb to run on the host
config.vm.provision "shell", inline: <<-SHELL
erb x=1 y=2 some_conf.erb > /etc/some_conf
SHELL
Vagrant file is actually a ruby script so you could use the ruby erb template engine. Make sure to create the class necessary for your template
# -*- mode: ruby -*-
# vi: set ft=ruby :
require 'erb'
Vagrant.configure("2") do |config|
template = ERB.new File.read("template/some_conf.erb")
p template.result(binding)
end
If you don't want to set up the new class etc and you have ruby2.2 installed you can just call erb from CLI directly, it will run on your host not the guest
# -*- mode: ruby -*-
# vi: set ft=ruby :
Vagrant.configure("2") do |config|
exec ('erb x=1 y=2 template/some_conf.erb > some_conf')
end
once the some_conf file is generated on the host, you can use the vagrant file provisioner to push this single file on the host
config.vm.provision "file", source: "some_conf", destination: "/somewhere/on/the/guest/some_conf"
Related
This may actually be a basic Ruby question more than a Vagrant-specific question. I have two Vagrantfiles. One makes a number of generic settings. The second one makes more specific settings for the particular instance. The first is loaded into the second. The idea is to keep common settings that are the same across all Vagrantfiles in one place so it's normalizes and I don't repeat myself. Some Vagrant settings are an array of strings. If I set one of these in the first Vagrantfile, and I try to add an element to it in the second file, I get an error:
Message: NoMethodError: undefined method `<<' for :__UNSET__VALUE__:Symbol
How can I add to an array?
Here is a boiled down first Vagrantfile with just what I'm interested in:
Vagrantfile.general
# -*- mode: ruby -*-
# vi: set ft=ruby :
Vagrant.configure('2') do |config|
config.vm.provider 'cloud_service' do |cs|
cs.tags = ['ExampleTag1']
end
end
Vagrantfile (Specific)
load 'Vagrantfile.general'
Vagrant.configure('2') do |config|
config.vm.provider 'cloud_service' do |cs|
cs.tags << 'ExampleTag2'
end
end
The following works, but I want to know how to add to the previous array rather than overriding the whole array.
Vagrantfile (Workaround)
load 'Vagrantfile.general'
Vagrant.configure('2') do |config|
config.vm.provider 'cloud_service' do |cs|
cs.tags = ['ExampleTag1', 'ExampleTag2']
end
end
Note: I understand that the tags can be kept in a YAML file that is loaded by first and second files and then added to or subtracted from before setting the tags in the provider. I have read this answer elsewhere. What I am seeking is a more elegant way to handle this. Basically I want to know if it is possible to access the object created by the first Vagrantfile where the array is located and add to it programmatically in the second Vagrantfile using Ruby code.
Update
Here is one option that is Ruby code, but does not access the array inside the Vagrant object: set a global variable in the first Vagrantfile. I'd still like to see an answer that accesses the Vagrant config object and modifies the array.
First
# -*- mode: ruby -*-
# vi: set ft=ruby :
$cs_tags = ['ExampleTag1']
Vagrant.configure('2') do |config|
config.vm.provider 'cloud_service' do |cs|
cs.tags = $cs_tags
end
end
Second
load 'Vagrantfile.general'
$cs_tags << 'ExampleTag2'
Vagrant.configure('2') do |config|
config.vm.provider 'cloud_service' do |cs|
cs.tags = $cs_tags
end
end
I am writing a Vagrantfile to set up a VM. There are some config parameters that I do not want to hardcode in the Vagrantfile, such as Memory and number of CPUs. As a consequence, I'm using a YAML file which gets loaded in the Vagrantfile to store these config parameters. One thing that is stored in the YAML file is the list of shell provisioner scripts to run. For instance:
---
machine_config:
mem: 2048
cpus: 2
provisioners:
-name: shell-script-1
path: <path-to-shell-script-1>
-name: shell-script-2
path: <path-to-shell-script-2>
---
The number of provisioners is not known a priori: in the YAML above there are two, but it's only an example. I'd like to have a Vagrantfile which can run all of the provisioners in the YAML file. What I mean is that I want to be able to add/remove provisioners to the YAML file without touching the Vagrantfile, yet the Vagrantfile should correctly run all of the provisioners in the YAML file. I searched on Google and there are plenty of examples on how to run the same, hardcoded provisioners on a dynamic number of VMs, but could find none for my problem.
What I'd like to do, written in pseudo-vagrantfile syntax, is:
require "yaml"
current_dir = File.dirname(File.expand_path(__FILE__))
yaml_config = YAML.load_file("#{current_dir}/machine_config.yaml")
machine_config = yaml_config["machine_config"]
additional_scripts = machine_config["provisioners"]
Vagrant.configure("2") do |config|
config.vm.box = <vm-box-to-use>
for each item $script in additional_scripts do
config.vm.provision "shell", path: $script["path"]
end
end
where machine_config.yaml is a YAML file like the one in the first example of this question, and $script is a variable that at every iteration of the loop holds a provisioner among those described in machine_config.yaml. As a last remark, I know nothing about Ruby and Ruby's syntax (maybe to someone with that knowledge the answer to my question is trivial, but I couldn't find it by googling).
The following will work
require "yaml"
current_dir = File.dirname(File.expand_path(__FILE__))
yaml_config = YAML.load_file("#{current_dir}/machine_config.yaml")
machine_config = yaml_config["machine_config"]
Vagrant.configure("2") do |config|
config.vm.box = "<vm-box-to-use>"
machine_config["provisioners"].each do |script|
config.vm.provision "shell", name: script['name'], path: script['path']
end
end
I was wondering where the default commands of vagrant are defined.
How does vagrant know what to do when you type in "vagrant up" where is this defined?
I want to make vagrant echo something when you type the command vagrant up for example.
vagrant reads the Vagrantfile when you execute the vagrant command
If you want to echo something when you run the command you'll need to put in this file which is roughly a ruby script
example :
# -*- mode: ruby -*-
# vi: set ft=ruby :
Vagrant.configure(2) do |config|
config.vm.box = "xxxxx"
.... your config
p "put your text here"
p "#{Dir.pwd}"
ARGV.each do|a|
puts "Argument: #{a}"
end
p ARGV[0]
and
I'm wanting to run a separate provisioning block in my Vagrant file on the newly provisioned server. At present when I run this from my CI server
vagrant up
the following blocks are executed successfully
config.vm.provider :linode do |provider, override|
#creates a new instance etc .. the following block runs on this instance
end
config.vm.provision :chef_solo do |chef|
chef.provisioning_path = "/tmp/deploy"
chef.cookbooks_path = ["cookbooks"]
chef.add_recipe = "mydeployagent"
end
now i want to run a separate provisioner afterwards. (a separate task in CI server) i.e.
config.vm.provision :chef_solo do |chef|
chef.provisioning_path = "/tmp/deploy"
chef.cookbooks_path = ["cookbooks"]
chef.add_recipe = "mydeploydatabaseagent"
end
I'm trying to figure out what I need to
run vagrant up so that it only executes the 1st provision block
run vagrant so that it will only run the 2nd provisioner block on the instance that was created in 1.
Thanks in advance
From Vagrant docs.
Vagrant.configure("2") do |config|
# ... other configuration
config.vm.provision "bootstrap", type: "shell" do |s|
s.inline = "echo hello"
end
end
The --provision-with flag can be used if you only want to run a
specific provisioner if you have multiple provisioners specified. For
example, if you have a shell and Puppet provisioner and only want to
run the shell one, you can do vagrant provision --provision-with
shell. The arguments to --provision-with can be the provisioner type
(such as "shell") or the provisioner name (such as "bootstrap" from
above).
Chef is about managing a desired state so you should be wanting it to ensure your two recipes are run at each time (and they should be idempotent).
I don't know any way to tell vagrant to use twice the same provisoner with different parameters (I can only think about ugly hacks in the vagrant file for that)
If the second recipe depends on the first one to have been executed then you may add a guard to skip this second recipe until the first one has run.
With chef-solo you may do something like this:
mydeployagent/recipes/default.rb
[...Whatever your recipe has to do (agent install, configuration, etc...]
# On a particular resource on your first recipe,
# trigger a notification to make a file which will be used as a guard for second recipe
remote_file "/opt/target/mydeplaoyagent" do
source [.. your source..]
notifies :create,"file[/opt/mydeployagent.initialized]"
end
file "/opt/mydeployagent.initialized" do
action :nothing
end
mydeploydatabaseagent/recipes/default.rb
#start the recipe by a guard to avoid doing anythng if first recipe has not been run
return unless ::File::exists?("/opt/mydeployagent.initialized")
[... your second recipe code here ...]
And in your vagrant file your can add the two recipes to your provisioner like:
config.vm.provision :chef_solo do |chef|
chef.provisioning_path = "/tmp/deploy"
chef.cookbooks_path = ["cookbooks"]
chef.add_recipe = "mydeployagent"
chef.add_recipe = "mydeploydatabaseagent"
end
Obviously the guard can be used on top of the first recipe too if it is not idempotent, but I highly encourage to rethink it to be able to run multiples times, you'll be happy to have it running when you'll have a configuration change to propagate in a file managed by this recipe (and trust me, you will have an update like this to manage one day).
Ok, i'm very new to Ruby (i come from PHP, Symfony2 and AngularJS) and relatively new when it comes to properly writing Vagrantfiles. I'm trying to create a multi-machine environment while trying to stick to DRY principles.
As i read that Vagrantfiles understand Ruby syntax, i looked up the way Ruby defines associative arrays. This happened to be quite easy, apparently not.
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"
#hash for boxes: 'box_name' => 'last_ip_octet'
boxes = {
'frontend' => '10',
'qp' => '11'
}
Vagrant.configure(VAGRANTFILE_API_VERSION) do |config|
config.vm.box = "chef/ubuntu-14.04"
#All machines, see the hash defined in top of the Vagrantfile for all the boxes
boxes.each do |key, value|
config.vm.define "#{key}.qp" do |#{key}_qp|
#{key}_qp.vm.network "private_network", ip: "192.168.51.#{value}"
#{key}_qp.vm.provision "shell", path: "../provisioning/agentinstall.sh"
#{key}_qp.vm.synced_folder "./share/#{key}.qp", "/var/www/html"
end
end
end
My problem reads as follows:
There is a syntax error in the following Vagrantfile. The syntax error
message is reproduced below for convenience:
/Users/Zowie/Documents/vagrant/project/temp/Vagrantfile:30: syntax error, unexpected keyword_end, expecting '|'
end
^
Unfortunately, i can't find any info on using Hashes or anything similar in Vagrantfiles.
I really hope you can help me out, because i'd not feel good while writing a super-long Vagrantfile with a lot of repetitions...
Thanks in advance!
The Stackoverflow website answered my question for me!
Thanks to Stackoverflow's code block feature, i noticed that my machine-specific configurations were commented out because i used a '#'.
I fixed it by using the following syntax in my loop (which is also easier to read):
boxes.each do |key, value|
config.vm.define "#{key}.qp" do |node|
node.vm.network "private_network", ip: "192.168.51.#{value}"
node.vm.provision "shell", path: "../provisioning/agentinstall.sh"
node.vm.synced_folder "./share/#{key}.qp", "/var/www/html"
end
end