Skip to content

Commit 8ece1e9

Browse files
authored
Fix xlml_to_buganizer.py cannot query two clusters with the same names in two locations (GoogleCloudPlatform#954)
* support multi location for the same cluster in the same project * update * remove useless code * update review
1 parent 29e6ffd commit 8ece1e9

1 file changed

Lines changed: 107 additions & 143 deletions

File tree

dags/dashboard/xlml_to_buganizer.py

Lines changed: 107 additions & 143 deletions
Original file line numberDiff line numberDiff line change
@@ -79,6 +79,102 @@ def build_malfunction_row(
7979
]
8080

8181

82+
def insert_cluster_status_lists(
83+
credentials, status_list, project_name, cluster_name, location, now_utc
84+
):
85+
"""Insert both cluster + nodepool status into list"""
86+
try:
87+
# Cannot query cluster info without location
88+
if not location:
89+
raise NotFound(
90+
"The requested resource can not be found on the server without location."
91+
)
92+
info = get_cluster_status(credentials, project_name, location, cluster_name)
93+
cluster_status = info["status"]
94+
cluster_status_message = info["status_message"]
95+
mal_node_pools = [
96+
node_pool
97+
for node_pool in info["node_pools"]
98+
if node_pool["status"]
99+
not in [
100+
Status.RUNNING.value,
101+
Status.PROVISIONING.value,
102+
Status.STOPPING.value,
103+
]
104+
]
105+
is_cluster_malfunction = (
106+
cluster_status != Status.RUNNING.value
107+
and cluster_status != Status.RECONCILING.value
108+
)
109+
malfunction_node_pools_exist = len(mal_node_pools) > 0
110+
111+
if is_cluster_malfunction and malfunction_node_pools_exist:
112+
status_list.append(
113+
build_malfunction_row(
114+
issue_type=IssueType.CLUSTER_AND_NODE_POOL,
115+
proj=project_name,
116+
cluster_name=cluster_name,
117+
cluster_location=location,
118+
cluster_status=cluster_status,
119+
cluster_status_message=cluster_status_message,
120+
node_pools=mal_node_pools,
121+
now_utc=now_utc,
122+
)
123+
)
124+
elif is_cluster_malfunction:
125+
status_list.append(
126+
build_malfunction_row(
127+
issue_type=IssueType.CLUSTER,
128+
proj=project_name,
129+
cluster_location=location,
130+
cluster_name=cluster_name,
131+
cluster_status=cluster_status,
132+
cluster_status_message=cluster_status_message,
133+
now_utc=now_utc,
134+
)
135+
)
136+
elif malfunction_node_pools_exist:
137+
status_list.append(
138+
build_malfunction_row(
139+
issue_type=IssueType.NODE_POOL,
140+
proj=project_name,
141+
cluster_name=cluster_name,
142+
cluster_location=location,
143+
cluster_status=cluster_status,
144+
cluster_status_message=cluster_status_message,
145+
node_pools=mal_node_pools,
146+
now_utc=now_utc,
147+
)
148+
)
149+
except NotFound:
150+
status_list.append(
151+
build_malfunction_row(
152+
issue_type=IssueType.CLUSTER,
153+
proj=project_name,
154+
cluster_name=cluster_name,
155+
cluster_location=UNKNOW_LOCATION,
156+
cluster_status=Status.NOT_EXIST.value,
157+
cluster_status_message=NOT_FOUND_MSG,
158+
now_utc=now_utc,
159+
)
160+
)
161+
except Exception as e:
162+
logging.info(
163+
f"Error fetching details for {project_name}/{cluster_name}: {e}"
164+
)
165+
status_list.append(
166+
build_malfunction_row(
167+
issue_type=IssueType.CLUSTER,
168+
proj=project_name,
169+
cluster_name=cluster_name,
170+
cluster_location=UNKNOW_LOCATION,
171+
cluster_status=Status.ERROR.value,
172+
cluster_status_message=str(e),
173+
now_utc=now_utc,
174+
)
175+
)
176+
177+
82178
def print_failed_cluster_info(cluster_status_rows):
83179
"""Log failed cluster info for debugging"""
84180
result_rows_list = []
@@ -115,7 +211,7 @@ def insert_gspread_rows(rows: List[List[str]], sheet_id: str):
115211
insert_data_option="INSERT_ROWS",
116212
value_input_option="RAW",
117213
)
118-
logging.info(f"Successfully appended rows to Google Sheet.")
214+
logging.info(f"Successfully appended {len(rows)} rows to Google Sheet.")
119215
except Exception as e:
120216
logging.error(f"An error occurred while writing to Google Sheet: {e}")
121217

