qontract-reconcile 0.10.1rc1138__py3-none-any.whl → 0.10.1rc1140__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.1rc1138
3
+ Version: 0.10.1rc1140
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
@@ -194,11 +194,11 @@ reconcile/endpoints_discovery/merge_request_manager.py,sha256=wUMsumxv8RnWaRatta
194
194
  reconcile/external_resources/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
195
195
  reconcile/external_resources/aws.py,sha256=7W-6d-lXO6JGwaxtO1Uc3Lw0p8csJ1EVgz__O1YWUOc,4793
196
196
  reconcile/external_resources/factories.py,sha256=nhdTqf1WEfRfgd5-70KAUJVz0ZvZ19C3Pz7wmotSdrs,4857
197
- reconcile/external_resources/integration.py,sha256=Ne_D5eXtR2a4kFg2Us5h7Zc_WIRgOBxaKbVPic0c6uw,6619
197
+ reconcile/external_resources/integration.py,sha256=gBVO5dE8JyZ3xYcYik-MTIp_18oU7_hpYc_oztyfElQ,6753
198
198
  reconcile/external_resources/integration_secrets_sync.py,sha256=dX09O3r6KURziUYYfiki10orNjOGVma-XojhVqd0ww4,1667
199
199
  reconcile/external_resources/manager.py,sha256=cs6QEirz9EaLiuxybZ_1ugUn61vNWlKAC4NKouqpd5I,16148
200
200
  reconcile/external_resources/meta.py,sha256=noaytFzmShpzLA_ebGh7wuP45mOfHIOnnoUxivjDa1I,672
201
- reconcile/external_resources/metrics.py,sha256=jsN3IF78Su23d3Qp4BTKXkgZPN6AKjdS8sZFEXmYCek,863
201
+ reconcile/external_resources/metrics.py,sha256=ORZStR2Hg5dYSXpPnM_ZLz48d3VSbN_WY0TcNogvcWk,1003
202
202
  reconcile/external_resources/model.py,sha256=UuQgrnv-SSkvSEQQGeCE2IZkhXjLTCVkP_mw8zBZsIQ,8349
203
203
  reconcile/external_resources/reconciler.py,sha256=3KFmkHsN7YAwJUSBpN1Xd_D2zM9Ea5_c2uMGWsfruZo,9707
204
204
  reconcile/external_resources/secrets_sync.py,sha256=6n0oDPLjd9Ql0lf6zsr1AZw8A6EEe3yCzl20XodtgkE,16229
@@ -655,7 +655,7 @@ reconcile/unleash_feature_toggles/integration.py,sha256=nx7BhtzCsTfPbOp60vI5MkNw
655
655
  reconcile/utils/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
656
656
  reconcile/utils/aggregated_list.py,sha256=km0xadW0jO4G_CqZPsXmoBURQ8c90FaTu5x4X1K1cZs,3357
657
657
  reconcile/utils/amtool.py,sha256=ngtBuVPETH6oAy5RnKzvreVbjwQCaATS_PYYwBprzjQ,2288
658
- reconcile/utils/aws_api.py,sha256=1EC9l2cxMW2IvnhsXaIcq6iohZ4rRgqyHJsXPs9Vtes,67398
658
+ reconcile/utils/aws_api.py,sha256=Mp5-euZUfKfnVzgMZd3LoWbegm1OrNjzpP1A-n2EiF0,67640
659
659
  reconcile/utils/aws_helper.py,sha256=MDbv5jrNdqqJ5pfBxniGdJXBBO_EYc2_Uf2w9ZzeMNs,2854
660
660
  reconcile/utils/batches.py,sha256=TtEm64a8lWhFuNbUVpFEmXVdU2Q0sTBrP_I0Cjbgh7g,320
661
661
  reconcile/utils/binary.py,sha256=7MaAFBpzuBUTJ_aA6G6-eult_BPMVyiXbBLD0Y6F-DM,2301
@@ -674,7 +674,7 @@ reconcile/utils/exceptions.py,sha256=DwfnWUpVOotpP79RWZ2pycmG6nKCL00RBIeZLYkQPW4
674
674
  reconcile/utils/expiration.py,sha256=3JaXH4psksR7z262k7FmdyREjCLqm66OpVMEbcfdWRo,1213
