Merge lp:~jimbaker/pyjuju/new-hook-semantics-1-departed-hook into lp:pyjuju

Proposed by Jim Baker
Status: Merged
Approved by: Kapil Thangavelu
Approved revision: 181
Merged at revision: 189
Proposed branch: lp:~jimbaker/pyjuju/new-hook-semantics-1-departed-hook
Merge into: lp:pyjuju
Diff against target: 106 lines (+40/-24)
2 files modified
ensemble/unit/lifecycle.py (+27/-21)
ensemble/unit/tests/test_lifecycle.py (+13/-3)
To merge this branch: bzr merge lp:~jimbaker/pyjuju/new-hook-semantics-1-departed-hook
Reviewer Review Type Date Requested Status
Kapil Thangavelu (community) Approve
Gustavo Niemeyer Approve
Review via email: mp+55383@code.launchpad.net

Description of the change

Adds a <name>-relation-departed hook, while keeping the existing
<name<-relation-changed hook. After some deliberation, the simplest way to
add this new hook semantic (and the other aspects, to be introduced in
future branches) is in UnitRelationCycle._execute_change_hook (of
ensemble.unit.lifecycle). This method iterates over the desired change
hooks corresponding to a given change. So in the case of a departed
event:

           if change.change_type == "departed":
               hook_names = [
                   "%s-relation-changed" % self._relation_name,
                   "%s-relation-departed" % self._relation_name]

A subsequent branch will remove the above <name>-relation-changed.

To post a comment you must log in.
Revision history for this message
Gustavo Niemeyer (niemeyer) wrote :

It looks good to me, but please wait for Kapil's review since he
knows more about this area.

Some minor comments as well:

[1]

+ self._log.debug("%s: executing change hook", self._relation_name)

This log message seems incorrect now. It should probably be something like

    "Executing hook %s" % hook_name

Please do something similar to the other log messages.

[2]

51 + if not self._error_handler:
52 + raise

It's not entirely clear to me that this is correct. Do we really want
to ignore the other hook when this happens?

[3]

         # Queue up our wait condition, of 3 hooks firing

s/3/4/

[4]

84 + "app-relation-changed",
85 + "app-relation-changed", "app-relation-departed"])
(...)
96 + "start",
97 + "app-relation-changed", "app-relation-departed",
98 + "app-relation-changed"])

Please organize the lists evenly.

[5]

+ ("#!/bin/bash\n" "echo departed >> %s\n" % (file_path)))

Please remove the parenthesis around file_path in both cases. They're
are not doing anything.

review: Approve
Revision history for this message
Kapil Thangavelu (hazmat) wrote :

Looks good to me, +1

[1] There are some pep8/style formatting issues, i've got a diff to fix here
https://pastebin.canonical.com/45766/

[2] Regarding gustavo's 2. Any error in a hook should exit processing as soon as possible, any error in the hook should be handled and as early possible. The hook executor will raise an errback on the execution deferred, and the workflow will transition to an error state. a recovery transition, will have to deal with partial state recoveries, but i think that's regardless of mechanism (although possible w/ btrfs snapshots pre transition would make a reasonable mechanism of partial minimization coupled with a multi hook execution flush of the associated hook api context buffers.

Revision history for this message
Kapil Thangavelu (hazmat) wrote :

forget the lp merge approve.

review: Approve
Revision history for this message
Jim Baker (jimbaker) wrote :

On Wed, Apr 6, 2011 at 9:40 AM, Gustavo Niemeyer <email address hidden>wrote:

> Review: Approve
> It looks good to me, but please wait for Kapil's review since he
> knows more about this area.
>
> Some minor comments as well:
>
>
> [1]
>
> + self._log.debug("%s: executing change hook",
> self._relation_name)
>
> This log message seems incorrect now. It should probably be something like
>
> "Executing hook %s" % hook_name
>
> Please do something similar to the other log messages.
>
>
Cleaned up

>
> [2]
>
> 51 + if not self._error_handler:
> 52 + raise
>
> It's not entirely clear to me that this is correct. Do we really want
> to ignore the other hook when this happens?
>
>
Kapil comments on this in his review; additionally, there has been no change
in this logic except that it has now part of an enclosing loop to
accommodate the fact that one or two hooks will be invoked for a change
event

>
> [3]
>
> # Queue up our wait condition, of 3 hooks firing
>
> s/3/4/
>

Changed

>
> [4]
>
> 84 + "app-relation-changed",
> 85 + "app-relation-changed",
> "app-relation-departed"])
> (...)
> 96 + "start",
> 97 + "app-relation-changed", "app-relation-departed",
> 98 + "app-relation-changed"])
>
> Please organize the lists evenly.
>
>
I reformatted this and also added comments to make more clear why I was
doing this organization to begin with, namely to show that the two hooks on
one line were actually resulting from one event.

