revamp
This commit is contained in:
320
config.go
320
config.go
@@ -1,227 +1,199 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"encoding/json"
|
||||
"errors"
|
||||
"fmt"
|
||||
"io/ioutil"
|
||||
"log"
|
||||
"regexp"
|
||||
"sync"
|
||||
"time"
|
||||
|
||||
"golang.org/x/crypto/ssh"
|
||||
log "github.com/sirupsen/logrus"
|
||||
)
|
||||
|
||||
type Config struct {
|
||||
Zfsnap map[string]string `json:"zfsnap"`
|
||||
Box map[string]*Box `json:"box"`
|
||||
Email EmailConfig `json:"email"`
|
||||
Apps []AppConfig `json:"apps"`
|
||||
Timezone string `json:"timezone"`
|
||||
Now time.Time `json:"-"`
|
||||
ScheduleDuration map[string]string `json:"schedule"`
|
||||
Box map[string]BoxConfig `json:"box"`
|
||||
Email EmailConfig `json:"email"`
|
||||
Apps []AppConfig `json:"apps"`
|
||||
Timezone string `json:"timezone"`
|
||||
Debug bool `json:"debug"`
|
||||
Admin AdminConfig `json:"admin"`
|
||||
box map[string]*Box `json:"-"`
|
||||
apps map[string]*App `json:"-"`
|
||||
timezone *time.Location `json:"-"`
|
||||
}
|
||||
|
||||
//Load config from file
|
||||
func (c *Config) Load() error {
|
||||
if *debugFlag {
|
||||
log.Printf("SSHConfig.Load : Start")
|
||||
}
|
||||
b, err := ioutil.ReadFile(*cfgFile)
|
||||
type AdminConfig struct {
|
||||
Addr string `json:"addr"`
|
||||
Username string `json:"username"`
|
||||
Password string `json:"password"`
|
||||
}
|
||||
|
||||
type BoxConfig struct {
|
||||
Addr string `json:"addr"`
|
||||
User string `json:"user"`
|
||||
Key string `json:"key"`
|
||||
}
|
||||
|
||||
type AppConfig struct {
|
||||
Name string `json:"name"`
|
||||
Schedule []string `json:"schedule"`
|
||||
Sources []string `json:"src"`
|
||||
Destinations []string `json:"dest"`
|
||||
Before map[string]string `json:"before"`
|
||||
After map[string]string `json:"after"`
|
||||
Active bool `json:"active"`
|
||||
}
|
||||
|
||||
// Load config from file
|
||||
func (c *Config) LoadFile(path string) error {
|
||||
log.WithFields(log.Fields{"path": path}).Debugf("starting")
|
||||
defer log.WithFields(log.Fields{"path": path}).Debugf("done")
|
||||
|
||||
b, err := ioutil.ReadFile(path)
|
||||
if err != nil {
|
||||
if *debugFlag {
|
||||
log.Printf("Config.Load : ioutil.ReadFile(%s) : %s", *cfgFile, err)
|
||||
}
|
||||
log.WithFields(log.Fields{"path": path, "error": err, "call": "ioutil.ReadFile"}).Errorf("")
|
||||
return err
|
||||
}
|
||||
|
||||
err = json.Unmarshal(b, &c)
|
||||
if err != nil {
|
||||
if *debugFlag {
|
||||
log.Printf("Config.Load : json.Unmarshal : %s", err)
|
||||
}
|
||||
log.WithFields(log.Fields{"path": path, "error": err, "call": "json.Unmarshal"}).Errorf("")
|
||||
return err
|
||||
}
|
||||
|
||||
if *debugFlag {
|
||||
log.Printf("Config.Load :\r\n%v", cfg)
|
||||
}
|
||||
|
||||
l, err := time.LoadLocation(cfg.Timezone)
|
||||
c.timezone, err = time.LoadLocation(cfg.Timezone)
|
||||
if err != nil {
|
||||
if *debugFlag {
|
||||
log.Printf("Config.Load : time.LoadLocation : %s", err)
|
||||
}
|
||||
log.WithFields(log.Fields{"path": path, "error": err, "call": "time.LoadLocation", "attr": cfg.Timezone}).Errorf("")
|
||||
return err
|
||||
}
|
||||
|
||||
if len(cfg.Email.SmtpHost) == 0 {
|
||||
if *debugFlag {
|
||||
log.Printf("Config.Load : no smtp")
|
||||
}
|
||||
return fmt.Errorf("no smtp")
|
||||
err := fmt.Errorf("no smtp")
|
||||
log.WithFields(log.Fields{"path": path, "error": err}).Errorf("")
|
||||
return err
|
||||
}
|
||||
|
||||
if len(cfg.Email.FromEmail) == 0 {
|
||||
if *debugFlag {
|
||||
log.Printf("Config.Load : no email from")
|
||||
}
|
||||
return fmt.Errorf("no email from")
|
||||
err := fmt.Errorf("no email from")
|
||||
log.WithFields(log.Fields{"path": path, "error": err}).Errorf("")
|
||||
return err
|
||||
}
|
||||
|
||||
if len(cfg.Email.ToEmail) == 0 {
|
||||
if *debugFlag {
|
||||
log.Printf("Config.Load : no email to")
|
||||
}
|
||||
return fmt.Errorf("no email to")
|
||||
err := fmt.Errorf("no email to")
|
||||
log.WithFields(log.Fields{"path": path, "error": err}).Errorf("")
|
||||
return err
|
||||
}
|
||||
|
||||
c.Now = time.Now().In(l)
|
||||
for k, v := range c.ScheduleDuration {
|
||||
switch k {
|
||||
case "hourly":
|
||||
case "daily":
|
||||
case "weekly":
|
||||
case "monthly":
|
||||
case "yearly":
|
||||
if _, err := Expiration(time.Now(), v); err != nil {
|
||||
log.WithFields(log.Fields{"path": path, "schedule": k, "deadline": v, "error": err}).Errorf("")
|
||||
return err
|
||||
}
|
||||
default:
|
||||
err := errors.New("invalid schedule")
|
||||
log.WithFields(log.Fields{"path": path, "schedule": k, "deadline": v, "error": err}).Errorf("")
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
c.box = make(map[string]*Box)
|
||||
for k, v := range c.Box {
|
||||
v.Name = k
|
||||
v.online = false
|
||||
v.zfs = NewZFSConfig()
|
||||
s := &SSHConfig{
|
||||
logged: false,
|
||||
name: k,
|
||||
}
|
||||
v.ssh = s
|
||||
keyRaw, err := ioutil.ReadFile(v.Key)
|
||||
if err != nil {
|
||||
if *debugFlag {
|
||||
log.Printf("Config.Load : ioutil.ReadFile(%s) : %s", k, err)
|
||||
}
|
||||
if b, err := c.NewBox(k, v.Addr, v.User, v.Key); err != nil {
|
||||
log.WithFields(log.Fields{"path": path, "call": "NewBox", "attr": k, "error": err}).Errorf("")
|
||||
return err
|
||||
}
|
||||
|
||||
key, err := ssh.ParseRawPrivateKey(keyRaw)
|
||||
if err != nil {
|
||||
if *debugFlag {
|
||||
log.Printf("Config.Load : ssh.ParseRawPrivateKey(%s) : %s", k, err)
|
||||
}
|
||||
return err
|
||||
}
|
||||
|
||||
s.signer, err = ssh.NewSignerFromKey(key)
|
||||
if err != nil {
|
||||
if *debugFlag {
|
||||
log.Printf("Config.Load : ssh.NewSignerFromKey(%s) : %s", k, err)
|
||||
}
|
||||
return err
|
||||
}
|
||||
|
||||
s.config = &ssh.ClientConfig{
|
||||
User: v.User,
|
||||
Auth: []ssh.AuthMethod{
|
||||
ssh.PublicKeys(s.signer),
|
||||
},
|
||||
HostKeyCallback: ssh.InsecureIgnoreHostKey(),
|
||||
Timeout: 5 * time.Second,
|
||||
}
|
||||
|
||||
s.client, err = ssh.Dial("tcp", v.Addr, s.config)
|
||||
if err != nil {
|
||||
if *debugFlag {
|
||||
log.Printf("Config.Load : ssh.Dial(%s) : %s", k, err)
|
||||
}
|
||||
} else {
|
||||
v.online = true
|
||||
session, err := s.client.NewSession()
|
||||
if err != nil {
|
||||
if *debugFlag {
|
||||
log.Printf("Config.Load : client.NewSession(%s) : %s", k, err)
|
||||
}
|
||||
if _, ok := c.box[k]; ok {
|
||||
err := errors.New("already exists")
|
||||
log.WithFields(log.Fields{"path": path, "attr": k, "error": err}).Errorf("")
|
||||
return err
|
||||
}
|
||||
|
||||
var b bytes.Buffer
|
||||
session.Stdout = &b
|
||||
|
||||
err = session.Run("TZ=\"" + cfg.Timezone + "\" zfsnap --version")
|
||||
if err != nil {
|
||||
if *debugFlag {
|
||||
log.Printf("Config.Load : client.NewSession(%s) : %s", k, err)
|
||||
}
|
||||
return err
|
||||
}
|
||||
if *debugFlag {
|
||||
log.Printf("Config.Load : logged into %s : %s", k, b.String())
|
||||
}
|
||||
session.Close()
|
||||
s.logged = true
|
||||
c.box[k] = b
|
||||
}
|
||||
}
|
||||
|
||||
for _, app := range c.Apps {
|
||||
for _, src := range app.Sources {
|
||||
if !src.Valid() {
|
||||
return fmt.Errorf("Source not valid : %s", string(src))
|
||||
}
|
||||
if _, ok := cfg.Box[src.Box()]; !ok {
|
||||
return fmt.Errorf("No box defined for source : %s", string(src))
|
||||
}
|
||||
if !cfg.Box[src.Box()].online {
|
||||
email.items = append(email.items, fmt.Sprintf("Source box offline for app : %s", app.Name))
|
||||
}
|
||||
}
|
||||
var allOffline bool = true
|
||||
for _, dest := range app.Destinations {
|
||||
if !dest.Valid() {
|
||||
return fmt.Errorf("Destination not valid : %s", string(dest))
|
||||
}
|
||||
if _, ok := cfg.Box[dest.Box()]; !ok {
|
||||
return fmt.Errorf("No box defined for destination : %s", string(dest))
|
||||
}
|
||||
if cfg.Box[dest.Box()].online {
|
||||
allOffline = false
|
||||
}
|
||||
}
|
||||
if allOffline {
|
||||
email.items = append(email.items, fmt.Sprintf("No online destination box for app : %s", app.Name))
|
||||
}
|
||||
|
||||
for val, before := range app.Before {
|
||||
_, err = regexp.Compile(val)
|
||||
if err != nil {
|
||||
if *debugFlag {
|
||||
log.Printf("Config.Load : invalid regex : %s", val)
|
||||
}
|
||||
c.apps = make(map[string]*App)
|
||||
for _, v := range c.Apps {
|
||||
if a, err := c.NewApp(v.Name, v.Sources, v.Destinations, v.Schedule, v.Before, v.After); err != nil {
|
||||
log.WithFields(log.Fields{"path": path, "call": "NewApp", "attr": v.Name, "error": err}).Errorf("")
|
||||
return err
|
||||
} else {
|
||||
if _, ok := c.apps[v.Name]; ok {
|
||||
err := errors.New("app already exists")
|
||||
log.WithFields(log.Fields{"path": path, "app": v.Name, "error": err}).Errorf("")
|
||||
return err
|
||||
}
|
||||
if !before.Valid() {
|
||||
return fmt.Errorf("Before not valid : %s", string(before))
|
||||
}
|
||||
if _, ok := cfg.Box[before.Box()]; !ok {
|
||||
return fmt.Errorf("No box defined for before : %s", string(before))
|
||||
}
|
||||
if !cfg.Box[before.Box()].online {
|
||||
email.items = append(email.items, fmt.Sprintf("Before box offline for app : %s", app.Name))
|
||||
}
|
||||
}
|
||||
for val, after := range app.After {
|
||||
_, err = regexp.Compile(val)
|
||||
if err != nil {
|
||||
if *debugFlag {
|
||||
log.Printf("Config.Load : invalid regex : %s", val)
|
||||
}
|
||||
return err
|
||||
}
|
||||
if !after.Valid() {
|
||||
return fmt.Errorf("After not valid : %s", string(after))
|
||||
}
|
||||
if _, ok := cfg.Box[after.Box()]; !ok {
|
||||
return fmt.Errorf("No box defined for after : %s", string(after))
|
||||
}
|
||||
if !cfg.Box[after.Box()].online {
|
||||
email.items = append(email.items, fmt.Sprintf("After box offline for app : %s", app.Name))
|
||||
}
|
||||
c.apps[v.Name] = a
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
//Close config
|
||||
func (c *Config) Close() error {
|
||||
return nil
|
||||
func (c *Config) Start(e *Email) {
|
||||
log.WithFields(log.Fields{}).Debugf("starting")
|
||||
defer log.WithFields(log.Fields{}).Debugf("done")
|
||||
|
||||
var wg sync.WaitGroup
|
||||
for _, b := range c.box {
|
||||
wg.Add(1)
|
||||
go func(box *Box) {
|
||||
defer wg.Done()
|
||||
if err := box.Open(); err != nil {
|
||||
log.WithFields(log.Fields{"name": box.name, "call": "Open", "error": err}).Errorf("")
|
||||
if e != nil {
|
||||
e.AddItem(fmt.Sprintf(" - Box : %s is down", box.name))
|
||||
}
|
||||
return
|
||||
}
|
||||
}(b)
|
||||
}
|
||||
|
||||
wg.Wait()
|
||||
}
|
||||
|
||||
// Run config
|
||||
func (c *Config) Run(e *Email) {
|
||||
log.WithFields(log.Fields{}).Debugf("starting")
|
||||
defer log.WithFields(log.Fields{}).Debugf("done")
|
||||
|
||||
var wg sync.WaitGroup
|
||||
for _, a := range cfg.apps {
|
||||
wg.Add(1)
|
||||
go func(app *App) {
|
||||
if err := app.Run(e.startTime); err != nil {
|
||||
e.AddItem(fmt.Sprintf(" - App : Error running %s (%s)", app.name, err))
|
||||
}
|
||||
wg.Done()
|
||||
}(a)
|
||||
}
|
||||
|
||||
wg.Wait()
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
func (c *Config) Stop(e *Email) {
|
||||
log.WithFields(log.Fields{}).Debugf("starting")
|
||||
defer log.WithFields(log.Fields{}).Debugf("done")
|
||||
|
||||
for _, b := range c.box {
|
||||
if err := b.Close(); err != nil {
|
||||
log.WithFields(log.Fields{"name": b.name, "call": "Close", "error": err}).Errorf("")
|
||||
}
|
||||
}
|
||||
|
||||
if len(e.items) > 0 {
|
||||
if err := e.Send(); err != nil {
|
||||
log.WithFields(log.Fields{"call": "email.Send", "error": err}).Errorf("")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user