675
675
  reconcile/utils/extended_early_exit.py,sha256=QSktrmfw37zSRMNk930tDbQsVeKxaPPPD43e79DGwZw,6754
676
676
  reconcile/utils/external_resource_spec.py,sha256=bhH_xneFwATdFumTPkiQmcVKYI0gcaWuqV6FpFdf_P0,7006
677
- reconcile/utils/external_resources.py,sha256=GC4wYuSXwk2ifr3aDEwnEiumaYqWhzgKK-hXp6pXemA,7516
677
+ reconcile/utils/external_resources.py,sha256=n3c7GyFb0o5sgQ67dAsGWV40ELHJH0vMKyJMkvMHEok,7659
678
678
  reconcile/utils/filtering.py,sha256=S4PbMHuFr3ED0P2Q_ea5CAaB7FimI62B-F5YTaKrphA,402
679
679
  reconcile/utils/git.py,sha256=wzVIYAeKlMGW538U1mkJWUI6h_mFRUY4lawh2AR8hw4,2345
680
680
  reconcile/utils/github_api.py,sha256=R8OvqyPdnRqvP-Efnv9RvIcbBlb4M0KC4RlbnJMD0Tg,2426
@@ -838,7 +838,7 @@ tools/app_interface_metrics_exporter.py,sha256=zkwkxdAUAxjdc-pzx2_oJXG25fo0Fnyd5
838
838
  tools/app_interface_reporter.py,sha256=oZPib4HPq0aZ2Zui1QGJGk6qQdfpeihujGDBnSdKyGE,17627
839
839
  tools/glitchtip_access_reporter.py,sha256=oPBnk_YoDuljU3v0FaChzOwwnk4vap1xEE67QEjzdqs,2948
840
840
  tools/glitchtip_access_revalidation.py,sha256=8kbBJk04mkq28kWoRDDkfCGIF3GRg3pJrFAh1sW0dbk,2821
841
- tools/qontract_cli.py,sha256=PV2tqOqpFqAQh1_ZGNNf7up4OTBSPZmpGjN7eHxlQ0s,136187
841
+ tools/qontract_cli.py,sha256=3k3cPk2ohxpOipQiV9gcsooYR0DZmnwr6xwiRZ9t2dk,139124
842
842
  tools/sd_app_sre_alert_report.py,sha256=e9vAdyenUz2f5c8-z-5WY0wv-SJ9aePKDH2r4IwB6pc,5063
843
843
  tools/template_validation.py,sha256=qpKYaTgk0GOPGa2Ct5_5sKdwIHtCAKIBGzsMPuJU5fw,3371
844
844
  tools/cli_commands/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
@@ -871,8 +871,8 @@ tools/test/test_qontract_cli.py,sha256=_D61RFGAN5x44CY1tYbouhlGXXABwYfxKSWSQx3Jr
871
871
  tools/test/test_saas_promotion_state.py,sha256=dy4kkSSAQ7bC0Xp2CociETGN-2aABEfL6FU5D9Jl00Y,6056
872
872
  tools/test/test_sd_app_sre_alert_report.py,sha256=v363r9zM7__0kR5K6mvJoGFcM9BvE33fWAayrqkpojA,2116
873
873
  tools/test/test_sre_checkpoints.py,sha256=SKqPPTl9ua0RFdSSofnoQX-JZE6dFLO3LRhfQzqtfh8,2607
