qontract-reconcile 0.10.1rc609__py3-none-any.whl → 0.10.1rc611__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.1rc609.dist-info → qontract_reconcile-0.10.1rc611.dist-info}/METADATA +1 -1
- {qontract_reconcile-0.10.1rc609.dist-info → qontract_reconcile-0.10.1rc611.dist-info}/RECORD +8 -8
- reconcile/utils/jobcontroller/controller.py +10 -2
- reconcile/utils/jobcontroller/models.py +46 -0
- reconcile/utils/rosa/rosa_cli.py +31 -5
- {qontract_reconcile-0.10.1rc609.dist-info → qontract_reconcile-0.10.1rc611.dist-info}/WHEEL +0 -0
- {qontract_reconcile-0.10.1rc609.dist-info → qontract_reconcile-0.10.1rc611.dist-info}/entry_points.txt +0 -0
- {qontract_reconcile-0.10.1rc609.dist-info → qontract_reconcile-0.10.1rc611.dist-info}/top_level.txt +0 -0
{qontract_reconcile-0.10.1rc609.dist-info → qontract_reconcile-0.10.1rc611.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.1rc611
|
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.1rc609.dist-info → qontract_reconcile-0.10.1rc611.dist-info}/RECORD
RENAMED
@@ -638,8 +638,8 @@ reconcile/utils/jinja2/extensions.py,sha256=zV_x8MhSHAynKhFnG3fULXrwsm5fUG_88Iyg
|
|
638
638
|
reconcile/utils/jinja2/filters.py,sha256=_kJjdMsY3lGS5PUn4NnpXUQDNrL1IwiKsB-0MhTMGYM,4521
|
639
639
|
reconcile/utils/jinja2/utils.py,sha256=1TV_BoKrCFxVPbRY4hhUmbasrPa_wm9AoabCEIOXRLg,5947
|
640
640
|
reconcile/utils/jobcontroller/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
641
|
-
reconcile/utils/jobcontroller/controller.py,sha256=
|
642
|
-
reconcile/utils/jobcontroller/models.py,sha256=
|
641
|
+
reconcile/utils/jobcontroller/controller.py,sha256=BeGm2KNfVnxPuQkfIFy3Tp9H_NSpoAu-gy40elUgvxs,14181
|
642
|
+
reconcile/utils/jobcontroller/models.py,sha256=z0gRJy8Ow1leYS-GrvTUv-t-mQDPHTJVDoHb1nVcZ_8,6349
|
643
643
|
reconcile/utils/membershipsources/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
644
644
|
reconcile/utils/membershipsources/app_interface_resolver.py,sha256=IlDiRtJZ0AfAGKEawybB6SvsKbm1POTXL6fpEt699E0,1979
|
645
645
|
reconcile/utils/membershipsources/models.py,sha256=IFu6KHFe-HUTJPiAO3fEw7i22yv4_ytgBW-h_wrO6V4,2015
|
@@ -671,7 +671,7 @@ reconcile/utils/ocm/subscriptions.py,sha256=4qSH8uF9JvFxiecVSV1Kh8-mPcGSTfTTK7In
|
|
671
671
|
reconcile/utils/ocm/syncsets.py,sha256=zvji9qWvInIRTdoybMaM9-VUhq4L1VewWfWCQmp4urc,940
|
672
672
|
reconcile/utils/ocm/upgrades.py,sha256=ZWDfg3VrmRGx7Re-JjecRjwmn7Rh-dsuLA3OljbCByg,6616
|
673
673
|
reconcile/utils/rosa/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
674
|
-
reconcile/utils/rosa/rosa_cli.py,sha256=
|
674
|
+
reconcile/utils/rosa/rosa_cli.py,sha256=b7V3EgO4b4hOnvZzrxdn-bxRbkINQysOg_ecuB8l5GA,11364
|
675
675
|
reconcile/utils/rosa/session.py,sha256=nPEmSNos0_ATQEVg2KckAlVyIEBvxUVo7pa0WMACoOU,4150
|
676
676
|
reconcile/utils/runtime/__init__.py,sha256=sfk92MGfsBh9tKYHl_FH17NdEsrGBwgDFTb7KNKoIfY,107
|
677
677
|
reconcile/utils/runtime/desired_state_diff.py,sha256=finZnWoVDCiYTgu4lGk8G4QOFAGgiIDhD3fcnVqYoxM,8108
|
@@ -712,8 +712,8 @@ tools/test/test_app_interface_metrics_exporter.py,sha256=SX7qL3D1SIRKFo95FoQztvf
|
|
712
712
|
tools/test/test_qontract_cli.py,sha256=OvalpVRfY4pNmpMaWHHYqBjV68b1eGQjX8SCyTAXb1w,3501
|
713
713
|
tools/test/test_sd_app_sre_alert_report.py,sha256=v363r9zM7__0kR5K6mvJoGFcM9BvE33fWAayrqkpojA,2116
|
714
714
|
tools/test/test_sre_checkpoints.py,sha256=SKqPPTl9ua0RFdSSofnoQX-JZE6dFLO3LRhfQzqtfh8,2607
|
715
|
-
qontract_reconcile-0.10.
|
716
|
-
qontract_reconcile-0.10.
|
717
|
-
qontract_reconcile-0.10.
|
718
|
-
qontract_reconcile-0.10.
|
719
|
-
qontract_reconcile-0.10.
|
715
|
+
qontract_reconcile-0.10.1rc611.dist-info/METADATA,sha256=re9XFwEechJsNUfAK4s4FAFgp4lnj3LKjF7hvvHN6bw,2382
|
716
|
+
qontract_reconcile-0.10.1rc611.dist-info/WHEEL,sha256=oiQVh_5PnQM0E3gPdiz09WCNmwiHDMaGer_elqB3coM,92
|
717
|
+
qontract_reconcile-0.10.1rc611.dist-info/entry_points.txt,sha256=rIxI5zWtHNlfpDeq1a7pZXAPoqf7HG32KMTN3MeWK_8,429
|
718
|
+
qontract_reconcile-0.10.1rc611.dist-info/top_level.txt,sha256=l5ISPoXzt0SdR4jVdkfa7RPSKNc8zAHYWAnR-Dw8Ey8,24
|
719
|
+
qontract_reconcile-0.10.1rc611.dist-info/RECORD,,
|
@@ -258,8 +258,16 @@ class K8sJobController:
|
|
258
258
|
|
259
259
|
def build_secret(self, job: K8sJob) -> Optional[V1Secret]:
|
260
260
|
secret_data = job.secret_data()
|
261
|
-
|
261
|
+
script_data = job.scripts()
|
262
|
+
# fail if both dicts have overlapping keys
|
263
|
+
if not set(secret_data).isdisjoint(script_data):
|
264
|
+
raise JobValidationError(
|
265
|
+
f"Secret data and script data have overlapping keys for {job.name()}"
|
266
|
+
)
|
267
|
+
data = {**secret_data, **script_data}
|
268
|
+
if not data:
|
262
269
|
return None
|
270
|
+
|
263
271
|
job_name = job.name()
|
264
272
|
job_uid = self._lookup_job_uid(job_name)
|
265
273
|
if not job_uid:
|
@@ -280,7 +288,7 @@ class K8sJobController:
|
|
280
288
|
)
|
281
289
|
],
|
282
290
|
),
|
283
|
-
string_data=
|
291
|
+
string_data=data,
|
284
292
|
)
|
285
293
|
|
286
294
|
def create_job(self, job: K8sJob) -> None:
|
@@ -10,8 +10,12 @@ from kubernetes.client import (
|
|
10
10
|
V1EnvVarSource,
|
11
11
|
V1Job,
|
12
12
|
V1JobSpec,
|
13
|
+
V1KeyToPath,
|
13
14
|
V1ObjectMeta,
|
14
15
|
V1SecretKeySelector,
|
16
|
+
V1SecretVolumeSource,
|
17
|
+
V1Volume,
|
18
|
+
V1VolumeMount,
|
15
19
|
)
|
16
20
|
|
17
21
|
|
@@ -133,7 +137,18 @@ class K8sJob(ABC):
|
|
133
137
|
"""
|
134
138
|
return {}
|
135
139
|
|
140
|
+
def scripts(self) -> dict[str, str]:
|
141
|
+
"""
|
142
|
+
If a job relies on some scripts, it should return them here. The
|
143
|
+
job controller will manage the lifecycle of a kubernetes Secret containing them.
|
144
|
+
"""
|
145
|
+
return {}
|
146
|
+
|
136
147
|
def secret_data_to_env_vars_secret_refs(self) -> list[V1EnvVar]:
|
148
|
+
"""
|
149
|
+
Helper function to generate env var references from the `secret_data`,
|
150
|
+
to be used in container specs
|
151
|
+
"""
|
137
152
|
secret_name = self.name()
|
138
153
|
return [
|
139
154
|
V1EnvVar(
|
@@ -147,3 +162,34 @@ class K8sJob(ABC):
|
|
147
162
|
)
|
148
163
|
for secret_key_name in self.secret_data().keys()
|
149
164
|
]
|
165
|
+
|
166
|
+
def scripts_volume_mount(self, directory: str) -> V1VolumeMount:
|
167
|
+
"""
|
168
|
+
Helper function to generate a volume mount for the `scripts` to be used
|
169
|
+
in container specs
|
170
|
+
"""
|
171
|
+
secret_name = self.name()
|
172
|
+
return V1VolumeMount(
|
173
|
+
name=secret_name,
|
174
|
+
mount_path=directory,
|
175
|
+
)
|
176
|
+
|
177
|
+
def scripts_volume(self) -> V1Volume:
|
178
|
+
"""
|
179
|
+
Helper function to generate a volume for the `scripts` to be used in
|
180
|
+
pod specs
|
181
|
+
"""
|
182
|
+
secret_name = self.name()
|
183
|
+
return V1Volume(
|
184
|
+
name=secret_name,
|
185
|
+
secret=V1SecretVolumeSource(
|
186
|
+
secret_name=secret_name,
|
187
|
+
items=[
|
188
|
+
V1KeyToPath(
|
189
|
+
key=script_name,
|
190
|
+
path=script_name,
|
191
|
+
)
|
192
|
+
for script_name in self.scripts().keys()
|
193
|
+
],
|
194
|
+
),
|
195
|
+
)
|
reconcile/utils/rosa/rosa_cli.py
CHANGED
@@ -1,6 +1,8 @@
|
|
1
|
+
import collections
|
1
2
|
import itertools
|
2
3
|
import os
|
3
4
|
import textwrap
|
5
|
+
from collections.abc import Iterable
|
4
6
|
from typing import Any, Callable, Optional
|
5
7
|
|
6
8
|
from kubernetes.client import (
|
@@ -22,6 +24,9 @@ from pydantic import BaseModel
|
|
22
24
|
|
23
25
|
from reconcile.utils.jobcontroller.models import JobStatus, K8sJob
|
24
26
|
|
27
|
+
SCRIPTS_MOUNT_PATH = "/scripts"
|
28
|
+
EXEC_SCRIPT = "execute.sh"
|
29
|
+
|
25
30
|
|
26
31
|
class LogHandle:
|
27
32
|
"""
|
@@ -32,10 +37,14 @@ class LogHandle:
|
|
32
37
|
def __init__(self, log_file: str) -> None:
|
33
38
|
self.log_file = log_file
|
34
39
|
|
35
|
-
def get_log_lines(
|
40
|
+
def get_log_lines(
|
41
|
+
self, max_lines: int = 5, from_file_end: bool = False
|
42
|
+
) -> Iterable[str]:
|
36
43
|
if max_lines <= 0:
|
37
44
|
return []
|
38
45
|
with open(self.log_file, "r", encoding="utf-8") as f:
|
46
|
+
if from_file_end:
|
47
|
+
return collections.deque(f, maxlen=max_lines)
|
39
48
|
return [line.rstrip() for line in itertools.islice(f, max_lines)]
|
40
49
|
|
41
50
|
def write_logs_to_logger(self, logger: Callable[..., None]) -> None:
|
@@ -64,15 +73,23 @@ class RosaCliResult:
|
|
64
73
|
self.command = command
|
65
74
|
self.log_handle = log_handle
|
66
75
|
|
67
|
-
def get_log_lines(
|
76
|
+
def get_log_lines(
|
77
|
+
self, max_lines: int = 5, from_file_end: bool = False
|
78
|
+
) -> Iterable[str]:
|
68
79
|
if self.log_handle:
|
69
|
-
return self.log_handle.get_log_lines(
|
80
|
+
return self.log_handle.get_log_lines(
|
81
|
+
max_lines=max_lines, from_file_end=from_file_end
|
82
|
+
)
|
70
83
|
return []
|
71
84
|
|
72
85
|
def write_logs_to_logger(self, logger: Callable[..., None]) -> None:
|
73
86
|
if self.log_handle:
|
74
87
|
self.log_handle.write_logs_to_logger(logger)
|
75
88
|
|
89
|
+
def cleanup(self) -> None:
|
90
|
+
if self.log_handle:
|
91
|
+
self.log_handle.cleanup()
|
92
|
+
|
76
93
|
|
77
94
|
class RosaCliException(Exception, RosaCliResult):
|
78
95
|
"""
|
@@ -135,6 +152,9 @@ class RosaJob(K8sJob, BaseModel, frozen=True, arbitrary_types_allowed=True):
|
|
135
152
|
def secret_data(self) -> dict[str, str]:
|
136
153
|
return {"OCM_TOKEN": self.ocm_token}
|
137
154
|
|
155
|
+
def scripts(self) -> dict[str, str]:
|
156
|
+
return {EXEC_SCRIPT: self.cmd}
|
157
|
+
|
138
158
|
def assume_role_arn(self) -> str:
|
139
159
|
return f"arn:aws:iam::{self.aws_account_id}:role/{self.aws_iam_role}"
|
140
160
|
|
@@ -223,8 +243,8 @@ class RosaJob(K8sJob, BaseModel, frozen=True, arbitrary_types_allowed=True):
|
|
223
243
|
V1Container(
|
224
244
|
name="rosa-cli",
|
225
245
|
image=self.image,
|
226
|
-
command=["/bin/bash"
|
227
|
-
args=[
|
246
|
+
command=["/bin/bash"],
|
247
|
+
args=[f"{SCRIPTS_MOUNT_PATH}/{EXEC_SCRIPT}"],
|
228
248
|
env=[
|
229
249
|
V1EnvVar(
|
230
250
|
name="AWS_SHARED_CREDENTIALS_FILE",
|
@@ -241,11 +261,16 @@ class RosaJob(K8sJob, BaseModel, frozen=True, arbitrary_types_allowed=True):
|
|
241
261
|
name="workdir",
|
242
262
|
mount_path="/.config",
|
243
263
|
),
|
264
|
+
V1VolumeMount(
|
265
|
+
name="workdir",
|
266
|
+
mount_path="/.aws",
|
267
|
+
),
|
244
268
|
V1VolumeMount(
|
245
269
|
name="bound-sa-token",
|
246
270
|
mount_path="/var/run/secrets/openshift/serviceaccount",
|
247
271
|
read_only=True,
|
248
272
|
),
|
273
|
+
self.scripts_volume_mount(SCRIPTS_MOUNT_PATH),
|
249
274
|
],
|
250
275
|
)
|
251
276
|
],
|
@@ -278,6 +303,7 @@ class RosaJob(K8sJob, BaseModel, frozen=True, arbitrary_types_allowed=True):
|
|
278
303
|
size_limit="10Mi",
|
279
304
|
),
|
280
305
|
),
|
306
|
+
self.scripts_volume(),
|
281
307
|
],
|
282
308
|
),
|
283
309
|
),
|
File without changes
|
File without changes
|
{qontract_reconcile-0.10.1rc609.dist-info → qontract_reconcile-0.10.1rc611.dist-info}/top_level.txt
RENAMED
File without changes
|