Parsing/populating Ruby variables from within a JSON file for chef.json

1,396 views
Skip to first unread message

bmur...@coredial.com

unread,
Jul 11, 2014, 2:31:06 PM7/11/14
to vagra...@googlegroups.com
So here's an interesting predicament.......


As I continue to make our Vagrantfile more dynamic and scalable, I'm running into a slight issue regarding chef.json. The old Vagrantfile has individual nodes declared and associated chef.json entries with a code block like:

chef.json = {
  :rabbitmq => {
          :port => 5000,
          :cluster_disk_nodes => ["rabbit@#{hostname(COMPONENTS[:app_mq1])}"],
  }
}


Both the hostname method as well as the COMPONENTS hash exist in the the old Vagrantfile and my working copy. The problem is, I've changed the Vagrantfile so the chef.json data is populated from a remote file like so:

if !node[:chef_json].empty?
        dna = JSON.parse(File.read("./chef_json/#{node[:chef_json]}"))
        chef.json.merge!(dna)
end

Each node (VM) has an key/value pairing to a file (ie: web.json or app-mq1.json). With that being said, these .json files are written out like how they were inline, but when Chef provisions, the Ruby variable is not being interpolated and therefore, I end up with a RabbitMQ config file like so:

vagrant@dev-app-mq1:$ sudo cat /etc/rabbitmq/rabbitmq-env.conf
###
# Generated by Chef
###

NODENAME=rabbit@#{hostname(COMPONENTS[:app_mq1])}

NODE_PORT=5000
CONFIG_FILE=/etc/rabbitmq/rabbitmq

MNESIA_BASE=/var/lib/rabbitmq/mnesia

Does anyone have any guidance on how to get this to work as I intend? The other workaround would be to put things back inline, but then I've lost some progress on optimization, etc.

Thanks,

Brendan

bmur...@coredial.com

unread,
Jul 11, 2014, 3:03:57 PM7/11/14
to vagra...@googlegroups.com
After looking over some of the Vagrant code (https://github.com/mitchellh/vagrant/blob/v1.6.1/plugins/provisioners/chef/provisioner/base.rb#L97-L116), it looks like the value of chef.json is parsed, written to a temp file, uploaded to the VM and executed. That helps me understand the process behind the scenes, but not address my issue.

bmur...@coredial.com

unread,
Jul 11, 2014, 3:17:26 PM7/11/14
to vagra...@googlegroups.com
Lastly, here is my chef_json/app-mq1.json contents:

cat chef_json/app-mq1.json
{
  "rabbitmq": {
    "port": 5000,
    "nodename": "rabbit@#{hostname(COMPONENTS[:app_mq1])}"
  }
}

Alvaro Miranda Aguilera

unread,
Jul 16, 2014, 6:07:43 PM7/16/14
to vagra...@googlegroups.com

if you write a json in the vagrant file and then you use

puts myjson.to_json

you see the same structure?

I think this won't be correctly interpreted back in vagrantfile


    "nodename": "rabbit@#{hostname(COMPONENTS[:app_mq1])}"

if you write this int he vagrantfile, it should read soemthing like

    "nodename": "rabbit@#{hostname(" + COMPONENTS[:app_mq1] + ")}"

rite?

Shawn Neal

unread,
Jul 16, 2014, 7:20:29 PM7/16/14
to vagra...@googlegroups.com
Why do you have such a large and complex Vagrantfile? It sounds like you have a lot of attributes that would be better managed outside Vagrant via Chef Server roles and environments.

Brendan Murtagh

unread,
Jul 17, 2014, 8:48:57 AM7/17/14
to vagra...@googlegroups.com
I agree, but I recently started at this position which initially was running on Vagrant v1.0.7 with a v1 config. The initial Vagrantfile was a single file, with 8 or 9 nodes configured inline and roughly ~600 lines. I’ve completed a big portion of reducing that complexitiy to simplify the management by creating a hash of the nodes with their basic settings, adding some logic for VM specs, and also moving some or most of the external stuff out to individual node-specific files to be parsed at runtime. 

Chef Roles are already in use. I agree that some/most of this JSON could be moved to Chef Environments, but I’m also learning Chef (and the current work’s Chef configuration) at this time. I come from a Puppet background with multiple environments and corresponding YAML files handling the environmental-specific configuration settings. While my learning process is still in progress, I’m hoping to address this without drastically changing the Vagrant or Chef setup. They’re also running with Chef v10.14.4 so part of my milestones for this project is also to get their infrastructure running on v11 (or latest) once this works with the existing version.

Brendan Murtagh

unread,
Jul 17, 2014, 8:52:19 AM7/17/14
to vagra...@googlegroups.com
Hi Alvaro,

When I had the chef.json entries inline with each node’s declaration, the JSON works as expected. The hostname() method and COMPONENTS hash are written within the Vagrantfile and accessible. It’s that transition when being parsed or interpolated from the external file, that the variables do not get populated with their true values. It seems like the file is read in during the provisioning process as literal strings which works for JSON entries that do not contain variables, but once a variable is introduced, it gets written as-is instead of its intended behavior.

Shawn Neal

unread,
Jul 17, 2014, 11:08:54 AM7/17/14
to vagra...@googlegroups.com
If you're just reading in a file then you'll need to take some explicit action to force interpolation to occur. This sounds like your exact question here: http://stackoverflow.com/questions/346380/in-ruby-can-you-perform-string-interpolation-on-data-read-from-a-file


On Thu, Jul 17, 2014 at 5:52 AM, Brendan Murtagh <bmur...@coredial.com> wrote:
Hi Alvaro,

When I had the chef.json entries inline with each node's declaration, the JSON works as expected. The hostname() method and COMPONENTS hash are written within the Vagrantfile and accessible. It's that transition when being parsed or interpolated from the external file, that the variables do not get populated with their true values. It seems like the file is read in during the provisioning process as literal strings which works for JSON entries that do not contain variables, but once a variable is introduced, it gets written as-is instead of its intended behavior.

--
You received this message because you are subscribed to a topic in the Google Groups "Vagrant" group.
To unsubscribe from this topic, visit https://groups.google.com/d/topic/vagrant-up/aOwrLLmmxS0/unsubscribe.
To unsubscribe from this group and all its topics, send an email to vagrant-up+...@googlegroups.com.
For more options, visit https://groups.google.com/d/optout.

Alvaro Miranda Aguilera

unread,
Jul 17, 2014, 3:34:46 PM7/17/14
to vagra...@googlegroups.com
Seems you have 2 options here at the moment.

One is what Shawn did share, that make perfect sense and is something you should try, to have 1 json file

2nd, is having 2 json files, and do the evaluation on Vagrantfile

Shawn approach will leave all the logic in json file

2nd approach will move the evaluation logic to the Vagrantfile..

let me know what you do and what works please.

Alvaro.



On Fri, Jul 18, 2014 at 12:52 AM, Brendan Murtagh <bmur...@coredial.com> wrote:
Hi Alvaro,

When I had the chef.json entries inline with each node's declaration, the JSON works as expected. The hostname() method and COMPONENTS hash are written within the Vagrantfile and accessible. It's that transition when being parsed or interpolated from the external file, that the variables do not get populated with their true values. It seems like the file is read in during the provisioning process as literal strings which works for JSON entries that do not contain variables, but once a variable is introduced, it gets written as-is instead of its intended behavior.

--
You received this message because you are subscribed to the Google Groups "Vagrant" group.
To unsubscribe from this group and stop receiving emails from it, send an email to vagrant-up+...@googlegroups.com.
Reply all
Reply to author
Forward
0 new messages