qontract-reconcile 0.10.1rc1062__py3-none-any.whl → 0.10.1rc1064__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.1rc1062
3
+ Version: 0.10.1rc1064
4
4
  Summary: Collection of tools to reconcile services with their desired state as defined in the app-interface DB.
5
5
  Home-page: https://github.com/app-sre/qontract-reconcile
6
6
  Author: Red Hat App-SRE Team
@@ -10,7 +10,7 @@ reconcile/aws_iam_password_reset.py,sha256=q96mwr2KeEQ5bpNniGlgIMZTxiuLSodcYfX-t
10
10
  reconcile/aws_support_cases_sos.py,sha256=hl_9L53yQYRQxKs3IWrd69Cc60XK067g_bJRM9B0udo,2975
11
11
  reconcile/blackbox_exporter_endpoint_monitoring.py,sha256=O1wFp52EyF538c6txaWBs8eMtUIy19gyHZ6VzJ6QXS8,3512
12
12
  reconcile/checkpoint.py,sha256=_JhMxrye5BgkRMxWYuf7Upli6XayPINKSsuo3ynHTRc,5010
13
- reconcile/cli.py,sha256=JCKHmEpzb1MnjtyBlyRPo27J2z03J1rrQ680Nlu7_m8,106890
13
+ reconcile/cli.py,sha256=PHoUlP86m-TdTpB3hyoHbOEDWR0_ZO-GSBQEGTmbTmc,107514
14
14
  reconcile/closedbox_endpoint_monitoring_base.py,sha256=rLh16BOlBOxTmJ8Si3wWyyEpmMlhh4Znx1Gc36qsmOc,4865
15
15
  reconcile/cluster_deployment_mapper.py,sha256=5gumAaRCcFXsabUJ1dnuUy9WrP_FEEM5JnOnE8ch9sE,2326
16
16
  reconcile/dashdotdb_base.py,sha256=l34QDu1G96_Ctnh7ZXdxXgSeCE93GQMdLAkWxmN6vDA,4775
@@ -161,9 +161,10 @@ reconcile/aws_version_sync/merge_request_manager/merge_request_manager.py,sha256
161
161
  reconcile/change_owners/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
162
162
  reconcile/change_owners/approver.py,sha256=Z3_11vnK2WNOxjEEXVDh0224-_-qbt9d6mBeVE-7fsc,2259
163
163
  reconcile/change_owners/bundle.py,sha256=ZIlXRo6Z2raeWSCUqYsexBdol-q-r9kWJs5O_YPaEYk,5273
164
+ reconcile/change_owners/change_log_tracking.py,sha256=zFpean5UB3-u5VQLJHzik8GmhBwYoNorR-l90-D1Pis,4160
164
165
  reconcile/change_owners/change_owners.py,sha256=0HRJhDm0oW3uYJFgzynqA1gA0lbhalhSkmWOiQmr-NM,17062
165
- reconcile/change_owners/change_types.py,sha256=PMDEWOAbRKjES3WDPGe_8bcrZgK3efLdVF22WvkUL3A,31938
166
- reconcile/change_owners/changes.py,sha256=CH38-hyOfbH6xFYWidw_LAniyPisKq9nGRQhUaT93do,17180
166
+ reconcile/change_owners/change_types.py,sha256=TjVtvmkU0s8w2NA6qvWQccB6PwlCrChFySlsHLYZjpE,32027
167
+ reconcile/change_owners/changes.py,sha256=pa3cNAL-Xawh700ARJJQjY0p09NA1J2329RcE0F0MHM,17224
167
168
  reconcile/change_owners/decision.py,sha256=iUJcIc_N_RqXIAY8D10RZqPMC2OinsHTMcqI6f6uylE,7606
168
169
  reconcile/change_owners/diff.py,sha256=0vyu29xCL24ZhUa7hqBni0NaxoCYRXLwvA-h8V23YQ4,9009
169
170
  reconcile/change_owners/implicit_ownership.py,sha256=6BehZvx4IjrphmOt_LLLk9_02Fl5BY5jd00Wuz_PBZk,4234
