qontract-reconcile 0.10.1rc601__py3-none-any.whl → 0.10.1rc603__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.
- {qontract_reconcile-0.10.1rc601.dist-info → qontract_reconcile-0.10.1rc603.dist-info}/METADATA +2 -2
- {qontract_reconcile-0.10.1rc601.dist-info → qontract_reconcile-0.10.1rc603.dist-info}/RECORD +8 -8
- reconcile/test/test_saasherder.py +5 -7
- reconcile/utils/saasherder/models.py +11 -9
- reconcile/utils/saasherder/saasherder.py +73 -64
- {qontract_reconcile-0.10.1rc601.dist-info → qontract_reconcile-0.10.1rc603.dist-info}/WHEEL +0 -0
- {qontract_reconcile-0.10.1rc601.dist-info → qontract_reconcile-0.10.1rc603.dist-info}/entry_points.txt +0 -0
- {qontract_reconcile-0.10.1rc601.dist-info → qontract_reconcile-0.10.1rc603.dist-info}/top_level.txt +0 -0
{qontract_reconcile-0.10.1rc601.dist-info → qontract_reconcile-0.10.1rc603.dist-info}/METADATA
RENAMED
@@ -1,6 +1,6 @@
|
|
1
1
|
Metadata-Version: 2.1
|
2
2
|
Name: qontract-reconcile
|
3
|
-
Version: 0.10.
|
3
|
+
Version: 0.10.1rc603
|
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
|
@@ -11,7 +11,7 @@ Classifier: Programming Language :: Python
|
|
11
11
|
Classifier: Programming Language :: Python :: 3
|
12
12
|
Classifier: Programming Language :: Python :: 3.11
|
13
13
|
Requires-Python: >=3.11
|
14
|
-
Requires-Dist: sretoolbox ~=2.5.
|
14
|
+
Requires-Dist: sretoolbox ~=2.5.2
|
15
15
|
Requires-Dist: Click <9.0,>=7.0
|
16
16
|
Requires-Dist: gql ==3.1.0
|
17
17
|
Requires-Dist: toml <0.11.0,>=0.10.0
|
{qontract_reconcile-0.10.1rc601.dist-info → qontract_reconcile-0.10.1rc603.dist-info}/RECORD
RENAMED
@@ -451,7 +451,7 @@ reconcile/test/test_quay_repos.py,sha256=TdkcRF_a8PLp01Kti9eZZN-vGup2yPBT4Iba3k0
|
|
451
451
|
reconcile/test/test_queries.py,sha256=SpH3RmNpBjEr_ne3VjAMCgKK8RE1z1zo7bypkT5uoO4,1946
|
452
452
|
reconcile/test/test_repo_owners.py,sha256=uRYMLbMmh-9usF0TerabZTZV-Z1CS4I6ybT-LQqCLe8,1423
|
453
453
|
reconcile/test/test_requests_sender.py,sha256=7fd9C2kEFS0-CYtlsif66N1kO9c44pzuBPAJKR9igqU,5385
|
454
|
-
reconcile/test/test_saasherder.py,sha256=
|
454
|
+
reconcile/test/test_saasherder.py,sha256=1_GyiXxxNqKSKE7PrtFJL7tUFg77d1oQPZzNBZW-DLQ,47042
|
455
455
|
reconcile/test/test_saasherder_allowed_secret_paths.py,sha256=5NHQwNJO66at6HiyMZ5sVRTQDwxdvlOQo0KmkBWCw5Q,4853
|
456
456
|
reconcile/test/test_secret_reader.py,sha256=kz7nzcPjvA08cytnvcA_PMA98AEyqJWsESkYeRn5xCk,4994
|
457
457
|
reconcile/test/test_slack_base.py,sha256=gpbWOLNxMMX6fyAbs1JakhLTnwfedb3f7WpUae4tQZE,5060
|
@@ -675,8 +675,8 @@ reconcile/utils/runtime/runner.py,sha256=72cc-I6yXyPov8UCLHpyERRy1eiMLpGite2roO0
|
|
675
675
|
reconcile/utils/runtime/sharding.py,sha256=roCdbnBklhTK_g34zbgQYqzpKPaNQ8J6Xd9XLO9-t6Q,16258
|
676
676
|
reconcile/utils/saasherder/__init__.py,sha256=J3MBZBFa5YmhqYm08QsjBXz8mFcVOCiOCkyIcw41t7E,343
|
677
677
|
reconcile/utils/saasherder/interfaces.py,sha256=XXY35h8VWQ66z3LBPxaoUAMkIW50264DQiecrzyV6oA,9076
|
678
|
-
reconcile/utils/saasherder/models.py,sha256=
|
679
|
-
reconcile/utils/saasherder/saasherder.py,sha256=
|
678
|
+
reconcile/utils/saasherder/models.py,sha256=1DKXUmiTS_MejUfSpFCeuBLMTgR4ldv2N1tAz8qHAwc,5547
|
679
|
+
reconcile/utils/saasherder/saasherder.py,sha256=h3qihKpL-UDUqi8V-i1mhfofXmydeGuHhG_Y9pVKJjk,86162
|
680
680
|
reconcile/utils/terraform/__init__.py,sha256=zNbiyTWo35AT1sFTElL2j_AA0jJ_yWE_bfFn-nD2xik,250
|
681
681
|
reconcile/utils/terraform/config.py,sha256=5UVrd563TMcvi4ooa5JvWVDW1I3bIWg484u79evfV_8,164
|
682
682
|
reconcile/utils/terraform/config_client.py,sha256=py-Ree-QUYD6Hvng6bM40VgSuttteehIKNgwOSoJO1o,4706
|
@@ -704,8 +704,8 @@ tools/test/test_app_interface_metrics_exporter.py,sha256=SX7qL3D1SIRKFo95FoQztvf
|
|
704
704
|
tools/test/test_qontract_cli.py,sha256=OvalpVRfY4pNmpMaWHHYqBjV68b1eGQjX8SCyTAXb1w,3501
|
705
705
|
tools/test/test_sd_app_sre_alert_report.py,sha256=v363r9zM7__0kR5K6mvJoGFcM9BvE33fWAayrqkpojA,2116
|
706
706
|
tools/test/test_sre_checkpoints.py,sha256=SKqPPTl9ua0RFdSSofnoQX-JZE6dFLO3LRhfQzqtfh8,2607
|
707
|
-
qontract_reconcile-0.10.
|
708
|
-
qontract_reconcile-0.10.
|
709
|
-
qontract_reconcile-0.10.
|
710
|
-
qontract_reconcile-0.10.
|
711
|
-
qontract_reconcile-0.10.
|
707
|
+
qontract_reconcile-0.10.1rc603.dist-info/METADATA,sha256=KHZWVtIXDu8ZCJNsPliWFbsu58gjPsYV5hJQUzzyiL8,2349
|
708
|
+
qontract_reconcile-0.10.1rc603.dist-info/WHEEL,sha256=oiQVh_5PnQM0E3gPdiz09WCNmwiHDMaGer_elqB3coM,92
|
709
|
+
qontract_reconcile-0.10.1rc603.dist-info/entry_points.txt,sha256=rTjAv28I_CHLM8ID3OPqMI_suoQ9s7tFbim4aYjn9kk,376
|
710
|
+
qontract_reconcile-0.10.1rc603.dist-info/top_level.txt,sha256=l5ISPoXzt0SdR4jVdkfa7RPSKNc8zAHYWAnR-Dw8Ey8,24
|
711
|
+
qontract_reconcile-0.10.1rc603.dist-info/RECORD,,
|
@@ -651,19 +651,17 @@ class TestGetContainerImagesDiffSaasFile(TestCase):
|
|
651
651
|
self.get_commit_sha_patcher = patch.object(
|
652
652
|
SaasHerder, "_get_commit_sha", autospec=True
|
653
653
|
)
|
654
|
-
self.
|
655
|
-
SaasHerder, "_check_image", autospec=True
|
656
|
-
)
|
654
|
+
self.get_image_patcher = patch.object(SaasHerder, "_get_image", autospec=True)
|
657
655
|
self.initiate_gh = self.initiate_gh_patcher.start()
|
658
656
|
self.get_commit_sha = self.get_commit_sha_patcher.start()
|
659
|
-
self.
|
657
|
+
self.get_image = self.get_image_patcher.start()
|
660
658
|
self.maxDiff = None
|
661
659
|
|
662
660
|
def tearDown(self) -> None:
|
663
661
|
for p in (
|
664
662
|
self.initiate_gh_patcher,
|
665
663
|
self.get_commit_sha_patcher,
|
666
|
-
self.
|
664
|
+
self.get_image_patcher,
|
667
665
|
):
|
668
666
|
p.stop()
|
669
667
|
|
@@ -680,7 +678,7 @@ class TestGetContainerImagesDiffSaasFile(TestCase):
|
|
680
678
|
saasherder.state = MagicMock()
|
681
679
|
saasherder.state.get.return_value = "asha"
|
682
680
|
self.get_commit_sha.return_value = "abcd4242"
|
683
|
-
self.
|
681
|
+
self.get_image.return_value = MagicMock()
|
684
682
|
expected = [
|
685
683
|
TriggerSpecContainerImage(
|
686
684
|
saas_file_name=self.saas_file.name,
|
@@ -717,7 +715,7 @@ class TestGetContainerImagesDiffSaasFile(TestCase):
|
|
717
715
|
saasherder.state = MagicMock()
|
718
716
|
saasherder.state.get.return_value = "asha"
|
719
717
|
self.get_commit_sha.return_value = "abcd4242"
|
720
|
-
self.
|
718
|
+
self.get_image.return_value = MagicMock()
|
721
719
|
expected = [
|
722
720
|
TriggerSpecContainerImage(
|
723
721
|
saas_file_name=self.saas_file.name,
|
@@ -192,19 +192,21 @@ class ImageAuth:
|
|
192
192
|
username: Optional[str] = None
|
193
193
|
password: Optional[str] = None
|
194
194
|
auth_server: Optional[str] = None
|
195
|
+
docker_config: Optional[dict[str, dict[str, dict[str, str]]]] = None
|
195
196
|
|
196
197
|
def getDockerConfigJson(self) -> dict:
|
197
|
-
|
198
|
-
|
199
|
-
|
200
|
-
|
201
|
-
|
202
|
-
|
203
|
-
|
204
|
-
|
198
|
+
if self.docker_config:
|
199
|
+
return self.docker_config
|
200
|
+
else:
|
201
|
+
return {
|
202
|
+
"auths": {
|
203
|
+
self.auth_server: {
|
204
|
+
"auth": base64.b64encode(
|
205
|
+
f"{self.username}:{self.password}".encode()
|
206
|
+
).decode(),
|
207
|
+
}
|
205
208
|
}
|
206
209
|
}
|
207
|
-
}
|
208
210
|
|
209
211
|
|
210
212
|
@dataclass
|
@@ -898,19 +898,20 @@ class SaasHerder: # pylint: disable=too-many-public-methods
|
|
898
898
|
return False
|
899
899
|
|
900
900
|
def _process_template(
|
901
|
-
self,
|
902
|
-
saas_file_name: str,
|
903
|
-
resource_template_name: str,
|
904
|
-
image_auth: ImageAuth,
|
905
|
-
url: str,
|
906
|
-
path: str,
|
907
|
-
provider: str,
|
908
|
-
hash_length: int,
|
909
|
-
target: SaasResourceTemplateTarget,
|
910
|
-
parameters: dict[str, str],
|
911
|
-
github: Github,
|
912
|
-
target_config_hash: str,
|
901
|
+
self, spec: TargetSpec
|
913
902
|
) -> tuple[list[Any], str, Optional[Promotion]]:
|
903
|
+
saas_file_name = spec.saas_file_name
|
904
|
+
resource_template_name = spec.resource_template_name
|
905
|
+
image_auth = spec.image_auth
|
906
|
+
url = spec.url
|
907
|
+
path = spec.path
|
908
|
+
provider = spec.provider
|
909
|
+
hash_length = spec.hash_length
|
910
|
+
target = spec.target
|
911
|
+
parameters = spec.parameters
|
912
|
+
github = spec.github
|
913
|
+
target_config_hash = spec.target_config_hash
|
914
|
+
|
914
915
|
if provider == "openshift-template":
|
915
916
|
environment_parameters = self._collect_parameters(
|
916
917
|
target.namespace.environment
|
@@ -997,25 +998,26 @@ class SaasHerder: # pylint: disable=too-many-public-methods
|
|
997
998
|
+ f"{str(e)}"
|
998
999
|
)
|
999
1000
|
raise
|
1000
|
-
|
1001
|
-
|
1002
|
-
|
1003
|
-
|
1004
|
-
|
1005
|
-
|
1006
|
-
|
1007
|
-
|
1008
|
-
|
1009
|
-
|
1010
|
-
|
1011
|
-
|
1012
|
-
|
1013
|
-
|
1014
|
-
f"[{saas_file_name}/{resource_template_name}] "
|
1015
|
-
+ f"{html_url}: error generating REPO_DIGEST for "
|
1016
|
-
+ f"{image_uri}: {str(e)}"
|
1001
|
+
|
1002
|
+
image_uri = f"{registry_image}:{image_tag}"
|
1003
|
+
error_prefix = (
|
1004
|
+
f"[{saas_file_name}/{resource_template_name}] {html_url}:"
|
1005
|
+
)
|
1006
|
+
img = self._get_image(
|
1007
|
+
image=image_uri,
|
1008
|
+
image_patterns=spec.image_patterns,
|
1009
|
+
image_auth=image_auth,
|
1010
|
+
error_prefix=error_prefix,
|
1011
|
+
)
|
1012
|
+
if not img:
|
1013
|
+
raise Exception(
|
1014
|
+
f"[{error_prefix}: error generating REPO_DIGEST for {image_uri}"
|
1017
1015
|
)
|
1018
|
-
|
1016
|
+
|
1017
|
+
if need_repo_digest:
|
1018
|
+
consolidated_parameters["REPO_DIGEST"] = img.url_digest
|
1019
|
+
if need_image_digest:
|
1020
|
+
consolidated_parameters["IMAGE_DIGEST"] = img.digest
|
1019
1021
|
|
1020
1022
|
oc = OCLocal("cluster", None, None, local=True)
|
1021
1023
|
try:
|
@@ -1125,38 +1127,53 @@ class SaasHerder: # pylint: disable=too-many-public-methods
|
|
1125
1127
|
return images
|
1126
1128
|
|
1127
1129
|
@staticmethod
|
1128
|
-
def
|
1130
|
+
def _get_image(
|
1129
1131
|
image: str,
|
1130
1132
|
image_patterns: Iterable[str],
|
1131
1133
|
image_auth: ImageAuth,
|
1132
1134
|
error_prefix: str,
|
1133
|
-
) ->
|
1134
|
-
error = False
|
1135
|
+
) -> Optional[Image]:
|
1135
1136
|
if not image_patterns:
|
1136
|
-
error = True
|
1137
1137
|
logging.error(
|
1138
1138
|
f"{error_prefix} imagePatterns is empty (does not contain {image})"
|
1139
1139
|
)
|
1140
|
+
return None
|
1140
1141
|
if image_patterns and not any(image.startswith(p) for p in image_patterns):
|
1141
|
-
error = True
|
1142
1142
|
logging.error(f"{error_prefix} Image is not in imagePatterns: {image}")
|
1143
|
+
return None
|
1144
|
+
|
1145
|
+
# .dockerconfigjson
|
1146
|
+
if image_auth.docker_config:
|
1147
|
+
# we rely on the secret in vault being ordered
|
1148
|
+
# https://peps.python.org/pep-0468/
|
1149
|
+
for registry, auth in image_auth.docker_config["auths"].items():
|
1150
|
+
if not image.startswith(registry):
|
1151
|
+
continue
|
1152
|
+
username, password = (
|
1153
|
+
base64.b64decode(auth["auth"]).decode("utf-8").split(":")
|
1154
|
+
)
|
1155
|
+
with suppress(Exception):
|
1156
|
+
return Image(
|
1157
|
+
image,
|
1158
|
+
username=username,
|
1159
|
+
password=password,
|
1160
|
+
auth_server=image_auth.auth_server,
|
1161
|
+
)
|
1162
|
+
|
1163
|
+
# basic auth fallback for backwards compatibility
|
1143
1164
|
try:
|
1144
|
-
|
1165
|
+
return Image(
|
1145
1166
|
image,
|
1146
1167
|
username=image_auth.username,
|
1147
1168
|
password=image_auth.password,
|
1148
1169
|
auth_server=image_auth.auth_server,
|
1149
1170
|
)
|
1150
|
-
if not valid:
|
1151
|
-
error = True
|
1152
|
-
logging.error(f"{error_prefix} Image does not exist: {image}")
|
1153
1171
|
except Exception as e:
|
1154
|
-
error = True
|
1155
1172
|
logging.error(
|
1156
1173
|
f"{error_prefix} Image is invalid: {image}. " + f"details: {str(e)}"
|
1157
1174
|
)
|
1158
1175
|
|
1159
|
-
return
|
1176
|
+
return None
|
1160
1177
|
|
1161
1178
|
def _check_images(
|
1162
1179
|
self,
|
@@ -1175,15 +1192,15 @@ class SaasHerder: # pylint: disable=too-many-public-methods
|
|
1175
1192
|
self.images.update(images)
|
1176
1193
|
if not images:
|
1177
1194
|
return False # no errors
|
1178
|
-
|
1179
|
-
self.
|
1195
|
+
images = threaded.run(
|
1196
|
+
self._get_image,
|
1180
1197
|
images,
|
1181
1198
|
self.available_thread_pool_size,
|
1182
1199
|
image_patterns=image_patterns,
|
1183
1200
|
image_auth=image_auth,
|
1184
1201
|
error_prefix=error_prefix,
|
1185
1202
|
)
|
1186
|
-
return
|
1203
|
+
return None in images
|
1187
1204
|
|
1188
1205
|
def _initiate_github(
|
1189
1206
|
self, saas_file: SaasFile, base_url: Optional[str] = None
|
@@ -1216,20 +1233,24 @@ class SaasHerder: # pylint: disable=too-many-public-methods
|
|
1216
1233
|
return ImageAuth()
|
1217
1234
|
|
1218
1235
|
creds = self.secret_reader.read_all_secret(saas_file.authentication.image)
|
1219
|
-
|
1220
|
-
|
1236
|
+
required_docker_config_keys = [".dockerconfigjson"]
|
1237
|
+
required_keys_basic_auth = ["user", "token"]
|
1238
|
+
ok = all(k in creds.keys() for k in required_keys_basic_auth) or all(
|
1239
|
+
k in creds.keys() for k in required_docker_config_keys
|
1240
|
+
)
|
1221
1241
|
if not ok:
|
1222
1242
|
logging.warning(
|
1223
1243
|
"the specified image authentication secret "
|
1224
1244
|
+ f"found in path {saas_file.authentication.image.path} "
|
1225
|
-
+ f"does not contain all required keys: {
|
1245
|
+
+ f"does not contain all required keys: {required_docker_config_keys} or {required_keys_basic_auth}"
|
1226
1246
|
)
|
1227
1247
|
return ImageAuth()
|
1228
1248
|
|
1229
1249
|
return ImageAuth(
|
1230
|
-
username=creds
|
1231
|
-
password=creds
|
1250
|
+
username=creds.get("user"),
|
1251
|
+
password=creds.get("token"),
|
1232
1252
|
auth_server=creds.get("url"),
|
1253
|
+
docker_config=json.loads(creds.get(".dockerconfigjson") or "{}"),
|
1233
1254
|
)
|
1234
1255
|
|
1235
1256
|
def populate_desired_state(self, ri: ResourceInventory) -> None:
|
@@ -1326,19 +1347,7 @@ class SaasHerder: # pylint: disable=too-many-public-methods
|
|
1326
1347
|
return None
|
1327
1348
|
|
1328
1349
|
try:
|
1329
|
-
resources, html_url, promotion = self._process_template(
|
1330
|
-
saas_file_name=spec.saas_file_name,
|
1331
|
-
resource_template_name=spec.resource_template_name,
|
1332
|
-
image_auth=spec.image_auth,
|
1333
|
-
url=spec.url,
|
1334
|
-
path=spec.path,
|
1335
|
-
provider=spec.provider,
|
1336
|
-
hash_length=spec.hash_length,
|
1337
|
-
target=spec.target,
|
1338
|
-
parameters=spec.parameters,
|
1339
|
-
github=spec.github,
|
1340
|
-
target_config_hash=spec.target_config_hash,
|
1341
|
-
)
|
1350
|
+
resources, html_url, promotion = self._process_template(spec)
|
1342
1351
|
except Exception as e:
|
1343
1352
|
# error log message send in _process_template. We log here debug to have a
|
1344
1353
|
# safeguard in case something breaks there unexpectedly. We cannot just
|
@@ -1670,10 +1679,10 @@ class SaasHerder: # pylint: disable=too-many-public-methods
|
|
1670
1679
|
image_uri = f"{image_registry}:{desired_image_tag}"
|
1671
1680
|
image_auth = self._initiate_image_auth(saas_file)
|
1672
1681
|
error_prefix = f"[{saas_file.name}/{rt.name}] {target.ref}:"
|
1673
|
-
|
1682
|
+
image = self._get_image(
|
1674
1683
|
image_uri, saas_file.image_patterns, image_auth, error_prefix
|
1675
1684
|
)
|
1676
|
-
if
|
1685
|
+
if not image:
|
1677
1686
|
continue
|
1678
1687
|
|
1679
1688
|
trigger_spec = TriggerSpecContainerImage(
|
File without changes
|
File without changes
|
{qontract_reconcile-0.10.1rc601.dist-info → qontract_reconcile-0.10.1rc603.dist-info}/top_level.txt
RENAMED
File without changes
|