Class: Chef::NodeMap

Inherits:
Object
  • Object
show all
Defined in:
lib/chef/node_map.rb

Constant Summary collapse

COLLISION_WARNING_14 =
<<~EOH.gsub(/\s+/, " ").strip
      %{type_caps} %{key} from a cookbook is overriding the %{type} from the client.  Please upgrade your cookbook
        or remove the cookbook from your run_list before the next major release of Chef.
EOH
COLLISION_WARNING_15 =
<<~EOH.gsub(/\s+/, " ").strip
      %{type_caps} %{key} from the client is overriding the %{type} from a cookbook.  Please upgrade your cookbook
        or remove the cookbook from your run_list.
EOH

Instance Method Summary collapse

Instance Method Details

#delete_canonical(key, klass) ⇒ Object

This method is part of a private API. You should avoid using this method if possible, as it may be removed or be changed in the future.

Seriously, don’t use this, it’s nearly certain to change on you

Returns:

  • remaining



195
196
197
198
199
200
201
202
203
204
205
# File 'lib/chef/node_map.rb', line 195

def delete_canonical(key, klass)
  remaining = map[key]
  if remaining
    remaining.delete_if { |matcher| matcher[:canonical] && Array(matcher[:klass]) == Array(klass) }
    if remaining.empty?
      map.delete(key)
      remaining = nil
    end
  end
  remaining
end

#delete_class(klass) ⇒ Hash

Remove a class from all its matchers in the node_map, will remove mappings completely if its the last matcher left

Note that this leaks the internal structure out a bit, but the main consumer of this (poise/halite) cares only about the keys in the returned Hash.

Parameters:

  • klass (Class)

    the class to seek and destroy

Returns:

  • (Hash)

    deleted entries in the same format as the @map



174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
# File 'lib/chef/node_map.rb', line 174

def delete_class(klass)
  raise "please use a Class type for the klass argument" unless klass.is_a?(Class)
  deleted = {}
  map.each do |key, matchers|
    deleted_matchers = []
    matchers.delete_if do |matcher|
      # because matcher[:klass] may be a string (which needs to die), coerce both to strings to compare somewhat canonically
      if matcher[:klass].to_s == klass.to_s
        deleted_matchers << matcher
        true
      end
    end
    deleted[key] = deleted_matchers unless deleted_matchers.empty?
    map.delete(key) if matchers.empty?
  end
  deleted
end

#get(node, key, canonical: nil) ⇒ Object

Get a value from the NodeMap via applying the node to the filters that were set on the key.

Parameters:

  • node (Chef::Node)

    The Chef::Node object for the run, or ‘nil` to ignore all filters.

  • key (Object)

    Key to look up

  • canonical (Boolean) (defaults to: nil)

    ‘true` or `false` to match canonical or non-canonical values only. `nil` to ignore canonicality. Default: `nil`

Returns:

  • (Object)

    Class



139
140
141
142
143
144
145
# File 'lib/chef/node_map.rb', line 139

def get(node, key, canonical: nil)
  return nil unless map.key?(key)
  map[key].map do |matcher|
    return matcher[:klass] if node_matches?(node, matcher) && canonical_matches?(canonical, matcher)
  end
  nil
end

#list(node, key, canonical: nil) ⇒ Object

List all matches for the given node and key from the NodeMap, from most-recently added to oldest.

Parameters:

  • node (Chef::Node)

    The Chef::Node object for the run, or ‘nil` to ignore all filters.

  • key (Object)

    Key to look up

  • canonical (Boolean) (defaults to: nil)

    ‘true` or `false` to match canonical or non-canonical values only. `nil` to ignore canonicality. Default: `nil`

Returns:

  • (Object)

    Class



159
160
161
162
163
164
# File 'lib/chef/node_map.rb', line 159

def list(node, key, canonical: nil)
  return [] unless map.key?(key)
  map[key].select do |matcher|
    node_matches?(node, matcher) && canonical_matches?(canonical, matcher)
  end.map { |matcher| matcher[:klass] }
end

#lock!void

This method returns an undefined value.

Set this map to locked mode. This is used to prevent future overwriting of existing names.

Since:

  • 14.2



226
227
228
229
230
231
232
233
# File 'lib/chef/node_map.rb', line 226

