qontract-reconcile 0.10.1rc938__py3-none-any.whl → 0.10.1rc940__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.1rc938
3
+ Version: 0.10.1rc940
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
@@ -57,14 +57,14 @@ reconcile/ocm_aws_infrastructure_access.py,sha256=SghVWdmgliPVo_xHsp-e64_iC3mqDi
57
57
  reconcile/ocm_clusters.py,sha256=bD8zlnUbwfDaVvHd-lSDGWmqY1ag_Gcr6kQ0y3aiKG4,16702
58
58
  reconcile/ocm_external_configuration_labels.py,sha256=imEpDv1RBpCSj8tHDv0R76hmNCFtcUzVNgS1yOVl8vs,3870
59
59
  reconcile/ocm_github_idp.py,sha256=glwXMsIBcl38-OmDDQCpe0YoLLXfoRgVQmqwXMEXjds,3946
60
- reconcile/ocm_groups.py,sha256=CluPyvmwE5JOZS2HQSReC1sD8L1ChhnJlAg8lcwdtxc,3395
60
+ reconcile/ocm_groups.py,sha256=-rTPMewkdyo1De6gs4u-294p3z34oUbGfuNi8ov56Sk,3424
61
61
  reconcile/ocm_machine_pools.py,sha256=poGfITOCJEMwYAJpiuL8SytgTcBmGIKEZPgNGld80TY,16563
62
62
  reconcile/ocm_update_recommended_version.py,sha256=IYkfLXIprOW1jguZeELcGP1iBPuj-b53R-FTqKulMl8,4204
63
63
  reconcile/ocm_upgrade_scheduler_org_updater.py,sha256=aLgyInt9oIWAg0XtCiwJRUSwdPx3masKV8kHzkyEEOQ,4282
64
64
  reconcile/openshift_base.py,sha256=MvydL1Qq6g_mex_EOWDFFuckt8VtE3LfHW0mhWkUJFs,49712
65
65
  reconcile/openshift_cluster_bots.py,sha256=eRPYZqWMKFNxLlSN0QG97V5t1iIESQ0BbGaiaQP5VB0,10940
66
66
  reconcile/openshift_clusterrolebindings.py,sha256=QfSy1Ik8eEY5XObc1Q4xyhqyErZenJmbPv_u9wcDNNo,5864
67
- reconcile/openshift_groups.py,sha256=wb_J5hvfpgS5RTeeZDPiQ_zvwbajGYysFNP603YNioE,9313
67
+ reconcile/openshift_groups.py,sha256=sK2wLWwNupztbfyFPl32VH42s_s8Mu3g-URdlisnwJc,9382
68
68
  reconcile/openshift_limitranges.py,sha256=UvCGo_OQ4XoDK55TJmn55qEhhlkhLzhU12tX8nT5kPQ,3442
69
69
  reconcile/openshift_namespace_labels.py,sha256=pqKAWjNicCQBoq7CwQI-dpW2dcy9sFZl-rBiO2m-e0k,15605
70
70
  reconcile/openshift_namespaces.py,sha256=DdQ4a76g0E7dgRPZEA8MTtZthzUJQjWAb5VdspArWtE,5795
@@ -461,11 +461,11 @@ reconcile/templates/jira-checkpoint-missinginfo.j2,sha256=c_Vvg-lEENsB3tgxm9B6Y9
461
461
  reconcile/templates/rosa-classic-cluster-creation.sh.j2,sha256=zqMMlKWV-aUDMtA-Xu5kl5pYLilewDClnKM0c807nNA,2146
462
462
  reconcile/templates/rosa-hcp-cluster-creation.sh.j2,sha256=DdnABKA-vu2x420JBBrr200bK8GE62GSzY9lBr2k7v8,2178
463
463
  reconcile/templating/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
464
- reconcile/templating/renderer.py,sha256=4Uo0HHRhXyXQNCMAEv3MiSL_R_B_DQ3MD7wtYcDzL9w,12951
464
+ reconcile/templating/renderer.py,sha256=rcaGQ8sQjsAyWJKjsOFgKhLEUnPvJAh7eDAJBHHSQMs,12800
465
465
  reconcile/templating/validator.py,sha256=5f9f35PCHOOdjb7KZquL2YdabyuAUokPDa4xutSEHIQ,5360
466
466
  reconcile/templating/lib/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
