qontract-reconcile 0.10.1rc479__py3-none-any.whl → 0.10.1rc488__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.1rc479
3
+ Version: 0.10.1rc488
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
@@ -22,8 +22,8 @@ Requires-Dist: ldap3 <2.10.0,>=2.9.1
22
22
  Requires-Dist: anymarkup <0.9.0,>=0.7.0
23
23
  Requires-Dist: python-gitlab <1.12.0,>=1.11.0
24
24
  Requires-Dist: semver ~=2.13
25
- Requires-Dist: boto3 ==1.26.139
26
- Requires-Dist: botocore ==1.29.139
25
+ Requires-Dist: boto3 ==1.34.15
26
+ Requires-Dist: botocore ==1.34.15
27
27
  Requires-Dist: urllib3 <1.27.0,>=1.25.4
28
28
  Requires-Dist: slack-sdk <4.0,>=3.10
29
29
  Requires-Dist: pypd <1.2.0,>=1.1.0
@@ -8,7 +8,7 @@ reconcile/aws_iam_password_reset.py,sha256=NwErtrqgBiXr7eGCAHdtGGOx0S7-4JnSc29Ie
8
8
  reconcile/aws_support_cases_sos.py,sha256=Jk6_XjDeJSYxgRGqcEAOcynt9qJF2r5HPIPcSKmoBv8,2974
9
9
  reconcile/blackbox_exporter_endpoint_monitoring.py,sha256=W_VJagnsJR1v5oqjlI3RJJE0_nhtJ0m81RS8zWA5u5c,3538
10
10
  reconcile/checkpoint.py,sha256=R2WFXUXLTB4sWMi4GeA4eegsuf_1-Q4vH8M0Toh3Ij4,5036
11
- reconcile/cli.py,sha256=2FIP7dyW9lxJwbJFyZou5noGIl0B0gEj9IKLNM0vk-I,81792
11
+ reconcile/cli.py,sha256=ZH37Rliz2M_yZIJf4kQ5PPmpr79Ex1frk712PTU9ae4,81820
12
12
  reconcile/closedbox_endpoint_monitoring_base.py,sha256=SMhkcQqprWvThrIJa3U_3uh5w1h-alleW1QnCJFY4Qw,4909
13
13
  reconcile/cluster_deployment_mapper.py,sha256=2Ah-nu-Mdig0pjuiZl_XLrmVAjYzFjORR3dMlCgkmw0,2352
14
14
  reconcile/dashdotdb_base.py,sha256=a5aPLVxyqPSbjdB0Ty-uliOtxwvEbbEljHJKxdK3-Zk,4813
@@ -68,7 +68,7 @@ reconcile/openshift_namespaces.py,sha256=DboMc6t0vXD54lL9ZP9P9fQnCRo2g_0z5FWubtW
68
68
  reconcile/openshift_network_policies.py,sha256=_qqv7yj17OM1J8KJPsFmzFZ85gzESJeBocC672z4_WU,4231
69
69
  reconcile/openshift_resourcequotas.py,sha256=yUi56PiOn3inMMfq_x_FEHmaW-reGipzoorjdar372g,2415
70
70
  reconcile/openshift_resources.py,sha256=kwsY5cko7udEKNlhL2oKiKv_5wzEw9wmmwROE016ng8,1400
71
- reconcile/openshift_resources_base.py,sha256=aMrblZnviFMiAPS5SZsYWmGIRA-l8XlHwtxPr_klui0,45728
71
+ reconcile/openshift_resources_base.py,sha256=ZhNVhotSbp2Ue3cMEqso-Cmug6vDV67lwX7pEvUpmbw,45787
72
72
  reconcile/openshift_rolebindings.py,sha256=0sEKajdqVuBSzlagyPbLxtNXQdI2vyabmbIRifs0des,6629
73
73
  reconcile/openshift_routes.py,sha256=fXvuPSjcjVw1X3j2EQvUAdbOepmIFdKk-M3qP8QzPiw,1075
74
74
  reconcile/openshift_saas_deploy.py,sha256=MySDWBQN2N3rv_B8ifWzRY5t2Afq3DEVkFECHMpW_Sk,11908
@@ -118,7 +118,7 @@ reconcile/vpc_peerings_validator.py,sha256=Kv22HJVlTW9l9GB2eXwjPWqdDbr_VuvQBNPtt
118
118
  reconcile/aus/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
119
119
  reconcile/aus/advanced_upgrade_service.py,sha256=DQ9FrphzhKsKaXbXeA2v3Qsg1ABXXEtPhD-R-FwUsBA,21007
120
120
  reconcile/aus/aus_label_source.py,sha256=X6FD4NYcX27llMUSmmBcCh-pG7U5FnBd0zl-0zwCj2U,4118
