qontract-reconcile 0.10.2.dev155__py3-none-any.whl → 0.10.2.dev157__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.dev155.dist-info → qontract_reconcile-0.10.2.dev157.dist-info}/METADATA +1 -1
- {qontract_reconcile-0.10.2.dev155.dist-info → qontract_reconcile-0.10.2.dev157.dist-info}/RECORD +10 -10
- reconcile/dynatrace_token_provider/integration.py +125 -86
- reconcile/dynatrace_token_provider/model.py +13 -0
- reconcile/dynatrace_token_provider/ocm.py +10 -22
- reconcile/gql_definitions/introspection.json +59 -7
- reconcile/gql_definitions/quay_membership/quay_membership.py +8 -0
- reconcile/quay_membership.py +7 -2
- {qontract_reconcile-0.10.2.dev155.dist-info → qontract_reconcile-0.10.2.dev157.dist-info}/WHEEL +0 -0
- {qontract_reconcile-0.10.2.dev155.dist-info → qontract_reconcile-0.10.2.dev157.dist-info}/entry_points.txt +0 -0
{qontract_reconcile-0.10.2.dev155.dist-info → qontract_reconcile-0.10.2.dev157.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.dev157
|
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.dev155.dist-info → qontract_reconcile-0.10.2.dev157.dist-info}/RECORD
RENAMED
@@ -89,7 +89,7 @@ reconcile/openshift_upgrade_watcher.py,sha256=9IB321hlRZZhzdaR9G3zoWAhVv0-KzNiEq
|
|
89
89
|
reconcile/openshift_users.py,sha256=JUWLb13USlQ4KvXZVsi3JES4csZnXlH0plhxskg_p6A,5300
|
90
90
|
reconcile/openshift_vault_secrets.py,sha256=9rTqV6wzCQx2Oh712E_Xj8wMG7u8Oh-pY8DWjlv4mZw,1660
|
91
91
|
reconcile/quay_base.py,sha256=h5xNjb7EZm8L2JgpO42r6w0UA4im5dabZXJSIW69zKU,1987
|
92
|
-
reconcile/quay_membership.py,sha256=
|
92
|
+
reconcile/quay_membership.py,sha256=cmeoRdr3-wVlymNHVhzhW0W-Tq6qt1hd2OOIhGXsmrY,6398
|
93
93
|
reconcile/quay_mirror.py,sha256=0KtQFwrvMNtlsPJ9F_-ICaVIjgIUjFxqipvAPcvyg3Q,15338
|
94
94
|
reconcile/quay_mirror_org.py,sha256=tXKuF6JtmaNRwu8_g_65U_Vpd6sFBYeXmJA-flVhylE,10764
|
95
95
|
reconcile/quay_permissions.py,sha256=9KOutS1w4RFQqkvMSy54VtsKNx56-phzP6yI_rEW-B8,4244
|
@@ -188,10 +188,10 @@ reconcile/cna/assets/asset_factory.py,sha256=7T7X_J6xIsoGETqBRI45_EyIKEdQcnRPt_G
|
|
188
188
|
reconcile/cna/assets/null.py,sha256=85mVh97atCoC0aLuX47poTZiyOthmziJeBsUw0c924w,1658
|
189
189
|
reconcile/dynatrace_token_provider/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
190
190
|
reconcile/dynatrace_token_provider/dependencies.py,sha256=lvkdwqHMsn_2kgj-tUIJdTUnUNxVoS6z8k4nPkGglnQ,3129
|
191
|
-
reconcile/dynatrace_token_provider/integration.py,sha256=
|
191
|
+
reconcile/dynatrace_token_provider/integration.py,sha256=1D6gIEwKvkzE9JDOcLb9EAP6JW8OK2OLNuiHAyot_XQ,28329
|
192
192
|
reconcile/dynatrace_token_provider/metrics.py,sha256=oP-6NTZENFdvWiS0krnmX6tq3xyOzQ8e6vS0CZWYUuw,1496
|
193
|
-
reconcile/dynatrace_token_provider/model.py,sha256=
|
194
|
-
reconcile/dynatrace_token_provider/ocm.py,sha256=
|
193
|
+
reconcile/dynatrace_token_provider/model.py,sha256=L6THhpPnSIeJ5n61IHhDT_JTiSEr_uWmgJAw83RUC_w,477
|
194
|
+
reconcile/dynatrace_token_provider/ocm.py,sha256=EPknDhLXkySs8Nv8jrrl12oRoe2bRFWx_CMiHpPQhmM,3734
|
195
195
|
reconcile/dynatrace_token_provider/validate.py,sha256=40_9QmHoB3-KBc0k_0D4QO00PpNNPS-gU9Z6cIcWga8,1920
|
196
196
|
reconcile/endpoints_discovery/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
197
197
|
reconcile/endpoints_discovery/integration.py,sha256=p50wu4OWO3RmL2A9TvjhRZkTYUe-l9aec4vgu0bxq8w,14926
|
@@ -227,7 +227,7 @@ reconcile/glitchtip_project_alerts/integration.py,sha256=BgMx-NyV9mTuv7Sotb2OioC
|
|
227
227
|
reconcile/glitchtip_project_dsn/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
228
228
|
reconcile/glitchtip_project_dsn/integration.py,sha256=2iugub-kHYkHNK33n0v9_TeWonuxCPah_VkoTPvaajE,8077
|
229
229
|
reconcile/gql_definitions/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
230
|
-
reconcile/gql_definitions/introspection.json,sha256=
|
230
|
+
reconcile/gql_definitions/introspection.json,sha256=cO8ytMUFP2x_mD8oq0pkcrgSFSj7epYDcdQTJCGTw-I,2289286
|
231
231
|
reconcile/gql_definitions/acs/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
232
232
|
reconcile/gql_definitions/acs/acs_instances.py,sha256=L91WW9LbhJbBSrECqShQpFtjoBOsmNIYLRpMbx1io5o,2181
|
233
233
|
reconcile/gql_definitions/acs/acs_policies.py,sha256=bN5i4mks10Z23KJSj7jqp966Osq2dps4d-sPH9gjxEA,7008
|
@@ -392,7 +392,7 @@ reconcile/gql_definitions/openshift_groups/managed_roles.py,sha256=rsHMgDWwnh0RR
|
|
392
392
|
reconcile/gql_definitions/openshift_serviceaccount_tokens/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
393
393
|
reconcile/gql_definitions/openshift_serviceaccount_tokens/tokens.py,sha256=FeraeQThHl2UbhTuCPDwm3ltczF_jfZn5E3Squ8-znw,3313
|
394
394
|
reconcile/gql_definitions/quay_membership/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
395
|
-
reconcile/gql_definitions/quay_membership/quay_membership.py,sha256=
|
395
|
+
reconcile/gql_definitions/quay_membership/quay_membership.py,sha256=MKBkrE-1YYelaAAxOdpqUwCo45kOVC8q29vXArqK_zM,3075
|
396
396
|
reconcile/gql_definitions/rhidp/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
397
397
|
reconcile/gql_definitions/rhidp/organizations.py,sha256=dW9y3ewFu3E-DFrZAi_SEewHYR0MWYeOB52vwnVcq5E,2580
|
398
398
|
reconcile/gql_definitions/service_dependencies/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
@@ -797,7 +797,7 @@ tools/saas_promotion_state/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJ
|
|
797
797
|
tools/saas_promotion_state/saas_promotion_state.py,sha256=UfwwRLS5Ya4_Nh1w5n1dvoYtchQvYE9yj1VANt2IKqI,3925
|
798
798
|
tools/sre_checkpoints/__init__.py,sha256=CDaDaywJnmRCLyl_NCcvxi-Zc0hTi_3OdwKiFOyS39I,145
|
799
799
|
tools/sre_checkpoints/util.py,sha256=zEDbGr18ZeHNQwW8pUsr2JRjuXIPz--WAGJxZo9sv_Y,894
|
800
|
-
qontract_reconcile-0.10.2.
|
801
|
-
qontract_reconcile-0.10.2.
|
802
|
-
qontract_reconcile-0.10.2.
|
803
|
-
qontract_reconcile-0.10.2.
|
800
|
+
qontract_reconcile-0.10.2.dev157.dist-info/METADATA,sha256=a0v4VwAX4CUOblutkjBWxC1LUvx_HwYZUsCE6exNgto,24627
|
801
|
+
qontract_reconcile-0.10.2.dev157.dist-info/WHEEL,sha256=qtCwoSJWgHk21S1Kb4ihdzI2rlJ1ZKaIurTj_ngOhyQ,87
|
802
|
+
qontract_reconcile-0.10.2.dev157.dist-info/entry_points.txt,sha256=5i9l54La3vQrDLAdwDKQWC0iG4sV9RRfOb1BpvzOWLc,698
|
803
|
+
qontract_reconcile-0.10.2.dev157.dist-info/RECORD,,
|
@@ -13,12 +13,15 @@ from reconcile.dynatrace_token_provider.metrics import (
|
|
13
13
|
DTPOrganizationErrorRate,
|
14
14
|
DTPTokensManagedGauge,
|
15
15
|
)
|
16
|
-
from reconcile.dynatrace_token_provider.model import
|
17
|
-
from reconcile.dynatrace_token_provider.ocm import (
|
18
|
-
DTP_LABEL_SEARCH,
|
19
|
-
DTP_TENANT_V2_LABEL,
|
16
|
+
from reconcile.dynatrace_token_provider.model import (
|
20
17
|
Cluster,
|
18
|
+
DynatraceAPIToken,
|
19
|
+
K8sSecret,
|
20
|
+
TokenSpecTenantBinding,
|
21
|
+
)
|
22
|
+
from reconcile.dynatrace_token_provider.ocm import (
|
21
23
|
OCMClient,
|
24
|
+
OCMCluster,
|
22
25
|
)
|
23
26
|
from reconcile.dynatrace_token_provider.validate import validate_token_specs
|
24
27
|
from reconcile.gql_definitions.dynatrace_token_provider.token_specs import (
|
@@ -37,6 +40,7 @@ from reconcile.utils.ocm.base import (
|
|
37
40
|
OCMServiceLogSeverity,
|
38
41
|
)
|
39
42
|
from reconcile.utils.ocm.labels import subscription_label_filter
|
43
|
+
from reconcile.utils.ocm.sre_capability_labels import sre_capability_label_key
|
40
44
|
from reconcile.utils.openshift_resource import (
|
41
45
|
QONTRACT_ANNOTATION_INTEGRATION,
|
42
46
|
QONTRACT_ANNOTATION_INTEGRATION_VERSION,
|
@@ -50,6 +54,9 @@ from reconcile.utils.semver_helper import make_semver
|
|
50
54
|
QONTRACT_INTEGRATION_VERSION = make_semver(2, 0, 1)
|
51
55
|
QONTRACT_INTEGRATION = "dynatrace-token-provider"
|
52
56
|
SYNCSET_AND_MANIFEST_ID = "ext-dynatrace-tokens-dtp"
|
57
|
+
DTP_LABEL_SEARCH = sre_capability_label_key("dtp", "%")
|
58
|
+
DTP_TENANT_V2_LABEL = sre_capability_label_key("dtp.v2", "tenant")
|
59
|
+
DTP_SPEC_V2_LABEL = sre_capability_label_key("dtp.v2", "token-spec")
|
53
60
|
|
54
61
|
|
55
62
|
class ReconcileErrorSummary(Exception):
|
@@ -103,6 +110,27 @@ class DynatraceTokenProviderIntegration(QontractReconcileIntegration[NoParams]):
|
|
103
110
|
cnt,
|
104
111
|
)
|
105
112
|
|
113
|
+
def _parse_ocm_data_to_cluster(self, ocm_cluster: OCMCluster) -> Cluster | None:
|
114
|
+
dt_tenant = ocm_cluster.labels.get(DTP_TENANT_V2_LABEL)
|
115
|
+
token_spec_name = ocm_cluster.labels.get(DTP_SPEC_V2_LABEL)
|
116
|
+
if not dt_tenant or not token_spec_name:
|
117
|
+
logging.warning(
|
118
|
+
f"[Missing DTP labels] {ocm_cluster.id=} {ocm_cluster.subscription_id=} {dt_tenant=} {token_spec_name=}"
|
119
|
+
)
|
120
|
+
return None
|
121
|
+
return Cluster(
|
122
|
+
id=ocm_cluster.id,
|
123
|
+
external_id=ocm_cluster.external_id,
|
124
|
+
organization_id=ocm_cluster.organization_id,
|
125
|
+
is_hcp=ocm_cluster.is_hcp,
|
126
|
+
dt_token_bindings=[
|
127
|
+
TokenSpecTenantBinding(
|
128
|
+
spec_name=token_spec_name,
|
129
|
+
tenant_id=dt_tenant,
|
130
|
+
)
|
131
|
+
],
|
132
|
+
)
|
133
|
+
|
106
134
|
def _filter_clusters(
|
107
135
|
self,
|
108
136
|
clusters: Iterable[Cluster],
|
@@ -110,18 +138,19 @@ class DynatraceTokenProviderIntegration(QontractReconcileIntegration[NoParams]):
|
|
110
138
|
) -> list[Cluster]:
|
111
139
|
filtered_clusters = []
|
112
140
|
for cluster in clusters:
|
113
|
-
|
114
|
-
|
115
|
-
|
116
|
-
|
117
|
-
|
118
|
-
|
119
|
-
|
120
|
-
|
121
|
-
|
122
|
-
|
123
|
-
|
124
|
-
|
141
|
+
for token_binding in cluster.dt_token_bindings:
|
142
|
+
token_spec = token_spec_by_name.get(token_binding.spec_name)
|
143
|
+
if not token_spec:
|
144
|
+
logging.debug(
|
145
|
+
f"[{cluster.id=}] Skipping cluster. {token_binding.spec_name=} does not exist."
|
146
|
+
)
|
147
|
+
continue
|
148
|
+
if cluster.organization_id in token_spec.ocm_org_ids:
|
149
|
+
filtered_clusters.append(cluster)
|
150
|
+
else:
|
151
|
+
logging.debug(
|
152
|
+
f"[{cluster.id=}] Skipping cluster for {token_spec.name=}. {cluster.organization_id=} is not defined in {token_spec.ocm_org_ids=}."
|
153
|
+
)
|
125
154
|
return filtered_clusters
|
126
155
|
|
127
156
|
def reconcile(self, dry_run: bool, dependencies: Dependencies) -> None:
|
@@ -131,17 +160,26 @@ class DynatraceTokenProviderIntegration(QontractReconcileIntegration[NoParams]):
|
|
131
160
|
with metrics.transactional_metrics(self.name):
|
132
161
|
unhandled_exceptions = []
|
133
162
|
for ocm_env_name, ocm_client in dependencies.ocm_client_by_env_name.items():
|
134
|
-
|
163
|
+
ocm_clusters: list[OCMCluster] = []
|
135
164
|
try:
|
136
|
-
|
165
|
+
ocm_clusters = ocm_client.discover_clusters_by_labels(
|
137
166
|
label_filter=subscription_label_filter().like(
|
138
167
|
"key", DTP_LABEL_SEARCH
|
139
168
|
),
|
140
169
|
)
|
141
170
|
except Exception as e:
|
142
171
|
unhandled_exceptions.append(f"{ocm_env_name}: {e}")
|
143
|
-
if not
|
172
|
+
if not ocm_clusters:
|
144
173
|
continue
|
174
|
+
clusters: list[Cluster] = [
|
175
|
+
cluster
|
176
|
+
for ocm_cluster in ocm_clusters
|
177
|
+
if (
|
178
|
+
cluster := self._parse_ocm_data_to_cluster(
|
179
|
+
ocm_cluster=ocm_cluster
|
180
|
+
)
|
181
|
+
)
|
182
|
+
]
|
145
183
|
filtered_clusters = self._filter_clusters(
|
146
184
|
clusters=clusters,
|
147
185
|
token_spec_by_name=dependencies.token_spec_by_name,
|
@@ -157,79 +195,80 @@ class DynatraceTokenProviderIntegration(QontractReconcileIntegration[NoParams]):
|
|
157
195
|
len(clusters),
|
158
196
|
)
|
159
197
|
for cluster in filtered_clusters:
|
160
|
-
|
161
|
-
|
162
|
-
|
163
|
-
|
164
|
-
|
165
|
-
|
166
|
-
tenant_id = cluster.dt_tenant
|
167
|
-
if not tenant_id:
|
168
|
-
_expose_errors_as_service_log(
|
169
|
-
ocm_client,
|
170
|
-
cluster_uuid=cluster.external_id,
|
171
|
-
error=f"Missing label {DTP_TENANT_V2_LABEL}",
|
172
|
-
)
|
173
|
-
logging.warn(
|
174
|
-
f"[{cluster.id=}] Missing value for label {DTP_TENANT_V2_LABEL}"
|
175
|
-
)
|
176
|
-
continue
|
177
|
-
if (
|
178
|
-
tenant_id
|
179
|
-
not in dependencies.dynatrace_client_by_tenant_id
|
198
|
+
for token_binding in cluster.dt_token_bindings:
|
199
|
+
try:
|
200
|
+
with DTPOrganizationErrorRate(
|
201
|
+
integration=self.name,
|
202
|
+
ocm_env=ocm_env_name,
|
203
|
+
org_id=cluster.organization_id,
|
180
204
|
):
|
181
|
-
|
182
|
-
|
183
|
-
|
184
|
-
|
185
|
-
|
186
|
-
|
187
|
-
|
188
|
-
|
189
|
-
|
190
|
-
|
191
|
-
|
192
|
-
|
205
|
+
tenant_id = token_binding.tenant_id
|
206
|
+
if not tenant_id:
|
207
|
+
_expose_errors_as_service_log(
|
208
|
+
ocm_client,
|
209
|
+
cluster_uuid=cluster.external_id,
|
210
|
+
error=f"Missing label {DTP_TENANT_V2_LABEL}",
|
211
|
+
)
|
212
|
+
logging.warning(
|
213
|
+
f"[{cluster.id=}] Missing value for label {DTP_TENANT_V2_LABEL}"
|
214
|
+
)
|
215
|
+
continue
|
216
|
+
if (
|
217
|
+
tenant_id
|
218
|
+
not in dependencies.dynatrace_client_by_tenant_id
|
219
|
+
):
|
220
|
+
_expose_errors_as_service_log(
|
221
|
+
ocm_client,
|
222
|
+
cluster_uuid=cluster.external_id,
|
223
|
+
error=f"Dynatrace tenant {tenant_id} does not exist",
|
224
|
+
)
|
225
|
+
logging.warning(
|
226
|
+
f"[{cluster.id=}] Dynatrace {tenant_id=} does not exist"
|
227
|
+
)
|
228
|
+
continue
|
229
|
+
dt_client = dependencies.dynatrace_client_by_tenant_id[
|
230
|
+
tenant_id
|
231
|
+
]
|
193
232
|
|
194
|
-
|
195
|
-
|
196
|
-
)
|
197
|
-
if not token_spec:
|
198
|
-
_expose_errors_as_service_log(
|
199
|
-
ocm_client,
|
200
|
-
cluster_uuid=cluster.external_id,
|
201
|
-
error=f"Token spec {cluster.token_spec_name} does not exist",
|
202
|
-
)
|
203
|
-
logging.warn(
|
204
|
-
f"[{cluster.id=}] Token spec '{cluster.token_spec_name}' does not exist"
|
233
|
+
token_spec = dependencies.token_spec_by_name.get(
|
234
|
+
token_binding.spec_name
|
205
235
|
)
|
206
|
-
|
207
|
-
|
208
|
-
|
209
|
-
|
210
|
-
|
236
|
+
if not token_spec:
|
237
|
+
_expose_errors_as_service_log(
|
238
|
+
ocm_client,
|
239
|
+
cluster_uuid=cluster.external_id,
|
240
|
+
error=f"Token spec {token_binding.spec_name} does not exist",
|
241
|
+
)
|
242
|
+
logging.warning(
|
243
|
+
f"[{cluster.id=}] Token spec '{token_binding.spec_name}' does not exist"
|
244
|
+
)
|
245
|
+
continue
|
246
|
+
if tenant_id not in existing_dtp_tokens:
|
247
|
+
existing_dtp_tokens[tenant_id] = (
|
248
|
+
dt_client.get_token_ids_map_for_name_prefix(
|
249
|
+
prefix="dtp"
|
250
|
+
)
|
211
251
|
)
|
212
|
-
)
|
213
252
|
|
214
|
-
|
215
|
-
|
216
|
-
|
217
|
-
|
218
|
-
|
219
|
-
|
220
|
-
|
221
|
-
|
222
|
-
|
223
|
-
|
224
|
-
|
225
|
-
|
226
|
-
|
227
|
-
|
253
|
+
"""
|
254
|
+
Note, that we consciously do not parallelize cluster processing
|
255
|
+
for now. We want to keep stress on OCM at a minimum. The amount
|
256
|
+
of tagged clusters is currently feasible to be processed sequentially.
|
257
|
+
"""
|
258
|
+
self.process_cluster(
|
259
|
+
dry_run=dry_run,
|
260
|
+
cluster=cluster,
|
261
|
+
dt_client=dt_client,
|
262
|
+
ocm_client=ocm_client,
|
263
|
+
existing_dtp_tokens=existing_dtp_tokens[tenant_id],
|
264
|
+
tenant_id=tenant_id,
|
265
|
+
token_spec=token_spec,
|
266
|
+
ocm_env_name=ocm_env_name,
|
267
|
+
)
|
268
|
+
except Exception as e:
|
269
|
+
unhandled_exceptions.append(
|
270
|
+
f"{ocm_env_name}/{cluster.organization_id}/{cluster.id}: {e}"
|
228
271
|
)
|
229
|
-
except Exception as e:
|
230
|
-
unhandled_exceptions.append(
|
231
|
-
f"{ocm_env_name}/{cluster.organization_id}/{cluster.id}: {e}"
|
232
|
-
)
|
233
272
|
self._expose_token_metrics()
|
234
273
|
|
235
274
|
if unhandled_exceptions:
|
@@ -12,3 +12,16 @@ class K8sSecret(BaseModel):
|
|
12
12
|
namespace_name: str
|
13
13
|
secret_name: str
|
14
14
|
tokens: list[DynatraceAPIToken]
|
15
|
+
|
16
|
+
|
17
|
+
class TokenSpecTenantBinding(BaseModel):
|
18
|
+
spec_name: str
|
19
|
+
tenant_id: str
|
20
|
+
|
21
|
+
|
22
|
+
class Cluster(BaseModel):
|
23
|
+
id: str
|
24
|
+
external_id: str
|
25
|
+
organization_id: str
|
26
|
+
is_hcp: bool
|
27
|
+
dt_token_bindings: list[TokenSpecTenantBinding]
|
@@ -1,6 +1,5 @@
|
|
1
1
|
from __future__ import annotations
|
2
2
|
|
3
|
-
import logging
|
4
3
|
from collections.abc import Mapping
|
5
4
|
from datetime import timedelta
|
6
5
|
from typing import Any
|
@@ -21,7 +20,6 @@ from reconcile.utils.ocm.manifests import (
|
|
21
20
|
patch_manifest,
|
22
21
|
)
|
23
22
|
from reconcile.utils.ocm.service_log import create_service_log
|
24
|
-
from reconcile.utils.ocm.sre_capability_labels import sre_capability_label_key
|
25
23
|
from reconcile.utils.ocm.syncsets import (
|
26
24
|
create_syncset,
|
27
25
|
get_syncset,
|
@@ -35,35 +33,25 @@ from reconcile.utils.ocm_base_client import (
|
|
35
33
|
Thin abstractions of reconcile.ocm module to reduce coupling.
|
36
34
|
"""
|
37
35
|
|
38
|
-
DTP_LABEL_SEARCH = sre_capability_label_key("dtp", "%")
|
39
|
-
DTP_TENANT_V2_LABEL = sre_capability_label_key("dtp.v2", "tenant")
|
40
|
-
DTP_SPEC_V2_LABEL = sre_capability_label_key("dtp.v2", "token-spec")
|
41
36
|
|
42
|
-
|
43
|
-
class Cluster(BaseModel):
|
37
|
+
class OCMCluster(BaseModel):
|
44
38
|
id: str
|
45
39
|
external_id: str
|
46
40
|
organization_id: str
|
47
|
-
|
48
|
-
token_spec_name: str
|
41
|
+
subscription_id: str
|
49
42
|
is_hcp: bool
|
43
|
+
labels: Mapping[str, str]
|
50
44
|
|
51
45
|
@staticmethod
|
52
|
-
def from_cluster_details(cluster: ClusterDetails) ->
|
53
|
-
|
54
|
-
|
55
|
-
if not dt_tenant or not token_spec_name:
|
56
|
-
logging.warning(
|
57
|
-
f"[Missing DTP labels] {cluster.ocm_cluster.id=} {cluster.ocm_cluster.subscription.id=} {dt_tenant=} {token_spec_name=}"
|
58
|
-
)
|
59
|
-
return None
|
60
|
-
return Cluster(
|
46
|
+
def from_cluster_details(cluster: ClusterDetails) -> OCMCluster | None:
|
47
|
+
labels = {key: label.value for key, label in cluster.labels.labels.items()}
|
48
|
+
return OCMCluster(
|
61
49
|
id=cluster.ocm_cluster.id,
|
62
50
|
external_id=cluster.ocm_cluster.external_id,
|
63
51
|
organization_id=cluster.organization_id,
|
64
|
-
|
65
|
-
token_spec_name=token_spec_name,
|
52
|
+
subscription_id=cluster.ocm_cluster.subscription.id,
|
66
53
|
is_hcp=cluster.ocm_cluster.is_rosa_hypershift(),
|
54
|
+
labels=labels,
|
67
55
|
)
|
68
56
|
|
69
57
|
|
@@ -117,13 +105,13 @@ class OCMClient:
|
|
117
105
|
manifest_map=manifest_map,
|
118
106
|
)
|
119
107
|
|
120
|
-
def discover_clusters_by_labels(self, label_filter: Filter) -> list[
|
108
|
+
def discover_clusters_by_labels(self, label_filter: Filter) -> list[OCMCluster]:
|
121
109
|
return [
|
122
110
|
cluster
|
123
111
|
for ocm_cluster in discover_clusters_by_labels(
|
124
112
|
ocm_api=self._ocm_client, label_filter=label_filter
|
125
113
|
)
|
126
|
-
if (cluster :=
|
114
|
+
if (cluster := OCMCluster.from_cluster_details(ocm_cluster))
|
127
115
|
]
|
128
116
|
|
129
117
|
def create_service_log(
|
@@ -11541,6 +11541,30 @@
|
|
11541
11541
|
},
|
11542
11542
|
"isDeprecated": false,
|
11543
11543
|
"deprecationReason": null
|
11544
|
+
},
|
11545
|
+
{
|
11546
|
+
"name": "external_users",
|
11547
|
+
"description": null,
|
11548
|
+
"args": [],
|
11549
|
+
"type": {
|
11550
|
+
"kind": "NON_NULL",
|
11551
|
+
"name": null,
|
11552
|
+
"ofType": {
|
11553
|
+
"kind": "LIST",
|
11554
|
+
"name": null,
|
11555
|
+
"ofType": {
|
11556
|
+
"kind": "NON_NULL",
|
11557
|
+
"name": null,
|
11558
|
+
"ofType": {
|
11559
|
+
"kind": "OBJECT",
|
11560
|
+
"name": "ExternalUser_v1",
|
11561
|
+
"ofType": null
|
11562
|
+
}
|
11563
|
+
}
|
11564
|
+
}
|
11565
|
+
},
|
11566
|
+
"isDeprecated": false,
|
11567
|
+
"deprecationReason": null
|
11544
11568
|
}
|
11545
11569
|
],
|
11546
11570
|
"inputFields": null,
|
@@ -27145,13 +27169,21 @@
|
|
27145
27169
|
"description": null,
|
27146
27170
|
"args": [],
|
27147
27171
|
"type": {
|
27148
|
-
"kind": "
|
27149
|
-
"name":
|
27150
|
-
"ofType":
|
27151
|
-
|
27152
|
-
|
27153
|
-
|
27154
|
-
|
27172
|
+
"kind": "SCALAR",
|
27173
|
+
"name": "String",
|
27174
|
+
"ofType": null
|
27175
|
+
},
|
27176
|
+
"isDeprecated": false,
|
27177
|
+
"deprecationReason": null
|
27178
|
+
},
|
27179
|
+
{
|
27180
|
+
"name": "quay_username",
|
27181
|
+
"description": null,
|
27182
|
+
"args": [],
|
27183
|
+
"type": {
|
27184
|
+
"kind": "SCALAR",
|
27185
|
+
"name": "String",
|
27186
|
+
"ofType": null
|
27155
27187
|
},
|
27156
27188
|
"isDeprecated": false,
|
27157
27189
|
"deprecationReason": null
|
@@ -27179,6 +27211,26 @@
|
|
27179
27211
|
},
|
27180
27212
|
"isDeprecated": false,
|
27181
27213
|
"deprecationReason": null
|
27214
|
+
},
|
27215
|
+
{
|
27216
|
+
"name": "roles",
|
27217
|
+
"description": null,
|
27218
|
+
"args": [],
|
27219
|
+
"type": {
|
27220
|
+
"kind": "LIST",
|
27221
|
+
"name": null,
|
27222
|
+
"ofType": {
|
27223
|
+
"kind": "NON_NULL",
|
27224
|
+
"name": null,
|
27225
|
+
"ofType": {
|
27226
|
+
"kind": "OBJECT",
|
27227
|
+
"name": "Role_v1",
|
27228
|
+
"ofType": null
|
27229
|
+
}
|
27230
|
+
}
|
27231
|
+
},
|
27232
|
+
"isDeprecated": false,
|
27233
|
+
"deprecationReason": null
|
27182
27234
|
}
|
27183
27235
|
],
|
27184
27236
|
"inputFields": null,
|
@@ -37,6 +37,9 @@ query QuayMembership {
|
|
37
37
|
bots {
|
38
38
|
quay_username
|
39
39
|
}
|
40
|
+
external_users {
|
41
|
+
quay_username
|
42
|
+
}
|
40
43
|
expirationDate
|
41
44
|
}
|
42
45
|
}
|
@@ -72,9 +75,14 @@ class BotV1(ConfiguredBaseModel):
|
|
72
75
|
quay_username: Optional[str] = Field(..., alias="quay_username")
|
73
76
|
|
74
77
|
|
78
|
+
class ExternalUserV1(ConfiguredBaseModel):
|
79
|
+
quay_username: Optional[str] = Field(..., alias="quay_username")
|
80
|
+
|
81
|
+
|
75
82
|
class RoleV1(ConfiguredBaseModel):
|
76
83
|
users: list[UserV1] = Field(..., alias="users")
|
77
84
|
bots: list[BotV1] = Field(..., alias="bots")
|
85
|
+
external_users: list[ExternalUserV1] = Field(..., alias="external_users")
|
78
86
|
expiration_date: Optional[str] = Field(..., alias="expirationDate")
|
79
87
|
|
80
88
|
|
reconcile/quay_membership.py
CHANGED
@@ -5,6 +5,7 @@ from collections.abc import Sequence
|
|
5
5
|
from reconcile.gql_definitions.quay_membership import quay_membership
|
6
6
|
from reconcile.gql_definitions.quay_membership.quay_membership import (
|
7
7
|
BotV1,
|
8
|
+
ExternalUserV1,
|
8
9
|
PermissionQuayOrgTeamV1,
|
9
10
|
UserV1,
|
10
11
|
)
|
@@ -80,7 +81,7 @@ def fetch_current_state(quay_api_store):
|
|
80
81
|
return state
|
81
82
|
|
82
83
|
|
83
|
-
def get_usernames(users: Sequence[UserV1 | BotV1]) -> list[str]:
|
84
|
+
def get_usernames(users: Sequence[UserV1 | BotV1 | ExternalUserV1]) -> list[str]:
|
84
85
|
return [u.quay_username for u in users if u.quay_username]
|
85
86
|
|
86
87
|
|
@@ -92,7 +93,11 @@ def fetch_desired_state():
|
|
92
93
|
p = process_permission(permission)
|
93
94
|
members: list[str] = []
|
94
95
|
for role in expiration.filter(permission.roles):
|
95
|
-
members +=
|
96
|
+
members += (
|
97
|
+
get_usernames(role.users)
|
98
|
+
+ get_usernames(role.bots)
|
99
|
+
+ get_usernames(role.external_users)
|
100
|
+
)
|
96
101
|
|
97
102
|
state.add(p, members)
|
98
103
|
|
{qontract_reconcile-0.10.2.dev155.dist-info → qontract_reconcile-0.10.2.dev157.dist-info}/WHEEL
RENAMED
File without changes
|
File without changes
|