qontract-reconcile 0.10.2.dev265__py3-none-any.whl → 0.10.2.dev267__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.4
2
2
  Name: qontract-reconcile
3
- Version: 0.10.2.dev265
3
+ Version: 0.10.2.dev267
4
4
  Summary: Collection of tools to reconcile services with their desired state as defined in the app-interface DB.
5
5
  Project-URL: homepage, https://github.com/app-sre/qontract-reconcile
6
6
  Project-URL: repository, https://github.com/app-sre/qontract-reconcile
@@ -65,7 +65,7 @@ reconcile/openshift_network_policies.py,sha256=p81ShFK1WSEGiWHVURopDpg8YvtA3RE3O
65
65
  reconcile/openshift_prometheus_rules.py,sha256=onowXab248zmHH8SbYDTc1W1bl7JiqRFU1xdTkZyLFg,1332
66
66
  reconcile/openshift_resourcequotas.py,sha256=yUi56PiOn3inMMfq_x_FEHmaW-reGipzoorjdar372g,2415
67
67
  reconcile/openshift_resources.py,sha256=I2nO_C37mG3rfyGrd4cGwN3mVseVGuTAHAyhFzLyqF4,1518
68
- reconcile/openshift_resources_base.py,sha256=x_uikuObZWv5ZfLdC2Yvm3MyXDK5Qpz5XFWOP6PDovo,42910
68
+ reconcile/openshift_resources_base.py,sha256=Z5l_EzH5nDuKPF1dkBiyo6oHa-jAgGU9z1OGH77o_2g,42929
69
69
  reconcile/openshift_rhcs_certs.py,sha256=d-72kJwgHMtdvs1F5uv-jC2lWmOyN2Wp1pQMbQ43FbU,10240
70
70
  reconcile/openshift_rolebindings.py,sha256=9mlJ2FjWUoH-rsjtasreA_hV-K5Z_YR00qR_RR60OZM,6555
71
71
  reconcile/openshift_routes.py,sha256=fXvuPSjcjVw1X3j2EQvUAdbOepmIFdKk-M3qP8QzPiw,1075
@@ -456,7 +456,7 @@ reconcile/oum/models.py,sha256=teH0bJTCMTzbdbYD9CU4yXDuMr34ceLcM0KuoIPU8gI,1712
456
456
  reconcile/oum/providers.py,sha256=lfG6d7YV-A4Lte45EMv1Gx4A346piJ_jAkrU5AHJZ_g,1834
457
457
  reconcile/oum/standalone.py,sha256=EN5y1S-3DwUZYzSRqRMtf63mI2slvBHKiU9zOTjYvWM,7334
458
458
  reconcile/prometheus_rules_tester/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
459
- reconcile/prometheus_rules_tester/integration.py,sha256=8lXZSaNqZAemulVNDxanrxwl7ZGfUxtwfptJlMPX8ac,9308
459
+ reconcile/prometheus_rules_tester/integration.py,sha256=TWsVBUeRLH3lUCf47sWWVgq4Rpkrq0i_eu2vkqQniP0,9619
460
460
  reconcile/rhidp/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
461
461
  reconcile/rhidp/common.py,sha256=rqifpncnE5--sYSx7ERMGDUjNHRI-SZkpuzwepZSps4,6766
462
462
  reconcile/rhidp/metrics.py,sha256=Yp0GtpjhieEdru0qkG3osBTJiKUzg6CAjwPoFTQDnCg,417
@@ -640,7 +640,7 @@ reconcile/utils/parse_dhms_duration.py,sha256=TONpLnec5gHeF7k815YNJpQyDjXhkxZIcv
640
640
  reconcile/utils/password_validator.py,sha256=XwuWg-8CPlcuG7dl_oQ1G1h2gSVSnfMym_VkuprpWVg,2183
641
641
  reconcile/utils/prometheus.py,sha256=Ad0rwLbxRuuYjHwkwJloHEdK0bvy42h-p-HIT1DhDhs,3832
642
642
  reconcile/utils/promotion_state.py,sha256=McSgGj3oog83ThJCrMR2v8q6Xb_Pxij-HEe_RbDu8cg,3946
643
- reconcile/utils/promtool.py,sha256=xPK4ejsXtK0csj7BVAs49IkilGWgWpHiC2j_oPAp4rQ,2831
643
+ reconcile/utils/promtool.py,sha256=YnqwMAzsQVGuBZ1j9zy3UcVPFQVJgBMLzQkxhK_KFkU,3079
644
644
  reconcile/utils/quay_api.py,sha256=Z6e3oPSp8sCIYxUSDTc6r_qZmXN8Ci_gK90DSCF7BDw,7814