@@ -233,7 +234,7 @@ reconcile/gql_definitions/aws_version_sync/clusters.py,sha256=2TOJOFxpTkZ2HKuqAG
233
234
  reconcile/gql_definitions/aws_version_sync/namespaces.py,sha256=eBLyXlSjWdmEE-jY9M2Ocgk7JGi2OsWisTkjHLfgU_A,4311
234
235
  reconcile/gql_definitions/change_owners/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
235
236
  reconcile/gql_definitions/change_owners/queries/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
236
- reconcile/gql_definitions/change_owners/queries/change_types.py,sha256=9S2YRNnSAvutjzuubZIQQe35yd8V2rGKvWSUI6yl11Q,5017
237
+ reconcile/gql_definitions/change_owners/queries/change_types.py,sha256=SjpKbLWmLh8iwPwfulbjHpH-M1hqBG1TOu_pZazox0Q,5084
237
238
  reconcile/gql_definitions/change_owners/queries/self_service_roles.py,sha256=BcTQvnefPiShG90ajU_l2V6HUYSEXgdAzgiwY89vQew,4790
238
239
  reconcile/gql_definitions/cluster_auth_rhidp/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
239
240
  reconcile/gql_definitions/cluster_auth_rhidp/clusters.py,sha256=Pp9P3Q30Be3szcVqOEOtPfYUNiGTq1xc5Juz-ApMMw0,3283
@@ -836,7 +837,7 @@ tools/app_interface_metrics_exporter.py,sha256=zkwkxdAUAxjdc-pzx2_oJXG25fo0Fnyd5
836
837
  tools/app_interface_reporter.py,sha256=oZPib4HPq0aZ2Zui1QGJGk6qQdfpeihujGDBnSdKyGE,17627
837
838
  tools/glitchtip_access_reporter.py,sha256=oPBnk_YoDuljU3v0FaChzOwwnk4vap1xEE67QEjzdqs,2948
838
839
  tools/glitchtip_access_revalidation.py,sha256=8kbBJk04mkq28kWoRDDkfCGIF3GRg3pJrFAh1sW0dbk,2821
839
- tools/qontract_cli.py,sha256=LDQgwmAg3kzgtPuODqhGgPeVfD_9g8dgiOOVRsJ3YRA,128234
840
+ tools/qontract_cli.py,sha256=5RQemctfItbH3S4TPjX2AmqmS1vCoI5j9zqhxFRJB44,129495
840
841
  tools/sd_app_sre_alert_report.py,sha256=e9vAdyenUz2f5c8-z-5WY0wv-SJ9aePKDH2r4IwB6pc,5063
841
842
  tools/template_validation.py,sha256=qpKYaTgk0GOPGa2Ct5_5sKdwIHtCAKIBGzsMPuJU5fw,3371
842
843
  tools/cli_commands/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
@@ -867,8 +868,8 @@ tools/test/test_qontract_cli.py,sha256=_D61RFGAN5x44CY1tYbouhlGXXABwYfxKSWSQx3Jr
867
868
  tools/test/test_saas_promotion_state.py,sha256=dy4kkSSAQ7bC0Xp2CociETGN-2aABEfL6FU5D9Jl00Y,6056
868
869
  tools/test/test_sd_app_sre_alert_report.py,sha256=v363r9zM7__0kR5K6mvJoGFcM9BvE33fWAayrqkpojA,2116
869
870
  tools/test/test_sre_checkpoints.py,sha256=SKqPPTl9ua0RFdSSofnoQX-JZE6dFLO3LRhfQzqtfh8,2607
