# frozen_string_literal: true require_relative '../../puppet' require_relative '../../puppet/pops' require_relative '../../puppet/pops/evaluator/json_strict_literal_evaluator' class Puppet::InfoService::ClassInformationService def initialize @file_to_result = {} @parser = Puppet::Pops::Parser::EvaluatingParser.new() end def classes_per_environment(env_file_hash) # In this version of puppet there is only one way to parse manifests, as feature switches per environment # are added or removed, this logic needs to change to compute the result per environment with the correct # feature flags in effect. unless env_file_hash.is_a?(Hash) raise ArgumentError, _('Given argument must be a Hash') end result = {} # for each environment # for each file # if file already processed, use last result or error # env_file_hash.each do |env, files| env_result = result[env] = {} files.each do |f| env_result[f] = result_of(f) end end result end private def type_parser Puppet::Pops::Types::TypeParser.singleton end def literal_evaluator @@literal_evaluator ||= Puppet::Pops::Evaluator::JsonStrictLiteralEvaluator.new end def result_of(f) entry = @file_to_result[f] if entry.nil? @file_to_result[f] = entry = parse_file(f) end entry end def parse_file(f) return { :error => _("The file %{f} does not exist") % { f: f } } unless Puppet::FileSystem.exist?(f) begin parse_result = @parser.parse_file(f) { :classes => parse_result.definitions.select { |d| d.is_a?(Puppet::Pops::Model::HostClassDefinition) }.map do |d| { :name => d.name, :params => d.parameters.map { |p| extract_param(p) } } end } rescue StandardError => e { :error => e.message } end end def extract_param(p) extract_default(extract_type({ :name => p.name }, p), p) end def extract_type(structure, p) return structure if p.type_expr.nil? structure[:type] = typeexpr_to_string(p.type_expr) structure end def extract_default(structure, p) value_expr = p.value return structure if value_expr.nil? default_value = value_as_literal(value_expr) structure[:default_literal] = default_value unless default_value.nil? structure[:default_source] = extract_value_source(value_expr) structure end def typeexpr_to_string(type_expr) begin type_parser.interpret_any(type_expr, nil).to_s rescue Puppet::ParseError # type is to complex - contains expressions that are not literal nil end end def value_as_literal(value_expr) catch(:not_literal) do return literal_evaluator.literal(value_expr) end nil end # Extracts the source for the expression def extract_value_source(value_expr) value_expr.locator.extract_tree_text(value_expr) end end