1
1
#!/usr/bin/env python3.11
2
2
import json
3
3
import os
4
- import openai
4
+ from openai import OpenAI
5
+
6
+ client = OpenAI (api_key = os .getenv ("OPENAI_API_KEY" ))
5
7
import subprocess
6
8
import sys
7
9
import argparse
14
16
parser .add_argument ("--gather-cluster-extension-state" , action = "store_true" ,
15
17
help = "Gather and save a compressed fingerprint of the cluster extension state to a file." )
16
18
parser .add_argument ("--no-tree" , action = "store_true" , help = "Do not print the tree output (only used if gather-cluster-extension-state is set)." )
19
+ parser .add_argument ("--prompt" , action = "store_true" , help = "Create the fingerprint file (if needed) and send it to OpenAI for diagnosis." )
17
20
args = parser .parse_args ()
18
21
19
22
NAMESPACE = args .namespace
20
23
21
- # If gather-cluster-extension-state is used, we want full info regardless of other flags
24
+ # If --prompt is used, we also want full info and to gather fingerprint, regardless of other flags
25
+ if args .prompt :
26
+ args .gather_cluster_extension_state = True
27
+
22
28
if args .gather_cluster_extension_state :
23
29
SHOW_EVENTS = True
24
30
WITH_EVENT_INFO = True
@@ -112,7 +118,6 @@ def get_resources_for_type(resource_name, namespaced):
112
118
113
119
# Collect resources
114
120
for (kind , plural_name , is_namespaced ) in resource_info :
115
- # If we are gathering CE state or SHOW_EVENTS is True, we process events, else skip if no events
116
121
if kind == "Event" and not SHOW_EVENTS :
117
122
continue
118
123
items = get_resources_for_type (plural_name , is_namespaced )
@@ -139,6 +144,7 @@ def get_resources_for_type(resource_name, namespaced):
139
144
uid_to_resource [uid ] = res_entry
140
145
all_uids .add (uid )
141
146
147
+ from collections import defaultdict
142
148
owner_to_children = defaultdict (list )
143
149
for uid , res in uid_to_resource .items ():
144
150
for (_ , _ , o_uid ) in res ["owners" ]:
@@ -211,9 +217,6 @@ def print_tree(uid, prefix="", is_last=True):
211
217
print_tree (c_uid , prefix = child_prefix , is_last = (i == len (children )- 1 ))
212
218
213
219
214
- ###############################
215
- # Code for gather fingerprint
216
- ###############################
217
220
def extract_resource_summary (kind , name , namespace ):
218
221
is_namespaced = (namespace is not None and namespace != "" )
219
222
cmd = ["kubectl" , "get" , kind .lower ()+ "/" + name ]
@@ -274,12 +277,10 @@ def extract_resource_summary(kind, name, namespace):
274
277
return summary
275
278
276
279
def load_fingerprint (file_path ):
277
- """Load the JSON fingerprint file from the specified path."""
278
280
with open (file_path , 'r' ) as f :
279
281
return json .load (f )
280
282
281
283
def generate_prompt (fingerprint ):
282
- """Generate the diagnostic prompt by embedding the fingerprint into the request."""
283
284
prompt = """
284
285
You are an expert in Kubernetes operations and diagnostics. I will provide you with a JSON file that represents a snapshot ("fingerprint") of the entire state of a Kubernetes namespace focusing on a particular ClusterExtension and all related resources. This fingerprint includes:
285
286
@@ -308,26 +309,17 @@ def generate_prompt(fingerprint):
308
309
return prompt
309
310
310
311
def send_to_openai (prompt , model = "gpt-4o" ):
311
- """Send the prompt to OpenAI's completions API and get the response."""
312
312
try :
313
- openai .api_key = os .getenv ("OPENAI_API_KEY" )
314
- if not openai .api_key :
313
+ if os .getenv ("OPENAI_API_KEY" ) is None :
315
314
raise ValueError ("OPENAI_API_KEY environment variable is not set." )
316
-
317
- response = openai .ChatCompletion .create (
318
- model = model ,
319
- messages = [{"role" : "user" , "content" : prompt }]
320
- )
321
-
322
- # Extract and return the assistant's message
323
- message_content = response ['choices' ][0 ]['message' ]['content' ]
315
+ response = client .chat .completions .create (model = model ,
316
+ messages = [{"role" : "user" , "content" : prompt }])
317
+ message_content = response .choices [0 ].message .content
324
318
return message_content
325
-
326
319
except Exception as e :
327
320
return f"Error communicating with OpenAI API: { e } "
328
321
329
322
def gather_fingerprint (namespace ):
330
- # Find cluster extension(s)
331
323
ce_uids = [uid for uid , res in uid_to_resource .items () if res ["kind" ] == "ClusterExtension" and res ["namespace" ] == namespace ]
332
324
if not ce_uids :
333
325
return []
@@ -342,7 +334,6 @@ def process_resource(uid):
342
334
nm = r ["name" ]
343
335
ns = r ["namespace" ]
344
336
summary = extract_resource_summary (k , nm , ns )
345
- # Deduplicate images
346
337
if "containers" in summary :
347
338
new_containers = []
348
339
for c in summary ["containers" ]:
@@ -360,7 +351,6 @@ def process_resource(uid):
360
351
results = []
361
352
for ce_uid in ce_uids :
362
353
fingerprint = {}
363
- # Include all discovered resources
364
354
for uid in uid_to_resource :
365
355
r = uid_to_resource [uid ]
366
356
key = f"{ r ['kind' ]} /{ r ['name' ]} "
@@ -375,12 +365,10 @@ def process_resource(uid):
375
365
results .append (fname )
376
366
return results
377
367
378
- # If gather-cluster-extension-state, generate state file(s)
379
368
state_files = []
380
369
if args .gather_cluster_extension_state :
381
370
state_files = gather_fingerprint (NAMESPACE )
382
371
383
- # Print tree unless --no-tree is given AND we are in gather-cluster-extension-state mode
384
372
if not (args .gather_cluster_extension_state and args .no_tree ):
385
373
for i , uid in enumerate (top_level_kinds ):
386
374
print_tree (uid , prefix = "" , is_last = (i == len (top_level_kinds )- 1 ))
@@ -389,4 +377,16 @@ def process_resource(uid):
389
377
if not state_files :
390
378
print ("No ClusterExtension found in the namespace, no state file created." , file = sys .stderr )
391
379
else :
392
- print ("Created state file(s):" , ", " .join (state_files ))
380
+ print ("Created state file(s):" , ", " .join (state_files ))
381
+
382
+ # If --prompt is used, we already created the fingerprint file. Now load and send to OpenAI.
383
+ if args .prompt :
384
+ if not state_files :
385
+ print ("No ClusterExtension found, cannot prompt OpenAI." , file = sys .stderr )
386
+ sys .exit (1 )
387
+ # Assume one ClusterExtension, take the first file
388
+ fingerprint_data = load_fingerprint (state_files [0 ])
389
+ prompt = generate_prompt (fingerprint_data )
390
+ response = send_to_openai (prompt )
391
+ print ("\n --- OpenAI Diagnosis ---\n " )
392
+ print (response )
0 commit comments