121
- reconcile/aus/base.py,sha256=aad4dkVOTZfeLLNmdDranLugt8Z3whQuae86yyYcgBU,44417
121
+ reconcile/aus/base.py,sha256=Qdktz7W7J9TWVCYJZHwyKoIf4sVI7W9o_WwzsOBxLfQ,44912
122
122
  reconcile/aus/cluster_version_data.py,sha256=j4UyEBi5mQuvPq5Lo7a_L_0blxvH790wJV07uAiikFU,7126
123
123
  reconcile/aus/metrics.py,sha256=fIew-rzi_kYuI5Gxn3-4bQVIr2oNibiKPyGnhB-xKU4,3538
124
124
  reconcile/aus/models.py,sha256=muBmbovxYtSNLFrTLVRcJYZ4dx6JLh8n3Q1-DjWJOHM,7098
@@ -131,11 +131,11 @@ reconcile/aws_ami_cleanup/integration.py,sha256=IW95cpMj2P5ffs-AxsR_TDQCJnYFBhLI
131
131
  reconcile/aws_cloudwatch_log_retention/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
132
132
  reconcile/aws_cloudwatch_log_retention/integration.py,sha256=0UcSZIrGvnGY4m9fj87oejIolIP_qTxtJInpmW9jrQ0,7772
133
133
  reconcile/aws_version_sync/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
134
- reconcile/aws_version_sync/integration.py,sha256=otccsHGCUyv0K2qhj8DmH9bXyKhHiYNF5wKQW3xJCz8,12260
134
+ reconcile/aws_version_sync/integration.py,sha256=dN_lZIeM5i0p6-yHYadGg65QgDs0nVWYJkPzU-KGwuo,15196
135
135
  reconcile/aws_version_sync/utils.py,sha256=sVv-48PKi2VITlqqvmpbjnFDOPeGqfKzgkpIszlmjL0,1708
136
136
  reconcile/aws_version_sync/merge_request_manager/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
137
137
  reconcile/aws_version_sync/merge_request_manager/merge_request.py,sha256=FeNcQaory5AXNVuVk-jJxPwtI4uSoURgkTH3rXAb2cc,6198
138
- reconcile/aws_version_sync/merge_request_manager/merge_request_manager.py,sha256=B2zc1l11Cyk_hWGEKV8FbHwncns67T2RPAjC1qpvM2U,8300
138
+ reconcile/aws_version_sync/merge_request_manager/merge_request_manager.py,sha256=6gYFzpdJ-KcftPtOP3nhe3F9N184-UGfo_f8MQrzGxg,8322
139
139
  reconcile/change_owners/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
140
140
  reconcile/change_owners/approver.py,sha256=GV8nwS-YJOJ8O-b9v3u60RSYECYH2EKAycjpoW6VmvU,2228
141
141
  reconcile/change_owners/bundle.py,sha256=dZ-GRCIgpSYwKzZD9rs64Ie09OptzDc8aR2X2msnt3Q,5363
@@ -175,7 +175,7 @@ reconcile/gql_definitions/aws_ami_cleanup/__init__.py,sha256=47DEQpj8HBSa-_TImW-
175
175
  reconcile/gql_definitions/aws_ami_cleanup/asg_namespaces.py,sha256=OJmeTu7uirLGAysZ3IQTtRXqMyL8noi_QZxPuWYxxmI,3678
176
176
  reconcile/gql_definitions/aws_version_sync/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
177
177
  reconcile/gql_definitions/aws_version_sync/clusters.py,sha256=2TOJOFxpTkZ2HKuqAGUo6tQkzOINSfO-bYDINVpJeL0,2295
178
- reconcile/gql_definitions/aws_version_sync/namespaces.py,sha256=AqjbVMm2ZfSb7GKPIiW7rMiNAIJGroHgV46vRTtAy-4,3886
178
+ reconcile/gql_definitions/aws_version_sync/namespaces.py,sha256=eBLyXlSjWdmEE-jY9M2Ocgk7JGi2OsWisTkjHLfgU_A,4311
179
179
  reconcile/gql_definitions/change_owners/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
180
180
  reconcile/gql_definitions/change_owners/queries/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
181
181
  reconcile/gql_definitions/change_owners/queries/change_types.py,sha256=C0Y3M9q7ZZRrawZ0QUOoCJ-EUDEqTUFhJf6fUlby4pY,4935
@@ -548,7 +548,7 @@ reconcile/utils/oc_map.py,sha256=nT69J5pdPeIDnIYjD9fwY6GkE3BMQCf-AF0rmHJuUNw,906
548
548
  reconcile/utils/ocm_base_client.py,sha256=UV-tQYrjg9kLYw2NE5nixrWlRz5bhyZsz8Q4WUhvBRA,6096
549
549
  reconcile/utils/openshift_resource.py,sha256=WYLetCNItODjoOVeYqbCaEx_Lv-ntsj6I97x-1o2yAk,25116
550
550
  reconcile/utils/openssl.py,sha256=QVvhzhpChq_4Daf_5wE1qeZJr4thg3DDjJPn4bOPD4E,365
