saltstack / saltstack/salt

[BUG] pip.install from local index fails using Salt 3005.1 onedir package

Open
#62,890 3 comments 0 reactions 0 assignees View on GitHub

Nobody has claimed this yet.

bug needs-triage
Dominant language
Python
Stars
15.7k
Forks
5.6k
Avg merge
2d 44m
Merged PRs (30d)
80

Description

Description
When I try to install a pip package from a local pip mirror/index, the installation fails with an "pip._vendor.pep517.wrappers.BackendInvalid" exception. It looks like this only happens when some building requirements are not (yet) met.

The minion is completely air-gapped, so we need to use a local pip repository.

When I connect the test minion to the internet and remove the "index_url", "find_links" and "trusted_host" parameters, the state runs fine.

Setup
Minion VM with Ubuntu 20.04 or Windows server 2019, with salt 3005.1 onedir packaging.

Using a local pip repository containing all necessary packages and dependencies, provisioned with pypi-mirror.

Steps to Reproduce the behavior
An example is the installation of pip-system-certs (but this error also occurs during installation of other pip packages with dependencies).

my_testing_installation:
  pip.installed:
    - name: pip-system-certs
    - index_url: "http://repo.foo.lan/pip"
    - find_links: "http://repo.foo.lan/pip"
    - trusted_host: "repo.foo.lan"

Version 3.1 of pip-system-certs tries to install setuptools-65.4.1.tar.gz.

