Browse code

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

Matt Davis authored on 2021/03/30 05:06:02
Showing 4 changed files
1 1
new file mode 100644
... ...
@@ -0,0 +1,2 @@
0
+minor_changes:
1
+- ansible-galaxy CLI - ``collection verify`` command now exits with a non-zero exit code on verification failure
... ...
@@ -549,7 +549,7 @@ class GalaxyCLI(CLI):
549 549
                 **galaxy_options
550 550
             ))
551 551
 
552
-        context.CLIARGS['func']()
552
+        return context.CLIARGS['func']()
553 553
 
554 554
     @property
555 555
     def api(self):
... ...
@@ -1091,13 +1091,16 @@ class GalaxyCLI(CLI):
1091 1091
 
1092 1092
         resolved_paths = [validate_collection_path(GalaxyCLI._resolve_path(path)) for path in search_paths]
1093 1093
 
1094
-        verify_collections(
1094
+        results = verify_collections(
1095 1095
             requirements, resolved_paths,
1096 1096
             self.api_servers, ignore_errors,
1097 1097
             local_verify_only=local_verify_only,
1098 1098
             artifacts_manager=artifacts_manager,
1099 1099
         )
1100 1100
 
1101
+        if any(result for result in results if not result.success):
1102
+            return 1
1103
+
1101 1104
         return 0
1102 1105
 
1103 1106
     @with_collection_artifacts_manager
... ...
@@ -131,16 +131,26 @@ MANIFEST_FILENAME = 'MANIFEST.json'
131 131
 ModifiedContent = namedtuple('ModifiedContent', ['filename', 'expected', 'installed'])
132 132
 
133 133
 
134
+# FUTURE: expose actual verify result details for a collection on this object, maybe reimplement as dataclass on py3.8+
135
+class CollectionVerifyResult:
136
+    def __init__(self, collection_name):  # type: (str) -> None
137
+        self.collection_name = collection_name  # type: str
138
+        self.success = True  # type: bool
139
+
140
+
134 141
 def verify_local_collection(
135 142
         local_collection, remote_collection,
136 143
         artifacts_manager,
137
-):  # type: (Candidate, Optional[Candidate], ConcreteArtifactsManager) -> None
144
+):  # type: (Candidate, Optional[Candidate], ConcreteArtifactsManager) -> CollectionVerifyResult
138 145
     """Verify integrity of the locally installed collection.
139 146
 
140 147
     :param local_collection: Collection being checked.
141 148
     :param remote_collection: Upstream collection (optional, if None, only verify local artifact)
142 149
     :param artifacts_manager: Artifacts manager.
150
+    :return: a collection verify result object.
143 151
     """
152
+    result = CollectionVerifyResult(local_collection.fqcn)
153
+
144 154
     b_collection_path = to_bytes(
145 155
         local_collection.src, errors='surrogate_or_strict',
146 156
     )
... ...
@@ -188,7 +198,8 @@ def verify_local_collection(
188 188
                 )
189 189
             )
190 190
             display.display(err)
191
-            return
191
+            result.success = False
192
+            return result
192 193
 
193 194
         # Verify the downloaded manifest hash matches the installed copy before verifying the file manifest
194 195
         manifest_hash = get_hash_from_validation_source(MANIFEST_FILENAME)
