qontract-reconcile 0.10.2.dev268__py3-none-any.whl → 0.10.2.dev270__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.
Files changed (60) hide show
  1. {qontract_reconcile-0.10.2.dev268.dist-info → qontract_reconcile-0.10.2.dev270.dist-info}/METADATA +1 -1
  2. {qontract_reconcile-0.10.2.dev268.dist-info → qontract_reconcile-0.10.2.dev270.dist-info}/RECORD +60 -60
  3. reconcile/aws_account_manager/integration.py +1 -1
  4. reconcile/aws_iam_keys.py +2 -1
  5. reconcile/aws_saml_idp/integration.py +2 -1
  6. reconcile/aws_saml_roles/integration.py +2 -1
  7. reconcile/aws_support_cases_sos.py +4 -1
  8. reconcile/cli.py +3 -2
  9. reconcile/dashdotdb_dvo.py +2 -1
  10. reconcile/dashdotdb_slo.py +4 -1
  11. reconcile/endpoints_discovery/integration.py +2 -1
  12. reconcile/gabi_authorized_users.py +2 -1
  13. reconcile/gitlab_owners.py +2 -1
  14. reconcile/gitlab_permissions.py +4 -1
  15. reconcile/glitchtip_project_dsn/integration.py +2 -1
  16. reconcile/integrations_manager.py +2 -1
  17. reconcile/ocm_clusters.py +2 -1
  18. reconcile/ocm_external_configuration_labels.py +2 -1
  19. reconcile/ocm_groups.py +2 -1
  20. reconcile/ocm_upgrade_scheduler_org_updater.py +6 -5
  21. reconcile/openshift_base.py +46 -28
  22. reconcile/openshift_clusterrolebindings.py +20 -6
  23. reconcile/openshift_groups.py +2 -1
  24. reconcile/openshift_limitranges.py +22 -12
  25. reconcile/openshift_namespace_labels.py +21 -5
  26. reconcile/openshift_namespaces.py +2 -1
  27. reconcile/openshift_network_policies.py +25 -6
  28. reconcile/openshift_prometheus_rules.py +2 -1
  29. reconcile/openshift_resourcequotas.py +21 -12
  30. reconcile/openshift_resources.py +2 -1
  31. reconcile/openshift_resources_base.py +3 -2
  32. reconcile/openshift_rhcs_certs.py +2 -1
  33. reconcile/openshift_rolebindings.py +34 -10
  34. reconcile/openshift_routes.py +11 -9
  35. reconcile/openshift_saas_deploy.py +3 -2
  36. reconcile/openshift_saas_deploy_trigger_cleaner.py +2 -1
  37. reconcile/openshift_saas_deploy_trigger_configs.py +2 -1
  38. reconcile/openshift_saas_deploy_trigger_images.py +2 -1
  39. reconcile/openshift_saas_deploy_trigger_moving_commits.py +2 -1
  40. reconcile/openshift_saas_deploy_trigger_upstream_jobs.py +2 -1
  41. reconcile/openshift_serviceaccount_tokens.py +2 -1
  42. reconcile/openshift_tekton_resources.py +2 -1
  43. reconcile/openshift_upgrade_watcher.py +2 -1
  44. reconcile/openshift_users.py +2 -1
  45. reconcile/openshift_vault_secrets.py +11 -9
  46. reconcile/skupper_network/integration.py +2 -1
  47. reconcile/terraform_aws_route53.py +2 -1
  48. reconcile/terraform_resources.py +3 -2
  49. reconcile/terraform_tgw_attachments.py +3 -2
  50. reconcile/terraform_users.py +2 -1
  51. reconcile/terraform_vpc_peerings.py +2 -1
  52. reconcile/utils/aws_api_typed/account.py +40 -8
  53. reconcile/utils/aws_api_typed/iam.py +19 -9
  54. reconcile/utils/constants.py +1 -0
  55. reconcile/utils/jinja2/utils.py +5 -4
  56. reconcile/utils/slo_document_manager.py +1 -1
  57. tools/app_interface_reporter.py +4 -1
  58. tools/cli_commands/container_images_report.py +3 -2
  59. {qontract_reconcile-0.10.2.dev268.dist-info → qontract_reconcile-0.10.2.dev270.dist-info}/WHEEL +0 -0
  60. {qontract_reconcile-0.10.2.dev268.dist-info → qontract_reconcile-0.10.2.dev270.dist-info}/entry_points.txt +0 -0
