qontract-reconcile 0.10.1rc868__py3-none-any.whl → 0.10.1rc870__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.1rc868
3
+ Version: 0.10.1rc870
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
@@ -39,11 +39,11 @@ reconcile/gitlab_permissions.py,sha256=1tFZws0-prcJ7vhFF3N7BxKocc16SjJuoqCkzRFES
39
39
  reconcile/gitlab_projects.py,sha256=K3tFf_aD1W4Ijp5q-9Qek3kwFGEWPcZ1kd7tzFJ4GyQ,1781
40
40
  reconcile/integrations_manager.py,sha256=J_VV-HINI7YNav2NPIolePZkll-7VBuBXWAyMNhsM_Q,9535
41
41
  reconcile/jenkins_base.py,sha256=0Gocu3fU2YTltaxBlbDQOUvP-7CP2OSQV1ZRwtWeVXw,875
42
- reconcile/jenkins_job_builder.py,sha256=8mcvvkPvcgw0hZnYTcuh9P-ltSHwqg8zihXh7oxGuzI,3957
42
+ reconcile/jenkins_job_builder.py,sha256=XNwEkC688eAUQg8Yd69hZSByvSTmUaIXnZ-1R8LEF94,3521
43
43
  reconcile/jenkins_job_builds_cleaner.py,sha256=ksO5TXHxIMV_SF8kO86Wz7qGnYwbdt10wdhpb4aaEyQ,3851
44
44
  reconcile/jenkins_job_cleaner.py,sha256=dQGInds5RV-s9caec0212GveZ32xlCi2HiPyrIkVyFM,1761
45
45
  reconcile/jenkins_roles.py,sha256=f8ELpZY36UjoaCpR_9LijQuIMuB6a7sVLFf_H1ct9Hc,4460
46
- reconcile/jenkins_webhooks.py,sha256=j8vhJMWcRhOdc9XzRSm0CPj84jsF3e4Syjm7r1BIsDE,1978
46
+ reconcile/jenkins_webhooks.py,sha256=K5h0OlCghoHlpot40IRq4BuezfKB1rj4Xk0dCUvqp3o,1952
47
47
  reconcile/jenkins_webhooks_cleaner.py,sha256=JsN_NVPfZJwv1JtSzZXDIHUqGiefL-DRffFnDGau9aY,1539
48
48
  reconcile/jenkins_worker_fleets.py,sha256=PMNGOX0krubFjInPiFT0za0KCiWBLEcVDuXdKRd1BrE,5378
49
49
  reconcile/jira_permissions_validator.py,sha256=iDsFdGqB8Zv9cjIVgYFq_N3xtRCrcR5uAZmcc51D-2o,13240
@@ -94,7 +94,7 @@ reconcile/quay_mirror.py,sha256=9NzbNoxl-NdD8CwImcXNG5xTdHmUJxBfeVk5XHH41J8,1488
94
94
  reconcile/quay_mirror_org.py,sha256=Oq-t3kSkgfeSAOUDjLCDRBeEvOIEBacfX38qrX_s0oc,10801
95
95
  reconcile/quay_permissions.py,sha256=9KOutS1w4RFQqkvMSy54VtsKNx56-phzP6yI_rEW-B8,4244
96
96
  reconcile/quay_repos.py,sha256=cuEYG0HUe0ut5yvLdEwOF5-CmccpXQHRb_wDazvDrvQ,6895
97
- reconcile/queries.py,sha256=dzg1HlbYYbV-UIpEuHVRpgm65q4B5zkF2EK4qhHWqrI,50839
97
+ reconcile/queries.py,sha256=CAHgFEUMSLOX_fZcHja10D53VKHZdWSUL9sUgNsFmYo,51272
98
98
  reconcile/query_validator.py,sha256=BAjGrU8_VhzTOv5k0-uz0hY9ziZyconv8VAhgre1Auc,1497
99
99
  reconcile/requests_sender.py,sha256=914iluuF4UVgG3VyxxtnHOu4yf6YKS2fIy6PViSsFTQ,3875
