# class Chef::Formatters::ErrorInspectors::CookbookResolveErrorInspector

### Attributes

exception[R]
expanded_run_list[R]

### Public Class Methods

new(expanded_run_list, exception) click to toggle source

    # File lib/chef/formatters/error_inspectors/cookbook_resolve_error_inspector.rb, line 31
    def initialize(expanded_run_list, exception)
      @expanded_run_list = expanded_run_list
      @exception = exception
    end

### Public Instance Methods

add_explanation(error_description) click to toggle source

    # File lib/chef/formatters/error_inspectors/cookbook_resolve_error_inspector.rb, line 36
    def add_explanation(error_description)
      case exception
      when Net::HTTPServerException, Net::HTTPFatalError
        humanize_http_exception(error_description)
      when *NETWORK_ERROR_CLASSES
        describe_network_errors(error_description)
      else
        error_description.section("Unexpected Error:","#{exception.class.name}: #{exception.message}")
      end
    end

describe_412_error(error_description) click to toggle source

    # File lib/chef/formatters/error_inspectors/cookbook_resolve_error_inspector.rb, line 80
            def describe_412_error(error_description)
              explanation = ""
              error_reasons = extract_412_error_message

              # Prepare the error message if there is detailed information
              # about individual cookbooks.
              if !error_reasons.respond_to?(:key?)
                explanation << error_reasons.to_s
              else
                if error_reasons.key?("non_existent_cookbooks") && !Array(error_reasons["non_existent_cookbooks"]).empty?
                  explanation << "The following cookbooks are required by the client but don't exist on the server:\n"
                  Array(error_reasons["non_existent_cookbooks"]).each do |cookbook|
                    explanation << "* #{cookbook}\n"
                  end
                  explanation << "\n"
                end
                if error_reasons.key?("cookbooks_with_no_versions") && !Array(error_reasons["cookbooks_with_no_versions"]).empty?
                  explanation << "The following cookbooks exist on the server, but there is no version that meets\nthe version constraints in this environment:\n"
                  Array(error_reasons["cookbooks_with_no_versions"]).each do |cookbook|
                    explanation << "* #{cookbook}\n"
                  end
                  explanation << "\n"
                end
              end

              if !explanation.empty?
                error_description.section("Missing Cookbooks:", explanation)
              else
                # If we don't have any cookbook details print a more
                # generic error message.
                if error_reasons.respond_to?(:key?) && error_reasons["message"]
                  explanation << "Error message: #{error_reasons["message"]}\n"
                end

                explanation << <<EOM
    You might be able to resolve this issue with:
      1-) Removing cookbook versions that depend on deleted cookbooks.
      2-) Removing unused cookbook versions.
      3-) Pinning exact cookbook versions using environments.
    EOM
                error_description.section("Cookbook dependency resolution error:", explanation)
              end

              error_description.section("Expanded Run List:", expanded_run_list_ul)
            end

expanded_run_list_ul() click to toggle source

    # File lib/chef/formatters/error_inspectors/cookbook_resolve_error_inspector.rb, line 126
    def expanded_run_list_ul
      @expanded_run_list.map {|i| "* #{i}"}.join("\n")
    end

extract_412_error_message() click to toggle source

In my tests, the error from the server is double JSON encoded, but we should not rely on this not getting fixed.

Return should be a Hash like this:

    { "non_existent_cookbooks"     => ["nope"],
      "cookbooks_with_no_versions" => [],
      "message" => "Run list contains invalid items: no such cookbook nope."}

    # File lib/chef/formatters/error_inspectors/cookbook_resolve_error_inspector.rb, line 137
    def extract_412_error_message
      # Example:
      # "{\"error\":[\"{\\\"non_existent_cookbooks\\\":[\\\"nope\\\"],\\\"cookbooks_with_no_versions\\\":[],\\\"message\\\":\\\"Run list contains invalid items: no such cookbook nope.\\\"}\"]}"

      wrapped_error_message = attempt_json_parse(exception.response.body)
      unless wrapped_error_message.kind_of?(Hash) && wrapped_error_message.key?("error")
        return wrapped_error_message.to_s
      end

      error_description = Array(wrapped_error_message["error"]).first
      if error_description.kind_of?(Hash)
        return error_description
      end
      attempt_json_parse(error_description)
    end

humanize_http_exception(error_description) click to toggle source

    # File lib/chef/formatters/error_inspectors/cookbook_resolve_error_inspector.rb, line 47
            def humanize_http_exception(error_description)
              response = exception.response
              case response
              when Net::HTTPUnauthorized
                # TODO: this is where you'd see conflicts b/c of username/clientname stuff
                describe_401_error(error_description)
              when Net::HTTPForbidden
                # TODO: we're rescuing errors from Node.find_or_create
                # * could be no write on nodes container
                # * could be no read on the node
                error_description.section("Authorization Error","This client is not authorized to read some of the information required to
    access its coobooks (HTTP 403).

    To access its cookbooks, a client needs to be able to read its environment and
    all of the cookbooks in its expanded run list.
    ")
                error_description.section("Expanded Run List:", expanded_run_list_ul)
                error_description.section("Server Response:", format_rest_error)
              when Net::HTTPPreconditionFailed
                describe_412_error(error_description)
              when Net::HTTPBadRequest
                describe_400_error(error_description)
              when Net::HTTPNotFound
              when Net::HTTPInternalServerError
                describe_500_error(error_description)
              when Net::HTTPBadGateway, Net::HTTPServiceUnavailable
                describe_503_error(error_description)
              else
                describe_http_error(error_description)
              end
            end