Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: add finally lifecycle hook to always run regardless of failure #1917

Merged
merged 1 commit into from Oct 22, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
4 changes: 3 additions & 1 deletion docs/content/docs/reference/lifecycle-hooks.md
Expand Up @@ -6,7 +6,7 @@ menu:
weight: 30
---

The life cycle hooks system allows running commands before or after any phase of Test Kitchen (`create`, `converge`, `verify`, or `destroy`). Commands can be run either locally on your workstation (the default) or remotely on the test instance.
The life cycle hooks system allows running commands before, after, or always after any phase of Test Kitchen (`create`, `converge`, `verify`, or `destroy`). Commands can be run either locally on your workstation (the default) or remotely on the test instance.

These hooks are configured under a new `lifecycle:` section in `kitchen.yml`:

Expand All @@ -17,6 +17,8 @@ lifecycle:
- echo after
- local: echo also after
- remote: echo after but in the instance
finally_create:
- echo run regardless of failure in create
```

You can also configure hooks on a single platform or suite:
Expand Down
12 changes: 8 additions & 4 deletions lib/kitchen/lifecycle_hooks.rb
Expand Up @@ -43,9 +43,13 @@ def initialize(config, state_file)
# @param block [Proc] Block of code implementing the lifecycle phase.
# @return [void]
def run_with_hooks(phase, state_file, &block)
run(phase, :pre)
yield
run(phase, :post)
begin
run(phase, :pre)
yield
run(phase, :post)
ensure
run(phase, :finally)
end
end

# @return [Kitchen::StateFile]
Expand All @@ -56,7 +60,7 @@ def run_with_hooks(phase, state_file, &block)
# Execute a specific lifecycle hook.
#
# @param phase [String] Lifecycle phase which is being executed.
# @param hook_timing [Symbol] `:pre` or `:post` to indicate which hook to run.
# @param hook_timing [Symbol] `:pre`, `:post`, or `:finally` to indicate which hook to run.
# @return [void]
def run(phase, hook_timing)
# Yes this has to be a symbol because of how data munger works.
Expand Down
4 changes: 4 additions & 0 deletions spec/kitchen/instance_spec.rb
Expand Up @@ -502,6 +502,7 @@ def platform(name = "platform")
it "calls lifecycle hooks" do
lifecycle_hooks.expects(:run).with(:create, :pre)
lifecycle_hooks.expects(:run).with(:create, :post)
lifecycle_hooks.expects(:run).with(:create, :finally)

instance.create
end
Expand Down Expand Up @@ -559,8 +560,10 @@ def platform(name = "platform")
it "calls lifecycle hooks" do
lifecycle_hooks.expects(:run).with(:create, :pre)
lifecycle_hooks.expects(:run).with(:create, :post)
lifecycle_hooks.expects(:run).with(:create, :finally)
lifecycle_hooks.expects(:run).with(:converge, :pre)
lifecycle_hooks.expects(:run).with(:converge, :post)
lifecycle_hooks.expects(:run).with(:converge, :finally)

instance.converge
end
Expand All @@ -585,6 +588,7 @@ def platform(name = "platform")
it "calls lifecycle hooks" do
lifecycle_hooks.expects(:run).with(:converge, :pre)
lifecycle_hooks.expects(:run).with(:converge, :post)
lifecycle_hooks.expects(:run).with(:converge, :finally)

instance.converge
end
Expand Down
12 changes: 12 additions & 0 deletions spec/kitchen/lifecycle_hooks_spec.rb
Expand Up @@ -108,6 +108,18 @@ def expect_remote_hook_generated_and_run(phase, hook)
run_lifecycle_hooks
end

it "runs finally even if stage fails" do
local_command = "echo foo"
config.update(finally_create: [local_command])
hook = expect_local_hook_generated_and_run(:finally, { local: local_command })
begin
lifecycle_hooks.run_with_hooks(:create, state_file) {
raise Error
}
rescue
end
end

it "runs a local command with a user option" do
hook = { local: "echo foo", user: "bar" }
config.update(post_create: [hook])
Expand Down