+++ title = "Attribute Precedence" draft = false gh_repo = "chef-web-docs"
[menu] [menu.infra] title = "Attribute Precedence" identifier = "chef_infra/cookbook_reference/attributes/attribute_precedence" parent = "chef_infra/cookbook_reference/attributes" +++
Chef Infra Client applies attributes in the following order:
Application Order (Last One Wins) | Attribute Type | Source Order |
---|---|---|
1 | default |
Cookbook attribute fileRecipeEnvironmentRole |
2 | force_default |
Cookbook attribute fileRecipe |
3 | normal |
JSON file passed with chef-client -j Cookbook attribute fileRecipe |
4 | override |
Cookbook attribute fileRecipeRoleEnvironment |
5 | force_override |
Cookbook attribute fileRecipe |
6 | automatic |
Identified 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.
{{< /note >}}
Attribute precedence, viewed from the same perspective as the overview diagram, where the numbers in the diagram match the order of attribute precedence:
Attribute precedence, when viewed as a table:
Attribute Files | Node/Recipe | Environment | Role | Ohai Data | |
---|---|---|---|---|---|
default | 1 | 2 | 3 | 4 | |
force_default | 5 | 6 | |||
normal | 7 | 8 | |||
override | 9 | 10 | 12 | 11 | |
force_override | 13 | 14 | |||
automatic | 15 |
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!"
Attribute precedence levels may be:
- Removed for a specific, named attribute precedence level.
- Removed for all attribute precedence levels.
- Fully assigned attributes.
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.
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
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.
{{< /note >}}
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
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'] = {...}
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}