Merge lp:~blake-rouse/maas/add-version-to-ui into lp:~maas-committers/maas/trunk
- add-version-to-ui
- Merge into trunk
Status: | Merged |
---|---|
Approved by: | Blake Rouse |
Approved revision: | no longer in the source branch. |
Merged at revision: | 3597 |
Proposed branch: | lp:~blake-rouse/maas/add-version-to-ui |
Merge into: | lp:~maas-committers/maas/trunk |
Diff against target: |
392 lines (+332/-2) 6 files modified
required-packages/base (+1/-0) src/maasserver/context_processors.py (+6/-0) src/maasserver/templates/maasserver/base.html (+1/-1) src/maasserver/templates/maasserver/index.html (+1/-1) src/maasserver/utils/tests/test_version.py (+194/-0) src/maasserver/utils/version.py (+129/-0) |
To merge this branch: | bzr merge lp:~blake-rouse/maas/add-version-to-ui |
Related bugs: |
Reviewer | Review Type | Date Requested | Status |
---|---|---|---|
Raphaël Badin (community) | Approve | ||
Review via email: mp+251615@code.launchpad.net |
Commit message
Show the installed version of MAAS on the WebUI.
This will show the installed version of the maas-region-
Description of the change
Christian Reis (kiko) wrote : | # |
Raphaël Badin (rvb) wrote : | # |
Feel free to disagree but I feel the caching is done at the wrong level in this branch. The result is that apt will be called a lot when running from a branch.
Blake Rouse (blake-rouse) wrote : | # |
Okay I made the required fixes and now have the documentation link working based on the version. More work then I wanted to do for this branch, but your welcome! :-)
Gavin Panella (allenap) wrote : | # |
A few comments (not a review):
- Instead of searching for a particular package name, how about
searching for the package that owns the current file? That's more
likely to be correct *and* it reduces the coupling between upstream
and the package. For example:
import inspect
from os.path import realpath
import apt
def find_package_
# This is quicker than you might think.
for pkg in cache:
if pkg.is_installed:
if filename in pkg.installed_
cache = apt.Cache()
source = inspect.
package = find_package_
if package is None:
pass # Check for branch info.
else:
installed = package.installed
# Use the *source* package name and version.
desc = "%s %s" % (installed.
- When reporting the branch version, also include the branch's location,
or its name. This is less important to the developer, but could be
very useful in screen-grabs, for example.
Blake Rouse (blake-rouse) wrote : | # |
Those are all great suggestions but I don't want to spend anymore time on this branch. There are many more important things that need to be completed and spending more time on this branch would be a waste of the time I have available to complete more important features.
I just did this quickly to fix a annoying bug, that will help the UI.
Raphaël Badin (rvb) wrote : | # |
> Those are all great suggestions but I don't want to spend anymore time on this branch.
All right then… I would have liked to see the caching problem fixed but it isn't critical.
Blake Rouse (blake-rouse) wrote : | # |
The caching problem is fixed. It will set the MAAS_VERSION to "", causing it not to be reloaded after the first call.
Preview Diff
1 | === modified file 'required-packages/base' |
2 | --- required-packages/base 2015-01-20 15:56:10 +0000 |
3 | +++ required-packages/base 2015-03-03 16:09:28 +0000 |
4 | @@ -13,6 +13,7 @@ |
5 | libjs-angularjs |
6 | libpq-dev |
7 | postgresql |
8 | +python-apt |
9 | python-bson |
10 | python-bzrlib |
11 | python-convoy |
12 | |
13 | === modified file 'src/maasserver/context_processors.py' |
14 | --- src/maasserver/context_processors.py 2015-02-26 18:34:40 +0000 |
15 | +++ src/maasserver/context_processors.py 2015-03-03 16:09:28 +0000 |
16 | @@ -23,6 +23,10 @@ |
17 | from maasserver.components import get_persistent_errors |
18 | from maasserver.forms import get_node_edit_form |
19 | from maasserver.models import Config |
20 | +from maasserver.utils.version import ( |
21 | + get_maas_doc_version, |
22 | + get_maas_version, |
23 | + ) |
24 | |
25 | |
26 | def yui(context): |
27 | @@ -86,4 +90,6 @@ |
28 | 'site_name': Config.objects.get_config('maas_name'), |
29 | }, |
30 | 'debug': settings.DEBUG, |
31 | + 'version': get_maas_version(), |
32 | + 'doc_version': get_maas_doc_version(), |
33 | } |
34 | |
35 | === modified file 'src/maasserver/templates/maasserver/base.html' |
36 | --- src/maasserver/templates/maasserver/base.html 2015-02-20 17:00:30 +0000 |
37 | +++ src/maasserver/templates/maasserver/base.html 2015-03-03 16:09:28 +0000 |
38 | @@ -123,7 +123,7 @@ |
39 | <div class="legal-inner"> |
40 | {% block footer-copyright %} |
41 | <p class="twelve-col"> |
42 | - <span class="version">MAAS</span> <a href="http://maas.ubuntu.com/docs1.7/changelog.html#id1">View release notes</a>|<a href="https://maas.ubuntu.com/documentation/">View documentation</a> |
43 | + <span class="version">MAAS Version {{version}}</span> <a href="http://maas.ubuntu.com/{{doc_version}}/changelog.html#id1">View release notes</a>|<a href="https://maas.ubuntu.com/{{doc_version}}/">View documentation</a> |
44 | </p> |
45 | <p class="twelve-col copy">© 2015 Canonical Ltd. Ubuntu and Canonical are registered trademarks of Canonical Ltd.</p> |
46 | {% endblock %} |
47 | |
48 | === modified file 'src/maasserver/templates/maasserver/index.html' |
49 | --- src/maasserver/templates/maasserver/index.html 2015-02-20 17:00:30 +0000 |
50 | +++ src/maasserver/templates/maasserver/index.html 2015-03-03 16:09:28 +0000 |
51 | @@ -102,7 +102,7 @@ |
52 | <div class="legal clearfix"> |
53 | <div class="legal-inner"> |
54 | <p class="twelve-col"> |
55 | - <span class="version">MAAS</span> <a href="http://maas.ubuntu.com/docs1.7/changelog.html#id1">View release notes</a>|<a href="https://maas.ubuntu.com/documentation/">View documentation</a> |
56 | + <span class="version">MAAS Version {{version}}</span> <a href="http://maas.ubuntu.com/{{doc_version}}/changelog.html#id1">View release notes</a>|<a href="https://maas.ubuntu.com/{{doc_version}}/">View documentation</a> |
57 | </p> |
58 | <p class="twelve-col copy">© 2015 Canonical Ltd. Ubuntu and Canonical are registered trademarks of Canonical Ltd.</p> |
59 | </div> |
60 | |
61 | === added file 'src/maasserver/utils/tests/test_version.py' |
62 | --- src/maasserver/utils/tests/test_version.py 1970-01-01 00:00:00 +0000 |
63 | +++ src/maasserver/utils/tests/test_version.py 2015-03-03 16:09:28 +0000 |
64 | @@ -0,0 +1,194 @@ |
65 | +# Copyright 2015 Canonical Ltd. This software is licensed under the |
66 | +# GNU Affero General Public License version 3 (see the file LICENSE). |
67 | + |
68 | +"""Test version utilities.""" |
69 | + |
70 | +from __future__ import ( |
71 | + absolute_import, |
72 | + print_function, |
73 | + unicode_literals, |
74 | + ) |
75 | + |
76 | +str = None |
77 | + |
78 | +__metaclass__ = type |
79 | +__all__ = [] |
80 | + |
81 | +import random |
82 | + |
83 | +from bzrlib.errors import NotBranchError |
84 | +from maasserver.utils import version |
85 | +from maastesting.matchers import MockCalledOnceWith |
86 | +from maastesting.testcase import MAASTestCase |
87 | +from mock import ( |
88 | + MagicMock, |
89 | + sentinel, |
90 | + ) |
91 | +from testtools.matchers import Is |
92 | + |
93 | + |
94 | +class TestGetVersionFromAPT(MAASTestCase): |
95 | + |
96 | + def test__returns_empty_string_if_package_not_in_cache(self): |
97 | + self.patch(version.apt_pkg, "Cache") |
98 | + self.assertEquals( |
99 | + "", |
100 | + version.get_version_from_apt(version.REGION_PACKAGE_NAME)) |
101 | + |
102 | + def test__returns_empty_string_if_not_current_ver_from_package(self): |
103 | + package = MagicMock() |
104 | + package.current_ver = None |
105 | + mock_cache = { |
106 | + version.REGION_PACKAGE_NAME: package, |
107 | + } |
108 | + self.patch(version.apt_pkg, "Cache").return_value = mock_cache |
109 | + self.assertEquals( |
110 | + "", |
111 | + version.get_version_from_apt(version.REGION_PACKAGE_NAME)) |
112 | + |
113 | + def test__returns_ver_str_from_package(self): |
114 | + package = MagicMock() |
115 | + package.current_ver.ver_str = sentinel.ver_str |
116 | + mock_cache = { |
117 | + version.REGION_PACKAGE_NAME: package, |
118 | + } |
119 | + self.patch(version.apt_pkg, "Cache").return_value = mock_cache |
120 | + self.assertIs( |
121 | + sentinel.ver_str, |
122 | + version.get_version_from_apt(version.REGION_PACKAGE_NAME)) |
123 | + |
124 | + |
125 | +class TestFormatVersion(MAASTestCase): |
126 | + |
127 | + scenarios = [ |
128 | + ("with ~", { |
129 | + "version": "1.8.0~alpha4+bzr356-0ubuntu1", |
130 | + "output": "1.8.0 (alpha4+bzr356)", |
131 | + }), |
132 | + ("without ~", { |
133 | + "version": "1.8.0+bzr356-0ubuntu1", |
134 | + "output": "1.8.0 (+bzr356)", |
135 | + }), |
136 | + ("without ~ or +", { |
137 | + "version": "1.8.0-0ubuntu1", |
138 | + "output": "1.8.0", |
139 | + }), |
140 | + ] |
141 | + |
142 | + def test__returns_formatted_version(self): |
143 | + self.assertEquals(self.output, version.format_version(self.version)) |
144 | + |
145 | + |
146 | +class TestGetMAASRegionPackageVersion(MAASTestCase): |
147 | + |
148 | + def test__returns_value_from_global(self): |
149 | + self.patch(version, "MAAS_VERSION", sentinel.maas_version) |
150 | + self.assertIs( |
151 | + sentinel.maas_version, version.get_maas_region_package_version()) |
152 | + |
153 | + def test__calls_get_version_from_apt_if_global_not_set(self): |
154 | + self.patch(version, "MAAS_VERSION", None) |
155 | + mock_apt = self.patch(version, "get_version_from_apt") |
156 | + version.get_maas_region_package_version() |
157 | + self.assertThat( |
158 | + mock_apt, MockCalledOnceWith(version.REGION_PACKAGE_NAME)) |
159 | + |
160 | + def test__calls_format_version_with_version_from_apt(self): |
161 | + self.patch(version, "MAAS_VERSION", None) |
162 | + current_ver = "1.8.0~alpha4+bzr356-0ubuntu1" |
163 | + mock_apt = self.patch(version, "get_version_from_apt") |
164 | + mock_apt.return_value = current_ver |
165 | + mock_format = self.patch(version, "format_version") |
166 | + mock_format.return_value = sentinel.format |
167 | + self.expectThat( |
168 | + version.get_maas_region_package_version(), Is(sentinel.format)) |
169 | + self.expectThat( |
170 | + mock_format, MockCalledOnceWith(current_ver)) |
171 | + |
172 | + def test__sets_global_value(self): |
173 | + self.patch(version, "MAAS_VERSION", None) |
174 | + current_ver = "1.8.0~alpha4+bzr356-0ubuntu1" |
175 | + mock_apt = self.patch(version, "get_version_from_apt") |
176 | + mock_apt.return_value = current_ver |
177 | + mock_format = self.patch(version, "format_version") |
178 | + mock_format.return_value = sentinel.format |
179 | + version.get_maas_region_package_version() |
180 | + self.assertIs(sentinel.format, version.MAAS_VERSION) |
181 | + |
182 | + |
183 | +class TestGetMAASBranch(MAASTestCase): |
184 | + |
185 | + def test__returns_None_if_Branch_is_None(self): |
186 | + self.patch(version, "Branch", None) |
187 | + self.assertIsNone(version.get_maas_branch()) |
188 | + |
189 | + def test__calls_Branch_open_with_current_dir(self): |
190 | + mock_open = self.patch(version.Branch, "open") |
191 | + mock_open.return_value = sentinel.branch |
192 | + self.expectThat(version.get_maas_branch(), Is(sentinel.branch)) |
193 | + self.expectThat(mock_open, MockCalledOnceWith(".")) |
194 | + |
195 | + def test__returns_None_on_NotBranchError(self): |
196 | + mock_open = self.patch(version.Branch, "open") |
197 | + mock_open.side_effect = NotBranchError("") |
198 | + self.assertIsNone(version.get_maas_branch()) |
199 | + |
200 | + |
201 | +class TestGetMAASVersion(MAASTestCase): |
202 | + |
203 | + def test__returns_version_from_get_maas_region_package_version(self): |
204 | + mock_version = self.patch(version, "get_maas_region_package_version") |
205 | + mock_version.return_value = sentinel.version |
206 | + self.assertIs(sentinel.version, version.get_maas_version()) |
207 | + |
208 | + def test__returns_unknown_if_version_is_empty_and_not_bzr_branch(self): |
209 | + mock_version = self.patch(version, "get_maas_region_package_version") |
210 | + mock_version.return_value = "" |
211 | + mock_branch = self.patch(version, "get_maas_branch") |
212 | + mock_branch.return_value = None |
213 | + self.assertEquals("unknown", version.get_maas_version()) |
214 | + |
215 | + def test__returns_from_source_and_revno_from_branch(self): |
216 | + mock_version = self.patch(version, "get_maas_region_package_version") |
217 | + mock_version.return_value = "" |
218 | + revno = random.randint(1, 5000) |
219 | + mock_branch = self.patch(version, "get_maas_branch") |
220 | + mock_branch.return_value.revno.return_value = revno |
221 | + self.assertEquals( |
222 | + "from source (+bzr%s)" % revno, version.get_maas_version()) |
223 | + |
224 | + |
225 | +class TestGetMAASMainVersion(MAASTestCase): |
226 | + |
227 | + def test__returns_main_version_from_package_version_with_space(self): |
228 | + mock_version = self.patch(version, "get_maas_region_package_version") |
229 | + mock_version.return_value = "1.8.0 (alpha4+bzr356)" |
230 | + self.assertEquals("1.8.0", version.get_maas_main_version()) |
231 | + |
232 | + def test__returns_main_version_from_package_version_without_space(self): |
233 | + mock_version = self.patch(version, "get_maas_region_package_version") |
234 | + mock_version.return_value = "1.8.0" |
235 | + self.assertEquals("1.8.0", version.get_maas_main_version()) |
236 | + |
237 | + def test__returns_empty_if_version_is_empty(self): |
238 | + mock_version = self.patch(version, "get_maas_region_package_version") |
239 | + mock_version.return_value = "" |
240 | + self.assertEquals("", version.get_maas_main_version()) |
241 | + |
242 | + |
243 | +class TestGetMAASDocVersion(MAASTestCase): |
244 | + |
245 | + def test__returns_doc_version_with_greater_than_1_decimals(self): |
246 | + mock_version = self.patch(version, "get_maas_main_version") |
247 | + mock_version.return_value = "1.8.0" |
248 | + self.assertEquals("doc1.8", version.get_maas_doc_version()) |
249 | + |
250 | + def test__returns_doc_version_with_equal_to_1_decimals(self): |
251 | + mock_version = self.patch(version, "get_maas_main_version") |
252 | + mock_version.return_value = "1.8" |
253 | + self.assertEquals("doc1.8", version.get_maas_doc_version()) |
254 | + |
255 | + def test__returns_just_doc_if_version_is_empty(self): |
256 | + mock_version = self.patch(version, "get_maas_main_version") |
257 | + mock_version.return_value = "" |
258 | + self.assertEquals("doc", version.get_maas_doc_version()) |
259 | |
260 | === added file 'src/maasserver/utils/version.py' |
261 | --- src/maasserver/utils/version.py 1970-01-01 00:00:00 +0000 |
262 | +++ src/maasserver/utils/version.py 2015-03-03 16:09:28 +0000 |
263 | @@ -0,0 +1,129 @@ |
264 | +# Copyright 2015 Canonical Ltd. This software is licensed under the |
265 | +# GNU Affero General Public License version 3 (see the file LICENSE). |
266 | + |
267 | +"""Version utilities.""" |
268 | + |
269 | +from __future__ import ( |
270 | + absolute_import, |
271 | + print_function, |
272 | + unicode_literals, |
273 | + ) |
274 | + |
275 | +str = None |
276 | + |
277 | +__metaclass__ = type |
278 | +__all__ = [ |
279 | + ] |
280 | + |
281 | +import apt_pkg |
282 | + |
283 | + |
284 | +try: |
285 | + from bzrlib.branch import Branch |
286 | + from bzrlib.errors import NotBranchError |
287 | +except ImportError: |
288 | + Branch = None |
289 | + |
290 | +# Initialize apt_pkg. |
291 | +apt_pkg.init() |
292 | + |
293 | +# Holds the version of maas. |
294 | +MAAS_VERSION = None |
295 | + |
296 | +# Name of maas package to get version from. |
297 | +REGION_PACKAGE_NAME = "maas-region-controller" |
298 | + |
299 | + |
300 | +def get_version_from_apt(package): |
301 | + """Return the version output from `apt_pkg.Cache` for the given package.""" |
302 | + cache = apt_pkg.Cache() |
303 | + version = None |
304 | + if package in cache: |
305 | + apt_package = cache[package] |
306 | + version = apt_package.current_ver |
307 | + |
308 | + if version is not None: |
309 | + return version.ver_str |
310 | + else: |
311 | + return "" |
312 | + |
313 | + |
314 | +def format_version(version): |
315 | + """Format `version` into a better looking string for the UI.""" |
316 | + if "~" in version: |
317 | + main_version, extra = version.split("~", 1) |
318 | + return "%s (%s)" % (main_version, extra.split("-", 1)[0]) |
319 | + elif "+" in version: |
320 | + main_version, extra = version.split("+", 1) |
321 | + return "%s (+%s)" % (main_version, extra.split("-", 1)[0]) |
322 | + else: |
323 | + return version.split("-", 1)[0] |
324 | + |
325 | + |
326 | +def get_maas_region_package_version(): |
327 | + """Return the dpkg version for `REGION_PACKAGE_NAME`. |
328 | + |
329 | + Lazy loads the version. Once loaded it will not be loaded again. |
330 | + This is to speed up the call to this method and also to require the |
331 | + region to be restarted to see the new version. |
332 | + """ |
333 | + global MAAS_VERSION |
334 | + if MAAS_VERSION is None: |
335 | + MAAS_VERSION = get_version_from_apt(REGION_PACKAGE_NAME) |
336 | + # Possible this returned an empty string, meaning its not installed |
337 | + # and no need to call again. |
338 | + if MAAS_VERSION: |
339 | + # It is a valid version so set the correct format. |
340 | + MAAS_VERSION = format_version(MAAS_VERSION) |
341 | + return MAAS_VERSION |
342 | + |
343 | + |
344 | +def get_maas_branch(): |
345 | + """Return the `bzrlib.branch.Branch` for this running MAAS.""" |
346 | + if Branch is None: |
347 | + return None |
348 | + try: |
349 | + return Branch.open(".") |
350 | + except NotBranchError: |
351 | + return None |
352 | + |
353 | + |
354 | +def get_maas_version(): |
355 | + """Return the version string for the running MAAS region.""" |
356 | + # MAAS is installed from package, return the version string. |
357 | + version = get_maas_region_package_version() |
358 | + if version: |
359 | + return version |
360 | + else: |
361 | + # Get the branch information |
362 | + branch = get_maas_branch() |
363 | + if branch is None: |
364 | + # Not installed not in branch, then no way to identify. This should |
365 | + # not happen, but just in case. |
366 | + return "unknown" |
367 | + else: |
368 | + return "from source (+bzr%s)" % branch.revno() |
369 | + |
370 | + |
371 | +def get_maas_main_version(): |
372 | + """Return the main version for the running MAAS region.""" |
373 | + # MAAS is installed from package, return the version string. |
374 | + version = get_maas_region_package_version() |
375 | + if version: |
376 | + if " " in version: |
377 | + return version.split(" ")[0] |
378 | + else: |
379 | + return version |
380 | + else: |
381 | + return "" |
382 | + |
383 | + |
384 | +def get_maas_doc_version(): |
385 | + """Return the doc version for the running MAAS region.""" |
386 | + main_version = get_maas_main_version() |
387 | + if not main_version: |
388 | + return "doc" |
389 | + if main_version.count('.') > 1: |
390 | + return "doc%s" % '.'.join(main_version.split('.')[:2]) |
391 | + else: |
392 | + return "doc%s" % main_version |
On Tue, Mar 03, 2015 at 03:22:27PM -0000, Blake Rouse wrote: version" >MAAS</ span> <a href="http:// maas.ubuntu. com/docs1. 7/changelog. html#id1">View release notes</a>|<a href="https:/ /maas.ubuntu. com/documentati on/">View documentation</a> version" >MAAS Version {{version}}</span> <a href="http:// maas.ubuntu. com/docs1. 7/changelog. html#id1">View release notes</a>|<a href="https:/ /maas.ubuntu. com/documentati on/">View documentation</a>
> - <span class="
> + <span class="
You may have considered this already but could we use the version string launchpad. net/~kiko
to automatically select the right subdirectory in the URL?
--
Christian Robottom Reis | [+1] 612 888 4935 | http://
Canonical VP Hyperscale | [+55 16] 9 9112 6430