qontract-reconcile 0.10.1rc349__py3-none-any.whl → 0.10.1rc350__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.1rc349.dist-info → qontract_reconcile-0.10.1rc350.dist-info}/METADATA +1 -1
- {qontract_reconcile-0.10.1rc349.dist-info → qontract_reconcile-0.10.1rc350.dist-info}/RECORD +19 -15
- reconcile/aus/advanced_upgrade_service.py +50 -13
- reconcile/aus/aus_label_source.py +115 -0
- reconcile/cli.py +4 -4
- reconcile/gql_definitions/advanced_upgrade_service/aus_clusters.py +11 -1
- reconcile/gql_definitions/advanced_upgrade_service/aus_organization.py +9 -1
- reconcile/gql_definitions/fragments/aus_organization.py +9 -11
- reconcile/gql_definitions/fragments/minimal_ocm_organization.py +29 -0
- reconcile/gql_definitions/{ocm_subscription_labels → ocm_labels}/clusters.py +8 -0
- reconcile/gql_definitions/ocm_labels/organizations.py +72 -0
- reconcile/ocm_labels/integration.py +406 -0
- reconcile/ocm_labels/label_sources.py +76 -0
- reconcile/utils/ocm/labels.py +61 -1
- reconcile/ocm_subscription_labels/integration.py +0 -250
- {qontract_reconcile-0.10.1rc349.dist-info → qontract_reconcile-0.10.1rc350.dist-info}/WHEEL +0 -0
- {qontract_reconcile-0.10.1rc349.dist-info → qontract_reconcile-0.10.1rc350.dist-info}/entry_points.txt +0 -0
- {qontract_reconcile-0.10.1rc349.dist-info → qontract_reconcile-0.10.1rc350.dist-info}/top_level.txt +0 -0
- /reconcile/gql_definitions/{ocm_subscription_labels → ocm_labels}/__init__.py +0 -0
- /reconcile/{ocm_subscription_labels → ocm_labels}/__init__.py +0 -0
@@ -1,250 +0,0 @@
|
|
1
|
-
from __future__ import annotations
|
2
|
-
|
3
|
-
import logging
|
4
|
-
from collections.abc import (
|
5
|
-
Callable,
|
6
|
-
Iterable,
|
7
|
-
)
|
8
|
-
from typing import (
|
9
|
-
Any,
|
10
|
-
Optional,
|
11
|
-
)
|
12
|
-
|
13
|
-
from pydantic import (
|
14
|
-
BaseModel,
|
15
|
-
validator,
|
16
|
-
)
|
17
|
-
|
18
|
-
from reconcile.gql_definitions.fragments.ocm_environment import OCMEnvironment
|
19
|
-
from reconcile.gql_definitions.ocm_subscription_labels.clusters import ClusterV1
|
20
|
-
from reconcile.gql_definitions.ocm_subscription_labels.clusters import (
|
21
|
-
query as cluster_query,
|
22
|
-
)
|
23
|
-
from reconcile.utils import gql
|
24
|
-
from reconcile.utils.differ import diff_mappings
|
25
|
-
from reconcile.utils.disabled_integrations import integration_is_enabled
|
26
|
-
from reconcile.utils.helpers import flatten
|
27
|
-
from reconcile.utils.ocm.clusters import (
|
28
|
-
ClusterDetails,
|
29
|
-
discover_clusters_for_organizations,
|
30
|
-
)
|
31
|
-
from reconcile.utils.ocm.labels import (
|
32
|
-
add_subscription_label,
|
33
|
-
delete_ocm_label,
|
34
|
-
update_ocm_label,
|
35
|
-
)
|
36
|
-
from reconcile.utils.ocm_base_client import (
|
37
|
-
OCMAPIClientConfigurationProtocol,
|
38
|
-
OCMBaseClient,
|
39
|
-
init_ocm_base_client,
|
40
|
-
)
|
41
|
-
from reconcile.utils.runtime.integration import (
|
42
|
-
PydanticRunParams,
|
43
|
-
QontractReconcileIntegration,
|
44
|
-
)
|
45
|
-
from reconcile.utils.secret_reader import SecretReaderBase
|
46
|
-
|
47
|
-
QONTRACT_INTEGRATION = "ocm-subscription-labels"
|
48
|
-
|
49
|
-
|
50
|
-
class EnvWithClusters(BaseModel):
|
51
|
-
env: OCMEnvironment
|
52
|
-
clusters: list[ClusterV1] = []
|
53
|
-
|
54
|
-
class Config:
|
55
|
-
arbitrary_types_allowed = True
|
56
|
-
|
57
|
-
|
58
|
-
class ClusterLabelState(BaseModel):
|
59
|
-
env: OCMEnvironment
|
60
|
-
ocm_api: OCMBaseClient
|
61
|
-
cluster_details: Optional[ClusterDetails] = None
|
62
|
-
labels: dict[str, str] = {}
|
63
|
-
|
64
|
-
class Config:
|
65
|
-
arbitrary_types_allowed = True
|
66
|
-
|
67
|
-
def __eq__(self, other: object) -> bool:
|
68
|
-
if not isinstance(other, ClusterLabelState):
|
69
|
-
raise NotImplementedError("Cannot compare to non ClusterState objects.")
|
70
|
-
return self.labels == other.labels
|
71
|
-
|
72
|
-
|
73
|
-
ClusterStates = dict[str, ClusterLabelState]
|
74
|
-
|
75
|
-
|
76
|
-
class OcmLabelsIntegrationParams(PydanticRunParams):
|
77
|
-
managed_label_prefixes: list[str] = []
|
78
|
-
|
79
|
-
@validator("managed_label_prefixes")
|
80
|
-
def must_end_with_dot( # pylint: disable=no-self-argument
|
81
|
-
cls, v: list[str]
|
82
|
-
) -> list[str]:
|
83
|
-
return [prefix + "." if not prefix.endswith(".") else prefix for prefix in v]
|
84
|
-
|
85
|
-
|
86
|
-
class OcmLabelsIntegration(QontractReconcileIntegration[OcmLabelsIntegrationParams]):
|
87
|
-
"""Sync cluster.ocm-labels to OCM."""
|
88
|
-
|
89
|
-
@property
|
90
|
-
def name(self) -> str:
|
91
|
-
return QONTRACT_INTEGRATION
|
92
|
-
|
93
|
-
def get_clusters(self, query_func: Callable) -> list[ClusterV1]:
|
94
|
-
data = cluster_query(query_func)
|
95
|
-
return [
|
96
|
-
c
|
97
|
-
for c in data.clusters or []
|
98
|
-
if c.ocm is not None and integration_is_enabled(self.name, c)
|
99
|
-
]
|
100
|
-
|
101
|
-
def get_ocm_environments(
|
102
|
-
self,
|
103
|
-
clusters: Iterable[ClusterV1],
|
104
|
-
) -> list[EnvWithClusters]:
|
105
|
-
envs: dict[str, EnvWithClusters] = {}
|
106
|
-
|
107
|
-
for cluster in clusters:
|
108
|
-
if cluster.ocm is None:
|
109
|
-
# already filtered out in get_clusters - make mypy happy
|
110
|
-
continue
|
111
|
-
if cluster.ocm.environment.name not in envs:
|
112
|
-
envs[cluster.ocm.environment.name] = EnvWithClusters(
|
113
|
-
env=cluster.ocm.environment, clusters=[cluster]
|
114
|
-
)
|
115
|
-
else:
|
116
|
-
envs[cluster.ocm.environment.name].clusters.append(cluster)
|
117
|
-
|
118
|
-
return list(envs.values())
|
119
|
-
|
120
|
-
def init_ocm_apis(
|
121
|
-
self,
|
122
|
-
envs: Iterable[EnvWithClusters],
|
123
|
-
init_ocm_base_client: Callable[
|
124
|
-
[OCMAPIClientConfigurationProtocol, SecretReaderBase], OCMBaseClient
|
125
|
-
] = init_ocm_base_client,
|
126
|
-
) -> None:
|
127
|
-
self.ocm_apis = {
|
128
|
-
env.env.name: init_ocm_base_client(env.env, self.secret_reader)
|
129
|
-
for env in envs
|
130
|
-
}
|
131
|
-
|
132
|
-
def get_early_exit_desired_state(self) -> Optional[dict[str, Any]]:
|
133
|
-
gqlapi = gql.get_api()
|
134
|
-
return {"clusters": [c.dict() for c in self.get_clusters(gqlapi.query)]}
|
135
|
-
|
136
|
-
def run(self, dry_run: bool) -> None:
|
137
|
-
gqlapi = gql.get_api()
|
138
|
-
clusters = self.get_clusters(gqlapi.query)
|
139
|
-
current_state = self.fetch_current_state(
|
140
|
-
clusters, self.params.managed_label_prefixes
|
141
|
-
)
|
142
|
-
desired_state = self.fetch_desired_state(clusters)
|
143
|
-
self.reconcile(dry_run, current_state, desired_state)
|
144
|
-
|
145
|
-
def fetch_current_state(
|
146
|
-
self, clusters: Iterable[ClusterV1], managed_label_prefixes: list[str]
|
147
|
-
) -> ClusterStates:
|
148
|
-
states: ClusterStates = {}
|
149
|
-
envs = self.get_ocm_environments(clusters)
|
150
|
-
self.init_ocm_apis(envs)
|
151
|
-
for env in envs:
|
152
|
-
for cluster_details in discover_clusters_for_organizations(
|
153
|
-
ocm_api=self.ocm_apis[env.env.name],
|
154
|
-
organization_ids=list({c.ocm.org_id for c in clusters if c.ocm}),
|
155
|
-
):
|
156
|
-
filtered_labels = {
|
157
|
-
label: value
|
158
|
-
for label, value in cluster_details.subscription_labels.get_values_dict().items()
|
159
|
-
if label.startswith(tuple(managed_label_prefixes))
|
160
|
-
}
|
161
|
-
states[cluster_details.ocm_cluster.name] = ClusterLabelState(
|
162
|
-
env=env.env,
|
163
|
-
ocm_api=self.ocm_apis[env.env.name],
|
164
|
-
cluster_details=cluster_details,
|
165
|
-
labels=filtered_labels,
|
166
|
-
)
|
167
|
-
return states
|
168
|
-
|
169
|
-
def fetch_desired_state(self, clusters: Iterable[ClusterV1]) -> ClusterStates:
|
170
|
-
states: ClusterStates = {}
|
171
|
-
for cluster in clusters:
|
172
|
-
if cluster.ocm is None:
|
173
|
-
# already filtered out in get_clusters - make mypy happy
|
174
|
-
continue
|
175
|
-
states[cluster.name] = ClusterLabelState(
|
176
|
-
env=cluster.ocm.environment,
|
177
|
-
ocm_api=self.ocm_apis[cluster.ocm.environment.name],
|
178
|
-
labels=flatten(cluster.ocm_subscription_labels or {}),
|
179
|
-
)
|
180
|
-
|
181
|
-
return states
|
182
|
-
|
183
|
-
def reconcile(
|
184
|
-
self,
|
185
|
-
dry_run: bool,
|
186
|
-
current_cluster_states: ClusterStates,
|
187
|
-
desired_cluster_states: ClusterStates,
|
188
|
-
) -> None:
|
189
|
-
for cluster_name, desired_cluster_state in desired_cluster_states.items():
|
190
|
-
try:
|
191
|
-
current_cluster_state = current_cluster_states[cluster_name]
|
192
|
-
if not (cluster_details := current_cluster_state.cluster_details):
|
193
|
-
# this should never happen - make mypy happy
|
194
|
-
raise RuntimeError("Cluster details not found.")
|
195
|
-
|
196
|
-
if desired_cluster_state == current_cluster_state:
|
197
|
-
continue
|
198
|
-
except KeyError:
|
199
|
-
logging.info(
|
200
|
-
f"Cluster '{cluster_name}' not found in OCM. Maybe it doesn't exist yet. Skipping."
|
201
|
-
)
|
202
|
-
continue
|
203
|
-
|
204
|
-
diff_result = diff_mappings(
|
205
|
-
current_cluster_state.labels, desired_cluster_state.labels
|
206
|
-
)
|
207
|
-
|
208
|
-
for label_to_add, value in diff_result.add.items():
|
209
|
-
logging.info(
|
210
|
-
[
|
211
|
-
"create_cluster_subscription_label",
|
212
|
-
cluster_name,
|
213
|
-
f"{label_to_add}={value}",
|
214
|
-
]
|
215
|
-
)
|
216
|
-
if not dry_run:
|
217
|
-
add_subscription_label(
|
218
|
-
ocm_api=desired_cluster_state.ocm_api,
|
219
|
-
ocm_cluster=cluster_details.ocm_cluster,
|
220
|
-
label=label_to_add,
|
221
|
-
value=value,
|
222
|
-
)
|
223
|
-
for label_to_rm, value in diff_result.delete.items():
|
224
|
-
logging.info(
|
225
|
-
[
|
226
|
-
"delete_cluster_subscription_label",
|
227
|
-
cluster_name,
|
228
|
-
f"{label_to_rm}={value}",
|
229
|
-
]
|
230
|
-
)
|
231
|
-
if not dry_run:
|
232
|
-
delete_ocm_label(
|
233
|
-
ocm_api=desired_cluster_state.ocm_api,
|
234
|
-
ocm_label=cluster_details.labels[label_to_rm],
|
235
|
-
)
|
236
|
-
for label_to_update, diff_pair in diff_result.change.items():
|
237
|
-
value = diff_pair.desired
|
238
|
-
logging.info(
|
239
|
-
[
|
240
|
-
"update_cluster_subscription_label",
|
241
|
-
cluster_name,
|
242
|
-
f"{label_to_update}={value}",
|
243
|
-
]
|
244
|
-
)
|
245
|
-
if not dry_run:
|
246
|
-
update_ocm_label(
|
247
|
-
ocm_api=desired_cluster_state.ocm_api,
|
248
|
-
ocm_label=cluster_details.labels[label_to_update],
|
249
|
-
value=value,
|
250
|
-
)
|
File without changes
|
File without changes
|
{qontract_reconcile-0.10.1rc349.dist-info → qontract_reconcile-0.10.1rc350.dist-info}/top_level.txt
RENAMED
File without changes
|
File without changes
|
File without changes
|