Class: ChefCore::CLIUX::UI::ErrorPrinter

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

Constant Summary collapse

DEFAULT_ERROR_NO =
"CHEFINT001".freeze

Instance Attribute Summary collapse

Class Method Summary collapse

Instance Method Summary collapse

Constructor Details

#initialize(wrapper: nil, config: nil, exception: nil) ⇒ ErrorPrinter

Returns a new instance of ErrorPrinter.



96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
# File 'lib/chef_core/cliux/ui/error_printer.rb', line 96

def initialize(wrapper: nil, config: nil, exception: nil)
  @exception = exception || wrapper.contained_exception
  @target_host = wrapper.target_host || target_host
  @command = @exception.respond_to?(:command) ? @exception.command : nil
  @pastel = Pastel.new
  @content = StringIO.new
  @config = config
  @id = if @exception.is_a? ChefCore::Error
          @exception.id
        else
          DEFAULT_ERROR_NO
        end
  @translation = ChefCore::Text::ErrorTranslation.new(id)
rescue => e
  ErrorPrinter.dump_unexpected_error(e)
  exit! 128
end

Instance Attribute Details

#configObject (readonly)

Returns the value of attribute config.



29
30
31
# File 'lib/chef_core/cliux/ui/error_printer.rb', line 29

def config
  @config
end

#exceptionObject (readonly)

Returns the value of attribute exception.



29
30
31
# File 'lib/chef_core/cliux/ui/error_printer.rb', line 29

def exception
  @exception
end

#idObject (readonly)

Returns the value of attribute id.



29
30
31
# File 'lib/chef_core/cliux/ui/error_printer.rb', line 29

def id
  @id
end

#pastelObject (readonly)

Returns the value of attribute pastel.



29
30
31
# File 'lib/chef_core/cliux/ui/error_printer.rb', line 29

def pastel
  @pastel
end

#target_hostObject (readonly)

Returns the value of attribute target_host.



29
30
31
# File 'lib/chef_core/cliux/ui/error_printer.rb', line 29

def target_host
  @target_host
end

#translationObject (readonly)

Returns the value of attribute translation.



29
30
31
# File 'lib/chef_core/cliux/ui/error_printer.rb', line 29

def translation
  @translation
end

Class Method Details

.capture_multiple_failures(e, config) ⇒ Object



56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
# File 'lib/chef_core/cliux/ui/error_printer.rb', line 56

def self.capture_multiple_failures(e, config)
  e.params << config[:error_output_path] # Tell the operator where to find this info
  File.open(config[:error_output_path], "w") do |out|
    e.jobs.each do |j|
      wrapped = ChefCore::Errors::StandardErrorResolver.wrap_exception(j.exception, j.target_host)
      ep = ErrorPrinter.new(wrapper: wrapped, config: config)
      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.



86
87
88
89
90
91
92
93
94
# File 'lib/chef_core/cliux/ui/error_printer.rb', line 86

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_core/cliux/ui/error_printer.rb', line 185

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

.show_error(e, config) ⇒ Object



40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
# File 'lib/chef_core/cliux/ui/error_printer.rb', line 40

def self.show_error(e, config)
  # Name is misleading - it's unwrapping but also doing further
  # error resolution for common errors:
  unwrapped = ChefCore::Errors::StandardErrorResolver.unwrap_exception(e)
  if unwrapped.class == ChefCore::MultiJobFailure
    capture_multiple_failures(unwrapped, config)
  end
  formatter = ErrorPrinter.new(wrapper: e,
                               exception: unwrapped,
                               config: config)

  Terminal.output(formatter.format_error)
rescue => ex
  dump_unexpected_error(ex)
end

.write_backtrace(e, args, config) ⇒ Object



74
75
76
77
78
79
80
81
82
# File 'lib/chef_core/cliux/ui/error_printer.rb', line 74

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

Instance Method Details

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



249
250
251
252
253
# File 'lib/chef_core/cliux/ui/error_printer.rb', line 249

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



255
256
257
258
259
260
261
262
263
# File 'lib/chef_core/cliux/ui/error_printer.rb', line 255

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_core/cliux/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



231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
# File 'lib/chef_core/cliux/ui/error_printer.rb', line 231

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



146
147
148
149
150
151
152
153
154
# File 'lib/chef_core/cliux/ui/error_printer.rb', line 146

def format_body
  if exception.is_a? ChefCore::Error
    format_workstation_exception
  elsif exception.is_a? Train::Error
    format_train_exception
  else
    format_other_exception
  end
end

#format_decoratedObject



132
133
134
135
136
137
138
139
140
# File 'lib/chef_core/cliux/ui/error_printer.rb', line 132

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

#format_errorObject



114
115
116
117
118
119
120
121
# File 'lib/chef_core/cliux/ui/error_printer.rb', line 114

def format_error
  if translation.decorations
    format_decorated
  else
    format_undecorated
  end
  @content.string
end


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

def format_footer
  if translation.log
    if translation.stack
      t.footer.both(config[:log_location], config[:stack_trace_path])
    else
      t.footer.log_only(config[:log_location])
    end
  else
    if translation.stack
      t.footer.stack_only
    else
      t.footer.neither
    end
  end
end

#format_headerObject



142
143
144
# File 'lib/chef_core/cliux/ui/error_printer.rb', line 142

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

#format_other_exceptionObject



216
217
218
# File 'lib/chef_core/cliux/ui/error_printer.rb', line 216

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

#format_train_exceptionObject

TODO this gets moved to trainerrormapper or simply removed since

many of these issues are now handled in the RemoteTarget::ConnectionFailure


207
208
209
210
211
212
213
214
# File 'lib/chef_core/cliux/ui/error_printer.rb', line 207

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

#format_undecoratedObject



123
124
125
126
127
128
129
130
# File 'lib/chef_core/cliux/ui/error_printer.rb', line 123

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_core/cliux/ui/error_printer.rb', line 200

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

#formatted_hostObject



220
221
222
223
224
225
226
227
# File 'lib/chef_core/cliux/ui/error_printer.rb', line 220

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, config) ⇒ Object



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

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

#tObject

‘t’ is a convenience method for accessing error i18n error definitions. It also serves as a workaround to let us verify that correct text key lookups happen in unit tests.



34
35
36
# File 'lib/chef_core/cliux/ui/error_printer.rb', line 34

def t
  ChefCore::Text.errors
end