summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorJohn Keiser <john@johnkeiser.com>2015-05-06 18:06:43 -0700
committerJohn Keiser <john@johnkeiser.com>2015-06-02 09:53:40 -0700
commitc298a6f52a11fcbe022a75b259cf40ca9168394c (patch)
tree54134a058317b6c59bb3cfa0e997e2db7fa39871
parent71c89b652687021c89a630a00224deccdc5e05a5 (diff)
downloadchef-c298a6f52a11fcbe022a75b259cf40ca9168394c.tar.gz
Add specificity matching
-rw-r--r--lib/chef/node_map.rb40
1 files changed, 36 insertions, 4 deletions
diff --git a/lib/chef/node_map.rb b/lib/chef/node_map.rb
index a7a0389a2a..a0af3b9169 100644
--- a/lib/chef/node_map.rb
+++ b/lib/chef/node_map.rb
@@ -50,11 +50,20 @@ class Chef
def set(key, value, filters = {}, &block)
validate_filter!(filters)
deprecate_filter!(filters)
+ new_matcher = { filters: filters, block: block, value: value }
@map[key] ||= []
- # we match on the first value we find, so we want to unshift so that the
- # last setter wins
- # FIXME: need a test for this behavior
- @map[key].unshift({ filters: filters, block: block, value: value })
+ # Decide where to insert the matcher; the new value is preferred over
+ # anything more specific (see `priority_of`) and is preferred over older
+ # values of the same specificity. (So all other things being equal,
+ # newest wins.)
+ insert_at = 0
+ @map[key].each_with_index do |matcher, index|
+ if specificity(new_matcher) >= specificity(matcher)
+ insert_at = index
+ break
+ end
+ end
+ @map[key].insert(insert_at, new_matcher)
self
end
@@ -89,6 +98,29 @@ class Chef
private
+ #
+ # Gives a value for "how specific" the matcher is.
+ # Things which specify more specific filters get a higher number
+ # (platform_version > platform > platform_family > os); things
+ # with a block have higher specificity than similar things without
+ # a block.
+ #
+ def specificity(matcher)
+ if matcher[:filters].has_key?(:platform_version)
+ specificity = 8
+ elsif matcher[:filters].has_key?(:platform)
+ specificity = 6
+ elsif matcher[:filters].has_key?(:platform_family)
+ specificity = 4
+ elsif matcher[:filters].has_key?(:os)
+ specificity = 2
+ else
+ specificity = 0
+ end
+ specificity += 1 if matcher[:block]
+ specificity
+ end
+
# only allow valid filter options
def validate_filter!(filters)
filters.each_key do |key|