100
100
  reconcile/resource_scraper.py,sha256=vo1N9vLJCYWvXlTwFRIpEuWjx_39ZV9zxJlpoPq4g3U,2330
@@ -476,7 +476,7 @@ reconcile/terraform_vpc_resources/integration.py,sha256=bqEq3qHaUVQkbGt100Fa7ZwN
476
476
  reconcile/terraform_vpc_resources/merge_request.py,sha256=loRymUigCIvaaT0s_NzktZchh-DGRQnCICdBSCAcFPY,1503
477
477
  reconcile/terraform_vpc_resources/merge_request_manager.py,sha256=Vj2nuQbQyrL4q_il1My-bLxYNh_r3YXqX45P8fwtP6Q,3259
478
478
  reconcile/test/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
479
- reconcile/test/conftest.py,sha256=0pO4UxFeBALKbL9gwemyap0VkbPR8n5TtZbf5c9pSv0,4303
479
+ reconcile/test/conftest.py,sha256=0GXXH9DKIL9DlvY4tavLqXPaK8UXoC5tOfxkGfGy4Bs,4365
480
480
  reconcile/test/fixtures.py,sha256=9SDWAUlSd1rCx7z3GhULHcpr-I6FyCsXxaFAZIqYQsQ,591
481
481
  reconcile/test/test_acs_notifiers.py,sha256=xf3WL6q6V7KQdTVSx6YI-pa4yzOX3mkvIJomgPUc3Mw,12746
482
482
  reconcile/test/test_acs_policies.py,sha256=8pwnXpAO-0OI-6oubjf_oPPlpZjVldeZfJJ9uhsNMWM,17579
@@ -686,7 +686,7 @@ reconcile/utils/pagerduty_api.py,sha256=fcSAUez6w51woDvbm0plJW2qSw6_NXQs1Fit_KTN
686
686
  reconcile/utils/parse_dhms_duration.py,sha256=TONpLnec5gHeF7k815YNJpQyDjXhkxZIcv9s8ffbTSY,1840
687
687
  reconcile/utils/password_validator.py,sha256=XwuWg-8CPlcuG7dl_oQ1G1h2gSVSnfMym_VkuprpWVg,2183
688
688
  reconcile/utils/prometheus.py,sha256=i5aCQ_I4WOg76iEjglVxxO9-OKby2N80ScErAbDtHE8,3848
689
- reconcile/utils/promotion_state.py,sha256=Gf58uvBWqGDZtNsnGbi6-K4LC94Mm_bJDs_Ztq1FPSs,2603
689
+ reconcile/utils/promotion_state.py,sha256=RswY0iMBw92ktyoorb-PFefaTAVlxyiKduVIoPyHpgU,3647
690
690
  reconcile/utils/promtool.py,sha256=kT2rFZSBaRqW7SSHAuYzGZzQxM5Dzk8KW1NnEUYZU_s,2896
691
691
  reconcile/utils/quay_api.py,sha256=EuOegpb-7ntEjkKLFwM2Oo4Nw7SyFtmyl3sQ9aXMtrM,8152
692
692
  reconcile/utils/raw_github_api.py,sha256=ZHC-SZuAyRe1zaMoOU7Krt1-zecDxENd9c_NzQYqK9g,2968
@@ -789,7 +789,7 @@ reconcile/utils/runtime/sharding.py,sha256=roCdbnBklhTK_g34zbgQYqzpKPaNQ8J6Xd9XL
789
789
  reconcile/utils/saasherder/__init__.py,sha256=J3MBZBFa5YmhqYm08QsjBXz8mFcVOCiOCkyIcw41t7E,343
790
790
  reconcile/utils/saasherder/interfaces.py,sha256=Tte-BAJ71FZF1J_ADay1UVIxLCJZcbefq4SRua4mn5w,9141
791
791
  reconcile/utils/saasherder/models.py,sha256=XiAb9pSmTxaNFa3XqNNfe1JxlGgTqsmd1nLi17iIV_g,5566
