diff options
author | Sloane Hertel <19572925+s-hertel@users.noreply.github.com> | 2022-09-28 10:12:30 -0400 |
---|---|---|
committer | GitHub <noreply@github.com> | 2022-09-28 09:12:30 -0500 |
commit | 551ddebeab451c1f158e4ef8b656d60ff57689be (patch) | |
tree | 40057217688f195761edf3e62110be4adbdb0c21 | |
parent | 00d60a45fa7cd61b25d622735ceb4c8f6a125a76 (diff) | |
download | ansible-551ddebeab451c1f158e4ef8b656d60ff57689be.tar.gz |
[2.13] Limit Galaxy API calls during ansible-galaxy dependency resolution (#78722)
* Limit Galaxy API calls during ansible-galaxy collection dependency resolution when possible
Installing a tarfile with a dependency from a Galaxy server (e.g. dependencies: {'ns.coll': '>=1.0.0'}) does not get the available versions of the dependency from the galaxy server if a sufficient version is already installed.
Co-authored-by: Sviatoslav Sydorenko <wk.cvs.github@sydorenko.org.ua>
(cherry picked from commit 41b62f7db459b7c3a1d97302da3b9f2cbf302670)
4 files changed, 87 insertions, 12 deletions
diff --git a/changelogs/fragments/77468-ansible-galaxy-remove-unnecessary-api-call.yml b/changelogs/fragments/77468-ansible-galaxy-remove-unnecessary-api-call.yml new file mode 100644 index 0000000000..ff360aef77 --- /dev/null +++ b/changelogs/fragments/77468-ansible-galaxy-remove-unnecessary-api-call.yml @@ -0,0 +1,5 @@ +bugfixes: +- >- + ``ansible-galaxy`` - remove extra server api call during dependency resolution + for requirements and dependencies that are already satisfied + (https://github.com/ansible/ansible/issues/77443). diff --git a/lib/ansible/galaxy/dependency_resolution/providers.py b/lib/ansible/galaxy/dependency_resolution/providers.py index ccb56a9d79..817a1eb227 100644 --- a/lib/ansible/galaxy/dependency_resolution/providers.py +++ b/lib/ansible/galaxy/dependency_resolution/providers.py @@ -14,6 +14,7 @@ if t.TYPE_CHECKING: ConcreteArtifactsManager, ) from ansible.galaxy.collection.galaxy_api_proxy import MultiGalaxyAPIProxy + from ansible.galaxy.api import GalaxyAPI from ansible.galaxy.collection.gpg import get_signature_from_source from ansible.galaxy.dependency_resolution.dataclasses import ( @@ -316,8 +317,18 @@ class CollectionDependencyProviderBase(AbstractProvider): # The fqcn is guaranteed to be the same version_req = "A SemVer-compliant version or '*' is required. See https://semver.org to learn how to compose it correctly. " version_req += "This is an issue with the collection." + + # If we're upgrading collections, we can't calculate preinstalled_candidates until the latest matches are found. + # Otherwise, we can potentially avoid a Galaxy API call by doing this first. + preinstalled_candidates = set() + if not self._upgrade and first_req.type == 'galaxy': + preinstalled_candidates = { + candidate for candidate in self._preferred_candidates + if candidate.fqcn == fqcn and + all(self.is_satisfied_by(requirement, candidate) for requirement in requirements) + } try: - coll_versions = self._api_proxy.get_collection_versions(first_req) + coll_versions = [] if preinstalled_candidates else self._api_proxy.get_collection_versions(first_req) # type: t.Iterable[t.Tuple[str, GalaxyAPI]] except TypeError as exc: if first_req.is_concrete_artifact: # Non hashable versions will cause a TypeError @@ -395,19 +406,20 @@ class CollectionDependencyProviderBase(AbstractProvider): reverse=True, # prefer newer versions over older ones ) - preinstalled_candidates = { - candidate for candidate in self._preferred_candidates - if candidate.fqcn == fqcn and - ( - # check if an upgrade is necessary - all(self.is_satisfied_by(requirement, candidate) for requirement in requirements) and + if not preinstalled_candidates: + preinstalled_candidates = { + candidate for candidate in self._preferred_candidates + if candidate.fqcn == fqcn and ( - not self._upgrade or - # check if an upgrade is preferred - all(SemanticVersion(latest.ver) <= SemanticVersion(candidate.ver) for latest in latest_matches) + # check if an upgrade is necessary + all(self.is_satisfied_by(requirement, candidate) for requirement in requirements) and + ( + not self._upgrade or + # check if an upgrade is preferred + all(SemanticVersion(latest.ver) <= SemanticVersion(candidate.ver) for latest in latest_matches) + ) ) - ) - } + } return list(preinstalled_candidates) + latest_matches diff --git a/test/integration/targets/ansible-galaxy-collection/tasks/install_offline.yml b/test/integration/targets/ansible-galaxy-collection/tasks/install_offline.yml new file mode 100644 index 0000000000..8edb773e56 --- /dev/null +++ b/test/integration/targets/ansible-galaxy-collection/tasks/install_offline.yml @@ -0,0 +1,51 @@ +- set_fact: + init_dir: "{{ galaxy_dir }}/offline/setup" + build_dir: "{{ galaxy_dir }}/offline/build" + install_dir: "{{ galaxy_dir }}/offline/collections" + +- name: create test directories + file: + path: "{{ item }}" + state: directory + loop: + - "{{ init_dir }}" + - "{{ build_dir }}" + - "{{ install_dir }}" + +- name: test installing a tarfile with an installed dependency offline + block: + - name: init two collections + command: ansible-galaxy collection init ns.{{ item }} --init-path {{ init_dir }} + loop: + - coll1 + - coll2 + + - name: add one collection as the dependency of the other + lineinfile: + path: "{{ galaxy_dir }}/offline/setup/ns/coll1/galaxy.yml" + regexp: "^dependencies:*" + line: "dependencies: {'ns.coll2': '1.0.0'}" + + - name: build both collections + command: ansible-galaxy collection build {{ init_dir }}/ns/{{ item }} + args: + chdir: "{{ build_dir }}" + loop: + - coll1 + - coll2 + + - name: install the dependency from the tarfile + command: ansible-galaxy collection install {{ build_dir }}/ns-coll2-1.0.0.tar.gz -p {{ install_dir }} -s offline + + - name: install the tarfile with the installed dependency + command: ansible-galaxy collection install {{ build_dir }}/ns-coll1-1.0.0.tar.gz -p {{ install_dir }} -s offline + + always: + - name: clean up test directories + file: + path: "{{ item }}" + state: absent + loop: + - "{{ init_dir }}" + - "{{ build_dir }}" + - "{{ install_dir }}" diff --git a/test/integration/targets/ansible-galaxy-collection/tasks/main.yml b/test/integration/targets/ansible-galaxy-collection/tasks/main.yml index 063b7f0896..724c861e69 100644 --- a/test/integration/targets/ansible-galaxy-collection/tasks/main.yml +++ b/test/integration/targets/ansible-galaxy-collection/tasks/main.yml @@ -56,6 +56,13 @@ loop_control: loop_var: resolvelib_version +- name: run ansible-galaxy collection offline installation tests + include_tasks: install_offline.yml + args: + apply: + environment: + ANSIBLE_CONFIG: '{{ galaxy_dir }}/ansible.cfg' + - name: run ansible-galaxy collection publish tests for {{ test_name }} include_tasks: publish.yml args: |