Skip to main content

Attribute Precedence

Chef Infra Client applies attributes in the following order:

Application Order (Last One Wins)Attribute TypeSource Order
1defaultCookbook attribute fileRecipeEnvironmentRole
2force_defaultCookbook attribute fileRecipe
3normalJSON file passed with chef-client -jCookbook attribute fileRecipe
4overrideCookbook attribute fileRecipeRoleEnvironment
5force_overrideCookbook attribute fileRecipe
6automaticIdentified by Ohai at the start of a Chef Infra Client Run

Note

The attribute precedence order for the sources “roles” and “environments” are opposite in the default and override. The default order is environment then role. The override order is role then environment

Applying the role override first lets you use the same role in a set of environments. Applying the environment override on top of the role override lets you define a subset of these with environment-specific settings.

This is useful if you have an environment that is different within a sub-set of a role. For example, the role for an application server may exist in all environments, but one environment may use a different database server.

Attribute precedence, viewed from the same perspective as the overview diagram, where the numbers in the diagram match the order of attribute precedence:

image

Attribute precedence, when viewed as a table:

Attribute FilesNode/RecipeEnvironmentRoleOhai Data
default1234
force_default56
normal78
override9101211
force_override1314
automatic15

Examples

The following examples are listed from low to high precedence.

Default attribute in /attributes/default.rb

default['apache']['dir'] = '/etc/apache2'

Default attribute in node object in recipe

node.default['apache']['dir'] = '/etc/apache2'

Default attribute in /environments/environment_name.rb

default_attributes({ 'apache' => {'dir' => '/etc/apache2'}})

Default attribute in /roles/role_name.rb

default_attributes({ 'apache' => {'dir' => '/etc/apache2'}})

Normal attribute set as a cookbook attribute

normal['apache']['dir'] = '/etc/apache2'

Normal attribute set in a recipe

node.normal['apache']['dir'] = '/etc/apache2'

Override attribute in /attributes/default.rb

override['apache']['dir'] = '/etc/apache2'

Override attribute in /roles/role_name.rb

override_attributes({ 'apache' => {'dir' => '/etc/apache2'}})

Override attribute in /environments/environment_name.rb

override_attributes({ 'apache' => {'dir' => '/etc/apache2'}})

Override attribute in a node object (from a recipe)

node.override['apache']['dir'] = '/etc/apache2'

Ensure that a default attribute has precedence over other attributes

When a default attribute is set like this:

default['attribute'] = 'value'

any value set by a role or an environment will replace it. To prevent this value from being replaced, use the force_default attribute precedence:

force_default['attribute'] = 'I will crush you, role or environment attribute'

or:

default!['attribute'] = "The '!' means I win!"

Ensure that an override attribute has precedence over other attributes

When an override attribute is set like this:

override['attribute'] = 'value'

any value set by a role or an environment will replace it. To prevent this value from being replaced, use the force_override attribute precedence:

force_override['attribute'] = 'I will crush you, role or environment attribute'

or:

override!['attribute'] = "The '!' means I win!"

Change Attributes

Attribute precedence levels may be:

  • Removed for a specific, named attribute precedence level.
  • Removed for all attribute precedence levels.
  • Fully assigned attributes.

Remove Precedence Level

A specific attribute precedence level for default, normal, and override attributes may be removed by using one of the following syntax patterns.

For default attributes:

  • node.rm_default('foo', 'bar')

For normal attributes:

  • node.rm_normal('foo', 'bar')

For override attributes:

  • node.rm_override('foo', 'bar')

These patterns return the computed value of the key being deleted for the specified precedence level.

Examples

The following examples show how to remove a specific, named attribute precedence level.

Delete a default value when only default values exist

Given the following code structure under 'foo':

node.default['foo'] = {
  'bar' => {
    'baz' => 52,
    'thing' => 'stuff',
  },
  'bat' => {
    'things' => [5, 6],
  },
}

And some role attributes:

# Please do not ever do this in real code :)
node.role_default['foo']['bar']['thing'] = 'otherstuff'

And a force attribute:

node.force_default['foo']['bar']['thing'] = 'allthestuff'

When the default attribute precedence node['foo']['bar'] is removed:

node.rm_default('foo', 'bar') #=> {'baz' => 52, 'thing' => 'allthestuff'}

What is left under 'foo' is only 'bat':

node.attributes.combined_default['foo'] #=> {'bat' => { 'things' => [5,6] } }

Delete default without touching higher precedence attributes

Given the following code structure:

node.default['foo'] = {
  'bar' => {
    'baz' => 52,
    'thing' => 'stuff',
  },
  'bat' => {
    'things' => [5, 6],
  },
}

And some role attributes:

# Please do not ever do this in real code :)
node.role_default['foo']['bar']['thing'] = 'otherstuff'

And a force attribute:

node.force_default['foo']['bar']['thing'] = 'allthestuff'

And also some override attributes:

node.override['foo']['bar']['baz'] = 99

Same delete as before:

node.rm_default('foo', 'bar') #=> { 'baz' => 52, 'thing' => 'allthestuff' }