551
- reconcile/utils/output.py,sha256=htcMXMe0y2dNnwu8MW8x0JZ5YBaEJRy5w4tR8yLF0OU,1738
551
+ reconcile/utils/output.py,sha256=4tObxIS_-EdJY_YCOOOmaYvHY40Q72IpYjWhjpJR1Ec,1856
552
552
  reconcile/utils/pagerduty_api.py,sha256=ckZZMn_ri7mUFsmMb8Lejuw5Lf_0-OWv8MbOzPc2zkQ,7567
553
553
  reconcile/utils/parse_dhms_duration.py,sha256=TONpLnec5gHeF7k815YNJpQyDjXhkxZIcv9s8ffbTSY,1840
554
554
  reconcile/utils/password_validator.py,sha256=XwuWg-8CPlcuG7dl_oQ1G1h2gSVSnfMym_VkuprpWVg,2183
@@ -567,7 +567,7 @@ reconcile/utils/state.py,sha256=-a3fOnGZnDRcTXw9Hg3QtGdKePGtnmoCkPeCt-5HgbE,1367
567
567
  reconcile/utils/structs.py,sha256=LcbLEg8WxfRqM6nW7NhcWN0YeqF7SQzxOgntmLs1SgY,352
568
568
  reconcile/utils/template.py,sha256=wTvRU4AnAV_o042tD4Mwls2dwWMuk7MKnde3MaCjaYg,331
569
569
  reconcile/utils/terraform_client.py,sha256=V7AMQOEU4tvUOT-LQN2cXLqcphD5L93PMGMfurQQyPY,31753
570
- reconcile/utils/terrascript_aws_client.py,sha256=zGVd9DLUKLucyCiYlNqt5O39kLTwEdgSGwKE86326DE,262689
570
+ reconcile/utils/terrascript_aws_client.py,sha256=67MwR-IOvuBJXUsKxokbWk4utyKe9EnhT688d6xJcDU,262986
571
571
  reconcile/utils/three_way_diff_strategy.py,sha256=nyqeQsLCoPI6e16k2CF3b9KNgQLU-rPf5RtfdUfVMwE,4468
572
572
  reconcile/utils/throughput.py,sha256=iP4UWAe2LVhDo69mPPmgo9nQ7RxHD6_GS8MZe-aSiuM,344
573
573
  reconcile/utils/unleash.py,sha256=1D56CsZfE3ShDtN3IErE1T2eeIwNmxhK-yYbCotJ99E,3601
@@ -599,7 +599,7 @@ reconcile/utils/mr/ocm_upgrade_scheduler_org_updates.py,sha256=RzEKRT_BhvB2ud9py
599
599
  reconcile/utils/mr/user_maintenance.py,sha256=cHPBn8zrReWLHalyk-EFdkFJe9zjVjRoZhT4t2zZfGE,3956
600
600
  reconcile/utils/ocm/__init__.py,sha256=5Pcf5cyftDWT5XRi1EzvNklOVxGplJi-v12HN3TDarc,57
601
601
  reconcile/utils/ocm/addons.py,sha256=8wVrt16i69KkXq1fQByVheSQRhrRELbuOHb7Tz9bKT0,1675
602
- reconcile/utils/ocm/base.py,sha256=iFGYzlXrGD-kKDat2IFkHFGO2m3nq7I0olQ5kbKvLww,11698
602
+ reconcile/utils/ocm/base.py,sha256=7jm37vMoIIaSnOZe02_6VZFIh0IDa-VVIqfR1FoddZQ,11872
603
603
  reconcile/utils/ocm/cluster_groups.py,sha256=F8oqVqN_4QUnGL0K61zZhoYIzJeP57EcmZpwmoV0mr4,1751
604
604
  reconcile/utils/ocm/clusters.py,sha256=Q6g5kGSNfxZUZ56LPFAYjOz8xJ2c6QG76V78GvyLxB0,7448
605
605
  reconcile/utils/ocm/identity_providers.py,sha256=dKed09N8iWmn39tI_MpwgVe47x23eLsknGbjMUxtwr4,2175
@@ -639,7 +639,7 @@ tools/app_interface_metrics_exporter.py,sha256=zkwkxdAUAxjdc-pzx2_oJXG25fo0Fnyd5
639
639
  tools/app_interface_reporter.py,sha256=upA-J-n-HXHKVDINRuMR7vTt-iJvQORKUVi9D3leQto,17738
640
640
  tools/glitchtip_access_reporter.py,sha256=oPBnk_YoDuljU3v0FaChzOwwnk4vap1xEE67QEjzdqs,2948
641
641
  tools/glitchtip_access_revalidation.py,sha256=8kbBJk04mkq28kWoRDDkfCGIF3GRg3pJrFAh1sW0dbk,2821
