Class: ChefApply::UI::ErrorPrinter

Inherits:
Object
  • Object
show all
Defined in:
lib/chef_apply/ui/error_printer.rb

Constant Summary collapse

DEFAULT_ERROR_NO =
"CHEFINT001"

Instance Attribute Summary collapse

Class Method Summary collapse

Instance Method Summary collapse

Constructor Details

#initialize(wrapper, unwrapped = nil, target_host = nil) ⇒ ErrorPrinter

Returns a new instance of ErrorPrinter.



91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
# File 'lib/chef_apply/ui/error_printer.rb', line 91

def initialize(wrapper, unwrapped = nil, target_host = nil)
  @exception = unwrapped || wrapper.contained_exception
  @target_host = wrapper.target_host || target_host
  @pastel = Pastel.new
  @show_log = exception.respond_to?(:show_log) ? exception.show_log : true
  @show_stack = exception.respond_to?(:show_stack) ? exception.show_stack : true
  @content = StringIO.new
  @command = exception.respond_to?(:command) ? exception.command : nil
  @id = DEFAULT_ERROR_NO
  if exception.respond_to?(:id) && exception.id =~ /CHEF.*/
    @id = exception.id
  end
  if exception.respond_to?(:decorate)
    @decorate = exception.decorate
  else
    @decorate = true
  end
rescue => e
  ErrorPrinter.dump_unexpected_error(e)
  exit! 128
end

Instance Attribute Details

#exceptionObject (readonly)

Returns the value of attribute exception.



28
29
30
# File 'lib/chef_apply/ui/error_printer.rb', line 28

def exception
  @exception
end

#idObject (readonly)

Returns the value of attribute id.



28
29
30
# File 'lib/chef_apply/ui/error_printer.rb', line 28

def id
  @id
end

#pastelObject (readonly)

Returns the value of attribute pastel.



28
29
30
# File 'lib/chef_apply/ui/error_printer.rb', line 28

def pastel
  @pastel
end

#show_logObject (readonly)

Returns the value of attribute show_log.



28
29
30
# File 'lib/chef_apply/ui/error_printer.rb', line 28

def show_log
  @show_log
end

#show_stackObject (readonly)

Returns the value of attribute show_stack.



28
29
30
# File 'lib/chef_apply/ui/error_printer.rb', line 28

def show_stack
  @show_stack
end

#target_hostObject (readonly)

Returns the value of attribute target_host.



28
29
30
# File 'lib/chef_apply/ui/error_printer.rb', line 28

def target_host
  @target_host
end

Class Method Details

.capture_multiple_failures(e) ⇒ Object



50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
# File 'lib/chef_apply/ui/error_printer.rb', line 50

def self.capture_multiple_failures(e)
  out_file = ChefApply::Config.error_output_path
  e.params << out_file # Tell the operator where to find this info
  File.open(out_file, "w") do |out|
    e.jobs.each do |j|
      wrapped = ChefApply::Errors::StandardErrorResolver.wrap_exception(j.exception, j.target_host)
      ep = ErrorPrinter.new(wrapped)
      msg = ep.format_body().tr("\n", " ").gsub(/ {2,}/, " ").chomp.strip
      out.write("Host: #{j.target_host.hostname} ")
      if ep.exception.respond_to? :id
        out.write("Error: #{ep.exception.id}: ")
      else
        out.write(": ")
      end
      out.write("#{msg}\n")
    end
  end
end

.dump_unexpected_error(e) ⇒ Object

Use this to dump an an exception to output. useful if an error occurs in the error handling itself.



81
82
83
84
85
86
87
88
89
# File 'lib/chef_apply/ui/error_printer.rb', line 81

def self.dump_unexpected_error(e)
  Terminal.output "INTERNAL ERROR"
  Terminal.output "-=" * 30
  Terminal.output "Message:"
  Terminal.output e.message if e.respond_to?(:message)
  Terminal.output "Backtrace:"
  Terminal.output e.backtrace if e.respond_to?(:backtrace)
  Terminal.output "=-" * 30
end

.error_summary(e) ⇒ Object



185
186
187
188
189
190
191
192
193
194
195
196
197
198
# File 'lib/chef_apply/ui/error_printer.rb', line 185

def self.error_summary(e)
  if e.kind_of? ChefApply::Error
    # By convention, all of our defined messages have a short summary on the first line.
    ChefApply::Text.errors.send(e.id, *e.params).split("\n").first
  elsif e.kind_of? String
    e
  else
    if e.respond_to? :message
      e.message
    else
      ChefApply::Text.errors.UNKNOWN
    end
  end
end

.show_error(e) ⇒ Object



37
38
39
40
41
42
43
44
45
46
47
48
# File 'lib/chef_apply/ui/error_printer.rb', line 37

def self.show_error(e)
  # Name is misleading - it's unwrapping but also doing further
  # error resolution for common errors:
  unwrapped = ChefApply::Errors::StandardErrorResolver.unwrap_exception(e)
  if unwrapped.class == ChefApply::MultiJobFailure
    capture_multiple_failures(unwrapped)
  end
  formatter = ErrorPrinter.new(e, unwrapped)
  Terminal.output(formatter.format_error)
rescue => e
  dump_unexpected_error(e)
end

.write_backtrace(e, args) ⇒ Object



69
70
71
72
73
74
75
76
77
# File 'lib/chef_apply/ui/error_printer.rb', line 69

def self.write_backtrace(e, args)
  formatter = ErrorPrinter.new(e)
  out = StringIO.new
  formatter.add_backtrace_header(out, args)
  formatter.add_formatted_backtrace(out)
  formatter.save_backtrace(out)
