Module: SousVide::EventMethods

Included in:
Handler
Defined in:
lib/sous_vide/event_methods.rb

Overview

This module implements Chef event methods. It’s based on Chef::EventDispatch::Base.

Nested resources are explicitly ignored and the code flow will be as follows:

  1. resource_action_start

  2. resource_* events (possibly multiple)

  3. resource_action_complete

If :resource_action_start assigned @processing_now only then other methods will perform any processing.

:resource_action_completed unassigns @processing_now so all events will be ignored until :resource_action_start is called again.

Instance Method Summary collapse

Instance Method Details

#converge_completeObject

Called when the converge phase is finished (success)



167
168
169
170
171
172
# File 'lib/sous_vide/event_methods.rb', line 167

def converge_complete
  debug("Received :converge_completed")
  @run_success = true
  @run_completed_at = Time.now.strftime("%F %T")
  send_to_output!
end

#converge_failed(_exception) ⇒ Object

Called if the converge phase fails



176
177
178
179
180
181
182
183
184
185
# File 'lib/sous_vide/event_methods.rb', line 176

def converge_failed(_exception)
  debug("Received :converge_failed")
  @run_success = false
  @run_completed_at = Time.now.strftime("%F %T")
  @run_phase = "post-converge"
  debug("Changed run phase to 'post-converge'.")

  consume_unprocessed_resources!
  send_to_output!
end

#converge_startObject

Called before convergence starts



159
160
161
162
163
164
# File 'lib/sous_vide/event_methods.rb', line 159

def converge_start
  debug("Received :converge_start")
  debug("Changed run phase to 'converge'.")
  @run_phase = "converge"
  @run_name ||= [@run_started_at, @chef_node_role, @chef_node_ipv4, @run_id].join(" ")
end

#resource_action_start(new_resource, action, notification_type, notifying_resource) ⇒ Object

Called before action is executed on a resource.



19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
# File 'lib/sous_vide/event_methods.rb', line 19

def resource_action_start(new_resource, action, notification_type, notifying_resource)
  if nested?(new_resource) # ignore nested resources
    new_r_name = "#{new_resource.resource_name}[#{new_resource.name}]##{action}"
    debug("Received :resource_action_start on #{new_r_name}.",
          "It's a nested resource and will be ignored.")
    return false
  end

  # This is a delayed notification. From now on we are in 'delayed' run phase.
  if notification_type == :delayed && @run_phase != "delayed"
    debug("Changed run phase to 'delayed'.")
    @run_phase = "delayed"
  end

  @processing_now = create(chef_resource: new_resource, action: action)
  debug("Received :resource_action_start on #{@processing_now}.")

  @execution_order += 1
  @processing_now.execution_order = @execution_order
  @processing_now.execution_phase = @run_phase
  @processing_now.started_at = Time.now.strftime("%F %T")

  @processing_now.chef_resource_handle = new_resource

  @processing_now.before_notifications = new_resource.before_notifications.size
  @processing_now.immediate_notifications = new_resource.immediate_notifications.size
  @processing_now.delayed_notifications = new_resource.delayed_notifications.size

  # When notifying resource is present notification_type will also be present.  It is nil for
  # delayed notifications.
  if notifying_resource
    _name = "#{notifying_resource.resource_name}[#{notifying_resource.name}]"
    debug("Notified from #{_name} (:#{notification_type})")
    @processing_now.notifying_resource = _name
  end
  @processing_now.notification_type = notification_type
  true
end

#resource_completed(new_resource) ⇒ Object

Called when a resource action has been completed.



87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
# File 'lib/sous_vide/event_methods.rb', line 87