642
- tools/qontract_cli.py,sha256=-zYPdrQKoFHGziVrHSxnlam-xw8T1Us15ABQldIjD-c,99735
642
+ tools/qontract_cli.py,sha256=I6q8SuJ5vM7ZUJpJK_EGKZhuf1Wl2D-7LLpLH46JIv4,99697
643
643
  tools/sd_app_sre_alert_report.py,sha256=e9vAdyenUz2f5c8-z-5WY0wv-SJ9aePKDH2r4IwB6pc,5063
644
644
  tools/cli_commands/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
645
645
  tools/cli_commands/gpg_encrypt.py,sha256=w8hl4jIEWk5wKbEFN6fVEOwUJGmdlvOqYodW3XSN7mU,4978
@@ -650,8 +650,8 @@ tools/test/test_app_interface_metrics_exporter.py,sha256=SX7qL3D1SIRKFo95FoQztvf
650
650
  tools/test/test_qontract_cli.py,sha256=awwTHEc2DWlykuqGIYM0WOBoSL0KRnOraCLk3C7izis,1401
651
651
  tools/test/test_sd_app_sre_alert_report.py,sha256=v363r9zM7__0kR5K6mvJoGFcM9BvE33fWAayrqkpojA,2116
652
652
  tools/test/test_sre_checkpoints.py,sha256=SKqPPTl9ua0RFdSSofnoQX-JZE6dFLO3LRhfQzqtfh8,2607