792
- reconcile/utils/saasherder/saasherder.py,sha256=4S3Q4tb11hmBI3rux5ajk-BUs-6Lfpg47uHZJj2uNKk,86787
792
+ reconcile/utils/saasherder/saasherder.py,sha256=kPYklRU9hPcdg573QQ0OoaXYG26t_v5COAz62QVPS3c,86883
793
793
  reconcile/utils/terraform/__init__.py,sha256=zNbiyTWo35AT1sFTElL2j_AA0jJ_yWE_bfFn-nD2xik,250
794
794
  reconcile/utils/terraform/config.py,sha256=5UVrd563TMcvi4ooa5JvWVDW1I3bIWg484u79evfV_8,164
795
795
  reconcile/utils/terraform/config_client.py,sha256=py-Ree-QUYD6Hvng6bM40VgSuttteehIKNgwOSoJO1o,4706
@@ -809,7 +809,7 @@ tools/app_interface_metrics_exporter.py,sha256=zkwkxdAUAxjdc-pzx2_oJXG25fo0Fnyd5
809
809
  tools/app_interface_reporter.py,sha256=upA-J-n-HXHKVDINRuMR7vTt-iJvQORKUVi9D3leQto,17738
810
810
  tools/glitchtip_access_reporter.py,sha256=oPBnk_YoDuljU3v0FaChzOwwnk4vap1xEE67QEjzdqs,2948
811
811
  tools/glitchtip_access_revalidation.py,sha256=8kbBJk04mkq28kWoRDDkfCGIF3GRg3pJrFAh1sW0dbk,2821
812
- tools/qontract_cli.py,sha256=y6Okq0KPRKXx8glu4g3GeDavtoosH8iqwy5YZMV8cwc,118517
812
+ tools/qontract_cli.py,sha256=GsODNjSs-1TP7U0JjHxYhF-9XWZe3cCk-rj7osApLr4,120445
813
813
  tools/sd_app_sre_alert_report.py,sha256=e9vAdyenUz2f5c8-z-5WY0wv-SJ9aePKDH2r4IwB6pc,5063
814
814
  tools/template_validation.py,sha256=-U-lTGeLaci8yWPEblCJeev2DOlY1jM9QOOh-O1zts8,3376
815
815
  tools/cli_commands/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
@@ -837,8 +837,8 @@ tools/test/test_app_interface_metrics_exporter.py,sha256=SX7qL3D1SIRKFo95FoQztvf
837
837
  tools/test/test_qontract_cli.py,sha256=_D61RFGAN5x44CY1tYbouhlGXXABwYfxKSWSQx3Jrss,4941
838
838
  tools/test/test_sd_app_sre_alert_report.py,sha256=v363r9zM7__0kR5K6mvJoGFcM9BvE33fWAayrqkpojA,2116
839
839
  tools/test/test_sre_checkpoints.py,sha256=SKqPPTl9ua0RFdSSofnoQX-JZE6dFLO3LRhfQzqtfh8,2607
840
- qontract_reconcile-0.10.1rc868.dist-info/METADATA,sha256=2Ic4FAzZN1tZWidUXUc0YXjGK1fFGxGkQa2rdANjHZ8,2273
841
- qontract_reconcile-0.10.1rc868.dist-info/WHEEL,sha256=GJ7t_kWBFywbagK5eo9IoUwLW6oyOeTKmQ-9iHFVNxQ,92
842
- qontract_reconcile-0.10.1rc868.dist-info/entry_points.txt,sha256=GKQqCl2j2X1BJQ69een6rHcR26PmnxnONLNOQB-nRjY,491
843
- qontract_reconcile-0.10.1rc868.dist-info/top_level.txt,sha256=l5ISPoXzt0SdR4jVdkfa7RPSKNc8zAHYWAnR-Dw8Ey8,24
844
- qontract_reconcile-0.10.1rc868.dist-info/RECORD,,
840
+ qontract_reconcile-0.10.1rc870.dist-info/METADATA,sha256=MdcpkzNM0kwEg9ZL_cY9KKyDGzleE4-buVLHbDT-1pM,2273
841
+ qontract_reconcile-0.10.1rc870.dist-info/WHEEL,sha256=GJ7t_kWBFywbagK5eo9IoUwLW6oyOeTKmQ-9iHFVNxQ,92
842
+ qontract_reconcile-0.10.1rc870.dist-info/entry_points.txt,sha256=GKQqCl2j2X1BJQ69een6rHcR26PmnxnONLNOQB-nRjY,491
843
+ qontract_reconcile-0.10.1rc870.dist-info/top_level.txt,sha256=l5ISPoXzt0SdR4jVdkfa7RPSKNc8zAHYWAnR-Dw8Ey8,24
844
+ qontract_reconcile-0.10.1rc870.dist-info/RECORD,,
@@ -7,7 +7,6 @@ from typing import (
7
7
  )