def resource_completed(new_resource)
  return false if @processing_now.nil? || nested?(new_resource) # ignore nested resources

  debug("Received :resource_completed on #{@processing_now}")
  @processing_now.duration_ms = (new_resource.elapsed_time.to_f * 1000).to_i

  # If a resource has notifications Chef will converge it in a forced_why_run mode to
  # determine if any update will happen and if the notifications should be called.
  #
  # When this event was fired in why_run mode we override it's status to `why-run`.
  #
  # This is also how Chef::Runner#focred_why_run is implemented so it should be reliable.
  #
  # TODO: what happens when :why_run for notification fails?
  if ::Chef::Config[:why_run]
    debug("Resource #{@processing_now.name}##{@processing_now.action} marked why-run",
          "because Chef::Config[:why_run] is true.")
    @processing_now.status = "why-run"
  end

  # This resource was not notified by another and is a subject of normal ordered converge
  # process. @resource_collection_cursor is pointing to next resource according to the
  # expanded resource collection.
  #
  # When chef-client fails we will take remaining entries and add to the report as
  # 'unprocessed'. It works because resources are ordered and we can keep track where we are.
  #
  # why-run mode and notifications are not in natural order and must not move the cursor.
  #
  # Having it pointing ahead is relevant because current resource has just been converged
  # (maybe 'failed') and it is not 'unprocessed'.
  if !@processing_now.notifying_resource && # not notified
     !::Chef::Config[:why_run] &&           # not why-run
     @run_phase == "converge"               # only converge phase

    @resource_collection_cursor += 1
  end

  @processing_now.completed_at = Time.now.strftime("%F %T")
  @processed << @processing_now
  @processing_now = nil
  true
end

#resource_failed(new_resource, _action, exception) ⇒ Object

Called when a resource fails and will not be retried.



132
133
134
135
136
137
138
139
140
# File 'lib/sous_vide/event_methods.rb', line 132

def resource_failed(new_resource, _action, exception)
  return false if @processing_now.nil? || nested?(new_resource) # ignore nested resources

  debug("Received :resource_failed on #{@processing_now}")
  @processing_now.status = "failed"
  @processing_now.error_source = new_resource.to_text
  @processing_now.error_output = exception.message
  true
end

#resource_failed_retriable(new_resource, _action, _remaining_retries, exception) ⇒ Object

Called when a resource fails, but will retry.

Resources with retries can succeed on subsequent attempts or ignore_failure option may be set and it’s the only place we can capture intermittent errors.

This event can fire multiple times, but we capture only the most recent error.



148
149
150
151
152
153
154
155
156
# File 'lib/sous_vide/event_methods.rb', line 148

def resource_failed_retriable(new_resource, _action, _remaining_retries, exception)
  return false if @processing_now.nil? || nested?(new_resource) # ignore nested resources

  debug("Received :resource_failed_retriable on #{@processing_now}")
  @processing_now.retries += 1
  @processing_now.error_source = new_resource.to_text
  @processing_now.error_output = exception.message
  true
end

#resource_skipped(new_resource, _action, conditional) ⇒ Object

Called when a resource action has been skipped b/c of a conditional.



68
69
70
71
72
73
74
75
# File 'lib/sous_vide/event_methods.rb', line 68

def resource_skipped(new_resource, _action, conditional)
  return false if @processing_now.nil? || nested?(new_resource) # ignore nested resources

  debug("Received :resource_skipped on #{@processing_now}", "(#{conditional.to_text})")
  @processing_now.guard_description = conditional.to_text
  @processing_now.status = "skipped"
  true
end

#resource_up_to_date(new_resource, _action) ⇒ Object

Called when a resource has no converge actions, e.g., it was already correct.



78
79
80
81
82
83
84
# File 'lib/sous_vide/event_methods.rb', line 78

def resource_up_to_date(new_resource, _action)
  return false if @processing_now.nil? || nested?(new_resource) # ignore nested resources

  debug("Received :resource_up_to_date on #{@processing_now}")
  @processing_now.status = "up-to-date"
  true
end

#resource_updated(new_resource, _action) ⇒ Object

Called after a resource has been completely converged, but only if modifications were made.



59
60
61
62
63
64
65
# File 'lib/sous_vide/event_methods.rb', line 59

def resource_updated(new_resource, _action)
  return false if @processing_now.nil? || nested?(new_resource) # ignore nested resources

  debug("Received :resource_updated on #{@processing_now}")
  @processing_now.status = "updated"
  true
end