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.
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: qontract-reconcile
3
- Version: 0.10.1rc609
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
@@ -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=9x1yiHHws4Zm38u4X00HdryqCsUWBnUX6w1_6hjNbfY,13866
642
- reconcile/utils/jobcontroller/models.py,sha256=N1C422MMWp4EvbMbLv1X9EbMq_bIMXQPmRl7MEYZqhQ,4947
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=zuXBRh-cJK4yOPjtiSRbUQRURemFiLlWdjj3XteuQ-4,10475
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.1rc609.dist-info/METADATA,sha256=oL9FPRy78cqHHZ7uyBOLtfr24QUHtZ_mv0Vc-ElwUOg,2382
716
- qontract_reconcile-0.10.1rc609.dist-info/WHEEL,sha256=oiQVh_5PnQM0E3gPdiz09WCNmwiHDMaGer_elqB3coM,92
717
- qontract_reconcile-0.10.1rc609.dist-info/entry_points.txt,sha256=rIxI5zWtHNlfpDeq1a7pZXAPoqf7HG32KMTN3MeWK_8,429
718
- qontract_reconcile-0.10.1rc609.dist-info/top_level.txt,sha256=l5ISPoXzt0SdR4jVdkfa7RPSKNc8zAHYWAnR-Dw8Ey8,24
719
- qontract_reconcile-0.10.1rc609.dist-info/RECORD,,
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
- if not secret_data:
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=secret_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
+ )
@@ -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(self, max_lines: int = 5) -> list[str]:
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(self, max_lines: int = 5) -> list[str]:
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(max_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", "-c"],
227
- args=[self.cmd],
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
  ),