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

Proposed by Brad Crittenden
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
Gary Poster (community) Approve
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.
Revision history for this message
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

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

Revision history for this message
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
Revision history for this message
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
=== added file '.bzrignore'
--- .bzrignore 1970-01-01 00:00:00 +0000
+++ .bzrignore 2012-12-18 13:27:18 +0000
@@ -0,0 +1,1 @@
1.emacs*
02
=== added file 'hooks/config-changed'
--- hooks/config-changed 1970-01-01 00:00:00 +0000
+++ hooks/config-changed 2012-12-18 13:27:18 +0000
@@ -0,0 +1,131 @@
1#!/usr/bin/env python2
2# -*- python -*-
3
4# Copyright 2012 Canonical Ltd. This software is licensed under the
5# GNU Affero General Public License version 3 (see the file LICENSE).
6
7import os.path
8from shutil import rmtree
9from subprocess import CalledProcessError
10import sys
11
12from charmhelpers import (
13 get_config,
14 log,
15 log_entry,
16 log_exit,
17 service_control,
18 STOP,
19)
20from shelltoolbox import (
21 DictDiffer,
22 su,
23)
24
25from utils import (
26 AGENT,
27 build,
28 config_json,
29 fetch,
30 GUI,
31 IMPROV,
32 start_agent,
33 start_gui,
34 start_improv,
35)
36
37
38def handle_config_changes(config, diff):
39 # Handle all configuration file changes.
40 log('Updating configuration.')
41
42 staging = config.get('staging')
43 staging_environment = config.get('staging-environment')
44 juju_api_port = config.get('juju-api-port')
45 added_or_changed = diff.added_or_changed
46
47 # Fetch new branches?
48 gui_branch = None
49 api_branch = None
50 if 'juju-gui-branch' in added_or_changed:
51 if os.path.exists('juju-gui'):
52 rmtree('juju-gui')
53 gui_branch = config['juju-gui-branch']
54 if 'juju-api-branch' in added_or_changed:
55 if os.path.exists('juju'):
56 rmtree('juju')
57 api_branch = config['juju-api-branch']
58 if gui_branch or api_branch:
59 fetch(gui_branch, api_branch)
60 build(config['command-log-file'])
61 # Restarting of the gui and api services is handled below.
62
63 # Handle changes to the improv server configuration.
64 if staging:
65 staging_properties = set(
66 ['staging', 'staging-environment', 'juju-api-port'])
67 staging_changed = added_or_changed & staging_properties
68 if staging_changed or (api_branch is not None):
69 if 'staging' in added_or_changed:
70 # 'staging' went from False to True, so the agent server is
71 # running and must be stopped.
72 current_api = AGENT
73 else:
74 # Only staging parameters changed, so the existing staging
75 # server must be stopped and later restarted.
76 current_api = IMPROV
77 log('Stopping %s.' % current_api)
78 service_control(current_api, STOP)
79
80 # Now the improv server can be cleanly started.
81 log('Starting or restarting improv')
82 start_improv(juju_api_port, staging_environment)
83 else:
84 agent_properties = set(['juju-api-port', 'staging'])
85 agent_changed = added_or_changed & agent_properties
86 if agent_changed or (api_branch is not None):
87 if 'staging' in added_or_changed:
88 # If 'staging' transitions to False we need to stop the backend
89 # and start the agent.
90 current_api = IMPROV
91 else:
92 # The agent is still running but the configuration has been
93 # updated -- bounce it.
94 current_api = AGENT
95 service_control(current_api, STOP)
96 start_agent(juju_api_port)
97
98 # Handle changes to the juju-gui configuration.
99 gui_properties = set(
100 ['juju-gui-console-enabled', 'juju-api-port', 'staging'])
101 gui_changed = added_or_changed & gui_properties
102 if gui_changed or (gui_branch is not None):
103 with su('root'):
104 service_control('nginx', STOP)
105 service_control(GUI, STOP)
106 console_enabled = config.get('juju-gui-console-enabled')
107 start_gui(juju_api_port, console_enabled, staging)
108
109
110def main():
111 config = get_config()
112 prev_config = config_json.get()
113 diff = DictDiffer(config, prev_config)
114
115 if not diff.modified:
116 log("No configuration changes, exiting.")
117 sys.exit(0)
118 handle_config_changes(config, diff)
119 config_json.set(config)
120
121
122if __name__ == '__main__':
123 log_entry()
124 try:
125 main()
126 except CalledProcessError as e:
127 log('Exception caught:')
128 log(e.output)
129 raise
130 finally:
131 log_exit()
0132
=== modified file 'hooks/install'
--- hooks/install 2012-12-12 17:29:39 +0000
+++ hooks/install 2012-12-18 13:27:18 +0000
@@ -1,12 +1,10 @@
1#!/usr/bin/env python21#!/usr/bin/env python2
2# -*- python -*-2# -*- python -*-
33
4import os
5from subprocess import (4from subprocess import (
6 CalledProcessError,5 CalledProcessError,
7 check_call,6 check_call,
8 )7 )
9import tempfile
108
11# python-shelltoolbox is installed as a dependency of python-charmhelpers.9# python-shelltoolbox is installed as a dependency of python-charmhelpers.
12check_call(['apt-get', 'install', '-y', 'python-charmhelpers'])10check_call(['apt-get', 'install', '-y', 'python-charmhelpers'])
@@ -22,53 +20,33 @@
22)20)
23from shelltoolbox import (21from shelltoolbox import (
24 apt_get_install,22 apt_get_install,
25 cd,
26 command,
27 install_extra_repositories,23 install_extra_repositories,
28 run,
29)24)
3025
31from utils import cmd_log26from utils import (
3227 build,
3328 cmd_log,
34def fetch(juju_gui_branch, juju_api_branch):29 config_json,
35 """Install required dependencies and retrieve Juju/Juju GUI branches."""30 fetch,
31 )
32
33
34DEB_DEPENDENCIES = (
35 'bzr', 'imagemagick', 'make', 'nginx', 'nodejs', 'npm', 'zookeeper')
36
37
38def get_dependencies():
36 log('Installing dependencies.')39 log('Installing dependencies.')
37 cmd_log(install_extra_repositories('ppa:chris-lea/node.js'))40 cmd_log(install_extra_repositories('ppa:chris-lea/node.js'))
38 cmd_log(41 cmd_log(apt_get_install(*DEB_DEPENDENCIES))
39 apt_get_install('bzr', 'imagemagick', 'make',
40 'nodejs', 'npm', 'zookeeper', 'nginx'))
41 log('Retrieving source checkouts.')
42 bzr_checkout = command('bzr', 'co', '--lightweight')
43 cmd_log(bzr_checkout(juju_gui_branch, 'juju-gui'))
44 cmd_log(bzr_checkout(juju_api_branch, 'juju'))
45
46
47def build(logpath):
48 """Set up Juju GUI and nginx."""
49 log('Building Juju GUI.')
50 with cd('juju-gui'):
51 logdir = os.path.dirname(logpath)
52 fd, name = tempfile.mkstemp(prefix='make-', dir=logdir)
53 log('Output from "make" sent to', name)
54 run('make', stdout=fd, stderr=fd)
55 log('Setting up nginx.')
56 nginx_default_site = '/etc/nginx/sites-enabled/default'
57 juju_gui_site = '/etc/nginx/sites-available/juju-gui'
58 if os.path.exists(nginx_default_site):
59 os.remove(nginx_default_site)
60 if not os.path.exists(juju_gui_site):
61 cmd_log(run('touch', juju_gui_site))
62 cmd_log(run('chown', 'ubuntu:', juju_gui_site))
63 cmd_log(
64 run('ln', '-s', juju_gui_site,
65 '/etc/nginx/sites-enabled/juju-gui'))
6642
6743
68def main():44def main():
69 config = get_config()45 config = get_config()
46 get_dependencies()
70 fetch(config['juju-gui-branch'], config['juju-api-branch'])47 fetch(config['juju-gui-branch'], config['juju-api-branch'])
71 build(config['command-log-file'])48 build(config['command-log-file'])
49 config_json.set(config)
7250
7351
74if __name__ == '__main__':52if __name__ == '__main__':
7553
=== modified file 'hooks/start'
--- hooks/start 2012-12-12 11:42:12 +0000
+++ hooks/start 2012-12-18 13:27:18 +0000
@@ -1,100 +1,20 @@
1#!/usr/bin/env python21#!/usr/bin/env python2
2#-*- python -*-2#-*- python -*-
33
4import json
5import os
6
7from charmhelpers import (4from charmhelpers import (
8 get_config,5 get_config,
9 log,6 log,
10 log_entry,7 log_entry,
11 log_exit,8 log_exit,
12 open_port,9 open_port,
13 service_control,
14 START,
15 unit_get,
16)
17from shelltoolbox import (
18 run,
19 su,
20)10)
2111
22from utils import (12from utils import (
23 get_zookeeper_address,13 start_agent,
24 render_to_file,14 start_gui,
15 start_improv,
25)16)
2617
27CURRENT_DIR = os.getcwd()
28JUJU_DIR = os.path.join(CURRENT_DIR, 'juju')
29JUJU_GUI_DIR = os.path.join(CURRENT_DIR, 'juju-gui')
30
31
32def start_gui(juju_api_port, console_enabled, staging):
33 """Set up and start the Juju GUI server."""
34 with su('root'):
35 run('chown', '-R', 'ubuntu:', JUJU_GUI_DIR)
36 build_dir = JUJU_GUI_DIR + '/build-'
37 build_dir += 'debug' if staging else 'prod'
38 log('Setting up Juju GUI start up script.')
39 render_to_file(
40 'juju-gui.conf.template', {'juju_gui_dir': JUJU_GUI_DIR},
41 '/etc/init/juju-gui.conf')
42 log('Generating the Juju GUI configuration file.')
43 context = {
44 'address': unit_get('public-address'),
45 'console_enabled': json.dumps(console_enabled),
46 'port': juju_api_port,
47 }
48 render_to_file(
49 'config.js.template', context,
50 os.path.join(build_dir, 'juju-ui', 'assets', 'config.js'))
51 log('Generating the nginx site configuration file.')
52 context = {
53 'server_root': build_dir
54 }
55 render_to_file(
56 'nginx.conf.template', context,
57 '/etc/nginx/sites-available/juju-gui')
58 log('Starting Juju GUI.')
59 with su('root'):
60 service_control('juju-gui', START)
61
62
63def start_improv(juju_api_port, staging_env):
64 """Start a simulated juju environment using ``improv.py``."""
65 log('Setting up staging start up script.')
66 context = {
67 'juju_dir': JUJU_DIR,
68 'port': juju_api_port,
69 'staging_env': staging_env,
70 }
71 render_to_file(
72 'juju-api-improv.conf.template', context,
73 '/etc/init/juju-api-improv.conf')
74 log('Starting the staging backend.')
75 with su('root'):
76 service_control('juju-api-improv', START)
77
78
79def start_agent(juju_api_port):
80 """Start the Juju agent and connect to the current environment."""
81 # Retrieve the Zookeeper address from the start up script.
82 unit_dir = os.path.realpath(os.path.join(CURRENT_DIR, '..'))
83 agent_file = '/etc/init/juju-{0}.conf'.format(os.path.basename(unit_dir))
84 zookeeper = get_zookeeper_address(agent_file)
85 log('Setting up API agent start up script.')
86 context = {
87 'juju_dir': JUJU_DIR,
88 'port': juju_api_port,
89 'zookeeper': zookeeper,
90 }
91 render_to_file(
92 'juju-api-agent.conf.template', context,
93 '/etc/init/juju-api-agent.conf')
94 log('Starting API agent.')
95 with su('root'):
96 service_control('juju-api-agent', START)
97
9818
99def open_ports(juju_api_port):19def open_ports(juju_api_port):
100 """Expose Juju GUI web server and websocket server ports."""20 """Expose Juju GUI web server and websocket server ports."""
10121
=== modified file 'hooks/stop'
--- hooks/stop 2012-12-10 14:37:15 +0000
+++ hooks/stop 2012-12-18 13:27:18 +0000
@@ -2,28 +2,11 @@
2#-*- python -*-2#-*- python -*-
33
4from charmhelpers import (4from charmhelpers import (
5 get_config,
6 log,
7 log_entry,5 log_entry,
8 log_exit,6 log_exit,
9 service_control,
10 STOP,
11)7)
12from shelltoolbox import su8
139from utils import stop
14
15def stop():
16 """Stop the Juju API agent."""
17 config = get_config()
18 with su('root'):
19 log('Stopping Juju GUI.')
20 service_control('juju-gui', STOP)
21 if config.get('staging'):
22 log('Stopping the staging backend.')
23 service_control('juju-api-improv', STOP)
24 else:
25 log('Stopping API agent.')
26 service_control('juju-api-agent', STOP)
2710
2811
29if __name__ == '__main__':12if __name__ == '__main__':
3013
=== modified file 'hooks/utils.py'
--- hooks/utils.py 2012-12-07 21:16:55 +0000
+++ hooks/utils.py 2012-12-18 13:27:18 +0000
@@ -1,10 +1,51 @@
1"""Juju GUI charm utilities."""1"""Juju GUI charm utilities."""
22
3__all__ = [
4 'AGENT',
5 'build',
6 'cmd_log',
7 'get_zookeeper_address',
8 'GUI',
9 'IMPROV',
10 'render_to_file',
11 'start_agent',
12 'start_gui',
13 'start_improv',
14 'stop',
15 ]
16
17import json
3import os18import os
4import logging19import logging
520import tempfile
6from shelltoolbox import search_file21
7from charmhelpers import get_config22from shelltoolbox import (
23 cd,
24 command,
25 run,
26 search_file,
27 Serializer,
28 su,
29 )
30from charmhelpers import (
31 get_config,
32 log,
33 service_control,
34 START,
35 STOP,
36 unit_get,
37)
38
39
40AGENT = 'juju-api-agent'
41IMPROV = 'juju-api-improv'
42GUI = 'juju-gui'
43CURRENT_DIR = os.getcwd()
44JUJU_DIR = os.path.join(CURRENT_DIR, 'juju')
45JUJU_GUI_DIR = os.path.join(CURRENT_DIR, 'juju-gui')
46
47# Store the configuration from on invocation to the next.
48config_json = Serializer('/tmp/config.json')
849
950
10def get_zookeeper_address(agent_file_path):51def get_zookeeper_address(agent_file_path):
@@ -36,6 +77,7 @@
3677
37results_log = None78results_log = None
3879
80
39def _setupLogging():81def _setupLogging():
40 global results_log82 global results_log
41 if results_log is not None:83 if results_log is not None:
@@ -45,7 +87,7 @@
45 filename=config['command-log-file'],87 filename=config['command-log-file'],
46 level=logging.INFO,88 level=logging.INFO,
47 format="%(asctime)s: %(name)s@%(levelname)s %(message)s")89 format="%(asctime)s: %(name)s@%(levelname)s %(message)s")
48 results_log = logging.getLogger('juju-gui')90 results_log = logging.getLogger(GUI)
4991
5092
51def cmd_log(results):93def cmd_log(results):
@@ -57,3 +99,124 @@
57 # Since 'results' may be multi-line output, start it on a separate line99 # Since 'results' may be multi-line output, start it on a separate line
58 # from the logger timestamp, etc.100 # from the logger timestamp, etc.
59 results_log.info('\n' + results)101 results_log.info('\n' + results)
102
103
104def start_improv(juju_api_port, staging_env,
105 config_path='/etc/init/juju-api-improv.conf'):
106 """Start a simulated juju environment using ``improv.py``."""
107 log('Setting up staging start up script.')
108 context = {
109 'juju_dir': JUJU_DIR,
110 'port': juju_api_port,
111 'staging_env': staging_env,
112 }
113 render_to_file(
114 'juju-api-improv.conf.template', context,
115 config_path)
116 log('Starting the staging backend.')
117 with su('root'):
118 service_control(IMPROV, START)
119
120
121def start_agent(juju_api_port, config_path='/etc/init/juju-api-agent.conf'):
122 """Start the Juju agent and connect to the current environment."""
123 # Retrieve the Zookeeper address from the start up script.
124 unit_dir = os.path.realpath(os.path.join(CURRENT_DIR, '..'))
125 agent_file = '/etc/init/juju-{0}.conf'.format(os.path.basename(unit_dir))
126 zookeeper = get_zookeeper_address(agent_file)
127 log('Setting up API agent start up script.')
128 context = {
129 'juju_dir': JUJU_DIR,
130 'port': juju_api_port,
131 'zookeeper': zookeeper,
132 }
133 render_to_file(
134 'juju-api-agent.conf.template', context,
135 config_path)
136 log('Starting API agent.')
137 with su('root'):
138 service_control(AGENT, START)
139
140
141def start_gui(juju_api_port, console_enabled, staging,
142 config_path='/etc/init/juju-gui.conf',
143 nginx_path='/etc/nginx/sites-available/juju-gui',
144 config_js_path=None):
145 """Set up and start the Juju GUI server."""
146 with su('root'):
147 run('chown', '-R', 'ubuntu:', JUJU_GUI_DIR)
148 build_dir = JUJU_GUI_DIR + '/build-'
149 build_dir += 'debug' if staging else 'prod'
150 log('Setting up Juju GUI start up script.')
151 render_to_file(
152 'juju-gui.conf.template', {}, config_path)
153 log('Generating the Juju GUI configuration file.')
154 context = {
155 'address': unit_get('public-address'),
156 'console_enabled': json.dumps(console_enabled),
157 'port': juju_api_port,
158 }
159 if config_js_path is None:
160 config_js_path = os.path.join(
161 build_dir, 'juju-ui', 'assets', 'config.js')
162 render_to_file(
163 'config.js.template', context,
164 config_js_path)
165 log('Generating the nginx site configuration file.')
166 context = {
167 'server_root': build_dir
168 }
169 render_to_file(
170 'nginx.conf.template', context, nginx_path)
171 log('Starting Juju GUI.')
172 with su('root'):
173 # Stop nginx so it will restart cleanly with the gui.
174 service_control('nginx', STOP)
175 service_control(GUI, START)
176
177
178def stop():
179 """Stop the Juju API agent."""
180 config = get_config()
181 with su('root'):
182 log('Stopping Juju GUI.')
183 service_control(GUI, STOP)
184 if config.get('staging'):
185 log('Stopping the staging backend.')
186 service_control(IMPROV, STOP)
187 else:
188 log('Stopping API agent.')
189 service_control(AGENT, STOP)
190
191
192def fetch(juju_gui_branch, juju_api_branch):
193 """Install required dependencies and retrieve Juju/Juju GUI branches."""
194 log('Retrieving source checkouts.')
195 bzr_checkout = command('bzr', 'co', '--lightweight')
196 if juju_gui_branch is not None:
197 cmd_log(run('rm', '-rf', 'juju-gui'))
198 cmd_log(bzr_checkout(juju_gui_branch, 'juju-gui'))
199 if juju_api_branch is not None:
200 cmd_log(run('rm', '-rf', 'juju'))
201 cmd_log(bzr_checkout(juju_api_branch, 'juju'))
202
203
204def build(logpath):
205 """Set up Juju GUI and nginx."""
206 log('Building Juju GUI.')
207 with cd('juju-gui'):
208 logdir = os.path.dirname(logpath)
209 fd, name = tempfile.mkstemp(prefix='make-', dir=logdir)
210 log('Output from "make" sent to', name)
211 run('make', stdout=fd, stderr=fd)
212 log('Setting up nginx.')
213 nginx_default_site = '/etc/nginx/sites-enabled/default'
214 juju_gui_site = '/etc/nginx/sites-available/juju-gui'
215 if os.path.exists(nginx_default_site):
216 os.remove(nginx_default_site)
217 if not os.path.exists(juju_gui_site):
218 cmd_log(run('touch', juju_gui_site))
219 cmd_log(run('chown', 'ubuntu:', juju_gui_site))
220 cmd_log(
221 run('ln', '-s', juju_gui_site,
222 '/etc/nginx/sites-enabled/juju-gui'))
60223
=== modified file 'revision'
--- revision 2012-12-14 16:48:29 +0000
+++ revision 2012-12-18 13:27:18 +0000
@@ -1,1 +1,1 @@
115116
22
=== modified file 'tests/test_utils.py'
--- tests/test_utils.py 2012-12-12 17:29:39 +0000
+++ tests/test_utils.py 2012-12-18 13:27:18 +0000
@@ -1,5 +1,6 @@
1#!/usr/bin/env python21#!/usr/bin/env python2
22
3from contextlib import contextmanager
3import os4import os
4import tempfile5import tempfile
5import unittest6import unittest
@@ -10,7 +11,13 @@
10 cmd_log,11 cmd_log,
11 get_zookeeper_address,12 get_zookeeper_address,
12 render_to_file,13 render_to_file,
14 start_agent,
15 start_gui,
16 start_improv,
17 stop,
13)18)
19# Import the whole utils package for monkey patching.
20import utils
1421
1522
16class GetZookeeperAddressTest(unittest.TestCase):23class GetZookeeperAddressTest(unittest.TestCase):
@@ -68,5 +75,106 @@
68 self.assertTrue(line.endswith(': juju-gui@INFO \nfoo\n'))75 self.assertTrue(line.endswith(': juju-gui@INFO \nfoo\n'))
6976
7077
78class StartStopTest(unittest.TestCase):
79
80 def setUp(self):
81 self.service_names = []
82 self.actions = []
83 self.svc_ctl_call_count = 0
84 self.fake_zk_address = '192.168.5.26'
85 # Monkey patches.
86 self.command = charmhelpers.command
87
88 def service_control_mock(service_name, action):
89 self.svc_ctl_call_count += 1
90 self.service_names.append(service_name)
91 self.actions.append(action)
92
93 def noop(*args):
94 pass
95
96 @contextmanager
97 def su(user):
98 yield None
99
100 def get_zookeeper_address_mock(fp):
101 return self.fake_zk_address
102
103 self.functions = dict(
104 service_control=(utils.service_control, service_control_mock),
105 log=(utils.log, noop),
106 su=(utils.su, su),
107 run=(utils.run, noop),
108 unit_get=(utils.unit_get, noop),
109 get_zookeeper_address=(
110 utils.get_zookeeper_address, get_zookeeper_address_mock)
111 )
112 # Apply the patches.
113 for fn, fcns in self.functions.items():
114 setattr(utils, fn, fcns[1])
115
116 self.destination_file = tempfile.NamedTemporaryFile()
117 self.addCleanup(self.destination_file.close)
118
119 def tearDown(self):
120 # Undo all of the monkey patching.
121 for fn, fcns in self.functions.items():
122 setattr(utils, fn, fcns[0])
123 charmhelpers.command = self.command
124
125 def test_start_improv(self):
126 port = '1234'
127 staging_env = 'large'
128 start_improv(port, staging_env, self.destination_file.name)
129 conf = self.destination_file.read()
130 self.assertTrue('--port %s' % port in conf)
131 self.assertTrue(staging_env + '.json' in conf)
132 self.assertEqual(self.svc_ctl_call_count, 1)
133 self.assertEqual(self.service_names, ['juju-api-improv'])
134 self.assertEqual(self.actions, [charmhelpers.START])
135
136 def test_start_agent(self):
137 port = '1234'
138 start_agent(port, self.destination_file.name)
139 conf = self.destination_file.read()
140 self.assertTrue('--port %s' % port in conf)
141 self.assertTrue('JUJU_ZOOKEEPER=%s' % self.fake_zk_address in conf)
142 self.assertEqual(self.svc_ctl_call_count, 1)
143 self.assertEqual(self.service_names, ['juju-api-agent'])
144 self.assertEqual(self.actions, [charmhelpers.START])
145
146 def test_start_gui(self):
147 port = '1234'
148 nginx_file = tempfile.NamedTemporaryFile()
149 self.addCleanup(nginx_file.close)
150 config_js_file = tempfile.NamedTemporaryFile()
151 self.addCleanup(config_js_file.close)
152 start_gui(port, False, True, self.destination_file.name,
153 nginx_file.name, config_js_file.name)
154 conf = self.destination_file.read()
155 self.assertTrue('/usr/sbin/nginx' in conf)
156 nginx_conf = nginx_file.read()
157 self.assertTrue('juju-gui/build-debug' in nginx_conf)
158 self.assertEqual(self.svc_ctl_call_count, 2)
159 self.assertEqual(self.service_names, ['nginx', 'juju-gui'])
160 self.assertEqual(self.actions, [charmhelpers.STOP, charmhelpers.START])
161
162 def test_stop_staging(self):
163 mock_config = {'staging': True}
164 charmhelpers.command = lambda *args: lambda: dumps(mock_config)
165 stop()
166 self.assertEqual(self.svc_ctl_call_count, 2)
167 self.assertEqual(self.service_names, ['juju-gui', 'juju-api-improv'])
168 self.assertEqual(self.actions, [charmhelpers.STOP, charmhelpers.STOP])
169
170 def test_stop_production(self):
171 mock_config = {'staging': False}
172 charmhelpers.command = lambda *args: lambda: dumps(mock_config)
173 stop()
174 self.assertEqual(self.svc_ctl_call_count, 2)
175 self.assertEqual(self.service_names, ['juju-gui', 'juju-api-agent'])
176 self.assertEqual(self.actions, [charmhelpers.STOP, charmhelpers.STOP])
177
178
71if __name__ == '__main__':179if __name__ == '__main__':
72 unittest.main(verbosity=2)180 unittest.main(verbosity=2)

Subscribers

People subscribed via source and target branches