@@ -1048,7 +1048,7 @@ var upgrader = websocket.Upgrader{
10481048 },
10491049}
10501050
1051- // GetScenarioRunLogs handles GET /api/v1/scenarios/run/{scenarioRunName}/logs/{clusterName} endpoint
1051+ // GetScenarioRunLogs handles GET /api/v1/scenarios/run/{scenarioRunName}/jobs/{jobId}/logs endpoint
10521052// It streams the stdout/stderr logs of a running or completed job via WebSocket
10531053func (h * Handler ) GetScenarioRunLogs (w http.ResponseWriter , r * http.Request ) {
10541054 logger := log .Log .WithName ("websocket-logs" )
@@ -1064,8 +1064,8 @@ func (h *Handler) GetScenarioRunLogs(w http.ResponseWriter, r *http.Request) {
10641064 }
10651065 defer conn .Close ()
10661066
1067- // Extract scenarioRunName and clusterName from path
1068- // Path format: /api/v1/scenarios/run/{scenarioRunName}/logs/{clusterName}
1067+ // Extract scenarioRunName and jobId from path
1068+ // Path format: /api/v1/scenarios/run/{scenarioRunName}/jobs/{jobId}/logs
10691069 path := r .URL .Path
10701070 prefix := "/api/v1/scenarios/run/"
10711071
@@ -1078,68 +1078,54 @@ func (h *Handler) GetScenarioRunLogs(w http.ResponseWriter, r *http.Request) {
10781078 // Remove prefix
10791079 remainder := path [len (prefix ):]
10801080
1081- // Split by "/logs/ "
1082- parts := strings .Split (remainder , "/logs /" )
1081+ // Split by "/jobs/" and "/logs "
1082+ parts := strings .Split (remainder , "/jobs /" )
10831083 if len (parts ) != 2 {
10841084 logger .Error (nil , "Invalid logs endpoint path format" , "path" , path )
1085- conn .WriteMessage (websocket .TextMessage , []byte ("ERROR: Invalid path format. Expected: /api/v1/scenarios/run/{scenarioRunName}/logs/{clusterName} " ))
1085+ conn .WriteMessage (websocket .TextMessage , []byte ("ERROR: Invalid path format. Expected: /api/v1/scenarios/run/{scenarioRunName}/jobs/{jobId}/logs " ))
10861086 return
10871087 }
10881088
10891089 scenarioRunName := parts [0 ]
1090- clusterName := parts [1 ]
1090+ jobIdAndLogs := parts [1 ]
10911091
1092- if scenarioRunName == "" || clusterName == "" {
1093- logger .Error (nil , "Empty scenarioRunName or clusterName in request path" , "path" , path )
1094- conn .WriteMessage (websocket .TextMessage , []byte ("ERROR: scenarioRunName and clusterName cannot be empty" ))
1092+ // Extract jobId (remove "/logs" suffix)
1093+ if ! strings .HasSuffix (jobIdAndLogs , "/logs" ) {
1094+ logger .Error (nil , "Invalid logs endpoint path format" , "path" , path )
1095+ conn .WriteMessage (websocket .TextMessage , []byte ("ERROR: Invalid path format. Expected: /api/v1/scenarios/run/{scenarioRunName}/jobs/{jobId}/logs" ))
10951096 return
10961097 }
10971098
1098- logger .Info ("WebSocket connection established" , "scenarioRunName" , scenarioRunName , "clusterName" , clusterName , "client_ip" , r .RemoteAddr )
1099-
1100- ctx := context .Background ()
1099+ jobId := strings .TrimSuffix (jobIdAndLogs , "/logs" )
11011100
1102- // Fetch the KrknScenarioRun CR
1103- var scenarioRun krknv1alpha1.KrknScenarioRun
1104- if err := h .client .Get (ctx , client.ObjectKey {
1105- Name : scenarioRunName ,
1106- Namespace : h .namespace ,
1107- }, & scenarioRun ); err != nil {
1108- logger .Error (err , "Failed to fetch scenario run" , "scenarioRunName" , scenarioRunName )
1109- conn .WriteMessage (websocket .TextMessage , []byte (fmt .Sprintf ("ERROR: Scenario run '%s' not found" , scenarioRunName )))
1101+ if scenarioRunName == "" || jobId == "" {
1102+ logger .Error (nil , "Empty scenarioRunName or jobId in request path" , "path" , path )
1103+ conn .WriteMessage (websocket .TextMessage , []byte ("ERROR: scenarioRunName and jobId cannot be empty" ))
11101104 return
11111105 }
11121106
1113- // Find the job for the requested cluster
1114- var jobId , podName string
1115- for _ , job := range scenarioRun .Status .ClusterJobs {
1116- if job .ClusterName == clusterName {
1117- jobId = job .JobId
1118- podName = job .PodName
1119- break
1120- }
1121- }
1107+ logger .Info ("WebSocket connection established" , "scenarioRunName" , scenarioRunName , "jobId" , jobId , "client_ip" , r .RemoteAddr )
11221108
1123- if jobId == "" {
1124- logger .Error (nil , "Cluster not found in scenario run" ,
1125- "scenarioRunName" , scenarioRunName ,
1126- "clusterName" , clusterName )
1127- conn .WriteMessage (websocket .TextMessage , []byte (fmt .Sprintf ("ERROR: Cluster '%s' not found in scenario run '%s'" , clusterName , scenarioRunName )))
1109+ ctx := context .Background ()
1110+
1111+ // Find pod by jobId label (no need to fetch the CR)
1112+ var podList corev1.PodList
1113+ if err := h .client .List (ctx , & podList , client .InNamespace (h .namespace ), client.MatchingLabels {
1114+ "krkn-job-id" : jobId ,
1115+ }); err != nil {
1116+ logger .Error (err , "Failed to list pods" , "jobId" , jobId )
1117+ conn .WriteMessage (websocket .TextMessage , []byte (fmt .Sprintf ("ERROR: Failed to list pods: %s" , err .Error ())))
11281118 return
11291119 }
11301120
1131- // Fetch the pod
1132- var pod corev1.Pod
1133- if err := h .client .Get (ctx , client.ObjectKey {
1134- Name : podName ,
1135- Namespace : h .namespace ,
1136- }, & pod ); err != nil {
1137- logger .Error (err , "Failed to fetch pod" , "podName" , podName )
1138- conn .WriteMessage (websocket .TextMessage , []byte (fmt .Sprintf ("ERROR: Pod '%s' not found" , podName )))
1121+ if len (podList .Items ) == 0 {
1122+ logger .Error (nil , "Job not found" , "jobId" , jobId )
1123+ conn .WriteMessage (websocket .TextMessage , []byte (fmt .Sprintf ("ERROR: Job with ID '%s' not found" , jobId )))
11391124 return
11401125 }
11411126
1142- logger .Info ("Found pod for cluster" , "scenarioRunName" , scenarioRunName , "clusterName" , clusterName , "podName" , pod .Name , "podPhase" , pod .Status .Phase )
1127+ pod := podList .Items [0 ]
1128+ logger .Info ("Found pod for job" , "scenarioRunName" , scenarioRunName , "jobId" , jobId , "podName" , pod .Name , "podPhase" , pod .Status .Phase )
11431129
11441130 // Parse query parameters
11451131 follow := r .URL .Query ().Get ("follow" ) == "true"
@@ -1163,7 +1149,7 @@ func (h *Handler) GetScenarioRunLogs(w http.ResponseWriter, r *http.Request) {
11631149
11641150 logger .Info ("Opening log stream" ,
11651151 "scenarioRunName" , scenarioRunName ,
1166- "clusterName " , clusterName ,
1152+ "jobId " , jobId ,
11671153 "podName" , pod .Name ,
11681154 "follow" , follow ,
11691155 "timestamps" , timestamps )
@@ -1174,15 +1160,15 @@ func (h *Handler) GetScenarioRunLogs(w http.ResponseWriter, r *http.Request) {
11741160 if err != nil {
11751161 logger .Error (err , "Failed to open log stream" ,
11761162 "scenarioRunName" , scenarioRunName ,
1177- "clusterName " , clusterName ,
1163+ "jobId " , jobId ,
11781164 "podName" , pod .Name ,
11791165 "namespace" , h .namespace )
11801166 conn .WriteMessage (websocket .TextMessage , []byte (fmt .Sprintf ("ERROR: Failed to open log stream: %s" , err .Error ())))
11811167 return
11821168 }
11831169 defer stream .Close ()
11841170
1185- logger .Info ("Streaming logs started" , "scenarioRunName" , scenarioRunName , "clusterName " , clusterName , "podName" , pod .Name )
1171+ logger .Info ("Streaming logs started" , "scenarioRunName" , scenarioRunName , "jobId " , jobId , "podName" , pod .Name )
11861172
11871173 // Read logs line by line and send via WebSocket
11881174 scanner := bufio .NewScanner (stream )
@@ -1193,7 +1179,7 @@ func (h *Handler) GetScenarioRunLogs(w http.ResponseWriter, r *http.Request) {
11931179 if err != nil {
11941180 logger .Error (err , "Failed to write log line to WebSocket, client likely disconnected" ,
11951181 "scenarioRunName" , scenarioRunName ,
1196- "clusterName " , clusterName ,
1182+ "jobId " , jobId ,
11971183 "podName" , pod .Name ,
11981184 "linesStreamed" , lineCount )
11991185 return
@@ -1205,7 +1191,7 @@ func (h *Handler) GetScenarioRunLogs(w http.ResponseWriter, r *http.Request) {
12051191 if err := scanner .Err (); err != nil {
12061192 logger .Error (err , "Log stream scanner error" ,
12071193 "scenarioRunName" , scenarioRunName ,
1208- "clusterName " , clusterName ,
1194+ "jobId " , jobId ,
12091195 "podName" , pod .Name ,
12101196 "linesStreamed" , lineCount )
12111197 conn .WriteMessage (websocket .TextMessage , []byte (fmt .Sprintf ("ERROR: Log stream error: %s" , err .Error ())))
@@ -1214,7 +1200,7 @@ func (h *Handler) GetScenarioRunLogs(w http.ResponseWriter, r *http.Request) {
12141200
12151201 logger .Info ("Log streaming completed" ,
12161202 "scenarioRunName" , scenarioRunName ,
1217- "clusterName " , clusterName ,
1203+ "jobId " , jobId ,
12181204 "podName" , pod .Name ,
12191205 "totalLines" , lineCount )
12201206
@@ -1383,8 +1369,8 @@ func (h *Handler) ScenariosRunRouter(w http.ResponseWriter, r *http.Request) {
13831369 }
13841370
13851371 if strings .HasPrefix (path , "/api/v1/scenarios/run/" ) {
1386- // Check for logs endpoint: /api/v1/scenarios/run/{scenarioRunName}/logs/{clusterName}
1387- if strings .Contains (path , "/logs/ " ) && r .Method == http .MethodGet {
1372+ // Check for logs endpoint: /api/v1/scenarios/run/{scenarioRunName}/jobs/{jobId}/logs
1373+ if strings .Contains (path , "/jobs/" ) && strings . HasSuffix ( path , "/logs " ) && r .Method == http .MethodGet {
13881374 h .GetScenarioRunLogs (w , r )
13891375 } else if r .Method == http .MethodGet {
13901376 h .GetScenarioRunStatus (w , r )
0 commit comments