summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorSloane Hertel <19572925+s-hertel@users.noreply.github.com>2021-08-10 18:00:03 -0400
committerGitHub <noreply@github.com>2021-08-10 18:00:03 -0400
commite24eb59de55c55a6da09757fc5947d9fd8936788 (patch)
tree6e954ecaf4bf55ba500e7b18b816e0232a9c6917
parent0055a328b3250b40de5b4027458c51887379b7eb (diff)
downloadansible-e24eb59de55c55a6da09757fc5947d9fd8936788.tar.gz
Improve ansible-galaxy error for InconsistentCandidate exception (#75235)
* Improve error for InconsistentCandidate exceptions * Add test case for installing a collection with an inconsistent version * Add test case for installing a collection that has a dependency with an inconsistent version Co-authored-by: Abhijeet Kasurde <akasurde@redhat.com> Co-authored-by: Sviatoslav Sydorenko <webknjaz@redhat.com>
-rw-r--r--changelogs/fragments/75235-ansible-galaxy-inconsistent-candidate-error.yml2
-rw-r--r--lib/ansible/galaxy/collection/__init__.py31
-rw-r--r--lib/ansible/galaxy/dependency_resolution/errors.py1
-rw-r--r--test/integration/targets/ansible-galaxy-collection/tasks/install.yml75
4 files changed, 109 insertions, 0 deletions
diff --git a/changelogs/fragments/75235-ansible-galaxy-inconsistent-candidate-error.yml b/changelogs/fragments/75235-ansible-galaxy-inconsistent-candidate-error.yml
new file mode 100644
index 0000000000..e56d524961
--- /dev/null
+++ b/changelogs/fragments/75235-ansible-galaxy-inconsistent-candidate-error.yml
@@ -0,0 +1,2 @@
+bugfixes:
+ - ansible-galaxy - Improve error message from dependency resolution when a candidate has inconsistent requirements (https://github.com/ansible/ansible/issues/75139).
diff --git a/lib/ansible/galaxy/collection/__init__.py b/lib/ansible/galaxy/collection/__init__.py
index 9af3068c8e..3a38dba32c 100644
--- a/lib/ansible/galaxy/collection/__init__.py
+++ b/lib/ansible/galaxy/collection/__init__.py
@@ -113,6 +113,7 @@ from ansible.galaxy.dependency_resolution.dataclasses import (
)
from ansible.galaxy.dependency_resolution.errors import (
CollectionDependencyResolutionImpossible,
+ CollectionDependencyInconsistentCandidate,
)
from ansible.galaxy.dependency_resolution.versioning import meets_requirements
from ansible.module_utils.six import raise_from
@@ -1353,3 +1354,33 @@ def _resolve_depenency_map(
AnsibleError('\n'.join(error_msg_lines)),
dep_exc,
)
+ except CollectionDependencyInconsistentCandidate as dep_exc:
+ parents = [
+ "%s.%s:%s" % (p.namespace, p.name, p.ver)
+ for p in dep_exc.criterion.iter_parent()
+ if p is not None
+ ]
+
+ error_msg_lines = [
+ (
+ 'Failed to resolve the requested dependencies map. '
+ 'Got the candidate {req.fqcn!s}:{req.ver!s} ({dep_origin!s}) '
+ 'which didn\'t satisfy all of the following requirements:'.
+ format(
+ req=dep_exc.candidate,
+ dep_origin='direct request'
+ if not parents else 'dependency of {parent!s}'.
+ format(parent=', '.join(parents))
+ )
+ )
+ ]
+
+ for req in dep_exc.criterion.iter_requirement():
+ error_msg_lines.append(
+ '* {req.fqcn!s}:{req.ver!s}'.format(req=req)
+ )
+
+ raise raise_from( # NOTE: Leading "raise" is a hack for mypy bug #9717
+ AnsibleError('\n'.join(error_msg_lines)),
+ dep_exc,
+ )
diff --git a/lib/ansible/galaxy/dependency_resolution/errors.py b/lib/ansible/galaxy/dependency_resolution/errors.py
index e57bd06e57..f5339a4d51 100644
--- a/lib/ansible/galaxy/dependency_resolution/errors.py
+++ b/lib/ansible/galaxy/dependency_resolution/errors.py
@@ -8,4 +8,5 @@ __metaclass__ = type
from resolvelib.resolvers import (
ResolutionImpossible as CollectionDependencyResolutionImpossible,
+ InconsistentCandidate as CollectionDependencyInconsistentCandidate,
)
diff --git a/test/integration/targets/ansible-galaxy-collection/tasks/install.yml b/test/integration/targets/ansible-galaxy-collection/tasks/install.yml
index 44c2fcd99f..8b25039a7b 100644
--- a/test/integration/targets/ansible-galaxy-collection/tasks/install.yml
+++ b/test/integration/targets/ansible-galaxy-collection/tasks/install.yml
@@ -163,6 +163,81 @@
- '"Installing ''namespace3.name:1.0.0'' to" in install_tarball.stdout'
- (install_tarball_actual.content | b64decode | from_json).collection_info.version == '1.0.0'
+- name: write a requirements file using the artifact and a conflicting version
+ copy:
+ content: |
+ collections:
+ - name: {{ galaxy_dir }}/namespace3.tar.gz
+ version: 1.2.0
+ dest: '{{ galaxy_dir }}/test_req.yml'
+
+- name: install the requirements file with mismatched versions
+ command: ansible-galaxy collection install -r '{{ galaxy_dir }}/test_req.yml' {{ galaxy_verbosity }}
+ ignore_errors: True
+ register: result
+
+- name: remove the requirements file
+ file:
+ path: '{{ galaxy_dir }}/test_req.yml'
+ state: absent
+
+- assert:
+ that: error == expected_error
+ vars:
+ reset_color: '\x1b\[0m'
+ color: '\x1b\[[0-9];[0-9]{2}m'
+ error: "{{ result.stderr | regex_replace(reset_color) | regex_replace(color) | regex_replace('\\n', ' ') }}"
+ expected_error: >-
+ ERROR! Failed to resolve the requested dependencies map.
+ Got the candidate namespace3.name:1.0.0 (direct request)
+ which didn't satisfy all of the following requirements:
+ * namespace3.name:1.2.0
+
+- name: test error for mismatched dependency versions
+ vars:
+ reset_color: '\x1b\[0m'
+ color: '\x1b\[[0-9];[0-9]{2}m'
+ error: "{{ result.stderr | regex_replace(reset_color) | regex_replace(color) | regex_replace('\\n', ' ') }}"
+ expected_error: >-
+ ERROR! Failed to resolve the requested dependencies map.
+ Got the candidate namespace3.name:1.0.0 (dependency of tmp_parent.name:1.0.0)
+ which didn't satisfy all of the following requirements:
+ * namespace3.name:1.2.0
+ block:
+ - name: init a new parent collection
+ command: ansible-galaxy collection init tmp_parent.name --init-path '{{ galaxy_dir }}/scratch'
+
+ - name: replace the dependencies
+ lineinfile:
+ path: "{{ galaxy_dir }}/scratch/tmp_parent/name/galaxy.yml"
+ regexp: "^dependencies:*"
+ line: "dependencies: { '{{ galaxy_dir }}/namespace3.tar.gz': '1.2.0' }"
+
+ - name: build the new artifact
+ command: ansible-galaxy collection build {{ galaxy_dir }}/scratch/tmp_parent/name
+ args:
+ chdir: "{{ galaxy_dir }}"
+
+ - name: install the artifact to verify the error is handled
+ command: ansible-galaxy collection install '{{ galaxy_dir }}/tmp_parent-name-1.0.0.tar.gz'
+ ignore_errors: yes
+ register: result
+
+ - debug: msg="Actual - {{ error }}"
+
+ - debug: msg="Expected - {{ expected_error }}"
+
+ - assert:
+ that: error == expected_error
+ always:
+ - name: clean up collection skeleton and artifact
+ file:
+ state: absent
+ path: "{{ item }}"
+ loop:
+ - "{{ galaxy_dir }}/scratch/tmp_parent/"
+ - "{{ galaxy_dir }}/tmp_parent-name-1.0.0.tar.gz"
+
- name: setup bad tarball - {{ test_name }}
script: build_bad_tar.py {{ galaxy_dir | quote }}