package daemon import ( "database/sql" "errors" "fmt" "os" "os/exec" "path/filepath" "time" "github.com/rs/zerolog" "github.com/rs/zerolog/log" "code.dumpstack.io/tools/out-of-tree/api" "code.dumpstack.io/tools/out-of-tree/artifact" "code.dumpstack.io/tools/out-of-tree/config/dotfiles" "code.dumpstack.io/tools/out-of-tree/daemon/db" "code.dumpstack.io/tools/out-of-tree/distro" "code.dumpstack.io/tools/out-of-tree/qemu" ) type jobProcessor struct { job api.Job log zerolog.Logger db *sql.DB } func newJobProcessor(job api.Job, db *sql.DB) (pj jobProcessor) { pj.job = job pj.db = db pj.log = log.With(). Str("uuid", job.UUID). Str("group", job.Group). Logger() return } func (pj jobProcessor) Update() (err error) { err = db.UpdateJob(pj.db, &pj.job) if err != nil { pj.log.Error().Err(err).Msgf("update job %v", pj.job) } return } func (pj jobProcessor) SetStatus(status api.Status) (err error) { pj.log.Info().Msgf(`%v -> %v`, pj.job.Status, status) pj.job.Status = status err = pj.Update() return } func (pj *jobProcessor) Process(res *Resources) (err error) { if pj.job.Status != api.StatusWaiting { err = errors.New("job is not available to process") return } if pj.job.Artifact.Qemu.Cpus == 0 { pj.job.Artifact.Qemu.Cpus = qemu.DefaultCPUs } if pj.job.Artifact.Qemu.Memory == 0 { pj.job.Artifact.Qemu.Memory = qemu.DefaultMemory } err = res.Allocate(pj.job) if err != nil { return } defer func() { res.Release(pj.job) }() log.Info().Msgf("process job %v", pj.job.UUID) pj.SetStatus(api.StatusRunning) pj.job.Started = time.Now() defer func() { pj.job.Finished = time.Now() if err != nil { pj.SetStatus(api.StatusFailure) } else { pj.SetStatus(api.StatusSuccess) } }() var tmp string tmp, err = os.MkdirTemp(dotfiles.Dir("tmp"), "") if err != nil { pj.log.Error().Err(err).Msg("mktemp") return } defer os.RemoveAll(tmp) tmprepo := filepath.Join(tmp, "repo") pj.log.Debug().Msgf("temp repo: %v", tmprepo) remote := fmt.Sprintf("git://localhost:9418/%s", pj.job.RepoName) pj.log.Debug().Msgf("remote: %v", remote) var raw []byte cmd := exec.Command("git", "clone", remote, tmprepo) raw, err = cmd.CombinedOutput() pj.log.Trace().Msgf("%v\n%v", cmd, string(raw)) if err != nil { pj.log.Error().Msgf("%v\n%v", cmd, string(raw)) return } cmd = exec.Command("git", "checkout", pj.job.Commit) cmd.Dir = tmprepo raw, err = cmd.CombinedOutput() pj.log.Trace().Msgf("%v\n%v", cmd, string(raw)) if err != nil { pj.log.Error().Msgf("%v\n%v", cmd, string(raw)) return } pj.job.Artifact.SourcePath = tmprepo var result *artifact.Result var dq *qemu.System pj.job.Artifact.Process(pj.log, pj.job.Target, false, "", "", 0, func(q *qemu.System, ka artifact.Artifact, ki distro.KernelInfo, res *artifact.Result) { result = res dq = q }, ) logdir := dotfiles.Dir("daemon/logs", pj.job.UUID) err = os.WriteFile(filepath.Join(logdir, "build.log"), []byte(result.Build.Output), 0644) if err != nil { pj.log.Error().Err(err).Msg("") } err = os.WriteFile(filepath.Join(logdir, "run.log"), []byte(result.Run.Output), 0644) if err != nil { pj.log.Error().Err(err).Msg("") } err = os.WriteFile(filepath.Join(logdir, "test.log"), []byte(result.Test.Output), 0644) if err != nil { pj.log.Error().Err(err).Msg("") } err = os.WriteFile(filepath.Join(logdir, "qemu.log"), []byte(dq.Stdout), 0644) if err != nil { pj.log.Error().Err(err).Msg("") } pj.log.Info().Msgf("build %v, run %v, test %v", result.Build.Ok, result.Run.Ok, result.Test.Ok) if !result.Test.Ok { err = errors.New("tests failed") } return }