467
- reconcile/templating/lib/merge_request_manager.py,sha256=P4IbblzXbubjlaxAYoahHFADUNIXLepfqwzdnHPs8OY,5608
468
- reconcile/templating/lib/model.py,sha256=Y1PeEP-koHKiH46JSwv4-PGRGQtcxZa9VmjTiuI6Lxs,889
467
+ reconcile/templating/lib/merge_request_manager.py,sha256=XwpOR4rVS9ZiJ_Mn8qfCXPZ7CRLMGSJgeIkqD-8Jhgc,5228
468
+ reconcile/templating/lib/model.py,sha256=YVUIXuPny3_kpFgBMSud8q_ndY5o882wKiX0l0A14L4,481
469
469
  reconcile/templating/lib/rendering.py,sha256=pbZv8uYlNtgzTjYMl5qSIW94vDS4CfE47aSjX4HpL-k,6210
470
470
  reconcile/terraform_init/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
471
471
  reconcile/terraform_init/integration.py,sha256=glQ9uy8Kj2aTQXCAupwSFeih7reX_xMX_UuWW_ywBMU,6100
@@ -763,7 +763,7 @@ reconcile/utils/mr/ocm_upgrade_scheduler_org_updates.py,sha256=ojnIjw-8vRnmCCxOG
763
763
  reconcile/utils/mr/user_maintenance.py,sha256=cHPBn8zrReWLHalyk-EFdkFJe9zjVjRoZhT4t2zZfGE,3956
764
764
  reconcile/utils/ocm/__init__.py,sha256=xv7CJp7K9LCQfa4gL_W0MMCOD1P4qOy8t5aZj1xXNUE,808
765
765
  reconcile/utils/ocm/addons.py,sha256=_LDdJ-gapM3s5exKlIUt-MlXZTAUoHezbYBU0QmvfWQ,7335
766
- reconcile/utils/ocm/base.py,sha256=IF-TOj9YqDBfuXRMAxCZ8xFTNsgLQdsAxCtIk4UjH4E,14457
766
+ reconcile/utils/ocm/base.py,sha256=iL_uMN03URDisWHpsaGGto_pLx652epUkuld_9ctx5o,14555
767
767
  reconcile/utils/ocm/cluster_groups.py,sha256=F8oqVqN_4QUnGL0K61zZhoYIzJeP57EcmZpwmoV0mr4,1751
768
768
  reconcile/utils/ocm/clusters.py,sha256=Fn4swizm1qq-XiNlIZ9SvahkftWAyNT8hF4kqRBpK4g,8287
769
769
  reconcile/utils/ocm/identity_providers.py,sha256=dKed09N8iWmn39tI_MpwgVe47x23eLsknGbjMUxtwr4,2175
@@ -842,8 +842,8 @@ tools/test/test_qontract_cli.py,sha256=_D61RFGAN5x44CY1tYbouhlGXXABwYfxKSWSQx3Jr
842
842
  tools/test/test_saas_promotion_state.py,sha256=dy4kkSSAQ7bC0Xp2CociETGN-2aABEfL6FU5D9Jl00Y,6056
843
843
  tools/test/test_sd_app_sre_alert_report.py,sha256=v363r9zM7__0kR5K6mvJoGFcM9BvE33fWAayrqkpojA,2116
844
844
  tools/test/test_sre_checkpoints.py,sha256=SKqPPTl9ua0RFdSSofnoQX-JZE6dFLO3LRhfQzqtfh8,2607
