qontract-reconcile 0.10.1rc770__py3-none-any.whl → 0.10.1rc772__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.1rc770.dist-info → qontract_reconcile-0.10.1rc772.dist-info}/METADATA +1 -1
- {qontract_reconcile-0.10.1rc770.dist-info → qontract_reconcile-0.10.1rc772.dist-info}/RECORD +14 -12
- reconcile/cli.py +15 -5
- reconcile/external_resources/integration.py +80 -37
- reconcile/external_resources/state.py +10 -8
- reconcile/gql_definitions/external_resources/aws_accounts.py +73 -0
- reconcile/gql_definitions/external_resources/external_resources_settings.py +24 -0
- reconcile/utils/aws_api_typed/api.py +11 -0
- reconcile/utils/aws_api_typed/dynamodb.py +16 -0
- reconcile/utils/promotion_state.py +2 -0
- reconcile/utils/saasherder/saasherder.py +3 -0
- {qontract_reconcile-0.10.1rc770.dist-info → qontract_reconcile-0.10.1rc772.dist-info}/WHEEL +0 -0
- {qontract_reconcile-0.10.1rc770.dist-info → qontract_reconcile-0.10.1rc772.dist-info}/entry_points.txt +0 -0
- {qontract_reconcile-0.10.1rc770.dist-info → qontract_reconcile-0.10.1rc772.dist-info}/top_level.txt +0 -0
{qontract_reconcile-0.10.1rc770.dist-info → qontract_reconcile-0.10.1rc772.dist-info}/METADATA
RENAMED
@@ -1,6 +1,6 @@
|
|
1
1
|
Metadata-Version: 2.1
|
2
2
|
Name: qontract-reconcile
|
3
|
-
Version: 0.10.
|
3
|
+
Version: 0.10.1rc772
|
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
|
{qontract_reconcile-0.10.1rc770.dist-info → qontract_reconcile-0.10.1rc772.dist-info}/RECORD
RENAMED
@@ -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=
|
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=
|
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=
|
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=
|
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
|
@@ -646,7 +647,7 @@ reconcile/utils/pagerduty_api.py,sha256=fcSAUez6w51woDvbm0plJW2qSw6_NXQs1Fit_KTN
|
|
646
647
|
reconcile/utils/parse_dhms_duration.py,sha256=TONpLnec5gHeF7k815YNJpQyDjXhkxZIcv9s8ffbTSY,1840
|
647
648
|
reconcile/utils/password_validator.py,sha256=XwuWg-8CPlcuG7dl_oQ1G1h2gSVSnfMym_VkuprpWVg,2183
|
648
649
|
reconcile/utils/prometheus.py,sha256=i5aCQ_I4WOg76iEjglVxxO9-OKby2N80ScErAbDtHE8,3848
|
649
|
-
reconcile/utils/promotion_state.py,sha256=
|
650
|
+
reconcile/utils/promotion_state.py,sha256=cCynjmatVjjBSf0nKUmKVvk3i_Kx24iqHsCWzWRphoA,2638
|
650
651
|
reconcile/utils/promtool.py,sha256=kT2rFZSBaRqW7SSHAuYzGZzQxM5Dzk8KW1NnEUYZU_s,2896
|
651
652
|
reconcile/utils/quay_api.py,sha256=EuOegpb-7ntEjkKLFwM2Oo4Nw7SyFtmyl3sQ9aXMtrM,8152
|
652
653
|
reconcile/utils/raw_github_api.py,sha256=ZHC-SZuAyRe1zaMoOU7Krt1-zecDxENd9c_NzQYqK9g,2968
|
@@ -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=
|
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
|
@@ -748,7 +750,7 @@ reconcile/utils/runtime/sharding.py,sha256=roCdbnBklhTK_g34zbgQYqzpKPaNQ8J6Xd9XL
|
|
748
750
|
reconcile/utils/saasherder/__init__.py,sha256=J3MBZBFa5YmhqYm08QsjBXz8mFcVOCiOCkyIcw41t7E,343
|
749
751
|
reconcile/utils/saasherder/interfaces.py,sha256=XXY35h8VWQ66z3LBPxaoUAMkIW50264DQiecrzyV6oA,9076
|
750
752
|
reconcile/utils/saasherder/models.py,sha256=1DKXUmiTS_MejUfSpFCeuBLMTgR4ldv2N1tAz8qHAwc,5547
|
751
|
-
reconcile/utils/saasherder/saasherder.py,sha256=
|
753
|
+
reconcile/utils/saasherder/saasherder.py,sha256=CfTNBjVS9HwH1hlz7K50J-vpT8B5UVJTmyd2ZGbAPAg,86293
|
752
754
|
reconcile/utils/terraform/__init__.py,sha256=zNbiyTWo35AT1sFTElL2j_AA0jJ_yWE_bfFn-nD2xik,250
|
753
755
|
reconcile/utils/terraform/config.py,sha256=5UVrd563TMcvi4ooa5JvWVDW1I3bIWg484u79evfV_8,164
|
754
756
|
reconcile/utils/terraform/config_client.py,sha256=py-Ree-QUYD6Hvng6bM40VgSuttteehIKNgwOSoJO1o,4706
|
@@ -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.
|
787
|
-
qontract_reconcile-0.10.
|
788
|
-
qontract_reconcile-0.10.
|
789
|
-
qontract_reconcile-0.10.
|
790
|
-
qontract_reconcile-0.10.
|
788
|
+
qontract_reconcile-0.10.1rc772.dist-info/METADATA,sha256=ggkGwJEVc2hcdi_OZpg-AGRT0hX8-c-5bD6NTbwG6ro,2382
|
789
|
+
qontract_reconcile-0.10.1rc772.dist-info/WHEEL,sha256=GJ7t_kWBFywbagK5eo9IoUwLW6oyOeTKmQ-9iHFVNxQ,92
|
790
|
+
qontract_reconcile-0.10.1rc772.dist-info/entry_points.txt,sha256=rIxI5zWtHNlfpDeq1a7pZXAPoqf7HG32KMTN3MeWK_8,429
|
791
|
+
qontract_reconcile-0.10.1rc772.dist-info/top_level.txt,sha256=l5ISPoXzt0SdR4jVdkfa7RPSKNc8zAHYWAnR-Dw8Ey8,24
|
792
|
+
qontract_reconcile-0.10.1rc772.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
|
-
"--
|
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,
|
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
|
-
|
59
|
-
|
60
|
-
|
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
|
-
|
63
|
-
|
64
|
-
|
65
|
-
|
66
|
-
|
67
|
-
|
68
|
-
|
69
|
-
|
70
|
-
|
71
|
-
|
72
|
-
|
73
|
-
|
74
|
-
|
75
|
-
|
76
|
-
|
77
|
-
|
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
|
-
|
81
|
-
|
82
|
-
|
83
|
-
|
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
|
-
|
89
|
-
|
90
|
-
|
91
|
-
|
92
|
-
|
93
|
-
|
94
|
-
|
95
|
-
|
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,
|
181
|
+
def __init__(self, aws_api: AWSApi, table_name: str) -> None:
|
182
182
|
self.adapter = DynamoDBStateAdapter()
|
183
|
-
self.
|
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.
|
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.
|
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.
|
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.
|
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.
|
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
|
@@ -1,5 +1,6 @@
|
|
1
1
|
import logging
|
2
2
|
from collections import defaultdict
|
3
|
+
from datetime import datetime
|
3
4
|
from typing import Optional
|
4
5
|
|
5
6
|
from pydantic import (
|
@@ -22,6 +23,7 @@ class PromotionData(BaseModel):
|
|
22
23
|
success: bool
|
23
24
|
target_config_hash: Optional[str]
|
24
25
|
saas_file: Optional[str]
|
26
|
+
check_in: Optional[datetime]
|
25
27
|
|
26
28
|
class Config:
|
27
29
|
smart_union = True
|
@@ -16,6 +16,7 @@ from collections.abc import (
|
|
16
16
|
Sequence,
|
17
17
|
)
|
18
18
|
from contextlib import suppress
|
19
|
+
from datetime import datetime, timezone
|
19
20
|
from types import TracebackType
|
20
21
|
from typing import (
|
21
22
|
Any,
|
@@ -1965,6 +1966,7 @@ class SaasHerder: # pylint: disable=too-many-public-methods
|
|
1965
1966
|
if not (self.state and self._promotion_state):
|
1966
1967
|
raise Exception("state is not initialized")
|
1967
1968
|
|
1969
|
+
now = datetime.now(timezone.utc)
|
1968
1970
|
for promotion in self.promotions:
|
1969
1971
|
if promotion is None:
|
1970
1972
|
continue
|
@@ -1982,6 +1984,7 @@ class SaasHerder: # pylint: disable=too-many-public-methods
|
|
1982
1984
|
saas_file=promotion.saas_file,
|
1983
1985
|
success=success,
|
1984
1986
|
target_config_hash=promotion.target_config_hash,
|
1987
|
+
check_in=now,
|
1985
1988
|
),
|
1986
1989
|
)
|
1987
1990
|
logging.info(
|
File without changes
|
File without changes
|
{qontract_reconcile-0.10.1rc770.dist-info → qontract_reconcile-0.10.1rc772.dist-info}/top_level.txt
RENAMED
File without changes
|