@@ -1015,13 +1015,17 @@ func (h *Handler) GetScenarioRunStatus(w http.ResponseWriter, r *http.Request) {
10151015 clusterJobs := make ([]ClusterJobStatusResponse , len (scenarioRun .Status .ClusterJobs ))
10161016 for i , job := range scenarioRun .Status .ClusterJobs {
10171017 clusterJobs [i ] = ClusterJobStatusResponse {
1018- ClusterName : job .ClusterName ,
1019- JobId : job .JobId ,
1020- PodName : job .PodName ,
1021- Phase : job .Phase ,
1022- Message : job .Message ,
1023- StartTime : convertMetaTime (job .StartTime ),
1024- CompletionTime : convertMetaTime (job .CompletionTime ),
1018+ ClusterName : job .ClusterName ,
1019+ JobId : job .JobId ,
1020+ PodName : job .PodName ,
1021+ Phase : job .Phase ,
1022+ Message : job .Message ,
1023+ StartTime : convertMetaTime (job .StartTime ),
1024+ CompletionTime : convertMetaTime (job .CompletionTime ),
1025+ RetryCount : job .RetryCount ,
1026+ MaxRetries : job .MaxRetries ,
1027+ CancelRequested : job .CancelRequested ,
1028+ FailureReason : job .FailureReason ,
10251029 }
10261030 }
10271031
@@ -1490,6 +1494,73 @@ func (h *Handler) DeleteSingleJob(w http.ResponseWriter, r *http.Request) {
14901494 w .WriteHeader (http .StatusNoContent )
14911495}
14921496
1497+ // GetSingleJob handles GET /api/v1/scenarios/run/jobs/{jobId}
1498+ // It returns the status of a single job by jobId (jobId is unique across all scenario runs)
1499+ func (h * Handler ) GetSingleJob (w http.ResponseWriter , r * http.Request ) {
1500+ // Parse path: /api/v1/scenarios/run/jobs/{jobId}
1501+ jobId , err := extractPathSuffix (r .URL .Path , "/api/v1/scenarios/run/jobs/" )
1502+ if err != nil {
1503+ writeJSONError (w , http .StatusBadRequest , ErrorResponse {
1504+ Error : "bad_request" ,
1505+ Message : "jobId " + err .Error (),
1506+ })
1507+ return
1508+ }
1509+
1510+ ctx := context .Background ()
1511+
1512+ // Find KrknScenarioRun containing this jobId
1513+ var scenarioRunList krknv1alpha1.KrknScenarioRunList
1514+ if err := h .client .List (ctx , & scenarioRunList , client .InNamespace (h .namespace )); err != nil {
1515+ writeJSONError (w , http .StatusInternalServerError , ErrorResponse {
1516+ Error : "internal_error" ,
1517+ Message : "Failed to list scenario runs: " + err .Error (),
1518+ })
1519+ return
1520+ }
1521+
1522+ // Search for job across all scenario runs
1523+ var foundJob * krknv1alpha1.ClusterJobStatus
1524+
1525+ for i := range scenarioRunList .Items {
1526+ sr := & scenarioRunList .Items [i ]
1527+ for j := range sr .Status .ClusterJobs {
1528+ if sr .Status .ClusterJobs [j ].JobId == jobId {
1529+ foundJob = & sr .Status .ClusterJobs [j ]
1530+ break
1531+ }
1532+ }
1533+ if foundJob != nil {
1534+ break
1535+ }
1536+ }
1537+
1538+ if foundJob == nil {
1539+ writeJSONError (w , http .StatusNotFound , ErrorResponse {
1540+ Error : "not_found" ,
1541+ Message : "Job '" + jobId + "' not found" ,
1542+ })
1543+ return
1544+ }
1545+
1546+ // Convert to response type
1547+ response := ClusterJobStatusResponse {
1548+ ClusterName : foundJob .ClusterName ,
1549+ JobId : foundJob .JobId ,
1550+ PodName : foundJob .PodName ,
1551+ Phase : foundJob .Phase ,
1552+ Message : foundJob .Message ,
1553+ StartTime : convertMetaTime (foundJob .StartTime ),
1554+ CompletionTime : convertMetaTime (foundJob .CompletionTime ),
1555+ RetryCount : foundJob .RetryCount ,
1556+ MaxRetries : foundJob .MaxRetries ,
1557+ CancelRequested : foundJob .CancelRequested ,
1558+ FailureReason : foundJob .FailureReason ,
1559+ }
1560+
1561+ writeJSON (w , http .StatusOK , response )
1562+ }
1563+
14931564func (h * Handler ) ScenariosRunRouter (w http.ResponseWriter , r * http.Request ) {
14941565 path := r .URL .Path
14951566
@@ -1514,9 +1585,11 @@ func (h *Handler) ScenariosRunRouter(w http.ResponseWriter, r *http.Request) {
15141585 return
15151586 }
15161587
1517- // Check for /jobs/{jobId} pattern (DELETE single job)
1588+ // Check for /jobs/{jobId} pattern (GET or DELETE single job)
15181589 if strings .HasPrefix (path , "/api/v1/scenarios/run/jobs/" ) {
15191590 switch r .Method {
1591+ case http .MethodGet :
1592+ h .GetSingleJob (w , r )
15201593 case http .MethodDelete :
15211594 h .DeleteSingleJob (w , r )
15221595 default :
0 commit comments