0

I have a Chef DataBag that I'm trying to read and use inside of a chef recipe, and its kicking my ass. Please note: I'm not a programmer, and the use of Chef is my first entry into Ruby.

Based off of the examples I've found online, here is the contents of the databag "AWSProd" that lives in a folder called WEB under the data_bags folder on my Chef server:

{
  "id" : "AWSProd",
  "hosted_sites" : {
    "siteA" : [
      {
        "site_name" : "siteA",
        "site_doc_root_folder" : "siteA",
        "site_simlink" : ""
     }
     ],
     "siteB" : [
      {
        "site_name" : "siteB",
        "site_doc_root_folder" : "siteB",
        "site_simlink" : ""
      }
      ]
  }
}

In my recipe, I'm using the following to grab the Databag for use:

WEB = data_bag("WEB")
WEB_env_globals = data_bag_item("WEB", node.chef_environment)

Then I basically want to iterate each site (siteA, siteB, etc) and grab those individual values for site_name, site_doc_root_folder, etc...

I'm trying to just echo the values so I know they work. I tried this:

WEB_env_globals["hosted_sites"].each do |site|
  each_sitename = site["site_name"] ## can't convert String into Integer
  each_site_doc_root_folder = site["site_doc_root_folder"]
  each_site_simlink = site["site_simlink"]

  execute "echo each site" do
    command "echo #{each_sitename} #{each_site_doc_root_folder} #{each_site_simlink}"
    action :run
  end

end

But I received a "can't convert String into Integer" error on the line where I have the double ##.

Then I tried replacing that line with something like this:

each_sitename = WEB_env_globals["hosted_sites"][site]["site_name"]

But then I get an "undefined method `[]' for nil:NilClass" error on that line.

I know I'm missing something completely basic with Ruby here, and I've been looking for about an hour for a clear explanation and cant find one. Help me Ruby-Won-Kenobi...

3 Answers3

1

In your data bag item, each site is an array of hashes. I don't think this is what you intended, since you would need to access it like:

site[0]["site_name"]

What you probably wanted was a data bag item like:

{
  "id" : "AWSProd",
  "hosted_sites" : {
    "siteA" : {
        "site_name" : "siteA",
        "site_doc_root_folder" : "siteA",
        "site_simlink" : ""
     },
     "siteB" : {
        "site_name" : "siteB",
        "site_doc_root_folder" : "siteB",
        "site_simlink" : ""
      }
  }
}
coderanger
  • 52,400
  • 4
  • 52
  • 75
0

Ok, so I got it! Took a little education on Hash Vs Arrays...

Below is the correct ruby block:

WEB_env_globals["hosted_sites"].each do |site,data|

  data.each do |hash|
    each_sitename = hash["site_name"]
    each_site_doc_root_folder = hash["site_doc_root_folder"]
    each_site_simlink = hash["site_simlink"]

      execute "echo each site #{site}" do
        command "echo #{each_sitename} #{each_site_doc_root_folder} #{each_site_simlink}"
        action :run
      end
    end
  end
OK999
  • 1,353
  • 2
  • 19
  • 39
  • Yea, that works, but I'd suggest you change the structure of your databag. You're listing the sitename twice, for example, which is not really needed. – Tejay Cardon Oct 23 '14 at 17:15
  • Thanks Tejay, we have a few sites that have sitenames that are ridiculous, so we wanted a "plain english" tag for the site. Although I guess that could just be another value in the list... Good food for thought! – HeavyObjectLifter Oct 23 '14 at 21:18
  • if that case, I would suggest you go with the approach that coderanger pointed out. That allows you to use an ID and name while still keeping a cleaner (and more ruby like) data structure. – Tejay Cardon Oct 23 '14 at 23:22
0

You are using the .each method on a Hash, but only capturing the key. WEB_env_globals['hosted_sites'].each do |key, value| - but you only gave it a name for the key.

each_sitename = site[....] - remember that site is the key (a String), so you're calling the [] method on String which expects an Integer and returns the char at that index.

*You can call .class on any ruby object to find out what type it is. This is very helpful for troublshooting

So you can change your databag to use an array of hashes:

change your databag:

{
  "id" : "AWSProd",
  "hosted_sites" : [
    {
      "site_name" : "siteA",
      "site_doc_root_folder" : "siteA",
      "site_simlink" : ""
    },
    {
      "site_name" : "siteB",
      "site_doc_root_folder" : "siteB",
      "site_simlink" : ""
    }
  ]
}

Or leave it alone and try this for your code: (note I also don't use an execute block for my debugging)

WEB_env_globals["hosted_sites"].each do |sitename, site_array|
  site_array.each do |site|
    each_sitename = site["site_name"]
    each_site_doc_root_folder = site["site_doc_root_folder"]
    each_site_simlink = site["site_simlink"]

     Chef::Log.debug "#{each_sitename} #{each_site_doc_root_folder} #{each_site_simlink}"
     # you could use puts or a higher level log message to make this easier to see in your output.
  end

end

Tejay Cardon
  • 4,193
  • 2
  • 16
  • 31