qontract-reconcile 0.10.1rc193__py3-none-any.whl → 0.10.1rc195__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.1rc193.dist-info → qontract_reconcile-0.10.1rc195.dist-info}/METADATA +1 -1
- {qontract_reconcile-0.10.1rc193.dist-info → qontract_reconcile-0.10.1rc195.dist-info}/RECORD +11 -9
- reconcile/gql_definitions/jira_watcher/__init__.py +0 -0
- reconcile/gql_definitions/jira_watcher/jira_watcher_boards.py +149 -0
- reconcile/jira_watcher.py +96 -29
- reconcile/queries.py +0 -56
- reconcile/utils/jira_client.py +1 -1
- reconcile/utils/ocm_base_client.py +5 -11
- {qontract_reconcile-0.10.1rc193.dist-info → qontract_reconcile-0.10.1rc195.dist-info}/WHEEL +0 -0
- {qontract_reconcile-0.10.1rc193.dist-info → qontract_reconcile-0.10.1rc195.dist-info}/entry_points.txt +0 -0
- {qontract_reconcile-0.10.1rc193.dist-info → qontract_reconcile-0.10.1rc195.dist-info}/top_level.txt +0 -0
{qontract_reconcile-0.10.1rc193.dist-info → qontract_reconcile-0.10.1rc195.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.1rc195
|
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.1rc193.dist-info → qontract_reconcile-0.10.1rc195.dist-info}/RECORD
RENAMED
@@ -41,7 +41,7 @@ reconcile/jenkins_webhooks.py,sha256=j8vhJMWcRhOdc9XzRSm0CPj84jsF3e4Syjm7r1BIsDE
|
|
41
41
|
reconcile/jenkins_webhooks_cleaner.py,sha256=JsN_NVPfZJwv1JtSzZXDIHUqGiefL-DRffFnDGau9aY,1539
|
42
42
|
reconcile/jenkins_worker_fleets.py,sha256=nwlsLSly85RukBGCVsEpoc92BVxvCjJetFlpNC5OwPc,5282
|
43
43
|
reconcile/jira_permissions_validator.py,sha256=Iul5-2_QgQ8joGfP542UQYA0Y5Qm5chvdRzUltCo_yM,1434
|
44
|
-
reconcile/jira_watcher.py,sha256=
|
44
|
+
reconcile/jira_watcher.py,sha256=CsSipmf7ThuNzj9Xy41vedPwJ-axxy47OPXMCZqZyks,5634
|
45
45
|
reconcile/ldap_users.py,sha256=uEWQ0V41tN9KCZi4ZKPamjrJ6djSpdpvDBo7yJ0e7ZI,3008
|
46
46
|
reconcile/mr_client_gateway.py,sha256=WhjMd-sIXDFCV8-rt8CEjurJ5OYB1pOD0K3o0tZRXQg,1885
|
47
47
|
reconcile/ocm_additional_routers.py,sha256=KfcFDVbNoc6n5dHWjYdAf1_DiVqVG6Tw23WLKoV8cdg,3306
|
@@ -87,7 +87,7 @@ reconcile/quay_mirror.py,sha256=dOKvUeiXXs8ryqLSxgOITQPMOnx0bTZakFKb4UqhaQc,1340
|
|
87
87
|
reconcile/quay_mirror_org.py,sha256=E1OdRe-ppxTkNCwu20iVRhEdG1fPDBroLY02NgiMN7c,10381
|
88
88
|
reconcile/quay_permissions.py,sha256=_3PCWjNWoU7VHlYgHzUevvL_jJmEMsWfXV_nzjeiyhU,4099
|
89
89
|
reconcile/quay_repos.py,sha256=7609RBVQihis96FNOOe-i9tCTYwcTVy4WpKAL6HpnkU,7031
|
90
|
-
reconcile/queries.py,sha256=
|
90
|
+
reconcile/queries.py,sha256=F6Q5RRWovTGh_s7hiqKThttjhYySLPjLSJn5HdaJ864,47914
|
91
91
|
reconcile/query_validator.py,sha256=oLEZIAsQCzxmmZ7b9dSw-OKuEjpI1dbVu4XfCfjpmi8,1503
|
92
92
|
reconcile/requests_sender.py,sha256=914iluuF4UVgG3VyxxtnHOu4yf6YKS2fIy6PViSsFTQ,3875
|
93
93
|
reconcile/resource_scraper.py,sha256=vo1N9vLJCYWvXlTwFRIpEuWjx_39ZV9zxJlpoPq4g3U,2330
|
@@ -212,6 +212,8 @@ reconcile/gql_definitions/jenkins_configs/__init__.py,sha256=47DEQpj8HBSa-_TImW-
|
|
212
212
|
reconcile/gql_definitions/jenkins_configs/jenkins_configs.py,sha256=8U4S1iRIQ86VTrKGd5k0xgi-DlwhYKOlevK_OWIKC98,2784
|
213
213
|
reconcile/gql_definitions/jira_permissions_validator/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
214
214
|
reconcile/gql_definitions/jira_permissions_validator/jira_boards_for_permissions_validator.py,sha256=5iHCIIfZYKopghfc7-BpoG2uvxNrm_qYwzdsRI_kVig,2266
|
215
|
+
reconcile/gql_definitions/jira_watcher/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
216
|
+
reconcile/gql_definitions/jira_watcher/jira_watcher_boards.py,sha256=BNB4DHdviKC3dKRQ3Dzs6P-mHWUfkRK7KlQ90GhuK4o,4011
|
215
217
|
reconcile/gql_definitions/jumphosts/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
216
218
|
reconcile/gql_definitions/jumphosts/jumphosts.py,sha256=V0K6V9_O8LFztCcBtJfaZ9PMkpXF2N43ju_R1ajTzho,2384
|
217
219
|
reconcile/gql_definitions/ocp_release_mirror/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
@@ -509,7 +511,7 @@ reconcile/utils/imap_client.py,sha256=byFAJATbITJPsGECSbvXBOcCnoeTUpDFiEjzOAxLm_
|
|
509
511
|
reconcile/utils/instrumented_wrappers.py,sha256=eVwMoa6FCrYxLv3RML3WpZF9qKVfCTjMxphgVXG03OM,1073
|
510
512
|
reconcile/utils/jenkins_api.py,sha256=BlHOUDG3Nn5K4qPU-js2ewV_IhciISfCnQwfaZlewu8,7054
|
511
513
|
reconcile/utils/jinja2_ext.py,sha256=l628RR9r9dAGBWLVegoCbSqnjojeizNGiq9Cstt02nE,1129
|
512
|
-
reconcile/utils/jira_client.py,sha256=
|
514
|
+
reconcile/utils/jira_client.py,sha256=r5KsAD-Kxj6pUbwhlq1ME_B_tbgkC5vwf4w_Nk_dksI,5022
|
513
515
|
reconcile/utils/jjb_client.py,sha256=bj40UNRbvQ93K-1tndzJI6Drt_sebRXq-qUpfDx2ozw,14496
|
514
516
|
reconcile/utils/jsonpath.py,sha256=NRpAEijKN4cMDjo7qivNPqpm0__GQQ1TiE0PBEBO45s,5572
|
515
517
|
reconcile/utils/jump_host.py,sha256=yJZBRnoCJ-fknhT9f_cli_1J5hBRh1PsRVDyDWpjhws,4935
|
@@ -523,7 +525,7 @@ reconcile/utils/oc.py,sha256=AIkBSXLnsJCE_9HCMN66-QK359pMBNPs4Q2XPI9zjyU,64780
|
|
523
525
|
reconcile/utils/oc_connection_parameters.py,sha256=mUP0WtpozdFDufcdNKy2OGweRvI3Faq8YLiR0ngJYes,9799
|
524
526
|
reconcile/utils/oc_filters.py,sha256=RWn8pC5A7ZZT7C6WPq9bOw5KBNkiAb5puFSr_FpdAf8,1358
|
525
527
|
reconcile/utils/oc_map.py,sha256=nT69J5pdPeIDnIYjD9fwY6GkE3BMQCf-AF0rmHJuUNw,9068
|
526
|
-
reconcile/utils/ocm_base_client.py,sha256=
|
528
|
+
reconcile/utils/ocm_base_client.py,sha256=7WHzSQ2j3FhZ1d8DA9v2szfg0tpzavfGCaVgGkvoquY,5003
|
527
529
|
reconcile/utils/openshift_resource.py,sha256=fLqURJcjrg8kd9jxbJ91fJBB5woiB4KT_wMKyADzffo,24214
|
528
530
|
reconcile/utils/openssl.py,sha256=QVvhzhpChq_4Daf_5wE1qeZJr4thg3DDjJPn4bOPD4E,365
|
529
531
|
reconcile/utils/output.py,sha256=htcMXMe0y2dNnwu8MW8x0JZ5YBaEJRy5w4tR8yLF0OU,1738
|
@@ -612,8 +614,8 @@ tools/test/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
612
614
|
tools/test/test_qontract_cli.py,sha256=awwTHEc2DWlykuqGIYM0WOBoSL0KRnOraCLk3C7izis,1401
|
613
615
|
tools/test/test_sd_app_sre_alert_report.py,sha256=JeLhgzpKCPgLvptwg_4ZvJHLVWKNG1T5845HXTkMBxA,1826
|
614
616
|
tools/test/test_sre_checkpoints.py,sha256=SKqPPTl9ua0RFdSSofnoQX-JZE6dFLO3LRhfQzqtfh8,2607
|
615
|
-
qontract_reconcile-0.10.
|
616
|
-
qontract_reconcile-0.10.
|
617
|
-
qontract_reconcile-0.10.
|
618
|
-
qontract_reconcile-0.10.
|
619
|
-
qontract_reconcile-0.10.
|
617
|
+
qontract_reconcile-0.10.1rc195.dist-info/METADATA,sha256=PUU-Wdx0R2NJQ6PZRfei54GtnyNCp2GZsMVHQn2PXvg,2320
|
618
|
+
qontract_reconcile-0.10.1rc195.dist-info/WHEEL,sha256=pkctZYzUS4AYVn6dJ-7367OJZivF2e8RA9b_ZBjif18,92
|
619
|
+
qontract_reconcile-0.10.1rc195.dist-info/entry_points.txt,sha256=Af70EWPJxsTiCNF6gA-pWdw1A0Heqn-PZF-oBc5NmiU,302
|
620
|
+
qontract_reconcile-0.10.1rc195.dist-info/top_level.txt,sha256=l5ISPoXzt0SdR4jVdkfa7RPSKNc8zAHYWAnR-Dw8Ey8,24
|
621
|
+
qontract_reconcile-0.10.1rc195.dist-info/RECORD,,
|
File without changes
|
@@ -0,0 +1,149 @@
|
|
1
|
+
"""
|
2
|
+
Generated by qenerate plugin=pydantic_v1. DO NOT MODIFY MANUALLY!
|
3
|
+
"""
|
4
|
+
from collections.abc import Callable # noqa: F401 # pylint: disable=W0611
|
5
|
+
from datetime import datetime # noqa: F401 # pylint: disable=W0611
|
6
|
+
from enum import Enum # noqa: F401 # pylint: disable=W0611
|
7
|
+
from typing import ( # noqa: F401 # pylint: disable=W0611
|
8
|
+
Any,
|
9
|
+
Optional,
|
10
|
+
Union,
|
11
|
+
)
|
12
|
+
|
13
|
+
from pydantic import ( # noqa: F401 # pylint: disable=W0611
|
14
|
+
BaseModel,
|
15
|
+
Extra,
|
16
|
+
Field,
|
17
|
+
Json,
|
18
|
+
)
|
19
|
+
|
20
|
+
from reconcile.gql_definitions.fragments.vault_secret import VaultSecret
|
21
|
+
|
22
|
+
|
23
|
+
DEFINITION = """
|
24
|
+
fragment VaultSecret on VaultSecret_v1 {
|
25
|
+
path
|
26
|
+
field
|
27
|
+
version
|
28
|
+
format
|
29
|
+
}
|
30
|
+
|
31
|
+
query JiraWatcherBoards {
|
32
|
+
jira_boards: jira_boards_v1 {
|
33
|
+
path
|
34
|
+
name
|
35
|
+
server {
|
36
|
+
serverUrl
|
37
|
+
token {
|
38
|
+
... VaultSecret
|
39
|
+
}
|
40
|
+
}
|
41
|
+
slack {
|
42
|
+
workspace {
|
43
|
+
name
|
44
|
+
integrations {
|
45
|
+
name
|
46
|
+
token {
|
47
|
+
... VaultSecret
|
48
|
+
}
|
49
|
+
channel
|
50
|
+
icon_emoji
|
51
|
+
username
|
52
|
+
}
|
53
|
+
api_client {
|
54
|
+
global {
|
55
|
+
max_retries
|
56
|
+
timeout
|
57
|
+
}
|
58
|
+
methods {
|
59
|
+
name
|
60
|
+
args
|
61
|
+
}
|
62
|
+
}
|
63
|
+
}
|
64
|
+
channel
|
65
|
+
}
|
66
|
+
}
|
67
|
+
}
|
68
|
+
"""
|
69
|
+
|
70
|
+
|
71
|
+
class ConfiguredBaseModel(BaseModel):
|
72
|
+
class Config:
|
73
|
+
smart_union = True
|
74
|
+
extra = Extra.forbid
|
75
|
+
|
76
|
+
|
77
|
+
class JiraServerV1(ConfiguredBaseModel):
|
78
|
+
server_url: str = Field(..., alias="serverUrl")
|
79
|
+
token: VaultSecret = Field(..., alias="token")
|
80
|
+
|
81
|
+
|
82
|
+
class SlackWorkspaceIntegrationV1(ConfiguredBaseModel):
|
83
|
+
name: str = Field(..., alias="name")
|
84
|
+
token: VaultSecret = Field(..., alias="token")
|
85
|
+
channel: str = Field(..., alias="channel")
|
86
|
+
icon_emoji: str = Field(..., alias="icon_emoji")
|
87
|
+
username: str = Field(..., alias="username")
|
88
|
+
|
89
|
+
|
90
|
+
class SlackWorkspaceApiClientGlobalConfigV1(ConfiguredBaseModel):
|
91
|
+
max_retries: Optional[int] = Field(..., alias="max_retries")
|
92
|
+
timeout: Optional[int] = Field(..., alias="timeout")
|
93
|
+
|
94
|
+
|
95
|
+
class SlackWorkspaceApiClientMethodConfigV1(ConfiguredBaseModel):
|
96
|
+
name: str = Field(..., alias="name")
|
97
|
+
args: Json = Field(..., alias="args")
|
98
|
+
|
99
|
+
|
100
|
+
class SlackWorkspaceApiClientV1(ConfiguredBaseModel):
|
101
|
+
q_global: Optional[SlackWorkspaceApiClientGlobalConfigV1] = Field(
|
102
|
+
..., alias="global"
|
103
|
+
)
|
104
|
+
methods: Optional[list[SlackWorkspaceApiClientMethodConfigV1]] = Field(
|
105
|
+
..., alias="methods"
|
106
|
+
)
|
107
|
+
|
108
|
+
|
109
|
+
class SlackWorkspaceV1(ConfiguredBaseModel):
|
110
|
+
name: str = Field(..., alias="name")
|
111
|
+
integrations: Optional[list[SlackWorkspaceIntegrationV1]] = Field(
|
112
|
+
..., alias="integrations"
|
113
|
+
)
|
114
|
+
api_client: Optional[SlackWorkspaceApiClientV1] = Field(..., alias="api_client")
|
115
|
+
|
116
|
+
|
117
|
+
class SlackOutputV1(ConfiguredBaseModel):
|
118
|
+
workspace: SlackWorkspaceV1 = Field(..., alias="workspace")
|
119
|
+
channel: Optional[str] = Field(..., alias="channel")
|
120
|
+
|
121
|
+
|
122
|
+
class JiraBoardV1(ConfiguredBaseModel):
|
123
|
+
path: str = Field(..., alias="path")
|
124
|
+
name: str = Field(..., alias="name")
|
125
|
+
server: JiraServerV1 = Field(..., alias="server")
|
126
|
+
slack: Optional[SlackOutputV1] = Field(..., alias="slack")
|
127
|
+
|
128
|
+
|
129
|
+
class JiraWatcherBoardsQueryData(ConfiguredBaseModel):
|
130
|
+
jira_boards: Optional[list[JiraBoardV1]] = Field(..., alias="jira_boards")
|
131
|
+
|
132
|
+
|
133
|
+
def query(query_func: Callable, **kwargs: Any) -> JiraWatcherBoardsQueryData:
|
134
|
+
"""
|
135
|
+
This is a convenience function which queries and parses the data into
|
136
|
+
concrete types. It should be compatible with most GQL clients.
|
137
|
+
You do not have to use it to consume the generated data classes.
|
138
|
+
Alternatively, you can also mime and alternate the behavior
|
139
|
+
of this function in the caller.
|
140
|
+
|
141
|
+
Parameters:
|
142
|
+
query_func (Callable): Function which queries your GQL Server
|
143
|
+
kwargs: optional arguments that will be passed to the query function
|
144
|
+
|
145
|
+
Returns:
|
146
|
+
JiraWatcherBoardsQueryData: queried data parsed into generated classes
|
147
|
+
"""
|
148
|
+
raw_data: dict[Any, Any] = query_func(DEFINITION, **kwargs)
|
149
|
+
return JiraWatcherBoardsQueryData(**raw_data)
|
reconcile/jira_watcher.py
CHANGED
@@ -1,10 +1,34 @@
|
|
1
1
|
import logging
|
2
|
+
from collections.abc import (
|
3
|
+
Callable,
|
4
|
+
Iterable,
|
5
|
+
Mapping,
|
6
|
+
)
|
7
|
+
from typing import Optional
|
2
8
|
|
3
|
-
from reconcile import
|
4
|
-
from reconcile.
|
9
|
+
from reconcile.gql_definitions.common.jira_settings import AppInterfaceSettingsV1
|
10
|
+
from reconcile.gql_definitions.jira_watcher.jira_watcher_boards import (
|
11
|
+
JiraBoardV1,
|
12
|
+
SlackOutputV1,
|
13
|
+
)
|
14
|
+
from reconcile.gql_definitions.jira_watcher.jira_watcher_boards import (
|
15
|
+
query as query_jira_boards,
|
16
|
+
)
|
17
|
+
from reconcile.slack_base import (
|
18
|
+
SlackApi,
|
19
|
+
slackapi_from_slack_workspace,
|
20
|
+
)
|
21
|
+
from reconcile.typed_queries.app_interface_vault_settings import (
|
22
|
+
get_app_interface_vault_settings,
|
23
|
+
)
|
24
|
+
from reconcile.typed_queries.jira_settings import get_jira_settings
|
25
|
+
from reconcile.utils import gql
|
5
26
|
from reconcile.utils.defer import defer
|
6
27
|
from reconcile.utils.jira_client import JiraClient
|
7
|
-
from reconcile.utils.secret_reader import
|
28
|
+
from reconcile.utils.secret_reader import (
|
29
|
+
SecretReaderBase,
|
30
|
+
create_secret_reader,
|
31
|
+
)
|
8
32
|
from reconcile.utils.sharding import is_in_shard_round_robin
|
9
33
|
from reconcile.utils.state import (
|
10
34
|
State,
|
@@ -14,8 +38,18 @@ from reconcile.utils.state import (
|
|
14
38
|
QONTRACT_INTEGRATION = "jira-watcher"
|
15
39
|
|
16
40
|
|
17
|
-
def fetch_current_state(
|
18
|
-
|
41
|
+
def fetch_current_state(
|
42
|
+
jira_board: JiraBoardV1,
|
43
|
+
settings: AppInterfaceSettingsV1,
|
44
|
+
secret_reader: SecretReaderBase,
|
45
|
+
) -> tuple[JiraClient, dict[str, dict]]:
|
46
|
+
token = secret_reader.read_secret(jira_board.server.token)
|
47
|
+
jira = JiraClient.create(
|
48
|
+
project_name=jira_board.name,
|
49
|
+
token=token,
|
50
|
+
server_url=jira_board.server.server_url,
|
51
|
+
jira_watcher_settings=settings.jira_watcher,
|
52
|
+
)
|
19
53
|
issues = jira.get_issues(fields=["key", "status", "summary"])
|
20
54
|
return jira, {
|
21
55
|
issue.key: {"status": issue.fields.status.name, "summary": issue.fields.summary}
|
@@ -23,11 +57,18 @@ def fetch_current_state(jira_board, settings):
|
|
23
57
|
}
|
24
58
|
|
25
59
|
|
26
|
-
def fetch_previous_state(state, project):
|
60
|
+
def fetch_previous_state(state: State, project: str) -> dict[str, dict]:
|
27
61
|
return state.get(project, {})
|
28
62
|
|
29
63
|
|
30
|
-
def format_message(
|
64
|
+
def format_message(
|
65
|
+
server: str,
|
66
|
+
key: str,
|
67
|
+
data: Mapping,
|
68
|
+
event: str,
|
69
|
+
previous_state: Optional[Mapping] = None,
|
70
|
+
current_state: Optional[Mapping] = None,
|
71
|
+
) -> str:
|
31
72
|
summary = data["summary"]
|
32
73
|
info = (
|
33
74
|
": {} -> {}".format(previous_state["status"], current_state["status"])
|
@@ -38,8 +79,12 @@ def format_message(server, key, data, event, previous_state=None, current_state=
|
|
38
79
|
return "{} ({}) {}{}".format(url, summary, event, info)
|
39
80
|
|
40
81
|
|
41
|
-
def calculate_diff(
|
42
|
-
|
82
|
+
def calculate_diff(
|
83
|
+
server: str,
|
84
|
+
current_state: Mapping[str, Mapping],
|
85
|
+
previous_state: Mapping[str, Mapping],
|
86
|
+
) -> list[str]:
|
87
|
+
messages: list[str] = []
|
43
88
|
new_issues = [
|
44
89
|
format_message(server, key, data, "created")
|
45
90
|
for key, data in current_state.items()
|
@@ -66,52 +111,74 @@ def calculate_diff(server, current_state, previous_state):
|
|
66
111
|
return messages
|
67
112
|
|
68
113
|
|
69
|
-
def init_slack(
|
70
|
-
secret_reader = SecretReader(queries.get_secret_reader_settings())
|
71
|
-
slack_info = jira_board["slack"]
|
72
|
-
|
114
|
+
def init_slack(slack: SlackOutputV1, secret_reader: SecretReaderBase) -> SlackApi:
|
73
115
|
return slackapi_from_slack_workspace(
|
74
|
-
|
116
|
+
slack.dict(by_alias=True),
|
75
117
|
secret_reader,
|
76
118
|
QONTRACT_INTEGRATION,
|
77
|
-
channel=
|
119
|
+
channel=slack.channel,
|
78
120
|
init_usergroups=False,
|
79
121
|
)
|
80
122
|
|
81
123
|
|
82
|
-
def act(
|
124
|
+
def act(
|
125
|
+
dry_run: bool,
|
126
|
+
slack: SlackOutputV1,
|
127
|
+
diffs: Iterable[str],
|
128
|
+
secret_reader: SecretReaderBase,
|
129
|
+
) -> None:
|
130
|
+
slack_api: Optional[SlackApi] = None
|
83
131
|
if not dry_run and diffs:
|
84
|
-
|
132
|
+
slack_api = init_slack(slack=slack, secret_reader=secret_reader)
|
85
133
|
|
86
|
-
for diff in reversed(diffs):
|
134
|
+
for diff in reversed(list(diffs)):
|
87
135
|
logging.info(diff)
|
88
136
|
if not dry_run:
|
89
|
-
|
137
|
+
if not slack_api:
|
138
|
+
raise RuntimeError("Slack API not initialized")
|
139
|
+
slack_api.chat_post_message(diff)
|
90
140
|
|
91
141
|
|
92
|
-
def write_state(
|
142
|
+
def write_state(
|
143
|
+
state: State, project: str, state_to_write: Mapping[str, Mapping]
|
144
|
+
) -> None:
|
93
145
|
state.add(project, value=state_to_write, force=True)
|
94
146
|
|
95
147
|
|
96
148
|
@defer
|
97
|
-
def run(dry_run, defer):
|
98
|
-
|
99
|
-
|
149
|
+
def run(dry_run: bool, defer: Optional[Callable]) -> None:
|
150
|
+
gql_api = gql.get_api()
|
151
|
+
vault_settings = get_app_interface_vault_settings()
|
152
|
+
secret_reader = create_secret_reader(use_vault=vault_settings.vault)
|
153
|
+
jira_boards = query_jira_boards(query_func=gql_api.query).jira_boards or []
|
154
|
+
settings = get_jira_settings(gql_api=gql_api)
|
100
155
|
state = init_state(integration=QONTRACT_INTEGRATION)
|
101
|
-
defer
|
156
|
+
if defer:
|
157
|
+
defer(state.cleanup)
|
102
158
|
for index, jira_board in enumerate(jira_boards):
|
103
|
-
if not
|
159
|
+
if not jira_board.slack:
|
160
|
+
continue
|
161
|
+
if not is_in_shard_round_robin(jira_board.name, index):
|
104
162
|
continue
|
105
|
-
jira, current_state = fetch_current_state(
|
163
|
+
jira, current_state = fetch_current_state(
|
164
|
+
jira_board=jira_board, settings=settings, secret_reader=secret_reader
|
165
|
+
)
|
106
166
|
if not current_state:
|
107
167
|
logging.warning(
|
108
168
|
"not acting on empty Jira boards. "
|
109
169
|
+ "please create a ticket to get started."
|
110
170
|
)
|
111
171
|
continue
|
112
|
-
previous_state = fetch_previous_state(state, jira.project)
|
172
|
+
previous_state = fetch_previous_state(state=state, project=jira.project)
|
113
173
|
if previous_state:
|
114
|
-
diffs = calculate_diff(
|
115
|
-
|
174
|
+
diffs = calculate_diff(
|
175
|
+
jira_board.server.server_url, current_state, previous_state
|
176
|
+
)
|
177
|
+
act(
|
178
|
+
dry_run=dry_run,
|
179
|
+
slack=jira_board.slack,
|
180
|
+
diffs=diffs,
|
181
|
+
secret_reader=secret_reader,
|
182
|
+
)
|
116
183
|
if not dry_run:
|
117
184
|
write_state(state, jira.project, current_state)
|
reconcile/queries.py
CHANGED
@@ -2171,62 +2171,6 @@ def get_pipelines_providers():
|
|
2171
2171
|
return pipelines_providers
|
2172
2172
|
|
2173
2173
|
|
2174
|
-
JIRA_BOARDS_QUERY = """
|
2175
|
-
{
|
2176
|
-
jira_boards: jira_boards_v1 {
|
2177
|
-
path
|
2178
|
-
name
|
2179
|
-
server {
|
2180
|
-
serverUrl
|
2181
|
-
token {
|
2182
|
-
path
|
2183
|
-
field
|
2184
|
-
version
|
2185
|
-
format
|
2186
|
-
}
|
2187
|
-
}
|
2188
|
-
{% if with_slack %}
|
2189
|
-
slack {
|
2190
|
-
workspace {
|
2191
|
-
name
|
2192
|
-
integrations {
|
2193
|
-
name
|
2194
|
-
token {
|
2195
|
-
path
|
2196
|
-
field
|
2197
|
-
version
|
2198
|
-
format
|
2199
|
-
}
|
2200
|
-
channel
|
2201
|
-
icon_emoji
|
2202
|
-
username
|
2203
|
-
}
|
2204
|
-
api_client {
|
2205
|
-
global {
|
2206
|
-
max_retries
|
2207
|
-
timeout
|
2208
|
-
}
|
2209
|
-
methods {
|
2210
|
-
name
|
2211
|
-
args
|
2212
|
-
}
|
2213
|
-
}
|
2214
|
-
}
|
2215
|
-
channel
|
2216
|
-
}
|
2217
|
-
{% endif %}
|
2218
|
-
}
|
2219
|
-
}
|
2220
|
-
"""
|
2221
|
-
|
2222
|
-
|
2223
|
-
def get_jira_boards(with_slack: Optional[bool] = True):
|
2224
|
-
"""Returns Jira boards resources defined in app-interface"""
|
2225
|
-
gqlapi = gql.get_api()
|
2226
|
-
query = Template(JIRA_BOARDS_QUERY).render(with_slack=with_slack)
|
2227
|
-
return gqlapi.query(query)["jira_boards"]
|
2228
|
-
|
2229
|
-
|
2230
2174
|
# Use APATH as the place holder because Python formatting interferes
|
2231
2175
|
# with graphql use of curly braces
|
2232
2176
|
JIRA_BOARDS_QUICK_QUERY = """
|
reconcile/utils/jira_client.py
CHANGED
@@ -89,23 +89,17 @@ class OCMBaseClient:
|
|
89
89
|
params_copy = params.copy()
|
90
90
|
params_copy["size"] = max_page_size
|
91
91
|
|
92
|
-
# fetch pages
|
93
|
-
records_seen = 0
|
94
92
|
while True:
|
95
|
-
rs = self.get(api_path, params=
|
93
|
+
rs = self.get(api_path, params=params_copy)
|
96
94
|
for item in rs.get("items", []):
|
97
95
|
yield item
|
98
|
-
total_records = rs.get("total", 0)
|
99
96
|
current_page = rs.get("page", 0)
|
100
97
|
records_on_page = rs.get("size", len(rs.get("items", [])))
|
101
|
-
|
102
|
-
if total_records > records_seen and (
|
103
|
-
max_pages and max_pages < current_page
|
104
|
-
):
|
105
|
-
# more page available
|
106
|
-
params_copy["page"] = current_page + 1
|
107
|
-
else:
|
98
|
+
if records_on_page < max_page_size:
|
108
99
|
return
|
100
|
+
if max_pages is not None and current_page >= max_pages:
|
101
|
+
return
|
102
|
+
params_copy["page"] = current_page + 1
|
109
103
|
|
110
104
|
def post(
|
111
105
|
self,
|
File without changes
|
File without changes
|
{qontract_reconcile-0.10.1rc193.dist-info → qontract_reconcile-0.10.1rc195.dist-info}/top_level.txt
RENAMED
File without changes
|