Class: Bolt::Result

Inherits:
Object
  • Object
show all
Defined in:
lib/bolt/result.rb

Direct Known Subclasses

ApplyResult

Instance Attribute Summary collapse

Class Method Summary collapse

Instance Method Summary collapse

Constructor Details

#initialize(target, error: nil, message: nil, value: nil, action: 'action', object: nil) ⇒ Result

Returns a new instance of Result.



112
113
114
115
116
117
118
119
120
121
122
# File 'lib/bolt/result.rb', line 112

def initialize(target, error: nil, message: nil, value: nil, action: 'action', object: nil)
  @target = target
  @value = value || {}
  @action = action
  @object = object
  if error && !error.is_a?(Hash)
    raise "TODO: how did we get a string error"
  end
  @value['_error'] = error if error
  @value['_output'] = message if message
end

Instance Attribute Details

#actionObject (readonly)

Returns the value of attribute action.



8
9
10
# File 'lib/bolt/result.rb', line 8

def action
  @action
end

#objectObject (readonly)

Returns the value of attribute object.



8
9
10
# File 'lib/bolt/result.rb', line 8

def object
  @object
end

#targetObject (readonly)

Returns the value of attribute target.



8
9
10
# File 'lib/bolt/result.rb', line 8

def target
  @target
end

#valueObject (readonly)

Returns the value of attribute value.



8
9
10
# File 'lib/bolt/result.rb', line 8

def value
  @value
end

Class Method Details

._pcore_init_from_hashObject



94
95
96
# File 'lib/bolt/result.rb', line 94

def self._pcore_init_from_hash
  raise "Result shouldn't be instantiated from a pcore_init class method. How did this get called?"
end

.for_command(target, stdout, stderr, exit_code, action, command) ⇒ Object



25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
# File 'lib/bolt/result.rb', line 25

def self.for_command(target, stdout, stderr, exit_code, action, command)
  value = {
    'stdout' => stdout,
    'stderr' => stderr,
    'exit_code' => exit_code
  }
  unless exit_code == 0
    value['_error'] = {
      'kind' => 'puppetlabs.tasks/command-error',
      'issue_code' => 'COMMAND_ERROR',
      'msg' => "The command failed with exit code #{exit_code}",
      'details' => { 'exit_code' => exit_code }
    }
  end
  new(target, value: value, action: action, object: command)
end

.for_download(target, source, destination, download) ⇒ Object



82
83
84
85
86
87
# File 'lib/bolt/result.rb', line 82

def self.for_download(target, source, destination, download)
  msg   = "Downloaded '#{target.host}:#{source}' to '#{destination}'"
  value = { 'path' => download }

  new(target, value: value, message: msg, action: 'download', object: source)
end

.for_task(target, stdout, stderr, exit_code, task) ⇒ Object



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
# File 'lib/bolt/result.rb', line 42

def self.for_task(target, stdout, stderr, exit_code, task)
  stdout.force_encoding('utf-8') unless stdout.encoding == Encoding::UTF_8
  value = if stdout.valid_encoding?
            parse_hash(stdout) || { '_output' => stdout }
          else
            { '_error' => { 'kind' => 'puppetlabs.tasks/task-error',
                            'issue_code' => 'TASK_ERROR',
                            'msg' => 'The task result contained invalid UTF-8 on stdout',
                            'details' => {} } }
          end

  if exit_code != 0 && value['_error'].nil?
    msg = if stdout.empty?
            if stderr.empty?
              "The task failed with exit code #{exit_code} and no output"
            else
              "The task failed with exit code #{exit_code} and no stdout, but stderr contained:\n#{stderr}"
            end
          else
            "The task failed with exit code #{exit_code}"
          end
    value['_error'] = { 'kind' => 'puppetlabs.tasks/task-error',
                        'issue_code' => 'TASK_ERROR',
                        'msg' => msg,
                        'details' => { 'exit_code' => exit_code } }
  end
  new(target, value: value, action: 'task', object: task)
end

.for_upload(target, source, destination) ⇒ Object



78
79
80
# File 'lib/bolt/result.rb', line 78

def self.for_upload(target, source, destination)
  new(target, message: "Uploaded '#{source}' to '#{target.host}:#{destination}'", action: 'upload', object: source)
end

.from_asserted_args(target, value) ⇒ Object

Satisfies the Puppet datatypes API



90
91
92
# File 'lib/bolt/result.rb', line 90

def self.from_asserted_args(target, value)
  new(target, value: value)
end

.from_exception(target, exception, action: 'action') ⇒ Object



