# frozen_string_literal: true class Puppet::Parser::Relationship attr_accessor :source, :target, :type PARAM_MAP = {:relationship => :before, :subscription => :notify} def arrayify(resources, left) case resources when Puppet::Pops::Evaluator::Collectors::AbstractCollector # on the LHS, go as far left as possible, else whatever the collected result is left ? leftmost_alternative(resources) : resources.collected.values when Array resources else [resources] end end def evaluate(catalog) arrayify(source, true).each do |s| arrayify(target, false).each do |t| mk_relationship(s, t, catalog) end end end def initialize(source, target, type) @source, @target, @type = source, target, type end def param_name PARAM_MAP[type] || raise(ArgumentError, _("Invalid relationship type %{relationship_type}") % { relationship_type: type }) end def mk_relationship(source, target, catalog) source_ref = canonical_ref(source) target_ref = canonical_ref(target) rel_param = param_name source_resource = catalog.resource(*source_ref) unless source_resource raise ArgumentError, _("Could not find resource '%{source}' for relationship on '%{target}'") % { source: source.to_s, target: target.to_s } end unless catalog.resource(*target_ref) raise ArgumentError, _("Could not find resource '%{target}' for relationship from '%{source}'") % { target: target.to_s, source: source.to_s } end Puppet.debug {"Adding relationship from #{source} to #{target} with '#{param_name}'"} if source_resource[rel_param].class != Array source_resource[rel_param] = [source_resource[rel_param]].compact end source_resource[rel_param] << (target_ref[1].nil? ? target_ref[0] : "#{target_ref[0]}[#{target_ref[1]}]") end private # Finds the leftmost alternative for a collector (if it is empty, try its empty alternative recursively until there is # either nothing left, or a non empty set is found. # def leftmost_alternative(x) if x.is_a?(Puppet::Pops::Evaluator::Collectors::AbstractCollector) collected = x.collected return collected.values unless collected.empty? adapter = Puppet::Pops::Adapters::EmptyAlternativeAdapter.get(x) adapter.nil? ? [] : leftmost_alternative(adapter.empty_alternative) elsif x.is_a?(Array) && x.size == 1 && x[0].is_a?(Puppet::Pops::Evaluator::Collectors::AbstractCollector) leftmost_alternative(x[0]) else x end end # Turns a PResourceType or PClassType into an array [type, title] and all other references to [ref, nil] # This is needed since it is not possible to find resources in the catalog based on the type system types :-( # (note, the catalog is also used on the agent side) def canonical_ref(ref) case ref when Puppet::Pops::Types::PResourceType [ref.type_name, ref.title] when Puppet::Pops::Types::PClassType ['class', ref.class_name] else [ref.to_s, nil] end end end