qontract-reconcile 0.10.2.dev245__py3-none-any.whl → 0.10.2.dev247__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.2.dev245.dist-info → qontract_reconcile-0.10.2.dev247.dist-info}/METADATA +1 -1
- {qontract_reconcile-0.10.2.dev245.dist-info → qontract_reconcile-0.10.2.dev247.dist-info}/RECORD +8 -8
- reconcile/utils/jenkins_api.py +68 -34
- reconcile/utils/saasherder/interfaces.py +6 -0
- reconcile/utils/saasherder/models.py +50 -1
- reconcile/utils/saasherder/saasherder.py +106 -77
- {qontract_reconcile-0.10.2.dev245.dist-info → qontract_reconcile-0.10.2.dev247.dist-info}/WHEEL +0 -0
- {qontract_reconcile-0.10.2.dev245.dist-info → qontract_reconcile-0.10.2.dev247.dist-info}/entry_points.txt +0 -0
{qontract_reconcile-0.10.2.dev245.dist-info → qontract_reconcile-0.10.2.dev247.dist-info}/METADATA
RENAMED
@@ -1,6 +1,6 @@
|
|
1
1
|
Metadata-Version: 2.4
|
2
2
|
Name: qontract-reconcile
|
3
|
-
Version: 0.10.2.
|
3
|
+
Version: 0.10.2.dev247
|
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
|
{qontract_reconcile-0.10.2.dev245.dist-info → qontract_reconcile-0.10.2.dev247.dist-info}/RECORD
RENAMED
@@ -616,7 +616,7 @@ reconcile/utils/helm.py,sha256=wC1h0GylhDFeZ6hZEtYy2giAGIIQroaQhkAtURoSlI8,3893
|
|
616
616
|
reconcile/utils/helpers.py,sha256=koyAtYnxsUVx-HIn6GpedcUE-ekz_VtoYDkiZ0iv8ik,1795
|
617
617
|
reconcile/utils/imap_client.py,sha256=h8YDiCSCvroErhpH_-KGYI7Y2WU2Q2oSpuxDFbOkSbY,1989
|
618
618
|
reconcile/utils/instrumented_wrappers.py,sha256=VqT4s0Bdicv224-uSeSaugtHXm-xJ3oSeBiqj0QQRiU,1942
|
619
|
-
reconcile/utils/jenkins_api.py,sha256=
|
619
|
+
reconcile/utils/jenkins_api.py,sha256=jNwdtBtO8DgMW_H8XfqkQs2r4JsLovHe03t5_F3M1xg,7961
|
620
620
|
reconcile/utils/jira_client.py,sha256=jj7E58PGssoCUEhUZpXoVYeyuChjrvjcg1AdNsONXO0,10545
|
621
621
|
reconcile/utils/jjb_client.py,sha256=e5cDeNAeJMGz3sZMJ1KUIMFyLdRet0YnC0Qgj1vTPHc,15239
|
622
622
|
reconcile/utils/jsonpath.py,sha256=wdxOMqR-GMpQf5vRPWRMqAF7bCiXDBkkcFfY2U4j_tk,5536
|
@@ -750,9 +750,9 @@ reconcile/utils/runtime/meta.py,sha256=dWdKS9eHVuowFkTK4lgXJ723vS1y9giOMzePUKnHn
|
|
750
750
|
reconcile/utils/runtime/runner.py,sha256=I30KRrX1UQbHc_Ir1cIZX3OfNSdoHKdnDSPAEB69Ilk,7944
|
751
751
|
reconcile/utils/runtime/sharding.py,sha256=r0ieUtNed7NvknSw6qQrCkKpVXE1shuHGnfFcnpA_k4,16142
|
752
752
|
reconcile/utils/saasherder/__init__.py,sha256=3U8plqMAPRE1kjwZ5YnIsYsggTf4_gS7flRUEuXVBAs,343
|
753
|
-
reconcile/utils/saasherder/interfaces.py,sha256=
|
754
|
-
reconcile/utils/saasherder/models.py,sha256=
|
755
|
-
reconcile/utils/saasherder/saasherder.py,sha256=
|
753
|
+
reconcile/utils/saasherder/interfaces.py,sha256=JEgR9Co2RxCEJZBBzIrcOURHeq1j-QO6rAUY5yNtsRA,9496
|
754
|
+
reconcile/utils/saasherder/models.py,sha256=uaurkt150llOAPvuZUBoG4VonOt94Ep1BO1AA6Tmjbc,12405
|
755
|
+
reconcile/utils/saasherder/saasherder.py,sha256=p87JKuQYzqjMQg62GJzqT9S0909XTWtmM7naFNXiG1w,92546
|
756
756
|
reconcile/utils/terraform/__init__.py,sha256=zNbiyTWo35AT1sFTElL2j_AA0jJ_yWE_bfFn-nD2xik,250
|
757
757
|
reconcile/utils/terraform/config.py,sha256=5UVrd563TMcvi4ooa5JvWVDW1I3bIWg484u79evfV_8,164
|
758
758
|
reconcile/utils/terraform/config_client.py,sha256=gRL1rQ0AqvShei_rcGqC3HDYGskOFKE1nPrJyJE9yno,4676
|
@@ -798,7 +798,7 @@ tools/saas_promotion_state/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJ
|
|
798
798
|
tools/saas_promotion_state/saas_promotion_state.py,sha256=UfwwRLS5Ya4_Nh1w5n1dvoYtchQvYE9yj1VANt2IKqI,3925
|
799
799
|
tools/sre_checkpoints/__init__.py,sha256=CDaDaywJnmRCLyl_NCcvxi-Zc0hTi_3OdwKiFOyS39I,145
|
800
800
|
tools/sre_checkpoints/util.py,sha256=zEDbGr18ZeHNQwW8pUsr2JRjuXIPz--WAGJxZo9sv_Y,894
|
801
|
-
qontract_reconcile-0.10.2.
|
802
|
-
qontract_reconcile-0.10.2.
|
803
|
-
qontract_reconcile-0.10.2.
|
804
|
-
qontract_reconcile-0.10.2.
|
801
|
+
qontract_reconcile-0.10.2.dev247.dist-info/METADATA,sha256=cWYfGDu3xHx3tih9Mz3sdUUtbrXc1Qks-MmS9n0J7Ps,24049
|
802
|
+
qontract_reconcile-0.10.2.dev247.dist-info/WHEEL,sha256=qtCwoSJWgHk21S1Kb4ihdzI2rlJ1ZKaIurTj_ngOhyQ,87
|
803
|
+
qontract_reconcile-0.10.2.dev247.dist-info/entry_points.txt,sha256=5i9l54La3vQrDLAdwDKQWC0iG4sV9RRfOb1BpvzOWLc,698
|
804
|
+
qontract_reconcile-0.10.2.dev247.dist-info/RECORD,,
|
reconcile/utils/jenkins_api.py
CHANGED
@@ -1,5 +1,6 @@
|
|
1
1
|
import logging
|
2
|
-
from
|
2
|
+
from collections.abc import Mapping
|
3
|
+
from typing import Any, NotRequired, Self, TypedDict
|
3
4
|
|
4
5
|
import requests
|
5
6
|
import toml
|
@@ -9,23 +10,40 @@ from sretoolbox.utils import retry
|
|
9
10
|
from reconcile.utils.secret_reader import SecretReaderBase
|
10
11
|
|
11
12
|
|
13
|
+
class JobBuildState(TypedDict):
|
14
|
+
_class: NotRequired[str]
|
15
|
+
number: int
|
16
|
+
result: NotRequired[str | None]
|
17
|
+
actions: NotRequired[list]
|
18
|
+
commit_sha: NotRequired[str]
|
19
|
+
|
20
|
+
|
12
21
|
class JenkinsApi:
|
13
22
|
"""Wrapper around Jenkins API calls"""
|
14
23
|
|
15
|
-
@
|
24
|
+
@classmethod
|
16
25
|
def init_jenkins_from_secret(
|
17
|
-
|
18
|
-
|
26
|
+
cls,
|
27
|
+
secret_reader: SecretReaderBase,
|
28
|
+
secret: Mapping[str, Any],
|
29
|
+
ssl_verify: bool = True,
|
30
|
+
) -> Self:
|
19
31
|
token_config = secret_reader.read(secret)
|
20
32
|
config = toml.loads(token_config)
|
21
|
-
return
|
33
|
+
return cls(
|
22
34
|
config["jenkins"]["url"],
|
23
35
|
config["jenkins"]["user"],
|
24
36
|
config["jenkins"]["password"],
|
25
37
|
ssl_verify=ssl_verify,
|
26
38
|
)
|
27
39
|
|
28
|
-
def __init__(
|
40
|
+
def __init__(
|
41
|
+
self,
|
42
|
+
url: str,
|
43
|
+
user: str,
|
44
|
+
password: str,
|
45
|
+
ssl_verify: bool = True,
|
46
|
+
):
|
29
47
|
self.url = url
|
30
48
|
self.user = user
|
31
49
|
self.password = password
|
@@ -43,7 +61,7 @@ class JenkinsApi:
|
|
43
61
|
res.raise_for_status()
|
44
62
|
return yaml.safe_load(res.text)
|
45
63
|
|
46
|
-
def apply_jcasc_config(self, config: dict[str, Any]):
|
64
|
+
def apply_jcasc_config(self, config: dict[str, Any]) -> None:
|
47
65
|
url = f"{self.url}/manage/configuration-as-code/apply"
|
48
66
|
res = requests.post(
|
49
67
|
url,
|
@@ -54,7 +72,7 @@ class JenkinsApi:
|
|
54
72
|
)
|
55
73
|
res.raise_for_status()
|
56
74
|
|
57
|
-
def get_job_names(self):
|
75
|
+
def get_job_names(self) -> list[str]:
|
58
76
|
url = f"{self.url}/api/json?tree=jobs[name]"
|
59
77
|
res = requests.get(
|
60
78
|
url, verify=self.ssl_verify, auth=(self.user, self.password), timeout=60
|
@@ -64,36 +82,52 @@ class JenkinsApi:
|
|
64
82
|
job_names = [r["name"] for r in res.json()["jobs"]]
|
65
83
|
return job_names
|
66
84
|
|
85
|
+
@staticmethod
|
86
|
+
def _get_commit_sha_from_build(build: Mapping[str, Any]) -> str | None:
|
87
|
+
for action in reversed(build.get("actions", [])):
|
88
|
+
if revision := action.get("lastBuiltRevision"):
|
89
|
+
return revision["SHA1"]
|
90
|
+
return None
|
91
|
+
|
92
|
+
def _build_job_build_state(self, build: Mapping) -> JobBuildState:
|
93
|
+
job_build_state = JobBuildState(number=build["number"])
|
94
|
+
if "_class" in build:
|
95
|
+
job_build_state["_class"] = build["_class"]
|
96
|
+
if "actions" in build:
|
97
|
+
job_build_state["actions"] = build["actions"]
|
98
|
+
if "result" in build:
|
99
|
+
job_build_state["result"] = build["result"]
|
100
|
+
if commit_sha := self._get_commit_sha_from_build(build):
|
101
|
+
job_build_state["commit_sha"] = commit_sha
|
102
|
+
return job_build_state
|
103
|
+
|
67
104
|
@retry()
|
68
|
-
def get_jobs_state(self):
|
105
|
+
def get_jobs_state(self) -> dict[str, list[JobBuildState]]:
|
69
106
|
url = f"{self.url}/api/json?tree=jobs[name,builds[number,result,actions[lastBuiltRevision[SHA1]]]]"
|
70
107
|
res = requests.get(
|
71
108
|
url, verify=self.ssl_verify, auth=(self.user, self.password), timeout=60
|
72
109
|
)
|
73
110
|
|
74
111
|
res.raise_for_status()
|
75
|
-
|
76
|
-
|
77
|
-
|
78
|
-
|
79
|
-
|
80
|
-
|
81
|
-
|
82
|
-
|
83
|
-
|
84
|
-
|
85
|
-
|
86
|
-
|
87
|
-
return jobs_state
|
88
|
-
|
89
|
-
def delete_build(self, job_name, build_id):
|
112
|
+
jobs = res.json().get("jobs") or []
|
113
|
+
return {
|
114
|
+
job["name"]: list(
|
115
|
+
map(
|
116
|
+
self._build_job_build_state,
|
117
|
+
job.get("builds", []),
|
118
|
+
)
|
119
|
+
)
|
120
|
+
for job in jobs
|
121
|
+
}
|
122
|
+
|
123
|
+
def delete_build(self, job_name: str, build_id: str) -> None:
|
90
124
|
url = f"{self.url}/job/{job_name}/{build_id}/doDelete"
|
91
125
|
res = requests.post(
|
92
126
|
url, verify=self.ssl_verify, auth=(self.user, self.password), timeout=60
|
93
127
|
)
|
94
128
|
res.raise_for_status()
|
95
129
|
|
96
|
-
def get_all_roles(self):
|
130
|
+
def get_all_roles(self) -> dict[str, Any]:
|
97
131
|
url = f"{self.url}/role-strategy/strategy/getAllRoles"
|
98
132
|
res = requests.get(
|
99
133
|
url, verify=self.ssl_verify, auth=(self.user, self.password), timeout=60
|
@@ -102,7 +136,7 @@ class JenkinsApi:
|
|
102
136
|
res.raise_for_status()
|
103
137
|
return res.json()
|
104
138
|
|
105
|
-
def assign_role_to_user(self, role, user):
|
139
|
+
def assign_role_to_user(self, role: str, user: str) -> None:
|
106
140
|
url = f"{self.url}/role-strategy/strategy/assignRole"
|
107
141
|
data = {"type": "globalRoles", "roleName": role, "sid": user}
|
108
142
|
res = requests.post(
|
@@ -115,7 +149,7 @@ class JenkinsApi:
|
|
115
149
|
|
116
150
|
res.raise_for_status()
|
117
151
|
|
118
|
-
def unassign_role_from_user(self, role, user):
|
152
|
+
def unassign_role_from_user(self, role: str, user: str) -> None:
|
119
153
|
url = f"{self.url}/role-strategy/strategy/unassignRole"
|
120
154
|
data = {"type": "globalRoles", "roleName": role, "sid": user}
|
121
155
|
res = requests.post(
|
@@ -128,7 +162,7 @@ class JenkinsApi:
|
|
128
162
|
|
129
163
|
res.raise_for_status()
|
130
164
|
|
131
|
-
def safe_restart(self, force_restart=False):
|
165
|
+
def safe_restart(self, force_restart: bool = False) -> None:
|
132
166
|
url = f"{self.url}/safeRestart"
|
133
167
|
if self.should_restart or force_restart:
|
134
168
|
logging.debug(
|
@@ -142,7 +176,7 @@ class JenkinsApi:
|
|
142
176
|
|
143
177
|
res.raise_for_status()
|
144
178
|
|
145
|
-
def get_builds(self, job_name):
|
179
|
+
def get_builds(self, job_name: str) -> list[dict[str, Any]]:
|
146
180
|
url = (
|
147
181
|
f"{self.url}/job/{job_name}/api/json"
|
148
182
|
+ "?tree=allBuilds[timestamp,result,id]"
|
@@ -157,14 +191,14 @@ class JenkinsApi:
|
|
157
191
|
except KeyError:
|
158
192
|
return []
|
159
193
|
|
160
|
-
def get_build_history(self, job_name, time_limit):
|
194
|
+
def get_build_history(self, job_name: str, time_limit: int) -> list[str]:
|
161
195
|
return [
|
162
196
|
b["result"]
|
163
197
|
for b in self.get_builds(job_name)
|
164
198
|
if time_limit < self.timestamp_seconds(b["timestamp"])
|
165
199
|
]
|
166
200
|
|
167
|
-
def is_job_running(self, job_name):
|
201
|
+
def is_job_running(self, job_name: str) -> bool:
|
168
202
|
url = f"{self.url}/job/{job_name}/lastBuild/api/json"
|
169
203
|
res = requests.get(
|
170
204
|
url, verify=self.ssl_verify, auth=(self.user, self.password), timeout=60
|
@@ -178,7 +212,7 @@ class JenkinsApi:
|
|
178
212
|
res.raise_for_status()
|
179
213
|
return res.json()["building"] is True
|
180
214
|
|
181
|
-
def get_crumb_kwargs(self):
|
215
|
+
def get_crumb_kwargs(self) -> dict[str, Any]:
|
182
216
|
try:
|
183
217
|
crumb_url = f"{self.url}/crumbIssuer/api/json"
|
184
218
|
res = requests.get(
|
@@ -197,7 +231,7 @@ class JenkinsApi:
|
|
197
231
|
|
198
232
|
return kwargs
|
199
233
|
|
200
|
-
def trigger_job(self, job_name):
|
234
|
+
def trigger_job(self, job_name: str) -> None:
|
201
235
|
kwargs = self.get_crumb_kwargs()
|
202
236
|
|
203
237
|
url = f"{self.url}/job/{job_name}/build"
|
@@ -212,5 +246,5 @@ class JenkinsApi:
|
|
212
246
|
res.raise_for_status()
|
213
247
|
|
214
248
|
@staticmethod
|
215
|
-
def timestamp_seconds(timestamp):
|
249
|
+
def timestamp_seconds(timestamp: float) -> int:
|
216
250
|
return int(timestamp / 1000)
|
@@ -245,6 +245,8 @@ class SaasResourceTemplateTargetPromotion(Protocol):
|
|
245
245
|
@property
|
246
246
|
def promotion_data(self) -> Sequence[SaasPromotionData] | None: ...
|
247
247
|
|
248
|
+
def dict(self, *, by_alias: bool = False) -> dict[str, Any]: ...
|
249
|
+
|
248
250
|
|
249
251
|
class Channel(Protocol):
|
250
252
|
name: str
|
@@ -289,6 +291,8 @@ class SaasResourceTemplateTargetUpstream(Protocol):
|
|
289
291
|
@property
|
290
292
|
def instance(self) -> SaasJenkinsInstance: ...
|
291
293
|
|
294
|
+
def dict(self, *, by_alias: bool = False) -> dict[str, Any]: ...
|
295
|
+
|
292
296
|
|
293
297
|
class SaasQuayInstance(Protocol):
|
294
298
|
url: str
|
@@ -307,6 +311,8 @@ class SaasResourceTemplateTargetImage(Protocol):
|
|
307
311
|
@property
|
308
312
|
def org(self) -> SaasQuayOrg: ...
|
309
313
|
|
314
|
+
def dict(self, *, by_alias: bool = False) -> dict[str, Any]: ...
|
315
|
+
|
310
316
|
|
311
317
|
class SaasResourceTemplateTarget(HasParameters, HasSecretParameters, Protocol):
|
312
318
|
path: str | None
|
@@ -4,7 +4,7 @@ import logging
|
|
4
4
|
from collections.abc import Iterable, Sequence
|
5
5
|
from dataclasses import dataclass
|
6
6
|
from enum import Enum
|
7
|
-
from typing import Any
|
7
|
+
from typing import Any, NotRequired, TypedDict
|
8
8
|
|
9
9
|
from github import Github
|
10
10
|
from pydantic import (
|
@@ -15,6 +15,7 @@ from pydantic import (
|
|
15
15
|
from reconcile.gql_definitions.fragments.saas_slo_document import (
|
16
16
|
SLODocument,
|
17
17
|
)
|
18
|
+
from reconcile.utils.jenkins_api import JobBuildState
|
18
19
|
from reconcile.utils.oc_connection_parameters import Cluster
|
19
20
|
from reconcile.utils.saasherder.interfaces import (
|
20
21
|
HasParameters,
|
@@ -78,8 +79,53 @@ class SLOKey:
|
|
78
79
|
cluster_name: str
|
79
80
|
|
80
81
|
|
82
|
+
class TriggerSpecConfigStateContentNamespaceApp(TypedDict):
|
83
|
+
name: str
|
84
|
+
|
85
|
+
|
86
|
+
class TriggerSpecConfigStateContentNamespaceCluster(TypedDict):
|
87
|
+
name: str
|
88
|
+
serverUrl: str
|
89
|
+
|
90
|
+
|
91
|
+
class TriggerSpecConfigStateContentNamespace(TypedDict):
|
92
|
+
name: str
|
93
|
+
cluster: TriggerSpecConfigStateContentNamespaceCluster
|
94
|
+
app: TriggerSpecConfigStateContentNamespaceApp
|
95
|
+
|
96
|
+
|
97
|
+
class TriggerSpecConfigStateContent(TypedDict):
|
98
|
+
"""
|
99
|
+
dict representation of reconcile.typed_queries.saas_files.SaasResourceTemplateTarget
|
100
|
+
with some additional fields.
|
101
|
+
"""
|
102
|
+
|
103
|
+
path: str | None
|
104
|
+
name: str | None
|
105
|
+
namespace: TriggerSpecConfigStateContentNamespace
|
106
|
+
ref: str | None
|
107
|
+
promotion: dict | None
|
108
|
+
parameters: str | None
|
109
|
+
secretParameters: list[dict] | None
|
110
|
+
slos: list[Any] | None
|
111
|
+
upstream: Any | None
|
112
|
+
images: list[Any] | None
|
113
|
+
disable: bool | None
|
114
|
+
delete: bool | None
|
115
|
+
|
116
|
+
# additional fields
|
117
|
+
saas_file_parameters: str | None
|
118
|
+
saas_file_managed_resource_types: list[str]
|
119
|
+
saas_file_managed_resource_names: NotRequired[list[Any]]
|
120
|
+
url: str
|
121
|
+
rt_parameters: str | None
|
122
|
+
rt_secretparameters: NotRequired[list[dict]]
|
123
|
+
saas_file_secretparameters: NotRequired[list[dict]]
|
124
|
+
|
125
|
+
|
81
126
|
@dataclass(frozen=True)
|
82
127
|
class TriggerSpecConfig(TriggerSpecBase):
|
128
|
+
state_content: TriggerSpecConfigStateContent | None
|
83
129
|
resource_template_url: str
|
84
130
|
slos: list[SLODocument] | None = None
|
85
131
|
target_name: str | None = None
|
@@ -110,6 +156,7 @@ class TriggerSpecConfig(TriggerSpecBase):
|
|
110
156
|
|
111
157
|
@dataclass(frozen=True)
|
112
158
|
class TriggerSpecMovingCommit(TriggerSpecBase):
|
159
|
+
state_content: str
|
113
160
|
ref: str
|
114
161
|
|
115
162
|
@property
|
@@ -123,6 +170,7 @@ class TriggerSpecMovingCommit(TriggerSpecBase):
|
|
123
170
|
|
124
171
|
@dataclass(frozen=True)
|
125
172
|
class TriggerSpecUpstreamJob(TriggerSpecBase):
|
173
|
+
state_content: JobBuildState
|
126
174
|
instance_name: str
|
127
175
|
job_name: str
|
128
176
|
|
@@ -137,6 +185,7 @@ class TriggerSpecUpstreamJob(TriggerSpecBase):
|
|
137
185
|
|
138
186
|
@dataclass(frozen=True)
|
139
187
|
class TriggerSpecContainerImage(TriggerSpecBase):
|
188
|
+
state_content: str
|
140
189
|
images: Sequence[str]
|
141
190
|
|
142
191
|
@property
|
@@ -6,7 +6,6 @@ import logging
|
|
6
6
|
import os
|
7
7
|
import re
|
8
8
|
from collections import (
|
9
|
-
ChainMap,
|
10
9
|
defaultdict,
|
11
10
|
)
|
12
11
|
from collections.abc import (
|
@@ -40,7 +39,7 @@ from reconcile.status import RunningState
|
|
40
39
|
from reconcile.utils import helm
|
41
40
|
from reconcile.utils.github_api import GithubRepositoryApi
|
42
41
|
from reconcile.utils.gitlab_api import GitLabApi
|
43
|
-
from reconcile.utils.jenkins_api import JenkinsApi
|
42
|
+
from reconcile.utils.jenkins_api import JenkinsApi, JobBuildState
|
44
43
|
from reconcile.utils.jjb_client import JJB
|
45
44
|
from reconcile.utils.oc import (
|
46
45
|
OCLocal,
|
@@ -74,6 +73,10 @@ from reconcile.utils.saasherder.models import (
|
|
74
73
|
SLOKey,
|
75
74
|
TargetSpec,
|
76
75
|
TriggerSpecConfig,
|
76
|
+
TriggerSpecConfigStateContent,
|
77
|
+
TriggerSpecConfigStateContentNamespace,
|
78
|
+
TriggerSpecConfigStateContentNamespaceApp,
|
79
|
+
TriggerSpecConfigStateContentNamespaceCluster,
|
77
80
|
TriggerSpecContainerImage,
|
78
81
|
TriggerSpecMovingCommit,
|
79
82
|
TriggerSpecUnion,
|
@@ -1516,8 +1519,10 @@ class SaasHerder: # pylint: disable=too-many-public-methods
|
|
1516
1519
|
)
|
1517
1520
|
return list(itertools.chain.from_iterable(results)), error
|
1518
1521
|
|
1519
|
-
def _get_upstream_jobs_current_state(
|
1520
|
-
|
1522
|
+
def _get_upstream_jobs_current_state(
|
1523
|
+
self,
|
1524
|
+
) -> tuple[dict[str, dict[str, list[JobBuildState]]], bool]:
|
1525
|
+
current_state: dict[str, dict[str, list[JobBuildState]]] = {}
|
1521
1526
|
error = False
|
1522
1527
|
if not self.jenkins_map:
|
1523
1528
|
raise Exception("jenkins_map is not initialized")
|
@@ -1534,7 +1539,7 @@ class SaasHerder: # pylint: disable=too-many-public-methods
|
|
1534
1539
|
|
1535
1540
|
def _build_trigger_spec_upstream_job_reason(
|
1536
1541
|
self,
|
1537
|
-
last_build_result:
|
1542
|
+
last_build_result: JobBuildState,
|
1538
1543
|
server_url: str,
|
1539
1544
|
job_name: str,
|
1540
1545
|
url: str,
|
@@ -1551,7 +1556,10 @@ class SaasHerder: # pylint: disable=too-many-public-methods
|
|
1551
1556
|
return f"{prefix}{server_url}/job/{job_name}/{last_build_result_number}"
|
1552
1557
|
|
1553
1558
|
def get_upstream_jobs_diff_saas_file(
|
1554
|
-
self,
|
1559
|
+
self,
|
1560
|
+
saas_file: SaasFile,
|
1561
|
+
dry_run: bool,
|
1562
|
+
current_state: dict[str, dict[str, list[JobBuildState]]],
|
1555
1563
|
) -> list[TriggerSpecUpstreamJob]:
|
1556
1564
|
trigger_specs = []
|
1557
1565
|
for rt in saas_file.resource_templates:
|
@@ -1816,7 +1824,7 @@ class SaasHerder: # pylint: disable=too-many-public-methods
|
|
1816
1824
|
return True
|
1817
1825
|
|
1818
1826
|
@staticmethod
|
1819
|
-
def remove_none_values(d:
|
1827
|
+
def remove_none_values(d: Mapping[Any, Any] | None) -> dict[Any, Any]:
|
1820
1828
|
if d is None:
|
1821
1829
|
return {}
|
1822
1830
|
new = {}
|
@@ -1859,7 +1867,7 @@ class SaasHerder: # pylint: disable=too-many-public-methods
|
|
1859
1867
|
|
1860
1868
|
def _build_trigger_spec_config_reason(
|
1861
1869
|
self,
|
1862
|
-
state_content:
|
1870
|
+
state_content: TriggerSpecConfigStateContent,
|
1863
1871
|
) -> str | None:
|
1864
1872
|
if not self.include_trigger_trace:
|
1865
1873
|
return None
|
@@ -1869,76 +1877,95 @@ class SaasHerder: # pylint: disable=too-many-public-methods
|
|
1869
1877
|
# to reduce false-positives.
|
1870
1878
|
auto_promotion_suffix = (
|
1871
1879
|
" [auto-promotion]"
|
1872
|
-
if (state_content
|
1880
|
+
if (state_content["promotion"] or {}).get("auto", False)
|
1873
1881
|
else ""
|
1874
1882
|
)
|
1875
1883
|
return f"{self.repo_url}/commit/{RunningState().commit}{auto_promotion_suffix}"
|
1876
1884
|
|
1885
|
+
def _build_trigger_spec_config_state_content(
|
1886
|
+
self,
|
1887
|
+
target: SaasResourceTemplateTarget,
|
1888
|
+
saas_file: SaasFile,
|
1889
|
+
resource_template: SaasResourceTemplate,
|
1890
|
+
) -> TriggerSpecConfigStateContent:
|
1891
|
+
state_content = TriggerSpecConfigStateContent(
|
1892
|
+
name=target.name,
|
1893
|
+
ref=target.ref,
|
1894
|
+
promotion=(
|
1895
|
+
target.promotion.dict(by_alias=True) if target.promotion else None
|
1896
|
+
),
|
1897
|
+
secretParameters=(
|
1898
|
+
[p.dict(by_alias=True) for p in target.secret_parameters]
|
1899
|
+
if target.secret_parameters
|
1900
|
+
else None
|
1901
|
+
),
|
1902
|
+
slos=(
|
1903
|
+
[slo.dict(by_alias=True) for slo in target.slos]
|
1904
|
+
if target.slos
|
1905
|
+
else None
|
1906
|
+
),
|
1907
|
+
upstream=(target.upstream.dict(by_alias=True) if target.upstream else None),
|
1908
|
+
images=(
|
1909
|
+
[i.dict(by_alias=True) for i in target.images]
|
1910
|
+
if target.images
|
1911
|
+
else None
|
1912
|
+
),
|
1913
|
+
disable=target.disable,
|
1914
|
+
delete=target.delete,
|
1915
|
+
namespace=self.sanitize_namespace(target.namespace),
|
1916
|
+
# add parent parameters to target config
|
1917
|
+
# before the GQL classes are introduced, the parameters attribute
|
1918
|
+
# was a json string. Keep it that way to be backwards compatible.
|
1919
|
+
saas_file_parameters=(
|
1920
|
+
json.dumps(saas_file.parameters, separators=(",", ":"))
|
1921
|
+
if saas_file.parameters is not None
|
1922
|
+
else None
|
1923
|
+
),
|
1924
|
+
# before the GQL classes are introduced, the parameters attribute
|
1925
|
+
# was a json string. Keep it that way to be backwards compatible.
|
1926
|
+
parameters=(
|
1927
|
+
json.dumps(target.parameters, separators=(",", ":"))
|
1928
|
+
if target.parameters is not None
|
1929
|
+
else None
|
1930
|
+
),
|
1931
|
+
# add managed resource types to target config
|
1932
|
+
saas_file_managed_resource_types=saas_file.managed_resource_types,
|
1933
|
+
url=resource_template.url,
|
1934
|
+
path=resource_template.path,
|
1935
|
+
# before the GQL classes are introduced, the parameters attribute
|
1936
|
+
# was a json string. Keep it that way to be backwards compatible.
|
1937
|
+
rt_parameters=(
|
1938
|
+
json.dumps(resource_template.parameters, separators=(",", ":"))
|
1939
|
+
if resource_template.parameters is not None
|
1940
|
+
else None
|
1941
|
+
),
|
1942
|
+
)
|
1943
|
+
if saas_file.managed_resource_names:
|
1944
|
+
state_content["saas_file_managed_resource_names"] = [
|
1945
|
+
m.dict() for m in saas_file.managed_resource_names
|
1946
|
+
]
|
1947
|
+
# include secret parameters from resource template and saas file
|
1948
|
+
if resource_template.secret_parameters:
|
1949
|
+
state_content["rt_secretparameters"] = [
|
1950
|
+
p.dict() for p in resource_template.secret_parameters
|
1951
|
+
]
|
1952
|
+
if saas_file.secret_parameters:
|
1953
|
+
state_content["saas_file_secretparameters"] = [
|
1954
|
+
p.dict() for p in saas_file.secret_parameters
|
1955
|
+
]
|
1956
|
+
return state_content
|
1957
|
+
|
1877
1958
|
def get_saas_targets_config_trigger_specs(
|
1878
1959
|
self, saas_file: SaasFile
|
1879
1960
|
) -> dict[str, TriggerSpecConfig]:
|
1880
1961
|
configs = {}
|
1881
1962
|
for rt in saas_file.resource_templates:
|
1882
1963
|
for target in rt.targets:
|
1883
|
-
|
1884
|
-
|
1885
|
-
|
1886
|
-
|
1887
|
-
# When the namespace key is looked up, the chainmap will
|
1888
|
-
# return the modified attribute (set in the first mapping)
|
1889
|
-
desired_target_config["namespace"] = self.sanitize_namespace(
|
1890
|
-
target.namespace
|
1891
|
-
)
|
1892
|
-
# add parent parameters to target config
|
1893
|
-
# before the GQL classes are introduced, the parameters attribute
|
1894
|
-
# was a json string. Keep it that way to be backwards compatible.
|
1895
|
-
desired_target_config["saas_file_parameters"] = (
|
1896
|
-
json.dumps(saas_file.parameters, separators=(",", ":"))
|
1897
|
-
if saas_file.parameters is not None
|
1898
|
-
else None
|
1964
|
+
desired_target_config = self._build_trigger_spec_config_state_content(
|
1965
|
+
target=target,
|
1966
|
+
saas_file=saas_file,
|
1967
|
+
resource_template=rt,
|
1899
1968
|
)
|
1900
|
-
|
1901
|
-
# before the GQL classes are introduced, the parameters attribute
|
1902
|
-
# was a json string. Keep it that way to be backwards compatible.
|
1903
|
-
desired_target_config["parameters"] = (
|
1904
|
-
json.dumps(target.parameters, separators=(",", ":"))
|
1905
|
-
if target.parameters is not None
|
1906
|
-
else None
|
1907
|
-
)
|
1908
|
-
|
1909
|
-
# add managed resource types to target config
|
1910
|
-
desired_target_config["saas_file_managed_resource_types"] = (
|
1911
|
-
saas_file.managed_resource_types
|
1912
|
-
)
|
1913
|
-
if saas_file.managed_resource_names:
|
1914
|
-
desired_target_config["saas_file_managed_resource_names"] = [
|
1915
|
-
m.dict() for m in saas_file.managed_resource_names
|
1916
|
-
]
|
1917
|
-
|
1918
|
-
desired_target_config["url"] = rt.url
|
1919
|
-
desired_target_config["path"] = rt.path
|
1920
|
-
# before the GQL classes are introduced, the parameters attribute
|
1921
|
-
# was a json string. Keep it that way to be backwards compatible.
|
1922
|
-
desired_target_config["rt_parameters"] = (
|
1923
|
-
json.dumps(rt.parameters, separators=(",", ":"))
|
1924
|
-
if rt.parameters is not None
|
1925
|
-
else None
|
1926
|
-
)
|
1927
|
-
|
1928
|
-
# include secret parameters from resource template and saas file
|
1929
|
-
if rt.secret_parameters:
|
1930
|
-
desired_target_config["rt_secretparameters"] = [
|
1931
|
-
p.dict() for p in rt.secret_parameters
|
1932
|
-
]
|
1933
|
-
if saas_file.secret_parameters:
|
1934
|
-
desired_target_config["saas_file_secretparameters"] = [
|
1935
|
-
p.dict() for p in saas_file.secret_parameters
|
1936
|
-
]
|
1937
|
-
|
1938
|
-
# Convert to dict, ChainMap is not JSON serializable
|
1939
|
-
# desired_target_config needs to be serialized to generate
|
1940
|
-
# its config hash and to be stored in S3
|
1941
|
-
serializable_target_config = dict(desired_target_config)
|
1942
1969
|
trigger_spec = TriggerSpecConfig(
|
1943
1970
|
saas_file_name=saas_file.name,
|
1944
1971
|
env_name=target.namespace.environment.name,
|
@@ -1948,12 +1975,12 @@ class SaasHerder: # pylint: disable=too-many-public-methods
|
|
1948
1975
|
cluster_name=target.namespace.cluster.name,
|
1949
1976
|
namespace_name=target.namespace.name,
|
1950
1977
|
target_name=target.name,
|
1951
|
-
state_content=
|
1978
|
+
state_content=desired_target_config,
|
1952
1979
|
resource_template_url=rt.url,
|
1953
1980
|
target_ref=target.ref,
|
1954
1981
|
slos=target.slos or None,
|
1955
1982
|
reason=self._build_trigger_spec_config_reason(
|
1956
|
-
state_content=
|
1983
|
+
state_content=desired_target_config
|
1957
1984
|
),
|
1958
1985
|
)
|
1959
1986
|
configs[trigger_spec.state_key] = trigger_spec
|
@@ -1963,15 +1990,17 @@ class SaasHerder: # pylint: disable=too-many-public-methods
|
|
1963
1990
|
@staticmethod
|
1964
1991
|
def sanitize_namespace(
|
1965
1992
|
namespace: SaasResourceTemplateTargetNamespace,
|
1966
|
-
) ->
|
1993
|
+
) -> TriggerSpecConfigStateContentNamespace:
|
1967
1994
|
"""Only keep fields that should trigger a new job."""
|
1968
|
-
return
|
1969
|
-
|
1970
|
-
|
1971
|
-
|
1972
|
-
|
1973
|
-
|
1974
|
-
|
1995
|
+
return TriggerSpecConfigStateContentNamespace(
|
1996
|
+
name=namespace.name,
|
1997
|
+
cluster=TriggerSpecConfigStateContentNamespaceCluster(
|
1998
|
+
name=namespace.cluster.name,
|
1999
|
+
serverUrl=namespace.cluster.server_url,
|
2000
|
+
),
|
2001
|
+
app=TriggerSpecConfigStateContentNamespaceApp(
|
2002
|
+
name=namespace.app.name,
|
2003
|
+
),
|
1975
2004
|
# TODO: add environment.parameters to the include list!?!?
|
1976
2005
|
)
|
1977
2006
|
|
{qontract_reconcile-0.10.2.dev245.dist-info → qontract_reconcile-0.10.2.dev247.dist-info}/WHEEL
RENAMED
File without changes
|
File without changes
|