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.



116
117
118
119
120
121
122
123
124
125
126
# File 'lib/bolt/result.rb', line 116

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



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

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



86
87
88
89
90
91
# File 'lib/bolt/result.rb', line 86

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
70
71
72
73
# 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

  if value.key?('_sensitive')
    value['_sensitive'] = Puppet::Pops::Types::PSensitiveType::Sensitive.new(value['_sensitive'])
  end
  new(target, value: value, action: 'task', object: task)
end

.for_upload(target, source, destination) ⇒ Object



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

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



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

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



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

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



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

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

#[](key) ⇒ Object



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

def [](key)
  value[key]
end

#_pcore_init_from_hash(init_hash) ⇒ Object



102
103
104
105
# File 'lib/bolt/result.rb', line 102

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



107
108
109
110
111
112
113
114
# File 'lib/bolt/result.rb', line 107

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

#eql?(other) ⇒ Boolean

Returns:

  • (Boolean)


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

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?



206
207
208
209
210
211
# File 'lib/bolt/result.rb', line 206

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



199
200
201
# File 'lib/bolt/result.rb', line 199

def error_hash
  value['_error']
end

#generic_valueObject



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

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

#messageObject



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

def message
  @value['_output']
end

#message?Boolean

Returns:

  • (Boolean)


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

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

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

Returns:

  • (Boolean)


191
192
193
# File 'lib/bolt/result.rb', line 191

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.



166
167
168
169
170
171
172
173
174
175
# File 'lib/bolt/result.rb', line 166

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

#sensitiveObject



213
214
215
# File 'lib/bolt/result.rb', line 213

def sensitive
  value['_sensitive']
end

#statusObject



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

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

#to_dataObject



177
178
179
180
181
182
183
184
185
# File 'lib/bolt/result.rb', line 177

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

#to_json(opts = nil) ⇒ Object



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

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

#to_sObject



158
159
160
# File 'lib/bolt/result.rb', line 158

def to_s
  to_json
end