Merge lp:~bac/charms/precise/juju-gui/config-changed into lp:~juju-gui/charms/precise/juju-gui/trunk

Proposed by Brad Crittenden on 2012-12-17
Status: Merged
Merged at revision: 21
Proposed branch: lp:~bac/charms/precise/juju-gui/config-changed
Merge into: lp:~juju-gui/charms/precise/juju-gui/trunk
Diff against target: 711 lines (+429/-145)
8 files modified
.bzrignore (+1/-0)
hooks/config-changed (+131/-0)
hooks/install (+16/-38)
hooks/start (+3/-83)
hooks/stop (+2/-19)
hooks/utils.py (+167/-4)
revision (+1/-1)
tests/test_utils.py (+108/-0)
To merge this branch: bzr merge lp:~bac/charms/precise/juju-gui/config-changed
Reviewer Review Type Date Requested Status
Nicola Larosa (community) Approve on 2012-12-18
Gary Poster (community) 2012-12-17 Approve on 2012-12-18
Review via email: mp+140316@code.launchpad.net

Description of the change

Add support for config-changed

The config-changed hook has been added. In order to share code from the
install hook, common routines were moved to utils. Those routines have unit
tests now, except for 'build' which didn't look like much.

Many of the functions in utils were modified for testability where they needed
to write files.

There is an issue lurking. Using 'juju set juju-gui juju-gui-branch=lp:<some
branch> seems to leave nginx in an odd state. It is started but it cannot
find the document root so it may be a race condition. I'll continue to hunt
for the problem while this cut is in review.

To post a comment you must log in.
Gary Poster (gary) wrote :

Thank you, Brad. The code is very clean, the tests are fast, and this is nice functionality.

The lurking issue you mentioned *may* be irrelevant when Francesco starts work on bug 188618.

As I understand it, the config-changed hook is called initially, after install and before start. See Kapil's answer to http://askubuntu.com/questions/82683/what-juju-charm-hooks-are-available-and-what-does-each-one-do and the discussion of config-changed that he links to in https://juju.ubuntu.com/docs/drafts/service-config.html#creating-charms . This means that the install hook can sometimes be wildly simple: it should be able to defer all of its work to the config-changed hook. The only trick then, AFAIK, is that config-changed needs to be able to detect if things have been started yet, and not restart if things have started...or, it should embrace making the start hook irrelevant, perhaps, and always start, as you do now. Checking with Kapil or Ben or a charmer about my understanding and related charm best practices would be a reasonable step for all of these observations.

To be honest, my big unhappiness and concern with this branch is that it is blocking Francesco starting cleanly on bug 1088618 tomorrow morning. That is something we must have to stay on schedule. We didn't need the config-changed functionality in order to meet the letter of the law, and as you know, I like to fulfill what we need and *then* back-fill the other bits that we have time for, if we still believe them to be important. I didn't communicate my position on this config-changed fix clearly in that regard, and it's my fault.

There are a number of ways to resolve this, but under the circumstances, I suggest the following. Francesco should start work tomorrow morning with trunk, merged with this branch. He should feel free to make any changes to it that he needs, ignoring my discussion of possible simplification above unless he needs to address it for his goals. He can propose his branch with your branch as a dependency.

You can pair with him to get his branch moving more quickly, if possible. Maybe in your morning you can resolve the answer to the question I raised at the top, and then in the afternoon he can pass the branch to you to take it farther after his EoD.

Francesco, please communicate with Nicola about this situation in case he wants to start bug 1083920: as few changes to the charm as possible during your work will be helpful.

Thanks again, Brad. This is good work. I want it to land, but there's some work to be done on it yet, and I don't want to have it at the expense of our schedule, so let's see if the coordination with Francesco that I propose might work.

Gary

21. By Brad Crittenden on 2012-12-18

Merged Francesco's changes. Cleanup, lint, and test changes after merging with trunk.

Gary Poster (gary) wrote :

Francesco's changes fix the problems you described, and he is not blocked.

Please make a bug/card to at least consider simplifying the install/config-changed/start situation, as I described above (or feel free to ask me to if you do not feel comfortable with writing the bug text).

Thank you!

Gary

review: Approve
Nicola Larosa (teknico) wrote :

Code looks good to me. Mind you, I only looked at the code, I did not actually run it, though.