870
- qontract_reconcile-0.10.1rc1062.dist-info/METADATA,sha256=LJpDWm58gsS5Jn18c20IY4zpZ7SxiL0lPZ62xXd8wSE,2213
871
- qontract_reconcile-0.10.1rc1062.dist-info/WHEEL,sha256=eOLhNAGa2EW3wWl_TU484h7q1UNgy0JXjjoqKoxAAQc,92
872
- qontract_reconcile-0.10.1rc1062.dist-info/entry_points.txt,sha256=GKQqCl2j2X1BJQ69een6rHcR26PmnxnONLNOQB-nRjY,491
873
- qontract_reconcile-0.10.1rc1062.dist-info/top_level.txt,sha256=l5ISPoXzt0SdR4jVdkfa7RPSKNc8zAHYWAnR-Dw8Ey8,24
874
- qontract_reconcile-0.10.1rc1062.dist-info/RECORD,,
871
+ qontract_reconcile-0.10.1rc1064.dist-info/METADATA,sha256=zhmq1Fv6WMFLlItqLsy0MfDZKKWBqgzndiykQ9TT4ic,2213
872
+ qontract_reconcile-0.10.1rc1064.dist-info/WHEEL,sha256=eOLhNAGa2EW3wWl_TU484h7q1UNgy0JXjjoqKoxAAQc,92
873
+ qontract_reconcile-0.10.1rc1064.dist-info/entry_points.txt,sha256=GKQqCl2j2X1BJQ69een6rHcR26PmnxnONLNOQB-nRjY,491
874
+ qontract_reconcile-0.10.1rc1064.dist-info/top_level.txt,sha256=l5ISPoXzt0SdR4jVdkfa7RPSKNc8zAHYWAnR-Dw8Ey8,24
875
+ qontract_reconcile-0.10.1rc1064.dist-info/RECORD,,
@@ -0,0 +1,119 @@
1
+ import logging
2
+ from collections.abc import Callable
3
+ from dataclasses import asdict, dataclass, field
4
+
5
+ from reconcile.change_owners.bundle import (
6
+ NoOpFileDiffResolver,
7
+ QontractServerDiff,
8
+ )
9
+ from reconcile.change_owners.change_owners import fetch_change_type_processors
10
+ from reconcile.change_owners.change_types import ChangeTypeContext
11
+ from reconcile.change_owners.changes import aggregate_file_moves, parse_bundle_changes
12
+ from reconcile.utils import gql
13
+ from reconcile.utils.defer import defer
14
+ from reconcile.utils.runtime.integration import (
15
+ PydanticRunParams,
16
+ QontractReconcileIntegration,
17
+ )
18
+ from reconcile.utils.state import init_state
19
+
20
+ QONTRACT_INTEGRATION = "change-log-tracking"
21
+ BUNDLE_DIFFS_OBJ = "bundle-diffs.json"
22
+
23
+
24
+ @dataclass
25
+ class ChangeLogItem:
26
+ commit: str
27
+ change_types: list[str] = field(default_factory=list)
28
+ error: bool = False
29
+
30
+
31
+ @dataclass
32
+ class ChangeLog:
33
+ items: list[ChangeLogItem] = field(default_factory=list)
34
+
35
+
36
+ class ChangeLogIntegrationParams(PydanticRunParams):
37
+ process_existing: bool = False
38
+
39
+
40
+ class ChangeLogIntegration(QontractReconcileIntegration[ChangeLogIntegrationParams]):
41
+ @property
42
+ def name(self) -> str:
43
+ return QONTRACT_INTEGRATION
44
+
45
+ @defer
46
+ def run(
47
+ self,
48
+ dry_run: bool,
49
+ defer: Callable | None = None,
50
+ ) -> None:
51
+ change_type_processors = [
52
+ ctp
53
+ for ctp in fetch_change_type_processors(
54
+ gql.get_api(), NoOpFileDiffResolver()
55
+ )
56
+ if ctp.labels and "change_log_tracking" in ctp.labels
57
+ ]
58
+
59
+ integration_state = init_state(
60
+ integration=self.name,
61
+ )
62
+ if defer:
63
+ defer(integration_state.cleanup)
64
+ diff_state = init_state(
65
+ integration=self.name,
66
+ )
67
+ if defer:
68
+ defer(diff_state.cleanup)
69
+ diff_state.state_path = "bundle-archive/diff"
70
+
71
+ if not self.params.process_existing:
72
+ existing_change_log = ChangeLog(**integration_state.get(BUNDLE_DIFFS_OBJ))
73
+ existing_change_log_items = [
74
+ ChangeLogItem(**i) # type: ignore[arg-type]
75
+ for i in existing_change_log.items
76
+ ]
77
+ change_log = ChangeLog()
78
+ for item in diff_state.ls():
79
+ key = item.lstrip("/")
80
+ commit = key.rstrip(".json")
81
+ if not self.params.process_existing:
82
+ existing_change_log_item = next(
83
+ (i for i in existing_change_log_items if i.commit == commit), None
84
+ )
85
+ if existing_change_log_item:
86
+ logging.debug(f"Found existing commit {commit}")
87
+ change_log.items.append(existing_change_log_item)
88
+ continue
89
+
90
+ logging.info(f"Processing commit {commit}")
91
+ change_log_item = ChangeLogItem(
92
+ commit=commit,
93
+ )
94
+ change_log.items.append(change_log_item)
95
+ obj = diff_state.get(key, None)
96
+ if not obj:
97
+ logging.error(f"Error processing commit {commit}")
98
+ change_log_item.error = True
99
+ continue
100
+ diff = QontractServerDiff(**obj)
101
+ changes = aggregate_file_moves(parse_bundle_changes(diff))
102
+ for change in changes:
103
+ logging.debug(f"Processing change {change}")
104
+ for ctp in change_type_processors:
105
+ logging.info(f"Processing change type {ctp.name}")
106
+ ctx = ChangeTypeContext(
107
+ change_type_processor=ctp,
108
+ context="",
109
+ origin="",
110
+ context_file=change.fileref,
111
+ approvers=[],
112
+ )
113
+ covered_diffs = change.cover_changes(ctx)
114
+ if covered_diffs:
115
+ if ctp.name not in change_log_item.change_types:
116
+ change_log_item.change_types.append(ctp.name)
117
+
118
+ if not dry_run:
119
+ integration_state.add(BUNDLE_DIFFS_OBJ, asdict(change_log), force=True)
@@ -22,6 +22,7 @@ import jinja2
22
22
  import jinja2.meta