645
645
  reconcile/utils/quay_mirror.py,sha256=dpWCNv5lITwIk6Q9RkmqaQKHNk_JPy27UQEribJ7E-U,1324
646
646
  reconcile/utils/raw_github_api.py,sha256=2WKtE8ABYYB9UGOAh9N_kLkksBWL3320Z2_scteZddI,2805
@@ -764,14 +764,14 @@ reconcile/utils/unleash/__init__.py,sha256=2PsN3GlLU8DOyWSvv5q9uzwuFn_vYtfEo-mmV
764
764
  reconcile/utils/unleash/client.py,sha256=YrJnauxjcy1ml7W2AHg7dzIH_fVK_GugoRu7IFmk6e0,3505
765
765
  reconcile/utils/unleash/server.py,sha256=907gDh9Ee8UxLqusnfpzE-7LUnttB38D4xhVJ0vMf_M,4439
766
766
  tools/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
767
- tools/alert_report.py,sha256=PqhFD2Yy_-dYCCSdNjoXbU6-FxbKnxCLLbcgjWmePhE,5051
767
+ tools/alert_report.py,sha256=cyQTei8_SYOggW_6uYvzhrF7Nae-9xOz-jmh9O9V8R8,5589
768
768
  tools/app_interface_metrics_exporter.py,sha256=f1qwTmQfEcs98uBVRyBa0k7GQXdiSwd7w1hDVjhdGcQ,2303
769
769
  tools/app_interface_reporter.py,sha256=0_oq1-mL0UOh1x5G8CoKaEVJqK-XTKE7PX7IbRTovBc,17224
770
770
  tools/app_sre_tekton_access_reporter.py,sha256=o9prLUgQpwO3msRWc2as1xT1y9OB3znkpgvLr0Ys8_M,3146
771
771
  tools/app_sre_tekton_access_revalidation.py,sha256=66nHEaY-bIqxIhpcmwN8AvQZu6ZXenfkg4Fut0pVZRM,2726
772
772
  tools/glitchtip_access_reporter.py,sha256=o01A6b88t3Wie6tj_tJWWVo2J01LxQ_a9giGm4UzEaU,2901
773
773
  tools/glitchtip_access_revalidation.py,sha256=PXN5wxl6OX8sxddPaakDF3X79nFLvpm-lz0mWLVelw0,2806
774
- tools/qontract_cli.py,sha256=pjUR7TJZccs-k8e-RH5z7Hmg7RIY-WQKGKn_4kvFq7Y,159087
774
+ tools/qontract_cli.py,sha256=DkIJX7jYRycBDKySQHVB6fNIAFpF3USkZaLJbcsYnDg,159204
775
775
  tools/template_validation.py,sha256=wW22pH4bJBN5CcK74RuIEwtiVmQ3zI1eSvNMAE6fbyc,3377
776
776
  tools/cli_commands/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
777
777
  tools/cli_commands/container_images_report.py,sha256=_8GuBsl_qYP5qfa5aa9krwyKHYu82MSh02deHEroSa4,5459
@@ -797,7 +797,7 @@ tools/saas_promotion_state/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJ
797
797
  tools/saas_promotion_state/saas_promotion_state.py,sha256=oF7C4hpIgyMTwTRm3Aun3cDCHIjVar65JoLp6NcJHlU,3909
798
798
  tools/sre_checkpoints/__init__.py,sha256=CDaDaywJnmRCLyl_NCcvxi-Zc0hTi_3OdwKiFOyS39I,145
799
799
  tools/sre_checkpoints/util.py,sha256=zEDbGr18ZeHNQwW8pUsr2JRjuXIPz--WAGJxZo9sv_Y,894
800
- qontract_reconcile-0.10.2.dev265.dist-info/METADATA,sha256=dstrNAtHuutiYmLbd0W36G_n51PgL2oUMhbPAhNMQ5w,24501
801
- qontract_reconcile-0.10.2.dev265.dist-info/WHEEL,sha256=qtCwoSJWgHk21S1Kb4ihdzI2rlJ1ZKaIurTj_ngOhyQ,87
802
- qontract_reconcile-0.10.2.dev265.dist-info/entry_points.txt,sha256=5i9l54La3vQrDLAdwDKQWC0iG4sV9RRfOb1BpvzOWLc,698
803
- qontract_reconcile-0.10.2.dev265.dist-info/RECORD,,
800
+ qontract_reconcile-0.10.2.dev267.dist-info/METADATA,sha256=8vXQoAu6tLPI2W0bpAPmQW3RzzD_eO20K4SvoXs-560,24501
801
+ qontract_reconcile-0.10.2.dev267.dist-info/WHEEL,sha256=qtCwoSJWgHk21S1Kb4ihdzI2rlJ1ZKaIurTj_ngOhyQ,87
802
+ qontract_reconcile-0.10.2.dev267.dist-info/entry_points.txt,sha256=5i9l54La3vQrDLAdwDKQWC0iG4sV9RRfOb1BpvzOWLc,698
803
+ qontract_reconcile-0.10.2.dev267.dist-info/RECORD,,
@@ -139,6 +139,7 @@ provider
139
139
  variables
