qontract-reconcile 0.10.2.dev256__py3-none-any.whl → 0.10.2.dev258__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.2.dev256.dist-info → qontract_reconcile-0.10.2.dev258.dist-info}/METADATA +1 -1
- {qontract_reconcile-0.10.2.dev256.dist-info → qontract_reconcile-0.10.2.dev258.dist-info}/RECORD +96 -95
- reconcile/aus/advanced_upgrade_service.py +1 -1
- reconcile/aus/base.py +2 -2
- reconcile/aus/version_gates/sts_version_gate_handler.py +2 -2
- reconcile/aws_account_manager/reconciler.py +22 -20
- reconcile/aws_iam_keys.py +5 -5
- reconcile/aws_iam_password_reset.py +5 -5
- reconcile/aws_saml_roles/integration.py +5 -5
- reconcile/aws_version_sync/integration.py +4 -3
- reconcile/cli.py +16 -12
- reconcile/closedbox_endpoint_monitoring_base.py +1 -0
- reconcile/database_access_manager.py +4 -4
- reconcile/dynatrace_token_provider/integration.py +2 -2
- reconcile/external_resources/manager.py +2 -2
- reconcile/external_resources/model.py +1 -1
- reconcile/external_resources/secrets_sync.py +2 -2
- reconcile/gabi_authorized_users.py +3 -3
- reconcile/github_org.py +2 -2
- reconcile/gitlab_housekeeping.py +1 -1
- reconcile/gitlab_mr_sqs_consumer.py +1 -1
- reconcile/glitchtip/integration.py +2 -2
- reconcile/jenkins_worker_fleets.py +5 -5
- reconcile/ldap_groups/integration.py +3 -3
- reconcile/ocm_clusters.py +2 -2
- reconcile/ocm_internal_notifications/integration.py +2 -2
- reconcile/ocm_labels/integration.py +3 -2
- reconcile/openshift_base.py +12 -11
- reconcile/openshift_cluster_bots.py +2 -2
- reconcile/openshift_resources_base.py +3 -3
- reconcile/openshift_rhcs_certs.py +2 -2
- reconcile/openshift_saas_deploy.py +1 -1
- reconcile/quay_membership.py +4 -4
- reconcile/rhidp/common.py +3 -2
- reconcile/run_integration.py +7 -4
- reconcile/saas_auto_promotions_manager/dependencies.py +95 -0
- reconcile/saas_auto_promotions_manager/integration.py +85 -165
- reconcile/skupper_network/integration.py +3 -3
- reconcile/slack_usergroups.py +4 -4
- reconcile/status_board.py +3 -3
- reconcile/terraform_cloudflare_dns.py +5 -5
- reconcile/terraform_cloudflare_users.py +15 -17
- reconcile/terraform_resources.py +6 -6
- reconcile/terraform_vpc_peerings.py +9 -9
- reconcile/unleash_feature_toggles/integration.py +1 -1
- reconcile/utils/aggregated_list.py +2 -2
- reconcile/utils/aws_api_typed/iam.py +2 -2
- reconcile/utils/aws_api_typed/organization.py +4 -4
- reconcile/utils/aws_api_typed/service_quotas.py +4 -4
- reconcile/utils/aws_api_typed/support.py +9 -9
- reconcile/utils/aws_helper.py +1 -1
- reconcile/utils/config.py +8 -4
- reconcile/utils/deadmanssnitch_api.py +2 -4
- reconcile/utils/glitchtip/models.py +18 -12
- reconcile/utils/gql.py +4 -4
- reconcile/utils/internal_groups/client.py +2 -2
- reconcile/utils/jinja2/utils.py +7 -3
- reconcile/utils/jjb_client.py +2 -2
- reconcile/utils/models.py +2 -1
- reconcile/utils/mr/__init__.py +3 -3
- reconcile/utils/mr/app_interface_reporter.py +2 -2
- reconcile/utils/mr/aws_access.py +5 -2
- reconcile/utils/mr/base.py +3 -3
- reconcile/utils/mr/user_maintenance.py +1 -1
- reconcile/utils/oc.py +11 -11
- reconcile/utils/oc_connection_parameters.py +4 -4
- reconcile/utils/ocm/base.py +3 -3
- reconcile/utils/ocm/products.py +8 -8
- reconcile/utils/ocm/search_filters.py +2 -2
- reconcile/utils/openshift_resource.py +21 -18
- reconcile/utils/pagerduty_api.py +5 -5
- reconcile/utils/quay_api.py +2 -2
- reconcile/utils/rosa/rosa_cli.py +1 -1
- reconcile/utils/rosa/session.py +2 -2
- reconcile/utils/runtime/desired_state_diff.py +7 -7
- reconcile/utils/saasherder/interfaces.py +1 -0
- reconcile/utils/saasherder/models.py +1 -1
- reconcile/utils/saasherder/saasherder.py +1 -1
- reconcile/utils/secret_reader.py +20 -20
- reconcile/utils/slack_api.py +5 -5
- reconcile/utils/slo_document_manager.py +6 -6
- reconcile/utils/state.py +8 -8
- reconcile/utils/terraform_client.py +3 -3
- reconcile/utils/terrascript/cloudflare_client.py +2 -2
- reconcile/utils/terrascript/cloudflare_resources.py +1 -0
- reconcile/utils/terrascript_aws_client.py +12 -11
- reconcile/utils/vault.py +22 -22
- reconcile/vault_replication.py +15 -15
- tools/cli_commands/erv2.py +3 -2
- tools/cli_commands/gpg_encrypt.py +9 -9
- tools/cli_commands/systems_and_tools.py +1 -1
- tools/qontract_cli.py +13 -14
- tools/saas_promotion_state/saas_promotion_state.py +4 -4
- tools/template_validation.py +5 -5
- {qontract_reconcile-0.10.2.dev256.dist-info → qontract_reconcile-0.10.2.dev258.dist-info}/WHEEL +0 -0
- {qontract_reconcile-0.10.2.dev256.dist-info → qontract_reconcile-0.10.2.dev258.dist-info}/entry_points.txt +0 -0
@@ -11,10 +11,10 @@ from reconcile.utils.aws_api_typed.iam import (
|
|
11
11
|
)
|
12
12
|
from reconcile.utils.aws_api_typed.organization import AwsOrganizationOU
|
13
13
|
from reconcile.utils.aws_api_typed.service_quotas import (
|
14
|
-
|
14
|
+
AWSResourceAlreadyExistsError,
|
15
15
|
)
|
16
|
-
from reconcile.utils.aws_api_typed.support import
|
17
|
-
from reconcile.utils.state import
|
16
|
+
from reconcile.utils.aws_api_typed.support import SupportPlan
|
17
|
+
from reconcile.utils.state import AbortStateTransactionError, State
|
18
18
|
|
19
19
|
TASK_CREATE_ACCOUNT = "create-account"
|
20
20
|
TASK_DESCRIBE_ACCOUNT = "describe-account"
|
@@ -66,7 +66,7 @@ class AWSReconciler:
|
|
66
66
|
|
67
67
|
logging.info(f"{name}: Creating account")
|
68
68
|
if self.dry_run:
|
69
|
-
raise
|
69
|
+
raise AbortStateTransactionError("Dry run")
|
70
70
|
|
71
71
|
status = aws_api.organizations.create_account(email=email, name=name)
|
72
72
|
# store the status id for future reference
|
@@ -98,7 +98,9 @@ class AWSReconciler:
|
|
98
98
|
f"Account creation failed: {status.failure_reason}"
|
99
99
|
)
|
100
100
|
case "IN_PROGRESS":
|
101
|
-
raise
|
101
|
+
raise AbortStateTransactionError(
|
102
|
+
"Account creation still in progress"
|
103
|
+
)
|
102
104
|
case _:
|
103
105
|
raise RuntimeError(
|
104
106
|
f"Unexpected account creation status: {status.state}"
|
@@ -115,7 +117,7 @@ class AWSReconciler:
|
|
115
117
|
logging.info(f"{name}: Setting tags {tags}")
|
116
118
|
_state.value = tags
|
117
119
|
if self.dry_run:
|
118
|
-
raise
|
120
|
+
raise AbortStateTransactionError("Dry run")
|
119
121
|
|
120
122
|
if _state.exists:
|
121
123
|
aws_api.organizations.untag_resource(
|
@@ -138,7 +140,7 @@ class AWSReconciler:
|
|
138
140
|
logging.info(f"{name}: Moving account to OU {ou}")
|
139
141
|
destination = self._get_destination_ou(aws_api, destination_path=ou)
|
140
142
|
if self.dry_run:
|
141
|
-
raise
|
143
|
+
raise AbortStateTransactionError("Dry run")
|
142
144
|
|
143
145
|
aws_api.organizations.move_account(
|
144
146
|
uid=uid,
|
@@ -157,7 +159,7 @@ class AWSReconciler:
|
|
157
159
|
|
158
160
|
logging.info(f"{name}: Set account alias '{new_alias}'")
|
159
161
|
if self.dry_run:
|
160
|
-
raise
|
162
|
+
raise AbortStateTransactionError("Dry run")
|
161
163
|
|
162
164
|
aws_api.iam.set_account_alias(account_alias=new_alias)
|
163
165
|
|
@@ -193,7 +195,7 @@ class AWSReconciler:
|
|
193
195
|
)
|
194
196
|
|
195
197
|
if self.dry_run:
|
196
|
-
raise
|
198
|
+
raise AbortStateTransactionError("Dry run")
|
197
199
|
|
198
200
|
ids = []
|
199
201
|
for new_quota in new_quotas:
|
@@ -203,8 +205,8 @@ class AWSReconciler:
|
|
203
205
|
quota_code=new_quota.quota_code,
|
204
206
|
desired_value=new_quota.value,
|
205
207
|
)
|
206
|
-
except
|
207
|
-
raise
|
208
|
+
except AWSResourceAlreadyExistsError:
|
209
|
+
raise AbortStateTransactionError(
|
208
210
|
f"{name}: A quota increase for this {new_quota.service_code}/{new_quota.quota_code} already exists. Try it again later."
|
209
211
|
) from None
|
210
212
|
ids.append(req.id)
|
@@ -227,7 +229,7 @@ class AWSReconciler:
|
|
227
229
|
|
228
230
|
logging.info(f"{name}: Checking quota change requests")
|
229
231
|
if self.dry_run:
|
230
|
-
raise
|
232
|
+
raise AbortStateTransactionError("Dry run")
|
231
233
|
|
232
234
|
_state.value = []
|
233
235
|
for request_id in request_ids:
|
@@ -255,14 +257,14 @@ class AWSReconciler:
|
|
255
257
|
if _state.exists:
|
256
258
|
return _state.value
|
257
259
|
|
258
|
-
if aws_api.support.get_support_level() ==
|
260
|
+
if aws_api.support.get_support_level() == SupportPlan.ENTERPRISE:
|
259
261
|
if self.dry_run:
|
260
|
-
raise
|
262
|
+
raise AbortStateTransactionError("Dry run")
|
261
263
|
return None
|
262
264
|
|
263
265
|
logging.info(f"{name}: Enabling enterprise support")
|
264
266
|
if self.dry_run:
|
265
|
-
raise
|
267
|
+
raise AbortStateTransactionError("Dry run")
|
266
268
|
|
267
269
|
case_id = aws_api.support.create_case(
|
268
270
|
subject=f"Add account {uid} to Enterprise Support",
|
@@ -291,7 +293,7 @@ class AWSReconciler:
|
|
291
293
|
|
292
294
|
logging.info(f"{name}: Checking enterprise support case {case_id}")
|
293
295
|
if self.dry_run:
|
294
|
-
raise
|
296
|
+
raise AbortStateTransactionError("Dry run")
|
295
297
|
|
296
298
|
case = aws_api.support.describe_case(case_id=case_id)
|
297
299
|
if case.status == "resolved":
|
@@ -300,7 +302,7 @@ class AWSReconciler:
|
|
300
302
|
logging.info(
|
301
303
|
f"Enterprise support case {case_id} is still open. Current status: {case.status}"
|
302
304
|
)
|
303
|
-
raise
|
305
|
+
raise AbortStateTransactionError("Enterprise support case still open")
|
304
306
|
|
305
307
|
def _set_security_contact(
|
306
308
|
self,
|
@@ -322,7 +324,7 @@ class AWSReconciler:
|
|
322
324
|
|
323
325
|
logging.info(f"{name}: Setting security contact")
|
324
326
|
if self.dry_run:
|
325
|
-
raise
|
327
|
+
raise AbortStateTransactionError("Dry run")
|
326
328
|
|
327
329
|
aws_api.account.set_security_contact(
|
328
330
|
name=name, title=title, email=email, phone_number=phone_number
|
@@ -369,7 +371,7 @@ class AWSReconciler:
|
|
369
371
|
logging.info(f"{name}: Disabling regions {to_disable_regions}")
|
370
372
|
|
371
373
|
if self.dry_run:
|
372
|
-
raise
|
374
|
+
raise AbortStateTransactionError("Dry run")
|
373
375
|
|
374
376
|
for aws_region in to_enable_regions:
|
375
377
|
aws_api.account.enable_region(aws_region)
|
@@ -408,7 +410,7 @@ class AWSReconciler:
|
|
408
410
|
|
409
411
|
logging.info(f"{name}: Creating IAM user '{user_name}'")
|
410
412
|
if self.dry_run:
|
411
|
-
raise
|
413
|
+
raise AbortStateTransactionError("Dry run")
|
412
414
|
|
413
415
|
aws_api.iam.create_user(user_name=user_name)
|
414
416
|
aws_api.iam.attach_user_policy(
|
reconcile/aws_iam_keys.py
CHANGED
@@ -48,9 +48,9 @@ def init_tf_working_dirs(
|
|
48
48
|
thread_pool_size: int,
|
49
49
|
settings: Mapping[str, Any],
|
50
50
|
) -> dict[str, str]:
|
51
|
-
#
|
52
|
-
|
53
|
-
|
51
|
+
# Avoid circular import
|
52
|
+
import reconcile.terraform_resources # noqa: PLC0415
|
53
|
+
|
54
54
|
# if the terraform-resources integration is disabled
|
55
55
|
# for an account, it means that Terrascript will not
|
56
56
|
# initiate that account's config and will not create
|
@@ -59,8 +59,8 @@ def init_tf_working_dirs(
|
|
59
59
|
# created by terraform-resources, but it is disabled
|
60
60
|
# tl;dr - we are good. how cool is this alignment...
|
61
61
|
ts = Terrascript(
|
62
|
-
QONTRACT_INTEGRATION,
|
63
|
-
QONTRACT_TF_PREFIX,
|
62
|
+
reconcile.terraform_resources.QONTRACT_INTEGRATION,
|
63
|
+
reconcile.terraform_resources.QONTRACT_TF_PREFIX,
|
64
64
|
thread_pool_size,
|
65
65
|
accounts,
|
66
66
|
settings=settings,
|
@@ -41,7 +41,7 @@ class AwsProfileToReset(BaseModel):
|
|
41
41
|
|
42
42
|
class AwsAccountWithResets(BaseModel):
|
43
43
|
account: Mapping[str, Any]
|
44
|
-
|
44
|
+
reset_passwords: list[AwsProfileToReset]
|
45
45
|
|
46
46
|
|
47
47
|
@defer
|
@@ -63,7 +63,7 @@ def run(dry_run: bool, defer: Callable | None = None) -> None:
|
|
63
63
|
|
64
64
|
account_reset = AwsAccountWithResets(
|
65
65
|
account=a,
|
66
|
-
|
66
|
+
reset_passwords=[],
|
67
67
|
)
|
68
68
|
accounts_to_reset.append(account_reset)
|
69
69
|
|
@@ -83,7 +83,7 @@ def run(dry_run: bool, defer: Callable | None = None) -> None:
|
|
83
83
|
logging.error(f"User {user_name} is not in account {account_name}")
|
84
84
|
sys.exit(1)
|
85
85
|
|
86
|
-
account_reset.
|
86
|
+
account_reset.reset_passwords.append(
|
87
87
|
AwsProfileToReset(
|
88
88
|
user_name=user_name,
|
89
89
|
state_key=state_key,
|
@@ -91,11 +91,11 @@ def run(dry_run: bool, defer: Callable | None = None) -> None:
|
|
91
91
|
)
|
92
92
|
|
93
93
|
for a in accounts_to_reset:
|
94
|
-
if not a.
|
94
|
+
if not a.reset_passwords:
|
95
95
|
continue
|
96
96
|
|
97
97
|
with AWSApi(1, [a.account], settings=settings) as aws_api:
|
98
|
-
for r in a.
|
98
|
+
for r in a.reset_passwords:
|
99
99
|
user_name = r.user_name
|
100
100
|
state_key = r.state_key
|
101
101
|
|
@@ -58,7 +58,7 @@ class AwsSamlRolesIntegrationParams(PydanticRunParams):
|
|
58
58
|
log_cached_log_output: bool = False
|
59
59
|
|
60
60
|
@validator("max_session_duration_hours")
|
61
|
-
def max_session_duration_range(cls, v: str | int) -> int:
|
61
|
+
def max_session_duration_range(cls, v: str | int) -> int: # noqa: N805
|
62
62
|
if 1 <= int(v) <= 12:
|
63
63
|
return int(v)
|
64
64
|
raise ValueError("max_session_duration_hours must be between 1 and 12 hours")
|
@@ -69,7 +69,7 @@ class CustomPolicy(BaseModel):
|
|
69
69
|
policy: dict[str, Any]
|
70
70
|
|
71
71
|
@validator("name")
|
72
|
-
def name_size(cls, v: str) -> str:
|
72
|
+
def name_size(cls, v: str) -> str: # noqa: N805
|
73
73
|
"""Check the policy name size.
|
74
74
|
|
75
75
|
See https://docs.aws.amazon.com/IAM/latest/UserGuide/reference_iam-quotas.html
|
@@ -81,7 +81,7 @@ class CustomPolicy(BaseModel):
|
|
81
81
|
return v
|
82
82
|
|
83
83
|
@validator("policy")
|
84
|
-
def policy_size(cls, v: dict[str, Any]) -> dict[str, Any]:
|
84
|
+
def policy_size(cls, v: dict[str, Any]) -> dict[str, Any]: # noqa: N805
|
85
85
|
"""Check the policy size.
|
86
86
|
|
87
87
|
See https://docs.aws.amazon.com/IAM/latest/UserGuide/reference_iam-quotas.html
|
@@ -104,7 +104,7 @@ class AwsRole(BaseModel):
|
|
104
104
|
managed_policies: list[ManagedPolicy]
|
105
105
|
|
106
106
|
@validator("name")
|
107
|
-
def name_size(cls, v: str) -> str:
|
107
|
+
def name_size(cls, v: str) -> str: # noqa: N805
|
108
108
|
"""Check the role name size.
|
109
109
|
|
110
110
|
See https://docs.aws.amazon.com/IAM/latest/UserGuide/reference_iam-quotas.html
|
@@ -116,7 +116,7 @@ class AwsRole(BaseModel):
|
|
116
116
|
return v
|
117
117
|
|
118
118
|
@root_validator
|
119
|
-
def validate_policies(cls, values: dict[str, Any]) -> dict[str, Any]:
|
119
|
+
def validate_policies(cls, values: dict[str, Any]) -> dict[str, Any]: # noqa: N805
|
120
120
|
"""Check the policies.
|
121
121
|
|
122
122
|
See https://docs.aws.amazon.com/IAM/latest/UserGuide/reference_iam-quotas.html
|
@@ -107,15 +107,16 @@ class ExternalResource(BaseModel):
|
|
107
107
|
)
|
108
108
|
|
109
109
|
@validator("resource_engine_version", pre=True)
|
110
|
-
def parse_resource_engine_version(
|
111
|
-
cls,
|
110
|
+
def parse_resource_engine_version(
|
111
|
+
cls, # noqa: N805
|
112
|
+
v: str | semver.VersionInfo,
|
112
113
|
) -> semver.VersionInfo:
|
113
114
|
if isinstance(v, semver.VersionInfo):
|
114
115
|
return v
|
115
116
|
return parse_semver(str(v), optional_minor_and_patch=True)
|
116
117
|
|
117
118
|
@root_validator(pre=True)
|
118
|
-
def set_resource_engine_version_format(cls, values: dict) -> dict:
|
119
|
+
def set_resource_engine_version_format(cls, values: dict) -> dict: # noqa: N805
|
119
120
|
resource_engine_version, resource_engine_version_format = (
|
120
121
|
str(values.get("resource_engine_version")),
|
121
122
|
values.get("resource_engine_version_format"),
|
reconcile/cli.py
CHANGED
@@ -21,7 +21,7 @@ from reconcile.status import (
|
|
21
21
|
RunningState,
|
22
22
|
)
|
23
23
|
from reconcile.utils import gql
|
24
|
-
from reconcile.utils.aggregated_list import
|
24
|
+
from reconcile.utils.aggregated_list import RunnerError
|
25
25
|
from reconcile.utils.amtool import AMTOOL_VERSION, AMTOOL_VERSION_REGEX
|
26
26
|
from reconcile.utils.binary import (
|
27
27
|
binary,
|
@@ -533,7 +533,7 @@ def register_faulthandler(fileobj: TextIOWrapper | None = sys.__stderr__) -> Non
|
|
533
533
|
logging.debug("faulthandler enabled.")
|
534
534
|
faulthandler.register(SIGUSR1, file=fileobj, all_threads=True)
|
535
535
|
logging.debug("SIGUSR1 registered with faulthandler.")
|
536
|
-
except
|
536
|
+
except RunnerError:
|
537
537
|
logging.warning("Failed to register USR1 or enable faulthandler.")
|
538
538
|
else:
|
539
539
|
logging.debug("Skipping, faulthandler already enabled")
|
@@ -591,13 +591,13 @@ def run_class_integration(
|
|
591
591
|
print_url=ctx.obj["gql_url_print"],
|
592
592
|
)
|
593
593
|
)
|
594
|
-
except gql.
|
594
|
+
except gql.GqlApiIntegrationNotFoundError as e:
|
595
595
|
sys.stderr.write(str(e) + "\n")
|
596
596
|
sys.exit(ExitCodes.INTEGRATION_NOT_FOUND)
|
597
|
-
except
|
597
|
+
except RunnerError as e:
|
598
598
|
sys.stderr.write(str(e) + "\n")
|
599
599
|
sys.exit(ExitCodes.ERROR)
|
600
|
-
except gql.
|
600
|
+
except gql.GqlApiErrorForbiddenSchemaError as e:
|
601
601
|
sys.stderr.write(str(e) + "\n")
|
602
602
|
sys.exit(ExitCodes.FORBIDDEN_SCHEMA)
|
603
603
|
except Exception:
|
@@ -2375,14 +2375,18 @@ def saas_auto_promotions_manager(
|
|
2375
2375
|
env_name: str | None,
|
2376
2376
|
app_name: str | None,
|
2377
2377
|
) -> None:
|
2378
|
-
|
2378
|
+
from reconcile.saas_auto_promotions_manager.integration import (
|
2379
|
+
SaasAutoPromotionsManager,
|
2380
|
+
)
|
2379
2381
|
|
2380
|
-
|
2381
|
-
|
2382
|
-
|
2383
|
-
|
2384
|
-
|
2385
|
-
|
2382
|
+
run_class_integration(
|
2383
|
+
integration=SaasAutoPromotionsManager.create(
|
2384
|
+
env_name=env_name,
|
2385
|
+
app_name=app_name,
|
2386
|
+
thread_pool_size=thread_pool_size,
|
2387
|
+
dry_run=ctx.obj["dry_run"],
|
2388
|
+
),
|
2389
|
+
ctx=ctx,
|
2386
2390
|
)
|
2387
2391
|
|
2388
2392
|
|
@@ -707,7 +707,7 @@ class DatabaseAccessManagerIntegration(QontractReconcileIntegration):
|
|
707
707
|
integration=QONTRACT_INTEGRATION, secret_reader=self.secret_reader
|
708
708
|
)
|
709
709
|
|
710
|
-
|
710
|
+
encountered_errors: list[Exception] = []
|
711
711
|
|
712
712
|
namespaces = get_database_access_namespaces()
|
713
713
|
for namespace in namespaces:
|
@@ -741,9 +741,9 @@ class DatabaseAccessManagerIntegration(QontractReconcileIntegration):
|
|
741
741
|
vault_client,
|
742
742
|
)
|
743
743
|
except (JobFailedError, ValueError) as e:
|
744
|
-
|
744
|
+
encountered_errors.append(e)
|
745
745
|
|
746
|
-
if
|
747
|
-
for err in
|
746
|
+
if encountered_errors:
|
747
|
+
for err in encountered_errors:
|
748
748
|
logging.error(err)
|
749
749
|
raise JobFailedError("One or more jobs failed to complete")
|
@@ -62,7 +62,7 @@ DTP_V3_SPEC_SUFFIX = "token-spec"
|
|
62
62
|
DTP_V3_TENANT_SUFFIX = "tenant"
|
63
63
|
|
64
64
|
|
65
|
-
class
|
65
|
+
class ReconcileError(Exception):
|
66
66
|
def __init__(self, exceptions: Iterable[str]) -> None:
|
67
67
|
self.exceptions = exceptions
|
68
68
|
|
@@ -279,7 +279,7 @@ class DynatraceTokenProviderIntegration(QontractReconcileIntegration[NoParams]):
|
|
279
279
|
self._expose_token_metrics()
|
280
280
|
|
281
281
|
if unhandled_exceptions:
|
282
|
-
raise
|
282
|
+
raise ReconcileError(unhandled_exceptions)
|
283
283
|
|
284
284
|
def process_cluster(
|
285
285
|
self,
|
@@ -20,7 +20,7 @@ from reconcile.external_resources.model import (
|
|
20
20
|
ExternalResourceKey,
|
21
21
|
ExternalResourceModuleConfiguration,
|
22
22
|
ExternalResourceOrphanedResourcesError,
|
23
|
-
|
23
|
+
ExternalResourceOutputResourceNameDuplicationsError,
|
24
24
|
ExternalResourcesInventory,
|
25
25
|
ExternalResourceValidationError,
|
26
26
|
ModuleInventory,
|
@@ -93,7 +93,7 @@ class ExternalResourceDryRunsValidator:
|
|
93
93
|
for spec in self.er_inventory.values()
|
94
94
|
)
|
95
95
|
if duplicates := [key for key, count in specs.items() if count > 1]:
|
96
|
-
raise
|
96
|
+
raise ExternalResourceOutputResourceNameDuplicationsError(duplicates)
|
97
97
|
|
98
98
|
def _check_orphaned_objects(self) -> None:
|
99
99
|
state_keys = self.state_mgr.get_all_resource_keys()
|
@@ -50,7 +50,7 @@ class ExternalResourceOrphanedResourcesError(Exception):
|
|
50
50
|
super().__init__("".join(msg))
|
51
51
|
|
52
52
|
|
53
|
-
class
|
53
|
+
class ExternalResourceOutputResourceNameDuplicationsError(Exception):
|
54
54
|
def __init__(self, duplicates: Iterable[tuple[str, str, str]]) -> None:
|
55
55
|
msg = [
|
56
56
|
"There are output_resource_name attribute duplications. ",
|
@@ -34,7 +34,7 @@ from reconcile.utils.oc import (
|
|
34
34
|
)
|
35
35
|
from reconcile.utils.oc_map import OCMap, init_oc_map_from_clusters
|
36
36
|
from reconcile.utils.openshift_resource import OpenshiftResource, ResourceInventory
|
37
|
-
from reconcile.utils.secret_reader import
|
37
|
+
from reconcile.utils.secret_reader import SecretNotFoundError, SecretReaderBase
|
38
38
|
from reconcile.utils.three_way_diff_strategy import three_way_diff_using_hash
|
39
39
|
from reconcile.utils.vault import (
|
40
40
|
VaultClient,
|
@@ -455,5 +455,5 @@ class VaultSecretsReconciler(SecretsReconciler):
|
|
455
455
|
spec.metadata[SECRET_UPDATED_AT] = data[SECRET_UPDATED_AT]
|
456
456
|
del data[SECRET_UPDATED_AT]
|
457
457
|
spec.secret = data
|
458
|
-
except
|
458
|
+
except SecretNotFoundError:
|
459
459
|
logging.info("Error getting secret from vault, skipping. [%s]", secret_path)
|
@@ -15,7 +15,7 @@ from typing import Any
|
|
15
15
|
import reconcile.openshift_base as ob
|
16
16
|
from reconcile import queries
|
17
17
|
from reconcile.status import ExitCodes
|
18
|
-
from reconcile.utils.aggregated_list import
|
18
|
+
from reconcile.utils.aggregated_list import RunnerError
|
19
19
|
from reconcile.utils.defer import defer
|
20
20
|
from reconcile.utils.disabled_integrations import integration_is_enabled
|
21
21
|
from reconcile.utils.external_resources import get_external_resource_specs
|
@@ -66,7 +66,7 @@ def fetch_desired_state(
|
|
66
66
|
for g in gabi_instances:
|
67
67
|
expiration_date = datetime.strptime(g["expirationDate"], "%Y-%m-%d").date()
|
68
68
|
if (expiration_date - date.today()).days > EXPIRATION_DAYS_MAX:
|
69
|
-
raise
|
69
|
+
raise RunnerError(
|
70
70
|
f"The maximum expiration date of {g['name']} shall not "
|
71
71
|
f"exceed {EXPIRATION_DAYS_MAX} days from today"
|
72
72
|
)
|
@@ -91,7 +91,7 @@ def fetch_desired_state(
|
|
91
91
|
found = True
|
92
92
|
break
|
93
93
|
if not found:
|
94
|
-
raise
|
94
|
+
raise RunnerError(
|
95
95
|
f"[gabi:{g['name']} (path: {g['path']})] Could not find RDS identifier {identifier} "
|
96
96
|
f"for account {account} in namespace {namespace['name']}. "
|
97
97
|
"If this is a removed read only instance, consider updating the identifier to the source replica."
|
reconcile/github_org.py
CHANGED
@@ -416,10 +416,10 @@ class RunnerAction:
|
|
416
416
|
|
417
417
|
@staticmethod
|
418
418
|
def raise_exception(msg: str) -> Callable:
|
419
|
-
def
|
419
|
+
def _raise_exception(params: dict, items: dict) -> None:
|
420
420
|
raise Exception(msg)
|
421
421
|
|
422
|
-
return
|
422
|
+
return _raise_exception
|
423
423
|
|
424
424
|
|
425
425
|
def service_is(service: str) -> Callable:
|
reconcile/gitlab_housekeeping.py
CHANGED
@@ -59,7 +59,7 @@ def run(dry_run: str, gitlab_project_id: str, defer: Callable | None = None) ->
|
|
59
59
|
merge_request = mr.init_from_sqs_message(body)
|
60
60
|
merge_request.submit_to_gitlab(gitlab_cli=gitlab_cli)
|
61
61
|
sqs_cli.delete_message(str(receipt_handle))
|
62
|
-
except mr.
|
62
|
+
except mr.UnknownMergeRequestTypeError as ex:
|
63
63
|
# Received an unknown MR type.
|
64
64
|
# This could be a producer being on a newer version
|
65
65
|
# of qontract-reconcile than the consumer.
|
@@ -58,7 +58,7 @@ def get_user_role(organization: Organization, roles: RoleV1) -> str:
|
|
58
58
|
return DEFAULT_MEMBER_ROLE
|
59
59
|
|
60
60
|
|
61
|
-
class
|
61
|
+
class GlitchtipError(Exception):
|
62
62
|
pass
|
63
63
|
|
64
64
|
|
@@ -102,7 +102,7 @@ def fetch_desired_state(
|
|
102
102
|
)
|
103
103
|
# Check project is unique within an organization
|
104
104
|
if project.name in [p.name for p in organization.projects]:
|
105
|
-
raise
|
105
|
+
raise GlitchtipError(f'project name "{project.name}" already in use!')
|
106
106
|
for glitchtip_team in glitchtip_project.teams:
|
107
107
|
users: list[User] = []
|
108
108
|
|
@@ -36,11 +36,11 @@ def get_current_state(jenkins: JenkinsApi) -> list[JenkinsWorkerFleet]:
|
|
36
36
|
|
37
37
|
|
38
38
|
def get_desired_state(
|
39
|
-
terrascript: Terrascript,
|
39
|
+
terrascript: Terrascript, worker_fleets: list[dict[str, Any]]
|
40
40
|
) -> list[JenkinsWorkerFleet]:
|
41
41
|
desired_state = []
|
42
42
|
|
43
|
-
for f in
|
43
|
+
for f in worker_fleets:
|
44
44
|
namespace = f["namespace"]
|
45
45
|
account = f["account"]
|
46
46
|
identifier = f["identifier"]
|
@@ -131,8 +131,8 @@ def run(dry_run: bool) -> None:
|
|
131
131
|
)
|
132
132
|
|
133
133
|
for instance in jenkins_instances:
|
134
|
-
|
135
|
-
if not
|
134
|
+
worker_fleets = instance.get("workerFleets", [])
|
135
|
+
if not worker_fleets:
|
136
136
|
# Skip instance if no fleets defined
|
137
137
|
continue
|
138
138
|
|
@@ -140,7 +140,7 @@ def run(dry_run: bool) -> None:
|
|
140
140
|
instance_name = instance["name"]
|
141
141
|
jenkins = JenkinsApi.init_jenkins_from_secret(secret_reader, token)
|
142
142
|
current_state = get_current_state(jenkins)
|
143
|
-
desired_state = get_desired_state(terrascript,
|
143
|
+
desired_state = get_desired_state(terrascript, worker_fleets)
|
144
144
|
act(dry_run, instance_name, current_state, desired_state, jenkins)
|
145
145
|
|
146
146
|
|
@@ -22,7 +22,7 @@ from reconcile.utils.exceptions import (
|
|
22
22
|
from reconcile.utils.helpers import find_duplicates
|
23
23
|
from reconcile.utils.internal_groups.client import (
|
24
24
|
InternalGroupsClient,
|
25
|
-
|
25
|
+
NotFoundError,
|
26
26
|
)
|
27
27
|
from reconcile.utils.internal_groups.models import (
|
28
28
|
Entity,
|
@@ -226,7 +226,7 @@ class LdapGroupsIntegration(QontractReconcileIntegration[LdapGroupsIntegrationPa
|
|
226
226
|
"""Reach out to the internal groups API and fetch all managed groups."""
|
227
227
|
groups = []
|
228
228
|
for group_name in group_names:
|
229
|
-
with contextlib.suppress(
|
229
|
+
with contextlib.suppress(NotFoundError):
|
230
230
|
groups.append(internal_groups_client.group(group_name))
|
231
231
|
return groups
|
232
232
|
|
@@ -263,7 +263,7 @@ class LdapGroupsIntegration(QontractReconcileIntegration[LdapGroupsIntegrationPa
|
|
263
263
|
for group_to_remove in diff_result.delete.values():
|
264
264
|
logging.info(["delete_ldap_group", group_to_remove.name])
|
265
265
|
if not dry_run:
|
266
|
-
with contextlib.suppress(
|
266
|
+
with contextlib.suppress(NotFoundError):
|
267
267
|
internal_groups_client.delete_group(group_to_remove.name)
|
268
268
|
self._managed_groups.remove(group_to_remove.name)
|
269
269
|
|
reconcile/ocm_clusters.py
CHANGED
@@ -26,7 +26,7 @@ from reconcile.utils.ocm.products import (
|
|
26
26
|
IGNORE_NETWORK_TYPE_ATTR,
|
27
27
|
OCMProduct,
|
28
28
|
OCMProductPortfolio,
|
29
|
-
|
29
|
+
OCMValidationError,
|
30
30
|
build_product_portfolio,
|
31
31
|
)
|
32
32
|
from reconcile.utils.rosa.session import RosaSessionBuilder
|
@@ -448,7 +448,7 @@ class OcmClusters(QontractReconcileIntegration[OcmClustersParams]):
|
|
448
448
|
"its manifest to app-interface"
|
449
449
|
)
|
450
450
|
error = True
|
451
|
-
except
|
451
|
+
except OCMValidationError as e:
|
452
452
|
logging.error("[%s] Error creating cluster: %s", cluster_name, e)
|
453
453
|
error = True
|
454
454
|
|
@@ -23,7 +23,7 @@ from reconcile.utils.runtime.integration import (
|
|
23
23
|
NoParams,
|
24
24
|
QontractReconcileIntegration,
|
25
25
|
)
|
26
|
-
from reconcile.utils.slack_api import
|
26
|
+
from reconcile.utils.slack_api import UserNotFoundError
|
27
27
|
|
28
28
|
QONTRACT_INTEGRATION = "ocm-internal-notifications"
|
29
29
|
|
@@ -52,7 +52,7 @@ class OcmInternalNotifications(QontractReconcileIntegration[NoParams]):
|
|
52
52
|
return self.slack.get_user_id_by_name(
|
53
53
|
user_name=user_name, mail_address=mail_address
|
54
54
|
)
|
55
|
-
except
|
55
|
+
except UserNotFoundError:
|
56
56
|
return None
|
57
57
|
|
58
58
|
def run(self, dry_run: bool) -> None:
|
@@ -63,8 +63,9 @@ class OcmLabelsIntegrationParams(PydanticRunParams):
|
|
63
63
|
ignored_label_prefixes: list[str] = []
|
64
64
|
|
65
65
|
@validator("managed_label_prefixes", "ignored_label_prefixes")
|
66
|
-
def must_end_with_dot(
|
67
|
-
cls,
|
66
|
+
def must_end_with_dot(
|
67
|
+
cls, # noqa: N805
|
68
|
+
v: list[str],
|
68
69
|
) -> list[str]:
|
69
70
|
return [prefix + "." if not prefix.endswith(".") else prefix for prefix in v]
|
70
71
|
|