653
- qontract_reconcile-0.10.1rc479.dist-info/METADATA,sha256=YHoqqZ7Hx-X-H_4gcWuf-q7XhNc1tYZhvqxrwEY17sY,2348
654
- qontract_reconcile-0.10.1rc479.dist-info/WHEEL,sha256=oiQVh_5PnQM0E3gPdiz09WCNmwiHDMaGer_elqB3coM,92
655
- qontract_reconcile-0.10.1rc479.dist-info/entry_points.txt,sha256=rTjAv28I_CHLM8ID3OPqMI_suoQ9s7tFbim4aYjn9kk,376
656
- qontract_reconcile-0.10.1rc479.dist-info/top_level.txt,sha256=l5ISPoXzt0SdR4jVdkfa7RPSKNc8zAHYWAnR-Dw8Ey8,24
657
- qontract_reconcile-0.10.1rc479.dist-info/RECORD,,
653
+ qontract_reconcile-0.10.1rc488.dist-info/METADATA,sha256=YzozXNG0W7es94FZSYSMBbZPR1DPRh0cv5gM4wIuLTo,2346
654
+ qontract_reconcile-0.10.1rc488.dist-info/WHEEL,sha256=oiQVh_5PnQM0E3gPdiz09WCNmwiHDMaGer_elqB3coM,92
655
+ qontract_reconcile-0.10.1rc488.dist-info/entry_points.txt,sha256=rTjAv28I_CHLM8ID3OPqMI_suoQ9s7tFbim4aYjn9kk,376
656
+ qontract_reconcile-0.10.1rc488.dist-info/top_level.txt,sha256=l5ISPoXzt0SdR4jVdkfa7RPSKNc8zAHYWAnR-Dw8Ey8,24
657
+ qontract_reconcile-0.10.1rc488.dist-info/RECORD,,
reconcile/aus/base.py CHANGED
@@ -163,7 +163,7 @@ class AdvancedUpgradeSchedulerBaseIntegration(
163
163
  self.process_upgrade_policies_in_org(dry_run, org_upgrade_spec)
164
164
  else:
165
165
  logging.debug(
166
- f"Skip org {org_name} in {ocm_env} because it defines no upgrade policies"
166
+ f"Skip org {org_upgrade_spec.org.org_id}/{org_name} in {ocm_env} because it defines no upgrade policies"
167
167
  )
168
168
 
169
169
  def get_upgrade_specs(self) -> dict[str, dict[str, OrganizationUpgradeSpec]]:
@@ -729,7 +729,7 @@ def gates_for_minor_version(
729
729
 
730
730
  def gates_to_agree(
731
731
  gates: list[OCMVersionGate],
732
- cluster_id: str,
732
+ cluster: OCMCluster,
733
733
  ocm_api: OCMBaseClient,
734
734
  ) -> list[OCMVersionGate]:
735
735
  """Check via OCM if a version is agreed
@@ -750,12 +750,18 @@ def gates_to_agree(
750
750
  # once we have proper and secure handling get gate agreements for STS clusters, we can use this condition:
751
751
  # `and (not g.sts_only or g.sts_only == cluster.is_sts())`
752
752
  if not g.sts_only
753
+ # consider only gates after the clusters current minor version
754
+ # OCM onls supports creating gate agreements for later minor versions than the
755
+ # current cluster version
756
+ and semver.match(
757
+ f"{cluster.minor_version()}.0", f"<{g.version_raw_id_prefix}.0"
758
+ )
753
759
  ]
754
760
 
755
761
  if applicable_gates:
756
762
  current_agreements = {
757
763
  agreement["version_gate"]["id"]
758
- for agreement in get_version_agreement(ocm_api, cluster_id)
764
+ for agreement in get_version_agreement(ocm_api, cluster.id)
759
765
  }
760
766
  return [gate for gate in applicable_gates if gate.id not in current_agreements]
761
767
  return []
@@ -806,18 +812,18 @@ def verify_current_should_skip(
806
812
  next_run = current.next_run
807
813
  if next_run and datetime.strptime(next_run, "%Y-%m-%dT%H:%M:%SZ") < now:
808
814
  logging.warning(
809
- f"[{desired.org.org_id}/{desired.cluster.name}] currently upgrading to blocked version '{version}'"
815
+ f"[{desired.org.org_id}/{desired.org.name}/{desired.cluster.name}] currently upgrading to blocked version '{version}'"
810
816
  )
811
817
  return True, None
812
818
  logging.debug(
813
- f"[{desired.org.org_id}/{desired.cluster.name}] found planned upgrade policy "
819
+ f"[{desired.org.org_id}/{desired.org.name}/{desired.cluster.name}] found planned upgrade policy "
814
820
  + f"with blocked version {version}"
815
821
  )
816
822
  return False, UpgradePolicyHandler(action="delete", policy=current)
817
823
 
818
824
  # else
819
825
  logging.debug(
820
- f"[{desired.org.org_id}/{desired.cluster.name}] skipping cluster with existing upgrade policy"
826
+ f"[{desired.org.org_id}/{desired.org.name}/{desired.cluster.name}] skipping cluster with existing upgrade policy"
821
827
  )
822
828
  return True, None
823
829
 
@@ -849,7 +855,7 @@ def verify_schedule_should_skip(
849
855
  within_upgrade_timeframe = next_schedule_in_seconds / 60 <= 10
850
856
  if not within_upgrade_timeframe:
851
857
  logging.debug(
852
- f"[{desired.org.org_id}/{desired.cluster.name}] skipping cluster with no upcoming upgrade"
858
+ f"[{desired.org.org_id}/{desired.org.name}/{desired.cluster.name}] skipping cluster with no upcoming upgrade"
853
859
  )
854
860
  return None
855
861
  return next_schedule.strftime("%Y-%m-%dT%H:%M:%SZ")
@@ -862,7 +868,7 @@ def verify_lock_should_skip(
862
868
  if any(lock in locked for lock in mutexes):
863
869
  locking = {lock: locked[lock] for lock in mutexes if lock in locked}
864
870
  logging.debug(
865
- f"[{desired.org.org_id}/{desired.cluster.name}] skipping cluster: locked out by {locking}"
871
+ f"[{desired.org.org_id}/{desired.org.name}/{desired.cluster.name}] skipping cluster: locked out by {locking}"
866
872
  )
867
873
  return True
868
874
  return False
@@ -1003,7 +1009,8 @@ def calculate_diff(
1003
1009
  else:
1004
1010
  target_version_prefix = get_version_prefix(version)
1005
1011
  minor_version_gates = gates_for_minor_version(
1006
- gates, target_version_prefix
1012
+ gates=gates,
1013
+ target_version_prefix=target_version_prefix,
1007
1014
  )
1008
1015
  # skipping upgrades when there are no version gates is a safety
1009
1016
  # precaution to prevent cluster upgrades being scheduled.
@@ -1013,7 +1020,7 @@ def calculate_diff(
1013
1020
  # this might change in the future - revisite for 4.16
1014
1021
  if not minor_version_gates:
1015
1022
  logging.debug(
1016
- f"[{spec.org.org_id}/{spec.cluster.name}] no gates found for {target_version_prefix}. "
1023
+ f"[{spec.org.org_id}/{spec.org.name}/{spec.cluster.name}] no gates found for {target_version_prefix}. "
1017
1024
  "Skip creation of an upgrade policy."
1018
1025
  )
1019
1026
  continue
@@ -1025,7 +1032,7 @@ def calculate_diff(
1025
1032
  GateAgreement(gate=g)
1026
1033
  for g in gates_to_agree(
1027
1034
  minor_version_gates,
1028
- spec.cluster.id,
1035
+ spec.cluster,
1029
1036
  ocm_api,
1030
1037
  )
1031
1038
  ],
@@ -29,6 +29,7 @@ from reconcile.gql_definitions.aws_version_sync.clusters import query as cluster
29
29
  from reconcile.gql_definitions.aws_version_sync.namespaces import (
30
30
  AWSAccountV1,
31
31
  NamespaceTerraformProviderResourceAWSV1,
32
+ NamespaceTerraformResourceElastiCacheV1,
32
33
  NamespaceTerraformResourceRDSV1,
33
34
  NamespaceV1,
34
35
  )
@@ -73,6 +74,8 @@ class ExternalResource(BaseModel):
73
74
  resource_identifier: str
74
75
  resource_engine: str
75
76
  resource_engine_version: semver.VersionInfo
77
+ # used to map AWS cache name to resource_identifier
78
+ redis_replication_group_id: str | None = None
76
79
 
77
80
  class Config:
78
81
  arbitrary_types_allowed = True
@@ -98,6 +101,8 @@ class ExternalResource(BaseModel):
98
101
 
99
102
  AwsExternalResources = list[ExternalResource]
100
103
  AppInterfaceExternalResources = list[ExternalResource]
104
+ UidAndReplicationGroupId = tuple[str, str]
105
+ ReplicationGroupIdToIdentifier = dict[UidAndReplicationGroupId, str]
101
106
 
102
107
 
103
108
  class AVSIntegration(QontractReconcileIntegration[AVSIntegrationParams]):
@@ -147,6 +152,7 @@ class AVSIntegration(QontractReconcileIntegration[AVSIntegrationParams]):
147
152
  self,
148
153
  clusters: Iterable[ClusterV1],
149
154
  timeout: int,
155
+ elasticache_replication_group_id_to_identifier: ReplicationGroupIdToIdentifier,
150
156
  prom_get_func: Callable = prom_get,
151
157
  ) -> list[ExternalResource]:
152
158
  metrics: list[ExternalResource] = []
@@ -158,6 +164,7 @@ class AVSIntegration(QontractReconcileIntegration[AVSIntegrationParams]):
158
164
  else None
159
165
  )
160
166
  try:
167
+ # RDS resources
161
168
  metrics += [
162
169
  ExternalResource(
163
170
  provider="aws",
@@ -170,8 +177,36 @@ class AVSIntegration(QontractReconcileIntegration[AVSIntegrationParams]):
170
177
  resource_engine_version=m["engine_version"],
171
178
  )
172
179
  for m in prom_get_func(
173
- cluster.prometheus_url,
174
- {"query": "aws_resources_exporter_rds_engineversion"},
180
+ url=cluster.prometheus_url,
181
+ params={"query": "aws_resources_exporter_rds_engineversion"},
182
+ token=token,
183
+ timeout=timeout,
184
+ )
185
+ ]
186
+ # ElastiCache resources
187
+ metrics += [
188
+ ExternalResource(
189
+ provider="aws",
190
+ provisioner=ExternalResourceProvisioner(
191
+ uid=m["aws_account_id"]
192
+ ),
193
+ resource_provider="elasticache",
194
+ # replication_group_id != resource_identifier!
195
+ resource_identifier=elasticache_replication_group_id_to_identifier.get(
196
+ (
197
+ m["aws_account_id"],
198
+ m["replication_group_id"],
199
+ ),
200
+ m["replication_group_id"],
201
+ ),
202
+ resource_engine=m["engine"],
203
+ resource_engine_version=m["engine_version"],
204
+ )
205
+ for m in prom_get_func(
206
+ url=cluster.prometheus_url,
207
+ params={
208
+ "query": "aws_resources_exporter_elasticache_redisversion"
209
+ },
175
210
  token=token,
176
211
  timeout=timeout,
177
212
  )
@@ -202,7 +237,11 @@ class AVSIntegration(QontractReconcileIntegration[AVSIntegrationParams]):
202
237
  continue
203
238
 
204
239
  # make mypy happy
205
- assert isinstance(resource, NamespaceTerraformResourceRDSV1)
240
+ assert isinstance(
241
+ resource,
242
+ NamespaceTerraformResourceElastiCacheV1
243
+ | NamespaceTerraformResourceRDSV1,
244
+ )
206
245
 
207
246
  values = {}
208
247
  # get/set the defaults file values from/to cache
@@ -214,6 +253,13 @@ class AVSIntegration(QontractReconcileIntegration[AVSIntegrationParams]):
214
253
  )
215
254
  _defaults_cache[resource.defaults] = values
216
255
  values = override_values(values, resource.overrides)
256
+ if resource.provider.lower() == "elasticache" and values[
257
+ "engine_version"
258
+ ].lower().endswith("x"):
259
+ # see https://gitlab.cee.redhat.com/service/app-interface/-/blob/master/docs/app-sre/sop/upgrade-redis-minor-version.md
260
+ # minor version not managed by app-interface anymore. skip it
261
+ continue
262
+
217
263
  external_resources.append(
218
264
  ExternalResource(
219
265
  namespace_file=ns.path,
@@ -225,6 +271,11 @@ class AVSIntegration(QontractReconcileIntegration[AVSIntegrationParams]):
225
271
  resource_identifier=resource.identifier,
226
272
  resource_engine=values["engine"],
227
273
  resource_engine_version=values["engine_version"],
274
+ redis_replication_group_id=values.get(
275
+ "replication_group_id", resource.identifier
276
+ )
277
+ if resource.provider.lower() == "elasticache"
278
+ else None,
228
279
  )
229
280
  )
230
281
 
@@ -268,7 +319,7 @@ class AVSIntegration(QontractReconcileIntegration[AVSIntegrationParams]):
268
319
  resource_provider=app_interface_resource.resource_provider,
269
320
  resource_identifier=app_interface_resource.resource_identifier,
270
321
  resource_engine=app_interface_resource.resource_engine,
271
- resource_engine_version=aws_resource.resource_engine_version,
322
+ resource_engine_version=str(aws_resource.resource_engine_version),
272
323
  )
273
324
 
274
325
  @defer
@@ -303,15 +354,24 @@ class AVSIntegration(QontractReconcileIntegration[AVSIntegrationParams]):
303
354
  gql_api.query,
304
355
  self.params.aws_resource_exporter_clusters,
305
356
  )
306
- external_resources_aws = self.get_aws_metrics(
307
- aws_resource_exporter_clusters,
308
- timeout=self.params.prometheus_timeout,
309
- )
310
357
  external_resources_app_interface = self.get_external_resource_specs(
311
358
  gql_api.get_resource,
312
359
  namespaces,
313
360
  supported_providers=self.params.supported_providers,
314
361
  )
362
+ external_resources_aws = self.get_aws_metrics(
363
+ aws_resource_exporter_clusters,
364
+ timeout=self.params.prometheus_timeout,
365
+ elasticache_replication_group_id_to_identifier={
366
+ (
367
+ external_resource.provisioner.uid,
368
+ external_resource.redis_replication_group_id,
369
+ ): external_resource.resource_identifier
370
+ for external_resource in external_resources_app_interface
371
+ if external_resource.redis_replication_group_id
372
+ },
373
+ )
374
+
315
375
  self.reconcile(
316
376
  merge_request_manager=merge_request_manager,
317
377
  external_resources_aws=external_resources_aws,
@@ -222,7 +222,7 @@ class MergeRequestManager:
222
222
  resource_engine_version=resource_engine_version,
223
223
  )
224
224
  title = self._renderer.render_title(resource_identifier=resource_identifier)
225
- logging.info("Open MR for %s", resource_identifier)
225
+ logging.info("Open MR for %s (%s)", resource_identifier, resource_engine)
226
226
  mr_labels = [AVS_LABEL]
227
227
  if self._auto_merge_enabled:
228
228
  mr_labels.append(AUTO_MERGE)
reconcile/cli.py CHANGED
@@ -1661,7 +1661,7 @@ def ldap_groups(ctx):
1661
1661
  )
1662
1662
  @click.option(
1663
1663
  "--supported-providers",
1664
- help="A comma seperated list of supported external resource providers to operator on. Default: rds",
1664
+ help="A comma seperated list of supported external resource providers to operator on. Default: rds, elasticache",
1665
1665
  required=False,
1666
1666
  envvar="AVS_SUPPORTED_PROVIDERS",
1667
1667
  )
@@ -1692,7 +1692,7 @@ def aws_version_sync(
1692
1692
  ),
1693
1693
  supported_providers=supported_providers.split(",")
1694
1694
  if supported_providers
1695
- else ["rds"],
1695
+ else ["rds", "elasticache"],
1696
1696
  clusters=clusters.split(",") if clusters else [],
1697
1697
  prometheus_timeout=int(prometheus_timeout)
1698
1698
  if prometheus_timeout
@@ -42,6 +42,11 @@ query AVSNamespaces {
42
42
  defaults
43
43
  overrides
44
44
  }
45
+ ... on NamespaceTerraformResourceElastiCache_v1 {
46
+ identifier
47
+ defaults
48
+ overrides
49
+ }
45
50
  }
46
51
  }
47
52
  }
@@ -87,8 +92,14 @@ class NamespaceTerraformResourceRDSV1(NamespaceTerraformResourceAWSV1):
87
92
  overrides: Optional[Json] = Field(..., alias="overrides")
88
93
 
89
94
 
95
+ class NamespaceTerraformResourceElastiCacheV1(NamespaceTerraformResourceAWSV1):
96
+ identifier: str = Field(..., alias="identifier")
97
+ defaults: str = Field(..., alias="defaults")
98
+ overrides: Optional[Json] = Field(..., alias="overrides")
99
+
100
+
90
101
  class NamespaceTerraformProviderResourceAWSV1(NamespaceExternalResourceV1):
91
- resources: list[Union[NamespaceTerraformResourceRDSV1, NamespaceTerraformResourceAWSV1]] = Field(..., alias="resources")
102
+ resources: list[Union[NamespaceTerraformResourceRDSV1, NamespaceTerraformResourceElastiCacheV1, NamespaceTerraformResourceAWSV1]] = Field(..., alias="resources")
92
103
 
93
104
 
94
105
  class DisableClusterAutomationsV1(ConfiguredBaseModel):
@@ -224,7 +224,6 @@ NAMESPACES_QUERY = """
224
224
  QONTRACT_INTEGRATION = "openshift_resources_base"
225
225
  QONTRACT_INTEGRATION_VERSION = make_semver(1, 9, 2)
226
226
  QONTRACT_BASE64_SUFFIX = "_qb64"
227
- APP_INT_BASE_URL = "https://gitlab.cee.redhat.com/service/app-interface"
228
227
  KUBERNETES_SECRET_DATA_KEY_RE = "^[-._a-zA-Z0-9]+$"
229
228
 
230
229
  _log_lock = Lock()
@@ -536,6 +535,9 @@ def fetch_provider_resource(
536
535
 
537
536
  if add_path_to_prom_rules:
538
537
  if body["kind"] == "PrometheusRule":
538
+ app_int_base_url = "https://gitlab.cee.redhat.com/service/app-interface"
539
+ if settings and "repoUrl" in settings:
540
+ app_int_base_url = settings["repoUrl"]
539
541
  try:
540
542
  groups = body["spec"]["groups"]
541
543
  for group in groups:
@@ -544,9 +546,8 @@ def fetch_provider_resource(
544
546
  annotations = rule.get("annotations")
545
547
  if not annotations:
546
548
  continue
547
- # TODO(mafriedm): make this better
548
549
  rule["annotations"]["html_url"] = (
549
- f"{APP_INT_BASE_URL}/blob/master/resources{path}"
550
+ f"{app_int_base_url}/blob/master/resources{path}"
550
551
  )
551
552
  except Exception:
552
553
  logging.warning(
@@ -8,6 +8,7 @@ from typing import (
8
8
  TypeVar,
9
9
  )
10
10
 
11
+ import semver
11
12
  from pydantic import (
12
13
  BaseModel,
13
14
  Field,
@@ -188,6 +189,10 @@ class OCMCluster(BaseModel):
188
189
 
189
190
  external_configuration: Optional[OCMExternalConfiguration]
190
191
 
192
+ def minor_version(self) -> str:
193
+ version_info = semver.parse(self.version.raw_id)
194
+ return f"{version_info['major']}.{version_info['minor']}"
195
+
191
196
  def available_upgrades(self) -> list[str]:
192
197
  return self.version.available_upgrades
193
198
 
reconcile/utils/output.py CHANGED
@@ -53,6 +53,8 @@ def format_table(content, columns, table_format="simple") -> str:
53
53
  cell = "<br />".join(cell)
54
54
  else:
55
55
  cell = "\n".join(cell)
56
+ if table_format == "github" and isinstance(cell, str):
57
+ cell = cell.replace("|", "&#124;")
56
58
  row_data.append(cell)
57
59
  table_data.append(row_data)
58
60
  return tabulate(table_data, headers=headers, tablefmt=table_format)
@@ -2620,7 +2620,7 @@ class TerrascriptClient: # pylint: disable=too-many-public-methods
2620
2620
 
2621
2621
  if "subscriptions" in common_values.keys():
2622
2622
  subscriptions = common_values.get("subscriptions")
2623
- for sub in subscriptions:
2623
+ for index, sub in enumerate(subscriptions):
2624
2624
  sub_values = {}
2625
2625
  sub_values["topic_arn"] = "${aws_sns_topic" + "." + identifier + ".arn}"
2626
2626
  protocol = sub["protocol"]
@@ -2630,7 +2630,11 @@ class TerrascriptClient: # pylint: disable=too-many-public-methods
2630
2630
  raise ValueError(msg)
2631
2631
  sub_values["protocol"] = protocol
2632
2632
  sub_values["endpoint"] = endpoint
2633
- sub_identifier = f"{identifier}_{protocol}_aws_sns_topic_subscription"
2633
+ # append suffix only in case there are multiple subscriptions
2634
+ suffix = f"_{index + 1}" if len(subscriptions) > 1 else ""
2635
+ sub_identifier = (
2636
+ f"{identifier}_{protocol}_aws_sns_topic_subscription{suffix}"
2637
+ )
2634
2638
  sub_tf_resource = aws_sns_topic_subscription(
2635
2639
  sub_identifier, **sub_values
2636
2640
  )
@@ -6018,6 +6022,7 @@ class TerrascriptClient: # pylint: disable=too-many-public-methods
6018
6022
  "${aws_cloudwatch_log_group.waf_log_group.arn}"
6019
6023
  ],
6020
6024
  resource_arn="${aws_wafv2_web_acl.api_waf.arn}",
6025
+ redacted_fields={"single_header": {"name": "authorization"}},
6021
6026
  )
6022
6027
  )
6023
6028
 
tools/qontract_cli.py CHANGED
@@ -2347,7 +2347,7 @@ def cluster_admin(ctx, org_name, cluster_name):
2347
2347
 
2348
2348
 
2349
2349
  @root.group()
2350
- @environ(["APP_INTERFACE_STATE_BUCKET", "APP_INTERFACE_STATE_BUCKET_ACCOUNT"])
2350
+ @environ(["APP_INTERFACE_STATE_BUCKET"])
2351
2351
  @click.pass_context
2352
2352
  def state(ctx):
2353
2353
  pass