review: Approve

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
1=== added file '.bzrignore'
2--- .bzrignore 1970-01-01 00:00:00 +0000
3+++ .bzrignore 2012-12-18 13:27:18 +0000
4@@ -0,0 +1,1 @@
5+.emacs*
6
7=== added file 'hooks/config-changed'
8--- hooks/config-changed 1970-01-01 00:00:00 +0000
9+++ hooks/config-changed 2012-12-18 13:27:18 +0000
10@@ -0,0 +1,131 @@
11+#!/usr/bin/env python2
12+# -*- python -*-
13+
14+# Copyright 2012 Canonical Ltd. This software is licensed under the
15+# GNU Affero General Public License version 3 (see the file LICENSE).
16+
17+import os.path
18+from shutil import rmtree
19+from subprocess import CalledProcessError
20+import sys
21+
22+from charmhelpers import (
23+ get_config,
24+ log,
25+ log_entry,
26+ log_exit,
27+ service_control,
28+ STOP,
29+)
30+from shelltoolbox import (
31+ DictDiffer,
32+ su,
33+)
34+
35+from utils import (
36+ AGENT,
37+ build,
38+ config_json,
39+ fetch,
40+ GUI,
41+ IMPROV,
42+ start_agent,
43+ start_gui,
44+ start_improv,
45+)
46+
47+
48+def handle_config_changes(config, diff):
49+ # Handle all configuration file changes.
50+ log('Updating configuration.')
51+
52+ staging = config.get('staging')
53+ staging_environment = config.get('staging-environment')
54+ juju_api_port = config.get('juju-api-port')
55+ added_or_changed = diff.added_or_changed
56+
57+ # Fetch new branches?
58+ gui_branch = None
59+ api_branch = None
60+ if 'juju-gui-branch' in added_or_changed:
61+ if os.path.exists('juju-gui'):
62+ rmtree('juju-gui')
63+ gui_branch = config['juju-gui-branch']
64+ if 'juju-api-branch' in added_or_changed:
65+ if os.path.exists('juju'):
66+ rmtree('juju')
67+ api_branch = config['juju-api-branch']
68+ if gui_branch or api_branch:
69+ fetch(gui_branch, api_branch)
70+ build(config['command-log-file'])
71+ # Restarting of the gui and api services is handled below.
72+
73+ # Handle changes to the improv server configuration.
74+ if staging:
75+ staging_properties = set(
76+ ['staging', 'staging-environment', 'juju-api-port'])
77+ staging_changed = added_or_changed & staging_properties
78+ if staging_changed or (api_branch is not None):
79+ if 'staging' in added_or_changed:
80+ # 'staging' went from False to True, so the agent server is
81+ # running and must be stopped.
82+ current_api = AGENT
83+ else:
84+ # Only staging parameters changed, so the existing staging
85+ # server must be stopped and later restarted.
86+ current_api = IMPROV
87+ log('Stopping %s.' % current_api)
88+ service_control(current_api, STOP)
89+
90+ # Now the improv server can be cleanly started.
91+ log('Starting or restarting improv')
92+ start_improv(juju_api_port, staging_environment)
93+ else:
94+ agent_properties = set(['juju-api-port', 'staging'])
95+ agent_changed = added_or_changed & agent_properties
96+ if agent_changed or (api_branch is not None):
97+ if 'staging' in added_or_changed:
98+ # If 'staging' transitions to False we need to stop the backend
99+ # and start the agent.
100+ current_api = IMPROV
101+ else:
102+ # The agent is still running but the configuration has been
103+ # updated -- bounce it.
104+ current_api = AGENT
105+ service_control(current_api, STOP)
106+ start_agent(juju_api_port)
107+
108+ # Handle changes to the juju-gui configuration.
109+ gui_properties = set(
110+ ['juju-gui-console-enabled', 'juju-api-port', 'staging'])
111+ gui_changed = added_or_changed & gui_properties
112+ if gui_changed or (gui_branch is not None):
113+ with su('root'):
114+ service_control('nginx', STOP)
115+ service_control(GUI, STOP)
116+ console_enabled = config.get('juju-gui-console-enabled')
117+ start_gui(juju_api_port, console_enabled, staging)
118+
119+
120+def main():
121+ config = get_config()
122+ prev_config = config_json.get()
123+ diff = DictDiffer(config, prev_config)
124+
125+ if not diff.modified:
126+ log("No configuration changes, exiting.")
127+ sys.exit(0)
128+ handle_config_changes(config, diff)
129+ config_json.set(config)
130+
131+
132+if __name__ == '__main__':
133+ log_entry()
134+ try:
135+ main()
136+ except CalledProcessError as e:
137+ log('Exception caught:')
138+ log(e.output)
139+ raise
140+ finally:
141+ log_exit()
142
143=== modified file 'hooks/install'
144--- hooks/install 2012-12-12 17:29:39 +0000
145+++ hooks/install 2012-12-18 13:27:18 +0000
146@@ -1,12 +1,10 @@
147 #!/usr/bin/env python2
148 # -*- python -*-
149
150-import os
151 from subprocess import (
152 CalledProcessError,
153 check_call,
154 )
155-import tempfile
156
157 # python-shelltoolbox is installed as a dependency of python-charmhelpers.
158 check_call(['apt-get', 'install', '-y', 'python-charmhelpers'])
159@@ -22,53 +20,33 @@
160 )
161 from shelltoolbox import (
162 apt_get_install,
163- cd,
164- command,
165 install_extra_repositories,
166- run,
167 )
168
169-from utils import cmd_log
170-
171-
172-def fetch(juju_gui_branch, juju_api_branch):
173- """Install required dependencies and retrieve Juju/Juju GUI branches."""
174+from utils import (
175+ build,
176+ cmd_log,
177+ config_json,
178+ fetch,
179+ )
180+
181+
182+DEB_DEPENDENCIES = (
183+ 'bzr', 'imagemagick', 'make', 'nginx', 'nodejs', 'npm', 'zookeeper')
184+
185+
186+def get_dependencies():
187 log('Installing dependencies.')
188 cmd_log(install_extra_repositories('ppa:chris-lea/node.js'))
189- cmd_log(
190- apt_get_install('bzr', 'imagemagick', 'make',
191- 'nodejs', 'npm', 'zookeeper', 'nginx'))
192- log('Retrieving source checkouts.')
193- bzr_checkout = command('bzr', 'co', '--lightweight')
194- cmd_log(bzr_checkout(juju_gui_branch, 'juju-gui'))
195- cmd_log(bzr_checkout(juju_api_branch, 'juju'))
196-
197-
198-def build(logpath):
199- """Set up Juju GUI and nginx."""
200- log('Building Juju GUI.')
201- with cd('juju-gui'):
202- logdir = os.path.dirname(logpath)
203- fd, name = tempfile.mkstemp(prefix='make-', dir=logdir)
204- log('Output from "make" sent to', name)
205- run('make', stdout=fd, stderr=fd)
206- log('Setting up nginx.')
207- nginx_default_site = '/etc/nginx/sites-enabled/default'
208- juju_gui_site = '/etc/nginx/sites-available/juju-gui'
209- if os.path.exists(nginx_default_site):
210- os.remove(nginx_default_site)
211- if not os.path.exists(juju_gui_site):
212- cmd_log(run('touch', juju_gui_site))
213- cmd_log(run('chown', 'ubuntu:', juju_gui_site))
214- cmd_log(
215- run('ln', '-s', juju_gui_site,
216- '/etc/nginx/sites-enabled/juju-gui'))
217+ cmd_log(apt_get_install(*DEB_DEPENDENCIES))
218
219
220 def main():
221 config = get_config()
222+ get_dependencies()
223 fetch(config['juju-gui-branch'], config['juju-api-branch'])
224 build(config['command-log-file'])
225+ config_json.set(config)
226
227
228 if __name__ == '__main__':
229
230=== modified file 'hooks/start'
231--- hooks/start 2012-12-12 11:42:12 +0000
232+++ hooks/start 2012-12-18 13:27:18 +0000
233@@ -1,100 +1,20 @@
234 #!/usr/bin/env python2
235 #-*- python -*-
236
237-import json
238-import os
239-
240 from charmhelpers import (
241 get_config,
242 log,
243 log_entry,
244 log_exit,
245 open_port,
246- service_control,
247- START,
248- unit_get,
249-)
250-from shelltoolbox import (
251- run,
252- su,
253 )
254
255 from utils import (
256- get_zookeeper_address,
257- render_to_file,
258+ start_agent,
259+ start_gui,
260+ start_improv,
261 )
262
263-CURRENT_DIR = os.getcwd()
264-JUJU_DIR = os.path.join(CURRENT_DIR, 'juju')
265-JUJU_GUI_DIR = os.path.join(CURRENT_DIR, 'juju-gui')
266-
267-
268-def start_gui(juju_api_port, console_enabled, staging):
269- """Set up and start the Juju GUI server."""
270- with su('root'):
271- run('chown', '-R', 'ubuntu:', JUJU_GUI_DIR)
272- build_dir = JUJU_GUI_DIR + '/build-'
273- build_dir += 'debug' if staging else 'prod'
274- log('Setting up Juju GUI start up script.')
275- render_to_file(
276- 'juju-gui.conf.template', {'juju_gui_dir': JUJU_GUI_DIR},
277- '/etc/init/juju-gui.conf')
278- log('Generating the Juju GUI configuration file.')
279- context = {
280- 'address': unit_get('public-address'),
281- 'console_enabled': json.dumps(console_enabled),
282- 'port': juju_api_port,
283- }
284- render_to_file(
285- 'config.js.template', context,
286- os.path.join(build_dir, 'juju-ui', 'assets', 'config.js'))
287- log('Generating the nginx site configuration file.')
288- context = {
289- 'server_root': build_dir
290- }
291- render_to_file(
292- 'nginx.conf.template', context,
293- '/etc/nginx/sites-available/juju-gui')
294- log('Starting Juju GUI.')
295- with su('root'):
296- service_control('juju-gui', START)
297-
298-
299-def start_improv(juju_api_port, staging_env):
300- """Start a simulated juju environment using ``improv.py``."""
301- log('Setting up staging start up script.')
302- context = {
303- 'juju_dir': JUJU_DIR,
304- 'port': juju_api_port,
305- 'staging_env': staging_env,
306- }
307- render_to_file(
308- 'juju-api-improv.conf.template', context,
309- '/etc/init/juju-api-improv.conf')
310- log('Starting the staging backend.')
311- with su('root'):
312- service_control('juju-api-improv', START)
313-
314-
315-def start_agent(juju_api_port):
316- """Start the Juju agent and connect to the current environment."""
317- # Retrieve the Zookeeper address from the start up script.
318- unit_dir = os.path.realpath(os.path.join(CURRENT_DIR, '..'))
319- agent_file = '/etc/init/juju-{0}.conf'.format(os.path.basename(unit_dir))
320- zookeeper = get_zookeeper_address(agent_file)
321- log('Setting up API agent start up script.')
322- context = {
323- 'juju_dir': JUJU_DIR,
324- 'port': juju_api_port,
325- 'zookeeper': zookeeper,
326- }
327- render_to_file(
328- 'juju-api-agent.conf.template', context,
329- '/etc/init/juju-api-agent.conf')
330- log('Starting API agent.')
331- with su('root'):
332- service_control('juju-api-agent', START)
333-
334
335 def open_ports(juju_api_port):
336 """Expose Juju GUI web server and websocket server ports."""
337
338=== modified file 'hooks/stop'
339--- hooks/stop 2012-12-10 14:37:15 +0000
340+++ hooks/stop 2012-12-18 13:27:18 +0000
341@@ -2,28 +2,11 @@
342 #-*- python -*-
343
344 from charmhelpers import (
345- get_config,
346- log,
347 log_entry,
348 log_exit,
349- service_control,
350- STOP,
351 )
352-from shelltoolbox import su
353-
354-
355-def stop():
356- """Stop the Juju API agent."""
357- config = get_config()
358- with su('root'):
359- log('Stopping Juju GUI.')
360- service_control('juju-gui', STOP)
361- if config.get('staging'):
362- log('Stopping the staging backend.')
363- service_control('juju-api-improv', STOP)
364- else:
365- log('Stopping API agent.')
366- service_control('juju-api-agent', STOP)
367+
368+from utils import stop
369
370
371 if __name__ == '__main__':
372
373=== modified file 'hooks/utils.py'
374--- hooks/utils.py 2012-12-07 21:16:55 +0000
375+++ hooks/utils.py 2012-12-18 13:27:18 +0000
376@@ -1,10 +1,51 @@
377 """Juju GUI charm utilities."""
378
379+__all__ = [
380+ 'AGENT',
381+ 'build',
382+ 'cmd_log',
383+ 'get_zookeeper_address',
384+ 'GUI',
385+ 'IMPROV',
386+ 'render_to_file',
387+ 'start_agent',
388+ 'start_gui',
389+ 'start_improv',
390+ 'stop',
391+ ]
392+
393+import json
394 import os
395 import logging
396-
397-from shelltoolbox import search_file
398-from charmhelpers import get_config
399+import tempfile
400+
401+from shelltoolbox import (
402+ cd,
403+ command,
404+ run,
405+ search_file,
406+ Serializer,
407+ su,
408+ )
409+from charmhelpers import (
410+ get_config,
411+ log,
412+ service_control,
413+ START,
414+ STOP,
415+ unit_get,
416+)
417+
418+
419+AGENT = 'juju-api-agent'
420+IMPROV = 'juju-api-improv'
421+GUI = 'juju-gui'
422+CURRENT_DIR = os.getcwd()
423+JUJU_DIR = os.path.join(CURRENT_DIR, 'juju')
424+JUJU_GUI_DIR = os.path.join(CURRENT_DIR, 'juju-gui')
425+
426+# Store the configuration from on invocation to the next.
427+config_json = Serializer('/tmp/config.json')
428
429
430 def get_zookeeper_address(agent_file_path):
431@@ -36,6 +77,7 @@
432
433 results_log = None
434
435+
436 def _setupLogging():
437 global results_log
438 if results_log is not None:
439@@ -45,7 +87,7 @@
440 filename=config['command-log-file'],
441 level=logging.INFO,
442 format="%(asctime)s: %(name)s@%(levelname)s %(message)s")
443- results_log = logging.getLogger('juju-gui')
444+ results_log = logging.getLogger(GUI)
445
446
447 def cmd_log(results):
448@@ -57,3 +99,124 @@
449 # Since 'results' may be multi-line output, start it on a separate line
450 # from the logger timestamp, etc.
451 results_log.info('\n' + results)
452+
453+
454+def start_improv(juju_api_port, staging_env,
455+ config_path='/etc/init/juju-api-improv.conf'):
456+ """Start a simulated juju environment using ``improv.py``."""
457+ log('Setting up staging start up script.')
458+ context = {
459+ 'juju_dir': JUJU_DIR,
460+ 'port': juju_api_port,
461+ 'staging_env': staging_env,
462+ }
463+ render_to_file(
464+ 'juju-api-improv.conf.template', context,
465+ config_path)
466+ log('Starting the staging backend.')
467+ with su('root'):
468+ service_control(IMPROV, START)
469+
470+
471+def start_agent(juju_api_port, config_path='/etc/init/juju-api-agent.conf'):
472+ """Start the Juju agent and connect to the current environment."""
473+ # Retrieve the Zookeeper address from the start up script.
474+ unit_dir = os.path.realpath(os.path.join(CURRENT_DIR, '..'))
475+ agent_file = '/etc/init/juju-{0}.conf'.format(os.path.basename(unit_dir))
476+ zookeeper = get_zookeeper_address(agent_file)
477+ log('Setting up API agent start up script.')
478+ context = {
479+ 'juju_dir': JUJU_DIR,
480+ 'port': juju_api_port,
481+ 'zookeeper': zookeeper,
482+ }
483+ render_to_file(
484+ 'juju-api-agent.conf.template', context,
485+ config_path)
486+ log('Starting API agent.')
487+ with su('root'):
488+ service_control(AGENT, START)
489+
490+
491+def start_gui(juju_api_port, console_enabled, staging,
492+ config_path='/etc/init/juju-gui.conf',
493+ nginx_path='/etc/nginx/sites-available/juju-gui',
494+ config_js_path=None):
495+ """Set up and start the Juju GUI server."""
496+ with su('root'):
497+ run('chown', '-R', 'ubuntu:', JUJU_GUI_DIR)
498+ build_dir = JUJU_GUI_DIR + '/build-'
499+ build_dir += 'debug' if staging else 'prod'
500+ log('Setting up Juju GUI start up script.')
501+ render_to_file(
502+ 'juju-gui.conf.template', {}, config_path)
503+ log('Generating the Juju GUI configuration file.')
504+ context = {
505+ 'address': unit_get('public-address'),
506+ 'console_enabled': json.dumps(console_enabled),
507+ 'port': juju_api_port,
508+ }
509+ if config_js_path is None:
510+ config_js_path = os.path.join(
511+ build_dir, 'juju-ui', 'assets', 'config.js')
512+ render_to_file(
513+ 'config.js.template', context,
514+ config_js_path)
515+ log('Generating the nginx site configuration file.')
516+ context = {
517+ 'server_root': build_dir
518+ }
519+ render_to_file(
520+ 'nginx.conf.template', context, nginx_path)
521+ log('Starting Juju GUI.')
522+ with su('root'):
523+ # Stop nginx so it will restart cleanly with the gui.
524+ service_control('nginx', STOP)
525+ service_control(GUI, START)
526+
527+
528+def stop():
529+ """Stop the Juju API agent."""
530+ config = get_config()
531+ with su('root'):
532+ log('Stopping Juju GUI.')
533+ service_control(GUI, STOP)
534+ if config.get('staging'):
535+ log('Stopping the staging backend.')
536+ service_control(IMPROV, STOP)
537+ else:
538+ log('Stopping API agent.')
539+ service_control(AGENT, STOP)
540+
541+
542+def fetch(juju_gui_branch, juju_api_branch):
543+ """Install required dependencies and retrieve Juju/Juju GUI branches."""
544+ log('Retrieving source checkouts.')
545+ bzr_checkout = command('bzr', 'co', '--lightweight')
546+ if juju_gui_branch is not None:
547+ cmd_log(run('rm', '-rf', 'juju-gui'))
548+ cmd_log(bzr_checkout(juju_gui_branch, 'juju-gui'))
549+ if juju_api_branch is not None:
550+ cmd_log(run('rm', '-rf', 'juju'))
551+ cmd_log(bzr_checkout(juju_api_branch, 'juju'))
552+
553+
554+def build(logpath):
555+ """Set up Juju GUI and nginx."""
556+ log('Building Juju GUI.')
557+ with cd('juju-gui'):
558+ logdir = os.path.dirname(logpath)
559+ fd, name = tempfile.mkstemp(prefix='make-', dir=logdir)
560+ log('Output from "make" sent to', name)
561+ run('make', stdout=fd, stderr=fd)
562+ log('Setting up nginx.')
563+ nginx_default_site = '/etc/nginx/sites-enabled/default'
564+ juju_gui_site = '/etc/nginx/sites-available/juju-gui'
565+ if os.path.exists(nginx_default_site):
566+ os.remove(nginx_default_site)
567+ if not os.path.exists(juju_gui_site):
568+ cmd_log(run('touch', juju_gui_site))
569+ cmd_log(run('chown', 'ubuntu:', juju_gui_site))
570+ cmd_log(
571+ run('ln', '-s', juju_gui_site,
572+ '/etc/nginx/sites-enabled/juju-gui'))
573
574=== modified file 'revision'
575--- revision 2012-12-14 16:48:29 +0000
576+++ revision 2012-12-18 13:27:18 +0000
577@@ -1,1 +1,1 @@
578-15
579+16
580
581=== modified file 'tests/test_utils.py'
582--- tests/test_utils.py 2012-12-12 17:29:39 +0000
583+++ tests/test_utils.py 2012-12-18 13:27:18 +0000
584@@ -1,5 +1,6 @@
585 #!/usr/bin/env python2
586
587+from contextlib import contextmanager
588 import os
589 import tempfile
590 import unittest
591@@ -10,7 +11,13 @@
592 cmd_log,
593 get_zookeeper_address,
594 render_to_file,
595+ start_agent,
596+ start_gui,
597+ start_improv,
598+ stop,
599 )
600+# Import the whole utils package for monkey patching.
601+import utils
602
603
604 class GetZookeeperAddressTest(unittest.TestCase):
605@@ -68,5 +75,106 @@
606 self.assertTrue(line.endswith(': juju-gui@INFO \nfoo\n'))
607
608
609+class StartStopTest(unittest.TestCase):
610+
611+ def setUp(self):
612+ self.service_names = []
613+ self.actions = []
614+ self.svc_ctl_call_count = 0
615+ self.fake_zk_address = '192.168.5.26'
616+ # Monkey patches.
617+ self.command = charmhelpers.command
618+
619+ def service_control_mock(service_name, action):
620+ self.svc_ctl_call_count += 1
621+ self.service_names.append(service_name)
622+ self.actions.append(action)
623+
624+ def noop(*args):
625+ pass
626+
627+ @contextmanager
628+ def su(user):
629+ yield None
630+
631+ def get_zookeeper_address_mock(fp):
632+ return self.fake_zk_address
633+
634+ self.functions = dict(
635+ service_control=(utils.service_control, service_control_mock),
636+ log=(utils.log, noop),
637+ su=(utils.su, su),
638+ run=(utils.run, noop),
639+ unit_get=(utils.unit_get, noop),
640+ get_zookeeper_address=(
641+ utils.get_zookeeper_address, get_zookeeper_address_mock)
642+ )
643+ # Apply the patches.
644+ for fn, fcns in self.functions.items():
645+ setattr(utils, fn, fcns[1])
646+
647+ self.destination_file = tempfile.NamedTemporaryFile()
648+ self.addCleanup(self.destination_file.close)
649+
650+ def tearDown(self):
651+ # Undo all of the monkey patching.
652+ for fn, fcns in self.functions.items():
653+ setattr(utils, fn, fcns[0])
654+ charmhelpers.command = self.command
655+
656+ def test_start_improv(self):
657+ port = '1234'
658+ staging_env = 'large'
659+ start_improv(port, staging_env, self.destination_file.name)
660+ conf = self.destination_file.read()
661+ self.assertTrue('--port %s' % port in conf)
662+ self.assertTrue(staging_env + '.json' in conf)
663+ self.assertEqual(self.svc_ctl_call_count, 1)
664+ self.assertEqual(self.service_names, ['juju-api-improv'])
665+ self.assertEqual(self.actions, [charmhelpers.START])
666+
667+ def test_start_agent(self):
668+ port = '1234'
669+ start_agent(port, self.destination_file.name)
670+ conf = self.destination_file.read()
671+ self.assertTrue('--port %s' % port in conf)
672+ self.assertTrue('JUJU_ZOOKEEPER=%s' % self.fake_zk_address in conf)
673+ self.assertEqual(self.svc_ctl_call_count, 1)
674+ self.assertEqual(self.service_names, ['juju-api-agent'])
675+ self.assertEqual(self.actions, [charmhelpers.START])
676+
677+ def test_start_gui(self):
678+ port = '1234'
679+ nginx_file = tempfile.NamedTemporaryFile()
680+ self.addCleanup(nginx_file.close)
681+ config_js_file = tempfile.NamedTemporaryFile()
682+ self.addCleanup(config_js_file.close)
683+ start_gui(port, False, True, self.destination_file.name,
684+ nginx_file.name, config_js_file.name)
685+ conf = self.destination_file.read()
686+ self.assertTrue('/usr/sbin/nginx' in conf)
687+ nginx_conf = nginx_file.read()
688+ self.assertTrue('juju-gui/build-debug' in nginx_conf)
689+ self.assertEqual(self.svc_ctl_call_count, 2)
690+ self.assertEqual(self.service_names, ['nginx', 'juju-gui'])
691+ self.assertEqual(self.actions, [charmhelpers.STOP, charmhelpers.START])
692+
693+ def test_stop_staging(self):
694+ mock_config = {'staging': True}
695+ charmhelpers.command = lambda *args: lambda: dumps(mock_config)
696+ stop()
697+ self.assertEqual(self.svc_ctl_call_count, 2)
698+ self.assertEqual(self.service_names, ['juju-gui', 'juju-api-improv'])
699+ self.assertEqual(self.actions, [charmhelpers.STOP, charmhelpers.STOP])
700+
701+ def test_stop_production(self):
702+ mock_config = {'staging': False}
703+ charmhelpers.command = lambda *args: lambda: dumps(mock_config)
704+ stop()
705+ self.assertEqual(self.svc_ctl_call_count, 2)
706+ self.assertEqual(self.service_names, ['juju-gui', 'juju-api-agent'])
707+ self.assertEqual(self.actions, [charmhelpers.STOP, charmhelpers.STOP])
708+
709+
710 if __name__ == '__main__':
711 unittest.main(verbosity=2)

Subscribers

People subscribed via source and target branches