-
Notifications
You must be signed in to change notification settings - Fork 1.6k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Portage: remove gentoolkit requirement for portage's python module #3870
Conversation
The package `app-portage/gentoolkit` is not a requirement for a working Gentoo/portage installation (i.e. the package is not provided in any `system` set). This prevents those hosts from using an otherwise usable module, and forces administrators who wish to use it to install an extra package (by some other method). Switch the portage ansible module to use the portage python module for checking if a package is installed. This allows ansible to work on more hosts without workarounds. Since a working python installation is required for portage to work, this does not add an extra, optional dependency.
With the inclusion of the portage module, we can access some constants and runtime variables. Unhardcode the path the the world sets file. This also makes it possible to access world sets in alternative installations when $ROOT is set.
This comment has been minimized.
This comment has been minimized.
Thanks for your contribution! Please note that right now this is a breaking change and cannot be merged as-is. The reason is that right now, the Ansible module also works when being executed with a Python interpreter on the machine which does not have the portage Python module installed. With this PR, having the portage Python module installed for the same Python this module is executed with is a hard requirement. The standard approach to handle such situations is to make the module work in both situations:
(Or even better: offer an option which configures the behavior, with default value Also, please note that every code change requires a changelog fragment. It should be a |
@0xdc nice work! Thank you for seeing it through and making the necessary changes to get it passing all tests and committed! |
- portage - Make the module work on systems without the non-system package | ||
gentoolkit. If possible, use the ansible interpreter's 'portage' module. | ||
(https://github.com/ansible-collections/community.general/pull/3870) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
- portage - Make the module work on systems without the non-system package | |
gentoolkit. If possible, use the ansible interpreter's 'portage' module. | |
(https://github.com/ansible-collections/community.general/pull/3870) | |
- portage - make the module work on systems without the non-system package | |
gentoolkit. If possible, use the Python interpreter's ``portage`` module | |
(https://github.com/ansible-collections/community.general/pull/3870). |
module.equery_path = module.get_bin_path('equery', required=True) | ||
try: | ||
import portage | ||
module.vartree = portage.vartree() |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Please don't modify module
, but instead pass vartree
and equery_path
on as explicit function parameters.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Also, this line is probably unlikely to cause an ImportError
and shouldn't be inside the try-block anyway (it could be moved to an else-block, though).
Why is it desirable to use gentoolkit at all? What system with Portage wouldn't have the Portage Python module available? That might be fragile and confusing to users in the case that the |
@ajakk that Python module is usually just available for one Python version (the system Python). Users could use another Python version for Ansible, or use a venv which has no access to the system-wide installed Python modules. If this would be a new module it would be ok to simply assume it is run with the correct Python interpreter and that module available, but this module was working before in such non-standard environments since it didn't rely on that Python module. So just changing it would be a breaking change, which isn't accepted without a long deprecation period (and during that period both ways have to be supported anyway, i.e. what this PR is now doing). |
No, the Portage Python module is available for whatever versions of Python Is there really a system on which you'd try to use Portage and also not have the Portage Python module? Edit: To clarify, I'm asking because I do not know of any case where this might break anything, so I don't know what concern there could be of breaking things. |
@ajakk you are for example ignoring the case where someone compiles their own Python (not from any Gentoo provided sources). Or does Gentoo have some magic that automatically detects any kind of Python interpreter running under it and inserts the module into it? Also there's the more common case of a virtual environment that does not include the system-wide installed modules (minimal venvs). And I'm also pretty sure that Gentoo doesn't provide a version of the Python module for every Python version that you can possibly install on it. |
I'm not ignoring anything. Hanlon's Razor. I don't see how the way Python is installed affects anything. If Portage is installed, the Portage module is available, and we know this because the Portage module is implicitly used anyway by Ansible uses the latest Python available, which is not necessarily the version for which a Portage module is installed. For example, I have Python 3.10 installed on some of my Gentoo systems. Ansible uses the latest Python available to it (3.10 for these systems), but the Portage module is (probably almost never) installed for Python 3.10 because the current Gentoo default is to build Python modules for Python 3.9, so Ansible does not work out of the box on such systems. So the breakage you're worried about is already happening. If a user is crazy enough to install Python outside of Portage (potentially breaking their Gentoo because the package manager is Python), I'm not sure why Ansible should try to be clever about it. And, on a package-by-package basis, Gentoo can provide versions of Python modules for any supported Python implementation. |
You make the assumption that the Python that's used by Ansible to execute the module (which can be configured by the user) is the same Python that's used to run |
This problem is going to exist either way by sending across For now, at the very least, it should be explicitly documented (is it already?) that the set An explicit shebang should be used and/or some check to verify the installed implementations for a module and bail out if they don't match (a hack to do this would be e.g. check whether Raising an (I'd note that considering a custom-compiled Python is out of scope in my view given we don't support that in Gentoo, not least because it wouldn't integrate with the |
Ansible does not simply copy a Python file over and executes it. It does a lot of other things as well, like replace the placeholder shebang (
So I don't really see what you folks are arguing here.
The user can tell Ansible to use such an interpreter on a Gentoo system. So yes, it is fully in scope here. |
I'm not an Ansible user (okay, I'm a very early beginner). I'm just offering a perspective as a Gentoo developer, I'm not trying to trivialise what Ansible does. I'm really pleased Gentoo even has a module! If I didn't value it, I'm not sure I'd be participating in the discussion 😉 Thanks for the links! The issue still remains about needing to align them though. It doesn't seem like a particularly huge burden to just request that if someone changes their chosen Python for use with Ansible, they verify that Portage is configured correctly to work with it. That is all my point was. Seems great that Ansible already offers that degree of flexibility. i.e. while it may be a breaking change, anyone using Gentoo and Ansible would likely be aware of the need to align things given that in the past (until very recently), the default One gets a significant benefit from this PR: not needing an external dependency.
I'm not arguing over anything - I think you may have read a bit too much into my comment. I'm just trying to give my perspective in case it's useful. I appreciate the links and information you've given 😃
I'm just pointing out that Gentoo doesn't consider such custom built packages outside of the package manager supported, so Ansible doesn't have to support it either. If it wants to, so be it. (And it's therefore, from an upstream perspective, not something to constrain the common case over.) But I did in fact offer two suggestions, which seem to fit within your parameters, to move forward rather than just "arguing" anyway. All I really wanted to add was I felt that it was a reasonable compromise to drop complexity and not bother falling back to gentoolkit given it's kind of an unconventional use of it, and document that people should align their Python impl if neither of those two options I gave are OK. |
But that's already happen, look at the diff for this PR: https://github.com/ansible-collections/community.general/pull/3870/files#diff-5b45bc5a4dfdda389c244385dcd8de464ae022cabb925972623bf5cb79e70d20R521 With this PR, the module tries to import the So there is no breaking change:
The first version of this PR completely replaced using the
Exactly, at least in case you told Ansible to use a Python interpreter which has access to the
But users can build restricted venvs that do not include system-wide libraries, so even if they use the same Python hat has the |
We can reduce complexity in the future by properly deprecating this fallback and removing it once the deprecation period expires. But we cannot do it right now, since right now there might be users which use a Python interpreter or a venv which does not provide |
Got it. Thank you for explaining! What do you think about:
It should have the same exit code behaviour as EDIT: Ah, I didn't see your second comment. I don't mind revisiting this suggestion later then. Better to not involve too many changes at once. |
Sounds like a good idea. (Assuming that But this can also be done in a separate / follow-up PR; doing too much in one PR is usually no the best idea :) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Additionally to Felix's sentiments, here's a few more maintainability/readability comments from me:
cmd = '%s list %s' % (module.equery_path, atom) | ||
try: | ||
installed = module.vartree.dbapi.match(atom) | ||
return bool(installed) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Could you move this to an else-block?
try: | ||
# When providing ROOT via ansible enviroment, portage.root may return a | ||
# portage._LegacyGlobalProxy instance. Force it to be a string. | ||
world_sets_path = os.path.join(str(portage.root), portage.const.WORLD_SETS_FILE) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
What part of this nested thing can actually cause a NameError
? It's hard to guess, could you wrap with try-except just that, and move the rest to an else-block?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Actually portage
is the thing that triggers the NameError
. But it always does, since portage
is not passed here. This needs to be fixed.
# portage._LegacyGlobalProxy instance. Force it to be a string. | ||
world_sets_path = os.path.join(str(portage.root), portage.const.WORLD_SETS_FILE) | ||
except NameError: | ||
world_sets_path = "/var/lib/portage/world_sets" |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Nit: changing the quotation style unnecessarily makes the diff harder to read, try to avoid such unrelated changes to make it easier for the reviewers to catch up with what's going on in the PR.
@@ -506,7 +515,12 @@ def main(): | |||
) | |||
|
|||
module.emerge_path = module.get_bin_path('emerge', required=True) | |||
module.equery_path = module.get_bin_path('equery', required=True) | |||
try: | |||
import portage |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Importing things in the middle of the control flow is an antipattern. A typical idiom used virtually everywhere is having try-except at the top of the module and then having if-else here.
module.equery_path = module.get_bin_path('equery', required=True) | ||
try: | ||
import portage | ||
module.vartree = portage.vartree() |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Also, this line is probably unlikely to cause an ImportError
and shouldn't be inside the try-block anyway (it could be moved to an else-block, though).
@0xdc can you please take a look at the review comments? |
Hi Felix, I haven't started making changes as per the reviews yet, but I did follow along with the discussions. I think that converting this module to use |
Ok. I hope someone will actually implement that though :) |
SUMMARY
The package
app-portage/gentoolkit
is not a requirement for a working Gentoo/portage installation (i.e. the package is not provided in anysystem
set). This prevents those hosts from using an otherwise usable module, and forces administrators who wish to use it to install an extra package (by some other method).Switch the portage ansible module to use the portage python module for checking if a package is installed. This allows ansible to work on more hosts without workarounds.
Since a working python installation is required for portage to work, this does not add an extra, optional dependency.
ISSUE TYPE
COMPONENT NAME
portage
ADDITIONAL INFORMATION
Before this change:
After this change: