From f0ff9a88a7801e0cc7dce516aa4850214416047a Mon Sep 17 00:00:00 2001 From: ouqiang Date: Sat, 16 Sep 2017 17:58:33 +0800 Subject: [PATCH] =?UTF-8?q?style:=20=E4=BB=A3=E7=A0=81=E6=A0=BC=E5=BC=8F?= =?UTF-8?q?=E5=8C=96?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- cmd/web.go | 296 +++++---- gocron-node.go | 102 ++- gocron.go | 24 +- models/host.go | 103 ++- models/login_log.go | 34 +- models/migration.go | 238 ++++--- models/model.go | 162 ++--- models/setting.go | 200 +++--- models/task.go | 341 +++++----- models/task_host.go | 91 ++- models/task_log.go | 129 ++-- models/user.go | 94 +-- modules/app/app.go | 153 +++-- modules/httpclient/http_client.go | 112 ++-- modules/logger/logger.go | 156 ++--- modules/notify/mail.go | 134 ++-- modules/notify/notify.go | 66 +- modules/notify/slack.go | 125 ++-- modules/rpc/auth/Certification.go | 12 +- modules/rpc/client/client.go | 117 ++-- modules/rpc/grpcpool/grpc_pool.go | 208 +++--- modules/rpc/server/server.go | 103 ++- modules/setting/setting.go | 166 ++--- modules/ssh/ssh.go | 210 +++---- modules/utils/json.go | 54 +- modules/utils/utils.go | 116 ++-- modules/utils/utils_test.go | 24 +- modules/utils/utils_unix.go | 52 +- modules/utils/utils_windows.go | 75 ++- routers/base/base.go | 28 +- routers/home.go | 4 +- routers/host/host.go | 312 +++++---- routers/install/install.go | 240 +++---- routers/loginlog/login_log.go | 50 +- routers/manage/manage.go | 168 +++-- routers/routers.go | 486 +++++++------- routers/task/task.go | 519 ++++++++------- routers/tasklog/task_log.go | 120 ++-- routers/user/user.go | 183 +++--- service/task.go | 595 +++++++++--------- vendor/github.com/Unknwon/com/math.go | 4 +- vendor/github.com/go-macaron/csrf/csrf.go | 2 +- .../github.com/go-macaron/session/memory.go | 434 ++++++------- vendor/github.com/go-xorm/core/cache.go | 5 +- vendor/github.com/jakecoffman/cron/cron.go | 2 +- vendor/github.com/jakecoffman/cron/parser.go | 12 +- vendor/golang.org/x/crypto/ssh/kex.go | 8 +- 47 files changed, 3411 insertions(+), 3458 deletions(-) diff --git a/cmd/web.go b/cmd/web.go index 0f57d91..5e275a9 100644 --- a/cmd/web.go +++ b/cmd/web.go @@ -1,194 +1,192 @@ package cmd import ( - "github.com/ouqiang/gocron/modules/app" - "github.com/ouqiang/gocron/routers" - "github.com/urfave/cli" - "gopkg.in/macaron.v1" - "os" - "os/signal" - "syscall" - "github.com/ouqiang/gocron/modules/logger" - "github.com/ouqiang/gocron/service" - "github.com/ouqiang/gocron/models" - "github.com/ouqiang/gocron/modules/setting" - "time" - "github.com/ouqiang/gocron/modules/rpc/grpcpool" + "github.com/ouqiang/gocron/models" + "github.com/ouqiang/gocron/modules/app" + "github.com/ouqiang/gocron/modules/logger" + "github.com/ouqiang/gocron/modules/rpc/grpcpool" + "github.com/ouqiang/gocron/modules/setting" + "github.com/ouqiang/gocron/routers" + "github.com/ouqiang/gocron/service" + "github.com/urfave/cli" + "gopkg.in/macaron.v1" + "os" + "os/signal" + "syscall" + "time" ) // web服务器默认端口 const DefaultPort = 5920 - var CmdWeb = cli.Command{ - Name: "web", - Usage: "run web server", - Action: runWeb, - Flags: []cli.Flag{ - cli.StringFlag{ - Name: "host", - Value: "0.0.0.0", - Usage: "bind host", - }, - cli.IntFlag{ - Name: "port,p", - Value: DefaultPort, - Usage: "bind port", - }, - cli.StringFlag{ - Name: "env,e", - Value: "prod", - Usage: "runtime environment, dev|test|prod", - }, - }, + Name: "web", + Usage: "run web server", + Action: runWeb, + Flags: []cli.Flag{ + cli.StringFlag{ + Name: "host", + Value: "0.0.0.0", + Usage: "bind host", + }, + cli.IntFlag{ + Name: "port,p", + Value: DefaultPort, + Usage: "bind port", + }, + cli.StringFlag{ + Name: "env,e", + Value: "prod", + Usage: "runtime environment, dev|test|prod", + }, + }, } func runWeb(ctx *cli.Context) { - // 设置运行环境 - setEnvironment(ctx) - // 初始化应用 - app.InitEnv(ctx.App.Version) - // 初始化模块 DB、定时任务等 - initModule() - // 捕捉信号,配置热更新等 - go catchSignal() - m := macaron.Classic() + // 设置运行环境 + setEnvironment(ctx) + // 初始化应用 + app.InitEnv(ctx.App.Version) + // 初始化模块 DB、定时任务等 + initModule() + // 捕捉信号,配置热更新等 + go catchSignal() + m := macaron.Classic() - // 注册路由 - routers.Register(m) - // 注册中间件. - routers.RegisterMiddleware(m) - host := parseHost(ctx) - port := parsePort(ctx) - m.Run(host, port) + // 注册路由 + routers.Register(m) + // 注册中间件. + routers.RegisterMiddleware(m) + host := parseHost(ctx) + port := parsePort(ctx) + m.Run(host, port) } -func initModule() { - if !app.Installed { - return - } +func initModule() { + if !app.Installed { + return + } - config, err := setting.Read(app.AppConfig) - if err != nil { - logger.Fatal("读取应用配置失败", err) - } - app.Setting = config + config, err := setting.Read(app.AppConfig) + if err != nil { + logger.Fatal("读取应用配置失败", err) + } + app.Setting = config - // 初始化DB - models.Db = models.CreateDb() + // 初始化DB + models.Db = models.CreateDb() - // 版本升级 - upgradeIfNeed() + // 版本升级 + upgradeIfNeed() - // 初始化定时任务 - serviceTask := new(service.Task) - serviceTask.Initialize() + // 初始化定时任务 + serviceTask := new(service.Task) + serviceTask.Initialize() } // 解析端口 func parsePort(ctx *cli.Context) int { - var port int = DefaultPort - if ctx.IsSet("port") { - port = ctx.Int("port") - } - if port <= 0 || port >= 65535 { - port = DefaultPort - } + var port int = DefaultPort + if ctx.IsSet("port") { + port = ctx.Int("port") + } + if port <= 0 || port >= 65535 { + port = DefaultPort + } - return port + return port } -func parseHost(ctx *cli.Context) string { - if ctx.IsSet("host") { - return ctx.String("host") - } +func parseHost(ctx *cli.Context) string { + if ctx.IsSet("host") { + return ctx.String("host") + } - return "0.0.0.0" + return "0.0.0.0" } -func setEnvironment(ctx *cli.Context) { - var env string = "prod" - if ctx.IsSet("env") { - env = ctx.String("env") - } +func setEnvironment(ctx *cli.Context) { + var env string = "prod" + if ctx.IsSet("env") { + env = ctx.String("env") + } - switch env { - case "test": - macaron.Env = macaron.TEST - case "dev": - macaron.Env = macaron.DEV - default: - macaron.Env = macaron.PROD - } + switch env { + case "test": + macaron.Env = macaron.TEST + case "dev": + macaron.Env = macaron.DEV + default: + macaron.Env = macaron.PROD + } } // 捕捉信号 -func catchSignal() { - c := make(chan os.Signal) - // todo 配置热更新, windows 不支持 syscall.SIGUSR1, syscall.SIGUSR2 - signal.Notify(c, syscall.SIGHUP, syscall.SIGINT, syscall.SIGTERM) - for { - s := <- c - logger.Info("收到信号 -- ", s) - switch s { - case syscall.SIGHUP: - logger.Info("收到终端断开信号, 忽略") - case syscall.SIGINT, syscall.SIGTERM: - shutdown() - } - } +func catchSignal() { + c := make(chan os.Signal) + // todo 配置热更新, windows 不支持 syscall.SIGUSR1, syscall.SIGUSR2 + signal.Notify(c, syscall.SIGHUP, syscall.SIGINT, syscall.SIGTERM) + for { + s := <-c + logger.Info("收到信号 -- ", s) + switch s { + case syscall.SIGHUP: + logger.Info("收到终端断开信号, 忽略") + case syscall.SIGINT, syscall.SIGTERM: + shutdown() + } + } } - // 应用退出 -func shutdown() { - defer func() { - logger.Info("已退出") - os.Exit(0) - }() +func shutdown() { + defer func() { + logger.Info("已退出") + os.Exit(0) + }() - if !app.Installed { - return - } - logger.Info("应用准备退出") - serviceTask := new(service.Task) - // 停止所有任务调度 - logger.Info("停止定时任务调度") - serviceTask.StopAll() + if !app.Installed { + return + } + logger.Info("应用准备退出") + serviceTask := new(service.Task) + // 停止所有任务调度 + logger.Info("停止定时任务调度") + serviceTask.StopAll() - taskNumInRunning := service.TaskNum.Num() - logger.Infof("正在运行的任务有%d个", taskNumInRunning) - if taskNumInRunning > 0 { - logger.Info("等待所有任务执行完成后退出") - } - for { - if taskNumInRunning <= 0 { - break - } - time.Sleep(3 * time.Second) - taskNumInRunning = service.TaskNum.Num() - } + taskNumInRunning := service.TaskNum.Num() + logger.Infof("正在运行的任务有%d个", taskNumInRunning) + if taskNumInRunning > 0 { + logger.Info("等待所有任务执行完成后退出") + } + for { + if taskNumInRunning <= 0 { + break + } + time.Sleep(3 * time.Second) + taskNumInRunning = service.TaskNum.Num() + } - // 释放gRPC连接池 - grpcpool.Pool.ReleaseAll() + // 释放gRPC连接池 + grpcpool.Pool.ReleaseAll() } // 判断应用是否需要升级, 当存在版本号文件且版本小于app.VersionId时升级 -func upgradeIfNeed() { - currentVersionId := app.GetCurrentVersionId() - // 没有版本号文件 - if currentVersionId == 0 { - return; - } - if currentVersionId >= app.VersionId { - return - } +func upgradeIfNeed() { + currentVersionId := app.GetCurrentVersionId() + // 没有版本号文件 + if currentVersionId == 0 { + return + } + if currentVersionId >= app.VersionId { + return + } - migration := new(models.Migration) - logger.Infof("版本升级开始, 当前版本号%d", currentVersionId) + migration := new(models.Migration) + logger.Infof("版本升级开始, 当前版本号%d", currentVersionId) - migration.Upgrade(currentVersionId) - app.UpdateVersionFile() + migration.Upgrade(currentVersionId) + app.UpdateVersionFile() - logger.Infof("已升级到最新版本%d", app.VersionId) -} \ No newline at end of file + logger.Infof("已升级到最新版本%d", app.VersionId) +} diff --git a/gocron-node.go b/gocron-node.go index 812317e..c751f0d 100644 --- a/gocron-node.go +++ b/gocron-node.go @@ -4,67 +4,65 @@ package main import ( + "flag" + "fmt" + "github.com/ouqiang/gocron/modules/rpc/auth" "github.com/ouqiang/gocron/modules/rpc/server" - "flag" - "runtime" - "os" - "fmt" - "strings" - "github.com/ouqiang/gocron/modules/rpc/auth" - "github.com/ouqiang/gocron/modules/utils" + "github.com/ouqiang/gocron/modules/utils" + "os" + "runtime" + "strings" ) const AppVersion = "1.2.2" -func main() { +func main() { var serverAddr string - var allowRoot bool - var version bool - var CAFile string - var certFile string - var keyFile string - var enableTLS bool - flag.BoolVar(&allowRoot, "allow-root", false, "./gocron-node -allow-root") - flag.StringVar(&serverAddr, "s", "0.0.0.0:5921", "./gocron-node -s ip:port") - flag.BoolVar(&version, "v", false, "./gocron-node -v") - flag.BoolVar(&enableTLS, "enable-tls", false, "./gocron-node -enable-tls") - flag.StringVar(&CAFile, "ca-file", "", "./gocron-node -ca-file path") - flag.StringVar(&certFile, "cert-file", "", "./gocron-node -cert-file path") - flag.StringVar(&keyFile, "key-file", "", "./gocron-node -key-file path") - flag.Parse() + var allowRoot bool + var version bool + var CAFile string + var certFile string + var keyFile string + var enableTLS bool + flag.BoolVar(&allowRoot, "allow-root", false, "./gocron-node -allow-root") + flag.StringVar(&serverAddr, "s", "0.0.0.0:5921", "./gocron-node -s ip:port") + flag.BoolVar(&version, "v", false, "./gocron-node -v") + flag.BoolVar(&enableTLS, "enable-tls", false, "./gocron-node -enable-tls") + flag.StringVar(&CAFile, "ca-file", "", "./gocron-node -ca-file path") + flag.StringVar(&certFile, "cert-file", "", "./gocron-node -cert-file path") + flag.StringVar(&keyFile, "key-file", "", "./gocron-node -key-file path") + flag.Parse() - if version { - fmt.Println(AppVersion) - return - } + if version { + fmt.Println(AppVersion) + return + } - if (enableTLS) { - if !utils.FileExist(CAFile) { - fmt.Printf("failed to read ca cert file: %s", CAFile) - return - } - if !utils.FileExist(certFile) { - fmt.Printf("failed to read server cert file: %s", certFile) - return - } - if !utils.FileExist(keyFile) { - fmt.Printf("failed to read server key file: %s", keyFile) - return - } - } + if enableTLS { + if !utils.FileExist(CAFile) { + fmt.Printf("failed to read ca cert file: %s", CAFile) + return + } + if !utils.FileExist(certFile) { + fmt.Printf("failed to read server cert file: %s", certFile) + return + } + if !utils.FileExist(keyFile) { + fmt.Printf("failed to read server key file: %s", keyFile) + return + } + } - certificate := auth.Certificate{ - CAFile: strings.TrimSpace(CAFile), - CertFile: strings.TrimSpace(certFile), - KeyFile: strings.TrimSpace(keyFile), - } - - - if runtime.GOOS != "windows" && os.Getuid() == 0 && !allowRoot { - fmt.Println("Do not run gocron-node as root user") - return - } + certificate := auth.Certificate{ + CAFile: strings.TrimSpace(CAFile), + CertFile: strings.TrimSpace(certFile), + KeyFile: strings.TrimSpace(keyFile), + } + if runtime.GOOS != "windows" && os.Getuid() == 0 && !allowRoot { + fmt.Println("Do not run gocron-node as root user") + return + } server.Start(serverAddr, enableTLS, certificate) -} \ No newline at end of file +} diff --git a/gocron.go b/gocron.go index 2f29406..086797d 100644 --- a/gocron.go +++ b/gocron.go @@ -4,22 +4,22 @@ package main import ( - "github.com/urfave/cli" - "os" + "github.com/urfave/cli" + "os" - "github.com/ouqiang/gocron/cmd" + "github.com/ouqiang/gocron/cmd" ) const AppVersion = "1.2.2" func main() { - app := cli.NewApp() - app.Name = "gocron" - app.Usage = "gocron service" - app.Version = AppVersion - app.Commands = []cli.Command{ - cmd.CmdWeb, - } - app.Flags = append(app.Flags, []cli.Flag{}...) - app.Run(os.Args) + app := cli.NewApp() + app.Name = "gocron" + app.Usage = "gocron service" + app.Version = AppVersion + app.Commands = []cli.Command{ + cmd.CmdWeb, + } + app.Flags = append(app.Flags, []cli.Flag{}...) + app.Run(os.Args) } diff --git a/models/host.go b/models/host.go index cd13247..ab0ad36 100644 --- a/models/host.go +++ b/models/host.go @@ -1,95 +1,94 @@ package models import ( - "github.com/go-xorm/xorm" + "github.com/go-xorm/xorm" ) // 主机 type Host struct { - Id int16 `xorm:"smallint pk autoincr"` - Name string `xorm:"varchar(64) notnull"` // 主机名称 - Alias string `xorm:"varchar(32) notnull default '' "` // 主机别名 - Port int `xorm:"notnull default 22"` // 主机端口 - Remark string `xorm:"varchar(100) notnull default '' "` // 备注 - BaseModel `xorm:"-"` - Selected bool `xorm:"-"` + Id int16 `xorm:"smallint pk autoincr"` + Name string `xorm:"varchar(64) notnull"` // 主机名称 + Alias string `xorm:"varchar(32) notnull default '' "` // 主机别名 + Port int `xorm:"notnull default 22"` // 主机端口 + Remark string `xorm:"varchar(100) notnull default '' "` // 备注 + BaseModel `xorm:"-"` + Selected bool `xorm:"-"` } // 新增 func (host *Host) Create() (insertId int16, err error) { - _, err = Db.Insert(host) - if err == nil { - insertId = host.Id - } + _, err = Db.Insert(host) + if err == nil { + insertId = host.Id + } - return + return } -func (host *Host) UpdateBean(id int16) (int64, error) { - return Db.ID(id).Cols("name,alias,port,remark").Update(host) +func (host *Host) UpdateBean(id int16) (int64, error) { + return Db.ID(id).Cols("name,alias,port,remark").Update(host) } - // 更新 func (host *Host) Update(id int, data CommonMap) (int64, error) { - return Db.Table(host).ID(id).Update(data) + return Db.Table(host).ID(id).Update(data) } // 删除 func (host *Host) Delete(id int) (int64, error) { - return Db.Id(id).Delete(new(Host)) + return Db.Id(id).Delete(new(Host)) } func (host *Host) Find(id int) error { - _, err := Db.Id(id).Get(host) + _, err := Db.Id(id).Get(host) - return err + return err } -func (host *Host) NameExists(name string, id int16) (bool, error) { - if id == 0 { - count, err := Db.Where("name = ?", name).Count(host); - return count > 0, err - } +func (host *Host) NameExists(name string, id int16) (bool, error) { + if id == 0 { + count, err := Db.Where("name = ?", name).Count(host) + return count > 0, err + } - count, err := Db.Where("name = ? AND id != ?", name, id).Count(host); - return count > 0, err + count, err := Db.Where("name = ? AND id != ?", name, id).Count(host) + return count > 0, err } func (host *Host) List(params CommonMap) ([]Host, error) { - host.parsePageAndPageSize(params) - list := make([]Host, 0) - session := Db.Desc("id") - host.parseWhere(session, params) - err := session.Limit(host.PageSize, host.pageLimitOffset()).Find(&list) + host.parsePageAndPageSize(params) + list := make([]Host, 0) + session := Db.Desc("id") + host.parseWhere(session, params) + err := session.Limit(host.PageSize, host.pageLimitOffset()).Find(&list) - return list, err + return list, err } func (host *Host) AllList() ([]Host, error) { - list := make([]Host, 0) - err := Db.Cols("name,port").Desc("id").Find(&list) + list := make([]Host, 0) + err := Db.Cols("name,port").Desc("id").Find(&list) - return list, err + return list, err } func (host *Host) Total(params CommonMap) (int64, error) { - session := Db.NewSession() - host.parseWhere(session, params) - return session.Count(host) + session := Db.NewSession() + host.parseWhere(session, params) + return session.Count(host) } // 解析where -func (host *Host) parseWhere(session *xorm.Session, params CommonMap) { - if len(params) == 0 { - return - } - id, ok := params["Id"] - if ok && id.(int) > 0 { - session.And("id = ?", id) - } - name, ok := params["Name"] - if ok && name.(string) != "" { - session.And("name = ?", name) - } -} \ No newline at end of file +func (host *Host) parseWhere(session *xorm.Session, params CommonMap) { + if len(params) == 0 { + return + } + id, ok := params["Id"] + if ok && id.(int) > 0 { + session.And("id = ?", id) + } + name, ok := params["Name"] + if ok && name.(string) != "" { + session.And("name = ?", name) + } +} diff --git a/models/login_log.go b/models/login_log.go index b86ade2..e4bf24e 100644 --- a/models/login_log.go +++ b/models/login_log.go @@ -1,36 +1,36 @@ package models import ( - "time" + "time" ) // 用户登录日志 type LoginLog struct { - Id int `xorm:"pk autoincr notnull "` - Username string `xorm:"varchar(32) notnull"` - Ip string `xorm:"varchar(15) not null"` - Created time.Time `xorm:"datetime notnull created"` - BaseModel `xorm:"-"` + Id int `xorm:"pk autoincr notnull "` + Username string `xorm:"varchar(32) notnull"` + Ip string `xorm:"varchar(15) not null"` + Created time.Time `xorm:"datetime notnull created"` + BaseModel `xorm:"-"` } func (log *LoginLog) Create() (insertId int, err error) { - _, err = Db.Insert(log) - if err == nil { - insertId = log.Id - } + _, err = Db.Insert(log) + if err == nil { + insertId = log.Id + } - return + return } func (log *LoginLog) List(params CommonMap) ([]LoginLog, error) { - log.parsePageAndPageSize(params) - list := make([]LoginLog, 0) - err := Db.Desc("id").Limit(log.PageSize, log.pageLimitOffset()).Find(&list) + log.parsePageAndPageSize(params) + list := make([]LoginLog, 0) + err := Db.Desc("id").Limit(log.PageSize, log.pageLimitOffset()).Find(&list) - return list, err + return list, err } func (log *LoginLog) Total() (int64, error) { - return Db.Count(log) -} \ No newline at end of file + return Db.Count(log) +} diff --git a/models/migration.go b/models/migration.go index 8d3999d..96b75e2 100644 --- a/models/migration.go +++ b/models/migration.go @@ -1,159 +1,157 @@ package models import ( - "errors" - "fmt" - "github.com/ouqiang/gocron/modules/logger" - "github.com/go-xorm/xorm" - "strconv" + "errors" + "fmt" + "github.com/go-xorm/xorm" + "github.com/ouqiang/gocron/modules/logger" + "strconv" ) - type Migration struct{} // 首次安装, 创建数据库表 func (migration *Migration) Install(dbName string) error { - if !isDatabaseExist(dbName) { - return errors.New("数据库不存在") - } - setting := new(Setting) - task := new(Task) - tables := []interface{}{ - &User{}, task, &TaskLog{}, &Host{}, setting,&LoginLog{},&TaskHost{}, - } - for _, table := range tables { - exist, err:= Db.IsTableExist(table) - if exist { - return errors.New("数据表已存在") - } - if err != nil { - return err - } - err = Db.Sync2(table) - if err != nil { - return err - } - } - setting.InitBasicField() - task.CreateTestTask() + if !isDatabaseExist(dbName) { + return errors.New("数据库不存在") + } + setting := new(Setting) + task := new(Task) + tables := []interface{}{ + &User{}, task, &TaskLog{}, &Host{}, setting, &LoginLog{}, &TaskHost{}, + } + for _, table := range tables { + exist, err := Db.IsTableExist(table) + if exist { + return errors.New("数据表已存在") + } + if err != nil { + return err + } + err = Db.Sync2(table) + if err != nil { + return err + } + } + setting.InitBasicField() + task.CreateTestTask() - return nil + return nil } // 判断数据库是否存在 func isDatabaseExist(name string) bool { - _, err := Db.Exec("use ?", name) + _, err := Db.Exec("use ?", name) - return err != nil + return err != nil } // 迭代升级数据库, 新建表、新增字段等 -func (migration *Migration) Upgrade(oldVersionId int) { - // v1.2版本不支持升级 - if oldVersionId == 120 { - return - } +func (migration *Migration) Upgrade(oldVersionId int) { + // v1.2版本不支持升级 + if oldVersionId == 120 { + return + } - versionIds := []int{110, 122} - upgradeFuncs := []func(*xorm.Session) error { - migration.upgradeFor110, - migration.upgradeFor122, - } + versionIds := []int{110, 122} + upgradeFuncs := []func(*xorm.Session) error{ + migration.upgradeFor110, + migration.upgradeFor122, + } - startIndex := -1 - // 从当前版本的下一版本开始升级 - for i, value := range versionIds { - if value > oldVersionId { - startIndex = i - break; - } - } + startIndex := -1 + // 从当前版本的下一版本开始升级 + for i, value := range versionIds { + if value > oldVersionId { + startIndex = i + break + } + } - if startIndex == -1 { - return - } + if startIndex == -1 { + return + } - length := len(versionIds) - if startIndex >= length { - return - } + length := len(versionIds) + if startIndex >= length { + return + } - session := Db.NewSession() - err := session.Begin() - if err != nil { - logger.Fatalf("开启事务失败-%s", err.Error()) - } - for startIndex < length { - err = upgradeFuncs[startIndex](session) - if err == nil { - startIndex++ - continue - } - dbErr := session.Rollback() - if dbErr != nil { - logger.Fatalf("事务回滚失败-%s",dbErr.Error()) - } - logger.Fatal(err) - } - err = session.Commit() - if err != nil { - logger.Fatalf("提交事务失败-%s", err.Error()) - } + session := Db.NewSession() + err := session.Begin() + if err != nil { + logger.Fatalf("开启事务失败-%s", err.Error()) + } + for startIndex < length { + err = upgradeFuncs[startIndex](session) + if err == nil { + startIndex++ + continue + } + dbErr := session.Rollback() + if dbErr != nil { + logger.Fatalf("事务回滚失败-%s", dbErr.Error()) + } + logger.Fatal(err) + } + err = session.Commit() + if err != nil { + logger.Fatalf("提交事务失败-%s", err.Error()) + } } // 升级到v1.1版本 func (migration *Migration) upgradeFor110(session *xorm.Session) error { - logger.Info("开始升级到v1.1") - // 创建表task_host - err := session.Sync2(new(TaskHost)) - if err != nil { - return err - } + logger.Info("开始升级到v1.1") + // 创建表task_host + err := session.Sync2(new(TaskHost)) + if err != nil { + return err + } - tableName := TablePrefix + "task" - // 把task对应的host_id写入task_host表 - sql := fmt.Sprintf("SELECT id, host_id FROM %s WHERE host_id > 0", tableName) - results, err := session.Query(sql) - if err != nil { - return err - } + tableName := TablePrefix + "task" + // 把task对应的host_id写入task_host表 + sql := fmt.Sprintf("SELECT id, host_id FROM %s WHERE host_id > 0", tableName) + results, err := session.Query(sql) + if err != nil { + return err + } - for _, value := range results { - taskHostModel := &TaskHost{} - taskId, err := strconv.Atoi(string(value["id"])) - if err != nil { - return err - } - hostId, err := strconv.Atoi(string(value["host_id"])) - if err != nil { - return err - } - taskHostModel.TaskId = taskId - taskHostModel.HostId = int16(hostId) - _, err = session.Insert(taskHostModel) - if err != nil { - return err - } - } + for _, value := range results { + taskHostModel := &TaskHost{} + taskId, err := strconv.Atoi(string(value["id"])) + if err != nil { + return err + } + hostId, err := strconv.Atoi(string(value["host_id"])) + if err != nil { + return err + } + taskHostModel.TaskId = taskId + taskHostModel.HostId = int16(hostId) + _, err = session.Insert(taskHostModel) + if err != nil { + return err + } + } + // 删除task表host_id字段 + _, err = session.Exec(fmt.Sprintf("ALTER TABLE %s DROP COLUMN host_id", tableName)) - // 删除task表host_id字段 - _, err = session.Exec(fmt.Sprintf("ALTER TABLE %s DROP COLUMN host_id", tableName)) + logger.Info("已升级到v1.1\n") - logger.Info("已升级到v1.1\n") - - return err + return err } // 升级到1.2.2版本 func (migration *Migration) upgradeFor122(session *xorm.Session) error { - logger.Info("开始升级到v1.2.2") + logger.Info("开始升级到v1.2.2") - tableName := TablePrefix + "task" - // task表增加tag字段 - _, err := session.Exec(fmt.Sprintf("ALTER TABLE %s ADD COLUMN tag VARCHAR(32) NOT NULL DEFAULT '' ", tableName)) + tableName := TablePrefix + "task" + // task表增加tag字段 + _, err := session.Exec(fmt.Sprintf("ALTER TABLE %s ADD COLUMN tag VARCHAR(32) NOT NULL DEFAULT '' ", tableName)) - logger.Info("已升级到v1.2.2\n") + logger.Info("已升级到v1.2.2\n") - return err -} \ No newline at end of file + return err +} diff --git a/models/model.go b/models/model.go index 2ebc993..4b18b5d 100644 --- a/models/model.go +++ b/models/model.go @@ -1,16 +1,16 @@ package models import ( - "fmt" - _ "github.com/go-sql-driver/mysql" - "github.com/go-xorm/core" - "github.com/go-xorm/xorm" - "gopkg.in/macaron.v1" - "strings" - "github.com/ouqiang/gocron/modules/logger" - "github.com/ouqiang/gocron/modules/app" - "time" - "github.com/ouqiang/gocron/modules/setting" + "fmt" + _ "github.com/go-sql-driver/mysql" + "github.com/go-xorm/core" + "github.com/go-xorm/xorm" + "github.com/ouqiang/gocron/modules/app" + "github.com/ouqiang/gocron/modules/logger" + "github.com/ouqiang/gocron/modules/setting" + "gopkg.in/macaron.v1" + "strings" + "time" ) type Status int8 @@ -20,105 +20,105 @@ var TablePrefix string = "" var Db *xorm.Engine const ( - Disabled Status = 0 // 禁用 - Failure Status = 0 // 失败 - Enabled Status = 1 // 启用 - Running Status = 1 // 运行中 - Finish Status = 2 // 完成 - Cancel Status = 3 // 取消 - Waiting Status = 5 // 等待中 + Disabled Status = 0 // 禁用 + Failure Status = 0 // 失败 + Enabled Status = 1 // 启用 + Running Status = 1 // 运行中 + Finish Status = 2 // 完成 + Cancel Status = 3 // 取消 + Waiting Status = 5 // 等待中 ) const ( - Page = 1 // 当前页数 - PageSize = 20 // 每页多少条数据 - MaxPageSize = 100000 // 每次最多取多少条 + Page = 1 // 当前页数 + PageSize = 20 // 每页多少条数据 + MaxPageSize = 100000 // 每次最多取多少条 ) const DefaultTimeFormat = "2006-01-02 15:04:05" -type BaseModel struct { - Page int `xorm:"-"` - PageSize int `xorm:"-"` +type BaseModel struct { + Page int `xorm:"-"` + PageSize int `xorm:"-"` } func (model *BaseModel) parsePageAndPageSize(params CommonMap) { - page, ok := params["Page"] - if ok { - model.Page = page.(int) - } - pageSize, ok := params["PageSize"] - if ok { - model.PageSize = pageSize.(int) - } - if model.Page <= 0 { - model.Page = Page - } - if model.PageSize <= 0 { - model.PageSize = MaxPageSize - } + page, ok := params["Page"] + if ok { + model.Page = page.(int) + } + pageSize, ok := params["PageSize"] + if ok { + model.PageSize = pageSize.(int) + } + if model.Page <= 0 { + model.Page = Page + } + if model.PageSize <= 0 { + model.PageSize = MaxPageSize + } } func (model *BaseModel) pageLimitOffset() int { - return (model.Page - 1) * model.PageSize + return (model.Page - 1) * model.PageSize } // 创建Db func CreateDb() *xorm.Engine { - dsn := getDbEngineDSN(app.Setting) - engine, err := xorm.NewEngine(app.Setting.Db.Engine, dsn) - if err != nil { - logger.Fatal("创建xorm引擎失败", err) - } - engine.SetMaxIdleConns(app.Setting.Db.MaxIdleConns) - engine.SetMaxOpenConns(app.Setting.Db.MaxOpenConns) + dsn := getDbEngineDSN(app.Setting) + engine, err := xorm.NewEngine(app.Setting.Db.Engine, dsn) + if err != nil { + logger.Fatal("创建xorm引擎失败", err) + } + engine.SetMaxIdleConns(app.Setting.Db.MaxIdleConns) + engine.SetMaxOpenConns(app.Setting.Db.MaxOpenConns) - if app.Setting.Db.Prefix != "" { - // 设置表前缀 - TablePrefix = app.Setting.Db.Prefix - mapper := core.NewPrefixMapper(core.SnakeMapper{}, app.Setting.Db.Prefix) - engine.SetTableMapper(mapper) - } - // 本地环境开启日志 - if macaron.Env == macaron.DEV { - engine.ShowSQL(true) - engine.Logger().SetLevel(core.LOG_DEBUG) - } + if app.Setting.Db.Prefix != "" { + // 设置表前缀 + TablePrefix = app.Setting.Db.Prefix + mapper := core.NewPrefixMapper(core.SnakeMapper{}, app.Setting.Db.Prefix) + engine.SetTableMapper(mapper) + } + // 本地环境开启日志 + if macaron.Env == macaron.DEV { + engine.ShowSQL(true) + engine.Logger().SetLevel(core.LOG_DEBUG) + } - go keepDbAlived(engine) + go keepDbAlived(engine) - return engine + return engine } // 创建临时数据库连接 -func CreateTmpDb(setting *setting.Setting) (*xorm.Engine, error) { - dsn := getDbEngineDSN(setting) +func CreateTmpDb(setting *setting.Setting) (*xorm.Engine, error) { + dsn := getDbEngineDSN(setting) - return xorm.NewEngine(setting.Db.Engine, dsn) + return xorm.NewEngine(setting.Db.Engine, dsn) } // 获取数据库引擎DSN mysql,sqlite func getDbEngineDSN(setting *setting.Setting) string { - engine := strings.ToLower(setting.Db.Engine) - var dsn string = "" - switch engine { - case "mysql": - dsn = fmt.Sprintf("%s:%s@tcp(%s:%d)/%s?charset=%s", - setting.Db.User, - setting.Db.Password, - setting.Db.Host, - setting.Db.Port , - setting.Db.Database, - setting.Db.Charset) - } + engine := strings.ToLower(setting.Db.Engine) + var dsn string = "" + switch engine { + case "mysql": + dsn = fmt.Sprintf("%s:%s@tcp(%s:%d)/%s?charset=%s", + setting.Db.User, + setting.Db.Password, + setting.Db.Host, + setting.Db.Port, + setting.Db.Database, + setting.Db.Charset) + } - return dsn + return dsn } -func keepDbAlived(engine *xorm.Engine) { - t := time.Tick(180 * time.Second) - for { - <- t - engine.Ping() - } -} \ No newline at end of file +func keepDbAlived(engine *xorm.Engine) { + t := time.Tick(180 * time.Second) + for { + <-t + engine.Ping() + } +} diff --git a/models/setting.go b/models/setting.go index 0007e0f..f34ea70 100644 --- a/models/setting.go +++ b/models/setting.go @@ -1,14 +1,14 @@ package models import ( - "encoding/json" + "encoding/json" ) -type Setting struct { - Id int `xorm:"int pk autoincr"` - Code string `xorm:"varchar(32) notnull"` - Key string `xorm:"varchar(64) notnull"` - Value string `xorm:"varchar(4096) notnull default '' "` +type Setting struct { + Id int `xorm:"int pk autoincr"` + Code string `xorm:"varchar(32) notnull"` + Key string `xorm:"varchar(64) notnull"` + Value string `xorm:"varchar(4096) notnull default '' "` } const SlackCode = "slack" @@ -21,154 +21,154 @@ const MailUserKey = "user" // 初始化基本字段 邮件、slack等 func (setting *Setting) InitBasicField() { - setting.Code = SlackCode; - setting.Key = SlackUrlKey - Db.Insert(setting) + setting.Code = SlackCode + setting.Key = SlackUrlKey + Db.Insert(setting) - setting.Id = 0 - setting.Code = MailCode - setting.Key = MailServerKey - Db.Insert(setting) + setting.Id = 0 + setting.Code = MailCode + setting.Key = MailServerKey + Db.Insert(setting) } // region slack配置 type Slack struct { - Url string - Channels []Channel + Url string + Channels []Channel } type Channel struct { - Id int - Name string + Id int + Name string } -func (setting *Setting) Slack() (Slack, error) { - list := make([]Setting, 0) - err := Db.Where("code = ?", SlackCode).Find(&list) - slack := Slack{Url:"", Channels:make([]Channel, 0)} - if err != nil { - return slack, err - } +func (setting *Setting) Slack() (Slack, error) { + list := make([]Setting, 0) + err := Db.Where("code = ?", SlackCode).Find(&list) + slack := Slack{Url: "", Channels: make([]Channel, 0)} + if err != nil { + return slack, err + } - setting.formatSlack(list, &slack) + setting.formatSlack(list, &slack) - return slack, err + return slack, err } -func (setting *Setting) formatSlack(list []Setting, slack *Slack) { - for _, v := range list { - if v.Key == SlackUrlKey { - slack.Url = v.Value - continue - } +func (setting *Setting) formatSlack(list []Setting, slack *Slack) { + for _, v := range list { + if v.Key == SlackUrlKey { + slack.Url = v.Value + continue + } - slack.Channels = append(slack.Channels, Channel{ - v.Id, v.Value, - }) - } + slack.Channels = append(slack.Channels, Channel{ + v.Id, v.Value, + }) + } } // 更新slack webhook url func (setting *Setting) UpdateSlackUrl(url string) (int64, error) { - setting.Value = url + setting.Value = url - return Db.Cols("value").Update(setting, Setting{Code:SlackCode, Key:SlackUrlKey}) + return Db.Cols("value").Update(setting, Setting{Code: SlackCode, Key: SlackUrlKey}) } - // 创建slack渠道 func (setting *Setting) CreateChannel(channel string) (int64, error) { - setting.Code = SlackCode - setting.Key = SlackChannelKey - setting.Value = channel + setting.Code = SlackCode + setting.Key = SlackChannelKey + setting.Value = channel - return Db.Insert(setting) + return Db.Insert(setting) } -func (setting *Setting) IsChannelExist(channel string) (bool) { - setting.Code = SlackCode - setting.Key = SlackChannelKey - setting.Value = channel +func (setting *Setting) IsChannelExist(channel string) bool { + setting.Code = SlackCode + setting.Key = SlackChannelKey + setting.Value = channel - count, _ := Db.Count(setting) + count, _ := Db.Count(setting) - return count > 0 + return count > 0 } // 删除slack渠道 -func (setting *Setting) RemoveChannel(id int) (int64, error) { - setting.Code = SlackCode - setting.Key = SlackChannelKey - setting.Id = id - return Db.Delete(setting) +func (setting *Setting) RemoveChannel(id int) (int64, error) { + setting.Code = SlackCode + setting.Key = SlackChannelKey + setting.Id = id + return Db.Delete(setting) } // endregion type Mail struct { - Host string - Port int - User string - Password string - MailUsers []MailUser + Host string + Port int + User string + Password string + MailUsers []MailUser } type MailUser struct { - Id int - Username string - Email string + Id int + Username string + Email string } // region 邮件配置 -func (setting *Setting) Mail() (Mail, error) { - list := make([]Setting, 0) - err := Db.Where("code = ?", MailCode).Find(&list) - mail := Mail{MailUsers:make([]MailUser, 0)} - if err != nil { - return mail, err - } +func (setting *Setting) Mail() (Mail, error) { + list := make([]Setting, 0) + err := Db.Where("code = ?", MailCode).Find(&list) + mail := Mail{MailUsers: make([]MailUser, 0)} + if err != nil { + return mail, err + } - setting.formatMail(list, &mail) + setting.formatMail(list, &mail) - return mail, err + return mail, err } -func (setting *Setting) formatMail(list []Setting, mail *Mail) { - mailUser := MailUser{} - for _, v := range list { - if v.Key == MailServerKey { - json.Unmarshal([]byte(v.Value), mail) - continue - } - json.Unmarshal([]byte(v.Value), &mailUser) - mailUser.Id = v.Id - mail.MailUsers = append(mail.MailUsers, mailUser) - } +func (setting *Setting) formatMail(list []Setting, mail *Mail) { + mailUser := MailUser{} + for _, v := range list { + if v.Key == MailServerKey { + json.Unmarshal([]byte(v.Value), mail) + continue + } + json.Unmarshal([]byte(v.Value), &mailUser) + mailUser.Id = v.Id + mail.MailUsers = append(mail.MailUsers, mailUser) + } } -func (setting *Setting) UpdateMailServer(config string) (int64, error) { - setting.Value = config - return Db.Cols("value").Update(setting, Setting{Code:MailCode, Key:MailServerKey}) +func (setting *Setting) UpdateMailServer(config string) (int64, error) { + setting.Value = config + return Db.Cols("value").Update(setting, Setting{Code: MailCode, Key: MailServerKey}) } func (setting *Setting) CreateMailUser(username, email string) (int64, error) { - setting.Code = MailCode - setting.Key = MailUserKey - mailUser := MailUser{0, username, email} - jsonByte, err := json.Marshal(mailUser) - if err != nil { - return 0, err - } - setting.Value = string(jsonByte) + setting.Code = MailCode + setting.Key = MailUserKey + mailUser := MailUser{0, username, email} + jsonByte, err := json.Marshal(mailUser) + if err != nil { + return 0, err + } + setting.Value = string(jsonByte) - return Db.Insert(setting) + return Db.Insert(setting) } -func (setting *Setting) RemoveMailUser(id int) (int64, error) { - setting.Code = MailCode - setting.Key = MailUserKey - setting.Id = id - return Db.Delete(setting) +func (setting *Setting) RemoveMailUser(id int) (int64, error) { + setting.Code = MailCode + setting.Key = MailUserKey + setting.Id = id + return Db.Delete(setting) } -// endregion \ No newline at end of file + +// endregion diff --git a/models/task.go b/models/task.go index c2c62ee..dffdba2 100644 --- a/models/task.go +++ b/models/task.go @@ -1,272 +1,271 @@ package models import ( - "time" - "github.com/go-xorm/xorm" - "errors" - "strings" + "errors" + "github.com/go-xorm/xorm" + "strings" + "time" ) type TaskProtocol int8 const ( - TaskHTTP TaskProtocol = iota + 1 // HTTP协议 - TaskRPC // RPC方式执行命令 + TaskHTTP TaskProtocol = iota + 1 // HTTP协议 + TaskRPC // RPC方式执行命令 ) type TaskLevel int8 const ( - TaskLevelParent TaskLevel = 1 // 父任务 - TaskLevelChild TaskLevel = 2 // 子任务(依赖任务) + TaskLevelParent TaskLevel = 1 // 父任务 + TaskLevelChild TaskLevel = 2 // 子任务(依赖任务) ) type TaskDependencyStatus int8 const ( - TaskDependencyStatusStrong TaskDependencyStatus = 1 // 强依赖 - TaskDependencyStatusWeak TaskDependencyStatus = 2 // 弱依赖 + TaskDependencyStatusStrong TaskDependencyStatus = 1 // 强依赖 + TaskDependencyStatusWeak TaskDependencyStatus = 2 // 弱依赖 ) // 任务 type Task struct { - Id int `xorm:"int pk autoincr"` - Name string `xorm:"varchar(32) notnull"` // 任务名称 - Level TaskLevel `xorm:"smallint notnull index default 1"` // 任务等级 1: 主任务 2: 依赖任务 - DependencyTaskId string `xorm:"varchar(64) notnull default ''"` // 依赖任务ID,多个ID逗号分隔 - DependencyStatus TaskDependencyStatus `xorm:"smallint notnull default 1"` // 依赖关系 1:强依赖 主任务执行成功, 依赖任务才会被执行 2:弱依赖 - Spec string `xorm:"varchar(64) notnull"` // crontab - Protocol TaskProtocol `xorm:"tinyint notnull index"` // 协议 1:http 2:系统命令 - Command string `xorm:"varchar(256) notnull"` // URL地址或shell命令 - Timeout int `xorm:"mediumint notnull default 0"` // 任务执行超时时间(单位秒),0不限制 - Multi int8 `xorm:"tinyint notnull default 1"` // 是否允许多实例运行 - RetryTimes int8 `xorm:"tinyint notnull default 0"` // 重试次数 - NotifyStatus int8 `xorm:"smallint notnull default 1"` // 任务执行结束是否通知 0: 不通知 1: 失败通知 2: 执行结束通知 - NotifyType int8 `xorm:"smallint notnull default 0"` // 通知类型 1: 邮件 2: slack - NotifyReceiverId string `xorm:"varchar(256) notnull default '' "` // 通知接受者ID, setting表主键ID,多个ID逗号分隔 - Tag string `xorm:"varchar(32) notnull default ''"` - Remark string `xorm:"varchar(100) notnull default ''"` // 备注 - Status Status `xorm:"tinyint notnull index default 0"` // 状态 1:正常 0:停止 - Created time.Time `xorm:"datetime notnull created"` // 创建时间 - Deleted time.Time `xorm:"datetime deleted"` // 删除时间 - BaseModel `xorm:"-"` - Hosts []TaskHostDetail `xorm:"-"` + Id int `xorm:"int pk autoincr"` + Name string `xorm:"varchar(32) notnull"` // 任务名称 + Level TaskLevel `xorm:"smallint notnull index default 1"` // 任务等级 1: 主任务 2: 依赖任务 + DependencyTaskId string `xorm:"varchar(64) notnull default ''"` // 依赖任务ID,多个ID逗号分隔 + DependencyStatus TaskDependencyStatus `xorm:"smallint notnull default 1"` // 依赖关系 1:强依赖 主任务执行成功, 依赖任务才会被执行 2:弱依赖 + Spec string `xorm:"varchar(64) notnull"` // crontab + Protocol TaskProtocol `xorm:"tinyint notnull index"` // 协议 1:http 2:系统命令 + Command string `xorm:"varchar(256) notnull"` // URL地址或shell命令 + Timeout int `xorm:"mediumint notnull default 0"` // 任务执行超时时间(单位秒),0不限制 + Multi int8 `xorm:"tinyint notnull default 1"` // 是否允许多实例运行 + RetryTimes int8 `xorm:"tinyint notnull default 0"` // 重试次数 + NotifyStatus int8 `xorm:"smallint notnull default 1"` // 任务执行结束是否通知 0: 不通知 1: 失败通知 2: 执行结束通知 + NotifyType int8 `xorm:"smallint notnull default 0"` // 通知类型 1: 邮件 2: slack + NotifyReceiverId string `xorm:"varchar(256) notnull default '' "` // 通知接受者ID, setting表主键ID,多个ID逗号分隔 + Tag string `xorm:"varchar(32) notnull default ''"` + Remark string `xorm:"varchar(100) notnull default ''"` // 备注 + Status Status `xorm:"tinyint notnull index default 0"` // 状态 1:正常 0:停止 + Created time.Time `xorm:"datetime notnull created"` // 创建时间 + Deleted time.Time `xorm:"datetime deleted"` // 删除时间 + BaseModel `xorm:"-"` + Hosts []TaskHostDetail `xorm:"-"` } func taskHostTableName() []string { - return []string{TablePrefix + "task_host", "th"} + return []string{TablePrefix + "task_host", "th"} } // 新增 func (task *Task) Create() (insertId int, err error) { - _, err = Db.Insert(task) - if err == nil { - insertId = task.Id - } + _, err = Db.Insert(task) + if err == nil { + insertId = task.Id + } - return + return } // 新增测试任务 func (task *Task) CreateTestTask() { - // HTTP任务 - task.Name = "测试HTTP任务" - task.Level = TaskLevelParent - task.Protocol = TaskHTTP - task.Spec = "*/30 * * * * *" - task.Tag = "test-task" - // 查询IP地址区域信息 - task.Command = "http://ip.taobao.com/service/getIpInfo.php?ip=117.27.140.253" - task.Status = Enabled - task.Create() + // HTTP任务 + task.Name = "测试HTTP任务" + task.Level = TaskLevelParent + task.Protocol = TaskHTTP + task.Spec = "*/30 * * * * *" + task.Tag = "test-task" + // 查询IP地址区域信息 + task.Command = "http://ip.taobao.com/service/getIpInfo.php?ip=117.27.140.253" + task.Status = Enabled + task.Create() } -func (task *Task) UpdateBean(id int) (int64, error) { - return Db.ID(id). - Cols("name,spec,protocol,command,timeout,multi,retry_times,remark,notify_status,notify_type,notify_receiver_id, dependency_task_id, dependency_status, tag"). - Update(task) +func (task *Task) UpdateBean(id int) (int64, error) { + return Db.ID(id). + Cols("name,spec,protocol,command,timeout,multi,retry_times,remark,notify_status,notify_type,notify_receiver_id, dependency_task_id, dependency_status, tag"). + Update(task) } // 更新 func (task *Task) Update(id int, data CommonMap) (int64, error) { - return Db.Table(task).ID(id).Update(data) + return Db.Table(task).ID(id).Update(data) } // 删除 func (task *Task) Delete(id int) (int64, error) { - return Db.Id(id).Delete(task) + return Db.Id(id).Delete(task) } // 禁用 func (task *Task) Disable(id int) (int64, error) { - return task.Update(id, CommonMap{"status": Disabled}) + return task.Update(id, CommonMap{"status": Disabled}) } // 激活 func (task *Task) Enable(id int) (int64, error) { - return task.Update(id, CommonMap{"status": Enabled}) + return task.Update(id, CommonMap{"status": Enabled}) } // 获取所有激活任务 func (task *Task) ActiveList() ([]Task, error) { - list := make([]Task, 0) - err := Db.Where("status = ? AND level = ?", Enabled, TaskLevelParent). - Find(&list) + list := make([]Task, 0) + err := Db.Where("status = ? AND level = ?", Enabled, TaskLevelParent). + Find(&list) - if err != nil { - return list, err - } + if err != nil { + return list, err + } - return task.setHostsForTasks(list) + return task.setHostsForTasks(list) } // 获取某个主机下的所有激活任务 func (task *Task) ActiveListByHostId(hostId int16) ([]Task, error) { - taskHostModel := new(TaskHost) - taskIds, err := taskHostModel.GetTaskIdsByHostId(hostId) - if err != nil { - return nil, err - } - list := make([]Task, 0) - err = Db.Where("status = ? AND level = ?", Enabled, TaskLevelParent). - In("id", taskIds...). - Find(&list) - if err != nil { - return list, err - } + taskHostModel := new(TaskHost) + taskIds, err := taskHostModel.GetTaskIdsByHostId(hostId) + if err != nil { + return nil, err + } + list := make([]Task, 0) + err = Db.Where("status = ? AND level = ?", Enabled, TaskLevelParent). + In("id", taskIds...). + Find(&list) + if err != nil { + return list, err + } - return task.setHostsForTasks(list) + return task.setHostsForTasks(list) } func (task *Task) setHostsForTasks(tasks []Task) ([]Task, error) { - taskHostModel := new(TaskHost) - var err error - for i, value := range tasks { - taskHostDetails, err := taskHostModel.GetHostIdsByTaskId(value.Id) - if err != nil { - return nil, err - } - tasks[i].Hosts = taskHostDetails - } + taskHostModel := new(TaskHost) + var err error + for i, value := range tasks { + taskHostDetails, err := taskHostModel.GetHostIdsByTaskId(value.Id) + if err != nil { + return nil, err + } + tasks[i].Hosts = taskHostDetails + } - return tasks, err + return tasks, err } // 判断任务名称是否存在 -func (task *Task) NameExist(name string, id int) (bool, error) { - if id > 0 { - count, err := Db.Where("name = ? AND status = ? AND id != ?", name, Enabled, id).Count(task); - return count > 0, err - } - count, err := Db.Where("name = ? AND status = ?", name, Enabled).Count(task); +func (task *Task) NameExist(name string, id int) (bool, error) { + if id > 0 { + count, err := Db.Where("name = ? AND status = ? AND id != ?", name, Enabled, id).Count(task) + return count > 0, err + } + count, err := Db.Where("name = ? AND status = ?", name, Enabled).Count(task) - return count > 0, err + return count > 0, err } func (task *Task) GetStatus(id int) (Status, error) { - exist, err := Db.Id(id).Get(task) - if err != nil { - return 0, err - } - if !exist { - return 0, errors.New("not exist") - } + exist, err := Db.Id(id).Get(task) + if err != nil { + return 0, err + } + if !exist { + return 0, errors.New("not exist") + } - return task.Status, nil + return task.Status, nil } -func(task *Task) Detail(id int) (Task, error) { - t := Task{} - _, err := Db.Where("id=?", id).Get(&t) +func (task *Task) Detail(id int) (Task, error) { + t := Task{} + _, err := Db.Where("id=?", id).Get(&t) - if err != nil { - return t, err - } + if err != nil { + return t, err + } - taskHostModel := new(TaskHost) - t.Hosts, err = taskHostModel.GetHostIdsByTaskId(id) + taskHostModel := new(TaskHost) + t.Hosts, err = taskHostModel.GetHostIdsByTaskId(id) - return t, err + return t, err } func (task *Task) List(params CommonMap) ([]Task, error) { - task.parsePageAndPageSize(params) - list := make([]Task, 0) - session := Db.Alias("t").Join("LEFT", taskHostTableName(), "t.id = th.task_id") - task.parseWhere(session, params) - err := session.GroupBy("t.id").Desc("t.id").Cols("t.*").Limit(task.PageSize, task.pageLimitOffset()).Find(&list) + task.parsePageAndPageSize(params) + list := make([]Task, 0) + session := Db.Alias("t").Join("LEFT", taskHostTableName(), "t.id = th.task_id") + task.parseWhere(session, params) + err := session.GroupBy("t.id").Desc("t.id").Cols("t.*").Limit(task.PageSize, task.pageLimitOffset()).Find(&list) - if err != nil { - return nil, err - } + if err != nil { + return nil, err + } - return task.setHostsForTasks(list) + return task.setHostsForTasks(list) } // 获取依赖任务列表 func (task *Task) GetDependencyTaskList(ids string) ([]Task, error) { - list := make([]Task, 0) - if ids == "" { - return list, nil - } - idList := strings.Split(ids, ",") - taskIds := make([]interface{}, len(idList)) - for i, v := range idList { - taskIds[i] = v - } - fields := "t.*" - err := Db.Alias("t"). - Where("t.level = ?", TaskLevelChild). - In("t.id", taskIds). - Cols(fields). - Find(&list) + list := make([]Task, 0) + if ids == "" { + return list, nil + } + idList := strings.Split(ids, ",") + taskIds := make([]interface{}, len(idList)) + for i, v := range idList { + taskIds[i] = v + } + fields := "t.*" + err := Db.Alias("t"). + Where("t.level = ?", TaskLevelChild). + In("t.id", taskIds). + Cols(fields). + Find(&list) - if err != nil { - return list, err - } + if err != nil { + return list, err + } - return task.setHostsForTasks(list) + return task.setHostsForTasks(list) } func (task *Task) Total(params CommonMap) (int64, error) { - session := Db.Alias("t").Join("LEFT", taskHostTableName(), "t.id = th.task_id") - task.parseWhere(session, params) - list := make([]Task, 0) + session := Db.Alias("t").Join("LEFT", taskHostTableName(), "t.id = th.task_id") + task.parseWhere(session, params) + list := make([]Task, 0) - err := session.GroupBy("t.id").Find(&list) + err := session.GroupBy("t.id").Find(&list) - return int64(len(list)), err + return int64(len(list)), err } // 解析where -func (task *Task) parseWhere(session *xorm.Session, params CommonMap) { - if len(params) == 0 { - return - } - id, ok := params["Id"] - if ok && id.(int) > 0 { - session.And("t.id = ?", id) - } - hostId, ok := params["HostId"] - if ok && hostId.(int) > 0 { - session.And("th.host_id = ?", hostId) - } - name, ok := params["Name"] - if ok && name.(string) != "" { - session.And("t.name LIKE ?", "%" + name.(string) + "%") - } - protocol, ok := params["Protocol"] - if ok && protocol.(int) > 0 { - session.And("protocol = ?", protocol) - } - status, ok := params["Status"] - if ok && status.(int) > -1 { - session.And("status = ?", status) - } +func (task *Task) parseWhere(session *xorm.Session, params CommonMap) { + if len(params) == 0 { + return + } + id, ok := params["Id"] + if ok && id.(int) > 0 { + session.And("t.id = ?", id) + } + hostId, ok := params["HostId"] + if ok && hostId.(int) > 0 { + session.And("th.host_id = ?", hostId) + } + name, ok := params["Name"] + if ok && name.(string) != "" { + session.And("t.name LIKE ?", "%"+name.(string)+"%") + } + protocol, ok := params["Protocol"] + if ok && protocol.(int) > 0 { + session.And("protocol = ?", protocol) + } + status, ok := params["Status"] + if ok && status.(int) > -1 { + session.And("status = ?", status) + } - tag, ok := params["Tag"] - if ok && tag.(string) != "" { - session.And("tag = ? ", tag) - } + tag, ok := params["Tag"] + if ok && tag.(string) != "" { + session.And("tag = ? ", tag) + } } - diff --git a/models/task_host.go b/models/task_host.go index 5d1015d..c812160 100644 --- a/models/task_host.go +++ b/models/task_host.go @@ -1,81 +1,80 @@ package models - type TaskHost struct { - Id int `xorm:"int pk autoincr"` - TaskId int `xorm:"int not null index"` - HostId int16 `xorm:"smallint not null index"` + Id int `xorm:"int pk autoincr"` + TaskId int `xorm:"int not null index"` + HostId int16 `xorm:"smallint not null index"` } type TaskHostDetail struct { - TaskHost `xorm:"extends"` - Name string - Port int - Alias string + TaskHost `xorm:"extends"` + Name string + Port int + Alias string } -func (TaskHostDetail) TableName() string { - return TablePrefix + "task_host" +func (TaskHostDetail) TableName() string { + return TablePrefix + "task_host" } func hostTableName() []string { - return []string{TablePrefix + "host", "h"} + return []string{TablePrefix + "host", "h"} } func (th *TaskHost) Remove(taskId int) error { - _, err := Db.Where("task_id = ?", taskId).Delete(new(TaskHost)) + _, err := Db.Where("task_id = ?", taskId).Delete(new(TaskHost)) - return err + return err } func (th *TaskHost) Add(taskId int, hostIds []int) error { - err := th.Remove(taskId) - if err != nil { - return err - } + err := th.Remove(taskId) + if err != nil { + return err + } - taskHosts := make([]TaskHost, len(hostIds)) - for i, value := range hostIds { - taskHosts[i].TaskId = taskId - taskHosts[i].HostId = int16(value) - } + taskHosts := make([]TaskHost, len(hostIds)) + for i, value := range hostIds { + taskHosts[i].TaskId = taskId + taskHosts[i].HostId = int16(value) + } - _, err = Db.Insert(&taskHosts) + _, err = Db.Insert(&taskHosts) - return err + return err } func (th *TaskHost) GetHostIdsByTaskId(taskId int) ([]TaskHostDetail, error) { - list := make([]TaskHostDetail, 0) - fields := "th.id,th.host_id,h.alias,h.name,h.port" - err := Db.Alias("th"). - Join("LEFT", hostTableName(), "th.host_id=h.id"). - Where("th.task_id = ?", taskId). - Cols(fields). - Find(&list) + list := make([]TaskHostDetail, 0) + fields := "th.id,th.host_id,h.alias,h.name,h.port" + err := Db.Alias("th"). + Join("LEFT", hostTableName(), "th.host_id=h.id"). + Where("th.task_id = ?", taskId). + Cols(fields). + Find(&list) - return list, err + return list, err } -func (th *TaskHost) GetTaskIdsByHostId(hostId int16) ([]interface{}, error) { - list := make([]TaskHost, 0) - err := Db.Where("host_id = ?", hostId).Cols("task_id").Find(&list) - if err != nil { - return nil, err - } +func (th *TaskHost) GetTaskIdsByHostId(hostId int16) ([]interface{}, error) { + list := make([]TaskHost, 0) + err := Db.Where("host_id = ?", hostId).Cols("task_id").Find(&list) + if err != nil { + return nil, err + } - taskIds := make([]interface{}, len(list)) - for i, value := range list { - taskIds[i] = value.TaskId - } + taskIds := make([]interface{}, len(list)) + for i, value := range list { + taskIds[i] = value.TaskId + } - return taskIds, err + return taskIds, err } // 判断主机id是否有引用 func (th *TaskHost) HostIdExist(hostId int16) (bool, error) { - count, err := Db.Where("host_id = ?", hostId).Count(th); + count, err := Db.Where("host_id = ?", hostId).Count(th) - return count > 0, err -} \ No newline at end of file + return count > 0, err +} diff --git a/models/task_log.go b/models/task_log.go index 47db18c..e38c059 100644 --- a/models/task_log.go +++ b/models/task_log.go @@ -1,99 +1,98 @@ package models import ( - "time" - "github.com/go-xorm/xorm" + "github.com/go-xorm/xorm" + "time" ) type TaskType int8 - // 任务执行日志 type TaskLog struct { - Id int64 `xorm:"bigint pk autoincr"` - TaskId int `xorm:"int notnull index default 0"` // 任务id - Name string `xorm:"varchar(32) notnull"` // 任务名称 - Spec string `xorm:"varchar(64) notnull"` // crontab - Protocol TaskProtocol `xorm:"tinyint notnull index"` // 协议 1:http 2:RPC - Command string `xorm:"varchar(256) notnull"` // URL地址或shell命令 - Timeout int `xorm:"mediumint notnull default 0"` // 任务执行超时时间(单位秒),0不限制 - RetryTimes int8 `xorm:"tinyint notnull default 0"` // 任务重试次数 - Hostname string `xorm:"varchar(128) notnull defalut '' "` // RPC主机名,逗号分隔 - StartTime time.Time `xorm:"datetime created"` // 开始执行时间 - EndTime time.Time `xorm:"datetime updated"` // 执行完成(失败)时间 - Status Status `xorm:"tinyint notnull index default 1"` // 状态 0:执行失败 1:执行中 2:执行完毕 3:任务取消(上次任务未执行完成) 4:异步执行 - Result string `xorm:"mediumtext notnull defalut '' "` // 执行结果 - TotalTime int `xorm:"-"` // 执行总时长 - BaseModel `xorm:"-"` + Id int64 `xorm:"bigint pk autoincr"` + TaskId int `xorm:"int notnull index default 0"` // 任务id + Name string `xorm:"varchar(32) notnull"` // 任务名称 + Spec string `xorm:"varchar(64) notnull"` // crontab + Protocol TaskProtocol `xorm:"tinyint notnull index"` // 协议 1:http 2:RPC + Command string `xorm:"varchar(256) notnull"` // URL地址或shell命令 + Timeout int `xorm:"mediumint notnull default 0"` // 任务执行超时时间(单位秒),0不限制 + RetryTimes int8 `xorm:"tinyint notnull default 0"` // 任务重试次数 + Hostname string `xorm:"varchar(128) notnull defalut '' "` // RPC主机名,逗号分隔 + StartTime time.Time `xorm:"datetime created"` // 开始执行时间 + EndTime time.Time `xorm:"datetime updated"` // 执行完成(失败)时间 + Status Status `xorm:"tinyint notnull index default 1"` // 状态 0:执行失败 1:执行中 2:执行完毕 3:任务取消(上次任务未执行完成) 4:异步执行 + Result string `xorm:"mediumtext notnull defalut '' "` // 执行结果 + TotalTime int `xorm:"-"` // 执行总时长 + BaseModel `xorm:"-"` } func (taskLog *TaskLog) Create() (insertId int64, err error) { - _, err = Db.Insert(taskLog) - if err == nil { - insertId = taskLog.Id - } + _, err = Db.Insert(taskLog) + if err == nil { + insertId = taskLog.Id + } - return + return } // 更新 func (taskLog *TaskLog) Update(id int64, data CommonMap) (int64, error) { - return Db.Table(taskLog).ID(id).Update(data) + return Db.Table(taskLog).ID(id).Update(data) } func (taskLog *TaskLog) List(params CommonMap) ([]TaskLog, error) { - taskLog.parsePageAndPageSize(params) - list := make([]TaskLog, 0) - session := Db.Desc("id") - taskLog.parseWhere(session, params) - err := session.Limit(taskLog.PageSize, taskLog.pageLimitOffset()).Find(&list) - if len(list) > 0 { - for i, item := range list { - endTime := item.EndTime - if item.Status == Running { - endTime = time.Now() - } - execSeconds := endTime.Sub(item.StartTime).Seconds() - list[i].TotalTime = int(execSeconds) - } - } + taskLog.parsePageAndPageSize(params) + list := make([]TaskLog, 0) + session := Db.Desc("id") + taskLog.parseWhere(session, params) + err := session.Limit(taskLog.PageSize, taskLog.pageLimitOffset()).Find(&list) + if len(list) > 0 { + for i, item := range list { + endTime := item.EndTime + if item.Status == Running { + endTime = time.Now() + } + execSeconds := endTime.Sub(item.StartTime).Seconds() + list[i].TotalTime = int(execSeconds) + } + } - return list, err + return list, err } // 清空表 -func (taskLog *TaskLog) Clear() (int64, error) { - return Db.Where("1=1").Delete(taskLog); +func (taskLog *TaskLog) Clear() (int64, error) { + return Db.Where("1=1").Delete(taskLog) } // 删除N个月前的日志 func (taskLog *TaskLog) Remove(id int) (int64, error) { - t := time.Now().AddDate(0, -id, 0) - return Db.Where("start_time <= ?", t.Format(DefaultTimeFormat)).Delete(taskLog) + t := time.Now().AddDate(0, -id, 0) + return Db.Where("start_time <= ?", t.Format(DefaultTimeFormat)).Delete(taskLog) } func (taskLog *TaskLog) Total(params CommonMap) (int64, error) { - session := Db.NewSession() - defer session.Close() - taskLog.parseWhere(session, params) - return session.Count(taskLog) + session := Db.NewSession() + defer session.Close() + taskLog.parseWhere(session, params) + return session.Count(taskLog) } // 解析where -func (taskLog *TaskLog) parseWhere(session *xorm.Session, params CommonMap) { - if len(params) == 0 { - return - } - taskId, ok := params["TaskId"] - if ok && taskId.(int) > 0 { - session.And("task_id = ?", taskId) - } - protocol, ok := params["Protocol"] - if ok && protocol.(int) > 0 { - session.And("protocol = ?", protocol) - } - status, ok := params["Status"] - if ok && status.(int) > -1 { - session.And("status = ?", status) - } -} \ No newline at end of file +func (taskLog *TaskLog) parseWhere(session *xorm.Session, params CommonMap) { + if len(params) == 0 { + return + } + taskId, ok := params["TaskId"] + if ok && taskId.(int) > 0 { + session.And("task_id = ?", taskId) + } + protocol, ok := params["Protocol"] + if ok && protocol.(int) > 0 { + session.And("protocol = ?", protocol) + } + status, ok := params["Status"] + if ok && status.(int) > -1 { + session.And("status = ?", status) + } +} diff --git a/models/user.go b/models/user.go index 64dd24a..00f8703 100644 --- a/models/user.go +++ b/models/user.go @@ -1,110 +1,110 @@ package models import ( - "github.com/ouqiang/gocron/modules/utils" - "time" + "github.com/ouqiang/gocron/modules/utils" + "time" ) const PasswordSaltLength = 6 // 用户model type User struct { - Id int `xorm:"pk autoincr notnull "` - Name string `xorm:"varchar(32) notnull unique"` // 用户名 - Password string `xorm:"char(32) notnull "` // 密码 - Salt string `xorm:"char(6) notnull "` // 密码盐值 - Email string `xorm:"varchar(50) notnull unique default '' "` // 邮箱 - Created time.Time `xorm:"datetime notnull created"` - Updated time.Time `xorm:"datetime updated"` - Deleted time.Time `xorm:"datetime deleted"` - IsAdmin int8 `xorm:"tinyint notnull default 0"` // 是否是管理员 1:管理员 0:普通用户 - Status Status `xorm:"tinyint notnull default 1"` // 1: 正常 0:禁用 - BaseModel `xorm:"-"` + Id int `xorm:"pk autoincr notnull "` + Name string `xorm:"varchar(32) notnull unique"` // 用户名 + Password string `xorm:"char(32) notnull "` // 密码 + Salt string `xorm:"char(6) notnull "` // 密码盐值 + Email string `xorm:"varchar(50) notnull unique default '' "` // 邮箱 + Created time.Time `xorm:"datetime notnull created"` + Updated time.Time `xorm:"datetime updated"` + Deleted time.Time `xorm:"datetime deleted"` + IsAdmin int8 `xorm:"tinyint notnull default 0"` // 是否是管理员 1:管理员 0:普通用户 + Status Status `xorm:"tinyint notnull default 1"` // 1: 正常 0:禁用 + BaseModel `xorm:"-"` } // 新增 func (user *User) Create() (insertId int, err error) { - user.Status = Enabled - user.Salt = user.generateSalt() - user.Password = user.encryptPassword(user.Password, user.Salt) + user.Status = Enabled + user.Salt = user.generateSalt() + user.Password = user.encryptPassword(user.Password, user.Salt) - _, err = Db.Insert(user) - if err == nil { - insertId = user.Id - } + _, err = Db.Insert(user) + if err == nil { + insertId = user.Id + } - return + return } // 更新 func (user *User) Update(id int, data CommonMap) (int64, error) { - return Db.Table(user).ID(id).Update(data) + return Db.Table(user).ID(id).Update(data) } -func (user *User) UpdatePassword(id int, password string) (int64, error) { - salt := user.generateSalt() - safePassword := user.encryptPassword(password, salt) +func (user *User) UpdatePassword(id int, password string) (int64, error) { + salt := user.generateSalt() + safePassword := user.encryptPassword(password, salt) - return user.Update(id, CommonMap{"password": safePassword, "salt": salt}) + return user.Update(id, CommonMap{"password": safePassword, "salt": salt}) } // 删除 func (user *User) Delete(id int) (int64, error) { - return Db.Id(id).Delete(user) + return Db.Id(id).Delete(user) } // 禁用 func (user *User) Disable(id int) (int64, error) { - return user.Update(id, CommonMap{"status": Disabled}) + return user.Update(id, CommonMap{"status": Disabled}) } // 激活 func (user *User) Enable(id int) (int64, error) { - return user.Update(id, CommonMap{"status": Enabled}) + return user.Update(id, CommonMap{"status": Enabled}) } // 验证用户名和密码 func (user *User) Match(username, password string) bool { - where := "(name = ? OR email = ?)" - _, err := Db.Where(where, username, username).Get(user) - if err != nil { - return false - } - hashPassword := user.encryptPassword(password, user.Salt) - if hashPassword != user.Password { - return false - } + where := "(name = ? OR email = ?)" + _, err := Db.Where(where, username, username).Get(user) + if err != nil { + return false + } + hashPassword := user.encryptPassword(password, user.Salt) + if hashPassword != user.Password { + return false + } - return true + return true } // 用户名是否存在 func (user *User) UsernameExists(username string) (int64, error) { - return Db.Where("name = ?", username).Count(user) + return Db.Where("name = ?", username).Count(user) } // 邮箱地址是否存在 func (user *User) EmailExists(email string) (int64, error) { - return Db.Where("email = ?", email).Count(user) + return Db.Where("email = ?", email).Count(user) } func (user *User) List() ([]User, error) { - list := make([]User, 0) - err := Db.Desc("id").Find(&list) + list := make([]User, 0) + err := Db.Desc("id").Find(&list) - return list, err + return list, err } func (user *User) Total() (int64, error) { - return Db.Count(user) + return Db.Count(user) } // 密码加密 func (user *User) encryptPassword(password, salt string) string { - return utils.Md5(password + salt) + return utils.Md5(password + salt) } // 生成密码盐值 func (user *User) generateSalt() string { - return utils.RandString(PasswordSaltLength) + return utils.RandString(PasswordSaltLength) } diff --git a/modules/app/app.go b/modules/app/app.go index f1df384..1af491a 100644 --- a/modules/app/app.go +++ b/modules/app/app.go @@ -1,117 +1,116 @@ package app import ( - "os" + "os" - "github.com/ouqiang/gocron/modules/logger" - "github.com/ouqiang/gocron/modules/utils" - "io/ioutil" - "strconv" - "strings" - "github.com/ouqiang/gocron/modules/setting" + "github.com/ouqiang/gocron/modules/logger" + "github.com/ouqiang/gocron/modules/setting" + "github.com/ouqiang/gocron/modules/utils" + "io/ioutil" + "strconv" + "strings" ) var ( - AppDir string // 应用根目录 - ConfDir string // 配置目录 - LogDir string // 日志目录 - DataDir string // 存放session等 - AppConfig string // 应用配置文件 - Installed bool // 应用是否安装过 - Setting *setting.Setting // 应用配置 - VersionId int // 版本号 - VersionFile string // 版本号文件 + AppDir string // 应用根目录 + ConfDir string // 配置目录 + LogDir string // 日志目录 + DataDir string // 存放session等 + AppConfig string // 应用配置文件 + Installed bool // 应用是否安装过 + Setting *setting.Setting // 应用配置 + VersionId int // 版本号 + VersionFile string // 版本号文件 ) - func InitEnv(versionString string) { - logger.InitLogger() - wd, err := os.Getwd() - if err != nil { - logger.Fatal(err) - } - AppDir = wd - ConfDir = AppDir + "/conf" - LogDir = AppDir + "/log" - DataDir = AppDir + "/data" - AppConfig = ConfDir + "/app.ini" - VersionFile = ConfDir + "/.version" - checkDirExists(ConfDir, LogDir, DataDir) - Installed = IsInstalled() - VersionId = ToNumberVersion(versionString) + logger.InitLogger() + wd, err := os.Getwd() + if err != nil { + logger.Fatal(err) + } + AppDir = wd + ConfDir = AppDir + "/conf" + LogDir = AppDir + "/log" + DataDir = AppDir + "/data" + AppConfig = ConfDir + "/app.ini" + VersionFile = ConfDir + "/.version" + checkDirExists(ConfDir, LogDir, DataDir) + Installed = IsInstalled() + VersionId = ToNumberVersion(versionString) } // 判断应用是否已安装 func IsInstalled() bool { - _, err := os.Stat(ConfDir + "/install.lock") - if os.IsNotExist(err) { - return false - } + _, err := os.Stat(ConfDir + "/install.lock") + if os.IsNotExist(err) { + return false + } - return true + return true } // 创建安装锁文件 func CreateInstallLock() error { - _, err := os.Create(ConfDir + "/install.lock") - if err != nil { - logger.Error("创建安装锁文件conf/install.lock失败") - } + _, err := os.Create(ConfDir + "/install.lock") + if err != nil { + logger.Error("创建安装锁文件conf/install.lock失败") + } - return err + return err } // 更新应用版本号文件 -func UpdateVersionFile() { - err := ioutil.WriteFile(VersionFile, - []byte(strconv.Itoa(VersionId)), - 0644, - ) +func UpdateVersionFile() { + err := ioutil.WriteFile(VersionFile, + []byte(strconv.Itoa(VersionId)), + 0644, + ) - if err != nil { - logger.Fatal(err) - } + if err != nil { + logger.Fatal(err) + } } // 获取应用当前版本号, 从版本号文件中读取 func GetCurrentVersionId() int { - if !utils.FileExist(VersionFile) { - return 0; - } + if !utils.FileExist(VersionFile) { + return 0 + } - bytes, err := ioutil.ReadFile(VersionFile) - if err != nil { - logger.Fatal(err) - } + bytes, err := ioutil.ReadFile(VersionFile) + if err != nil { + logger.Fatal(err) + } - versionId, err := strconv.Atoi(strings.TrimSpace(string(bytes))) - if err != nil { - logger.Fatal(err) - } + versionId, err := strconv.Atoi(strings.TrimSpace(string(bytes))) + if err != nil { + logger.Fatal(err) + } - return versionId + return versionId } // 把字符串版本号a.b.c转换为整数版本号abc -func ToNumberVersion(versionString string) int { - v := strings.Replace(versionString, ".", "", -1) - if len(v) < 3 { - v += "0" - } +func ToNumberVersion(versionString string) int { + v := strings.Replace(versionString, ".", "", -1) + if len(v) < 3 { + v += "0" + } - versionId, err := strconv.Atoi(v) - if err != nil { - logger.Fatal(err) - } + versionId, err := strconv.Atoi(v) + if err != nil { + logger.Fatal(err) + } - return versionId + return versionId } // 检测目录是否存在 func checkDirExists(path ...string) { - for _, value := range path { - if !utils.FileExist(value) { - logger.Fatal(value + "目录不存在或无权限访问") - } - } -} \ No newline at end of file + for _, value := range path { + if !utils.FileExist(value) { + logger.Fatal(value + "目录不存在或无权限访问") + } + } +} diff --git a/modules/httpclient/http_client.go b/modules/httpclient/http_client.go index 36d5c19..29f4334 100644 --- a/modules/httpclient/http_client.go +++ b/modules/httpclient/http_client.go @@ -3,81 +3,81 @@ package httpclient // http-client import ( - "io/ioutil" - "net/http" - "time" - "fmt" - "bytes" + "bytes" + "fmt" + "io/ioutil" + "net/http" + "time" ) -type ResponseWrapper struct { - StatusCode int - Body string - Header http.Header +type ResponseWrapper struct { + StatusCode int + Body string + Header http.Header } func Get(url string, timeout int) ResponseWrapper { - req, err := http.NewRequest("GET", url, nil) - if err != nil { - return createRequestError(err) - } + req, err := http.NewRequest("GET", url, nil) + if err != nil { + return createRequestError(err) + } - return request(req, timeout) + return request(req, timeout) } -func PostParams(url string,params string, timeout int) ResponseWrapper { - buf := bytes.NewBufferString(params) - req, err := http.NewRequest("POST", url, buf) - if err != nil { - return createRequestError(err) - } - req.Header.Set("Content-type", "application/x-www-form-urlencoded") +func PostParams(url string, params string, timeout int) ResponseWrapper { + buf := bytes.NewBufferString(params) + req, err := http.NewRequest("POST", url, buf) + if err != nil { + return createRequestError(err) + } + req.Header.Set("Content-type", "application/x-www-form-urlencoded") - return request(req, timeout) + return request(req, timeout) } func PostJson(url string, body string, timeout int) ResponseWrapper { - buf := bytes.NewBufferString(body) - req, err := http.NewRequest("POST", url, buf) - if err != nil { - return createRequestError(err) - } - req.Header.Set("Content-type", "application/json") + buf := bytes.NewBufferString(body) + req, err := http.NewRequest("POST", url, buf) + if err != nil { + return createRequestError(err) + } + req.Header.Set("Content-type", "application/json") - return request(req, timeout) + return request(req, timeout) } func request(req *http.Request, timeout int) ResponseWrapper { - wrapper := ResponseWrapper{StatusCode: 0, Body: "", Header: make(http.Header)} - client := &http.Client{} - if timeout > 0 { - client.Timeout = time.Duration(timeout) * time.Second - } - setRequestHeader(req) - resp, err := client.Do(req) - if err != nil { - wrapper.Body = fmt.Sprintf("执行HTTP请求错误-%s", err.Error()) - return wrapper - } - defer resp.Body.Close() - body, err := ioutil.ReadAll(resp.Body) - if err != nil { - wrapper.Body = fmt.Sprintf("读取HTTP请求返回值失败-%s", err.Error()) - return wrapper - } - wrapper.StatusCode = resp.StatusCode - wrapper.Body = string(body) - wrapper.Header = resp.Header + wrapper := ResponseWrapper{StatusCode: 0, Body: "", Header: make(http.Header)} + client := &http.Client{} + if timeout > 0 { + client.Timeout = time.Duration(timeout) * time.Second + } + setRequestHeader(req) + resp, err := client.Do(req) + if err != nil { + wrapper.Body = fmt.Sprintf("执行HTTP请求错误-%s", err.Error()) + return wrapper + } + defer resp.Body.Close() + body, err := ioutil.ReadAll(resp.Body) + if err != nil { + wrapper.Body = fmt.Sprintf("读取HTTP请求返回值失败-%s", err.Error()) + return wrapper + } + wrapper.StatusCode = resp.StatusCode + wrapper.Body = string(body) + wrapper.Header = resp.Header - return wrapper + return wrapper } -func setRequestHeader(req *http.Request) { - req.Header.Set("Accept-Language", "zh-CN,zh;q=0.8,en;q=0.6") - req.Header.Set("User-Agent", "Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/57.0.2987.133 Safari/537.36 golang/gocron") +func setRequestHeader(req *http.Request) { + req.Header.Set("Accept-Language", "zh-CN,zh;q=0.8,en;q=0.6") + req.Header.Set("User-Agent", "Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/57.0.2987.133 Safari/537.36 golang/gocron") } func createRequestError(err error) ResponseWrapper { - errorMessage := fmt.Sprintf("创建HTTP请求错误-%s", err.Error()) - return ResponseWrapper{0, errorMessage, make(http.Header)} -} \ No newline at end of file + errorMessage := fmt.Sprintf("创建HTTP请求错误-%s", err.Error()) + return ResponseWrapper{0, errorMessage, make(http.Header)} +} diff --git a/modules/logger/logger.go b/modules/logger/logger.go index 04398b3..cf76940 100644 --- a/modules/logger/logger.go +++ b/modules/logger/logger.go @@ -1,11 +1,11 @@ package logger import ( - "github.com/cihub/seelog" - "gopkg.in/macaron.v1" - "fmt" - "os" - "runtime" + "fmt" + "github.com/cihub/seelog" + "gopkg.in/macaron.v1" + "os" + "runtime" ) // 日志库 @@ -22,113 +22,113 @@ const ( FATAL ) -func InitLogger() { - config := getLogConfig() - l, err := seelog.LoggerFromConfigAsString(config) - if err != nil { - panic(err) - } - logger = l +func InitLogger() { + config := getLogConfig() + l, err := seelog.LoggerFromConfigAsString(config) + if err != nil { + panic(err) + } + logger = l } func Debug(v ...interface{}) { - if macaron.Env != macaron.DEV { - return - } + if macaron.Env != macaron.DEV { + return + } write(DEBUG, v) } -func Debugf(format string, v ...interface{}) { - if macaron.Env != macaron.DEV { - return - } - writef(DEBUG, format, v...) +func Debugf(format string, v ...interface{}) { + if macaron.Env != macaron.DEV { + return + } + writef(DEBUG, format, v...) } func Info(v ...interface{}) { write(INFO, v) } -func Infof(format string, v ...interface{}) { - writef(INFO, format, v...) +func Infof(format string, v ...interface{}) { + writef(INFO, format, v...) } func Warn(v ...interface{}) { write(WARN, v) } -func Warnf(format string, v ...interface{}) { - writef(WARN, format, v...) +func Warnf(format string, v ...interface{}) { + writef(WARN, format, v...) } func Error(v ...interface{}) { write(ERROR, v) } -func Errorf(format string, v ...interface{}) { - writef(ERROR, format, v...) +func Errorf(format string, v ...interface{}) { + writef(ERROR, format, v...) } func Fatal(v ...interface{}) { write(FATAL, v) } -func Fatalf(format string, v ...interface{}) { - writef(FATAL, format, v...) +func Fatalf(format string, v ...interface{}) { + writef(FATAL, format, v...) } func write(level Level, v ...interface{}) { - defer logger.Flush() + defer logger.Flush() - content := "" - if macaron.Env == macaron.DEV { - pc, file, line, ok := runtime.Caller(2) - if ok { - content = fmt.Sprintf("#%s#%s#%d行#", file, runtime.FuncForPC(pc).Name(), line) - } - } + content := "" + if macaron.Env == macaron.DEV { + pc, file, line, ok := runtime.Caller(2) + if ok { + content = fmt.Sprintf("#%s#%s#%d行#", file, runtime.FuncForPC(pc).Name(), line) + } + } - switch level { - case DEBUG: - logger.Debug(content, v) - case INFO: - logger.Info(content, v) - case WARN: - logger.Warn(content, v) - case FATAL: - logger.Critical(content, v) - os.Exit(1) - case ERROR: - logger.Error(content, v) + switch level { + case DEBUG: + logger.Debug(content, v) + case INFO: + logger.Info(content, v) + case WARN: + logger.Warn(content, v) + case FATAL: + logger.Critical(content, v) + os.Exit(1) + case ERROR: + logger.Error(content, v) } } -func writef(level Level, format string, v ...interface{}) { - defer logger.Flush() +func writef(level Level, format string, v ...interface{}) { + defer logger.Flush() - content := "" - if macaron.Env == macaron.DEV { - pc, file, line, ok := runtime.Caller(2) - if ok { - content = fmt.Sprintf("#%s#%s#%d行#", file, runtime.FuncForPC(pc).Name(), line) - } - } + content := "" + if macaron.Env == macaron.DEV { + pc, file, line, ok := runtime.Caller(2) + if ok { + content = fmt.Sprintf("#%s#%s#%d行#", file, runtime.FuncForPC(pc).Name(), line) + } + } - format = content + format + format = content + format - switch level { - case DEBUG: - logger.Debugf(format, v...) - case INFO: - logger.Infof(format, v...) - case WARN: - logger.Warnf(format, v...) - case FATAL: - logger.Criticalf(format, v...) - os.Exit(1) - case ERROR: - logger.Errorf(format, v...) - } + switch level { + case DEBUG: + logger.Debugf(format, v...) + case INFO: + logger.Infof(format, v...) + case WARN: + logger.Warnf(format, v...) + case FATAL: + logger.Criticalf(format, v...) + os.Exit(1) + case ERROR: + logger.Errorf(format, v...) + } } func getLogConfig() string { @@ -145,16 +145,16 @@ func getLogConfig() string { ` - consoleConfig := "" - if macaron.Env == macaron.DEV { - consoleConfig = - ` + consoleConfig := "" + if macaron.Env == macaron.DEV { + consoleConfig = + ` ` - } - config = fmt.Sprintf(config, consoleConfig) + } + config = fmt.Sprintf(config, consoleConfig) - return config + return config } diff --git a/modules/notify/mail.go b/modules/notify/mail.go index f9ba373..aa3edd0 100644 --- a/modules/notify/mail.go +++ b/modules/notify/mail.go @@ -1,84 +1,82 @@ package notify import ( - "github.com/ouqiang/gocron/models" - "github.com/ouqiang/gocron/modules/logger" - "strconv" - "strings" - "github.com/ouqiang/gocron/modules/utils" - "time" - "github.com/go-gomail/gomail" + "github.com/go-gomail/gomail" + "github.com/ouqiang/gocron/models" + "github.com/ouqiang/gocron/modules/logger" + "github.com/ouqiang/gocron/modules/utils" + "strconv" + "strings" + "time" ) // @author qiang.ou // @date 2017/5/1-00:19 - type Mail struct { - } -func (mail *Mail) Send(msg Message) { - model := new(models.Setting) - mailSetting, err := model.Mail() - logger.Debugf("%+v", mailSetting) - if err != nil { - logger.Error("#mail#从数据库获取mail配置失败", err) - return - } - if mailSetting.Host == "" { - logger.Error("#mail#Host为空") - return - } - if mailSetting.Port == 0 { - logger.Error("#mail#Port为空") - return - } - if mailSetting.User == "" { - logger.Error("#mail#User为空") - return - } - if mailSetting.Password == "" { - logger.Error("#mail#Password为空") - return - } - toUsers := mail.getActiveMailUsers(mailSetting, msg) - mail.send(mailSetting, toUsers, msg) +func (mail *Mail) Send(msg Message) { + model := new(models.Setting) + mailSetting, err := model.Mail() + logger.Debugf("%+v", mailSetting) + if err != nil { + logger.Error("#mail#从数据库获取mail配置失败", err) + return + } + if mailSetting.Host == "" { + logger.Error("#mail#Host为空") + return + } + if mailSetting.Port == 0 { + logger.Error("#mail#Port为空") + return + } + if mailSetting.User == "" { + logger.Error("#mail#User为空") + return + } + if mailSetting.Password == "" { + logger.Error("#mail#Password为空") + return + } + toUsers := mail.getActiveMailUsers(mailSetting, msg) + mail.send(mailSetting, toUsers, msg) } -func (mail *Mail) send(mailSetting models.Mail, toUsers []string, msg Message) { - body := msg["content"].(string) - body = strings.Replace(body, "\n", "
", -1) - gomailMessage := gomail.NewMessage() - gomailMessage.SetHeader("From", mailSetting.User) - gomailMessage.SetHeader("To", toUsers...) - gomailMessage.SetHeader("Subject", "gocron-定时任务监控通知") - gomailMessage.SetBody("text/html", body) - mailer := gomail.NewPlainDialer(mailSetting.Host, mailSetting.Port, - mailSetting.User, mailSetting.Password) - maxTimes := 3 - i := 0 - for i < maxTimes { - err := mailer.DialAndSend(gomailMessage) - if err == nil { - break; - } - i += 1 - time.Sleep(2 * time.Second) - if i < maxTimes { - logger.Errorf("mail#发送消息失败#%s#消息内容-%s", err.Error(), msg["content"]) - } - } +func (mail *Mail) send(mailSetting models.Mail, toUsers []string, msg Message) { + body := msg["content"].(string) + body = strings.Replace(body, "\n", "
", -1) + gomailMessage := gomail.NewMessage() + gomailMessage.SetHeader("From", mailSetting.User) + gomailMessage.SetHeader("To", toUsers...) + gomailMessage.SetHeader("Subject", "gocron-定时任务监控通知") + gomailMessage.SetBody("text/html", body) + mailer := gomail.NewPlainDialer(mailSetting.Host, mailSetting.Port, + mailSetting.User, mailSetting.Password) + maxTimes := 3 + i := 0 + for i < maxTimes { + err := mailer.DialAndSend(gomailMessage) + if err == nil { + break + } + i += 1 + time.Sleep(2 * time.Second) + if i < maxTimes { + logger.Errorf("mail#发送消息失败#%s#消息内容-%s", err.Error(), msg["content"]) + } + } } -func (mail *Mail) getActiveMailUsers(mailSetting models.Mail, msg Message) []string { - taskReceiverIds := strings.Split(msg["task_receiver_id"].(string), ",") - users := []string{} - for _, v := range(mailSetting.MailUsers) { - if utils.InStringSlice(taskReceiverIds, strconv.Itoa(v.Id)) { - users = append(users, v.Email) - } - } +func (mail *Mail) getActiveMailUsers(mailSetting models.Mail, msg Message) []string { + taskReceiverIds := strings.Split(msg["task_receiver_id"].(string), ",") + users := []string{} + for _, v := range mailSetting.MailUsers { + if utils.InStringSlice(taskReceiverIds, strconv.Itoa(v.Id)) { + users = append(users, v.Email) + } + } - return users -} \ No newline at end of file + return users +} diff --git a/modules/notify/notify.go b/modules/notify/notify.go index 00714f6..1e34ddf 100644 --- a/modules/notify/notify.go +++ b/modules/notify/notify.go @@ -1,52 +1,52 @@ package notify import ( - "time" - "github.com/ouqiang/gocron/modules/logger" - "fmt" + "fmt" + "github.com/ouqiang/gocron/modules/logger" + "time" ) type Message map[string]interface{} type Notifiable interface { - Send(msg Message) + Send(msg Message) } var queue chan Message = make(chan Message, 100) -func init() { - go run() +func init() { + go run() } // 把消息推入队列 func Push(msg Message) { - queue <- msg + queue <- msg } func run() { - for msg := range queue { - // 根据任务配置发送通知 - taskType, taskTypeOk := msg["task_type"] - _, taskReceiverIdOk := msg["task_receiver_id"] - _, nameOk := msg["name"] - _, outputOk := msg["output"] - _, statusOk := msg["status"] - if !taskTypeOk || !taskReceiverIdOk || !nameOk || !outputOk || !statusOk { - logger.Errorf("#notify#参数不完整#%+v", msg) - continue - } - msg["content"] = fmt.Sprintf("============\n============\n============\n任务名称: %s\n状态: %s\n输出:\n %s\n", msg["name"], msg["status"], msg["output"]) - logger.Debugf("%+v", msg) - switch(taskType.(int8)) { - case 1: - // 邮件 - mail := Mail{} - go mail.Send(msg) - case 2: - // Slack - slack := Slack{} - go slack.Send(msg) - } - time.Sleep(1 * time.Second) - } -} \ No newline at end of file + for msg := range queue { + // 根据任务配置发送通知 + taskType, taskTypeOk := msg["task_type"] + _, taskReceiverIdOk := msg["task_receiver_id"] + _, nameOk := msg["name"] + _, outputOk := msg["output"] + _, statusOk := msg["status"] + if !taskTypeOk || !taskReceiverIdOk || !nameOk || !outputOk || !statusOk { + logger.Errorf("#notify#参数不完整#%+v", msg) + continue + } + msg["content"] = fmt.Sprintf("============\n============\n============\n任务名称: %s\n状态: %s\n输出:\n %s\n", msg["name"], msg["status"], msg["output"]) + logger.Debugf("%+v", msg) + switch taskType.(int8) { + case 1: + // 邮件 + mail := Mail{} + go mail.Send(msg) + case 2: + // Slack + slack := Slack{} + go slack.Send(msg) + } + time.Sleep(1 * time.Second) + } +} diff --git a/modules/notify/slack.go b/modules/notify/slack.go index 6c5f8e8..d64d570 100644 --- a/modules/notify/slack.go +++ b/modules/notify/slack.go @@ -1,78 +1,79 @@ package notify + // 发送消息到slack import ( - "fmt" - "github.com/ouqiang/gocron/modules/httpclient" - "github.com/ouqiang/gocron/modules/logger" - "github.com/ouqiang/gocron/modules/utils" - "strings" - "github.com/ouqiang/gocron/models" - "strconv" - "time" + "fmt" + "github.com/ouqiang/gocron/models" + "github.com/ouqiang/gocron/modules/httpclient" + "github.com/ouqiang/gocron/modules/logger" + "github.com/ouqiang/gocron/modules/utils" + "strconv" + "strings" + "time" ) -type Slack struct {} +type Slack struct{} -func (slack *Slack) Send(msg Message) { - model := new(models.Setting) - slackSetting, err := model.Slack() - if err != nil { - logger.Error("#slack#从数据库获取slack配置失败", err) - return - } - if slackSetting.Url == "" { - logger.Error("#slack#webhook-url为空") - return - } - if len(slackSetting.Channels) == 0 { - logger.Error("#slack#channels配置为空") - return - } - logger.Debugf("%+v", slackSetting) - channels := slack.getActiveSlackChannels(slackSetting, msg) - logger.Debugf("%+v", channels) - for _, channel := range(channels) { - slack.send(msg, slackSetting.Url, channel) - } +func (slack *Slack) Send(msg Message) { + model := new(models.Setting) + slackSetting, err := model.Slack() + if err != nil { + logger.Error("#slack#从数据库获取slack配置失败", err) + return + } + if slackSetting.Url == "" { + logger.Error("#slack#webhook-url为空") + return + } + if len(slackSetting.Channels) == 0 { + logger.Error("#slack#channels配置为空") + return + } + logger.Debugf("%+v", slackSetting) + channels := slack.getActiveSlackChannels(slackSetting, msg) + logger.Debugf("%+v", channels) + for _, channel := range channels { + slack.send(msg, slackSetting.Url, channel) + } } -func (slack *Slack) send(msg Message, slackUrl string, channel string) { - formatBody := slack.format(msg["content"].(string), channel) - timeout := 30 - maxTimes := 3 - i := 0 - for i < maxTimes { - resp := httpclient.PostJson(slackUrl, formatBody, timeout) - if resp.StatusCode == 200 { - break; - } - i += 1 - time.Sleep(2 * time.Second) - if i < maxTimes { - logger.Errorf("slack#发送消息失败#%s#消息内容-%s", resp.Body, msg["content"]) - } - } +func (slack *Slack) send(msg Message, slackUrl string, channel string) { + formatBody := slack.format(msg["content"].(string), channel) + timeout := 30 + maxTimes := 3 + i := 0 + for i < maxTimes { + resp := httpclient.PostJson(slackUrl, formatBody, timeout) + if resp.StatusCode == 200 { + break + } + i += 1 + time.Sleep(2 * time.Second) + if i < maxTimes { + logger.Errorf("slack#发送消息失败#%s#消息内容-%s", resp.Body, msg["content"]) + } + } } -func (slack *Slack) getActiveSlackChannels(slackSetting models.Slack, msg Message) []string { - taskReceiverIds := strings.Split(msg["task_receiver_id"].(string), ",") - channels := []string{} - for _, v := range(slackSetting.Channels) { - if utils.InStringSlice(taskReceiverIds, strconv.Itoa(v.Id)) { - channels = append(channels, v.Name) - } - } +func (slack *Slack) getActiveSlackChannels(slackSetting models.Slack, msg Message) []string { + taskReceiverIds := strings.Split(msg["task_receiver_id"].(string), ",") + channels := []string{} + for _, v := range slackSetting.Channels { + if utils.InStringSlice(taskReceiverIds, strconv.Itoa(v.Id)) { + channels = append(channels, v.Name) + } + } - return channels + return channels } // 格式化消息内容 -func (slack *Slack) format(content string, channel string) string { - content = utils.EscapeJson(content) - specialChars := []string{"&", "<", ">"} - replaceChars := []string{"&", "<", ">"} - content = utils.ReplaceStrings(content, specialChars, replaceChars) +func (slack *Slack) format(content string, channel string) string { + content = utils.EscapeJson(content) + specialChars := []string{"&", "<", ">"} + replaceChars := []string{"&", "<", ">"} + content = utils.ReplaceStrings(content, specialChars, replaceChars) - return fmt.Sprintf(`{"text":"%s","username":"监控", "channel":"%s"}`, content, channel) -} \ No newline at end of file + return fmt.Sprintf(`{"text":"%s","username":"监控", "channel":"%s"}`, content, channel) +} diff --git a/modules/rpc/auth/Certification.go b/modules/rpc/auth/Certification.go index 767e109..7e41853 100644 --- a/modules/rpc/auth/Certification.go +++ b/modules/rpc/auth/Certification.go @@ -3,16 +3,16 @@ package auth import ( "crypto/tls" "crypto/x509" - "io/ioutil" "errors" "fmt" "google.golang.org/grpc/credentials" + "io/ioutil" ) type Certificate struct { - CAFile string - CertFile string - KeyFile string + CAFile string + CertFile string + KeyFile string ServerName string } @@ -33,14 +33,12 @@ func (c Certificate) GetTLSConfigForServer() (*tls.Config, error) { return nil, errors.New("failed to append client certs") } - tlsConfig := &tls.Config{ ClientAuth: tls.RequireAndVerifyClientCert, Certificates: []tls.Certificate{certificate}, ClientCAs: certPool, } - return tlsConfig, nil } @@ -68,4 +66,4 @@ func (c Certificate) GetTransportCredsForClient() (credentials.TransportCredenti }) return transportCreds, nil -} \ No newline at end of file +} diff --git a/modules/rpc/client/client.go b/modules/rpc/client/client.go index 937459d..d6d1b51 100644 --- a/modules/rpc/client/client.go +++ b/modules/rpc/client/client.go @@ -1,80 +1,65 @@ package client import ( - pb "github.com/ouqiang/gocron/modules/rpc/proto" - "golang.org/x/net/context" - "fmt" - "time" - "errors" - "github.com/ouqiang/gocron/modules/rpc/grpcpool" - "google.golang.org/grpc/codes" - "google.golang.org/grpc" - "github.com/ouqiang/gocron/modules/logger" + "errors" + "fmt" + "github.com/ouqiang/gocron/modules/logger" + "github.com/ouqiang/gocron/modules/rpc/grpcpool" + pb "github.com/ouqiang/gocron/modules/rpc/proto" + "golang.org/x/net/context" + "google.golang.org/grpc" + "google.golang.org/grpc/codes" + "time" ) var ( - errUnavailable = errors.New("无法连接远程服务器") + errUnavailable = errors.New("无法连接远程服务器") ) -func ExecWithRetry(ip string, port int, taskReq *pb.TaskRequest) (string, error) { - tryTimes := 60 - i := 0 - for i < tryTimes { - output, err := Exec(ip, port, taskReq) - if err != errUnavailable { - return output, err - } - i++ - time.Sleep(2 * time.Second) - } +func Exec(ip string, port int, taskReq *pb.TaskRequest) (string, error) { + defer func() { + if err := recover(); err != nil { + logger.Error("panic#rpc/client.go:Exec#", err) + } + }() + addr := fmt.Sprintf("%s:%d", ip, port) + conn, err := grpcpool.Pool.Get(addr) + if err != nil { + return "", err + } + isConnClosed := false + defer func() { + if !isConnClosed { + grpcpool.Pool.Put(addr, conn) + } + }() + c := pb.NewTaskClient(conn) + if taskReq.Timeout <= 0 || taskReq.Timeout > 86400 { + taskReq.Timeout = 86400 + } + timeout := time.Duration(taskReq.Timeout) * time.Second + ctx, cancel := context.WithTimeout(context.Background(), timeout) + defer cancel() + resp, err := c.Run(ctx, taskReq) + if err != nil { + return parseGRPCError(err, conn, &isConnClosed) + } - return "", errUnavailable -} + if resp.Error == "" { + return resp.Output, nil + } -func Exec(ip string, port int, taskReq *pb.TaskRequest) (string, error) { - defer func() { - if err := recover(); err != nil { - logger.Error("panic#rpc/client.go:Exec#", err) - } - } () - addr := fmt.Sprintf("%s:%d", ip, port) - conn, err := grpcpool.Pool.Get(addr) - if err != nil { - return "", err - } - isConnClosed := false - defer func() { - if !isConnClosed { - grpcpool.Pool.Put(addr, conn) - } - }() - c := pb.NewTaskClient(conn) - if taskReq.Timeout <= 0 || taskReq.Timeout > 86400 { - taskReq.Timeout = 86400 - } - timeout := time.Duration(taskReq.Timeout) * time.Second - ctx, cancel := context.WithTimeout(context.Background(), timeout) - defer cancel() - resp, err := c.Run(ctx, taskReq) - if err != nil { - return parseGRPCError(err, conn, &isConnClosed) - } - - if resp.Error == "" { - return resp.Output, nil - } - - return resp.Output, errors.New(resp.Error) + return resp.Output, errors.New(resp.Error) } func parseGRPCError(err error, conn *grpc.ClientConn, connClosed *bool) (string, error) { - switch grpc.Code(err) { - case codes.Unavailable, codes.Internal: - conn.Close() - *connClosed = true - return "", errUnavailable - case codes.DeadlineExceeded: - return "", errors.New("执行超时, 强制结束") - } - return "", err + switch grpc.Code(err) { + case codes.Unavailable, codes.Internal: + conn.Close() + *connClosed = true + return "", errUnavailable + case codes.DeadlineExceeded: + return "", errors.New("执行超时, 强制结束") + } + return "", err } diff --git a/modules/rpc/grpcpool/grpc_pool.go b/modules/rpc/grpcpool/grpc_pool.go index d6ce85e..36eec75 100644 --- a/modules/rpc/grpcpool/grpc_pool.go +++ b/modules/rpc/grpcpool/grpc_pool.go @@ -1,141 +1,139 @@ package grpcpool import ( - "github.com/silenceper/pool" - "sync" - "time" - "google.golang.org/grpc" - "errors" - "github.com/ouqiang/gocron/modules/rpc/auth" - "github.com/ouqiang/gocron/modules/app" - "strings" -) - - -var ( - Pool GRPCPool + "errors" + "github.com/ouqiang/gocron/modules/app" + "github.com/ouqiang/gocron/modules/rpc/auth" + "github.com/silenceper/pool" + "google.golang.org/grpc" + "strings" + "sync" + "time" ) var ( - ErrInvalidConn = errors.New("invalid connection") + Pool GRPCPool ) -func init() { - Pool = GRPCPool{ - make(map[string]pool.Pool), - sync.RWMutex{}, - } +var ( + ErrInvalidConn = errors.New("invalid connection") +) + +func init() { + Pool = GRPCPool{ + make(map[string]pool.Pool), + sync.RWMutex{}, + } } type GRPCPool struct { - // map key格式 ip:port - conns map[string]pool.Pool - sync.RWMutex + // map key格式 ip:port + conns map[string]pool.Pool + sync.RWMutex } -func (p *GRPCPool) Get(addr string) (*grpc.ClientConn, error) { - p.RLock() - pool, ok := p.conns[addr] - p.RUnlock() - if !ok { - err := p.newCommonPool(addr) - if err != nil { - return nil, err - } - } +func (p *GRPCPool) Get(addr string) (*grpc.ClientConn, error) { + p.RLock() + pool, ok := p.conns[addr] + p.RUnlock() + if !ok { + err := p.newCommonPool(addr) + if err != nil { + return nil, err + } + } - p.RLock() - pool = p.conns[addr] - p.RUnlock() - conn, err := pool.Get() - if err != nil { - return nil, err - } + p.RLock() + pool = p.conns[addr] + p.RUnlock() + conn, err := pool.Get() + if err != nil { + return nil, err + } - return conn.(*grpc.ClientConn), nil + return conn.(*grpc.ClientConn), nil } func (p *GRPCPool) Put(addr string, conn *grpc.ClientConn) error { - p.RLock() - defer p.RUnlock() - pool, ok := p.conns[addr] - if ok { - return pool.Put(conn) - } + p.RLock() + defer p.RUnlock() + pool, ok := p.conns[addr] + if ok { + return pool.Put(conn) + } - return ErrInvalidConn + return ErrInvalidConn } - // 释放连接池 func (p *GRPCPool) Release(addr string) { - p.Lock() - defer p.Unlock() - pool, ok := p.conns[addr] - if !ok { - return - } - pool.Release() - delete(p.conns, addr) + p.Lock() + defer p.Unlock() + pool, ok := p.conns[addr] + if !ok { + return + } + pool.Release() + delete(p.conns, addr) } // 释放所有连接池 -func (p *GRPCPool) ReleaseAll() { - p.Lock() - defer p.Unlock() - for _, pool := range(p.conns) { - pool.Release() - } +func (p *GRPCPool) ReleaseAll() { + p.Lock() + defer p.Unlock() + for _, pool := range p.conns { + pool.Release() + } } // 初始化底层连接池 -func (p *GRPCPool) newCommonPool(addr string) (error) { - p.Lock() - defer p.Unlock() - commonPool, ok := p.conns[addr] - if ok { - return nil - } - poolConfig := &pool.PoolConfig{ - InitialCap: 1, - MaxCap: 30, - Factory: func() (interface{}, error) { - if !app.Setting.EnableTLS { - return grpc.Dial(addr, grpc.WithInsecure()) - } +func (p *GRPCPool) newCommonPool(addr string) error { + p.Lock() + defer p.Unlock() + commonPool, ok := p.conns[addr] + if ok { + return nil + } + poolConfig := &pool.PoolConfig{ + InitialCap: 1, + MaxCap: 30, + Factory: func() (interface{}, error) { + if !app.Setting.EnableTLS { + return grpc.Dial(addr, grpc.WithInsecure()) + } - server := strings.Split(addr, ":") + server := strings.Split(addr, ":") - certificate := auth.Certificate{ - CAFile: app.Setting.CAFile, - CertFile: app.Setting.CertFile, - KeyFile: app.Setting.KeyFile, - ServerName: server[0], - } + certificate := auth.Certificate{ + CAFile: app.Setting.CAFile, + CertFile: app.Setting.CertFile, + KeyFile: app.Setting.KeyFile, + ServerName: server[0], + } - transportCreds, err := certificate.GetTransportCredsForClient() - if err != nil { - return nil, err - } + transportCreds, err := certificate.GetTransportCredsForClient() + if err != nil { + return nil, err + } - return grpc.Dial(addr, grpc.WithTransportCredentials(transportCreds)) - }, - Close: func(v interface{}) error { - conn, ok := v.(*grpc.ClientConn) - if ok && conn != nil { - return conn.Close() - } - return ErrInvalidConn - }, - IdleTimeout: 3 * time.Minute, - } + return grpc.Dial(addr, grpc.WithTransportCredentials(transportCreds)) + }, + Close: func(v interface{}) error { + conn, ok := v.(*grpc.ClientConn) + if ok && conn != nil { + return conn.Close() + } + return ErrInvalidConn + }, + IdleTimeout: 3 * time.Minute, + } - commonPool, err := pool.NewChannelPool(poolConfig) - if err != nil { - return err - } + commonPool, err := pool.NewChannelPool(poolConfig) + if err != nil { + return err + } - p.conns[addr] = commonPool + p.conns[addr] = commonPool - return nil -} \ No newline at end of file + return nil +} diff --git a/modules/rpc/server/server.go b/modules/rpc/server/server.go index a529a40..1e0ad55 100644 --- a/modules/rpc/server/server.go +++ b/modules/rpc/server/server.go @@ -1,65 +1,64 @@ package server import ( - "golang.org/x/net/context" - "net" - "google.golang.org/grpc/grpclog" - "google.golang.org/grpc" - pb "github.com/ouqiang/gocron/modules/rpc/proto" - "github.com/ouqiang/gocron/modules/utils" - "github.com/ouqiang/gocron/modules/rpc/auth" - "google.golang.org/grpc/credentials" + "github.com/ouqiang/gocron/modules/rpc/auth" + pb "github.com/ouqiang/gocron/modules/rpc/proto" + "github.com/ouqiang/gocron/modules/utils" + "golang.org/x/net/context" + "google.golang.org/grpc" + "google.golang.org/grpc/credentials" + "google.golang.org/grpc/grpclog" + "net" ) -type Server struct {} +type Server struct{} -func (s Server) Run(ctx context.Context, req *pb.TaskRequest) (*pb.TaskResponse, error) { - defer func() { - if err := recover(); err != nil { - grpclog.Println(err) - } - } () - output, err := utils.ExecShell(ctx, req.Command) - resp := new(pb.TaskResponse) - resp.Output = output - if err != nil { - resp.Error = err.Error() - } else { - resp.Error = "" - } +func (s Server) Run(ctx context.Context, req *pb.TaskRequest) (*pb.TaskResponse, error) { + defer func() { + if err := recover(); err != nil { + grpclog.Println(err) + } + }() + output, err := utils.ExecShell(ctx, req.Command) + resp := new(pb.TaskResponse) + resp.Output = output + if err != nil { + resp.Error = err.Error() + } else { + resp.Error = "" + } - return resp, nil + return resp, nil } -func Start(addr string, enableTLS bool, certificate auth.Certificate) { - defer func() { - if err := recover(); err != nil { - grpclog.Println("panic", err) - } - } () +func Start(addr string, enableTLS bool, certificate auth.Certificate) { + defer func() { + if err := recover(); err != nil { + grpclog.Println("panic", err) + } + }() - l, err := net.Listen("tcp", addr) - if err != nil { - grpclog.Fatal(err) - } + l, err := net.Listen("tcp", addr) + if err != nil { + grpclog.Fatal(err) + } - var s *grpc.Server - if enableTLS { - tlsConfig, err := certificate.GetTLSConfigForServer() - if err != nil { - grpclog.Fatal(err) - } - opt := grpc.Creds(credentials.NewTLS(tlsConfig)) - s = grpc.NewServer(opt) - pb.RegisterTaskServer(s, Server{}) - grpclog.Printf("listen %s with TLS", addr) - } else { - s = grpc.NewServer() - pb.RegisterTaskServer(s, Server{}) - grpclog.Printf("listen %s", addr) - } + var s *grpc.Server + if enableTLS { + tlsConfig, err := certificate.GetTLSConfigForServer() + if err != nil { + grpclog.Fatal(err) + } + opt := grpc.Creds(credentials.NewTLS(tlsConfig)) + s = grpc.NewServer(opt) + pb.RegisterTaskServer(s, Server{}) + grpclog.Printf("listen %s with TLS", addr) + } else { + s = grpc.NewServer() + pb.RegisterTaskServer(s, Server{}) + grpclog.Printf("listen %s", addr) + } - err = s.Serve(l) - grpclog.Fatal(err) + err = s.Serve(l) + grpclog.Fatal(err) } - diff --git a/modules/setting/setting.go b/modules/setting/setting.go index d5da574..b41e72d 100644 --- a/modules/setting/setting.go +++ b/modules/setting/setting.go @@ -1,111 +1,111 @@ package setting import ( - "errors" - "gopkg.in/ini.v1" - "github.com/ouqiang/gocron/modules/utils" - "github.com/ouqiang/gocron/modules/logger" + "errors" + "github.com/ouqiang/gocron/modules/logger" + "github.com/ouqiang/gocron/modules/utils" + "gopkg.in/ini.v1" ) const DefaultSection = "default" type Setting struct { - Db struct{ - Engine string - Host string - Port int - User string - Password string - Database string - Prefix string - Charset string - MaxIdleConns int - MaxOpenConns int - } - AllowIps string - AppName string - ApiKey string - ApiSecret string - ApiSignEnable bool + Db struct { + Engine string + Host string + Port int + User string + Password string + Database string + Prefix string + Charset string + MaxIdleConns int + MaxOpenConns int + } + AllowIps string + AppName string + ApiKey string + ApiSecret string + ApiSignEnable bool - EnableTLS bool - CAFile string - CertFile string - KeyFile string + EnableTLS bool + CAFile string + CertFile string + KeyFile string } // 读取配置 -func Read(filename string) (*Setting,error) { - config, err := ini.Load(filename) - if err != nil { - return nil, err - } - section := config.Section(DefaultSection) +func Read(filename string) (*Setting, error) { + config, err := ini.Load(filename) + if err != nil { + return nil, err + } + section := config.Section(DefaultSection) - var s Setting + var s Setting - s.Db.Engine = section.Key("db.engine").MustString("mysql") - s.Db.Host = section.Key("db.host").MustString("127.0.0.1") - s.Db.Port = section.Key("db.port").MustInt(3306) - s.Db.User = section.Key("db.user").MustString("") - s.Db.Password = section.Key("db.password").MustString("") - s.Db.Database = section.Key("db.database").MustString("gocron") - s.Db.Prefix = section.Key("db.prefix").MustString("") - s.Db.Charset = section.Key("db.charset").MustString("utf8") - s.Db.MaxIdleConns = section.Key("db.max.idle.conns").MustInt(30) - s.Db.MaxOpenConns = section.Key("db.max.open.conns").MustInt(100) + s.Db.Engine = section.Key("db.engine").MustString("mysql") + s.Db.Host = section.Key("db.host").MustString("127.0.0.1") + s.Db.Port = section.Key("db.port").MustInt(3306) + s.Db.User = section.Key("db.user").MustString("") + s.Db.Password = section.Key("db.password").MustString("") + s.Db.Database = section.Key("db.database").MustString("gocron") + s.Db.Prefix = section.Key("db.prefix").MustString("") + s.Db.Charset = section.Key("db.charset").MustString("utf8") + s.Db.MaxIdleConns = section.Key("db.max.idle.conns").MustInt(30) + s.Db.MaxOpenConns = section.Key("db.max.open.conns").MustInt(100) - s.AllowIps = section.Key("allow_ips").MustString("") - s.AppName = section.Key("app.name").MustString("定时任务管理系统") - s.ApiKey = section.Key("api.key").MustString("") - s.ApiSecret = section.Key("api.secret").MustString("") - s.ApiSignEnable = section.Key("api.sign.enable").MustBool(true) + s.AllowIps = section.Key("allow_ips").MustString("") + s.AppName = section.Key("app.name").MustString("定时任务管理系统") + s.ApiKey = section.Key("api.key").MustString("") + s.ApiSecret = section.Key("api.secret").MustString("") + s.ApiSignEnable = section.Key("api.sign.enable").MustBool(true) - s.EnableTLS = section.Key("enable_tls").MustBool(false) - s.CAFile = section.Key("ca_file").MustString("") - s.CertFile = section.Key("cert_file").MustString("") - s.KeyFile = section.Key("key_file").MustString("") + s.EnableTLS = section.Key("enable_tls").MustBool(false) + s.CAFile = section.Key("ca_file").MustString("") + s.CertFile = section.Key("cert_file").MustString("") + s.KeyFile = section.Key("key_file").MustString("") - if s.EnableTLS { - if !utils.FileExist(s.CAFile) { - logger.Fatalf("failed to read ca cert file: %s", s.CAFile) - } + if s.EnableTLS { + if !utils.FileExist(s.CAFile) { + logger.Fatalf("failed to read ca cert file: %s", s.CAFile) + } - if !utils.FileExist(s.CertFile) { - logger.Fatalf("failed to read client cert file: %s", s.CertFile) - } + if !utils.FileExist(s.CertFile) { + logger.Fatalf("failed to read client cert file: %s", s.CertFile) + } - if !utils.FileExist(s.KeyFile) { - logger.Fatalf("failed to read client key file: %s", s.KeyFile) - } - } + if !utils.FileExist(s.KeyFile) { + logger.Fatalf("failed to read client key file: %s", s.KeyFile) + } + } - return &s, nil + return &s, nil } // 写入配置 func Write(config []string, filename string) error { - if len(config) == 0 { - return errors.New("参数不能为空") - } - if len(config) % 2 != 0 { - return errors.New("参数不匹配") - } + if len(config) == 0 { + return errors.New("参数不能为空") + } + if len(config)%2 != 0 { + return errors.New("参数不匹配") + } - file := ini.Empty() + file := ini.Empty() - section, err := file.NewSection(DefaultSection) - if err != nil { - return err - } - for i := 0 ;i < len(config); { - _, err = section.NewKey(config[i], config[i+1]) - if err != nil { - return err - } - i += 2 - } - err = file.SaveTo(filename) + section, err := file.NewSection(DefaultSection) + if err != nil { + return err + } + for i := 0; i < len(config); { + _, err = section.NewKey(config[i], config[i+1]) + if err != nil { + return err + } + i += 2 + } + err = file.SaveTo(filename) - return err + return err } diff --git a/modules/ssh/ssh.go b/modules/ssh/ssh.go index 5b255f9..da9ab81 100644 --- a/modules/ssh/ssh.go +++ b/modules/ssh/ssh.go @@ -1,143 +1,139 @@ package ssh import ( - "golang.org/x/crypto/ssh" - "fmt" - "net" - "time" - "errors" + "errors" + "fmt" + "golang.org/x/crypto/ssh" + "net" + "time" ) -type HostAuthType int8 // 认证方式 +type HostAuthType int8 // 认证方式 const ( - HostPassword = 1 // 密码认证 - HostPublicKey = 2 // 公钥认证 + HostPassword = 1 // 密码认证 + HostPublicKey = 2 // 公钥认证 ) const SSHConnectTimeout = 10 - -type SSHConfig struct { - AuthType HostAuthType - User string - Password string - PrivateKey string - Host string - Port int - ExecTimeout int// 执行超时时间 +type SSHConfig struct { + AuthType HostAuthType + User string + Password string + PrivateKey string + Host string + Port int + ExecTimeout int // 执行超时时间 } type Result struct { - Output string - Err error + Output string + Err error } func parseSSHConfig(sshConfig SSHConfig) (config *ssh.ClientConfig, err error) { - timeout := time.Duration(SSHConnectTimeout) * time.Second - // 密码认证 - if sshConfig.AuthType == HostPassword { - config = &ssh.ClientConfig{ - User: sshConfig.User, - Auth: []ssh.AuthMethod{ - ssh.Password(sshConfig.Password), - }, - Timeout: timeout, - HostKeyCallback:func(hostname string, remote net.Addr, key ssh.PublicKey) error { - return nil - }, - } + timeout := time.Duration(SSHConnectTimeout) * time.Second + // 密码认证 + if sshConfig.AuthType == HostPassword { + config = &ssh.ClientConfig{ + User: sshConfig.User, + Auth: []ssh.AuthMethod{ + ssh.Password(sshConfig.Password), + }, + Timeout: timeout, + HostKeyCallback: func(hostname string, remote net.Addr, key ssh.PublicKey) error { + return nil + }, + } - return - } + return + } - signer, err := ssh.ParsePrivateKey([]byte(sshConfig.PrivateKey)) - if err != nil { - return - } + signer, err := ssh.ParsePrivateKey([]byte(sshConfig.PrivateKey)) + if err != nil { + return + } - // 公钥认证 - config = &ssh.ClientConfig{ - User: sshConfig.User, - Auth: []ssh.AuthMethod{ - ssh.PublicKeys(signer), - }, - Timeout: timeout, - HostKeyCallback:func(hostname string, remote net.Addr, key ssh.PublicKey) error { - return nil - }, - } + // 公钥认证 + config = &ssh.ClientConfig{ + User: sshConfig.User, + Auth: []ssh.AuthMethod{ + ssh.PublicKeys(signer), + }, + Timeout: timeout, + HostKeyCallback: func(hostname string, remote net.Addr, key ssh.PublicKey) error { + return nil + }, + } - return + return } - // 执行shell命令 func Exec(sshConfig SSHConfig, cmd string) (output string, err error) { - client, err := getClient(sshConfig) - if err != nil { - return "", err - } - defer client.Close() + client, err := getClient(sshConfig) + if err != nil { + return "", err + } + defer client.Close() - session, err := client.NewSession() + session, err := client.NewSession() - if err != nil { - return "", err - } - defer session.Close() + if err != nil { + return "", err + } + defer session.Close() - // 后台运行 - if sshConfig.ExecTimeout < 0 { - go session.CombinedOutput(cmd) - time.Sleep(10 * time.Second) - return "", nil - } - // 不限制超时 - if sshConfig.ExecTimeout == 0 { - outputByte, execErr := session.CombinedOutput(cmd) - output = string(outputByte) - err = execErr - return - } + // 后台运行 + if sshConfig.ExecTimeout < 0 { + go session.CombinedOutput(cmd) + time.Sleep(10 * time.Second) + return "", nil + } + // 不限制超时 + if sshConfig.ExecTimeout == 0 { + outputByte, execErr := session.CombinedOutput(cmd) + output = string(outputByte) + err = execErr + return + } - var resultChan chan Result = make(chan Result) - var timeoutChan chan bool = make(chan bool) - go func() { - output, err := session.CombinedOutput(cmd) - resultChan <- Result{string(output), err} - }() - // todo 等待超时后,如何停止远程正在执行的任务, 使用timeout命令,但不具有通用性 - go triggerTimeout(timeoutChan, sshConfig.ExecTimeout) - select { - case result := <- resultChan: - output = result.Output - err = result.Err - case <- timeoutChan: - output = "" - err = errors.New("timeout") - } + var resultChan chan Result = make(chan Result) + var timeoutChan chan bool = make(chan bool) + go func() { + output, err := session.CombinedOutput(cmd) + resultChan <- Result{string(output), err} + }() + // todo 等待超时后,如何停止远程正在执行的任务, 使用timeout命令,但不具有通用性 + go triggerTimeout(timeoutChan, sshConfig.ExecTimeout) + select { + case result := <-resultChan: + output = result.Output + err = result.Err + case <-timeoutChan: + output = "" + err = errors.New("timeout") + } - return + return } -func getClient(sshConfig SSHConfig) (*ssh.Client, error) { - config, err := parseSSHConfig(sshConfig) - if err != nil { - return nil, err - } - addr := fmt.Sprintf("%s:%d", sshConfig.Host, sshConfig.Port) +func getClient(sshConfig SSHConfig) (*ssh.Client, error) { + config, err := parseSSHConfig(sshConfig) + if err != nil { + return nil, err + } + addr := fmt.Sprintf("%s:%d", sshConfig.Host, sshConfig.Port) - return ssh.Dial("tcp", addr, config) + return ssh.Dial("tcp", addr, config) } - -func triggerTimeout(ch chan bool, timeout int){ - // 最长执行时间不能超过24小时 - if timeout <= 0 || timeout > 86400 { - timeout = 86400 - } - time.Sleep(time.Duration(timeout) * time.Second) - close(ch) +func triggerTimeout(ch chan bool, timeout int) { + // 最长执行时间不能超过24小时 + if timeout <= 0 || timeout > 86400 { + timeout = 86400 + } + time.Sleep(time.Duration(timeout) * time.Second) + close(ch) } - diff --git a/modules/utils/json.go b/modules/utils/json.go index e45d583..cbc5f0a 100644 --- a/modules/utils/json.go +++ b/modules/utils/json.go @@ -1,16 +1,16 @@ package utils import ( - "encoding/json" - "github.com/ouqiang/gocron/modules/logger" + "encoding/json" + "github.com/ouqiang/gocron/modules/logger" ) // json 格式输出 type response struct { - Code int `json:"code"` // 状态码 0:成功 非0:失败 - Message string `json:"message"` // 信息 - Data interface{} `json:"data"` // 数据 + Code int `json:"code"` // 状态码 0:成功 非0:失败 + Message string `json:"message"` // 信息 + Data interface{} `json:"data"` // 数据 } type JsonResponse struct{} @@ -26,40 +26,40 @@ const SuccessContent = "操作成功" const FailureContent = "操作失败" func JsonResponseByErr(err error) string { - json := JsonResponse{} - if err != nil { - return json.CommonFailure(FailureContent, err) - } + json := JsonResponse{} + if err != nil { + return json.CommonFailure(FailureContent, err) + } - return json.Success(SuccessContent, nil) + return json.Success(SuccessContent, nil) } func (j *JsonResponse) Success(message string, data interface{}) string { - return j.response(ResponseSuccess, message, data) + return j.response(ResponseSuccess, message, data) } func (j *JsonResponse) Failure(code int, message string) string { - return j.response(code, message, nil) + return j.response(code, message, nil) } -func (j *JsonResponse) CommonFailure(message string, err... error) string { - if len(err) > 0 { - logger.Warn(err) - } - return j.Failure(ResponseFailure, message) +func (j *JsonResponse) CommonFailure(message string, err ...error) string { + if len(err) > 0 { + logger.Warn(err) + } + return j.Failure(ResponseFailure, message) } func (j *JsonResponse) response(code int, message string, data interface{}) string { - resp := response{ - Code: code, - Message: message, - Data: data, - } + resp := response{ + Code: code, + Message: message, + Data: data, + } - result, err := json.Marshal(resp) - if err != nil { - logger.Error(err) - } + result, err := json.Marshal(resp) + if err != nil { + logger.Error(err) + } - return string(result) + return string(result) } diff --git a/modules/utils/utils.go b/modules/utils/utils.go index 5ce46f2..a1cacab 100644 --- a/modules/utils/utils.go +++ b/modules/utils/utils.go @@ -1,107 +1,107 @@ package utils import ( - "crypto/md5" - "encoding/hex" - "math/rand" - "time" - "runtime" - "github.com/Tang-RoseChild/mahonia" - "strings" - "os" - "fmt" + "crypto/md5" + "encoding/hex" + "fmt" + "github.com/Tang-RoseChild/mahonia" + "math/rand" + "os" + "runtime" + "strings" + "time" ) // 生成长度为length的随机字符串 func RandString(length int64) string { - sources := []byte("0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ") - result := []byte{} - r := rand.New(rand.NewSource(time.Now().UnixNano())) - sourceLength := len(sources) - var i int64 = 0 - for ; i < length; i++ { - result = append(result, sources[r.Intn(sourceLength)]) - } + sources := []byte("0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ") + result := []byte{} + r := rand.New(rand.NewSource(time.Now().UnixNano())) + sourceLength := len(sources) + var i int64 = 0 + for ; i < length; i++ { + result = append(result, sources[r.Intn(sourceLength)]) + } - return string(result) + return string(result) } // 生成32位MD5摘要 func Md5(str string) string { - m := md5.New() - m.Write([]byte(str)) + m := md5.New() + m.Write([]byte(str)) - return hex.EncodeToString(m.Sum(nil)) + return hex.EncodeToString(m.Sum(nil)) } // 生成0-max之间随机数 func RandNumber(max int) int { - r := rand.New(rand.NewSource(time.Now().UnixNano())) + r := rand.New(rand.NewSource(time.Now().UnixNano())) - return r.Intn(max) + return r.Intn(max) } // 判断当前系统是否是windows func IsWindows() bool { - return runtime.GOOS == "windows" + return runtime.GOOS == "windows" } // GBK编码转换为UTF8 func GBK2UTF8(s string) (string, bool) { - dec := mahonia.NewDecoder("gbk") + dec := mahonia.NewDecoder("gbk") - return dec.ConvertStringOK(s) + return dec.ConvertStringOK(s) } // 批量替换字符串 -func ReplaceStrings(s string, old []string, replace []string) string { - if s == "" { - return s - } - if len(old) != len(replace) { - return s - } +func ReplaceStrings(s string, old []string, replace []string) string { + if s == "" { + return s + } + if len(old) != len(replace) { + return s + } - for i, v := range old { - s = strings.Replace(s, v, replace[i], 1000) - } + for i, v := range old { + s = strings.Replace(s, v, replace[i], 1000) + } - return s + return s } func InStringSlice(slice []string, element string) bool { - element = strings.TrimSpace(element) - for _, v := range slice { - if strings.TrimSpace(v) == element{ - return true - } - } + element = strings.TrimSpace(element) + for _, v := range slice { + if strings.TrimSpace(v) == element { + return true + } + } - return false + return false } // 转义json特殊字符 -func EscapeJson(s string) string { - specialChars := []string{"\\", "\b","\f", "\n", "\r", "\t", "\"",} - replaceChars := []string{ "\\\\", "\\b", "\\f", "\\n", "\\r", "\\t", "\\\"",} +func EscapeJson(s string) string { + specialChars := []string{"\\", "\b", "\f", "\n", "\r", "\t", "\""} + replaceChars := []string{"\\\\", "\\b", "\\f", "\\n", "\\r", "\\t", "\\\""} - return ReplaceStrings(s, specialChars, replaceChars) + return ReplaceStrings(s, specialChars, replaceChars) } // 判断文件是否存在及是否有权限访问 func FileExist(file string) bool { - _, err := os.Stat(file) - if os.IsNotExist(err) { - return false - } - if os.IsPermission(err) { - return false - } + _, err := os.Stat(file) + if os.IsNotExist(err) { + return false + } + if os.IsPermission(err) { + return false + } - return true + return true } // 格式化环境变量 func FormatUnixEnv(key, value string) string { - return fmt.Sprintf("export %s=%s; ", key, value) -} \ No newline at end of file + return fmt.Sprintf("export %s=%s; ", key, value) +} diff --git a/modules/utils/utils_test.go b/modules/utils/utils_test.go index f92401a..4fe09d6 100644 --- a/modules/utils/utils_test.go +++ b/modules/utils/utils_test.go @@ -3,22 +3,22 @@ package utils import "testing" func TestRandString(t *testing.T) { - str := RandString(32) - if len(str) != 32 { - t.Fatalf("长度不匹配,目标长度32, 实际%d-%s", len(str), str) - } + str := RandString(32) + if len(str) != 32 { + t.Fatalf("长度不匹配,目标长度32, 实际%d-%s", len(str), str) + } } func TestMd5(t *testing.T) { - str := Md5("123456") - if len(str) != 32 { - t.Fatalf("长度不匹配,目标长度32, 实际%d-%s", len(str), str) - } + str := Md5("123456") + if len(str) != 32 { + t.Fatalf("长度不匹配,目标长度32, 实际%d-%s", len(str), str) + } } func TestRandNumber(t *testing.T) { - num := RandNumber(10000) - if num <= 0 && num >= 10000 { - t.Fatalf("随机数不在有效范围内-%d", num) - } + num := RandNumber(10000) + if num <= 0 && num >= 10000 { + t.Fatalf("随机数不在有效范围内-%d", num) + } } diff --git a/modules/utils/utils_unix.go b/modules/utils/utils_unix.go index 16539a3..d6d68c9 100644 --- a/modules/utils/utils_unix.go +++ b/modules/utils/utils_unix.go @@ -3,35 +3,35 @@ package utils import ( - "os/exec" - "syscall" - "golang.org/x/net/context" - "errors" + "errors" + "golang.org/x/net/context" + "os/exec" + "syscall" ) type Result struct { - output string - err error + output string + err error } // 执行shell命令,可设置执行超时时间 -func ExecShell(ctx context.Context, command string) (string, error) { - cmd := exec.Command("/bin/bash", "-c", command) - cmd.SysProcAttr = &syscall.SysProcAttr{ - Setpgid: true, - } - var resultChan chan Result = make(chan Result) - go func() { - output ,err := cmd.CombinedOutput() - resultChan <- Result{string(output), err} - }() - select { - case <- ctx.Done(): - if cmd.Process.Pid > 0 { - syscall.Kill(-cmd.Process.Pid, syscall.SIGKILL) - } - return "", errors.New("timeout killed") - case result := <- resultChan: - return result.output, result.err - } -} \ No newline at end of file +func ExecShell(ctx context.Context, command string) (string, error) { + cmd := exec.Command("/bin/bash", "-c", command) + cmd.SysProcAttr = &syscall.SysProcAttr{ + Setpgid: true, + } + var resultChan chan Result = make(chan Result) + go func() { + output, err := cmd.CombinedOutput() + resultChan <- Result{string(output), err} + }() + select { + case <-ctx.Done(): + if cmd.Process.Pid > 0 { + syscall.Kill(-cmd.Process.Pid, syscall.SIGKILL) + } + return "", errors.New("timeout killed") + case result := <-resultChan: + return result.output, result.err + } +} diff --git a/modules/utils/utils_windows.go b/modules/utils/utils_windows.go index 6ad321d..bd60bb5 100644 --- a/modules/utils/utils_windows.go +++ b/modules/utils/utils_windows.go @@ -3,51 +3,48 @@ package utils import ( - "syscall" - "os/exec" - "strconv" - "golang.org/x/net/context" - "errors" + "errors" + "golang.org/x/net/context" + "os/exec" + "strconv" + "syscall" ) type Result struct { - output string - err error + output string + err error } // 执行shell命令,可设置执行超时时间 -func ExecShell(ctx context.Context, command string) (string, error) { - cmd := exec.Command("cmd", "/C", command) - // 隐藏cmd窗口 - cmd.SysProcAttr = &syscall.SysProcAttr{ - HideWindow: true, - } - var resultChan chan Result = make(chan Result) - go func() { - output ,err := cmd.CombinedOutput() - resultChan <- Result{string(output), err} - }() - select { - case <- ctx.Done(): - if cmd.Process.Pid > 0 { - exec.Command("taskkill", "/F", "/T", "/PID", strconv.Itoa(cmd.Process.Pid)).Run() - cmd.Process.Kill() - } - return "", errors.New("timeout killed") - case result := <- resultChan: - return ConvertEncoding(result.output), result.err - } - - - return "", nil +func ExecShell(ctx context.Context, command string) (string, error) { + cmd := exec.Command("cmd", "/C", command) + // 隐藏cmd窗口 + cmd.SysProcAttr = &syscall.SysProcAttr{ + HideWindow: true, + } + var resultChan chan Result = make(chan Result) + go func() { + output, err := cmd.CombinedOutput() + resultChan <- Result{string(output), err} + }() + select { + case <-ctx.Done(): + if cmd.Process.Pid > 0 { + exec.Command("taskkill", "/F", "/T", "/PID", strconv.Itoa(cmd.Process.Pid)).Run() + cmd.Process.Kill() + } + return "", errors.New("timeout killed") + case result := <-resultChan: + return ConvertEncoding(result.output), result.err + } } -func ConvertEncoding(outputGBK string) (string) { - // windows平台编码为gbk,需转换为utf8才能入库 - outputUTF8, ok := GBK2UTF8(outputGBK) - if ok { - return outputUTF8 - } +func ConvertEncoding(outputGBK string) string { + // windows平台编码为gbk,需转换为utf8才能入库 + outputUTF8, ok := GBK2UTF8(outputGBK) + if ok { + return outputUTF8 + } - return "命令输出转换编码失败(gbk to utf8)" -} \ No newline at end of file + return "命令输出转换编码失败(gbk to utf8)" +} diff --git a/routers/base/base.go b/routers/base/base.go index da19c74..11a412d 100644 --- a/routers/base/base.go +++ b/routers/base/base.go @@ -1,20 +1,20 @@ package base import ( - "gopkg.in/macaron.v1" - "github.com/ouqiang/gocron/models" + "github.com/ouqiang/gocron/models" + "gopkg.in/macaron.v1" ) -func ParsePageAndPageSize(ctx *macaron.Context, params models.CommonMap) { - page := ctx.QueryInt("page") - pageSize := ctx.QueryInt("page_size") - if page <= 0 { - page = 1 - } - if pageSize <= 0 { - pageSize = models.PageSize - } +func ParsePageAndPageSize(ctx *macaron.Context, params models.CommonMap) { + page := ctx.QueryInt("page") + pageSize := ctx.QueryInt("page_size") + if page <= 0 { + page = 1 + } + if pageSize <= 0 { + pageSize = models.PageSize + } - params["Page"] = page - params["PageSize"] = pageSize -} \ No newline at end of file + params["Page"] = page + params["PageSize"] = pageSize +} diff --git a/routers/home.go b/routers/home.go index 84ebdc2..1d504b5 100644 --- a/routers/home.go +++ b/routers/home.go @@ -3,6 +3,6 @@ package routers import "gopkg.in/macaron.v1" // 首页 -func Home(ctx *macaron.Context) { - ctx.Redirect("/task") +func Home(ctx *macaron.Context) { + ctx.Redirect("/task") } diff --git a/routers/host/host.go b/routers/host/host.go index 9625df4..ce8fa9a 100644 --- a/routers/host/host.go +++ b/routers/host/host.go @@ -1,194 +1,192 @@ package host import ( - "gopkg.in/macaron.v1" - "github.com/ouqiang/gocron/models" - "github.com/ouqiang/gocron/modules/utils" - "github.com/ouqiang/gocron/modules/logger" - "strconv" - "github.com/ouqiang/gocron/service" - "github.com/Unknwon/paginater" - "fmt" - "html/template" - "github.com/ouqiang/gocron/routers/base" - "github.com/go-macaron/binding" - "github.com/ouqiang/gocron/modules/rpc/grpcpool" - "strings" - "github.com/ouqiang/gocron/modules/rpc/client" - "github.com/ouqiang/gocron/modules/rpc/proto" + "fmt" + "github.com/Unknwon/paginater" + "github.com/go-macaron/binding" + "github.com/ouqiang/gocron/models" + "github.com/ouqiang/gocron/modules/logger" + "github.com/ouqiang/gocron/modules/rpc/client" + "github.com/ouqiang/gocron/modules/rpc/grpcpool" + "github.com/ouqiang/gocron/modules/rpc/proto" + "github.com/ouqiang/gocron/modules/utils" + "github.com/ouqiang/gocron/routers/base" + "github.com/ouqiang/gocron/service" + "gopkg.in/macaron.v1" + "html/template" + "strconv" + "strings" ) -func Index(ctx *macaron.Context) { - hostModel := new(models.Host) - queryParams := parseQueryParams(ctx) - total, err := hostModel.Total(queryParams) - hosts, err := hostModel.List(queryParams) - if err != nil { - logger.Error(err) - } - name, ok := queryParams["name"].(string) - var safeNameHTML = "" - if ok { - safeNameHTML = template.HTMLEscapeString(name) - } - PageParams := fmt.Sprintf("id=%d&name=%s&page_size=%d", - queryParams["Id"], safeNameHTML, queryParams["PageSize"]); - queryParams["PageParams"] = template.URL(PageParams) - p := paginater.New(int(total), queryParams["PageSize"].(int), queryParams["Page"].(int), 5) - ctx.Data["Pagination"] = p - ctx.Data["Title"] = "主机列表" - ctx.Data["Hosts"] = hosts - ctx.Data["Params"] = queryParams - ctx.HTML(200, "host/index") +func Index(ctx *macaron.Context) { + hostModel := new(models.Host) + queryParams := parseQueryParams(ctx) + total, err := hostModel.Total(queryParams) + hosts, err := hostModel.List(queryParams) + if err != nil { + logger.Error(err) + } + name, ok := queryParams["name"].(string) + var safeNameHTML = "" + if ok { + safeNameHTML = template.HTMLEscapeString(name) + } + PageParams := fmt.Sprintf("id=%d&name=%s&page_size=%d", + queryParams["Id"], safeNameHTML, queryParams["PageSize"]) + queryParams["PageParams"] = template.URL(PageParams) + p := paginater.New(int(total), queryParams["PageSize"].(int), queryParams["Page"].(int), 5) + ctx.Data["Pagination"] = p + ctx.Data["Title"] = "主机列表" + ctx.Data["Hosts"] = hosts + ctx.Data["Params"] = queryParams + ctx.HTML(200, "host/index") } -func Create(ctx *macaron.Context) { - ctx.Data["Title"] = "添加主机" - ctx.HTML(200, "host/host_form") +func Create(ctx *macaron.Context) { + ctx.Data["Title"] = "添加主机" + ctx.HTML(200, "host/host_form") } -func Edit(ctx *macaron.Context) { - ctx.Data["Title"] = "编辑主机" - hostModel := new(models.Host) - id := ctx.ParamsInt(":id") - err := hostModel.Find(id) - if err != nil { - logger.Errorf("获取主机详情失败#主机id-%d", id) - } - ctx.Data["Host"] = hostModel - ctx.HTML(200, "host/host_form") +func Edit(ctx *macaron.Context) { + ctx.Data["Title"] = "编辑主机" + hostModel := new(models.Host) + id := ctx.ParamsInt(":id") + err := hostModel.Find(id) + if err != nil { + logger.Errorf("获取主机详情失败#主机id-%d", id) + } + ctx.Data["Host"] = hostModel + ctx.HTML(200, "host/host_form") } type HostForm struct { - Id int16 - Name string `binding:"Required;MaxSize(64)"` - Alias string `binding:"Required;MaxSize(32)"` - Port int `binding:"Required;Range(1-65535)"` - Remark string + Id int16 + Name string `binding:"Required;MaxSize(64)"` + Alias string `binding:"Required;MaxSize(32)"` + Port int `binding:"Required;Range(1-65535)"` + Remark string } func (f HostForm) Error(ctx *macaron.Context, errs binding.Errors) { - if len(errs) == 0 { - return - } - json := utils.JsonResponse{} - content := json.CommonFailure("表单验证失败, 请检测输入") + if len(errs) == 0 { + return + } + json := utils.JsonResponse{} + content := json.CommonFailure("表单验证失败, 请检测输入") - ctx.Resp.Write([]byte(content)) + ctx.Resp.Write([]byte(content)) } -func Store(ctx *macaron.Context, form HostForm) string { - json := utils.JsonResponse{} - hostModel := new(models.Host) - id := form.Id - nameExist, err := hostModel.NameExists(form.Name, form.Id) - if err != nil { - return json.CommonFailure("操作失败", err) - } - if nameExist { - return json.CommonFailure("主机名已存在") - } +func Store(ctx *macaron.Context, form HostForm) string { + json := utils.JsonResponse{} + hostModel := new(models.Host) + id := form.Id + nameExist, err := hostModel.NameExists(form.Name, form.Id) + if err != nil { + return json.CommonFailure("操作失败", err) + } + if nameExist { + return json.CommonFailure("主机名已存在") + } - hostModel.Name = strings.TrimSpace(form.Name) - hostModel.Alias = strings.TrimSpace(form.Alias) - hostModel.Port = form.Port - hostModel.Remark = strings.TrimSpace(form.Remark) - isCreate := false - oldHostModel := new(models.Host) - err = oldHostModel.Find(int(id)) - if err != nil { - return json.CommonFailure("主机不存在") - } + hostModel.Name = strings.TrimSpace(form.Name) + hostModel.Alias = strings.TrimSpace(form.Alias) + hostModel.Port = form.Port + hostModel.Remark = strings.TrimSpace(form.Remark) + isCreate := false + oldHostModel := new(models.Host) + err = oldHostModel.Find(int(id)) + if err != nil { + return json.CommonFailure("主机不存在") + } - if id > 0 { - _, err = hostModel.UpdateBean(id) - } else { - isCreate = true - id, err = hostModel.Create() - } - if err != nil { - return json.CommonFailure("保存失败", err) - } + if id > 0 { + _, err = hostModel.UpdateBean(id) + } else { + isCreate = true + id, err = hostModel.Create() + } + if err != nil { + return json.CommonFailure("保存失败", err) + } - if !isCreate { - oldAddr := fmt.Sprintf("%s:%d", oldHostModel.Name, oldHostModel.Port) - newAddr := fmt.Sprintf("%s:%d", hostModel.Name, hostModel.Port) - if oldAddr != newAddr { - grpcpool.Pool.Release(oldAddr) - } + if !isCreate { + oldAddr := fmt.Sprintf("%s:%d", oldHostModel.Name, oldHostModel.Port) + newAddr := fmt.Sprintf("%s:%d", hostModel.Name, hostModel.Port) + if oldAddr != newAddr { + grpcpool.Pool.Release(oldAddr) + } - taskModel := new(models.Task) - tasks, err := taskModel.ActiveListByHostId(id) - if err != nil { - return json.CommonFailure("刷新任务主机信息失败", err) - } - serviceTask := new(service.Task) - serviceTask.BatchAdd(tasks) - } + taskModel := new(models.Task) + tasks, err := taskModel.ActiveListByHostId(id) + if err != nil { + return json.CommonFailure("刷新任务主机信息失败", err) + } + serviceTask := new(service.Task) + serviceTask.BatchAdd(tasks) + } - return json.Success("保存成功", nil) + return json.Success("保存成功", nil) } -func Remove(ctx *macaron.Context) string { - id, err := strconv.Atoi(ctx.Params(":id")) - json := utils.JsonResponse{} - if err != nil { - return json.CommonFailure("参数错误", err) - } - taskHostModel := new(models.TaskHost) - exist,err := taskHostModel.HostIdExist(int16(id)) - if err != nil { - return json.CommonFailure("操作失败", err) - } - if exist { - return json.CommonFailure("有任务引用此主机,不能删除") - } +func Remove(ctx *macaron.Context) string { + id, err := strconv.Atoi(ctx.Params(":id")) + json := utils.JsonResponse{} + if err != nil { + return json.CommonFailure("参数错误", err) + } + taskHostModel := new(models.TaskHost) + exist, err := taskHostModel.HostIdExist(int16(id)) + if err != nil { + return json.CommonFailure("操作失败", err) + } + if exist { + return json.CommonFailure("有任务引用此主机,不能删除") + } - hostModel := new(models.Host) - err = hostModel.Find(int(id)) - if err != nil { - return json.CommonFailure("主机不存在") - } + hostModel := new(models.Host) + err = hostModel.Find(int(id)) + if err != nil { + return json.CommonFailure("主机不存在") + } - _, err =hostModel.Delete(id) - if err != nil { - return json.CommonFailure("操作失败", err) - } + _, err = hostModel.Delete(id) + if err != nil { + return json.CommonFailure("操作失败", err) + } - addr := fmt.Sprintf("%s:%d", hostModel.Name, hostModel.Port) - grpcpool.Pool.Release(addr) + addr := fmt.Sprintf("%s:%d", hostModel.Name, hostModel.Port) + grpcpool.Pool.Release(addr) - - return json.Success("操作成功", nil) + return json.Success("操作成功", nil) } -func Ping(ctx *macaron.Context) string { - id := ctx.ParamsInt(":id") - hostModel := new(models.Host) - err := hostModel.Find(id) - json := utils.JsonResponse{} - if err != nil || hostModel.Id <= 0{ - return json.CommonFailure("主机不存在", err) - } +func Ping(ctx *macaron.Context) string { + id := ctx.ParamsInt(":id") + hostModel := new(models.Host) + err := hostModel.Find(id) + json := utils.JsonResponse{} + if err != nil || hostModel.Id <= 0 { + return json.CommonFailure("主机不存在", err) + } + taskReq := &rpc.TaskRequest{} + taskReq.Command = "echo hello" + taskReq.Timeout = 10 + output, err := client.Exec(hostModel.Name, hostModel.Port, taskReq) + if err != nil { + return json.CommonFailure("连接失败-"+err.Error()+" "+output, err) + } - taskReq := &rpc.TaskRequest{} - taskReq.Command = "echo hello" - taskReq.Timeout = 10 - output, err := client.Exec(hostModel.Name, hostModel.Port, taskReq) - if err != nil { - return json.CommonFailure("连接失败-" + err.Error() + " " + output, err) - } - - return json.Success("连接成功", nil) + return json.Success("连接成功", nil) } // 解析查询参数 -func parseQueryParams(ctx *macaron.Context) (models.CommonMap) { - var params models.CommonMap = models.CommonMap{} - params["Id"] = ctx.QueryInt("id") - params["Name"] = ctx.QueryTrim("name") - base.ParsePageAndPageSize(ctx, params) +func parseQueryParams(ctx *macaron.Context) models.CommonMap { + var params models.CommonMap = models.CommonMap{} + params["Id"] = ctx.QueryInt("id") + params["Name"] = ctx.QueryTrim("name") + base.ParsePageAndPageSize(ctx, params) - return params -} \ No newline at end of file + return params +} diff --git a/routers/install/install.go b/routers/install/install.go index 821839f..f9bc750 100644 --- a/routers/install/install.go +++ b/routers/install/install.go @@ -1,163 +1,163 @@ package install import ( - "github.com/ouqiang/gocron/models" - "github.com/ouqiang/gocron/modules/app" - "github.com/ouqiang/gocron/modules/setting" - "github.com/ouqiang/gocron/modules/utils" - "gopkg.in/macaron.v1" - "strconv" - "fmt" - "github.com/ouqiang/gocron/service" - "github.com/go-macaron/binding" + "fmt" + "github.com/go-macaron/binding" + "github.com/ouqiang/gocron/models" + "github.com/ouqiang/gocron/modules/app" + "github.com/ouqiang/gocron/modules/setting" + "github.com/ouqiang/gocron/modules/utils" + "github.com/ouqiang/gocron/service" + "gopkg.in/macaron.v1" + "strconv" ) // 系统安装 type InstallForm struct { - DbType string `binding:"In(mysql)"` - DbHost string `binding:"Required;MaxSize(50)"` - DbPort int `binding:"Required;Range(1,65535)"` - DbUsername string `binding:"Required;MaxSize(50)"` - DbPassword string `binding:"Required;MaxSize(30)"` - DbName string `binding:"Required;MaxSize(50)"` - DbTablePrefix string `binding:"MaxSize(20)"` - AdminUsername string `binding:"Required;MinSize(3)"` - AdminPassword string `binding:"Required;MinSize(6)"` - ConfirmAdminPassword string `binding:"Required;MinSize(6)"` - AdminEmail string `binding:"Required;Email;MaxSize(50)"` + DbType string `binding:"In(mysql)"` + DbHost string `binding:"Required;MaxSize(50)"` + DbPort int `binding:"Required;Range(1,65535)"` + DbUsername string `binding:"Required;MaxSize(50)"` + DbPassword string `binding:"Required;MaxSize(30)"` + DbName string `binding:"Required;MaxSize(50)"` + DbTablePrefix string `binding:"MaxSize(20)"` + AdminUsername string `binding:"Required;MinSize(3)"` + AdminPassword string `binding:"Required;MinSize(6)"` + ConfirmAdminPassword string `binding:"Required;MinSize(6)"` + AdminEmail string `binding:"Required;Email;MaxSize(50)"` } func (f InstallForm) Error(ctx *macaron.Context, errs binding.Errors) { - if len(errs) == 0 { - return - } - json := utils.JsonResponse{} - content := json.CommonFailure("表单验证失败, 请检测输入") + if len(errs) == 0 { + return + } + json := utils.JsonResponse{} + content := json.CommonFailure("表单验证失败, 请检测输入") - ctx.Resp.Write([]byte(content)) + ctx.Resp.Write([]byte(content)) } func Create(ctx *macaron.Context) { - if app.Installed { - ctx.Redirect("/") - } - ctx.Data["Title"] = "安装" - ctx.Data["DisableNav"] = true - ctx.HTML(200, "install/create") + if app.Installed { + ctx.Redirect("/") + } + ctx.Data["Title"] = "安装" + ctx.Data["DisableNav"] = true + ctx.HTML(200, "install/create") } // 安装 func Store(ctx *macaron.Context, form InstallForm) string { - json := utils.JsonResponse{} - if app.Installed { - return json.CommonFailure("系统已安装!") - } - if form.AdminPassword != form.ConfirmAdminPassword { - return json.CommonFailure("两次输入密码不匹配") - } - err := testDbConnection(form) - if err != nil { - return json.CommonFailure("数据库连接失败", err) - } - // 写入数据库配置 - err = writeConfig(form) - if err != nil { - return json.CommonFailure("数据库配置写入文件失败", err) - } + json := utils.JsonResponse{} + if app.Installed { + return json.CommonFailure("系统已安装!") + } + if form.AdminPassword != form.ConfirmAdminPassword { + return json.CommonFailure("两次输入密码不匹配") + } + err := testDbConnection(form) + if err != nil { + return json.CommonFailure("数据库连接失败", err) + } + // 写入数据库配置 + err = writeConfig(form) + if err != nil { + return json.CommonFailure("数据库配置写入文件失败", err) + } - appConfig, err := setting.Read(app.AppConfig) - if err != nil { - return json.CommonFailure("读取应用配置失败", err) - } - app.Setting = appConfig + appConfig, err := setting.Read(app.AppConfig) + if err != nil { + return json.CommonFailure("读取应用配置失败", err) + } + app.Setting = appConfig - models.Db = models.CreateDb() - // 创建数据库表 - migration := new(models.Migration) - err = migration.Install(form.DbName) - if err != nil { - return json.CommonFailure(fmt.Sprintf("创建数据库表失败-%s", err.Error()), err) - } + models.Db = models.CreateDb() + // 创建数据库表 + migration := new(models.Migration) + err = migration.Install(form.DbName) + if err != nil { + return json.CommonFailure(fmt.Sprintf("创建数据库表失败-%s", err.Error()), err) + } - // 创建管理员账号 - err = createAdminUser(form) - if err != nil { - return json.CommonFailure("创建管理员账号失败", err) - } + // 创建管理员账号 + err = createAdminUser(form) + if err != nil { + return json.CommonFailure("创建管理员账号失败", err) + } - // 创建安装锁 - err = app.CreateInstallLock() - if err != nil { - return json.CommonFailure("创建文件安装锁失败", err) - } + // 创建安装锁 + err = app.CreateInstallLock() + if err != nil { + return json.CommonFailure("创建文件安装锁失败", err) + } - // 更新版本号文件 - app.UpdateVersionFile() + // 更新版本号文件 + app.UpdateVersionFile() - app.Installed = true - // 初始化定时任务 - serviceTask := new(service.Task) - serviceTask.Initialize() + app.Installed = true + // 初始化定时任务 + serviceTask := new(service.Task) + serviceTask.Initialize() - return json.Success("安装成功", nil) + return json.Success("安装成功", nil) } // 配置写入文件 func writeConfig(form InstallForm) error { - dbConfig := []string{ - "db.engine", form.DbType, - "db.host", form.DbHost, - "db.port", strconv.Itoa(form.DbPort), - "db.user", form.DbUsername, - "db.password",form.DbPassword, - "db.database", form.DbName, - "db.prefix", form.DbTablePrefix, - "db.charset", "utf8", - "db.max.idle.conns", "30", - "db.max.open.conns", "100", - "allow_ips", "", - "app.name", "定时任务管理系统", // 应用名称 - "api.key", "", - "api.secret", "", - "enable_tls", "false", - "ca_file", "", - "cert_file", "", - "key_file", "", - } + dbConfig := []string{ + "db.engine", form.DbType, + "db.host", form.DbHost, + "db.port", strconv.Itoa(form.DbPort), + "db.user", form.DbUsername, + "db.password", form.DbPassword, + "db.database", form.DbName, + "db.prefix", form.DbTablePrefix, + "db.charset", "utf8", + "db.max.idle.conns", "30", + "db.max.open.conns", "100", + "allow_ips", "", + "app.name", "定时任务管理系统", // 应用名称 + "api.key", "", + "api.secret", "", + "enable_tls", "false", + "ca_file", "", + "cert_file", "", + "key_file", "", + } - return setting.Write(dbConfig, app.AppConfig) + return setting.Write(dbConfig, app.AppConfig) } // 创建管理员账号 func createAdminUser(form InstallForm) error { - user := new(models.User) - user.Name = form.AdminUsername - user.Password = form.AdminPassword - user.Email = form.AdminEmail - user.IsAdmin = 1 - _, err := user.Create() + user := new(models.User) + user.Name = form.AdminUsername + user.Password = form.AdminPassword + user.Email = form.AdminEmail + user.IsAdmin = 1 + _, err := user.Create() - return err + return err } // 测试数据库连接 func testDbConnection(form InstallForm) error { - var s setting.Setting - s.Db.Engine = form.DbType - s.Db.Host = form.DbHost - s.Db.Port = form.DbPort - s.Db.User = form.DbUsername - s.Db.Password = form.DbPassword - s.Db.Charset = "utf8" - db, err := models.CreateTmpDb(&s) - if err != nil { - return err - } + var s setting.Setting + s.Db.Engine = form.DbType + s.Db.Host = form.DbHost + s.Db.Port = form.DbPort + s.Db.User = form.DbUsername + s.Db.Password = form.DbPassword + s.Db.Charset = "utf8" + db, err := models.CreateTmpDb(&s) + if err != nil { + return err + } - defer db.Close() - err = db.Ping() + defer db.Close() + err = db.Ping() - return err + return err -} \ No newline at end of file +} diff --git a/routers/loginlog/login_log.go b/routers/loginlog/login_log.go index 1b6e1e2..e8c5a9f 100644 --- a/routers/loginlog/login_log.go +++ b/routers/loginlog/login_log.go @@ -1,30 +1,30 @@ package loginlog import ( - "gopkg.in/macaron.v1" - "github.com/Unknwon/paginater" - "fmt" - "github.com/ouqiang/gocron/modules/logger" - "github.com/ouqiang/gocron/models" - "github.com/ouqiang/gocron/routers/base" - "html/template" + "fmt" + "github.com/Unknwon/paginater" + "github.com/ouqiang/gocron/models" + "github.com/ouqiang/gocron/modules/logger" + "github.com/ouqiang/gocron/routers/base" + "gopkg.in/macaron.v1" + "html/template" ) -func Index(ctx *macaron.Context) { - loginLogModel := new(models.LoginLog) - params := models.CommonMap{} - base.ParsePageAndPageSize(ctx, params) - total, err := loginLogModel.Total() - loginLogs, err := loginLogModel.List(params) - if err != nil { - logger.Error(err) - } - PageParams := fmt.Sprintf("page_size=%d", params["PageSize"]); - params["PageParams"] = template.URL(PageParams) - p := paginater.New(int(total), params["PageSize"].(int), params["Page"].(int), 5) - ctx.Data["Pagination"] = p - ctx.Data["Title"] = "登录日志" - ctx.Data["LoginLogs"] = loginLogs - ctx.Data["Params"] = params - ctx.HTML(200, "manage/login_log") -} \ No newline at end of file +func Index(ctx *macaron.Context) { + loginLogModel := new(models.LoginLog) + params := models.CommonMap{} + base.ParsePageAndPageSize(ctx, params) + total, err := loginLogModel.Total() + loginLogs, err := loginLogModel.List(params) + if err != nil { + logger.Error(err) + } + PageParams := fmt.Sprintf("page_size=%d", params["PageSize"]) + params["PageParams"] = template.URL(PageParams) + p := paginater.New(int(total), params["PageSize"].(int), params["Page"].(int), 5) + ctx.Data["Pagination"] = p + ctx.Data["Title"] = "登录日志" + ctx.Data["LoginLogs"] = loginLogs + ctx.Data["Params"] = params + ctx.HTML(200, "manage/login_log") +} diff --git a/routers/manage/manage.go b/routers/manage/manage.go index 1ea6290..eb1637e 100644 --- a/routers/manage/manage.go +++ b/routers/manage/manage.go @@ -1,138 +1,136 @@ package manage import ( - "gopkg.in/macaron.v1" - "github.com/ouqiang/gocron/modules/utils" - "github.com/ouqiang/gocron/models" - "github.com/ouqiang/gocron/modules/logger" - "encoding/json" + "encoding/json" + "github.com/ouqiang/gocron/models" + "github.com/ouqiang/gocron/modules/logger" + "github.com/ouqiang/gocron/modules/utils" + "gopkg.in/macaron.v1" ) - // region slack -func EditSlack(ctx *macaron.Context) { - ctx.Data["Title"] = "Slack配置" - settingModel := new(models.Setting) - slack, err := settingModel.Slack() - if err != nil { - logger.Error(err) - } - ctx.Data["Slack"] = slack - ctx.HTML(200, "manage/slack") +func EditSlack(ctx *macaron.Context) { + ctx.Data["Title"] = "Slack配置" + settingModel := new(models.Setting) + slack, err := settingModel.Slack() + if err != nil { + logger.Error(err) + } + ctx.Data["Slack"] = slack + ctx.HTML(200, "manage/slack") } func Slack(ctx *macaron.Context) string { - settingModel := new(models.Setting) - slack, err := settingModel.Slack() - if err != nil { - logger.Error(err) - } - json := utils.JsonResponse{} + settingModel := new(models.Setting) + slack, err := settingModel.Slack() + if err != nil { + logger.Error(err) + } + json := utils.JsonResponse{} - return json.Success("", slack) + return json.Success("", slack) } func UpdateSlackUrl(ctx *macaron.Context) string { - url := ctx.QueryTrim("url") - settingModel := new(models.Setting) - _, err := settingModel.UpdateSlackUrl(url) + url := ctx.QueryTrim("url") + settingModel := new(models.Setting) + _, err := settingModel.UpdateSlackUrl(url) - return utils.JsonResponseByErr(err) + return utils.JsonResponseByErr(err) } -func CreateSlackChannel(ctx *macaron.Context) string { - channel := ctx.QueryTrim("channel") - settingModel := new(models.Setting) - if settingModel.IsChannelExist(channel) { - json := utils.JsonResponse{} +func CreateSlackChannel(ctx *macaron.Context) string { + channel := ctx.QueryTrim("channel") + settingModel := new(models.Setting) + if settingModel.IsChannelExist(channel) { + json := utils.JsonResponse{} - return json.CommonFailure("Channel已存在") - } - _, err := settingModel.CreateChannel(channel) + return json.CommonFailure("Channel已存在") + } + _, err := settingModel.CreateChannel(channel) - return utils.JsonResponseByErr(err) + return utils.JsonResponseByErr(err) } -func RemoveSlackChannel(ctx *macaron.Context) string { - id := ctx.ParamsInt(":id") - settingModel := new(models.Setting) - _, err := settingModel.RemoveChannel(id) +func RemoveSlackChannel(ctx *macaron.Context) string { + id := ctx.ParamsInt(":id") + settingModel := new(models.Setting) + _, err := settingModel.RemoveChannel(id) - return utils.JsonResponseByErr(err) + return utils.JsonResponseByErr(err) } // endregion // region 邮件 -func EditMail(ctx *macaron.Context) { - ctx.Data["Title"] = "邮件配置" - settingModel := new(models.Setting) - mail, err := settingModel.Mail() - if err != nil { - logger.Error(err) - } - ctx.Data["Mail"] = mail - ctx.HTML(200, "manage/mail") +func EditMail(ctx *macaron.Context) { + ctx.Data["Title"] = "邮件配置" + settingModel := new(models.Setting) + mail, err := settingModel.Mail() + if err != nil { + logger.Error(err) + } + ctx.Data["Mail"] = mail + ctx.HTML(200, "manage/mail") } func Mail(ctx *macaron.Context) string { - settingModel := new(models.Setting) - mail, err := settingModel.Mail() - if err != nil { - logger.Error(err) - } + settingModel := new(models.Setting) + mail, err := settingModel.Mail() + if err != nil { + logger.Error(err) + } - json := utils.JsonResponse{} + json := utils.JsonResponse{} - return json.Success("", mail) + return json.Success("", mail) } - type MailServerForm struct { - Host string `binding:"Required;MaxSize(100)"` - Port int `binding:"Required;Range(1-65535)"` - User string `binding:"Required;MaxSize(64);Email"` - Password string `binding:"Required;MaxSize(64)"` + Host string `binding:"Required;MaxSize(100)"` + Port int `binding:"Required;Range(1-65535)"` + User string `binding:"Required;MaxSize(64);Email"` + Password string `binding:"Required;MaxSize(64)"` } func UpdateMailServer(ctx *macaron.Context, form MailServerForm) string { - jsonByte, _ := json.Marshal(form) - settingModel := new(models.Setting) - _, err := settingModel.UpdateMailServer(string(jsonByte)) + jsonByte, _ := json.Marshal(form) + settingModel := new(models.Setting) + _, err := settingModel.UpdateMailServer(string(jsonByte)) - return utils.JsonResponseByErr(err) + return utils.JsonResponseByErr(err) } func ClearMailServer(ctx *macaron.Context) string { - jsonByte, _ := json.Marshal(MailServerForm{}) - settingModel := new(models.Setting) - _, err := settingModel.UpdateMailServer(string(jsonByte)) + jsonByte, _ := json.Marshal(MailServerForm{}) + settingModel := new(models.Setting) + _, err := settingModel.UpdateMailServer(string(jsonByte)) - return utils.JsonResponseByErr(err) + return utils.JsonResponseByErr(err) } -func CreateMailUser(ctx *macaron.Context) string { - username := ctx.QueryTrim("username") - email := ctx.QueryTrim("email") - settingModel := new(models.Setting) - if username == "" || email == "" { - json := utils.JsonResponse{} +func CreateMailUser(ctx *macaron.Context) string { + username := ctx.QueryTrim("username") + email := ctx.QueryTrim("email") + settingModel := new(models.Setting) + if username == "" || email == "" { + json := utils.JsonResponse{} - return json.CommonFailure("用户名、邮箱均不能为空") - } - _, err := settingModel.CreateMailUser(username, email) + return json.CommonFailure("用户名、邮箱均不能为空") + } + _, err := settingModel.CreateMailUser(username, email) - return utils.JsonResponseByErr(err) + return utils.JsonResponseByErr(err) } -func RemoveMailUser(ctx *macaron.Context) string { - id := ctx.ParamsInt(":id") - settingModel := new(models.Setting) - _, err := settingModel.RemoveMailUser(id) +func RemoveMailUser(ctx *macaron.Context) string { + id := ctx.ParamsInt(":id") + settingModel := new(models.Setting) + _, err := settingModel.RemoveMailUser(id) - return utils.JsonResponseByErr(err) + return utils.JsonResponseByErr(err) } -// endregion \ No newline at end of file +// endregion diff --git a/routers/routers.go b/routers/routers.go index 4fd7a7f..1238dc5 100644 --- a/routers/routers.go +++ b/routers/routers.go @@ -1,27 +1,27 @@ package routers import ( - "github.com/go-macaron/binding" - "github.com/ouqiang/gocron/routers/install" - "gopkg.in/macaron.v1" - "github.com/ouqiang/gocron/routers/task" - "github.com/ouqiang/gocron/routers/host" - "github.com/ouqiang/gocron/routers/tasklog" - "github.com/ouqiang/gocron/modules/utils" - "github.com/go-macaron/session" - "github.com/go-macaron/toolbox" - "strings" - "github.com/ouqiang/gocron/modules/app" - "github.com/ouqiang/gocron/modules/logger" - "github.com/ouqiang/gocron/routers/user" - "github.com/go-macaron/gzip" - "github.com/ouqiang/gocron/routers/manage" - "github.com/ouqiang/gocron/routers/loginlog" - "time" - "strconv" - "html/template" - "github.com/go-macaron/cache" - "github.com/go-macaron/captcha" + "github.com/go-macaron/binding" + "github.com/go-macaron/cache" + "github.com/go-macaron/captcha" + "github.com/go-macaron/gzip" + "github.com/go-macaron/session" + "github.com/go-macaron/toolbox" + "github.com/ouqiang/gocron/modules/app" + "github.com/ouqiang/gocron/modules/logger" + "github.com/ouqiang/gocron/modules/utils" + "github.com/ouqiang/gocron/routers/host" + "github.com/ouqiang/gocron/routers/install" + "github.com/ouqiang/gocron/routers/loginlog" + "github.com/ouqiang/gocron/routers/manage" + "github.com/ouqiang/gocron/routers/task" + "github.com/ouqiang/gocron/routers/tasklog" + "github.com/ouqiang/gocron/routers/user" + "gopkg.in/macaron.v1" + "html/template" + "strconv" + "strings" + "time" ) // 静态文件目录 @@ -29,261 +29,261 @@ const StaticDir = "public" // 路由注册 func Register(m *macaron.Macaron) { - // 所有GET方法,自动注册HEAD方法 - m.SetAutoHead(true) - // 首页 - m.Get("/", Home) - // 系统安装 - m.Group("/install", func() { - m.Get("", install.Create) - m.Post("/store", binding.Bind(install.InstallForm{}), install.Store) - }) + // 所有GET方法,自动注册HEAD方法 + m.SetAutoHead(true) + // 首页 + m.Get("/", Home) + // 系统安装 + m.Group("/install", func() { + m.Get("", install.Create) + m.Post("/store", binding.Bind(install.InstallForm{}), install.Store) + }) - // 用户 - m.Group("/user", func() { - m.Get("/login", user.Login) - m.Post("/login", user.ValidateLogin) - m.Get("/logout", user.Logout) - m.Get("/editPassword", user.EditPassword) - m.Post("/editPassword", user.UpdatePassword) - }) + // 用户 + m.Group("/user", func() { + m.Get("/login", user.Login) + m.Post("/login", user.ValidateLogin) + m.Get("/logout", user.Logout) + m.Get("/editPassword", user.EditPassword) + m.Post("/editPassword", user.UpdatePassword) + }) - // 定时任务 - m.Group("/task", func() { - m.Get("/create", task.Create) - m.Post("/store", binding.Bind(task.TaskForm{}), task.Store) - m.Get("/edit/:id", task.Edit) - m.Get("", task.Index) - m.Get("/log", tasklog.Index) - m.Post("/log/clear", tasklog.Clear) - m.Post("/remove/:id", task.Remove) - m.Post("/enable/:id", task.Enable) - m.Post("/disable/:id", task.Disable) - m.Get("/run/:id", task.Run) - }) + // 定时任务 + m.Group("/task", func() { + m.Get("/create", task.Create) + m.Post("/store", binding.Bind(task.TaskForm{}), task.Store) + m.Get("/edit/:id", task.Edit) + m.Get("", task.Index) + m.Get("/log", tasklog.Index) + m.Post("/log/clear", tasklog.Clear) + m.Post("/remove/:id", task.Remove) + m.Post("/enable/:id", task.Enable) + m.Post("/disable/:id", task.Disable) + m.Get("/run/:id", task.Run) + }) - // 主机 - m.Group("/host", func() { - m.Get("/create", host.Create) - m.Get("/edit/:id", host.Edit) - m.Post("/store", binding.Bind(host.HostForm{}), host.Store) - m.Get("", host.Index) - m.Get("/ping/:id", host.Ping) - m.Post("/remove/:id", host.Remove) - }) + // 主机 + m.Group("/host", func() { + m.Get("/create", host.Create) + m.Get("/edit/:id", host.Edit) + m.Post("/store", binding.Bind(host.HostForm{}), host.Store) + m.Get("", host.Index) + m.Get("/ping/:id", host.Ping) + m.Post("/remove/:id", host.Remove) + }) - // 管理 - m.Group("/manage", func() { - m.Group("/slack", func() { - m.Get("/", manage.Slack) - m.Get("/edit", manage.EditSlack) - m.Post("/url", manage.UpdateSlackUrl) - m.Post("/channel", manage.CreateSlackChannel) - m.Post("/channel/remove/:id", manage.RemoveSlackChannel) - }) - m.Group("/mail", func() { - m.Get("/", manage.Mail) - m.Get("/edit", manage.EditMail) - m.Post("/server", binding.Bind(manage.MailServerForm{}), manage.UpdateMailServer) - m.Post("/server/clear", manage.ClearMailServer) - m.Post("/user", manage.CreateMailUser) - m.Post("/user/remove/:id", manage.RemoveMailUser) - }) - m.Get("/login-log", loginlog.Index) - }) + // 管理 + m.Group("/manage", func() { + m.Group("/slack", func() { + m.Get("/", manage.Slack) + m.Get("/edit", manage.EditSlack) + m.Post("/url", manage.UpdateSlackUrl) + m.Post("/channel", manage.CreateSlackChannel) + m.Post("/channel/remove/:id", manage.RemoveSlackChannel) + }) + m.Group("/mail", func() { + m.Get("/", manage.Mail) + m.Get("/edit", manage.EditMail) + m.Post("/server", binding.Bind(manage.MailServerForm{}), manage.UpdateMailServer) + m.Post("/server/clear", manage.ClearMailServer) + m.Post("/user", manage.CreateMailUser) + m.Post("/user/remove/:id", manage.RemoveMailUser) + }) + m.Get("/login-log", loginlog.Index) + }) - // API - m.Group("/api/v1", func() { - m.Post("/tasklog/remove/:id", tasklog.Remove) - m.Post("/task/enable/:id", task.Enable) - m.Post("/task/disable/:id", task.Disable) - }, apiAuth); + // API + m.Group("/api/v1", func() { + m.Post("/tasklog/remove/:id", tasklog.Remove) + m.Post("/task/enable/:id", task.Enable) + m.Post("/task/disable/:id", task.Disable) + }, apiAuth) - // 404错误 - m.NotFound(func(ctx *macaron.Context) { - if isGetRequest(ctx) && !isAjaxRequest(ctx) { - ctx.Data["Title"] = "404 - NOT FOUND" - ctx.HTML(404, "error/404") - } else { - json := utils.JsonResponse{} - ctx.Resp.Write([]byte(json.Failure(utils.NotFound, "您访问的地址不存在"))) - } - }) - // 50x错误 - m.InternalServerError(func(ctx *macaron.Context) { - logger.Debug("500错误") - if isGetRequest(ctx) && !isAjaxRequest(ctx) { - ctx.Data["Title"] = "500 - INTERNAL SERVER ERROR" - ctx.HTML(500, "error/500") - } else { - json := utils.JsonResponse{} - ctx.Resp.Write([]byte(json.Failure(utils.ServerError, "网站暂时无法访问,请稍后再试"))) - } - }) + // 404错误 + m.NotFound(func(ctx *macaron.Context) { + if isGetRequest(ctx) && !isAjaxRequest(ctx) { + ctx.Data["Title"] = "404 - NOT FOUND" + ctx.HTML(404, "error/404") + } else { + json := utils.JsonResponse{} + ctx.Resp.Write([]byte(json.Failure(utils.NotFound, "您访问的地址不存在"))) + } + }) + // 50x错误 + m.InternalServerError(func(ctx *macaron.Context) { + logger.Debug("500错误") + if isGetRequest(ctx) && !isAjaxRequest(ctx) { + ctx.Data["Title"] = "500 - INTERNAL SERVER ERROR" + ctx.HTML(500, "error/500") + } else { + json := utils.JsonResponse{} + ctx.Resp.Write([]byte(json.Failure(utils.ServerError, "网站暂时无法访问,请稍后再试"))) + } + }) } // 中间件注册 func RegisterMiddleware(m *macaron.Macaron) { - m.Use(macaron.Logger()) - m.Use(macaron.Recovery()) - if macaron.Env != macaron.DEV { - m.Use(gzip.Gziper()) - } - m.Use(macaron.Static(StaticDir)) - m.Use(macaron.Renderer(macaron.RenderOptions{ - Directory: "templates", - Extensions: []string{".html"}, - // 模板语法分隔符,默认为 ["{{", "}}"] - Delims: macaron.Delims{"{{{", "}}}"}, - // 追加的 Content-Type 头信息,默认为 "UTF-8" - Charset: "UTF-8", - // 渲染具有缩进格式的 JSON,默认为不缩进 - IndentJSON: true, - // 渲染具有缩进格式的 XML,默认为不缩进 - IndentXML: true, - Funcs: []template.FuncMap{map[string]interface{} { - "HostFormat": func(index int) bool { - return (index + 1) % 3 == 0 - }, - "unescape": func(str string) template.HTML { - return template.HTML(str) - }, - }}, - })) - m.Use(cache.Cacher()) - m.Use(captcha.Captchaer()) - m.Use(session.Sessioner(session.Options{ - Provider: "file", - ProviderConfig: app.DataDir + "/sessions", - })) - m.Use(toolbox.Toolboxer(m)) - checkAppInstall(m) - m.Use(func(ctx *macaron.Context, sess session.Store){ - if app.Installed { - ipAuth(ctx) - userAuth(ctx, sess) - setShareData(ctx, sess) - } - }) + m.Use(macaron.Logger()) + m.Use(macaron.Recovery()) + if macaron.Env != macaron.DEV { + m.Use(gzip.Gziper()) + } + m.Use(macaron.Static(StaticDir)) + m.Use(macaron.Renderer(macaron.RenderOptions{ + Directory: "templates", + Extensions: []string{".html"}, + // 模板语法分隔符,默认为 ["{{", "}}"] + Delims: macaron.Delims{"{{{", "}}}"}, + // 追加的 Content-Type 头信息,默认为 "UTF-8" + Charset: "UTF-8", + // 渲染具有缩进格式的 JSON,默认为不缩进 + IndentJSON: true, + // 渲染具有缩进格式的 XML,默认为不缩进 + IndentXML: true, + Funcs: []template.FuncMap{map[string]interface{}{ + "HostFormat": func(index int) bool { + return (index+1)%3 == 0 + }, + "unescape": func(str string) template.HTML { + return template.HTML(str) + }, + }}, + })) + m.Use(cache.Cacher()) + m.Use(captcha.Captchaer()) + m.Use(session.Sessioner(session.Options{ + Provider: "file", + ProviderConfig: app.DataDir + "/sessions", + })) + m.Use(toolbox.Toolboxer(m)) + checkAppInstall(m) + m.Use(func(ctx *macaron.Context, sess session.Store) { + if app.Installed { + ipAuth(ctx) + userAuth(ctx, sess) + setShareData(ctx, sess) + } + }) } // region 自定义中间件 /** 系统未安装,重定向到安装页面 **/ -func checkAppInstall(m *macaron.Macaron) { - m.Use(func(ctx *macaron.Context) { - installUrl := "/install" - if strings.HasPrefix(ctx.Req.URL.Path, installUrl) { - return - } - if !app.Installed { - ctx.Redirect(installUrl) - } - }) +func checkAppInstall(m *macaron.Macaron) { + m.Use(func(ctx *macaron.Context) { + installUrl := "/install" + if strings.HasPrefix(ctx.Req.URL.Path, installUrl) { + return + } + if !app.Installed { + ctx.Redirect(installUrl) + } + }) } // IP验证, 通过反向代理访问gocron,需设置Header X-Real-IP才能获取到客户端真实IP -func ipAuth(ctx *macaron.Context) { - allowIpsStr := app.Setting.AllowIps - if allowIpsStr == "" { - return - } - clientIp := ctx.RemoteAddr() - allowIps := strings.Split(allowIpsStr, ",") - if !utils.InStringSlice(allowIps, clientIp) { - logger.Warnf("非法IP访问-%s", clientIp) - ctx.Status(403) - } +func ipAuth(ctx *macaron.Context) { + allowIpsStr := app.Setting.AllowIps + if allowIpsStr == "" { + return + } + clientIp := ctx.RemoteAddr() + allowIps := strings.Split(allowIpsStr, ",") + if !utils.InStringSlice(allowIps, clientIp) { + logger.Warnf("非法IP访问-%s", clientIp) + ctx.Status(403) + } } // 用户认证 -func userAuth(ctx *macaron.Context, sess session.Store) { - if user.IsLogin(sess) { - return - } - uri := ctx.Req.URL.Path - found := false - excludePaths := []string{"/install", "/user/login", "/api"} - for _, path := range excludePaths { - if strings.HasPrefix(uri, path) { - found = true - break - } - } - if !found { - ctx.Redirect("/user/login") - } +func userAuth(ctx *macaron.Context, sess session.Store) { + if user.IsLogin(sess) { + return + } + uri := ctx.Req.URL.Path + found := false + excludePaths := []string{"/install", "/user/login", "/api"} + for _, path := range excludePaths { + if strings.HasPrefix(uri, path) { + found = true + break + } + } + if !found { + ctx.Redirect("/user/login") + } } /** 设置共享数据 **/ -func setShareData(ctx *macaron.Context, sess session.Store) { - ctx.Data["URI"] = ctx.Req.URL.Path - urlPath := strings.TrimPrefix(ctx.Req.URL.Path, "/") - paths := strings.Split(urlPath, "/") - ctx.Data["Controller"] = "" - ctx.Data["Action"] = "" - if len(paths) > 0 { - ctx.Data["Controller"] = paths[0] - } - if len(paths) > 1 { - ctx.Data["Action"] = paths[1] - } - ctx.Data["LoginUsername"] = user.Username(sess) - ctx.Data["LoginUid"] = user.Uid(sess) - ctx.Data["AppName"] = app.Setting.AppName +func setShareData(ctx *macaron.Context, sess session.Store) { + ctx.Data["URI"] = ctx.Req.URL.Path + urlPath := strings.TrimPrefix(ctx.Req.URL.Path, "/") + paths := strings.Split(urlPath, "/") + ctx.Data["Controller"] = "" + ctx.Data["Action"] = "" + if len(paths) > 0 { + ctx.Data["Controller"] = paths[0] + } + if len(paths) > 1 { + ctx.Data["Action"] = paths[1] + } + ctx.Data["LoginUsername"] = user.Username(sess) + ctx.Data["LoginUid"] = user.Uid(sess) + ctx.Data["AppName"] = app.Setting.AppName } /** API接口签名验证 **/ -func apiAuth(ctx *macaron.Context) { - if !app.Setting.ApiSignEnable { - return - } - apiKey := strings.TrimSpace(app.Setting.ApiKey) - apiSecret := strings.TrimSpace(app.Setting.ApiSecret) - json := utils.JsonResponse{} - if apiKey == "" || apiSecret == "" { - msg := json.CommonFailure("使用API前, 请先配置密钥") - ctx.Write([]byte(msg)) - return - } - currentTimestamp := time.Now().Unix() - time := ctx.QueryInt64("time") - if time <= 0 { - msg := json.CommonFailure("参数time不能为空") - ctx.Write([]byte(msg)) - return - } - if time < (currentTimestamp - 1800) { - msg := json.CommonFailure("time无效") - ctx.Write([]byte(msg)) - return - } - sign := ctx.QueryTrim("sign") - if sign == "" { - msg := json.CommonFailure("参数sign不能为空") - ctx.Write([]byte(msg)) - return - } - raw := apiKey + strconv.FormatInt(time, 10) + strings.TrimSpace(ctx.Req.URL.Path) + apiSecret - realSign := utils.Md5(raw) - if sign != realSign { - msg := json.CommonFailure("签名验证失败") - ctx.Write([]byte(msg)) - return - } +func apiAuth(ctx *macaron.Context) { + if !app.Setting.ApiSignEnable { + return + } + apiKey := strings.TrimSpace(app.Setting.ApiKey) + apiSecret := strings.TrimSpace(app.Setting.ApiSecret) + json := utils.JsonResponse{} + if apiKey == "" || apiSecret == "" { + msg := json.CommonFailure("使用API前, 请先配置密钥") + ctx.Write([]byte(msg)) + return + } + currentTimestamp := time.Now().Unix() + time := ctx.QueryInt64("time") + if time <= 0 { + msg := json.CommonFailure("参数time不能为空") + ctx.Write([]byte(msg)) + return + } + if time < (currentTimestamp - 1800) { + msg := json.CommonFailure("time无效") + ctx.Write([]byte(msg)) + return + } + sign := ctx.QueryTrim("sign") + if sign == "" { + msg := json.CommonFailure("参数sign不能为空") + ctx.Write([]byte(msg)) + return + } + raw := apiKey + strconv.FormatInt(time, 10) + strings.TrimSpace(ctx.Req.URL.Path) + apiSecret + realSign := utils.Md5(raw) + if sign != realSign { + msg := json.CommonFailure("签名验证失败") + ctx.Write([]byte(msg)) + return + } } // endregion func isAjaxRequest(ctx *macaron.Context) bool { - req := ctx.Req.Header.Get("X-Requested-With") - if req == "XMLHttpRequest" { - return true - } + req := ctx.Req.Header.Get("X-Requested-With") + if req == "XMLHttpRequest" { + return true + } - return false + return false } func isGetRequest(ctx *macaron.Context) bool { - return ctx.Req.Method == "GET" -} \ No newline at end of file + return ctx.Req.Method == "GET" +} diff --git a/routers/task/task.go b/routers/task/task.go index b35cd8d..f8f6cc3 100644 --- a/routers/task/task.go +++ b/routers/task/task.go @@ -1,335 +1,334 @@ package task import ( - "gopkg.in/macaron.v1" - "github.com/ouqiang/gocron/models" - "github.com/ouqiang/gocron/modules/logger" - "github.com/ouqiang/gocron/modules/utils" - "github.com/ouqiang/gocron/service" - "strconv" - "github.com/jakecoffman/cron" - "github.com/Unknwon/paginater" - "fmt" - "html/template" - "github.com/ouqiang/gocron/routers/base" - "github.com/go-macaron/binding" - "strings" + "fmt" + "github.com/Unknwon/paginater" + "github.com/go-macaron/binding" + "github.com/jakecoffman/cron" + "github.com/ouqiang/gocron/models" + "github.com/ouqiang/gocron/modules/logger" + "github.com/ouqiang/gocron/modules/utils" + "github.com/ouqiang/gocron/routers/base" + "github.com/ouqiang/gocron/service" + "gopkg.in/macaron.v1" + "html/template" + "strconv" + "strings" ) type TaskForm struct { - Id int - Level models.TaskLevel `binding:"Required;In(1,2)"` - DependencyStatus models.TaskDependencyStatus - DependencyTaskId string - Name string `binding:"Required;MaxSize(32)"` - Spec string - Protocol models.TaskProtocol `binding:"In(1,2)"` - Command string `binding:"Required;MaxSize(256)"` - Timeout int `binding:"Range(0,86400)"` - Multi int8 `binding:"In(1,2)"` - RetryTimes int8 - HostId string - Tag string - Remark string - NotifyStatus int8 `binding:"In(1,2,3)"` - NotifyType int8 `binding:"In(1,2,3)"` - NotifyReceiverId string + Id int + Level models.TaskLevel `binding:"Required;In(1,2)"` + DependencyStatus models.TaskDependencyStatus + DependencyTaskId string + Name string `binding:"Required;MaxSize(32)"` + Spec string + Protocol models.TaskProtocol `binding:"In(1,2)"` + Command string `binding:"Required;MaxSize(256)"` + Timeout int `binding:"Range(0,86400)"` + Multi int8 `binding:"In(1,2)"` + RetryTimes int8 + HostId string + Tag string + Remark string + NotifyStatus int8 `binding:"In(1,2,3)"` + NotifyType int8 `binding:"In(1,2,3)"` + NotifyReceiverId string } - func (f TaskForm) Error(ctx *macaron.Context, errs binding.Errors) { - if len(errs) == 0 { - return - } - json := utils.JsonResponse{} - content := json.CommonFailure("表单验证失败, 请检测输入") + if len(errs) == 0 { + return + } + json := utils.JsonResponse{} + content := json.CommonFailure("表单验证失败, 请检测输入") - ctx.Resp.Write([]byte(content)) + ctx.Resp.Write([]byte(content)) } // 首页 -func Index(ctx *macaron.Context) { - taskModel := new(models.Task) - queryParams := parseQueryParams(ctx) - total, err := taskModel.Total(queryParams) - if err != nil { - logger.Error(err) - } - tasks, err := taskModel.List(queryParams) - if err != nil { - logger.Error(err) - } - name, ok := queryParams["name"].(string) - var safeNameHTML = "" - if ok { - safeNameHTML = template.HTMLEscapeString(name) - } - PageParams := fmt.Sprintf("id=%d&host_id=%d&name=%s&protocol=%d&tag=%s&status=%d&page_size=%d", - queryParams["Id"], queryParams["HostId"], safeNameHTML, queryParams["Protocol"], queryParams["Tag"], queryParams["Status"], queryParams["PageSize"]); - queryParams["PageParams"] = template.URL(PageParams) - p := paginater.New(int(total), queryParams["PageSize"].(int), queryParams["Page"].(int), 5) - ctx.Data["Pagination"] = p - setHostsToTemplate(ctx) - ctx.Data["Params"] = queryParams - ctx.Data["Title"] = "任务列表" - ctx.Data["Tasks"] = tasks - ctx.HTML(200, "task/index") +func Index(ctx *macaron.Context) { + taskModel := new(models.Task) + queryParams := parseQueryParams(ctx) + total, err := taskModel.Total(queryParams) + if err != nil { + logger.Error(err) + } + tasks, err := taskModel.List(queryParams) + if err != nil { + logger.Error(err) + } + name, ok := queryParams["name"].(string) + var safeNameHTML = "" + if ok { + safeNameHTML = template.HTMLEscapeString(name) + } + PageParams := fmt.Sprintf("id=%d&host_id=%d&name=%s&protocol=%d&tag=%s&status=%d&page_size=%d", + queryParams["Id"], queryParams["HostId"], safeNameHTML, queryParams["Protocol"], queryParams["Tag"], queryParams["Status"], queryParams["PageSize"]) + queryParams["PageParams"] = template.URL(PageParams) + p := paginater.New(int(total), queryParams["PageSize"].(int), queryParams["Page"].(int), 5) + ctx.Data["Pagination"] = p + setHostsToTemplate(ctx) + ctx.Data["Params"] = queryParams + ctx.Data["Title"] = "任务列表" + ctx.Data["Tasks"] = tasks + ctx.HTML(200, "task/index") } // 新增页面 -func Create(ctx *macaron.Context) { - setHostsToTemplate(ctx) - ctx.Data["Title"] = "添加任务" - ctx.HTML(200, "task/task_form") +func Create(ctx *macaron.Context) { + setHostsToTemplate(ctx) + ctx.Data["Title"] = "添加任务" + ctx.HTML(200, "task/task_form") } // 编辑页面 -func Edit(ctx *macaron.Context) { - id := ctx.ParamsInt(":id") - taskModel := new(models.Task) - task, err := taskModel.Detail(id) - if err != nil || task.Id != id { - logger.Errorf("编辑任务#获取任务详情失败#任务ID-%d#%s", id, err.Error()) - ctx.Redirect("/task") - } - hostModel := new(models.Host) - hostModel.PageSize = -1 - hosts, err := hostModel.List(models.CommonMap{}) - if err != nil { - logger.Error(err) - } else { - for i, host := range(hosts) { - if inHosts(task.Hosts, host.Id) { - hosts[i].Selected = true - } - } - } +func Edit(ctx *macaron.Context) { + id := ctx.ParamsInt(":id") + taskModel := new(models.Task) + task, err := taskModel.Detail(id) + if err != nil || task.Id != id { + logger.Errorf("编辑任务#获取任务详情失败#任务ID-%d#%s", id, err.Error()) + ctx.Redirect("/task") + } + hostModel := new(models.Host) + hostModel.PageSize = -1 + hosts, err := hostModel.List(models.CommonMap{}) + if err != nil { + logger.Error(err) + } else { + for i, host := range hosts { + if inHosts(task.Hosts, host.Id) { + hosts[i].Selected = true + } + } + } - ctx.Data["Task"] = task - ctx.Data["Hosts"] = hosts - ctx.Data["Title"] = "编辑" - ctx.HTML(200, "task/task_form") + ctx.Data["Task"] = task + ctx.Data["Hosts"] = hosts + ctx.Data["Title"] = "编辑" + ctx.HTML(200, "task/task_form") } // 保存任务 -func Store(ctx *macaron.Context, form TaskForm) string { - json := utils.JsonResponse{} - taskModel := models.Task{} - var id int = form.Id - nameExists, err := taskModel.NameExist(form.Name, form.Id) - if err != nil { - return json.CommonFailure(utils.FailureContent, err) - } - if nameExists { - return json.CommonFailure("任务名称已存在") - } +func Store(ctx *macaron.Context, form TaskForm) string { + json := utils.JsonResponse{} + taskModel := models.Task{} + var id int = form.Id + nameExists, err := taskModel.NameExist(form.Name, form.Id) + if err != nil { + return json.CommonFailure(utils.FailureContent, err) + } + if nameExists { + return json.CommonFailure("任务名称已存在") + } - if form.Protocol == models.TaskRPC && form.HostId == "" { - return json.CommonFailure("请选择主机名") - } + if form.Protocol == models.TaskRPC && form.HostId == "" { + return json.CommonFailure("请选择主机名") + } - taskModel.Name = form.Name - taskModel.Protocol = form.Protocol - taskModel.Command = form.Command - taskModel.Timeout = form.Timeout - taskModel.Tag = form.Tag - taskModel.Remark = form.Remark - taskModel.Multi = form.Multi - taskModel.RetryTimes = form.RetryTimes - if taskModel.Multi != 1 { - taskModel.Multi = 0 - } - taskModel.NotifyStatus = form.NotifyStatus - 1 - taskModel.NotifyType = form.NotifyType - 1 - taskModel.NotifyReceiverId = form.NotifyReceiverId - taskModel.Spec = form.Spec - taskModel.Level = form.Level - taskModel.DependencyStatus = form.DependencyStatus - taskModel.DependencyTaskId = strings.TrimSpace(form.DependencyTaskId) - if taskModel.NotifyStatus > 0 && taskModel.NotifyReceiverId == "" { - return json.CommonFailure("至少选择一个通知接收者") - } - if taskModel.Protocol == models.TaskHTTP { - command := strings.ToLower(taskModel.Command) - if !strings.HasPrefix(command, "http://") && !strings.HasPrefix(command, "https://") { - return json.CommonFailure("请输入正确的URL地址") - } - if taskModel.Timeout > 300 { - return json.CommonFailure("HTTP任务超时时间不能超过300秒") - } - } + taskModel.Name = form.Name + taskModel.Protocol = form.Protocol + taskModel.Command = form.Command + taskModel.Timeout = form.Timeout + taskModel.Tag = form.Tag + taskModel.Remark = form.Remark + taskModel.Multi = form.Multi + taskModel.RetryTimes = form.RetryTimes + if taskModel.Multi != 1 { + taskModel.Multi = 0 + } + taskModel.NotifyStatus = form.NotifyStatus - 1 + taskModel.NotifyType = form.NotifyType - 1 + taskModel.NotifyReceiverId = form.NotifyReceiverId + taskModel.Spec = form.Spec + taskModel.Level = form.Level + taskModel.DependencyStatus = form.DependencyStatus + taskModel.DependencyTaskId = strings.TrimSpace(form.DependencyTaskId) + if taskModel.NotifyStatus > 0 && taskModel.NotifyReceiverId == "" { + return json.CommonFailure("至少选择一个通知接收者") + } + if taskModel.Protocol == models.TaskHTTP { + command := strings.ToLower(taskModel.Command) + if !strings.HasPrefix(command, "http://") && !strings.HasPrefix(command, "https://") { + return json.CommonFailure("请输入正确的URL地址") + } + if taskModel.Timeout > 300 { + return json.CommonFailure("HTTP任务超时时间不能超过300秒") + } + } - if taskModel.RetryTimes > 10 || taskModel.RetryTimes < 0 { - return json.CommonFailure("任务重试次数取值0-10") - } + if taskModel.RetryTimes > 10 || taskModel.RetryTimes < 0 { + return json.CommonFailure("任务重试次数取值0-10") + } - if (taskModel.DependencyStatus != models.TaskDependencyStatusStrong && - taskModel.DependencyStatus != models.TaskDependencyStatusWeak) { - return json.CommonFailure("请选择依赖关系") - } + if taskModel.DependencyStatus != models.TaskDependencyStatusStrong && + taskModel.DependencyStatus != models.TaskDependencyStatusWeak { + return json.CommonFailure("请选择依赖关系") + } - if taskModel.Level == models.TaskLevelParent { - _, err = cron.Parse(form.Spec) - if err != nil { - return json.CommonFailure("crontab表达式解析失败", err) - } - } else { - taskModel.DependencyTaskId = "" - taskModel.Spec = "" - } + if taskModel.Level == models.TaskLevelParent { + _, err = cron.Parse(form.Spec) + if err != nil { + return json.CommonFailure("crontab表达式解析失败", err) + } + } else { + taskModel.DependencyTaskId = "" + taskModel.Spec = "" + } - if id > 0 && taskModel.DependencyTaskId != "" { - dependencyTaskIds := strings.Split(taskModel.DependencyTaskId, ",") - if utils.InStringSlice(dependencyTaskIds, strconv.Itoa(id)) { - return json.CommonFailure("不允许设置当前任务为子任务") - } - } + if id > 0 && taskModel.DependencyTaskId != "" { + dependencyTaskIds := strings.Split(taskModel.DependencyTaskId, ",") + if utils.InStringSlice(dependencyTaskIds, strconv.Itoa(id)) { + return json.CommonFailure("不允许设置当前任务为子任务") + } + } - if id == 0 { - // 任务添加后开始调度执行 - taskModel.Status = models.Running - id, err = taskModel.Create() - } else { - _, err = taskModel.UpdateBean(id) - } + if id == 0 { + // 任务添加后开始调度执行 + taskModel.Status = models.Running + id, err = taskModel.Create() + } else { + _, err = taskModel.UpdateBean(id) + } - if err != nil { - return json.CommonFailure("保存失败", err) - } + if err != nil { + return json.CommonFailure("保存失败", err) + } - taskHostModel := new(models.TaskHost) - if form.Protocol == models.TaskRPC { - hostIdStrList := strings.Split(form.HostId, ",") - hostIds := make([]int, len(hostIdStrList)) - for i, hostIdStr := range hostIdStrList { - hostIds[i], _ = strconv.Atoi(hostIdStr) - } - taskHostModel.Add(id, hostIds) - } else { - taskHostModel.Remove(id) - } + taskHostModel := new(models.TaskHost) + if form.Protocol == models.TaskRPC { + hostIdStrList := strings.Split(form.HostId, ",") + hostIds := make([]int, len(hostIdStrList)) + for i, hostIdStr := range hostIdStrList { + hostIds[i], _ = strconv.Atoi(hostIdStr) + } + taskHostModel.Add(id, hostIds) + } else { + taskHostModel.Remove(id) + } - status, err := taskModel.GetStatus(id) - if status == models.Enabled && taskModel.Level == models.TaskLevelParent { - addTaskToTimer(id) - } + status, err := taskModel.GetStatus(id) + if status == models.Enabled && taskModel.Level == models.TaskLevelParent { + addTaskToTimer(id) + } - return json.Success("保存成功", nil) + return json.Success("保存成功", nil) } // 删除任务 func Remove(ctx *macaron.Context) string { - id := ctx.ParamsInt(":id") - json := utils.JsonResponse{} - taskModel := new(models.Task) - _, err := taskModel.Delete(id) - if err != nil { - return json.CommonFailure(utils.FailureContent, err) - } + id := ctx.ParamsInt(":id") + json := utils.JsonResponse{} + taskModel := new(models.Task) + _, err := taskModel.Delete(id) + if err != nil { + return json.CommonFailure(utils.FailureContent, err) + } - taskHostModel := new(models.TaskHost) - taskHostModel.Remove(id) + taskHostModel := new(models.TaskHost) + taskHostModel.Remove(id) - service.Cron.RemoveJob(strconv.Itoa(id)) + service.Cron.RemoveJob(strconv.Itoa(id)) - return json.Success(utils.SuccessContent, nil) + return json.Success(utils.SuccessContent, nil) } // 激活任务 func Enable(ctx *macaron.Context) string { - return changeStatus(ctx, models.Enabled) + return changeStatus(ctx, models.Enabled) } // 暂停任务 func Disable(ctx *macaron.Context) string { - return changeStatus(ctx, models.Disabled) + return changeStatus(ctx, models.Disabled) } // 手动运行任务 func Run(ctx *macaron.Context) string { - id := ctx.ParamsInt(":id") - json := utils.JsonResponse{} - taskModel := new(models.Task) - task , err := taskModel.Detail(id) - if err != nil || task.Id <= 0 { - return json.CommonFailure("获取任务详情失败", err) - } + id := ctx.ParamsInt(":id") + json := utils.JsonResponse{} + taskModel := new(models.Task) + task, err := taskModel.Detail(id) + if err != nil || task.Id <= 0 { + return json.CommonFailure("获取任务详情失败", err) + } - task.Spec = "手动运行" - serviceTask := new(service.Task) - serviceTask.Run(task) + task.Spec = "手动运行" + serviceTask := new(service.Task) + serviceTask.Run(task) - return json.Success("任务已开始运行, 请到任务日志中查看结果", nil); + return json.Success("任务已开始运行, 请到任务日志中查看结果", nil) } // 改变任务状态 func changeStatus(ctx *macaron.Context, status models.Status) string { - id := ctx.ParamsInt(":id") - json := utils.JsonResponse{} - taskModel := new(models.Task) - _, err := taskModel.Update(id, models.CommonMap{ - "Status": status, - }) - if err != nil { - return json.CommonFailure(utils.FailureContent, err) - } + id := ctx.ParamsInt(":id") + json := utils.JsonResponse{} + taskModel := new(models.Task) + _, err := taskModel.Update(id, models.CommonMap{ + "Status": status, + }) + if err != nil { + return json.CommonFailure(utils.FailureContent, err) + } - if status == models.Enabled { - addTaskToTimer(id) - } else { - service.Cron.RemoveJob(strconv.Itoa(id)) - } + if status == models.Enabled { + addTaskToTimer(id) + } else { + service.Cron.RemoveJob(strconv.Itoa(id)) + } - return json.Success(utils.SuccessContent, nil) + return json.Success(utils.SuccessContent, nil) } // 添加任务到定时器 -func addTaskToTimer(id int) { - taskModel := new(models.Task) - task, err := taskModel.Detail(id) - if err != nil { - logger.Error(err) - return - } +func addTaskToTimer(id int) { + taskModel := new(models.Task) + task, err := taskModel.Detail(id) + if err != nil { + logger.Error(err) + return + } - taskService := service.Task{} - taskService.Add(task) + taskService := service.Task{} + taskService.Add(task) } // 解析查询参数 -func parseQueryParams(ctx *macaron.Context) (models.CommonMap) { - var params models.CommonMap = models.CommonMap{} - params["Id"] = ctx.QueryInt("id") - params["HostId"] = ctx.QueryInt("host_id") - params["Name"] = ctx.QueryTrim("name") - params["Protocol"] = ctx.QueryInt("protocol") - params["Tag"] = ctx.QueryTrim("tag") - status := ctx.QueryInt("status") - if status >=0 { - status -= 1 - } - params["Status"] = status - base.ParsePageAndPageSize(ctx, params) +func parseQueryParams(ctx *macaron.Context) models.CommonMap { + var params models.CommonMap = models.CommonMap{} + params["Id"] = ctx.QueryInt("id") + params["HostId"] = ctx.QueryInt("host_id") + params["Name"] = ctx.QueryTrim("name") + params["Protocol"] = ctx.QueryInt("protocol") + params["Tag"] = ctx.QueryTrim("tag") + status := ctx.QueryInt("status") + if status >= 0 { + status -= 1 + } + params["Status"] = status + base.ParsePageAndPageSize(ctx, params) - return params + return params } -func setHostsToTemplate(ctx *macaron.Context) { - hostModel := new(models.Host) - hostModel.PageSize = -1 - hosts, err := hostModel.List(models.CommonMap{}) - if err != nil { - logger.Error(err) - } - ctx.Data["Hosts"] = hosts +func setHostsToTemplate(ctx *macaron.Context) { + hostModel := new(models.Host) + hostModel.PageSize = -1 + hosts, err := hostModel.List(models.CommonMap{}) + if err != nil { + logger.Error(err) + } + ctx.Data["Hosts"] = hosts } func inHosts(slice []models.TaskHostDetail, element int16) bool { - for _, v := range slice { - if v.HostId == element { - return true - } - } + for _, v := range slice { + if v.HostId == element { + return true + } + } - return false -} \ No newline at end of file + return false +} diff --git a/routers/tasklog/task_log.go b/routers/tasklog/task_log.go index dc7dfb3..6fa5d79 100644 --- a/routers/tasklog/task_log.go +++ b/routers/tasklog/task_log.go @@ -3,78 +3,78 @@ package tasklog // 任务日志 import ( - "gopkg.in/macaron.v1" - "github.com/ouqiang/gocron/models" - "github.com/ouqiang/gocron/modules/logger" - "github.com/ouqiang/gocron/modules/utils" - "github.com/Unknwon/paginater" - "fmt" - "html/template" - "github.com/ouqiang/gocron/routers/base" + "fmt" + "github.com/Unknwon/paginater" + "github.com/ouqiang/gocron/models" + "github.com/ouqiang/gocron/modules/logger" + "github.com/ouqiang/gocron/modules/utils" + "github.com/ouqiang/gocron/routers/base" + "gopkg.in/macaron.v1" + "html/template" ) -func Index(ctx *macaron.Context) { - logModel := new(models.TaskLog) - queryParams := parseQueryParams(ctx) - total, err := logModel.Total(queryParams) - if err != nil { - logger.Error(err) - } - logs, err := logModel.List(queryParams) - if err != nil { - logger.Error(err) - } - PageParams := fmt.Sprintf("task_id=%d&protocol=%d&status=%d&page_size=%d", - queryParams["TaskId"], queryParams["Protocol"], queryParams["Status"], - queryParams["PageSize"]); - queryParams["PageParams"] = template.URL(PageParams) - p := paginater.New(int(total), queryParams["PageSize"].(int), queryParams["Page"].(int), 5) - ctx.Data["Pagination"] = p - ctx.Data["Title"] = "任务日志" - ctx.Data["Logs"] = logs - ctx.Data["Params"] = queryParams - ctx.HTML(200, "task/log") +func Index(ctx *macaron.Context) { + logModel := new(models.TaskLog) + queryParams := parseQueryParams(ctx) + total, err := logModel.Total(queryParams) + if err != nil { + logger.Error(err) + } + logs, err := logModel.List(queryParams) + if err != nil { + logger.Error(err) + } + PageParams := fmt.Sprintf("task_id=%d&protocol=%d&status=%d&page_size=%d", + queryParams["TaskId"], queryParams["Protocol"], queryParams["Status"], + queryParams["PageSize"]) + queryParams["PageParams"] = template.URL(PageParams) + p := paginater.New(int(total), queryParams["PageSize"].(int), queryParams["Page"].(int), 5) + ctx.Data["Pagination"] = p + ctx.Data["Title"] = "任务日志" + ctx.Data["Logs"] = logs + ctx.Data["Params"] = queryParams + ctx.HTML(200, "task/log") } // 清空日志 -func Clear(ctx *macaron.Context) string { - taskLogModel := new(models.TaskLog) - _, err := taskLogModel.Clear() - json := utils.JsonResponse{} - if err != nil { - return json.CommonFailure(utils.FailureContent) - } +func Clear(ctx *macaron.Context) string { + taskLogModel := new(models.TaskLog) + _, err := taskLogModel.Clear() + json := utils.JsonResponse{} + if err != nil { + return json.CommonFailure(utils.FailureContent) + } - return json.Success(utils.SuccessContent, nil) + return json.Success(utils.SuccessContent, nil) } // 删除N个月前的日志 func Remove(ctx *macaron.Context) string { - month := ctx.ParamsInt(":id") - json := utils.JsonResponse{} - if month < 1 || month > 12 { - return json.CommonFailure("参数取值范围1-12") - } - taskLogModel := new(models.TaskLog) - _, err := taskLogModel.Remove(month) - if err != nil { - return json.CommonFailure("删除失败", err) - } + month := ctx.ParamsInt(":id") + json := utils.JsonResponse{} + if month < 1 || month > 12 { + return json.CommonFailure("参数取值范围1-12") + } + taskLogModel := new(models.TaskLog) + _, err := taskLogModel.Remove(month) + if err != nil { + return json.CommonFailure("删除失败", err) + } - return json.Success("删除成功", nil) + return json.Success("删除成功", nil) } // 解析查询参数 -func parseQueryParams(ctx *macaron.Context) (models.CommonMap) { - var params models.CommonMap = models.CommonMap{} - params["TaskId"] = ctx.QueryInt("task_id") - params["Protocol"] = ctx.QueryInt("protocol") - status := ctx.QueryInt("status") - if status >=0 { - status -= 1 - } - params["Status"] = status - base.ParsePageAndPageSize(ctx, params) +func parseQueryParams(ctx *macaron.Context) models.CommonMap { + var params models.CommonMap = models.CommonMap{} + params["TaskId"] = ctx.QueryInt("task_id") + params["Protocol"] = ctx.QueryInt("protocol") + status := ctx.QueryInt("status") + if status >= 0 { + status -= 1 + } + params["Status"] = status + base.ParsePageAndPageSize(ctx, params) - return params -} \ No newline at end of file + return params +} diff --git a/routers/user/user.go b/routers/user/user.go index 45707d3..5aa89aa 100644 --- a/routers/user/user.go +++ b/routers/user/user.go @@ -1,127 +1,126 @@ package user import ( - "gopkg.in/macaron.v1" - "github.com/ouqiang/gocron/modules/utils" - "github.com/ouqiang/gocron/models" - "github.com/go-macaron/session" - "github.com/ouqiang/gocron/modules/logger" - "github.com/go-macaron/captcha" + "github.com/go-macaron/captcha" + "github.com/go-macaron/session" + "github.com/ouqiang/gocron/models" + "github.com/ouqiang/gocron/modules/logger" + "github.com/ouqiang/gocron/modules/utils" + "gopkg.in/macaron.v1" ) // @author qiang.ou // @date 2017/4/23-14:16 -func Login(ctx *macaron.Context) { - ctx.Data["Title"] = "用户登录" - ctx.HTML(200, "user/login") +func Login(ctx *macaron.Context) { + ctx.Data["Title"] = "用户登录" + ctx.HTML(200, "user/login") } -func EditPassword(ctx *macaron.Context) { - ctx.Data["Title"] = "修改密码" - ctx.HTML(200, "user/editPassword") +func EditPassword(ctx *macaron.Context) { + ctx.Data["Title"] = "修改密码" + ctx.HTML(200, "user/editPassword") } -func UpdatePassword(ctx *macaron.Context, sess session.Store) string { - oldPassword := ctx.QueryTrim("old_password") - newPassword := ctx.QueryTrim("new_password") - confirmNewPassword := ctx.QueryTrim("confirm_new_password") - json := utils.JsonResponse{} - if oldPassword == "" || newPassword == "" || confirmNewPassword == "" { - return json.CommonFailure("原密码和新密码均不能为空") - } - if newPassword != confirmNewPassword { - return json.CommonFailure("两次输入密码不一致") - } - if oldPassword == newPassword { - return json.CommonFailure("原密码与新密码不能相同") - } - userModel := new(models.User) - if !userModel.Match(Username(sess), oldPassword) { - return json.CommonFailure("原密码输入错误") - } - _, err := userModel.UpdatePassword(Uid(sess), newPassword) - if err != nil { - return json.CommonFailure("修改失败") - } +func UpdatePassword(ctx *macaron.Context, sess session.Store) string { + oldPassword := ctx.QueryTrim("old_password") + newPassword := ctx.QueryTrim("new_password") + confirmNewPassword := ctx.QueryTrim("confirm_new_password") + json := utils.JsonResponse{} + if oldPassword == "" || newPassword == "" || confirmNewPassword == "" { + return json.CommonFailure("原密码和新密码均不能为空") + } + if newPassword != confirmNewPassword { + return json.CommonFailure("两次输入密码不一致") + } + if oldPassword == newPassword { + return json.CommonFailure("原密码与新密码不能相同") + } + userModel := new(models.User) + if !userModel.Match(Username(sess), oldPassword) { + return json.CommonFailure("原密码输入错误") + } + _, err := userModel.UpdatePassword(Uid(sess), newPassword) + if err != nil { + return json.CommonFailure("修改失败") + } - return json.Success("修改成功", nil) + return json.Success("修改成功", nil) } func ValidateLogin(ctx *macaron.Context, sess session.Store, cpt *captcha.Captcha) string { - username := ctx.QueryTrim("username") - password := ctx.QueryTrim("password") - json := utils.JsonResponse{} - if username == "" || password == "" { - return json.CommonFailure("用户名、密码不能为空") - } - userModel := new (models.User) - if !userModel.Match(username, password) { - return json.CommonFailure("用户名或密码错误") - } - if !cpt.VerifyReq(ctx.Req) { - return json.Failure(utils.CaptchaError, "验证码错误") - } + username := ctx.QueryTrim("username") + password := ctx.QueryTrim("password") + json := utils.JsonResponse{} + if username == "" || password == "" { + return json.CommonFailure("用户名、密码不能为空") + } + userModel := new(models.User) + if !userModel.Match(username, password) { + return json.CommonFailure("用户名或密码错误") + } + if !cpt.VerifyReq(ctx.Req) { + return json.Failure(utils.CaptchaError, "验证码错误") + } - loginLogModel := new(models.LoginLog) - loginLogModel.Username = userModel.Name - loginLogModel.Ip = ctx.RemoteAddr() - _, err := loginLogModel.Create() - if err != nil { - logger.Error("记录用户登录日志失败", err) - } + loginLogModel := new(models.LoginLog) + loginLogModel.Username = userModel.Name + loginLogModel.Ip = ctx.RemoteAddr() + _, err := loginLogModel.Create() + if err != nil { + logger.Error("记录用户登录日志失败", err) + } + sess.Set("username", userModel.Name) + sess.Set("uid", userModel.Id) + sess.Set("isAdmin", userModel.IsAdmin) - sess.Set("username", userModel.Name) - sess.Set("uid", userModel.Id) - sess.Set("isAdmin", userModel.IsAdmin) - - return json.Success("登录成功", nil) + return json.Success("登录成功", nil) } func Logout(ctx *macaron.Context, sess session.Store) { - if IsLogin(sess) { - err := sess.Destory(ctx) - if err != nil { - logger.Error("用户退出登录失败", err) - } - } + if IsLogin(sess) { + err := sess.Destory(ctx) + if err != nil { + logger.Error("用户退出登录失败", err) + } + } - Login(ctx) + Login(ctx) } -func Username(sess session.Store) string { - username,ok := sess.Get("username").(string) - if ok { - return username - } +func Username(sess session.Store) string { + username, ok := sess.Get("username").(string) + if ok { + return username + } - return "" + return "" } -func Uid(sess session.Store) int { - uid,ok := sess.Get("uid").(int) - if ok { - return uid - } +func Uid(sess session.Store) int { + uid, ok := sess.Get("uid").(int) + if ok { + return uid + } - return 0 + return 0 } -func IsLogin(sess session.Store) bool { - uid, ok := sess.Get("uid").(int) - if ok && uid > 0 { - return true - } +func IsLogin(sess session.Store) bool { + uid, ok := sess.Get("uid").(int) + if ok && uid > 0 { + return true + } - return false + return false } -func IsAdmin(sess session.Store) bool { - isAdmin, ok := sess.Get("isAdmin").(int8) - if ok && isAdmin > 0 { - return true - } +func IsAdmin(sess session.Store) bool { + isAdmin, ok := sess.Get("isAdmin").(int8) + if ok && isAdmin > 0 { + return true + } - return false -} \ No newline at end of file + return false +} diff --git a/service/task.go b/service/task.go index ca2d403..2ce94f6 100644 --- a/service/task.go +++ b/service/task.go @@ -1,154 +1,155 @@ package service import ( - "github.com/ouqiang/gocron/models" - "strconv" - "time" - "github.com/ouqiang/gocron/modules/logger" - "github.com/jakecoffman/cron" - "errors" - "fmt" - "github.com/ouqiang/gocron/modules/httpclient" - "github.com/ouqiang/gocron/modules/notify" - "sync" - rpcClient "github.com/ouqiang/gocron/modules/rpc/client" - pb "github.com/ouqiang/gocron/modules/rpc/proto" - "strings" + "errors" + "fmt" + "github.com/jakecoffman/cron" + "github.com/ouqiang/gocron/models" + "github.com/ouqiang/gocron/modules/httpclient" + "github.com/ouqiang/gocron/modules/logger" + "github.com/ouqiang/gocron/modules/notify" + rpcClient "github.com/ouqiang/gocron/modules/rpc/client" + pb "github.com/ouqiang/gocron/modules/rpc/proto" + "strconv" + "strings" + "sync" + "time" ) // 定时任务调度管理器 var Cron *cron.Cron + // 同一任务是否有实例处于运行中 var runInstance Instance + // 任务计数-正在运行中的任务 var TaskNum TaskCount // 任务计数 type TaskCount struct { - num int - sync.RWMutex + num int + sync.RWMutex } -func (c *TaskCount) Add() { - c.Lock() - defer c.Unlock() - c.num += 1 +func (c *TaskCount) Add() { + c.Lock() + defer c.Unlock() + c.num += 1 } -func (c *TaskCount) Done() { - c.Lock() - defer c.Unlock() - c.num -= 1 +func (c *TaskCount) Done() { + c.Lock() + defer c.Unlock() + c.num -= 1 } -func (c *TaskCount) Num() int { - c.RLock() - defer c.RUnlock() +func (c *TaskCount) Num() int { + c.RLock() + defer c.RUnlock() - return c.num + return c.num } // 任务ID作为Key type Instance struct { - Status map[int]bool - sync.RWMutex + Status map[int]bool + sync.RWMutex } // 是否有任务处于运行中 func (i *Instance) has(key int) bool { - i.RLock() - defer i.RUnlock() - running, ok := i.Status[key] - if ok && running { - return true - } + i.RLock() + defer i.RUnlock() + running, ok := i.Status[key] + if ok && running { + return true + } - return false + return false } -func (i *Instance) add(key int) { - i.Lock() - defer i.Unlock() - i.Status[key] = true +func (i *Instance) add(key int) { + i.Lock() + defer i.Unlock() + i.Status[key] = true } -func (i *Instance) done(key int) { - i.Lock() - defer i.Unlock() - delete(i.Status, key) +func (i *Instance) done(key int) { + i.Lock() + defer i.Unlock() + delete(i.Status, key) } type Task struct{} type TaskResult struct { - Result string - Err error - RetryTimes int8 + Result string + Err error + RetryTimes int8 } // 初始化任务, 从数据库取出所有任务, 添加到定时任务并运行 func (task *Task) Initialize() { - Cron = cron.New() - Cron.Start() - runInstance = Instance{make(map[int]bool), sync.RWMutex{}} - TaskNum = TaskCount{0, sync.RWMutex{}} + Cron = cron.New() + Cron.Start() + runInstance = Instance{make(map[int]bool), sync.RWMutex{}} + TaskNum = TaskCount{0, sync.RWMutex{}} - taskModel := new(models.Task) - taskList, err := taskModel.ActiveList() - if err != nil { - logger.Error("定时任务初始化#获取任务列表错误-", err.Error()) - return - } - if len(taskList) == 0 { - logger.Debug("任务列表为空") - return - } - task.BatchAdd(taskList) + taskModel := new(models.Task) + taskList, err := taskModel.ActiveList() + if err != nil { + logger.Error("定时任务初始化#获取任务列表错误-", err.Error()) + return + } + if len(taskList) == 0 { + logger.Debug("任务列表为空") + return + } + task.BatchAdd(taskList) } // 批量添加任务 -func (task *Task) BatchAdd(tasks []models.Task) { - for _, item := range tasks { - task.Add(item) - } +func (task *Task) BatchAdd(tasks []models.Task) { + for _, item := range tasks { + task.Add(item) + } } // 添加任务 func (task *Task) Add(taskModel models.Task) { - if taskModel.Level == models.TaskLevelChild { - logger.Errorf("添加任务失败#不允许添加子任务到调度器#任务Id-%d", taskModel.Id); - return - } - taskFunc := createJob(taskModel) - if taskFunc == nil { - logger.Error("创建任务处理Job失败,不支持的任务协议#", taskModel.Protocol) - return - } + if taskModel.Level == models.TaskLevelChild { + logger.Errorf("添加任务失败#不允许添加子任务到调度器#任务Id-%d", taskModel.Id) + return + } + taskFunc := createJob(taskModel) + if taskFunc == nil { + logger.Error("创建任务处理Job失败,不支持的任务协议#", taskModel.Protocol) + return + } - cronName := strconv.Itoa(taskModel.Id) - // Cron任务采用数组存储, 删除任务需遍历数组, 并对数组重新赋值, 任务较多时,有性能问题 - Cron.RemoveJob(cronName) - err := Cron.AddFunc(taskModel.Spec, taskFunc, cronName) - if err != nil { - logger.Error("添加任务到调度器失败#", err) - } + cronName := strconv.Itoa(taskModel.Id) + // Cron任务采用数组存储, 删除任务需遍历数组, 并对数组重新赋值, 任务较多时,有性能问题 + Cron.RemoveJob(cronName) + err := Cron.AddFunc(taskModel.Spec, taskFunc, cronName) + if err != nil { + logger.Error("添加任务到调度器失败#", err) + } } // 停止所有任务 -func (task *Task) StopAll() { - Cron.Stop() +func (task *Task) StopAll() { + Cron.Stop() } // 直接运行任务 -func (task *Task) Run(taskModel models.Task) { - go createJob(taskModel)() +func (task *Task) Run(taskModel models.Task) { + go createJob(taskModel)() } type Handler interface { - Run(taskModel models.Task) (string, error) + Run(taskModel models.Task) (string, error) } - // HTTP任务 type HTTPHandler struct{} @@ -156,259 +157,257 @@ type HTTPHandler struct{} const HttpExecTimeout = 300 func (h *HTTPHandler) Run(taskModel models.Task) (result string, err error) { - if taskModel.Timeout <= 0 || taskModel.Timeout > HttpExecTimeout { - taskModel.Timeout = HttpExecTimeout - } - resp := httpclient.Get(taskModel.Command, taskModel.Timeout) - // 返回状态码非200,均为失败 - if resp.StatusCode != 200 { - return resp.Body, errors.New(fmt.Sprintf("HTTP状态码非200-->%d", resp.StatusCode)) - } + if taskModel.Timeout <= 0 || taskModel.Timeout > HttpExecTimeout { + taskModel.Timeout = HttpExecTimeout + } + resp := httpclient.Get(taskModel.Command, taskModel.Timeout) + // 返回状态码非200,均为失败 + if resp.StatusCode != 200 { + return resp.Body, errors.New(fmt.Sprintf("HTTP状态码非200-->%d", resp.StatusCode)) + } - return resp.Body, err + return resp.Body, err } // RPC调用执行任务 -type RPCHandler struct {} +type RPCHandler struct{} -func (h *RPCHandler) Run(taskModel models.Task) (result string, err error) { - taskRequest := new(pb.TaskRequest) - taskRequest.Timeout = int32(taskModel.Timeout) - taskRequest.Command = taskModel.Command - var resultChan chan TaskResult = make(chan TaskResult, len(taskModel.Hosts)) - for _, taskHost := range taskModel.Hosts { - go func(th models.TaskHostDetail) { - output, err := rpcClient.ExecWithRetry(th.Name, th.Port, taskRequest) - var errorMessage string = "" - if err != nil { - errorMessage = err.Error() - } - outputMessage := fmt.Sprintf("主机: [%s-%s]\n%s\n%s\n\n", - th.Alias, th.Name, errorMessage, output, - ) - resultChan <- TaskResult{Err:err, Result: outputMessage} - }(taskHost) - } +func (h *RPCHandler) Run(taskModel models.Task) (result string, err error) { + taskRequest := new(pb.TaskRequest) + taskRequest.Timeout = int32(taskModel.Timeout) + taskRequest.Command = taskModel.Command + var resultChan chan TaskResult = make(chan TaskResult, len(taskModel.Hosts)) + for _, taskHost := range taskModel.Hosts { + go func(th models.TaskHostDetail) { + output, err := rpcClient.Exec(th.Name, th.Port, taskRequest) + var errorMessage string = "" + if err != nil { + errorMessage = err.Error() + } + outputMessage := fmt.Sprintf("主机: [%s-%s]\n%s\n%s\n\n", + th.Alias, th.Name, errorMessage, output, + ) + resultChan <- TaskResult{Err: err, Result: outputMessage} + }(taskHost) + } - var aggregationErr error = nil - var aggregationResult string = "" - for i := 0; i < len(taskModel.Hosts); i++ { - taskResult := <- resultChan - aggregationResult += taskResult.Result - if taskResult.Err != nil { - aggregationErr = taskResult.Err - } - } + var aggregationErr error = nil + var aggregationResult string = "" + for i := 0; i < len(taskModel.Hosts); i++ { + taskResult := <-resultChan + aggregationResult += taskResult.Result + if taskResult.Err != nil { + aggregationErr = taskResult.Err + } + } - return aggregationResult, aggregationErr + return aggregationResult, aggregationErr } - // 创建任务日志 func createTaskLog(taskModel models.Task, status models.Status) (int64, error) { - taskLogModel := new(models.TaskLog) - taskLogModel.TaskId = taskModel.Id - taskLogModel.Name = taskModel.Name - taskLogModel.Spec = taskModel.Spec - taskLogModel.Protocol = taskModel.Protocol - taskLogModel.Command = taskModel.Command - taskLogModel.Timeout = taskModel.Timeout - if taskModel.Protocol == models.TaskRPC { - var aggregationHost string = "" - for _, host := range taskModel.Hosts { - aggregationHost += fmt.Sprintf("%s-%s
", host.Alias, host.Name) - } - taskLogModel.Hostname = aggregationHost - } - taskLogModel.StartTime = time.Now() - taskLogModel.Status = status - insertId, err := taskLogModel.Create() + taskLogModel := new(models.TaskLog) + taskLogModel.TaskId = taskModel.Id + taskLogModel.Name = taskModel.Name + taskLogModel.Spec = taskModel.Spec + taskLogModel.Protocol = taskModel.Protocol + taskLogModel.Command = taskModel.Command + taskLogModel.Timeout = taskModel.Timeout + if taskModel.Protocol == models.TaskRPC { + var aggregationHost string = "" + for _, host := range taskModel.Hosts { + aggregationHost += fmt.Sprintf("%s-%s
", host.Alias, host.Name) + } + taskLogModel.Hostname = aggregationHost + } + taskLogModel.StartTime = time.Now() + taskLogModel.Status = status + insertId, err := taskLogModel.Create() - return insertId, err + return insertId, err } // 更新任务日志 func updateTaskLog(taskLogId int64, taskResult TaskResult) (int64, error) { - taskLogModel := new(models.TaskLog) - var status models.Status - var result string = taskResult.Result - if taskResult.Err != nil { - status = models.Failure - } else { - status = models.Finish - } - return taskLogModel.Update(taskLogId, models.CommonMap{ - "retry_times": taskResult.RetryTimes, - "status": status, - "result": result, - }) + taskLogModel := new(models.TaskLog) + var status models.Status + var result string = taskResult.Result + if taskResult.Err != nil { + status = models.Failure + } else { + status = models.Finish + } + return taskLogModel.Update(taskLogId, models.CommonMap{ + "retry_times": taskResult.RetryTimes, + "status": status, + "result": result, + }) } func createJob(taskModel models.Task) cron.FuncJob { - var handler Handler = createHandler(taskModel) - if handler == nil { - return nil - } - taskFunc := func() { - TaskNum.Add() - defer TaskNum.Done() - taskLogId := beforeExecJob(taskModel) - if taskLogId <= 0 { - return - } - logger.Infof("开始执行任务#%s#命令-%s", taskModel.Name, taskModel.Command) - taskResult := execJob(handler, taskModel) - logger.Infof("任务完成#%s#命令-%s", taskModel.Name, taskModel.Command) - afterExecJob(taskModel, taskResult, taskLogId) - } + var handler Handler = createHandler(taskModel) + if handler == nil { + return nil + } + taskFunc := func() { + TaskNum.Add() + defer TaskNum.Done() + taskLogId := beforeExecJob(taskModel) + if taskLogId <= 0 { + return + } + logger.Infof("开始执行任务#%s#命令-%s", taskModel.Name, taskModel.Command) + taskResult := execJob(handler, taskModel) + logger.Infof("任务完成#%s#命令-%s", taskModel.Name, taskModel.Command) + afterExecJob(taskModel, taskResult, taskLogId) + } - return taskFunc + return taskFunc } -func createHandler(taskModel models.Task) Handler { - var handler Handler = nil - switch taskModel.Protocol { - case models.TaskHTTP: - handler = new(HTTPHandler) - case models.TaskRPC: - handler = new(RPCHandler) - } +func createHandler(taskModel models.Task) Handler { + var handler Handler = nil + switch taskModel.Protocol { + case models.TaskHTTP: + handler = new(HTTPHandler) + case models.TaskRPC: + handler = new(RPCHandler) + } - - return handler; + return handler } // 任务前置操作 -func beforeExecJob(taskModel models.Task) (taskLogId int64) { - if taskModel.Multi == 0 && runInstance.has(taskModel.Id) { - createTaskLog(taskModel, models.Cancel) - return - } - if taskModel.Multi == 0 { - runInstance.add(taskModel.Id) - } - taskLogId, err := createTaskLog(taskModel, models.Running) - if err != nil { - logger.Error("任务开始执行#写入任务日志失败-", err) - return - } +func beforeExecJob(taskModel models.Task) (taskLogId int64) { + if taskModel.Multi == 0 && runInstance.has(taskModel.Id) { + createTaskLog(taskModel, models.Cancel) + return + } + if taskModel.Multi == 0 { + runInstance.add(taskModel.Id) + } + taskLogId, err := createTaskLog(taskModel, models.Running) + if err != nil { + logger.Error("任务开始执行#写入任务日志失败-", err) + return + } - logger.Debugf("任务命令-%s", taskModel.Command) + logger.Debugf("任务命令-%s", taskModel.Command) - return taskLogId + return taskLogId } // 任务执行后置操作 -func afterExecJob(taskModel models.Task, taskResult TaskResult, taskLogId int64) { - _, err := updateTaskLog(taskLogId, taskResult) - if err != nil { - logger.Error("任务结束#更新任务日志失败-", err) - } +func afterExecJob(taskModel models.Task, taskResult TaskResult, taskLogId int64) { + _, err := updateTaskLog(taskLogId, taskResult) + if err != nil { + logger.Error("任务结束#更新任务日志失败-", err) + } - // 发送邮件 - go SendNotification(taskModel, taskResult) - // 执行依赖任务 - go execDependencyTask(taskModel, taskResult) + // 发送邮件 + go SendNotification(taskModel, taskResult) + // 执行依赖任务 + go execDependencyTask(taskModel, taskResult) } // 执行依赖任务, 多个任务并发执行 -func execDependencyTask(taskModel models.Task, taskResult TaskResult) { - // 父任务才能执行子任务 - if taskModel.Level != models.TaskLevelParent { - return - } +func execDependencyTask(taskModel models.Task, taskResult TaskResult) { + // 父任务才能执行子任务 + if taskModel.Level != models.TaskLevelParent { + return + } - // 是否存在子任务 - dependencyTaskId := strings.TrimSpace(taskModel.DependencyTaskId) - if dependencyTaskId == "" { - return - } + // 是否存在子任务 + dependencyTaskId := strings.TrimSpace(taskModel.DependencyTaskId) + if dependencyTaskId == "" { + return + } - // 父子任务关系为强依赖, 父任务执行失败, 不执行依赖任务 - if taskModel.DependencyStatus == models.TaskDependencyStatusStrong && taskResult.Err != nil { - logger.Infof("父子任务为强依赖关系, 父任务执行失败, 不运行依赖任务#主任务ID-%d", taskModel.Id) - return - } + // 父子任务关系为强依赖, 父任务执行失败, 不执行依赖任务 + if taskModel.DependencyStatus == models.TaskDependencyStatusStrong && taskResult.Err != nil { + logger.Infof("父子任务为强依赖关系, 父任务执行失败, 不运行依赖任务#主任务ID-%d", taskModel.Id) + return + } - // 获取子任务 - model := new(models.Task) - tasks , err := model.GetDependencyTaskList(dependencyTaskId) - if err != nil { - logger.Errorf("获取依赖任务失败#主任务ID-%d#%s", taskModel.Id, err.Error()) - return - } - if len(tasks) == 0 { - logger.Errorf("依赖任务列表为空#主任务ID-%d", taskModel.Id) - } - serviceTask := new(Task) - for _, task := range tasks { - task.Spec = fmt.Sprintf("依赖任务(主任务ID-%d)", taskModel.Id) - serviceTask.Run(task) - } + // 获取子任务 + model := new(models.Task) + tasks, err := model.GetDependencyTaskList(dependencyTaskId) + if err != nil { + logger.Errorf("获取依赖任务失败#主任务ID-%d#%s", taskModel.Id, err.Error()) + return + } + if len(tasks) == 0 { + logger.Errorf("依赖任务列表为空#主任务ID-%d", taskModel.Id) + } + serviceTask := new(Task) + for _, task := range tasks { + task.Spec = fmt.Sprintf("依赖任务(主任务ID-%d)", taskModel.Id) + serviceTask.Run(task) + } } // 发送任务结果通知 -func SendNotification(taskModel models.Task, taskResult TaskResult) { - var statusName string - // 未开启通知 - if taskModel.NotifyStatus == 0 { - return - } - if taskModel.NotifyStatus == 1 && taskResult.Err == nil { - // 执行失败才发送通知 - return - } - if taskModel.NotifyReceiverId == "" { - return - } - if taskResult.Err != nil { - statusName = "失败" - } else { - statusName = "成功" - } - // 发送通知 - msg := notify.Message{ - "task_type": taskModel.NotifyType, - "task_receiver_id": taskModel.NotifyReceiverId, - "name": taskModel.Name, - "output": taskResult.Result, - "status": statusName, - "taskId": taskModel.Id, - }; - notify.Push(msg) +func SendNotification(taskModel models.Task, taskResult TaskResult) { + var statusName string + // 未开启通知 + if taskModel.NotifyStatus == 0 { + return + } + if taskModel.NotifyStatus == 1 && taskResult.Err == nil { + // 执行失败才发送通知 + return + } + if taskModel.NotifyReceiverId == "" { + return + } + if taskResult.Err != nil { + statusName = "失败" + } else { + statusName = "成功" + } + // 发送通知 + msg := notify.Message{ + "task_type": taskModel.NotifyType, + "task_receiver_id": taskModel.NotifyReceiverId, + "name": taskModel.Name, + "output": taskResult.Result, + "status": statusName, + "taskId": taskModel.Id, + } + notify.Push(msg) } // 执行具体任务 -func execJob(handler Handler, taskModel models.Task) TaskResult { - defer func() { - if err := recover(); err != nil { - logger.Error("panic#service/task.go:execJob#", err) - } - } () - if taskModel.Multi == 0 { - defer runInstance.done(taskModel.Id) - } - // 默认只运行任务一次 - var execTimes int8 = 1 - if (taskModel.RetryTimes > 0) { - execTimes += taskModel.RetryTimes - } - var i int8 = 0 - var output string - var err error - for i < execTimes { - output, err = handler.Run(taskModel) - if err == nil { - return TaskResult{Result: output, Err: err, RetryTimes: i} - } - i++ - if i < execTimes { - logger.Warnf("任务执行失败#任务id-%d#重试第%d次#输出-%s#错误-%s", taskModel.Id, i, output, err.Error()) - // 重试间隔时间,每次递增1分钟 - time.Sleep( time.Duration(i) * time.Minute) - } - } +func execJob(handler Handler, taskModel models.Task) TaskResult { + defer func() { + if err := recover(); err != nil { + logger.Error("panic#service/task.go:execJob#", err) + } + }() + if taskModel.Multi == 0 { + defer runInstance.done(taskModel.Id) + } + // 默认只运行任务一次 + var execTimes int8 = 1 + if taskModel.RetryTimes > 0 { + execTimes += taskModel.RetryTimes + } + var i int8 = 0 + var output string + var err error + for i < execTimes { + output, err = handler.Run(taskModel) + if err == nil { + return TaskResult{Result: output, Err: err, RetryTimes: i} + } + i++ + if i < execTimes { + logger.Warnf("任务执行失败#任务id-%d#重试第%d次#输出-%s#错误-%s", taskModel.Id, i, output, err.Error()) + // 重试间隔时间,每次递增1分钟 + time.Sleep(time.Duration(i) * time.Minute) + } + } - return TaskResult{Result: output, Err: err, RetryTimes: taskModel.RetryTimes} -} \ No newline at end of file + return TaskResult{Result: output, Err: err, RetryTimes: taskModel.RetryTimes} +} diff --git a/vendor/github.com/Unknwon/com/math.go b/vendor/github.com/Unknwon/com/math.go index 99c56b6..62b77e8 100644 --- a/vendor/github.com/Unknwon/com/math.go +++ b/vendor/github.com/Unknwon/com/math.go @@ -14,12 +14,12 @@ package com -// PowInt is int type of math.Pow function. +// PowInt is int type of math.Pow function. func PowInt(x int, y int) int { if y <= 0 { return 1 } else { - if y % 2 == 0 { + if y%2 == 0 { sqrt := PowInt(x, y/2) return sqrt * sqrt } else { diff --git a/vendor/github.com/go-macaron/csrf/csrf.go b/vendor/github.com/go-macaron/csrf/csrf.go index 9c1e38b..d9c9685 100644 --- a/vendor/github.com/go-macaron/csrf/csrf.go +++ b/vendor/github.com/go-macaron/csrf/csrf.go @@ -124,7 +124,7 @@ type Options struct { // Cookie value used to set and get token. Cookie string // Cookie path. - CookiePath string + CookiePath string CookieHttpOnly bool // Key used for getting the unique ID per user. SessionKey string diff --git a/vendor/github.com/go-macaron/session/memory.go b/vendor/github.com/go-macaron/session/memory.go index 4ad9293..f76f159 100644 --- a/vendor/github.com/go-macaron/session/memory.go +++ b/vendor/github.com/go-macaron/session/memory.go @@ -1,217 +1,217 @@ -// Copyright 2013 Beego Authors -// Copyright 2014 The Macaron Authors -// -// Licensed under the Apache License, Version 2.0 (the "License"): you may -// not use this file except in compliance with the License. You may obtain -// a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, WITHOUT -// WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the -// License for the specific language governing permissions and limitations -// under the License. - -package session - -import ( - "container/list" - "fmt" - "sync" - "time" -) - -// MemStore represents a in-memory session store implementation. -type MemStore struct { - sid string - lock sync.RWMutex - data map[interface{}]interface{} - lastAccess time.Time -} - -// NewMemStore creates and returns a memory session store. -func NewMemStore(sid string) *MemStore { - return &MemStore{ - sid: sid, - data: make(map[interface{}]interface{}), - lastAccess: time.Now(), - } -} - -// Set sets value to given key in session. -func (s *MemStore) Set(key, val interface{}) error { - s.lock.Lock() - defer s.lock.Unlock() - - s.data[key] = val - return nil -} - -// Get gets value by given key in session. -func (s *MemStore) Get(key interface{}) interface{} { - s.lock.RLock() - defer s.lock.RUnlock() - - return s.data[key] -} - -// Delete deletes a key from session. -func (s *MemStore) Delete(key interface{}) error { - s.lock.Lock() - defer s.lock.Unlock() - - delete(s.data, key) - return nil -} - -// ID returns current session ID. -func (s *MemStore) ID() string { - return s.sid -} - -// Release releases resource and save data to provider. -func (_ *MemStore) Release() error { - return nil -} - -// Flush deletes all session data. -func (s *MemStore) Flush() error { - s.lock.Lock() - defer s.lock.Unlock() - - s.data = make(map[interface{}]interface{}) - return nil -} - -// MemProvider represents a in-memory session provider implementation. -type MemProvider struct { - lock sync.RWMutex - maxLifetime int64 - data map[string]*list.Element - // A priority list whose lastAccess newer gets higer priority. - list *list.List -} - -// Init initializes memory session provider. -func (p *MemProvider) Init(maxLifetime int64, _ string) error { - p.lock.Lock() - p.maxLifetime = maxLifetime - p.lock.Unlock() - return nil -} - -// update expands time of session store by given ID. -func (p *MemProvider) update(sid string) error { - p.lock.Lock() - defer p.lock.Unlock() - - if e, ok := p.data[sid]; ok { - e.Value.(*MemStore).lastAccess = time.Now() - p.list.MoveToFront(e) - return nil - } - return nil -} - -// Read returns raw session store by session ID. -func (p *MemProvider) Read(sid string) (_ RawStore, err error) { - p.lock.RLock() - e, ok := p.data[sid] - p.lock.RUnlock() - - if ok { - if err = p.update(sid); err != nil { - return nil, err - } - return e.Value.(*MemStore), nil - } - - // Create a new session. - p.lock.Lock() - defer p.lock.Unlock() - - s := NewMemStore(sid) - p.data[sid] = p.list.PushBack(s) - return s, nil -} - -// Exist returns true if session with given ID exists. -func (p *MemProvider) Exist(sid string) bool { - p.lock.RLock() - defer p.lock.RUnlock() - - _, ok := p.data[sid] - return ok -} - -// Destory deletes a session by session ID. -func (p *MemProvider) Destory(sid string) error { - p.lock.Lock() - defer p.lock.Unlock() - - e, ok := p.data[sid] - if !ok { - return nil - } - - p.list.Remove(e) - delete(p.data, sid) - return nil -} - -// Regenerate regenerates a session store from old session ID to new one. -func (p *MemProvider) Regenerate(oldsid, sid string) (RawStore, error) { - if p.Exist(sid) { - return nil, fmt.Errorf("new sid '%s' already exists", sid) - } - - s, err := p.Read(oldsid) - if err != nil { - return nil, err - } - - if err = p.Destory(oldsid); err != nil { - return nil, err - } - - s.(*MemStore).sid = sid - - p.lock.Lock() - defer p.lock.Unlock() - p.data[sid] = p.list.PushBack(s) - return s, nil -} - -// Count counts and returns number of sessions. -func (p *MemProvider) Count() int { - return p.list.Len() -} - -// GC calls GC to clean expired sessions. -func (p *MemProvider) GC() { - p.lock.RLock() - for { - // No session in the list. - e := p.list.Back() - if e == nil { - break - } - - if (e.Value.(*MemStore).lastAccess.Unix() + p.maxLifetime) < time.Now().Unix() { - p.lock.RUnlock() - p.lock.Lock() - p.list.Remove(e) - delete(p.data, e.Value.(*MemStore).sid) - p.lock.Unlock() - p.lock.RLock() - } else { - break - } - } - p.lock.RUnlock() -} - -func init() { - Register("memory", &MemProvider{list: list.New(), data: make(map[string]*list.Element)}) -} +// Copyright 2013 Beego Authors +// Copyright 2014 The Macaron Authors +// +// Licensed under the Apache License, Version 2.0 (the "License"): you may +// not use this file except in compliance with the License. You may obtain +// a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, WITHOUT +// WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the +// License for the specific language governing permissions and limitations +// under the License. + +package session + +import ( + "container/list" + "fmt" + "sync" + "time" +) + +// MemStore represents a in-memory session store implementation. +type MemStore struct { + sid string + lock sync.RWMutex + data map[interface{}]interface{} + lastAccess time.Time +} + +// NewMemStore creates and returns a memory session store. +func NewMemStore(sid string) *MemStore { + return &MemStore{ + sid: sid, + data: make(map[interface{}]interface{}), + lastAccess: time.Now(), + } +} + +// Set sets value to given key in session. +func (s *MemStore) Set(key, val interface{}) error { + s.lock.Lock() + defer s.lock.Unlock() + + s.data[key] = val + return nil +} + +// Get gets value by given key in session. +func (s *MemStore) Get(key interface{}) interface{} { + s.lock.RLock() + defer s.lock.RUnlock() + + return s.data[key] +} + +// Delete deletes a key from session. +func (s *MemStore) Delete(key interface{}) error { + s.lock.Lock() + defer s.lock.Unlock() + + delete(s.data, key) + return nil +} + +// ID returns current session ID. +func (s *MemStore) ID() string { + return s.sid +} + +// Release releases resource and save data to provider. +func (_ *MemStore) Release() error { + return nil +} + +// Flush deletes all session data. +func (s *MemStore) Flush() error { + s.lock.Lock() + defer s.lock.Unlock() + + s.data = make(map[interface{}]interface{}) + return nil +} + +// MemProvider represents a in-memory session provider implementation. +type MemProvider struct { + lock sync.RWMutex + maxLifetime int64 + data map[string]*list.Element + // A priority list whose lastAccess newer gets higer priority. + list *list.List +} + +// Init initializes memory session provider. +func (p *MemProvider) Init(maxLifetime int64, _ string) error { + p.lock.Lock() + p.maxLifetime = maxLifetime + p.lock.Unlock() + return nil +} + +// update expands time of session store by given ID. +func (p *MemProvider) update(sid string) error { + p.lock.Lock() + defer p.lock.Unlock() + + if e, ok := p.data[sid]; ok { + e.Value.(*MemStore).lastAccess = time.Now() + p.list.MoveToFront(e) + return nil + } + return nil +} + +// Read returns raw session store by session ID. +func (p *MemProvider) Read(sid string) (_ RawStore, err error) { + p.lock.RLock() + e, ok := p.data[sid] + p.lock.RUnlock() + + if ok { + if err = p.update(sid); err != nil { + return nil, err + } + return e.Value.(*MemStore), nil + } + + // Create a new session. + p.lock.Lock() + defer p.lock.Unlock() + + s := NewMemStore(sid) + p.data[sid] = p.list.PushBack(s) + return s, nil +} + +// Exist returns true if session with given ID exists. +func (p *MemProvider) Exist(sid string) bool { + p.lock.RLock() + defer p.lock.RUnlock() + + _, ok := p.data[sid] + return ok +} + +// Destory deletes a session by session ID. +func (p *MemProvider) Destory(sid string) error { + p.lock.Lock() + defer p.lock.Unlock() + + e, ok := p.data[sid] + if !ok { + return nil + } + + p.list.Remove(e) + delete(p.data, sid) + return nil +} + +// Regenerate regenerates a session store from old session ID to new one. +func (p *MemProvider) Regenerate(oldsid, sid string) (RawStore, error) { + if p.Exist(sid) { + return nil, fmt.Errorf("new sid '%s' already exists", sid) + } + + s, err := p.Read(oldsid) + if err != nil { + return nil, err + } + + if err = p.Destory(oldsid); err != nil { + return nil, err + } + + s.(*MemStore).sid = sid + + p.lock.Lock() + defer p.lock.Unlock() + p.data[sid] = p.list.PushBack(s) + return s, nil +} + +// Count counts and returns number of sessions. +func (p *MemProvider) Count() int { + return p.list.Len() +} + +// GC calls GC to clean expired sessions. +func (p *MemProvider) GC() { + p.lock.RLock() + for { + // No session in the list. + e := p.list.Back() + if e == nil { + break + } + + if (e.Value.(*MemStore).lastAccess.Unix() + p.maxLifetime) < time.Now().Unix() { + p.lock.RUnlock() + p.lock.Lock() + p.list.Remove(e) + delete(p.data, e.Value.(*MemStore).sid) + p.lock.Unlock() + p.lock.RLock() + } else { + break + } + } + p.lock.RUnlock() +} + +func init() { + Register("memory", &MemProvider{list: list.New(), data: make(map[string]*list.Element)}) +} diff --git a/vendor/github.com/go-xorm/core/cache.go b/vendor/github.com/go-xorm/core/cache.go index bf81bd5..cd95e54 100644 --- a/vendor/github.com/go-xorm/core/cache.go +++ b/vendor/github.com/go-xorm/core/cache.go @@ -1,11 +1,11 @@ package core import ( + "bytes" + "encoding/gob" "errors" "fmt" "time" - "bytes" - "encoding/gob" ) const ( @@ -55,7 +55,6 @@ func encodeIds(ids []PK) (string, error) { return buf.String(), err } - func decodeIds(s string) ([]PK, error) { pks := make([]PK, 0) diff --git a/vendor/github.com/jakecoffman/cron/cron.go b/vendor/github.com/jakecoffman/cron/cron.go index 3b4af82..e42c7e5 100644 --- a/vendor/github.com/jakecoffman/cron/cron.go +++ b/vendor/github.com/jakecoffman/cron/cron.go @@ -90,7 +90,7 @@ type FuncJob func() func (f FuncJob) Run() { f() } // AddFunc adds a func to the Cron to be run on the given schedule. -func (c *Cron) AddFunc(spec string, cmd func(), name string) error { +func (c *Cron) AddFunc(spec string, cmd func(), name string) error { return c.AddJob(spec, FuncJob(cmd), name) } diff --git a/vendor/github.com/jakecoffman/cron/parser.go b/vendor/github.com/jakecoffman/cron/parser.go index 28326c5..dcde409 100644 --- a/vendor/github.com/jakecoffman/cron/parser.go +++ b/vendor/github.com/jakecoffman/cron/parser.go @@ -1,12 +1,12 @@ package cron import ( + "fmt" "log" "math" "strconv" "strings" "time" - "fmt" ) // Parse returns a new crontab schedule representing the given spec. @@ -160,7 +160,7 @@ func all(r bounds) uint64 { // parseDescriptor returns a pre-defined schedule for the expression, or panics // if none matches. -func parseDescriptor(spec string) (Schedule,error) { +func parseDescriptor(spec string) (Schedule, error) { switch spec { case "@yearly", "@annually": return &SpecSchedule{ @@ -217,10 +217,10 @@ func parseDescriptor(spec string) (Schedule,error) { if strings.HasPrefix(spec, every) { duration, err := time.ParseDuration(spec[len(every):]) if err != nil { - return nil, fmt.Errorf("Failed to parse duration %s: %s", spec, err) - } - return Every(duration),nil + return nil, fmt.Errorf("Failed to parse duration %s: %s", spec, err) + } + return Every(duration), nil } - return nil, fmt.Errorf("Unrecognized descriptor: %s", spec) + return nil, fmt.Errorf("Unrecognized descriptor: %s", spec) } diff --git a/vendor/golang.org/x/crypto/ssh/kex.go b/vendor/golang.org/x/crypto/ssh/kex.go index c87fbeb..f91c277 100644 --- a/vendor/golang.org/x/crypto/ssh/kex.go +++ b/vendor/golang.org/x/crypto/ssh/kex.go @@ -383,8 +383,8 @@ func init() { // 4253 and Oakley Group 2 in RFC 2409. p, _ := new(big.Int).SetString("FFFFFFFFFFFFFFFFC90FDAA22168C234C4C6628B80DC1CD129024E088A67CC74020BBEA63B139B22514A08798E3404DDEF9519B3CD3A431B302B0A6DF25F14374FE1356D6D51C245E485B576625E7EC6F44C42E9A637ED6B0BFF5CB6F406B7EDEE386BFB5A899FA5AE9F24117C4B1FE649286651ECE65381FFFFFFFFFFFFFFFF", 16) kexAlgoMap[kexAlgoDH1SHA1] = &dhGroup{ - g: new(big.Int).SetInt64(2), - p: p, + g: new(big.Int).SetInt64(2), + p: p, pMinus1: new(big.Int).Sub(p, bigOne), } @@ -393,8 +393,8 @@ func init() { p, _ = new(big.Int).SetString("FFFFFFFFFFFFFFFFC90FDAA22168C234C4C6628B80DC1CD129024E088A67CC74020BBEA63B139B22514A08798E3404DDEF9519B3CD3A431B302B0A6DF25F14374FE1356D6D51C245E485B576625E7EC6F44C42E9A637ED6B0BFF5CB6F406B7EDEE386BFB5A899FA5AE9F24117C4B1FE649286651ECE45B3DC2007CB8A163BF0598DA48361C55D39A69163FA8FD24CF5F83655D23DCA3AD961C62F356208552BB9ED529077096966D670C354E4ABC9804F1746C08CA18217C32905E462E36CE3BE39E772C180E86039B2783A2EC07A28FB5C55DF06F4C52C9DE2BCBF6955817183995497CEA956AE515D2261898FA051015728E5A8AACAA68FFFFFFFFFFFFFFFF", 16) kexAlgoMap[kexAlgoDH14SHA1] = &dhGroup{ - g: new(big.Int).SetInt64(2), - p: p, + g: new(big.Int).SetInt64(2), + p: p, pMinus1: new(big.Int).Sub(p, bigOne), }