845
- qontract_reconcile-0.10.1rc938.dist-info/METADATA,sha256=8eH4i_Fb7X4grau64UTXOtx-nGf9JlgMej8egsZGHAU,2262
846
- qontract_reconcile-0.10.1rc938.dist-info/WHEEL,sha256=GJ7t_kWBFywbagK5eo9IoUwLW6oyOeTKmQ-9iHFVNxQ,92
847
- qontract_reconcile-0.10.1rc938.dist-info/entry_points.txt,sha256=GKQqCl2j2X1BJQ69een6rHcR26PmnxnONLNOQB-nRjY,491
848
- qontract_reconcile-0.10.1rc938.dist-info/top_level.txt,sha256=l5ISPoXzt0SdR4jVdkfa7RPSKNc8zAHYWAnR-Dw8Ey8,24
849
- qontract_reconcile-0.10.1rc938.dist-info/RECORD,,
845
+ qontract_reconcile-0.10.1rc940.dist-info/METADATA,sha256=J7rHvi9BG3xcDI8XfR2-qBhCHm4A-q0RZ9GyVkmRF8k,2262
846
+ qontract_reconcile-0.10.1rc940.dist-info/WHEEL,sha256=GJ7t_kWBFywbagK5eo9IoUwLW6oyOeTKmQ-9iHFVNxQ,92
847
+ qontract_reconcile-0.10.1rc940.dist-info/entry_points.txt,sha256=GKQqCl2j2X1BJQ69een6rHcR26PmnxnONLNOQB-nRjY,491
848
+ qontract_reconcile-0.10.1rc940.dist-info/top_level.txt,sha256=l5ISPoXzt0SdR4jVdkfa7RPSKNc8zAHYWAnR-Dw8Ey8,24
849
+ qontract_reconcile-0.10.1rc940.dist-info/RECORD,,
reconcile/ocm_groups.py CHANGED
@@ -13,6 +13,7 @@ from reconcile import (
13
13
  from reconcile.status import ExitCodes
14
14
  from reconcile.utils.disabled_integrations import integration_is_enabled
15
15
  from reconcile.utils.ocm import OCMMap
16
+ from reconcile.utils.ocm.base import OCMClusterGroupId
16
17
 
17
18
  QONTRACT_INTEGRATION = "ocm-groups"
18
19
 
@@ -76,9 +77,12 @@ def run(dry_run, thread_pool_size=10):
76
77
  ocm_map, current_state = fetch_current_state(clusters, thread_pool_size)
77
78
  desired_state = openshift_groups.fetch_desired_state(clusters=ocm_map.clusters())
78
79
 
79
- # we only manage dedicated-admins via OCM
80
- current_state = [s for s in current_state if s["group"] == "dedicated-admins"]
81
- desired_state = [s for s in desired_state if s["group"] == "dedicated-admins"]
80
+ current_state = [
81
+ s for s in current_state if s["group"] in OCMClusterGroupId.values()
82
+ ]
83
+ desired_state = [
84
+ s for s in desired_state if s["group"] in OCMClusterGroupId.values()
85
+ ]
82
86
 
83
87
  diffs = openshift_groups.calculate_diff(current_state, desired_state)
84
88
  openshift_groups.validate_diffs(diffs)
@@ -100,8 +104,9 @@ def early_exit_desired_state(*args, **kwargs) -> dict[str, Any]:
100
104
  if integration_is_enabled(QONTRACT_INTEGRATION, c) and _cluster_is_compatible(c)
101
105
  ]
102
106
  desired_state = openshift_groups.fetch_desired_state(clusters=clusters)
103
- # we only manage dedicated-admins via OCM
104
- desired_state = [s for s in desired_state if s["group"] == "dedicated-admins"]
107
+ desired_state = [
108
+ s for s in desired_state if s["group"] in OCMClusterGroupId.values()
109
+ ]
105
110
 
