Chathuranga Abeyrathna
Chathuranga Abeyrathna

Reputation: 789

Chef template loop ruby

I have json file which import as environment file to chef,

    { 
    "mongodb": {
      "replicaset": {
        "nodes": [
          "test-mongo1:27017",
          "test-mongo2:27017"
        ]
      }
    }
}

I chef cookbook template added as below to build the mongo connection string,

"mongo": {
    "url" : "mongodb://<% node['mongodb']['replicaset']['nodes'].each do |replica| -%>admin:123456@<%= replica %>/user_db1",
<% end %>

But output like below and not validating as JSON

"mongo": {
"url" : "mongodb://admin:123456@test-mongo1:27017/user_db1",
admin:123456@test-mongo2:27017/user_db1",

Expected Result:

"mongo": {
"url" : "mongodb://admin:123456@test-mongo1:27017/user_db1,admin:123456@test-mongo2:27017/user_db1",

Followed How to Run for each loop in template chef, But my ruby isn't the best because I'm just starting out with all of this stuff. Any help would be great, thanks.

Upvotes: 2

Views: 357

Answers (1)

Holger Just
Holger Just

Reputation: 55908

At first, we can attempt to fix the loop so that only the necessary parts are included in the inner block:

"mongo": {
    "url" : "mongodb://<% node['mongodb']['replicaset']['nodes'].each do |replica| -%>admin:123456@<%= replica %>/user_db1,<% end %>",

This however still results in a problem: There is a trailing comma since we always add it after each iteration.

A better approach would thus be to first build a list of URLs and then join them with a comma to a single string. That way, there is only a comma added between elements. This should work instead:

"mongo": {
    "url" : "mongodb://<%= node['mongodb']['replicaset']['nodes'].map { |replica| "admin:123456@#{replica}/user_db1" }.join(",") %>",

Basically, what this code does is the following:

output = ""

output << "\"mongo\": {\n"
output << "    \"url\" : \"mongodb://"
urls = node['mongodb']['replicaset']['nodes'].map do |replica|
  output << "admin:123456@"
  output << replica
  output << "/user_db1"
end
output << urls.join(", ")
output << "\","

Here, output represents the output of the ERB template. In the actual ERB implementation, there is a bit more logic involved, but the basic logic works similar to that.

When doing this in an actual cookbook, usually a better approach is to build the URLs in your recipe already and pass them to your template prebuild:

In recipes/default.rb:

urls = node['mongodb']['replicaset']['nodes'].map do |replica| 
  "admin:123456@#{replica}/user_db1"
end

template '/path/to/mongo.json' do
  source 'my_template.json.erb'
  variables mongo_urls: urls
end

In templates/default/my_template.json.erb:

"mongo": {
    "url" : "mongodb://<%= @mongo_urls.join(",") %>",

Upvotes: 4

Related Questions