23
23
  import jsonpath_ng
24
24
  import networkx
25
+ from pydantic import Json
25
26
 
26
27
  from reconcile.change_owners.approver import (
27
28
  Approver,
@@ -474,6 +475,7 @@ class ChangeTypeProcessor:
474
475
  """
475
476
 
476
477
  name: str
478
+ labels: Json | None
477
479
  description: str
478
480
  priority: ChangeTypePriority
479
481
  context_type: BundleFileType
@@ -715,6 +717,7 @@ def init_change_type_processors(
715
717
  # build raw change-type-processor
716
718
  processors[change_type.name] = ChangeTypeProcessor(
717
719
  name=change_type.name,
720
+ labels=change_type.labels,
718
721
  description=change_type.description,
719
722
  priority=ChangeTypePriority(change_type.priority),
720
723
  context_type=BundleFileType[change_type.context_type.upper()],
@@ -102,7 +102,7 @@ class BundleFileChange:
102
102
  def is_file_creation(self) -> bool:
103
103
  return self.old is None and self.new is not None
104
104
 
105
- def cover_changes(self, change_type_context: ChangeTypeContext) -> None:
105
+ def cover_changes(self, change_type_context: ChangeTypeContext) -> dict[str, Diff]:
106
106
  """
107
107
  Figure out if a ChangeTypeV1 covers detected changes within the BundleFile.
108
108
  Base idea:
@@ -121,7 +121,7 @@ class BundleFileChange:
121
121
  # as a source of approvers
122
122
  if self.metadata_only_change and not self.diffs:
123
123
  self._metadata_only_diff_coverage().coverage.append(change_type_context)
124
- return
124
+ return {}
125
125
 
126
126
  covered_diffs = {}
127
127
  # observe the new state for added fields or list items or entire object sutrees
@@ -139,6 +139,8 @@ class BundleFileChange:
139
139
  )
140
140
  )
141
141
 
