// Copyright 2023 Mikhail Klementev. All rights reserved. // Use of this source code is governed by a AGPLv3 license // (or later) that can be found in the LICENSE file. package main import ( "errors" "fmt" "os" "os/exec" "os/user" "time" "github.com/rs/zerolog/log" ) type container struct { name string timeout time.Duration Volumes struct { LibModules string UsrSrc string Boot string } } func NewContainer(name string, timeout time.Duration) (c container, err error) { c.name = name c.timeout = timeout usr, err := user.Current() if err != nil { return } c.Volumes.LibModules = fmt.Sprintf( "%s/.out-of-tree/volumes/%s/lib/modules", usr.HomeDir, name) os.MkdirAll(c.Volumes.LibModules, 0777) c.Volumes.UsrSrc = fmt.Sprintf( "%s/.out-of-tree/volumes/%s/usr/src", usr.HomeDir, name) os.MkdirAll(c.Volumes.UsrSrc, 0777) c.Volumes.Boot = fmt.Sprintf( "%s/.out-of-tree/volumes/%s/boot", usr.HomeDir, name) os.MkdirAll(c.Volumes.Boot, 0777) return } func (c container) Build(imagePath string) (output string, err error) { args := []string{"build"} args = append(args, "-t", c.name, imagePath) cmd := exec.Command("docker", args...) log.Debug().Msgf("%v", cmd) rawOutput, err := cmd.CombinedOutput() output = string(rawOutput) return } func (c container) Run(workdir string, command string) (output string, err error) { cmd := exec.Command("docker", "run", "--rm", "-v", workdir+":/work", "-v", c.Volumes.LibModules+":/lib/modules", "-v", c.Volumes.UsrSrc+":/usr/src", "-v", c.Volumes.Boot+":/boot", c.name, "bash", "-c", "cd /work && "+command) log.Debug().Msgf("%v", cmd) timer := time.AfterFunc(c.timeout, func() { log.Info().Str("container", c.name). Str("workdir", workdir). Str("command", command). Msg("killing container by timeout") cmd.Process.Kill() }) defer timer.Stop() raw, err := cmd.CombinedOutput() if err != nil { e := fmt.Sprintf("error `%v` for cmd `%v` with output `%v`", err, command, string(raw)) err = errors.New(e) return } output = string(raw) return }