>
> [5]
>
>
> + ("#!/bin/bash\n" "echo departed >> %s\n" % (file_path)))
>
> Please remove the parenthesis around file_path in both cases. They're
> are not doing anything.
>

Done

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
1=== modified file 'ensemble/unit/lifecycle.py'
2--- ensemble/unit/lifecycle.py 2011-02-25 13:55:16 +0000
3+++ ensemble/unit/lifecycle.py 2011-03-29 17:12:32 +0000
4@@ -279,31 +279,37 @@
5 """
6 socket_path = os.path.join(self._unit_path, HOOK_SOCKET_FILE)
7 if hook_name is None:
8- hook_name = "%s-relation-changed" % self._relation_name
9-
10- hook_path = os.path.join(
11- self._unit_path, "formula", "hooks", hook_name)
12-
13+ if change.change_type == "departed":
14+ hook_names = [
15+ "%s-relation-changed" % self._relation_name,
16+ "%s-relation-departed" % self._relation_name]
17+ else:
18+ hook_names = ["%s-relation-changed" % self._relation_name]
19+ else:
20+ hook_names = [hook_name]
21 invoker = RelationInvoker(
22 context, change, "constant", socket_path, hook_log)
23
24- yield self._run_lock.acquire()
25- self._log.debug("%s: executing change hook", self._relation_name)
26- try:
27- yield self._executor(invoker, hook_path)
28- except Exception, e:
29- yield self._run_lock.release()
30- self._log.warn("%s: change hook error: %s", self._relation_name, e)
31+ for hook_name in hook_names:
32+ hook_path = os.path.join(
33+ self._unit_path, "formula", "hooks", hook_name)
34+ yield self._run_lock.acquire()
35+ self._log.debug("%s: executing change hook", self._relation_name)
36+ try:
37+ yield self._executor(invoker, hook_path)
38+ except Exception, e:
39+ yield self._run_lock.release()
40+ self._log.warn("%s: change hook error: %s", self._relation_name, e)
41
42- if not self._error_handler:
43- raise
44- self._log.info(
45- "%s: invoke change hook error handler", self._relation_name)
46- # We can't hold the run lock, when we invoke the error handler, or
47- # we get a deadlock if the handler manipulates the lifecycle.
48- yield self._error_handler(change, e)
49- else:
50- yield self._run_lock.release()
51+ if not self._error_handler:
52+ raise
53+ self._log.info(
54+ "%s: invoke change hook error handler", self._relation_name)
55+ # We can't hold the run lock, when we invoke the error handler, or
56+ # we get a deadlock if the handler manipulates the lifecycle.
57+ yield self._error_handler(change, e)
58+ else:
59+ yield self._run_lock.release()
60
61 def set_hook_error_handler(self, handler):
62 """Set an error handler to be invoked if a hook errors.
63
64=== modified file 'ensemble/unit/tests/test_lifecycle.py'
65--- ensemble/unit/tests/test_lifecycle.py 2011-02-27 01:12:50 +0000
66+++ ensemble/unit/tests/test_lifecycle.py 2011-03-29 17:12:32 +0000
67@@ -226,6 +226,9 @@
68 self.write_hook(
69 "%s-relation-changed" % relation_name,
70 ("#!/bin/bash\n" "echo $ENSEMBLE_CHANGE >> %s\n" % (file_path)))
71+ self.write_hook(
72+ "%s-relation-departed" % relation_name,
73+ ("#!/bin/bash\n" "echo departed >> %s\n" % (file_path)))
74
75 self.assertFalse(os.path.exists(file_path))
76 return file_path
77@@ -251,7 +254,10 @@
78 ["joined"])
79
80 # Queue up our wait condition, of 3 hooks firing
81- hooks_complete = self.wait_on_hook("app-relation-changed", 3)
82+ hooks_complete = self.wait_on_hook(
83+ sequence=["app-relation-changed",
84+ "app-relation-changed",
85+ "app-relation-changed", "app-relation-departed"])
86
87 # add another.
88 wordpress2_states = yield self.add_opposite_service_unit(
89@@ -371,11 +377,15 @@
90 # XXX - With scheduler local state recovery, we should get the modify.
91
92 # Start and verify events.
93- hooks_executed = self.wait_on_hook("app-relation-changed", 2)
94+ hooks_executed = self.wait_on_hook(
95+ sequence=[
96+ "start",
97+ "app-relation-changed", "app-relation-departed",
98+ "app-relation-changed"])
99 yield self.lifecycle.start()
100 yield hooks_executed
101 self.assertEqual([x.strip() for x in open(file_path)],
102- ["joined", "joined", "departed", "joined"])
103+ ["joined", "joined", "departed", "departed", "joined"])
104
105 @inlineCallbacks
106 def test_lock_start_stop_watch(self):

Subscribers

People subscribed via source and target branches

to status/vote changes: