Skip to content

Commit 6be2fbb

Browse files
committed
Merge pull request ansible#323 from ansible/apt-wildcard
Build on mvo5's pull request to implement apt version wildcard
2 parents 4bc1e46 + 0c312e4 commit 6be2fbb

File tree

1 file changed

+53
-21
lines changed

1 file changed

+53
-21
lines changed

packaging/os/apt.py

Lines changed: 53 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -167,7 +167,7 @@
167167
HAS_PYTHON_APT = False
168168

169169
def package_split(pkgspec):
170-
parts = pkgspec.split('=')
170+
parts = pkgspec.split('=', 1)
171171
if len(parts) > 1:
172172
return parts[0], parts[1]
173173
else:
@@ -205,19 +205,34 @@ def package_status(m, pkgname, version, cache, state):
205205
# assume older version of python-apt is installed
206206
package_is_installed = pkg.isInstalled
207207

208-
if version and package_is_installed:
208+
if version:
209209
try:
210210
installed_version = pkg.installed.version
211211
except AttributeError:
212212
installed_version = pkg.installedVersion
213-
return package_is_installed and fnmatch.fnmatch(installed_version, version), False, has_files
213+
214+
avail_upgrades = fnmatch.filter((p.version for p in pkg.versions), version)
215+
216+
if package_is_installed:
217+
# Only claim the package is installed if the version is matched as well
218+
package_is_installed = fnmatch.fnmatch(installed_version, version)
219+
220+
# Only claim the package is upgradable if a candidate matches the version
221+
package_is_upgradable = False
222+
for candidate in avail_upgrades:
223+
if pkg.versions[candidate] > p.installed:
224+
package_is_upgradable = True
225+
break
226+
else:
227+
package_is_upgradable = bool(avail_upgrades)
214228
else:
215229
try:
216230
package_is_upgradable = pkg.is_upgradable
217231
except AttributeError:
218232
# assume older version of python-apt is installed
219233
package_is_upgradable = pkg.isUpgradable
220-
return package_is_installed, package_is_upgradable, has_files
234+
235+
return package_is_installed, package_is_upgradable, has_files
221236

222237
def expand_dpkg_options(dpkg_options_compressed):
223238
options_list = dpkg_options_compressed.split(',')
@@ -229,39 +244,54 @@ def expand_dpkg_options(dpkg_options_compressed):
229244

230245
def expand_pkgspec_from_fnmatches(m, pkgspec, cache):
231246
new_pkgspec = []
232-
for pkgname_or_fnmatch_pattern in pkgspec:
233-
# note that any of these chars is not allowed in a (debian) pkgname
234-
if [c for c in pkgname_or_fnmatch_pattern if c in "*?[]!"]:
235-
if "=" in pkgname_or_fnmatch_pattern:
236-
m.fail_json(msg="pkgname wildcard and version can not be mixed")
247+
for pkgspec_pattern in pkgspec:
248+
pkgname_pattern, version = package_split(pkgspec_pattern)
249+
250+
# note that none of these chars is allowed in a (debian) pkgname
251+
if frozenset('*?[]!').intersection(pkgname_pattern):
237252
# handle multiarch pkgnames, the idea is that "apt*" should
238253
# only select native packages. But "apt*:i386" should still work
239-
if not ":" in pkgname_or_fnmatch_pattern:
240-
matches = fnmatch.filter(
241-
[pkg.name for pkg in cache
242-
if not ":" in pkg.name], pkgname_or_fnmatch_pattern)
254+
if not ":" in pkgname_pattern:
255+
try:
256+
pkg_name_cache = _non_multiarch
257+
except NameError:
258+
pkg_name_cache = _non_multiarch = [pkg.name for pkg in cache if not ':' in pkg.name]
243259
else:
244-
matches = fnmatch.filter(
245-
[pkg.name for pkg in cache], pkgname_or_fnmatch_pattern)
260+
try:
261+
pkg_name_cache = _all_pkg_names
262+
except NameError:
263+
pkg_name_cache = _all_pkg_names = [pkg.name for pkg in cache]
264+
matches = fnmatch.filter(pkg_name_cache, pkgname_pattern)
246265

247266
if len(matches) == 0:
248-
m.fail_json(msg="No package(s) matching '%s' available" % str(pkgname_or_fnmatch_pattern))
267+
m.fail_json(msg="No package(s) matching '%s' available" % str(pkgname_pattern))
249268
else:
250269
new_pkgspec.extend(matches)
251270
else:
252-
new_pkgspec.append(pkgname_or_fnmatch_pattern)
271+
# No wildcards in name
272+
new_pkgspec.append(pkgspec_pattern)
253273
return new_pkgspec
254274

255275
def install(m, pkgspec, cache, upgrade=False, default_release=None,
256276
install_recommends=True, force=False,
257277
dpkg_options=expand_dpkg_options(DPKG_OPTIONS)):
278+
pkg_list = []
258279
packages = ""
259280
pkgspec = expand_pkgspec_from_fnmatches(m, pkgspec, cache)
260281
for package in pkgspec:
261282
name, version = package_split(package)
262283
installed, upgradable, has_files = package_status(m, name, version, cache, state='install')
263284
if not installed or (upgrade and upgradable):
264-
packages += "'%s' " % package
285+
pkg_list.append("'%s'" % package)
286+
if installed and upgradable and version:
287+
# This happens when the package is installed, a newer version is
288+
# available, and the version is a wildcard that matches both
289+
#
290+
# We do not apply the upgrade flag because we cannot specify both
291+
# a version and state=latest. (This behaviour mirrors how apt
292+
# treats a version with wildcard in the package)
293+
pkg_list.append("'%s'" % package)
294+
packages = ' '.join(pkg_list)
265295

266296
if len(packages) != 0:
267297
if force:
@@ -350,13 +380,14 @@ def install_deb(m, debs, cache, force, install_recommends, dpkg_options):
350380

351381
def remove(m, pkgspec, cache, purge=False,
352382
dpkg_options=expand_dpkg_options(DPKG_OPTIONS)):
353-
packages = ""
383+
pkg_list = []
354384
pkgspec = expand_pkgspec_from_fnmatches(m, pkgspec, cache)
355385
for package in pkgspec:
356386
name, version = package_split(package)
357387
installed, upgradable, has_files = package_status(m, name, version, cache, state='remove')
358388
if installed or (has_files and purge):
359-
packages += "'%s' " % package
389+
pkg_list.append("'%s'" % package)
390+
packages = ' '.join(pkg_list)
360391

361392
if len(packages) == 0:
362393
m.exit_json(changed=False)
@@ -567,4 +598,5 @@ def main():
567598
# import module snippets
568599
from ansible.module_utils.basic import *
569600

570-
main()
601+
if __name__ == "__main__":
602+
main()

0 commit comments

Comments
 (0)