Merge lp:~ahasenack/landscape-client/landscape-client-12.05-0ubuntu1-quantal into lp:ubuntu/quantal/landscape-client
- landscape-client-12.05-0ubuntu1-quantal
- Merge into quantal
Proposed by
Andreas Hasenack
Status: | Merged | ||||
---|---|---|---|---|---|
Merged at revision: | 46 | ||||
Proposed branch: | lp:~ahasenack/landscape-client/landscape-client-12.05-0ubuntu1-quantal | ||||
Merge into: | lp:ubuntu/quantal/landscape-client | ||||
Diff against target: |
5384 lines (+762/-3332) 39 files modified
debian/changelog (+41/-0) debian/control (+6/-4) debian/landscape-client.templates (+1/-1) debian/landscape-common.templates (+1/-1) debian/po/templates.pot (+28/-6) debian/rules (+9/-6) debian/source.lintian-overrides (+16/-0) debian/source/format (+1/-0) debian/watch (+2/-0) landscape/__init__.py (+6/-2) landscape/constants.py (+3/-2) landscape/deployment.py (+1/-1) landscape/manager/aptsources.py (+2/-2) landscape/manager/tests/test_aptsources.py (+1/-1) landscape/monitor/packagemonitor.py (+1/-1) landscape/package/changer.py (+37/-119) landscape/package/facade.py (+159/-494) landscape/package/interface.py (+0/-84) landscape/package/releaseupgrader.py (+2/-2) landscape/package/reporter.py (+13/-110) landscape/package/skeleton.py (+0/-48) landscape/package/store.py (+0/-37) landscape/package/taskhandler.py (+4/-8) landscape/package/tests/helpers.py (+0/-39) landscape/package/tests/test_changer.py (+202/-521) landscape/package/tests/test_facade.py (+88/-735) landscape/package/tests/test_interface.py (+0/-34) landscape/package/tests/test_releaseupgrader.py (+2/-2) landscape/package/tests/test_reporter.py (+94/-695) landscape/package/tests/test_skeleton.py (+28/-87) landscape/package/tests/test_store.py (+3/-103) landscape/package/tests/test_taskhandler.py (+4/-48) landscape/ui/view/tests/test_configuration.py (+3/-0) landscape/ui/view/ui/landscape-client-settings.glade (+1/-1) landscape/watchdog.py (+1/-1) po/fr.po (+1/-1) po/landscape-client.pot (+1/-1) smart-update/Makefile (+0/-6) smart-update/smart-update.c (+0/-129) |
||||
To merge this branch: | bzr merge lp:~ahasenack/landscape-client/landscape-client-12.05-0ubuntu1-quantal | ||||
Related bugs: |
|
Reviewer | Review Type | Date Requested | Status |
---|---|---|---|
Ubuntu branches | Pending | ||
Review via email: mp+108569@code.launchpad.net |
Commit message
Description of the change
This updates landscape-client in quantal to upstream version 12.05.
Upstream now removes debian/* from the tarball, so this branch also addresses that. For details, please see the debian/changelog new entries.
To post a comment you must log in.
Preview Diff
[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
1 | === modified file 'debian/changelog' | |||
2 | --- debian/changelog 2012-03-28 10:59:58 +0000 | |||
3 | +++ debian/changelog 2012-06-04 14:08:28 +0000 | |||
4 | @@ -1,3 +1,44 @@ | |||
5 | 1 | landscape-client (12.05-0ubuntu1) quantal; urgency=low | ||
6 | 2 | |||
7 | 3 | * New upstream release 12.05 (r561 in trunk) (LP: #1004678). | ||
8 | 4 | * Make all subpackages that depend on each other require the exact same | ||
9 | 5 | version, instead of >= $version. | ||
10 | 6 | * Added python-gi to client depends starting natty. | ||
11 | 7 | * Make change-packages also handle package holds (LP: #972489). | ||
12 | 8 | * Fix typo in glade file (LP: #983096). | ||
13 | 9 | * Tidy up apt facade (LP: #985493). | ||
14 | 10 | * Remove SmartFacade and its tests, no longer used (LP: #996269). | ||
15 | 11 | * Remove check for apt version, since we won't release this for | ||
16 | 12 | Hardy where that check matters (LP: #996837). | ||
17 | 13 | * Remove methods that were smart specific. We no longer use smart | ||
18 | 14 | (LP: #996841). | ||
19 | 15 | * Remove smart-update helper binary. We no longer use smart | ||
20 | 16 | (LP: #997408). | ||
21 | 17 | * Remove test-mixins that were useful only during the apt-to-smart | ||
22 | 18 | code migration. Now with smart gone, they are no longer necessary | ||
23 | 19 | (LP: #997760). | ||
24 | 20 | * Build the packages from precise onward, not just precise. | ||
25 | 21 | * Assorted packaging fixes: | ||
26 | 22 | - Switched to format 3.0 (quilt). | ||
27 | 23 | - Added source lintian overrides, with comments. | ||
28 | 24 | - Updated debian/rules: | ||
29 | 25 | - Added build-arch and build-indep dummy target. | ||
30 | 26 | - Build the GUI packages from precise onwards, and not just on precise. | ||
31 | 27 | - Re-enable dh_lintian. | ||
32 | 28 | - Strip the binaries (dh_strip), and also call dh_shlibdeps for the | ||
33 | 29 | automatic shlibs dependency. | ||
34 | 30 | - Added python-gi from natty onwards. | ||
35 | 31 | - Used __Choices instead of _Choices in landscape-common.templates, it's | ||
36 | 32 | better for translators. | ||
37 | 33 | - Updated standard version to 3.8.2, the version from Lucid (oldest one | ||
38 | 34 | we support) | ||
39 | 35 | - Added shlibs depends. | ||
40 | 36 | - Dropped deprecated ${Source-Version} and replaced it with | ||
41 | 37 | ${binary:Version} | ||
42 | 38 | - Require exact version of the sibling package instead of >=. | ||
43 | 39 | |||
44 | 40 | -- Andreas Hasenack <andreas@canonical.com> Fri, 01 Jun 2012 17:48:43 -0300 | ||
45 | 41 | |||
46 | 1 | landscape-client (12.04.3-0ubuntu1) precise; urgency=low | 42 | landscape-client (12.04.3-0ubuntu1) precise; urgency=low |
47 | 2 | 43 | ||
48 | 3 | * Warn on unicode entry into settings UI (LP: #956612). | 44 | * Warn on unicode entry into settings UI (LP: #956612). |
49 | 4 | 45 | ||
50 | === modified file 'debian/control' | |||
51 | --- debian/control 2012-03-22 10:10:39 +0000 | |||
52 | +++ debian/control 2012-06-04 14:08:28 +0000 | |||
53 | @@ -4,12 +4,13 @@ | |||
54 | 4 | Maintainer: Ubuntu Developers <ubuntu-devel-discuss@lists.ubuntu.com> | 4 | Maintainer: Ubuntu Developers <ubuntu-devel-discuss@lists.ubuntu.com> |
55 | 5 | XSBC-Original-Maintainer: Landscape Team <landscape-team@canonical.com> | 5 | XSBC-Original-Maintainer: Landscape Team <landscape-team@canonical.com> |
56 | 6 | Build-Depends: debhelper (>= 5), po-debconf, python-dev, lsb-release, gawk, python-twisted-core, python-distutils-extra | 6 | Build-Depends: debhelper (>= 5), po-debconf, python-dev, lsb-release, gawk, python-twisted-core, python-distutils-extra |
58 | 7 | Standards-Version: 3.8.0 | 7 | Standards-Version: 3.8.2 |
59 | 8 | XS-Python-Version: >= 2.4, << 2.8 | 8 | XS-Python-Version: >= 2.4, << 2.8 |
60 | 9 | 9 | ||
61 | 10 | Package: landscape-common | 10 | Package: landscape-common |
62 | 11 | Architecture: any | 11 | Architecture: any |
63 | 12 | Depends: ${python:Depends}, ${misc:Depends}, ${extra:Depends}, | 12 | Depends: ${python:Depends}, ${misc:Depends}, ${extra:Depends}, |
64 | 13 | ${shlibs:Depends}, | ||
65 | 13 | python-gnupginterface, | 14 | python-gnupginterface, |
66 | 14 | python-twisted-core, | 15 | python-twisted-core, |
67 | 15 | python-apt, | 16 | python-apt, |
68 | @@ -33,9 +34,10 @@ | |||
69 | 33 | Package: landscape-client | 34 | Package: landscape-client |
70 | 34 | Architecture: any | 35 | Architecture: any |
71 | 35 | Depends: ${python:Depends}, ${misc:Depends}, ${extra:Depends}, | 36 | Depends: ${python:Depends}, ${misc:Depends}, ${extra:Depends}, |
72 | 37 | ${shlibs:Depends}, | ||
73 | 36 | python-twisted-web, | 38 | python-twisted-web, |
74 | 37 | python-twisted-names, | 39 | python-twisted-names, |
76 | 38 | landscape-common (>= ${Source-Version}) | 40 | landscape-common (= ${binary:Version}) |
77 | 39 | Suggests: ${extra:Suggests} | 41 | Suggests: ${extra:Suggests} |
78 | 40 | Description: The Landscape administration system client | 42 | Description: The Landscape administration system client |
79 | 41 | Landscape is a web-based tool for managing Ubuntu systems. This | 43 | Landscape is a web-based tool for managing Ubuntu systems. This |
80 | @@ -48,8 +50,8 @@ | |||
81 | 48 | Package: landscape-client-ui | 50 | Package: landscape-client-ui |
82 | 49 | Architecture: any | 51 | Architecture: any |
83 | 50 | Depends: ${python:Depends}, ${misc:Depends}, | 52 | Depends: ${python:Depends}, ${misc:Depends}, |
86 | 51 | landscape-client (>= ${Source-Version}), | 53 | landscape-client (= ${binary:Version}), |
87 | 52 | landscape-client-ui-install (>= ${Source-Version}), | 54 | landscape-client-ui-install (= ${binary:Version}), |
88 | 53 | python-gi, | 55 | python-gi, |
89 | 54 | python-dbus, | 56 | python-dbus, |
90 | 55 | policykit-1, | 57 | policykit-1, |
91 | 56 | 58 | ||
92 | === modified file 'debian/landscape-client.templates' | |||
93 | --- debian/landscape-client.templates 2012-03-19 09:33:34 +0000 | |||
94 | +++ debian/landscape-client.templates 2012-06-04 14:08:28 +0000 | |||
95 | @@ -64,7 +64,7 @@ | |||
96 | 64 | Template: landscape-client/tags | 64 | Template: landscape-client/tags |
97 | 65 | Type: string | 65 | Type: string |
98 | 66 | Default: | 66 | Default: |
100 | 67 | _Description: Initial tags for first registration | 67 | _Description: Initial tags for first registration: |
101 | 68 | Comma separated list of tags which will be assigned to this computer on its | 68 | Comma separated list of tags which will be assigned to this computer on its |
102 | 69 | first registration. Once the machine is registered, these tags can only be | 69 | first registration. Once the machine is registered, these tags can only be |
103 | 70 | changed using the Landscape server. | 70 | changed using the Landscape server. |
104 | 71 | 71 | ||
105 | === modified file 'debian/landscape-common.templates' | |||
106 | --- debian/landscape-common.templates 2012-03-19 09:33:34 +0000 | |||
107 | +++ debian/landscape-common.templates 2012-06-04 14:08:28 +0000 | |||
108 | @@ -6,7 +6,7 @@ | |||
109 | 6 | # try to keep below ~71 characters. | 6 | # try to keep below ~71 characters. |
110 | 7 | # DO NOT USE commas (,) in Choices translations otherwise | 7 | # DO NOT USE commas (,) in Choices translations otherwise |
111 | 8 | # this will break the choices shown to users | 8 | # this will break the choices shown to users |
113 | 9 | _Choices: Do not display sysinfo on login, Cache sysinfo in /etc/motd, Run sysinfo on every login | 9 | __Choices: Do not display sysinfo on login, Cache sysinfo in /etc/motd, Run sysinfo on every login |
114 | 10 | Default: Cache sysinfo in /etc/motd | 10 | Default: Cache sysinfo in /etc/motd |
115 | 11 | _Description: landscape-sysinfo configuration: | 11 | _Description: landscape-sysinfo configuration: |
116 | 12 | Landscape includes a tool and a set of modules that can display | 12 | Landscape includes a tool and a set of modules that can display |
117 | 13 | 13 | ||
118 | === modified file 'debian/po/templates.pot' | |||
119 | --- debian/po/templates.pot 2012-03-19 09:33:34 +0000 | |||
120 | +++ debian/po/templates.pot 2012-06-04 14:08:28 +0000 | |||
121 | @@ -6,9 +6,9 @@ | |||
122 | 6 | #, fuzzy | 6 | #, fuzzy |
123 | 7 | msgid "" | 7 | msgid "" |
124 | 8 | msgstr "" | 8 | msgstr "" |
126 | 9 | "Project-Id-Version: PACKAGE VERSION\n" | 9 | "Project-Id-Version: landscape-client\n" |
127 | 10 | "Report-Msgid-Bugs-To: landscape-client@packages.debian.org\n" | 10 | "Report-Msgid-Bugs-To: landscape-client@packages.debian.org\n" |
129 | 11 | "POT-Creation-Date: 2012-03-07 17:07+0100\n" | 11 | "POT-Creation-Date: 2012-06-01 18:35-0300\n" |
130 | 12 | "PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" | 12 | "PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" |
131 | 13 | "Last-Translator: FULL NAME <EMAIL@ADDRESS>\n" | 13 | "Last-Translator: FULL NAME <EMAIL@ADDRESS>\n" |
132 | 14 | "Language-Team: LANGUAGE <LL@li.org>\n" | 14 | "Language-Team: LANGUAGE <LL@li.org>\n" |
133 | @@ -152,7 +152,7 @@ | |||
134 | 152 | #. Type: string | 152 | #. Type: string |
135 | 153 | #. Description | 153 | #. Description |
136 | 154 | #: ../landscape-client.templates:11001 | 154 | #: ../landscape-client.templates:11001 |
138 | 155 | msgid "Initial tags for first registration" | 155 | msgid "Initial tags for first registration:" |
139 | 156 | msgstr "" | 156 | msgstr "" |
140 | 157 | 157 | ||
141 | 158 | #. Type: string | 158 | #. Type: string |
142 | @@ -187,9 +187,31 @@ | |||
143 | 187 | #. DO NOT USE commas (,) in Choices translations otherwise | 187 | #. DO NOT USE commas (,) in Choices translations otherwise |
144 | 188 | #. this will break the choices shown to users | 188 | #. this will break the choices shown to users |
145 | 189 | #: ../landscape-common.templates:1001 | 189 | #: ../landscape-common.templates:1001 |
149 | 190 | msgid "" | 190 | msgid "Do not display sysinfo on login" |
150 | 191 | "Do not display sysinfo on login, Cache sysinfo in /etc/motd, Run sysinfo on " | 191 | msgstr "" |
151 | 192 | "every login" | 192 | |
152 | 193 | #. Type: select | ||
153 | 194 | #. Choices | ||
154 | 195 | #. Translators beware! the following three strings form a single | ||
155 | 196 | #. Choices menu. - Every one of these strings has to fit in a standard | ||
156 | 197 | #. 80 characters console, as the fancy screen setup takes up some space | ||
157 | 198 | #. try to keep below ~71 characters. | ||
158 | 199 | #. DO NOT USE commas (,) in Choices translations otherwise | ||
159 | 200 | #. this will break the choices shown to users | ||
160 | 201 | #: ../landscape-common.templates:1001 | ||
161 | 202 | msgid "Cache sysinfo in /etc/motd" | ||
162 | 203 | msgstr "" | ||
163 | 204 | |||
164 | 205 | #. Type: select | ||
165 | 206 | #. Choices | ||
166 | 207 | #. Translators beware! the following three strings form a single | ||
167 | 208 | #. Choices menu. - Every one of these strings has to fit in a standard | ||
168 | 209 | #. 80 characters console, as the fancy screen setup takes up some space | ||
169 | 210 | #. try to keep below ~71 characters. | ||
170 | 211 | #. DO NOT USE commas (,) in Choices translations otherwise | ||
171 | 212 | #. this will break the choices shown to users | ||
172 | 213 | #: ../landscape-common.templates:1001 | ||
173 | 214 | msgid "Run sysinfo on every login" | ||
174 | 193 | msgstr "" | 215 | msgstr "" |
175 | 194 | 216 | ||
176 | 195 | #. Type: select | 217 | #. Type: select |
177 | 196 | 218 | ||
178 | === modified file 'debian/rules' | |||
179 | --- debian/rules 2012-03-28 10:59:58 +0000 | |||
180 | +++ debian/rules 2012-06-04 14:08:28 +0000 | |||
181 | @@ -9,7 +9,7 @@ | |||
182 | 9 | endif | 9 | endif |
183 | 10 | 10 | ||
184 | 11 | dh_extra_flags = -plandscape-common -plandscape-client | 11 | dh_extra_flags = -plandscape-common -plandscape-client |
186 | 12 | ifneq (,$(filter $(dist_release),precise)) | 12 | ifeq (,$(filter $(dist_release),hardy lucid natty oneiric)) |
187 | 13 | # We want landscape-client-ui only from precise onward | 13 | # We want landscape-client-ui only from precise onward |
188 | 14 | dh_extra_flags += -plandscape-client-ui -plandscape-client-ui-install | 14 | dh_extra_flags += -plandscape-client-ui -plandscape-client-ui-install |
189 | 15 | endif | 15 | endif |
190 | @@ -30,6 +30,10 @@ | |||
191 | 30 | landscape_common_substvars = debian/landscape-common.substvars | 30 | landscape_common_substvars = debian/landscape-common.substvars |
192 | 31 | landscape_client_substvars = debian/landscape-client.substvars | 31 | landscape_client_substvars = debian/landscape-client.substvars |
193 | 32 | 32 | ||
194 | 33 | build-arch: build | ||
195 | 34 | |||
196 | 35 | build-indep: build | ||
197 | 36 | |||
198 | 33 | build: build-stamp | 37 | build: build-stamp |
199 | 34 | build-stamp: | 38 | build-stamp: |
200 | 35 | dh_testdir | 39 | dh_testdir |
201 | @@ -69,10 +73,7 @@ | |||
202 | 69 | # do nothing | 73 | # do nothing |
203 | 70 | # | 74 | # |
204 | 71 | binary-arch: build install | 75 | binary-arch: build install |
209 | 72 | # That's not present in Ubuntu releases we still support, so | 76 | dh_lintian |
206 | 73 | # we're just installing the lintian overrides file by hand | ||
207 | 74 | # for now. | ||
208 | 75 | #dh_lintian | ||
210 | 76 | dh_testdir | 77 | dh_testdir |
211 | 77 | dh_testroot | 78 | dh_testroot |
212 | 78 | dh_installdocs | 79 | dh_installdocs |
213 | @@ -82,8 +83,10 @@ | |||
214 | 82 | dh_installinit -- start 45 2 3 4 5 . stop 15 0 1 6 . | 83 | dh_installinit -- start 45 2 3 4 5 . stop 15 0 1 6 . |
215 | 83 | dh_installlogrotate | 84 | dh_installlogrotate |
216 | 84 | dh_installdebconf | 85 | dh_installdebconf |
217 | 86 | dh_strip | ||
218 | 85 | dh_compress | 87 | dh_compress |
219 | 86 | dh_fixperms | 88 | dh_fixperms |
220 | 89 | dh_shlibdeps | ||
221 | 87 | 90 | ||
222 | 88 | ifneq (,$(findstring $(dist_release),hardy)) | 91 | ifneq (,$(findstring $(dist_release),hardy)) |
223 | 89 | # We depend on bug-fixed versions of python-dbus and pycurl on hardy | 92 | # We depend on bug-fixed versions of python-dbus and pycurl on hardy |
224 | @@ -100,7 +103,7 @@ | |||
225 | 100 | ifeq (,$(filter $(dist_release),hardy lucid)) | 103 | ifeq (,$(filter $(dist_release),hardy lucid)) |
226 | 101 | # Starting natty, no more hal or dbus | 104 | # Starting natty, no more hal or dbus |
227 | 102 | echo "extra:Depends=libpam-modules (>= 1.0.1-9ubuntu3)" >> $(landscape_common_substvars) | 105 | echo "extra:Depends=libpam-modules (>= 1.0.1-9ubuntu3)" >> $(landscape_common_substvars) |
229 | 103 | echo "extra:Depends=python-pycurl, gir1.2-gudev-1.0 (>= 165-0ubuntu2)" >> $(landscape_client_substvars) | 106 | echo "extra:Depends=python-pycurl, gir1.2-gudev-1.0 (>= 165-0ubuntu2), python-gi" >> $(landscape_client_substvars) |
230 | 104 | echo "extra:Suggests=python-dbus, hal" >> $(landscape_client_substvars) | 107 | echo "extra:Suggests=python-dbus, hal" >> $(landscape_client_substvars) |
231 | 105 | endif | 108 | endif |
232 | 106 | 109 | ||
233 | 107 | 110 | ||
234 | === added directory 'debian/source' | |||
235 | === added file 'debian/source.lintian-overrides' | |||
236 | --- debian/source.lintian-overrides 1970-01-01 00:00:00 +0000 | |||
237 | +++ debian/source.lintian-overrides 2012-06-04 14:08:28 +0000 | |||
238 | @@ -0,0 +1,16 @@ | |||
239 | 1 | # we use dh_python or dh_python2 depending on the ubuntu release | ||
240 | 2 | # the package is being built on, this is detected dynamically | ||
241 | 3 | # in the rules file | ||
242 | 4 | landscape-client source: dh_python-is-obsolete | ||
243 | 5 | |||
244 | 6 | # the package has to build on lucid, where the standards version | ||
245 | 7 | # is 3.8.2 | ||
246 | 8 | landscape-client source: ancient-standards-version | ||
247 | 9 | |||
248 | 10 | # it's a bug that should be fixed in quantal | ||
249 | 11 | landscape-client source: unknown-field-in-dsc original-maintainer | ||
250 | 12 | |||
251 | 13 | # this is only used in a very specific client upgrade from | ||
252 | 14 | # the dbus to the amp version | ||
253 | 15 | landscape-client: start-stop-daemon-in-maintainer-script postinst:130 | ||
254 | 16 | |||
255 | 0 | 17 | ||
256 | === added file 'debian/source/format' | |||
257 | --- debian/source/format 1970-01-01 00:00:00 +0000 | |||
258 | +++ debian/source/format 2012-06-04 14:08:28 +0000 | |||
259 | @@ -0,0 +1,1 @@ | |||
260 | 1 | 3.0 (quilt) | ||
261 | 0 | 2 | ||
262 | === added file 'debian/watch' | |||
263 | --- debian/watch 1970-01-01 00:00:00 +0000 | |||
264 | +++ debian/watch 2012-06-04 14:08:28 +0000 | |||
265 | @@ -0,0 +1,2 @@ | |||
266 | 1 | version=3 | ||
267 | 2 | https://launchpad.net/landscape-client/+download https://launchpad.net/landscape-client/[^/]+/[^/]+/\+download/landscape-client-(.*)\.tar\.(?:bz2|gz) | ||
268 | 0 | 3 | ||
269 | === modified file 'landscape/__init__.py' | |||
270 | --- landscape/__init__.py 2012-03-28 10:59:58 +0000 | |||
271 | +++ landscape/__init__.py 2012-06-04 14:08:28 +0000 | |||
272 | @@ -1,7 +1,7 @@ | |||
273 | 1 | import sys | 1 | import sys |
274 | 2 | 2 | ||
275 | 3 | DEBIAN_REVISION = "" | 3 | DEBIAN_REVISION = "" |
277 | 4 | UPSTREAM_VERSION = "12.04.2" | 4 | UPSTREAM_VERSION = "12.05" |
278 | 5 | VERSION = "%s%s" % (UPSTREAM_VERSION, DEBIAN_REVISION) | 5 | VERSION = "%s%s" % (UPSTREAM_VERSION, DEBIAN_REVISION) |
279 | 6 | 6 | ||
280 | 7 | # The "server-api" field of outgoing messages will be set to this value, and | 7 | # The "server-api" field of outgoing messages will be set to this value, and |
281 | @@ -32,7 +32,11 @@ | |||
282 | 32 | # * Add "policy" field to "change-packages" | 32 | # * Add "policy" field to "change-packages" |
283 | 33 | # * Add new "change-package-locks" client accepted message type. | 33 | # * Add new "change-package-locks" client accepted message type. |
284 | 34 | # | 34 | # |
286 | 35 | CLIENT_API = "3.3" | 35 | # 3.4: |
287 | 36 | # * Add "hold" field to "change-packages" | ||
288 | 37 | # * Add "remove-hold" field to "change-packages" | ||
289 | 38 | |||
290 | 39 | CLIENT_API = "3.4" | ||
291 | 36 | 40 | ||
292 | 37 | from twisted.python import util | 41 | from twisted.python import util |
293 | 38 | 42 | ||
294 | 39 | 43 | ||
295 | === modified file 'landscape/constants.py' | |||
296 | --- landscape/constants.py 2012-03-21 16:15:49 +0000 | |||
297 | +++ landscape/constants.py 2012-06-04 14:08:28 +0000 | |||
298 | @@ -12,6 +12,7 @@ | |||
299 | 12 | SUCCESS_RESULT = 1 | 12 | SUCCESS_RESULT = 1 |
300 | 13 | ERROR_RESULT = 100 | 13 | ERROR_RESULT = 100 |
301 | 14 | DEPENDENCY_ERROR_RESULT = 101 | 14 | DEPENDENCY_ERROR_RESULT = 101 |
302 | 15 | CLIENT_VERSION_ERROR_RESULT = 102 | ||
303 | 15 | POLICY_STRICT = 0 | 16 | POLICY_STRICT = 0 |
304 | 16 | POLICY_ALLOW_INSTALLS = 1 | 17 | POLICY_ALLOW_INSTALLS = 1 |
305 | 17 | POLICY_ALLOW_ALL_CHANGES = 2 | 18 | POLICY_ALLOW_ALL_CHANGES = 2 |
306 | @@ -25,8 +26,8 @@ | |||
307 | 25 | # package reporter. | 26 | # package reporter. |
308 | 26 | # 2. We lost some package data, for example by a deb archive becoming | 27 | # 2. We lost some package data, for example by a deb archive becoming |
309 | 27 | # inaccessible for a while. The earliest we can reasonably assume that to be | 28 | # inaccessible for a while. The earliest we can reasonably assume that to be |
311 | 28 | # resolved is in 60 minutes, when the smart cronjob runs again. | 29 | # resolved is in 60 minutes, when the package reporter runs again. |
312 | 29 | 30 | ||
313 | 30 | # So we'll give the problem one chance to resolve itself, by only waiting for | 31 | # So we'll give the problem one chance to resolve itself, by only waiting for |
315 | 31 | # one run of smart update. | 32 | # one run of apt-update. |
316 | 32 | UNKNOWN_PACKAGE_DATA_TIMEOUT = 70 * 60 | 33 | UNKNOWN_PACKAGE_DATA_TIMEOUT = 70 * 60 |
317 | 33 | 34 | ||
318 | === modified file 'landscape/deployment.py' | |||
319 | --- landscape/deployment.py 2012-03-19 09:33:34 +0000 | |||
320 | +++ landscape/deployment.py 2012-06-04 14:08:28 +0000 | |||
321 | @@ -282,7 +282,7 @@ | |||
322 | 282 | - C{quiet} (C{False}) | 282 | - C{quiet} (C{False}) |
323 | 283 | - C{log_dir} (C{"/var/log/landscape"}) | 283 | - C{log_dir} (C{"/var/log/landscape"}) |
324 | 284 | - C{log_level} (C{"info"}) | 284 | - C{log_level} (C{"info"}) |
326 | 285 | - C{url} (C{"http://landcape.canonical.com/message-system"}) | 285 | - C{url} (C{"http://landscape.canonical.com/message-system"}) |
327 | 286 | - C{ping_url} (C{"http://landscape.canonical.com/ping"}) | 286 | - C{ping_url} (C{"http://landscape.canonical.com/ping"}) |
328 | 287 | - C{ssl_public_key} | 287 | - C{ssl_public_key} |
329 | 288 | - C{server_autodiscover} (C{"false"}) | 288 | - C{server_autodiscover} (C{"false"}) |
330 | 289 | 289 | ||
331 | === modified file 'landscape/manager/aptsources.py' | |||
332 | --- landscape/manager/aptsources.py 2012-03-19 09:33:34 +0000 | |||
333 | +++ landscape/manager/aptsources.py 2012-06-04 14:08:28 +0000 | |||
334 | @@ -156,8 +156,8 @@ | |||
335 | 156 | """Once the repositories are modified, trigger a reporter run.""" | 156 | """Once the repositories are modified, trigger a reporter run.""" |
336 | 157 | reporter = find_reporter_command() | 157 | reporter = find_reporter_command() |
337 | 158 | 158 | ||
340 | 159 | # Force a smart-update run, because the sources.list has changed | 159 | # Force an apt-update run, because the sources.list has changed |
341 | 160 | args = ["--force-smart-update"] | 160 | args = ["--force-apt-update"] |
342 | 161 | 161 | ||
343 | 162 | if self.registry.config.config is not None: | 162 | if self.registry.config.config is not None: |
344 | 163 | args.append("--config=%s" % self.registry.config.config) | 163 | args.append("--config=%s" % self.registry.config.config) |
345 | 164 | 164 | ||
346 | === modified file 'landscape/manager/tests/test_aptsources.py' | |||
347 | --- landscape/manager/tests/test_aptsources.py 2012-03-19 09:33:34 +0000 | |||
348 | +++ landscape/manager/tests/test_aptsources.py 2012-06-04 14:08:28 +0000 | |||
349 | @@ -388,7 +388,7 @@ | |||
350 | 388 | 388 | ||
351 | 389 | def _run_process(command, args, env={}, path=None, uid=None, gid=None): | 389 | def _run_process(command, args, env={}, path=None, uid=None, gid=None): |
352 | 390 | self.assertEqual(find_reporter_command(), command) | 390 | self.assertEqual(find_reporter_command(), command) |
354 | 391 | self.assertEqual(["--force-smart-update", "--config=%s" % | 391 | self.assertEqual(["--force-apt-update", "--config=%s" % |
355 | 392 | self.manager.config.config], args) | 392 | self.manager.config.config], args) |
356 | 393 | deferred.callback(("ok", "", 0)) | 393 | deferred.callback(("ok", "", 0)) |
357 | 394 | return deferred | 394 | return deferred |
358 | 395 | 395 | ||
359 | === modified file 'landscape/monitor/packagemonitor.py' | |||
360 | --- landscape/monitor/packagemonitor.py 2012-03-19 09:33:34 +0000 | |||
361 | +++ landscape/monitor/packagemonitor.py 2012-06-04 14:08:28 +0000 | |||
362 | @@ -58,7 +58,7 @@ | |||
363 | 58 | 58 | ||
364 | 59 | class FakeFacade(object): | 59 | class FakeFacade(object): |
365 | 60 | """ | 60 | """ |
367 | 61 | A fake facade to workaround the issue that the SmartFacade | 61 | A fake facade to workaround the issue that the AptFacade |
368 | 62 | essentially allows only once instance per process. | 62 | essentially allows only once instance per process. |
369 | 63 | """ | 63 | """ |
370 | 64 | 64 | ||
371 | 65 | 65 | ||
372 | === modified file 'landscape/package/changer.py' | |||
373 | --- landscape/package/changer.py 2012-03-19 09:33:34 +0000 | |||
374 | +++ landscape/package/changer.py 2012-06-04 14:08:28 +0000 | |||
375 | @@ -9,8 +9,8 @@ | |||
376 | 9 | from twisted.internet.defer import maybeDeferred, succeed | 9 | from twisted.internet.defer import maybeDeferred, succeed |
377 | 10 | 10 | ||
378 | 11 | from landscape.constants import ( | 11 | from landscape.constants import ( |
381 | 12 | SUCCESS_RESULT, ERROR_RESULT, DEPENDENCY_ERROR_RESULT, POLICY_STRICT, | 12 | SUCCESS_RESULT, ERROR_RESULT, DEPENDENCY_ERROR_RESULT, |
382 | 13 | POLICY_ALLOW_INSTALLS, POLICY_ALLOW_ALL_CHANGES, | 13 | POLICY_STRICT, POLICY_ALLOW_INSTALLS, POLICY_ALLOW_ALL_CHANGES, |
383 | 14 | UNKNOWN_PACKAGE_DATA_TIMEOUT) | 14 | UNKNOWN_PACKAGE_DATA_TIMEOUT) |
384 | 15 | 15 | ||
385 | 16 | from landscape.lib.fs import create_file | 16 | from landscape.lib.fs import create_file |
386 | @@ -18,7 +18,7 @@ | |||
387 | 18 | from landscape.package.taskhandler import ( | 18 | from landscape.package.taskhandler import ( |
388 | 19 | PackageTaskHandler, PackageTaskHandlerConfiguration, PackageTaskError, | 19 | PackageTaskHandler, PackageTaskHandlerConfiguration, PackageTaskError, |
389 | 20 | run_task_handler) | 20 | run_task_handler) |
391 | 21 | from landscape.manager.manager import FAILED, SUCCEEDED | 21 | from landscape.manager.manager import FAILED |
392 | 22 | 22 | ||
393 | 23 | 23 | ||
394 | 24 | class UnknownPackageData(Exception): | 24 | class UnknownPackageData(Exception): |
395 | @@ -38,7 +38,7 @@ | |||
396 | 38 | """Value object to hold the results of change packages operation. | 38 | """Value object to hold the results of change packages operation. |
397 | 39 | 39 | ||
398 | 40 | @ivar code: The result code of the requested changes. | 40 | @ivar code: The result code of the requested changes. |
400 | 41 | @ivar text: The output from Smart. | 41 | @ivar text: The output from Apt. |
401 | 42 | @ivar installs: Possible additional packages that need to be installed | 42 | @ivar installs: Possible additional packages that need to be installed |
402 | 43 | in order to fulfill the request. | 43 | in order to fulfill the request. |
403 | 44 | @ivar removals: Possible additional packages that need to be removed | 44 | @ivar removals: Possible additional packages that need to be removed |
404 | @@ -80,10 +80,6 @@ | |||
405 | 80 | # Nothing was done | 80 | # Nothing was done |
406 | 81 | return | 81 | return |
407 | 82 | 82 | ||
408 | 83 | # In order to let the reporter run smart-update cleanly, | ||
409 | 84 | # we have to deinitialize Smart, so that the write lock | ||
410 | 85 | # gets released | ||
411 | 86 | self._facade.deinit() | ||
412 | 87 | if os.getuid() == 0: | 83 | if os.getuid() == 0: |
413 | 88 | os.setgid(grp.getgrnam("landscape").gr_gid) | 84 | os.setgid(grp.getgrnam("landscape").gr_gid) |
414 | 89 | os.setuid(pwd.getpwnam("landscape").pw_uid) | 85 | os.setuid(pwd.getpwnam("landscape").pw_uid) |
415 | @@ -103,8 +99,6 @@ | |||
416 | 103 | return result.addErrback(self.unknown_package_data_error, task) | 99 | return result.addErrback(self.unknown_package_data_error, task) |
417 | 104 | if message["type"] == "change-package-locks": | 100 | if message["type"] == "change-package-locks": |
418 | 105 | return self.handle_change_package_locks(message) | 101 | return self.handle_change_package_locks(message) |
419 | 106 | if message["type"] == "change-package-holds": | ||
420 | 107 | return self.handle_change_package_holds(message) | ||
421 | 108 | 102 | ||
422 | 109 | def unknown_package_data_error(self, failure, task): | 103 | def unknown_package_data_error(self, failure, task): |
423 | 110 | """Handle L{UnknownPackageData} data errors. | 104 | """Handle L{UnknownPackageData} data errors. |
424 | @@ -145,7 +139,7 @@ | |||
425 | 145 | self._facade.clear_channels() | 139 | self._facade.clear_channels() |
426 | 146 | 140 | ||
427 | 147 | def init_channels(self, binaries=()): | 141 | def init_channels(self, binaries=()): |
429 | 148 | """Initialize the Smart channels as needed. | 142 | """Initialize the Apt channels as needed. |
430 | 149 | 143 | ||
431 | 150 | @param binaries: A possibly empty list of 3-tuples of the form | 144 | @param binaries: A possibly empty list of 3-tuples of the form |
432 | 151 | (hash, id, deb), holding the hash, the id and the content of | 145 | (hash, id, deb), holding the hash, the id and the content of |
433 | @@ -168,12 +162,16 @@ | |||
434 | 168 | 162 | ||
435 | 169 | self._facade.ensure_channels_reloaded() | 163 | self._facade.ensure_channels_reloaded() |
436 | 170 | 164 | ||
438 | 171 | def mark_packages(self, upgrade=False, install=(), remove=(), reset=True): | 165 | def mark_packages(self, upgrade=False, install=(), remove=(), |
439 | 166 | hold=(), remove_hold=(), reset=True): | ||
440 | 172 | """Mark packages for upgrade, installation or removal. | 167 | """Mark packages for upgrade, installation or removal. |
441 | 173 | 168 | ||
442 | 174 | @param upgrade: If C{True} mark all installed packages for upgrade. | 169 | @param upgrade: If C{True} mark all installed packages for upgrade. |
443 | 175 | @param install: A list of package ids to be marked for installation. | 170 | @param install: A list of package ids to be marked for installation. |
444 | 176 | @param remove: A list of package ids to be marked for removal. | 171 | @param remove: A list of package ids to be marked for removal. |
445 | 172 | @param hold: A list of package ids to be marked for holding. | ||
446 | 173 | @param remove_hold: A list of package ids to be marked to have a hold | ||
447 | 174 | removed. | ||
448 | 177 | @param reset: If C{True} all existing marks will be reset. | 175 | @param reset: If C{True} all existing marks will be reset. |
449 | 178 | """ | 176 | """ |
450 | 179 | if reset: | 177 | if reset: |
451 | @@ -182,16 +180,19 @@ | |||
452 | 182 | if upgrade: | 180 | if upgrade: |
453 | 183 | self._facade.mark_global_upgrade() | 181 | self._facade.mark_global_upgrade() |
454 | 184 | 182 | ||
459 | 185 | for ids, mark_func in [(install, self._facade.mark_install), | 183 | for mark_function, mark_ids in [ |
460 | 186 | (remove, self._facade.mark_remove)]: | 184 | (self._facade.mark_install, install), |
461 | 187 | for id in ids: | 185 | (self._facade.mark_remove, remove), |
462 | 188 | hash = self._store.get_id_hash(id) | 186 | (self._facade.mark_hold, hold), |
463 | 187 | (self._facade.mark_remove_hold, remove_hold)]: | ||
464 | 188 | for mark_id in mark_ids: | ||
465 | 189 | hash = self._store.get_id_hash(mark_id) | ||
466 | 189 | if hash is None: | 190 | if hash is None: |
468 | 190 | raise UnknownPackageData(id) | 191 | raise UnknownPackageData(mark_id) |
469 | 191 | package = self._facade.get_package_by_hash(hash) | 192 | package = self._facade.get_package_by_hash(hash) |
470 | 192 | if package is None: | 193 | if package is None: |
471 | 193 | raise UnknownPackageData(hash) | 194 | raise UnknownPackageData(hash) |
473 | 194 | mark_func(package) | 195 | mark_function(package) |
474 | 195 | 196 | ||
475 | 196 | def change_packages(self, policy): | 197 | def change_packages(self, policy): |
476 | 197 | """Perform the requested changes. | 198 | """Perform the requested changes. |
477 | @@ -202,10 +203,9 @@ | |||
478 | 202 | @return: A L{ChangePackagesResult} holding the details about the | 203 | @return: A L{ChangePackagesResult} holding the details about the |
479 | 203 | outcome of the requested changes. | 204 | outcome of the requested changes. |
480 | 204 | """ | 205 | """ |
482 | 205 | # Delay importing these so that we don't import Smart unless | 206 | # Delay importing these so that we don't import Apt unless |
483 | 206 | # we really need to. | 207 | # we really need to. |
486 | 207 | from landscape.package.facade import ( | 208 | from landscape.package.facade import DependencyError, TransactionError |
485 | 208 | DependencyError, TransactionError, SmartError) | ||
487 | 209 | 209 | ||
488 | 210 | result = ChangePackagesResult() | 210 | result = ChangePackagesResult() |
489 | 211 | count = 0 | 211 | count = 0 |
490 | @@ -213,7 +213,7 @@ | |||
491 | 213 | count += 1 | 213 | count += 1 |
492 | 214 | try: | 214 | try: |
493 | 215 | result.text = self._facade.perform_changes() | 215 | result.text = self._facade.perform_changes() |
495 | 216 | except (TransactionError, SmartError), exception: | 216 | except TransactionError, exception: |
496 | 217 | result.code = ERROR_RESULT | 217 | result.code = ERROR_RESULT |
497 | 218 | result.text = exception.args[0] | 218 | result.text = exception.args[0] |
498 | 219 | except DependencyError, exception: | 219 | except DependencyError, exception: |
499 | @@ -264,10 +264,11 @@ | |||
500 | 264 | """Handle a C{change-packages} message.""" | 264 | """Handle a C{change-packages} message.""" |
501 | 265 | 265 | ||
502 | 266 | self.init_channels(message.get("binaries", ())) | 266 | self.init_channels(message.get("binaries", ())) |
507 | 267 | self.mark_packages(message.get("upgrade-all", False), | 267 | self.mark_packages(upgrade=message.get("upgrade-all", False), |
508 | 268 | message.get("install", ()), | 268 | install=message.get("install", ()), |
509 | 269 | message.get("remove", ())) | 269 | remove=message.get("remove", ()), |
510 | 270 | 270 | hold=message.get("hold", ()), | |
511 | 271 | remove_hold=message.get("remove-hold", ())) | ||
512 | 271 | result = self.change_packages(message.get("policy", POLICY_STRICT)) | 272 | result = self.change_packages(message.get("policy", POLICY_STRICT)) |
513 | 272 | self._clear_binaries() | 273 | self._clear_binaries() |
514 | 273 | 274 | ||
515 | @@ -289,99 +290,16 @@ | |||
516 | 289 | def handle_change_package_locks(self, message): | 290 | def handle_change_package_locks(self, message): |
517 | 290 | """Handle a C{change-package-locks} message. | 291 | """Handle a C{change-package-locks} message. |
518 | 291 | 292 | ||
612 | 292 | Create and delete package locks as requested by the given C{message}. | 293 | Package locks aren't supported anymore. |
613 | 293 | """ | 294 | """ |
614 | 294 | 295 | ||
615 | 295 | if not self._facade.supports_package_locks: | 296 | response = { |
616 | 296 | response = { | 297 | "type": "operation-result", |
617 | 297 | "type": "operation-result", | 298 | "operation-id": message.get("operation-id"), |
618 | 298 | "operation-id": message.get("operation-id"), | 299 | "status": FAILED, |
619 | 299 | "status": FAILED, | 300 | "result-text": "This client doesn't support package locks.", |
620 | 300 | "result-text": "This client doesn't support package locks.", | 301 | "result-code": 1} |
621 | 301 | "result-code": 1} | 302 | return self._broker.send_message(response, True) |
529 | 302 | return self._broker.send_message(response, True) | ||
530 | 303 | |||
531 | 304 | for lock in message.get("create", ()): | ||
532 | 305 | self._facade.set_package_lock(*lock) | ||
533 | 306 | for lock in message.get("delete", ()): | ||
534 | 307 | self._facade.remove_package_lock(*lock) | ||
535 | 308 | self._facade.save_config() | ||
536 | 309 | |||
537 | 310 | response = {"type": "operation-result", | ||
538 | 311 | "operation-id": message.get("operation-id"), | ||
539 | 312 | "status": SUCCEEDED, | ||
540 | 313 | "result-text": "Package locks successfully changed.", | ||
541 | 314 | "result-code": 0} | ||
542 | 315 | |||
543 | 316 | logging.info("Queuing message with change package locks results to " | ||
544 | 317 | "exchange urgently.") | ||
545 | 318 | return self._broker.send_message(response, True) | ||
546 | 319 | |||
547 | 320 | def _send_change_package_holds_response(self, response): | ||
548 | 321 | """Log that a package holds result is sent and send the response.""" | ||
549 | 322 | logging.info("Queuing message with change package holds results to " | ||
550 | 323 | "exchange urgently.") | ||
551 | 324 | return self._broker.send_message(response, True) | ||
552 | 325 | |||
553 | 326 | def handle_change_package_holds(self, message): | ||
554 | 327 | """Handle a C{change-package-holds} message. | ||
555 | 328 | |||
556 | 329 | Create and delete package holds as requested by the given C{message}. | ||
557 | 330 | """ | ||
558 | 331 | if not self._facade.supports_package_holds: | ||
559 | 332 | response = { | ||
560 | 333 | "type": "operation-result", | ||
561 | 334 | "operation-id": message.get("operation-id"), | ||
562 | 335 | "status": FAILED, | ||
563 | 336 | "result-text": "This client doesn't support package holds.", | ||
564 | 337 | "result-code": 1} | ||
565 | 338 | return self._send_change_package_holds_response(response) | ||
566 | 339 | |||
567 | 340 | not_installed = set() | ||
568 | 341 | holds_to_create = message.get("create", []) | ||
569 | 342 | versions_to_create = set() | ||
570 | 343 | for id in holds_to_create: | ||
571 | 344 | hash = self._store.get_id_hash(id) | ||
572 | 345 | hold_version = self._facade.get_package_by_hash(hash) | ||
573 | 346 | if (hold_version | ||
574 | 347 | and self._facade.is_package_installed(hold_version)): | ||
575 | 348 | versions_to_create.add((hold_version.package, hold_version)) | ||
576 | 349 | else: | ||
577 | 350 | not_installed.add(str(id)) | ||
578 | 351 | holds_to_remove = message.get("delete", []) | ||
579 | 352 | versions_to_remove = set() | ||
580 | 353 | for id in holds_to_remove: | ||
581 | 354 | hash = self._store.get_id_hash(id) | ||
582 | 355 | hold_version = self._facade.get_package_by_hash(hash) | ||
583 | 356 | if (hold_version | ||
584 | 357 | and self._facade.is_package_installed(hold_version)): | ||
585 | 358 | versions_to_remove.add((hold_version.package, hold_version)) | ||
586 | 359 | |||
587 | 360 | if not_installed: | ||
588 | 361 | response = { | ||
589 | 362 | "type": "operation-result", | ||
590 | 363 | "operation-id": message.get("operation-id"), | ||
591 | 364 | "status": FAILED, | ||
592 | 365 | "result-text": "Package holds not changed, since the" + | ||
593 | 366 | " following packages are not installed: %s" % ( | ||
594 | 367 | ", ".join(sorted(not_installed))), | ||
595 | 368 | "result-code": 1} | ||
596 | 369 | return self._send_change_package_holds_response(response) | ||
597 | 370 | |||
598 | 371 | for package, hold_version in versions_to_create: | ||
599 | 372 | self._facade.set_package_hold(hold_version) | ||
600 | 373 | for package, hold_version in versions_to_remove: | ||
601 | 374 | self._facade.remove_package_hold(hold_version) | ||
602 | 375 | |||
603 | 376 | self._facade.reload_channels() | ||
604 | 377 | |||
605 | 378 | response = {"type": "operation-result", | ||
606 | 379 | "operation-id": message.get("operation-id"), | ||
607 | 380 | "status": SUCCEEDED, | ||
608 | 381 | "result-text": "Package holds successfully changed.", | ||
609 | 382 | "result-code": 0} | ||
610 | 383 | |||
611 | 384 | return self._send_change_package_holds_response(response) | ||
622 | 385 | 303 | ||
623 | 386 | @staticmethod | 304 | @staticmethod |
624 | 387 | def find_command(): | 305 | def find_command(): |
625 | 388 | 306 | ||
626 | === modified file 'landscape/package/facade.py' | |||
627 | --- landscape/package/facade.py 2012-03-21 16:15:49 +0000 | |||
628 | +++ landscape/package/facade.py 2012-06-04 14:08:28 +0000 | |||
629 | @@ -5,15 +5,6 @@ | |||
630 | 5 | from cStringIO import StringIO | 5 | from cStringIO import StringIO |
631 | 6 | from operator import attrgetter | 6 | from operator import attrgetter |
632 | 7 | 7 | ||
633 | 8 | has_smart = True | ||
634 | 9 | try: | ||
635 | 10 | import smart | ||
636 | 11 | from smart.transaction import ( | ||
637 | 12 | Transaction, PolicyInstall, PolicyUpgrade, PolicyRemove, Failed) | ||
638 | 13 | from smart.const import INSTALL, REMOVE, UPGRADE, ALWAYS, NEVER | ||
639 | 14 | except ImportError: | ||
640 | 15 | has_smart = False | ||
641 | 16 | |||
642 | 17 | # Importing apt throws a FutureWarning on hardy, that we don't want to | 8 | # Importing apt throws a FutureWarning on hardy, that we don't want to |
643 | 18 | # see. | 9 | # see. |
644 | 19 | import warnings | 10 | import warnings |
645 | @@ -24,19 +15,13 @@ | |||
646 | 24 | import apt_inst | 15 | import apt_inst |
647 | 25 | import apt_pkg | 16 | import apt_pkg |
648 | 26 | 17 | ||
649 | 27 | has_new_enough_apt = True | ||
650 | 28 | from aptsources.sourceslist import SourcesList | 18 | from aptsources.sourceslist import SourcesList |
658 | 29 | try: | 19 | from apt.progress.text import AcquireProgress |
659 | 30 | from apt.progress.text import AcquireProgress | 20 | from apt.progress.base import InstallProgress |
653 | 31 | from apt.progress.base import InstallProgress | ||
654 | 32 | except ImportError: | ||
655 | 33 | AcquireProgress = object | ||
656 | 34 | InstallProgress = object | ||
657 | 35 | has_new_enough_apt = False | ||
660 | 36 | 21 | ||
661 | 37 | from landscape.lib.fs import append_file, create_file, read_file | 22 | from landscape.lib.fs import append_file, create_file, read_file |
662 | 38 | from landscape.constants import UBUNTU_PATH | 23 | from landscape.constants import UBUNTU_PATH |
664 | 39 | from landscape.package.skeleton import build_skeleton, build_skeleton_apt | 24 | from landscape.package.skeleton import build_skeleton_apt |
665 | 40 | 25 | ||
666 | 41 | 26 | ||
667 | 42 | class TransactionError(Exception): | 27 | class TransactionError(Exception): |
668 | @@ -54,10 +39,6 @@ | |||
669 | 54 | ", ".join([str(package) for package in self.packages])) | 39 | ", ".join([str(package) for package in self.packages])) |
670 | 55 | 40 | ||
671 | 56 | 41 | ||
672 | 57 | class SmartError(Exception): | ||
673 | 58 | """Raised when Smart fails in an undefined way.""" | ||
674 | 59 | |||
675 | 60 | |||
676 | 61 | class ChannelError(Exception): | 42 | class ChannelError(Exception): |
677 | 62 | """Raised when channels fail to load.""" | 43 | """Raised when channels fail to load.""" |
678 | 63 | 44 | ||
679 | @@ -108,8 +89,6 @@ | |||
680 | 108 | database. | 89 | database. |
681 | 109 | """ | 90 | """ |
682 | 110 | 91 | ||
683 | 111 | supports_package_holds = True | ||
684 | 112 | supports_package_locks = False | ||
685 | 113 | _dpkg_status = "/var/lib/dpkg/status" | 92 | _dpkg_status = "/var/lib/dpkg/status" |
686 | 114 | 93 | ||
687 | 115 | def __init__(self, root=None): | 94 | def __init__(self, root=None): |
688 | @@ -127,6 +106,8 @@ | |||
689 | 127 | self._version_installs = [] | 106 | self._version_installs = [] |
690 | 128 | self._global_upgrade = False | 107 | self._global_upgrade = False |
691 | 129 | self._version_removals = [] | 108 | self._version_removals = [] |
692 | 109 | self._version_hold_creations = [] | ||
693 | 110 | self._version_hold_removals = [] | ||
694 | 130 | self.refetch_package_index = False | 111 | self.refetch_package_index = False |
695 | 131 | 112 | ||
696 | 132 | def _ensure_dir_structure(self): | 113 | def _ensure_dir_structure(self): |
697 | @@ -150,9 +131,6 @@ | |||
698 | 150 | os.makedirs(full_path) | 131 | os.makedirs(full_path) |
699 | 151 | return full_path | 132 | return full_path |
700 | 152 | 133 | ||
701 | 153 | def deinit(self): | ||
702 | 154 | """This method exists solely to be compatible with C{SmartFacade}.""" | ||
703 | 155 | |||
704 | 156 | def get_packages(self): | 134 | def get_packages(self): |
705 | 157 | """Get all the packages available in the channels.""" | 135 | """Get all the packages available in the channels.""" |
706 | 158 | return self._hash2pkg.itervalues() | 136 | return self._hash2pkg.itervalues() |
707 | @@ -167,17 +145,6 @@ | |||
708 | 167 | if (self.is_package_installed(version) | 145 | if (self.is_package_installed(version) |
709 | 168 | and self._is_package_held(version.package))] | 146 | and self._is_package_held(version.package))] |
710 | 169 | 147 | ||
711 | 170 | def get_package_locks(self): | ||
712 | 171 | """Return all set package locks. | ||
713 | 172 | |||
714 | 173 | @return: A C{list} of ternary tuples, contaning the name, relation | ||
715 | 174 | and version details for each lock currently set on the system. | ||
716 | 175 | |||
717 | 176 | XXX: This method isn't implemented yet. It's here to make the | ||
718 | 177 | transition to Apt in the package reporter easier. | ||
719 | 178 | """ | ||
720 | 179 | return [] | ||
721 | 180 | |||
722 | 181 | def get_package_holds(self): | 148 | def get_package_holds(self): |
723 | 182 | """Return the name of all the packages that are on hold.""" | 149 | """Return the name of all the packages that are on hold.""" |
724 | 183 | return [version.package.name for version in self.get_locked_packages()] | 150 | return [version.package.name for version in self.get_locked_packages()] |
725 | @@ -204,7 +171,8 @@ | |||
726 | 204 | 171 | ||
727 | 205 | @param version: The version of the package to unhold. | 172 | @param version: The version of the package to unhold. |
728 | 206 | """ | 173 | """ |
730 | 207 | if not self._is_package_held(version.package): | 174 | if (not self.is_package_installed(version) or |
731 | 175 | not self._is_package_held(version.package)): | ||
732 | 208 | return | 176 | return |
733 | 209 | self._set_dpkg_selections(version.package.name + " install") | 177 | self._set_dpkg_selections(version.package.name + " install") |
734 | 210 | 178 | ||
735 | @@ -556,50 +524,125 @@ | |||
736 | 556 | all_info.append(info + or_divider.join(relation_infos)) | 524 | all_info.append(info + or_divider.join(relation_infos)) |
737 | 557 | return "\n".join(all_info) | 525 | return "\n".join(all_info) |
738 | 558 | 526 | ||
742 | 559 | def perform_changes(self): | 527 | def _set_frontend_noninteractive(self): |
743 | 560 | """Perform the pending package operations.""" | 528 | """ |
744 | 561 | # Try to enforce non-interactivity | 529 | Set the environment to avoid attempts by apt to interact with a user. |
745 | 530 | """ | ||
746 | 562 | os.environ["DEBIAN_FRONTEND"] = "noninteractive" | 531 | os.environ["DEBIAN_FRONTEND"] = "noninteractive" |
747 | 563 | os.environ["APT_LISTCHANGES_FRONTEND"] = "none" | 532 | os.environ["APT_LISTCHANGES_FRONTEND"] = "none" |
748 | 564 | os.environ["APT_LISTBUGS_FRONTEND"] = "none" | 533 | os.environ["APT_LISTBUGS_FRONTEND"] = "none" |
749 | 534 | |||
750 | 535 | def _default_path_when_missing(self): | ||
751 | 536 | """ | ||
752 | 537 | If no PATH is set in the environment, use the Ubuntu default PATH. | ||
753 | 538 | |||
754 | 539 | When the client is launched from the landscape-client-settings-ui the | ||
755 | 540 | PATH variable is incorrectly set, this method rectifies that. | ||
756 | 541 | """ | ||
757 | 565 | # dpkg will fail if no path is set. | 542 | # dpkg will fail if no path is set. |
758 | 566 | if "PATH" not in os.environ: | 543 | if "PATH" not in os.environ: |
759 | 567 | os.environ["PATH"] = UBUNTU_PATH | 544 | os.environ["PATH"] = UBUNTU_PATH |
760 | 545 | |||
761 | 546 | def _setup_dpkg_for_changes(self): | ||
762 | 547 | """ | ||
763 | 548 | Setup environment and apt options for successful package operations. | ||
764 | 549 | """ | ||
765 | 550 | self._set_frontend_noninteractive() | ||
766 | 551 | self._default_path_when_missing() | ||
767 | 568 | apt_pkg.config.clear("DPkg::options") | 552 | apt_pkg.config.clear("DPkg::options") |
768 | 569 | apt_pkg.config.set("DPkg::options::", "--force-confold") | 553 | apt_pkg.config.set("DPkg::options::", "--force-confold") |
769 | 570 | 554 | ||
770 | 555 | def _perform_hold_changes(self): | ||
771 | 556 | """ | ||
772 | 557 | Perform pending hold operations on packages. | ||
773 | 558 | """ | ||
774 | 559 | hold_changes = (len(self._version_hold_creations) > 0 or | ||
775 | 560 | len(self._version_hold_removals) > 0) | ||
776 | 561 | if not hold_changes: | ||
777 | 562 | return None | ||
778 | 563 | not_installed = [version for version in | ||
779 | 564 | self._version_hold_creations | ||
780 | 565 | if not self.is_package_installed(version)] | ||
781 | 566 | if not_installed: | ||
782 | 567 | raise TransactionError( | ||
783 | 568 | "Cannot perform the changes, since the following " + | ||
784 | 569 | "packages are not installed: %s" % ", ".join( | ||
785 | 570 | [version.package.name | ||
786 | 571 | for version in sorted(not_installed)])) | ||
787 | 572 | |||
788 | 573 | for version in self._version_hold_creations: | ||
789 | 574 | self.set_package_hold(version) | ||
790 | 575 | |||
791 | 576 | for version in self._version_hold_removals: | ||
792 | 577 | self.remove_package_hold(version) | ||
793 | 578 | |||
794 | 579 | return "Package holds successfully changed." | ||
795 | 580 | |||
796 | 581 | def _commit_package_changes(self): | ||
797 | 582 | """ | ||
798 | 583 | Commit cached APT operations and give feedback on the results as a | ||
799 | 584 | string. | ||
800 | 585 | """ | ||
801 | 586 | fetch_output = StringIO() | ||
802 | 587 | # Redirect stdout and stderr to a file. We need to work with the | ||
803 | 588 | # file descriptors, rather than sys.stdout/stderr, since dpkg is | ||
804 | 589 | # run in a subprocess. | ||
805 | 590 | fd, install_output_path = tempfile.mkstemp() | ||
806 | 591 | old_stdout = os.dup(1) | ||
807 | 592 | old_stderr = os.dup(2) | ||
808 | 593 | os.dup2(fd, 1) | ||
809 | 594 | os.dup2(fd, 2) | ||
810 | 595 | install_progress = LandscapeInstallProgress() | ||
811 | 596 | try: | ||
812 | 597 | self._cache.commit( | ||
813 | 598 | fetch_progress=LandscapeAcquireProgress(fetch_output), | ||
814 | 599 | install_progress=install_progress) | ||
815 | 600 | if not install_progress.dpkg_exited: | ||
816 | 601 | raise SystemError("dpkg didn't exit cleanly.") | ||
817 | 602 | except SystemError, error: | ||
818 | 603 | result_text = ( | ||
819 | 604 | fetch_output.getvalue() + read_file(install_output_path)) | ||
820 | 605 | raise TransactionError(error.args[0] + | ||
821 | 606 | "\n\nPackage operation log:\n" + | ||
822 | 607 | result_text) | ||
823 | 608 | else: | ||
824 | 609 | result_text = ( | ||
825 | 610 | fetch_output.getvalue() + read_file(install_output_path)) | ||
826 | 611 | finally: | ||
827 | 612 | # Restore stdout and stderr. | ||
828 | 613 | os.dup2(old_stdout, 1) | ||
829 | 614 | os.dup2(old_stderr, 2) | ||
830 | 615 | os.remove(install_output_path) | ||
831 | 616 | return result_text | ||
832 | 617 | |||
833 | 618 | def _preprocess_installs(self, fixer): | ||
834 | 619 | for version in self._version_installs: | ||
835 | 620 | # Set the candidate version, so that the version we want to | ||
836 | 621 | # install actually is the one getting installed. | ||
837 | 622 | version.package.candidate = version | ||
838 | 623 | # Set auto_fix=False to avoid removing the package we asked to | ||
839 | 624 | # install when we need to resolve dependencies. | ||
840 | 625 | version.package.mark_install(auto_fix=False) | ||
841 | 626 | fixer.clear(version.package._pkg) | ||
842 | 627 | fixer.protect(version.package._pkg) | ||
843 | 628 | |||
844 | 629 | def _preprocess_removes(self, fixer): | ||
845 | 571 | held_package_names = set() | 630 | held_package_names = set() |
846 | 631 | |||
847 | 572 | package_installs = set( | 632 | package_installs = set( |
848 | 573 | version.package for version in self._version_installs) | 633 | version.package for version in self._version_installs) |
849 | 634 | |||
850 | 574 | package_upgrades = set( | 635 | package_upgrades = set( |
851 | 575 | version.package for version in self._version_removals | 636 | version.package for version in self._version_removals |
852 | 576 | if version.package in package_installs) | 637 | if version.package in package_installs) |
872 | 577 | version_changes = self._version_installs[:] | 638 | |
854 | 578 | version_changes.extend(self._version_removals) | ||
855 | 579 | if not version_changes and not self._global_upgrade: | ||
856 | 580 | return None | ||
857 | 581 | fixer = apt_pkg.ProblemResolver(self._cache._depcache) | ||
858 | 582 | already_broken_packages = self._get_broken_packages() | ||
859 | 583 | for version in self._version_installs: | ||
860 | 584 | # Set the candidate version, so that the version we want to | ||
861 | 585 | # install actually is the one getting installed. | ||
862 | 586 | version.package.candidate = version | ||
863 | 587 | version.package.mark_install(auto_fix=False) | ||
864 | 588 | # If we need to resolve dependencies, try avoiding having | ||
865 | 589 | # the package we asked to be installed from being removed. | ||
866 | 590 | # (This is what would have been done if auto_fix would have | ||
867 | 591 | # been True. | ||
868 | 592 | fixer.clear(version.package._pkg) | ||
869 | 593 | fixer.protect(version.package._pkg) | ||
870 | 594 | if self._global_upgrade: | ||
871 | 595 | self._cache.upgrade(dist_upgrade=True) | ||
873 | 596 | for version in self._version_removals: | 639 | for version in self._version_removals: |
874 | 597 | if self._is_package_held(version.package): | 640 | if self._is_package_held(version.package): |
875 | 598 | held_package_names.add(version.package.name) | 641 | held_package_names.add(version.package.name) |
876 | 599 | if version.package in package_upgrades: | 642 | if version.package in package_upgrades: |
877 | 600 | # The server requests the old version to be removed for | 643 | # The server requests the old version to be removed for |
878 | 601 | # upgrades, since Smart worked that way. For Apt we have | 644 | # upgrades, since Smart worked that way. For Apt we have |
880 | 602 | # to take care not to mark upgraded packages for # removal. | 645 | # to take care not to mark upgraded packages for removal. |
881 | 603 | continue | 646 | continue |
882 | 604 | version.package.mark_delete(auto_fix=False) | 647 | version.package.mark_delete(auto_fix=False) |
883 | 605 | # Configure the resolver in the same way | 648 | # Configure the resolver in the same way |
884 | @@ -614,50 +657,65 @@ | |||
885 | 614 | "Can't perform the changes, since the following packages" + | 657 | "Can't perform the changes, since the following packages" + |
886 | 615 | " are held: %s" % ", ".join(sorted(held_package_names))) | 658 | " are held: %s" % ", ".join(sorted(held_package_names))) |
887 | 616 | 659 | ||
888 | 660 | def _preprocess_global_upgrade(self): | ||
889 | 661 | if self._global_upgrade: | ||
890 | 662 | self._cache.upgrade(dist_upgrade=True) | ||
891 | 663 | |||
892 | 664 | def _resolve_broken_packages(self, fixer, already_broken_packages): | ||
893 | 665 | """ | ||
894 | 666 | Attempt to automatically resolve problems with broken packages. | ||
895 | 667 | """ | ||
896 | 617 | now_broken_packages = self._get_broken_packages() | 668 | now_broken_packages = self._get_broken_packages() |
897 | 618 | if now_broken_packages != already_broken_packages: | 669 | if now_broken_packages != already_broken_packages: |
898 | 619 | try: | 670 | try: |
899 | 620 | fixer.resolve(True) | 671 | fixer.resolve(True) |
900 | 621 | except SystemError, error: | 672 | except SystemError, error: |
903 | 622 | raise TransactionError( | 673 | raise TransactionError(error.args[0] + "\n" + |
904 | 623 | error.args[0] + "\n" + self._get_unmet_dependency_info()) | 674 | self._get_unmet_dependency_info()) |
905 | 675 | |||
906 | 676 | def _preprocess_package_changes(self): | ||
907 | 677 | version_changes = self._version_installs[:] | ||
908 | 678 | version_changes.extend(self._version_removals) | ||
909 | 679 | if (not version_changes and not self._global_upgrade): | ||
910 | 680 | return [] | ||
911 | 681 | already_broken_packages = self._get_broken_packages() | ||
912 | 682 | fixer = apt_pkg.ProblemResolver(self._cache._depcache) | ||
913 | 683 | self._preprocess_installs(fixer) | ||
914 | 684 | self._preprocess_global_upgrade() | ||
915 | 685 | self._preprocess_removes(fixer) | ||
916 | 686 | self._resolve_broken_packages(fixer, already_broken_packages) | ||
917 | 687 | return version_changes | ||
918 | 688 | |||
919 | 689 | def _perform_package_changes(self): | ||
920 | 690 | """ | ||
921 | 691 | Perform pending install/remove/upgrade operations. | ||
922 | 692 | """ | ||
923 | 693 | version_changes = self._preprocess_package_changes() | ||
924 | 624 | if not self._check_changes(version_changes): | 694 | if not self._check_changes(version_changes): |
925 | 625 | return None | 695 | return None |
956 | 626 | fetch_output = StringIO() | 696 | return self._commit_package_changes() |
957 | 627 | # Redirect stdout and stderr to a file. We need to work with the | 697 | |
958 | 628 | # file descriptors, rather than sys.stdout/stderr, since dpkg is | 698 | def perform_changes(self): |
959 | 629 | # run in a subprocess. | 699 | """ |
960 | 630 | fd, install_output_path = tempfile.mkstemp() | 700 | Perform the pending package operations. |
961 | 631 | old_stdout = os.dup(1) | 701 | """ |
962 | 632 | old_stderr = os.dup(2) | 702 | self._setup_dpkg_for_changes() |
963 | 633 | os.dup2(fd, 1) | 703 | hold_result_text = self._perform_hold_changes() |
964 | 634 | os.dup2(fd, 2) | 704 | package_result_text = self._perform_package_changes() |
965 | 635 | install_progress = LandscapeInstallProgress() | 705 | results = [] |
966 | 636 | try: | 706 | if package_result_text is not None: |
967 | 637 | self._cache.commit( | 707 | results.append(package_result_text) |
968 | 638 | fetch_progress=LandscapeAcquireProgress(fetch_output), | 708 | if hold_result_text is not None: |
969 | 639 | install_progress=install_progress) | 709 | results.append(hold_result_text) |
970 | 640 | if not install_progress.dpkg_exited: | 710 | if len(results) > 0: |
971 | 641 | raise SystemError("dpkg didn't exit cleanly.") | 711 | return " ".join(results) |
942 | 642 | except SystemError, error: | ||
943 | 643 | result_text = ( | ||
944 | 644 | fetch_output.getvalue() + read_file(install_output_path)) | ||
945 | 645 | raise TransactionError( | ||
946 | 646 | error.args[0] + "\n\nPackage operation log:\n" + result_text) | ||
947 | 647 | else: | ||
948 | 648 | result_text = ( | ||
949 | 649 | fetch_output.getvalue() + read_file(install_output_path)) | ||
950 | 650 | finally: | ||
951 | 651 | # Restore stdout and stderr. | ||
952 | 652 | os.dup2(old_stdout, 1) | ||
953 | 653 | os.dup2(old_stderr, 2) | ||
954 | 654 | os.remove(install_output_path) | ||
955 | 655 | return result_text | ||
972 | 656 | 712 | ||
973 | 657 | def reset_marks(self): | 713 | def reset_marks(self): |
974 | 658 | """Clear the pending package operations.""" | 714 | """Clear the pending package operations.""" |
975 | 659 | del self._version_installs[:] | 715 | del self._version_installs[:] |
976 | 660 | del self._version_removals[:] | 716 | del self._version_removals[:] |
977 | 717 | del self._version_hold_removals[:] | ||
978 | 718 | del self._version_hold_creations[:] | ||
979 | 661 | self._global_upgrade = False | 719 | self._global_upgrade = False |
980 | 662 | self._cache.clear() | 720 | self._cache.clear() |
981 | 663 | 721 | ||
982 | @@ -673,403 +731,10 @@ | |||
983 | 673 | """Mark the package for removal.""" | 731 | """Mark the package for removal.""" |
984 | 674 | self._version_removals.append(version) | 732 | self._version_removals.append(version) |
985 | 675 | 733 | ||
1386 | 676 | 734 | def mark_hold(self, version): | |
1387 | 677 | class SmartFacade(object): | 735 | """Mark the package to be held.""" |
1388 | 678 | """Wrapper for tasks using Smart. | 736 | self._version_hold_creations.append(version) |
1389 | 679 | 737 | ||
1390 | 680 | This object wraps Smart features, in a way that makes using and testing | 738 | def mark_remove_hold(self, version): |
1391 | 681 | these features slightly more comfortable. | 739 | """Mark the package to have its hold removed.""" |
1392 | 682 | 740 | self._version_hold_removals.append(version) | |
993 | 683 | @param smart_init_kwargs: A dictionary that can be used to pass specific | ||
994 | 684 | keyword parameters to to L{smart.init}. | ||
995 | 685 | """ | ||
996 | 686 | |||
997 | 687 | _deb_package_type = None | ||
998 | 688 | supports_package_holds = False | ||
999 | 689 | supports_package_locks = True | ||
1000 | 690 | |||
1001 | 691 | def __init__(self, smart_init_kwargs={}, sysconf_args=None): | ||
1002 | 692 | if not has_smart: | ||
1003 | 693 | raise RuntimeError( | ||
1004 | 694 | "Smart needs to be installed if SmartFacade is used.") | ||
1005 | 695 | self._smart_init_kwargs = smart_init_kwargs.copy() | ||
1006 | 696 | self._smart_init_kwargs.setdefault("interface", "landscape") | ||
1007 | 697 | self._sysconfig_args = sysconf_args or {} | ||
1008 | 698 | self._reset() | ||
1009 | 699 | |||
1010 | 700 | def _reset(self): | ||
1011 | 701 | # This attribute is initialized lazily in the _get_ctrl() method. | ||
1012 | 702 | self._ctrl = None | ||
1013 | 703 | self._pkg2hash = {} | ||
1014 | 704 | self._hash2pkg = {} | ||
1015 | 705 | self._marks = {} | ||
1016 | 706 | self._caching = ALWAYS | ||
1017 | 707 | self._channels_reloaded = False | ||
1018 | 708 | |||
1019 | 709 | def deinit(self): | ||
1020 | 710 | """Deinitialize the Facade and the Smart library.""" | ||
1021 | 711 | if self._ctrl: | ||
1022 | 712 | smart.deinit() | ||
1023 | 713 | self._reset() | ||
1024 | 714 | |||
1025 | 715 | def _get_ctrl(self): | ||
1026 | 716 | if self._ctrl is None: | ||
1027 | 717 | if self._smart_init_kwargs.get("interface") == "landscape": | ||
1028 | 718 | from landscape.package.interface import ( | ||
1029 | 719 | install_landscape_interface) | ||
1030 | 720 | install_landscape_interface() | ||
1031 | 721 | self._ctrl = smart.init(**self._smart_init_kwargs) | ||
1032 | 722 | for key, value in self._sysconfig_args.items(): | ||
1033 | 723 | smart.sysconf.set(key, value, soft=True) | ||
1034 | 724 | smart.initDistro(self._ctrl) | ||
1035 | 725 | smart.initPlugins() | ||
1036 | 726 | smart.sysconf.set("pm-iface-output", True, soft=True) | ||
1037 | 727 | smart.sysconf.set("deb-non-interactive", True, soft=True) | ||
1038 | 728 | |||
1039 | 729 | # We can't import it before hand because reaching .deb.* depends | ||
1040 | 730 | # on initialization (yeah, sucky). | ||
1041 | 731 | from smart.backends.deb.base import DebPackage | ||
1042 | 732 | self._deb_package_type = DebPackage | ||
1043 | 733 | |||
1044 | 734 | self.smart_initialized() | ||
1045 | 735 | return self._ctrl | ||
1046 | 736 | |||
1047 | 737 | def smart_initialized(self): | ||
1048 | 738 | """Hook called when the Smart library is initialized.""" | ||
1049 | 739 | |||
1050 | 740 | def ensure_channels_reloaded(self): | ||
1051 | 741 | """Reload the channels if they haven't been reloaded yet.""" | ||
1052 | 742 | if self._channels_reloaded: | ||
1053 | 743 | return | ||
1054 | 744 | self._channels_reloaded = True | ||
1055 | 745 | self.reload_channels() | ||
1056 | 746 | |||
1057 | 747 | def reload_channels(self, force_reload_binaries=False): | ||
1058 | 748 | """ | ||
1059 | 749 | Reload Smart channels, getting all the cache (packages) in memory. | ||
1060 | 750 | |||
1061 | 751 | @raise: L{ChannelError} if Smart fails to reload the channels. | ||
1062 | 752 | """ | ||
1063 | 753 | ctrl = self._get_ctrl() | ||
1064 | 754 | |||
1065 | 755 | try: | ||
1066 | 756 | reload_result = ctrl.reloadChannels(caching=self._caching) | ||
1067 | 757 | except smart.Error: | ||
1068 | 758 | failed = True | ||
1069 | 759 | else: | ||
1070 | 760 | # Raise an error only if we are trying to download remote lists | ||
1071 | 761 | failed = not reload_result and self._caching == NEVER | ||
1072 | 762 | if failed: | ||
1073 | 763 | raise ChannelError("Smart failed to reload channels (%s)" | ||
1074 | 764 | % smart.sysconf.get("channels")) | ||
1075 | 765 | |||
1076 | 766 | self._hash2pkg.clear() | ||
1077 | 767 | self._pkg2hash.clear() | ||
1078 | 768 | |||
1079 | 769 | for pkg in self.get_packages(): | ||
1080 | 770 | hash = self.get_package_skeleton(pkg, False).get_hash() | ||
1081 | 771 | self._hash2pkg[hash] = pkg | ||
1082 | 772 | self._pkg2hash[pkg] = hash | ||
1083 | 773 | |||
1084 | 774 | self.channels_reloaded() | ||
1085 | 775 | |||
1086 | 776 | def channels_reloaded(self): | ||
1087 | 777 | """Hook called after Smart channels are reloaded.""" | ||
1088 | 778 | |||
1089 | 779 | def get_package_skeleton(self, pkg, with_info=True): | ||
1090 | 780 | """Return a skeleton for the provided package. | ||
1091 | 781 | |||
1092 | 782 | The skeleton represents the basic structure of the package. | ||
1093 | 783 | |||
1094 | 784 | @param pkg: Package to build skeleton from. | ||
1095 | 785 | @param with_info: If True, the skeleton will include information | ||
1096 | 786 | useful for sending data to the server. Such information isn't | ||
1097 | 787 | necessary if the skeleton will be used to build a hash. | ||
1098 | 788 | |||
1099 | 789 | @return: a L{PackageSkeleton} object. | ||
1100 | 790 | """ | ||
1101 | 791 | return build_skeleton(pkg, with_info) | ||
1102 | 792 | |||
1103 | 793 | def get_package_hash(self, pkg): | ||
1104 | 794 | """Return a hash from the given package. | ||
1105 | 795 | |||
1106 | 796 | @param pkg: a L{smart.backends.deb.base.DebPackage} objects | ||
1107 | 797 | """ | ||
1108 | 798 | return self._pkg2hash.get(pkg) | ||
1109 | 799 | |||
1110 | 800 | def get_package_hashes(self): | ||
1111 | 801 | """Get the hashes of all the packages available in the channels.""" | ||
1112 | 802 | return self._pkg2hash.values() | ||
1113 | 803 | |||
1114 | 804 | def get_packages(self): | ||
1115 | 805 | """ | ||
1116 | 806 | Get all the packages available in the channels. | ||
1117 | 807 | |||
1118 | 808 | @return: a C{list} of L{smart.backends.deb.base.DebPackage} objects | ||
1119 | 809 | """ | ||
1120 | 810 | return [pkg for pkg in self._get_ctrl().getCache().getPackages() | ||
1121 | 811 | if isinstance(pkg, self._deb_package_type)] | ||
1122 | 812 | |||
1123 | 813 | def get_locked_packages(self): | ||
1124 | 814 | """Get all packages in the channels matching the set locks.""" | ||
1125 | 815 | return smart.pkgconf.filterByFlag("lock", self.get_packages()) | ||
1126 | 816 | |||
1127 | 817 | def get_packages_by_name(self, name): | ||
1128 | 818 | """ | ||
1129 | 819 | Get all available packages matching the provided name. | ||
1130 | 820 | |||
1131 | 821 | @return: a C{list} of L{smart.backends.deb.base.DebPackage} objects | ||
1132 | 822 | """ | ||
1133 | 823 | return [pkg for pkg in self._get_ctrl().getCache().getPackages(name) | ||
1134 | 824 | if isinstance(pkg, self._deb_package_type)] | ||
1135 | 825 | |||
1136 | 826 | def get_package_by_hash(self, hash): | ||
1137 | 827 | """ | ||
1138 | 828 | Get all available packages matching the provided hash. | ||
1139 | 829 | |||
1140 | 830 | @return: a C{list} of L{smart.backends.deb.base.DebPackage} objects | ||
1141 | 831 | """ | ||
1142 | 832 | return self._hash2pkg.get(hash) | ||
1143 | 833 | |||
1144 | 834 | def mark_install(self, pkg): | ||
1145 | 835 | self._marks[pkg] = INSTALL | ||
1146 | 836 | |||
1147 | 837 | def mark_remove(self, pkg): | ||
1148 | 838 | self._marks[pkg] = REMOVE | ||
1149 | 839 | |||
1150 | 840 | def mark_upgrade(self, pkg): | ||
1151 | 841 | self._marks[pkg] = UPGRADE | ||
1152 | 842 | |||
1153 | 843 | def mark_global_upgrade(self): | ||
1154 | 844 | """Upgrade all installed packages.""" | ||
1155 | 845 | for package in self.get_packages(): | ||
1156 | 846 | if self.is_package_installed(package): | ||
1157 | 847 | self.mark_upgrade(package) | ||
1158 | 848 | |||
1159 | 849 | def reset_marks(self): | ||
1160 | 850 | self._marks.clear() | ||
1161 | 851 | |||
1162 | 852 | def perform_changes(self): | ||
1163 | 853 | ctrl = self._get_ctrl() | ||
1164 | 854 | cache = ctrl.getCache() | ||
1165 | 855 | |||
1166 | 856 | transaction = Transaction(cache) | ||
1167 | 857 | |||
1168 | 858 | policy = PolicyInstall | ||
1169 | 859 | |||
1170 | 860 | only_remove = True | ||
1171 | 861 | for pkg, oper in self._marks.items(): | ||
1172 | 862 | if oper == UPGRADE: | ||
1173 | 863 | policy = PolicyUpgrade | ||
1174 | 864 | if oper != REMOVE: | ||
1175 | 865 | only_remove = False | ||
1176 | 866 | transaction.enqueue(pkg, oper) | ||
1177 | 867 | |||
1178 | 868 | if only_remove: | ||
1179 | 869 | policy = PolicyRemove | ||
1180 | 870 | |||
1181 | 871 | transaction.setPolicy(policy) | ||
1182 | 872 | |||
1183 | 873 | try: | ||
1184 | 874 | transaction.run() | ||
1185 | 875 | except Failed, e: | ||
1186 | 876 | raise TransactionError(e.args[0]) | ||
1187 | 877 | changeset = transaction.getChangeSet() | ||
1188 | 878 | |||
1189 | 879 | if not changeset: | ||
1190 | 880 | return None # Nothing to do. | ||
1191 | 881 | |||
1192 | 882 | missing = [] | ||
1193 | 883 | for pkg, op in changeset.items(): | ||
1194 | 884 | if self._marks.get(pkg) != op: | ||
1195 | 885 | missing.append(pkg) | ||
1196 | 886 | if missing: | ||
1197 | 887 | raise DependencyError(missing) | ||
1198 | 888 | |||
1199 | 889 | try: | ||
1200 | 890 | self._ctrl.commitChangeSet(changeset) | ||
1201 | 891 | except smart.Error, e: | ||
1202 | 892 | raise TransactionError(e.args[0]) | ||
1203 | 893 | |||
1204 | 894 | output = smart.iface.get_output_for_landscape() | ||
1205 | 895 | failed = smart.iface.has_failed_for_landscape() | ||
1206 | 896 | |||
1207 | 897 | smart.iface.reset_for_landscape() | ||
1208 | 898 | |||
1209 | 899 | if failed: | ||
1210 | 900 | raise SmartError(output) | ||
1211 | 901 | return output | ||
1212 | 902 | |||
1213 | 903 | def reload_cache(self): | ||
1214 | 904 | cache = self._get_ctrl().getCache() | ||
1215 | 905 | cache.reset() | ||
1216 | 906 | cache.load() | ||
1217 | 907 | |||
1218 | 908 | def get_arch(self): | ||
1219 | 909 | """ | ||
1220 | 910 | Get the host dpkg architecture. | ||
1221 | 911 | """ | ||
1222 | 912 | self._get_ctrl() | ||
1223 | 913 | from smart.backends.deb.loader import DEBARCH | ||
1224 | 914 | return DEBARCH | ||
1225 | 915 | |||
1226 | 916 | def set_arch(self, arch): | ||
1227 | 917 | """ | ||
1228 | 918 | Set the host dpkg architecture. | ||
1229 | 919 | |||
1230 | 920 | To take effect it must be called before L{reload_channels}. | ||
1231 | 921 | |||
1232 | 922 | @param arch: the dpkg architecture to use (e.g. C{"i386"}) | ||
1233 | 923 | """ | ||
1234 | 924 | self._get_ctrl() | ||
1235 | 925 | smart.sysconf.set("deb-arch", arch) | ||
1236 | 926 | |||
1237 | 927 | # XXX workaround Smart setting DEBARCH statically in the | ||
1238 | 928 | # smart.backends.deb.base module | ||
1239 | 929 | import smart.backends.deb.loader as loader | ||
1240 | 930 | loader.DEBARCH = arch | ||
1241 | 931 | |||
1242 | 932 | def set_caching(self, mode): | ||
1243 | 933 | """ | ||
1244 | 934 | Set Smart's caching mode. | ||
1245 | 935 | |||
1246 | 936 | @param mode: The caching mode to pass to Smart's C{reloadChannels} | ||
1247 | 937 | when calling L{reload_channels} (e.g C{smart.const.NEVER} or | ||
1248 | 938 | C{smart.const.ALWAYS}). | ||
1249 | 939 | """ | ||
1250 | 940 | self._caching = mode | ||
1251 | 941 | |||
1252 | 942 | def reset_channels(self): | ||
1253 | 943 | """Remove all configured Smart channels.""" | ||
1254 | 944 | self._get_ctrl() | ||
1255 | 945 | smart.sysconf.set("channels", {}, soft=True) | ||
1256 | 946 | |||
1257 | 947 | def add_channel(self, alias, channel): | ||
1258 | 948 | """ | ||
1259 | 949 | Add a Smart channel. | ||
1260 | 950 | |||
1261 | 951 | This method can be called more than once to set multiple channels. | ||
1262 | 952 | To take effect it must be called before L{reload_channels}. | ||
1263 | 953 | |||
1264 | 954 | @param alias: A string identifying the channel to be added. | ||
1265 | 955 | @param channel: A C{dict} holding information about the channel to | ||
1266 | 956 | add (see the Smart API for details about valid keys and values). | ||
1267 | 957 | """ | ||
1268 | 958 | channels = self.get_channels() | ||
1269 | 959 | channels.update({alias: channel}) | ||
1270 | 960 | smart.sysconf.set("channels", channels, soft=True) | ||
1271 | 961 | |||
1272 | 962 | def add_channel_apt_deb(self, url, codename, components): | ||
1273 | 963 | """Add a Smart channel of type C{"apt-deb"}. | ||
1274 | 964 | |||
1275 | 965 | @see: L{add_channel} | ||
1276 | 966 | """ | ||
1277 | 967 | alias = codename | ||
1278 | 968 | channel = {"baseurl": url, "distribution": codename, | ||
1279 | 969 | "components": components, "type": "apt-deb"} | ||
1280 | 970 | self.add_channel(alias, channel) | ||
1281 | 971 | |||
1282 | 972 | def add_channel_deb_dir(self, path): | ||
1283 | 973 | """Add a Smart channel of type C{"deb-dir"}. | ||
1284 | 974 | |||
1285 | 975 | @see: L{add_channel} | ||
1286 | 976 | """ | ||
1287 | 977 | alias = path | ||
1288 | 978 | channel = {"path": path, "type": "deb-dir"} | ||
1289 | 979 | self.add_channel(alias, channel) | ||
1290 | 980 | |||
1291 | 981 | def clear_channels(self): | ||
1292 | 982 | """Clear channels. | ||
1293 | 983 | |||
1294 | 984 | This method exists to be compatible with AptFacade. Smart | ||
1295 | 985 | doesn't need to clear its channels. | ||
1296 | 986 | """ | ||
1297 | 987 | |||
1298 | 988 | def get_channels(self): | ||
1299 | 989 | """ | ||
1300 | 990 | @return: A C{dict} of all configured channels. | ||
1301 | 991 | """ | ||
1302 | 992 | self._get_ctrl() | ||
1303 | 993 | return smart.sysconf.get("channels") | ||
1304 | 994 | |||
1305 | 995 | def get_package_locks(self): | ||
1306 | 996 | """Return all set package locks. | ||
1307 | 997 | |||
1308 | 998 | @return: A C{list} of ternary tuples, contaning the name, relation | ||
1309 | 999 | and version details for each lock currently set on the system. | ||
1310 | 1000 | """ | ||
1311 | 1001 | self._get_ctrl() | ||
1312 | 1002 | locks = [] | ||
1313 | 1003 | locks_by_name = smart.pkgconf.getFlagTargets("lock") | ||
1314 | 1004 | for name in locks_by_name: | ||
1315 | 1005 | for condition in locks_by_name[name]: | ||
1316 | 1006 | relation = condition[0] or "" | ||
1317 | 1007 | version = condition[1] or "" | ||
1318 | 1008 | locks.append((name, relation, version)) | ||
1319 | 1009 | return locks | ||
1320 | 1010 | |||
1321 | 1011 | def _validate_lock_condition(self, relation, version): | ||
1322 | 1012 | if relation and not version: | ||
1323 | 1013 | raise RuntimeError("Package lock version not provided") | ||
1324 | 1014 | if version and not relation: | ||
1325 | 1015 | raise RuntimeError("Package lock relation not provided") | ||
1326 | 1016 | |||
1327 | 1017 | def set_package_lock(self, name, relation=None, version=None): | ||
1328 | 1018 | """Set a new package lock. | ||
1329 | 1019 | |||
1330 | 1020 | Any package matching the given name and possibly the given version | ||
1331 | 1021 | condition will be locked. | ||
1332 | 1022 | |||
1333 | 1023 | @param name: The name a package must match in order to be locked. | ||
1334 | 1024 | @param relation: Optionally, the relation of the version condition the | ||
1335 | 1025 | package must satisfy in order to be considered as locked. | ||
1336 | 1026 | @param version: Optionally, the version associated with C{relation}. | ||
1337 | 1027 | |||
1338 | 1028 | @note: If used at all, the C{relation} and C{version} parameter must be | ||
1339 | 1029 | both provided. | ||
1340 | 1030 | """ | ||
1341 | 1031 | self._validate_lock_condition(relation, version) | ||
1342 | 1032 | self._get_ctrl() | ||
1343 | 1033 | smart.pkgconf.setFlag("lock", name, relation, version) | ||
1344 | 1034 | |||
1345 | 1035 | def remove_package_lock(self, name, relation=None, version=None): | ||
1346 | 1036 | """Remove a package lock.""" | ||
1347 | 1037 | self._validate_lock_condition(relation, version) | ||
1348 | 1038 | self._get_ctrl() | ||
1349 | 1039 | smart.pkgconf.clearFlag("lock", name=name, relation=relation, | ||
1350 | 1040 | version=version) | ||
1351 | 1041 | |||
1352 | 1042 | def save_config(self): | ||
1353 | 1043 | """Flush the current smart configuration to disk.""" | ||
1354 | 1044 | control = self._get_ctrl() | ||
1355 | 1045 | control.saveSysConf() | ||
1356 | 1046 | |||
1357 | 1047 | def is_package_installed(self, package): | ||
1358 | 1048 | """Is the package installed?""" | ||
1359 | 1049 | return package.installed | ||
1360 | 1050 | |||
1361 | 1051 | def is_package_available(self, package): | ||
1362 | 1052 | """Is the package available for installation?""" | ||
1363 | 1053 | for loader in package.loaders: | ||
1364 | 1054 | # Is the package also in a non-installed | ||
1365 | 1055 | # loader? IOW, "available". | ||
1366 | 1056 | if not loader.getInstalled(): | ||
1367 | 1057 | return True | ||
1368 | 1058 | return False | ||
1369 | 1059 | |||
1370 | 1060 | def is_package_upgrade(self, package): | ||
1371 | 1061 | """Is the package an upgrade for another installed package?""" | ||
1372 | 1062 | is_upgrade = False | ||
1373 | 1063 | for upgrade in package.upgrades: | ||
1374 | 1064 | for provides in upgrade.providedby: | ||
1375 | 1065 | for provides_package in provides.packages: | ||
1376 | 1066 | if provides_package.installed: | ||
1377 | 1067 | is_upgrade = True | ||
1378 | 1068 | break | ||
1379 | 1069 | else: | ||
1380 | 1070 | continue | ||
1381 | 1071 | break | ||
1382 | 1072 | else: | ||
1383 | 1073 | continue | ||
1384 | 1074 | break | ||
1385 | 1075 | return is_upgrade | ||
1393 | 1076 | 741 | ||
1394 | === removed file 'landscape/package/interface.py' | |||
1395 | --- landscape/package/interface.py 2012-03-19 09:33:34 +0000 | |||
1396 | +++ landscape/package/interface.py 1970-01-01 00:00:00 +0000 | |||
1397 | @@ -1,84 +0,0 @@ | |||
1398 | 1 | import logging | ||
1399 | 2 | import types | ||
1400 | 3 | import sys | ||
1401 | 4 | |||
1402 | 5 | try: | ||
1403 | 6 | import smart.interfaces | ||
1404 | 7 | from smart.interface import Interface | ||
1405 | 8 | from smart.const import ERROR, WARNING, INFO, DEBUG | ||
1406 | 9 | except ImportError: | ||
1407 | 10 | # Smart is optional if AptFacade is being used. | ||
1408 | 11 | Interface = object | ||
1409 | 12 | |||
1410 | 13 | |||
1411 | 14 | class LandscapeInterface(Interface): | ||
1412 | 15 | |||
1413 | 16 | __output = "" | ||
1414 | 17 | __failed = False | ||
1415 | 18 | |||
1416 | 19 | def reset_for_landscape(self): | ||
1417 | 20 | """Reset output and failed flag.""" | ||
1418 | 21 | self.__failed = False | ||
1419 | 22 | self.__output = u"" | ||
1420 | 23 | |||
1421 | 24 | def get_output_for_landscape(self): | ||
1422 | 25 | """showOutput() is cached, and returned by this method.""" | ||
1423 | 26 | return self.__output | ||
1424 | 27 | |||
1425 | 28 | def has_failed_for_landscape(self): | ||
1426 | 29 | """Return true if any error() messages were logged.""" | ||
1427 | 30 | return self.__failed | ||
1428 | 31 | |||
1429 | 32 | def error(self, msg): | ||
1430 | 33 | self.__failed = True | ||
1431 | 34 | # Calling these logging.* functions here instead of message() | ||
1432 | 35 | # below will output the message or not depending on the debug | ||
1433 | 36 | # level set in landscape-client, rather than the one set in | ||
1434 | 37 | # Smart's configuration. | ||
1435 | 38 | logging.error("[Smart] %s", msg) | ||
1436 | 39 | super(LandscapeInterface, self).error(msg) | ||
1437 | 40 | |||
1438 | 41 | def warning(self, msg): | ||
1439 | 42 | logging.warning("[Smart] %s", msg) | ||
1440 | 43 | super(LandscapeInterface, self).warning(msg) | ||
1441 | 44 | |||
1442 | 45 | def info(self, msg): | ||
1443 | 46 | logging.info("[Smart] %s", msg) | ||
1444 | 47 | super(LandscapeInterface, self).info(msg) | ||
1445 | 48 | |||
1446 | 49 | def debug(self, msg): | ||
1447 | 50 | logging.debug("[Smart] %s", msg) | ||
1448 | 51 | super(LandscapeInterface, self).debug(msg) | ||
1449 | 52 | |||
1450 | 53 | def message(self, level, msg): | ||
1451 | 54 | prefix = {ERROR: "ERROR", WARNING: "WARNING", | ||
1452 | 55 | INFO: "INFO", DEBUG: "DEBUG"}.get(level) | ||
1453 | 56 | self.showOutput("%s: %s\n" % (prefix, msg)) | ||
1454 | 57 | |||
1455 | 58 | def showOutput(self, output): | ||
1456 | 59 | if not isinstance(output, unicode): | ||
1457 | 60 | try: | ||
1458 | 61 | output = output.decode("utf-8") | ||
1459 | 62 | except UnicodeDecodeError: | ||
1460 | 63 | output = output.decode("ascii", "replace") | ||
1461 | 64 | self.__output += output | ||
1462 | 65 | |||
1463 | 66 | |||
1464 | 67 | class LandscapeInterfaceModule(types.ModuleType): | ||
1465 | 68 | |||
1466 | 69 | def __init__(self): | ||
1467 | 70 | super(LandscapeInterfaceModule, self).__init__("landscape") | ||
1468 | 71 | |||
1469 | 72 | def create(self, ctrl, command=None, argv=None): | ||
1470 | 73 | return LandscapeInterface(ctrl) | ||
1471 | 74 | |||
1472 | 75 | |||
1473 | 76 | def install_landscape_interface(): | ||
1474 | 77 | if "smart.interfaces.landscape" not in sys.modules: | ||
1475 | 78 | # Plug the interface in a place Smart will recognize. | ||
1476 | 79 | smart.interfaces.landscape = LandscapeInterfaceModule() | ||
1477 | 80 | sys.modules["smart.interfaces.landscape"] = smart.interfaces.landscape | ||
1478 | 81 | |||
1479 | 82 | |||
1480 | 83 | def uninstall_landscape_interface(): | ||
1481 | 84 | sys.modules.pop("smart.interfaces.landscape", None) | ||
1482 | 85 | 0 | ||
1483 | === modified file 'landscape/package/releaseupgrader.py' | |||
1484 | --- landscape/package/releaseupgrader.py 2011-07-18 15:16:18 +0000 | |||
1485 | +++ landscape/package/releaseupgrader.py 2012-06-04 14:08:28 +0000 | |||
1486 | @@ -301,8 +301,8 @@ | |||
1487 | 301 | 301 | ||
1488 | 302 | reporter = find_reporter_command() | 302 | reporter = find_reporter_command() |
1489 | 303 | 303 | ||
1492 | 304 | # Force a smart-update run, because the sources.list has changed | 304 | # Force an apt-update run, because the sources.list has changed |
1493 | 305 | args = ["--force-smart-update"] | 305 | args = ["--force-apt-update"] |
1494 | 306 | 306 | ||
1495 | 307 | if self._config.config is not None: | 307 | if self._config.config is not None: |
1496 | 308 | args.append("--config=%s" % self._config.config) | 308 | args.append("--config=%s" % self._config.config) |
1497 | 309 | 309 | ||
1498 | === modified file 'landscape/package/reporter.py' | |||
1499 | --- landscape/package/reporter.py 2012-03-19 09:33:34 +0000 | |||
1500 | +++ landscape/package/reporter.py 2012-06-04 14:08:28 +0000 | |||
1501 | @@ -12,7 +12,6 @@ | |||
1502 | 12 | from landscape.lib.fs import touch_file | 12 | from landscape.lib.fs import touch_file |
1503 | 13 | from landscape.lib import bpickle | 13 | from landscape.lib import bpickle |
1504 | 14 | 14 | ||
1505 | 15 | from landscape.package.facade import AptFacade | ||
1506 | 16 | from landscape.package.taskhandler import ( | 15 | from landscape.package.taskhandler import ( |
1507 | 17 | PackageTaskHandlerConfiguration, PackageTaskHandler, run_task_handler) | 16 | PackageTaskHandlerConfiguration, PackageTaskHandler, run_task_handler) |
1508 | 18 | from landscape.package.store import UnknownHashIDRequest, FakePackageStore | 17 | from landscape.package.store import UnknownHashIDRequest, FakePackageStore |
1509 | @@ -31,9 +30,9 @@ | |||
1510 | 31 | reporter-specific options. | 30 | reporter-specific options. |
1511 | 32 | """ | 31 | """ |
1512 | 33 | parser = super(PackageReporterConfiguration, self).make_parser() | 32 | parser = super(PackageReporterConfiguration, self).make_parser() |
1514 | 34 | parser.add_option("--force-smart-update", default=False, | 33 | parser.add_option("--force-apt-update", default=False, |
1515 | 35 | action="store_true", | 34 | action="store_true", |
1517 | 36 | help="Force running smart-update.") | 35 | help="Force running apt-update.") |
1518 | 37 | return parser | 36 | return parser |
1519 | 38 | 37 | ||
1520 | 39 | 38 | ||
1521 | @@ -41,15 +40,14 @@ | |||
1522 | 41 | """Report information about the system packages. | 40 | """Report information about the system packages. |
1523 | 42 | 41 | ||
1524 | 43 | @cvar queue_name: Name of the task queue to pick tasks from. | 42 | @cvar queue_name: Name of the task queue to pick tasks from. |
1527 | 44 | @cvar smart_update_interval: Time interval in minutes to pass to | 43 | @cvar apt_update_interval: Don't update the APT index more often |
1528 | 45 | the C{--after} command line option of C{smart-update}. | 44 | than the given interval in minutes. |
1529 | 46 | """ | 45 | """ |
1530 | 47 | config_factory = PackageReporterConfiguration | 46 | config_factory = PackageReporterConfiguration |
1531 | 48 | 47 | ||
1532 | 49 | queue_name = "reporter" | 48 | queue_name = "reporter" |
1533 | 50 | 49 | ||
1536 | 51 | smart_update_interval = 60 | 50 | apt_update_interval = 60 |
1535 | 52 | smart_update_filename = "/usr/lib/landscape/smart-update" | ||
1537 | 53 | apt_update_filename = "/usr/lib/landscape/apt-update" | 51 | apt_update_filename = "/usr/lib/landscape/apt-update" |
1538 | 54 | sources_list_filename = "/etc/apt/sources.list" | 52 | sources_list_filename = "/etc/apt/sources.list" |
1539 | 55 | sources_list_directory = "/etc/apt/sources.list.d" | 53 | sources_list_directory = "/etc/apt/sources.list.d" |
1540 | @@ -57,13 +55,7 @@ | |||
1541 | 57 | def run(self): | 55 | def run(self): |
1542 | 58 | result = Deferred() | 56 | result = Deferred() |
1543 | 59 | 57 | ||
1551 | 60 | if isinstance(self._facade, AptFacade): | 58 | result.addCallback(lambda x: self.run_apt_update()) |
1545 | 61 | # Update APT cache if APT facade is enabled. | ||
1546 | 62 | result.addCallback(lambda x: self.run_apt_update()) | ||
1547 | 63 | else: | ||
1548 | 64 | # Run smart-update before anything else, to make sure that | ||
1549 | 65 | # the SmartFacade will load freshly updated channels | ||
1550 | 66 | result.addCallback(lambda x: self.run_smart_update()) | ||
1552 | 67 | 59 | ||
1553 | 68 | # If the appropriate hash=>id db is not there, fetch it | 60 | # If the appropriate hash=>id db is not there, fetch it |
1554 | 69 | result.addCallback(lambda x: self.fetch_hash_id_db()) | 61 | result.addCallback(lambda x: self.fetch_hash_id_db()) |
1555 | @@ -183,44 +175,6 @@ | |||
1556 | 183 | 175 | ||
1557 | 184 | return False | 176 | return False |
1558 | 185 | 177 | ||
1559 | 186 | def run_smart_update(self): | ||
1560 | 187 | """Run smart-update and log a warning in case of non-zero exit code. | ||
1561 | 188 | |||
1562 | 189 | @return: a deferred returning (out, err, code) | ||
1563 | 190 | """ | ||
1564 | 191 | if self._config.force_smart_update or self._apt_sources_have_changed(): | ||
1565 | 192 | args = () | ||
1566 | 193 | else: | ||
1567 | 194 | args = ("--after", str(self.smart_update_interval)) | ||
1568 | 195 | result = spawn_process(self.smart_update_filename, args=args) | ||
1569 | 196 | |||
1570 | 197 | def callback((out, err, code)): | ||
1571 | 198 | # smart-update --after N will exit with error code 1 when it | ||
1572 | 199 | # doesn't actually run the update code because to enough time | ||
1573 | 200 | # has passed yet, but we don't actually consider it a failure. | ||
1574 | 201 | smart_failed = False | ||
1575 | 202 | if code != 0 and code != 1: | ||
1576 | 203 | smart_failed = True | ||
1577 | 204 | if code == 1 and err.strip() != "": | ||
1578 | 205 | smart_failed = True | ||
1579 | 206 | if smart_failed: | ||
1580 | 207 | logging.warning("'%s' exited with status %d (%s)" % ( | ||
1581 | 208 | self.smart_update_filename, code, err)) | ||
1582 | 209 | logging.debug("'%s' exited with status %d (out='%s', err='%s'" % ( | ||
1583 | 210 | self.smart_update_filename, code, out, err)) | ||
1584 | 211 | touch_file(self._config.update_stamp_filename) | ||
1585 | 212 | if not smart_failed and not self._facade.get_channels(): | ||
1586 | 213 | code = 1 | ||
1587 | 214 | err = "There are no APT sources configured in %s or %s." % ( | ||
1588 | 215 | self.sources_list_filename, self.sources_list_directory) | ||
1589 | 216 | deferred = self._broker.call_if_accepted( | ||
1590 | 217 | "package-reporter-result", self.send_result, code, err) | ||
1591 | 218 | deferred.addCallback(lambda ignore: (out, err, code)) | ||
1592 | 219 | return deferred | ||
1593 | 220 | |||
1594 | 221 | result.addCallback(callback) | ||
1595 | 222 | return result | ||
1596 | 223 | |||
1597 | 224 | def _apt_update_timeout_expired(self, interval): | 178 | def _apt_update_timeout_expired(self, interval): |
1598 | 225 | """Check if the apt-update timeout has passed.""" | 179 | """Check if the apt-update timeout has passed.""" |
1599 | 226 | stamp = self._config.update_stamp_filename | 180 | stamp = self._config.update_stamp_filename |
1600 | @@ -237,8 +191,8 @@ | |||
1601 | 237 | 191 | ||
1602 | 238 | @return: a deferred returning (out, err, code) | 192 | @return: a deferred returning (out, err, code) |
1603 | 239 | """ | 193 | """ |
1606 | 240 | if (self._config.force_smart_update or self._apt_sources_have_changed() | 194 | if (self._config.force_apt_update or self._apt_sources_have_changed() |
1607 | 241 | or self._apt_update_timeout_expired(self.smart_update_interval)): | 195 | or self._apt_update_timeout_expired(self.apt_update_interval)): |
1608 | 242 | 196 | ||
1609 | 243 | result = spawn_process(self.apt_update_filename) | 197 | result = spawn_process(self.apt_update_filename) |
1610 | 244 | 198 | ||
1611 | @@ -323,7 +277,6 @@ | |||
1612 | 323 | self._store.clear_available_upgrades() | 277 | self._store.clear_available_upgrades() |
1613 | 324 | self._store.clear_installed() | 278 | self._store.clear_installed() |
1614 | 325 | self._store.clear_locked() | 279 | self._store.clear_locked() |
1615 | 326 | self._store.clear_package_locks() | ||
1616 | 327 | 280 | ||
1617 | 328 | # Don't clear the hash_id_requests table because the messages | 281 | # Don't clear the hash_id_requests table because the messages |
1618 | 329 | # associated with the existing requests might still have to be | 282 | # associated with the existing requests might still have to be |
1619 | @@ -405,7 +358,7 @@ | |||
1620 | 405 | def request_unknown_hashes(self): | 358 | def request_unknown_hashes(self): |
1621 | 406 | """Detect available packages for which we have no hash=>id mappings. | 359 | """Detect available packages for which we have no hash=>id mappings. |
1622 | 407 | 360 | ||
1624 | 408 | This method will verify if there are packages that Smart knows | 361 | This method will verify if there are packages that APT knows |
1625 | 409 | about but for which we don't have an id yet (no hash => id | 362 | about but for which we don't have an id yet (no hash => id |
1626 | 410 | translation), and deliver a message (unknown-package-hashes) | 363 | translation), and deliver a message (unknown-package-hashes) |
1627 | 411 | to request them. | 364 | to request them. |
1628 | @@ -466,16 +419,13 @@ | |||
1629 | 466 | reactor. | 419 | reactor. |
1630 | 467 | """ | 420 | """ |
1631 | 468 | 421 | ||
1636 | 469 | def changes_detected(results): | 422 | def changes_detected(result): |
1637 | 470 | # Release all smart locks, in case the changer runs after us. | 423 | if result: |
1634 | 471 | self._facade.deinit() | ||
1635 | 472 | if True in results: | ||
1638 | 473 | # Something has changed, notify the broker. | 424 | # Something has changed, notify the broker. |
1639 | 474 | return self._broker.fire_event("package-data-changed") | 425 | return self._broker.fire_event("package-data-changed") |
1640 | 475 | 426 | ||
1644 | 476 | result = gather_results([self.detect_packages_changes(), | 427 | deferred = self.detect_packages_changes() |
1645 | 477 | self.detect_package_locks_changes()]) | 428 | return deferred.addCallback(changes_detected) |
1643 | 478 | return result.addCallback(changes_detected) | ||
1646 | 479 | 429 | ||
1647 | 480 | def detect_packages_changes(self): | 430 | def detect_packages_changes(self): |
1648 | 481 | """Detect changes in the universe of known packages. | 431 | """Detect changes in the universe of known packages. |
1649 | @@ -609,53 +559,6 @@ | |||
1650 | 609 | 559 | ||
1651 | 610 | return result | 560 | return result |
1652 | 611 | 561 | ||
1653 | 612 | def detect_package_locks_changes(self): | ||
1654 | 613 | """Detect changes in known package locks. | ||
1655 | 614 | |||
1656 | 615 | This method will verify if there are package locks that: | ||
1657 | 616 | |||
1658 | 617 | - are now set, and were not; | ||
1659 | 618 | - were previously set but are not anymore; | ||
1660 | 619 | |||
1661 | 620 | In all cases, the server is notified of the new situation | ||
1662 | 621 | with a "packages" message. | ||
1663 | 622 | |||
1664 | 623 | @return: A deferred resulting in C{True} if package lock changes were | ||
1665 | 624 | detected with respect to the previous run, or C{False} otherwise. | ||
1666 | 625 | """ | ||
1667 | 626 | old_package_locks = set(self._store.get_package_locks()) | ||
1668 | 627 | current_package_locks = set(self._facade.get_package_locks()) | ||
1669 | 628 | |||
1670 | 629 | set_package_locks = current_package_locks - old_package_locks | ||
1671 | 630 | unset_package_locks = old_package_locks - current_package_locks | ||
1672 | 631 | |||
1673 | 632 | message = {} | ||
1674 | 633 | if set_package_locks: | ||
1675 | 634 | message["created"] = sorted(set_package_locks) | ||
1676 | 635 | if unset_package_locks: | ||
1677 | 636 | message["deleted"] = sorted(unset_package_locks) | ||
1678 | 637 | |||
1679 | 638 | if not message: | ||
1680 | 639 | return succeed(False) | ||
1681 | 640 | |||
1682 | 641 | message["type"] = "package-locks" | ||
1683 | 642 | result = self.send_message(message) | ||
1684 | 643 | |||
1685 | 644 | logging.info("Queuing message with changes in known package locks:" | ||
1686 | 645 | " %d created, %d deleted." % | ||
1687 | 646 | (len(set_package_locks), len(unset_package_locks))) | ||
1688 | 647 | |||
1689 | 648 | def update_currently_known(result): | ||
1690 | 649 | if set_package_locks: | ||
1691 | 650 | self._store.add_package_locks(set_package_locks) | ||
1692 | 651 | if unset_package_locks: | ||
1693 | 652 | self._store.remove_package_locks(unset_package_locks) | ||
1694 | 653 | return True | ||
1695 | 654 | |||
1696 | 655 | result.addCallback(update_currently_known) | ||
1697 | 656 | |||
1698 | 657 | return result | ||
1699 | 658 | |||
1700 | 659 | 562 | ||
1701 | 660 | class FakeGlobalReporter(PackageReporter): | 563 | class FakeGlobalReporter(PackageReporter): |
1702 | 661 | """ | 564 | """ |
1703 | 662 | 565 | ||
1704 | === modified file 'landscape/package/skeleton.py' | |||
1705 | --- landscape/package/skeleton.py 2012-03-19 09:33:34 +0000 | |||
1706 | +++ landscape/package/skeleton.py 2012-06-04 14:08:28 +0000 | |||
1707 | @@ -47,54 +47,6 @@ | |||
1708 | 47 | return digest.digest() | 47 | return digest.digest() |
1709 | 48 | 48 | ||
1710 | 49 | 49 | ||
1711 | 50 | def build_skeleton(pkg, with_info=False, with_unicode=False): | ||
1712 | 51 | if not build_skeleton.inited: | ||
1713 | 52 | build_skeleton.inited = True | ||
1714 | 53 | global DebPackage, DebNameProvides, DebOrDepends | ||
1715 | 54 | |||
1716 | 55 | # Importing from backends depends on smart.init(). | ||
1717 | 56 | from smart.backends.deb.base import ( | ||
1718 | 57 | DebPackage, DebNameProvides, DebOrDepends) | ||
1719 | 58 | |||
1720 | 59 | if not isinstance(pkg, DebPackage): | ||
1721 | 60 | raise PackageTypeError() | ||
1722 | 61 | |||
1723 | 62 | if with_unicode: | ||
1724 | 63 | skeleton = PackageSkeleton(DEB_PACKAGE, unicode(pkg.name), | ||
1725 | 64 | unicode(pkg.version)) | ||
1726 | 65 | else: | ||
1727 | 66 | skeleton = PackageSkeleton(DEB_PACKAGE, pkg.name, pkg.version) | ||
1728 | 67 | relations = set() | ||
1729 | 68 | for relation in pkg.provides: | ||
1730 | 69 | if isinstance(relation, DebNameProvides): | ||
1731 | 70 | relations.add((DEB_NAME_PROVIDES, str(relation))) | ||
1732 | 71 | else: | ||
1733 | 72 | relations.add((DEB_PROVIDES, str(relation))) | ||
1734 | 73 | for relation in pkg.requires: | ||
1735 | 74 | if isinstance(relation, DebOrDepends): | ||
1736 | 75 | relations.add((DEB_OR_REQUIRES, str(relation))) | ||
1737 | 76 | else: | ||
1738 | 77 | relations.add((DEB_REQUIRES, str(relation))) | ||
1739 | 78 | for relation in pkg.upgrades: | ||
1740 | 79 | relations.add((DEB_UPGRADES, str(relation))) | ||
1741 | 80 | for relation in pkg.conflicts: | ||
1742 | 81 | relations.add((DEB_CONFLICTS, str(relation))) | ||
1743 | 82 | |||
1744 | 83 | skeleton.relations = sorted(relations) | ||
1745 | 84 | |||
1746 | 85 | if with_info: | ||
1747 | 86 | info = pkg.loaders.keys()[0].getInfo(pkg) | ||
1748 | 87 | skeleton.section = info.getGroup() | ||
1749 | 88 | skeleton.summary = info.getSummary() | ||
1750 | 89 | skeleton.description = info.getDescription() | ||
1751 | 90 | skeleton.size = sum(info.getSize(url) for url in info.getURLs()) | ||
1752 | 91 | skeleton.installed_size = info.getInstalledSize() | ||
1753 | 92 | |||
1754 | 93 | return skeleton | ||
1755 | 94 | |||
1756 | 95 | build_skeleton.inited = False | ||
1757 | 96 | |||
1758 | 97 | |||
1759 | 98 | def relation_to_string(relation_tuple): | 50 | def relation_to_string(relation_tuple): |
1760 | 99 | """Convert an apt relation to a string representation. | 51 | """Convert an apt relation to a string representation. |
1761 | 100 | 52 | ||
1762 | 101 | 53 | ||
1763 | === modified file 'landscape/package/store.py' | |||
1764 | --- landscape/package/store.py 2012-03-19 09:33:34 +0000 | |||
1765 | +++ landscape/package/store.py 2012-06-04 14:08:28 +0000 | |||
1766 | @@ -249,40 +249,6 @@ | |||
1767 | 249 | cursor.execute("DELETE FROM locked") | 249 | cursor.execute("DELETE FROM locked") |
1768 | 250 | 250 | ||
1769 | 251 | @with_cursor | 251 | @with_cursor |
1770 | 252 | def get_package_locks(self, cursor): | ||
1771 | 253 | """Get all package locks.""" | ||
1772 | 254 | cursor.execute("SELECT name, relation, version FROM package_locks") | ||
1773 | 255 | return [(row[0], row[1], row[2]) for row in cursor.fetchall()] | ||
1774 | 256 | |||
1775 | 257 | @with_cursor | ||
1776 | 258 | def add_package_locks(self, cursor, locks): | ||
1777 | 259 | """Add a list of package locks to the store. | ||
1778 | 260 | |||
1779 | 261 | @param locks: A C{list} of ternary tuples each one contains the | ||
1780 | 262 | name, the relation and the version of the package lock to be added. | ||
1781 | 263 | """ | ||
1782 | 264 | for name, relation, version in locks: | ||
1783 | 265 | cursor.execute("REPLACE INTO package_locks VALUES (?, ?, ?)", | ||
1784 | 266 | (name, relation or "", version or "",)) | ||
1785 | 267 | |||
1786 | 268 | @with_cursor | ||
1787 | 269 | def remove_package_locks(self, cursor, locks): | ||
1788 | 270 | """Remove a list of package locks from the store. | ||
1789 | 271 | |||
1790 | 272 | @param locks: A C{list} of ternary tuples each one contains the name, | ||
1791 | 273 | the relation and the version of the package lock to be removed. | ||
1792 | 274 | """ | ||
1793 | 275 | for name, relation, version in locks: | ||
1794 | 276 | cursor.execute("DELETE FROM package_locks WHERE name=? AND " | ||
1795 | 277 | "relation=? AND version=?", | ||
1796 | 278 | (name, relation or "", version or "")) | ||
1797 | 279 | |||
1798 | 280 | @with_cursor | ||
1799 | 281 | def clear_package_locks(self, cursor): | ||
1800 | 282 | """Remove all package locks.""" | ||
1801 | 283 | cursor.execute("DELETE FROM package_locks") | ||
1802 | 284 | |||
1803 | 285 | @with_cursor | ||
1804 | 286 | def add_hash_id_request(self, cursor, hashes): | 252 | def add_hash_id_request(self, cursor, hashes): |
1805 | 287 | hashes = list(hashes) | 253 | hashes = list(hashes) |
1806 | 288 | cursor.execute("INSERT INTO hash_id_request (hashes, timestamp)" | 254 | cursor.execute("INSERT INTO hash_id_request (hashes, timestamp)" |
1807 | @@ -458,9 +424,6 @@ | |||
1808 | 458 | # try block. | 424 | # try block. |
1809 | 459 | cursor = db.cursor() | 425 | cursor = db.cursor() |
1810 | 460 | try: | 426 | try: |
1811 | 461 | cursor.execute("CREATE TABLE package_locks" | ||
1812 | 462 | " (name TEXT NOT NULL, relation TEXT, version TEXT," | ||
1813 | 463 | " UNIQUE(name, relation, version))") | ||
1814 | 464 | cursor.execute("CREATE TABLE locked" | 427 | cursor.execute("CREATE TABLE locked" |
1815 | 465 | " (id INTEGER PRIMARY KEY)") | 428 | " (id INTEGER PRIMARY KEY)") |
1816 | 466 | cursor.execute("CREATE TABLE available" | 429 | cursor.execute("CREATE TABLE available" |
1817 | 467 | 430 | ||
1818 | === modified file 'landscape/package/taskhandler.py' | |||
1819 | --- landscape/package/taskhandler.py 2012-03-19 09:33:34 +0000 | |||
1820 | +++ landscape/package/taskhandler.py 2012-06-04 14:08:28 +0000 | |||
1821 | @@ -254,19 +254,15 @@ | |||
1822 | 254 | words = re.findall("[A-Z][a-z]+", cls.__name__) | 254 | words = re.findall("[A-Z][a-z]+", cls.__name__) |
1823 | 255 | init_logging(config, "-".join(word.lower() for word in words)) | 255 | init_logging(config, "-".join(word.lower() for word in words)) |
1824 | 256 | 256 | ||
1826 | 257 | # Setup our umask for Smart to use, this needs to setup file permissions to | 257 | # Setup our umask for Apt to use, this needs to setup file permissions to |
1827 | 258 | # 0644 so... | 258 | # 0644 so... |
1828 | 259 | os.umask(022) | 259 | os.umask(022) |
1829 | 260 | 260 | ||
1830 | 261 | package_store = cls.package_store_class(config.store_filename) | 261 | package_store = cls.package_store_class(config.store_filename) |
1831 | 262 | # Delay importing of the facades so that we don't | 262 | # Delay importing of the facades so that we don't |
1839 | 263 | # import Smart unless we need to. | 263 | # import Apt unless we need to. |
1840 | 264 | from landscape.package.facade import ( | 264 | from landscape.package.facade import AptFacade |
1841 | 265 | AptFacade, SmartFacade, has_new_enough_apt) | 265 | package_facade = AptFacade() |
1835 | 266 | if has_new_enough_apt: | ||
1836 | 267 | package_facade = AptFacade() | ||
1837 | 268 | else: | ||
1838 | 269 | package_facade = SmartFacade() | ||
1842 | 270 | 266 | ||
1843 | 271 | def finish(): | 267 | def finish(): |
1844 | 272 | connector.disconnect() | 268 | connector.disconnect() |
1845 | 273 | 269 | ||
1846 | === modified file 'landscape/package/tests/helpers.py' | |||
1847 | --- landscape/package/tests/helpers.py 2012-03-19 09:33:34 +0000 | |||
1848 | +++ landscape/package/tests/helpers.py 2012-06-04 14:08:28 +0000 | |||
1849 | @@ -3,12 +3,6 @@ | |||
1850 | 3 | import textwrap | 3 | import textwrap |
1851 | 4 | import time | 4 | import time |
1852 | 5 | 5 | ||
1853 | 6 | try: | ||
1854 | 7 | import smart | ||
1855 | 8 | except ImportError: | ||
1856 | 9 | # Smart is optional if AptFacade is being used. | ||
1857 | 10 | pass | ||
1858 | 11 | |||
1859 | 12 | import apt_inst | 6 | import apt_inst |
1860 | 13 | import apt_pkg | 7 | import apt_pkg |
1861 | 14 | 8 | ||
1862 | @@ -127,39 +121,6 @@ | |||
1863 | 127 | test_case.facade.add_channel_deb_dir(test_case.repository_dir) | 121 | test_case.facade.add_channel_deb_dir(test_case.repository_dir) |
1864 | 128 | 122 | ||
1865 | 129 | 123 | ||
1866 | 130 | class SmartHelper(object): | ||
1867 | 131 | |||
1868 | 132 | def set_up(self, test_case): | ||
1869 | 133 | test_case.smart_dir = test_case.makeDir() | ||
1870 | 134 | test_case.smart_config = test_case.makeFile("") | ||
1871 | 135 | test_case.repository_dir = test_case.makeDir() | ||
1872 | 136 | create_simple_repository(test_case.repository_dir) | ||
1873 | 137 | |||
1874 | 138 | def tear_down(self, test_case): | ||
1875 | 139 | if smart.iface.object: | ||
1876 | 140 | smart.deinit() | ||
1877 | 141 | |||
1878 | 142 | |||
1879 | 143 | class SmartFacadeHelper(SmartHelper): | ||
1880 | 144 | |||
1881 | 145 | def set_up(self, test_case): | ||
1882 | 146 | super(SmartFacadeHelper, self).set_up(test_case) | ||
1883 | 147 | |||
1884 | 148 | from landscape.package.facade import SmartFacade | ||
1885 | 149 | |||
1886 | 150 | class Facade(SmartFacade): | ||
1887 | 151 | repository_dir = test_case.repository_dir | ||
1888 | 152 | |||
1889 | 153 | def smart_initialized(self): | ||
1890 | 154 | self.reset_channels() | ||
1891 | 155 | self.add_channel_deb_dir(test_case.repository_dir) | ||
1892 | 156 | |||
1893 | 157 | test_case.Facade = Facade | ||
1894 | 158 | test_case.facade = Facade({"datadir": test_case.smart_dir, | ||
1895 | 159 | "configfile": test_case.smart_config}, | ||
1896 | 160 | {"sync-apt-sources": False}) | ||
1897 | 161 | |||
1898 | 162 | |||
1899 | 163 | PKGNAME1 = "name1_version1-release1_all.deb" | 124 | PKGNAME1 = "name1_version1-release1_all.deb" |
1900 | 164 | PKGNAME2 = "name2_version2-release2_all.deb" | 125 | PKGNAME2 = "name2_version2-release2_all.deb" |
1901 | 165 | PKGNAME3 = "name3_version3-release3_all.deb" | 126 | PKGNAME3 = "name3_version3-release3_all.deb" |
1902 | 166 | 127 | ||
1903 | === modified file 'landscape/package/tests/test_changer.py' | |||
1904 | --- landscape/package/tests/test_changer.py 2012-03-21 16:15:49 +0000 | |||
1905 | +++ landscape/package/tests/test_changer.py 2012-06-04 14:08:28 +0000 | |||
1906 | @@ -6,12 +6,6 @@ | |||
1907 | 6 | 6 | ||
1908 | 7 | from twisted.internet.defer import Deferred | 7 | from twisted.internet.defer import Deferred |
1909 | 8 | 8 | ||
1910 | 9 | try: | ||
1911 | 10 | from smart.cache import Provides | ||
1912 | 11 | except ImportError: | ||
1913 | 12 | # Smart is optional if AptFacade is being used. | ||
1914 | 13 | pass | ||
1915 | 14 | |||
1916 | 15 | from landscape.lib.fs import create_file, read_file, touch_file | 9 | from landscape.lib.fs import create_file, read_file, touch_file |
1917 | 16 | from landscape.package.changer import ( | 10 | from landscape.package.changer import ( |
1918 | 17 | PackageChanger, main, find_changer_command, UNKNOWN_PACKAGE_DATA_TIMEOUT, | 11 | PackageChanger, main, find_changer_command, UNKNOWN_PACKAGE_DATA_TIMEOUT, |
1919 | @@ -19,19 +13,107 @@ | |||
1920 | 19 | POLICY_ALLOW_ALL_CHANGES, ERROR_RESULT) | 13 | POLICY_ALLOW_ALL_CHANGES, ERROR_RESULT) |
1921 | 20 | from landscape.package.store import PackageStore | 14 | from landscape.package.store import PackageStore |
1922 | 21 | from landscape.package.facade import ( | 15 | from landscape.package.facade import ( |
1924 | 22 | DependencyError, TransactionError, SmartError, has_new_enough_apt) | 16 | DependencyError, TransactionError) |
1925 | 23 | from landscape.package.changer import ( | 17 | from landscape.package.changer import ( |
1926 | 24 | PackageChangerConfiguration, ChangePackagesResult) | 18 | PackageChangerConfiguration, ChangePackagesResult) |
1927 | 25 | from landscape.tests.mocker import ANY | 19 | from landscape.tests.mocker import ANY |
1928 | 26 | from landscape.tests.helpers import ( | 20 | from landscape.tests.helpers import ( |
1929 | 27 | LandscapeTest, BrokerServiceHelper) | 21 | LandscapeTest, BrokerServiceHelper) |
1930 | 28 | from landscape.package.tests.helpers import ( | 22 | from landscape.package.tests.helpers import ( |
1932 | 29 | SmartFacadeHelper, HASH1, HASH2, HASH3, PKGDEB1, PKGDEB2, PKGNAME2, | 23 | HASH1, HASH2, HASH3, PKGDEB1, PKGDEB2, |
1933 | 30 | AptFacadeHelper, SimpleRepositoryHelper) | 24 | AptFacadeHelper, SimpleRepositoryHelper) |
1938 | 31 | from landscape.manager.manager import FAILED, SUCCEEDED | 25 | from landscape.manager.manager import FAILED |
1939 | 32 | 26 | ||
1940 | 33 | 27 | ||
1941 | 34 | class PackageChangerTestMixin(object): | 28 | class AptPackageChangerTest(LandscapeTest): |
1942 | 29 | |||
1943 | 30 | helpers = [AptFacadeHelper, SimpleRepositoryHelper, BrokerServiceHelper] | ||
1944 | 31 | |||
1945 | 32 | def setUp(self): | ||
1946 | 33 | |||
1947 | 34 | def set_up(ignored): | ||
1948 | 35 | |||
1949 | 36 | self.store = PackageStore(self.makeFile()) | ||
1950 | 37 | self.config = PackageChangerConfiguration() | ||
1951 | 38 | self.config.data_path = self.makeDir() | ||
1952 | 39 | os.mkdir(self.config.package_directory) | ||
1953 | 40 | os.mkdir(self.config.binaries_path) | ||
1954 | 41 | touch_file(self.config.update_stamp_filename) | ||
1955 | 42 | self.changer = PackageChanger( | ||
1956 | 43 | self.store, self.facade, self.remote, self.config) | ||
1957 | 44 | service = self.broker_service | ||
1958 | 45 | service.message_store.set_accepted_types(["change-packages-result", | ||
1959 | 46 | "operation-result"]) | ||
1960 | 47 | |||
1961 | 48 | result = super(AptPackageChangerTest, self).setUp() | ||
1962 | 49 | return result.addCallback(set_up) | ||
1963 | 50 | |||
1964 | 51 | def set_pkg1_installed(self): | ||
1965 | 52 | """Return the hash of a package that is installed.""" | ||
1966 | 53 | self._add_system_package("foo") | ||
1967 | 54 | self.facade.reload_channels() | ||
1968 | 55 | [foo] = self.facade.get_packages_by_name("foo") | ||
1969 | 56 | return self.facade.get_package_hash(foo) | ||
1970 | 57 | |||
1971 | 58 | def set_pkg2_satisfied(self): | ||
1972 | 59 | """Return the hash of a package that can be installed.""" | ||
1973 | 60 | self._add_package_to_deb_dir(self.repository_dir, "bar") | ||
1974 | 61 | self.facade.reload_channels() | ||
1975 | 62 | [bar] = self.facade.get_packages_by_name("bar") | ||
1976 | 63 | return self.facade.get_package_hash(bar) | ||
1977 | 64 | |||
1978 | 65 | def set_pkg1_and_pkg2_satisfied(self): | ||
1979 | 66 | """Make a package depend on another package. | ||
1980 | 67 | |||
1981 | 68 | Return the hashes of the two packages. | ||
1982 | 69 | """ | ||
1983 | 70 | self._add_package_to_deb_dir( | ||
1984 | 71 | self.repository_dir, "foo", control_fields={"Depends": "bar"}) | ||
1985 | 72 | self._add_package_to_deb_dir(self.repository_dir, "bar") | ||
1986 | 73 | self.facade.reload_channels() | ||
1987 | 74 | [foo] = self.facade.get_packages_by_name("foo") | ||
1988 | 75 | [bar] = self.facade.get_packages_by_name("bar") | ||
1989 | 76 | return ( | ||
1990 | 77 | self.facade.get_package_hash(foo), | ||
1991 | 78 | self.facade.get_package_hash(bar)) | ||
1992 | 79 | |||
1993 | 80 | def set_pkg2_upgrades_pkg1(self): | ||
1994 | 81 | """Make it so that one package upgrades another. | ||
1995 | 82 | |||
1996 | 83 | Return the hashes of the two packages. | ||
1997 | 84 | """ | ||
1998 | 85 | self._add_system_package("foo", version="1.0") | ||
1999 | 86 | self._add_package_to_deb_dir(self.repository_dir, "foo", version="2.0") | ||
2000 | 87 | self.facade.reload_channels() | ||
2001 | 88 | foo_1, foo_2 = sorted(self.facade.get_packages_by_name("foo")) | ||
2002 | 89 | return ( | ||
2003 | 90 | self.facade.get_package_hash(foo_1), | ||
2004 | 91 | self.facade.get_package_hash(foo_2)) | ||
2005 | 92 | |||
2006 | 93 | def remove_pkg2(self): | ||
2007 | 94 | """Remove package name2 from its repository.""" | ||
2008 | 95 | packages_file = os.path.join(self.repository_dir, "Packages") | ||
2009 | 96 | packages_contents = read_file(packages_file) | ||
2010 | 97 | packages_contents = "\n\n".join( | ||
2011 | 98 | [stanza for stanza in packages_contents.split("\n\n") | ||
2012 | 99 | if "Package: name2" not in stanza]) | ||
2013 | 100 | create_file(packages_file, packages_contents) | ||
2014 | 101 | |||
2015 | 102 | def get_transaction_error_message(self): | ||
2016 | 103 | """Return part of the apt transaction error message.""" | ||
2017 | 104 | return "Unable to correct problems" | ||
2018 | 105 | |||
2019 | 106 | def get_binaries_channels(self, binaries_path): | ||
2020 | 107 | """Return the channels that will be used for the binaries.""" | ||
2021 | 108 | return [{"baseurl": "file://%s" % binaries_path, | ||
2022 | 109 | "components": "", | ||
2023 | 110 | "distribution": "./", | ||
2024 | 111 | "type": "deb"}] | ||
2025 | 112 | |||
2026 | 113 | def get_package_name(self, version): | ||
2027 | 114 | """Return the name of the package.""" | ||
2028 | 115 | return version.package.name | ||
2029 | 116 | |||
2030 | 35 | 117 | ||
2031 | 36 | def disable_clear_channels(self): | 118 | def disable_clear_channels(self): |
2032 | 37 | """Disable clear_channels(), so that it doesn't remove test setup. | 119 | """Disable clear_channels(), so that it doesn't remove test setup. |
2033 | @@ -441,7 +523,7 @@ | |||
2034 | 441 | 523 | ||
2035 | 442 | def test_tasks_are_isolated_cache(self): | 524 | def test_tasks_are_isolated_cache(self): |
2036 | 443 | """ | 525 | """ |
2038 | 444 | The package (apt/smart) cache should be reset between task runs. | 526 | The package (APT) cache should be reset between task runs. |
2039 | 445 | In this test, we try to run two different operations, first | 527 | In this test, we try to run two different operations, first |
2040 | 446 | installing package 2, then removing package 1. Both tasks will | 528 | installing package 2, then removing package 1. Both tasks will |
2041 | 447 | fail for lack of superuser privileges. If the package cache | 529 | fail for lack of superuser privileges. If the package cache |
2042 | @@ -533,7 +615,7 @@ | |||
2043 | 533 | """ | 615 | """ |
2044 | 534 | Besides asking for individual changes, the server may also request | 616 | Besides asking for individual changes, the server may also request |
2045 | 535 | the client to perform a global upgrade. This would be the equivalent | 617 | the client to perform a global upgrade. This would be the equivalent |
2047 | 536 | of a "smart upgrade" command being executed in the command line. | 618 | of a "apt-get upgrade" command being executed in the command line. |
2048 | 537 | """ | 619 | """ |
2049 | 538 | hash1, hash2 = self.set_pkg2_upgrades_pkg1() | 620 | hash1, hash2 = self.set_pkg2_upgrades_pkg1() |
2050 | 539 | self.store.set_hash_ids({hash1: 1, hash2: 2}) | 621 | self.store.set_hash_ids({hash1: 1, hash2: 2}) |
2051 | @@ -638,8 +720,7 @@ | |||
2052 | 638 | """ | 720 | """ |
2053 | 639 | After the package changer has run, we want the package-reporter to run | 721 | After the package changer has run, we want the package-reporter to run |
2054 | 640 | to report the recent changes. If we're running as root, we want to | 722 | to report the recent changes. If we're running as root, we want to |
2057 | 641 | change to the "landscape" user and "landscape" group. We also want to | 723 | change to the "landscape" user and "landscape" group. |
2056 | 642 | deinitialize Smart to let the reporter run smart-update cleanly. | ||
2058 | 643 | """ | 724 | """ |
2059 | 644 | 725 | ||
2060 | 645 | # We are running as root | 726 | # We are running as root |
2061 | @@ -647,13 +728,8 @@ | |||
2062 | 647 | getuid_mock() | 728 | getuid_mock() |
2063 | 648 | self.mocker.result(0) | 729 | self.mocker.result(0) |
2064 | 649 | 730 | ||
2065 | 650 | # The order matters (first smart then gid and finally uid) | ||
2066 | 651 | self.mocker.order() | 731 | self.mocker.order() |
2067 | 652 | 732 | ||
2068 | 653 | # Deinitialize smart | ||
2069 | 654 | facade_mock = self.mocker.patch(self.facade) | ||
2070 | 655 | facade_mock.deinit() | ||
2071 | 656 | |||
2072 | 657 | # We want to return a known gid | 733 | # We want to return a known gid |
2073 | 658 | grnam_mock = self.mocker.replace("grp.getgrnam") | 734 | grnam_mock = self.mocker.replace("grp.getgrnam") |
2074 | 659 | grnam_mock("landscape") | 735 | grnam_mock("landscape") |
2075 | @@ -810,27 +886,6 @@ | |||
2076 | 810 | "type": "change-packages-result"}]) | 886 | "type": "change-packages-result"}]) |
2077 | 811 | return result.addCallback(got_result) | 887 | return result.addCallback(got_result) |
2078 | 812 | 888 | ||
2079 | 813 | def test_smart_error_with_unicode_data(self): | ||
2080 | 814 | self.store.set_hash_ids({HASH1: 1}) | ||
2081 | 815 | self.store.add_task("changer", | ||
2082 | 816 | {"type": "change-packages", "install": [1], | ||
2083 | 817 | "operation-id": 123}) | ||
2084 | 818 | |||
2085 | 819 | def raise_error(self): | ||
2086 | 820 | raise SmartError(u"áéÃóú") | ||
2087 | 821 | self.replace_perform_changes(raise_error) | ||
2088 | 822 | self.disable_clear_channels() | ||
2089 | 823 | |||
2090 | 824 | result = self.changer.handle_tasks() | ||
2091 | 825 | |||
2092 | 826 | def got_result(result): | ||
2093 | 827 | self.assertMessages(self.get_pending_messages(), | ||
2094 | 828 | [{"operation-id": 123, | ||
2095 | 829 | "result-code": 100, | ||
2096 | 830 | "result-text": u"áéÃóú", | ||
2097 | 831 | "type": "change-packages-result"}]) | ||
2098 | 832 | return result.addCallback(got_result) | ||
2099 | 833 | |||
2100 | 834 | def test_update_stamp_exists(self): | 889 | def test_update_stamp_exists(self): |
2101 | 835 | """ | 890 | """ |
2102 | 836 | L{PackageChanger.update_exists} returns C{True} if the | 891 | L{PackageChanger.update_exists} returns C{True} if the |
2103 | @@ -892,360 +947,6 @@ | |||
2104 | 892 | self.assertFalse(os.path.exists(existing_deb_path)) | 947 | self.assertFalse(os.path.exists(existing_deb_path)) |
2105 | 893 | 948 | ||
2106 | 894 | 949 | ||
2107 | 895 | class SmartPackageChangerTest(LandscapeTest, PackageChangerTestMixin): | ||
2108 | 896 | |||
2109 | 897 | helpers = [SmartFacadeHelper, BrokerServiceHelper] | ||
2110 | 898 | |||
2111 | 899 | def setUp(self): | ||
2112 | 900 | |||
2113 | 901 | def set_up(ignored): | ||
2114 | 902 | |||
2115 | 903 | self.store = PackageStore(self.makeFile()) | ||
2116 | 904 | self.config = PackageChangerConfiguration() | ||
2117 | 905 | self.config.data_path = self.makeDir() | ||
2118 | 906 | os.mkdir(self.config.package_directory) | ||
2119 | 907 | os.mkdir(self.config.binaries_path) | ||
2120 | 908 | touch_file(self.config.update_stamp_filename) | ||
2121 | 909 | self.changer = PackageChanger( | ||
2122 | 910 | self.store, self.facade, self.remote, self.config) | ||
2123 | 911 | service = self.broker_service | ||
2124 | 912 | service.message_store.set_accepted_types(["change-packages-result", | ||
2125 | 913 | "operation-result"]) | ||
2126 | 914 | |||
2127 | 915 | result = super(SmartPackageChangerTest, self).setUp() | ||
2128 | 916 | return result.addCallback(set_up) | ||
2129 | 917 | |||
2130 | 918 | def set_pkg1_installed(self): | ||
2131 | 919 | previous = self.Facade.channels_reloaded | ||
2132 | 920 | |||
2133 | 921 | def callback(self): | ||
2134 | 922 | previous(self) | ||
2135 | 923 | self.get_packages_by_name("name1")[0].installed = True | ||
2136 | 924 | self.Facade.channels_reloaded = callback | ||
2137 | 925 | return HASH1 | ||
2138 | 926 | |||
2139 | 927 | def set_pkg2_upgrades_pkg1(self): | ||
2140 | 928 | previous = self.Facade.channels_reloaded | ||
2141 | 929 | |||
2142 | 930 | def callback(self): | ||
2143 | 931 | from smart.backends.deb.base import DebUpgrades | ||
2144 | 932 | previous(self) | ||
2145 | 933 | [pkg2] = self.get_packages_by_name("name2") | ||
2146 | 934 | pkg2.upgrades += (DebUpgrades("name1", "=", "version1-release1"),) | ||
2147 | 935 | self.reload_cache() # Relink relations. | ||
2148 | 936 | self.Facade.channels_reloaded = callback | ||
2149 | 937 | self.set_pkg2_satisfied() | ||
2150 | 938 | self.set_pkg1_installed() | ||
2151 | 939 | return HASH1, HASH2 | ||
2152 | 940 | |||
2153 | 941 | def set_pkg2_satisfied(self): | ||
2154 | 942 | previous = self.Facade.channels_reloaded | ||
2155 | 943 | |||
2156 | 944 | def callback(self): | ||
2157 | 945 | previous(self) | ||
2158 | 946 | [pkg2] = self.get_packages_by_name("name2") | ||
2159 | 947 | pkg2.requires = () | ||
2160 | 948 | self.reload_cache() # Relink relations. | ||
2161 | 949 | self.Facade.channels_reloaded = callback | ||
2162 | 950 | return HASH2 | ||
2163 | 951 | |||
2164 | 952 | def set_pkg1_and_pkg2_satisfied(self): | ||
2165 | 953 | previous = self.Facade.channels_reloaded | ||
2166 | 954 | |||
2167 | 955 | def callback(self): | ||
2168 | 956 | previous(self) | ||
2169 | 957 | |||
2170 | 958 | provide1 = Provides("prerequirename1", "prerequireversion1") | ||
2171 | 959 | provide2 = Provides("requirename1", "requireversion1") | ||
2172 | 960 | [pkg2] = self.get_packages_by_name("name2") | ||
2173 | 961 | pkg2.provides += (provide1, provide2) | ||
2174 | 962 | |||
2175 | 963 | provide1 = Provides("prerequirename2", "prerequireversion2") | ||
2176 | 964 | provide2 = Provides("requirename2", "requireversion2") | ||
2177 | 965 | [pkg1] = self.get_packages_by_name("name1") | ||
2178 | 966 | pkg1.provides += (provide1, provide2) | ||
2179 | 967 | |||
2180 | 968 | # Ask Smart to reprocess relationships. | ||
2181 | 969 | self.reload_cache() | ||
2182 | 970 | self.Facade.channels_reloaded = callback | ||
2183 | 971 | return HASH1, HASH2 | ||
2184 | 972 | |||
2185 | 973 | def remove_pkg2(self): | ||
2186 | 974 | os.remove(os.path.join(self.repository_dir, PKGNAME2)) | ||
2187 | 975 | |||
2188 | 976 | def get_transaction_error_message(self): | ||
2189 | 977 | return "requirename1 = requireversion1" | ||
2190 | 978 | |||
2191 | 979 | def get_binaries_channels(self, binaries_path): | ||
2192 | 980 | return {binaries_path: {"type": "deb-dir", | ||
2193 | 981 | "path": binaries_path}} | ||
2194 | 982 | |||
2195 | 983 | def get_package_name(self, package): | ||
2196 | 984 | return package.name | ||
2197 | 985 | |||
2198 | 986 | def test_change_package_locks(self): | ||
2199 | 987 | """ | ||
2200 | 988 | The L{PackageChanger.handle_tasks} method appropriately creates and | ||
2201 | 989 | deletes package locks as requested by the C{change-package-locks} | ||
2202 | 990 | message. | ||
2203 | 991 | """ | ||
2204 | 992 | self.facade.set_package_lock("bar") | ||
2205 | 993 | self.store.add_task("changer", {"type": "change-package-locks", | ||
2206 | 994 | "create": [("foo", ">=", "1.0")], | ||
2207 | 995 | "delete": [("bar", None, None)], | ||
2208 | 996 | "operation-id": 123}) | ||
2209 | 997 | |||
2210 | 998 | def assert_result(result): | ||
2211 | 999 | self.facade.deinit() | ||
2212 | 1000 | self.assertEqual(self.facade.get_package_locks(), | ||
2213 | 1001 | [("foo", ">=", "1.0")]) | ||
2214 | 1002 | self.assertIn("Queuing message with change package locks results " | ||
2215 | 1003 | "to exchange urgently.", self.logfile.getvalue()) | ||
2216 | 1004 | self.assertMessages(self.get_pending_messages(), | ||
2217 | 1005 | [{"type": "operation-result", | ||
2218 | 1006 | "operation-id": 123, | ||
2219 | 1007 | "status": SUCCEEDED, | ||
2220 | 1008 | "result-text": "Package locks successfully" | ||
2221 | 1009 | " changed.", | ||
2222 | 1010 | "result-code": 0}]) | ||
2223 | 1011 | |||
2224 | 1012 | result = self.changer.handle_tasks() | ||
2225 | 1013 | return result.addCallback(assert_result) | ||
2226 | 1014 | |||
2227 | 1015 | def test_change_package_locks_create_with_already_existing(self): | ||
2228 | 1016 | """ | ||
2229 | 1017 | The L{PackageChanger.handle_tasks} method gracefully handles requests | ||
2230 | 1018 | for creating package locks that already exist. | ||
2231 | 1019 | """ | ||
2232 | 1020 | self.facade.set_package_lock("foo") | ||
2233 | 1021 | self.store.add_task("changer", {"type": "change-package-locks", | ||
2234 | 1022 | "create": [("foo", None, None)], | ||
2235 | 1023 | "operation-id": 123}) | ||
2236 | 1024 | |||
2237 | 1025 | def assert_result(result): | ||
2238 | 1026 | self.facade.deinit() | ||
2239 | 1027 | self.assertEqual(self.facade.get_package_locks(), | ||
2240 | 1028 | [("foo", "", "")]) | ||
2241 | 1029 | self.assertMessages(self.get_pending_messages(), | ||
2242 | 1030 | [{"type": "operation-result", | ||
2243 | 1031 | "operation-id": 123, | ||
2244 | 1032 | "status": SUCCEEDED, | ||
2245 | 1033 | "result-text": "Package locks successfully" | ||
2246 | 1034 | " changed.", | ||
2247 | 1035 | "result-code": 0}]) | ||
2248 | 1036 | |||
2249 | 1037 | result = self.changer.handle_tasks() | ||
2250 | 1038 | return result.addCallback(assert_result) | ||
2251 | 1039 | |||
2252 | 1040 | def test_change_package_locks_delete_without_already_existing(self): | ||
2253 | 1041 | """ | ||
2254 | 1042 | The L{PackageChanger.handle_tasks} method gracefully handles requests | ||
2255 | 1043 | for deleting package locks that don't exist. | ||
2256 | 1044 | """ | ||
2257 | 1045 | self.store.add_task("changer", {"type": "change-package-locks", | ||
2258 | 1046 | "delete": [("foo", ">=", "1.0")], | ||
2259 | 1047 | "operation-id": 123}) | ||
2260 | 1048 | |||
2261 | 1049 | def assert_result(result): | ||
2262 | 1050 | self.facade.deinit() | ||
2263 | 1051 | self.assertEqual(self.facade.get_package_locks(), []) | ||
2264 | 1052 | self.assertMessages(self.get_pending_messages(), | ||
2265 | 1053 | [{"type": "operation-result", | ||
2266 | 1054 | "operation-id": 123, | ||
2267 | 1055 | "status": SUCCEEDED, | ||
2268 | 1056 | "result-text": "Package locks successfully" | ||
2269 | 1057 | " changed.", | ||
2270 | 1058 | "result-code": 0}]) | ||
2271 | 1059 | |||
2272 | 1060 | result = self.changer.handle_tasks() | ||
2273 | 1061 | return result.addCallback(assert_result) | ||
2274 | 1062 | |||
2275 | 1063 | def test_dpkg_error(self): | ||
2276 | 1064 | """ | ||
2277 | 1065 | Verify that errors emitted by dpkg are correctly reported to | ||
2278 | 1066 | the server as problems. | ||
2279 | 1067 | |||
2280 | 1068 | This test is to make sure that Smart reports the problem | ||
2281 | 1069 | correctly. It doesn't make sense for AptFacade, since there we | ||
2282 | 1070 | don't call dpkg. | ||
2283 | 1071 | """ | ||
2284 | 1072 | self.log_helper.ignore_errors(".*dpkg") | ||
2285 | 1073 | |||
2286 | 1074 | installed_hash = self.set_pkg1_installed() | ||
2287 | 1075 | self.store.set_hash_ids({installed_hash: 1}) | ||
2288 | 1076 | self.store.add_task("changer", | ||
2289 | 1077 | {"type": "change-packages", "remove": [1], | ||
2290 | 1078 | "operation-id": 123}) | ||
2291 | 1079 | |||
2292 | 1080 | result = self.changer.handle_tasks() | ||
2293 | 1081 | |||
2294 | 1082 | def got_result(result): | ||
2295 | 1083 | messages = self.get_pending_messages() | ||
2296 | 1084 | self.assertEqual(len(messages), 1, "Too many messages") | ||
2297 | 1085 | message = messages[0] | ||
2298 | 1086 | self.assertEqual(message["operation-id"], 123) | ||
2299 | 1087 | self.assertEqual(message["result-code"], 100) | ||
2300 | 1088 | self.assertEqual(message["type"], "change-packages-result") | ||
2301 | 1089 | text = message["result-text"] | ||
2302 | 1090 | # We can't test the actual content of the message because the dpkg | ||
2303 | 1091 | # error can be localized | ||
2304 | 1092 | self.assertIn("\n[remove] name1_version1-release1\ndpkg: ", text) | ||
2305 | 1093 | self.assertIn("ERROR", text) | ||
2306 | 1094 | self.assertIn("(2)", text) | ||
2307 | 1095 | return result.addCallback(got_result) | ||
2308 | 1096 | |||
2309 | 1097 | def test_change_package_holds(self): | ||
2310 | 1098 | """ | ||
2311 | 1099 | If C{SmartFacade} is used, the L{PackageChanger.handle_tasks} | ||
2312 | 1100 | method fails the activity, since it can't add or remove dpkg holds. | ||
2313 | 1101 | """ | ||
2314 | 1102 | self.facade.reload_channels() | ||
2315 | 1103 | self.store.add_task("changer", {"type": "change-package-holds", | ||
2316 | 1104 | "create": [1], | ||
2317 | 1105 | "delete": [2], | ||
2318 | 1106 | "operation-id": 123}) | ||
2319 | 1107 | |||
2320 | 1108 | def assert_result(result): | ||
2321 | 1109 | self.assertIn("Queuing message with change package holds results " | ||
2322 | 1110 | "to exchange urgently.", self.logfile.getvalue()) | ||
2323 | 1111 | self.assertMessages( | ||
2324 | 1112 | self.get_pending_messages(), | ||
2325 | 1113 | [{"type": "operation-result", | ||
2326 | 1114 | "operation-id": 123, | ||
2327 | 1115 | "status": FAILED, | ||
2328 | 1116 | "result-text": "This client doesn't support package holds.", | ||
2329 | 1117 | "result-code": 1}]) | ||
2330 | 1118 | |||
2331 | 1119 | result = self.changer.handle_tasks() | ||
2332 | 1120 | return result.addCallback(assert_result) | ||
2333 | 1121 | |||
2334 | 1122 | def test_global_upgrade(self): | ||
2335 | 1123 | """ | ||
2336 | 1124 | Besides asking for individual changes, the server may also request | ||
2337 | 1125 | the client to perform a global upgrade. This would be the equivalent | ||
2338 | 1126 | of a "smart upgrade" command being executed in the command line. | ||
2339 | 1127 | |||
2340 | 1128 | This test should be run for both C{AptFacade} and | ||
2341 | 1129 | C{SmartFacade}, but due to the smart test setting up that two | ||
2342 | 1130 | packages with different names upgrade each other, the message | ||
2343 | 1131 | doesn't correctly report that the old version should be | ||
2344 | 1132 | uninstalled. The test is still useful, since it shows that the | ||
2345 | 1133 | message contains the changes that smart says are needed. | ||
2346 | 1134 | |||
2347 | 1135 | Making the test totally correct is a lot of work, that is not | ||
2348 | 1136 | worth doing, since we're removing smart soon. | ||
2349 | 1137 | """ | ||
2350 | 1138 | hash1, hash2 = self.set_pkg2_upgrades_pkg1() | ||
2351 | 1139 | self.store.set_hash_ids({hash1: 1, hash2: 2}) | ||
2352 | 1140 | |||
2353 | 1141 | self.store.add_task("changer", | ||
2354 | 1142 | {"type": "change-packages", "upgrade-all": True, | ||
2355 | 1143 | "operation-id": 123}) | ||
2356 | 1144 | |||
2357 | 1145 | result = self.changer.handle_tasks() | ||
2358 | 1146 | |||
2359 | 1147 | def got_result(result): | ||
2360 | 1148 | self.assertMessages(self.get_pending_messages(), | ||
2361 | 1149 | [{"operation-id": 123, | ||
2362 | 1150 | "must-install": [2], | ||
2363 | 1151 | "result-code": 101, | ||
2364 | 1152 | "type": "change-packages-result"}]) | ||
2365 | 1153 | |||
2366 | 1154 | return result.addCallback(got_result) | ||
2367 | 1155 | |||
2368 | 1156 | |||
2369 | 1157 | class AptPackageChangerTest(LandscapeTest, PackageChangerTestMixin): | ||
2370 | 1158 | |||
2371 | 1159 | if not has_new_enough_apt: | ||
2372 | 1160 | skip = "Can't use AptFacade on hardy" | ||
2373 | 1161 | |||
2374 | 1162 | helpers = [AptFacadeHelper, SimpleRepositoryHelper, BrokerServiceHelper] | ||
2375 | 1163 | |||
2376 | 1164 | def setUp(self): | ||
2377 | 1165 | |||
2378 | 1166 | def set_up(ignored): | ||
2379 | 1167 | |||
2380 | 1168 | self.store = PackageStore(self.makeFile()) | ||
2381 | 1169 | self.config = PackageChangerConfiguration() | ||
2382 | 1170 | self.config.data_path = self.makeDir() | ||
2383 | 1171 | os.mkdir(self.config.package_directory) | ||
2384 | 1172 | os.mkdir(self.config.binaries_path) | ||
2385 | 1173 | touch_file(self.config.update_stamp_filename) | ||
2386 | 1174 | self.changer = PackageChanger( | ||
2387 | 1175 | self.store, self.facade, self.remote, self.config) | ||
2388 | 1176 | service = self.broker_service | ||
2389 | 1177 | service.message_store.set_accepted_types(["change-packages-result", | ||
2390 | 1178 | "operation-result"]) | ||
2391 | 1179 | |||
2392 | 1180 | result = super(AptPackageChangerTest, self).setUp() | ||
2393 | 1181 | return result.addCallback(set_up) | ||
2394 | 1182 | |||
2395 | 1183 | def set_pkg1_installed(self): | ||
2396 | 1184 | """Return the hash of a package that is installed.""" | ||
2397 | 1185 | self._add_system_package("foo") | ||
2398 | 1186 | self.facade.reload_channels() | ||
2399 | 1187 | [foo] = self.facade.get_packages_by_name("foo") | ||
2400 | 1188 | return self.facade.get_package_hash(foo) | ||
2401 | 1189 | |||
2402 | 1190 | def set_pkg2_satisfied(self): | ||
2403 | 1191 | """Return the hash of a package that can be installed.""" | ||
2404 | 1192 | self._add_package_to_deb_dir(self.repository_dir, "bar") | ||
2405 | 1193 | self.facade.reload_channels() | ||
2406 | 1194 | [bar] = self.facade.get_packages_by_name("bar") | ||
2407 | 1195 | return self.facade.get_package_hash(bar) | ||
2408 | 1196 | |||
2409 | 1197 | def set_pkg1_and_pkg2_satisfied(self): | ||
2410 | 1198 | """Make a package depend on another package. | ||
2411 | 1199 | |||
2412 | 1200 | Return the hashes of the two packages. | ||
2413 | 1201 | """ | ||
2414 | 1202 | self._add_package_to_deb_dir( | ||
2415 | 1203 | self.repository_dir, "foo", control_fields={"Depends": "bar"}) | ||
2416 | 1204 | self._add_package_to_deb_dir(self.repository_dir, "bar") | ||
2417 | 1205 | self.facade.reload_channels() | ||
2418 | 1206 | [foo] = self.facade.get_packages_by_name("foo") | ||
2419 | 1207 | [bar] = self.facade.get_packages_by_name("bar") | ||
2420 | 1208 | return ( | ||
2421 | 1209 | self.facade.get_package_hash(foo), | ||
2422 | 1210 | self.facade.get_package_hash(bar)) | ||
2423 | 1211 | |||
2424 | 1212 | def set_pkg2_upgrades_pkg1(self): | ||
2425 | 1213 | """Make it so that one package upgrades another. | ||
2426 | 1214 | |||
2427 | 1215 | Return the hashes of the two packages. | ||
2428 | 1216 | """ | ||
2429 | 1217 | self._add_system_package("foo", version="1.0") | ||
2430 | 1218 | self._add_package_to_deb_dir(self.repository_dir, "foo", version="2.0") | ||
2431 | 1219 | self.facade.reload_channels() | ||
2432 | 1220 | foo_1, foo_2 = sorted(self.facade.get_packages_by_name("foo")) | ||
2433 | 1221 | return ( | ||
2434 | 1222 | self.facade.get_package_hash(foo_1), | ||
2435 | 1223 | self.facade.get_package_hash(foo_2)) | ||
2436 | 1224 | |||
2437 | 1225 | def remove_pkg2(self): | ||
2438 | 1226 | """Remove package name2 from its repository.""" | ||
2439 | 1227 | packages_file = os.path.join(self.repository_dir, "Packages") | ||
2440 | 1228 | packages_contents = read_file(packages_file) | ||
2441 | 1229 | packages_contents = "\n\n".join( | ||
2442 | 1230 | [stanza for stanza in packages_contents.split("\n\n") | ||
2443 | 1231 | if "Package: name2" not in stanza]) | ||
2444 | 1232 | create_file(packages_file, packages_contents) | ||
2445 | 1233 | |||
2446 | 1234 | def get_transaction_error_message(self): | ||
2447 | 1235 | """Return part of the apt transaction error message.""" | ||
2448 | 1236 | return "Unable to correct problems" | ||
2449 | 1237 | |||
2450 | 1238 | def get_binaries_channels(self, binaries_path): | ||
2451 | 1239 | """Return the channels that will be used for the binaries.""" | ||
2452 | 1240 | return [{"baseurl": "file://%s" % binaries_path, | ||
2453 | 1241 | "components": "", | ||
2454 | 1242 | "distribution": "./", | ||
2455 | 1243 | "type": "deb"}] | ||
2456 | 1244 | |||
2457 | 1245 | def get_package_name(self, version): | ||
2458 | 1246 | """Return the name of the package.""" | ||
2459 | 1247 | return version.package.name | ||
2460 | 1248 | |||
2461 | 1249 | def test_binaries_available_in_cache(self): | 950 | def test_binaries_available_in_cache(self): |
2462 | 1250 | """ | 951 | """ |
2463 | 1251 | If binaries are included in the changes-packages message, those | 952 | If binaries are included in the changes-packages message, those |
2464 | @@ -1275,7 +976,7 @@ | |||
2465 | 1275 | def test_change_package_holds(self): | 976 | def test_change_package_holds(self): |
2466 | 1276 | """ | 977 | """ |
2467 | 1277 | The L{PackageChanger.handle_tasks} method appropriately creates and | 978 | The L{PackageChanger.handle_tasks} method appropriately creates and |
2469 | 1278 | deletes package holds as requested by the C{change-package-holds} | 979 | deletes package holds as requested by the C{change-packages} |
2470 | 1279 | message. | 980 | message. |
2471 | 1280 | """ | 981 | """ |
2472 | 1281 | self._add_system_package("foo") | 982 | self._add_system_package("foo") |
2473 | @@ -1292,23 +993,22 @@ | |||
2474 | 1292 | old_mtime = time.time() - 10 | 993 | old_mtime = time.time() - 10 |
2475 | 1293 | os.utime(self.facade._dpkg_status, (old_mtime, old_mtime)) | 994 | os.utime(self.facade._dpkg_status, (old_mtime, old_mtime)) |
2476 | 1294 | self.facade.reload_channels() | 995 | self.facade.reload_channels() |
2480 | 1295 | self.store.add_task("changer", {"type": "change-package-holds", | 996 | self.store.add_task("changer", {"type": "change-packages", |
2481 | 1296 | "create": [foo.package.id], | 997 | "hold": [foo.package.id], |
2482 | 1297 | "delete": [bar.package.id], | 998 | "remove-hold": [bar.package.id], |
2483 | 1298 | "operation-id": 123}) | 999 | "operation-id": 123}) |
2484 | 1299 | 1000 | ||
2485 | 1300 | def assert_result(result): | 1001 | def assert_result(result): |
2486 | 1301 | self.facade.reload_channels() | 1002 | self.facade.reload_channels() |
2487 | 1302 | self.assertEqual(["foo"], self.facade.get_package_holds()) | 1003 | self.assertEqual(["foo"], self.facade.get_package_holds()) |
2489 | 1303 | self.assertIn("Queuing message with change package holds results " | 1004 | self.assertIn("Queuing response with change package results " |
2490 | 1304 | "to exchange urgently.", self.logfile.getvalue()) | 1005 | "to exchange urgently.", self.logfile.getvalue()) |
2491 | 1305 | self.assertMessages( | 1006 | self.assertMessages( |
2492 | 1306 | self.get_pending_messages(), | 1007 | self.get_pending_messages(), |
2494 | 1307 | [{"type": "operation-result", | 1008 | [{"type": "change-packages-result", |
2495 | 1308 | "operation-id": 123, | 1009 | "operation-id": 123, |
2496 | 1309 | "status": SUCCEEDED, | ||
2497 | 1310 | "result-text": "Package holds successfully changed.", | 1010 | "result-text": "Package holds successfully changed.", |
2499 | 1311 | "result-code": 0}]) | 1011 | "result-code": 1}]) |
2500 | 1312 | 1012 | ||
2501 | 1313 | result = self.changer.handle_tasks() | 1013 | result = self.changer.handle_tasks() |
2502 | 1314 | return result.addCallback(assert_result) | 1014 | return result.addCallback(assert_result) |
2503 | @@ -1316,7 +1016,7 @@ | |||
2504 | 1316 | def test_create_package_holds_with_identical_version(self): | 1016 | def test_create_package_holds_with_identical_version(self): |
2505 | 1317 | """ | 1017 | """ |
2506 | 1318 | The L{PackageChanger.handle_tasks} method appropriately creates | 1018 | The L{PackageChanger.handle_tasks} method appropriately creates |
2508 | 1319 | holds as requested by the C{change-package-holds} message even | 1019 | holds as requested by the C{change-packages} message even |
2509 | 1320 | when versions from two different packages are the same. | 1020 | when versions from two different packages are the same. |
2510 | 1321 | """ | 1021 | """ |
2511 | 1322 | self._add_system_package("foo", version="1.1") | 1022 | self._add_system_package("foo", version="1.1") |
2512 | @@ -1327,12 +1027,13 @@ | |||
2513 | 1327 | [foo] = self.facade.get_packages_by_name("foo") | 1027 | [foo] = self.facade.get_packages_by_name("foo") |
2514 | 1328 | [bar] = self.facade.get_packages_by_name("bar") | 1028 | [bar] = self.facade.get_packages_by_name("bar") |
2515 | 1329 | self.facade.reload_channels() | 1029 | self.facade.reload_channels() |
2519 | 1330 | self.store.add_task("changer", {"type": "change-package-holds", | 1030 | self.store.add_task("changer", {"type": "change-packages", |
2520 | 1331 | "create": [foo.package.id, | 1031 | "hold": [foo.package.id, |
2521 | 1332 | bar.package.id], | 1032 | bar.package.id], |
2522 | 1333 | "operation-id": 123}) | 1033 | "operation-id": 123}) |
2523 | 1334 | 1034 | ||
2524 | 1335 | def assert_result(result): | 1035 | def assert_result(result): |
2525 | 1036 | self.facade.reload_channels() | ||
2526 | 1336 | self.assertEqual(["foo", "bar"], self.facade.get_package_holds()) | 1037 | self.assertEqual(["foo", "bar"], self.facade.get_package_holds()) |
2527 | 1337 | 1038 | ||
2528 | 1338 | result = self.changer.handle_tasks() | 1039 | result = self.changer.handle_tasks() |
2529 | @@ -1341,7 +1042,7 @@ | |||
2530 | 1341 | def test_delete_package_holds_with_identical_version(self): | 1042 | def test_delete_package_holds_with_identical_version(self): |
2531 | 1342 | """ | 1043 | """ |
2532 | 1343 | The L{PackageChanger.handle_tasks} method appropriately deletes | 1044 | The L{PackageChanger.handle_tasks} method appropriately deletes |
2534 | 1344 | holds as requested by the C{change-package-holds} message even | 1045 | holds as requested by the C{change-packages} message even |
2535 | 1345 | when versions from two different packages are the same. | 1046 | when versions from two different packages are the same. |
2536 | 1346 | """ | 1047 | """ |
2537 | 1347 | self._add_system_package("foo", version="1.1") | 1048 | self._add_system_package("foo", version="1.1") |
2538 | @@ -1354,12 +1055,13 @@ | |||
2539 | 1354 | self.facade.set_package_hold(foo) | 1055 | self.facade.set_package_hold(foo) |
2540 | 1355 | self.facade.set_package_hold(bar) | 1056 | self.facade.set_package_hold(bar) |
2541 | 1356 | self.facade.reload_channels() | 1057 | self.facade.reload_channels() |
2545 | 1357 | self.store.add_task("changer", {"type": "change-package-holds", | 1058 | self.store.add_task("changer", {"type": "change-packages", |
2546 | 1358 | "delete": [foo.package.id, | 1059 | "remove-hold": [foo.package.id, |
2547 | 1359 | bar.package.id], | 1060 | bar.package.id], |
2548 | 1360 | "operation-id": 123}) | 1061 | "operation-id": 123}) |
2549 | 1361 | 1062 | ||
2550 | 1362 | def assert_result(result): | 1063 | def assert_result(result): |
2551 | 1064 | self.facade.reload_channels() | ||
2552 | 1363 | self.assertEqual([], self.facade.get_package_holds()) | 1065 | self.assertEqual([], self.facade.get_package_holds()) |
2553 | 1364 | 1066 | ||
2554 | 1365 | result = self.changer.handle_tasks() | 1067 | result = self.changer.handle_tasks() |
2555 | @@ -1367,7 +1069,7 @@ | |||
2556 | 1367 | 1069 | ||
2557 | 1368 | def test_change_package_holds_create_already_held(self): | 1070 | def test_change_package_holds_create_already_held(self): |
2558 | 1369 | """ | 1071 | """ |
2560 | 1370 | If the C{change-package-holds} message requests to add holds for | 1072 | If the C{change-packages} message requests to add holds for |
2561 | 1371 | packages that are already held, the activity succeeds, since the | 1073 | packages that are already held, the activity succeeds, since the |
2562 | 1372 | end result is that the requested package holds are there. | 1074 | end result is that the requested package holds are there. |
2563 | 1373 | """ | 1075 | """ |
2564 | @@ -1377,29 +1079,28 @@ | |||
2565 | 1377 | [foo] = self.facade.get_packages_by_name("foo") | 1079 | [foo] = self.facade.get_packages_by_name("foo") |
2566 | 1378 | self.facade.set_package_hold(foo) | 1080 | self.facade.set_package_hold(foo) |
2567 | 1379 | self.facade.reload_channels() | 1081 | self.facade.reload_channels() |
2570 | 1380 | self.store.add_task("changer", {"type": "change-package-holds", | 1082 | self.store.add_task("changer", {"type": "change-packages", |
2571 | 1381 | "create": [foo.package.id], | 1083 | "hold": [foo.package.id], |
2572 | 1382 | "operation-id": 123}) | 1084 | "operation-id": 123}) |
2573 | 1383 | 1085 | ||
2574 | 1384 | def assert_result(result): | 1086 | def assert_result(result): |
2575 | 1385 | self.facade.reload_channels() | 1087 | self.facade.reload_channels() |
2576 | 1386 | self.assertEqual(["foo"], self.facade.get_package_holds()) | 1088 | self.assertEqual(["foo"], self.facade.get_package_holds()) |
2578 | 1387 | self.assertIn("Queuing message with change package holds results " | 1089 | self.assertIn("Queuing response with change package results " |
2579 | 1388 | "to exchange urgently.", self.logfile.getvalue()) | 1090 | "to exchange urgently.", self.logfile.getvalue()) |
2580 | 1389 | self.assertMessages( | 1091 | self.assertMessages( |
2581 | 1390 | self.get_pending_messages(), | 1092 | self.get_pending_messages(), |
2583 | 1391 | [{"type": "operation-result", | 1093 | [{"type": "change-packages-result", |
2584 | 1392 | "operation-id": 123, | 1094 | "operation-id": 123, |
2585 | 1393 | "status": SUCCEEDED, | ||
2586 | 1394 | "result-text": "Package holds successfully changed.", | 1095 | "result-text": "Package holds successfully changed.", |
2588 | 1395 | "result-code": 0}]) | 1096 | "result-code": 1}]) |
2589 | 1396 | 1097 | ||
2590 | 1397 | result = self.changer.handle_tasks() | 1098 | result = self.changer.handle_tasks() |
2591 | 1398 | return result.addCallback(assert_result) | 1099 | return result.addCallback(assert_result) |
2592 | 1399 | 1100 | ||
2593 | 1400 | def test_change_package_holds_create_other_version_installed(self): | 1101 | def test_change_package_holds_create_other_version_installed(self): |
2594 | 1401 | """ | 1102 | """ |
2596 | 1402 | If the C{change-package-holds} message requests to add holds for | 1103 | If the C{change-packages} message requests to add holds for |
2597 | 1403 | packages that have a different version installed than the one | 1104 | packages that have a different version installed than the one |
2598 | 1404 | being requested to hold, the activity fails. | 1105 | being requested to hold, the activity fails. |
2599 | 1405 | 1106 | ||
2600 | @@ -1420,30 +1121,29 @@ | |||
2601 | 1420 | self.facade.get_package_hash(bar1): 3, | 1121 | self.facade.get_package_hash(bar1): 3, |
2602 | 1421 | self.facade.get_package_hash(bar2): 4}) | 1122 | self.facade.get_package_hash(bar2): 4}) |
2603 | 1422 | self.facade.reload_channels() | 1123 | self.facade.reload_channels() |
2606 | 1423 | self.store.add_task("changer", {"type": "change-package-holds", | 1124 | self.store.add_task("changer", {"type": "change-packages", |
2607 | 1424 | "create": [2, 3], | 1125 | "hold": [2, 3], |
2608 | 1425 | "operation-id": 123}) | 1126 | "operation-id": 123}) |
2609 | 1426 | 1127 | ||
2610 | 1427 | def assert_result(result): | 1128 | def assert_result(result): |
2611 | 1428 | self.facade.reload_channels() | 1129 | self.facade.reload_channels() |
2612 | 1429 | self.assertEqual([], self.facade.get_package_holds()) | 1130 | self.assertEqual([], self.facade.get_package_holds()) |
2614 | 1430 | self.assertIn("Queuing message with change package holds results " | 1131 | self.assertIn("Queuing response with change package results " |
2615 | 1431 | "to exchange urgently.", self.logfile.getvalue()) | 1132 | "to exchange urgently.", self.logfile.getvalue()) |
2616 | 1432 | self.assertMessages( | 1133 | self.assertMessages( |
2617 | 1433 | self.get_pending_messages(), | 1134 | self.get_pending_messages(), |
2619 | 1434 | [{"type": "operation-result", | 1135 | [{"type": "change-packages-result", |
2620 | 1435 | "operation-id": 123, | 1136 | "operation-id": 123, |
2625 | 1436 | "status": FAILED, | 1137 | "result-text": "Cannot perform the changes, since the" + |
2626 | 1437 | "result-text": "Package holds not changed, since the" + | 1138 | " following packages are not installed: foo", |
2627 | 1438 | " following packages are not installed: 2", | 1139 | "result-code": 100}]) |
2624 | 1439 | "result-code": 1}]) | ||
2628 | 1440 | 1140 | ||
2629 | 1441 | result = self.changer.handle_tasks() | 1141 | result = self.changer.handle_tasks() |
2630 | 1442 | return result.addCallback(assert_result) | 1142 | return result.addCallback(assert_result) |
2631 | 1443 | 1143 | ||
2632 | 1444 | def test_change_package_holds_create_not_installed(self): | 1144 | def test_change_package_holds_create_not_installed(self): |
2633 | 1445 | """ | 1145 | """ |
2635 | 1446 | If the C{change-package-holds} message requests to add holds for | 1146 | If the C{change-packages} message requests to add holds for |
2636 | 1447 | packages that aren't installed, the whole activity is failed. If | 1147 | packages that aren't installed, the whole activity is failed. If |
2637 | 1448 | multiple holds are specified, those won't be added. There's no | 1148 | multiple holds are specified, those won't be added. There's no |
2638 | 1449 | difference between a package that is available in some | 1149 | difference between a package that is available in some |
2639 | @@ -1460,62 +1160,71 @@ | |||
2640 | 1460 | [foo] = self.facade.get_packages_by_name("foo") | 1160 | [foo] = self.facade.get_packages_by_name("foo") |
2641 | 1461 | [bar] = self.facade.get_packages_by_name("bar") | 1161 | [bar] = self.facade.get_packages_by_name("bar") |
2642 | 1462 | [baz] = self.facade.get_packages_by_name("baz") | 1162 | [baz] = self.facade.get_packages_by_name("baz") |
2647 | 1463 | self.store.add_task("changer", {"type": "change-package-holds", | 1163 | self.store.add_task("changer", {"type": "change-packages", |
2648 | 1464 | "create": [foo.package.id, | 1164 | "hold": [foo.package.id, |
2649 | 1465 | bar.package.id, | 1165 | bar.package.id, |
2650 | 1466 | baz.package.id], | 1166 | baz.package.id], |
2651 | 1467 | "operation-id": 123}) | 1167 | "operation-id": 123}) |
2652 | 1468 | 1168 | ||
2653 | 1469 | def assert_result(result): | 1169 | def assert_result(result): |
2654 | 1470 | self.facade.reload_channels() | 1170 | self.facade.reload_channels() |
2655 | 1471 | self.assertEqual([], self.facade.get_package_holds()) | 1171 | self.assertEqual([], self.facade.get_package_holds()) |
2657 | 1472 | self.assertIn("Queuing message with change package holds results " | 1172 | self.assertIn("Queuing response with change package results " |
2658 | 1473 | "to exchange urgently.", self.logfile.getvalue()) | 1173 | "to exchange urgently.", self.logfile.getvalue()) |
2659 | 1474 | self.assertMessages( | 1174 | self.assertMessages( |
2660 | 1475 | self.get_pending_messages(), | 1175 | self.get_pending_messages(), |
2662 | 1476 | [{"type": "operation-result", | 1176 | [{"type": "change-packages-result", |
2663 | 1477 | "operation-id": 123, | 1177 | "operation-id": 123, |
2666 | 1478 | "status": FAILED, | 1178 | "result-text": "Cannot perform the changes, since the " |
2665 | 1479 | "result-text": "Package holds not changed, since the " | ||
2667 | 1480 | "following packages are not installed: " | 1179 | "following packages are not installed: " |
2671 | 1481 | "%s, %s" % tuple(sorted([bar.package.id, | 1180 | "%s, %s" % tuple(sorted([bar.package.name, |
2672 | 1482 | baz.package.id])), | 1181 | baz.package.name])), |
2673 | 1483 | "result-code": 1}]) | 1182 | "result-code": 100}]) |
2674 | 1484 | 1183 | ||
2675 | 1485 | result = self.changer.handle_tasks() | 1184 | result = self.changer.handle_tasks() |
2676 | 1486 | return result.addCallback(assert_result) | 1185 | return result.addCallback(assert_result) |
2677 | 1487 | 1186 | ||
2678 | 1488 | def test_change_package_holds_create_unknown_hash(self): | 1187 | def test_change_package_holds_create_unknown_hash(self): |
2679 | 1489 | """ | 1188 | """ |
2683 | 1490 | If the C{change-package-holds} message requests to add holds for | 1189 | If the C{change-packages} message requests to add holds for |
2684 | 1491 | packages that the client doesn't know about, it's being treated | 1190 | packages that the client doesn't know about results in a not yet |
2685 | 1492 | as the packages not being installed. | 1191 | synchronized message and a failure of the operation. |
2686 | 1493 | """ | 1192 | """ |
2708 | 1494 | self.facade.reload_channels() | 1193 | |
2709 | 1495 | self.store.add_task("changer", {"type": "change-package-holds", | 1194 | self.store.add_task("changer", |
2710 | 1496 | "create": [1], | 1195 | {"type": "change-packages", |
2711 | 1497 | "operation-id": 123}) | 1196 | "hold": [123], |
2712 | 1498 | 1197 | "operation-id": 123}) | |
2713 | 1499 | def assert_result(result): | 1198 | |
2714 | 1500 | self.facade.reload_channels() | 1199 | time_mock = self.mocker.replace("time.time") |
2715 | 1501 | self.assertEqual([], self.facade.get_package_holds()) | 1200 | time_mock() |
2716 | 1502 | self.assertIn("Queuing message with change package holds results " | 1201 | self.mocker.result(time.time() + UNKNOWN_PACKAGE_DATA_TIMEOUT) |
2717 | 1503 | "to exchange urgently.", self.logfile.getvalue()) | 1202 | self.mocker.count(1, None) |
2718 | 1504 | self.assertMessages( | 1203 | self.mocker.replay() |
2719 | 1505 | self.get_pending_messages(), | 1204 | |
2720 | 1506 | [{"type": "operation-result", | 1205 | try: |
2721 | 1507 | "operation-id": 123, | 1206 | result = self.changer.handle_tasks() |
2722 | 1508 | "status": FAILED, | 1207 | self.mocker.verify() |
2723 | 1509 | "result-text": "Package holds not changed, since the" + | 1208 | finally: |
2724 | 1510 | " following packages are not installed: 1", | 1209 | # Reset it earlier so that Twisted has the true time function. |
2725 | 1511 | "result-code": 1}]) | 1210 | self.mocker.reset() |
2726 | 1512 | 1211 | ||
2727 | 1513 | result = self.changer.handle_tasks() | 1212 | self.assertIn("Package data not yet synchronized with server (123)", |
2728 | 1514 | return result.addCallback(assert_result) | 1213 | self.logfile.getvalue()) |
2729 | 1214 | |||
2730 | 1215 | def got_result(result): | ||
2731 | 1216 | message = {"type": "change-packages-result", | ||
2732 | 1217 | "operation-id": 123, | ||
2733 | 1218 | "result-code": 100, | ||
2734 | 1219 | "result-text": "Package data has changed. " | ||
2735 | 1220 | "Please retry the operation."} | ||
2736 | 1221 | self.assertMessages(self.get_pending_messages(), [message]) | ||
2737 | 1222 | self.assertEqual(self.store.get_next_task("changer"), None) | ||
2738 | 1223 | return result.addCallback(got_result) | ||
2739 | 1515 | 1224 | ||
2740 | 1516 | def test_change_package_holds_delete_not_held(self): | 1225 | def test_change_package_holds_delete_not_held(self): |
2741 | 1517 | """ | 1226 | """ |
2743 | 1518 | If the C{change-package-holds} message requests to remove holds | 1227 | If the C{change-packages} message requests to remove holds |
2744 | 1519 | for packages that aren't held, the activity succeeds if the | 1228 | for packages that aren't held, the activity succeeds if the |
2745 | 1520 | right version is installed, since the end result is that the | 1229 | right version is installed, since the end result is that the |
2746 | 1521 | hold is removed. | 1230 | hold is removed. |
2747 | @@ -1524,29 +1233,28 @@ | |||
2748 | 1524 | self.facade.reload_channels() | 1233 | self.facade.reload_channels() |
2749 | 1525 | self._hash_packages_by_name(self.facade, self.store, "foo") | 1234 | self._hash_packages_by_name(self.facade, self.store, "foo") |
2750 | 1526 | [foo] = self.facade.get_packages_by_name("foo") | 1235 | [foo] = self.facade.get_packages_by_name("foo") |
2753 | 1527 | self.store.add_task("changer", {"type": "change-package-holds", | 1236 | self.store.add_task("changer", {"type": "change-packages", |
2754 | 1528 | "delete": [foo.package.id], | 1237 | "remove-hold": [foo.package.id], |
2755 | 1529 | "operation-id": 123}) | 1238 | "operation-id": 123}) |
2756 | 1530 | 1239 | ||
2757 | 1531 | def assert_result(result): | 1240 | def assert_result(result): |
2758 | 1532 | self.facade.reload_channels() | 1241 | self.facade.reload_channels() |
2759 | 1533 | self.assertEqual([], self.facade.get_package_holds()) | 1242 | self.assertEqual([], self.facade.get_package_holds()) |
2761 | 1534 | self.assertIn("Queuing message with change package holds results " | 1243 | self.assertIn("Queuing response with change package results " |
2762 | 1535 | "to exchange urgently.", self.logfile.getvalue()) | 1244 | "to exchange urgently.", self.logfile.getvalue()) |
2763 | 1536 | self.assertMessages( | 1245 | self.assertMessages( |
2764 | 1537 | self.get_pending_messages(), | 1246 | self.get_pending_messages(), |
2766 | 1538 | [{"type": "operation-result", | 1247 | [{"type": "change-packages-result", |
2767 | 1539 | "operation-id": 123, | 1248 | "operation-id": 123, |
2768 | 1540 | "status": SUCCEEDED, | ||
2769 | 1541 | "result-text": "Package holds successfully changed.", | 1249 | "result-text": "Package holds successfully changed.", |
2771 | 1542 | "result-code": 0}]) | 1250 | "result-code": 1}]) |
2772 | 1543 | 1251 | ||
2773 | 1544 | result = self.changer.handle_tasks() | 1252 | result = self.changer.handle_tasks() |
2774 | 1545 | return result.addCallback(assert_result) | 1253 | return result.addCallback(assert_result) |
2775 | 1546 | 1254 | ||
2776 | 1547 | def test_change_package_holds_delete_different_version_held(self): | 1255 | def test_change_package_holds_delete_different_version_held(self): |
2777 | 1548 | """ | 1256 | """ |
2779 | 1549 | If the C{change-package-holds} message requests to remove holds | 1257 | If the C{change-packages} message requests to remove holds |
2780 | 1550 | for packages that aren't held, the activity succeeds if the | 1258 | for packages that aren't held, the activity succeeds if the |
2781 | 1551 | right version is installed, since the end result is that the | 1259 | right version is installed, since the end result is that the |
2782 | 1552 | hold is removed. | 1260 | hold is removed. |
2783 | @@ -1556,60 +1264,34 @@ | |||
2784 | 1556 | self.repository_dir, "foo", version="2.0") | 1264 | self.repository_dir, "foo", version="2.0") |
2785 | 1557 | self.facade.reload_channels() | 1265 | self.facade.reload_channels() |
2786 | 1558 | [foo1, foo2] = sorted(self.facade.get_packages_by_name("foo")) | 1266 | [foo1, foo2] = sorted(self.facade.get_packages_by_name("foo")) |
2787 | 1559 | self.facade.set_package_hold(foo1) | ||
2788 | 1560 | self.store.set_hash_ids({self.facade.get_package_hash(foo1): 1, | 1267 | self.store.set_hash_ids({self.facade.get_package_hash(foo1): 1, |
2789 | 1561 | self.facade.get_package_hash(foo2): 2}) | 1268 | self.facade.get_package_hash(foo2): 2}) |
2790 | 1269 | self.facade.mark_install(foo1) | ||
2791 | 1270 | self.facade.mark_hold(foo1) | ||
2792 | 1271 | self.facade.perform_changes() | ||
2793 | 1562 | self.facade.reload_channels() | 1272 | self.facade.reload_channels() |
2796 | 1563 | self.store.add_task("changer", {"type": "change-package-holds", | 1273 | self.store.add_task("changer", {"type": "change-packages", |
2797 | 1564 | "delete": [2], | 1274 | "remove-hold": [2], |
2798 | 1565 | "operation-id": 123}) | 1275 | "operation-id": 123}) |
2799 | 1566 | 1276 | ||
2800 | 1567 | def assert_result(result): | 1277 | def assert_result(result): |
2801 | 1568 | self.facade.reload_channels() | 1278 | self.facade.reload_channels() |
2802 | 1569 | self.assertEqual(["foo"], self.facade.get_package_holds()) | 1279 | self.assertEqual(["foo"], self.facade.get_package_holds()) |
2839 | 1570 | self.assertIn("Queuing message with change package holds results " | 1280 | self.assertIn("Queuing response with change package results " |
2840 | 1571 | "to exchange urgently.", self.logfile.getvalue()) | 1281 | "to exchange urgently.", self.logfile.getvalue()) |
2841 | 1572 | self.assertMessages( | 1282 | self.assertMessages( |
2842 | 1573 | self.get_pending_messages(), | 1283 | self.get_pending_messages(), |
2843 | 1574 | [{"type": "operation-result", | 1284 | [{"type": "change-packages-result", |
2844 | 1575 | "operation-id": 123, | 1285 | "operation-id": 123, |
2845 | 1576 | "status": SUCCEEDED, | 1286 | "result-text": "Package holds successfully changed.", |
2846 | 1577 | "result-text": "Package holds successfully changed.", | 1287 | "result-code": 1}]) |
2811 | 1578 | "result-code": 0}]) | ||
2812 | 1579 | |||
2813 | 1580 | result = self.changer.handle_tasks() | ||
2814 | 1581 | return result.addCallback(assert_result) | ||
2815 | 1582 | |||
2816 | 1583 | def test_change_package_holds_delete_unknown_hash(self): | ||
2817 | 1584 | """ | ||
2818 | 1585 | If the C{change-package-holds} message requests to remove holds | ||
2819 | 1586 | for packages that aren't known by the client, the activity | ||
2820 | 1587 | succeeds, since the end result is that the package isn't | ||
2821 | 1588 | held at that version. | ||
2822 | 1589 | """ | ||
2823 | 1590 | self.store.add_task("changer", {"type": "change-package-holds", | ||
2824 | 1591 | "delete": [1], | ||
2825 | 1592 | "operation-id": 123}) | ||
2826 | 1593 | |||
2827 | 1594 | def assert_result(result): | ||
2828 | 1595 | self.facade.reload_channels() | ||
2829 | 1596 | self.assertEqual([], self.facade.get_package_holds()) | ||
2830 | 1597 | self.assertIn("Queuing message with change package holds results " | ||
2831 | 1598 | "to exchange urgently.", self.logfile.getvalue()) | ||
2832 | 1599 | self.assertMessages( | ||
2833 | 1600 | self.get_pending_messages(), | ||
2834 | 1601 | [{"type": "operation-result", | ||
2835 | 1602 | "operation-id": 123, | ||
2836 | 1603 | "status": SUCCEEDED, | ||
2837 | 1604 | "result-text": "Package holds successfully changed.", | ||
2838 | 1605 | "result-code": 0}]) | ||
2847 | 1606 | 1288 | ||
2848 | 1607 | result = self.changer.handle_tasks() | 1289 | result = self.changer.handle_tasks() |
2849 | 1608 | return result.addCallback(assert_result) | 1290 | return result.addCallback(assert_result) |
2850 | 1609 | 1291 | ||
2851 | 1610 | def test_change_package_holds_delete_not_installed(self): | 1292 | def test_change_package_holds_delete_not_installed(self): |
2852 | 1611 | """ | 1293 | """ |
2854 | 1612 | If the C{change-package-holds} message requests to remove holds | 1294 | If the C{change-packages} message requests to remove holds |
2855 | 1613 | for packages that aren't installed, the activity succeeds, since | 1295 | for packages that aren't installed, the activity succeeds, since |
2856 | 1614 | the end result is still that the package isn't held at the | 1296 | the end result is still that the package isn't held at the |
2857 | 1615 | requested version. | 1297 | requested version. |
2858 | @@ -1618,31 +1300,30 @@ | |||
2859 | 1618 | self.facade.reload_channels() | 1300 | self.facade.reload_channels() |
2860 | 1619 | self._hash_packages_by_name(self.facade, self.store, "foo") | 1301 | self._hash_packages_by_name(self.facade, self.store, "foo") |
2861 | 1620 | [foo] = self.facade.get_packages_by_name("foo") | 1302 | [foo] = self.facade.get_packages_by_name("foo") |
2864 | 1621 | self.store.add_task("changer", {"type": "change-package-holds", | 1303 | self.store.add_task("changer", {"type": "change-packages", |
2865 | 1622 | "delete": [foo.package.id], | 1304 | "remove-hold": [foo.package.id], |
2866 | 1623 | "operation-id": 123}) | 1305 | "operation-id": 123}) |
2867 | 1624 | 1306 | ||
2868 | 1625 | def assert_result(result): | 1307 | def assert_result(result): |
2869 | 1626 | self.facade.reload_channels() | 1308 | self.facade.reload_channels() |
2870 | 1627 | self.assertEqual([], self.facade.get_package_holds()) | 1309 | self.assertEqual([], self.facade.get_package_holds()) |
2872 | 1628 | self.assertIn("Queuing message with change package holds results " | 1310 | self.assertIn("Queuing response with change package results " |
2873 | 1629 | "to exchange urgently.", self.logfile.getvalue()) | 1311 | "to exchange urgently.", self.logfile.getvalue()) |
2874 | 1630 | self.assertMessages( | 1312 | self.assertMessages( |
2875 | 1631 | self.get_pending_messages(), | 1313 | self.get_pending_messages(), |
2877 | 1632 | [{"type": "operation-result", | 1314 | [{"type": "change-packages-result", |
2878 | 1633 | "operation-id": 123, | 1315 | "operation-id": 123, |
2879 | 1634 | "status": SUCCEEDED, | ||
2880 | 1635 | "result-text": "Package holds successfully changed.", | 1316 | "result-text": "Package holds successfully changed.", |
2882 | 1636 | "result-code": 0}]) | 1317 | "result-code": 1}]) |
2883 | 1637 | 1318 | ||
2884 | 1638 | result = self.changer.handle_tasks() | 1319 | result = self.changer.handle_tasks() |
2885 | 1639 | return result.addCallback(assert_result) | 1320 | return result.addCallback(assert_result) |
2886 | 1640 | 1321 | ||
2887 | 1641 | def test_change_package_locks(self): | 1322 | def test_change_package_locks(self): |
2888 | 1642 | """ | 1323 | """ |
2892 | 1643 | If C{AptFacade} is used, the L{PackageChanger.handle_tasks} | 1324 | The L{PackageChanger.handle_tasks} method fails |
2893 | 1644 | method fails the activity, since it can't add or remove locks because | 1325 | change-package-locks activities, since it can't add or remove |
2894 | 1645 | apt doesn't support this. | 1326 | locks because apt doesn't support this. |
2895 | 1646 | """ | 1327 | """ |
2896 | 1647 | self.store.add_task("changer", {"type": "change-package-locks", | 1328 | self.store.add_task("changer", {"type": "change-package-locks", |
2897 | 1648 | "create": [("foo", ">=", "1.0")], | 1329 | "create": [("foo", ">=", "1.0")], |
2898 | 1649 | 1330 | ||
2899 | === modified file 'landscape/package/tests/test_facade.py' | |||
2900 | --- landscape/package/tests/test_facade.py 2012-03-21 16:15:49 +0000 | |||
2901 | +++ landscape/package/tests/test_facade.py 2012-06-04 14:08:28 +0000 | |||
2902 | @@ -1,40 +1,22 @@ | |||
2903 | 1 | import time | ||
2904 | 2 | import os | 1 | import os |
2905 | 3 | import re | ||
2906 | 4 | import sys | ||
2907 | 5 | import textwrap | 2 | import textwrap |
2908 | 6 | import tempfile | 3 | import tempfile |
2909 | 7 | 4 | ||
2910 | 8 | try: | ||
2911 | 9 | import smart | ||
2912 | 10 | from smart.control import Control | ||
2913 | 11 | from smart.cache import Provides | ||
2914 | 12 | from smart.const import NEVER, ALWAYS | ||
2915 | 13 | except ImportError: | ||
2916 | 14 | # Smart is optional if AptFacade is being used. | ||
2917 | 15 | pass | ||
2918 | 16 | |||
2919 | 17 | import apt_pkg | 5 | import apt_pkg |
2920 | 18 | from apt.package import Package | 6 | from apt.package import Package |
2921 | 19 | from aptsources.sourceslist import SourcesList | 7 | from aptsources.sourceslist import SourcesList |
2922 | 20 | 8 | ||
2923 | 21 | from twisted.internet import reactor | ||
2924 | 22 | from twisted.internet.defer import Deferred | ||
2925 | 23 | from twisted.internet.utils import getProcessOutputAndValue | ||
2926 | 24 | |||
2927 | 25 | from landscape.constants import UBUNTU_PATH | 9 | from landscape.constants import UBUNTU_PATH |
2928 | 26 | from landscape.lib.fs import read_file, create_file | 10 | from landscape.lib.fs import read_file, create_file |
2929 | 27 | from landscape.package import facade as facade_module | ||
2930 | 28 | from landscape.package.facade import ( | 11 | from landscape.package.facade import ( |
2933 | 29 | TransactionError, DependencyError, ChannelError, SmartError, AptFacade, | 12 | TransactionError, DependencyError, ChannelError, AptFacade) |
2932 | 30 | has_new_enough_apt) | ||
2934 | 31 | 13 | ||
2935 | 32 | from landscape.tests.mocker import ANY | 14 | from landscape.tests.mocker import ANY |
2936 | 33 | from landscape.tests.helpers import LandscapeTest, EnvironSaverHelper | 15 | from landscape.tests.helpers import LandscapeTest, EnvironSaverHelper |
2937 | 34 | from landscape.package.tests.helpers import ( | 16 | from landscape.package.tests.helpers import ( |
2941 | 35 | SmartFacadeHelper, HASH1, HASH2, HASH3, PKGNAME1, PKGNAME2, PKGNAME3, | 17 | HASH1, HASH2, HASH3, PKGNAME1, PKGNAME2, PKGNAME3, |
2942 | 36 | PKGNAME4, PKGDEB4, PKGDEB1, PKGNAME_MINIMAL, PKGDEB_MINIMAL, | 18 | PKGDEB1, PKGNAME_MINIMAL, PKGDEB_MINIMAL, |
2943 | 37 | create_full_repository, create_deb, AptFacadeHelper, | 19 | create_deb, AptFacadeHelper, |
2944 | 38 | create_simple_repository) | 20 | create_simple_repository) |
2945 | 39 | 21 | ||
2946 | 40 | 22 | ||
2947 | @@ -60,9 +42,6 @@ | |||
2948 | 60 | 42 | ||
2949 | 61 | class AptFacadeTest(LandscapeTest): | 43 | class AptFacadeTest(LandscapeTest): |
2950 | 62 | 44 | ||
2951 | 63 | if not has_new_enough_apt: | ||
2952 | 64 | skip = "Can't use AptFacade on hardy" | ||
2953 | 65 | |||
2954 | 66 | helpers = [AptFacadeHelper, EnvironSaverHelper] | 45 | helpers = [AptFacadeHelper, EnvironSaverHelper] |
2955 | 67 | 46 | ||
2956 | 68 | def version_sortkey(self, version): | 47 | def version_sortkey(self, version): |
2957 | @@ -1564,16 +1543,24 @@ | |||
2958 | 1564 | self._add_package_to_deb_dir(deb_dir, "bar", version="1.5") | 1543 | self._add_package_to_deb_dir(deb_dir, "bar", version="1.5") |
2959 | 1565 | self._add_system_package("baz") | 1544 | self._add_system_package("baz") |
2960 | 1566 | self.facade.add_channel_apt_deb("file://%s" % deb_dir, "./") | 1545 | self.facade.add_channel_apt_deb("file://%s" % deb_dir, "./") |
2961 | 1546 | self._add_system_package("quux", version="1.0") | ||
2962 | 1547 | self._add_system_package("wibble", version="1.0") | ||
2963 | 1567 | self.facade.reload_channels() | 1548 | self.facade.reload_channels() |
2964 | 1568 | [foo] = self.facade.get_packages_by_name("foo") | 1549 | [foo] = self.facade.get_packages_by_name("foo") |
2965 | 1569 | self.facade.mark_install(foo) | 1550 | self.facade.mark_install(foo) |
2966 | 1570 | self.facade.mark_global_upgrade() | 1551 | self.facade.mark_global_upgrade() |
2967 | 1571 | [baz] = self.facade.get_packages_by_name("baz") | 1552 | [baz] = self.facade.get_packages_by_name("baz") |
2968 | 1572 | self.facade.mark_remove(baz) | 1553 | self.facade.mark_remove(baz) |
2969 | 1554 | [quux] = self.facade.get_packages_by_name("quux") | ||
2970 | 1555 | self.facade.mark_hold(quux) | ||
2971 | 1556 | [wibble] = self.facade.get_packages_by_name("wibble") | ||
2972 | 1557 | self.facade.mark_remove_hold(wibble) | ||
2973 | 1573 | self.facade.reset_marks() | 1558 | self.facade.reset_marks() |
2974 | 1574 | self.assertEqual(self.facade._version_installs, []) | 1559 | self.assertEqual(self.facade._version_installs, []) |
2975 | 1575 | self.assertEqual(self.facade._version_removals, []) | 1560 | self.assertEqual(self.facade._version_removals, []) |
2976 | 1576 | self.assertFalse(self.facade._global_upgrade) | 1561 | self.assertFalse(self.facade._global_upgrade) |
2977 | 1562 | self.assertEqual(self.facade._version_hold_creations, []) | ||
2978 | 1563 | self.assertEqual(self.facade._version_hold_removals, []) | ||
2979 | 1577 | self.assertEqual(self.facade.perform_changes(), None) | 1564 | self.assertEqual(self.facade.perform_changes(), None) |
2980 | 1578 | 1565 | ||
2981 | 1579 | def test_reset_marks_resets_cache(self): | 1566 | def test_reset_marks_resets_cache(self): |
2982 | @@ -2349,50 +2336,81 @@ | |||
2983 | 2349 | self.assertEqual( | 2336 | self.assertEqual( |
2984 | 2350 | ["baz", "foo"], sorted(self.facade.get_package_holds())) | 2337 | ["baz", "foo"], sorted(self.facade.get_package_holds())) |
2985 | 2351 | 2338 | ||
3021 | 2352 | def test_set_package_hold(self): | 2339 | def test_mark_hold_and_perform_hold_changes(self): |
3022 | 2353 | """ | 2340 | """ |
3023 | 2354 | C{set_package_hold} marks a package to be on hold. | 2341 | Test that L{perform_hold_changes} holds packages that have previously |
3024 | 2355 | """ | 2342 | been marked for hold. |
3025 | 2356 | self._add_system_package("foo") | 2343 | """ |
3026 | 2357 | self.facade.reload_channels() | 2344 | self._add_system_package("foo") |
3027 | 2358 | [foo] = self.facade.get_packages_by_name("foo") | 2345 | self.facade.reload_channels() |
3028 | 2359 | self.facade.set_package_hold(foo) | 2346 | [foo] = self.facade.get_packages_by_name("foo") |
3029 | 2360 | self.facade.reload_channels() | 2347 | self.facade.mark_hold(foo) |
3030 | 2361 | 2348 | self.assertEqual("Package holds successfully changed.", | |
3031 | 2362 | self.assertEqual(["foo"], self.facade.get_package_holds()) | 2349 | self.facade._perform_hold_changes()) |
3032 | 2363 | 2350 | self.facade.reload_channels() | |
3033 | 2364 | def test_set_package_hold_existing_hold(self): | 2351 | self.assertEqual(["foo"], self.facade.get_package_holds()) |
3034 | 2365 | """ | 2352 | |
3035 | 2366 | If a package is already hel, C{set_package_hold} doesn't return | 2353 | def test_mark_hold(self): |
3036 | 2367 | an error. | 2354 | """ |
3037 | 2368 | """ | 2355 | C{mark_hold} marks a package to be held. |
3038 | 2369 | self._add_system_package( | 2356 | """ |
3039 | 2370 | "foo", control_fields={"Status": "hold ok installed"}) | 2357 | self._add_system_package("foo") |
3040 | 2371 | self.facade.reload_channels() | 2358 | self.facade.reload_channels() |
3041 | 2372 | [foo] = self.facade.get_packages_by_name("foo") | 2359 | [foo] = self.facade.get_packages_by_name("foo") |
3042 | 2373 | self.facade.set_package_hold(foo) | 2360 | self.facade.mark_hold(foo) |
3043 | 2374 | self.facade.reload_channels() | 2361 | self.facade.perform_changes() |
3044 | 2375 | 2362 | self.facade.reload_channels() | |
3045 | 2376 | self.assertEqual(["foo"], self.facade.get_package_holds()) | 2363 | self.assertEqual(["foo"], self.facade.get_package_holds()) |
3046 | 2377 | 2364 | ||
3047 | 2378 | def test_remove_package_hold(self): | 2365 | def test_two_holds_with_the_same_version_id(self): |
3048 | 2379 | """ | 2366 | """ |
3049 | 2380 | C{remove_package_hold} marks a package not to be on hold. | 2367 | Test C{mark_hold} can distinguish between two different packages with |
3050 | 2381 | """ | 2368 | the same version number (the version number is used to make the unique |
3051 | 2382 | self._add_system_package( | 2369 | hash for the package version). |
3052 | 2383 | "foo", control_fields={"Status": "hold ok installed"}) | 2370 | """ |
3053 | 2384 | self.facade.reload_channels() | 2371 | self._add_system_package("foo", version="1.0") |
3054 | 2385 | [foo] = self.facade.get_packages_by_name("foo") | 2372 | self._add_system_package("bar", version="1.0") |
3055 | 2386 | self.facade.remove_package_hold(foo) | 2373 | self.facade.reload_channels() |
3056 | 2374 | [foo] = self.facade.get_packages_by_name("foo") | ||
3057 | 2375 | [bar] = self.facade.get_packages_by_name("bar") | ||
3058 | 2376 | self.facade.mark_hold(foo) | ||
3059 | 2377 | self.facade.mark_hold(bar) | ||
3060 | 2378 | self.assertEqual(2, len(self.facade._version_hold_creations)) | ||
3061 | 2379 | |||
3062 | 2380 | def test_mark_hold_existing_hold(self): | ||
3063 | 2381 | """ | ||
3064 | 2382 | If a package is already held, C{mark_hold} and | ||
3065 | 2383 | C{perform_changes} won't return an error. | ||
3066 | 2384 | """ | ||
3067 | 2385 | self._add_system_package( | ||
3068 | 2386 | "foo", control_fields={"Status": "hold ok installed"}) | ||
3069 | 2387 | self.facade.reload_channels() | ||
3070 | 2388 | [foo] = self.facade.get_packages_by_name("foo") | ||
3071 | 2389 | self.facade.mark_hold(foo) | ||
3072 | 2390 | self.facade.perform_changes() | ||
3073 | 2391 | self.facade.reload_channels() | ||
3074 | 2392 | |||
3075 | 2393 | self.assertEqual(["foo"], self.facade.get_package_holds()) | ||
3076 | 2394 | |||
3077 | 2395 | def test_mark_remove_hold(self): | ||
3078 | 2396 | """ | ||
3079 | 2397 | C{mark_remove_hold} marks a package as not held. | ||
3080 | 2398 | """ | ||
3081 | 2399 | self._add_system_package( | ||
3082 | 2400 | "foo", control_fields={"Status": "hold ok installed"}) | ||
3083 | 2401 | self.facade.reload_channels() | ||
3084 | 2402 | [foo] = self.facade.get_packages_by_name("foo") | ||
3085 | 2403 | self.facade.mark_remove_hold(foo) | ||
3086 | 2404 | self.facade.perform_changes() | ||
3087 | 2387 | self.facade.reload_channels() | 2405 | self.facade.reload_channels() |
3088 | 2388 | 2406 | ||
3089 | 2389 | self.assertEqual([], self.facade.get_package_holds()) | 2407 | self.assertEqual([], self.facade.get_package_holds()) |
3090 | 2390 | 2408 | ||
3092 | 2391 | def test_remove_package_hold_no_package(self): | 2409 | def test_mark_remove_hold_no_package(self): |
3093 | 2392 | """ | 2410 | """ |
3097 | 2393 | If a package doesn't exist, C{remove_package_hold} doesn't | 2411 | If a package doesn't exist, C{mark_remove_hold} followed by |
3098 | 2394 | return an error. It's up to the caller to make sure that the | 2412 | C{perform_changes} doesn't return an error. It's up to the caller to |
3099 | 2395 | package exist, if it's important. | 2413 | make sure that the package exist, if it's important. |
3100 | 2396 | """ | 2414 | """ |
3101 | 2397 | self._add_system_package("foo") | 2415 | self._add_system_package("foo") |
3102 | 2398 | deb_dir = self.makeDir() | 2416 | deb_dir = self.makeDir() |
3103 | @@ -2400,21 +2418,23 @@ | |||
3104 | 2400 | self.facade.add_channel_apt_deb("file://%s" % deb_dir, "./") | 2418 | self.facade.add_channel_apt_deb("file://%s" % deb_dir, "./") |
3105 | 2401 | self.facade.reload_channels() | 2419 | self.facade.reload_channels() |
3106 | 2402 | [bar] = self.facade.get_packages_by_name("bar") | 2420 | [bar] = self.facade.get_packages_by_name("bar") |
3108 | 2403 | self.facade.remove_package_hold(bar) | 2421 | self.facade.mark_remove_hold(bar) |
3109 | 2422 | self.facade.perform_changes() | ||
3110 | 2404 | self.facade.reload_channels() | 2423 | self.facade.reload_channels() |
3111 | 2405 | 2424 | ||
3112 | 2406 | self.assertEqual([], self.facade.get_package_holds()) | 2425 | self.assertEqual([], self.facade.get_package_holds()) |
3113 | 2407 | 2426 | ||
3115 | 2408 | def test_remove_package_hold_no_hold(self): | 2427 | def test_mark_remove_hold_no_hold(self): |
3116 | 2409 | """ | 2428 | """ |
3117 | 2410 | If a package isn't held, the existing selection is retained when | 2429 | If a package isn't held, the existing selection is retained when |
3119 | 2411 | C{remove_package_hold} is called. | 2430 | C{mark_remove_hold} and C{perform_changes} are called. |
3120 | 2412 | """ | 2431 | """ |
3121 | 2413 | self._add_system_package( | 2432 | self._add_system_package( |
3122 | 2414 | "foo", control_fields={"Status": "deinstall ok installed"}) | 2433 | "foo", control_fields={"Status": "deinstall ok installed"}) |
3123 | 2415 | self.facade.reload_channels() | 2434 | self.facade.reload_channels() |
3124 | 2416 | [foo] = self.facade.get_packages_by_name("foo") | 2435 | [foo] = self.facade.get_packages_by_name("foo") |
3126 | 2417 | self.facade.remove_package_hold(foo) | 2436 | self.facade.mark_remove_hold(foo) |
3127 | 2437 | self.facade.perform_changes() | ||
3128 | 2418 | self.facade.reload_channels() | 2438 | self.facade.reload_channels() |
3129 | 2419 | 2439 | ||
3130 | 2420 | self.assertEqual([], self.facade.get_package_holds()) | 2440 | self.assertEqual([], self.facade.get_package_holds()) |
3131 | @@ -2430,670 +2450,3 @@ | |||
3132 | 2430 | test_wb_mark_install_upgrade_non_main_arch_dependency_error.skip = ( | 2450 | test_wb_mark_install_upgrade_non_main_arch_dependency_error.skip = ( |
3133 | 2431 | skip_message) | 2451 | skip_message) |
3134 | 2432 | test_wb_mark_install_upgrade_non_main_arch.skip = skip_message | 2452 | test_wb_mark_install_upgrade_non_main_arch.skip = skip_message |
3135 | 2433 | |||
3136 | 2434 | |||
3137 | 2435 | class SmartFacadeTest(LandscapeTest): | ||
3138 | 2436 | |||
3139 | 2437 | helpers = [SmartFacadeHelper] | ||
3140 | 2438 | |||
3141 | 2439 | def test_needs_smart(self): | ||
3142 | 2440 | """ | ||
3143 | 2441 | If the Smart python modules can't be imported, a C{RuntimeError} | ||
3144 | 2442 | is raised when trying to create a C{SmartFacade}. | ||
3145 | 2443 | """ | ||
3146 | 2444 | |||
3147 | 2445 | def reset_has_smart(): | ||
3148 | 2446 | facade_module.has_smart = old_has_smart | ||
3149 | 2447 | |||
3150 | 2448 | self.addCleanup(reset_has_smart) | ||
3151 | 2449 | old_has_smart = facade_module.has_smart | ||
3152 | 2450 | facade_module.has_smart = False | ||
3153 | 2451 | |||
3154 | 2452 | self.assertRaises(RuntimeError, self.Facade) | ||
3155 | 2453 | |||
3156 | 2454 | def test_get_packages(self): | ||
3157 | 2455 | self.facade.reload_channels() | ||
3158 | 2456 | pkgs = self.facade.get_packages() | ||
3159 | 2457 | self.assertEqual(sorted(pkg.name for pkg in pkgs), | ||
3160 | 2458 | ["name1", "name2", "name3"]) | ||
3161 | 2459 | |||
3162 | 2460 | def test_get_packages_wont_return_non_debian_packages(self): | ||
3163 | 2461 | self.facade.reload_channels() | ||
3164 | 2462 | ctrl_mock = self.mocker.patch(Control) | ||
3165 | 2463 | |||
3166 | 2464 | class StubPackage(object): | ||
3167 | 2465 | pass | ||
3168 | 2466 | |||
3169 | 2467 | cache_mock = ctrl_mock.getCache() | ||
3170 | 2468 | cache_mock.getPackages() | ||
3171 | 2469 | self.mocker.result([StubPackage(), StubPackage()]) | ||
3172 | 2470 | self.mocker.replay() | ||
3173 | 2471 | self.assertEqual(self.facade.get_packages(), []) | ||
3174 | 2472 | |||
3175 | 2473 | def test_get_packages_by_name(self): | ||
3176 | 2474 | self.facade.reload_channels() | ||
3177 | 2475 | pkgs = self.facade.get_packages_by_name("name1") | ||
3178 | 2476 | self.assertEqual([pkg.name for pkg in pkgs], ["name1"]) | ||
3179 | 2477 | pkgs = self.facade.get_packages_by_name("name2") | ||
3180 | 2478 | self.assertEqual([pkg.name for pkg in pkgs], ["name2"]) | ||
3181 | 2479 | |||
3182 | 2480 | def test_get_packages_by_name_wont_return_non_debian_packages(self): | ||
3183 | 2481 | self.facade.reload_channels() | ||
3184 | 2482 | ctrl_mock = self.mocker.patch(Control) | ||
3185 | 2483 | |||
3186 | 2484 | class StubPackage(object): | ||
3187 | 2485 | pass | ||
3188 | 2486 | |||
3189 | 2487 | cache_mock = ctrl_mock.getCache() | ||
3190 | 2488 | cache_mock.getPackages("name") | ||
3191 | 2489 | self.mocker.result([StubPackage(), StubPackage()]) | ||
3192 | 2490 | self.mocker.replay() | ||
3193 | 2491 | self.assertEqual(self.facade.get_packages_by_name("name"), []) | ||
3194 | 2492 | |||
3195 | 2493 | def test_get_package_skeleton(self): | ||
3196 | 2494 | self.facade.reload_channels() | ||
3197 | 2495 | [pkg1] = self.facade.get_packages_by_name("name1") | ||
3198 | 2496 | [pkg2] = self.facade.get_packages_by_name("name2") | ||
3199 | 2497 | skeleton1 = self.facade.get_package_skeleton(pkg1) | ||
3200 | 2498 | skeleton2 = self.facade.get_package_skeleton(pkg2) | ||
3201 | 2499 | self.assertEqual(skeleton1.get_hash(), HASH1) | ||
3202 | 2500 | self.assertEqual(skeleton2.get_hash(), HASH2) | ||
3203 | 2501 | |||
3204 | 2502 | def test_build_skeleton_with_info(self): | ||
3205 | 2503 | self.facade.reload_channels() | ||
3206 | 2504 | [pkg] = self.facade.get_packages_by_name("name1") | ||
3207 | 2505 | skeleton = self.facade.get_package_skeleton(pkg, True) | ||
3208 | 2506 | self.assertEqual(skeleton.section, "Group1") | ||
3209 | 2507 | self.assertEqual(skeleton.summary, "Summary1") | ||
3210 | 2508 | self.assertEqual(skeleton.description, "Description1") | ||
3211 | 2509 | self.assertEqual(skeleton.size, 1038) | ||
3212 | 2510 | self.assertEqual(skeleton.installed_size, 28672) | ||
3213 | 2511 | |||
3214 | 2512 | def test_get_package_hash(self): | ||
3215 | 2513 | self.facade.reload_channels() | ||
3216 | 2514 | [pkg] = self.facade.get_packages_by_name("name1") | ||
3217 | 2515 | self.assertEqual(self.facade.get_package_hash(pkg), HASH1) | ||
3218 | 2516 | [pkg] = self.facade.get_packages_by_name("name2") | ||
3219 | 2517 | self.assertEqual(self.facade.get_package_hash(pkg), HASH2) | ||
3220 | 2518 | |||
3221 | 2519 | def test_get_package_hashes(self): | ||
3222 | 2520 | self.facade.reload_channels() | ||
3223 | 2521 | hashes = self.facade.get_package_hashes() | ||
3224 | 2522 | self.assertEqual(sorted(hashes), sorted([HASH1, HASH2, HASH3])) | ||
3225 | 2523 | |||
3226 | 2524 | def test_get_package_by_hash(self): | ||
3227 | 2525 | self.facade.reload_channels() | ||
3228 | 2526 | pkg = self.facade.get_package_by_hash(HASH1) | ||
3229 | 2527 | self.assertEqual(pkg.name, "name1") | ||
3230 | 2528 | pkg = self.facade.get_package_by_hash(HASH2) | ||
3231 | 2529 | self.assertEqual(pkg.name, "name2") | ||
3232 | 2530 | pkg = self.facade.get_package_by_hash("none") | ||
3233 | 2531 | self.assertEqual(pkg, None) | ||
3234 | 2532 | |||
3235 | 2533 | def test_reload_channels_clears_hash_cache(self): | ||
3236 | 2534 | # Load hashes. | ||
3237 | 2535 | self.facade.reload_channels() | ||
3238 | 2536 | |||
3239 | 2537 | # Hold a reference to packages. | ||
3240 | 2538 | [pkg1] = self.facade.get_packages_by_name("name1") | ||
3241 | 2539 | [pkg2] = self.facade.get_packages_by_name("name2") | ||
3242 | 2540 | [pkg3] = self.facade.get_packages_by_name("name3") | ||
3243 | 2541 | self.assertTrue(pkg1 and pkg2) | ||
3244 | 2542 | |||
3245 | 2543 | # Remove the package from the repository. | ||
3246 | 2544 | os.unlink(os.path.join(self.repository_dir, PKGNAME1)) | ||
3247 | 2545 | |||
3248 | 2546 | # Forcibly change the mtime of our repository, so that Smart | ||
3249 | 2547 | # will consider it as changed (if the change is inside the | ||
3250 | 2548 | # same second the directory's mtime will be the same) | ||
3251 | 2549 | mtime = int(time.time() + 1) | ||
3252 | 2550 | os.utime(self.repository_dir, (mtime, mtime)) | ||
3253 | 2551 | |||
3254 | 2552 | # Reload channels. | ||
3255 | 2553 | self.facade.reload_channels() | ||
3256 | 2554 | |||
3257 | 2555 | # Only packages with name2 and name3 should be loaded, and they're | ||
3258 | 2556 | # not the same objects anymore. | ||
3259 | 2557 | self.assertEqual( | ||
3260 | 2558 | sorted([pkg.name for pkg in self.facade.get_packages()]), | ||
3261 | 2559 | ["name2", "name3"]) | ||
3262 | 2560 | self.assertNotEquals(set(self.facade.get_packages()), | ||
3263 | 2561 | set([pkg2, pkg3])) | ||
3264 | 2562 | |||
3265 | 2563 | # The hash cache shouldn't include either of the old packages. | ||
3266 | 2564 | self.assertEqual(self.facade.get_package_hash(pkg1), None) | ||
3267 | 2565 | self.assertEqual(self.facade.get_package_hash(pkg2), None) | ||
3268 | 2566 | self.assertEqual(self.facade.get_package_hash(pkg3), None) | ||
3269 | 2567 | |||
3270 | 2568 | # Also, the hash for package1 shouldn't be present at all. | ||
3271 | 2569 | self.assertEqual(self.facade.get_package_by_hash(HASH1), None) | ||
3272 | 2570 | |||
3273 | 2571 | # While HASH2 and HASH3 should point to the new packages. | ||
3274 | 2572 | new_pkgs = self.facade.get_packages() | ||
3275 | 2573 | self.assertTrue(self.facade.get_package_by_hash(HASH2) | ||
3276 | 2574 | in new_pkgs) | ||
3277 | 2575 | self.assertTrue(self.facade.get_package_by_hash(HASH3) | ||
3278 | 2576 | in new_pkgs) | ||
3279 | 2577 | |||
3280 | 2578 | # Which are not the old packages. | ||
3281 | 2579 | self.assertFalse(pkg2 in new_pkgs) | ||
3282 | 2580 | self.assertFalse(pkg3 in new_pkgs) | ||
3283 | 2581 | |||
3284 | 2582 | def test_ensure_reload_channels(self): | ||
3285 | 2583 | """ | ||
3286 | 2584 | The L{SmartFacade.ensure_channels_reloaded} can be called more | ||
3287 | 2585 | than once, but channels will be reloaded only the first time. | ||
3288 | 2586 | """ | ||
3289 | 2587 | self.assertEqual(len(self.facade.get_packages()), 0) | ||
3290 | 2588 | self.facade.ensure_channels_reloaded() | ||
3291 | 2589 | self.assertEqual(len(self.facade.get_packages()), 3) | ||
3292 | 2590 | |||
3293 | 2591 | # Calling it once more won't reload channels again. | ||
3294 | 2592 | self.facade.get_packages_by_name("name1")[0].installed = True | ||
3295 | 2593 | self.facade.ensure_channels_reloaded() | ||
3296 | 2594 | self.assertTrue(self.facade.get_packages_by_name("name1")[0].installed) | ||
3297 | 2595 | |||
3298 | 2596 | def test_perform_changes_with_nothing_to_do(self): | ||
3299 | 2597 | """perform_changes() should return None when there's nothing to do. | ||
3300 | 2598 | """ | ||
3301 | 2599 | self.facade.reload_channels() | ||
3302 | 2600 | self.assertEqual(self.facade.perform_changes(), None) | ||
3303 | 2601 | |||
3304 | 2602 | def test_reset_marks(self): | ||
3305 | 2603 | """perform_changes() should return None when there's nothing to do. | ||
3306 | 2604 | """ | ||
3307 | 2605 | self.facade.reload_channels() | ||
3308 | 2606 | [pkg] = self.facade.get_packages_by_name("name1") | ||
3309 | 2607 | self.facade.mark_install(pkg) | ||
3310 | 2608 | self.facade.reset_marks() | ||
3311 | 2609 | self.assertEqual(self.facade.perform_changes(), None) | ||
3312 | 2610 | |||
3313 | 2611 | def test_mark_install_transaction_error(self): | ||
3314 | 2612 | """ | ||
3315 | 2613 | Mark package 'name1' for installation, and try to perform changes. | ||
3316 | 2614 | It should fail because 'name1' depends on 'requirename1'. | ||
3317 | 2615 | """ | ||
3318 | 2616 | self.facade.reload_channels() | ||
3319 | 2617 | |||
3320 | 2618 | [pkg] = self.facade.get_packages_by_name("name1") | ||
3321 | 2619 | self.facade.mark_install(pkg) | ||
3322 | 2620 | exception = self.assertRaises(TransactionError, | ||
3323 | 2621 | self.facade.perform_changes) | ||
3324 | 2622 | self.assertIn("requirename", exception.args[0]) | ||
3325 | 2623 | |||
3326 | 2624 | def test_mark_install_dependency_error(self): | ||
3327 | 2625 | """ | ||
3328 | 2626 | Now we artificially inject the needed dependencies of 'name1' | ||
3329 | 2627 | in 'name2', but we don't mark 'name2' for installation, and | ||
3330 | 2628 | that should make perform_changes() fail with a dependency | ||
3331 | 2629 | error on the needed package. | ||
3332 | 2630 | """ | ||
3333 | 2631 | self.facade.reload_channels() | ||
3334 | 2632 | |||
3335 | 2633 | provide1 = Provides("prerequirename1", "prerequireversion1") | ||
3336 | 2634 | provide2 = Provides("requirename1", "requireversion1") | ||
3337 | 2635 | [pkg2] = self.facade.get_packages_by_name("name2") | ||
3338 | 2636 | pkg2.provides += (provide1, provide2) | ||
3339 | 2637 | |||
3340 | 2638 | # We have to satisfy *both* packages. | ||
3341 | 2639 | provide1 = Provides("prerequirename2", "prerequireversion2") | ||
3342 | 2640 | provide2 = Provides("requirename2", "requireversion2") | ||
3343 | 2641 | [pkg1] = self.facade.get_packages_by_name("name1") | ||
3344 | 2642 | pkg1.provides += (provide1, provide2) | ||
3345 | 2643 | |||
3346 | 2644 | # Ask Smart to reprocess relationships. | ||
3347 | 2645 | self.facade.reload_cache() | ||
3348 | 2646 | |||
3349 | 2647 | self.assertEqual(pkg1.requires[0].providedby[0].packages[0], pkg2) | ||
3350 | 2648 | self.assertEqual(pkg1.requires[1].providedby[0].packages[0], pkg2) | ||
3351 | 2649 | |||
3352 | 2650 | self.facade.mark_install(pkg1) | ||
3353 | 2651 | try: | ||
3354 | 2652 | self.facade.perform_changes() | ||
3355 | 2653 | except DependencyError, exception: | ||
3356 | 2654 | pass | ||
3357 | 2655 | else: | ||
3358 | 2656 | exception = None | ||
3359 | 2657 | self.assertTrue(exception, "DependencyError not raised") | ||
3360 | 2658 | self.assertEqual(exception.packages, [pkg2]) | ||
3361 | 2659 | |||
3362 | 2660 | def test_mark_remove_dependency_error(self): | ||
3363 | 2661 | """ | ||
3364 | 2662 | Besides making 'name1' satisfy 'name2' and the contrary. We'll | ||
3365 | 2663 | mark both packages installed, so that we can get an error on | ||
3366 | 2664 | removal. | ||
3367 | 2665 | """ | ||
3368 | 2666 | self.facade.reload_channels() | ||
3369 | 2667 | |||
3370 | 2668 | provide1 = Provides("prerequirename1", "prerequireversion1") | ||
3371 | 2669 | provide2 = Provides("requirename1", "requireversion1") | ||
3372 | 2670 | [pkg2] = self.facade.get_packages_by_name("name2") | ||
3373 | 2671 | pkg2.provides += (provide1, provide2) | ||
3374 | 2672 | |||
3375 | 2673 | # We have to satisfy *both* packages. | ||
3376 | 2674 | provide1 = Provides("prerequirename2", "prerequireversion2") | ||
3377 | 2675 | provide2 = Provides("requirename2", "requireversion2") | ||
3378 | 2676 | [pkg1] = self.facade.get_packages_by_name("name1") | ||
3379 | 2677 | pkg1.provides += (provide1, provide2) | ||
3380 | 2678 | |||
3381 | 2679 | # Ask Smart to reprocess relationships. | ||
3382 | 2680 | self.facade.reload_cache() | ||
3383 | 2681 | |||
3384 | 2682 | pkg1.installed = True | ||
3385 | 2683 | pkg2.installed = True | ||
3386 | 2684 | |||
3387 | 2685 | self.assertEqual(pkg1.requires[0].providedby[0].packages[0], pkg2) | ||
3388 | 2686 | self.assertEqual(pkg1.requires[1].providedby[0].packages[0], pkg2) | ||
3389 | 2687 | |||
3390 | 2688 | self.facade.mark_remove(pkg2) | ||
3391 | 2689 | try: | ||
3392 | 2690 | output = self.facade.perform_changes() | ||
3393 | 2691 | except DependencyError, exception: | ||
3394 | 2692 | output = "" | ||
3395 | 2693 | else: | ||
3396 | 2694 | exception = None | ||
3397 | 2695 | self.assertTrue(exception, "DependencyError not raised. Output: %s" | ||
3398 | 2696 | % repr(output)) | ||
3399 | 2697 | self.assertEqual(exception.packages, [pkg1]) | ||
3400 | 2698 | |||
3401 | 2699 | def test_mark_upgrade_dependency_error(self): | ||
3402 | 2700 | """Artificially make pkg2 upgrade pkg1, and mark pkg1 for upgrade.""" | ||
3403 | 2701 | |||
3404 | 2702 | # The backend only works after initialized. | ||
3405 | 2703 | from smart.backends.deb.base import DebUpgrades, DebConflicts | ||
3406 | 2704 | |||
3407 | 2705 | self.facade.reload_channels() | ||
3408 | 2706 | |||
3409 | 2707 | [pkg1] = self.facade.get_packages_by_name("name1") | ||
3410 | 2708 | [pkg2] = self.facade.get_packages_by_name("name2") | ||
3411 | 2709 | |||
3412 | 2710 | # Artificially make pkg2 be self-satisfied, and make it upgrade and | ||
3413 | 2711 | # conflict with pkg1. | ||
3414 | 2712 | pkg2.requires = [] | ||
3415 | 2713 | pkg2.upgrades = [DebUpgrades("name1", "=", "version1-release1")] | ||
3416 | 2714 | pkg2.conflicts = [DebConflicts("name1", "=", "version1-release1")] | ||
3417 | 2715 | |||
3418 | 2716 | # pkg1 will also be self-satisfied. | ||
3419 | 2717 | pkg1.requires = [] | ||
3420 | 2718 | |||
3421 | 2719 | # Ask Smart to reprocess relationships. | ||
3422 | 2720 | self.facade.reload_cache() | ||
3423 | 2721 | |||
3424 | 2722 | # Mark the pkg1 as installed. Must be done after reloading | ||
3425 | 2723 | # the cache as reloading will reset it to the loader installed | ||
3426 | 2724 | # status. | ||
3427 | 2725 | pkg1.installed = True | ||
3428 | 2726 | |||
3429 | 2727 | # Check that the linkage worked. | ||
3430 | 2728 | self.assertEqual(pkg2.upgrades[0].providedby[0].packages[0], pkg1) | ||
3431 | 2729 | |||
3432 | 2730 | # Perform the upgrade test. | ||
3433 | 2731 | self.facade.mark_upgrade(pkg1) | ||
3434 | 2732 | try: | ||
3435 | 2733 | self.facade.perform_changes() | ||
3436 | 2734 | except DependencyError, exception: | ||
3437 | 2735 | pass | ||
3438 | 2736 | else: | ||
3439 | 2737 | exception = None | ||
3440 | 2738 | self.assertTrue(exception, "DependencyError not raised") | ||
3441 | 2739 | |||
3442 | 2740 | # Both packages should be included in the dependency error. One | ||
3443 | 2741 | # must be removed, and the other installed. | ||
3444 | 2742 | self.assertEqual(set(exception.packages), set([pkg1, pkg2])) | ||
3445 | 2743 | |||
3446 | 2744 | def test_perform_changes_with_logged_error(self): | ||
3447 | 2745 | self.log_helper.ignore_errors(".*dpkg") | ||
3448 | 2746 | |||
3449 | 2747 | self.facade.reload_channels() | ||
3450 | 2748 | |||
3451 | 2749 | [pkg] = self.facade.get_packages_by_name("name1") | ||
3452 | 2750 | pkg.requires = () | ||
3453 | 2751 | |||
3454 | 2752 | self.facade.reload_cache() | ||
3455 | 2753 | |||
3456 | 2754 | self.facade.mark_install(pkg) | ||
3457 | 2755 | |||
3458 | 2756 | try: | ||
3459 | 2757 | output = self.facade.perform_changes() | ||
3460 | 2758 | except SmartError, exception: | ||
3461 | 2759 | output = "" | ||
3462 | 2760 | else: | ||
3463 | 2761 | exception = None | ||
3464 | 2762 | |||
3465 | 2763 | self.assertTrue(exception, | ||
3466 | 2764 | "SmartError not raised. Output: %s" % repr(output)) | ||
3467 | 2765 | # We can't check the whole message because the dpkg error can be | ||
3468 | 2766 | # localized. We can't use str(exception) either because it can contain | ||
3469 | 2767 | # unicode | ||
3470 | 2768 | self.assertIn("ERROR", exception.args[0]) | ||
3471 | 2769 | self.assertIn("(2)", exception.args[0]) | ||
3472 | 2770 | self.assertIn("\n[unpack] name1_version1-release1\ndpkg: ", | ||
3473 | 2771 | exception.args[0]) | ||
3474 | 2772 | |||
3475 | 2773 | def test_perform_changes_is_non_interactive(self): | ||
3476 | 2774 | from smart.backends.deb.pm import DebPackageManager | ||
3477 | 2775 | |||
3478 | 2776 | self.facade.reload_channels() | ||
3479 | 2777 | |||
3480 | 2778 | [pkg] = self.facade.get_packages_by_name("name1") | ||
3481 | 2779 | pkg.requires = () | ||
3482 | 2780 | |||
3483 | 2781 | self.facade.reload_cache() | ||
3484 | 2782 | |||
3485 | 2783 | self.facade.mark_install(pkg) | ||
3486 | 2784 | |||
3487 | 2785 | environ = [] | ||
3488 | 2786 | |||
3489 | 2787 | def check_environ(self, argv, output): | ||
3490 | 2788 | environ.append(os.environ.get("DEBIAN_FRONTEND")) | ||
3491 | 2789 | environ.append(os.environ.get("APT_LISTCHANGES_FRONTEND")) | ||
3492 | 2790 | return 0 | ||
3493 | 2791 | |||
3494 | 2792 | DebPackageManager.dpkg, olddpkg = check_environ, DebPackageManager.dpkg | ||
3495 | 2793 | |||
3496 | 2794 | try: | ||
3497 | 2795 | self.facade.perform_changes() | ||
3498 | 2796 | finally: | ||
3499 | 2797 | DebPackageManager.dpkg = olddpkg | ||
3500 | 2798 | |||
3501 | 2799 | self.assertEqual(environ, ["noninteractive", "none", | ||
3502 | 2800 | "noninteractive", "none"]) | ||
3503 | 2801 | |||
3504 | 2802 | def test_perform_changes_with_policy_remove(self): | ||
3505 | 2803 | """ | ||
3506 | 2804 | When requested changes are only about removing packages, we set | ||
3507 | 2805 | the Smart transaction policy to C{PolicyRemove}. | ||
3508 | 2806 | """ | ||
3509 | 2807 | create_deb(self.repository_dir, PKGNAME4, PKGDEB4) | ||
3510 | 2808 | self.facade.reload_channels() | ||
3511 | 2809 | |||
3512 | 2810 | # Importing these modules fail if Smart is not initialized | ||
3513 | 2811 | from smart.backends.deb.base import DebRequires | ||
3514 | 2812 | |||
3515 | 2813 | pkg1 = self.facade.get_package_by_hash(HASH1) | ||
3516 | 2814 | pkg1.requires.append(DebRequires("name3", ">=", "version3-release3")) | ||
3517 | 2815 | |||
3518 | 2816 | pkg3 = self.facade.get_package_by_hash(HASH3) | ||
3519 | 2817 | |||
3520 | 2818 | # Ask Smart to reprocess relationships. | ||
3521 | 2819 | self.facade.reload_cache() | ||
3522 | 2820 | |||
3523 | 2821 | pkg1.installed = True | ||
3524 | 2822 | pkg3.installed = True | ||
3525 | 2823 | |||
3526 | 2824 | self.facade.mark_remove(pkg3) | ||
3527 | 2825 | error = self.assertRaises(DependencyError, self.facade.perform_changes) | ||
3528 | 2826 | [missing] = error.packages | ||
3529 | 2827 | self.assertIdentical(pkg1, missing) | ||
3530 | 2828 | |||
3531 | 2829 | def test_perform_changes_with_commit_change_set_errors(self): | ||
3532 | 2830 | |||
3533 | 2831 | self.facade.reload_channels() | ||
3534 | 2832 | |||
3535 | 2833 | [pkg] = self.facade.get_packages_by_name("name1") | ||
3536 | 2834 | pkg.requires = () | ||
3537 | 2835 | |||
3538 | 2836 | self.facade.mark_install(pkg) | ||
3539 | 2837 | |||
3540 | 2838 | ctrl_mock = self.mocker.patch(Control) | ||
3541 | 2839 | ctrl_mock.commitChangeSet(ANY) | ||
3542 | 2840 | self.mocker.throw(smart.Error("commit error")) | ||
3543 | 2841 | self.mocker.replay() | ||
3544 | 2842 | |||
3545 | 2843 | self.assertRaises(TransactionError, self.facade.perform_changes) | ||
3546 | 2844 | |||
3547 | 2845 | def test_deinit_cleans_the_state(self): | ||
3548 | 2846 | self.facade.reload_channels() | ||
3549 | 2847 | self.assertTrue(self.facade.get_package_by_hash(HASH1)) | ||
3550 | 2848 | self.facade.deinit() | ||
3551 | 2849 | self.assertFalse(self.facade.get_package_by_hash(HASH1)) | ||
3552 | 2850 | |||
3553 | 2851 | def test_deinit_deinits_smart(self): | ||
3554 | 2852 | self.facade.reload_channels() | ||
3555 | 2853 | self.assertTrue(smart.iface.object) | ||
3556 | 2854 | self.facade.deinit() | ||
3557 | 2855 | self.assertFalse(smart.iface.object) | ||
3558 | 2856 | |||
3559 | 2857 | def test_deinit_when_smart_wasnt_initialized(self): | ||
3560 | 2858 | self.assertFalse(smart.iface.object) | ||
3561 | 2859 | # Nothing bad should happen. | ||
3562 | 2860 | self.facade.deinit() | ||
3563 | 2861 | |||
3564 | 2862 | def test_reload_channels_wont_consider_non_debian_packages(self): | ||
3565 | 2863 | |||
3566 | 2864 | class StubPackage(object): | ||
3567 | 2865 | pass | ||
3568 | 2866 | |||
3569 | 2867 | pkg = StubPackage() | ||
3570 | 2868 | |||
3571 | 2869 | ctrl_mock = self.mocker.patch(Control) | ||
3572 | 2870 | cache_mock = ctrl_mock.getCache() | ||
3573 | 2871 | cache_mock.getPackages() | ||
3574 | 2872 | self.mocker.result([pkg]) | ||
3575 | 2873 | self.mocker.replay() | ||
3576 | 2874 | |||
3577 | 2875 | self.facade.reload_channels() | ||
3578 | 2876 | self.assertEqual(self.facade.get_package_hash(pkg), None) | ||
3579 | 2877 | |||
3580 | 2878 | def test_reload_channels_with_channel_error(self): | ||
3581 | 2879 | """ | ||
3582 | 2880 | The L{SmartFacade.reload_channels} method raises a L{ChannelsError} if | ||
3583 | 2881 | smart fails to load the configured channels. | ||
3584 | 2882 | """ | ||
3585 | 2883 | ctrl_mock = self.mocker.patch(Control) | ||
3586 | 2884 | ctrl_mock.reloadChannels(caching=ALWAYS) | ||
3587 | 2885 | self.mocker.throw(smart.Error(u"Channel information is locked")) | ||
3588 | 2886 | self.mocker.replay() | ||
3589 | 2887 | self.assertRaises(ChannelError, self.facade.reload_channels) | ||
3590 | 2888 | |||
3591 | 2889 | def test_reset_add_get_channels(self): | ||
3592 | 2890 | |||
3593 | 2891 | channels = [("alias0", {"type": "test"}), | ||
3594 | 2892 | ("alias1", {"type": "test"})] | ||
3595 | 2893 | |||
3596 | 2894 | self.facade.reset_channels() | ||
3597 | 2895 | |||
3598 | 2896 | self.assertEqual(self.facade.get_channels(), {}) | ||
3599 | 2897 | |||
3600 | 2898 | self.facade.add_channel(*channels[0]) | ||
3601 | 2899 | self.facade.add_channel(*channels[1]) | ||
3602 | 2900 | |||
3603 | 2901 | self.assertEqual(self.facade.get_channels(), dict(channels)) | ||
3604 | 2902 | |||
3605 | 2903 | def test_add_apt_deb_channel(self): | ||
3606 | 2904 | """ | ||
3607 | 2905 | The L{SmartFacade.add_channel_apt_deb} add a Smart channel of | ||
3608 | 2906 | type C{"apt-deb"}. | ||
3609 | 2907 | """ | ||
3610 | 2908 | self.facade.reset_channels() | ||
3611 | 2909 | self.facade.add_channel_apt_deb("http://url/", "name", "component") | ||
3612 | 2910 | self.assertEqual(self.facade.get_channels(), | ||
3613 | 2911 | {"name": {"baseurl": "http://url/", | ||
3614 | 2912 | "distribution": "name", | ||
3615 | 2913 | "components": "component", | ||
3616 | 2914 | "type": "apt-deb"}}) | ||
3617 | 2915 | |||
3618 | 2916 | def test_add_deb_dir_channel(self): | ||
3619 | 2917 | """ | ||
3620 | 2918 | The L{SmartFacade.add_channel_deb_dir} add a Smart channel of | ||
3621 | 2919 | type C{"deb-dir"}. | ||
3622 | 2920 | """ | ||
3623 | 2921 | self.facade.reset_channels() | ||
3624 | 2922 | self.facade.add_channel_deb_dir("/my/repo") | ||
3625 | 2923 | self.assertEqual(self.facade.get_channels(), | ||
3626 | 2924 | {"/my/repo": {"path": "/my/repo", | ||
3627 | 2925 | "type": "deb-dir"}}) | ||
3628 | 2926 | |||
3629 | 2927 | def test_get_arch(self): | ||
3630 | 2928 | """ | ||
3631 | 2929 | The L{SmartFacade.get_arch} should return the system dpkg | ||
3632 | 2930 | architecture. | ||
3633 | 2931 | """ | ||
3634 | 2932 | deferred = Deferred() | ||
3635 | 2933 | |||
3636 | 2934 | def do_test(): | ||
3637 | 2935 | result = getProcessOutputAndValue("/usr/bin/dpkg", | ||
3638 | 2936 | ("--print-architecture",)) | ||
3639 | 2937 | |||
3640 | 2938 | def callback((out, err, code)): | ||
3641 | 2939 | self.assertEqual(self.facade.get_arch(), out.strip()) | ||
3642 | 2940 | result.addCallback(callback) | ||
3643 | 2941 | result.chainDeferred(deferred) | ||
3644 | 2942 | |||
3645 | 2943 | reactor.callWhenRunning(do_test) | ||
3646 | 2944 | return deferred | ||
3647 | 2945 | |||
3648 | 2946 | def test_set_arch_multiple_times(self): | ||
3649 | 2947 | |||
3650 | 2948 | repo = create_full_repository(self.makeDir()) | ||
3651 | 2949 | |||
3652 | 2950 | self.facade.set_arch("i386") | ||
3653 | 2951 | self.facade.reset_channels() | ||
3654 | 2952 | self.facade.add_channel_apt_deb(repo.url, repo.codename, | ||
3655 | 2953 | " ".join(repo.components)) | ||
3656 | 2954 | self.facade.reload_channels() | ||
3657 | 2955 | |||
3658 | 2956 | pkgs = self.facade.get_packages() | ||
3659 | 2957 | self.assertEqual(len(pkgs), 2) | ||
3660 | 2958 | self.assertEqual(pkgs[0].name, "syslinux") | ||
3661 | 2959 | self.assertEqual(pkgs[1].name, "kairos") | ||
3662 | 2960 | |||
3663 | 2961 | self.facade.deinit() | ||
3664 | 2962 | self.facade.set_arch("amd64") | ||
3665 | 2963 | self.facade.reset_channels() | ||
3666 | 2964 | self.facade.add_channel_apt_deb(repo.url, repo.codename, | ||
3667 | 2965 | " ".join(repo.components)) | ||
3668 | 2966 | self.facade.reload_channels() | ||
3669 | 2967 | |||
3670 | 2968 | pkgs = self.facade.get_packages() | ||
3671 | 2969 | self.assertEqual(len(pkgs), 2) | ||
3672 | 2970 | self.assertEqual(pkgs[0].name, "libclthreads2") | ||
3673 | 2971 | self.assertEqual(pkgs[1].name, "kairos") | ||
3674 | 2972 | |||
3675 | 2973 | def test_set_caching_with_reload_error(self): | ||
3676 | 2974 | |||
3677 | 2975 | alias = "alias" | ||
3678 | 2976 | channel = {"type": "deb-dir", | ||
3679 | 2977 | "path": "/does/not/exist"} | ||
3680 | 2978 | |||
3681 | 2979 | self.facade.reset_channels() | ||
3682 | 2980 | self.facade.add_channel(alias, channel) | ||
3683 | 2981 | self.facade.set_caching(NEVER) | ||
3684 | 2982 | |||
3685 | 2983 | self.assertRaises(ChannelError, self.facade.reload_channels) | ||
3686 | 2984 | self.facade._channels = {} | ||
3687 | 2985 | |||
3688 | 2986 | ignore_re = re.compile("\[Smart\].*'alias'.*/does/not/exist") | ||
3689 | 2987 | |||
3690 | 2988 | self.log_helper.ignored_exception_regexes = [ignore_re] | ||
3691 | 2989 | |||
3692 | 2990 | def test_init_landscape_plugins(self): | ||
3693 | 2991 | """ | ||
3694 | 2992 | The landscape plugin which helps managing proxies is loaded when smart | ||
3695 | 2993 | is initialized: this sets a smart configuration variable and load the | ||
3696 | 2994 | module. | ||
3697 | 2995 | """ | ||
3698 | 2996 | self.facade.reload_channels() | ||
3699 | 2997 | self.assertTrue(smart.sysconf.get("use-landscape-proxies")) | ||
3700 | 2998 | self.assertIn("smart.plugins.landscape", sys.modules) | ||
3701 | 2999 | |||
3702 | 3000 | def test_get_package_locks_with_no_lock(self): | ||
3703 | 3001 | """ | ||
3704 | 3002 | If no package locks are set, L{SmartFacade.get_package_locks} returns | ||
3705 | 3003 | an empty C{list}. | ||
3706 | 3004 | """ | ||
3707 | 3005 | self.assertEqual(self.facade.get_package_locks(), []) | ||
3708 | 3006 | |||
3709 | 3007 | def test_get_package_locks_with_one_lock(self): | ||
3710 | 3008 | """ | ||
3711 | 3009 | If one lock is set, the list of locks contains one item. | ||
3712 | 3010 | """ | ||
3713 | 3011 | self.facade.set_package_lock("name1", "<", "version1") | ||
3714 | 3012 | self.assertEqual(self.facade.get_package_locks(), | ||
3715 | 3013 | [("name1", "<", "version1")]) | ||
3716 | 3014 | |||
3717 | 3015 | def test_get_package_locks_with_many_locks(self): | ||
3718 | 3016 | """ | ||
3719 | 3017 | It's possible to have more than one package lock and several conditions | ||
3720 | 3018 | for each of them. | ||
3721 | 3019 | """ | ||
3722 | 3020 | self.facade.set_package_lock("name1", "<", "version1") | ||
3723 | 3021 | self.facade.set_package_lock("name1", ">=", "version3") | ||
3724 | 3022 | self.facade.set_package_lock("name2") | ||
3725 | 3023 | self.assertEqual(sorted(self.facade.get_package_locks()), | ||
3726 | 3024 | sorted([("name1", "<", "version1"), | ||
3727 | 3025 | ("name1", ">=", "version3"), | ||
3728 | 3026 | ("name2", "", "")])) | ||
3729 | 3027 | |||
3730 | 3028 | def test_set_package_lock(self): | ||
3731 | 3029 | """ | ||
3732 | 3030 | It is possible to lock a package by simply specifying its name. | ||
3733 | 3031 | """ | ||
3734 | 3032 | self.facade.set_package_lock("name1") | ||
3735 | 3033 | self.facade.reload_channels() | ||
3736 | 3034 | [package] = self.facade.get_locked_packages() | ||
3737 | 3035 | self.assertEqual(package.name, "name1") | ||
3738 | 3036 | |||
3739 | 3037 | def test_set_package_lock_with_matching_condition(self): | ||
3740 | 3038 | """ | ||
3741 | 3039 | It is possible to set a package lock specifying both a | ||
3742 | 3040 | package name and version condition. Any matching package | ||
3743 | 3041 | will be locked. | ||
3744 | 3042 | """ | ||
3745 | 3043 | self.facade.set_package_lock("name1", "<", "version2") | ||
3746 | 3044 | self.facade.reload_channels() | ||
3747 | 3045 | [package] = self.facade.get_locked_packages() | ||
3748 | 3046 | self.assertEqual(package.name, "name1") | ||
3749 | 3047 | |||
3750 | 3048 | def test_set_package_lock_with_non_matching_condition(self): | ||
3751 | 3049 | """ | ||
3752 | 3050 | If the package lock conditions do not match any package, | ||
3753 | 3051 | no package will be locked. | ||
3754 | 3052 | """ | ||
3755 | 3053 | self.facade.set_package_lock("name1", "<", "version1") | ||
3756 | 3054 | self.facade.reload_channels() | ||
3757 | 3055 | self.assertEqual(self.facade.get_locked_packages(), []) | ||
3758 | 3056 | |||
3759 | 3057 | def test_set_package_lock_with_missing_version(self): | ||
3760 | 3058 | """ | ||
3761 | 3059 | When specifing a relation for a package lock condition, a version | ||
3762 | 3060 | must be provided as well. | ||
3763 | 3061 | """ | ||
3764 | 3062 | error = self.assertRaises(RuntimeError, self.facade.set_package_lock, | ||
3765 | 3063 | "name1", "<", "") | ||
3766 | 3064 | self.assertEqual(str(error), "Package lock version not provided") | ||
3767 | 3065 | |||
3768 | 3066 | def test_set_package_lock_with_missing_relation(self): | ||
3769 | 3067 | """ | ||
3770 | 3068 | When specifing a version for a package lock condition, a relation | ||
3771 | 3069 | must be provided as well. | ||
3772 | 3070 | """ | ||
3773 | 3071 | error = self.assertRaises(RuntimeError, self.facade.set_package_lock, | ||
3774 | 3072 | "name1", "", "version1") | ||
3775 | 3073 | self.assertEqual(str(error), "Package lock relation not provided") | ||
3776 | 3074 | |||
3777 | 3075 | def test_remove_package_lock(self): | ||
3778 | 3076 | """ | ||
3779 | 3077 | It is possibly to remove a package lock without any version condition. | ||
3780 | 3078 | """ | ||
3781 | 3079 | self.facade.set_package_lock("name1") | ||
3782 | 3080 | self.facade.remove_package_lock("name1") | ||
3783 | 3081 | self.assertEqual(self.facade.get_locked_packages(), []) | ||
3784 | 3082 | |||
3785 | 3083 | def test_remove_package_lock_with_condition(self): | ||
3786 | 3084 | """ | ||
3787 | 3085 | It is possibly to remove a package lock with a version condition. | ||
3788 | 3086 | """ | ||
3789 | 3087 | self.facade.set_package_lock("name1", "<", "version1") | ||
3790 | 3088 | self.facade.remove_package_lock("name1", "<", "version1") | ||
3791 | 3089 | self.assertEqual(self.facade.get_locked_packages(), []) | ||
3792 | 3090 | |||
3793 | 3091 | def test_save_config(self): | ||
3794 | 3092 | """ | ||
3795 | 3093 | It is possible to lock a package by simply specifying its name. | ||
3796 | 3094 | """ | ||
3797 | 3095 | self.facade.set_package_lock("python", "=>", "2.5") | ||
3798 | 3096 | self.facade.save_config() | ||
3799 | 3097 | self.facade.deinit() | ||
3800 | 3098 | self.assertEqual(self.facade.get_package_locks(), | ||
3801 | 3099 | [("python", "=>", "2.5")]) | ||
3802 | 3100 | 2453 | ||
3803 | === removed file 'landscape/package/tests/test_interface.py' | |||
3804 | --- landscape/package/tests/test_interface.py 2011-07-18 15:16:18 +0000 | |||
3805 | +++ landscape/package/tests/test_interface.py 1970-01-01 00:00:00 +0000 | |||
3806 | @@ -1,34 +0,0 @@ | |||
3807 | 1 | # -*- encoding: utf-8 -*- | ||
3808 | 2 | from landscape.package.interface import LandscapeInterface | ||
3809 | 3 | |||
3810 | 4 | from landscape.tests.helpers import LandscapeTest | ||
3811 | 5 | from landscape.package.tests.helpers import SmartFacadeHelper | ||
3812 | 6 | |||
3813 | 7 | |||
3814 | 8 | class LandscapeInterfaceTest(LandscapeTest): | ||
3815 | 9 | |||
3816 | 10 | helpers = [SmartFacadeHelper] | ||
3817 | 11 | |||
3818 | 12 | def setUp(self): | ||
3819 | 13 | super(LandscapeInterfaceTest, self).setUp() | ||
3820 | 14 | self.facade.reload_channels() | ||
3821 | 15 | self.iface = LandscapeInterface(None) | ||
3822 | 16 | |||
3823 | 17 | def test_message_with_unicode_and_utf8(self): | ||
3824 | 18 | self.iface.info(u"áéÃóú") | ||
3825 | 19 | self.iface.info("áéÃóú") | ||
3826 | 20 | self.assertEqual(self.iface.get_output_for_landscape(), | ||
3827 | 21 | u"INFO: áéÃóú\nINFO: áéÃóú\n") | ||
3828 | 22 | |||
3829 | 23 | def test_message_with_unicode_and_unknown_encoding(self): | ||
3830 | 24 | self.iface.info(u"áéÃóú") | ||
3831 | 25 | self.iface.info("aeÃou\xc3") # UTF-8 expects a byte after \xc3 | ||
3832 | 26 | c = u"\N{REPLACEMENT CHARACTER}" | ||
3833 | 27 | self.assertEqual(self.iface.get_output_for_landscape(), | ||
3834 | 28 | u"INFO: áéÃóú\nINFO: ae%s%sou%s\n" % (c, c, c)) | ||
3835 | 29 | |||
3836 | 30 | def test_output_with_unicode_and_utf8(self): | ||
3837 | 31 | self.iface.showOutput(u"áéÃóú") | ||
3838 | 32 | self.iface.showOutput("áéÃóú") | ||
3839 | 33 | self.assertEqual(self.iface.get_output_for_landscape(), | ||
3840 | 34 | u"áéÃóúáéÃóú") | ||
3841 | 35 | 0 | ||
3842 | === modified file 'landscape/package/tests/test_releaseupgrader.py' | |||
3843 | --- landscape/package/tests/test_releaseupgrader.py 2012-03-19 09:33:34 +0000 | |||
3844 | +++ landscape/package/tests/test_releaseupgrader.py 2012-06-04 14:08:28 +0000 | |||
3845 | @@ -655,7 +655,7 @@ | |||
3846 | 655 | 655 | ||
3847 | 656 | def check_result((out, err, code)): | 656 | def check_result((out, err, code)): |
3848 | 657 | self.assertFalse(os.path.exists(upgrade_tool_directory)) | 657 | self.assertFalse(os.path.exists(upgrade_tool_directory)) |
3850 | 658 | self.assertEqual(out, "--force-smart-update\n%s\n" | 658 | self.assertEqual(out, "--force-apt-update\n%s\n" |
3851 | 659 | % os.getcwd()) | 659 | % os.getcwd()) |
3852 | 660 | self.assertEqual(err, "") | 660 | self.assertEqual(err, "") |
3853 | 661 | self.assertEqual(code, 0) | 661 | self.assertEqual(code, 0) |
3854 | @@ -738,7 +738,7 @@ | |||
3855 | 738 | result = self.upgrader.finish() | 738 | result = self.upgrader.finish() |
3856 | 739 | 739 | ||
3857 | 740 | def check_result((out, err, code)): | 740 | def check_result((out, err, code)): |
3859 | 741 | self.assertEqual(out, "--force-smart-update " | 741 | self.assertEqual(out, "--force-apt-update " |
3860 | 742 | "--config=/some/config\n") | 742 | "--config=/some/config\n") |
3861 | 743 | self.assertEqual(err, "") | 743 | self.assertEqual(err, "") |
3862 | 744 | self.assertEqual(code, 0) | 744 | self.assertEqual(code, 0) |
3863 | 745 | 745 | ||
3864 | === modified file 'landscape/package/tests/test_reporter.py' | |||
3865 | --- landscape/package/tests/test_reporter.py 2012-03-19 09:33:34 +0000 | |||
3866 | +++ landscape/package/tests/test_reporter.py 2012-06-04 14:08:28 +0000 | |||
3867 | @@ -1,4 +1,3 @@ | |||
3868 | 1 | import glob | ||
3869 | 2 | import sys | 1 | import sys |
3870 | 3 | import os | 2 | import os |
3871 | 4 | import unittest | 3 | import unittest |
3872 | @@ -17,9 +16,9 @@ | |||
3873 | 17 | PackageReporter, HASH_ID_REQUEST_TIMEOUT, main, find_reporter_command, | 16 | PackageReporter, HASH_ID_REQUEST_TIMEOUT, main, find_reporter_command, |
3874 | 18 | PackageReporterConfiguration, FakeGlobalReporter, FakeReporter) | 17 | PackageReporterConfiguration, FakeGlobalReporter, FakeReporter) |
3875 | 19 | from landscape.package import reporter | 18 | from landscape.package import reporter |
3877 | 20 | from landscape.package.facade import AptFacade, has_new_enough_apt | 19 | from landscape.package.facade import AptFacade |
3878 | 21 | from landscape.package.tests.helpers import ( | 20 | from landscape.package.tests.helpers import ( |
3880 | 22 | SmartFacadeHelper, AptFacadeHelper, SimpleRepositoryHelper, | 21 | AptFacadeHelper, SimpleRepositoryHelper, |
3881 | 23 | HASH1, HASH2, HASH3, PKGNAME1) | 22 | HASH1, HASH2, HASH3, PKGNAME1) |
3882 | 24 | from landscape.tests.helpers import ( | 23 | from landscape.tests.helpers import ( |
3883 | 25 | LandscapeTest, BrokerServiceHelper, EnvironSaverHelper) | 24 | LandscapeTest, BrokerServiceHelper, EnvironSaverHelper) |
3884 | @@ -30,18 +29,63 @@ | |||
3885 | 30 | 29 | ||
3886 | 31 | class PackageReporterConfigurationTest(unittest.TestCase): | 30 | class PackageReporterConfigurationTest(unittest.TestCase): |
3887 | 32 | 31 | ||
3889 | 33 | def test_force_smart_update_option(self): | 32 | def test_force_apt_update_option(self): |
3890 | 34 | """ | 33 | """ |
3892 | 35 | The L{PackageReporterConfiguration} supports a '--force-smart-update' | 34 | The L{PackageReporterConfiguration} supports a '--force-apt-update' |
3893 | 36 | command line option. | 35 | command line option. |
3894 | 37 | """ | 36 | """ |
3895 | 38 | config = PackageReporterConfiguration() | 37 | config = PackageReporterConfiguration() |
3902 | 39 | self.assertFalse(config.force_smart_update) | 38 | self.assertFalse(config.force_apt_update) |
3903 | 40 | config.load(["--force-smart-update"]) | 39 | config.load(["--force-apt-update"]) |
3904 | 41 | self.assertTrue(config.force_smart_update) | 40 | self.assertTrue(config.force_apt_update) |
3905 | 42 | 41 | ||
3906 | 43 | 42 | ||
3907 | 44 | class PackageReporterTestMixin(object): | 43 | class PackageReporterAptTest(LandscapeTest): |
3908 | 44 | |||
3909 | 45 | helpers = [AptFacadeHelper, SimpleRepositoryHelper, BrokerServiceHelper] | ||
3910 | 46 | |||
3911 | 47 | Facade = AptFacade | ||
3912 | 48 | |||
3913 | 49 | def setUp(self): | ||
3914 | 50 | |||
3915 | 51 | def set_up(ignored): | ||
3916 | 52 | self.store = PackageStore(self.makeFile()) | ||
3917 | 53 | self.config = PackageReporterConfiguration() | ||
3918 | 54 | self.reporter = PackageReporter( | ||
3919 | 55 | self.store, self.facade, self.remote, self.config) | ||
3920 | 56 | self.config.data_path = self.makeDir() | ||
3921 | 57 | os.mkdir(self.config.package_directory) | ||
3922 | 58 | |||
3923 | 59 | result = super(PackageReporterAptTest, self).setUp() | ||
3924 | 60 | return result.addCallback(set_up) | ||
3925 | 61 | |||
3926 | 62 | def _clear_repository(self): | ||
3927 | 63 | """Remove all packages from self.repository.""" | ||
3928 | 64 | create_file(self.repository_dir + "/Packages", "") | ||
3929 | 65 | |||
3930 | 66 | def set_pkg1_upgradable(self): | ||
3931 | 67 | """Make it so that package "name1" is considered to be upgradable. | ||
3932 | 68 | |||
3933 | 69 | Return the hash of the package that upgrades "name1". | ||
3934 | 70 | """ | ||
3935 | 71 | self._add_package_to_deb_dir( | ||
3936 | 72 | self.repository_dir, "name1", version="version2") | ||
3937 | 73 | self.facade.reload_channels() | ||
3938 | 74 | name1_upgrade = sorted(self.facade.get_packages_by_name("name1"))[1] | ||
3939 | 75 | return self.facade.get_package_hash(name1_upgrade) | ||
3940 | 76 | |||
3941 | 77 | def set_pkg1_installed(self): | ||
3942 | 78 | """Make it so that package "name1" is considered installed.""" | ||
3943 | 79 | self._install_deb_file(os.path.join(self.repository_dir, PKGNAME1)) | ||
3944 | 80 | |||
3945 | 81 | def _make_fake_apt_update(self, out="output", err="error", code=0): | ||
3946 | 82 | """Create a fake apt-update executable""" | ||
3947 | 83 | self.reporter.apt_update_filename = self.makeFile( | ||
3948 | 84 | "#!/bin/sh\n" | ||
3949 | 85 | "echo -n %s\n" | ||
3950 | 86 | "echo -n %s >&2\n" | ||
3951 | 87 | "exit %d" % (out, err, code)) | ||
3952 | 88 | os.chmod(self.reporter.apt_update_filename, 0755) | ||
3953 | 45 | 89 | ||
3954 | 46 | def test_set_package_ids_with_all_known(self): | 90 | def test_set_package_ids_with_all_known(self): |
3955 | 47 | self.store.add_hash_id_request(["hash1", "hash2"]) | 91 | self.store.add_hash_id_request(["hash1", "hash2"]) |
3956 | @@ -497,64 +541,6 @@ | |||
3957 | 497 | 541 | ||
3958 | 498 | return result | 542 | return result |
3959 | 499 | 543 | ||
3960 | 500 | def test_run_smart_update(self): | ||
3961 | 501 | """ | ||
3962 | 502 | The L{PackageReporter.run_smart_update} method should run smart-update | ||
3963 | 503 | with the proper arguments. | ||
3964 | 504 | """ | ||
3965 | 505 | self.reporter.sources_list_filename = "/I/Dont/Exist" | ||
3966 | 506 | self.reporter.sources_list_directory = "/I/Dont/Exist" | ||
3967 | 507 | self.reporter.smart_update_filename = self.makeFile( | ||
3968 | 508 | "#!/bin/sh\necho -n $@") | ||
3969 | 509 | os.chmod(self.reporter.smart_update_filename, 0755) | ||
3970 | 510 | debug_mock = self.mocker.replace("logging.debug") | ||
3971 | 511 | debug_mock("'%s' exited with status 0 (out='--after %d', err=''" % ( | ||
3972 | 512 | self.reporter.smart_update_filename, | ||
3973 | 513 | self.reporter.smart_update_interval)) | ||
3974 | 514 | warning_mock = self.mocker.replace("logging.warning") | ||
3975 | 515 | self.expect(warning_mock(ANY)).count(0) | ||
3976 | 516 | self.mocker.replay() | ||
3977 | 517 | deferred = Deferred() | ||
3978 | 518 | |||
3979 | 519 | def do_test(): | ||
3980 | 520 | |||
3981 | 521 | result = self.reporter.run_smart_update() | ||
3982 | 522 | |||
3983 | 523 | def callback((out, err, code)): | ||
3984 | 524 | interval = self.reporter.smart_update_interval | ||
3985 | 525 | self.assertEqual(err, "") | ||
3986 | 526 | self.assertEqual(out, "--after %d" % interval) | ||
3987 | 527 | self.assertEqual(code, 0) | ||
3988 | 528 | result.addCallback(callback) | ||
3989 | 529 | result.chainDeferred(deferred) | ||
3990 | 530 | |||
3991 | 531 | reactor.callWhenRunning(do_test) | ||
3992 | 532 | return deferred | ||
3993 | 533 | |||
3994 | 534 | def test_run_smart_update_with_force_smart_update(self): | ||
3995 | 535 | """ | ||
3996 | 536 | L{PackageReporter.run_smart_update} forces a smart-update run if | ||
3997 | 537 | the '--force-smart-update' command line option was passed. | ||
3998 | 538 | |||
3999 | 539 | """ | ||
4000 | 540 | self.config.load(["--force-smart-update"]) | ||
4001 | 541 | self.reporter.smart_update_filename = self.makeFile( | ||
4002 | 542 | "#!/bin/sh\necho -n $@") | ||
4003 | 543 | os.chmod(self.reporter.smart_update_filename, 0755) | ||
4004 | 544 | |||
4005 | 545 | deferred = Deferred() | ||
4006 | 546 | |||
4007 | 547 | def do_test(): | ||
4008 | 548 | result = self.reporter.run_smart_update() | ||
4009 | 549 | |||
4010 | 550 | def callback((out, err, code)): | ||
4011 | 551 | self.assertEqual(out, "") | ||
4012 | 552 | result.addCallback(callback) | ||
4013 | 553 | result.chainDeferred(deferred) | ||
4014 | 554 | |||
4015 | 555 | reactor.callWhenRunning(do_test) | ||
4016 | 556 | return deferred | ||
4017 | 557 | |||
4018 | 558 | def test_wb_apt_sources_have_changed(self): | 544 | def test_wb_apt_sources_have_changed(self): |
4019 | 559 | """ | 545 | """ |
4020 | 560 | The L{PackageReporter._apt_sources_have_changed} method returns a bool | 546 | The L{PackageReporter._apt_sources_have_changed} method returns a bool |
4021 | @@ -581,244 +567,6 @@ | |||
4022 | 581 | content="deb http://foo ./") | 567 | content="deb http://foo ./") |
4023 | 582 | self.assertTrue(self.reporter._apt_sources_have_changed()) | 568 | self.assertTrue(self.reporter._apt_sources_have_changed()) |
4024 | 583 | 569 | ||
4025 | 584 | def test_run_smart_update_with_force_smart_update_if_sources_changed(self): | ||
4026 | 585 | """ | ||
4027 | 586 | L{PackageReporter.run_smart_update} forces a smart-update run if | ||
4028 | 587 | the APT sources.list file has changed. | ||
4029 | 588 | |||
4030 | 589 | """ | ||
4031 | 590 | self.assertEqual(self.reporter.sources_list_filename, | ||
4032 | 591 | "/etc/apt/sources.list") | ||
4033 | 592 | self.reporter.sources_list_filename = self.makeFile("deb ftp://url ./") | ||
4034 | 593 | self.reporter.smart_update_filename = self.makeFile( | ||
4035 | 594 | "#!/bin/sh\necho -n $@") | ||
4036 | 595 | os.chmod(self.reporter.smart_update_filename, 0755) | ||
4037 | 596 | |||
4038 | 597 | deferred = Deferred() | ||
4039 | 598 | |||
4040 | 599 | def do_test(): | ||
4041 | 600 | result = self.reporter.run_smart_update() | ||
4042 | 601 | |||
4043 | 602 | def callback((out, err, code)): | ||
4044 | 603 | # Smart update was called without the --after parameter | ||
4045 | 604 | self.assertEqual(out, "") | ||
4046 | 605 | result.addCallback(callback) | ||
4047 | 606 | result.chainDeferred(deferred) | ||
4048 | 607 | |||
4049 | 608 | reactor.callWhenRunning(do_test) | ||
4050 | 609 | return deferred | ||
4051 | 610 | |||
4052 | 611 | def test_run_smart_update_warns_about_failures(self): | ||
4053 | 612 | """ | ||
4054 | 613 | The L{PackageReporter.run_smart_update} method should log a warning | ||
4055 | 614 | in case smart-update terminates with a non-zero exit code other than 1. | ||
4056 | 615 | """ | ||
4057 | 616 | self.reporter.smart_update_filename = self.makeFile( | ||
4058 | 617 | "#!/bin/sh\necho -n error >&2\necho -n output\nexit 2") | ||
4059 | 618 | os.chmod(self.reporter.smart_update_filename, 0755) | ||
4060 | 619 | logging_mock = self.mocker.replace("logging.warning") | ||
4061 | 620 | logging_mock("'%s' exited with status 2" | ||
4062 | 621 | " (error)" % self.reporter.smart_update_filename) | ||
4063 | 622 | self.mocker.replay() | ||
4064 | 623 | deferred = Deferred() | ||
4065 | 624 | |||
4066 | 625 | def do_test(): | ||
4067 | 626 | result = self.reporter.run_smart_update() | ||
4068 | 627 | |||
4069 | 628 | def callback((out, err, code)): | ||
4070 | 629 | self.assertEqual(out, "output") | ||
4071 | 630 | self.assertEqual(err, "error") | ||
4072 | 631 | self.assertEqual(code, 2) | ||
4073 | 632 | result.addCallback(callback) | ||
4074 | 633 | result.chainDeferred(deferred) | ||
4075 | 634 | |||
4076 | 635 | reactor.callWhenRunning(do_test) | ||
4077 | 636 | return deferred | ||
4078 | 637 | |||
4079 | 638 | def test_run_smart_update_report_smart_failure(self): | ||
4080 | 639 | """ | ||
4081 | 640 | If L{PackageReporter.run_smart_update} fails, a message is sent to the | ||
4082 | 641 | server reporting the error, to be able to fix the problem centrally. | ||
4083 | 642 | """ | ||
4084 | 643 | message_store = self.broker_service.message_store | ||
4085 | 644 | message_store.set_accepted_types(["package-reporter-result"]) | ||
4086 | 645 | self.reporter.smart_update_filename = self.makeFile( | ||
4087 | 646 | "#!/bin/sh\necho -n error >&2\necho -n output\nexit 2") | ||
4088 | 647 | os.chmod(self.reporter.smart_update_filename, 0755) | ||
4089 | 648 | deferred = Deferred() | ||
4090 | 649 | |||
4091 | 650 | def do_test(): | ||
4092 | 651 | result = self.reporter.run_smart_update() | ||
4093 | 652 | |||
4094 | 653 | def callback(ignore): | ||
4095 | 654 | self.assertMessages(message_store.get_pending_messages(), | ||
4096 | 655 | [{"type": "package-reporter-result", | ||
4097 | 656 | "code": 2, "err": u"error"}]) | ||
4098 | 657 | result.addCallback(callback) | ||
4099 | 658 | result.chainDeferred(deferred) | ||
4100 | 659 | |||
4101 | 660 | reactor.callWhenRunning(do_test) | ||
4102 | 661 | return deferred | ||
4103 | 662 | |||
4104 | 663 | def test_run_smart_update_report_no_sources(self): | ||
4105 | 664 | """ | ||
4106 | 665 | L{PackageReporter.run_smart_update} reports a failure if smart | ||
4107 | 666 | succeeds but there are no APT sources defined. Smart doesn't | ||
4108 | 667 | fail if there are no sources, but we fake a failure in order to | ||
4109 | 668 | re-use the PackageReporterAlert on the server. | ||
4110 | 669 | """ | ||
4111 | 670 | self.facade.reset_channels() | ||
4112 | 671 | message_store = self.broker_service.message_store | ||
4113 | 672 | message_store.set_accepted_types(["package-reporter-result"]) | ||
4114 | 673 | self.reporter.smart_update_filename = self.makeFile( | ||
4115 | 674 | "#!/bin/sh\necho -n error >&2\necho -n output\nexit 0") | ||
4116 | 675 | os.chmod(self.reporter.smart_update_filename, 0755) | ||
4117 | 676 | deferred = Deferred() | ||
4118 | 677 | |||
4119 | 678 | def do_test(): | ||
4120 | 679 | result = self.reporter.run_smart_update() | ||
4121 | 680 | |||
4122 | 681 | def callback(ignore): | ||
4123 | 682 | error = "There are no APT sources configured in %s or %s." % ( | ||
4124 | 683 | self.reporter.sources_list_filename, | ||
4125 | 684 | self.reporter.sources_list_directory) | ||
4126 | 685 | self.assertMessages(message_store.get_pending_messages(), | ||
4127 | 686 | [{"type": "package-reporter-result", | ||
4128 | 687 | "code": 1, "err": error}]) | ||
4129 | 688 | result.addCallback(callback) | ||
4130 | 689 | result.chainDeferred(deferred) | ||
4131 | 690 | |||
4132 | 691 | reactor.callWhenRunning(do_test) | ||
4133 | 692 | return deferred | ||
4134 | 693 | |||
4135 | 694 | def test_run_smart_update_report_smart_failure_no_sources(self): | ||
4136 | 695 | """ | ||
4137 | 696 | If L{PackageReporter.run_smart_update} fails and there are no | ||
4138 | 697 | APT sources configured, the Smart error takes precedence. | ||
4139 | 698 | """ | ||
4140 | 699 | self.facade.reset_channels() | ||
4141 | 700 | message_store = self.broker_service.message_store | ||
4142 | 701 | message_store.set_accepted_types(["package-reporter-result"]) | ||
4143 | 702 | self.reporter.smart_update_filename = self.makeFile( | ||
4144 | 703 | "#!/bin/sh\necho -n error >&2\necho -n output\nexit 2") | ||
4145 | 704 | os.chmod(self.reporter.smart_update_filename, 0755) | ||
4146 | 705 | deferred = Deferred() | ||
4147 | 706 | |||
4148 | 707 | def do_test(): | ||
4149 | 708 | result = self.reporter.run_smart_update() | ||
4150 | 709 | |||
4151 | 710 | def callback(ignore): | ||
4152 | 711 | self.assertMessages(message_store.get_pending_messages(), | ||
4153 | 712 | [{"type": "package-reporter-result", | ||
4154 | 713 | "code": 2, "err": u"error"}]) | ||
4155 | 714 | result.addCallback(callback) | ||
4156 | 715 | result.chainDeferred(deferred) | ||
4157 | 716 | |||
4158 | 717 | reactor.callWhenRunning(do_test) | ||
4159 | 718 | return deferred | ||
4160 | 719 | |||
4161 | 720 | def test_run_smart_update_report_success(self): | ||
4162 | 721 | """ | ||
4163 | 722 | L{PackageReporter.run_smart_update} also reports success to be able to | ||
4164 | 723 | know the proper state of the client. | ||
4165 | 724 | """ | ||
4166 | 725 | message_store = self.broker_service.message_store | ||
4167 | 726 | message_store.set_accepted_types(["package-reporter-result"]) | ||
4168 | 727 | self.reporter.smart_update_filename = self.makeFile( | ||
4169 | 728 | "#!/bin/sh\necho -n error >&2\necho -n output\nexit 0") | ||
4170 | 729 | os.chmod(self.reporter.smart_update_filename, 0755) | ||
4171 | 730 | deferred = Deferred() | ||
4172 | 731 | |||
4173 | 732 | def do_test(): | ||
4174 | 733 | result = self.reporter.run_smart_update() | ||
4175 | 734 | |||
4176 | 735 | def callback(ignore): | ||
4177 | 736 | self.assertMessages(message_store.get_pending_messages(), | ||
4178 | 737 | [{"type": "package-reporter-result", | ||
4179 | 738 | "code": 0, "err": u"error"}]) | ||
4180 | 739 | result.addCallback(callback) | ||
4181 | 740 | result.chainDeferred(deferred) | ||
4182 | 741 | |||
4183 | 742 | reactor.callWhenRunning(do_test) | ||
4184 | 743 | return deferred | ||
4185 | 744 | |||
4186 | 745 | def test_run_smart_update_warns_exit_code_1_and_non_empty_stderr(self): | ||
4187 | 746 | """ | ||
4188 | 747 | The L{PackageReporter.run_smart_update} method should log a warning | ||
4189 | 748 | in case smart-update terminates with exit code 1 and non empty stderr. | ||
4190 | 749 | """ | ||
4191 | 750 | self.reporter.smart_update_filename = self.makeFile( | ||
4192 | 751 | "#!/bin/sh\necho -n \"error \" >&2\nexit 1") | ||
4193 | 752 | os.chmod(self.reporter.smart_update_filename, 0755) | ||
4194 | 753 | logging_mock = self.mocker.replace("logging.warning") | ||
4195 | 754 | logging_mock("'%s' exited with status 1" | ||
4196 | 755 | " (error )" % self.reporter.smart_update_filename) | ||
4197 | 756 | self.mocker.replay() | ||
4198 | 757 | deferred = Deferred() | ||
4199 | 758 | |||
4200 | 759 | def do_test(): | ||
4201 | 760 | result = self.reporter.run_smart_update() | ||
4202 | 761 | |||
4203 | 762 | def callback((out, err, code)): | ||
4204 | 763 | self.assertEqual(out, "") | ||
4205 | 764 | self.assertEqual(err, "error ") | ||
4206 | 765 | self.assertEqual(code, 1) | ||
4207 | 766 | result.addCallback(callback) | ||
4208 | 767 | result.chainDeferred(deferred) | ||
4209 | 768 | |||
4210 | 769 | reactor.callWhenRunning(do_test) | ||
4211 | 770 | return deferred | ||
4212 | 771 | |||
4213 | 772 | def test_run_smart_update_ignores_exit_code_1_and_empty_output(self): | ||
4214 | 773 | """ | ||
4215 | 774 | The L{PackageReporter.run_smart_update} method should not log anything | ||
4216 | 775 | in case smart-update terminates with exit code 1 and output containing | ||
4217 | 776 | only a newline character. | ||
4218 | 777 | """ | ||
4219 | 778 | self.reporter.smart_update_filename = self.makeFile( | ||
4220 | 779 | "#!/bin/sh\necho\nexit 1") | ||
4221 | 780 | os.chmod(self.reporter.smart_update_filename, 0755) | ||
4222 | 781 | logging_mock = self.mocker.replace("logging.warning") | ||
4223 | 782 | self.expect(logging_mock(ANY)).count(0) | ||
4224 | 783 | self.mocker.replay() | ||
4225 | 784 | deferred = Deferred() | ||
4226 | 785 | |||
4227 | 786 | def do_test(): | ||
4228 | 787 | |||
4229 | 788 | result = self.reporter.run_smart_update() | ||
4230 | 789 | |||
4231 | 790 | def callback((out, err, code)): | ||
4232 | 791 | self.assertEqual(out, "\n") | ||
4233 | 792 | self.assertEqual(err, "") | ||
4234 | 793 | self.assertEqual(code, 1) | ||
4235 | 794 | result.addCallback(callback) | ||
4236 | 795 | result.chainDeferred(deferred) | ||
4237 | 796 | |||
4238 | 797 | reactor.callWhenRunning(do_test) | ||
4239 | 798 | return deferred | ||
4240 | 799 | |||
4241 | 800 | def test_run_smart_update_touches_stamp_file(self): | ||
4242 | 801 | """ | ||
4243 | 802 | The L{PackageReporter.run_smart_update} method touches a stamp file | ||
4244 | 803 | after running the smart-update wrapper. | ||
4245 | 804 | """ | ||
4246 | 805 | self.reporter.sources_list_filename = "/I/Dont/Exist" | ||
4247 | 806 | self.reporter.smart_update_filename = "/bin/true" | ||
4248 | 807 | deferred = Deferred() | ||
4249 | 808 | |||
4250 | 809 | def do_test(): | ||
4251 | 810 | |||
4252 | 811 | result = self.reporter.run_smart_update() | ||
4253 | 812 | |||
4254 | 813 | def callback(ignored): | ||
4255 | 814 | self.assertTrue( | ||
4256 | 815 | os.path.exists(self.config.update_stamp_filename)) | ||
4257 | 816 | result.addCallback(callback) | ||
4258 | 817 | result.chainDeferred(deferred) | ||
4259 | 818 | |||
4260 | 819 | reactor.callWhenRunning(do_test) | ||
4261 | 820 | return deferred | ||
4262 | 821 | |||
4263 | 822 | def test_remove_expired_hash_id_request(self): | 570 | def test_remove_expired_hash_id_request(self): |
4264 | 823 | request = self.store.add_hash_id_request(["hash1"]) | 571 | request = self.store.add_hash_id_request(["hash1"]) |
4265 | 824 | request.message_id = 9999 | 572 | request.message_id = 9999 |
4266 | @@ -1133,11 +881,7 @@ | |||
4267 | 1133 | 881 | ||
4268 | 1134 | upgrade_hash = self.set_pkg1_upgradable() | 882 | upgrade_hash = self.set_pkg1_upgradable() |
4269 | 1135 | self.set_pkg1_installed() | 883 | self.set_pkg1_installed() |
4275 | 1136 | # Don't reload for SmartFacade, since the hash of pkg2 will be | 884 | self.facade.reload_channels() |
4271 | 1137 | # changed, resulting in that name2 will be considered not | ||
4272 | 1138 | # available.. | ||
4273 | 1139 | if isinstance(self.facade, AptFacade): | ||
4274 | 1140 | self.facade.reload_channels() | ||
4276 | 1141 | 885 | ||
4277 | 1142 | self.store.set_hash_ids( | 886 | self.store.set_hash_ids( |
4278 | 1143 | {HASH1: 1, upgrade_hash: 2, HASH3: 3}) | 887 | {HASH1: 1, upgrade_hash: 2, HASH3: 3}) |
4279 | @@ -1193,20 +937,13 @@ | |||
4280 | 1193 | result = self.reporter.detect_packages_changes() | 937 | result = self.reporter.detect_packages_changes() |
4281 | 1194 | return result.addCallback(got_result) | 938 | return result.addCallback(got_result) |
4282 | 1195 | 939 | ||
4284 | 1196 | def test_detect_changes_considers_packages_and_locks_changes(self): | 940 | def test_detect_changes_considers_packages_changes(self): |
4285 | 1197 | """ | 941 | """ |
4289 | 1198 | The L{PackageReporter.detect_changes} method considers both package and | 942 | The L{PackageReporter.detect_changes} method package changes. |
4287 | 1199 | package locks changes. It also releases smart locks by calling the | ||
4288 | 1200 | L{SmartFacade.deinit} method. | ||
4290 | 1201 | """ | 943 | """ |
4291 | 1202 | reporter_mock = self.mocker.patch(self.reporter) | 944 | reporter_mock = self.mocker.patch(self.reporter) |
4292 | 1203 | reporter_mock.detect_packages_changes() | 945 | reporter_mock.detect_packages_changes() |
4293 | 1204 | self.mocker.result(succeed(True)) | 946 | self.mocker.result(succeed(True)) |
4294 | 1205 | reporter_mock.detect_package_locks_changes() | ||
4295 | 1206 | self.mocker.result(succeed(True)) | ||
4296 | 1207 | |||
4297 | 1208 | facade_mock = self.mocker.patch(self.facade) | ||
4298 | 1209 | facade_mock.deinit() | ||
4299 | 1210 | 947 | ||
4300 | 1211 | self.mocker.replay() | 948 | self.mocker.replay() |
4301 | 1212 | return self.reporter.detect_changes() | 949 | return self.reporter.detect_changes() |
4302 | @@ -1219,8 +956,6 @@ | |||
4303 | 1219 | """ | 956 | """ |
4304 | 1220 | reporter_mock = self.mocker.patch(self.reporter) | 957 | reporter_mock = self.mocker.patch(self.reporter) |
4305 | 1221 | reporter_mock.detect_packages_changes() | 958 | reporter_mock.detect_packages_changes() |
4306 | 1222 | self.mocker.result(succeed(False)) | ||
4307 | 1223 | reporter_mock.detect_package_locks_changes() | ||
4308 | 1224 | self.mocker.result(succeed(True)) | 959 | self.mocker.result(succeed(True)) |
4309 | 1225 | callback = self.mocker.mock() | 960 | callback = self.mocker.mock() |
4310 | 1226 | callback() | 961 | callback() |
4311 | @@ -1236,11 +971,7 @@ | |||
4312 | 1236 | 971 | ||
4313 | 1237 | results = [Deferred() for i in range(7)] | 972 | results = [Deferred() for i in range(7)] |
4314 | 1238 | 973 | ||
4320 | 1239 | # Either the Apt or Smart cache will be updated, not both. | 974 | reporter_mock.run_apt_update() |
4316 | 1240 | if isinstance(self.facade, AptFacade): | ||
4317 | 1241 | reporter_mock.run_apt_update() | ||
4318 | 1242 | else: | ||
4319 | 1243 | reporter_mock.run_smart_update() | ||
4321 | 1244 | self.mocker.result(results[0]) | 975 | self.mocker.result(results[0]) |
4322 | 1245 | 976 | ||
4323 | 1246 | reporter_mock.fetch_hash_id_db() | 977 | reporter_mock.fetch_hash_id_db() |
4324 | @@ -1306,16 +1037,19 @@ | |||
4325 | 1306 | This is done in the reporter so that we know it happens when | 1037 | This is done in the reporter so that we know it happens when |
4326 | 1307 | no other reporter is possibly running at the same time. | 1038 | no other reporter is possibly running at the same time. |
4327 | 1308 | """ | 1039 | """ |
4328 | 1040 | self._add_system_package("foo") | ||
4329 | 1041 | self.facade.reload_channels() | ||
4330 | 1042 | [foo] = self.facade.get_packages_by_name("foo") | ||
4331 | 1043 | foo_hash = self.facade.get_package_hash(foo) | ||
4332 | 1044 | self.facade.set_package_hold(foo) | ||
4333 | 1045 | self.facade.reload_channels() | ||
4334 | 1309 | message_store = self.broker_service.message_store | 1046 | message_store = self.broker_service.message_store |
4335 | 1310 | message_store.set_accepted_types(["package-locks"]) | 1047 | message_store.set_accepted_types(["package-locks"]) |
4337 | 1311 | self.store.set_hash_ids({HASH1: 3, HASH2: 4}) | 1048 | self.store.set_hash_ids({foo_hash: 3, HASH2: 4}) |
4338 | 1312 | self.store.add_available([1]) | 1049 | self.store.add_available([1]) |
4339 | 1313 | self.store.add_available_upgrades([2]) | 1050 | self.store.add_available_upgrades([2]) |
4340 | 1314 | self.store.add_installed([2]) | 1051 | self.store.add_installed([2]) |
4341 | 1315 | self.store.add_locked([3]) | 1052 | self.store.add_locked([3]) |
4342 | 1316 | self.store.add_package_locks([("name1", None, None)]) | ||
4343 | 1317 | if self.facade.supports_package_locks: | ||
4344 | 1318 | self.facade.set_package_lock("name1") | ||
4345 | 1319 | request1 = self.store.add_hash_id_request(["hash3"]) | 1053 | request1 = self.store.add_hash_id_request(["hash3"]) |
4346 | 1320 | request2 = self.store.add_hash_id_request(["hash4"]) | 1054 | request2 = self.store.add_hash_id_request(["hash4"]) |
4347 | 1321 | 1055 | ||
4348 | @@ -1328,12 +1062,6 @@ | |||
4349 | 1328 | self.assertEqual(self.store.get_available_upgrades(), [2]) | 1062 | self.assertEqual(self.store.get_available_upgrades(), [2]) |
4350 | 1329 | self.assertEqual(self.store.get_available(), [1]) | 1063 | self.assertEqual(self.store.get_available(), [1]) |
4351 | 1330 | self.assertEqual(self.store.get_installed(), [2]) | 1064 | self.assertEqual(self.store.get_installed(), [2]) |
4352 | 1331 | # XXX: Don't check get_locked() and get_package_locks() until | ||
4353 | 1332 | # package locks are implemented in AptFacade. | ||
4354 | 1333 | if not isinstance(self.facade, AptFacade): | ||
4355 | 1334 | self.assertEqual(self.store.get_locked(), [3]) | ||
4356 | 1335 | self.assertEqual( | ||
4357 | 1336 | self.store.get_package_locks(), [("name1", "", "")]) | ||
4358 | 1337 | self.assertEqual(self.store.get_hash_id_request(request1.id).id, | 1065 | self.assertEqual(self.store.get_hash_id_request(request1.id).id, |
4359 | 1338 | request1.id) | 1066 | request1.id) |
4360 | 1339 | 1067 | ||
4361 | @@ -1344,7 +1072,7 @@ | |||
4362 | 1344 | def check_result(result): | 1072 | def check_result(result): |
4363 | 1345 | 1073 | ||
4364 | 1346 | # The hashes should not go away. | 1074 | # The hashes should not go away. |
4366 | 1347 | hash1 = self.store.get_hash_id(HASH1) | 1075 | hash1 = self.store.get_hash_id(foo_hash) |
4367 | 1348 | hash2 = self.store.get_hash_id(HASH2) | 1076 | hash2 = self.store.get_hash_id(HASH2) |
4368 | 1349 | self.assertEqual([hash1, hash2], [3, 4]) | 1077 | self.assertEqual([hash1, hash2], [3, 4]) |
4369 | 1350 | 1078 | ||
4370 | @@ -1353,12 +1081,9 @@ | |||
4371 | 1353 | 1081 | ||
4372 | 1354 | # After running the resychronize task, detect_packages_changes is | 1082 | # After running the resychronize task, detect_packages_changes is |
4373 | 1355 | # called, and the existing known hashes are made available. | 1083 | # called, and the existing known hashes are made available. |
4380 | 1356 | self.assertEqual(self.store.get_available(), [3, 4]) | 1084 | self.assertEqual(self.store.get_available(), [4]) |
4381 | 1357 | self.assertEqual(self.store.get_installed(), []) | 1085 | self.assertEqual(self.store.get_installed(), [3]) |
4382 | 1358 | # XXX: Don't check get_locked() until package locks are | 1086 | self.assertEqual(self.store.get_locked(), [3]) |
4377 | 1359 | # implemented in AptFacade. | ||
4378 | 1360 | if not isinstance(self.facade, AptFacade): | ||
4379 | 1361 | self.assertEqual(self.store.get_locked(), [3]) | ||
4383 | 1362 | 1087 | ||
4384 | 1363 | # The two original hash id requests should be still there, and | 1088 | # The two original hash id requests should be still there, and |
4385 | 1364 | # a new hash id request should also be detected for HASH3. | 1089 | # a new hash id request should also be detected for HASH3. |
4386 | @@ -1371,314 +1096,14 @@ | |||
4387 | 1371 | elif request.id == request2.id: | 1096 | elif request.id == request2.id: |
4388 | 1372 | self.assertEqual(request.hashes, ["hash4"]) | 1097 | self.assertEqual(request.hashes, ["hash4"]) |
4389 | 1373 | elif not new_request_found: | 1098 | elif not new_request_found: |
4391 | 1374 | self.assertEqual(request.hashes, [HASH3]) | 1099 | self.assertEqual(request.hashes, [HASH3, HASH1]) |
4392 | 1375 | else: | 1100 | else: |
4393 | 1376 | self.fail("Unexpected hash-id request!") | 1101 | self.fail("Unexpected hash-id request!") |
4394 | 1377 | self.assertEqual(requests_count, 3) | 1102 | self.assertEqual(requests_count, 3) |
4395 | 1378 | 1103 | ||
4396 | 1379 | # XXX: Don't check for package-locks messages until package | ||
4397 | 1380 | # locks are implemented in AptFacade. | ||
4398 | 1381 | if not isinstance(self.facade, AptFacade): | ||
4399 | 1382 | self.assertMessages(message_store.get_pending_messages(), | ||
4400 | 1383 | [{"type": "package-locks", | ||
4401 | 1384 | "created": [("name1", "", "")]}]) | ||
4402 | 1385 | |||
4403 | 1386 | deferred.addCallback(check_result) | 1104 | deferred.addCallback(check_result) |
4404 | 1387 | return deferred | 1105 | return deferred |
4405 | 1388 | 1106 | ||
4406 | 1389 | |||
4407 | 1390 | class PackageReporterSmartTest(LandscapeTest, PackageReporterTestMixin): | ||
4408 | 1391 | |||
4409 | 1392 | helpers = [SmartFacadeHelper, BrokerServiceHelper] | ||
4410 | 1393 | |||
4411 | 1394 | def setUp(self): | ||
4412 | 1395 | |||
4413 | 1396 | def set_up(ignored): | ||
4414 | 1397 | self.store = PackageStore(self.makeFile()) | ||
4415 | 1398 | self.config = PackageReporterConfiguration() | ||
4416 | 1399 | self.reporter = PackageReporter( | ||
4417 | 1400 | self.store, self.facade, self.remote, self.config) | ||
4418 | 1401 | self.config.data_path = self.makeDir() | ||
4419 | 1402 | os.mkdir(self.config.package_directory) | ||
4420 | 1403 | |||
4421 | 1404 | result = super(PackageReporterSmartTest, self).setUp() | ||
4422 | 1405 | return result.addCallback(set_up) | ||
4423 | 1406 | |||
4424 | 1407 | def _clear_repository(self): | ||
4425 | 1408 | """Remove all packages from self.repository.""" | ||
4426 | 1409 | for filename in glob.glob(self.repository_dir + "/*"): | ||
4427 | 1410 | os.unlink(filename) | ||
4428 | 1411 | |||
4429 | 1412 | def set_pkg1_upgradable(self): | ||
4430 | 1413 | """Make it so that package "name1" is considered to be upgradable. | ||
4431 | 1414 | |||
4432 | 1415 | Return the hash of the package that upgrades "name1". | ||
4433 | 1416 | """ | ||
4434 | 1417 | previous = self.Facade.channels_reloaded | ||
4435 | 1418 | |||
4436 | 1419 | def callback(self): | ||
4437 | 1420 | from smart.backends.deb.base import DebUpgrades | ||
4438 | 1421 | previous(self) | ||
4439 | 1422 | pkg2 = self.get_packages_by_name("name2")[0] | ||
4440 | 1423 | pkg2.upgrades += (DebUpgrades("name1", "=", "version1-release1"),) | ||
4441 | 1424 | self.reload_cache() # Relink relations. | ||
4442 | 1425 | self.Facade.channels_reloaded = callback | ||
4443 | 1426 | return HASH2 | ||
4444 | 1427 | |||
4445 | 1428 | def set_pkg1_installed(self): | ||
4446 | 1429 | """Make it so that package "name1" is considered installed.""" | ||
4447 | 1430 | previous = self.Facade.channels_reloaded | ||
4448 | 1431 | |||
4449 | 1432 | def callback(self): | ||
4450 | 1433 | previous(self) | ||
4451 | 1434 | self.get_packages_by_name("name1")[0].installed = True | ||
4452 | 1435 | self.Facade.channels_reloaded = callback | ||
4453 | 1436 | |||
4454 | 1437 | def test_detect_packages_changes_with_locked(self): | ||
4455 | 1438 | """ | ||
4456 | 1439 | If Smart indicates locked packages we didn't know about, report | ||
4457 | 1440 | them to the server. | ||
4458 | 1441 | """ | ||
4459 | 1442 | message_store = self.broker_service.message_store | ||
4460 | 1443 | message_store.set_accepted_types(["packages"]) | ||
4461 | 1444 | |||
4462 | 1445 | self.facade.set_package_lock("name1") | ||
4463 | 1446 | self.facade.set_package_lock("name2", ">=", "version2") | ||
4464 | 1447 | |||
4465 | 1448 | self.store.set_hash_ids({HASH1: 1, HASH2: 2}) | ||
4466 | 1449 | self.store.add_available([1, 2]) | ||
4467 | 1450 | |||
4468 | 1451 | def got_result(result): | ||
4469 | 1452 | self.assertMessages(message_store.get_pending_messages(), | ||
4470 | 1453 | [{"type": "packages", "locked": [1, 2]}]) | ||
4471 | 1454 | self.assertEqual(sorted(self.store.get_locked()), [1, 2]) | ||
4472 | 1455 | |||
4473 | 1456 | result = self.reporter.detect_packages_changes() | ||
4474 | 1457 | return result.addCallback(got_result) | ||
4475 | 1458 | |||
4476 | 1459 | def test_detect_packages_changes_with_locked_and_ranges(self): | ||
4477 | 1460 | """ | ||
4478 | 1461 | Ranges are used when reporting changes to 3 or more locked packages | ||
4479 | 1462 | having consecutive ids. | ||
4480 | 1463 | """ | ||
4481 | 1464 | message_store = self.broker_service.message_store | ||
4482 | 1465 | message_store.set_accepted_types(["packages"]) | ||
4483 | 1466 | |||
4484 | 1467 | self.facade.set_package_lock("name1") | ||
4485 | 1468 | self.facade.set_package_lock("name2", ">=", "version2") | ||
4486 | 1469 | self.facade.set_package_lock("name3", "<", "version4") | ||
4487 | 1470 | |||
4488 | 1471 | self.store.set_hash_ids({HASH1: 1, HASH2: 2, HASH3: 3}) | ||
4489 | 1472 | self.store.add_available([1, 2, 3]) | ||
4490 | 1473 | |||
4491 | 1474 | def got_result(result): | ||
4492 | 1475 | self.assertMessages(message_store.get_pending_messages(), | ||
4493 | 1476 | [{"type": "packages", "locked": [(1, 3)]}]) | ||
4494 | 1477 | self.assertEqual(sorted(self.store.get_locked()), [1, 2, 3]) | ||
4495 | 1478 | |||
4496 | 1479 | result = self.reporter.detect_packages_changes() | ||
4497 | 1480 | return result.addCallback(got_result) | ||
4498 | 1481 | |||
4499 | 1482 | def test_detect_packages_changes_with_locked_with_unknown_hash(self): | ||
4500 | 1483 | """ | ||
4501 | 1484 | Locked packages whose hashes are unknown don't get reported. | ||
4502 | 1485 | """ | ||
4503 | 1486 | self.facade.set_package_lock("name1") | ||
4504 | 1487 | |||
4505 | 1488 | def got_result(result): | ||
4506 | 1489 | self.assertEqual(self.store.get_locked(), []) | ||
4507 | 1490 | |||
4508 | 1491 | result = self.reporter.detect_packages_changes() | ||
4509 | 1492 | return result.addCallback(got_result) | ||
4510 | 1493 | |||
4511 | 1494 | def test_detect_packages_changes_with_locked_and_previously_known(self): | ||
4512 | 1495 | """ | ||
4513 | 1496 | We don't report locked packages we already know about. | ||
4514 | 1497 | """ | ||
4515 | 1498 | message_store = self.broker_service.message_store | ||
4516 | 1499 | message_store.set_accepted_types(["packages"]) | ||
4517 | 1500 | |||
4518 | 1501 | self.facade.set_package_lock("name1") | ||
4519 | 1502 | self.facade.set_package_lock("name2", ">=", "version2") | ||
4520 | 1503 | |||
4521 | 1504 | self.store.set_hash_ids({HASH1: 1, HASH2: 2}) | ||
4522 | 1505 | self.store.add_available([1, 2]) | ||
4523 | 1506 | self.store.add_locked([1]) | ||
4524 | 1507 | |||
4525 | 1508 | def got_result(result): | ||
4526 | 1509 | self.assertMessages(message_store.get_pending_messages(), | ||
4527 | 1510 | [{"type": "packages", "locked": [2]}]) | ||
4528 | 1511 | |||
4529 | 1512 | self.assertEqual(sorted(self.store.get_locked()), [1, 2]) | ||
4530 | 1513 | |||
4531 | 1514 | result = self.reporter.detect_packages_changes() | ||
4532 | 1515 | return result.addCallback(got_result) | ||
4533 | 1516 | |||
4534 | 1517 | def test_detect_packages_changes_with_not_locked(self): | ||
4535 | 1518 | """ | ||
4536 | 1519 | We report when a package was previously locked and isn't anymore. | ||
4537 | 1520 | """ | ||
4538 | 1521 | message_store = self.broker_service.message_store | ||
4539 | 1522 | message_store.set_accepted_types(["packages"]) | ||
4540 | 1523 | |||
4541 | 1524 | self.store.set_hash_ids({HASH1: 1}) | ||
4542 | 1525 | self.store.add_available([1]) | ||
4543 | 1526 | self.store.add_locked([1]) | ||
4544 | 1527 | |||
4545 | 1528 | def got_result(result): | ||
4546 | 1529 | self.assertMessages(message_store.get_pending_messages(), | ||
4547 | 1530 | [{"type": "packages", "not-locked": [1]}]) | ||
4548 | 1531 | self.assertEqual(self.store.get_locked(), []) | ||
4549 | 1532 | |||
4550 | 1533 | result = self.reporter.detect_packages_changes() | ||
4551 | 1534 | return result.addCallback(got_result) | ||
4552 | 1535 | |||
4553 | 1536 | def test_detect_package_locks_changes_with_create_locks(self): | ||
4554 | 1537 | """ | ||
4555 | 1538 | If Smart indicates package locks we didn't know about, report | ||
4556 | 1539 | them to the server. | ||
4557 | 1540 | """ | ||
4558 | 1541 | message_store = self.broker_service.message_store | ||
4559 | 1542 | message_store.set_accepted_types(["package-locks"]) | ||
4560 | 1543 | |||
4561 | 1544 | self.facade.set_package_lock("name") | ||
4562 | 1545 | |||
4563 | 1546 | logging_mock = self.mocker.replace("logging.info") | ||
4564 | 1547 | logging_mock("Queuing message with changes in known package locks:" | ||
4565 | 1548 | " 1 created, 0 deleted.") | ||
4566 | 1549 | self.mocker.replay() | ||
4567 | 1550 | |||
4568 | 1551 | def got_result(result): | ||
4569 | 1552 | self.assertMessages(message_store.get_pending_messages(), | ||
4570 | 1553 | [{"type": "package-locks", | ||
4571 | 1554 | "created": [("name", "", "")]}]) | ||
4572 | 1555 | self.assertEqual(self.store.get_package_locks(), | ||
4573 | 1556 | [("name", "", "")]) | ||
4574 | 1557 | |||
4575 | 1558 | result = self.reporter.detect_package_locks_changes() | ||
4576 | 1559 | return result.addCallback(got_result) | ||
4577 | 1560 | |||
4578 | 1561 | def test_detect_package_locks_changes_with_already_known_locks(self): | ||
4579 | 1562 | """ | ||
4580 | 1563 | We don't report changes about locks we already know about. | ||
4581 | 1564 | """ | ||
4582 | 1565 | message_store = self.broker_service.message_store | ||
4583 | 1566 | message_store.set_accepted_types(["package-locks"]) | ||
4584 | 1567 | |||
4585 | 1568 | self.facade.set_package_lock("name1") | ||
4586 | 1569 | self.facade.set_package_lock("name2", "<", "1.2") | ||
4587 | 1570 | |||
4588 | 1571 | self.store.add_package_locks([("name1", "", "")]) | ||
4589 | 1572 | |||
4590 | 1573 | logging_mock = self.mocker.replace("logging.info") | ||
4591 | 1574 | logging_mock("Queuing message with changes in known package locks:" | ||
4592 | 1575 | " 1 created, 0 deleted.") | ||
4593 | 1576 | self.mocker.replay() | ||
4594 | 1577 | |||
4595 | 1578 | def got_result(result): | ||
4596 | 1579 | self.assertMessages(message_store.get_pending_messages(), | ||
4597 | 1580 | [{"type": "package-locks", | ||
4598 | 1581 | "created": [("name2", "<", "1.2")]}]) | ||
4599 | 1582 | self.assertEqual(sorted(self.store.get_package_locks()), | ||
4600 | 1583 | [("name1", "", ""), | ||
4601 | 1584 | ("name2", "<", "1.2")]) | ||
4602 | 1585 | |||
4603 | 1586 | result = self.reporter.detect_package_locks_changes() | ||
4604 | 1587 | return result.addCallback(got_result) | ||
4605 | 1588 | |||
4606 | 1589 | def test_detect_package_locks_changes_with_deleted_locks(self): | ||
4607 | 1590 | """ | ||
4608 | 1591 | If Smart indicates newly unset package locks, report them to the | ||
4609 | 1592 | server. | ||
4610 | 1593 | """ | ||
4611 | 1594 | message_store = self.broker_service.message_store | ||
4612 | 1595 | message_store.set_accepted_types(["package-locks"]) | ||
4613 | 1596 | |||
4614 | 1597 | self.store.add_package_locks([("name1", "", "")]) | ||
4615 | 1598 | |||
4616 | 1599 | logging_mock = self.mocker.replace("logging.info") | ||
4617 | 1600 | logging_mock("Queuing message with changes in known package locks:" | ||
4618 | 1601 | " 0 created, 1 deleted.") | ||
4619 | 1602 | self.mocker.replay() | ||
4620 | 1603 | |||
4621 | 1604 | def got_result(result): | ||
4622 | 1605 | self.assertMessages(message_store.get_pending_messages(), | ||
4623 | 1606 | [{"type": "package-locks", | ||
4624 | 1607 | "deleted": [("name1", "", "")]}]) | ||
4625 | 1608 | self.assertEqual(self.store.get_package_locks(), []) | ||
4626 | 1609 | |||
4627 | 1610 | result = self.reporter.detect_package_locks_changes() | ||
4628 | 1611 | return result.addCallback(got_result) | ||
4629 | 1612 | |||
4630 | 1613 | def test_detect_package_locks_changes_with_locked_already_known(self): | ||
4631 | 1614 | """ | ||
4632 | 1615 | If we didn't detect any change in the package locks, we don't send any | ||
4633 | 1616 | message, and we return a deferred resulting in C{False}. | ||
4634 | 1617 | """ | ||
4635 | 1618 | message_store = self.broker_service.message_store | ||
4636 | 1619 | message_store.set_accepted_types(["package-locks"]) | ||
4637 | 1620 | |||
4638 | 1621 | self.facade.set_package_lock("name1") | ||
4639 | 1622 | self.store.add_package_locks([("name1", "", "")]) | ||
4640 | 1623 | |||
4641 | 1624 | def got_result(result): | ||
4642 | 1625 | self.assertFalse(result) | ||
4643 | 1626 | self.assertMessages(message_store.get_pending_messages(), []) | ||
4644 | 1627 | |||
4645 | 1628 | result = self.reporter.detect_packages_changes() | ||
4646 | 1629 | return result.addCallback(got_result) | ||
4647 | 1630 | |||
4648 | 1631 | |||
4649 | 1632 | class PackageReporterAptTest(LandscapeTest, PackageReporterTestMixin): | ||
4650 | 1633 | |||
4651 | 1634 | if not has_new_enough_apt: | ||
4652 | 1635 | skip = "Can't use AptFacade on hardy" | ||
4653 | 1636 | |||
4654 | 1637 | helpers = [AptFacadeHelper, SimpleRepositoryHelper, BrokerServiceHelper] | ||
4655 | 1638 | |||
4656 | 1639 | Facade = AptFacade | ||
4657 | 1640 | |||
4658 | 1641 | def setUp(self): | ||
4659 | 1642 | |||
4660 | 1643 | def set_up(ignored): | ||
4661 | 1644 | self.store = PackageStore(self.makeFile()) | ||
4662 | 1645 | self.config = PackageReporterConfiguration() | ||
4663 | 1646 | self.reporter = PackageReporter( | ||
4664 | 1647 | self.store, self.facade, self.remote, self.config) | ||
4665 | 1648 | self.config.data_path = self.makeDir() | ||
4666 | 1649 | os.mkdir(self.config.package_directory) | ||
4667 | 1650 | |||
4668 | 1651 | result = super(PackageReporterAptTest, self).setUp() | ||
4669 | 1652 | return result.addCallback(set_up) | ||
4670 | 1653 | |||
4671 | 1654 | def _clear_repository(self): | ||
4672 | 1655 | """Remove all packages from self.repository.""" | ||
4673 | 1656 | create_file(self.repository_dir + "/Packages", "") | ||
4674 | 1657 | |||
4675 | 1658 | def set_pkg1_upgradable(self): | ||
4676 | 1659 | """Make it so that package "name1" is considered to be upgradable. | ||
4677 | 1660 | |||
4678 | 1661 | Return the hash of the package that upgrades "name1". | ||
4679 | 1662 | """ | ||
4680 | 1663 | self._add_package_to_deb_dir( | ||
4681 | 1664 | self.repository_dir, "name1", version="version2") | ||
4682 | 1665 | self.facade.reload_channels() | ||
4683 | 1666 | name1_upgrade = sorted(self.facade.get_packages_by_name("name1"))[1] | ||
4684 | 1667 | return self.facade.get_package_hash(name1_upgrade) | ||
4685 | 1668 | |||
4686 | 1669 | def set_pkg1_installed(self): | ||
4687 | 1670 | """Make it so that package "name1" is considered installed.""" | ||
4688 | 1671 | self._install_deb_file(os.path.join(self.repository_dir, PKGNAME1)) | ||
4689 | 1672 | |||
4690 | 1673 | def _make_fake_apt_update(self, out="output", err="error", code=0): | ||
4691 | 1674 | """Create a fake apt-update executable""" | ||
4692 | 1675 | self.reporter.apt_update_filename = self.makeFile( | ||
4693 | 1676 | "#!/bin/sh\n" | ||
4694 | 1677 | "echo -n %s\n" | ||
4695 | 1678 | "echo -n %s >&2\n" | ||
4696 | 1679 | "exit %d" % (out, err, code)) | ||
4697 | 1680 | os.chmod(self.reporter.apt_update_filename, 0755) | ||
4698 | 1681 | |||
4699 | 1682 | def test_run_apt_update(self): | 1107 | def test_run_apt_update(self): |
4700 | 1683 | """ | 1108 | """ |
4701 | 1684 | The L{PackageReporter.run_apt_update} method should run apt-update. | 1109 | The L{PackageReporter.run_apt_update} method should run apt-update. |
4702 | @@ -1708,13 +1133,13 @@ | |||
4703 | 1708 | reactor.callWhenRunning(do_test) | 1133 | reactor.callWhenRunning(do_test) |
4704 | 1709 | return deferred | 1134 | return deferred |
4705 | 1710 | 1135 | ||
4707 | 1711 | def test_run_apt_update_with_force_smart_update(self): | 1136 | def test_run_apt_update_with_force_apt_update(self): |
4708 | 1712 | """ | 1137 | """ |
4709 | 1713 | L{PackageReporter.run_apt_update} forces an apt-update run if the | 1138 | L{PackageReporter.run_apt_update} forces an apt-update run if the |
4711 | 1714 | '--force-smart-update' command line option was passed. | 1139 | '--force-apt-update' command line option was passed. |
4712 | 1715 | """ | 1140 | """ |
4713 | 1716 | self.makeFile("", path=self.config.update_stamp_filename) | 1141 | self.makeFile("", path=self.config.update_stamp_filename) |
4715 | 1717 | self.config.load(["--force-smart-update"]) | 1142 | self.config.load(["--force-apt-update"]) |
4716 | 1718 | self._make_fake_apt_update() | 1143 | self._make_fake_apt_update() |
4717 | 1719 | 1144 | ||
4718 | 1720 | deferred = Deferred() | 1145 | deferred = Deferred() |
4719 | @@ -1730,7 +1155,7 @@ | |||
4720 | 1730 | reactor.callWhenRunning(do_test) | 1155 | reactor.callWhenRunning(do_test) |
4721 | 1731 | return deferred | 1156 | return deferred |
4722 | 1732 | 1157 | ||
4724 | 1733 | def test_run_apt_update_with_force_smart_update_if_sources_changed(self): | 1158 | def test_run_apt_update_with_force_apt_update_if_sources_changed(self): |
4725 | 1734 | """ | 1159 | """ |
4726 | 1735 | L{PackageReporter.run_apt_update} forces an apt-update run if the APT | 1160 | L{PackageReporter.run_apt_update} forces an apt-update run if the APT |
4727 | 1736 | sources.list file has changed. | 1161 | sources.list file has changed. |
4728 | @@ -1929,7 +1354,22 @@ | |||
4729 | 1929 | return deferred | 1354 | return deferred |
4730 | 1930 | 1355 | ||
4731 | 1931 | 1356 | ||
4733 | 1932 | class GlobalPackageReporterTestMixin(object): | 1357 | class GlobalPackageReporterAptTest(LandscapeTest): |
4734 | 1358 | |||
4735 | 1359 | helpers = [AptFacadeHelper, SimpleRepositoryHelper, BrokerServiceHelper] | ||
4736 | 1360 | |||
4737 | 1361 | def setUp(self): | ||
4738 | 1362 | |||
4739 | 1363 | def set_up(ignored): | ||
4740 | 1364 | self.store = FakePackageStore(self.makeFile()) | ||
4741 | 1365 | self.config = PackageReporterConfiguration() | ||
4742 | 1366 | self.reporter = FakeGlobalReporter( | ||
4743 | 1367 | self.store, self.facade, self.remote, self.config) | ||
4744 | 1368 | self.config.data_path = self.makeDir() | ||
4745 | 1369 | os.mkdir(self.config.package_directory) | ||
4746 | 1370 | |||
4747 | 1371 | result = super(GlobalPackageReporterAptTest, self).setUp() | ||
4748 | 1372 | return result.addCallback(set_up) | ||
4749 | 1933 | 1373 | ||
4750 | 1934 | def test_store_messages(self): | 1374 | def test_store_messages(self): |
4751 | 1935 | """ | 1375 | """ |
4752 | @@ -1937,13 +1377,13 @@ | |||
4753 | 1937 | """ | 1377 | """ |
4754 | 1938 | message_store = self.broker_service.message_store | 1378 | message_store = self.broker_service.message_store |
4755 | 1939 | message_store.set_accepted_types(["package-reporter-result"]) | 1379 | message_store.set_accepted_types(["package-reporter-result"]) |
4757 | 1940 | self.reporter.smart_update_filename = self.makeFile( | 1380 | self.reporter.apt_update_filename = self.makeFile( |
4758 | 1941 | "#!/bin/sh\necho -n error >&2\necho -n output\nexit 0") | 1381 | "#!/bin/sh\necho -n error >&2\necho -n output\nexit 0") |
4760 | 1942 | os.chmod(self.reporter.smart_update_filename, 0755) | 1382 | os.chmod(self.reporter.apt_update_filename, 0755) |
4761 | 1943 | deferred = Deferred() | 1383 | deferred = Deferred() |
4762 | 1944 | 1384 | ||
4763 | 1945 | def do_test(): | 1385 | def do_test(): |
4765 | 1946 | result = self.reporter.run_smart_update() | 1386 | result = self.reporter.run_apt_update() |
4766 | 1947 | 1387 | ||
4767 | 1948 | def callback(ignore): | 1388 | def callback(ignore): |
4768 | 1949 | message = {"type": "package-reporter-result", | 1389 | message = {"type": "package-reporter-result", |
4769 | @@ -1962,47 +1402,6 @@ | |||
4770 | 1962 | return deferred | 1402 | return deferred |
4771 | 1963 | 1403 | ||
4772 | 1964 | 1404 | ||
4773 | 1965 | class GlobalPackageReporterAptTest(LandscapeTest, | ||
4774 | 1966 | GlobalPackageReporterTestMixin): | ||
4775 | 1967 | |||
4776 | 1968 | if not has_new_enough_apt: | ||
4777 | 1969 | skip = "Can't use AptFacade on hardy" | ||
4778 | 1970 | |||
4779 | 1971 | helpers = [AptFacadeHelper, SimpleRepositoryHelper, BrokerServiceHelper] | ||
4780 | 1972 | |||
4781 | 1973 | def setUp(self): | ||
4782 | 1974 | |||
4783 | 1975 | def set_up(ignored): | ||
4784 | 1976 | self.store = FakePackageStore(self.makeFile()) | ||
4785 | 1977 | self.config = PackageReporterConfiguration() | ||
4786 | 1978 | self.reporter = FakeGlobalReporter( | ||
4787 | 1979 | self.store, self.facade, self.remote, self.config) | ||
4788 | 1980 | self.config.data_path = self.makeDir() | ||
4789 | 1981 | os.mkdir(self.config.package_directory) | ||
4790 | 1982 | |||
4791 | 1983 | result = super(GlobalPackageReporterAptTest, self).setUp() | ||
4792 | 1984 | return result.addCallback(set_up) | ||
4793 | 1985 | |||
4794 | 1986 | |||
4795 | 1987 | class GlobalPackageReporterSmartTest(LandscapeTest, | ||
4796 | 1988 | GlobalPackageReporterTestMixin): | ||
4797 | 1989 | |||
4798 | 1990 | helpers = [SmartFacadeHelper, BrokerServiceHelper] | ||
4799 | 1991 | |||
4800 | 1992 | def setUp(self): | ||
4801 | 1993 | |||
4802 | 1994 | def set_up(ignored): | ||
4803 | 1995 | self.store = FakePackageStore(self.makeFile()) | ||
4804 | 1996 | self.config = PackageReporterConfiguration() | ||
4805 | 1997 | self.reporter = FakeGlobalReporter( | ||
4806 | 1998 | self.store, self.facade, self.remote, self.config) | ||
4807 | 1999 | self.config.data_path = self.makeDir() | ||
4808 | 2000 | os.mkdir(self.config.package_directory) | ||
4809 | 2001 | |||
4810 | 2002 | result = super(GlobalPackageReporterSmartTest, self).setUp() | ||
4811 | 2003 | return result.addCallback(set_up) | ||
4812 | 2004 | |||
4813 | 2005 | |||
4814 | 2006 | class FakePackageReporterTest(LandscapeTest): | 1405 | class FakePackageReporterTest(LandscapeTest): |
4815 | 2007 | 1406 | ||
4816 | 2008 | helpers = [EnvironSaverHelper, BrokerServiceHelper] | 1407 | helpers = [EnvironSaverHelper, BrokerServiceHelper] |
4817 | 2009 | 1408 | ||
4818 | === modified file 'landscape/package/tests/test_skeleton.py' | |||
4819 | --- landscape/package/tests/test_skeleton.py 2012-03-19 09:33:34 +0000 | |||
4820 | +++ landscape/package/tests/test_skeleton.py 2012-06-04 14:08:28 +0000 | |||
4821 | @@ -1,21 +1,10 @@ | |||
4822 | 1 | try: | ||
4823 | 2 | import smart | ||
4824 | 3 | from smart.cache import Package | ||
4825 | 4 | except ImportError: | ||
4826 | 5 | # Smart is optional if AptFacade is being used. | ||
4827 | 6 | pass | ||
4828 | 7 | |||
4829 | 8 | from landscape.package.interface import ( | ||
4830 | 9 | install_landscape_interface, uninstall_landscape_interface) | ||
4831 | 10 | |||
4832 | 11 | from landscape.package.facade import has_new_enough_apt | ||
4833 | 12 | from landscape.package.skeleton import ( | 1 | from landscape.package.skeleton import ( |
4835 | 13 | build_skeleton, PackageTypeError, build_skeleton_apt, DEB_PROVIDES, | 2 | build_skeleton_apt, DEB_PROVIDES, |
4836 | 14 | DEB_NAME_PROVIDES, DEB_REQUIRES, DEB_OR_REQUIRES, DEB_UPGRADES, | 3 | DEB_NAME_PROVIDES, DEB_REQUIRES, DEB_OR_REQUIRES, DEB_UPGRADES, |
4837 | 15 | DEB_CONFLICTS) | 4 | DEB_CONFLICTS) |
4838 | 16 | 5 | ||
4839 | 17 | from landscape.package.tests.helpers import ( | 6 | from landscape.package.tests.helpers import ( |
4841 | 18 | AptFacadeHelper, SmartHelper, HASH1, create_simple_repository, create_deb, | 7 | AptFacadeHelper, HASH1, create_simple_repository, create_deb, |
4842 | 19 | PKGNAME_MINIMAL, PKGDEB_MINIMAL, HASH_MINIMAL, PKGNAME_SIMPLE_RELATIONS, | 8 | PKGNAME_MINIMAL, PKGDEB_MINIMAL, HASH_MINIMAL, PKGNAME_SIMPLE_RELATIONS, |
4843 | 20 | PKGDEB_SIMPLE_RELATIONS, HASH_SIMPLE_RELATIONS, PKGNAME_VERSION_RELATIONS, | 9 | PKGDEB_SIMPLE_RELATIONS, HASH_SIMPLE_RELATIONS, PKGNAME_VERSION_RELATIONS, |
4844 | 21 | PKGDEB_VERSION_RELATIONS, HASH_VERSION_RELATIONS, | 10 | PKGDEB_VERSION_RELATIONS, HASH_VERSION_RELATIONS, |
4845 | @@ -47,16 +36,32 @@ | |||
4846 | 47 | PKGDEB_OR_RELATIONS) | 36 | PKGDEB_OR_RELATIONS) |
4847 | 48 | 37 | ||
4848 | 49 | 38 | ||
4859 | 50 | class SkeletonTestMixin(object): | 39 | class SkeletonAptTest(LandscapeTest): |
4860 | 51 | """Tests for building a skeleton from a package. | 40 | """C{PackageSkeleton} tests for apt packages.""" |
4861 | 52 | 41 | ||
4862 | 53 | This class should be mixed in to test different backends, like smart | 42 | helpers = [AptFacadeHelper, SkeletonTestHelper] |
4863 | 54 | and apt. | 43 | |
4864 | 55 | 44 | def setUp(self): | |
4865 | 56 | The main test case classes need to implement C{get_package(name)} to | 45 | super(SkeletonAptTest, self).setUp() |
4866 | 57 | get a package by name, and C{build_skeleton(package, with_info, | 46 | self.facade.add_channel_deb_dir(self.skeleton_repository_dir) |
4867 | 58 | with_unicode}, which builds the skeleton. | 47 | # Don't use reload_channels(), since that causes the test setup |
4868 | 59 | """ | 48 | # depending on build_skeleton_apt working correctly, which makes |
4869 | 49 | # it harder to do TDD for these tests. | ||
4870 | 50 | self.facade._cache.open(None) | ||
4871 | 51 | self.facade._cache.update(None) | ||
4872 | 52 | self.facade._cache.open(None) | ||
4873 | 53 | |||
4874 | 54 | def get_package(self, name): | ||
4875 | 55 | """Return the package with the specified name.""" | ||
4876 | 56 | # Don't use get_packages(), since that causes the test setup | ||
4877 | 57 | # depending on build_skeleton_apt working correctly, which makes | ||
4878 | 58 | # it harder to to TDD for these tests. | ||
4879 | 59 | package = self.facade._cache[name] | ||
4880 | 60 | return package.candidate | ||
4881 | 61 | |||
4882 | 62 | def build_skeleton(self, *args, **kwargs): | ||
4883 | 63 | """Build the skeleton to be tested.""" | ||
4884 | 64 | return build_skeleton_apt(*args, **kwargs) | ||
4885 | 60 | 65 | ||
4886 | 61 | def test_build_skeleton(self): | 66 | def test_build_skeleton(self): |
4887 | 62 | """ | 67 | """ |
4888 | @@ -253,67 +258,3 @@ | |||
4889 | 253 | (DEB_UPGRADES, "or-relations < 1.0")] | 258 | (DEB_UPGRADES, "or-relations < 1.0")] |
4890 | 254 | self.assertEqual(relations, skeleton.relations) | 259 | self.assertEqual(relations, skeleton.relations) |
4891 | 255 | self.assertEqual(HASH_OR_RELATIONS, skeleton.get_hash()) | 260 | self.assertEqual(HASH_OR_RELATIONS, skeleton.get_hash()) |
4892 | 256 | |||
4893 | 257 | |||
4894 | 258 | class SmartSkeletonTest(LandscapeTest, SkeletonTestMixin): | ||
4895 | 259 | """C{PackageSkeleton} tests for smart packages.""" | ||
4896 | 260 | |||
4897 | 261 | helpers = [SmartHelper, SkeletonTestHelper] | ||
4898 | 262 | |||
4899 | 263 | def setUp(self): | ||
4900 | 264 | super(SmartSkeletonTest, self).setUp() | ||
4901 | 265 | install_landscape_interface() | ||
4902 | 266 | self.ctrl = smart.init(interface="landscape", datadir=self.smart_dir) | ||
4903 | 267 | smart.sysconf.set( | ||
4904 | 268 | "channels", {"alias": {"type": "deb-dir", | ||
4905 | 269 | "path": self.skeleton_repository_dir}}) | ||
4906 | 270 | self.ctrl.reloadChannels() | ||
4907 | 271 | self.cache = self.ctrl.getCache() | ||
4908 | 272 | |||
4909 | 273 | def tearDown(self): | ||
4910 | 274 | uninstall_landscape_interface() | ||
4911 | 275 | super(SmartSkeletonTest, self).tearDown() | ||
4912 | 276 | |||
4913 | 277 | def get_package(self, name): | ||
4914 | 278 | """Return the package with the specified name.""" | ||
4915 | 279 | [package] = self.cache.getPackages(name) | ||
4916 | 280 | return package | ||
4917 | 281 | |||
4918 | 282 | def build_skeleton(self, *args, **kwargs): | ||
4919 | 283 | """Build the skeleton to be tested.""" | ||
4920 | 284 | return build_skeleton(*args, **kwargs) | ||
4921 | 285 | |||
4922 | 286 | def test_refuse_to_build_non_debian_packages(self): | ||
4923 | 287 | self.assertRaises(PackageTypeError, build_skeleton, | ||
4924 | 288 | Package("name", "version")) | ||
4925 | 289 | |||
4926 | 290 | |||
4927 | 291 | class SkeletonAptTest(LandscapeTest, SkeletonTestMixin): | ||
4928 | 292 | """C{PackageSkeleton} tests for apt packages.""" | ||
4929 | 293 | |||
4930 | 294 | if not has_new_enough_apt: | ||
4931 | 295 | skip = "Can't use AptFacade on hardy" | ||
4932 | 296 | |||
4933 | 297 | helpers = [AptFacadeHelper, SkeletonTestHelper] | ||
4934 | 298 | |||
4935 | 299 | def setUp(self): | ||
4936 | 300 | super(SkeletonAptTest, self).setUp() | ||
4937 | 301 | self.facade.add_channel_deb_dir(self.skeleton_repository_dir) | ||
4938 | 302 | # Don't use reload_channels(), since that causes the test setup | ||
4939 | 303 | # depending on build_skeleton_apt working correctly, which makes | ||
4940 | 304 | # it harder to to TDD for these tests. | ||
4941 | 305 | self.facade._cache.open(None) | ||
4942 | 306 | self.facade._cache.update(None) | ||
4943 | 307 | self.facade._cache.open(None) | ||
4944 | 308 | |||
4945 | 309 | def get_package(self, name): | ||
4946 | 310 | """Return the package with the specified name.""" | ||
4947 | 311 | # Don't use get_packages(), since that causes the test setup | ||
4948 | 312 | # depending on build_skeleton_apt working correctly, which makes | ||
4949 | 313 | # it harder to to TDD for these tests. | ||
4950 | 314 | package = self.facade._cache[name] | ||
4951 | 315 | return package.candidate | ||
4952 | 316 | |||
4953 | 317 | def build_skeleton(self, *args, **kwargs): | ||
4954 | 318 | """Build the skeleton to be tested.""" | ||
4955 | 319 | return build_skeleton_apt(*args, **kwargs) | ||
4956 | 320 | 261 | ||
4957 | === modified file 'landscape/package/tests/test_store.py' | |||
4958 | --- landscape/package/tests/test_store.py 2011-07-18 15:16:18 +0000 | |||
4959 | +++ landscape/package/tests/test_store.py 2012-06-04 14:08:28 +0000 | |||
4960 | @@ -1,6 +1,5 @@ | |||
4961 | 1 | import threading | 1 | import threading |
4962 | 2 | import time | 2 | import time |
4963 | 3 | import sys | ||
4964 | 4 | 3 | ||
4965 | 5 | import sqlite3 | 4 | import sqlite3 |
4966 | 6 | from landscape.tests.helpers import LandscapeTest | 5 | from landscape.tests.helpers import LandscapeTest |
4967 | @@ -356,11 +355,9 @@ | |||
4968 | 356 | 355 | ||
4969 | 357 | database = sqlite3.connect(filename) | 356 | database = sqlite3.connect(filename) |
4970 | 358 | cursor = database.cursor() | 357 | cursor = database.cursor() |
4976 | 359 | for table in ["package_locks", "locked"]: | 358 | cursor.execute("pragma table_info(locked)") |
4977 | 360 | query = "pragma table_info(%s)" % table | 359 | result = cursor.fetchall() |
4978 | 361 | cursor.execute(query) | 360 | self.assertTrue(len(result) > 0) |
4974 | 362 | result = cursor.fetchall() | ||
4975 | 363 | self.assertTrue(len(result) > 0) | ||
4979 | 364 | 361 | ||
4980 | 365 | def test_add_and_get_locked(self): | 362 | def test_add_and_get_locked(self): |
4981 | 366 | """ | 363 | """ |
4982 | @@ -401,103 +398,6 @@ | |||
4983 | 401 | self.store1.clear_locked() | 398 | self.store1.clear_locked() |
4984 | 402 | self.assertEqual(self.store2.get_locked(), []) | 399 | self.assertEqual(self.store2.get_locked(), []) |
4985 | 403 | 400 | ||
4986 | 404 | def test_get_package_locks_with_no_lock(self): | ||
4987 | 405 | """ | ||
4988 | 406 | L{PackageStore.get_package_locks} returns an empty list if no package | ||
4989 | 407 | locks are stored. | ||
4990 | 408 | """ | ||
4991 | 409 | self.assertEqual(self.store1.get_package_locks(), []) | ||
4992 | 410 | |||
4993 | 411 | def test_add_package_locks(self): | ||
4994 | 412 | """ | ||
4995 | 413 | L{PackageStore.add_package_locks} adds a package lock to the store. | ||
4996 | 414 | """ | ||
4997 | 415 | self.store1.add_package_locks([("name", "", "")]) | ||
4998 | 416 | self.assertEqual(self.store2.get_package_locks(), | ||
4999 | 417 | [("name", "", "")]) | ||
5000 | 418 |
The diff has been truncated for viewing.