def lock!
  map.each do |key, matchers|
    matchers.each do |matcher|
      matcher[:locked] = true
    end
  end
  @locked = true
end

#locked?Boolean

Check if this map has been locked.

Returns:

  • (Boolean)

Since:

  • 14.2



212
213
214
215
216
217
218
# File 'lib/chef/node_map.rb', line 212

def locked?
  if defined?(@locked)
    @locked
  else
    false
  end
end

#set(key, klass, platform: nil, platform_version: nil, platform_family: nil, os: nil, canonical: nil, override: nil, allow_cookbook_override: false, __core_override__: false, chef_version: nil) {|node| ... } ⇒ NodeMap

Set a key/value pair on the map with a filter. The filter must be true when applied to the node in order to retrieve the value.

Parameters:

  • key (Object)

    Key to store

  • value (Object)

    Value associated with the key

  • filters (Hash)

    Node filter options to apply to key retrieval

  • allow_cookbook_override (Boolean, String) (defaults to: false)

    Allow a cookbook to add to this key even in locked mode. If a string is given, it should be a Gem::Requirement-compatible value indicating for which Chef versions an override from cookbooks is allowed.

  • __core_override__ (Boolean) (defaults to: false)

    Advanced-mode override to add to a key even in locked mode.

  • chef_version (String) (defaults to: nil)

    version constraint to match against the running Chef::VERSION

Yields:

  • (node)

    Arbitrary node filter as a block which takes a node argument

Returns:

  • (NodeMap)

    Returns self for possible chaining



69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
# File 'lib/chef/node_map.rb', line 69

def set(key, klass, platform: nil, platform_version: nil, platform_family: nil, os: nil, canonical: nil, override: nil, allow_cookbook_override: false, __core_override__: false, chef_version: nil, &block) # rubocop:disable Lint/UnderscorePrefixedVariableName
  new_matcher = { klass: klass }
  new_matcher[:platform] = platform if platform
  new_matcher[:platform_version] = platform_version if platform_version
  new_matcher[:platform_family] = platform_family if platform_family
  new_matcher[:os] = os if os
  new_matcher[:block] = block if block
  new_matcher[:canonical] = canonical if canonical
  new_matcher[:override] = override if override
  new_matcher[:cookbook_override] = allow_cookbook_override
  new_matcher[:core_override] = __core_override__

  if chef_version && Chef::VERSION !~ chef_version
    return map
  end

  # Check if the key is already present and locked, unless the override is allowed.
  # The checks to see if we should reject, in order:
  # 1. Core override mode is not set.
  # 2. The key exists.
  # 3. At least one previous `provides` is now locked.
  # 4. No previous `provides` had `allow_cookbook_override`, either set to
  #    true or with a string version matcher that still matches Chef::VERSION
  if !__core_override__ && map[key] && map[key].any? { |matcher| matcher[:locked] } && !map[key].any? { |matcher| matcher[:cookbook_override].is_a?(String) ? Chef::VERSION =~ matcher[:cookbook_override] : matcher[:cookbook_override] }
    # If we ever use locked mode on things other than the resource and provider handler maps, this probably needs a tweak.
    type_of_thing = if klass < Chef::Resource
                      "resource"
                    elsif klass < Chef::Provider
                      "provider"
                    else
                      klass.superclass.to_s
                    end
    # For now, only log the warning.
    Chef.deprecated(:map_collision, COLLISION_WARNING_14 % { type: type_of_thing, key: key, type_caps: type_of_thing.capitalize })
    # In 15.0, uncomment this and remove the log above.
    # Chef.deprecated(:map_collision, COLLISION_WARNING_15 % {type: type_of_thing, key: key, type_caps: type_of_thing.capitalize}))
    # return
  end

  # The map is sorted in order of preference already; we just need to find
  # our place in it (just before the first value with the same preference level).
  insert_at = nil
  map[key] ||= []
  map[key].each_with_index do |matcher, index|
    cmp = compare_matchers(key, new_matcher, matcher)
    if cmp && cmp <= 0
      insert_at = index
      break
    end
  end
  if insert_at
    map[key].insert(insert_at, new_matcher)
  else
    map[key] << new_matcher
  end
  map
end