Back to Chef

Before Notifications

docs/dev/design_documents/resource_before_notifications.md

19.3.22.5 KB
Original Source

Before Notifications

Let users trigger another resource prior to an action happening.

Motivation

As a Chef user,
I want to make one resource action a precondition for another,
So that when two resources depend on each other, I can make an update succeed.

As a Chef user,
I want this action only to happen if the other resource *does* update,
So that I don't unnecessarily run my preconditions on every single Chef run.

As a Chef user,
I want Chef to run an action if a resource updates,
So that I don't have to implement the resource test logic in my recipe.

Specification

We add a new :before timing which causes a notification to happen before the resource actually updates. If the resource will not actually update, this event does not fire.

The events you can specify would become:

  • :before - before the resource updates, but only if an update will occur.
  • :immediately - after the resource updates, but only if an update occurred.
  • :delayed - after the resource updates, at the end of the run.
ruby
package "foo" do
  action :upgrade
  # The package upgrade will fail if we try to upgrade while it runs!!!
  notifies :stop, "service[blah]", :before
end

service "blah" do
end

This will work for both subscribes and notifies.

Backwards Compatibility

This will only affect resources, which have :before on them, and will not modify any existing resources or recipes.

Implementation

There is a tricky implementation detail here, because both the test part -- "is my package version lower than the latest?" -- and the set part -- "call rpm and update the package"-- of a resource are both part of the action. Chef only runs one action to run at a time, and it seems ill-advised to change that without a lot of extra thinking.

To get around this without breaking the model, we propose that resources with an :before action run a why-run test of the action and trigger off of that, before running the action for real. The flow of this resource:

ruby
package "foo" do
  action :upgrade
  notifies :stop, "service[blah]", :before
end

The execution of the package upgrade looks like this:

  1. If :before events are on the resource: a. raise an error if the resource does not support why-run. b. Turn on why-run temporarily. c. Run the action. d. Send the notification if updated_by_last_action? is true. e. Turn off why-run.
  2. Run the action (for real!).
  3. Send :immediate or :delayed notifications if updated_by_last_action? is true.