8
8
 
9
9
  from reconcile import queries
10
- from reconcile.utils import gql
11
10
  from reconcile.utils.defer import defer
12
11
  from reconcile.utils.jjb_client import JJB
13
12
  from reconcile.utils.secret_reader import (
@@ -16,46 +15,14 @@ from reconcile.utils.secret_reader import (
16
15
  )
17
16
  from reconcile.utils.state import init_state
18
17
 
19
- QUERY = """
20
- {
21
- jenkins_configs: jenkins_configs_v1 {
22
- name
23
- app {
24
- name
25
- }
26
- instance {
27
- name
28
- serverUrl
29
- token {
30
- path
31
- field
32
- version
33
- format
34
- }
35
- deleteMethod
36
- }
37
- type
38
- config
39
- config_path {
40
- content
41
- }
42
- }
43
- }
44
- """
45
-
46
18
  QONTRACT_INTEGRATION = "jenkins-job-builder"
47
19
  GENERATE_TYPE = ["jobs", "views"]
48
20
 
49
21
 
50
- def get_jenkins_configs():
51
- gqlapi = gql.get_api()
52
- return gqlapi.query(QUERY)["jenkins_configs"]
53
-
54
-
55
22
  def collect_configs(
56
23
  instance_name: Optional[str], config_name: Optional[str]
57
24
  ) -> list[dict[str, Any]]:
58
- configs = get_jenkins_configs()
25
+ configs = queries.get_jenkins_configs()
59
26
  if instance_name is not None:
60
27
  configs = [n for n in configs if n["instance"]["name"] == instance_name]
61
28
  if not configs:
@@ -3,10 +3,7 @@ import logging
3
3
  from typing import Any
4
4
 
5
5
  from reconcile import queries
6
- from reconcile.jenkins_job_builder import (
7
- get_jenkins_configs,
8
- init_jjb,
9
- )
6
+ from reconcile.jenkins_job_builder import init_jjb
10
7
  from reconcile.utils.defer import defer
11
8
  from reconcile.utils.gitlab_api import GitLabApi
12
9
  from reconcile.utils.jjb_client import JJB
@@ -62,5 +59,5 @@ def run(dry_run, defer=None):
62
59
 
63
60
  def early_exit_desired_state(*args, **kwargs) -> dict[str, Any]:
64
61
  return {
65
- "jenkins_configs": get_jenkins_configs(),
62
+ "jenkins_configs": queries.get_jenkins_configs(),
66
63
  }
reconcile/queries.py CHANGED
@@ -2793,3 +2793,36 @@ BLACKBOX_EXPORTER_MONITORING_PROVIDER = """
2793
2793
  def get_blackbox_exporter_monitoring_provider() -> dict:
2794
2794
  gqlapi = gql.get_api()
2795
2795
  return gqlapi.query(BLACKBOX_EXPORTER_MONITORING_PROVIDER)["providers"]
2796
+
2797
+
2798
+ JENKINS_CONFIGS = """
2799
+ {
2800
+ jenkins_configs: jenkins_configs_v1 {
2801
+ name
2802
+ app {
2803
+ name
2804
+ }
2805
+ instance {
2806
+ name
2807
+ serverUrl
2808
+ token {
2809
+ path
2810
+ field
2811
+ version
2812
+ format
2813
+ }
2814
+ deleteMethod
2815
+ }
2816
+ type
2817
+ config
2818
+ config_path {
2819
+ content
2820
+ }
2821
+ }
2822
+ }
2823
+ """
2824
+
2825
+
2826
+ def get_jenkins_configs():
2827
+ gqlapi = gql.get_api()
2828
+ return gqlapi.query(JENKINS_CONFIGS)["jenkins_configs"]
@@ -10,7 +10,7 @@ from typing import (
10
10
  Any,
11
11
  Optional,
12
12
  )
13
- from unittest.mock import create_autospec
13
+ from unittest.mock import MagicMock, create_autospec
14
14
 
15
15
  import pytest
16
16
  from pydantic import BaseModel
@@ -74,7 +74,8 @@ def s3_state_builder() -> Callable[[Mapping], State]:
74
74
  return get(key)
75
75
 
76
76
  state = create_autospec(spec=State)
77
- state.get = get
77
+ mock_get = MagicMock(side_effect=get)
78
+ state.get = mock_get
78
79
  state.__getitem__ = __getitem__
79
80
  state.ls.side_effect = [data.get("ls", [])]
80
81
  return state
@@ -34,11 +34,17 @@ class PromotionState:
34
34
  A wrapper around a reconcile.utils.state.State object.
35
35
  This is dedicated to storing and retrieving information
36
36
  about promotions on S3.
37
+
38
+ Note, that PromotionsState holds 2 caches.
39
+ One cache for the promotion data that has already been fetched.
40
+ Another cache for commit sha lookup, i.e., checking if a commit sha
41
+ exists in S3 before making any API calls to it.
37
42
  """
38
43
 
39
44
  def __init__(self, state: State):
40
45
  self._state = state
41
46
  self._commits_by_channel: dict[str, set[str]] = defaultdict(set)
47
+ self._promotion_data_cache: dict[str, PromotionData | None] = {}
42
48
 
43
49
  def _target_key(self, channel: str, target_uid: str) -> str:
44
50
  return f"{channel}/{target_uid}"
@@ -59,19 +65,36 @@ class PromotionState:
59
65
  self._commits_by_channel[key].add(commit_sha)
60
66
 
61
67
  def get_promotion_data(
62
- self, sha: str, channel: str, target_uid: str = "", local_lookup: bool = True
68
+ self,
69
+ sha: str,
70
+ channel: str,
71
+ target_uid: str = "",
72
+ pre_check_sha_exists: bool = True,
73
+ use_cache: bool = False,
63
74
  ) -> Optional[PromotionData]:
75
+ """
76
+ Fetch promotion data from S3.
77
+
78
+ @param use_cache: Each fetched promotion data is cached locally. Setting this
79
+ flag to True will use the cache if the data is already fetched.
80
+
81
+ @param pre_check_sha_exists: If set to True, we will check if the commit sha exists
82
+ in local cache and if not will exit before making any API calls. Note, that this requires
83
+ a prior call to cache_commit_shas_from_s3 to populate the local commit cache.
84
+ """
64
85
  cache_key_v2 = self._target_key(channel=channel, target_uid=target_uid)
65
- if local_lookup and sha not in self._commits_by_channel[cache_key_v2]:
86
+ if pre_check_sha_exists and sha not in self._commits_by_channel[cache_key_v2]:
66
87
  # Lets reduce unecessary calls to S3
67
88
  return None
68
89
 
69
90
  path_v2 = f"promotions_v2/{channel}/{target_uid}/{sha}"
70
- try:
71
- data = self._state.get(path_v2)
72
- return PromotionData(**data)
73
- except KeyError:
74
- return None
91
+ if use_cache and path_v2 in self._promotion_data_cache:
92
+ return self._promotion_data_cache[path_v2]
93
+
94
+ data = self._state.get(path_v2)
95
+ promotion_data = PromotionData(**data)
96
+ self._promotion_data_cache[path_v2] = promotion_data
97
+ return promotion_data
75
98
 
76
99
  def publish_promotion_data(
77
100
  self, sha: str, channel: str, target_uid: str, data: PromotionData
@@ -1899,7 +1899,7 @@ class SaasHerder: # pylint: disable=too-many-public-methods
1899
1899
  sha=promotion.commit_sha,
1900
1900
  channel=channel.name,
1901
1901
  target_uid=target_uid,
1902
- local_lookup=False,
1902
+ pre_check_sha_exists=False,
1903
1903
  )
1904
1904
  if not (deployment and deployment.success):
1905
1905
  logging.error(
@@ -2007,6 +2007,7 @@ class SaasHerder: # pylint: disable=too-many-public-methods
2007
2007
  saas_file=promotion.saas_file,
2008
2008
  success=success,
2009
2009
  target_config_hash=promotion.target_config_hash,
2010
+ # TODO: do not override - check if timestamp already exists
2010
2011
  check_in=str(now),
2011
2012
  ),
2012
2013
  )
