How do I use Chef data bags from my Vagrantfile? - ruby

I am attempting to use Fnichol's Chef User recipe with Vagrant to automatically create a specific user account when I run vagrant up. Because I setup this user for nearly every project that I work on, I'd like to store the user data in a data bag that I am loading from a JSON file that I can re-use on multiple projects.
This is what my Vagrantfile currently looks like: http://pastebin.com/b0riZZCz
It fails with the error:
[2014-01-20T16:09:39+00:00] ERROR: could not find recipe ben for cookbook user
I have created a data bag called "users" and inside that data bag I've created a data bag item named "ben" from the following JSON:
{
"id": "ben",
"comment": "Ben Harold",
"groups": ["admin", "www-data"],
"ssh_keys": ["...longtextstring..."],
"ssh_keygen": false
}
I'm attempting to follow the usage instructions at http://fnichol.github.io/chef-user/ to:
Import the Chef Users recipe
Tell the Chef Users recipe to create the user ben from the data bag users
It appears to me that the Chef provisioning syntax from within a Vagrantfile is considerably different than the syntax that is presented in most of the available documentation. For example:
"Simply include recipe[user] in your run_list and the user_account
resource will be available."
I'm confused as to the definition of run_list, as well as the recipe[user] syntax. From the Vagrant documentation, it seems that my run_list is everything in this block:
config.vm.provision :chef_solo do |chef|
chef.add_recipe "apt"
...etc...
end
However, I've also found references to chef.run_list being defined within that block, although I have not been able to find any documentation specifically referring to chef.run_list.
Question 1
Is the run_list simply the code within the config.vm.provision :chef_solo do |chef| block, or is it something else?
Question 2
Where is the documentation for chef.run_list? I'm not looking for the documentation on the Chef site. The syntax seems completely different to me.
I've been messing with this for several hours. I've tried a bunch of stuff that does not work. I am able to import the Chef User recipe, but I haven't been able to figure out how to tell Chef to run the user recipe against the data bag item "ben".
Question 3
How do I get Chef to run the user recipe with the data bag item "ben"? Or am I doing it completely wrong?

Answer 1
No, run_list is actually just an array of strings, that represent recipes/roles that should be run on Vagrant Machine. And add_recipe adds new recipes into this list. Like that:
config.vm.provision :chef_solo do |chef|
[...]
chef.add_recipe 'cookbook::recipe' #now run_list has it in
chef.add_role 'myrole' #now run_list has a role too
#adding more recipes from ENV variable, just as example
chef.run_list += ENV['CHEF_RUN_LIST'].split ',' if ENV.has_key? 'CHEF_RUN_LIST'
[...]
end
Answer 2
You are editing Vagrantfile, so documentation is on Vagrant site
Answer 3
You need to tell Vagrant where are your cookbooks, data bags and roles.
config.vm.provision :chef_solo do |chef|
[...]
chef.cookbooks_path = 'cookbooks' #paths are relative to Vagrantfile location
chef.roles_path = 'roles'
chef.data_bags_path = 'data_bags'
[...]
end

Related

With vagrant-berkshelf deprecated, will it be possible to drive builds from Vagrant, with Chef solely as a provisioner?

I have had a number of issues recently with vagrant-berkshelf not syncing Chef cookbooks on an existing machine reliably. And, basically, when doing research on workarounds, I see something like:
vagrant-berkshelf is deprecated, use Test Kitchen instead.
My use case is that I have Vagrantfiles, used to build VMs and DigitalOcean droplets, that are hand-written and only use Chef to provision the VMs. I am most definitely approaching Chef as a user, not an author or tester of cookbooks.
So, I am in a case of Vagrant -> Chef, not Chef -> Vagrant.
When looking at Kitche-Vagrant, I see that:
The kitchen-vagrant driver for Kitchen generates a single Vagrantfile for each instance of Kitchen in a sandboxed directory..
My question is: if my workflow relies on hand-written, complex, Vagrantfiles, can I continue to use Chef as a provisioner without having to rely on vagrant-berkshelf?
Some of the possible alternatives I see are:
mangle Test Kitchen configuration to work with my exiting Vagrantfile. I fear that this is not the intent of this tool and will not end well.
use chef.cookbooks_path attribute in vagrant and let it take the place of vagrant-berkshelf.
switch out provisioners and use say Vagrant->Ansible.
The Vagrantfile below is somewhat simplified, but the gist is that the Vagrantfile is in charge and Chef is just used to provision.
# -*- mode: ruby -*-
# vi: set ft=ruby :
#...grab some variables from my host environment...
DJANGO_SECRET_KEY = ENV['BUILD_DJANGO_SECRET_KEY']
Vagrant.configure('2') do |config|
config.vm.define "myserver" do |config|
config.vm.provider :digital_ocean do |provider, override|
override.ssh.private_key_path = digoconf.private_key_path
override.vm.box_url = "https://github.com/devopsgroup-io/vagrant-digitalocean/raw/master/box/digital_ocean.box"
provider.token = digoconf.TOKEN
...
end
#had chef_client before, that worked too.
config.vm.provision "chef_zero" do |chef|
chef.log_level = "info"
#I haven't tested these out
#chef.cookbooks_path = ["../community/cookbooks","../.berkshelf/cookbooks"]
env_for_chef = " DJANGO_SECRET_KEY='#{DJANGO_SECRET_KEY}'"
chef.binary_env = env_for_chef
chef.environment = "digitalocean"
chef.add_recipe "base::install"
end
end
end
It isn't deprecated per se, but it does no longer have a maintainer and does highly recommend against its use. There is no replacement for the workflow you describe. Sorry. If you are interested in taking over as maintainer, I can put you in contact with the team.