10
11
12
13
14
15
16
17
18
19
20
21
22
23
# File 'lib/bolt/result.rb', line 10

def self.from_exception(target, exception, action: 'action')
  if exception.is_a?(Bolt::Error)
    error = exception.to_h
  else
    error = {
      'kind' => 'puppetlabs.tasks/exception-error',
      'issue_code' => 'EXCEPTION',
      'msg' => exception.message,
      'details' => { 'class' => exception.class.to_s }
    }
    error['details']['stack_trace'] = exception.backtrace.join('\n') if exception.backtrace
  end
  Result.new(target, error: error, action: action)
end

.parse_hash(string) ⇒ Object



71
72
73
74
75
76
# File 'lib/bolt/result.rb', line 71

def self.parse_hash(string)
  value = JSON.parse(string)
  value if value.is_a? Hash
rescue JSON::ParserError
  nil
end

Instance Method Details

#==(other) ⇒ Object



146
147
148
# File 'lib/bolt/result.rb', line 146

def ==(other)
  eql?(other)
end

#[](key) ⇒ Object



142
143
144
# File 'lib/bolt/result.rb', line 142

def [](key)
  value[key]
end

#_pcore_init_from_hash(init_hash) ⇒ Object



98
99
100
101
# File 'lib/bolt/result.rb', line 98

def _pcore_init_from_hash(init_hash)
  opts = init_hash.reject { |k, _v| k == 'target' }
  initialize(init_hash['target'], opts.transform_keys(&:to_sym))
end

#_pcore_init_hashObject



103
104
105
106
107
108
109
110
# File 'lib/bolt/result.rb', line 103

def _pcore_init_hash
  { 'target' => @target,
    'error' => @value['_error'],
    'message' => @value['_output'],
    'value' => @value,
    'action' => @action,
    'object' => @object }
end

#eql?(other) ⇒ Boolean

Returns:

  • (Boolean)


136
137
138
139
140
# File 'lib/bolt/result.rb', line 136

def eql?(other)
  self.class == other.class &&
    target == other.target &&
    value == other.value
end

#errorObject

Warning: This will fail outside of a compilation. Use error_hash inside bolt. Is it crazy for this to behave differently outside a compiler?



202
203
204
205
206
207
# File 'lib/bolt/result.rb', line 202

def error
  if error_hash
    Puppet::DataTypes::Error.from_asserted_hash(error_hash)

  end
end

#error_hashObject

This allows access to errors outside puppet compilation it should be prefered over error in bolt code



195
196
197
# File 'lib/bolt/result.rb', line 195

def error_hash
  value['_error']
end

#generic_valueObject



132
133
134
# File 'lib/bolt/result.rb', line 132

def generic_value
  safe_value.reject { |k, _| %w[_error _output].include? k }
end

#messageObject



124
125
126
# File 'lib/bolt/result.rb', line 124

def message
  @value['_output']
end

#message?Boolean

Returns:

  • (Boolean)


128
129
130
# File 'lib/bolt/result.rb', line 128

def message?
  message && !message.strip.empty?
end

#ok?Boolean Also known as: ok, success?

Returns:

  • (Boolean)


187
188
189
# File 'lib/bolt/result.rb', line 187

def ok?
  error_hash.nil?
end

#safe_valueObject

This is the value with all non-UTF-8 characters removed, suitable for printing or converting to JSON. It should only be possible to have non-UTF-8 characters in stdout/stderr keys as they are not allowed from tasks but we scrub the whole thing just in case.



162
163
164
165
166
167
168
169
170
171
# File 'lib/bolt/result.rb', line 162

def safe_value
  Bolt::Util.walk_vals(value) do |val|
    if val.is_a?(String)
      # Replace invalid bytes with hex codes, ie. \xDE\xAD\xBE\xEF
      val.scrub { |c| c.bytes.map { |b| "\\x" + b.to_s(16).upcase }.join }
    else
      val
    end
  end
end

#statusObject



183
184
185
# File 'lib/bolt/result.rb', line 183

def status
  ok? ? 'success' : 'failure'
end

#to_dataObject



173
174
175
176
177
178
179
180
181
# File 'lib/bolt/result.rb', line 173

def to_data
  {
    "target" => @target.name,
    "action" => action,
    "object" => object,
    "status" => status,
    "value" => safe_value
  }
end

#to_json(opts = nil) ⇒ Object



150
151
152
# File 'lib/bolt/result.rb', line 150

def to_json(opts = nil)
  to_data.to_json(opts)
end

#to_sObject



154
155
156
# File 'lib/bolt/result.rb', line 154

def to_s
  to_json
end