package main import ( "bytes" "encoding/json" "fmt" "io/ioutil" "log" "regexp" "time" "golang.org/x/crypto/ssh" ) type Config struct { Zfsnap map[string]string `json:"zfsnap"` Box map[string]*Box `json:"box"` Apps []AppConfig `json:apps` Timezone string `json:"timezone"` Now time.Time `json:"-"` } //Load config from file func (c *Config) Load() error { if *debugFlag { log.Printf("SSHConfig.Load : Start") } b, err := ioutil.ReadFile(*cfgFile) if err != nil { if *debugFlag { log.Printf("Config.Load : ioutil.ReadFile(%s) : %s", *cfgFile, err) } return err } err = json.Unmarshal(b, &c) if err != nil { if *debugFlag { log.Printf("Config.Load : json.Unmarshal : %s", err) } return err } l, err := time.LoadLocation(cfg.Timezone) if err != nil { if *debugFlag { log.Printf("Config.Load : time.LoadLocation : %s", err) } return err } c.Now = time.Now().In(l) for k, v := range c.Box { v.Name = k 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) } 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(), } 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) } return err } session, err := s.client.NewSession() if err != nil { if *debugFlag { log.Printf("Config.Load : client.NewSession(%s) : %s", k, err) } 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 } 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)) } } 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)) } } for val, before := range app.Before { _, err = regexp.Compile(val) if err != nil { if *debugFlag { log.Printf("Config.Load : invalid regex : %s", val) } 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)) } } 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)) } } } return nil } //Close config func (c *Config) Close() error { return nil }