Merge lp:~fwereade/pyjuju/agent-trash-old-sessions into lp:pyjuju

Proposed by William Reade
Status: Superseded
Proposed branch: lp:~fwereade/pyjuju/agent-trash-old-sessions
Merge into: lp:pyjuju
Prerequisite: lp:~fwereade/pyjuju/upstartify-agents
Diff against target: 911 lines (+293/-49)
29 files modified
examples/oneiric/mysql/hooks/install (+2/-2)
examples/oneiric/mysql/hooks/start (+3/-1)
examples/oneiric/mysql/hooks/stop (+1/-1)
juju/agents/base.py (+56/-3)
juju/agents/tests/common.py (+1/-0)
juju/agents/tests/test_base.py (+175/-17)
juju/agents/tests/test_machine.py (+3/-4)
juju/agents/tests/test_unit.py (+3/-1)
juju/agents/unit.py (+8/-3)
juju/control/tests/test_status.py (+2/-0)
juju/lib/statemachine.py (+1/-1)
juju/machine/tests/test_unit_deployment.py (+3/-1)
juju/machine/unit.py (+6/-2)
juju/providers/common/cloudinit.py (+4/-2)
juju/providers/common/tests/data/cloud_init_bootstrap (+2/-0)
juju/providers/common/tests/data/cloud_init_bootstrap_zookeepers (+2/-0)
juju/providers/common/tests/data/cloud_init_branch (+1/-0)
juju/providers/common/tests/data/cloud_init_branch_trunk (+1/-0)
juju/providers/common/tests/data/cloud_init_distro (+1/-0)
juju/providers/common/tests/data/cloud_init_ppa (+1/-0)
juju/providers/ec2/tests/data/bootstrap_cloud_init (+2/-0)
juju/providers/ec2/tests/data/launch_cloud_init (+1/-0)
juju/providers/ec2/tests/data/launch_cloud_init_branch (+1/-0)
juju/providers/ec2/tests/data/launch_cloud_init_ppa (+1/-0)
juju/providers/local/agent.py (+2/-1)
juju/providers/local/tests/test_agent.py (+4/-3)
juju/providers/orchestra/tests/data/bootstrap_user_data (+2/-0)
juju/providers/orchestra/tests/data/launch_user_data (+1/-0)
juju/unit/lifecycle.py (+3/-7)
To merge this branch: bzr merge lp:~fwereade/pyjuju/agent-trash-old-sessions
Reviewer Review Type Date Requested Status
Juju Engineering Pending
Review via email: mp+83905@code.launchpad.net

This proposal has been superseded by a proposal from 2011-12-12.

Description of the change

Agents now require a --session-file argument, which is written to on connect and deleted on close.

If, on startup, the session file exists and refers to a valid session, the session is explicitly closed before any further work is done.

IMO this is the best approach because it minimises the number of cases we have to think about; rather than attempting to re-engage with an existing session (set up watches but skip ephemeral node creation... more considerations?), we always have to set up exactly the same state, whatever the reason we're restarting.

Session files are chmodded to 600 but, on reflection, I'm not sure this actually helps... a malicious hook could still plausibly hijack them. Thoughts?

