qontract-reconcile 0.10.1rc976__py3-none-any.whl → 0.10.1rc978__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.1rc976.dist-info → qontract_reconcile-0.10.1rc978.dist-info}/METADATA +1 -1
- {qontract_reconcile-0.10.1rc976.dist-info → qontract_reconcile-0.10.1rc978.dist-info}/RECORD +110 -110
- reconcile/aus/healthchecks.py +1 -1
- reconcile/aws_account_manager/integration.py +23 -21
- reconcile/aws_account_manager/reconciler.py +1 -1
- reconcile/aws_saml_idp/integration.py +5 -5
- reconcile/aws_saml_roles/integration.py +5 -5
- reconcile/aws_version_sync/utils.py +3 -3
- reconcile/cli.py +11 -2
- reconcile/cna/state.py +2 -2
- reconcile/database_access_manager.py +2 -5
- reconcile/external_resources/manager.py +3 -3
- reconcile/external_resources/model.py +1 -1
- reconcile/external_resources/secrets_sync.py +2 -2
- reconcile/external_resources/state.py +1 -1
- reconcile/gcr_mirror.py +2 -6
- reconcile/jira_permissions_validator.py +4 -4
- reconcile/ldap_groups/integration.py +4 -7
- reconcile/ocm_internal_notifications/integration.py +2 -2
- reconcile/openshift_base.py +14 -14
- reconcile/openshift_cluster_bots.py +1 -1
- reconcile/openshift_clusterrolebindings.py +9 -10
- reconcile/openshift_namespace_labels.py +2 -2
- reconcile/openshift_namespaces.py +1 -1
- reconcile/openshift_resources_base.py +9 -9
- reconcile/openshift_rolebindings.py +8 -11
- reconcile/openshift_saas_deploy_trigger_base.py +8 -5
- reconcile/oum/base.py +1 -1
- reconcile/quay_mirror.py +3 -10
- reconcile/queries.py +1 -1
- reconcile/saas_auto_promotions_manager/merge_request_manager/mr_parser.py +2 -2
- reconcile/saas_auto_promotions_manager/merge_request_manager/renderer.py +1 -1
- reconcile/saas_auto_promotions_manager/utils/saas_files_inventory.py +0 -1
- reconcile/skupper_network/integration.py +3 -1
- reconcile/skupper_network/site_controller.py +8 -8
- reconcile/slack_usergroups.py +10 -10
- reconcile/status_board.py +1 -1
- reconcile/statuspage/status.py +1 -3
- reconcile/terraform_cloudflare_dns.py +2 -3
- reconcile/terraform_cloudflare_users.py +2 -3
- reconcile/terraform_repo.py +5 -3
- reconcile/terraform_resources.py +16 -16
- reconcile/terraform_tgw_attachments.py +6 -6
- reconcile/terraform_vpc_peerings.py +8 -8
- reconcile/terraform_vpc_resources/integration.py +1 -1
- reconcile/test/test_aws_cloudwatch_log_retention.py +2 -5
- reconcile/test/test_github_org.py +18 -16
- reconcile/test/test_github_repo_invites.py +10 -10
- reconcile/test/test_integrations_manager.py +11 -11
- reconcile/test/test_ocm_additional_routers.py +6 -6
- reconcile/test/test_ocm_clusters.py +1 -0
- reconcile/test/test_ocm_update_recommended_version.py +2 -2
- reconcile/test/test_openshift_serviceaccount_tokens.py +5 -5
- reconcile/test/test_openshift_tekton_resources.py +3 -3
- reconcile/test/test_saasherder.py +48 -48
- reconcile/test/test_sql_query.py +1 -1
- reconcile/test/test_terraform_cloudflare_resources.py +14 -14
- reconcile/test/test_terraform_cloudflare_users.py +4 -4
- reconcile/test/test_terraform_resources.py +32 -32
- reconcile/test/test_terraform_tgw_attachments.py +5 -5
- reconcile/typed_queries/saas_files.py +1 -1
- reconcile/unleash_feature_toggles/integration.py +2 -2
- reconcile/utils/aggregated_list.py +1 -1
- reconcile/utils/aws_api.py +1 -1
- reconcile/utils/aws_api_typed/iam.py +4 -2
- reconcile/utils/aws_api_typed/service_quotas.py +2 -2
- reconcile/utils/binary.py +1 -1
- reconcile/utils/clusterhealth/telemeter.py +5 -3
- reconcile/utils/config.py +4 -2
- reconcile/utils/expiration.py +1 -1
- reconcile/utils/external_resources.py +4 -7
- reconcile/utils/git.py +1 -3
- reconcile/utils/gitlab_api.py +1 -4
- reconcile/utils/gql.py +9 -8
- reconcile/utils/helm.py +1 -1
- reconcile/utils/imap_client.py +3 -1
- reconcile/utils/internal_groups/client.py +1 -1
- reconcile/utils/jinja2/utils.py +4 -4
- reconcile/utils/jobcontroller/models.py +2 -2
- reconcile/utils/jump_host.py +1 -1
- reconcile/utils/ldap_client.py +1 -1
- reconcile/utils/merge_request_manager/merge_request_manager.py +1 -4
- reconcile/utils/models.py +2 -5
- reconcile/utils/oc.py +18 -28
- reconcile/utils/ocm/ocm.py +3 -1
- reconcile/utils/ocm/products.py +2 -2
- reconcile/utils/ocm/search_filters.py +4 -11
- reconcile/utils/ocm_base_client.py +1 -4
- reconcile/utils/openshift_resource.py +12 -19
- reconcile/utils/quay_api.py +1 -3
- reconcile/utils/repo_owners.py +2 -8
- reconcile/utils/runtime/runner.py +1 -1
- reconcile/utils/saasherder/saasherder.py +4 -4
- reconcile/utils/secret_reader.py +2 -2
- reconcile/utils/slack_api.py +1 -1
- reconcile/utils/sqs_gateway.py +1 -1
- reconcile/utils/state.py +4 -4
- reconcile/utils/terraform/config_client.py +1 -1
- reconcile/utils/terraform_client.py +2 -2
- reconcile/utils/terrascript_aws_client.py +13 -23
- reconcile/utils/three_way_diff_strategy.py +3 -9
- reconcile/utils/unleash/client.py +2 -2
- reconcile/utils/vault.py +11 -14
- reconcile/utils/vcs.py +1 -1
- reconcile/vault_replication.py +1 -1
- tools/app_interface_reporter.py +2 -3
- tools/qontract_cli.py +1 -1
- {qontract_reconcile-0.10.1rc976.dist-info → qontract_reconcile-0.10.1rc978.dist-info}/WHEEL +0 -0
- {qontract_reconcile-0.10.1rc976.dist-info → qontract_reconcile-0.10.1rc978.dist-info}/entry_points.txt +0 -0
- {qontract_reconcile-0.10.1rc976.dist-info → qontract_reconcile-0.10.1rc978.dist-info}/top_level.txt +0 -0
@@ -121,7 +121,7 @@ class TestOpenshiftTektonResources:
|
|
121
121
|
self.test_data.providers = [self.provider1, self.provider2_wr]
|
122
122
|
|
123
123
|
tkn_providers = otr.fetch_tkn_providers(None)
|
124
|
-
keys_expected =
|
124
|
+
keys_expected = {self.provider1["name"], self.provider2_wr["name"]}
|
125
125
|
assert tkn_providers.keys() == keys_expected
|
126
126
|
|
127
127
|
def test_duplicate_providers(self) -> None:
|
@@ -146,10 +146,10 @@ class TestOpenshiftTektonResources:
|
|
146
146
|
self.test_data.providers = [self.provider1]
|
147
147
|
desired_resources = otr.fetch_desired_resources(otr.fetch_tkn_providers(None))
|
148
148
|
|
149
|
-
expected_task_names =
|
149
|
+
expected_task_names = {
|
150
150
|
"o-push-gateway-openshift-saas-deploy-task-status-metric",
|
151
151
|
"o-openshift-saas-deploy-saas1",
|
152
|
-
|
152
|
+
}
|
153
153
|
expected_pipeline_name = "o-saas-deploy-saas1"
|
154
154
|
|
155
155
|
task_names = set()
|
@@ -1487,39 +1487,39 @@ def test_render_templated_parameters(
|
|
1487
1487
|
assert saas_file.resource_templates[0].targets[0].secret_parameters == [
|
1488
1488
|
SaasResourceTemplateTargetV2_SaasSecretParametersV1(
|
1489
1489
|
name="no-template",
|
1490
|
-
secret=
|
1491
|
-
path
|
1492
|
-
field
|
1493
|
-
version
|
1494
|
-
format
|
1495
|
-
|
1490
|
+
secret={
|
1491
|
+
"path": "path/to/secret",
|
1492
|
+
"field": "secret_key",
|
1493
|
+
"version": 1,
|
1494
|
+
"format": None,
|
1495
|
+
},
|
1496
1496
|
),
|
1497
1497
|
SaasResourceTemplateTargetV2_SaasSecretParametersV1(
|
1498
1498
|
name="ignore-go-template",
|
1499
|
-
secret=
|
1500
|
-
path
|
1501
|
-
field
|
1502
|
-
version
|
1503
|
-
format
|
1504
|
-
|
1499
|
+
secret={
|
1500
|
+
"path": "path/{{ .GO_PARAM }}/secret",
|
1501
|
+
"field": "{{ .GO_PARAM }}-secret_key",
|
1502
|
+
"version": 1,
|
1503
|
+
"format": None,
|
1504
|
+
},
|
1505
1505
|
),
|
1506
1506
|
SaasResourceTemplateTargetV2_SaasSecretParametersV1(
|
1507
1507
|
name="template-param-1",
|
1508
|
-
secret=
|
1509
|
-
path
|
1510
|
-
field
|
1511
|
-
version
|
1512
|
-
format
|
1513
|
-
|
1508
|
+
secret={
|
1509
|
+
"path": "path/appsres03ue1/test-namespace/secret",
|
1510
|
+
"field": "secret_key",
|
1511
|
+
"version": 1,
|
1512
|
+
"format": None,
|
1513
|
+
},
|
1514
1514
|
),
|
1515
1515
|
SaasResourceTemplateTargetV2_SaasSecretParametersV1(
|
1516
1516
|
name="template-param-2",
|
1517
|
-
secret=
|
1518
|
-
path
|
1519
|
-
field
|
1520
|
-
version
|
1521
|
-
format
|
1522
|
-
|
1517
|
+
secret={
|
1518
|
+
"path": "path/appsres03ue1/test-namespace/secret",
|
1519
|
+
"field": "App-SRE-stage-secret_key",
|
1520
|
+
"version": 1,
|
1521
|
+
"format": None,
|
1522
|
+
},
|
1523
1523
|
),
|
1524
1524
|
]
|
1525
1525
|
|
@@ -1549,38 +1549,38 @@ def test_render_templated_parameters_in_init(
|
|
1549
1549
|
assert saas_file.resource_templates[0].targets[0].secret_parameters == [
|
1550
1550
|
SaasResourceTemplateTargetV2_SaasSecretParametersV1(
|
1551
1551
|
name="no-template",
|
1552
|
-
secret=
|
1553
|
-
path
|
1554
|
-
field
|
1555
|
-
version
|
1556
|
-
format
|
1557
|
-
|
1552
|
+
secret={
|
1553
|
+
"path": "path/to/secret",
|
1554
|
+
"field": "secret_key",
|
1555
|
+
"version": 1,
|
1556
|
+
"format": None,
|
1557
|
+
},
|
1558
1558
|
),
|
1559
1559
|
SaasResourceTemplateTargetV2_SaasSecretParametersV1(
|
1560
1560
|
name="ignore-go-template",
|
1561
|
-
secret=
|
1562
|
-
path
|
1563
|
-
field
|
1564
|
-
version
|
1565
|
-
format
|
1566
|
-
|
1561
|
+
secret={
|
1562
|
+
"path": "path/{{ .GO_PARAM }}/secret",
|
1563
|
+
"field": "{{ .GO_PARAM }}-secret_key",
|
1564
|
+
"version": 1,
|
1565
|
+
"format": None,
|
1566
|
+
},
|
1567
1567
|
),
|
1568
1568
|
SaasResourceTemplateTargetV2_SaasSecretParametersV1(
|
1569
1569
|
name="template-param-1",
|
1570
|
-
secret=
|
1571
|
-
path
|
1572
|
-
field
|
1573
|
-
version
|
1574
|
-
format
|
1575
|
-
|
1570
|
+
secret={
|
1571
|
+
"path": "path/appsres03ue1/test-namespace/secret",
|
1572
|
+
"field": "secret_key",
|
1573
|
+
"version": 1,
|
1574
|
+
"format": None,
|
1575
|
+
},
|
1576
1576
|
),
|
1577
1577
|
SaasResourceTemplateTargetV2_SaasSecretParametersV1(
|
1578
1578
|
name="template-param-2",
|
1579
|
-
secret=
|
1580
|
-
path
|
1581
|
-
field
|
1582
|
-
version
|
1583
|
-
format
|
1584
|
-
|
1579
|
+
secret={
|
1580
|
+
"path": "path/appsres03ue1/test-namespace/secret",
|
1581
|
+
"field": "App-SRE-stage-secret_key",
|
1582
|
+
"version": 1,
|
1583
|
+
"format": None,
|
1584
|
+
},
|
1585
1585
|
),
|
1586
1586
|
]
|
reconcile/test/test_sql_query.py
CHANGED
@@ -107,7 +107,7 @@ def setup_mocks(
|
|
107
107
|
mocked_queries.get_app_interface_settings.return_value = {}
|
108
108
|
mocked_queries.get_app_interface_sql_queries.return_value = [sql_query]
|
109
109
|
mocked_state = create_autospec(State)
|
110
|
-
mocked_state.ls.return_value = [f"/{k}" for k in state
|
110
|
+
mocked_state.ls.return_value = [f"/{k}" for k in state]
|
111
111
|
mocked_state.__getitem__.side_effect = lambda x: state[x]
|
112
112
|
mocked_secret_reader = mocker.patch("reconcile.sql_query.SecretReader")
|
113
113
|
mocked_secret_reader.return_value.read_all_secret.return_value = (
|
@@ -166,23 +166,23 @@ def mock_app_interface_vault_settings(mocker):
|
|
166
166
|
|
167
167
|
|
168
168
|
def secret_reader_side_effect(*args):
|
169
|
-
if {
|
169
|
+
if args[0] == {
|
170
170
|
"path": "aws-account-path",
|
171
171
|
"field": "token",
|
172
172
|
"version": 1,
|
173
173
|
"q_format": "plain",
|
174
|
-
}
|
174
|
+
}:
|
175
175
|
aws_acct_creds = {}
|
176
176
|
aws_acct_creds["aws_access_key_id"] = "key_id"
|
177
177
|
aws_acct_creds["aws_secret_access_key"] = "access_key"
|
178
178
|
return aws_acct_creds
|
179
179
|
|
180
|
-
if {
|
180
|
+
if args[0] == {
|
181
181
|
"path": "cf-account-path",
|
182
182
|
"field": "key",
|
183
183
|
"version": 1,
|
184
184
|
"q_format": "plain",
|
185
|
-
}
|
185
|
+
}:
|
186
186
|
cf_acct_creds = {}
|
187
187
|
cf_acct_creds["api_token"] = "api_token"
|
188
188
|
cf_acct_creds["account_id"] = "account_id"
|
@@ -289,8 +289,8 @@ def test_cloudflare_accounts_validation(
|
|
289
289
|
with caplog.at_level(logging.INFO), pytest.raises(SystemExit) as sample:
|
290
290
|
integ.run(True, None, False, 10)
|
291
291
|
assert sample.value.code == 0
|
292
|
-
assert [
|
293
|
-
|
292
|
+
assert [rec.message for rec in caplog.records] == [
|
293
|
+
"No Cloudflare accounts were detected, nothing to do."
|
294
294
|
]
|
295
295
|
|
296
296
|
|
@@ -313,8 +313,8 @@ def test_namespace_validation(
|
|
313
313
|
with caplog.at_level(logging.INFO), pytest.raises(SystemExit) as sample:
|
314
314
|
integ.run(True, None, False, 10)
|
315
315
|
assert sample.value.code == 0
|
316
|
-
assert [
|
317
|
-
|
316
|
+
assert [rec.message for rec in caplog.records] == [
|
317
|
+
"No namespaces were detected, nothing to do."
|
318
318
|
]
|
319
319
|
|
320
320
|
|
@@ -356,27 +356,27 @@ def test_cloudflare_namespace_validation(
|
|
356
356
|
with caplog.at_level(logging.INFO), pytest.raises(SystemExit) as sample:
|
357
357
|
integ.run(True, None, False, 10)
|
358
358
|
assert sample.value.code == 0
|
359
|
-
assert [
|
360
|
-
|
359
|
+
assert [rec.message for rec in caplog.records] == [
|
360
|
+
"No cloudflare namespaces were detected, nothing to do."
|
361
361
|
]
|
362
362
|
|
363
363
|
|
364
364
|
def custom_ssl_secret_reader_side_effect(*args):
|
365
365
|
"""For use of secret_reader inside cloudflare client"""
|
366
|
-
if {
|
366
|
+
if args[0] == {
|
367
367
|
"path": "certificate/secret/cert/path",
|
368
368
|
"field": "certificate.crt",
|
369
369
|
"version": 1,
|
370
370
|
"q_format": "plain",
|
371
|
-
}
|
371
|
+
}:
|
372
372
|
return "----- CERTIFICATE -----"
|
373
373
|
|
374
|
-
if {
|
374
|
+
if args[0] == {
|
375
375
|
"path": "certificate/secret/cert/path",
|
376
376
|
"field": "certificate.key",
|
377
377
|
"version": 1,
|
378
378
|
"q_format": "plain",
|
379
|
-
}
|
379
|
+
}:
|
380
380
|
return "----- KEY -----"
|
381
381
|
|
382
382
|
|
@@ -426,23 +426,23 @@ def app_interface_settings_cloudflare_and_vault():
|
|
426
426
|
|
427
427
|
|
428
428
|
def secret_reader_side_effect(*args):
|
429
|
-
if {
|
429
|
+
if args[0] == {
|
430
430
|
"path": "some-path",
|
431
431
|
"field": "some-field",
|
432
432
|
"version": None,
|
433
433
|
"q_format": None,
|
434
|
-
}
|
434
|
+
}:
|
435
435
|
aws_acct_creds = {}
|
436
436
|
aws_acct_creds["aws_access_key_id"] = "key_id"
|
437
437
|
aws_acct_creds["aws_secret_access_key"] = "access_key"
|
438
438
|
return aws_acct_creds
|
439
439
|
|
440
|
-
if {
|
440
|
+
if args[0] == {
|
441
441
|
"path": "creds",
|
442
442
|
"field": "some-field",
|
443
443
|
"version": None,
|
444
444
|
"q_format": None,
|
445
|
-
}
|
445
|
+
}:
|
446
446
|
cf_acct_creds = {}
|
447
447
|
cf_acct_creds["api_token"] = "api_token"
|
448
448
|
cf_acct_creds["account_id"] = "account_id"
|
@@ -449,22 +449,22 @@ def test_terraform_resources_runner_dry_run(
|
|
449
449
|
|
450
450
|
defer = MagicMock()
|
451
451
|
|
452
|
-
runner_params =
|
453
|
-
accounts
|
454
|
-
account_names
|
455
|
-
tf_namespaces
|
456
|
-
tf
|
457
|
-
ts
|
458
|
-
secret_reader
|
459
|
-
dry_run
|
460
|
-
enable_deletion
|
461
|
-
thread_pool_size
|
462
|
-
internal
|
463
|
-
use_jump_host
|
464
|
-
light
|
465
|
-
vault_output_path
|
466
|
-
defer
|
467
|
-
|
452
|
+
runner_params = {
|
453
|
+
"accounts": [{"name": "a"}],
|
454
|
+
"account_names": {"a"},
|
455
|
+
"tf_namespaces": [],
|
456
|
+
"tf": tf,
|
457
|
+
"ts": ts,
|
458
|
+
"secret_reader": secret_reader,
|
459
|
+
"dry_run": True,
|
460
|
+
"enable_deletion": False,
|
461
|
+
"thread_pool_size": 10,
|
462
|
+
"internal": None,
|
463
|
+
"use_jump_host": True,
|
464
|
+
"light": False,
|
465
|
+
"vault_output_path": "",
|
466
|
+
"defer": defer,
|
467
|
+
}
|
468
468
|
|
469
469
|
result = integ.runner(**runner_params)
|
470
470
|
|
@@ -494,22 +494,22 @@ def test_terraform_resources_runner_no_dry_run(
|
|
494
494
|
mocked_ob = mocker.patch("reconcile.terraform_resources.ob")
|
495
495
|
mocked_ob.realize_data.return_value = [{"action": "applied"}]
|
496
496
|
|
497
|
-
runner_params =
|
498
|
-
accounts
|
499
|
-
account_names
|
500
|
-
tf_namespaces
|
501
|
-
tf
|
502
|
-
ts
|
503
|
-
secret_reader
|
504
|
-
dry_run
|
505
|
-
enable_deletion
|
506
|
-
thread_pool_size
|
507
|
-
internal
|
508
|
-
use_jump_host
|
509
|
-
light
|
510
|
-
vault_output_path
|
511
|
-
defer
|
512
|
-
|
497
|
+
runner_params = {
|
498
|
+
"accounts": [{"name": "a"}],
|
499
|
+
"account_names": {"a"},
|
500
|
+
"tf_namespaces": [],
|
501
|
+
"tf": tf,
|
502
|
+
"ts": ts,
|
503
|
+
"secret_reader": secret_reader,
|
504
|
+
"dry_run": False,
|
505
|
+
"enable_deletion": False,
|
506
|
+
"thread_pool_size": 10,
|
507
|
+
"internal": None,
|
508
|
+
"use_jump_host": True,
|
509
|
+
"light": False,
|
510
|
+
"vault_output_path": "",
|
511
|
+
"defer": defer,
|
512
|
+
}
|
513
513
|
|
514
514
|
result = integ.runner(**runner_params)
|
515
515
|
|
@@ -1124,7 +1124,7 @@ def test_duplicate_tgw_connection_names(
|
|
1124
1124
|
with pytest.raises(integ.ValidationError) as e:
|
1125
1125
|
integ.run(True)
|
1126
1126
|
|
1127
|
-
assert "duplicate tgw connection names found"
|
1127
|
+
assert str(e.value) == "duplicate tgw connection names found"
|
1128
1128
|
|
1129
1129
|
|
1130
1130
|
def test_missing_vpc_id(
|
@@ -1149,7 +1149,7 @@ def test_missing_vpc_id(
|
|
1149
1149
|
with pytest.raises(RuntimeError) as e:
|
1150
1150
|
integ.run(True)
|
1151
1151
|
|
1152
|
-
assert "Could not find VPC ID for cluster"
|
1152
|
+
assert str(e.value) == "Could not find VPC ID for cluster"
|
1153
1153
|
|
1154
1154
|
|
1155
1155
|
def test_error_in_tf_plan(
|
@@ -1176,7 +1176,7 @@ def test_error_in_tf_plan(
|
|
1176
1176
|
with pytest.raises(RuntimeError) as e:
|
1177
1177
|
integ.run(True)
|
1178
1178
|
|
1179
|
-
assert "Error running terraform plan"
|
1179
|
+
assert str(e.value) == "Error running terraform plan"
|
1180
1180
|
|
1181
1181
|
|
1182
1182
|
def test_disabled_deletions_detected_in_tf_plan(
|
@@ -1203,7 +1203,7 @@ def test_disabled_deletions_detected_in_tf_plan(
|
|
1203
1203
|
with pytest.raises(RuntimeError) as e:
|
1204
1204
|
integ.run(True)
|
1205
1205
|
|
1206
|
-
assert "Disabled deletions detected running terraform plan"
|
1206
|
+
assert str(e.value) == "Disabled deletions detected running terraform plan"
|
1207
1207
|
|
1208
1208
|
|
1209
1209
|
def test_error_in_terraform_apply(
|
@@ -1230,7 +1230,7 @@ def test_error_in_terraform_apply(
|
|
1230
1230
|
with pytest.raises(RuntimeError) as e:
|
1231
1231
|
integ.run(False)
|
1232
1232
|
|
1233
|
-
assert "Error running terraform apply"
|
1233
|
+
assert str(e.value) == "Error running terraform apply"
|
1234
1234
|
|
1235
1235
|
|
1236
1236
|
def test_early_exit_desired_state(
|
@@ -141,7 +141,7 @@ class UnleashTogglesIntegration(
|
|
141
141
|
except KeyError:
|
142
142
|
raise ValueError(
|
143
143
|
f"[{instance.name}/{project_id}/{add.name}] Invalid feature toggle type '{add.unleash.q_type}', Possible values are: {', '.join(FeatureToggleType.__members__)}"
|
144
|
-
)
|
144
|
+
) from None
|
145
145
|
if not dry_run:
|
146
146
|
client.create_feature_toggle(
|
147
147
|
project_id=project_id,
|
@@ -161,7 +161,7 @@ class UnleashTogglesIntegration(
|
|
161
161
|
except KeyError:
|
162
162
|
raise ValueError(
|
163
163
|
f"[{instance.name}/{project_id}/{change.current.name}] Invalid feature toggle type '{change.desired.unleash.q_type}', Possible values are: {', '.join(FeatureToggleType.__members__)}"
|
164
|
-
)
|
164
|
+
) from None
|
165
165
|
if not dry_run:
|
166
166
|
client.update_feature_toggle(
|
167
167
|
project_id=project_id,
|
reconcile/utils/aws_api.py
CHANGED
@@ -818,7 +818,7 @@ class AWSApi: # pylint: disable=too-many-public-methods
|
|
818
818
|
assume_role may be None for ROSA (CCS) clusters where we own the account
|
819
819
|
"""
|
820
820
|
required_keys = ["name", "assume_region"]
|
821
|
-
ok = all(elem in account
|
821
|
+
ok = all(elem in account for elem in required_keys)
|
822
822
|
if not ok:
|
823
823
|
account_name = account.get("name")
|
824
824
|
raise KeyError(f"[{account_name}] account is missing required keys")
|
@@ -41,7 +41,9 @@ class AWSApiIam:
|
|
41
41
|
user = self.client.create_user(UserName=user_name)
|
42
42
|
return AWSUser(**user["User"])
|
43
43
|
except self.client.exceptions.EntityAlreadyExistsException:
|
44
|
-
raise AWSEntityAlreadyExistsException(
|
44
|
+
raise AWSEntityAlreadyExistsException(
|
45
|
+
f"User {user_name} already exists"
|
46
|
+
) from None
|
45
47
|
|
46
48
|
def attach_user_policy(self, user_name: str, policy_arn: str) -> None:
|
47
49
|
"""Attach a policy to a user."""
|
@@ -62,4 +64,4 @@ class AWSApiIam:
|
|
62
64
|
if self.get_account_alias() != account_alias:
|
63
65
|
raise ValueError(
|
64
66
|
"Account alias already exists for another AWS account. Choose another one!"
|
65
|
-
)
|
67
|
+
) from None
|
@@ -64,7 +64,7 @@ class AWSApiServiceQuotas:
|
|
64
64
|
except self.client.exceptions.ResourceAlreadyExistsException:
|
65
65
|
raise AWSResourceAlreadyExistsException(
|
66
66
|
f"Service quota increase request {service_code=}, {quota_code=} already exists."
|
67
|
-
)
|
67
|
+
) from None
|
68
68
|
|
69
69
|
def get_service_quota(self, service_code: str, quota_code: str) -> AWSQuota:
|
70
70
|
"""Return the current value of the service quota."""
|
@@ -76,4 +76,4 @@ class AWSApiServiceQuotas:
|
|
76
76
|
except self.client.exceptions.NoSuchResourceException:
|
77
77
|
raise AWSNoSuchResourceException(
|
78
78
|
f"Service quota {service_code=}, {quota_code=} not found."
|
79
|
-
)
|
79
|
+
) from None
|
reconcile/utils/binary.py
CHANGED
@@ -1,4 +1,4 @@
|
|
1
|
-
from functools import
|
1
|
+
from functools import lru_cache
|
2
2
|
|
3
3
|
from reconcile.utils.clusterhealth.providerbase import (
|
4
4
|
ClusterHealth,
|
@@ -13,9 +13,11 @@ TELEMETER_SOURCE = "telemeter"
|
|
13
13
|
class TelemeterClusterHealthProvider(ClusterHealthProvider):
|
14
14
|
def __init__(self, querier: PrometheusQuerier):
|
15
15
|
self.querier = querier
|
16
|
+
self.cluster_health_for_org = lru_cache(maxsize=None)(
|
17
|
+
self._cluster_health_for_org
|
18
|
+
)
|
16
19
|
|
17
|
-
|
18
|
-
def cluster_health_for_org(self, org_id: str) -> dict[str, ClusterHealth]:
|
20
|
+
def _cluster_health_for_org(self, org_id: str) -> dict[str, ClusterHealth]:
|
19
21
|
vectors_by_cluster = group_by(
|
20
22
|
self.querier.instant_vector_query(telemeter_alert_query(org_id)),
|
21
23
|
lambda v: v.mandatory_label("_id"),
|
reconcile/utils/config.py
CHANGED
@@ -35,7 +35,7 @@ def read(secret):
|
|
35
35
|
config = config[t]
|
36
36
|
return config[field]
|
37
37
|
except Exception as e:
|
38
|
-
raise SecretNotFound(f"key not found in config file {path}: {str(e)}")
|
38
|
+
raise SecretNotFound(f"key not found in config file {path}: {str(e)}") from None
|
39
39
|
|
40
40
|
|
41
41
|
def read_all(secret):
|
@@ -47,4 +47,6 @@ def read_all(secret):
|
|
47
47
|
config = config[t]
|
48
48
|
return config
|
49
49
|
except Exception as e:
|
50
|
-
raise SecretNotFound(
|
50
|
+
raise SecretNotFound(
|
51
|
+
f"secret {path} not found in config file: {str(e)}"
|
52
|
+
) from None
|
reconcile/utils/expiration.py
CHANGED
@@ -62,10 +62,7 @@ def get_provision_providers(namespace_info: Mapping[str, Any]) -> set[str]:
|
|
62
62
|
|
63
63
|
|
64
64
|
def managed_external_resources(namespace_info: Mapping[str, Any]) -> bool:
|
65
|
-
|
66
|
-
return True
|
67
|
-
|
68
|
-
return False
|
65
|
+
return bool(namespace_info.get("managedExternalResources"))
|
69
66
|
|
70
67
|
|
71
68
|
def get_inventory_count_combinations(
|
@@ -141,7 +138,7 @@ class ResourceValueResolver:
|
|
141
138
|
resource = self._spec.resource
|
142
139
|
|
143
140
|
keys_to_add = [
|
144
|
-
key for key in self._spec.resource
|
141
|
+
key for key in self._spec.resource if key not in self._IGNORE_KEYS
|
145
142
|
]
|
146
143
|
|
147
144
|
defaults_path = resource.get("defaults", None)
|
@@ -176,7 +173,7 @@ class ResourceValueResolver:
|
|
176
173
|
values.pop("$schema", None)
|
177
174
|
except anymarkup.AnyMarkupError:
|
178
175
|
e_msg = "Could not parse data. Skipping resource: {}"
|
179
|
-
raise FetchResourceError(e_msg.format(path))
|
176
|
+
raise FetchResourceError(e_msg.format(path)) from None
|
180
177
|
return values
|
181
178
|
|
182
179
|
@staticmethod
|
@@ -185,7 +182,7 @@ class ResourceValueResolver:
|
|
185
182
|
try:
|
186
183
|
raw_values = gqlapi.get_resource(path)
|
187
184
|
except gql.GqlGetResourceError as e:
|
188
|
-
raise FetchResourceError(str(e))
|
185
|
+
raise FetchResourceError(str(e)) from e
|
189
186
|
return raw_values
|
190
187
|
|
191
188
|
@staticmethod
|
reconcile/utils/git.py
CHANGED
@@ -39,9 +39,7 @@ def has_uncommited_changes() -> bool:
|
|
39
39
|
result = subprocess.run(
|
40
40
|
cmd, stdout=subprocess.PIPE, stderr=subprocess.STDOUT, check=True
|
41
41
|
)
|
42
|
-
|
43
|
-
return True
|
44
|
-
return False
|
42
|
+
return bool(result.stdout)
|
45
43
|
|
46
44
|
|
47
45
|
def show_uncommited_changes() -> str:
|
reconcile/utils/gitlab_api.py
CHANGED
@@ -239,10 +239,7 @@ class GitLabApi: # pylint: disable=too-many-public-methods
|
|
239
239
|
def get_project_maintainers(
|
240
240
|
self, repo_url: str | None = None, query: dict | None = None
|
241
241
|
) -> list[str] | None:
|
242
|
-
if repo_url is None
|
243
|
-
project = self.project
|
244
|
-
else:
|
245
|
-
project = self.get_project(repo_url)
|
242
|
+
project = self.project if repo_url is None else self.get_project(repo_url)
|
246
243
|
if project is None:
|
247
244
|
return None
|
248
245
|
if query:
|
reconcile/utils/gql.py
CHANGED
@@ -1,3 +1,4 @@
|
|
1
|
+
import contextlib
|
1
2
|
import logging
|
2
3
|
import textwrap
|
3
4
|
import threading
|
@@ -44,10 +45,8 @@ def capture_and_forget(error):
|
|
44
45
|
:type error: Exception
|
45
46
|
"""
|
46
47
|
|
47
|
-
|
48
|
+
with contextlib.suppress(Exception):
|
48
49
|
capture_exception(error)
|
49
|
-
except Exception:
|
50
|
-
pass
|
51
50
|
|
52
51
|
|
53
52
|
class GqlApiError(Exception):
|
@@ -142,11 +141,13 @@ class GqlApi:
|
|
142
141
|
gql(query), variables, get_execution_result=True
|
143
142
|
).formatted
|
144
143
|
except requests.exceptions.ConnectionError as e:
|
145
|
-
raise GqlApiError(f"Could not connect to GraphQL server ({e})")
|
144
|
+
raise GqlApiError(f"Could not connect to GraphQL server ({e})") from None
|
146
145
|
except TransportQueryError as e:
|
147
|
-
raise GqlApiError(f"`error` returned with GraphQL response {e}")
|
146
|
+
raise GqlApiError(f"`error` returned with GraphQL response {e}") from None
|
148
147
|
except AssertionError:
|
149
|
-
raise GqlApiError(
|
148
|
+
raise GqlApiError(
|
149
|
+
"`data` field missing from GraphQL response payload"
|
150
|
+
) from None
|
150
151
|
except Exception as e:
|
151
152
|
raise GqlApiError("Unexpected error occurred") from e
|
152
153
|
|
@@ -187,7 +188,7 @@ class GqlApi:
|
|
187
188
|
if q_result:
|
188
189
|
templates = q_result["templates"]
|
189
190
|
except GqlApiError:
|
190
|
-
raise GqlGetResourceError(path, "Template not found.")
|
191
|
+
raise GqlGetResourceError(path, "Template not found.") from None
|
191
192
|
|
192
193
|
if len(templates) != 1:
|
193
194
|
raise GqlGetResourceError(path, "Expecting one and only one template.")
|
@@ -212,7 +213,7 @@ class GqlApi:
|
|
212
213
|
"resources"
|
213
214
|
]
|
214
215
|
except GqlApiError:
|
215
|
-
raise GqlGetResourceError(path, "Resource not found.")
|
216
|
+
raise GqlGetResourceError(path, "Resource not found.") from None
|
216
217
|
|
217
218
|
if len(resources) != 1:
|
218
219
|
raise GqlGetResourceError(path, "Expecting one and only one resource.")
|