Is it possible to load a Vagrantfile config from within another Vagrantfile?

Vagrant Multi-Machine functionality seems pretty cool, however one thing that bothers me (or is isn't immediately apparent) is that there doesn't seem to be a good way to share configuration options between a "parent" Vagrantfile and "children" Vagrantfiles. Is there a way to effectively and maintainably share configuration options between the parent and it's children? An example might make this more clear.
Let's assume I've got a platform which is comprised of 3 apps/services: API, Web, and Worker.
Let's presume a directory structure of the following:
/some_platform
/api
# app code...
Vagrantfile
/web
# app code...
Vagrantfile
/worker
# app code...
Vagrantfile
Vagrantfile
Let's say /some_platform/api/Vagrantfile looks like:
Vagrant.configure("2") do |config|
config.vm.box = "debian/jessie64"
end
Presumably the web and worker Vagrantfiles look similar.
Now, using the wonders of Multi-Machine I rely on Vagrant coordinate these VMs, and /some_platform/Vagrantfile looks like:
Vagrant.configure("2") do |config|
config.vm.define "web" do |api|
api.vm.box = "debian/jessie64"
end
config.vm.define "web" do |web|
web.vm.box = "debian/jessie64"
end
config.vm.define "web" do |worker|
worker.vm.box = "debian/jessie64"
end
end
I realize this example is contrived, but it's easy to see how once you get more and more complex config declarations, it's annoying and hazardous to have that config duplicated in two places.
You might be wondering "Why does each project have it's own Vagrantfile?" Doing so provides a single source of truth for how the server that app runs on should be setup. I realize there are provisioners you can use (and I will use them), but you still have to declare a few other things outside of that and I want to keep that DRY so that I can either bring up a cluster of apps via Multi-Machine, or I can work on a single app and change it's VM/server setup.
What I'd really love is a way to merge other Vagrantfiles into a "parent" file.
Is that possible? Or am I crazy for trying? Any clever ideas on how to achieve this? I've mucked about with some yaml files and POROs to skate around this issue, but none of the hacks feel very satisfying.
Good news!
You can in fact apply DRY principles in a Vagrantfile.
First: Create a file /some_platform/DRY_vagrant/Vagrantfile.sensible to hold some sensible defaults :
Vagrant.configure("2") do |config|
# With the setting below, any vagrantfile VM without a 'config.vm.box' will
# automatically inherit "debian/jessie64"
config.vm.box = "debian/jessie64"
end
Second: Create a file /some_platform/DRY_vagrant/Vagrantfile.worker for the 'worker' virtual machine :
Vagrant.configure("2") do |config|
config.vm.define "worker" do |worker|
# This 'worker' VM will not inherit "debian/jessie64".
# Instead, this VM will explicitly use "debian/stretch64"
worker.vm.box = "debian/stretch64"
end
end
Finally: Create a file /some_platform/Vagrantfile to tie it all together :
# Load Sensible Defaults
sensible_defaults_vagrantfile = '/some_platform/DRY_vagrant/Vagrantfile.sensible'
load sensible_defaults_vagrantfile if File.exists?(sensible_defaults_vagrantfile)
# Define the 'api' VM within the main Vagrantfile
Vagrant.configure("2") do |config|
config.vm.define "api" do |api|
# This 'api' VM will automatically inherit the "debian/jessie64" which we
# configured in Vagrantfile.sensible
# Make customizations to the 'api' VM
api.vm.hostname = "vm-debian-jessie64-api"
end
end
# Load the 'worker' VM
worker_vm_vagrantfile = '/some_platform/DRY_vagrant/Vagrantfile.worker'
load worker_vm_vagrantfile if File.exists?(worker_vm_vagrantfile)
This approach can be used for almost any other vagrantfile config options. It is not limited to just the "config.vm.box" setting.
Hope this helped!
There are 2 things you can look at (probably more, but those two comes to my mind)
look at How to template Vagrantfile using Ruby? its an example how you can read the content of another file, Vagrantfile is just a ruby script so you can use all the power of ruby.
vagrant has a concept of loading and merging, see from doc so if you wanted to do something anytime you run a vagrant command, you could create a Vagrantfile under your ~/.vagrant.d/ folder and it will always run
one drawback (or at least to pay attention) : the Vagrantfile is a ruby script that is evaluated each (and every time) a vagrant command is executed (up, status, halt ....)

what is the proper way to override chef recipe attributes?

I am adding this Solr Chef recipe an existing vagrant box . I want to override the version attribute here . Do I override these variables in one of the json files or it has to be overridden in the recipe attributes file itself?
For your use case it's ok to override it in Vagrantfile directly. Check more info about chef solo and vagrant here.
tldr;
Just provide chef.json option with attributes in your Vagrantfile like:
Vagrant.configure("2") do |config|
config.vm.provision "chef_solo" do |chef|
# ...
chef.json = {
"solr" => {
"version" => "4.6.1"
}
}
end
end
In general practise it's common to set attributes inside roles, or by creating wrapper cookbooks around community ones, and with that there is no need for changing original source code.

Creating a custom Vagrantfile from Packer

I've been trying to figure out how to create a custom vagrant file from packer, I understand that in the post-processor section you will define a directory from which to scrap from, what I do not understand is if there needs to be a specifically named file inside as to which to gather data from.
"post-processors": [{
"vagrantfile_template": "configs/vagrantfile_template",
"type": "vagrant"
}],
The above code to my knowledge would look under configs/vagrantfile_template, but what would need to be in here? Would I create a Vagrantfile and place it there, or would it need to be a specifically named Ruby file?
The vagrantfile_template option in a vagrant post-processor points directly to a file and not a directory [0]. The contents of this file should be structured like a normal Vagrantfile and contain any customizations for the box artifact that you are creating.
For example, if you wanted users of your custom Vagrant box to not have the /vagrant shared folder mounted by default, your Vagrantfile template might look like this...
Vagrant.configure("2") do |config|
config.vm.synced_folder \
".",
"/vagrant",
:disabled => true
end
Resources
[0]: https://github.com/mitchellh/packer/blob/53b1db16692c7bf2e654ac125b8676c02154d07a/post-processor/vagrant/post-processor.go#L113-L138

vagrantfile cookbook path

I want to set the cookbook path to a certain place, so that I don't need to
modify the Vagrantfile everytime(after vagrant init).
I find Vagrantfile load several places, so I decide to set my cookbook path info
in ~/.vagrant.d/Vagrantfile,(this file is the 3rd of Vagrantfile Load Order) like:
...
config.vm.provision :chef_solo do |chef|
chef.cookbooks_path = ["D:/lib/chef/cookbooks"]
chef.add_recipe "dev::default"
end
...
but when I make a new vm, and modify Vagrantfile(this file is the 4th of Vagrantfile Load Order):
...
config.vm.provision :chef_solo do |chef|
chef.add_recipe "torch"
end
...
error:
[2013-02-28T03:23:36+00:00] ERROR: Running exception handlers
[2013-02-28T03:23:36+00:00] ERROR: Exception handlers complete
[2013-02-28T03:23:36+00:00] FATAL: Stacktrace dumped to /tmp/vagrant-chef-1/chef-stacktrace.out
[2013-02-28T03:23:36+00:00] FATAL: Chef::Exceptions::CookbookNotFound: Cookbook torch not found. If
you're loading torch from another cookbook, make sure you configure the dependency in your metadata
Chef never successfully completed! Any errors should be visible in the
output above. Please fix your recipes so that they properly complete.
but I am sure the specific cookbook is under my cookbook path.
Try the following steps:
- Create a folder cookbooks in the vagrantfile's directory
- checkout the cookbooks to this directory
- add the following to your vagrantfile
chef.cookbooks_path = ["cookbooks"]
hope to have helped

Resources