140
140
  enable_query_support
141
141
  tests
142
+ promtool_version
142
143
  }
143
144
  """
144
145
 
@@ -42,6 +42,7 @@ QONTRACT_INTEGRATION_VERSION = make_semver(0, 1, 0)
42
42
  PROVIDERS = ["prometheus-rule"]
43
43
 
44
44
  NAMESPACE_NAME = "openshift-customer-monitoring"
45
+ DEFAULT_PROMTOOL_VERSION = "2.55.1"
45
46
 
46
47
 
47
48
  class TestContent(BaseModel):
@@ -57,6 +58,7 @@ class Test(BaseModel):
57
58
  rule_length: int
58
59
  tests: list[TestContent] | None
59
60
  result: CommandExecutionResult | None = None
61
+ promtool_version: str
60
62
 
61
63
 
62
64
  class RuleToFetch(BaseModel):
@@ -80,6 +82,8 @@ def fetch_rule_and_tests(
80
82
  rule_body = openshift_resource.body
81
83
  rule_length = len(yaml.dump(rule_body)) # Same as prometheus-operator does it.
82
84
 
85
+ promtool_version = rule.resource.get("promtool_version") or DEFAULT_PROMTOOL_VERSION
86
+
83
87
  if rule.resource["type"] == "resource-template-extracurlyjinja2":
84
88
  variables = json.loads(rule.resource.get("variables") or "{}")
85
89
  variables["resource"] = rule.resource
@@ -113,6 +117,7 @@ def fetch_rule_and_tests(
113
117
  rule=rule_body,
114
118
  rule_length=rule_length,
115
119
  tests=tests,
120
+ promtool_version=promtool_version,
116
121
  )
117
122
 
118
123
 
@@ -204,7 +209,9 @@ def check_rule_length(rule_length: int) -> CommandExecutionResult:
204
209
 
205
210
  def run_test(test: Test, alerting_services: Iterable[str]) -> None:
206
211
  """Checks rules, run tests and stores the result in test.result"""
207
- check_rule_result = promtool.check_rule(test.rule["spec"])
212
+ check_rule_result = promtool.check_rule(
213
+ test.rule["spec"], promtool_version=test.promtool_version
214
+ )
208
215
  valid_services_result = check_valid_services(test.rule, alerting_services)
209
216
  rule_length_result = check_rule_length(test.rule_length)
210
217
  test.result = check_rule_result and valid_services_result and rule_length_result
@@ -214,7 +221,9 @@ def run_test(test: Test, alerting_services: Iterable[str]) -> None:
214
221
 
215
222
  rule_files = {test.rule_path: test.rule["spec"]}
216
223
  for t in test.tests or []:
217
- result = promtool.run_test(t.test, rule_files)
224
+ result = promtool.run_test(
225
+ t.test, rule_files, promtool_version=test.promtool_version
226
+ )
218
227
  test.result = test.result and result
219
228
 
220
229
 
@@ -9,17 +9,28 @@ import yaml
9
9
  from reconcile.utils.defer import defer
10
10
  from reconcile.utils.structs import CommandExecutionResult
11
11
 
12
- PROMTOOL_VERSION = ["2.55.1"]
12
+ PROMTOOL_VERSION = ["2.55.1", "3.2.1"]
13
13
  PROMTOOL_VERSION_REGEX = r"^promtool,\sversion\s([\d]+\.[\d]+\.[\d]+).+$"
14
14
 
15
15
 
16
- def check_rule(yaml_spec: Mapping) -> CommandExecutionResult:
16
+ def _bin(version: str | None = None) -> str:
17
+ return f"promtool-{version}" if version else "promtool"
18
+
19
+
20
+ def check_rule(
21
+ yaml_spec: Mapping,
22
+ promtool_version: str | None = None,
23
+ ) -> CommandExecutionResult:
17
24
  """Run promtool check rules on the given yaml spec given as dict"""
18
- return _run_yaml_spec_cmd(cmd=["promtool", "check", "rules"], yaml_spec=yaml_spec)
25
+ return _run_yaml_spec_cmd(
26
+ cmd=[_bin(promtool_version), "check", "rules"], yaml_spec=yaml_spec
27
+ )
19
28
 
20
29
 
21
30
  def run_test(
22
- test_yaml_spec: MutableMapping, rule_files: Mapping[str, Mapping]
31
+ test_yaml_spec: MutableMapping,
32
+ rule_files: Mapping[str, Mapping],
33
+ promtool_version: str | None = None,
23
34
  ) -> CommandExecutionResult:
24
35
  """Run promtool test rules
