gomanager/runner/runner.go

170 lines
4.0 KiB
Go

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
}