File Manager
Viewing File: provides_map.rb
#
# Author:: Adam Jacob (<adam@chef.io>)
# Author:: Daniel DeLeo (<dan@chef.io>)
# Copyright:: Copyright (c) 2008-2016 Chef Software, Inc.
# License:: Apache License, Version 2.0
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
#
require_relative "mash"
require_relative "exception"
require_relative "mixin/os"
require_relative "dsl"
module Ohai
class ProvidesMap
attr_reader :map
def initialize
@map = Mash.new
end
# @param [Ohai::DSL::Plugin] plugin
# @param [Array] provided_attributes
#
# @return void
#
def set_providers_for(plugin, provided_attributes)
unless plugin.is_a?(Ohai::DSL::Plugin)
raise ArgumentError, "set_providers_for only accepts Ohai Plugin classes (got: #{plugin})"
end
provided_attributes.each do |attribute|
attrs = @map
parts = normalize_and_validate(attribute)
parts.each do |part|
attrs[part] ||= Mash.new
attrs = attrs[part]
end
attrs[:_plugins] ||= []
attrs[:_plugins] << plugin
end
end
#
# gather plugins providing exactly the attributes listed
#
# @param [Array] attributes
#
# @return [Array] plugin names
#
def find_providers_for(attributes)
plugins = []
attributes.each do |attribute|
attrs = select_subtree(@map, attribute)
raise Ohai::Exceptions::AttributeNotFound, "No such attribute: \'#{attribute}\'" unless attrs
raise Ohai::Exceptions::ProviderNotFound, "Cannot find plugin providing attribute: \'#{attribute}\'" unless attrs[:_plugins]
plugins += attrs[:_plugins]
end
plugins.uniq
end
# This function is used to fetch the plugins for the attributes specified
# in the CLI options to Ohai.
# It first attempts to find the plugins for the attributes
# or the sub attributes given.
# If it can't find any, it looks for plugins that might
# provide the parents of a given attribute and returns the
# first parent found.
#
# @param [Array] attributes
#
# @return [Array] plugin names
def deep_find_providers_for(attributes)
plugins = []
attributes.each do |attribute|
attrs = select_subtree(@map, attribute)
unless attrs
attrs = select_closest_subtree(@map, attribute)
unless attrs
raise Ohai::Exceptions::AttributeNotFound, "No such attribute: \'#{attribute}\'"
end
end
collect_plugins_in(attrs, plugins)
end
plugins.uniq
end
# This function is used to fetch the plugins from
# 'depends "languages"' statements in plugins.
# It gathers plugins providing each of the attributes listed, or the
# plugins providing the closest parent attribute
#
# @param [Array] attributes
#
# @return [Array] plugin names
def find_closest_providers_for(attributes)
plugins = []
attributes.each do |attribute|
parts = normalize_and_validate(attribute)
raise Ohai::Exceptions::AttributeNotFound, "No such attribute: \'#{attribute}\'" unless @map[parts[0]]
attrs = select_closest_subtree(@map, attribute)
raise Ohai::Exceptions::ProviderNotFound, "Cannot find plugin providing attribute: \'#{attribute}\'" unless attrs
plugins += attrs[:_plugins]
end
plugins.uniq
end
def all_plugins(attribute_filter = nil)
if attribute_filter.nil?
collected = []
collect_plugins_in(map, collected).uniq
else
deep_find_providers_for(Array(attribute_filter))
end
end
private
def normalize_and_validate(attribute)
raise Ohai::Exceptions::AttributeSyntaxError, "Attribute contains duplicate '/' characters: #{attribute}" if %r{//+}.match?(attribute)
raise Ohai::Exceptions::AttributeSyntaxError, "Attribute contains a trailing '/': #{attribute}" if %r{/$}.match?(attribute)
parts = attribute.split("/")
parts.shift if parts.length != 0 && parts[0].length == 0 # attribute begins with a '/'
parts
end
def select_subtree(provides_map, attribute)
subtree = provides_map
parts = normalize_and_validate(attribute)
parts.each do |part|
return nil unless subtree[part]
subtree = subtree[part]
end
subtree
end
def select_closest_subtree(provides_map, attribute)
attr, *rest = normalize_and_validate(attribute)
# return nil if the top-level part of the attribute is not a
# top-level key in the provides_map (can't search any lower, and
# no information to return from this level of the search)
return nil unless provides_map[attr]
# attr is a key in the provides_map, search for the sub
# attribute under attr (if attribute = attr/sub1/sub2 then we
# search provides_map[attr] for sub1/sub2)
unless rest.empty?
subtree = select_closest_subtree(provides_map[attr], rest.join("/"))
end
if subtree.nil?
# no subtree of provides_map[attr] either 1) has a
# subattribute, 2) has a plugin providing a subattribute.
unless provides_map[attr][:_plugins]
# no providers for this attribute, this subtree won't do.
return nil # no providers for this attribute
else
# there are providers for this attribute, return its subtree
# to indicate this is the closest subtree
return provides_map[attr]
end
end
# we've already found a closest subtree or we've search all
# parent attributes of the original attribute and found no
# providers (subtree is nil in this case)
subtree
end
# Takes a section of the map, recursively searches for a `_plugins` key
# to find all the plugins in that section of the map. If given the whole
# map, it will find all of the plugins that have at least one provided
# attribute.
def collect_plugins_in(provides_map, collected)
provides_map.each_key do |plugin|
if plugin.eql?("_plugins")
collected.concat(provides_map[plugin])
else
collect_plugins_in(provides_map[plugin], collected)
end
end
collected
end
end
end