Merge lp:~teknico/charms/precise/juju-gui/integrate-builtin-server-2 into lp:~juju-gui/charms/precise/juju-gui/trunk
- Precise Pangolin (12.04)
- integrate-builtin-server-2
- Merge into trunk
Status: | Merged |
---|---|
Merged at revision: | 89 |
Proposed branch: | lp:~teknico/charms/precise/juju-gui/integrate-builtin-server-2 |
Merge into: | lp:~juju-gui/charms/precise/juju-gui/trunk |
Diff against target: |
794 lines (+267/-90) 16 files modified
Makefile (+1/-1) Operation.md (+13/-2) config.yaml (+7/-0) config/apache-site.template (+1/-1) config/guiserver.conf.template (+13/-0) hooks/backend.py (+28/-1) hooks/utils.py (+90/-32) revision (+1/-1) server/guiserver/apps.py (+2/-2) server/guiserver/manage.py (+3/-2) server/guiserver/tests/test_apps.py (+2/-2) server/runserver.py (+2/-2) server/setup.py (+1/-1) tests/20-functional.test (+17/-3) tests/test_backends.py (+23/-15) tests/test_utils.py (+63/-25) |
To merge this branch: | bzr merge lp:~teknico/charms/precise/juju-gui/integrate-builtin-server-2 |
Related bugs: |
Reviewer | Review Type | Date Requested | Status |
---|---|---|---|
charmers | Pending | ||
Review via email: mp+179138@code.launchpad.net |
Commit message
Description of the change
Integrate the built-in server into the charm.
Add a "builtin-server" option to config.yaml, defaulting to false, to enable
a new Tornado-based built-in web server, in place of haproxy and Apache (still
the default).
Add a "guiserver.conf" Upstart config file, generated via a template.
Add a BuiltinServerMixin to hooks/backend.py .
Add tests for the new functions in hooks/utils.py .
Clean up all paths and constants in hooks/utils.py .
Miscellaneuos cleanup.
QA1 (quick):
$ make unittest
QA2 (long, 40 minutes):
$ make ftest
QA3 (long but shorter than the former):
$ juju bootstrap
$ make deploy
(check web access)
$ juju set juju-gui builtin-server=true
(check web access)
$ juju set juju-gui builtin-
(check web access)
$ juju destroy-environment
Nicola Larosa (teknico) wrote : | # |
Francesco Banconi (frankban) wrote : | # |
This branch is nice Nicola, we are almost there, thank you.
Please fix the problem I described below, I will QA again later.
https:/
File config/
https:/
config/
/usr/local/
Cool.
https:/
File hooks/utils.py (right):
https:/
hooks/utils.py:110: SERVER_DIR = os.path.
I am not sure I like all those constants. I agree they are used in more
than one place (but usually only two times AFAIK, and e.g. UNIT_NAME is
used only once), so this prevents typos, but I also liked the
possibility to read the functions without having to jump at the
beginning of the file. As I said, I see the value in this change, and I
can live with this.
The real request here is to fix all those "os.path.join('', ...": see
below.
https:/
hooks/utils.py:132: config_json = Serializer(
'config.json'))
I don't think this is the same as before: this results in
"tmp/config.json".
You might want to use "os.path.
but I am also ok with the original version.
Note that this generates an install error, that prevented me to QA this
branch. I'll retry it later when this is fixed.
https:/
hooks/utils.py:549: api_url = '{}://127.
The "x if y else z" is not so easy to read when it is not a one-liner
IMHO.
I had to read this carefully, perhaps the more usual "if: else:" can be
better?
https:/
hooks/utils.py:564: def start_builtin_
ssl_cert_path, insecure):
What do you think about inverting serve_tests and ssl_cert_path? This
way we can have a similar signature for start_builtin_
write_builtin_
https:/
File tests/20-
https:/
tests/20-
server_header)
The header check is great. I'd also add an environment check, i.e. we
must ensure that the WebSocket is connected (basically the same thing
we do in the other functional tests).
Nicola Larosa (teknico) wrote : | # |
frankban wrote:
https:/
> hooks/utils.py:110: SERVER_DIR = os.path.
> I also liked the possibility to read the functions without having
> to jump at the beginning of the file.
Hopefully the names of the constants are meaningful enough that you
don't need to see the associated values to be able to understand the
code.
> As I said, I see the value in this change, and I can live with this.
Good.
https:/
> hooks/utils.py:132: config_json = Serializer(
> 'config.json'))
> I don't think this is the same as before: this results in
> "tmp/config.json". You might want to use "os.path.
> 'tmp', 'config.json')", but I am also ok with the original version.
Oh, sorry, I did not check it, will fix.
> Note that this generates an install error, that prevented me to
> QA this branch. I'll retry it later when this is fixed.
Weird, this did not happen to me in QA.
https:/
> hooks/utils.py:549: api_url = '{}://127.
> The "x if y else z" is not so easy to read when it is not a
> one-liner IMHO. I had to read this carefully, perhaps the more
> usual "if: else:" can be better?
Hrmpf. You know I like it, and I know you don't like it. Is it really
that ugly? :-)
https:/
> hooks/utils.py:564: def start_builtin_
> ssl_cert_path, insecure):
> What do you think about inverting serve_tests and ssl_cert_path?
> This way we can have a similar signature for start_builtin_
> and write_builtin_
Uhm, I don't like it either but there was a reason for it, I'll check.
https:/
> tests/20-
server_header)
> The header check is great. I'd also add an environment check, i.e. we
> must ensure that the WebSocket is connected (basically the same thing
> we do in the other functional tests).
Will do.
- 89. By Nicola Larosa
-
Fixes per review.
- 90. By Nicola Larosa
-
More fixes.
- 91. By Nicola Larosa
-
Merge from trunk.
Nicola Larosa (teknico) wrote : | # |
Please take a look.
Madison Scott-Clary (makyo) wrote : | # |
Code looks good with Francesco's comments, QAing now.
https:/
File hooks/utils.py (right):
https:/
hooks/utils.py:549: api_url = '{}://127.
On 2013/08/08 11:12:58, frankban wrote:
> The "x if y else z" is not so easy to read when it is not a one-liner
IMHO.
> I had to read this carefully, perhaps the more usual "if: else:" can
be better?
I agree in this instance. The x if y else z construct works much better
in a single-line format than across multiple lines; in this instance,
it's hard to tell that this is conditional until halfway through, then
you have to parse it inside out across multiple lines.
Madison Scott-Clary (makyo) wrote : | # |
QA3 worked for me, LGTM
Francesco Banconi (frankban) wrote : | # |
LGTM with the changes below.
I run the functional tests, they were passing, the last one was stopped
by the timeout: 40 mins are not enough, please increase that value (60
mins?).
QA:
switching from the legacy to the new server (and back) worked well. The
builtin server stopped working after setting secure=false on the charm.
I think upstart is not happy with the generated configuration file when
--insecure is passed.
I believe this branch can land with the timeout fix, but please add a
card to fix the "insecure" bug. Thank you!
Nicola Larosa (teknico) wrote : | # |
frankban wrote:
> I run the functional tests, they were passing, the last one was
stopped
> by the timeout: 40 mins are not enough, please increase that value
> (60 mins?).
Timeout increased to 60 minutes.
> QA:
> switching from the legacy to the new server (and back) worked well.
The
> I think upstart builtin server stopped working after setting
> secure=false on the charm. is not happy with the generated
> configuration file when --insecure is passed.
Indeed, there was a mistake in config/
thank you.
- 92. By Nicola Larosa
-
Increase functional tests timeout, fix config/
guiserver. conf.template . - 93. By Nicola Larosa
-
Fix the insecure and testsroot options handling of the server.
- 94. By Nicola Larosa
-
Remove leftover assert in test.
- 95. By Nicola Larosa
-
Better invocation example, shorten conditions.
Nicola Larosa (teknico) wrote : | # |
*** Submitted:
Integrate the built-in server into the charm.
Add a "builtin-server" option to config.yaml, defaulting to false, to
enable
a new Tornado-based built-in web server, in place of haproxy and Apache
(still
the default).
Add a "guiserver.conf" Upstart config file, generated via a template.
Add a BuiltinServerMixin to hooks/backend.py .
Add tests for the new functions in hooks/utils.py .
Clean up all paths and constants in hooks/utils.py .
Miscellaneuos cleanup.
QA1 (quick):
$ make unittest
QA2 (long, 40 minutes):
$ make ftest
QA3 (long but shorter than the former):
$ juju bootstrap
$ make deploy
(check web access)
$ juju set juju-gui builtin-server=true
(check web access)
$ juju set juju-gui builtin-
(check web access)
$ juju destroy-environment
R=frankban, matthew.scott
CC=
https:/
Preview Diff
1 | === modified file 'Makefile' |
2 | --- Makefile 2013-08-06 14:17:23 +0000 |
3 | +++ Makefile 2013-08-09 13:41:32 +0000 |
4 | @@ -14,7 +14,7 @@ |
5 | # You should have received a copy of the GNU Affero General Public License |
6 | # along with this program. If not, see <http://www.gnu.org/licenses/>. |
7 | |
8 | -JUJUTEST = juju-test --timeout=40m -v -e "$(JUJU_ENV)" --upload-tools |
9 | +JUJUTEST = juju-test --timeout=60m -v -e "$(JUJU_ENV)" --upload-tools |
10 | VENV = ./tests/.venv |
11 | |
12 | all: setup |
13 | |
14 | === modified file 'Operation.md' |
15 | --- Operation.md 2013-07-29 10:40:50 +0000 |
16 | +++ Operation.md 2013-08-09 13:41:32 +0000 |
17 | @@ -12,8 +12,8 @@ |
18 | ## How it works ## |
19 | |
20 | The Juju GUI is a client-side, JavaScript application that runs inside a |
21 | -web browser. The browser connects to a custom-made server deployed by |
22 | -the charm. |
23 | +web browser. The browser connects to a built-in server deployed by the |
24 | +charm. |
25 | |
26 | ## Server ## |
27 | |
28 | @@ -27,3 +27,14 @@ |
29 | Juju connections are bidirectional, using the WebSocket protocol on the |
30 | same port as the HTTPS connection, allowing changes in the Juju |
31 | environment to be propagated and shown immediately by the browser. |
32 | + |
33 | +## Activation ## |
34 | + |
35 | +Previously the Juju GUI has been served by a combination of haproxy and |
36 | +Apache, specifically deployed and configured by the charm. |
37 | + |
38 | +The new built-in server replaces them both and can be enabled by |
39 | +setting the config option `builtin-server` to `true`. |
40 | + |
41 | +In the future haproxy, Apache and the mentioned config option will be |
42 | +removed; only the built-in server will remain. |
43 | |
44 | === modified file 'config.yaml' |
45 | --- config.yaml 2013-08-05 09:38:39 +0000 |
46 | +++ config.yaml 2013-08-09 13:41:32 +0000 |
47 | @@ -166,3 +166,10 @@ |
48 | a link to juju.ubuntu.com |
49 | type: boolean |
50 | default: false |
51 | + builtin-server: |
52 | + description: | |
53 | + Enable the built-in server, disabling both haproxy and Apache. |
54 | + This is a temporary option: the built-in server will be |
55 | + the only server in the future. |
56 | + type: boolean |
57 | + default: false |
58 | |
59 | === modified file 'config/apache-site.template' |
60 | --- config/apache-site.template 2013-08-01 14:33:00 +0000 |
61 | +++ config/apache-site.template 2013-08-09 13:41:32 +0000 |
62 | @@ -21,7 +21,7 @@ |
63 | |
64 | CustomLog ${APACHE_LOG_DIR}/access.log combined |
65 | |
66 | - {{if serve_tests}} |
67 | + {{if tests_root}} |
68 | Alias /test {{tests_root}} |
69 | {{endif}} |
70 | |
71 | |
72 | === added file 'config/guiserver.conf.template' |
73 | --- config/guiserver.conf.template 1970-01-01 00:00:00 +0000 |
74 | +++ config/guiserver.conf.template 2013-08-09 13:41:32 +0000 |
75 | @@ -0,0 +1,13 @@ |
76 | +description "GUIServer" |
77 | +author "Canonical" |
78 | + |
79 | +start on (filesystem and net-device-up IFACE=lo) |
80 | +stop on runlevel [!2345] |
81 | + |
82 | +exec /usr/bin/python /usr/local/bin/runserver.py \ |
83 | + --guiroot="{{gui_root}}" \ |
84 | + --apiurl="{{api_url}}" \ |
85 | + --apiversion="{{api_version}}" \ |
86 | + --sslpath="{{ssl_cert_path}}" \ |
87 | + {{if tests_root}} --testsroot="{{tests_root}}" {{endif}} \ |
88 | + {{if insecure}} --insecure {{endif}} |
89 | |
90 | === added directory 'deps' |
91 | === added file 'deps/tornado-3.1.tar.gz' |
92 | Binary files deps/tornado-3.1.tar.gz 1970-01-01 00:00:00 +0000 and deps/tornado-3.1.tar.gz 2013-08-09 13:41:32 +0000 differ |
93 | === modified file 'hooks/backend.py' |
94 | --- hooks/backend.py 2013-08-05 09:59:17 +0000 |
95 | +++ hooks/backend.py 2013-08-09 13:41:32 +0000 |
96 | @@ -180,6 +180,28 @@ |
97 | utils.stop_haproxy_apache() |
98 | |
99 | |
100 | +class BuiltinServerMixin(ServerInstallMixinBase): |
101 | + """Manage the builtin server via Upstart.""" |
102 | + |
103 | + debs = ('openssl', 'python-pip') |
104 | + |
105 | + def install(self, backend): |
106 | + utils.install_tornado() |
107 | + utils.install_builtin_server() |
108 | + self._setup_certificates(backend) |
109 | + |
110 | + def start(self, backend): |
111 | + config = backend.config |
112 | + build_dir = utils.compute_build_dir( |
113 | + config['staging'], config['serve-tests']) |
114 | + utils.start_builtin_server( |
115 | + build_dir, config['ssl-cert-path'], config['serve-tests'], |
116 | + not config['secure']) |
117 | + |
118 | + def stop(self, backend): |
119 | + utils.stop_builtin_server() |
120 | + |
121 | + |
122 | def chain_methods(name): |
123 | """Helper to compose a set of mixin objects into a callable. |
124 | |
125 | @@ -249,7 +271,12 @@ |
126 | |
127 | # We always install and start the GUI. |
128 | self.mixins.append(GuiMixin()) |
129 | - self.mixins.append(HaproxyApacheMixin()) |
130 | + # TODO: eventually this option will go away, as well as haproxy and |
131 | + # Apache. |
132 | + if config['builtin-server']: |
133 | + self.mixins.append(BuiltinServerMixin()) |
134 | + else: |
135 | + self.mixins.append(HaproxyApacheMixin()) |
136 | |
137 | def different(self, *keys): |
138 | """Return a boolean indicating if the current config |
139 | |
140 | === modified file 'hooks/utils.py' |
141 | --- hooks/utils.py 2013-08-08 08:13:24 +0000 |
142 | +++ hooks/utils.py 2013-08-09 13:41:32 +0000 |
143 | @@ -17,19 +17,14 @@ |
144 | """Juju GUI charm utilities.""" |
145 | |
146 | __all__ = [ |
147 | - 'AGENT', |
148 | - 'APACHE', |
149 | 'APACHE_SITE', |
150 | 'APACHE_PORTS', |
151 | 'API_PORT', |
152 | 'CURRENT_DIR', |
153 | - 'HAPROXY', |
154 | - 'IMPROV', |
155 | 'JUJU_DIR', |
156 | 'JUJU_GUI_DIR', |
157 | 'JUJU_PEM', |
158 | 'WEB_PORT', |
159 | - 'bzr_checkout', |
160 | 'cmd_log', |
161 | 'compute_build_dir', |
162 | 'fetch_api', |
163 | @@ -53,9 +48,11 @@ |
164 | 'setup_gui', |
165 | 'setup_haproxy_config', |
166 | 'start_agent', |
167 | + 'start_builtin_server', |
168 | 'start_haproxy_apache', |
169 | 'start_improv', |
170 | 'stop_agent', |
171 | + 'stop_builtin_server', |
172 | 'stop_haproxy_apache', |
173 | 'stop_improv', |
174 | 'write_gui_config', |
175 | @@ -99,20 +96,31 @@ |
176 | |
177 | AGENT = 'juju-api-agent' |
178 | APACHE = 'apache2' |
179 | +BUILTIN_SERVER = 'guiserver' |
180 | +HAPROXY = 'haproxy' |
181 | IMPROV = 'juju-api-improv' |
182 | -HAPROXY = 'haproxy' |
183 | |
184 | API_PORT = 8080 |
185 | WEB_PORT = 8000 |
186 | |
187 | CURRENT_DIR = os.getcwd() |
188 | +CONFIG_DIR = os.path.join(CURRENT_DIR, 'config') |
189 | JUJU_DIR = os.path.join(CURRENT_DIR, 'juju') |
190 | JUJU_GUI_DIR = os.path.join(CURRENT_DIR, 'juju-gui') |
191 | -APACHE_SITE = '/etc/apache2/sites-available/juju-gui' |
192 | -APACHE_PORTS = '/etc/apache2/ports.conf' |
193 | -HAPROXY_PATH = '/etc/haproxy/haproxy.cfg' |
194 | -SYS_INIT_DIR = '/etc/init/' |
195 | -CONFIG_DIR = os.path.join(os.path.dirname(__file__), '..', 'config') |
196 | +SERVER_DIR = os.path.join(CURRENT_DIR, 'server') |
197 | +TORNADO_PATH = os.path.join(CURRENT_DIR, 'deps', 'tornado-3.1.tar.gz') |
198 | + |
199 | +APACHE_CFG_DIR = os.path.join(os.path.sep, 'etc', 'apache2') |
200 | +APACHE_PORTS = os.path.join(APACHE_CFG_DIR, 'ports.conf') |
201 | +APACHE_SITE = os.path.join(APACHE_CFG_DIR, 'sites-available', 'juju-gui') |
202 | +HAPROXY_CFG_PATH = os.path.join(os.path.sep, 'etc', 'haproxy', 'haproxy.cfg') |
203 | + |
204 | +SYS_INIT_DIR = os.path.join(os.path.sep, 'etc', 'init') |
205 | +AGENT_INIT_PATH = os.path.join(SYS_INIT_DIR, 'juju-api-agent.conf') |
206 | +GUISERVER_INIT_PATH = os.path.join(SYS_INIT_DIR, 'guiserver.conf') |
207 | +HAPROXY_INIT_PATH = os.path.join(SYS_INIT_DIR, 'haproxy.conf') |
208 | +IMPROV_INIT_PATH = os.path.join(SYS_INIT_DIR, 'juju-api-improv.conf') |
209 | + |
210 | JUJU_PEM = 'juju.includes-private-key.pem' |
211 | DEB_BUILD_DEPENDENCIES = ( |
212 | 'bzr', 'g++', 'imagemagick', 'make', 'nodejs', 'npm', |
213 | @@ -120,7 +128,7 @@ |
214 | |
215 | |
216 | # Store the configuration from on invocation to the next. |
217 | -config_json = Serializer('/tmp/config.json') |
218 | +config_json = Serializer(os.path.join(os.path.sep, 'tmp', 'config.json')) |
219 | # Bazaar checkout command. |
220 | bzr_checkout = command('bzr', 'co', '--lightweight') |
221 | # Whether or not the charm is deployed using juju-core. |
222 | @@ -289,8 +297,7 @@ |
223 | The argument *destination* is a file path. |
224 | The argument *context* is a dict-like object. |
225 | """ |
226 | - template_path = os.path.join( |
227 | - os.path.dirname(__file__), '..', 'config', template_name) |
228 | + template_path = os.path.join(CONFIG_DIR, template_name) |
229 | template = tempita.Template.from_filename(template_path) |
230 | with open(destination, 'w') as stream: |
231 | stream.write(template.substitute(context)) |
232 | @@ -328,9 +335,7 @@ |
233 | 'port': API_PORT, |
234 | 'staging_env': staging_env, |
235 | } |
236 | - render_to_file( |
237 | - 'juju-api-improv.conf.template', context, |
238 | - '/etc/init/juju-api-improv.conf') |
239 | + render_to_file('juju-api-improv.conf.template', context, IMPROV_INIT_PATH) |
240 | log('Starting the staging backend.') |
241 | with su('root'): |
242 | service_control(IMPROV, START) |
243 | @@ -342,15 +347,15 @@ |
244 | with su('root'): |
245 | service_control(IMPROV, STOP) |
246 | log('Removing the staging Upstart script.') |
247 | - cmd_log(run('rm', '-f', '/etc/init/juju-api-improv.conf')) |
248 | + cmd_log(run('rm', '-f', IMPROV_INIT_PATH)) |
249 | |
250 | |
251 | def start_agent(ssl_cert_path, read_only=False): |
252 | """Start the Juju agent and connect to the current environment.""" |
253 | # Retrieve the Zookeeper address from the start up script. |
254 | - unit_dir = os.path.realpath(os.path.join(CURRENT_DIR, '..')) |
255 | - agent_file = os.path.join( |
256 | - SYS_INIT_DIR, 'juju-{}.conf'.format(os.path.basename(unit_dir))) |
257 | + unit_name = os.path.basename( |
258 | + os.path.realpath(os.path.join(CURRENT_DIR, '..'))) |
259 | + agent_file = os.path.join(SYS_INIT_DIR, 'juju-{}.conf'.format(unit_name)) |
260 | zookeeper = get_zookeeper_address(agent_file) |
261 | log('Setting up the API agent Upstart script.') |
262 | context = { |
263 | @@ -360,9 +365,7 @@ |
264 | 'zookeeper': zookeeper, |
265 | 'read_only': read_only |
266 | } |
267 | - render_to_file( |
268 | - 'juju-api-agent.conf.template', context, |
269 | - '/etc/init/juju-api-agent.conf') |
270 | + render_to_file('juju-api-agent.conf.template', context, AGENT_INIT_PATH) |
271 | log('Starting the API agent.') |
272 | with su('root'): |
273 | service_control(AGENT, START) |
274 | @@ -374,7 +377,7 @@ |
275 | with su('root'): |
276 | service_control(AGENT, STOP) |
277 | log('Removing the API agent Upstart script.') |
278 | - cmd_log(run('rm', '-f', '/etc/init/juju-api-agent.conf')) |
279 | + cmd_log(run('rm', '-f', AGENT_INIT_PATH)) |
280 | |
281 | |
282 | def compute_build_dir(in_staging, serve_tests): |
283 | @@ -442,7 +445,7 @@ |
284 | is_legacy_juju = legacy_juju() |
285 | if is_legacy_juju: |
286 | # The PyJuju API agent is listening on localhost. |
287 | - api_address = '127.0.0.1:{0}'.format(API_PORT) |
288 | + api_address = '127.0.0.1:{}'.format(API_PORT) |
289 | else: |
290 | # Retrieve the juju-core API server address. |
291 | api_address = get_api_address() |
292 | @@ -458,25 +461,24 @@ |
293 | 'web_port': WEB_PORT, |
294 | 'secure': secure |
295 | } |
296 | - render_to_file('haproxy.cfg.template', context, HAPROXY_PATH) |
297 | + render_to_file('haproxy.cfg.template', context, HAPROXY_CFG_PATH) |
298 | |
299 | |
300 | def remove_haproxy_setup(): |
301 | """Remove haproxy setup.""" |
302 | log('Removing haproxy setup.') |
303 | - cmd_log(run('rm', '-f', HAPROXY_PATH)) |
304 | - config_path = os.path.join(SYS_INIT_DIR, 'haproxy.conf') |
305 | - cmd_log(run('rm', '-f', config_path)) |
306 | + cmd_log(run('rm', '-f', HAPROXY_CFG_PATH)) |
307 | + cmd_log(run('rm', '-f', HAPROXY_INIT_PATH)) |
308 | |
309 | |
310 | def setup_apache_config(build_dir, serve_tests=False): |
311 | """Set up the Apache configuration.""" |
312 | log('Generating the Apache site configuration files.') |
313 | + tests_root = os.path.join(JUJU_GUI_DIR, 'test', '') if serve_tests else '' |
314 | context = { |
315 | 'port': WEB_PORT, |
316 | - 'serve_tests': serve_tests, |
317 | 'server_root': build_dir, |
318 | - 'tests_root': os.path.join(JUJU_GUI_DIR, 'test', ''), |
319 | + 'tests_root': tests_root, |
320 | } |
321 | render_to_file('apache-ports.template', context, APACHE_PORTS) |
322 | cmd_log(run('chown', 'ubuntu:', APACHE_PORTS)) |
323 | @@ -523,6 +525,62 @@ |
324 | remove_apache_setup() |
325 | |
326 | |
327 | +def install_tornado(): |
328 | + """Install Tornado from a local tarball.""" |
329 | + log('Installing Tornado.') |
330 | + with su('root'): |
331 | + cmd_log(run('pip', 'install', TORNADO_PATH)) |
332 | + |
333 | + |
334 | +def install_builtin_server(): |
335 | + """Install the builtin server code.""" |
336 | + log('Installing the builtin server.') |
337 | + setup_cmd = os.path.join(SERVER_DIR, 'setup.py') |
338 | + with su('root'): |
339 | + cmd_log(run('/usr/bin/python', setup_cmd, 'install')) |
340 | + |
341 | + |
342 | +def write_builtin_server_startup( |
343 | + gui_root, ssl_cert_path, serve_tests=False, insecure=False): |
344 | + """Generate the builtin server Upstart file.""" |
345 | + log('Generating the builtin server Upstart file.') |
346 | + url_prefix = 'ws' if insecure else 'wss' |
347 | + is_legacy_juju = legacy_juju() |
348 | + api_address = get_api_address() |
349 | + if is_legacy_juju: |
350 | + api_url = '{}://127.0.0.1:{}'.format(url_prefix, API_PORT) |
351 | + else: |
352 | + api_url = '{}://{}'.format(url_prefix, api_address) |
353 | + tests_root = os.path.join(JUJU_GUI_DIR, 'test', '') if serve_tests else '' |
354 | + context = { |
355 | + 'gui_root': gui_root, |
356 | + 'api_url': api_url, |
357 | + 'api_version': 'python' if is_legacy_juju else 'go', |
358 | + 'ssl_cert_path': ssl_cert_path, |
359 | + 'tests_root': tests_root, |
360 | + 'insecure': insecure, |
361 | + } |
362 | + render_to_file( |
363 | + 'guiserver.conf.template', context, GUISERVER_INIT_PATH) |
364 | + |
365 | + |
366 | +def start_builtin_server(build_dir, ssl_cert_path, serve_tests, insecure): |
367 | + """Start the builtin server.""" |
368 | + write_builtin_server_startup( |
369 | + build_dir, ssl_cert_path, serve_tests, insecure) |
370 | + log('Starting the builtin server.') |
371 | + with su('root'): |
372 | + service_control(BUILTIN_SERVER, RESTART) |
373 | + |
374 | + |
375 | +def stop_builtin_server(): |
376 | + """Stop the builtin server.""" |
377 | + log('Stopping the builtin server.') |
378 | + with su('root'): |
379 | + service_control(BUILTIN_SERVER, STOP) |
380 | + cmd_log(run('rm', '-f', GUISERVER_INIT_PATH)) |
381 | + |
382 | + |
383 | def get_npm_cache_archive_url(Launchpad=Launchpad): |
384 | """Figure out the URL of the most recent NPM cache archive on Launchpad.""" |
385 | launchpad = Launchpad.login_anonymously('Juju GUI charm', 'production') |
386 | |
387 | === modified file 'revision' |
388 | --- revision 2013-08-08 13:09:10 +0000 |
389 | +++ revision 2013-08-09 13:41:32 +0000 |
390 | @@ -1,1 +1,1 @@ |
391 | -67 |
392 | +68 |
393 | |
394 | === modified file 'server/guiserver/apps.py' |
395 | --- server/guiserver/apps.py 2013-08-07 16:00:27 +0000 |
396 | +++ server/guiserver/apps.py 2013-08-09 13:41:32 +0000 |
397 | @@ -50,8 +50,8 @@ |
398 | (r'^/juju-ui/(.*)', web.StaticFileHandler, {'path': static_path}), |
399 | (r'^/(favicon\.ico)$', web.StaticFileHandler, {'path': guiroot}), |
400 | ] |
401 | - if options.servetests: |
402 | - params = {'path': options.servetests, 'default_filename': 'index.html'} |
403 | + if options.testsroot: |
404 | + params = {'path': options.testsroot, 'default_filename': 'index.html'} |
405 | server_handlers.append( |
406 | # Serve the Juju GUI tests. |
407 | (r'^/test/(.*)', web.StaticFileHandler, params), |
408 | |
409 | === modified file 'server/guiserver/manage.py' |
410 | --- server/guiserver/manage.py 2013-08-05 09:38:39 +0000 |
411 | +++ server/guiserver/manage.py 2013-08-09 13:41:32 +0000 |
412 | @@ -100,8 +100,9 @@ |
413 | help='the Juju API version/implementation. Currently the possible ' |
414 | 'values are "go" (default) or "python".') |
415 | define( |
416 | - 'servetests', type=str, |
417 | - help='The Juju GUI tests path. If not provided, tests are not served.') |
418 | + 'testsroot', type=str, |
419 | + help='The filesystem path of the Juju GUI tests directory. ' |
420 | + 'If not provided, tests are not served.') |
421 | define( |
422 | 'sslpath', type=str, default=DEFAULT_SSL_PATH, |
423 | help='The path where the SSL certificates are stored.') |
424 | |
425 | === modified file 'server/guiserver/tests/test_apps.py' |
426 | --- server/guiserver/tests/test_apps.py 2013-08-07 16:36:38 +0000 |
427 | +++ server/guiserver/tests/test_apps.py 2013-08-09 13:41:32 +0000 |
428 | @@ -77,7 +77,7 @@ |
429 | |
430 | def test_serving_gui_tests(self): |
431 | # The server can be configured to serve GUI unit tests. |
432 | - app = self.get_app(servetests='/my/tests/') |
433 | + app = self.get_app(testsroot='/my/tests/') |
434 | spec = self.get_url_spec(app, r'^/test/(.*)$') |
435 | self.assertIsNotNone(spec) |
436 | self.assertIn('path', spec.kwargs) |
437 | @@ -85,7 +85,7 @@ |
438 | |
439 | def test_not_serving_gui_tests(self): |
440 | # The server can be configured to avoid serving GUI unit tests. |
441 | - app = self.get_app(servetests=None) |
442 | + app = self.get_app(testsroot=None) |
443 | spec = self.get_url_spec(app, r'^/test/(.*)$') |
444 | self.assertIsNone(spec) |
445 | |
446 | |
447 | === modified file 'server/runserver.py' |
448 | --- server/runserver.py 2013-08-07 09:50:50 +0000 |
449 | +++ server/runserver.py 2013-08-09 13:41:32 +0000 |
450 | @@ -20,8 +20,8 @@ |
451 | --guiroot="/var/lib/juju/agents/unit-juju-gui-0/charm/juju-gui/build-prod" |
452 | --apiurl="wss://ec2-75-101-177-185.compute-1.example.com:17070" |
453 | --apiversion="go" |
454 | - --sslpath=/etc/ssl/juju-gui |
455 | - --servetests |
456 | + --sslpath="/etc/ssl/juju-gui" |
457 | + --tests_root="/var/lib/juju/agents/unit-juju-gui-0/charm/juju-gui/test/" |
458 | --insecure |
459 | |
460 | The --sslpath option is ignored if --insecure is set. |
461 | |
462 | === modified file 'server/setup.py' |
463 | --- server/setup.py 2013-07-17 15:20:47 +0000 |
464 | +++ server/setup.py 2013-08-09 13:41:32 +0000 |
465 | @@ -38,7 +38,7 @@ |
466 | keywords='juju gui server', |
467 | packages=[ |
468 | PROJECT_NAME, |
469 | - '{0}.tests'.format(PROJECT_NAME), |
470 | + '{}.tests'.format(PROJECT_NAME), |
471 | ], |
472 | scripts=['runserver.py'], |
473 | classifiers=[ |
474 | |
475 | === modified file 'tests/20-functional.test' |
476 | --- tests/20-functional.test 2013-08-05 15:00:27 +0000 |
477 | +++ tests/20-functional.test 2013-08-09 13:41:32 +0000 |
478 | @@ -82,7 +82,7 @@ |
479 | Retry loading the page until the page is found or a timeout exception |
480 | is raised. |
481 | """ |
482 | - base_url = 'https://{0}:{1}'.format(hostname, self.port) |
483 | + base_url = 'https://{}:{}'.format(hostname, self.port) |
484 | url = urlparse.urljoin(base_url, path) |
485 | |
486 | def page_ready(driver): |
487 | @@ -134,7 +134,7 @@ |
488 | # started by the charm in the machine. Right now this does not |
489 | # work in pyJuju, so the desired effect is achieved by keeping |
490 | # track of started services and manually stopping them here. |
491 | - target = 'ubuntu@{0}'.format(hostname) |
492 | + target = 'ubuntu@{}'.format(hostname) |
493 | for service in services: |
494 | ssh(target, 'sudo', 'service', service, 'stop') |
495 | |
496 | @@ -177,6 +177,20 @@ |
497 | # The staging environment contains five deployed services. |
498 | self.assertSetEqual(set(STAGING_SERVICES), self.get_service_names()) |
499 | |
500 | + def test_builtin_server(self): |
501 | + # Ensure the Juju GUI and builtin server are correctly set up. |
502 | + unit_info = self.juju_deploy( |
503 | + self.charm, options={'builtin-server': 'true'}) |
504 | + hostname = unit_info['public-address'] |
505 | + self.navigate_to(hostname) |
506 | + self.handle_browser_warning() |
507 | + self.assertEnvironmentIsConnected() |
508 | + conn = httplib.HTTPSConnection(hostname) |
509 | + conn.request('HEAD', '/') |
510 | + headers = conn.getresponse().getheaders() |
511 | + server_header = dict(headers)['server'] |
512 | + self.assertIn('TornadoServer', server_header) |
513 | + |
514 | def test_branch_source(self): |
515 | # Ensure the Juju GUI is correctly deployed from a Bazaar branch. |
516 | unit_info = self.juju_deploy( |
517 | @@ -192,7 +206,7 @@ |
518 | self.charm, options={'juju-gui-source': JUJU_GUI_TEST_BRANCH}) |
519 | hostname = unit_info['public-address'] |
520 | conn = httplib.HTTPSConnection(hostname) |
521 | - conn.request('GET', '/') |
522 | + conn.request('HEAD', '/') |
523 | headers = conn.getresponse().getheaders() |
524 | # There is only one Cache-Control header. |
525 | self.assertEqual(zip(*headers)[0].count('cache-control'), 1) |
526 | |
527 | === modified file 'tests/test_backends.py' |
528 | --- tests/test_backends.py 2013-08-05 09:59:17 +0000 |
529 | +++ tests/test_backends.py 2013-08-09 13:41:32 +0000 |
530 | @@ -46,8 +46,8 @@ |
531 | """Ensure the correct mixins and property values are collected.""" |
532 | |
533 | def test_staging_backend(self): |
534 | - test_backend = backend.Backend( |
535 | - config={'sandbox': False, 'staging': True}) |
536 | + test_backend = backend.Backend(config={ |
537 | + 'sandbox': False, 'staging': True, 'builtin-server': False}) |
538 | mixin_names = get_mixin_names(test_backend) |
539 | self.assertEqual( |
540 | ('ImprovMixin', 'GuiMixin', 'HaproxyApacheMixin'), |
541 | @@ -57,8 +57,8 @@ |
542 | test_backend.debs) |
543 | |
544 | def test_sandbox_backend(self): |
545 | - test_backend = backend.Backend( |
546 | - config={'sandbox': True, 'staging': False}) |
547 | + test_backend = backend.Backend(config={ |
548 | + 'sandbox': True, 'staging': False, 'builtin-server': False}) |
549 | mixin_names = get_mixin_names(test_backend) |
550 | self.assertEqual( |
551 | ('SandboxMixin', 'GuiMixin', 'HaproxyApacheMixin'), |
552 | @@ -68,8 +68,8 @@ |
553 | test_backend.debs) |
554 | |
555 | def test_python_backend(self): |
556 | - test_backend = backend.Backend( |
557 | - config={'sandbox': False, 'staging': False}) |
558 | + test_backend = backend.Backend(config={ |
559 | + 'sandbox': False, 'staging': False, 'builtin-server': False}) |
560 | mixin_names = get_mixin_names(test_backend) |
561 | self.assertEqual( |
562 | ('PythonMixin', 'GuiMixin', 'HaproxyApacheMixin'), |
563 | @@ -86,8 +86,8 @@ |
564 | # Create a fake agent file. |
565 | agent_path = os.path.join(base_dir, 'agent.conf') |
566 | open(agent_path, 'w').close() |
567 | - test_backend = backend.Backend( |
568 | - config={'sandbox': False, 'staging': False}) |
569 | + test_backend = backend.Backend(config={ |
570 | + 'sandbox': False, 'staging': False, 'builtin-server': False}) |
571 | # Cleanup. |
572 | utils.CURRENT_DIR = orig_current_dir |
573 | shutil.rmtree(base_dir) |
574 | @@ -118,6 +118,8 @@ |
575 | 'find_missing_packages': utils.find_missing_packages, |
576 | 'get_api_address': utils.get_api_address, |
577 | 'get_npm_cache_archive_url': utils.get_npm_cache_archive_url, |
578 | + 'install_builtin_server': utils.install_builtin_server, |
579 | + 'install_tornado': utils.install_tornado, |
580 | 'parse_source': utils.parse_source, |
581 | 'prime_npm_cache': utils.prime_npm_cache, |
582 | 'remove_apache_setup': utils.remove_apache_setup, |
583 | @@ -128,6 +130,7 @@ |
584 | 'setup_haproxy_config': utils.setup_haproxy_config, |
585 | 'start_agent': utils.start_agent, |
586 | 'start_improv': utils.start_improv, |
587 | + 'write_builtin_server_startup': utils.write_builtin_server_startup, |
588 | 'write_gui_config': utils.write_gui_config, |
589 | } |
590 | self.charmhelpers_mocks = { |
591 | @@ -192,11 +195,12 @@ |
592 | self.assertTrue( |
593 | self.called.get(mocked), '{} was not called'.format(mocked)) |
594 | |
595 | - def test_install_improv(self): |
596 | + def test_install_improv_builtin(self): |
597 | test_backend = backend.Backend(config=self.alwaysTrue) |
598 | test_backend.install() |
599 | for mocked in ( |
600 | 'apt_get_install', 'fetch_api', 'find_missing_packages', |
601 | + 'install_builtin_server', 'install_tornado', |
602 | ): |
603 | self.assertTrue( |
604 | self.called.get(mocked), '{} was not called'.format(mocked)) |
605 | @@ -211,12 +215,12 @@ |
606 | self.assertTrue( |
607 | self.called.get(mocked), '{} was not called'.format(mocked)) |
608 | |
609 | - def test_start_improv(self): |
610 | + def test_start_improv_builtin(self): |
611 | test_backend = backend.Backend(config=self.alwaysTrue) |
612 | test_backend.start() |
613 | for mocked in ( |
614 | 'compute_build_dir', 'open_port', 'start_improv', 'su', |
615 | - 'write_gui_config', |
616 | + 'write_builtin_server_startup', 'write_gui_config', |
617 | ): |
618 | self.assertTrue( |
619 | self.called.get(mocked), '{} was not called'.format(mocked)) |
620 | @@ -231,16 +235,20 @@ |
621 | |
622 | def test_same_config(self): |
623 | test_backend = backend.Backend( |
624 | - config={'sandbox': False, 'staging': False}, |
625 | - prev_config={'sandbox': False, 'staging': False}, |
626 | + config={ |
627 | + 'sandbox': False, 'staging': False, 'builtin-server': False}, |
628 | + prev_config={ |
629 | + 'sandbox': False, 'staging': False, 'builtin-server': False}, |
630 | ) |
631 | self.assertFalse(test_backend.different('sandbox')) |
632 | self.assertFalse(test_backend.different('staging')) |
633 | |
634 | def test_different_config(self): |
635 | test_backend = backend.Backend( |
636 | - config={'sandbox': False, 'staging': False}, |
637 | - prev_config={'sandbox': True, 'staging': False}, |
638 | + config={ |
639 | + 'sandbox': False, 'staging': False, 'builtin-server': False}, |
640 | + prev_config={ |
641 | + 'sandbox': True, 'staging': False, 'builtin-server': False}, |
642 | ) |
643 | self.assertTrue(test_backend.different('sandbox')) |
644 | self.assertFalse(test_backend.different('staging')) |
645 | |
646 | === modified file 'tests/test_utils.py' |
647 | --- tests/test_utils.py 2013-08-07 09:50:50 +0000 |
648 | +++ tests/test_utils.py 2013-08-09 13:41:32 +0000 |
649 | @@ -45,6 +45,8 @@ |
650 | log_hook, |
651 | parse_source, |
652 | get_npm_cache_archive_url, |
653 | + install_builtin_server, |
654 | + install_tornado, |
655 | remove_apache_setup, |
656 | remove_haproxy_setup, |
657 | render_to_file, |
658 | @@ -52,11 +54,14 @@ |
659 | setup_apache_config, |
660 | setup_haproxy_config, |
661 | start_agent, |
662 | + start_builtin_server, |
663 | start_haproxy_apache, |
664 | start_improv, |
665 | stop_agent, |
666 | + stop_builtin_server, |
667 | stop_haproxy_apache, |
668 | stop_improv, |
669 | + write_builtin_server_startup, |
670 | write_gui_config, |
671 | ) |
672 | # Import the whole utils package for monkey patching. |
673 | @@ -379,7 +384,7 @@ |
674 | |
675 | def setUp(self): |
676 | self.zookeeper_address = 'example.com:2000' |
677 | - contents = 'env JUJU_ZOOKEEPER="{0}"\n'.format(self.zookeeper_address) |
678 | + contents = 'env JUJU_ZOOKEEPER="{}"\n'.format(self.zookeeper_address) |
679 | with tempfile.NamedTemporaryFile(delete=False) as agent_file: |
680 | agent_file.write(contents) |
681 | self.agent_file_path = agent_file.name |
682 | @@ -674,30 +679,15 @@ |
683 | result, build_dir, 'in_staging: {}, serve_tests: {}'.format( |
684 | in_staging, serve_tests)) |
685 | |
686 | - def test_write_gui_config(self): |
687 | - write_gui_config( |
688 | - False, 'This is login help.', True, True, self.charmworld_url, |
689 | - self.build_dir, use_analytics=True, config_js_path='config') |
690 | - js_conf = self.files['config'] |
691 | - self.assertIn('consoleEnabled: false', js_conf) |
692 | - self.assertIn('user: "admin"', js_conf) |
693 | - self.assertIn('password: "admin"', js_conf) |
694 | - self.assertIn('login_help: "This is login help."', js_conf) |
695 | - self.assertIn('readOnly: true', js_conf) |
696 | - self.assertIn("socket_url: 'wss://", js_conf) |
697 | - self.assertIn('socket_protocol: "wss"', js_conf) |
698 | - self.assertIn('charmworldURL: "http://charmworld.example"', js_conf) |
699 | - self.assertIn('useAnalytics: true', js_conf) |
700 | - |
701 | def test_setup_haproxy_config(self): |
702 | setup_haproxy_config(self.ssl_cert_path) |
703 | haproxy_conf = self.files['haproxy.cfg'] |
704 | - self.assertIn('ca-base {0}'.format(self.ssl_cert_path), haproxy_conf) |
705 | - self.assertIn('crt-base {0}'.format(self.ssl_cert_path), haproxy_conf) |
706 | - self.assertIn('ws1 127.0.0.1:{0}'.format(API_PORT), haproxy_conf) |
707 | - self.assertIn('web1 127.0.0.1:{0}'.format(WEB_PORT), haproxy_conf) |
708 | - self.assertIn('ca-file {0}'.format(JUJU_PEM), haproxy_conf) |
709 | - self.assertIn('crt {0}'.format(JUJU_PEM), haproxy_conf) |
710 | + self.assertIn('ca-base {}'.format(self.ssl_cert_path), haproxy_conf) |
711 | + self.assertIn('crt-base {}'.format(self.ssl_cert_path), haproxy_conf) |
712 | + self.assertIn('ws1 127.0.0.1:{}'.format(API_PORT), haproxy_conf) |
713 | + self.assertIn('web1 127.0.0.1:{}'.format(WEB_PORT), haproxy_conf) |
714 | + self.assertIn('ca-file {}'.format(JUJU_PEM), haproxy_conf) |
715 | + self.assertIn('crt {}'.format(JUJU_PEM), haproxy_conf) |
716 | self.assertIn('redirect scheme https', haproxy_conf) |
717 | |
718 | def test_remove_haproxy_setup(self): |
719 | @@ -708,9 +698,9 @@ |
720 | setup_apache_config(self.build_dir, serve_tests=True) |
721 | apache_site_conf = self.files['SITE_NOT_THERE'] |
722 | self.assertIn('juju-gui/build-', apache_site_conf) |
723 | - self.assertIn('VirtualHost *:{0}'.format(WEB_PORT), apache_site_conf) |
724 | + self.assertIn('VirtualHost *:{}'.format(WEB_PORT), apache_site_conf) |
725 | self.assertIn( |
726 | - 'Alias /test {0}/test/'.format(JUJU_GUI_DIR), apache_site_conf) |
727 | + 'Alias /test {}/test/'.format(JUJU_GUI_DIR), apache_site_conf) |
728 | apache_ports_conf = self.files['PORTS_NOT_THERE'] |
729 | self.assertIn('NameVirtualHost *:8000', apache_ports_conf) |
730 | self.assertIn('Listen 8000', apache_ports_conf) |
731 | @@ -720,7 +710,7 @@ |
732 | self.assertEqual(self.run_call_count, 3) |
733 | |
734 | def test_start_haproxy_apache(self): |
735 | - start_haproxy_apache('build_dir', False, self.ssl_cert_path, True) |
736 | + start_haproxy_apache(JUJU_GUI_DIR, False, self.ssl_cert_path, True) |
737 | self.assertEqual(self.svc_ctl_call_count, 2) |
738 | self.assertEqual(self.service_names, ['apache2', 'haproxy']) |
739 | self.assertEqual( |
740 | @@ -732,6 +722,54 @@ |
741 | self.assertEqual(self.service_names, ['haproxy', 'apache2']) |
742 | self.assertEqual(self.actions, [charmhelpers.STOP, charmhelpers.STOP]) |
743 | |
744 | + def test_install_tornado(self): |
745 | + install_tornado() |
746 | + self.assertEqual(self.run_call_count, 1) |
747 | + |
748 | + def test_install_builtin_server(self): |
749 | + install_builtin_server() |
750 | + self.assertEqual(self.run_call_count, 1) |
751 | + |
752 | + def test_write_builtin_server_startup(self): |
753 | + write_builtin_server_startup( |
754 | + JUJU_GUI_DIR, self.ssl_cert_path, serve_tests=True, insecure=True) |
755 | + guiserver_conf = self.files['guiserver.conf'] |
756 | + self.assertIn('description "GUIServer"', guiserver_conf) |
757 | + self.assertIn('--apiurl="ws://127.0.0.1:8080"', guiserver_conf) |
758 | + self.assertIn('--apiversion="python"', guiserver_conf) |
759 | + self.assertIn( |
760 | + '--testsroot="{}/test/"'.format(JUJU_GUI_DIR), guiserver_conf) |
761 | + self.assertIn('--insecure', guiserver_conf) |
762 | + |
763 | + def test_start_builtin_server(self): |
764 | + start_builtin_server( |
765 | + JUJU_GUI_DIR, self.ssl_cert_path, False, insecure=False) |
766 | + self.assertEqual(self.svc_ctl_call_count, 1) |
767 | + self.assertEqual(self.service_names, ['guiserver']) |
768 | + self.assertEqual(self.actions, [charmhelpers.RESTART]) |
769 | + |
770 | + def test_stop_builtin_server(self): |
771 | + stop_builtin_server() |
772 | + self.assertEqual(self.svc_ctl_call_count, 1) |
773 | + self.assertEqual(self.service_names, ['guiserver']) |
774 | + self.assertEqual(self.actions, [charmhelpers.STOP]) |
775 | + self.assertEqual(self.run_call_count, 1) |
776 | + |
777 | + def test_write_gui_config(self): |
778 | + write_gui_config( |
779 | + False, 'This is login help.', True, True, self.charmworld_url, |
780 | + self.build_dir, use_analytics=True, config_js_path='config') |
781 | + js_conf = self.files['config'] |
782 | + self.assertIn('consoleEnabled: false', js_conf) |
783 | + self.assertIn('user: "admin"', js_conf) |
784 | + self.assertIn('password: "admin"', js_conf) |
785 | + self.assertIn('login_help: "This is login help."', js_conf) |
786 | + self.assertIn('readOnly: true', js_conf) |
787 | + self.assertIn("socket_url: 'wss://", js_conf) |
788 | + self.assertIn('socket_protocol: "wss"', js_conf) |
789 | + self.assertIn('charmworldURL: "http://charmworld.example"', js_conf) |
790 | + self.assertIn('useAnalytics: true', js_conf) |
791 | + |
792 | def test_write_gui_config_insecure(self): |
793 | write_gui_config( |
794 | False, 'This is login help.', True, True, self.charmworld_url, |
Reviewers: mp+179138_ code.launchpad. net,
Message:
Please take a look.
Description:
Integrate the built-in server into the charm.
Add a "builtin-server" option to config.yaml, defaulting to false, to
enable
a new Tornado-based built-in web server, in place of haproxy and Apache
(still
the default).
Add a "guiserver.conf" Upstart config file, generated via a template.
Add a BuiltinServerMixin to hooks/backend.py .
Add tests for the new functions in hooks/utils.py .
Clean up all paths and constants in hooks/utils.py .
Miscellaneuos cleanup.
QA1 (quick):
$ make unittest
QA2 (long, 40 minutes):
$ make ftest
QA3 (long but shorter than the former):
$ juju bootstrap server= false
$ make deploy
(check web access)
$ juju set juju-gui builtin-server=true
(check web access)
$ juju set juju-gui builtin-
(check web access)
$ juju destroy-environment
https:/ /code.launchpad .net/~teknico/ charms/ precise/ juju-gui/ integrate- builtin- server- 2/+merge/ 179138
(do not edit description out of merge proposal)
Please review this at https:/ /codereview. appspot. com/12650044/
Affected files: guiserver. conf.template 3.1.tar. gz functional. test backends. py
M Operation.md
A [revision details]
M config.yaml
A config/
A deps/tornado-
M hooks/backend.py
M hooks/utils.py
M revision
M server/setup.py
M tests/20-
M tests/test_
M tests/test_utils.py