Apt: stop upgrading wildcard packages with state=present

This is meant to be a discussion around issue #84466, where it was requested I open a topic here to determine whether this change should be implemented.

Basically the issue at hand is to stop the apt module from upgrading packages except when using state=latest. Currently if you use state=present, and your package name contains a glob (e.g. foo=1.*), then using state=present will still involve installing the latest version, even if the currently installed version satisfies the requirement.

The only reason I’ve heard for desiring this behavior, is that “because when you use a glob, that’s how apt behaves” (this includes variant arguments like “when you query the apt api with a glob, that’s how it behaves”. Plus the way an API behaves is an implementation detail, and should have no bearing on user interface/experience). This glob argument is flawed. The glob has absolutely nothing to do with apt returning the latest version. If you query a package without a glob, apt gives you the latest version. Yes, if you do apt install foo from the command line, it installs the latest version. Yet we don’t install the latest version if you do name=foo state=present. Therefore this argument regarding globs is completely invalid.

Another argument that might come up against the change is that “we would have to add the logic to the module to do this filtering there instead of letting apt do it”. This argument is also invalid. We already do that filtering, and not the apt module. In fact implementing this change would result in less code, as there is code in the module specifically to enable this behavior (because the behavior is an exception to normal flow).

As such, the only remaining argument I can see is “it would be a breaking change”. That argument is valid. However the counter to that is that’s not documented, and in fact the documentation says the opposite. It is both documented, and well understood by the community (IMHO) that using state=present will not upgrade a package, and is there to ensure the specified criteria is met. It also means that state=latest is useless, as it is effectively no different from state=present. Thus this behavior violates what is documented, and violates the principal of least surprise. I would consider addressing this to be more of a bug fix than anything else.

If we are afraid of changing the behavior, maybe we provide an option to preserve the behavior, and emit a warning that it’s deprecated and will go away in a future version. I suspect this glob usage is not very common, so I expect any undesired impact (meaning people who expect the behavior, and don’t consider it a bug) would be minimal.

There is also no simple way to obtain the behavior and keep ansible from upgrading if an upgrade is available. You would have to implement all sorts of messy and complicated logic such as 1) check if package is installed 2) if not installed, then install it 3) add a preferences file to /etc/apt to prevent further upgrades. But then if you do want to actually upgrade, you have to 1) remove the mask 2) check if upgrade is available 3) if so, do upgrade 4) put mask back. You could also do this logic manually with multiple ansible tasks to query the currently installed version, check if it matches the glob, and if so skip the install task. But all these options are IMHO excessively complicated to address an exception to the normal/expected behavior.

 
So ultimately, what arguments are there for keeping this behavior? What direction do we want to go?

I agree that the current behavior is surprising and not what I would expect. (But then, I also wouldn’t use wildcards in package names.)

The current behavior has been there for many many years, so even if it is not documented and is surprising if you don’t know about it, changing it is a major change for users of this ‘feature’. IMO this must come with a deprecation period and must definitely not happen in a bugfix release. (What should happen in a bugfix release is updating the documentation to match the actual behavior.)

If the behavior is changed, it is probably best to first make it configurable, then deprecate the default value of the configuration, and eventually (when the deprecation period is over) flip the default. That would minimize the bad side-effects of changing this long-time behavior, and allow users to change to the behavior they want / expect as soon as the configuration is there.