qontract-reconcile 0.10.1rc696__py3-none-any.whl → 0.10.1rc702__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.1rc696.dist-info → qontract_reconcile-0.10.1rc702.dist-info}/METADATA +1 -1
- {qontract_reconcile-0.10.1rc696.dist-info → qontract_reconcile-0.10.1rc702.dist-info}/RECORD +42 -18
- reconcile/aws_account_manager/__init__.py +0 -0
- reconcile/aws_account_manager/integration.py +342 -0
- reconcile/aws_account_manager/merge_request_manager.py +111 -0
- reconcile/aws_account_manager/reconciler.py +353 -0
- reconcile/aws_account_manager/utils.py +38 -0
- reconcile/aws_saml_idp/integration.py +2 -0
- reconcile/aws_version_sync/integration.py +12 -11
- reconcile/aws_version_sync/merge_request_manager/merge_request_manager.py +39 -112
- reconcile/cli.py +79 -0
- reconcile/gql_definitions/aws_account_manager/__init__.py +0 -0
- reconcile/gql_definitions/aws_account_manager/aws_accounts.py +163 -0
- reconcile/gql_definitions/cost_report/__init__.py +0 -0
- reconcile/gql_definitions/cost_report/app_names.py +68 -0
- reconcile/gql_definitions/cost_report/settings.py +77 -0
- reconcile/gql_definitions/fragments/aws_account_managed.py +49 -0
- reconcile/queries.py +7 -1
- reconcile/templating/lib/merge_request_manager.py +8 -82
- reconcile/templating/renderer.py +2 -2
- reconcile/typed_queries/cost_report/__init__.py +0 -0
- reconcile/typed_queries/cost_report/app_names.py +22 -0
- reconcile/typed_queries/cost_report/settings.py +15 -0
- reconcile/utils/aws_api_typed/api.py +49 -6
- reconcile/utils/aws_api_typed/iam.py +22 -7
- reconcile/utils/aws_api_typed/organization.py +78 -30
- reconcile/utils/aws_api_typed/service_quotas.py +79 -0
- reconcile/utils/aws_api_typed/support.py +79 -0
- reconcile/utils/merge_request_manager/merge_request_manager.py +102 -0
- reconcile/utils/oauth2_backend_application_session.py +102 -0
- reconcile/utils/state.py +42 -38
- tools/cli_commands/cost_report/__init__.py +0 -0
- tools/cli_commands/cost_report/command.py +172 -0
- tools/cli_commands/cost_report/cost_management_api.py +57 -0
- tools/cli_commands/cost_report/model.py +29 -0
- tools/cli_commands/cost_report/response.py +48 -0
- tools/cli_commands/cost_report/view.py +333 -0
- tools/qontract_cli.py +10 -2
- tools/test/test_qontract_cli.py +20 -0
- {qontract_reconcile-0.10.1rc696.dist-info → qontract_reconcile-0.10.1rc702.dist-info}/WHEEL +0 -0
- {qontract_reconcile-0.10.1rc696.dist-info → qontract_reconcile-0.10.1rc702.dist-info}/entry_points.txt +0 -0
- {qontract_reconcile-0.10.1rc696.dist-info → qontract_reconcile-0.10.1rc702.dist-info}/top_level.txt +0 -0
{qontract_reconcile-0.10.1rc696.dist-info → qontract_reconcile-0.10.1rc702.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.1rc702
|
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.1rc696.dist-info → qontract_reconcile-0.10.1rc702.dist-info}/RECORD
RENAMED
@@ -9,7 +9,7 @@ reconcile/aws_iam_password_reset.py,sha256=NwErtrqgBiXr7eGCAHdtGGOx0S7-4JnSc29Ie
|
|
9
9
|
reconcile/aws_support_cases_sos.py,sha256=Jk6_XjDeJSYxgRGqcEAOcynt9qJF2r5HPIPcSKmoBv8,2974
|
10
10
|
reconcile/blackbox_exporter_endpoint_monitoring.py,sha256=W_VJagnsJR1v5oqjlI3RJJE0_nhtJ0m81RS8zWA5u5c,3538
|
11
11
|
reconcile/checkpoint.py,sha256=R2WFXUXLTB4sWMi4GeA4eegsuf_1-Q4vH8M0Toh3Ij4,5036
|
12
|
-
reconcile/cli.py,sha256=
|
12
|
+
reconcile/cli.py,sha256=deAj6fNIYnrEKgOKv2UYZKAvuvEmYVUWj4nDSG6y99U,96070
|
13
13
|
reconcile/closedbox_endpoint_monitoring_base.py,sha256=SMhkcQqprWvThrIJa3U_3uh5w1h-alleW1QnCJFY4Qw,4909
|
14
14
|
reconcile/cluster_deployment_mapper.py,sha256=2Ah-nu-Mdig0pjuiZl_XLrmVAjYzFjORR3dMlCgkmw0,2352
|
15
15
|
reconcile/dashdotdb_base.py,sha256=a5aPLVxyqPSbjdB0Ty-uliOtxwvEbbEljHJKxdK3-Zk,4813
|
@@ -92,7 +92,7 @@ reconcile/quay_mirror.py,sha256=9NzbNoxl-NdD8CwImcXNG5xTdHmUJxBfeVk5XHH41J8,1488
|
|
92
92
|
reconcile/quay_mirror_org.py,sha256=Oq-t3kSkgfeSAOUDjLCDRBeEvOIEBacfX38qrX_s0oc,10801
|
93
93
|
reconcile/quay_permissions.py,sha256=9KOutS1w4RFQqkvMSy54VtsKNx56-phzP6yI_rEW-B8,4244
|
94
94
|
reconcile/quay_repos.py,sha256=cuEYG0HUe0ut5yvLdEwOF5-CmccpXQHRb_wDazvDrvQ,6895
|
95
|
-
reconcile/queries.py,sha256=
|
95
|
+
reconcile/queries.py,sha256=NFYbAkPkffZDU6rW1tn9r16jjLSmqWLPKBO6FkSbUWg,50829
|
96
96
|
reconcile/query_validator.py,sha256=BAjGrU8_VhzTOv5k0-uz0hY9ziZyconv8VAhgre1Auc,1497
|
97
97
|
reconcile/requests_sender.py,sha256=914iluuF4UVgG3VyxxtnHOu4yf6YKS2fIy6PViSsFTQ,3875
|
98
98
|
reconcile/resource_scraper.py,sha256=vo1N9vLJCYWvXlTwFRIpEuWjx_39ZV9zxJlpoPq4g3U,2330
|
@@ -135,20 +135,25 @@ reconcile/aus/version_gates/handler.py,sha256=S_isQPYHbG4DERiUEvQBZ6ngiFX3uMmATA
|
|
135
135
|
reconcile/aus/version_gates/ingress_gate_handler.py,sha256=ZCtyggBzzcb0prtdbMpJsVkj5leYN-vS7srM9vbq9xo,1096
|
136
136
|
reconcile/aus/version_gates/ocp_gate_handler.py,sha256=RW1ppDaCZXVegV9AzzqYXxDUu_Z_7d43Z5h2Pk_piKc,716
|
137
137
|
reconcile/aus/version_gates/sts_version_gate_handler.py,sha256=PhJ7yBh2q-rv9CJcfFhc0H11nyDyG7NAryNS3F74xdY,3697
|
138
|
+
reconcile/aws_account_manager/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
139
|
+
reconcile/aws_account_manager/integration.py,sha256=TLlhxnHXRCVz2GYJQei-dBdSpeLseEkoVUwHhgi41fk,13804
|
140
|
+
reconcile/aws_account_manager/merge_request_manager.py,sha256=zZct3NxWMBQupl4QfD7ULxnt4ipt_2FBoH_NusboIuw,3781
|
141
|
+
reconcile/aws_account_manager/reconciler.py,sha256=AqAA3TIEfuYzIogHSBgwYTebxbTy1D6JhcxdLiOfCsc,13588
|
142
|
+
reconcile/aws_account_manager/utils.py,sha256=K4rAjEMK-eQ_Sv4lOf6dPynQy97xZ4h-n6cJn5Z6zVw,1248
|
138
143
|
reconcile/aws_ami_cleanup/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
139
144
|
reconcile/aws_ami_cleanup/integration.py,sha256=IW95cpMj2P5ffs-AxsR_TDQCJnYFBhLIfP2de7dz_8A,10109
|
140
145
|
reconcile/aws_cloudwatch_log_retention/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
141
146
|
reconcile/aws_cloudwatch_log_retention/integration.py,sha256=0UcSZIrGvnGY4m9fj87oejIolIP_qTxtJInpmW9jrQ0,7772
|
142
147
|
reconcile/aws_saml_idp/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
143
|
-
reconcile/aws_saml_idp/integration.py,sha256=
|
148
|
+
reconcile/aws_saml_idp/integration.py,sha256=q0usjBp79aydlcD8kAq-5T2NKhZgEblGLvBBPHiJdKw,4956
|
144
149
|
reconcile/aws_saml_roles/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
145
150
|
reconcile/aws_saml_roles/integration.py,sha256=kC4Rnbuy07TMvZO4rjUEcQkJev10M0Ro6r7YXcB7j_c,9530
|
146
151
|
reconcile/aws_version_sync/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
147
|
-
reconcile/aws_version_sync/integration.py,sha256=
|
152
|
+
reconcile/aws_version_sync/integration.py,sha256=uI6k0nNS_jRrVaIcgm30Hj_M6GIJmexU2X-6Dxe0CZo,17271
|
148
153
|
reconcile/aws_version_sync/utils.py,sha256=sVv-48PKi2VITlqqvmpbjnFDOPeGqfKzgkpIszlmjL0,1708
|
149
154
|
reconcile/aws_version_sync/merge_request_manager/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
150
155
|
reconcile/aws_version_sync/merge_request_manager/merge_request.py,sha256=2FbqLLdqxycWNvX1eNbwMjWSVBb7q0p-8t5Db0m7b4Q,4842
|
151
|
-
reconcile/aws_version_sync/merge_request_manager/merge_request_manager.py,sha256=
|
156
|
+
reconcile/aws_version_sync/merge_request_manager/merge_request_manager.py,sha256=3bRpw7DluiYw3daRg0yAyCSGYf39Ru0d8lUjoepDSpU,5525
|
152
157
|
reconcile/change_owners/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
153
158
|
reconcile/change_owners/approver.py,sha256=GV8nwS-YJOJ8O-b9v3u60RSYECYH2EKAycjpoW6VmvU,2228
|
154
159
|
reconcile/change_owners/bundle.py,sha256=dZ-GRCIgpSYwKzZD9rs64Ie09OptzDc8aR2X2msnt3Q,5363
|
@@ -185,6 +190,8 @@ reconcile/gql_definitions/advanced_upgrade_service/aus_clusters.py,sha256=zrZCHa
|
|
185
190
|
reconcile/gql_definitions/advanced_upgrade_service/aus_organization.py,sha256=uFx75cLe1a4xyArr6ekoAUbrzSRnhR7S2vaR3G5Fzbw,3299
|
186
191
|
reconcile/gql_definitions/app_interface_metrics_exporter/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
187
192
|
reconcile/gql_definitions/app_interface_metrics_exporter/onboarding_status.py,sha256=uVEEqU6YYmKsNTo6EWlFnoVmqha2rvBDx-wiD64VmG0,1679
|
193
|
+
reconcile/gql_definitions/aws_account_manager/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
194
|
+
reconcile/gql_definitions/aws_account_manager/aws_accounts.py,sha256=c3RmQwbHa9UlfGwruufkA8PUfeiRJZ-lXEDInAREZqE,4405
|
188
195
|
reconcile/gql_definitions/aws_ami_cleanup/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
189
196
|
reconcile/gql_definitions/aws_ami_cleanup/asg_namespaces.py,sha256=OJmeTu7uirLGAysZ3IQTtRXqMyL8noi_QZxPuWYxxmI,3678
|
190
197
|
reconcile/gql_definitions/aws_saml_idp/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
@@ -230,6 +237,9 @@ reconcile/gql_definitions/common/saasherder_settings.py,sha256=nqQLcMwYxLseqq0BE
|
|
230
237
|
reconcile/gql_definitions/common/smtp_client_settings.py,sha256=JU6t6D-Qj-z1gLlgUiHKe0W7AxWQdty9jlv-ig_43tM,2248
|
231
238
|
reconcile/gql_definitions/common/state_aws_account.py,sha256=LAdpCG2-ykVpWBPO0Zu1WvG-hwKXyDC0fJQxJRpbqCk,2198
|
232
239
|
reconcile/gql_definitions/common/users.py,sha256=uDiEDqa4QP89I2oFuKhCtVB61ZviIt7Y75fgrcCm7M4,1681
|
240
|
+
reconcile/gql_definitions/cost_report/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
241
|
+
reconcile/gql_definitions/cost_report/app_names.py,sha256=fzqYXyiTSll359J1F1o7qapco0MSxgs3sr_Ssb2Kbns,1786
|
242
|
+
reconcile/gql_definitions/cost_report/settings.py,sha256=0nhBDJ5MZ1m7XkNDGrRLmsnUbzqZ4WRh_DDEEzKhcxU,2153
|
233
243
|
reconcile/gql_definitions/dashdotdb_slo/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
234
244
|
reconcile/gql_definitions/dashdotdb_slo/slo_documents_query.py,sha256=zUa-CmpOwiymVmOV6KwDHH5mMl06p000320FcOas6hU,4315
|
235
245
|
reconcile/gql_definitions/dynatrace_token_provider/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
@@ -237,6 +247,7 @@ reconcile/gql_definitions/dynatrace_token_provider/dynatrace_bootstrap_tokens.py
|
|
237
247
|
reconcile/gql_definitions/fragments/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
238
248
|
reconcile/gql_definitions/fragments/aus_organization.py,sha256=ARI87YAbC0VjFri9eVGYrRPBc4s0kWsa25RR8FFoq7E,4433
|
239
249
|
reconcile/gql_definitions/fragments/aws_account_common.py,sha256=d_FwpS_dY8o8DCLa3NERs93FVxQLiDUIPm5tGNac-iw,2320
|
250
|
+
reconcile/gql_definitions/fragments/aws_account_managed.py,sha256=zXbux0Bb7QZ37fU4LLMKB4m0rT3Y8bD10C1TuOsH_ZQ,1470
|
240
251
|
reconcile/gql_definitions/fragments/aws_account_sso.py,sha256=ITR3PLz4Iq1SiWAoYGWPDuHJnAmTyZ0QQqs2Zsi8pxA,979
|
241
252
|
reconcile/gql_definitions/fragments/aws_infra_management_account.py,sha256=uAmALVRF2gBM3p_Dmez_ew6KVAtetamwOPkRIPZAlGc,1254
|
242
253
|
reconcile/gql_definitions/fragments/aws_vpc.py,sha256=T2egTwi2Rb0IRBBmsyag8xKpu_m6GbIAy80fhZNZwk8,1434
|
@@ -400,10 +411,10 @@ reconcile/templates/jira-checkpoint-missinginfo.j2,sha256=c_Vvg-lEENsB3tgxm9B6Y9
|
|
400
411
|
reconcile/templates/rosa-classic-cluster-creation.sh.j2,sha256=0UHfYtXRVJqP07VJQx456cRI6EbZNBgamtP_8nb4WPY,2353
|
401
412
|
reconcile/templates/rosa-hcp-cluster-creation.sh.j2,sha256=O7Bf3WQIJhsZoEqaYA0wRktUO4yXXCb4BQkuvvp-C80,2385
|
402
413
|
reconcile/templating/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
403
|
-
reconcile/templating/renderer.py,sha256=
|
414
|
+
reconcile/templating/renderer.py,sha256=xXCzRuhkDOCPELRKzjkAARTgs4iOoNcdDN_hlKJxyjg,8516
|
404
415
|
reconcile/templating/validator.py,sha256=QGH2VSk7sVBuojhk9quRAbj_XBykHN-KZ53DbEneUJs,4391
|
405
416
|
reconcile/templating/lib/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
406
|
-
reconcile/templating/lib/merge_request_manager.py,sha256=
|
417
|
+
reconcile/templating/lib/merge_request_manager.py,sha256=El3ufdVjHP4EW-LztX7zPiI9syJBJ6ETGRmiM9K4Nqw,5112
|
407
418
|
reconcile/templating/lib/model.py,sha256=TYiH2xL63awd30U-fkZDLEzuxkLdUEgzJ913DEzpFoM,265
|
408
419
|
reconcile/templating/lib/rendering.py,sha256=_BVQ2gqip8K1AgLYfaTWh8NKJFTW6VjUZ6rBI_GH30E,5061
|
409
420
|
reconcile/test/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
@@ -546,6 +557,9 @@ reconcile/typed_queries/tekton_pipeline_providers.py,sha256=2mpHBdsNPQB94tw0H9ae
|
|
546
557
|
reconcile/typed_queries/terraform_namespaces.py,sha256=71ARJ-GzkU9tBM0IfJTL3NF4349SJy-Mgs_DwAgUz_g,444
|
547
558
|
reconcile/typed_queries/app_interface_metrics_exporter/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
548
559
|
reconcile/typed_queries/app_interface_metrics_exporter/onboarding_status.py,sha256=X-N1WJGOL6OR9940P0_K4-YJzkL5Vg4favhYrBxXD9A,327
|
560
|
+
reconcile/typed_queries/cost_report/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
561
|
+
reconcile/typed_queries/cost_report/app_names.py,sha256=HMEMIqAbMyVQfoQ5YXTXE4xDt7FaXBRz0QIHnsIZC1c,478
|
562
|
+
reconcile/typed_queries/cost_report/settings.py,sha256=xbTMMUQnbub2pav4B-ctzzRe7ijjTv2bqfqdtb9OnO0,589
|
549
563
|
reconcile/typed_queries/terraform_tgw_attachments/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
550
564
|
reconcile/typed_queries/terraform_tgw_attachments/aws_accounts.py,sha256=T5HSeyBcGKP-LzDmIzs-WlBwOtSenYpm0Odw5--xdOg,410
|
551
565
|
reconcile/utils/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
@@ -592,6 +606,7 @@ reconcile/utils/lean_terraform_client.py,sha256=zReyNPJbr2uOdrdh8Qfe-OZQBoRwxb5Z
|
|
592
606
|
reconcile/utils/make.py,sha256=QaEwucrzbl8-VHS66Wfdjfo0ubmAcvt_hZGpiGsKU50,231
|
593
607
|
reconcile/utils/metrics.py,sha256=7nXdctmZ0UtGMHPpS3V55sfH4xpMPqdYaJ3JKAUc_sM,18474
|
594
608
|
reconcile/utils/models.py,sha256=It_Q1WNIvw_EDCsiSWzIgpSPr_X9jMgbJI-DR3N23xY,4677
|
609
|
+
reconcile/utils/oauth2_backend_application_session.py,sha256=kWUX2LTwKniD01-0a7x-kV9ud3Q30DpLgh1xDbUhLSI,3298
|
595
610
|
reconcile/utils/oc.py,sha256=ILAlP-AZMtWeyAepLoMnYbDJfyyMs-Z0fOEo9JXQfkE,65490
|
596
611
|
reconcile/utils/oc_connection_parameters.py,sha256=85slrnDigYwYmzhyceVkMElWzFArp4ge1d-fHXVqh0w,9729
|
597
612
|
reconcile/utils/oc_filters.py,sha256=R2Lf3fo0jQCeE62Ygeo_KN24XbAosq0QbjimYG6qHI4,1402
|
@@ -616,7 +631,7 @@ reconcile/utils/sharding.py,sha256=gkYf0lD3IUKQPEmdRJZ70mdDT1c9qWjbdP7evRsUis4,8
|
|
616
631
|
reconcile/utils/slack_api.py,sha256=OPmzU6L9rJx2XXDlZkMlxLjOWu17yC-fVCoUItzQrXw,16295
|
617
632
|
reconcile/utils/smtp_client.py,sha256=gJNbBQJpAt5PX4t_TaeNHsXM8vt50bFgndml6yK2b5o,2800
|
618
633
|
reconcile/utils/sqs_gateway.py,sha256=gFl9DM4DmGnptuxTOe4lS3YTyE80eSAvK42ljS8h4dA,2287
|
619
|
-
reconcile/utils/state.py,sha256=
|
634
|
+
reconcile/utils/state.py,sha256=FK8NLT1xyumuXpYRm0Nk6pWpOE_U6-NovGn6zKCw8vw,16298
|
620
635
|
reconcile/utils/structs.py,sha256=LcbLEg8WxfRqM6nW7NhcWN0YeqF7SQzxOgntmLs1SgY,352
|
621
636
|
reconcile/utils/template.py,sha256=wTvRU4AnAV_o042tD4Mwls2dwWMuk7MKnde3MaCjaYg,331
|
622
637
|
reconcile/utils/terraform_client.py,sha256=mZEKpu6nbfiQd60wRkc8-5sljBTUTOgaAKnF89itMzU,32085
|
@@ -632,10 +647,12 @@ reconcile/utils/acs/base.py,sha256=Qih-xZ3RBJZEE291iHHlv7lUY6ShcAvSj1PA3_aTTnM,2
|
|
632
647
|
reconcile/utils/acs/policies.py,sha256=_jAz6cv8KRYtDsXjGoJgNbD8_9PUa5LSwwVlpK4A_cQ,5505
|
633
648
|
reconcile/utils/acs/rbac.py,sha256=ugsLM9Pb7FbUbdq85E3VzXGMaB9ZovXob7tdWCxwqZ8,8808
|
634
649
|
reconcile/utils/aws_api_typed/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
635
|
-
reconcile/utils/aws_api_typed/api.py,sha256=
|
636
|
-
reconcile/utils/aws_api_typed/iam.py,sha256=
|
637
|
-
reconcile/utils/aws_api_typed/organization.py,sha256=
|
650
|
+
reconcile/utils/aws_api_typed/api.py,sha256=gqfZISSQLp6tHEAwEsroLWwyU4ZdbwHi9p0rNBQyLuI,7901
|
651
|
+
reconcile/utils/aws_api_typed/iam.py,sha256=ka46H2-SzTCgy6EJYapKTzyZK9vR1bkfD0wF8bDdy1Q,2201
|
652
|
+
reconcile/utils/aws_api_typed/organization.py,sha256=oXftcLVuSs9qej6efdssl38FvjeZaQC5R2Wj3NzxX4U,5529
|
653
|
+
reconcile/utils/aws_api_typed/service_quotas.py,sha256=OU1D8LCmMw1IT87nt45LqXhguzcWwC8AaBdDTI7tz98,3018
|
638
654
|
reconcile/utils/aws_api_typed/sts.py,sha256=5Sauncj9Fif3YDLkJYkBZrtOX0v0bGAqOmY0A5Bh9yA,1237
|
655
|
+
reconcile/utils/aws_api_typed/support.py,sha256=PH3UW96Ne4_8I1J-_Vqj-DsK73gYGeVdOH13eD1783c,2447
|
639
656
|
reconcile/utils/cloud_resource_best_practice/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
640
657
|
reconcile/utils/cloud_resource_best_practice/aws_rds.py,sha256=EvE6XKLsrZ531MJptKqPht2lOETrOjySTHXk6CzMgo0,2279
|
641
658
|
reconcile/utils/clusterhealth/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
@@ -659,6 +676,7 @@ reconcile/utils/membershipsources/app_interface_resolver.py,sha256=IlDiRtJZ0AfAG
|
|
659
676
|
reconcile/utils/membershipsources/models.py,sha256=IFu6KHFe-HUTJPiAO3fEw7i22yv4_ytgBW-h_wrO6V4,2015
|
660
677
|
reconcile/utils/membershipsources/resolver.py,sha256=meERrCZqeVJZR2lHdZEznaZDsCsUb194UEMOjbE4aYA,3168
|
661
678
|
reconcile/utils/merge_request_manager/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
679
|
+
reconcile/utils/merge_request_manager/merge_request_manager.py,sha256=6i2uwSJoeYiZSRwcnBG5ICQXyIC8umvQ_TzubR7_jvA,3374
|
662
680
|
reconcile/utils/merge_request_manager/parser.py,sha256=5pGoz8Q6EuYXlUc1z-D0FahdRP2YLO8CpACoa9HcgtQ,2098
|
663
681
|
reconcile/utils/mr/__init__.py,sha256=JQS8xmLZdG4TgjAFZp2ltuV9c1sDFHIHJmpn1bkY9rM,2386
|
664
682
|
reconcile/utils/mr/app_interface_reporter.py,sha256=6Kpg93V9FvcOke9Jimkva359MQ-ZyBIkUpf8QIA6-to,1793
|
@@ -717,20 +735,26 @@ tools/app_interface_metrics_exporter.py,sha256=zkwkxdAUAxjdc-pzx2_oJXG25fo0Fnyd5
|
|
717
735
|
tools/app_interface_reporter.py,sha256=upA-J-n-HXHKVDINRuMR7vTt-iJvQORKUVi9D3leQto,17738
|
718
736
|
tools/glitchtip_access_reporter.py,sha256=oPBnk_YoDuljU3v0FaChzOwwnk4vap1xEE67QEjzdqs,2948
|
719
737
|
tools/glitchtip_access_revalidation.py,sha256=8kbBJk04mkq28kWoRDDkfCGIF3GRg3pJrFAh1sW0dbk,2821
|
720
|
-
tools/qontract_cli.py,sha256=
|
738
|
+
tools/qontract_cli.py,sha256=AdOUq_74y-s1nDbF6Yh0z1sTQ1r8yS3nPG9yP-slWfE,111846
|
721
739
|
tools/sd_app_sre_alert_report.py,sha256=e9vAdyenUz2f5c8-z-5WY0wv-SJ9aePKDH2r4IwB6pc,5063
|
722
740
|
tools/template_validation.py,sha256=-U-lTGeLaci8yWPEblCJeev2DOlY1jM9QOOh-O1zts8,3376
|
723
741
|
tools/cli_commands/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
724
742
|
tools/cli_commands/gpg_encrypt.py,sha256=w8hl4jIEWk5wKbEFN6fVEOwUJGmdlvOqYodW3XSN7mU,4978
|
743
|
+
tools/cli_commands/cost_report/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
744
|
+
tools/cli_commands/cost_report/command.py,sha256=ecT4SjCwhoWNFF9Xb1spqYzI7QMwX9EFWz6XFSW4bas,6317
|
745
|
+
tools/cli_commands/cost_report/cost_management_api.py,sha256=IuPbrtNpgt1wvzHAPQjGPdCobXIkvkusqP8uXv3G204,1743
|
746
|
+
tools/cli_commands/cost_report/model.py,sha256=blNk52T6KIDy-7w4V6qvZhGFWplxgDfZ5jRZimPDYJo,593
|
747
|
+
tools/cli_commands/cost_report/response.py,sha256=lOmIohSuwJBxoSAC9LCwihBsyWgutHCIW-nvd6_HX5Y,867
|
748
|
+
tools/cli_commands/cost_report/view.py,sha256=LtM5AVv1pZ9yT1C_IcTrHHyXlQkRWeS3nnDf5XQVoVQ,8554
|
725
749
|
tools/sre_checkpoints/__init__.py,sha256=CDaDaywJnmRCLyl_NCcvxi-Zc0hTi_3OdwKiFOyS39I,145
|
726
750
|
tools/sre_checkpoints/util.py,sha256=zEDbGr18ZeHNQwW8pUsr2JRjuXIPz--WAGJxZo9sv_Y,894
|
727
751
|
tools/test/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
728
752
|
tools/test/test_app_interface_metrics_exporter.py,sha256=SX7qL3D1SIRKFo95FoQztvftCWEEf-g1mfXOtgCog-g,1271
|
729
|
-
tools/test/test_qontract_cli.py,sha256=
|
753
|
+
tools/test/test_qontract_cli.py,sha256=UEwAW7PA_GIrbqzaLxpkCxbuVjEFLNvnVG-6VyoCGIc,4147
|
730
754
|
tools/test/test_sd_app_sre_alert_report.py,sha256=v363r9zM7__0kR5K6mvJoGFcM9BvE33fWAayrqkpojA,2116
|
731
755
|
tools/test/test_sre_checkpoints.py,sha256=SKqPPTl9ua0RFdSSofnoQX-JZE6dFLO3LRhfQzqtfh8,2607
|
732
|
-
qontract_reconcile-0.10.
|
733
|
-
qontract_reconcile-0.10.
|
734
|
-
qontract_reconcile-0.10.
|
735
|
-
qontract_reconcile-0.10.
|
736
|
-
qontract_reconcile-0.10.
|
756
|
+
qontract_reconcile-0.10.1rc702.dist-info/METADATA,sha256=Sk3NS11248E7ukRziTkbkvYPypcX1zWYqHoDJBXKxB4,2382
|
757
|
+
qontract_reconcile-0.10.1rc702.dist-info/WHEEL,sha256=GJ7t_kWBFywbagK5eo9IoUwLW6oyOeTKmQ-9iHFVNxQ,92
|
758
|
+
qontract_reconcile-0.10.1rc702.dist-info/entry_points.txt,sha256=rIxI5zWtHNlfpDeq1a7pZXAPoqf7HG32KMTN3MeWK_8,429
|
759
|
+
qontract_reconcile-0.10.1rc702.dist-info/top_level.txt,sha256=l5ISPoXzt0SdR4jVdkfa7RPSKNc8zAHYWAnR-Dw8Ey8,24
|
760
|
+
qontract_reconcile-0.10.1rc702.dist-info/RECORD,,
|
File without changes
|
@@ -0,0 +1,342 @@
|
|
1
|
+
from collections.abc import Callable, Iterable
|
2
|
+
from datetime import datetime, timezone
|
3
|
+
from typing import Any
|
4
|
+
|
5
|
+
import jinja2
|
6
|
+
|
7
|
+
from reconcile.aws_account_manager.merge_request_manager import MergeRequestManager
|
8
|
+
from reconcile.aws_account_manager.reconciler import AWSReconciler
|
9
|
+
from reconcile.aws_account_manager.utils import validate
|
10
|
+
from reconcile.gql_definitions.aws_account_manager.aws_accounts import (
|
11
|
+
AWSAccountManaged,
|
12
|
+
AWSAccountRequestV1,
|
13
|
+
AWSAccountV1,
|
14
|
+
)
|
15
|
+
from reconcile.gql_definitions.aws_account_manager.aws_accounts import (
|
16
|
+
query as aws_accounts_query,
|
17
|
+
)
|
18
|
+
from reconcile.typed_queries.app_interface_repo_url import get_app_interface_repo_url
|
19
|
+
from reconcile.typed_queries.github_orgs import get_github_orgs
|
20
|
+
from reconcile.typed_queries.gitlab_instances import get_gitlab_instances
|
21
|
+
from reconcile.utils import gql
|
22
|
+
from reconcile.utils.aws_api_typed.api import AWSApi, AWSStaticCredentials
|
23
|
+
from reconcile.utils.aws_api_typed.iam import AWSAccessKey
|
24
|
+
from reconcile.utils.defer import defer
|
25
|
+
from reconcile.utils.disabled_integrations import integration_is_enabled
|
26
|
+
from reconcile.utils.runtime.integration import (
|
27
|
+
PydanticRunParams,
|
28
|
+
QontractReconcileIntegration,
|
29
|
+
)
|
30
|
+
from reconcile.utils.semver_helper import make_semver
|
31
|
+
from reconcile.utils.state import init_state
|
32
|
+
from reconcile.utils.unleash import get_feature_toggle_state
|
33
|
+
from reconcile.utils.vcs import VCS
|
34
|
+
|
35
|
+
QONTRACT_INTEGRATION = "aws-account-manager"
|
36
|
+
QONTRACT_INTEGRATION_VERSION = make_semver(1, 0, 0)
|
37
|
+
|
38
|
+
|
39
|
+
class AwsAccountMgmtIntegrationParams(PydanticRunParams):
|
40
|
+
account_name: str | None
|
41
|
+
flavor: str
|
42
|
+
organization_account_role: str = "OrganizationAccountAccessRole"
|
43
|
+
default_tags: dict[str, str] = {}
|
44
|
+
initial_user_name: str = "terraform"
|
45
|
+
initial_user_policy_arn: str = "arn:aws:iam::aws:policy/AdministratorAccess"
|
46
|
+
initial_user_secret_vault_path: str = (
|
47
|
+
"app-sre/creds/terraform/{account_name}/config"
|
48
|
+
)
|
49
|
+
account_tmpl_resource: str = "/aws-account-manager/account-tmpl.yml"
|
50
|
+
template_collection_root_path: str = "data/templating/collections/aws-account"
|
51
|
+
|
52
|
+
|
53
|
+
class AwsAccountMgmtIntegration(
|
54
|
+
QontractReconcileIntegration[AwsAccountMgmtIntegrationParams]
|
55
|
+
):
|
56
|
+
"""Create and manage AWS accounts."""
|
57
|
+
|
58
|
+
@property
|
59
|
+
def name(self) -> str:
|
60
|
+
return QONTRACT_INTEGRATION
|
61
|
+
|
62
|
+
def get_early_exit_desired_state(
|
63
|
+
self, query_func: Callable | None = None
|
64
|
+
) -> dict[str, Any]:
|
65
|
+
"""Return the desired state for early exit."""
|
66
|
+
if not query_func:
|
67
|
+
query_func = gql.get_api().query
|
68
|
+
payer_accounts, non_organization_accounts = self.get_aws_accounts(
|
69
|
+
query_func, account_name=self.params.account_name
|
70
|
+
)
|
71
|
+
return {
|
72
|
+
"payer_accounts": [account.dict() for account in payer_accounts],
|
73
|
+
"non_organization_accounts": [
|
74
|
+
account.dict() for account in non_organization_accounts
|
75
|
+
],
|
76
|
+
}
|
77
|
+
|
78
|
+
@staticmethod
|
79
|
+
def render_account_tmpl_file(
|
80
|
+
template: str, account_request: AWSAccountRequestV1, uid: str, settings: dict
|
81
|
+
) -> str:
|
82
|
+
for k, v in settings.items():
|
83
|
+
if not isinstance(v, str):
|
84
|
+
continue
|
85
|
+
# render string templates with account name
|
86
|
+
settings[k] = v.format(account_name=account_request.name)
|
87
|
+
tmpl = jinja2.Template(
|
88
|
+
template,
|
89
|
+
undefined=jinja2.StrictUndefined,
|
90
|
+
trim_blocks=True,
|
91
|
+
lstrip_blocks=True,
|
92
|
+
keep_trailing_newline=True,
|
93
|
+
).render({
|
94
|
+
"accountRequest": account_request.dict(by_alias=True),
|
95
|
+
"uid": uid,
|
96
|
+
"settings": settings,
|
97
|
+
"timestamp": int(datetime.now(tz=timezone.utc).timestamp()),
|
98
|
+
})
|
99
|
+
return tmpl
|
100
|
+
|
101
|
+
def get_aws_accounts(
|
102
|
+
self, query_func: Callable, account_name: str | None = None
|
103
|
+
) -> tuple[list[AWSAccountV1], list[AWSAccountV1]]:
|
104
|
+
"""Get all AWS payer and non-organization accounts."""
|
105
|
+
data = aws_accounts_query(query_func)
|
106
|
+
|
107
|
+
all_aws_accounts = [
|
108
|
+
account
|
109
|
+
for account in data.accounts or []
|
110
|
+
if integration_is_enabled(self.name, account)
|
111
|
+
and (not account_name or account.name == account_name)
|
112
|
+
]
|
113
|
+
for account in all_aws_accounts:
|
114
|
+
validate(account)
|
115
|
+
|
116
|
+
payer_accounts = [
|
117
|
+
account
|
118
|
+
for account in all_aws_accounts
|
119
|
+
if account.organization_accounts or account.account_requests
|
120
|
+
]
|
121
|
+
all_organization_account_names = {
|
122
|
+
org_account.name
|
123
|
+
for payer_account in payer_accounts
|
124
|
+
for org_account in payer_account.organization_accounts or []
|
125
|
+
}
|
126
|
+
|
127
|
+
non_organization_accounts = [
|
128
|
+
account
|
129
|
+
for account in all_aws_accounts
|
130
|
+
if account.name not in all_organization_account_names
|
131
|
+
]
|
132
|
+
return payer_accounts, non_organization_accounts
|
133
|
+
|
134
|
+
def save_access_key(self, account: str, access_key: AWSAccessKey) -> None:
|
135
|
+
"""Write the AWS secret to Vault."""
|
136
|
+
self.secret_reader.vault_client.write( # type: ignore[attr-defined] # mypy doesn't recognize the VaultClient.__new__ method
|
137
|
+
secret={
|
138
|
+
"data": {
|
139
|
+
"aws_access_key_id": access_key.access_key_id,
|
140
|
+
"aws_secret_access_key": access_key.secret_access_key,
|
141
|
+
},
|
142
|
+
"path": self.params.initial_user_secret_vault_path.format(
|
143
|
+
account_name=account
|
144
|
+
).strip("/"),
|
145
|
+
},
|
146
|
+
decode_base64=False,
|
147
|
+
)
|
148
|
+
|
149
|
+
def create_accounts(
|
150
|
+
self,
|
151
|
+
aws_api: AWSApi,
|
152
|
+
reconciler: AWSReconciler,
|
153
|
+
merge_request_manager: MergeRequestManager,
|
154
|
+
account_template: str,
|
155
|
+
account_requests: Iterable[AWSAccountRequestV1],
|
156
|
+
) -> None:
|
157
|
+
"""Create new AWS accounts."""
|
158
|
+
for account_request in account_requests:
|
159
|
+
if not (
|
160
|
+
uid := reconciler.create_organization_account(
|
161
|
+
aws_api=aws_api,
|
162
|
+
name=account_request.name,
|
163
|
+
email=account_request.account_owner.email,
|
164
|
+
)
|
165
|
+
):
|
166
|
+
continue
|
167
|
+
|
168
|
+
with aws_api.assume_role(
|
169
|
+
account_id=uid, role=self.params.organization_account_role
|
170
|
+
) as account_role_api:
|
171
|
+
if access_key := reconciler.create_iam_user(
|
172
|
+
aws_api=account_role_api,
|
173
|
+
name=account_request.name,
|
174
|
+
user_name=self.params.initial_user_name,
|
175
|
+
user_policy_arn=self.params.initial_user_policy_arn,
|
176
|
+
):
|
177
|
+
self.save_access_key(account_request.name, access_key)
|
178
|
+
|
179
|
+
merge_request_manager.create_account_file(
|
180
|
+
title=f"{account_request.name}: AWS account template collection file",
|
181
|
+
account_tmpl_file_path=f"{self.params.template_collection_root_path}/{account_request.name}.yml",
|
182
|
+
account_tmpl_file_content=self.render_account_tmpl_file(
|
183
|
+
template=account_template,
|
184
|
+
account_request=account_request,
|
185
|
+
uid=uid,
|
186
|
+
settings=self.params.dict(),
|
187
|
+
),
|
188
|
+
account_request_file_path=f"data/{account_request.path.strip('/')}",
|
189
|
+
)
|
190
|
+
|
191
|
+
def reconcile_organization_accounts(
|
192
|
+
self,
|
193
|
+
aws_api: AWSApi,
|
194
|
+
reconciler: AWSReconciler,
|
195
|
+
organization_accounts: Iterable[AWSAccountManaged],
|
196
|
+
) -> None:
|
197
|
+
"""Reconcile organization accounts."""
|
198
|
+
for account in organization_accounts:
|
199
|
+
assert account.organization # mypy
|
200
|
+
reconciler.reconcile_organization_account(
|
201
|
+
aws_api=aws_api,
|
202
|
+
name=account.name,
|
203
|
+
uid=account.uid,
|
204
|
+
ou=account.organization.ou,
|
205
|
+
tags=self.params.default_tags
|
206
|
+
| account.organization.tags
|
207
|
+
| {"app-interface-name": account.name},
|
208
|
+
enterprise_support=account.premium_support,
|
209
|
+
)
|
210
|
+
|
211
|
+
with aws_api.assume_role(
|
212
|
+
account_id=account.uid, role=self.params.organization_account_role
|
213
|
+
) as account_role_api:
|
214
|
+
self.reconcile_account(account_role_api, reconciler, account)
|
215
|
+
|
216
|
+
def reconcile_account(
|
217
|
+
self,
|
218
|
+
aws_api: AWSApi,
|
219
|
+
reconciler: AWSReconciler,
|
220
|
+
account: AWSAccountManaged,
|
221
|
+
create_initial_user: bool = True,
|
222
|
+
) -> None:
|
223
|
+
"""Reconcile an AWS account."""
|
224
|
+
reconciler.reconcile_account(
|
225
|
+
aws_api=aws_api,
|
226
|
+
name=account.name,
|
227
|
+
alias=account.alias,
|
228
|
+
quotas=[q for ql in account.quota_limits or [] for q in ql.quotas],
|
229
|
+
)
|
230
|
+
|
231
|
+
def reconcile_payer_accounts(
|
232
|
+
self,
|
233
|
+
reconciler: AWSReconciler,
|
234
|
+
merge_request_manager: MergeRequestManager,
|
235
|
+
default_state_path: str,
|
236
|
+
account_template: str,
|
237
|
+
payer_accounts: Iterable[AWSAccountV1],
|
238
|
+
) -> None:
|
239
|
+
"""Reconcile all payer accounts including account creation."""
|
240
|
+
# reconcile accounts within payer accounts, aka organization accounts
|
241
|
+
for payer_account in payer_accounts:
|
242
|
+
# having a state per flavor and payer account makes it easier in a shared environment
|
243
|
+
reconciler.state.state_path = default_state_path
|
244
|
+
reconciler.state.state_path += f"/{payer_account.name}"
|
245
|
+
aws_account_manager_role = (
|
246
|
+
payer_account.automation_role.aws_account_manager
|
247
|
+
if payer_account.automation_role
|
248
|
+
else None
|
249
|
+
)
|
250
|
+
if not aws_account_manager_role:
|
251
|
+
raise ValueError(
|
252
|
+
f"awsAccountManager role is not defined for account {payer_account.name}"
|
253
|
+
)
|
254
|
+
|
255
|
+
secret = self.secret_reader.read_all_secret(payer_account.automation_token)
|
256
|
+
with AWSApi(
|
257
|
+
AWSStaticCredentials(
|
258
|
+
access_key_id=secret["aws_access_key_id"],
|
259
|
+
secret_access_key=secret["aws_secret_access_key"],
|
260
|
+
region=payer_account.resources_default_region,
|
261
|
+
)
|
262
|
+
) as payer_account_aws_api:
|
263
|
+
with payer_account_aws_api.assume_role(
|
264
|
+
account_id=payer_account.uid,
|
265
|
+
role=aws_account_manager_role,
|
266
|
+
) as acct_manager_role_aws_api:
|
267
|
+
self.create_accounts(
|
268
|
+
acct_manager_role_aws_api,
|
269
|
+
reconciler,
|
270
|
+
merge_request_manager,
|
271
|
+
account_template,
|
272
|
+
payer_account.account_requests or [],
|
273
|
+
)
|
274
|
+
self.reconcile_organization_accounts(
|
275
|
+
acct_manager_role_aws_api,
|
276
|
+
reconciler,
|
277
|
+
payer_account.organization_accounts or [],
|
278
|
+
)
|
279
|
+
|
280
|
+
def reconcile_non_organization_accounts(
|
281
|
+
self,
|
282
|
+
reconciler: AWSReconciler,
|
283
|
+
default_state_path: str,
|
284
|
+
non_organization_accounts: Iterable[AWSAccountV1],
|
285
|
+
) -> None:
|
286
|
+
"""Reconcile accounts not part of an organization via a payer account (e.g. payer accounts themselves)"""
|
287
|
+
for account in non_organization_accounts:
|
288
|
+
reconciler.state.state_path = default_state_path
|
289
|
+
reconciler.state.state_path += f"/{account.name}"
|
290
|
+
secret = self.secret_reader.read_all_secret(account.automation_token)
|
291
|
+
with AWSApi(
|
292
|
+
AWSStaticCredentials(
|
293
|
+
access_key_id=secret["aws_access_key_id"],
|
294
|
+
secret_access_key=secret["aws_secret_access_key"],
|
295
|
+
region=account.resources_default_region,
|
296
|
+
)
|
297
|
+
) as account_aws_api:
|
298
|
+
self.reconcile_account(account_aws_api, reconciler, account)
|
299
|
+
|
300
|
+
@defer
|
301
|
+
def run(self, dry_run: bool, defer: Callable | None = None) -> None:
|
302
|
+
"""Run the integration."""
|
303
|
+
gql_api = gql.get_api()
|
304
|
+
payer_accounts, non_organization_accounts = self.get_aws_accounts(
|
305
|
+
gql_api.query, account_name=self.params.account_name
|
306
|
+
)
|
307
|
+
state = init_state(self.name, self.secret_reader)
|
308
|
+
default_state_path = f"state/{self.name}/{self.params.flavor}"
|
309
|
+
reconciler = AWSReconciler(state, dry_run)
|
310
|
+
vcs = VCS(
|
311
|
+
secret_reader=self.secret_reader,
|
312
|
+
github_orgs=get_github_orgs(),
|
313
|
+
gitlab_instances=get_gitlab_instances(),
|
314
|
+
app_interface_repo_url=get_app_interface_repo_url(),
|
315
|
+
dry_run=dry_run,
|
316
|
+
allow_deleting_mrs=False,
|
317
|
+
allow_opening_mrs=True,
|
318
|
+
)
|
319
|
+
if defer:
|
320
|
+
defer(vcs.cleanup)
|
321
|
+
merge_request_manager = MergeRequestManager(
|
322
|
+
vcs=vcs,
|
323
|
+
auto_merge_enabled=get_feature_toggle_state(
|
324
|
+
integration_name=f"{self.name}-allow-auto-merge-mrs", default=False
|
325
|
+
),
|
326
|
+
)
|
327
|
+
merge_request_manager.fetch_open_merge_requests()
|
328
|
+
account_template = gql_api.get_resource(path=self.params.account_tmpl_resource)[
|
329
|
+
"content"
|
330
|
+
]
|
331
|
+
self.reconcile_payer_accounts(
|
332
|
+
reconciler=reconciler,
|
333
|
+
merge_request_manager=merge_request_manager,
|
334
|
+
default_state_path=default_state_path,
|
335
|
+
account_template=account_template,
|
336
|
+
payer_accounts=payer_accounts,
|
337
|
+
)
|
338
|
+
self.reconcile_non_organization_accounts(
|
339
|
+
reconciler=reconciler,
|
340
|
+
default_state_path=default_state_path,
|
341
|
+
non_organization_accounts=non_organization_accounts,
|
342
|
+
)
|
@@ -0,0 +1,111 @@
|
|
1
|
+
import logging
|
2
|
+
|
3
|
+
from gitlab.exceptions import GitlabGetError
|
4
|
+
from gitlab.v4.objects import ProjectMergeRequest
|
5
|
+
|
6
|
+
from reconcile.utils.gitlab_api import GitLabApi
|
7
|
+
from reconcile.utils.mr.base import MergeRequestBase
|
8
|
+
from reconcile.utils.mr.labels import AUTO_MERGE
|
9
|
+
from reconcile.utils.vcs import VCS
|
10
|
+
|
11
|
+
AWS_MGR = "aws-account-manager"
|
12
|
+
|
13
|
+
|
14
|
+
class AwsAccountMR(MergeRequestBase):
|
15
|
+
name = "AwsAccount"
|
16
|
+
|
17
|
+
def __init__(
|
18
|
+
self,
|
19
|
+
title: str,
|
20
|
+
description: str,
|
21
|
+
account_tmpl_file_path: str,
|
22
|
+
account_tmpl_file_content: str,
|
23
|
+
account_request_file_path: str,
|
24
|
+
labels: list[str],
|
25
|
+
):
|
26
|
+
super().__init__()
|
27
|
+
self._title = title
|
28
|
+
self._description = description
|
29
|
+
self._account_tmpl_file_path = account_tmpl_file_path.lstrip("/")
|
30
|
+
self._account_tmpl_file_content = account_tmpl_file_content
|
31
|
+
self._account_request_file_path = account_request_file_path.lstrip("/")
|
32
|
+
self.labels = labels
|
33
|
+
|
34
|
+
@property
|
35
|
+
def title(self) -> str:
|
36
|
+
return self._title
|
37
|
+
|
38
|
+
@property
|
39
|
+
def description(self) -> str:
|
40
|
+
return self._description
|
41
|
+
|
42
|
+
def process(self, gitlab_cli: GitLabApi) -> None:
|
43
|
+
gitlab_cli.create_file(
|
44
|
+
branch_name=self.branch,
|
45
|
+
file_path=self._account_tmpl_file_path,
|
46
|
+
commit_message="add account template file",
|
47
|
+
content=self._account_tmpl_file_content,
|
48
|
+
)
|
49
|
+
gitlab_cli.delete_file(
|
50
|
+
branch_name=self.branch,
|
51
|
+
file_path=self._account_request_file_path,
|
52
|
+
commit_message="delete account request file",
|
53
|
+
)
|
54
|
+
|
55
|
+
|
56
|
+
class MergeRequestManager:
|
57
|
+
"""Manager for AWS account merge requests."""
|
58
|
+
|
59
|
+
def __init__(self, vcs: VCS, auto_merge_enabled: bool):
|
60
|
+
self._open_mrs: list[ProjectMergeRequest] = []
|
61
|
+
self._vcs = vcs
|
62
|
+
self._auto_merge_enabled = auto_merge_enabled
|
63
|
+
|
64
|
+
def _merge_request_already_exists(self, aws_acccount_file_path: str) -> bool:
|
65
|
+
return any(
|
66
|
+
aws_acccount_file_path == diff["new_path"]
|
67
|
+
for mr in self._open_mrs
|
68
|
+
for diff in mr.changes()["changes"]
|
69
|
+
)
|
70
|
+
|
71
|
+
def fetch_open_merge_requests(self) -> None:
|
72
|
+
all_open_mrs = self._vcs.get_open_app_interface_merge_requests()
|
73
|
+
self._open_mrs = [mr for mr in all_open_mrs if AWS_MGR in mr.labels]
|
74
|
+
|
75
|
+
def create_account_file(
|
76
|
+
self,
|
77
|
+
title: str,
|
78
|
+
account_tmpl_file_path: str,
|
79
|
+
account_tmpl_file_content: str,
|
80
|
+
account_request_file_path: str,
|
81
|
+
) -> None:
|
82
|
+
"""Open new MR (if not already present) for an AWS account and remove the account request file."""
|
83
|
+
if self._merge_request_already_exists(account_tmpl_file_path):
|
84
|
+
return None
|
85
|
+
|
86
|
+
try:
|
87
|
+
self._vcs.get_file_content_from_app_interface_master(
|
88
|
+
file_path=account_tmpl_file_path
|
89
|
+
)
|
90
|
+
# File already exists
|
91
|
+
raise FileExistsError(
|
92
|
+
f"File {account_tmpl_file_path} already exists in the repository"
|
93
|
+
)
|
94
|
+
except GitlabGetError as e:
|
95
|
+
if e.response_code != 404:
|
96
|
+
raise e
|
97
|
+
|
98
|
+
logging.info("Open MR for %s", account_tmpl_file_path)
|
99
|
+
mr_labels = [AWS_MGR]
|
100
|
+
if self._auto_merge_enabled:
|
101
|
+
mr_labels.append(AUTO_MERGE)
|
102
|
+
self._vcs.open_app_interface_merge_request(
|
103
|
+
mr=AwsAccountMR(
|
104
|
+
title=title,
|
105
|
+
description=f"New AWS account template collection file {account_tmpl_file_path}",
|
106
|
+
account_tmpl_file_path=account_tmpl_file_path,
|
107
|
+
account_tmpl_file_content=account_tmpl_file_content,
|
108
|
+
account_request_file_path=account_request_file_path,
|
109
|
+
labels=mr_labels,
|
110
|
+
)
|
111
|
+
)
|