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
=== modified file 'ensemble/unit/lifecycle.py'
--- ensemble/unit/lifecycle.py 2011-02-25 13:55:16 +0000
+++ ensemble/unit/lifecycle.py 2011-03-29 17:12:32 +0000
@@ -279,31 +279,37 @@
279 """279 """
280 socket_path = os.path.join(self._unit_path, HOOK_SOCKET_FILE)280 socket_path = os.path.join(self._unit_path, HOOK_SOCKET_FILE)
281 if hook_name is None:281 if hook_name is None:
282 hook_name = "%s-relation-changed" % self._relation_name282 if change.change_type == "departed":
283283 hook_names = [
284 hook_path = os.path.join(284 "%s-relation-changed" % self._relation_name,
285 self._unit_path, "formula", "hooks", hook_name)285 "%s-relation-departed" % self._relation_name]
286286 else:
287 hook_names = ["%s-relation-changed" % self._relation_name]
288 else:
289 hook_names = [hook_name]
287 invoker = RelationInvoker(290 invoker = RelationInvoker(
288 context, change, "constant", socket_path, hook_log)291 context, change, "constant", socket_path, hook_log)
289292
290 yield self._run_lock.acquire()293 for hook_name in hook_names:
291 self._log.debug("%s: executing change hook", self._relation_name)294 hook_path = os.path.join(
292 try:295 self._unit_path, "formula", "hooks", hook_name)
293 yield self._executor(invoker, hook_path)296 yield self._run_lock.acquire()
294 except Exception, e:297 self._log.debug("%s: executing change hook", self._relation_name)
295 yield self._run_lock.release()298 try:
296 self._log.warn("%s: change hook error: %s", self._relation_name, e)299 yield self._executor(invoker, hook_path)
300 except Exception, e:
301 yield self._run_lock.release()
302 self._log.warn("%s: change hook error: %s", self._relation_name, e)
297303
298 if not self._error_handler:304 if not self._error_handler:
299 raise305 raise
300 self._log.info(306 self._log.info(
301 "%s: invoke change hook error handler", self._relation_name)307 "%s: invoke change hook error handler", self._relation_name)
302 # We can't hold the run lock, when we invoke the error handler, or308 # We can't hold the run lock, when we invoke the error handler, or
303 # we get a deadlock if the handler manipulates the lifecycle.309 # we get a deadlock if the handler manipulates the lifecycle.
304 yield self._error_handler(change, e)310 yield self._error_handler(change, e)
305 else:311 else:
306 yield self._run_lock.release()312 yield self._run_lock.release()
307313
308 def set_hook_error_handler(self, handler):314 def set_hook_error_handler(self, handler):
309 """Set an error handler to be invoked if a hook errors.315 """Set an error handler to be invoked if a hook errors.
310316
=== modified file 'ensemble/unit/tests/test_lifecycle.py'
--- ensemble/unit/tests/test_lifecycle.py 2011-02-27 01:12:50 +0000
+++ ensemble/unit/tests/test_lifecycle.py 2011-03-29 17:12:32 +0000
@@ -226,6 +226,9 @@
226 self.write_hook(226 self.write_hook(
227 "%s-relation-changed" % relation_name,227 "%s-relation-changed" % relation_name,
228 ("#!/bin/bash\n" "echo $ENSEMBLE_CHANGE >> %s\n" % (file_path)))228 ("#!/bin/bash\n" "echo $ENSEMBLE_CHANGE >> %s\n" % (file_path)))
229 self.write_hook(
230 "%s-relation-departed" % relation_name,
231 ("#!/bin/bash\n" "echo departed >> %s\n" % (file_path)))
229232
230 self.assertFalse(os.path.exists(file_path))233 self.assertFalse(os.path.exists(file_path))
231 return file_path234 return file_path
@@ -251,7 +254,10 @@
251 ["joined"])254 ["joined"])
252255
253 # Queue up our wait condition, of 3 hooks firing256 # Queue up our wait condition, of 3 hooks firing
254 hooks_complete = self.wait_on_hook("app-relation-changed", 3)257 hooks_complete = self.wait_on_hook(
258 sequence=["app-relation-changed",
259 "app-relation-changed",
260 "app-relation-changed", "app-relation-departed"])
255261
256 # add another.262 # add another.
257 wordpress2_states = yield self.add_opposite_service_unit(263 wordpress2_states = yield self.add_opposite_service_unit(
@@ -371,11 +377,15 @@
371 # XXX - With scheduler local state recovery, we should get the modify.377 # XXX - With scheduler local state recovery, we should get the modify.
372378
373 # Start and verify events.379 # Start and verify events.
374 hooks_executed = self.wait_on_hook("app-relation-changed", 2)380 hooks_executed = self.wait_on_hook(
381 sequence=[
382 "start",
383 "app-relation-changed", "app-relation-departed",
384 "app-relation-changed"])
375 yield self.lifecycle.start()385 yield self.lifecycle.start()
376 yield hooks_executed386 yield hooks_executed
377 self.assertEqual([x.strip() for x in open(file_path)],387 self.assertEqual([x.strip() for x in open(file_path)],
378 ["joined", "joined", "departed", "joined"])388 ["joined", "joined", "departed", "departed", "joined"])
379389
380 @inlineCallbacks390 @inlineCallbacks
381 def test_lock_start_stop_watch(self):391 def test_lock_start_stop_watch(self):

Subscribers

People subscribed via source and target branches

to status/vote changes: