cronsun/conf/conf.go

304 lines
6.2 KiB
Go
Raw Normal View History

2017-01-04 09:27:36 +00:00
package conf
import (
2018-03-08 10:33:20 +00:00
"errors"
2018-09-26 07:26:39 +00:00
"fmt"
2018-03-08 06:35:49 +00:00
"io/ioutil"
"os"
2017-01-04 09:27:36 +00:00
"path"
2018-03-08 10:33:20 +00:00
"strings"
2017-01-04 09:27:36 +00:00
"time"
client "github.com/coreos/etcd/clientv3"
"github.com/fsnotify/fsnotify"
2017-04-05 10:12:35 +00:00
"github.com/go-gomail/gomail"
2018-07-20 09:20:27 +00:00
"github.com/gofrs/uuid"
2017-01-04 09:27:36 +00:00
2017-05-12 06:48:24 +00:00
"github.com/shunfei/cronsun/db"
2017-05-12 07:38:50 +00:00
"github.com/shunfei/cronsun/event"
"github.com/shunfei/cronsun/log"
2017-05-09 10:40:19 +00:00
"github.com/shunfei/cronsun/utils"
2017-01-04 09:27:36 +00:00
)
var (
2017-01-09 02:32:14 +00:00
Config = new(Conf)
initialized bool
watcher *fsnotify.Watcher
exitChan = make(chan struct{})
2017-01-04 09:27:36 +00:00
)
func Init(confFile string, watchConfiFile bool) error {
2017-01-09 02:32:14 +00:00
if initialized {
return nil
}
2018-02-23 07:31:58 +00:00
if err := Config.parse(confFile); err != nil {
2017-01-04 09:27:36 +00:00
return err
}
if watchConfiFile {
if err := Config.watch(confFile); err != nil {
return err
}
2017-01-04 09:27:36 +00:00
}
2017-01-09 02:32:14 +00:00
initialized = true
2017-01-04 09:27:36 +00:00
return nil
}
type Conf struct {
2017-04-06 08:50:07 +00:00
Node string // node 进程路径
Proc string // 当前执行任务路径
Cmd string // cmd 路径
Once string // 马上执行任务路径
2018-05-26 12:58:29 +00:00
Csctl string // csctl 发送执行命令的路径
2017-04-06 08:50:07 +00:00
Lock string // job lock 路径
Group string // 节点分组
Noticer string // 通知
2017-01-05 02:37:48 +00:00
2018-04-29 13:19:50 +00:00
PIDFile string
2018-03-08 10:33:20 +00:00
UUIDFile string
2017-01-06 08:59:31 +00:00
Ttl int64 // 节点超时时间,单位秒
ReqTimeout int // 请求超时时间,单位秒
2017-03-06 09:34:30 +00:00
// 执行任务信息过期时间,单位秒
// 0 为不过期
ProcTtl int64
// 记录任务执行中的信息的执行时间阀值,单位秒
// 0 为不限制
ProcReq int64
// 单机任务锁过期时间,单位秒
// 默认 300
LockTtl int64
2017-01-05 12:35:41 +00:00
2018-02-27 07:19:41 +00:00
Etcd *etcdConfig
2017-05-11 10:08:25 +00:00
Mgo *db.Config
Web *webConfig
2017-04-05 10:12:35 +00:00
Mail *MailConf
2017-03-03 02:31:12 +00:00
Security *Security
2017-01-09 02:32:14 +00:00
}
2018-02-27 07:19:41 +00:00
type etcdConfig struct {
Endpoints []string
Username string
Password string
DialTimeout int64 // 单位秒
conf client.Config
}
func (e *etcdConfig) Copy() client.Config {
return e.conf
}
2017-01-09 02:32:14 +00:00
type webConfig struct {
BindAddr string
Auth struct {
Enabled bool
}
2017-12-07 07:37:01 +00:00
Session SessionConfig
LogCleaner struct {
EveryMinute int
ExpirationDays int
}
}
type SessionConfig struct {
Expiration int
CookieName string
StorePrefixPath string
2017-01-04 09:27:36 +00:00
}
2017-01-12 08:35:30 +00:00
2017-04-05 10:12:35 +00:00
type MailConf struct {
Enable bool
To []string
// 如果配置,则按 http api 方式发送,否则按 smtp 方式发送
HttpAPI string
2017-04-05 10:12:35 +00:00
// 如果此时间段内没有邮件发送,则关闭 SMTP 连接,单位/秒
Keepalive int64
*gomail.Dialer
2017-04-05 10:12:35 +00:00
}
2017-03-03 02:31:12 +00:00
type Security struct {
// 是不开启安全选项
// true 开启
// 所执行的命令只能是机器上的脚本,仅支持配置项里的扩展名
// 执行用户只能选择配置里的用户
// false 关闭,命令和用户可以用动填写
2017-03-09 06:42:59 +00:00
Open bool `json:"open"`
2017-03-03 02:31:12 +00:00
// 配置执行用户
2017-03-09 06:42:59 +00:00
Users []string `json:"users"`
2017-03-03 02:31:12 +00:00
// 支持的执行的脚本扩展名
2017-03-09 06:42:59 +00:00
Ext []string `json:"ext"`
2017-03-03 02:31:12 +00:00
}
2017-01-12 08:35:30 +00:00
// 返回前后包含斜杆的 /a/b/ 的前缀
func cleanKeyPrefix(p string) string {
p = path.Clean(p)
if p[0] != '/' {
p = "/" + p
}
p += "/"
return p
}
2018-03-08 10:33:20 +00:00
var errUUIDFilePathRequired = errors.New("the UUIDFile file path is required, see base.json.sample")
2018-03-08 06:35:49 +00:00
func (c *Conf) UUID() (string, error) {
2018-03-08 10:33:20 +00:00
c.UUIDFile = strings.TrimSpace(c.UUIDFile)
if len(c.UUIDFile) == 0 {
return "", errUUIDFilePathRequired
}
c.UUIDFile = path.Clean(c.UUIDFile)
b, err := ioutil.ReadFile(c.UUIDFile)
2018-03-08 06:35:49 +00:00
if err == nil {
if len(b) == 0 {
return c.genUUID()
}
suid := strings.Join(strings.Fields(string(b)), "")
return suid, nil
2018-03-08 06:35:49 +00:00
}
if !os.IsNotExist(err) {
return "", err
}
return c.genUUID()
}
func (c *Conf) genUUID() (string, error) {
u, err := uuid.NewV4()
if err != nil {
return "", err
}
2018-03-08 10:33:20 +00:00
uuidDir := path.Dir(c.UUIDFile)
if err := os.MkdirAll(uuidDir, 0755); err != nil {
2018-09-26 07:26:39 +00:00
return "", fmt.Errorf("failed to write UUID to file: %s. you can change UUIDFile config in base.json", err)
2018-03-08 10:33:20 +00:00
}
err = ioutil.WriteFile(c.UUIDFile, []byte(u.String()), 0600)
2018-03-08 06:35:49 +00:00
if err != nil {
2018-09-26 07:26:39 +00:00
return "", fmt.Errorf("failed to write UUID to file: %s. you can change UUIDFile config in base.json", err)
2018-03-08 06:35:49 +00:00
}
return u.String(), nil
}
2018-02-23 07:31:58 +00:00
func (c *Conf) parse(confFile string) error {
err := utils.LoadExtendConf(confFile, c)
if err != nil {
return err
}
if c.Etcd.DialTimeout > 0 {
2018-02-27 07:19:41 +00:00
c.Etcd.conf.DialTimeout = time.Duration(c.Etcd.DialTimeout) * time.Second
}
2018-02-27 07:19:41 +00:00
c.Etcd.conf.Username = c.Etcd.Username
c.Etcd.conf.Password = c.Etcd.Password
c.Etcd.conf.Endpoints = c.Etcd.Endpoints
2017-03-14 07:23:53 +00:00
if c.Ttl <= 0 {
c.Ttl = 10
}
if c.LockTtl < 2 {
c.LockTtl = 300
}
2017-04-05 10:12:35 +00:00
if c.Mail.Keepalive <= 0 {
c.Mail.Keepalive = 30
}
2017-05-11 10:08:25 +00:00
if c.Mgo.Timeout <= 0 {
c.Mgo.Timeout = 10 * time.Second
} else {
c.Mgo.Timeout *= time.Second
}
2017-12-07 07:37:01 +00:00
if c.Web != nil {
if c.Web.LogCleaner.EveryMinute < 0 {
c.Web.LogCleaner.EveryMinute = 30
}
if c.Web.LogCleaner.ExpirationDays <= 0 {
c.Web.LogCleaner.ExpirationDays = 1
}
}
c.Node = cleanKeyPrefix(c.Node)
c.Proc = cleanKeyPrefix(c.Proc)
2017-04-06 08:50:07 +00:00
c.Cmd = cleanKeyPrefix(c.Cmd)
c.Once = cleanKeyPrefix(c.Once)
2018-05-26 12:58:29 +00:00
c.Csctl = cleanKeyPrefix(c.Csctl)
2017-04-06 08:50:07 +00:00
c.Lock = cleanKeyPrefix(c.Lock)
c.Group = cleanKeyPrefix(c.Group)
2017-04-06 08:50:07 +00:00
c.Noticer = cleanKeyPrefix(c.Noticer)
return nil
}
2018-02-23 07:31:58 +00:00
func (c *Conf) watch(confFile string) error {
var err error
watcher, err = fsnotify.NewWatcher()
if err != nil {
return err
}
go func() {
duration := 3 * time.Second
timer, update := time.NewTimer(duration), false
for {
select {
case <-exitChan:
return
case event := <-watcher.Events:
// 保存文件时会产生多个事件
if event.Op&(fsnotify.Write|fsnotify.Chmod) > 0 {
update = true
}
timer.Reset(duration)
case <-timer.C:
if update {
2018-02-23 07:31:58 +00:00
c.reload(confFile)
2017-03-08 09:00:22 +00:00
event.Emit(event.WAIT, nil)
update = false
}
timer.Reset(duration)
case err := <-watcher.Errors:
log.Warnf("config watcher err: %v", err)
}
}
}()
2018-02-23 07:31:58 +00:00
return watcher.Add(confFile)
}
// 重新加载配置项
// 注:与系统资源相关的选项不生效,需重启程序
// Etcd
// Mgo
// Web
2018-02-23 07:31:58 +00:00
func (c *Conf) reload(confFile string) {
cf := new(Conf)
2018-02-23 07:31:58 +00:00
if err := cf.parse(confFile); err != nil {
2017-05-12 07:38:50 +00:00
log.Warnf("config file reload err: %s", err.Error())
return
}
2017-03-09 09:43:08 +00:00
// etcd key 选项需要重启
2018-05-26 12:58:29 +00:00
cf.Node, cf.Proc, cf.Cmd, cf.Once, cf.Csctl, cf.Lock, cf.Group, cf.Noticer = c.Node, c.Proc, c.Cmd, c.Once, c.Csctl, c.Lock, c.Group, c.Noticer
2017-03-09 09:43:08 +00:00
*c = *cf
2018-02-23 07:31:58 +00:00
log.Infof("config file[%s] reload success", confFile)
return
}
func Exit(i interface{}) {
close(exitChan)
if watcher != nil {
watcher.Close()
}
}