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.
- {qontract_reconcile-0.10.1rc868.dist-info → qontract_reconcile-0.10.1rc870.dist-info}/METADATA +1 -1
- {qontract_reconcile-0.10.1rc868.dist-info → qontract_reconcile-0.10.1rc870.dist-info}/RECORD +12 -12
- reconcile/jenkins_job_builder.py +1 -34
- reconcile/jenkins_webhooks.py +2 -5
- reconcile/queries.py +33 -0
- reconcile/test/conftest.py +3 -2
- reconcile/utils/promotion_state.py +30 -7
- reconcile/utils/saasherder/saasherder.py +2 -1
- tools/qontract_cli.py +70 -0
- {qontract_reconcile-0.10.1rc868.dist-info → qontract_reconcile-0.10.1rc870.dist-info}/WHEEL +0 -0
- {qontract_reconcile-0.10.1rc868.dist-info → qontract_reconcile-0.10.1rc870.dist-info}/entry_points.txt +0 -0
- {qontract_reconcile-0.10.1rc868.dist-info → qontract_reconcile-0.10.1rc870.dist-info}/top_level.txt +0 -0
{qontract_reconcile-0.10.1rc868.dist-info → qontract_reconcile-0.10.1rc870.dist-info}/METADATA
RENAMED
@@ -1,6 +1,6 @@
|
|
1
1
|
Metadata-Version: 2.1
|
2
2
|
Name: qontract-reconcile
|
3
|
-
Version: 0.10.
|
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
|
{qontract_reconcile-0.10.1rc868.dist-info → qontract_reconcile-0.10.1rc870.dist-info}/RECORD
RENAMED
@@ -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=
|
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=
|
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=
|
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=
|
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=
|
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=
|
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=
|
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.
|
841
|
-
qontract_reconcile-0.10.
|
842
|
-
qontract_reconcile-0.10.
|
843
|
-
qontract_reconcile-0.10.
|
844
|
-
qontract_reconcile-0.10.
|
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,,
|
reconcile/jenkins_job_builder.py
CHANGED
@@ -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:
|
reconcile/jenkins_webhooks.py
CHANGED
@@ -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"]
|
reconcile/test/conftest.py
CHANGED
@@ -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
|
-
|
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,
|
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
|
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
|
-
|
71
|
-
|
72
|
-
|
73
|
-
|
74
|
-
|
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
|
-
|
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
|
File without changes
|
File without changes
|
{qontract_reconcile-0.10.1rc868.dist-info → qontract_reconcile-0.10.1rc870.dist-info}/top_level.txt
RENAMED
File without changes
|