tools/qontract_cli.py CHANGED
@@ -2712,6 +2712,76 @@ def systems_and_tools(ctx):
2712
2712
  print_output(ctx.obj["options"], inventory.data, inventory.columns)
2713
2713
 
2714
2714
 
2715
+ @get.command
2716
+ @click.pass_context
2717
+ def jenkins_jobs(ctx):
2718
+ jenkins_configs = queries.get_jenkins_configs()
2719
+
2720
+ # stats dicts
2721
+ apps = {}
2722
+ totals = {"rhel8": 0, "other": 0}
2723
+
2724
+ for jc in jenkins_configs:
2725
+ app_name = jc["app"]["name"]
2726
+
2727
+ if app_name not in apps:
2728
+ apps[app_name] = {"rhel8": 0, "other": 0}
2729
+
2730
+ config = json.loads(jc["config"]) if jc["config"] else []
2731
+ for c in config:
2732
+ if "project" not in c:
2733
+ continue
2734
+
2735
+ project = c["project"]
2736
+ root_node = project.get("node") or ""
2737
+ if "jobs" not in project:
2738
+ continue
2739
+
2740
+ for pj in project["jobs"]:
2741
+ for job in pj.values():
2742
+ node = job["node"] if "node" in job else root_node
2743
+ if node == "rhel8":
2744
+ apps[app_name]["rhel8"] += 1
2745
+ totals["rhel8"] += 1
2746
+ else:
2747
+ apps[app_name]["other"] += 1
2748
+ totals["other"] += 1
2749
+
2750
+ results = [
2751
+ {"app": app} | stats
2752
+ for app, stats in sorted(apps.items(), key=lambda i: i[0].lower())
2753
+ if not (stats["other"] == 0 and stats["rhel8"] == 0)
2754
+ ]
2755
+ results.append({"app": "TOTALS"} | totals)
2756
+
2757
+ if ctx.obj["options"]["output"] == "md":
2758
+ json_table = {
2759
+ "filter": True,
2760
+ "fields": [
2761
+ {"key": "app"},
2762
+ {"key": "other"},
2763
+ {"key": "rhel8"},
2764
+ ],
2765
+ "items": results,
2766
+ }
2767
+
2768
+ print(
2769
+ f"""
2770
+ You can view the source of this Markdown to extract the JSON data.
2771
+
2772
+ {len(results)} apps with Jenkins jobs
2773
+
2774
+ ```json:table
2775
+ {json.dumps(json_table)}
2776
+ ```
2777
+ """
2778
+ )
2779
+ else:
2780
+ columns = ["app", "other", "rhel8"]
2781
+ ctx.obj["options"]["sort"] = False
2782
+ print_output(ctx.obj["options"], results, columns)
2783
+
2784
+
2715
2785
  @root.group(name="set")
2716
2786
  @output
2717
2787
  @click.pass_context