142
+ return covered_diffs
143
+
142
144
  def _cover_changes_for_diffs(
143
145
  self,
144
146
  diffs: list[DiffCoverage],
reconcile/cli.py CHANGED
@@ -3557,6 +3557,29 @@ def change_owners(
3557
3557
  )
3558
3558
 
3559
3559
 
3560
+ @integration.command(short_help="Analyze bundle diffs by change types.")
3561
+ @click.option(
3562
+ "--process-existing/--no-process-existing",
3563
+ default=False,
3564
+ help="wait for pending/running pipelines before acting.",
3565
+ )
3566
+ @click.pass_context
3567
+ def change_log_tracking(ctx, process_existing):
3568
+ from reconcile.change_owners.change_log_tracking import (
3569
+ ChangeLogIntegration,
3570
+ ChangeLogIntegrationParams,
3571
+ )
3572
+
3573
+ run_class_integration(
3574
+ ChangeLogIntegration(
3575
+ ChangeLogIntegrationParams(
3576
+ process_existing=process_existing,
3577
+ )
3578
+ ),
3579
+ ctx=ctx.obj,
3580
+ )
3581
+
3582
+
3560
3583
  @integration.command(
3561
3584
  short_help="Configure and enforce glitchtip instance configuration."
3562
3585
  )
@@ -22,6 +22,7 @@ DEFINITION = """
22
22
  query ChangeTypes($name: String) {
23
23
  change_types: change_types_v1(name: $name) {
24
24
  name
25
+ labels
25
26
  description
26
27
  priority
27
28
  contextType
@@ -117,6 +118,7 @@ class ChangeTypeV1_ChangeTypeV1(ConfiguredBaseModel):
117
118
 
118
119
  class ChangeTypeV1(ConfiguredBaseModel):
119
120
  name: str = Field(..., alias="name")
121
+ labels: Optional[Json] = Field(..., alias="labels")
120
122
  description: str = Field(..., alias="description")
121
123
  priority: str = Field(..., alias="priority")
122
124
  context_type: str = Field(..., alias="contextType")
tools/qontract_cli.py CHANGED
@@ -47,6 +47,12 @@ from reconcile.aus.base import (
47
47
  )
48
48
  from reconcile.aus.models import OrganizationUpgradeSpec
49
49
  from reconcile.change_owners.bundle import NoOpFileDiffResolver
50
+ from reconcile.change_owners.change_log_tracking import (
51
+ ChangeLog,
52
+ ChangeLogIntegration,
53
+ ChangeLogIntegrationParams,
54
+ ChangeLogItem,
55
+ )
50
56
  from reconcile.change_owners.change_owners import (
51
57
  fetch_change_type_processors,
52
58
  fetch_self_service_roles,
@@ -85,6 +91,7 @@ from reconcile.jenkins_job_builder import init_jjb
85
91
  from reconcile.slack_base import slackapi_from_queries
86
92
  from reconcile.status_board import StatusBoardExporterIntegration
87
93
  from reconcile.typed_queries.alerting_services_settings import get_alerting_services
94
+ from reconcile.typed_queries.app_interface_repo_url import get_app_interface_repo_url
88
95
  from reconcile.typed_queries.app_interface_vault_settings import (
89
96
  get_app_interface_vault_settings,
90
97
  )
@@ -2857,6 +2864,35 @@ def container_image_details(ctx):
2857
2864
  print_output(ctx.obj["options"], data, columns)
2858
2865
 
2859
2866
 
2867
+ @get.command
2868
+ @click.pass_context
2869
+ def change_log_tracking(ctx):
2870
+ repo_url = get_app_interface_repo_url()
2871
+ change_types = fetch_change_type_processors(gql.get_api(), NoOpFileDiffResolver())
2872
+ state = init_state(
2873
+ integration=ChangeLogIntegration(ChangeLogIntegrationParams()).name
2874
+ )
2875
+ change_log = ChangeLog(**state.get("bundle-diffs.json"))
2876
+ data: list[dict[str, str]] = []
2877
+ for item in change_log.items:
2878
+ change_log_item = ChangeLogItem(**item)
2879
+ commit = change_log_item.commit
2880
+ covered_change_types_descriptions = [
2881
+ ct.description
2882
+ for ct in change_types
2883
+ if ct.name in change_log_item.change_types
2884
+ ]
2885
+ item = {
2886
+ "commit": f"[{commit}]({repo_url}/commit/{commit})",
2887
+ "changes": ", ".join(covered_change_types_descriptions),
2888
+ "error": change_log_item.error,
2889
+ }
2890
+ data.append(item)
2891
+
2892
+ columns = ["commit", "changes", "error"]
2893
+ print_output(ctx.obj["options"], data, columns)
2894
+
2895
+
2860
2896
  @root.group(name="set")
2861
2897
  @output
2862
2898
  @click.pass_context