Merge lp:~bjornt/landscape-client/apt-upgrade-all-packages into lp:~landscape/landscape-client/trunk

Proposed by Björn Tillenius
Status: Merged
Approved by: Alberto Donato
Approved revision: 435
Merged at revision: 432
Proposed branch: lp:~bjornt/landscape-client/apt-upgrade-all-packages
Merge into: lp:~landscape/landscape-client/trunk
Diff against target: 273 lines (+81/-39)
3 files modified
landscape/package/changer.py (+1/-3)
landscape/package/facade.py (+14/-15)
landscape/package/tests/test_facade.py (+66/-21)
To merge this branch: bzr merge lp:~bjornt/landscape-client/apt-upgrade-all-packages
Reviewer Review Type Date Requested Status
Alberto Donato (community) Approve
Free Ekanayaka (community) Approve
Review via email: mp+88688@code.launchpad.net

Description of the change

Ask Apt to upgrade the whole system, rather than marking each package
for upgrade when the server requests a general system upgrade. This
makes the system upgrade holds for dependencies.

I'm making a dist-upgrade, because the existing tests require it. It's
easy to change, if we want a normal apt-get upgrade.

To post a comment you must log in.
Revision history for this message
Free Ekanayaka (free.ekanayaka) wrote :

I believe we don't want dist-upgrade, as it can have quite disruptive effects like removing packages, or replacing postgres-8 with postgres-9 (in principle).

Also I didn't look at the code very in depth, but will this have really the same behavior as before? The important thing is to go back to the server if there are package changes that haven't been explicitly requested by the server (with install/remove messages) and that are not allowed by the auto-apply policy in effect.

review: Needs Information
435. By Björn Tillenius

Merge trunk, resolve conflicts.

Revision history for this message
Björn Tillenius (bjornt) wrote :

We can change it to use upgrade instead of dist-upgrade. However, I do think that the current Smart behavior is like dist-upgrade. I will do some testing to confirm, it's easy to change in another branch.

And yes, it will tell the changes to the server. cache.upgrade() doesn't upgrade the packages. You have to call cache.commit() for the changes to take place. But since the changes aren't in _package_installs/_package_removals, a DependencyError is raised before the commit.

Revision history for this message
Free Ekanayaka (free.ekanayaka) wrote :

Thanks for explaining Bjorn, there's even a test raising DependencyError indeed.

So I guess the current Smart behavior is like dist-ugprade because we use PolicyUpgrade? I don't know the details of that, it might indeed be similar to dist-upgrade. However, dist-upgrade is really meant for cross-release upgrades ("distribution upgrades") not for regular updates, and we have separate machinery for release upgrades, so I'd really advocate for a plain apt-get upgrade.

Looks great, +1!

review: Approve
Revision history for this message
Alberto Donato (ack) wrote :

looks good! +1