The other attribute precedence levels are unaffected:

node.attributes.combined_override['foo'] #=> { 'bar' => {'baz' => 99} }
node['foo'] #=> { 'bar' => {'baz' => 99}, 'bat' => { 'things' => [5,6] }

Delete override without touching lower precedence attributes

Given the following code structure, which has an override attribute:

node.override['foo'] = {
  'bar' => {
    'baz' => 52,
    'thing' => 'stuff',
  },
  'bat' => {
    'things' => [5, 6],
  },
}

with a single default value:

node.default['foo']['bar']['baz'] = 11

and a force at each attribute precedence:

node.force_default['foo']['bar']['baz'] = 55
node.force_override['foo']['bar']['baz'] = 99

Delete the override:

node.rm_override('foo', 'bar') #=> { 'baz' => 99, 'thing' => 'stuff' }

The other attribute precedence levels are unaffected:

node.attributes.combined_default['foo'] #=> { 'bar' => {'baz' => 55} }

Non-existent key deletes return nil

node.rm_default("no", "such", "thing") #=> nil

Remove All Levels

All attribute precedence levels may be removed by using the following syntax pattern:

  • node.rm('foo', 'bar')

Note

Using node['foo'].delete('bar') will throw an exception that points to the new API.

Examples

The following examples show how to remove all attribute precedence levels.

Delete all attribute precedence levels

Given the following code structure:

node.default['foo'] = {
  'bar' => {
    'baz' => 52,
    'thing' => 'stuff',
  },
  'bat' => {
    'things' => [5, 6],
  },
}

With override attributes:

node.override['foo']['bar']['baz'] = 999

Removing the 'bar' key returns the computed value:

node.rm('foo', 'bar') #=> {'baz' => 999, 'thing' => 'stuff'}

Looking at 'foo', all that’s left is the 'bat' entry:

node['foo'] #=> {'bat' => { 'things' => [5,6] } }

Non-existent key deletes return nil

node.rm_default("no", "such", "thing") #=> nil

Full Assignment

Use ! to clear out the key for the named attribute precedence level, and then complete the write by using one of the following syntax patterns:

  • node.default!['foo']['bar'] = {...}
  • node.force_default!['foo']['bar'] = {...}
  • node.normal!['foo']['bar'] = {...}
  • node.override!['foo']['bar'] = {...}
  • node.force_override!['foo']['bar'] = {...}

Examples

The following examples show how to remove all attribute precedence levels.

Just one component

Given the following code structure:

node.default['foo']['bar'] = {'a' => 'b'}
node.default!['foo']['bar'] = {'c' => 'd'}

The '!' caused the entire ‘bar’ key to be overwritten:

node['foo'] #=> {'bar' => {'c' => 'd'}

Multiple components; one “after”

Given the following code structure:

node.default['foo']['bar'] = {'a' => 'b'}
# Please do not ever do this in real code :)
node.role_default['foo']['bar'] = {'c' => 'd'}
node.default!['foo']['bar'] = {'d' => 'e'}

The '!' write overwrote the “cookbook-default” value of 'bar', but since role data is later in the resolution list, it was unaffected:

node['foo'] #=> {'bar' => {'c' => 'd', 'd' => 'e'}

Multiple components; all “before”

Given the following code structure:

node.default['foo']['bar'] = {'a' => 'b'}
# Please do not ever do this in real code :)
node.role_default['foo']['bar'] = {'c' => 'd'}
node.force_default!['foo']['bar'] = {'d' => 'e'}

With force_default! there is no other data under 'bar':

node['foo'] #=> {'bar' => {'d' => 'e'}

Multiple precedence levels

Given the following code structure:

node.default['foo'] = {
  'bar' => {
    'baz' => 52,
    'thing' => 'stuff',
  },
  'bat' => {
   'things' => [5, 6],
  },
}

And some attributes:

# Please do not ever do this in real code :)
node.role_default['foo']['bar']['baz'] = 55
node.force_default['foo']['bar']['baz'] = 66

And other precedence levels:

node.normal['foo']['bar']['baz'] = 88
node.override['foo']['bar']['baz'] = 99

With a full assignment:

node.default!['foo']['bar'] = {}

Role default and force default are left in default, plus other precedence levels:

node.attributes.combined_default['foo'] #=> {'bar' => {'baz' => 66}, 'bat'=>{'things'=>[5, 6]}}
node.attributes.normal['foo'] #=> {'bar' => {'baz' => 88}}
node.attributes.combined_override['foo'] #=> {'bar' => {'baz' => 99}}
node['foo']['bar'] #=> {'baz' => 99}

If force_default! is written:

node.force_default!['foo']['bar'] = {}

the difference is:

node.attributes.combined_default['foo'] #=> {'bat'=>{'things'=>[5, 6]}, 'bar' => {}}
node.attributes.normal['foo'] #=> {'bar' => {'baz' => 88}}
node.attributes.combined_override['foo'] #=> {'bar' => {'baz' => 99}}
node['foo']['bar'] #=> {'baz' => 99}
Edit this page on GitHub

Thank you for your feedback!

×