106
111
  return {
107
112
  "state": desired_state,
@@ -31,6 +31,7 @@ from reconcile.utils.oc_map import (
31
31
  OCMap,
32
32
  init_oc_map_from_clusters,
33
33
  )
34
+ from reconcile.utils.ocm.base import OCMClusterGroupId
34
35
  from reconcile.utils.secret_reader import create_secret_reader
35
36
  from reconcile.utils.sharding import is_in_shard
36
37
 
@@ -270,16 +271,19 @@ def run(
270
271
  defer(oc_map.cleanup)
271
272
  desired_state = fetch_desired_state(oc_map.clusters())
272
273
 
273
- # we only manage dedicated-admins via OCM
274
274
  current_state = [
275
275
  s
276
276
  for s in current_state
277
- if not (s["cluster"] in ocm_clusters and s["group"] == "dedicated-admins")
277
+ if not (
278
+ s["cluster"] in ocm_clusters and s["group"] in OCMClusterGroupId.values()
279
+ )
278
280
  ]
279
281
  desired_state = [
280
282
  s
281
283
  for s in desired_state
282
- if not (s["cluster"] in ocm_clusters and s["group"] == "dedicated-admins")
284
+ if not (
285
+ s["cluster"] in ocm_clusters and s["group"] in OCMClusterGroupId.values()
286
+ )
283
287
  ]
284
288
 
285
289
  ob.publish_cluster_desired_metrics_from_state(
@@ -4,7 +4,7 @@ import string
4
4
 
5
5
  from pydantic import BaseModel
6
6
 
7
- from reconcile.templating.lib.model import TemplateOutput
7
+ from reconcile.templating.lib.model import TemplateOutput, TemplateResult
8
8
  from reconcile.utils.gitlab_api import GitLabApi
9
9
  from reconcile.utils.merge_request_manager.merge_request_manager import (
10
10
  MergeRequestManagerBase,
@@ -24,14 +24,14 @@ TR_LABEL = "template-output"
24
24
 
25
25
  VERSION_REF = "tr_version"
26
26
  COLLECTION_REF = "collection"
27
- TEMPLATE_COLLECTION_HASH_REF = "collection_hash"
27
+ TEMPLATE_RESULT_HASH_REF = "result_hash"
28
28
 
29
29
  COMPILED_REGEXES = {
30
30
  i: re.compile(rf".*{i}: (.*)$", re.MULTILINE)
31
31
  for i in [
32
32
  VERSION_REF,
33
33
  COLLECTION_REF,
34
- TEMPLATE_COLLECTION_HASH_REF,
34
+ TEMPLATE_RESULT_HASH_REF,
35
35
  ]
36
36
  }
37
37
 
@@ -47,7 +47,7 @@ Parts of this description are used by the Template Renderer to manage the MR.
47
47
 
48
48
  * {VERSION_REF}: $version
49
49
  * {COLLECTION_REF}: $collection
50
- * {TEMPLATE_COLLECTION_HASH_REF}: $collection_hash
50
+ * {TEMPLATE_RESULT_HASH_REF}: $result_hash
51
51
  """
52
52
  )
53
53
 
@@ -63,10 +63,10 @@ def create_parser() -> Parser:
63
63
 
64
64
 
65
65
  def render_description(
66
- collection: str, collection_hash: str, version: str = TR_VERSION
66
+ collection: str, result_hash: str, version: str = TR_VERSION
67
67
  ) -> str:
68
68
  return MR_DESC.substitute(
69
- collection=collection, collection_hash=collection_hash, version=version
69
+ collection=collection, result_hash=result_hash, version=version
70
70
  )
71
71
 
72
72
 
@@ -76,7 +76,7 @@ def render_title(collection: str) -> str:
76
76
 
77
77
  class TemplateInfo(BaseModel):
78
78
  collection: str
79
- collection_hash: str
79
+ result_hash: str
80
80
 
81
81
 
82
82
  class TemplateRenderingMR(MergeRequestBase):
@@ -122,7 +122,7 @@ class TemplateRenderingMR(MergeRequestBase):
122
122
 
123
123
 
124
124
  class MrData(BaseModel):
125
- data: list[TemplateOutput]
125
+ result: TemplateResult
126
126
  auto_approved: bool
127
127
 
128
128
 
@@ -134,21 +134,17 @@ class MergeRequestManager(MergeRequestManagerBase[TemplateInfo]):
134
134
  if not self._housekeeping_ran:
135
135
  self.housekeeping()
136
136
 
137
- output = data.data
138
- collections = {o.input.collection for o in output}
139
- collection_hashes = {o.input.calc_template_hash() for o in output}
140
- additional_labels = {label for o in output for label in o.input.labels}
141
- # From the way the code is written, we can assert that there is only one collection and one template hash
142
- assert len(collections) == 1
143
- assert len(collection_hashes) == 1
144
- collection = collections.pop()
145
- collection_hash = collection_hashes.pop()
137
+ result = data.result
138
+ output = result.outputs
139
+ collection = result.collection
140
+ result_hash = result.calc_result_hash()
141
+ additional_labels = result.labels
146
142
 
147
143
  """Create a new MR with the rendered template."""
148
144
  if mr := self._merge_request_already_exists({"collection": collection}):
149
- if mr.mr_info.collection_hash == collection_hash:
145
+ if mr.mr_info.result_hash == result_hash:
150
146
  logging.info(
151
- "MR already exists and has the same template hash. Skipping: %s",
147
+ "MR already exists and has the same result hash. Skipping: %s",
152
148
  mr.raw.attributes.get("web_url", "NO_WEBURL"),
153
149
  )
154
150
  return None
@@ -159,13 +155,13 @@ class MergeRequestManager(MergeRequestManagerBase[TemplateInfo]):
159
155
  )
160
156
  self._vcs.close_app_interface_mr(
161
157
  mr.raw,
162
- "Closing this MR because the collection hash has changed.",
158
+ "Closing this MR because the result hash has changed.",
163
159
  )
164
160
 
165
- description = render_description(collection, collection_hash)
161
+ description = render_description(collection, result_hash)
166
162
  title = render_title(collection)
167
163
 
168
- logging.info("Opening MR for %s with hash (%s)", collection, collection_hash)
164
+ logging.info("Opening MR for %s with hash (%s)", collection, result_hash)
169
165
  mr_labels = [TR_LABEL]
170
166
 
171
167
  if data.auto_approved:
@@ -1,34 +1,20 @@
1
- from typing import Any
2
-
3
1
  from deepdiff import DeepHash
4
2
  from pydantic import BaseModel
5
3
 
6
- from reconcile.gql_definitions.templating.template_collection import (
7
- TemplateV1,
8
- )
9
-
10
-
11
- class TemplateInput(BaseModel):
12
- collection: str
13
- templates: list[TemplateV1] = []
14
- variables: list[dict[str, Any]] = []
15
- collection_hash: str = ""
16
- enable_auto_approval: bool = False
17
- labels: list[str] = []
18
-
19
- def calc_template_hash(self) -> str:
20
- if not self.collection_hash:
21
- hashable = {
22
- "templates": sorted(self.templates, key=lambda x: x.name),
23
- "variables": self.variables,
24
- }
25
- self.collection_hash = DeepHash(hashable)[hashable]
26
- return self.collection_hash
27
-
28
4
 
29
5
  class TemplateOutput(BaseModel):
30
- input: TemplateInput
31
6
  is_new: bool = False
32
7
  path: str
33
8
  content: str
34
9
  auto_approved: bool = False
10
+
11
+
12
+ class TemplateResult(BaseModel):
13
+ collection: str
14
+ enable_auto_approval: bool = False
15
+ labels: list[str] = []
16
+ outputs: list[TemplateOutput] = []
17
+
18
+ def calc_result_hash(self) -> str:
19
+ hashable = {o.path: o for o in self.outputs}
20
+ return DeepHash(hashable)[hashable]
@@ -20,7 +20,7 @@ from reconcile.templating.lib.merge_request_manager import (
20
20
  MrData,
21
21
  create_parser,
22
22
  )
23
- from reconcile.templating.lib.model import TemplateInput, TemplateOutput
23
+ from reconcile.templating.lib.model import TemplateOutput, TemplateResult
24
24
  from reconcile.templating.lib.rendering import (
25
25
  Renderer,
26
26
  TemplateData,
@@ -59,7 +59,7 @@ def get_template_collections(
59
59
 
60
60
  class FilePersistence(ABC):
61
61
  @abstractmethod
62
- def write(self, outputs: list[TemplateOutput]) -> None:
62
+ def write(self, result: TemplateResult) -> None:
63
63
  pass
64
64
 
65
65
  @abstractmethod
@@ -89,8 +89,8 @@ class LocalFilePersistence(FilePersistence):
89
89
  raise ValueError("app_interface_data_path should end with /data")
90
90
  self.app_interface_data_path = app_interface_data_path
91
91
 
92
- def write(self, outputs: list[TemplateOutput]) -> None:
93
- for output in outputs:
92
+ def write(self, result: TemplateResult) -> None:
93
+ for output in result.outputs:
94
94
  filepath = Path(join_path(self.app_interface_data_path, output.path))
95
95
  filepath.parent.mkdir(parents=True, exist_ok=True)
96
96
  filepath.write_text(output.content, encoding="utf-8")
@@ -111,9 +111,11 @@ class PersistenceTransaction(FilePersistence):
111
111
  self.dry_run = dry_run
112
112
  self.content_cache: dict[str, str | None] = {}
113
113
  self.output_cache: dict[str, TemplateOutput] = {}
114
+ self.result: TemplateResult
114
115
 
115
- def write(self, outputs: list[TemplateOutput]) -> None:
116
- for output in outputs:
116
+ def write(self, result: TemplateResult) -> None:
117
+ self.result = result
118
+ for output in result.outputs:
117
119
  self.content_cache[output.path] = output.content
118
120
  self.output_cache[output.path] = output
119
121
 
@@ -127,7 +129,8 @@ class PersistenceTransaction(FilePersistence):
127
129
 
128
130
  def __exit__(self, exc_type: Any, exc_value: Any, traceback: Any) -> None:
129
131
  if not self.dry_run and self.output_cache:
130
- self.persistence.write(list(self.output_cache.values()))
132
+ self.result.outputs = list(self.output_cache.values())
133
+ self.persistence.write(self.result)
131
134
 
132
135
 
133
136
  class ClonedRepoGitlabPersistence(FilePersistence):
@@ -143,18 +146,19 @@ class ClonedRepoGitlabPersistence(FilePersistence):
143
146
  self.vcs = vcs
144
147
  self.mr_manager = mr_manager
145
148
 
146
- def write(self, outputs: list[TemplateOutput]) -> None:
149
+ def write(self, result: TemplateResult) -> None:
147
150
  self.mr_manager.housekeeping()
148
151
 
149
- if any([o.input.enable_auto_approval for o in outputs]):
150
- auto_approved = [o for o in outputs if o.auto_approved]
152
+ if result.enable_auto_approval:
153
+ auto_approved = [o for o in result.outputs if o.auto_approved]
151
154
  if auto_approved:
155
+ result.outputs = auto_approved
152
156
  self.mr_manager.create_merge_request(
153
- MrData(data=auto_approved, auto_approved=True)
157
+ MrData(result=result, auto_approved=True)
154
158
  )
155
159
  return
156
160
 
157
- self.mr_manager.create_merge_request(MrData(data=outputs, auto_approved=False))
161
+ self.mr_manager.create_merge_request(MrData(result=result, auto_approved=False))
158
162
 
159
163
  def read(self, path: str) -> str | None:
160
164
  return self._read_local_file(join_path(self.local_path, path))
@@ -231,7 +235,6 @@ class TemplateRendererIntegration(QontractReconcileIntegration):
231
235
  variables: dict,
232
236
  persistence: FilePersistence,
233
237
  ruaml_instance: yaml.YAML,
234
- template_input: TemplateInput,
235
238
  ) -> TemplateOutput | None:
236
239
  r = TemplateRendererIntegration._create_renderer(
237
240
  template, variables, secret_reader=self.secret_reader
@@ -259,7 +262,6 @@ class TemplateRendererIntegration(QontractReconcileIntegration):
259
262
  content=output,
260
263
  is_new=current_str is None,
261
264
  auto_approved=template.auto_approved or False,
262
- input=template_input,
263
265
  )
264
266
  return None
265
267
 
@@ -269,7 +271,6 @@ class TemplateRendererIntegration(QontractReconcileIntegration):
269
271
  gql_api: GqlApi,
270
272
  persistence: FilePersistence,
271
273
  ruamel_instance: yaml.YAML,
272
- input: TemplateInput,
273
274
  each: dict[str, Any],
274
275
  ) -> list[TemplateOutput]:
275
276
  variables = {}
@@ -280,12 +281,11 @@ class TemplateRendererIntegration(QontractReconcileIntegration):
280
281
  ),
281
282
  "static": unpack_static_variables(collection.variables, each),
282
283
  }
283
- input.variables.append(variables)
284
284
 
285
285
  outputs: list[TemplateOutput] = []
286
286
  for template in collection.templates:
287
287
  output = self.process_template(
288
- template, variables, persistence, ruamel_instance, input
288
+ template, variables, persistence, ruamel_instance
289
289
  )
290
290
  if output:
291
291
  outputs.append(output)
@@ -303,27 +303,24 @@ class TemplateRendererIntegration(QontractReconcileIntegration):
303
303
  for_each_items: list[dict[str, Any]] = [{}]
304
304
  if c.for_each and c.for_each.items:
305
305
  for_each_items = c.for_each.items
306
- input = TemplateInput(
306
+ result = TemplateResult(
307
307
  collection=c.name,
308
- templates=c.templates,
309
308
  enable_auto_approval=c.enable_auto_approval or False,
310
309
  labels=c.additional_mr_labels or [],
311
310
  )
312
311
  with PersistenceTransaction(persistence, dry_run) as p:
313
- outputs: list[TemplateOutput] = []
314
312
  for item in for_each_items:
315
- outputs.extend(
313
+ result.outputs.extend(
316
314
  self.reconcile_template_collection(
317
315
  c,
318
316
  gql_no_validation,
319
317
  p,
320
318
  ruamel_instance,
321
- input,
322
319
  item,
323
320
  )
324
321
  )
325
- if not dry_run and outputs:
326
- p.write(outputs)
322
+ if not dry_run and result.outputs:
323
+ p.write(result)
327
324
 
328
325
  @property
329
326
  def name(self) -> str:
@@ -64,6 +64,10 @@ class OCMClusterGroupId(Enum):
64
64
  DEDICATED_ADMINS = "dedicated-admins"
65
65
  CLUSTER_ADMINS = "cluster-admins"
66
66
 
67
+ @classmethod
68
+ def values(cls) -> list[str]:
69
+ return [group.value for group in cls]
70
+
67
71
 
68
72
  class OCMClusterUser(BaseModel):
69
73
  """