qontract-reconcile 0.10.1rc769__py3-none-any.whl → 0.10.1rc771__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: qontract-reconcile
3
- Version: 0.10.1rc769
3
+ Version: 0.10.1rc771
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
@@ -10,7 +10,7 @@ reconcile/aws_iam_password_reset.py,sha256=NwErtrqgBiXr7eGCAHdtGGOx0S7-4JnSc29Ie
10
10
  reconcile/aws_support_cases_sos.py,sha256=Jk6_XjDeJSYxgRGqcEAOcynt9qJF2r5HPIPcSKmoBv8,2974
11
11
  reconcile/blackbox_exporter_endpoint_monitoring.py,sha256=W_VJagnsJR1v5oqjlI3RJJE0_nhtJ0m81RS8zWA5u5c,3538
12
12
  reconcile/checkpoint.py,sha256=R2WFXUXLTB4sWMi4GeA4eegsuf_1-Q4vH8M0Toh3Ij4,5036
13
- reconcile/cli.py,sha256=knLmE8YaI61Vg6tbcl5YW8HXIthq91yQknVtnXygCgY,99475
13
+ reconcile/cli.py,sha256=0m82693uepqTj5rWIXToHTTxdjXs-cScKTj0Wkjxjrg,99574
14
14
  reconcile/closedbox_endpoint_monitoring_base.py,sha256=SMhkcQqprWvThrIJa3U_3uh5w1h-alleW1QnCJFY4Qw,4909
15
15
  reconcile/cluster_deployment_mapper.py,sha256=2Ah-nu-Mdig0pjuiZl_XLrmVAjYzFjORR3dMlCgkmw0,2352
16
16
  reconcile/dashdotdb_base.py,sha256=a5aPLVxyqPSbjdB0Ty-uliOtxwvEbbEljHJKxdK3-Zk,4813
@@ -179,14 +179,14 @@ reconcile/cna/assets/null.py,sha256=Fby1Fbn7oNRIGNasdyhRDvXJ0ktpxv-pUAPN0lZWSzk,
179
179
  reconcile/external_resources/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
180
180
  reconcile/external_resources/aws.py,sha256=JvjKaABy2Pg8u8Lq82Acv4zMvpE3_qGKes7OG-zlHOM,2956
181
181
  reconcile/external_resources/factories.py,sha256=bLboXX5Dq0xN60mtDGNjCOLC6HlKofXMWQxVbRwMMwo,4485
182
- reconcile/external_resources/integration.py,sha256=QQZdKTeHoUD7afUTNKh878u-KWovYptAFZwjUBTIbCg,3317
182
+ reconcile/external_resources/integration.py,sha256=rIfDVnTUy3qdmrh7NRwvZ7l1hOiNfk0a57J1ZsRSNrg,4959
183
183
  reconcile/external_resources/manager.py,sha256=APszaw9PRIiHnFyCffHZjIFseIwhlYROIPLt18pHUTQ,13685
184
184
  reconcile/external_resources/meta.py,sha256=SA4Km1r7ePdcNqHn2GA4ByQp4ZnIeo_n8qOOd-11IEg,151
185
185
  reconcile/external_resources/metrics.py,sha256=m2TIOao2N7pD6k45driFbBGVCC_N7ai44m-lLPfa5qk,454
186
186
  reconcile/external_resources/model.py,sha256=FJUb7rHU2l7YSAv-t4QaacL9pqheFBxhPydWSPqu3vY,7413
187
187
  reconcile/external_resources/reconciler.py,sha256=E50X_lnOD0OWYXMzyZld1P6dCFJFYjHGyICWff9bxlc,9323
188
188
  reconcile/external_resources/secrets_sync.py,sha256=g-ksvzmTlCTwo3PM3FgYXm0LUBcnwfAxcvisuR1jAMY,7982