@@ -130,7 +226,7 @@ def get_clusters_from_view(credential) -> List[Any]:
130226
dataset_id = context["params"]["source_bq_dataset_id"] or DEFAULT_DATASET_ID
131227
client = bigquery.Client(project=project_id, credentials=credential)
132228
query = f"""
133-
SELECT project_name, cluster_name
229+
SELECT project_name, cluster_name, region
134230
FROM `{project_id}.{dataset_id}.cluster_view`
135231
"""
136232
rows = list(client.query(query).result())
@@ -141,14 +237,6 @@ def get_clusters_from_view(credential) -> List[Any]:
141237
# ================================================================
142238
# Step 4: GKE Functions
143239
# ================================================================
144-
def list_clusters_by_project(credentials, project_id) -> List[Any]:
145-
"""List all clusters in a given GCP project"""
146-
client = container_v1.ClusterManagerClient(credentials=credentials)
147-
parent = f"projects/{project_id}/locations/-"
148-
response = client.list_clusters(request={"parent": parent})
149-
return response.clusters if response and response.clusters else []
150-
151-
152240
def get_cluster_status(
153241
credentials, project_id, location, cluster_name
154242
) -> Dict[str, Any]:
@@ -180,7 +268,7 @@ def get_cluster_status(
180268

181269
return {
182270
"project_id": project_id,
183-
"region": location,
271+
"location": location,
184272
"cluster_name": cluster_name,
185273
"status": container_v1.Cluster.Status(cluster.status).name
186274
if cluster.status
@@ -193,144 +281,25 @@ def get_cluster_status(
193281
# ================================================================
194282
# Step 5: Workflow Functions
195283
# ================================================================
196-
def fetch_clusters_list(credential):
284+
def fetch_clusters(credential) -> List[str]:
197285
clusters = get_clusters_from_view(credential=credential)
198286
if not clusters:
199287
logging.info("No rows found in view.")
200288
return []
201289
return clusters
202290

203291

204-
def fetch_clusters_location(credentials, clusters) -> Dict[str, Any]:
205-
"""Map project::cluster_name -> location"""
206-
cluster_locations = {}
207-
projects = set(cluster.project_name for cluster in clusters)
208-
logging.info(f"Fetching clusters from {len(projects)} distinct projects...")
209-
for idx, project in enumerate(projects, 1):
210-
try:
211-
clusters = list_clusters_by_project(
212-
credentials=credentials, project_id=project
213-
)
214-
logging.info(
215-
f"[{idx}/{len(projects)}] {project}: {len(clusters)} clusters"
216-
)
217-
for cluster in clusters:
218-
key = f"{project}::{cluster.name}"
219-
cluster_locations[key] = cluster.location
220-
except Exception as e:
221-
logging.info(f"Error listing clusters for {project}: {e}")
222-
return cluster_locations
223-
224-
225-
def insert_cluster_status_lists(
226-
credentials, status_list, project_name, location, cluster_name, now_utc
227-
):
228-
"""Insert both cluster + nodepool status into list"""
229-
try:
230-
# Cannot query cluster info without location
231-
if not location:
232-
raise NotFound("The requested resource was not found on the server.")
233-
info = get_cluster_status(credentials, project_name, location, cluster_name)
234-
cluster_status = info["status"]
235-
cluster_status_message = info["status_message"]
236-
mal_node_pools = [
237-
node_pool
238-
for node_pool in info["node_pools"]
239-
if node_pool["status"] != Status.RUNNING.value
240-
and node_pool["status"] != Status.PROVISIONING.value
241-
and node_pool["status"] != Status.STOPPING.value
242-
]
243-
is_cluster_malfunction = (
244-
cluster_status != Status.RUNNING.value
245-
and cluster_status != Status.RECONCILING.value
246-
)
247-
malfunction_node_pools_exist = len(mal_node_pools) > 0
248-
249-
if is_cluster_malfunction and malfunction_node_pools_exist:
250-
status_list.append(
251-
build_malfunction_row(
252-
issue_type=IssueType.CLUSTER_AND_NODE_POOL,
253-
proj=project_name,
254-
cluster_name=cluster_name,
255-
cluster_location=location,
256-
cluster_status=cluster_status,
257-
cluster_status_message=cluster_status_message,
258-
node_pools=mal_node_pools,
259-
now_utc=now_utc,
260-
)
261-
)
262-
elif is_cluster_malfunction:
263-
status_list.append(
264-
build_malfunction_row(
265-
issue_type=IssueType.CLUSTER,
266-
proj=project_name,
267-
cluster_location=location,
268-
cluster_name=cluster_name,
269-
cluster_status=cluster_status,
270-
cluster_status_message=cluster_status_message,
271-
now_utc=now_utc,
272-
)
273-
)
274-
elif malfunction_node_pools_exist:
275-
status_list.append(
276-
build_malfunction_row(
277-
issue_type=IssueType.NODE_POOL,
278-
proj=project_name,
279-
cluster_name=cluster_name,
280-
cluster_location=location,
281-
cluster_status=cluster_status,
282-
cluster_status_message=cluster_status_message,
283-
node_pools=mal_node_pools,
284-
now_utc=now_utc,
285-
)
286-
)
287-
except NotFound:
288-
status_list.append(
289-
build_malfunction_row(
290-
issue_type=IssueType.CLUSTER,
291-
proj=project_name,
292-
cluster_name=cluster_name,
293-
cluster_location=UNKNOW_LOCATION,
294-
cluster_status=Status.NOT_EXIST.value,
295-
cluster_status_message=NOT_FOUND_MSG,
296-
now_utc=now_utc,
297-
)
298-
)
299-
except Exception as e:
300-
logging.info(
301-
f"Error fetching details for {project_name}/{cluster_name}: {e}"
302-
)
303-
status_list.append(
304-
build_malfunction_row(
305-
issue_type=IssueType.CLUSTER,
306-
proj=project_name,
307-
cluster_name=cluster_name,
308-
cluster_location=UNKNOW_LOCATION,
309-
cluster_status=Status.ERROR.value,
310-
cluster_status_message=str(e),
311-
now_utc=now_utc,
312-
)
313-
)
314-
315-
316-
def fetch_clusters_status(
317-
credentials, clusters_list: List[Any], cluster_locations: Dict[str, Any]
318-
) -> List[Any]:
292+
def fetch_clusters_status(credentials, clusters: List[Any]) -> List[Any]:
319293
"""Fetch status for all clusters & node pools"""
320294
cluster_status_rows = []
321295
now_utc = datetime.now(timezone.utc).isoformat()
322-
for cluster in clusters_list:
323-
project_name = cluster.project_name
324-
cluster_name = cluster.cluster_name
325-
key = f"{project_name}::{cluster_name}"
326-
location = cluster_locations.get(key)
327-
# cluster_status_rows is parameter for output
296+
for cluster in clusters:
328297
insert_cluster_status_lists(
329298
credentials,
330299
cluster_status_rows,
331-
project_name,
332-
location,
333-
cluster_name,
300+
cluster.project_name,
301+
cluster.cluster_name,
302+
cluster.region,
334303
now_utc,
335304
)
336305
print_failed_cluster_info(cluster_status_rows)
@@ -345,13 +314,8 @@ def pull_clusters_status() -> List[Any]:
345314
credentials, _ = google.auth.default(
346315
scopes=["https://www.googleapis.com/auth/cloud-platform"]
347316
)
348-
target_clusters_list = fetch_clusters_list(credentials)
349-
target_clusters_locations = fetch_clusters_location(
350-
credentials, target_clusters_list
351-
)
352-
return fetch_clusters_status(
353-
credentials, target_clusters_list, target_clusters_locations
354-
)
317+
target_clusters = fetch_clusters(credentials)
318+
return fetch_clusters_status(credentials, target_clusters)
355319

356320

357321
@task

0 commit comments

Comments
 (0)