Search code examples
rubyvagrantchef-infra

Dynamically Populate Chef Cookbook Attributes


I'm trying to create a recipe which pulls in variables which are defined in a YAML configuration file and then uses their values as recipe attributes.

A couple points:

  1. My config.yml file resides in the root of my chef directory (that is, outside of my cookbooks directory, and on the same level as my Vagrantfile which drives the provisioning process.

  2. I use a similar approach to populate values into my Vagrantfile, which is what gave me the idea for this solution. I want to maintain a list of user-configurable values which need to be used in both my Vagrantfile as well as various recipes inside various cookbooks.

As an example, here are two values from config.yml:

---
basics:
  vm_ip: 192.168.56.11
  vm_hostname: luma.com

I use the following in my Vagrantfile to read these values:

require 'yaml'
config_file = "config.yml"
settings = YAML.load_file(File.dirname(File.expand_path(__FILE__)) + "/#{config_file}")

And I reference them as such elsewhere in the Vagrantfile (Syntax abbreviated):

machine.vm.network 'private_network', ip: settings['basics']['vm_ip']
machine.vm.hostname = settings['basics']['vm_hostname']

This approach works great for the Vagrantfile, but of course, that's outside of Chef's purview.

From a Chef perspective, I thought I could perhaps follow a similar approach, since Chef is Ruby-based. I have a cookbook base with an attributes folder, and a default.rb attributes file. My initial attempt was to simply duplicate the approach in default.rb:

require 'yaml'
config_file = "config.yml"
settings = YAML.load_file(File.dirname(File.expand_path("../../../", __FILE__)) + "/#{config_file}")

And then reference the config.yml values as attributes like so:

default['vm']['ip'] = settings['basics']['vm_ip']
default['vm']['hostname'] = settings['basics']['vm_hostname']

This fails because, as I've (naively) learned, the __FILE__ value refers to a path on the node rather than to a file in the local directory, as this attributes file is used during convergence, etc. Stupid me.

So, my thought now is to dynamically populate the attributes file as part of the Vagrantfile, since that's outside of Chef's process. Before I go through the effort of writing that, I'm wondering if there's a more appropriate chef-based approach I should consider.


Solution

  • i might be wrong, but it seems you are trying to invent the wheel. let me elaborate...

    vagrant

    from vagrant perspective, you should leverage test-kitchen in conjunection with vagrant driver.

    once you set it up, take advantage of custom kitchen configuration -- use general configuration and (user) custom configuration (each user might have a different custom conifugration, corresponding to the config.yml you mentioned`)

    note also, that kitchen.yml supports erb. you can use it to do things dynamically.

    chef

    from chef perspective, is a configuration management and you should use it correctly. that is, rather than using an external file (config.yml) to feed chef with values, take advantage of recipe attributes, json attributes or even data bags to store values which will be used during your recipe.

    note that attributes can be generated dynamically, you will just have to put some logic for that (same as you did for config.yml)