189
- reconcile/external_resources/state.py,sha256=rRePQdA3A6U9oFDDs9aaL5Ja7nSIXpOC1wHwY8R47_8,9197
189
+ reconcile/external_resources/state.py,sha256=V1lpgr0SmESA_x_MAk_tz3GCH9bfzvNmA21u6jHlGzU,9332
190
190
  reconcile/glitchtip/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
191
191
  reconcile/glitchtip/integration.py,sha256=Y7ofQg_xCt3dOln3pjeXp7rAnwohCgD2zcUAb-Hciis,8375
192
192
  reconcile/glitchtip/reconciler.py,sha256=nUvDv7qG1ly0cA16MmlL6NV71yl1mJYLT2mui7lmi0Y,12402
@@ -262,9 +262,10 @@ reconcile/gql_definitions/dashdotdb_slo/slo_documents_query.py,sha256=zUa-CmpOwi
262
262
  reconcile/gql_definitions/dynatrace_token_provider/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
263
263
  reconcile/gql_definitions/dynatrace_token_provider/dynatrace_bootstrap_tokens.py,sha256=38b9JR7gFTdID3EJO9zeqrCT1adbLrurOLYIGAKPxtA,2045
264
264
  reconcile/gql_definitions/external_resources/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
265
+ reconcile/gql_definitions/external_resources/aws_accounts.py,sha256=XR69j9dpTQ0gv8y-AZN7AJ0dPvO-wbHscyCDgrax6Bk,2046
265
266
  reconcile/gql_definitions/external_resources/external_resources_modules.py,sha256=2VIb3hC2MRNhYDZ1al4PvudT2oSfd3fPGpzv4CEJNiw,2341
266
267
  reconcile/gql_definitions/external_resources/external_resources_namespaces.py,sha256=UyOAUY1rROenjTz6y-uSEFjrEwhh-lPsIQPbi6EQLFg,40915
267
- reconcile/gql_definitions/external_resources/external_resources_settings.py,sha256=Gnuy7dqikXHtDjh7P2nkPux9uCDf-a2t4WINMUyiXkc,2240
268
+ reconcile/gql_definitions/external_resources/external_resources_settings.py,sha256=989_pG9NWKB5BPvdwqjqZUYp_2qf-xYmJ9c9kq8Kmfw,2886
268
269
  reconcile/gql_definitions/fragments/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
269
270
  reconcile/gql_definitions/fragments/aus_organization.py,sha256=ARI87YAbC0VjFri9eVGYrRPBc4s0kWsa25RR8FFoq7E,4433
270
271
  reconcile/gql_definitions/fragments/aws_account_common.py,sha256=d_FwpS_dY8o8DCLa3NERs93FVxQLiDUIPm5tGNac-iw,2320
@@ -676,7 +677,8 @@ reconcile/utils/acs/notifiers.py,sha256=2n5blP9N1FdGLZuy3do9bpjd8NKg88kmLNNqhAGn
676
677
  reconcile/utils/acs/policies.py,sha256=_jAz6cv8KRYtDsXjGoJgNbD8_9PUa5LSwwVlpK4A_cQ,5505
677
678
  reconcile/utils/acs/rbac.py,sha256=ugsLM9Pb7FbUbdq85E3VzXGMaB9ZovXob7tdWCxwqZ8,8808
678
679
  reconcile/utils/aws_api_typed/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
679
- reconcile/utils/aws_api_typed/api.py,sha256=uqLNd_uNB11sQqLfcrqvXb1Z_K8GtX3K0FZ8zC_FK-A,8329
680
+ reconcile/utils/aws_api_typed/api.py,sha256=NJJXmV01nBtQLf-Vv5xfieboCsY3inNd4ekRKxS_-Wo,8790
681
+ reconcile/utils/aws_api_typed/dynamodb.py,sha256=AKUbz8HGzmSq4cnpjJe7PgqsikMkjbpbzUD2UJv2b58,383
680
682
  reconcile/utils/aws_api_typed/iam.py,sha256=ka46H2-SzTCgy6EJYapKTzyZK9vR1bkfD0wF8bDdy1Q,2201