25
36
 
@@ -52,7 +63,7 @@ def run_test(
52
63
  defer(lambda: _cleanup(temp_rule_files.values()))
53
64
 
54
65
  return _run_yaml_spec_cmd(
55
- cmd=["promtool", "test", "rules"], yaml_spec=temp_test_yaml_spec
66
+ cmd=[_bin(promtool_version), "test", "rules"], yaml_spec=temp_test_yaml_spec
56
67
  )
57
68
 
58
69
 
tools/alert_report.py CHANGED
@@ -12,17 +12,22 @@ class Alert:
12
12
  message: str
13
13
  timestamp: float
14
14
  username: str
15
+ responsed: bool
15
16
 
16
17
 
17
18
  class AlertStat:
18
19
  def __init__(self) -> None:
19
20
  self._triggered_alerts = 0
20
21
  self._resolved_alerts = 0
22
+ self._responsed_alerts = 0
21
23
  self._elapsed_times: list[float] = []
22
24
 
23
25
  def add_triggered(self) -> None:
24
26
  self._triggered_alerts += 1
25
27
 
28
+ def add_responsed(self) -> None:
29
+ self._responsed_alerts += 1
30
+
26
31
  def add_resolved(self) -> None:
27
32
  self._resolved_alerts += 1
28
33
 
@@ -37,6 +42,10 @@ class AlertStat:
37
42
  def resolved_alerts(self) -> int:
38
43
  return self._resolved_alerts
39
44
 
45
+ @property
46
+ def responsed_alerts(self) -> int:
47
+ return self._responsed_alerts
48
+
40
49
  @property
41
50
  def elapsed_times(self) -> list[float]:
42
51
  return self._elapsed_times
@@ -53,6 +62,8 @@ def group_alerts(messages: list[dict]) -> dict[str, list[Alert]]:
53
62
  continue
54
63
 
55
64
  timestamp = float(m["ts"])
65
+ responsed = bool(m.get("reply_count", 0) or m.get("reactions", []))
66
+
56
67
  for at in m.get("attachments", []):
57
68
  if "title" not in at:
58
69
  continue
@@ -79,6 +90,7 @@ def group_alerts(messages: list[dict]) -> dict[str, list[Alert]]:
79
90
  message=alert_message,
80
91
  timestamp=timestamp,
81
92
  username=m["username"],
93
+ responsed=responsed,
82
94
  )
83
95
  )
84
96
  else:
@@ -93,6 +105,7 @@ def group_alerts(messages: list[dict]) -> dict[str, list[Alert]]:
93
105
  message="placeholder",
94
106
  timestamp=timestamp,
95
107
  username=m["username"],
108
+ responsed=responsed,
96
109
  )
97
110
  )
98
111
  continue
@@ -114,6 +127,7 @@ def group_alerts(messages: list[dict]) -> dict[str, list[Alert]]:
114
127
  message=alert_message,
115
128
  timestamp=timestamp,
116
129
  username=m["username"],
130
+ responsed=responsed,
117
131
  )
118
132
  )
119
133
 
@@ -135,6 +149,8 @@ def gen_alert_stats(alerts: dict[str, list[Alert]]) -> dict[str, AlertStat]:
135
149
  if al.state == "FIRING":
136
150
  alert_stats[alert_name].add_triggered()
137
151
  temp[key] = al
152
+ if al.responsed:
153
+ alert_stats[alert_name].add_responsed()
138
154
 
139
155
  if al.state == "RESOLVED":
140
156
  alert_stats[alert_name].add_resolved()
tools/qontract_cli.py CHANGED
@@ -869,6 +869,7 @@ def alert_report(
869
869
  "Triggered",
870
870
  "Resolved",
871
871
  "Median time to resolve (h:mm:ss)",
872
+ "Response Rate",
872
873
  ]
873
874
  table_data: list[dict[str, str]] = []
874
875
  for alert_name, data in sorted(
@@ -884,6 +885,7 @@ def alert_report(
884
885
  "Triggered": str(data.triggered_alerts),
885
886
  "Resolved": str(data.resolved_alerts),
886
887
  "Median time to resolve (h:mm:ss)": median_elapsed,
888
+ "Response Rate": f"{data.responsed_alerts / data.triggered_alerts * 100:.2f}%",
887
889
  })
888
890
 
889
891
  # TODO(mafriedm, rporres): Fix this