Using an air-gapped minion with access to a local pip repository (http://repo.foo.lan/pip).

Expected behavior
In previous versions of Salt (3004.1), all pip packages and dependencies were installed from our local pip repository using the given state.

The state is also working fine on 3005.1 when the minion is connected to the internet and the index_url/find_links/trusted_host parameters are omitted.

          ID: my_testing_installation
    Function: pip.installed
        Name: pip-system-certs
      Result: True
     Comment: All packages were successfully installed
     Started: 13:29:29.609256
    Duration: 70575.297 ms
     Changes:
              ----------
              pip-system-certs==3.1:
                  Installed

Screenshots
The installation on 3005.1 onedir (air-gapped minion with local pip repo) fails with the following error:

----------
          ID: my_testing_installation
    Function: pip.installed
        Name: pip-system-certs
      Result: False
     Comment: Failed to install packages: pip-system-certs. Error: Looking in indexes: http://repo.foo.lan/pip
              Looking in links: http://repo.foo.lan/pip, http://repo.foo.lan/pip
              Collecting pip-system-certs
                Using cached http://repo.foo.lan/pip/pip-system-certs/pip_system_certs-3.1.tar.gz (4.9 kB)
                Preparing metadata (setup.py): started
                Preparing metadata (setup.py): finished with status 'error'   error: subprocess-exited-with-error

                python setup.py egg_info did not run successfully.
                exit code: 1

                [111 lines of output]
                setuptools/installer.py:27: SetuptoolsDeprecationWarning: setuptools.installer is deprecated. Requirements should be satisfied by a PEP 517 installer.
                  warnings.warn(
                  error: subprocess-exited-with-error

                  pip subprocess to install build dependencies did not run successfully.
                  exit code: 2

                  [59 lines of output]
                  Looking in indexes: http://repo.foo.lan/pip
                  Looking in links: http://repo.foo.lan/pip, http://repo.foo.lan/pip
                  Collecting setuptools>=45
                    Using cached http://repo.foo.lan/pip/setuptools/setuptools-65.4.1.tar.gz (2.6 MB)
                    Getting requirements to build wheel: started
                    Getting requirements to build wheel: finished with status 'done'
                  ERROR: Exception:
                  Traceback (most recent call last):
                    File "pip/_internal/cli/base_command.py", line 167, in exc_logging_wrapper
                      status = run_func(*args)
                    File "pip/_internal/cli/req_command.py", line 247, in wrapper
                      return func(self, options, args)
                    File "pip/_internal/commands/install.py", line 369, in run
                      requirement_set = resolver.resolve(
                    File "pip/_internal/resolution/resolvelib/resolver.py", line 92, in resolve
                      result = self._result = resolver.resolve(
                    File "pip/_vendor/resolvelib/resolvers.py", line 481, in resolve
                      state = resolution.resolve(requirements, max_rounds=max_rounds)
                    File "pip/_vendor/resolvelib/resolvers.py", line 348, in resolve
                      self._add_to_criteria(self.state.criteria, r, parent=None)
                    File "pip/_vendor/resolvelib/resolvers.py", line 172, in _add_to_criteria
                      if not criterion.candidates:
                    File "pip/_vendor/resolvelib/structs.py", line 151, in __bool__
                      return bool(self._sequence)
                    File "pip/_internal/resolution/resolvelib/found_candidates.py", line 155, in __bool__
                      return any(self)
                    File "pip/_internal/resolution/resolvelib/found_candidates.py", line 143, in <genexpr>
                      return (c for c in iterator if id(c) not in self._incompatible_ids)
                    File "pip/_internal/resolution/resolvelib/found_candidates.py", line 47, in _iter_built
                      candidate = func()
                    File "pip/_internal/resolution/resolvelib/factory.py", line 206, in _make_candidate_from_link
                      self._link_candidate_cache[link] = LinkCandidate(
                    File "pip/_internal/resolution/resolvelib/candidates.py", line 297, in __init__
                      super().__init__(
                    File "pip/_internal/resolution/resolvelib/candidates.py", line 162, in __init__
                      self.dist = self._prepare()
                    File "pip/_internal/resolution/resolvelib/candidates.py", line 231, in _prepare
                      dist = self._prepare_distribution()
                    File "pip/_internal/resolution/resolvelib/candidates.py", line 308, in _prepare_distribution
                      return preparer.prepare_linked_requirement(self._ireq, parallel_builds=True)
                    File "pip/_internal/operations/prepare.py", line 438, in prepare_linked_requirement
                      return self._prepare_linked_requirement(req, parallel_builds)
                    File "pip/_internal/operations/prepare.py", line 524, in _prepare_linked_requirement
                      dist = _get_prepared_distribution(
                    File "pip/_internal/operations/prepare.py", line 68, in _get_prepared_distribution
                      abstract_dist.prepare_distribution_metadata(
                    File "pip/_internal/distributions/sdist.py", line 48, in prepare_distribution_metadata
                      self._install_build_reqs(finder)
                    File "pip/_internal/distributions/sdist.py", line 118, in _install_build_reqs
                      build_reqs = self._get_build_requires_wheel()
                    File "pip/_internal/distributions/sdist.py", line 95, in _get_build_requires_wheel
                      return backend.get_requires_for_build_wheel()
                    File "pip/_internal/utils/misc.py", line 685, in get_requires_for_build_wheel
                      return super().get_requires_for_build_wheel(config_settings=cs)
                    File "pip/_vendor/pep517/wrappers.py", line 172, in get_requires_for_build_wheel
                      return self._call_hook('get_requires_for_build_wheel', {
                    File "pip/_vendor/pep517/wrappers.py", line 334, in _call_hook
                      raise BackendInvalid(
                  pip._vendor.pep517.wrappers.BackendInvalid
                  [end of output]

                  note: This error originates from a subprocess, and is likely not a problem with pip.
                error: subprocess-exited-with-error

                pip subprocess to install build dependencies did not run successfully.
                exit code: 2

                See above for output.

                note: This error originates from a subprocess, and is likely not a problem with pip.
                Traceback (most recent call last):
                  File "setuptools/installer.py", line 82, in fetch_build_egg
                    subprocess.check_call(cmd)
                  File "subprocess.py", line 373, in check_call
                subprocess.CalledProcessError: Command '['/opt/saltstack/salt/run/run', '-m', 'pip', '--disable-pip-version-check', 'wheel', '--no-deps', '-w', '/tmp/tmpnr764scx', '--quiet', 'setuptools_scm']' returned non-zero exit status 1.

                The above exception was the direct cause of the following exception:

                Traceback (most recent call last):
                  File "tiamatpip/cli.py", line 383, in _handle_runpy_return
                    runpy_f(*args, **kwargs)
                  File "runpy.py", line 87, in _run_code
                  File "<tiamat-pip-run-code>", line 2, in <module>
                  File "<pip-setuptools-caller>", line 34, in <module>
                  File "/tmp/pip-install-gm5jr0o3/pip-system-certs_70f845dcadfa48f898ab8d848bd0114a/setup.py", line 37, in <module>
                    setup(
                  File "setuptools/__init__.py", line 154, in setup
                    _install_setup_requires(attrs)
                  File "setuptools/__init__.py", line 148, in _install_setup_requires
                    dist.fetch_build_eggs(dist.setup_requires)
                  File "setuptools/dist.py", line 820, in fetch_build_eggs
                    resolved_dists = pkg_resources.working_set.resolve(
                  File "pkg_resources/__init__.py", line 771, in resolve
                    dist = best[req.key] = env.best_match(
                  File "pkg_resources/__init__.py", line 1056, in best_match
                    return self.obtain(req, installer)
                  File "pkg_resources/__init__.py", line 1068, in obtain
                    return installer(requirement)
                  File "setuptools/dist.py", line 891, in fetch_build_egg
                    return fetch_build_egg(self, req)
                  File "setuptools/installer.py", line 84, in fetch_build_egg
                    raise DistutilsError(str(e)) from e
                distutils.errors.DistutilsError: Command '['/opt/saltstack/salt/run/run', '-m', 'pip', '--disable-pip-version-check', 'wheel', '--no-deps', '-w', '/tmp/tmpnr764scx', '--quiet', 'setuptools_scm']' returned non-zero exit status 1.
                [end of output]

                note: This error originates from a subprocess, and is likely not a problem with pip.
              error: metadata-generation-failed

              Encountered error while generating package metadata.

              See above for output.

              note: This is an issue with the package mentioned above, not pip.
              hint: See above for details.
     Started: 12:55:36.308463
    Duration: 13579.152 ms

Versions Report

salt --versions-report
Salt Version:
          Salt: 3005.1

Dependency Versions:
          cffi: 1.14.6
      cherrypy: 18.6.1
      dateutil: 2.8.1
     docker-py: Not Installed
         gitdb: Not Installed
     gitpython: Not Installed
        Jinja2: 3.1.0
       libgit2: Not Installed
      M2Crypto: Not Installed
          Mako: Not Installed
       msgpack: 1.0.2
  msgpack-pure: Not Installed
  mysql-python: Not Installed
     pycparser: 2.21
      pycrypto: Not Installed
  pycryptodome: 3.9.8
        pygit2: Not Installed
        Python: 3.9.14 (main, Oct  3 2022, 21:19:16)
  python-gnupg: 0.4.8
        PyYAML: 5.4.1
         PyZMQ: 23.2.0
         smmap: Not Installed
       timelib: 0.2.4
       Tornado: 4.5.3
           ZMQ: 4.3.4

System Versions:
          dist: ubuntu 20.04 focal
        locale: utf-8
       machine: x86_64
       release: 5.4.0-126-generic
        system: Linux
       version: Ubuntu 20.04 focal

(Also tested on Windows server 2019 with Salt 3005.1 onedir).

Additional context
I tried setting the index_url/find_links/trusted_host parameters via env_vars, extra_args and/or pip config too, but those don't work either.

Contributor guide

Open the contributing guide

First steps

  1. Read the whole issue, then the project's contributing guide.
  2. Comment on the issue to say you are picking it up — it saves two people doing the same work.
  3. Fork the repository and make your change on a branch.
  4. Open a pull request that references the issue number.

Research direction

Start with the pip.installed state using the provided air-gapped local-index reproduction on Salt 3005.1, comparing it with Salt 3004.1 and the internet-connected case. Trace how build requirements are installed for the onedir package, especially the failing setuptools_scm command. Done means pip packages and dependencies install successfully from the configured local repository without the BackendInvalid failure.

Written by the indexing model from the issue text.

Assessment

Tech stack
python
Domain
devops, infrastructure
Issue type
Bug
Difficulty
4/5
Estimated time
3-5 days
Activity status
Stale
Clarity
Mostly clear
Newbie friendliness
35/100

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.