The cloud-init runcmds have changed again, and these make up a tediously large proportion of the diff. Sorry for the noise :(.

To post a comment you must log in.
Revision history for this message
Kapil Thangavelu (hazmat) wrote :

Excerpts from William Reade's message of Wed Nov 30 09:56:37 UTC 2011:
> William Reade has proposed merging lp:~fwereade/juju/agent-trash-old-sessions into lp:juju with lp:~fwereade/juju/upstartify-agents as a prerequisite.
>
> Requested reviews:
> juju hackers (juju)
> Related bugs:
> Bug #898082 in juju: "immediate agent restart fails"
> https://bugs.launchpad.net/juju/+bug/898082
>
> For more details, see:
> https://code.launchpad.net/~fwereade/juju/agent-trash-old-sessions/+merge/83905
>
> Agents now require a --session-file argument, which is written to on connect and deleted on close.
>
> If, on startup, the session file exists and refers to a valid session, the session is explicitly closed before any further work is done.
>
> IMO this is the best approach because it minimises the number of cases we have to think about; rather than attempting to re-engage with an existing session (set up watches but skip ephemeral node creation... more considerations?), we always have to set up exactly the same state, whatever the reason we're restarting.
>
> Session files are chmodded to 600 but, on reflection, I'm not sure this actually helps... a malicious hook could still plausibly hijack them. Thoughts?
>
> The cloud-init runcmds have changed again, and these make up a tediously large proportion of the diff. Sorry for the noise :(.

Haven't looked at the diff, just a comment on the approach, this seems pretty
reasonable and simple. The watch callbacks (transient registration with libzk)
are gone so we can't usefully reconnect to the original session watches, we'd
have to restablish them anyways. Effectively sessions are primarily useful for
transient error activity not process restarts.

However there is a caveat wrt to session closing and ephemeral nodes that's
worth keeping in mind, namely that the ephemerals are coordination points for
things like relation membership notification, and this will result in transient
-depart/-join hook execution as the old session closes and the new one comes
into place. I don't see any reasonable alternatives that are significantly more
complex though.

476. By William Reade

merge parent

Revision history for this message
William Reade (fwereade) wrote :

rejecting due to abandonment of prereq

477. By William Reade

agents now require --session-file, while will be used to shoot old sessions in the head when the agent comes up, to ensure we're starting from clean ZK state

478. By William Reade

merge parent

479. By William Reade

merge parent

480. By William Reade

merge parent

481. By William Reade

merge parent

482. By William Reade

merge parent

483. By William Reade

merge parent

484. By William Reade

merge parent

485. By William Reade

address review point

486. By William Reade

merge parent

487. By William Reade

merge parent

488. By William Reade

merge parent

489. By William Reade

merge parent

490. By William Reade

merge parent

491. By William Reade

merge parent

492. By William Reade

merge parent

493. By William Reade

merge parent

Unmerged revisions

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
1=== modified file 'examples/oneiric/mysql/hooks/install'
2--- examples/oneiric/mysql/hooks/install 2011-09-15 18:56:08 +0000
3+++ examples/oneiric/mysql/hooks/install 2011-12-02 14:44:25 +0000
4@@ -21,5 +21,5 @@
5 juju-log "Editing my.cnf to allow listening on all interfaces"
6 sed --in-place=old 's/127\.0\.0\.1/0.0.0.0/' /etc/mysql/my.cnf
7
8-juju-log "Restarting mysql service"
9-service mysql restart
10+juju-log "Stopping mysql service"
11+service mysql stop
12
13=== modified file 'examples/oneiric/mysql/hooks/start'
14--- examples/oneiric/mysql/hooks/start 2011-02-03 01:23:43 +0000
15+++ examples/oneiric/mysql/hooks/start 2011-12-02 14:44:25 +0000
16@@ -1,1 +1,3 @@
17-#!/bin/bash
18\ No newline at end of file
19+#!/bin/bash
20+juju-log "Starting mysql service"
21+service mysql start || service mysql restart
22
23=== modified file 'examples/oneiric/mysql/hooks/stop'
24--- examples/oneiric/mysql/hooks/stop 2011-09-15 18:56:08 +0000
25+++ examples/oneiric/mysql/hooks/stop 2011-12-02 14:44:25 +0000
26@@ -1,3 +1,3 @@
27 #!/bin/bash
28 juju-log "Stopping mysql service"
29-/etc/init.d/mysql stop
30+service mysql stop || true
31
32=== modified file 'juju/agents/base.py'
33--- juju/agents/base.py 2011-09-22 13:23:00 +0000
34+++ juju/agents/base.py 2011-12-02 14:44:25 +0000
35@@ -1,7 +1,9 @@
36 import argparse
37 import os
38+import logging
39+import stat
40 import sys
41-import logging
42+import yaml
43
44 import zookeeper
45
46@@ -18,6 +20,23 @@
47 from juju.state.environment import GlobalSettingsStateManager
48
49
50+def load_client_id(path):
51+ try:
52+ with open(path) as f:
53+ return yaml.load(f.read())
54+ except IOError:
55+ return None
56+
57+
58+def save_client_id(path, client_id):
59+ parent = os.path.dirname(path)
60+ if not os.path.exists(parent):
61+ os.makedirs(parent)
62+ with open(path, "w") as f:
63+ f.write(yaml.dump(client_id))
64+ os.chmod(path, stat.S_IRUSR | stat.S_IWUSR)
65+
66+
67 class TwistedOptionNamespace(object):
68 """
69 An argparse namespace implementation that is compatible with twisted
70@@ -153,13 +172,40 @@
71 "Invalid juju-directory %r, does not exist." % (
72 options.get("juju_directory")))
73
74+ if options["session_file"] is None:
75+ raise JujuError("No session file specified")
76+
77 self.config = options
78
79 @inlineCallbacks
80+ def _kill_existing_session(self):
81+ try:
82+ # We might have died suddenly, in which case the session may
83+ # still be alive. If this is the case, shoot it in the head, so
84+ # it doesn't interfere with our attempts to recreate our state.
85+ # (We need to be able to recreate our state *anyway*, and it's
86+ # much simpler to force ourselves to recreate it every time than
87+ # it is to mess around partially recreating partial state.)
88+ client_id = load_client_id(self.config["session_file"])
89+ if client_id is None:
90+ return
91+ temp_client = yield ZookeeperClient().connect(
92+ self.config["zookeeper_servers"], client_id=client_id)
93+ yield temp_client.close()
94+ except zookeeper.ZooKeeperException:
95+ # We don't really care what went wrong; just that we're not able
96+ # to connect using the old session, and therefore we should be ok
97+ # to start a fresh one without transient state hanging around.
98+ pass
99+
100+ @inlineCallbacks
101 def connect(self):
102 """Return an authenticated connection to the juju zookeeper."""
103- hosts = self.config["zookeeper_servers"]
104- self.client = yield ZookeeperClient().connect(hosts)
105+ yield self._kill_existing_session()
106+ self.client = yield ZookeeperClient().connect(
107+ self.config["zookeeper_servers"])
108+ save_client_id(
109+ self.config["session_file"], self.client.client_id)
110
111 principals = self.config.get("principals", ())
112 for principal in principals:
113@@ -200,6 +246,9 @@
114 finally:
115 if self.client and self.client.connected:
116 self.client.close()
117+ session_file = self.config["session_file"]
118+ if os.path.exists(session_file):
119+ os.unlink(session_file)
120
121 def set_watch_enabled(self, flag):
122 """Set boolean flag for whether this agent should watching zookeeper.
123@@ -285,3 +334,7 @@
124 parser.add_argument(
125 "--juju-directory", default=juju_home, type=os.path.abspath,
126 help="juju working directory ($JUJU_HOME)")
127+
128+ parser.add_argument(
129+ "--session-file", default=None, type=os.path.abspath,
130+ help="like a pidfile, but for the zookeeper session id")
131
132=== modified file 'juju/agents/tests/common.py'
133--- juju/agents/tests/common.py 2011-09-15 19:24:47 +0000
134+++ juju/agents/tests/common.py 2011-12-02 14:44:25 +0000
135@@ -55,6 +55,7 @@
136 options = TwistedOptionNamespace()
137 options["juju_directory"] = self.juju_directory
138 options["zookeeper_servers"] = get_test_zookeeper_address()
139+ options["session_file"] = self.makeFile()
140 return succeed(options)
141
142 @inlineCallbacks
143
144=== modified file 'juju/agents/tests/test_base.py'
145--- juju/agents/tests/test_base.py 2011-12-02 14:44:24 +0000
146+++ juju/agents/tests/test_base.py 2011-12-02 14:44:25 +0000
147@@ -2,13 +2,14 @@
148 import json
149 import logging
150 import os
151+import stat
152 import sys
153-
154+import yaml
155
156 from twisted.application.app import AppLogger
157 from twisted.application.service import IService, IServiceCollection
158 from twisted.internet.defer import (
159- succeed, Deferred, inlineCallbacks, returnValue)
160+ fail, succeed, Deferred, inlineCallbacks, returnValue)
161 from twisted.python.components import Componentized
162 from twisted.python import log
163
164@@ -20,6 +21,7 @@
165
166 from juju.agents.base import (
167 BaseAgent, TwistedOptionNamespace, AgentRunner, AgentLogger)
168+from juju.agents.dummy import DummyAgent
169 from juju.errors import NoConnection, JujuError
170 from juju.lib.zklog import ZookeeperHandler
171
172@@ -34,8 +36,8 @@
173 @inlineCallbacks
174 def setUp(self):
175 yield super(BaseAgentTest, self).setUp()
176- self.change_environment(
177- JUJU_HOME=self.makeDir())
178+ self.juju_home = self.makeDir()
179+ self.change_environment(JUJU_HOME=self.juju_home)
180
181 def test_as_app(self):
182 """The agent class can be accessed as an application."""
183@@ -79,6 +81,8 @@
184 # Agent options
185 self.assertEqual(parser.get_default("principals"), [])
186 self.assertEqual(parser.get_default("zookeeper_servers"), "")
187+ self.assertEqual(parser.get_default("juju_directory"), self.juju_home)
188+ self.assertEqual(parser.get_default("session_file"), None)
189
190 def test_twistd_flags_correspond(self):
191 parser = argparse.ArgumentParser()
192@@ -99,7 +103,8 @@
193 log_file_path = self.makeFile()
194
195 options = parser.parse_args(
196- ["--logfile", log_file_path], namespace=TwistedOptionNamespace())
197+ ["--logfile", log_file_path, "--session-file", self.makeFile()],
198+ namespace=TwistedOptionNamespace())
199
200 def match_observer(observer):
201 return isinstance(observer.im_self, log.PythonLoggingObserver)
202@@ -180,8 +185,9 @@
203 This will create an agent instance, parse the cli args, passes them to
204 the agent, and starts the agent runner.
205 """
206- self.change_args("es-agent", "--zookeeper-servers",
207- get_test_zookeeper_address())
208+ self.change_args(
209+ "es-agent", "--zookeeper-servers", get_test_zookeeper_address(),
210+ "--session-file", self.makeFile())
211 runner = self.mocker.patch(AgentRunner)
212 runner.run()
213 mock_agent = self.mocker.patch(BaseAgent)
214@@ -220,7 +226,8 @@
215
216 self.change_args(
217 "es-agent", "--nodaemon",
218- "--zookeeper-servers", get_test_zookeeper_address())
219+ "--zookeeper-servers", get_test_zookeeper_address(),
220+ "--session-file", self.makeFile())
221 runner = self.mocker.patch(AgentRunner)
222 logger = self.mocker.patch(AppLogger)
223 logger.start(MATCH_APP)
224@@ -230,6 +237,7 @@
225 DummyAgent.run()
226 return started
227
228+ @inlineCallbacks
229 def test_stop_service_stub_closes_agent(self):
230 """The base class agent, stopService will the stop method.
231
232@@ -238,6 +246,7 @@
233 """
234 mock_agent = self.mocker.patch(BaseAgent)
235 mock_client = self.mocker.mock(ZookeeperClient)
236+ session_file = self.makeFile()
237
238 # connection is closed after agent.stop invoked.
239 with self.mocker.order():
240@@ -259,11 +268,17 @@
241 self.mocker.result(mock_client)
242 mock_client.close()
243
244+ # delete session file
245+ mock_agent.config
246+ self.mocker.result({"session_file": session_file})
247+
248 self.mocker.replay()
249
250 agent = BaseAgent()
251- return agent.stopService()
252+ yield agent.stopService()
253+ self.assertFalse(os.path.exists(session_file))
254
255+ @inlineCallbacks
256 def test_stop_service_stub_ignores_disconnected_agent(self):
257 """The base class agent, stopService will the stop method.
258
259@@ -271,6 +286,7 @@
260 """
261 mock_agent = self.mocker.patch(BaseAgent)
262 mock_client = self.mocker.mock(ZookeeperClient)
263+ session_file = self.makeFile()
264
265 # connection is closed after agent.stop invoked.
266 with self.mocker.order():
267@@ -286,10 +302,14 @@
268 mock_client.connected
269 self.mocker.result(False)
270
271+ mock_agent.config
272+ self.mocker.result({"session_file": session_file})
273+
274 self.mocker.replay()
275
276 agent = BaseAgent()
277- return agent.stopService()
278+ yield agent.stopService()
279+ self.assertFalse(os.path.exists(session_file))
280
281 def test_run_base_raises_error(self):
282 """The base class agent, raises a notimplemented error when started."""
283@@ -297,12 +317,15 @@
284 client.connect(get_test_zookeeper_address())
285 client_mock = self.mocker.mock()
286 self.mocker.result(succeed(client_mock))
287+ client_mock.client_id
288+ self.mocker.result((123, "abc"))
289 self.mocker.replay()
290
291 agent = BaseAgent()
292 agent.configure({
293 "zookeeper_servers": get_test_zookeeper_address(),
294- "juju_directory": self.makeDir()})
295+ "juju_directory": self.makeDir(),
296+ "session_file": self.makeFile()})
297 d = agent.startService()
298 self.failUnlessFailure(d, NotImplementedError)
299 return d
300@@ -313,22 +336,39 @@
301 client = self.mocker.patch(ZookeeperClient)
302 client.connect("x2.example.com")
303 self.mocker.result(succeed(mock_client))
304+ mock_client.client_id
305+ self.mocker.result((123, "abc"))
306 self.mocker.replay()
307
308 agent = BaseAgent()
309 agent.configure({"zookeeper_servers": "x2.example.com",
310- "juju_directory": self.makeDir()})
311+ "juju_directory": self.makeDir(),
312+ "session_file": self.makeFile()})
313 result = agent.connect()
314 self.assertEqual(result.result, mock_client)
315 self.assertEqual(agent.client, mock_client)
316
317- def test_non_existant_directory(self):
318+ def test_nonexistent_directory(self):
319 """If the juju directory does not exist an error should be raised.
320 """
321 juju_directory = self.makeDir()
322 os.rmdir(juju_directory)
323 data = {"zookeeper_servers": get_test_zookeeper_address(),
324- "juju_directory": juju_directory}
325+ "juju_directory": juju_directory,
326+ "session_file": self.makeFile()}
327+
328+ agent = BaseAgent()
329+ self.assertRaises(
330+ JujuError,
331+ agent.configure,
332+ data)
333+
334+ def test_bad_session_file(self):
335+ """If the session file cannot be created an error should be raised.
336+ """
337+ data = {"zookeeper_servers": get_test_zookeeper_address(),
338+ "juju_directory": self.makeDir(),
339+ "session_file": None}
340
341 agent = BaseAgent()
342 self.assertRaises(
343@@ -341,7 +381,8 @@
344 juju_directory = self.makeDir()
345 self.change_args(
346 "es-agent", "--zookeeper-servers", get_test_zookeeper_address(),
347- "--juju-directory", juju_directory)
348+ "--juju-directory", juju_directory,
349+ "--session-file", self.makeFile())
350
351 agent = BaseAgent()
352 parser = argparse.ArgumentParser()
353@@ -363,7 +404,9 @@
354 agent = BaseAgent()
355 parser = argparse.ArgumentParser()
356 agent.setup_options(parser)
357- options = parser.parse_args(namespace=TwistedOptionNamespace())
358+ options = parser.parse_args(
359+ ["--session-file", self.makeFile()],
360+ namespace=TwistedOptionNamespace())
361 agent.configure(options)
362 self.assertEqual(
363 agent.config["juju_directory"], juju_directory)
364@@ -379,6 +422,8 @@
365 client = self.mocker.patch(ZookeeperClient)
366 client.connect("x1.example.com")
367 self.mocker.result(succeed(client))
368+ client.client_id
369+ self.mocker.result((123, "abc"))
370 client.add_auth("digest", "admin:abc")
371 client.add_auth("digest", "agent:xyz")
372 client.exists("/")
373@@ -387,7 +432,105 @@
374 agent = BaseAgent()
375 parser = argparse.ArgumentParser()
376 agent.setup_options(parser)
377- options = parser.parse_args(namespace=TwistedOptionNamespace())
378+ options = parser.parse_args(
379+ ["--session-file", self.makeFile()],
380+ namespace=TwistedOptionNamespace())
381+ agent.configure(options)
382+ d = agent.startService()
383+ self.failUnlessFailure(d, NotImplementedError)
384+ return d
385+
386+ def test_connect_closes_running_session(self):
387+ self.change_args("es-agent")
388+ self.change_environment(
389+ JUJU_HOME=self.makeDir(),
390+ JUJU_ZOOKEEPER="x1.example.com")
391+
392+ session_file = self.makeFile()
393+ with open(session_file, "w") as f:
394+ f.write(yaml.dump((123, "abc")))
395+ mock_client_1 = self.mocker.mock()
396+ client = self.mocker.patch(ZookeeperClient)
397+ client.connect("x1.example.com", client_id=(123, "abc"))
398+ self.mocker.result(succeed(mock_client_1))
399+ mock_client_1.close()
400+ self.mocker.result(None)
401+
402+ mock_client_2 = self.mocker.mock()
403+ client.connect("x1.example.com")
404+ self.mocker.result(succeed(mock_client_2))
405+ mock_client_2.client_id
406+ self.mocker.result((456, "def"))
407+ self.mocker.replay()
408+
409+ agent = BaseAgent()
410+ parser = argparse.ArgumentParser()
411+ agent.setup_options(parser)
412+ options = parser.parse_args(
413+ ["--session-file", session_file],
414+ namespace=TwistedOptionNamespace())
415+ agent.configure(options)
416+ d = agent.startService()
417+ self.failUnlessFailure(d, NotImplementedError)
418+ return d
419+
420+ def test_connect_handles_expired_session(self):
421+ self.change_args("es-agent")
422+ self.change_environment(
423+ JUJU_HOME=self.makeDir(),
424+ JUJU_ZOOKEEPER="x1.example.com")
425+
426+ session_file = self.makeFile()
427+ with open(session_file, "w") as f:
428+ f.write(yaml.dump((123, "abc")))
429+ client = self.mocker.patch(ZookeeperClient)
430+ client.connect("x1.example.com", client_id=(123, "abc"))
431+ self.mocker.result(fail(zookeeper.SessionExpiredException()))
432+
433+ mock_client = self.mocker.mock()
434+ client.connect("x1.example.com")
435+ self.mocker.result(succeed(mock_client))
436+ mock_client.client_id
437+ self.mocker.result((456, "def"))
438+ self.mocker.replay()
439+
440+ agent = BaseAgent()
441+ parser = argparse.ArgumentParser()
442+ agent.setup_options(parser)
443+ options = parser.parse_args(
444+ ["--session-file", session_file],
445+ namespace=TwistedOptionNamespace())
446+ agent.configure(options)
447+ d = agent.startService()
448+ self.failUnlessFailure(d, NotImplementedError)
449+ return d
450+
451+ def test_connect_handles_nonsense_session(self):
452+ self.change_args("es-agent")
453+ self.change_environment(
454+ JUJU_HOME=self.makeDir(),
455+ JUJU_ZOOKEEPER="x1.example.com")
456+
457+ session_file = self.makeFile()
458+ with open(session_file, "w") as f:
459+ f.write(yaml.dump("cheesy wotsits"))
460+ client = self.mocker.patch(ZookeeperClient)
461+ client.connect("x1.example.com", client_id="cheesy wotsits")
462+ self.mocker.result(fail(zookeeper.ZooKeeperException()))
463+
464+ mock_client = self.mocker.mock()
465+ client.connect("x1.example.com")
466+ self.mocker.result(succeed(mock_client))
467+ mock_client.client_id
468+ self.mocker.result((456, "def"))
469+ self.mocker.replay()
470+
471+ agent = BaseAgent()
472+ parser = argparse.ArgumentParser()
473+ agent.setup_options(parser)
474+ options = parser.parse_args(
475+ ["--session-file", session_file],
476+ namespace=TwistedOptionNamespace())
477 agent.configure(options)
478 d = agent.startService()
479 self.failUnlessFailure(d, NotImplementedError)
480@@ -405,6 +548,21 @@
481 agent.set_watch_enabled(False)
482 self.assertFalse(agent.get_watch_enabled())
483
484+ @inlineCallbacks
485+ def test_session_file_permissions(self):
486+ session_file = self.makeFile()
487+ agent = DummyAgent()
488+ agent.configure({
489+ "session_file": session_file,
490+ "juju_directory": self.makeDir(),
491+ "zookeeper_servers": get_test_zookeeper_address()})
492+ yield agent.startService()
493+ mode = os.stat(session_file).st_mode
494+ mask = stat.S_IRWXU | stat.S_IRWXG | stat.S_IRWXO
495+ self.assertEquals(mode & mask, stat.S_IRUSR | stat.S_IWUSR)
496+ yield agent.stopService()
497+ self.assertFalse(os.path.exists(session_file))
498+
499
500 class AgentDebugLogSettingsWatch(AgentTestBase):
501
502
503=== modified file 'juju/agents/tests/test_machine.py'
504--- juju/agents/tests/test_machine.py 2011-10-05 13:59:44 +0000
505+++ juju/agents/tests/test_machine.py 2011-12-02 14:44:25 +0000
506@@ -120,10 +120,9 @@
507 # initially setup by get_agent_config in setUp
508 self.change_environment(JUJU_MACHINE_ID="")
509 self.change_args("es-agent",
510- "--zookeeper-servers",
511- get_test_zookeeper_address(),
512- "--juju-directory",
513- self.makeDir())
514+ "--zookeeper-servers", get_test_zookeeper_address(),
515+ "--juju-directory", self.makeDir(),
516+ "--session-file", self.makeFile())
517 parser = argparse.ArgumentParser()
518 self.agent.setup_options(parser)
519 options = parser.parse_args(namespace=TwistedOptionNamespace())
520
521=== modified file 'juju/agents/tests/test_unit.py'
522--- juju/agents/tests/test_unit.py 2011-12-02 14:44:24 +0000
523+++ juju/agents/tests/test_unit.py 2011-12-02 14:44:25 +0000
524@@ -170,7 +170,8 @@
525 self.change_args(
526 "unit-agent",
527 "--juju-directory", self.makeDir(),
528- "--zookeeper-servers", get_test_zookeeper_address())
529+ "--zookeeper-servers", get_test_zookeeper_address(),
530+ "--session-file", self.makeFile())
531
532 parser = argparse.ArgumentParser()
533 self.agent.setup_options(parser)
534@@ -202,6 +203,7 @@
535 options = {}
536 options["juju_directory"] = self.juju_directory
537 options["zookeeper_servers"] = get_test_zookeeper_address()
538+ options["session_file"] = self.makeFile()
539 options["unit_name"] = "rabbit-1"
540 agent = self.agent_class()
541 agent.configure(options)
542
543=== modified file 'juju/agents/unit.py'
544--- juju/agents/unit.py 2011-12-02 14:44:24 +0000
545+++ juju/agents/unit.py 2011-12-02 14:44:25 +0000
546@@ -80,10 +80,11 @@
547 self.config["juju_directory"], "state")
548
549 # Setup the server portion of the cli api exposed to hooks.
550+ socket_path = os.path.join(self.unit_directory, HOOK_SOCKET_FILE)
551+ if os.path.exists(socket_path):
552+ os.unlink(socket_path)
553 from twisted.internet import reactor
554- self.api_socket = reactor.listenUNIX(
555- os.path.join(self.unit_directory, HOOK_SOCKET_FILE),
556- self.api_factory)
557+ self.api_socket = reactor.listenUNIX(socket_path, self.api_factory)
558
559 # Setup the unit state's address
560 address = yield get_unit_address(self.client)
561@@ -98,6 +99,10 @@
562 # Inform the system, we're alive.
563 yield self.unit_state.connect_agent()
564
565+ # Start paying attention to the debug-log setting
566+ if self.get_watch_enabled():
567+ yield self.unit_state.watch_hook_debug(self.cb_watch_hook_debug)
568+
569 self.lifecycle = UnitLifecycle(
570 self.client,
571 self.unit_state,
572
573=== modified file 'juju/control/tests/test_status.py'
574--- juju/control/tests/test_status.py 2011-11-30 00:57:05 +0000
575+++ juju/control/tests/test_status.py 2011-12-02 14:44:25 +0000
576@@ -107,6 +107,7 @@
577 options = TwistedOptionNamespace()
578 options["juju_directory"] = path
579 options["zookeeper_servers"] = get_test_zookeeper_address()
580+ options["session_file"] = self.makeFile()
581 for k, v in extra_options.items():
582 options[k] = v
583 agent.configure(options)
584@@ -302,6 +303,7 @@
585 options = TwistedOptionNamespace()
586 options["juju_directory"] = self.makeDir()
587 options["zookeeper_servers"] = get_test_zookeeper_address()
588+ options["session_file"] = self.makeFile()
589 options["machine_id"] = "0"
590 agent.configure(options)
591 agent.set_watch_enabled(False)
592
593=== modified file 'juju/lib/statemachine.py'
594--- juju/lib/statemachine.py 2011-05-04 19:40:59 +0000
595+++ juju/lib/statemachine.py 2011-12-02 14:44:25 +0000
596@@ -213,7 +213,7 @@
597 state_dict = yield self._load()
598 if not state_dict:
599 returnValue({})
600- returnValue(state_dict["state_variables"])
601+ returnValue(state_dict.get("state_variables", {}))
602
603 def set_observer(self, observer):
604 """Set a callback, that will be notified on state changes.
605
606=== modified file 'juju/machine/tests/test_unit_deployment.py'
607--- juju/machine/tests/test_unit_deployment.py 2011-12-02 14:44:24 +0000
608+++ juju/machine/tests/test_unit_deployment.py 2011-12-02 14:44:25 +0000
609@@ -114,7 +114,9 @@
610 self.deployment.directory, "charm.log")
611 command = " ".join([
612 sys.executable, "-m", "juju.agents.dummy",
613- "--nodaemon", "--logfile", log_file])
614+ "--nodaemon", "--logfile", log_file,
615+ "--session-file",
616+ "/var/run/juju/unit-wordpress-0-agent.zksession"])
617 self.assertEquals(exec_, command)
618 d.addCallback(verify_upstart)
619 return d
620
621=== modified file 'juju/machine/unit.py'
622--- juju/machine/unit.py 2011-12-02 14:44:24 +0000
623+++ juju/machine/unit.py 2011-12-02 14:44:25 +0000
624@@ -87,7 +87,9 @@
625 self.service.set_command(" ".join((
626 sys.executable, "-m", self.unit_agent_module,
627 "--nodaemon",
628- "--logfile", os.path.join(self.directory, "charm.log"))))
629+ "--logfile", os.path.join(self.directory, "charm.log"),
630+ "--session-file",
631+ "/var/run/juju/unit-%s-agent.zksession" % self.unit_path_name)))
632 return self.service.start()
633
634 @inlineCallbacks
635@@ -230,7 +232,9 @@
636 sys.executable,
637 "-m", "juju.agents.unit",
638 "--nodaemon",
639- "--logfile", "/var/log/juju/unit-%s.log" % self.unit_path_name)))
640+ "--logfile", "/var/log/juju/unit-%s.log" % self.unit_path_name,
641+ "--session-file",
642+ "/var/run/juju/unit-%s-agent.zksession" % self.unit_path_name)))
643 yield service.install()
644
645 # Create a symlink on the host for easier access to the unit log file
646
647=== modified file 'juju/providers/common/cloudinit.py'
648--- juju/providers/common/cloudinit.py 2011-12-02 14:44:24 +0000
649+++ juju/providers/common/cloudinit.py 2011-12-02 14:44:25 +0000
650@@ -48,7 +48,8 @@
651 {"JUJU_MACHINE_ID": machine_id, "JUJU_ZOOKEEPER": zookeeper_hosts})
652 service.set_command(
653 "python -m juju.agents.machine --nodaemon "
654- "--logfile /var/log/juju/machine-agent.log")
655+ "--logfile /var/log/juju/machine-agent.log "
656+ "--session-file /var/run/juju/machine-agent.zksession")
657 return service.get_cloud_init_commands()
658
659
660@@ -58,7 +59,8 @@
661 service.set_environ({"JUJU_ZOOKEEPER": zookeeper_hosts})
662 service.set_command(
663 "python -m juju.agents.provision --nodaemon "
664- "--logfile /var/log/juju/provision-agent.log")
665+ "--logfile /var/log/juju/provision-agent.log "
666+ "--session-file /var/run/juju/provision-agent.zksession")
667 return service.get_cloud_init_commands()
668
669
670
671=== modified file 'juju/providers/common/tests/data/cloud_init_bootstrap'
672--- juju/providers/common/tests/data/cloud_init_bootstrap 2011-12-02 14:44:24 +0000
673+++ juju/providers/common/tests/data/cloud_init_bootstrap 2011-12-02 14:44:25 +0000
674@@ -28,6 +28,7 @@
675
676
677 exec python -m juju.agents.machine --nodaemon --logfile /var/log/juju/machine-agent.log
678+ --session-file /var/run/juju/machine-agent.zksession
679
680 EOF
681
682@@ -50,6 +51,7 @@
683
684
685 exec python -m juju.agents.provision --nodaemon --logfile /var/log/juju/provision-agent.log
686+ --session-file /var/run/juju/provision-agent.zksession
687
688 EOF
689
690
691=== modified file 'juju/providers/common/tests/data/cloud_init_bootstrap_zookeepers'
692--- juju/providers/common/tests/data/cloud_init_bootstrap_zookeepers 2011-12-02 14:44:24 +0000
693+++ juju/providers/common/tests/data/cloud_init_bootstrap_zookeepers 2011-12-02 14:44:25 +0000
694@@ -28,6 +28,7 @@
695
696
697 exec python -m juju.agents.machine --nodaemon --logfile /var/log/juju/machine-agent.log
698+ --session-file /var/run/juju/machine-agent.zksession
699
700 EOF
701
702@@ -50,6 +51,7 @@
703
704
705 exec python -m juju.agents.provision --nodaemon --logfile /var/log/juju/provision-agent.log
706+ --session-file /var/run/juju/provision-agent.zksession
707
708 EOF
709
710
711=== modified file 'juju/providers/common/tests/data/cloud_init_branch'
712--- juju/providers/common/tests/data/cloud_init_branch 2011-12-02 14:44:24 +0000
713+++ juju/providers/common/tests/data/cloud_init_branch 2011-12-02 14:44:25 +0000
714@@ -31,6 +31,7 @@
715
716
717 exec python -m juju.agents.machine --nodaemon --logfile /var/log/juju/machine-agent.log
718+ --session-file /var/run/juju/machine-agent.zksession
719
720 EOF
721
722
723=== modified file 'juju/providers/common/tests/data/cloud_init_branch_trunk'
724--- juju/providers/common/tests/data/cloud_init_branch_trunk 2011-12-02 14:44:24 +0000
725+++ juju/providers/common/tests/data/cloud_init_branch_trunk 2011-12-02 14:44:25 +0000
726@@ -31,6 +31,7 @@
727
728
729 exec python -m juju.agents.machine --nodaemon --logfile /var/log/juju/machine-agent.log
730+ --session-file /var/run/juju/machine-agent.zksession
731
732 EOF
733
734
735=== modified file 'juju/providers/common/tests/data/cloud_init_distro'
736--- juju/providers/common/tests/data/cloud_init_distro 2011-12-02 14:44:24 +0000
737+++ juju/providers/common/tests/data/cloud_init_distro 2011-12-02 14:44:25 +0000
738@@ -27,6 +27,7 @@
739
740
741 exec python -m juju.agents.machine --nodaemon --logfile /var/log/juju/machine-agent.log
742+ --session-file /var/run/juju/machine-agent.zksession
743
744 EOF
745
746
747=== modified file 'juju/providers/common/tests/data/cloud_init_ppa'
748--- juju/providers/common/tests/data/cloud_init_ppa 2011-12-02 14:44:24 +0000
749+++ juju/providers/common/tests/data/cloud_init_ppa 2011-12-02 14:44:25 +0000
750@@ -29,6 +29,7 @@
751
752
753 exec python -m juju.agents.machine --nodaemon --logfile /var/log/juju/machine-agent.log
754+ --session-file /var/run/juju/machine-agent.zksession
755
756 EOF
757
758
759=== modified file 'juju/providers/ec2/tests/data/bootstrap_cloud_init'
760--- juju/providers/ec2/tests/data/bootstrap_cloud_init 2011-12-02 14:44:24 +0000
761+++ juju/providers/ec2/tests/data/bootstrap_cloud_init 2011-12-02 14:44:25 +0000
762@@ -28,6 +28,7 @@
763
764
765 exec python -m juju.agents.machine --nodaemon --logfile /var/log/juju/machine-agent.log
766+ --session-file /var/run/juju/machine-agent.zksession
767
768 EOF
769
770@@ -50,6 +51,7 @@
771
772
773 exec python -m juju.agents.provision --nodaemon --logfile /var/log/juju/provision-agent.log
774+ --session-file /var/run/juju/provision-agent.zksession
775
776 EOF
777
778
779=== modified file 'juju/providers/ec2/tests/data/launch_cloud_init'
780--- juju/providers/ec2/tests/data/launch_cloud_init 2011-12-02 14:44:24 +0000
781+++ juju/providers/ec2/tests/data/launch_cloud_init 2011-12-02 14:44:25 +0000
782@@ -27,6 +27,7 @@
783
784
785 exec python -m juju.agents.machine --nodaemon --logfile /var/log/juju/machine-agent.log
786+ --session-file /var/run/juju/machine-agent.zksession
787
788 EOF
789
790
791=== modified file 'juju/providers/ec2/tests/data/launch_cloud_init_branch'
792--- juju/providers/ec2/tests/data/launch_cloud_init_branch 2011-12-02 14:44:24 +0000
793+++ juju/providers/ec2/tests/data/launch_cloud_init_branch 2011-12-02 14:44:25 +0000
794@@ -31,6 +31,7 @@
795
796
797 exec python -m juju.agents.machine --nodaemon --logfile /var/log/juju/machine-agent.log
798+ --session-file /var/run/juju/machine-agent.zksession
799
800 EOF
801
802
803=== modified file 'juju/providers/ec2/tests/data/launch_cloud_init_ppa'
804--- juju/providers/ec2/tests/data/launch_cloud_init_ppa 2011-12-02 14:44:24 +0000
805+++ juju/providers/ec2/tests/data/launch_cloud_init_ppa 2011-12-02 14:44:25 +0000
806@@ -29,6 +29,7 @@
807
808
809 exec python -m juju.agents.machine --nodaemon --logfile /var/log/juju/machine-agent.log
810+ --session-file /var/run/juju/machine-agent.zksession
811
812 EOF
813
814
815=== modified file 'juju/providers/local/agent.py'
816--- juju/providers/local/agent.py 2011-12-02 14:44:24 +0000
817+++ juju/providers/local/agent.py 2011-12-02 14:44:25 +0000
818@@ -47,7 +47,8 @@
819 self._service.set_environ(env)
820 self._service_args = [
821 sys.executable, "-m", self.agent_module,
822- "--nodaemon", "--logfile", log_file]
823+ "--nodaemon", "--logfile", log_file,
824+ "--session-file", "/var/run/juju/%s-machine-agent.zksession" % juju_unit_namespace]
825
826 @property
827 def juju_origin(self):
828
829=== modified file 'juju/providers/local/tests/test_agent.py'
830--- juju/providers/local/tests/test_agent.py 2011-12-02 14:44:24 +0000
831+++ juju/providers/local/tests/test_agent.py 2011-12-02 14:44:25 +0000
832@@ -14,7 +14,6 @@
833
834 class ManagedAgentTest(TestCase):
835
836-
837 @inlineCallbacks
838 def test_managed_agent_config(self):
839 subprocess_calls = []
840@@ -60,8 +59,10 @@
841 if line.startswith("exec"):
842 exec_ = line[5:-1]
843
844- expect_exec = "%s -m juju.agents.machine --nodaemon --logfile %s" % (
845- sys.executable, log_file)
846+ expect_exec = (
847+ "%s -m juju.agents.machine --nodaemon --logfile %s "
848+ "--session-file /var/run/juju/ns1-machine-agent.zksession"
849+ % (sys.executable, log_file))
850 self.assertEquals(exec_, expect_exec)
851
852 env = dict((k, v.strip('"')) for (k, v) in env)
853
854=== modified file 'juju/providers/orchestra/tests/data/bootstrap_user_data'
855--- juju/providers/orchestra/tests/data/bootstrap_user_data 2011-12-02 14:44:24 +0000
856+++ juju/providers/orchestra/tests/data/bootstrap_user_data 2011-12-02 14:44:25 +0000
857@@ -27,6 +27,7 @@
858
859
860 exec python -m juju.agents.machine --nodaemon --logfile /var/log/juju/machine-agent.log
861+ --session-file /var/run/juju/machine-agent.zksession
862
863 EOF
864
865@@ -49,6 +50,7 @@
866
867
868 exec python -m juju.agents.provision --nodaemon --logfile /var/log/juju/provision-agent.log
869+ --session-file /var/run/juju/provision-agent.zksession
870
871 EOF
872
873
874=== modified file 'juju/providers/orchestra/tests/data/launch_user_data'
875--- juju/providers/orchestra/tests/data/launch_user_data 2011-12-02 14:44:24 +0000
876+++ juju/providers/orchestra/tests/data/launch_user_data 2011-12-02 14:44:25 +0000
877@@ -26,6 +26,7 @@
878
879
880 exec python -m juju.agents.machine --nodaemon --logfile /var/log/juju/machine-agent.log
881+ --session-file /var/run/juju/machine-agent.zksession
882
883 EOF
884
885
886=== modified file 'juju/unit/lifecycle.py'
887--- juju/unit/lifecycle.py 2011-12-02 14:44:24 +0000
888+++ juju/unit/lifecycle.py 2011-12-02 14:44:25 +0000
889@@ -76,10 +76,6 @@
890 watches = []
891
892 try:
893- # Verify current state
894- assert not self._running, "Already started"
895-
896- # Execute the start hook
897 if fire_hooks:
898 yield self._execute_hook("config-changed")
899 yield self._execute_hook("start")
900@@ -239,9 +235,9 @@
901 """Add and remove unit lifecycles per the service relations Determine.
902 """
903 # changes relation delta of global zk state with our memory state.
904- new_relations = dict([(service_relation.internal_relation_id,
905- service_relation) for
906- service_relation in new_relations])
907+ new_relations = dict([
908+ (service_relation.internal_relation_id, service_relation)
909+ for service_relation in new_relations])
910 added = set(new_relations.keys()) - set(self._relations.keys())
911 removed = set(self._relations.keys()) - set(new_relations.keys())
912

Subscribers

People subscribed via source and target branches

to status/vote changes: