7

Let say I have a cookbook which configures and installs a magical deamon:

magical-deamon/recipes/default.rb:

template "/etc/magical-deamon/magical.conf" do
    source "magical.conf"
    mode 0644
    notifies :restart, resources(:service => "magical-deamon")
end

magical-deamon/attributes/default.rb:

default['magical-deamon']['memory'] = 1024

magical-deamon/templates/default/magical.conf.erb:

memory = <%= node['magical-deamon']['memory'] %>

As I understood Chef, i would use either the node-attributes to set the memory value like:

{
    "normal": {
        "tags": [],
        "magical-deamon": {
            "memory": 256
        }
    },
    "name": "server.example.com",
    "chef_environment": "production",
    "run_list": [
        "role[base]"
    ]
}

Or through a role:

{
    "name": "base",
    "default_attributes": {
        "magical-deamon": {
                "memory": 756
            }
    },
    "json_class": "Chef::Role",
    "env_run_lists": {
    },
    "run_list": [
    ],
    "description": "base role applied to all nodes",
    "chef_type": "role",
    "override_attributes": {
    },
  }
}

Or an Environment:

{
  "name": "production",
  "default_attributes": {
    "magical-deamon": {
        "memory": 756
    }
  },
  "json_class": "Chef::Environment",
  "description": "",
  "cookbook_versions": {
  },
  "override_attributes": {
  },
  "chef_type": "environment"
}

So far so good...

Now i had the silly idea to set 'memory' to a node specific dynamic value.

Lets say our magical deamon should consume 75% of the total Memory the node possesses.

value = total_memory * 0.75

Coming from a programmer background, i like to leave that knowledge out of the cookbook, because i like my cookbook reusable for other people.

I thought the right place would be somewhere where attributes are being set. But doing such a calculation within json or ruby dsl is not possible.

So my questions are:

  • Is my general approach (value = total_memory * 0.75) a stupid idea?
  • How would you set that kind of attribute? Keep in mind: There will be more than one value, and one node :) And there might be some calculation involved MB -> KB and rounding and so forth. Hardwiring every attribute into the recipe, should not be an option ;)
Alex Jäger
  • 73
  • 1
  • 1
  • 6

1 Answers1

10

I'm not so keen on the total_memory idea it's somewhat of an indirect quantity. Maybe it makes more sense in the context of whatever the magical daemon is though.

For tunable attributes like total memory, I pretty much would do as you have laid out in the question by adding a sensible default value to attributes/default.rb (cuts down on support questions when someone forgets to explicitly set a value) and override with environment, role or node-specific values where necessary.

It's possible to do arithmetic inside the ERB file like this:

memory = <%= (node['memory']['total'][0..-3].to_i / 1024) * 
             node['magical-daemon']['memory'] %>

Ohai makes available the statistics from free(1) which includes the total memory in kB. node['memory']['total'] = '12312432kB' on my workstation.

I also try and use attributes with the lowest priority as much as possible, i.e prefer default over normal attributes, and normal attributes over override attributes. So,

  • choose a sensible recipe default where possible
  • use a default environment attribute (you use an override attribute in the example)
  • use a role attribute for groups of nodes (again you use an override attribute)
  • and finally default node attribute

See the attribute precedence link in the Chef wiki) for the order in which attributes override each other.

Using the lowest precedence default attributes where possible allows you to set the attribute value depending on the environment, role and node, but frees up the upper levels of precedence when you need to do something tricky.

Tim Potter
  • 1,764
  • 15
  • 15
  • 1. thanks for clearing up the order of the attributes! That helped a lot! 2. The magical-deamon could be a memcached or a mysqld where i like to set value according to the resources. 3. If i use your "ERB - Approach" i bind the cookbook to my configration which cripples reusability. 4. But i think you got a valid point with the "ERB - Approach", as I am being a ruby noob, is there a way i can include helpers into the ERB? Then i could set something like 'function(total_memory*75)' and use the helper to calculate. – Alex Jäger Feb 21 '12 at 09:02
  • 1
    Good point about memcached and mysql. It makes a lot of sense to try and use all the memory on the node. I realise I don't think that way because I mainly work on system-level recipes (networking and authentication) and the application guys do the service-level stuff. – Tim Potter Feb 21 '12 at 21:28
  • 1
    Tim's got the answer there I'd think. You can also see how elasticsearch does the same thing here : https://github.com/elasticsearch/cookbook-elasticsearch/blob/master/attributes/default.rb#L54 – jonathanserafini Oct 27 '14 at 23:04