review: Approve

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
1=== modified file 'landscape/package/changer.py'
2--- landscape/package/changer.py 2011-12-13 10:29:04 +0000
3+++ landscape/package/changer.py 2012-01-16 15:12:26 +0000
4@@ -169,9 +169,7 @@
5 self._facade.reset_marks()
6
7 if upgrade:
8- for package in self._facade.get_packages():
9- if self._facade.is_package_installed(package):
10- self._facade.mark_upgrade(package)
11+ self._facade.mark_global_upgrade()
12
13 for ids, mark_func in [(install, self._facade.mark_install),
14 (remove, self._facade.mark_remove)]:
15
16=== modified file 'landscape/package/facade.py'
17--- landscape/package/facade.py 2012-01-13 09:37:40 +0000
18+++ landscape/package/facade.py 2012-01-16 15:12:26 +0000
19@@ -90,7 +90,7 @@
20 self._pkg2hash = {}
21 self._hash2pkg = {}
22 self._package_installs = []
23- self._package_upgrades = []
24+ self._global_upgrade = False
25 self._package_removals = []
26 self.refetch_package_index = False
27
28@@ -363,7 +363,7 @@
29 held_package_names = set()
30 package_changes = self._package_installs[:]
31 package_changes.extend(self._package_removals)
32- if not package_changes and not self._package_upgrades:
33+ if not package_changes and not self._global_upgrade:
34 return None
35 fixer = apt_pkg.ProblemResolver(self._cache._depcache)
36 for version in self._package_installs:
37@@ -377,14 +377,8 @@
38 # been True.
39 fixer.clear(version.package._pkg)
40 fixer.protect(version.package._pkg)
41- for version in self._package_upgrades:
42- if self._is_package_held(version.package):
43- continue
44- version.package.mark_install(
45- auto_fix=False,
46- from_user=not version.package.is_auto_installed)
47- fixer.clear(version.package._pkg)
48- fixer.protect(version.package._pkg)
49+ if self._global_upgrade:
50+ self._cache.upgrade(dist_upgrade=True)
51 for version in self._package_removals:
52 if self._is_package_held(version.package):
53 held_package_names.add(version.package.name)
54@@ -448,17 +442,16 @@
55 def reset_marks(self):
56 """Clear the pending package operations."""
57 del self._package_installs[:]
58- del self._package_upgrades[:]
59 del self._package_removals[:]
60+ self._global_upgrade = False
61
62 def mark_install(self, version):
63 """Mark the package for installation."""
64 self._package_installs.append(version)
65
66- def mark_upgrade(self, version):
67- """Mark the package for upgrade."""
68- if version.package.candidate != version:
69- self._package_upgrades.append(version)
70+ def mark_global_upgrade(self):
71+ """Upgrade all installed packages."""
72+ self._global_upgrade = True
73
74 def mark_remove(self, version):
75 """Mark the package for removal."""
76@@ -626,6 +619,12 @@
77 def mark_upgrade(self, pkg):
78 self._marks[pkg] = UPGRADE
79
80+ def mark_global_upgrade(self):
81+ """Upgrade all installed packages."""
82+ for package in self.get_packages():
83+ if self.is_package_installed(package):
84+ self.mark_upgrade(package)
85+
86 def reset_marks(self):
87 self._marks.clear()
88
89
90=== modified file 'landscape/package/tests/test_facade.py'
91--- landscape/package/tests/test_facade.py 2012-01-16 14:12:40 +0000
92+++ landscape/package/tests/test_facade.py 2012-01-16 15:12:26 +0000
93@@ -905,14 +905,13 @@
94 self.facade.reload_channels()
95 foo = self.facade.get_packages_by_name("foo")[0]
96 self.facade.mark_install(foo)
97- bar_10 = sorted(self.facade.get_packages_by_name("bar"))[0]
98- self.facade.mark_upgrade(bar_10)
99+ self.facade.mark_global_upgrade()
100 [baz] = self.facade.get_packages_by_name("baz")
101 self.facade.mark_remove(baz)
102 self.facade.reset_marks()
103 self.assertEqual(self.facade._package_installs, [])
104- self.assertEqual(self.facade._package_upgrades, [])
105 self.assertEqual(self.facade._package_removals, [])
106+ self.assertFalse(self.facade._global_upgrade)
107 self.assertEqual(self.facade.perform_changes(), None)
108
109 def test_wb_mark_install_adds_to_list(self):
110@@ -930,10 +929,10 @@
111 install = self.facade._package_installs[0]
112 self.assertEqual("minimal", install.package.name)
113
114- def test_wb_mark_upgrade_adds_to_list(self):
115+ def test_wb_mark_global_upgrade_sets_variable(self):
116 """
117- C{mark_upgrade} adds the package to the list of packages to be
118- upgraded.
119+ C{mark_global_upgrade} sets a variable, so that the actual
120+ upgrade happens in C{perform_changes}.
121 """
122 deb_dir = self.makeDir()
123 self._add_system_package("foo", version="1.0")
124@@ -941,8 +940,9 @@
125 self.facade.add_channel_apt_deb("file://%s" % deb_dir, "./")
126 self.facade.reload_channels()
127 foo_10 = sorted(self.facade.get_packages_by_name("foo"))[0]
128- self.facade.mark_upgrade(foo_10)
129- self.assertEqual([foo_10], self.facade._package_upgrades)
130+ self.facade.mark_global_upgrade()
131+ self.assertTrue(self.facade._global_upgrade)
132+ self.assertEqual(foo_10, foo_10.package.installed)
133
134 def test_wb_mark_remove_adds_to_list(self):
135 """
136@@ -1062,7 +1062,27 @@
137 ("single-arch", "2.0")],
138 sorted(changes))
139
140- def test_mark_upgrade_candidate_version(self):
141+ def test_mark_global_upgrade(self):
142+ """
143+ C{mark_global_upgrade} upgrades all packages that can be
144+ upgraded. It makes C{perform_changes} raise a C{DependencyError}
145+ with the required changes, so that the user can review the
146+ changes and approve them.
147+ """
148+ deb_dir = self.makeDir()
149+ self._add_system_package("foo", version="1.0")
150+ self._add_system_package("bar")
151+ self._add_package_to_deb_dir(deb_dir, "foo", version="2.0")
152+ self._add_package_to_deb_dir(deb_dir, "baz")
153+ self.facade.add_channel_apt_deb("file://%s" % deb_dir, "./")
154+ self.facade.reload_channels()
155+ foo2 = sorted(self.facade.get_packages_by_name("foo"))[1]
156+ self.facade.mark_global_upgrade()
157+ exception = self.assertRaises(
158+ DependencyError, self.facade.perform_changes)
159+ self.assertEqual([foo2], exception.packages)
160+
161+ def test_mark_global_upgrade_candidate_version(self):
162 """
163 If more than one version is available, the package will be
164 upgraded to the candidate version. Since the user didn't request
165@@ -1077,16 +1097,16 @@
166 self.facade.reload_channels()
167 foo1, foo2, foo3 = sorted(self.facade.get_packages_by_name("foo"))
168 self.assertEqual(foo3, foo1.package.candidate)
169- self.facade.mark_upgrade(foo1)
170+ self.facade.mark_global_upgrade()
171 exception = self.assertRaises(
172 DependencyError, self.facade.perform_changes)
173 self.assertEqual([foo3], exception.packages)
174
175- def test_mark_upgrade_no_upgrade(self):
176+ def test_mark_global_upgrade_no_upgrade(self):
177 """
178 If the candidate version of a package is already installed,
179- mark_upgrade() won't request an upgrade to be made. I.e.
180- perform_changes() won't do anything.
181+ C{mark_global_upgrade()} won't request an upgrade to be made. I.e.
182+ C{perform_changes()} won't do anything.
183 """
184 deb_dir = self.makeDir()
185 self._add_system_package("foo", version="3.0")
186@@ -1096,10 +1116,10 @@
187 self.facade.reload_channels()
188 foo3 = sorted(self.facade.get_packages_by_name("foo"))[-1]
189 self.assertEqual(foo3, foo3.package.candidate)
190- self.facade.mark_upgrade(foo3)
191+ self.facade.mark_global_upgrade()
192 self.assertEqual(None, self.facade.perform_changes())
193
194- def test_mark_upgrade_preserves_auto(self):
195+ def test_mark_global_upgrade_preserves_auto(self):
196 """
197 Upgrading a package will retain its auto-install status.
198 """
199@@ -1114,8 +1134,7 @@
200 noauto1, noauto2 = sorted(self.facade.get_packages_by_name("noauto"))
201 auto1.package.mark_auto(True)
202 noauto1.package.mark_auto(False)
203- self.facade.mark_upgrade(auto1)
204- self.facade.mark_upgrade(noauto1)
205+ self.facade.mark_global_upgrade()
206 self.assertRaises(DependencyError, self.facade.perform_changes)
207 self.assertTrue(auto2.package.is_auto_installed)
208 self.assertFalse(noauto2.package.is_auto_installed)
209@@ -1211,7 +1230,7 @@
210 error = self.assertRaises(DependencyError, self.facade.perform_changes)
211 self.assertEqual([bar], error.packages)
212
213- def test_mark_upgrade_dependency_error(self):
214+ def test_mark_global_upgrade_dependency_error(self):
215 """
216 If a package is marked for upgrade, a DependencyError will be
217 raised, indicating which version of the package will be
218@@ -1226,7 +1245,7 @@
219 self.facade.reload_channels()
220 foo_10, foo_15 = sorted(self.facade.get_packages_by_name("foo"))
221 [bar] = self.facade.get_packages_by_name("bar")
222- self.facade.mark_upgrade(foo_10)
223+ self.facade.mark_global_upgrade()
224 error = self.assertRaises(DependencyError, self.facade.perform_changes)
225 self.assertEqual(
226 sorted([bar, foo_15], key=self.version_sortkey),
227@@ -1266,7 +1285,7 @@
228 "Can't perform the changes, since the following packages" +
229 " are held: bar, foo", error.args[0])
230
231- def test_mark_upgrade_held_packages(self):
232+ def test_mark_global_upgrade_held_packages(self):
233 """
234 If a package that is on hold is marked for upgrade,
235 C{perform_changes} won't request to install a newer version of
236@@ -1280,10 +1299,36 @@
237 self.facade.add_channel_apt_deb("file://%s" % deb_dir, "./")
238 self.facade.reload_channels()
239 [foo_10, foo_15] = sorted(self.facade.get_packages_by_name("foo"))
240- self.facade.mark_upgrade(foo_10)
241+ self.facade.mark_global_upgrade()
242 self.assertEqual(None, self.facade.perform_changes())
243 self.assertEqual(foo_10, foo_15.package.installed)
244
245+ def test_mark_global_upgrade_held_dependencies(self):
246+ """
247+ If a package that can be upgraded, but that package depends on a
248+ newer version of a held package, the first package won't be
249+ marked as requiring upgrade.
250+ """
251+ self._add_system_package(
252+ "foo", version="1.0",
253+ control_fields={"Status": "hold ok installed"})
254+ self._add_system_package(
255+ "bar", version="1.0",
256+ control_fields={"Depends": "foo"})
257+ deb_dir = self.makeDir()
258+ self._add_package_to_deb_dir(deb_dir, "foo", version="2.0")
259+ self._add_package_to_deb_dir(
260+ deb_dir, "bar", version="2.0",
261+ control_fields={"Depends": "foo (>> 1.0)"})
262+ self.facade.add_channel_apt_deb("file://%s" % deb_dir, "./")
263+ self.facade.reload_channels()
264+ [foo_1, foo_2] = sorted(self.facade.get_packages_by_name("foo"))
265+ [bar_1, bar_2] = sorted(self.facade.get_packages_by_name("bar"))
266+ self.facade.mark_global_upgrade()
267+ self.assertEqual(None, self.facade.perform_changes())
268+ self.assertEqual(foo_1, foo_2.package.installed)
269+ self.assertEqual(bar_1, bar_2.package.installed)
270+
271 def test_get_locked_packages_simple(self):
272 """
273 C{get_locked_packages} returns all packages that are marked as

Subscribers

People subscribed via source and target branches

to all changes: