qontract-reconcile 0.10.1rc1153__py3-none-any.whl → 0.10.1rc1155__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: qontract-reconcile
3
- Version: 0.10.1rc1153
3
+ Version: 0.10.1rc1155
4
4
  Summary: Collection of tools to reconcile services with their desired state as defined in the app-interface DB.
5
5
  Home-page: https://github.com/app-sre/qontract-reconcile
6
6
  Author: Red Hat App-SRE Team
@@ -90,7 +90,7 @@ reconcile/openshift_users.py,sha256=63mar-swgidz8f10TCPJcofbMN9FETq-HuVFpi8dUL4,
90
90
  reconcile/openshift_vault_secrets.py,sha256=9rTqV6wzCQx2Oh712E_Xj8wMG7u8Oh-pY8DWjlv4mZw,1660
91
91
  reconcile/quay_base.py,sha256=h5xNjb7EZm8L2JgpO42r6w0UA4im5dabZXJSIW69zKU,1987
92
92
  reconcile/quay_membership.py,sha256=YDLY7ayDsatEpYCe_iMdf3pkvrbmN8mAwrDKUJUa4Lg,6260
93
- reconcile/quay_mirror.py,sha256=dWvYqZcFu6RFUwyOVxNByRWx2FLIYTO2OFr2N1YtjJc,14706
93
+ reconcile/quay_mirror.py,sha256=mFp4Z5Nwl-DcFbbsJBOB8f9ldohFT-V67o868d5ux1s,15369
94
94
  reconcile/quay_mirror_org.py,sha256=utrJpJaKCs7U6WX6DODdfCeB0EmX-lUC8Y5fkmpgFSs,10764
95
95
  reconcile/quay_permissions.py,sha256=9KOutS1w4RFQqkvMSy54VtsKNx56-phzP6yI_rEW-B8,4244
96
96
  reconcile/quay_repos.py,sha256=cuEYG0HUe0ut5yvLdEwOF5-CmccpXQHRb_wDazvDrvQ,6895
@@ -199,7 +199,7 @@ reconcile/external_resources/integration_secrets_sync.py,sha256=dX09O3r6KURziUYY
199
199
  reconcile/external_resources/manager.py,sha256=wcqTawNS4qoBHFVfyCfHtWXh4L3AlgcNYx_Ov_vEjNg,17914
200
200
  reconcile/external_resources/meta.py,sha256=noaytFzmShpzLA_ebGh7wuP45mOfHIOnnoUxivjDa1I,672
201
201
  reconcile/external_resources/metrics.py,sha256=nMbyonGZEJDD1lYzpQY2eR9TNwvxYC4ZCcpi6wrExcM,1037
202
- reconcile/external_resources/model.py,sha256=ibOd0pa6hyJX-MK3LGVxOa3ra_Xfq7mbd5T_9d3bfhM,8444
202
+ reconcile/external_resources/model.py,sha256=Ta8eOJ97RxCgBtWqsF4MWzQhgsOC2PoFfBKM5y3rM6U,8378
203
203
  reconcile/external_resources/reconciler.py,sha256=3KFmkHsN7YAwJUSBpN1Xd_D2zM9Ea5_c2uMGWsfruZo,9707
204
204
  reconcile/external_resources/secrets_sync.py,sha256=6n0oDPLjd9Ql0lf6zsr1AZw8A6EEe3yCzl20XodtgkE,16229
205
205
  reconcile/external_resources/state.py,sha256=UupSa6tl4-73_J6Fhisn-qHal3v3uAUS5s5sk85LGDs,9343
@@ -549,7 +549,7 @@ reconcile/test/test_openshift_tekton_resources.py,sha256=O6-qPQwSiPHQKm1a1iqRU2V
549
549
  reconcile/test/test_openshift_upgrade_watcher.py,sha256=0GDQ_YFHIX8DbkbDYSuLv9uZeeg4NwP1vlOqvSaZvN4,7183
550
550
  reconcile/test/test_prometheus_rules_tester.py,sha256=cgVkPM3KcAw69bOkJ6iR2Lfog_WgblyoqVRtXv4ly7o,5685
551
551
  reconcile/test/test_quay_membership.py,sha256=e29Giz5S9ckFgjpTO8PBo8qVPocIQmy4WqsRhgTFd9A,2643
552
- reconcile/test/test_quay_mirror.py,sha256=UBLQDtet5WkWnh98ev5cI3yAflQl07awg2dXTefqoUk,6311
552
+ reconcile/test/test_quay_mirror.py,sha256=9TIu1-YQl0CG705m-FeEOs4OwKG3UVJzVhISw525Lco,6324
553
553
  reconcile/test/test_quay_mirror_org.py,sha256=67XwB0WkqO6l9SeR9cSfjODyt-P587Kvqvam7hR8rLQ,3008
554
554
  reconcile/test/test_quay_repos.py,sha256=TdkcRF_a8PLp01Kti9eZZN-vGup2yPBT4Iba3k08qe0,1810
555
555
  reconcile/test/test_queries.py,sha256=SpH3RmNpBjEr_ne3VjAMCgKK8RE1z1zo7bypkT5uoO4,1946
@@ -683,7 +683,7 @@ reconcile/utils/gpg.py,sha256=EKG7_fdMv8BMlV5yUdPiqoTx-KrzmVSEAl2sLkaKwWI,1123
683
683
  reconcile/utils/gql.py,sha256=C0thIm_k9MBldfqwHzyqtYZk9sIvMdm9IbbnXLGwjD8,14158
684
684
  reconcile/utils/grouping.py,sha256=vr9SFHZ7bqmHYrvYcEZt-Er3-yQYfAAdq5sHLZVmXPY,456
685
685
  reconcile/utils/helm.py,sha256=hr4J_9mBZwbc1FDNfFh4QKAj0h3eLxyTN2Y3UxIRp8U,3893
686
- reconcile/utils/helpers.py,sha256=1PK_6gTzg2kJ8Ac8zZHB2yEubewDWtVspTl5bVbvbEY,1462
686
+ reconcile/utils/helpers.py,sha256=womAD2bKPUAFOjHvNPAe_2Hsb-oVTxuQiYPGeR-Thp0,1772
687
687
  reconcile/utils/imap_client.py,sha256=h8YDiCSCvroErhpH_-KGYI7Y2WU2Q2oSpuxDFbOkSbY,1989
688
688
  reconcile/utils/instrumented_wrappers.py,sha256=eVwMoa6FCrYxLv3RML3WpZF9qKVfCTjMxphgVXG03OM,1073
689
689
  reconcile/utils/jenkins_api.py,sha256=RaKuZmO7_lbI-hE6c_Pq2a6CQdmBVj7BcP2jR68cIbI,7081
@@ -880,8 +880,8 @@ tools/test/test_qontract_cli.py,sha256=iuzKbQ6ahinvjoQmQLBrG4shey0z-1rB6qCgS8T6d
880
880
  tools/test/test_saas_promotion_state.py,sha256=dy4kkSSAQ7bC0Xp2CociETGN-2aABEfL6FU5D9Jl00Y,6056
881
881
  tools/test/test_sd_app_sre_alert_report.py,sha256=v363r9zM7__0kR5K6mvJoGFcM9BvE33fWAayrqkpojA,2116
882
882
  tools/test/test_sre_checkpoints.py,sha256=SKqPPTl9ua0RFdSSofnoQX-JZE6dFLO3LRhfQzqtfh8,2607
883
- qontract_reconcile-0.10.1rc1153.dist-info/METADATA,sha256=W7PsNpp1_srAAOioGMxIeZNzgmkrmBV5ra6opFDI-58,2213
884
- qontract_reconcile-0.10.1rc1153.dist-info/WHEEL,sha256=eOLhNAGa2EW3wWl_TU484h7q1UNgy0JXjjoqKoxAAQc,92
885
- qontract_reconcile-0.10.1rc1153.dist-info/entry_points.txt,sha256=GKQqCl2j2X1BJQ69een6rHcR26PmnxnONLNOQB-nRjY,491
886
- qontract_reconcile-0.10.1rc1153.dist-info/top_level.txt,sha256=l5ISPoXzt0SdR4jVdkfa7RPSKNc8zAHYWAnR-Dw8Ey8,24
887
- qontract_reconcile-0.10.1rc1153.dist-info/RECORD,,
883
+ qontract_reconcile-0.10.1rc1155.dist-info/METADATA,sha256=R8XN1xU4JfYBpQJVZ8l8iWuRWuQ8DpVGaYE-pIpLRwk,2213
884
+ qontract_reconcile-0.10.1rc1155.dist-info/WHEEL,sha256=eOLhNAGa2EW3wWl_TU484h7q1UNgy0JXjjoqKoxAAQc,92
885
+ qontract_reconcile-0.10.1rc1155.dist-info/entry_points.txt,sha256=GKQqCl2j2X1BJQ69een6rHcR26PmnxnONLNOQB-nRjY,491
886
+ qontract_reconcile-0.10.1rc1155.dist-info/top_level.txt,sha256=l5ISPoXzt0SdR4jVdkfa7RPSKNc8zAHYWAnR-Dw8Ey8,24
887
+ qontract_reconcile-0.10.1rc1155.dist-info/RECORD,,
@@ -76,7 +76,25 @@ SUPPORTED_RESOURCE_TYPES = (
76
76
 
77
77
 
78
78
  class ExternalResourcesInventory(MutableMapping):
79
- _inventory: dict[ExternalResourceKey, ExternalResourceSpec] = {}
79
+ def __init__(self, namespaces: Iterable[NamespaceV1]) -> None:
80
+ self._inventory: dict[ExternalResourceKey, ExternalResourceSpec] = {}
81
+
82
+ desired_providers = [
83
+ (p, ns)
84
+ for ns in namespaces
85
+ for p in ns.external_resources or []
86
+ if isinstance(p, SUPPORTED_RESOURCE_PROVIDERS) and p.resources
87
+ ]
88
+
89
+ desired_specs = [
90
+ self._build_external_resource_spec(ns, p, r)
91
+ for (p, ns) in desired_providers
92
+ for r in p.resources
93
+ if isinstance(r, SUPPORTED_RESOURCE_TYPES) and r.managed_by_erv2
94
+ ]
95
+
96
+ for spec in desired_specs:
97
+ self._inventory[ExternalResourceKey.from_spec(spec)] = spec
80
98
 
81
99
  def _build_external_resource_spec(
82
100
  self,
@@ -100,24 +118,6 @@ class ExternalResourcesInventory(MutableMapping):
100
118
  spec.metadata[MODULE_OVERRIDES] = resource.module_overrides
101
119
  return spec
102
120
 
103
- def __init__(self, namespaces: Iterable[NamespaceV1]) -> None:
104
- desired_providers = [
105
- (p, ns)
106
- for ns in namespaces
107
- for p in ns.external_resources or []
108
- if isinstance(p, SUPPORTED_RESOURCE_PROVIDERS) and p.resources
109
- ]
110
-
111
- desired_specs = [
112
- self._build_external_resource_spec(ns, p, r)
113
- for (p, ns) in desired_providers
114
- for r in p.resources
115
- if isinstance(r, SUPPORTED_RESOURCE_TYPES) and r.managed_by_erv2
116
- ]
117
-
118
- for spec in desired_specs:
119
- self._inventory[ExternalResourceKey.from_spec(spec)] = spec
120
-
121
121
  def __getitem__(self, key: ExternalResourceKey) -> ExternalResourceSpec | None:
122
122
  return self._inventory[key]
123
123
 
@@ -161,8 +161,6 @@ class ExternalResourceModuleKey(BaseModel, frozen=True):
161
161
 
162
162
 
163
163
  class ModuleInventory:
164
- inventory: dict[ExternalResourceModuleKey, ExternalResourcesModuleV1]
165
-
166
164
  def __init__(
167
165
  self, inventory: dict[ExternalResourceModuleKey, ExternalResourcesModuleV1]
168
166
  ):
reconcile/quay_mirror.py CHANGED
@@ -1,6 +1,5 @@
1
1
  import logging
2
2
  import os
3
- import re
4
3
  import sys
5
4
  import tempfile
6
5
  import time
@@ -29,6 +28,7 @@ from reconcile.utils import (
29
28
  metrics,
30
29
  sharding,
31
30
  )
31
+ from reconcile.utils.helpers import match_patterns
32
32
  from reconcile.utils.instrumented_wrappers import InstrumentedImage as Image
33
33
  from reconcile.utils.instrumented_wrappers import InstrumentedSkopeo as Skopeo
34
34
  from reconcile.utils.secret_reader import SecretReader
@@ -205,20 +205,38 @@ class QuayMirror:
205
205
  return summary
206
206
 
207
207
  @staticmethod
208
- def sync_tag(tags, tags_exclude, candidate):
209
- if tags is not None:
210
- # When tags is defined, we don't look at tags_exclude
211
- return any(re.match(tag, candidate) for tag in tags)
212
-
213
- if tags_exclude is not None:
214
- for tag_exclude in tags_exclude:
215
- if re.match(tag_exclude, candidate):
216
- return False
208
+ def sync_tag(
209
+ tags: Iterable[str] | None,
210
+ tags_exclude: Iterable[str] | None,
211
+ candidate: str,
212
+ ) -> bool:
213
+ """
214
+ Determine if the candidate tag should sync, tags_exclude check take precedence.
215
+ :param tags: regex patterns to filter, match means to sync, None means no filter
216
+ :param tags_exclude: regex patterns to filter, match means not to sync, None means no filter
217
+ :param candidate: tag to check
218
+ :return: bool, True means to sync, False means not to sync
219
+ """
220
+ if not tags and not tags_exclude:
217
221
  return True
218
222
 
219
- # Both tags and tags_exclude are None, so
220
- # tag must be synced
221
- return True
223
+ if not tags:
224
+ # only tags_exclude provided
225
+ assert tags_exclude # mypy can't infer not None
226
+ return not match_patterns(tags_exclude, candidate)
227
+
228
+ if not tags_exclude:
229
+ # only tags provided
230
+ return match_patterns(tags, candidate)
231
+
232
+ # both tags and tags_exclude provided
233
+ return not match_patterns(
234
+ tags_exclude,
235
+ candidate,
236
+ ) and match_patterns(
237
+ tags,
238
+ candidate,
239
+ )
222
240
 
223
241
  def process_sync_tasks(self):
224
242
  if self.is_compare_tags:
@@ -96,22 +96,15 @@ class TestIsCompareTags:
96
96
  (["^sha256-.+sig$", "^main-.+"], None, "main-755781cc", True),
97
97
  (["^sha256-.+sig$", "^main-.+"], None, "sha256-8b5.sig", True),
98
98
  (["^sha256-.+sig$", "^main-.+"], None, "1.2.3", False),
99
- # Tags exclude tests.
99
+ # # Tags exclude tests.
100
100
  (None, ["^sha256-.+sig$", "^main-.+"], "main-755781cc", False),
101
101
  (None, ["^sha256-.+sig$", "^main-.+"], "sha256-8b5.sig", False),
102
- # When both includes and excludes are explicitly given, includes take precedence.
103
- (
104
- ["^sha256-.+sig$", "^main-.+"],
105
- ["^sha256-.+sig$", "^main-.+"],
106
- "main-755781cc",
107
- True,
108
- ),
109
- (
110
- ["^sha256-.+sig$", "^main-.+"],
111
- ["^sha256-.+sig$", "^main-.+"],
112
- "sha256-8b5.sig",
113
- True,
114
- ),
102
+ (None, ["^sha256-.+sig$", "^main-.+"], "1.2.3", True),
103
+ # When both includes and excludes are explicitly given, exclude take precedence.
104
+ (["^sha256-.+sig$", "^main-.+"], ["main-755781cc"], "main-755781cc", False),
105
+ (["^sha256-.+sig$", "^main-.+"], ["main-755781cc"], "sha256-8b5.sig", True),
106
+ # both include and exclude are not set
107
+ (None, None, "main-755781cc", True),
115
108
  ],
116
109
  )
117
110
  def test_sync_tag(tags, tags_exclude, candidate, result):
@@ -1,5 +1,6 @@
1
1
  import logging
2
2
  import random
3
+ import re
3
4
  import string
4
5
  from collections import Counter
5
6
  from collections.abc import (
@@ -52,3 +53,13 @@ def generate_random_password(string_length: int = 20) -> str:
52
53
  """Generate a random string of letters and digits"""
53
54
  letters_and_digits = string.ascii_letters + string.digits
54
55
  return "".join(random.choices(letters_and_digits, k=string_length))
56
+
57
+
58
+ def match_patterns(patterns: Iterable[str], s: str) -> bool:
59
+ """
60
+ Check if any pattern matches the string.
61
+ :param patterns: patterns to match
62
+ :param s: string to check
63
+ :return: True if any pattern matches, False otherwise
64
+ """
65
+ return any(re.match(p, s) for p in patterns)