ansible / ansible-builder

An Ansible execution environment builder
Other
289 stars 93 forks source link

Account for PEP668 in pip invocations #627

Closed Shrews closed 7 months ago

Shrews commented 10 months ago

Fixes #604 Fixes #646

nitzmahone commented 10 months ago

This looks good, but was just playing with it while testing some other core PEP668 work Matt C and I were doing and tripped over a nasty problem that's only apparent when actually running in a PEP668-marked environment: it doesn't look like ensurepip can respect the envvar, and will thus refuse to work at all.

It doesn't look like any RHELish OSs are marking things yet (only recent Debian/Ubuntu), but it's easy to fake it up for testing using the following as your base image:

FROM fedora:39
RUN touch /usr/lib64/python3.12/EXTERNALLY-MANAGED

Build that locally, then use the resultant image as your base in a vanilla EE with the default Python settings, and it'll :bomb: hard on ensurepip. It explicitly blocks inheritance of all PIP_ envvars when pip is launched to bootstrap itself, and there's no other apparent way to pass that through.

So either we'll need to look up, zap (and possibly re-create?) the marker file, not use ensurepip, or ... something else.

Shrews commented 10 months ago

Well that's a bunch of poo... For reference, this builder EE is enough show the error:

---
version: 3

images:
  base_image:
    name: docker.io/library/fedora:39

additional_build_steps:
  prepend_base:
    - RUN touch /usr/lib64/python3.12/EXTERNALLY-MANAGED
sivel commented 10 months ago

I have no idea if this is intended behavior, but you can also bypass EXTERNALLY-MANAGED by just supplying --root, with either pip or ensurepip. If you set --root / that works just like we do now.

[root@8f8a1523eade /]# touch /usr/lib64/python3.12/EXTERNALLY-MANAGED
[root@8f8a1523eade /]# python3 -m ensurepip
error: externally-managed-environment

× This environment is externally managed
╰─> The Python environment under /usr is managed externally, and may not be
    manipulated by the user. Please use specific tooling from the distributor of
    the Python installation to interact with this environment instead.

note: If you believe this is a mistake, please contact your Python installation or OS distribution provider. You can override this, at the risk of breaking your Python installation or OS, by passing --break-system-packages.
hint: See PEP 668 for the detailed specification.
[SNIP]
[root@8f8a1523eade /]# python3 -m ensurepip --root /
Looking in links: /tmp/tmpn6uk_n0x
Processing /tmp/tmpn6uk_n0x/pip-23.2.1-py3-none-any.whl
Installing collected packages: pip
Successfully installed pip-23.2.1
WARNING: Running pip as the 'root' user can result in broken permissions and conflicting behaviour with the system package manager. It is recommended to use a virtual environment instead: https://pip.pypa.io/warnings/venv
sivel commented 10 months ago

Ok, using --root is expected to bypass the check:

https://github.com/pypa/pip/blob/9685f64fe8c78e1e39cd9b32e5615f42e0a01f1c/src/pip/_internal/commands/install.py#L270-L274

        # Check whether the environment we're installing into is externally
        # managed, as specified in PEP 668. Specifying --root, --target, or
        # --prefix disables the check, since there's no reliable way to locate
        # the EXTERNALLY-MANAGED file for those cases. An exception is also
        # made specifically for "--dry-run --report" for convenience.
nitzmahone commented 10 months ago

I'm going to verify one more thing to be sure, but after messing around with ensurepip --root /, I think it's basically working for us by accident. I didn't really like adding ensurepip in the first place, but it's kind of a necessary evil for common vanilla base images that wouldn't require users to always manually add a prepend_base and figure out how to get pip in there.

Much as I hate to say it, I think we're going to want to hide the ensurepip stuff behind a reusable script indirection where we can do a little more work to make sure we've got a pip available that can do what we want. The risk is that that script indirection becomes another place where everyone wants to hang "magic" to silently make pip work for $random_base_image_with_unique_busted_pip, but I think it's preferable to the game of distributed whac-a-mole I can see this one becoming down the road :laughing: .

Shrews commented 7 months ago

Per internal discussion, going to move the ensurepip call to a new target script to encapsulate future changes that might be needed for non-RHEL distros, and add a flag to disable the call.