nonzero exit code on ansible galaxy collection verify failures (#74051)

This commit is contained in:
Matt Davis 2021-03-29 13:06:02 -07:00 committed by GitHub
parent 1527078a8f
commit 454c7e37ec
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
4 changed files with 48 additions and 13 deletions

View file

@ -0,0 +1,2 @@
minor_changes:
- ansible-galaxy CLI - ``collection verify`` command now exits with a non-zero exit code on verification failure

View file

@ -549,7 +549,7 @@ class GalaxyCLI(CLI):
**galaxy_options **galaxy_options
)) ))
context.CLIARGS['func']() return context.CLIARGS['func']()
@property @property
def api(self): def api(self):
@ -1091,13 +1091,16 @@ class GalaxyCLI(CLI):
resolved_paths = [validate_collection_path(GalaxyCLI._resolve_path(path)) for path in search_paths] resolved_paths = [validate_collection_path(GalaxyCLI._resolve_path(path)) for path in search_paths]
verify_collections( results = verify_collections(
requirements, resolved_paths, requirements, resolved_paths,
self.api_servers, ignore_errors, self.api_servers, ignore_errors,
local_verify_only=local_verify_only, local_verify_only=local_verify_only,
artifacts_manager=artifacts_manager, artifacts_manager=artifacts_manager,
) )
if any(result for result in results if not result.success):
return 1
return 0 return 0
@with_collection_artifacts_manager @with_collection_artifacts_manager

View file

