216 lines
4.3 KiB
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
|
|
|
|
}
|