HeavyObjectLifter
HeavyObjectLifter

Reputation: 87

Chef Data_bag usage via Ruby Recipe error?

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...

Upvotes: 0

Views: 551

Answers (3)

HeavyObjectLifter
HeavyObjectLifter

Reputation: 87

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

Upvotes: 0

Tejay Cardon
Tejay Cardon

Reputation: 4223

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

Upvotes: 0

coderanger
coderanger

Reputation: 54267

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" : ""
      }
  }
}

Upvotes: 1

Related Questions