Class: Chef::Formatters::ErrorInspectors::CompileErrorInspector

Inherits:
Object
  • Object
show all
Defined in:
lib/chef/formatters/error_inspectors/compile_error_inspector.rb

Overview

CompileErrorInspector

Wraps exceptions that occur during the compile phase of a Chef run and tries to find the code responsible for the error.

Instance Attribute Summary collapse

Instance Method Summary collapse

Constructor Details

#initialize(path, exception) ⇒ CompileErrorInspector

Returns a new instance of CompileErrorInspector.



31
32
33
34
35
36
37
# File 'lib/chef/formatters/error_inspectors/compile_error_inspector.rb', line 31

def initialize(path, exception)
  @path, @exception = path, exception
  @backtrace_lines_in_cookbooks = nil
  @file_lines = nil
  @culprit_backtrace_entry = nil
  @culprit_line = nil
end

Instance Attribute Details

#exceptionObject (readonly)

Returns the value of attribute exception.



29
30
31
# File 'lib/chef/formatters/error_inspectors/compile_error_inspector.rb', line 29

def exception
  @exception
end

#pathObject (readonly)

Returns the value of attribute path.



28
29
30
# File 'lib/chef/formatters/error_inspectors/compile_error_inspector.rb', line 28

def path
  @path
end

Instance Method Details

#add_explanation(error_description) ⇒ Object



39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
# File 'lib/chef/formatters/error_inspectors/compile_error_inspector.rb', line 39

def add_explanation(error_description)
  error_description.section(exception.class.name, exception.message)

  if found_error_in_cookbooks?
    traceback = filtered_bt.map { |line| "  #{line}" }.join("\n")
    error_description.section("Cookbook Trace: (most recent call first)", traceback)
    error_description.section("Relevant File Content:", context)
  end

  if exception_message_modifying_frozen?
    msg = <<-MESSAGE
    Ruby objects are often frozen to prevent further modifications
    when they would negatively impact the process (e.g. values inside
    Ruby's ENV class) or to prevent polluting other objects when default
    values are passed by reference to many instances of an object (e.g.
    the empty Array as a Chef resource default, passed by reference
    to every instance of the resource).

    Chef uses Object#freeze to ensure the default values of properties
    inside Chef resources are not modified, so that when a new instance
    of a Chef resource is created, and Object#dup copies values by
    reference, the new resource is not receiving a default value that
    has been by a previous instance of that resource.

    Instead of modifying an object that contains a default value for all
    instances of a Chef resource, create a new object and assign it to
    the resource's parameter, e.g.:

    fruit_basket = resource(:fruit_basket, 'default')

    # BAD: modifies 'contents' object for all new fruit_basket instances
    fruit_basket.contents << 'apple'

    # GOOD: allocates new array only owned by this fruit_basket instance
    fruit_basket.contents %w(apple)

    MESSAGE

    error_description.section("Additional information:", msg.gsub(/^ {6}/, ""))
  end
end

#backtrace_lines_in_cookbooksObject



136
137
138
139
140
141
142
143
144
# File 'lib/chef/formatters/error_inspectors/compile_error_inspector.rb', line 136

def backtrace_lines_in_cookbooks
  @backtrace_lines_in_cookbooks ||=
    begin
      filters = Array(Chef::Config.cookbook_path).map { |p| /^#{Regexp.escape(p)}/i }
      r = exception.backtrace.select { |line| filters.any? { |filter| line =~ filter } }
      Chef::Log.trace("Filtered backtrace of compile error: #{r.join(",")}")
      r
    end
end

#contextObject



81
82
83
84
85
86
87
88
89
90
# File 'lib/chef/formatters/error_inspectors/compile_error_inspector.rb', line 81

def context
  context_lines = []
  context_lines << "#{culprit_file}:\n\n"
  Range.new(display_lower_bound, display_upper_bound).each do |i|
    line_nr = (i + 1).to_s.rjust(3)
    indicator = (i + 1) == culprit_line ? ">> " : ":  "
    context_lines << "#{line_nr}#{indicator}#{file_lines[i]}"
  end
  context_lines.join("")
end

#culprit_backtrace_entryObject



108
109
110
111
112
113
114
# File 'lib/chef/formatters/error_inspectors/compile_error_inspector.rb', line 108

def culprit_backtrace_entry
  @culprit_backtrace_entry ||= begin
    bt_entry = filtered_bt.first
    Chef::Log.trace("Backtrace entry for compile error: '#{bt_entry}'")
    bt_entry
  end
end

#culprit_fileObject



124
125
126
# File 'lib/chef/formatters/error_inspectors/compile_error_inspector.rb', line 124

def culprit_file
  @culprit_file ||= culprit_backtrace_entry[/^((?:.\:)?[^:]+):(\d+)/, 1]
end

#culprit_lineObject



116
117
118
119
120
121
122
# File 'lib/chef/formatters/error_inspectors/compile_error_inspector.rb', line 116

def culprit_line
  @culprit_line ||= begin
    line_number = culprit_backtrace_entry[/^(?:.\:)?[^:]+:(\d+)/, 1].to_i
    Chef::Log.trace("Line number of compile error: '#{line_number}'")
    line_number
  end
end

#display_lower_boundObject



92
93
94
95
96
# File 'lib/chef/formatters/error_inspectors/compile_error_inspector.rb', line 92

def display_lower_bound
  lower = (culprit_line - 8)
  lower = 0 if lower < 0
  lower
end

#display_upper_boundObject



98
99
100
101
102
# File 'lib/chef/formatters/error_inspectors/compile_error_inspector.rb', line 98

def display_upper_bound
  upper = (culprit_line + 8)
  upper = file_lines.size if upper > file_lines.size
  upper
end

#exception_message_modifying_frozen?Boolean

Returns:

  • (Boolean)


146
147
148
# File 'lib/chef/formatters/error_inspectors/compile_error_inspector.rb', line 146

def exception_message_modifying_frozen?
  exception.message.include?("can't modify frozen")
end

#file_linesObject



104
105
106
# File 'lib/chef/formatters/error_inspectors/compile_error_inspector.rb', line 104

def file_lines
  @file_lines ||= IO.readlines(culprit_file)
end

#filtered_btObject



128
129
130
# File 'lib/chef/formatters/error_inspectors/compile_error_inspector.rb', line 128

def filtered_bt
  backtrace_lines_in_cookbooks.count > 0 ? backtrace_lines_in_cookbooks : exception.backtrace
end

#found_error_in_cookbooks?Boolean

Returns:

  • (Boolean)


132
133
134
# File 'lib/chef/formatters/error_inspectors/compile_error_inspector.rb', line 132

def found_error_in_cookbooks?
  !backtrace_lines_in_cookbooks.empty?
end