@ -131,16 +131,26 @@ MANIFEST_FILENAME = 'MANIFEST.json'
ModifiedContent = namedtuple('ModifiedContent', ['filename', 'expected', 'installed']) ModifiedContent = namedtuple('ModifiedContent', ['filename', 'expected', 'installed'])
# FUTURE: expose actual verify result details for a collection on this object, maybe reimplement as dataclass on py3.8+
class CollectionVerifyResult:
def __init__(self, collection_name): # type: (str) -> None
self.collection_name = collection_name # type: str
self.success = True # type: bool
def verify_local_collection( def verify_local_collection(
local_collection, remote_collection, local_collection, remote_collection,
artifacts_manager, artifacts_manager,
): # type: (Candidate, Optional[Candidate], ConcreteArtifactsManager) -> None ): # type: (Candidate, Optional[Candidate], ConcreteArtifactsManager) -> CollectionVerifyResult
"""Verify integrity of the locally installed collection. """Verify integrity of the locally installed collection.
:param local_collection: Collection being checked. :param local_collection: Collection being checked.
:param remote_collection: Upstream collection (optional, if None, only verify local artifact) :param remote_collection: Upstream collection (optional, if None, only verify local artifact)
:param artifacts_manager: Artifacts manager. :param artifacts_manager: Artifacts manager.
:return: a collection verify result object.
""" """
result = CollectionVerifyResult(local_collection.fqcn)
b_collection_path = to_bytes( b_collection_path = to_bytes(
local_collection.src, errors='surrogate_or_strict', local_collection.src, errors='surrogate_or_strict',
) )
@ -188,7 +198,8 @@ def verify_local_collection(
) )
) )
display.display(err) display.display(err)
return result.success = False
return result
# Verify the downloaded manifest hash matches the installed copy before verifying the file manifest # Verify the downloaded manifest hash matches the installed copy before verifying the file manifest
manifest_hash = get_hash_from_validation_source(MANIFEST_FILENAME) manifest_hash = get_hash_from_validation_source(MANIFEST_FILENAME)
@ -214,6 +225,7 @@ def verify_local_collection(
_verify_file_hash(b_collection_path, manifest_data['name'], expected_hash, modified_content) _verify_file_hash(b_collection_path, manifest_data['name'], expected_hash, modified_content)
if modified_content: if modified_content:
result.success = False
display.display( display.display(
'Collection {fqcn!s} contains modified content ' 'Collection {fqcn!s} contains modified content '
'in the following files:'. 'in the following files:'.
@ -229,6 +241,8 @@ def verify_local_collection(
format(coll=local_collection, what=what), format(coll=local_collection, what=what),
) )
return result
def build_collection(u_collection_path, u_output_path, force): def build_collection(u_collection_path, u_output_path, force):
# type: (Text, Text, bool) -> Text # type: (Text, Text, bool) -> Text
@ -574,7 +588,7 @@ def verify_collections(
ignore_errors, # type: bool ignore_errors, # type: bool
local_verify_only, # type: bool local_verify_only, # type: bool
artifacts_manager, # type: ConcreteArtifactsManager artifacts_manager, # type: ConcreteArtifactsManager
): # type: (...) -> None ): # type: (...) -> List[CollectionVerifyResult]
r"""Verify the integrity of locally installed collections. r"""Verify the integrity of locally installed collections.
:param collections: The collections to check. :param collections: The collections to check.
@ -583,7 +597,10 @@ def verify_collections(
:param ignore_errors: Whether to ignore any errors when verifying the collection. :param ignore_errors: Whether to ignore any errors when verifying the collection.
:param local_verify_only: When True, skip downloads and only verify local manifests. :param local_verify_only: When True, skip downloads and only verify local manifests.
:param artifacts_manager: Artifacts manager. :param artifacts_manager: Artifacts manager.
:return: list of CollectionVerifyResult objects describing the results of each collection verification
""" """
results = [] # type: List[CollectionVerifyResult]
api_proxy = MultiGalaxyAPIProxy(apis, artifacts_manager) api_proxy = MultiGalaxyAPIProxy(apis, artifacts_manager)
with _display_progress(): with _display_progress():
@ -656,11 +673,13 @@ def verify_collections(
) )
raise raise
verify_local_collection( result = verify_local_collection(
local_collection, remote_collection, local_collection, remote_collection,
artifacts_manager, artifacts_manager,
) )
results.append(result)
except AnsibleError as err: except AnsibleError as err:
if ignore_errors: if ignore_errors:
display.warning( display.warning(
@ -672,6 +691,8 @@ def verify_collections(
else: else:
raise raise
return results
@contextmanager @contextmanager
def _tempdir(): def _tempdir():

View file

@ -16,11 +16,11 @@
- name: test verifying a tarfile - name: test verifying a tarfile
command: ansible-galaxy collection verify {{ galaxy_dir }}/ansible_test-verify-1.0.0.tar.gz command: ansible-galaxy collection verify {{ galaxy_dir }}/ansible_test-verify-1.0.0.tar.gz
register: verify register: verify
ignore_errors: yes failed_when: verify.rc == 0
- assert: - assert:
that: that:
- verify.failed - verify.rc != 0
- >- - >-
"ERROR! 'file' type is not supported. The format namespace.name is expected." in verify.stderr "ERROR! 'file' type is not supported. The format namespace.name is expected." in verify.stderr
@ -48,11 +48,11 @@
- name: verify a collection that doesn't appear to be installed - name: verify a collection that doesn't appear to be installed
command: ansible-galaxy collection verify ansible_test.verify:1.0.0 command: ansible-galaxy collection verify ansible_test.verify:1.0.0
register: verify register: verify
ignore_errors: true failed_when: verify.rc == 0
- assert: - assert:
that: that:
- verify.failed - verify.rc != 0
- "'Collection ansible_test.verify is not installed in any of the collection paths.' in verify.stderr" - "'Collection ansible_test.verify is not installed in any of the collection paths.' in verify.stderr"
- name: create a modules directory - name: create a modules directory
@ -86,9 +86,11 @@
environment: environment:
ANSIBLE_COLLECTIONS_PATH: '{{ galaxy_dir }}' ANSIBLE_COLLECTIONS_PATH: '{{ galaxy_dir }}'
register: verify register: verify
failed_when: verify.rc == 0
- assert: - assert:
that: that:
- verify.rc != 0
- '"ansible_test.verify has the version ''1.0.0'' but is being compared to ''2.0.0''" in verify.stdout' - '"ansible_test.verify has the version ''1.0.0'' but is being compared to ''2.0.0''" in verify.stdout'
- name: install the new version from the server - name: install the new version from the server
@ -147,9 +149,11 @@
register: verify register: verify
environment: environment:
ANSIBLE_COLLECTIONS_PATH: '{{ galaxy_dir }}' ANSIBLE_COLLECTIONS_PATH: '{{ galaxy_dir }}'
failed_when: verify.rc == 0
- assert: - assert:
that: that:
- verify.rc != 0
- "'Collection ansible_test.verify contains modified content in the following files:\n plugins/modules/test_module.py' in verify.stdout" - "'Collection ansible_test.verify contains modified content in the following files:\n plugins/modules/test_module.py' in verify.stdout"
- name: modify the FILES.json to match the new checksum - name: modify the FILES.json to match the new checksum
@ -165,9 +169,11 @@
register: verify register: verify
environment: environment:
ANSIBLE_COLLECTIONS_PATH: '{{ galaxy_dir }}' ANSIBLE_COLLECTIONS_PATH: '{{ galaxy_dir }}'
failed_when: verify.rc == 0
- assert: - assert:
that: that:
- verify.rc != 0
- "'Collection ansible_test.verify contains modified content in the following files:\n FILES.json' in verify.stdout" - "'Collection ansible_test.verify contains modified content in the following files:\n FILES.json' in verify.stdout"
- name: get the checksum of the FILES.json - name: get the checksum of the FILES.json
@ -187,9 +193,11 @@
register: verify register: verify
environment: environment:
ANSIBLE_COLLECTIONS_PATH: '{{ galaxy_dir }}' ANSIBLE_COLLECTIONS_PATH: '{{ galaxy_dir }}'
failed_when: verify.rc == 0
- assert: - assert:
that: that:
- verify.rc != 0
- "'Collection ansible_test.verify contains modified content in the following files:\n MANIFEST.json' in verify.stdout" - "'Collection ansible_test.verify contains modified content in the following files:\n MANIFEST.json' in verify.stdout"
- name: remove the artifact metadata to test verifying a collection without it - name: remove the artifact metadata to test verifying a collection without it
@ -215,11 +223,11 @@
register: verify register: verify
environment: environment:
ANSIBLE_COLLECTIONS_PATH: '{{ galaxy_dir }}' ANSIBLE_COLLECTIONS_PATH: '{{ galaxy_dir }}'
ignore_errors: yes failed_when: verify.rc == 0
- assert: - assert:
that: that:
- verify.failed - verify.rc != 0
- "'Collection ansible_test.verify does not have a MANIFEST.json' in verify.stderr" - "'Collection ansible_test.verify does not have a MANIFEST.json' in verify.stderr"
- name: update the collection version to something not present on the server - name: update the collection version to something not present on the server
@ -260,9 +268,10 @@
register: verify register: verify
environment: environment:
ANSIBLE_COLLECTIONS_PATH: '{{ galaxy_dir }}' ANSIBLE_COLLECTIONS_PATH: '{{ galaxy_dir }}'
ignore_errors: yes failed_when: verify.rc == 0
- assert: - assert:
that: that:
- verify.rc != 0
- "'Collection ansible_test.verify contains modified content in the following files:' in verify.stdout" - "'Collection ansible_test.verify contains modified content in the following files:' in verify.stdout"
- "'plugins/modules/test_module.py' in verify.stdout" - "'plugins/modules/test_module.py' in verify.stdout"