Attribute Precedence
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’s 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:
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 |
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 don't 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’s 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 don't 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
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 don't 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 don't 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 don't 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}