qontract-reconcile 0.10.1rc946__py3-none-any.whl → 0.10.1rc947__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.1rc946.dist-info → qontract_reconcile-0.10.1rc947.dist-info}/METADATA +1 -1
- {qontract_reconcile-0.10.1rc946.dist-info → qontract_reconcile-0.10.1rc947.dist-info}/RECORD +8 -6
- reconcile/dynatrace_token_provider/dependencies.py +54 -0
- reconcile/dynatrace_token_provider/integration.py +81 -132
- reconcile/dynatrace_token_provider/ocm.py +97 -0
- {qontract_reconcile-0.10.1rc946.dist-info → qontract_reconcile-0.10.1rc947.dist-info}/WHEEL +0 -0
- {qontract_reconcile-0.10.1rc946.dist-info → qontract_reconcile-0.10.1rc947.dist-info}/entry_points.txt +0 -0
- {qontract_reconcile-0.10.1rc946.dist-info → qontract_reconcile-0.10.1rc947.dist-info}/top_level.txt +0 -0
{qontract_reconcile-0.10.1rc946.dist-info → qontract_reconcile-0.10.1rc947.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.1rc947
|
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.1rc946.dist-info → qontract_reconcile-0.10.1rc947.dist-info}/RECORD
RENAMED
@@ -179,8 +179,10 @@ reconcile/cna/assets/asset.py,sha256=KWgA4fuDAEGsJwmR52WwK_YgSJMW-1cV2la3lmNf4iE
|
|
179
179
|
reconcile/cna/assets/asset_factory.py,sha256=7T7X_J6xIsoGETqBRI45_EyIKEdQcnRPt_GAuVuLQcc,785
|
180
180
|
reconcile/cna/assets/null.py,sha256=85mVh97atCoC0aLuX47poTZiyOthmziJeBsUw0c924w,1658
|
181
181
|
reconcile/dynatrace_token_provider/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
182
|
-
reconcile/dynatrace_token_provider/
|
182
|
+
reconcile/dynatrace_token_provider/dependencies.py,sha256=uJLvR48kxfqjnBuP60XQx5RbkWPL3__FCgWjqwhKEjo,2160
|
183
|
+
reconcile/dynatrace_token_provider/integration.py,sha256=Tt7TxLqhEZW0bpnNVMDwf8ATZJqviYBaPCw-jO4So9Y,14712
|
183
184
|
reconcile/dynatrace_token_provider/metrics.py,sha256=xiKkl8fTEBQaXJelGCPNTZhHAWdO1M3pCXNr_Tei63c,1285
|
185
|
+
reconcile/dynatrace_token_provider/ocm.py,sha256=IwksRMyGcJnamV88ORlBoyOr7uRENhMaHBoSXaGfwDY,2784
|
184
186
|
reconcile/external_resources/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
185
187
|
reconcile/external_resources/aws.py,sha256=JvjKaABy2Pg8u8Lq82Acv4zMvpE3_qGKes7OG-zlHOM,2956
|
186
188
|
reconcile/external_resources/factories.py,sha256=DXgaLxoO87zZ76VOpRpu2GeYGhsbfOnOx5mrzgo4Gf4,4767
|
@@ -842,8 +844,8 @@ tools/test/test_qontract_cli.py,sha256=_D61RFGAN5x44CY1tYbouhlGXXABwYfxKSWSQx3Jr
|
|
842
844
|
tools/test/test_saas_promotion_state.py,sha256=dy4kkSSAQ7bC0Xp2CociETGN-2aABEfL6FU5D9Jl00Y,6056
|
843
845
|
tools/test/test_sd_app_sre_alert_report.py,sha256=v363r9zM7__0kR5K6mvJoGFcM9BvE33fWAayrqkpojA,2116
|
844
846
|
tools/test/test_sre_checkpoints.py,sha256=SKqPPTl9ua0RFdSSofnoQX-JZE6dFLO3LRhfQzqtfh8,2607
|
845
|
-
qontract_reconcile-0.10.
|
846
|
-
qontract_reconcile-0.10.
|
847
|
-
qontract_reconcile-0.10.
|
848
|
-
qontract_reconcile-0.10.
|
849
|
-
qontract_reconcile-0.10.
|
847
|
+
qontract_reconcile-0.10.1rc947.dist-info/METADATA,sha256=QNbbPwtFuuLOZcxaB5ZtfE7CwXA1JNufPkLI1Lu6h6U,2262
|
848
|
+
qontract_reconcile-0.10.1rc947.dist-info/WHEEL,sha256=GJ7t_kWBFywbagK5eo9IoUwLW6oyOeTKmQ-9iHFVNxQ,92
|
849
|
+
qontract_reconcile-0.10.1rc947.dist-info/entry_points.txt,sha256=GKQqCl2j2X1BJQ69een6rHcR26PmnxnONLNOQB-nRjY,491
|
850
|
+
qontract_reconcile-0.10.1rc947.dist-info/top_level.txt,sha256=l5ISPoXzt0SdR4jVdkfa7RPSKNc8zAHYWAnR-Dw8Ey8,24
|
851
|
+
qontract_reconcile-0.10.1rc947.dist-info/RECORD,,
|
@@ -0,0 +1,54 @@
|
|
1
|
+
from reconcile.dynatrace_token_provider.ocm import OCMClient
|
2
|
+
from reconcile.typed_queries.dynatrace_environments import get_dynatrace_environments
|
3
|
+
from reconcile.typed_queries.ocm import get_ocm_environments
|
4
|
+
from reconcile.utils.dynatrace.client import DynatraceClient
|
5
|
+
from reconcile.utils.ocm_base_client import (
|
6
|
+
init_ocm_base_client,
|
7
|
+
)
|
8
|
+
from reconcile.utils.secret_reader import SecretReaderBase
|
9
|
+
|
10
|
+
|
11
|
+
class Dependencies:
|
12
|
+
"""
|
13
|
+
Depenedencies class to hold all the dependencies (API clients) for the Dynatrace Token Provider.
|
14
|
+
Dependency inversion simplifies setting up tests.
|
15
|
+
"""
|
16
|
+
|
17
|
+
def __init__(
|
18
|
+
self,
|
19
|
+
secret_reader: SecretReaderBase,
|
20
|
+
dynatrace_client_by_tenant_id: dict[str, DynatraceClient],
|
21
|
+
ocm_client_by_env_name: dict[str, OCMClient],
|
22
|
+
):
|
23
|
+
self.secret_reader = secret_reader
|
24
|
+
self.dynatrace_client_by_tenant_id: dict[str, DynatraceClient] = (
|
25
|
+
dynatrace_client_by_tenant_id
|
26
|
+
)
|
27
|
+
self.ocm_client_by_env_name: dict[str, OCMClient] = ocm_client_by_env_name
|
28
|
+
|
29
|
+
def populate(self) -> None:
|
30
|
+
self._populate_dynatrace_client_map()
|
31
|
+
self._populate_ocm_clients()
|
32
|
+
|
33
|
+
def _populate_dynatrace_client_map(self) -> None:
|
34
|
+
dynatrace_environments = get_dynatrace_environments()
|
35
|
+
if not dynatrace_environments:
|
36
|
+
raise RuntimeError("No Dynatrace environment defined.")
|
37
|
+
for tenant in dynatrace_environments:
|
38
|
+
dt_api_token = self.secret_reader.read_secret(tenant.bootstrap_token)
|
39
|
+
dt_client = DynatraceClient.create(
|
40
|
+
environment_url=tenant.environment_url,
|
41
|
+
token=dt_api_token,
|
42
|
+
api=None,
|
43
|
+
)
|
44
|
+
tenant_id = tenant.environment_url.split(".")[0].removeprefix("https://")
|
45
|
+
self.dynatrace_client_by_tenant_id[tenant_id] = dt_client
|
46
|
+
|
47
|
+
def _populate_ocm_clients(self) -> None:
|
48
|
+
ocm_environments = get_ocm_environments()
|
49
|
+
self.ocm_client_by_env_name = {
|
50
|
+
env.name: OCMClient(
|
51
|
+
ocm_client=init_ocm_base_client(env, self.secret_reader)
|
52
|
+
)
|
53
|
+
for env in ocm_environments
|
54
|
+
}
|
@@ -1,56 +1,29 @@
|
|
1
1
|
import base64
|
2
2
|
import logging
|
3
|
-
import sys
|
4
3
|
from collections.abc import Iterable, Mapping
|
5
4
|
from datetime import timedelta
|
6
5
|
from typing import Any
|
7
6
|
|
8
|
-
from
|
9
|
-
from dynatrace.environment_v2.tokens_api import ApiTokenCreated
|
10
|
-
|
7
|
+
from reconcile.dynatrace_token_provider.dependencies import Dependencies
|
11
8
|
from reconcile.dynatrace_token_provider.metrics import (
|
12
9
|
DTPClustersManagedGauge,
|
13
10
|
DTPOrganizationErrorRate,
|
14
11
|
)
|
15
|
-
from reconcile.
|
16
|
-
query as ocm_environment_query,
|
17
|
-
)
|
18
|
-
from reconcile.gql_definitions.dynatrace_token_provider import (
|
19
|
-
dynatrace_bootstrap_tokens,
|
20
|
-
)
|
21
|
-
from reconcile.gql_definitions.dynatrace_token_provider.dynatrace_bootstrap_tokens import (
|
22
|
-
DynatraceEnvironmentQueryData,
|
23
|
-
)
|
24
|
-
from reconcile.gql_definitions.fragments.ocm_environment import OCMEnvironment
|
12
|
+
from reconcile.dynatrace_token_provider.ocm import Cluster, OCMClient
|
25
13
|
from reconcile.utils import (
|
26
|
-
gql,
|
27
14
|
metrics,
|
28
15
|
)
|
16
|
+
from reconcile.utils.dynatrace.client import DynatraceAPITokenCreated, DynatraceClient
|
29
17
|
from reconcile.utils.ocm.base import (
|
30
18
|
OCMClusterServiceLogCreateModel,
|
31
19
|
OCMServiceLogSeverity,
|
32
20
|
)
|
33
|
-
from reconcile.utils.ocm.clusters import (
|
34
|
-
ClusterDetails,
|
35
|
-
discover_clusters_by_labels,
|
36
|
-
)
|
37
21
|
from reconcile.utils.ocm.labels import subscription_label_filter
|
38
|
-
from reconcile.utils.ocm.service_log import create_service_log
|
39
22
|
from reconcile.utils.ocm.sre_capability_labels import sre_capability_label_key
|
40
|
-
from reconcile.utils.ocm.syncsets import (
|
41
|
-
create_syncset,
|
42
|
-
get_syncset,
|
43
|
-
patch_syncset,
|
44
|
-
)
|
45
|
-
from reconcile.utils.ocm_base_client import (
|
46
|
-
OCMBaseClient,
|
47
|
-
init_ocm_base_client,
|
48
|
-
)
|
49
23
|
from reconcile.utils.runtime.integration import (
|
50
24
|
PydanticRunParams,
|
51
25
|
QontractReconcileIntegration,
|
52
26
|
)
|
53
|
-
from reconcile.utils.secret_reader import SecretReaderBase
|
54
27
|
|
55
28
|
QONTRACT_INTEGRATION = "dynatrace-token-provider"
|
56
29
|
SYNCSET_ID = "ext-dynatrace-tokens-dtp"
|
@@ -81,12 +54,19 @@ class DynatraceTokenProviderIntegration(
|
|
81
54
|
return QONTRACT_INTEGRATION
|
82
55
|
|
83
56
|
def run(self, dry_run: bool) -> None:
|
57
|
+
dependencies = Dependencies(
|
58
|
+
secret_reader=self.secret_reader,
|
59
|
+
dynatrace_client_by_tenant_id={},
|
60
|
+
ocm_client_by_env_name={},
|
61
|
+
)
|
62
|
+
dependencies.populate()
|
63
|
+
self.reconcile(dry_run=dry_run, dependencies=dependencies)
|
64
|
+
|
65
|
+
def reconcile(self, dry_run: bool, dependencies: Dependencies) -> None:
|
84
66
|
with metrics.transactional_metrics(self.name):
|
85
67
|
unhandled_exceptions = []
|
86
|
-
for
|
87
|
-
|
88
|
-
clusters = discover_clusters_by_labels(
|
89
|
-
ocm_api=ocm_client,
|
68
|
+
for ocm_env_name, ocm_client in dependencies.ocm_client_by_env_name.items():
|
69
|
+
clusters = ocm_client.discover_clusters_by_labels(
|
90
70
|
label_filter=subscription_label_filter().like(
|
91
71
|
"key", dtp_label_key("%")
|
92
72
|
),
|
@@ -94,7 +74,7 @@ class DynatraceTokenProviderIntegration(
|
|
94
74
|
metrics.set_gauge(
|
95
75
|
DTPClustersManagedGauge(
|
96
76
|
integration=self.name,
|
97
|
-
ocm_env=
|
77
|
+
ocm_env=ocm_env_name,
|
98
78
|
),
|
99
79
|
len(clusters),
|
100
80
|
)
|
@@ -106,7 +86,6 @@ class DynatraceTokenProviderIntegration(
|
|
106
86
|
for cluster in clusters
|
107
87
|
if cluster.organization_id in self.params.ocm_organization_ids
|
108
88
|
]
|
109
|
-
dt_clients = self.get_all_dynatrace_clients(self.secret_reader)
|
110
89
|
dtp_tenant_label_key = f"{dtp_label_key(None)}.tenant"
|
111
90
|
existing_dtp_tokens = {}
|
112
91
|
|
@@ -114,87 +93,60 @@ class DynatraceTokenProviderIntegration(
|
|
114
93
|
try:
|
115
94
|
with DTPOrganizationErrorRate(
|
116
95
|
integration=self.name,
|
117
|
-
ocm_env=
|
96
|
+
ocm_env=ocm_env_name,
|
118
97
|
org_id=cluster.organization_id,
|
119
98
|
):
|
120
|
-
tenant_id = cluster.
|
121
|
-
dtp_tenant_label_key
|
122
|
-
)
|
99
|
+
tenant_id = cluster.dt_tenant
|
123
100
|
if not tenant_id:
|
124
101
|
_expose_errors_as_service_log(
|
125
102
|
ocm_client,
|
126
|
-
cluster_uuid=cluster.
|
103
|
+
cluster_uuid=cluster.external_id,
|
127
104
|
error=f"Missing label {dtp_tenant_label_key}",
|
128
105
|
)
|
129
106
|
continue
|
130
|
-
if
|
107
|
+
if (
|
108
|
+
tenant_id
|
109
|
+
not in dependencies.dynatrace_client_by_tenant_id
|
110
|
+
):
|
131
111
|
_expose_errors_as_service_log(
|
132
112
|
ocm_client,
|
133
|
-
cluster_uuid=cluster.
|
113
|
+
cluster_uuid=cluster.external_id,
|
134
114
|
error=f"Dynatrace tenant {tenant_id} does not exist",
|
135
115
|
)
|
136
116
|
continue
|
117
|
+
dt_client = dependencies.dynatrace_client_by_tenant_id[
|
118
|
+
tenant_id
|
119
|
+
]
|
137
120
|
|
138
121
|
if tenant_id not in existing_dtp_tokens:
|
139
122
|
existing_dtp_tokens[tenant_id] = (
|
140
|
-
|
123
|
+
dt_client.get_token_ids_for_name_prefix(
|
124
|
+
prefix="dtp-"
|
125
|
+
)
|
141
126
|
)
|
142
127
|
|
143
128
|
self.process_cluster(
|
144
129
|
dry_run,
|
145
130
|
cluster,
|
146
|
-
|
131
|
+
dt_client,
|
147
132
|
ocm_client,
|
148
133
|
existing_dtp_tokens[tenant_id],
|
149
134
|
tenant_id,
|
150
135
|
)
|
151
136
|
except Exception as e:
|
152
137
|
unhandled_exceptions.append(
|
153
|
-
f"{
|
138
|
+
f"{ocm_env_name}/{cluster.organization_id}/{cluster.external_id}: {e}"
|
154
139
|
)
|
155
140
|
|
156
141
|
if unhandled_exceptions:
|
157
142
|
raise ReconcileErrorSummary(unhandled_exceptions)
|
158
|
-
sys.exit(0)
|
159
|
-
|
160
|
-
def get_ocm_environments(self) -> list[OCMEnvironment]:
|
161
|
-
return ocm_environment_query(gql.get_api().query).environments
|
162
|
-
|
163
|
-
def get_all_dynatrace_tenants(self) -> DynatraceEnvironmentQueryData:
|
164
|
-
dt_tenants = dynatrace_bootstrap_tokens.query(query_func=gql.get_api().query)
|
165
|
-
return dt_tenants
|
166
|
-
|
167
|
-
def get_all_dynatrace_clients(
|
168
|
-
self, secret_reader: SecretReaderBase
|
169
|
-
) -> dict[str, Dynatrace]:
|
170
|
-
dt_tenants = self.get_all_dynatrace_tenants()
|
171
|
-
dynatrace_clients = {}
|
172
|
-
if not dt_tenants.environments:
|
173
|
-
raise RuntimeError("No Dynatrace environment defined.")
|
174
|
-
for tenant in dt_tenants.environments:
|
175
|
-
dt_bootstrap_token = secret_reader.read_secret(tenant.bootstrap_token)
|
176
|
-
dt_client = Dynatrace(
|
177
|
-
tenant.environment_url,
|
178
|
-
dt_bootstrap_token,
|
179
|
-
)
|
180
|
-
tenant_id = tenant.environment_url.split(".")[0].removeprefix("https://")
|
181
|
-
dynatrace_clients[tenant_id] = dt_client
|
182
|
-
return dynatrace_clients
|
183
|
-
|
184
|
-
def get_all_dtp_tokens(self, dt_client: Dynatrace) -> list[str]:
|
185
|
-
try:
|
186
|
-
dt_tokens = dt_client.tokens.list()
|
187
|
-
except Exception as e:
|
188
|
-
logging.error("Failed to retrieve all dtp tokens")
|
189
|
-
raise e
|
190
|
-
return [token.id for token in dt_tokens if token.name.startswith("dtp-")]
|
191
143
|
|
192
144
|
def process_cluster(
|
193
145
|
self,
|
194
146
|
dry_run: bool,
|
195
|
-
cluster:
|
196
|
-
dt_client:
|
197
|
-
ocm_client:
|
147
|
+
cluster: Cluster,
|
148
|
+
dt_client: DynatraceClient,
|
149
|
+
ocm_client: OCMClient,
|
198
150
|
existing_dtp_tokens: Iterable[str],
|
199
151
|
tenant_id: str,
|
200
152
|
) -> None:
|
@@ -204,11 +156,10 @@ class DynatraceTokenProviderIntegration(
|
|
204
156
|
if not dry_run:
|
205
157
|
try:
|
206
158
|
(ingestion_token, operator_token) = self.create_dynatrace_tokens(
|
207
|
-
dt_client, cluster.
|
159
|
+
dt_client, cluster.external_id
|
208
160
|
)
|
209
|
-
create_syncset(
|
210
|
-
|
211
|
-
cluster.ocm_cluster.id,
|
161
|
+
ocm_client.create_syncset(
|
162
|
+
cluster.id,
|
212
163
|
self.construct_syncset(
|
213
164
|
ingestion_token, operator_token, dt_api_url
|
214
165
|
),
|
@@ -216,46 +167,46 @@ class DynatraceTokenProviderIntegration(
|
|
216
167
|
except Exception as e:
|
217
168
|
_expose_errors_as_service_log(
|
218
169
|
ocm_client,
|
219
|
-
cluster.
|
170
|
+
cluster.external_id,
|
220
171
|
f"DTP can't create Syncset with the tokens {str(e.args)}",
|
221
172
|
)
|
222
173
|
logging.info(
|
223
|
-
f"Ingestion and operator tokens created in Dynatrace for cluster {cluster.
|
174
|
+
f"Ingestion and operator tokens created in Dynatrace for cluster {cluster.external_id}."
|
224
175
|
)
|
225
176
|
logging.info(
|
226
|
-
f"SyncSet {SYNCSET_ID} created in cluster {cluster.
|
177
|
+
f"SyncSet {SYNCSET_ID} created in cluster {cluster.external_id}."
|
227
178
|
)
|
228
179
|
else:
|
229
180
|
tokens = self.get_tokens_from_syncset(existing_syncset)
|
230
181
|
need_patching = False
|
231
182
|
for token_name, token in tokens.items():
|
232
|
-
if token
|
183
|
+
if token.id not in existing_dtp_tokens:
|
233
184
|
need_patching = True
|
234
185
|
logging.info(f"{token_name} missing in Dynatrace.")
|
235
186
|
if token_name == DYNATRACE_INGESTION_TOKEN_NAME:
|
236
187
|
if not dry_run:
|
237
188
|
ingestion_token = self.create_dynatrace_ingestion_token(
|
238
|
-
dt_client, cluster.
|
189
|
+
dt_client, cluster.external_id
|
239
190
|
)
|
240
|
-
token
|
241
|
-
token
|
191
|
+
token.id = ingestion_token.id
|
192
|
+
token.token = ingestion_token.token
|
242
193
|
logging.info(
|
243
|
-
f"Ingestion token created in Dynatrace for cluster {cluster.
|
194
|
+
f"Ingestion token created in Dynatrace for cluster {cluster.external_id}."
|
244
195
|
)
|
245
196
|
elif token_name == DYNATRACE_OPERATOR_TOKEN_NAME:
|
246
197
|
if not dry_run:
|
247
198
|
operator_token = self.create_dynatrace_operator_token(
|
248
|
-
dt_client, cluster.
|
199
|
+
dt_client, cluster.external_id
|
249
200
|
)
|
250
|
-
token
|
251
|
-
token
|
201
|
+
token.id = operator_token.id
|
202
|
+
token.token = operator_token.token
|
252
203
|
logging.info(
|
253
|
-
f"Operator token created in Dynatrace for cluster {cluster.
|
204
|
+
f"Operator token created in Dynatrace for cluster {cluster.external_id}."
|
254
205
|
)
|
255
206
|
elif token_name == DYNATRACE_INGESTION_TOKEN_NAME:
|
256
|
-
ingestion_token =
|
207
|
+
ingestion_token = token
|
257
208
|
elif token_name == DYNATRACE_OPERATOR_TOKEN_NAME:
|
258
|
-
operator_token =
|
209
|
+
operator_token = token
|
259
210
|
if need_patching:
|
260
211
|
if not dry_run:
|
261
212
|
patch_syncset_payload = self.construct_base_syncset(
|
@@ -265,25 +216,22 @@ class DynatraceTokenProviderIntegration(
|
|
265
216
|
)
|
266
217
|
try:
|
267
218
|
logging.info(f"Patching syncset {SYNCSET_ID}.")
|
268
|
-
patch_syncset(
|
269
|
-
|
270
|
-
cluster_id=cluster.ocm_cluster.id,
|
219
|
+
ocm_client.patch_syncset(
|
220
|
+
cluster_id=cluster.id,
|
271
221
|
syncset_id=SYNCSET_ID,
|
272
222
|
syncset_map=patch_syncset_payload,
|
273
223
|
)
|
274
224
|
except Exception as e:
|
275
225
|
_expose_errors_as_service_log(
|
276
226
|
ocm_client,
|
277
|
-
cluster.
|
227
|
+
cluster.external_id,
|
278
228
|
f"DTP can't patch Syncset {SYNCSET_ID} due to {str(e.args)}",
|
279
229
|
)
|
280
230
|
logging.info(f"Syncset {SYNCSET_ID} patched.")
|
281
231
|
|
282
|
-
def get_syncset(
|
283
|
-
self, ocm_client: OCMBaseClient, cluster: ClusterDetails
|
284
|
-
) -> dict[str, Any]:
|
232
|
+
def get_syncset(self, ocm_client: OCMClient, cluster: Cluster) -> dict[str, Any]:
|
285
233
|
try:
|
286
|
-
syncset = get_syncset(
|
234
|
+
syncset = ocm_client.get_syncset(cluster.id, SYNCSET_ID)
|
287
235
|
except Exception as e:
|
288
236
|
if "Not Found" in e.args[0]:
|
289
237
|
syncset = None
|
@@ -291,7 +239,9 @@ class DynatraceTokenProviderIntegration(
|
|
291
239
|
raise e
|
292
240
|
return syncset
|
293
241
|
|
294
|
-
def get_tokens_from_syncset(
|
242
|
+
def get_tokens_from_syncset(
|
243
|
+
self, syncset: Mapping[str, Any]
|
244
|
+
) -> dict[str, DynatraceAPITokenCreated]:
|
295
245
|
tokens: dict[str, Any] = {}
|
296
246
|
for resource in syncset["resources"]:
|
297
247
|
if resource["kind"] == "Secret":
|
@@ -301,20 +251,20 @@ class DynatraceTokenProviderIntegration(
|
|
301
251
|
resource["data"]["dataIngestTokenId"]
|
302
252
|
)
|
303
253
|
ingest_token = self.base64_decode(resource["data"]["dataIngestToken"])
|
304
|
-
tokens[DYNATRACE_INGESTION_TOKEN_NAME] =
|
305
|
-
|
306
|
-
|
307
|
-
|
308
|
-
tokens[DYNATRACE_OPERATOR_TOKEN_NAME] =
|
309
|
-
|
310
|
-
|
311
|
-
|
254
|
+
tokens[DYNATRACE_INGESTION_TOKEN_NAME] = DynatraceAPITokenCreated(
|
255
|
+
id=ingest_token_id,
|
256
|
+
token=ingest_token,
|
257
|
+
)
|
258
|
+
tokens[DYNATRACE_OPERATOR_TOKEN_NAME] = DynatraceAPITokenCreated(
|
259
|
+
id=operator_token_id,
|
260
|
+
token=operator_token,
|
261
|
+
)
|
312
262
|
return tokens
|
313
263
|
|
314
264
|
def construct_base_syncset(
|
315
265
|
self,
|
316
|
-
ingestion_token:
|
317
|
-
operator_token:
|
266
|
+
ingestion_token: DynatraceAPITokenCreated,
|
267
|
+
operator_token: DynatraceAPITokenCreated,
|
318
268
|
dt_api_url: str,
|
319
269
|
) -> dict[str, Any]:
|
320
270
|
return {
|
@@ -346,8 +296,8 @@ class DynatraceTokenProviderIntegration(
|
|
346
296
|
|
347
297
|
def construct_syncset(
|
348
298
|
self,
|
349
|
-
ingestion_token:
|
350
|
-
operator_token:
|
299
|
+
ingestion_token: DynatraceAPITokenCreated,
|
300
|
+
operator_token: DynatraceAPITokenCreated,
|
351
301
|
dt_api_url: str,
|
352
302
|
) -> dict[str, Any]:
|
353
303
|
syncset = self.construct_base_syncset(
|
@@ -359,17 +309,17 @@ class DynatraceTokenProviderIntegration(
|
|
359
309
|
return syncset
|
360
310
|
|
361
311
|
def create_dynatrace_ingestion_token(
|
362
|
-
self, dt_client:
|
363
|
-
) ->
|
364
|
-
return dt_client.
|
312
|
+
self, dt_client: DynatraceClient, cluster_uuid: str
|
313
|
+
) -> DynatraceAPITokenCreated:
|
314
|
+
return dt_client.create_api_token(
|
365
315
|
name=f"dtp-ingestion-token-{cluster_uuid}",
|
366
316
|
scopes=["metrics.ingest", "logs.ingest", "events.ingest"],
|
367
317
|
)
|
368
318
|
|
369
319
|
def create_dynatrace_operator_token(
|
370
|
-
self, dt_client:
|
371
|
-
) ->
|
372
|
-
return dt_client.
|
320
|
+
self, dt_client: DynatraceClient, cluster_uuid: str
|
321
|
+
) -> DynatraceAPITokenCreated:
|
322
|
+
return dt_client.create_api_token(
|
373
323
|
name=f"dtp-operator-token-{cluster_uuid}",
|
374
324
|
scopes=[
|
375
325
|
"activeGateTokenManagement.create",
|
@@ -382,8 +332,8 @@ class DynatraceTokenProviderIntegration(
|
|
382
332
|
)
|
383
333
|
|
384
334
|
def create_dynatrace_tokens(
|
385
|
-
self, dt_client:
|
386
|
-
) -> tuple[
|
335
|
+
self, dt_client: DynatraceClient, cluster_uuid: str
|
336
|
+
) -> tuple[DynatraceAPITokenCreated, DynatraceAPITokenCreated]:
|
387
337
|
ingestion_token = self.create_dynatrace_ingestion_token(dt_client, cluster_uuid)
|
388
338
|
operation_token = self.create_dynatrace_operator_token(dt_client, cluster_uuid)
|
389
339
|
return (ingestion_token, operation_token)
|
@@ -394,10 +344,9 @@ def dtp_label_key(config_atom: str | None) -> str:
|
|
394
344
|
|
395
345
|
|
396
346
|
def _expose_errors_as_service_log(
|
397
|
-
ocm_api:
|
347
|
+
ocm_api: OCMClient, cluster_uuid: str, error: str
|
398
348
|
) -> None:
|
399
|
-
create_service_log(
|
400
|
-
ocm_api=ocm_api,
|
349
|
+
ocm_api.create_service_log(
|
401
350
|
service_log=OCMClusterServiceLogCreateModel(
|
402
351
|
cluster_uuid=cluster_uuid,
|
403
352
|
severity=OCMServiceLogSeverity.Warning,
|
@@ -0,0 +1,97 @@
|
|
1
|
+
from __future__ import annotations
|
2
|
+
|
3
|
+
from collections.abc import Mapping
|
4
|
+
from datetime import timedelta
|
5
|
+
from typing import Any
|
6
|
+
|
7
|
+
from pydantic import BaseModel
|
8
|
+
|
9
|
+
from reconcile.utils.ocm.base import (
|
10
|
+
OCMClusterServiceLogCreateModel,
|
11
|
+
)
|
12
|
+
from reconcile.utils.ocm.clusters import (
|
13
|
+
ClusterDetails,
|
14
|
+
discover_clusters_by_labels,
|
15
|
+
)
|
16
|
+
from reconcile.utils.ocm.labels import Filter
|
17
|
+
from reconcile.utils.ocm.service_log import create_service_log
|
18
|
+
from reconcile.utils.ocm.sre_capability_labels import sre_capability_label_key
|
19
|
+
from reconcile.utils.ocm.syncsets import (
|
20
|
+
create_syncset,
|
21
|
+
get_syncset,
|
22
|
+
patch_syncset,
|
23
|
+
)
|
24
|
+
from reconcile.utils.ocm_base_client import (
|
25
|
+
OCMBaseClient,
|
26
|
+
)
|
27
|
+
|
28
|
+
"""
|
29
|
+
Thin abstractions of reconcile.ocm module to reduce coupling.
|
30
|
+
"""
|
31
|
+
|
32
|
+
|
33
|
+
class Cluster(BaseModel):
|
34
|
+
id: str
|
35
|
+
external_id: str
|
36
|
+
organization_id: str
|
37
|
+
dt_tenant: str
|
38
|
+
|
39
|
+
@staticmethod
|
40
|
+
def from_cluster_details(cluster: ClusterDetails) -> Cluster:
|
41
|
+
dt_tenant = cluster.labels.get_label_value(
|
42
|
+
f"{sre_capability_label_key('dtp', None)}.tenant"
|
43
|
+
)
|
44
|
+
return Cluster(
|
45
|
+
id=cluster.ocm_cluster.id,
|
46
|
+
external_id=cluster.ocm_cluster.external_id,
|
47
|
+
organization_id=cluster.organization_id,
|
48
|
+
dt_tenant=dt_tenant,
|
49
|
+
)
|
50
|
+
|
51
|
+
|
52
|
+
class OCMClient:
|
53
|
+
"""
|
54
|
+
Thin OOP wrapper around OCMBaseClient to avoid function mocking in tests
|
55
|
+
"""
|
56
|
+
|
57
|
+
def __init__(self, ocm_client: OCMBaseClient):
|
58
|
+
self._ocm_client = ocm_client
|
59
|
+
|
60
|
+
def create_syncset(self, cluster_id: str, syncset_map: Mapping) -> None:
|
61
|
+
create_syncset(
|
62
|
+
ocm_client=self._ocm_client, cluster_id=cluster_id, syncset_map=syncset_map
|
63
|
+
)
|
64
|
+
|
65
|
+
def get_syncset(self, cluster_id: str, syncset_id: str) -> Any:
|
66
|
+
return get_syncset(
|
67
|
+
ocm_client=self._ocm_client, cluster_id=cluster_id, syncset_id=syncset_id
|
68
|
+
)
|
69
|
+
|
70
|
+
def patch_syncset(
|
71
|
+
self, cluster_id: str, syncset_id: str, syncset_map: Mapping
|
72
|
+
) -> None:
|
73
|
+
patch_syncset(
|
74
|
+
ocm_client=self._ocm_client,
|
75
|
+
cluster_id=cluster_id,
|
76
|
+
syncset_id=syncset_id,
|
77
|
+
syncset_map=syncset_map,
|
78
|
+
)
|
79
|
+
|
80
|
+
def discover_clusters_by_labels(self, label_filter: Filter) -> list[Cluster]:
|
81
|
+
return [
|
82
|
+
Cluster.from_cluster_details(cluster)
|
83
|
+
for cluster in discover_clusters_by_labels(
|
84
|
+
ocm_api=self._ocm_client, label_filter=label_filter
|
85
|
+
)
|
86
|
+
]
|
87
|
+
|
88
|
+
def create_service_log(
|
89
|
+
self,
|
90
|
+
service_log: OCMClusterServiceLogCreateModel,
|
91
|
+
dedup_interval: timedelta | None,
|
92
|
+
) -> None:
|
93
|
+
create_service_log(
|
94
|
+
ocm_api=self._ocm_client,
|
95
|
+
service_log=service_log,
|
96
|
+
dedup_interval=dedup_interval,
|
97
|
+
)
|
File without changes
|
File without changes
|
{qontract_reconcile-0.10.1rc946.dist-info → qontract_reconcile-0.10.1rc947.dist-info}/top_level.txt
RENAMED
File without changes
|