874
- qontract_reconcile-0.10.1rc1138.dist-info/METADATA,sha256=MdJI51HuCxKlzbbtp160xeQgzXkWQTaujR5wEInmxZQ,2213
875
- qontract_reconcile-0.10.1rc1138.dist-info/WHEEL,sha256=eOLhNAGa2EW3wWl_TU484h7q1UNgy0JXjjoqKoxAAQc,92
876
- qontract_reconcile-0.10.1rc1138.dist-info/entry_points.txt,sha256=GKQqCl2j2X1BJQ69een6rHcR26PmnxnONLNOQB-nRjY,491
877
- qontract_reconcile-0.10.1rc1138.dist-info/top_level.txt,sha256=l5ISPoXzt0SdR4jVdkfa7RPSKNc8zAHYWAnR-Dw8Ey8,24
878
- qontract_reconcile-0.10.1rc1138.dist-info/RECORD,,
874
+ qontract_reconcile-0.10.1rc1140.dist-info/METADATA,sha256=ni5-15CF_5tDAFjlb69EjjEQDKwnhaP6RD6VMA4P2cg,2213
875
+ qontract_reconcile-0.10.1rc1140.dist-info/WHEEL,sha256=eOLhNAGa2EW3wWl_TU484h7q1UNgy0JXjjoqKoxAAQc,92
876
+ qontract_reconcile-0.10.1rc1140.dist-info/entry_points.txt,sha256=GKQqCl2j2X1BJQ69een6rHcR26PmnxnONLNOQB-nRjY,491
877
+ qontract_reconcile-0.10.1rc1140.dist-info/top_level.txt,sha256=l5ISPoXzt0SdR4jVdkfa7RPSKNc8zAHYWAnR-Dw8Ey8,24
878
+ qontract_reconcile-0.10.1rc1140.dist-info/RECORD,,
@@ -30,6 +30,7 @@ from reconcile.typed_queries.external_resources import (
30
30
  )
31
31
  from reconcile.utils import gql
32
32
  from reconcile.utils.aws_api_typed.api import AWSApi, AWSStaticCredentials
33
+ from reconcile.utils.external_resources import publish_metrics
33
34
  from reconcile.utils.jobcontroller.controller import (
34
35
  build_job_controller,
35
36
  )
