jobwatch/app/state/state.go

216 lines
4.3 KiB
Go

package state
import (
"bufio"
"encoding/json"
"fmt"
"io/ioutil"
"jobwatch/types"
"log"
"os"
"path/filepath"
"strings"
"time"
)
/*
*/
type State struct {
JobId string `json:"job_id"`
JobInstanceId string `json:"job_inst_id"`
LastRun string `json:"last_run"`
LastRunAge int `json:"last_run_age_seconds"`
LastRunAgeState int `json:"last_run_age_state"`
LastRunWarnAt types.TimeSpec `json:"last_run_warn_at"`
LastRunCritAt types.TimeSpec `json:"last_run_crit_at"`
LastExitCode int `json:"last_exit_code"`
LastState int `json:"last_state"`
}
type summaryEntry struct {
State State `json:"state"`
LogEntries []string `json:"log_entries"`
}
type summaryPerUser struct {
UserId string `json:"userd_id"`
Path string `json:"-"`
Entries []summaryEntry `json:"entries"`
}
type summary struct {
Entries []summaryPerUser `json:"all"`
}
func getStateDir() (string, error) {
var dir string
dir = "/var/lib/jobwatch/states"
if os.Getuid() > 0 {
if od, err := os.UserHomeDir(); err == nil {
dir = od + "/.jobwatch/states"
}
}
err := os.MkdirAll(dir, 0770)
return dir, err
}
func findAllStateDirs() []summaryPerUser {
var dirs []summaryPerUser
var res []summaryPerUser
var su summaryPerUser
su.UserId = "root"
su.Path = "/var/lib/jobwatch/states"
dirs = append(dirs, su)
f, err := os.Open("/etc/passwd")
if err == nil {
defer f.Close()
rdr := bufio.NewReader(f)
for {
line, err := rdr.ReadString(10)
parts := strings.Split(line, ":")
if len(parts) > 6 {
hd := parts[5]
var su summaryPerUser
su.UserId = parts[0]
su.Path = filepath.FromSlash(hd + "/.jobwatch/states")
dirs = append(dirs, su)
}
if err != nil {
break
}
}
for _, d := range dirs {
if _, err := os.Stat(d.Path); err == nil {
res = append(res, d)
}
}
}
return res
}
func LoadState(path string) (*State, error) {
data, err := os.ReadFile(path)
if err != nil {
return nil, err
}
var res State
err = json.Unmarshal(data, &res)
if err != nil {
return nil, fmt.Errorf("Error unmashaling state: %v", err)
}
return &res, nil
}
func WriteLog(job types.Job, line ...string) {
fmt.Println("B")
}
func (state *State) ReEval() error {
lr, err := time.Parse(time.RFC3339, state.LastRun)
if err != nil {
return err
}
age := time.Now().Sub(lr)
state.LastRunAge = int(age.Seconds())
if age.Seconds() > float64(state.LastRunCritAt.AsSeconds()) {
state.LastRunAgeState = int(types.CRIT)
} else if age.Seconds() > float64(state.LastRunWarnAt.AsSeconds()) {
state.LastRunAgeState = int(types.WARN)
} else {
state.LastRunAgeState = int(types.OK)
}
return nil
}
func StateFromJob(job types.Job) State {
var res State
res.JobId = job.Id
res.JobInstanceId = job.InstId
res.LastRunCritAt = job.LastRunCrit
res.LastRunWarnAt = job.LastRunWarn
return res
}
func (st State) SaveState() error {
dir, err := getStateDir()
if err != nil {
return err
}
data, err := json.MarshalIndent(st, " ", " ")
if err != nil {
return fmt.Errorf("Error mashaling state: %v", err)
}
if st.JobInstanceId == "" {
dir = filepath.FromSlash(fmt.Sprintf("%v/%v.state.json", dir, st.JobId))
} else {
dir = filepath.FromSlash(fmt.Sprintf("%v/%v_%v.state.json", dir, st.JobId, st.JobInstanceId))
}
if err := os.WriteFile(dir, data, 0600); err != nil {
return fmt.Errorf("Error writing state file %v: %v", dir, err)
}
return nil
}
func ShowAll() error {
dirs := findAllStateDirs()
for i, d := range dirs {
fsi, err := ioutil.ReadDir(d.Path)
log.Printf("Scanning path %v", d.Path)
if err != nil {
continue
}
for _, e := range fsi {
p := filepath.FromSlash(d.Path + "/" + e.Name())
log.Printf("Processing state file %v", p)
st, err := LoadState(p)
if err == nil {
var se summaryEntry
se.State = *st
if err := se.State.ReEval(); err != nil {
log.Printf("Ignoring error while reeval: %v", err)
}
d.Entries = append(d.Entries, se)
dirs[i] = d
} else {
log.Printf("Ignoring error processing state file %v: %v", p, err)
}
}
}
if len(dirs) > 0 {
bytes, err := json.MarshalIndent(dirs, " ", " ")
if err != nil {
return err
}
fmt.Printf("%v", string(bytes))
}
return nil
}