681
683
  reconcile/utils/aws_api_typed/organization.py,sha256=oXftcLVuSs9qej6efdssl38FvjeZaQC5R2Wj3NzxX4U,5529
682
684
  reconcile/utils/aws_api_typed/s3.py,sha256=J2uOTtEFgMyKT22pa4DbFnV7zfg575m2DeidQaeselM,1034
@@ -783,8 +785,8 @@ tools/test/test_app_interface_metrics_exporter.py,sha256=SX7qL3D1SIRKFo95FoQztvf
783
785
  tools/test/test_qontract_cli.py,sha256=w2l4BHB09k1d-BGJ1jBUNCqDv7zkqYrMHojQXg-21kQ,4155
784
786
  tools/test/test_sd_app_sre_alert_report.py,sha256=v363r9zM7__0kR5K6mvJoGFcM9BvE33fWAayrqkpojA,2116
785
787
  tools/test/test_sre_checkpoints.py,sha256=SKqPPTl9ua0RFdSSofnoQX-JZE6dFLO3LRhfQzqtfh8,2607
786
- qontract_reconcile-0.10.1rc769.dist-info/METADATA,sha256=5H5IpMZ9UroJcmYE5gaf7AwF0yRZS-AWxlPIefGupoI,2382
787
- qontract_reconcile-0.10.1rc769.dist-info/WHEEL,sha256=GJ7t_kWBFywbagK5eo9IoUwLW6oyOeTKmQ-9iHFVNxQ,92
788
- qontract_reconcile-0.10.1rc769.dist-info/entry_points.txt,sha256=rIxI5zWtHNlfpDeq1a7pZXAPoqf7HG32KMTN3MeWK_8,429
789
- qontract_reconcile-0.10.1rc769.dist-info/top_level.txt,sha256=l5ISPoXzt0SdR4jVdkfa7RPSKNc8zAHYWAnR-Dw8Ey8,24
790
- qontract_reconcile-0.10.1rc769.dist-info/RECORD,,
788
+ qontract_reconcile-0.10.1rc771.dist-info/METADATA,sha256=IZek7vs8l2MYJYkp-QzWFAXJzXAjWH3IKtlpefsU3QA,2382
789
+ qontract_reconcile-0.10.1rc771.dist-info/WHEEL,sha256=GJ7t_kWBFywbagK5eo9IoUwLW6oyOeTKmQ-9iHFVNxQ,92
790
+ qontract_reconcile-0.10.1rc771.dist-info/entry_points.txt,sha256=rIxI5zWtHNlfpDeq1a7pZXAPoqf7HG32KMTN3MeWK_8,429
791
+ qontract_reconcile-0.10.1rc771.dist-info/top_level.txt,sha256=l5ISPoXzt0SdR4jVdkfa7RPSKNc8zAHYWAnR-Dw8Ey8,24
792
+ qontract_reconcile-0.10.1rc771.dist-info/RECORD,,
reconcile/cli.py CHANGED
@@ -3515,9 +3515,15 @@ def deadmanssnitch(ctx):
3515
3515
  @integration.command(short_help="Manages External Resources")
3516
3516
  @click.pass_context
3517
3517
  @threaded(default=5)
3518
- @click.option("--cluster", help="Cluster where the Jobs will be created", default=None)
3519
3518
  @click.option(
3520
- "--namespace", help="Namespace where the Jobs will be created", default=None
3519
+ "--workers_cluster",
3520
+ help="Cluster name where the Jobs will be created",
3521
+ default=None,
3522
+ )
3523
+ @click.option(
3524
+ "--workers_namespace",
3525
+ help="Namespace name where the Jobs will be created",
3526
+ default=None,
3521
3527
  )
