feat(backend): add in-memory collect job manager and mock executor
This commit is contained in:
@@ -1,6 +1,7 @@
|
||||
package server
|
||||
|
||||
import (
|
||||
"context"
|
||||
"crypto/rand"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
@@ -572,28 +573,12 @@ func (s *Server) handleCollectStart(w http.ResponseWriter, r *http.Request) {
|
||||
return
|
||||
}
|
||||
|
||||
jobID := generateJobID()
|
||||
now := time.Now().UTC()
|
||||
progress := 0
|
||||
|
||||
s.collectMu.Lock()
|
||||
s.collectJobs[jobID] = &CollectJobStatusResponse{
|
||||
JobID: jobID,
|
||||
Status: "queued",
|
||||
Progress: &progress,
|
||||
Logs: []string{"Job queued"},
|
||||
UpdatedAt: now,
|
||||
}
|
||||
s.collectMu.Unlock()
|
||||
job := s.jobManager.CreateJob(req)
|
||||
s.startMockCollectionJob(job.ID, req)
|
||||
|
||||
w.Header().Set("Content-Type", "application/json")
|
||||
w.WriteHeader(http.StatusAccepted)
|
||||
json.NewEncoder(w).Encode(CollectJobResponse{
|
||||
JobID: jobID,
|
||||
Status: "queued",
|
||||
Message: "Collection job accepted",
|
||||
CreatedAt: now,
|
||||
})
|
||||
_ = json.NewEncoder(w).Encode(job.toJobResponse("Collection job accepted"))
|
||||
}
|
||||
|
||||
func (s *Server) handleCollectStatus(w http.ResponseWriter, r *http.Request) {
|
||||
@@ -603,17 +588,13 @@ func (s *Server) handleCollectStatus(w http.ResponseWriter, r *http.Request) {
|
||||
return
|
||||
}
|
||||
|
||||
s.collectMu.RLock()
|
||||
job, ok := s.collectJobs[jobID]
|
||||
if !ok || job == nil {
|
||||
s.collectMu.RUnlock()
|
||||
job, ok := s.jobManager.GetJob(jobID)
|
||||
if !ok {
|
||||
jsonError(w, "Collect job not found", http.StatusNotFound)
|
||||
return
|
||||
}
|
||||
resp := *job
|
||||
s.collectMu.RUnlock()
|
||||
|
||||
jsonResponse(w, resp)
|
||||
jsonResponse(w, job.toStatusResponse())
|
||||
}
|
||||
|
||||
func (s *Server) handleCollectCancel(w http.ResponseWriter, r *http.Request) {
|
||||
@@ -623,25 +604,76 @@ func (s *Server) handleCollectCancel(w http.ResponseWriter, r *http.Request) {
|
||||
return
|
||||
}
|
||||
|
||||
s.collectMu.Lock()
|
||||
job, ok := s.collectJobs[jobID]
|
||||
if !ok || job == nil {
|
||||
s.collectMu.Unlock()
|
||||
job, ok := s.jobManager.CancelJob(jobID)
|
||||
if !ok {
|
||||
jsonError(w, "Collect job not found", http.StatusNotFound)
|
||||
return
|
||||
}
|
||||
|
||||
now := time.Now().UTC()
|
||||
progress := 0
|
||||
job.Status = "canceled"
|
||||
job.Progress = &progress
|
||||
job.Logs = append(job.Logs, "Job canceled by user")
|
||||
job.Error = ""
|
||||
job.UpdatedAt = now
|
||||
resp := *job
|
||||
s.collectMu.Unlock()
|
||||
jsonResponse(w, job.toStatusResponse())
|
||||
}
|
||||
|
||||
jsonResponse(w, resp)
|
||||
func (s *Server) startMockCollectionJob(jobID string, req CollectRequest) {
|
||||
ctx, cancel := context.WithCancel(context.Background())
|
||||
if attached := s.jobManager.AttachJobCancel(jobID, cancel); !attached {
|
||||
cancel()
|
||||
return
|
||||
}
|
||||
|
||||
go func() {
|
||||
steps := []struct {
|
||||
delay time.Duration
|
||||
status string
|
||||
progress int
|
||||
log string
|
||||
}{
|
||||
{delay: 250 * time.Millisecond, status: CollectStatusRunning, progress: 20, log: "Подключение..."},
|
||||
{delay: 250 * time.Millisecond, status: CollectStatusRunning, progress: 50, log: "Сбор инвентаря..."},
|
||||
{delay: 250 * time.Millisecond, status: CollectStatusRunning, progress: 80, log: "Нормализация..."},
|
||||
}
|
||||
|
||||
for _, step := range steps {
|
||||
if !waitWithCancel(ctx, step.delay) {
|
||||
return
|
||||
}
|
||||
|
||||
if job, ok := s.jobManager.GetJob(jobID); !ok || isTerminalCollectStatus(job.Status) {
|
||||
return
|
||||
}
|
||||
|
||||
s.jobManager.UpdateJobStatus(jobID, step.status, step.progress, "")
|
||||
s.jobManager.AppendJobLog(jobID, step.log)
|
||||
}
|
||||
|
||||
if !waitWithCancel(ctx, 250*time.Millisecond) {
|
||||
return
|
||||
}
|
||||
|
||||
if job, ok := s.jobManager.GetJob(jobID); !ok || isTerminalCollectStatus(job.Status) {
|
||||
return
|
||||
}
|
||||
|
||||
if strings.Contains(strings.ToLower(req.Host), "fail") {
|
||||
s.jobManager.UpdateJobStatus(jobID, CollectStatusFailed, 100, "Mock: не удалось завершить сбор")
|
||||
s.jobManager.AppendJobLog(jobID, "Сбор завершен с ошибкой")
|
||||
return
|
||||
}
|
||||
|
||||
s.jobManager.UpdateJobStatus(jobID, CollectStatusSuccess, 100, "")
|
||||
s.jobManager.AppendJobLog(jobID, "Сбор завершен")
|
||||
}()
|
||||
}
|
||||
|
||||
func waitWithCancel(ctx context.Context, d time.Duration) bool {
|
||||
timer := time.NewTimer(d)
|
||||
defer timer.Stop()
|
||||
|
||||
select {
|
||||
case <-ctx.Done():
|
||||
return false
|
||||
case <-timer.C:
|
||||
return true
|
||||
}
|
||||
}
|
||||
|
||||
func validateCollectRequest(req CollectRequest) error {
|
||||
|
||||
Reference in New Issue
Block a user