rescue => ex
  dump_unexpected_error(ex)
end

Instance Method Details

#_format_single(out, exception, backtrace = nil) ⇒ Object



246
247
248
249
250
# File 'lib/chef_apply/ui/error_printer.rb', line 246

def _format_single(out, exception, backtrace = nil)
  out.puts "#{exception.class}: #{exception.message}"
  backtrace ||= exception.backtrace.to_a
  backtrace.each { |trace| out.puts "\t#{trace}" }
end

#_unique_trace(backtrace1, backtrace2) ⇒ Object



252
253
254
255
256
257
258
259
# File 'lib/chef_apply/ui/error_printer.rb', line 252

def _unique_trace(backtrace1, backtrace2)
  i = 1
  while i <= backtrace1.size && i <= backtrace2.size
    break if backtrace1[-i] != backtrace2[-i]
    i += 1
  end
  backtrace1[0..-i]
end

#add_backtrace_header(out, args) ⇒ Object



172
173
174
175
176
177
# File 'lib/chef_apply/ui/error_printer.rb', line 172

def add_backtrace_header(out, args)
  out.write("\n#{"-" * 80}\n")
  out.print("#{Time.now}: Error encountered while running the following:\n")
  out.print("  #{args.join(' ')}\n")
  out.print("Backtrace:\n")
end

#add_formatted_backtrace(out) ⇒ Object



228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
# File 'lib/chef_apply/ui/error_printer.rb', line 228

def add_formatted_backtrace(out)
  _format_single(out, exception)
  current_backtrace = exception.backtrace
  cause = exception.cause
  until cause.nil?
    cause_trace = _unique_trace(cause.backtrace.to_a, current_backtrace)
    out.print "Caused by: "
    _format_single(out, cause, cause_trace)
    backtrace_length = cause.backtrace.length
    if backtrace_length > cause_trace.length
      out.print "\t... #{backtrace_length - cause_trace.length} more"
    end
    out.print "\n"
    current_backtrace = cause.backtrace
    cause = cause.cause
  end
end

#format_bodyObject



145
146
147
148
149
150
151
152
153
# File 'lib/chef_apply/ui/error_printer.rb', line 145

def format_body
  if exception.kind_of? ChefApply::Error
    format_workstation_exception
  elsif exception.kind_of? Train::Error
    format_train_exception
  else
    format_other_exception
  end
end

#format_decoratedObject



131
132
133
134
135
136
137
138
139
# File 'lib/chef_apply/ui/error_printer.rb', line 131

def format_decorated
  @content << "\n"
  @content << format_header()
  @content << "\n\n"
  @content << format_body()
  @content << "\n"
  @content << format_footer()
  @content << "\n"
end

#format_errorObject



113
114
115
116
117
118
119
120
# File 'lib/chef_apply/ui/error_printer.rb', line 113

def format_error
  if @decorate
    format_decorated
  else
    format_undecorated
  end
  @content.string
end


155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
# File 'lib/chef_apply/ui/error_printer.rb', line 155

def format_footer
  if show_log
    if show_stack
      t.footer.both(ChefApply::Config.log.location,
                    ChefApply::Config.stack_trace_path)
    else
      t.footer.log_only(ChefApply::Config.log.location)
    end
  else
    if show_stack
      t.footer.stack_only
    else
      t.footer.neither
    end
  end
end

#format_headerObject



141
142
143
# File 'lib/chef_apply/ui/error_printer.rb', line 141

def format_header
  pastel.decorate(@id, :bold)
end

#format_other_exceptionObject



214
215
216
# File 'lib/chef_apply/ui/error_printer.rb', line 214

def format_other_exception
  t.send(DEFAULT_ERROR_NO, exception.message)
end

#format_train_exceptionObject



205
206
207
208
209
210
211
212
# File 'lib/chef_apply/ui/error_printer.rb', line 205

def format_train_exception
  backend, host = formatted_host()
  if host.nil?
    t.CHEFTRN002(exception.message)
  else
    t.CHEFTRN001(backend, host, exception.message)
  end
end

#format_undecoratedObject



122
123
124
125
126
127
128
129
# File 'lib/chef_apply/ui/error_printer.rb', line 122

def format_undecorated
  @content << "\n"
  @content << format_body()
  if @command
    @content << "\n"
    @content << @command.usage
  end
end

#format_workstation_exceptionObject



200
201
202
203
# File 'lib/chef_apply/ui/error_printer.rb', line 200

def format_workstation_exception
  params = exception.params
  t.send(@id, *params)
end

#formatted_hostObject



218
219
220
221
222
223
224
# File 'lib/chef_apply/ui/error_printer.rb', line 218

def formatted_host
  return nil if target_host.nil?
  cfg = target_host.config
  port = cfg[:port].nil? ? "" : ":#{cfg[:port]}"
  user = cfg[:user].nil? ? "" : "#{cfg[:user]}@"
  "#{user}#{target_host.hostname}#{port}"
end

#save_backtrace(output) ⇒ Object



179
180
181
182
183
# File 'lib/chef_apply/ui/error_printer.rb', line 179

def save_backtrace(output)
  File.open(ChefApply::Config.stack_trace_path, "ab+") do |f|
    f.write(output.string)
  end
end

#tObject

TODO define ‘t’ as a method is a temporary workaround to ensure that text key lookups are testable.



31
32
33
# File 'lib/chef_apply/ui/error_printer.rb', line 31

def t
  ChefApply::Text.errors
end