Class: Chef::NodeMap

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

Constant Summary collapse

COLLISION_WARNING =
<<~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



177
178
179
180
181
182
183
184
185
186
187
# File 'lib/chef/node_map.rb', line 177

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



156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
# File 'lib/chef/node_map.rb', line 156

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)

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

Returns:

  • (Object)

    Class



121
122
123
124
125
126
127
# File 'lib/chef/node_map.rb', line 121

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)

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

Returns:

  • (Object)

    Class



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

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



208
209
210
211
212
213
214
215
# File 'lib/chef/node_map.rb', line 208

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



194
195
196
197
198
199
200
# File 'lib/chef/node_map.rb', line 194

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, chef_version: nil, target_mode: 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

  • chef_version (String)

    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



58
59
60
61
62
63
64
65
66
67
68
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
# File 'lib/chef/node_map.rb', line 58

def set(key, klass, platform: nil, platform_version: nil, platform_family: nil, os: nil, canonical: nil, override: nil, chef_version: nil, target_mode: nil, &block)
  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[:target_mode] = target_mode

  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.
  if 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
    Chef::Log.warn( COLLISION_WARNING % { type: type_of_thing, key: key, type_caps: type_of_thing.capitalize } )
  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