... ...
@@ -214,6 +225,7 @@ def verify_local_collection(
214 214
             _verify_file_hash(b_collection_path, manifest_data['name'], expected_hash, modified_content)
215 215
 
216 216
     if modified_content:
217
+        result.success = False
217 218
         display.display(
218 219
             'Collection {fqcn!s} contains modified content '
219 220
             'in the following files:'.
... ...
@@ -229,6 +241,8 @@ def verify_local_collection(
229 229
             format(coll=local_collection, what=what),
230 230
         )
231 231
 
232
+    return result
233
+
232 234
 
233 235
 def build_collection(u_collection_path, u_output_path, force):
234 236
     # type: (Text, Text, bool) -> Text
... ...
@@ -574,7 +588,7 @@ def verify_collections(
574 574
         ignore_errors,  # type: bool
575 575
         local_verify_only,  # type: bool
576 576
         artifacts_manager,  # type: ConcreteArtifactsManager
577
-):  # type: (...) -> None
577
+):  # type: (...) -> List[CollectionVerifyResult]
578 578
     r"""Verify the integrity of locally installed collections.
579 579
 
580 580
     :param collections: The collections to check.
... ...
@@ -583,7 +597,10 @@ def verify_collections(
583 583
     :param ignore_errors: Whether to ignore any errors when verifying the collection.
584 584
     :param local_verify_only: When True, skip downloads and only verify local manifests.
585 585
     :param artifacts_manager: Artifacts manager.
586
+    :return: list of CollectionVerifyResult objects describing the results of each collection verification
586 587
     """
588
+    results = []  # type: List[CollectionVerifyResult]
589
+
587 590
     api_proxy = MultiGalaxyAPIProxy(apis, artifacts_manager)
588 591
 
589 592
     with _display_progress():
... ...
@@ -656,11 +673,13 @@ def verify_collections(
656 656
                             )
657 657
                         raise
658 658
 
659
-                verify_local_collection(
659
+                result = verify_local_collection(
660 660
                     local_collection, remote_collection,
661 661
                     artifacts_manager,
662 662
                 )
663 663
 
664
+                results.append(result)
665
+
664 666
             except AnsibleError as err:
665 667
                 if ignore_errors:
666 668
                     display.warning(
... ...
@@ -672,6 +691,8 @@ def verify_collections(
672 672
                 else:
673 673
                     raise
674 674
 
675
+    return results
676
+
675 677
 
676 678
 @contextmanager
677 679
 def _tempdir():
... ...
@@ -16,11 +16,11 @@
16 16
 - name: test verifying a tarfile
17 17
   command: ansible-galaxy collection verify {{ galaxy_dir }}/ansible_test-verify-1.0.0.tar.gz
18 18
   register: verify
19
-  ignore_errors: yes
19
+  failed_when: verify.rc == 0
20 20
 
21 21
 - assert:
22 22
     that:
23
-      - verify.failed
23
+      - verify.rc != 0
24 24
       - >-
25 25
         "ERROR! 'file' type is not supported. The format namespace.name is expected." in verify.stderr
26 26
 
... ...
@@ -48,11 +48,11 @@
48 48
 - name: verify a collection that doesn't appear to be installed
49 49
   command: ansible-galaxy collection verify ansible_test.verify:1.0.0
50 50
   register: verify
51
-  ignore_errors: true
51
+  failed_when: verify.rc == 0
52 52
 
53 53
 - assert:
54 54
     that:
55
-      - verify.failed
55
+      - verify.rc != 0
56 56
       - "'Collection ansible_test.verify is not installed in any of the collection paths.' in verify.stderr"
57 57
 
58 58
 - name: create a modules directory
... ...
@@ -86,9 +86,11 @@
86 86
   environment:
87 87
     ANSIBLE_COLLECTIONS_PATH: '{{ galaxy_dir }}'
88 88
   register: verify
89
+  failed_when: verify.rc == 0
89 90
 
90 91
 - assert:
91 92
     that:
93
+      - verify.rc != 0
92 94
       - '"ansible_test.verify has the version ''1.0.0'' but is being compared to ''2.0.0''" in verify.stdout'
93 95
 
94 96
 - name: install the new version from the server
... ...
@@ -147,9 +149,11 @@
147 147
   register: verify
148 148
   environment:
149 149
     ANSIBLE_COLLECTIONS_PATH: '{{ galaxy_dir }}'
150
+  failed_when: verify.rc == 0
150 151
 
151 152
 - assert:
152 153
     that:
154
+      - verify.rc != 0
153 155
       - "'Collection ansible_test.verify contains modified content in the following files:\n    plugins/modules/test_module.py' in verify.stdout"
154 156
 
155 157
 - name: modify the FILES.json to match the new checksum
... ...
@@ -165,9 +169,11 @@
165 165
   register: verify
166 166
   environment:
167 167
     ANSIBLE_COLLECTIONS_PATH: '{{ galaxy_dir }}'
168
+  failed_when: verify.rc == 0
168 169
 
169 170
 - assert:
170 171
     that:
172
+      - verify.rc != 0
171 173
       - "'Collection ansible_test.verify contains modified content in the following files:\n    FILES.json' in verify.stdout"
172 174
 
173 175
 - name: get the checksum of the FILES.json
... ...
@@ -187,9 +193,11 @@
187 187
   register: verify
188 188
   environment:
189 189
     ANSIBLE_COLLECTIONS_PATH: '{{ galaxy_dir }}'
190
+  failed_when: verify.rc == 0
190 191
 
191 192
 - assert:
192 193
     that:
194
+      - verify.rc != 0
193 195
       - "'Collection ansible_test.verify contains modified content in the following files:\n    MANIFEST.json' in verify.stdout"
194 196
 
195 197
 - name: remove the artifact metadata to test verifying a collection without it
... ...
@@ -215,11 +223,11 @@
215 215
   register: verify
216 216
   environment:
217 217
     ANSIBLE_COLLECTIONS_PATH: '{{ galaxy_dir }}'
218
-  ignore_errors: yes
218
+  failed_when: verify.rc == 0
219 219
 
220 220
 - assert:
221 221
     that:
222
-      - verify.failed
222
+      - verify.rc != 0
223 223
       - "'Collection ansible_test.verify does not have a MANIFEST.json' in verify.stderr"
224 224
 
225 225
 - name: update the collection version to something not present on the server
... ...
@@ -260,9 +268,10 @@
260 260
   register: verify
261 261
   environment:
262 262
     ANSIBLE_COLLECTIONS_PATH: '{{ galaxy_dir }}'
263
-  ignore_errors: yes
263
+  failed_when: verify.rc == 0
264 264
 
265 265
 - assert:
266 266
     that:
267
+      - verify.rc != 0
267 268
       - "'Collection ansible_test.verify contains modified content in the following files:' in verify.stdout"
268 269
       - "'plugins/modules/test_module.py' in verify.stdout"