@@ -167,6 +168,7 @@ def run(
167
168
  for k, e in er_mgr.errors.items():
168
169
  logging.error("ExternalResourceKey: %s, Error: %s" % (k, e))
169
170
  else:
171
+ publish_metrics(er_mgr.er_inventory, QONTRACT_INTEGRATION)
170
172
  er_mgr.handle_resources()
171
173
 
172
174
 
@@ -1,10 +1,15 @@
1
1
  from pydantic import BaseModel
2
2
 
3
- from reconcile.utils.metrics import CounterMetric, GaugeMetric
3
+ from reconcile.external_resources.meta import QONTRACT_INTEGRATION
4
+ from reconcile.utils.metrics import (
5
+ CounterMetric,
6
+ GaugeMetric,
7
+ normalize_integration_name,
8
+ )
4
9
 
5
10
 
6
11
  class ExternalResourcesBaseMetric(BaseModel):
7
- integration = "external_resources"
12
+ integration = normalize_integration_name(QONTRACT_INTEGRATION)
8
13
  provision_provider: str
9
14
  provisioner_name: str
10
15
  provider: str
@@ -1598,6 +1598,14 @@ class AWSApi: # pylint: disable=too-many-public-methods
1598
1598
  rds = self._account_rds_client(account_name, **optional_kwargs)
1599
1599
  return rds.describe_db_instances(DBInstanceIdentifier=db_instance_name)
1600
1600
 
1601
+ def describe_rds_recommendations(
1602
+ self,
1603
+ account_name: str,
1604
+ region_name: str | None = None,
1605
+ ):
1606
+ rds = self._account_rds_client(account_name, region_name)
1607
+ return rds.describe_db_recommendations()
1608
+
1601
1609
  def get_db_valid_upgrade_target(
1602
1610
  self,
1603
1611
  account_name: str,
@@ -8,6 +8,7 @@ from typing import Any
8
8
 
9
9
  import anymarkup
10
10
 
11
+ from reconcile.external_resources.model import ExternalResourcesInventory
11
12
  from reconcile.utils import (
12
13
  gql,
13
14
  metrics,
@@ -66,14 +67,17 @@ def managed_external_resources(namespace_info: Mapping[str, Any]) -> bool:
66
67
 
67
68
 
68
69
  def get_inventory_count_combinations(
69
- inventory: ExternalResourceSpecInventory,
70
+ inventory: ExternalResourceSpecInventory | ExternalResourcesInventory,
70
71
  ) -> Counter[tuple]:
71
72
  return Counter(
72
73
  (k.provision_provider, k.provisioner_name, k.provider) for k in inventory
73
74
  )
74
75
 
75
76
 
76
- def publish_metrics(inventory: ExternalResourceSpecInventory, integration: str) -> None:
77
+ def publish_metrics(
78
+ inventory: ExternalResourceSpecInventory | ExternalResourcesInventory,
79
+ integration: str,
80
+ ) -> None:
77
81
  count_combinations = get_inventory_count_combinations(inventory)
78
82
  integration_name = metrics.normalize_integration_name(integration)
79
83
  for combination, count in count_combinations.items():
tools/qontract_cli.py CHANGED
@@ -1749,6 +1749,85 @@ You can view the source of this Markdown to extract the JSON data.
1749
1749
  print_output(ctx.obj["options"], results, columns)
1750
1750
 
1751
1751
 
1752
+ @get.command
1753
+ @click.pass_context
1754
+ def rds_recommendations(ctx):
1755
+ IGNORED_STATUSES = ("resolved",)
1756
+ IGNORED_SEVERITIES = ("informational",)
1757
+
1758
+ settings = queries.get_app_interface_settings()
1759
+
1760
+ # Only check AWS accounts for which we have RDS resources defined
1761
+ targetted_accounts = []
1762
+ namespaces = queries.get_namespaces()
1763
+ for namespace_info in namespaces:
1764
+ if not managed_external_resources(namespace_info):
1765
+ continue
1766
+ for spec in get_external_resource_specs(namespace_info):
1767
+ if spec.provider == "rds":
1768
+ targetted_accounts.append(spec.provisioner_name)
1769
+
1770
+ accounts = [
1771
+ a for a in queries.get_aws_accounts() if a["name"] in targetted_accounts
1772
+ ]
1773
+ accounts.sort(key=lambda a: a["name"])
1774
+
1775
+ columns = [
1776
+ # 'RecommendationId',
1777
+ # 'TypeId',
1778
+ # 'ResourceArn',
1779
+ "ResourceName", # Non-AWS field
1780
+ "Severity",
1781
+ "Category",
1782
+ "Impact",
1783
+ "Status",
1784
+ "Detection",
1785
+ "Recommendation",
1786
+ "Description",
1787
+ # 'Source',
1788
+ # 'TypeDetection',
1789
+ # 'TypeRecommendation',
1790
+ # 'AdditionalInfo'
1791
+ ]
1792
+
1793
+ ctx.obj["options"]["sort"] = False
1794
+
1795
+ print("[TOC]")
1796
+ for account in accounts:
1797
+ account_name = account.get("name")
1798
+ account_deployment_regions = account.get("supportedDeploymentRegions")
1799
+ for region in account_deployment_regions or []:
1800
+ with AWSApi(1, [account], settings=settings, init_users=False) as aws:
1801
+ try:
1802
+ data = aws.describe_rds_recommendations(account_name, region)
1803
+ recommendations = data.get("DBRecommendations", [])
1804
+ except Exception as e:
1805
+ logging.error(f"Error describing RDS recommendations: {e}")
1806
+ continue
1807
+
1808
+ # Add field ResourceName infered from ResourceArn
1809
+ recommendations = [
1810
+ {**rec, "ResourceName": rec["ResourceArn"].split(":")[-1]}
1811
+ for rec in recommendations
1812
+ if rec.get("Status") not in IGNORED_STATUSES
1813
+ and rec.get("Severity") not in IGNORED_SEVERITIES
1814
+ ]
1815
+ # The Description field has \n that are causing issues with the markdown table
1816
+ recommendations = [
1817
+ {**rec, "Description": rec["Description"].replace("\n", " ")}
1818
+ for rec in recommendations
1819
+ ]
1820
+ # If we have no recommendations to show, skip
1821
+ if not recommendations:
1822
+ continue
1823
+ # Sort by ResourceName
1824
+ recommendations.sort(key=lambda r: r["ResourceName"])
1825
+
1826
+ print(f"# {account_name} - {region}")
1827
+ print("Note: Severity informational is not shown.")
1828
+ print_output(ctx.obj["options"], recommendations, columns)
1829
+
1830
+
1752
1831
  @get.command()
1753
1832
  @click.pass_context
1754
1833
  def products(ctx):