@@ -19,6 +19,7 @@ import (
1919
2020const (
2121 buildPath = "/api/v1/build"
22+ getBuildPath = "/api/v1/builds/{job_id}" // SYNC analog of download API with timeout
2223 jobStatusPath = "/api/v1/status/{job_id}"
2324 downloadPath = "/api/v1/download/{job_id}"
2425 helloPath = "/api/v1/hello"
@@ -134,6 +135,7 @@ func (s *BuildServer) registerRoutes() {
134135 s .router .HandleFunc (buildPath , s .buildModuleHandler ()).Methods ("POST" )
135136 s .router .HandleFunc (jobStatusPath , s .getStatusHandler ()).Methods ("GET" )
136137 s .router .HandleFunc (downloadPath , s .downloadModule ()).Methods ("GET" )
138+ s .router .HandleFunc (getBuildPath , s .helloHandler ()).Methods ("GET" )
137139 s .router .HandleFunc (helloPath , s .helloHandler ()).Methods ("GET" )
138140}
139141
@@ -144,9 +146,9 @@ func (s *BuildServer) replyErrorFormated(code int, remoteAddr string, w http.Res
144146 s .logger .Error ("HTTP error" , "remote_addr" , remoteAddr , "code" , code , "error" , msg )
145147}
146148
147- func (s * BuildServer ) replyError (code int , remoteAddr string , w http.ResponseWriter , error error ) {
149+ func (s * BuildServer ) replyError (code int , remoteAddr string , w http.ResponseWriter , errorOccurred error ) {
148150 w .WriteHeader (code )
149- s .logger .Error ("HTTP error" , "remote_addr" , remoteAddr , "code" , code , "error" , error )
151+ s .logger .Error ("HTTP error" , "remote_addr" , remoteAddr , "code" , code , "error" , errorOccurred )
150152}
151153
152154func (s * BuildServer ) helloHandler () http.HandlerFunc {
@@ -354,3 +356,86 @@ func (s *BuildServer) downloadModule() http.HandlerFunc {
354356 s .logger .Debug ("Module file sent successfully" , "remote_addr" , remoteAddr )
355357 }
356358}
359+
360+ func (s * BuildServer ) getBuiLdModule () http.HandlerFunc {
361+ return func (w http.ResponseWriter , r * http.Request ) {
362+ remoteAddr := r .RemoteAddr
363+ vars := mux .Vars (r )
364+ jobID := vars ["job_id" ]
365+ s .logger .Debug ("Download request for job" , "remote_addr" , remoteAddr , "job_id" , jobID )
366+
367+ job := s .buildService .GetJob (jobID )
368+
369+ switch job .Status {
370+ case model .StatusNotExist :
371+ s .logger .Debug ("Job not found" , "remote_addr" , remoteAddr , "job_id" , jobID )
372+ s .replyErrorFormated (http .StatusNotFound , remoteAddr , w , "Job not found: %s" , jobID )
373+ return
374+ case model .StatusFailed :
375+ s .logger .Debug ("Job is failed " , "remote_addr" , remoteAddr , "job_id" , jobID )
376+ s .replyErrorFormated (http .StatusInternalServerError , remoteAddr , w , "Job is failed: %s" , jobID )
377+ return
378+ case model .StatusPending :
379+ s .logger .Debug ("Job is pending " , "remote_addr" , remoteAddr , "job_id" , jobID )
380+ s .replyErrorFormated (http .StatusAccepted , remoteAddr , w , "Job is pending: %s" , jobID )
381+ return
382+ }
383+
384+ // from this line wait until s.buildService.GetJob(jobID).Status == model.StatusCompleted with check step 5 sec and timeout 5 min
385+ const (
386+ pollInterval = 5 * time .Second
387+ waitTimeout = 5 * time .Minute
388+ )
389+
390+ deadline := time .Now ().Add (waitTimeout )
391+ for job .Status != model .StatusCompleted {
392+ // Check for client cancellation
393+ select {
394+ case <- r .Context ().Done ():
395+ s .logger .Debug ("Client cancelled download request while waiting for job completion" , "remote_addr" , remoteAddr , "job_id" , jobID )
396+ return
397+ case <- time .After (pollInterval ):
398+ }
399+
400+ if time .Now ().After (deadline ) {
401+ s .logger .Debug ("Timed out waiting for job completion" , "remote_addr" , remoteAddr , "job_id" , jobID )
402+ s .replyErrorFormated (http .StatusGatewayTimeout , remoteAddr , w , "Timed out waiting for job completion: %s" , jobID )
403+ return
404+ }
405+
406+ job = s .buildService .GetJob (jobID )
407+
408+ if job .Status == model .StatusFailed {
409+ s .logger .Debug ("Job failed while waiting" , "remote_addr" , remoteAddr , "job_id" , jobID )
410+ s .replyErrorFormated (http .StatusInternalServerError , remoteAddr , w , "Job failed: %s" , jobID )
411+ return
412+ }
413+ }
414+
415+ s .logger .Debug ("Job status" , "remote_addr" , remoteAddr , "status" , job .Status , "cache_path" , job .CachePath )
416+ if job .Status != model .StatusCompleted {
417+ s .logger .Debug ("Job not completed" , "remote_addr" , remoteAddr , "status" , job .Status )
418+ s .replyErrorFormated (http .StatusBadRequest , remoteAddr , w , "Job is not completed yet. Status: %s" , job .Status )
419+ return
420+ }
421+
422+ if job .CachePath == "" {
423+ s .logger .Debug ("Cache path not set for completed job" , "remote_addr" , remoteAddr )
424+ s .replyErrorFormated (http .StatusInternalServerError , remoteAddr , w , "Cache path not set for completed job" )
425+ return
426+ }
427+
428+ cacheInfo , err := os .Stat (job .CachePath )
429+ if os .IsNotExist (err ) {
430+ s .logger .Debug ("Cache file not found" , "remote_addr" , remoteAddr , "cache_path" , job .CachePath )
431+ s .replyErrorFormated (http .StatusNotFound , remoteAddr , w , "Cache file not found: %s" , job .CachePath )
432+ return
433+ }
434+
435+ s .logger .Info ("Serving module file" , "remote_addr" , remoteAddr , "cache_path" , job .CachePath , "size_bytes" , cacheInfo .Size ())
436+ w .Header ().Set ("Content-Type" , "application/gzip" )
437+ w .Header ().Set ("Content-Disposition" , fmt .Sprintf ("attachment; filename=\" drbd-modules-%s.tar.gz\" " , job .KernelVersion ))
438+ http .ServeFile (w , r , job .CachePath )
439+ s .logger .Debug ("Module file sent successfully" , "remote_addr" , remoteAddr )
440+ }
441+ }
0 commit comments