3522
3528
  @click.option(
3523
3529
  "--dry-run-job-suffix",
@@ -3525,17 +3531,21 @@ def deadmanssnitch(ctx):
3525
3531
  default="",
3526
3532
  )
3527
3533
  def external_resources(
3528
- ctx, cluster: str, namespace: str, dry_run_job_suffix: str, thread_pool_size: int
3534
+ ctx,
3535
+ workers_cluster: str,
3536
+ workers_namespace: str,
3537
+ dry_run_job_suffix: str,
3538
+ thread_pool_size: int,
3529
3539
  ):
3530
3540
  import reconcile.external_resources.integration
3531
3541
 
3532
3542
  run_integration(
3533
3543
  reconcile.external_resources.integration,
3534
3544
  ctx.obj,
3535
- cluster,
3536
- namespace,
3537
3545
  dry_run_job_suffix,
3538
3546
  thread_pool_size,
3547
+ workers_cluster,
3548
+ workers_namespace,
3539
3549
  )
3540
3550
 
3541
3551
 
@@ -1,4 +1,5 @@
1
1
  import logging
2
+ from typing import Callable
2
3
 
3
4
  from reconcile.external_resources.manager import (
4
5
  ExternalResourcesInventory,
@@ -15,6 +16,9 @@ from reconcile.external_resources.secrets_sync import (
15
16
  build_incluster_secrets_reconciler,
16
17
  )
17
18
  from reconcile.external_resources.state import ExternalResourcesStateDynamoDB
19
+ from reconcile.gql_definitions.external_resources.aws_accounts import (
20
+ query as aws_accounts_query,
21
+ )
18
22
  from reconcile.typed_queries.app_interface_vault_settings import (
19
23
  get_app_interface_vault_settings,
20
24
  )
@@ -23,6 +27,8 @@ from reconcile.typed_queries.external_resources import (
23
27
  get_namespaces,
24
28
  get_settings,
25
29
  )
30
+ from reconcile.utils import gql
31
+ from reconcile.utils.aws_api_typed.api import AWSApi, AWSStaticCredentials
26
32
  from reconcile.utils.jobcontroller.controller import (
27
33
  build_job_controller,
28
34
  )
@@ -30,7 +36,7 @@ from reconcile.utils.oc import (
30
36
  OCCli,
31
37
  )
32
38
  from reconcile.utils.openshift_resource import OpenshiftResource, ResourceInventory
33
- from reconcile.utils.secret_reader import create_secret_reader
39
+ from reconcile.utils.secret_reader import SecretReaderBase, create_secret_reader
34
40
 
35
41
 
36
42
  def fetch_current_state(
@@ -41,12 +47,38 @@ def fetch_current_state(
41
47
  ri.add_current(cluster, namespace, "Job", r.name, r)
42
48
 
43
49
 
50
+ def get_aws_api(
51
+ query_func: Callable,
52
+ account_name: str,
53
+ region: str,
54
+ secret_reader: SecretReaderBase,
55
+ ) -> AWSApi:
56
+ accounts = (
57
+ aws_accounts_query(
58
+ query_func, variables={"filter": {"name": account_name}}
59
+ ).accounts
60
+ or []
61
+ )
62
+ if not accounts:
63
+ raise Exception(
64
+ "External Resources configured AWS account does not exist or can not be found"
65
+ )
66
+ account = accounts[0]
67
+ automation_token = secret_reader.read_all_secret(account.automation_token)
68
+ aws_credentials = AWSStaticCredentials(
69
+ access_key_id=automation_token["aws_access_key_id"],
70
+ secret_access_key=automation_token["aws_secret_access_key"],
71
+ region=region,
72
+ )
73
+ return AWSApi(aws_credentials)
74
+
75
+
44
76
  def run(
45
77
  dry_run: bool,
46
- cluster: str,
47
- namespace: str,
48
78
  dry_run_job_suffix: str,
49
79
  thread_pool_size: int,
80
+ workers_cluster: str | None = None,
81
+ workers_namespace: str | None = None,
50
82
  ) -> None:
51
83
  vault_settings = get_app_interface_vault_settings()
52
84
  secret_reader = create_secret_reader(use_vault=vault_settings.vault)
@@ -55,41 +87,52 @@ def run(
55
87
  namespaces = [ns for ns in get_namespaces() if ns.external_resources]
56
88
  er_inventory = ExternalResourcesInventory(namespaces)
57
89
 
58
- er_mgr = ExternalResourcesManager(
59
- thread_pool_size=thread_pool_size,
60
- settings=er_settings,
90
+ if not workers_cluster:
91
+ workers_cluster = er_settings.workers_cluster.name
92
+ if not workers_namespace:
93
+ workers_namespace = er_settings.workers_namespace.name
94
+
95
+ with get_aws_api(
96
+ query_func=gql.get_api().query,
97
+ account_name=er_settings.state_dynamodb_account.name,
98
+ region=er_settings.state_dynamodb_region,
61
99
  secret_reader=secret_reader,
62
- factories=setup_factories(
63
- er_settings, m_inventory, er_inventory, secret_reader
64
- ),
65
- er_inventory=er_inventory,
66
- module_inventory=m_inventory,
67
- state_manager=ExternalResourcesStateDynamoDB(
68
- table_name=er_settings.state_dynamodb_table,
69
- region_name=er_settings.state_dynamodb_region,
70
- ),
71
- reconciler=K8sExternalResourcesReconciler(
72
- controller=build_job_controller(
73
- integration=QONTRACT_INTEGRATION,
74
- integration_version=QONTRACT_INTEGRATION_VERSION,
75
- cluster=cluster,
76
- namespace=namespace,
77
- secret_reader=secret_reader,
100
+ ) as aws_api:
101
+ er_mgr = ExternalResourcesManager(
102
+ thread_pool_size=thread_pool_size,
103
+ settings=er_settings,
104
+ secret_reader=secret_reader,
105
+ factories=setup_factories(
106
+ er_settings, m_inventory, er_inventory, secret_reader
107
+ ),
108
+ er_inventory=er_inventory,
109
+ module_inventory=m_inventory,
110
+ state_manager=ExternalResourcesStateDynamoDB(
111
+ aws_api=aws_api,
112
+ table_name=er_settings.state_dynamodb_table,
113
+ ),
114
+ reconciler=K8sExternalResourcesReconciler(
115
+ controller=build_job_controller(
116
+ integration=QONTRACT_INTEGRATION,
117
+ integration_version=QONTRACT_INTEGRATION_VERSION,
118
+ cluster=workers_cluster,
119
+ namespace=workers_namespace,
120
+ secret_reader=secret_reader,
121
+ dry_run=dry_run,
122
+ ),
78
123
  dry_run=dry_run,
124
+ dry_run_job_suffix=dry_run_job_suffix,
79
125
  ),
80
- dry_run=dry_run,
81
- dry_run_job_suffix=dry_run_job_suffix,
82
- ),
83
- secrets_reconciler=build_incluster_secrets_reconciler(
84
- cluster, namespace, secret_reader, vault_path="app-sre"
85
- ),
86
- )
126
+ secrets_reconciler=build_incluster_secrets_reconciler(
127
+ workers_cluster, workers_namespace, secret_reader, vault_path="app-sre"
128
+ ),
129
+ )
87
130
 
88
- if dry_run:
89
- er_mgr.handle_dry_run_resources()
90
- if er_mgr.errors:
91
- logging.error("Validation Errors:")
92
- for k, e in er_mgr.errors.items():
93
- logging.error("ExternalResourceKey: %s, Error: %s" % (k, e))
94
- else:
95
- er_mgr.handle_resources()
131
+ if dry_run:
132
+ er_mgr.handle_dry_run_resources()
133
+ if er_mgr.errors:
134
+ logging.error("Validation Errors:")
135
+ for k, e in er_mgr.errors.items():
136
+ logging.error("ExternalResourceKey: %s, Error: %s" % (k, e))
137
+ else:
138
+ er_mgr.handle_resources()
@@ -4,7 +4,6 @@ from datetime import datetime, timezone
4
4
  from enum import Enum
5
5
  from typing import Any
6
6
 
7
- import boto3
8
7
  from pydantic import BaseModel
9
8
 
10
9
  from reconcile.external_resources.model import (
@@ -12,6 +11,7 @@ from reconcile.external_resources.model import (
12
11
  ExternalResourceModuleConfiguration,
13
12
  Reconciliation,
14
13
  )
14
+ from reconcile.utils.aws_api_typed.api import AWSApi
15
15
 
16
16
  DATE_FORMAT = "%Y-%m-%dT%H:%M:%SZ"
17
17
 
@@ -178,16 +178,16 @@ class ExternalResourcesStateDynamoDB:
178
178
  f"{DynamoDBStateAdapter.RECONC}.{DynamoDBStateAdapter.RECONC_RESOURCE_HASH}",
179
179
  ])
180
180
 
181
- def __init__(self, table_name: str, region_name: str) -> None:
181
+ def __init__(self, aws_api: AWSApi, table_name: str) -> None:
182
182
  self.adapter = DynamoDBStateAdapter()
183
- self.client = boto3.client("dynamodb", region_name=region_name)
183
+ self.aws_api = aws_api
184
184
  self._table = table_name
185
185
  self.partial_resources = self._get_partial_resources()
186
186
 
187
187
  def get_external_resource_state(
188
188
  self, key: ExternalResourceKey
189
189
  ) -> ExternalResourceState:
190
- data = self.client.get_item(
190
+ data = self.aws_api.dynamodb.boto3_client.get_item(
191
191
  TableName=self._table,
192
192
  ConsistentRead=True,
193
193
  Key={self.adapter.ER_KEY_HASH: {"S": key.hash()}},
@@ -207,10 +207,12 @@ class ExternalResourcesStateDynamoDB:
207
207
  self,
208
208
  state: ExternalResourceState,
209
209
  ) -> None:
210
- self.client.put_item(TableName=self._table, Item=self.adapter.serialize(state))
210
+ self.aws_api.dynamodb.boto3_client.put_item(
211
+ TableName=self._table, Item=self.adapter.serialize(state)
212
+ )
211
213
 
212
214
  def del_external_resource_state(self, key: ExternalResourceKey) -> None:
213
- self.client.delete_item(
215
+ self.aws_api.dynamodb.boto3_client.delete_item(
214
216
  TableName=self._table,
215
217
  Key={self.adapter.ER_KEY_HASH: {"S": key.hash()}},
216
218
  )
@@ -224,7 +226,7 @@ class ExternalResourcesStateDynamoDB:
224
226
  """
225
227
  logging.info("Getting Managed resources from DynamoDb")
226
228
  partials = {}
227
- for item in self.client.scan(
229
+ for item in self.aws_api.dynamodb.boto3_client.scan(
228
230
  TableName=self._table, ProjectionExpression=self.PARTIALS_PROJECTED_VALUES
229
231
  ).get("Items", []):
230
232
  s = self.adapter.deserialize(item, partial_data=True)
@@ -237,7 +239,7 @@ class ExternalResourcesStateDynamoDB:
237
239
  def update_resource_status(
238
240
  self, key: ExternalResourceKey, status: ResourceStatus
239
241
  ) -> None:
240
- self.client.update_item(
242
+ self.aws_api.dynamodb.boto3_client.update_item(
241
243
  TableName=self._table,
242
244
  Key={self.adapter.ER_KEY_HASH: {"S": key.hash()}},
243
245
  UpdateExpression="set resource_status=:new_value",
@@ -0,0 +1,73 @@
1
+ """
2
+ Generated by qenerate plugin=pydantic_v1. DO NOT MODIFY MANUALLY!
3
+ """
4
+ from collections.abc import Callable # noqa: F401 # pylint: disable=W0611
5
+ from datetime import datetime # noqa: F401 # pylint: disable=W0611
6
+ from enum import Enum # noqa: F401 # pylint: disable=W0611
7
+ from typing import ( # noqa: F401 # pylint: disable=W0611
8
+ Any,
9
+ Optional,
10
+ Union,
11
+ )
12
+
13
+ from pydantic import ( # noqa: F401 # pylint: disable=W0611
14
+ BaseModel,
15
+ Extra,
16
+ Field,
17
+ Json,
18
+ )
19
+
20
+ from reconcile.gql_definitions.fragments.vault_secret import VaultSecret
21
+
22
+
23
+ DEFINITION = """
24
+ fragment VaultSecret on VaultSecret_v1 {
25
+ path
26
+ field
27
+ version
28
+ format
29
+ }
30
+
31
+ query AWSExternalResourcesAccounts($filter: JSON) {
32
+ accounts: awsaccounts_v1(filter: $filter) {
33
+ name
34
+ automationToken {
35
+ ...VaultSecret
36
+ }
37
+ }
38
+ }
39
+ """
40
+
41
+
42
+ class ConfiguredBaseModel(BaseModel):
43
+ class Config:
44
+ smart_union=True
45
+ extra=Extra.forbid
46
+
47
+
48
+ class AWSAccountV1(ConfiguredBaseModel):
49
+ name: str = Field(..., alias="name")
50
+ automation_token: VaultSecret = Field(..., alias="automationToken")
51
+
52
+
53
+ class AWSExternalResourcesAccountsQueryData(ConfiguredBaseModel):
54
+ accounts: Optional[list[AWSAccountV1]] = Field(..., alias="accounts")
55
+
56
+
57
+ def query(query_func: Callable, **kwargs: Any) -> AWSExternalResourcesAccountsQueryData:
58
+ """
59
+ This is a convenience function which queries and parses the data into
60
+ concrete types. It should be compatible with most GQL clients.
61
+ You do not have to use it to consume the generated data classes.
62
+ Alternatively, you can also mime and alternate the behavior
63
+ of this function in the caller.
64
+
65
+ Parameters:
66
+ query_func (Callable): Function which queries your GQL Server
67
+ kwargs: optional arguments that will be passed to the query function
68
+
69
+ Returns:
70
+ AWSExternalResourcesAccountsQueryData: queried data parsed into generated classes
71
+ """
72
+ raw_data: dict[Any, Any] = query_func(DEFINITION, **kwargs)
73
+ return AWSExternalResourcesAccountsQueryData(**raw_data)
@@ -21,8 +21,17 @@ from pydantic import ( # noqa: F401 # pylint: disable=W0611
21
21
  DEFINITION = """
22
22
  query ExternalResourcesSettings {
23
23
  settings: external_resources_settings_v1 {
24
+ state_dynamodb_account {
25
+ name
26
+ }
24
27
  state_dynamodb_table
25
28
  state_dynamodb_region
29
+ workers_cluster {
30
+ name
31
+ }
32
+ workers_namespace {
33
+ name
34
+ }
26
35
  tf_state_bucket
27
36
  tf_state_region
28
37
  tf_state_dynamodb_table
@@ -37,9 +46,24 @@ class ConfiguredBaseModel(BaseModel):
37
46
  extra=Extra.forbid
38
47
 
39
48
 
49
+ class AWSAccountV1(ConfiguredBaseModel):
50
+ name: str = Field(..., alias="name")
51
+
52
+
53
+ class ClusterV1(ConfiguredBaseModel):
54
+ name: str = Field(..., alias="name")
55
+
56
+
57
+ class NamespaceV1(ConfiguredBaseModel):
58
+ name: str = Field(..., alias="name")
59
+
60
+
40
61
  class ExternalResourcesSettingsV1(ConfiguredBaseModel):
62
+ state_dynamodb_account: AWSAccountV1 = Field(..., alias="state_dynamodb_account")
41
63
  state_dynamodb_table: str = Field(..., alias="state_dynamodb_table")
42
64
  state_dynamodb_region: str = Field(..., alias="state_dynamodb_region")
65
+ workers_cluster: ClusterV1 = Field(..., alias="workers_cluster")
66
+ workers_namespace: NamespaceV1 = Field(..., alias="workers_namespace")
43
67
  tf_state_bucket: Optional[str] = Field(..., alias="tf_state_bucket")
44
68
  tf_state_region: Optional[str] = Field(..., alias="tf_state_region")
45
69
  tf_state_dynamodb_table: Optional[str] = Field(..., alias="tf_state_dynamodb_table")
@@ -9,12 +9,14 @@ from boto3 import Session
9
9
  from botocore.client import BaseClient
10
10
  from pydantic import BaseModel
11
11
 
12
+ import reconcile.utils.aws_api_typed.dynamodb
12
13
  import reconcile.utils.aws_api_typed.iam
13
14
  import reconcile.utils.aws_api_typed.organization
14
15
  import reconcile.utils.aws_api_typed.s3
15
16
  import reconcile.utils.aws_api_typed.service_quotas
16
17
  import reconcile.utils.aws_api_typed.sts
17
18
  import reconcile.utils.aws_api_typed.support
19
+ from reconcile.utils.aws_api_typed.dynamodb import AWSApiDynamoDB
18
20
  from reconcile.utils.aws_api_typed.iam import AWSApiIam
19
21
  from reconcile.utils.aws_api_typed.organization import AWSApiOrganizations
20
22
  from reconcile.utils.aws_api_typed.s3 import AWSApiS3
@@ -30,6 +32,7 @@ SubApi = TypeVar(
30
32
  AWSApiServiceQuotas,
31
33
  AWSApiSts,
32
34
  AWSApiSupport,
35
+ AWSApiDynamoDB,
33
36
  )
34
37
 
35
38
 
@@ -183,6 +186,9 @@ class AWSApi:
183
186
  case reconcile.utils.aws_api_typed.support.AWSApiSupport:
184
187
  client = self.session.client("support")
185
188
  api = api_cls(client)
189
+ case reconcile.utils.aws_api_typed.dynamodb.AWSApiDynamoDB:
190
+ client = self.session.client("dynamodb")
191
+ api = api_cls(client)
186
192
  case _:
187
193
  raise ValueError(f"Unknown API class: {api_cls}")
188
194
 
@@ -219,6 +225,11 @@ class AWSApi:
219
225
  """Return an AWS Support Api client."""
220
226
  return self._init_sub_api(AWSApiSupport)
221
227
 
228
+ @cached_property
229
+ def dynamodb(self) -> AWSApiDynamoDB:
230
+ """Return an AWS DynamoDB Api client"""
231
+ return self._init_sub_api(AWSApiDynamoDB)
232
+
222
233
  def assume_role(self, account_id: str, role: str) -> AWSApi:
223
234
  """Return a new AWSApi with the assumed role."""
224
235
  credentials = self.sts.assume_role(account_id=account_id, role=role)
@@ -0,0 +1,16 @@
1
+ from typing import TYPE_CHECKING
2
+
3
+ if TYPE_CHECKING:
4
+ from mypy_boto3_dynamodb import DynamoDBClient
5
+ else:
6
+ DynamoDBClient = object
7
+
8
+
9
+ class AWSApiDynamoDB:
10
+ def __init__(self, client: DynamoDBClient) -> None:
11
+ self.client = client
12
+
13
+ @property
14
+ def boto3_client(self) -> DynamoDBClient:
15
+ """Gets the RAW boto3 DynamoDB client"""
16
+ return self.client