@@ -22,6 +22,7 @@ from reconcile.typed_queries.app_interface_vault_settings import (
22
22
  )
23
23
  from reconcile.typed_queries.rhcs_provider_settings import get_rhcs_provider_settings
24
24
  from reconcile.utils import gql, metrics
25
+ from reconcile.utils.constants import DEFAULT_THREAD_POOL_SIZE
25
26
  from reconcile.utils.defer import defer
26
27
  from reconcile.utils.disabled_integrations import integration_is_enabled
27
28
  from reconcile.utils.metrics import GaugeMetric, normalize_integration_name
@@ -243,7 +244,7 @@ def fetch_desired_state(
243
244
  @defer
244
245
  def run(
245
246
  dry_run: bool,
246
- thread_pool_size: int = 10,
247
+ thread_pool_size: int = DEFAULT_THREAD_POOL_SIZE,
247
248
  internal: bool | None = None,
248
249
  use_jump_host: bool = True,
249
250
  cluster_name: Iterable[str] | None = None,
@@ -1,5 +1,7 @@
1
1
  import contextlib
2
2
  import sys
3
+ from collections.abc import Callable, Mapping, Sequence
4
+ from typing import Any
3
5
 
4
6
  import reconcile.openshift_base as ob
5
7
  from reconcile import queries
@@ -7,9 +9,13 @@ from reconcile.utils import (
7
9
  expiration,
8
10
  gql,
9
11
  )
12
+ from reconcile.utils.constants import DEFAULT_THREAD_POOL_SIZE
10
13
  from reconcile.utils.defer import defer
11
14
  from reconcile.utils.openshift_resource import OpenshiftResource as OR
12
- from reconcile.utils.openshift_resource import ResourceKeyExistsError
15
+ from reconcile.utils.openshift_resource import (
16
+ ResourceInventory,
17
+ ResourceKeyExistsError,
18
+ )
13
19
  from reconcile.utils.semver_helper import make_semver
14
20
  from reconcile.utils.sharding import is_in_shard
15
21
 
@@ -49,7 +55,7 @@ QONTRACT_INTEGRATION = "openshift-rolebindings"
49
55
  QONTRACT_INTEGRATION_VERSION = make_semver(0, 3, 0)
50
56
 
51
57
 
52
- def construct_user_oc_resource(role, user):
58
+ def construct_user_oc_resource(role: str, user: str) -> tuple[OR, str]:
53
59
  name = f"{role}-{user}"
54
60
  body = {
55
61
  "apiVersion": "rbac.authorization.k8s.io/v1",
@@ -66,7 +72,7 @@ def construct_user_oc_resource(role, user):
66
72
  )
67
73
 
68
74
 
69
- def construct_sa_oc_resource(role, namespace, sa_name):
75
+ def construct_sa_oc_resource(role: str, namespace: str, sa_name: str) -> tuple[OR, str]:
70
76
  name = f"{role}-{namespace}-{sa_name}"
71
77
  body = {
72
78
  "apiVersion": "rbac.authorization.k8s.io/v1",
@@ -85,9 +91,16 @@ def construct_sa_oc_resource(role, namespace, sa_name):
85
91
  )
86
92
 
87
93
 
88
- def fetch_desired_state(ri, oc_map, enforced_user_keys=None):
94
+ def fetch_desired_state(
95
+ ri: ResourceInventory | None,
96
+ oc_map: ob.ClusterMap | None,
97
+ enforced_user_keys: list[str] | None = None,
98
+ ) -> list[dict[str, str]]:
89
99
  gqlapi = gql.get_api()
90
- roles: list[dict] = expiration.filter(gqlapi.query(ROLES_QUERY)["roles"])
100
+ roles_query_result = gqlapi.query(ROLES_QUERY)
101
+ if not roles_query_result:
102
+ return []
103
+ roles: Sequence[Mapping[str, Any]] = expiration.filter(roles_query_result["roles"])
91
104
  users_desired_state = []
92
105
  for role in roles:
93
106
  permissions = [
@@ -124,11 +137,15 @@ def fetch_desired_state(ri, oc_map, enforced_user_keys=None):
124
137
 
125
138
  # get username keys based on used IDPs
126
139
  user_keys = ob.determine_user_keys_for_access(
127
- cluster, cluster_info["auth"], enforced_user_keys=enforced_user_keys
140
+ cluster,
141
+ cluster_info.get("auth") or [],
142
+ enforced_user_keys=enforced_user_keys,
128
143
  )
129
144
  # create user rolebindings for user * user_keys
130
- for user in role["users"]:
131
- for username in {user[user_key] for user_key in user_keys}:
145
+ for user in role.get("users") or []:
146
+ for username in {user.get(key) for key in user_keys}:
147
+ if not username:
148
+ continue
132
149
  # used by openshift-users and github integrations
133
150
  # this is just to simplify things a bit on the their side
134
151
  users_desired_state.append({"cluster": cluster, "user": username})
@@ -171,7 +188,13 @@ def fetch_desired_state(ri, oc_map, enforced_user_keys=None):
171
188
 
172
189
 
173
190
  @defer
174
- def run(dry_run, thread_pool_size=10, internal=None, use_jump_host=True, defer=None):
191
+ def run(
192
+ dry_run: bool,
193
+ thread_pool_size: int = DEFAULT_THREAD_POOL_SIZE,
194
+ internal: bool | None = None,
195
+ use_jump_host: bool = True,
196
+ defer: Callable | None = None,
197
+ ) -> None:
175
198
  namespaces = [
176
199
  namespace_info
177
200
  for namespace_info in queries.get_namespaces()
@@ -190,7 +213,8 @@ def run(dry_run, thread_pool_size=10, internal=None, use_jump_host=True, defer=N
190
213
  internal=internal,
191
214
  use_jump_host=use_jump_host,
192
215
  )
193
- defer(oc_map.cleanup)
216
+ if defer:
217
+ defer(oc_map.cleanup)
194
218
  fetch_desired_state(ri, oc_map)
195
219
  ob.publish_metrics(ri, QONTRACT_INTEGRATION)
196
220
  ob.realize_data(dry_run, oc_map, ri, thread_pool_size)
@@ -1,6 +1,8 @@
1
+ from collections.abc import Callable
1
2
  from typing import Any
2
3
 
3
4
  import reconcile.openshift_resources_base as orb
5
+ from reconcile.utils.constants import DEFAULT_THREAD_POOL_SIZE
4
6
  from reconcile.utils.runtime.integration import DesiredStateShardConfig
5
7
  from reconcile.utils.semver_helper import make_semver
6
8
 
@@ -10,14 +12,14 @@ PROVIDERS = ["route"]
10
12
 
11
13
 
12
14
  def run(
13
- dry_run,
14
- thread_pool_size=10,
15
- internal=None,
16
- use_jump_host=True,
17
- cluster_name=None,
18
- namespace_name=None,
19
- defer=None,
20
- ):
15
+ dry_run: bool,
16
+ thread_pool_size: int = DEFAULT_THREAD_POOL_SIZE,
17
+ internal: bool | None = None,
18
+ use_jump_host: bool = True,
19
+ cluster_name: str | None = None,
20
+ namespace_name: str | None = None,
21
+ defer: Callable | None = None,
22
+ ) -> None:
21
23
  orb.QONTRACT_INTEGRATION = QONTRACT_INTEGRATION
22
24
  orb.QONTRACT_INTEGRATION_VERSION = QONTRACT_INTEGRATION_VERSION
23
25
 
@@ -32,7 +34,7 @@ def run(
32
34
  )
33
35
 
34
36
 
35
- def early_exit_desired_state(*args, **kwargs) -> dict[str, Any]:
37
+ def early_exit_desired_state(*args: Any, **kwargs: Any) -> dict[str, Any]:
36
38
  return orb.early_exit_desired_state(PROVIDERS)
37
39
 
38
40
 
@@ -25,6 +25,7 @@ from reconcile.typed_queries.saas_files import (
25
25
  SaasFileList,
26
26
  get_saasherder_settings,
27
27
  )
28
+ from reconcile.utils.constants import DEFAULT_THREAD_POOL_SIZE
28
29
  from reconcile.utils.defer import defer
29
30
  from reconcile.utils.gitlab_api import GitLabApi
30
31
  from reconcile.utils.openshift_resource import ResourceInventory
@@ -109,7 +110,7 @@ def slack_notify(
109
110
  @defer
110
111
  def run(
111
112
  dry_run: bool,
112
- thread_pool_size: int = 10,
113
+ thread_pool_size: int = DEFAULT_THREAD_POOL_SIZE,
113
114
  io_dir: str = "throughput/",
114
115
  use_jump_host: bool = True,
115
116
  saas_file_name: str | None = None,
@@ -259,7 +260,7 @@ def run(
259
260
  all_callers=[sf.name for sf in all_saas_files if not sf.deprecated],
260
261
  wait_for_namespace=True,
261
262
  no_dry_run_skip_compare=(not saasherder.compare),
262
- take_over=saasherder.take_over,
263
+ take_over=bool(saasherder.take_over),
263
264
  )
264
265
 
265
266
  if not dry_run:
@@ -18,6 +18,7 @@ from reconcile.typed_queries.app_interface_vault_settings import (
18
18
  from reconcile.typed_queries.tekton_pipeline_providers import (
19
19
  get_tekton_pipeline_providers,
20
20
  )
21
+ from reconcile.utils.constants import DEFAULT_THREAD_POOL_SIZE
21
22
  from reconcile.utils.defer import defer
22
23
  from reconcile.utils.oc_map import (
23
24
  OCLogMsg,
@@ -63,7 +64,7 @@ def get_pipeline_runs_to_delete(
63
64
  @defer
64
65
  def run(
65
66
  dry_run: bool,
66
- thread_pool_size: int = 10,
67
+ thread_pool_size: int = DEFAULT_THREAD_POOL_SIZE,
67
68
  internal: bool | None = None,
68
69
  use_jump_host: bool = True,
69
70
  defer: Callable | None = None,
@@ -2,6 +2,7 @@ import sys
2
2
 
3
3
  import reconcile.openshift_saas_deploy_trigger_base as osdt_base
4
4
  from reconcile.status import ExitCodes
5
+ from reconcile.utils.constants import DEFAULT_THREAD_POOL_SIZE
5
6
  from reconcile.utils.saasherder import TriggerTypes
6
7
  from reconcile.utils.semver_helper import make_semver
7
8
 
@@ -11,7 +12,7 @@ QONTRACT_INTEGRATION_VERSION = make_semver(0, 3, 0)
11
12
 
12
13
  def run(
13
14
  dry_run: bool,
14
- thread_pool_size: int = 10,
15
+ thread_pool_size: int = DEFAULT_THREAD_POOL_SIZE,
15
16
  internal: bool | None = None,
16
17
  use_jump_host: bool = True,
17
18
  include_trigger_trace: bool = False,
@@ -2,6 +2,7 @@ import sys
2
2
 
3
3
  import reconcile.openshift_saas_deploy_trigger_base as osdt_base
4
4
  from reconcile.status import ExitCodes
5
+ from reconcile.utils.constants import DEFAULT_THREAD_POOL_SIZE
5
6
  from reconcile.utils.saasherder import TriggerTypes
6
7
  from reconcile.utils.semver_helper import make_semver
7
8
 
@@ -11,7 +12,7 @@ QONTRACT_INTEGRATION_VERSION = make_semver(0, 1, 0)
11
12
 
12
13
  def run(
13
14
  dry_run: bool,
14
- thread_pool_size: int = 10,
15
+ thread_pool_size: int = DEFAULT_THREAD_POOL_SIZE,
15
16
  internal: bool | None = None,
16
17
  use_jump_host: bool = True,
17
18
  include_trigger_trace: bool = False,
@@ -2,6 +2,7 @@ import sys
2
2
 
3
3
  import reconcile.openshift_saas_deploy_trigger_base as osdt_base
4
4
  from reconcile.status import ExitCodes
5
+ from reconcile.utils.constants import DEFAULT_THREAD_POOL_SIZE
5
6
  from reconcile.utils.saasherder import TriggerTypes
6
7
  from reconcile.utils.semver_helper import make_semver
7
8
 
@@ -11,7 +12,7 @@ QONTRACT_INTEGRATION_VERSION = make_semver(0, 3, 0)
11
12
 
12
13
  def run(
13
14
  dry_run: bool,
14
- thread_pool_size: int = 10,
15
+ thread_pool_size: int = DEFAULT_THREAD_POOL_SIZE,
15
16
  internal: bool | None = None,
16
17
  use_jump_host: bool = True,
17
18
  include_trigger_trace: bool = False,
@@ -2,6 +2,7 @@ import sys
2
2
 
3
3
  import reconcile.openshift_saas_deploy_trigger_base as osdt_base
4
4
  from reconcile.status import ExitCodes
5
+ from reconcile.utils.constants import DEFAULT_THREAD_POOL_SIZE
5
6
  from reconcile.utils.saasherder import TriggerTypes
6
7
  from reconcile.utils.semver_helper import make_semver
7
8
 
@@ -11,7 +12,7 @@ QONTRACT_INTEGRATION_VERSION = make_semver(0, 3, 0)
11
12
 
12
13
  def run(
13
14
  dry_run: bool,
14
- thread_pool_size: int = 10,
15
+ thread_pool_size: int = DEFAULT_THREAD_POOL_SIZE,
15
16
  internal: bool | None = None,
16
17
  use_jump_host: bool = True,
17
18
  include_trigger_trace: bool = False,
@@ -9,6 +9,7 @@ from reconcile.gql_definitions.openshift_serviceaccount_tokens.tokens import (
9
9
  query as serviceaccount_tokens_query,
10
10
  )
11
11
  from reconcile.utils import gql
12
+ from reconcile.utils.constants import DEFAULT_THREAD_POOL_SIZE
12
13
  from reconcile.utils.defer import defer
13
14
  from reconcile.utils.disabled_integrations import integration_is_enabled
14
15
  from reconcile.utils.oc import OC_Map
@@ -204,7 +205,7 @@ def get_namespaces_with_serviceaccount_tokens(
204
205
  @defer
205
206
  def run(
206
207
  dry_run: bool,
207
- thread_pool_size: int = 10,
208
+ thread_pool_size: int = DEFAULT_THREAD_POOL_SIZE,
208
209
  internal: bool | None = None,
209
210
  use_jump_host: bool = True,
210
211
  vault_output_path: str = "",
@@ -11,6 +11,7 @@ from reconcile import openshift_base as ob
11
11
  from reconcile import queries
12
12
  from reconcile.status import ExitCodes
13
13
  from reconcile.utils import gql
14
+ from reconcile.utils.constants import DEFAULT_THREAD_POOL_SIZE
14
15
  from reconcile.utils.defer import defer
15
16
  from reconcile.utils.openshift_resource import OpenshiftResource as OR
16
17
  from reconcile.utils.parse_dhms_duration import (
@@ -421,7 +422,7 @@ def build_one_per_saas_file_tkn_task_name(
421
422
 
422
423
  def run(
423
424
  dry_run: bool,
424
- thread_pool_size: int = 10,
425
+ thread_pool_size: int = DEFAULT_THREAD_POOL_SIZE,
425
426
  internal: bool | None = None,
426
427
  use_jump_host: bool = True,
427
428
  saas_file_name: str | None = None,
@@ -12,6 +12,7 @@ from reconcile.typed_queries.app_interface_vault_settings import (
12
12
  get_app_interface_vault_settings,
13
13
  )
14
14
  from reconcile.typed_queries.clusters import get_clusters
15
+ from reconcile.utils.constants import DEFAULT_THREAD_POOL_SIZE
15
16
  from reconcile.utils.defer import defer
16
17
  from reconcile.utils.oc_map import (
17
18
  OCLogMsg,
@@ -155,7 +156,7 @@ def notify_cluster_new_version(
155
156
  @defer
156
157
  def run(
157
158
  dry_run: bool,
158
- thread_pool_size: int = 10,
159
+ thread_pool_size: int = DEFAULT_THREAD_POOL_SIZE,
159
160
  internal: bool | None = None,
160
161
  use_jump_host: bool = True,
161
162
  defer: Callable | None = None,
@@ -22,6 +22,7 @@ from reconcile.typed_queries.app_interface_vault_settings import (
22
22
  get_app_interface_vault_settings,
23
23
  )
24
24
  from reconcile.typed_queries.clusters_minimal import get_clusters_minimal
25
+ from reconcile.utils.constants import DEFAULT_THREAD_POOL_SIZE
25
26
  from reconcile.utils.defer import defer
26
27
  from reconcile.utils.oc_map import (
27
28
  OCLogMsg,
@@ -167,7 +168,7 @@ def act(diff: Mapping[str, Any], oc_map: OCMap) -> None:
167
168
  @defer
168
169
  def run(
169
170
  dry_run: bool,
170
- thread_pool_size: int = 10,
171
+ thread_pool_size: int = DEFAULT_THREAD_POOL_SIZE,
171
172
  internal: bool | None = None,
172
173
  use_jump_host: bool = True,
173
174
  defer: Callable | None = None,
@@ -1,9 +1,11 @@
1
1
  from collections import defaultdict
2
+ from collections.abc import Callable
2
3
  from typing import Any
3
4
 
4
5
  from deepdiff import DeepHash
5
6
 
6
7
  import reconcile.openshift_resources_base as orb
8
+ from reconcile.utils.constants import DEFAULT_THREAD_POOL_SIZE
7
9
  from reconcile.utils.runtime.integration import DesiredStateShardConfig
8
10
  from reconcile.utils.semver_helper import make_semver
9
11
 
@@ -13,14 +15,14 @@ PROVIDERS = ["vault-secret"]
13
15
 
14
16
 
15
17
  def run(
16
- dry_run,
17
- thread_pool_size=10,
18
- internal=None,
19
- use_jump_host=True,
20
- cluster_name=None,
21
- namespace_name=None,
22
- defer=None,
23
- ):
18
+ dry_run: bool,
19
+ thread_pool_size: int = DEFAULT_THREAD_POOL_SIZE,
20
+ internal: bool | None = None,
21
+ use_jump_host: bool = True,
22
+ cluster_name: str | None = None,
23
+ namespace_name: str | None = None,
24
+ defer: Callable | None = None,
25
+ ) -> None:
24
26
  orb.QONTRACT_INTEGRATION = QONTRACT_INTEGRATION
25
27
  orb.QONTRACT_INTEGRATION_VERSION = QONTRACT_INTEGRATION_VERSION
26
28
 
@@ -35,7 +37,7 @@ def run(
35
37
  )
36
38
 
37
39
 
38
- def early_exit_desired_state(*args, **kwargs) -> dict[str, Any]:
40
+ def early_exit_desired_state(*args: Any, **kwargs: Any) -> dict[str, Any]:
39
41
  namespaces, _ = orb.get_namespaces(PROVIDERS)
40
42
 
41
43
  state_for_clusters = defaultdict(list)
@@ -21,6 +21,7 @@ from reconcile.typed_queries.app_interface_vault_settings import (
21
21
  get_app_interface_vault_settings,
22
22
  )
23
23
  from reconcile.utils import gql
24
+ from reconcile.utils.constants import DEFAULT_THREAD_POOL_SIZE
24
25
  from reconcile.utils.defer import defer
25
26
  from reconcile.utils.disabled_integrations import integration_is_enabled
26
27
  from reconcile.utils.oc_map import (
@@ -246,7 +247,7 @@ def get_skupper_networks(query_func: Callable) -> list[SkupperNetworkV1]:
246
247
  @defer
247
248
  def run(
248
249
  dry_run: bool,
249
- thread_pool_size: int = 10,
250
+ thread_pool_size: int = DEFAULT_THREAD_POOL_SIZE,
250
251
  internal: bool | None = None,
251
252
  use_jump_host: bool = True,
252
253
  defer: Callable | None = None,
@@ -10,6 +10,7 @@ from reconcile import queries
10
10
  from reconcile.status import ExitCodes
11
11
  from reconcile.utils import dnsutils
12
12
  from reconcile.utils.aws_api import AWSApi
13
+ from reconcile.utils.constants import DEFAULT_THREAD_POOL_SIZE
13
14
  from reconcile.utils.defer import defer
14
15
  from reconcile.utils.external_resources import (
15
16
  PROVIDER_AWS,
@@ -205,7 +206,7 @@ def run(
205
206
  dry_run: bool = False,
206
207
  print_to_file: str | None = None,
207
208
  enable_deletion: bool = True,
208
- thread_pool_size: int = 10,
209
+ thread_pool_size: int = DEFAULT_THREAD_POOL_SIZE,
209
210
  account_name: str | None = None,
210
211
  defer=None,
211
212
  ):
@@ -31,6 +31,7 @@ from reconcile.typed_queries.app_interface_vault_settings import (
31
31
  from reconcile.typed_queries.terraform_namespaces import get_namespaces
32
32
  from reconcile.utils import gql
33
33
  from reconcile.utils.aws_api import AWSApi
34
+ from reconcile.utils.constants import DEFAULT_THREAD_POOL_SIZE
34
35
  from reconcile.utils.defer import defer
35
36
  from reconcile.utils.extended_early_exit import (
36
37
  ExtendedEarlyExitRunnerResult,
@@ -367,7 +368,7 @@ def run(
367
368
  dry_run: bool,
368
369
  print_to_file: str | None = None,
369
370
  enable_deletion: bool = False,
370
- thread_pool_size: int = 10,
371
+ thread_pool_size: int = DEFAULT_THREAD_POOL_SIZE,
371
372
  internal: bool | None = None,
372
373
  use_jump_host: bool = True,
373
374
  light: bool = False,
@@ -473,7 +474,7 @@ def runner(
473
474
  secret_reader: SecretReaderBase,
474
475
  dry_run: bool,
475
476
  enable_deletion: bool = False,
476
- thread_pool_size: int = 10,
477
+ thread_pool_size: int = DEFAULT_THREAD_POOL_SIZE,
477
478
  internal: bool | None = None,
478
479
  use_jump_host: bool = True,
479
480
  light: bool = False,
@@ -37,6 +37,7 @@ from reconcile.typed_queries.terraform_tgw_attachments.aws_accounts import (
37
37
  )
38
38
  from reconcile.utils import gql
39
39
  from reconcile.utils.aws_api import AWSApi
40
+ from reconcile.utils.constants import DEFAULT_THREAD_POOL_SIZE
40
41
  from reconcile.utils.defer import defer
41
42
  from reconcile.utils.disabled_integrations import integration_is_enabled
42
43
  from reconcile.utils.extended_early_exit import (
@@ -423,7 +424,7 @@ def setup(
423
424
  account_name: str | None,
424
425
  desired_state_data_source: DesiredStateDataSource,
425
426
  tgw_accounts: list[dict[str, Any]],
426
- thread_pool_size: int = 10,
427
+ thread_pool_size: int = DEFAULT_THREAD_POOL_SIZE,
427
428
  print_to_file: str | None = None,
428
429
  ) -> tuple[SecretReaderBase, AWSApi, Terraform, Terrascript]:
429
430
  tgw_clusters = desired_state_data_source.clusters
@@ -500,7 +501,7 @@ def run(
500
501
  dry_run: bool,
501
502
  print_to_file: str | None = None,
502
503
  enable_deletion: bool = False,
503
- thread_pool_size: int = 10,
504
+ thread_pool_size: int = DEFAULT_THREAD_POOL_SIZE,
504
505
  account_name: str | None = None,
505
506
  defer: Callable | None = None,
506
507
  enable_extended_early_exit: bool = False,
@@ -16,6 +16,7 @@ from reconcile.utils import (
16
16
  gql,
17
17
  )
18
18
  from reconcile.utils.aws_api import AWSApi
19
+ from reconcile.utils.constants import DEFAULT_THREAD_POOL_SIZE
19
20
  from reconcile.utils.disabled_integrations import integration_is_enabled
20
21
  from reconcile.utils.runtime.integration import DesiredStateShardConfig
21
22
  from reconcile.utils.secret_reader import SecretReader
@@ -237,7 +238,7 @@ def run(
237
238
  dry_run: bool,
238
239
  print_to_file: str | None = None,
239
240
  enable_deletion: bool = False,
240
- thread_pool_size: int = 10,
241
+ thread_pool_size: int = DEFAULT_THREAD_POOL_SIZE,
241
242
  send_mails: bool = True,
242
243
  account_name: str | None = None,
243
244
  ):
@@ -12,6 +12,7 @@ from reconcile.utils import (
12
12
  ocm,
13
13
  )
14
14
  from reconcile.utils.aws_api import AWSApi
15
+ from reconcile.utils.constants import DEFAULT_THREAD_POOL_SIZE
15
16
  from reconcile.utils.defer import defer
16
17
  from reconcile.utils.extended_early_exit import (
17
18
  ExtendedEarlyExitRunnerResult,
@@ -566,7 +567,7 @@ def run(
566
567
  dry_run: bool,
567
568
  print_to_file: bool | None = None,
568
569
  enable_deletion: bool = False,
569
- thread_pool_size: int = 10,
570
+ thread_pool_size: int = DEFAULT_THREAD_POOL_SIZE,
570
571
  account_name: str | None = None,
571
572
  enable_extended_early_exit: bool = False,
572
573
  extended_early_exit_cache_ttl_seconds: int = 3600,
@@ -1,11 +1,14 @@
1
+ import logging
1
2
  from enum import StrEnum
2
3
  from typing import TYPE_CHECKING
3
4
 
4
5
  if TYPE_CHECKING:
5
6
  from mypy_boto3_account import AccountClient
7
+ from mypy_boto3_account.type_defs import AlternateContactTypeDef
6
8
  else:
7
- AccountClient = object
9
+ AccountClient = AlternateContactTypeDef = object
8
10
 
11
+ import botocore
9
12
  from pydantic import BaseModel
10
13
 
11
14
 
@@ -22,6 +25,9 @@ class Region(BaseModel):
22
25
  status: OptStatus
23
26
 
24
27
 
28
+ log = logging.getLogger(__name__)
29
+
30
+
25
31
  class AWSApiAccount:
26
32
  def __init__(self, client: AccountClient) -> None:
27
33
  self.client = client
@@ -30,13 +36,39 @@ class AWSApiAccount:
30
36
  self, name: str, title: str, email: str, phone_number: str
31
37
  ) -> None:
32
38
  """Set the security contact for the account."""
33
- self.client.put_alternate_contact(
34
- AlternateContactType="SECURITY",
35
- EmailAddress=email,
36
- Name=name,
37
- Title=title,
38
- PhoneNumber=phone_number,
39
- )
39
+ try:
40
+ self.client.put_alternate_contact(
41
+ AlternateContactType="SECURITY",
42
+ EmailAddress=email,
43
+ Name=name,
44
+ Title=title,
45
+ PhoneNumber=phone_number,
46
+ )
47
+ except botocore.exceptions.ClientError as e:
48
+ if e.response["Error"]["Code"] != "AccessDenied":
49
+ raise
50
+
51
+ # This exception is raised if the user does not have permission to perform this action.
52
+ # Let's see if the current security contact is already set to the same values.
53
+ current_contact = self.get_security_contact()
54
+ if (
55
+ not current_contact
56
+ or current_contact["EmailAddress"] != email
57
+ or current_contact["Name"] != name
58
+ or current_contact["Title"] != title
59
+ or current_contact["PhoneNumber"] != phone_number
60
+ ):
61
+ raise
62
+
63
+ def get_security_contact(self) -> AlternateContactTypeDef | None:
64
+ """Get the security contact for the account."""
65
+ try:
66
+ return self.client.get_alternate_contact(AlternateContactType="SECURITY")[
67
+ "AlternateContact"
68
+ ]
69
+ except self.client.exceptions.ResourceNotFoundException:
70
+ log.warning("Security contact not set.")
71
+ return None
40
72
 
41
73
  def list_regions(self) -> list[Region]:
42
74
  """List all regions in the account."""
@@ -1,5 +1,6 @@
1
1
  from typing import TYPE_CHECKING
2
2
 
3
+ import botocore
3
4
  from pydantic import BaseModel, Field
4
5
 
5
6
  if TYPE_CHECKING:
@@ -40,10 +41,12 @@ class AWSApiIam:
40
41
  try:
41
42
  user = self.client.create_user(UserName=user_name)
42
43
  return AWSUser(**user["User"])
43
- except self.client.exceptions.EntityAlreadyExistsException:
44
- raise AWSEntityAlreadyExistsError(
45
- f"User {user_name} already exists"
46
- ) from None
44
+ except botocore.exceptions.ClientError as e:
45
+ if e.response["Error"]["Code"] == "EntityAlreadyExists":
46
+ raise AWSEntityAlreadyExistsError(
47
+ f"User {user_name} already exists"
48
+ ) from e
49
+ raise
47
50
 
48
51
  def attach_user_policy(self, user_name: str, policy_arn: str) -> None:
49
52
  """Attach a policy to a user."""
@@ -60,8 +63,15 @@ class AWSApiIam:
60
63
  """Set the account alias."""
61
64
  try:
62
65
  self.client.create_account_alias(AccountAlias=account_alias)
63
- except self.client.exceptions.EntityAlreadyExistsException:
64
- if self.get_account_alias() != account_alias:
65
- raise ValueError(
66
- "Account alias already exists for another AWS account. Choose another one!"
67
- ) from None
66
+ except botocore.exceptions.ClientError as e:
67
+ if e.response["Error"]["Code"] == "EntityAlreadyExists":
68
+ if self.get_account_alias() != account_alias:
69
+ raise ValueError(
70
+ "Account alias already exists for another AWS account. Choose another one!"
71
+ ) from e
72
+ elif e.response["Error"]["Code"] == "AccessDenied":
73
+ # AccessDeniedException can occur if the user does not have permission to create an account alias.
74
+ # This can happen if the alias is already set and we don't have permission to change it.
75
+ # If the existing alias is the one we want, we can ignore the error.
76
+ if self.get_account_alias() != account_alias:
77
+ raise
@@ -3,3 +3,4 @@
3
3
  from pathlib import Path
4
4
 
5
5
  PROJ_ROOT = (Path(__file__) / ".." / "..").resolve()
6
+ DEFAULT_THREAD_POOL_SIZE = 10