package runner import ( "code.icod.de/dalu/gomanager/ent" "context" "fmt" "io" "os" "os/exec" "strings" ) type Runner struct { client *ent.Client } func NewRunner(client *ent.Client) *Runner { r := new(Runner) r.client = client return r } // Run checks all projects sequentially func (r *Runner) Run() error { ms, e := r.client.Project.Query().All(context.Background()) if e != nil { return fmt.Errorf("error querying projects: %s", e) } if len(ms) == 0 { return nil } v, e := getCurrentGoVersion() if e != nil { return fmt.Errorf("error getting current go version: %s", e) } for _, m := range ms { fv, e := getVersionOfFile(fmt.Sprintf("%s/%s", m.RootPath, m.BinaryPath)) if e != nil { return fmt.Errorf("error getting version of file: %s", e) } if v != fv { l, e := goBuildBinary(m.RootPath) if e != nil { return fmt.Errorf("error in goBuildBinary: %s", e) } _, e = r.client.Logentry. Create(). SetContent(l). Save(context.Background()) if e != nil { return fmt.Errorf("error creating go build log entry: %s", e) } if m.MoveToTarget { if fl, e := copyFile( fmt.Sprintf("%s/%s", m.RootPath, m.BinaryPath), fmt.Sprintf("%s/%s", m.RootPath, m.BinaryTargetPath), ); e != nil { return fmt.Errorf("error copying file to target: %s", e) } else { _, e = r.client.Logentry. Create(). SetContent(fl). Save(context.Background()) if e != nil { return fmt.Errorf("error creating move to target log entry: %s", e) } } } else { if e := chownBinary(m.User, m.Group, fmt.Sprintf("%s/%s", m.RootPath, m.BinaryPath)); e != nil { return fmt.Errorf("error chowning binary: %s", e) } } if l, e := restartSystemdService(m.ServiceName); e != nil { return fmt.Errorf("error restarting systemd service %s: %s", m.ServiceName, e) } else { _, e = r.client.Logentry. Create(). SetContent(l). Save(context.Background()) if e != nil { return fmt.Errorf("error creating service restart log entry: %s", e) } } } } return nil } // getCurrentGoVersion returns the version of the installed go package or a non-nil error func getCurrentGoVersion() (string, error) { cmd := exec.Command("go", "version") var out strings.Builder cmd.Stdout = &out if e := cmd.Run(); e != nil { return "", e } split := strings.Split(out.String(), " ") version := split[2] return version, nil } // getVersionOfFile returns the version of a go binary or a non-nil error func getVersionOfFile(file string) (string, error) { cmd := exec.Command("go", "version", file) var out strings.Builder cmd.Stdout = &out if e := cmd.Run(); e != nil { return "", e } split := strings.Split(out.String(), " ") version := split[1] return version, nil } // goBuildBinary does a go build in the root directory, then "cd"s back to the previous working directory. func goBuildBinary(root string) (string, error) { cwd, e := os.Getwd() if e != nil { return "", e } if e := os.Chdir(root); e != nil { return "", e } cmd := exec.Command("go", "build") var out strings.Builder cmd.Stdout = &out if e := cmd.Run(); e != nil { return "", e } if e := os.Chdir(cwd); e != nil { return "", e } return out.String(), nil } func chownBinary(user, group, file string) error { cmd := exec.Command("chown", fmt.Sprintf("%s:%s", user, group), file) return cmd.Run() } func copyFile(src, dst string) (string, error) { srcF, e := os.Open(src) if e != nil { return "", e } defer srcF.Close() dstF, e := os.Open(dst) if e != nil { return "", e } defer dstF.Close() if size, e := io.Copy(dstF, srcF); e != nil { return "", e } else { return fmt.Sprintf("copied %d bytes from %s to %s", size, src, dst), nil } } // restartSystemdService restarts the named systemd service via systemctl restart shell command func restartSystemdService(name string) (string, error) { cmd := exec.Command("systemctl", "restart", name) var out strings.Builder cmd.Stdout = &out if e := cmd.Run(); e != nil { return "", e } return out.String(), nil }